2 Commits 3b045a9b87 ... 31d99de5b1

Author SHA1 Message Date
  LXY 31d99de5b1 Merge branch 'master' of http://gogs.gisvg.com/YDM/museums-ui 2 months ago
  lmm 0ede585840 最新前端 2 months ago

+ 93 - 1
src/App.vue

@@ -23,6 +23,7 @@ const setDefaultTheme = () => {
   appStore.setIsDark(isDarkTheme)
 }
 setDefaultTheme()
+
 </script>
 <template>
   <ConfigGlobal :size="currentSize">
@@ -45,7 +46,22 @@ body {
   padding: 0 !important;
   margin: 0;
   overflow: hidden;
-
+  --left-menu-bg-active-color:#dcb86b;
+  --left-menu-text-active-color:#fff;
+  --left-menu-bg-color:#333;
+  --left-menu-bg-light-color:#333;
+  --left-menu-text-color:#fff;
+  // --top-header-bg-color:#f1ead8;
+  --logo-title-text-color:#dcb86b;
+  --el-tag-border-color:rgba(220,184,107,0.5);
+  --el-menu-base-level-padding:6px;
+  // --left-menu-bg-active-color:RGBA(195,127,64,0.1);
+  // --left-menu-text-active-color:#dcb86b;
+  // --left-menu-bg-color:#333;
+  // --left-menu-bg-light-color:#464646;
+  // --left-menu-text-color:#fff;
+  // --top-header-bg-color:#f1ead8;
+  // --app-content-bg-color:#fdf5e1;
   #app {
     @extend .size;
   }
@@ -54,4 +70,80 @@ body {
 .#{$prefix-cls}-grey-mode {
   filter: grayscale(100%);
 }
+
+.bg-\[var\(--app-content-bg-color\)\]{
+  background-color: unset;
+  // background-image: ;
+}
+
+.w-\[calc\(var\(--logo-height\)-10px\)\]{
+  width:calc(var(--logo-height) - -300px);
+  // color: #333;
+}
+
+:root{
+  --logo-height:70px;
+}
+
+.v-menu[data-v-c3357e93] .el-menu .el-menu-item.is-active{
+  color:#dcb86b !important;
+}
+
+.el-dropdown{
+  // border: none !important;
+  // margin: 0 !important;
+}
+.el-tag, .el-tag.el-tag--primary{
+  // --el-tag-border-color:rgba(220,184,107,1) !important;
+  // --el-tag-bg-color:rgba(220,184,107,0.1) !important;
+}
+// --left-menu-bg-active-color{
+  // RGBA(195,127,64,0.1)
+// }
+
+.v-menu[data-v-c3357e93]{
+  // margin: 10px;
+  padding: 15px;
+}
+
+@media (min-width: 100px) {
+  .v-menu[data-v-c3357e93]{
+  padding: 0;
+}
+  
+}
+
+
+@media (min-width: 768px) {
+  .v-menu[data-v-c3357e93]{
+    padding: 15px;
+}
+  
+}
+
+.el-menu-item{
+  // padding: 15px !important;
+  border-radius: 15px !important;
+}
+
+#v-tool-header{
+  padding: 10px 15px;
+  // background-color: #fff;
+}
+
+.el-scrollbar__view{}
+// .el-scrollbar__wrap--hidden-default{
+// background-color: #dcb86b !important;
+// }
+
+.mt-\[calc\(var\(--top-tool-height\)\+var\(--tags-view-height\)\)\]{
+  margin-top: 70px;
+}
+
+.v-tags-view__item[data-v-a7799cdb]{
+  border-radius: 4px !important; 
+  margin-left: 10px !important;
+  top: 0 !important;
+  height: calc(100% - 0px) !important;
+}
 </style>

+ 54 - 0
src/api/workbench/index.ts

@@ -0,0 +1,54 @@
+import request from '@/config/axios'
+
+export interface WorkbenchVO {
+    //   id: number
+    //   mail: string
+    //   username: string
+    //   password: string
+    //   host: string
+    //   port: number
+    //   sslEnable: boolean
+    //   starttlsEnable: boolean
+    // name:value
+    // 'samples': {
+    //     '标本总数': number
+    //     '矿物': number
+    //    ' 化石': number
+    //     '陨石': number
+    //     '岩石矿石': number
+    // }
+    inStockCount:number
+    outStockCount:number
+    returnCount:number
+    year:string
+}
+
+// 获得按类型分类标本总数
+export const getSpecimenTypeList = async () => {
+    return await request.get({ url: '/museums/specimen-info/statistics/allType' })
+}
+
+// 获得按类型分类标本数(按年份)
+export const getSpecimenYearList = async () => {
+    return await request.get({ url: '/museums/specimen-info/statistics/yearly' })
+}
+
+// 获得标本来源列表
+export const getSpecimenSourceList = async () => {
+    return await request.get({ url: '/museums/specimen-info/statistics/source' })
+}
+
+// 获得本年标本入库数量(按月份)
+export const getEnterSpecimenList = async (year: number) => {
+  return await request.get({ url:  `/museums/specimen-info/statistics/entry/${year}` })
+}
+
+// 获得本年标本出库数量(按月份)
+export const getOutSpecimenList = async (year: number) => {
+    return await request.get({ url:  `/museums/specimen-outbound/statistics/outgoing/${year}` })
+  }
+
+  // 获得本年标本回库数量(按月份)
+export const getReturnSpecimenList = async (year: number) => {
+    return await request.get({ url:  `/museums/specimen-outbound/statistics/return/${year}` })
+  }

BIN
src/assets/imgs/ck5.png


BIN
src/assets/imgs/hk2.png


BIN
src/assets/imgs/hs4.png


BIN
src/assets/imgs/kw4.png


BIN
src/assets/imgs/kw5.png


BIN
src/assets/imgs/logo.png


BIN
src/assets/imgs/rk3.png


BIN
src/assets/imgs/tj3.png


BIN
src/assets/imgs/ys5.png


BIN
src/assets/imgs/zs2.png


+ 2 - 2
src/layout/components/Logo/src/Logo.vue

@@ -66,10 +66,10 @@ watch(
       ]"
       to="/"
     >
-      <img
+      <!-- <img
         class="h-[calc(var(--logo-height)-10px)] w-[calc(var(--logo-height)-10px)]"
         src="@/assets/imgs/logo.png"
-      />
+      /> -->
       <div
         v-if="show"
         :class="[

+ 49 - 160
src/layout/components/TagsView/src/TagsView.vue

@@ -8,6 +8,8 @@ import { useAppStore } from '@/store/modules/app'
 import { useI18n } from '@/hooks/web/useI18n'
 import { filterAffixTags } from './helper'
 import { ContextMenu, ContextMenuExpose } from '@/layout/components/ContextMenu'
+import ToolHeader from '@/layout/components/ToolHeader1.vue'
+// import  ToolHeader  from '../../ToolHeader.vue'
 import { useDesign } from '@/hooks/web/useDesign'
 import { useTemplateRefsList } from '@vueuse/core'
 import { ElScrollbar } from 'element-plus'
