zxl
4 天以前 eece5b838b0fe29ea003c355b1228de4795d3873
提交流程表单
5个文件已修改
1个文件已添加
689 ■■■■ 已修改文件
src/permission.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/WebViewEntry.vue 68 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/WebViewSend.vue 502 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/projectProcess/detail/index.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/workbench.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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()
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
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;
src/views/WebViewSend.vue
New file
@@ -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>
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="督办" append-to-body>
      <el-form ref="superviseForm" :model="superviseForm" :rules="superviseRules" label-width="80px">
        <el-form-item label="督办内容" prop="content">
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); // 更新记录的日期
      }
    },
  },