feat(order): 新增税率设置功能并优化订单详情页面
- 在订单详情页面添加税率输入框,支持修改产品税率 - 新增 FileType 类型用于区分文件类型- 优化订单信息展示布局,调整部分字段显示位置 - 添加文件类型前缀到文件列表显示 - 重构部分代码以支持新的税率设置功能master
parent
a29d0f589b
commit
a66959fd22
|
|
@ -46,6 +46,15 @@ export const submitApproval = (params: any): Promise<AxiosResponse<ApiResponse<a
|
||||||
formData.append(`variables[${variableKey}]`, params[key][variableKey].toString())
|
formData.append(`variables[${variableKey}]`, params[key][variableKey].toString())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else if (key === 'taxRateData' && Array.isArray(params[key])) {
|
||||||
|
// 特殊处理taxRateData数组
|
||||||
|
params[key].forEach((item: any, index: number) => {
|
||||||
|
Object.keys(item).forEach(itemKey => {
|
||||||
|
if (item[itemKey] !== undefined && item[itemKey] !== null) {
|
||||||
|
formData.append(`taxRateData[${index}].${itemKey}`, item[itemKey].toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
formData.append(key, params[key].toString())
|
formData.append(key, params[key].toString())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export interface LoginParams {
|
||||||
|
|
||||||
// 订单状态类型
|
// 订单状态类型
|
||||||
export type OrderStatus = '0' | '1' | '2' // 待审批、已审批、已拒绝
|
export type OrderStatus = '0' | '1' | '2' // 待审批、已审批、已拒绝
|
||||||
|
export type FileType = '0' | '1' | '2' // 待审批、已审批、已拒绝
|
||||||
export type PayMethod = '1-1' | '1-2' | '2-1' | '2-2' | '2-3' // 待审批、已审批、已拒绝
|
export type PayMethod = '1-1' | '1-2' | '2-1' | '2-2' | '2-3' // 待审批、已审批、已拒绝
|
||||||
|
|
||||||
// 审批状态类型
|
// 审批状态类型
|
||||||
|
|
|
||||||
|
|
@ -135,24 +135,17 @@
|
||||||
</div><div class="info-item">
|
</div><div class="info-item">
|
||||||
<span class="label">联系方式</span>
|
<span class="label">联系方式</span>
|
||||||
<span class="value">{{ currentOrderInfo.notifierPhone}}</span>
|
<span class="value">{{ currentOrderInfo.notifierPhone}}</span>
|
||||||
</div><div class="info-item">
|
|
||||||
<span class="label">其他特别说明</span>
|
|
||||||
<span class="value">{{ currentOrderInfo.remark}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="label">付款方式</span>
|
|
||||||
<span class="value">{{ getPayMethod(currentOrderInfo.paymentMethod) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="label">付款比例</span>
|
|
||||||
<span class="value">{{ currentOrderInfo.paymentRatio }}%</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">付款说明</span>
|
<span class="label">付款说明</span>
|
||||||
<span class="value">{{ currentOrderInfo.paymentDescription }}</span>
|
<span class="value">{{ currentOrderInfo.paymentDescription }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">其他特别说明</span>
|
||||||
|
<span class="value">{{ currentOrderInfo.remark }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -192,6 +185,18 @@
|
||||||
<span class="product-label">单价</span>
|
<span class="product-label">单价</span>
|
||||||
<span class="product-value">{{ formatAmount(product.price) }}</span>
|
<span class="product-value">{{ formatAmount(product.price) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">税率</span>
|
||||||
|
<span class="product-value">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="product.taxRate"
|
||||||
|
:disabled="!isBusinessApproval"
|
||||||
|
@change="updateTaxRate(product)"
|
||||||
|
class="tax-rate-input"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -225,6 +230,18 @@
|
||||||
<span class="product-label">单价</span>
|
<span class="product-label">单价</span>
|
||||||
<span class="product-value">{{ formatAmount(product.price) }}</span>
|
<span class="product-value">{{ formatAmount(product.price) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">税率</span>
|
||||||
|
<span class="product-value">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="product.taxRate"
|
||||||
|
:disabled="!isBusinessApproval"
|
||||||
|
@change="updateTaxRate(product)"
|
||||||
|
class="tax-rate-input"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -258,6 +275,18 @@
|
||||||
<span class="product-label">单价</span>
|
<span class="product-label">单价</span>
|
||||||
<span class="product-value">{{ formatAmount(product.price) }}</span>
|
<span class="product-value">{{ formatAmount(product.price) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="product-row">
|
||||||
|
<span class="product-label">税率</span>
|
||||||
|
<span class="product-value">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="product.taxRate"
|
||||||
|
:disabled="!isBusinessApproval"
|
||||||
|
@change="updateTaxRate(product)"
|
||||||
|
class="tax-rate-input"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -301,7 +330,7 @@
|
||||||
<van-icon name="description"/>
|
<van-icon name="description"/>
|
||||||
<div class="file-info">
|
<div class="file-info">
|
||||||
<div class="file-name">{{ file.fileName }}</div>
|
<div class="file-name">{{ file.fileName }}</div>
|
||||||
<div class="file-meta">{{ file.uploadUserName }} · {{ formatDate(file.uploadTime) }}</div>
|
<div class="file-meta">{{getFileType(file.fileSort)}}.{{ file.uploadUserName }} · {{ formatDate(file.uploadTime) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<van-icon name="arrow"/>
|
<van-icon name="arrow"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -465,7 +494,7 @@ import {
|
||||||
getApprovalStatusColor,
|
getApprovalStatusColor,
|
||||||
getFilePreviewUrl
|
getFilePreviewUrl
|
||||||
} from '@/utils'
|
} from '@/utils'
|
||||||
import type {OrderStatus, ApprovalStatus, AttachmentFile, payMethod, PayMethod} from '@/types'
|
import type {OrderStatus, ApprovalStatus, AttachmentFile, payMethod, PayMethod, FileType} from '@/types'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
@ -479,6 +508,11 @@ const currentApprovalStatus = ref<ApprovalStatus>(3)
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
const selectedTag = ref('')
|
const selectedTag = ref('')
|
||||||
const selectedDiscount = ref('') // 现金折扣率选择
|
const selectedDiscount = ref('') // 现金折扣率选择
|
||||||
|
const taxRateData = ref<any[]>([])
|
||||||
|
|
||||||
|
const isBusinessApproval = computed(() => {
|
||||||
|
return currentOrder.value?.todo?.taskName?.startsWith('商务')
|
||||||
|
})
|
||||||
|
|
||||||
// Tab页签
|
// Tab页签
|
||||||
const activeTab = ref('order')
|
const activeTab = ref('order')
|
||||||
|
|
@ -517,7 +551,14 @@ const getPayMethod= (method: PayMethod) => {
|
||||||
}
|
}
|
||||||
return methodMap[method] || ''
|
return methodMap[method] || ''
|
||||||
}
|
}
|
||||||
|
const getFileType = (type: FileType) => {
|
||||||
|
const fileTypeMap = {
|
||||||
|
'0': '商务折扣审批',
|
||||||
|
'1': '合同',
|
||||||
|
'2': '补充附件'
|
||||||
|
}
|
||||||
|
return fileTypeMap[type] || '补充附件'
|
||||||
|
}
|
||||||
// 获取步骤图标
|
// 获取步骤图标
|
||||||
const getStepIcon = (status?: ApprovalStatus) => {
|
const getStepIcon = (status?: ApprovalStatus) => {
|
||||||
if (status === undefined || status === null) return 'clock'
|
if (status === undefined || status === null) return 'clock'
|
||||||
|
|
@ -620,26 +661,56 @@ const getOpinionTags = () => {
|
||||||
if (currentApprovalStatus.value === 0) {
|
if (currentApprovalStatus.value === 0) {
|
||||||
// 驳回常用意见
|
// 驳回常用意见
|
||||||
return [
|
return [
|
||||||
'资料不齐全,请补充相关文件',
|
'经审查有问题,驳回'
|
||||||
'订单金额需要重新核实',
|
|
||||||
'客户信息有误,请修正',
|
|
||||||
'产品配置不符合要求',
|
|
||||||
'需要提供更多技术细节',
|
|
||||||
'合同条款需要调整'
|
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
// 通过常用意见
|
// 通过常用意见
|
||||||
return [
|
return [
|
||||||
'审核通过,订单信息完整',
|
'所有信息已阅,审核通过'
|
||||||
'符合公司政策,同意执行',
|
|
||||||
'客户资质良好,建议通过',
|
|
||||||
'产品配置合理,批准发货',
|
|
||||||
'风险可控,同意此订单',
|
|
||||||
'经审查无误,予以批准'
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initializeTaxRates = () => {
|
||||||
|
if (!currentOrderInfo.value) return;
|
||||||
|
|
||||||
|
const allProducts = [
|
||||||
|
...(currentOrderInfo.value.softwareProjectProductInfoList || []),
|
||||||
|
...(currentOrderInfo.value.hardwareProjectProductInfoList || []),
|
||||||
|
...(currentOrderInfo.value.maintenanceProjectProductInfoList || [])
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize taxRate on product objects for v-model
|
||||||
|
if (currentOrderInfo.value.softwareProjectProductInfoList) {
|
||||||
|
currentOrderInfo.value.softwareProjectProductInfoList.forEach(p => p.taxRate = p.taxRate ?? null);
|
||||||
|
}
|
||||||
|
if (currentOrderInfo.value.hardwareProjectProductInfoList) {
|
||||||
|
currentOrderInfo.value.hardwareProjectProductInfoList.forEach(p => p.taxRate = p.taxRate ?? null);
|
||||||
|
}
|
||||||
|
if (currentOrderInfo.value.maintenanceProjectProductInfoList) {
|
||||||
|
currentOrderInfo.value.maintenanceProjectProductInfoList.forEach(p => p.taxRate = p.taxRate ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
taxRateData.value = allProducts.map(p => ({
|
||||||
|
productId: p.id,
|
||||||
|
projectId: currentOrderInfo.value.projectId,
|
||||||
|
taxRate: p.taxRate
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateTaxRate = (product: any) => {
|
||||||
|
const existing = taxRateData.value.find(item => item.productId === product.id);
|
||||||
|
if (existing) {
|
||||||
|
existing.taxRate = product.taxRate;
|
||||||
|
} else if (currentOrderInfo.value) {
|
||||||
|
taxRateData.value.push({
|
||||||
|
productId: product.id,
|
||||||
|
projectId: currentOrderInfo.value.projectId,
|
||||||
|
taxRate: product.taxRate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 选择标签
|
// 选择标签
|
||||||
const selectTag = (tag: string) => {
|
const selectTag = (tag: string) => {
|
||||||
if (selectedTag.value === tag) {
|
if (selectedTag.value === tag) {
|
||||||
|
|
@ -686,6 +757,7 @@ const submitApproval = async () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
|
taxRateData,
|
||||||
...currentOrder.value.todo, // 展开todo中的所有参数
|
...currentOrder.value.todo, // 展开todo中的所有参数
|
||||||
|
|
||||||
approveOpinion: opinion || undefined, // 添加审批意见
|
approveOpinion: opinion || undefined, // 添加审批意见
|
||||||
|
|
@ -701,6 +773,11 @@ const submitApproval = async () => {
|
||||||
params.allPriceCountValue = selectedDiscount.value
|
params.allPriceCountValue = selectedDiscount.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果是商务审批,则添加税率数据
|
||||||
|
if (isBusinessApproval.value && taxRateData.value.length > 0) {
|
||||||
|
params.taxRateData = taxRateData.value;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('提交审批参数:', params)
|
console.log('提交审批参数:', params)
|
||||||
|
|
||||||
await submitApprovalApi(params)
|
await submitApprovalApi(params)
|
||||||
|
|
@ -733,6 +810,7 @@ onMounted(async () => {
|
||||||
console.log('获取订单详情结果:', result)
|
console.log('获取订单详情结果:', result)
|
||||||
console.log('当前订单信息:', currentOrder.value)
|
console.log('当前订单信息:', currentOrder.value)
|
||||||
console.log('当前订单基本信息:', currentOrderInfo.value)
|
console.log('当前订单基本信息:', currentOrderInfo.value)
|
||||||
|
initializeTaxRates()
|
||||||
|
|
||||||
// 检查订单详情中是否包含现金折扣率值
|
// 检查订单详情中是否包含现金折扣率值
|
||||||
if (currentOrder.value && currentOrder.value.projectOrderInfo) {
|
if (currentOrder.value && currentOrder.value.projectOrderInfo) {
|
||||||
|
|
@ -1209,6 +1287,29 @@ onMounted(async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tax-rate-input {
|
||||||
|
width: 80px;
|
||||||
|
text-align: right;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-color-primary);
|
||||||
|
background-color: var(--background-color-primary);
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: var(--background-color-secondary);
|
||||||
|
color: var(--text-color-tertiary);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.discount-options {
|
.discount-options {
|
||||||
:deep(.van-radio-group) {
|
:deep(.van-radio-group) {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default defineConfig({
|
||||||
open: true,
|
open: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://192.168.2.134:28080',
|
target: 'http://localhost:28080',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue