feat(oa): 新增项目进度和进度步骤跟踪的API接口
添加项目进度管理和进度步骤跟踪的相关API接口,包括列表查询、详情获取、新增、修改、删除等功能
This commit is contained in:
61
api/oa/projectSchedule.js
Normal file
61
api/oa/projectSchedule.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import request from "@/util/oaRequest"
|
||||||
|
|
||||||
|
// 查询项目进度列表
|
||||||
|
export function listProjectSchedule (query) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectSchedule/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询项目进度详细
|
||||||
|
export function getProjectSchedule (scheduleId) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectSchedule/' + scheduleId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增项目进度
|
||||||
|
export function addProjectSchedule (data) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectSchedule',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改项目进度
|
||||||
|
export function updateProjectSchedule (data) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectSchedule',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改项目进度
|
||||||
|
export function completeSchedule (data) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectSchedule/complete',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除项目进度
|
||||||
|
export function delProjectSchedule (scheduleId) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectSchedule/' + scheduleId,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addByProjectId (data) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectSchedule/addByProjectId',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
61
api/oa/projectScheduleStep.js
Normal file
61
api/oa/projectScheduleStep.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import request from "@/util/oaRequest"
|
||||||
|
|
||||||
|
// 查询项目进度步骤跟踪列表
|
||||||
|
export function listProjectScheduleStep (query) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectScheduleStep/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询项目进度步骤跟踪详细
|
||||||
|
export function getProjectScheduleStep (trackId) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectScheduleStep/' + trackId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增项目进度步骤跟踪
|
||||||
|
export function addProjectScheduleStep (data) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectScheduleStep',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改项目进度步骤跟踪
|
||||||
|
export function updateProjectScheduleStep (data) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectScheduleStep',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改项目进度步骤跟踪
|
||||||
|
export function changeBatch (data) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectScheduleStep/change-batch',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除项目进度步骤跟踪
|
||||||
|
export function delProjectScheduleStep (trackId) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectScheduleStep/' + trackId,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listPage (query) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectScheduleStep/listPage',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="remaining-time" :class="computedClass">
|
<view class="remaining-time" :class="computedClass">
|
||||||
<uni-icons
|
|
||||||
:type="isExpired ? 'clock' : 'time'"
|
|
||||||
:size="14"
|
|
||||||
class="icon"
|
|
||||||
></uni-icons>
|
|
||||||
<text class="text">{{ displayText }}</text>
|
<text class="text">{{ displayText }}</text>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -13,53 +8,83 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'RemainingTime',
|
name: 'RemainingTime',
|
||||||
props: {
|
props: {
|
||||||
// 截至日期(ISO格式,如:'2025-12-31T23:59:59')
|
// 截至日期(支持多种格式或null)
|
||||||
expireDate: {
|
expireDate: {
|
||||||
type: String,
|
type: [String, null], // 允许字符串或null
|
||||||
required: true,
|
default: null, // 默认null
|
||||||
validator(value) {
|
validator(value) {
|
||||||
// 验证日期格式有效性
|
// 允许null,非null时检查是否能解析为有效日期
|
||||||
|
if (value === null) return true;
|
||||||
return !isNaN(Date.parse(value));
|
return !isNaN(Date.parse(value));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 阈值天数(超过此值为绿色,否则红色)
|
// 阈值天数
|
||||||
thresholdDays: {
|
thresholdDays: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true,
|
default: 3,
|
||||||
default: 3,
|
|
||||||
validator(value) {
|
validator(value) {
|
||||||
return value >= 0;
|
return value >= 0;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
// 是否已完成
|
||||||
|
finished: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
// 计算剩余天数(负数表示逾期)
|
// 计算剩余天数(处理null和无效日期)
|
||||||
remainingDays() {
|
remainingDays() {
|
||||||
|
// 如果无截止日期或日期无效,返回null
|
||||||
|
if (!this.expireDate) return null;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const expire = new Date(this.expireDate);
|
const expire = new Date(this.expireDate);
|
||||||
|
// 无效日期处理
|
||||||
|
if (isNaN(expire.getTime())) return null;
|
||||||
const diffTime = expire - now;
|
const diffTime = expire - now;
|
||||||
// 转换为天数并四舍五入
|
|
||||||
return Math.round(diffTime / (1000 * 60 * 60 * 24));
|
return Math.round(diffTime / (1000 * 60 * 60 * 24));
|
||||||
},
|
},
|
||||||
// 是否已逾期
|
// 是否已逾期(仅在有有效日期时判断)
|
||||||
isExpired() {
|
isExpired() {
|
||||||
return this.remainingDays < 0;
|
return this.remainingDays !== null && this.remainingDays < 0;
|
||||||
},
|
},
|
||||||
// 显示文本
|
// 显示文本(优先级:完成 > 无日期 > 逾期 > 剩余时间)
|
||||||
displayText() {
|
displayText() {
|
||||||
|
if (this.finished) {
|
||||||
|
return '已完成';
|
||||||
|
}
|
||||||
|
// 无截止日期的情况
|
||||||
|
if (this.remainingDays === null) {
|
||||||
|
return '无截止日期';
|
||||||
|
}
|
||||||
if (this.isExpired) {
|
if (this.isExpired) {
|
||||||
return `已逾期 ${Math.abs(this.remainingDays)} 天`;
|
return `已逾期 ${Math.abs(this.remainingDays)} 天`;
|
||||||
}
|
}
|
||||||
return `剩余 ${this.remainingDays} 天`;
|
return `剩余 ${this.remainingDays} 天`;
|
||||||
},
|
},
|
||||||
// 计算样式类
|
// 图标类型
|
||||||
computedClass() {
|
getIconType() {
|
||||||
if (this.isExpired) {
|
if (this.finished) {
|
||||||
return 'expired'; // 逾期状态
|
return 'checkmark';
|
||||||
}
|
}
|
||||||
return this.remainingDays > this.thresholdDays
|
if (this.remainingDays === null) {
|
||||||
? 'normal' // 正常状态(超过阈值)
|
return 'help'; // 无日期时显示帮助图标
|
||||||
: 'warning'; // 警告状态(低于阈值)
|
}
|
||||||
|
return this.isExpired ? 'clock' : 'time';
|
||||||
|
},
|
||||||
|
// 样式类
|
||||||
|
computedClass() {
|
||||||
|
if (this.finished) {
|
||||||
|
return 'finished';
|
||||||
|
}
|
||||||
|
// 无截止日期的样式
|
||||||
|
if (this.remainingDays === null) {
|
||||||
|
return 'no-date';
|
||||||
|
}
|
||||||
|
if (this.isExpired) {
|
||||||
|
return 'expired';
|
||||||
|
}
|
||||||
|
return this.remainingDays > this.thresholdDays ? 'normal' : 'warning';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -83,19 +108,30 @@ export default {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 正常状态(超过阈值) */
|
/* 正常状态 */
|
||||||
.normal {
|
.normal {
|
||||||
color: #00b42a; /* 绿色 */
|
color: #00b42a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 警告状态(低于阈值) */
|
/* 警告状态 */
|
||||||
.warning {
|
.warning {
|
||||||
color: #f53f3f; /* 红色 */
|
color: #f53f3f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 逾期状态 */
|
/* 逾期状态 */
|
||||||
.expired {
|
.expired {
|
||||||
background-color: #fef0f0;
|
background-color: #fef0f0;
|
||||||
color: #f53f3f; /* 红色背景+红色文字 */
|
color: #f53f3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 完成状态 */
|
||||||
|
.finished {
|
||||||
|
background-color: #00b42a;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无截止日期状态 */
|
||||||
|
.no-date {
|
||||||
|
color: #888888; /* 灰色文字 */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -77,12 +77,10 @@
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'notice':
|
case 'notice':
|
||||||
getNotice(id)
|
getFeedback(id)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.article = {
|
this.article = {
|
||||||
...res.data,
|
...res.data,
|
||||||
title: res.data.noticeTitle,
|
|
||||||
content: res.data.noticeContent,
|
|
||||||
};
|
};
|
||||||
uni.setNavigationBarTitle({
|
uni.setNavigationBarTitle({
|
||||||
title: '通知详情'
|
title: '通知详情'
|
||||||
@@ -107,8 +105,10 @@
|
|||||||
uni.setNavigationBarTitle({
|
uni.setNavigationBarTitle({
|
||||||
title: '反馈详情'
|
title: '反馈详情'
|
||||||
})
|
})
|
||||||
// 标记为已读
|
if (this.article.state == 0) {
|
||||||
toRead(this.article);
|
// 标记为已读
|
||||||
|
toRead(this.article);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '未找到消息',
|
title: '未找到消息',
|
||||||
|
|||||||
@@ -70,7 +70,8 @@
|
|||||||
title: '',
|
title: '',
|
||||||
projectId: '',
|
projectId: '',
|
||||||
startTime: '',
|
startTime: '',
|
||||||
endTime: ''
|
endTime: '',
|
||||||
|
type: 'feedback'
|
||||||
},
|
},
|
||||||
total: 0, // 总条数
|
total: 0, // 总条数
|
||||||
loading: false, // 加载状态
|
loading: false, // 加载状态
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default {
|
|||||||
text: '任务中心',
|
text: '任务中心',
|
||||||
icon: '/static/images/task.png',
|
icon: '/static/images/task.png',
|
||||||
url: '/pages/workbench/task/task',
|
url: '/pages/workbench/task/task',
|
||||||
category: "信息中心"
|
category: "项目中心"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '项目排产',
|
text: '项目排产',
|
||||||
|
|||||||
@@ -8,8 +8,13 @@
|
|||||||
|
|
||||||
<!-- 公告列表 -->
|
<!-- 公告列表 -->
|
||||||
<div class="notice-list" v-else>
|
<div class="notice-list" v-else>
|
||||||
<div class="notice-item" v-for="(notice, index) in notices" :key="index" @click="toDetail(notice.noticeId)">
|
<div class="notice-item" v-for="(notice, index) in notices" :key="index" @click="toDetail(notice)">
|
||||||
<div class="notice-title">{{ notice.noticeTitle }}</div>
|
<div class="notice-title">
|
||||||
|
<text>{{ notice.title }}</text>
|
||||||
|
<view class="dot" v-if="notice.state == 0">
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</div>
|
||||||
<div class="notice-meta">
|
<div class="notice-meta">
|
||||||
<span class="create-by">{{ notice.createBy || '未知发布人' }}</span>
|
<span class="create-by">{{ notice.createBy || '未知发布人' }}</span>
|
||||||
<span class="create-time">{{ notice.createTime || '未知时间' }}</span>
|
<span class="create-time">{{ notice.createTime || '未知时间' }}</span>
|
||||||
@@ -20,7 +25,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listNotice } from '@/api/oa/notice.js'
|
import { listFeedback, toRead } from '@/api/oa/feedback.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@@ -29,7 +34,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
listNotice({ pageSize: 999, pageNum: 1 }).then(res => {
|
listFeedback({ pageSize: 999, pageNum: 1, type: 'notice' }).then(res => {
|
||||||
// 确保res.rows存在,避免空数据报错
|
// 确保res.rows存在,避免空数据报错
|
||||||
this.notices = res?.rows || [];
|
this.notices = res?.rows || [];
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@@ -38,10 +43,16 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toDetail(id) {
|
toDetail(notice) {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/workbench/article/article?type=notice&id=' + id
|
url: '/pages/workbench/article/article?type=notice&id=' + notice.feedbackId
|
||||||
})
|
})
|
||||||
|
if (notice.state == 0) {
|
||||||
|
// 标记为已读
|
||||||
|
toRead(notice);
|
||||||
|
notice.state = 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,11 +83,21 @@ export default {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis; // 标题过长省略
|
text-overflow: ellipsis; // 标题过长省略
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
display: block;
|
||||||
|
width: 20rpx;
|
||||||
|
height: 20rpx;
|
||||||
|
background-color: red;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.notice-meta {
|
.notice-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,22 +1,583 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view class="report-schedule">
|
||||||
|
<!-- header始终显示 -->
|
||||||
|
<!-- <view class="search-bar">
|
||||||
|
<view class="search-container">
|
||||||
|
<view class="task-type-button-container">
|
||||||
|
<view class="task-type-button" @click="openDrawer">
|
||||||
|
<uni-icons type="settings" color="#2979ff" size="22"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="search-input custom-search-input">
|
||||||
|
<input v-model="queryParams.title" class="input" type="text" placeholder="搜索项目名称" @confirm="onSearch"
|
||||||
|
@keyup.enter="onSearch" />
|
||||||
|
<view class="search-icon" @click="onSearch">
|
||||||
|
<uni-icons type="search" color="#bbb" size="20"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view v-if="searchName" class="clear-icon" @click="onClearSearch">
|
||||||
|
<uni-icons type="closeempty" color="#bbb" size="18"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="add-button" @click="handleAdd">
|
||||||
|
<uni-icons type="plusempty" color="#2979ff" size="22"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view> -->
|
||||||
|
|
||||||
|
<!-- 筛选抽屉 -->
|
||||||
|
<uni-drawer ref="drawerRef" mode="left" :width="320">
|
||||||
|
<view class="drawer-content">
|
||||||
|
<view class="drawer-title">筛选</view>
|
||||||
|
<view class="drawer-form">
|
||||||
|
<view class="drawer-form-item">
|
||||||
|
<text class="drawer-label">需求方</text>
|
||||||
|
<oa-user-select v-model="queryParams.requesterId" placeholder="请选择需求方" />
|
||||||
|
</view>
|
||||||
|
<view class="drawer-form-item">
|
||||||
|
<text class="drawer-label">负责人</text>
|
||||||
|
<oa-user-select v-model="queryParams.ownerId" placeholder="请选择负责人" />
|
||||||
|
</view>
|
||||||
|
<view class="drawer-form-item">
|
||||||
|
<text class="drawer-label">关联项目</text>
|
||||||
|
<oa-project-select v-model="queryParams.projectId" placeholder="请选择关联项目" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="drawer-btns">
|
||||||
|
<button class="drawer-btn-primary" @click="applyFilterAndClose">确定</button>
|
||||||
|
<button class="drawer-btn" @click="resetFilterAndClose">重置</button>
|
||||||
|
<button class="drawer-btn" @click="closeDrawer">关闭</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-drawer>
|
||||||
|
|
||||||
|
<view>
|
||||||
|
<!-- 自定义列表,右滑菜单(uni-ui实现) -->
|
||||||
|
<scroll-view scroll-y style="height: 100vh;" @scrolltolower="loadMore">
|
||||||
|
<view v-if="customerList.length">
|
||||||
|
<uni-swipe-action>
|
||||||
|
<block v-for="(item, index) in customerList" :key="item.scheduleId">
|
||||||
|
<uni-swipe-action-item :right-options="getSwipeOptions(item)" @click="swipeActionClick($event, item)"
|
||||||
|
style="margin-bottom: 16rpx;">
|
||||||
|
<view class="card" @click="toDetail(item.scheduleId)">
|
||||||
|
<view class="card-title">
|
||||||
|
<text class="project">{{ item.projectName }}</text>
|
||||||
|
<uni-tag :type="item.status == 1 ? 'info' : 'success'" :text="item.status == 1 ? '进行中' : '已完成'"></uni-tag>
|
||||||
|
</view>
|
||||||
|
<view class="card-content">
|
||||||
|
<view>负责人:{{ item.steward }}</view>
|
||||||
|
<view>项目状态:<text v-if="item.isTop" style="color: #ff4d4f;">重点关注</text>
|
||||||
|
<text v-else style="">一般项目</text>
|
||||||
|
</view>
|
||||||
|
<view>开始时间:{{ item.startTime }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-swipe-action-item>
|
||||||
|
</block>
|
||||||
|
</uni-swipe-action>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty">暂无数据</view>
|
||||||
|
<view class="load-more-tips">
|
||||||
|
<u-loading-icon v-if="loadingMore" text="加载中..." size="20" textSize="14" />
|
||||||
|
<text v-else-if="!hasMore && customerList.length">没有更多了</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 新增/编辑弹窗 -->
|
||||||
|
<uni-popup ref="popupRef" type="bottom">
|
||||||
|
<view class="popup-content">
|
||||||
|
<view class="uni-form">
|
||||||
|
<view class="uni-form-item">
|
||||||
|
<text class="uni-form-label">选择要使用的模板</text>
|
||||||
|
<uni-data-checkbox v-model="form.templateType" :localdata="templateTypeOptions" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="uni-form-item">
|
||||||
|
<text class="uni-form-label">选择项目</text>
|
||||||
|
<oa-project-select v-model="form.projectId" placeholder="请选择要绑定的项目" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="popup-btns">
|
||||||
|
<u-button type="primary" @click="submitForm">确定</u-button>
|
||||||
|
<u-button @click="closePopup">取消</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<uni-fab @fabClick="handleAdd" horizontal="right"></uni-fab>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import {
|
||||||
|
addProjectSchedule,
|
||||||
|
listProjectSchedule,
|
||||||
|
updateProjectSchedule,
|
||||||
|
delProjectSchedule,
|
||||||
|
getProjectSchedule
|
||||||
|
} from '@/api/oa/projectSchedule.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
templateTypeOptions: [{
|
||||||
|
text: '信息化',
|
||||||
|
value: 'software'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '自动化',
|
||||||
|
value: 'automation'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
expressId: undefined,
|
||||||
|
description: undefined,
|
||||||
|
reportTime: undefined,
|
||||||
|
reportBy: undefined,
|
||||||
|
status: undefined,
|
||||||
|
},
|
||||||
|
projectOptions: [],
|
||||||
|
headerOptions: [],
|
||||||
|
customerList: [],
|
||||||
|
total: 0,
|
||||||
|
single: true,
|
||||||
|
multiple: true,
|
||||||
|
form: {},
|
||||||
|
showStartDate: false,
|
||||||
|
showEndDate: false,
|
||||||
|
swipeOptions: [{
|
||||||
|
text: '删除',
|
||||||
|
style: {
|
||||||
|
backgroundColor: '#fa3534',
|
||||||
|
color: '#fff'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
// 新增:筛选相关
|
||||||
|
searchName: '',
|
||||||
|
filterProject: '',
|
||||||
|
filterHeader: '',
|
||||||
|
filterDateRange: [],
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
loadingMore: false,
|
||||||
|
hasMore: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onLoad() {
|
||||||
|
this.pageNum = 1
|
||||||
|
this.hasMore = true
|
||||||
|
this.handleQuery()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
openDrawer() {
|
||||||
|
this.$refs.drawerRef.open();
|
||||||
|
},
|
||||||
|
toDetail(id) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/workbench/project/step?id=' + id
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeDrawer() {
|
||||||
|
this.$refs.drawerRef.close();
|
||||||
|
},
|
||||||
|
applyFilterAndClose() {
|
||||||
|
this.applyFilter();
|
||||||
|
this.closeDrawer();
|
||||||
|
},
|
||||||
|
resetFilterAndClose() {
|
||||||
|
this.resetFilter();
|
||||||
|
this.closeDrawer();
|
||||||
|
},
|
||||||
|
// 搜索框检索
|
||||||
|
onSearch() {
|
||||||
|
this.searchName = this.searchName
|
||||||
|
this.handleQuery()
|
||||||
|
},
|
||||||
|
onClearSearch() {
|
||||||
|
this.searchName = ''
|
||||||
|
this.queryParams.scheduleName = ''
|
||||||
|
this.handleQuery()
|
||||||
|
},
|
||||||
|
// 筛选抽屉应用
|
||||||
|
applyFilter() {
|
||||||
|
this.queryParams.projectId = this.filterProject
|
||||||
|
this.queryParams.header = this.filterHeader
|
||||||
|
if (this.filterDateRange && this.filterDateRange.length === 2) {
|
||||||
|
this.queryParams.dateRange = this.filterDateRange
|
||||||
|
} else {
|
||||||
|
this.queryParams.dateRange = []
|
||||||
|
}
|
||||||
|
this.handleQuery()
|
||||||
|
},
|
||||||
|
// 筛选抽屉重置
|
||||||
|
resetFilter() {
|
||||||
|
this.queryParams = {
|
||||||
|
...this.queryParams,
|
||||||
|
level: undefined,
|
||||||
|
source: undefined,
|
||||||
|
industryId: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.pageNum = 1
|
||||||
|
this.hasMore = true
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
if (this.queryParams.dateRange && this.queryParams.dateRange.length === 2) {
|
||||||
|
this.queryParams.startDate = this.queryParams.dateRange[0]
|
||||||
|
this.queryParams.endDate = this.queryParams.dateRange[1]
|
||||||
|
} else {
|
||||||
|
this.queryParams.startDate = ''
|
||||||
|
this.queryParams.endDate = ''
|
||||||
|
}
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
this.loadingMore = true
|
||||||
|
this.queryParams.pageNum = this.pageNum
|
||||||
|
this.queryParams.pageSize = this.pageSize
|
||||||
|
listProjectSchedule(this.queryParams).then(res => {
|
||||||
|
const rows = res.rows || []
|
||||||
|
if (this.pageNum === 1) {
|
||||||
|
this.customerList = rows
|
||||||
|
} else {
|
||||||
|
this.customerList = this.customerList.concat(rows)
|
||||||
|
}
|
||||||
|
// 判断是否还有更多
|
||||||
|
this.hasMore = rows.length === this.pageSize
|
||||||
|
this.total = res.total || 0
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadingMore = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadMore() {
|
||||||
|
if (!this.hasMore || this.loadingMore) return
|
||||||
|
this.pageNum++
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
handleAdd() {
|
||||||
|
this.form = {
|
||||||
|
scheduleId: this.$store.state.user.id,
|
||||||
|
title: undefined,
|
||||||
|
requesterId: undefined,
|
||||||
|
ownerId: undefined,
|
||||||
|
projectId: undefined,
|
||||||
|
description: undefined,
|
||||||
|
deadline: undefined,
|
||||||
|
status: 0,
|
||||||
|
remark: undefined,
|
||||||
|
accessory: undefined,
|
||||||
|
createBy: undefined,
|
||||||
|
createTime: undefined,
|
||||||
|
updateBy: undefined,
|
||||||
|
updateTime: undefined,
|
||||||
|
delFlag: undefined
|
||||||
|
}
|
||||||
|
this.$refs.popupRef.open('bottom')
|
||||||
|
},
|
||||||
|
handleUpdate(row) {
|
||||||
|
getProjectSchedule(row.scheduleId).then(res => {
|
||||||
|
this.form = res.data || {}
|
||||||
|
this.$refs.popupRef.open('bottom')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDelete(item) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认删除',
|
||||||
|
content: `确定要删除排产“${item.scheduleName}”吗?`,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
const ids = item ? [item.scheduleId] : this.selectedIds
|
||||||
|
delProjectSchedule(ids).then(() => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
this.getList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleExport() {
|
||||||
|
// 导出逻辑可根据实际API实现
|
||||||
|
uni.showToast({
|
||||||
|
title: '导出功能开发中',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitForm() {
|
||||||
|
if (this.form.scheduleId) {
|
||||||
|
updateProjectSchedule(this.form).then(() => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '修改成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
this.closePopup()
|
||||||
|
this.getList()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
addProjectSchedule(this.form).then(() => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '新增成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
this.closePopup()
|
||||||
|
this.getList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closePopup() {
|
||||||
|
this.$refs.popupRef.close()
|
||||||
|
},
|
||||||
|
getSwipeOptions(item) {
|
||||||
|
const options = [{
|
||||||
|
text: '删除',
|
||||||
|
style: {
|
||||||
|
backgroundColor: '#fa3534',
|
||||||
|
color: '#fff'
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
swipeActionClick(e, item) {
|
||||||
|
const text = e.content.text;
|
||||||
|
if (text === '删除') {
|
||||||
|
this.handleDelete(item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleFinish(row) {
|
||||||
|
updateRequirements({
|
||||||
|
...row,
|
||||||
|
status: 2
|
||||||
|
}).then(_ => {
|
||||||
|
uni.showToast({
|
||||||
|
title: "采购完成"
|
||||||
|
})
|
||||||
|
this.getList()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
|
.report-schedule {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding-bottom: 120rpx;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
.search-bar {
|
||||||
|
padding: 20rpx;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-type-button-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-type-button {
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 100rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 30rpx;
|
||||||
|
outline: none;
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
margin-left: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-icon {
|
||||||
|
margin-left: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-content {
|
||||||
|
padding: 32rpx 24rpx 24rpx 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-form {
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-form-item {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-btns {
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-btn-primary {
|
||||||
|
flex: 1;
|
||||||
|
background: #2979ff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 16rpx 0;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-btn {
|
||||||
|
flex: 1;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #333;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 16rpx 0;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project {
|
||||||
|
color: #2979ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size: 24rpx;
|
||||||
|
padding: 0 12rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-1 {
|
||||||
|
background: #e0f7fa;
|
||||||
|
color: #009688;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-0,
|
||||||
|
.status-undefined {
|
||||||
|
background: #fffbe6;
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-other {
|
||||||
|
background: #fbeff2;
|
||||||
|
color: #e91e63;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content view {
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
color: #666;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
text-align: center;
|
||||||
|
color: #bbb;
|
||||||
|
margin: 40rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content {
|
||||||
|
padding: 24rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx 16rpx 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-form {
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
max-height: 50vh;
|
||||||
|
min-height: 40vh;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-form-item {
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more-tips {
|
||||||
|
text-align: center;
|
||||||
|
color: #bbb;
|
||||||
|
padding: 24rpx 0 32rpx 0;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-ops {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gantt-toggle-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
padding: 16rpx 0 8rpx 0;
|
||||||
|
background: #fff;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,22 +1,333 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view class="container">
|
||||||
|
<!-- 第一级:tabNode横向滚动Tab -->
|
||||||
</view>
|
<view class="tab-container">
|
||||||
|
<scroll-view
|
||||||
|
class="tab-scroll"
|
||||||
|
scroll-x
|
||||||
|
show-scrollbar="false"
|
||||||
|
:scroll-with-animation="true"
|
||||||
|
>
|
||||||
|
<view class="tab-wrapper">
|
||||||
|
<view
|
||||||
|
v-for="(tab, index) in tabOption"
|
||||||
|
:key="index"
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'tab-active': defaultTabNode === tab.value }"
|
||||||
|
@click="handleTabClick(tab.value)"
|
||||||
|
>
|
||||||
|
<text>{{ tab.label }}</text>
|
||||||
|
<!-- 选中状态显示清除图标 -->
|
||||||
|
<text
|
||||||
|
v-if="defaultTabNode === tab.value"
|
||||||
|
class="clear-icon"
|
||||||
|
@click.stop="clearTab"
|
||||||
|
>×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 第二级:firstLevelNode横向滚动Tab(联动) -->
|
||||||
|
<view class="tab-container second-tab" v-if="filteredFirstLevelOptions.length > 0">
|
||||||
|
<scroll-view
|
||||||
|
class="tab-scroll"
|
||||||
|
scroll-x
|
||||||
|
show-scrollbar="false"
|
||||||
|
:scroll-with-animation="true"
|
||||||
|
:disabled="!defaultTabNode"
|
||||||
|
>
|
||||||
|
<view class="tab-wrapper">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in filteredFirstLevelOptions"
|
||||||
|
:key="index"
|
||||||
|
class="tab-item"
|
||||||
|
:class="{
|
||||||
|
'tab-active': defaultFirstLevelNode === item.value,
|
||||||
|
'tab-disabled': !defaultTabNode
|
||||||
|
}"
|
||||||
|
@click="handleFirstLevelClick(item.value)"
|
||||||
|
>
|
||||||
|
<text>{{ item.label }}</text>
|
||||||
|
<!-- 选中状态显示清除图标 -->
|
||||||
|
<text
|
||||||
|
v-if="defaultFirstLevelNode === item.value"
|
||||||
|
class="clear-icon"
|
||||||
|
@click.stop="clearFirstLevel"
|
||||||
|
>×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 筛选后的列表 -->
|
||||||
|
<view class="list-container">
|
||||||
|
<view class="summary">{{ scheduleSummary }}</view>
|
||||||
|
<view v-if="loading" class="loading">加载中...</view>
|
||||||
|
<view v-else-if="filterList.length === 0" class="empty">暂无数据</view>
|
||||||
|
<view v-else class="list-item" v-for="(item, index) in filterList" :key="item.id || index">
|
||||||
|
<view class="item-name">
|
||||||
|
{{ item.stepName }}
|
||||||
|
<oa-remind-time :expire-date="item.planEnd" :threshold-days="5" :finished="item.status == 2"/>
|
||||||
|
</view>
|
||||||
|
<view class="item-info">
|
||||||
|
状态:{{ item.status === 2 ? '已完成' : item.status === 1 ? '待验收' : '未开始' }}
|
||||||
|
</view>
|
||||||
|
<view class="item-info">
|
||||||
|
负责人:{{ item.nodeHeader || '未指定' }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
import { listPage } from "@/api/oa/projectScheduleStep";
|
||||||
data() {
|
|
||||||
return {
|
export default {
|
||||||
|
data() {
|
||||||
}
|
return {
|
||||||
},
|
projectScheduleStepList: [],
|
||||||
methods: {
|
total: 0,
|
||||||
|
loading: false,
|
||||||
}
|
defaultTabNode: "", // 默认不选中
|
||||||
}
|
defaultFirstLevelNode: "", // 默认不选中
|
||||||
|
filterParams: {
|
||||||
|
onlyMy: false,
|
||||||
|
onlyUnfinished: false
|
||||||
|
},
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
scheduleId: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 第一级Tab选项
|
||||||
|
tabOption() {
|
||||||
|
const seen = new Set();
|
||||||
|
const tabNodes = [];
|
||||||
|
for (const item of this.projectScheduleStepList) {
|
||||||
|
const currentTabNode = item.tabNode;
|
||||||
|
if (currentTabNode && !seen.has(currentTabNode)) {
|
||||||
|
seen.add(currentTabNode);
|
||||||
|
tabNodes.push(currentTabNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tabNodes.map(item => ({ label: item, value: item }));
|
||||||
|
},
|
||||||
|
|
||||||
|
// 第二级原始选项(带关联tabNode)
|
||||||
|
firstLevelOption() {
|
||||||
|
const uniqueMap = {};
|
||||||
|
this.projectScheduleStepList.forEach(item => {
|
||||||
|
if (item.firstLevelNode && item.tabNode) {
|
||||||
|
const uniqueKey = `${item.firstLevelNode}-${item.tabNode}`;
|
||||||
|
uniqueMap[uniqueKey] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Object.values(uniqueMap).map(item => ({
|
||||||
|
label: item.firstLevelNode,
|
||||||
|
value: item.firstLevelNode,
|
||||||
|
tabNode: item.tabNode
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
// 联动过滤后的第二级选项
|
||||||
|
filteredFirstLevelOptions() {
|
||||||
|
if (!this.defaultTabNode) return [];
|
||||||
|
return this.firstLevelOption.filter(option =>
|
||||||
|
option.tabNode === this.defaultTabNode
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 统计信息
|
||||||
|
scheduleSummary() {
|
||||||
|
const totalCount = this.projectScheduleStepList.length;
|
||||||
|
const completedCount = this.projectScheduleStepList.filter(item => item.status === 2).length;
|
||||||
|
const pendingCount = this.projectScheduleStepList.filter(item => item.status === 1).length;
|
||||||
|
return `已完成(${completedCount})+ 待验收(${pendingCount}) / 总节点数(${totalCount})`;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 筛选后的列表
|
||||||
|
filterList() {
|
||||||
|
// const { onlyMy, onlyUnfinished } = this.filterParams;
|
||||||
|
|
||||||
|
let filtered = this.projectScheduleStepList.filter(item => {
|
||||||
|
// 只有选中了才过滤,未选中则不过滤该条件
|
||||||
|
if (this.defaultTabNode && item.tabNode !== this.defaultTabNode) return false;
|
||||||
|
if (this.defaultFirstLevelNode && item.firstLevelNode !== this.defaultFirstLevelNode) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// if (onlyMy) {
|
||||||
|
// filtered = filtered.filter(item => item.nodeHeader === this.$store.getters.nickName);
|
||||||
|
// }
|
||||||
|
// if (onlyUnfinished) {
|
||||||
|
// filtered = filtered.filter(item => item.status !== 2);
|
||||||
|
// }
|
||||||
|
console.log('筛选后的列表', filtered)
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listPage(this.queryParams).then(response => {
|
||||||
|
this.projectScheduleStepList = response.rows || [];
|
||||||
|
this.total = response.total || 0;
|
||||||
|
// 移除自动选中逻辑,保持默认不选中
|
||||||
|
}).catch(() => {
|
||||||
|
this.projectScheduleStepList = [];
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 第一级Tab点击
|
||||||
|
handleTabClick(value) {
|
||||||
|
// 如果点击的是已选中的Tab,不做操作(清除通过图标实现)
|
||||||
|
if (this.defaultTabNode === value) return;
|
||||||
|
this.defaultTabNode = value;
|
||||||
|
this.defaultFirstLevelNode = ""; // 切换Tab时重置第二级
|
||||||
|
},
|
||||||
|
|
||||||
|
// 清除第一级选中状态
|
||||||
|
clearTab() {
|
||||||
|
this.defaultTabNode = "";
|
||||||
|
this.defaultFirstLevelNode = ""; // 同时清空第二级
|
||||||
|
},
|
||||||
|
|
||||||
|
// 第二级Tab点击
|
||||||
|
handleFirstLevelClick(value) {
|
||||||
|
if (!this.defaultTabNode) return;
|
||||||
|
// 如果点击的是已选中的Tab,不做操作(清除通过图标实现)
|
||||||
|
if (this.defaultFirstLevelNode === value) return;
|
||||||
|
this.defaultFirstLevelNode = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 清除第二级选中状态
|
||||||
|
clearFirstLevel() {
|
||||||
|
this.defaultFirstLevelNode = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.queryParams.scheduleId = options.id;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding: 16rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
/* 横向滚动Tab样式 */
|
||||||
|
.tab-container {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 8rpx 0;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.second-tab {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-scroll {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-wrapper {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
padding: 12rpx 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #333;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx; /* 文字和清除图标间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.tab-active {
|
||||||
|
background-color: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.tab-disabled {
|
||||||
|
color: #ccc;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 清除图标样式 */
|
||||||
|
.clear-icon {
|
||||||
|
font-size: 24rpx;
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: rgba(255,255,255,0.3);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表样式 */
|
||||||
|
.list-container {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
padding: 16rpx 0;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading, .empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 60rpx 0;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
padding: 20rpx 0;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-info {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user