feat(purchaseorder): 新增供应商确认功能及相关页面组件

- 新增供应商确认状态枚举及流程类型枚举
- 新增供应商采购单列表查询接口
- 新增供应商确认操作相关API方法
- 新建供应商确认页面vendorConfirm.vue
- 实现采购单供应商确认状态管理
- 更新OrderInfoDisplay组件引用路径
- 修复mapper中applyTime判断逻辑错误
- 添加页面初始化时路由参数打印调试信息
- 实现线上/线下流程类型选择功能
- 增加采购单详情查看和历史版本查看功能
- 实现供应商确认和驳回操作功能
- 添加确认状态和流程类型的查询条件
- 优化采购单列表展示和筛选功能
- 实现供应商确认状态标签显示
- 增加采购单发起日期和负责人信息展示
- 修复审批状态判断逻辑用于控制详情查看权限
- 实现采购单撤回功能及相关提示确认
- 添加含税总金额字段展示
- 增加联系人及联系电话信息展示列
dev_1.0.0
chenhao 2025-11-28 11:29:56 +08:00
parent c99ba06d89
commit d775904dc3
12 changed files with 546 additions and 9 deletions

View File

@ -8,6 +8,13 @@ export function listPurchaseorder(query) {
params: query
})
}
export function listVendorPurchaseorder(query) {
return request({
url: '/sip/purchaseorder/vendor/list',
method: 'get',
params: query
})
}
// 查询待审批采购单主表列表
export function listApprovePurchaseorder(query) {

View File

@ -138,7 +138,7 @@ import ApproveLayout from '@/views/approve/ApproveLayout.vue';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
import OrderInfoDisplay from '@/components/order/OrderInfoDisplay.vue';
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue';
export default {
name: "Approve",

View File

@ -88,7 +88,7 @@ import { getDicts } from "@/api/system/dict/data";
import CheckoutDialog from './CheckoutDialog.vue';
import OuterDetailDialog from './OuterDetailDialog.vue';
import OrderDetailDrawer from "@/views/project/order/OrderDetailDrawer.vue";
import OrderInfo from "@/components/order/OrderInfoDisplay.vue";
import OrderInfo from "@/views/project/order/components/OrderInfoDisplay.vue";
export default {
name: "ExecutionEdit",

View File

@ -167,7 +167,7 @@
import { getOrder, addOrder, updateOrder, delContractFile, uploadContractFile, getProject } from "@/api/project/order";
import ProductConfig from '@/views/project/info/ProductConfig.vue';
import SelectCommitType from "./SelectCommitType.vue";
import OrderInfo from '@/components/order/OrderInfo.vue';
import OrderInfo from '@/views/project/order/components/OrderInfo.vue';
import SelectProject from "@/views/project/info/SelectProject";
import SelectUser from "@/views/system/user/selectUser";
import SelectPartner from "@/views/system/partner/selectPartner";

View File

@ -134,7 +134,7 @@
<script>
import { getOrder } from "@/api/project/order";
import ProductConfig from '@/views/project/info/ProductConfig.vue';
import OrderInfo from '@/components/order/OrderInfoDisplay.vue';
import OrderInfo from '@/views/project/order/components/OrderInfoDisplay.vue';
export default {
name: "OrderDetailDrawer",

View File

@ -339,6 +339,7 @@ export default {
};
},
created() {
console.log(this.$route.query)
this.getList();
},
watch:{

View File

@ -0,0 +1,472 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="采购单号" prop="purchaseNo">
<el-input
v-model="queryParams.purchaseNo"
placeholder="请输入采购单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="采购方名称" prop="buyerName">
<el-input
v-model="queryParams.buyerName"
placeholder="请输入采购方名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="制造商名称" prop="vendorName">
<el-input
v-model="queryParams.vendorName"
placeholder="请输入制造商名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="汇智负责人" prop="ownerName">
<el-input
v-model="queryParams.ownerName"
placeholder="请输入汇智负责人"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="供应商确认状态" prop="confirmStatus">
<el-select v-model="queryParams.confirmStatus" placeholder="请选择供应商确认状态" clearable>
<el-option
v-for="dict in dict.type.vendor_confirm_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- <el-row :gutter="10" class="mb8">-->
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- type="primary"-->
<!-- plain-->
<!-- icon="el-icon-plus"-->
<!-- size="mini"-->
<!-- @click="handleAdd"-->
<!-- v-hasPermi="['sip:purchaseorder:add']"-->
<!-- >新增-->
<!-- </el-button>-->
<!-- </el-col>-->
<!-- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>-->
<!-- </el-row>-->
<el-table v-loading="loading" :data="purchaseorderList" @selection-change="handleSelectionChange">
<!-- <el-table-column type="selection" width="55" align="center" />-->
<el-table-column label="采购编号" align="center" prop="purchaseNo" width="180"/>
<el-table-column label="采购方名称" align="center" prop="buyerName" />
<el-table-column label="制造商名称" align="center" prop="vendorName" width="120"/>
<el-table-column label="联系人" align="center" prop="vendorUser" width="100"/>
<el-table-column label="联系电话" align="center" prop="vendorPhone" width="120"/>
<el-table-column label="含税总计金额" align="center" prop="totalAmount" width="120"/>
<el-table-column label="发起日期" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="汇智负责人" align="center" prop="ownerName" width="120"/>
<el-table-column label="供应商确认状态" align="center" prop="confirmStatus" width="120">
<template slot-scope="scope">
<dict-tag :options="dict.type.vendor_confirm_status" :value="scope.row.confirmStatus"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button
v-if="scope.row.approveStatus === '1' || scope.row.approveStatus === '2'"
size="mini"
type="text"
icon="el-icon-view"
@click="handleViewDetails(scope.row)"
v-hasPermi="['sip:purchaseorder:query']"
>查看详情
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改采购单主表对话框 -->
<el-dialog :title="title" :visible.sync="open" width="80vw" append-to-body>
<purchase-order-detail ref="purchaseOrderDetail" :order-data="currentOrderData"
@close="open = false"
@success="getList">
</purchase-order-detail>
<template slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</template>
</el-dialog>
<!-- 流程类型选择对话框 -->
<el-dialog
title="选择流程类型"
:visible.sync="openFlowTypeDialog"
width="30%"
append-to-body
>
<el-form :model="{ flowType: selectedFlowType }" ref="flowTypeForm" label-width="80px">
<el-form-item label="流程类型" prop="flowType"
:rules="[{ required: true, message: '请选择流程类型', trigger: 'change' }]">
<el-select v-model="selectedFlowType" placeholder="请选择流程类型" style="width:100%">
<el-option label="线上" value="online"></el-option>
<el-option label="线下" value="offline"></el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitApplyWithFlowType"> </el-button>
<el-button @click="openFlowTypeDialog = false"> </el-button>
</div>
</el-dialog>
<!-- 订单详情 -->
<template v-if="detailOrderData">
<el-dialog
v-if="detailOrderData.confirmStatus === '0' "
:visible.sync="showDetailDrawer"
@close="showDetailDrawer=false"
width="80vw"
>
<ApproveLayout title="采购单详情" style="max-height: 70vh;overflow-y: auto">
<purchase-order-detail-view ref="dialogViewOnly" :order-data="detailOrderData"
@close="showDetailDrawer = false"
@success="getList" :showHistory="true"
@view-history-detail="handleViewHistoryDetailEvent">
</purchase-order-detail-view>
<template #footer>
<span> {{ currentDetailPurchaseNo }}</span>
</template>
</ApproveLayout>
<template #footer class="dialog-footer">
<el-button type="primary" @click="vendorConfirm('1')"> </el-button>
<el-button type="danger" @click="vendorConfirm('2')"> </el-button>
</template>
</el-dialog>
<el-drawer
v-else
:visible.sync="showDetailDrawer"
@close="showDetailDrawer=false"
size="80vw"
>
<ApproveLayout title="采购单详情">
<purchase-order-detail-view ref="detailViewOnly" :order-data="detailOrderData"
@close="showDetailDrawer = false"
@success="getList" :showHistory="true"
@view-history-detail="handleViewHistoryDetailEvent">
</purchase-order-detail-view>
<template #footer>
<span>订单编号: {{ currentDetailPurchaseNo }}</span>
</template>
</ApproveLayout>
</el-drawer>
</template>
<!-- 历史订单详情抽屉 -->
<el-drawer
title="历史订单详情"
:visible.sync="showHistoryDetailDrawer"
direction="rtl"
size="80%"
>
<ApproveLayout title="采购单历史详情" v-if="showHistoryDetailDrawer">
<purchase-order-detail-view ref="historyDetailView" :order-data="historyDetailOrderData"
@close="showHistoryDetailDrawer = false">
</purchase-order-detail-view>
<template #footer>
<span>订单编号: {{ historyDetailOrderData ? historyDetailOrderData.purchaseNo : '' }}</span>
<span v-if="historyDetailOrderData"> | : {{ historyDetailOrderData.version }}</span>
</template>
</ApproveLayout>
</el-drawer>
</div>
</template>
<script>
import {
applyPurchaseorder,
delPurchaseorder,
getPurchaseorder,
getPurchaseOrderHistoryDetail,
listVendorPurchaseorder,
recallPurchaseorder,
vendorConfirmStatus
} from "@/api/sip/purchaseorder";
import PurchaseOrderDetail from './components/PurchaseOrderDetail';
import ApproveLayout from "@/views/approve/ApproveLayout.vue";
import PurchaseOrderDetailView from "@/views/purchaseorder/components/PurchaseOrderDetailView.vue";
export default {
name: "Purchaseorder",
dicts: ['approve_status', 'vendor_confirm_status', 'purchase_status'],
components: {
PurchaseOrderDetail,
ApproveLayout,
PurchaseOrderDetailView
},
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
currentOrderId: null,
//
currentOrderData: null,
openFlowTypeDialog: false, //
currentApplyingOrder: null, //
selectedFlowType: null, //
//
purchaseorderList: [],
//
title: "",
//
open: false,
//
showDetailDrawer: false,
//
detailOrderData: null,
//
currentDetailPurchaseNo: null,
//
showHistoryDetailDrawer: false,
//
historyDetailOrderData: null,
//
queryParams: {
pageNum: 1,
pageSize: 10,
purchaseNo: null,
buyerName: null,
vendorName: null,
ownerName: null,
approveStatus: null,
confirmStatus: '0',
status: null,
},
//
form: {},
//
rules: {}
};
},
created() {
console.log(this.$route.query)
this.getList();
},
watch: {
//
open(val) {
if (!val) {
this.currentOrderId = null;
this.currentOrderData = null;
this.$refs.purchaseOrderDetail?.resetForm();
}
},
//
showDetailDrawer(val) {
if (!val) {
this.detailOrderData = null;
this.currentDetailPurchaseNo = null;
this.$refs.detailViewOnly?.resetForm();
this.$refs.dialogViewOnly?.resetForm();
}
},
//
showHistoryDetailDrawer(val) {
if (!val) {
this.historyDetailOrderData = null;
this.$refs.historyDetailView?.resetForm();
}
}
},
methods: {
/** 查询采购单主表列表 */
getList() {
this.loading = true;
listVendorPurchaseorder(this.queryParams).then(response => {
this.purchaseorderList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
},
vendorConfirm(type) {
//
let data = {
id: this.detailOrderData.id,
confirmStatus: type // 1 ()
};
vendorConfirmStatus(data).then(() => {
this.getList();
this.$modal.msgSuccess("操作成功");
});
},
//
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.currentOrderData = null;
this.currentOrderId = null;
this.open = true;
this.title = "添加采购单主表";
},
/** 修改按钮操作 */
handleUpdate(row) {
getPurchaseorder(row.id).then(response => {
this.currentOrderData = response.data;
this.currentOrderId = row.id;
this.open = true;
this.title = "修改采购单主表";
});
},
/** 提交按钮 */
submitForm() {
this.$refs.purchaseOrderDetail.submitForm()
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除采购单主表编号为"' + ids + '"的数据项?').then(function () {
return delPurchaseorder(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
},
/** 申请采购按钮操作 */
handleApply(row) {
this.currentApplyingOrder = row; //
this.selectedFlowType = null; //
this.openFlowTypeDialog = true; //
},
/** 查看详情按钮操作 */
handleViewDetails(row) {
getPurchaseorder(row.id).then(response => {
this.detailOrderData = response.data;
console.log(this.detailOrderData)
this.currentDetailPurchaseNo = row.purchaseNo;
console.log('1111')
this.$nextTick(() => {
console.log(2222)
this.showDetailDrawer = true;
});
});
},
/** 处理查看历史详情事件 */
handleViewHistoryDetailEvent(row) {
getPurchaseOrderHistoryDetail(row.id).then(response => {
this.historyDetailOrderData = response.data;
this.showHistoryDetailDrawer = true;
this.showDetailDrawer = false; //
});
},
/** 提交申请采购(带流程类型) */
submitApplyWithFlowType() {
this.$refs.flowTypeForm.validate(valid => {
if (valid) {
let form = {...this.currentApplyingOrder};
form.approveStatus = '1'; // 1 ()
form.flowType = this.selectedFlowType; //
applyPurchaseorder(form).then(() => {
this.openFlowTypeDialog = false; //
this.getList();
this.$modal.msgSuccess("申请采购成功");
}).catch(() => {
this.openFlowTypeDialog = false; //
});
}
});
},
/** 撤回按钮操作 */
handleRecall(row) {
this.$modal.confirm('是否确认撤回采购订单编号为"' + row.purchaseNo + '"的数据项?').then(() => {
recallPurchaseorder(row.id).then(() => {
this.getList();
this.$modal.msgSuccess("撤回成功");
});
this.getList(); //
}).catch(() => {
});
},
/** 发起供应商确认操作 */
handleInitiateVendorConfirmation(row) {
this.$modal.confirm('是否确认发起供应商确认编号为"' + row.purchaseNo + '"的数据项?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//
let data = {
id: row.id,
confirmStatus: '0' // 1 ()
};
vendorConfirmStatus(data).then(() => {
this.getList();
this.$modal.msgSuccess("发起供应商确认成功");
});
}).catch(() => {
});
}
}
};
</script>

View File

@ -4,6 +4,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.ApproveStatusEnum;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sip.domain.OmsPurchaseOrder;
@ -16,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
/**
@ -49,6 +51,20 @@ public class OmsPurchaseOrderController extends BaseController
todoService.fillPurchaseOrderApproveNode(list);
return getDataTable(list);
}
/**
*
*/
@RequiresPermissions("sip:purchaseorder:list")
@GetMapping("/vendor/list")
public TableDataInfo listVendor(OmsPurchaseOrder omsPurchaseOrder)
{
omsPurchaseOrder.setApproveStatus(ApproveStatusEnum.APPROVE_COMPLETE.getCode());
omsPurchaseOrder.setFlowType(OmsPurchaseOrder.FlowTypeEnum.ONLINE.getCode());
startPage();
List<OmsPurchaseOrder> list = omsPurchaseOrderService.selectOmsPurchaseOrderList(omsPurchaseOrder);
return getDataTable(list);
}
/**
*

View File

@ -5,6 +5,7 @@ import java.util.Date;
import java.util.List;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.Getter;
import lombok.ToString;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -104,5 +105,39 @@ public class OmsPurchaseOrder extends BaseEntity
/** 采购单明细表信息 */
private List<OmsPurchaseOrderItem> omsPurchaseOrderItemList;
@Getter
public enum ConfirmStatusEnum {
WAIT_CONFIRM("0", "待确认"),
CONFIRM("1", "已确认"),
REJECT("2", "已驳回"),
;
private final String value;
private final String code;
ConfirmStatusEnum(String code, String value) {
this.code = code;
this.value = value;
}
}
@Getter
public enum FlowTypeEnum {
ONLINE("online", "线上"),
OFFLINE("offline", "线下"),
;
private final String value;
private final String code;
FlowTypeEnum(String code, String value) {
this.code = code;
this.value = value;
}
}
}

View File

@ -77,10 +77,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectOmsPurchaseOrderList" parameterType="OmsPurchaseOrder" resultMap="OmsPurchaseOrderResult">
<include refid="selectOmsPurchaseOrderRelationVo"/>
<where>
<if test="purchaseNo != null and purchaseNo != ''"> and purchase_no = #{purchaseNo}</if>
<if test="vendorId != null "> and vendor_id = #{vendorId}</if>
<if test="status != null "> and status = #{status}</if>
<if test="approveStatus != null "> and approve_status = #{approveStatus}</if>
<if test="purchaseNo != null and purchaseNo != ''"> and t1.purchase_no = #{purchaseNo}</if>
<if test="buyerName != null and buyerName != ''"> and t1.buyer_name = #{buyerName}</if>
<if test="vendorId != null "> and t1.vendor_id = #{vendorId}</if>
<if test="status != null "> and t1.status = #{status}</if>
<if test="confirmStatus != null "> and t1.confirm_status = #{confirmStatus}</if>
<if test="approveStatus != null "> and t1.approve_status = #{approveStatus}</if>
<if test="flowType != null "> and t1.flow_type = #{flowType}</if>
<if test="vendorName != null "> and t2.vendor_name = #{vendorName}</if>
<if test="ownerName != null "> and t1.owner_name = #{ownerName}</if>
</where>
</select>
@ -116,7 +122,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="entity.approveUser != null "> and t3.approve_user = #{entity.approveUser}</if>
<if test="entity.params.applyTimeStart != null and entity.params.applyTimeEnd != ''">
<choose>
<when test="params.applyTimeStart != null and params.applyTimeEnd != null">
<when test="entity.params.applyTimeStart != null and entity.params.applyTimeEnd != null">
and t3.apply_time between date_format(#{entity.params.applyTimeStart}, '%Y-%m-%d 00:00:00') and
date_format(#{entity.params.applyTimeEnd}, '%Y-%m-%d 23:59:59')
</when>