wenhongquan 3 years ago
parent
commit
5af954de13

+ 3 - 0
package.json

@@ -15,7 +15,9 @@
         "url": "https://gitee.com/y_project/RuoYi-Vue.git"
     },
     "dependencies": {
+        "@amap/amap-jsapi-loader": "^1.0.1",
         "@element-plus/icons-vue": "1.1.4",
+        "@vue/babel-plugin-jsx": "^1.1.1",
         "axios": "0.26.1",
         "echarts": "5.3.2",
         "element-plus": "2.1.8",
@@ -23,6 +25,7 @@
         "fuse.js": "6.5.3",
         "js-cookie": "3.0.1",
         "jsencrypt": "3.2.1",
+        "lodash": "^4.17.21",
         "moment": "^2.29.3",
         "nprogress": "0.2.0",
         "vue": "3.2.31",

BIN
public/icon_map_location@2x.png


BIN
src/assets/images/icon_map_location@2x.png


BIN
src/assets/images/location1.png


+ 188 - 188
src/components/ImageUpload/index.vue

@@ -1,188 +1,188 @@
-<template>
-  <div class="component-upload-image">
-    <el-upload
-      multiple
-      :action="uploadImgUrl"
-      list-type="picture-card"
-      :on-success="handleUploadSuccess"
-      :before-upload="handleBeforeUpload"
-      :limit="limit"
-      :on-error="handleUploadError"
-      :on-exceed="handleExceed"
-      name="file"
-      :on-remove="handleRemove"
-      :show-file-list="true"
-      :headers="headers"
-      :file-list="fileList"
-      :on-preview="handlePictureCardPreview"
-      :class="{ hide: fileList.length >= limit }"
-    >
-      <el-icon class="avatar-uploader-icon"><plus /></el-icon>
-    </el-upload>
-    <!-- 上传提示 -->
-    <div class="el-upload__tip" v-if="showTip">
-      请上传
-      <template v-if="fileSize">
-        大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
-      </template>
-      <template v-if="fileType">
-        格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
-      </template>
-      的文件
-    </div>
-
-    <el-dialog
-      v-model="dialogVisible"
-      title="预览"
-      width="800px"
-      append-to-body
-    >
-      <img
-        :src="dialogImageUrl"
-        style="display: block; max-width: 100%; margin: 0 auto"
-      />
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import { getToken } from "@/utils/auth";
-
-const props = defineProps({
-  modelValue: [String, Object, Array],
-  // 图片数量限制
-  limit: {
-    type: Number,
-    default: 5,
-  },
-  // 大小限制(MB)
-  fileSize: {
-    type: Number,
-    default: 5,
-  },
-  // 文件类型, 例如['png', 'jpg', 'jpeg']
-  fileType: {
-    type: Array,
-    default: () => ["png", "jpg", "jpeg"],
-  },
-  // 是否显示提示
-  isShowTip: {
-    type: Boolean,
-    default: true
-  },
-});
-
-const { proxy } = getCurrentInstance();
-const emit = defineEmits();
-const number = ref(0);
-const uploadList = ref([]);
-const dialogImageUrl = ref("");
-const dialogVisible = ref(false);
-const baseUrl = import.meta.env.VITE_APP_BASE_API;
-const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
-const headers = ref({ Authorization: "Bearer " + getToken() });
-const fileList = ref([]);
-const showTip = computed(
-  () => props.isShowTip && (props.fileType || props.fileSize)
-);
-
-watch(() => props.modelValue, val => {
-  if (val) {
-    // 首先将值转为数组
-    const list = Array.isArray(val) ? val : props.modelValue.split(",");
-    // 然后将数组转为对象数组
-    fileList.value = list.map(item => {
-      if (typeof item === "string") {
-        if (item.indexOf(baseUrl) === -1) {
-          item = { name: baseUrl + item, url: baseUrl + item };
-        } else {
-          item = { name: item, url: item };
-        }
-      }
-      return item;
-    });
-  } else {
-    fileList.value = [];
-    return [];
-  }
-},{ deep: true, immediate: true });
-
-// 删除图片
-function handleRemove(file, files) {
-  emit("update:modelValue", listToString(fileList.value));
-}
-
-// 上传成功回调
-function handleUploadSuccess(res) {
-  uploadList.value.push({ name: res.fileName, url: res.fileName });
-  if (uploadList.value.length === number.value) {
-    fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
-    uploadList.value = [];
-    number.value = 0;
-    emit("update:modelValue", listToString(fileList.value));
-    proxy.$modal.closeLoading();
-  }
-}
-
-// 上传前loading加载
-function handleBeforeUpload(file) {
-  let isImg = false;
-  if (props.fileType.length) {
-    let fileExtension = "";
-    if (file.name.lastIndexOf(".") > -1) {
-      fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
-    }
-    isImg = props.fileType.some(type => {
-      if (file.type.indexOf(type) > -1) return true;
-      if (fileExtension && fileExtension.indexOf(type) > -1) return true;
-      return false;
-    });
-  } else {
-    isImg = file.type.indexOf("image") > -1;
-  }
-  if (!isImg) {
-    proxy.$modal.msgError(
-      `文件格式不正确, 请上传${props.fileType.join("/")}图片格式文件!`
-    );
-    return false;
-  }
-  if (props.fileSize) {
-    const isLt = file.size / 1024 / 1024 < props.fileSize;
-    if (!isLt) {
-      proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
-      return false;
-    }
-  }
-  proxy.$modal.loading("正在上传图片,请稍候...");
-  number.value++;
-}
-
-// 文件个数超出
-function handleExceed() {
-  proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
-}
-
-// 上传失败
-function handleUploadError() {
-  proxy.$modal.msgError("上传图片失败");
-  proxy.$modal.closeLoading();
-}
-
-// 预览
-function handlePictureCardPreview(file) {
-  dialogImageUrl.value = file.url;
-  dialogVisible.value = true;
-}
-
-// 对象转成指定字符串分隔
-function listToString(list, separator) {
-  let strs = "";
-  separator = separator || ",";
-  for (let i in list) {
-    if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
-      strs += list[i].url.replace(baseUrl, "") + separator;
-    }
-  }
-  return strs != "" ? strs.substr(0, strs.length - 1) : "";
-}
-</script>
+<template>
+  <div class="component-upload-image">
+    <el-upload
+      multiple
+      :action="uploadImgUrl"
+      list-type="picture-card"
+      :on-success="handleUploadSuccess"
+      :before-upload="handleBeforeUpload"
+      :limit="limit"
+      :on-error="handleUploadError"
+      :on-exceed="handleExceed"
+      name="file"
+      :on-remove="handleRemove"
+      :show-file-list="true"
+      :headers="headers"
+      :file-list="fileList"
+      :on-preview="handlePictureCardPreview"
+      :class="{ hide: fileList.length >= limit }"
+    >
+      <el-icon class="avatar-uploader-icon"><plus /></el-icon>
+    </el-upload>
+    <!-- 上传提示 -->
+    <div class="el-upload__tip" v-if="showTip">
+      请上传
+      <template v-if="fileSize">
+        大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
+      </template>
+      <template v-if="fileType">
+        格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
+      </template>
+      的文件
+    </div>
+
+    <el-dialog
+      v-model="dialogVisible"
+      title="预览"
+      width="800px"
+      append-to-body
+    >
+      <img
+        :src="dialogImageUrl"
+        style="display: block; max-width: 100%; margin: 0 auto"
+      />
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { getToken } from "@/utils/auth";
+
+const props = defineProps({
+  modelValue: [String, Object, Array],
+  // 图片数量限制
+  limit: {
+    type: Number,
+    default: 10,
+  },
+  // 大小限制(MB)
+  fileSize: {
+    type: Number,
+    default: 50,
+  },
+  // 文件类型, 例如['png', 'jpg', 'jpeg']
+  fileType: {
+    type: Array,
+    default: () => ["png", "jpg", "jpeg"],
+  },
+  // 是否显示提示
+  isShowTip: {
+    type: Boolean,
+    default: false
+  },
+});
+
+const { proxy } = getCurrentInstance();
+const emit = defineEmits();
+const number = ref(0);
+const uploadList = ref([]);
+const dialogImageUrl = ref("");
+const dialogVisible = ref(false);
+const baseUrl = import.meta.env.VITE_APP_BASE_API;
+const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
+const headers = ref({ Authorization: "Bearer " + getToken() });
+const fileList = ref([]);
+const showTip = computed(
+  () => props.isShowTip && (props.fileType || props.fileSize)
+);
+
+watch(() => props.modelValue, val => {
+  if (val) {
+    // 首先将值转为数组
+    const list = Array.isArray(val) ? val : props.modelValue.split(",");
+    // 然后将数组转为对象数组
+    fileList.value = list.map(item => {
+      if (typeof item === "string") {
+        if (item.indexOf(baseUrl) === -1) {
+          item = { name: baseUrl + item, url: baseUrl + item };
+        } else {
+          item = { name: item, url: item };
+        }
+      }
+      return item;
+    });
+  } else {
+    fileList.value = [];
+    return [];
+  }
+},{ deep: true, immediate: true });
+
+// 删除图片
+function handleRemove(file, files) {
+  emit("update:modelValue", listToString(fileList.value));
+}
+
+// 上传成功回调
+function handleUploadSuccess(res) {
+  uploadList.value.push({ name: res.fileName, url: res.fileName });
+  if (uploadList.value.length === number.value) {
+    fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
+    uploadList.value = [];
+    number.value = 0;
+    emit("update:modelValue", listToString(fileList.value));
+    proxy.$modal.closeLoading();
+  }
+}
+
+// 上传前loading加载
+function handleBeforeUpload(file) {
+  let isImg = false;
+  if (props.fileType.length) {
+    let fileExtension = "";
+    if (file.name.lastIndexOf(".") > -1) {
+      fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
+    }
+    isImg = props.fileType.some(type => {
+      if (file.type.indexOf(type) > -1) return true;
+      if (fileExtension && fileExtension.indexOf(type) > -1) return true;
+      return false;
+    });
+  } else {
+    isImg = file.type.indexOf("image") > -1;
+  }
+  if (!isImg) {
+    proxy.$modal.msgError(
+      `文件格式不正确, 请上传${props.fileType.join("/")}图片格式文件!`
+    );
+    return false;
+  }
+  if (props.fileSize) {
+    const isLt = file.size / 1024 / 1024 < props.fileSize;
+    if (!isLt) {
+      proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
+      return false;
+    }
+  }
+  proxy.$modal.loading("正在上传图片,请稍候...");
+  number.value++;
+}
+
+// 文件个数超出
+function handleExceed() {
+  proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
+}
+
+// 上传失败
+function handleUploadError() {
+  proxy.$modal.msgError("上传图片失败");
+  proxy.$modal.closeLoading();
+}
+
+// 预览
+function handlePictureCardPreview(file) {
+  dialogImageUrl.value = file.url;
+  dialogVisible.value = true;
+}
+
+// 对象转成指定字符串分隔
+function listToString(list, separator) {
+  let strs = "";
+  separator = separator || ",";
+  for (let i in list) {
+    if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
+      strs += list[i].url.replace(baseUrl, "") + separator;
+    }
+  }
+  return strs != "" ? strs.substr(0, strs.length - 1) : "";
+}
+</script>

