Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
b02f7f5add
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<div style="width: 100%; height: 400px">
|
||||
<el-amap
|
||||
>
|
||||
</el-amap>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { initAMapApiLoader } from '@vuemap/vue-amap';
|
||||
import '@vuemap/vue-amap/dist/style.css';
|
||||
initAMapApiLoader({
|
||||
// key: '95fa72137f4263f8e64ae01f766ad09c',
|
||||
key: 'a0415acfc35af15f10221bfa5a6850b4',
|
||||
securityJsCode: 'cae6108ec3dd222f946d1a7237c78be0',
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
</style>
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div class="home-title">
|
||||
<div v-if="title">{{ title }}</div>
|
||||
<div v-else>
|
||||
<slot name="title"></slot>
|
||||
</div>
|
||||
<div class="extra-text">
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
<div class="home-title-english">{{ english }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Guide">
|
||||
interface guideProps {
|
||||
title?: string;
|
||||
english?: string;
|
||||
}
|
||||
|
||||
const props = defineProps<guideProps>();
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.home-title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
padding-left: 18px;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: @primary-color;
|
||||
border: 1px solid #b4c0da;
|
||||
transform: translateY(-50%);
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
.extra-text {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.home-title-english {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,112 @@
|
|||
<template>
|
||||
<div class="chart" ref="chart"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
|
||||
const props = defineProps({
|
||||
// 图表数据
|
||||
x: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
y: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
maxY:{
|
||||
type:Number,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 绘制图表
|
||||
*/
|
||||
const createChart = () => {
|
||||
nextTick(() => {
|
||||
const myChart = echarts.init(proxy.$refs.chart);
|
||||
const options = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: props.x,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: '{b0}<br />{a0}: {c0}',
|
||||
// formatter: '{b0}<br />{a0}: {c0}<br />{a1}: {c1}%'
|
||||
},
|
||||
grid: {
|
||||
top: '2%',
|
||||
bottom: '5%',
|
||||
left: props.maxY > 100000 ? '90px' : '50px',
|
||||
right: '50px',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '消息量',
|
||||
data: props.y,
|
||||
type: 'bar',
|
||||
// type: 'line',
|
||||
// smooth: true,
|
||||
color: '#597EF7',
|
||||
barWidth: '30%',
|
||||
// areaStyle: {
|
||||
// color: {
|
||||
// type: 'linear',
|
||||
// x: 0,
|
||||
// y: 0,
|
||||
// x2: 0,
|
||||
// y2: 1,
|
||||
// colorStops: [
|
||||
// {
|
||||
// offset: 0,
|
||||
// color: '#685DEB', // 100% 处的颜色
|
||||
// },
|
||||
// {
|
||||
// offset: 1,
|
||||
// color: '#FFFFFF', // 0% 处的颜色
|
||||
// },
|
||||
// ],
|
||||
// global: false, // 缺省为 false
|
||||
// },
|
||||
// },
|
||||
},
|
||||
{
|
||||
name: '占比',
|
||||
data: props.y,
|
||||
// data: percentageY,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbolSize: 0, // 拐点大小
|
||||
color: '#96ECE3',
|
||||
},
|
||||
],
|
||||
}
|
||||
myChart.setOption(options);
|
||||
window.addEventListener('resize', function () {
|
||||
myChart.resize();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.y,
|
||||
() => createChart(),
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-radio-group
|
||||
v-if="quickBtn"
|
||||
default-value="today"
|
||||
button-style="solid"
|
||||
v-model:value="radioValue"
|
||||
@change="(e) => handleBtnChange(e.target.value)"
|
||||
>
|
||||
<a-radio-button
|
||||
v-for="item in quickBtnList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
<a-range-picker
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
valueFormat="YYYY-MM-DD HH:mm:ss"
|
||||
style="margin-left: 12px"
|
||||
@change="rangeChange"
|
||||
v-model:value="rangeVal"
|
||||
:allowClear="false"
|
||||
>
|
||||
</a-range-picker>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import moment from 'moment';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
interface BtnOptions {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface EmitProps {
|
||||
(e: 'change', data: Record<string, any>): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<EmitProps>();
|
||||
|
||||
const props = defineProps({
|
||||
// 显示快捷按钮
|
||||
quickBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 快捷按钮列表
|
||||
quickBtnList: {
|
||||
type: Array as PropType<BtnOptions[]>,
|
||||
default: [
|
||||
{ label: '今日', value: 'today' },
|
||||
{ label: '近一周', value: 'week' },
|
||||
{ label: '近一月', value: 'month' },
|
||||
{ label: '近一年', value: 'year' },
|
||||
],
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'today',
|
||||
},
|
||||
});
|
||||
|
||||
const radioValue = ref(props.type || 'week' || undefined);
|
||||
const rangeVal = ref<[string, string]>();
|
||||
|
||||
const rangeChange = (val: any) => {
|
||||
radioValue.value = undefined;
|
||||
emit('change', {
|
||||
start: moment(val[0]).valueOf(),
|
||||
end: moment(val[1]).valueOf(),
|
||||
type: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
const getTimeByType = (type: string) => {
|
||||
switch (type) {
|
||||
case 'hour':
|
||||
return moment().subtract(1, 'hours').valueOf();
|
||||
case 'week':
|
||||
return moment().subtract(6, 'days').valueOf();
|
||||
case 'month':
|
||||
return moment().subtract(29, 'days').valueOf();
|
||||
case 'year':
|
||||
return moment().subtract(365, 'days').valueOf();
|
||||
default:
|
||||
return moment().startOf('day').valueOf();
|
||||
}
|
||||
};
|
||||
|
||||
const handleBtnChange = (val: string) => {
|
||||
radioValue.value = val;
|
||||
let endTime = moment(new Date()).valueOf();
|
||||
let startTime = getTimeByType(val);
|
||||
if (val === 'yesterday') {
|
||||
startTime = moment().subtract(1, 'days').startOf('day').valueOf();
|
||||
endTime = moment().subtract(1, 'days').endOf('day').valueOf();
|
||||
}
|
||||
rangeVal.value = [
|
||||
moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
moment(endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
];
|
||||
emit('change', {
|
||||
start: startTime,
|
||||
end: endTime,
|
||||
type: val,
|
||||
});
|
||||
};
|
||||
handleBtnChange(radioValue.value);
|
||||
watch(
|
||||
() => radioValue.value,
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
</script>
|
|
@ -24,7 +24,10 @@
|
|||
:footer="onlineFooter"
|
||||
:value="onlineToday"
|
||||
>
|
||||
<BarChart :chartXData="barChartXData" :chartYData="barChartYData"></BarChart> </TopCard
|
||||
<BarChart
|
||||
:chartXData="barChartXData"
|
||||
:chartYData="barChartYData"
|
||||
></BarChart> </TopCard
|
||||
></a-col>
|
||||
<a-col :span="6"
|
||||
><TopCard
|
||||
|
@ -32,15 +35,50 @@
|
|||
:footer="messageFooter"
|
||||
:value="dayMessage"
|
||||
>
|
||||
<LineChart :chartXData="lineChartXData" :chartYData="lineChartYData"></LineChart> </TopCard
|
||||
<LineChart
|
||||
:chartXData="lineChartXData"
|
||||
:chartYData="lineChartYData"
|
||||
></LineChart> </TopCard
|
||||
></a-col>
|
||||
</a-row>
|
||||
<a-row :span="24">
|
||||
<a-col :span="24">
|
||||
<div class="message-card">
|
||||
<Guide title="设备消息">
|
||||
<template #extra>
|
||||
<TimeSelect
|
||||
key="flow-static"
|
||||
:type="'week'"
|
||||
:quickBtnList="quickBtnList"
|
||||
@change="getEcharts"
|
||||
/>
|
||||
</template>
|
||||
</Guide>
|
||||
<div class="message-chart">
|
||||
<MessageChart :x="messageChartXData" :y="messageChartYData" :maxY="messageMaxChartYData"></MessageChart>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :span="24">
|
||||
<a-col :span="24">
|
||||
<div class="device-position">
|
||||
<Guide title="设备分布"></Guide>
|
||||
<div class="device-map">
|
||||
<Amap></Amap>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import BarChart from './components/BarChart.vue';
|
||||
import LineChart from './components/LineChart.vue';
|
||||
import TimeSelect from './components/TimeSelect.vue';
|
||||
import Guide from './components/Guide.vue';
|
||||
import MessageChart from './components/messageChart.vue';
|
||||
import {
|
||||
productCount,
|
||||
deviceCount,
|
||||
|
@ -51,6 +89,7 @@ import encodeQuery from '@/utils/encodeQuery';
|
|||
import { getImage } from '@/utils/comm';
|
||||
import type { Footer } from '@/views/device/DashBoard/typings';
|
||||
import TopCard from '@/views/device/DashBoard/components/TopCard.vue';
|
||||
import Amap from './components/Amap.vue'
|
||||
let productTotal = ref(0);
|
||||
let productFooter = ref<Footer[]>([
|
||||
{
|
||||
|
@ -95,6 +134,15 @@ let lineChartYData = ref<any[]>([]);
|
|||
let lineChartXData = ref<any[]>([]);
|
||||
let barChartXData = ref<any[]>([]);
|
||||
let barChartYData = ref<any[]>([]);
|
||||
let messageChartXData = ref<any[]>([]);
|
||||
let messageChartYData = ref<any[]>([]);
|
||||
let messageMaxChartYData = ref<number>();
|
||||
const quickBtnList = [
|
||||
{ label: '昨日', value: 'yesterday' },
|
||||
{ label: '近一周', value: 'week' },
|
||||
{ label: '近一月', value: 'month' },
|
||||
{ label: '近一年', value: 'year' },
|
||||
];
|
||||
const getProductData = () => {
|
||||
productCount().then((res) => {
|
||||
if (res.status == 200) {
|
||||
|
@ -224,7 +272,7 @@ const getDevice = () => {
|
|||
const oneDay = res.result.find(
|
||||
(item: any) => item.group === 'oneday',
|
||||
)?.data.value;
|
||||
dayMessage = oneDay;
|
||||
dayMessage.value = oneDay;
|
||||
messageFooter.value[0].value = thisMonth;
|
||||
const today = res.result.filter(
|
||||
(item: any) => item.group === 'today',
|
||||
|
@ -237,6 +285,68 @@ const getDevice = () => {
|
|||
});
|
||||
};
|
||||
getDevice();
|
||||
const getEcharts = (data: any) => {
|
||||
let _time = '1h';
|
||||
let format = 'HH';
|
||||
let limit = 12;
|
||||
const dt = data.end - data.start;
|
||||
const hour = 60 * 60 * 1000;
|
||||
const days = hour * 24;
|
||||
const months = days * 30;
|
||||
const year = 365 * days;
|
||||
if (dt <= days) {
|
||||
limit = Math.abs(Math.ceil(dt / hour));
|
||||
} else if (dt > days && dt < year) {
|
||||
limit = Math.abs(Math.ceil(dt / days)) + 1;
|
||||
_time = '1d';
|
||||
format = 'M月dd日';
|
||||
} else if (dt >= year) {
|
||||
limit = Math.abs(Math.floor(dt / months));
|
||||
_time = '1M';
|
||||
format = 'yyyy年-M月';
|
||||
}
|
||||
dashboard([
|
||||
{
|
||||
dashboard: 'device',
|
||||
object: 'message',
|
||||
measurement: 'quantity',
|
||||
dimension: 'agg',
|
||||
group: 'device_msg',
|
||||
params: {
|
||||
time: _time,
|
||||
format: format,
|
||||
limit: limit,
|
||||
from: data.start,
|
||||
to: data.end,
|
||||
},
|
||||
},
|
||||
]).then((res:any) => {
|
||||
if (res.status === 200) {
|
||||
messageChartXData.value = res.result
|
||||
.map((item: any) =>
|
||||
_time === '1h'
|
||||
? `${item.data.timeString}时`
|
||||
: item.data.timeString,
|
||||
)
|
||||
.reverse();
|
||||
messageChartYData.value = res.result.map((item: any) => item.data.value).reverse();
|
||||
messageMaxChartYData.value = Math.max.apply(null, messageChartYData.value.length ? messageChartYData.value : [0]);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.message-card,.device-position{
|
||||
margin-top: 24px;
|
||||
padding: 24px;
|
||||
background-color: white;
|
||||
}
|
||||
.message-chart {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
.amap-box{
|
||||
height: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue