feat: 初始化数据大屏模块

dev-deviceSetting
donghao 1 year ago
parent feb64ea1de
commit 130131aea4

@ -1,3 +1,4 @@
shamefully-hoist=true
strict-peer-dependencies=false
shell-emulator=true
shell-emulator=true
registry=https://registry.npmmirror.com/

@ -0,0 +1,3 @@
{
"type": "module"
}

@ -0,0 +1,101 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-01-12 14:35:28
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-02-22 14:16:05
* @FilePath: \General-AI-Platform-Web-Client\index.html
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
/>
<title>pure-admin-thin</title>
<link rel="icon" href="/favicon.ico" />
<!-- update 2024-01-21 16:41 -->
<link
rel="stylesheet"
href="//at.alicdn.com/t/c/font_4412653_g3j0o4m1zi.css"
/>
<script src="https://threejs.org/build/three.js"></script>
<script>
window.process = {};
</script>
</head>
<body>
<div id="app">
<style>
html,
body,
#app {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow: hidden;
}
.loader,
.loader::before,
.loader::after {
width: 2.5em;
height: 2.5em;
border-radius: 50%;
animation: load-animation 1.8s infinite ease-in-out;
animation-fill-mode: both;
}
.loader {
position: relative;
top: 0;
margin: 80px auto;
font-size: 10px;
color: #406eeb;
text-indent: -9999em;
transform: translateZ(0);
transform: translate(-50%, 0);
animation-delay: -0.16s;
}
.loader::before,
.loader::after {
position: absolute;
top: 0;
content: "";
}
.loader::before {
left: -3.5em;
animation-delay: -0.32s;
}
.loader::after {
left: 3.5em;
}
@keyframes load-animation {
0%,
80%,
100% {
box-shadow: 0 2.5em 0 -1.3em;
}
40% {
box-shadow: 0 2.5em 0 0;
}
}
</style>
<div class="loader"></div>
</div>
<script type="module" src="/src/pages/dataScreen/main.ts"></script>
</body>
</html>

@ -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",

@ -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:
{

@ -0,0 +1,51 @@
<svg width="433" height="292" viewBox="0 0 433 292" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_iii_8677_100860)">
<path d="M2 274.13V3H416.766L432 21.6778V274.13L418.24 291H15.76L2 274.13Z" fill="#00183E"
fill-opacity="0.9" />
</g>
<path d="M2 274.13V3H416.766L432 21.6778V274.13L418.24 291H15.76L2 274.13Z" stroke="#20A0FD"
stroke-opacity="0.4" stroke-width="1.5" />
<path d="M413 12.88L415.88 10L425 19.12L422.12 22L413 12.88Z" fill="white" />
<path d="M378 3H416.86L432 20.6882V50" stroke="#2ABAF0" stroke-width="2" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M405.528 48H421.554L418.027 51H402L405.528 48Z"
fill="url(#paint0_linear_8677_100860)" />
<path d="M2 37V2H74" stroke="#2CCAFF" stroke-width="3" />
<defs>
<filter id="filter0_iii_8677_100860" x="1.25" y="-1.75" width="431.5" height="297.5"
filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feColorMatrix in="SourceAlpha" type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feOffset dy="-4" />
<feGaussianBlur stdDeviation="4" />
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1" />
<feColorMatrix type="matrix"
values="0 0 0 0 0.109804 0 0 0 0 0.498039 0 0 0 0 0.94902 0 0 0 0.6 0" />
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_8677_100860" />
<feColorMatrix in="SourceAlpha" type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feOffset dy="-4" />
<feGaussianBlur stdDeviation="4" />
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1" />
<feColorMatrix type="matrix"
values="0 0 0 0 0.109804 0 0 0 0 0.498039 0 0 0 0 0.94902 0 0 0 0.6 0" />
<feBlend mode="normal" in2="effect1_innerShadow_8677_100860"
result="effect2_innerShadow_8677_100860" />
<feColorMatrix in="SourceAlpha" type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feOffset dy="4" />
<feGaussianBlur stdDeviation="4" />
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1" />
<feColorMatrix type="matrix"
values="0 0 0 0 0.109804 0 0 0 0 0.498039 0 0 0 0 0.94902 0 0 0 0.6 0" />
<feBlend mode="normal" in2="effect2_innerShadow_8677_100860"
result="effect3_innerShadow_8677_100860" />
</filter>
<linearGradient id="paint0_linear_8677_100860" x1="411.777" y1="48" x2="411.777" y2="51"
gradientUnits="userSpaceOnUse">
<stop stop-color="#FEF643" />
<stop offset="1" stop-color="#FFA217" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -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({
<p> {vFirst.name}</p>
</div>
</div>
<div class="box" v-show={vFirst?.expended}>
{Array.isArray(vFirst.childList) &&
vFirst.childList.length && (
<ul class="child_info_box">
{vFirst.childList.map((item, index) => {
return (
<li key={index}>
<CollapseTreeItem
info={item}
></CollapseTreeItem>
</li>
);
})}
</ul>
)}
</div>
<Transition
enterActiveClass={styles.topToBottom_enter_active}
leaveActiveClass={styles.topToBottom_leave_active}
>
<div class="box" v-show={vFirst?.expended}>
{Array.isArray(vFirst.childList) &&
vFirst.childList.length && (
<ul class="child_info_box" key="123">
{vFirst.childList.map((item, index) => {
return (
<li key={index}>
<CollapseTreeItem
info={item}
></CollapseTreeItem>
</li>
);
})}
</ul>
)}
</div>
</Transition>
</li>
);
})}