+ 376 - 0
src/components/MapSelect/index.jsx

@@ -0,0 +1,376 @@
+import { defineComponent, onMounted, ref, reactive, watch } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import './index.scss';
+import Map from '@/components/MapView';
+import { ElButton } from 'element-plus';
+
+/** @ts-ignore */
+import icon_location_svg1 from "@/assets/images/location1.png";
+
+/** @ts-ignore */
+import icon_location from '@/assets/images/icon_map_location@2x.png';
+import { cloneDeep } from 'lodash';
+
+
+
+
+
+
+export default defineComponent({
+  name: 'MapSelect',
+  props: {
+    formv: {
+      type: Object,
+      default: { addr: '', locations: '' },
+    },
+    editable: {
+      type: String,
+      default:true
+    }
+  },
+  setup(props, ctx) {
+
+    const form = ref({ addr: '', locations: '' });
+    const route = useRoute();
+
+    const map = ref(null);
+    const state = reactive({
+      // map: map.value,
+      _marker: null ,
+      loading: false,
+    });
+    const mapselect = ref(false);
+
+    watch(
+      () => props.formv.locations,
+      () => {
+        form.value = props.formv;
+        setpoint(
+          Number(form.value.locations.split(',')[0]),
+          Number(form.value.locations.split(',')[1]),
+          form.value.addr);
+      },
+    );
+
+    onMounted(() => {
+
+      if (props.formv.locations) {
+        mapselect.value = false;
+        // form.value = cloneDeep(props.formv);
+        //  console.log(form.value);
+        // setpoint(
+        //   Number(form.value.locations.split(',')[0]),
+        //   Number(form.value.locations.split(',')[1]),
+        //   form.value.addr,
+        // );
+      } else {
+        mapselect.value = true;
+      }
+
+    });
+
+    let allmark = [];
+
+    const selectpoint = () => {
+      if (mapselect.value) {
+        var center = window.map.getCenter();
+        getlocalname(center.lng.toFixed(6) + ',' + center.lat.toFixed(6));
+      } else {
+        mapselect.value = true;
+        state._marker?.remove();
+      }
+    };
+    const setpoint = (longitude, latitude, name) => {
+      if (state._marker) {
+        state._marker.remove();
+      }
+
+      // var el = document.createElement('div');
+      // el.id = 'marker';
+      // el.style.backgroundImage = `url(${icon_location})`;
+      // el.style.backgroundSize = 'cover';
+      // el.style.width = '24px';
+      // el.style.height = '24px';
+      // el.style.borderRadius = '50%';
+
+      var icon = new AMap.Icon({
+    size: new AMap.Size(40, 50),    // 图标尺寸
+    image: '/icon_map_location@2x.png',  // Icon的图像
+    imageSize: new AMap.Size(20, 20)   // 根据所设置的大小拉伸或压缩图片
+    });
+
+
+      state._marker = new AMap.Marker({
+
+    position: new AMap.LngLat(longitude, latitude),
+        offset: new AMap.Pixel(-10, -10),
+        icon: icon,
+
+      })
+      state._marker.setLabel({
+        direction:'top',
+        offset: new AMap.Pixel(0, -10),  //设置文本标注偏移量
+        content: `<div class='info'>${name}</div>`, //设置文本标注内容
+    });
+      window.map.add(state._marker)
+      window.map.setZoomAndCenter(14,
+        [longitude, latitude]
+      )
+
+
+
+        // .setLngLat([longitude, latitude])
+        // .setPopup(popup)
+        // .addTo(map.value);
+      // map.value.flyTo({
+      //   center: [longitude, latitude],
+      //   zoom: 14,
+      //   bearing: 0,
+      //   pitch: 0,
+      //   duration: 2000,
+      // });
+      // state._marker.togglePopup();
+      form.value.addr = name;
+      form.value.locations = longitude + ',' + latitude;
+      // whdata.value.longitude = longitude;
+      mapselect.value = false;
+      props.formv = form.value;
+      ctx.emit('update:formv', form.value);
+    };
+    const searchaddr = (name) => {
+      mapselect.value = false;
+      fetch(
+        `https://restapi.amap.com/v5/place/text?region=南宁市&keywords=${name}&key=${"1c7f1c8eda2ccbe7d0e125a7e2fc2a61"}`,
+        // `${
+        //   import.meta.env.VITE_MAP_SERVER
+        // }/service/lbs/search/v1/keywords?keywords=${name}&city=宿迁&citylimit=true&page_idx=1&page_size=10&key=${
+        //   window.key
+        // }`,
+      )
+        .then((res) => res.json())
+        .then((data) => {
+          // console.log(data.pois)
+          var clearallmark = () => {
+            state._marker?.remove();
+            if (allmark.length > 0) {
+              allmark.map((item) => {
+                item.remove();
+              });
+              allmark = [];
+            }
+          };
+          clearallmark();
+          var top = [];
+          var bottom = [];
+
+          data.pois.map((item) => {
+            if (top.length < 1) {
+              top = [
+                Number(item.location.split(',')[0]),
+                Number(item.location.split(',')[1]),
+              ];
+              bottom = [
+                Number(item.location.split(',')[0]),
+                Number(item.location.split(',')[1]),
+              ];
+            }
+
+
+
+
+             var icon = new AMap.Icon({
+               size: new AMap.Size(40, 50),    // 图标尺寸
+               image: '/icon_map_location@2x.png',  // Icon的图像
+               imageSize: new AMap.Size(20, 20)   // 根据所设置的大小拉伸或压缩图片
+              });
+
+
+            var marker = new AMap.Marker({
+                position: new AMap.LngLat( Number(item.location.split(',')[0]), Number(item.location.split(',')[1])),
+                offset: new AMap.Pixel(-10, -10),
+                icon: icon,
+            })
+            var address = item.pname+item.cityname+item.adname+item.address+`(${item.name})`
+
+            marker.setLabel({
+                direction:'top',
+                offset: new AMap.Pixel(0, -10),  //设置文本标注偏移量
+                content: `<div class='info'>${address}</div>`, //设置文本标注内容
+            });
+            marker.on("click", () => {
+              clearallmark();
+              setpoint(
+                Number(item.location.split(',')[0]),
+                Number(item.location.split(',')[1]),
+                address,
+              );
+
+            })
+            window.map.add(marker)
+            // window.map.setZoomAndCenter(14,
+            //   [longitude, latitude]
+            // )
+
+
+
+
+            allmark.push(marker);
+
+            if (top[0] < Number(item.location.split(',')[0])) {
+              top[0] = Number(item.location.split(',')[0]);
+            }
+            if (top[1] > Number(item.location.split(',')[1])) {
+              top[1] = Number(item.location.split(',')[1]);
+            }
+            if (bottom[0] > Number(item.location.split(',')[0])) {
+              bottom[0] = Number(item.location.split(',')[0]);
+            }
+            if (bottom[1] < Number(item.location.split(',')[1])) {
+              bottom[1] = Number(item.location.split(',')[1]);
+            }
+          });
+
+          window.map.setBounds( new AMap.Bounds(top, bottom));
+        });
+    };
+
+    const getlocalname = (location) => {
+      fetch(
+        `https://restapi.amap.com/v3/geocode/regeo?location=${location}&key=1c7f1c8eda2ccbe7d0e125a7e2fc2a61`,
+      //   `${
+      //     import.meta.env.VITE_MAP_SERVER as string
+      //   }/service/lbs/reverse/v1/regeo?location=${location}&key=${window.key}`,
+      )
+        .then((res) => res.json())
+        .then((data) => {
+          setpoint(
+            Number(location.split(',')[0]),
+            Number(location.split(',')[1]),
+            data.regeocode.formatted_address,
+          );
+        });
+    };
+
+    const getLocation = () => {
+
+      if (navigator.geolocation) {
+        navigator.geolocation.getCurrentPosition(
+          (location1) => {
+            var lat = null;
+            var lon = null;
+            var iserror = false;
+            try {
+              lat = location1.coords.latitude;
+              lon = location1.coords.longitude;
+              if (lat === null || lat === undefined) iserror = true;
+            } catch (e) {
+              iserror = true;
+            }
+            if (!iserror) {
+              //  debugger
+              var location = `${lon},${lat}`;
+              getlocalname(location);
+              //  setpoint(lon,lat,data.regeocodes[0].formatted_address)
+            } else {
+              // Notify({
+              //   type: 'danger',
+              //   message: 'App不支持地理定位。',
+              // });
+            }
+          },
+          (error) => {
+            switch (error.code) {
+              case error.PERMISSION_DENIED:
+                alert('定位失败,用户拒绝请求地理定位');
+                break;
+              case error.POSITION_UNAVAILABLE:
+                alert('定位失败,位置信息是不可用');
+                break;
+              case error.TIMEOUT:
+                alert('定位失败,请求获取用户位置超时');
+                break;
+            }
+          },
+        );
+      } else {
+        alert('浏览器不支持地理定位。');
+      }
+      // api_getusergps().then((res) => {
+      //   var lat = null;
+      //   var lon = null;
+      //   var iserror = false;
+      //   try {
+      //     lat = res.result[0].lat;
+      //     lon = res.result[0].lon;
+      //     if (lat === null || lat === undefined) iserror = true;
+      //   } catch (e) {
+      //     iserror = true;
+      //   }
+      //   if (!iserror) {
+      //     //  debugger
+      //     var location = `${lon},${lat}`;
+      //     getlocalname(location);
+      //     //  setpoint(lon,lat,data.regeocodes[0].formatted_address)
+      //   } else {
+      //     // Notify({
+      //     //   type: 'danger',
+      //     //   message: 'App不支持地理定位。',
+      //     // });
+      //   }
+      // });
+    };
+
+
+    return {
+      map,
+      mapselect,
+      searchaddr,
+      selectpoint,
+      getLocation,
+    };
+  },
+  render() {
+    return (
+      <div class={"mapselect-c"}>
+        <Map
+          v-model:map={this.map}
+          style={{
+            borderRadius: "2px",
+            boxShadow: "0px 0px 0px 1px #d9d9d9;",
+            height: "300px",
+          }}
+        />
+        <ElButton
+          type="primary"
+          size="small"
+          onClick={this.selectpoint}
+          style={`position:absolute;right:10px;top:10px;${
+            this.editable ? "" : "display:none"
+          }`}
+        >
+          {this.mapselect ? "确定" : "选点"}
+        </ElButton>
+        <ElButton
+          type="text"
+          // size="large"
+          onClick={this.getLocation}
+          style={`position:absolute;right:10px;bottom:10px;${
+            this.editable ? "" : "display:none"
+          }`}
+        >
+          <el-icon size="25">
+            <img src={icon_location_svg1} style={ 'width:30px'}></img>
+          </el-icon>
+        </ElButton>
+
+        <img
+          style={
+            "width:20px;position:absolute;top:48%;left:48%;" +
+            `${this.mapselect ? "" : "display:none"}`
+          }
+          src={icon_location}
+        />
+      </div>
+    );
+  }
+});

