Просмотр исходного кода

Merge branch 'dev' of http://110.41.163.243:9980/kd-aiot/kd-aiot-frontend into dev

yusheng 9 месяцев назад
Родитель
Сommit
d299ea9c59

+ 91 - 0
src/api/system/dataDesensitization/index.js

@@ -0,0 +1,91 @@
+import request from '@/utils/request';
+
+//新增脱敏规则
+export async function saveDatadesensitizetype(data) {
+  const res = await request.post('/sys/datadesensitizetype/save', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//修改脱敏规则
+export async function updateDatadesensitizetype(data) {
+  const res = await request.put('/sys/datadesensitizetype/update', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//删除脱敏规则
+export async function deleteDatadesensitizetype(data) {
+  const res = await request.delete('/sys/datadesensitizetype/delete', { data });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//脱敏规则详情
+export async function detailDatadesensitizetype(id) {
+  const res = await request.get(`/sys/datadesensitizetype/getById/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//脱敏规则分页
+export async function pageDatadesensitizetype(params) {
+  const res = await request.get(`/sys/datadesensitizetype/page`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//新增数据脱敏
+export async function saveDatadesensitizerule(data) {
+  const res = await request.post('/sys/datadesensitizerule/save', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//修改数据脱敏
+export async function updateDatadesensitizerule(data) {
+  const res = await request.put('/sys/datadesensitizerule/update', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//删除数据脱敏
+export async function deleteDatadesensitizerule(data) {
+  const res = await request.delete('/sys/datadesensitizerule/delete', { data });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//数据脱敏详情
+export async function detailDatadesensitizerule(id) {
+  const res = await request.get(`/sys/datadesensitizerule/getById/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//数据脱敏分页
+export async function datadesensitizerulePage(params) {
+  const res = await request.get(`/sys/datadesensitizerule/page`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 1 - 1
src/views/rulesManagement/releaseRules/index.vue

@@ -85,7 +85,7 @@
           },
           {
             prop: 'classifyName',
-            label: '记录分类',
+            label: '记录规则分类',
             align: 'center',
             showOverflowTooltip: true,
             minWidth: 110

+ 228 - 0
src/views/system/dataDesensitization/components/AddDesensitizerule.vue

@@ -0,0 +1,228 @@
+<template>
+  <div class="container">
+    <!-- 单据弹窗 -->
+    <ele-modal
+      :title="title"
+      :before-close="handleClose"
+      custom-class="ele-dialog-form long-dialog-form"
+      :visible.sync="visible"
+      :close-on-click-modal="false"
+      :maxable="true"
+      :resizable="true"
+      width="80%"
+      height="80%"
+    >
+      <header-title title="基本信息"> </header-title>
+      <el-form
+        label-width="120px"
+        ref="form"
+        :model="form"
+        :rules="rules"
+        style="margin-top: 30px; padding-right: 20px"
+      >
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="页面名称" prop="moduleName">
+              <el-input
+                placeholder="请输入"
+                v-model="form.moduleName"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="脱敏名称" prop="functionName">
+              <el-input
+                placeholder="请输入"
+                v-model="form.functionName"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="脱敏URI地址" prop="requestUri">
+              <el-input
+                placeholder="请输入以/开头的地址"
+                v-model="form.requestUri"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <RulesDatail ref="rulesDatail" :detailList="detailList"></RulesDatail>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button size="small" @click="handleClose">关 闭</el-button>
+        <el-button size="small" @click="sumbit" type="primary" v-click-once
+          >确 认</el-button
+        >
+      </div>
+    </ele-modal>
+  </div>
+</template>
+<script>
+  import dictMixins from '@/mixins/dictMixins';
+  import RulesDatail from './RulesDatail.vue';
+  import {
+    saveDatadesensitizerule,
+    detailDatadesensitizerule,
+    updateDatadesensitizerule
+  } from '@/api/system/dataDesensitization';
+
+  export default {
+    mixins: [dictMixins],
+    components: {
+      RulesDatail
+    },
+    props: {
+      visible: {
+        type: Boolean,
+        default: false
+      },
+      isUpdate: {
+        type: Boolean,
+        default: false
+      },
+      id: {
+        type: String,
+        default: () => ''
+      }
+    },
+    data() {
+      return {
+        dataId: '',
+        title: '',
+        form: {
+          functionName: '',
+          moduleName: '',
+          requestUri: ''
+        },
+        detailList: [],
+        rules: {
+          functionName: [
+            { required: true, message: '请输入功能名称', trigger: 'blur' }
+          ],
+          moduleName: [
+            { required: true, message: '请输入模块名称', trigger: 'blur' }
+          ],
+          requestUri: [
+            { required: true, message: '请输入以/开头的地址', trigger: 'blur' },
+            {
+              pattern: /^\/(?!api\/).*/,
+              message: '必须以/开头且不能以/api开头',
+              trigger: 'blur'
+            }
+          ]
+        },
+
+        // 提交状态
+        loading: false
+        // 是否是修改
+      };
+    },
+    created() {
+      console.log('add--created');
+      console.log(this.detailList);
+
+      this.init();
+    },
+    mounted() {
+      console.log(this.detailList, 'mounted');
+    },
+    methods: {
+      async init() {
+        this.title = this.isUpdate ? '修改' : '新增';
+        if (this.isUpdate) {
+          this.dataId = this.id;
+          const res = await detailDatadesensitizerule(this.dataId);
+          this.form.functionName = res.functionName;
+          this.form.moduleName = res.moduleName;
+          this.form.requestUri = res.requestUri;
+          this.detailList = res.detailList;
+          console.log(this.detailList);
+        }
+      },
+
+      //表单验证
+      getValidate() {
+        return new Promise((resolve, reject) => {
+          this.$refs.form.validate((valid) => {
+            resolve(valid);
+          });
+        });
+      },
+
+      //保存
+      async sumbit() {
+        const res = await this.getValidate();
+
+        console.log(this.form);
+        if (!res) return;
+
+        // return;
+
+        this.form.detailList =
+          (await this.$refs.rulesDatail.getTableValidate()) || [];
+
+        if (!this.form.detailList.length) {
+          return this.$message.warning('请至少添加一条脱敏规则');
+        }
+
+        console.log(this.form);
+
+        if (!this.isUpdate) {
+          await saveDatadesensitizerule(this.form);
+          this.$message.success('新增成功');
+          this.cancel();
+        } else {
+          const params = Object.assign({}, this.form, { id: this.dataId });
+          await updateDatadesensitizerule(params);
+          this.$message.success('修改成功');
+          this.cancel();
+        }
+
+        // this.loading = true;
+        // let valid;
+        // try {
+        //   const res = await this.getValidate();
+        //   valid = res;
+        // } catch (error) {
+        //   valid = error;
+        // }
+        // if (!valid) return;
+
+        // if (!this.isUpdate) {
+        //   try {
+        //     await saveDatadesensitizetype(this.form);
+        //     this.$message.success('新增成功');
+        //     this.cancel();
+        //   } catch (error) {}
+        // } else {
+        //   try {
+        //     const params = Object.assign({}, this.form, { id: this.ruleId });
+        //     console.log(params);
+        //     await updateDatadesensitizetype(params);
+        //     this.$message.success('修改成功');
+        //     this.cancel();
+        //   } catch (error) {}
+        // }
+      },
+      cancel() {
+        this.$emit('success');
+        this.$emit('update:id', '');
+        this.$emit('update:isUpdate', false);
+        this.$emit('update:visible', false);
+      },
+      handleClose() {
+        this.cancel();
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  //   .container {
+  //     height: 80vh;
+  //   }
+</style>

+ 186 - 0
src/views/system/dataDesensitization/components/AddRules.vue

@@ -0,0 +1,186 @@
+<template>
+  <div class="container">
+    <!-- 单据弹窗 -->
+    <ele-modal
+      append-to-body
+      :title="title"
+      :before-close="handleClose"
+      custom-class="ele-dialog-form long-dialog-form"
+      :visible.sync="visible"
+      :close-on-click-modal="false"
+      :maxable="true"
+      :resizable="true"
+      width="50%"
+    >
+      <el-form
+        label-width="100px"
+        ref="form"
+        :model="form"
+        :rules="rules"
+        style="margin-top: 30px; padding-right: 20px"
+      >
+        <el-form-item label="规则名称" prop="dataName">
+          <el-input placeholder="请输入" v-model="form.dataName"></el-input>
+        </el-form-item>
+
+        <el-form-item label="保留前几位" prop="prefixKeep">
+          <el-input placeholder="请输入" v-model="form.prefixKeep"></el-input>
+        </el-form-item>
+
+        <el-form-item label="替换字符" prop="replaceChar">
+          <el-input
+            placeholder="请输入"
+            v-model="form.replaceChar"
+            maxlength="1"
+            show-word-limit
+          ></el-input>
+        </el-form-item>
+
+        <el-form-item label="保留后几位" prop="suffixKeep">
+          <el-input placeholder="请输入" v-model="form.suffixKeep"></el-input>
+        </el-form-item>
+      </el-form>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button size="small" @click="handleClose">关 闭</el-button>
+        <el-button size="small" @click="sumbit" type="primary" v-click-once
+          >确 认</el-button
+        >
+      </div>
+    </ele-modal>
+  </div>
+</template>
+<script>
+  import dictMixins from '@/mixins/dictMixins';
+  import { copyObj } from '@/utils/util';
+  import { deepClone } from '@/utils/index';
+  // import fileMain from '@/components/upload/fileUpload';
+
+  import {
+    saveDatadesensitizetype,
+    detailDatadesensitizetype,
+    updateDatadesensitizetype
+  } from '@/api/system/dataDesensitization';
+
+  export default {
+    mixins: [dictMixins],
+    components: {
+      // fileUpload
+    },
+    props: {
+      visible: {
+        type: Boolean,
+        default: false
+      },
+      isUpdate: {
+        type: Boolean,
+        default: false
+      },
+      id: {
+        type: String,
+        default: () => ''
+      }
+    },
+    data() {
+      return {
+        ruleId: '',
+        title: '',
+        form: {
+          dataName: '',
+          prefixKeep: 0,
+          replaceChar: '*',
+          suffixKeep: 0
+        },
+        rules: {
+          dataName: [
+            { required: true, message: '请输入规则名称', trigger: 'blur' }
+          ],
+          prefixKeep: [
+            { required: true, message: '请输入保留位数', trigger: 'blur' }
+          ],
+          suffixKeep: [
+            { required: true, message: '请输入保留位数', trigger: 'blur' }
+          ]
+        },
+
+        // 提交状态
+        loading: false
+        // 是否是修改
+      };
+    },
+    created() {
+      this.init();
+    },
+    methods: {
+      async init() {
+        this.title = this.isUpdate ? '修改' : '新增';
+        if (this.isUpdate) {
+          this.ruleId = this.id;
+          const res = await detailDatadesensitizetype(this.ruleId);
+          console.log(res);
+          this.form.dataName = res.dataName;
+          this.form.prefixKeep = res.prefixKeep;
+          this.form.replaceChar = res.replaceChar;
+          this.form.suffixKeep = res.suffixKeep;
+        }
+      },
+
+      //表单验证
+      getValidate() {
+        return new Promise((resolve, reject) => {
+          this.$refs.form.validate((valid) => {
+            if (!valid) {
+              reject(false);
+            } else {
+              resolve(true);
+            }
+          });
+        });
+      },
+
+      //保存
+      async sumbit() {
+        this.loading = true;
+        let valid;
+        try {
+          const res = await this.getValidate();
+          valid = res;
+        } catch (error) {
+          valid = error;
+        }
+        if (!valid) return;
+
+        if (!this.isUpdate) {
+          try {
+            await saveDatadesensitizetype(this.form);
+            this.$message.success('新增成功');
+            this.cancel();
+          } catch (error) {}
+        } else {
+          try {
+            const params = Object.assign({}, this.form, { id: this.ruleId });
+            console.log(params);
+            await updateDatadesensitizetype(params);
+            this.$message.success('修改成功');
+            this.cancel();
+          } catch (error) {}
+        }
+      },
+      cancel() {
+        this.$emit('success');
+        this.$emit('update:id', '');
+        this.$emit('update:isUpdate', false);
+        this.$emit('update:visible', false);
+      },
+      handleClose() {
+        this.cancel();
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  //   .container {
+  //     padding: 10px 0;
+  //   }
+</style>

+ 379 - 0
src/views/system/dataDesensitization/components/RulesDatail.vue

@@ -0,0 +1,379 @@
+<template>
+  <div class="container">
+    <el-form ref="form" :model="form">
+      <ele-pro-table
+        style="height: 100%"
+        ref="table"
+        :columns="columns"
+        :datasource="form.datasource"
+        :page-size="20"
+        @columns-change="handleColumnChange"
+        :cache-key="cacheKeyUrl"
+        height="calc(100% - 100px)"
+        :needPage="false"
+      >
+        <!-- 表头工具栏 -->
+        <template v-slot:toolbar>
+          <el-button
+            size="small"
+            type="primary"
+            icon="el-icon-plus"
+            class="ele-btn-icon"
+            @click="addHandler"
+            v-if="$hasPermission('main:team:save')"
+          >
+            添加脱敏规则
+          </el-button>
+        </template>
+
+        <!-- 操作列 -->
+        <template v-slot:action="{ row, $index }">
+          <el-popconfirm
+            class="ele-action"
+            title="确定要删除此信息吗?"
+            @confirm="handleDelInfo($index)"
+          >
+            <template v-slot:reference>
+              <el-link type="danger" :underline="false" icon="el-icon-delete">
+                删除
+              </el-link>
+            </template>
+          </el-popconfirm>
+        </template>
+
+        <template v-slot:fieldName="{ row, $index }">
+          <el-form-item
+            :prop="'datasource.' + $index + '.fieldName'"
+            :rules="{
+              required: true,
+              message: '请输入脱敏字段key',
+              trigger: ['blur']
+            }"
+          >
+            <el-input v-model="row.fieldName" clearable></el-input>
+          </el-form-item>
+        </template>
+
+        <template v-slot:fieldAlias="{ row, $index }">
+          <el-form-item
+            :prop="'datasource.' + $index + '.fieldAlias'"
+            :rules="{
+              required: true,
+              message: '请输入脱敏字段名称',
+              trigger: ['blur']
+            }"
+          >
+            <el-input v-model="row.fieldAlias" clearable></el-input>
+          </el-form-item>
+        </template>
+
+        <template v-slot:typeId="{ row, $index }">
+          <el-form-item
+            :prop="'datasource.' + $index + '.typeId'"
+            :rules="{
+              required: true,
+              message: '请选择',
+              trigger: ['blur', 'change']
+            }"
+          >
+            <el-select
+              v-if="typeList.length"
+              v-model="row.typeId"
+              collapse-tags
+              filterable
+              placeholder="请选择"
+              style="width: 100%"
+              clearable
+              size="medium"
+            >
+              <el-option
+                v-for="item in typeList"
+                :key="item.id"
+                :label="item.dataName"
+                :value="item.id"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </template>
+
+        <template v-slot:roleAuthorityList="{ row, $index }">
+          <el-form-item :prop="'datasource.' + $index + '.roleAuthorityList'">
+            <el-select
+              v-if="roleList.length"
+              v-model="row.roleAuthorityList"
+              multiple
+          
+              filterable
+              placeholder="请选择"
+              style="width: 100%"
+              clearable
+              size="medium"
+            >
+              <el-option
+                v-for="item in roleList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </template>
+
+        <template v-slot:userAuthorityList="{ row, $index }">
+          <el-form-item :prop="'datasource.' + $index + '.userAuthorityList'">
+            <el-select
+              :ref="'userRef' + $index"
+              v-if="userList.length"
+              v-model="row.userAuthorityList"
+              multiple
+            
+              filterable
+              placeholder="请选择"
+              style="width: 100%"
+              clearable
+              size="medium"
+              @focus.prevent="openUserPage(row, $index)"
+            >
+              <el-option
+                v-for="item in userList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </template>
+
+        <template v-slot:status="{ row, $index }">
+          <el-form-item :prop="'datasource.' + $index + '.status'">
+            <el-switch
+              v-model="row.status"
+              :active-text="row.status == 1 ? '启用' : '停用'"
+              :active-value="1"
+              :inactive-value="0"
+            >
+            </el-switch>
+          </el-form-item>
+        </template>
+
+        <template v-slot:headerRequired="{ column }">
+          <span class="is-required">{{ column.label }}</span>
+        </template>
+      </ele-pro-table>
+    </el-form>
+
+    <UserPage
+      @success="userSelect"
+      ref="UserPage"
+      :visible.sync="userVisible"
+    ></UserPage>
+  </div>
+</template>
+
+<script>
+  import tabMixins from '@/mixins/tableColumnsMixin';
+  import UserPage from './UserPage.vue';
+
+  import { getRolesListAPI } from '@/api/system/role';
+  import { getUserPage } from '@/api/system/organization';
+  import { pageDatadesensitizetype } from '@/api/system/dataDesensitization';
+  export default {
+    mixins: [tabMixins],
+    components: { UserPage },
+    props: {
+      detailList: {
+        type: Array,
+        default: () => []
+      }
+    },
+    watch: {
+      detailList: {
+        handler(newValue) {
+          this.form.datasource = newValue;
+        }
+      }
+    },
+    data() {
+      return {
+        cacheKeyUrl: 'mainData-system-dataDesensitization',
+        form: {
+          datasource: []
+        },
+        roleList: [],
+        userList: [],
+        typeList: [],
+        index: null,
+        userVisible: false
+      };
+    },
+    computed: {
+      columns() {
+        let arr = [
+          {
+            width: 55,
+            label: '序号',
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            fixed: 'left'
+          },
+          {
+            prop: 'fieldAlias',
+            slot: 'fieldAlias',
+            label: '脱敏字段名称',
+            width: 120,
+            align: 'center',
+            showOverflowTooltip: true,
+            headerSlot: 'headerRequired'
+          },
+          {
+            prop: 'fieldName',
+            slot: 'fieldName',
+            label: '脱敏字段key',
+            width: 120,
+            align: 'center',
+            showOverflowTooltip: true,
+            headerSlot: 'headerRequired'
+          },
+
+          {
+            prop: 'typeId',
+            slot: 'typeId',
+            label: '脱敏规则类型',
+            align: 'center',
+            width: 150,
+            showOverflowTooltip: true,
+            headerSlot: 'headerRequired'
+          },
+          {
+            prop: 'roleAuthorityList',
+            slot: 'roleAuthorityList',
+            label: '可查看角色',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'userAuthorityList',
+            slot: 'userAuthorityList',
+            label: '可查看账号',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'status',
+            slot: 'status',
+            width: 120,
+            label: '启用状态',
+            slot: 'status',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+
+          {
+            columnKey: 'action',
+            label: '操作',
+            width: 80,
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            showOverflowTooltip: true,
+            fixed: 'right'
+          }
+        ];
+        return arr;
+      }
+    },
+    methods: {
+      async getRolesList() {
+        const res = await getRolesListAPI();
+        this.roleList = res;
+      },
+      async getUserList() {
+        let { list } = await getUserPage({ pageNum: 1, size: -1 });
+        this.userList = list;
+      },
+      async getTypeList() {
+        let res = await pageDatadesensitizetype({
+          pageNum: 1,
+          size: -1
+        });
+        this.typeList = res.list;
+      },
+      addHandler() {
+        this.form.datasource.push({
+          fieldName: '',
+          fieldAlias: '',
+          typeId: '',
+          roleAuthorityList: [],
+          userAuthorityList: [],
+          status: 1
+        });
+      },
+      openUserPage(row, index) {
+        console.log(row);
+        this.index = index;
+        console.log(this.$refs);
+        let ref = 'userRef' + index;
+
+        this.$refs[ref].blur();
+
+        this.$refs.UserPage.open('1', row.userAuthorityList);
+
+        // this.userVisible = true;
+      },
+      visibleType() {
+        this.$nextTick(() => {
+          this.$refs.userRef.blur();
+        });
+      },
+      userSelect(e) {
+        let ids = e.data.map((item) => item.id);
+        console.log(this.index);
+        this.$set(this.form.datasource[this.index], 'userAuthorityList', ids);
+
+        console.log(e);
+      },
+      handleDelInfo(index) {
+        this.form.datasource.splice(index, 1);
+      },
+
+      getTableValidate() {
+        return new Promise((resolve, reject) => {
+          // if (this.form.datasource.length == 0) return this.$message.warning('请添加至少一条交付物信息')
+          this.$refs.form.validate((valid) => {
+            if (!valid) {
+              this.$message.warning('有必填项未填,请检查');
+              reject('有必填项未填,请检查');
+            } else {
+              resolve(this.form.datasource);
+            }
+          });
+        });
+      }
+    },
+    created() {
+      if (this.detailList.length) {
+        this.form.datasource = detailList;
+      }
+      this.getRolesList();
+      this.getUserList();
+      this.getTypeList();
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    width: 100%;
+    height: 100%;
+  }
+
+  table th .is-required:before {
+    content: '*';
+    color: red;
+    margin-right: 4px;
+  }
+</style>

+ 246 - 0
src/views/system/dataDesensitization/components/RulesList.vue

@@ -0,0 +1,246 @@
+<template>
+  <div>
+    <ele-modal
+      title="规则列表"
+      :visible.sync="visible"
+      :before-close="handleClose"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      append-to-body
+      width="70%"
+      :maxable="true"
+      :resizable="true"
+    >
+      <el-card shadow="never">
+        <ele-pro-table
+          ref="table"
+          :columns="columns"
+          :datasource="datasource"
+          row-key="id"
+          :selection.sync="selection"
+          height="calc(100vh - 350px)"
+          class="dict-table"
+        >
+          <!-- 表头工具栏 -->
+          <template v-slot:toolbar>
+            <el-button
+              size="small"
+              type="primary"
+              icon="el-icon-plus"
+              class="ele-btn-icon"
+              @click="addRules"
+            >
+              新建
+            </el-button>
+            <el-button
+              size="small"
+              type="danger"
+              el-icon-delete
+              class="ele-btn-icon"
+              @click="allDelBtn"
+              :disabled="selection?.length === 0"
+            >
+              批量删除
+            </el-button>
+          </template>
+
+          <!-- 操作列 -->
+          <template v-slot:action="{ row }">
+            <el-link
+              type="primary"
+              :underline="false"
+              icon="el-icon-edit"
+              @click="editRules(row.id)"
+            >
+              修改
+            </el-link>
+
+            <el-popconfirm
+              class="ele-action"
+              title="确定要删除此信息吗?"
+              @confirm="remove(row.id)"
+            >
+              <template v-slot:reference>
+                <el-link type="danger" :underline="false" icon="el-icon-delete">
+                  删除
+                </el-link>
+              </template>
+            </el-popconfirm>
+          </template>
+        </ele-pro-table>
+      </el-card>
+    </ele-modal>
+
+    <AddRules
+      @success="successHandler"
+      v-if="rulesVisible"
+      :visible.sync="rulesVisible"
+      :isUpdate.sync="isUpdate"
+      :id="ruleId"
+    ></AddRules>
+  </div>
+</template>
+
+<script>
+  import AddRules from './AddRules.vue';
+  import {
+    pageDatadesensitizetype,
+    deleteDatadesensitizetype
+  } from '@/api/system/dataDesensitization';
+  export default {
+    name: 'RulesList',
+    components: {
+      AddRules
+    },
+    props: {
+      visible: {
+        type: Boolean,
+        default: false
+      }
+    },
+    data() {
+      return {
+        selection: [],
+        rulesVisible: false,
+        isUpdate: false,
+        ruleId: ''
+      };
+    },
+    methods: {
+      //批量删除
+      async allDelBtn() {
+        if (this.selection?.length === 0) return;
+        let ids = this.selection.map((item) => item.id);
+        console.log(this.selection);
+        console.log(ids);
+        await deleteDatadesensitizetype(ids);
+        this.selection = [];
+        this.reload();
+      },
+      async remove(id) {
+        await deleteDatadesensitizetype([id]);
+        this.reload();
+      },
+      async datasource({ page, limit, where, order }) {
+        let res = await pageDatadesensitizetype({
+          pageNum: page,
+          size: limit,
+          ...where
+        });
+        return res;
+      },
+      reload(where) {
+        this.$refs.table.reload({ page: 1, where: where });
+      },
+      handleClose() {
+        this.$emit('update:visible', false);
+      },
+      successHandler() {
+        this.reload();
+      },
+      addRules() {
+        this.rulesVisible = true;
+      },
+      editRules(id) {
+        this.ruleId = id;
+        this.isUpdate = true;
+        this.rulesVisible = true;
+      }
+    },
+    computed: {
+      columns() {
+        let arr = [
+          {
+            width: 45,
+            type: 'selection',
+            columnKey: 'selection',
+            align: 'center',
+            fixed: 'left',
+            reserveSelection: true
+          },
+          {
+            width: 55,
+            label: '序号',
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            fixed: 'left'
+          },
+          {
+            prop: 'dataName',
+            label: '规则名称',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'prefixKeep',
+            label: '保留前几位',
+            align: 'center',
+            width: 100,
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'replaceChar',
+            label: '替换字符',
+            align: 'center',
+            width: 100,
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'suffixKeep',
+            label: '保留后几位',
+            align: 'center',
+            width: 100,
+            showOverflowTooltip: true
+          },
+          {
+            columnKey: 'action',
+            label: '操作',
+            width: 220,
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            showOverflowTooltip: true,
+            fixed: 'right'
+          }
+        ];
+        return arr;
+      }
+    },
+    created() {
+      console.log('created');
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .tree_col {
+    border: 1px solid #eee;
+    padding: 10px 0;
+    box-sizing: border-box;
+    height: 500px;
+    overflow: auto;
+  }
+
+  .table_col {
+    padding-left: 10px;
+
+    ::v-deep .el-table th.el-table__cell {
+      background: #f2f2f2;
+    }
+  }
+
+  .pagination {
+    text-align: right;
+    padding: 10px 0;
+  }
+
+  .btns {
+    text-align: center;
+    padding: 10px 0;
+  }
+
+  .topsearch {
+    margin-bottom: 15px;
+  }
+</style>

+ 230 - 0
src/views/system/dataDesensitization/components/UserPage.vue

@@ -0,0 +1,230 @@
+<!-- 用户编辑弹窗 -->
+<template>
+  <ele-modal
+    width="65vw"
+    :visible.sync="show"
+    :close-on-click-modal="false"
+    custom-class="ele-dialog-form"
+    append-to-body
+    @close="cancel"
+    :maxable="true"
+  >
+    <ele-split-layout
+      width="210px"
+      allow-collapse
+      :right-style="{ overflow: 'hidden' }"
+    >
+      <div>
+        <div class="ele-border-lighter sys-organization-list">
+          <el-tree
+            ref="tree"
+            :data="data"
+            highlight-current
+            node-key="id"
+            :props="{ label: 'name' }"
+            :expand-on-click-node="false"
+            :default-expand-all="true"
+            @node-click="onNodeClick"
+          /> </div
+        >.
+      </div>
+      <template v-slot:content>
+        <org-user-search @search="reload"> </org-user-search>
+        <ele-pro-table
+          row-key="id"
+          ref="table"
+          :columns="columns"
+          :datasource="datasource"
+          tool-class="ele-toolbar-form"
+          :selection.sync="selectionList"
+          @done="doneHandler"
+          @selection-change="selectionChange"
+        >
+        </ele-pro-table>
+      </template>
+    </ele-split-layout>
+    <template v-slot:footer>
+      <el-button @click="cancel">取消</el-button>
+      <el-button type="primary" @click="save"> 确认 </el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+  import { getUserPage } from '@/api/system/organization';
+  import OrgUserSearch from '@/views/system/organization/components/org-user-search.vue';
+
+  import { listOrganizations } from '@/api/system/organization';
+  export default {
+    components: {
+      OrgUserSearch
+    },
+    data() {
+      return {
+        data: [],
+        initIds: [],
+        selectIds: [],
+        selectionList: [],
+        show: false,
+        organizationId: '',
+        isHasAccount: '',
+        // 表格列配置
+        columns: [
+          {
+            width: 45,
+            type: 'selection',
+            columnKey: 'selection',
+            align: 'center',
+            fixed: 'left',
+            reserveSelection: true
+          },
+          {
+            columnKey: 'index',
+            type: 'index',
+            label: '序号',
+            width: 55,
+            align: 'center',
+            showOverflowTooltip: true,
+            fixed: 'left'
+          },
+          {
+            prop: 'name',
+            label: '姓名',
+            sortable: 'custom',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'jobNumber',
+            label: '工号',
+            sortable: 'custom',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'loginName',
+            label: '用户账号',
+            sortable: 'custom',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'sex',
+            label: '性别',
+            sortable: 'custom',
+            showOverflowTooltip: true,
+            minWidth: 80,
+            formatter: (_row, _column, cellValue) => {
+              return cellValue == 1 ? '男' : cellValue == 2 ? '女' : '';
+            }
+          }
+        ],
+        current: {}
+      };
+    },
+
+    created() {},
+    methods: {
+      /* 查询 */
+      query() {
+        listOrganizations()
+          .then((list) => {
+            let _list = list.filter((i) => i.name != '超级管理员');
+
+            this.data = this.$util.toTreeData({
+              data: _list,
+              idField: 'id',
+              parentIdField: 'parentId'
+            });
+
+            this.$nextTick(() => {
+              this.onNodeClick(this.data[0]);
+            });
+          })
+          .catch((e) => {
+            // this.$message.error(e.message);
+          });
+      },
+      open(isHasAccount, initIds) {
+        // if (initIds.length) {
+        //   this.initIds = initIds;
+        // }
+        this.isHasAccount = isHasAccount;
+        this.query();
+        this.show = true;
+      },
+      /* 表格数据源 */
+      async datasource({ page, limit, where, order }) {
+        let data = await getUserPage({
+          ...where,
+          ...order,
+          pageNum: page,
+          size: limit,
+          groupId: this.organizationId,
+          hasAccount: this.isHasAccount || ''
+        });
+
+        return data;
+      },
+      /* 保存编辑 */
+      save() {
+        console.log(this.selectionList);
+        let ids = this.selectionList.map((item) => item.id);
+
+        let selectIds = [...new Set()];
+
+        let data = JSON.parse(JSON.stringify(this.selectionList));
+        this.$emit('success', { data });
+        this.show = false;
+      },
+      selectionChange(e) {
+        console.log(e);
+      },
+      //   update(e) {
+      //     console.log(e);
+      //   },
+      onNodeClick(val) {
+        this.organizationId = val.id;
+        this.reload();
+      },
+      /* 刷新表格 */
+      reload(where) {
+        this.$refs.table.reload({ pageNum: 1, where: where });
+      },
+      doneHandler() {
+        // console.log('doneHandler');
+        // console.log(this.selectIds);
+
+        if (this.initIds.length) {
+          this.$nextTick(() => {
+            this.$refs.table.setSelectedRowKeys(this.initIds);
+            this.selectIds = this.selectionList.map((item) => item.id);
+            // console.log(this.initIds);
+            // this.initIds=[]
+
+            // this.initIds = this.initIds.filter(
+            //   (item) => !this.selectIds.includes(item)
+            // );
+            // console.log(this.initIds);
+
+            // console.log(this.selectIds);
+
+            // this.selectIds = [];
+          });
+        }
+      },
+      cancel() {
+        this.show = false;
+      }
+    }
+  };
+</script>
+<style scoped lang="scss">
+  :deep(.ele-split-panel-wrap) {
+    height: 50vh;
+    overflow-y: auto;
+  }
+  :deep(.el-checkbox__input.is-disabled .el-checkbox__inner) {
+    background-color: #f3f3f3;
+  }
+</style>

+ 245 - 0
src/views/system/dataDesensitization/index.vue

@@ -0,0 +1,245 @@
+<template>
+  <div class="container">
+    <div class="top-se">
+      <seek-page :seekList="seekList" @search="reload"></seek-page>
+    </div>
+
+    <ele-pro-table
+      style="height: calc(100% - 120px)"
+      ref="table"
+      :columns="columns"
+      :datasource="datasource"
+      :selection.sync="selectionList"
+      row-key="id"
+      :page-size="20"
+      @columns-change="handleColumnChange"
+      :cache-key="cacheKeyUrl"
+      height="calc(100% - 100px)"
+    >
+      <!-- 表头工具栏 -->
+      <template v-slot:toolbar>
+        <el-button
+          size="small"
+          type="primary"
+          icon="el-icon-plus"
+          class="ele-btn-icon"
+          @click="addHandler"
+          v-if="$hasPermission('main:team:save')"
+        >
+          新建
+        </el-button>
+
+        <el-button
+          size="small"
+          type="danger"
+          el-icon-delete
+          class="ele-btn-icon"
+          @click="allDelBtn"
+          :disabled="selectionList?.length === 0"
+        >
+          批量删除
+        </el-button>
+
+        <el-button
+          type="success"
+          class="ele-btn-icon"
+          size="small"
+          @click="openRules"
+        >
+          脱敏规则表
+        </el-button>
+      </template>
+      <!-- 操作列 -->
+      <template v-slot:action="{ row }">
+        <el-link
+          type="primary"
+          :underline="false"
+          icon="el-icon-edit"
+          @click="editHandler(row.id)"
+          v-if="$hasPermission('main:team:update')"
+        >
+          修改
+        </el-link>
+        <el-popconfirm
+          class="ele-action"
+          title="确定要删除此数据吗?"
+          @confirm="deleteHandler(row.id)"
+          v-if="$hasPermission('main:team:delete')"
+        >
+          <template v-slot:reference>
+            <el-link type="danger" :underline="false" icon="el-icon-delete">
+              删除
+            </el-link>
+          </template>
+        </el-popconfirm>
+      </template>
+    </ele-pro-table>
+
+    <RulesList v-if="rulesVisible" :visible.sync="rulesVisible"></RulesList>
+
+    <AddDesensitizerule
+      @success="successHandler"
+      v-if="desensitizeruleVisible"
+      :visible.sync="desensitizeruleVisible"
+      :isUpdate.sync="isUpdate"
+      :id="dataId"
+    ></AddDesensitizerule>
+  </div>
+</template>
+
+<script>
+  import tabMixins from '@/mixins/tableColumnsMixin';
+  import {
+    datadesensitizerulePage,
+    deleteDatadesensitizerule
+  } from '@/api/system/dataDesensitization';
+  import RulesList from './components/RulesList';
+  import AddDesensitizerule from './components/AddDesensitizerule';
+  export default {
+    mixins: [tabMixins],
+    components: {
+      RulesList,
+      AddDesensitizerule
+    },
+    data() {
+      return {
+        selectionList: [],
+        rulesVisible: false,
+        desensitizeruleVisible: false,
+        isUpdate: false,
+        dataId: '',
+        cacheKeyUrl: 'mainData-system-dataDesensitization'
+      };
+    },
+    computed: {
+      columns() {
+        let arr = [
+          {
+            width: 45,
+            type: 'selection',
+            columnKey: 'selection',
+            align: 'center',
+            fixed: 'left',
+            reserveSelection: true
+          },
+          {
+            width: 55,
+            label: '序号',
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            fixed: 'left'
+          },
+          {
+            prop: 'moduleName',
+            label: '脱敏页面',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'functionName',
+            label: '脱敏名称',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+
+          {
+            prop: 'requestUri',
+            label: '请求接口',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+
+          {
+            columnKey: 'action',
+            label: '操作',
+            width: 220,
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            showOverflowTooltip: true,
+            fixed: 'right'
+          }
+        ];
+        return arr;
+      },
+      seekList() {
+        return [
+          {
+            label: '脱敏页面:',
+            value: 'moduleName',
+            type: 'input',
+            placeholder: ''
+          },
+          {
+            label: '脱敏名称:',
+            value: 'functionName',
+            type: 'input',
+            placeholder: ''
+          },
+
+          {
+            label: '请求接口:',
+            value: 'requestUri',
+            type: 'input',
+            placeholder: ''
+          }
+        ];
+      }
+    },
+    methods: {
+      async datasource({ page, limit, where, order }) {
+        let res = await datadesensitizerulePage({
+          ...where,
+          pageNum: page,
+          size: limit
+        });
+        return res;
+      },
+      addHandler() {
+        this.desensitizeruleVisible = true;
+      },
+      editHandler(id) {
+        this.dataId = id;
+        this.isUpdate = true;
+        this.desensitizeruleVisible = true;
+      },
+      async deleteHandler(id) {
+        await deleteDatadesensitizerule([id]);
+        this.reload();
+      },
+      search() {},
+
+      async allDelBtn() {
+        if (!this.selectionList.length) return;
+        let ids = this.selectionList.map((item) => item.id);
+        await deleteDatadesensitizerule(ids);
+        this.selection = [];
+        this.reload();
+      },
+
+      openRules() {
+        this.rulesVisible = true;
+      },
+      successHandler() {
+        this.reload();
+      },
+      reload(where) {
+        this.$refs.table.reload({ page: 1, where: where });
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    width: 100%;
+    height: 100%;
+  }
+
+  .top-se {
+    background-color: #fff;
+    padding: 20px;
+    height: 100px;
+  }
+</style>