diff --git a/api/oa/projectSchedule.js b/api/oa/projectSchedule.js
new file mode 100644
index 0000000..03117a5
--- /dev/null
+++ b/api/oa/projectSchedule.js
@@ -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
+ })
+}
diff --git a/api/oa/projectScheduleStep.js b/api/oa/projectScheduleStep.js
new file mode 100644
index 0000000..d707b95
--- /dev/null
+++ b/api/oa/projectScheduleStep.js
@@ -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
+ })
+}
diff --git a/components/oa/oa-remind-time/index.vue b/components/oa/oa-remind-time/index.vue
index 0d76dde..3dc9cb5 100644
--- a/components/oa/oa-remind-time/index.vue
+++ b/components/oa/oa-remind-time/index.vue
@@ -1,10 +1,5 @@
-
{{ displayText }}
@@ -13,53 +8,83 @@
export default {
name: 'RemainingTime',
props: {
- // 截至日期(ISO格式,如:'2025-12-31T23:59:59')
+ // 截至日期(支持多种格式或null)
expireDate: {
- type: String,
- required: true,
+ type: [String, null], // 允许字符串或null
+ default: null, // 默认null
validator(value) {
- // 验证日期格式有效性
+ // 允许null,非null时检查是否能解析为有效日期
+ if (value === null) return true;
return !isNaN(Date.parse(value));
}
},
- // 阈值天数(超过此值为绿色,否则红色)
+ // 阈值天数
thresholdDays: {
type: Number,
- required: true,
- default: 3,
+ default: 3,
validator(value) {
return value >= 0;
}
+ },
+ // 是否已完成
+ finished: {
+ type: Boolean,
+ default: false
}
},
computed: {
- // 计算剩余天数(负数表示逾期)
+ // 计算剩余天数(处理null和无效日期)
remainingDays() {
+ // 如果无截止日期或日期无效,返回null
+ if (!this.expireDate) return null;
const now = new Date();
const expire = new Date(this.expireDate);
+ // 无效日期处理
+ if (isNaN(expire.getTime())) return null;
const diffTime = expire - now;
- // 转换为天数并四舍五入
return Math.round(diffTime / (1000 * 60 * 60 * 24));
},
- // 是否已逾期
+ // 是否已逾期(仅在有有效日期时判断)
isExpired() {
- return this.remainingDays < 0;
+ return this.remainingDays !== null && this.remainingDays < 0;
},
- // 显示文本
+ // 显示文本(优先级:完成 > 无日期 > 逾期 > 剩余时间)
displayText() {
+ if (this.finished) {
+ return '已完成';
+ }
+ // 无截止日期的情况
+ if (this.remainingDays === null) {
+ return '无截止日期';
+ }
if (this.isExpired) {
return `已逾期 ${Math.abs(this.remainingDays)} 天`;
}
return `剩余 ${this.remainingDays} 天`;
},
- // 计算样式类
- computedClass() {
- if (this.isExpired) {
- return 'expired'; // 逾期状态
+ // 图标类型
+ getIconType() {
+ if (this.finished) {
+ return 'checkmark';
}
- return this.remainingDays > this.thresholdDays
- ? 'normal' // 正常状态(超过阈值)
- : 'warning'; // 警告状态(低于阈值)
+ if (this.remainingDays === null) {
+ return 'help'; // 无日期时显示帮助图标
+ }
+ 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;
}
-/* 正常状态(超过阈值) */
+/* 正常状态 */
.normal {
- color: #00b42a; /* 绿色 */
+ color: #00b42a;
}
-/* 警告状态(低于阈值) */
+/* 警告状态 */
.warning {
- color: #f53f3f; /* 红色 */
+ color: #f53f3f;
}
/* 逾期状态 */
.expired {
background-color: #fef0f0;
- color: #f53f3f; /* 红色背景+红色文字 */
+ color: #f53f3f;
+}
+
+/* 完成状态 */
+.finished {
+ background-color: #00b42a;
+ color: #ffffff;
+}
+
+/* 无截止日期状态 */
+.no-date {
+ color: #888888; /* 灰色文字 */
}
\ No newline at end of file
diff --git a/pages/workbench/article/article.vue b/pages/workbench/article/article.vue
index 92bdcc3..7a2e0ba 100644
--- a/pages/workbench/article/article.vue
+++ b/pages/workbench/article/article.vue
@@ -77,12 +77,10 @@
this.loading = true;
switch (type) {
case 'notice':
- getNotice(id)
+ getFeedback(id)
.then(res => {
this.article = {
...res.data,
- title: res.data.noticeTitle,
- content: res.data.noticeContent,
};
uni.setNavigationBarTitle({
title: '通知详情'
@@ -107,8 +105,10 @@
uni.setNavigationBarTitle({
title: '反馈详情'
})
- // 标记为已读
- toRead(this.article);
+ if (this.article.state == 0) {
+ // 标记为已读
+ toRead(this.article);
+ }
} else {
uni.showToast({
title: '未找到消息',
diff --git a/pages/workbench/feedback/feedback.vue b/pages/workbench/feedback/feedback.vue
index fda74e2..ad0b5e5 100644
--- a/pages/workbench/feedback/feedback.vue
+++ b/pages/workbench/feedback/feedback.vue
@@ -70,7 +70,8 @@
title: '',
projectId: '',
startTime: '',
- endTime: ''
+ endTime: '',
+ type: 'feedback'
},
total: 0, // 总条数
loading: false, // 加载状态
diff --git a/pages/workbench/index/index.vue b/pages/workbench/index/index.vue
index cd72cc9..9fb0bbf 100644
--- a/pages/workbench/index/index.vue
+++ b/pages/workbench/index/index.vue
@@ -58,7 +58,7 @@ export default {
text: '任务中心',
icon: '/static/images/task.png',
url: '/pages/workbench/task/task',
- category: "信息中心"
+ category: "项目中心"
},
{
text: '项目排产',
diff --git a/pages/workbench/notice/notice.vue b/pages/workbench/notice/notice.vue
index 4c8ab00..55532f9 100644
--- a/pages/workbench/notice/notice.vue
+++ b/pages/workbench/notice/notice.vue
@@ -8,8 +8,13 @@
-
-
{{ notice.noticeTitle }}
+
+
+ {{ notice.title }}
+
+
+
+
{{ notice.createBy || '未知发布人' }}
{{ notice.createTime || '未知时间' }}
@@ -20,7 +25,7 @@
-
+ .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;
+ }
+
\ No newline at end of file
diff --git a/pages/workbench/project/step.vue b/pages/workbench/project/step.vue
index 8183fd0..d904cd3 100644
--- a/pages/workbench/project/step.vue
+++ b/pages/workbench/project/step.vue
@@ -1,22 +1,333 @@
-
-
-
+
+
+
+
+
+
+ {{ tab.label }}
+
+ ×
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+ ×
+
+
+
+
+
+
+
+ {{ scheduleSummary }}
+ 加载中...
+ 暂无数据
+
+
+ {{ item.stepName }}
+
+
+
+ 状态:{{ item.status === 2 ? '已完成' : item.status === 1 ? '待验收' : '未开始' }}
+
+
+ 负责人:{{ item.nodeHeader || '未指定' }}
+
+
+
+
-
+/* 横向滚动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;
+}
+
\ No newline at end of file