bill hace 1 día
padre
commit
3ae4e425bd

BIN
public/images/chrome.png


BIN
public/images/eg.png


BIN
public/images/err.png


BIN
public/images/ff.png


BIN
public/images/safar.png


+ 77 - 0
src/below.vue

@@ -0,0 +1,77 @@
+<template>
+  <div class="below-layout">
+    <div class="content">
+      <img src="/images/err.png" alt="" />
+      <p>无法打开页面,请升级或更换浏览器后重新打开</p>
+      <span>建议使用以下浏览器</span>
+      <div class="list">
+        <div v-for="item in items">
+          <a :href="item.link"><img :src="item.icon" /></a>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+const items = [
+  {
+    icon: "images/ff.png",
+    link: "http://www.firefox.com.cn/",
+  },
+  {
+    icon: "images/eg.png",
+    link: "https://www.microsoft.com/en-us/edge",
+  },
+  {
+    icon: "images/safar.png",
+    link: "https://www.apple.com/safari/",
+  },
+  {
+    icon: "images/chrome.png",
+    link: "https://www.google.com/chrome/",
+  },
+];
+</script>
+
+<style lang="scss" scoped>
+.below-layout {
+  position: fixed;
+  inset: 0;
+  background: #f7f7f7;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  .content {
+    color: #999;
+    text-align: center;
+    img {
+      width: 200px;
+    }
+
+    p {
+      font-size: 16px;
+      margin: 20px 0;
+    }
+
+    span {
+      font-size: 14px;
+    }
+  }
+}
+
+.list {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-top: 20px;
+
+  > div {
+    margin: 0 10px;
+    img {
+      width: 70px !important;
+    }
+  }
+}
+</style>

+ 6 - 6
src/components/tagging/sign-new.vue

@@ -26,18 +26,18 @@
           />
         </h2>
         <div class="content">
-          <template v-if="defStyleType.id !== taggingStyle?.typeId">
+          <template v-if="defStyleType.id !== taggingStyle?.typeId && tagging.part">
             <p><span>遗留部位:</span>{{ tagging.part }}</p>
           </template>
-          <div class="p">
+          <div class="p" v-if="tagging.desc">
             <span v-if="defStyleType.id !== taggingStyle?.typeId"> 特征描述: </span>
             <div v-html="tagging.desc"></div>
           </div>
           <template v-if="defStyleType.id !== taggingStyle?.typeId">
-            <p><span>提取方法:</span>{{ tagging.method }}</p>
-            <p><span>提取时间:</span>{{ tagging.tqTime }}</p>
-            <p><span>提取人:</span>{{ tagging.principal }}</p>
-            <p><span>委托状态:</span>{{ tagging.tqStatus }}</p>
+            <p v-if="tagging.method"><span>提取方法:</span>{{ tagging.method }}</p>
+            <p v-if="tagging.tqTime"><span>提取时间:</span>{{ tagging.tqTime }}</p>
+            <p v-if="tagging.principal"><span>提取人:</span>{{ tagging.principal }}</p>
+            <p v-if="tagging.tqStatus"><span>委托状态:</span>{{ tagging.tqStatus }}</p>
           </template>
         </div>
         <Images

+ 2 - 2
src/layout/edit/header/index.vue

@@ -5,13 +5,13 @@
     </div>
 
     <div class="control">
-      <ui-icon
+      <!-- <ui-icon
         :class="{pure: params.pure}"
         style="margin-right: 10px"
         :type="custom.full ? 'window_n' : 'window_m'"
         ctrl
         @click="custom.full = !custom.full"
-      />
+      /> -->
       <template v-if="isEdit">
         <ui-button width="105px" @click="leave">退出</ui-button>
         <ui-button width="105px" type="primary" class="save" v-if="isOld" @click="save">

+ 1 - 1
src/layout/right-fill-pano.vue

@@ -1,5 +1,5 @@
 <template>
-  <div id="right-pano" v-if="!custom.full">
+  <div id="right-pano" v-show="!custom.full">
     <span
       class="ctrl-pano-c fun-ctrl strengthen-left strengthen-top strengthen-bottom"
       v-if="custom.shwoRightCtrlPano && custom.viewMode !== 'full'"

+ 13 - 6
src/main.ts

@@ -1,6 +1,7 @@
-import { createApp, watchEffect } from "vue";
+import { App, createApp, watchEffect } from "vue";
 import "./style.scss";
-import App from "./app.vue";
+import Home from "./app.vue";
+import Below from './below.vue'
 import Components from "bill/index";
 import router from "./router";
 import { params } from "@/env";
@@ -10,11 +11,17 @@ import * as URL from "@/api/constant";
 import VueKonva from "vue-konva";
 // import 'ant-design-vue/dist/reset.css';
 import "@/assets/style/global.less";
