组合关系
组合就是强聚合 ,聚合就是双向的多对一,一对多
强:最强级联 一方放弃关系维护
单据都会用到组合关系
保存的时候双方都能找到对方
Purchasebill
package com.xpc.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
//采购订单
@Entity
@Table(name = "purchasebill")
public class Purchasebill extends BaseDomain {
private Date vdate;//交易时间
@JoinColumn(name = "totalAmount")
private BigDecimal totalamount;//总金额(计算出来的)
@JoinColumn(name = "totalNum")
private BigDecimal totalnum;//总数量(计算数来的)
@JoinColumn(name = "inputTime")
private Date inputtime = new Date();//录入时间(自动生成)
@JoinColumn(name = "auditorTime")
private Date auditortime;//审核时间
private Integer status = 0;//单据状态 0:待审 1:已审 -1:作废
@ManyToOne(fetch = FetchType.LAZY,optional = false)
@JoinColumn(name = "supplier_id")
private Supplier supplier;//供应商
@ManyToOne
@JoinColumn(name = "auditor_id")
private Employee auditor;//审核员
@ManyToOne(fetch = FetchType.LAZY,optional = false)
@JoinColumn(name = "inputUser_id")
private Employee inputuser;//录入人
@ManyToOne(fetch = FetchType.LAZY,optional = false)
@JoinColumn(name = "buyer_id")
private Employee buyer;//采购员
@OneToMany(cascade = CascadeType.ALL, mappedBy = "bill", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Purchasebillitem> items = new ArrayList<Purchasebillitem>();
有关时间的get方法上加 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
有关时间的set方法上加 @DateTimeFormat(pattern = "yyyy-MM-dd")
}
Purchasebillitem
package com.xpc.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.math.BigDecimal;
//采购明细
@Entity
@Table(name = "purchasebillitem")
public class Purchasebillitem extends BaseDomain {
private BigDecimal price;//价格
private BigDecimal num;//数量
private BigDecimal amount;//小计
private String descs;//描述
@ManyToOne(fetch = FetchType.LAZY,optional = false)
@JoinColumn(name = "product_id")
private Product product;//产品
@ManyToOne(fetch = FetchType.LAZY,optional = false)
@JoinColumn(name = "bill_id")
@JsonIgnore //生成json的时候忽略这个字段
private Purchasebill bill;
}
Supplier
package com.xpc.domain;
import javax.persistence.*;
//供应商表
@Entity
@Table(name = "supplier")
public class Supplier extends BaseDomain {
private String name;
}
修改添加jsp和js,让数据都能正常的显示
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>采购管理</title>
<%--引入相应的css与js--%>
<%@include file="/WEB-INF/views/head.jsp" %>
<%-- shiro标签的引入 --%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<script src="/js/model/purchasebill.js"></script>
<style type="text/css">
.bodyCls{
min-height: 100px;
}
</style>
</head>
<body>
<%--pagination:分页条支持--%>
<table id="purchasebillGrid" class="easyui-datagrid"
data-options="url:'/purchasebill/page',
fitColumns:true,
singleSelect:true,
fit:true,
pagination:true,
toolbar:'#gridTools'"
enableHeaderClickMenu="true">
<%-- enableHeaderClickMenu="true" 隐藏字段--%>
<thead>
<tr>
<%-- sortable="true" 排序支持--%>
<th data-options="field:'vdate',width:100" sortable="true">交易时间</th>
<th data-options="field:'totalamount',width:100" sortable="true">总金额</th>
<th data-options="field:'totalnum',width:100" sortable="true">总数量</th>
<th data-options="field:'status',width:100,formatter:formatStatus" sortable="true">状态</th>
<th data-options="field:'supplier',width:100,formatter:formatObj" sortable="true">供应商</th>
<th data-options="field:'auditor',width:100,formatter:formatObj" sortable="true">审核员</th>
<th data-options="field:'inputuser',width:100,formatter:formatObj" sortable="true">录入人</th>
<th data-options="field:'buyer',width:100,formatter:formatObj" sortable="true">采购员</th>
</tr>
</thead>
</table>
<%--grid顶部工具栏--%>
<div id="gridTools" style="padding:5px;height:auto">
<%--功能条--%>
<div style="margin-bottom:5px">
<shiro:hasPermission name="employee:save">
<a href="#" data-method="add" class="easyui-linkbutton" iconCls="icon-add" plain="true">添加</a>
</shiro:hasPermission>
<shiro:hasPermission name="employee:update">
<a href="#" data-method="update" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
</shiro:hasPermission>
<shiro:hasPermission name="employee:delete">
<a href="#" data-method="del" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</shiro:hasPermission>
</div>
</div>
<%--添加与修改的表单对话框--%>
<div id="editDialog" class="easyui-dialog" title="功能编辑" style="width:900px;"
data-options="iconCls:'icon-save',resizable:true,modal:true,closed:true">
<form id="editForm" method="post">
<input id="purchasebill" type="hidden" name="id"/>
<table cellpadding="5">
<tr>
<td>交易时间:</td>
<td><input class="easyui-datebox" type="text" name="vdate"
data-options="required:true"></input>
</td>
<td>供应商:</td>
<td>
<input name="supplier.id" class="easyui-combobox" panelHeight="auto"
data-options="valueField:'id',textField:'name',url:'/util/supplier',required:true" />
</td>
<td>采购员:</td>
<td>
<input name="buyer.id" class="easyui-combobox"
data-options="valueField:'id',textField:'username',url:'/util/buyer',required:true" />
</td>
</tr>
</table>
</form>
<div style="text-align:center;padding:5px">
<a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
<a href="javascript:void(0)" class="easyui-linkbutton" data-method="closeDialog">关闭</a>
</div>
</div>
</body>
</html>
js
function formatObj(vale) {
return vale?vale.name || vale.username:"";
}
function formatStatus(vale) {
if(vale == -1){
return `<span style="color: grey"><s>作废</s></span>`;
}else if(vale == 0){
return `<span style="color: red">待审</span>`;
}else if(vale == 1){
return `<span style="color: green">已审</span>`;
}
}
$(function () {
//1.获取常用的一些组件
var purchasebillGrid = $("#purchasebillGrid");
var searchForm = $("#searchForm");
var editDialog = $("#editDialog");
var editForm = $("#editForm");
//2.绑定相应的事件
$("*[data-method]").on("click",function(){
var methodName = $(this).data("method");
window.itsource[methodName]();
})
itsource={
//添加
add(){
//让密码框失效且隐藏起来
$("*[data-edit]").show();
$("*[data-edit] input").validatebox("enable");
//1.清空form中的数据
editForm.form("clear");
//2.打开弹出框(居中)
editDialog.dialog("center").dialog("open");
},
//修改
update(){
//1.获取到选中的那一行数据
let row = purchasebillGrid.datagrid("getSelected");
//2.如果没有选中,给出提示,后面的代码就不再执行了
if(!row){
$.messager.alert('警告','没选中,无法修改',"warning");
return ;
}
//让密码框失效且隐藏起来
$("*[data-edit]").hide();
$("*[data-edit] input").validatebox("disable");
//把结果进行回显
/**
* 部门:<input name="department.id"
* row: department.id = 4 -> "department.id"= 4
*/
if(row.supplier){
row["supplier.id"] = row.supplier.id;
}
if(row.buyer){
row["buyer.id"] = row.buyer.id;
}
editForm.form("load",row);
//打开弹出框(居中)
editDialog.dialog("center").dialog("open");
},
//保存功能
save(){
var url = "/purchasebill/save";
//获到id的值
var purchasebill = $("#purchasebill").val();
if(purchasebill){
url = "/purchasebill/update?cmd=_update";
}
// 做一些检查
// 返回false可以阻止提交;
return $(this).form('validate');
},
//data : {success:true/false,msg:xxx} -> 字符串
success:function(data){
var result = JSON.parse(data);
if(result.success){
purchasebillGrid.datagrid("reload");
}else{
$.messager.alert('错误',`失败了, 原因是:${result.msg}`,"error");
}
//关闭弹出框
itsource.closeDialog();
}
});
},
//删除
del(){
//1.获取到选中的那一行数据
let row = purchasebillGrid.datagrid("getSelected");
//2.如果没有选中,给出提示,后面的代码就不再执行了
if(!row){
$.messager.alert('警告','没选中!',"warning");
return ;
}
//3.如果选中,确定是否要执行删除
$.messager.confirm('确认','您确认想要删除记录吗?',function(r){
if (r){
//4.如果确定删除,把id传到后台,后台删除数据
$.get("/purchasebill/delete",{id:row.id},function (result) {
//5.后台会返回 {success:true/false,msg:xxx}
//6.后台返回true:刷新数据 / 后台返回false:提示错误信息
if(result.success){
purchasebillGrid.datagrid("reload");
}else{
$.messager.alert('错误',`失败了 原因是:${result.msg}`,"error");
}
})
}
});
},
//查询
search(){
//serializeObject:拿到一个form中的所有数据,封装成json对象
var params = searchForm.serializeObject();
purchasebillGrid.datagrid("load",params);
},
//关闭窗口
closeDialog(){
editDialog.dialog("close");
}
}
})
- 然后完成条件查询的工具栏
jsp
<%--查询条--%>
<form id="searchForm">
日期:<input class="easyui-datebox" name="beginDate" style="width: 120px"></input>
- <input class="easyui-datebox" name="endDate" style="width: 120px"></input>
状态:<select class="easyui-combobox" name="status" data-options="panelHeight:'auto'" style="width: 110px">
<option value="">--请选择--</option>
<option value="0">待审</option>
<option value="-1">作废</option>
<option value="1">已审</option>
</select>
<a href="#" data-method="search" class="easyui-linkbutton" iconCls="icon-search">查询</a>
</form>
PurchasebillQuery
package com.xpc.query;
import java.util.Date;
import com.github.wenhao.jpa.Specifications;
import com.xpc.domain.Purchasebill;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.format.annotation.DateTimeFormat;
public class PurchasebillQuery extends BaseQuery{
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date beginDate;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date endDate;
private Integer status;
@Override
public Specification createSpec() {
Date addDays = null;
if(endDate != null)
addDays = DateUtils.addDays(endDate, 1); //结束时间加一,解决查不到当天非00:00:00的情况
Specification<Purchasebill> specification = Specifications.<Purchasebill>and()
.ge(beginDate!=null, "vdate",beginDate) //大于等于
.lt(addDays!=null, "vdate",addDays) //小于 小于就去掉了第二天0点的情况
.eq(status!=null,"status",status )//等于
.build();
return specification;
}
get/set方法省略
}
- 添加采购订单
jsp
<!-- 编辑框支持 -->
<script src="/easyui/plugin/cellEdit/jeasyui.extensions.datagrid.getColumnInfo.js"></script>
<script src="/easyui/plugin/cellEdit/jeasyui.extensions.datagrid.editors.js"></script>
<script src="/easyui/plugin/cellEdit/jeasyui.extensions.datagrid.edit.cellEdit.js"></script>
<%--明细数据--%>
<table id="itemsGrid"></table>
<div id="itemsTools">
<a href="javascript:void(0)" id="btnInsert" data-options="iconCls:'icon-add',plain:true" class="easyui-linkbutton">增加</a>
<a href="javascript:void(0)" id="btnRemove" data-options="iconCls:'icon-remove',plain:true" class="easyui-linkbutton">删除</a>
</div>
js
onSubmit: function(param){
//1.获取dg到所有的值
var rows = dg.datagrid("getRows");
//2.遍历所有的行
for(var i=0;i<rows.length;i++){
var row = rows[i];
//3.把值放到params中去
param[`items[${i}].product.id`] = row.product.id;
param[`items[${i}].num`] = row.num;
param[`items[${i}].price`] = row.price;
param[`items[${i}].descs`] = row.descs;
}
//这段代码要写在最后面
/**
* dg: 明细的grid组件
* defaultRow:默认的行数据(表头)
*insertPosition:插入数据的位置 (bottom:下面 top:上面)
*/
var dg = $("#itemsGrid"),
defaultRow = { id: "", product: "", productColor: "", productImg: "", num: "0", price: "0", amount: "0" ,descs:""},
insertPosition = "bottom";
var dgInit = function () {
var getColumns = function () {
var result = [];
var normal = [
{
field: 'product', title: '商品', width: 180,
editor: {
type: "combobox",
options: {
valueField:'id',
textField:'name',
panelHeight:'auto',
url:'/util/findProducts',
required: true
}
},
formatter(v){
return v?v.name:"";
}
},
{
field: 'productColor', title: '颜色', width: 80,
formatter(vaer,row,inde){
if(row && row.product){
return `<div style="width:25px;height:25px;background:${row.product.color};"></div>`;
}
}
},
{
field: 'productImg', title: '图片', width: 100,
formatter(vaer,row,inde){
if(row && row.product){
return `<img src="${row.product.smallpic}" width="50" height="50">`;
}
}
},
{
field: 'num', title: '数量', width: 100,
editor: {
type: "numberbox",
options: {
precision:2, //精确到小数点后面两位
required: true
}
}
},
{
field: 'price', title: '价格', width: 100,
editor: {
type: "numberbox",
options: {
precision:2, //精确到小数点后面两位
required: true
}
}
},
{
field: 'amount', title: '小计', width: 100,
formatter(vaer,row,inde){
if(row && row.num && row.price){
return (row.num*row.price).toFixed(2);
}
return 0;
}
},
{
field: 'descs', title: '备注', width: 100,
editor: {
type: "text"
}
}
];
result.push(normal);
return result;
};
var options = {
idField: "ID", //id的字段(唯一的)
rownumbers: true, // 行号
fitColumns: true, //列的自适应
fit: true, //自适应父窗口
border: true, //是否显示边框
singleSelect: true,
columns: getColumns(),
toolbar:'#itemsTools',
bodyCls:"bodyCls",
//表示开启单元格编辑功能
enableCellEdit: true
};
dg.datagrid(options);
};
//拿到插入的那一行数据的索引
var getInsertRowIndex = function () {
return insertPosition == "top" ? 0 : dg.datagrid("getRows").length;
}
var buttonBindEvent = function () {
//添加一行数据
$("#btnInsert").click(function () {
var targetIndex = getInsertRowIndex(), targetRow = $.extend({}, defaultRow, { ID: $.util.guid() });
dg.datagrid("insertRow", { index: targetIndex, row: targetRow });
dg.datagrid("editCell", { index: 0, field: "Code" });
});
//删除一行数据
$("#btnRemove").click(function () {
var row = dg.datagrid("getSelected");
if(row){
//2.这里要的是这一行的行号
var index = dg.datagrid("getRowIndex",row);
//3.把这一行数据删除掉
dg.datagrid("deleteRow",index);
}
});
/*$("#btnSave").click(function () {
var rows = dg.datagrid("getRows"), len = rows.length;
for (var i = 0; i < len; i++) {
dg.datagrid("endEdit", i);
}
});*/
};
//把grid初始化与事务绑定完成
dgInit(); buttonBindEvent();
- 后台保存与计算
PurchasebillController
public JsonResult saveOrUpdate(Purchasebill purchasebill){
//拿到订单的所有明细
List<Purchasebillitem> items = purchasebill.getItems();
//准备总数量与总金额
BigDecimal totalNum = new BigDecimal("0");
BigDecimal totalAmount = new BigDecimal("0");
for(Purchasebillitem list:items){
//确定明细对应的订单
list.setBill(purchasebill);
//计算当前数据的小计 multiply:乘
BigDecimal amount = list.getPrice().multiply(list.getNum());
list.setAmount(amount);
//计算总数量与总金额
totalNum = totalNum.add(list.getNum());
totalAmount = totalAmount.add(list.getAmount());
}
//设置总数量与总金额
purchasebill.setTotalnum(totalNum);
purchasebill.setTotalamount(totalAmount);
try {
purchasebillService.save(purchasebill);
return new JsonResult();
} catch (Exception e) {
e.printStackTrace();
return new JsonResult(false,e.getMessage());
}
}
- 修改数据回显
js
update(){
...
if(row.supplier){
row["supplier.id"] = row.supplier.id;
}
if(row.buyer){
row["buyer.id"] = row.buyer.id;
}
...
//拿到这行数据中的items(明细)
var items = [...row.items];
dg.datagrid("loadData",items);
add(){
...
//1.清空form中的数据
editForm.form("clear");
//清空grid中的数据
dg.datagrid("loadData",[]);
n to n问题解决
//解决n-to-n的问题,把关联对象设置为null
dbPurchasebill.setSupplier(null);//供应商
dbPurchasebill.setBuyer(null);//采购员
dbPurchasebill.getItems().clear();