admin-vue3/src/views/erp/purchase/in/components/PurchaseInItemForm.vue

477 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div>
<div>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
:disabled="disabled"
>
<el-table :data="formData" show-summary :summary-method="getSummaries" class="-mt-10px">
<el-table-column label="序号" type="index" align="center" width="60" />
<el-table-column label="仓库名称" min-width="125">
<template #default="{ row, $index }">
<el-form-item
:prop="`${$index}.warehouseId`"
:rules="formRules.warehouseId"
class="mb-0px!"
>
<el-select
v-model="row.warehouseId"
clearable
filterable
placeholder="请选择仓库"
@change="onChangeWarehouse(row)"
>
<el-option
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="货位" min-width="125">
<template #default="{ row, $index }">
<el-form-item
:prop="`${$index}.warehouseLocationId`"
:rules="formRules.warehouseLocationId"
class="mb-0px!"
>
<el-select
v-model="row.warehouseLocationId"
clearable
filterable
placeholder="请选择货位"
>
<el-option
v-for="item in locationList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品名称" min-width="180">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.productName" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="库存" min-width="100">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.stockCount" :formatter="erpCountInputFormatter" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="条码" min-width="150">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.productBarCode" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="单位" min-width="80">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.productUnitName" />
</el-form-item>
</template>
</el-table-column>
<el-table-column
label="已入库/原数量"
fixed="right"
min-width="120"
v-if="formData[0]?.totalCount != null"
>
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled :value="`${row.inCount}/${row.totalCount}`" />
</el-form-item>
</template>
</el-table-column>
<el-table-column
label="码"
fixed="right"
min-width="40"
>
<template #default="{ row , $index}">
<el-form-item class="mb-0px!">
<el-button :disabled="row.ifQr" @click="openQrDialog($index)" link>
<Icon icon="fa:qrcode" />
</el-button>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="数量" prop="count" fixed="right" min-width="140">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.count`" :rules="formRules.count" class="mb-0px!">
<el-input-number
v-model="row.count"
controls-position="right"
:min="0.001"
:precision="3"
class="!w-100%"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品单价" fixed="right" min-width="120">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productPrice`" class="mb-0px!">
<el-input-number
v-model="row.productPrice"
controls-position="right"
:min="0.01"
:precision="2"
class="!w-100%"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="金额" prop="totalProductPrice" fixed="right" min-width="100">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.totalProductPrice`" class="mb-0px!">
<el-input
disabled
v-model="row.totalProductPrice"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="税率(%" fixed="right" min-width="115">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.taxPercent`" class="mb-0px!">
<el-input-number
v-model="row.taxPercent"
controls-position="right"
:min="0"
:precision="2"
class="!w-100%"
/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="税额" prop="taxPrice" fixed="right" min-width="120">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.taxPrice`" class="mb-0px!">
<el-form-item :prop="`${$index}.taxPrice`" class="mb-0px!">
<el-input disabled v-model="row.taxPrice" :formatter="erpPriceInputFormatter" />
</el-form-item>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="税额合计" prop="totalPrice" fixed="right" min-width="100">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.totalPrice`" class="mb-0px!">
<el-input disabled v-model="row.totalPrice" :formatter="erpPriceInputFormatter" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="备注" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.remark`" class="mb-0px!">
<el-input v-model="row.remark" placeholder="请输入备注" />
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="60">
<template #default="{ $index }">
<el-button :disabled="formData.length === 1" @click="handleDelete($index)" link>
<span v-show="formData.length > 1 && !disabled" style="color: red;">—</span>
<span v-show="formData.length === 1 || disabled">—</span>
</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
</div>
<div>
<div>
<!-- 一物一码扫码弹窗 -->
<Dialog
:title="productName"
v-model="qrDialogVisible"
:appendToBody="true"
>
<!-- 显示有效行数 -->
<div>输入的有效行数: {{ qrCodeLineCount }}</div>
<div>需要扫描数: {{ qrNeedCount }}</div>
<el-input type="textarea" v-model="qrCodes" :rows="10" @input="onQrCodeChange" />
<template #footer>
<el-button @click="submitQr" type="primary">
确 定
</el-button>
<el-button @click="qrDialogVisible = false">取 消</el-button>
</template>
</Dialog>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { WarehouseApi as WarehouseLocactionApi ,WarehouseLocationVO} from '@/api/erp/warehouse'
import { StockApi } from '@/api/erp/stock/stock'
import {
erpCountInputFormatter,
erpPriceInputFormatter,
erpPriceMultiply,
getSumValue,
matchRulesFromDictOptions
} from '@/utils'
import { WarehouseApi, WarehouseVO } from '@/api/erp/stock/warehouse'
const props = defineProps<{
items: undefined
disabled: false
}>()
const formLoading = ref(false) // 表单的加载中
const formData = ref([])
const formRules = reactive({
warehouseId: [{ required: true, message: '仓库不能为空', trigger: 'blur' }],
productId: [{ required: true, message: '产品不能为空', trigger: 'blur' }],
warehouseLocationId: [{ required: true, message: '货位不能为空', trigger: 'blur' }],
count: [{ required: true, message: '产品数量不能为空', trigger: 'blur' }]
})
const formRef = ref([]) // 表单 Ref
const warehouseList = ref<WarehouseVO[]>([]) // 仓库列表
const locationList = ref<WarehouseLocationVO[]>([]) // 仓库列表
const defaultWarehouse = ref<WarehouseVO>(undefined) // 默认仓库
//一物一码弹窗
const qrDialogVisible = ref(false)
// 扫码内容
const qrCodes = ref('')
// 当前点击行
const currentRow = ref<typeof props.items[0]>(undefined)
// 当前行号
const currentRowIndex = ref(-1)
// 一物一码弹窗名
const productName = ref('')
// 记录二维码文本域中的有效行数
const qrCodeLineCount = ref(0);
const message = useMessage() // 消息弹窗
// 需要扫描二维码个数
const qrNeedCount = computed(() => {
return currentRow.value?.count
})
// 监听二维码文本域内容的变化
const onQrCodeChange = () => {
// 检查 qrCode.value 是否为 undefined 或 null
if (qrCodes.value) {
const lines = qrCodes.value.split('\n');
// 先将每一行去除首尾空格
const trimmedLines = lines.map(line => line.trim());
const validLines = trimmedLines.filter(line =>{
return matchRulesFromDictOptions(line)
});
// 去重
const uniqueLines = [...new Set(validLines)];
qrCodeLineCount.value = uniqueLines.length;
// 检查有效行数是否超过 row.count
if (currentRow.value && qrCodeLineCount.value > currentRow.value.count) {
message.warning('输入的有效行数不能超过该产品的数量!');
// 截取多余的行数
qrCodes.value = validLines.slice(0, currentRow.value.count).join('\n');
qrCodeLineCount.value = currentRow.value.count;
}
} else {
qrCodeLineCount.value = 0;
}
};
// 控制一物一码弹窗
const openQrDialog = (index:number) => {
// 确保 currentRow 更新为当前点击的行
currentRow.value = formData.value[index] ; // 使用解构赋值确保创建一个新对象
currentRowIndex.value = index;
// 检查当前行是否有二维码数据
if (currentRow.value.qrcodes) {
qrCodes.value = currentRow.value.qrcodes;
} else {
qrCodes.value = ''; // 如果没有二维码数据,清空文本域
}
// 更新弹窗标题
productName.value = currentRow.value.productName;
// 重新计算有效行数
onQrCodeChange();
// 显示弹窗
qrDialogVisible.value = true;
};
/** 提交扫码 */
const submitQr = async () => {
// 扫码内容不能为空
if (qrCodes.value == null || qrCodes.value == undefined || qrCodes.value === '') {
return;
}
// 找到当前行在 formData 中的索引
const index = currentRowIndex.value; // 获取当前行的索引
if (index !== -1 && formData.value[index]) { // 确保索引有效且对应行存在
// 更新 formData 中对应行的 qrcode 值
formData.value[index].qrcodes = qrCodes.value;
//更新有效行数
formData.value[index].qrCodeLineCount = qrCodeLineCount.value;
}
// 关闭弹窗
qrDialogVisible.value = false;
};
/** 初始化设置入库项 */
watch(
() => props.items,
async (val) => {
val.forEach((item) => {
if (item.warehouseId == null) {
item.warehouseId = defaultWarehouse.value?.id
}
if (item.stockCount === null && item.warehouseId != null) {
setStockCount(item)
}
})
formData.value = val
},
{ immediate: true }
)
/** 监听合同产品变化,计算合同产品总价 */
watch(
() => formData.value,
(val) => {
if (!val || val.length === 0) {
return
}
// 循环处理
val.forEach((item) => {
item.totalProductPrice = erpPriceMultiply(item.productPrice, item.count)
item.taxPrice = erpPriceMultiply(item.totalProductPrice, item.taxPercent / 100.0)
if (item.totalProductPrice != null) {
item.totalPrice = item.totalProductPrice + (item.taxPrice || 0)
} else {
item.totalPrice = undefined
}
})
},
{ deep: true }
)
/** 合计 */
const getSummaries = (param: SummaryMethodProps) => {
const { columns, data } = param
const sums: string[] = []
columns.forEach((column, index: number) => {
if (index === 0) {
sums[index] = '合计'
return
}
if (['count', 'totalProductPrice', 'taxPrice', 'totalPrice'].includes(column.property)) {
const sum = getSumValue(data.map((item) => Number(item[column.property])))
sums[index] =
column.property === 'count' ? erpCountInputFormatter(sum) : erpPriceInputFormatter(sum)
} else {
sums[index] = ''
}
})
return sums
}
/** 新增按钮操作 */
const handleAdd = () => {
const row = {
id: undefined,
productId: undefined,
productUnitName: undefined, // 产品单位
productBarCode: undefined, // 产品条码
productPrice: undefined,
stockCount: undefined,
count: 1,
totalProductPrice: undefined,
taxPercent: undefined,
taxPrice: undefined,
totalPrice: undefined,
remark: undefined
}
formData.value.push(row)
}
const queryLocationParams = reactive({
pageNo: 1,
pageSize: 10,
warehouseId: undefined as unknown
})
//仓库变更,同步变更货位列表
const onChangeWarehouse = async (row: typeof props.items[0]) => {
queryLocationParams.warehouseId = row.warehouseId;
try {
const response = await WarehouseLocactionApi.getWarehouseLocationPage(queryLocationParams);
if (response) {
if (response.list.length !== 0) {
locationList.value = response.list;
} else {
console.error('获取货位列表失败,错误码:', response.code, ',错误信息:', response.msg);
}
} else {
console.error('获取货位列表失败,响应为空');
}
} catch (error: any) {
if (error.response) {
// Axios错误有响应体
console.error('获取货位列表失败HTTP错误码:', error.response.status, ',错误信息:', error.response.data);
} else if (error.request) {
// 发出了请求,但没有收到响应
console.error('获取货位列表失败,没有收到响应');
} else {
// 其他错误
console.error('获取货位列表时发生错误:', error.message);
}
}
};
/** 删除按钮操作 */
const handleDelete = (index: number) => {
formData.value.splice(index, 1)
}
/** 加载库存 */
const setStockCount = async (row: any) => {
if (!row.productId) {
return
}
const count = await StockApi.getStockCount(row.productId)
row.stockCount = count || 0
}
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
}
defineExpose({ validate })
/** 初始化 */
onMounted(async () => {
warehouseList.value = await WarehouseApi.getWarehouseSimpleList()
defaultWarehouse.value = warehouseList.value.find((item) => item.defaultStatus)
locationList.value = onChangeWarehouse({pageNo: 1,pageSize: 10,warehouseId: defaultWarehouse.value?.id})
})
</script>