浏览代码

feat: 移植map与settting

gemercheung 11 月之前
父节点
当前提交
7bf02b0dab

+ 15 - 0
map.html

@@ -0,0 +1,15 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="icon" type="image/ico" href="/favicon.ico" id="app-icon" />
+</head>
+
+<body>
+  <div id="app"></div>
+  <script type="module" src="/src/app/map/main.ts"></script>
+</body>
+
+</html>

+ 1 - 0
src/app/fire/routeConfig.ts

@@ -17,6 +17,7 @@ export const menuRouteNames = [
   FireRouteName.downloadLog,
   FireRouteName.role,
   FireRouteName.user,
+  FireRouteName.setting,
 ];
 
 export const routes: Routes = [

+ 333 - 0
src/app/map/App.vue

@@ -0,0 +1,333 @@
+<template>
+  <!-- <div>地图页面</div> -->
+  <div class="tabbar">
+    <div class="nav">
+      <el-button-group class="ml-4">
+        <el-button
+          :type="currentType(0) ? 'primary' : 'default'"
+          @click="handleSelect(0)"
+          >地图</el-button
+        >
+        <el-button
+          :type="currentType(1) ? 'primary' : 'default'"
+          @click="handleSelect(1)"
+          >卡片</el-button
+        >
+      </el-button-group>
+    </div>
+    <el-form-item label="所属架构:" class="filter">
+      <com-company v-model="state.deptId" :id="state.caseId" hideAll />
+    </el-form-item>
+  </div>
+  <div ref="mapEl" class="map-container" v-show="currentType(0)"></div>
+  <div class="card-container" v-show="currentType(1)">
+    <div class="card-list">
+      <template v-for="item of list" v-if="list.length > 0">
+        <el-card
+          style="width: 480px"
+          shadow="hover"
+          @click="handCardClick(item.caseId)"
+        >
+          <img
+            class="cover"
+            :src="
+              item.cover
+                ? item.cover
+                : 'https://test-mix3d.4dkankan.com/code/assets/pic.d5781b0c.jpg'
+            "
+            style="width: 100%"
+          />
+          <div class="card">
+            <span> 项目: {{ item.projectName }}</span>
+            <span> 地址: {{ item.projectAddress }}</span>
+            <span> 类别: {{ item.projectSite }}</span>
+          </div>
+        </el-card>
+      </template>
+      <template v-else>
+        <div class="no-data">
+          <img :src="emptyBG" />
+          <span>暂无数据</span>
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, ref, computed, onBeforeMount } from "vue";
+// import { useRouteQuery } from "@vueuse/router";
+import AMapLoader from "@amap/amap-jsapi-loader";
+import axios from "axios";
+import { getFuseCodeLink } from "../../view/case/help";
+import comCompany from "./company-select/index.vue";
+import { reactive } from "vue";
+import { watch } from "vue";
+import { ElLoading } from "element-plus";
+import linkIco from "@/assets/image/fire.ico";
+import { useUrlSearchParams } from "@vueuse/core";
+import emptyBG from "@/assets/image/empty__empty.png";
+
+const params = useUrlSearchParams("history");
+console.log("params", params.deptId);
+
+const current = ref(0);
+const list = ref<any>([]);
+
+const state = reactive({
+  deptId: params.deptId || "",
+  // caseId: params.caseId || "",
+});
+
+const link = document.querySelector<HTMLLinkElement>("#app-icon")!;
+link.setAttribute("href", linkIco);
+
+document.title = "案件显示";
+
+const currentType = computed(() => (type: number) => current.value === type);
+const handleSelect = (type: number) => {
+  current.value = type;
+};
+
+const markers = ref<any>([]);
+
+const getQuery = (
+  caseId: number,
+  share: boolean = false,
+  single: boolean = false
+) =>
+  `${getFuseCodeLink(caseId, true)}${share ? "&share=1" : ""}${
+    single ? "&single=1" : ""
+  }#show/summary`;
+
+const request = axios.create({
+  baseURL: "",
+  timeout: 1000,
+  headers: {
+    share: 1,
+    "Content-Type": "application/json",
+  },
+});
+const mapEl = ref<HTMLDivElement>();
+let AMap, map;
+
+const queryURL = `${import.meta.env.VITE_SEVER_URL}/fusion-xj/web/fireProject/queryProject`
+
+// debugger;
+const getDataQuest = () => {
+  return new Promise(async (reslove, reject) => {
+    const res = await request.post(
+      queryURL,
+      {
+        pageNum: 1,
+        pageSize: 10000,
+        deptId: state.deptId,
+      }
+    );
+    console.log("res.data", res);
+    if (res.status === 200 && res.data.code === 0) {
+      reslove(res.data.data.list);
+    } else {
+      reslove([]);
+    }
+  });
+};
+
+const refresh = async () => {
+  const loading = ElLoading.service({
+    lock: true,
+    text: "Loading",
+    background: "rgba(0, 0, 0, 0.7)",
+  });
+  map.remove(markers.value);
+  markers.value = [];
+  initMakers();
+  loading.close();
+};
+
+watch(
+  () => state.deptId,
+  () => {
+    refresh();
+  },
+  {
+    immediate: false,
+    deep: true,
+  }
+);
+const initMakers = async () => {
+  const data = (await getDataQuest()) as any as any[];
+  console.log("data", data);
+  const positions: any[] = [];
+  list.value = data as any[];
+  Array.from(data).forEach((item: any) => {
+    // console.log(item)
+    const latlng = item.latlng;
+    const coord = latlng.split(",");
+
+    console.log("coord", coord, item.caseId);
+    const url = getQuery(item.caseId, true);
+    console.log("url", url);
+    const icon = new AMap.Icon({
+      image:
+        "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
+      size: new AMap.Size(22, 28), //图标所处区域大小
+      imageSize: new AMap.Size(22, 28), //图标大小
+    });
+
+    const pos = coord.reverse();
+    positions.push(pos);
+    const marker = new AMap.Marker({
+      icon: icon,
+      position: pos,
+      title: item.title,
+      label: item.title,
+      extData: { url: url, id: item.caseId },
+      // offset: new AMap.Pixel(-26, -54),
+    });
+    markers.value.push(marker);
+    marker.setMap(map);
+
+    marker.on("click", () => {
+      const data = marker.getExtData();
+      window.open(data.url);
+      console.log("click", data);
+    });
+  });
+
+  var polygon = new AMap.Polygon({
+    path: positions,
+    map: map,
+    strokeOpacity: 0, //透明
+    fillOpacity: 0, //透明
+    bubble: true, //事件穿透到地图
+  });
+  var overlaysList = map.getAllOverlays("polygon"); //获取多边形图层
+  map.setFitView(); //自适应显示
+};
+const loadMap = async () => {
+  AMap = await AMapLoader.load({
+    plugins: ["AMap.PlaceSearch"],
+    key: "e661b00bdf2c44cccf71ef6070ef41b8",
+    version: "2.0",
+  });
+
+  map = new AMap.Map(mapEl.value, {
+    WebGLParams: {
+      preserveDrawingBuffer: true,
+    },
+    resizeEnable: true,
+  });
+
+  //添加插件
+  AMap.plugin(
+    ["AMap.ToolBar", "AMap.Scale", "AMap.HawkEye", "AMap.MapType"],
+    function () {
+      //异步同时加载多个插件
+      // map.addControl(new AMap.HawkEye()); //显示缩略图
+      map.addControl(new AMap.Scale()); //显示当前地图中心的比例尺
+      map.addControl(new AMap.MapType()); //显示当前地图中心的比例尺
+    }
+  );
+  console.log("map", map);
+
+  initMakers();
+};
+
+onMounted(() => {
+  // console.log("caseId", caseId);
+  loadMap();
+});
+const handCardClick = (id: number) => {
+  const url = getQuery(id, true);
+  console.log("url", url);
+  window.open(url);
+};
+</script>
+<style>
+body {
+  padding: 0;
+  margin: 0;
+}
+
+.map-container {
+  width: 100%;
+  height: 100vh;
+}
+
+.tabbar {
+  display: flex;
+  width: 100%;
+  position: fixed;
+  top: 30px;
+  z-index: 10000;
+  justify-items: center;
+  justify-content: center;
+}
+
+.tabbar .nav {
+  display: flex;
+  /* background: white; */
+  justify-content: center;
+  align-items: center;
+}
+.el-form-item {
+  margin-bottom: 0px !important;
+}
+
+.tabbar .nav .nav_item {
+  padding: 10px;
+  cursor: pointer;
+}
+
+.card-container {
+  width: 100%;
+  padding-top: 100px;
+}
+
+.card-list {
+  margin: 0 auto;
+
+  display: flex;
+  gap: 50px 25px;
+  width: 90%;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: center;
+}
+
+.cover {
+  cursor: pointer;
+  max-height: 220px;
+  object-fit: cover;
+}
+
+.card {
+  display: flex;
+  flex-direction: column;
+  cursor: pointer;
+  gap: 10px;
+}
+
+.filter {
+  margin: 0;
+  margin-left: 20px;
+}
+
+.amap-ctrl-list-layer {
+  z-index: 100000;
+}
+.no-data {
+  width: 100%;
+  height: 100%;
+  /* background: red; */
+  min-height: 530px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+}
+.no-data span {
+  color: #999;
+}
+</style>

