Răsfoiți Sursa

1.1.0 动态表单审批
- 审批增加动态表单类型
- 优化tag菜单标签
- 菜单处理动态表单

liuyanpeng 1 săptămână în urmă
părinte
comite
0f81c8a6ed

+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
   "name": "client-base-v5",
   "description": "Leanwo Prodog Client",
-  "version": "1.0.9",
+  "version": "1.1.0",
   "author": "yangzhijie1488 <yangzhijie1488@163.com>",
   "scripts": {
     "ins": "npm install --registry https://npm.leanwo.com",

+ 8 - 1
src/client/MenuNode.vue

@@ -65,6 +65,7 @@ export default {
 
     // 打开对应菜单窗口
     async clickMenuNode(menuInfo) {
+      console.log(menuInfo, 'menuInfo');
       const nodeType = this.model.menuNodeType;
       if (!nodeType || nodeType === 'NONE') return;
 
@@ -102,7 +103,7 @@ export default {
           setTimeout(() => {
             // 触发事件
             menuClickedEvent.value = {
-              path: this.$route.path,
+              path: this.$route.fullPath,
               menuInfo: menuInfo,
             };
           }, 50);
@@ -146,6 +147,12 @@ export default {
           this.model.customerWindowNo,
         );
         if (res.errorCode === 0) {
+          const windowType = res.data.windowType;
+          if(windowType == 'DYNAMIC_FORM'){
+            const formKey = JSON.parse(res.data.dynamicFormConfig).formKey;
+            this.$router.push('/desktop/launch?key=' + formKey);
+            return;
+          }
           this.$router.push(res.data.routeUrl);
         } else {
           Notify.error(this.$t('lang.Notify.error'), res.errorMessage, true);

+ 123 - 41
src/client/WorkTab.vue

@@ -41,6 +41,69 @@ const activeKey = ref('/desktop/dashboard');
 // 菜单节点映射表 - 存储路径与菜单节点的对应关系
 const pathMenuMap = ref({});
 
+// 获取不含 query 的路径,用于标题解析等
+const getPathname = fullPath => fullPath.split('?')[0];
+
+// 解析 query 参数
+const parseQuery = fullPath => {
+  const queryIndex = fullPath.indexOf('?');
+  if (queryIndex < 0) {
+    return {};
+  }
+  const params = {};
+  new URLSearchParams(fullPath.slice(queryIndex + 1)).forEach((value, key) => {
+    params[key] = value;
+  });
+  return params;
+};
+
+// 获取标签页稳定标识(忽略 uuid 等每次跳转都会变的参数)
+const getTabIdentity = fullPath => {
+  const pathname = getPathname(fullPath);
+  if (
+    pathname.includes('/desktop/window1/')
+    || pathname.includes('/desktop/window/')
+    || pathname.includes('/desktop/sheetWindow/')
+  ) {
+    return pathname;
+  }
+  if (pathname === '/desktop/launch') {
+    const query = parseQuery(fullPath);
+    if (query.key) {
+      return `${pathname}?key=${query.key}`;
+    }
+  }
+  return fullPath;
+};
+
+// 根据路由同步当前激活的标签
+const syncActiveTab = fullPath => {
+  const tabIdentity = getTabIdentity(fullPath);
+  const existTab = tabs.value.find(
+    tab => tab.key === tabIdentity || getTabIdentity(tab.path) === tabIdentity,
+  );
+  if (existTab) {
+    activeKey.value = existTab.key;
+  }
+};
+
+// 规范化并去重本地恢复的标签页
+const normalizeTabs = parsedTabs => {
+  const tabMap = new Map();
+  parsedTabs.forEach(tab => {
+    const path = tab.path || tab.key;
+    const identity = getTabIdentity(path);
+    if (!tabMap.has(identity)) {
+      tabMap.set(identity, {
+        ...tab,
+        key: identity,
+        path: path,
+      });
+    }
+  });
+  return Array.from(tabMap.values());
+};
+
 // 初始化标签页
 onMounted(() => {
   // 尝试从本地存储中恢复标签页
@@ -58,7 +121,8 @@ onMounted(() => {
             closable: false,
           });
         }
-        tabs.value = parsedTabs;
+        tabs.value = normalizeTabs(parsedTabs);
+        saveTabsToLocalStorage();
       }
     } catch (e) {
       console.error('恢复标签页失败', e);
@@ -68,46 +132,50 @@ onMounted(() => {
   // 如果当前路由不是首页,添加当前路由的标签页
   if (route.path !== '/desktop/dashboard') {
     setTimeout(() => {
-      addTab(route.path);
+      addTab(route.fullPath);
     }, 100);
   }
 });
 
 // 添加标签页方法
-const addTab = async path => {
+const addTab = async fullPath => {
+  const tabIdentity = getTabIdentity(fullPath);
   // 如果标签页已存在,则激活该标签页
-  const existTab = tabs.value.find(tab => tab.path === path);
+  const existTab = tabs.value.find(
+    tab => tab.key === tabIdentity || getTabIdentity(tab.path) === tabIdentity,
+  );
   if (existTab) {
     activeKey.value = existTab.key;
+    if (existTab.path !== fullPath) {
+      existTab.path = fullPath;
+      saveTabsToLocalStorage();
+    }
     return;
   }
+
   // 尝试获取菜单标题
-  let title = await getMenuTitle(path);
-
-  // if (!title) {
-  //   // 如果没有找到菜单标题,则从路由元数据中获取
-  //   const matchedRoute = router.getRoutes().find(route => route.path === path);
-  //   if (matchedRoute && matchedRoute.meta && matchedRoute.meta.title) {
-  //     title = matchedRoute.meta.title;
-  //   } else {
-  //     // 如果路由也没有定义标题,则从路径中提取
-  //     const pathSegments = path.split('/');
-  //     title = pathSegments[pathSegments.length - 1] || '未命名页面';
-  //   }
-  // }
+  let title = await getMenuTitle(fullPath);
 
   if (title) {
+    // 异步获取标题期间可能已添加,再次检查
+    const existAfter = tabs.value.find(
+      tab => tab.key === tabIdentity || getTabIdentity(tab.path) === tabIdentity,
+    );
+    if (existAfter) {
+      activeKey.value = existAfter.key;
+      return;
+    }
 
-    // 创建新标签页
+    // 创建新标签页(key 使用稳定标识,path 保留最近一次完整路径)
     const newTab = {
-      key: path,
+      key: tabIdentity,
       title: title,
-      path: path,
+      path: fullPath,
       closable: true, // 非首页标签可以关闭
     };
 
     tabs.value.push(newTab);
-    activeKey.value = path;
+    activeKey.value = tabIdentity;
 
     // 保存标签页到本地存储
     saveTabsToLocalStorage();
@@ -116,8 +184,12 @@ const addTab = async path => {
 };
 
 // 获取菜单标题
-const getMenuTitle = async path => {
+const getMenuTitle = async fullPath => {
+  const path = getPathname(fullPath);
   // 如果有缓存的菜单信息,直接使用
+  if (pathMenuMap.value[fullPath]) {
+    return pathMenuMap.value[fullPath].title;
+  }
   if (pathMenuMap.value[path]) {
     return pathMenuMap.value[path].title;
   }
@@ -189,25 +261,30 @@ const removeTab = targetKey => {
     }
     console.log(activeKey.value, 'activeKey.value');
     // 跳转到新激活标签页对应的路由
-    if (activeKey.value.includes('/desktop/window1/')) {
-      router.push(activeKey.value + '?uuid=' + Uuid.createUUID());
-    } else {
-      router.push(activeKey.value);
-    }
+    router.push(getTabRoutePath(activeKey.value));
   }
 
   // 保存标签页到本地存储
   saveTabsToLocalStorage();
 };
 
+// 获取标签页跳转路径(window/window1 每次打开刷新 uuid,其余保留 query)
+const getTabRoutePath = tabKeyOrPath => {
+  const pathname = getPathname(tabKeyOrPath);
+  if (
+    pathname.includes('/desktop/window1/')
+    || pathname.includes('/desktop/window/')
+    || pathname.includes('/desktop/sheetWindow/')
+  ) {
+    return pathname + '?uuid=' + Uuid.createUUID();
+  }
+  return tabKeyOrPath;
+};
+
 // 切换标签页
 const onChange = newActiveKey => {
-  let uidWindow = newActiveKey;
-  if (newActiveKey.includes('/desktop/window1/')) {
-    uidWindow = newActiveKey + '?uuid=' + Uuid.createUUID();
-  }
   activeKey.value = newActiveKey;
-  router.push(uidWindow);
+  router.push(getTabRoutePath(newActiveKey));
 };
 
 // 保存标签页到本地存储
@@ -223,20 +300,23 @@ const saveTabsToLocalStorage = () => {
 watch(menuClickedEvent, event => {
   if (event && event.path && event.menuInfo) {
     pathMenuMap.value[event.path] = event.menuInfo;
+    pathMenuMap.value[getPathname(event.path)] = event.menuInfo;
   }
 }, { deep: true, immediate: true });
 
 
-// 监听路由变化,添加标签页
+// 监听路由变化,同步或添加标签页
 watch(
-  () => route.path,
-  newPath => {
+  () => route.fullPath,
+  newFullPath => {
     // 有一些特殊路由可能不需要添加标签页,可以在这里过滤
     const ignorePaths = ['/login', '/loginNode', '/loginGraphic'];
-    if (!ignorePaths.includes(newPath)) {
+    const pathname = getPathname(newFullPath);
+    if (!ignorePaths.includes(pathname)) {
       nextTick(() => {
         setTimeout(() => {
-          addTab(newPath);
+          syncActiveTab(newFullPath);
+          addTab(newFullPath);
         }, 100);
       });
     }
@@ -259,16 +339,18 @@ watch(
 );
 
 // 供外部组件调用,用于添加带有菜单信息的标签页
-const addTabWithMenuInfo = (path, menuInfo) => {
-  // 缓存菜单信息
-  pathMenuMap.value[path] = {
+const addTabWithMenuInfo = (fullPath, menuInfo) => {
+  // 缓存菜单信息(同时缓存含 query 与不含 query 的 key)
+  const menuData = {
     title: menuInfo.title || menuInfo.name || '',
     menuNodeType: menuInfo.menuNodeType,
     no: menuInfo.no,
   };
+  pathMenuMap.value[fullPath] = menuData;
+  pathMenuMap.value[getPathname(fullPath)] = menuData;
 
   // 添加标签页
-  addTab(path);
+  addTab(fullPath);
 };
 
 // 暴露方法给其他组件使用

+ 6 - 3
src/index.js

@@ -21,8 +21,8 @@ window.CRUDId = -2147483640;
 
 import Login from './client/Login.vue';
 import Desktop from './client/Desktop.vue';
-// import Dashboard from './dashboard/Dashboard.vue';
-import Dashboard from './dashboard/NewDashboard.vue';
+import Dashboard from './dashboard/Dashboard.vue';
+// import Dashboard from './dashboard/NewDashboard.vue';
 import SheetWindow from './sheetWindow/SheetWindow.vue';
 import TabFormEdit1 from './window1/tabFormEdit/TabFormEdit.vue';
 import TabFormView1 from './window1/tabFormView/TabFormView.vue';
@@ -90,7 +90,8 @@ import ApproveComment from './workflow/ApproveComment.vue';
 import HistoryApproveComment from './workflow/HistoryApproveComment.vue';
 import LoginGraphic from './client/LoginGraphic.vue';
 import LoginNode from './client/LoginNode.vue';
-
+import LaunchApproval from './workflow/LaunchApproval.vue';
+import FormManage from './workflow/FormManage.vue';
 export {
   App,
   downloadStore,
@@ -168,4 +169,6 @@ export {
   LoginGraphic,
   LoginNode,
   AntdAuthImage,
+  LaunchApproval,
+  FormManage,
 };

+ 19 - 13
src/routes/main_routes.js

@@ -1,9 +1,9 @@
 import Login from '../client/Login.vue';
 import Desktop from '../client/Desktop.vue';
-// const Dashboard = () => import(/* webpackChunkName: "component-1" */ '../dashboard/Dashboard.vue');
-const Dashboard = () => import(/* webpackChunkName: "component-1" */ '../dashboard/NewDashboard.vue');
-const LoginNode = () =>import('../client/LoginNode.vue');
-const LoginGraphic= () =>import('../client/LoginGraphic.vue');
+const Dashboard = () => import(/* webpackChunkName: "component-1" */ '../dashboard/Dashboard.vue');
+// const Dashboard = () => import(/* webpackChunkName: "component-1" */ '../dashboard/NewDashboard.vue');
+const LoginNode = () => import('../client/LoginNode.vue');
+const LoginGraphic = () => import('../client/LoginGraphic.vue');
 
 const CurdWindow1 = () => import(/* webpackChunkName: "component-2" */ '../window1/CurdWindow.vue');
 const TabFormEdit1 = () => import(/* webpackChunkName: "component-4" */ '../window1/tabFormEdit/TabFormEdit.vue');
@@ -51,6 +51,8 @@ const RetrievePassword = () => import(/* webpackChunkName: "component-test-67" *
 const KnowledgeTrain1 = () => import(/* webpackChunkName: "component-test-68" */ '../confirm/KnowledgeTrain1.vue');
 const KnowledgeTrainAnswer = () => import(/* webpackChunkName: "component-test-69" */ '../confirm/KnowledgeTrainAnswer.vue');
 const ReportApprove = () => import(/* webpackChunkName: "component-71" */ '../workflow/ReportApprove.vue');
+const LaunchApproval = () => import(/* webpackChunkName: "component-75" */ '../workflow/LaunchApproval.vue');
+const FormManage = () => import(/* webpackChunkName: "component-76" */ '../workflow/FormManage.vue');
 const ExcelReport = () => import(/* webpackChunkName: "component-72" */ '../client/ExcelReport.vue');
 const DateExcelReport = () => import(/* webpackChunkName: "component-73" */ '../client/DateExcelReport.vue');
 const DelegationReport = () => import(/* webpackChunkName: "component-74" */ '../client/DelegationReport.vue');
@@ -70,13 +72,13 @@ const CasLogin = () => import('../client/CasLogin.vue');
 const PrinterConfiguration = () => import('../printer/PrinterConfiguration.vue');
 const ShortcutMenu = () => import('../dashboard/ShortcutMenu.vue');
 const FlowChart = () => import('../dashboard/FlowChart.vue');
-const DeviceManagement = () =>import('../device/DeviceManagement.vue');
-const DeviceTimingSwitcher = () =>import('../device/DeviceTimingSwitcher.vue');
-const OperationLog = () =>import('../operationLog/OperationLog.vue');
-const ViewEdit = () =>import('../window1/ViewEdit.vue');
+const DeviceManagement = () => import('../device/DeviceManagement.vue');
+const DeviceTimingSwitcher = () => import('../device/DeviceTimingSwitcher.vue');
+const OperationLog = () => import('../operationLog/OperationLog.vue');
+const ViewEdit = () => import('../window1/ViewEdit.vue');
 const TestPc = () => import('../widget/AudioField.vue');
 
-const UseEditorTest = ()=>import('../widget/UseEditorTest.vue');
+const UseEditorTest = () => import('../widget/UseEditorTest.vue');
 
 import { ProcessReport } from 'pc-component-v3';
 
@@ -128,7 +130,7 @@ export default [
       { path: 'sheetWindow/:windowNo', component: SheetWindow },
 
       // 快捷菜单
-      { path: 'shortcutMenu',name:'ShortcutMenu', component: ShortcutMenu },
+      { path: 'shortcutMenu', name: 'ShortcutMenu', component: ShortcutMenu },
       { path: 'assetFlowChart', component: FlowChart },
 
       // 属性编辑窗口
@@ -156,7 +158,7 @@ export default [
       // CRUD编辑窗口
       // eslint-disable-next-line
       { path: 'window1/TabFormEditModal', component: TabFormEditModal },
-      
+
 
       // eslint-disable-next-line
       { path: 'window1/window-edit/:type/:uuid', component: TabFormEdit1 },
@@ -349,7 +351,7 @@ export default [
       { path: '/TabFormEditModalTest', component: TabFormEditModalTest },
 
 
-      
+
 
       { path: '/webrt', component: WebRtcCapture },
       { path: '/WebSocketCapture', component: WebSocketCapture },
@@ -373,7 +375,7 @@ export default [
       { path: '/desktop/archivalRecord', component: ArchivalRecord },//数据归档记录下载
       { path: '/desktop/excelImport', component: ExcelImport },
       { path: '/desktop/identityManager', component: IdentityManager }, // 身份源管理
-      { path: '/desktop/createIdentity', name:'createIdentity',component: CreateIdentity }, // 身份源管理
+      { path: '/desktop/createIdentity', name: 'createIdentity', component: CreateIdentity }, // 身份源管理
       { path: '/desktop/printerConfiguration', component: PrinterConfiguration },
       { path: '/desktop/deviceManagement', component: DeviceManagement },
       { path: '/desktop/deviceTimingSwitcher', component: DeviceTimingSwitcher },
@@ -383,6 +385,10 @@ export default [
       { path: '/loginGraphic', component: LoginGraphic },
       { path: '/desktop/testPc', component: TestPc },
       { path: '/desktop/useEditorTest', component: UseEditorTest },
+      // 发起审批(动态表单)
+      { path: '/desktop/launch', component: LaunchApproval },
+      // 表单管理
+      { path: '/desktop/formManage', component: FormManage },
 
     ],
   },

+ 3 - 19
src/workflow/ApprovedWorkflow.vue

@@ -24,7 +24,7 @@
   >
     <template #bodyCell="{ column, record }">
       <template v-if="column.key === 'title'">
-        <span v-if="record.category != 'CurdWindow'">
+        <span v-if="record.category != 'CurdWindow' && record.category != 'DynamicForm'">
           {{ record.name }}
         </span>
         <span v-else>
@@ -33,7 +33,7 @@
       </template>
       <template v-if="column.key === 'description'">
         <span
-          v-if="record.category != 'CurdWindow'"
+          v-if="record.category != 'CurdWindow' && record.category != 'DynamicForm'"
           style="white-space: pre-line"
         >{{ record.description }}</span>
         <span v-else style="white-space: pre-line">{{ record.description }} <br /> {{ record.content }}</span>
@@ -79,6 +79,7 @@ import WindowService from '../common/WindowService.js';
 import CustomerTask from './CustomerTask.vue';
 import { Notify } from 'pc-component-v3';
 import { queryAuth, addAuth } from '../api/authorization/index.js';
+import { parseContent } from './parseWorkflowContent.js';
 
 const emit = defineEmits(['refreshStasticCount']);
 const customerTask = ref(null);
@@ -234,23 +235,6 @@ const addAuthorization = (params, taskInfo) => {
   );
 };
 
-// 处理content json
-const parseContent = content => {
-  const x = content;
-  try {
-    let content = JSON.parse(x);
-    let parentForm = '';
-    if (content != null && content.parentForm != null) {
-      content.parentForm.forEach(item => {
-        parentForm = parentForm + item.title + ':' + item.content + ',\n';
-      });
-      return parentForm;
-    } else {
-      return null;
-    }
-    // eslint-disable-next-line no-empty
-  } catch (e) {}
-};
 </script>
 
 <style scoped>

+ 3 - 19
src/workflow/CopyTaskWorkflow.vue

@@ -34,7 +34,7 @@
   >
     <template #bodyCell="{ column, record }">
       <template v-if="column.key === 'title'">
-        <span v-if="record.category != 'CurdWindow'">
+        <span v-if="record.category != 'CurdWindow' && record.category != 'DynamicForm'">
           {{ record.name }}
         </span>
         <span v-else>
@@ -43,7 +43,7 @@
       </template>
       <template v-if="column.key === 'description'">
         <span
-          v-if="record.category != 'CurdWindow'"
+          v-if="record.category != 'CurdWindow' && record.category != 'DynamicForm'"
           style="white-space: pre-line"
         >{{ record.description }}</span>
         <span v-else style="white-space: pre-line">{{ record.content }}</span>
@@ -87,6 +87,7 @@ import WindowService from '../common/WindowService.js';
 import CustomerTask from './CustomerTask.vue';
 import { Notify } from 'pc-component-v3';
 import { queryAuth, addAuth } from '../api/authorization/index.js';
+import { parseContent } from './parseWorkflowContent.js';
 
 
 const emit = defineEmits(['refreshStasticCount']);
@@ -287,23 +288,6 @@ const addAuthorization = (params, taskInfo) => {
   );
 };
 
-// 处理content json
-const parseContent = content => {
-  const x = content;
-  try {
-    let content = JSON.parse(x);
-    let parentForm = '';
-    if (content != null && content.parentForm != null) {
-      content.parentForm.forEach(item => {
-        parentForm = parentForm + item.title + ':' + item.content + ',\n';
-      });
-      return parentForm;
-    } else {
-      return null;
-    }
-    // eslint-disable-next-line no-empty
-  } catch (e) {}
-};
 </script>
 
 <style scoped>

+ 38 - 0
src/workflow/FormManage.vue

@@ -0,0 +1,38 @@
+<template>
+  <div class="launch-approval">
+    <iframe
+      v-if="iframeUrl"
+      :src="iframeUrl"
+      class="launch-approval-iframe"
+      frameborder="0"
+    />
+    <a-empty v-else description="缺少 key 参数,无法打开发起审批页面" />
+  </div>
+</template>
+
+<script setup>
+import { computed } from 'vue';
+import Common from '../common/Common.js';
+
+
+const iframeUrl = computed(() => {
+  return Common.getRedirectUrl('workflow.html#/form-manage');
+  // return 'http://192.168.1.111:10025/workflow.html#/form-manage';
+});
+</script>
+
+<style scoped>
+.launch-approval {
+  /* 抵消 App.vue .center-container 的 padding,仅占右侧内容区 */
+  margin: 10px -20px 0 -20px;
+  height: calc(100vh - 100px);
+  overflow: hidden;
+}
+
+.launch-approval-iframe {
+  display: block;
+  width: 100%;
+  height: 100%;
+  border: none;
+}
+</style>

+ 44 - 0
src/workflow/LaunchApproval.vue

@@ -0,0 +1,44 @@
+<template>
+  <div class="launch-approval">
+    <iframe
+      v-if="iframeUrl"
+      :src="iframeUrl"
+      class="launch-approval-iframe"
+      frameborder="0"
+    />
+    <a-empty v-else description="缺少 key 参数,无法打开发起审批页面" />
+  </div>
+</template>
+
+<script setup>
+import { computed } from 'vue';
+import { useRoute } from 'vue-router';
+import Common from '../common/Common.js';
+
+const route = useRoute();
+
+const iframeUrl = computed(() => {
+  const key = route.query.key;
+  if (key == null || key === '') {
+    return '';
+  }
+  return Common.getRedirectUrl('workflow.html#/form-runtime?key=' + key);
+  // return 'http://192.168.1.111:10025/workflow.html#/form-runtime?key=' + key;
+});
+</script>
+
+<style scoped>
+.launch-approval {
+  /* 抵消 App.vue .center-container 的 padding,仅占右侧内容区 */
+  margin: 10px -20px 0 -20px;
+  height: calc(100vh - 100px);
+  overflow: hidden;
+}
+
+.launch-approval-iframe {
+  display: block;
+  width: 100%;
+  height: 100%;
+  border: none;
+}
+</style>

+ 6 - 17
src/workflow/MyApplyWorkflow.vue

@@ -65,6 +65,8 @@ import { approvedColumns } from './configData.js';
 import CustomerTask from './CustomerTask.vue';
 import { Uuid } from 'pc-component-v3';
 import { queryAuth, addAuth } from '../api/authorization/index.js';
+import TaskOpenUtil from './TaskOpenUtil1.js';
+import { parseContent } from './parseWorkflowContent.js';
 
 const emit = defineEmits(['refreshStasticCount']);
 const customerTask = ref(null);
@@ -159,6 +161,10 @@ const searchApprove = (params, isSearch) => {
 
 // 跳到审批页
 const goWindow = taskInfo => {
+  if (taskInfo.category === 'DynamicForm') {
+    window.open(TaskOpenUtil.buildDynamicFormUrl(taskInfo));
+    return;
+  }
   const type = 'view';
   const windowNo = taskInfo.windowNo;
   const tabIndex = taskInfo.tabIndex;
@@ -217,23 +223,6 @@ const addAuthorization = (params, taskInfo) => {
   );
 };
 
-// 处理content json
-const parseContent = content => {
-  const x = content;
-  try {
-    let content = JSON.parse(x);
-    let parentForm = '';
-    if (content != null && content.parentForm != null) {
-      content.parentForm.forEach(item => {
-        parentForm = parentForm + item.title + ':' + item.content + ',\n';
-      });
-      return parentForm;
-    } else {
-      return null;
-    }
-    // eslint-disable-next-line no-empty
-  } catch (e) {}
-};
 </script>
 
 <style scoped>

+ 3 - 19
src/workflow/NeedApproveWorkflow.vue

@@ -24,7 +24,7 @@
   >
     <template #bodyCell="{ column, record }">
       <template v-if="column.key === 'title'">
-        <span v-if="record.category != 'CurdWindow'">
+        <span v-if="record.category != 'CurdWindow' && record.category != 'DynamicForm'">
           {{ record.name }}
         </span>
         <span v-else>
@@ -33,7 +33,7 @@
       </template>
       <template v-if="column.key === 'description'">
         <span
-          v-if="record.category != 'CurdWindow'"
+          v-if="record.category != 'CurdWindow' && record.category != 'DynamicForm'"
           style="white-space: pre-line"
         >{{ record.description }}</span>
         <span v-else style="white-space: pre-line">{{ record.description }} <br /> {{ record.content }}</span>
@@ -67,6 +67,7 @@ import WindowService from '../common/WindowService.js';
 import CustomerTask from './CustomerTask.vue';
 import { Notify } from 'pc-component-v3';
 import { queryAuth, addAuth } from '../api/authorization/index.js';
+import { parseContent } from './parseWorkflowContent.js';
 
 const emit = defineEmits(['refreshStasticCount']);
 const total = ref(0);
@@ -203,23 +204,6 @@ const addAuthorization = (params, taskInfo) => {
   );
 };
 
-// 处理content json
-const parseContent = content => {
-  const x = content;
-  try {
-    let content = JSON.parse(x);
-    let parentForm = '';
-    if (content != null && content.parentForm != null) {
-      content.parentForm.forEach(item => {
-        parentForm = parentForm + item.title + ':' + item.content + ',\n';
-      });
-      return parentForm;
-    } else {
-      return null;
-    }
-    // eslint-disable-next-line no-empty
-  } catch (e) {}
-};
 </script>
 
 <style scoped>

+ 6 - 17
src/workflow/NotApproveWorkflow.vue

@@ -65,6 +65,8 @@ import { approvedColumns } from './configData.js';
 import CustomerTask from './CustomerTask.vue';
 import { Uuid } from 'pc-component-v3';
 import { queryAuth, addAuth } from '../api/authorization/index.js';
+import TaskOpenUtil from './TaskOpenUtil1.js';
+import { parseContent } from './parseWorkflowContent.js';
 
 const emit = defineEmits(['refreshStasticCount']);
 const customerTask = ref(null);
@@ -159,6 +161,10 @@ const searchApprove = (params, isSearch) => {
 
 // 跳转到审批页
 const goWindow = taskInfo => {
+  if (taskInfo.category === 'DynamicForm') {
+    window.open(TaskOpenUtil.buildDynamicFormUrl(taskInfo));
+    return;
+  }
   const type = 'view';
   const windowNo = taskInfo.windowNo;
   const tabIndex = taskInfo.tabIndex;
@@ -217,23 +223,6 @@ const addAuthorization = (params, taskInfo) => {
   );
 };
 
-// 处理content json
-const parseContent = content => {
-  const x = content;
-  try {
-    let content = JSON.parse(x);
-    let parentForm = '';
-    if (content != null && content.parentForm != null) {
-      content.parentForm.forEach(item => {
-        parentForm = parentForm + item.title + ':' + item.content + ',\n';
-      });
-      return parentForm;
-    } else {
-      return null;
-    }
-    // eslint-disable-next-line no-empty
-  } catch (e) {}
-};
 </script>
 
 <style scoped>

+ 32 - 0
src/workflow/TaskOpenUtil1.js

@@ -7,6 +7,17 @@ const  t  = i18n.global.t;
 
 export default {
 
+  /**
+   * 构建动态表单审批页 URL
+   */
+  buildDynamicFormUrl: function(taskInfo) {
+    return Common.getRedirectUrl(
+      'workflow.html#/form-runtime/approval?key=' + taskInfo.windowNo + '&recordId=' + taskInfo.recordId,
+    );
+    // return 'http://192.168.1.111:10025/workflow.html#/form-runtime/approval?key=' + taskInfo.windowNo + '&recordId=' + taskInfo.recordId;
+    
+  },
+
   /**
      * 打开任务
      */
@@ -17,6 +28,13 @@ export default {
     var recordId = taskInfo.recordId;
     
     return new Promise((resolve, reject) => {
+      if (taskInfo.category === 'DynamicForm') {
+        resolve({
+          type: 'newWindow',
+          url: _self.buildDynamicFormUrl(taskInfo),
+        });
+        return;
+      }
       // 1. 首先判断category, category 如果等于 'CurdWindow' 说明是 CURD 窗口,否则是自定义的窗口
       // 2. 判断formKey是否存在
       // 2.1 如果存在那么就直接打开
@@ -139,6 +157,13 @@ export default {
     // 2.2 如果不存在那么,判断formKeyApp是否存在,如果不存在,那么表示本任务存在异常,如果存在,表示该任务需要在APP上执行,那么进行正确的提示。
     
     return new Promise((resolve, reject) => {
+      if (historyTaskInfo.category === 'DynamicForm') {
+        resolve({
+          type: 'newWindow',
+          url: _self.buildDynamicFormUrl(historyTaskInfo),
+        });
+        return;
+      }
       if(historyTaskInfo.category === 'CurdWindow'  || historyTaskInfo.category != 'MandatoryTask'){
         if(historyTaskInfo.formKey === null || historyTaskInfo.formKey === ''){
           let newUrl = '/#/desktop/window1/window-read/'
@@ -236,6 +261,13 @@ export default {
     // 2.2 如果不存在那么,判断formKeyApp是否存在,如果不存在,那么表示本任务存在异常,如果存在,表示该任务需要在APP上执行,那么进行正确的提示。
     
     return new Promise((resolve, reject) => {
+      if (copyTask.category === 'DynamicForm') {
+        resolve({
+          type: 'newWindow',
+          url: _self.buildDynamicFormUrl(copyTask),
+        });
+        return;
+      }
       if(copyTask.category === 'CurdWindow' || copyTask.category != 'MandatoryTask'){
         if(copyTask.formKey === null || copyTask.formKey === ''){
           let newUrl = '/#/desktop/window1/window-read/'

+ 1 - 1
src/workflow/WorkflowEdit.vue

@@ -646,7 +646,7 @@ export default {
       var _self = this;
 
       var url =
-        '/workflow.html#/?windowNo=' +
+        '/workflow.html#/approver?windowNo=' +
         _self.selectedWorkflow.window.keyStr +
         '&workflowId=' +
         _self.selectedWorkflow.id;

+ 26 - 0
src/workflow/parseWorkflowContent.js

@@ -0,0 +1,26 @@
+function formatFieldContent(content) {
+  if (content === true || content === 'true') {
+    return '是';
+  }
+  if (content == null) {
+    return '';
+  }
+  return String(content);
+}
+
+/**
+ * 解析工作流动态表单 content JSON,格式化 parentForm 展示文本
+ */
+export function parseContent(content) {
+  try {
+    const parsed = JSON.parse(content);
+    if (parsed == null || parsed.parentForm == null) {
+      return null;
+    }
+    return parsed.parentForm
+      .map(item => item.title + ':' + formatFieldContent(item.content))
+      .join(',\n');
+  } catch (e) {
+    // eslint-disable-next-line no-empty
+  }
+}