<script setup lang="ts">
import { ref, reactive, onUnmounted, nextTick } from 'vue';
import { getErrorTranslate, traduction } from '@/utils/language';
import { ElForm, ElNotification as elNotification } from 'element-plus';
import FileUpload from '@/components/FileUpload/FileUpload.vue';
import { UploadFile } from 'element-plus/es/components/upload/src/upload';
import CustomAlert from '@/components/CustAlert/CustAlert.vue';
import { exportCSR, task } from '@/services/web-service';
import { downloadFileWithResponse } from '@/utils/common-service';
import { notificationIcons } from '@/model/base-constant';
import RefreshIcon from '@/components/RefreshIcon/RefreshIcon.vue';
import { IUploadConfig } from '@/model/base-interface';
import { strVerification } from '@/validators/validation-functions';
import ClearableIcon from '@/components/ClearableIcon/ClearableIcon.vue';
import ErrorIcon from '@/components/ErrorIcon/ErrorIcon.vue';
import { escapeHeader, showElMessage } from '@/utils/utils';
import { setStoreData, showFailedMessage, showSuccessMessage, showWarningMessage } from '@/utils/composition';
import step1Check from '@/assets/common/image/ssl-step1-check.svg';
import step1Complete from '@/assets/common/image/ssl-step1-complete.svg';
import step1CompleteHover from '@/assets/common/image/ssl-step1-complete-hover.svg';
import step2 from '@/assets/common/image/ssl-step2.svg';
import step2Hover from '@/assets/common/image/ssl-step2-hover.svg';
import step2Check from '@/assets/common/image/ssl-step2-check.svg';
import $http from '@/utils/http-service';
import { IPV4_IP, IP_REGEX_V4, IPV6, DOMAIN_NAME } from '@/utils/regular';
import { UI_REST_GENERALDOWNLOAD } from '@/api/api';
import CaCertificateService from '@/services/caCertificate.service';
import UploadingIcon from '@/components/UploadingIcon/UploadingIcon.vue';

const emit = defineEmits(['close']);
const dialogVisible = ref(true);
const generateDisabled = ref(false);
const generateTaskRunning = ref(false);
let certUpload: any[] = [];
const uploadDisabled = ref(false);
const uploadFailList = ref([false, false]);
let notification: any = null;
let timeout = null as any;
// 表单校验结果
let validateResult = {};
const props = defineProps({
  data: {
    type: String,
    required: true,
    default: '',
  },
  isGm: {
    type: Boolean,
    required: true,
  },
});

const formRules = reactive({
  country: {
    type: 'text',
    required: true,
    matchRule: /^[a-zA-Z]{2}$/,
    message: traduction('VALIDATOR_INCORRECT_REINPUT'),
    validator: strVerification,
    trigger: 'change',
  },
  province: {
    type: 'text',
    required: false,
    containsEmptyString: true,
    matchRule: /^[a-zA-Z\d\-\.\_\s]{0,128}$/,
    message: traduction('VALIDATOR_INCORRECT_REINPUT'),
    validator: strVerification,
    trigger: 'change',
  },
  city: {
    type: 'text',
    required: false,
    containsEmptyString: true,
    matchRule: /^[a-zA-Z\d\-\.\_\s]{0,128}$/,
    message: traduction('VALIDATOR_INCORRECT_REINPUT'),
    validator: strVerification,
    trigger: 'change',
  },
  company: {
    type: 'text',
    required: false,
    containsEmptyString: true,
    matchRule: /^[a-zA-Z\d\-\.\_\s]{0,64}$/,
    message: traduction('VALIDATOR_INCORRECT_REINPUT'),
    validator: strVerification,
    trigger: 'change',
  },
  department: {
    type: 'text',
    required: false,
    containsEmptyString: true,
    matchRule: /^[a-zA-Z\d\-\.\_\s]{0,64}$/,
    message: traduction('VALIDATOR_INCORRECT_REINPUT'),
    validator: strVerification,
    trigger: 'change',
  },
  name: {
    type: 'text',
    required: true,
    containsEmptyString: true,
    matchRule: /^[ a-zA-Z\d\-\.\_\s]{1,64}$/,
    message: traduction('VALIDATOR_INCORRECT_REINPUT'),
    validator: strVerification,
    trigger: 'change',
  },
  alternativeNames: {
    id: 'alternativeNames',
    type: 'string',
    required: false,
    message: traduction('IBMC_IPV6_ADDRES_ERROR_4'),
    validator: (rule: any, value: string) => {
      let res = true;
      if (value === '') {
        return res;
      }
      let sanArr = value.split(',');
      if (sanArr.length > 50) {
        return res = false;
      }
      sanArr.forEach((s) => {
        if (s.length > 255) {
          res = false;
        }
        let sArr = <any>[];
        let sliceIndex = s.indexOf(':');
        if (sliceIndex > 0 && sliceIndex < s.length - 1) {
          sArr = [s.slice(0, sliceIndex), s.slice(sliceIndex + 1, s.length)];
          let sKey = sArr[0];
          let sValue = sArr[1];
          if (sanValidator(sKey, sValue) === false) {
            res = false;
          }
        } else {
          res = false;
        }
      });
      return res;
    },
    containsEmptyString: true,
    trigger: 'change',
  },
} as any);

