feat: 新增需求

This commit is contained in:
caijun
2026-01-20 18:07:35 +08:00
parent 9f5b2a92c4
commit 4243e1213f
26 changed files with 2837 additions and 936 deletions

View File

@@ -931,6 +931,8 @@
name: this.config.name,
billNoPrefix: this.config.billNoPrefix
}
console.log(config,'config12233')
const url = '/pages/apply/dynamicModelList/' + type + '?config=' +
this.jnpf.base64.encode(JSON.stringify(config))
uni.navigateTo({

View File

@@ -1,63 +1,75 @@
<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" />
<!-- 审批记录区域 -->
<view v-if="false&&(dataForm.approveStatus == 1 || dataForm.approveStatus == 2)">
<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-content">
<view class="task-header">
<text class="task-name">任务{{ item.name }}</text>
<view class="status-tag" :class="getStatusTagClass(item.result)">
{{ item.result === 1 ? '处理中' : (item.result === 2 ? '通过' : '不通过') }}
</view>
</view>
<view class="info-row">
<text class="info-label">审批人</text>
<text class="info-value">{{ item.assigneeUser.nickname }}</text>
<view class="dept-tag">{{ item.assigneeUser.deptName }}</view>
<text class="info-label ml-20">创建时间</text>
<text class="info-value">{{ formatTime(item.createTime) }}</text>
<text class="info-label ml-20" v-if="item.endTime">审批时间</text>
<text class="info-value" v-if="item.endTime">{{ formatTime(item.endTime) }}</text>
<text class="info-label ml-20" v-if="item.durationInMillis">耗时</text>
<text class="info-value" v-if="item.durationInMillis">{{ formatDuration(item.durationInMillis) }}</text>
</view>
<view class="reason-box" v-if="item.reason">
{{ item.reason }}
</view>
</view>
</view>
</view>
<view v-if="approvalData.length">
<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)">
<!-- <u-icon
:name="item.result === 1 ? 'clock' : (item.result === 2 ? 'checkmark-circle' : 'close-circle')"
size="20"
:color="item.result === 1 ? '#409EFF' : (item.result === 2 ? '#67C23A' : '#F56C6C')"
/> -->
</view>
<!-- 竖线最后一条隐藏 -->
<view class="approval-line" v-if="index < approvalData.length - 1"></view>
</view>
<!-- 审批内容 -->
<view class="approval-content">
<!-- 任务名称 + 状态标签 -->
<view class="task-header">
<text class="task-name">任务{{ item.name }}</text>
<!-- 状态标签参考图的右上角样式 -->
<view class="status-tag" :class="getStatusTagClass(item.result)">
{{ item.result === 1 ? '审批中' : (item.result === 2 ? '已提交' : '不通过') }}
<!-- 时间显示在标签右侧 -->
<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>
</view>
<!-- 底部操作按钮区域 -->
<view class="buttom-actions" v-if="btnType === 'btn_edit' || btnType === 'btn_add'">
<CustomButton
class="u-flex buttom-btn-left-inner"
:btnText="getCancelText"
btnIcon="icon-ym icon-ym-add-cancel"
customIcon
:btnLoading="btnLoading"
/>
<u-button class="buttom-btn" type="primary" @click.stop="submit" :disabled="idDsabled" :loading="btnLoading">
{{getOkText}}
<view v-if="!loading && (!(dataForm.approveStatus == 1 || dataForm.approveStatus == 2 || dataForm.approveStatus == 4) || !dataForm.id || config.current && config.current == 2)" class="buttom-actions">
<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">
发起流程
</u-button>
<view class="more-btn" @click.stop="toggleMoreMenu" v-if="dataForm.id && moreMenuList.length && !idDsabled">
<u-button v-if="!config.current" class="buttom-btn" type="primary" @click.stop="submit" :disabled="idDsabled" :loading="btnLoading">
保存
</u-button>
<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">
<u-icon name="more-dot-fill" size="34"></u-icon>
<text class="more-text">{{$t('common.moreText')}}</text>
@@ -69,7 +81,7 @@
:style="{color: item.color || '#333'}"
>
<view @click.stop="handleMoreMenuClick(item)">
<text class="menu-icon" :class="item.icon"></text>
<!-- <text class="menu-icon" :class="item.icon"></text> -->
<text class="menu-label">{{item.label}}</text>
</view>
</view>
@@ -207,6 +219,66 @@
</view>
</view>
</u-popup>
<!-- 抄送规则弹窗 -->
<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">
<text class="label">任务名称</text>
<text class="value">{{ copyForm.taskName || '安全监护人' }}</text>
</view>
<view class="form-item">
<text class="label">任务标识</text>
<text class="value">{{ copyForm.taskId || '' }}</text>
</view>
<view class="form-item">
<text class="label">流程名称</text>
<text class="value">{{ copyForm.flowName || '动土作业审批' }}</text>
</view>
<view class="form-item">
<text class="label">流程标识</text>
<text class="value">{{ copyForm.flowId || '' }}</text>
</view>
<view class="form-item required">
<text class="label">规则类型</text>
<u-select v-model="copyForm.ruleType" placeholder="请选择"> -->
<!-- <u-select-item label="用户" value="user"></u-select-item> -->
<!-- 如果有其他类型可以在这里添加 -->
<!-- </u-select> -->
<!-- </view> -->
<view class="form-item-user required">
<text class="label">指定用户</text>
<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>
</view>
</template>
@@ -226,7 +298,7 @@
} from '@/api/apply/visualDev'
import UserSelect from '@/components/Jnpf/CandidateSelect'
import {getOrganizeSelector} from '@/api/common.js'
import flowBtn from '../../workFlow/components/flowBtn.vue'
export default {
components: {
CustomButton,
@@ -278,21 +350,26 @@
showApprovalReasonDialog: false, // 弹窗显示状态
approvalType: '', // 审批类型through(通过) / fail(不通过)
approvalReason: '', // 审批意见内容
approvalSubmitting: false // 提交加载状态
approvalSubmitting: false, // 提交加载状态
// 抄送
showCopyDialog: false,
copySubmitting: false,
copyForm: {
processInstanceKey: '',
processInstanceName: '',
options: {},
reason: ''
}
}
},
computed: {
getOkText() {
const text = this.formConf.confirmButtonTextI18nCode ?
this.$t(this.formConf.confirmButtonTextI18nCode, this.formConf.confirmButtonText) :
this.formConf.confirmButtonText;
return text || this.$t('common.okText');
// btnType
console.log(this.btnType,'btnType--')
return this.$t('common.saveText');
},
getCancelText() {
const text = this.formConf.cancelButtonTextI18nCode ?
this.$t(this.formConf.cancelButtonTextI18nCode, this.formConf.cancelButtonText) :
this.formConf.cancelButtonText;
return text || this.$t('common.cancelText');
getProcessText() {
return '发起流程'
},
isAllCandidatesSelected() {
const result = this.approverList.every(item=>{
@@ -325,7 +402,7 @@
btnType = '',
modelId,
isPreview = '0',
id = ''
id = '',
} = config
const formPermissionList =[]
Object.assign(this, {
@@ -358,6 +435,9 @@
...this.approverList[index],
[name] : value
}
},
handleRuleUserChange(node, value, data){
},
confirmLaunchFlow() {
const result = this.approverList.every(item=>{
@@ -387,6 +467,10 @@
processDefinitionId: this.selectedProcess
}
}
this.getListCreateData(params)
},
// 请求日志
getListCreateData(params){
getListCreate(params,this.modelId).then(res=>{
if(res.code == 0) {
uni.showToast({
@@ -403,66 +487,36 @@
initMoreMenuList() {
let menuList = [
// {
// label: '通过',
// value: 'through',
// icon: 'icon-ym icon-ym-flow-launch',
// color: '#67c23a'
// },
// {
// label: '不通过',
// value: 'fail',
// icon: 'icon-ym icon-ym-flow-launch',
// color: '#f56c6c'
// },
// {
// label: '发起流程',
// value: 'launchFlow',
// icon: 'icon-ym icon-ym-flow-launch',
// color: '#0293fc'
// },
{
// label: '删除',
// value: 'delete',
// icon: 'icon-ym icon-ym-app-delete',
// color: '#dd524d'
// }
]
console.log(this.dataForm.approveStatus,'approveStatus--')
if((!this.dataForm.approveStatus && this.dataForm.id) || this.dataForm.approveStatus == 3){
// 未审核 | 驳回
menuList.push({
label: '删除',
value: 'delete',
icon: 'icon-ym icon-ym-app-delete',
color: '#dd524d'
}
]
if(this.dataForm.approveStatus == 0 || this.dataForm.approveStatus == 3 || !this.dataForm.approveStatus){
menuList.unshift({
label: '发起流程',
value: 'launchFlow',
icon: 'icon-ym icon-ym-flow-launch',
color: '#0293fc'
},)
}
if(this.dataForm.approveStatus == 1){
menuList.unshift({
label: '通过',
value: 'through',
icon: 'icon-ym icon-ym-flow-launch',
color: '#67c23a'
},
{
label: '不通过',
value: 'fail',
icon: 'icon-ym icon-ym-flow-launch',
color: '#f56c6c'
})
}
// if(this.dataForm.approveStatus == 1){
// menuList.unshift({
// label: '通过',
// value: 'through',
// icon: 'icon-ym icon-ym-flow-launch',
// color: '#67c23a'
// },
// {
// label: '不通过',
// value: 'fail',
// icon: 'icon-ym icon-ym-flow-launch',
// color: '#f56c6c'
// })
// }
if(this.config.current == 2){
menuList.unshift({
label: '抄送',
value: 'send',
icon: 'icon-ym icon-ym-flow-launch',
color: '#409eff'
},
{
label: '回退',
value: 'reject',
icon: 'icon-ym icon-ym-flow-launch',
color: '#e6a23c'
})
}
this.moreMenuList = menuList
},
toggleMoreMenu() {
@@ -481,18 +535,15 @@
case 'edit':
this.handleEdit()
break
case 'launchFlow':
this.handleLaunchFlow()
case 'send':
this.handleSend()
break
case 'reject':
this.handleReject()
break
case 'delete':
this.handleDelete()
break
case 'through':
this.handleThrough()
break
case 'fail':
this.handleFail()
break
}
},
handleEdit() {
@@ -536,6 +587,43 @@
this.loadingProcess = false
}
},
// 抄送
handleSend() {
this.showCopyDialog = true
},
// 抄送-提交
submitCopyRule(){
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 = {
processType: '3', // 回退code todo
// processDefinitionId:Object.values(this.copyForm.options),
processInstanceId: this.formData.processInstanceId,
id: this.dataForm.id,
data: {
taskId: id
}
}
this.getListCreateData(params)
},
selectProcess(item) {
this.selectedProcess = item
},
@@ -571,17 +659,7 @@
processDefinitionId: this.selectedProcess
}
}
const res = await getListCreate(params,this.modelId)
if(res.code == 0) {
uni.showToast({
title: '发起成功',
icon: 'success'
})
setTimeout(() => {
uni.$emit('refresh')
uni.navigateBack()
}, 1500)
}
this.getListCreateData(params)
}
} else {
uni.showToast({
@@ -652,11 +730,10 @@
this.showApprovalReasonDialog = true
},
// 新增:提交审批结果(通过/不通过)
// 提交审批结果(通过/不通过)
async submitApprovalResult() {
try {
this.approvalSubmitting = true
console.log(this.formData,'formData----')
// 构造审批参数(
const params = {
processType: this.approvalType === 'through' ? 1 : 2, // 1=通过2=不通过
@@ -669,26 +746,8 @@
}
}
//调用审批接口(替换为你的实际审批接口)
const res = await getListCreate(params,this.modelId)
if (res.code !== 0) {
uni.showToast({
title: res.msg || '审批失败',
icon: 'none'
})
return
}
// 关闭弹窗,刷新审批记录
this.showApprovalReasonDialog = false
uni.showToast({
title: '发起成功',
icon: 'success'
})
setTimeout(() => {
// 返回上一页或刷新列表
uni.$emit('refresh')
uni.navigateBack()
}, 1500)
//调用审批接口
this.getListCreateData(params)
} catch (err) {
console.error('提交审批结果失败:', err)
@@ -802,6 +861,7 @@
applyDepName: res.data.applyDepName || '',
applyDepData: res.data.applyDepData || null,
};
console.log(this.formData,'formData123')
this.$nextTick(()=>{
if (this.$refs.dynamicForm && this.$refs.dynamicForm.setFormData) {
this.$refs.dynamicForm.setFormData('applyDepId', this.formData.applyDepId);
@@ -1220,20 +1280,31 @@
.buttom-actions {
display: flex;
align-items: center;
justify-content: flex-end;
padding: 20rpx 30rpx;
// justify-content: flex-end;
// padding: 20rpx 30rpx;
background: #fff;
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 99;
gap: 20rpx;
z-index: 999 !important;
padding: 10rpx 0rpx;
// gap: 20rpx;
.buttom-btn {
min-width: 180rpx;
width: 300rpx;
min-width: 300rpx;
padding: 12rpx 15rpx;
flex: 1;
}
}
.launch-flow-btn {
background: #19be6b !important;
}
.launch-flow-btn[disabled] {
background: #19be6b !important;
opacity: 0.6;
}
.more-btn {
position: relative;
@@ -1243,7 +1314,7 @@
justify-content: center;
width: 80rpx;
height: 80rpx;
flex: 1;
.more-text {
font-size: 22rpx;
color: #606266;
@@ -1493,25 +1564,26 @@
}
.approval-list {
padding: 20rpx 30rpx;
padding: 10rpx 30rpx;
}
.approval-item {
display: flex;
position: relative;
margin-bottom: 30rpx;
margin-bottom: 20rpx;
padding-top: 10rpx;
}
.approval-line-container {
display: flex;
flex-direction: column;
align-items: center;
margin-right: 20rpx;
margin-right: 16rpx;
}
.approval-line {
width: 2rpx;
background-color: #e5e5e5;
background-color: #8e8585;
flex: 1;
margin-top: 8rpx;
}
@@ -1523,15 +1595,20 @@
display: flex;
align-items: center;
justify-content: center;
// background-color: #fff;
border: 2rpx solid;
/* 状态点边框色 */
&.status-processing {
background-color: #E6F7FF;
background-color: #409EFF;
border-color: #409EFF;
}
&.status-pass {
background-color: #F0F9EB;
background-color: #67C23A;
border-color: #67C23A;
}
&.status-reject {
background-color: #FFF2F0;
background-color: #F56C6C;
border-color: #F56C6C;
}
}
@@ -1546,34 +1623,45 @@
.task-header {
display: flex;
align-items: center;
margin-bottom: 16rpx;
justify-content: space-between;
margin-bottom: 12rpx;
.task-name {
font-size: 28rpx;
color: #333;
}
}
.status-tag {
font-size: 24rpx;
padding: 4rpx 12rpx;
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-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;
}
}
.info-row {
font-size: 24rpx;
color: #666;
@@ -1586,6 +1674,23 @@
.info-value {
color: #666;
}
.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;
}
.dept-tag {
display: inline-block;
background-color: #F5F7FA;
@@ -1642,4 +1747,52 @@
padding: 20rpx 30rpx;
border-top: 1rpx solid #f0f0f0;
}
.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;
}
</style>