这一次就一鼓作气把Journal
类相关的内容全部写完。
展示日期的修改
由于Java
的Date
对象,其中的getYear()
等方法都被删除了,所以在展示凭证号的时候,就要使用Thymeleaf
提供的函数了,如下:
<td th:text="${#dates.format(journal.getNoteDate(), 'yyyy') +'-'+ #dates.format(journal.getNoteDate(), 'MM') + '-' + #numbers.formatDecimal(journal.getNoteNumber(),4,0,'POINT')}"></td>
修改Journal
类的功能
这个也是套路了,表单可以复用,只要把其他的页面修改一下就可以了,只要记得给journal
类加上一个生成自己的编辑URL
的方法就行了:
public String editUrl() {
int cid = contract.getId();
return "/pms/journal/" + cid + "/edit/" + id;
}
之后就来编写控制器。
控制器
控制器也是套路了:
// 显示编辑页面
@GetMapping("/{cid}/edit/{id}")
public String editJournalPage(@PathVariable("cid") int cid,
@PathVariable("id") int jid, Model model) {
Contract contract = contractService.getContractById(cid);
Set<Budget> budgets = contract.getBudgets();
Budget budget = (Budget) (budgets.toArray()[0]);
Project project = budget.getProject();
Journal journal = journalService.findById(jid);
model.addAttribute("project", project);
model.addAttribute("budgets", budgets);
model.addAttribute("contract", contract);
model.addAttribute("journal", journal);
return "pms/journal/journalEdit";
}
// 接受编辑修改
@PostMapping("/{cid}/edit/{id}")
public String editJournal(
@Valid @ModelAttribute("journal") Journal journal,
BindingResult rs, @PathVariable("cid") int cid,
@PathVariable("id") int jid, Model model,
RedirectAttributes attributes) {
Contract contract = contractService.getContractById(cid);
journal.setContract(contract);
Set<Budget> budgets = contract.getBudgets();
Budget budget = (Budget) (budgets.toArray()[0]);
Project project = budget.getProject();
model.addAttribute("project", project);
model.addAttribute("contract", contract);
model.addAttribute("budgets", budgets);
if (rs.hasErrors()) {
return "pms/journal/journalEdit";
}
if (!journal.isValid()) {
model.addAttribute("numberError", "借贷方不平,请检查数字。");
return "pms/journal/journalEdit";
}
journal.setContract(contract);
journal.setProject_id(project.getId());
journalService.save(journal);
attributes.addFlashAttribute("updated", "成功修改明细记录");
return "redirect:" + journal.absoluteUrl();
}
接受编辑修改的控制器其实和添加的控制器非常相似。然后是页面,其实也就是把添加页面拿过来修改一下:
<main class="container">
<nav style="--bs-breadcrumb-divider: '>';" aria-label="breadcrumb" class="mt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list" th:href="@{/pms/project/list}"
class="link-secondary">项目管理</a></li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${project.absoluteUrl()}"
class="link-secondary">[[${project.projectName}]]</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${contract.absoluteUrl()}"
class="link-secondary">[[${contract.name}]]</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${contract.journalListUrl()}"
class="link-secondary">明细记录列表</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${journal.editUrl()}"
class="link-secondary">编辑明细记录</a>
</li>
</ol>
</nav>
<div class="py-1 text-center">
<h2>编辑明细记录</h2>
</div>
<div class="row py-1">
<div class="col-sm-4"></div>
<div th:if="${numberError}" class="text-center alert alert-danger col-12 col-sm-4" role="alert">
[[${numberError}]]
</div>
<div class="col-sm-4"></div>
</div>
<div class="row g-5">
<div class="col-2"></div>
<div class="col-md-8 col-lg-8">
<p class="text-secondary">不需填写的数值请保留0.00字样,不要删除</p>
<form class="needs-validation" action="/pms/journal/add" th:action="${journal.editUrl()}" method="post"
th:object="${journal}">
<div class="row g-3 mb-3">
<h4 class="mb-1">基础信息</h4>
<div class="col-sm-6">
<p class="mb-0">所属预算条目</p>
<select class="form-select mt-2" aria-label="budget" name="budget" th:field="*{budget}">
<option th:each="budget, control:${budgets}" th:value="${budget.getId()}" >
[[${budget.budgetName}]]
</option>
</select>
</div>
<div class="col-sm-6">
<label for="description" class="form-label">说明</label>
<input type="text" class="form-control" id="description" th:field="*{description}"
th:classappend="${#fields.hasErrors('description')}? 'is-invalid'" required>
<div id="descriptionError" th:if="${#fields.hasErrors('description')}"
th:errors="*{description}"
class="invalid-feedback"></div>
</div>
<div class="col-6">
<label for="noteDate" class="form-label">凭证日期</label>
<input type="date" class="form-control" id="noteDate" th:field="*{noteDate}"
th:classappend="${#fields.hasErrors('noteDate')}? 'is-invalid'" required>
<div id="noteDateError" th:if="${#fields.hasErrors('noteDate')}"
th:errors="*{noteDate}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="noteNumber" class="form-label">凭证号</label>
<input type="number" step="1" class="form-control" id="noteNumber" th:field="*{noteNumber}"
th:classappend="${#fields.hasErrors('noteNumber')}? 'is-invalid'" required>
<div id="noteNumberError" th:if="${#fields.hasErrors('noteNumber')}"
th:errors="*{noteNumber}"
class="invalid-feedback"></div>
</div>
<hr>
<h4 class="mb-1">损益</h4>
<div class="col-sm-6">
<label for="revenuePL" class="form-label">营业收入</label>
<input type="number" step="0.01" class="form-control" id="revenuePL" th:field="*{revenuePL}"
th:classappend="${#fields.hasErrors('revenuePL')}? 'is-invalid'" required>
<div id="revenuePLError" th:if="${#fields.hasErrors('revenuePL')}"
th:errors="*{revenuePL}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="costPL" class="form-label">营业成本</label>
<input type="number" step="0.01" class="form-control" id="costPL" th:field="*{costPL}"
th:classappend="${#fields.hasErrors('costPL')}? 'is-invalid'" required>
<div id="costPLError" th:if="${#fields.hasErrors('costPL')}"
th:errors="*{costPL}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="otherPL" class="form-label">其他损益</label>
<input type="number" step="0.01" class="form-control" id="otherPL" th:field="*{otherPL}"
th:classappend="${#fields.hasErrors('otherPL')}? 'is-invalid'" required>
<div id="otherPLError" th:if="${#fields.hasErrors('otherPL')}"
th:errors="*{otherPL}"
class="invalid-feedback"></div>
</div>
<hr>
<h4 class="mb-1">资产</h4>
<div class="col-sm-6">
<label for="cash" class="form-label">货币资金</label>
<input type="number" step="0.01" class="form-control" id="cash" th:field="*{cash}"
th:classappend="${#fields.hasErrors('cash')}? 'is-invalid'" required>
<div id="cashError" th:if="${#fields.hasErrors('cash')}"
th:errors="*{cash}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="contractAsset" class="form-label">合同资产</label>
<input type="number" step="0.01" class="form-control" id="contractAsset" th:field="*{contractAsset}"
th:classappend="${#fields.hasErrors('contractAsset')}? 'is-invalid'" required>
<div id="contractAssetError" th:if="${#fields.hasErrors('contractAsset')}"
th:errors="*{contractAsset}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="contractCost" class="form-label">合同履约成本</label>
<input type="number" step="0.01" class="form-control" id="contractCost" th:field="*{contractCost}"
th:classappend="${#fields.hasErrors('contractCost')}? 'is-invalid'" required>
<div id="contractCostError" th:if="${#fields.hasErrors('contractCost')}"
th:errors="*{contractCost}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="contractAcquireCost" class="form-label">合同取得成本</label>
<input type="number" step="0.01" class="form-control" id="contractAcquireCost" th:field="*{contractAcquireCost}"
th:classappend="${#fields.hasErrors('contractAcquireCost')}? 'is-invalid'" required>
<div id="contractAcquireCostError" th:if="${#fields.hasErrors('contractAcquireCost')}"
th:errors="*{contractAcquireCost}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="accountReceivable" class="form-label">应收账款</label>
<input type="number" step="0.01" class="form-control" id="accountReceivable" th:field="*{accountReceivable}"
th:classappend="${#fields.hasErrors('accountReceivable')}? 'is-invalid'" required>
<div id="accountReceivableError" th:if="${#fields.hasErrors('accountReceivable')}"
th:errors="*{accountReceivable}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="prepaid" class="form-label">预付账款</label>
<input type="number" step="0.01" class="form-control" id="prepaid" th:field="*{prepaid}"
th:classappend="${#fields.hasErrors('prepaid')}? 'is-invalid'" required>
<div id="prepaidError" th:if="${#fields.hasErrors('prepaid')}"
th:errors="*{prepaid}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="inputVAT" class="form-label">进项税金</label>
<input type="number" step="0.01" class="form-control" id="inputVAT" th:field="*{inputVAT}"
th:classappend="${#fields.hasErrors('inputVAT')}? 'is-invalid'" required>
<div id="inputVATError" th:if="${#fields.hasErrors('inputVAT')}"
th:errors="*{inputVAT}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="otherAsset" class="form-label">其他资产</label>
<input type="number" step="0.01" class="form-control" id="otherAsset" th:field="*{otherAsset}"
th:classappend="${#fields.hasErrors('otherAsset')}? 'is-invalid'" required>
<div id="otherAssetError" th:if="${#fields.hasErrors('otherAsset')}"
th:errors="*{otherAsset}"
class="invalid-feedback"></div>
</div>
<hr>
<h4 class="mb-1">负债</h4>
<div class="col-sm-6">
<label for="contractLiability" class="form-label">合同负债</label>
<input type="number" step="0.01" class="form-control" id="contractLiability" th:field="*{contractLiability}"
th:classappend="${#fields.hasErrors('contractLiability')}? 'is-invalid'" required>
<div id="contractLiabilityError" th:if="${#fields.hasErrors('contractLiability')}"
th:errors="*{contractLiability}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="accountPayable" class="form-label">应付账款</label>
<input type="number" step="0.01" class="form-control" id="accountPayable" th:field="*{accountPayable}"
th:classappend="${#fields.hasErrors('accountPayable')}? 'is-invalid'" required>
<div id="accountPayableError" th:if="${#fields.hasErrors('accountPayable')}"
th:errors="*{accountPayable}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="outputVAT" class="form-label">销项税金</label>
<input type="number" step="0.01" class="form-control" id="outputVAT" th:field="*{outputVAT}"
th:classappend="${#fields.hasErrors('outputVAT')}? 'is-invalid'" required>
<div id="outputVATError" th:if="${#fields.hasErrors('outputVAT')}"
th:errors="*{outputVAT}"
class="invalid-feedback"></div>
</div>
<div class="col-sm-6">
<label for="otherLiability" class="form-label">其他负债</label>
<input type="number" step="0.01" class="form-control" id="otherLiability" th:field="*{otherLiability}"
th:classappend="${#fields.hasErrors('otherLiability')}? 'is-invalid'" required>
<div id="otherLiabilityError" th:if="${#fields.hasErrors('otherLiability')}"
th:errors="*{otherLiability}"
class="invalid-feedback"></div>
</div>
</div>
<div class="text-center mb-4">
<button class="btn btn-primary btn-lg" type="submit"><i class="fas fa-check"></i> 提交</button>
<a class="btn btn-secondary btn-lg" th:href="${journal.absoluteUrl()}" href="/pms/project/list"><i class="fas fa-undo-alt"></i> 返回</a>
</div>
</form>
</div>
<div class="col-2"></div>
</div>
</main>
感觉这里有点纯粹灌水的感觉了,其实代码都差不多。
详情页与删除功能
这里就继续灌水,写到这里大家如果一路看下来,对于套路也都熟悉了,控制器如下:
// 详情页面
@GetMapping("/detail/{id}")
public String detailPage(@PathVariable("id") int jid, Model model) {
Journal journal = journalService.findById(jid);
Contract contract = journal.getContract();
Set<Budget> budgets = contract.getBudgets();
Budget budget = (Budget) (budgets.toArray()[0]);
Project project = budget.getProject();
model.addAttribute("project", project);
model.addAttribute("contract", contract);
model.addAttribute("journal", journal);
return "pms/journal/journalDetail";
}
详情页面也是类似的,用一个表格来展示:
<main class="col-lg-8 mx-auto container">
<nav style="--bs-breadcrumb-divider: '>';" aria-label="breadcrumb" class="mt-3">
<ol class="breadcrumb">
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list" th:href="@{/pms/project/list}"
class="link-secondary">项目管理</a></li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${project.absoluteUrl()}"
class="link-secondary">[[${project.projectName}]]</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${project.contractListUrl()}"
class="link-secondary">合同列表</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${contract.absoluteUrl()}"
class="link-secondary">[[${contract.name}]]</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${contract.journalListUrl()}"
class="link-secondary">明细记录列表</a>
</li>
<li class="breadcrumb-item" aria-current="page"><a href="/pms/project/list"
th:href="${journal.absoluteUrl()}"
class="link-secondary">明细记录</a>
</li>
</ol>
</nav>
<h1 th:text="${journal.description}">明细记录</h1>
<div class="row py-1" >
<div class="col-sm-4"></div>
<div th:if="${updated}" class="text-center alert alert-success col-12 col-sm-4" role="alert">
[[${updated}]]
</div>
<div class="col-sm-4"></div>
</div>
<hr>
<div class="row g-5">
<div class="col-md-9">
<table class="table table-sm table-striped" th:object="${journal}">
<thead>
<tr>
<th>属性</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>说明</td>
<td th:text="*{description}"></td>
</tr>
<tr>
<td>凭证号</td>
<td th:text="${#dates.format(journal.getNoteDate(), 'yyyy') +'-'+ #dates.format(journal.getNoteDate(), 'MM') + '-' + #numbers.formatDecimal(journal.getNoteNumber(),4,0,'POINT')}"></td>
</tr>
<tr>
<td>损益-营业收入</td>
<td th:text="${#numbers.formatDecimal(journal.revenuePL,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>损益-营业成本</td>
<td th:text="${#numbers.formatDecimal(journal.costPL,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>损益-其他</td>
<td th:text="${#numbers.formatDecimal(journal.otherPL,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>资产-货币资金</td>
<td th:text="${#numbers.formatDecimal(journal.cash,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>资产-合同资产</td>
<td th:text="${#numbers.formatDecimal(journal.contractAsset,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>资产-合同履约成本</td>
<td th:text="${#numbers.formatDecimal(journal.contractCost,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>资产-合同取得成本</td>
<td th:text="${#numbers.formatDecimal(journal.contractAcquireCost,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>资产-应收账款</td>
<td th:text="${#numbers.formatDecimal(journal.accountReceivable,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>资产-预付账款</td>
<td th:text="${#numbers.formatDecimal(journal.prepaid,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>资产-进项税金</td>
<td th:text="${#numbers.formatDecimal(journal.inputVAT,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>资产-其他资产</td>
<td th:text="${#numbers.formatDecimal(journal.otherAsset,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>负债-合同负债</td>
<td th:text="${#numbers.formatDecimal(journal.contractLiability,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>负债-应付账款</td>
<td th:text="${#numbers.formatDecimal(journal.accountPayable,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>负债-销项税金</td>
<td th:text="${#numbers.formatDecimal(journal.outputVAT,1,'COMMA',2,'POINT')}"></td>
</tr>
<tr>
<td>负债-其他负债</td>
<td th:text="${#numbers.formatDecimal(journal.otherLiability,1,'COMMA',2,'POINT')}"></td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-3">
<h2>功能</h2>
<ul class="list-group list-group-flush">
<li class="list-group-item"><a href="/pms/project/detail" th:href="${journal.editUrl()}"><i
class="fas fa-pencil-alt"></i> 编辑本记录</a></li>
<li class="list-group-item"><a href="#" class="link-danger" data-bs-toggle="modal"
data-bs-target="#deleteModal"><i class="fas fa-trash-alt"></i> 删除本记录</a>
</li>
</ul>
</div>
</div>
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteLabel">删除付款结算记录</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
将删除[[${journal.description}]]。
</div>
<form class="modal-footer" method="post" action="/pms/project/delete" th:action="@{/pms/journal/delete}">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><i class="fas fa-undo"></i>
返回
</button>
<input type="hidden" name="deleteId" th:value="${journal.getId()}">
<input type="hidden" name="contract" th:value="${contract.id}">
<input type="hidden" name="name" th:value="${journal.getDescription()}">
<button type="submit" class="btn btn-danger"><i class="fas fa-info-circle"></i>
确定删除
</button>
</form>
</div>
</div>
</div>
</main>
这里因为已经把删除功能写好了,所以再添加一个删除控制器:
// 删除Journal
@PostMapping("/delete")
public String deleteJournal(@RequestParam("deleteId")int jid,
@RequestParam("name")String name,
@RequestParam("contract")int cid,
RedirectAttributes attributes) {
journalService.deleteJournal(jid);
Contract contract = contractService.getContractById(cid);
attributes.addFlashAttribute("deleted", "成功删除明细记录:" + name);
return "redirect:" + contract.journalListUrl();
}
之后还是老样子,在Spring Security
中加入对应的权限保护:
.antMatchers("/pms/journal/delete").hasAnyAuthority("ADMIN", "SUPERUSER")
这样就完成了删除功能的编写。最后就是把各种图标调整一下。
之后就是最后的功能,就是编写Budget
和Project
类中用来展示其汇总的所有明细记录的数据了。