2026-02-05 10:42:50 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view class="approval-detail-page">
|
2026-04-20 15:56:29 +08:00
|
|
|
|
<view class="flow-summary-card" v-if="detailData">
|
|
|
|
|
|
<view class="summary-head">
|
|
|
|
|
|
<text class="summary-title">审批信息</text>
|
|
|
|
|
|
<text class="status-tag" :class="statusClass(detailData.flowStatus)">{{ statusText(detailData.flowStatus) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="summary-grid">
|
|
|
|
|
|
<view class="summary-item">
|
|
|
|
|
|
<text class="label">是否通过</text>
|
|
|
|
|
|
<text class="value">{{ detailData.approved ? '已通过' : (detailData.flowStatus === 'rejected' ? '已驳回' : '待审批') }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="summary-item">
|
|
|
|
|
|
<text class="label">当前状态</text>
|
|
|
|
|
|
<text class="value">{{ statusText(detailData.flowStatus) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="summary-item">
|
|
|
|
|
|
<text class="label">当前节点</text>
|
|
|
|
|
|
<text class="value">{{ detailData.currentNodeName || detailData.currentNodeId || '-' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="summary-item">
|
|
|
|
|
|
<text class="label">当前审批人</text>
|
|
|
|
|
|
<text class="value">{{ assigneeText }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="flow-history-card" v-if="detailData && detailData.actionTimeline && detailData.actionTimeline.length">
|
|
|
|
|
|
<view class="card-title">审批流程</view>
|
|
|
|
|
|
<view class="timeline-item" v-for="(item, index) in detailData.actionTimeline" :key="item.actionId || index">
|
|
|
|
|
|
<view class="timeline-dot" :class="item.action"></view>
|
|
|
|
|
|
<view class="timeline-line" v-if="index !== detailData.actionTimeline.length - 1"></view>
|
|
|
|
|
|
<view class="timeline-content">
|
|
|
|
|
|
<view class="timeline-header">
|
|
|
|
|
|
<text class="timeline-name">{{ item.actionUserName || '未知审批人' }}</text>
|
|
|
|
|
|
<text class="timeline-time">{{ formatTime(item.createTime) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="timeline-meta">
|
|
|
|
|
|
<text>节点:{{ item.nodeName || item.nodeId || '-' }}</text>
|
|
|
|
|
|
<text>动作:{{ item.actionText || item.action || '-' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="timeline-meta">
|
|
|
|
|
|
<text>任务状态:{{ statusText(item.taskStatus) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="timeline-remark" v-if="item.remark">意见:{{ item.remark }}</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-02-05 10:42:50 +08:00
|
|
|
|
<component
|
|
|
|
|
|
:is="currentDetailComponent"
|
|
|
|
|
|
:bizId="bizId"
|
|
|
|
|
|
v-if="bizId && bizType"
|
2026-04-22 13:10:58 +08:00
|
|
|
|
@early-end="handleTravelEarlyEnd"
|
2026-02-05 10:42:50 +08:00
|
|
|
|
></component>
|
|
|
|
|
|
|
2026-04-22 13:10:58 +08:00
|
|
|
|
<view class="detail-action-bar" v-if="canTravelEarlyEnd">
|
|
|
|
|
|
<button class="btn early-end-btn" @click="handleTravelEarlyEnd">提前结束</button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="seal-inline-card" v-if="canSealApprove">
|
|
|
|
|
|
<view class="seal-inline-head">
|
|
|
|
|
|
<text class="seal-inline-title">用印签章审批</text>
|
|
|
|
|
|
<text class="seal-inline-subtitle">当前申请已绑定印章:{{ sealTypeLabel }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="seal-form-row">
|
|
|
|
|
|
<text class="seal-form-label">签章页码</text>
|
|
|
|
|
|
<input class="seal-form-input" type="number" v-model="sealStampForm.pageNo" :placeholder="sealPageHint" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="seal-form-row seal-form-row--info">
|
|
|
|
|
|
<text class="seal-form-label">PDF页数</text>
|
|
|
|
|
|
<text class="seal-form-value">{{ sealPageTotal || '-' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="seal-form-actions">
|
|
|
|
|
|
<button class="btn approve-btn" @click="submitSealStamp">签章通过</button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="approval-btn-bar" v-if="canApprove && bizType !== 'seal'">
|
2026-02-05 10:42:50 +08:00
|
|
|
|
<button class="btn reject-btn" @click="handleReject">驳回</button>
|
|
|
|
|
|
<button class="btn approve-btn" @click="handleApprove">通过</button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import HRMLeaveDetail from '@/components/hrm/detailPanels/leave.vue'
|
|
|
|
|
|
import HRMReimburseDetail from '@/components/hrm/detailPanels/reimburse.vue'
|
|
|
|
|
|
import HRMSealDetail from '@/components/hrm/detailPanels/seal.vue'
|
|
|
|
|
|
import HRMTravelDetail from '@/components/hrm/detailPanels/travel.vue'
|
2026-04-14 09:33:58 +08:00
|
|
|
|
import HRMAppropriationDetail from '@/components/hrm/detailPanels/appropriation.vue'
|
2026-02-05 10:42:50 +08:00
|
|
|
|
import {
|
|
|
|
|
|
approveFlowTask,
|
|
|
|
|
|
rejectFlowTask,
|
2026-04-20 15:56:29 +08:00
|
|
|
|
getFlowTaskDetailByBiz,
|
2026-02-05 10:42:50 +08:00
|
|
|
|
} from '@/api/hrm/flow';
|
2026-04-22 13:10:58 +08:00
|
|
|
|
import { earlyEndTravelReq } from '@/api/hrm/travel';
|
|
|
|
|
|
import { stampSealJava, getSealPdfPages } from '@/api/hrm/seal';
|
2026-02-05 10:42:50 +08:00
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
components: {
|
|
|
|
|
|
HRMLeaveDetail,
|
|
|
|
|
|
HRMReimburseDetail,
|
|
|
|
|
|
HRMSealDetail,
|
2026-04-14 09:33:58 +08:00
|
|
|
|
HRMTravelDetail,
|
|
|
|
|
|
HRMAppropriationDetail
|
2026-02-05 10:42:50 +08:00
|
|
|
|
},
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
bizId: undefined,
|
|
|
|
|
|
bizType: undefined,
|
2026-04-20 15:56:29 +08:00
|
|
|
|
detailData: null,
|
2026-02-05 10:42:50 +08:00
|
|
|
|
bizTypeComponentMap: {
|
2026-04-20 15:56:29 +08:00
|
|
|
|
leave: 'HRMLeaveDetail',
|
|
|
|
|
|
reimburse: 'HRMReimburseDetail',
|
|
|
|
|
|
seal: 'HRMSealDetail',
|
|
|
|
|
|
travel: 'HRMTravelDetail',
|
2026-04-14 09:33:58 +08:00
|
|
|
|
appropriation: 'HRMAppropriationDetail'
|
2026-04-22 13:10:58 +08:00
|
|
|
|
},
|
|
|
|
|
|
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
|
2026-02-05 10:42:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
currentDetailComponent() {
|
|
|
|
|
|
return this.bizTypeComponentMap[this.bizType] || '';
|
|
|
|
|
|
},
|
2026-04-20 15:56:29 +08:00
|
|
|
|
currentTask() {
|
|
|
|
|
|
return this.detailData?.currentTask || null;
|
|
|
|
|
|
},
|
2026-02-05 10:42:50 +08:00
|
|
|
|
canApprove() {
|
2026-04-20 15:56:29 +08:00
|
|
|
|
return this.currentTask && this.currentTask.status === 'pending' &&
|
|
|
|
|
|
(this.currentTask?.assigneeUserName === this.$store.getters.storeOaName
|
|
|
|
|
|
|| this.currentTask?.assigneeUserId === this.$store.getters.storeOaId)
|
2026-02-05 10:42:50 +08:00
|
|
|
|
},
|
2026-04-22 13:10:58 +08:00
|
|
|
|
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;
|
|
|
|
|
|
},
|
2026-04-20 15:56:29 +08:00
|
|
|
|
assigneeText() {
|
|
|
|
|
|
return this.currentTask?.assigneeNickName || this.currentTask?.assigneeUserName || this.currentTask?.assigneeUserId || '-';
|
|
|
|
|
|
}
|
2026-02-05 10:42:50 +08:00
|
|
|
|
},
|
|
|
|
|
|
watch: {
|
|
|
|
|
|
bizId: {
|
2026-04-20 15:56:29 +08:00
|
|
|
|
immediate: true,
|
2026-02-05 10:42:50 +08:00
|
|
|
|
handler(newVal) {
|
|
|
|
|
|
if (!newVal || !this.bizType) return;
|
2026-04-20 15:56:29 +08:00
|
|
|
|
getFlowTaskDetailByBiz(this.bizType, newVal)
|
2026-02-05 10:42:50 +08:00
|
|
|
|
.then(res => {
|
2026-04-20 15:56:29 +08:00
|
|
|
|
this.detailData = res.data || null;
|
2026-02-05 10:42:50 +08:00
|
|
|
|
})
|
|
|
|
|
|
.catch(err => {
|
2026-04-20 15:56:29 +08:00
|
|
|
|
console.error('获取审批详情失败:', err);
|
2026-02-05 10:42:50 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
2026-04-20 15:56:29 +08:00
|
|
|
|
statusText(status) {
|
|
|
|
|
|
const map = {
|
|
|
|
|
|
pending: '审批中',
|
|
|
|
|
|
running: '流转中',
|
|
|
|
|
|
approved: '已通过',
|
|
|
|
|
|
rejected: '已驳回',
|
|
|
|
|
|
reject: '已驳回',
|
|
|
|
|
|
withdraw: '已撤回',
|
2026-04-22 13:10:58 +08:00
|
|
|
|
withdrawn: '已撤回',
|
|
|
|
|
|
finished: '已结束'
|
2026-04-20 15:56:29 +08:00
|
|
|
|
};
|
|
|
|
|
|
return map[status] || status || '-';
|
|
|
|
|
|
},
|
|
|
|
|
|
statusClass(status) {
|
|
|
|
|
|
return status || 'default';
|
|
|
|
|
|
},
|
|
|
|
|
|
formatTime(time) {
|
|
|
|
|
|
if (!time) return '-';
|
|
|
|
|
|
const d = new Date(time);
|
|
|
|
|
|
return Number.isNaN(d.getTime()) ? time : `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
|
|
|
|
|
|
},
|
2026-02-05 10:42:50 +08:00
|
|
|
|
handleApprove() {
|
|
|
|
|
|
if (!this.currentTask?.taskId) {
|
|
|
|
|
|
uni.showToast({ title: '暂无审批任务', icon: 'none' });
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
uni.showModal({
|
|
|
|
|
|
title: '确认通过',
|
|
|
|
|
|
content: '是否确定通过该审批?',
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
|
approveFlowTask(this.currentTask.taskId)
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
uni.showToast({ title: '审批通过成功' });
|
2026-04-20 15:56:29 +08:00
|
|
|
|
setTimeout(() => uni.navigateBack(), 1500);
|
2026-02-05 10:42:50 +08:00
|
|
|
|
})
|
|
|
|
|
|
.catch(err => {
|
|
|
|
|
|
uni.showToast({ title: '审批通过失败', icon: 'none' });
|
|
|
|
|
|
console.error('审批通过失败:', err);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
handleReject() {
|
|
|
|
|
|
if (!this.currentTask?.taskId) {
|
|
|
|
|
|
uni.showToast({ title: '暂无审批任务', icon: 'none' });
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
uni.showModal({
|
|
|
|
|
|
title: '确认驳回',
|
|
|
|
|
|
content: '是否确定驳回该审批?',
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
|
rejectFlowTask(this.currentTask.taskId)
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
uni.showToast({ title: '审批驳回成功' });
|
2026-04-20 15:56:29 +08:00
|
|
|
|
setTimeout(() => uni.navigateBack(), 1500);
|
2026-02-05 10:42:50 +08:00
|
|
|
|
})
|
|
|
|
|
|
.catch(err => {
|
|
|
|
|
|
uni.showToast({ title: '审批驳回失败', icon: 'none' });
|
|
|
|
|
|
console.error('审批驳回失败:', err);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-04-22 13:10:58 +08:00
|
|
|
|
},
|
|
|
|
|
|
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' });
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-02-05 10:42:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
onLoad(options) {
|
|
|
|
|
|
this.bizId = options.bizId || '';
|
|
|
|
|
|
this.bizType = options.bizType || '';
|
|
|
|
|
|
if (!this.bizId || !this.bizType) {
|
|
|
|
|
|
uni.showToast({ title: '参数缺失,无法加载详情', icon: 'none' });
|
|
|
|
|
|
}
|
2026-04-22 13:10:58 +08:00
|
|
|
|
if (this.bizType === 'seal') {
|
|
|
|
|
|
this.fetchSealPageTotal();
|
|
|
|
|
|
}
|
2026-02-05 10:42:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.approval-detail-page {
|
|
|
|
|
|
min-height: 100vh;
|
2026-04-20 15:56:29 +08:00
|
|
|
|
padding: 24rpx 24rpx 140rpx;
|
|
|
|
|
|
background: #f7f8fa;
|
2026-02-05 10:42:50 +08:00
|
|
|
|
}
|
2026-04-20 15:56:29 +08:00
|
|
|
|
.flow-summary-card, .flow-history-card {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 24rpx;
|
|
|
|
|
|
padding: 24rpx;
|
|
|
|
|
|
margin-bottom: 24rpx;
|
|
|
|
|
|
box-shadow: 0 8rpx 30rpx rgba(0,0,0,0.04);
|
|
|
|
|
|
}
|
|
|
|
|
|
.summary-head {
|
2026-02-05 10:42:50 +08:00
|
|
|
|
display: flex;
|
2026-04-20 15:56:29 +08:00
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 20rpx;
|
2026-02-05 10:42:50 +08:00
|
|
|
|
}
|
2026-04-20 15:56:29 +08:00
|
|
|
|
.summary-title, .card-title {
|
2026-02-05 10:42:50 +08:00
|
|
|
|
font-size: 32rpx;
|
2026-04-20 15:56:29 +08:00
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #1f2937;
|
2026-02-05 10:42:50 +08:00
|
|
|
|
}
|
2026-04-20 15:56:29 +08:00
|
|
|
|
.status-tag {
|
|
|
|
|
|
padding: 8rpx 16rpx;
|
|
|
|
|
|
border-radius: 999rpx;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
background: #eef2ff;
|
|
|
|
|
|
color: #4f46e5;
|
2026-02-05 10:42:50 +08:00
|
|
|
|
}
|
2026-04-20 15:56:29 +08:00
|
|
|
|
.summary-grid {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.summary-item {
|
|
|
|
|
|
width: calc(50% - 8rpx);
|
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
|
padding: 20rpx;
|
2026-02-05 10:42:50 +08:00
|
|
|
|
}
|
2026-04-20 15:56:29 +08:00
|
|
|
|
.label { display:block; color:#6b7280; font-size:24rpx; margin-bottom: 8rpx; }
|
|
|
|
|
|
.value { display:block; color:#111827; font-size:28rpx; font-weight:500; }
|
|
|
|
|
|
.timeline-item { position: relative; display:flex; padding-top: 24rpx; }
|
|
|
|
|
|
.timeline-dot { width: 18rpx; height:18rpx; border-radius:50%; background:#cbd5e1; margin-right: 16rpx; margin-top: 8rpx; flex-shrink:0; }
|
|
|
|
|
|
.timeline-dot.approve, .timeline-dot.approved, .timeline-dot.running { background:#22c55e; }
|
|
|
|
|
|
.timeline-dot.reject, .timeline-dot.rejected { background:#ef4444; }
|
|
|
|
|
|
.timeline-line { position:absolute; left: 8rpx; top: 36rpx; bottom:-4rpx; width:2rpx; background:#e5e7eb; }
|
|
|
|
|
|
.timeline-content { flex:1; padding-bottom: 8rpx; }
|
|
|
|
|
|
.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; }
|
2026-04-22 13:10:58 +08:00
|
|
|
|
.detail-action-bar { display: flex; justify-content: center; margin: 24rpx 0; }
|
2026-04-20 15:56:29 +08:00
|
|
|
|
.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; }
|
2026-04-22 13:10:58 +08:00
|
|
|
|
.early-end-btn { flex: none; min-width: 260rpx; background-color: #007aff; color: #fff; }
|
2026-04-20 15:56:29 +08:00
|
|
|
|
.reject-btn { background-color: #fff; color: #ff4757; border: 1px solid #ff4757; }
|
|
|
|
|
|
.approve-btn { background-color: #007aff; color: #fff; }
|
|
|
|
|
|
</style>
|