添加iot首页

This commit is contained in:
easy 2023-01-06 18:15:19 +08:00
parent cb8f393a17
commit c691f246fe
11 changed files with 800 additions and 30 deletions

31
components.d.ts vendored
View File

@ -7,37 +7,10 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb']
ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
AButton: typeof import('ant-design-vue/es')['Button']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
ACard: typeof import('ant-design-vue/es')['Card']
ACol: typeof import('ant-design-vue/es')['Col']
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
ADescriptions: typeof import('ant-design-vue/es')['Descriptions']
ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem']
ADivider: typeof import('ant-design-vue/es')['Divider']
ADrawer: typeof import('ant-design-vue/es')['Drawer']
AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem']
AInput: typeof import('ant-design-vue/es')['Input']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
AModal: typeof import('ant-design-vue/es')['Modal']
APopover: typeof import('ant-design-vue/es')['Popover']
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
ARow: typeof import('ant-design-vue/es')['Row']
ASelect: typeof import('ant-design-vue/es')['Select']
ASpace: typeof import('ant-design-vue/es')['Space']
ASpin: typeof import('ant-design-vue/es')['Spin']
AStep: typeof import('ant-design-vue/es')['Step']
ASteps: typeof import('ant-design-vue/es')['Steps']
ASwitch: typeof import('ant-design-vue/es')['Switch']
ATable: typeof import('ant-design-vue/es')['Table']
ATabPane: typeof import('ant-design-vue/es')['TabPane']
ATabs: typeof import('ant-design-vue/es')['Tabs']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
AUpload: typeof import('ant-design-vue/es')['Upload']
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
GeoComponent: typeof import('./src/components/GeoComponent/index.vue')['default']
MonacoEditor: typeof import('./src/components/MonacoEditor/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']

View File

@ -23,6 +23,10 @@ export default [
{
path: '/demo',
component: () => import('@/views/demo/index.vue')
}
},
{
path: '/iot/home',
component: () => import('@/views/iot/home/index.vue')
},
// end: 测试用, 可删除
]

View File

@ -0,0 +1,99 @@
<template>
<a-card class="device-count-container">
<template #title>
<h5 class="title">基础统计</h5>
</template>
<template #extra>
<span style="color: #1d39c4; cursor: pointer" @click="jumpPage"
>详情</span
>
</template>
<div class="box-list">
<div class="box-item">
<div class="label">CPU使用率</div>
<div class="value">{{ cpu }}</div>
<div class="chart" ref="cpuChart"></div>
</div>
<div class="box-item">
<div class="label">JVM内存</div>
<div class="value">{{ jvm }}</div>
<div class="chart" ref="jvmChart"></div>
</div>
</div>
</a-card>
</template>
<script setup lang="ts">
const cpu = ref('0%');
const jvm = ref('10%');
const jumpPage = () => {};
</script>
<style lang="less" scoped>
.device-count-container {
:deep(.ant-card-body) {
padding-top: 0;
}
.title {
position: relative;
z-index: 2;
display: flex;
justify-content: space-between;
margin-bottom: 12px;
padding-left: 18px;
font-weight: 700;
font-size: 18px;
&::before {
position: absolute;
top: 50%;
left: 0;
width: 8px;
height: 8px;
background-color: #1d39c4;
border: 1px solid #b4c0da;
transform: translateY(-50%);
content: ' ';
}
}
.box-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 24px;
gap: 24px;
.box-item {
position: relative;
padding: 16px;
background: linear-gradient(
135.62deg,
#f6f7fd 22.27%,
hsla(0, 0%, 100%, 0.86) 91.82%
);
border-radius: 2px;
box-shadow: 0 4px 18px #efefef;
.label {
color: #4f4f4f;
}
.value {
margin: 20px 0;
color: rgba(0, 0, 0, 0.85);
font-weight: 700;
font-size: 20px;
}
.chart {
position: absolute;
right: 10%;
bottom: 0;
width: 90px;
height: 90px;
}
}
}
}
</style>

View File