@@ -262,40 +264,20 @@ watch(
 </script>
 
 <template>
-  <div
-    :id="prefixCls"
-    :class="prefixCls"
-    class="relative w-full flex bg-[#fff] dark:bg-[var(--el-bg-color)]"
-  >
-    <span
-      :class="tagsViewImmerse ? '' : `${prefixCls}__tool ${prefixCls}__tool--first`"
-      class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
-      @click="move(-200)"
-    >
-      <Icon
-        :hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
-        color="var(--el-text-color-placeholder)"
-        icon="ep:d-arrow-left"
-      />
-    </span>
-    <div class="flex-1 overflow-hidden">
-      <ElScrollbar ref="scrollbarRef" class="h-full" @scroll="scroll">
-        <div class="h-[var(--tags-view-height)] flex">
-          <ContextMenu
-            v-for="item in visitedViews"
-            :key="item.fullPath"
-            :ref="itemRefs.set"
-            :class="[
-              `${prefixCls}__item`,
-              tagsViewImmerse ? `${prefixCls}__item--immerse` : '',
-              tagsViewIcon ? `${prefixCls}__item--icon` : '',
-              tagsViewImmerse && tagsViewIcon ? `${prefixCls}__item--immerse--icon` : '',
-              item?.meta?.affix ? `${prefixCls}__item--affix` : '',
-              {
-                'is-active': isActive(item)
-              }
-            ]"
-            :schema="[
+  <div class="flex-1 overflow-hidden" style="display: none;">
+    <!-- <span>eeeee</span> -->
+    <ElScrollbar ref="scrollbarRef" class="h-full" @scroll="scroll">
+      <div class="h-[var(--tags-view-height)] flex">
+        <ContextMenu v-for="item in visitedViews" :key="item.fullPath" :ref="itemRefs.set" :class="[
+          `${prefixCls}__item`,
+          tagsViewImmerse ? `${prefixCls}__item--immerse` : '',
+          tagsViewIcon ? `${prefixCls}__item--icon` : '',
+          tagsViewImmerse && tagsViewIcon ? `${prefixCls}__item--immerse--icon` : '',
+          item?.meta?.affix ? `${prefixCls}__item--affix` : '',
+          {
+            'is-active': isActive(item)
+          }
+        ]" :schema="[
               {
                 icon: 'ep:refresh',
                 label: t('common.reload'),
@@ -351,130 +333,38 @@ watch(
                   closeAllTags()
                 }
               }
-            ]"
-            :tag-item="item"
-            @visible-change="visibleChange"
-          >
-            <div>
-              <router-link :ref="tagLinksRefs.set" v-slot="{ navigate }" :to="{ ...item }" custom>
-                <div
-                  :class="`h-full flex items-center justify-center whitespace-nowrap pl-15px ${prefixCls}__item--label`"
-                  @click="navigate"
-                >
-                  <Icon
-                    v-if="
-                      tagsViewIcon &&
-                      (item?.meta?.icon ||
-                        (item?.matched &&
-                          item.matched[0] &&
-                          item.matched[item.matched.length - 1].meta?.icon))
-                    "
-                    :icon="item?.meta?.icon || item.matched[item.matched.length - 1].meta.icon"
-                    :size="12"
-                    class="mr-5px"
-                  />
-                  {{ t(item?.meta?.title as string) }}
-                  <Icon
-                    :class="`${prefixCls}__item--close`"
-                    :size="12"
-                    color="#333"
-                    icon="ep:close"
-                    @click.prevent.stop="closeSelectedTag(item)"
-                  />
-                </div>
-              </router-link>
-            </div>
-          </ContextMenu>
-        </div>
-      </ElScrollbar>
-    </div>
-    <span
-      :class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
-      class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
-      @click="move(200)"
-    >
-      <Icon
-        :hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
-        color="var(--el-text-color-placeholder)"
-        icon="ep:d-arrow-right"
-      />
-    </span>
-    <span
-      :class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
-      class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
-      @click="refreshSelectedTag(selectedTag)"
-    >
-      <Icon
-        :hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
-        color="var(--el-text-color-placeholder)"
-        icon="ep:refresh-right"
-      />
-    </span>
-    <ContextMenu
-      :schema="[
-        {
-          icon: 'ep:refresh',
-          label: t('common.reload'),
-          command: () => {
-            refreshSelectedTag(selectedTag)
-          }
-        },
-        {
-          icon: 'ep:close',
-          label: t('common.closeTab'),
-          disabled: !!visitedViews?.length && selectedTag?.meta.affix,
-          command: () => {
-            closeSelectedTag(selectedTag!)
-          }
-        },
-        {
-          divided: true,
-          icon: 'ep:d-arrow-left',
-          label: t('common.closeTheLeftTab'),
-          disabled: !!visitedViews?.length && selectedTag?.fullPath === visitedViews[0].fullPath,
-          command: () => {
-            closeLeftTags()
-          }
-        },
-        {
-          icon: 'ep:d-arrow-right',
-          label: t('common.closeTheRightTab'),
-          disabled:
-            !!visitedViews?.length &&
-            selectedTag?.fullPath === visitedViews[visitedViews.length - 1].fullPath,
-          command: () => {
-            closeRightTags()
-          }
-        },
-        {
-          divided: true,
-          icon: 'ep:discount',
-          label: t('common.closeOther'),
-          command: () => {
-            closeOthersTags()
-          }
-        },
-        {
-          icon: 'ep:minus',
-          label: t('common.closeAll'),
-          command: () => {
-            closeAllTags()
-          }
-        }
-      ]"
-      trigger="click"
-    >
-      <span
-        :class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
-        class="block h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
-      >
-        <Icon
-          :hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
-          color="var(--el-text-color-placeholder)"
-          icon="ep:menu"
-        />
-      </span>
-    </ContextMenu>
+            ]" :tag-item="item" @visible-change="visibleChange">
+          <div>
+            <router-link :ref="tagLinksRefs.set" v-slot="{ navigate }" :to="{ ...item }" custom>
+              <div
+                :class="`h-full flex items-center justify-center whitespace-nowrap pl-15px ${prefixCls}__item--label`"
+                @click="navigate">
+                <Icon v-if="
+                  tagsViewIcon &&
+                  (item?.meta?.icon ||
+                    (item?.matched &&
+                      item.matched[0] &&
+                      item.matched[item.matched.length - 1].meta?.icon))
+                " :icon="item?.meta?.icon || item.matched[item.matched.length - 1].meta.icon" :size="12"
+                  class="mr-5px" />
+                {{ t(item?.meta?.title as string) }}
+                <Icon :class="`${prefixCls}__item--close`" :size="12" color="#333" icon="ep:close"
+                  @click.prevent.stop="closeSelectedTag(item)" />
+
+              </div>
+
+            </router-link>
+          </div>
+        </ContextMenu>
+        <span :class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
+          class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
+          @click="refreshSelectedTag(selectedTag)">
+          <Icon :hover-color="isDark ? '#fff' : 'var(--el-color-black)'" color="var(--el-text-color-placeholder)"
+            icon="ep:refresh-right" />
+        </span>
+      </div>
+    </ElScrollbar>
+
   </div>
 </template>
 
@@ -568,8 +458,7 @@ $prefix-cls: #{$namespace}-tags-view;
     padding-right: 35px;
     margin: 0 -10px;
     border: none !important;
-    -webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='68' height='34' viewBox='0 0 68 34' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m27,0c-7.99582,0 -11.95105,0.00205 -12,12l0,6c0,8.284 -0.48549,16.49691 -8.76949,16.49691l54.37857,-0.11145c-8.284,0 -8.60908,-8.10146 -8.60908,-16.38546l0,-6c0.11145,-12.08445 -4.38441,-12 -12,-12l-13,0z' fill='%23409eff'/%3E%3C/svg%3E")
-      12 27 15;
+    -webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='68' height='34' viewBox='0 0 68 34' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m27,0c-7.99582,0 -11.95105,0.00205 -12,12l0,6c0,8.284 -0.48549,16.49691 -8.76949,16.49691l54.37857,-0.11145c-8.284,0 -8.60908,-8.10146 -8.60908,-16.38546l0,-6c0.11145,-12.08445 -4.38441,-12 -12,-12l-13,0z' fill='%23409eff'/%3E%3C/svg%3E") 12 27 15;
 
     .#{$prefix-cls}__item--label {
       padding-left: 35px;

+ 529 - 0
src/layout/components/TagsView/src/TagsView1.vue

