index.vue 4.18 KB
<script lang="ts" setup>
  import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type';
  import { option } from './config';
  import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';
  import { Spin } from 'ant-design-vue';
  import { computed, ref } from 'vue';
  import { useComponentScale } from '../../../hook/useComponentScale';
  import { useSendCommand } from '../../../hook/useSendCommand';
  import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';

  const props = defineProps<{
    config: ComponentPropsConfigType<typeof option>;
  }>();

  const { getScale } = useComponentScale(props);

  const currentValue = ref(false);

  const getDesign = computed(() => {
    const { option } = props.config;
    const { attribute, attributeRename, attributeName } = option;
    return {
      attribute: attributeRename || attributeName || attribute,
    };
  });

  const { sendCommand, loading } = useSendCommand();
  const handleChange = async (event: Event) => {
    const target = event.target as HTMLInputElement;
    const value = target.checked;

    const flag = await sendCommand(props.config.option, value);
    if (flag) currentValue.value = value;
    flag ? (currentValue.value = value) : (target.checked = !value);
  };

  const updateFn: DataFetchUpdateFn = (message, attribute) => {
    const { data = {} } = message;
    const [latest] = data[attribute] || [];
    const [_, value] = latest;
    currentValue.value = isNaN(value as unknown as number) ? false : !!Number(value);
  };

  useDataFetch(props, updateFn);
</script>

<template>
  <main class="w-full h-full flex flex-col justify-center">
    <DeviceName :config="config" class="text-center" />
    <main class="w-full h-full flex flex-col justify-center items-center">
      <Spin :spinning="loading">
        <label class="sliding-switch" :style="getScale">
          <input
            type="checkbox"
            :value="currentValue"
            :checked="currentValue"
            @change="handleChange"
          />
          <span class="slider"></span>
          <span class="on">ON</span>
          <span class="off">OFF</span>
        </label>
        <div class="text-center mt-2 text-gray-700" :style="getScale">
          {{ getDesign.attribute || '属性' }}
        </div>
      </Spin>
    </main>
  </main>
</template>

<style scoped lang="less">
  :deep(.ant-spin-container) {
    @apply !flex !flex-col justify-center items-center !flex-nowrap;
  }

  .sliding-switch {
    position: relative;
    display: block;
    font-weight: 700;
    line-height: 40px;
    width: 80px;
    height: 40px;
    font-size: 14px;
    cursor: pointer;
    user-select: none;

    input[type='checkbox'] {
      display: none;
    }

    .slider {
      width: 80px;
      height: 40px;
      display: flex;
      align-items: center;
      box-sizing: border-box;
      border: 2px solid #ecf0f3;
      border-radius: 20px;
      box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%),
        inset -2px -2px 8px #fff, inset -2px -2px 12px hsl(0deg 0% 100% / 50%),
        inset 2px 2px 4px hsl(0deg 0% 100% / 10%), inset 2px 2px 8px rgb(0 0 0 / 30%),
        2px 2px 8px rgb(0 0 0 / 30%);
      background-color: #ecf0f3;
      z-index: -1;
    }

    .slider::after {
      cursor: pointer;
      display: block;
      content: '';
      width: 24px;
      height: 24px;
      border-radius: 50%;
      margin-left: 6px;
      margin-right: 6px;
      background-color: #ecf0f3;
      box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%),
        inset 2px 2px 4px hsl(0deg 0% 100% / 10%), 2px 2px 8px rgb(0 0 0 / 30%);
      z-index: 999;
      transition: 0.5s;
    }

    input:checked ~ .off {
      opacity: 0;
    }

    input:checked ~ .slider::after {
      transform: translateX(35px);
    }

    input:not(:checked) ~ .on {
      opacity: 0;
      transform: translateX(0);
    }

    .on,
    .off {
      position: absolute;
      top: 0;
      display: inline-block;
      margin-left: 3px;
      width: 34px;
      text-align: center;
      transition: 0.2s;
    }

    .on {
      color: #039be5;
    }

    .off {
      right: 6px;
      color: #999;
    }
  }
</style>