123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 |
- <!-- 物联网卡-详情 -->
- <template>
- <div>
- <el-card shadow="nover">
- <el-descriptions class="margin-top" title="基本信息" :column="3" :size="size" border>
- <!-- 卡号 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <user />
- </el-icon>
- 卡号
- </div>
- </template>
- {{sim.accNumber}}
- </el-descriptions-item>
- <!-- ICCID -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <iphone />
- </el-icon>
- ICCID
- </div>
- </template>
- {{sim.iccid}}
- </el-descriptions-item>
- <!-- 绑定设备 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <iphone />
- </el-icon>
- 绑定设备
- </div>
- </template>
- {{sim.bindDeviceName}}
- </el-descriptions-item>
- <!-- 平台类型 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <iphone />
- </el-icon>
- 平台类型
- </div>
- </template>
- {{sim.platTypes}}
- </el-descriptions-item>
- <!-- 平台名称 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <location />
- </el-icon>
- 平台名称
- </div>
- </template>
- {{sim.platName}}
- </el-descriptions-item>
- <!-- 运营商 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <location />
- </el-icon>
- 运营商
- </div>
- </template>
- {{formatOperator(sim.types)}}
- </el-descriptions-item>
- <!-- 类型 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <location />
- </el-icon>
- 类型
- </div>
- </template>
- {{formatType(sim.simTypes)}}
- </el-descriptions-item>
-
- <!-- 激活日期 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <location />
- </el-icon>
- 激活日期
- </div>
- </template>
- {{sim.activationTime}}
- </el-descriptions-item>
- <!-- 更新时间 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <location />
- </el-icon>
- 更新时间
- </div>
- </template>
- {{sim.updatedAt}}
- </el-descriptions-item>
- <!-- 总流量 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <location />
- </el-icon>
- 总流量
- </div>
- </template>
- {{sim.totalFlow}}
- </el-descriptions-item>
- <!-- 使用流量 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <location />
- </el-icon>
- 使用流量
- </div>
- </template>
- {{sim.usedFlow}}
- </el-descriptions-item>
- <!-- 剩余流量 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <location />
- </el-icon>
- 剩余流量
- </div>
- </template>
- {{sim.leaveFlow}}
- </el-descriptions-item>
- <!-- 状态 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <tickets />
- </el-icon>
- 状态
- </div>
- </template>
- <el-tag size="small">{{formatStatus(sim.simStatus)}}</el-tag>
- </el-descriptions-item>
- <!-- 说明 -->
- <el-descriptions-item>
- <template #label>
- <div class="cell-item">
- <el-icon :style="iconStyle">
- <office-building />
- </el-icon>
- 说明
- </div>
- </template>
- {{sim.iccid}}
- </el-descriptions-item>
- </el-descriptions>
- </el-card>
- <div class="statistics-wrap gap-3">
- <el-card shadow="nover" class="left-wrap">
- <div class="top-title-wrap">
- <div class="title">{{ $t('message.iotCard.detail.flowChart.title') }}</div>
- <div class="operate-wrap">
- <el-button-group>
- <el-button @click="changeDate(1)" :type="activeIndex == 1 ? 'primary' : ''">{{ $t('message.iotCard.detail.flowChart.buttons.yesterday') }}</el-button>
- <el-button @click="changeDate(2)" :type="activeIndex == 2 ? 'primary' : ''">{{ $t('message.iotCard.detail.flowChart.buttons.week') }}</el-button>
- <el-button @click="changeDate(3)" :type="activeIndex == 3 ? 'primary' : ''">{{ $t('message.iotCard.detail.flowChart.buttons.month') }}</el-button>
- <el-button @click="changeDate(4)" :type="activeIndex == 4 ? 'primary' : ''">{{ $t('message.iotCard.detail.flowChart.buttons.year') }}</el-button>
- </el-button-group>
- <el-date-picker
- class="date-picker-wrap"
- v-model="dateRange"
- type="daterange"
- :range-separator="$t('message.iotCard.detail.flowChart.datePicker.rangeSeparator')"
- :start-placeholder="$t('message.iotCard.detail.flowChart.datePicker.startPlaceholder')"
- :end-placeholder="$t('message.iotCard.detail.flowChart.datePicker.endPlaceholder')"
- format="YYYY-MM-DD HH:mm:ss"
- date-format="YYYY/MM/DD"
- time-format="hh:mm:ss"
- @change="handleDateChange"
- />
- </div>
- </div>
- <div style="height: 460px;" ref="flowLine"></div>
- </el-card>
- <el-card shadow="nover" class="right-wrap">
- <div class="title">{{ $t('message.iotCard.detail.dataStatistics.title') }}</div>
- <div class="line-wrap flow-line-wrap">
- <div class="text-wrap">
- <div class="text">{{ $t('message.iotCard.detail.dataStatistics.yesterdayFlow') }}</div>
- <div>{{formatSize(statisticsData.yesterdayTotal * 1024 * 1024)}}</div>
- </div>
- <div class="line-inner-wrap" ref="yesterdayLine"></div>
- </div>
- <div class="line-wrap flow-line-wrap">
- <div class="text-wrap">
- <div class="text">{{ $t('message.iotCard.detail.dataStatistics.monthFlow') }}</div>
- <div>{{formatSize(statisticsData.monthTotal * 1024 * 1024)}}</div>
- </div>
- <div class="line-inner-wrap" ref="monthLine"></div>
- </div>
- <div class="line-wrap flow-line-wrap">
- <div class="text-wrap">
- <div class="text">{{ $t('message.iotCard.detail.dataStatistics.yearFlow') }}</div>
- <div>{{formatSize(statisticsData.yearTotal * 1024 * 1024)}}</div>
- </div>
- <div class="line-inner-wrap" ref="yearLine"></div>
- </div>
-
- </el-card>
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import { ref, reactive, nextTick, watch, markRaw } from "vue";
- import { formatSize } from "/@/utils/common";
- import api from '/@/api/modules/iotCard';
- import { useStore } from '/@/store/index';
- import { useRoute } from 'vue-router';
- import * as echarts from 'echarts';
- import dayjs from 'dayjs';
- import { useI18n } from 'vue-i18n';
- const store = useStore();
- const route = useRoute();
- const { t } = useI18n();
- const sim = ref({
- accNumber: "",// 卡号
- iccid: "",// ICCID
- bindDeviceName: "",// 绑定设备
- platName: "",// 平台对接
- types: "",// 运营商
- simTypes: "",// 类型
- totalFlow: "",// 总流量
- usedFlow: "",// 使用流量
- leaveFlow: "",// 剩余流量
- activationTime: "",// 激活日期
- updatedAt: "",// 更新时间
- simStatus: "",// 状态
- remark: ""// 说明
- })
- const flowLine = ref();
- const yesterdayLine = ref();
- const monthLine = ref();
- const yearLine = ref();
- const dateRange = ref<any>([
- dayjs(new Date()).subtract(6, 'day'),
- dayjs(new Date()),
- ])
- const activeIndex= ref(2);
- const flowLineXAxisData = ref<any>([]);
- const flowLineData = ref<any>([]);
- const yearLineXAxisData = ref<any>([]);
- const yearLineData = ref<any>([]);
- const monthLineXAxisData = ref<any>([]);
- const monthLineData = ref<any>([]);
- const yesterdayLineXAxisData = ref<any>([dayjs(new Date()).subtract(1, 'day').format('YYYY-MM-DD')]);
- const yesterdayLineData = ref<any>([]);
- const statisticsData = ref({
- monthTotal: 0,
- yearTotal: 0,
- yesterdayTotal: 0
- })
- const state = reactive({
- global: {
- yesterdayLine: null,
- monthLine: null,
- yearLine: null,
- dispose: [null, '', undefined],
- } as any,
- myCharts: [],
- charts: {
- theme: '',
- bgColor: '',
- color: '#303133',
- },
- });
- const getDetailInfo = async () => {
- const res = await api.simCard.detailItem({ id: route.params.id });
- sim.value = res.sim;
- statisticsData.value = {
- monthTotal: res.monthFlow,
- yearTotal: res.yearFlow,
- yesterdayTotal: res.yesterdayFlow
- }
- await getFlowDataByDateRange();
- res.yearDataList.reverse().forEach((item:any) => {
- yearLineXAxisData.value.push(item.date);
- yearLineData.value.push(item.value);
- })
- res.monthDataList.reverse().forEach((item:any) => {
- monthLineXAxisData.value.push(item.date);
- monthLineData.value.push(item.value);
- })
- yesterdayLineData.value = [res.yearFlow];
- iniFlowLineChart();
- initYesterdayLineChart();
- initMonthLineChart();
- initYearLineChart();
- }
- getDetailInfo();
- const getFlowDataByDateRange = async () => {
- const simFlowRes = await api.simCard.getFlowDataByDateRange({
- sdate: activeIndex.value !== 1 ? dateRange.value[0].format('YYYY-MM-DD HH:mm:ss') : dateRange.value[0],
- edate: activeIndex.value !== 1 ? dateRange.value[1].format('YYYY-MM-DD HH:mm:ss') : dateRange.value[1],
- accNumber: sim.value.accNumber,
- types: sim.value.types
- })
- simFlowRes.data.reverse().forEach((item:any) => {
- flowLineXAxisData.value.push(item.date);
- flowLineData.value.push(item.value);
- })
- iniFlowLineChart();
- }
- const changeDate = (key:number) => {
- // 1 昨天 2近一周 3近一月 4近一年
- activeIndex.value = key;
- if(key === 1) {
- // 昨天
- const yesterday = dayjs(new Date()).subtract(1, 'day').format('YYYY-MM-DD')
- dateRange.value = [
- yesterday + " 00:00:00",
- yesterday + " 23:59:59"
- ]
- }else if(key === 2) {
- // 近一周
- dateRange.value = [
- dayjs(new Date()).subtract(6, 'day'),
- dayjs(new Date()),
- ]
- }else if(key === 3) {
- // 近一月
- dateRange.value = [
- dayjs(new Date()).subtract(1, 'month'),
- dayjs(new Date()),
- ]
- }else if(key === 4) {
- // 近一年
- dateRange.value = [
- dayjs(new Date()).subtract(1, 'year'),
- dayjs(new Date()),
- ]
- }
- flowLineXAxisData.value = [];
- flowLineData.value = [];
- getFlowDataByDateRange();
- }
- // 流量统计时间筛选
- const handleDateChange = async (value:any) => {
- dateRange.value = [
- value[0],
- value[1]
- ]
- activeIndex.value == 2;
- // 这里可以添加相应的处理逻辑
- getFlowDataByDateRange();
- }
- const formatOperator = (val:number) => {
- // 1电信,2联通,3移动
- const operators = ['', t('message.iotCard.index.operators.telecom'), t('message.iotCard.index.operators.unicom'), t('message.iotCard.index.operators.mobile')];
- return operators[val];
- }
- const formatType = (val:number) => {
- // 1月卡,2季卡,3年卡,4其他
- const types = ['', t('message.iotCard.index.types.monthly'), t('message.iotCard.index.types.quarterly'), t('message.iotCard.index.types.yearly'), t('message.iotCard.index.types.other')];
- return types[val];
- }
- const formatStatus = (val:any) => {
- // 1:可激活 2:测试激活 3:测试去激活 4:在用 5:停机 6:运营商管理状态
- const statuses = ['', t('message.iotCard.index.status.activatable'), t('message.iotCard.index.status.testActivated'), t('message.iotCard.index.status.testDeactivated'), t('message.iotCard.index.status.inUse'), t('message.iotCard.index.status.suspended'), t('message.iotCard.index.status.operatorManaged')];
- return statuses[val];
- }
- // 折线图 - 昨日流量消耗
- const initYesterdayLineChart = () => {
- if (!state.global.dispose.some((b: any) => b === state.global.yesterdayLine)) state.global.yesterdayLine.dispose();
- state.global.yesterdayLine = markRaw(echarts.init(yesterdayLine.value, state.charts.theme));
- const option = {
- backgroundColor: state.charts.bgColor,
- xAxis: {
- data: yesterdayLineXAxisData.value,
- show: false,
- },
- yAxis: [
- {
- type: 'value',
- name: '',
- show: false,
- splitLine: { show: false, lineStyle: { type: 'dashed', color: '#f5f5f5' } }
- },
- ],
- tooltip: {
- trigger: 'axis',
- formatter: function (params:any) {
- var relVal = params[0].name
- let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
- relVal += '<br/>' + circle + ' ' + t('message.iotCard.detail.charts.tooltip.flow') + ': ' + formatSize(params[0].value*1024*1024)
- return relVal;
- }
- },
- grid: { top: 10, right: 10, bottom: 10, left: 10 },
- series: [
- {
- name: t('message.iotCard.detail.charts.series.flow'),
- type: 'line',
- smooth: true,
- data: yesterdayLineData.value,
- lineStyle: { color: '#fe9a8b' },
- itemStyle: { color: '#fe9a8b', borderColor: '#fe9a8b' }
- }
- ],
- };
- (<any>state.global.yesterdayLine).setOption(option);
- (<any>state.myCharts).push(state.global.yesterdayLine);
- };
- // 折线图 - 当月流量消耗
- const initMonthLineChart = () => {
- if (!state.global.dispose.some((b: any) => b === state.global.monthLine)) state.global.monthLine.dispose();
- state.global.monthLine = markRaw(echarts.init(monthLine.value, state.charts.theme));
- const option = {
- backgroundColor: state.charts.bgColor,
- xAxis: {
- data: monthLineXAxisData.value,
- show: false,
- },
- yAxis: [
- {
- type: 'value',
- name: '',
- show: false,
- splitLine: { show: false, lineStyle: { type: 'dashed', color: '#f5f5f5' } }
- },
- ],
- tooltip: {
- trigger: 'axis',
- formatter: function (params:any) {
- var relVal = params[0].name
- let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
- relVal += '<br/>' + circle + ' ' + t('message.iotCard.detail.charts.tooltip.flow') + ': ' + formatSize(params[0].value*1024*1024)
- return relVal;
- }
- },
- grid: { top: 10, right: 10, bottom: 10, left: 10 },
- series: [
- {
- name: t('message.iotCard.detail.charts.series.flow'),
- type: 'line',
- smooth: true,
- data: monthLineData.value,
- lineStyle: { color: '#9E87FF' },
- itemStyle: { color: '#9E87FF', borderColor: '#9E87FF' },
- }
- ],
- };
- (<any>state.global.monthLine).setOption(option);
- (<any>state.myCharts).push(state.global.monthLine);
- };
- // 折线图 - 本年流量消耗
- const initYearLineChart = () => {
- if (!state.global.dispose.some((b: any) => b === state.global.yearLine)) state.global.yearLine.dispose();
- state.global.yearLine = markRaw(echarts.init(yearLine.value, state.charts.theme));
- const option = {
- backgroundColor: state.charts.bgColor,
- xAxis: {
- data: yearLineXAxisData.value,
- show: false,
- },
- yAxis: [
- {
- type: 'value',
- name: '',
- show: false,
- splitLine: { show: false, lineStyle: { type: 'dashed', color: '#f5f5f5' } }
- },
- ],
- tooltip: {
- trigger: 'axis',
- formatter: function (params:any) {
- var relVal = params[0].name
- let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
- relVal += '<br/>' + circle + ' ' + t('message.iotCard.detail.charts.tooltip.flow') + ': ' + formatSize(params[0].value*1024*1024)
- return relVal;
- }
- },
- grid: { top: 10, right: 10, bottom: 10, left: 10 },
- series: [
- {
- name: t('message.iotCard.detail.charts.series.flow'),
- type: 'line',
- smooth: true,
- data: yearLineData.value,
- lineStyle: { color: '#fe9a8b' },
- itemStyle: { color: '#fe9a8b', borderColor: '#fe9a8b' }
- }
- ],
- };
- (<any>state.global.yearLine).setOption(option);
- (<any>state.myCharts).push(state.global.yearLine);
- };
- // 折线图 - 流量统计
- const iniFlowLineChart = async () => {
- if (!state.global.dispose.some((b: any) => b === state.global.flowLine)) state.global.flowLine.dispose();
- state.global.flowLine = markRaw(echarts.init(flowLine.value, state.charts.theme));
- const option = {
- backgroundColor: state.charts.bgColor,
- grid: { top: 70, right: 20, bottom: 30, left: 30 },
- tooltip: {
- trigger: 'axis',
- formatter: function (params:any) {
- var relVal = params[0].name
- let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
- relVal += '<br/>' + circle + ' ' + t('message.iotCard.detail.charts.tooltip.flow') + ': ' + params[0].value + 'MB'
- return relVal;
- }
- },
- xAxis: {
- data: flowLineXAxisData.value,
- },
- yAxis: [
- {
- type: 'value',
- name: '',
- splitLine: { show: true, lineStyle: { type: 'dashed', color: '#f5f5f5' } },
- axisLabel: {
- margin: 2,
- formatter: function (value:any) {
- if (value >= 10000 && value < 10000000) {
- value = value / 10000 + "W";
- } else if (value >= 10000000) {
- value = value / 10000000 + "KW";
- }
- return value;
- }
- },
- },
- ],
- series: [
- {
- name: t('message.iotCard.detail.charts.series.flow'),
- type: 'line',
- symbolSize: 6,
- symbol: 'circle',
- smooth: true,
- data: flowLineData.value,
- lineStyle: { color: '#9E87FF' },
- itemStyle: { color: '#9E87FF', borderColor: '#9E87FF' },
- areaStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: '#9E87FFb3' },
- { offset: 1, color: '#9E87FF03' },
- ]),
- },
- emphasis: {
- itemStyle: {
- color: {
- type: 'radial',
- x: 0.5,
- y: 0.5,
- r: 0.5,
- colorStops: [
- { offset: 0, color: '#9E87FF' },
- { offset: 0.4, color: '#9E87FF' },
- { offset: 0.5, color: '#fff' },
- { offset: 0.7, color: '#fff' },
- { offset: 0.8, color: '#fff' },
- { offset: 1, color: '#fff' },
- ],
- },
- borderColor: '#9E87FF',
- borderWidth: 2,
- },
- },
- },
- ],
- };
- (<any>state.global.flowLine).setOption(option);
- (<any>state.myCharts).push(state.global.flowLine);
- };
- // 监听 vuex 中是否开启深色主题
- watch(
- () => store.state.themeConfig.themeConfig.isIsDark,
- (isIsDark) => {
- nextTick(() => {
- state.charts.theme = isIsDark ? 'dark' : '';
- state.charts.bgColor = isIsDark ? 'transparent' : '';
- state.charts.color = isIsDark ? '#dadada' : '#303133';
- setTimeout(() => {
- iniFlowLineChart();
- initYesterdayLineChart();
- initMonthLineChart();
- initYearLineChart();
- }, 500);
- });
- },
- {
- deep: true,
- immediate: true,
- }
- );
- </script>
- <style lang="scss" scoped>
- .statistics-wrap {
- display: flex;
- justify-content: space-between;
- align-items: normal;
- margin-top: 20px;
- .title {
- color: var(--el-text-color-primary);
- font-size: 16px;
- font-weight: 700;
- }
- .left-wrap {
- width: 66%;
- .top-title-wrap {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16px;
- :deep(.el-date-editor.el-input__wrapper) {
- width: 360px!important;
- margin-left: 12px;
- }
- .operate-wrap {
- display: flex;
- justify-content: center;
- align-items: center;
- }
- }
- }
- .right-wrap {
- width: 36%;
- .line-wrap {
- width: 100%;
- background-color: #fff;
- background: #fcfcfc;
- border: 1px solid #e0e4e8;
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 20px;
- position: relative;
- .text-wrap {
- position: absolute;
- left: 20px;
- top: 40px;
- .text {
- font-size: 14px;
- color: #000000a3;
- }
- div:nth-child(2) {
- font-size: 32px;
- font-weight: 700;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- }
- .line-inner-wrap {
- height: 100px;
- width: calc(100% - 120px);
- margin-left: 120px;
- }
- }
- .line-wrap:not(:nth-child(1)) {
- margin-top: 20px;
- }
- }
- }
- </style>
|