support gift

This commit is contained in:
Timi
2026-09-10 01:33:58 +08:00
parent 8a88df99fc
commit 33b1c0a432
9 changed files with 279 additions and 154 deletions
@@ -26,6 +26,7 @@
<div class="title"> <div class="title">
<div class="heading"> <div class="heading">
<strong v-text="item.productNameSnapshot || '未命名产品'" /> <strong v-text="item.productNameSnapshot || '未命名产品'" />
<t-tag v-if="item.gifted" size="small" theme="success" content="赠送" />
<span v-text="getItemDescription(item)" /> <span v-text="getItemDescription(item)" />
</div> </div>
<div <div
@@ -34,6 +34,7 @@
> >
<template #description> <template #description>
<div class="item-description"> <div class="item-description">
<t-tag v-if="isGiftedFulfillmentItem(item)" size="small" theme="success" content="赠送" />
<product-sku-snapshot-tags <product-sku-snapshot-tags
:product-name="item.productNameSnapshot" :product-name="item.productNameSnapshot"
:snapshot="item.skuSpecSnapshot" :snapshot="item.skuSpecSnapshot"
@@ -200,6 +201,10 @@ function getFulfillmentQuantityText(item: OrderFulfillmentItem): string {
return `履约数量 ${getPackageQuantityText(item.quantity)} ${item.unitSnapshot || "单位"}`; return `履约数量 ${getPackageQuantityText(item.quantity)} ${item.unitSnapshot || "单位"}`;
} }
function isGiftedFulfillmentItem(item: OrderFulfillmentItem): boolean {
return Boolean(order.value?.itemList?.find(orderItem => orderItem.id === item.sourceOrderItemId)?.gifted);
}
async function loadData(): Promise<void> { async function loadData(): Promise<void> {
const id = String(route.params.id || ""); const id = String(route.params.id || "");
if (!id) return; if (!id) return;
+257 -151
View File
@@ -44,20 +44,19 @@
:customer="selectedCustomer" :customer="selectedCustomer"
v-entrance-transition v-entrance-transition
/> />
<t-form-item v-if="isCustomerPackageCreate" label="套餐" required-mark> <t-form-item
<t-input v-if="isCustomerPackageCreate"
:model-value="config?.name || ''" label="套餐"
readonly required-mark
borderless content-align="right"
placeholder="请选择套餐" arrow
@click="configSelectorVisible = true" @click="configSelectorVisible = true"
/> >选择套餐</t-form-item>
</t-form-item>
<div v-if="config && !isSaleable" class="saleable-tip red">套餐当前不可购买,请检查套餐状态和有效期</div> <div v-if="config && !isSaleable" class="saleable-tip red">套餐当前不可购买,请检查套餐状态和有效期</div>
<section class="package-purchase-form"> <t-form-item v-if="config" label-width="0">
<div v-if="config" class="config-summary"> <div class="config-summary">
<div class="header"> <div class="header">
<div class="text-bold" v-text="config.name || '未命名套餐'" /> <div class="name text-bold clip-text" v-text="config.name || '未命名套餐'" />
<div class="price"> <div class="price">
<span class="label gray">标准价:</span> <span class="label gray">标准价:</span>
<span class="value red text-bold" v-text="formatPackageAmount(config.standardAmount)" /> <span class="value red text-bold" v-text="formatPackageAmount(config.standardAmount)" />
@@ -65,80 +64,93 @@
</div> </div>
<div class="description" v-text="config.description" /> <div class="description" v-text="config.description" />
</div> </div>
<div v-else class="center-tips" v-text="'当前套餐未返回,无法完成购买。'" /> </t-form-item>
<t-form-item label="成交价(元)" label-width="6rem" required-mark> <t-form-item label="成交价(元)" label-width="6rem" required-mark>
<t-input <t-input
v-model.trim="saleAmountText" v-model.trim="saleAmountText"
type="number" type="number"
align="right" align="right"
borderless borderless
:readonly="!canPriceOverride" :readonly="!canPriceOverride"
placeholder="请输入成交价" placeholder="请输入成交价"
/> />
</t-form-item> </t-form-item>
<t-form-item label-width="0"> <t-form-item v-if="config" label="购买权益明细" label-align="top">
<t-form class="items" label-width="2.5rem"> <t-form class="items" label-width="2.5rem">
<div class="title"> <span v-if="!canPriceOverride">当前账号无定制权限,仅可等值转换单位</span>
<strong>购买权益明细</strong> <t-form-item
<span v-if="!canPriceOverride">当前账号无定制权限,仅可等值转换单位</span> v-for="(item, index) in packagePurchaseItemList"
</div> :key="`${index}-${item.skuId}-${item.skuUnitId}-${item.gifted}`"
<t-form-item class="item"
v-for="(item, index) in packagePurchaseItemList" >
:key="`${index}-${item.skuId}-${item.skuUnitId}`" <template #label>
class="item" <t-image
> v-if="getItemPhotoURL(item)"
<template #label> :src="getItemPhotoURL(item)"
<t-image :alt="getItemName(item)"
v-if="getItemPhotoURL(item)" fit="cover"
:src="getItemPhotoURL(item)" class="avatar"
:alt="getItemName(item)" />
fit="cover" <div
class="avatar" v-else
/> class="avatar fallback"
<div v-text="getItemName(item).slice(0, 1)"
v-else />
class="avatar fallback" </template>
v-text="getItemName(item).slice(0, 1)" <div class="content">
/> <div class="main">
</template>
<div class="content">
<span class="name" v-text="getItemLabel(item)" /> <span class="name" v-text="getItemLabel(item)" />
<t-tag v-if="item.gifted" size="small" theme="success" content="赠" />
<t-input <t-input
class="input" class="input"
:model-value="item.totalQuantity" :model-value="item.totalQuantity"
type="number" type="number"
:readonly="!canPriceOverride" :readonly="!canEditItem(item)"
placeholder="总权益数量" placeholder="总权益数量"
borderless borderless
align="right" align="right"
@update:model-value="updateItemQuantity(index, $event)" @update:model-value="updateItemQuantity(index, $event)"
> >
<template #suffix-icon>
<div class="actions">
<t-button
size="extra-small"
variant="outline"
:content="getItemUnit(item)?.name || '选择单位'"
@click="openItemUnitPicker(index)"
/>
<t-button
v-if="canPriceOverride"
class="remove"
size="extra-small"
theme="danger"
variant="text"
content="移除"
@click="removePurchaseItem(index)"
/>
</div>
</template>
</t-input> </t-input>
<div class="actions">
<t-button
size="extra-small"
variant="outline"
:content="getItemUnit(item)?.name || '选择单位'"
@click="openItemUnitPicker(index)"
/>
<t-button
v-if="canRemoveItem(item)"
class="remove"
size="extra-small"
theme="danger"
variant="text"
content="移除"
@click="removePurchaseItem(index)"
/>
</div>
</div> </div>
</t-form-item> <t-input
<div v-if="!packagePurchaseItemList.length" class="center-tips" v-text="'当前套餐暂无权益明细'" /> v-if="item.gifted"
</t-form> class="gift-remark"
</t-form-item> :model-value="item.remark || ''"
</section> align="right"
borderless
placeholder="赠送说明(选填)"
@update:model-value="updateItemRemark(index, $event)"
/>
</div>
</t-form-item>
<product-selector
v-if="canGiftCreate"
:model-value="undefined"
label=""
placeholder="添加可售赠送产品"
@update:model-value="addGiftProduct"
/>
<div v-if="!packagePurchaseItemList.length" class="center-tips" v-text="'当前套餐暂无权益明细'" />
</t-form>
</t-form-item>
<t-form-item label="购买时间" content-align="right"> <t-form-item label="购买时间" content-align="right">
<div class="time-actions"> <div class="time-actions">
<t-button <t-button
@@ -254,9 +266,14 @@ import { useSettingStore } from "@/store/settingStore";
import type { Customer } from "@/types/Customer"; import type { Customer } from "@/types/Customer";
import type { PhotoItem } from "@/types/Photo"; import type { PhotoItem } from "@/types/Photo";
import type { GaoPackageConfig, PackageAttachment, PackagePurchaseItem } from "@/types/Package"; import type { GaoPackageConfig, PackageAttachment, PackagePurchaseItem } from "@/types/Package";
import type { ProductSku, ProductSkuUnit } from "@/types/Product"; import type { Product, ProductSku, ProductSkuUnit } from "@/types/Product";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { addPackageQuantity, convertPackageQuantity, formatPackageAmount, normalizePackageQuantity } from "@/utils/package"; import {
addPackageQuantity,
convertPackageQuantity,
formatPackageAmount,
normalizePackageQuantity
} from "@/utils/package";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
import { permissionCodes } from "@/utils/permission"; import { permissionCodes } from "@/utils/permission";
import { uploadPhotoList } from "@/utils/photo"; import { uploadPhotoList } from "@/utils/photo";
@@ -280,6 +297,7 @@ const config = ref<GaoPackageConfig>();
const configList = ref<GaoPackageConfig[]>([]); const configList = ref<GaoPackageConfig[]>([]);
const configSelectorVisible = ref(false); const configSelectorVisible = ref(false);
const skuMap = reactive<Record<string, ProductSku>>({}); const skuMap = reactive<Record<string, ProductSku>>({});
const giftProductMap = reactive<Record<string, Product>>({});
const unitPickerVisible = ref(false); const unitPickerVisible = ref(false);
const unitPickerIndex = ref(-1); const unitPickerIndex = ref(-1);
const unitPickerDraft = ref<Array<string | number>>([]); const unitPickerDraft = ref<Array<string | number>>([]);
@@ -417,6 +435,7 @@ function buildAttachments(): PackageAttachment[] {
const { const {
isSaleable, isSaleable,
canPriceOverride, canPriceOverride,
canGiftCreate,
saleAmountText, saleAmountText,
packagePurchaseItemList, packagePurchaseItemList,
applyConfig, applyConfig,
@@ -439,16 +458,19 @@ const unitPickerOptions = computed(() => {
function getItemLabel(item: PackagePurchaseItem): string { function getItemLabel(item: PackagePurchaseItem): string {
const configItem = config.value?.itemList?.find(configItem => configItem.skuId === item.skuId); const configItem = config.value?.itemList?.find(configItem => configItem.skuId === item.skuId);
if (!configItem) { const product = item.gifted ? giftProductMap[item.skuId] : undefined;
return "产品权益"; const productName = item.gifted
} ? product?.name || item.productNameSnapshot || "未命名产品"
const productName = configItem.productNameSnapshot || "未命名产品"; : configItem?.productNameSnapshot || "未命名产品";
const skuText = getProductSkuSnapshotText(productName, configItem.skuNameSnapshot, configItem.specSummarySnapshot); const skuName = item.gifted ? item.skuNameSnapshot || getItemSku(item)?.name : configItem?.skuNameSnapshot;
const specSummary = item.gifted ? item.specSummarySnapshot || getItemSku(item)?.specSummary : configItem?.specSummarySnapshot;
const skuText = getProductSkuSnapshotText(productName, skuName, specSummary);
return skuText ? `${productName} · ${skuText}` : productName; return skuText ? `${productName} · ${skuText}` : productName;
} }
function getItemName(item: PackagePurchaseItem): string { function getItemName(item: PackagePurchaseItem): string {
return config.value?.itemList?.find(configItem => configItem.skuId === item.skuId)?.productNameSnapshot || "未命名产品"; return item.gifted ? giftProductMap[item.skuId]?.name || item.productNameSnapshot || "未命名产品"
: config.value?.itemList?.find(configItem => configItem.skuId === item.skuId)?.productNameSnapshot || "未命名产品";
} }
function getItemPhotoURL(item: PackagePurchaseItem): string { function getItemPhotoURL(item: PackagePurchaseItem): string {
@@ -464,6 +486,9 @@ function getItemUnit(item: PackagePurchaseItem): ProductSkuUnit | undefined {
} }
function getSelectableUnitList(item: PackagePurchaseItem): ProductSkuUnit[] { function getSelectableUnitList(item: PackagePurchaseItem): ProductSkuUnit[] {
if (item.gifted) {
return (getItemSku(item)?.unitList || []).filter(unit => unit.allowSale !== false && !!unit.id);
}
const maxBaseQuantityPerUnit = Math.max(...(config.value?.itemList || []) const maxBaseQuantityPerUnit = Math.max(...(config.value?.itemList || [])
.filter(configItem => configItem.skuId === item.skuId) .filter(configItem => configItem.skuId === item.skuId)
.map(configItem => Number(configItem.baseQuantityPerUnitSnapshot || 0))); .map(configItem => Number(configItem.baseQuantityPerUnitSnapshot || 0)));
@@ -473,7 +498,7 @@ function getSelectableUnitList(item: PackagePurchaseItem): ProductSkuUnit[] {
function openItemUnitPicker(index: number): void { function openItemUnitPicker(index: number): void {
const item = packagePurchaseItemList.value[index]; const item = packagePurchaseItemList.value[index];
if (!item || !getSelectableUnitList(item).length) { if (!item || !canEditItem(item) || !getSelectableUnitList(item).length) {
Message.warning("当前 SKU 没有可用于套餐权益的销售单位"); Message.warning("当前 SKU 没有可用于套餐权益的销售单位");
return; return;
} }
@@ -488,7 +513,7 @@ function confirmItemUnitPicker(value: Array<string | number>): void {
const skuUnitId = String(value[0] || ""); const skuUnitId = String(value[0] || "");
const currentUnit = item && getItemUnit(item); const currentUnit = item && getItemUnit(item);
const targetUnit = item && getSelectableUnitList(item).find(unit => unit.id === skuUnitId); const targetUnit = item && getSelectableUnitList(item).find(unit => unit.id === skuUnitId);
if (!item || !currentUnit || !targetUnit || !skuUnitId || skuUnitId === item.skuUnitId) { if (!item || !canEditItem(item) || !currentUnit || !targetUnit || !skuUnitId || skuUnitId === item.skuUnitId) {
unitPickerVisible.value = false; unitPickerVisible.value = false;
return; return;
} }
@@ -498,7 +523,7 @@ function confirmItemUnitPicker(value: Array<string | number>): void {
return; return;
} }
const duplicateIndex = packagePurchaseItemList.value.findIndex((candidate, candidateIndex) => candidateIndex !== index const duplicateIndex = packagePurchaseItemList.value.findIndex((candidate, candidateIndex) => candidateIndex !== index
&& candidate.skuId === item.skuId && candidate.skuUnitId === skuUnitId); && candidate.skuId === item.skuId && candidate.skuUnitId === skuUnitId && Boolean(candidate.gifted) === Boolean(item.gifted));
if (duplicateIndex < 0) { if (duplicateIndex < 0) {
packagePurchaseItemList.value[index] = { ...item, skuUnitId, totalQuantity: convertedQuantity }; packagePurchaseItemList.value[index] = { ...item, skuUnitId, totalQuantity: convertedQuantity };
} else { } else {
@@ -522,7 +547,63 @@ function confirmItemUnitPicker(value: Array<string | number>): void {
unitPickerVisible.value = false; unitPickerVisible.value = false;
} }
async function addGiftProduct(value?: { skuId: string; skuUnitId: string }): Promise<void> {
if (!value || !canGiftCreate.value) {
return;
}
try {
const sku = skuMap[value.skuId] || await ProductAPI.skuDetail(value.skuId);
skuMap[value.skuId] = sku;
const productId = sku.productId || "";
const product = productId ? giftProductMap[value.skuId] || await ProductAPI.detail(productId) : undefined;
if (!product) {
Message.warning("赠送产品信息加载失败,请重新选择");
return;
}
giftProductMap[value.skuId] = product;
const existingIndex = packagePurchaseItemList.value.findIndex(item => item.gifted
&& item.skuId === value.skuId && item.skuUnitId === value.skuUnitId);
if (0 <= existingIndex) {
const existingItem = packagePurchaseItemList.value[existingIndex];
const totalQuantity = addPackageQuantity(existingItem.totalQuantity, "1");
if (!totalQuantity) {
Message.warning("赠送数量合并后不能精确保留三位小数");
return;
}
packagePurchaseItemList.value[existingIndex] = { ...existingItem, totalQuantity };
} else {
const maxSort = Math.max(-1, ...packagePurchaseItemList.value.map(item => item.sort ?? -1));
packagePurchaseItemList.value.push({
skuId: value.skuId,
skuUnitId: value.skuUnitId,
totalQuantity: "1",
gifted: true,
sort: maxSort + 1,
remark: null,
productNameSnapshot: product.name,
skuNameSnapshot: sku.name,
specSummarySnapshot: sku.specSummary
});
}
resetRequestNo();
} catch (error) {
Message.error(getErrorMessage(error, "赠送产品加载失败"));
}
}
function canEditItem(item: PackagePurchaseItem): boolean {
return Boolean(item.gifted) ? canGiftCreate.value : canPriceOverride.value;
}
function canRemoveItem(item: PackagePurchaseItem): boolean {
return canEditItem(item);
}
function updateItemQuantity(index: number, value: string | number): void { function updateItemQuantity(index: number, value: string | number): void {
const item = packagePurchaseItemList.value[index];
if (!item || !canEditItem(item)) {
return;
}
packagePurchaseItemList.value = packagePurchaseItemList.value.map((item, itemIndex) => itemIndex === index ? { packagePurchaseItemList.value = packagePurchaseItemList.value.map((item, itemIndex) => itemIndex === index ? {
...item, ...item,
totalQuantity: String(value) totalQuantity: String(value)
@@ -530,8 +611,18 @@ function updateItemQuantity(index: number, value: string | number): void {
resetRequestNo(); resetRequestNo();
} }
function updateItemRemark(index: number, value: string | number): void {
const item = packagePurchaseItemList.value[index];
if (!item?.gifted || !canGiftCreate.value) {
return;
}
packagePurchaseItemList.value[index] = { ...item, remark: String(value) || null };
resetRequestNo();
}
function removePurchaseItem(index: number): void { function removePurchaseItem(index: number): void {
if (!canPriceOverride.value) { const item = packagePurchaseItemList.value[index];
if (!item || !canRemoveItem(item)) {
return; return;
} }
packagePurchaseItemList.value = packagePurchaseItemList.value.filter((_, itemIndex) => itemIndex !== index); packagePurchaseItemList.value = packagePurchaseItemList.value.filter((_, itemIndex) => itemIndex !== index);
@@ -662,84 +753,86 @@ watch(config, value => {
margin: 1rem; margin: 1rem;
} }
.saleable-tip { .config-summary {
padding: 0 1rem .5rem; gap: .25rem;
font-size: .8125rem; width: 100%;
line-height: 1.5; padding: .5rem .75rem;
}
.time-actions {
gap: .5rem;
display: flex; display: flex;
align-items: center; background: var(--td-error-color-1);
justify-content: flex-end; border-radius: .75rem;
} flex-direction: column;
.package-purchase-form { .header {
gap: .75rem;
display: grid;
margin-top: 1rem;
.config-summary {
gap: .25rem;
margin: 0 1rem;
padding: .5rem .75rem;
display: flex; display: flex;
background: var(--td-error-color-1); justify-content: space-between;
border-radius: .75rem;
flex-direction: column;
.header { .name {
flex: 1;
}
.price {
flex: none;
display: flex; display: flex;
justify-content: space-between; align-items: baseline;
.price { .label {
display: flex; font-size: .75rem;
align-items: baseline; }
.label { .value {
font-size: .75rem; font-size: 1.75rem;
} line-height: 1;
.value {
font-size: 1.75rem;
line-height: 1;
}
} }
} }
} }
.items { .description {
gap: .5rem; width: 100%;
margin: 0; font-size: .75rem;
display: grid; min-width: 0;
max-width: 100%;
word-break: break-word;
white-space: normal;
overflow-wrap: anywhere;
}
}
.title { .items {
font-size: .8125rem; gap: .5rem;
margin-bottom: .5rem; flex: 1;
margin: 0;
display: grid;
.title {
font-size: .8125rem;
margin-bottom: .5rem;
}
.item {
padding: 10px;
box-shadow: .25rem .25rem 1rem rgba(0, 0, 0, .08);
border-radius: 1rem;
.avatar {
width: 2rem;
height: 2rem;
flex: none;
border-radius: .5rem;
} }
.item { &::after {
padding: 10px; display: none;
box-shadow: .25rem .25rem 1rem rgba(0, 0, 0, .08); }
border-radius: 1rem;
.avatar { .content {
width: 2rem; gap: .25rem;
height: 2rem; display: flex;
flex: none; flex-direction: column;
border-radius: .5rem;
}
&::after { .main {
display: none; gap: .25rem;
}
.content {
display: flex; display: flex;
height: 2rem;
align-items: center; align-items: center;
justify-content: space-between;
.name { .name {
min-width: 0; min-width: 0;
@@ -749,13 +842,19 @@ watch(config, value => {
} }
.input { .input {
min-width: 0;
flex: 1; flex: 1;
min-width: 0;
margin-top: .2rem;
}
.actions { .actions {
gap: .25rem; gap: .25rem;
display: flex; margin: .1rem 0 0 .75rem;
} display: flex;
}
.gift-remark {
padding: 0;
} }
} }
} }
@@ -763,6 +862,13 @@ watch(config, value => {
} }
} }
.time-actions {
gap: .5rem;
display: flex;
align-items: center;
justify-content: flex-end;
}
.file-input { .file-input {
display: none; display: none;
} }
+5 -1
View File
@@ -18,6 +18,7 @@ interface UsePackagePurchaseOptions {
/** 套餐配置直接购买流程,购买请求只依赖套餐配置 ID,不再关联购买事件。 */ /** 套餐配置直接购买流程,购买请求只依赖套餐配置 ID,不再关联购买事件。 */
export function usePackagePurchase(options: UsePackagePurchaseOptions) { export function usePackagePurchase(options: UsePackagePurchaseOptions) {
const canPriceOverride = computed(() => options.hasPermission(permissionCodes.customerPackage.priceOverride)); const canPriceOverride = computed(() => options.hasPermission(permissionCodes.customerPackage.priceOverride));
const canGiftCreate = computed(() => options.hasPermission(permissionCodes.customerPackage.giftCreate));
const saleAmountText = ref(""); const saleAmountText = ref("");
const packagePurchaseItemList = ref<PackagePurchaseItem[]>([]); const packagePurchaseItemList = ref<PackagePurchaseItem[]>([]);
const packageRequestNo = ref(""); const packageRequestNo = ref("");
@@ -45,6 +46,7 @@ export function usePackagePurchase(options: UsePackagePurchaseOptions) {
skuId: item.skuId, skuId: item.skuId,
skuUnitId: item.skuUnitId, skuUnitId: item.skuUnitId,
totalQuantity: normalizePackageQuantity(item.defaultQuantity) || "", totalQuantity: normalizePackageQuantity(item.defaultQuantity) || "",
gifted: false,
sort: item.sort ?? index, sort: item.sort ?? index,
remark: item.remark ?? null remark: item.remark ?? null
})); }));
@@ -77,6 +79,7 @@ export function usePackagePurchase(options: UsePackagePurchaseOptions) {
skuId: item.skuId, skuId: item.skuId,
skuUnitId: item.skuUnitId, skuUnitId: item.skuUnitId,
totalQuantity, totalQuantity,
gifted: Boolean(item.gifted),
sort: item.sort ?? index, sort: item.sort ?? index,
remark: item.remark ?? null remark: item.remark ?? null
}] : []; }] : [];
@@ -124,7 +127,7 @@ export function usePackagePurchase(options: UsePackagePurchaseOptions) {
} }
if (quantity !== "0") { if (quantity !== "0") {
hasPositiveQuantity = true; hasPositiveQuantity = true;
const key = `${item.skuId}\u0000${item.skuUnitId}`; const key = `${item.skuId}\u0000${item.skuUnitId}\u0000${Boolean(item.gifted)}`;
if (keySet.has(key)) { if (keySet.has(key)) {
Message.warning("最终套餐权益不能重复相同 SKU 和单位"); Message.warning("最终套餐权益不能重复相同 SKU 和单位");
return false; return false;
@@ -142,6 +145,7 @@ export function usePackagePurchase(options: UsePackagePurchaseOptions) {
return { return {
isSaleable, isSaleable,
canPriceOverride, canPriceOverride,
canGiftCreate,
saleAmountText, saleAmountText,
packagePurchaseItemList, packagePurchaseItemList,
applyConfig, applyConfig,
@@ -97,6 +97,7 @@
<div class="main"> <div class="main">
<div class="title"> <div class="title">
<span class="name text-bold" v-text="item.productNameSnapshot || '未命名产品'" /> <span class="name text-bold" v-text="item.productNameSnapshot || '未命名产品'" />
<t-tag v-if="item.gifted" size="small" theme="success" content="赠送" />
<t-tag <t-tag
:content="getItemFulfillmentStatus(item).text" :content="getItemFulfillmentStatus(item).text"
:theme="getItemFulfillmentStatus(item).theme" :theme="getItemFulfillmentStatus(item).theme"
@@ -231,7 +231,8 @@ function sumProductQuantity(field: "quantity" | "fulfilledBaseQuantity"): string
function getProductText(item: NonNullable<SalesOrder["itemList"]>[number]): string { function getProductText(item: NonNullable<SalesOrder["itemList"]>[number]): string {
const quantity = getPackageQuantityText(item.quantity); const quantity = getPackageQuantityText(item.quantity);
const skuText = getProductSkuSnapshotText(item.productNameSnapshot, item.skuNameSnapshot, item.specSummarySnapshot); const skuText = getProductSkuSnapshotText(item.productNameSnapshot, item.skuNameSnapshot, item.specSummarySnapshot);
return `${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""} × ${quantity}${item.unitSnapshot ? ` ${item.unitSnapshot}` : ""}`; const giftText = item.gifted ? "赠送 · " : "";
return `${giftText}${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""} × ${quantity}${item.unitSnapshot ? ` ${item.unitSnapshot}` : ""}`;
} }
</script> </script>
+4
View File
@@ -61,8 +61,12 @@ export type PackagePurchaseItem = {
skuId: string; skuId: string;
skuUnitId: string; skuUnitId: string;
totalQuantity: number | string; totalQuantity: number | string;
gifted?: boolean;
sort?: number; sort?: number;
remark?: string | null; remark?: string | null;
productNameSnapshot?: string;
skuNameSnapshot?: string;
specSummarySnapshot?: string | null;
}; };
export type PackageAttachment = Partial<Attachment> & { tempFileId?: string; attachType?: string; name?: string }; export type PackageAttachment = Partial<Attachment> & { tempFileId?: string; attachType?: string; name?: string };
+1
View File
@@ -15,6 +15,7 @@ export type FundRecordStatus = "PENDING" | "VERIFIED" | "REJECTED" | "CANCELLED"
export type GaoSalesOrderItem = { export type GaoSalesOrderItem = {
salesOrderId?: string; salesOrderId?: string;
itemType?: GaoSalesOrderItemType; itemType?: GaoSalesOrderItemType;
gifted?: boolean;
skuId?: string; skuId?: string;
skuUnitId?: string; skuUnitId?: string;
attachmentList?: Attachment[]; attachmentList?: Attachment[];
+3 -1
View File
@@ -70,7 +70,8 @@ export const permissionCodes = {
create: "GAO:CUSTOMER_PACKAGE:CREATE", create: "GAO:CUSTOMER_PACKAGE:CREATE",
read: "GAO:CUSTOMER_PACKAGE:READ", read: "GAO:CUSTOMER_PACKAGE:READ",
cancel: "GAO:CUSTOMER_PACKAGE:CANCEL", cancel: "GAO:CUSTOMER_PACKAGE:CANCEL",
priceOverride: "GAO:CUSTOMER_PACKAGE:PRICE_OVERRIDE" priceOverride: "GAO:CUSTOMER_PACKAGE:PRICE_OVERRIDE",
giftCreate: "GAO:CUSTOMER_PACKAGE:GIFT_CREATE"
}, },
salesOrder: { salesOrder: {
create: "GAO:SALES_ORDER:CREATE", create: "GAO:SALES_ORDER:CREATE",
@@ -207,6 +208,7 @@ export const gaoStoreScopePermissionCodes = [
permissionCodes.customerPackage.read, permissionCodes.customerPackage.read,
permissionCodes.customerPackage.cancel, permissionCodes.customerPackage.cancel,
permissionCodes.customerPackage.priceOverride, permissionCodes.customerPackage.priceOverride,
permissionCodes.customerPackage.giftCreate,
permissionCodes.salesOrder.create, permissionCodes.salesOrder.create,
permissionCodes.salesOrder.read, permissionCodes.salesOrder.read,
permissionCodes.salesOrder.update, permissionCodes.salesOrder.update,