@ -0,0 +1,126 @@
<template>
<a-card class="boot-card-container" :bordered="false">
<template #title>
<h5 class="title">{{ cardTitle }}</h5>
</template>
<div class="box">
<div
class="box-item"
v-for="(item,index) in cardData"
@click="jumpPage(item)"
>
<div class="item-english">{{ item.english }}</div>
<div class="item-title">{{ item.label }}</div>
<img class="item-image" :src="`/images/home/${index + 1}.png`" alt="" />
</div>
</div>
</a-card>
</template>
<script setup lang="ts">
import { message } from 'ant-design-vue';
type configItem = {
auth: boolean;
link: string;
english: string;
label: string;
save?: boolean;
};
const router = useRouter();
const props = defineProps({
cardData: Array<configItem>,
cardTitle: String,
});
const { cardData, cardTitle } = toRefs(props);
const jumpPage = (linkItem: configItem): void => {
if (linkItem.auth && linkItem.link) {
router.push(`${linkItem.link}${linkItem.save ? '?save=true' : ''}`);
} else {
message.warning('暂无权限,请联系管理员');
}
};
</script>
<style lang="less" scoped>
.boot-card-container {
:deep(.ant-card-body) {
padding-top: 0;
}
.title {
position: relative;
z-index: 2;
display: flex;
justify-content: space-between;
margin-bottom: 12px;
padding-left: 18px;
font-weight: 700;
font-size: 18px;
&::before {
position: absolute;
top: 50%;
left: 0;
width: 8px;
height: 8px;
background-color: #1d39c4;
border: 1px solid #b4c0da;
transform: translateY(-50%);
content: ' ';
}
}
.box {
display: grid;
grid-column-gap: 56px;
grid-template-columns: repeat(3, 1fr);
.box-item {
cursor: pointer;
position: relative;
padding: 16px;
background: linear-gradient(
135.62deg,
#f6f7fd 22.27%,
rgba(255, 255, 255, 0.86) 91.82%
);
border-radius: 2px;
box-shadow: 0 4px 18px #efefef;
.item-english {
color: #4f4f4f;
}
.item-title {
margin: 20px 0;
color: @text-color;
font-weight: 700;
font-size: 20px;
}
.item-image {
position: absolute;
right: 10%;
bottom: 0;
}
&::after {
position: absolute;
top: 50%;
right: -60px;
width: 60px;
height: 40px;
transform: translateY(-50%);
content: ' ';
}
&:nth-child(1) {
&::after {
background: url('/images/home/arrow-2.png') no-repeat center;
}
}
&:nth-child(2) {
&::after {
background: url('/images/home/arrow-2.png') no-repeat center;
}
}
}
}
}
</style>

View File

@ -0,0 +1,105 @@
<template>
<div class="device-home-container">
<a-row :gutter="10">
<a-col :span="14">
<BootCard :cardData="bootConfig" cardTitle="运维引导" />
</a-col>
<a-col :span="10">
<BasicCountCard />
</a-col>
</a-row>
<a-row>
<PlatformPicCard />
</a-row>
<a-row>
<StepCard
cardTitle="运维管理推荐步骤"
tooltip="请根据业务需要对下述步骤进行选择性操作。"
:dataList="stepDetails"
/>
</a-row>
</div>
</template>
<script setup lang="ts" name="devOpsHome">
import BootCard from '../BootCard.vue';
import BasicCountCard from '../BasicCountCard.vue';
import PlatformPicCard from '../PlatformPicCard.vue';
import StepCard from '../StepCard.vue';
// import {getImage} from '@/utils/comm'
// -
const bootConfig = [
{
english: 'STEP1',
label: '设备接入配置',
link: '/a',
auth: true,
save: true,
},
{
english: 'STEP2',
label: '日志排查',
link: '/b',
auth: true,
save: true,
},
{
english: 'STEP3',
label: '实时监控',
link: '/c',
auth: false,
save: true,
},
];
// -
const stepDetails = [
{
title: '创建产品',
details:
'产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。',
iconUrl: '/images/home/bottom-4.png',
linkUrl: '/a',
auth: true,
},
{
title: '配置产品接入方式',
details:
'通过产品对同一类型的设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。',
iconUrl: '/images/home/bottom-1.png',
linkUrl: '/a',
auth: true,
},
{
title: '添加测试设备',
details: '添加单个设备,用于验证产品模型是否配置正确。',
iconUrl: '/images/home/bottom-5.png',
linkUrl: '/a',
auth: true,
},
{
title: '功能调试',
details:
'对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
iconUrl: '/images/home/bottom-2.png',
linkUrl: '/a',
auth: true,
},
{
title: '批量添加设备',
details: '批量添加同一产品下的设备',
iconUrl: '/images/home/bottom-3.png',
linkUrl: '/a',
auth: false,
},
];
</script>
<style lang="less" scoped>
.device-home-container {
.ant-row {
margin-bottom: 24px;
}
}
</style>

View File