function sanValidator(key: string, value: string): boolean {
  let res = true;
  const URIReg = new RegExp('^(?<protocol>https?):\/\/(?<domain>[^\\s/$.?#].[^\\s]*)$');
  const EMAILReg = new RegExp('^[a-zA-Z0-9][a-zA-Z0-9_\\-.]{0,63}@[a-zA-Z0-9_\\-.]{1,254}\\.[a-zA-Z0-9]{2,64}$');
  switch(key) {
    case 'email':
      res = EMAILReg.test(value);
      break;
    case 'URI':
      res = URIReg.test(value);
      break;
    case 'IP':
      res = IPV6.test(value) || IPV4_IP.test(value);
      break;
    case 'DNS':
      res = DOMAIN_NAME.test(value);
      break;
    default: 
      res = false;
      break;
  };
  return res;
};

const csrValue = reactive({
  country: '',
  province: '',
  city: '',
  company: '',
  department: '',
  name: '',
  alternativeNames: '',
});
const popoverVisible = reactive({
  country: false,
  province: false,
  city: false,
  company: false,
  department: false,
  name: false,
});
const ruleFormRef = ref();

const csrInfo = ref([
  {
    id: 'country',
    label: traduction('SERVICE_WBS_DLOG_COUNTRY'),
    tip: traduction('SERVICE_COUNTRY_FORMAT'),
    tipShow: false,
  },
  {
    id: 'province',
    label: traduction('SERVICE_WBS_DLOG_PROVINCE'),
    tip: traduction('SERVICE_PROVINCE_FORMAT'),
    tipShow: false,
  },
  {
    id: 'city',
    label: traduction('SERVICE_WBS_DLOG_CITY'),
    tip: traduction('SERVICE_CITY_FORMAT'),
    tipShow: false,
  },
  {
    id: 'company',
    label: traduction('SERVICE_WBS_DLOG_COMPONY'),
    tip: traduction('SERVICE_COMPANY_FORMAT'),
    tipShow: false,
  },
  {
    id: 'department',
    label: traduction('SERVICE_WBS_DLOG_DEPARTMENT'),
    tip: traduction('SERVICE_DEPARTMENT_FORMAT'),
    tipShow: false,
  },
  {
    id: 'name',
    label: traduction('SERVICE_WBS_DLOG_NAME'),
    tip: traduction('SERVICE_NAME_FORMAT'),
    tipShow: false,
  },
  {
    id: 'alternativeNames',
    label: traduction('IBMC_SUBJECT_ALTERNATIVE_NAME'),
    tip: traduction('IBMC_SUBJECT_ALTERNATIVE_NAME_FORMAT'),
    tipShow: false,
  },
]);

