From eece5b838b0fe29ea003c355b1228de4795d3873 Mon Sep 17 00:00:00 2001
From: zxl <763096477@qq.com>
Date: 星期日, 04 一月 2026 11:14:06 +0800
Subject: [PATCH] 提交流程表单

---
 src/views/workbench.vue                   |   56 ++-
 src/permission.js                         |    2 
 src/views/projectProcess/detail/index.vue |   56 ----
 src/views/WebViewEntry.vue                |   68 ++++
 src/router/index.js                       |    5 
 src/views/WebViewSend.vue                 |  502 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 600 insertions(+), 89 deletions(-)

diff --git a/src/permission.js b/src/permission.js
index 5ac6f70..c53ae98 100644
--- a/src/permission.js
+++ b/src/permission.js
@@ -8,7 +8,7 @@
 
 NProgress.configure({ showSpinner: false })
 
-const whiteList = ['/login', '/register','/web-view-entry']
+const whiteList = ['/login', '/register','/web-view-entry','/web-view-send']
 
 router.beforeEach((to, from, next) => {
   NProgress.start()
diff --git a/src/router/index.js b/src/router/index.js
index 14fc845..a1681b2 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -47,6 +47,11 @@
     hidden: true // 涓嶆樉绀哄湪渚ц竟鏍忥紝浠呯敤浜� uni-app 鍐呭祵璁块棶
   },
   {
+    path: '/web-view-send', // 鍐呭祵鍏ュ彛椤甸潰璺敱
+    component: () => import('@/views//WebViewSend'),
+    hidden: true // 涓嶆樉绀哄湪渚ц竟鏍忥紝浠呯敤浜� uni-app 鍐呭祵璁块棶
+  },
+  {
     path: '/login',
     component: () => import('@/views/login'),
     hidden: true
diff --git a/src/views/WebViewEntry.vue b/src/views/WebViewEntry.vue
index 113a30c..9e3a1bb 100644
--- a/src/views/WebViewEntry.vue
+++ b/src/views/WebViewEntry.vue
@@ -1,10 +1,10 @@
 <template>
   <div class="app-container">
     <el-card class="box-card" >
-      <div slot="header" class="clearfix" style="display: flex">
-        <div style="flex: 1" class="el-icon-document">{{`浠诲姟璇︽儏锛歚 + this.processName}}</div>
-        <div style="flex: 2; color: #303133">{{projectName + '鈥斺��' + flowName}}</div>
-        <el-button style="float: right;" size="mini" type="danger" @click="goBack">鍏抽棴</el-button>
+      <div slot="header" class="clearfix header-container">
+        <div class="header-title el-icon-document">{{`浠诲姟璇︽儏锛歚 + this.processName}}</div>
+        <div class="header-subtitle">{{projectName + '鈥斺��' + flowName}}</div>
+        <!-- <el-button class="header-close-btn" size="mini" type="danger" @click="goBack">鍏抽棴</el-button> -->
       </div>
       <el-tabs  tab-position="top" v-model="activeName" @tab-click="handleClick">
         <!--琛ㄥ崟淇℃伅-->
@@ -14,7 +14,7 @@
             <div v-if="formDataList && formDataList.length > 0">
               <div v-for="(formDataObj, index) in formDataList" :key="index" class="form-warp" style="position: relative">
                 <el-row>
-                  <el-col :span="18">
+                  <el-col :xs="24" :sm="18">
                     <div v-if="formDataObj.current">
                       <div class="current">褰撳墠闃舵锛�<span>{{formDataObj.beforeNodeName}}</span></div>
                     </div>
@@ -42,7 +42,7 @@
                       </el-alert>
                     </div>
                   </el-col>
-                  <el-col :span="6">
+                  <el-col :xs="24" :sm="6">
                     <log-time-line v-if="formDataObj.events.length > 0" :log-list="formDataObj.events"/>
                   </el-col>
                 </el-row>
@@ -52,7 +52,7 @@
         </el-tab-pane>
         <!--娴佺▼娴佽浆璁板綍-->
         <el-tab-pane label="娴佽浆璁板綍" name="2">
-          <el-col :span="16" :offset="4" >
+          <el-col :xs="24" :sm="{span: 16, offset: 4}" >
             <div class="block">
               <el-timeline>
                 <el-timeline-item
@@ -158,8 +158,6 @@
   },
   created() {
     this.initPage()
-
-
     // 娴佺▼浠诲姟閲嶈幏鍙栧彉閲忚〃鍗�
     this.processVariables( this.taskForm.taskId, this.taskForm.procInsId)
     this.getFlowRecordList(this.taskForm.procInsId);
@@ -297,6 +295,57 @@
 };
 </script>
 <style lang="scss" scoped>
+.header-container {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 10px;
+
+  .header-title {
+    flex: 1;
+    min-width: 150px;
+    font-weight: bold;
+    color: #409EFF;
+  }
+
+  .header-subtitle {
+    flex: 2;
+    min-width: 200px;
+    color: #303133;
+    font-size: 14px;
+  }
+
+  .header-close-btn {
+    margin-left: auto;
+  }
+}
+
+@media screen and (max-width: 768px) {
+  .header-container {
+    flex-direction: column;
+    align-items: flex-start;
+    
+    .header-subtitle {
+      min-width: 100%;
+      margin-bottom: 5px;
+    }
+    
+    .header-close-btn {
+      margin-left: 0;
+      width: 100%;
+    }
+  }
+
+  .form-warp {
+    min-width: auto !important;
+    padding: 10px !important;
+  }
+
+  ::v-deep .el-descriptions-item__label {
+    width: 80px;
+  }
+}
+
 .test-form {
   margin: 15px auto;
   width: 800px;
@@ -327,7 +376,6 @@
 }
 
 .form-warp {
-  min-width: 700px;
   padding: 20px;
   margin-top: 5px;
   margin-bottom: 20px;
diff --git a/src/views/WebViewSend.vue b/src/views/WebViewSend.vue
new file mode 100644
index 0000000..4e5c5e0
--- /dev/null
+++ b/src/views/WebViewSend.vue
@@ -0,0 +1,502 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card" >
+      <div slot="header" class="clearfix header-container">
+        <div class="header-title el-icon-document">{{`浠诲姟鍔炵悊锛歚 + processName}}</div>
+        <div class="header-subtitle">{{projectName + '鈥斺��' + flowName}}</div>
+        <!-- <el-button class="header-close-btn" size="mini" type="danger" @click="goBack">鍏抽棴</el-button> -->
+      </div>
+      <el-tabs tab-position="top" v-model="activeName" @tab-click="handleClick">
+        <!--琛ㄥ崟淇℃伅-->
+        <el-tab-pane label="琛ㄥ崟淇℃伅" name="1">
+          <!--鍒濆鍖栨祦绋嬪姞杞借〃鍗曚俊鎭�-->
+          <el-col :span="24" v-loading="formLoading" class="tab-min-height">
+            <div v-if="formDataList && formDataList.length > 0">
+              <div v-for="(formDataObj, index) in formDataList" :key="index" class="form-warp" style="position: relative">
+                <el-row>
+                  <el-col :xs="24" :sm="18">
+                    <div v-if="formDataObj.current">
+                      <!-- 褰撳墠鑺傜偣鍙崗鍚屻�佽浆鍔炵瓑鎿嶄綔 -->
+                      <div class="op-list mobile-op-list">
+                        <el-button size="mini" type="primary" :disabled="formDataObj.taskStatus === '鎸傝捣' || submitLoading" v-loading="submitLoading" @click="submitForm">纭骞舵彁浜�</el-button>
+                        <el-button size="mini" type="primary" v-if="!isWait" :disabled="formDataObj.taskStatus === '鎸傝捣'" @click="openDelegation(formDataList[0].beforeNodeName)">杞姙</el-button>
+                        <el-button v-if="formDataObj.canJump && !isWait" :disabled="formDataObj.taskStatus === '鎸傝捣'" size="mini" type="primary" @click="jumpTask()">璺宠繃</el-button>
+                        <el-button v-if="formDataObj.canWait && !isWait" :disabled="formDataObj.taskStatus === '鎸傝捣'" size="mini" type="primary" @click="waitTask()">瀹圭己</el-button>
+                        <el-button v-if="formDataObj.canHangup && formDataObj.taskStatus !== '鎸傝捣' && !isWait" size="mini" type="primary" @click="hangup">鎸傝捣</el-button>
+                        <el-button v-if="formDataObj.canHangup && formDataObj.taskStatus === '鎸傝捣' && !isWait" size="mini" type="primary" @click="cancelHangup">缁撴潫鎸傝捣</el-button>
+                      </div>
+                      <div class="current">褰撳墠闃舵锛�<span>{{formDataObj.beforeNodeName}}</span><span v-if="formDataObj.taskStatus === 'HANGUP'">锛堟寕璧蜂腑锛�</span></div>
+                    </div>
+                    <div v-else-if="formDataList.length > 1">
+                      <!-- 鍓嶇疆鑺傜偣鍙┏鍥� -->
+                      <div class="reject-but mobile-reject-but">
+                        <el-button type="danger" size="mini" @click="openRejectTask(formDataObj.beforeNodeName)">椹� 鍥�</el-button>
+                      </div>
+                      <div class="before">鍓嶇疆闃舵锛�<span>{{formDataObj.beforeNodeName}}</span></div>
+                    </div>
+                    <div v-if="formDataObj != null && formDataObj.formJsonObj != null">
+                      <v-form-render :form-data="formDataObj.formJsonObj.formJson" :ref="'form' + index"/>
+                    </div>
+                    <div v-else>
+                      <el-alert title="鏈粦瀹氳〃鍗�" type="warning" :closable="false"></el-alert>
+                    </div>
+                    <div v-if="formDataList.length <= 1">
+                      <div class="before_none">鍓嶇疆闃舵锛�<span>涓嶅瓨鍦ㄥ墠缃樁娈�</span></div>
+                      <el-alert title="涓嶅瓨鍦ㄥ墠缃樁娈�" type="warning" :closable="false"></el-alert>
+                    </div>
+                  </el-col>
+                  <el-col :xs="24" :sm="6">
+                    <log-time-line v-if="formDataObj.events.length > 0" :log-list="formDataObj.events"/>
+                  </el-col>
+                </el-row>
+              </div>
+            </div>
+          </el-col>
+        </el-tab-pane>
+        <!--娴佺▼鍥�-->
+<!--        <el-tab-pane label="娴佺▼鍥�" name="2">-->
+<!--          <div v-loading="imgLoading" class="tab-min-height">-->
+<!--            <bpmn-viewer :flowData="flowData" :procInsId="procInsId"/>-->
+<!--          </div>-->
+<!--        </el-tab-pane>-->
+      </el-tabs>
+    </el-card>
+
+    <!--閫夋嫨娴佺▼鎺ユ敹浜�-->
+    <el-dialog :title="taskTitle" :visible.sync="taskOpen" width="90%" append-to-body>
+      <flow-user v-if="checkSendUser" :checkType="checkType" @handleUserSelect="handleUserSelect"/>
+      <flow-role v-if="checkSendRole" @handleRoleSelect="handleRoleSelect"/>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="taskOpen = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitTask">鎻� 浜�</el-button>
+      </span>
+    </el-dialog>
+
+    <!--椹冲洖寮圭獥-->
+    <el-dialog :title="`椹冲洖锛歚 + rejectForm.taskName" :visible.sync="rejectShow" width="90%" :destroy-on-close="true" :close-on-click-modal="false">
+      <div>
+        <el-input type="textarea" :rows="3" placeholder="瀹℃牳寤鸿" v-model="rejectForm.comment"></el-input>
+      </div>
+      <div class="opBut">
+        <el-button type="danger" size="small" :disabled="rejectLoading" v-loading="rejectLoading" @click="rejectTask">椹冲洖</el-button>
+      </div>
+    </el-dialog>
+
+    <!--杞姙寮圭獥-->
+    <el-dialog :title="`杞姙锛歚 + delegationForm.taskName" :visible.sync="delegationShow" width="90%" :destroy-on-close="true" :close-on-click-modal="false">
+      <div>
+        <el-form :model="delegationForm" :rules="delegationFormRules" ref="delegationForm" label-width="80px" class="demo-ruleForm">
+          <el-alert title="璇烽�夋嫨瑕佽浆鍔炵殑鐢ㄦ埛瀵硅薄" type="info" :closable="false" style="margin-bottom: 8px" show-icon></el-alert>
+          <el-form-item label="鐢ㄦ埛绫诲瀷" prop="peopleType">
+            <el-select v-model="delegationForm.peopleType" @change="peopleTypeChange" placeholder="璇烽�夋嫨鐢ㄦ埛绫诲瀷" style="width: 100%">
+              <el-option label="鎸囧畾浜哄憳" value="FIX_USER"></el-option>
+              <el-option label="鍊欓�変汉鍛�" value="USER"></el-option>
+              <el-option label="鍊欓�夐儴闂�" value="DEPT"></el-option>
+              <el-option label="鍊欓�夎鑹�" value="ROLE"></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item v-if="delegationForm.peopleType === 'DEPT'" label="鍊欓�夐儴闂�" prop="targetId">
+            <MyDept ref="dept" :checkeds="delegationDeptSelect" :title="`閫夋嫨杞姙閮ㄩ棬`" :show="deptShow" @close="closeDept" @submit="getDeptSelect" :key="deptKey"/>
+            <div class="tag-group">
+              <el-tag v-for="dept in delegationDeptSelect" :key="dept.id" type="info" closable @close="removeDept(dept)">{{dept.label}}</el-tag>
+              <el-button type="text" @click="editDept">閫夋嫨</el-button>
+            </div>
+          </el-form-item>
+          <el-form-item v-if="delegationForm.peopleType === 'FIX_USER'" label="鎸囧畾浜哄憳" prop="targetId">
+            <single-user ref="singleUser" :select-user="delegationForm.targetId" :title="`閫夋嫨杞姙鎺ユ敹浜哄憳`" :show="singleUserShow" @close="closeSingleUser" @submit="getSingleUserSelect" :key="singleUserKey"/>
+            <div class="tag-group">
+              <el-tag v-for="user in delegationUserSelect" :key="user.userId" type="info" closable @close="removeSingleUser(user)">{{user.nickName}}</el-tag>
+              <el-button type="text" @click="editSingleUser">閫夋嫨</el-button>
+            </div>
+          </el-form-item>
+          <el-form-item v-if="delegationForm.peopleType === 'USER'" label="鍊欓�変汉鍛�" prop="targetId">
+            <mult-user ref="multUser" :select-user="delegationUserSelect" :title="`閫夋嫨杞姙鎺ユ敹浜哄憳`" :show="multUserShow" @close="closeMultUser" @submit="getMultUserSelect" :key="multUserKey"/>
+            <div class="tag-group">
+              <el-tag v-for="user in delegationUserSelect" :key="user.userId" type="info" closable @close="removeMultUser(user)">{{user.nickName}}</el-tag>
+              <el-button type="text" @click="editMultUser">閫夋嫨</el-button>
+            </div>
+          </el-form-item>
+          <el-form-item v-if="delegationForm.peopleType === 'ROLE'" label="鍊欓�夎鑹�" prop="targetId">
+            <my-role ref="role" :select-values="delegationRoleSelect" :title="`閫夋嫨杞姙瑙掕壊`" :show="roleShow" @close="closeRole" @submit="getRoleSelect" :key="roleKey"/>
+            <div class="tag-group">
+              <el-tag v-for="role in delegationRoleSelect" :key="role.roleId" type="info" closable @close="removeRole(role)">{{role.roleName}}</el-tag>
+              <el-button type="text" @click="editRole">閫夋嫨</el-button>
+            </div>
+          </el-form-item>
+        </el-form>
+      </div>
+      <div class="opBut">
+        <el-button type="danger" size="small" :disabled="delegationButLoading" v-loading="delegationButLoading" @click="delegation">杞� 鍔�</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { flowXmlAndNode } from "@/api/flowable/definition";
+import BpmnViewer from '@/components/Process/viewer';
+import MyDept from '@/components/flow/Dept/MyDept'
+import SingleUser from '@/components/flow/User/SingleUser'
+import MultUser from '@/components/flow/User/MultUser'
+import MyRole from '@/components/flow/Role/MyRole'
+import FlowUser from '@/components/flow/User'
+import FlowRole from '@/components/flow/Role'
+import { completeSubmitFormTask, waitCompleteSubmitFormTask, rejectTask } from "@/api/flowable/process";
+import { flowTaskForm } from "@/api/flowable/todo";
+import LogTimeLine from "@/views/projectProcess/components/LogTimeLine";
+import { cancelTaskHangup, taskDelegation, taskHangup, taskJump, taskWait } from "@/api/projectProcess/projectProcess";
+import { setToken } from "@/utils/auth";
+
+export default {
+  name: "WebViewSend",
+  components: {
+    BpmnViewer, MyRole, MyDept, SingleUser, MultUser, LogTimeLine, FlowUser, FlowRole
+  },
+  data() {
+    return {
+      isWait: false,
+      rejectLoading: false,
+      submitLoading: false,
+      delegationButLoading: false,
+      formLoading: false,
+      imgLoading: false,
+      roleKey: 'role',
+      deptKey: 'dept',
+      multUserKey: 0,
+      singleUserKey: 'single',
+      projectName: '',
+      flowName: '',
+      processName: '',
+      roleShow: false,
+      delegationRoleSelect: [],
+      multUserShow: false,
+      singleUserShow: false,
+      delegationUserSelect: [],
+      deptShow: false,
+      delegationDeptSelect: [],
+      delegationFormRules: {
+        peopleType: [{ required: true, message: '璇烽�夋嫨鐢ㄦ埛绫诲瀷', trigger: 'change' }],
+        targetId: [{ required: true, message: '璇烽�夋嫨杞姙瀵硅薄', trigger: 'blur' }],
+      },
+      delegationForm: {
+        taskId: '',
+        peopleType: '',
+        targetId: '',
+        taskName: '',
+        processInsId: '',
+        projectId: '',
+      },
+      delegationShow: false,
+      rejectShow: false,
+      formDataList: [],
+      taskId: '',
+      flowData: {},
+      activeName: '1',
+      procInsId: "",
+      deployId: "",
+      projectId: "",
+      taskTitle: null,
+      taskOpen: false,
+      checkSendUser: false,
+      checkSendRole: false,
+      checkType: '',
+      checkValues: null,
+      formData: {},
+      multiInstanceVars: '',
+      formJson: {},
+      rejectForm: {
+        comment: '',
+        taskId: '',
+        taskName: ''
+      }
+    };
+  },
+  async created() {
+    await this.initPage();
+    if (this.taskId) {
+      this.getFlowFormData(this.taskId);
+    }
+  },
+  methods: {
+    async initPage() {
+      try {
+        const urlToken = this.getUrlParam('token');
+        console.log(urlToken)
+
+        if (urlToken) {
+          const token = decodeURIComponent(urlToken);
+          setToken(token);
+          this.$store.commit('SET_TOKEN', token);
+        }
+
+        this.deployId = decodeURIComponent(this.getUrlParam('deployId'));
+        this.taskId = decodeURIComponent(this.getUrlParam('taskId'));
+        this.procInsId = decodeURIComponent(this.getUrlParam('procInsId'));
+        this.projectName = decodeURIComponent(this.getUrlParam('projectName'));
+        this.flowName = decodeURIComponent(this.getUrlParam('flowName'));
+        this.processName = decodeURIComponent(this.getUrlParam('processName'));
+        this.projectId = decodeURIComponent(this.getUrlParam('projectId'));
+        this.isWait = this.getUrlParam('isWait') === 'true';
+      } catch (err) {
+        console.error('椤甸潰鍒濆鍖栧け璐ワ細', err);
+      }
+    },
+    getUrlParam(name) {
+      const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`);
+      const r = window.location.search.substr(1).match(reg);
+      return r != null ? r[2] : '';
+    },
+    handleClick(tab) {
+      if (tab.name === '2') {
+        this.imgLoading = true;
+        flowXmlAndNode({ processInsId: this.procInsId, deployId: this.deployId }).then(res => {
+          this.imgLoading = false;
+          this.flowData = res.data;
+        });
+      }
+    },
+    getFlowFormData(taskId) {
+      this.formLoading = true;
+      flowTaskForm({ taskId }).then(res => {
+        this.formDataList = res.data;
+        if (this.formDataList && this.formDataList.length > 0) {
+          this.$nextTick(() => {
+            this.formDataList.forEach((formDataObj, index) => {
+              if (this.$refs['form' + index] && formDataObj.formJsonObj) {
+                this.$refs['form' + index][0].setFormJson(formDataObj.formJsonObj.formJson);
+                this.$refs['form' + index][0].setFormData(formDataObj.formJsonObj);
+              }
+            });
+            if (this.formDataList[0].formJsonObj) {
+              this.formJson = this.formDataList[0].formJsonObj.formJson;
+            }
+          });
+        }
+        this.formLoading = false;
+      }).catch(() => {
+        this.formLoading = false;
+      });
+    },
+    submitForm() {
+      this.$confirm(`纭畾瑕佹彁浜や换鍔°��${this.processName}銆戝悧?`, '鎻愮ず', {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }).then(() => {
+        if (this.$refs['form0']) {
+          this.$refs['form0'][0].getFormData().then(formData => {
+            this.submitLoading = true;
+            this.formData = formData;
+            const param = { formJson: this.formJson };
+            Object.assign(param, formData);
+            const api = this.isWait ? waitCompleteSubmitFormTask : completeSubmitFormTask;
+            api(this.taskId, param).then(res => {
+              this.$message.success(res.msg);
+              this.submitLoading = false;
+              this.goBack();
+            }).catch(() => { this.submitLoading = false; });
+          });
+        } else {
+          const api = this.isWait ? waitCompleteSubmitFormTask : completeSubmitFormTask;
+          api(this.taskId, {}).then(res => {
+            this.$message.success(res.msg);
+            this.goBack();
+          });
+        }
+      });
+    },
+    openRejectTask(taskName) {
+      this.rejectForm.taskName = taskName;
+      this.rejectForm.taskId = this.taskId;
+      this.rejectShow = true;
+    },
+    rejectTask() {
+      this.$confirm(`纭畾瑕侀┏鍥炰换鍔°��${this.rejectForm.taskName}銆戝悧?`, '鎻愮ず', {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }).then(() => {
+        this.rejectLoading = true;
+        rejectTask(this.rejectForm).then(res => {
+          this.rejectShow = false;
+          this.$message.success("椹冲洖鎴愬姛");
+          this.goBack();
+        }).finally(() => { this.rejectLoading = false; });
+      });
+    },
+    openDelegation(taskName) {
+      this.delegationForm.taskName = taskName;
+      this.delegationForm.taskId = this.taskId;
+      this.delegationShow = true;
+    },
+    delegation() {
+      this.$refs['delegationForm'].validate((valid) => {
+        if (valid) {
+          this.delegationForm.projectId = this.projectId;
+          this.delegationForm.processInsId = this.procInsId;
+          this.delegationButLoading = true;
+          taskDelegation(this.delegationForm).then(res => {
+            this.$message.success("宸插彂璧疯浆鍔炵敵璇�");
+            this.delegationShow = false;
+            this.goBack();
+          }).finally(() => { this.delegationButLoading = false; });
+        }
+      });
+    },
+    // 鍏朵粬杈呭姪鏂规硶锛堣浆鍔炵浉鍏筹級
+    peopleTypeChange(val) {
+      if (val === 'DEPT') { this.deptShow = true; }
+      else if (val === 'FIX_USER') { this.singleUserShow = true; }
+      else if (val === 'USER') { this.multUserShow = true; }
+      else if (val === 'ROLE') { this.roleShow = true; }
+    },
+    getDeptSelect(list) { this.delegationDeptSelect = list; this.delegationForm.targetId = list.map(i => i.id).join(","); this.deptShow = false; },
+    getMultUserSelect(list) { this.delegationUserSelect = list; this.delegationForm.targetId = list.map(i => i.userId).join(","); this.multUserShow = false; },
+    getRoleSelect(list) { this.delegationRoleSelect = list; this.delegationForm.targetId = list.map(i => i.roleId).join(","); this.roleShow = false; },
+    getSingleUserSelect(user) { this.delegationUserSelect = user ? [user] : []; this.delegationForm.targetId = user ? user.userId : ''; this.singleUserShow = false; },
+    editDept() { this.deptShow = true; },
+    editSingleUser() { this.singleUserShow = true; },
+    editRole() { this.roleShow = true; },
+    editMultUser() { this.multUserShow = true; },
+    removeDept(dept) { this.delegationDeptSelect = this.delegationDeptSelect.filter(i => i.id !== dept.id); },
+    removeRole(role) { this.delegationRoleSelect = this.delegationRoleSelect.filter(i => i.roleId !== role.roleId); },
+    removeMultUser(user) { this.delegationUserSelect = this.delegationUserSelect.filter(i => i.userId !== user.userId); },
+    removeSingleUser() { this.delegationUserSelect = []; this.delegationForm.targetId = ''; },
+    closeDept() { this.deptShow = false; },
+    closeSingleUser() { this.singleUserShow = false; },
+    closeRole() { this.roleShow = false; },
+    closeMultUser() { this.multUserShow = false; },
+    // 鎸傝捣/瀹圭己/璺宠繃
+    hangup() { this.$prompt('澶囨敞璇存槑', '纭畾瑕佹寕璧锋浠诲姟鍚�').then(({ value }) => { taskHangup({ taskId: this.taskId, projectId: this.projectId, processInsId: this.procInsId, reason: value }).then(() => { this.$message.success("鎿嶄綔鎴愬姛"); this.goBack(); }); }); },
+    cancelHangup() { this.$confirm('纭畾瑕佸彇娑堟寕璧峰悧?').then(() => { cancelTaskHangup({ taskId: this.taskId, projectId: this.projectId, processInsId: this.procInsId }).then(() => { this.$message.success("鎿嶄綔鎴愬姛"); this.goBack(); }); }); },
+    waitTask() { this.$prompt('澶囨敞璇存槑', '纭畾瑕佸缂烘浠诲姟鍚�').then(({ value }) => { taskWait({ taskId: this.taskId, projectId: this.projectId, processInsId: this.procInsId, desc: value }).then(() => { this.$message.success("鎿嶄綔鎴愬姛"); this.goBack(); }); }); },
+    jumpTask() { this.$prompt('澶囨敞璇存槑', '纭畾瑕佽烦杩囨浠诲姟鍚�').then(({ value }) => { taskJump({ taskId: this.taskId, projectId: this.projectId, processInsId: this.procInsId, desc: value }).then(() => { this.$message.success("鎿嶄綔鎴愬姛"); this.goBack(); }); }); },
+    goBack() {
+      // WebView 妯″紡涓嬮�氬父涓嶉渶瑕佸鏉傜殑 goBack 閫昏緫锛屾垨鑰呭彲浠ラ�氱煡鐖跺鍣�
+      console.log('Task completed');
+    },
+    // 澶勭悊鎺ユ敹浜洪�夋嫨
+    handleUserSelect(selection) {
+      if (selection) {
+        if (selection instanceof Array) {
+          const selectVal = selection.map(item => item.userId);
+          this.checkValues = this.multiInstanceVars ? selectVal : selectVal.join(',');
+        } else {
+          this.checkValues = selection.userId;
+        }
+      }
+    },
+    handleRoleSelect(selection) {
+      if (selection) {
+        if (selection instanceof Array) {
+          this.checkValues = selection.map(item => item.roleId).join(',');
+        } else {
+          this.checkValues = selection;
+        }
+      }
+    },
+    submitTask() {
+      if (!this.checkValues && (this.checkSendUser || this.checkSendRole)) {
+        this.$message.error("璇烽�夋嫨鎺ユ敹瀵硅薄!");
+        return;
+      }
+      const param = { formJson: this.formJson };
+      Object.assign(param, this.formData);
+      this.$set(param, this.multiInstanceVars || "approval", this.checkValues);
+      completeSubmitFormTask(this.taskId, param).then(res => {
+        this.$message.success(res.msg);
+        this.goBack();
+      });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.header-container {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 10px;
+
+  .header-title {
+    flex: 1;
+    min-width: 150px;
+    font-weight: bold;
+    color: #409EFF;
+  }
+
+  .header-subtitle {
+    flex: 2;
+    min-width: 200px;
+    color: #303133;
+    font-size: 14px;
+  }
+}
+
+.tag-group {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 5px;
+}
+
+@media screen and (max-width: 768px) {
+  .header-container {
+    flex-direction: column;
+    align-items: flex-start;
+    .header-subtitle { min-width: 100%; margin-bottom: 5px; }
+  }
+
+  .mobile-op-list, .mobile-reject-but {
+    position: static !important;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 5px;
+    margin-bottom: 10px;
+    padding: 10px;
+    background: #f8f9fa;
+    border-radius: 4px;
+
+    .el-button {
+      margin-left: 0 !important;
+      flex: 1;
+      min-width: 80px;
+    }
+  }
+
+  .form-warp {
+    padding: 10px !important;
+  }
+}
+
+.form-warp {
+  padding: 20px;
+  margin-top: 5px;
+  margin-bottom: 20px;
+  box-shadow: rgba(67, 71, 85, 0.27) 0px 0px 0.1em, rgba(90, 125, 188, 0.05) 0px 0.1em 0.5em;
+}
+
+.opBut {
+  display: flex; justify-content: center; align-items: center; width: 100%; margin-top: 15px;
+}
+
+.current, .before, .before_none {
+  margin-bottom: 15px;
+  color: #E6A23C;
+  span { font-weight: bold; }
+}
+.current span { color: #409EFF; }
+.before span, .before_none span { color: #F56C6C; }
+
+.tab-min-height {
+  min-height: 400px;
+}
+
+.op-list, .reject-but {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  z-index: 10;
+}
+</style>
diff --git a/src/views/projectProcess/detail/index.vue b/src/views/projectProcess/detail/index.vue
index 9c8bc79..8914d1b 100644
--- a/src/views/projectProcess/detail/index.vue
+++ b/src/views/projectProcess/detail/index.vue
@@ -127,62 +127,6 @@
       <log-view style="padding: 10px 20px" :log-list="logList"/>
     </el-drawer>
 
-<!--    <el-dialog-->
-<!--      :title="`${this.queryParams.processName}锛氭祦杞褰昤"-->
-<!--      :visible.sync="processRecordShow"-->
-<!--      :fullscreen="true"-->
-<!--      :close-on-click-modal="false"-->
-<!--      :destroy-on-close="true"-->
-<!--    >-->
-<!--      <div>-->
-<!--        <log-view :log-list="logList"/>-->
-<!--&lt;!&ndash;        <div class="block">&ndash;&gt;-->
-<!--&lt;!&ndash;          <el-timeline>&ndash;&gt;-->
-<!--&lt;!&ndash;            <el-timeline-item&ndash;&gt;-->
-<!--&lt;!&ndash;              v-for="(item,index ) in flowRecordList"&ndash;&gt;-->
-<!--&lt;!&ndash;              :key="index"&ndash;&gt;-->
-<!--&lt;!&ndash;              :icon="setIcon(item.finishTime)"&ndash;&gt;-->
-<!--&lt;!&ndash;              :color="setColor(item.finishTime)"&ndash;&gt;-->
-<!--&lt;!&ndash;            >&ndash;&gt;-->
-<!--&lt;!&ndash;              <p style="font-weight: 700">{{item.taskName}}&ndash;&gt;-->
-<!--&lt;!&ndash;                <span v-if="item.comment && item.comment.type === '3'" style="color: red">(鎵ц浜嗛┏鍥�)</span>&ndash;&gt;-->
-<!--&lt;!&ndash;                <span v-if="item.overtime && item.overtime==='red'" style="color: red">(宸茶秴鏃�)</span>&ndash;&gt;-->
-<!--&lt;!&ndash;                <span v-if="item.overtime && item.overtime==='yellow'" style="color: orange">(鍗冲皢瓒呮椂)</span>&ndash;&gt;-->
-<!--&lt;!&ndash;              </p>&ndash;&gt;-->
-<!--&lt;!&ndash;              <el-card :body-style="{ padding: '10px' }">&ndash;&gt;-->
-<!--&lt;!&ndash;                <el-descriptions class="margin-top" :column="1" size="small" border>&ndash;&gt;-->
-<!--&lt;!&ndash;                  <el-descriptions-item v-if="item.assigneeName" label-class-name="my-label">&ndash;&gt;-->
-<!--&lt;!&ndash;                    <template slot="label"><i class="el-icon-user"></i>鍔炵悊浜�</template>&ndash;&gt;-->
-<!--&lt;!&ndash;                    {{item.assigneeName}}&ndash;&gt;-->
-<!--&lt;!&ndash;                    <el-tag type="info" size="mini">{{item.deptName}}</el-tag>&ndash;&gt;-->
-<!--&lt;!&ndash;                  </el-descriptions-item>&ndash;&gt;-->
-<!--&lt;!&ndash;                  <el-descriptions-item v-if="item.candidate" label-class-name="my-label">&ndash;&gt;-->
-<!--&lt;!&ndash;                    <template slot="label"><i class="el-icon-user"></i>鍊欓�夊姙鐞�</template>&ndash;&gt;-->
-<!--&lt;!&ndash;                    {{item.candidate}}&ndash;&gt;-->
-<!--&lt;!&ndash;                  </el-descriptions-item>&ndash;&gt;-->
-<!--&lt;!&ndash;                  <el-descriptions-item label-class-name="my-label">&ndash;&gt;-->
-<!--&lt;!&ndash;                    <template slot="label"><i class="el-icon-date"></i>鎺ユ敹鏃堕棿</template>&ndash;&gt;-->
-<!--&lt;!&ndash;                    {{item.createTime}}&ndash;&gt;-->
-<!--&lt;!&ndash;                  </el-descriptions-item>&ndash;&gt;-->
-<!--&lt;!&ndash;                  <el-descriptions-item v-if="item.finishTime" label-class-name="my-label">&ndash;&gt;-->
-<!--&lt;!&ndash;                    <template slot="label"><i class="el-icon-date"></i>澶勭悊鏃堕棿</template>&ndash;&gt;-->
-<!--&lt;!&ndash;                    {{item.finishTime}}&ndash;&gt;-->
-<!--&lt;!&ndash;                  </el-descriptions-item>&ndash;&gt;-->
-<!--&lt;!&ndash;                  <el-descriptions-item v-if="item.duration"  label-class-name="my-label">&ndash;&gt;-->
-<!--&lt;!&ndash;                    <template slot="label"><i class="el-icon-time"></i>鑰楁椂</template>&ndash;&gt;-->
-<!--&lt;!&ndash;                    {{item.duration}}&ndash;&gt;-->
-<!--&lt;!&ndash;                  </el-descriptions-item>&ndash;&gt;-->
-<!--&lt;!&ndash;                  <el-descriptions-item v-if="item.comment" label-class-name="my-label">&ndash;&gt;-->
-<!--&lt;!&ndash;                    <template slot="label"><i class="el-icon-tickets"></i>澶勭悊鎰忚</template>&ndash;&gt;-->
-<!--&lt;!&ndash;                    {{item.comment.comment}}&ndash;&gt;-->
-<!--&lt;!&ndash;                  </el-descriptions-item>&ndash;&gt;-->
-<!--&lt;!&ndash;                </el-descriptions>&ndash;&gt;-->
-<!--&lt;!&ndash;              </el-card>&ndash;&gt;-->
-<!--&lt;!&ndash;            </el-timeline-item>&ndash;&gt;-->
-<!--&lt;!&ndash;          </el-timeline>&ndash;&gt;-->
-<!--&lt;!&ndash;        </div>&ndash;&gt;-->
-<!--      </div>-->
-<!--    </el-dialog>-->
     <el-dialog :visible.sync="superviseShow" width="1000px"  title="鐫e姙" append-to-body>
       <el-form ref="superviseForm" :model="superviseForm" :rules="superviseRules" label-width="80px">
         <el-form-item label="鐫e姙鍐呭" prop="content">
diff --git a/src/views/workbench.vue b/src/views/workbench.vue
index cd36e6f..a7675dd 100644
--- a/src/views/workbench.vue
+++ b/src/views/workbench.vue
@@ -222,7 +222,15 @@
         <div class="schedule-list-section">
           <div class="section-header">
             <h3>鏃ョ▼鍒楄〃</h3>
-            <div>
+            <div class="header-actions">
+              <el-date-picker
+                v-model="calendarDate"
+                type="date"
+                placeholder="閫夋嫨鏃ユ湡"
+                size="mini"
+                style="width: 140px; margin-right: 10px"
+                :clearable="false"
+              />
               <el-button type="text" icon="el-icon-plus" @click="handleAddSchedule">鏂板</el-button>
               <el-button type="text" icon="el-icon-refresh" @click="getScheduleList" :disabled="scheduleRefreshing">鍒锋柊</el-button>
             </div>
@@ -375,12 +383,20 @@
         </el-calendar>
       </div>
       <div class="schedule-list-section">
-        <div class="section-header">
-          <h3>涓婃姤鍒楄〃</h3>
-          <div>
-            <el-button type="text" icon="el-icon-refresh" @click="getReportList" :disabled="reportRefreshing">鍒锋柊</el-button>
+          <div class="section-header">
+            <h3>涓婃姤鍒楄〃</h3>
+            <div class="header-actions">
+              <el-date-picker
+                v-model="reportCalendarDate"
+                type="date"
+                placeholder="閫夋嫨鏃ユ湡"
+                size="mini"
+                style="width: 140px; margin-right: 10px"
+                :clearable="false"
+              />
+              <el-button type="text" icon="el-icon-refresh" @click="getReportList" :disabled="reportRefreshing">鍒锋柊</el-button>
+            </div>
           </div>
-        </div>
         <div class="schedule-list">
           <el-table
             v-loading="reportLoading"
@@ -543,29 +559,25 @@
   },
   watch:{
     calendarDate(newDate, oldDate) {
-      // 鎺掗櫎銆岀偣鍑绘棩鏈熷崟鍏冩牸銆嶅鑷寸殑鍙樺寲锛堜粎鏃ユ湡鍙橈紝鏈堜唤/骞翠唤涓嶅彉锛�
-      const isOnlyDayChange =
-        newDate.getFullYear() === oldDate.getFullYear() &&
-        newDate.getMonth() === oldDate.getMonth() &&
-        newDate.getDate() !== oldDate.getDate();
+      // 妫�鏌ユ槸鍚﹁法鏈�/璺ㄥ勾
+      const isMonthChanged =
+        newDate.getFullYear() !== oldDate.getFullYear() ||
+        newDate.getMonth() !== oldDate.getMonth();
 
-      if (!isOnlyDayChange) {
-        // 瑙﹀彂鎸夐挳鐐瑰嚮閫昏緫锛堜笂鏈�/涓嬫湀/浠婃棩锛�
+      if (isMonthChanged) {
+        // 瑙﹀彂鍚庣鎷夊彇鏁存湀鏁版嵁
         this.getScheduleList();
-        this.lastCalendarDate = new Date(newDate); // 鏇存柊璁板綍鐨勬棩鏈�
       }
     },
     reportCalendarDate(newDate, oldDate) {
-      // 鎺掗櫎銆岀偣鍑绘棩鏈熷崟鍏冩牸銆嶅鑷寸殑鍙樺寲锛堜粎鏃ユ湡鍙橈紝鏈堜唤/骞翠唤涓嶅彉锛�
-      const isOnlyDayChange =
-        newDate.getFullYear() === oldDate.getFullYear() &&
-        newDate.getMonth() === oldDate.getMonth() &&
-        newDate.getDate() !== oldDate.getDate();
+      // 妫�鏌ユ槸鍚﹁法鏈�/璺ㄥ勾
+      const isMonthChanged =
+        newDate.getFullYear() !== oldDate.getFullYear() ||
+        newDate.getMonth() !== oldDate.getMonth();
 
-      if (!isOnlyDayChange) {
-        // 瑙﹀彂鎸夐挳鐐瑰嚮閫昏緫锛堜笂鏈�/涓嬫湀/浠婃棩锛�
+      if (isMonthChanged) {
+        // 瑙﹀彂鍚庣鎷夊彇鏁存湀鏁版嵁
         this.getReportList();
-        this.lastReportCalendarDate = new Date(newDate); // 鏇存柊璁板綍鐨勬棩鏈�
       }
     },
   },

--
Gitblit v1.8.0