|
|
@@ -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>
|