const stepList = ref([
  {
    label: traduction('SERVICE_WBS_DLOG_RADIO_CSR'),
    id: 'generate',
    checked: true,
    img: step1Check,
  },
  {
    label: traduction('CERT_IMPORT_SERVER_CERT'),
    id: 'getCred',
    checked: false,
    img: step2,
  },
]);

init();
function init() {
  const subjects = props.data.split(',');
  for (const key of subjects) {
    const subjectArr = key.split('=');
    if (subjectArr[0].trim() === 'C') {
      csrValue.country = subjectArr[1];
    } else if (subjectArr[0].trim() === 'S') {
      csrValue.province = subjectArr[1];
    } else if (subjectArr[0].trim() === 'L') {
      csrValue.city = subjectArr[1];
    } else if (subjectArr[0].trim() === 'O') {
      csrValue.company = subjectArr[1];
    } else if (subjectArr[0].trim() === 'OU') {
      csrValue.department = subjectArr[1];
    } else if (subjectArr[0].trim() === 'CN') {
      csrValue.name = subjectArr[1];
    }
  }
  nextTick(()=>{
    ruleFormRef.value.validate();
  });
}

function focus(key: string) {
  popoverVisible[key] = true;
}

function blur(key: string) {
  popoverVisible[key] = false;
}

function clear(key: string) {
  // 如果没有focus，那么这个地方需要手动调用一次校验
  if (!popoverVisible[key]) {
    ruleFormRef.value.validateField(key);
  }
}

// 输入框 发生input 事件
function input(key: string, value: string) {
  csrValue[key] = value;
  validateResult[key] = formRules[key].validator(formRules[key], csrValue[key], () => {});
  nextTick(() => {
    setSaveBtnState();
  });
}

// 表单校验发生时 - 控制保存按钮状态 state:校验结果
function validate(key: any, state: boolean) {
  validateResult[key] = state;
  nextTick(() => {
    setSaveBtnState();
  });
}

function setSaveBtnState() {
  // 表单是否通过了校验
  for (let key in validateResult) {
    // 任意一个校验失败
    if (!validateResult[key]) {
      generateDisabled.value = true;
      return;
    }
  }
  generateDisabled.value = false;
}

function confirm() {
  const param = {
    Country: csrValue.country,
    CommonName: csrValue.name,
    City: csrValue.city,
    State: csrValue.province,
    OrgName: csrValue.company,
    OrgUnit: csrValue.department,
    AlternativeNames: csrValue.alternativeNames,
  };

  recursionCsr(param);
  handleDialogClose(false);
}

function handleDialogClose(isNeedRefresh: boolean) {
  dialogVisible.value = false;
  emit('close', isNeedRefresh);
}

const loct = localStorage.getItem('loct');

const setUploadFail = (index: 0 | 1, value: boolean) =>
  index === 0 ? [value, uploadFailList.value[1]] : [uploadFailList.value[0], value];

const onAddItem = (index: 0 | 1) => (file: UploadFile, list: UploadFile[], msg?: any, upload?: any): boolean => {
  let type = file.name.substring(file.name.lastIndexOf('.') + 1);
  let acceptTypes = ['crt', 'cer', 'pem', 'der'];
  certUpload.push(upload);
  setUploadFail(index, false);
  uploadDisabled.value = true;
  if (acceptTypes.includes(type) && (file.size as any) > 102400) {
    msg.value = traduction('SERVICE_WBS_FILE_SIZE_VALI_TIP02');
    setUploadFail(index, true);
    return false;
  } else if (
    acceptTypes.includes(type) &&
    (file.size as any) <= 102400
  ) {
    uploadDisabled.value = false;
    return true;
  } else {
    msg.value = traduction('SERVICE_UPLOADIN_CORRECT');
    setUploadFail(index, true);
    return false;
  }
};

