add product stock preview

This commit is contained in:
Timi
2026-09-16 14:53:36 +08:00
parent 443d0a6190
commit f8a9c0e649
12 changed files with 456 additions and 86 deletions
+27 -19
View File
@@ -50,6 +50,7 @@
placeholder="请选择产品"
required-mark
@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 || '未选择单位'" />
@@ -111,7 +112,7 @@ import { useCurrentStoreStore } from "@/store/currentStoreStore";
import { useNavBarStore } from "@/store/navBarStore";
import type { GaoPackageConfig, PackageConfigItem, PackageConfigStatus } from "@/types/Package";
import ProductAPI from "@/api/ProductAPI";
import type { Product, ProductSku, ProductSkuUnit } from "@/types/Product";
import type { Product, ProductSelection, ProductSku, ProductSkuUnit } from "@/types/Product";
import { getErrorMessage } from "@/utils/error";
import { formatPackageAmountInput, getPackageConfigStatusText, normalizePackageQuantity, parsePackageAmount } from "@/utils/package";
import { Message } from "@/utils/message";
@@ -204,23 +205,27 @@ const estimatedOriginalAmount = computed<number | undefined>(() => {
});
const estimatedOriginalAmountText = computed(() => estimatedOriginalAmount.value === undefined ? "待完善" : `¥${formatProductAmount(estimatedOriginalAmount.value)}`);
async function applyProduct(index: number, value?: { skuId: string; skuUnitId: string }): Promise<void> {
function applyProduct(index: number, value?: { skuId: string; skuUnitId: string }): void {
if (!value) {
return;
}
const sku = skuCache.get(value.skuId) || await ProductAPI.skuDetail(value.skuId);
skuCache.set(value.skuId, sku);
const product = productCache.get(sku.productId || "") || await ProductAPI.detail(sku.productId || "");
productCache.set(sku.productId || "", product);
const unit = (sku.unitList || []).find(item => item.id === value.skuUnitId);
Object.assign(form.itemList[index], {
skuId: value.skuId,
skuUnitId: value.skuUnitId,
productNameSnapshot: product.name,
skuNameSnapshot: sku.name,
specSummarySnapshot: sku.specSummary,
unitSnapshot: unit?.name || "",
baseQuantityPerUnitSnapshot: unit?.baseQuantityPerUnit
skuUnitId: value.skuUnitId
});
}
function applyProductSelection(index: number, selection: ProductSelection): void {
skuCache.set(selection.sku.id || "", selection.sku);
productCache.set(selection.product.id || "", selection.product);
Object.assign(form.itemList[index], {
productNameSnapshot: selection.product.name,
productIdSnapshot: selection.product.id,
skuCodeSnapshot: selection.sku.code,
skuNameSnapshot: selection.sku.name,
specSummarySnapshot: selection.sku.specSummary,
unitSnapshot: selection.unit.name,
baseQuantityPerUnitSnapshot: selection.unit.baseQuantityPerUnit
});
}
@@ -396,13 +401,16 @@ async function loadData(): Promise<void> {
}
const result = await PackageAPI.configDetail(routeId.value);
const itemList = (result.itemList || []).map(createItem);
const skuIdList = [...new Set(itemList.map(item => item.skuId).filter((id): id is string => !!id))];
await Promise.all(skuIdList.map(async skuId => {
const sku = itemList.find(item => item.skuId === skuId)?.sku || await ProductAPI.skuDetail(skuId);
skuCache.set(skuId, sku);
if (sku.productId) {
productCache.set(sku.productId, await ProductAPI.detail(sku.productId));
for (const item of itemList) {
if (item.skuId && item.sku) {
skuCache.set(item.skuId, item.sku);
}
}
const productIdList = [...new Set(itemList
.map(item => item.sku?.productId || item.productIdSnapshot)
.filter((id): id is string => !!id))];
await Promise.all(productIdList.map(async productId => {
productCache.set(productId, await ProductAPI.detail(productId));
}));
Object.assign(form, {
id: result.id || routeId.value,
+108 -36
View File
@@ -199,11 +199,14 @@
<t-switch
v-model="fulfillAll"
size="small"
:disabled="!canFulfillImmediately"
:disabled="!canFulfillImmediately || isStockChecking"
/>
</t-form-item>
<t-form-item label-width="0">
<div class="red">全部履约会同步启用全额收款;请先准备全部产品库存,任意一步失败,整张订单都会回滚。</div>
<div class="stock-tips">
<div class="tips red">全部履约会同步启用全额收款;请先准备充足的库存,任意一步失败都会下单失败。</div>
<product-stock-preview v-if="stockPreviewList.length" :items="stockPreviewList" />
</div>
</t-form-item>
<t-form-item v-if="(paymentVerified && parsedPaymentAmount) || (receiveFullPayment && parsedSaleAmount)" label-width="0">
<photo-scroller
@@ -337,6 +340,7 @@ import CustomerList from "@/components/CustomerList.vue";
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
import CustomerSelector from "@/components/CustomerSelector.vue";
import PhotoScroller from "@/components/PhotoScroller.vue";
import ProductStockPreview from "@/components/ProductStockPreview.vue";
import { usePackagePurchase } from "@/pages/package/usePackagePurchase";
import { useNavBarStore } from "@/store/navBarStore";
import { useSettingStore } from "@/store/settingStore";
@@ -351,10 +355,13 @@ import {
formatPackageAmount,
getPackageQuantityText,
normalizePackageQuantity,
parsePackageAmount
parsePackageAmount,
toPackageBaseQuantity
} from "@/utils/package";
import { Message } from "@/utils/message";
import { permissionCodes } from "@/utils/permission";
import { buildProductStockPreview, getProductStockShortageMessage, mergeProductStockRequirements } from "@/utils/productStock";
import type { ProductStockPreviewItem } from "@/utils/productStock";
import { uploadPhotoList } from "@/utils/photo";
import { getProductPhotoURL, getProductSkuSnapshotText } from "@/utils/product";
@@ -510,6 +517,9 @@ const paymentPhotoUrlList = computed(() => paymentPhotoItems.value.map(item => i
const fulfillmentPhotoUrlList = computed(() => fulfillmentPhotoItems.value.map(item => item.url));
const receiveFullPayment = ref(false);
const fulfillAll = ref(false);
const isStockChecking = ref(false);
const stockPreviewList = ref<ProductStockPreviewItem[]>([]);
let stockCheckToken = 0;
const parsedPaymentAmount = computed(() => parsePackageAmount(paymentAmountText.value));
const paymentVerified = computed(() => !receiveFullPayment.value
&& 0 < paymentAmountText.value.trim().length
@@ -553,11 +563,29 @@ const canReceiveImmediately = computed(() => userStore.hasPermissions(permission
&& userStore.hasPermissions(permissionCodes.fundReceive.verify));
const canFulfillImmediately = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.create)
&& userStore.hasPermissions(permissionCodes.orderFulfillment.verify)
&& userStore.hasPermissions(permissionCodes.productStock.read)
&& (parsedSaleAmount.value === 0 || canReceiveImmediately.value));
watch(fulfillAll, value => {
if (value && parsedSaleAmount.value !== 0) {
receiveFullPayment.value = true;
watch(fulfillAll, async value => {
stockCheckToken += 1;
const token = stockCheckToken;
if (!value) {
isStockChecking.value = false;
return;
}
isStockChecking.value = true;
try {
if (!await validateDirectStock() && token === stockCheckToken && fulfillAll.value) {
fulfillAll.value = false;
return;
}
if (token === stockCheckToken && fulfillAll.value && parsedSaleAmount.value !== 0) {
receiveFullPayment.value = true;
}
} finally {
if (token === stockCheckToken) {
isStockChecking.value = false;
}
}
});
@@ -603,6 +631,14 @@ function getItemSku(item: PackagePurchaseItem): ProductSku | undefined {
return skuMap[item.skuId] || config.value?.itemList?.find(configItem => configItem.skuId === item.skuId)?.sku;
}
function syncConfigSkuMap(value?: GaoPackageConfig): void {
for (const item of value?.itemList || []) {
if (item.skuId && item.sku) {
skuMap[item.skuId] = item.sku;
}
}
}
function getItemUnit(item: PackagePurchaseItem): ProductSkuUnit | undefined {
return getItemSku(item)?.unitList?.find(unit => unit.id === item.skuUnitId);
}
@@ -762,10 +798,70 @@ function validateItemDecimalQuantity(): boolean {
return true;
}
async function validateDirectStock(): Promise<boolean> {
stockPreviewList.value = [];
const selected = packagePurchaseItemList.value.filter(item => {
const quantity = normalizePackageQuantity(item.totalQuantity);
return !!quantity && quantity !== "0";
});
const skuIdList = [...new Set(selected.map(item => item.skuId).filter(Boolean))];
await Promise.all(skuIdList.map(async skuId => {
if (skuMap[skuId]) {
return;
}
try {
skuMap[skuId] = await ProductAPI.skuDetail(skuId);
} catch {
// 统一在下方提示,避免部分 SKU 查询失败时静默跳过库存校验
}
}));
for (const item of selected) {
if (!skuMap[item.skuId]) {
Message.warning(`${getItemName(item)} 的库存信息加载失败,请刷新后重试`);
return false;
}
if (!getItemUnit(item)) {
Message.warning(`${getItemName(item)} 的权益单位不存在,请刷新后重试`);
return false;
}
}
const requirementList = mergeProductStockRequirements(selected.flatMap(item => {
const sku = skuMap[item.skuId];
const unit = getItemUnit(item);
const quantity = normalizePackageQuantity(item.totalQuantity);
const baseQuantity = quantity && unit ? toPackageBaseQuantity(quantity, unit.baseQuantityPerUnit) : undefined;
if (!sku?.stockEnabled || !baseQuantity) {
return [];
}
return [{ skuId: item.skuId, baseQuantity, productName: getItemName(item), unitName: "基础单位", stockEnabled: sku.stockEnabled }];
}));
if (!requirementList.length) {
return true;
}
try {
const stockList = await ProductAPI.stockBatchDetail({ skuIdList: requirementList.map(item => item.skuId) });
stockPreviewList.value = buildProductStockPreview(stockList, requirementList);
const shortageMessage = getProductStockShortageMessage(stockList, requirementList);
if (shortageMessage) {
Message.warning(`${shortageMessage},不能直接履约`);
return false;
}
return true;
} catch (error) {
stockPreviewList.value = [];
Message.error(getErrorMessage(error, "库存查询失败,暂不能直接履约"));
return false;
}
}
async function submitRecord(): Promise<void> {
if (isSaving.value) {
return;
}
if (isStockChecking.value) {
Message.warning("库存正在预检,请稍候再提交");
return;
}
if (!validatePurchaseForm() || !validateItemDecimalQuantity()) {
return;
}
@@ -796,6 +892,7 @@ async function purchasePackage(): Promise<void> {
isSaving.value = true;
try {
if (fulfillAll.value && !await validateDirectStock()) return;
const result = await submitPurchase();
if (!result?.salesOrderId) {
Message.error("套餐购买成功但未返回完整订单关联,请联系管理员");
@@ -823,8 +920,8 @@ async function loadPageData(): Promise<void> {
return;
}
config.value = await PackageAPI.configDetail(configId.value);
syncConfigSkuMap(config.value);
applyConfig(config.value);
await loadConfigSkuList(config.value);
}
async function selectConfig(item: GaoPackageConfig): Promise<void> {
@@ -832,21 +929,11 @@ async function selectConfig(item: GaoPackageConfig): Promise<void> {
return;
}
config.value = await PackageAPI.configDetail(item.id);
await loadConfigSkuList(config.value);
syncConfigSkuMap(config.value);
applyConfig(config.value);
configSelectorVisible.value = false;
}
async function loadConfigSkuList(value?: GaoPackageConfig): Promise<void> {
const skuIdList = [...new Set((value?.itemList || []).map(item => item.skuId).filter(Boolean))];
await Promise.all(skuIdList.map(async skuId => {
try {
skuMap[skuId] = await ProductAPI.skuDetail(skuId);
} catch {
// 后端会校验最终权益单位,单个 SKU 的单位加载失败不影响套餐查看
}
}));
}
onMounted(async () => {
navBarStore.clearRight();
if (userStore.hasPermissions(permissionCodes.customerPackage.create)) {
@@ -895,23 +982,8 @@ watch(config, value => {
width: 100%;
}
.straight-through {
width: 100%;
padding: .75rem;
background: var(--td-bg-color-secondarycontainer);
border-radius: .875rem;
.title {
font-size: .9375rem;
font-weight: 600;
}
.tips {
color: var(--td-text-color-secondary);
margin: .5rem .5rem 0;
font-size: .75rem;
line-height: 1.5;
}
.tips {
font-size: .875rem;
}
.customer-profile {
+57 -12
View File
@@ -203,7 +203,7 @@
<script setup lang="ts">
import { BottomPopup } from "timi-tdesign-mobile";
import type { ColorObject } from "tdesign-mobile-vue";
import { DialogPlugin, type ColorObject } from "tdesign-mobile-vue";
import { type Attachment, AttachmentAPI, useUserStore } from "timi-web-api";
import ProductAPI from "@/api/ProductAPI";
import PhotoScroller from "@/components/PhotoScroller.vue";
@@ -212,7 +212,7 @@ import ProductSkuUnitEditor from "@/pages/product/component/ProductSkuUnitEditor
import ProductSkuUnitBatchEditor from "@/pages/product/component/ProductSkuUnitBatchEditor.vue";
import { useNavBarStore } from "@/store/navBarStore";
import type { PhotoItem } from "@/types/Photo";
import type { Product, ProductAttachmentType, ProductCategory, ProductSku, ProductSkuUnit, ProductSpec } from "@/types/Product";
import type { Product, ProductAttachmentType, ProductCategory, ProductSku, ProductSkuUnit, ProductSpec, ProductType } from "@/types/Product";
import type { ProductAttachmentItem, ProductForm, ProductSkuForm as SkuForm, ProductSkuUnitForm as UnitForm, ProductSpecForm as SpecForm, ProductSpecValueForm as SpecValueForm } from "@/types/ProductEditor";
import { compressUploadImage } from "@/utils/imageCompression";
import { getAttachmentReadURL } from "@/utils/attachment";
@@ -464,19 +464,64 @@ function openTypePicker(): void {
typePickerVisible.value = true;
}
function confirmType(value: Array<string | number>): void {
async function confirmType(value: Array<string | number>): Promise<void> {
const type = String(value[0] || "");
if (type === "SERVICE" || type === "GOODS" || type === "CONSUMABLE") {
form.productType = type;
if (type === "SERVICE") {
form.stockEnabled = false;
form.skuList.forEach(sku => {
sku.stockEnabled = false;
sku.unitList.forEach(unit => { unit.allowPurchase = false; });
});
}
if (type !== "SERVICE" && type !== "GOODS" && type !== "CONSUMABLE") {
typePickerVisible.value = false;
return;
}
typePickerVisible.value = false;
if (type !== "SERVICE" || form.productType === "SERVICE") {
applyProductType(type);
return;
}
if (!await canChangeToService()) {
return;
}
const dialog = DialogPlugin.confirm({
title: "切换为服务产品",
content: "切换后将关闭产品及全部 SKU 的库存管理,并清空最低库存设置;已有库存流水会保留。确定继续吗?",
cancelBtn: "取消",
confirmBtn: "确定切换",
onCancel() {
dialog.destroy();
},
onConfirm() {
dialog.destroy();
applyProductType("SERVICE");
}
});
}
function applyProductType(type: ProductType): void {
form.productType = type;
if (type !== "SERVICE") {
return;
}
form.stockEnabled = false;
form.skuList.forEach(sku => {
sku.stockEnabled = false;
sku.minStockQuantityText = "";
sku.unitList.forEach(unit => { unit.allowPurchase = false; });
});
}
async function canChangeToService(): Promise<boolean> {
const skuIdList = form.skuList.map(sku => sku.id).filter((id): id is string => !!id);
if (!skuIdList.length) {
return true;
}
try {
const stockList = await ProductAPI.stockBatchDetail({ skuIdList });
if (stockList.some(stock => 0 < Number(stock.reservedQuantity || 0))) {
Message.warning("当前产品存在预占库存,不能切换为服务产品,请先完成或取消相关订单");
return false;
}
return true;
} catch (error) {
Message.error(getErrorMessage(error, "无法确认预占库存,暂不能切换为服务产品"));
return false;
}
}
function openCategoryPicker(): void {
+9 -2
View File
@@ -29,7 +29,14 @@
@click="toSkuList(item)"
>
<template #leftIcon>
<div class="avatar fallback" v-text="(item.productName || '产').slice(0, 1)" />
<t-image
v-if="getProductPhotoURL(item)"
:src="getProductPhotoURL(item)"
:alt="item.productName || '产品图片'"
fit="cover"
class="avatar"
/>
<div v-else class="avatar fallback" v-text="(item.productName || '产').slice(0, 1)" />
</template>
<template #title>
<div class="title">
@@ -67,7 +74,7 @@ import ProductAPI from "@/api/ProductAPI";
import { useNavBarStore } from "@/store/navBarStore";
import type { ProductStockProduct } from "@/types/Product";
import { getErrorMessage } from "@/utils/error";
import { getProductQuantityText } from "@/utils/product";
import { getProductPhotoURL, getProductQuantityText } from "@/utils/product";
import { Message } from "@/utils/message";
import { permissionCodes } from "@/utils/permission";
+14 -2
View File
@@ -32,7 +32,14 @@
@click="toDetail(item)"
>
<template #leftIcon>
<div class="avatar fallback" v-text="(item.sku?.name || 'S').slice(0, 1)" />
<t-image
v-if="getSkuPhotoURL(item)"
:src="getSkuPhotoURL(item)"
:alt="item.sku?.name || 'SKU 图片'"
fit="cover"
class="avatar"
/>
<div v-else class="avatar fallback" v-text="(item.sku?.name || 'S').slice(0, 1)" />
</template>
<template #title>
<div class="title">
@@ -71,7 +78,7 @@ import ProductAPI from "@/api/ProductAPI";
import { useNavBarStore } from "@/store/navBarStore";
import type { Product, ProductStock } from "@/types/Product";
import { getErrorMessage } from "@/utils/error";
import { getProductQuantityText } from "@/utils/product";
import { getProductPhotoURL, getProductQuantityText } from "@/utils/product";
import { Message } from "@/utils/message";
import { permissionCodes } from "@/utils/permission";
@@ -102,6 +109,11 @@ const emptyText = computed(() => {
}
return "该产品暂无启用库存管理的 SKU";
});
function getSkuPhotoURL(item: ProductStock): string {
return item.sku ? getProductPhotoURL(item.sku) : "";
}
let keywordTimer: number | undefined;
let requestToken = 0;
let suppressKeywordFetch = false;
+45 -12
View File
@@ -165,11 +165,14 @@
<t-switch
v-model="fulfillAll"
size="small"
:disabled="!canFulfillImmediately"
:disabled="!canFulfillImmediately || isStockChecking"
/>
</t-form-item>
<t-form-item label-width="0">
<div class="red">全部履约会同步启用全额收款;请先准备全部产品库存,任意一步失败,整张订单都会回滚。</div>
<div class="stock-tips">
<div class="tips red">全部履约会同步启用全额收款;请先准备充足的库存,任意一步失败都会下单失败。</div>
<product-stock-preview v-if="stockPreviewList.length" :items="stockPreviewList" />
</div>
</t-form-item>
<t-form-item v-if="(paymentVerified && paymentAmount) || (receiveFullPayment && totalAmount)" label-width="0">
<photo-scroller
@@ -230,6 +233,7 @@ import CodeScanner from "@/components/CodeScanner.vue";
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
import CustomerSelector from "@/components/CustomerSelector.vue";
import PhotoScroller from "@/components/PhotoScroller.vue";
import ProductStockPreview from "@/components/ProductStockPreview.vue";
import type { Customer } from "@/types/Customer";
import type { Product, ProductSku, ProductSkuUnit } from "@/types/Product";
import type { PhotoItem } from "@/types/Photo";
@@ -239,7 +243,8 @@ import { formatProductAmount, getProductQuantityStep } from "@/utils/product";
import { getErrorMessage } from "@/utils/error";
import { Message } from "@/utils/message";
import { permissionCodes } from "@/utils/permission";
import { getProductStockShortageMessage, mergeProductStockRequirements } from "@/utils/productStock";
import { buildProductStockPreview, getProductStockShortageMessage, mergeProductStockRequirements } from "@/utils/productStock";
import type { ProductStockPreviewItem } from "@/utils/productStock";
import { uploadPhotoList } from "@/utils/photo";
import { useNavBarStore } from "@/store/navBarStore";
import { useSettingStore } from "@/store/settingStore";
@@ -269,6 +274,9 @@ const paymentPhotoUrlList = computed(() => paymentPhotoItems.value.map(item => i
const fulfillmentPhotoUrlList = computed(() => fulfillmentPhotoItems.value.map(item => item.url));
const receiveFullPayment = ref(false);
const fulfillAll = ref(false);
const isStockChecking = ref(false);
const stockPreviewList = ref<ProductStockPreviewItem[]>([]);
let stockCheckToken = 0;
const paymentAmount = computed(() => parsePackageAmount(paymentAmountText.value));
const paymentVerified = computed(() => !receiveFullPayment.value
&& 0 < paymentAmountText.value.trim().length
@@ -293,9 +301,26 @@ const canFulfillImmediately = computed(() => userStore.hasPermissions(permission
&& userStore.hasPermissions(permissionCodes.productStock.read)
&& (totalAmount.value === 0 || canReceiveImmediately.value));
watch(fulfillAll, value => {
if (value && totalAmount.value !== 0) {
receiveFullPayment.value = true;
watch(fulfillAll, async value => {
stockCheckToken += 1;
const token = stockCheckToken;
if (!value) {
isStockChecking.value = false;
return;
}
isStockChecking.value = true;
try {
if (!await validateDirectStock() && token === stockCheckToken && fulfillAll.value) {
fulfillAll.value = false;
return;
}
if (token === stockCheckToken && fulfillAll.value && totalAmount.value !== 0) {
receiveFullPayment.value = true;
}
} finally {
if (token === stockCheckToken) {
isStockChecking.value = false;
}
}
});
@@ -469,6 +494,7 @@ function buildAttachments(source: PhotoItem[] = photoItems.value): Partial<Attac
}
async function validateDirectStock(): Promise<boolean> {
stockPreviewList.value = [];
const requirementList = mergeProductStockRequirements(items.value.flatMap(item => {
const sku = getSku(item);
const unit = getUnit(item);
@@ -477,13 +503,14 @@ async function validateDirectStock(): Promise<boolean> {
if (!item.skuId || !sku?.stockEnabled || !baseQuantity) {
return [];
}
return [{ skuId: item.skuId, baseQuantity, productName: getProduct(item)?.name || item.productName || "未命名产品", unitName: "基础单位" }];
return [{ skuId: item.skuId, baseQuantity, productName: getProduct(item)?.name || item.productName || "未命名产品", unitName: "基础单位", stockEnabled: sku.stockEnabled }];
}));
if (!requirementList.length) {
return true;
}
try {
const stockList = await ProductAPI.stockBatchDetail({ skuIdList: requirementList.map(item => item.skuId) });
stockPreviewList.value = buildProductStockPreview(stockList, requirementList);
const shortageMessage = getProductStockShortageMessage(stockList, requirementList);
if (shortageMessage) {
Message.warning(`${shortageMessage},不能直接履约`);
@@ -491,6 +518,7 @@ async function validateDirectStock(): Promise<boolean> {
}
return true;
} catch (error) {
stockPreviewList.value = [];
Message.error(getErrorMessage(error, "库存查询失败,暂不能直接履约"));
return false;
}
@@ -518,7 +546,12 @@ function validate(): boolean {
return true;
}
async function save(): Promise<void> {
if (isSaving.value || !validate()) return;
if (isSaving.value) return;
if (isStockChecking.value) {
Message.warning("库存正在预检,请稍候再提交");
return;
}
if (!validate()) return;
if (paymentVerified.value || receiveFullPayment.value || fulfillAll.value) {
const dialog = DialogPlugin.confirm({
title: "确认直通完成交易",
@@ -631,6 +664,10 @@ onUnmounted(() => navBarStore.clearRight());
}
}
.tips {
font-size: .875rem;
}
.product-part {
> .header {
@@ -738,10 +775,6 @@ onUnmounted(() => navBarStore.clearRight());
.textarea {
width: 100%;
}
.photo-part {
padding: 1rem;
}
}
.submit-wrap {
+1 -1
View File
@@ -59,7 +59,7 @@
<t-form-item v-if="canDirectVerify" label="直接核销收款" content-align="right">
<t-switch v-model="directVerify" size="small" />
<template #help v-if="directVerify">
<span class="red">店长直接核销后,收款会立即计入订单已收款,不再进入待核销队列。</span>
<span class="red">直接核销后,收款会立即计入订单已收款,不再进入待核销队列。</span>
</template>
</t-form-item>
<t-form-item label="备注">