Compare commits
50 Commits
82efb8c251
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
646f69bb56 | ||
|
|
756444ef2b | ||
|
|
6dfc201c66 | ||
|
|
870e1c612d | ||
|
|
fbcd930887 | ||
|
|
a1bcc2e478 | ||
|
|
a84cd57fad | ||
|
|
c49a6d8288 | ||
|
|
796d083823 | ||
|
|
817f6e3436 | ||
|
|
3e32aa6313 | ||
|
|
255a0be7f2 | ||
|
|
1c422f5436 | ||
|
|
46894e04fe | ||
|
|
1ea0fc04bf | ||
|
|
7a9c31ca27 | ||
|
|
7b24ea86f5 | ||
|
|
b789a4af92 | ||
|
|
c6f93aaa44 | ||
|
|
3fe8c80696 | ||
|
|
3860c9583b | ||
|
|
82bebc0dd8 | ||
|
|
2c68fe898a | ||
|
|
e8259fbf91 | ||
|
|
84b0d15a0c | ||
|
|
d932d26830 | ||
|
|
b62c62c6e0 | ||
|
|
10b421d682 | ||
|
|
1899ebf226 | ||
|
|
02f151307d | ||
|
|
c0adc172c1 | ||
|
|
b90847a0f1 | ||
|
|
e25d288fae | ||
|
|
0500d3e688 | ||
|
|
45d6cc53ca | ||
|
|
d9881a50c1 | ||
|
|
6df829cf90 | ||
|
|
5dc15bb2a9 | ||
|
|
08f440f7c8 | ||
|
|
826973f42a | ||
|
|
e040f6d93b | ||
|
|
a38039f4aa | ||
|
|
5af9b7c0ff | ||
|
|
10ebbd6b6f | ||
|
|
0772a91c26 | ||
|
|
3d827c0033 | ||
|
|
f388a0c5b1 | ||
|
|
324bd1166f | ||
|
|
64767e9ca0 | ||
|
|
a4f548b847 |
@@ -15,7 +15,7 @@ export default {
|
||||
* 新增分类
|
||||
*/
|
||||
createCategory(data) {
|
||||
return api.request('/products/categories', 'POST', data)
|
||||
return api.request('/products/categories', 'POST', {}, data)
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -33,12 +33,19 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取商品列表
|
||||
* 获取商品列表(只显示上架)
|
||||
*/
|
||||
getProducts(params) {
|
||||
return api.request('/products', 'GET', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取所有商品(包括下架,用于管理)
|
||||
*/
|
||||
getAllProducts(params) {
|
||||
return api.request('/products/all', 'GET', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取商品详情
|
||||
*/
|
||||
@@ -50,14 +57,14 @@ export default {
|
||||
* 新增商品
|
||||
*/
|
||||
createProduct(data) {
|
||||
return api.request('/products', 'POST', data)
|
||||
return api.request('/products', 'POST', {}, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改商品
|
||||
*/
|
||||
updateProduct(id, data) {
|
||||
return api.request(`/products/${id}`, 'PUT', data)
|
||||
return api.request(`/products/${id}`, 'PUT', {}, data)
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/product/list",
|
||||
"pagePath": "pages/product/manage",
|
||||
"text": "商品"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -74,10 +74,6 @@
|
||||
<view class="menu-icon-box pink"><text class="icon-text">类</text></view>
|
||||
<text class="menu-text">种类管理</text>
|
||||
</view>
|
||||
<view class="menu-item" @click="goStock()">
|
||||
<view class="menu-icon-box cyan"><text class="icon-text">库</text></view>
|
||||
<text class="menu-text">库存管理</text>
|
||||
</view>
|
||||
<view class="menu-item" @click="goTo('/pages/stock/in')">
|
||||
<view class="menu-icon-box orange"><text class="icon-text">入</text></view>
|
||||
<text class="menu-text">入库</text>
|
||||
@@ -145,14 +141,16 @@ export default {
|
||||
else this.greeting = '晚上好'
|
||||
},
|
||||
goTo(url) {
|
||||
// tabBar 页面需要用 switchTab
|
||||
if (url.startsWith('/pages/product/manage') || url.startsWith('/pages/order/list') || url.startsWith('/pages/index/index')) {
|
||||
uni.switchTab({ url })
|
||||
} else {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
},
|
||||
goToTab(url) {
|
||||
uni.switchTab({ url })
|
||||
},
|
||||
goStock() {
|
||||
uni.navigateTo({ url: '/pages/stock/list' })
|
||||
},
|
||||
logout() {
|
||||
uni.showModal({
|
||||
title: '退出登录',
|
||||
|
||||
@@ -46,18 +46,24 @@
|
||||
|
||||
<view v-for="(item, index) in orderItems" :key="index" class="order-item">
|
||||
<view class="item-info">
|
||||
<text class="item-name">{{ item.productName }}</text>
|
||||
<text class="item-spec">{{ item.spec || '-' }}</text>
|
||||
<text class="item-name-box">{{ item.productName }}</text>
|
||||
<text class="item-spec">{{ item.spec ? ' ' + item.spec : '' }}</text>
|
||||
<text class="item-dims" v-if="item.length && item.width">{{ item.length }}x{{ item.width }}</text>
|
||||
<text class="item-stock">库存: {{ stocks[item.productId] || 0 }}</text>
|
||||
</view>
|
||||
<view class="item-edit">
|
||||
<view class="quantity-edit">
|
||||
<text class="qty-label">数量</text>
|
||||
<view class="qty-wrapper">
|
||||
<text class="qty-btn" @click="qtyMinus(index)">-</text>
|
||||
<input
|
||||
class="qty-input"
|
||||
type="number"
|
||||
v-model="item.quantity"
|
||||
@change="calcAmount"
|
||||
@change="onQuantityChange(index)"
|
||||
/>
|
||||
<text class="qty-btn" @click="qtyPlus(index)">+</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="price-edit">
|
||||
<text class="qty-label">单价</text>
|
||||
@@ -174,6 +180,7 @@
|
||||
|
||||
<script>
|
||||
import orderApi from '@/api/order'
|
||||
import stockApi from '@/api/stock'
|
||||
import productApi from '@/api/product'
|
||||
import customerApi from '@/api/customer'
|
||||
|
||||
@@ -197,6 +204,7 @@ export default {
|
||||
orderItems: [],
|
||||
productList: [],
|
||||
searchKeyword: '',
|
||||
stocks: {}, // 商品库存
|
||||
|
||||
// 金额相关
|
||||
discountRate: 100, // 折扣率(保留逻辑,默认100%不打折)
|
||||
@@ -218,6 +226,7 @@ export default {
|
||||
onLoad(options) {
|
||||
this.loadCustomersByType()
|
||||
this.loadProducts()
|
||||
this.loadStocks()
|
||||
if (options.orderId) {
|
||||
this.editingOrderId = options.orderId
|
||||
// 编辑模式
|
||||
@@ -249,7 +258,10 @@ export default {
|
||||
spec: item.productSpec,
|
||||
unit: item.unit,
|
||||
price: item.price,
|
||||
quantity: item.quantity
|
||||
quantity: item.quantity,
|
||||
length: item.length || '',
|
||||
width: item.width || '',
|
||||
area: item.area || ''
|
||||
}))
|
||||
|
||||
this.discountRate = order.discountRate
|
||||
@@ -295,6 +307,20 @@ export default {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
async loadStocks() {
|
||||
try {
|
||||
const res = await stockApi.getStockList({ page: 1, pageSize: 500 })
|
||||
const stockMap = {}
|
||||
if (res.records) {
|
||||
res.records.forEach(s => {
|
||||
stockMap[s.productId] = s.quantity || 0
|
||||
})
|
||||
}
|
||||
this.stocks = stockMap
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
selectCustomer(e) {
|
||||
this.selectedCustomer = this.filteredCustomers[e.detail.value]
|
||||
},
|
||||
@@ -335,6 +361,9 @@ export default {
|
||||
spec: product.spec,
|
||||
unit: product.unit,
|
||||
price: product.price,
|
||||
length: product.length || '',
|
||||
width: product.width || '',
|
||||
area: product.area || '',
|
||||
quantity: 1
|
||||
})
|
||||
this.calcAmount()
|
||||
@@ -343,6 +372,38 @@ export default {
|
||||
this.orderItems.splice(index, 1)
|
||||
this.calcAmount()
|
||||
},
|
||||
qtyMinus(index) {
|
||||
const item = this.orderItems[index]
|
||||
const stock = this.stocks[item.productId] || 0
|
||||
if (item.quantity > 1) {
|
||||
item.quantity--
|
||||
this.calcAmount()
|
||||
}
|
||||
},
|
||||
qtyPlus(index) {
|
||||
const item = this.orderItems[index]
|
||||
const stock = this.stocks[item.productId] || 0
|
||||
if (item.quantity < stock) {
|
||||
item.quantity++
|
||||
this.calcAmount()
|
||||
} else {
|
||||
uni.showToast({ title: '库存不足', icon: 'none' })
|
||||
}
|
||||
},
|
||||
onQuantityChange(index) {
|
||||
const item = this.orderItems[index]
|
||||
const stock = this.stocks[item.productId] || 0
|
||||
let qty = parseInt(item.quantity)
|
||||
if (isNaN(qty) || qty < 1) {
|
||||
qty = 1
|
||||
}
|
||||
if (qty > stock) {
|
||||
qty = stock
|
||||
uni.showToast({ title: '库存不足,已调整为最大库存', icon: 'none' })
|
||||
}
|
||||
item.quantity = qty
|
||||
this.calcAmount()
|
||||
},
|
||||
// 核心金额计算逻辑
|
||||
calcAmount() {
|
||||
// 1. 计算原价 = Σ(单价 × 数量)
|
||||
@@ -390,7 +451,10 @@ export default {
|
||||
items: this.orderItems.map(item => ({
|
||||
productId: item.productId,
|
||||
quantity: item.quantity,
|
||||
price: item.price
|
||||
price: item.price,
|
||||
length: item.length || null,
|
||||
width: item.width || null,
|
||||
area: item.area || null
|
||||
})),
|
||||
discountRate: 100, // 保留折扣率逻辑,默认100%
|
||||
discountMoney: parseFloat(this.discountMoney) || 0,
|
||||
@@ -514,13 +578,19 @@ export default {
|
||||
|
||||
.item-info {
|
||||
margin-bottom: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
.item-name-box {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
color: #333;
|
||||
width: 170rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.item-spec {
|
||||
@@ -528,6 +598,21 @@ export default {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.item-dims {
|
||||
font-size: 22rpx;
|
||||
color: #667eea;
|
||||
background: #f0f0ff;
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 4rpx;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.item-stock {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.item-edit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -536,23 +621,69 @@ export default {
|
||||
.quantity-edit, .price-edit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 20rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.qty-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-right: 8rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.qty-input, .price-input {
|
||||
.qty-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.qty-btn {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
color: #667eea;
|
||||
background: #fff;
|
||||
border: 1rpx solid #ddd;
|
||||
}
|
||||
|
||||
.qty-btn:first-child {
|
||||
border-radius: 8rpx 0 0 8rpx;
|
||||
}
|
||||
|
||||
.qty-btn:last-child {
|
||||
border-radius: 0 8rpx 8rpx 0;
|
||||
}
|
||||
|
||||
.qty-input {
|
||||
width: 100rpx;
|
||||
height: 56rpx;
|
||||
height: 48rpx;
|
||||
background: #fff;
|
||||
border: 1rpx solid #ddd;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.price-input {
|
||||
width: 120rpx;
|
||||
height: 48rpx;
|
||||
background: #fff;
|
||||
border: 1rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.stock-info {
|
||||
font-size: 20rpx;
|
||||
color: #999;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
.item-subtotal {
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
<view class="section-title">商品明细</view>
|
||||
<view class="items-list">
|
||||
<view class="item-row header">
|
||||
<text class="item-name">商品名称</text>
|
||||
<text class="item-info">商品信息</text>
|
||||
<text class="item-area">总面积(m²)</text>
|
||||
<text class="item-qty">数量</text>
|
||||
<text class="item-price">单价</text>
|
||||
<text class="item-subtotal">小计</text>
|
||||
@@ -46,10 +47,11 @@
|
||||
:key="index"
|
||||
class="item-row"
|
||||
>
|
||||
<text class="item-name">{{ item.productName }}</text>
|
||||
<text class="item-info"><text class="item-name-text">{{ item.productName }}</text><text class="item-spec-text">{{ item.productSpec ? ' ' + item.productSpec : '' }}</text><text class="item-dims-text">{{ item.length || '-' }}x{{ item.width || '-' }}</text></text>
|
||||
<text class="item-area">{{ calcArea(item) }}</text>
|
||||
<text class="item-qty">{{ item.quantity }}</text>
|
||||
<text class="item-price">¥{{ item.price }}</text>
|
||||
<text class="item-subtotal">¥{{ (item.price * item.quantity).toFixed(2) }}</text>
|
||||
<text class="item-subtotal">¥{{ (item.price * item.quantity).toFixed(0) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -170,6 +172,12 @@ export default {
|
||||
if (!time) return ''
|
||||
return time.substring(0, 16).replace('T', ' ')
|
||||
},
|
||||
calcArea(item) {
|
||||
if (item.length && item.width && item.quantity) {
|
||||
return (item.length * item.width * item.quantity / 1000000).toFixed(4)
|
||||
}
|
||||
return '-'
|
||||
},
|
||||
getPaymentMethod(method) {
|
||||
const map = {
|
||||
'cash': '现金',
|
||||
@@ -345,7 +353,8 @@ export default {
|
||||
display: flex;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f8f8f8;
|
||||
font-size: 26rpx;
|
||||
font-size: 24rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-row.header {
|
||||
@@ -355,12 +364,36 @@ export default {
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
flex: 2;
|
||||
.item-info {
|
||||
flex: 3;
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.item-name-text {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-spec-text {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.item-dims-text {
|
||||
color: #666;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.item-area {
|
||||
flex: 1.5;
|
||||
text-align: center;
|
||||
color: #667eea;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-qty {
|
||||
flex: 1;
|
||||
flex: 0.8;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,14 +158,9 @@ export default {
|
||||
|
||||
const list = res.records || []
|
||||
|
||||
// 加载每个订单的明细
|
||||
// 直接使用订单中的items,不再单独请求
|
||||
for (const order of list) {
|
||||
try {
|
||||
const detail = await orderApi.getOrderDetail(order.orderId)
|
||||
this.$set(this.orderItemsMap, order.orderId, detail.items || [])
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
this.$set(this.orderItemsMap, order.orderId, order.items || [])
|
||||
}
|
||||
|
||||
if (this.page === 1) {
|
||||
|
||||
@@ -219,15 +219,21 @@ export default {
|
||||
this.returnQty++
|
||||
}
|
||||
},
|
||||
confirmReturn() {
|
||||
async confirmReturn() {
|
||||
if (this.returnQty <= 0 || this.returnQty > this.currentItem.returnableQty) {
|
||||
uni.showToast({ title: '退货数量无效', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await orderApi.refundOrder(this.currentItem.orderId)
|
||||
uni.showToast({ title: '退货成功', icon: 'success' })
|
||||
// TODO: 调用后端创建退货订单
|
||||
this.closePopup()
|
||||
this.loadReturnableGoods()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
uni.showToast({ title: e.message || '退货失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +159,9 @@ export default {
|
||||
this.loadProducts()
|
||||
},
|
||||
viewDetail(item) {
|
||||
// 跳转到商品管理页面进行编辑
|
||||
uni.navigateTo({
|
||||
url: `/pages/product/detail?productId=${item.productId}`
|
||||
url: `/pages/product/manage?productId=${item.productId}`
|
||||
})
|
||||
},
|
||||
getStock(productId) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-section">
|
||||
<view class="search-bar">
|
||||
<input
|
||||
class="search-input"
|
||||
@@ -8,31 +9,53 @@
|
||||
placeholder="搜索商品名称"
|
||||
@confirm="search"
|
||||
/>
|
||||
<button class="search-btn" @click="search">搜索</button>
|
||||
<button class="add-btn" @click="addProduct">+</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<!-- 分类侧栏 + 商品列表 -->
|
||||
<view class="content-wrapper">
|
||||
<!-- 左侧分类 -->
|
||||
<scroll-view scroll-y class="category-sidebar">
|
||||
<view class="category-item" :class="{ active: !categoryId }" @click="selectCategory('')">
|
||||
全部
|
||||
</view>
|
||||
<view v-for="cat in categories" :key="cat.categoryId" class="category-item" :class="{ active: categoryId === cat.categoryId }" @click="selectCategory(cat.categoryId)">
|
||||
{{ cat.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 右侧商品列表 -->
|
||||
<scroll-view scroll-y class="product-scroll">
|
||||
<view class="product-list">
|
||||
<view
|
||||
v-for="item in products"
|
||||
v-for="item in productList"
|
||||
:key="item.productId"
|
||||
class="product-item"
|
||||
>
|
||||
<view class="product-info" @click="editProduct(item)">
|
||||
<view class="product-info">
|
||||
<view class="product-header">
|
||||
<text class="product-name">{{ item.name }}</text>
|
||||
<text class="product-category" v-if="item.categoryName">{{ item.categoryName }}</text>
|
||||
</view>
|
||||
<view class="product-row">
|
||||
<text class="product-spec">{{ item.spec || '-' }}</text>
|
||||
<view class="product-price">
|
||||
<text class="price">¥{{ item.price }}</text>
|
||||
<text class="unit">/{{ item.unit }}</text>
|
||||
<text class="product-unit">/{{ item.unit }}</text>
|
||||
<text class="product-price-text">¥{{ item.price }}</text>
|
||||
</view>
|
||||
<view class="product-spec-row" v-if="item.length && item.width">
|
||||
<text class="spec-text">规格:{{ item.length }} x {{ item.width }} = {{ item.area }} m²</text>
|
||||
</view>
|
||||
<view class="product-status">
|
||||
<text :class="['status', item.status === 1 ? 'on' : 'off']">
|
||||
{{ item.status === 1 ? '上架' : '下架' }}
|
||||
{{ item.status === 1 ? '已上架' : '已下架' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="product-actions">
|
||||
<view class="action-btn edit" @click="editProduct(item)" v-if="item.status === 1">
|
||||
编辑
|
||||
</view>
|
||||
<view class="action-btn" @click="toggleStatus(item)">
|
||||
{{ item.status === 1 ? '下架' : '上架' }}
|
||||
</view>
|
||||
@@ -43,10 +66,12 @@
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-if="products.length === 0" class="empty">
|
||||
<view v-if="productList.length === 0" class="empty">
|
||||
<text>暂无商品</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 商品表单弹窗 -->
|
||||
<view class="modal" v-if="showModal">
|
||||
@@ -58,48 +83,48 @@
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<view class="form-item">
|
||||
<text class="label">分类</text>
|
||||
<text class="label"><text class="required">*</text>分类</text>
|
||||
<picker :range="categories" range-key="name" @change="onCategoryChange">
|
||||
<view class="picker">
|
||||
{{ form.categoryId ? getCategoryName(form.categoryId) : '请选择分类' }}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<view class="form-item">
|
||||
<text class="label"><text class="required">*</text>商品名称</text>
|
||||
<input class="input" v-model="form.name" placeholder="请输入商品名称" />
|
||||
</view>
|
||||
<view class="form-row size-row">
|
||||
<view class="form-item half">
|
||||
<text class="label">长度</text>
|
||||
<input class="input" type="digit" v-model="form.length" placeholder="非必须" />
|
||||
<text class="label">长度(mm)</text>
|
||||
<input class="input" type="digit" v-model="form.length" maxlength="5" />
|
||||
</view>
|
||||
<view class="form-item half">
|
||||
<text class="label">宽度</text>
|
||||
<input class="input" type="digit" v-model="form.width" placeholder="非必须" />
|
||||
<text class="label">宽度(mm)</text>
|
||||
<input class="input" type="digit" v-model="form.width" maxlength="5" />
|
||||
</view>
|
||||
<view class="form-item half">
|
||||
<text class="label">面积</text>
|
||||
<input class="input" type="digit" v-model="form.area" placeholder="自动计算" disabled />
|
||||
<text class="label">面积(m²)</text>
|
||||
<input class="input" type="digit" v-model="form.area" disabled />
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<view class="form-item half">
|
||||
<text class="label">规格</text>
|
||||
<input class="input" v-model="form.spec" placeholder="非必须" />
|
||||
<text class="label">颜色</text>
|
||||
<input class="input" v-model="form.spec" />
|
||||
</view>
|
||||
<view class="form-item half">
|
||||
<text class="label">单位*</text>
|
||||
<text class="label"><text class="required">*</text>单位</text>
|
||||
<input class="input" v-model="form.unit" placeholder="如:个、箱、米" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">商品名称*</text>
|
||||
<input class="input" v-model="form.name" placeholder="请输入商品名称" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">价格*</text>
|
||||
<text class="label"><text class="required">*</text>价格</text>
|
||||
<input class="input" type="digit" v-model="form.price" placeholder="请输入价格" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="label">备注</text>
|
||||
<textarea class="textarea" v-model="form.remark" placeholder="请输入备注" />
|
||||
<textarea class="textarea" v-model="form.remark" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
@@ -119,7 +144,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
keyword: '',
|
||||
products: [],
|
||||
categoryId: '',
|
||||
productList: [],
|
||||
categories: [],
|
||||
showModal: false,
|
||||
isEdit: false,
|
||||
@@ -138,22 +164,27 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
if (!canManageProduct()) {
|
||||
uni.showToast({ title: '无权限', icon: 'none' })
|
||||
uni.navigateBack()
|
||||
return
|
||||
}
|
||||
onLoad(options) {
|
||||
// 移除权限检查,允许所有用户访问
|
||||
this.loadCategories()
|
||||
this.loadProducts()
|
||||
// 如果传入了 productId,则打开编辑弹窗
|
||||
if (options.productId) {
|
||||
this.$nextTick(() => {
|
||||
const item = this.productList.find(p => p.productId === options.productId)
|
||||
if (item) {
|
||||
this.editProduct(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
computedArea() {
|
||||
const l = parseFloat(this.form.length)
|
||||
const w = parseFloat(this.form.width)
|
||||
// 长度(cm) × 宽度(cm) ÷ 10000 = 面积(m²)
|
||||
// 长度(mm) × 宽度(mm) ÷ 1000000 = 面积(m²)
|
||||
if (!isNaN(l) && !isNaN(w) && l > 0 && w > 0) {
|
||||
return (l * w / 10000).toFixed(2)
|
||||
return (l * w / 1000000).toFixed(4)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
@@ -174,16 +205,21 @@ export default {
|
||||
},
|
||||
async loadProducts() {
|
||||
try {
|
||||
const res = await productApi.getProducts({
|
||||
const res = await productApi.getAllProducts({
|
||||
keyword: this.keyword,
|
||||
categoryId: this.categoryId,
|
||||
page: 1,
|
||||
pageSize: 100
|
||||
})
|
||||
this.products = res.records || []
|
||||
this.productList = res.records || []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
},
|
||||
selectCategory(id) {
|
||||
this.categoryId = id
|
||||
this.loadProducts()
|
||||
},
|
||||
search() {
|
||||
this.loadProducts()
|
||||
},
|
||||
@@ -196,6 +232,9 @@ export default {
|
||||
unit: '',
|
||||
price: '',
|
||||
categoryId: '',
|
||||
length: '',
|
||||
width: '',
|
||||
area: '',
|
||||
remark: '',
|
||||
status: 1
|
||||
}
|
||||
@@ -218,6 +257,10 @@ export default {
|
||||
return cat ? cat.name : ''
|
||||
},
|
||||
async saveProduct() {
|
||||
if (!this.form.categoryId) {
|
||||
uni.showToast({ title: '请选择分类', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!this.form.name) {
|
||||
uni.showToast({ title: '请输入商品名称', icon: 'none' })
|
||||
return
|
||||
@@ -232,11 +275,12 @@ export default {
|
||||
}
|
||||
|
||||
try {
|
||||
const formData = JSON.parse(JSON.stringify(this.form))
|
||||
if (this.isEdit) {
|
||||
await productApi.updateProduct(this.form)
|
||||
await productApi.updateProduct(formData.productId, formData)
|
||||
uni.showToast({ title: '更新成功', icon: 'success' })
|
||||
} else {
|
||||
await productApi.createProduct(this.form)
|
||||
await productApi.createProduct(formData)
|
||||
uni.showToast({ title: '创建成功', icon: 'success' })
|
||||
}
|
||||
this.closeModal()
|
||||
@@ -249,8 +293,7 @@ export default {
|
||||
async toggleStatus(item) {
|
||||
const newStatus = item.status === 1 ? 0 : 1
|
||||
try {
|
||||
await productApi.updateProduct({
|
||||
productId: item.productId,
|
||||
await productApi.updateProduct(item.productId, {
|
||||
status: newStatus
|
||||
})
|
||||
uni.showToast({ title: newStatus === 1 ? '已上架' : '已下架', icon: 'success' })
|
||||
@@ -284,6 +327,81 @@ export default {
|
||||
|
||||
<style>
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* 搜索区域 */
|
||||
.search-section {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 50rpx;
|
||||
padding: 0 20rpx;
|
||||
height: 70rpx;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
background: #3cc51f;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
font-size: 36rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
/* 内容区域:侧栏+列表 */
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 分类侧栏 */
|
||||
.category-sidebar {
|
||||
width: 160rpx;
|
||||
background: #fff;
|
||||
flex-shrink: 0;
|
||||
border-right: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
padding: 28rpx 16rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
border-left: 6rpx solid transparent;
|
||||
}
|
||||
|
||||
.category-item.active {
|
||||
background: #f8f9fa;
|
||||
color: #667eea;
|
||||
font-weight: bold;
|
||||
border-left-color: #667eea;
|
||||
}
|
||||
|
||||
/* 商品列表 */
|
||||
.product-scroll {
|
||||
flex: 1;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.product-list {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
@@ -347,13 +465,57 @@ export default {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.product-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.product-category {
|
||||
font-size: 22rpx;
|
||||
color: #667eea;
|
||||
background: #f0f0ff;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
|
||||
.product-spec {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.product-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.product-unit {
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
.product-price-text {
|
||||
color: #ff4d4f;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.product-spec-row {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.spec-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
background: #f5f5f5;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
@@ -404,6 +566,11 @@ export default {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.action-btn.edit {
|
||||
background: #f0f5ff;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.action-btn.delete {
|
||||
background: #fff1f0;
|
||||
color: #ff4d4f;
|
||||
@@ -469,6 +636,28 @@ export default {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.form-row .half {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.size-row {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.size-row .half {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-row.three .half {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
@@ -476,13 +665,25 @@ export default {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #ff4d4f;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.input[disabled] {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
height: 70rpx;
|
||||
padding: 0 20rpx;
|
||||
background: #f5f5f5;
|
||||
background: #fff;
|
||||
border: 2rpx solid #e5e5e5;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.picker {
|
||||
|
||||
@@ -14,23 +14,32 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分类行 -->
|
||||
<view class="category-row">
|
||||
<!-- 分类侧栏 + 商品列表 -->
|
||||
<view class="content-wrapper">
|
||||
<!-- 左侧分类 -->
|
||||
<scroll-view scroll-y class="category-sidebar">
|
||||
<view class="category-item" :class="{ active: !categoryId }" @click="selectCategory('')">
|
||||
全部
|
||||
</view>
|
||||
<view v-for="cat in categories" :key="cat.categoryId" class="category-item" :class="{ active: categoryId === cat.categoryId }" @click="selectCategory(cat.categoryId)">
|
||||
{{ cat.name }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<!-- 右侧商品列表 -->
|
||||
<scroll-view scroll-y class="product-scroll">
|
||||
<view class="product-list">
|
||||
<view v-for="item in productList" :key="item.productId" class="product-item" @click="openProductDetail(item)">
|
||||
<view v-for="item in productList" :key="item.productId" class="product-item" :class="{ disabled: stocks[item.productId] === 0 }" @click="selectProduct(item)">
|
||||
<view class="product-info">
|
||||
<text class="product-name">{{ item.name }}</text>
|
||||
<view class="product-row">
|
||||
<text class="product-spec">{{ item.spec || '-' }}</text>
|
||||
<text class="product-size" v-if="item.length && item.width">{{ item.length }} x {{ item.width }} = {{ item.area }} m²</text>
|
||||
</view>
|
||||
<view class="product-stock">
|
||||
<text class="stock-label">库存:</text>
|
||||
<text class="stock-value" :class="{ 'stock-zero': stocks[item.productId] === 0 }">{{ stocks[item.productId] || 0 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="product-price">
|
||||
<text class="price">¥{{ item.price }}</text>
|
||||
@@ -44,6 +53,7 @@
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 商品详情/选择弹窗 -->
|
||||
<view class="modal-mask" v-if="showModal" @click="closeModal">
|
||||
@@ -79,6 +89,7 @@
|
||||
|
||||
<script>
|
||||
import productApi from '@/api/product'
|
||||
import stockApi from '@/api/stock'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -87,6 +98,7 @@ export default {
|
||||
categoryId: '',
|
||||
categories: [],
|
||||
productList: [],
|
||||
stocks: {}, // 商品库存
|
||||
page: 1,
|
||||
pageSize: 50,
|
||||
loading: false,
|
||||
@@ -116,6 +128,17 @@ export default {
|
||||
this.getProducts()
|
||||
},
|
||||
methods: {
|
||||
selectProduct(item) {
|
||||
// 库存为0不可选中
|
||||
if (this.stocks[item.productId] === 0) {
|
||||
uni.showToast({ title: '库存不足', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const pages = getCurrentPages()
|
||||
const prevPage = pages[pages.length - 2]
|
||||
prevPage.$vm.addProduct(item)
|
||||
uni.navigateBack()
|
||||
},
|
||||
calcArea() {
|
||||
const length = parseFloat(this.selectedProduct.length) || 0
|
||||
const width = parseFloat(this.selectedProduct.width) || 0
|
||||
@@ -140,6 +163,16 @@ export default {
|
||||
pageSize: this.pageSize
|
||||
})
|
||||
this.productList = res.records || []
|
||||
|
||||
// 获取每个商品的库存
|
||||
const stockRes = await stockApi.getStockList({ page: 1, pageSize: 500 })
|
||||
const stockMap = {}
|
||||
if (stockRes.records) {
|
||||
stockRes.records.forEach(s => {
|
||||
stockMap[s.productId] = s.quantity || 0
|
||||
})
|
||||
}
|
||||
this.stocks = stockMap
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
} finally {
|
||||
@@ -217,32 +250,37 @@ export default {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 分类行 */
|
||||
.category-row {
|
||||
/* 内容区域:侧栏+列表 */
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
padding: 20rpx;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
height: calc(100vh - 130rpx);
|
||||
}
|
||||
|
||||
.category-row .category-item {
|
||||
display: inline-block;
|
||||
padding: 16rpx 32rpx;
|
||||
/* 分类侧栏 */
|
||||
.category-sidebar {
|
||||
width: 160rpx;
|
||||
background: #fff;
|
||||
flex-shrink: 0;
|
||||
border-right: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.category-sidebar .category-item {
|
||||
padding: 28rpx 16rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
border-radius: 30rpx;
|
||||
margin-right: 16rpx;
|
||||
background: #f5f5f5;
|
||||
text-align: center;
|
||||
border-left: 6rpx solid transparent;
|
||||
}
|
||||
|
||||
.category-row .category-item.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
.category-sidebar .category-item.active {
|
||||
background: #f8f9fa;
|
||||
color: #667eea;
|
||||
font-weight: bold;
|
||||
border-left-color: #667eea;
|
||||
}
|
||||
|
||||
.product-scroll {
|
||||
height: calc(100vh - 260rpx);
|
||||
flex: 1;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
@@ -261,6 +299,11 @@ export default {
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.product-item.disabled {
|
||||
opacity: 0.5;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -278,6 +321,42 @@ export default {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.product-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.product-size {
|
||||
font-size: 22rpx;
|
||||
color: #667eea;
|
||||
background: #f0f0ff;
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
|
||||
.product-stock {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.stock-label {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.stock-value {
|
||||
font-size: 22rpx;
|
||||
color: #333;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
.stock-value.stock-zero {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@
|
||||
<view class="section-title">商品明细</view>
|
||||
<view class="items-list">
|
||||
<view class="item-row header">
|
||||
<text class="item-name">商品</text>
|
||||
<text class="item-info">商品信息</text>
|
||||
<text class="item-area">总面积(m²)</text>
|
||||
<text class="item-qty">数量</text>
|
||||
<text class="item-price">单价</text>
|
||||
<text class="item-subtotal">小计</text>
|
||||
@@ -60,10 +61,11 @@
|
||||
:key="index"
|
||||
class="item-row"
|
||||
>
|
||||
<text class="item-name">{{ item.productName }}</text>
|
||||
<text class="item-info"><text class="item-name-text">{{ item.productName }}</text><text class="item-spec-text">{{ item.productSpec ? ' ' + item.productSpec : '' }}</text><text class="item-dims-text">{{ item.length || '-' }}x{{ item.width || '-' }}</text></text>
|
||||
<text class="item-area">{{ calcArea(item) }}</text>
|
||||
<text class="item-qty">{{ item.quantity }}</text>
|
||||
<text class="item-price">¥{{ item.price }}</text>
|
||||
<text class="item-subtotal">¥{{ (item.price * item.quantity).toFixed(2) }}</text>
|
||||
<text class="item-subtotal">¥{{ (item.price * item.quantity).toFixed(0) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -153,6 +155,12 @@ export default {
|
||||
formatTime(time) {
|
||||
if (!time) return ''
|
||||
return time.substring(0, 16).replace('T', ' ')
|
||||
},
|
||||
calcArea(item) {
|
||||
if (item.length && item.width && item.quantity) {
|
||||
return (item.length * item.width * item.quantity / 1000000).toFixed(4)
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,9 +292,10 @@ export default {
|
||||
|
||||
.item-row {
|
||||
display: flex;
|
||||
padding: 20rpx 0;
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1rpx solid #f8f8f8;
|
||||
font-size: 26rpx;
|
||||
font-size: 24rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-row.header {
|
||||
@@ -295,12 +304,36 @@ export default {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
flex: 2;
|
||||
.item-info {
|
||||
flex: 3;
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.item-name-text {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-spec-text {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.item-dims-text {
|
||||
color: #666;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.item-area {
|
||||
flex: 1.5;
|
||||
text-align: center;
|
||||
color: #667eea;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-qty {
|
||||
flex: 1;
|
||||
flex: 0.8;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,18 @@
|
||||
@click="openQuantityPopup(item)"
|
||||
>
|
||||
<view class="product-info">
|
||||
<view class="product-header">
|
||||
<text class="product-name">{{ item.name }}</text>
|
||||
<text class="product-category" v-if="item.categoryName">{{ item.categoryName }}</text>
|
||||
</view>
|
||||
<view class="product-row">
|
||||
<text class="product-spec">{{ item.spec || '-' }}</text>
|
||||
<text class="product-unit">/{{ item.unit }}</text>
|
||||
<text class="product-price-text">¥{{ item.price }}</text>
|
||||
</view>
|
||||
<view class="product-spec-row" v-if="item.length && item.width">
|
||||
<text class="spec-text">{{ item.length }} x {{ item.width }} = {{ item.area }} m²</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="product-add">
|
||||
<text class="add-icon">+</text>
|
||||
@@ -320,11 +330,30 @@ export default {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.product-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.product-category {
|
||||
font-size: 22rpx;
|
||||
color: #667eea;
|
||||
background: #f0f4ff;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
.product-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
@@ -333,6 +362,28 @@ export default {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.product-unit {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.product-price-text {
|
||||
font-size: 26rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 500;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.product-spec-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.spec-text {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.product-add {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
|
||||
Reference in New Issue
Block a user