const onError = (error: any, file: UploadFile, list: UploadFile[]): void => {
  const errorCode = JSON.parse(error.message)?.error[0]?.code;
  if (errorCode === 'NoValidSession') {
    setStoreData(
      'event',
      'error401',
      `${traduction('SessionTimeout.errorMessage')}${Date.now()}`,
    );
  }
  showFailedMessage(getErrorTranslate(errorCode));
  handleDialogClose(true);
};

const uploadConfig = reactive({
  id: 'certUpload',
  action: '',
  headers: {
    From: 'WebUI',
    'X-CSRF-Token': escapeHeader(loct ? JSON.parse(loct).to : ''),
  },
  method: 'POST',
  name: 'Content',
  data: { CertificateType: '' },
  isAutoUpload: false,
  accept: '.crt, .cer, .pem, .der',
  onAddItem: onAddItem(0),
  onError,
  onSuccess: (response: any, file: UploadFile, list: UploadFile[]): void => {
    showSuccessMessage();
    handleDialogClose(true);
  },
  onBeforeUpload: (file: UploadFile): any => {
    // 上传前动态添加额外参数
    uploadConfig.data.CertificateType = 'Server';
    if (file) {
      updateServerCertificate(file);
    }
    return true;
  },
} as IUploadConfig);

const uploadConfig2 = reactive({
  id: 'certUpload2',
  action: '',
  headers: {
    From: 'WebUI',
    'X-CSRF-Token': escapeHeader(loct ? JSON.parse(loct).to : ''),
  },
  method: 'POST',
  name: 'Content',
  data: { CertificateType: '' },
  isAutoUpload: false,
  accept: '.crt, .cer, .pem, .der',
  onAddItem: onAddItem(1),
  onError,
  onSuccess: (response: any, file: UploadFile, list: UploadFile[]): void => {
    showSuccessMessage();
    handleDialogClose(true);
  },
  onBeforeUpload: (file: UploadFile): any => {
    // 上传前动态添加额外参数
    uploadConfig2.data.CertificateType = 'Server';
    if (file) {
      updateServerCertificate(file);
    }
    return true;
  },
} as IUploadConfig);

function updateServerCertificate(file: any) {
  const clientFormData = new FormData();
  clientFormData.append('imgfile', file, file.name);
  CaCertificateService.uploadCerFile(clientFormData)
    .then(() => {
      CaCertificateService.importServersCerCtrl(file.name)
        .then((res) => {
          if (res?.data?.ValidateResult !== 0) {
            showWarningMessage(traduction('CERT_IMPORT_WARNING'));
          } else {
            showSuccessMessage();
          }
          handleDialogClose(true);
        })
        .catch(error => {
          const errorCode = error.data?.error[0]?.code;
          if (errorCode === 'NoValidSession') {
            setStoreData(
              'event',
              'error401',
              `${traduction('SessionTimeout.errorMessage')}${Date.now()}`,
            );
          }
          showFailedMessage(getErrorTranslate(errorCode));
          handleDialogClose(true);
        });
    })
    .catch(error => {
      const errorCode = JSON.parse(error.message)?.error[0]?.code;
      showFailedMessage(getErrorTranslate(errorCode));
      handleDialogClose(true);
    });
}

const submitUpload = () => {
  certUpload.forEach(item => item?.submit());
  certUpload = [];
};

