feat(order): 新增税率设置功能并优化订单详情页面

- 在订单详情页面添加税率输入框,支持修改产品税率
- 新增 FileType 类型用于区分文件类型- 优化订单信息展示布局,调整部分字段显示位置
- 添加文件类型前缀到文件列表显示
- 重构部分代码以支持新的税率设置功能
master
chenhao 2025-09-19 18:46:50 +08:00
parent a29d0f589b
commit a66959fd22
4 changed files with 139 additions and 28 deletions

View File

@ -46,6 +46,15 @@ export const submitApproval = (params: any): Promise<AxiosResponse<ApiResponse<a
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 {
formData.append(key, params[key].toString())
}

View File

@ -15,6 +15,7 @@ export interface LoginParams {
// 订单状态类型
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' // 待审批、已审批、已拒绝
// 审批状态类型

View File

@ -135,24 +135,17 @@
</div><div class="info-item">
<span class="label">联系方式</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 class="info-item">
<span class="label">付款说明</span>
<span class="value">{{ currentOrderInfo.paymentDescription }}</span>
</div>
<div class="info-item">
<span class="label">其他特别说明</span>
<span class="value">{{ currentOrderInfo.remark }}</span>
</div>
</div>
</div>
</div>
@ -192,6 +185,18 @@
<span class="product-label">单价</span>
<span class="product-value">{{ formatAmount(product.price) }}</span>
</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>
@ -225,6 +230,18 @@
<span class="product-label">单价</span>
<span class="product-value">{{ formatAmount(product.price) }}</span>
</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>
@ -258,6 +275,18 @@
<span class="product-label">单价</span>
<span class="product-value">{{ formatAmount(product.price) }}</span>
</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>
@ -301,7 +330,7 @@
<van-icon name="description"/>
<div class="file-info">
<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>
<van-icon name="arrow"/>
</div>
@ -465,7 +494,7 @@ import {
getApprovalStatusColor,
getFilePreviewUrl
} 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 router = useRouter()
@ -479,6 +508,11 @@ const currentApprovalStatus = ref<ApprovalStatus>(3)
const submitting = ref(false)
const selectedTag = ref('')
const selectedDiscount = ref('') //
const taxRateData = ref<any[]>([])
const isBusinessApproval = computed(() => {
return currentOrder.value?.todo?.taskName?.startsWith('商务')
})
// Tab
const activeTab = ref('order')
@ -517,7 +551,14 @@ const getPayMethod= (method: PayMethod) => {
}
return methodMap[method] || ''
}
const getFileType = (type: FileType) => {
const fileTypeMap = {
'0': '商务折扣审批',
'1': '合同',
'2': '补充附件'
}
return fileTypeMap[type] || '补充附件'
}
//
const getStepIcon = (status?: ApprovalStatus) => {
if (status === undefined || status === null) return 'clock'
@ -620,26 +661,56 @@ const getOpinionTags = () => {
if (currentApprovalStatus.value === 0) {
//
return [
'资料不齐全,请补充相关文件',
'订单金额需要重新核实',
'客户信息有误,请修正',
'产品配置不符合要求',
'需要提供更多技术细节',
'合同条款需要调整'
'经审查有问题,驳回'
]
} else {
//
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) => {
if (selectedTag.value === tag) {
@ -686,6 +757,7 @@ const submitApproval = async () => {
try {
const params = {
taxRateData,
...currentOrder.value.todo, // todo
approveOpinion: opinion || undefined, //
@ -701,6 +773,11 @@ const submitApproval = async () => {
params.allPriceCountValue = selectedDiscount.value
}
//
if (isBusinessApproval.value && taxRateData.value.length > 0) {
params.taxRateData = taxRateData.value;
}
console.log('提交审批参数:', params)
await submitApprovalApi(params)
@ -733,6 +810,7 @@ onMounted(async () => {
console.log('获取订单详情结果:', result)
console.log('当前订单信息:', currentOrder.value)
console.log('当前订单基本信息:', currentOrderInfo.value)
initializeTaxRates()
//
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 {
:deep(.van-radio-group) {
display: flex;

View File

@ -27,7 +27,7 @@ export default defineConfig({
open: true,
proxy: {
'/api': {
target: 'http://192.168.2.134:28080',
target: 'http://localhost:28080',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api/, ''),