@ -0,0 +1,158 @@
<script setup lang="ts">
import "animate.css";
// src/components/ReIcon/src/offlineIcon.ts 使addIcon
import "@/components/ReIcon/src/offlineIcon";
import { setType } from "./types";
import { useResizeObserver } from "@vueuse/core";
import { useAppStoreHook } from "@/store/modules/app";
import { useSettingStoreHook } from "@/store/modules/settings";
import { deviceDetection, useGlobal } from "@pureadmin/utils";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import { ref, reactive, computed, onMounted, onBeforeMount } from "vue";
import { useRoute } from "vue-router";
const route = useRoute();
const appWrapperRef = ref();
const isMobile = deviceDetection();
const pureSetting = useSettingStoreHook();
const { $storage } = useGlobal<GlobalPropertiesApi>();
const set: setType = reactive({
sidebar: computed(() => {
return useAppStoreHook().sidebar;
}),
device: computed(() => {
return useAppStoreHook().device;
}),
fixedHeader: computed(() => {
return pureSetting.fixedHeader;
}),
classes: computed(() => {
return {
hideSidebar: !set.sidebar.opened,
openSidebar: set.sidebar.opened,
withoutAnimation: set.sidebar.withoutAnimation,
mobile: set.device === "mobile"
};
}),
hideTabs: computed(() => {
return $storage?.configure.hideTabs;
})
});
function setTheme(layoutModel: string) {
window.document.body.setAttribute("layout", layoutModel);
$storage.layout = {
layout: `${layoutModel}`,
theme: $storage.layout?.theme,
darkMode: $storage.layout?.darkMode,
sidebarStatus: $storage.layout?.sidebarStatus,
epThemeColor: $storage.layout?.epThemeColor
};
}
function toggle(device: string, bool: boolean) {
useAppStoreHook().toggleDevice(device);
useAppStoreHook().toggleSideBar(bool, "resize");
}
//
let isAutoCloseSidebar = true;
useResizeObserver(appWrapperRef, entries => {
if (isMobile) return;
const entry = entries[0];
const { width } = entry.contentRect;
width <= 760 ? setTheme("vertical") : setTheme(useAppStoreHook().layout);
/** width app-wrapper
* 0 < width <= 760 隐藏侧边栏
* 760 < width <= 990 折叠侧边栏
* width > 990 展开侧边栏
*/
if (width > 0 && width <= 760) {
toggle("mobile", false);
isAutoCloseSidebar = true;
} else if (width > 760 && width <= 990) {
if (isAutoCloseSidebar) {
toggle("desktop", false);
isAutoCloseSidebar = false;
}
} else if (width > 990 && !set.sidebar.isClickCollapse) {
toggle("desktop", true);
isAutoCloseSidebar = true;
} else {
toggle("desktop", false);
isAutoCloseSidebar = false;
}
});
onMounted(() => {
if (isMobile) {
toggle("mobile", false);
}
});
onBeforeMount(() => {
useDataThemeChange().dataThemeChange();
});
</script>
<template>
<div ref="appWrapperRef" :class="[set.classes, route?.meta?.bodyClass]">
<dv-full-screen-container>
<div class="w-full ds_app_box">
<header>顶部</header>
<!-- 主体内容 -->
<router-view />
<footer>底部</footer>
</div></dv-full-screen-container
>
</div>
</template>
<style lang="scss">
.app-wrapper {
position: relative;
width: 100%;
height: 100%;
padding: 24px;
&::after {
display: table;
clear: both;
content: "";
}
&.mobile.openSidebar {
position: fixed;
top: 0;
}
}
.app-mask {
position: absolute;
top: 0;
z-index: 999;
width: 100%;
height: 100%;
background: #000;
opacity: 0.3;
}
.re-screen {
margin-top: 12px;
}
// .computePowerAllocation_page {
// background: url("@/assets/computePower/bg.png");
// background-repeat: no-repeat;
// .navbar {
// background-color: transparent !important;
// }
// }
</style>