function recursionCsr(params: any): void {
  if (params.AlternativeNames !== undefined && params.AlternativeNames.length > 0) {
    let sanArr = params.AlternativeNames.split(',');
    params.AlternativeNames = sanArr;
  } else {
    params.AlternativeNames = [];
  }
  if (props.isGm) {
    params.KeyPairAlgorithm = 'TPM_ALG_SM2';
  }
  exportCSR(params).then(
    (res: any) => {
      generateTaskRunning.value = true;
      generateDisabled.value = true;
      const taskUrl = res.data.url;
      taskCsr(taskUrl);
      notification = elNotification({
        title: traduction('CSR_GENERATING_TITLE'),
        message: traduction('CSR_GENERATING'),
        icon: UploadingIcon as any,
        duration: 0,
        position: 'bottom-right',
        offset: 10,
      });
    },
    (error: any) => {
      generateDisabled.value = false;
      generateTaskRunning.value = false;
      if (error.data.error[0].code === 'DuplicateExportingErr') {
        notification = elNotification({
          title: traduction('CSR_GENERATING_CONFICT_TITLE'),
          message: traduction('CSR_GENERATING_CONFICT'),
          icon: notificationIcons.warning as any,
          duration: 10000,
          position: 'bottom-right',
          offset: 10,
        });
      } else {
        notification = elNotification({
          title: traduction('CSR_GENERATING_FAILED_TITLE'),
          message: traduction('CSR_GENERATING_FAILED'),
          icon: notificationIcons.error as any,
          duration: 10000,
          position: 'bottom-right',
          offset: 10,
        });
      }
    },
  );
}

function downloadOperateData() {
  const data = {
    Path: props.isGm ? '/tmp/gm_csr.tar.gz' : '/tmp/server.csr',
    TransferProtocol: 'HTTPS',
  };
  return $http.post(UI_REST_GENERALDOWNLOAD, data, { responseType: 'blob' });
}

// 开始下载
function beginDownLoad() {
  downloadOperateData()
    .then((res: any) => {
      const fileName = res.headers['content-disposition'].split('=')[1];
      downloadFileWithResponse(res.data, fileName);
      showElMessage('success', traduction('COMMON_SUCCESS'));
    })
    .catch(() => {
      showElMessage('error', traduction('COMMON_FAILED'));
    });
}

function taskCsr(taskUrl: string): void {
  task(taskUrl).then((taskData: any) => {
    if (taskData.data.prepare_progress === 100 && taskData.data.state === 'Completed') {
      beginDownLoad();
      generateTaskRunning.value = false;
      generateDisabled.value = false;

      notification.close();
      notification = elNotification({
        title: traduction('CSR_GENERATING_SUCCESS_TITLE'),
        message: traduction('CSR_GENERATING_SUCCESS'),
        icon: notificationIcons.success as any,
        duration: 5000,
        position: 'bottom-right',
        offset: 10,
      });
    } else if (taskData.data.prepare_progress <= 100) {
      timeout = setTimeout(() => {
        taskCsr(taskUrl);
      }, 3000);
    }
  });
}

function chooseStep(stepId: string) {
  setUploadFail(0, false);
  setUploadFail(1, false);
  if (stepId === 'generate') {
    stepList.value[0].checked = true;
    stepList.value[1].checked = false;
    stepList.value[0].img = step1Check;
    stepList.value[1].img = step2;
  } else {
    stepList.value[0].checked = false;
    stepList.value[1].checked = true;
    stepList.value[0].img = step1Complete;
    stepList.value[1].img = step2Check;
  }
}

function mouseEnter(stepId: string) {
  if (stepId === 'generate') {
    if (stepList.value[0].checked === false) {
      stepList.value[0].img = step1CompleteHover;
    }
  } else {
    if (stepList.value[1].checked === false) {
      stepList.value[1].img = step2Hover;
    }
  }
}
function mouseLeave(stepId: string) {
  if (stepId === 'generate') {
    if (stepList.value[0].checked === false) {
      stepList.value[0].img = step1Complete;
    }
  } else {
    if (stepList.value[1].checked === false) {
      stepList.value[1].img = step2;
    }
  }
}

onUnmounted(() => {
  if (timeout !== null) {
    clearTimeout(timeout);
  }
});
</script>

