|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="test-cost-container">
|
|
|
|
|
|
<el-card class="table-card">
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
|
<span>测试化验加工费明细</span>
|
|
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
|
<span class="text-sm" style="white-space: nowrap;">行数:</span>
|
|
|
|
|
|
<el-input-number
|
|
|
|
|
|
v-model="addRowCount"
|
|
|
|
|
|
:min="1"
|
|
|
|
|
|
:max="10"
|
|
|
|
|
|
:step="1"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<el-button type="primary" size="small" @click="addRow">添加</el-button>
|
|
|
|
|
|
<el-button size="small" @click="deleteSelectedRows" :disabled="selectedRows.length === 0">删除</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<el-table :data="testData" border style="width: 100%" row-key="id" @selection-change="handleSelectionChange">
|
|
|
|
|
|
<el-table-column type="selection" width="55" />
|
|
|
|
|
|
<el-table-column prop="testName" label="测试项目" width="200">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model="scope.row.testName" placeholder="请输入测试项目" :data-row-index="scope.$index" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="testUnit" label="测试单位" width="180">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model="scope.row.testUnit" placeholder="请输入测试单位" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="sampleCount" label="样品数量" width="120">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model.number="scope.row.sampleCount"
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
placeholder="样品数量"
|
|
|
|
|
|
@change="calculateAmount"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="unitPrice" label="单价" width="120">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model.number="scope.row.unitPrice"
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
placeholder="单价"
|
|
|
|
|
|
@change="calculateAmount"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="amount" label="金额" width="120">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model.number="scope.row.amount" type="number" placeholder="金额" disabled />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="testDate" label="测试日期" width="120">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-date-picker
|
|
|
|
|
|
v-model="scope.row.testDate"
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
placeholder="选择日期"
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="remarks" label="备注">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model="scope.row.remarks" placeholder="请输入备注" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="操作" width="100" fixed="right">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-button type="danger" size="small" @click="deleteRow(scope.row.id)">删除</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="total-row">
|
|
|
|
|
|
<div class="total-item">
|
|
|
|
|
|
<span>测试化验加工费总计:</span>
|
|
|
|
|
|
<el-input v-model.number="totalAmount" type="number" disabled style="width: 150px; margin-left: 10px;" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { ref, reactive, watch } from 'vue'
|
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
|
|
|
|
|
|
|
// 接收项目ID作为props
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
|
projectId: string
|
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
// 定义事件
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
|
update: [data: { totalAmount: number }]
|
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
// 测试化验加工费数据
|
|
|
|
|
|
const testData = reactive<any[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
// 选中的行
|
|
|
|
|
|
const selectedRows = ref<any[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
// 添加行数
|
|
|
|
|
|
const addRowCount = ref(1)
|
|
|
|
|
|
|
|
|
|
|
|
// 总金额
|
|
|
|
|
|
const totalAmount = ref(0)
|
|
|
|
|
|
|
|
|
|
|
|
// ID计数器
|
|
|
|
|
|
let idCounter = 1
|
|
|
|
|
|
|
|
|
|
|
|
// 监听项目ID变化
|
|
|
|
|
|
watch(() => props.projectId, (newProjectId) => {
|
|
|
|
|
|
if (newProjectId) {
|
|
|
|
|
|
loadTestData(newProjectId)
|
|
|
|
|
|
}
|
|
|
|
|
|
}, { immediate: true })
|
|
|
|
|
|
|
|
|
|
|
|
// 加载测试化验加工费数据
|
|
|
|
|
|
const loadTestData = async (projectId: string) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 这里应该调用API获取测试化验加工费数据,暂时使用模拟数据
|
|
|
|
|
|
console.log('加载测试化验加工费数据:', projectId)
|
|
|
|
|
|
|
|
|
|
|
|
// 清空现有数据
|
|
|
|
|
|
testData.splice(0, testData.length)
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟数据
|
|
|
|
|
|
const mockData = [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: idCounter++,
|
|
|
|
|
|
testName: '材料强度测试',
|
|
|
|
|
|
testUnit: '国家材料测试中心',
|
|
|
|
|
|
sampleCount: 5,
|
|
|
|
|
|
unitPrice: 10000,
|
|
|
|
|
|
amount: 50000,
|
|
|
|
|
|
testDate: '2023-06-15',
|
|
|
|
|
|
remarks: ''
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: idCounter++,
|
|
|
|
|
|
testName: '成分分析',
|
|
|
|
|
|
testUnit: '第三方检测机构',
|
|
|
|
|
|
sampleCount: 3,
|
|
|
|
|
|
unitPrice: 10000,
|
|
|
|
|
|
amount: 30000,
|
|
|
|
|
|
testDate: '2023-07-20',
|
|
|
|
|
|
remarks: ''
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 添加到响应式数组
|
|
|
|
|
|
mockData.forEach(item => testData.push(item))
|
|
|
|
|
|
|
|
|
|
|
|
// 计算总金额
|
|
|
|
|
|
calculateTotal()
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
ElMessage.error('加载测试化验加工费数据失败')
|
|
|
|
|
|
console.error('加载测试化验加工费数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加新行
|
|
|
|
|
|
const addRow = () => {
|
|
|
|
|
|
const startIndex = testData.length
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < addRowCount.value; i++) {
|
|
|
|
|
|
testData.push({
|
|
|
|
|
|
id: idCounter++,
|
|
|
|
|
|
testName: '',
|
|
|
|
|
|
testUnit: '',
|
|
|
|
|
|
sampleCount: 0,
|
|
|
|
|
|
unitPrice: 0,
|
|
|
|
|
|
amount: 0,
|
|
|
|
|
|
testDate: '',
|
|
|
|
|
|
remarks: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ElMessage.success(`成功添加 ${addRowCount.value} 行`)
|
|
|
|
|
|
|
|
|
|
|
|
// 延迟聚焦到新添加行的第一个输入框
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
const firstNewInput = document.querySelector(`[data-row-index="${startIndex}"]`)
|
|
|
|
|
|
if (firstNewInput) {
|
|
|
|
|
|
(firstNewInput as HTMLInputElement).focus()
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 100)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理选择变化
|
|
|
|
|
|
const handleSelectionChange = (selection: any[]) => {
|
|
|
|
|
|
selectedRows.value = selection
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除选中的行
|
|
|
|
|
|
const deleteSelectedRows = () => {
|
|
|
|
|
|
if (selectedRows.value.length === 0) {
|
|
|
|
|
|
ElMessage.warning('请先选择要删除的行')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const idsToDelete = new Set(selectedRows.value.map(row => row.id))
|
|
|
|
|
|
const newTestData = testData.filter(item => !idsToDelete.has(item.id))
|
|
|
|
|
|
|
|
|
|
|
|
testData.splice(0, testData.length, ...newTestData)
|
|
|
|
|
|
selectedRows.value = []
|
|
|
|
|
|
calculateTotal()
|
|
|
|
|
|
ElMessage.success('删除成功')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算单项金额
|
|
|
|
|
|
const calculateAmount = () => {
|
|
|
|
|
|
testData.forEach(item => {
|
|
|
|
|
|
item.amount = (item.sampleCount || 0) * (item.unitPrice || 0)
|
|
|
|
|
|
})
|
|
|
|
|
|
calculateTotal()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算总金额
|
|
|
|
|
|
const calculateTotal = () => {
|
|
|
|
|
|
totalAmount.value = testData.reduce((sum, item) => sum + (item.amount || 0), 0)
|
|
|
|
|
|
|
|
|
|
|
|
// 触发update事件通知父组件
|
|
|
|
|
|
emit('update', { totalAmount: totalAmount.value })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 刷新数据
|
|
|
|
|
|
const refreshData = () => {
|
|
|
|
|
|
if (props.projectId) {
|
|
|
|
|
|
loadTestData(props.projectId)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重置数据
|
|
|
|
|
|
const resetData = () => {
|
|
|
|
|
|
testData.splice(0, testData.length)
|
|
|
|
|
|
totalAmount.value = 0
|
|
|
|
|
|
idCounter = 1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取表单数据
|
|
|
|
|
|
const getFormData = () => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
testData: [...testData],
|
|
|
|
|
|
totalAmount: totalAmount.value
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 暴露方法给父组件
|
|
|
|
|
|
defineExpose({
|
|
|
|
|
|
refreshData,
|
|
|
|
|
|
resetData,
|
|
|
|
|
|
getFormData
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.test-cost-container {
|
|
|
|
|
|
padding: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.table-card {
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.total-row {
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
text-align: right;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.total-item {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|