fix sku display
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
v-if="item.skuCodeSnapshot || item.skuSpecSnapshot?.length || item.skuNameSnapshot || item.specSummarySnapshot || item.unitSnapshot"
|
||||
class="sku-tags"
|
||||
>
|
||||
<product-sku-snapshot-tags
|
||||
<product-sku-tags
|
||||
:product-name="item.productNameSnapshot"
|
||||
:snapshot="item.skuSpecSnapshot"
|
||||
:fallback="item.specSummarySnapshot || item.skuNameSnapshot"
|
||||
@@ -128,7 +128,7 @@ import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||
import FulfillmentAPI from "@/api/FulfillmentAPI";
|
||||
import ProductAPI from "@/api/ProductAPI";
|
||||
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
|
||||
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
|
||||
import ProductSkuTags from "@/components/ProductSkuTags.vue";
|
||||
import ProductStockPreview from "@/components/ProductStockPreview.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { Attachment } from "timi-web-api";
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<template #description>
|
||||
<div class="item-description">
|
||||
<t-tag v-if="isGiftedFulfillmentItem(item)" size="small" theme="success" content="赠送" />
|
||||
<product-sku-snapshot-tags
|
||||
<product-sku-tags
|
||||
:product-name="item.productNameSnapshot"
|
||||
:snapshot="item.skuSpecSnapshot"
|
||||
:fallback="item.specSummarySnapshot || item.skuNameSnapshot || '默认 SKU'"
|
||||
@@ -152,7 +152,7 @@ import FulfillmentAPI from "@/api/FulfillmentAPI";
|
||||
import ProductAPI from "@/api/ProductAPI";
|
||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
|
||||
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
|
||||
import ProductSkuTags from "@/components/ProductSkuTags.vue";
|
||||
import ProductStockPreview from "@/components/ProductStockPreview.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { OrderFulfillment, OrderFulfillmentItem } from "@/types/Fulfillment";
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<t-cell
|
||||
class="item"
|
||||
v-for="item in config.itemList || []"
|
||||
:title="getItemTitle(item)"
|
||||
:key="item.id || `${item.skuId}-${item.skuUnitId}`"
|
||||
:note="`${item.defaultQuantity} ${item.unitSnapshot || ''}${item.remark ? `|${item.remark}` : ''}`"
|
||||
>
|
||||
@@ -48,12 +47,14 @@
|
||||
v-text="(item.productNameSnapshot || '产').slice(0, 1)"
|
||||
/>
|
||||
</template>
|
||||
<template #description>
|
||||
<t-tag
|
||||
:content="item.skuCodeSnapshot || item.sku?.code || 'SKU'"
|
||||
theme="primary"
|
||||
variant="light"
|
||||
/>
|
||||
<template #title>
|
||||
<div class="title">
|
||||
<span v-text="item.productNameSnapshot || '未命名产品'" />
|
||||
<product-sku-tags
|
||||
:product-name="item.productNameSnapshot"
|
||||
:fallback="item.specSummarySnapshot || item.skuNameSnapshot || '默认 SKU'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</t-cell>
|
||||
<div v-if="!config.itemList?.length" class="empty" v-text="'暂无产品权益'" />
|
||||
@@ -82,6 +83,7 @@ import { DialogPlugin } from "tdesign-mobile-vue";
|
||||
import { Time } from "timi-web";
|
||||
import { useUserStore } from "timi-web-api";
|
||||
import PackageAPI from "@/api/PackageAPI";
|
||||
import ProductSkuTags from "@/components/ProductSkuTags.vue";
|
||||
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { GaoPackageConfig, PackageConfigItem, PackageConfigStatus } from "@/types/Package";
|
||||
@@ -89,7 +91,7 @@ import { getErrorMessage } from "@/utils/error";
|
||||
import { formatPackageAmount, getPackageConfigStatusText } from "@/utils/package";
|
||||
import { Message } from "@/utils/message";
|
||||
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
||||
import { getProductPhotoURL as getProductPhotoUrl, getProductSkuSnapshotText } from "@/utils/product";
|
||||
import { getProductPhotoURL as getProductPhotoUrl } from "@/utils/product";
|
||||
|
||||
defineOptions({ name: "PackageConfigDetail" });
|
||||
|
||||
@@ -116,11 +118,6 @@ function getItemPhotoURL(item: PackageConfigItem): string {
|
||||
return getProductPhotoUrl(item.sku || {});
|
||||
}
|
||||
|
||||
function getItemTitle(item: PackageConfigItem): string {
|
||||
const skuText = getProductSkuSnapshotText(item.productNameSnapshot, item.skuNameSnapshot, item.specSummarySnapshot);
|
||||
return `${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""}`;
|
||||
}
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
const id = String(route.params.id || "");
|
||||
if (!id) {
|
||||
@@ -286,16 +283,20 @@ onUnmounted(() => navBarStore.clearRight());
|
||||
.item {
|
||||
padding: 10px;
|
||||
|
||||
.item-title {
|
||||
gap: .5rem;
|
||||
:deep(.t-cell__title) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,9 +52,6 @@
|
||||
@update:model-value="applyProduct(index, $event)"
|
||||
@select="applyProductSelection(index, $event)"
|
||||
/>
|
||||
<t-form-item v-if="item.skuId" label="SKU 单位" content-align="right">
|
||||
<span v-text="getUnit(item)?.name || '未选择单位'" />
|
||||
</t-form-item>
|
||||
<t-form-item label="默认配额" content-align="right" required-mark>
|
||||
<t-stepper
|
||||
v-model="item.defaultQuantity"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
v-model="selection"
|
||||
:selected-product="form.product"
|
||||
required-mark
|
||||
@select="handleProductSelection"
|
||||
/>
|
||||
<t-form-item label="默认积分" required-mark>
|
||||
<t-input
|
||||
@@ -34,10 +35,14 @@
|
||||
<t-textarea v-model.trim="form.remark" maxlength="500" autosize />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
<div v-if="form.id" class="actions" v-entrance-transition="70">
|
||||
<t-button class="bg-white" theme="danger" variant="outline" @click="disable">停用配置</t-button>
|
||||
<t-button theme="danger" @click="remove">删除</t-button>
|
||||
</div>
|
||||
<t-button
|
||||
v-if="form.id"
|
||||
class="cell-button"
|
||||
theme="danger"
|
||||
v-entrance-transition="70"
|
||||
content="删除"
|
||||
@click="remove"
|
||||
/>
|
||||
</div>
|
||||
<t-popup v-model="statusVisible" placement="bottom">
|
||||
<t-picker
|
||||
@@ -55,6 +60,7 @@ import PointAPI from "@/api/PointAPI";
|
||||
import ProductSelector from "@/components/ProductSelector.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { PointRedeemSku, PointRedeemSkuStatus } from "@/types/Point";
|
||||
import type { ProductSelection } from "@/types/Product";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { Message } from "@/utils/message";
|
||||
import { normalizePackageQuantity } from "@/utils/package";
|
||||
@@ -71,6 +77,8 @@ const isSaving = ref(false);
|
||||
const form = reactive({
|
||||
id: "",
|
||||
product: undefined as PointRedeemSku["product"],
|
||||
sku: undefined as PointRedeemSku["sku"],
|
||||
unit: undefined as PointRedeemSku["unit"],
|
||||
pointText: "",
|
||||
status: "DRAFT" as PointRedeemSkuStatus,
|
||||
totalText: "",
|
||||
@@ -97,6 +105,12 @@ function parseLimit(value: string): string | null | undefined {
|
||||
return value ? normalizePackageQuantity(value) : null;
|
||||
}
|
||||
|
||||
function handleProductSelection(value: ProductSelection): void {
|
||||
form.product = value.product;
|
||||
form.sku = value.sku;
|
||||
form.unit = value.unit;
|
||||
}
|
||||
|
||||
function confirmStatus(value: Array<string | number>): void {
|
||||
form.status = String(value[0]) as PointRedeemSkuStatus;
|
||||
statusVisible.value = false;
|
||||
@@ -110,6 +124,8 @@ async function load(): Promise<void> {
|
||||
const item = await PointAPI.redeemSkuDetail(id);
|
||||
form.id = item.id || "";
|
||||
form.product = item.product;
|
||||
form.sku = item.sku;
|
||||
form.unit = item.unit;
|
||||
selection.value = item.skuId && item.skuUnitId ? { skuId: item.skuId, skuUnitId: item.skuUnitId } : undefined;
|
||||
form.pointText = String(item.defaultPointCost || "");
|
||||
form.status = item.status || "DRAFT";
|
||||
@@ -155,22 +171,6 @@ async function save(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function disable(): Promise<void> {
|
||||
if (!form.id || isSaving.value) {
|
||||
return;
|
||||
}
|
||||
isSaving.value = true;
|
||||
try {
|
||||
await PointAPI.redeemSkuDisable(form.id);
|
||||
Message.success("兑换配置已停用");
|
||||
await router.replace({ name: "PointRedeemSkuList" });
|
||||
} catch (error) {
|
||||
Message.error(getErrorMessage(error, "兑换配置停用失败"));
|
||||
} finally {
|
||||
isSaving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(): Promise<void> {
|
||||
if (!form.id || isSaving.value) {
|
||||
return;
|
||||
@@ -209,17 +209,5 @@ onUnmounted(() => navBarStore.clearRight());
|
||||
.tips {
|
||||
font-size: .875rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
--td-button-border-radius: 1rem;
|
||||
|
||||
gap: .75rem;
|
||||
margin: 0 1rem;
|
||||
display: flex;
|
||||
|
||||
.t-button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
v-for="(item, index) in result.list"
|
||||
:key="item.id"
|
||||
class="item"
|
||||
:title="item.product?.name || item.sku?.name || '未命名产品'"
|
||||
arrow
|
||||
:description="item.unit?.name || '单位未知'"
|
||||
v-entrance-transition="index * 30"
|
||||
@click="toEdit(item)"
|
||||
>
|
||||
@@ -35,19 +35,23 @@
|
||||
v-text="(item.product?.name || item.sku?.name || '产').slice(0, 1)"
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
<div class="title">
|
||||
<strong v-text="item.product?.name || item.sku?.name || '未命名产品'" />
|
||||
<template #note>
|
||||
<span class="orange" v-text="formatPoint(item.defaultPointCost)" />
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="description">
|
||||
<t-tag
|
||||
size="small"
|
||||
:theme="item.status === 'ACTIVE' ? 'success' : 'default'"
|
||||
:content="statusText(item.status)"
|
||||
/>
|
||||
<product-sku-tags
|
||||
:product-name="item.product?.name"
|
||||
:fallback="item.sku?.specSummary || item.sku?.name || '默认 SKU'"
|
||||
:unit="item.unit?.name"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #note>
|
||||
<span class="orange" v-text="formatPoint(item.defaultPointCost)" />
|
||||
</template>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
<t-empty v-else :description="isLoading ? '正在加载兑换配置' : '暂无兑换配置'" />
|
||||
@@ -63,6 +67,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Toolkit } from "timi-web";
|
||||
import PointAPI from "@/api/PointAPI";
|
||||
import ProductSkuTags from "@/components/ProductSkuTags.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { PointRedeemSku, PointRedeemSkuStatus } from "@/types/Point";
|
||||
import { Message } from "@/utils/message";
|
||||
@@ -202,12 +207,16 @@ onUnmounted(() => {
|
||||
|
||||
.item {
|
||||
padding: 10px;
|
||||
border-radius: 1rem;
|
||||
|
||||
.avatar {
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
border-radius: .875rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
<t-form-item label="兑换商品" required-mark content-align="right" arrow @click="skuVisible = true">
|
||||
<span v-text="skuName || '请选择兑换商品'" />
|
||||
</t-form-item>
|
||||
<product-sku-form-item
|
||||
:sku="selectedSku?.sku"
|
||||
:unit="selectedSku?.unit"
|
||||
/>
|
||||
<t-form-item label="兑换数量" content-align="right" required-mark>
|
||||
<t-stepper
|
||||
v-model="quantity"
|
||||
@@ -97,10 +101,26 @@
|
||||
v-for="item in skuList"
|
||||
:key="item.id"
|
||||
:title="item.product?.name || item.sku?.name || '未命名产品'"
|
||||
:note="`${item.sku?.specSummary || item.sku?.code || '默认 SKU'} · ${item.unit?.name || ''} · ${formatPoint(item.defaultPointCost)} / 单位`"
|
||||
:note="`${formatPoint(item.defaultPointCost)} / ${item.unit?.name || '单位未知'}`"
|
||||
class="item"
|
||||
arrow
|
||||
@click="selectSku(item)"
|
||||
/>
|
||||
>
|
||||
<template #left-icon>
|
||||
<t-image
|
||||
v-if="getProductPhoto(item)"
|
||||
:src="getProductPhoto(item)"
|
||||
:alt="item.product?.name || item.sku?.name || '产品图片'"
|
||||
fit="cover"
|
||||
class="avatar"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="avatar fallback"
|
||||
v-text="(item.product?.name || item.sku?.name || '产').slice(0, 1)"
|
||||
/>
|
||||
</template>
|
||||
</t-cell>
|
||||
<t-empty v-if="!skuList.length" description="暂无启用的兑换商品" />
|
||||
</div>
|
||||
</bottom-popup>
|
||||
@@ -124,7 +144,7 @@ import { getErrorMessage } from "@/utils/error";
|
||||
import { Message } from "@/utils/message";
|
||||
import { formatWanInteger } from "@/utils/money";
|
||||
import { normalizePackageQuantity } from "@/utils/package";
|
||||
import { getProductQuantityStep } from "@/utils/product";
|
||||
import { getProductPhotoURL, getProductQuantityStep } from "@/utils/product";
|
||||
import { uploadPhotoList } from "@/utils/photo";
|
||||
import { permissionCodes } from "@/utils/permission";
|
||||
|
||||
@@ -254,6 +274,11 @@ function selectSku(item: PointRedeemSku): void {
|
||||
skuVisible.value = false;
|
||||
}
|
||||
|
||||
function getProductPhoto(item: PointRedeemSku): string {
|
||||
const product = item.product || item.sku;
|
||||
return product ? getProductPhotoURL(product) : "";
|
||||
}
|
||||
|
||||
function closeSkuPicker(): void {
|
||||
skuVisible.value = false;
|
||||
}
|
||||
@@ -351,6 +376,32 @@ onUnmounted(() => navBarStore.clearRight());
|
||||
max-height: 70vh;
|
||||
overflow: auto;
|
||||
padding: 1rem 0;
|
||||
|
||||
.item {
|
||||
padding: .625rem 1rem;
|
||||
border-radius: 1rem;
|
||||
|
||||
:deep(.t-cell__title) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: .5rem !important;
|
||||
}
|
||||
|
||||
.fallback {
|
||||
color: var(--td-brand-color);
|
||||
display: flex;
|
||||
font-size: 1rem;
|
||||
background: var(--td-brand-color-1);
|
||||
font-weight: 600;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-dark) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<section class="snapshot-head" v-entrance-transition>
|
||||
<div class="eyebrow">库存 SKU 快照</div>
|
||||
<h2 v-text="record.productNameSnapshot || '未命名产品'" />
|
||||
<product-sku-snapshot-tags
|
||||
<product-sku-tags
|
||||
class="sku"
|
||||
:product-name="record.productNameSnapshot"
|
||||
:snapshot="record.skuSpecSnapshot"
|
||||
@@ -138,7 +138,7 @@ import { PassTimeLabel } from "timi-web";
|
||||
import { useUserStore, type Attachment } from "timi-web-api";
|
||||
import ProductAPI from "@/api/ProductAPI";
|
||||
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
|
||||
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
|
||||
import ProductSkuTags from "@/components/ProductSkuTags.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { PhotoItem } from "@/types/Photo";
|
||||
import type { StockBizType, StockRecord } from "@/types/Product";
|
||||
|
||||
@@ -169,7 +169,7 @@ const editorVisible = ref(false);
|
||||
const skuEditorZIndex = 12000;
|
||||
const photoUrlList = computed(() => sku.value.photoItemList.map(item => item.url));
|
||||
const skuSpecEntries = computed(() => getProductSkuSpecEntries(sku.value, props.specList));
|
||||
const skuTitle = computed(() => props.specList.length ? `SKU 明细 ${props.skuIndex + 1}` : "默认 SKU");
|
||||
const skuTitle = computed(() => sku.value.name?.trim() || sku.value.specSummary?.trim() || (props.specList.length ? `SKU 明细 ${props.skuIndex + 1}` : "默认 SKU"));
|
||||
|
||||
function openEditor(): void {
|
||||
editorVisible.value = true;
|
||||
|
||||
@@ -62,9 +62,6 @@
|
||||
required-mark
|
||||
@update:model-value="applyProduct(index, $event)"
|
||||
/>
|
||||
<t-form-item v-if="item.skuId" label="SKU 单位" content-align="right">
|
||||
<span v-text="getUnit(item)?.name || '未选择单位'" />
|
||||
</t-form-item>
|
||||
<t-form-item :label="canPriceOverride ? '成交单价' : '单价'">
|
||||
<t-input
|
||||
v-model.trim="item.unitPriceText"
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
v-text="`¥${formatPackageAmount(item.lineAmount)}`"
|
||||
/>
|
||||
<t-tag v-else size="small" content="套餐权益" />
|
||||
<product-sku-snapshot-tags
|
||||
<product-sku-tags
|
||||
:product-name="item.productNameSnapshot"
|
||||
:snapshot="item.skuSpecSnapshot"
|
||||
:fallback="item.specSummarySnapshot || item.skuNameSnapshot"
|
||||
@@ -226,7 +226,7 @@ import FulfillmentAPI from "@/api/FulfillmentAPI";
|
||||
import SalesOrderAPI from "@/api/SalesOrderAPI";
|
||||
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
||||
import MediaSelector from "@/components/mediaSelector/MediaSelector.vue";
|
||||
import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue";
|
||||
import ProductSkuTags from "@/components/ProductSkuTags.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { GaoSalesOrderItem, GaoSalesPaymentStatus, SalesOrder } from "@/types/SalesOrder";
|
||||
import type { PhotoItem } from "@/types/Photo";
|
||||
|
||||
Reference in New Issue
Block a user