+ 55 - 0
src/app/map/company-select/index.vue

@@ -0,0 +1,55 @@
+<template>
+  <el-cascader
+    style="width: 100%"
+    v-model="state.path"
+    :disabled="disabled"
+    placeholder="勘验单位:"
+    :options="state.options"
+    :props="{ expandTrigger: 'hover', checkStrictly: true }"
+  />
+</template>
+
+<script setup lang="ts">
+import { useTreeSelect, Props, TreeOption } from "@/hook/treeSelect";
+import { getOrganizationTree } from "./organization";
+import { watchEffect } from "vue";
+
+const emit = defineEmits<{
+  (e: "update:data", data: TreeOption | null): void;
+  (e: "update:modelValue", value: string): void;
+  (e: "update:label", value: string): void;
+  (e: "update:path", path: string[]): void;
+}>();
+const props = defineProps<Props>();
+
+const state = useTreeSelect(
+  props,
+  getOrganizationTree,
+  (val) => emit("update:modelValue", val),
+  {
+    label: "name",
+    level: "level",
+  }
+);
+
+watchEffect(() => {
+  emit("update:data", state.currentOption);
+});
+watchEffect(() => {
+  emit("update:label", state.label);
+});
+watchEffect(() => {
+  emit("update:path", state.path);
+});
+</script>
+
+<style scoped>
+.aaa::after {
+  content: "";
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+}
+</style>