+import { isFirefoxBelow } from "./utils";
 
-const app = createApp(App);
-app.use(Components);
-app.use(router);
-app.use(VueKonva);
+let app: App
+if (isFirefoxBelow("115.9")) {
+  app = createApp(Below)
+} else {
+  app = createApp(Home);
+  app.use(Components);
+  app.use(router);
+  app.use(VueKonva);
+}
 app.mount("#app");
 
 if (import.meta.env.DEV) {

+ 53 - 0
src/utils/index.ts

@@ -284,3 +284,56 @@ export function encodePwd(str: string, strv = "") {
 
   return front + str2 + middle + str1 + end;
 }
+
+
+/**
+ * 判断是否为 Firefox 且版本小于 targetVersion(如 "115.9")
+ * @param {string} targetVersion - 目标版本字符串,例如 "115.9"
+ * @returns {boolean}
+ */
+export function isFirefoxBelow(targetVersion = "115.9") {
+  // 尝试使用 navigator.userAgentData (更现代) 如果可用且包含 brands
+  // 注意:userAgentData.brand/version 可能随浏览器策略返回受限信息
+  const navigator = window.navigator as any
+  if (navigator.userAgentData && Array.isArray(navigator.userAgentData.brands)) {
+    // brands 形如 [{brand: "Chromium", version: "116"}, ...] 或可能被 UA 凭空
+    for (const b of navigator.userAgentData.brands) {
+      if (typeof b.brand === "string" && /firefox/i.test(b.brand)) {
+        const ver = b.version || "";
+        return compareVersionStrings(ver, targetVersion) < 0;
+      }
+    }
+  }
+
+  // 回退到 userAgent 字符串
+  const ua = navigator.userAgent || "";
+  // Firefox 的 UA 通常包含 "Firefox/<version>"
+  const m = ua.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/i);
+  if (m && m[1]) {
+    const ver = m[1];
+    return compareVersionStrings(ver, targetVersion) < 0;
+  }
+
+  // 不是 Firefox 或版本未知
+  return false;
+}
+
+/**
+ * 比较两个版本字符串(如 "115.9.1" 与 "115.9")
+ * 返回:
+ *   -1 如果 v1 < v2
+ *    0 如果 v1 == v2
+ *    1 如果 v1 > v2
+ */
+function compareVersionStrings(v1: string, v2: string) {
+  const seg1 = String(v1).split('.').map(s => parseInt(s, 10) || 0);
+  const seg2 = String(v2).split('.').map(s => parseInt(s, 10) || 0);
+  const len = Math.max(seg1.length, seg2.length);
+  for (let i = 0; i < len; i++) {
+    const a = seg1[i] || 0;
+    const b = seg2[i] || 0;
+    if (a < b) return -1;
+    if (a > b) return 1;
+  }
+  return 0;
+}

+ 31 - 8
src/views/login.vue

@@ -3,7 +3,7 @@
     <div class="login-content">
       <div class="header">
         <img src="/favicon.ico" />
-        <p>实景三维</p>
+        <p>登录多元融合</p>
       </div>
 
       <div class="body">
@@ -15,11 +15,20 @@
         />
         <br />
         <ui-input
-          type="password"
+          :type="showPwd ? 'text' : 'password'"
           placeholder="请输入密码"
           v-model="password"
           style="width: 100%; margin-top: 20px"
-        />
+        >
+          <template #icon>
+            <ui-icon
+              style="cursor: pointer"
+              class="ctrl-pwd"
+              :type="showPwd ? 'eye-s' : 'eye-n'"
+              @click="showPwd = !showPwd"
+            ></ui-icon>
+          </template>
+        </ui-input>
         <br />
         <ui-input
           type="checkbox"
@@ -41,13 +50,16 @@
 import { ref } from "vue";
 import { encodePwd } from "@/utils";
 import GAxios from "axios";
-import { setToken } from "@/api";
+import { fetchSetting, setToken } from "@/api";
 import { Message } from "bill/expose-common";
 import { params } from "@/env";
+import { GET_SETTING, UPDATE_SETTING } from "@/api/constant";
+import { currentLayout, RoutesName } from "@/router";
 
 const username = ref(localStorage.getItem("fuse-username") || "");
 const password = ref(localStorage.getItem("fuse-password") || "");
 const mark = ref(!!localStorage.getItem("fuse-mark"));
