bill 2 周之前
父节点
当前提交
f038e4abe8

+ 4 - 2
package.json

@@ -12,9 +12,11 @@
   "dependencies": {
     "@ant-design/icons-vue": "^7.0.1",
     "@types/three": "^0.169.0",
-    "ant-design-vue": "^4.2.6",
+    "ant-design-vue": "4.1.0",
     "axios": "^0.27.2",
     "coordtransform": "^2.1.2",
+    "dayjs": "^1.11.13",
+    "js-base64": "^3.7.8",
     "konva": "^9.3.18",
     "less": "^4.1.3",
     "mitt": "^3.0.0",
@@ -25,7 +27,7 @@
     "vite-plugin-mkcert": "^1.10.1",
     "vue": "3.2.47",
     "vue-cropper": "1.0.2",
-    "vue-konva": "^3.2.0",
+    "vue-konva": "3.2.0",
     "vue-router": "^4.5.0",
     "vuedraggable": "^4.1.0"
   },

文件差异内容过多而无法显示
+ 817 - 1093
pnpm-lock.yaml


+ 6 - 0
src/api/constant.ts

@@ -58,7 +58,9 @@ export const DELETE_TAGGING_POINT = `${namespace}/caseTagPoint/delete`
 
 // 标签样式类型列表
 export const TAGGING_STYLE_LIST = `${namespace}/edit/hotIcon/list`
+export const TAGGING_STYLE_TREE = `${namespace}/edit/hotIcon/treeList`
 export const INSERT_TAGGING_STYLE = `${namespace}/edit/hotIcon/add`
+
 export const DELETE_TAGGING_STYLE = `${namespace}/edit/hotIcon/delete`
 
 // 测量线
@@ -109,6 +111,8 @@ export const UPDATE_SETTING = `${namespace}/caseSettings/saveOrUpdate`
 
 // 卷宗类型
 export const FOLDER_TYPE_LIST = `${namespace}/caseFilesType/allList`
+// 卷宗类型
+export const TAGGING_TYPE_LIST = `${namespace}/caseFilesType/getByParentId?parentId=49`
 
 // 卷宗
 // export const FLODER_LIST = `${namespace}/caseFiles/allList`
@@ -123,6 +127,8 @@ export const MATERIAL_PAG = `/service/manage/dictFile/pageList/media-library`
 export const ADD_MATERIAL = `/service/manage/common/upload/fileNew`
 export const DEL_MATERIAL = `/service/manage/dictFile/del/media-library`
 export const MATERIAL_GROUP_LIST = `/service/manage/dict/getByKey/media-library`
+export const MATERIAL_TA_GROUP_LIST = `/service/manage/dict/getByUseType/trace_evidence`
+export const SYNC_MATERIAL = `/service/manage/caseFusion/refreshTraceEvidenceInfoList/`
 
 
 // 动画模块

+ 7 - 7
src/api/instance.ts

@@ -26,7 +26,7 @@ export const {
   setHook,
 } = instance;
 