<template>
  <div v-if="dialogVisible" v-reqKeepAlive.dialog>
    <el-dialog
      v-model="dialogVisible"
      :title="traduction('CERT_SERVER_TITLE')"
      :class="{ 'server-cert-dialog': true, 'error-dialog': uploadFailList[0] }"
      :close-on-click-modal="false"
      :before-close="handleDialogClose"
    >
      <div id="fileCRSId">
        <div class="step-filed">
          <template v-for="(step, index) in stepList" :key="step.id">
            <div
              :id="step.id"
              class="step"
              @click="chooseStep(step.id)"
              @mouseenter="mouseEnter(step.id)"
              @mouseleave="mouseLeave(step.id)"
            >
              <img class="step-icon" :src="step.img" />
              <div>
                <div
                  :class="[
                    step.checked ? 'check-label' : 'uncheck-label',
                    step.id === 'generate' && !step.checked ? 'complete' : ''
                  ]"
                >
                  {{ step.label }}
                </div>
              </div>
            </div>
            <el-divider
              v-if="index === 0"
              :class="step.id === 'generate' && !step.checked ? 'next' : ''"
            ></el-divider>
          </template>
        </div>
        <CustomAlert v-if="stepList[0].checked" :content="traduction('CERT_SKIP_GENERATE_CSR')" />
        <el-form
          v-if="stepList[0].checked"
          ref="ruleFormRef"
          label-width="auto"
          label-position="left"
          :model="csrValue"
          :rules="formRules"
          :validate-on-rule-change="false"
          @validate="validate"
        >
          <el-form-item
            v-for="(item, index) in csrInfo"
            :key="item.id"
            :rules="formRules[item.id]"
            :label="$t(item.label)"
            :prop="item.id"
            :class="{ 'not-require': index !== 0 && index !== 5 }"
          >
            <el-popover
              v-model:visible="popoverVisible[item.id]"
              popper-class="no-wrap-popover"
              placement="top"
              :content="item.tip"
              :append-to-body="false"
              trigger="focus"
              trigger-keys
            >
              <template #reference>
                <el-input
                  v-model="csrValue[item.id]"
                  v-addId.input="item.id"
                  v-clearable
                  clearable
                  @focus="focus(item.id)"
                  @blur="blur(item.id)"
                  @input="input(item.id, $event)"
                  @clear="clear(item.id)"
                >
                  <template #suffix>
                    <ErrorIcon />
                    <ClearableIcon />
                  </template>
                </el-input>
              </template>
            </el-popover>
          </el-form-item>
        </el-form>
        <el-form v-if="stepList[1].checked">
          <template v-if="isGm">
            <el-form-item
              label-width="100px"
              :label="$t('CERT_GM_ENC')"
              :class="{ 'error-item': uploadFailList[0] }"
            >
              <FileUpload :data="uploadConfig"></FileUpload>
              <span class="format-text">{{ traduction('CERT_SERVER_FORMAT') }}</span>
            </el-form-item>
            <el-form-item
              label-width="100px"
              :label="$t('CERT_GM_SIG')"
              :class="{ 'error-item': uploadFailList[1] }"
            >
              <FileUpload :data="uploadConfig2"></FileUpload>
              <span class="format-text">{{ traduction('CERT_SERVER_FORMAT') }}</span>
            </el-form-item>
          </template>
          <template v-else>
            <el-form-item
              :label="$t('ALARM_REPORT_SYSLOG_CER_FILE')"
              :class="{ 'error-item': uploadFailList[0] }"
            >
              <FileUpload :data="uploadConfig"></FileUpload>
              <span class="format-text">{{ traduction('CERT_SERVER_FORMAT') }}</span>
            </el-form-item>
          </template>
        </el-form>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button
            v-if="stepList[0].checked"
            id="webOkBut"
            class="confirm"
            type="primary"
            size="small"
            :disabled="generateDisabled"
            @click="confirm()"
          >
            {{ $t('SERVICE_WBS_DLOG_GENERATE') }}
          </el-button>
          <el-button
            v-if="stepList[0].checked"
            id="webCancelBut"
            class="confirm"
            size="small"
            @click="handleDialogClose"
          >
            {{ $t('COMMON_CANCEL') }}
          </el-button>

          <el-button
            v-if="stepList[1].checked"
            id="webConfirmBut"
            class="confirm"
            type="primary"
            size="small"
            :disabled="uploadDiabled"
            @click="submitUpload"
          >
            {{ $t('COMMON_CONFIRM') }}
          </el-button>
          <el-button
            v-if="stepList[1].checked"
            id="webCancelBut"
            class="confirm"
            size="small"
            @click="handleDialogClose(false)"
          >
            {{ $t('COMMON_CANCEL') }}
          </el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<style lang="scss" scoped>
