Yunshang Office System: Mitarbeitergenehmigung
Direkter Zugang zur Station B [wie Silicon Valley] :
https://www.bilibili.com/video/BV1Ya411S7aT
Dieser Blog-Beitrag wird hauptsächlich im Zusammenhang mit dem Kurs veröffentlicht, enthält einige meiner eigenen Meinungen und bietet relevante Lösungen für die Probleme, die im Lernprozess auftreten. Gemeinsam lernen und gemeinsam Fortschritte machen! ! !
Im Teil zur Mitarbeitergenehmigung sind mir mehrere Probleme und Fehler aufgefallen
1. Im Front-End-Code der Mitarbeiterseite in der Ressource war die Version von Node-Sass zu problematisch. Am Ende wusste ich wirklich nicht, was ich tun sollte, also habe ich die Version von Node (10 oder 14 in der Ressource) herabgestuft, da ich das Haupt-Back-End bin und das Wissen über das Front-End begrenzt sein wird. Ich habe auch im Internet gesucht, um die Version in package.json zu ändern, aber es hat nicht funktioniert. Deshalb habe ich Ihnen bereits beim ersten Hinweis gesagt, dass Sie die Version ändern sollen. Wenn Sie das Projektstartproblem nach der Suche immer noch nicht lösen können, können Sie ein Downgrade durchführen oder die MVN-Verwaltung [selbst
suchen
] 3. Folgendes ist mir aufgefallen: Die Seite wurde auf P98 nicht normal angezeigt. Ich habe immer noch nicht herausgefunden, warum, also habe ich dieses hinzugefügt und die Seite kaum normal angezeigt, etwa 172 Zeilen für (let i = 0; i < Response.data.records.length; i++) { let item = Response.data.records[i]; try { item.formValues = JSON.parse(item.formValues); this.list.pus h(item); } Catch (error) { console.error("JSON-Parsing-Fehler: " + error); console.log("Unparsable JSON string: " + item.formValues); } }
Artikelverzeichnis
- Yunshang Office System: Mitarbeitergenehmigung
-
- 1. Funktionsbeschreibung
- 2. Mitarbeitergenehmigung
-
- 1. OA-Genehmigung
- 2. Genehmigungsantrag
- 3. Ausstehende Liste
- 4. Einzelheiten zur Genehmigung
- 5. Genehmigung
- 6. Verarbeitet
- 7. Initiiert
- Drei, meine Güte
1. Funktionsbeschreibung
Das Hintergrundmanagement hat die Genehmigung bereits entworfen und Mitarbeiter können den erforderlichen Genehmigungsantrag über das mobile Endgerät einreichen. Der nächste Schritt ist der Kernprozess der Activiti-Genehmigung. Die Genehmigung durch die für die Genehmigung verantwortliche Person umfasst: Genehmigung und Ablehnung der Genehmigung. Nachdem die Genehmigung bestanden wurde, wird sie automatisch zum nächsten Genehmigungsknoten weitergeleitet. Wenn die Genehmigung abgelehnt wird, wird sie direkt zum Endknoten weitergeleitet und der Prozess endet.
2. Mitarbeitergenehmigung
Wenn es Ihnen unangenehm ist, es auf dem PC anzusehen, können Sie es auf F12 ansehen
1. OA-Genehmigung
Die OA-Genehmigung erfolgt auf dem mobilen Endgerät, das von den Mitarbeitern des Unternehmens verwendet wird, wie in der Abbildung dargestellt:
1.1. Abfragegenehmigungsklassifizierung und Vorlagenschnittstelle
Besorgen Sie sich die Genehmigungsklassifizierung und die entsprechende Genehmigungsvorlage
1.1.1. Definieren Sie die Serviceschnittstelle
Operationsklasse: ProcessTypeService
List<ProcessType> findProcessType();
1.1.2, Implementierung der Serviceschnittstelle
@Autowired
private ProcessTemplateService processTemplateService;
@Override
public List<ProcessType> findProcessType() {
//1 查询所有审批分类,返回list集合
List<ProcessType> processTypeList = baseMapper.selectList(null);
//2 遍历返回所有审批分类list集合
for (ProcessType processType:processTypeList) {
//3 得到每个审批分类,根据审批分类id查询对应审批模板
//审批分类id
Long typeId = processType.getId();
//根据审批分类id查询对应审批模板
LambdaQueryWrapper<ProcessTemplate> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ProcessTemplate::getProcessTypeId,typeId);
List<ProcessTemplate> processTemplateList = processTemplateService.list(wrapper);
//4 根据审批分类id查询对应审批模板数据(List)封装到每个审批分类对象里面
processType.setProcessTemplateList(processTemplateList);
}
return processTypeList;
}
1.1.3, Controller-Schnittstelle
Erstellen Sie die Klasse ProcessApiController
@Api(tags = "审批流管理")
@RestController
@RequestMapping(value="/admin/process")
@CrossOrigin //跨域
public class ProcessApiController {
@Autowired
private ProcessTypeService processTypeService;
@ApiOperation(value = "获取全部审批分类及模板")
@GetMapping("findProcessType")
public Result findProcessType() {
return Result.ok(processTypeService.findProcessType());
}
}
1.2. Front-End-Entwicklung
1.2.1. Verweisen Sie auf die Front-End-Entwicklungsumgebung
Importieren Sie das Front-End-Projekt-Framework aus der Ressourcenbibliothek: guigu-oa-web, die statische Ressourcenseiten enthält, die wir direkt verwenden.
Schritt 1: Abhängigkeiten installieren
npm install
Schritt zwei: Beginnen
npm run serve
Schritt 3: Konfiguration ändern
1. Ändern Sie den API-Basispfad der Datei src/utils/request.js
2. Ändern Sie die Token-Informationen der Datei src/utils/request.js.
Verwenden Sie das Token-Tool theoretisch, um ein temporäres Token für die Verwendung zu generieren. Wir werden die WeChat-Autorisierungsanmeldung in Zukunft verbessern
1.2.2. Definieren Sie die API-Schnittstelle
Erstellen Sie src/api/process.js
import request from '@/utils/request'
const api_name = '/admin/process'
export default {
findProcessType() {
return request({
url: `${
api_name}/findProcessType`,
method: 'get'
})
}
}
1.2.3. Routing hinzufügen
Fügen Sie Routing in src/router/index.js hinzu
{
path: '/',
name: 'OA审批',
component: () =>
import('../views/index.vue'),
}
1.2.4, Seitenrendering
Ansichten/index.vue-Seite
<template>
<div>
<div class="container">
<van-nav-bar
title="OA审批"
/>
<van-collapse v-model="activeNames">
<van-collapse-item
v-for="(item,key) in list"
:title="item.name"
:name="item.key">
<van-row>
<van-col v-for="template in item.processTemplateList" :key="template.id" span="6">
<div class="item" @click="apply(template.id)">
<img :src="template.iconUrl"/>
<span>{
{template.name}}</span>
</div>
</van-col>
</van-row>
</van-collapse-item>
</van-collapse>
</div>
</div>
</template>
<script>
import api from '@/api/process'
export default {
name: "process",
data() {
return {
list: [],
activeNames: [0,1,2,3,4]
};
},
created(){
this.fetchData();
},
methods: {
fetchData() {
api.findProcessType().then(response => {
console.log(response.data);
this.list = response.data;
//全部展开
this.activeNames = []
this.list.forEach((item,index) => {
this.activeNames.push(index)
})
});
},
apply(id) {
this.$router.push({ path: '/apply/'+id })
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding-bottom: 50px;
.item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
width: 40px;
height: 40px;
border-radius: 10px;
}
span {
font-size: 12px;
padding: 8px 4px;
text-align: center;
}
}
}
</style>
2. Genehmigungsantrag
Rendern Sie dynamische Formulare basierend auf Genehmigungsvorlagen und starten Sie Prozessinstanzen basierend auf dynamischen Formularen
2.1. Erhalten Sie Genehmigungsvorlagendaten
Operationsklasse: ProcessApiController
@Autowired
private ProcessTemplateService processTemplateService;
@ApiOperation(value = "获取审批模板")
@GetMapping("getProcessTemplate/{processTemplateId}")
public Result get(@PathVariable Long processTemplateId) {
ProcessTemplate processTemplate = processTemplateService.getById(processTemplateId);
return Result.ok(processTemplate);
}
2.2. Rendering der Seite des Genehmigungsantrags
2.2.1. Schnittstelle definieren
Fügen Sie die Schnittstelle in der Datei src/api/process.js hinzu
getProcessTemplate(processTemplateId) {
return request({
url: `${
api_name}/getProcessTemplate/`+processTemplateId,
method: 'get'
})
}
2.2.2. Dynamische Formen rendern
Die Initialisierungsumgebung wurde integriert: Mit der Form-Create-Komponente können wir die von der Form-Designer-Komponente gespeicherten Daten direkt rendern
Erstellen Sie src/views/apply.vue
<template>
<div>
<van-nav-bar
title="发起审批"
left-text="返回"
left-arrow
@click-left="() => $router.back()"
/>
<div style="margin: 10px 10px 0 0;border: 0px solid red;">
<form-create
:rule="rule"
:option="option"
@submit="onSubmit"
></form-create>
</div>
</div>
</template>
<script>
import api from "@/api/process";
export default {
name: "process",
data() {
return {
processTemplateprocessTemplate: null,
rule: [],
option: {}
};
},
created() {
let processTemplateId = this.$route.params.processTemplateId;
this.fetchData(processTemplateId);
},
methods: {
fetchData(processTemplateId) {
api.getProcessTemplate(processTemplateId).then(response => {
console.log(response.data);
this.processTemplate = response.data;
this.rule = JSON.parse(this.processTemplate.formProps);
this.option = JSON.parse(this.processTemplate.formOptions);
});
},
onSubmit(formData) {
console.log(formData)
}
}
};
</script>
<style lang="scss" scoped>
.el-form {
.el-form-item {
/deep/ .el-form-item__label {
font-size: 18px;
font-weight: 800;
color: blue;
}
}
}
</style>
2.2.3. Testen
Nehmen wir als Beispiel Überstunden, alles andere ist in Ordnung
Druckdaten übermitteln:
Drucken Sie Daten für unsere benutzerdefinierten Formularattribute und für Eingabewerte.
Die Folgedaten werden auf zwei Arten genutzt:
1. Detaillierte Anzeige der Genehmigung
2. Beurteilen Sie als Prozessinstanzparameter die Prozessrichtung, wie in der folgenden Abbildung dargestellt:
2.2.4. Daten kapseln und übermitteln
onSubmit(formData) {
console.log(formData)
let formShowData = {
};
this.rule.forEach(item => {
for (let key in formData) {
if (key === item.field) {
console.log(item.title, formData[key]);
formShowData[item.title] = formData[key];
}
}
});
let DATA = {
formData: formData,
formShowData: formShowData
};
console.log(DATA);
let processFormVo = {
"processTemplateId": this.processTemplate.id,
"processTypeId": this.processTemplate.processTypeId,
"formValues": JSON.stringify(DATA)
};
console.log(processFormVo)
}
Druckdaten
Wir konvertieren die Formulardaten in zwei Teile: formData-Formulardaten und formShowData-Formularanzeigedaten und konstruieren sie in ein formValues-Attribut-JSON-Objekt, das bei späterer Verwendung leicht abgerufen werden kann
2.3. Starten Sie die Prozessinstanz
2.3.1. Ändern Sie den Authentifizierungsfilter
Das Spring-Security-Modul fügt eine Toolklasse hinzu, um die aktuellen Anmeldeinformationen über ThreadLocal aufzuzeichnen
package com.atguigu.security.custom;
/**
* 获取当前用户信息帮助类
*/
public class LoginUserInfoHelper {
private static ThreadLocal<Long> userId = new ThreadLocal<Long>();
private static ThreadLocal<String> username = new ThreadLocal<String>();
public static void setUserId(Long _userId) {
userId.set(_userId);
}
public static Long getUserId() {
return userId.get();
}
public static void removeUserId() {
userId.remove();
}
public static void setUsername(String _username) {
username.set(_username);
}
public static String getUsername() {
return username.get();
}
public static void removeUsername() {
username.remove();
}
}
Ändern Sie den Filter TokenAuthenticationFilter
Fügen Sie Folgendes hinzu
/**
* <p>
* 认证解析token过滤器
* </p>
*/
public class TokenAuthenticationFilter extends OncePerRequestFilter {
...................
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
....................
if (!StringUtils.isEmpty(username)) {
//通过ThreadLocal记录当前登录人信息
LoginUserInfoHelper.setUserId(JwtHelper.getUserId(token));
LoginUserInfoHelper.setUsername(username);
....................
}
}
2.3.2. Kapseln Sie das Startprozessobjekt
Für Genehmigungsanwendungsattribute
@Data
@ApiModel(description = "流程表单")
public class ProcessFormVo {
@ApiModelProperty(value = "审批模板id")
private Long processTemplateId;
@ApiModelProperty(value = "审批类型id")
private Long processTypeId;
@ApiModelProperty(value = "表单值")
private String formValues;
}
2.3.3, Service-Realisierungsfunktion
@Autowired
private ProcessTemplateService processTemplateService;
@Autowired
private SysUserService sysUserService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
public void startUp(ProcessFormVo processFormVo) {
SysUser sysUser = sysUserService.getById(LoginUserInfoHelper.getUserId());
ProcessTemplate processTemplate = processTemplateService.getById(processFormVo.getProcessTemplateId());
Process process = new Process();
BeanUtils.copyProperties(processFormVo, process);
String workNo = System.currentTimeMillis() + "";
process.setProcessCode(workNo);
process.setUserId(LoginUserInfoHelper.getUserId());
process.setFormValues(processFormVo.getFormValues());
process.setTitle(sysUser.getName() + "发起" + processTemplate.getName() + "申请");
process.setStatus(1);
processMapper.insert(process);
//绑定业务id
String businessKey = String.valueOf(process.getId());
//流程参数
Map<String, Object> variables = new HashMap<>();
//将表单数据放入流程实例中
JSONObject jsonObject = JSON.parseObject(process.getFormValues());
JSONObject formData = jsonObject.getJSONObject("formData");
Map<String, Object> map = new HashMap<>();
//循环转换
for (Map.Entry<String, Object> entry : formData.entrySet()) {
map.put(entry.getKey(), entry.getValue());
}
variables.put("data", map);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processTemplate.getProcessDefinitionKey(), businessKey, variables);
//业务表关联当前流程实例id
String processInstanceId = processInstance.getId();
process.setProcessInstanceId(processInstanceId);
//计算下一个审批人,可能有多个(并行审批)
List<Task> taskList = this.getCurrentTaskList(processInstanceId);
if (!CollectionUtils.isEmpty(taskList)) {
List<String> assigneeList = new ArrayList<>();
for(Task task : taskList) {
SysUser user = sysUserService.getByUsername(task.getAssignee());
assigneeList.add(user.getName());
//推送消息给下一个审批人,后续完善
}
process.setDescription("等待" + StringUtils.join(assigneeList.toArray(), ",") + "审批");
}
processMapper.updateById(process);
}
/**
* 获取当前任务列表
* @param processInstanceId
* @return
*/
private List<Task> getCurrentTaskList(String processInstanceId) {
List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
return tasks;
}
2.3.4, Controller-Schnittstelle
@Autowired
private ProcessService processService;
@ApiOperation(value = "启动流程")
@PostMapping("/startUp")
public Result start(@RequestBody ProcessFormVo processFormVo) {
processService.startUp(processFormVo);
return Result.ok();
}
2.4. Aktualisieren Sie die Front-End-Übermittlungsschnittstelle
1. API-Schnittstelle hinzufügen
startUp(processFormVo) {
return request({
url: `${
api_name}/startUp`,
method: 'post',
data: processFormVo
})
},
2. Übermittlungsschnittstelle aktualisieren
onSubmit(formData) {
...
console.log(processFormVo)
api.startUp(processFormVo).then(response => {
//调整到已发起列表
this.$router.push({
path: "/list/2" });
});
}
2.5. Einreichungsdatensatz aufzeichnen
Daten, die den folgenden ähneln
Wir müssen das Betriebsverhalten jedes Knotens aufzeichnen
2.5.1, Mapper-Klasse
package com.atguigu.process.mapper;
import com.atguigu.model.process.ProcessRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProcessRecordMapper extends BaseMapper<ProcessRecord> {
}
2.5.2, Serviceschnittstelle
package com.atguigu.process.service;
import com.atguigu.model.process.ProcessRecord;
import com.baomidou.mybatisplus.extension.service.IService;
public interface ProcessRecordService extends IService<ProcessRecord> {
void record(Long processId, Integer status, String description);
}
2.5.3, Serviceschnittstelle
package com.atguigu.process.service.impl;
import com.atguigu.model.process.ProcessRecord;
import com.atguigu.model.system.SysUser;
import com.atguigu.process.mapper.ProcessRecordMapper;
import com.atguigu.process.service.ProcessRecordService;
import com.atguigu.security.custom.LoginUserInfoHelper;
import com.atguigu.system.service.SysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@SuppressWarnings({
"unchecked", "rawtypes"})
public class ProcessRecordServiceImpl extends ServiceImpl<ProcessRecordMapper, ProcessRecord> implements ProcessRecordService {
@Autowired
private ProcessRecordMapper processRecordMapper;
@Autowired
private SysUserService sysUserService;
@Override
public void record(Long processId, Integer status, String description) {
SysUser sysUser = sysUserService.getById(LoginUserInfoHelper.getUserId());
ProcessRecord processRecord = new ProcessRecord();
processRecord.setProcessId(processId);
processRecord.setStatus(status);
processRecord.setDescription(description);
processRecord.setOperateUserId(sysUser.getId());
processRecord.setOperateUser(sysUser.getName());
processRecordMapper.insert(processRecord);
}
}
2.5.4. Ändern Sie die Startprozessschnittstelle
@Autowired
private ProcessRecordService processRecordService;
public void startUp(ProcessFormVo processFormVo) {
...
//记录操作行为
processRecordService.record(process.getId(), 1, "发起申请");
}
3. Ausstehende Liste
Nach dem Starten des Prozesses kann der Genehmiger die Genehmigungsliste in der Liste „Ausstehend“ abrufen
3.1. Serverschnittstelle
3.1.1. Schnittstelle definieren
IPage<ProcessVo> findPending(Page<Process> pageParam);
3.1.2. Schnittstellenimplementierung
@Override
public IPage<ProcessVo> findPending(Page<Process> pageParam) {
// 根据当前人的ID查询
TaskQuery query = taskService.createTaskQuery().taskAssignee(LoginUserInfoHelper.getUsername()).orderByTaskCreateTime().desc();
List<Task> list = query.listPage((int) ((pageParam.getCurrent() - 1) * pageParam.getSize()), (int) pageParam.getSize());
long totalCount = query.count();
List<ProcessVo> processList = new ArrayList<>();
// 根据流程的业务ID查询实体并关联
for (Task item : list) {
String processInstanceId = item.getProcessInstanceId();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (processInstance == null) {
continue;
}
// 业务key
String businessKey = processInstance.getBusinessKey();
if (businessKey == null) {
continue;
}
Process process = this.getById(Long.parseLong(businessKey));
ProcessVo processVo = new ProcessVo();
BeanUtils.copyProperties(process, processVo);
processVo.setTaskId(item.getId());
processList.add(processVo);
}
IPage<ProcessVo> page = new Page<ProcessVo>(pageParam.getCurrent(), pageParam.getSize(), totalCount);
page.setRecords(processList);
return page;
}
3.1.3, Controller-Schnittstelle
@ApiOperation(value = "待处理")
@GetMapping("/findPending/{page}/{limit}")
public Result findPending(
@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit) {
Page<Process> pageParam = new Page<>(page, limit);
return Result.ok(processService.findPending(pageParam));
}
3.2. Front-End-Rendering
3.2.1. Definieren Sie die API-Schnittstelle
Fügen Sie eine Schnittstelle in src/api/process.js hinzu
findPending(page, limit) {
return request({
url: `${
api_name}/findPending/`+page+`/`+ limit,
method: 'get'
})
}
3.2.2. Route hinzufügen
Fügen Sie Routing in src/router/index.js hinzu
Die Liste verfügt über drei Registerkartenschalter. activeIndex stellt die Sequenznummer des Schalters dar: 0: Genehmigung ausstehend, 1: genehmigt, 2: eingeleitet
{
path: '/list/:activeIndex',
name: '审批列表',
component: () =>
import('../views/list.vue'),
},
3.2.3, Seitenrendering
Erstellen Sie src/views/list.vue
<template>
<div class="container">
<van-nav-bar
title="审批列表"
/>
<van-tabs v-model="activeIndex" @click="tabSwitch">
<van-tab
v-for="(item,key) in tabList"
:key="key"
:title="item.title"
>
</van-tab>
</van-tabs>
<div class="list-wrap" >
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
:immediate-check="false"
>
<van-cell v-for="item in list" :key="item.id" @click="info(item.id, item.taskId)">
<template slot="default">
<div class="item-wrap">
<div class="item-header">
<img src="https://static.dingtalk.com/media/lALOnahFD80CgM0CgA_640_640.png_450x10000q90.jpg" alt="">
<h3>{
{item.title}}</h3>
<span>{
{item.createTime}}</span>
</div>
<div class="item-block">
<p v-for="(value,key) in item.formValues.formShowData" v-if="key !== '图片'">{
{ key }}:<span v-html="value"></span></p>
</div>
<div class="item-status">
<span :class="item.status === 1 ? '审批中' : item.status === 2 ? 'pass' : 'refused'">{
{ item.status === 1 ? '审批中' : item.status === 2 ? '审批通过' : '审批拒绝' }}</span>
</div>
</div>
</template>
</van-cell>
</van-list>
</van-pull-refresh>
</div>
</div>
</template>
<script>
import api from '@/api/process'
export default {
name: "process",
data() {
return {
list: [],
loading: false,
finished: false,
refreshing: false,
pageNo: 1,
pageSize: 10,
pages: 1,
activeIndex: 0,
tabList: [
{ title:"待处理", },
{ title:"已处理", },
{ title:"已发起", }
]
};
},
created(){
this.activeIndex = parseInt(this.$route.params.activeIndex);
this.onLoad()
},
methods: {
tabSwitch() {
//tab切换,重新初始化数据
this.list = []
this.pageNo = 1
this.finished = false
//tabs切换时,如果之前的tab已经滚动到底部(list加载到底部),直接点击其他的tab,将再触发一次onload事件。
//可能调用2次onLoad()方法,延迟执行,通过时间差解决问题
setTimeout(() => {
if(!this.finished) {
this.onLoad();
}
}, 500);
},
onLoad() {
if(this.activeIndex === 0) {
this.findPending()
}
if(this.activeIndex === 1) {
this.findProcessed()
}
if(this.activeIndex === 2) {
this.findStarted()
}
},
onRefresh() {
// 清空列表数据
this.finished = false;
this.pageNo = 1;
// 重新加载数据
// 将 loading 设置为 true,表示处于加载状态
this.loading = true;
this.onLoad();
},
findPending() {
console.log(this.pageNo)
api.findPending(this.pageNo, this.pageSize).then(response => {
console.log(response.data);
if (this.refreshing) {
this.list = [];
this.refreshing = false;
}
for (let i=0;i<response.data.records.length;i++) {
let item = response.data.records[i]
item.formValues = JSON.parse(item.formValues)
this.list.push(item);
}
this.pages = response.data.pages;
this.loading = false;
if(this.pageNo >= this.pages) {
this.finished = true;
}
this.pageNo++;
});
},
findProcessed() {
},
findStarted() {
},
info(id, taskId) {
this.$router.push({ path: '/show/'+id+'/'+taskId })
}
}
}
</script>
<style lang="scss" scoped>
/deep/ .van-nav-bar {
background: #1D1E20;
}
/deep/ .van-nav-bar__title {
color: #fff;
}
.container {
padding-bottom: 50px;
.list-wrap {
margin-top: 4px;
border-top: 1px solid #ebedf0;
}
.item-wrap {
font-size: 12px;
color: #A7A8A9;
.item-header {
display: flex;
align-items: center;
img {
width: 20px;
height: 20px;
border-radius: 4px;
margin-right: 4px;
}
h3 {
flex: 1;
font-size: 15px;
color: #000;
padding: 0;
margin: 0;
}
}
.item-block {
padding: 4px 0;
font-size: 14px;
p {
padding: 0;
margin: 0;
line-height: 20px;
}
}
.item-status {
.pass {
color: #4CB971;
}
.refused {
color: #EB8473;
}
}
}
}
</style>
3.2.4. Testen
Derzeit ist es vom Abteilungsleiter genehmigt. Wechseln Sie den Token zum Konto des Abteilungsleiters und sehen Sie sich die ausstehende Genehmigung an
Um das Testen zu erleichtern, erstellen wir eine Seite zum Kontowechsel
1. Routing hinzufügen
{
path: '/test',
name: '切换测试账号',
component: () =>
import('../views/test.vue'),
}
2. Erstellen Sie eine neue Vue-Seite
Neue Ansichten/test.vue
Beschreibung: Das Token ist möglicherweise abgelaufen und kann einmalig dynamisch über die Klasse JwtHelper.java generiert werden
<template>
<div>
<div>账号切换</div>
<button @click="wjl()" type="default" size="mini">王经理</button>
<button @click="rsjl()" type="default" size="mini">李人事经理</button>
<button @click="zzjl()" type="default" size="mini">张总经理</button>
<button @click="lisi()" type="default" size="mini">李四</button>
<div>当前token:{
{ token }}</div>
</div>
</template>
<script>
export default {
name: "Test",
data() {
return {
token: ''
};
},
created(){
},
methods: {
wjl() {
window.localStorage.setItem('token', '');
let token = 'eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAKtWKi5NUrJScgwN8dANDXYNUtJRSq0oULIyNDM3tDA3NTQ311EqLU4t8kxRsjKCMPMSc1OBWrIy89JzMoHqofznfd3Pd_c_n9CmVAsAgwZGsFYAAAA.H9lJkVALwz35h4BN1TNCAh1FttynUkIcsSdDJr46sA8O7pHtDZLA2TCNlTiPFI5ifYJ3nEyPdQnlqq1KM_dR3A'
window.localStorage.setItem('token', token);
this.token = window.localStorage.getItem('token')
},
rsjl() {
window.localStorage.setItem('token', '');
let token = 'eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAKtWKi5NUrJScgwN8dANDXYNUtJRSq0oULIyNDM3tDA3NTIz11EqLU4t8kxRsjKGMPMSc1OBWoqKs3KAqqG8Z3P7nuza9WRX9_Pd_c8ntCnVAgAxtvYPWgAAAA.za6RgrrHFBfBFudpawIwHB4EfKloakef0CEmXwITPFpaS7LC2RJ7a2uFw4MwA9FlQS_YTm2xSPmDBI_zDUOQbQ'
window.localStorage.setItem('token', token);
this.token = window.localStorage.getItem('token')
},
zzjl() {
window.localStorage.setItem('token', '');
let token = 'eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAKtWKi5NUrJScgwN8dANDXYNUtJRSq0oULIyNDM3tDA3NTYy1VEqLU4t8kxRsoIy8xJzU4FaqrJygIqhnKd7Fjxr2P18d__zCW1KtQAR8Ch1VgAAAA.szrvYa3nJprMhVjLGcGZ1mptv0Q5nQDOu81l4CtvtMXtEzsSuEUrf3sHL8v9jJF30Iq2qUXUMQYBgD5kYapd_A'
window.localStorage.setItem('token', token);
this.token = window.localStorage.getItem('token')
},
lisi() {
window.localStorage.setItem('token', '');
let token = 'eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAKtWKi5NUrJScgwN8dANDXYNUtJRSq0oULIyNDM3NjA1M7M001EqLU4t8kxRsjKBMPMSc1OBWnIyizOVagG7ronSQQAAAA.Tw_w8JwifsxxQQVPQWTfTiJb3AL8xA2v9DcfZrqxm8R0Lgy3qAA9Sf5NPcVpFsdwip7gWdX31qwzUf10LAnM2w'
window.localStorage.setItem('token', token);
this.token = window.localStorage.getItem('token')
}
}
}
</script>
3. Zugriffstest
http://localhost:9090/#/test
Wer in Zukunft eine Genehmigung benötigt, wechselt einfach auf diese Seite
4. Einzelheiten zur Genehmigung
Die Genehmigung gliedert sich in 2 Teile:
1. Einzelheiten zur Genehmigung
2. Ausführung der Genehmigung: Genehmigung und Genehmigungsfehler (Prozess direkt beenden)
4.1. Schnittstelle für Genehmigungsdetails
4.1.1, Serviceschnittstelle
Map<String, Object> show(Long id);
4.1.2. Implementierung der Serviceschnittstelle
@Override
public Map<String, Object> show(Long id) {
Process process = this.getById(id);
List<ProcessRecord> processRecordList = processRecordService.list(new LambdaQueryWrapper<ProcessRecord>().eq(ProcessRecord::getProcessId, id));
ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateId());
Map<String, Object> map = new HashMap<>();
map.put("process", process);
map.put("processRecordList", processRecordList);
map.put("processTemplate", processTemplate);
//计算当前用户是否可以审批,能够查看详情的用户不是都能审批,审批后也不能重复审批
boolean isApprove = false;
List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
if (!CollectionUtils.isEmpty(taskList)) {
for(Task task : taskList) {
if(task.getAssignee().equals(LoginUserInfoHelper.getUsername())) {
isApprove = true;
}
}
}
map.put("isApprove", isApprove);
return map;
}
4.1.3, Controller-Schnittstelle
@ApiOperation(value = "获取审批详情")
@GetMapping("show/{id}")
public Result show(@PathVariable Long id) {
return Result.ok(processService.show(id));
}
4.2. Front-End-Rendering
4.2.1. Definieren Sie die API-Schnittstelle
Fügen Sie eine Schnittstelle in src/api/process.js hinzu
show(id) {
return request({
url: `${
api_name}/show/`+id,
method: 'get'
})
},
4.2.2. Routing hinzufügen
Fügen Sie Routing in src/router/index.js hinzu
Beim Ausführen von Aufgaben wird die aktuelle Aufgaben-ID verwendet: taskId
{
path: '/show/:id/:taskId',
name: '审批详情',
component: () =>
import('../views/show.vue'),
},
4.2.3, Seitenrendering
Erstellen Sie src/views/show.vue
<template>
<div class="container">
<van-nav-bar
title="审批详情"
left-text="返回"
left-arrow
@click-left="() => $router.back()"
/>
<van-list>
<van-cell>
<template slot="default">
<div class="header-warp">
<h4>{
{ process.title }}</h4>
<p>{
{ process.createTime }}</p>
<span class="pass" v-if="process.status === 1">审批中</span>
<div class="seal-wrap" v-if="process.status === 2">
<seal-avatar></seal-avatar>
</div>
<div class="seal-wrap" v-if="process.status === -1">
<seal-avatar title="已拒绝" color="#EB8473"></seal-avatar>
</div>
</div>
</template>
</van-cell>
<van-cell>
<template slot="default">
<div class="detail-wrap">
<div class="item" v-for="(value,key) in formValues.formShowData">
<h5>{
{ key }}</h5>
<p v-html="value"></p>
</div>
</div>
</template>
</van-cell>
<van-cell>
<template slot="default">
<div class="result">
<h3>流程</h3>
<van-steps direction="vertical" :active="processRecordList.length - 1">
<van-step v-for="item in processRecordList">
<h4>{
{ item.operateUser }} {
{ item.description }}</h4>
<p>{
{ item.createTime }}</p>
</van-step>
</van-steps>
</div>
</template>
</van-cell>
</van-list>
<div class="notice" v-if="isApprove">
<van-icon name="bullhorn-o" />
<p>{
{ process.title }}</p>
<span class="pass">{
{ process.status === 1 ? '审批中' : process.status === 2 ? '审批通过' : '审批拒绝' }}</span>
</div>
<div class="footer" v-if="taskId != 0">
<div class="left-action">
<div class="action back" @click="() => $router.back()">
<van-icon name="revoke" />
<span>返回</span>
</div>
</div>
<div class="right-button">
<van-button @click="approve(-1)" type="default" size="small">审批拒绝</van-button>
<span style="margin: 0 4px"></span>
<van-button @click="approve(1)" type="info" size="small">审批通过</van-button>
</div>
</div>
</div>
</template>
<script>
import SealAvatar from '../components/Seal.vue';
import api from '@/api/process'
export default {
name: "process",
components: {
SealAvatar,
},
props: {
msg: String
},
data() {
return {
taskId: 0,
process: { },
formValues: {},
processRecordList: [],
isApprove: false
};
},
created(){
this.taskId = this.$route.params.taskId;
let id = this.$route.params.id;
this.fetchData(id);
},
methods: {
fetchData(id) {
api.show(id).then(response => {
this.process = response.data.process
this.formValues = JSON.parse(this.process.formValues)
this.processRecordList = response.data.processRecordList
this.isApprove = response.data.isApprove
})
},
approve(status) {
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding-bottom: 86px;
.header-warp {
position: relative;
h4 {
font-size: 16px;
margin: 5px;
}
p {
font-size: 16px;
margin: 5px;
}
.pass {
color: #4CB971;
margin: 5px;
}
.refused {
color: #EB8473;
margin: 5px;
}
}
.detail-wrap {
.item {
h5 {
color: #838485;
margin: 5px;
}
p {
color: #1B1F22;
font-size: 16px;
margin: 5px;
}
}
}
.result {
font-size: 14px;
h4, p {
margin: 5px;
font-size: 14px;
}
}
.seal-wrap {
position: absolute;
top: 20px;
right: 30px;
}
.notice {
display: flex;
align-items: center;
width: 100%;
font-size: 12px;
padding: 8px 10px;
background: #FEFBE8;
position: fixed;
bottom: 53px;
z-index: 10;
p {
flex: 1;
margin: 4px;
}
.pass {
color: #07c160;
margin-right: 20px;
}
}
.footer {
// height: 50px;
padding: 10px;
background: #F8F8F8;
display: flex;
align-items: center;
position: fixed;
width: 100%;
bottom: 0;
z-index: 10;
.left-action {
flex: 1;
.action {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
span {
font-size: 12px;
color: #838485;
}
}
}
.right-button {
margin-right: 20px;
}
}
}
/deep/ .van-cell {
position: inherit;
overflow: visible;
}
/deep/ .van-cell__value{
position: inherit;
overflow: visible;
}
</style>
5. Genehmigung
5.1. Genehmigungsschnittstelle
5.1.1, Serviceschnittstelle
void approve(ApprovalVo approvalVo);
5.1.2. Implementierung der Serviceschnittstelle
Wenn die Genehmigung nicht bestanden wird, beenden Sie den Prozess direkt (Hinweis: Der parallele Genehmigungsprozess kann nicht erfüllt werden und unser Unternehmen erfordert derzeit keine parallele Verarbeitung.)
public void approve(ApprovalVo approvalVo) {
Map<String, Object> variables1 = taskService.getVariables(approvalVo.getTaskId());
for (Map.Entry<String, Object> entry : variables1.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
String taskId = approvalVo.getTaskId();
if (approvalVo.getStatus() == 1) {
//已通过
Map<String, Object> variables = new HashMap<String, Object>();
taskService.complete(taskId, variables);
} else {
//驳回
this.endTask(taskId);
}
String description = approvalVo.getStatus().intValue() == 1 ? "已通过" : "驳回";
processRecordService.record(approvalVo.getProcessId(), approvalVo.getStatus(), description);
//计算下一个审批人
Process process = this.getById(approvalVo.getProcessId());
List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
if (!CollectionUtils.isEmpty(taskList)) {
List<String> assigneeList = new ArrayList<>();
for(Task task : taskList) {
SysUser sysUser = sysUserService.getByUsername(task.getAssignee());
assigneeList.add(sysUser.getName());
//推送消息给下一个审批人
}
process.setDescription("等待" + StringUtils.join(assigneeList.toArray(), ",") + "审批");
process.setStatus(1);
} else {
if(approvalVo.getStatus().intValue() == 1) {
process.setDescription("审批完成(同意)");
process.setStatus(2);
} else {
process.setDescription("审批完成(拒绝)");
process.setStatus(-1);
}
}
//推送消息给申请人
this.updateById(process);
}
private void endTask(String taskId) {
// 当前任务
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
List endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
// 并行任务可能为null
if(CollectionUtils.isEmpty(endEventList)) {
return;
}
FlowNode endFlowNode = (FlowNode) endEventList.get(0);
FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
// 临时保存当前活动的原始方向
List originalSequenceFlowList = new ArrayList<>();
originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
// 清理活动方向
currentFlowNode.getOutgoingFlows().clear();
// 建立新方向
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId");
newSequenceFlow.setSourceFlowElement(currentFlowNode);
newSequenceFlow.setTargetFlowElement(endFlowNode);
List newSequenceFlowList = new ArrayList<>();
newSequenceFlowList.add(newSequenceFlow);
// 当前节点指向新的方向
currentFlowNode.setOutgoingFlows(newSequenceFlowList);
// 完成当前任务
taskService.complete(task.getId());
}
5.1.3, Controller-Schnittstelle
@ApiOperation(value = "审批")
@PostMapping("approve")
public Result approve(@RequestBody ApprovalVo approvalVo) {
processService.approve(approvalVo);
return Result.ok();
}
5.2. Front-End-Implementierung
5.2.1. Definieren Sie die API-Schnittstelle
Fügen Sie eine Schnittstelle in src/api/process.js hinzu
approve(approvalVo) {
return request({
url: `${
api_name}/approve`,
method: 'post',
data: approvalVo
})
},
5.2.2, Seitenverarbeitung
Ergänzen Sie die Seitengenehmigungsfunktion src/views/show.vue
approve(status) {
let approvalVo = {
processId: this.process.id,
taskId: this.taskId,
status: status
}
api.approve(approvalVo).then(response => {
this.$router.push({
path: '/list/1' })
})
}
6. Verarbeitet
6.1. Verarbeitete Schnittstelle
6.1.1, Serviceschnittstelle
IPage<ProcessVo> findProcessed(Page<Process> pageParam);
6.1.2. Implementierung der Serviceschnittstelle
@Autowired
private HistoryService historyService;
@Override
public IPage<ProcessVo> findProcessed(Page<Process> pageParam) {
// 根据当前人的ID查询
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().taskAssignee(LoginUserInfoHelper.getUsername()).finished().orderByTaskCreateTime().desc();
List<HistoricTaskInstance> list = query.listPage((int) ((pageParam.getCurrent() - 1) * pageParam.getSize()), (int) pageParam.getSize());
long totalCount = query.count();
List<ProcessVo> processList = new ArrayList<>();
for (HistoricTaskInstance item : list) {
String processInstanceId = item.getProcessInstanceId();
Process process = this.getOne(new LambdaQueryWrapper<Process>().eq(Process::getProcessInstanceId, processInstanceId));
ProcessVo processVo = new ProcessVo();
BeanUtils.copyProperties(process, processVo);
processVo.setTaskId("0");
processList.add(processVo);
}
IPage<ProcessVo> page = new Page<ProcessVo>(pageParam.getCurrent(), pageParam.getSize(), totalCount);
page.setRecords(processList);
return page;
}
6.1.3, Controller-Schnittstelle
@ApiOperation(value = "已处理")
@GetMapping("/findProcessed/{page}/{limit}")
public Result findProcessed(
@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit) {
Page<Process> pageParam = new Page<>(page, limit);
return Result.ok(processService.findProcessed(pageParam));
}
6.2. Front-End-Implementierung
6.2.1. Definieren Sie die API-Schnittstelle
Fügen Sie eine Schnittstelle in src/api/process.js hinzu
findProcessed(page, limit) {
return request({
url: `${
api_name}/findProcessed/`+page+`/`+ limit,
method: 'get'
})
},
6.2.2, Seitenverarbeitung
Fügen Sie die Seitenmethode src/views/list.vue hinzu
findProcessed() {
console.log(this.pageNo)
api.findProcessed(this.pageNo, this.pageSize).then(response => {
console.log(response.data);
if (this.refreshing) {
this.list = [];
this.refreshing = false;
}
for (let i=0;i<response.data.records.length;i++) {
let item = response.data.records[i]
item.formValues = JSON.parse(item.formValues)
this.list.push(item);
}
this.pages = response.data.pages;
this.loading = false;
if(this.pageNo >= this.pages) {
this.finished = true;
}
this.pageNo++;
});
},
7. Initiiert
7.1. Initiierte Schnittstelle
7.1.1, Serviceschnittstelle
IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam);
7.1.2. Implementierung der Serviceschnittstelle
@Override
public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
ProcessQueryVo processQueryVo = new ProcessQueryVo();
processQueryVo.setUserId(LoginUserInfoHelper.getUserId());
IPage<ProcessVo> page = processMapper.selectPage(pageParam, processQueryVo);
for (ProcessVo item : page.getRecords()) {
item.setTaskId("0");
}
return page;
}
7.1.3, Controller-Schnittstelle
@ApiOperation(value = "已发起")
@GetMapping("/findStarted/{page}/{limit}")
public Result findStarted(
@ApiParam(name = "page", value = "当前页码", required = true)
@PathVariable Long page,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit) {
Page<ProcessVo> pageParam = new Page<>(page, limit);
return Result.ok(processService.findStarted(pageParam));
}
7.2. Front-End-Implementierung
7.2.1. Definieren Sie die API-Schnittstelle
Fügen Sie eine Schnittstelle in src/api/process.js hinzu
findStarted(page, limit) {
return request({
url: `${
api_name}/findStarted/`+page+`/`+ limit,
method: 'get'
})
},
7.2.2, Seitenverarbeitung
Fügen Sie die Seitenmethode src/views/list.vue hinzu
findStarted() {
console.log(this.pageNo)
api.findStarted(this.pageNo, this.pageSize).then(response => {
console.log(response.data);
if (this.refreshing) {
this.list = [];
this.refreshing = false;
}
for (let i=0;i<response.data.records.length;i++) {
let item = response.data.records[i]
item.formValues = JSON.parse(item.formValues)
this.list.push(item);
}
this.pages = response.data.pages;
this.loading = false;
if(this.pageNo >= this.pages) {
this.finished = true;
}
this.pageNo++;
});
},
for (let i = 0; i < response.data.records.length; i++) {
let item = response.data.records[i];
try {
item.formValues = JSON.parse(item.formValues);
this.list.push(item);
} catch (error) {
console.error("JSON 解析错误: " + error);
console.log("无法解析的 JSON 字符串: " + item.formValues);
}
}
Drei, meine Güte
1. Grundlegende Informationen
1.1. Grundlegende Informationsschnittstelle
1.1.1, Serviceschnittstelle
Operationsklasse: SysUserService
Map<String, Object> getCurrentUser();
1.1.2, Implementierung der Serviceschnittstelle
Operationsklasse: SysUserServiceImpl
@Autowired
private SysDeptService sysDeptService;
@Autowired
private SysPostService sysPostService;
@Override
public Map<String, Object> getCurrentUser() {
SysUser sysUser = sysUserMapper.selectById(LoginUserInfoHelper.getUserId());
//SysDept sysDept = sysDeptService.getById(sysUser.getDeptId());
//SysPost sysPost = sysPostService.getById(sysUser.getPostId());
Map<String, Object> map = new HashMap<>();
map.put("name", sysUser.getName());
map.put("phone", sysUser.getPhone());
//map.put("deptName", sysDept.getName());
//map.put("postName", sysPost.getName());
return map;
}
1.1.3, Controller-Schnittstelle
Operationsklasse: SysUserController
@ApiOperation(value = "获取当前用户基本信息")
@GetMapping("getCurrentUser")
public Result getCurrentUser() {
return Result.ok(sysUserService.getCurrentUser());
}
1.2. Front-End-Implementierung
1.2.1. Definieren Sie die API-Schnittstelle
Erstellen Sie src/api/userInfo.js
import request from '@/utils/request'
export default {
getCurrentUser() {
return request({
url: `/admin/system/sysUser/getCurrentUser/`,
method: 'get'
})
},
}
2.2.2. Routing hinzufügen
Fügen Sie Routing in src/router/index.js hinzu
{
path: '/user',
name: '基本信息',
component: () =>
import('../views/user.vue'),
},
2.2.4, Seitenrealisierung
Erstellen Sie src/views/user.vue
<template>
<div class="container">
<van-nav-bar
title="基本信息"
/>
<div class="detail-wrap">
<div class="item">
<h5>用户姓名:{
{
user.name }}</h5>
</div>
<div class="item">
<h5>手机号:{
{
user.phone }}</h5>
</div>
<div class="item">
<h5>所在部门:{
{
user.deptName }}</h5>
</div>
<div class="item">
<h5>岗位:{
{
user.postName }}</h5>
</div>
</div>
</div>
</template>
<script>
import api from '@/api/userInfo'
export default {
name: "process",
data() {
return {
user: {
}
};
},
created(){
this.fetchData();
},
methods: {
fetchData() {
// debugger
api.getCurrentUser().then(response => {
this.user = response.data
})
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding: 20px;
.detail-wrap {
.item {
h5 {
color: #838485;
margin: 10px;
}
p {
color: #1B1F22;
margin: 0;
}
}
}
}
</style>
2. Über uns
2.1. Routing hinzufügen
Fügen Sie Routing in src/router/index.js hinzu
{
path: '/about',
name: '关于我们',
component: () =>
import('../views/about.vue'),
},
2.2. Seite hinzufügen
<template>
<div class="container">
<van-nav-bar
title="关于我们"
/>
<div class="detail-wrap">
<div class="item">
云尚办公
</div>
</div>
</div>
</template>
<script>
export default {
name: "process",
data() {
return {
};
},
created(){
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.container {
padding: 20px;
.detail-wrap {
.item {
color: #838485;
}
}
}
</style>