From 2c653e72af89dcef3c1784693b749dc8052c0ad5 Mon Sep 17 00:00:00 2001 From: chenjiale Date: Mon, 22 Dec 2025 10:10:38 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(env):=20=E6=B7=BB=E5=8A=A0=E7=99=BE?= =?UTF-8?q?=E5=BA=A6=E7=BB=9F=E8=AE=A1=E5=BC=80=E5=85=B3=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 .env、.env.production 和 .env.static 文件中新增 `VITE_APP_BAIDU_ENABLE` 配置项, 用于控制是否启用百度统计功能,默认设置为 false。 feat(package): 替换构建脚本中的 tsx 命令为 esno 将 build 相关命令中的 `tsx` 替换为 `pnpm exec esno`,以统一执行方式并提高兼容性。 fix(drawer): 优化 detail 模式下的容器挂载逻辑 当内容区容器不存在或高度为 0 时,回退到 body 容器并避免添加 __detail 类, 防止因 absolute 定位导致抽屉不可见的问题。 feat(icon): 改进图标缺失时的展示样式 引入 `.iconify-missing` 样式类,在图标加载失败时显示带问号的圆形占位符, 提升用户体验与界面可读性。 feat(router): 新增模型创建页面路由 添加 `/model/create` 路由及其子路由,支持从模型列表跳转至新建模型页面, 隐藏菜单和面包屑导航。 feat(tongji): 增强百度统计初始化逻辑 增加对 `VITE_APP_BAIDU_ENABLE` 环境变量的支持,并确保 script 标签不会重复插入, 增强百度统计的安全性和可控性。 refactor(model): 移除 CreateModel 抽屉组件引用 移除 ModelCard 中对 CreateModel 抽屉组件的依赖,改为通过路由跳转实现创建功能, 简化组件结构并降低耦合度。 ``` --- .env | 1 + .env.production | 1 + .env.static | 3 +- package.json | 6 +- src/components/Drawer/src/BasicDrawer.vue | 21 ++- src/components/Icon/src/Icon.vue | 22 ++- src/router/routes/index.ts | 22 +++ src/utils/tongji.ts | 52 ++++--- src/views/model/create/index.vue | 177 ++++++++++++++++++++++ src/views/model/list/ModelCard.vue | 15 +- 10 files changed, 286 insertions(+), 34 deletions(-) create mode 100644 src/views/model/create/index.vue diff --git a/.env b/.env index 50527b1..fcc0588 100644 --- a/.env +++ b/.env @@ -17,4 +17,5 @@ VITE_GLOB_APP_CAPTCHA_ENABLE = true VITE_APP_DOCALERT_ENABLE=false # 百度统计 +VITE_APP_BAIDU_ENABLE = false VITE_APP_BAIDU_CODE = eb21166668bf766b9d059a6fd1c10777 diff --git a/.env.production b/.env.production index 839f3bc..d34cf98 100644 --- a/.env.production +++ b/.env.production @@ -24,4 +24,5 @@ VITE_GLOB_API_URL_PREFIX = VITE_USE_PWA = false # 百度统计 +VITE_APP_BAIDU_ENABLE = false VITE_APP_BAIDU_CODE = eb21166668bf766b9d059a6fd1c10777 diff --git a/.env.static b/.env.static index 9bfa7ac..f4d03d1 100644 --- a/.env.static +++ b/.env.static @@ -24,7 +24,8 @@ VITE_GLOB_API_URL_PREFIX = VITE_USE_PWA = false # 百度统计 +VITE_APP_BAIDU_ENABLE = false VITE_APP_BAIDU_CODE = eb21166668bf766b9d059a6fd1c10777 # 验证码的开关 -VITE_GLOB_APP_CAPTCHA_ENABLE = false \ No newline at end of file +VITE_GLOB_APP_CAPTCHA_ENABLE = false diff --git a/package.json b/package.json index ca9d14a..6afa12a 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,9 @@ "serve": "pnpm dev", "dev": "vite", "front": "vite --mode front", - "build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 vite build && tsx ./build/script/postBuild.ts", - "build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode test && tsx ./build/script/postBuild.ts", - "build:static": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode static && tsx ./build/script/postBuild.ts", + "build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 vite build && pnpm exec esno ./build/script/postBuild.ts", + "build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode test && pnpm exec esno ./build/script/postBuild.ts", + "build:static": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode static && pnpm exec esno ./build/script/postBuild.ts", "build:no-cache": "pnpm store prune && pnpm build", "report": "cross-env REPORT=true pnpm build", "type:check": "vue-tsc --noEmit --skipLibCheck", diff --git a/src/components/Drawer/src/BasicDrawer.vue b/src/components/Drawer/src/BasicDrawer.vue index 5440169..14939a5 100644 --- a/src/components/Drawer/src/BasicDrawer.vue +++ b/src/components/Drawer/src/BasicDrawer.vue @@ -52,11 +52,26 @@ const getProps = computed((): DrawerProps => { if (!width) opt.width = '100%' + // detail 模式默认挂载到内容区容器,避免遮挡侧边栏/头部等 + // 但某些部署场景下内容区容器不存在或高度为 0,会导致 Drawer 由于 `position: absolute` 看起来“空白” const detailCls = `${prefixCls}__detail` - opt.rootClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls - if (!getContainer) - opt.getContainer = `.${prefixVar}-layout-content` + const selector = `.${prefixVar}-layout-content` + const layoutEl = typeof document === 'undefined' + ? null + : (document.querySelector(selector) as HTMLElement | null) + + const canUseLayoutContainer = !!layoutEl && layoutEl.getBoundingClientRect().height > 0 + + if (canUseLayoutContainer) { + opt.rootClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls + if (!getContainer) + opt.getContainer = layoutEl + } + else { + // 回退到默认容器(body);同时不要挂 `__detail` 类,避免 absolute 导致不可见 + opt.rootClassName = wrapClassName + } } return opt as DrawerProps }) diff --git a/src/components/Icon/src/Icon.vue b/src/components/Icon/src/Icon.vue index 528667a..b650884 100644 --- a/src/components/Icon/src/Icon.vue +++ b/src/components/Icon/src/Icon.vue @@ -50,8 +50,8 @@ async function update() { } else { const span = document.createElement('span') - span.className = 'iconify' - span.dataset.icon = icon + span.className = 'iconify-missing' + span.title = icon el.textContent = '' el.appendChild(span) } @@ -99,4 +99,22 @@ span.iconify { background-color: @iconify-bg-color; border-radius: 100%; } + +span.iconify-missing { + display: flex; + align-items: center; + justify-content: center; + min-width: 1em; + min-height: 1em; + background-color: @iconify-bg-color; + border-radius: 100%; + font-size: 0.75em; + color: currentcolor; + + &::after { + content: '?'; + line-height: 1; + opacity: 0.65; + } +} diff --git a/src/router/routes/index.ts b/src/router/routes/index.ts index 976093a..59ab86e 100644 --- a/src/router/routes/index.ts +++ b/src/router/routes/index.ts @@ -119,6 +119,28 @@ export const basicRoutes = [ RootRoute, ProfileRoute, CodegenRoute, + { + path: '/model/create', + name: 'ModelCreateBasic', + component: LAYOUT, + meta: { + title: '新建模型', + hideMenu: true, + hideBreadcrumb: true, + }, + children: [ + { + path: '', + name: 'ModelCreate', + component: () => import('@/views/model/create/index.vue'), + meta: { + title: '新建模型', + hideMenu: true, + currentActiveMenu: '/model/list', + }, + }, + ], + }, { path: '/model/assess-report/:id?', name: 'AssessReportBasic', diff --git a/src/utils/tongji.ts b/src/utils/tongji.ts index 270074f..d82bf00 100644 --- a/src/utils/tongji.ts +++ b/src/utils/tongji.ts @@ -1,23 +1,35 @@ import { router } from '@/router' -// 用于 router push -window._hmt = window._hmt || [] -// HM_ID +declare global { + interface Window { + _hmt?: any[] + } +} + +// HM_ID(构建时注入) const HM_ID = import.meta.env.VITE_APP_BAIDU_CODE -;(function () { - // 有值的时候,才开启 - if (!HM_ID) - return - - const hm = document.createElement('script') - hm.src = `https://hm.baidu.com/hm.js?${HM_ID}` - const s = document.getElementsByTagName('script')[0] - s.parentNode.insertBefore(hm, s) -})() - -router.afterEach((to) => { - if (!HM_ID) - return - - _hmt.push(['_trackPageview', to.fullPath]) -}) + +// 统计开关:默认关闭,离线/内网部署建议保持关闭 +const BAIDU_TONGJI_ENABLED = import.meta.env.VITE_APP_BAIDU_ENABLE === 'true' + +const shouldEnable = BAIDU_TONGJI_ENABLED && !!HM_ID + +if (shouldEnable) { + window._hmt = window._hmt || [] + + ;(function () { + const existing = document.querySelector(`script[data-hm-id="${HM_ID}"]`) + if (existing) + return + + const hm = document.createElement('script') + hm.setAttribute('data-hm-id', HM_ID) + hm.src = `https://hm.baidu.com/hm.js?${HM_ID}` + const s = document.getElementsByTagName('script')[0] + s?.parentNode?.insertBefore(hm, s) + })() + + router.afterEach((to) => { + window._hmt?.push(['_trackPageview', to.fullPath]) + }) +} diff --git a/src/views/model/create/index.vue b/src/views/model/create/index.vue new file mode 100644 index 0000000..8a1d00f --- /dev/null +++ b/src/views/model/create/index.vue @@ -0,0 +1,177 @@ + + + + + diff --git a/src/views/model/list/ModelCard.vue b/src/views/model/list/ModelCard.vue index c31207b..38a98f4 100644 --- a/src/views/model/list/ModelCard.vue +++ b/src/views/model/list/ModelCard.vue @@ -3,12 +3,10 @@ import { Card, Dropdown, Menu, Popconfirm } from 'ant-design-vue' import type { PropType } from 'vue' import { ref, watch } from 'vue' import type { ModelItem } from './data' -import CreateModel from './CreateModel.vue' import Icon from '@/components/Icon/index' import { modelCardListApi, modelDeleteApi } from '@/api/alert/model/models' import type { ModelQueryParams } from '@/api/alert/model/model/models' import { useGo } from '@/hooks/web/usePage' -import { useDrawer } from '@/components/Drawer' import { useMessage } from '@/hooks/web/useMessage' const props = defineProps({ @@ -26,7 +24,6 @@ const props = defineProps({ }, }) -const [registerDraw, { openDrawer }] = useDrawer() const { createMessage } = useMessage() // 点击模型卡片跳转训练页面 @@ -97,6 +94,15 @@ async function confirmDelete(id: number | string) { createMessage.success('删除成功') await loadModelList(props.selectData) } + +function goCreateModel() { + const query: string[] = [] + if (props.systemId !== undefined && props.systemId !== null) + query.push(`systemId=${encodeURIComponent(String(props.systemId))}`) + if (props.unitId !== undefined && props.unitId !== null) + query.push(`unitId=${encodeURIComponent(String(props.unitId))}`) + go(`/model/create${query.length ? `?${query.join('&')}` : ''}`) +} - + -