Browse Source

Merge branch 'master' into use-pinia

# Conflicts:
#	package-lock.json
#	src/App.vue
#	src/layout/MenuV2/MenuV2.vue
#	src/layout/index.vue
#	src/router/index.ts
lighter 1 year ago
parent
commit
6c40247142

BIN
deps/element-plus.2.5.2.2.tar.gz → deps/element-plus.2.7.1.1.tar.gz


File diff suppressed because it is too large
+ 236 - 189
package-lock.json


+ 10 - 0
src/App.vue

@@ -21,9 +21,12 @@ import {formatDateToStr} from '@/utils/moment-utils'
 import {ElLink} from 'element-plus'
 import JsDialogComp from "@/components/js-dialog-comp/JsDialogComp.vue";
 import {CyMessageBox} from "@/components/cy/message-box";
+<<<<<<< HEAD
 import {useProgressBarStore} from "@/pinia/progress-bar-store";
 import {useUserStore} from "@/pinia/user-store";
 import {useSystemStore} from "@/pinia/system-store";
+=======
+>>>>>>> master
 
 const progressBarStore = useProgressBarStore()
 const userStore = useUserStore()
@@ -610,4 +613,11 @@ input[type=number]::-webkit-outer-spin-button {
   @include cy-card;
 }
 
+.layout_card_main {
+  width: 100%;
+  height: 100%;
+  @include cy-card;
+}
+
+
 </style>

+ 18 - 1
src/api/settings/menu-settings.ts

@@ -7,7 +7,7 @@ export interface IntergrationMenu {
     path: string;
     icon: string;
     name: string;
-    parentId?: number;
+    parentId?: number | null;
     metaTitle: string;
     metaHideTabs?: boolean;
     metaPassRule?: boolean;
@@ -20,6 +20,7 @@ export interface IntergrationMenu {
     pathParams?: string;
     children?: IntergrationMenu[];
     completePath?: string
+    cascaders?: number[] | null
 }
 
 export function getMenuSettings() {
@@ -35,3 +36,19 @@ export function getUserMenu() {
         url: "/menuSettings/getUserMenu",
     })
 }
+
+export function updateMenuSettings(menuList: IntergrationMenu) {
+    return requestV2({
+        method: "post",
+        url: '/menuSettings/updateMenuSettings',
+        data: menuList
+    });
+}
+
+export function deleteMenuSettings(id: number) {
+    return requestV2({
+        method: "get",
+        url: "/menuSettings/deleteMenuSettings",
+        params: {id}
+    })
+}

+ 1 - 1
src/components/cy/combo-grid/src/CyComboGrid.vue

