2026-01-19 17:34:15 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view class="dynamicModel-form-v jnpf-wrap jnpf-wrap-form" v-if="showPage">
|
|
|
|
|
|
<JnpfParser :formConf="formConf" ref="dynamicForm" @submit="sumbitForm" :key="key" v-if="!loading" />
|
2026-01-28 17:32:24 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 固定的底部审批记录(只显示第一条) -->
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-if="hasFixedApproval && showFixedApproval"
|
|
|
|
|
|
class="fixed-approval-bar"
|
|
|
|
|
|
:style="{ bottom: bottomOffset + 'rpx' }"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view class="fixed-approval-content" @tap="scrollToApproval">
|
|
|
|
|
|
<!-- 左侧:状态点 -->
|
|
|
|
|
|
<view class="fixed-status-icon" :class="getStatusClass(firstApproval?.result)"></view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 中间:审批信息 -->
|
|
|
|
|
|
<view class="fixed-approval-info">
|
|
|
|
|
|
<view class="fixed-approval-header">
|
|
|
|
|
|
<text class="fixed-task-name">任务:{{ firstApproval?.name || '' }}</text>
|
|
|
|
|
|
<text class="status-tag" :class="getStatusTagClass(firstApproval.result)">
|
|
|
|
|
|
{{ getStatusText(firstApproval.result) }}
|
|
|
|
|
|
<text class="status-time">{{ formatTime(firstApproval.createTime) }}</text>
|
|
|
|
|
|
</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="fixed-approval-user">
|
|
|
|
|
|
<text class="fixed-info-label">审批人:</text>
|
|
|
|
|
|
<text class="fixed-user-name">{{ firstApproval?.assigneeUser?.nickname || '' }}</text>
|
|
|
|
|
|
<text class="fixed-dept-tag">{{ firstApproval?.assigneeUser?.deptName || '' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="reason-box" v-if="firstApproval.reason">
|
|
|
|
|
|
{{ firstApproval.reason }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 右侧:向下箭头按钮 -->
|
|
|
|
|
|
<view class="fixed-scroll-btn" @tap.stop="scrollToApproval">
|
|
|
|
|
|
<text class="fixed-btn-text">更多</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 完整的审批记录区域 -->
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-if="approvalData.length && isApproval"
|
|
|
|
|
|
id="approval-record-all"
|
|
|
|
|
|
class="full-approval-section"
|
|
|
|
|
|
:class="{ 'has-fixed-bar': hasFixedApproval && showFixedApproval }"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view class="approval-title">审批记录</view>
|
|
|
|
|
|
<view class="approval-list">
|
|
|
|
|
|
<view
|
|
|
|
|
|
class="approval-item"
|
|
|
|
|
|
v-for="(item, index) in approvalData"
|
|
|
|
|
|
:key="item.id"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 左侧状态线 + 状态点 -->
|
|
|
|
|
|
<view class="approval-line-container">
|
|
|
|
|
|
<!-- 状态点 -->
|
|
|
|
|
|
<view class="status-icon" :class="getStatusClass(item.result)"></view>
|
|
|
|
|
|
<!-- 竖线(最后一条隐藏) -->
|
|
|
|
|
|
<view class="approval-line" v-if="index < approvalData.length - 1"></view>
|
|
|
|
|
|
</view>
|
2026-01-20 18:07:35 +08:00
|
|
|
|
|
2026-01-28 17:32:24 +08:00
|
|
|
|
<!-- 审批内容 -->
|
|
|
|
|
|
<view class="approval-content">
|
|
|
|
|
|
<!-- 任务名称 + 状态标签 -->
|
|
|
|
|
|
<view class="task-header">
|
|
|
|
|
|
<text class="task-name">任务:{{ item.name }}</text>
|
|
|
|
|
|
<view class="status-tag" :class="getStatusTagClass(item.result)">
|
|
|
|
|
|
{{ getStatusText(item.result) }}
|
|
|
|
|
|
<text class="status-time">{{ formatTime(item.createTime) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 审批人信息 -->
|
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
|
<text class="info-label">审批人:</text>
|
|
|
|
|
|
<text class="info-value">{{ item.assigneeUser?.nickname || '' }}</text>
|
|
|
|
|
|
<text class="info-value-tag">{{ item.assigneeUser?.deptName || '' }}</text>
|
|
|
|
|
|
<view class="pending-tag" v-if="item.result === 1">待审批</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<!-- 审批意见(有则显示) -->
|
|
|
|
|
|
<view class="reason-box" v-if="item.reason">
|
|
|
|
|
|
{{ item.reason }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-01-19 17:34:15 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 底部操作按钮区域 -->
|
2026-01-28 17:32:24 +08:00
|
|
|
|
<view v-if="!loading && isShowBtn" class="buttom-actions" id="bottom-actions">
|
2026-01-20 18:07:35 +08:00
|
|
|
|
<u-button v-if="(!dataForm.approveStatus && dataForm.id) || dataForm.approveStatus == 3" class="buttom-btn launch-flow-btn" type="primary" @click.stop="handleLaunchFlow" :disabled="idDsabled" :loading="btnLoading">
|
|
|
|
|
|
发起流程
|
2026-01-19 17:34:15 +08:00
|
|
|
|
</u-button>
|
2026-01-25 20:22:58 +08:00
|
|
|
|
<u-button v-if="!config.current && !dataForm.approveStatus" class="buttom-btn" type="primary" @click.stop="submit" :disabled="idDsabled" :loading="btnLoading">
|
2026-01-20 18:07:35 +08:00
|
|
|
|
保存
|
|
|
|
|
|
</u-button>
|
2026-01-25 20:22:58 +08:00
|
|
|
|
<u-button v-if="!config.current && (dataForm.approveStatus == 1 || dataForm.approveStatus == 2)" class="buttom-btn" type="primary" @click.stop="goProcess">
|
|
|
|
|
|
查看流程
|
|
|
|
|
|
</u-button>
|
2026-01-27 09:00:58 +08:00
|
|
|
|
<u-button v-if="config.current == 1 && dataForm.approveStatus && dataForm.approveStatus !==0" class="buttom-btn" type="error" @click.stop="handleCancle">
|
|
|
|
|
|
取消
|
|
|
|
|
|
</u-button>
|
2026-01-20 18:07:35 +08:00
|
|
|
|
<u-button v-if="config.current == 2" class="buttom-btn" type="error" @click.stop="handleFail" :disabled="idDsabled" :loading="btnLoading">
|
|
|
|
|
|
不通过
|
|
|
|
|
|
</u-button>
|
|
|
|
|
|
<u-button v-if="config.current == 2" class="buttom-btn launch-flow-btn" type="primary" @click.stop="handleThrough" :disabled="idDsabled" :loading="btnLoading">
|
|
|
|
|
|
通过
|
|
|
|
|
|
</u-button>
|
|
|
|
|
|
<view class="more-btn" @click.stop="toggleMoreMenu" v-if="dataForm.id && moreMenuList.length && !idDsabled || config.current == 2">
|
2026-01-19 17:34:15 +08:00
|
|
|
|
<u-icon name="more-dot-fill" size="34"></u-icon>
|
|
|
|
|
|
<text class="more-text">{{$t('common.moreText')}}</text>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="more-menu" v-show="showMoreMenu" @click.stop>
|
|
|
|
|
|
<view
|
|
|
|
|
|
class="menu-item"
|
|
|
|
|
|
v-for="(item, index) in moreMenuList"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
:style="{color: item.color || '#333'}"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view @click.stop="handleMoreMenuClick(item)">
|
|
|
|
|
|
<text class="menu-label">{{item.label}}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 流程选择弹框 -->
|
|
|
|
|
|
<u-popup v-model="showProcessDialog" mode="center" :round="10" >
|
|
|
|
|
|
<view class="process-dialog">
|
|
|
|
|
|
<view class="dialog-header">
|
|
|
|
|
|
<text class="dialog-title">选择流程表单</text>
|
|
|
|
|
|
<u-icon name="close" size="24" color="#999" @click="closeProcessDialog" class="close-icon"></u-icon>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="process-list" v-if="!loadingProcess">
|
2026-01-22 11:01:19 +08:00
|
|
|
|
<u-radio-group v-model="selectedProcess" @change="selectProcess">
|
2026-01-19 17:34:15 +08:00
|
|
|
|
<view
|
|
|
|
|
|
class="process-item"
|
|
|
|
|
|
v-for="(item, index) in processList"
|
|
|
|
|
|
:key="item.id"
|
2026-01-22 11:01:19 +08:00
|
|
|
|
:class="{'selected-item': selectedProcess && selectedProcess === item.id}"
|
2026-01-19 17:34:15 +08:00
|
|
|
|
>
|
|
|
|
|
|
<view class="process-item-left">
|
|
|
|
|
|
<u-radio :name="item.id">{{ item.name }}</u-radio>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</u-radio-group>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="empty-process" v-if="processList.length === 0">
|
|
|
|
|
|
<u-icon name="list" size="60" color="#c0c4cc"></u-icon>
|
|
|
|
|
|
<text class="empty-text">暂无可用流程</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="loading-process" v-else>
|
|
|
|
|
|
<u-loading mode="circle" size="40"></u-loading>
|
|
|
|
|
|
<text class="loading-text">加载中...</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="dialog-footer" v-if="processList.length > 0">
|
|
|
|
|
|
<u-button class="footer-btn cancel-btn" @click="closeProcessDialog">取消</u-button>
|
|
|
|
|
|
<u-button class="footer-btn submit-btn" type="primary" :disabled="!selectedProcess" @click="launchSelectedProcess">提交</u-button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</u-popup>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 审批人弹框 -->
|
|
|
|
|
|
<u-popup v-model="showApproverDialog" mode="center" :round="10" @close="closeApproverDialog">
|
|
|
|
|
|
<view class="approver-dialog">
|
|
|
|
|
|
<view class="dialog-header">
|
|
|
|
|
|
<text class="dialog-title">发起人选择审批人</text>
|
|
|
|
|
|
<u-icon name="close" size="24" color="#999" @click="closeApproverDialog" class="close-icon"></u-icon>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="approver-content" v-if="!loadingApprover && approverList.length > 0">
|
|
|
|
|
|
<view class="approver-node" v-for="(node, index) in approverList" :key="node.id">
|
|
|
|
|
|
<view class="node-title">
|
|
|
|
|
|
<text class="node-label">审批节点:</text>
|
|
|
|
|
|
<text class="node-name">{{ node.taskDefinitionName || '未命名节点' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="candidate-section">
|
|
|
|
|
|
<view class="section-title">
|
|
|
|
|
|
<text class="section-label">候选人<text style="color:red">*</text></text>
|
|
|
|
|
|
<view>
|
|
|
|
|
|
<user-select
|
|
|
|
|
|
v-model="selectedCandidates"
|
|
|
|
|
|
:placeholder="'请选择审批人'"
|
|
|
|
|
|
:multiple="false"
|
|
|
|
|
|
:selectType="type"
|
|
|
|
|
|
@change="(value, data) => handleUserChange(node, value, data,index)"
|
|
|
|
|
|
style="margin-top: 10rpx;"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="loading-approver" v-else-if="loadingApprover">
|
|
|
|
|
|
<u-loading mode="circle" size="40"></u-loading>
|
|
|
|
|
|
<text class="loading-text">加载中...</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="empty-approver" v-else>
|
|
|
|
|
|
<u-icon name="user" size="60" color="#c0c4cc"></u-icon>
|
|
|
|
|
|
<text class="empty-text">暂无审批人</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="dialog-footer" v-if="approverList.length > 0">
|
|
|
|
|
|
<u-button class="footer-btn confirm-btn" type="primary" :disabled="!isAllCandidatesSelected" @click="confirmLaunchFlow">确定</u-button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</u-popup>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 审批意见输入弹窗 -->
|
|
|
|
|
|
<u-popup
|
|
|
|
|
|
v-model="showApprovalReasonDialog"
|
|
|
|
|
|
mode="center"
|
|
|
|
|
|
:round="10"
|
|
|
|
|
|
:closeOnClickOverlay="false"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view class="approval-reason-dialog">
|
|
|
|
|
|
<!-- 弹窗标题 -->
|
|
|
|
|
|
<view class="dialog-header">
|
|
|
|
|
|
<text class="dialog-title">{{ approvalType === 'through' ? '审批通过' : '审批不通过' }}</text>
|
|
|
|
|
|
<u-icon name="close" size="24" color="#999" @click="showApprovalReasonDialog = false" class="close-icon"></u-icon>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 输入框区域 -->
|
|
|
|
|
|
<view class="dialog-content">
|
2026-01-27 09:00:58 +08:00
|
|
|
|
<view class="input-label">
|
|
|
|
|
|
审批意见
|
|
|
|
|
|
<text style="color: red;">*</text>
|
|
|
|
|
|
</view>
|
2026-01-19 17:34:15 +08:00
|
|
|
|
<u-input
|
|
|
|
|
|
v-model="approvalReason"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
placeholder="请输入审批意见(选填)"
|
|
|
|
|
|
:rows="4"
|
|
|
|
|
|
border="surround"
|
|
|
|
|
|
class="reason-input"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 按钮区域 -->
|
|
|
|
|
|
<view class="dialog-footer">
|
|
|
|
|
|
<u-button class="footer-btn cancel-btn" @click="showApprovalReasonDialog = false">取消</u-button>
|
|
|
|
|
|
<u-button
|
|
|
|
|
|
class="footer-btn submit-btn"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
@click="submitApprovalResult"
|
|
|
|
|
|
:loading="approvalSubmitting"
|
|
|
|
|
|
>
|
|
|
|
|
|
确认提交
|
|
|
|
|
|
</u-button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</u-popup>
|
2026-01-20 18:07:35 +08:00
|
|
|
|
<!-- 抄送规则弹窗 -->
|
|
|
|
|
|
<u-popup v-model="showCopyDialog" mode="center" :round="10">
|
|
|
|
|
|
<view class="copy-dialog">
|
|
|
|
|
|
<view class="dialog-header">
|
|
|
|
|
|
<text class="dialog-title">修改任务规则</text>
|
|
|
|
|
|
<u-icon name="close" size="24" color="#999" @click="showCopyDialog = false" class="close-icon"></u-icon>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="dialog-content">
|
|
|
|
|
|
<view class="form-item-user required">
|
2026-01-27 09:00:58 +08:00
|
|
|
|
<view>
|
|
|
|
|
|
<text class="label">指定用户</text>
|
|
|
|
|
|
<text style="color: red;">*</text>
|
|
|
|
|
|
</view>
|
2026-01-20 18:07:35 +08:00
|
|
|
|
<view>
|
|
|
|
|
|
<user-select
|
|
|
|
|
|
v-model="copyForm.options"
|
|
|
|
|
|
placeholder="请选择用户"
|
|
|
|
|
|
:multiple="true"
|
|
|
|
|
|
:selectType="type"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="form-item required">
|
|
|
|
|
|
<text class="label">抄送原因</text>
|
|
|
|
|
|
<u-input
|
|
|
|
|
|
v-model="copyForm.reason"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
placeholder="请输入抄送原因"
|
|
|
|
|
|
:rows="3"
|
|
|
|
|
|
border="surround"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="dialog-footer">
|
|
|
|
|
|
<u-button class="footer-btn cancel-btn" @click="showCopyDialog = false">取消</u-button>
|
|
|
|
|
|
<u-button class="footer-btn submit-btn" type="primary" @click="submitCopyRule" :loading="copySubmitting">确定</u-button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</u-popup>
|
2026-01-27 09:00:58 +08:00
|
|
|
|
<!-- 取消原因弹框 -->
|
|
|
|
|
|
<u-popup v-model="showCacleDialog" mode="center" :round="10">
|
|
|
|
|
|
<view class="copy-dialog">
|
|
|
|
|
|
<view class="dialog-header">
|
|
|
|
|
|
<text class="dialog-title">取消流程</text>
|
|
|
|
|
|
<u-icon name="close" size="24" color="#999" @click="showCacleDialog = false" class="close-icon"></u-icon>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="dialog-content">
|
|
|
|
|
|
<view class="form-item required">
|
|
|
|
|
|
<text class="label">取消原因</text>
|
|
|
|
|
|
<u-input
|
|
|
|
|
|
v-model="cacleReason"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
placeholder="请输入取消原因"
|
|
|
|
|
|
:rows="3"
|
|
|
|
|
|
border="surround"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="dialog-footer">
|
|
|
|
|
|
<u-button class="footer-btn cancel-btn" @click="showCacleDialog = false">取消</u-button>
|
|
|
|
|
|
<u-button class="footer-btn submit-btn" type="primary" @click="submitCancel" :loading="cacleSubmitting">确定</u-button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</u-popup>
|
2026-01-19 17:34:15 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import CustomButton from '@/components/CustomButton'
|
|
|
|
|
|
import {
|
|
|
|
|
|
getDesForm,
|
|
|
|
|
|
createModel,
|
|
|
|
|
|
updateModel,
|
|
|
|
|
|
getModelInfo,
|
|
|
|
|
|
getdbformlist,
|
|
|
|
|
|
getAssignList,
|
|
|
|
|
|
getListCreate,
|
|
|
|
|
|
deteleModel,
|
|
|
|
|
|
launchFlow,
|
|
|
|
|
|
getByProcess
|
|
|
|
|
|
} from '@/api/apply/visualDev'
|
|
|
|
|
|
import UserSelect from '@/components/Jnpf/CandidateSelect'
|
|
|
|
|
|
import {getOrganizeSelector} from '@/api/common.js'
|
2026-01-28 17:32:24 +08:00
|
|
|
|
|
2026-01-19 17:34:15 +08:00
|
|
|
|
export default {
|
|
|
|
|
|
components: {
|
|
|
|
|
|
CustomButton,
|
|
|
|
|
|
UserSelect
|
|
|
|
|
|
},
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
webType: '',
|
|
|
|
|
|
type: 'all',
|
|
|
|
|
|
showPage: false,
|
|
|
|
|
|
btnLoading: false,
|
|
|
|
|
|
loading: true,
|
|
|
|
|
|
isPreview: '0',
|
|
|
|
|
|
modelId: '',
|
|
|
|
|
|
formConf: {},
|
|
|
|
|
|
formData: {},
|
|
|
|
|
|
dataForm: {
|
|
|
|
|
|
id: '',
|
|
|
|
|
|
data: ''
|
|
|
|
|
|
},
|
|
|
|
|
|
btnType: '',
|
|
|
|
|
|
formPermissionList: {},
|
|
|
|
|
|
formList: [],
|
|
|
|
|
|
key: +new Date(),
|
|
|
|
|
|
config: {},
|
|
|
|
|
|
clickType: 'submit',
|
|
|
|
|
|
prevDis: false,
|
|
|
|
|
|
nextDis: false,
|
|
|
|
|
|
index: 0,
|
|
|
|
|
|
userInfo: {},
|
|
|
|
|
|
isAdd: false,
|
|
|
|
|
|
showMoreMenu: false,
|
|
|
|
|
|
moreMenuList: [],
|
2026-01-28 17:32:24 +08:00
|
|
|
|
createData: {},
|
2026-01-19 17:34:15 +08:00
|
|
|
|
deptList: [],
|
|
|
|
|
|
showProcessDialog: false,
|
|
|
|
|
|
processList: [],
|
|
|
|
|
|
selectedProcess: '',
|
|
|
|
|
|
loadingProcess: false,
|
|
|
|
|
|
showApproverDialog: false,
|
|
|
|
|
|
approverList: [],
|
|
|
|
|
|
selectedApprover: null,
|
|
|
|
|
|
loadingApprover: false,
|
|
|
|
|
|
currentProcessId: '',
|
|
|
|
|
|
selectedCandidates: {},
|
2026-01-28 17:32:24 +08:00
|
|
|
|
approvalData: [],
|
|
|
|
|
|
isProcess: false,
|
|
|
|
|
|
showApprovalReasonDialog: false,
|
|
|
|
|
|
approvalType: '',
|
|
|
|
|
|
approvalReason: '',
|
|
|
|
|
|
approvalSubmitting: false,
|
2026-01-20 18:07:35 +08:00
|
|
|
|
showCopyDialog: false,
|
|
|
|
|
|
copySubmitting: false,
|
|
|
|
|
|
copyForm: {
|
|
|
|
|
|
processInstanceKey: '',
|
|
|
|
|
|
processInstanceName: '',
|
|
|
|
|
|
options: {},
|
|
|
|
|
|
reason: ''
|
2026-01-27 09:00:58 +08:00
|
|
|
|
},
|
|
|
|
|
|
showCacleDialog: false,
|
|
|
|
|
|
cacleReason: '',
|
|
|
|
|
|
cacleSubmitting: false,
|
2026-01-28 17:32:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 新增:固定审批记录相关
|
|
|
|
|
|
showFixedApproval: true,
|
|
|
|
|
|
isScrollingToApproval: false,
|
|
|
|
|
|
scrollTimer: null,
|
|
|
|
|
|
// bottomOffset: 100,
|
|
|
|
|
|
scrollHandler: null, // 用于存储滚动监听函数
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
getOkText() {
|
2026-01-20 18:07:35 +08:00
|
|
|
|
return this.$t('common.saveText');
|
2026-01-19 17:34:15 +08:00
|
|
|
|
},
|
2026-01-20 18:07:35 +08:00
|
|
|
|
getProcessText() {
|
|
|
|
|
|
return '发起流程'
|
2026-01-19 17:34:15 +08:00
|
|
|
|
},
|
|
|
|
|
|
isAllCandidatesSelected() {
|
|
|
|
|
|
const result = this.approverList.every(item=>{
|
|
|
|
|
|
const {taskDefinitionKey} = item
|
|
|
|
|
|
return !!item[taskDefinitionKey]
|
|
|
|
|
|
})
|
|
|
|
|
|
return result
|
|
|
|
|
|
},
|
|
|
|
|
|
idDsabled() {
|
|
|
|
|
|
const {approveStatus} = this.dataForm
|
|
|
|
|
|
return approveStatus == 2 || approveStatus == 4
|
2026-01-22 11:01:19 +08:00
|
|
|
|
},
|
|
|
|
|
|
isApproval(){
|
|
|
|
|
|
const {current} = this.config
|
2026-01-25 20:22:58 +08:00
|
|
|
|
return !!current || this.isProcess
|
2026-01-27 09:00:58 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 是否显示底部按钮
|
|
|
|
|
|
isShowBtn(){
|
2026-01-27 15:42:20 +08:00
|
|
|
|
const {approveStatus} = this.dataForm
|
2026-01-27 09:00:58 +08:00
|
|
|
|
const {config,isProcess} = this
|
2026-01-27 15:42:20 +08:00
|
|
|
|
if(config.current == 1 && approveStatus == 0) return false
|
|
|
|
|
|
if(config.current == 2) return true
|
|
|
|
|
|
if(config.current == 3 || config.current == 4) return false
|
2026-01-27 09:00:58 +08:00
|
|
|
|
if(!isProcess) return true
|
2026-01-27 15:42:20 +08:00
|
|
|
|
if(isProcess) return false
|
2026-01-27 09:00:58 +08:00
|
|
|
|
return false
|
2026-01-28 17:32:24 +08:00
|
|
|
|
},
|
|
|
|
|
|
bottomOffset(){
|
|
|
|
|
|
if(!this.loading && this.isShowBtn) return 85
|
|
|
|
|
|
return 0
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
hasFixedApproval() {
|
|
|
|
|
|
console.log(this.approvalData.length > 0 && this.isApproval,'判断---')
|
|
|
|
|
|
return this.approvalData.length > 0 && this.isApproval
|
|
|
|
|
|
},
|
|
|
|
|
|
firstApproval() {
|
|
|
|
|
|
console.log(this.approvalData[0],'数据--')
|
|
|
|
|
|
return this.approvalData.length > 0 ? this.approvalData[0] : null
|
|
|
|
|
|
},
|
2026-01-19 17:34:15 +08:00
|
|
|
|
},
|
|
|
|
|
|
onLoad(option) {
|
|
|
|
|
|
this.init(option)
|
|
|
|
|
|
},
|
2026-01-28 17:32:24 +08:00
|
|
|
|
onReady() {
|
|
|
|
|
|
this.calculateBottomOffset()
|
|
|
|
|
|
this.bindPageScroll()
|
|
|
|
|
|
},
|
|
|
|
|
|
onUnload() {
|
|
|
|
|
|
// 清除滚动监听
|
|
|
|
|
|
if (this.scrollHandler) {
|
|
|
|
|
|
uni.$off('scroll', this.scrollHandler)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (this.scrollTimer) {
|
|
|
|
|
|
clearTimeout(this.scrollTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
onPageScroll(e) {
|
|
|
|
|
|
console.log('页面滚动事件触发:', e.scrollTop)
|
|
|
|
|
|
|
|
|
|
|
|
// 如果正在滚动到审批区域,不处理
|
|
|
|
|
|
if (this.isScrollingToApproval) return
|
|
|
|
|
|
|
|
|
|
|
|
// 防抖处理
|
|
|
|
|
|
if (this.scrollTimer) {
|
|
|
|
|
|
clearTimeout(this.scrollTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.scrollTimer = setTimeout(() => {
|
|
|
|
|
|
this.checkApprovalVisibility()
|
|
|
|
|
|
}, 50)
|
|
|
|
|
|
},
|
2026-01-19 17:34:15 +08:00
|
|
|
|
methods: {
|
|
|
|
|
|
init(option) {
|
|
|
|
|
|
const parseConfig = (rawConfig) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return JSON.parse(this.jnpf.base64.decode(rawConfig)) || {}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const config = parseConfig(option.config)
|
|
|
|
|
|
const {
|
|
|
|
|
|
index,
|
|
|
|
|
|
currentMenu,
|
|
|
|
|
|
btnType = '',
|
|
|
|
|
|
modelId,
|
|
|
|
|
|
isPreview = '0',
|
2026-01-20 18:07:35 +08:00
|
|
|
|
id = '',
|
2026-01-19 17:34:15 +08:00
|
|
|
|
} = config
|
|
|
|
|
|
const formPermissionList =[]
|
|
|
|
|
|
Object.assign(this, {
|
|
|
|
|
|
userInfo: uni.getStorageSync('userInfo') || {},
|
|
|
|
|
|
config,
|
|
|
|
|
|
index,
|
|
|
|
|
|
formPermissionList,
|
|
|
|
|
|
formList: formPermissionList?.formList || [],
|
|
|
|
|
|
btnType,
|
|
|
|
|
|
modelId,
|
|
|
|
|
|
isPreview,
|
|
|
|
|
|
dataForm: {
|
|
|
|
|
|
id
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (id) {
|
|
|
|
|
|
this.initMoreMenuList()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 11:01:19 +08:00
|
|
|
|
const getNavigationTitle = () =>{
|
|
|
|
|
|
let titleName = ''
|
2026-01-25 20:22:58 +08:00
|
|
|
|
if(this.config.current || this.isProcess){
|
2026-01-22 11:01:19 +08:00
|
|
|
|
titleName = this.config.name + '详情'
|
|
|
|
|
|
}else {
|
|
|
|
|
|
const name = this.dataForm.id ? this.$t('common.editText') : this.$t('common.addText')
|
|
|
|
|
|
titleName = name + this.config.name
|
|
|
|
|
|
}
|
|
|
|
|
|
return titleName
|
|
|
|
|
|
}
|
2026-01-19 17:34:15 +08:00
|
|
|
|
uni.setNavigationBarTitle({
|
2026-01-22 11:01:19 +08:00
|
|
|
|
title: getNavigationTitle()
|
2026-01-19 17:34:15 +08:00
|
|
|
|
})
|
|
|
|
|
|
this.getDesForm()
|
|
|
|
|
|
},
|
2026-01-28 17:32:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 计算底部按钮高度
|
|
|
|
|
|
calculateBottomOffset() {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
const query = uni.createSelectorQuery().in(this)
|
|
|
|
|
|
query.select('#bottom-actions').boundingClientRect(data => {
|
|
|
|
|
|
if (data) {
|
|
|
|
|
|
this.bottomOffset = data.height + 20
|
|
|
|
|
|
}
|
|
|
|
|
|
}).exec()
|
|
|
|
|
|
}, 300)
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 监听页面滚动
|
|
|
|
|
|
bindPageScroll() {
|
|
|
|
|
|
// 初始检查一次
|
|
|
|
|
|
this.checkApprovalVisibility()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 检查审批记录是否可见
|
|
|
|
|
|
checkApprovalVisibility() {
|
|
|
|
|
|
if (!this.hasFixedApproval || this.isScrollingToApproval) return
|
|
|
|
|
|
|
|
|
|
|
|
const query = uni.createSelectorQuery().in(this)
|
|
|
|
|
|
query.select('#approval-record-all').boundingClientRect(data => {
|
|
|
|
|
|
if (!data) {
|
|
|
|
|
|
this.showFixedApproval = true
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取窗口高度
|
|
|
|
|
|
const windowHeight = uni.getSystemInfoSync().windowHeight || 667
|
|
|
|
|
|
|
|
|
|
|
|
// 计算审批记录区域底部距离屏幕顶部的距离
|
|
|
|
|
|
const approvalBottom = data.top + data.height
|
|
|
|
|
|
console.log(approvalBottom,'approvalBottom--')
|
|
|
|
|
|
|
|
|
|
|
|
// 判断:如果审批记录区域底部在屏幕内(能看到最下方的审批记录),则隐藏固定底栏
|
|
|
|
|
|
// 这里使用一个阈值,比如底部距离屏幕顶部小于窗口高度的90%(即能看到大部分)
|
|
|
|
|
|
const isBottomVisible = approvalBottom <= windowHeight * 0.9
|
|
|
|
|
|
|
|
|
|
|
|
// 或者判断整个区域是否在视口内
|
|
|
|
|
|
const isFullyVisible = data.top >= 0 && data.bottom <= windowHeight
|
|
|
|
|
|
|
|
|
|
|
|
// 只要能看到最下方的审批记录(底部在视口内),就隐藏固定底栏
|
|
|
|
|
|
// 看不到最下方的审批记录时,显示固定底栏
|
|
|
|
|
|
this.showFixedApproval = !isBottomVisible && !isFullyVisible
|
|
|
|
|
|
|
|
|
|
|
|
console.log('审批记录可见性检查:', {
|
|
|
|
|
|
top: data.top,
|
|
|
|
|
|
bottom: data.bottom,
|
|
|
|
|
|
approvalBottom,
|
|
|
|
|
|
windowHeight,
|
|
|
|
|
|
isBottomVisible,
|
|
|
|
|
|
isFullyVisible,
|
|
|
|
|
|
showFixedApproval: this.showFixedApproval
|
|
|
|
|
|
})
|
|
|
|
|
|
}).exec()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 滚动到审批记录区域
|
|
|
|
|
|
scrollToApproval() {
|
|
|
|
|
|
this.isScrollingToApproval = true
|
|
|
|
|
|
this.showFixedApproval = false
|
|
|
|
|
|
|
|
|
|
|
|
// 先滚动到审批记录区域
|
|
|
|
|
|
uni.pageScrollTo({
|
|
|
|
|
|
selector: '#approval-record-all',
|
|
|
|
|
|
duration: 300,
|
|
|
|
|
|
success: () => {
|
|
|
|
|
|
// 滚动完成后,延迟检查可见性
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.isScrollingToApproval = false
|
|
|
|
|
|
this.checkApprovalVisibility()
|
|
|
|
|
|
}, 500)
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: () => {
|
|
|
|
|
|
this.isScrollingToApproval = false
|
|
|
|
|
|
this.scrollToApprovalByPosition()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 备选滚动方法
|
|
|
|
|
|
scrollToApprovalByPosition() {
|
|
|
|
|
|
const query = uni.createSelectorQuery().in(this)
|
|
|
|
|
|
query.select('#approval-record-all').boundingClientRect(data => {
|
|
|
|
|
|
if (data) {
|
|
|
|
|
|
uni.pageScrollTo({
|
|
|
|
|
|
scrollTop: data.top - 50,
|
|
|
|
|
|
duration: 300,
|
|
|
|
|
|
success: () => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.isScrollingToApproval = false
|
|
|
|
|
|
this.checkApprovalVisibility()
|
|
|
|
|
|
}, 500)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}).exec()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 获取状态文本
|
|
|
|
|
|
getStatusText(result) {
|
|
|
|
|
|
switch(result) {
|
|
|
|
|
|
case 1: return '审批中'
|
|
|
|
|
|
case 2: return '已提交'
|
|
|
|
|
|
case 3: return '不通过'
|
|
|
|
|
|
default: return '未知'
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
handleUserChange(node, value, data,index) {
|
2026-01-19 17:34:15 +08:00
|
|
|
|
const name = this.approverList[index]['taskDefinitionKey']
|
2026-01-28 17:32:24 +08:00
|
|
|
|
this.approverList[index] = {
|
2026-01-19 17:34:15 +08:00
|
|
|
|
...this.approverList[index],
|
|
|
|
|
|
[name] : value
|
|
|
|
|
|
}
|
2026-01-20 18:07:35 +08:00
|
|
|
|
},
|
|
|
|
|
|
handleRuleUserChange(node, value, data){
|
|
|
|
|
|
|
2026-01-19 17:34:15 +08:00
|
|
|
|
},
|
|
|
|
|
|
confirmLaunchFlow() {
|
|
|
|
|
|
const result = this.approverList.every(item=>{
|
|
|
|
|
|
const {taskDefinitionKey} = item
|
|
|
|
|
|
return !!item[taskDefinitionKey]
|
|
|
|
|
|
})
|
|
|
|
|
|
if(!result){
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '请选择候选人',
|
|
|
|
|
|
icon: 'error'
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const assignee = {}
|
|
|
|
|
|
this.approverList.forEach(item=>{
|
|
|
|
|
|
const key = item.taskDefinitionKey;
|
|
|
|
|
|
assignee[key] = item[key];
|
|
|
|
|
|
})
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
processType: 0,
|
|
|
|
|
|
processDefinitionId: this.selectedProcess,
|
|
|
|
|
|
id: this.dataForm.id,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
assignee,
|
|
|
|
|
|
dbFormId : this.modelId,
|
|
|
|
|
|
dataId: this.dataForm.id,
|
|
|
|
|
|
processDefinitionId: this.selectedProcess
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-20 18:07:35 +08:00
|
|
|
|
this.getListCreateData(params)
|
|
|
|
|
|
},
|
|
|
|
|
|
// 请求日志
|
|
|
|
|
|
getListCreateData(params){
|
2026-01-19 17:34:15 +08:00
|
|
|
|
getListCreate(params,this.modelId).then(res=>{
|
|
|
|
|
|
if(res.code == 0) {
|
2026-01-25 20:22:58 +08:00
|
|
|
|
let name = '发起成功'
|
|
|
|
|
|
switch(this.approvalType){
|
|
|
|
|
|
case 'through':
|
|
|
|
|
|
name = '审批通过成功'
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'fail':
|
|
|
|
|
|
name = '审批不通过成功'
|
|
|
|
|
|
break;
|
2026-01-27 09:00:58 +08:00
|
|
|
|
case 'cancel':
|
|
|
|
|
|
name = '取消成功'
|
|
|
|
|
|
break;
|
2026-01-25 20:22:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-19 17:34:15 +08:00
|
|
|
|
uni.showToast({
|
2026-01-25 20:22:58 +08:00
|
|
|
|
title: name,
|
2026-01-19 17:34:15 +08:00
|
|
|
|
icon: 'success'
|
|
|
|
|
|
})
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
uni.$emit('refresh')
|
|
|
|
|
|
uni.navigateBack()
|
|
|
|
|
|
}, 1500)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
initMoreMenuList() {
|
2026-01-28 17:32:24 +08:00
|
|
|
|
let menuList = []
|
2026-01-20 18:07:35 +08:00
|
|
|
|
console.log(this.dataForm.approveStatus,'approveStatus--')
|
|
|
|
|
|
if((!this.dataForm.approveStatus && this.dataForm.id) || this.dataForm.approveStatus == 3){
|
|
|
|
|
|
// 未审核 | 驳回
|
|
|
|
|
|
menuList.push({
|
2026-01-19 17:34:15 +08:00
|
|
|
|
label: '删除',
|
|
|
|
|
|
value: 'delete',
|
|
|
|
|
|
icon: 'icon-ym icon-ym-app-delete',
|
|
|
|
|
|
color: '#dd524d'
|
2026-01-20 18:07:35 +08:00
|
|
|
|
})
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
2026-01-20 18:07:35 +08:00
|
|
|
|
if(this.config.current == 2){
|
2026-01-19 17:34:15 +08:00
|
|
|
|
menuList.unshift({
|
2026-01-20 18:07:35 +08:00
|
|
|
|
label: '抄送',
|
|
|
|
|
|
value: 'send',
|
2026-01-19 17:34:15 +08:00
|
|
|
|
icon: 'icon-ym icon-ym-flow-launch',
|
2026-01-20 18:07:35 +08:00
|
|
|
|
color: '#409eff'
|
2026-01-19 17:34:15 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-01-20 18:07:35 +08:00
|
|
|
|
label: '回退',
|
|
|
|
|
|
value: 'reject',
|
2026-01-19 17:34:15 +08:00
|
|
|
|
icon: 'icon-ym icon-ym-flow-launch',
|
2026-01-20 18:07:35 +08:00
|
|
|
|
color: '#e6a23c'
|
2026-01-19 17:34:15 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
this.moreMenuList = menuList
|
|
|
|
|
|
},
|
|
|
|
|
|
toggleMoreMenu() {
|
|
|
|
|
|
this.showMoreMenu = !this.showMoreMenu
|
|
|
|
|
|
if (this.showMoreMenu) {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
uni.$once('clickOutside', () => {
|
|
|
|
|
|
this.showMoreMenu = false
|
|
|
|
|
|
})
|
|
|
|
|
|
}, 100)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
handleMoreMenuClick(item) {
|
|
|
|
|
|
this.showMoreMenu = false
|
|
|
|
|
|
switch (item.value) {
|
|
|
|
|
|
case 'edit':
|
|
|
|
|
|
this.handleEdit()
|
|
|
|
|
|
break
|
2026-01-20 18:07:35 +08:00
|
|
|
|
case 'send':
|
|
|
|
|
|
this.handleSend()
|
|
|
|
|
|
break
|
|
|
|
|
|
case 'reject':
|
|
|
|
|
|
this.handleReject()
|
2026-01-19 17:34:15 +08:00
|
|
|
|
break
|
|
|
|
|
|
case 'delete':
|
|
|
|
|
|
this.handleDelete()
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
handleEdit() {
|
|
|
|
|
|
this.btnType = 'btn_edit'
|
|
|
|
|
|
this.initData()
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '进入编辑状态',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
handleLaunchFlow() {
|
|
|
|
|
|
this.getProcessList()
|
|
|
|
|
|
},
|
|
|
|
|
|
async getProcessList() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.loadingProcess = true
|
|
|
|
|
|
const res = await getdbformlist(this.modelId)
|
|
|
|
|
|
if (res.code === 0 || res.code === 200) {
|
|
|
|
|
|
this.processList = res.data || []
|
2026-01-22 11:01:19 +08:00
|
|
|
|
if(this.processList.length === 1){
|
2026-01-27 09:00:58 +08:00
|
|
|
|
this.selectProcess(res.data[0].id)
|
2026-01-22 11:01:19 +08:00
|
|
|
|
}
|
2026-01-19 17:34:15 +08:00
|
|
|
|
if (this.processList.length === 0) {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '暂无可用流程',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.showProcessDialog = true
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: res.msg || '获取流程列表失败',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取流程列表失败:', error)
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '获取流程列表失败',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.loadingProcess = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-20 18:07:35 +08:00
|
|
|
|
// 抄送
|
|
|
|
|
|
handleSend() {
|
|
|
|
|
|
this.showCopyDialog = true
|
|
|
|
|
|
},
|
|
|
|
|
|
// 抄送-提交
|
|
|
|
|
|
submitCopyRule(){
|
2026-01-27 09:00:58 +08:00
|
|
|
|
const {options,reason} = this.copyForm
|
|
|
|
|
|
if(!options.length || !reason){
|
|
|
|
|
|
const name = !options.length ? '指定用户不能为空' : '抄送原因不能为空'
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: name,
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-01-20 18:07:35 +08:00
|
|
|
|
const {id,processInstance} = this.approvalData[0]
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
processType: '4',
|
|
|
|
|
|
processDefinitionId:Object.values(this.copyForm.options),
|
|
|
|
|
|
processInstanceId: this.formData.processInstanceId,
|
|
|
|
|
|
id: this.dataForm.id,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
startUserId: processInstance.startUserId,
|
|
|
|
|
|
taskId: id,
|
|
|
|
|
|
...this.copyForm,
|
|
|
|
|
|
options: Object.values(this.copyForm.options),
|
|
|
|
|
|
processInstanceKey:processInstance.id,
|
|
|
|
|
|
processInstanceName:processInstance.name
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
this.getListCreateData(params)
|
|
|
|
|
|
},
|
|
|
|
|
|
handleReject() {
|
|
|
|
|
|
const {id,processInstance} = this.approvalData[0]
|
|
|
|
|
|
const params = {
|
2026-01-28 17:32:24 +08:00
|
|
|
|
processType: '3',
|
2026-01-20 18:07:35 +08:00
|
|
|
|
processInstanceId: this.formData.processInstanceId,
|
|
|
|
|
|
id: this.dataForm.id,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
taskId: id
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
this.getListCreateData(params)
|
|
|
|
|
|
},
|
2026-01-19 17:34:15 +08:00
|
|
|
|
selectProcess(item) {
|
2026-01-22 11:01:19 +08:00
|
|
|
|
console.log(item,'item----')
|
2026-01-19 17:34:15 +08:00
|
|
|
|
this.selectedProcess = item
|
|
|
|
|
|
},
|
|
|
|
|
|
async launchSelectedProcess() {
|
|
|
|
|
|
if (!this.selectedProcess) {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '请选择一个流程',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.showProcessDialog = false
|
|
|
|
|
|
await this.getApproverList()
|
|
|
|
|
|
},
|
|
|
|
|
|
async getApproverList() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await getAssignList(this.selectedProcess)
|
|
|
|
|
|
if (res.code === 0 || res.code === 200) {
|
|
|
|
|
|
this.approverList = res.data || []
|
|
|
|
|
|
let userSelectRules = res.data?.filter(rule => rule.type === 35)
|
|
|
|
|
|
if(userSelectRules?.length > 0){
|
|
|
|
|
|
this.loadingApprover = true
|
|
|
|
|
|
this.showApproverDialog = true
|
|
|
|
|
|
this.selectedCandidates = {}
|
|
|
|
|
|
}else{
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
processType: 0,
|
|
|
|
|
|
processDefinitionId: this.selectedProcess,
|
|
|
|
|
|
id: this.dataForm.id,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
dbFormId : this.modelId,
|
|
|
|
|
|
dataId: this.dataForm.id,
|
|
|
|
|
|
processDefinitionId: this.selectedProcess
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-20 18:07:35 +08:00
|
|
|
|
this.getListCreateData(params)
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: res.msg || '获取审批人失败',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取审批人失败:', error)
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '获取审批人失败',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.loadingApprover = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
closeProcessDialog() {
|
|
|
|
|
|
this.showProcessDialog = false
|
|
|
|
|
|
this.selectedProcess = ''
|
|
|
|
|
|
},
|
|
|
|
|
|
closeApproverDialog() {
|
|
|
|
|
|
this.showApproverDialog = false
|
|
|
|
|
|
},
|
|
|
|
|
|
handleDelete() {
|
|
|
|
|
|
uni.showModal({
|
|
|
|
|
|
title: '警告',
|
|
|
|
|
|
content: '删除后数据无法恢复,确认删除吗?',
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
|
createModel(this.modelId,{
|
|
|
|
|
|
operateType: 3,
|
|
|
|
|
|
id: this.dataForm.id,
|
|
|
|
|
|
data: {}
|
|
|
|
|
|
}, this.modelId).then(res => {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: res.msg || '删除成功',
|
|
|
|
|
|
icon: 'success',
|
|
|
|
|
|
complete: () => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
uni.$emit('refresh')
|
|
|
|
|
|
uni.navigateBack()
|
|
|
|
|
|
}, 1500)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}).catch(err => {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: err.msg || '删除失败',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 点击通过 - 打开审批意见弹窗
|
|
|
|
|
|
handleThrough() {
|
|
|
|
|
|
this.approvalType = 'through'
|
|
|
|
|
|
this.approvalReason = '' // 清空之前的意见
|
|
|
|
|
|
this.showApprovalReasonDialog = true
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 点击不通过 - 打开审批意见弹窗
|
|
|
|
|
|
handleFail() {
|
|
|
|
|
|
this.approvalType = 'fail'
|
|
|
|
|
|
this.approvalReason = '' // 清空之前的意见
|
|
|
|
|
|
this.showApprovalReasonDialog = true
|
|
|
|
|
|
},
|
2026-01-27 09:00:58 +08:00
|
|
|
|
// 取消
|
|
|
|
|
|
handleCancle() {
|
|
|
|
|
|
this.approvalType = 'cancel'
|
|
|
|
|
|
this.cacleReason = ''
|
|
|
|
|
|
this.showCacleDialog = true
|
|
|
|
|
|
},
|
|
|
|
|
|
// 取消弹框确认
|
|
|
|
|
|
submitCancel() {
|
|
|
|
|
|
if(!this.cacleReason){
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '请输入取消原因',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.cacleSubmitting = true
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
processType: 5,
|
|
|
|
|
|
processInstanceId: this.formData.processInstanceId, // 流程实例ID
|
|
|
|
|
|
id: this.formData.id,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
dbFormId : this.modelId,
|
|
|
|
|
|
id: this.approvalData && this.approvalData[0].id, // 审批记录第一条id
|
|
|
|
|
|
reason: this.cacleReason || '',
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//调用
|
|
|
|
|
|
this.getListCreateData(params)
|
|
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error('取消失败:', err)
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '取消失败,请重试',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.cacleSubmitting = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-25 20:22:58 +08:00
|
|
|
|
// 查看流程
|
|
|
|
|
|
goProcess(){
|
|
|
|
|
|
this.isProcess = true
|
|
|
|
|
|
console.log(this.config,'config-')
|
|
|
|
|
|
uni.setNavigationBarTitle({
|
|
|
|
|
|
title: `${this.config.name}流程详情`
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
2026-01-19 17:34:15 +08:00
|
|
|
|
|
2026-01-20 18:07:35 +08:00
|
|
|
|
// 提交审批结果(通过/不通过)
|
2026-01-19 17:34:15 +08:00
|
|
|
|
async submitApprovalResult() {
|
2026-01-27 09:00:58 +08:00
|
|
|
|
if(!this.approvalReason){
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '请填写审批意见',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-01-19 17:34:15 +08:00
|
|
|
|
try {
|
|
|
|
|
|
this.approvalSubmitting = true
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
processType: this.approvalType === 'through' ? 1 : 2, // 1=通过,2=不通过
|
|
|
|
|
|
processInstanceId: this.formData.processInstanceId, // 流程实例ID
|
|
|
|
|
|
id: this.formData.id,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
dbFormId : this.modelId,
|
|
|
|
|
|
id: this.approvalData && this.approvalData[0].id, // 审批记录第一条id
|
|
|
|
|
|
reason: this.approvalReason || '', // 审批意见
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-20 18:07:35 +08:00
|
|
|
|
//调用审批接口
|
|
|
|
|
|
this.getListCreateData(params)
|
2026-01-19 17:34:15 +08:00
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error('提交审批结果失败:', err)
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '提交失败,请重试',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.approvalSubmitting = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-23 11:09:28 +08:00
|
|
|
|
// 获取布局和初始值
|
2026-01-19 17:34:15 +08:00
|
|
|
|
async getDesForm() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await getDesForm(this.config.modelId);
|
|
|
|
|
|
if((res.code == 0 || res.code == 200) && res.data !== null){
|
|
|
|
|
|
const data = res.data ? JSON.parse(res.data) : {};
|
|
|
|
|
|
this.formConf = JSON.parse(data.formData);
|
|
|
|
|
|
console.log(this.formConf,'formConf--')
|
2026-01-23 11:09:28 +08:00
|
|
|
|
console.log(data,'data--111')
|
2026-01-19 17:34:15 +08:00
|
|
|
|
this.formConf.labelWidth = 160;
|
2026-01-23 11:09:28 +08:00
|
|
|
|
this.createData = data
|
2026-01-19 17:34:15 +08:00
|
|
|
|
await this.getDeptList();
|
|
|
|
|
|
await this.fillFormData(this.formConf, this.formData);
|
|
|
|
|
|
this.showPage = true;
|
|
|
|
|
|
await this.initData();
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: '暂无此页面',
|
|
|
|
|
|
icon: 'none',
|
|
|
|
|
|
complete: () => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
uni.navigateBack();
|
|
|
|
|
|
}, 1500)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取表单配置失败:', error);
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
getDeptList() {
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
|
getOrganizeSelector({type:'all'}).then(res => {
|
|
|
|
|
|
if (res.code == 0 && res.data) {
|
|
|
|
|
|
this.deptList = this.formatDeptData(res.data);
|
|
|
|
|
|
}
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
|
console.error('获取部门列表失败:', error);
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
formatDeptData(data) {
|
|
|
|
|
|
const result = [];
|
|
|
|
|
|
const formatItem = (item) => {
|
|
|
|
|
|
let deptId = item.depthd || item.id || item.deptId;
|
|
|
|
|
|
let deptName = item.depthName || item.deptName || item.fullName || item.name || item.orgNameTree;
|
|
|
|
|
|
deptId = String(deptId || '');
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
deptId: deptId,
|
|
|
|
|
|
deptName: deptName ,
|
|
|
|
|
|
depthd: item.depthd,
|
|
|
|
|
|
depthName: item.depthName,
|
|
|
|
|
|
id: item.id
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
if (Array.isArray(data)) {
|
|
|
|
|
|
data.forEach(item => {
|
|
|
|
|
|
const formatted = formatItem(item);
|
|
|
|
|
|
if (formatted.deptId) {
|
|
|
|
|
|
result.push(formatted);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item.children && Array.isArray(item.children)) {
|
|
|
|
|
|
const children = this.formatDeptData(item.children);
|
|
|
|
|
|
result.push(...children);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
},
|
|
|
|
|
|
async initData() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await this.$nextTick();
|
|
|
|
|
|
if (this.dataForm.id) {
|
|
|
|
|
|
const extra = {
|
|
|
|
|
|
modelId: this.modelId,
|
|
|
|
|
|
id: this.dataForm.id,
|
|
|
|
|
|
type: 1
|
|
|
|
|
|
};
|
|
|
|
|
|
uni.setStorageSync('dynamicModelExtra', extra);
|
|
|
|
|
|
const res = await getModelInfo(this.modelId, this.dataForm.id);
|
|
|
|
|
|
if(!!res.data.approveStatus&&(res.data.approveStatus !== 0 || res.data.approveStatus == 3)){
|
|
|
|
|
|
// 不是未发起和驳回的都不可编辑
|
|
|
|
|
|
this.disableAllFormFields();
|
|
|
|
|
|
}
|
|
|
|
|
|
this.dataForm = res.data;
|
|
|
|
|
|
if (!res.data) return;
|
2026-01-22 11:01:19 +08:00
|
|
|
|
const {fields = []} = this.formConf
|
|
|
|
|
|
const result = fields.reduce((acc,current)=>{
|
|
|
|
|
|
const key = current.__vModel__;
|
|
|
|
|
|
if(res.data.hasOwnProperty(key)){
|
|
|
|
|
|
acc[key] = res.data[key];
|
|
|
|
|
|
}else if(res.data.jeelowcode_subtable_data && res.data.jeelowcode_subtable_data.hasOwnProperty(key)){
|
|
|
|
|
|
acc[key] = res.data.jeelowcode_subtable_data[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
},{})
|
2026-01-19 17:34:15 +08:00
|
|
|
|
this.formData = {
|
|
|
|
|
|
...res.data,
|
|
|
|
|
|
id: this.dataForm.id,
|
|
|
|
|
|
sDate: res.data.sDate,
|
|
|
|
|
|
eDate: res.data.eDate ? new Date(res.data.eDate).getTime() : null,
|
|
|
|
|
|
applyDepId: res.data.applyDepId ? String(res.data.applyDepId) : '',
|
|
|
|
|
|
applyDepName: res.data.applyDepName || '',
|
|
|
|
|
|
applyDepData: res.data.applyDepData || null,
|
2026-01-22 11:01:19 +08:00
|
|
|
|
...result
|
2026-01-19 17:34:15 +08:00
|
|
|
|
};
|
2026-01-20 18:07:35 +08:00
|
|
|
|
console.log(this.formData,'formData123')
|
2026-01-19 17:34:15 +08:00
|
|
|
|
this.$nextTick(()=>{
|
|
|
|
|
|
if (this.$refs.dynamicForm && this.$refs.dynamicForm.setFormData) {
|
|
|
|
|
|
this.$refs.dynamicForm.setFormData('applyDepId', this.formData.applyDepId);
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
// 获取审批记录
|
|
|
|
|
|
await this.getApprovalData();
|
|
|
|
|
|
this.initMoreMenuList()
|
|
|
|
|
|
} else {
|
2026-01-23 11:09:28 +08:00
|
|
|
|
console.log(this.formData,'formData111')
|
2026-01-19 17:34:15 +08:00
|
|
|
|
const {user,deptInfoList} = this.userInfo
|
|
|
|
|
|
this.isAdd = true;
|
|
|
|
|
|
this.formData = {
|
|
|
|
|
|
...this.formData,
|
2026-01-23 11:09:28 +08:00
|
|
|
|
...this.createData,
|
2026-01-19 17:34:15 +08:00
|
|
|
|
applyDepId: deptInfoList ? String(deptInfoList[0].deptId) : '' ,
|
|
|
|
|
|
applyDepName: deptInfoList && deptInfoList[0].deptName,
|
|
|
|
|
|
applyUser: user.nickname
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
if (this.deptList && this.deptList.length > 0) {
|
|
|
|
|
|
await this.fillFormData(this.formConf, this.formData);
|
|
|
|
|
|
if (this.$refs.dynamicForm && this.$refs.dynamicForm.setFormData) {
|
|
|
|
|
|
this.$refs.dynamicForm.setFormData(this.formConf.formData);
|
|
|
|
|
|
}
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.key = +new Date();
|
|
|
|
|
|
}, 200);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn('部门列表未加载完成,等待重试');
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.initData();
|
|
|
|
|
|
}, 300);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('初始化数据失败:', error);
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
disableAllFormFields() {
|
|
|
|
|
|
const disableField = (field) => {
|
|
|
|
|
|
// 禁用当前字段
|
|
|
|
|
|
if (field.__config__) {
|
|
|
|
|
|
this.$set(field.__config__, 'disabled', true);
|
|
|
|
|
|
this.$set(field, 'disabled', true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 禁用子表字段
|
|
|
|
|
|
if (field.children && Array.isArray(field.children)) {
|
|
|
|
|
|
field.children.forEach(child => disableField(child));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 禁用嵌套字段
|
|
|
|
|
|
if (field.__config__ && field.__config__.children && Array.isArray(field.__config__.children)) {
|
|
|
|
|
|
field.__config__.children.forEach(child => disableField(child));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 特殊处理组件值
|
|
|
|
|
|
if (field.modelValue !== undefined) {
|
|
|
|
|
|
this.$set(field, 'modelValue', field.modelValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (field.value !== undefined) {
|
|
|
|
|
|
this.$set(field, 'value', field.value);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历所有顶级字段
|
|
|
|
|
|
if (this.formConf && this.formConf.fields && Array.isArray(this.formConf.fields)) {
|
|
|
|
|
|
this.formConf.fields.forEach(field => {
|
|
|
|
|
|
disableField(field);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 禁用整个表单组件
|
|
|
|
|
|
if (this.$refs.dynamicForm) {
|
|
|
|
|
|
this.$refs.dynamicForm.disabled = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
async fillFormData(form, data) {
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
const loop = (list, parent) => {
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
let item = list[i];
|
|
|
|
|
|
let vModel = item.__vModel__;
|
|
|
|
|
|
let config = item.__config__;
|
|
|
|
|
|
if (vModel === 'applyDepId' && config.jnpfKey === 'organizeSelect') {
|
|
|
|
|
|
this.type = item.type = config.type
|
|
|
|
|
|
item.options = [...(this.deptList || [])].map(dept => ({
|
|
|
|
|
|
id: String(dept.deptId || dept.id),
|
|
|
|
|
|
orgNameTree: dept.deptName || dept.depthName || dept.orgNameTree ,
|
|
|
|
|
|
deptId: dept.deptId,
|
|
|
|
|
|
deptName: dept.deptName,
|
|
|
|
|
|
...dept
|
|
|
|
|
|
}));
|
|
|
|
|
|
const applyDepId = data.applyDepId ? String(data.applyDepId) : '';
|
|
|
|
|
|
console.log(applyDepId,'applyDepId---')
|
|
|
|
|
|
const applyDepName = data.applyDepName || '';
|
|
|
|
|
|
if (applyDepId && applyDepId !== '') {
|
|
|
|
|
|
let matchedOption = item.options.find(opt =>
|
|
|
|
|
|
String(opt.id) === applyDepId
|
|
|
|
|
|
);
|
|
|
|
|
|
if (!matchedOption && applyDepName) {
|
|
|
|
|
|
matchedOption = {
|
|
|
|
|
|
id: applyDepId,
|
|
|
|
|
|
orgNameTree: applyDepName,
|
|
|
|
|
|
deptId: applyDepId,
|
|
|
|
|
|
deptName: applyDepName
|
|
|
|
|
|
};
|
|
|
|
|
|
item.options.unshift(matchedOption);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
config.defaultValue = applyDepId;
|
|
|
|
|
|
item.modelValue = applyDepId;
|
|
|
|
|
|
item.value = applyDepId;
|
|
|
|
|
|
item.__value__ = matchedOption ? matchedOption.orgNameTree : applyDepName;
|
|
|
|
|
|
const innerSelectedData = matchedOption ? [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: matchedOption.id,
|
|
|
|
|
|
orgNameTree: matchedOption.orgNameTree,
|
|
|
|
|
|
deptId: matchedOption.deptId,
|
|
|
|
|
|
deptName: matchedOption.deptName
|
|
|
|
|
|
}
|
|
|
|
|
|
] : [];
|
|
|
|
|
|
item.props = {
|
|
|
|
|
|
value: 'id',
|
|
|
|
|
|
label: 'orgNameTree',
|
|
|
|
|
|
innerSelectedData: innerSelectedData
|
|
|
|
|
|
};
|
|
|
|
|
|
item.selectedData = matchedOption ? [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: matchedOption.id,
|
|
|
|
|
|
orgNameTree: matchedOption.orgNameTree,
|
|
|
|
|
|
deptId: matchedOption.deptId,
|
|
|
|
|
|
deptName: matchedOption.deptName
|
|
|
|
|
|
}
|
|
|
|
|
|
] : [];
|
|
|
|
|
|
item.innerSelectedData = item.selectedData;
|
|
|
|
|
|
if (!form.formData) form.formData = {};
|
|
|
|
|
|
form.formData[vModel] = applyDepId;
|
|
|
|
|
|
form.formData['applyDepName'] = item.__value__;
|
|
|
|
|
|
console.log(form.formData,'form.formData---')
|
|
|
|
|
|
console.log(item,'item---')
|
|
|
|
|
|
} else {
|
|
|
|
|
|
item.selectedData = [];
|
|
|
|
|
|
item.modelValue = '';
|
|
|
|
|
|
item.__value__ = '';
|
|
|
|
|
|
config.defaultValue = '';
|
|
|
|
|
|
if (form.formData) {
|
|
|
|
|
|
form.formData[vModel] = '';
|
|
|
|
|
|
form.formData['applyDepName'] = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-23 11:09:28 +08:00
|
|
|
|
if(vModel == 'applyUser') {
|
|
|
|
|
|
this.$set(item, 'disabled', true);
|
|
|
|
|
|
}
|
2026-01-19 17:34:15 +08:00
|
|
|
|
if (vModel) {
|
|
|
|
|
|
let val = data.hasOwnProperty(vModel) ? data[vModel] : config.defaultValue;
|
|
|
|
|
|
if (!config.isSubTable) config.defaultValue = val;
|
|
|
|
|
|
if (this.isAdd || config.isSubTable) {
|
|
|
|
|
|
if (config.defaultCurrent) {
|
|
|
|
|
|
if (config.jnpfKey === 'datePicker') {
|
|
|
|
|
|
if (!data.hasOwnProperty(vModel)) {
|
|
|
|
|
|
let format = this.jnpf.handelFormat(item.format)
|
|
|
|
|
|
let dateStr = this.jnpf.toDate(new Date().getTime(), format)
|
|
|
|
|
|
let time = format === 'yyyy' ? '-01-01 00:00:00' : format === 'yyyy-MM' ?
|
|
|
|
|
|
'-01 00:00:00' : format === 'yyyy-MM-dd' ?
|
|
|
|
|
|
' 00:00:00' : ''
|
|
|
|
|
|
val = new Date(dateStr + time).getTime()
|
|
|
|
|
|
config.defaultValue = val
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (config.jnpfKey === 'timePicker') {
|
|
|
|
|
|
if (!data.hasOwnProperty(vModel)) {
|
|
|
|
|
|
config.defaultValue = this.jnpf.toDate(new Date(), item.format)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (config.jnpfKey === 'organizeSelect' && this.userInfo.organizeIds?.length) {
|
|
|
|
|
|
config.defaultValue = item.multiple ? this.userInfo.organizeIds :
|
|
|
|
|
|
this.userInfo.organizeId
|
|
|
|
|
|
}
|
|
|
|
|
|
if (config.jnpfKey === 'posSelect' && this.userInfo.positionIds?.length) {
|
|
|
|
|
|
config.defaultValue = item.multiple ? this.userInfo.positionIds :
|
|
|
|
|
|
this.userInfo.positionId
|
|
|
|
|
|
}
|
|
|
|
|
|
const userId = this.userInfo.userId
|
|
|
|
|
|
if (config.jnpfKey === 'userSelect' && userId) {
|
|
|
|
|
|
config.defaultValue = item.multiple ? [userId] : userId;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (config.jnpfKey === 'usersSelect' && userId) {
|
|
|
|
|
|
config.defaultValue = [userId + '--user'];
|
|
|
|
|
|
}
|
|
|
|
|
|
if (config.jnpfKey === 'sign' && this.userInfo.signImg) {
|
|
|
|
|
|
config.defaultValue = this.userInfo.signImg
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const btn_detail = this.$permission.hasBtnP('btn_detail', this.formPermissionList?.menuId)
|
|
|
|
|
|
const btn_edit = this.$permission.hasBtnP('btn_edit', this.formPermissionList?.menuId)
|
|
|
|
|
|
if (!!this.dataForm.id && !btn_edit && btn_detail) item.disabled = btn_detail
|
|
|
|
|
|
let noShow = !config.noShow ? false : config.noShow
|
|
|
|
|
|
let isVisibility = false
|
|
|
|
|
|
if (!config.visibility || (Array.isArray(config.visibility) && config.visibility.includes('app'))) {
|
|
|
|
|
|
isVisibility = true
|
|
|
|
|
|
}
|
|
|
|
|
|
this.$set(config, 'isVisibility', isVisibility)
|
|
|
|
|
|
if (this.formPermissionList.useFormPermission) {
|
|
|
|
|
|
let id = config.isSubTable ? parent.__vModel__ + '-' + vModel : vModel
|
|
|
|
|
|
noShow = true
|
|
|
|
|
|
if (this.formList && this.formList.length) {
|
|
|
|
|
|
noShow = !this.formList.some(o => o.enCode === id)
|
|
|
|
|
|
}
|
|
|
|
|
|
noShow = config.noShow ? config.noShow : noShow
|
|
|
|
|
|
this.$set(config, 'noShow', noShow)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let noShow = config.noShow ? config.noShow : false,
|
|
|
|
|
|
isVisibility = false
|
|
|
|
|
|
if (!config.visibility || (Array.isArray(config.visibility) && config.visibility.includes('app'))) {
|
|
|
|
|
|
isVisibility = true
|
|
|
|
|
|
}
|
|
|
|
|
|
this.$set(config, 'isVisibility', isVisibility)
|
|
|
|
|
|
this.$set(config, 'noShow', noShow)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (config && config.children && Array.isArray(config.children)) {
|
|
|
|
|
|
loop(config.children, item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
loop(form.fields);
|
|
|
|
|
|
if (!form.formData) {
|
|
|
|
|
|
form.formData = {};
|
|
|
|
|
|
}
|
|
|
|
|
|
const formDataToMerge = {
|
|
|
|
|
|
...data,
|
|
|
|
|
|
applyDepId: data.applyDepId || '',
|
|
|
|
|
|
applyDepName: data.applyDepName || ''
|
|
|
|
|
|
};
|
|
|
|
|
|
form.formData = { ...form.formData, ...formDataToMerge };
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
const applyField = form.fields.find(f => f.__vModel__ === 'applyDepId');
|
|
|
|
|
|
if (!applyField || !applyField.selectedData || applyField.selectedData.length === 0) {
|
|
|
|
|
|
console.warn('申请单位无选中数据');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const dynamicForm = this.$refs.dynamicForm;
|
|
|
|
|
|
if (!dynamicForm) return;
|
|
|
|
|
|
const findOrganizeSelect = (component) => {
|
|
|
|
|
|
if (component.$options.name === 'jnpf-organize-select') {
|
|
|
|
|
|
return component;
|
|
|
|
|
|
}
|
2026-01-27 15:42:20 +08:00
|
|
|
|
const children = component.$children || [];
|
|
|
|
|
|
for (let child of children) {
|
2026-01-19 17:34:15 +08:00
|
|
|
|
const res = findOrganizeSelect(child);
|
|
|
|
|
|
if (res) return res;
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
};
|
|
|
|
|
|
const organizeSelect = findOrganizeSelect(dynamicForm);
|
|
|
|
|
|
if (organizeSelect) {
|
|
|
|
|
|
organizeSelect.selectedData = applyField.selectedData;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn('未找到jnpf-organize-select组件');
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 1000);
|
|
|
|
|
|
this.key = +new Date();
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
const finalApplyField = form.fields.find(f => f.__vModel__ === 'applyDepId');
|
|
|
|
|
|
if (finalApplyField) {
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 100);
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
sumbitForm(data, callback) {
|
|
|
|
|
|
const {billNoPrefix} = this.config
|
|
|
|
|
|
if (!data) return;
|
|
|
|
|
|
const filePathArr = [...data.attachment]
|
|
|
|
|
|
const attachment = filePathArr.map(item => item.fileUrl)
|
|
|
|
|
|
this.btnLoading = true;
|
|
|
|
|
|
const applyDepName = this.deptList?.filter(item=>item.deptId === data.applyDepId)[0]?.deptName
|
|
|
|
|
|
const formData = {
|
|
|
|
|
|
...this.formData,
|
|
|
|
|
|
...data,
|
|
|
|
|
|
id: data?.id || '',
|
|
|
|
|
|
applyDepName,
|
|
|
|
|
|
attachment: attachment.join(',')
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if(!this.dataForm.id){
|
|
|
|
|
|
formData.billNo = billNoPrefix + new Date().getTime()
|
|
|
|
|
|
}
|
|
|
|
|
|
this.dataForm.data = JSON.stringify(formData);
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
id: this.dataForm.id,
|
|
|
|
|
|
operateType: this.dataForm.id ? 1 : 0,
|
|
|
|
|
|
attachments: data.attachment,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
...formData
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const formMethod = createModel;
|
|
|
|
|
|
formMethod(this.modelId, params).then(res => {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title:'保存成功',
|
|
|
|
|
|
complete: () => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (this.clickType == 'save_add') {
|
|
|
|
|
|
this.key = +new Date();
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
this.$refs.dynamicForm && this.$refs.dynamicForm.resetForm();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
this.btnLoading = false;
|
|
|
|
|
|
this.initData();
|
|
|
|
|
|
if (this.clickType != 'save_proceed' && this.clickType != 'save_add') {
|
|
|
|
|
|
uni.$emit('refresh');
|
|
|
|
|
|
uni.navigateBack();
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 1500);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}).catch(() => {
|
|
|
|
|
|
this.btnLoading = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
if (callback && typeof callback === "function") callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
commonSubmit(type) {
|
|
|
|
|
|
this.clickType = type;
|
|
|
|
|
|
this.submit(type);
|
|
|
|
|
|
},
|
|
|
|
|
|
submit(type) {
|
2026-01-23 15:16:31 +08:00
|
|
|
|
console.log(type,'type333')
|
2026-01-19 17:34:15 +08:00
|
|
|
|
this.clickType = type;
|
|
|
|
|
|
this.$refs.dynamicForm && this.$refs.dynamicForm.submitForm();
|
|
|
|
|
|
},
|
|
|
|
|
|
// 获取审批记录
|
|
|
|
|
|
async getApprovalData() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await getByProcess(this.formData.processInstanceId);
|
|
|
|
|
|
const result = []
|
|
|
|
|
|
if (res.code === 0) {
|
|
|
|
|
|
res.data.forEach((task) => {
|
|
|
|
|
|
if (task.result !== 4) {
|
|
|
|
|
|
result.push(task)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
result.sort((a, b) => {
|
|
|
|
|
|
// 有已完成的情况,按照完成时间倒序
|
|
|
|
|
|
if (a.endTime && b.endTime) {
|
|
|
|
|
|
return b.endTime - a.endTime
|
|
|
|
|
|
} else if (a.endTime) {
|
|
|
|
|
|
return 1
|
|
|
|
|
|
} else if (b.endTime) {
|
|
|
|
|
|
return -1
|
|
|
|
|
|
// 都是未完成,按照创建时间倒序
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return b.createTime - a.createTime
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
this.approvalData = result
|
2026-01-28 17:32:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 审批记录加载完成后,检查一次可见性
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.checkApprovalVisibility()
|
|
|
|
|
|
}, 300)
|
|
|
|
|
|
})
|
2026-01-19 17:34:15 +08:00
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error('获取审批记录失败:', err);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
// 状态样式
|
|
|
|
|
|
getStatusClass(result) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
'status-processing': result === 1,
|
|
|
|
|
|
'status-pass': result === 2,
|
|
|
|
|
|
'status-reject': result === 3
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
getStatusTagClass(result) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
'tag-processing': result === 1,
|
|
|
|
|
|
'tag-pass': result === 2,
|
|
|
|
|
|
'tag-reject': result === 3
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
// 时间格式化
|
|
|
|
|
|
formatTime(timestamp) {
|
|
|
|
|
|
if (!timestamp) return '-';
|
|
|
|
|
|
const date = new Date(timestamp);
|
|
|
|
|
|
return `${date.getFullYear()}-${this.padZero(date.getMonth() + 1)}-${this.padZero(date.getDate())} ${this.padZero(date.getHours())}:${this.padZero(date.getMinutes())}:${this.padZero(date.getSeconds())}`;
|
|
|
|
|
|
},
|
|
|
|
|
|
padZero(num) {
|
|
|
|
|
|
return num.toString().padStart(2, '0');
|
|
|
|
|
|
},
|
|
|
|
|
|
// 耗时格式化
|
|
|
|
|
|
formatDuration(millis) {
|
|
|
|
|
|
if (!millis) return '-';
|
|
|
|
|
|
const seconds = Math.floor(millis / 1000);
|
|
|
|
|
|
if (seconds < 60) {
|
|
|
|
|
|
return `${seconds} 秒`;
|
|
|
|
|
|
}
|
|
|
|
|
|
const minutes = Math.floor(seconds / 60);
|
|
|
|
|
|
if (minutes < 60) {
|
|
|
|
|
|
return `${minutes} 分钟`;
|
|
|
|
|
|
}
|
|
|
|
|
|
const hours = Math.floor(minutes / 60);
|
|
|
|
|
|
return `${hours} 小时 ${minutes % 60} 分钟`;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
|
page {
|
|
|
|
|
|
background-color: #f0f2f6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-28 17:32:24 +08:00
|
|
|
|
/* 固定的审批记录底栏 */
|
|
|
|
|
|
.fixed-approval-bar {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
z-index: 998;
|
|
|
|
|
|
background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%);
|
|
|
|
|
|
border-top: 1rpx solid #e8e8e8;
|
|
|
|
|
|
box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.08);
|
|
|
|
|
|
padding: 20rpx 30rpx;
|
|
|
|
|
|
transition: transform 0.3s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-approval-content {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-status-icon {
|
|
|
|
|
|
width: 20rpx;
|
|
|
|
|
|
height: 20rpx;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
margin-right: 20rpx;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
|
|
|
|
|
|
&.status-processing {
|
|
|
|
|
|
background-color: #409EFF;
|
|
|
|
|
|
border: 3rpx solid rgba(64, 158, 255, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
&.status-pass {
|
|
|
|
|
|
background-color: #67C23A;
|
|
|
|
|
|
border: 3rpx solid rgba(103, 194, 58, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
&.status-reject {
|
|
|
|
|
|
background-color: #F56C6C;
|
|
|
|
|
|
border: 3rpx solid rgba(245, 108, 108, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-approval-info {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
.fixed-approval-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-task-name {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
margin-bottom: 8rpx;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-approval-user {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-user-name {
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
margin-right: 12rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-dept-tag {
|
|
|
|
|
|
background: #f4f4f5;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
padding: 2rpx 10rpx;
|
|
|
|
|
|
border-radius: 4rpx;
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-scroll-btn {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
background: #409EFF;
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
padding: 12rpx 20rpx;
|
|
|
|
|
|
border-radius: 30rpx;
|
|
|
|
|
|
margin-left: 20rpx;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
background: #3a8ee6;
|
|
|
|
|
|
transform: scale(0.95);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-scroll-btn .icon-ym {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
margin-bottom: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.fixed-btn-text {
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 完整的审批记录区域 */
|
|
|
|
|
|
.full-approval-section {
|
|
|
|
|
|
margin-top: 20rpx;
|
|
|
|
|
|
background: white;
|
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
|
|
|
|
&.has-fixed-bar {
|
|
|
|
|
|
margin-bottom: 120rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-title {
|
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
padding: 24rpx 30rpx;
|
|
|
|
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
|
background: #f9fafc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-list {
|
|
|
|
|
|
padding: 0 30rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
padding: 30rpx 0;
|
|
|
|
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-line-container {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-right: 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-icon {
|
|
|
|
|
|
width: 20rpx;
|
|
|
|
|
|
height: 20rpx;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
|
|
|
|
|
|
&.status-processing {
|
|
|
|
|
|
background-color: #409EFF;
|
|
|
|
|
|
border: 4rpx solid rgba(64, 158, 255, 0.2);
|
|
|
|
|
|
box-shadow: 0 0 0 4rpx rgba(64, 158, 255, 0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
&.status-pass {
|
|
|
|
|
|
background-color: #67C23A;
|
|
|
|
|
|
border: 4rpx solid rgba(103, 194, 58, 0.2);
|
|
|
|
|
|
box-shadow: 0 0 0 4rpx rgba(103, 194, 58, 0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
&.status-reject {
|
|
|
|
|
|
background-color: #F56C6C;
|
|
|
|
|
|
border: 4rpx solid rgba(245, 108, 108, 0.2);
|
|
|
|
|
|
box-shadow: 0 0 0 4rpx rgba(245, 108, 108, 0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-line {
|
|
|
|
|
|
width: 2rpx;
|
|
|
|
|
|
background-color: #e8e8e8;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
margin-top: 10rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.task-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.task-name {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-tag {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
padding: 6rpx 16rpx;
|
|
|
|
|
|
border-radius: 30rpx;
|
|
|
|
|
|
|
|
|
|
|
|
&.tag-processing {
|
|
|
|
|
|
background-color: rgba(64, 158, 255, 0.1);
|
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.tag-pass {
|
|
|
|
|
|
background-color: rgba(103, 194, 58, 0.1);
|
|
|
|
|
|
color: #67C23A;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.tag-reject {
|
|
|
|
|
|
background-color: rgba(245, 108, 108, 0.1);
|
|
|
|
|
|
color: #F56C6C;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-time {
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
margin-left: 12rpx;
|
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
|
|
|
|
|
|
.info-label {
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
margin-right: 8rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-value {
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
margin-right: 12rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-value-tag {
|
|
|
|
|
|
background: #f4f4f5;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
padding: 4rpx 12rpx;
|
|
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pending-tag {
|
|
|
|
|
|
background-color: #fef0f0;
|
|
|
|
|
|
color: #f56c6c;
|
|
|
|
|
|
padding: 4rpx 12rpx;
|
|
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
margin-left: 12rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.reason-box {
|
|
|
|
|
|
background: #f9f9f9;
|
|
|
|
|
|
padding: 20rpx;
|
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
margin-top: 16rpx;
|
|
|
|
|
|
border-left: 4rpx solid #e8e8e8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 底部按钮区域 */
|
2026-01-19 17:34:15 +08:00
|
|
|
|
.buttom-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
2026-01-20 18:07:35 +08:00
|
|
|
|
z-index: 999 !important;
|
|
|
|
|
|
padding: 10rpx 0rpx;
|
2026-01-28 17:32:24 +08:00
|
|
|
|
// border-top: 1rpx solid #f0f0f0;
|
|
|
|
|
|
// box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.buttom-btn {
|
|
|
|
|
|
min-width: 200rpx;
|
|
|
|
|
|
padding: 12rpx 15rpx;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
// margin: 0 10rpx;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
2026-01-28 17:32:24 +08:00
|
|
|
|
|
2026-01-20 18:07:35 +08:00
|
|
|
|
.launch-flow-btn {
|
|
|
|
|
|
background: #19be6b !important;
|
|
|
|
|
|
}
|
2026-01-28 17:32:24 +08:00
|
|
|
|
|
2026-01-20 18:07:35 +08:00
|
|
|
|
.launch-flow-btn[disabled] {
|
2026-01-28 17:32:24 +08:00
|
|
|
|
background: #19be6b !important;
|
|
|
|
|
|
opacity: 0.6;
|
2026-01-20 18:07:35 +08:00
|
|
|
|
}
|
2026-01-28 17:32:24 +08:00
|
|
|
|
|
2026-01-19 17:34:15 +08:00
|
|
|
|
.more-btn {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 80rpx;
|
|
|
|
|
|
height: 80rpx;
|
2026-01-20 18:07:35 +08:00
|
|
|
|
flex: 1;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
.more-text {
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
margin-top: 4rpx;
|
|
|
|
|
|
}
|
2026-01-28 17:32:24 +08:00
|
|
|
|
|
2026-01-19 17:34:15 +08:00
|
|
|
|
.more-menu {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: 100%;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.1);
|
|
|
|
|
|
width: 200rpx;
|
|
|
|
|
|
z-index: 100;
|
|
|
|
|
|
padding: 10rpx 0;
|
|
|
|
|
|
|
|
|
|
|
|
.menu-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 16rpx 20rpx;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
cursor: pointer;
|
2026-01-28 17:32:24 +08:00
|
|
|
|
transition: background-color 0.2s;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-28 17:32:24 +08:00
|
|
|
|
&:active {
|
|
|
|
|
|
background-color: #ebeef5;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.menu-label {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 流程选择弹框样式 */
|
|
|
|
|
|
.process-dialog {
|
|
|
|
|
|
width: 90vw;
|
|
|
|
|
|
max-height: 70vh;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.dialog-header {
|
|
|
|
|
|
padding: 30rpx;
|
|
|
|
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.dialog-title {
|
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.close-icon {
|
|
|
|
|
|
padding: 10rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.process-list {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
max-height: 50vh;
|
|
|
|
|
|
padding: 20rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.process-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
padding: 24rpx 30rpx;
|
|
|
|
|
|
border-bottom: 1rpx solid #f8f8f8;
|
|
|
|
|
|
transition: background-color 0.3s;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
|
|
|
|
&.selected-item {
|
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
|
border-left: 4rpx solid #2979ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.process-item:active {
|
|
|
|
|
|
background-color: #f0f2f5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.process-item-left {
|
|
|
|
|
|
margin-right: 20rpx;
|
|
|
|
|
|
padding-top: 6rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-process {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 60rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-text {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
margin-top: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loading-process {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 60rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loading-text {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
margin-top: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.dialog-footer {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
padding: 30rpx;
|
|
|
|
|
|
border-top: 1rpx solid #f0f0f0;
|
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.footer-btn {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.cancel-btn {
|
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.submit-btn {
|
|
|
|
|
|
background: linear-gradient(135deg, #3d6dcc, #2a52b5);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.submit-btn[disabled] {
|
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 审批人选择弹框样式 */
|
|
|
|
|
|
.approver-dialog {
|
|
|
|
|
|
width: 90vw;
|
|
|
|
|
|
max-height: 80vh;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approver-content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
max-height: 60vh;
|
|
|
|
|
|
padding: 30rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approver-node {
|
|
|
|
|
|
margin-bottom: 40rpx;
|
|
|
|
|
|
padding: 20rpx;
|
|
|
|
|
|
background: #f9fafc;
|
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.node-title {
|
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
padding-bottom: 10rpx;
|
|
|
|
|
|
border-bottom: 1rpx solid #ebeef5;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.node-label {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.node-name {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.candidate-section {
|
|
|
|
|
|
margin-top: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-label {
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loading-approver {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 60rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-approver {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 60rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.confirm-btn {
|
|
|
|
|
|
background: linear-gradient(135deg, #3d6dcc, #2a52b5);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
margin: 0 30rpx 30rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.confirm-btn[disabled] {
|
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 审批记录样式 */
|
|
|
|
|
|
.approval-title {
|
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
padding: 20rpx 30rpx;
|
|
|
|
|
|
border-bottom: 1rpx solid #eee;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-list {
|
2026-01-20 18:07:35 +08:00
|
|
|
|
padding: 10rpx 30rpx;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
position: relative;
|
2026-01-20 18:07:35 +08:00
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
padding-top: 10rpx;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-line-container {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
2026-01-20 18:07:35 +08:00
|
|
|
|
margin-right: 16rpx;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-line {
|
|
|
|
|
|
width: 2rpx;
|
2026-01-20 18:07:35 +08:00
|
|
|
|
background-color: #8e8585;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
flex: 1;
|
|
|
|
|
|
margin-top: 8rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-icon {
|
|
|
|
|
|
width: 32rpx;
|
|
|
|
|
|
height: 32rpx;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-01-20 18:07:35 +08:00
|
|
|
|
// background-color: #fff;
|
|
|
|
|
|
border: 2rpx solid;
|
|
|
|
|
|
/* 状态点边框色 */
|
2026-01-19 17:34:15 +08:00
|
|
|
|
&.status-processing {
|
2026-01-20 18:07:35 +08:00
|
|
|
|
background-color: #409EFF;
|
|
|
|
|
|
border-color: #409EFF;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
&.status-pass {
|
2026-01-20 18:07:35 +08:00
|
|
|
|
background-color: #67C23A;
|
|
|
|
|
|
border-color: #67C23A;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
&.status-reject {
|
2026-01-20 18:07:35 +08:00
|
|
|
|
background-color: #F56C6C;
|
|
|
|
|
|
border-color: #F56C6C;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
padding: 20rpx;
|
|
|
|
|
|
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.task-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-01-20 18:07:35 +08:00
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
margin-bottom: 12rpx;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
|
|
|
|
|
|
.task-name {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
}
|
2026-01-20 18:07:35 +08:00
|
|
|
|
}
|
2026-01-19 17:34:15 +08:00
|
|
|
|
|
2026-01-20 18:07:35 +08:00
|
|
|
|
.status-tag {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
padding: 2rpx 8rpx;
|
|
|
|
|
|
border-radius: 4rpx;
|
|
|
|
|
|
margin-left: 12rpx;
|
|
|
|
|
|
|
|
|
|
|
|
/* 状态对应的样式 */
|
|
|
|
|
|
&.tag-processing {
|
|
|
|
|
|
background-color: #E6F7FF;
|
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.tag-pass {
|
|
|
|
|
|
background-color: #F0F9EB;
|
|
|
|
|
|
color: #67C23A;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.tag-reject {
|
|
|
|
|
|
background-color: #FFF2F0;
|
|
|
|
|
|
color: #F56C6C;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 状态标签内的时间 */
|
|
|
|
|
|
.status-time {
|
|
|
|
|
|
font-size: 20rpx;
|
|
|
|
|
|
margin-left: 8rpx;
|
2026-01-19 17:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-20 18:07:35 +08:00
|
|
|
|
|
2026-01-19 17:34:15 +08:00
|
|
|
|
.info-row {
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
|
|
|
|
|
|
|
.info-label {
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
}
|
|
|
|
|
|
.info-value {
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
}
|
2026-01-20 18:07:35 +08:00
|
|
|
|
.info-value-tag {
|
|
|
|
|
|
background: #f4f4f5;
|
|
|
|
|
|
color: #a0a4a7;
|
|
|
|
|
|
padding: 2rpx 10rpx;
|
|
|
|
|
|
margin-left: 10rpx;
|
|
|
|
|
|
border: 10rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.pending-tag {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
background-color: #FAFAFA;
|
|
|
|
|
|
border: 1rpx solid #E5E5E5;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
padding: 2rpx 8rpx;
|
|
|
|
|
|
border-radius: 4rpx;
|
|
|
|
|
|
margin-left: 10rpx;
|
|
|
|
|
|
font-size: 20rpx;
|
|
|
|
|
|
}
|
2026-01-19 17:34:15 +08:00
|
|
|
|
.dept-tag {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
background-color: #F5F7FA;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
padding: 2rpx 8rpx;
|
|
|
|
|
|
border-radius: 4rpx;
|
|
|
|
|
|
margin-left: 8rpx;
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.ml-20 {
|
|
|
|
|
|
margin-left: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.reason-box {
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
background-color: #F9F9F9;
|
|
|
|
|
|
padding: 12rpx;
|
|
|
|
|
|
border-radius: 4rpx;
|
|
|
|
|
|
margin-top: 8rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 新增:审批意见弹窗样式 */
|
|
|
|
|
|
.approval-reason-dialog {
|
|
|
|
|
|
width: 80vw;
|
|
|
|
|
|
max-width: 600rpx;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-reason-dialog .dialog-header {
|
|
|
|
|
|
padding: 20rpx 30rpx;
|
|
|
|
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-reason-dialog .dialog-content {
|
|
|
|
|
|
padding: 30rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-reason-dialog .input-label {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-reason-dialog .reason-input {
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.approval-reason-dialog .dialog-footer {
|
|
|
|
|
|
padding: 20rpx 30rpx;
|
|
|
|
|
|
border-top: 1rpx solid #f0f0f0;
|
|
|
|
|
|
}
|
2026-01-20 18:07:35 +08:00
|
|
|
|
|
|
|
|
|
|
.copy-dialog {
|
|
|
|
|
|
width: 90vw;
|
|
|
|
|
|
max-height: 80vh;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.dialog-content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
padding: 30rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-item-user {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-item {
|
|
|
|
|
|
margin-bottom: 24rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-item.required .label::after {
|
|
|
|
|
|
content: '*';
|
|
|
|
|
|
color: #F56C6C;
|
|
|
|
|
|
margin-left: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.label {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.value {
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
padding: 12rpx;
|
|
|
|
|
|
background: #f9fafc;
|
|
|
|
|
|
border-radius: 6rpx;
|
|
|
|
|
|
}
|
2026-01-19 17:34:15 +08:00
|
|
|
|
</style>
|