|
|
|
|
@ -199,6 +199,40 @@ function parseVenuePcRowsFromResponse(p: HikPeopleCountingResponse | null): Dash
|
|
|
|
|
|
|
|
|
|
const dashboardVenuePcRows = computed((): DashboardVenuePcRow[] => parseVenuePcRowsFromResponse(pcData.value))
|
|
|
|
|
|
|
|
|
|
type VenuePcViewMode = 'chart' | 'table'
|
|
|
|
|
const venuePcViewMode = ref<VenuePcViewMode>('chart')
|
|
|
|
|
|
|
|
|
|
const dashboardVenuePcEnterTotal = computed(() =>
|
|
|
|
|
dashboardVenuePcRows.value.reduce((sum, row) => sum + (Number(row.enter) || 0), 0),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const venuePcRangeLabel = computed(() => {
|
|
|
|
|
const picked = pickVenuePcRangeFromPicker()
|
|
|
|
|
if (!picked) return ''
|
|
|
|
|
return picked.start === picked.end ? picked.start : `${picked.start} 至 ${picked.end}`
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const venuePcMaxEnter = computed(() => {
|
|
|
|
|
const rows = dashboardVenuePcRows.value
|
|
|
|
|
if (!rows.length) return 1
|
|
|
|
|
return Math.max(1, ...rows.map((r) => Number(r.enter) || 0))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function venuePcBarHeightPct(enter: number): string {
|
|
|
|
|
const max = venuePcMaxEnter.value
|
|
|
|
|
const v = Number(enter) || 0
|
|
|
|
|
if (v <= 0) return '0%'
|
|
|
|
|
return `${Math.max(6, (v / max) * 100)}%`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onVenuePcChartWheel(e: WheelEvent) {
|
|
|
|
|
const el = e.currentTarget as HTMLElement | null
|
|
|
|
|
if (!el || el.scrollWidth <= el.clientWidth + 1) return
|
|
|
|
|
if (Math.abs(e.deltaX) >= Math.abs(e.deltaY)) return
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
el.scrollLeft += e.deltaY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function dashboardVenuePcSummary(ctx: { data: DashboardVenuePcRow[] }) {
|
|
|
|
|
const rows = Array.isArray(ctx.data) ? ctx.data : []
|
|
|
|
|
let enter = 0
|
|
|
|
|
@ -1075,18 +1109,75 @@ onMounted(async () => {
|
|
|
|
|
<a-spin :loading="pcLoading">
|
|
|
|
|
<a-alert v-if="pcError" type="warning" show-icon style="margin-bottom: 10px">{{ pcError }}</a-alert>
|
|
|
|
|
|
|
|
|
|
<div v-if="dashboardVenuePcRows.length" class="dash-venue-pc-table-wrap">
|
|
|
|
|
<a-table
|
|
|
|
|
class="dash-table dash-venue-pc-table"
|
|
|
|
|
row-key="venueId"
|
|
|
|
|
:columns="dashboardVenuePcColumns"
|
|
|
|
|
:data="dashboardVenuePcRows"
|
|
|
|
|
:pagination="false"
|
|
|
|
|
size="small"
|
|
|
|
|
table-layout-fixed
|
|
|
|
|
:scroll="{ y: 260 }"
|
|
|
|
|
:summary="dashboardVenuePcSummary"
|
|
|
|
|
/>
|
|
|
|
|
<div v-if="dashboardVenuePcRows.length" class="dash-venue-pc-content">
|
|
|
|
|
<div class="dash-venue-pc-viewbar">
|
|
|
|
|
<div class="dash-venue-pc-total">
|
|
|
|
|
<span v-if="venuePcRangeLabel" class="dash-venue-pc-total__range">{{ venuePcRangeLabel }}</span>
|
|
|
|
|
<span class="dash-venue-pc-total__label">合计入馆人数</span>
|
|
|
|
|
<strong class="dash-venue-pc-total__value">{{ dashboardVenuePcEnterTotal }}</strong>
|
|
|
|
|
</div>
|
|
|
|
|
<a-space size="small">
|
|
|
|
|
<a-button
|
|
|
|
|
size="small"
|
|
|
|
|
:type="venuePcViewMode === 'chart' ? 'primary' : 'secondary'"
|
|
|
|
|
@click="venuePcViewMode = 'chart'"
|
|
|
|
|
>
|
|
|
|
|
图表
|
|
|
|
|
</a-button>
|
|
|
|
|
<a-button
|
|
|
|
|
size="small"
|
|
|
|
|
:type="venuePcViewMode === 'table' ? 'primary' : 'secondary'"
|
|
|
|
|
@click="venuePcViewMode = 'table'"
|
|
|
|
|
>
|
|
|
|
|
数据
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-space>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-show="venuePcViewMode === 'chart'" class="dash-venue-pc-chart">
|
|
|
|
|
<div
|
|
|
|
|
class="dash-venue-pc-chart__scroll"
|
|
|
|
|
role="img"
|
|
|
|
|
aria-label="各场馆入馆人数柱状图"
|
|
|
|
|
@wheel="onVenuePcChartWheel"
|
|
|
|
|
>
|
|
|
|
|
<div class="dash-venue-pc-chart__plot">
|
|
|
|
|
<div
|
|
|
|
|
v-for="row in dashboardVenuePcRows"
|
|
|
|
|
:key="String(row.venueId)"
|
|
|
|
|
class="dash-venue-pc-chart__col"
|
|
|
|
|
>
|
|
|
|
|
<span class="dash-venue-pc-chart__col-value">{{ row.enter }}</span>
|
|
|
|
|
<div class="dash-venue-pc-chart__col-track">
|
|
|
|
|
<div
|
|
|
|
|
class="dash-venue-pc-chart__col-bar"
|
|
|
|
|
:style="{ height: venuePcBarHeightPct(row.enter) }"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<span
|
|
|
|
|
class="dash-venue-pc-chart__col-label"
|
|
|
|
|
:title="String(row.venueName || row.venueId)"
|
|
|
|
|
>
|
|
|
|
|
{{ row.venueName || row.venueId }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-show="venuePcViewMode === 'table'" class="dash-venue-pc-table-wrap">
|
|
|
|
|
<a-table
|
|
|
|
|
class="dash-table dash-venue-pc-table"
|
|
|
|
|
row-key="venueId"
|
|
|
|
|
:columns="dashboardVenuePcColumns"
|
|
|
|
|
:data="dashboardVenuePcRows"
|
|
|
|
|
:pagination="false"
|
|
|
|
|
size="small"
|
|
|
|
|
table-layout-fixed
|
|
|
|
|
:scroll="{ y: 260 }"
|
|
|
|
|
:summary="dashboardVenuePcSummary"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<a-empty v-else-if="!pcLoading && !pcError" description="暂无数据,可调时间段或检查客流归档与场馆映射" />
|
|
|
|
|
</a-spin>
|
|
|
|
|
@ -1334,6 +1425,8 @@ onMounted(async () => {
|
|
|
|
|
border: 1px solid #e8eaed;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.04);
|
|
|
|
|
min-width: 0;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-bundle .dash-overview-dual,
|
|
|
|
|
@ -2031,6 +2124,7 @@ onMounted(async () => {
|
|
|
|
|
|
|
|
|
|
.dash-metric-card--venue-pc {
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-metric-card__icon--venue-pc {
|
|
|
|
|
@ -2040,12 +2134,156 @@ onMounted(async () => {
|
|
|
|
|
|
|
|
|
|
.dash-metric-card__body--venue-pc {
|
|
|
|
|
min-height: 0;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-metric-card__body--venue-pc :deep(.arco-spin),
|
|
|
|
|
.dash-metric-card__body--venue-pc :deep(.arco-spin-children) {
|
|
|
|
|
display: block;
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-table-wrap {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-content {
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-viewbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 10px 16px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-total {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
gap: 6px 10px;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
color: #4e5969;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-total__range {
|
|
|
|
|
color: #86909c;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-total__value {
|
|
|
|
|
color: #165dff;
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart {
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
padding: 4px 0 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__scroll {
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow-x: scroll;
|
|
|
|
|
overflow-y: hidden;
|
|
|
|
|
overscroll-behavior-x: contain;
|
|
|
|
|
-webkit-overflow-scrolling: touch;
|
|
|
|
|
touch-action: pan-x;
|
|
|
|
|
padding-bottom: 6px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
background: #fafbfd;
|
|
|
|
|
border: 1px solid #f0f1f5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__scroll::-webkit-scrollbar {
|
|
|
|
|
height: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__scroll::-webkit-scrollbar-thumb {
|
|
|
|
|
background: #c9cdd4;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__scroll::-webkit-scrollbar-track {
|
|
|
|
|
background: #f2f3f5;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__plot {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
flex-wrap: nowrap;
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
min-height: 280px;
|
|
|
|
|
padding: 8px 8px 4px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__col {
|
|
|
|
|
flex: 0 0 38px;
|
|
|
|
|
width: 38px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__col-value {
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #1d2129;
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__col-track {
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 220px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
border-radius: 4px 4px 0 0;
|
|
|
|
|
background: linear-gradient(180deg, #f7f8fa 0%, #fafbfd 100%);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__col-bar {
|
|
|
|
|
width: 20px;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
border-radius: 4px 4px 0 0;
|
|
|
|
|
background: linear-gradient(180deg, #4080ff 0%, #165dff 100%);
|
|
|
|
|
transition: height 0.25s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-chart__col-label {
|
|
|
|
|
width: 38px;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
color: #4e5969;
|
|
|
|
|
text-align: center;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
display: -webkit-box;
|
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
-webkit-line-clamp: 2;
|
|
|
|
|
line-clamp: 2;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dash-venue-pc-bundle :deep(.dash-venue-pc-table.arco-table),
|
|
|
|
|
.dash-venue-pc-table-wrap :deep(.dash-venue-pc-table.arco-table) {
|
|
|
|
|
width: 100%;
|
|
|
|
|
|