-const gotoLogin = () => {
+export const gotoLogin = () => {
   if (import.meta.env.DEV) {
     GAxios.post("/service/manage/login", {
       password: "MRinIEn3ExMjM0NTY=Q5Lm39urQWzN7k4oCG",
@@ -46,10 +46,10 @@ addReqErrorHandler((err) => {
   // Message.error(err.message)
   console.error(err);
   hideLoad();
-  gotoLogin();
 });
 
 addResErrorHandler((response, data) => {
+  console.log('hahah')
   if (response && response.status !== 200) {
     Message.error(response.statusText);
   } else if (data) {
@@ -58,12 +58,12 @@ addResErrorHandler((response, data) => {
         ? ResCodeDesc[data.code]
         : data?.message || data?.msg;
     if (data.code === ResCode.TOKEN_INVALID) {
-      gotoLogin();
+      // gotoLogin();
     } else if (data.code === ResCode.UN_AUTH) {
-      Dialog.alert({content: msg, okText: '我知道了'}).then(() => {
-        gotoLogin();
-      })
-      throw msg
+      // Dialog.alert({content: msg, okText: '我知道了'}).then(() => {
+      //   gotoLogin();
+      // })
+      // throw msg
     } else {
       Message.error(msg || '服务出现异常,请稍后再试');
     }

+ 65 - 50
src/api/material.ts

@@ -5,20 +5,26 @@ import {
   DEL_MATERIAL,
   MATERIAL_GROUP_LIST,
   MATERIAL_PAG,
+  MATERIAL_TA_GROUP_LIST,
+  SYNC_MATERIAL,
   UPLOAD_HEADS,
 } from "./constant";
 import axios from "./instance";
+import { params } from "@/env";
 
 type ServiceMaterialGroup = {
   dictKey: string;
   dictName: string;
-  useType: string
+  useType: string;
   id: number;
+  dictIconList?: {
+    iconUrl: string;
+  }[];
 };
 type ServiceMaterial = {
   createTime: string;
   dictId: number;
-  status: number,
+  status: number;
   dictName: string;
   fileFormat: string;
   fileName: string;
@@ -35,7 +41,8 @@ type ServiceMaterial = {
 export type MaterialGroup = {
   id: number;
   name: string;
-  useType: string,
+  useType: string;
+  icons: string[];
 };
 
 export type Material = {
@@ -48,70 +55,79 @@ export type Material = {
   status: number;
   group: string;
   uploadId?: number;
-  isSystem?: number
+  isSystem?: number;
   modelId?: number;
 };
 
 export type MaterialPageProps = PagingRequest<
-  Partial<Material> & { groupIds: number[], formats: string[] }
+  Partial<Material> & { groupIds: number[]; formats: string[], 
+  useType?: string }
 >;
 export const fetchMaterialPage = async (params: MaterialPageProps) => {
-  //
-  const material = await axios.post<PagingResult<ServiceMaterial[]>>(MATERIAL_PAG, {
-    pageNum: params.pageNum,
-    pageSize: params.pageSize,
-    name: params.name,
-    dictIds: params.groupIds,
-    
-    fileFormats: params.formats
-  });
+  const material = await axios.post<PagingResult<ServiceMaterial[]>>(
+    MATERIAL_PAG,
+    {
+      pageNum: params.pageNum,
+      pageSize: params.pageSize,
+      name: params.name,
+      dictIds: params.groupIds,
+      fileFormats: params.formats,
+      useType: params.useType
+    }
+  );
   const nm = {
     ...material,
-    list: material.list.map((item): Material => ({
-      ...item,
-      id: item.id,
-      name: item.name,
-      format: item.fileFormat,
-      url: item.fileUrl,
-      size: Number(item.fileSize),
-      groupId: item.dictId,
-      status: item.status,
-      group: item.dictName,
-      uploadId: item.uploadId
-    }))
-  }
+    list: material.list.map(
+      (item): Material => ({
+        ...item,
+        id: item.id,
+        name: item.name,
+        format: item.fileFormat,
+        url: item.fileUrl,
+        size: Number(item.fileSize),
+        groupId: item.dictId,
+        status: item.status,
+        group: item.dictName,
+        uploadId: item.uploadId,
+      })
+    ),
+  };
 
-  // const testUrls = [
-  //   'dog.glb', 'man--running.glb', 'man--walk.glb', 'Soldier.glb', 'Xbot.glb', 'Man.glb'
-  // ]
-  // nm.list.unshift(
-  //   ...testUrls.map((item, ndx) => ({
-  //     id: ndx,
-  //     name: item,
-  //     format: 'glb',
-  //     url: `/animation/${item}`,
-  //     size: 1,
-  //     groupId: 1,
-  //     status: 1,
-  //     group: '动画模型',
-  //     uploadId: 1
-  //   }))
-  // )
-  
   return nm;
 };
 
-export const fetchMaterialGroups = async () => {
-  return (await axios.get<ServiceMaterialGroup[]>(MATERIAL_GROUP_LIST)).map(
-    (item) => ({
+export const fetchMaterialGroups = async (useType?: string) => {
+  if (useType !== "trace_evidence") {
+    return (await axios.get<ServiceMaterialGroup[]>(MATERIAL_GROUP_LIST)).map(
+      (item) => ({
+        name: item.dictName,
+        useType: item.useType,
+        key: item.dictKey,
+        id: item.id,
+        icons: item.dictIconList
+          ? item.dictIconList.map((item) => item.iconUrl)
+          : [],
+      })
+    ) as MaterialGroup[];
+  } else {
+    return (
+      await axios.get<ServiceMaterialGroup[]>(MATERIAL_TA_GROUP_LIST)
+    ).map((item) => ({
       name: item.dictName,
       useType: item.useType,
       key: item.dictKey,
       id: item.id,
-    })
-  ) as MaterialGroup[];
+      icons: item.dictIconList
+        ? item.dictIconList.map((item) => item.iconUrl)
+        : [],
+    })) as MaterialGroup[];
+  }
 };
 
+export const syncMaterialAll = async () => {
+  await axios.get(SYNC_MATERIAL + params.caseId)
+}
+
 export const addMaterial = (file: File) => {
   return axios<string>({
     method: "POST",
@@ -119,7 +135,6 @@ export const addMaterial = (file: File) => {
     data: jsonToForm({ file }),
     headers: { ...UPLOAD_HEADS },
   });
-  
 };
 
 export const delMaterial = (id: Material["id"]) => {

+ 2 - 0
src/api/setup.ts

@@ -153,6 +153,7 @@ export const axiosFactory = () => {
 
   axiosRaw.interceptors.response.use(
     (response: AxiosResponse<ResData<any>>) => {
+      
       for (const hook of axiosConfig.hook) {
         hook.after && hook.after(response.config)
       }
@@ -169,6 +170,7 @@ export const axiosFactory = () => {
         if (response.data.code === ResCode.TOKEN_INVALID) {
           delToken()
         }
+        // console.error(response?.data?.message)
         throw new Error(response?.data?.message)
       } else {
         return response.data.data

+ 50 - 27
src/api/tagging-style.ts

@@ -4,9 +4,11 @@ import {
   INSERT_TAGGING_STYLE,
   DELETE_TAGGING_STYLE,
   UPLOAD_HEADS,
+  TAGGING_STYLE_TREE,
 } from "./constant";
 import { jsonToForm } from "@/utils";
 import { params } from "@/env";
+import { fetchMaterialGroups } from "./material";
 interface ServiceStyle {
   iconId: number;
   iconTitle: string;
@@ -16,9 +18,9 @@ interface ServiceStyle {
 }
 
 export const defStyleType = {
-  id: 8,
+  id: -999,
   name: "其他",
-}
+};
 export const styleTypes = [
   {
     id: 1,
@@ -31,30 +33,21 @@ export const styleTypes = [
       { id: 6, name: "其他" },
     ],
   },
-  {
-    id: 7,
-    name: "物证",
-  },
-  defStyleType
 ];
 
-export const getStyleTypeName = (id: number, all = styleTypes): string => {
+export const getStyleTypeName = (id: number, all: any = styleTypes): string => {
   for (const item of all) {
     if (id === item.id) {
-      return item.name
-    } else if ('children' in item && item.children) {
-      const cname = getStyleTypeName(id, item.children)
+      return item.name;
+    } else if ("children" in item && item.children) {
+      const cname = getStyleTypeName(id, item.children);
       if (cname) {
-        return cname
+        return cname;
       }
     }
   }
-  if (all === styleTypes) {
-    return defStyleType.name
-  } else {
-    return ''
-  }
-}
+  return "";
+};
 
 export interface TaggingStyle {
   id: string;
@@ -64,13 +57,15 @@ export interface TaggingStyle {
   default: boolean;
 }
 
-const toLocal = (serviceStyle: ServiceStyle): TaggingStyle => ({
-  id: serviceStyle.iconId.toString(),
-  lastUse: serviceStyle.lastUse,
-  typeId: Number(serviceStyle.iconTitle) || defStyleType.id,
-  icon: serviceStyle.iconUrl,
-  default: Boolean(serviceStyle.isSystem),
-});
+const toLocal = (serviceStyle: ServiceStyle): TaggingStyle => {
+  return {
+    id: serviceStyle.iconId.toString(),
+    lastUse: serviceStyle.lastUse,
+    typeId: Number(serviceStyle.iconTitle),
+    icon: serviceStyle.iconUrl,
+    default: Boolean(serviceStyle.isSystem),
+  };
+};
 
 const toService = (style: TaggingStyle): ServiceStyle => ({
   iconId: Number(style.id),
@@ -84,10 +79,37 @@ export type TaggingStyles = TaggingStyle[];
 
 export const fetchTaggingStyles = async () => {
   const reqParams = params.share ? { fusionId: params.caseId } : {};
-  const data = await axios.get<ServiceStyle[]>(TAGGING_STYLE_LIST, {
+  const treeData = await axios.get<any>(TAGGING_STYLE_TREE, {
     params: reqParams,
   });
-  return data.map(toLocal);
+  const styles: any[] = [];
+  const genTree = (tree: any, parent?: any) => {
+    for (const item of tree) {
+      if (item.iconUrl) {
+        delete parent.children;
+        styles.push(toLocal({ ...item, iconTitle: parent.id }));
+      } else {
+        const data = {
+          id: item.iconId,
+          name: item.iconTitle,
+          children: [],
+        };
+        parent.children.push(data);
+        genTree(item.childrenList, data)
+      }
+    }
+  };
+  const tree: any = { children: [] };
+  genTree(treeData, tree);
+  styleTypes.length = 0
+  styleTypes.push(...tree.children)
+  Object.assign(defStyleType, tree.children[tree.children.length - 1])
+  
+
+  // const data = await axios.get<ServiceStyle[]>(TAGGING_STYLE_LIST, {
+  //   params: reqParams,
+  // });
+  return styles;
 };
 
 export const postAddTaggingStyle = async (props: {
@@ -101,6 +123,7 @@ export const postAddTaggingStyle = async (props: {
     data: jsonToForm({
       file: new File([props.file], `${props.iconTitle}.png`),
       iconTitle: props.iconTitle.toString(),
+      parentId: props.iconTitle,
       fusionId: params.caseId,
     }),
   });

+ 29 - 2
src/api/tagging.ts

@@ -4,7 +4,8 @@ import {
   TAGGING_LIST,
   DELETE_TAGGING,
   INSERT_TAGGING,
-  UPDATE_TAGGING
+  UPDATE_TAGGING,
+  TAGGING_TYPE_LIST
 } from './constant'
 
 import type { FuseModel } from './fuse-model'
@@ -26,6 +27,11 @@ interface ServerTagging {
   show3dTitle: number
   audio: string
   fileName: string
+
+  // 提取状态
+  tqStatus?: tqStatusEnum,
+  // 提取时间
+  tqTime?: string
 }
 
 export interface Tagging {
@@ -40,6 +46,17 @@ export interface Tagging {
   images: string[],
   audio: string
   audioName: string
+
+  // 提取状态
+  tqStatus?: tqStatusEnum,
+  // 提取时间
+  tqTime?: string
+}
+
+export enum tqStatusEnum {
+  UN = '未送检',
+  ING = '送检中',
+  END = '完成检验'
 }
 
 export type Taggings = Tagging[]
@@ -58,6 +75,10 @@ const serviceToLocal = (serviceTagging: ServerTagging): Tagging => ({
   principal: serviceTagging.getUser,
   audio: serviceTagging.audio,
   images: JSON.parse(serviceTagging.tagImgUrl),
+  // 提取状态
+  tqStatus: serviceTagging.tqStatus,
+  // 提取时间
+  tqTime: serviceTagging.tqTime
 })
 
 const localToService = (tagging: Tagging, update = false): PartialProps<ServerTagging, 'tagId' | 'hotIconUrl'> & { fusionId: number } => ({
@@ -73,11 +94,17 @@ const localToService = (tagging: Tagging, update = false): PartialProps<ServerTa
   "leaveBehind": tagging.part,
   "tagDescribe": tagging.desc,
   "tagTitle": tagging.title,
-  audio: tagging.audio
+  audio: tagging.audio,
+
+  // 提取状态
+  tqStatus: tagging.tqStatus,
+  // 提取时间
+  tqTime: tagging.tqTime
 })
 
 
 export const fetchTaggings = async () => {
+  axios.get(TAGGING_TYPE_LIST)
   const staggings = await axios.get<ServerTagging[]>(TAGGING_LIST, { params: { fusionId: params.caseId } })
   return staggings.map(serviceToLocal)
 }

+ 97 - 3
src/app.vue

@@ -1,5 +1,5 @@
 <template>
-  <ConfigProvider v-bind="config">
+  <ConfigProvider v-bind="config" v-if="!showLogin">
     <template v-for="needMount in needMounts">
       <Teleport :to="needMount[0]">
         <component
@@ -42,6 +42,45 @@
 
     <PwdModel v-if="inputPwd" @close="inputPwd = false" />
   </ConfigProvider>
+
+  <ui-dialog v-else>
+    <template v-slot:header>
+      <span>用户登录</span>
+    </template>
+    <div>
+      <ui-input
+        type="text"
+        placeholder="请输入账号"
+        v-model="username"
+        style="width: 360px"
+      />
+      <br />
+      <ui-input
+        type="password"
+        placeholder="请输入密码"
+        v-model="password"
+        style="width: 360px; margin-top: 20px"
+      />
+      <br />
+      <ui-input
+        type="checkbox"
+        @click.stop
+        label="记住密码"
+        style="margin-top: 20px"
+        :modelValue="mark"
+        @update:modelValue="(select: any) => mark = select"
+      />
+      <!-- <ui-input
+        type="checkbox"
+        label="记住密码"
+        v-model="mark"
+        style="margin-top: 20px"
+      /> -->
+    </div>
+    <template v-slot:footer>
+      <ui-button type="submit" @click="login(username, password)">登录</ui-button>
+    </template>
+  </ui-dialog>
 </template>
 
 <script lang="ts" setup>
@@ -58,11 +97,33 @@ import {
   scenes,
 } from "@/store";
 import router, { currentLayout, RoutesName } from "./router";
-import { loadPack, needMounts } from "@/utils";
+import { asyncTimeout, encodePwd, loadPack, needMounts } from "@/utils";
 import { ConfigProvider } from "ant-design-vue";
 import PwdModel from "@/layout/pwd.vue";
 import { config } from "./config";
 import { sdk, sdkLoaded } from "./sdk";
+import GAxios from "axios";
+
+import { addReqErrorHandler, addResErrorHandler, ResCode, setToken } from "./api";
+
+const gotoLogin = () => {
+  if (import.meta.env.DEV) {
+    login("super-admin", "Aa123456");
+  } else {
+    showLogin.value = true;
+  }
+};
+
+addResErrorHandler((data: any) => {
+  data = data.data;
+  if (data.code === ResCode.TOKEN_INVALID) {
+    gotoLogin();
+  } else if (data.code === ResCode.UN_AUTH) {
+    gotoLogin();
+  }
+});
+addReqErrorHandler(gotoLogin);
+
 // https://192.168.0.13:7173/index.html?caseId=509&sign=vGxCu4X5321fkWpZN6HnqYBiE6iI71DDWzdgjEaUKIh7vDWo3o5yhqHdHhGr4Z3W#/show
 const isStandard = ref(true);
 watchEffect(() => {
@@ -97,6 +158,7 @@ const stopWatch = watch(
     // }
 
     params.share = true;
+    await asyncTimeout(100);
     await refreshCase();
     if (caseProject.value) {
       await loadPack(initialSetting);
@@ -107,7 +169,7 @@ const stopWatch = watch(
     loaded.value = true;
   },
   { immediate: true }
-); 
+);
 
 watchEffect(() => {
   prefix.value = caseProject.value?.fusionTitle || "多元融合";
@@ -132,6 +194,37 @@ const layoutStyles = computed(() => {
   }
   return styles;
 });
+
+const showLogin = ref(false);
+const username = ref(localStorage.getItem("fuse-username") || "");
+const password = ref(localStorage.getItem("fuse-password") || "");
+const mark = ref(!!localStorage.getItem("fuse-mark"));
+const login = (username: string, password: string) => {
+  GAxios.post("/service/manage/login", {
+    password: encodePwd(password),
+    userName: username,
+    username: username,
+  }).then((res) => {
+    setToken(res.data.data.token);
+
+    setTimeout(() => {
+      const p = new URLSearchParams(location.search);
+      p.delete("token");
+      location.search = "?" + p.toString();
+    }, 100);
+
+    loaded.value = false;
+    if (mark.value) {
+      localStorage.setItem("fuse-username", username);
+      localStorage.setItem("fuse-password", password);
+      localStorage.setItem("fuse-mark", "1");
+    } else {
+      localStorage.removeItem("fuse-username");
+      localStorage.removeItem("fuse-password");
+      localStorage.removeItem("fuse-mark");
+    }
+  });
+};
 </script>
 
 <style scoped lang="scss">
@@ -166,6 +259,7 @@ const layoutStyles = computed(() => {
     var(--editor-menu-left) + var(--editor-menu-width) - var(--left-pano-width)
   ) !important;
 }
+
 .edit-mode {
   --editor-menu-left: calc(-1 * var(--editor-menu-width));
 }

+ 40 - 12
src/components/materials/index.vue

@@ -16,7 +16,15 @@
           已选择数据<span>( {{ rowSelection.selectedRowKeys.length }} )</span>
         </p>
         <div class="up-se">
-          <span class="upload fun-ctrls">
+          <Button
+            type="primary"
+            ghost
+            v-if="useType === 'trace_evidence'"
+            @click="foreceRefresh"
+          >
+            刷新
+          </Button>
+          <span class="upload fun-ctrls" v-if="!readonly">
             <ui-input
               class="input"
               :accept="ft"
@@ -25,7 +33,7 @@
               type="file"
             >
               <template v-slot:replace>
-                <Button type="primary" ghost v-if="!readonly">
+                <Button type="primary" ghost>
                   <ui-icon type="add" class="icon" />上传文件
                 </Button>
               </template>
@@ -107,6 +115,7 @@ import {
   Material,
   MaterialGroup,
   MaterialPageProps,
+  syncMaterialAll,
 } from "@/api/material";
 import Message from "bill/components/message/message.vue";
 import { Dialog } from "bill/expose-common";
@@ -119,6 +128,7 @@ const props = defineProps<{
   count?: number;
   readonly?: boolean;
   groupIds: number[];
+  useType?: string;
   afterClose?: () => void;
 }>();
 
@@ -138,8 +148,10 @@ const params = reactive({
   pageNum: 1,
   pageSize: 10,
   formats: props.format,
+  useType: props.useType,
   groupIds: props.groupIds,
 }) as MaterialPageProps;
+console.error(params);
 const origin = ref<PagingResult<Material[]>>({
   list: [],
   pageNum: 1,
@@ -147,22 +159,27 @@ const origin = ref<PagingResult<Material[]>>({
   total: 0,
 });
 const groups = ref<MaterialGroup[]>([]);
-const selectKeys = ref<Material["id"][]>([]);
+// const selectKeys = ref<Material["id"][]>([]);
 const allData: Record<Material["id"], Material> = reactive({});
 
-const rowSelection: any = ref({
-  selectedRowKeys: selectKeys,
+const rowSelection = ref({
+  selectedRowKeys: [] as Material["id"][],
   onChange: (ids: number[]) => {
-    const otherPageKeys = selectKeys.value.filter(
+    console.log(ids);
+    const otherPageKeys = rowSelection.value.selectedRowKeys.filter(
       (key) => !origin.value.list.some((item) => key === item.id)
     );
     const newKeys = Array.from(new Set([...otherPageKeys, ...ids]));
     if (typeof props.count !== "number" || props.count >= newKeys.length) {
-      selectKeys.value = newKeys;
+      rowSelection.value.selectedRowKeys = newKeys;
     } else {
       Message.error(`最多选择${props.count}项`);
     }
   },
+  onSelectAll: (selected: boolean, selectedRows: Material[], changeRows: Material[]) => {
+    // 显式处理全选逻辑
+    console.log("全选状态变化:", selected);
+  },
   getCheckboxProps: (record: Material) => {
     return {
       disabled:
@@ -194,6 +211,12 @@ const cloumns = computed(() => {
     },
     {
       width: "100px",
+      title: "id",
+      dataIndex: "id",
+      key: "id",
+    },
+    {
+      width: "100px",
       title: "状态",
       dataIndex: "status",
       key: "status",
@@ -227,7 +250,7 @@ const cloumns = computed(() => {
 });
 
 const fetchData = createLoadPack(() =>
-  Promise.all([fetchMaterialGroups(), fetchMaterialPage(params)])
+  Promise.all([fetchMaterialGroups(props.useType), fetchMaterialPage(params)])
 );
 const refresh = debounceStack(
   fetchData,
@@ -243,6 +266,11 @@ const refresh = debounceStack(
   160
 );
 
+const foreceRefresh = async () => {
+  await syncMaterialAll();
+  await refresh();
+};
+
 const uploadHandler = async (file: File) => {
   await addMaterial(file);
   refresh();
@@ -257,10 +285,10 @@ const addHandler = async (file: File) => {
 const delHandler = async (id: Material["id"]) => {
   if (await Dialog.confirm("确定要删除此数据吗?")) {
     await delMaterial(id);
-    const ndx = selectKeys.value.indexOf(id);
-    console.log(selectKeys.value, id);
+    const ndx = rowSelection.value.selectedRowKeys.indexOf(id);
+    console.log(rowSelection.value.selectedRowKeys, id);
     if (~ndx) {
-      selectKeys.value.splice(ndx, 1);
+      rowSelection.value.selectedRowKeys.splice(ndx, 1);
     }
     refresh();
   }
@@ -269,7 +297,7 @@ const delHandler = async (id: Material["id"]) => {
 const okHandler = () => {
   emit(
     "selectMaterials",
-    selectKeys.value.map((id) => allData[id])
+    rowSelection.value.selectedRowKeys.map((id) => allData[id])
   );
 };
 const handleTableChange: TableProps["onChange"] = (pag, filters) => {

+ 2 - 0
src/components/materials/quisk.ts

@@ -11,10 +11,12 @@ export const selectMaterials = async (props: {
   count?: number
   readonly?: boolean;
   groupIds?: number[]
+  useType?: string
 } = {}) => {
   return new Promise<Material[] | null>((resolve) => {
     const mprops = reactive({
       ...props,
+      count: 3,
       visible: true,
       onSelectMaterials: (val: Material[]) => {
         resolve(val);

+ 5 - 1
src/components/tagging/sign-new.vue

@@ -26,14 +26,18 @@
           />
         </h2>
         <div class="content">
+          <template v-if="defStyleType.id !== taggingStyle?.typeId">
+            <p><span>遗留部位:</span>{{ tagging.part }}</p>
+          </template>
           <div class="p">
             <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.part }}</p>
             <p><span>提取方法:</span>{{ tagging.method }}</p>
+            <p><span>提取时间:</span>{{ tagging.method }}</p>
             <p><span>提取人:</span>{{ tagging.principal }}</p>
+            <p><span>委托状态:</span>{{ tagging.principal }}</p>
           </template>
         </div>
         <Images

+ 1 - 0
src/layout/edit/scene-select.vue

@@ -162,6 +162,7 @@ const selects = ref<Key[]>(selectIds.value);
 const rowSelection: any = ref({
   selectedRowKeys: selects,
   onChange: (ids: string[]) => {
+    console.error(ids)
     ids = ids.filter(id => !selectIds.value.includes(id))
     cache[type.value] = ids
     const curIds = [...selectIds.value]

+ 2 - 2
src/layout/scene-list/index.vue

@@ -39,7 +39,7 @@
             {{ SceneTypeDesc[item.raw.type as SceneType] }}
           </p>
         </div>
-        <Button
+        <!-- <Button
           size="small"
           type="primary"
           ghost
@@ -48,7 +48,7 @@
           @click.stop="sync(item as Scene)"
         >
           同屏勘验
-        </Button>
+        </Button> -->
       </div>
     </template>
   </List>

+ 4 - 1
src/store/tagging.ts

@@ -1,5 +1,5 @@
 import { ref } from 'vue'
-import { togetherCallback } from '@/utils'
+import { formatDate, togetherCallback } from '@/utils'
 import { autoSetModeCallback, createTemploraryID } from './sys'
 import { defaultStyle, getTaggingStyle, initialTaggingStyles, lastUseStyle } from './tagging-style'
 import { 
@@ -23,6 +23,7 @@ import {
   postDeleteTagging,
   postUpdateTagging, 
   TaggingPositionType, 
+  tqStatusEnum, 
   uploadFile
 } from '@/api'
 import { 
@@ -60,6 +61,8 @@ export const createTagging = (tagging: Partial<Tagging> = {}): Tagging => {
     principal: '',
     audio: '',
     images: [],
+    tqTime: formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss'),
+    tqStatus: tqStatusEnum.UN,
     ...tagging
   }
 }

+ 111 - 1
src/utils/index.ts

@@ -173,4 +173,114 @@ export * from './diff'
 export * from './params'
 export * from './file-serve'
 export * from './video-cover'
-export * from './meta'
+export * from './meta'
+
+
+
+
+
+function randomWord(randomFlag: boolean, min: number, max?: number) {
+  let str = "";
+  let range = min;
+  const arr = [
+    "0",
+    "1",
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "a",
+    "b",
+    "c",
+    "d",
+    "e",
+    "f",
+    "g",
+    "h",
+    "i",
+    "j",
+    "k",
+    "l",
+    "m",
+    "n",
+    "o",
+    "p",
+    "q",
+    "r",
+    "s",
+    "t",
+    "u",
+    "v",
+    "w",
+    "x",
+    "y",
+    "z",
+    "A",
+    "B",
+    "C",
+    "D",
+    "E",
+    "F",
+    "G",
+    "H",
+    "I",
+    "J",
+    "K",
+    "L",
+    "M",
+    "N",
+    "O",
+    "P",
+    "Q",
+    "R",
+    "S",
+    "T",
+    "U",
+    "V",
+    "W",
+    "X",
+    "Y",
+    "Z",
+  ];
+  // 随机产生
+  if (randomFlag && max) {
+    range = Math.round(Math.random() * (max - min)) + min;
+  }
+  for (let i = 0; i < range; i++) {
+    const pos = Math.round(Math.random() * (arr.length - 1));
+    str += arr[pos];
+  }
+  return str;
+}
+import { Base64 } from "js-base64";
+/**
+ * 密码加密
+ * @param {String} pwd
+ */
+export function encodePwd(str: string, strv: string): string[];
+export function encodePwd(str: string): string;
+export function encodePwd(str: string, strv = "") {
+  str = Base64.encode(str);
+  const NUM = 2;
+  const front = randomWord(false, 8);
+  const middle = randomWord(false, 8);
+  const end = randomWord(false, 8);
+
+  const str1 = str.substring(0, NUM);
+  const str2 = str.substring(NUM);
+
+  if (strv) {
+    const strv1 = strv.substring(0, NUM);
+    const strv2 = strv.substring(NUM);
+    return [
+      front + str2 + middle + str1 + end,
+      front + strv2 + middle + strv1 + end,
+    ];
+  }
+
+  return front + str2 + middle + str1 + end;
+}

+ 93 - 12
src/views/tagging/hot/edit.vue

@@ -31,6 +31,18 @@
       <div class="input">
         <ui-input type="checkbox" label="标题常驻" v-model="tagging.show3dTitle" />
       </div>
+      <template v-if="defStyleType.id !== type">
+        <ui-input
+          class="input preplace"
+          width="100%"
+          placeholder=""
+          type="text"
+          v-model="tagging.part"
+          :maxlength="60"
+        >
+          <template #preIcon><span>遗留部位:</span></template>
+        </ui-input>
+      </template>
 
       <ui-input
         class="input"
@@ -46,31 +58,45 @@
           class="input preplace"
           width="100%"
           placeholder=""
-          type="text"
-          v-model="tagging.part"
-          :maxlength="60"
+          type="select"
+          v-model="tagging.method"
+          :options="tqMethodOptions"
         >
-          <template #preIcon><span>遗留部位:</span></template>
+          <template #preIcon><span>提取方法:</span></template>
+        </ui-input>
+        <ui-input class="input preplace" width="100%" placeholder="" type="text">
+          <template #preIcon>
+            <span>提取时间:</span>
+            <span class="tq-time">
+              <DatePicker
+                show-time
+                valueFormat="YYYY-MM-DD HH:mm:ss"
+                :value="tagging.tqTime"
+                @update:value="(val) => (tagging.tqTime = val)"
+              />
+            </span>
+          </template>
         </ui-input>
         <ui-input
           class="input preplace"
           width="100%"
-          placeholder=""
           type="text"
-          v-model="tagging.method"
+          placeholder=""
+          v-model="tagging.principal"
           :maxlength="60"
         >
-          <template #preIcon><span>提取方法:</span></template>
+          <template #preIcon><span>提取:</span></template>
         </ui-input>
         <ui-input
           class="input preplace"
           width="100%"
-          type="text"
+          type="select"
+          :options="tqStatusOptions"
           placeholder=""
-          v-model="tagging.principal"
+          v-model="tagging.tqStatus"
           :maxlength="60"
         >
-          <template #preIcon><span>提取人:</span></template>
+          <template #preIcon><span>委托状态:</span></template>
         </ui-input>
       </template>
       <div class="input" style="padding-top: 10px">
@@ -157,6 +183,7 @@
 import StyleTypeSelect from "./style-type-select.vue";
 import StylesManage from "./styles.vue";
 import Images from "./images.vue";
+
 import { computed, ref, watchEffect } from "vue";
 import { Dialog, Message } from "bill/index";
 import {
@@ -168,14 +195,48 @@ import {
   isTemploraryID,
   defaultStyle,
 } from "@/store";
-import { defStyleType, styleTypes } from "@/api";
+import { defStyleType, styleTypes, tqStatusEnum } from "@/api";
 import { selectMaterials } from "@/components/materials/quisk";
 import { getFileName } from "@/utils";
+import { DatePicker } from "ant-design-vue";
+import locale from "ant-design-vue/es/date-picker/locale/zh_CN";
+import "dayjs/locale/zh-cn";
+
+import dayjs from "dayjs";
+dayjs.locale("zh-cn");
 
 export type EditProps = {
   data: Tagging;
 };
 
+const tqStatusOptions = [
+  { label: tqStatusEnum.UN, value: tqStatusEnum.UN },
+  { label: tqStatusEnum.ING, value: tqStatusEnum.ING },
+  { label: tqStatusEnum.END, value: tqStatusEnum.END },
+];
+
+const tqMethodOptions = [
+  { label: "未送检", value: "未送检" },
+  { label: "鉴定委托", value: "鉴定委托" },
+  { label: "程序受理", value: "程序受理" },
+  { label: "案件预受理", value: "案件预受理" },
+  { label: "受理登记", value: "受理登记" },
+  { label: "不予受理审核", value: "不予受理审核" },
+  { label: "检验鉴定", value: "检验鉴定" },
+  { label: "文书拟稿", value: "文书拟稿" },
+  { label: "鉴定复核", value: "鉴定复核" },
+  { label: "技术审批(授权签字人审批)", value: "技术审批(授权签字人审批)" },
+  { label: "程序审批(科室主任审核)", value: "程序审批(科室主任审核)" },
+  { label: "领导审批", value: "领导审批" },
+  { label: "打印报告", value: "打印报告" },
+  { label: "提交归档", value: "提交归档" },
+  { label: "文书发放", value: "文书发放" },
+  { label: "检验鉴定完成", value: "检验鉴定完成" },
+  { label: "自动失效", value: "自动失效" },
+  { label: "鉴定终止", value: "鉴定终止" },
+  { label: "不予受理", value: "不予受理" },
+];
+
 const imageSize = 100 * 1024 * 1024;
 const imageCount = 10;
 const imageFormat = ["jpg", "png", "mp4"];
@@ -443,8 +504,28 @@ const imageSelect = async () => {
 }
 </style>
 
-<style>
+<style lang="scss">
 .edit-hot-layer .input.ui-input .text.suffix input {
   padding-right: 60px;
 }
+
+.tq-time .ant-picker {
+  position: absolute;
+  top: -8px;
+  bottom: -8px;
+  background: none;
+  border: none !important;
+  outline: none !important;
+  box-shadow: none !important;
+  width: 280px;
+
+  input {
+    outline: none !important;
+    border: none !important;
+    background: none;
+    box-shadow: none !important;
+    text-align: left;
+    padding-left: 10px !important;
+  }
+}
 </style>

+ 22 - 15
src/views/tagging/hot/index.vue

@@ -1,21 +1,21 @@
 <template>
   <ui-group title="标签列表" class="tagging-list">
     <template #header>
-      <StyleTypeSelect v-model:value="type" all count />
-    </template>
-    <template #icon>
-      <ui-icon
-        ctrl
-        :class="{ active: showSearch }"
-        type="search"
-        @click="showSearch = !showSearch"
-        style="margin-right: 20px"
-      />
-      <ui-icon
-        ctrl
-        :type="custom.showTaggings ? 'eye-s' : 'eye-n'"
-        @click="custom.showTaggings = !custom.showTaggings"
-      />
+      <div class="header">
+        <StyleTypeSelect v-model:value="type" all count />
+        <span>
+          <ui-icon
+            ctrl
+            :class="{ active: showSearch }"
+            type="search"
+            @click="showSearch = !showSearch"
+            style="margin-right: 20px" />
+          <ui-icon
+            ctrl
+            :type="custom.showTaggings ? 'eye-s' : 'eye-n'"
+            @click="custom.showTaggings = !custom.showTaggings"
+        /></span>
+      </div>
     </template>
     <ui-group-option v-if="showSearch">
       <ui-input type="text" width="100%" placeholder="搜索" v-model="keyword">
@@ -149,4 +149,11 @@ defineExpose({
 .edit-layout {
   z-index: 999;
 }
+
+.header {
+  width: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
 </style>

+ 18 - 5
src/views/tagging/hot/style-type-select.vue

@@ -1,14 +1,17 @@
 <template>
   <!-- <Menu style="width: 256px" mode="vertical" :items="getItems()" @click="handleClick" /> -->
   <Dropdown placement="bottom" v-if="current.length">
-    <span>
-      {{ current.map((i: any) => i.title).join("/") }}
-      <span class="count" v-if="count">({{ current[current.length - 1].count }})</span>
-      <DownOutlined />
+    <span class="current">
+      <span class="title">{{ current.map((i: any) => i.title).join("/") }}</span>
+      <span>
+        <span class="count" v-if="count"
+          >({{ current[current.length - 1].count }})</span
+        >
+        <DownOutlined />
+      </span>
     </span>
     <template #overlay>
       <Menu
-        style="width: 120px"
         :active-key="value.toString()"
         mode="vertical"
         popupClassName="child-items"
@@ -112,6 +115,16 @@ span {
     color: var(--color-main-normal);
   }
 }
+.current {
+  max-width: 100%;
+  display: flex;
+  .title {
+    max-width: 190px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+}
 </style>
 
 <style>

+ 22 - 4
src/views/tagging/index.vue

@@ -19,10 +19,18 @@
 
   <Teleport to=".laser-layer">
     <div class="quisks" v-if="!isEdit && !currentIsFullView">
-      <div class="quisk-item fun-ctrl" @click="quiskAdd('tagging')">
-        <ui-icon type="label" />
-        <span>标签</span>
-      </div>
+      <Dropdown>
+        <div class="quisk-item fun-ctrl">
+          <ui-icon type="label" />
+          <span>标签</span>
+        </div>
+        <template #overlay>
+          <Menu>
+            <menu-item key="1" @click="exposeTagging">导入</menu-item>
+            <menu-item key="2" @click="quiskAdd('tagging')">新增</menu-item>
+          </Menu>
+        </template>
+      </Dropdown>
       <!-- <div class="quisk-item fun-ctrl" @click="quiskAdd('monitor')">
         <ui-icon type="a-animation_s" />
         <span>监控</span>
@@ -38,6 +46,8 @@ import Monitor from "./monitor/index.vue";
 import { RightFillPano } from "@/layout";
 import { nextTick, reactive, ref, watchEffect } from "vue";
 import { currentIsFullView } from "@/utils/full";
+import { Dropdown, MenuItem, Menu } from "ant-design-vue";
+import { selectMaterials } from "@/components/materials/quisk";
 
 const current = ref("tagging");
 const tabs = reactive([
@@ -54,6 +64,13 @@ const quiskAdd = async (key: string) => {
   await nextTick();
   quiskObj.value.add();
 };
+
+const exposeTagging = async () => {
+  const list = await selectMaterials({
+    useType: "trace_evidence",
+  });
+  console.log(list);
+};
 </script>
 
 <style lang="scss" scoped>
@@ -124,6 +141,7 @@ const quiskAdd = async (key: string) => {
       margin-top: 6px;
       font-size: 14px;
     }
+
     .icon {
       font-size: 22px;
     }

+ 2 - 2
vite.config.ts

@@ -5,8 +5,8 @@ import mkcert from 'vite-plugin-mkcert'
 
 import { resolve } from 'path'
 
-const oss = `https://survey.4dkankan.com/`
-const ip = `http://192.168.0.43:8808`
+const oss = `http://192.168.0.125:1804/`
+const ip = `http://192.168.0.125:1804/`
 const proxy = {
   '/offlineData': {
     target: 'http://192.168.0.43:9000/',