提交代码

This commit is contained in:
zlwu committed 2025-08-11 10:53:47 +08:00
1 parent ed9826f92b
commit d634846222
6 files changed
+867 -20

No files matched your search

+2 -2
View File
@@ -599,7 +599,7 @@ export default {
<style scoped>
.file-upload-container {
width: 90%;
width: 100%;
text-align: left;
}
@@ -640,7 +640,7 @@ export default {
border: 1rpx solid #dcdfe6;
border-radius: 8rpx;
overflow: hidden;
max-height: 400rpx;
max-height: 250rpx;
overflow-y: auto;
}
@@ -3,11 +3,11 @@
<u-popup
:show="visible"
mode="center"
width="85%"
width="100%"
height="90%"
border-radius="12"
@close="cancel"
:custom-style="{ width: '80vw' }"
:custom-style="{ width: '90vw' }"
>
<view class="submit-verify-container">
<view class="header">
@@ -198,7 +198,7 @@
>
退回
</u-button>
<u-button type="info" @click="cancel" class="action-btn">取消</u-button>
<!-- <u-button type="info" @click="cancel" class="action-btn">取消</u-button> -->
</view>
</view>
</view>
@@ -245,10 +245,11 @@
<u-popup
:show="backVisible"
mode="center"
width="90%"
width="80%"
height="70%"
border-radius="12"
@close="backVisible = false"
:custom-style="{ width: '90vw' }"
>
<view class="back-container">
<view class="header">
@@ -1109,6 +1110,7 @@ export default {
.content {
flex: 1;
padding: 30rpx;
width: 90% !important;
}
.form-item {
+14 -13
View File
@@ -1,11 +1,12 @@
<template>
<u-popup
v-model="visible"
:show="visible"
mode="center"
width="95%"
height="85%"
border-radius="12"
@close="close"
:custom-style="{ width: '90vw' }"
>
<view class="user-select-container">
<!-- 标题栏 -->
@@ -40,7 +41,7 @@
</view>
<!-- 部门选择 -->
<view class="dept-section">
<!-- <view class="dept-section">
<u-subsection
:list="deptList"
v-model="currentDeptIndex"
@@ -48,10 +49,10 @@
activeColor="#2979ff"
fontSize="28rpx"
/>
</view>
</view> -->
<!-- 用户列表 -->
<view class="user-list">
<scroll-view class="user-list" scroll-y="true" @scrolltolower="loadMore">
<u-loading-page
v-if="loading"
loading-text="正在加载用户列表..."
@@ -89,12 +90,12 @@
</view>
</template>
</template>
</view>
</scroll-view>
<!-- 分页加载 -->
<view v-if="!loading && userList.length > 0" class="load-more">
<u-loadmore :status="loadStatus" @loadmore="loadMore" color="#2979ff" />
</view>
<!-- 分页加载提示 -->
<!-- <view v-if="!loading && userList.length > 0" class="load-more">
<u-loadmore :status="loadStatus" color="#2979ff" />
</view> -->
<!-- 底部操作按钮 -->
<view class="footer">
@@ -193,7 +194,7 @@ export default {
async getDeptList() {
try {
const response = await request({
url: '/system/dept/treeselect',
url: '/system/user/deptTree',
method: 'GET',
});
@@ -294,7 +295,7 @@ export default {
// 加载更多
loadMore() {
if (this.loadStatus === 'loadmore') {
if (this.loadStatus === 'loadmore' && !this.loading) {
this.queryParams.pageNum++;
this.getUserList(true);
}
@@ -457,9 +458,9 @@ export default {
}
.user-list {
flex: 1;
overflow-y: auto;
height: 600rpx; /* 固定高度 */
padding: 0 30rpx;
width: 90% !important;
}
.empty-data {
+8
View File
@@ -33,6 +33,14 @@
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/taskWaiting",
"style": {
"navigationBarTitleText": "我的待办",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/task/taskFinish",
"style": {
+1 -1
View File
@@ -725,7 +725,7 @@ export default {
// 跳转到我的待办
gotoAllTaskWaiting() {
uni.navigateTo({
url: '/pages/task/allTaskWaiting',
url: '/pages/task/taskWaiting',
});
},
+836
View File
@@ -0,0 +1,836 @@
<template>
<view class="task-waiting-container">
<!-- 顶部导航 -->
<u-navbar title="我的待办" :border-bottom="false" bg-color="#3c9cff" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</u-navbar>
<!-- 搜索区域 -->
<view class="search-container">
<u-search
v-model="queryParams.flowName"
placeholder="请输入流程定义名称"
:show-action="true"
action-text="搜索"
:animation="true"
@search="handleQuery"
@custom="handleQuery"
></u-search>
<!-- 申请人选择 -->
<view class="filter-section">
<view class="filter-item" @click="openUserSelect">
<u-icon name="account" size="20" color="#3c9cff"></u-icon>
<text class="filter-text">
{{
selectedUsers.length > 0
? `已选择${selectedUsers.length}个申请人`
: '选择申请人'
}}
</text>
<u-badge
v-if="selectedUsers.length > 0"
:count="selectedUsers.length"
:max="99"
></u-badge>
<u-icon name="arrow-right" size="16" color="#c0c4cc"></u-icon>
</view>
</view>
</view>
<!-- 加载状态 -->
<u-loading-page
:loading="loading && taskList.length === 0"
loading-text="加载中..."
></u-loading-page>
<!-- 待办任务列表 -->
<view v-if="!loading || taskList.length > 0" class="content-wrapper">
<!-- 空状态 -->
<view v-if="taskList.length === 0 && !loading" class="empty-container">
<u-empty text="暂无待办任务" mode="data"></u-empty>
</view>
<!-- 任务卡片列表 -->
<view v-else class="task-list">
<view v-for="(item, index) in taskList" :key="item.id || index" class="task-card">
<!-- 卡片头部 -->
<view class="card-header">
<view class="task-title">{{ item.flowName || '-' }}</view>
<view class="task-status">
<u-tag
:text="getFlowStatusText(item.flowStatus)"
:type="getFlowStatusType(item.flowStatus)"
size="mini"
></u-tag>
</view>
</view>
<!-- 卡片内容 -->
<view class="card-content">
<view class="info-item">
<text class="label">流程编码:</text>
<text class="value">{{ item.flowCode || '-' }}</text>
</view>
<view class="info-item">
<text class="label">流程分类:</text>
<text class="value">{{ item.categoryName || '-' }}</text>
</view>
<view class="info-item">
<text class="label">任务名称:</text>
<text class="value">{{ item.nodeName || '-' }}</text>
</view>
<view class="info-item">
<text class="label">申请人:</text>
<text class="value">{{ item.createByName || '-' }}</text>
</view>
<view class="info-item">
<text class="label">办理人:</text>
<view class="assignee-tags">
<template v-if="item.assigneeNames">
<u-tag
v-for="(name, idx) in item.assigneeNames.split(',')"
:key="idx"
:text="name"
type="success"
size="mini"
style="margin-right: 8rpx; margin-bottom: 8rpx"
></u-tag>
</template>
<u-tag v-else text="无" type="info" size="mini"></u-tag>
</view>
</view>
<view class="info-item">
<text class="label">创建时间:</text>
<text class="value">{{ formatTime(item.createTime) }}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="card-actions">
<view class="action-btn primary-btn" @click="handleApproval(item)">
<u-icon name="edit-pen" size="16" color="#3c9cff"></u-icon>
<text class="btn-text">办理</text>
</view>
</view>
</view>
</view>
<!-- 滚动加载更多 -->
<u-loadmore
v-if="taskList.length > 0"
:status="loadmoreStatus"
:load-text="{
loadmore: '轻轻上拉',
loading: '努力加载中',
nomore: '没有更多了',
}"
@loadmore="loadMore"
></u-loadmore>
</view>
<!-- 申请人选择弹窗 -->
<u-popup
:show="userSelectDialog.visible"
mode="bottom"
:round="14"
@close="cancelUserSelect"
>
<view class="user-select-popup">
<view class="popup-header">
<text class="popup-title">选择申请人</text>
<u-icon
name="close"
@click="cancelUserSelect"
size="24"
color="#909399"
></u-icon>
</view>
<view class="popup-content">
<u-search
v-model="userSearchKeyword"
placeholder="搜索用户名或姓名"
:show-action="false"
@input="searchUsers"
style="height: 60rpx !important"
></u-search>
<scroll-view scroll-y class="user-list">
<view
v-for="user in userList"
:key="user.userId"
class="user-item"
@click="toggleUserSelect(user)"
>
<view class="user-info">
<u-avatar :text="getAvatarText(user.nickName)" size="40"></u-avatar>
<view class="user-details">
<text class="user-name">{{ user.nickName }}</text>
<text class="user-username">{{ user.userName }}</text>
</view>
</view>
<u-checkbox
v-model="user.selected"
@change="onCheckboxChange(user, $event)"
@click.stop
></u-checkbox>
</view>
</scroll-view>
</view>
<view class="popup-footer">
<u-button @click="cancelUserSelect" type="info" size="default">取消</u-button>
<u-button @click="confirmUserSelect" type="primary" size="default">
确定
</u-button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import { request } from '@/common/request.js';
import { getDict, getDictTagType } from '@/common/dict.js';
export default {
name: 'TaskWaiting',
data() {
return {
loading: false,
taskList: [],
total: 0,
loadmoreStatus: 'loadmore', // loadmore, loading, nomore
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
flowName: '', // 流程定义名称
createByIds: [], // 申请人IDs
},
// 流程状态字典
flowStatusDict: [],
// 申请人选择相关
userSelectDialog: {
visible: false,
},
selectedUsers: [], // 已选择的申请人
userList: [], // 用户列表
userSearchKeyword: '', // 用户搜索关键词
userSearchTimer: null, // 搜索防抖定时器
};
},
async created() {
await this.loadFlowStatusDict();
await this.getList();
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack();
},
// 加载流程状态字典
async loadFlowStatusDict() {
try {
this.flowStatusDict = await getDict('wf_business_status');
console.log('流程状态字典加载完成:', this.flowStatusDict);
} catch (error) {
console.error('加载流程状态字典失败:', error);
}
},
// 获取流程状态显示文本
getFlowStatusText(status) {
if (!this.flowStatusDict || this.flowStatusDict.length === 0) {
// 字典未加载时的降级处理
const statusMap = {
0: '待提交',
1: '审批中',
2: '已通过',
3: '已拒绝',
4: '已撤回',
5: '已终止',
6: '已作废',
};
return statusMap[status] || '未知';
}
const statusItem = this.flowStatusDict.find((item) => item.value === status);
return statusItem ? statusItem.label : '未知';
},
// 获取流程状态标签类型
getFlowStatusType(status) {
const fallbackMap = {
0: 'info', // 待提交
1: 'warning', // 审批中
2: 'success', // 已通过
3: 'error', // 已拒绝
4: 'info', // 已撤回
5: 'error', // 已终止
6: 'error', // 已作废
};
return getDictTagType(this.flowStatusDict, status, fallbackMap);
},
// 获取待办任务列表
async getList(isLoadMore = false) {
if (!isLoadMore) {
this.loading = true;
this.queryParams.pageNum = 1;
} else {
this.loadmoreStatus = 'loading';
}
try {
const res = await request({
url: '/workflow/task/pageByTaskWait',
method: 'GET',
params: {
pageNum: this.queryParams.pageNum,
pageSize: this.queryParams.pageSize,
flowName: this.queryParams.flowName || undefined,
createByIds:
this.queryParams.createByIds.length > 0
? this.queryParams.createByIds
: undefined,
},
});
if (res && res.code === 200) {
const newList = res.rows || [];
if (isLoadMore) {
this.taskList = [...this.taskList, ...newList];
// 判断是否还有更多数据
if (newList.length < this.queryParams.pageSize) {
this.loadmoreStatus = 'nomore';
} else {
this.loadmoreStatus = 'loadmore';
}
} else {
this.taskList = newList;
this.total = res.total || 0;
// 重置加载更多状态
if (this.taskList.length >= this.total) {
this.loadmoreStatus = 'nomore';
} else {
this.loadmoreStatus = 'loadmore';
}
}
} else {
if (!isLoadMore) {
this.taskList = [];
this.total = 0;
}
uni.showToast({
title: res?.msg || '获取数据失败',
icon: 'none',
});
if (isLoadMore) {
this.loadmoreStatus = 'loadmore';
}
}
} catch (error) {
console.error('获取待办任务列表失败:', error);
if (!isLoadMore) {
this.taskList = [];
this.total = 0;
}
uni.showToast({
title: '获取数据失败',
icon: 'none',
});
if (isLoadMore) {
this.loadmoreStatus = 'loadmore';
}
} finally {
if (!isLoadMore) {
this.loading = false;
}
}
},
// 搜索操作
handleQuery() {
this.getList();
},
// 加载更多
loadMore() {
if (this.loadmoreStatus === 'nomore' || this.loadmoreStatus === 'loading') {
return;
}
this.queryParams.pageNum++;
this.getList(true);
},
// 格式化时间
formatTime(timeStr) {
if (!timeStr) return '-';
try {
const date = new Date(timeStr);
if (isNaN(date.getTime())) return timeStr;
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
} catch (error) {
console.error('时间格式化失败:', error);
return timeStr;
}
},
// 办理任务
handleApproval(row) {
console.log('办理任务:', row);
// 动态跳转到formPath对应的路径,携带businessId和taskId参数
if (row.formPath) {
// 确保路径以pages开头
let fullPath = row.formPath;
// 统一处理路径:去掉开头的/,然后添加pages前缀
if (!fullPath.startsWith('pages/')) {
// 如果以/开头,先去掉/
if (fullPath.startsWith('/')) {
fullPath = fullPath.substring(1);
}
// 添加pages前缀
fullPath = `pages/${fullPath}`;
}
const url = `/${fullPath}?id=${row.businessId}&taskId=${row.id}&type=approval`;
// 添加调试信息
console.log('办理任务 - 原始formPath:', row.formPath);
console.log('办理任务 - 处理后的fullPath:', fullPath);
console.log('办理任务 - 最终跳转URL:', url);
// 使用navigateTo进行跳转
uni.navigateTo({
url: url,
fail: (err) => {
console.error('办理页面跳转失败:', err);
// 如果跳转失败,使用原有逻辑作为备选
this.fallbackApproval(row);
},
});
} else {
// 如果没有formPath,使用原有逻辑作为备选
this.fallbackApproval(row);
}
},
// 备选办理方法(原有逻辑)
fallbackApproval(row) {
console.log('使用备选办理逻辑:', row);
// 根据表单类型跳转到对应的审批页面
if (row.formCustom === 'Y') {
// 自定义表单
uni.navigateTo({
url: `/pages/workflow/form/custom?businessId=${row.businessId}&taskId=${
row.id
}&formPath=${encodeURIComponent(row.formPath || '')}`,
});
} else {
// 默认表单或其他类型
uni.navigateTo({
url: `/pages/workflow/approval/index?businessId=${row.businessId}&taskId=${row.id}&type=approval`,
});
}
},
// 打开申请人选择
openUserSelect() {
this.userSelectDialog.visible = true;
this.loadUserList();
},
// 取消申请人选择
cancelUserSelect() {
this.userSelectDialog.visible = false;
this.userSearchKeyword = '';
},
// 确认申请人选择
confirmUserSelect() {
// 获取当前弹窗中选中的用户
const currentSelected = this.userList.filter((user) => user.selected);
console.log('确认选择 - 用户列表:', this.userList);
console.log('确认选择 - 选中的用户:', currentSelected);
if (currentSelected.length > 0) {
// 如果有选中的用户,更新已选择的用户列表
this.selectedUsers = currentSelected;
this.queryParams.createByIds = currentSelected.map((user) => user.userId);
console.log('更新选中用户:', this.selectedUsers);
} else {
// 如果没有选中任何用户,清空已选择的用户列表
this.selectedUsers = [];
this.queryParams.createByIds = [];
console.log('清空选中用户');
}
this.userSelectDialog.visible = false;
this.handleQuery();
},
// 切换用户选择状态
toggleUserSelect(user) {
this.$set(user, 'selected', !user.selected);
console.log('切换用户选择:', user.nickName, '选中状态:', user.selected);
},
// checkbox变化处理
onCheckboxChange(user, value) {
console.log('Checkbox变化:', user.nickName, '新值:', value);
this.$set(user, 'selected', value);
},
// 加载用户列表
async loadUserList() {
try {
const res = await request({
url: '/system/user/list',
method: 'GET',
params: {
pageNum: 1,
pageSize: 100,
userName: this.userSearchKeyword || undefined,
nickName: this.userSearchKeyword || undefined,
},
});
if (res && res.code === 200) {
// 每次加载用户列表时,所有用户默认都不选中
this.userList = (res.rows || []).map((user) => ({
...user,
selected: false,
}));
console.log('加载用户列表完成,用户数量:', this.userList.length);
} else {
this.userList = [];
uni.showToast({
title: res?.msg || '获取用户列表失败',
icon: 'none',
});
}
} catch (error) {
console.error('获取用户列表失败:', error);
this.userList = [];
uni.showToast({
title: '获取用户列表失败',
icon: 'none',
});
}
},
// 搜索用户(防抖处理)
searchUsers() {
if (this.userSearchTimer) {
clearTimeout(this.userSearchTimer);
}
this.userSearchTimer = setTimeout(() => {
this.loadUserList();
}, 500);
},
// 获取头像显示文字(取前两个字符)
getAvatarText(nickName) {
if (!nickName) return '';
// 取前两个字符,如果只有一个字符则返回该字符
return nickName.length > 1 ? nickName.substring(0, 1) : nickName;
},
},
};
</script>
<style lang="scss" scoped>
.task-waiting-container {
background-color: #f5f6fa;
min-height: 100vh;
}
.search-container {
padding: 20rpx;
background-color: #fff;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.filter-section {
margin-top: 20rpx;
}
.filter-item {
display: flex;
align-items: center;
padding: 24rpx;
background-color: #f8f9fa;
border-radius: 12rpx;
border: 1px solid #e4e7ed;
&:active {
background-color: #f0f0f0;
}
}
.filter-text {
flex: 1;
margin-left: 16rpx;
font-size: 28rpx;
color: #303133;
}
.content-wrapper {
padding: 20rpx;
}
.empty-container {
padding: 100rpx 20rpx;
}
.task-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.task-card {
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
overflow: hidden;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
border-bottom: 1px solid #f0f0f0;
}
.task-title {
font-size: 32rpx;
font-weight: bold;
color: #303133;
flex: 1;
}
.task-status {
margin-left: 16rpx;
}
.card-content {
padding: 24rpx;
}
.info-item {
display: flex;
align-items: flex-start;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
}
.label {
font-size: 28rpx;
color: #606266;
min-width: 140rpx;
margin-right: 16rpx;
}
.value {
font-size: 28rpx;
color: #303133;
flex: 1;
word-break: break-all;
}
.assignee-tags {
flex: 1;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.card-actions {
display: flex;
justify-content: flex-end;
padding: 20rpx;
border-top: 1px solid #f0f0f0;
}
.action-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 16rpx 40rpx;
border-radius: 12rpx;
background-color: #f8f9fa;
min-width: 120rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
opacity: 0.8;
}
}
.btn-text {
font-size: 24rpx;
margin-top: 8rpx;
color: #303133;
font-weight: 500;
}
.primary-btn {
background-color: rgba(60, 156, 255, 0);
.btn-text {
color: #3c9cff;
}
}
// 申请人选择弹窗样式
.user-select-popup {
background-color: #fff;
border-radius: 14rpx 14rpx 0 0;
overflow: hidden;
height: 80vh;
display: flex;
flex-direction: column;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0; /* 防止头部被压缩 */
}
.popup-title {
font-size: 32rpx;
font-weight: bold;
color: #303133;
}
.popup-content {
flex: 1;
padding: 24rpx 24rpx 0 24rpx; /* 底部不需要padding,由footer提供 */
display: flex;
flex-direction: column;
overflow: hidden; /* 防止内容溢出 */
}
.user-list {
margin-top: 20rpx;
flex: 1; /* 使用弹性布局,自动占满剩余空间 */
overflow-y: auto; /* 添加垂直滚动条 */
min-height: 0; /* 确保flex子元素可以收缩 */
}
.user-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
border-bottom: 1px solid #f8f9fa;
&:last-child {
border-bottom: none;
}
&:active {
background-color: #f8f9fa;
}
}
.user-info {
display: flex;
align-items: center;
flex: 1;
}
.user-details {
margin-left: 20rpx;
flex: 1;
}
.user-name {
display: block;
font-size: 28rpx;
color: #303133;
font-weight: 500;
}
.user-username {
display: block;
font-size: 24rpx;
color: #909399;
margin-top: 4rpx;
}
.popup-footer {
padding: 24rpx;
border-top: 1px solid #f0f0f0;
display: flex;
gap: 20rpx;
flex-shrink: 0; /* 防止底部按钮区域被压缩 */
background-color: #fff; /* 确保背景色一致 */
}
// uview组件样式调整
::v-deep .u-search {
background-color: #f5f6fa;
/* 清除默认的 flex: 1 */
flex: none;
}
// 弹窗内搜索框样式优化
.popup-content ::v-deep .u-search {
background-color: #f8f9fa;
border-radius: 8rpx;
margin-bottom: 0; /* 移除默认margin */
}
.popup-content ::v-deep .u-search__content {
height: 60rpx; /* 设置搜索框高度 */
border-radius: 8rpx;
}
.popup-content ::v-deep .u-input {
font-size: 28rpx; /* 调整输入框字体大小 */
}
::v-deep .u-tag {
margin: 0;
}
::v-deep .u-loadmore {
padding: 40rpx 0;
}
</style>