From 244dc8a056d2c1b2e60121c1ef75b2edc7916eb7 Mon Sep 17 00:00:00 2001
From: 86156 <823267011@qq.com>
Date: Wed, 22 Apr 2026 13:10:58 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=8A=9E=E5=85=AC=EF=BC=8C?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=A1=E6=89=B9=EF=BC=8C=E7=BC=BA=E5=B0=91?=
=?UTF-8?q?=E7=94=B5=E5=AD=90=E7=AD=BE=E7=AB=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
api/hrm/seal.js | 7 ++
components/hrm/RequestForm.vue | 18 +++
components/hrm/detailPanels/travel.vue | 25 ++++
pages/hrm/approve/approve.vue | 165 +++++++++++++++++++------
pages/workbench/hrm/detail/detail.vue | 109 +++++++++++++++-
pages/workbench/hrm/seal/seal.vue | 6 +-
6 files changed, 287 insertions(+), 43 deletions(-)
diff --git a/api/hrm/seal.js b/api/hrm/seal.js
index 28a014a..4252f96 100644
--- a/api/hrm/seal.js
+++ b/api/hrm/seal.js
@@ -16,6 +16,13 @@ export function getSealReq(bizId) {
})
}
+export function getSealPdfPages(bizId) {
+ return request({
+ url: `/hrm/seal/${bizId}/pdfPages`,
+ method: 'get'
+ })
+}
+
export function addSealReq(data) {
return request({
url: '/hrm/seal',
diff --git a/components/hrm/RequestForm.vue b/components/hrm/RequestForm.vue
index 3ebf73f..e90e706 100644
--- a/components/hrm/RequestForm.vue
+++ b/components/hrm/RequestForm.vue
@@ -48,6 +48,14 @@
+
+
+
+ 盖章示例(当前选择)
+
+ 请先选择用印类型
+
+
@@ -246,6 +254,16 @@ export default {
normalizeInputType(inputType) {
return inputType || 'text'
},
+ getSealPreviewSrc(field) {
+ if (!field || field.key !== 'sealType') return ''
+ const value = this.form[field.key]
+ const map = {
+ '山东福安德信息科技有限公司采购部专用章FAD201400201.png': 'http://49.232.154.205:10900/fad-oa/files%2Fstamp%2F山东福安德信息科技有限公司采购部专用章FAD201400201.png',
+ '山东福安德信息科技有限公司采购部专用章FAD201400202.png': 'http://49.232.154.205:10900/fad-oa/files%2Fstamp%2F山东福安德信息科技有限公司采购部专用章FAD201400202.png',
+ '山东福安德信息科技有限公司采购部专用章FAD201400401.png': 'http://49.232.154.205:10900/fad-oa/files%2Fstamp%2F山东福安德信息科技有限公司采购部专用章FAD201400401.png'
+ }
+ return map[value] || ''
+ },
onInputFocus(fieldKey) {
this.currentInputFieldKey = fieldKey
},
diff --git a/components/hrm/detailPanels/travel.vue b/components/hrm/detailPanels/travel.vue
index 534b870..8811a52 100644
--- a/components/hrm/detailPanels/travel.vue
+++ b/components/hrm/detailPanels/travel.vue
@@ -1,6 +1,11 @@
+
+
+ 出差状态
+ {{ travelStatusText }}
+
@@ -66,6 +71,11 @@
申请时间
{{ detail.createTime || '无' }}
+
+
+ 实际结束时间
+ {{ detail.actualEndTime }}
+
@@ -74,6 +84,10 @@
+
+
+
+
@@ -147,6 +161,14 @@
detail: {}
}
},
+ computed: {
+ travelStatusText() {
+ return this.detail.actualEndTime ? '已结束' : '出差中';
+ },
+ canEarlyEnd() {
+ return !this.detail.actualEndTime;
+ }
+ },
watch: {
bizId: {
handler(newVal) {
@@ -175,6 +197,9 @@
},
openPopup() {
this.$refs.popup.open()
+ },
+ handleEarlyEnd() {
+ this.$emit('early-end', this.detail)
}
}
}
diff --git a/pages/hrm/approve/approve.vue b/pages/hrm/approve/approve.vue
index 8168932..c3fc3e7 100644
--- a/pages/hrm/approve/approve.vue
+++ b/pages/hrm/approve/approve.vue
@@ -65,7 +65,7 @@
全部
审批中
- 已通过
+ 出差状态
已驳回
@@ -89,7 +89,7 @@
暂无我的申请
-
+
{{ getBizTypeText(item.bizType) }}
@@ -99,9 +99,9 @@
{{ formatMyApplyInfo(item) }}
{{ formatDate(item.createTime || item.applyTime || item.startTime) }}
-
- {{ myApplyStatusText(item) }}
- 提前结束
+
+ 提前结束
+ {{ myApplyStatusText(item) }}
@@ -126,6 +126,7 @@
import { getUserProfile } from '@/api/oa/user.js';
import { approveFlowTask, listMyFlowInstance, listTodoFlowTask, listDoneFlowTask, rejectFlowTask } from '@/api/hrm/flow';
import { earlyEndTravelReq } from '@/api/hrm/travel';
+import { listCc } from '@/api/hrm/cc';
export default {
name: 'HrmApproval',
@@ -180,9 +181,9 @@ export default {
computed: {
currentList() {
if (this.activeTopTab === 'myApply') return this.myApplyList;
- if (this.activeApprovalTab === 'todo') return this.todoList;
- if (this.activeApprovalTab === 'done') return this.doneList;
- return this.ccList;
+ if (this.activeApprovalTab === 'todo') return this.todoList.filter(item => item.bizType !== 'seal');
+ if (this.activeApprovalTab === 'done') return this.doneList.filter(item => item.bizType !== 'seal');
+ return this.ccList.filter(item => item.bizType !== 'seal');
},
emptyText() {
if (this.activeTopTab === 'myApply') return '暂无我的申请';
@@ -286,8 +287,8 @@ export default {
async loadCcList() {
this.loading = true;
try {
- const res = await listMyFlowInstance({ pageNum: 1, pageSize: 200 });
- let list = (res.rows || res.data || []).filter(item => item.ccFlag === 1 || item.isCc === 1 || item.readFlag === 0);
+ const res = await listCc({ ccUserId: this.currentOaUserId, pageNum: 1, pageSize: 200 });
+ let list = res.rows || res.data || [];
if (this.query.bizType) list = list.filter(item => item.bizType === this.query.bizType);
this.ccList = list;
this.summary.cc = list.length;
@@ -356,17 +357,22 @@ export default {
if (this.myApplyStatusTab === 'rejected') return ['rejected', 'reject'].includes(s);
return true;
},
+ myApplyStatusText(item) {
+ if (item?.bizType === 'travel') {
+ return item?.actualEndTime ? '已结束' : '出差中';
+ }
+ return this.statusText(item?.status);
+ },
+ myApplyStatusClass(item) {
+ if (item?.bizType === 'travel') {
+ return item?.actualEndTime ? 'travel-ended' : 'travel-running';
+ }
+ return this.statusType(item?.status);
+ },
statusText(status) {
const map = { pending: '待审批', draft: '草稿', approved: '已通过', rejected: '已驳回', running: '审批中', finished: '已完成', revoked: '已撤销' };
return map[status] || status || '-';
},
- myApplyStatusText(item) {
- if (item.bizType === 'travel' && (item.status === 'approved' || item.status === 'finished')) {
- const endTime = item.actualEndTime || item.endTime || item.realEndTime;
- return endTime ? '已结束' : '出差中';
- }
- return this.statusText(item.status);
- },
statusType(status) {
const map = { pending: 'warning', running: 'warning', draft: 'info', approved: 'success', rejected: 'danger', finished: 'success', revoked: 'danger' };
return map[status] || 'info';
@@ -408,6 +414,10 @@ export default {
},
goDetail(task) {
if (!task) return;
+ if (task.bizType === 'seal') {
+ uni.showToast({ title: '用印审批暂时关闭', icon: 'none' });
+ return;
+ }
const bizId = task.bizId || task.instId;
const bizType = task.bizType;
if (!bizType || !bizId) {
@@ -426,10 +436,49 @@ export default {
};
uni.navigateTo({ url: map[type] || '/pages/workbench/hrm/apply/apply' });
},
+ canShowMyApplyEarlyEnd(item) {
+ return item?.bizType === 'travel' && item?.actualEndTime == null;
+ },
+ goTravelEarlyEnd(task) {
+ if (!task || task.bizType !== 'travel') {
+ uni.showToast({ title: '仅出差申请支持提前结束', icon: 'none' });
+ return;
+ }
+ const bizId = task.bizId || task.instId;
+ if (!bizId) {
+ uni.showToast({ title: '缺少出差单编号', icon: 'none' });
+ return;
+ }
+ uni.showModal({
+ title: '提前结束出差',
+ content: '确定要提前结束该出差吗?',
+ success: (res) => {
+ if (!res.confirm) return;
+ earlyEndTravelReq(bizId)
+ .then(() => {
+ uni.showToast({ title: '提前结束成功', icon: 'none' });
+ this.refreshCurrentList();
+ })
+ .catch((err) => {
+ console.error('提前结束失败:', err);
+ uni.showToast({ title: '提前结束失败', icon: 'none' });
+ });
+ }
+ });
+ },
goTravelEarlyEnd() {
uni.showToast({ title: '请进入出差详情后发起提前结束', icon: 'none' });
},
+
showActionMenu(task) {
+ if (task?.bizType === 'seal') {
+ this.goDetail(task);
+ return;
+ }
+ if (this.activeApprovalTab === 'cc' || this.activeApprovalTab === 'done') {
+ this.goDetail(task);
+ return;
+ }
if (this.activeApprovalTab !== 'todo') {
this.goDetail(task);
return;
@@ -706,11 +755,68 @@ export default {
.item-tag,
.status-tag {
- padding: 0;
- border-radius: 0;
+ padding: 4rpx 14rpx;
+ border-radius: 999rpx;
font-size: 22rpx;
+ line-height: 1.4;
+ background: #f3f4f6;
color: #6b7280;
- background: transparent !important;
+}
+
+.status-tag.primary {
+ background: rgba(22, 119, 255, 0.12);
+ color: #1677ff;
+}
+
+.status-tag.success {
+ background: rgba(34, 197, 94, 0.12);
+ color: #16a34a;
+}
+
+.status-tag.warning,
+.travel-running {
+ background: rgba(245, 158, 11, 0.12);
+ color: #d97706;
+}
+
+.status-tag.danger {
+ background: rgba(239, 68, 68, 0.12);
+ color: #dc2626;
+}
+
+.status-tag.info,
+.travel-ended {
+ background: #f3f4f6;
+ color: #9ca3af;
+}
+
+.travel-running {
+ color: #f59e0b;
+}
+
+.travel-ended {
+ color: #9ca3af;
+}
+
+.my-apply-actions {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 12rpx;
+ justify-content: flex-end;
+ flex-shrink: 0;
+ margin-left: 12rpx;
+}
+
+.my-apply-early-end {
+ padding: 10rpx 18rpx;
+ border-radius: 999rpx;
+ background: #1677ff;
+ color: #fff;
+ font-size: 22rpx;
+ line-height: 1;
+ white-space: nowrap;
+ box-shadow: 0 6rpx 14rpx rgba(22, 119, 255, 0.22);
}
.item-main {
@@ -732,25 +838,6 @@ export default {
.request-info { font-size: 26rpx; color: #6b7280; }
.time-info { font-size: 24rpx; color: #9ca3af; }
-.early-end-btn {
- flex: 0 0 auto;
- padding: 6rpx 16rpx;
- font-size: 22rpx;
- color: #1677ff;
- background: #eef4ff;
- border-radius: 6rpx;
- border: 1rpx solid #1677ff;
-}
-
-.right-section {
- display: flex;
- flex-direction: column;
- align-items: flex-end;
- justify-content: center;
- gap: 8rpx;
- padding-right: 8rpx;
-}
-
.apply-panel {
padding-top: 16rpx;
}
diff --git a/pages/workbench/hrm/detail/detail.vue b/pages/workbench/hrm/detail/detail.vue
index cef732c..bd82958 100644
--- a/pages/workbench/hrm/detail/detail.vue
+++ b/pages/workbench/hrm/detail/detail.vue
@@ -51,9 +51,32 @@
:is="currentDetailComponent"
:bizId="bizId"
v-if="bizId && bizType"
+ @early-end="handleTravelEarlyEnd"
>
-
+
+
+
+
+
+
+ 用印签章审批
+ 当前申请已绑定印章:{{ sealTypeLabel }}
+
+
+ 签章页码
+
+
+
+ PDF页数
+ {{ sealPageTotal || '-' }}
+
+
+
+
+
+
+
@@ -71,6 +94,8 @@
rejectFlowTask,
getFlowTaskDetailByBiz,
} from '@/api/hrm/flow';
+ import { earlyEndTravelReq } from '@/api/hrm/travel';
+import { stampSealJava, getSealPdfPages } from '@/api/hrm/seal';
export default {
components: {
@@ -91,7 +116,17 @@
seal: 'HRMSealDetail',
travel: 'HRMTravelDetail',
appropriation: 'HRMAppropriationDetail'
- }
+ },
+ sealStampPresets: [
+ { key: 'left-bottom', label: '山东福安德信息科技有限公司采购部专用章FAD201400201.png', preview: 'http://49.232.154.205:10900/fad-oa/files%2Fstamp%2F山东福安德信息科技有限公司采购部专用章FAD201400201.png' },
+ { key: 'center-bottom', label: '山东福安德信息科技有限公司采购部专用章FAD201400202.png', preview: 'http://49.232.154.205:10900/fad-oa/files%2Fstamp%2F山东福安德信息科技有限公司采购部专用章FAD201400202.png' },
+ { key: 'right-bottom', label: '山东福安德信息科技有限公司采购部专用章FAD201400401.png', preview: 'http://49.232.154.205:10900/fad-oa/files%2Fstamp%2F山东福安德信息科技有限公司采购部专用章FAD201400401.png' }
+ ],
+ sealStampForm: {
+ position: 'right-bottom',
+ pageNo: 1
+ },
+ sealPageTotal: 0
}
},
computed: {
@@ -106,6 +141,18 @@
(this.currentTask?.assigneeUserName === this.$store.getters.storeOaName
|| this.currentTask?.assigneeUserId === this.$store.getters.storeOaId)
},
+ canSealApprove() {
+ return this.bizType === 'seal' && this.canApprove;
+ },
+ sealPageHint() {
+ return this.sealPageTotal > 0 ? `1-${this.sealPageTotal}` : '1-1';
+ },
+ sealTypeLabel() {
+ return this.detailData?.sealType || '-';
+ },
+ canTravelEarlyEnd() {
+ return this.bizType === 'travel' && this.detailData?.actualEndTime == null;
+ },
assigneeText() {
return this.currentTask?.assigneeNickName || this.currentTask?.assigneeUserName || this.currentTask?.assigneeUserId || '-';
}
@@ -134,7 +181,8 @@
rejected: '已驳回',
reject: '已驳回',
withdraw: '已撤回',
- withdrawn: '已撤回'
+ withdrawn: '已撤回',
+ finished: '已结束'
};
return map[status] || status || '-';
},
@@ -191,6 +239,56 @@
}
}
});
+ },
+ async fetchSealPageTotal() {
+ try {
+ const res = await getSealPdfPages(this.bizId);
+ this.sealPageTotal = Number(res?.data || 0);
+ } catch (e) {
+ this.sealPageTotal = 0;
+ }
+ },
+ submitSealStamp() {
+ const pageNo = Number(this.sealStampForm.pageNo) || 1;
+ const payload = {
+ pageNo,
+ xPx: 120,
+ yPx: 120,
+ viewportWidth: 750,
+ viewportHeight: 1334
+ };
+ stampSealJava(this.bizId, payload)
+ .then(() => {
+ uni.showToast({ title: '签章成功', icon: 'none' });
+ setTimeout(() => uni.navigateBack(), 1000);
+ })
+ .catch(err => {
+ console.error('签章失败:', err);
+ uni.showToast({ title: '签章失败', icon: 'none' });
+ });
+ },
+ handleTravelEarlyEnd() {
+ if (!this.canTravelEarlyEnd) {
+ uni.showToast({ title: '当前出差已结束', icon: 'none' });
+ return;
+ }
+ const bizId = this.bizId;
+ uni.showModal({
+ title: '提前结束出差',
+ content: '确认提前结束该出差吗?',
+ success: (res) => {
+ if (!res.confirm) return;
+ earlyEndTravelReq(bizId)
+ .then(() => {
+ uni.showToast({ title: '提前结束成功', icon: 'none' });
+ setTimeout(() => uni.navigateBack(), 1200);
+ })
+ .catch(err => {
+ console.error('提前结束失败:', err);
+ uni.showToast({ title: '提前结束失败', icon: 'none' });
+ });
+ }
+ });
}
},
onLoad(options) {
@@ -199,6 +297,9 @@
if (!this.bizId || !this.bizType) {
uni.showToast({ title: '参数缺失,无法加载详情', icon: 'none' });
}
+ if (this.bizType === 'seal') {
+ this.fetchSealPageTotal();
+ }
}
}
@@ -256,8 +357,10 @@
.timeline-header, .timeline-meta { display:flex; justify-content:space-between; gap: 16rpx; font-size:24rpx; color:#6b7280; }
.timeline-name { color:#111827; font-weight:600; }
.timeline-remark { margin-top: 8rpx; color:#374151; font-size:26rpx; }
+.detail-action-bar { display: flex; justify-content: center; margin: 24rpx 0; }
.approval-btn-bar { position: fixed; bottom: 0; left: 0; right: 0; display: flex; padding: 20rpx; background-color: #fff; border-top: 1px solid #eee; z-index: 99; }
.btn { flex: 1; height: 88rpx; line-height: 88rpx; border-radius: 44rpx; font-size: 32rpx; border: none; margin: 0 10rpx; }
+.early-end-btn { flex: none; min-width: 260rpx; background-color: #007aff; color: #fff; }
.reject-btn { background-color: #fff; color: #ff4757; border: 1px solid #ff4757; }
.approve-btn { background-color: #007aff; color: #fff; }
diff --git a/pages/workbench/hrm/seal/seal.vue b/pages/workbench/hrm/seal/seal.vue
index 4e75631..4282316 100644
--- a/pages/workbench/hrm/seal/seal.vue
+++ b/pages/workbench/hrm/seal/seal.vue
@@ -21,7 +21,11 @@ export default {
initialForm: { sealType: '', applyFileIds: '', reason: '', remark: '' },
sections: [
{ key: 'basic', title: '基础信息', fields: [
- { key: 'sealType', label: '用印类型', type: 'select', required: true, placeholder: '请选择用印类型', options: ['合同用印', '公章', '财务章', '法人章', '其他'] },
+ { key: 'sealType', label: '用印类型', type: 'select', required: true, placeholder: '请选择用印类型', options: [
+ '山东福安德信息科技有限公司采购部专用章FAD201400201.png',
+ '山东福安德信息科技有限公司采购部专用章FAD201400202.png',
+ '山东福安德信息科技有限公司采购部专用章FAD201400401.png'
+ ] },
{ key: 'applyFileIds', label: '附件', type: 'file', required: true, placeholder: '上传盖章文件', accept: '.pdf' }
]},
{ key: 'desc', title: '说明', fields: [