完善POS

This commit is contained in:
richie
2026-07-22 21:07:51 +08:00
parent ece2713e44
commit 11958221e4
3 changed files with 760 additions and 51 deletions
@@ -0,0 +1,39 @@
package cn.qihangerp.erp.controller.pos;
import cn.qihangerp.common.AjaxResult;
import cn.qihangerp.model.entity.OGoodsInventory;
import cn.qihangerp.model.entity.OGoodsInventoryBatch;
import cn.qihangerp.security.common.BaseController;
import cn.qihangerp.service.OGoodsInventoryBatchService;
import cn.qihangerp.service.OGoodsInventoryService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@AllArgsConstructor
@RestController
@RequestMapping("/api/pos-api/inventory")
public class PosInventoryController extends BaseController {
private final OGoodsInventoryService goodsInventoryService;
private final OGoodsInventoryBatchService inventoryBatchService;
@GetMapping("/sku/{skuId}")
public AjaxResult getInventoryBySkuId(@PathVariable("skuId") String skuId) {
LambdaQueryWrapper<OGoodsInventory> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OGoodsInventory::getSkuId, skuId);
OGoodsInventory inventory = goodsInventoryService.getOne(queryWrapper);
return success(inventory);
}
@GetMapping("/sku/{skuId}/batches")
public AjaxResult getBatchesBySkuId(@PathVariable("skuId") String skuId) {
LambdaQueryWrapper<OGoodsInventoryBatch> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OGoodsInventoryBatch::getSkuId, skuId);
queryWrapper.gt(OGoodsInventoryBatch::getCurrentQty, 0);
List<OGoodsInventoryBatch> batches = inventoryBatchService.list(queryWrapper);
return success(batches);
}
}
+28
View File
@@ -1,5 +1,25 @@
import request from '@/utils/request'
export function getGoodsList(query?: Record<string, any>) {
return request({ url: '/api/erp-api/goods/list', method: 'get', params: query })
}
export function getGoodsDetail(id: number | string) {
return request({ url: '/api/pos-api/goods/' + id, method: 'get' })
}
export function getGoodsSkuList(goodsId: number | string) {
return request({ url: '/api/pos-api/goods/' + goodsId + '/sku', method: 'get' })
}
export function searchGoodsByBarcode(barcode: string) {
return request({ url: '/api/pos-api/goods/barcode/' + barcode, method: 'get' })
}
export function searchGoods(keyword: string) {
return request({ url: '/api/pos-api/goods/search', method: 'get', params: { keyword } })
}
export function submitOrder(data: Record<string, any>) {
return request({ url: '/api/pos-api/cashier/submit', method: 'post', data })
}
@@ -51,3 +71,11 @@ export function addMember(data: Record<string, any>) {
export function updateMember(data: Record<string, any>) {
return request({ url: '/api/pos-api/member', method: 'put', data })
}
export function getSkuInventory(skuId: number | string) {
return request({ url: '/api/pos-api/inventory/sku/' + skuId, method: 'get' })
}
export function getSkuInventoryBatches(skuId: number | string) {
return request({ url: '/api/pos-api/inventory/sku/' + skuId + '/batches', method: 'get' })
}
+693 -51
View File
@@ -9,6 +9,7 @@
placeholder="搜索商品/扫码..."
clearable
size="large"
@keyup.enter="handleSearch"
>
<template #prefix>
<el-icon :size="18"><Search /></el-icon>
@@ -45,7 +46,8 @@
v-for="product in filteredProducts"
:key="product.id"
class="product-card"
@click="addToCart(product)"
:class="{ 'has-multi': product.skuList && product.skuList.length > 1, 'no-stock': getTotalAvailableStock(product) <= 0 }"
@click="handleProductClick(product)"
>
<div class="product-image">
<img
@@ -57,17 +59,57 @@
<div v-else class="img-fallback">
<el-icon :size="28"><Picture /></el-icon>
</div>
<div v-if="product.skuList && product.skuList.length > 1" class="sku-badge">
{{ product.skuList.length }}种规格
</div>
<div v-if="hasMultipleBatches(product)" class="batch-badge">
多批次
</div>
<div v-if="getTotalAvailableStock(product) <= 0" class="no-stock-badge">
无库存
</div>
</div>
<div class="product-info">
<div class="product-name" :title="product.name">{{ product.name }}</div>
<div class="product-code">{{ product.goodsNum || '-' }}</div>
<div class="product-name" :title="product.name">
{{ product.name }}
<span v-if="product.shortName" class="product-short-name">({{ product.shortName }})</span>
</div>
<div class="product-meta-row">
<span class="meta-item" v-if="product.goodsNum">
<el-icon><CollectionTag /></el-icon>
{{ product.goodsNum }}
</span>
<span class="meta-item" v-if="product.unitName">
单位: {{ product.unitName }}
</span>
</div>
<div v-if="product.skuList && product.skuList.length > 0" class="product-sku-list">
<div v-for="sku in product.skuList.slice(0, 2)" :key="sku.id" class="sku-item-mini">
<span class="sku-name">{{ getSkuDisplayName(sku) }}</span>
<span class="sku-code" v-if="sku.skuCode">{{ sku.skuCode }}</span>
<span class="sku-barcode" v-if="sku.barCode">条码:{{ sku.barCode }}</span>
<span class="sku-stock" :class="{ 'low-stock': (sku.inventory?.availableQuantity ?? 0) <= 0 }">
库存:{{ sku.inventory?.availableQuantity ?? '-' }}
</span>
</div>
<div v-if="product.skuList.length > 2" class="sku-more">
还有{{ product.skuList.length - 2 }}种...
</div>
</div>
<div class="product-bottom">
<span class="price">¥{{ formatPrice(product.retailPrice) }}</span>
<div class="price-info">
<span class="price">¥{{ formatPrice(product.retailPrice) }}</span>
<span class="total-stock" :class="{ 'low-stock': getTotalAvailableStock(product) <= 0 }">
<el-icon><Box /></el-icon>
总库存:{{ getTotalAvailableStock(product) }}
</span>
</div>
<el-button
class="add-btn"
type="primary"
size="small"
circle
:disabled="getTotalAvailableStock(product) <= 0"
>
<el-icon><Plus /></el-icon>
</el-button>
@@ -106,7 +148,18 @@
<div v-else class="img-placeholder"><el-icon><Picture /></el-icon></div>
</div>
<div class="item-detail">
<div class="item-name">{{ item.name }}</div>
<div class="item-name" :title="item.name">
{{ item.shortName || item.name }}
<span class="item-full-name" v-if="item.shortName">{{ item.name }}</span>
</div>
<div class="item-sku-row">
<span class="item-sku" v-if="item.skuName">{{ item.skuName }}</span>
<span class="item-sku-code" v-if="item.skuCode">{{ item.skuCode }}</span>
</div>
<div class="item-meta-row">
<span class="item-barcode" v-if="item.barCode">条码: {{ item.barCode }}</span>
<span class="item-batch" v-if="item.batchNum">批次: {{ item.batchNum }}</span>
</div>
<div class="item-price">¥{{ item.price.toFixed(2) }}</div>
</div>
<div class="item-qty">
@@ -157,6 +210,63 @@
</div>
</div>
<el-dialog
v-model="showSkuDialog"
title="选择规格"
width="650px"
class="sku-dialog"
>
<div class="sku-dialog-body">
<div class="product-preview">
<img v-if="selectedProduct?.image" :src="selectedProduct.image" />
<div class="preview-info">
<h3>{{ selectedProduct?.name }}</h3>
<p class="preview-meta">
<span v-if="selectedProduct?.goodsNum">编码: {{ selectedProduct.goodsNum }}</span>
<span v-if="selectedProduct?.retailPrice">零售价: ¥{{ formatPrice(selectedProduct.retailPrice) }}</span>
</p>
</div>
</div>
<div class="sku-list">
<div
v-for="sku in selectedProduct?.skuList"
:key="sku.id"
class="sku-item"
:class="{
active: selectedSku?.id === sku.id,
'no-stock': (sku.inventory?.availableQuantity ?? 0) <= 0
}"
@click="(sku.inventory?.availableQuantity ?? 0) > 0 && selectSku(sku)"
>
<div class="sku-info">
<div class="sku-name">{{ getSkuDisplayName(sku) }}</div>
<div class="sku-meta">
<span v-if="sku.skuCode">SKU: {{ sku.skuCode }}</span>
<span v-if="sku.barCode">条码: {{ sku.barCode }}</span>
</div>
<div class="sku-stock-info">
<span class="stock-item">
<el-icon><Box /></el-icon>
库存: {{ sku.inventory?.availableQuantity ?? '-' }}
</span>
<span v-if="sku.batches && sku.batches.length > 1" class="batch-indicator">
{{ sku.batches.length }}个批次
</span>
</div>
</div>
<div class="sku-price">¥{{ formatPrice(sku.retailPrice) }}</div>
</div>
</div>
</div>
<template #footer>
<el-button @click="showSkuDialog = false">取消</el-button>
<el-button type="primary" :disabled="!selectedSku" @click="confirmSkuSelect">
确认选择
</el-button>
</template>
</el-dialog>
<el-dialog
v-model="showPaymentDialog"
title="确认结账"
@@ -226,13 +336,58 @@ import { ref, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Search, Loading, Goods, Picture, Plus, Minus, Close,
ShoppingCart, Delete, Wallet, CreditCard, Money, CircleCheckFilled
ShoppingCart, Delete, Wallet, CreditCard, Money, CircleCheckFilled,
CollectionTag, Postcard, Box
} from '@element-plus/icons-vue'
import { listGoods } from '@/api/goods/goods'
import { getGoodsList, getSkuInventory, getSkuInventoryBatches } from '@/api/pos/pos'
import { listCategory } from '@/api/goods/category'
import { addOrder } from '@/api/order/salesOrder'
const products = ref<any[]>([])
interface InventoryInfo {
skuId: string
quantity: number
availableQuantity: number
}
interface InventoryBatch {
id: number
skuId: string
batchNum: string
currentQty: number
barcode: string
stockStatus: number
}
interface Product {
id: string
name: string
shortName?: string
image?: string
goodsNum?: string
barCode?: string
unitName?: string
retailPrice?: number
categoryId?: number
skuList?: SkuItem[]
[key: string]: any
}
interface SkuItem {
id: string
goodsId?: string
skuName?: string
skuCode?: string
barCode?: string
colorValue?: string
sizeValue?: string
styleValue?: string
retailPrice?: number
inventory?: InventoryInfo
batches?: InventoryBatch[]
[key: string]: any
}
const products = ref<Product[]>([])
const categories = ref<any[]>([])
const loading = ref(false)
const hasMore = ref(false)
@@ -243,9 +398,12 @@ const selectedCategory = ref<string | number>('all')
const cartItems = ref<any[]>([])
const showPaymentDialog = ref(false)
const showSuccessDialog = ref(false)
const showSkuDialog = ref(false)
const selectedPay = ref('')
const lastOrderNo = ref('')
const lastOrderAmount = ref(0)
const selectedProduct = ref<Product | null>(null)
const selectedSku = ref<SkuItem | null>(null)
const payMethods = [
{ key: 'wechat', name: '微信支付', icon: Wallet },
@@ -279,6 +437,38 @@ function formatPrice(price: any) {
return Number(price).toFixed(2)
}
function getTotalAvailableStock(product: Product): number {
if (!product.skuList || product.skuList.length === 0) {
return product.inventory?.availableQuantity ?? (product as any).quantity ?? 0
}
let total = 0
for (const sku of product.skuList) {
total += sku.inventory?.availableQuantity ?? 0
}
return total
}
function hasMultipleBatches(product: Product): boolean {
if (!product.skuList || product.skuList.length === 0) return false
for (const sku of product.skuList) {
if (sku.batches && sku.batches.length > 1) {
return true
}
}
return false
}
function getSkuDisplayName(sku: SkuItem): string {
if (sku.colorValue || sku.sizeValue || sku.styleValue) {
const parts = []
if (sku.colorValue) parts.push(sku.colorValue)
if (sku.sizeValue) parts.push(sku.sizeValue)
if (sku.styleValue) parts.push(sku.styleValue)
return parts.join('/')
}
return sku.skuName || '默认'
}
function handleImageError(event: Event) {
const target = event.target as HTMLImageElement
target.style.display = 'none'
@@ -291,7 +481,7 @@ async function loadProducts(reset = true) {
}
loading.value = true
try {
const res = await listGoods({ pageNum: pageNum.value, pageSize: 24, status: 1 })
const res = await getGoodsList({ pageNum: pageNum.value, pageSize: 24, status: 1 })
const list = res.rows || []
products.value = reset ? list : [...products.value, ...list]
hasMore.value = list.length >= 24
@@ -315,20 +505,114 @@ function selectCategory(id: string | number) {
selectedCategory.value = id
}
function addToCart(product: any) {
const idx = cartItems.value.findIndex(i => i.goodsId === product.id)
function handleSearch() {
console.log('搜索:', searchKeyword.value)
}
async function loadSkuInventory(sku: SkuItem) {
try {
const invRes = await getSkuInventory(sku.id)
if (invRes && invRes.data) {
sku.inventory = {
skuId: sku.id,
quantity: invRes.data.quantity || 0,
availableQuantity: invRes.data.availableQuantity || 0
}
}
const batchRes = await getSkuInventoryBatches(sku.id)
if (batchRes && batchRes.data && batchRes.data.length > 0) {
sku.batches = batchRes.data.filter((b: InventoryBatch) => b.currentQty > 0)
}
} catch (e) {
console.error('加载SKU库存失败', e)
}
}
async function handleProductClick(product: Product) {
if (product.skuList && product.skuList.length > 1) {
selectedProduct.value = product
selectedSku.value = null
for (const sku of product.skuList) {
if (!sku.inventory || !sku.batches) {
await loadSkuInventory(sku)
}
}
showSkuDialog.value = true
} else {
await addToCart(product)
}
}
function selectSku(sku: SkuItem) {
selectedSku.value = sku
}
async function confirmSkuSelect() {
if (!selectedProduct.value || !selectedSku.value) return
const sku = selectedSku.value
const product = selectedProduct.value
const firstBatch = sku.batches && sku.batches.length > 0 ? sku.batches[0] : null
await addToCartWithSku(product, sku, firstBatch)
}
async function addToCart(product: Product) {
const sku = product.skuList?.[0]
if (!sku) {
ElMessage.error('该商品没有SKU')
return
}
if (!sku.inventory) {
await loadSkuInventory(sku)
}
const firstBatch = sku.batches && sku.batches.length > 0 ? sku.batches[0] : null
await addToCartWithSku(product, sku, firstBatch)
}
async function addToCartWithSku(product: Product, sku: SkuItem, batch?: InventoryBatch) {
const cartItem = {
goodsId: product.id,
skuId: sku.id,
name: product.name,
shortName: product.shortName || '',
skuName: sku.skuName || `${sku.colorValue || ''}${sku.sizeValue ? ' / ' + sku.sizeValue : ''}`,
image: product.image,
price: sku.retailPrice || product.retailPrice || 0,
barCode: batch?.barcode || sku.barCode || product.barCode,
skuCode: sku.skuCode,
batchNum: batch?.batchNum || '',
quantity: 1,
}
addToCartWithItem(cartItem)
closeDialogs()
}
function closeDialogs() {
showSkuDialog.value = false
selectedProduct.value = null
selectedSku.value = null
}
function addToCartWithItem(item: any) {
const idx = cartItems.value.findIndex(i =>
i.goodsId === item.goodsId && i.skuId === item.skuId && i.barCode === item.barCode
)
if (idx > -1) {
cartItems.value[idx].quantity++
} else {
cartItems.value.push({
goodsId: product.id,
name: product.name,
image: product.image,
price: product.retailPrice || 0,
quantity: 1,
})
cartItems.value.push(item)
}
ElMessage.success(`已添加: ${product.name}`)
const name = item.shortName || item.name
const skuInfo = item.skuName ? ` (${item.skuName})` : ''
const batchInfo = item.batchNum ? ` - 批次:${item.batchNum}` : ''
ElMessage.success(`已添加: ${name}${skuInfo}${batchInfo}`)
}
function increaseQty(idx: number) {
@@ -376,9 +660,12 @@ async function confirmPay() {
orderNo,
items: cartItems.value.map(i => ({
goodsId: i.goodsId,
skuId: i.skuId,
name: i.name,
skuName: i.skuName,
price: i.price,
quantity: i.quantity,
barCode: i.barCode,
})),
payAmount: finalAmount.value,
payMethod: selectedPay.value,
@@ -511,7 +798,7 @@ onMounted(() => {
.product-grid {
flex: 1;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 12px;
padding: 16px;
overflow-y: auto;
@@ -555,21 +842,25 @@ onMounted(() => {
transition: all 0.2s;
border: 1px solid #e4e7ed;
&:hover:not(.disabled) {
&:hover:not(.no-stock) {
transform: translateY(-2px);
border-color: #B4471D;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}
&.disabled {
opacity: 0.5;
&.has-multi {
border-style: dashed;
}
&.no-stock {
opacity: 0.6;
cursor: not-allowed;
}
.product-image {
position: relative;
width: 100%;
height: 140px;
height: 120px;
background: #f5f7fa;
overflow: hidden;
@@ -589,44 +880,126 @@ onMounted(() => {
display: block;
}
.stock-badge {
.sku-badge {
position: absolute;
top: 8px;
left: 8px;
padding: 3px 8px;
background: rgba(180, 71, 29, 0.9);
color: #fff;
border-radius: 10px;
font-size: 11px;
font-weight: 500;
}
.batch-badge {
position: absolute;
top: 8px;
right: 8px;
padding: 2px 8px;
padding: 3px 8px;
background: rgba(64, 158, 255, 0.9);
color: #fff;
border-radius: 10px;
font-size: 11px;
font-weight: 600;
font-weight: 500;
}
&.out {
background: rgba(245, 108, 108, 0.9);
color: #fff;
}
&.low {
background: rgba(230, 162, 60, 0.9);
color: #fff;
}
.no-stock-badge {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
padding: 3px 12px;
background: rgba(245, 108, 108, 0.9);
color: #fff;
border-radius: 10px;
font-size: 11px;
font-weight: 500;
}
}
.product-info {
padding: 10px 12px 12px;
padding: 10px;
.product-name {
font-size: 13px;
font-weight: 500;
font-weight: 600;
color: #303133;
margin-bottom: 4px;
margin-bottom: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
line-height: 18px;
.product-short-name {
font-weight: normal;
color: #606266;
font-size: 12px;
}
}
.product-code {
font-size: 11px;
color: #909399;
.product-meta-row {
display: flex;
gap: 10px;
margin-bottom: 6px;
.meta-item {
display: flex;
align-items: center;
gap: 3px;
font-size: 11px;
color: #909399;
.el-icon {
font-size: 12px;
}
}
}
.product-sku-list {
margin-bottom: 8px;
background: #f5f7fa;
border-radius: 6px;
padding: 6px 8px;
.sku-item-mini {
display: flex;
flex-wrap: wrap;
gap: 6px;
font-size: 11px;
color: #606266;
padding: 2px 0;
.sku-name {
font-weight: 500;
color: #303133;
}
.sku-code {
color: #909399;
}
.sku-barcode {
color: #909399;
}
.sku-stock {
margin-left: auto;
color: #67c23a;
&.low-stock {
color: #f56c6c;
}
}
}
.sku-more {
font-size: 10px;
color: #909399;
padding-top: 2px;
}
}
.product-bottom {
@@ -634,10 +1007,32 @@ onMounted(() => {
justify-content: space-between;
align-items: center;
.price {
font-size: 16px;
font-weight: 700;
color: #B4471D;
.price-info {
display: flex;
flex-direction: column;
gap: 2px;
.price {
font-size: 16px;
font-weight: 700;
color: #B4471D;
}
.total-stock {
display: flex;
align-items: center;
gap: 2px;
font-size: 11px;
color: #67c23a;
.el-icon {
font-size: 12px;
}
&.low-stock {
color: #f56c6c;
}
}
}
.add-btn {
@@ -739,7 +1134,7 @@ onMounted(() => {
.cart-item {
display: flex;
align-items: center;
align-items: flex-start;
gap: 10px;
padding: 10px;
background: #f5f7fa;
@@ -786,12 +1181,39 @@ onMounted(() => {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 4px;
.item-full-name {
font-size: 11px;
color: #909399;
margin-left: 4px;
}
}
.item-sku-row {
display: flex;
gap: 6px;
font-size: 11px;
color: #606266;
margin-bottom: 2px;
.item-sku-code {
color: #909399;
}
}
.item-meta-row {
display: flex;
gap: 8px;
font-size: 11px;
color: #909399;
margin-bottom: 2px;
}
.item-price {
font-size: 12px;
color: #909399;
margin-top: 2px;
font-size: 14px;
font-weight: 600;
color: #B4471D;
}
}
@@ -799,6 +1221,7 @@ onMounted(() => {
display: flex;
align-items: center;
gap: 6px;
margin-top: 12px;
:deep(.el-button) {
width: 24px;
@@ -924,6 +1347,225 @@ onMounted(() => {
}
}
.sku-dialog, .batch-dialog {
:deep(.el-dialog) {
border-radius: 16px;
background: #ffffff;
}
}
.sku-dialog {
.sku-dialog-body {
.product-preview {
display: flex;
gap: 16px;
padding: 16px;
background: #f5f7fa;
border-radius: 12px;
margin-bottom: 20px;
img {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 8px;
}
.preview-info {
flex: 1;
h3 {
font-size: 16px;
font-weight: 600;
color: #303133;
margin: 0 0 8px 0;
}
.preview-meta {
font-size: 13px;
color: #606266;
span {
margin-right: 16px;
}
}
}
}
.sku-list {
max-height: 350px;
overflow-y: auto;
&::-webkit-scrollbar {
width: 6px;
}
.sku-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 16px;
border: 2px solid #e4e7ed;
border-radius: 10px;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.2s;
&:hover:not(.no-stock) {
border-color: #dcdfe6;
}
&.active {
border-color: #B4471D;
background: rgba(180, 71, 29, 0.05);
}
&.no-stock {
opacity: 0.5;
cursor: not-allowed;
}
.sku-info {
flex: 1;
.sku-name {
font-size: 14px;
font-weight: 500;
color: #303133;
margin-bottom: 6px;
}
.sku-meta {
font-size: 12px;
color: #909399;
margin-bottom: 6px;
span {
margin-right: 12px;
}
}
.sku-stock-info {
display: flex;
align-items: center;
gap: 12px;
font-size: 12px;
.stock-item {
display: flex;
align-items: center;
gap: 4px;
color: #67c23a;
.el-icon {
font-size: 14px;
}
}
.batch-indicator {
padding: 2px 8px;
background: #ecf5ff;
color: #409eff;
border-radius: 10px;
font-size: 11px;
}
}
}
.sku-price {
font-size: 18px;
font-weight: 600;
color: #B4471D;
}
}
}
}
}
.batch-dialog {
.batch-dialog-body {
.batch-product-info {
padding: 12px 16px;
background: #f5f7fa;
border-radius: 8px;
margin-bottom: 16px;
.batch-product-name {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.batch-product-sku {
font-size: 13px;
color: #606266;
}
}
.batch-list {
max-height: 350px;
overflow-y: auto;
&::-webkit-scrollbar {
width: 6px;
}
.batch-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border: 2px solid #e4e7ed;
border-radius: 10px;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.2s;
&:hover:not(.no-stock) {
border-color: #dcdfe6;
}
&.active {
border-color: #B4471D;
background: rgba(180, 71, 29, 0.05);
}
&.no-stock {
opacity: 0.5;
cursor: not-allowed;
}
.batch-info {
.batch-barcode {
display: flex;
align-items: center;
gap: 8px;
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 6px;
.el-icon {
color: #909399;
}
}
.batch-details {
display: flex;
gap: 16px;
font-size: 12px;
color: #606266;
}
}
.batch-select {
flex-shrink: 0;
}
}
}
}
}
.payment-dialog {
:deep(.el-dialog) {
background: #ffffff;