Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
easy 2023-02-13 18:11:46 +08:00
commit b02f7f5add
5 changed files with 421 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,10 @@
:footer="onlineFooter" :footer="onlineFooter"
:value="onlineToday" :value="onlineToday"
> >
<BarChart :chartXData="barChartXData" :chartYData="barChartYData"></BarChart> </TopCard <BarChart
:chartXData="barChartXData"
:chartYData="barChartYData"
></BarChart> </TopCard
></a-col> ></a-col>
<a-col :span="6" <a-col :span="6"
><TopCard ><TopCard
@ -32,15 +35,50 @@
:footer="messageFooter" :footer="messageFooter"
:value="dayMessage" :value="dayMessage"
> >
<LineChart :chartXData="lineChartXData" :chartYData="lineChartYData"></LineChart> </TopCard <LineChart
:chartXData="lineChartXData"
:chartYData="lineChartYData"
></LineChart> </TopCard
></a-col> ></a-col>
</a-row> </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> </div>
</page-container> </page-container>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import BarChart from './components/BarChart.vue'; import BarChart from './components/BarChart.vue';
import LineChart from './components/LineChart.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 { import {
productCount, productCount,
deviceCount, deviceCount,
@ -51,6 +89,7 @@ import encodeQuery from '@/utils/encodeQuery';
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import type { Footer } from '@/views/device/DashBoard/typings'; import type { Footer } from '@/views/device/DashBoard/typings';
import TopCard from '@/views/device/DashBoard/components/TopCard.vue'; import TopCard from '@/views/device/DashBoard/components/TopCard.vue';
import Amap from './components/Amap.vue'
let productTotal = ref(0); let productTotal = ref(0);
let productFooter = ref<Footer[]>([ let productFooter = ref<Footer[]>([
{ {
@ -95,6 +134,15 @@ let lineChartYData = ref<any[]>([]);
let lineChartXData = ref<any[]>([]); let lineChartXData = ref<any[]>([]);
let barChartXData = ref<any[]>([]); let barChartXData = ref<any[]>([]);
let barChartYData = 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 = () => { const getProductData = () => {
productCount().then((res) => { productCount().then((res) => {
if (res.status == 200) { if (res.status == 200) {
@ -224,19 +272,81 @@ const getDevice = () => {
const oneDay = res.result.find( const oneDay = res.result.find(
(item: any) => item.group === 'oneday', (item: any) => item.group === 'oneday',
)?.data.value; )?.data.value;
dayMessage = oneDay; dayMessage.value = oneDay;
messageFooter.value[0].value = thisMonth; messageFooter.value[0].value = thisMonth;
const today = res.result.filter( const today = res.result.filter(
(item: any) => item.group === 'today', (item: any) => item.group === 'today',
); );
const x = today.map((item: any) => item.data.timeString).reverse(); const x = today.map((item: any) => item.data.timeString).reverse();
const y = today.map((item: any) => item.data.value).reverse(); const y = today.map((item: any) => item.data.value).reverse();
lineChartXData.value = x ; lineChartXData.value = x;
lineChartYData.value = y ; lineChartYData.value = y;
} }
}); });
}; };
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> </script>
<style lang="less" scoped> <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> </style>