+ 22 - 0
src/components/MapSelect/index.scss

@@ -0,0 +1,22 @@
+.mapselect-c {
+    min-width: 200px;
+    min-height: 100px;
+    width: 100%;
+    position: relative;
+}
+
+.amap-marker-label {
+    border: 0;
+    background-color: transparent;
+}
+
+.info {
+    position: relative;
+    margin: 0;
+    top: 0;
+    right: 0;
+    min-width: 0;
+    color: #333;
+    background: #fff;
+    padding: 4px 10px
+}

+ 60 - 0
src/components/MapView/index.vue

@@ -0,0 +1,60 @@
+<template>
+   <div id="container"></div>
+</template>
+<script lang="ts" >
+import {defineComponent,onMounted} from 'vue'
+import AMapLoader from '@amap/amap-jsapi-loader';
+import { shallowRef } from '@vue/reactivity'
+
+export default defineComponent({
+   name: 'MapSelect',
+  props: {
+    map: Object,
+  },
+ setup(props, ctx) {
+   window._AMapSecurityConfig = {
+            securityJsCode:'ec6f8c65a04b41e168298850f64b9739',
+  }
+   const map = shallowRef(null);
+   const initMap = ()=>{
+        AMapLoader.load({
+            key:"33424b3226727e5038f0756003f50060",             // 申请好的Web端开发者Key,首次调用 load 时必填
+            version:"2.0",      // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
+            plugins:[''],       // 需要使用的的插件列表,如比例尺'AMap.Scale'等
+        }).then((AMap)=>{
+            map.value = new AMap.Map("container",{  //设置地图容器id
+                viewMode:"3D",    //是否为3D地图模式
+                zoom:10,           //初始化地图级别
+                center:[108.320004,22.82402], //初始化地图中心点位置
+            });
+            window.map = map.value;
+            ctx.emit('update:map', map);
+        }).catch(e=>{
+            console.log(e);
+        })
+    };
+    onMounted(()=>{
+      initMap();
+
+    })
+
+ }
+
+});
+
+
+
+
+
+
+
+</script>
+
+<style  scoped>
+    #container{
+        padding:0px;
+        margin: 0px;
+        width: 100%;
+        height: 800px;
+    }
+</style>