+const showPwd = ref(false);
 const login = (username: string, password: string) => {
   if (!username) {
     return Message.error("账号不能为空");
@@ -56,7 +68,12 @@ const login = (username: string, password: string) => {
     return Message.error("密码不能为空");
   }
 
-  console.error("params.caseId ", params.caseId);
+  const isView = [RoutesName.show, RoutesName.signModel, RoutesName.error].includes(
+    currentLayout.value!
+  );
+  const type = isView ? "view" : "edit";
+  const headers = { fusionId: params.caseId, "page-type": type };
+
   GAxios.post(
     "/service/manage/login",
     {
@@ -64,12 +81,18 @@ const login = (username: string, password: string) => {
       userName: username,
       username: username,
     },
-    { headers: { fusionId: params.caseId } }
-  ).then((res) => {
+    { headers }
+  ).then(async (res) => {
     if (res.data.code !== 0) {
       return Message.error(res.data.message);
     }
-
+    const res1 = await GAxios.get(GET_SETTING, {
+      params: { fusionId: params.caseId },
+      headers: { ...headers, token: res.data.data.token },
+    });
+    if (res1.data.code === 40111) {
+      return Message.error("您没有权限,请联系管理员开通");
+    }
     setToken(res.data.data.token);
 
     setTimeout(() => {

+ 2 - 0
src/views/tagging/hot/edit.vue

@@ -268,6 +268,8 @@ const submitHandler = () => {
     Message.error("请选择图标样式!");
   } else if (!tagging.value.title.trim()) {
     Message.error("标签标题必须填写!");
+  } else if (tagging.value.title.trim().length > 15) {
+    Message.error("标题长度在15字以内!");
   }
   //  else if (!tagging.value.images.length) {
   //   Message.error("至少上传一张图片!");

+ 7 - 1
src/views/tagging/hot/sign.vue

@@ -7,7 +7,12 @@
     <div class="info">
       <img :src="getResource(getFileUrl(findImage))" v-if="findImage" />
       <div>
-        <p>{{ tagging.title }}</p>
+        <Popover>
+          <template #content>
+            <p style="max-width: 300px">{{ tagging.title }}</p>
+          </template>
+          <p>{{ tagging.title }}</p>
+        </Popover>
         <span>放置:{{ positions.length }}</span>
       </div>
     </div>
@@ -46,6 +51,7 @@ import {
 
 import type { Tagging } from "@/store";
 import { flyTagging, flyTaggingPosition } from "@/hook/use-fly";
+import { Popover } from "ant-design-vue";
 
 const props = withDefaults(
   defineProps<{

+ 54 - 16
src/views/tagging/hot/style-float-select.vue

@@ -9,9 +9,13 @@
     <div class="fsd">
       <ui-input
         type="text"
-        readonly
-        :modelValue="[current[current.length - 1]] .map((i: any) => i.title).join('/')"
+        :modelValue="focus ? keyword : [current[current.length - 1]]
+        .map((i: any) => i.title)
+        .join('/')"
+        @update:modelValue="(val) => (keyword = val)"
         width="100%"
+        @focus="focus = true"
+        @blur="focus = false"
       >
         <template #icon>
           <DownOutlined />
@@ -23,28 +27,29 @@
       <div class="flat" @click.stop="visible = true">
         <div>
           <span
-            v-for="item in items"
+            v-for="item in filterItems"
             @click.stop="
               () => {
                 handleClick(item), !item.children && (visible = false);
               }
             "
-            :class="{ active: current?.includes(item) }"
+            :class="{ active: current?.some((i) => item.key === i.key) }"
           >
             {{ item.title }}
             <DownOutlined v-if="item.children?.length" class="jdd" />
           </span>
         </div>
         <div>
-          <template v-if="current[0].children">
+          <template v-if="filterItems.find((item) => item.key === current[0].key)">
             <span
-              v-for="item in current[0].children"
+              v-for="item in filterItems.find((item) => item.key === current[0].key)
+                .children"
               @click.stop="
                 () => {
                   handleClick(item), (visible = false);
                 }
               "
-              :class="{ active: current?.includes(item) }"
+              :class="{ active: current?.some((i) => item.key === i.key) }"
             >
               {{ item.title }}
             </span>
@@ -64,6 +69,7 @@ import { taggings, getTaggingStyle } from "@/store";
 
 const props = defineProps<{ value: number; all?: boolean; count?: boolean }>();
 const emit = defineEmits<{ (e: "update:value", v: number): void }>();
+const keyword = ref("");
 const allType = { name: "全部", id: -1 };
 const getTypeCount = (item: any) => {
   if (item.id === allType.id) {
@@ -85,19 +91,23 @@ const getTypeCount = (item: any) => {
 };
 const open = ref(false);
 
-const getItems = (types = styleTypes): any => {
+const getItems = (types = styleTypes, parentHit = 0): any => {
   return types
     .map((item) => {
       let count = 0;
       if (props.count) {
         count = getTypeCount(item);
       }
+      const hit = item.name.includes(keyword.value) ? 1 : 0;
+      const children = "children" in item ? getItems(item.children as any, hit) : null;
+
       return {
         label: item.name + (props.count ? ` (${count}) ` : ""),
         title: item.name,
+        hit: children?.length ? children.reduce((t, c) => t + c.hit, 0) : hit + parentHit,
         count,
         key: item.id,
-        children: "children" in item ? getItems(item.children) : null,
+        children,
       };
     })
     .filter((item) => !props.count || item.count || item.key === allType.id);
@@ -124,11 +134,27 @@ const types = computed(() => {
 });
 const visible = ref(false);
 const items = computed(() => getItems(types.value));
+const filterItems = computed(() => {
+  const filter = (items: any[]): any => {
+    return items
+      .map((item) => {
+        if (item.children) {
+          return { ...item, children: filter(item.children) };
+        } else {
+          return { ...item };
+        }
+      })
+      .filter((item) => {
+        return item.hit > 0;
+      });
+  };
+  return filter(items.value);
+});
 const current = computed(() => getCurrentItem(props.value));
 
 const oldValue = ref(props.value);
 watchEffect(() => {
-  if (!current.value[current.value.length - 1].children?.length) {
+  if (current.value && !current.value[current.value.length - 1].children?.length) {
     oldValue.value = props.value;
   }
 });
@@ -162,6 +188,17 @@ if (props.count && props.all) {
 const handleClick = (info: any) => {
   emit("update:value", info.key);
 };
+
+const focus = ref(false);
+watchEffect(() => {
+  if (!focus.value) {
+    // setTimeout(() => {
+    //   keyword.value = [current.value[current.value.length - 1]]
+    //     .map((i: any) => i.title)
+    //     .join("/");
+    // }, 50);
+  }
+});
 </script>
 
 <style scoped lang="scss">
@@ -229,12 +266,12 @@ span {
   position: relative;
   width: 100%;
   cursor: pointer;
-  &::after {
-    content: "";
-    position: absolute;
-    inset: 0;
-    z-index: 999;
-  }
+  // &::after {
+  //   content: "";
+  //   position: absolute;
+  //   inset: 0;
+  //   z-index: 999;
+  // }
 }
 </style>
 
@@ -246,6 +283,7 @@ span {
 .ant-dropdown-menu {
   border-radius: 2px !important;
 }
+
 .style-float-select-overlay-parent {
   position: absolute;
   inset: 0;

+ 7 - 2
src/views/tagging/hot/style.scss

@@ -26,14 +26,14 @@
   }
 
   .info {
-    flex: 1;
-
+    width: calc(100% - 48px);
     display: flex;
     align-items: center;
 
     img {
       width: 48px;
       height: 48px;
+      flex: none;
       object-fit: cover;
       border-radius: 4px;
       overflow: hidden;
@@ -42,10 +42,15 @@
 
     div {
       margin-left: 10px;
+      width: calc(100% - 58px);
 
       p {
         color: #fff;
         font-size: 14px;
+        height: 20px;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
       }
       span {
         color: rgba(255,255,255,.6);

+ 13 - 6
src/views/tagging/index.vue

@@ -25,9 +25,13 @@
           <span>标签</span>
         </div>
         <template #overlay>
-          <Menu>
-            <menu-item key="1" @click="exposeTagging">导入</menu-item>
-            <menu-item key="2" @click="quiskAdd('tagging')">新增</menu-item>
+          <Menu class="tag-menu">
+            <menu-item key="1" @click="exposeTagging" style="text-align: center"
+              >导入</menu-item
+            >
+            <menu-item key="2" @click="quiskAdd('tagging')" style="text-align: center"
+              >新增</menu-item
+            >
           </Menu>
         </template>
       </Dropdown>
@@ -88,7 +92,7 @@ const exposeTagging = async () => {
 
   const addTaggings = list!.map((item) => {
     const tagging = createTagging({
-      styleId: defaultStyle.value?.id,
+      styleId: defStyleType.id as any,
       title: item.content?.title || item.name,
       desc: item.content?.feature || "",
       tqTime: formatDate(
@@ -109,11 +113,9 @@ const exposeTagging = async () => {
       tagging.audio = item.url;
       tagging.audioName = item.name;
     }
-    console.log(item.dictId, tagging.styleId);
     if (item.dictId) {
       const typeId = getStyleTypeId(item.dictId);
       const styles = taggingStyles.value.filter((item) => item.typeId === typeId);
-      console.log(styles);
       if (styles.length) {
         tagging.styleId = styles[0].id;
       }
@@ -200,3 +202,8 @@ const exposeTagging = async () => {
   }
 }
 </style>
+<style>
+.tag-menu .ant-dropdown-menu-item:hover {
+  background: rgba(0, 200, 175, 0.3) !important;
+}
+</style>