|
@@ -1,428 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="overview">
|
|
|
- <el-row :gutter="12" style="padding: 12px; row-gap: 12px;">
|
|
|
- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
- <el-card shadow="hover" style="padding: 10px 14px;">
|
|
|
- <div class="top-info">
|
|
|
- <div class="title">流失概况</div>
|
|
|
- <div class="aside">
|
|
|
- <div class="data-source-status">数据源状态: Demo数据</div>
|
|
|
- <el-button class="goto-smart-operation" type="primary" link>前往智能运营发短信
|
|
|
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
- <path d="M10.6667 4.66699L13.3334 7.33366L10.6667 10.0003" stroke="#167AF0" stroke-width="1.5"
|
|
|
- stroke-linecap="round" stroke-linejoin="round" />
|
|
|
- <path d="M2.66671 12.6663V8.33301C2.66671 7.78071 3.11441 7.33301 3.66671 7.33301H13.3334"
|
|
|
- stroke="#167AF0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
|
|
- </svg>
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="top-content">
|
|
|
- <div class="el-card" style="flex: 1; overflow: visible;">
|
|
|
- <div class="content-item">
|
|
|
- <div class="content-item-left">
|
|
|
- <DoughnutChart :data="churnData" width="150px" height="150px" :radius="['35px', '55px']"
|
|
|
- :show-label="false" :show-legend="false" />
|
|
|
- </div>
|
|
|
- <div class="content-item-right">
|
|
|
- <div class="content-item-title">当周卸载流失设备数</div>
|
|
|
- <div class="content-item-value">38</div>
|
|
|
- <div class="content-item-percent">环比<span>-74.23%</span>
|
|
|
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
- <mask id="mask0_611_555" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="14"
|
|
|
- height="14">
|
|
|
- <rect width="14" height="14" fill="#D9D9D9" />
|
|
|
- </mask>
|
|
|
- <g mask="url(#mask0_611_555)">
|
|
|
- <path
|
|
|
- d="M11.1702 4.43275C11.5204 4.43302 11.8046 4.71698 11.8047 5.06725L11.8041 10.7783C11.8041 11.1287 11.5201 11.4128 11.1696 11.4128L5.45857 11.4134C5.10831 11.4133 4.82435 11.1291 4.82408 10.7789C4.82408 10.4284 5.1087 10.1438 5.45916 10.1438L9.6377 10.1438L2.69566 3.20174C2.44785 2.95393 2.44785 2.55215 2.69566 2.30434C2.94348 2.05652 3.34526 2.05652 3.59307 2.30434L10.5351 9.24637L10.5351 5.06783C10.5351 4.71737 10.8197 4.43275 11.1702 4.43275Z"
|
|
|
- fill="#00BC71" />
|
|
|
- </g>
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="el-card" style="flex: 1; overflow: visible;">
|
|
|
- <div class="content-item">
|
|
|
- <div class="content-item-left">
|
|
|
- <DoughnutChart :data="recallData" width="150px" height="150px" :radius="['35px', '55px']"
|
|
|
- :show-label="false" :show-legend="false" />
|
|
|
- </div>
|
|
|
- <div class="content-item-right">
|
|
|
- <div class="content-item-title">当周卸载召回设备数</div>
|
|
|
- <div class="content-item-value">38</div>
|
|
|
- <div class="content-item-percent">
|
|
|
- 环比<span>+74.23%</span>
|
|
|
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
- <mask id="mask0_611_558" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="14"
|
|
|
- height="14">
|
|
|
- <rect width="14" height="14" transform="matrix(1 0 0 -1 0 14)" fill="#D9D9D9" />
|
|
|
- </mask>
|
|
|
- <g mask="url(#mask0_611_558)">
|
|
|
- <path
|
|
|
- d="M11.1702 9.56725C11.5204 9.56698 11.8046 9.28302 11.8047 8.93275L11.8041 3.22173C11.8041 2.87127 11.5201 2.58723 11.1696 2.58723L5.45857 2.58665C5.10831 2.58668 4.82435 2.87093 4.82408 3.22114C4.82408 3.5716 5.1087 3.85622 5.45916 3.85622L9.6377 3.85622L2.69566 10.7983C2.44785 11.0461 2.44785 11.4479 2.69566 11.6957C2.94348 11.9435 3.34526 11.9435 3.59307 11.6957L10.5351 4.75363L10.5351 8.93217C10.5351 9.28263 10.8197 9.56725 11.1702 9.56725Z"
|
|
|
- fill="#E64242" />
|
|
|
- </g>
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- <el-row :gutter="12" style="padding: 0 12px 12px; row-gap: 12px;">
|
|
|
- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
- <el-card shadow="hover">
|
|
|
- <div class="trend-container">
|
|
|
- <div class="title">流失趋势</div>
|
|
|
- <div class="tabs">
|
|
|
- <div class="tabs-item" :class="{ active: activeTab === 'churnTrend' }" @click="handleTabClick('churnTrend')">卸载流失设备</div>
|
|
|
- <div class="tabs-item" :class="{ active: activeTab === 'recallTrend' }" @click="handleTabClick('recallTrend')">卸载召回设备</div>
|
|
|
- </div>
|
|
|
- <!-- 折线图 -->
|
|
|
- <div class="chart-container">
|
|
|
- <LineChart :data="currentChartData" :color="'#167af0'" :title="currentChartTitle" height="270px"
|
|
|
- :smooth="false" :area-style="true" />
|
|
|
- <div class="echarts-name">{{currentChartTitle}}</div>
|
|
|
- </div>
|
|
|
- <el-divider style="margin: 30px 0; background: rgba(230, 230, 230, 1);"/>
|
|
|
- <div class="table-container">
|
|
|
- <div class="btn-toggle-table" :class="{ 'hide-table': hideTable }" @click="hideTable = !hideTable">
|
|
|
- {{ hideTable ? '展开' : '收起' }}明细数据<svg
|
|
|
- width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
- <path d="M10.5 8.75L7 5.25L3.5 8.75" stroke="#167AF0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
-
|
|
|
- <el-table class="statistics-table" :data="state.dataList" row-key="date" style="width: 100%"
|
|
|
- border :cell-style="tableStyle.cellStyle"
|
|
|
- :header-cell-style="tableStyle.headerCellStyle"
|
|
|
- v-if="!hideTable"
|
|
|
- >
|
|
|
- <el-table-column :label="'日期'" prop="date" show-overflow-tooltip></el-table-column>
|
|
|
- <el-table-column :label="'卸载流失设备'" :formatter="statusFormatter" prop="churn" show-overflow-tooltip></el-table-column>
|
|
|
- <el-table-column :label="'卸载召回设备'" :formatter="statusFormatter" prop="recall" show-overflow-tooltip></el-table-column>
|
|
|
- </el-table>
|
|
|
-
|
|
|
- <pagination
|
|
|
- v-if="!hideTable"
|
|
|
- @current-change="currentChangeHandle"
|
|
|
- @size-change="sizeChangeHandle"
|
|
|
- v-bind="state.pagination">
|
|
|
- </pagination>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script lang="ts" name="churnOverview" setup>
|
|
|
-import DoughnutChart from './echarts/DoughnutChart.vue'
|
|
|
-import LineChart from './echarts/LineChart.vue'
|
|
|
-import { BasicTableProps, useTable } from '/@/hooks/table';
|
|
|
-import { ref, computed, reactive } from 'vue'
|
|
|
-
|
|
|
-// 流失数据
|
|
|
-const churnData = ref([
|
|
|
- { name: '流失设备', value: 38, itemStyle: { color: 'rgba(0, 188, 113, 1)' } },
|
|
|
- { name: '其他', value: 62, itemStyle: { color: 'rgba(239, 239, 239, 1)' } }
|
|
|
-])
|
|
|
-
|
|
|
-// 召回数据
|
|
|
-const recallData = ref([
|
|
|
- { name: '召回设备', value: 38, itemStyle: { color: 'rgba(230, 66, 66, 1)' } },
|
|
|
- { name: '其他', value: 62, itemStyle: { color: 'rgba(239, 239, 239, 1)' } }
|
|
|
-])
|
|
|
-
|
|
|
-// 流失趋势数据
|
|
|
-const churnTrendData = ref([
|
|
|
- { date: '2025-01-01', value: 25 },
|
|
|
- { date: '2025-01-02', value: 32 },
|
|
|
- { date: '2025-01-03', value: 28 },
|
|
|
- { date: '2025-01-04', value: 45 },
|
|
|
- { date: '2025-01-05', value: 38 },
|
|
|
- { date: '2025-01-06', value: 42 },
|
|
|
- { date: '2025-01-07', value: 35 },
|
|
|
- { date: '2025-01-08', value: 48 },
|
|
|
- { date: '2025-01-09', value: 52 },
|
|
|
- { date: '2025-01-10', value: 39 },
|
|
|
- { date: '2025-01-11', value: 44 },
|
|
|
- { date: '2025-01-12', value: 37 },
|
|
|
- { date: '2025-01-13', value: 41 },
|
|
|
- { date: '2025-01-14', value: 46 }
|
|
|
-])
|
|
|
-
|
|
|
-// 召回趋势数据
|
|
|
-const recallTrendData = ref([
|
|
|
- { date: '2025-01-01', value: 15 },
|
|
|
- { date: '2025-01-02', value: 22 },
|
|
|
- { date: '2025-01-03', value: 18 },
|
|
|
- { date: '2025-01-04', value: 35 },
|
|
|
- { date: '2025-01-05', value: 28 },
|
|
|
- { date: '2025-01-06', value: 32 },
|
|
|
- { date: '2025-01-07', value: 25 },
|
|
|
- { date: '2025-01-08', value: 38 },
|
|
|
- { date: '2025-01-09', value: 42 },
|
|
|
- { date: '2025-01-10', value: 29 },
|
|
|
- { date: '2025-01-11', value: 34 },
|
|
|
- { date: '2025-01-12', value: 27 },
|
|
|
- { date: '2025-01-13', value: 31 },
|
|
|
- { date: '2025-01-14', value: 36 }
|
|
|
-])
|
|
|
-
|
|
|
-const activeTab = ref('churnTrend')
|
|
|
-
|
|
|
-// 计算当前图表数据
|
|
|
-const currentChartData = computed(() => {
|
|
|
- return activeTab.value === 'churnTrend' ? churnTrendData.value : recallTrendData.value
|
|
|
-})
|
|
|
-
|
|
|
-// 计算当前图表标题
|
|
|
-const currentChartTitle = computed(() => {
|
|
|
- return activeTab.value === 'churnTrend' ? '卸载流失设备' : '卸载召回设备'
|
|
|
-})
|
|
|
-
|
|
|
-const handleTabClick = (tab: string) => {
|
|
|
- activeTab.value = tab
|
|
|
-}
|
|
|
-
|
|
|
-// 表格数据
|
|
|
-const hideTable = ref(false);
|
|
|
-const state: BasicTableProps = reactive<BasicTableProps>({
|
|
|
- queryForm: {
|
|
|
- ip: '',
|
|
|
- },
|
|
|
- pageList: () => Promise.resolve([]),
|
|
|
- pagination: {
|
|
|
- current: 1,
|
|
|
- size: 10,
|
|
|
- total: 0,
|
|
|
- pageSizes: [5, 10, 20, 50, 100]
|
|
|
- },
|
|
|
- dataList: [
|
|
|
- {
|
|
|
- date: '2025-01-01',
|
|
|
- churn: 10,
|
|
|
- recall: 20
|
|
|
- },
|
|
|
- {
|
|
|
- date: '2025-01-02',
|
|
|
- churn: 15,
|
|
|
- recall: 25
|
|
|
- },
|
|
|
- {
|
|
|
- date: '2025-01-03',
|
|
|
- churn: 20,
|
|
|
- recall: 30
|
|
|
- },
|
|
|
- {
|
|
|
- date: '2025-01-04',
|
|
|
- churn: 25,
|
|
|
- recall: 35
|
|
|
- }
|
|
|
- ]
|
|
|
-});
|
|
|
-const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
|
|
|
-const statusFormatter = (row: any, column: any, cellValue: any, index: any) => {
|
|
|
- return cellValue || '--';
|
|
|
-}
|
|
|
-
|
|
|
-</script>
|
|
|
-<style scoped lang="scss">
|
|
|
-svg {
|
|
|
- vertical-align: middle;
|
|
|
- margin: 0 0 0 12px;
|
|
|
-}
|
|
|
-.overview {
|
|
|
- font-family: Source Han Sans SC;
|
|
|
- color: rgba(18, 18, 18, 1);
|
|
|
-}
|
|
|
-
|
|
|
-.top-info {
|
|
|
- color: rgba(18, 18, 18, 1);
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 25px;
|
|
|
-
|
|
|
-
|
|
|
- .title {
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 500;
|
|
|
- line-height: 20px;
|
|
|
- padding: 4px 0;
|
|
|
- }
|
|
|
-
|
|
|
- .aside {
|
|
|
- display: flex;
|
|
|
- }
|
|
|
-
|
|
|
- .data-source-status {
|
|
|
- font-weight: 400;
|
|
|
- font-size: 14px;
|
|
|
- line-height: 20px;
|
|
|
- color: rgba(18, 18, 18, 1);
|
|
|
- padding: 4px 16px 4px 0;
|
|
|
- }
|
|
|
-
|
|
|
- .goto-smart-operation {
|
|
|
- padding: 4px 12px;
|
|
|
- border-radius: 4px;
|
|
|
- border: 1px solid rgba(22, 122, 240, 1);
|
|
|
- font-weight: 400;
|
|
|
- font-size: 14px;
|
|
|
- line-height: 20px;
|
|
|
- color: rgba(22, 122, 240, 1);
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-.top-content {
|
|
|
- display: flex;
|
|
|
- gap: 20px;
|
|
|
-
|
|
|
- .content-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 0;
|
|
|
- flex: 1;
|
|
|
- padding: 27.5px;
|
|
|
- justify-content: center;
|
|
|
- }
|
|
|
-
|
|
|
- .content-item-left {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- }
|
|
|
-
|
|
|
- .content-item-right {
|
|
|
- // flex: 1;
|
|
|
- }
|
|
|
-
|
|
|
- .content-item-title {
|
|
|
- color: rgba(18, 18, 18, 1);
|
|
|
- font-weight: 400;
|
|
|
- font-size: 15px;
|
|
|
- line-height: 20px;
|
|
|
- margin-bottom: 12px;
|
|
|
- }
|
|
|
-
|
|
|
- .content-item-value {
|
|
|
- font-weight: 500;
|
|
|
- font-style: Medium;
|
|
|
- font-size: 28px;
|
|
|
- margin-bottom: 16px;
|
|
|
- line-height: 41px;
|
|
|
- }
|
|
|
-
|
|
|
- .content-item-percent {
|
|
|
- font-weight: 400;
|
|
|
- font-size: 14px;
|
|
|
- vertical-align: middle;
|
|
|
- line-height: 20px;
|
|
|
- }
|
|
|
-
|
|
|
- .content-item-percent span {
|
|
|
- color: rgba(0, 188, 113, 1);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.trend-container {
|
|
|
- padding: 10px 14px;
|
|
|
- .title {
|
|
|
- line-height: 19px;
|
|
|
- font-weight: 500;
|
|
|
- font-size: 16px;
|
|
|
- padding-left: 12px;
|
|
|
- position: relative;
|
|
|
- margin-bottom: 53px;
|
|
|
- &::before {
|
|
|
- content: '';
|
|
|
- position: absolute;
|
|
|
- left: 0;
|
|
|
- top: 50%;
|
|
|
- transform: translateY(-50%);
|
|
|
- width: 4px;
|
|
|
- height: 14px;
|
|
|
- background: rgba(22, 122, 240, 1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .tabs {
|
|
|
- width: 240px;
|
|
|
- display: flex;
|
|
|
- margin-left: 85px;
|
|
|
- .tabs-item {
|
|
|
- flex: 1;
|
|
|
- height: 32px;
|
|
|
- line-height: 30px;
|
|
|
- border: 1px solid rgba(22, 122, 240, 1);
|
|
|
- text-align: center;
|
|
|
- color: rgba(22, 122, 240, 1);
|
|
|
- cursor: pointer;
|
|
|
- &.active {
|
|
|
- background: rgba(22, 122, 240, 1);
|
|
|
- color: #ffffff;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .chart-container {
|
|
|
- margin: 20px 85px 0;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
-
|
|
|
- .echarts-name {
|
|
|
- display: inline-block;
|
|
|
- margin: 28px auto 0;
|
|
|
- padding-left: 16px;
|
|
|
- font-weight: 400;
|
|
|
- font-size: 14px;
|
|
|
- line-height: 20px;
|
|
|
- color: rgba(18, 18, 18, 1);
|
|
|
- position: relative;
|
|
|
- &::before {
|
|
|
- content: '';
|
|
|
- position: absolute;
|
|
|
- left: 0;
|
|
|
- top: 50%;
|
|
|
- transform: translateY(-50%);
|
|
|
- width: 8px;
|
|
|
- height: 8px;
|
|
|
- background: rgba(22, 122, 240, 1);
|
|
|
- border-radius: 50%;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .table-container {
|
|
|
- padding: 0 85px;
|
|
|
-
|
|
|
- .btn-toggle-table {
|
|
|
- font-weight: 500;
|
|
|
- font-size: 14px;
|
|
|
- line-height: 20px;
|
|
|
- color: rgba(22, 122, 240, 1);
|
|
|
- margin-bottom: 20px;
|
|
|
- cursor: pointer;
|
|
|
- svg {
|
|
|
- transition: transform 0.3s ease-in-out;
|
|
|
- }
|
|
|
- &.hide-table svg {
|
|
|
- transform: rotate(-180deg);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .table-container {
|
|
|
- margin-top: 20px;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-</style>
|