@ -0,0 +1,34 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-02-22 14:04:50
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-02-22 14:21:13
* @FilePath: \General-AI-Platform-Web-Client\src\pages\dataScreen\App.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<el-config-provider :locale="currentLocale">
<router-view />
<ReDialog />
</el-config-provider>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/lib/locale/lang/zh-cn";
import en from "element-plus/lib/locale/lang/en";
import { ReDialog } from "@/components/ReDialog";
export default defineComponent({
name: "app",
components: {
[ElConfigProvider.name]: ElConfigProvider,
ReDialog
},
computed: {
currentLocale() {
return this.$storage.locale?.locale === "zh" ? zhCn : en;
}
}
});
</script>

@ -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");
});

@ -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<string, any> = 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<RouteRecordRaw> = formatTwoStageRoutes(
formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
);
/** 用于渲染菜单,保持原始层级 */
export const constantMenus: Array<RouteComponent> = 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<DataInfo<number>>(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;

@ -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;

@ -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<RouteConfigsTable>;

@ -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;

@ -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<string>, b: Array<string>) {
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<DataInfo<number>>(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<RouteRecordRaw>) {
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<string> {
return router.currentRoute.value.meta.auths as Array<string>;
}
/** 是否有按钮级别的权限 */
function hasAuth(value: string | Array<string>): 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
};

@ -0,0 +1,205 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-01-19 09:38:33
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-01-23 14:51:05
* @FilePath: \General-AI-Platform-Web-Client\src\views\demo\cube.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script setup lang="ts">
import { computed } from "vue";
import { ComputePowerPoolItem } from "../typing";
defineOptions({
name: "ComputePowerCube"
});
// TODO
const props = defineProps({
list: {
type: Array as ComputePowerPoolItem[],
defaultValue: {}
}
});
const listData = computed(() => {
let finalList = [];
const totalHeight = 262;
finalList = props.list.map(item => {
item.value = Math.floor((item.proportion / 100) * totalHeight);
return item;
});
return finalList.reverse();
});
const fetchPathValByHeight = val => {
return val - 12;
};
</script>
<template>
<div class="computePowerCube_wrap">
<div class="flex justify-start des_wrap pb-[16px]">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M12 22C14.7614 22 17.2614 20.8807 19.0711 19.0711C20.8807 17.2614 22 14.7614 22 12C22 9.2386 20.8807 6.7386 19.0711 4.92893C17.2614 3.11929 14.7614 2 12 2C9.2386 2 6.7386 3.11929 4.92893 4.92893C3.11929 6.7386 2 9.2386 2 12C2 14.7614 3.11929 17.2614 4.92893 19.0711C6.7386 20.8807 9.2386 22 12 22Z"
stroke="#FAAD14"
stroke-width="2"
stroke-linejoin="round"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 18.5C12.6904 18.5 13.25 17.9404 13.25 17.25C13.25 16.5597 12.6904 16 12 16C11.3097 16 10.75 16.5597 10.75 17.25C10.75 17.9404 11.3097 18.5 12 18.5Z"
fill="#FAAD14"
/>
<path
d="M12 6V14"
stroke="#FAAD14"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<p class="pf-1 w-[196px] pl-[8px]">
可根据检测项重要性调整滑块以 配置各算法所占用的算力比例
达到目标运算需求
</p>
</div>
<div class="cube_body">
<ul class="cube_info">
<li
v-for="(v, k) in listData"
:key="k"
class="flex items-center justify-center"
>
<div
class="bg_cube"
:style="{ height: v.value + 'px', background: v.bgColor }"
>
<div class="flex items-center cube_tag" v-if="v.type === 2">
<div class="bg-black h-[2px] w-[34px]"></div>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="16"
viewBox="0 0 20 16"
fill="none"
>
<path
d="M18 1C18.8284 1 19.5 1.67157 19.5 2.5V13.5C19.5 14.3284 18.8284 15 18 15H7.74624C7.32544 15 6.924 14.8233 6.63986 14.5129L1.60508 9.01334C1.08028 8.4401 1.08025 7.56095 1.605 6.98766L6.63987 1.4872C6.92402 1.17678 7.32549 1 7.74633 1H18Z"
fill="#FFBD5C"
stroke="#FA8916"
/>
</svg>
<p class="pf-1 w-[140px] h-[20px] single-line">
<span class="px-[8px]">{{ v.proportion }}%</span
><span>{{ v.name }}</span>
</p>
</div>
<div class="flex items-center cube_tag" v-if="v.type === 1">
<div class="bg-black h-[2px] w-[34px]"></div>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="16"
viewBox="0 0 20 16"
fill="none"
>
<path
d="M18 1C18.8284 1 19.5 1.67157 19.5 2.5V13.5C19.5 14.3284 18.8284 15 18 15H7.74624C7.32544 15 6.924 14.8233 6.63986 14.5129L1.60508 9.01334C1.08028 8.4401 1.08025 7.56095 1.605 6.98766L6.63987 1.4872C6.92402 1.17678 7.32549 1 7.74633 1H18Z"
fill="#74A5FB"
stroke="#3778F2"
/>
</svg>
<p class="pf-1 w-[140px] h-[20px] single-line">
<span class="px-[8px]">{{ v.proportion }}%</span
><span>{{ v.name }}</span>
</p>
</div>
</div>
</li>
<li>
<div class="cube_info_line"></div>
</li>
</ul>
</div>
<div class="bg_body_logo"></div>
</div>
</template>
<style lang="scss">
.computePowerCube_wrap {
position: relative;
overflow: hidden;
.bg_body_logo {
/* width: 358.886px;
height: 343.026px;
bottom: 0;
position: absolute;
left: -48px;
background: url("@/assets/computePower/bgLogo.svg") no-repeat 50% 50%;
opacity: 0.02; */
}
.cube_info {
position: relative;
margin: 0 auto;
width: 42px;
border-radius: 4px;
border: 4px solid rgba(21, 77, 221, 0.4);
background: #fff;
& > li {
.bg_cube {
width: 100%;
position: relative;
}
.cube_tag {
position: absolute;
left: 0px;
top: -8px;
width: 200px;
}
}
}
.cube_info_line {
position: absolute;
left: 12px;
top: 13px;
width: 6px;
height: 233px;
background: rgba(255, 255, 255, 0.8);
filter: blur(4px);
}
.cube_body {
margin-left: 34px;
width: 45px;
border: 2px solid #b8b8b8;
border-radius: 6px;
.cube_btn {
position: relative;
width: 40px;
margin: 0 auto;
& > li {
width: 20px;
height: 20px;
position: absolute;
left: 40px;
top: 69px;
}
& > li:nth-child(2) {
top: 159px;
}
}
}
}
</style>

@ -0,0 +1,104 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-01-19 09:38:33
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-01-23 11:37:11
* @FilePath: \General-AI-Platform-Web-Client\src\views\demo\cube.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script setup lang="ts">
import { computed } from "vue";
import { ComputePowerPoolItem } from "../typing";
defineOptions({
name: "ComputePowerType"
});
const props = defineProps({
info: {
type: Object as ComputePowerPoolItem,
defaultValue: {}
}
});
const currentInfo = computed(() => props.info);
</script>
<template>
<div class="flex items-center computePowerType_wrap">
<svg
style="padding-top: 4px"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="28"
viewBox="0 0 24 28"
fill="none"
>
<path
d="M12 10.9102L24 17.4556L12 24.0011L0 17.4556L12 10.9102Z"
:fill="currentInfo.color"
/>
<path
d="M24 6.54545L12 0V10.9091L24 17.4545V6.54545Z"
:fill="currentInfo.color"
/>
<path
d="M12 0L0 6.54546V17.4545L12 10.9091V0Z"
:fill="currentInfo.color"
/>
<path
d="M12 13.0923L24 6.54688V17.456L12 24.0014V13.0923Z"
:fill="currentInfo.color"
/>
<path
d="M0 6.54688L12 13.0923V24.0014L0 17.456V6.54688Z"
:fill="currentInfo.color"
/>
<rect
width="13.669"
height="13.669"
transform="matrix(0.877895 0.478853 -0.877895 0.478853 12 0)"
:fill="currentInfo.color"
/>
<g filter="url(#filter0_f_7277_81321)">
<rect x="10.8452" y="12" width="1.1547" height="12" fill="white" />
</g>
<defs>
<filter
id="filter0_f_7277_81321"
x="6.84521"
y="8"
width="9.15479"
height="20"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="2"
result="effect1_foregroundBlur_7277_81321"
/>
</filter>
</defs>
</svg>
<div class="flex">
<p class="pl-[8px] pf-1">{{ currentInfo.name }}</p>
<p class="pf-1">{{ currentInfo.proportion }}%</p>
<p class="pl-[8px] pf-2" v-if="currentInfo.pretreatmentEfficiency">
预计处理效率: {{ currentInfo.pretreatmentEfficiency }}/
</p>
</div>
</div>
</template>
<style lang="scss">
.computePowerType_wrap {
position: relative;
}
</style>

@ -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;
}
}

