index.vue 3.88 KB
<script setup lang="ts">
  import { InputGroup, Select, Input } from 'ant-design-vue';
  import { computed, ref, unref } from 'vue';
  import { InputTypeEnum } from './config';
  import { isNullOrUnDef } from '/@/utils/is';

  const props = withDefaults(
    defineProps<{
      type?: InputTypeEnum;
      value?: number | string;
      hexWithPrefix?: boolean;
      hexUpperCase?: boolean;
      disabled?: boolean;
      maxValue?: number;
      showScaleSelect?: boolean;
    }>(),
    {
      type: InputTypeEnum.HEX,
      hexWithPrefix: false,
      hexUpperCase: true,
      maxValue: Number.MAX_SAFE_INTEGER,
      showScaleSelect: true,
    }
  );

  const emits = defineEmits(['update:value', 'change', 'hexChange']);

  const typOptions = Object.values(InputTypeEnum).map((value) => ({ label: value, value }));

  const inputType = ref(props.type);

  const validateDECReg = /^[\d]+$/;

  const validateHEXReg = /^(0x)?[\da-fA-F]+$/;

  const getMaxValue = computed(() => {
    const { maxValue } = props;
    return maxValue;
  });

  const getDecToHex = (value: number | string) => {
    const { hexUpperCase, hexWithPrefix } = props;
    let hex = Number(value).toString(16);
    hex = hexUpperCase ? hex.toUpperCase() : hex;
    return `${hexWithPrefix ? '0x' : ''}${hex}`;
  };

  const getHexToDec = (value: number | string) => {
    return parseInt(value, 16);
  };

  const getDECValue = (value: number | string) => {
    return parseInt(value, unref(inputType) === InputTypeEnum.HEX ? 16 : 10);
  };

  const getFormatValue = (value: number | string) => {
    if (unref(inputType) === InputTypeEnum.HEX) {
      const { hexUpperCase, hexWithPrefix } = props;
      if (hexUpperCase) value = value.toString().toUpperCase();
      if (hexWithPrefix) value = `0x${value}`;
      return value;
    }

    return value;
  };

  const getInputValue = computed({
    get() {
      const { type, value } = props;

      if (isNullOrUnDef(value)) return;

      if (type === unref(inputType)) return value;

      if (type === InputTypeEnum.HEX && unref(inputType) === InputTypeEnum.DEC) {
        return getHexToDec(value);
      } else {
        return getDecToHex(value);
      }
    },
    set(value: string) {
      const { type } = props;

      if (unref(inputType) === InputTypeEnum.HEX) value = value.replace('0x', '');

      if (!value) {
        emits('update:value', undefined);
        return;
      }

      if (unref(inputType) === InputTypeEnum.HEX && !validateHEXReg.test(value)) return;

      if (unref(inputType) === InputTypeEnum.DEC && !validateDECReg.test(value)) return;

      if (getDECValue(value) > unref(getMaxValue)) return;

      if (type === unref(inputType)) {
        emits('update:value', getFormatValue(value));
        return;
      }

      if (type === InputTypeEnum.HEX && unref(inputType) === InputTypeEnum.DEC) {
        emits('update:value', getDecToHex(value));
      } else {
        emits('update:value', getHexToDec(value));
      }
    },
  });

  const getNegationValue = computed(() => {
    const { hexWithPrefix } = props;
    if (isNullOrUnDef(unref(getInputValue))) return 0;
    return unref(inputType) === InputTypeEnum.HEX
      ? getHexToDec(unref(getInputValue)!)
      : `${hexWithPrefix ? '0x' : ''}${Number(unref(getInputValue)).toString(16).toUpperCase()}`;
  });

  const handleInputTypeChange = (e) => {
    emits('hexChange', e);
  };
</script>

<template>
  <InputGroup class="!flex w-full h-full justify-center items-center" compact>
    <Select
      v-if="showScaleSelect"
      v-model:value="inputType"
      class="min-w-20"
      :options="typOptions"
      :disabled="disabled"
      @change="handleInputTypeChange"
    />
    <Input v-bind="$attrs" v-model:value="getInputValue" class="!w-full" :disabled="disabled" />
    <div class="min-w-14 h-full px-2 flex-grow flex-shrink-0 text-center leading-8 bg-gray-200">
      {{ getNegationValue }}
    </div>
  </InputGroup>
</template>