This commit is contained in:
2025-08-15 02:11:07 +08:00
parent 5e8fc48bc7
commit e1295bdda0
5 changed files with 176 additions and 97 deletions

View File

@ -78,6 +78,22 @@ export const uploadCode = (data: any) => {
return request(config);
};
// 获取物流单号
export const ltnList = (data: any) => {
const config: any = {
url: '/cailiaoshebei/ltn/list?docId=' + data.docId,
method: 'get'
};
// 如果 query.token 存在,就覆盖请求头里的 token
if (data.token) {
config.headers = {
Authorization: data.token
};
}
return request(config);
};
export const listLink = (data: any) => {
return request({
url: '/cailiaoshebei/ltn/list',

View File

@ -145,17 +145,13 @@
<div v-for="(item, index) in form.itemList" :key="index" class="detail-item">
<el-row>
<el-col :span="12">
<el-form-item
:label="index === 0 ? '名称' : ''"
:prop="`itemList.${index}.name`"
:rules="{ required: true, message: '名称不能为空', trigger: 'blur' }"
>
<el-form-item label="名称" :prop="`itemList.${index}.name`" :rules="{ required: true, message: '名称不能为空', trigger: 'blur' }">
<el-input v-model="item.name" placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="index === 0 ? '规格' : ''"
label="规格"
:prop="`itemList.${index}.specification`"
:rules="{ required: true, message: '规格不能为空', trigger: 'blur' }"
>
@ -163,17 +159,13 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="index === 0 ? '单位' : ''"
:prop="`itemList.${index}.unit`"
:rules="{ required: true, message: '单位不能为空', trigger: 'blur' }"
>
<el-form-item label="单位" :prop="`itemList.${index}.unit`" :rules="{ required: true, message: '单位不能为空', trigger: 'blur' }">
<el-input v-model="item.unit" placeholder="请输入单位" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="index === 0 ? '数量' : ''"
label="数量"
:prop="`itemList.${index}.quantity`"
:rules="{ required: true, message: '数量不能为空', trigger: 'blur' }"
>
@ -182,7 +174,7 @@
</el-col>
<el-col :span="12">
<el-form-item
:label="index === 0 ? '验收' : ''"
label="验收"
:prop="`itemList.${index}.acceptedQuantity`"
:rules="{ required: true, message: '验收数量不能为空', trigger: 'blur' }"
>
@ -191,7 +183,7 @@
</el-col>
<el-col :span="12">
<el-form-item
:label="index === 0 ? '缺件' : ''"
label="缺件"
:prop="`itemList.${index}.shortageQuantity`"
:rules="{ required: true, message: '缺件数量不能为空', trigger: 'blur' }"
>
@ -200,7 +192,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="index === 0 ? '备注' : ''" prop="remark">
<el-form-item label="备注" prop="remark">
<el-input v-model="item.remark" placeholder="请输入备注" />
</el-form-item>
</el-col>
@ -508,6 +500,7 @@ const getlistPurchase = async () => {
projectId: currentProject.value?.id,
status: 'finish'
});
purchaseDocList.value = res.rows;
if (purchaseDocList.value && purchaseDocList.value.length > 0) {
purchaseDocList.value.forEach((item) => {
@ -522,6 +515,8 @@ const getdemandInfo = async (docId: string) => {
if (res.code == 200) {
// 需求表单赋值
form.value.itemList = [];
// form.value.itemList 清空
console.log(form.value.itemList);
res.data.forEach((item, index) => {
let obj = {
quantity: item.demandQuantity,

View File

@ -25,7 +25,7 @@
border
>
<el-table-column prop="num" label="编号" />
<el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="name" label="工程或费用名称" width="180" />
<el-table-column prop="unit" label="单位" />
<el-table-column prop="quantity" label="数量" width="60" />
<el-table-column prop="batchNumber" label="批次号" width="200" />

View File

@ -7,14 +7,12 @@
<el-form-item label="采购单编号" prop="docCode">
<el-input v-model="queryParams.docCode" placeholder="请输入采购单编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="设备统称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入设备统称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="到货日期" prop="arrivalDate">
<el-date-picker clearable v-model="queryParams.arrivalDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择到货日期" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@ -23,45 +21,35 @@
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['cailiaoshebei:purchaseDoc:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['cailiaoshebei:purchaseDoc:edit']"
>修改</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="purchaseDocList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="采购单编号" align="center" prop="docCode" width="90" />
<el-table-column type="index" width="55" label="序号" align="center" />
<el-table-column label="批次号" align="center" prop="planCode" width="120" />
<el-table-column label="采购单编号" align="center" prop="docCode" width="160" />
<el-table-column label="供应商" align="center" prop="supplier" />
<!-- <el-table-column label="事由" align="center" prop="reason" /> -->
<el-table-column label="设备统称" align="center" prop="name" />
<el-table-column label="到货日期" align="center" prop="arrivalDate" width="180">
<el-table-column label="到货日期" align="center" prop="arrivalDate" width="120">
<template #default="scope">
<span>{{ parseTime(scope.row.arrivalDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<!-- <el-table-column label="负责人联系方式" align="center" prop="designDirectorTel" width="130" />
<el-table-column label="现场联系方式" align="center" prop="technicalDirectorTel" width="130" /> -->
<el-table-column label="收货地址" align="center" prop="receivingAddress" />
<el-table-column label="联系人" align="center" prop="contacts" />
<!-- <el-table-column label="项目负责人" align="center" prop="projectDirector" width="90" /> -->
<el-table-column label="物流单" align="center" prop="remark" width="150">
<el-table-column label="物流单号" align="center" prop="remark" width="150">
<template #default="scope">
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['out:monthPlan:remove']">查看物流单</el-button>
</template>
</el-table-column>
<el-table-column label="采购经办人" align="center" prop="purchasingAgent" width="90" />
<el-table-column label="日期" align="center" prop="preparedDate" width="180">
<el-table-column label="日期" align="center" prop="preparedDate" width="120">
<template #default="scope">
<span>{{ parseTime(scope.row.preparedDate, '{y}-{m}-{d}') }}</span>
</template>
@ -71,7 +59,6 @@
<el-link :href="scope.row.feedbackUrl" target="_blank" type="primary" v-if="scope.row.feedbackUrl">回单</el-link>
</template>
</el-table-column>
<el-table-column label="审核状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.status"></dict-tag>
@ -79,10 +66,33 @@
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="160">
<template #default="scope">
<el-button link type="primary" icon="Finished" @click="handleAudit(scope.row)" v-hasPermi="['cailiaoshebei:purchaseDoc:edit']">
<el-button
link
type="primary"
v-if="scope.row.status == 'draft' || scope.row.status == 'back'"
icon="Finished"
@click="handleAudit(scope.row)"
v-hasPermi="['cailiaoshebei:purchaseDoc:edit']"
>
审核</el-button
>
<el-button link type="primary" icon="Upload" @click="handleUpload(scope.row)" v-hasPermi="['cailiaoshebei:purchaseDoc:edit']"
<el-button
link
type="primary"
v-if="scope.row.status != 'draft'"
icon="view"
@click="handleViewDetail(scope.row)"
v-hasPermi="['cailiaoshebei:purchaseDoc:edit']"
>
查看</el-button
>
<el-button
link
type="primary"
v-if="!scope.row.feedbackUrl && scope.row.status == 'finish'"
icon="Upload"
@click="handleUpload(scope.row)"
v-hasPermi="['cailiaoshebei:purchaseDoc:edit']"
>上传</el-button
>
<el-button
@ -106,7 +116,6 @@
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改物资-采购联系单对话框 -->
@ -118,7 +127,6 @@
></el-col>
<el-col :span="12" :offset="0"
><el-form-item label="供应商" prop="supplier">
<!-- <el-input v-model="form.supplier" placeholder="请输入供应商" /> -->
<el-select v-model="form.supplier" value-key="id" placeholder="请选择供应商" clearable filterable @change="">
<el-option v-for="item in supplierOptions" :key="item.id" :label="item.name" :value="item.name"> </el-option>
</el-select> </el-form-item
@ -148,11 +156,11 @@
></el-col>
<el-col :span="12" :offset="0"
><el-form-item label="负责人联系方式" prop="designDirectorTel">
<el-input v-model="form.designDirectorTel" placeholder="请输入设计负责人联系方式" type="number" /> </el-form-item
<el-input v-model="form.designDirectorTel" placeholder="请输入设计负责人联系方式" /> </el-form-item
></el-col>
<el-col :span="12" :offset="0"
><el-form-item label="现场联系方式" prop="technicalDirectorTel">
<el-input v-model="form.technicalDirectorTel" placeholder="请输入现场技术负责人联系方式" type="number" /> </el-form-item
<el-input v-model="form.technicalDirectorTel" placeholder="请输入现场技术负责人联系方式" /> </el-form-item
></el-col>
<el-col :span="12" :offset="0">
<el-form-item label="收货地址" prop="receivingAddress">
@ -169,11 +177,11 @@
><el-form-item label="采购经办人" prop="purchasingAgent">
<el-input v-model="form.purchasingAgent" placeholder="请输入采购经办人" /> </el-form-item
></el-col>
<el-col :span="12" :offset="0"
<!-- <el-col :span="12" :offset="0"
><el-form-item label="日期" prop="preparedDate">
<el-date-picker clearable v-model="form.preparedDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期">
</el-date-picker> </el-form-item
></el-col>
></el-col> -->
</el-row>
</el-form>
<el-table v-loading="loading" :data="selectPlanList" v-if="form.id">
@ -251,7 +259,7 @@ const total = ref(0);
const feedbackUrl = ref('');
const queryFormRef = ref<ElFormInstance>();
const purchaseDocFormRef = ref<ElFormInstance>();
const IP = 'http://192.168.110.142:7788';
const IP = 'http://192.168.110.151:7788';
const dialog = reactive<DialogOption>({
visible: false,
@ -275,7 +283,6 @@ const initFormData: any = {
receivingAddress: undefined,
contacts: undefined,
associationList: [],
projectDirector: undefined,
purchasingAgent: undefined,
preparedDate: undefined,
@ -312,7 +319,16 @@ const data = reactive({
params: {}
},
rules: {
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }]
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
// 电话号码验证
technicalDirectorTel: [
{ required: true, message: '请输入电话', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
],
designDirectorTel: [
{ required: true, message: '请输入电话', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
]
}
});
@ -356,7 +372,7 @@ const fileList = ref([]);
const viewVisible = ref(false);
const handleView = async (row?: any) => {
const res = await listLink({
id: row.id
docId: row.id
});
fileList.value = res.rows;
@ -448,7 +464,7 @@ const getSupplierList = async () => {
supplierOptions.value = res.rows;
};
/** 删除按钮操作 */
/** 分享按钮操作 */
const handleShare = async (row?: PurchaseDocVO) => {
const textarea = document.createElement('textarea');
const data = JSON.stringify({
@ -457,8 +473,10 @@ const handleShare = async (row?: PurchaseDocVO) => {
projectId: currentProject.value?.id,
token: 'Bearer ' + getToken()
});
// 获取当前域名地址
console.log(location);
textarea.value = IP + '/materials/purchaseDoc/uploadCode?data=' + data;
// textarea.value = location.host + '/materials/purchaseDoc/uploadCode?data=' + data;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
@ -499,6 +517,14 @@ const handleAudit = async (row?: PurchaseDocVO) => {
type: 'update'
});
};
/** 审核按钮操作 */
const handleViewDetail = async (row?: PurchaseDocVO) => {
proxy?.$tab.closePage(route);
proxy?.$tab.openPage('/materials/purchaseDoc/indexEdit', '审核采购联系单', {
id: row.id,
type: 'view'
});
};
onMounted(() => {
getList();

View File

@ -3,32 +3,48 @@
<el-card shadow="always" :body-style="{ padding: '20px' }">
<template #header>
<div>
<span><!-- card title --></span>
<span>物流单号填写</span>
</div>
</template>
<!-- card body -->
<el-form :model="shareForm" label-width="80px">
<!-- 表单引用用于触发整体验证 -->
<el-form :model="shareForm" label-width="80px" ref="formRef" :rules="formRules">
<!-- 循环项添加prop属性指定嵌套字段路径 -->
<div v-for="(item, index) in shareForm.list" :key="index" class="row-wrap flex items-center">
<el-row :gutter="20" class="w-full">
<!-- 计划必填 + 选择触发验证 -->
<el-col :xs="24" :sm="12" :lg="8">
<el-form-item label="计划">
<el-form-item label="计划" :prop="`list[${index}].planId`" :rules="[{ required: true, message: '请选择计划', trigger: 'change' }]">
<el-select v-model="item.planId" placeholder="请选择">
<el-option v-for="plan in planList" :key="plan.id" :label="plan.name" :value="plan.id" />
<el-option v-for="plan in planList" :key="plan.id" :label="plan.name" :value="plan.id.toString()" />
</el-select>
</el-form-item>
</el-col>
<!-- 数量必填 + 数字验证大于0 -->
<el-col :xs="24" :sm="12" :lg="8">
<el-form-item label="数量">
<el-input v-model="item.num" />
<el-form-item label="数量" :prop="`list[${index}].num`">
<el-input v-model.number="item.num" placeholder="请填写数量" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :lg="8">
<el-form-item label="物流单号">
<el-input v-model="item.ltn" />
<!-- 物流单号必填 + 非空验证 -->
<el-col :xs="20" :sm="10" :lg="6">
<el-form-item
label="物流单号"
:prop="`list[${index}].ltn`"
:rules="[
{ required: true, message: '请填写物流单号', trigger: 'blur' },
{ min: 3, max: 50, message: '物流单号长度需在3-50字符之间', trigger: 'blur' }
]"
>
<el-input v-model="item.ltn" placeholder="请填写物流单号" />
</el-form-item>
</el-col>
<el-col :xs="4" :sm="2" :lg="2" class="flex items-center justify-center">
<el-button type="danger" icon="Delete" size="small" @click="deleteRow(index)" :disabled="shareForm.list.length <= 1" />
</el-col>
</el-row>
</div>
@ -43,11 +59,17 @@
<script lang="ts" setup>
import { getBatch } from '@/api/materials/batchPlan';
import { uploadCode } from '@/api/materials/purchaseDoc';
import { removeToken, setToken } from '@/utils/auth';
import { uploadCode, ltnList } from '@/api/materials/purchaseDoc';
import { removeToken } from '@/utils/auth';
import { useRoute } from 'vue-router';
import { getCurrentInstance, onMounted, onUnmounted, ref } from 'vue';
import type { ElForm } from 'element-plus';
const route = useRoute();
const { proxy } = getCurrentInstance();
const { proxy } = getCurrentInstance() as any;
// 表单引用,用于调用验证方法
const formRef = ref<InstanceType<typeof ElForm>>(null);
const shareForm = ref({
docId: '',
@ -58,11 +80,15 @@ const shareForm = ref({
list: [
{
planId: '',
num: '',
num: 0, // 初始化数量为0避免undefined影响数字验证
ltn: ''
}
]
});
// 表单验证规则
const formRules = ref({});
const planList = ref([]);
const getPlanList = async () => {
const res = await getBatch({
@ -73,50 +99,69 @@ const getPlanList = async () => {
token: shareForm.value.token
});
planList.value = res.rows;
getOrder();
};
const addRow = () => {
shareForm.value.list.push({
planId: '',
num: '',
num: 0, // 新增行初始化数量为0
ltn: ''
});
console.log('🚀 ~ addRow ~ form.list:', shareForm.value.list);
};
//检测主要信息填写状况
function validateAndClean(arr) {
// 过滤掉全空的数据项
const cleanedArr = arr.filter((item) => !Object.values(item).every((v) => v === '' || v == null));
let hasFullItem = false; // 是否有一条全填数据
for (const item of cleanedArr) {
const keys = Object.keys(item).filter((k) => k !== 'id');
const allFilled = keys.every((k) => item[k] !== '' && item[k] != null);
if (allFilled) {
hasFullItem = true; // 有一条全填
}
const allEmpty = Object.values(item).every((v) => v === '' || v == null);
// 如果不是全填,也不是全空(部分填) → 直接返回失败
if (!allFilled && !allEmpty) {
return { valid: false, data: cleanedArr };
}
}
// 如果没有至少一条全填,返回失败
if (!hasFullItem) {
return { valid: false, data: cleanedArr };
}
return { valid: true, data: cleanedArr };
}
const deleteRow = (index: number) => {
proxy?.$modal
.confirm('确定要删除这行数据吗?')
.then(() => {
shareForm.value.list.splice(index, 1);
})
.catch(() => {});
};
// 表单提交验证
const onSubmit = async () => {
const result = validateAndClean(shareForm.value.list);
if (!result.valid) {
proxy?.$modal.msgError('验证失败,主要信息存在部分字段缺失的数据项');
// 先触发Element Plus表单的整体验证
const formValid = await formRef.value?.validate().catch(() => false);
if (!formValid) {
proxy?.$modal.msgError('请完善所有必填项信息');
return;
}
await uploadCode(shareForm.value);
proxy?.$modal.msgSuccess('上传成功');
// 再执行数据清洗和完整性校验(避免空行提交)
const cleanedArr = shareForm.value.list.filter((item) => item.planId && item.num > 0 && item.ltn);
if (cleanedArr.length === 0) {
proxy?.$modal.msgError('至少需填写一条完整的物流单信息');
return;
}
// 提交前替换列表为清洗后的数据(排除空行)
const submitData = {
...shareForm.value,
list: cleanedArr
};
try {
await uploadCode(submitData);
proxy?.$modal.msgSuccess('上传成功');
// getOrder();
// 重置表单
// shareForm.value.list = [{ planId: '', num: 0, ltn: '' }];
} catch (error) {
proxy?.$modal.msgError('上传失败,请重试');
}
};
const getOrder = async () => {
let res = await ltnList(shareForm.value);
shareForm.value.list =
res.rows.length > 0
? res.rows.map((item) => ({
planId: item.planId?.toString() || '', // 确保与下拉框value类型一致
num: item.num || 0,
ltn: item.ltn || ''
}))
: [{ planId: '', num: 0, ltn: '' }];
};
onMounted(() => {
@ -126,9 +171,6 @@ onMounted(() => {
shareForm.value.mrpBaseId = data.mrpBaseId || '';
shareForm.value.projectId = data.projectId || '';
shareForm.value.token = data.token || '';
console.log('🚀 ~ form.value:', shareForm.value);
// setToken(form.token);
getPlanList();
});