@@ -0,0 +1,529 @@
+<script lang="ts" setup>
+import { computed, nextTick, onMounted, ref, unref, watch } from 'vue'
+import type { RouteLocationNormalizedLoaded, RouterLinkProps } from 'vue-router'
+import { useRouter } from 'vue-router'
+import { usePermissionStore } from '@/store/modules/permission'
+import { useTagsViewStore } from '@/store/modules/tagsView'
+import { useAppStore } from '@/store/modules/app'
+import { useI18n } from '@/hooks/web/useI18n'
+import { filterAffixTags } from './helper'
+import { ContextMenu, ContextMenuExpose } from '@/layout/components/ContextMenu'
+import ToolHeader from '@/layout/components/ToolHeader1.vue'
+// import  ToolHeader  from '../../ToolHeader.vue'
+import { useDesign } from '@/hooks/web/useDesign'
+import { useTemplateRefsList } from '@vueuse/core'
+import { ElScrollbar } from 'element-plus'
+import { useScrollTo } from '@/hooks/event/useScrollTo'
+
+const { getPrefixCls } = useDesign()
+
+const prefixCls = getPrefixCls('tags-view')
+
+const { t } = useI18n()
+
+const { currentRoute, push, replace } = useRouter()
+
+const permissionStore = usePermissionStore()
+
+const routers = computed(() => permissionStore.getRouters)
+
+const tagsViewStore = useTagsViewStore()
+
+const visitedViews = computed(() => tagsViewStore.getVisitedViews)
+
+const affixTagArr = ref<RouteLocationNormalizedLoaded[]>([])
+
+const appStore = useAppStore()
+
+const tagsViewImmerse = computed(() => appStore.getTagsViewImmerse)
+
+const tagsViewIcon = computed(() => appStore.getTagsViewIcon)
+
+const isDark = computed(() => appStore.getIsDark)
+
+// 初始化tag
+const initTags = () => {
+  affixTagArr.value = filterAffixTags(unref(routers))
+  for (const tag of unref(affixTagArr)) {
+    // Must have tag name
+    if (tag.name) {
+      tagsViewStore.addVisitedView(tag)
+    }
+  }
+}
+
+const selectedTag = ref<RouteLocationNormalizedLoaded>()
+
+// 新增tag
+const addTags = () => {
+  const { name } = unref(currentRoute)
+  if (name) {
+    selectedTag.value = unref(currentRoute)
+    tagsViewStore.addView(unref(currentRoute))
+  }
+  return false
+}
+
+// 关闭选中的tag
+const closeSelectedTag = (view: RouteLocationNormalizedLoaded) => {
+  if (view?.meta?.affix) return
+  tagsViewStore.delView(view)
+  if (isActive(view)) {
+    toLastView()
+  }
+}
+
+// 关闭全部
+const closeAllTags = () => {
+  tagsViewStore.delAllViews()
+  toLastView()
+}
+
+// 关闭其它
+const closeOthersTags = () => {
+  tagsViewStore.delOthersViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
+}
+
+// 重新加载
+const refreshSelectedTag = async (view?: RouteLocationNormalizedLoaded) => {
+  if (!view) return
+  tagsViewStore.delCachedView()
+  const { path, query } = view
+  await nextTick()
+  replace({
+    path: '/redirect' + path,
+    query: query
+  })
+}
+
+// 关闭左侧
+const closeLeftTags = () => {
+  tagsViewStore.delLeftViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
+}
+
+// 关闭右侧
+const closeRightTags = () => {
+  tagsViewStore.delRightViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
+}
+
+// 跳转到最后一个
+const toLastView = () => {
+  const visitedViews = tagsViewStore.getVisitedViews
+  const latestView = visitedViews.slice(-1)[0]
+  if (latestView) {
+    push(latestView)
+  } else {
+    if (
+      unref(currentRoute).path === permissionStore.getAddRouters[0].path ||
+      unref(currentRoute).path === permissionStore.getAddRouters[0].redirect
+    ) {
+      addTags()
+      return
+    }
+    // TODO: You can set another route
+    push('/')
+  }
+}
+
+// 滚动到选中的tag
+const moveToCurrentTag = async () => {
+  await nextTick()
+  for (const v of unref(visitedViews)) {
+    if (v.fullPath === unref(currentRoute).path) {
+      moveToTarget(v)
+      if (v.fullPath !== unref(currentRoute).fullPath) {
+        tagsViewStore.updateVisitedView(unref(currentRoute))
+      }
+
+      break
+    }
+  }
+}
+
+const tagLinksRefs = useTemplateRefsList<RouterLinkProps>()
+
+const moveToTarget = (currentTag: RouteLocationNormalizedLoaded) => {
+  const wrap$ = unref(scrollbarRef)?.wrapRef
+  let firstTag: Nullable<RouterLinkProps> = null
+  let lastTag: Nullable<RouterLinkProps> = null
+
+  const tagList = unref(tagLinksRefs)
+  // find first tag and last tag
+  if (tagList.length > 0) {
+    firstTag = tagList[0]
+    lastTag = tagList[tagList.length - 1]
+  }
+  if ((firstTag?.to as RouteLocationNormalizedLoaded).fullPath === currentTag.fullPath) {
+    // 直接滚动到0的位置
+    const { start } = useScrollTo({
+      el: wrap$!,
+      position: 'scrollLeft',
+      to: 0,
+      duration: 500
+    })
+    start()
+  } else if ((lastTag?.to as RouteLocationNormalizedLoaded).fullPath === currentTag.fullPath) {
+    // 滚动到最后的位置
+    const { start } = useScrollTo({
+      el: wrap$!,
+      position: 'scrollLeft',
+      to: wrap$!.scrollWidth - wrap$!.offsetWidth,
+      duration: 500
+    })
+    start()
+  } else {
+    // find preTag and nextTag
+    const currentIndex: number = tagList.findIndex(
+      (item) => (item?.to as RouteLocationNormalizedLoaded).fullPath === currentTag.fullPath
+    )
+    const tgsRefs = document.getElementsByClassName(`${prefixCls}__item`)
+
+    const prevTag = tgsRefs[currentIndex - 1] as HTMLElement
+    const nextTag = tgsRefs[currentIndex + 1] as HTMLElement
+
+    // the tag's offsetLeft after of nextTag
+    const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + 4
+
+    // the tag's offsetLeft before of prevTag
+    const beforePrevTagOffsetLeft = prevTag.offsetLeft - 4
+
+    if (afterNextTagOffsetLeft > unref(scrollLeftNumber) + wrap$!.offsetWidth) {
+      const { start } = useScrollTo({
+        el: wrap$!,
+        position: 'scrollLeft',
+        to: afterNextTagOffsetLeft - wrap$!.offsetWidth,
+        duration: 500
+      })
+      start()
+    } else if (beforePrevTagOffsetLeft < unref(scrollLeftNumber)) {
+      const { start } = useScrollTo({
+        el: wrap$!,
+        position: 'scrollLeft',
+        to: beforePrevTagOffsetLeft,
+        duration: 500
+      })
+      start()
+    }
+  }
+}
+
+// 是否是当前tag
+const isActive = (route: RouteLocationNormalizedLoaded): boolean => {
+  return route.path === unref(currentRoute).path
+}
+
+// 所有右键菜单组件的元素
+const itemRefs = useTemplateRefsList<ComponentRef<typeof ContextMenu & ContextMenuExpose>>()
+
+// 右键菜单装填改变的时候
+const visibleChange = (visible: boolean, tagItem: RouteLocationNormalizedLoaded) => {
+  if (visible) {
+    for (const v of unref(itemRefs)) {
+      const elDropdownMenuRef = v.elDropdownMenuRef
+      if (tagItem.fullPath !== v.tagItem.fullPath) {
+        elDropdownMenuRef?.handleClose()
+      }
+    }
+  }
+}
+
+// elscroll 实例
+const scrollbarRef = ref<ComponentRef<typeof ElScrollbar>>()
+
+// 保存滚动位置
+const scrollLeftNumber = ref(0)
+
+const scroll = ({ scrollLeft }) => {
+  scrollLeftNumber.value = scrollLeft as number
+}
+
+// 移动到某个位置
+const move = (to: number) => {
+  const wrap$ = unref(scrollbarRef)?.wrapRef
+  const { start } = useScrollTo({
+    el: wrap$!,
+    position: 'scrollLeft',
+    to: unref(scrollLeftNumber) + to,
+    duration: 500
+  })
+  start()
+}
+
+onMounted(() => {
+  initTags()
+  addTags()
+})
+
+watch(
+  () => currentRoute.value,
+  () => {
+    addTags()
+    moveToCurrentTag()
+  }
+)
+</script>
+
+<template>
+  <div class="flex-1 overflow-hidden">
+    <!-- <span>eeeee</span> -->
+    <ElScrollbar ref="scrollbarRef" class="h-full" @scroll="scroll">
+      <div class="h-[var(--tags-view-height)] flex">
+        <ContextMenu v-for="item in visitedViews" :key="item.fullPath" :ref="itemRefs.set" :class="[
+          `${prefixCls}__item`,
+          tagsViewImmerse ? `${prefixCls}__item--immerse` : '',
+          tagsViewIcon ? `${prefixCls}__item--icon` : '',
+          tagsViewImmerse && tagsViewIcon ? `${prefixCls}__item--immerse--icon` : '',
+          item?.meta?.affix ? `${prefixCls}__item--affix` : '',
+          {
+            'is-active': isActive(item)
+          }
+        ]" :schema="[
+              {
+                icon: 'ep:refresh',
+                label: t('common.reload'),
+                disabled: selectedTag?.fullPath !== item.fullPath,
+                command: () => {
+                  refreshSelectedTag(item)
+                }
+              },
+              {
+                icon: 'ep:close',
+                label: t('common.closeTab'),
+                disabled: !!visitedViews?.length && selectedTag?.meta.affix,
+                command: () => {
+                  closeSelectedTag(item)
+                }
+              },
+              {
+                divided: true,
+                icon: 'ep:d-arrow-left',
+                label: t('common.closeTheLeftTab'),
+                disabled:
+                  !!visitedViews?.length &&
+                  (item.fullPath === visitedViews[0].fullPath ||
+                    selectedTag?.fullPath !== item.fullPath),
+                command: () => {
+                  closeLeftTags()
+                }
+              },
+              {
+                icon: 'ep:d-arrow-right',
+                label: t('common.closeTheRightTab'),
+                disabled:
+                  !!visitedViews?.length &&
+                  (item.fullPath === visitedViews[visitedViews.length - 1].fullPath ||
+                    selectedTag?.fullPath !== item.fullPath),
+                command: () => {
+                  closeRightTags()
+                }
+              },
+              {
+                divided: true,
+                icon: 'ep:discount',
+                label: t('common.closeOther'),
+                disabled: selectedTag?.fullPath !== item.fullPath,
+                command: () => {
+                  closeOthersTags()
+                }
+              },
+              {
+                icon: 'ep:minus',
+                label: t('common.closeAll'),
+                command: () => {
+                  closeAllTags()
+                }
+              }
+            ]" :tag-item="item" @visible-change="visibleChange">
+          <div>
+            <router-link :ref="tagLinksRefs.set" v-slot="{ navigate }" :to="{ ...item }" custom>
+              <div
+                :class="`h-full flex items-center justify-center whitespace-nowrap pl-15px ${prefixCls}__item--label`"
+                @click="navigate">
+                <Icon v-if="
+                  tagsViewIcon &&
+                  (item?.meta?.icon ||
+                    (item?.matched &&
+                      item.matched[0] &&
+                      item.matched[item.matched.length - 1].meta?.icon))
+                " :icon="item?.meta?.icon || item.matched[item.matched.length - 1].meta.icon" :size="12"
+                  class="mr-5px" />
+                {{ t(item?.meta?.title as string) }}
+                <Icon :class="`${prefixCls}__item--close`" :size="12" color="#333" icon="ep:close"
+                  @click.prevent.stop="closeSelectedTag(item)" />
+
+              </div>
+
+            </router-link>
+          </div>
+        </ContextMenu>
+        <span :class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
+          class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
+          @click="refreshSelectedTag(selectedTag)">
+          <Icon :hover-color="isDark ? '#fff' : 'var(--el-color-black)'" color="var(--el-text-color-placeholder)"
+            icon="ep:refresh-right" />
+        </span>
+      </div>
+    </ElScrollbar>
+
+  </div>
+</template>
+
+<style lang="scss" scoped>
+$prefix-cls: #{$namespace}-tags-view;
+
+.#{$prefix-cls} {
+  :deep(.#{$elNamespace}-scrollbar__view) {
+    height: 100%;
+  }
+
+  &__tool {
+    position: relative;
+
+    &::before {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      border-left: 1px solid var(--el-border-color);
+      content: '';
+    }
+
+    &--first {
+      &::before {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        border-right: 1px solid var(--el-border-color);
+        border-left: none;
+        content: '';
+      }
+    }
+  }
+
+  &__item {
+    position: relative;
+    top: 3px;
+    height: calc(100% - 6px);
+    padding-right: 15px;
+    margin-left: 4px;
+    font-size: 12px;
+    cursor: pointer;
+    border: 1px solid #d9d9d9;
+    border-radius: 2px;
+    box-sizing: border-box;
+
+    &--close {
+      position: absolute;
+      top: 50%;
+      right: 5px;
+      display: none;
+      transform: translate(0, -50%);
+    }
+
+    &:not(.#{$prefix-cls}__item--affix):hover {
+      .#{$prefix-cls}__item--close {
+        display: block;
+      }
+    }
+  }
+
+  &__item--icon {
+    padding-right: 20px;
+  }
+
+  &__item:not(.is-active) {
+    &:hover {
+      color: var(--el-color-primary);
+    }
+  }
+
+  &__item.is-active {
+    color: var(--el-color-white);
+    background-color: var(--el-color-primary);
+    border: 1px solid var(--el-color-primary);
+
+    .#{$prefix-cls}__item--close {
+      :deep(span) {
+        color: var(--el-color-white) !important;
+      }
+    }
+  }
+
+  &__item--immerse {
+    top: 2px;
+    height: calc(100% - 3px);
+    padding-right: 35px;
+    margin: 0 -10px;
+    border: none !important;
+    -webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='68' height='34' viewBox='0 0 68 34' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m27,0c-7.99582,0 -11.95105,0.00205 -12,12l0,6c0,8.284 -0.48549,16.49691 -8.76949,16.49691l54.37857,-0.11145c-8.284,0 -8.60908,-8.10146 -8.60908,-16.38546l0,-6c0.11145,-12.08445 -4.38441,-12 -12,-12l-13,0z' fill='%23409eff'/%3E%3C/svg%3E") 12 27 15;
+
+    .#{$prefix-cls}__item--label {
+      padding-left: 35px;
+    }
+
+    .#{$prefix-cls}__item--close {
+      right: 20px;
+    }
+  }
+
+  &__item--immerse--icon {
+    padding-right: 35px;
+  }
+
+  &__item--immerse:not(.is-active) {
+    &:hover {
+      color: var(--el-color-white);
+      background-color: var(--el-color-primary);
+
+      .#{$prefix-cls}__item--close {
+        :deep(span) {
+          color: var(--el-color-white) !important;
+        }
+      }
+    }
+  }
+}
+
+.dark {
+  .#{$prefix-cls} {
+    &__tool {
+      &--first {
+        &::after {
+          display: none;
+        }
+      }
+    }
+
+    &__item {
+      border: 1px solid var(--el-border-color);
+    }
+
+    &__item:not(.is-active) {
+      &:hover {
+        color: var(--el-color-primary);
+      }
+    }
+
+    &__item.is-active {
+      color: var(--el-color-white);
+      background-color: var(--el-color-primary);
+      border: 1px solid var(--el-color-primary);
+
+      .#{$prefix-cls}__item--close {
+        :deep(span) {
+          color: var(--el-color-white) !important;
+        }
+      }
+    }
+
+    &__item--immerse:not(.is-active) {
+      &:hover {
+        color: var(--el-color-white);
+      }
+    }
+  }
+}
+</style>

