From 82742bde5847d0b90e9b9adf27580613d0cbc91a Mon Sep 17 00:00:00 2001 From: david Date: Wed, 29 Jul 2026 17:28:18 +0800 Subject: [PATCH] fix dashboard empty data and split dev scripts --- .../logic/platform/dashboard/statistics.go | 7 ++- .../src/views/dashboard/DashboardPage.vue | 27 +++++++---- scripts/run_backend.sh | 44 +++++++++++++++++ scripts/run_frontend.sh | 23 +++++++++ scripts/run_platform.sh | 47 ++----------------- 5 files changed, 95 insertions(+), 53 deletions(-) create mode 100755 scripts/run_backend.sh create mode 100755 scripts/run_frontend.sh diff --git a/backend/api/internal/logic/platform/dashboard/statistics.go b/backend/api/internal/logic/platform/dashboard/statistics.go index 98bdda3..0abf1ca 100644 --- a/backend/api/internal/logic/platform/dashboard/statistics.go +++ b/backend/api/internal/logic/platform/dashboard/statistics.go @@ -71,7 +71,12 @@ var productStatusNames = map[int]string{ // GetDashboardOverview 汇总组织、人员、资产、订单、支付及客服维度的数据。 func GetDashboardOverview() (DashboardStatistics, error) { - var result DashboardStatistics + result := DashboardStatistics{ + OrderStatuses: make([]MetricSlice, 0), + ProductStatuses: make([]MetricSlice, 0), + PaymentChannels: make([]MetricSlice, 0), + RecentOrders: make([]DailyMetric, 0), + } counts := []struct { model any where string diff --git a/frontend/platform_admin/src/views/dashboard/DashboardPage.vue b/frontend/platform_admin/src/views/dashboard/DashboardPage.vue index b0b53b7..d7d7bcc 100644 --- a/frontend/platform_admin/src/views/dashboard/DashboardPage.vue +++ b/frontend/platform_admin/src/views/dashboard/DashboardPage.vue @@ -85,34 +85,45 @@ const availableActions = computed(() => actions.filter( (action) => userStore.role === 'root' || userStore.menuCodes.includes(action.menu), )); const centsToYuan = (value: number) => value / 100; +const safeArray = (value: T[] | null | undefined): T[] => + Array.isArray(value) ? value : []; const pieOption = (data: { name: string; value: number }[]): EChartsOption => ({ tooltip: { trigger: 'item' }, legend: { bottom: 0 }, series: [{ type: 'pie', radius: ['42%', '68%'], center: ['50%', '44%'], data, label: { formatter: '{b}\\n{c}' } }], }); -const orderStatusOption = computed(() => pieOption(overview.value.order_statuses)); -const productStatusOption = computed(() => pieOption(overview.value.product_statuses)); +const orderStatusOption = computed(() => pieOption(safeArray(overview.value.order_statuses))); +const productStatusOption = computed(() => pieOption(safeArray(overview.value.product_statuses))); const orderTrendOption = computed(() => ({ tooltip: { trigger: 'axis' }, legend: { data: ['订单数', '订单金额'] }, grid: { left: 48, right: 58, bottom: 34 }, - xAxis: { type: 'category', data: overview.value.recent_orders.map((item) => item.date.slice(5)) }, + xAxis: { type: 'category', data: safeArray(overview.value.recent_orders).map((item) => item.date.slice(5)) }, yAxis: [{ type: 'value', name: '单' }, { type: 'value', name: '元' }], series: [ - { name: '订单数', type: 'bar', data: overview.value.recent_orders.map((item) => item.order_count) }, - { name: '订单金额', type: 'line', yAxisIndex: 1, smooth: true, data: overview.value.recent_orders.map((item) => centsToYuan(item.order_amount)) }, + { name: '订单数', type: 'bar', data: safeArray(overview.value.recent_orders).map((item) => item.order_count) }, + { name: '订单金额', type: 'line', yAxisIndex: 1, smooth: true, data: safeArray(overview.value.recent_orders).map((item) => centsToYuan(item.order_amount)) }, ], })); const paymentChannelOption = computed(() => ({ tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { left: 70, right: 24, bottom: 28 }, xAxis: { type: 'value', name: '元' }, - yAxis: { type: 'category', data: overview.value.payment_channels.map((item) => item.name || '未知渠道') }, - series: [{ type: 'bar', data: overview.value.payment_channels.map((item) => centsToYuan(item.value)) }], + yAxis: { type: 'category', data: safeArray(overview.value.payment_channels).map((item) => item.name || '未知渠道') }, + series: [{ type: 'bar', data: safeArray(overview.value.payment_channels).map((item) => centsToYuan(item.value)) }], })); onMounted(async () => { loading.value = true; try { const data = await platformApi.overview(); - overview.value = { ...data, today_order_amount: centsToYuan(data.today_order_amount), paid_amount: centsToYuan(data.paid_amount) }; + overview.value = { + ...emptyOverview(), + ...data, + today_order_amount: centsToYuan(data.today_order_amount ?? 0), + paid_amount: centsToYuan(data.paid_amount ?? 0), + order_statuses: safeArray(data.order_statuses), + product_statuses: safeArray(data.product_statuses), + payment_channels: safeArray(data.payment_channels), + recent_orders: safeArray(data.recent_orders), + }; } catch (error) { Message.error((error as Error).message); } finally { diff --git a/scripts/run_backend.sh b/scripts/run_backend.sh new file mode 100755 index 0000000..cdfc07c --- /dev/null +++ b/scripts/run_backend.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +BACKEND_DIR="${PROJECT_ROOT}/backend/api" +RUNTIME_CONFIG_ROOT="" +BACKEND_PID="" + +cleanup() { + trap - EXIT INT TERM + if [[ -n "${BACKEND_PID}" ]] && kill -0 "${BACKEND_PID}" 2>/dev/null; then + kill "${BACKEND_PID}" 2>/dev/null || true + wait "${BACKEND_PID}" 2>/dev/null || true + fi + if [[ -n "${RUNTIME_CONFIG_ROOT}" ]]; then + rm -f "${RUNTIME_CONFIG_ROOT}/etc/heqi_dev.yaml" + rmdir "${RUNTIME_CONFIG_ROOT}/etc" "${RUNTIME_CONFIG_ROOT}" 2>/dev/null || true + fi +} + +if ! command -v go >/dev/null 2>&1; then + echo "缺少命令:go" >&2 + exit 1 +fi + +export BSM_RuntimeMode="${BSM_RuntimeMode:-dev}" +export BSM_JwtSecretKey="${BSM_JwtSecretKey:-local-dev-key-16}" + +if [[ -z "${BSM_Prefix:-}" ]]; then + RUNTIME_CONFIG_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/heqi-platform-backend.XXXXXX")" + mkdir -p "${RUNTIME_CONFIG_ROOT}/etc" + cp "${BACKEND_DIR}/etc/platform_dev.yaml" "${RUNTIME_CONFIG_ROOT}/etc/heqi_dev.yaml" + export BSM_Prefix="${RUNTIME_CONFIG_ROOT}" +fi + +trap cleanup EXIT INT TERM + +echo "启动平台后端:http://localhost:12426/heqi/platform/v1" +cd "${BACKEND_DIR}" +go run ./cmd/main/main.go & +BACKEND_PID=$! +wait "${BACKEND_PID}" diff --git a/scripts/run_frontend.sh b/scripts/run_frontend.sh new file mode 100755 index 0000000..46ac402 --- /dev/null +++ b/scripts/run_frontend.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +FRONTEND_DIR="${PROJECT_ROOT}/frontend/platform_admin" + +if ! command -v pnpm >/dev/null 2>&1; then + echo "缺少命令:pnpm" >&2 + exit 1 +fi + +if [[ ! -d "${FRONTEND_DIR}/node_modules" ]]; then + echo "前端依赖尚未安装,请先运行:cd ${FRONTEND_DIR} && pnpm install" >&2 + exit 1 +fi + +export VITE_API_BASE_URL="${VITE_API_BASE_URL:-http://localhost:12426/heqi/platform/v1}" + +echo "启动平台前端:http://localhost:5173" +cd "${FRONTEND_DIR}" +exec pnpm exec vite --config ./config/vite.config.ts --host 0.0.0.0 diff --git a/scripts/run_platform.sh b/scripts/run_platform.sh index 755ae67..b9a562e 100755 --- a/scripts/run_platform.sh +++ b/scripts/run_platform.sh @@ -3,14 +3,10 @@ set -Eeuo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" -BACKEND_DIR="${PROJECT_ROOT}/backend/api" -FRONTEND_DIR="${PROJECT_ROOT}/frontend/platform_admin" BACKEND_PID="" FRONTEND_PID="" CLEANED_UP=0 -RUNTIME_CONFIG_ROOT="" cleanup() { if [[ "${CLEANED_UP}" -eq 1 ]]; then @@ -29,55 +25,18 @@ cleanup() { wait "${pid}" 2>/dev/null || true fi done - if [[ -n "${RUNTIME_CONFIG_ROOT}" ]]; then - rm -f "${RUNTIME_CONFIG_ROOT}/etc/heqi_dev.yaml" - rmdir "${RUNTIME_CONFIG_ROOT}/etc" "${RUNTIME_CONFIG_ROOT}" 2>/dev/null || true - fi } -require_command() { - if ! command -v "$1" >/dev/null 2>&1; then - echo "缺少命令:$1" >&2 - exit 1 - fi -} - -require_command go -require_command pnpm - -if [[ ! -d "${FRONTEND_DIR}/node_modules" ]]; then - echo "前端依赖尚未安装,请先运行:cd ${FRONTEND_DIR} && pnpm install" >&2 - exit 1 -fi - -export BSM_RuntimeMode="${BSM_RuntimeMode:-dev}" -export BSM_JwtSecretKey="${BSM_JwtSecretKey:-local-dev-key-16}" -export VITE_API_BASE_URL="${VITE_API_BASE_URL:-http://localhost:12426/heqi/platform/v1}" - -if [[ -z "${BSM_Prefix:-}" ]]; then - RUNTIME_CONFIG_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/heqi-platform.XXXXXX")" - mkdir -p "${RUNTIME_CONFIG_ROOT}/etc" - cp "${BACKEND_DIR}/etc/platform_dev.yaml" "${RUNTIME_CONFIG_ROOT}/etc/heqi_dev.yaml" - export BSM_Prefix="${RUNTIME_CONFIG_ROOT}" -fi - trap cleanup EXIT INT TERM -echo "启动平台后端:http://localhost:12426/heqi/platform/v1" -( - cd "${BACKEND_DIR}" - exec go run ./cmd/main/main.go -) & +"${SCRIPT_DIR}/run_backend.sh" & BACKEND_PID=$! -echo "启动平台前端:http://localhost:5173" -( - cd "${FRONTEND_DIR}" - exec pnpm exec vite --config ./config/vite.config.ts --host 0.0.0.0 -) & +"${SCRIPT_DIR}/run_frontend.sh" & FRONTEND_PID=$! echo "前后端调试服务已启动,按 Ctrl+C 同时停止。" +echo "日常开发建议在两个终端分别运行 run_backend.sh 和 run_frontend.sh。" while true; do if ! kill -0 "${BACKEND_PID}" 2>/dev/null; then