diff --git a/.npmrc b/.npmrc
index 0154bc0..86c14a0 100644
--- a/.npmrc
+++ b/.npmrc
@@ -1,3 +1,4 @@
shamefully-hoist=true
strict-peer-dependencies=false
-shell-emulator=true
\ No newline at end of file
+shell-emulator=true
+registry=https://registry.npmmirror.com/
\ No newline at end of file
diff --git a/.vite/deps_temp_399fba7b/package.json b/.vite/deps_temp_399fba7b/package.json
new file mode 100644
index 0000000..3dbc1ca
--- /dev/null
+++ b/.vite/deps_temp_399fba7b/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "module"
+}
diff --git a/indexDataScreen.html b/indexDataScreen.html
new file mode 100644
index 0000000..2d35f8a
--- /dev/null
+++ b/indexDataScreen.html
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+ pure-admin-thin
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package.json b/package.json
index 019b1c8..8110fed 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
"not op_mini all"
],
"dependencies": {
- "@jiaminghi/data-view": "^2.10.0",
+ "@kjgl77/datav-vue3": "^1.7.2",
"@pureadmin/descriptions": "^1.1.1",
"@pureadmin/table": "^2.3.2",
"@pureadmin/utils": "^1.9.6",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f26ea96..45c81a1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -5,9 +5,9 @@ settings:
excludeLinksFromLockfile: false
dependencies:
- "@jiaminghi/data-view":
- specifier: ^2.10.0
- version: 2.10.0
+ "@kjgl77/datav-vue3":
+ specifier: ^1.7.2
+ version: 1.7.2(vue@3.3.4)
"@pureadmin/descriptions":
specifier: ^1.1.1
version: 1.1.1(element-plus@2.3.6)
@@ -1477,16 +1477,6 @@ packages:
}
dev: false
- /@jiaminghi/data-view@2.10.0:
- resolution:
- {
- integrity: sha512-Cud2MTiMcqc5k2KWabR/svuVQmXHANqURo+yj40370/LdI/gyUJ6LG203hWXEnT1nMCeiv/SLVmxv3PXLScCeA==
- }
- dependencies:
- "@babel/runtime": 7.23.9
- "@jiaminghi/charts": 0.2.18
- dev: false
-
/@jiaminghi/transition@1.1.11:
resolution:
{
@@ -1570,6 +1560,21 @@ packages:
"@jridgewell/sourcemap-codec": 1.4.15
dev: true
+ /@kjgl77/datav-vue3@1.7.2(vue@3.3.4):
+ resolution:
+ {
+ integrity: sha512-Fvllk4rJEdUwLdumvsnPADZNPMKkDdC7u/vJEAPBsMh6UgFQytIT+SVtjj1vy/TXtgcH/te9hlXBPgV8LVSmQw==
+ }
+ dependencies:
+ "@jiaminghi/c-render": 0.4.3
+ "@jiaminghi/charts": 0.2.18
+ "@jiaminghi/color": 1.1.3
+ "@vueuse/core": 10.8.0(vue@3.3.4)
+ transitivePeerDependencies:
+ - "@vue/composition-api"
+ - vue
+ dev: false
+
/@nodelib/fs.scandir@2.1.5:
resolution:
{
@@ -1951,6 +1956,13 @@ packages:
}
dev: false
+ /@types/web-bluetooth@0.0.20:
+ resolution:
+ {
+ integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==
+ }
+ dev: false
+
/@typescript-eslint/eslint-plugin@5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.43.0)(typescript@5.0.4):
resolution:
{
@@ -2391,6 +2403,21 @@ packages:
- vue
dev: false
+ /@vueuse/core@10.8.0(vue@3.3.4):
+ resolution:
+ {
+ integrity: sha512-G9Ok9fjx10TkNIPn8V1dJmK1NcdJCtYmDRyYiTMUyJ1p0Tywc1zmOoCQ2xhHYyz8ULBU4KjIJQ9n+Lrty74iVw==
+ }
+ dependencies:
+ "@types/web-bluetooth": 0.0.20
+ "@vueuse/metadata": 10.8.0
+ "@vueuse/shared": 10.8.0(vue@3.3.4)
+ vue-demi: 0.14.7(vue@3.3.4)
+ transitivePeerDependencies:
+ - "@vue/composition-api"
+ - vue
+ dev: false
+
/@vueuse/core@9.13.0(vue@3.3.4):
resolution:
{
@@ -2413,6 +2440,13 @@ packages:
}
dev: false
+ /@vueuse/metadata@10.8.0:
+ resolution:
+ {
+ integrity: sha512-Nim/Vle5OgXcXhAvGOgkJQXB1Yb+Kq/fMbLuv3YYDYbiQrwr39ljuD4k9fPeq4yUyokYRo2RaNQmbbIMWB/9+w==
+ }
+ dev: false
+
/@vueuse/metadata@9.13.0:
resolution:
{
@@ -2455,6 +2489,18 @@ packages:
- vue
dev: false
+ /@vueuse/shared@10.8.0(vue@3.3.4):
+ resolution:
+ {
+ integrity: sha512-dUdy6zwHhULGxmr9YUg8e+EnB39gcM4Fe2oKBSrh3cOsV30JcMPtsyuspgFCUo5xxFNaeMf/W2yyKfST7Bg8oQ==
+ }
+ dependencies:
+ vue-demi: 0.14.7(vue@3.3.4)
+ transitivePeerDependencies:
+ - "@vue/composition-api"
+ - vue
+ dev: false
+
/@vueuse/shared@9.13.0(vue@3.3.4):
resolution:
{
@@ -9029,6 +9075,24 @@ packages:
vue: 3.3.4
dev: false
+ /vue-demi@0.14.7(vue@3.3.4):
+ resolution:
+ {
+ integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==
+ }
+ engines: { node: ">=12" }
+ hasBin: true
+ requiresBuild: true
+ peerDependencies:
+ "@vue/composition-api": ^1.0.0-rc.1
+ vue: ^3.0.0-0 || ^2.6.0
+ peerDependenciesMeta:
+ "@vue/composition-api":
+ optional: true
+ dependencies:
+ vue: 3.3.4
+ dev: false
+
/vue-eslint-parser@9.3.1(eslint@8.43.0):
resolution:
{
diff --git a/src/assets/svg/screenBgCommon.svg b/src/assets/svg/screenBgCommon.svg
new file mode 100644
index 0000000..b764dc1
--- /dev/null
+++ b/src/assets/svg/screenBgCommon.svg
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/src/components/CustomTree/src/collapseTree.tsx b/src/components/CustomTree/src/collapseTree.tsx
index 96e8cf6..e491e6f 100644
--- a/src/components/CustomTree/src/collapseTree.tsx
+++ b/src/components/CustomTree/src/collapseTree.tsx
@@ -1,6 +1,8 @@
import { defineComponent, reactive } from "vue";
import CollapseTreeItem from "./collapseTreeItem";
import "./collapseTreeStyle.scss";
+import styles from "./collapseTreeStyle.scss";
+
const testDeviceTreeData = [
{
name: "临沂总部",
@@ -122,22 +124,27 @@ export default defineComponent({
{vFirst.name}
-
- {Array.isArray(vFirst.childList) &&
- vFirst.childList.length && (
-
- {vFirst.childList.map((item, index) => {
- return (
- -
-
-
- );
- })}
-
- )}
-
+
+
+ {Array.isArray(vFirst.childList) &&
+ vFirst.childList.length && (
+
+ {vFirst.childList.map((item, index) => {
+ return (
+ -
+
+
+ );
+ })}
+
+ )}
+
+
);
})}
diff --git a/src/layout/dataScreenIndex.vue b/src/layout/dataScreenIndex.vue
new file mode 100644
index 0000000..87622c7
--- /dev/null
+++ b/src/layout/dataScreenIndex.vue
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
diff --git a/src/pages/dataScreen/App.vue b/src/pages/dataScreen/App.vue
new file mode 100644
index 0000000..6dfcbec
--- /dev/null
+++ b/src/pages/dataScreen/App.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/dataScreen/main.ts b/src/pages/dataScreen/main.ts
new file mode 100644
index 0000000..e0b3add
--- /dev/null
+++ b/src/pages/dataScreen/main.ts
@@ -0,0 +1,72 @@
+/*
+ * @Author: donghao donghao@supervision.ltd
+ * @Date: 2024-02-22 14:04:38
+ * @LastEditors: donghao donghao@supervision.ltd
+ * @LastEditTime: 2024-02-22 17:48:10
+ * @FilePath: \General-AI-Platform-Web-Client\src\pages\dataScreen\main.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+import App from "./App.vue";
+import router from "./router";
+import { setupStore } from "@/store";
+import ElementPlus from "element-plus";
+import { useI18n } from "@/plugins/i18n";
+import { getServerConfig } from "@/config";
+import { createApp, Directive } from "vue";
+import { MotionPlugin } from "@vueuse/motion";
+import { useEcharts } from "@/plugins/echarts";
+import { injectResponsiveStorage } from "@/utils/responsive";
+
+import DataVVue3 from "@kjgl77/datav-vue3";
+// import Table from "@pureadmin/table";
+// import PureDescriptions from "@pureadmin/descriptions";
+
+// 引入重置样式
+import "@/style/reset.scss";
+// 导入公共样式
+import "@/style/index.scss";
+// 一定要在main.ts中导入tailwind.css,防止vite每次hmr都会请求src/style/index.scss整体css文件导致热更新慢的问题
+import "@/style/tailwind.css";
+import "element-plus/dist/index.css";
+// 导入字体图标
+import "@/assets/iconfont/iconfont.js";
+import "@/assets/iconfont/iconfont.css";
+
+const app = createApp(App);
+
+// 自定义指令
+import * as directives from "@/directives";
+Object.keys(directives).forEach(key => {
+ app.directive(key, (directives as { [key: string]: Directive })[key]);
+});
+
+// 全局注册`@iconify/vue`图标库
+import {
+ IconifyIconOffline,
+ IconifyIconOnline,
+ FontIcon
+} from "@/components/ReIcon";
+app.component("IconifyIconOffline", IconifyIconOffline);
+app.component("IconifyIconOnline", IconifyIconOnline);
+app.component("FontIcon", FontIcon);
+
+// 全局注册按钮级别权限组件
+import { Auth } from "@/components/ReAuth";
+app.component("Auth", Auth);
+
+getServerConfig(app).then(async config => {
+ app.use(router);
+ await router.isReady();
+ injectResponsiveStorage(app, config);
+ setupStore(app);
+ app
+ .use(MotionPlugin)
+ .use(useI18n)
+ .use(ElementPlus)
+ .use(useEcharts)
+ .use(DataVVue3);
+ // .use(useEcharts);
+ // .use(Table);
+ // .use(PureDescriptions);
+ app.mount("#app");
+});
diff --git a/src/pages/dataScreen/router/index.ts b/src/pages/dataScreen/router/index.ts
new file mode 100644
index 0000000..0f37e76
--- /dev/null
+++ b/src/pages/dataScreen/router/index.ts
@@ -0,0 +1,202 @@
+// import "@/utils/sso";
+import { getConfig } from "@/config";
+import NProgress from "@/utils/progress";
+import { transformI18n } from "@/plugins/i18n";
+import { sessionKey, type DataInfo } from "@/utils/auth";
+import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
+import { usePermissionStoreHook } from "@/store/modules/permission";
+import {
+ Router,
+ createRouter,
+ RouteRecordRaw,
+ RouteComponent
+} from "vue-router";
+import {
+ ascending,
+ getTopMenu,
+ initRouter,
+ isOneOfArray,
+ getHistoryMode,
+ findRouteByPath,
+ handleAliveRoute,
+ formatTwoStageRoutes,
+ formatFlatteningRoutes
+} from "./utils";
+import { buildHierarchyTree } from "@/utils/tree";
+import { isUrl, openLink, storageSession, isAllEmpty } from "@pureadmin/utils";
+
+import remainingRouter from "./modules/remaining";
+
+/** 自动导入全部静态路由,无需再手动引入!匹配 src/router/modules 目录(任何嵌套级别)中具有 .ts 扩展名的所有文件,除了 remaining.ts 文件
+ * 如何匹配所有文件请看:https://github.com/mrmlnc/fast-glob#basic-syntax
+ * 如何排除文件请看:https://cn.vitejs.dev/guide/features.html#negative-patterns
+ */
+const modules: Record = import.meta.glob(
+ ["./modules/**/*.ts", "!./modules/**/remaining.ts"],
+ {
+ eager: true
+ }
+);
+
+/** 原始静态路由(未做任何处理) */
+const routes = [];
+
+Object.keys(modules).forEach(key => {
+ routes.push(modules[key].default);
+});
+
+/** 导出处理后的静态路由(三级及以上的路由全部拍成二级) */
+export const constantRoutes: Array = formatTwoStageRoutes(
+ formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
+);
+
+/** 用于渲染菜单,保持原始层级 */
+export const constantMenus: Array = ascending(
+ routes.flat(Infinity)
+).concat(...remainingRouter);
+
+/** 不参与菜单的路由 */
+export const remainingPaths = Object.keys(remainingRouter).map(v => {
+ return remainingRouter[v].path;
+});
+
+/** 创建路由实例 */
+export const router: Router = createRouter({
+ history: getHistoryMode(import.meta.env.VITE_ROUTER_HISTORY),
+ routes: constantRoutes.concat(...(remainingRouter as any)),
+ strict: true,
+ scrollBehavior(to, from, savedPosition) {
+ return new Promise(resolve => {
+ if (savedPosition) {
+ return savedPosition;
+ } else {
+ if (from.meta.saveSrollTop) {
+ const top: number =
+ document.documentElement.scrollTop || document.body.scrollTop;
+ resolve({ left: 0, top });
+ }
+ }
+ });
+ }
+});
+
+/** 重置路由 */
+export function resetRouter() {
+ router.getRoutes().forEach(route => {
+ const { name, meta } = route;
+ if (name && router.hasRoute(name) && meta?.backstage) {
+ router.removeRoute(name);
+ router.options.routes = formatTwoStageRoutes(
+ formatFlatteningRoutes(
+ buildHierarchyTree(ascending(routes.flat(Infinity)))
+ )
+ );
+ }
+ });
+ usePermissionStoreHook().clearAllCachePage();
+}
+
+/** 路由白名单 */
+const whiteList = ["/login"];
+
+const { VITE_HIDE_HOME } = import.meta.env;
+
+router.beforeEach((to: ToRouteType, _from, next) => {
+ if (to.meta?.keepAlive) {
+ handleAliveRoute(to, "add");
+ // 页面整体刷新和点击标签页刷新
+ if (_from.name === undefined || _from.name === "Redirect") {
+ handleAliveRoute(to);
+ }
+ }
+ const userInfo = storageSession().getItem>(sessionKey);
+ NProgress.start();
+ const externalLink = isUrl(to?.name as string);
+ if (!externalLink) {
+ to.matched.some(item => {
+ if (!item.meta.title) return "";
+ const Title = getConfig().Title;
+ if (Title)
+ document.title = `${transformI18n(item.meta.title)} | ${Title}`;
+ else document.title = transformI18n(item.meta.title);
+ });
+ }
+ /** 如果已经登录并存在登录信息后不能跳转到路由白名单,而是继续保持在当前页面 */
+ function toCorrectRoute() {
+ whiteList.includes(to.fullPath) ? next(_from.fullPath) : next();
+ }
+ if (userInfo) {
+ // 无权限跳转403页面
+ if (to.meta?.roles && !isOneOfArray(to.meta?.roles, userInfo?.roles)) {
+ next({ path: "/error/403" });
+ }
+ // 开启隐藏首页后在浏览器地址栏手动输入首页welcome路由则跳转到404页面
+ if (VITE_HIDE_HOME === "true" && to.fullPath === "/welcome") {
+ next({ path: "/error/404" });
+ }
+ if (_from?.name) {
+ // name为超链接
+ if (externalLink) {
+ openLink(to?.name as string);
+ NProgress.done();
+ } else {
+ toCorrectRoute();
+ }
+ } else {
+ // 刷新
+ if (
+ usePermissionStoreHook().wholeMenus.length === 0 &&
+ to.path !== "/login"
+ ) {
+ initRouter().then((router: Router) => {
+ if (!useMultiTagsStoreHook().getMultiTagsCache) {
+ const { path } = to;
+ const route = findRouteByPath(
+ path,
+ router.options.routes[0].children
+ );
+ getTopMenu(true);
+ // query、params模式路由传参数的标签页不在此处处理
+ if (route && route.meta?.title) {
+ if (isAllEmpty(route.parentId) && route.meta?.backstage) {
+ // 此处为动态顶级路由(目录)
+ const { path, name, meta } = route.children[0];
+ useMultiTagsStoreHook().handleTags("push", {
+ path,
+ name,
+ meta
+ });
+ } else {
+ const { path, name, meta } = route;
+ useMultiTagsStoreHook().handleTags("push", {
+ path,
+ name,
+ meta
+ });
+ }
+ }
+ }
+ // 确保动态路由完全加入路由列表并且不影响静态路由(注意:动态路由刷新时router.beforeEach可能会触发两次,第一次触发动态路由还未完全添加,第二次动态路由才完全添加到路由列表,如果需要在router.beforeEach做一些判断可以在to.name存在的条件下去判断,这样就只会触发一次)
+ if (isAllEmpty(to.name)) router.push(to.fullPath);
+ });
+ }
+ toCorrectRoute();
+ }
+ } else {
+ if (to.path !== "/login") {
+ if (whiteList.indexOf(to.path) !== -1) {
+ next();
+ } else {
+ next({ path: "/login" });
+ }
+ } else {
+ next();
+ }
+ }
+});
+
+router.afterEach(() => {
+ NProgress.done();
+});
+
+export default router;
diff --git a/src/pages/dataScreen/router/modules/computePowerAllocation.ts b/src/pages/dataScreen/router/modules/computePowerAllocation.ts
new file mode 100644
index 0000000..9b8fbb9
--- /dev/null
+++ b/src/pages/dataScreen/router/modules/computePowerAllocation.ts
@@ -0,0 +1,15 @@
+import { $t } from "@/plugins/i18n";
+
+export default {
+ path: "/computePowerAllocation",
+ meta: {
+ title: $t("menus.hsComputePowerAllocation"),
+ icon: "icon-suanlipeizhi",
+ // showLink: false,
+ bodyClass: "computePowerAllocation_page",
+ rank: 6,
+ roles: ["admin", "common"]
+ },
+ component: () => import("@/views/computePowerAllocation/index.vue"),
+ name: "computePowerAllocationIndex"
+} as RouteConfigsTable;
diff --git a/src/pages/dataScreen/router/modules/remaining.ts b/src/pages/dataScreen/router/modules/remaining.ts
new file mode 100644
index 0000000..cbc985a
--- /dev/null
+++ b/src/pages/dataScreen/router/modules/remaining.ts
@@ -0,0 +1,31 @@
+import { $t } from "@/plugins/i18n";
+const Layout = () => import("@/layout/index.vue");
+
+export default [
+ {
+ path: "/login",
+ name: "Login",
+ component: () => import("@/views/login/index.vue"),
+ meta: {
+ title: $t("menus.hslogin"),
+ showLink: false,
+ rank: 101
+ }
+ },
+ {
+ path: "/redirect",
+ component: Layout,
+ meta: {
+ title: $t("status.hsLoad"),
+ showLink: false,
+ rank: 102
+ },
+ children: [
+ {
+ path: "/redirect/:path(.*)",
+ name: "Redirect",
+ component: () => import("@/layout/redirect.vue")
+ }
+ ]
+ }
+] as Array;
diff --git a/src/pages/dataScreen/router/modules/workbench.ts b/src/pages/dataScreen/router/modules/workbench.ts
new file mode 100644
index 0000000..fa4a304
--- /dev/null
+++ b/src/pages/dataScreen/router/modules/workbench.ts
@@ -0,0 +1,35 @@
+/*
+ * @Author: donghao donghao@supervision.ltd
+ * @Date: 2024-01-12 14:35:28
+ * @LastEditors: donghao donghao@supervision.ltd
+ * @LastEditTime: 2024-02-22 14:28:13
+ * @FilePath: \General-AI-Platform-Web-Client\src\router\modules\workbench.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+import { $t } from "@/plugins/i18n";
+
+const Layout = () => import("@/layout/dataScreenIndex.vue");
+
+export default {
+ path: "/",
+ name: "Workbench",
+ component: Layout,
+ redirect: "/workbench",
+ meta: {
+ title: $t("menus.hshome"),
+ icon: "icon-gongzuotai-weixuan",
+ // showLink: false,
+ rank: 1
+ },
+ children: [
+ {
+ path: "/workbench",
+ name: "Workbench",
+ component: () => import("@/pages/dataScreen/views/home/homeIndex.vue"),
+ meta: {
+ title: "工作台",
+ roles: ["admin", "common"]
+ }
+ }
+ ]
+} as RouteConfigsTable;
diff --git a/src/pages/dataScreen/router/utils.ts b/src/pages/dataScreen/router/utils.ts
new file mode 100644
index 0000000..2390b80
--- /dev/null
+++ b/src/pages/dataScreen/router/utils.ts
@@ -0,0 +1,386 @@
+import {
+ RouterHistory,
+ RouteRecordRaw,
+ RouteComponent,
+ createWebHistory,
+ createWebHashHistory
+} from "vue-router";
+import { router } from "./index";
+import { isProxy, toRaw } from "vue";
+import { useTimeoutFn } from "@vueuse/core";
+import {
+ isString,
+ cloneDeep,
+ isAllEmpty,
+ intersection,
+ storageSession,
+ isIncludeAllChildren
+} from "@pureadmin/utils";
+import { getConfig } from "@/config";
+import { menuType } from "@/layout/types";
+import { buildHierarchyTree } from "@/utils/tree";
+import { sessionKey, type DataInfo } from "@/utils/auth";
+import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
+import { usePermissionStoreHook } from "@/store/modules/permission";
+const IFrame = () => import("@/layout/frameView.vue");
+// https://cn.vitejs.dev/guide/features.html#glob-import
+const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
+
+// 动态路由
+import { getAsyncRoutes } from "@/api/routes";
+
+function handRank(routeInfo: any) {
+ const { name, path, parentId, meta } = routeInfo;
+ return isAllEmpty(parentId)
+ ? isAllEmpty(meta?.rank) ||
+ (meta?.rank === 0 && name !== "Workbench" && path !== "/")
+ ? true
+ : false
+ : false;
+}
+
+/** 按照路由中meta下的rank等级升序来排序路由 */
+function ascending(arr: any[]) {
+ arr.forEach((v, index) => {
+ // 当rank不存在时,根据顺序自动创建,首页路由永远在第一位
+ if (handRank(v)) v.meta.rank = index + 2;
+ });
+ return arr.sort(
+ (a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
+ return a?.meta.rank - b?.meta.rank;
+ }
+ );
+}
+
+/** 过滤meta中showLink为false的菜单 */
+function filterTree(data: RouteComponent[]) {
+ const newTree = cloneDeep(data).filter(
+ (v: { meta: { showLink: boolean } }) => v.meta?.showLink !== false
+ );
+ newTree.forEach(
+ (v: { children }) => v.children && (v.children = filterTree(v.children))
+ );
+ return newTree;
+}
+
+/** 过滤children长度为0的的目录,当目录下没有菜单时,会过滤此目录,目录没有赋予roles权限,当目录下只要有一个菜单有显示权限,那么此目录就会显示 */
+function filterChildrenTree(data: RouteComponent[]) {
+ const newTree = cloneDeep(data).filter((v: any) => v?.children?.length !== 0);
+ newTree.forEach(
+ (v: { children }) => v.children && (v.children = filterTree(v.children))
+ );
+ return newTree;
+}
+
+/** 判断两个数组彼此是否存在相同值 */
+function isOneOfArray(a: Array, b: Array) {
+ return Array.isArray(a) && Array.isArray(b)
+ ? intersection(a, b).length > 0
+ ? true
+ : false
+ : true;
+}
+
+/** 从sessionStorage里取出当前登陆用户的角色roles,过滤无权限的菜单 */
+function filterNoPermissionTree(data: RouteComponent[]) {
+ const currentRoles =
+ storageSession().getItem>(sessionKey)?.roles ?? [];
+ const newTree = cloneDeep(data).filter((v: any) =>
+ isOneOfArray(v.meta?.roles, currentRoles)
+ );
+ newTree.forEach(
+ (v: any) => v.children && (v.children = filterNoPermissionTree(v.children))
+ );
+ return filterChildrenTree(newTree);
+}
+
+/** 通过指定 `key` 获取父级路径集合,默认 `key` 为 `path` */
+function getParentPaths(value: string, routes: RouteRecordRaw[], key = "path") {
+ // 深度遍历查找
+ function dfs(routes: RouteRecordRaw[], value: string, parents: string[]) {
+ for (let i = 0; i < routes.length; i++) {
+ const item = routes[i];
+ // 返回父级path
+ if (item[key] === value) return parents;
+ // children不存在或为空则不递归
+ if (!item.children || !item.children.length) continue;
+ // 往下查找时将当前path入栈
+ parents.push(item.path);
+
+ if (dfs(item.children, value, parents).length) return parents;
+ // 深度遍历查找未找到时当前path 出栈
+ parents.pop();
+ }
+ // 未找到时返回空数组
+ return [];
+ }
+
+ return dfs(routes, value, []);
+}
+
+/** 查找对应 `path` 的路由信息 */
+function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
+ let res = routes.find((item: { path: string }) => item.path == path);
+ if (res) {
+ return isProxy(res) ? toRaw(res) : res;
+ } else {
+ for (let i = 0; i < routes.length; i++) {
+ if (
+ routes[i].children instanceof Array &&
+ routes[i].children.length > 0
+ ) {
+ res = findRouteByPath(path, routes[i].children);
+ if (res) {
+ return isProxy(res) ? toRaw(res) : res;
+ }
+ }
+ }
+ return null;
+ }
+}
+
+function addPathMatch() {
+ if (!router.hasRoute("pathMatch")) {
+ router.addRoute({
+ path: "/:pathMatch(.*)",
+ name: "pathMatch",
+ redirect: "/error/404"
+ });
+ }
+}
+
+/** 处理动态路由(后端返回的路由) */
+function handleAsyncRoutes(routeList) {
+ if (routeList.length === 0) {
+ usePermissionStoreHook().handleWholeMenus(routeList);
+ } else {
+ formatFlatteningRoutes(addAsyncRoutes(routeList)).map(
+ (v: RouteRecordRaw) => {
+ // 防止重复添加路由
+ if (
+ router.options.routes[0].children.findIndex(
+ value => value.path === v.path
+ ) !== -1
+ ) {
+ return;
+ } else {
+ // 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转
+ router.options.routes[0].children.push(v);
+ // 最终路由进行升序
+ ascending(router.options.routes[0].children);
+ if (!router.hasRoute(v?.name)) router.addRoute(v);
+ const flattenRouters: any = router
+ .getRoutes()
+ .find(n => n.path === "/");
+ router.addRoute(flattenRouters);
+ }
+ }
+ );
+ usePermissionStoreHook().handleWholeMenus(routeList);
+ }
+ addPathMatch();
+}
+
+/** 初始化路由(`new Promise` 写法防止在异步请求中造成无限循环)*/
+function initRouter() {
+ if (getConfig()?.CachingAsyncRoutes) {
+ // 开启动态路由缓存本地sessionStorage
+ const key = "async-routes";
+ const asyncRouteList = storageSession().getItem(key) as any;
+ if (asyncRouteList && asyncRouteList?.length > 0) {
+ return new Promise(resolve => {
+ handleAsyncRoutes(asyncRouteList);
+ resolve(router);
+ });
+ } else {
+ return new Promise(resolve => {
+ getAsyncRoutes().then(({ data }) => {
+ handleAsyncRoutes(cloneDeep(data));
+ storageSession().setItem(key, data);
+ resolve(router);
+ });
+ });
+ }
+ } else {
+ return new Promise(resolve => {
+ getAsyncRoutes().then(({ data }) => {
+ handleAsyncRoutes(cloneDeep(data));
+ resolve(router);
+ });
+ });
+ }
+}
+
+/**
+ * 将多级嵌套路由处理成一维数组
+ * @param routesList 传入路由
+ * @returns 返回处理后的一维路由
+ */
+function formatFlatteningRoutes(routesList: RouteRecordRaw[]) {
+ if (routesList.length === 0) return routesList;
+ let hierarchyList = buildHierarchyTree(routesList);
+ for (let i = 0; i < hierarchyList.length; i++) {
+ if (hierarchyList[i].children) {
+ hierarchyList = hierarchyList
+ .slice(0, i + 1)
+ .concat(hierarchyList[i].children, hierarchyList.slice(i + 1));
+ }
+ }
+ return hierarchyList;
+}
+
+/**
+ * 一维数组处理成多级嵌套数组(三级及以上的路由全部拍成二级,keep-alive 只支持到二级缓存)
+ * https://github.com/pure-admin/vue-pure-admin/issues/67
+ * @param routesList 处理后的一维路由菜单数组
+ * @returns 返回将一维数组重新处理成规定路由的格式
+ */
+function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
+ if (routesList.length === 0) return routesList;
+ const newRoutesList: RouteRecordRaw[] = [];
+ routesList.forEach((v: RouteRecordRaw) => {
+ if (v.path === "/") {
+ newRoutesList.push({
+ component: v.component,
+ name: v.name,
+ path: v.path,
+ redirect: v.redirect,
+ meta: v.meta,
+ children: []
+ });
+ } else {
+ newRoutesList[0]?.children.push({ ...v });
+ }
+ });
+ return newRoutesList;
+}
+
+/** 处理缓存路由(添加、删除、刷新) */
+function handleAliveRoute({ name }: ToRouteType, mode?: string) {
+ switch (mode) {
+ case "add":
+ usePermissionStoreHook().cacheOperate({
+ mode: "add",
+ name
+ });
+ break;
+ case "delete":
+ usePermissionStoreHook().cacheOperate({
+ mode: "delete",
+ name
+ });
+ break;
+ case "refresh":
+ usePermissionStoreHook().cacheOperate({
+ mode: "refresh",
+ name
+ });
+ break;
+ default:
+ usePermissionStoreHook().cacheOperate({
+ mode: "delete",
+ name
+ });
+ useTimeoutFn(() => {
+ usePermissionStoreHook().cacheOperate({
+ mode: "add",
+ name
+ });
+ }, 100);
+ }
+}
+
+/** 过滤后端传来的动态路由 重新生成规范路由 */
+function addAsyncRoutes(arrRoutes: Array) {
+ if (!arrRoutes || !arrRoutes.length) return;
+ const modulesRoutesKeys = Object.keys(modulesRoutes);
+ arrRoutes.forEach((v: RouteRecordRaw) => {
+ // 将backstage属性加入meta,标识此路由为后端返回路由
+ v.meta.backstage = true;
+ // 父级的redirect属性取值:如果子级存在且父级的redirect属性不存在,默认取第一个子级的path;如果子级存在且父级的redirect属性存在,取存在的redirect属性,会覆盖默认值
+ if (v?.children && v.children.length && !v.redirect)
+ v.redirect = v.children[0].path;
+ // 父级的name属性取值:如果子级存在且父级的name属性不存在,默认取第一个子级的name;如果子级存在且父级的name属性存在,取存在的name属性,会覆盖默认值(注意:测试中发现父级的name不能和子级name重复,如果重复会造成重定向无效(跳转404),所以这里给父级的name起名的时候后面会自动加上`Parent`,避免重复)
+ if (v?.children && v.children.length && !v.name)
+ v.name = (v.children[0].name as string) + "Parent";
+ if (v.meta?.frameSrc) {
+ v.component = IFrame;
+ } else {
+ // 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会跟path保持一致)
+ const index = v?.component
+ ? modulesRoutesKeys.findIndex(ev => ev.includes(v.component as any))
+ : modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
+ v.component = modulesRoutes[modulesRoutesKeys[index]];
+ }
+ if (v?.children && v.children.length) {
+ addAsyncRoutes(v.children);
+ }
+ });
+ return arrRoutes;
+}
+
+/** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */
+function getHistoryMode(routerHistory): RouterHistory {
+ // len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
+ const historyMode = routerHistory.split(",");
+ const leftMode = historyMode[0];
+ const rightMode = historyMode[1];
+ // no param
+ if (historyMode.length === 1) {
+ if (leftMode === "hash") {
+ return createWebHashHistory("");
+ } else if (leftMode === "h5") {
+ return createWebHistory("");
+ }
+ } //has param
+ else if (historyMode.length === 2) {
+ if (leftMode === "hash") {
+ return createWebHashHistory(rightMode);
+ } else if (leftMode === "h5") {
+ return createWebHistory(rightMode);
+ }
+ }
+}
+
+/** 获取当前页面按钮级别的权限 */
+function getAuths(): Array {
+ return router.currentRoute.value.meta.auths as Array;
+}
+
+/** 是否有按钮级别的权限 */
+function hasAuth(value: string | Array): boolean {
+ if (!value) return false;
+ /** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */
+ const metaAuths = getAuths();
+ if (!metaAuths) return false;
+ const isAuths = isString(value)
+ ? metaAuths.includes(value)
+ : isIncludeAllChildren(value, metaAuths);
+ return isAuths ? true : false;
+}
+
+/** 获取所有菜单中的第一个菜单(顶级菜单)*/
+function getTopMenu(tag = false): menuType {
+ const topMenu = usePermissionStoreHook().wholeMenus[0]?.children[0];
+ tag && useMultiTagsStoreHook().handleTags("push", topMenu);
+ return topMenu;
+}
+
+export {
+ hasAuth,
+ getAuths,
+ ascending,
+ filterTree,
+ initRouter,
+ getTopMenu,
+ addPathMatch,
+ isOneOfArray,
+ getHistoryMode,
+ addAsyncRoutes,
+ getParentPaths,
+ findRouteByPath,
+ handleAliveRoute,
+ formatTwoStageRoutes,
+ formatFlatteningRoutes,
+ filterNoPermissionTree
+};
diff --git a/src/pages/dataScreen/views/computePowerAllocation/components/computePowerCube.vue b/src/pages/dataScreen/views/computePowerAllocation/components/computePowerCube.vue
new file mode 100644
index 0000000..ef7d867
--- /dev/null
+++ b/src/pages/dataScreen/views/computePowerAllocation/components/computePowerCube.vue
@@ -0,0 +1,205 @@
+
+
+
+
+
+
+
+
+ 可根据检测项重要性调整滑块以 配置各算法所占用的算力比例,
+ 达到目标运算需求
+
+
+
+
+
+ -
+
+
+
+
+
+ {{ v.proportion }}%{{ v.name }}
+
+
+
+
+
+
+ {{ v.proportion }}%{{ v.name }}
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/dataScreen/views/computePowerAllocation/components/computePowerType.vue b/src/pages/dataScreen/views/computePowerAllocation/components/computePowerType.vue
new file mode 100644
index 0000000..db635e1
--- /dev/null
+++ b/src/pages/dataScreen/views/computePowerAllocation/components/computePowerType.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
{{ currentInfo.name }}:
+
{{ currentInfo.proportion }}%
+
+ 预计处理效率: {{ currentInfo.pretreatmentEfficiency }}帧/秒
+
+
+
+
+
+
diff --git a/src/pages/dataScreen/views/computePowerAllocation/computePowerAllocation.scss b/src/pages/dataScreen/views/computePowerAllocation/computePowerAllocation.scss
new file mode 100644
index 0000000..95644df
--- /dev/null
+++ b/src/pages/dataScreen/views/computePowerAllocation/computePowerAllocation.scss
@@ -0,0 +1,81 @@
+.computePowerAllocation_wrap {
+ height: 100%;
+
+ // padding-top: calc(50vh - 330px - 48px);
+ .computePowerAllocation_body {
+ border-radius: 12px;
+ background: rgba(21, 77, 221, 0.05);
+ width: 1080px;
+ height: 660px;
+ margin: 0 auto;
+ }
+
+ .computePower_header {
+ text-align: center;
+ position: relative;
+ height: 46px;
+
+ & > span {
+ position: absolute;
+ line-height: 46px;
+ font-size: 24px;
+ font-weight: 700;
+ background: linear-gradient(180deg, #014be6 0%, #014be6 100%);
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+ }
+
+ .computePower_banner {
+ padding: 32px 32px 0;
+
+ .banner_left {
+ .banner_group {
+ & > li {
+ width: 266px;
+ height: 88px;
+ border-radius: 8px;
+ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.1);
+ position: relative;
+ overflow: hidden;
+
+ .bg_banner_group_logo {
+ width: 266px;
+ height: 166.25px;
+ top: -38px;
+ position: absolute;
+ left: 52px;
+ background: url("@/assets/computePower/computerTypeLogo.png")
+ no-repeat 0 0;
+ background-size: cover;
+ opacity: 0.05;
+ }
+ }
+ }
+ }
+
+ .bg_banner_center {
+ width: 360px;
+ height: 360px;
+ background: url("@/assets/computePower/banner.png");
+ background-repeat: no-repeat;
+ background-size: contain;
+ }
+
+ .banner_right {
+ .computePowerCube_wrap {
+ padding: 16px;
+ width: 266px;
+ height: 400px;
+ border-radius: 8px;
+ background-color: white;
+ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.1);
+ }
+ }
+ }
+
+ .computePower_footer {
+ padding: 10px;
+ }
+}
diff --git a/src/pages/dataScreen/views/computePowerAllocation/index.vue b/src/pages/dataScreen/views/computePowerAllocation/index.vue
new file mode 100644
index 0000000..0c52fc4
--- /dev/null
+++ b/src/pages/dataScreen/views/computePowerAllocation/index.vue
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
硬件组成
+
+ -
+
+
{{ v.label }}
+
+
+
+
+
{{ null }}
+
+
+
+
+
+
+
+
diff --git a/src/pages/dataScreen/views/computePowerAllocation/typing.ts b/src/pages/dataScreen/views/computePowerAllocation/typing.ts
new file mode 100644
index 0000000..93925d6
--- /dev/null
+++ b/src/pages/dataScreen/views/computePowerAllocation/typing.ts
@@ -0,0 +1,16 @@
+/*
+ * @Author: donghao donghao@supervision.ltd
+ * @Date: 2024-01-22 13:30:43
+ * @LastEditors: donghao donghao@supervision.ltd
+ * @LastEditTime: 2024-01-22 13:30:53
+ * @FilePath: \General-AI-Platform-Web-Client\src\views\computePowerAllocation\typing.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+export type ComputePowerPoolItem = {
+ name: string;
+ type: number; // 类别
+ color?: string; //
+ proportion: number;
+ pretreatmentEfficiency?: number;
+};
diff --git a/src/pages/dataScreen/views/home/homeIndex.scss b/src/pages/dataScreen/views/home/homeIndex.scss
new file mode 100644
index 0000000..3adcf42
--- /dev/null
+++ b/src/pages/dataScreen/views/home/homeIndex.scss
@@ -0,0 +1,5 @@
+.left_box_1 {
+ width: 430px;
+ height: 288px;
+ background: url("@/assets/svg/screenBgCommon.svg") no-repeat;
+}
diff --git a/src/pages/dataScreen/views/home/homeIndex.vue b/src/pages/dataScreen/views/home/homeIndex.vue
new file mode 100644
index 0000000..6dac01e
--- /dev/null
+++ b/src/pages/dataScreen/views/home/homeIndex.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/vite.config.ts b/vite.config.ts
index 8a7a336..4422085 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,3 +1,11 @@
+/*
+ * @Author: donghao donghao@supervision.ltd
+ * @Date: 2024-02-22 13:38:05
+ * @LastEditors: donghao donghao@supervision.ltd
+ * @LastEditTime: 2024-02-22 14:18:31
+ * @FilePath: \General-AI-Platform-Web-Client\vite.config.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
import dayjs from "dayjs";
import { resolve } from "path";
import pkg from "./package.json";
@@ -57,7 +65,8 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
chunkSizeWarningLimit: 4000,
rollupOptions: {
input: {
- index: pathResolve("index.html")
+ index: pathResolve("index.html"),
+ indexDataScreen: pathResolve("indexDataScreen.html")
},
// 静态资源分类打包
output: {