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

  enum AddressTypeEnum {
    DEC = 'DEC',
    HEX = 'HEX',
  }

  const emit = defineEmits(['update:value']);

  const props = withDefaults(
    defineProps<{
      value?: number | string;
      disabled?: boolean;
      maxValue?: number | string;
      minValue?: number | string;
      type?: string;
      toUpperCase?: boolean;
      disabledSwitch?: boolean;
    }>(),
    {
      inputProps: () => ({}),
      type: 'DEC',
      maxValue: parseInt('FFFF', 16),
      minValue: 0,
      toUpperCase: true,
    }
  );

  const maxHexPadLength = computed(() => {
    const { maxValue, type } = props;
    if (type === AddressTypeEnum.DEC) {
      return Number(maxValue).toString(16).length;
    } else {
      return maxValue.toString().length;
    }
  });

  const inputType = ref(props.type);

  const getInputValue = computed(() => {
    const { type, toUpperCase } = props;
    let { value } = props;
    if (isNaN(value as number)) value = undefined;

    if (isNullOrUnDef(value)) return value;

    if (type === AddressTypeEnum.DEC) {
      if (unref(inputType) === AddressTypeEnum.DEC) {
        return Number(value);
      } else {
        const _value = Number(value).toString(16);
        return toUpperCase ? _value.toUpperCase() : _value;
      }
    } else {
      if (unref(inputType) === AddressTypeEnum.DEC) {
        return parseInt(value, 16);
      } else {
        return toUpperCase ? value.toString().toUpperCase() : value;
      }
    }
  });

  const getValueOpposite = computed(() => {
    if (isNullOrUnDef(unref(getInputValue))) {
      if (unref(inputType) === AddressTypeEnum.DEC) {
        return `0x${decToHex(props.minValue).padStart(unref(maxHexPadLength), '0').toUpperCase()}`;
      } else {
        return hexToDec(props.minValue);
      }
    }
    if (unref(inputType) === AddressTypeEnum.DEC)
      return `0x${decToHex(unref(getInputValue)!)
        .toString()
        .padStart(unref(maxHexPadLength), '0')
        .toUpperCase()}`;
    else return hexToDec(unref(getInputValue)!);
  });

  const addressTypeOptions = [
    { label: AddressTypeEnum.DEC, value: AddressTypeEnum.DEC },
    { label: AddressTypeEnum.HEX, value: AddressTypeEnum.HEX },
  ];

  const getValueByInputType = (value: string | number) => {
    let { type, toUpperCase, maxValue, minValue } = props;

    if (type === AddressTypeEnum.DEC) {
      if (unref(inputType) === AddressTypeEnum.HEX) {
        value = hexToDec(value);
      }
      value = Number(value);
      maxValue = Number(maxValue);
      minValue = Number(minValue);
      value = value > maxValue ? maxValue : value;
      value = value < minValue ? minValue : value;
    } else {
      if (unref(inputType) === AddressTypeEnum.DEC) {
        value = decToHex(value);
      }

      const _maxValue = parseInt(maxValue, 16);
      const _minValue = parseInt(minValue, 16);

      value = parseInt(value, 16) > _maxValue ? maxValue : value;
      value = parseInt(value, 16) < _minValue ? minValue : value;

      value = toUpperCase ? value.toString().toUpperCase() : value;
    }

    return value;
  };

  const validate = (value: string | number) => {
    if (unref(inputType) === AddressTypeEnum.DEC) {
      return /^[0-9]\d*$/.test(value.toString());
    } else {
      return /(0x)?[0-9a-fA-F]+/.test(value.toString());
    }
  };

  const handleSyncValue = (event: ChangeEvent) => {
    const value = (event.target as HTMLInputElement).value;
    if (isNullOrUnDef(value) || value === '') {
      emit('update:value', null);
      return;
    }
    if (!validate(value)) return;
    const syncValue = getValueByInputType(value);
    emit('update:value', syncValue);
  };

  function decToHex(value: number | string) {
    return Number(value).toString(16);
  }

  function hexToDec(value: number | string) {
    return parseInt(value, 16);
  }
</script>

<template>
  <InputGroup compact class="!flex">
    <Select
      v-if="!disabledSwitch"
      v-model:value="inputType"
      :options="addressTypeOptions"
      :disabled="disabled"
      class="bg-gray-200 max-w-20"
    />
    <Input
      :value="getInputValue"
      @change="handleSyncValue"
      :disabled="disabled"
      :placeholder="`请输入${
        inputType === AddressTypeEnum.DEC ? '十进制' : '十六进制'
      }寄存器地址码`"
    />
    <div class="text-center h-8 leading-8 px-2 bg-gray-200 cursor-pointer rounded-1 w-20">
      <div>{{ getValueOpposite }}</div>
    </div>
  </InputGroup>
</template>