.step-filed {
  display: flex;
  width: 100%;
  margin-bottom: 12px;
  .step {
    display: -webkit-box;
    flex: none;
    float: left;
    &:last-child {
      float: right;
    }
  }

  .step-icon {
    margin-right: 8px;
  }

  .check-label {
    font-weight: 600;
    font-size: 14px;
    line-height: 22px;
    color: var(--o-text-color-primary);
  }

  .uncheck-label {
    font-weight: 600;
    font-size: 14px;
    line-height: 22px;
    color: var(--o-text-color-secondary);
    &:hover {
      color: var(--o-color-primary-secondary);
    }
  }

  .complete {
    color: var(--o-color-primary);
  }

  .el-divider--horizontal {
    margin: 11px 8px !important;
    border-top-color: var(--o-border-color-lighter);
  }
  .next {
    border-top-color: var(--o-color-primary);
  }
}
.server-cert-dialog {
  .el-form-item__label {
    display: flex;
    margin-left: 8px;
  }
  .bc-alert {
    margin-bottom: 16px;
  }

  .el-input__icon.el-icon {
    margin-top: calc(50% - 8px);
  }

  .error-text {
    color: var(--o-color-danger);
    line-height: 16px;
    margin-top: 4px;
  }

  .not-require .el-form-item__label {
    padding-left: 5px;
  }

  .el-form-item__label {
    margin-left: 0;
  }
}
:deep(.el-popper) {
  max-width: 86%;
}

.format-text {
  line-height: 16px;
  margin-top: 4px;
  color: var(--o-text-color-tertiary);
}
.error-item {
  margin-bottom: 0 !important;
  .format-text {
    margin-bottom: 0;
  }

  .file-upload {
    display: grid;
  }
}
</style>

<style lang="scss">
.server-cert-dialog {
  width: 544px !important;
  max-height: fit-content !important;

  .el-dialog__header {
    background: var(--o-bg-color-base);
    box-shadow: inset 0 -1px 0 0 var(--o-border-color-light);
    border-radius: 4px 4px 0 0;
  }

  .el-dialog__body {
    max-height: fit-content !important;
  }

  .el-button {
    padding: 0 12px;
    min-width: 64px;
    height: 24px;
    font-size: 12px;
  }

  .confirmBtn {
    height: 24px;
    font-size: 12px;
  }

  .dialog-footer {
    text-align: center;
  }

  input::-webkit-input-placeholder,
  textarea::-webkit-input-placeholder {
    color: $FONT-COLOR-PLACEHOLDER !important;
    font-size: $FONT-12 !important;
  }

  input::-moz-placeholder,
  textarea::-moz-placeholder {
    color: $FONT-COLOR-PLACEHOLDER !important;
    font-size: $FONT-12 !important;
  }

  .error-input {
    .el-input__inner {
      background: var(--o-color-danger-secondary);
      border-color: var(--o-form-item-border-color_error);
      box-shadow: 0 0 0 1px var(--el-color-danger) inset;
    }
    .el-input__inner:focus {
      border-color: var(--o-form-item-border-color_error);
    }
  }

  .el-form-item {
    &:last-child {
      margin-bottom: 0;
    }
  }
}
.error-dialog {
  .el-dialog__body {
    padding-bottom: 4px !important;
  }
}
</style>
