fix SalesOrderList.vue style
This commit is contained in:
@@ -88,9 +88,12 @@ async function loadData(): Promise<void> {
|
|||||||
}
|
}
|
||||||
function getItemTitle(item: SourceItem): string { return `${item.productNameSnapshot || "未命名产品"} · ${item.unitSnapshot || "未设置单位"}`; }
|
function getItemTitle(item: SourceItem): string { return `${item.productNameSnapshot || "未命名产品"} · ${item.unitSnapshot || "未设置单位"}`; }
|
||||||
function getItemDescription(item: SourceItem): string {
|
function getItemDescription(item: SourceItem): string {
|
||||||
return isPackage.value
|
if (isPackage.value) {
|
||||||
? `可履约 ${getPackageQuantityText((item as CustomerPackageItem).availableQuantity)}`
|
return `本次可履约 ${getPackageQuantityText((item as CustomerPackageItem).availableQuantity)}`;
|
||||||
: `待履约 ${getPackageQuantityText((item as SalesOrderItem).reservedQuantity)}`;
|
}
|
||||||
|
const orderItem = item as SalesOrderItem;
|
||||||
|
const availableQuantity = Math.max(0, Number(orderItem.quantity || 0) - Number(orderItem.reservedQuantity || 0) - Number(orderItem.fulfilledQuantity || 0));
|
||||||
|
return `本次可履约 ${getPackageQuantityText(availableQuantity.toFixed(3))}`;
|
||||||
}
|
}
|
||||||
function confirmTime(value: string | number): void { deliveredAtText.value = String(value); pickerVisible.value = false; }
|
function confirmTime(value: string | number): void { deliveredAtText.value = String(value); pickerVisible.value = false; }
|
||||||
function resolveTimestamp(value: string): number | undefined {
|
function resolveTimestamp(value: string): number | undefined {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
v-for="item in order.itemList"
|
v-for="item in order.itemList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:title="item.productNameSnapshot || '未命名产品'"
|
:title="item.productNameSnapshot || '未命名产品'"
|
||||||
:description="`${item.quantity || 0} ${item.unitSnapshot || ''} · 已履约 ${item.fulfilledQuantity || 0} · 待履约 ${item.reservedQuantity || 0}`"
|
:description="getItemDescription(item)"
|
||||||
:note="formatPackageAmount(item.lineAmount)"
|
:note="formatPackageAmount(item.lineAmount)"
|
||||||
/>
|
/>
|
||||||
</t-cell-group>
|
</t-cell-group>
|
||||||
@@ -55,8 +55,8 @@ import SalesOrderAPI from "@/api/SalesOrderAPI";
|
|||||||
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
||||||
import SalesOrderFinancePanel from "@/pages/salesOrder/components/SalesOrderFinancePanel.vue";
|
import SalesOrderFinancePanel from "@/pages/salesOrder/components/SalesOrderFinancePanel.vue";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { SalesOrder } from "@/types/SalesOrder";
|
import type { SalesOrder, SalesOrderItem } from "@/types/SalesOrder";
|
||||||
import { formatPackageAmount } from "@/utils/package";
|
import { formatPackageAmount, getPackageQuantityText } from "@/utils/package";
|
||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { getSalesOrderStatusText, getSalesOrderTypeText, getStatusTheme } from "@/utils/salesOrder";
|
import { getSalesOrderStatusText, getSalesOrderTypeText, getStatusTheme } from "@/utils/salesOrder";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
@@ -75,6 +75,12 @@ const canReceive = computed(() => !!order.value && order.value.status !== "CANCE
|
|||||||
const canRefund = computed(() => !!order.value && order.value.status === "CANCELLED" && order.value.refundStatus === "PENDING" && userStore.hasPermissions(permissionCodes.fund.refund));
|
const canRefund = computed(() => !!order.value && order.value.status === "CANCELLED" && order.value.refundStatus === "PENDING" && userStore.hasPermissions(permissionCodes.fund.refund));
|
||||||
const canConfirmNoRefund = computed(() => !!order.value && order.value.status === "CANCELLED" && order.value.refundStatus === "PENDING" && userStore.hasPermissions(permissionCodes.fund.refund));
|
const canConfirmNoRefund = computed(() => !!order.value && order.value.status === "CANCELLED" && order.value.refundStatus === "PENDING" && userStore.hasPermissions(permissionCodes.fund.refund));
|
||||||
const canCancel = computed(() => !!order.value && order.value.status === "ACTIVE" && userStore.hasPermissions(permissionCodes.salesOrder.cancel));
|
const canCancel = computed(() => !!order.value && order.value.status === "ACTIVE" && userStore.hasPermissions(permissionCodes.salesOrder.cancel));
|
||||||
|
function getItemDescription(item: SalesOrderItem): string {
|
||||||
|
const quantity = Number(item.quantity || 0);
|
||||||
|
const fulfilledQuantity = Number(item.fulfilledQuantity || 0);
|
||||||
|
const remainingQuantity = Math.max(0, quantity - fulfilledQuantity);
|
||||||
|
return `${getPackageQuantityText(item.quantity)} ${item.unitSnapshot || ""} · 已履约 ${getPackageQuantityText(item.fulfilledQuantity)} · 剩余待履约 ${getPackageQuantityText(remainingQuantity.toFixed(3))}`;
|
||||||
|
}
|
||||||
|
|
||||||
async function loadData(): Promise<void> {
|
async function loadData(): Promise<void> {
|
||||||
const id = String(route.params.id || "");
|
const id = String(route.params.id || "");
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
</t-dropdown-menu>
|
</t-dropdown-menu>
|
||||||
</div>
|
</div>
|
||||||
<t-pull-down-refresh
|
<t-pull-down-refresh
|
||||||
|
class="list-wrap"
|
||||||
v-model="refreshing"
|
v-model="refreshing"
|
||||||
:refresh-timeout="30000"
|
:refresh-timeout="30000"
|
||||||
@refresh="refresh"
|
@refresh="refresh"
|
||||||
@@ -34,30 +35,20 @@
|
|||||||
class="view-tip"
|
class="view-tip"
|
||||||
v-text="viewMode === 'pending-receive' ? '当前显示未收清订单' : '当前显示待处理退款订单'"
|
v-text="viewMode === 'pending-receive' ? '当前显示未收清订单' : '当前显示待处理退款订单'"
|
||||||
/>
|
/>
|
||||||
<t-cell-group v-if="displayList.length" theme="card">
|
<div v-if="displayList.length" class="items">
|
||||||
<t-cell
|
<sales-order-list-cell
|
||||||
|
class="item"
|
||||||
v-for="(item, index) in displayList"
|
v-for="(item, index) in displayList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
arrow
|
:order="item"
|
||||||
|
:can-receive="canReceive"
|
||||||
|
:can-fulfill="canCreateFulfillment"
|
||||||
v-entrance-transition="index * 30"
|
v-entrance-transition="index * 30"
|
||||||
@click="toDetail(item)"
|
@click="toDetail(item)"
|
||||||
>
|
@receive="toPayment(item)"
|
||||||
<template #title>
|
@fulfill="toFulfillment(item)"
|
||||||
<div class="title">
|
/>
|
||||||
<strong v-text="item.orderNo || item.id || '未命名订单'" />
|
|
||||||
<t-tag :content="getSalesOrderStatusText(item.status)" variant="light-outline" :theme="getStatusTheme(item.status)" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
<template #note><span class="amount" v-text="formatPackageAmount(item.saleAmount)" /></template>
|
|
||||||
<template #description>
|
|
||||||
<div class="description">
|
|
||||||
<span v-text="getSalesOrderTypeText(item.orderType)" />
|
|
||||||
<span v-text="item.customer?.name || item.customerId || '未知客户'" />
|
|
||||||
<t-tag :content="getSalesPaymentStatusText(item.paymentStatus)" theme="primary" variant="outline" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</t-cell>
|
|
||||||
</t-cell-group>
|
|
||||||
<div v-else class="center-tips" v-text="loading ? '正在加载销售订单' : '暂无销售订单'" />
|
<div v-else class="center-tips" v-text="loading ? '正在加载销售订单' : '暂无销售订单'" />
|
||||||
<div class="center-tips" v-text="loading ? '正在加载' : noMore ? '已经到底了' : '上拉加载更多'" />
|
<div class="center-tips" v-text="loading ? '正在加载' : noMore ? '已经到底了' : '上拉加载更多'" />
|
||||||
</t-pull-down-refresh>
|
</t-pull-down-refresh>
|
||||||
@@ -71,11 +62,10 @@ import { Toolkit } from "timi-web";
|
|||||||
import { useUserStore } from "timi-web-api";
|
import { useUserStore } from "timi-web-api";
|
||||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||||
import NavBarActionMenu from "@/components/NavBarActionMenu.vue";
|
import NavBarActionMenu from "@/components/NavBarActionMenu.vue";
|
||||||
|
import SalesOrderListCell from "@/pages/salesOrder/components/SalesOrderListCell.vue";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { SalesOrder, SalesOrderStatus, SalesOrderType, SalesPaymentStatus } from "@/types/SalesOrder";
|
import type { SalesOrder, SalesOrderStatus, SalesOrderType, SalesPaymentStatus } from "@/types/SalesOrder";
|
||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { formatPackageAmount } from "@/utils/package";
|
|
||||||
import { getSalesOrderStatusText, getSalesOrderTypeText, getSalesPaymentStatusText, getStatusTheme } from "@/utils/salesOrder";
|
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
@@ -85,7 +75,15 @@ const router = useRouter();
|
|||||||
const navBarStore = useNavBarStore();
|
const navBarStore = useNavBarStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const canCreate = computed(() => userStore.hasPermissions(permissionCodes.salesOrder.create));
|
const canCreate = computed(() => userStore.hasPermissions(permissionCodes.salesOrder.create));
|
||||||
const actions = computed(() => canCreate.value ? [{ key: "create", text: "直接产品下单", icon: "add", theme: "primary" as const, action: toCreate }] : []);
|
const canReceive = computed(() => userStore.hasPermissions(permissionCodes.fundReceive.create));
|
||||||
|
const canCreateFulfillment = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.create));
|
||||||
|
const actions = computed(() => canCreate.value ? [{
|
||||||
|
key: "create",
|
||||||
|
text: "直接产品下单",
|
||||||
|
icon: "add",
|
||||||
|
theme: "primary" as const,
|
||||||
|
action: toCreate
|
||||||
|
}] : []);
|
||||||
const keyword = ref("");
|
const keyword = ref("");
|
||||||
const orderType = ref("ALL");
|
const orderType = ref("ALL");
|
||||||
const viewMode = computed(() => route.query.view === "pending-receive" || route.query.view === "pending-refund" ? String(route.query.view) : "");
|
const viewMode = computed(() => route.query.view === "pending-receive" || route.query.view === "pending-refund" ? String(route.query.view) : "");
|
||||||
@@ -127,14 +125,69 @@ async function fetchList(reset: boolean): Promise<void> {
|
|||||||
noMore.value = rows.length < pageSize || total.value <= list.value.length;
|
noMore.value = rows.length < pageSize || total.value <= list.value.length;
|
||||||
} finally { loading.value = false; }
|
} finally { loading.value = false; }
|
||||||
}
|
}
|
||||||
async function refresh(): Promise<void> { try { await fetchList(true); } catch (error) { Message.error(getErrorMessage(error, "销售订单加载失败")); } finally { refreshing.value = false; } }
|
|
||||||
async function loadMore(): Promise<void> { try { await fetchList(false); } catch (error) { Message.error(getErrorMessage(error, "销售订单加载失败")); } }
|
async function refresh(): Promise<void> {
|
||||||
async function selectOrderType(value: DropdownValue): Promise<void> { if (!Array.isArray(value)) { orderType.value = String(value); await fetchList(true); } }
|
try {
|
||||||
async function selectStatus(value: DropdownValue): Promise<void> { if (!Array.isArray(value)) { status.value = String(value); await fetchList(true); } }
|
await fetchList(true);
|
||||||
async function selectPaymentStatus(value: DropdownValue): Promise<void> { if (!Array.isArray(value)) { paymentStatus.value = String(value); await fetchList(true); } }
|
} catch (error) {
|
||||||
async function toDetail(item: SalesOrder): Promise<void> { if (item.id) await router.push(`/management/sales-orders/${item.id}`); }
|
Message.error(getErrorMessage(error, "销售订单加载失败"));
|
||||||
async function toCreate(): Promise<void> { await router.push("/management/sales-orders/create"); }
|
} finally {
|
||||||
const search = Toolkit.debounce(async () => { try { await fetchList(true); } catch (error) { Message.error(getErrorMessage(error, "销售订单加载失败")); } }, false);
|
refreshing.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadMore(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await fetchList(false);
|
||||||
|
} catch (error) {
|
||||||
|
Message.error(getErrorMessage(error, "销售订单加载失败"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectOrderType(value: DropdownValue): Promise<void> {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
orderType.value = String(value);
|
||||||
|
await fetchList(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectStatus(value: DropdownValue): Promise<void> {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
status.value = String(value);
|
||||||
|
await fetchList(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectPaymentStatus(value: DropdownValue): Promise<void> {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
paymentStatus.value = String(value);
|
||||||
|
await fetchList(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toDetail(item: SalesOrder): Promise<void> {
|
||||||
|
if (item.id) await router.push(`/management/sales-orders/${item.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toPayment(item: SalesOrder): Promise<void> {
|
||||||
|
if (item.id) await router.push(`/management/sales-orders/${item.id}/payment`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toFulfillment(item: SalesOrder): Promise<void> {
|
||||||
|
if (item.id) await router.push({ path: "/management/order-fulfillments/create", query: { salesOrderId: item.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toCreate(): Promise<void> {
|
||||||
|
await router.push("/management/sales-orders/create");
|
||||||
|
}
|
||||||
|
|
||||||
|
const search = Toolkit.debounce(async () => {
|
||||||
|
try {
|
||||||
|
await fetchList(true);
|
||||||
|
} catch (error) {
|
||||||
|
Message.error(getErrorMessage(error, "销售订单加载失败"));
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
watch(keyword, () => search());
|
watch(keyword, () => search());
|
||||||
watch(viewMode, async value => {
|
watch(viewMode, async value => {
|
||||||
status.value = value === "pending-refund" ? "CANCELLED" : "ALL";
|
status.value = value === "pending-refund" ? "CANCELLED" : "ALL";
|
||||||
@@ -174,11 +227,11 @@ onUnmounted(() => { navBarStore.clearRight(); search.cancel(); });
|
|||||||
--td-spacer-1: 6px;
|
--td-spacer-1: 6px;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
border: 1px solid rgba(148, 163, 184, .18);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-top: -1rem;
|
margin-top: -1rem;
|
||||||
padding-top: .8rem;
|
padding-top: .8rem;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid rgba(148, 163, 184, .18);
|
|
||||||
border-radius: 0 0 1rem 1rem;
|
border-radius: 0 0 1rem 1rem;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
@@ -197,21 +250,8 @@ onUnmounted(() => { navBarStore.clearRight(); search.cancel(); });
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.title, .description {
|
.list-wrap {
|
||||||
gap: .5rem;
|
padding-top: 1rem;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
color: var(--td-text-color-secondary);
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.amount {
|
|
||||||
color: var(--td-brand-color);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-tip {
|
.view-tip {
|
||||||
color: var(--td-text-color-secondary);
|
color: var(--td-text-color-secondary);
|
||||||
@@ -219,6 +259,18 @@ onUnmounted(() => { navBarStore.clearRight(); search.cancel(); });
|
|||||||
font-size: .8125rem;
|
font-size: .8125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.items {
|
||||||
|
gap: 1rem;
|
||||||
|
display: flex;
|
||||||
|
padding: 0 1rem;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
color: var(--td-text-color-secondary);
|
color: var(--td-text-color-secondary);
|
||||||
padding: 3rem 1rem;
|
padding: 3rem 1rem;
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sales-order-cell bg-white">
|
||||||
|
<div class="header">
|
||||||
|
<div class="left">
|
||||||
|
<t-image
|
||||||
|
v-if="photoURL"
|
||||||
|
:src="photoURL"
|
||||||
|
:alt="customerName"
|
||||||
|
fit="cover"
|
||||||
|
class="avatar"
|
||||||
|
/>
|
||||||
|
<div v-else class="avatar fallback" v-text="avatarText" />
|
||||||
|
</div>
|
||||||
|
<div class="main">
|
||||||
|
<div class="customer-status">
|
||||||
|
<strong class="customer" v-text="customerName" />
|
||||||
|
<div class="status">
|
||||||
|
<t-tag
|
||||||
|
:content="getSalesOrderStatusText(props.order.status)"
|
||||||
|
size="small"
|
||||||
|
variant="light-outline"
|
||||||
|
:theme="getStatusTheme(props.order.status)"
|
||||||
|
/>
|
||||||
|
<t-tag
|
||||||
|
:content="getSalesPaymentStatusText(props.order.paymentStatus)"
|
||||||
|
:theme="getPaymentStatusTheme(props.order.paymentStatus)"
|
||||||
|
size="small"
|
||||||
|
variant="light-outline"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="order-no" v-text="orderNoText" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<t-divider />
|
||||||
|
<div class="product">
|
||||||
|
<div class="items">
|
||||||
|
<t-tag
|
||||||
|
v-for="(item, index) in itemList"
|
||||||
|
:key="item.id || `${item.productId}-${index}`"
|
||||||
|
:content="item.productNameSnapshot || '未命名产品'"
|
||||||
|
theme="primary"
|
||||||
|
variant="light"
|
||||||
|
/>
|
||||||
|
<t-tag v-if="!itemList.length" content="暂无产品" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary">
|
||||||
|
<span v-text="productSummaryText" />
|
||||||
|
</div>
|
||||||
|
<t-divider />
|
||||||
|
<div class="time-payment">
|
||||||
|
<pass-time-label class="time gray" :timestamp="props.order.purchasedAt" underline />
|
||||||
|
<div class="amount">
|
||||||
|
<span class="red">¥</span>
|
||||||
|
<strong class="value red" v-text="formatPackageAmount(props.order.saleAmount)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="showActions" class="actions" @click.stop>
|
||||||
|
<t-button
|
||||||
|
v-if="showReceiveAction"
|
||||||
|
variant="outline"
|
||||||
|
size="extra-small"
|
||||||
|
@click.stop="emit('receive')"
|
||||||
|
>登记收款</t-button>
|
||||||
|
<t-button
|
||||||
|
v-if="showFulfillAction"
|
||||||
|
variant="outline"
|
||||||
|
size="extra-small"
|
||||||
|
@click.stop="emit('fulfill')"
|
||||||
|
>履约产品</t-button>
|
||||||
|
<t-button
|
||||||
|
class="detail"
|
||||||
|
size="extra-small"
|
||||||
|
theme="primary"
|
||||||
|
content="详情"
|
||||||
|
@click="emit('click')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PassTimeLabel, Time } from "timi-web";
|
||||||
|
import type { SalesOrder, SalesPaymentStatus } from "@/types/SalesOrder";
|
||||||
|
import { getCustomerPhotoURL } from "@/utils/customer";
|
||||||
|
import { formatPackageAmount, getPackageQuantityText } from "@/utils/package";
|
||||||
|
import { getSalesOrderStatusText, getSalesPaymentStatusText, getStatusTheme } from "@/utils/salesOrder";
|
||||||
|
|
||||||
|
defineOptions({ name: "SalesOrderListCell" });
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
order: SalesOrder;
|
||||||
|
canReceive: boolean;
|
||||||
|
canFulfill: boolean;
|
||||||
|
}>();
|
||||||
|
const emit = defineEmits<{
|
||||||
|
click: [];
|
||||||
|
receive: [];
|
||||||
|
fulfill: [];
|
||||||
|
}>();
|
||||||
|
const itemList = computed(() => props.order.itemList || []);
|
||||||
|
const customerName = computed(() => props.order.customer?.name || props.order.customer?.code || props.order.customerId || "未知客户");
|
||||||
|
const avatarText = computed(() => customerName.value.slice(0, 1) || "客");
|
||||||
|
const photoURL = computed(() => getCustomerPhotoURL(props.order.customer, 64));
|
||||||
|
const orderNoText = computed(() => `订单号:${props.order.orderNo || props.order.id || "未知"}`);
|
||||||
|
const totalProductQuantity = computed(() => sumProductQuantity("quantity"));
|
||||||
|
const fulfilledProductQuantity = computed(() => sumProductQuantity("fulfilledQuantity"));
|
||||||
|
const productSummaryText = computed(() => `共 ${itemList.value.length} 项 · 总数量 ${totalProductQuantity.value} · 已履约 ${fulfilledProductQuantity.value}`);
|
||||||
|
const showReceiveAction = computed(() => props.canReceive && props.order.status !== "CANCELLED" && props.order.paymentStatus !== "PAID");
|
||||||
|
const showFulfillAction = computed(() => props.canFulfill && props.order.orderType === "DIRECT_PRODUCT" && props.order.status === "ACTIVE");
|
||||||
|
const showActions = computed(() => showReceiveAction.value || showFulfillAction.value);
|
||||||
|
|
||||||
|
function getPaymentStatusTheme(status?: SalesPaymentStatus): "success" | "warning" | "danger" | "default" {
|
||||||
|
if (status === "PAID") {
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
if (status === "PARTIALLY_PAID") {
|
||||||
|
return "warning";
|
||||||
|
}
|
||||||
|
if (status === "UNPAID") {
|
||||||
|
return "danger";
|
||||||
|
}
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
function sumProductQuantity(field: "quantity" | "fulfilledQuantity"): string {
|
||||||
|
let total = 0;
|
||||||
|
for (const item of itemList.value) {
|
||||||
|
const quantity = Number(item[field] || 0);
|
||||||
|
if (Number.isFinite(quantity)) {
|
||||||
|
total += quantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getPackageQuantityText(total.toFixed(3));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.sales-order-cell {
|
||||||
|
gap: .5rem;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
gap: .5rem;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 3.5rem;
|
||||||
|
height: 3.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
gap: .5rem;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.customer-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.customer {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
gap: .25rem;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-no {
|
||||||
|
color: var(--td-text-color-secondary);
|
||||||
|
font-size: .75rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.product {
|
||||||
|
gap: .5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.items {
|
||||||
|
flex: 1;
|
||||||
|
gap: .35rem;
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
scrollbar-width: none;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
|
:deep(.t-tag) {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
color: var(--td-text-color-secondary);
|
||||||
|
font-size: .75rem;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-payment {
|
||||||
|
gap: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-size: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: end;
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
--td-button-border-radius: .5rem;
|
||||||
|
|
||||||
|
gap: .5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
|
||||||
|
.detail {
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
padding-right: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.sales-order-cell .avatar .t-image__img) {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html.theme-dark) .sales-order-cell {
|
||||||
|
.fallback {
|
||||||
|
background: #115e59;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user