@ -0,0 +1,146 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-01-19 09:22:30
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-02-22 14:26:57
* @FilePath: \General-AI-Platform-Web-Client\src\views\computePowerAllocation\index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script setup lang="ts">
import { ref } from "vue"; //api/json
import ComputePowerCube from "./components/computePowerCube.vue";
import ComputePowerType from "./components/computePowerType.vue";
import { ComputePowerPoolItem } from "./typing";
defineOptions({
name: "ComputePowerAllocation"
});
const groupList = ref<Record<string, any>[]>([
{
label: "NVIDIA GeForce 4090"
},
{
label: "NVIDIA GeForce 4090"
},
{
label: "NVIDIA GeForce 4090"
},
{
label: "NVIDIA GeForce 4090"
}
]); //dom;
const poolsData: ComputePowerPoolItem[] = [
{
name: "离岗监控",
type: 1,
proportion: 34,
pretreatmentEfficiency: 20,
color: "#3879FE",
bgColor: "linear-gradient(180deg, #015DE6 0%, #4881F6 100%)"
},
{
name: "玩手机监控",
type: 2,
proportion: 46,
pretreatmentEfficiency: 15,
color: "#FAA90B",
bgColor: "linear-gradient(180deg, #FA8316 0%, #FAAD16 100%)"
},
{
name: "空闲算力",
type: 0,
proportion: 20,
pretreatmentEfficiency: 0,
color: "#DCDCDC",
bgColor: "linear-gradient(90deg, #7B7979 0%, #C1C1C1 100%)"
}
];
</script>
<template>
<div
class="flex items-center justify-center main_booy_container computePowerAllocation_wrap"
>
<div class="computePowerAllocation_body">
<div class="flex justify-center computePower_header">
<svg
xmlns="http://www.w3.org/2000/svg"
width="667"
height="50"
viewBox="0 0 667 50"
fill="none"
>
<path
d="M4 1.25H2.39949L3.42366 2.47992L21.2082 23.8375C25.9105 29.4845 32.879 32.75 40.2275 32.75H169.682C173.92 32.75 178.078 33.9086 181.706 36.1005L192.922 42.8776C199.28 46.7193 206.567 48.75 213.996 48.75H316.342C317.146 48.75 317.909 48.3989 318.431 47.7889L320.231 45.6869L319.662 45.199L320.231 45.6869C320.469 45.4096 320.816 45.25 321.181 45.25H346.911C347.242 45.25 347.56 45.3818 347.795 45.6165L350.121 47.9438C350.636 48.46 351.336 48.75 352.066 48.75H443.362C450.566 48.75 457.641 46.8403 463.867 43.2154L476.417 35.9077C479.969 33.8396 484.006 32.75 488.116 32.75H630.515C638.594 32.75 646.165 28.8065 650.796 22.186L664.615 2.42988L665.44 1.25H664H4Z"
fill="url(#paint0_linear_7277_81243)"
stroke="white"
stroke-width="1.5"
/>
<defs>
<linearGradient
id="paint0_linear_7277_81243"
x1="334"
y1="10"
x2="334"
y2="48"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#154DDD" stop-opacity="0.15" />
<stop offset="1" stop-color="#154DDD" stop-opacity="0" />
</linearGradient>
</defs>
</svg>
<span>苏胜天算力资源池</span>
</div>
<div class="flex items-center justify-between computePower_banner">
<div class="banner_left">
<p class="hf-1">硬件组成</p>
<ul class="banner_group">
<li
v-for="(v, k) in groupList"
:key="k"
class="flex items-center bg-white p-[16px] mt-[16px]"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M8.948 8.7975V7.3675C9.08911 7.35702 9.23051 7.35102 9.372 7.3495C13.294 7.2255 15.865 10.7235 15.865 10.7235C15.865 10.7235 13.091 14.5745 10.115 14.5745C9.72158 14.5753 9.33059 14.5128 8.957 14.3895V10.0435C10.485 10.2285 10.794 10.9005 11.704 12.4285L13.744 10.7145C13.744 10.7145 12.252 8.7625 9.744 8.7625C9.47818 8.75656 9.21227 8.76826 8.948 8.7975ZM8.948 4.0625V6.2005L9.372 6.1735C14.822 5.9885 18.382 10.6435 18.382 10.6435C18.382 10.6435 14.302 15.6075 10.052 15.6075C9.68491 15.6063 9.31858 15.5739 8.957 15.5105V16.8355C9.257 16.8705 9.567 16.8975 9.867 16.8975C13.824 16.8975 16.687 14.8745 19.46 12.4895C19.919 12.8605 21.8 13.7525 22.19 14.1415C19.557 16.3495 13.418 18.1255 9.937 18.1255C9.602 18.1255 9.284 18.1075 8.966 18.0725V19.9365H24V4.0625H8.948ZM8.948 14.3885V15.5195C5.291 14.8655 4.275 11.0595 4.275 11.0595C4.275 11.0595 6.033 9.1155 8.948 8.7975V10.0345H8.94C7.412 9.8485 6.21 11.2795 6.21 11.2795C6.21 11.2795 6.89 13.6915 8.949 14.3895M2.456 10.8995C2.456 10.8995 4.62 7.7025 8.956 7.3665V6.2005C4.153 6.5895 0 10.6525 0 10.6525C0 10.6525 2.35 17.4545 8.948 18.0725V16.8355C4.108 16.2355 2.456 10.8995 2.456 10.8995Z"
fill="#76B900"
/>
</svg>
<p class="pf-2 pl-[12px]">{{ v.label }}</p>
<div class="bg_banner_group_logo">&nbsp;</div>
</li>
</ul>
</div>
<div class="bg_banner_center">{{ null }}</div>
<div class="banner_right">
<p class="hf-1 pb-[16px]">算力配置</p>
<ComputePowerCube :list="poolsData" />
</div>
</div>
<ul
class="flex items-center justify-center computePower_footer mt-[70px]"
>
<li
v-for="(v, k) in poolsData"
:key="k"
class="flex items-center justify-center mx-[20px]"
>
<ComputePowerType :info="v" />
</li>
</ul>
</div>
</div>
</template>
<style lang="scss">
@import "./computePowerAllocation.scss";
</style>

@ -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;
};

@ -0,0 +1,5 @@
.left_box_1 {
width: 430px;
height: 288px;
background: url("@/assets/svg/screenBgCommon.svg") no-repeat;
}

@ -0,0 +1,26 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-02-22 14:27:21
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-02-22 15:43:48
* @FilePath: \General-AI-Platform-Web-Client\src\pages\dataScreen\views\home\homeIndex.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<div class="flex justify-between w-full ds_home_wrap">
<div class="left">
<div class="left_box_1">123</div>
<div class="left_box_1">123</div>
<div class="left_box_1">123</div>
</div>
<div class="right">
<div class="left_box_1">123</div>
<div class="left_box_1">123</div>
<div class="left_box_1">123</div>
</div>
</div>
</template>
<style lang="scss">
@import "./homeIndex.scss";
</style>

@ -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: {

Loading…
Cancel
Save