Files
im-uniapp/pages/workbench/project/schedule.vue
砂糖 08029c6406 feat(oa): 新增项目进度和进度步骤跟踪的API接口
添加项目进度管理和进度步骤跟踪的相关API接口,包括列表查询、详情获取、新增、修改、删除等功能
2025-11-07 17:18:33 +08:00

583 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<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>
</template>
<script>
import {
addProjectSchedule,
listProjectSchedule,
updateProjectSchedule,
delProjectSchedule,
getProjectSchedule
} from '@/api/oa/projectSchedule.js'
export default {
data() {
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: {
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>
<style scoped>
.report-schedule {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx;
}
.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>