update sku tags

This commit is contained in:
Timi
2026-09-07 15:34:02 +08:00
parent 149c026234
commit d56719df1c
12 changed files with 253 additions and 55 deletions
@@ -24,8 +24,27 @@
</div>
<div class="main">
<div class="title">
<strong v-text="getItemTitle(item)" />
<span v-text="getItemDescription(item)" />
<div class="heading">
<strong v-text="item.productNameSnapshot || '未命名产品'" />
<span v-text="getItemDescription(item)" />
</div>
<div
v-if="item.skuCodeSnapshot || item.skuSpecSnapshot?.length || item.skuNameSnapshot || item.specSummarySnapshot || item.unitSnapshot"
class="sku-tags"
>
<t-tag
v-if="item.skuCodeSnapshot"
size="small"
theme="primary"
variant="light"
:content="item.skuCodeSnapshot"
/>
<product-sku-snapshot-tags
:snapshot="item.skuSpecSnapshot"
:fallback="item.skuNameSnapshot || item.specSummarySnapshot"
:unit="item.unitSnapshot"
/>
</div>
</div>
<div class="input">
<t-input
@@ -87,6 +106,7 @@
import SalesOrderAPI from "@/api/SalesOrderAPI";
import FulfillmentAPI from "@/api/FulfillmentAPI";
import PhotoScroller from "@/components/PhotoScroller.vue";
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
import { useNavBarStore } from "@/store/navBarStore";
import { useUserStore } from "timi-web-api";
import type { GaoOrderFulfillmentItemRequest } from "@/types/Fulfillment";
@@ -140,12 +160,7 @@ function getAvailableQuantity(item: GaoSalesOrderItem): string {
}
function getItemDescription(item: GaoSalesOrderItem): string {
return ` · ${item.unitSnapshot || "业务单位"} · 剩余 ${getAvailableQuantity(item)}`;
}
function getItemTitle(item: GaoSalesOrderItem): string {
const skuText = item.skuNameSnapshot || item.specSummarySnapshot;
return `${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""}`;
return `剩余 ${getAvailableQuantity(item)}`;
}
function fillAllQuantity(item: GaoSalesOrderItem): void {
@@ -281,7 +296,37 @@ onUnmounted(() => navBarStore.clearRight());
justify-content: space-between;
.title {
gap: .375rem;
display: flex;
flex-direction: column;
.heading {
gap: .5rem;
display: flex;
min-width: 0;
align-items: baseline;
justify-content: space-between;
strong {
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
span {
color: var(--td-text-color-secondary);
font-size: .75rem;
white-space: nowrap;
}
}
.sku-tags {
gap: .375rem;
display: flex;
flex-wrap: wrap;
align-items: center;
}
}
.input {
@@ -30,10 +30,19 @@
class="item"
v-for="item in fulfillment.itemList || []"
:key="item.id"
:title="getItemTitle(item)"
:description="`${item.quantity} ${item.unitSnapshot}`"
:title="item.productNameSnapshot || '未命名产品'"
:note="`待履约 ${item.reservedBefore || 0} → ${item.reservedAfter || 0}`"
>
<template #description>
<div class="item-description">
<product-sku-snapshot-tags
:snapshot="item.skuSpecSnapshot"
:fallback="item.skuNameSnapshot || item.specSummarySnapshot || '默认 SKU'"
:unit="item.unitSnapshot"
/>
<span v-text="`履约数量 ${item.quantity || 0}`" />
</div>
</template>
<template #leftIcon>
<t-image
v-if="getProductPhotoURL(item)"
@@ -121,6 +130,7 @@ import { useUserStore } from "timi-web-api";
import FulfillmentAPI from "@/api/FulfillmentAPI";
import SalesOrderAPI from "@/api/SalesOrderAPI";
import PhotoScroller from "@/components/PhotoScroller.vue";
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
import { useNavBarStore } from "@/store/navBarStore";
import type { OrderFulfillment } from "@/types/Fulfillment";
import type { GaoSalesOrderItemType, SalesOrder } from "@/types/SalesOrder";
@@ -175,10 +185,6 @@ const sourceItemType = computed<GaoSalesOrderItemType | undefined>(() => {
return order.value?.itemList?.find(item => item.id === sourceOrderItemId)?.itemType;
});
function getItemTitle(item: NonNullable<OrderFulfillment["itemList"]>[number]): string {
const skuText = item.skuNameSnapshot || item.specSummarySnapshot;
return `${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""}`;
}
async function loadData(): Promise<void> {
const id = String(route.params.id || "");
if (!id) return;
@@ -318,6 +324,13 @@ onUnmounted(() => navBarStore.clearRight());
.item {
padding: 10px;
.item-description {
gap: .375rem;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.avatar {
width: 3.5rem;
height: 3.5rem;
@@ -29,13 +29,21 @@
<t-divider />
<div class="product">
<div class="items">
<t-tag
<div
v-for="(item, index) in itemList"
:key="item.id || `${item.skuId}-${item.skuUnitId}-${index}`"
:content="getProductText(item)"
theme="primary"
variant="light"
/>
class="item"
>
<div class="item-title">
<strong v-text="item.productNameSnapshot || '未命名产品'" />
<span v-text="`× ${getPackageQuantityText(item.quantity)}`" />
</div>
<product-sku-snapshot-tags
:snapshot="item.skuSpecSnapshot"
:fallback="item.skuNameSnapshot || item.specSummarySnapshot || '默认 SKU'"
:unit="item.unitSnapshot"
/>
</div>
<t-tag v-if="!itemList.length" content="暂无产品" />
</div>
</div>
@@ -64,7 +72,8 @@
<script setup lang="ts">
import { PassTimeLabel } from "timi-web";
import type { OrderFulfillment, OrderFulfillmentItem } from "@/types/Fulfillment";
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
import type { OrderFulfillment } from "@/types/Fulfillment";
import { getCustomerPhotoURL } from "@/utils/customer";
import { getPackageQuantityText } from "@/utils/package";
import { getFulfillmentStatusText, getStatusTheme } from "@/utils/salesOrder";
@@ -99,14 +108,6 @@ const totalProductQuantity = computed(() => {
});
const productSummaryText = computed(() => `共 ${itemList.value.length} 项 · 总数量 ${totalProductQuantity.value}`);
function getProductText(item: OrderFulfillmentItem): string {
const productName = item.productNameSnapshot || "未命名产品";
const skuText = item.skuNameSnapshot || item.specSummarySnapshot;
const quantity = getPackageQuantityText(item.quantity);
const unit = item.unitSnapshot ? ` ${item.unitSnapshot}` : "";
return `${productName}${skuText ? ` · ${skuText}` : ""} × ${quantity}${unit}`;
}
function getUserName(user: OrderFulfillment["operator"], userId: string | undefined, fallback: string): string {
return user?.nick || user?.name || userId || fallback;
}
@@ -178,6 +179,34 @@ function getUserName(user: OrderFulfillment["operator"], userId: string | undefi
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
.item {
gap: .375rem;
display: flex;
min-width: 9rem;
flex: 0 0 auto;
flex-direction: column;
white-space: normal;
.item-title {
gap: .375rem;
display: flex;
align-items: baseline;
strong {
max-width: 9rem;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
span {
color: var(--td-text-color-secondary);
font-size: .75rem;
white-space: nowrap;
}
}
}
:deep(.t-tag) {
flex: 0 0 auto;
}
@@ -5,7 +5,12 @@
<section class="snapshot-head" v-entrance-transition>
<div class="eyebrow">库存 SKU 快照</div>
<h2 v-text="record.productNameSnapshot || '未命名产品'" />
<div class="sku" v-text="record.skuNameSnapshot || record.specSummarySnapshot || '默认 SKU'" />
<product-sku-snapshot-tags
class="sku"
:snapshot="record.skuSpecSnapshot"
:fallback="record.skuNameSnapshot || record.specSummarySnapshot || '默认 SKU'"
:unit="record.unitSnapshot"
/>
<div class="meta">
<span v-text="record.skuCodeSnapshot || '无 SKU 编码'" />
<t-tag size="small" variant="light-outline" :theme="getOperationTheme(record.onHandDelta)" :content="getStockOperationText(record.operationType)" />
@@ -132,6 +137,7 @@ import { PassTimeLabel } from "timi-web";
import { useUserStore, type Attachment } from "timi-web-api";
import ProductAPI from "@/api/ProductAPI";
import PhotoScroller from "@/components/PhotoScroller.vue";
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
import { useNavBarStore } from "@/store/navBarStore";
import type { PhotoItem } from "@/types/Photo";
import type { StockBizType, StockRecord } from "@/types/Product";
+7 -6
View File
@@ -54,7 +54,12 @@
</template>
<template #description>
<div class="description">
<span v-text="getRecordTitle(record)" />
<strong v-text="record.productNameSnapshot || '未命名产品'" />
<product-sku-snapshot-tags
:snapshot="record.skuSpecSnapshot"
:fallback="record.skuNameSnapshot || record.specSummarySnapshot || '默认 SKU'"
:unit="record.unitSnapshot"
/>
<pass-time-label :timestamp="record.occurredAt" underline />
</div>
</template>
@@ -70,6 +75,7 @@
import { type DropdownValue } from "tdesign-mobile-vue";
import { PassTimeLabel } from "timi-web";
import ProductAPI from "@/api/ProductAPI";
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
import { useNavBarStore } from "@/store/navBarStore";
import type { StockOperationType, StockRecord, StockRecordDirection } from "@/types/Product";
import { getErrorMessage } from "@/utils/error";
@@ -157,11 +163,6 @@ function getStockQuantityText(record: StockRecord): string {
return `${getProductQuantityText(record.quantity)}${unitText}`;
}
function getRecordTitle(record: StockRecord): string {
const skuText = record.skuNameSnapshot || record.specSummarySnapshot;
return `${record.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""}${record.unitSnapshot ? ` / ${record.unitSnapshot}` : ""}`;
}
function getDeltaText(value?: number | string): string {
const amount = Number(value || 0);
const text = String(value || "0").replace(/^-/, "");
+11 -9
View File
@@ -96,7 +96,7 @@
</div>
<div class="main">
<div class="title">
<span class="name text-bold" v-text="getItemTitle(item)" />
<span class="name text-bold" v-text="item.productNameSnapshot || '未命名产品'" />
<t-tag
:content="getItemFulfillmentStatus(item).text"
:theme="getItemFulfillmentStatus(item).theme"
@@ -111,13 +111,17 @@
/>
<t-tag v-else size="small" content="套餐权益" />
<t-tag
v-if="item.skuCodeSnapshot || item.skuNameSnapshot || item.specSummarySnapshot"
v-if="item.skuCodeSnapshot"
size="small"
theme="primary"
variant="light"
:content="item.skuCodeSnapshot || item.skuNameSnapshot || item.specSummarySnapshot"
:content="item.skuCodeSnapshot"
/>
<product-sku-snapshot-tags
:snapshot="item.skuSpecSnapshot"
:fallback="item.skuNameSnapshot || item.specSummarySnapshot"
:unit="item.unitSnapshot"
/>
<t-tag size="small" theme="primary" variant="light" :content="item.unitSnapshot" />
</div>
</div>
</div>
@@ -216,6 +220,7 @@ import FulfillmentAPI from "@/api/FulfillmentAPI";
import SalesOrderAPI from "@/api/SalesOrderAPI";
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
import PhotoScroller from "@/components/PhotoScroller.vue";
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
import { useNavBarStore } from "@/store/navBarStore";
import type { GaoSalesOrderItem, GaoSalesPaymentStatus, SalesOrder } from "@/types/SalesOrder";
import type { PhotoItem } from "@/types/Photo";
@@ -257,11 +262,6 @@ const canCancel = computed(() => !!order.value && order.value.status === "ACTIVE
const canFulfillByPayment = computed(() => order.value?.paymentStatus === "PAID" || (order.value?.paymentStatus === "PARTIALLY_PAID" && canOperateStock.value));
const photoURLList = computed(() => photoItems.value.map(item => item.url));
function getItemTitle(item: GaoSalesOrderItem): string {
const skuText = item.skuNameSnapshot || item.specSummarySnapshot;
return `${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""}`;
}
function toPhotoItems(attachmentList?: readonly Partial<Attachment>[]): PhotoItem[] {
return (attachmentList || [])
.filter(item => !!item.id)
@@ -594,6 +594,8 @@ onUnmounted(() => navBarStore.clearRight());
.description {
gap: .5rem;
display: flex;
flex-wrap: wrap;
align-items: center;
}
}
}