+ 68 - 0
src/app/map/company-select/organization.ts

@@ -0,0 +1,68 @@
+import {
+  addTreeitem,
+  axios,
+  delTreeitem,
+  editTreeitem,
+  getTreeselect,
+} from "@/request";
+import { useUrlSearchParams } from "@vueuse/core";
+const params = useUrlSearchParams("history");
+console.log("params", params.deptId);
+
+
+const request = axios.create({
+  baseURL: `${import.meta.env.VITE_SEVER_URL}/`,
+  timeout: 1000,
+  headers: {
+    share: 1,
+    'Content-Type': 'application/json'
+  },
+});
+
+export enum DeptType {
+  corps = 1,
+  detachment = 2,
+  brigade = 3,
+}
+
+export type Organization = {
+  ancestors: string;
+  children?: Organization[];
+  deptType: DeptType;
+  id: string;
+  leader: string;
+  level: number;
+  name: string;
+  parentId: string;
+  parentName: string;
+  phone: string;
+  remark: string;
+};
+
+export const getOrganizationTree = async (type?: string) =>
+  (await axios.get<Organization[]>(getTreeselect, { headers: { share: 1 }, params: { type, deptId: params.deptId, ingoreRes: true, share: 1, } })).data;
+
+export const addOrganization = async (
+  dept: Omit<Organization, "id">,
+  deptIds: string[]
+) => {
+  await axios.post(addTreeitem, {
+    superior: "sheq",
+    ...dept,
+    deptIdList: deptIds.join(","),
+  });
+};
+
+export const setOrganization = async (
+  dept: Organization,
+  deptIds: string[]
+) => {
+  await axios.post(editTreeitem, {
+    superior: "sheq",
+    ...dept,
+    deptIdList: deptIds.join(","),
+  });
+};
+
+export const delOrganization = async (deptId: string) =>
+  axios.post(delTreeitem + deptId);

+ 14 - 0
src/app/map/main.ts

@@ -0,0 +1,14 @@
+import { createApp } from "vue";
+import "element-plus/dist/index.css";
+import * as ElementPlusIconsVue from "@element-plus/icons-vue";
+import App from "./App.vue";
+import ElementPlus from "element-plus";
+
+const app = createApp(App);
+
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+  app.component(key, component);
+}
+
+app.use(ElementPlus);
+app.mount("#app");

+ 3 - 0
src/main.ts

@@ -26,3 +26,6 @@ $ico.setAttribute("rel", "icon");
 $ico.setAttribute("type", "image/svg+xml");
 $ico.setAttribute("href", appConstant.ico);
 document.head.appendChild($ico);
+
+import "./setSystem";
+

+ 4 - 0
src/request/urls.ts

@@ -215,3 +215,7 @@ export const downloadSceneList = "/fusion/sceneDownLog/list";
 export const offLine = "/web/fireProject/offLine"; //{roomId}
 export const onLine = "/web/fireProject/onLine"; //{roomId}
 export const onLineCheck = "/web/fireProject/onLineCheck";
+
+//settting
+export const getSysSetting = `/fusion-xj/systemSetting/info`;
+export const updateSysSetting = `/fusion-xj/systemSetting/save`;

+ 7 - 0
src/router/config.ts

@@ -91,6 +91,12 @@ export const routes: Routes = [
         component: () => import("@/view/statistics/index.vue"),
         meta: { title: "数据统计", icon: "iconfire_user" },
       },
