修改minicard 和 侧壁案例

This commit is contained in:
niyyzf 2024-07-23 12:51:10 +08:00
parent 5148df4491
commit 4277e5cc92
10 changed files with 191 additions and 148 deletions

View File

@ -11,7 +11,7 @@ $tertiary-color: #c4c744;
$bg-color-1: #3B8AFF;
$bg-color-2: #0051B5;
$unfocused-color: #8A92A6;
$unfocused-color: #a4abbc;
$light-text-color: #04040B;
$light-unfocused-color: $unfocused-color;

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import {useDataStore} from "~/strores/DataStore";
import {useMainLayoutStore} from "~/strores/UseMainLayoutStore";
import { MoveDown,CircleAlert,CircleX } from 'lucide-vue-next';
const props = defineProps({
title: {
type: String,
@ -32,14 +32,15 @@ const dataStore = useDataStore()
const mainStore = useMainLayoutStore()
const status=ref<string>("success")
//dataStore
const value=ref<number>(Number(values.value[0]))
dataStore.$subscribe((_, state) => {
//d3YT#cpuUserUsagekeyvalue
const watcher = Array.isArray(props.watcher) ? [...props.watcher] : [props.watcher];
// 使mapstate.datawatcherKey
values.value = watcher.map(watcherKey => state.data[watcherKey as string]);
//50warning 80error
const value=Number(values.value[0])
status.value = value > 80 ? "error" : value > 50 ? "warning" : "success";
value.value=Number(values.value[0])
status.value = value.value > 80 ? "error" : value.value > 50 ? "warning" : "success";
})
</script>
@ -48,26 +49,23 @@ dataStore.$subscribe((_, state) => {
<n-popover trigger="hover" placement="bottom">
<template #trigger>
<div class="mini-card-box">
<!-- <svg fill="none" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">-->
<!-- <circle cx="36" cy="36" r="32" stroke="#E9ECEF" stroke-width="2"/>-->
<!-- <circle :id="title" :stroke-dasharray="circumference" cx="36"-->
<!-- cy="36" r="32" stroke="red"-->
<!-- stroke-linecap="round"-->
<!-- stroke-width="4" transform="rotate(-90, 36, 36)"/>-->
<!-- <path-->
<!-- :id="title+'Arrow'"-->
<!-- d="M26.1904 44.784C25.8209 45.1944 25.854 45.8267 26.2645 46.1963C26.6749 46.5658 27.3072 46.5327 27.6767 46.1223L26.1904 44.784ZM43.7763 27.8042C43.7474 27.2527 43.2768 26.829 42.7253 26.8579L33.7376 27.3289C33.1861 27.3578 32.7624 27.8284 32.7913 28.3799C32.8202 28.9314 33.2908 29.3551 33.8423 29.3262L41.8313 28.9075L42.25 36.8965C42.2789 37.4481 42.7495 37.8717 43.301 37.8428C43.8525 37.8139 44.2762 37.3434 44.2473 36.7919L43.7763 27.8042ZM27.6767 46.1223L43.5208 28.5257L42.0345 27.1874L26.1904 44.784L27.6767 46.1223Z"-->
<!-- fill="#ADB5BD"/>-->
<!-- </svg>-->
<n-progress type="dashboard" processing gap-position="bottom" :status="status" :percentage="
<n-progress type="circle" processing :status="status" :percentage="
values[0]
" style="width: 4.427vw; max-width:120px;min-width: 80px" />
" style="width: 68px;">
<MoveDown class="arrow" :style="{transform:`rotate(${(100-value)/100*360*-1}deg)`}" v-if="status==='success'"/>
<div class="warning" v-if="status==='warning'">
<CircleAlert />
</div>
<div class="error" v-if="status==='error'">
<CircleX />
</div>
</n-progress>
<div class="text">
<h3>
{{ title }}
</h3>
<h2>
<span>{{ Number(values[0]??0).toFixed(2)}}{{ unit }}</span>
{{ Number(values[0]??0).toFixed(2)}}{{ unit }}
</h2>
</div>
</div>
@ -86,10 +84,12 @@ dataStore.$subscribe((_, state) => {
display: flex;
align-items: center;
background: $light-bg-color;
padding: $padding*1.5;
padding: 20px 24px ;
border-radius: $radius;
box-shadow: 0 10px 30px 0 rgba(17, 38, 146, 0.05);
gap: $gap*3;
gap: 25px;
border: $border;
*{
@include SC_Font()
@ -104,28 +104,76 @@ dataStore.$subscribe((_, state) => {
}
}
.arrow{
width: 30px;
height: 30px;
stroke: $light-unfocused-color;
//
transform-origin: center;
//
transition: transform 0.3s ease-in-out;
}
.text {
display: flex;
flex-direction: column;
gap: $gap;
justify-content: center;
h2, h3 {
color: $light-unfocused-color;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 175%; /* 28px */
//
text-wrap: nowrap;
font-size: 16px;
font-weight: 400;
}
h2 {
font-size: 25px;
font-weight: 800;
font-size: 19px;
font-weight: 600;
color: $light-text-color;
.dark-mode & {
color: $dark-text-color;
}
}
}
.warning,.error{
display: flex;
place-items: center;
place-content: center;
width: 70px;
height: 70px;
border-radius: 50%;
animation: background-fade-error 0.5s infinite ease-in-out;
svg{
width: 40px;
height: 40px;
stroke: #fff;
}
}
.warning{
animation: background-fade 1s infinite ease-in-out;
}
@keyframes background-fade {
0% {
background-color: rgba(255, 255, 0, 0.3); /* 黄色,完全透明 */
}
50% {
background-color: rgba(255, 255, 0, 1); /* 黄色,完全不透明 */
}
100% {
background-color: rgba(255, 255, 0, 0.3); /* 黄色,完全透明 */
}
}
@keyframes background-fade-error {
0% {
background-color: rgba(255, 0, 0, 0.3); /* 红色,完全透明 */
}
50% {
background-color: rgba(255, 0, 0, 1); /* 红色,完全不透明 */
box-shadow: 0 0 10px rgba(255, 0, 0, 0.5); /* 红色阴影 */
}
100% {
background-color: rgba(255, 0, 0, 0.3); /* 红色,完全透明 */
}
}
</style>

View File

@ -11,6 +11,7 @@ import {useLoadStore} from "~/strores/LoadStore";
import type {HttpType} from "~/types/baseType";
import type {UserInfoType} from "~/types/UserType";
import {deepEqual} from "~/utils";
import { Atom ,LineChart,Combine} from 'lucide-vue-next';
const layout = ref<IGridItem[]>([])
const presetLayouts = ref<Layouts>(<Layouts>{})
@ -28,22 +29,6 @@ onMounted(() => {
})
const visible = ref(false)
const cardVisible = ref(false)
const items = ref([
{
label: '添加图表',
icon: 'pi pi-chart-line',
command: () => {
visible.value = true
}
},
{
label: '添加信息卡片',
icon: 'pi pi-plus',
command: () => {
cardVisible.value = true
}
},
])
const layoutChangedEvent = _.throttle((newLayout: IGridItem[]) => {
let breakPoint = 'lg';
@ -103,40 +88,38 @@ watchDebounced(
</script>
<template>
<div class="main-grid-layout">
<SpeedDial :model="items" class="speed-button" :tooltipOptions="{ position: 'left' }" />
<Dialog
v-model:visible="visible"
:pt="{
root: {
style:'border:unset;background-color:unset;'
},
mask: {
style: 'backdrop-filter: blur(20px)'
}
}"
modal
>
<template #container="{ closeCallback }">
<SettingCard :close-callback="closeCallback"/>
</template>
</Dialog>
<Dialog
v-model:visible="cardVisible"
:pt="{
root: {
style:'border:unset;background-color:unset;'
},
mask: {
style: 'backdrop-filter: blur(20px)'
}
}"
modal
>
<template #container="{ closeCallback }">
<AddCard :close-callback="closeCallback"/>
<n-float-button :right="42" :bottom="90" position="fixed" style="z-index: 200" type="primary" menu-trigger="hover">
<n-icon>
<Atom/>
</n-icon>
<template #menu>
<n-float-button @click=" visible = true" >
<n-icon>
<LineChart/>
</n-icon>
</n-float-button>
<n-float-button @click="cardVisible = true">
<n-icon>
<Combine />
</n-icon>
</n-float-button>
</template>
</Dialog>
</n-float-button >
<n-modal
v-model:show="visible"
preset="card"
style="width: 500px"
>
<SettingCard />
</n-modal>
<n-modal
v-model:show="cardVisible"
preset="card"
style="width: 500px"
>
<AddCard/>
</n-modal>
<GridLayout
ref="el"
v-model:layout="layout"

View File

@ -119,7 +119,7 @@ onMounted(() => {
'Authorization': 'Bearer ' + useCookie('token').value
},
params: {
serverId: mainLayoutStore.SelectServer.id
serverId: mainLayoutStore.SelectServer.value
},
baseURL: useRuntimeConfig().public.baseUrl
}).then(res => {
@ -175,15 +175,12 @@ onMounted(() => {
<div v-if="select===1" class="step2-box">
<div v-for="(chart,index) in ServerSelect" class="Select-box">
<p>数据槽 {{ index + 1 }}</p>
<Dropdown v-model="ServerSelect[index]" :highlightOnSelect="false" :options="ServerValues" :pt="{
root:{
style:'background:#eee'
}
}"
checkmark optionLabel="dataName" placeholder="选择一个数据来源"/>
<n-select v-model:value="ServerSelect[index]" :options="ServerValues.map(serverId=>{})" />
<Icon :size="20" :stroke-width="0.7" name="X" @click="removeServerValue(index)"/>
</div>
<Button icon="pi pi-plus" label="添加一个数据槽" @click="addServerValue"/>
<n-button type="info" @click="addServerValue">
添加一个数据槽
</n-button>
</div>
<div v-if="select===2" class="step3-box">
<div v-for="setting in CardSettings" class="setting-box">

View File

@ -1,9 +0,0 @@
<script lang="ts" setup>
</script>
<template>
13123
</template>
<style lang="scss" scoped></style>

View File

@ -25,7 +25,6 @@ onMounted(()=>{
const mainLayoutStore = useMainLayoutStore()
const connection = new HubConnectionBuilder()
.withUrl(`${useRuntimeConfig().public.baseUrl}/TerminalHub`,{

View File

@ -2,7 +2,7 @@
import {useMainLayoutStore} from "~/strores/UseMainLayoutStore";
import {type MenuOption, NIcon} from "naive-ui";
import { LayoutGrid,Computer,Cpu,Cctv,UserRoundCog,Goal,Settings,LogOut,PanelLeftClose,PanelLeftOpen } from 'lucide-vue-next';
import { LayoutGrid,Computer,Cpu,Cctv,UserRoundCog,Goal,Settings,LogOut,PanelLeftClose,PanelLeftOpen,SquareTerminal } from 'lucide-vue-next';
import type {Component} from "vue";
import { useLoadingBar } from 'naive-ui'
const {$gsap} = useNuxtApp()
@ -25,6 +25,11 @@ const menuOptions: MenuOption[] = [
key: 'host',
icon:renderIcon(Computer),
children: [
{
label: '终端',
key: 'terminal',
icon:renderIcon(SquareTerminal)
},
{
label: 'CPU',
key: 'host/cpu',
@ -58,6 +63,9 @@ const menuOptions: MenuOption[] = [
icon:renderIcon(LogOut)
}
]
defineExpose({
menuOptions
})
watch(()=>activeKey.value,async (newValue)=>{
loadingBar.start()
await navigateTo("/"+newValue)

View File

@ -1,6 +1,5 @@
<script lang="ts" setup>
import {useMainLayoutStore} from "~/strores/UseMainLayoutStore";
import SidebarRight from "~/components/SidebarRight.vue";
import {Signature} from 'lucide-vue-next';
const router = useRouter()
@ -10,13 +9,37 @@ const routes=computed(()=>{
return x.charAt(0).toUpperCase() + x.slice(1)
})
})
const visibleRight = ref(false)
type menuType = {
[key: string]: string;
}
const menus: menuType = {
'Home': '概览',
'Host': '主机',
'Cpu': '处理器',
'Network': '网络',
'ServerUser': '用户监测',
'Id': '用户',
'User': '账号管理',
'InspectionRecords': '巡检记录',
'Settings': '设置'
}
const getMenuInfo = (key: string) => {
return menus[key] ?? key
}
const value = ref()
const options = ref(['@gmail.com', '@outlook.com', '@yahoo.com'])
</script>
<template>
<section class="title-bar-layout">
<n-breadcrumb separator=">">
<n-breadcrumb-item v-for="i in routes" :key="i">
{{ getMenuInfo(i) }}
</n-breadcrumb-item>
</n-breadcrumb>
<div>
</div>
<n-auto-complete
v-model:value="value"
:input-props="{
@ -26,10 +49,6 @@ const options = ref(['@gmail.com', '@outlook.com', '@yahoo.com'])
placeholder="搜索"
clearable
/>
<div class="action">
<Icon name="BellRing" @click="visibleRight=true" :stroke-width="1.5" :size="20"/>
<Icon name="Mail" :stroke-width="1.5" :size="20"/>
</div>
<div class="user">
<UserMini/>
</div>
@ -50,6 +69,7 @@ const options = ref(['@gmail.com', '@outlook.com', '@yahoo.com'])
border-bottom: $border;
gap: $gap*2;
@include SC_Font();
.dark-mode & {
background: $dark-bg-color;
border-bottom: $border-dark;
@ -70,42 +90,4 @@ const options = ref(['@gmail.com', '@outlook.com', '@yahoo.com'])
}
}
.name {
font-weight: 700;
font-size: 26px;
color: $light-text-color;
.dark-mode & {
color: $dark-text-color;
}
}
.user {
grid-column: 4;
}
.action {
display: flex;
gap: $gap*2;
grid-column: 3;
svg{
cursor: pointer;
stroke: rgba(51, 51, 51, 0.5);
&:hover{
stroke: $light-text-color;
}
.dark-mode &{
stroke: rgba(255, 255, 255, 0.5);
&:hover{
stroke: $dark-text-color;
}
}
}
}
:deep(.p-breadcrumb){
background: unset;
.p-menuitem-text{
color: #D3D3D3;
}
}
</style>

View File

@ -201,7 +201,7 @@ onKeyStroke('Shift', (e) => {
<Term/>
</n-modal>
<div class="main-box">
<n-back-top :right="50" style="z-index: 200"/>
<n-back-top :right="40" style="z-index: 200"/>
<div ref="mainRef" class="main">
<div class="hero">
<div class="hero-box">

35
web/pages/terminal.vue Normal file
View File

@ -0,0 +1,35 @@
<script setup lang="ts">
definePageMeta({
layout: 'main',
middleware: ['auth']
})
const panels=ref<(string|number)[]>([1, 2, 3, 4, 5])
const name=ref<string|number>(1)
const handleAdd=()=>{
//
panels.value.push(panels.value.length+1)
}
const handleClose=(name: number|string)=>{
panels.value=panels.value.filter((item)=>item!=name)
}
</script>
<template>
<div class="terminal-layout">
<Term/>
</div>
</template>
<style scoped lang="scss">
@import "base";
.terminal-layout{
width: 100%;
height: 100%;
border-radius: $radius;
border: $border;
}
</style>