DeviceStep1.vue 8.98 KB
<template>
  <div class="step1">
    <div class="step1-form">
      <BasicForm @register="register">
        <template #iconSelect>
          <Upload
            name="avatar"
            list-type="picture-card"
            class="avatar-uploader"
            :show-upload-list="false"
            :customRequest="customUpload"
            :before-upload="beforeUpload"
          >
            <img v-if="devicePic" :src="devicePic" alt="avatar" />
            <div v-else>
              <PlusOutlined />
              <div class="ant-upload-text">图片上传</div>
            </div>
          </Upload>
        </template>
        <template #deviceAddress>
          <Input disabled v-model:value="positionState.address">
            <template #addonAfter>
              <EnvironmentTwoTone @click="selectPosition" />
            </template>
          </Input>
        </template>
      </BasicForm>
    </div>
    <Modal
      v-model:visible="visible"
      title="设备位置"
      @ok="handleOk"
      width="800px"
      @cancel="handleCancel"
      centered
    >
      <div>
        <a-form :label-col="labelCol">
          <a-row :gutter="20" class="pt-4 pl-6">
            <a-col :span="8">
              <a-form-item label="经度">
                <Input type="input" v-model:value="positionState.longitude" disabled />
              </a-form-item>
            </a-col>
            <a-col :span="8">
              <a-form-item label="纬度">
                <Input type="input" v-model:value="positionState.latitude" disabled />
              </a-form-item>
            </a-col>
          </a-row>
        </a-form>
        <div ref="wrapRef" style="height: 300px; width: 90%" class="ml-6"></div>
      </div>
    </Modal>
  </div>
