Files
crm_uiapp/src/pages-erp/sale-order/form/index.vue

424 lines
12 KiB
Vue
Raw Normal View History

<template>
<view class="yd-page-container erp-page">
<wd-navbar
:title="getTitle"
left-arrow
placeholder
safe-area-inset-top
fixed
@click-left="handleBack"
/>
<view class="erp-form-body">
<wd-form ref="formRef" :model="formData" :rules="formRules">
<wd-cell-group title="基本信息" border>
<wd-picker
v-model="formData.customerId"
label="客户"
label-width="180rpx"
prop="customerId"
:columns="customerColumns"
placeholder="请选择"
/>
<wd-datetime-picker
v-model="formData.orderTime"
type="date"
label="订单时间"
label-width="180rpx"
placeholder="请选择"
/>
<wd-picker
v-model="formData.saleUserId"
label="销售人员"
label-width="180rpx"
:columns="userColumns"
placeholder="请选择"
/>
</wd-cell-group>
<wd-cell-group title="付款信息" border>
<wd-picker
v-model="formData.paymentCondition"
label="付款条件"
label-width="180rpx"
:columns="paymentConditionColumns"
placeholder="请选择"
/>
<wd-picker
v-model="formData.paymentMethod"
label="付款方式"
label-width="180rpx"
:columns="paymentMethodColumns"
placeholder="请选择"
/>
<wd-picker
v-model="formData.accountId"
label="结算账户"
label-width="180rpx"
:columns="accountColumns"
placeholder="请选择"
/>
<wd-input
v-model="formData.depositPrice"
label="收取订金"
label-width="180rpx"
placeholder="请输入订金"
type="number"
clearable
/>
</wd-cell-group>
<wd-cell-group title="订单产品" border>
<view class="erp-form-item-list">
<view
v-for="(item, index) in formData.items"
:key="index"
class="erp-form-item-card erp-form-item-card--padded"
>
<view class="mb-12rpx flex items-center justify-between">
<text class="erp-card-title">{{ item.productName || `产品${index + 1}` }}</text>
<wd-button size="small" type="error" plain @click="removeItem(index)">删除</wd-button>
</view>
<view class="erp-form-input-row">
<text class="erp-form-input-label">数量</text>
<wd-input
v-model="item.count"
type="number"
placeholder="数量"
clearable
@change="calculateTotal"
/>
</view>
<view class="erp-form-input-row">
<text class="erp-form-input-label">单价</text>
<wd-input
v-model="item.unitPrice"
type="number"
placeholder="单价"
clearable
@change="calculateTotal"
/>
</view>
<view class="erp-form-total-row">
<text class="erp-form-input-label">小计</text>
<text class="text-28rpx text-[#409eff] font-semibold">{{ formatPrice(calculateItemTotal(item)) }}</text>
</view>
</view>
<wd-button type="primary" plain block @click="showProductPicker = true">添加产品</wd-button>
</view>
</wd-cell-group>
<wd-cell-group title="优惠信息" border>
<wd-input
v-model="formData.discountPercent"
label="优惠率(%)"
label-width="180rpx"
placeholder="请输入优惠率"
type="number"
clearable
@change="calculateTotal"
/>
<wd-input
:model-value="formatPrice(formData.discountPrice || 0)"
label="收款优惠"
label-width="180rpx"
placeholder="自动计算"
disabled
/>
<wd-input
:model-value="formatPrice(formData.totalPrice || 0)"
label="优惠后金额"
label-width="180rpx"
placeholder="自动计算"
disabled
/>
</wd-cell-group>
<wd-cell-group title="其他信息" border>
<wd-textarea
v-model="formData.remark"
label="备注"
label-width="180rpx"
placeholder="请输入备注"
:maxlength="200"
show-word-limit
clearable
/>
</wd-cell-group>
</wd-form>
</view>
<view class="yd-detail-footer">
<wd-button type="primary" block :loading="formLoading" @click="handleSubmit">
保存
</wd-button>
</view>
<wd-popup v-model="showProductPicker" position="bottom" custom-style="height: 60%">
<view class="erp-form-item-list">
<view class="mb-24rpx flex items-center justify-between">
<text class="text-32rpx font-semibold">选择产品</text>
<wd-button size="small" @click="showProductPicker = false">关闭</wd-button>
</view>
<scroll-view scroll-y class="product-picker-list">
<view
v-for="product in productList"
:key="product.id"
class="product-picker-item"
:class="{ 'is-active': selectedProductId === product.id }"
@click="selectedProductId = product.id"
>
<view class="product-picker-item__main">
<text class="product-picker-item__name">{{ product.name }}</text>
<text class="product-picker-item__price">¥{{ formatPrice(product.salePrice) }}</text>
</view>
</view>
</scroll-view>
<wd-button type="primary" block class="mt-24rpx" @click="addProduct">确定添加</wd-button>
</view>
</wd-popup>
</view>
</template>
<script lang="ts" setup>
import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
import type { SaleOrder, SaleOrderItem } from '@/api/erp/sale-order'
import { computed, onMounted, ref } from 'vue'
import { useToast } from 'wot-design-uni'
import { createSaleOrder, getSaleOrder, updateSaleOrder } from '@/api/erp/sale-order'
import { getProductSimpleList } from '@/api/erp/product'
import { getCustomerSimpleList } from '@/api/erp/customer'
import { getSimpleUserList } from '@/api/system/user'
import { getAccountSimpleList } from '@/api/erp/account'
import { navigateBackPlus } from '@/utils'
const props = defineProps<{
id?: number | any
}>()
definePage({
style: {
navigationBarTitleText: '',
navigationStyle: 'custom',
},
})
const toast = useToast()
const getTitle = computed(() => (props.id ? '编辑订单' : '新增订单'))
const formLoading = ref(false)
const formRef = ref<FormInstance>()
const customerList = ref<any[]>([])
const userList = ref<any[]>([])
const productList = ref<any[]>([])
const accountList = ref<any[]>([])
const showProductPicker = ref(false)
const selectedProductId = ref<number>()
const formData = ref<SaleOrder>({
id: undefined,
customerId: undefined,
orderTime: Date.now(),
saleUserId: undefined,
paymentCondition: undefined,
paymentMethod: undefined,
accountId: undefined,
depositPrice: undefined,
discountPercent: undefined,
discountPrice: 0,
totalPrice: 0,
remark: undefined,
items: [],
})
const formRules = {
customerId: [{ required: true, message: '请选择客户' }],
}
const customerColumns = computed(() => customerList.value.map(v => ({ label: v.name, value: v.id })))
const userColumns = computed(() => userList.value.map(v => ({ label: v.nickname, value: v.id })))
const accountColumns = computed(() => accountList.value.map(v => ({ label: v.name, value: v.id })))
const paymentConditionOptions = [
{ label: '款到发货', value: 1 },
{ label: '货到付款', value: 2 },
{ label: '月结', value: 3 },
]
const paymentConditionColumns = computed(() => paymentConditionOptions.map(v => ({ label: v.label, value: v.value })))
const paymentMethodOptions = [
{ label: '现金', value: 1 },
{ label: '银行转账', value: 2 },
{ label: '微信支付', value: 3 },
{ label: '支付宝', value: 4 },
]
const paymentMethodColumns = computed(() => paymentMethodOptions.map(v => ({ label: v.label, value: v.value })))
function formatPrice(price?: number) {
if (price === undefined || price === null) return '0.00'
return Number(price).toFixed(2)
}
function calculateItemTotal(item: SaleOrderItem) {
const count = Number(item.count) || 0
const unitPrice = Number(item.unitPrice) || 0
return count * unitPrice
}
function calculateTotal() {
if (!formData.value.items || formData.value.items.length === 0) {
formData.value.totalPrice = 0
formData.value.discountPrice = 0
return
}
const total = formData.value.items.reduce((sum, item) => sum + calculateItemTotal(item), 0)
const discountPercent = Number(formData.value.discountPercent) || 0
formData.value.discountPrice = total * (discountPercent / 100)
formData.value.totalPrice = total - formData.value.discountPrice
}
function addProduct() {
const product = productList.value.find(p => p.id === selectedProductId.value)
if (!product) {
toast.warning('请选择产品')
return
}
const newItem: SaleOrderItem = {
productId: product.id,
productName: product.name,
count: 1,
unitPrice: product.salePrice || 0,
totalPrice: product.salePrice || 0,
}
formData.value.items = [...(formData.value.items || []), newItem]
selectedProductId.value = undefined
showProductPicker.value = false
calculateTotal()
}
function removeItem(index: number) {
formData.value.items?.splice(index, 1)
calculateTotal()
}
function handleBack() {
navigateBackPlus('/pages-erp/sale-order/index')
}
async function getDetail() {
if (!props.id) return
try {
toast.loading('加载中...')
formData.value = await getSaleOrder(props.id)
calculateTotal()
} finally {
toast.close()
}
}
async function loadOptions() {
const [customersResult, usersResult, productsResult, accountsResult] = await Promise.allSettled([
getCustomerSimpleList(),
getSimpleUserList(),
getProductSimpleList(),
getAccountSimpleList(),
])
customerList.value = customersResult.status === 'fulfilled' ? (customersResult.value || []) : []
userList.value = usersResult.status === 'fulfilled' ? (usersResult.value || []) : []
productList.value = productsResult.status === 'fulfilled' ? (productsResult.value || []) : []
accountList.value = accountsResult.status === 'fulfilled' ? (accountsResult.value || []) : []
if ([customersResult, usersResult, productsResult, accountsResult].some(result => result.status === 'rejected')) {
console.error('加载选项失败', {
customersResult,
usersResult,
productsResult,
accountsResult,
})
}
}
async function handleSubmit() {
const { valid } = await formRef.value!.validate()
if (!valid) return
if (!formData.value.items || formData.value.items.length === 0) {
toast.warning('请添加订单产品')
return
}
formLoading.value = true
try {
const submitData = { ...formData.value }
if (submitData.orderTime) {
submitData.orderTime = new Date(submitData.orderTime).getTime()
}
submitData.items = (submitData.items || []).map(item => ({
...item,
totalPrice: calculateItemTotal(item),
}))
if (props.id) {
await updateSaleOrder(submitData)
toast.success('修改成功')
} else {
await createSaleOrder(submitData)
toast.success('新增成功')
}
setTimeout(() => {
handleBack()
}, 500)
} finally {
formLoading.value = false
}
}
onMounted(async () => {
await loadOptions()
await getDetail()
})
</script>
<style lang="scss" scoped>
.product-picker-list {
max-height: 720rpx;
}
.product-picker-item {
padding: 24rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.product-picker-item.is-active {
background: #f0f7ff;
}
.product-picker-item__main {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24rpx;
}
.product-picker-item__name {
flex: 1;
font-size: 28rpx;
color: #333;
}
.product-picker-item__price {
flex-shrink: 0;
font-size: 24rpx;
color: #999;
}
</style>