+ 11 - 8
src/layout/components/ToolHeader.vue

@@ -10,7 +10,7 @@ import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
 import RouterSearch from '@/components/RouterSearch/index.vue'
 import { useAppStore } from '@/store/modules/app'
 import { useDesign } from '@/hooks/web/useDesign'
-
+import  TagsView  from '@/layout/components/TagsView/src/TagsView1.vue'
 const { getPrefixCls, variables } = useDesign()
 
 const prefixCls = getPrefixCls('tool-header')
@@ -53,31 +53,34 @@ export default defineComponent({
           'dark:bg-[var(--el-bg-color)]'
         ]}
       >
+      <TagsView/>
         {layout.value !== 'top' ? (
           <div class="h-full flex items-center">
+           
+            {breadcrumb.value ? <Breadcrumb class="lt-md:hidden"></Breadcrumb> : undefined}
             {hamburger.value && layout.value !== 'cutMenu' ? (
               <Collapse class="custom-hover" color="var(--top-header-text-color)"></Collapse>
             ) : undefined}
-            {breadcrumb.value ? <Breadcrumb class="lt-md:hidden"></Breadcrumb> : undefined}
           </div>
         ) : undefined}
         <div class="h-full flex items-center">
+
           {screenfull.value ? (
             <Screenfull class="custom-hover" color="var(--top-header-text-color)"></Screenfull>
           ) : undefined}
           {search.value ? <RouterSearch isModal={false} /> : undefined}