+ 228 - 0
src/components/Task/Add/index.vue

@@ -0,0 +1,228 @@
+<template>
+    <div class="taskadd">
+        <el-form :model="taskinfo" label-width="120px">
+            <el-row>
+                <el-col :span="12">
+                    <el-form-item label="事件来源:">
+
+                        <el-select v-model="taskinfo.taskType" class="m-1" placeholder="请选择">
+                            <el-option v-for="item in task_type" :label="item.label" :value="item.value"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="班组区域:">
+
+                        <el-select v-model="taskinfo.taskDeptRange" class="m-1" placeholder="请选择">
+                            <el-option v-for="item in alldept" :label="item.deptName" :value="item.deptId"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="来件时间:">
+
+                        <el-date-picker v-model="taskinfo.taskTime" type="datetime" placeholder="请选择" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="要求完成时间:">
+
+                        <el-date-picker v-model="taskinfo.taskReqCompleteTime" type="datetime" placeholder="请选择" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="事件分类:">
+                        <el-select v-model="taskinfo.taskEventCategory" class="m-1" placeholder="请选择">
+                            <el-option v-for="item in task_event_category" :label="item.label" :value="item.value"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="事件类型:">
+
+                        <el-select v-model="taskinfo.taskEventType" class="m-1" placeholder="请选择">
+                            <el-option v-for="item in task_event_type" :label="item.label" :value="item.value"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="任务内容:">
+                        <el-input v-model="taskinfo.taskContent" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="设施编号:">
+                        <el-input v-model="taskinfo.taskFacilitieCode" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="来件备注:">
+                        <el-input v-model="taskinfo.taskFromRemark" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="投诉人电话">
+                        <el-input v-model="taskinfo.taskComplainConnect" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="道路名">
+                        <el-input v-model="taskinfo.taskAddrRoad" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="其他工单号">
+                        <el-input v-model="taskinfo.taskOtherId" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="事件地址">
+                        <div style="position:relative;width:100%">
+                            <div style="width:80%">
+                                <el-input v-model="taskinfo.taskAddr" />
+                            </div>
+                            <el-button type="primary" style="position:absolute;right:0;top:0" @click="searchaddr">查询</el-button>
+                        </div>
+
+
+
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <el-form-item label="照片附件">
+
+                    </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                    <div style="padding-left:30px">
+                        <MapSelect ref="mapSelect" v-model:formv="formlocation"></MapSelect>
+                    </div>
+
+                </el-col>
+
+                <el-col :span="12">
+                    <div style="padding-left:120px">
+                        <ImageUpload v-model="fileList"></ImageUpload>
+
+                    </div>
+
+                </el-col>
+            </el-row>
+
+
+
+
+        </el-form>
+
+
+
+    </div>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+import { useDict } from '@/utils/dict';
+import { cloneDeep } from 'lodash';
+import { listDept, getDept } from "@/api/system/dept";
+import MapSelect from '@/components/MapSelect';
+import { Plus } from '@element-plus/icons-vue'
+// 文件上传组件
+import ImageUpload from "@/components/ImageUpload"
+
+
+const mapSelect = ref(null);
+const formlocation = ref({ addr: '', locations: '' });
+
+watch(
+    () => formlocation.value.addr,
+    () => {
+        taskinfo.value.taskAddr = formlocation.value.addr;
+    },
+);
+watch(
+    () => formlocation.value.locations,
+    () => {
+        taskinfo.value.taskLocation = formlocation.value.locations;
+    },
+);
+
+
+const searchaddr = () => {
+
+    if (taskinfo.value.taskAddr) {
+        mapSelect.value.searchaddr(taskinfo.value.taskAddr);
+    } else {
+        ElMessage.warning('请输入地址');
+    }
+
+};
+const alldept = ref([]);
+listDept().then(response => {
+    alldept.value = cloneDeep(response.data);
+});
+const { task_status, task_type, task_event_type, task_event_category } = useDict("task_status", "task_type", "task_event_type", "task_event_category");
+
+const fileList = ref([])
+
+
+
+const taskinfo = ref({
+    name: "",
+    taskName: null,
+    taskContent: null,
+    taskAddr: null,
+    taskLocation: null,
+    taskCode: null,
+    taskType: null,
+    taskEventType: null,
+    taskEventCategory: null,
+    taskFromRemark: null,
+    taskAddrRoad: null,
+    taskOtherId: null,
+    taskTime: null,
+    taskReqCompleteTime: null,
+    status: 1,
+    taskCreater: null,
+    taskReporter: null,
+    taskPics: null,
+    taskVideos: null,
+    taskComplainConnect: null,
+    taskDeptRange: "",
+    taskFacilitieCode: ""
+})
+</script>
+
+<style lang="scss" scoped>
+.taskadd {
+    .el-select {
+        width: 100% !important;
+    }
+}
+</style>
+
+<style>
+.el-date-editor {
+    width: 100% !important;
+}
+
+.el-upload--picture-card {
+    --el-upload-picture-card-size: 100px !important;
+}
+
+.el-upload-list--picture-card {
+    --el-upload-list-picture-card-size: 100px;
+}
+</style>

