security-config-panel.vue 9.64 KB
<template>
  <ConfigPanelShell
    :modules="modules"
    :loading="loading"
    :saving="saving"
    storage-key="securityConfigActiveModule"
    default-module="login"
    nav-theme="warning"
    alert-title="左侧选择安全模块,右侧仅展示当前配置;保存后全量生效。"
    alert-type="warning"
    @save="$emit('save')"
  >
        <template #login>
          <el-card shadow="never" class="config-section">
            <div slot="header" class="config-section__header">
              <span>登录策略</span>
              <span class="config-section__tag">全站</span>
            </div>
            <p class="config-section__tip">
              控制账号登录互踢、会话超时与登录成功后的提示信息。
            </p>
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="单一登录">
                  <el-select v-model="form.singleLogin" placeholder="请选择" style="width: 100%">
                    <el-option label="后登录踢出先登录" value="1" />
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="超时登出">
                  <el-input-number v-model="form.tokenTimeout" :min="1" :precision="0" :step="1"
                    controls-position="right" />
                  <span class="field-suffix">分钟</span>
                </el-form-item>
              </el-col>
            </el-row>
          </el-card>

          <el-card shadow="never" class="config-section">
            <div slot="header" class="config-section__header">
              <span>上次登录提示</span>
              <span class="config-section__tag">登录页</span>
            </div>
            <p class="config-section__tip">
              开启后,用户登录成功时将展示上一次登录的时间、地点与 IP 信息,便于发现异常登录。
            </p>
            <el-form-item label="上次登录">
              <el-switch v-model="form.lastLoginTimeSwitch" :active-value="1" :inactive-value="0" />
              <span class="field-suffix field-suffix--text">
                {{ Number(form.lastLoginTimeSwitch) === 1 ? '已开启' : '已关闭' }}
              </span>
            </el-form-item>
            <div v-if="Number(form.lastLoginTimeSwitch) === 1" class="login-preview">
              <div class="login-preview__title">效果预览</div>
              <div class="login-preview__card">
                <div class="login-preview__card-title">上次登录信息提示</div>
                <p>时间:2018-10-17 12:40</p>
                <p>地点:上海市</p>
                <p>IP:255.255.0.0</p>
              </div>
            </div>
          </el-card>
        </template>

        <template #display>
          <el-card shadow="never" class="config-section">
            <div slot="header" class="config-section__header">
              <span>页面水印</span>
              <span class="config-section__tag">管理后台 / 手机端</span>
            </div>
            <p class="config-section__tip">
              开启后管理后台与员工手机端均显示「部门、姓名、电话、时间」水印;调整后请点击顶部保存持久化。
            </p>
            <el-form-item label="页面水印">
              <el-switch v-model="form.watermarkSwitch" :active-value="1" :inactive-value="0"
                @change="$emit('watermark-switch-change')" />
              <span class="field-suffix field-suffix--text">
                {{ Number(form.watermarkSwitch) === 1 ? '已开启' : '已关闭' }}
              </span>
            </el-form-item>
            <template v-if="Number(form.watermarkSwitch) === 1">
              <el-form-item label="PC水印深浅">
                <div class="opacity-row">
                  <el-slider v-model="form.watermarkOpacityPc" :min="1" :max="100" :step="1" show-input
                    input-size="small" class="opacity-slider" @change="$emit('watermark-opacity-change')" />
                  <span class="form-tip">仅影响管理后台,数值越大越深(默认 10)</span>
                </div>
              </el-form-item>
              <el-form-item label="手机水印深浅">
                <div class="opacity-row">
                  <el-slider v-model="form.watermarkOpacityApp" :min="1" :max="100" :step="1" show-input
                    input-size="small" class="opacity-slider" />
                  <span class="form-tip">仅影响员工手机端,数值越大越深(默认 14)</span>
                </div>
              </el-form-item>
            </template>
          </el-card>

          <el-card shadow="never" class="config-section">
            <div slot="header" class="config-section__header">
              <span>手机号脱敏</span>
              <span class="config-section__tag">全站列表</span>
            </div>
            <p class="config-section__tip">
              关闭时发送日志等展示明文手机号;开启后全站列表、详情与发送日志按脱敏规则展示。
            </p>
            <el-form-item label="手机号脱敏">
              <el-switch v-model="form.privacyMobileMask" :active-value="1" :inactive-value="0"
                @change="$emit('privacy-mobile-mask-change')" />
              <span class="field-suffix field-suffix--text">
                {{ Number(form.privacyMobileMask) === 1 ? '已开启' : '已关闭' }}
              </span>
            </el-form-item>
          </el-card>
        </template>

        <template #access>
          <el-card shadow="never" class="config-section">
            <div slot="header" class="config-section__header">
              <span>IP 白名单</span>
              <span class="config-section__tag">访问控制</span>
            </div>
            <p class="config-section__tip">
              开启验证后,仅允许白名单内的 IP 访问系统;每行一个 IP,支持多行配置。
            </p>
            <el-form-item label="开启验证">
              <el-switch v-model="form.whitelistSwitch" :active-value="1" :inactive-value="0" />
              <span class="field-suffix field-suffix--text">
                {{ Number(form.whitelistSwitch) === 1 ? '已开启' : '已关闭' }}
              </span>
            </el-form-item>
            <el-form-item label="允许访问IP">
              <el-input
                type="textarea"
                :autosize="{ minRows: 6, maxRows: 14 }"
                v-model="form.whiteListIp"
                placeholder="每行一个 IP,例如:&#10;192.168.1.100&#10;10.0.0.0/24"
              />
            </el-form-item>
          </el-card>
        </template>
  </ConfigPanelShell>