-          {size.value ? (
+          {/* {size.value ? (
             <SizeDropdown class="custom-hover" color="var(--top-header-text-color)"></SizeDropdown>
-          ) : undefined}
-          {locale.value ? (
+          ) : undefined} */}
+          {/* {locale.value ? (
             <LocaleDropdown
               class="custom-hover"
               color="var(--top-header-text-color)"
             ></LocaleDropdown>
-          ) : undefined}
-          {message.value ? (
+          ) : undefined} */}
+          {/* {message.value ? (
             <Message class="custom-hover" color="var(--top-header-text-color)"></Message>
-          ) : undefined}
+          ) : undefined} */}
           <UserInfo></UserInfo>
         </div>
       </div>

+ 54 - 0
src/layout/components/ToolHeader1.vue

@@ -0,0 +1,54 @@
+<script lang="tsx">
+import { defineComponent, computed } from 'vue'
+import { Collapse } from '@/layout/components/Collapse'
+import { useAppStore } from '@/store/modules/app'
+import { useDesign } from '@/hooks/web/useDesign'
+
+const { getPrefixCls, variables } = useDesign()
+
+
+
+const appStore = useAppStore()
+
+
+// 折叠图标
+const hamburger = computed(() => appStore.getHamburger)
+
+
+// 布局
+const layout = computed(() => appStore.getLayout)
+
+
+export default defineComponent({
+  name: 'ToolHeader',
+  setup() {
+    return () => (
+      <div
+        id={`${variables.namespace}-tool-header`}
+      >
+        {layout.value !== 'top' ? (
+          <div class="h-full flex items-center toolhamburger">
+            {hamburger.value && layout.value !== 'cutMenu' ? (
+              <Collapse class="custom-hover" color="var(--top-header-text-color)"></Collapse>
+            ) : undefined}
+          
+          </div>
+        ) : undefined}
+       
+      </div>
+    )
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+$prefix-cls: #{$namespace}-tool-header;
+
+.#{$prefix-cls} {
+  transition: left var(--transition-time-02);
+}
+
+.toolhamburger{
+  margin-top: 8px;
+}
+</style>

+ 41 - 25
src/store/modules/app.ts

@@ -48,31 +48,47 @@ export const useAppStore = defineStore('app', {
       title: import.meta.env.VITE_APP_TITLE, // 标题
       pageLoading: false, // 路由跳转loading
 
-      breadcrumb: true, // 面包屑
-      breadcrumbIcon: true, // 面包屑图标
-      collapse: false, // 折叠菜单
-      uniqueOpened: true, // 是否只保持一个子菜单的展开
-      hamburger: true, // 折叠图标
-      screenfull: true, // 全屏图标
-      search: true, // 搜索图标
-      size: true, // 尺寸图标
-      locale: true, // 多语言图标
-      message: true, // 消息图标
-      tagsView: true, // 标签页
-      tagsViewImmerse: false, // 标签页沉浸
-      tagsViewIcon: true, // 是否显示标签图标
-      logo: true, // logo
-      fixedHeader: true, // 固定toolheader
-      footer: true, // 显示页脚
-      greyMode: false, // 是否开始灰色模式,用于特殊悼念日
-      fixedMenu: wsCache.get('fixedMenu') || false, // 是否固定菜单
-
-      layout: wsCache.get(CACHE_KEY.LAYOUT) || 'classic', // layout布局
-      isDark: wsCache.get(CACHE_KEY.IS_DARK) || false, // 是否是暗黑模式
-      currentSize: wsCache.get('default') || 'default', // 组件尺寸
-      theme: wsCache.get(CACHE_KEY.THEME) || {
+    
+      // 面包屑
+      breadcrumb: true,
+      // 面包屑图标
+      breadcrumbIcon: true,
+      // 折叠图标
+      hamburger: true,
+      // 全屏图标
+      screenfull: true,
+      // 尺寸图标
+      size: true,
+      // 多语言图标
+      locale: true,
+      // 消息图标
+      message: true,
+      // 标签页
+      tagsView: true,
+      // 标签页
+      tagsViewImmerse: false,
+      // 标签页图标
+      tagsViewIcon: true,
+      // logo
+      logo: true,
+      // 菜单手风琴
+      uniqueOpened: true,
+      // 固定header
+      fixedHeader: true,
+      // 页脚
+      footer: true,
+      // 灰色模式
+      greyMode: false,
+      // layout布局
+      layout: 'classic',
+      // 暗黑模式
+      isDark: false,
+      // 组件尺寸
+      currentSize: 'default',
+      // 主题相关
+      theme: {
         // 主题色
-        elColorPrimary: '#409eff',
+        elColorPrimary: '#dcb86b',
         // 左侧菜单边框颜色
         leftMenuBorderColor: 'inherit',
         // 左侧菜单背景颜色
@@ -90,7 +106,7 @@ export const useAppStore = defineStore('app', {
         // logo字体颜色
         logoTitleTextColor: '#fff',
         // logo边框颜色
-        logoBorderColor: 'inherit',
+        logoBorderColor: '#001529',
         // 头部背景颜色
         topHeaderBgColor: '#fff',
         // 头部字体颜色

+ 533 - 211
src/views/Home/Index.vue

@@ -1,188 +1,290 @@
 <template>
-  <div>
-    <el-card shadow="never">
-      <el-skeleton :loading="loading" animated>
-        <el-row :gutter="16" justify="space-between">
-          <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
-            <div class="flex items-center">
-              <el-avatar :src="avatar" :size="70" class="mr-16px">
-                <img src="@/assets/imgs/avatar.gif" alt="" />
-              </el-avatar>
-              <div>
-                <div class="text-20px">
-                  {{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
-                </div>
-                <div class="mt-10px text-14px text-gray-500">
-                  {{ t('workplace.toady') }},20℃ - 32℃!
+  <div class="bg">
+    <!-- <div>
+      <el-card shadow="never">
+        <el-skeleton :loading="loading" animated>
+          <el-row :gutter="16" justify="space-between">
+            <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
+              <div class="flex items-center">
+                <el-avatar :src="avatar" :size="70" class="mr-16px">
+                  <img src="@/assets/imgs/avatar.gif" alt="" />
+                </el-avatar>
+                <div>
+                  <div class="text-20px">
+                    {{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
+                  </div>
+                  <div class="mt-10px text-14px text-gray-500">
+                    {{ t('workplace.toady') }},20℃ - 32℃!
+                  </div>
                 </div>
               </div>
-            </div>
-          </el-col>
-          <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
-            <div class="h-70px flex items-center justify-end lt-sm:mt-10px">
-              <div class="px-8px text-right">
-                <div class="mb-16px text-14px text-gray-400">{{ t('workplace.project') }}</div>
-                <CountTo
-                  class="text-20px"
-                  :start-val="0"
-                  :end-val="totalSate.project"
-                  :duration="2600"
-                />
-              </div>
-              <el-divider direction="vertical" />
-              <div class="px-8px text-right">
-                <div class="mb-16px text-14px text-gray-400">{{ t('workplace.toDo') }}</div>
-                <CountTo
-                  class="text-20px"
-                  :start-val="0"
-                  :end-val="totalSate.todo"
-                  :duration="2600"
-                />
-              </div>
-              <el-divider direction="vertical" border-style="dashed" />
-              <div class="px-8px text-right">
-                <div class="mb-16px text-14px text-gray-400">{{ t('workplace.access') }}</div>
-                <CountTo
-                  class="text-20px"
-                  :start-val="0"
-                  :end-val="totalSate.access"
-                  :duration="2600"
-                />
-              </div>
-            </div>
-          </el-col>
-        </el-row>
-      </el-skeleton>
-    </el-card>
-  </div>
+            </el-col>
 
-  <el-row class="mt-8px" :gutter="8" justify="space-between">
-    <el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-8px">
-      <el-card shadow="never">
-        <template #header>
-          <div class="h-3 flex justify-between">
-            <span>{{ t('workplace.project') }}</span>
-            <el-link
-              type="primary"
-              :underline="false"
-              href="https://github.com/yudaocode"
-              target="_blank"
-            >
-              {{ t('action.more') }}
-            </el-link>
-          </div>
-        </template>
-        <el-skeleton :loading="loading" animated>
-          <el-row>
-            <el-col
-              v-for="(item, index) in projects"
-              :key="`card-${index}`"
-              :xl="8"
-              :lg="8"
-              :md="8"
-              :sm="24"
-              :xs="24"
-            >
-              <el-card shadow="hover" class="mr-5px mt-5px">
-                <div class="flex items-center">
-                  <Icon :icon="item.icon" :size="50" class="mr-8px" />
-                  <span class="text-16px">{{ item.name }}</span>
+            <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
+              <div class="h-70px flex items-center justify-end lt-sm:mt-10px">
+                <div class="px-8px text-right">
+                  <div class="mb-16px text-14px text-gray-400">{{ t('workplace.project') }}</div>
+                  <CountTo class="text-20px" :start-val="0" :end-val="totalSate.project" :duration="2600" />
+                </div>
+                <el-divider direction="vertical" />
+                <div class="px-8px text-right">
+                  <div class="mb-16px text-14px text-gray-400">{{ t('workplace.toDo') }}</div>
+                  <CountTo class="text-20px" :start-val="0" :end-val="totalSate.todo" :duration="2600" />
                 </div>
-                <div class="mt-12px text-9px text-gray-400">{{ t(item.message) }}</div>
-                <div class="mt-12px flex justify-between text-12px text-gray-400">
-                  <span>{{ item.personal }}</span>
-                  <span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span>
+                <el-divider direction="vertical" border-style="dashed" />
+                <div class="px-8px text-right">
+                  <div class="mb-16px text-14px text-gray-400">{{ t('workplace.access') }}</div>
+                  <CountTo class="text-20px" :start-val="0" :end-val="totalSate.access" :duration="2600" />
                 </div>
-              </el-card>
+              </div>
             </el-col>
           </el-row>
         </el-skeleton>
       </el-card>
+    </div> -->
 
-      <el-card shadow="never" class="mt-8px">
-        <el-skeleton :loading="loading" animated>
-          <el-row :gutter="20" justify="space-between">
-            <el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
-              <el-card shadow="hover" class="mb-8px">
-                <el-skeleton :loading="loading" animated>
-                  <Echart :options="pieOptionsData" :height="280" />
-                </el-skeleton>
-              </el-card>
-            </el-col>
-            <el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
-              <el-card shadow="hover" class="mb-8px">
-                <el-skeleton :loading="loading" animated>
-                  <Echart :options="barOptionsData" :height="280" />
-                </el-skeleton>
-              </el-card>
-            </el-col>
-          </el-row>
-        </el-skeleton>
-      </el-card>
-    </el-col>
-    <el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-8px">
-      <el-card shadow="never">
-        <template #header>
-          <div class="h-3 flex justify-between">
-            <span>{{ t('workplace.shortcutOperation') }}</span>
-          </div>
-        </template>
-        <el-skeleton :loading="loading" animated>
-          <el-row>
-            <el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-8px">
-              <div class="flex items-center">
-                <Icon :icon="item.icon" class="mr-8px" />
-                <el-link type="default" :underline="false" @click="setWatermark(item.name)">
-                  {{ item.name }}
-                </el-link>
+    <el-skeleton :loading="loading" animated>
+      <el-row :gutter="20">
+        <el-col>
+          <div class="introduce">
+            <div class="introduce-num" shadow="hover">
+              <div class="introduce-img">
+                <img src="/@/assets/imgs/zs2.png" alt="" />
               </div>
-            </el-col>
-          </el-row>
-        </el-skeleton>
-      </el-card>
-      <el-card shadow="never" class="mt-8px">
-        <template #header>
-          <div class="h-3 flex justify-between">
-            <span>{{ t('workplace.notice') }}</span>
-            <el-link type="primary" :underline="false">{{ t('action.more') }}</el-link>
+              <div class="introduce-text">
+                <div>标本总数</div>
+                <div class="multiple-text-shadow">{{ totalSamples }}</div>
+              </div>
+            </div>
+            <div class="introduce-num">
+              <div class="introduce-img">
+                <img src="/@/assets/imgs/kw5.png" alt="" />
+              </div>
+              <div class="introduce-text">
+                <div>矿物</div>
+                <div>{{ mineralCount }}</div>
+              </div>
+            </div>
+            <div class="introduce-num">
+              <div class="introduce-img">
+                <img src="/@/assets/imgs/kw4.png" alt="" />
+              </div>
+              <div class="introduce-text">
+                <div>岩石矿石</div>
+                <div>{{ fossilCount }}</div>
+              </div>
+            </div>
+            <div class="introduce-num">
+              <div class="introduce-img">
+                <img style="width: 90px;height: 90px;" src="/@/assets/imgs/hs4.png" alt="" />
+              </div>
+              <div class="introduce-text">
+                <div>化石</div>
+                <div>{{ meteoriteCount }}</div>
+              </div>
+            </div>
+            <div class="introduce-num">
+              <div class="introduce-img">
+                <img style="width: 90px;height: 100px;" src="/@/assets/imgs/ys5.png" alt="" />
+              </div>
+              <div class="introduce-text">
+                <div>陨石</div>
+                <div>{{ rockOreCount }}</div>
+              </div>
+            </div>
+
           </div>
-        </template>
-        <el-skeleton :loading="loading" animated>
-          <div v-for="(item, index) in notice" :key="`dynamics-${index}`">
-            <div class="flex items-center">
-              <el-avatar :src="avatar" :size="35" class="mr-16px">
-                <img src="@/assets/imgs/avatar.gif" alt="" />
-              </el-avatar>
-              <div>
-                <div class="text-14px">
-                  <Highlight :keys="item.keys.map((v) => t(v))">
-                    {{ item.type }} : {{ item.title }}
-                  </Highlight>
+
+        </el-col>
+      </el-row>
+    </el-skeleton>
+    <!-- 本年标本出入回库情况new -->
+    <el-skeleton :loading="loading" animated>
+      <el-row>
+        <!-- 本年标本出库 -->
+        <el-col :xl="5" :lg="5" :md="5" :sm="24" :xs="24">
+          <el-card shadow="hover" class="mr-5px mt-5px">
+            <div class="title"><img style="width: 25px;height: 25px;" src="/@/assets/imgs/ck5.png" alt="" /><span>本月出库数</span></div>
+            <div class="specimen-month">
+              <div class="month">
+                <div class="month-bottom">
+                  <div>{{outNumber}}</div>
+                  <div>月环比&nbsp;<span>{{outRatio.toFixed(0)}}%</span></div>
+                </div>
+              </div>
+              <div class="month1">
+                <Echart :options="outRatioOptionsData" :height="280" :width="'140%'" />
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+        <!-- 本年标本入库 -->
+        <el-col :xl="5" :lg="5" :md="5" :sm="24" :xs="24">
+
+          <el-card shadow="hover" class="mr-5px mt-5px">
+            <div class="title"><img style="width: 30px;height: 30px;" src="/@/assets/imgs/rk3.png" alt="" /><span>本月入库数</span></div>
+            <div class="specimen-month">
+              <div class="month">
+                <div class="month-bottom">
+                  <div>{{enterNumber}}</div>
+                  <div>月环比&nbsp;<span>{{enterRatio.toFixed(0)}}%</span></div>
                 </div>
-                <div class="mt-16px text-12px text-gray-400">
-                  {{ formatTime(item.date, 'yyyy-MM-dd') }}
+              </div>
+              <div class="month1">
+                <Echart :options="enterRatioOptionsData" :height="280" :width="'140%'" />
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+
+        <!-- 本年标本回库 -->
+        <el-col :xl="5" :lg="5" :md="5" :sm="24" :xs="24">
+
+          <el-card shadow="hover" class="mr-5px mt-5px">
+            <div class="title"><img style="width: 25px;height: 25px;" src="/@/assets/imgs/hk2.png" alt="" /><span>本月回库数</span></div>
+            <div class="specimen-month">
+              <div class="month">
+                <div class="month-bottom">
+                  <div>{{returnNumber}}</div>
+                  <div>月环比&nbsp;<span>{{returnRatio.toFixed(0)}}%</span></div>
                 </div>
               </div>
+              <div class="month1">
+                <Echart :options="returnRatioOptionsData" :height="280" :width="'140%'" />
+              </div>
             </div>
-            <el-divider />
-          </div>
-        </el-skeleton>
-      </el-card>
-    </el-col>
-  </el-row>
+          </el-card>
+        </el-col>
+        <el-col :xl="9" :lg="9" :md="9" :sm="24" :xs="24">
+          <el-card shadow="hover">
+            <template #header>
+              <div class="title"><img src="/@/assets/imgs/tj3.png" alt="" /><span>本年标本数量统计</span></div>
+            </template>
+            <el-skeleton :loading="loading" animated>
+              <Echart :options="mouthOptionsData" :height="250" />
+            </el-skeleton>
+          </el-card>
+          <!-- 历年标本来源增减统计 -->
+
+        </el-col>
+      </el-row>
+    </el-skeleton>
+
+    <!-- 本年标本出入回库情况2 -->
+    <el-skeleton :loading="loading" animated>
+      <el-row>
+        <el-col :xl="9" :lg="9" :md="9" :sm="24" :xs="24">
+          <el-card shadow="hover">
+            <template #header>
+              <div class="title"><img src="/@/assets/imgs/tj3.png" alt="" /><span>历年标本数量统计</span></div>
+            </template>
+            <el-skeleton :loading="loading" animated>
+              <Echart :options="yearOptionsData" :height="310" />
+            </el-skeleton>
+          </el-card>
+          <!-- 历年标本来源增减统计 -->
+
+        </el-col>
+        <el-col :xl="9" :lg="9" :md="9" :sm="24" :xs="24">
+          <el-card shadow="hover">
+            <template #header>
+              <div class="title"><img src="/@/assets/imgs/tj3.png" alt="" /><span>历年标本来源增减统计</span></div>
+            </template>
+            <el-skeleton :loading="loading" animated>
+              <Echart :options="originOptions111Data" :height="310" />
+            </el-skeleton>
+          </el-card>
+          <!-- 历年标本来源增减统计 -->
+
+        </el-col>
+        <!-- 本年标本回库 -->
+        <el-col :xl="6" :lg="6" :md="6" :sm="24" :xs="24">
+
+          <el-card shadow="hover" class="mr-5px mt-5px">
+            <div class="title"><img src="/@/assets/imgs/tj3.png" alt="" /><span>按标本类型统计</span></div>
+            <Echart :options="pieOptionsData" :height="335" />
+
+          </el-card>
+        </el-col>
+
+      </el-row>
+    </el-skeleton>
+  </div>
+
 </template>
 <script lang="ts" setup>
 import { set } from 'lodash-es'
 import { EChartsOption } from 'echarts'
-import { formatTime } from '@/utils'
+// import { formatTime } from '@/utils'
+import * as echarts from 'echarts';
+import Echarts from '/@/components/Echarts/index.vue';
 
 import { useUserStore } from '@/store/modules/user'
 import { useWatermark } from '@/hooks/web/useWatermark'
-import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
-import { pieOptions, barOptions } from './echarts-data'
-
+import type { WorkplaceTotal, Project, Shortcut } from './types'
+import { pieOptions, barOptions, barOptions1, outOptions, enterOptions, backOptions, yearOptions, originOptions, enterRatioOptions, originOptions111, mouthOptions,outRatioOptions,returnRatioOptions } from './echarts-data'
+// import MyChart from '/@/components/Echarts/echarts.vue';
 defineOptions({ name: 'Home' })
 
+
+//获取标本类型数
+import * as WorkbenchApi from '@/api/workbench'
+// const workbenchList = ref([] as WorkbenchApi.WorkbenchVO[]) //标本列表
+const totalSamples = ref(0);
+const mineralCount = ref(0);
+const fossilCount = ref(0);
+const meteoriteCount = ref(0);
+const rockOreCount = ref(0);
+const enterNumber = ref(0);
+const outNumber = ref(0);
+const returnNumber = ref(0);
+const enterNumber1 = ref(0);
+const outNumber1 = ref(0);
+const returnNumber1 = ref(0);
+const enterRatio = ref(0);
+const outRatio = ref(0);
+const returnRatio = ref(0);
+
+//初始化
+onMounted(async () => {
+  const { samples } = await WorkbenchApi.getSpecimenTypeList();
+  totalSamples.value = samples['标本总数'];
+  mineralCount.value = samples['矿物'];
+  fossilCount.value = samples['化石'];
+  meteoriteCount.value = samples['陨石'];
+  rockOreCount.value = samples['岩石矿石'];
+
+
+  const month = `${new Date().getMonth() + 1}月`
+  console.log('month', month);
+  const enterSpecimen = ref();
+  const outSpecimen = ref();
+  const returnSpecimen = ref();
+  const year = new Date().getFullYear()
+  // 使用 await 获取数据
+  //本月出、入、回库数
+  enterSpecimen.value = await WorkbenchApi.getEnterSpecimenList(year);
+  outSpecimen.value = await WorkbenchApi.getOutSpecimenList(year);
+  returnSpecimen.value = await WorkbenchApi.getReturnSpecimenList(year);
+  const enter = enterSpecimen.value.data
+  const out = outSpecimen.value.data
+  const return1 = returnSpecimen.value.data
+  enterNumber.value = enter[enter.length - 1].number;
+  outNumber.value = out[out.length - 1].number;
+  returnNumber.value = return1[return1.length - 1].number;
+  enterNumber1.value = enter[enter.length - 2].number;
+  outNumber1.value = out[out.length - 2].number;
+  returnNumber1.value = return1[return1.length - 2].number;
+  console.log(enterNumber.value,outNumber,returnNumber);
+  enterRatio.value = (enterNumber.value-enterNumber1.value)/enterNumber.value*100;
+  outRatio.value = (outNumber.value-outNumber1.value)/outNumber.value*100;
+  returnRatio.value = (returnNumber.value-returnNumber1.value)/returnNumber.value*100;
+})
+
+
+
+
+
 const { t } = useI18n()
 const userStore = useUserStore()
 const { setWatermark } = useWatermark()
@@ -256,37 +358,7 @@ const getProject = async () => {
   projects = Object.assign(projects, data)
 }
 
-// 获取通知公告
-let notice = reactive<Notice[]>([])
-const getNotice = async () => {
-  const data = [
-    {
-      title: '系统支持 JDK 8/17/21,Vue 2/3',
-      type: '通知',
-      keys: ['通知', '8', '17', '21', '2', '3'],
-      date: new Date()
-    },
-    {
-      title: '后端提供 Spring Boot 2.7/3.2 + Cloud 双架构',
-      type: '公告',
-      keys: ['公告', 'Boot', 'Cloud'],
-      date: new Date()
-    },
-    {
-      title: '全部开源,个人与企业可 100% 直接使用,无需授权',
-      type: '通知',
-      keys: ['通知', '无需授权'],
-      date: new Date()
-    },
-    {
-      title: '国内使用最广泛的快速开发平台,超 300+ 人贡献',
-      type: '公告',
-      keys: ['公告', '最广泛'],
-      date: new Date()
-    }
-  ]
-  notice = Object.assign(notice, data)
-}
+
 
 // 获取快捷入口
 let shortcut = reactive<Shortcut[]>([])
@@ -328,30 +400,57 @@ const getShortcut = async () => {
 }
 
 // 用户来源
-const getUserAccessSource = async () => {
+// const getUserAccessSource = async () => {
+//   const data = [
+//     { value: 335, name: 'analysis.directAccess' },
+//     { value: 310, name: 'analysis.mailMarketing' },
+//     { value: 234, name: 'analysis.allianceAdvertising' },
+//     { value: 135, name: 'analysis.videoAdvertising' },
+//     { value: 1548, name: 'analysis.searchEngines' }
+//   ]
+//   set(
+//     pieOptionsData,
+//     'legend.data',
+//     data.map((v) => t(v.name))
+//   )
+//   pieOptionsData!.series![0].data = data.map((v) => {
+//     return {
+//       name: t(v.name),
+//       value: v.value
+//     }
+//   })
+// }
+const barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
+
+// 周活跃量
+const getWeeklyUserActivity = async () => {
   const data = [
-    { value: 335, name: 'analysis.directAccess' },
-    { value: 310, name: 'analysis.mailMarketing' },
-    { value: 234, name: 'analysis.allianceAdvertising' },
-    { value: 135, name: 'analysis.videoAdvertising' },
-    { value: 1548, name: 'analysis.searchEngines' }
+    { value: 13253, name: 'analysis.monday' },
+    { value: 34235, name: 'analysis.tuesday' },
+    { value: 26321, name: 'analysis.wednesday' },
+    { value: 12340, name: 'analysis.thursday' },
+    { value: 24643, name: 'analysis.friday' },
+    { value: 1322, name: 'analysis.saturday' },
+    { value: 1324, name: 'analysis.sunday' }
   ]
   set(
-    pieOptionsData,
-    'legend.data',
+    barOptionsData,
+    'xAxis.data',
     data.map((v) => t(v.name))
   )
-  pieOptionsData!.series![0].data = data.map((v) => {
-    return {
-      name: t(v.name),
-      value: v.value
+  set(barOptionsData, 'series', [
+    {
+      name: t('analysis.activeQuantity'),
+      data: data.map((v) => v.value),
+      type: 'bar'
     }
-  })
+  ])
 }
-const barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
+
+const barOptionsData1 = reactive<EChartsOption>(barOptions1) as EChartsOption
 
 // 周活跃量
-const getWeeklyUserActivity = async () => {
+const getWeeklyUserActivity1 = async () => {
   const data = [
     { value: 13253, name: 'analysis.monday' },
     { value: 34235, name: 'analysis.tuesday' },
@@ -362,30 +461,253 @@ const getWeeklyUserActivity = async () => {
     { value: 1324, name: 'analysis.sunday' }
   ]
   set(
-    barOptionsData,
+    barOptionsData1,
     'xAxis.data',
     data.map((v) => t(v.name))
   )
-  set(barOptionsData, 'series', [
+  set(barOptionsData1, 'series', [
     {
       name: t('analysis.activeQuantity'),
       data: data.map((v) => v.value),
-      type: 'bar'
+      type: 'line'
     }
   ])
 }
 
+
 const getAllApi = async () => {
   await Promise.all([
     getCount(),
     getProject(),
-    getNotice(),
+    // getNotice(),
     getShortcut(),
-    getUserAccessSource(),
-    getWeeklyUserActivity()
+    // getUserAccessSource(),
+    getWeeklyUserActivity(),
+    getWeeklyUserActivity1(),
   ])
   loading.value = false
 }
 
+
+
+//出库
+const outOptionsData = reactive<EChartsOption>(outOptions) as EChartsOption
+//入库
+const enterOptionsData = reactive<EChartsOption>(enterOptions) as EChartsOption
+//回库backOptions
+const backOptionsData = reactive<EChartsOption>(backOptions) as EChartsOption
+//历年标本数量统计yearOptions
+const yearOptionsData = reactive<EChartsOption>(yearOptions) as EChartsOption
+//历年标本来源统计originOptions
+const originOptionsData = reactive<EChartsOption>(originOptions) as EChartsOption
+//水位waterOptions
+const enterRatioOptionsData = reactive<EChartsOption>(enterRatioOptions) as EChartsOption
+//水位waterOptions
+const outRatioOptionsData = reactive<EChartsOption>(outRatioOptions) as EChartsOption
+//水位waterOptions
+const returnRatioOptionsData = reactive<EChartsOption>(returnRatioOptions) as EChartsOption
+//历年来源originOptions111
+const originOptions111Data = reactive<EChartsOption>(originOptions111) as EChartsOption
+//历年来源originOptions111
+const mouthOptionsData = reactive<EChartsOption>(mouthOptions) as EChartsOption
 getAllApi()
+
+
+
 </script>
+<style scoped>
+.bg {
+  background-image: url("/@/asset/imgs/bg.jpg");
+  background-repeat: no-repeat;
+  background-attachment: fixed;
+  background-size: cover;
+  /* background-color: #f8f3e0; */
+}
+
+.el-card {
+  --el-card-border-color: #fff !important;
+  margin: 0 10px 20px 10px;
+  --el-card-border-radius: .625rem;
+}
+
+.title {
+  font-size: 22px;
+  font-weight: bold;
+  margin-bottom: 10px;
+  display: flex;
+  align-items: center;
+}
+
+.title img {
+  width: 30px;
+  height: 30px;
+}
+
+.title span {
+  margin-left: 10px;
+}
+
+.specimen-month {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin: 0 5%;
+}
+
+.month {
+  width: 40%;
+  /* padding: 0 10px; */
+}
+
+.month1 {
+  width: 60%;
+  padding: 0 10px;
+}
+
+.month-bottom {
+  /* display: flex;
+  justify-content: space-between; */
+  /* margin-top: 24%; */
+}
+
+.month-bottom div:nth-child(1) {
+
+  font-size: 40px;
+  font-weight: bold;
+}
+
+.month-bottom span {
+  font-size: 20px;
+  font-weight: bold;
+  color: #f7cb6b;
+  /* color: #e7a13f; */
+}
+
+.introduce {
+  display: flex;
+  margin: 0 10px 20px 10px;
+  justify-content: space-between;
+
+}
+
+.introduce-num {
+  width: 19%;
+  padding: 20px 0;
+  display: flex;
+  align-items: center;
+  /* background-color: antiquewhite; */
+  /* background-color: #fdf5e1; */
+  background-color: #f9f2ec;
+
+  justify-content: space-around;
+  border-radius: .625rem;
+  border: 3px solid #fff;
+  /* background-image:url("/@/assets/imgs/introduce.jpg"); */
+}
+
+/* .introduce-num:nth-child(1) {
+  background-color: rgba(152,83,74,0.3);
+}
+.introduce-num:nth-child(2) {
+  background-color: rgba(199,89,51,0.3);
+}
+.introduce-num:nth-child(3) {
+  background-color: rgba(217,133,102,0.3);
+}
+.introduce-num:nth-child(4) {
+  background-color: rgba(238,204,147,0.3);
+}
+.introduce-num:nth-child(5) {
+  background-color: rgba(159,180,207,0.3);
+} */
+.introduce-img img {
+  width: 80px;
+  height: 80px;
+  /* margin-top: 10%; */
+}
+
+.introduce-text {
+  /* text-align: center; */
+}
+
+.introduce-text div:nth-child(1) {
+  font-size: 1.4rem;
+}
+
+.introduce-text div:nth-child(2) {
+  font-size: 2.8rem;
+  color: rgba(143, 53, 36, 0.7);
+  /* color: #deb887; */
+  font-weight: bold;
+}
+
+@media (min-width: 100px) {
+  .introduce {
+    flex-direction: column;
+  }
+  .introduce-num{
+    width: 100%;
+    margin-bottom: 20px;
+  }
+  
+}
+
+@media (min-width: 400px) {
+  .introduce {
+    flex-direction: column;
+  }
+  .introduce-num{
+    width: 100%;
+    margin-bottom: 20px;
+  }
+  
+}
+
+
+@media (min-width: 800px) {
+  .introduce {
+    flex-direction: column;
+  }
+  .introduce-num{
+    width: 100%;
+  }
+  .introduce-num:nth-child(5){
+    width: 100%;
+    margin-bottom: 0px;
+  }
+}
+
+@media (min-width:1000px) {
+  .introduce {
+    flex-direction: row;
+  }
+  .introduce-num{
+    width: 19%;
+    margin-bottom: 0px;
+  }
+  .introduce-num:nth-child(5){
+    width: 19%;
+    margin-bottom: 0px;
+  }
+}
+
+
+@media (min-width:2560px) {
+  .introduce {
+    flex-direction: row;
+  }
+  .introduce-num{
+    width: 19%;
+    margin-bottom: 0px;
+  }
+  .introduce-num:nth-child(5){
+    width: 19%;
+    margin-bottom: 0px;
+  }
+}
+
+
+.multiple-text-shadow {
+  /* -webkit-text-stroke: 0.3px #fff; */
+}
+</style>

File diff suppressed because it is too large
+ 1034 - 118
src/views/Home/echarts-data.ts


Some files were not shown because too many files changed in this diff