+ 16 - 16
src/utils/dict.js

@@ -1,17 +1,17 @@
-import { getDicts } from '@/api/system/dict/data'
-
-/**
- * 获取字典数据
- */
-export function useDict(...args) {
-  const res = ref({});
-  return (() => {
-    args.forEach((d, index) => {
-      res.value[d] = [];
-      getDicts(d).then(resp => {
-        res.value[d] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass }))
-      })
-    })
-    return toRefs(res.value);
-  })()
+import { getDicts } from '@/api/system/dict/data'
+
+/**
+ * 获取字典数据
+ */
+export function useDict(...args) {
+    const res = ref({});
+    return (() => {
+        args.forEach((d, index) => {
+            res.value[d] = [];
+            getDicts(d).then(resp => {
+                res.value[d] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, remark: p.remark }))
+            })
+        })
+        return toRefs(res.value);
+    })()
 }

+ 128 - 64
src/views/task/list/index.vue

@@ -1,98 +1,162 @@
 <template>
-  <div style="padding:10px">
+  <div style="padding: 10px">
     <div class="formbody">
-      <el-form :inline="true" v-model="queryparameters" class="demo-form-inline">
-         <el-form-item label="任务时间">
-           <el-date-picker
-             v-model="queryparameters.times"
-             type="daterange"
-             range-separator="到"
-             start-placeholder="开始时间"
-             end-placeholder="结束时间"
-           />
-         </el-form-item>
-          <el-form-item label="任务状态">
-            <el-select  v-model="queryparameters.status" class="m-1" placeholder="请选择" >
-             </el-select>
-         </el-form-item>
-          <el-form-item label="区属">
-            <el-select v-model="queryparameters.area" class="m-2" placeholder="请选择" >
-             </el-select>
-         </el-form-item>
-         <el-form-item>
-      <el-button type="primary" >查询</el-button>
-    </el-form-item>
-    <el-form-item>
-      <el-button type="success" >新增</el-button>
-    </el-form-item>
-       </el-form>
-
-
-        <el-table :data="tableData" class="eltable" height="250" style="width: 100%">
-    <el-table-column prop="date" label="Date" width="180" />
-    <el-table-column prop="name" label="Name" width="180" />
-    <el-table-column prop="address" label="Address" />
-  </el-table>
+      <el-form
+        :inline="true"
+        v-model="queryparameters"
+        class="demo-form-inline"
+      >
+        <el-form-item label="任务时间">
+          <el-date-picker
+            v-model="queryparameters.times"
+            type="daterange"
+            range-separator="到"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+          />
+        </el-form-item>
+        <el-form-item label="任务状态">
+          <el-select
+            v-model="queryparameters.status"
+            class="m-1"
+            placeholder="请选择"
+          >
+          <el-option v-for="item in task_status" :label="item.label" :value="item.value"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="区属">
+          <el-select
+            v-model="queryparameters.area"
+            class="m-2"
+            placeholder="请选择"
+          >
+          <el-option v-for="item in area" :label="item.label" :value="item.value"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary">查询</el-button>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="success" @click="showadd= true">新增</el-button>
+        </el-form-item>
+      </el-form>
+
+      <el-table
+        :data="tableData"
+        class="eltable"
+        height="250"
+        style="width: 100%"
+      >
+        <el-table-column prop="date" label="序号" width="50" />
+        <el-table-column prop="name" label="工单号" width="180" />
+        <el-table-column prop="address" label="行政区" />
+        <el-table-column prop="address" label="事件来源" />
+        <el-table-column prop="address" label="状态" />
+        <el-table-column prop="address" label="道路名" />
+        <el-table-column prop="address" label="任务内容" />
+        <el-table-column prop="address" label="来件时间" />
+        <el-table-column prop="address" label="操作" />
+      </el-table>
+      <div style="text-align: right; margin-top: 10px; width: 100%">
+        <el-pagination
+          style="float: right"
+          small
+          background
+          layout="prev, pager, next"
+          :total="50"
+          class="mt-4"
+        />
+      </div>
     </div>