</template>
<script lang="ts">
  import { defineComponent, ref, nextTick, unref, reactive, watch } from 'vue';
  import { BasicForm, useForm } from '/@/components/Form';
  import { step1Schemas } from '../../config/data';
  import { useScript } from '/@/hooks/web/useScript';
  import { Input, Divider, Upload, message, Modal, Form, Row, Col } from 'ant-design-vue';
  import { EnvironmentTwoTone, PlusOutlined } from '@ant-design/icons-vue';
  import { upload } from '/@/api/oss/ossFileUploader';
  import { FileItem } from '/@/components/Upload/src/typing';
  import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
  export default defineComponent({
    components: {
      BasicForm,
      Input,
      [Input.Group.name]: Input.Group,
      [Divider.name]: Divider,
      Upload,
      EnvironmentTwoTone,
      // LoadingOutlined,
      PlusOutlined,
      Modal,
      [Form.name]: Form,
      [Form.Item.name]: Form.Item,
      [Row.name]: Row,
      [Col.name]: Col,
    },
    props: {
      deviceInfo: {
        type: Object,
        default: () => ({}),
      },
    },
    emits: ['next'],
    setup(props, { emit }) {
      const devicePic = ref('');
      let positionState = reactive<{
        longitude: string;
        latitude: string;
        description?: string;
        address: string;
      }>({
        longitude: '',
        latitude: '',
        address: '',
      });
      const [register, { validate, resetFields, setFieldsValue, getFieldsValue }] = useForm({
        labelWidth: 100,
        schemas: step1Schemas,
        actionColOptions: {
          span: 14,
        },
        labelAlign: 'left',
        showResetButton: false,
        submitButtonOptions: {
          text: '下一步',
        },
        submitFunc: customSubmitFunc,
      });
      async function customSubmitFunc() {
        try {
          let values = await validate();
          values = { devicePic: devicePic.value, ...positionState, ...values };
          delete values.icon;
          delete values.deviceAddress;
          emit('next', values);
          // 获取输入的数据
        } catch (e) {}
      }

      // 图片上传
      async function customUpload({ file }) {
        if (beforeUpload(file)) {
          const formData = new FormData();
          formData.append('file', file);
          const response = await upload(formData);
          if (response.fileStaticUri) {
            devicePic.value = response.fileStaticUri;
          }
        }
      }
      const beforeUpload = (file: FileItem) => {
        const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
        if (!isJpgOrPng) {
          message.error('只能上传图片文件!');
        }
        const isLt2M = (file.size as number) / 1024 / 1024 < 2;
        if (!isLt2M) {
          message.error('图片大小不能超过2MB!');
        }
        return isJpgOrPng && isLt2M;
      };

      // 地图的弹框
      const visible = ref(false);
      const selectPosition = () => {
        visible.value = true;
        if (!positionState.longitude) {
          positionState.longitude = '104.04666605565338';
          positionState.latitude = '30.543516387560476';
          initMap(positionState.longitude, positionState.latitude);
        } else {
          initMap(positionState.longitude, positionState.latitude);
        }
      };

      // 地图
      const wrapRef = ref<HTMLDivElement | null>(null);
      const { toPromise } = useScript({ src: BAI_DU_MAP_URL });

      async function initMap(longitude, latitude) {
        await toPromise();
        await nextTick();
        const wrapEl = unref(wrapRef);
        const BMap = (window as any).BMap;
        if (!wrapEl) return;
        let preMarker = null;
        const map = new BMap.Map(wrapEl);
        let myIcon = new BMap.Icon(
          'http://api.map.baidu.com/img/markers.png',
          new BMap.Size(40, 25),
          {
            offset: new BMap.Size(0, 0), // 指定定位位置
            imageOffset: new BMap.Size(20, -260), // 设置图片偏移
          }
        );
        const point = new BMap.Point(Number(longitude), Number(latitude));
        let marker = new BMap.Marker(point, { icon: myIcon });
        if (marker) {
          map.removeOverlay(preMarker);
        }
        map.addOverlay(marker);
        preMarker = marker;
        map.centerAndZoom(point, 15);
        map.enableScrollWheelZoom(true);
        map.addEventListener('click', (e) => {
          const { lat, lng } = e.point;
          positionState.latitude = lat + '';
          positionState.longitude = lng + '';
          let gc = new BMap.Geocoder();
          let newPoint = new BMap.Point(lng, lat);

          // 添加锚点
          if (!e.overlay) {
            let marker = new BMap.Marker(e.point, { icon: myIcon });
            map.removeOverlay(preMarker);
            map.addOverlay(marker);
            preMarker = marker;
          }
          //获取详细的地址,精确到街道的名称
          gc.getLocation(newPoint, (rs) => {
            let addComp = rs.addressComponents;
            let addrname = addComp.city + addComp.district + addComp.street + addComp.streetNumber;
            positionState.address = addrname;
          });
        });
      }
      // 确定选择的位置
      const handleOk = () => {
        visible.value = false;
      };
      // 取消选择位置
      const handleCancel = () => {
        for (let key in positionState) {
          positionState[key] = '';
        }
      };
      // 父组件调用更新字段值的方法
      function parentSetFieldsValue(data) {
        setFieldsValue(data);
      }
      // 父组件调用获取字段值的方法
      function parentGetFieldsValue() {
        return getFieldsValue();
      }

      // 父组件调用表单验证
      async function parentValidate() {
        const valid = await validate();
        return valid;
      }

      // 父组件重置图片
      function parentResetDevicePic() {
        devicePic.value = '';
      }
      // 父组件重置位置
      function parentResetPositionState() {
        for (let key in positionState) {
          positionState[key] = '';
        }
      }

      watch(
        () => props.deviceInfo,
        (newValue) => {
          positionState.longitude = newValue.longitude;
          positionState.latitude = newValue.latitude;
          positionState.address = newValue.address;
          devicePic.value = newValue.avatar;
        }
      );

      return {
        resetFields,
        positionState,
        register,
        beforeUpload,
        customUpload,
        selectPosition,
        devicePic,
        visible,
        handleOk,
        handleCancel,
        wrapRef,
        labelCol: { style: { width: '40px' } },
        parentSetFieldsValue,
        parentGetFieldsValue,
        parentValidate,
        parentResetDevicePic,
        parentResetPositionState,
      };
    },
  });
</script>
<style lang="less" scoped>
  .step1 {
    &-form {
      width: 450px;
      margin: 0 auto;
    }

    h3 {
      margin: 0 0 12px;
      font-size: 16px;
      line-height: 32px;
      color: @text-color;
    }

    h4 {
      margin: 0 0 4px;
      font-size: 14px;
      line-height: 22px;
      color: @text-color;
    }

    p {
      color: @text-color;
    }
  }

  .pay-select {
    width: 20%;
  }

  .pay-input {
    width: 70%;
  }
</style>