<template>
|
<div class="huanxin-code-manage">
|
<FYSearchBar @search="onSearch">
|
<template #options>
|
<!-- 区县 -->
|
<FYOptionLocation
|
:initValue="false"
|
:allOption="false"
|
:level="3"
|
:checkStrictly="false"
|
v-model:value="formSearch.locations"
|
></FYOptionLocation>
|
<!-- 场景类型 -->
|
<FYOptionScene
|
:initValue="false"
|
:allOption="false"
|
:type="1"
|
v-model:value="formSearch.scenetype"
|
></FYOptionScene>
|
<!-- 时间 -->
|
<FYOptionTime
|
:initValue="false"
|
type="month"
|
v-model:value="formSearch.time"
|
></FYOptionTime>
|
</template>
|
<template #buttons v-if="$slots.buttons"> </template>
|
</FYSearchBar>
|
<!-- 顶部宏观看板区 -->
|
<el-row :gutter="20" class="dashboard">
|
<el-col :span="8">
|
<el-card shadow="hover" class="dashboard-card green-card" @click="filterByCode('green')">
|
<div class="card-content">
|
<div class="card-title">绿码店铺数</div>
|
<div class="card-value">{{ statistics.greenCount }}</div>
|
<div class="card-percentage">{{ statistics.greenPercentage }}%</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="8">
|
<el-card shadow="hover" class="dashboard-card yellow-card" @click="filterByCode('yellow')">
|
<div class="card-content">
|
<div class="card-title">黄码店铺数</div>
|
<div class="card-value">{{ statistics.yellowCount }}</div>
|
<div class="card-percentage">{{ statistics.yellowPercentage }}%</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="8">
|
<el-card shadow="hover" class="dashboard-card red-card" @click="filterByCode('red')">
|
<div class="card-content">
|
<div class="card-title">红码店铺数</div>
|
<div class="card-value">{{ statistics.redCount }}</div>
|
<div class="card-percentage">{{ statistics.redPercentage }}%</div>
|
</div>
|
</el-card>
|
</el-col>
|
</el-row>
|
|
<!-- 评分模型配置按钮 -->
|
<!-- <div class="model-config" v-if="isAdmin">
|
<el-button type="primary" @click="openModelConfig">
|
<el-icon><Setting /></el-icon>
|
<span>评分模型配置</span>
|
</el-button>
|
</div> -->
|
|
<!-- 店铺列表 -->
|
<div class="shop-list">
|
<el-table :data="pagedShopList" style="width: 100%">
|
<el-table-column prop="shopName" label="店铺名称" />
|
<el-table-column prop="district" label="所在区县" width="120" />
|
<el-table-column prop="town" label="所在街镇" width="150" />
|
<el-table-column label="环信码" width="100">
|
<template #default="scope">
|
<el-tag :type="getCodeType(scope.row.code)">{{ getCodeText(scope.row.code) }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="score" label="当前评分" width="120" sortable />
|
<el-table-column label="评分变化趋势" width="150">
|
<template #default="scope">
|
<div class="trend">
|
<el-icon :class="['trend-icon', scope.row.trend > 0 ? 'up' : 'down']">
|
<ArrowUp v-if="scope.row.trend > 0" />
|
<ArrowDown v-else />
|
</el-icon>
|
<span :class="scope.row.trend > 0 ? 'up' : 'down'">
|
{{ scope.row.trend > 0 ? '+' : '' }}{{ scope.row.trend }}分
|
</span>
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column prop="lastUpdate" label="上次更新时间" width="180" />
|
<el-table-column label="操作" width="100" fixed="right">
|
<template #default="scope">
|
<el-button size="small" @click="viewDetails(scope.row)">查看详情</el-button>
|
<!-- <el-button size="small" type="warning" @click="viewRiskWarnings(scope.row)"
|
>风险预警记录</el-button
|
> -->
|
</template>
|
</el-table-column>
|
</el-table>
|
<!-- 分页组件 -->
|
<div class="pagination">
|
<el-pagination
|
v-model:current-page="currentPage"
|
v-model:page-size="pageSize"
|
:page-sizes="[10, 20, 50, 100]"
|
layout="total, sizes, prev, pager, next, jumper"
|
:total="filteredShopList.length"
|
@size-change="handleSizeChange"
|
@current-change="handleCurrentChange"
|
/>
|
</div>
|
</div>
|
|
<!-- 详情抽屉 -->
|
<el-drawer
|
v-model="drawerVisible"
|
:title="selectedShop?.shopName || '店铺详情'"
|
direction="rtl"
|
size="60%"
|
>
|
<div v-if="selectedShop" class="shop-details">
|
<el-row justify="space-between" style="flex-wrap: nowrap">
|
<!-- 环信码大图标及当前评分 -->
|
<div class="code-header">
|
<div class="score-info">
|
<div class="score-label">当前评分</div>
|
<div class="score-value">{{ selectedShop.score }}</div>
|
</div>
|
<div class="code-icon">
|
<el-image
|
class="image"
|
:src="codeImageUrl"
|
:preview-src-list="[codeImageUrl]"
|
:initial-index="0"
|
fit="cover"
|
lazy
|
/>
|
</div>
|
</div>
|
|
<!-- 评分维度雷达图 -->
|
<div class="chart-section">
|
<h3>评分维度分析</h3>
|
<div class="radar-chart">
|
<canvas ref="radarChart" width="500" height="400"></canvas>
|
</div>
|
</div>
|
</el-row>
|
|
<!-- 评分历史趋势图 -->
|
<div class="chart-section">
|
<h3>评分历史趋势</h3>
|
<div class="trend-chart">
|
<canvas ref="trendChart" width="800" height="350"></canvas>
|
</div>
|
</div>
|
|
<!-- 风险预警记录 -->
|
<div class="warning-section">
|
<h3>风险预警记录</h3>
|
<el-table :data="selectedShop.warnings" style="width: 100%">
|
<el-table-column prop="time" label="预警时间" width="180" />
|
<el-table-column prop="content" label="预警内容" />
|
<el-table-column prop="score" label="当时评分" width="120" />
|
<el-table-column label="处理状态" width="120">
|
<template #default="scope">
|
<el-tag :type="scope.row.handled ? 'success' : 'danger'">
|
{{ scope.row.handled ? '已处理' : '未处理' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
</div>
|
</el-drawer>
|
|
<!-- 评分模型配置弹窗 -->
|
<el-dialog v-model="modelConfigVisible" title="评分模型配置" width="80%">
|
<div class="model-config-content">
|
<!-- 这里应该集成真实的配置表单 -->
|
<el-empty description="配置表单加载中..." />
|
</div>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="modelConfigVisible = false">取消</el-button>
|
<el-button type="primary" @click="saveModelConfig">保存配置</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import dayjs from 'dayjs'
|
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
import { Setting, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
|
import * as echarts from 'echarts'
|
import userApi from '@/api/fytz/userApi'
|
import creditApi from '@/api/fytz/creditApi'
|
|
// 搜索表单
|
const formSearch = ref({
|
locations: {
|
aCode: null,
|
aName: null,
|
cCode: '3100',
|
cName: '上海市',
|
dCode: '310104',
|
dName: '徐汇区',
|
mCode: null,
|
mName: null,
|
pCode: '31',
|
pName: '上海市',
|
tCode: null,
|
tName: null,
|
},
|
scenetype: {
|
label: '餐饮',
|
value: '1',
|
},
|
time: dayjs('2023-08-01').date(1).toDate(),
|
})
|
// 状态
|
const drawerVisible = ref(false)
|
const modelConfigVisible = ref(false)
|
const selectedShop = ref(null)
|
const isAdmin = ref(true) // 模拟管理员权限
|
const filterCode = ref('all')
|
// 分页相关
|
const currentPage = ref(1)
|
const pageSize = ref(10)
|
// 环信码图片URL
|
const codeImageUrl = ref('')
|
|
// 统计数据
|
const statistics = reactive({
|
greenCount: 125,
|
greenPercentage: 65.8,
|
yellowCount: 45,
|
yellowPercentage: 23.7,
|
redCount: 20,
|
redPercentage: 10.5,
|
})
|
|
// 店铺名称列表
|
const shopNames = [
|
'付小姐在成都',
|
'吉刻联盟',
|
'家在塔啦',
|
'狼来了',
|
'乐凯撒星游店',
|
'馨远美食小镇(哈尼美食广场)',
|
'棒约翰',
|
'弄堂咪道',
|
'杨记齐齐哈尔烤肉',
|
'上海稔传餐饮管理有限公司(人生一串)',
|
'缘家',
|
'泉盛餐饮(上海)有限公司(食其家)',
|
'丰茂烤串',
|
'上海泰煌餐饮管理有限公司(泰煌鸡)',
|
'徐汇区辰熙餐馆(小铁君串烧居酒屋)',
|
]
|
|
// 徐汇区街镇列表
|
const xuhuiTowns = [
|
'天平路街道',
|
'湖南路街道',
|
'斜土路街道',
|
'枫林路街道',
|
'长桥街道',
|
'田林街道',
|
'虹梅路街道',
|
'康健新村街道',
|
'徐家汇街道',
|
'凌云路街道',
|
'龙华街道',
|
'漕河泾街道',
|
'华泾镇',
|
]
|
|
function onSearch() {
|
const f = formSearch.value
|
const area = {}
|
// 行政区划
|
area.provinceCode = f.locations.pCode
|
area.provinceName = f.locations.pName
|
if (area.provinceCode == null) {
|
area.provinceCode = null
|
area.provinceName = null
|
}
|
area.cityCode = f.locations.cCode
|
area.cityName = f.locations.cName
|
area.districtCode = f.locations.dCode
|
area.districtName = f.locations.dName
|
area.townCode = f.locations.tCode
|
area.townName = f.locations.tName
|
// 场景类型
|
area.sceneTypes = []
|
f.scenetype.value == null ? (area.sceneTypes = []) : (area.sceneTypes = [f.scenetype.value])
|
// 上下线状态
|
area.online = true
|
// 关键字
|
area.searchText = ''
|
|
userApi.fetchUser(currentPage.value, pageSize.value, area).then((res) => {
|
if (res) {
|
res.data
|
res.head.totalCount
|
|
shopList.value = res.data.map((item, index) => {
|
const { score, code } = generateRandomScore()
|
return {
|
id: index + 1,
|
guid: item.biGuid,
|
shopName: item.biName,
|
district: item.biDistrictName,
|
town: item.biTownName,
|
code: code,
|
score: score,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [
|
{
|
time: generateRandomDate(),
|
content: '净化器运行时长不足',
|
score: 90,
|
handled: true,
|
},
|
],
|
}
|
})
|
}
|
})
|
}
|
|
// 生成2023年8月内的随机时间
|
function generateRandomDate() {
|
const year = 2023
|
const month = 7 // 0-11,8月是7
|
const day = Math.floor(Math.random() * 31) + 1 // 1-31
|
const hour = Math.floor(Math.random() * 24) // 0-23
|
const minute = Math.floor(Math.random() * 60) // 0-59
|
|
const date = new Date(year, month, day, hour, minute)
|
return date.toISOString().slice(0, 16).replace('T', ' ')
|
}
|
|
// 随机选择数组元素
|
function getRandomElement(array) {
|
return array[Math.floor(Math.random() * array.length)]
|
}
|
|
// 生成随机评分和对应环信码等级
|
function generateRandomScore() {
|
const score = Math.floor(Math.random() * 101) // 0-100
|
let code
|
if (score >= 90) {
|
code = 'green'
|
} else if (score >= 60) {
|
code = 'yellow'
|
} else {
|
code = 'red'
|
}
|
return {
|
score,
|
code,
|
}
|
}
|
|
// 生成随机评分趋势
|
function generateRandomTrend() {
|
return Math.floor(Math.random() * 11) - 5 // -5 到 5
|
}
|
|
// 店铺数据
|
const shopList = ref([
|
{
|
id: 1,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'green',
|
score: 90,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [
|
{
|
time: generateRandomDate(),
|
content: '净化器运行时长不足',
|
score: 90,
|
handled: true,
|
},
|
],
|
},
|
{
|
id: 2,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'yellow',
|
score: 75,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [
|
{
|
time: generateRandomDate(),
|
content: '投诉次数较多',
|
score: 80,
|
handled: false,
|
},
|
],
|
},
|
{
|
id: 3,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'red',
|
score: 60,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [
|
{
|
time: generateRandomDate(),
|
content: '排放浓度超标',
|
score: 65,
|
handled: false,
|
},
|
{
|
time: generateRandomDate(),
|
content: '清洗频次不足',
|
score: 62,
|
handled: false,
|
},
|
],
|
},
|
{
|
id: 4,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'green',
|
score: 92,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [],
|
},
|
{
|
id: 5,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'yellow',
|
score: 78,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [
|
{
|
time: generateRandomDate(),
|
content: '风机联动率低',
|
score: 75,
|
handled: true,
|
},
|
],
|
},
|
{
|
id: 6,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'green',
|
score: 90,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [],
|
},
|
{
|
id: 7,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'red',
|
score: 55,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [
|
{
|
time: generateRandomDate(),
|
content: '未安装油烟净化设备',
|
score: 60,
|
handled: false,
|
},
|
],
|
},
|
{
|
id: 8,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'yellow',
|
score: 72,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [
|
{
|
time: generateRandomDate(),
|
content: '净化器清洗不及时',
|
score: 75,
|
handled: true,
|
},
|
],
|
},
|
{
|
id: 9,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'green',
|
score: 93,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [],
|
},
|
{
|
id: 10,
|
shopName: getRandomElement(shopNames),
|
district: '徐汇区',
|
town: getRandomElement(xuhuiTowns),
|
code: 'yellow',
|
score: 76,
|
trend: generateRandomTrend(),
|
lastUpdate: generateRandomDate(),
|
warnings: [
|
{
|
time: generateRandomDate(),
|
content: '排放浓度接近标准限值',
|
score: 78,
|
handled: true,
|
},
|
],
|
},
|
])
|
|
// 过滤后的店铺列表
|
const filteredShopList = computed(() => {
|
if (filterCode.value === 'all') {
|
return shopList.value
|
}
|
return shopList.value.filter((shop) => shop.code === filterCode.value)
|
})
|
|
// 分页后的店铺列表
|
const pagedShopList = computed(() => {
|
const start = (currentPage.value - 1) * pageSize.value
|
const end = start + pageSize.value
|
return filteredShopList.value.slice(start, end)
|
})
|
|
// 生命周期
|
onMounted(() => {
|
// 这里可以从API获取数据
|
console.log('环信码管理页面加载')
|
onSearch()
|
})
|
|
// 方法
|
function onBack() {
|
// 回退逻辑
|
console.log('回退')
|
}
|
|
function filterByCode(code) {
|
filterCode.value = code === filterCode.value ? 'all' : code
|
currentPage.value = 1 // 重置到第一页
|
}
|
|
// 分页方法
|
function handleSizeChange(size) {
|
pageSize.value = size
|
currentPage.value = 1
|
}
|
|
function handleCurrentChange(current) {
|
currentPage.value = current
|
}
|
|
function getCodeType(code) {
|
switch (code) {
|
case 'green':
|
return 'success'
|
case 'yellow':
|
return 'warning'
|
case 'red':
|
return 'danger'
|
default:
|
return ''
|
}
|
}
|
|
function getCodeText(code) {
|
switch (code) {
|
case 'green':
|
return '绿码'
|
case 'yellow':
|
return '黄码'
|
case 'red':
|
return '红码'
|
default:
|
return ''
|
}
|
}
|
|
// 雷达图和趋势图引用
|
const radarChart = ref(null)
|
const trendChart = ref(null)
|
let radarChartInstance = null
|
let trendChartInstance = null
|
|
function viewDetails(shop) {
|
selectedShop.value = shop
|
drawerVisible.value = true
|
|
// 获取环信码图片
|
if (shop.guid && shop.shopName) {
|
creditApi.fetchCodeUrl(shop.guid, shop.shopName).then((res) => {
|
if (res && res.url) {
|
codeImageUrl.value = res.url
|
}
|
})
|
}
|
|
// 延迟绘制图表,确保DOM已更新
|
setTimeout(() => {
|
drawRadarChart()
|
drawTrendChart()
|
}, 100)
|
}
|
|
// 绘制雷达图
|
function drawRadarChart() {
|
if (!radarChart.value) return
|
|
// 销毁旧实例
|
if (radarChartInstance) {
|
radarChartInstance.dispose()
|
}
|
|
// 初始化echarts实例
|
radarChartInstance = echarts.init(radarChart.value)
|
|
// 雷达图数据
|
const labels = [
|
'在线监测设备',
|
'净化设施设备',
|
'在线监测设备维护',
|
'净化设施设备维护',
|
'在线监测数据量级',
|
'空调和风机噪声',
|
'台站管理',
|
'信用承诺自评',
|
]
|
|
// 生成随机评分数据(实际项目中应从API获取)
|
const data = labels.map(() => Math.floor(Math.random() * 40) + 60) // 60-100分
|
|
// 配置项
|
const option = {
|
radar: {
|
indicator: labels.map((label) => ({
|
name: label,
|
max: 100,
|
})),
|
radius: '70%',
|
},
|
series: [
|
{
|
type: 'radar',
|
data: [
|
{
|
value: data,
|
name: '评分维度',
|
areaStyle: {
|
color: 'rgba(103, 194, 58, 0.2)',
|
},
|
lineStyle: {
|
color: '#67c23a',
|
},
|
itemStyle: {
|
color: '#67c23a',
|
},
|
},
|
],
|
},
|
],
|
}
|
|
// 渲染图表
|
radarChartInstance.setOption(option)
|
|
// 监听窗口大小变化
|
window.addEventListener('resize', () => {
|
radarChartInstance.resize()
|
})
|
}
|
|
// 绘制趋势图
|
function drawTrendChart() {
|
if (!trendChart.value) return
|
|
// 销毁旧实例
|
if (trendChartInstance) {
|
trendChartInstance.dispose()
|
}
|
|
// 初始化echarts实例
|
trendChartInstance = echarts.init(trendChart.value)
|
|
// 生成过去12个月的标签
|
const labels = []
|
const data = []
|
const now = dayjs()
|
|
for (let i = 11; i >= 0; i--) {
|
const date = now.subtract(i, 'month')
|
labels.push(date.format('YYYY-MM'))
|
// 生成随机评分数据(实际项目中应从API获取)
|
data.push(Math.floor(Math.random() * 30) + 70) // 70-100分
|
}
|
|
// 配置项
|
const option = {
|
tooltip: {
|
trigger: 'axis',
|
},
|
grid: {
|
left: '3%',
|
right: '4%',
|
bottom: '3%',
|
containLabel: true,
|
},
|
xAxis: {
|
type: 'category',
|
boundaryGap: false,
|
data: labels,
|
axisLabel: {
|
rotate: 45,
|
},
|
},
|
yAxis: {
|
type: 'value',
|
min: 60,
|
max: 100,
|
interval: 8,
|
},
|
series: [
|
{
|
name: '评分',
|
type: 'line',
|
data: data,
|
smooth: true,
|
lineStyle: {
|
color: '#409eff',
|
},
|
itemStyle: {
|
color: '#409eff',
|
},
|
areaStyle: {
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{
|
offset: 0,
|
color: 'rgba(64, 158, 255, 0.3)',
|
},
|
{
|
offset: 1,
|
color: 'rgba(64, 158, 255, 0.1)',
|
},
|
]),
|
},
|
},
|
],
|
}
|
|
// 渲染图表
|
trendChartInstance.setOption(option)
|
|
// 监听窗口大小变化
|
window.addEventListener('resize', () => {
|
trendChartInstance.resize()
|
})
|
}
|
|
function viewRiskWarnings(shop) {
|
selectedShop.value = shop
|
drawerVisible.value = true
|
// 这里可以滚动到风险预警部分
|
}
|
|
function openModelConfig() {
|
modelConfigVisible.value = true
|
}
|
|
function saveModelConfig() {
|
// 保存配置逻辑
|
modelConfigVisible.value = false
|
console.log('保存评分模型配置')
|
}
|
</script>
|
|
<style scoped>
|
.huanxin-code-manage {
|
padding: 20px;
|
}
|
|
.dashboard {
|
margin-bottom: 30px;
|
}
|
|
.dashboard-card {
|
cursor: pointer;
|
transition: all 0.3s ease;
|
}
|
|
.dashboard-card:hover {
|
transform: translateY(-5px);
|
}
|
|
.card-content {
|
text-align: center;
|
padding: 20px 0;
|
}
|
|
.card-title {
|
font-size: 16px;
|
margin-bottom: 10px;
|
}
|
|
.card-value {
|
font-size: 32px;
|
font-weight: bold;
|
margin-bottom: 5px;
|
}
|
|
.card-percentage {
|
font-size: 14px;
|
opacity: 0.8;
|
}
|
|
.green-card .card-value {
|
color: #67c23a;
|
}
|
|
.yellow-card .card-value {
|
color: #e6a23c;
|
}
|
|
.red-card .card-value {
|
color: #f56c6c;
|
}
|
|
.model-config {
|
text-align: right;
|
margin-bottom: 20px;
|
}
|
|
.shop-list {
|
margin-bottom: 20px;
|
}
|
|
.pagination {
|
margin-top: 20px;
|
text-align: right;
|
}
|
|
.trend {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.trend-icon {
|
margin-right: 5px;
|
}
|
|
.trend-icon.up {
|
color: #67c23a;
|
}
|
|
.trend-icon.down {
|
color: #f56c6c;
|
}
|
|
.trend .up {
|
color: #67c23a;
|
}
|
|
.trend .down {
|
color: #f56c6c;
|
}
|
|
.map-container {
|
position: relative;
|
height: 600px;
|
border: 1px solid #e4e7ed;
|
border-radius: 4px;
|
}
|
|
.map-placeholder {
|
height: 100%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.map-legend {
|
position: absolute;
|
bottom: 20px;
|
right: 20px;
|
background: white;
|
padding: 10px;
|
border-radius: 4px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
}
|
|
.legend-item {
|
display: flex;
|
align-items: center;
|
margin-bottom: 5px;
|
}
|
|
.legend-dot {
|
width: 12px;
|
height: 12px;
|
border-radius: 50%;
|
margin-right: 8px;
|
}
|
|
.legend-dot.green {
|
background-color: #67c23a;
|
}
|
|
.legend-dot.yellow {
|
background-color: #e6a23c;
|
}
|
|
.legend-dot.red {
|
background-color: #f56c6c;
|
}
|
|
.shop-details {
|
padding: 20px;
|
}
|
|
.code-header {
|
display: flex;
|
align-items: flex-start;
|
flex-direction: column;
|
margin-bottom: 20px;
|
}
|
|
.code-icon {
|
margin-right: 30px;
|
}
|
|
.score-info {
|
flex: 1;
|
}
|
|
.score-label {
|
font-size: 16px;
|
margin-bottom: 5px;
|
}
|
|
.score-value {
|
font-size: 48px;
|
font-weight: bold;
|
}
|
|
.chart-section {
|
margin-bottom: 20px;
|
}
|
|
.chart-section h3 {
|
margin-bottom: 10px;
|
font-size: 16px;
|
}
|
|
.radar-chart {
|
width: 500px;
|
height: 500px;
|
border: 1px solid #e4e7ed;
|
border-radius: 4px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.trend-chart {
|
height: 350px;
|
border: 1px solid #e4e7ed;
|
border-radius: 4px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.warning-section {
|
margin-top: 20px;
|
}
|
|
.warning-section h3 {
|
margin-bottom: 10px;
|
font-size: 16px;
|
}
|
|
.image {
|
width: 300px;
|
/* height: 250px; */
|
border-radius: 4px;
|
margin-bottom: 6px;
|
}
|
</style>
|