+
+    <el-dialog v-model="showadd" title="新增任务" width="70%" draggable>
+     <div>
+
+     <TaskAdd></TaskAdd>
+
+     </div>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="showadd = false">取消</el-button>
+        <el-button type="primary" @click="showadd = false"
+          >保存</el-button
+        >
+      </span>
+    </template>
+  </el-dialog>
   </div>
 </template>
-<script lang="ts" setup>
-import { defineComponent,ref,reactive } from 'vue'
+<script setup>
+import { defineComponent, ref, reactive,onMounted } from "vue";
+import { useDict } from '@/utils/dict';
+import  TaskAdd  from '@/components/Task/Add'
+
+
+const showadd = ref(false);
+
 
-    const queryparameters = ref({times:"",status:"",area:""})
-    const tableData = [
+const {task_status} = useDict("task_status");
+
+const {area} = useDict("area");
+
+
+const queryparameters = ref({ times: "", status: "", area: "" });
+const tableData = [
   {
-    date: '2016-05-03',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+    date: "1",
+    name: "Tom",
+    address: "No. 189, Grove St, Los Angeles",
   },
   {
-    date: '2016-05-02',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+    date: "2",
+    name: "Tom",
+    address: "No. 189, Grove St, Los Angeles",
   },
   {
-    date: '2016-05-04',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+    date: "3",
+    name: "Tom",
+    address: "No. 189, Grove St, Los Angeles",
   },
   {
-    date: '2016-05-01',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+    date: "4",
+    name: "Tom",
+    address: "No. 189, Grove St, Los Angeles",
   },
   {
-    date: '2016-05-08',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+    date: "5",
+    name: "Tom",
+    address: "No. 189, Grove St, Los Angeles",
   },
   {
-    date: '2016-05-06',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+    date: "6",
+    name: "Tom",
+    address: "No. 189, Grove St, Los Angeles",
   },
   {
-    date: '2016-05-07',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+    date: "7",
+    name: "Tom",
+    address: "No. 189, Grove St, Los Angeles",
   },
-]
+];
 
