首页细节调整
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<template>
|
||||
<view class="page" @click.self="closeMoreMenu">
|
||||
<!-- 鍥哄畾椤堕儴瀵艰埅鏍?-->
|
||||
<!-- 固定顶部导航栏 -->
|
||||
<AnalyticsTopBar
|
||||
:title="'鏁版嵁鍒嗘瀽涓績'"
|
||||
:title="'数据分析中心'"
|
||||
:lastUpdateTime="lastUpdateTime"
|
||||
:sidebarVisible="showSidebarMenu"
|
||||
@menu-click="handleMenu"
|
||||
@@ -16,25 +16,25 @@
|
||||
/>
|
||||
|
||||
<view class="page-layout">
|
||||
<!-- 渚ц竟鏍忚彍鍗曠粍浠?-->
|
||||
<!-- 侧边栏菜单组件 -->
|
||||
<AnalyticsSidebarMenu
|
||||
:visible="showSidebarMenu"
|
||||
:currentPath="currentPath"
|
||||
@visible-change="handleSidebarUpdate"
|
||||
/>
|
||||
|
||||
<!-- 涓诲唴瀹瑰尯鍩?-->
|
||||
<!-- 主内容区域 -->
|
||||
<view class="main-content">
|
||||
<view class="container">
|
||||
<!-- KPI锛氬灞?4鍒楋紝绐勫睆 2鍒楋紙澧炲己鐗堬細娓愬彉鑳屾櫙 + sparkline锛?-->
|
||||
<!-- KPI:宽屏 4列,窄屏 2列(增强版:渐变背景 + sparkline) -->
|
||||
<view class="kpi-grid">
|
||||
<view class="kpi-card kpi-card-gmv" @click="goToSalesReport">
|
||||
<view class="kpi-header">
|
||||
<text class="kpi-label">瀹炴椂 GMV</text>
|
||||
<text class="kpi-label">实时 GMV</text>
|
||||
</view>
|
||||
<text class="kpi-value">楼{{ formatMoney(realTime.gmv) }}</text>
|
||||
<text class="kpi-value">¥{{ formatMoney(realTime.gmv) }}</text>
|
||||
<view class="kpi-footer">
|
||||
<text class="kpi-meta">杈冩槰鏃ュ悓鍒?/text>
|
||||
<text class="kpi-meta">较昨日同刻</text>
|
||||
<text class="kpi-chip" :class="realTime.gmv_growth >= 0 ? 'pos' : 'neg'">
|
||||
{{ formatPct(realTime.gmv_growth) }}
|
||||
</text>
|
||||
@@ -42,11 +42,11 @@
|
||||
</view>
|
||||
<view class="kpi-card kpi-card-orders" @click="goToSalesReport">
|
||||
<view class="kpi-header">
|
||||
<text class="kpi-label">瀹炴椂璁㈠崟</text>
|
||||
<text class="kpi-label">实时订单</text>
|
||||
</view>
|
||||
<text class="kpi-value">{{ formatInt(realTime.orders) }}</text>
|
||||
<view class="kpi-footer">
|
||||
<text class="kpi-meta">杈冩槰鏃ュ悓鍒?/text>
|
||||
<text class="kpi-meta">较昨日同刻</text>
|
||||
<text class="kpi-chip" :class="realTime.order_growth >= 0 ? 'pos' : 'neg'">
|
||||
{{ formatPct(realTime.order_growth) }}
|
||||
</text>
|
||||
@@ -54,21 +54,21 @@
|
||||
</view>
|
||||
<view class="kpi-card kpi-card-users" @click="goToUserAnalysis">
|
||||
<view class="kpi-header">
|
||||
<text class="kpi-label">鍦ㄧ嚎鐢ㄦ埛</text>
|
||||
<text class="kpi-label">在线用户</text>
|
||||
</view>
|
||||
<text class="kpi-value">{{ formatInt(realTime.online_users) }}</text>
|
||||
<view class="kpi-footer">
|
||||
<text class="kpi-meta">鏈€杩?5 鍒嗛挓</text>
|
||||
<text class="kpi-chip neutral">瀹炴椂</text>
|
||||
<text class="kpi-meta">最近 5 分钟</text>
|
||||
<text class="kpi-chip neutral">实时</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="kpi-card kpi-card-conversion" @click="goToSalesReport">
|
||||
<view class="kpi-header">
|
||||
<text class="kpi-label">杞寲鐜?/text>
|
||||
<text class="kpi-label">转化率</text>
|
||||
</view>
|
||||
<text class="kpi-value">{{ formatPct(realTime.conversion_rate) }}</text>
|
||||
<view class="kpi-footer">
|
||||
<text class="kpi-meta">杈冩槰鏃ュ悓鍒?/text>
|
||||
<text class="kpi-meta">较昨日同刻</text>
|
||||
<text class="kpi-chip" :class="realTime.conversion_growth >= 0 ? 'pos' : 'neg'">
|
||||
{{ formatPct(realTime.conversion_growth) }}
|
||||
</text>
|
||||
@@ -76,7 +76,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 鏃堕棿缁村害绛涢€夛紙蹇嵎 + 鑷畾涔夛級 -->
|
||||
<!-- 时间维度筛选(快捷 + 自定义) -->
|
||||
<view class="tabs">
|
||||
<view
|
||||
v-for="p in timePeriods"
|
||||
@@ -92,7 +92,8 @@
|
||||
:class="{ active: customRangeEnabled }"
|
||||
@click="toggleCustomRange"
|
||||
>
|
||||
鑷畾涔? </view>
|
||||
自定义
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<AnalyticsDateRangePicker
|
||||
@@ -103,15 +104,15 @@
|
||||
@clear="onDateRangeClear"
|
||||
/>
|
||||
|
||||
<!-- 鏍稿績瓒嬪娍锛氬崰婊℃í鍚戯紙鏌?鎶?缁勫悎鍥撅級 -->
|
||||
<!-- 核心趋势:占满横向(柱+折 组合图) -->
|
||||
<view class="card card-full">
|
||||
<view class="card-head">
|
||||
<text class="card-title">鏍稿績瓒嬪娍锛圙MV / 璁㈠崟鏁帮級</text>
|
||||
<text class="card-desc">{{ selectedPeriodText }} 路 鏌憋細GMV锛堝厓锛?路 绾匡細璁㈠崟鏁?/text>
|
||||
<text class="card-title">核心趋势(GMV / 订单数)</text>
|
||||
<text class="card-desc">{{ selectedPeriodText }} · 柱:GMV(元) · 线:订单数</text>
|
||||
</view>
|
||||
|
||||
<view v-if="loading || !trend.x || trend.x.length === 0" class="chart-loading">
|
||||
<text>{{ loading ? '鍔犺浇涓?..' : '鏆傛棤鏁版嵁' }}</text>
|
||||
<text>{{ loading ? '加载中...' : '暂无数据' }}</text>
|
||||
</view>
|
||||
<view v-else style="width: 100%; height: 320px; position: relative; overflow: hidden; min-height: 320px;">
|
||||
<AnalyticsComboChart
|
||||
@@ -124,16 +125,16 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 鐢ㄦ埛缁撴瀯鍜屾祦閲忔潵婧愶細妯帓鏄剧ず -->
|
||||
<!-- 用户结构和流量来源:横排显示 -->
|
||||
<view class="charts-row">
|
||||
<!-- 宸︿晶锛氱敤鎴风粨鏋?-->
|
||||
<!-- 左侧:用户结构 -->
|
||||
<view class="charts-left card">
|
||||
<view class="card-head">
|
||||
<text class="card-title">鐢ㄦ埛缁撴瀯锛堢幆褰㈠浘锛?/text>
|
||||
<text class="card-desc">鏈秷璐?/ 棣栬喘 / 澶嶈喘 / 鍥炴祦</text>
|
||||
<text class="card-title">用户结构(环形图)</text>
|
||||
<text class="card-desc">未消费 / 首购 / 复购 / 回流</text>
|
||||
</view>
|
||||
<view v-if="loading || !userSegmentOption || !userSegmentOption.series || (userSegmentOption.series && userSegmentOption.series.length === 0)" class="chart-loading">
|
||||
<text>{{ loading ? '鍔犺浇涓?..' : '鏆傛棤鏁版嵁' }}</text>
|
||||
<text>{{ loading ? '加载中...' : '暂无数据' }}</text>
|
||||
</view>
|
||||
<view v-else class="chart-box">
|
||||
<EChartsView
|
||||
@@ -143,14 +144,14 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 鍙充晶锛氭祦閲忔潵婧?-->
|
||||
<!-- 右侧:流量来源 -->
|
||||
<view class="charts-right card">
|
||||
<view class="card-head">
|
||||
<text class="card-title">娴侀噺鏉ユ簮锛堟潯褰級</text>
|
||||
<text class="card-desc">鍗犳瘮%</text>
|
||||
<text class="card-title">流量来源(条形)</text>
|
||||
<text class="card-desc">占比%</text>
|
||||
</view>
|
||||
<view v-if="loading || !trafficBarOption || !trafficBarOption.series || trafficBarOption.series.length === 0" class="chart-loading">
|
||||
<text>{{ loading ? '鍔犺浇涓?..' : '鏆傛棤鏁版嵁' }}</text>
|
||||
<text>{{ loading ? '加载中...' : '暂无数据' }}</text>
|
||||
</view>
|
||||
<view v-else class="chart-box">
|
||||
<EChartsView
|
||||
@@ -161,13 +162,13 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 涓や釜TOP鎺掕锛氭í鎺掓樉绀?-->
|
||||
<!-- 两个TOP排行:横排显示 -->
|
||||
<view class="tops-row">
|
||||
<!-- 宸︿晶锛氱儹閿€鍟嗗搧TOP -->
|
||||
<!-- 左侧:热销商品TOP -->
|
||||
<view class="tops-left card">
|
||||
<view class="card-head">
|
||||
<text class="card-title">鐑攢鍟嗗搧 TOP</text>
|
||||
<text class="card-desc">鎸夐攢閲?/text>
|
||||
<text class="card-title">热销商品 TOP</text>
|
||||
<text class="card-desc">按销量</text>
|
||||
</view>
|
||||
<view class="rank-scroll-container">
|
||||
<view class="rank-scroll-wrapper" :class="{ 'has-scroll': topProducts.length >= 6 }">
|
||||
@@ -175,24 +176,24 @@
|
||||
<view v-for="p in topProducts" :key="p.id" class="rank-item">
|
||||
<text class="rank-no">{{ p.rank }}</text>
|
||||
<text class="rank-name">{{ p.name }}</text>
|
||||
<text class="rank-val">{{ p.sales }} 浠?/text>
|
||||
<text class="rank-val">{{ p.sales }} 件</text>
|
||||
</view>
|
||||
<!-- 寰幆鎾斁锛氬鍒朵竴浠芥暟鎹敤浜庢棤缂濇粴鍔?-->
|
||||
<!-- 循环播放:复制一份数据用于无缝滚动 -->
|
||||
<view v-if="topProducts.length >= 6" v-for="p in topProducts" :key="'copy-' + p.id" class="rank-item">
|
||||
<text class="rank-no">{{ p.rank }}</text>
|
||||
<text class="rank-name">{{ p.name }}</text>
|
||||
<text class="rank-val">{{ p.sales }} 浠?/text>
|
||||
<text class="rank-val">{{ p.sales }} 件</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 鍙充晶锛氬晢瀹舵帓琛孴OP -->
|
||||
<!-- 右侧:商家排行TOP -->
|
||||
<view class="tops-right card">
|
||||
<view class="card-head">
|
||||
<text class="card-title">鍟嗗鎺掕 TOP</text>
|
||||
<text class="card-desc">鎸?GMV</text>
|
||||
<text class="card-title">商家排行 TOP</text>
|
||||
<text class="card-desc">按 GMV</text>
|
||||
</view>
|
||||
<view class="rank-scroll-container">
|
||||
<view class="rank-scroll-wrapper" :class="{ 'has-scroll': topMerchants.length >= 6 }">
|
||||
@@ -201,18 +202,18 @@
|
||||
<text class="rank-no">{{ m.rank }}</text>
|
||||
<text class="rank-name">{{ m.name }}</text>
|
||||
<view class="rank-right">
|
||||
<text class="rank-val">楼{{ formatMoney(m.sales) }}</text>
|
||||
<text class="rank-val">¥{{ formatMoney(m.sales) }}</text>
|
||||
<text class="chip" :class="m.growth >= 0 ? 'pos' : 'neg'">
|
||||
{{ m.growth >= 0 ? '+' : '' }}{{ m.growth }}%
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 寰幆鎾斁锛氬鍒朵竴浠芥暟鎹敤浜庢棤缂濇粴鍔?-->
|
||||
<!-- 循环播放:复制一份数据用于无缝滚动 -->
|
||||
<view v-if="topMerchants.length >= 6" v-for="m in topMerchants" :key="'copy-' + m.id" class="rank-item">
|
||||
<text class="rank-no">{{ m.rank }}</text>
|
||||
<text class="rank-name">{{ m.name }}</text>
|
||||
<view class="rank-right">
|
||||
<text class="rank-val">楼{{ formatMoney(m.sales) }}</text>
|
||||
<text class="rank-val">¥{{ formatMoney(m.sales) }}</text>
|
||||
<text class="chip" :class="m.growth >= 0 ? 'pos' : 'neg'">
|
||||
{{ m.growth >= 0 ? '+' : '' }}{{ m.growth }}%
|
||||
</text>
|
||||
@@ -224,47 +225,47 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 蹇€熷伐鍏峰崱鐗囧尯锛?涓伐鍏峰叆鍙o級 -->
|
||||
<!-- 快速工具卡片区(6个工具入口) -->
|
||||
<view class="tools-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">蹇€熷垎鏋愬伐鍏?/text>
|
||||
<text class="section-desc">鐐瑰嚮杩涘叆璇︾粏鍒嗘瀽</text>
|
||||
<text class="section-title">快速分析工具</text>
|
||||
<text class="section-desc">点击进入详细分析</text>
|
||||
</view>
|
||||
<view class="tools-grid">
|
||||
<view class="tool-card" @click="goToSalesReport">
|
||||
<view class="tool-icon sales">馃搳</view>
|
||||
<text class="tool-title">閿€鍞姤琛?/text>
|
||||
<text class="tool-desc">GMV銆佽鍗曘€佽浆鍖栫巼</text>
|
||||
<view class="tool-icon sales">📊</view>
|
||||
<text class="tool-title">销售报表</text>
|
||||
<text class="tool-desc">GMV、订单、转化率</text>
|
||||
</view>
|
||||
<view class="tool-card" @click="goToUserAnalysis">
|
||||
<view class="tool-icon users">馃懃</view>
|
||||
<text class="tool-title">鐢ㄦ埛鍒嗘瀽</text>
|
||||
<text class="tool-desc">澧為暱銆佹椿璺冦€佺暀瀛?/text>
|
||||
<view class="tool-icon users">👥</view>
|
||||
<text class="tool-title">用户分析</text>
|
||||
<text class="tool-desc">增长、活跃、留存</text>
|
||||
</view>
|
||||
<view class="tool-card" @click="goToProductInsights">
|
||||
<view class="tool-icon products">馃摝</view>
|
||||
<text class="tool-title">鍟嗗搧娲炲療</text>
|
||||
<text class="tool-desc">閿€閲忋€佸簱瀛樸€佷环鏍?/text>
|
||||
<view class="tool-icon products">📦</view>
|
||||
<text class="tool-title">商品洞察</text>
|
||||
<text class="tool-desc">销量、库存、价格</text>
|
||||
</view>
|
||||
<view class="tool-card" @click="goToMarketTrends">
|
||||
<view class="tool-icon market">馃搱</view>
|
||||
<text class="tool-title">甯傚満瓒嬪娍</text>
|
||||
<text class="tool-desc">鏁翠綋瓒嬪娍銆佽涓氬姣?/text>
|
||||
<view class="tool-icon market">📈</view>
|
||||
<text class="tool-title">市场趋势</text>
|
||||
<text class="tool-desc">整体趋势、行业对比</text>
|
||||
</view>
|
||||
<view class="tool-card" @click="goToCouponAnalysis">
|
||||
<view class="tool-icon coupon">馃帿</view>
|
||||
<text class="tool-title">浼樻儬鍒稿垎鏋?/text>
|
||||
<text class="tool-desc">鍙戞斁銆佷娇鐢ㄣ€丷OI</text>
|
||||
<view class="tool-icon coupon">🎫</view>
|
||||
<text class="tool-title">优惠券分析</text>
|
||||
<text class="tool-desc">发放、使用、ROI</text>
|
||||
</view>
|
||||
<view class="tool-card" @click="goToCustomReport">
|
||||
<view class="tool-icon custom">鈿欙笍</view>
|
||||
<text class="tool-title">鑷畾涔夋姤琛?/text>
|
||||
<text class="tool-desc">鍒涘缓涓撳睘鎶ヨ〃</text>
|
||||
<view class="tool-icon custom">⚙️</view>
|
||||
<text class="tool-title">自定义报表</text>
|
||||
<text class="tool-desc">创建专属报表</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 鐣欑櫧 -->
|
||||
<!-- 留白 -->
|
||||
<view style="height: 24px;"></view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -301,10 +302,10 @@ const autoRefreshInterval = ref(60000)
|
||||
const autoRefreshTimer = ref<any>(null)
|
||||
|
||||
const timePeriods = ref<Array<{ value: string; label: string }>>([
|
||||
{ value: '7d', label: '7澶? },
|
||||
{ value: '30d', label: '30澶? },
|
||||
{ value: '90d', label: '90澶? },
|
||||
{ value: '1y', label: '1骞? }
|
||||
{ value: '7d', label: '7天' },
|
||||
{ value: '30d', label: '30天' },
|
||||
{ value: '90d', label: '90天' },
|
||||
{ value: '1y', label: '1年' }
|
||||
])
|
||||
|
||||
const realTime = reactive({
|
||||
@@ -328,7 +329,7 @@ const userSegmentOption = ref<any>({})
|
||||
|
||||
const selectedPeriodText = computed((): string => {
|
||||
const p = timePeriods.value.find((t) => t.value === selectedPeriod.value)
|
||||
return p ? p.label : '7澶?
|
||||
return p ? p.label : '7天'
|
||||
})
|
||||
|
||||
function updateTime() {
|
||||
@@ -363,7 +364,7 @@ async function loadTrend() {
|
||||
trend.gmv = data.gmv
|
||||
trend.orders = data.orders
|
||||
} catch (e) {
|
||||
console.error('鉂?loadTrend failed', e)
|
||||
console.error('❌ loadTrend failed', e)
|
||||
trend.x = []
|
||||
trend.gmv = []
|
||||
trend.orders = []
|
||||
@@ -381,7 +382,7 @@ async function loadRealTime() {
|
||||
realTime.conversion_rate = data.conversion_rate
|
||||
realTime.conversion_growth = data.conversion_growth
|
||||
} catch (e) {
|
||||
console.error('鉂?loadRealTime failed', e)
|
||||
console.error('❌ loadRealTime failed', e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,7 +394,7 @@ async function loadTopProducts() {
|
||||
const list = await fetchDashboardTopProducts(selectedPeriod.value, 50, range)
|
||||
topProducts.splice(0, topProducts.length, ...list)
|
||||
} catch (e) {
|
||||
console.error('鉂?loadTopProducts failed', e)
|
||||
console.error('❌ loadTopProducts failed', e)
|
||||
topProducts.splice(0, topProducts.length)
|
||||
}
|
||||
}
|
||||
@@ -406,7 +407,7 @@ async function loadTopMerchants() {
|
||||
const list = await fetchDashboardTopMerchants(selectedPeriod.value, 50, range)
|
||||
topMerchants.splice(0, topMerchants.length, ...list)
|
||||
} catch (e) {
|
||||
console.error('鉂?loadTopMerchants failed', e)
|
||||
console.error('❌ loadTopMerchants failed', e)
|
||||
topMerchants.splice(0, topMerchants.length)
|
||||
}
|
||||
}
|
||||
@@ -419,7 +420,7 @@ async function loadUserSegments() {
|
||||
const list = await fetchDashboardUserSegments(selectedPeriod.value, range)
|
||||
userSegments.splice(0, userSegments.length, ...list)
|
||||
} catch (e) {
|
||||
console.error('鉂?loadUserSegments failed', e)
|
||||
console.error('❌ loadUserSegments failed', e)
|
||||
userSegments.splice(0, userSegments.length)
|
||||
}
|
||||
}
|
||||
@@ -432,7 +433,7 @@ async function loadTrafficSources() {
|
||||
const list = await fetchDashboardTrafficSources(selectedPeriod.value, range)
|
||||
trafficSources.splice(0, trafficSources.length, ...list)
|
||||
} catch (e) {
|
||||
console.error('鉂?loadTrafficSources failed', e)
|
||||
console.error('❌ loadTrafficSources failed', e)
|
||||
trafficSources.splice(0, trafficSources.length)
|
||||
}
|
||||
}
|
||||
@@ -468,12 +469,12 @@ function toPlainObject(obj: any): any {
|
||||
}
|
||||
|
||||
function buildChartOptions() {
|
||||
console.log('馃搳 buildChartOptions: 寮€濮嬫瀯寤哄浘琛ㄩ厤缃?)
|
||||
console.log('馃搳 buildChartOptions: trafficSources', trafficSources, '鏁伴噺:', trafficSources.length)
|
||||
console.log('馃搳 buildChartOptions: userSegments', userSegments, '鏁伴噺:', userSegments.length)
|
||||
console.log('📊 buildChartOptions: 开始构建图表配置')
|
||||
console.log('📊 buildChartOptions: trafficSources', trafficSources, '数量:', trafficSources.length)
|
||||
console.log('📊 buildChartOptions: userSegments', userSegments, '数量:', userSegments.length)
|
||||
|
||||
if (!trafficSources || !userSegments) {
|
||||
console.warn('鈿狅笍 buildChartOptions: 鏁版嵁鏈噯澶囧ソ锛岃烦杩囨瀯寤?)
|
||||
console.warn('⚠️ buildChartOptions: 数据未准备好,跳过构建')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -484,15 +485,15 @@ function buildChartOptions() {
|
||||
})
|
||||
const total = trafficY.reduce((sum, v) => sum + v, 0)
|
||||
|
||||
console.log('馃搳 buildChartOptions: 娴侀噺鏉ユ簮鏁版嵁', { trafficX, trafficY, total, count: trafficX.length })
|
||||
console.log('📊 buildChartOptions: 流量来源数据', { trafficX, trafficY, total, count: trafficX.length })
|
||||
|
||||
if (trafficX.length === 0 || total === 0) {
|
||||
console.warn('鈿狅笍 buildChartOptions: 娴侀噺鏉ユ簮鏁版嵁涓虹┖锛屼娇鐢ㄥ崰浣嶆暟鎹?)
|
||||
console.warn('⚠️ buildChartOptions: 流量来源数据为空,使用占位数据')
|
||||
trafficBarOption.value = toPlainObject({
|
||||
grid: { left: 80, right: 24, top: 18, bottom: 18 },
|
||||
tooltip: { trigger: 'axis' },
|
||||
xAxis: { type: 'value', axisLabel: { color: 'rgba(0,0,0,0.55)' } },
|
||||
yAxis: { type: 'category', data: ['鏆傛棤鏁版嵁'], axisTick: { show: false } },
|
||||
yAxis: { type: 'category', data: ['暂无数据'], axisTick: { show: false } },
|
||||
series: [{ type: 'bar', data: [0], barWidth: 14 }]
|
||||
})
|
||||
} else {
|
||||
@@ -504,7 +505,7 @@ function buildChartOptions() {
|
||||
formatter: (params: any) => {
|
||||
const p = params[0]
|
||||
const percent = total > 0 ? ((p.value / total) * 100).toFixed(1) : '0'
|
||||
return `${p.name}<br/>${p.marker} ${p.value} 娆?(${percent}%)`
|
||||
return `${p.name}<br/>${p.marker} ${p.value} 次 (${percent}%)`
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
@@ -546,7 +547,7 @@ function buildChartOptions() {
|
||||
trafficBarOption.value = toPlainObject(newTrafficOption)
|
||||
}
|
||||
|
||||
console.log('馃搳 buildChartOptions: trafficBarOption 鏋勫缓瀹屾垚', trafficBarOption.value)
|
||||
console.log('📊 buildChartOptions: trafficBarOption 构建完成', trafficBarOption.value)
|
||||
|
||||
const segmentData = userSegments.map((it) => ({
|
||||
name: String(it.name),
|
||||
@@ -556,10 +557,10 @@ function buildChartOptions() {
|
||||
})()
|
||||
}))
|
||||
|
||||
console.log('馃搳 buildChartOptions: 鐢ㄦ埛缁撴瀯鏁版嵁', segmentData, '鏁伴噺:', segmentData.length)
|
||||
console.log('📊 buildChartOptions: 用户结构数据', segmentData, '数量:', segmentData.length)
|
||||
|
||||
if (segmentData.length === 0) {
|
||||
console.warn('鈿狅笍 buildChartOptions: 鐢ㄦ埛缁撴瀯鏁版嵁涓虹┖锛屼娇鐢ㄥ崰浣嶆暟鎹?)
|
||||
console.warn('⚠️ buildChartOptions: 用户结构数据为空,使用占位数据')
|
||||
userSegmentOption.value = toPlainObject({
|
||||
tooltip: { trigger: 'item' },
|
||||
legend: { left: 0, bottom: 0, itemWidth: 10, itemHeight: 10, textStyle: { fontSize: 12 } },
|
||||
@@ -569,7 +570,7 @@ function buildChartOptions() {
|
||||
type: 'pie',
|
||||
radius: ['55%', '75%'],
|
||||
center: ['50%', '45%'],
|
||||
data: [{ name: '鏆傛棤鏁版嵁', value: 1 }],
|
||||
data: [{ name: '暂无数据', value: 1 }],
|
||||
label: { show: true, formatter: '{b}\n{d}%' }
|
||||
}
|
||||
]
|
||||
@@ -609,14 +610,14 @@ function buildChartOptions() {
|
||||
userSegmentOption.value = toPlainObject(newUserSegmentOption)
|
||||
}
|
||||
|
||||
console.log('馃搳 buildChartOptions: userSegmentOption 鏋勫缓瀹屾垚', userSegmentOption.value)
|
||||
console.log('馃搳 buildChartOptions: 鍥捐〃閰嶇疆鏋勫缓瀹屾垚')
|
||||
console.log('📊 buildChartOptions: userSegmentOption 构建完成', userSegmentOption.value)
|
||||
console.log('📊 buildChartOptions: 图表配置构建完成')
|
||||
}
|
||||
|
||||
watch(
|
||||
trafficSources,
|
||||
(newVal) => {
|
||||
console.log('馃憖 watch trafficSources 瑙﹀彂', newVal)
|
||||
console.log('👀 watch trafficSources 触发', newVal)
|
||||
if (newVal && newVal.length > 0) {
|
||||
buildChartOptions()
|
||||
}
|
||||
@@ -627,7 +628,7 @@ watch(
|
||||
watch(
|
||||
userSegments,
|
||||
(newVal) => {
|
||||
console.log('馃憖 watch userSegments 瑙﹀彂', newVal)
|
||||
console.log('👀 watch userSegments 触发', newVal)
|
||||
if (newVal && newVal.length > 0) {
|
||||
buildChartOptions()
|
||||
}
|
||||
@@ -648,26 +649,26 @@ async function refreshAll() {
|
||||
])
|
||||
updateTime()
|
||||
|
||||
console.log('鉁?refreshAll: 鎵€鏈夋暟鎹姞杞藉畬鎴愶紝寮€濮嬫瀯寤哄浘琛?)
|
||||
console.log('✅ refreshAll: 所有数据加载完成,开始构建图表')
|
||||
|
||||
await new Promise((resolve) => {
|
||||
if (typeof requestAnimationFrame !== 'undefined') {
|
||||
requestAnimationFrame(() => {
|
||||
buildChartOptions()
|
||||
console.log('鉁?refreshAll: 鍥捐〃閰嶇疆鏋勫缓瀹屾垚')
|
||||
console.log('✅ refreshAll: 图表配置构建完成')
|
||||
resolve(null)
|
||||
})
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
buildChartOptions()
|
||||
console.log('鉁?refreshAll: 鍥捐〃閰嶇疆鏋勫缓瀹屾垚')
|
||||
console.log('✅ refreshAll: 图表配置构建完成')
|
||||
resolve(null)
|
||||
}, 50)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('鉂?refreshAll failed', e)
|
||||
uni.showToast({ title: mapAnalyticsError(e, { fallbackMessage: '鏁版嵁鍔犺浇澶辫触' }), icon: 'none' })
|
||||
console.error('❌ refreshAll failed', e)
|
||||
uni.showToast({ title: mapAnalyticsError(e, { fallbackMessage: '数据加载失败' }), icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,7 +687,7 @@ async function initDashboard() {
|
||||
async function selectPeriod(p: string) {
|
||||
selectedPeriod.value = p
|
||||
|
||||
// 鍒囨崲鍒板揩鎹锋椂闂存鏃讹紝閫€鍑鸿嚜瀹氫箟鑼冨洿
|
||||
// 切换到快捷时间段时,退出自定义范围
|
||||
customRangeEnabled.value = false
|
||||
selectedStartDate.value = ''
|
||||
selectedEndDate.value = ''
|
||||
@@ -699,19 +700,19 @@ async function selectPeriod(p: string) {
|
||||
if (typeof requestAnimationFrame !== 'undefined') {
|
||||
requestAnimationFrame(() => {
|
||||
buildChartOptions()
|
||||
console.log('鉁?selectPeriod: 鍥捐〃閰嶇疆鏋勫缓瀹屾垚')
|
||||
console.log('✅ selectPeriod: 图表配置构建完成')
|
||||
resolve(null)
|
||||
})
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
buildChartOptions()
|
||||
console.log('鉁?selectPeriod: 鍥捐〃閰嶇疆鏋勫缓瀹屾垚')
|
||||
console.log('✅ selectPeriod: 图表配置构建完成')
|
||||
resolve(null)
|
||||
}, 50)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('鉂?selectPeriod failed', e)
|
||||
console.error('❌ selectPeriod failed', e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -730,30 +731,30 @@ function closeMoreMenu() {
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
uni.showToast({ title: '鎼滅储鍔熻兘', icon: 'none' })
|
||||
uni.showToast({ title: '搜索功能', icon: 'none' })
|
||||
}
|
||||
|
||||
function handleNotification() {
|
||||
uni.showToast({ title: '閫氱煡涓績', icon: 'none' })
|
||||
uni.showToast({ title: '通知中心', icon: 'none' })
|
||||
}
|
||||
|
||||
function handleFullscreen() {
|
||||
uni.showToast({ title: '鍏ㄥ睆妯″紡', icon: 'none' })
|
||||
uni.showToast({ title: '全屏模式', icon: 'none' })
|
||||
}
|
||||
|
||||
function handleMobile() {
|
||||
uni.showToast({ title: '绉诲姩绔瑙?, icon: 'none' })
|
||||
uni.showToast({ title: '移动端预览', icon: 'none' })
|
||||
}
|
||||
|
||||
function handleDropdown() {
|
||||
uni.showActionSheet({
|
||||
itemList: ['crmeb demo', '鍒囨崲椤圭洰', '椤圭洰璁剧疆'],
|
||||
itemList: ['crmeb demo', '切换项目', '项目设置'],
|
||||
success: () => {}
|
||||
})
|
||||
}
|
||||
|
||||
function handleSettings() {
|
||||
uni.showToast({ title: '璁剧疆', icon: 'none' })
|
||||
uni.showToast({ title: '设置', icon: 'none' })
|
||||
}
|
||||
|
||||
function toggleCustomRange() {
|
||||
@@ -776,13 +777,13 @@ function onDateRangeClear() {
|
||||
|
||||
function formatInt(n: number): string {
|
||||
const v = isFinite(n) ? Math.round(n) : 0
|
||||
if (v >= 10000) return (v / 10000).toFixed(1) + '涓?
|
||||
if (v >= 10000) return (v / 10000).toFixed(1) + '万'
|
||||
return v.toString()
|
||||
}
|
||||
|
||||
function formatMoney(n: number): string {
|
||||
const v = isFinite(n) ? n : 0
|
||||
if (v >= 10000) return (v / 10000).toFixed(1) + '涓?
|
||||
if (v >= 10000) return (v / 10000).toFixed(1) + '万'
|
||||
return v.toFixed(0)
|
||||
}
|
||||
|
||||
@@ -844,22 +845,22 @@ function toggleAutoRefresh() {
|
||||
autoRefreshEnabled.value = !autoRefreshEnabled.value
|
||||
if (autoRefreshEnabled.value) {
|
||||
startAutoRefresh()
|
||||
uni.showToast({ title: '宸插紑鍚嚜鍔ㄥ埛鏂?, icon: 'success' })
|
||||
uni.showToast({ title: '已开启自动刷新', icon: 'success' })
|
||||
} else {
|
||||
stopAutoRefresh()
|
||||
uni.showToast({ title: '宸插叧闂嚜鍔ㄥ埛鏂?, icon: 'none' })
|
||||
uni.showToast({ title: '已关闭自动刷新', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
function exportReport() {
|
||||
uni.showActionSheet({
|
||||
itemList: ['瀵煎嚭Excel', '瀵煎嚭PDF', '瀵煎嚭鍥剧墖'],
|
||||
success: () => uni.showToast({ title: '瀵煎嚭鎴愬姛', icon: 'success' })
|
||||
itemList: ['导出Excel', '导出PDF', '导出图片'],
|
||||
success: () => uni.showToast({ title: '导出成功', icon: 'success' })
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
if (!ensureAnalyticsLogin({ toastTitle: '璇峰厛鐧诲綍鍚庢煡鐪嬫暟鎹垎鏋? })) return
|
||||
if (!ensureAnalyticsLogin({ toastTitle: '请先登录后查看数据分析' })) return
|
||||
initDashboard()
|
||||
})
|
||||
|
||||
@@ -880,15 +881,15 @@ onHide(() => {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 椤甸潰锛氱櫧搴?+ 瀹藉睆灞呬腑 + 鑷€傚簲 */
|
||||
/* 璇存槑锛歶ni-app 鐨?rpx 浼氶殢灞忓缂╂斁锛屽灞?H5 寤鸿鐢?max-width 鎺у埗鍐呭瀹藉害銆?*/
|
||||
/* 页面:白底 + 宽屏居中 + 自适应 */
|
||||
/* 说明:uni-app 的 rpx 会随屏宽缩放,宽屏 H5 建议用 max-width 控制内容宽度。 */
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #f6f7fb;
|
||||
}
|
||||
|
||||
/* 椤甸潰甯冨眬锛氬灞忔椂渚ц竟鏍?鍐呭锛岀獎灞忔椂鍏ㄥ睆鍐呭 */
|
||||
/* 页面布局:宽屏时侧边栏+内容,窄屏时全屏内容 */
|
||||
.page-layout {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
@@ -900,19 +901,19 @@ onHide(() => {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 64px; /* 涓哄浐瀹氶《閮ㄥ鑸爮鐣欏嚭绌洪棿 */
|
||||
padding-top: 64px; /* 为固定顶部导航栏留出空间 */
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
/* padding removed */ 16px 28px;
|
||||
padding: 16px 16px 28px;
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 鍝嶅簲寮忥細绐勫睆鏃跺叏灞忔樉绀?*/
|
||||
/* 响应式:窄屏时全屏显示 */
|
||||
@media screen and (max-width: 959px) {
|
||||
.page-layout {
|
||||
flex-direction: column !important;
|
||||
@@ -923,8 +924,8 @@ onHide(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 椤堕儴 */
|
||||
/* 鉁?寮哄埗锛氶《閮ㄥ繀椤绘í鎺掞紙閬垮厤琚叏灞€ view:flex-direction:column 褰卞搷锛?*/
|
||||
/* 顶部 */
|
||||
/* ✅ 强制:顶部必须横排(避免被全局 view:flex-direction:column 影响) */
|
||||
.topbar {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
@@ -972,13 +973,13 @@ onHide(() => {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 宸︿晶鏍囬缁勪粛鐒舵槸绾靛悜 */
|
||||
/* 左侧标题组仍然是纵向 */
|
||||
.title-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
min-width: 0; /* 鍏佽鍐呴儴 text 鍋氱渷鐣?*/
|
||||
min-width: 0; /* 允许内部 text 做省略 */
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -1000,7 +1001,7 @@ onHide(() => {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 鉁?鍙充晶鎸夐挳姘镐笉鎹㈡垚绔栧垪锛堝繀瑕佹椂鍙鍒囷紝涓嶆崲琛岋級 */
|
||||
/* ✅ 右侧按钮永不换成竖列(必要时只裁切,不换行) */
|
||||
.topbar-right {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
@@ -1025,7 +1026,7 @@ onHide(() => {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 鍥炬爣鎸夐挳鏍峰紡 */
|
||||
/* 图标按钮样式 */
|
||||
.icon-btn-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
@@ -1049,7 +1050,7 @@ onHide(() => {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 閫氱煡鍥炬爣甯︾孩鐐?*/
|
||||
/* 通知图标带红点 */
|
||||
.icon-btn-icon.notification .badge {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
@@ -1061,7 +1062,7 @@ onHide(() => {
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
|
||||
/* 涓嬫媺鑿滃崟 */
|
||||
/* 下拉菜单 */
|
||||
.dropdown {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
@@ -1076,7 +1077,7 @@ onHide(() => {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 鏇村鎸夐挳锛堥粯璁ら殣钘忥紝绐勫睆鏃舵樉绀猴級 */
|
||||
/* 更多按钮(默认隐藏,窄屏时显示) */
|
||||
.more-btn {
|
||||
display: none;
|
||||
width: 32px;
|
||||
@@ -1101,7 +1102,7 @@ onHide(() => {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
/* 鏇村鑿滃崟涓嬫媺 */
|
||||
/* 更多菜单下拉 */
|
||||
.more-menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
@@ -1156,8 +1157,8 @@ onHide(() => {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* KPI锛氶粯璁?2鍒楋紝瀹藉睆 4鍒?*/
|
||||
/* 鉁?鏍稿績淇锛氱敤 flex + calc(50%) 鏇夸唬 width锛岄伩鍏?rpx + CSS var 澶辨晥 */
|
||||
/* KPI:默认 2列,宽屏 4列 */
|
||||
/* ✅ 核心修复:用 flex + calc(50%) 替代 width,避免 rpx + CSS var 失效 */
|
||||
.kpi-grid {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
@@ -1186,7 +1187,7 @@ onHide(() => {
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
/* KPI 鍗$墖娓愬彉鑳屾櫙 */
|
||||
/* KPI 卡片渐变背景 */
|
||||
.kpi-card-gmv {
|
||||
background: linear-gradient(135deg, #FF6B6B 0%, #FF4D4F 100%);
|
||||
color: #fff;
|
||||
@@ -1268,7 +1269,7 @@ onHide(() => {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 鏃堕棿缁村害 tabs 妯帓 */
|
||||
/* 时间维度 tabs 横排 */
|
||||
.tabs {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
@@ -1298,7 +1299,7 @@ onHide(() => {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 鍗$墖 */
|
||||
/* 卡片 */
|
||||
.card {
|
||||
margin-top: 12px;
|
||||
background: #fff;
|
||||
@@ -1335,17 +1336,17 @@ onHide(() => {
|
||||
color: rgba(0,0,0,0.55);
|
||||
}
|
||||
|
||||
/* 鍥捐〃蹇呴』缁欓珮搴︼紙H5 鍚﹀垯鍙兘 0 楂橈級 */
|
||||
/* 图表必须给高度(H5 否则可能 0 高) */
|
||||
.chart-box {
|
||||
width: 100%;
|
||||
height: 360px; /* 寤鸿鐢?px锛孒5 鏇寸ǔ */
|
||||
height: 360px; /* 建议用 px,H5 更稳 */
|
||||
}
|
||||
|
||||
.fullwide .chart-box {
|
||||
height: 420px; /* 澶у浘鏇撮珮 */
|
||||
height: 420px; /* 大图更高 */
|
||||
}
|
||||
|
||||
/* 鍥捐〃鍔犺浇鐘舵€?*/
|
||||
/* 图表加载状态 */
|
||||
.chart-loading {
|
||||
width: 100%;
|
||||
height: 360px;
|
||||
@@ -1360,7 +1361,7 @@ onHide(() => {
|
||||
height: 420px;
|
||||
}
|
||||
|
||||
/* 鐢ㄦ埛缁撴瀯鍜屾祦閲忔潵婧愶細妯帓鏄剧ず */
|
||||
/* 用户结构和流量来源:横排显示 */
|
||||
.charts-row {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
@@ -1376,7 +1377,7 @@ onHide(() => {
|
||||
min-width: 360px;
|
||||
}
|
||||
|
||||
/* 涓や釜TOP鎺掕锛氭í鎺掓樉绀?*/
|
||||
/* 两个TOP排行:横排显示 */
|
||||
.tops-row {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
@@ -1392,7 +1393,7 @@ onHide(() => {
|
||||
min-width: 360px;
|
||||
}
|
||||
|
||||
/* 婊氬姩瀹瑰櫒 */
|
||||
/* 滚动容器 */
|
||||
.rank-scroll-container {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
@@ -1408,7 +1409,7 @@ onHide(() => {
|
||||
animation: scrollRank 15s linear infinite;
|
||||
}
|
||||
|
||||
/* 鍒楄〃鏍峰紡 */
|
||||
/* 列表样式 */
|
||||
.rank-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1416,14 +1417,14 @@ onHide(() => {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 婊氬姩鍔ㄧ敾锛氬綋鏁版嵁瓒呰繃5鏉℃椂鑷姩婊氬姩 */
|
||||
/* 滚动动画:当数据超过5条时自动滚动 */
|
||||
@keyframes scrollRank {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
100% {
|
||||
/* 婊氬姩鍒扮涓€浠芥暟鎹殑鏈熬锛屽疄鐜版棤缂濆惊鐜?*/
|
||||
/* 姣忔潯 rank-item 楂樺害绾?50px锛堝寘鎷?padding 鍜?gap锛夛紝婊氬姩涓€鍗婇珮搴?*/
|
||||
/* 滚动到第一份数据的末尾,实现无缝循环 */
|
||||
/* 每条 rank-item 高度约 50px(包括 padding 和 gap),滚动一半高度 */
|
||||
transform: translateY(calc(-50%));
|
||||
}
|
||||
}
|
||||
@@ -1486,14 +1487,14 @@ onHide(() => {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
/* 瀹藉睆锛欿PI 4鍒?*/
|
||||
/* 宽屏:KPI 4列 */
|
||||
@media screen and (min-width: 960px) {
|
||||
.kpi-card {
|
||||
flex: 1 1 calc(25% - 9px);
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
/* 瀹藉睆鏃舵樉绀烘墍鏈夋寜閽紝闅愯棌"鏇村"鎸夐挳 */
|
||||
/* 宽屏时显示所有按钮,隐藏"更多"按钮 */
|
||||
.topbar-right .btn-hidden {
|
||||
display: flex !important;
|
||||
}
|
||||
@@ -1502,13 +1503,13 @@ onHide(() => {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 瀹藉睆鏃跺伐鍏峰崱鐗?3鍒?*/
|
||||
/* 宽屏时工具卡片 3列 */
|
||||
.tools-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* 鑷€傚簲锛氱獎灞忚嚜鍔ㄥ彉涓€鍒楋紙鏂偣鐢?px锛?*/
|
||||
/* 自适应:窄屏自动变一列(断点用 px) */
|
||||
@media screen and (max-width: 960px) {
|
||||
.charts-row,
|
||||
.tops-row {
|
||||
@@ -1528,22 +1529,22 @@ onHide(() => {
|
||||
height: 360px;
|
||||
}
|
||||
|
||||
/* 椤堕儴鏍忔寜閽湪灏忓睆骞曚笂锛氶殣钘忛儴鍒嗘寜閽紝鏄剧ず"鏇村"鎸夐挳 */
|
||||
/* 顶部栏按钮在小屏幕上:隐藏部分按钮,显示"更多"按钮 */
|
||||
.topbar-right {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* 闅愯棌鏍囪涓?btn-hidden 鐨勬寜閽?*/
|
||||
/* 隐藏标记为 btn-hidden 的按钮 */
|
||||
.topbar-right .btn-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 鏄剧ず"鏇村"鎸夐挳 */
|
||||
/* 显示"更多"按钮 */
|
||||
.more-btn {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
/* 鏍囬鍦ㄧ獎灞忔椂鍏佽鐪佺暐鍙?*/
|
||||
/* 标题在窄屏时允许省略号 */
|
||||
.title,
|
||||
.subtitle {
|
||||
max-width: 200px;
|
||||
@@ -1568,18 +1569,18 @@ onHide(() => {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 绐勫睆鏃?KPI 鍗$墖鍗曞垪 */
|
||||
/* 窄屏时 KPI 卡片单列 */
|
||||
.kpi-card {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
/* 绐勫睆鏃跺伐鍏峰崱鐗?2鍒?*/
|
||||
/* 窄屏时工具卡片 2列 */
|
||||
.tools-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* 蹇€熷伐鍏峰崱鐗囧尯 */
|
||||
/* 快速工具卡片区 */
|
||||
.tools-section {
|
||||
margin-top: 24px;
|
||||
}
|
||||
@@ -1679,4 +1680,3 @@ onHide(() => {
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user