</template>

<script>
import ConfigPanelShell from './config-panel-shell.vue'

const MODULES = [
  { key: 'login', label: '登录与会话', desc: '互踢、超时与登录提示', icon: 'el-icon-user', iconClass: 'module-icon--login' },
  { key: 'display', label: '水印与脱敏', desc: '页面水印与手机号隐私', icon: 'el-icon-view', iconClass: 'module-icon--display' },
  { key: 'access', label: '访问控制', desc: 'IP 白名单限制', icon: 'el-icon-lock', iconClass: 'module-icon--access' }
]

export default {
  name: 'SecurityConfigPanel',
  components: { ConfigPanelShell },
  props: {
    form: {
      type: Object,
      required: true
    },
    loading: {
      type: Boolean,
      default: false
    },
    saving: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      modules: MODULES
    }
  }
}
</script>

<style lang="scss" scoped>
.module-icon {
  margin-right: 8px;
  font-size: 16px;
  flex-shrink: 0;

  &--login {
    color: #409EFF;
  }

  &--display {
    color: #67C23A;
  }

  &--access {
    color: #E6A23C;
  }
}

.config-section {
  margin-bottom: 16px;
  border: 1px solid #ebeef5;

  &:last-child {
    margin-bottom: 0;
  }

  &__header {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 500;
  }

  &__tag {
    padding: 0 8px;
    height: 22px;
    line-height: 22px;
    border-radius: 4px;
    background: #f4f4f5;
    color: #909399;
    font-size: 12px;
    font-weight: normal;
  }

  &__tip {
    margin: 0 0 12px;
    color: #909399;
    font-size: 13px;
    line-height: 1.6;
  }
}

.field-suffix {
  margin-left: 8px;
  color: #909399;
  font-size: 13px;

  &--text {
    display: inline-block;
    min-width: 48px;
  }
}

.form-tip {
  color: #909399;
  font-size: 13px;
  line-height: 1.6;
}

.opacity-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  width: 100%;
}

.opacity-slider {
  width: 360px;
  max-width: 100%;
}

.login-preview {
  margin-top: 4px;
  padding-left: 120px;

  &__title {
    margin-bottom: 8px;
    color: #909399;
    font-size: 12px;
  }

  &__card {
    width: 300px;
    max-width: 100%;
    padding: 12px 16px;
    border: 1px solid #ebeef5;
    border-radius: 8px;
    background: #fafafa;

    &-title {
      margin-bottom: 8px;
      color: #303133;
      font-size: 14px;
      font-weight: 500;
    }

    p {
      margin: 4px 0;
      color: #606266;
      font-size: 13px;
      line-height: 1.6;
    }
  }
}

.ncc-content-dark {
  .config-section {
    border-color: rgba(255, 255, 255, 0.08);

    &__tag {
      background: rgba(255, 255, 255, 0.08);
      color: rgba(255, 255, 255, 0.55);
    }

    &__tip,
    .form-tip,
    .field-suffix {
      color: rgba(255, 255, 255, 0.55);
    }
  }

  .login-preview__card {
    border-color: rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.03);

    &-title {
      color: rgba(255, 255, 255, 0.85);
    }

    p {
      color: rgba(255, 255, 255, 0.65);
    }
  }
}
</style>