@@ -183,7 +183,7 @@ defineExpose({
                      @click.stop="toggleMenu"
                      @keydown.down.stop.prevent="navigateOptions('next')"
                      @keydown.up.stop.prevent="navigateOptions('prev')"
-                     @keydown.esc.stop.prevent="handleEsc"
+                     @keydown.esc="handleEsc"
                      @keydown.enter.stop.prevent="selectOption"
                      @keydown.delete.stop="deletePrevTag"
               />

+ 11 - 1
src/components/cy/combo-grid/src/index.ts

@@ -567,10 +567,20 @@ export default function UesComboGrid(props: CyComboGridOptionsV2, emits: any) {
         tableRef.value?.scrollToRow(selectData)
     }
 
-    function handleEsc() {
+    function handleEsc(event: KeyboardEvent) {
+
+        function handleStop() {
+            event.stopPropagation();
+            event.preventDefault();
+        }
+
         if (states.inputValue.length > 0) {
             states.inputValue = ''
+            handleStop()
         } else {
+            if (expanded.value) {
+                handleStop()
+            }
             expanded.value = false
         }
     }

+ 2 - 6
src/components/cy/dialog/src/useCyDialog.ts

@@ -119,11 +119,7 @@ export function UseCyDialog(props: IsCyDialog, emit: any) {
     useDraggable(boxRef, headerRef, draggable)
 
     function closed(closingMethod: ClosingMethod, val: any) {
-        const id = ctx?.attrs.id as string
-        if (id) {
-            compList.value[id].onClosed(closingMethod, val)
-        }
-        emit('closed')
+        emit('closed', closingMethod, val)
     }
 
     async function handleConfirm() {
@@ -147,7 +143,6 @@ export function UseCyDialog(props: IsCyDialog, emit: any) {
         next(null)
     }
 
-
     async function handleCancel() {
         state.cancelLoading = true;
 
@@ -180,6 +175,7 @@ export function UseCyDialog(props: IsCyDialog, emit: any) {
         handleCancel()
     }
 
+
     return {
         ns,
         headerRef,

+ 27 - 31
src/layout/HeaderV2/RouteNavigation.vue

@@ -1,12 +1,14 @@
 <template>
-  <right-click-menu :mouse-position="mousePosition"
-                    :config="opt"/>
-  <div class="route_navigation_main" ref="tagRef">
+  <right-click-menu
+      :mouse-position="mousePosition"
+      :config="opt"/>
+  <div class="route_navigation_main"
+       ref="tagRef">
     <div v-for="(item,index) in menuList"
          :class="index === currentIndex ? 'active_text item' :  'item'"
          :key="index"
          @contextmenu.prevent="contextmenuItem(item,index,$event)"
-         @click="router.push(item.path)">
+         @click="handleRouterPush(item)">
       {{ item?.title }}
       <el-icon @click.stop.prevent="closeRouter(index,item.path)">
         <Close/>
@@ -19,10 +21,9 @@
 <script setup>
 import {ref} from "vue";
 import tabsHook from "@/layout/HeaderV2/tabs-hook";
-import {useRouter} from "vue-router";
 import RightClickMenu from "@/components/menu-item/RightClickMenu";
+import router from '@/router'
 
-const router = useRouter()
 const tagRef = ref(null)
 
 const opt = [
@@ -72,7 +73,6 @@ const opt = [
   }
 ]
 
-
 const mousePosition = ref()
 const contextmenuItem = async (item, index, event) => {
   mousePosition.value = {
@@ -82,10 +82,8 @@ const contextmenuItem = async (item, index, event) => {
   }
 }
 
-let menuList = ref([])
-menuList.value = tabsHook.getItem()
-const menuKey = ref([])
-menuKey.value = tabsHook.getItemKey();
+const menuList = ref(tabsHook.getItem())
+const menuKey = ref(tabsHook.getItemKey())
 
 const currentPath = ref('')
 const currentIndex = ref(-1)
@@ -93,7 +91,6 @@ const currentIndex = ref(-1)
 const closeRouter = (index, path) => {
   menuList.value.splice(index, 1)
   menuKey.value.splice(index, 1)
-
   if (menuList.value.length === 0) {
     let data = {
       path: '/dashboard',
@@ -127,7 +124,7 @@ const backToTheHomepage = () => {
 // 设置滚动条位置
 const handleScrolling = async (str) => {
   currentIndex.value = menuList.value.findIndex((item) => {
-    return item.path === str.path
+    return item.name === str.name
   })
   if (currentIndex.value === -1) return
   let itemData = tagRef.value.querySelectorAll('.item')
@@ -145,6 +142,12 @@ const handleScrolling = async (str) => {
 
 const selectedStyle = ref({})
 
+function handleRouterPush(item) {
+  router.push({
+    name: item.name,
+    query: item.query
+  })
+}
 
 watch(() => menuList.value, () => {
   tabsHook.setItem(menuList.value)
@@ -158,38 +161,32 @@ const createLabels = (str, data) => {
   menuKey.value.push(str)
 }
 
-const routingChanges = (flag = true) => {
-  let currentRoute = router.currentRoute.value
+const routingChanges = (createTabs = true) => {
+  const currentRoute = router.currentRoute.value
   currentPath.value = currentRoute.path
-  let data = {
+
+  const data = {
     path: currentRoute.path,
-    title: currentRoute.meta.title
+    title: currentRoute.meta.title,
+    name: currentRoute.name,
+    query: currentRoute.query
   }
-  let str = JSON.stringify(data)
 
-  if (currentRoute.meta.parentName && currentRoute.meta.activeMenu) {
-    data.path = currentRoute.meta.activeMenu
-    data.title = currentRoute.meta.parentName
-    str = JSON.stringify(data)
-    if (flag) {
-      createLabels(str, data)
-    }
-  }
+  const routerInfoToStr = JSON.stringify(data)
+
   nextTick(() => {
     handleScrolling(data)
   })
   // 不需要创建
   if (currentRoute.meta.hideTabs) return;
-  if (flag) {
-    createLabels(str, data)
+  if (createTabs) {
+    createLabels(routerInfoToStr, data)
   }
 }
 
 watch(() => router.currentRoute.value, () => {
   routingChanges()
 }, {immediate: true})
-
-
 </script>
 
 <style scoped lang="scss">
@@ -234,7 +231,6 @@ $item-height: 40px;
   /* 滚动条的圆角宽度 */
 }
 
-
 .route_navigation_main:hover::-webkit-scrollbar-thumb { /*滚动条里面小方块*/
   border-radius: 10px;
   -webkit-box-shadow: inset 0 0 5px rgba(28, 28, 28, 0.2);

+ 4 - 4
src/layout/HeaderV2/tabs-hook.js → src/layout/HeaderV2/tabs-hook.ts

@@ -1,13 +1,13 @@
-import {clone} from "@/utils/clone";
+import {clone} from "xe-utils";
 
 const tabsHook = {
-    setItem: function (arr) {
+    setItem: function (arr: any) {
         localStorage.setItem('tabs', JSON.stringify(arr))
     },
-    getItem: function () {
+    getItem: function (): any[] {
         return clone(JSON.parse(localStorage.getItem('tabs') || '[]'))
     },
-    getItemKey: function () {
+    getItemKey: function (): any[] {
         let data = this.getItem()
         let temp = []
         for (let i = 0; i < data.length; i++) {

+ 2 - 2
src/layout/MenuV2/MenuItemV2.vue

@@ -1,7 +1,7 @@
 <template>
   <el-sub-menu v-if="data?.children && data?.children.length > 0"
                :index="data?.path"
-               :id="data?.completePath"
+               :id="data.name"
                :router="data?.completePath">
     <template #title>
       <i :class="data?.icon"
@@ -13,7 +13,7 @@
                   v-for="item in data?.children"/>
   </el-sub-menu>
   <el-menu-item v-else :index="data?.completePath"
-                :id="data?.completePath"
+                :id="data.name"
                 :router="data?.completePath">
     <i :class="data?.icon" v-if="data?.icon"></i>
     <span v-html="highlightTitle(data?.metaTitle)"></span>

+ 8 - 8
src/layout/MenuV2/MenuV2.vue

@@ -29,6 +29,7 @@
 </template>
 
 <script setup lang="ts">
+import store from '@/store'
 import MenuItemV2 from "./MenuItemV2.vue";
 import {computed, nextTick, onMounted, reactive, Ref, ref, watch} from "vue";
 import {useRoute} from "vue-router";
@@ -39,9 +40,7 @@ import sleep from "@/utils/sleep";
 import {treeSearch} from "@/utils/array-utils";
 import {useCompRef} from "@/utils/useCompRef";
 import {ElMenu} from "element-plus";
-import {useSystemStore} from "@/pinia/system-store";
-
-const systemStore = useSystemStore()
+import {getUserMenu} from "@/api/settings/menu-settings";
 
 const menuText: Ref<string> = ref('')
 const menuRef = useCompRef(ElMenu)
@@ -78,7 +77,7 @@ const expandNodes = (treeData) => {
   const traverse = tempData => {
     XEUtils.arrayEach(tempData, (item) => {
       if (item.children !== null && item.children.length > 0) {
-        menuLaunch(item.completeRoute)
+        menuLaunch(item.completePath)
         traverse(item.children);
       }
     })
@@ -101,13 +100,13 @@ const menuLaunch = async path => {
   }
 }
 
-const expandOneMenu = computed<boolean>(() => systemStore.expandOneMenu)
+const expandOneMenu = computed<boolean>(() => store.state['app'].expandOneMenu)
 const route = useRoute()
-const isCollapse = computed<boolean>(() => systemStore.isCollapse)
+const isCollapse = computed<boolean>(() => store.state['app'].isCollapse)
 
 const expandMenu = () => {
   if (isCollapse.value) {
-    systemStore.setCollapse(false)
+    store.commit('app/isCollapseChange', false)
     setTimeout(() => {
       searchRef.value!.focus()
     }, 100)
@@ -131,7 +130,7 @@ const activeMenu = computed<string>(() => {
 })
 
 const smoothScrolling = () => {
-  const routerPath = document.getElementById(router.currentRoute.value.fullPath)
+  const routerPath = document.getElementById(<string>router.currentRoute.value.name)
   if (!routerPath) return
   routerPath.scrollIntoView({
     block: 'start',
@@ -152,6 +151,7 @@ watch(() => router.currentRoute.value, async () => {
 
 onMounted(async () => {
   await nextTick()
+  menuData.value = await getUserMenu()
   barHeight.height = (floatingRef.value.clientHeight - logoRef.value.clientHeight - 36) + 'px'
   await sleep(500)
   smoothScrolling()

+ 5 - 0
src/layout/index.vue

@@ -1,6 +1,11 @@
 <template>
+<<<<<<< HEAD
   <el-container style="height: 100vh">
     <div class="mask" v-show="!isCollapse" @click="hideMenu"></div>
+=======
+  <el-container style="height: 100%;width: 100%">
+    <div class="mask" v-show="!isCollapse && !contentFullScreen" @click="hideMenu"></div>
+>>>>>>> master
     <el-aside :width="isCollapse ? '50px' : '260px'"
               class="layout_aside"
               :class="isCollapse ? 'hide-aside' : 'show-side'"

+ 7 - 22
src/router/createNode.js

@@ -10,28 +10,13 @@ export function createNameComponent(component) {
                 const name = (comm.default.name || 'vueAdminBox') + '$' + Date.now()
                 const tempComm = defineComponent({
                     name,
-                    setup() {
-                        const isReload = ref(false)
-                        return {
-                            isReload,
-                        }
-                    },
-                    render: function () {
-                        if (this.isReload) {
-                            return h('div', {
-                                style: {
-                                    height: '100%',
-                                    width: '100%'
-                                }
-                            }, [h(reload)])
-                        } else {
-                            return h('div', {
-                                style: {
-                                    height: '100%',
-                                    width: '100%'
-                                }
-                            }, [createVNode(comm.default)])
-                        }
+                    render() {
+                        return h('div', {
+                            style: {
+                                height: '100%',
+                                width: '100%'
+                            }
+                        }, [createVNode(comm.default)])
                     },
                 })
                 res(tempComm)

+ 22 - 42
src/router/index.ts

@@ -5,7 +5,6 @@
  * @params alwaysShow: 只有一个子路由时是否总是展示菜单,默认false
  */
 import {createRouter, createWebHistory, RouteRecordRaw} from 'vue-router'
-import store from '@/store'
 import NProgress from '@/utils/system/nprogress'
 import {changeTitle} from '@/utils/system/title'
 
@@ -14,26 +13,20 @@ import {changeTitle} from '@/utils/system/title'
 import Dashboard from './modules/dashboard'
 import {getUserMenu, IntergrationMenu} from "@/api/settings/menu-settings";
 import {createVNode, defineComponent, h} from "vue";
-import Layout from "@/layout/index.vue";
 import {userInfoStore} from "@/utils/store-public";
-import {stringIsBlank} from "@/utils/blank-utils";
-import XEUtils from "xe-utils";
+import {stringNotBlank} from "@/utils/blank-utils";
 
 
-function dashboardRouter(data: Array<RouteRecordRaw>) {
-    data.forEach((item) => {
-        XEUtils.set(item, "meta.needToken", false)
-        if (item.children && item.children.length > 0) {
-            dashboardRouter(item.children)
-        }
-    })
-}
-
-dashboardRouter(Dashboard)
-
 const router = createRouter({
     history: createWebHistory(),
-    routes: [...Dashboard],
+    routes: [...Dashboard, {
+        path: '/:path(.*)*',
+        name: 'notFound',
+        component: import('@/views/system/404.vue'),
+        meta: {
+            hideTabs: true
+        }
+    }],
 })
 
 const vueFile = import.meta.glob('../views/**/*.{vue,tsx}');
@@ -49,21 +42,11 @@ async function beforeAddRoutes() {
     routers.forEach(item => {
         router.addRoute(item)
     })
-
-    // 404 要添加到最后面才可以,不然一开始就会是 404
-    router.addRoute({
-        path: '/:path(.*)*',
-        name: 'notFound',
-        component: Layout,
-        redirect: '/404',
-        hideMenu: true,
-    })
 }
 
 function createNameComponent(item) {
     return () => {
         return new Promise(resolve => {
-
             vueFile[`${item.component}`]().then(res => {
                 const tmp = defineComponent({
                     name: item.name,
@@ -132,36 +115,33 @@ function addRoutes(menus: IntergrationMenu[]) {
 router.beforeEach(async (to, _from, next) => {
         NProgress.start();
 
+        const hasToken = stringNotBlank(userInfoStore.value.token)
+
         function tmpNext(...arg) {
             to.meta.title && changeTitle(to.meta.title)
-            if (to.meta.needToken && stringIsBlank(userInfoStore.value.token)) {
+            if (!hasToken) {
                 next('/login')
                 return
             }
             next(...arg);
         }
 
-        if (XEUtils.has(to, 'meta.needToken') && !to.meta.needToken) {
-            tmpNext()
-            return
-        }
-
-        if (!asyncFinished) {
-            await beforeAddRoutes()
-            tmpNext({path: to.path, query: to.query, replace: true});
-            asyncFinished = true
+        if (to.name === 'notFound' && hasToken) {
+            if (!asyncFinished) {
+                await beforeAddRoutes()
+                tmpNext({path: to.path, query: to.query, replace: true});
+                asyncFinished = true
+            } else {
+                tmpNext();
+            }
         } else {
-            tmpNext();
+            to.meta.title && changeTitle(to.meta.title)
+            next()
         }
     }
 )
 
 router.afterEach((to, _from) => {
-    const keepAliveComponentsName = store.getters['keepAlive/keepAliveComponentsName'] || []
-    const name = to.matched[to.matched.length - 1].components.default.name
-    if (to.meta && to.meta.cache && name && !keepAliveComponentsName.includes(name)) {
-        store.commit('keepAlive/addKeepAliveComponentsName', name)
-    }
     NProgress.done()
 })
 

+ 11 - 24
src/router/modules/dashboard.ts

@@ -18,57 +18,67 @@ declare module 'vue-router' {
 const route: Array<RouteRecordRaw> = [
     {
         path: '/login',
+        name: 'login',
         component: createNameComponent(() => import('@/views/system/login.vue')),
         hideMenu: true,
         meta: {title: '登录', hideTabs: true},
     }, {
         path: '/jumpRedirect',
+        name: 'jumpRedirect',
         component: createNameComponent(() => import('@/views/system/JumpRedirect.vue')),
         hideMenu: true,
         meta: {title: '跳转重定向', hideTabs: true},
     },
     {
         path: '/mzEmr/:patientInfo?',
+        name: 'mzEmr',
         component: createNameComponent(() => import('@/views/mz-emr/MzEmr.vue')),
         hideMenu: true,
         meta: {title: '门诊电子病历', hideTabs: true},
     },
     {
         path: '/shareholderCard/:patientInfo?',
+        name: 'shareholderCard',
         component: createNameComponent(() => import('@/views/mz-emr/ShareholderCard.vue')),
         hideMenu: true,
         meta: {title: '股东卡优惠', hideTabs: true},
     },
     {
         path: '/scrollSource',
+        name: 'scrollSource',
         component: createNameComponent(() => import('@/views/single-page/TodayClinicResource.vue')),
         hideMenu: true,
         meta: {title: '今日号源', hideTabs: true},
     },
     {
         path: '/lottery',
+        name: 'lottery',
         component: createNameComponent(() => import('@/views/single-page/Lottery.vue')),
         meta: {title: '抽奖'}
     },
     {
         path: '/myEmrEditor/:pat?/:refresh?',
+        name: 'myEmrEditor',
         component: createNameComponent(() => import('@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/Home.vue')),
         hideMenu: true,
         meta: {title: '电子病历', hideTabs: true},
     },
     {
         path: '/fluorescenceTest',
+        name: 'fluorescenceTest',
         component: createNameComponent(() => import('@/components/zhu-yuan-yi-sheng/emr/auxiliary-tools/fluorescence-test/FluorescenceTest.vue')),
         hideMenu: true,
         meta: {title: '荧光检验', hideTabs: true},
     },
     {
         path: '/siSettleDetailList/:patientId?/:times?',
+        name: 'siSettleDetailList',
         component: createNameComponent(() => import('@/views/medical-insurance/allpatient/SiSettleDetailList.vue')),
         meta: {title: '医保结算单'},
     },
     {
         path: '/view/patient360',
+        name: 'patient360',
         component: createNameComponent(() => import('@/views/view/patient360/src/Patient360.vue')),
         meta: {title: '患者360', hideTabs: true},
     },
@@ -80,6 +90,7 @@ const route: Array<RouteRecordRaw> = [
         children: [
             {
                 path: 'dashboard',
+                name: 'dashboard',
                 component: createNameComponent(() => import('@/views/dashboard/index.vue')),
                 meta: {title: '首页'},
             },
@@ -91,30 +102,6 @@ const route: Array<RouteRecordRaw> = [
         component: () => import('@/views/system/Blank.vue'),
         meta: {hideTabs: true},
     },
-    {
-        path: '/system',
-        component: Layout,
-        redirect: '/404',
-        hideMenu: true,
-        meta: {title: '系统目录'},
-        children: [
-            {
-                path: '/404',
-                component: createNameComponent(() => import('@/views/system/404.vue')),
-                meta: {title: '404', hideTabs: true},
-            },
-            {
-                path: '/401',
-                component: createNameComponent(() => import('@/views/system/401.vue')),
-                meta: {title: '401', hideTabs: true},
-            },
-            {
-                path: '/redirect/:path(.*)',
-                component: createNameComponent(() => import('@/views/system/redirect.vue')),
-                meta: {title: 'redirect', hideTabs: true},
-            },
-        ],
-    },
 ];
 
 export default route

+ 2 - 2
src/utils/array-utils.ts

@@ -1,14 +1,14 @@
 import XEUtils from "xe-utils";
 
 declare type TreeSearch<D> = {
-    children?: D[],
+    children?: TreeSearch<D>[],
     $visible?: boolean
 } & D
 
 export const treeSearch = <D = any>(data: D[], iterator: (item: TreeSearch<D>) => boolean): D[] => {
     data = XEUtils.clone(data, true);
 
-    function filterDataByVisible(tempData: D[]): D[] {
+    function filterDataByVisible(tempData: D[]): TreeSearch<D>[] {
         return XEUtils.filter(tempData, (item: TreeSearch<D>): boolean => {
             if (item.children) {
                 item.children = filterDataByVisible(item.children);

+ 11 - 2
src/utils/request.js

@@ -1,5 +1,5 @@
 import axios from 'axios'
-import {ElMessage, ElMessageBox} from 'element-plus'
+import {ElMessage, ElMessageBox, ElNotification} from 'element-plus'
 import router from '@/router'
 import {startLoading, endLoading} from './loading'
 import {CyMessageBox} from "@/components/cy/message-box";
@@ -82,7 +82,16 @@ service.interceptors.response.use(
             }).then(() => {
             }).catch(() => {
             })
-            response.data.data
+            return response.data.data
+        }
+        if (response.data.code === 203) {
+            ElNotification({
+                type: 'success',
+                message: response.data.message,
+                duration: 3000,
+                title: "成功"
+            })
+            return response.data.data
         }
 
 

+ 1 - 1
src/views/mz-emr/MzEmr.vue

@@ -158,7 +158,7 @@ const getId = () => {
 const appContext = () => {
   return {
     endpoints: {
-      app: "http://172.16.32.160:9205/thyy/api/dataEmr/comp",
+      app: "http://172.16.32.167:9205/thyy/api/dataEmr/comp",
       his: import.meta.env.VITE_BASE_URL,
     },
     input: {

+ 2 - 2
src/views/operate-monitoring/IncomeStatistics.vue

@@ -36,7 +36,7 @@
 import PageLayer from '@/layout/PageLayer.vue'
 import Dig from "@/utils/math";
 import { clone } from '@/utils/clone'
-import { reactive, ref } from '@vue/reactivity'
+import { reactive, ref } from 'vue'
 import { shortcuts } from '@/data/shortcuts'
 import { formatDate, getDateRangeFormatDate } from '@/utils/date'
 import { ElMessage } from 'element-plus'
@@ -274,4 +274,4 @@ const exportData = () => {
     Export(data, title, `收入量统计`)
 }
 
-</script>
+</script>

+ 2 - 2
src/views/operate-monitoring/OperateMonitoring.vue

@@ -37,7 +37,7 @@ import PageLayer from '@/layout/PageLayer.vue'
 import { getDate } from '@/utils/date'
 import { clone } from '@/utils/clone'
 import router from "@/router"
-import { ref } from '@vue/reactivity'
+import { ref } from 'vue'
 import XcTable from "@/components/xiao-chan/xc-table/XcTable"
 import { selectReportPortal, selectReportGroup } from '@/api/reports/high-report'
 import ReportFirstPage from '@/components/operate-monitoring/ReportFirstPage.vue'
@@ -105,4 +105,4 @@ const reportFirstInfo = (row) => {
     color: #fff;
     background: #1e90ff;
 }
-</style>
+</style>

+ 81 - 37
src/views/settings/menu-settings/MenuSettings.vue

@@ -1,62 +1,106 @@
 <script setup lang="ts">
 import {onMounted, ref} from "vue";
-import {getMenuSettings, IntergrationMenu} from "@/api/settings/menu-settings";
-import {ElButton, ElTable, ElButtonGroup} from "element-plus";
+import {deleteMenuSettings, getMenuSettings, IntergrationMenu} from "@/api/settings/menu-settings";
+import {ElButton, ElButtonGroup} from "element-plus";
 import Dialog from "@/views/settings/menu-settings/dialog.vue";
+import {CyMessageBox} from "@/components/cy/message-box";
 
 const menuTreeData = ref<IntergrationMenu[]>([])
-const dialog = ref()
+const dialogRef = ref()
 
 function modify(row) {
-  dialog.value.openDialog(row)
+  dialogRef.value.openDialog(row)
 }
 
-onMounted(async () => {
+async function loadingData() {
   menuTreeData.value = await getMenuSettings()
+}
+
+async function delById(id: number) {
+  await CyMessageBox.confirm({
+    message: "是否删除页面",
+    type: "delete",
+  })
+  await deleteMenuSettings(id);
+  loadingData();
+}
+
+function addRouterClick(row: IntergrationMenu | null = null) {
+  let cascaders = []
+  if (row !== null) {
+    if (row.parentId === null) {
+      cascaders = [row.id];
+    } else {
+      if (row.type === 1) {
+        cascaders = [...row.cascaders, row.id];
+      } else {
+        cascaders = [...row.cascaders];
+      }
+    }
+  }
+
+  dialogRef.value.openDialog({
+    children: [],
+    component: "",
+    icon: "",
+    id: null,
+    metaHideTabs: false,
+    metaPassRule: false,
+    metaTitle: "",
+    name: "",
+    cascaders,
+    parentId: 0,
+    path: "",
+    pathParams: "",
+    redirect: "",
+    sort: 0,
+    type: 0
+  });
+}
+
+onMounted(() => {
+  loadingData()
 })
 </script>
 
 <template>
   <vxe-table
+      border="inner"
+      height="auto"
       show-overflow
       :row-config="{isHover: true, useKey: true}"
       :column-config="{resizable: true}"
-      :tree-config="{transform: true, rowField: 'id', parentField: 'parentId', line: true}"
+      :tree-config="{  rowField: 'id', parentField: 'parentId', showLine: true}"
       :data="menuTreeData">
-    <vxe-column field="metaTitle" title="标题" tree-node></vxe-column>
+    <vxe-column field="metaTitle" title="标题" tree-node>
+      <template #default="{row}">
+        <span :class="row.icon"></span>
+        {{ row.metaTitle }}
+      </template>
+    </vxe-column>
     <vxe-column field="path" title="路径"></vxe-column>
     <vxe-column field="component" title="组件路径"></vxe-column>
-    <vxe-column title="操作" fixed="right"></vxe-column>
+    <vxe-column title="操作" fixed="right" width="220">
+      <template #header>
+        <el-button
+            type="primary"
+            @click="addRouterClick()"
+        >
+          新增
+        </el-button>
+      </template>
+      <template #default="{row}">
+        <el-button-group>
+          <el-button text type="primary" @click="addRouterClick(row)">新增</el-button>
+          <el-button text type="primary" @click="modify(row)">修改</el-button>
+          <el-button text type="primary" @click="delById(row.id)">删除</el-button>
+        </el-button-group>
+      </template>
+    </vxe-column>
   </vxe-table>
-
-  <!--  <el-table-->
-  <!--      class="no_el-table"-->
-  <!--      :data="menuTreeData"-->
-  <!--      highlight-current-row-->
-  <!--      height="100%"-->
-  <!--      style="width: 100%"-->
-  <!--      row-key="id"-->
-  <!--      :tree-props="{ children: 'children' }"-->
-  <!--  >-->
-  <!--    <el-table-column prop="metaTitle" label="标题">-->
-  <!--      <template #default="{row}">-->
-  <!--        <span :class="`${row.icon}`"></span>-->
-  <!--        <span>&nbsp;&nbsp;&nbsp;{{ row.metaTitle }}</span>-->
-  <!--      </template>-->
-  <!--    </el-table-column>-->
-  <!--    <el-table-column prop="path" label="路径"/>-->
-  <!--    <el-table-column prop="component" label="组件路径"/>-->
-  <!--    <el-table-column label="操作">-->
-  <!--      <template #default="{row}">-->
-  <!--        <el-button-group>-->
-  <!--          <el-button type="primary" size="large" link @click="modify(row)">修改</el-button>-->
-  <!--          <el-button type="danger" size="large" link>删除</el-button>-->
-  <!--        </el-button-group>-->
-  <!--      </template>-->
-  <!--    </el-table-column>-->
-  <!--  </el-table>-->
-
-  <Dialog :menuTreeData="menuTreeData" ref="dialog"/>
+  <Dialog :cascaderData="menuTreeData"
+          @saveCallback="loadingData()"
+          ref="dialogRef"/>
 </template>
 
 <style lang="scss">

+ 175 - 49
src/views/settings/menu-settings/dialog.vue

@@ -1,7 +1,6 @@
 <script setup lang="ts">
 import {ref} from "vue";
-import {isDev} from "@/utils/public";
-import {IntergrationMenu} from "@/api/settings/menu-settings";
+import {IntergrationMenu, updateMenuSettings} from "@/api/settings/menu-settings";
 import {
   ElButton,
   ElCascader,
@@ -13,22 +12,27 @@ import {
   ElInput,
   ElPopover,
   ElInputNumber,
-  ElSwitch
+  ElSwitch,
+  FormRules,
+  ElSelectV2
 } from "element-plus";
 import XEUtils from "xe-utils";
 import icon from '@/icons/iconfont.css'
 import {eachAndReturnList} from "@/utils/cyRefList";
+import {useCompRef} from "@/utils/useCompRef";
+import {QuestionFilled} from "@element-plus/icons-vue";
 
 const props = defineProps<{
-  menuTreeData: any[],
+  cascaderData: any[],
+}>()
+
+const emits = defineEmits<{
+  (e: "saveCallback"): void,
 }>()
 
-declare type VueIntergrationPlatformMenu = {
-  parentCascader: number[]
-} & IntergrationMenu
 
-const showDialog = ref(isDev)
-const currentData = ref<VueIntergrationPlatformMenu>({
+const showDialog = ref(false)
+const currentData = ref<IntergrationMenu>({
   children: [],
   component: "",
   icon: "",
@@ -37,7 +41,7 @@ const currentData = ref<VueIntergrationPlatformMenu>({
   metaPassRule: false,
   metaTitle: "",
   name: "",
-  parentCascader: [],
+  cascaders: [],
   parentId: 0,
   path: "",
   pathParams: "",
@@ -46,27 +50,84 @@ const currentData = ref<VueIntergrationPlatformMenu>({
   type: 0
 })
 
-function save() {
-  if (currentData.value.parentCascader.length > 0) {
-    currentData.value.parentId = currentData.value.parentCascader[currentData.value.parentCascader.length - 1]
+const fromRef = useCompRef(ElForm)
+
+
+const rules: FormRules<IntergrationMenu> = reactive({})
+type D = keyof IntergrationMenu
+
+function addRequired(key: D[]) {
+  key.forEach(item => {
+    rules[item] = [{required: true, trigger: 'change', message: '必填'}]
+  })
+}
+
+addRequired(['path', 'component', 'name', 'metaTitle'])
+
+async function save() {
+  await fromRef.value?.validate()
+  if (currentData.value.cascaders?.length > 0) {
+    currentData.value.parentId = currentData.value!.cascaders?.[currentData.value.cascaders!.length - 1]
   }
+  await updateMenuSettings(currentData.value)
+  showDialog.value = false
+  emits('saveCallback')
 }
 
+const icons = ref([])
+const routerKey = ref([])
+
+const layout = [
+  {
+    value: 'Layout',
+    label: 'Layout'
+  },
+  {
+    value: 'EmptyRouter',
+    label: 'EmptyRouter'
+  }
+]
+
+function dialogOpen() {
+  const regex = /.(?<icon>.*):before/g;
+  const matches = (icon as string).matchAll(regex);
+  icons.value = eachAndReturnList((matches as string[]), (item) => {
+    return item[1]
+  })
+
+  const view = import.meta.glob('/src/views/**/*.{vue,tsx}');
+
+  routerKey.value = eachAndReturnList(Object.keys(view), (item) => {
+    const tmp = item.replace("/src", "..")
+    return {
+      value: tmp,
+      label: tmp,
+    }
+  })
+  routerKey.value.push(...layout)
+}
+
+function handlePath() {
+  const isStartsWith = currentData.value.path.startsWith("/");
+  if (currentData.value.cascaders === null || currentData.value.cascaders!.length === 0) {
+    if (!isStartsWith) {
+      currentData.value.path = "/" + currentData.value.path
+    }
+  } else {
+    if (isStartsWith) {
+      currentData.value.path = currentData.value.path.substring(1)
+    }
+  }
+}
 
-const regex = /.(?<icon>.*):before/g;
-const matches = (icon as string).matchAll(regex);
-const icons = ref(eachAndReturnList((matches as string[]), (item) => {
-  return item[1]
-}))
 
 function iconClick(icon) {
   currentData.value.icon = `iconfont ${icon}`
 }
 
 defineExpose({
-  openDialog: (row: VueIntergrationPlatformMenu) => {
+  openDialog: (row: IntergrationMenu) => {
     currentData.value = XEUtils.clone(row, true)
-    currentData.value.parentCascader = []
     showDialog.value = true;
   }
 })
@@ -75,59 +136,98 @@ defineExpose({
 
 <template>
   <el-dialog v-model="showDialog"
+             @open="dialogOpen"
              title="菜单编辑"
              destroy-on-close
              draggable>
-    <el-form label-width="80px" size="default">
+    <el-form
+        ref="fromRef"
+        :model="currentData"
+        :rules="rules"
+        label-width="80px"
+        size="default">
       <el-row :gutter="10">
+
+        <el-col :span="24">
+          <el-form-item label="类型" prop="type">
+            <el-switch v-model="currentData.type"
+                       :active-value="1"
+                       :inactive-value="2"
+                       active-text="目录"
+                       inactive-text="页面"
+            />
+          </el-form-item>
+        </el-col>
+
         <el-col :span="24">
-          <el-form-item label="父节点">
+          <el-form-item label="父节点" prop="cascaders">
             <el-cascader
-                v-model="currentData.parentCascader"
+                @change="handlePath"
+                v-model="currentData.cascaders"
                 style="width: 100%"
                 :props="{ checkStrictly: true, value: 'id', label: 'metaTitle' }"
-                :options="menuTreeData"
+                :options="cascaderData"
                 clearable/>
           </el-form-item>
         </el-col>
 
-
         <el-col :span="24">
-          <el-form-item label="完整路径">
-            <el-input v-model="currentData.path"/>
+          <el-form-item prop="path">
+            <template #label>
+              路径
+              <el-tooltip content="如果是在根目录则不需要已 / 开头" placement="top">
+                <el-icon>
+                  <QuestionFilled/>
+                </el-icon>
+              </el-tooltip>
+            </template>
+            <el-input v-model="currentData.path"
+                      @blur="handlePath"
+            />
           </el-form-item>
         </el-col>
 
         <el-col :span="24">
-          <el-form-item label="组件路径">
-            <el-input v-model="currentData.component"/>
-          </el-form-item>
-        </el-col>
+          <el-form-item label="组件路径" prop="component">
+            <el-select-v2
+                v-model="currentData.component"
+                :options="routerKey"
+                style="width: 100%"
+                filterable
+            />
 
-        <el-col :span="24">
-          <el-form-item label="路径参数">
-            <el-input v-model="currentData.pathParams"/>
           </el-form-item>
         </el-col>
 
         <el-col :span="12">
-          <el-form-item label="类型">
-            <el-switch v-model="currentData.type"
-                       :active-value="1"
-                       :inactive-value="2"
-                       active-text="目录"
-                       inactive-text="页面"
-            />
+          <el-form-item prop="name">
+            <template #label>
+              名称
+              <el-tooltip content="这个名称必须是唯一" placement="top">
+                <el-icon>
+                  <QuestionFilled/>
+                </el-icon>
+              </el-tooltip>
+            </template>
+            <el-input v-model="currentData.name"
+                      clearable maxlength="50"/>
           </el-form-item>
         </el-col>
 
         <el-col :span="12">
-          <el-form-item label="路由名称">
-            <el-input v-model="currentData.name"
+          <el-form-item label="标题" prop="metaTitle">
+            <el-input v-model="currentData.metaTitle"
                       clearable maxlength="50"/>
           </el-form-item>
         </el-col>
 
+        <el-col :span="24">
+          <el-form-item label="路径参数" prop="pathParams">
+            <el-input v-model="currentData.pathParams"/>
+          </el-form-item>
+        </el-col>
+
+
         <el-col :span="12">
           <el-form-item label="菜单图标">
             <el-popover :width="0" trigger="click">
@@ -154,12 +254,6 @@ defineExpose({
           </el-form-item>
         </el-col>
 
-        <el-col :span="12">
-          <el-form-item label="菜单名称">
-            <el-input v-model="currentData.metaTitle"
-                      clearable maxlength="50"/>
-          </el-form-item>
-        </el-col>
 
         <el-col :span="12">
           <el-form-item label="重定向">
@@ -196,6 +290,38 @@ defineExpose({
           </el-form-item>
         </el-col>
 
+        <el-col :span="12">
+          <el-form-item label="卡片类型">
+            <el-switch v-model="currentData.mainCard"
+                       :active-value="1"
+                       :inactive-value="0"
+                       active-text="是"
+                       inactive-text="否"
+            />
+          </el-form-item>
+        </el-col>
+
+        <el-col :span="12">
+          <el-form-item label="超出滚动">
+            <el-switch v-model="currentData.mainOverflowAuto"
+                       :active-value="1"
+                       :inactive-value="0"
+                       active-text="是"
+                       inactive-text="否"
+            />
+          </el-form-item>
+        </el-col>
+
+        <el-col :span="12">
+          <el-form-item label="无需登录">
+            <el-switch v-model="currentData.metaNeedToken"
+                       :active-value="1"
+                       :inactive-value="0"
+                       active-text="是"
+                       inactive-text="否"
+            />
+          </el-form-item>
+        </el-col>
 
       </el-row>
     </el-form>

+ 7 - 7
src/views/single-page/TodayClinicResource.vue

@@ -3,7 +3,7 @@
     <div class="title-box">
       <img :src="logo" alt="">
       <p class="title-text">今日开放号源</p>
-      <p class="title-time">{{currentTimeText}}</p>
+      <p class="title-time">{{ currentTimeText }}</p>
     </div>
     <table>
       <tr>
@@ -33,8 +33,8 @@ const w22 = width * 0.22
 const config = reactive({
   data: [],
   rowNum: 4,
-  columnWidth: [w22,w12,w18,w12,w14,w22],
-  align: ['center','center','center','center','center','center'],
+  columnWidth: [w22, w12, w18, w12, w14, w22],
+  align: ['center', 'center', 'center', 'center', 'center', 'center'],
   carousel: 'single',
   hoverPause: false
 })
@@ -72,9 +72,9 @@ onMounted(() => {
   queryResourceData()
   setInterval(() => {
     let date = new Date();
-    let dateStr=date.getFullYear() + "/" +(date.getMonth() + 1) + "/" + date.getDate();
+    let dateStr = date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate();
     let timeStr = date.toLocaleTimeString();
-    let weekStr='星期'+'日一二三四五六'.charAt(new Date().getDay());
+    let weekStr = '星期' + '日一二三四五六'.charAt(new Date().getDay());
     currentTimeText.value = dateStr + " --- " + timeStr + " --- " + weekStr;
   }, 1000)
 })
@@ -94,7 +94,7 @@ onMounted(() => {
 
   .title-box {
     position: relative;
-    background-image: linear-gradient(to top,white 0%, lightskyblue 100%);
+    background-image: linear-gradient(to top, white 0%, lightskyblue 100%);
     display: table;
     width: 100vw;
     height: 10vh;
@@ -166,4 +166,4 @@ onMounted(() => {
 
 }
 
-</style>
+</style>

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