+      {
+        name: RouteName.setting,
+        path: "setting",
+        component: () => import("@/view/setting/index.vue"),
+        meta: { title: "系统设置", icon: "icon-nav-setup" },
+      },
     ],
   },
   {
@@ -99,4 +105,5 @@ export const routes: Routes = [
     component: () => import("@/view/case/draw/index.vue"),
     meta: { title: "绘制卷宗图" },
   },
+
 ];

+ 1 - 0
src/router/routeName.ts

@@ -15,6 +15,7 @@ export const RouteName = {
   role: "role",
   sceneInitiator: "sceneInitiator",
   sceneVisitor: "sceneVisitor",
+  setting: "setting",
 } as const;
 
 type RouteNamesType = typeof RouteName;

+ 79 - 0
src/setSystem.ts

@@ -0,0 +1,79 @@
+import { ref } from "vue";
+import { title } from "./store/system";
+import { appConstant } from "./app";
+import {
+  caseFileTypes,
+  caseFiles,
+  insertCaseFile,
+  deleteCaseFile,
+  updateCaseFile,
+  axios,
+  caseFileInfo,
+  saveCaseFileInfo,
+  getSysSetting,
+  updateSysSetting,
+} from "@/request";
+
+const modules = import.meta.glob("@/assets/style/theme/*.scss", {
+  query: "?inline",
+});
+
+axios.get(getSysSetting).then((data) => {
+  systemData.value.name = data.data.title;
+  systemData.value.color = data.data.themeColour;
+  refresh();
+});
+
+const update = () => {
+  axios.post(updateSysSetting, {
+    title: systemData.value.name,
+    themeColour: systemData.value.color,
+  });
+};
+
+export const themeColors = [
+  "d8000a",
+  "0960bd",
+  "0084f4",
+  "009688",
+  "536dfe",
+  "ff5c93",
+  "ee4f12",
+  "0096c7",
+  "9c27b0",
+  "ff9800",
+];
+
+export const systemData = ref({
+  name: appConstant.name,
+  color: themeColors[0],
+});
+
+const $style = document.createElement("style");
+$style.setAttribute("type", "text/css");
+document.body.appendChild($style);
+
+const refresh = async () => {
+  title.value = systemData.value.name;
+
+  const key = Object.keys(modules).find((key) =>
+    key.includes(systemData.value.color)
+  );
+  if (key) {
+    const res1: any = await modules[key]();
+    const res2: any = await import("@/assets/style/public.scss?inline");
+    $style.innerHTML = res1.default + res2.default;
+  }
+};
+
+export const setTheme = async (color: string) => {
+  systemData.value.color = color;
+  await update();
+  refresh();
+};
+
+export const setTitle = async (d: string) => {
+  systemData.value.name = d;
+  await update();
+  refresh();
+};

+ 55 - 0
src/view/setting/index.vue

@@ -0,0 +1,55 @@
+<template>
+  <com-head :options="[{ name: '系统设置', value: '2' }]" notContent> </com-head>
+
+  <div class="body-layer" style="padding: 24px" v-loading="loading">
+    <el-form :model="form" label-width="auto" style="max-width: 600px">
+      <el-form-item label="系统标题">
+        <el-input
+          v-model="form.name"
+          placeholder="请输入系统标题"
+          :maxlength="100"
+          show-word-limit
+        />
+      </el-form-item>
+      <el-form-item label="系统主题色">
+        <el-radio-group v-model="form.color">
+          <el-radio :label="color" size="large" v-for="color in themeColors">
+            <span class="radio-box" :style="{ 'background-color': '#' + color }"></span>
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="onSubmit">提交</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script setup lang="ts">
+import comHead from "@/components/head/index.vue";
+import { setTheme, setTitle, systemData, themeColors } from "@/setSystem";
+
+import { reactive, ref } from "vue";
+
+// do not use same name with ref
+const form = reactive({
+  ...systemData.value,
+});
+
+const loading = ref(false);
+const onSubmit = async () => {
+  loading.value = true;
+  await setTheme(form.color);
+  await setTitle(form.name);
+  loading.value = false;
+};
+</script>
+
+<style lang="scss" scoped>
+.radio-box {
+  display: inline-block;
+  width: 20px;
+  height: 20px;
+  vertical-align: middle;
+}
+</style>

+ 7 - 0
vite.config.ts

@@ -16,6 +16,13 @@ export default defineConfig({
   },
   base: "./",
   build: {
+    rollupOptions: {
+      input: {
+        index: resolve(__dirname, "index.html"),
+        map: resolve(__dirname, "map.html"),
+        // 在这里继续添加更多页面
+      },
+    },
     outDir: `dist/${app}`,
   },
   resolve: {