fix dashboard empty data and split dev scripts
This commit is contained in:
@@ -71,7 +71,12 @@ var productStatusNames = map[int]string{
|
|||||||
|
|
||||||
// GetDashboardOverview 汇总组织、人员、资产、订单、支付及客服维度的数据。
|
// GetDashboardOverview 汇总组织、人员、资产、订单、支付及客服维度的数据。
|
||||||
func GetDashboardOverview() (DashboardStatistics, error) {
|
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 {
|
counts := []struct {
|
||||||
model any
|
model any
|
||||||
where string
|
where string
|
||||||
|
|||||||
@@ -85,34 +85,45 @@ const availableActions = computed(() => actions.filter(
|
|||||||
(action) => userStore.role === 'root' || userStore.menuCodes.includes(action.menu),
|
(action) => userStore.role === 'root' || userStore.menuCodes.includes(action.menu),
|
||||||
));
|
));
|
||||||
const centsToYuan = (value: number) => value / 100;
|
const centsToYuan = (value: number) => value / 100;
|
||||||
|
const safeArray = <T,>(value: T[] | null | undefined): T[] =>
|
||||||
|
Array.isArray(value) ? value : [];
|
||||||
const pieOption = (data: { name: string; value: number }[]): EChartsOption => ({
|
const pieOption = (data: { name: string; value: number }[]): EChartsOption => ({
|
||||||
tooltip: { trigger: 'item' }, legend: { bottom: 0 },
|
tooltip: { trigger: 'item' }, legend: { bottom: 0 },
|
||||||
series: [{ type: 'pie', radius: ['42%', '68%'], center: ['50%', '44%'], data, label: { formatter: '{b}\\n{c}' } }],
|
series: [{ type: 'pie', radius: ['42%', '68%'], center: ['50%', '44%'], data, label: { formatter: '{b}\\n{c}' } }],
|
||||||
});
|
});
|
||||||
const orderStatusOption = computed(() => pieOption(overview.value.order_statuses));
|
const orderStatusOption = computed(() => pieOption(safeArray(overview.value.order_statuses)));
|
||||||
const productStatusOption = computed(() => pieOption(overview.value.product_statuses));
|
const productStatusOption = computed(() => pieOption(safeArray(overview.value.product_statuses)));
|
||||||
const orderTrendOption = computed<EChartsOption>(() => ({
|
const orderTrendOption = computed<EChartsOption>(() => ({
|
||||||
tooltip: { trigger: 'axis' }, legend: { data: ['订单数', '订单金额'] },
|
tooltip: { trigger: 'axis' }, legend: { data: ['订单数', '订单金额'] },
|
||||||
grid: { left: 48, right: 58, bottom: 34 },
|
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: '元' }],
|
yAxis: [{ type: 'value', name: '单' }, { type: 'value', name: '元' }],
|
||||||
series: [
|
series: [
|
||||||
{ name: '订单数', type: 'bar', data: overview.value.recent_orders.map((item) => item.order_count) },
|
{ name: '订单数', type: 'bar', data: safeArray(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: 'line', yAxisIndex: 1, smooth: true, data: safeArray(overview.value.recent_orders).map((item) => centsToYuan(item.order_amount)) },
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
const paymentChannelOption = computed<EChartsOption>(() => ({
|
const paymentChannelOption = computed<EChartsOption>(() => ({
|
||||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { left: 70, right: 24, bottom: 28 },
|
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { left: 70, right: 24, bottom: 28 },
|
||||||
xAxis: { type: 'value', name: '元' },
|
xAxis: { type: 'value', name: '元' },
|
||||||
yAxis: { type: 'category', data: overview.value.payment_channels.map((item) => item.name || '未知渠道') },
|
yAxis: { type: 'category', data: safeArray(overview.value.payment_channels).map((item) => item.name || '未知渠道') },
|
||||||
series: [{ type: 'bar', data: overview.value.payment_channels.map((item) => centsToYuan(item.value)) }],
|
series: [{ type: 'bar', data: safeArray(overview.value.payment_channels).map((item) => centsToYuan(item.value)) }],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const data = await platformApi.overview();
|
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) {
|
} catch (error) {
|
||||||
Message.error((error as Error).message);
|
Message.error((error as Error).message);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
44
scripts/run_backend.sh
Executable file
44
scripts/run_backend.sh
Executable file
@@ -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}"
|
||||||
23
scripts/run_frontend.sh
Executable file
23
scripts/run_frontend.sh
Executable file
@@ -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
|
||||||
@@ -3,14 +3,10 @@
|
|||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
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=""
|
BACKEND_PID=""
|
||||||
FRONTEND_PID=""
|
FRONTEND_PID=""
|
||||||
CLEANED_UP=0
|
CLEANED_UP=0
|
||||||
RUNTIME_CONFIG_ROOT=""
|
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if [[ "${CLEANED_UP}" -eq 1 ]]; then
|
if [[ "${CLEANED_UP}" -eq 1 ]]; then
|
||||||
@@ -29,55 +25,18 @@ cleanup() {
|
|||||||
wait "${pid}" 2>/dev/null || true
|
wait "${pid}" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
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
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
echo "启动平台后端:http://localhost:12426/heqi/platform/v1"
|
"${SCRIPT_DIR}/run_backend.sh" &
|
||||||
(
|
|
||||||
cd "${BACKEND_DIR}"
|
|
||||||
exec go run ./cmd/main/main.go
|
|
||||||
) &
|
|
||||||
BACKEND_PID=$!
|
BACKEND_PID=$!
|
||||||
|
|
||||||
echo "启动平台前端:http://localhost:5173"
|
"${SCRIPT_DIR}/run_frontend.sh" &
|
||||||
(
|
|
||||||
cd "${FRONTEND_DIR}"
|
|
||||||
exec pnpm exec vite --config ./config/vite.config.ts --host 0.0.0.0
|
|
||||||
) &
|
|
||||||
FRONTEND_PID=$!
|
FRONTEND_PID=$!
|
||||||
|
|
||||||
echo "前后端调试服务已启动,按 Ctrl+C 同时停止。"
|
echo "前后端调试服务已启动,按 Ctrl+C 同时停止。"
|
||||||
|
echo "日常开发建议在两个终端分别运行 run_backend.sh 和 run_frontend.sh。"
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
if ! kill -0 "${BACKEND_PID}" 2>/dev/null; then
|
if ! kill -0 "${BACKEND_PID}" 2>/dev/null; then
|
||||||
|
|||||||
Reference in New Issue
Block a user