+onMounted(()=>{
 
-</script>
 
+})
+
+</script>
 
 <style lang="scss" scoped>
-.formbody{
+.formbody {
   background: rgb(247, 247, 247);
   width: 100%;
   border-radius: 5px;
   min-height: 90vh;
   padding: 15px 10px;
-  .eltable{
-    border: 1px solid rgb(199, 199, 199);
+  .eltable {
+    border: 1px solid #e5e9f2;
     border-radius: 5px;
+    min-height: 80vh;
   }
 }
-
 </style>

+ 6 - 0
vite.config.js

@@ -7,6 +7,7 @@ export default defineConfig(({ mode, command }) => {
     const env = loadEnv(mode, process.cwd())
     const { VITE_APP_ENV } = env
     return {
+
         // 部署生产环境和开发环境下的URL。
         // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
         // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
@@ -23,6 +24,11 @@ export default defineConfig(({ mode, command }) => {
             // https://cn.vitejs.dev/config/#resolve-extensions
             extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"],
         },
+        esbuild: {
+            jsxFactory: 'h',
+            jsxFragment: 'Fragment',
+            jsxInject: "import { h } from 'vue';"
+        },
         // vite 相关配置
         server: {
             port: 8085,

+ 16 - 15
vite/plugins/index.js

@@ -1,15 +1,16 @@
-import vue from '@vitejs/plugin-vue'
-
-import createAutoImport from './auto-import'
-import createSvgIcon from './svg-icon'
-import createCompression from './compression'
-import createSetupExtend from './setup-extend'
-
-export default function createVitePlugins(viteEnv, isBuild = false) {
-    const vitePlugins = [vue()]
-    vitePlugins.push(createAutoImport())
-	vitePlugins.push(createSetupExtend())
-    vitePlugins.push(createSvgIcon(isBuild))
-	isBuild && vitePlugins.push(...createCompression(viteEnv))
-    return vitePlugins
-}
+import vue from '@vitejs/plugin-vue'
+import vuejsx from "@vue/babel-plugin-jsx";
+
+import createAutoImport from './auto-import'
+import createSvgIcon from './svg-icon'
+import createCompression from './compression'
+import createSetupExtend from './setup-extend'
+
+export default function createVitePlugins(viteEnv, isBuild = false) {
+    const vitePlugins = [vue(), vuejsx({})]
+    vitePlugins.push(createAutoImport())
+    vitePlugins.push(createSetupExtend())
+    vitePlugins.push(createSvgIcon(isBuild))
+    isBuild && vitePlugins.push(...createCompression(viteEnv))
+    return vitePlugins
+}