Files
todo-frontend/src/pages/order/create.vue
Agent 161a5236e4
All checks were successful
continuous-integration/drone/push Build is passing
fix: 订单列表默认加载全部订单,不再默认筛选未完成
2026-03-27 15:51:14 +00:00

630 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page">
<!-- 客户信息 -->
<view class="section card-section">
<view class="section-header">
<text class="section-icon">👤</text>
<text class="section-title">客户信息</text>
</view>
<view class="form-item">
<text class="label">客户</text>
<picker
mode="selector"
:range="customers"
range-key="name"
@change="selectCustomer"
>
<view class="picker-value">
{{ selectedCustomer ? selectedCustomer.name : '请选择客户' }}
<text class="arrow"></text>
</view>
</picker>
</view>
</view>
<!-- 商品选择 -->
<view class="section card-section">
<view class="section-header">
<text class="section-icon">📦</text>
<text class="section-title">商品明细</text>
<text class="add-btn" @click="goToSelectProduct">+ 添加商品</text>
</view>
<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>
</view>
<view class="item-edit">
<view class="quantity-edit">
<text class="qty-label">数量</text>
<input
class="qty-input"
type="number"
v-model="item.quantity"
@change="calcAmount"
/>
</view>
<view class="price-edit">
<text class="qty-label">单价</text>
<input
class="price-input"
type="digit"
v-model="item.price"
@change="calcAmount"
/>
</view>
<text class="item-subtotal">¥{{ (item.price * item.quantity).toFixed(2) }}</text>
<text class="delete-btn" @click="removeItem(index)">×</text>
</view>
</view>
<view v-if="orderItems.length === 0" class="empty-tip">
<text class="empty-icon">🛒</text>
<text>请添加商品</text>
</view>
</view>
<!-- 优惠设置 -->
<view class="section card-section">
<view class="section-header">
<text class="section-icon">🎫</text>
<text class="section-title">优惠设置</text>
</view>
<view class="form-item">
<text class="label">优惠金额</text>
<input
class="discount-input"
type="digit"
v-model="discountMoney"
@input="calcAmount"
/>
<text class="unit"></text>
</view>
</view>
<!-- 订单金额 -->
<view class="section amount-section">
<view class="amount-header">
<text class="amount-icon">💰</text>
<text class="amount-title">订单金额</text>
</view>
<view class="amount-row">
<text class="amount-label">原价合计</text>
<text class="amount-value">¥{{ totalAmount.toFixed(2) }}</text>
</view>
<view class="amount-row">
<text class="amount-label">优惠金额</text>
<text class="amount-value discount">-¥{{ discountAmount.toFixed(2) }}</text>
</view>
<view class="amount-row actual">
<text class="amount-label">实付金额</text>
<text class="amount-value">¥{{ actualAmount.toFixed(2) }}</text>
</view>
</view>
<!-- 支付方式 -->
<view class="section card-section">
<view class="section-header">
<text class="section-icon">💳</text>
<text class="section-title">支付方式</text>
</view>
<view class="payment-methods">
<view
class="method-item"
:class="{ active: paymentMethod === 'cash' }"
@click="paymentMethod = 'cash'"
>
💵 现金
</view>
<view
class="method-item"
:class="{ active: paymentMethod === 'wechat' }"
@click="paymentMethod = 'wechat'"
>
💬 微信
</view>
<view
class="method-item"
:class="{ active: paymentMethod === 'alipay' }"
@click="paymentMethod = 'alipay'"
>
💰 支付宝
</view>
</view>
</view>
<!-- 备注 -->
<view class="section card-section">
<view class="section-header">
<text class="section-icon">📝</text>
<text class="section-title">备注</text>
</view>
<textarea
class="remark-input"
v-model="remark"
placeholder="请输入备注信息"
/>
</view>
<!-- 提交按钮 -->
<view class="submit-bar">
<view class="submit-info">
<text>实付: </text>
<text class="submit-amount">¥{{ actualAmount.toFixed(2) }}</text>
</view>
<button class="submit-btn" @click="createOrder">创建订单</button>
</view>
</view>
</template>
<script>
import orderApi from '@/api/order'
import productApi from '@/api/product'
import customerApi from '@/api/customer'
export default {
data() {
return {
// 客户相关
customers: [],
selectedCustomer: null,
// 商品相关
orderItems: [],
productList: [],
searchKeyword: '',
// 金额相关
discountRate: 100, // 折扣率保留逻辑默认100%不打折)
discountMoney: 0, // 优惠金额
totalAmount: 0, // 原价
discountAmount: 0, // 优惠金额(计算结果)
actualAmount: 0, // 实付金额
// 其他
paymentMethod: 'cash',
remark: '',
// 编辑模式
editingOrderId: null
}
},
onLoad(options) {
this.loadCustomers()
this.loadProducts()
if (options.orderId) {
this.editingOrderId = options.orderId
this.loadOrder(options.orderId)
}
},
methods: {
async loadOrder(orderId) {
try {
const detail = await orderApi.getOrderDetail(orderId)
const order = detail.order
// 设置订单信息
if (order.customerId) {
this.selectedCustomer = this.customers.find(c => c.customerId === order.customerId)
}
this.orderItems = (detail.items || []).map(item => ({
productId: item.productId,
productName: item.productName,
spec: item.productSpec,
unit: item.unit,
price: item.price,
quantity: item.quantity
}))
this.discountRate = order.discountRate
this.remark = order.remark || ''
this.paymentMethod = order.paymentMethod || 'cash'
this.calcAmount()
} catch (e) {
console.error(e)
}
},
async loadCustomers() {
try {
const res = await customerApi.getCustomers({ page: 1, pageSize: 100 })
this.customers = res.records || []
} catch (e) {
console.error(e)
}
},
async loadProducts() {
try {
const res = await productApi.getProducts({ page: 1, pageSize: 100 })
this.productList = res.records || []
} catch (e) {
console.error(e)
}
},
selectCustomer(e) {
this.selectedCustomer = this.customers[e.detail.value]
},
goToSelectProduct() {
uni.navigateTo({ url: '/pages/product/select' })
},
addProduct(product) {
// 检查是否已添加
const exists = this.orderItems.find(item => item.productId === product.productId)
if (exists) {
uni.showToast({ title: '商品已添加', icon: 'none' })
return
}
this.orderItems.push({
productId: product.productId,
productName: product.name,
spec: product.spec,
unit: product.unit,
price: product.price,
quantity: 1
})
this.calcAmount()
},
removeItem(index) {
this.orderItems.splice(index, 1)
this.calcAmount()
},
// 核心金额计算逻辑
calcAmount() {
// 1. 计算原价 = Σ(单价 × 数量)
this.totalAmount = this.orderItems.reduce((sum, item) => {
return sum + (item.price * item.quantity)
}, 0)
// 2. 优惠金额直接使用用户输入的值
this.discountAmount = parseFloat(this.discountMoney) || 0
// 3. 计算实付金额 = 原价 - 优惠金额
this.actualAmount = this.totalAmount - this.discountAmount
},
async createOrder() {
if (this.orderItems.length === 0) {
uni.showToast({ title: '请添加商品', icon: 'none' })
return
}
// 校验:客户必须选择
if (!this.selectedCustomer) {
uni.showToast({ title: '请选择客户', icon: 'none' })
return
}
// 校验实付金额必须大于0
if (this.actualAmount <= 0) {
uni.showToast({ title: '实付金额必须大于0', icon: 'none' })
return
}
const data = {
customerId: this.selectedCustomer ? this.selectedCustomer.customerId : null,
items: this.orderItems.map(item => ({
productId: item.productId,
quantity: item.quantity,
price: item.price
})),
discountRate: 100, // 保留折扣率逻辑默认100%
discountMoney: parseFloat(this.discountMoney) || 0,
remark: this.remark,
paymentMethod: this.paymentMethod
}
try {
let order
if (this.editingOrderId) {
order = await orderApi.updateOrder(this.editingOrderId, data)
uni.showToast({ title: '订单更新成功', icon: 'success' })
} else {
order = await orderApi.createOrder(data)
uni.showToast({ title: '订单创建成功', icon: 'success' })
}
// 跳转到订单列表
setTimeout(() => {
uni.switchTab({
url: '/pages/order/list'
})
}, 1500)
} catch (e) {
console.error(e)
}
}
}
}
</script>
<style>
/* 全局 */
.page {
padding: 20rpx;
padding-bottom: 180rpx;
background: #f8f9fa;
}
/* 卡片区块 */
.card-section {
background: #fff;
border-radius: 20rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
}
.section-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.section-icon {
font-size: 32rpx;
margin-right: 12rpx;
}
.section-title {
font-size: 28rpx;
font-weight: bold;
flex: 1;
}
.add-btn {
color: #667eea;
font-size: 26rpx;
font-weight: 500;
}
.form-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.label {
width: 140rpx;
font-size: 28rpx;
color: #666;
}
.picker-value {
flex: 1;
font-size: 28rpx;
color: #333;
display: flex;
justify-content: space-between;
align-items: center;
}
.arrow {
font-size: 32rpx;
color: #ccc;
}
.discount-input {
width: 120rpx;
text-align: right;
font-size: 28rpx;
padding: 10rpx;
background: #f5f5f5;
border-radius: 8rpx;
}
.unit {
font-size: 28rpx;
color: #666;
margin-left: 10rpx;
}
/* 订单商品项 */
.order-item {
padding: 20rpx;
background: #f9f9f9;
border-radius: 12rpx;
margin-bottom: 16rpx;
}
.item-info {
margin-bottom: 16rpx;
}
.item-name {
font-size: 28rpx;
font-weight: bold;
display: block;
color: #333;
}
.item-spec {
font-size: 24rpx;
color: #999;
}
.item-edit {
display: flex;
align-items: center;
}
.quantity-edit, .price-edit {
display: flex;
align-items: center;
margin-right: 20rpx;
}
.qty-label {
font-size: 24rpx;
color: #666;
margin-right: 8rpx;
}
.qty-input, .price-input {
width: 100rpx;
height: 56rpx;
background: #fff;
border: 1rpx solid #ddd;
border-radius: 8rpx;
text-align: center;
font-size: 24rpx;
}
.item-subtotal {
flex: 1;
text-align: right;
font-size: 28rpx;
font-weight: bold;
color: #ff4d4f;
}
.delete-btn {
margin-left: 20rpx;
font-size: 40rpx;
color: #999;
padding: 10rpx;
}
.empty-tip {
padding: 40rpx;
text-align: center;
color: #999;
display: flex;
flex-direction: column;
align-items: center;
}
.empty-icon {
font-size: 60rpx;
margin-bottom: 10rpx;
}
/* 金额区域 */
.amount-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.amount-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.amount-icon {
font-size: 32rpx;
margin-right: 12rpx;
}
.amount-title {
font-size: 28rpx;
font-weight: bold;
color: #fff;
}
.amount-row {
display: flex;
justify-content: space-between;
padding: 12rpx 0;
}
.amount-label {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
}
.amount-value {
font-size: 26rpx;
color: #fff;
}
.amount-value.discount {
color: #7dff7d;
}
.amount-row.actual {
border-top: 1rpx solid rgba(255, 255, 255, 0.2);
padding-top: 20rpx;
margin-top: 12rpx;
}
.amount-row.actual .amount-value {
font-size: 36rpx;
font-weight: bold;
}
/* 支付方式 */
.payment-methods {
display: flex;
gap: 20rpx;
}
.method-item {
flex: 1;
padding: 24rpx;
text-align: center;
background: #f5f5f5;
border-radius: 12rpx;
font-size: 26rpx;
transition: all 0.3s;
}
.method-item.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.4);
}
/* 备注 */
.remark-input {
width: 100%;
height: 120rpx;
font-size: 28rpx;
background: #f9f9f9;
border-radius: 12rpx;
padding: 20rpx;
box-sizing: border-box;
}
/* 底部提交 */
.submit-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
padding: 20rpx 30rpx;
background: #fff;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
}
.submit-info {
flex: 1;
font-size: 28rpx;
}
.submit-amount {
font-size: 40rpx;
font-weight: bold;
color: #ff4d4f;
}
.submit-btn {
width: 240rpx;
height: 88rpx;
line-height: 88rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
border: none;
border-radius: 44rpx;
font-size: 30rpx;
font-weight: bold;
box-shadow: 0 8rpx 30rpx rgba(102, 126, 234, 0.4);
}
</style>