@ -0,0 +1,99 @@
<template>
<a-card class="device-count-container">
<template #title>
<h5 class="title">设备统计</h5>
</template>
<template #extra>
<span style="color: #1d39c4; cursor: pointer" @click="jumpPage"
>详情</span
>
</template>
<div class="box-list">
<div class="box-item">
<div class="label">产品数量</div>
<div class="value">{{ projectNum }}</div>
<img src="/images/home/top-1.png" alt="" />
</div>
<div class="box-item">
<div class="label">设备数量</div>
<div class="value">{{ deviceNum }}</div>
<img src="/images/home/top-2.png" alt="" />
</div>
</div>
</a-card>
</template>
<script setup lang="ts">
const projectNum = ref(0);
const deviceNum = ref(0);
const jumpPage = () => {};
</script>
<style lang="less" scoped>
.device-count-container {
:deep(.ant-card-body) {
padding-top: 0;
}
.title {
position: relative;
z-index: 2;
display: flex;
justify-content: space-between;
margin-bottom: 12px;
padding-left: 18px;
font-weight: 700;
font-size: 18px;
&::before {
position: absolute;
top: 50%;
left: 0;
width: 8px;
height: 8px;
background-color: #1d39c4;
border: 1px solid #b4c0da;
transform: translateY(-50%);
content: ' ';
}
}
.box-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 24px;
gap: 24px;
.box-item {
position: relative;
padding: 16px;
background: linear-gradient(
135.62deg,
#f6f7fd 22.27%,
hsla(0, 0%, 100%, 0.86) 91.82%
);
border-radius: 2px;
box-shadow: 0 4px 18px #efefef;
.label {
color: #4f4f4f;
}
.value {
margin: 20px 0;
color: rgba(0, 0, 0, 0.85);
font-weight: 700;
font-size: 20px;
}
img {
position: absolute;
right: 10%;
bottom: 0;
width: 90px;
height: 90px;
}
}
}
}
</style>

View File

@ -0,0 +1,103 @@
<template>
<div class="device-home-container">
<a-row :gutter="10">
<a-col :span="14">
<BootCard :cardData="bootConfig" cardTitle="物联网引导" />
</a-col>
<a-col :span="10">
<DeviceCountCard />
</a-col>
</a-row>
<a-row>
<PlatformPicCard />
</a-row>
<a-row>
<StepCard
cardTitle="设备接入推荐步骤"
tooltip="不同的设备因为通信协议的不同,存在接入步骤的差异"
:dataList="stepDetails"
/>
</a-row>
</div>
</template>
<script setup lang="ts" name="deviceHome">
import BootCard from '../BootCard.vue';
import DeviceCountCard from '../DeviceCountCard.vue';
import PlatformPicCard from '../PlatformPicCard.vue';
import StepCard from '../StepCard.vue';
// import {getImage} from '@/utils/comm'
// -
const bootConfig = [
{
english: 'STEP1',
label: '创建产品',
link: '/a',
auth: true,
save: true,
},
{
english: 'STEP2',
label: '创建设备',
link: '/b',
auth: true,
save: true,
},
{
english: 'STEP3',
label: '规则引擎',
link: '/c',
auth: false,
save: true,
},
];
// -
const stepDetails = [
{
title: '协议管理',
details:
'根据业务需求自定义开发对应的产品(设备模型)接入协议,并上传到平台。',
iconUrl: '/images/home/bottom-1.png',
linkUrl: '/a',
auth: true,
},
{
title: '证书管理',
details: '统一维护平台内的证书,用于数据通信加密。',
iconUrl: '/images/home/bottom-6.png',
linkUrl: '/a',
auth: true,
},
{
title: '网络组件',
details: '根据不同的传输类型配置平台底层网络组件相关参数。',
iconUrl: '/images/home/bottom-3.png',
linkUrl: '/a',
auth: true,
},
{
title: '设备接入网关',
details: '根据不同的传输类型,关联消息协议,配置设备接入网关相关参数。',
iconUrl: '/images/home/bottom-4.png',
linkUrl: '/a',
auth: true,
},
{
title: '日志管理',
details: '监控系统日志,及时处理系统异常。',
iconUrl: '/images/home/bottom-5.png',
linkUrl: '/a',
auth: false,
},
];
</script>
<style lang="less" scoped>
.device-home-container {
.ant-row {
margin-bottom: 24px;
}
}
</style>

View File

@ -0,0 +1,30 @@
<template>
<div class="init-home-container">
<div class="title">请选择首页视图</div>
</div>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped>
.init-home-container {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: calc(100vh - 150px);
padding: 20px;
background-color: white;
.title {
margin-top: 28px;
margin-bottom: 48px;
font-weight: 400;
font-size: 26px;
text-align: center;
}
}
</style>

