update order chart style

This commit is contained in:
Timi
2026-09-23 14:59:50 +08:00
parent 139afad052
commit 7595df5df9
10 changed files with 129 additions and 95 deletions
@@ -4,7 +4,7 @@
caption="排除已取消订单,前 10" caption="排除已取消订单,前 10"
:option="chartOption" :option="chartOption"
:loading="loading" :loading="loading"
height="17rem" height="14rem"
/> />
</template> </template>
@@ -27,17 +27,18 @@ const chartOption = computed<EChartsOption>(() => {
const list = props.list.slice(0, 10); const list = props.list.slice(0, 10);
return { return {
...buildHorizontalBarOption(list.map(getCustomerName)), ...buildHorizontalBarOption(list.map(getCustomerName)),
legend: { show: false }, grid: {
top: 0,
left: 0,
right: 0,
bottom: 0,
showLabel: false
},
xAxis: { xAxis: {
type: "value", type: "value",
axisLabel: {
color: "#52607a",
formatter: (value: number) => `${formatChartYuan(value)} 元`
},
splitLine: { lineStyle: { color: "rgba(148, 163, 184, .2)" } } splitLine: { lineStyle: { color: "rgba(148, 163, 184, .2)" } }
}, },
series: [{ series: [{
name: "净收款",
type: "bar", type: "bar",
tooltip: { valueFormatter: value => `${formatChartYuan(Number(value))} 元` }, tooltip: { valueFormatter: value => `${formatChartYuan(Number(value))} 元` },
data: list.map(item => getNumber(item.netPaidAmount) / 100) data: list.map(item => getNumber(item.netPaidAmount) / 100)
@@ -39,6 +39,7 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
{ {
type: "value", type: "value",
name: "订单", name: "订单",
minInterval: 1,
axisLabel: { color: "#52607a" }, axisLabel: { color: "#52607a" },
splitLine: { lineStyle: { color: "rgba(148, 163, 184, .2)" } } splitLine: { lineStyle: { color: "rgba(148, 163, 184, .2)" } }
}, },
@@ -4,7 +4,7 @@
caption="当前订单快照" caption="当前订单快照"
:option="chartOption" :option="chartOption"
:loading="loading" :loading="loading"
height="17rem" height="6rem"
/> />
</template> </template>
@@ -27,16 +27,35 @@ const chartOption = computed<EChartsOption>(() => {
const list = props.list; const list = props.list;
return { return {
...buildHorizontalBarOption(list.map(item => getPaymentStatusText(item.paymentStatus))), ...buildHorizontalBarOption(list.map(item => getPaymentStatusText(item.paymentStatus))),
legend: { show: false }, grid: {
top: 0,
left: 0,
right: 0,
bottom: 0,
showLabel: false
},
series: [{ series: [{
name: "订单数",
type: "bar", type: "bar",
data: list.map(item => getNumber(item.orderCount)) data: list.map(item => ({
value: getNumber(item.orderCount),
itemStyle: { color: getPaymentStatusColor(item.paymentStatus) }
}))
}] }]
}; };
}); });
function getPaymentStatusText(value?: string): string { function getPaymentStatusText(value?: string): string {
return ({ UNPAID: "未付款", PARTIALLY_PAID: "部分付款", PAID: "已付款" } as Record<string, string>)[value || ""] || "未知状态"; return ({
UNPAID: "未付款",
PARTIALLY_PAID: "部分付款",
PAID: "已付款"
} as Record<string, string>)[value || ""] || "未知状态";
}
function getPaymentStatusColor(value?: string): string {
return ({
UNPAID: "#F33434",
PARTIALLY_PAID: "#EB9C25",
PAID: "#0F766E"
} as Record<string, string>)[value || ""] || "#94A3B8";
} }
</script> </script>
@@ -2,25 +2,17 @@
<section class="receivable-section"> <section class="receivable-section">
<div class="title-row"> <div class="title-row">
<h2 v-text="'应收构成'" /> <h2 v-text="'应收构成'" />
<span class="caption" v-text="'时间范围内创建订单的当前快照'" /> <span class="caption">成交金额拆分;待收余额已扣除待核销</span>
</div>
<div class="receivable-list">
<div v-for="item in detailList" :key="item.label" class="receivable-item">
<span v-text="item.label" />
<strong v-text="item.value" />
</div>
</div> </div>
<div ref="chartElement" class="chart" />
</section> </section>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { EChartsOption } from "echarts";
import type { GaoSalesOrderReceivableStatView } from "@/types/GaoChart"; import type { GaoSalesOrderReceivableStatView } from "@/types/GaoChart";
import { formatMoney } from "@/pages/product/component/chartUtils"; import { buildPieChartOption, formatChartYuan, getNumber } from "@/pages/product/component/chartUtils";
import { useStoreChart } from "@/pages/store/component/useStoreChart";
type DetailItem = {
label: string;
value: string;
};
defineOptions({ defineOptions({
name: "OrderReceivableCard" name: "OrderReceivableCard"
@@ -30,13 +22,40 @@ const props = defineProps<{
receivable: GaoSalesOrderReceivableStatView; receivable: GaoSalesOrderReceivableStatView;
}>(); }>();
const detailList = computed<DetailItem[]>(() => [ const chartOption = computed<EChartsOption>(() => {
{ label: "有效收款", value: formatMoney(props.receivable.paidAmount) }, const pendingPaidAmount = getNumber(props.receivable.pendingPaidAmount);
{ label: "待核销", value: formatMoney(props.receivable.pendingPaidAmount) }, const unpaidAmount = getNumber(props.receivable.unpaidAmount);
{ label: "未收", value: formatMoney(props.receivable.unpaidAmount) }, return {
{ label: "有效退款", value: formatMoney(props.receivable.refundedAmount) }, ...buildPieChartOption(),
{ label: "净收款", value: formatMoney(props.receivable.netPaidAmount) } tooltip: {
]); trigger: "item",
confine: true,
formatter: params => {
const item = Array.isArray(params) ? params[0] : params;
return item
? `${item.name}:${formatChartYuan(Number(item.value))} 元(${item.percent}%)`
: "";
}
},
series: [
{
name: "成交金额",
type: "pie",
radius: ["0%", "58%"],
center: ["50%", "44%"],
label: { color: "#52607a", formatter: "{b} {d}%" },
data: [
{ name: "净收款", value: getNumber(props.receivable.netPaidAmount) / 100 },
{ name: "有效退款", value: getNumber(props.receivable.refundedAmount) / 100 },
{ name: "待核销", value: pendingPaidAmount / 100 },
{ name: "待收余额", value: Math.max(0, unpaidAmount - pendingPaidAmount) / 100 }
]
}
]
};
});
const { chartElement } = useStoreChart(chartOption);
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@@ -65,30 +84,9 @@ const detailList = computed<DetailItem[]>(() => [
font-size: .6875rem; font-size: .6875rem;
} }
.receivable-list { .chart {
gap: .5rem; width: 100%;
display: grid; height: 15rem;
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.receivable-item {
padding: .625rem .5rem;
background: rgba(241, 245, 249, .8);
border-radius: .75rem;
span {
color: #64748b;
display: block;
font-size: .6875rem;
}
strong {
color: #0f172a;
margin-top: .25rem;
display: block;
font-size: .8125rem;
font-weight: 700;
}
} }
} }
@@ -101,23 +99,6 @@ const detailList = computed<DetailItem[]>(() => [
color: #e2e8f0; color: #e2e8f0;
} }
.receivable-item {
background: rgba(30, 41, 59, .88);
span {
color: #94a3b8;
}
strong {
color: #e2e8f0;
}
}
}
}
@media (max-width: 42rem) {
.receivable-list {
grid-template-columns: repeat(2, minmax(0, 1fr));
} }
} }
</style> </style>
@@ -4,7 +4,7 @@
caption="当前订单快照" caption="当前订单快照"
:option="chartOption" :option="chartOption"
:loading="loading" :loading="loading"
height="17rem" height="6rem"
/> />
</template> </template>
@@ -27,11 +27,19 @@ const chartOption = computed<EChartsOption>(() => {
const list = props.list; const list = props.list;
return { return {
...buildHorizontalBarOption(list.map(item => getOrderStatusText(item.status))), ...buildHorizontalBarOption(list.map(item => getOrderStatusText(item.status))),
legend: { show: false }, grid: {
top: 0,
left: 0,
right: 0,
bottom: 0,
showLabel: false
},
series: [{ series: [{
name: "订单数",
type: "bar", type: "bar",
data: list.map(item => getNumber(item.orderCount)) data: list.map(item => ({
value: getNumber(item.orderCount),
itemStyle: { color: getOrderStatusColor(item.status) }
}))
}] }]
}; };
}); });
@@ -39,4 +47,12 @@ const chartOption = computed<EChartsOption>(() => {
function getOrderStatusText(value?: string): string { function getOrderStatusText(value?: string): string {
return ({ ACTIVE: "进行中", FULFILLED: "已完成", CANCELLED: "已取消" } as Record<string, string>)[value || ""] || "未知状态"; return ({ ACTIVE: "进行中", FULFILLED: "已完成", CANCELLED: "已取消" } as Record<string, string>)[value || ""] || "未知状态";
} }
function getOrderStatusColor(value?: string): string {
return ({
ACTIVE: "#EB9C25",
FULFILLED: "#0F766E",
CANCELLED: "#F33434"
} as Record<string, string>)[value || ""] || "#94A3B8";
}
</script> </script>
@@ -4,7 +4,7 @@
caption="按订单数量" caption="按订单数量"
:option="chartOption" :option="chartOption"
:loading="loading" :loading="loading"
height="17rem" height="10rem"
/> />
</template> </template>
@@ -35,23 +35,43 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
axisTick: { show: false }, axisTick: { show: false },
axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true } axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true }
}, },
yAxis: [
{
type: "value",
name: "基础单位",
minInterval: 1,
axisLabel: { color: "#52607a" },
splitLine: { lineStyle: { color: "rgba(148, 163, 184, .2)" } }
},
{
type: "value",
name: "订单",
position: "right",
minInterval: 1,
axisLabel: { color: "#52607a" },
splitLine: { show: false }
}
],
series: [ series: [
{ {
name: "直接销量", name: "直接销量",
type: "bar", type: "bar",
stack: "quantity", stack: "quantity",
yAxisIndex: 0,
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.directBaseQuantity)) data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.directBaseQuantity))
}, },
{ {
name: "套餐权益", name: "套餐权益",
type: "bar", type: "bar",
stack: "quantity", stack: "quantity",
yAxisIndex: 0,
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.packageBaseQuantity)) data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.packageBaseQuantity))
}, },
{ {
name: "订单数", name: "订单数",
type: "line", type: "line",
smooth: true, smooth: true,
yAxisIndex: 1,
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.orderCount)) data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.orderCount))
} }
] ]
@@ -20,8 +20,7 @@
<tbody> <tbody>
<tr v-for="item in sortedList" :key="item.productId"> <tr v-for="item in sortedList" :key="item.productId">
<td> <td>
<div class="product-name" v-text="item.productName || '未命名产品'" /> <div class="product-name" v-text="item.productName + '长名称长名称长名称长名称长名称长名称长名称长名称长名称' || '未命名产品'" />
<div class="product-type" v-text="getProductTypeText(item.productType)" />
<div v-if="hasLowStockSku(item)" class="sku-list"> <div v-if="hasLowStockSku(item)" class="sku-list">
<div v-for="sku in getLowStockSkuList(item)" :key="`${sku.skuId}-${sku.skuCode}`" class="sku-item"> <div v-for="sku in getLowStockSkuList(item)" :key="`${sku.skuId}-${sku.skuCode}`" class="sku-item">
<span v-text="getSkuName(sku)" /> <span v-text="getSkuName(sku)" />
@@ -73,10 +72,6 @@ function getLowStockSkuList(item: GaoProductStockHealthView): GaoProductSkuStock
function getSkuName(item: GaoProductSkuStockHealthView): string { function getSkuName(item: GaoProductSkuStockHealthView): string {
return [item.skuName || "未命名规格", item.specSummary].filter(Boolean).join(" · "); return [item.skuName || "未命名规格", item.specSummary].filter(Boolean).join(" · ");
} }
function getProductTypeText(value?: string): string {
return ({ SERVICE: "服务", GOODS: "商品", CONSUMABLE: "耗材" } as Record<string, string>)[value || ""] || "未分类";
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@@ -118,14 +113,14 @@ function getProductTypeText(value?: string): string {
th { th {
color: #94a3b8; color: #94a3b8;
padding: .5rem .375rem; padding: .25rem;
text-align: left; text-align: left;
font-weight: 500; font-weight: 500;
} }
td { td {
color: #334155; color: #334155;
padding: .625rem .375rem; padding: .25rem;
border-top: 1px solid rgba(148, 163, 184, .16); border-top: 1px solid rgba(148, 163, 184, .16);
} }
@@ -135,7 +130,7 @@ function getProductTypeText(value?: string): string {
} }
.product-name { .product-name {
max-width: 7rem; max-width: 6rem;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
@@ -32,6 +32,7 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
xAxis: { xAxis: {
type: "category", type: "category",
data: labelList, data: labelList,
axisLine: { onZero: true },
axisTick: { show: false }, axisTick: { show: false },
axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true } axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true }
}, },
@@ -39,12 +40,12 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
{ {
name: "入库", name: "入库",
type: "bar", type: "bar",
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.inQuantity)) data: visibleDateTextList.map(dateText => Math.abs(getNumber(getDailyItem(props.list, dateText)?.inQuantity)))
}, },
{ {
name: "出库", name: "出库",
type: "bar", type: "bar",
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.outQuantity)) data: visibleDateTextList.map(dateText => -Math.abs(getNumber(getDailyItem(props.list, dateText)?.outQuantity)))
}, },
{ {
name: "库存净变化", name: "库存净变化",
@@ -55,12 +56,12 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
{ {
name: "新增预占", name: "新增预占",
type: "line", type: "line",
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.reserveQuantity)) data: visibleDateTextList.map(dateText => Math.abs(getNumber(getDailyItem(props.list, dateText)?.reserveQuantity)))
}, },
{ {
name: "释放预占", name: "释放预占",
type: "line", type: "line",
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.releaseQuantity)) data: visibleDateTextList.map(dateText => -Math.abs(getNumber(getDailyItem(props.list, dateText)?.releaseQuantity)))
} }
] ]
}; };
+4 -4
View File
@@ -56,10 +56,10 @@ export function buildHorizontalBarOption(categoryList: string[]): EChartsOption
return { return {
color: chartColorList, color: chartColorList,
grid: { grid: {
top: 12, top: 32,
left: 8, left: 0,
right: 18, right: 0,
bottom: 12, bottom: 0,
containLabel: true containLabel: true
}, },
legend: { legend: {