MES审批复用钉钉审批设置
This commit is contained in:
@@ -944,6 +944,11 @@ jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules
|
|||||||
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java
|
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java
|
||||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
||||||
|
|
||||||
|
-- author:GHT---date:20260610--for: 【IM审批通用化】MES发起审批按钮按ding_tpl_bind路由匹配(与钉钉按钮一致) -----
|
||||||
|
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/approval/controller/MesXslApprovalLaunchController.java
|
||||||
|
jeecgboot-vue3/src/components/ApprovalLaunch/index.vue
|
||||||
|
jeecgboot-vue3/src/views/approval/flow/launch.api.ts
|
||||||
|
|
||||||
-- author:GHT---date:20260610--for: 【IM审批通用化】IM工作通知公众号(同事列表置顶+审批消息统一推送) -----
|
-- author:GHT---date:20260610--for: 【IM审批通用化】IM工作通知公众号(同事列表置顶+审批消息统一推送) -----
|
||||||
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_147__sys_im_work_notify_user.sql
|
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_147__sys_im_work_notify_user.sql
|
||||||
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/service/impl/SysImChatServiceImpl.java
|
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/im/service/impl/SysImChatServiceImpl.java
|
||||||
|
|||||||
@@ -16,11 +16,13 @@ import org.jeecg.modules.xslmes.approval.service.IMesXslApprovalHandleService;
|
|||||||
import org.jeecg.modules.xslmes.approval.service.IMesXslApprovalInstanceService;
|
import org.jeecg.modules.xslmes.approval.service.IMesXslApprovalInstanceService;
|
||||||
import org.jeecg.modules.xslmes.approval.vo.ApprovalGateVo;
|
import org.jeecg.modules.xslmes.approval.vo.ApprovalGateVo;
|
||||||
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
import org.jeecg.modules.xslmes.common.MesXslTenantUtils;
|
||||||
|
import org.jeecg.modules.xslmes.dingtalk.service.IMesXslDingTplBindService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -61,19 +63,37 @@ public class MesXslApprovalLaunchController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private JdbcTemplate jdbcTemplate;
|
private JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
//update-begin---author:GHT ---date:20260610 for:【IM审批通用化】MES发起按钮与钉钉绑定同路由匹配-----------
|
||||||
|
@Autowired
|
||||||
|
private IMesXslDingTplBindService dingTplBindService;
|
||||||
|
//update-end---author:GHT ---date:20260610 for:【IM审批通用化】MES发起按钮与钉钉绑定同路由匹配-----------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已发布审批流列表(按租户隔离),即"可发起的单据类型"。
|
* 已发布审批流列表(按租户隔离),即"可发起的单据类型"。
|
||||||
* 同时按"功能模块(单据表)"自动反查其菜单路由填入 routePath,供前端控制悬浮按钮仅在该功能页显示,无需手工配置。
|
* routePath 有值时:与钉钉审批按钮一致,先按 mes_xsl_ding_tpl_bind 解析当前页绑定,再返回该页业务表下已发布审批流。
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "发起审批-已发布审批流列表")
|
@Operation(summary = "发起审批-已发布审批流列表")
|
||||||
@GetMapping("/publishedList")
|
@GetMapping("/publishedList")
|
||||||
public Result<List<MesXslApprovalFlow>> publishedList() {
|
public Result<List<MesXslApprovalFlow>> publishedList(
|
||||||
|
@RequestParam(name = "routePath", required = false) String routePath) {
|
||||||
QueryWrapper<MesXslApprovalFlow> qw = new QueryWrapper<>();
|
QueryWrapper<MesXslApprovalFlow> qw = new QueryWrapper<>();
|
||||||
qw.eq("status", "1");
|
qw.eq("status", "1");
|
||||||
Integer tenantId = MesXslTenantUtils.resolveTenantId(null);
|
Integer tenantId = MesXslTenantUtils.resolveTenantId(null);
|
||||||
if (tenantId != null) {
|
if (tenantId != null) {
|
||||||
qw.eq("tenant_id", tenantId);
|
qw.eq("tenant_id", tenantId);
|
||||||
}
|
}
|
||||||
|
//update-begin---author:GHT ---date:20260610 for:【IM审批通用化】MES发起按钮与钉钉绑定同路由匹配-----------
|
||||||
|
if (oConvertUtils.isNotEmpty(routePath)) {
|
||||||
|
if (dingTplBindService.resolveActiveByRoutePath(routePath.trim()) == null) {
|
||||||
|
return Result.OK(Collections.emptyList());
|
||||||
|
}
|
||||||
|
String bizTable = resolveTableByRoutePath(routePath);
|
||||||
|
if (oConvertUtils.isEmpty(bizTable) || !IDENTIFIER.matcher(bizTable).matches()) {
|
||||||
|
return Result.OK(Collections.emptyList());
|
||||||
|
}
|
||||||
|
qw.eq("biz_table", bizTable);
|
||||||
|
}
|
||||||
|
//update-end---author:GHT ---date:20260610 for:【IM审批通用化】MES发起按钮与钉钉绑定同路由匹配-----------
|
||||||
qw.orderByDesc("create_time");
|
qw.orderByDesc("create_time");
|
||||||
List<MesXslApprovalFlow> list = approvalFlowService.list(qw);
|
List<MesXslApprovalFlow> list = approvalFlowService.list(qw);
|
||||||
// 未手工指定 route_path 时,按单据表名自动反查菜单路由
|
// 未手工指定 route_path 时,按单据表名自动反查菜单路由
|
||||||
@@ -90,6 +110,59 @@ public class MesXslApprovalLaunchController {
|
|||||||
* 约定:jeecg 代码生成的列表组件名为 表名驼峰 + List(如 mes_xsl_formula_spec -> MesXslFormulaSpecList),
|
* 约定:jeecg 代码生成的列表组件名为 表名驼峰 + List(如 mes_xsl_formula_spec -> MesXslFormulaSpecList),
|
||||||
* 对应 sys_permission.component 形如 xslmes/mesXslFormulaSpec/MesXslFormulaSpecList,取其 url 即路由。
|
* 对应 sys_permission.component 形如 xslmes/mesXslFormulaSpec/MesXslFormulaSpecList,取其 url 即路由。
|
||||||
*/
|
*/
|
||||||
|
//update-begin---author:GHT ---date:20260610 for:【IM审批通用化】按前端路由反查业务表(与钉钉绑定路由解析一致)-----------
|
||||||
|
private String resolveTableByRoutePath(String routePath) {
|
||||||
|
if (oConvertUtils.isEmpty(routePath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String component = resolveComponentByRoutePath(routePath);
|
||||||
|
if (oConvertUtils.isEmpty(component)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String comp = component.contains("/") ? component.substring(component.lastIndexOf('/') + 1) : component;
|
||||||
|
if (comp.endsWith("List")) {
|
||||||
|
comp = comp.substring(0, comp.length() - "List".length());
|
||||||
|
}
|
||||||
|
return camelToUnderline(comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveComponentByRoutePath(String routePath) {
|
||||||
|
if (oConvertUtils.isEmpty(routePath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String path = routePath.trim().replaceAll("/+$", "");
|
||||||
|
String sql = "SELECT component FROM sys_permission WHERE menu_type IN (0,1) "
|
||||||
|
+ "AND (del_flag = 0 OR del_flag IS NULL) AND url = ? "
|
||||||
|
+ "ORDER BY menu_type DESC LIMIT 1";
|
||||||
|
try {
|
||||||
|
List<String> list = jdbcTemplate.queryForList(sql, String.class, path);
|
||||||
|
return list.isEmpty() ? null : list.get(0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("反查菜单组件失败 routePath={}", routePath, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String camelToUnderline(String camel) {
|
||||||
|
if (oConvertUtils.isEmpty(camel)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < camel.length(); i++) {
|
||||||
|
char c = camel.charAt(i);
|
||||||
|
if (Character.isUpperCase(c)) {
|
||||||
|
if (i > 0) {
|
||||||
|
sb.append('_');
|
||||||
|
}
|
||||||
|
sb.append(Character.toLowerCase(c));
|
||||||
|
} else {
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
//update-end---author:GHT ---date:20260610 for:【IM审批通用化】按前端路由反查业务表(与钉钉绑定路由解析一致)-----------
|
||||||
|
|
||||||
private String resolveRoutePathByTable(String table) {
|
private String resolveRoutePathByTable(String table) {
|
||||||
if (oConvertUtils.isEmpty(table) || !IDENTIFIER.matcher(table).matches()) {
|
if (oConvertUtils.isEmpty(table) || !IDENTIFIER.matcher(table).matches()) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<!--
|
<!--
|
||||||
全局「发起审批」悬浮按钮
|
全局「发起审批」悬浮按钮
|
||||||
仅在「设计并发布了审批流、且能匹配到对应功能页路由」的页面显示。
|
与「钉钉审批」按钮一致:当前页存在 mes_xsl_ding_tpl_bind 绑定且钉钉模板启用时显示;
|
||||||
|
弹窗内可选该页业务表下已发布的 MES 审批流。
|
||||||
支持两种发起方式:
|
支持两种发起方式:
|
||||||
1)列表多选联动:在列表勾选数据后点击,弹窗自动带入选中单据并可批量发起;
|
1)列表多选联动:在列表勾选数据后点击,弹窗自动带入选中单据并可批量发起;
|
||||||
2)手动选择:未勾选时,在弹窗内搜索选择单条单据发起。
|
2)手动选择:未勾选时,在弹窗内搜索选择单条单据发起。
|
||||||
@@ -57,11 +58,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, reactive, ref } from 'vue';
|
import { computed, reactive, ref, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
import { getPublishedFlows, getBizRecords, launchApproval, launchApprovalBatch } from '/@/views/approval/flow/launch.api';
|
import { getPublishedFlows, getBizRecords, launchApproval, launchApprovalBatch } from '/@/views/approval/flow/launch.api';
|
||||||
|
import { getBindingByRoute } from '/@/views/xslmes/dingtalk/dingTplBind/dingTplBind.api';
|
||||||
import { useApprovalSelection } from './useApprovalSelection';
|
import { useApprovalSelection } from './useApprovalSelection';
|
||||||
|
|
||||||
defineOptions({ name: 'ApprovalLaunchFloat' });
|
defineOptions({ name: 'ApprovalLaunchFloat' });
|
||||||
@@ -78,6 +80,7 @@
|
|||||||
const bizDataId = ref<string>();
|
const bizDataId = ref<string>();
|
||||||
const bizTitle = ref<string>('');
|
const bizTitle = ref<string>('');
|
||||||
|
|
||||||
|
const binding = ref<any>(null);
|
||||||
const flowList = ref<any[]>([]);
|
const flowList = ref<any[]>([]);
|
||||||
const recordList = ref<any[]>([]);
|
const recordList = ref<any[]>([]);
|
||||||
// 打开弹窗时快照的列表勾选行(批量模式数据源)
|
// 打开弹窗时快照的列表勾选行(批量模式数据源)
|
||||||
@@ -86,18 +89,10 @@
|
|||||||
// 悬浮位置(右下角)
|
// 悬浮位置(右下角)
|
||||||
const floatStyle = reactive({ right: '24px', bottom: '120px' });
|
const floatStyle = reactive({ right: '24px', bottom: '120px' });
|
||||||
|
|
||||||
function normalizePath(p?: string) {
|
// 与钉钉审批按钮一致:按 mes_xsl_ding_tpl_bind + 路由解析是否显示
|
||||||
return (p || '').trim().replace(/\/+$/, '');
|
const show = computed(() => !!binding.value);
|
||||||
}
|
|
||||||
|
|
||||||
// 当前路由匹配到的已发布审批流
|
const matchedFlows = computed(() => flowList.value);
|
||||||
const matchedFlows = computed(() => {
|
|
||||||
const cur = normalizePath(currentRoute.value?.path);
|
|
||||||
if (!cur) return [];
|
|
||||||
return flowList.value.filter((f) => f.routePath && normalizePath(f.routePath) === cur);
|
|
||||||
});
|
|
||||||
|
|
||||||
const show = computed(() => matchedFlows.value.length > 0);
|
|
||||||
|
|
||||||
const flowOptions = computed(() =>
|
const flowOptions = computed(() =>
|
||||||
matchedFlows.value.map((f) => ({
|
matchedFlows.value.map((f) => ({
|
||||||
@@ -130,15 +125,27 @@
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(loadFlows);
|
watch(
|
||||||
|
() => currentRoute.value?.path,
|
||||||
async function loadFlows() {
|
async (path) => {
|
||||||
|
binding.value = null;
|
||||||
|
flowList.value = [];
|
||||||
|
if (!path || path === '/' || path.startsWith('/login')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
flowList.value = (await getPublishedFlows()) || [];
|
const bind = await getBindingByRoute(path);
|
||||||
|
binding.value = bind || null;
|
||||||
|
if (binding.value) {
|
||||||
|
flowList.value = (await getPublishedFlows(path)) || [];
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
binding.value = null;
|
||||||
flowList.value = [];
|
flowList.value = [];
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
function resetSelection() {
|
function resetSelection() {
|
||||||
flowId.value = undefined;
|
flowId.value = undefined;
|
||||||
@@ -149,6 +156,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function openModal() {
|
async function openModal() {
|
||||||
|
if (!matchedFlows.value.length) {
|
||||||
|
createMessage.warning('当前页面暂无已发布的 MES 审批流,请先在审批流设计中发布');
|
||||||
|
return;
|
||||||
|
}
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
resetSelection();
|
resetSelection();
|
||||||
// 读取当前列表页的勾选行
|
// 读取当前列表页的勾选行
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ enum Api {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 已发布审批流列表(可发起的单据类型)
|
* 已发布审批流列表(可发起的单据类型)
|
||||||
|
* @param routePath 当前功能页路由;传入时与钉钉审批按钮一致,按 mes_xsl_ding_tpl_bind 绑定页过滤
|
||||||
*/
|
*/
|
||||||
export const getPublishedFlows = () => defHttp.get({ url: Api.publishedList });
|
export const getPublishedFlows = (routePath?: string) =>
|
||||||
|
defHttp.get({ url: Api.publishedList, params: routePath ? { routePath } : undefined });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据审批流查询其绑定单据的记录列表
|
* 根据审批流查询其绑定单据的记录列表
|
||||||
|
|||||||
Reference in New Issue
Block a user