View File

@ -0,0 +1,61 @@
<template>
<a-card class="platform-pic-container">
<div class="title">
<span>平台架构图</span>
<p>PLATFORM ARCHITECTURE DIAGRAM</p>
</div>
<img src="/images/home/content.png" class="bj" alt="" />
</a-card>
</template>
<script setup lang="ts"></script>
<style lang="less" scoped>
:deep(.ant-card-body) {
padding: 0;
}
.platform-pic-container {
position: relative;
width: 100%;
overflow: hidden;
min-height: 480px;
.bj {
display: block;
width: 100%;
}
.title {
position: absolute;
width: 100%;
z-index: 2;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-bottom: 12px;
padding: 24px 40px;
font-weight: 700;
font-size: 18px;
p{
position: absolute;
top: 50px;
width: 100%;
opacity: .3;
font-size: 12px;
}
&::before {
position: absolute;
top: 50%;
left: 24px;
width: 8px;
height: 8px;
background-color: #1d39c4;
border: 1px solid #b4c0da;
transform: translateY(-50%);
content: ' ';
}
}
}
</style>

View File

@ -0,0 +1,127 @@
<template>
<a-card class="step-container">
<h5 class="title">
<span style="margin-right: 12px">{{ cardTitle }}</span>
<a-tooltip placement="top">
<template #title>
<span>{{ tooltip }}</span>
</template>
<question-circle-outlined style="padding-top: 5px" />
</a-tooltip>
</h5>
<div class="box-list">
<div
class="list-item"
v-for="item in dataList"
@click="jumpPage(item)"
>
<div class="box-top">
<span class="top-title">{{ item.title }}</span>
<img :src="item.iconUrl" alt="" />
</div>
<div class="box-details">{{ item.details }}</div>
</div>
</div>
</a-card>
</template>
<script setup lang="ts">
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
interface listConfig {
title: string;
details: string;
iconUrl: string;
linkUrl: string;
params?: object;
auth: boolean;
}
const props = defineProps({
cardTitle: String,
tooltip: String,
dataList: Array<listConfig>,
});
const router = useRouter();
const { cardTitle, tooltip, dataList } = toRefs(props);
const jumpPage = (row: listConfig) => {
if (row.auth && row.linkUrl) {
router.push(`${row.linkUrl}${row.params ? '?save=true' : ''}`);
} else {
message.warning('暂无权限,请联系管理员');
}
};
</script>
<style lang="less" scoped>
.step-container {
width: 100%;
.title {
position: relative;
z-index: 2;
display: flex;
margin-bottom: 12px;
padding-left: 18px;
font-weight: 700;
font-size: 18px;
&::before {
position: absolute;
top: 50%;
left: 0;
width: 8px;
height: 8px;
background-color: #1d39c4;
border: 1px solid #b4c0da;
transform: translateY(-50%);
content: ' ';
}
}
.box-list {
grid-template-columns: repeat(5, 1fr);
display: grid;
grid-column-gap: 66px;
.list-item {
position: relative;
.box-top {
position: relative;
padding: 16px 24px;
background-color: #f8f9fd;
color: #333;
font-weight: 700;
font-size: 14px;
cursor: pointer;
img {
position: absolute;
top: 0;
right: 0;
z-index: 1;
height: 100%;
}
}
.box-details {
padding: 24px;
border: 1px solid #e5edf4;
border-top: none;
}
&:not(:last-child)::after {
position: absolute;
top: 50%;
right: -60px;
width: 60px;
height: 40px;
transform: translateY(-50%);
content: ' ';
background: url(/images/home/arrow-1.png) no-repeat 50%;
}
}
}
}
</style>

View File

@ -0,0 +1,43 @@
<template>
<div class="container">
<div class="header"></div>
<div class="left"></div>
<div class="content iot-home-container">
<!-- <InitHome /> -->
<DeviceHome />
</div>
</div>
</template>
<script lang="ts" setup>
import InitHome from './components/InitHome/index.vue';
import DeviceHome from './components/DeviceHome/index.vue';
</script>
<style lang="less" scoped>
.container {
background: #f0f2f5;
width: 100vw;
min-height: 100vh;
display: flex;
flex-wrap: wrap;
.header {
height: 48px;
background: #7e1c1c;
width: 100vw;
}
.left {
background: #352d85;
width: 210px;
min-height: calc(100vh - 48px);
}
.content {
margin: 24px;
width: calc(100vw - 258px);
min-height: calc(100vh - 96px);
}
}
// .iot-home-container {
// }
</style>