“出国申请审批流程已实现10.29”
This commit is contained in:
1 parent
32b9822146
commit
4277e2fca8
15 files changed
+1040
-321
No files matched your search
@@ -157,6 +157,7 @@ export interface ApprovalRequest {
|
||||
applicationId: number | string;
|
||||
approvalStatus: string;
|
||||
approvalOpinion?: string;
|
||||
approvalSignature?: string; // 审批签字(base64图片)
|
||||
taskId?: string; // 🔴 新增:工作流任务ID,支持标准工作流模式
|
||||
}
|
||||
|
||||
@@ -885,6 +886,7 @@ export interface ItineraryChangeApprovalRequest {
|
||||
changeId: number | string;
|
||||
approved: boolean; // true=通过,false=拒绝
|
||||
opinion?: string; // 审批意见
|
||||
signature?: string; // 审批签字(base64图片)
|
||||
}
|
||||
|
||||
// 查询行程变更列表
|
||||
@@ -1030,3 +1032,54 @@ export function getHrApprovers(applicantId: string | number): Promise<{ data: nu
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 出国申请进度查询(监控)相关API ====================
|
||||
|
||||
// 审批监控查询参数类型
|
||||
export interface OverseasMonitorQuery {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
applicantName?: string;
|
||||
department?: string;
|
||||
destination?: string;
|
||||
approvalLevel?: string;
|
||||
currentApprover?: string;
|
||||
status?: string;
|
||||
applyTimeStart?: string;
|
||||
applyTimeEnd?: string;
|
||||
}
|
||||
|
||||
// 查询待审批列表(监控)
|
||||
export function getPendingList(query: OverseasMonitorQuery) {
|
||||
return request({
|
||||
url: '/rs/overseas/monitor/pending/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询已完成审批列表(监控)
|
||||
export function getCompletedList(query: OverseasMonitorQuery) {
|
||||
return request({
|
||||
url: '/rs/overseas/monitor/completed/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询申请详情(监控)
|
||||
export function getDetail(applicationId: string | number) {
|
||||
return request({
|
||||
url: `/rs/overseas/monitor/detail/${applicationId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 下载审批表PDF
|
||||
export function downloadApprovalPdf(applicationId: string | number) {
|
||||
return request({
|
||||
url: `/rs/overseas/monitor/downloadApprovalPdf/${applicationId}`,
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
@@ -1,278 +1,247 @@
|
||||
<template>
|
||||
<div class="signature-pad-wrapper">
|
||||
<div class="signature-header">
|
||||
<span class="signature-title">手写签名</span>
|
||||
<el-button v-if="hasSignature" size="small" type="danger" :icon="Delete" @click="handleClear">清空</el-button>
|
||||
</div>
|
||||
<div class="signature-canvas-container">
|
||||
<div class="signature-pad-container">
|
||||
<div class="signature-canvas-wrapper">
|
||||
<canvas
|
||||
ref="canvasRef"
|
||||
class="signature-canvas"
|
||||
@mousedown="handleMouseDown"
|
||||
@mousemove="handleMouseMove"
|
||||
@mouseup="handleMouseUp"
|
||||
@mouseleave="handleMouseUp"
|
||||
@touchstart="handleTouchStart"
|
||||
@touchmove="handleTouchMove"
|
||||
@touchend="handleTouchEnd"
|
||||
></canvas>
|
||||
<div v-if="!hasSignature" class="signature-placeholder">请在此处签名</div>
|
||||
:width="width"
|
||||
:height="height"
|
||||
@mousedown="startDrawing"
|
||||
@mousemove="draw"
|
||||
@mouseup="stopDrawing"
|
||||
@mouseleave="stopDrawing"
|
||||
@touchstart="handleTouch"
|
||||
@touchmove="handleTouch"
|
||||
@touchend="stopDrawing"
|
||||
/>
|
||||
</div>
|
||||
<div class="signature-buttons">
|
||||
<el-button size="small" @click="clearSignature">清除</el-button>
|
||||
<el-button v-if="showSaveButton" size="small" type="primary" @click="saveSignature">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
||||
import { Delete } from '@element-plus/icons-vue';
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
modelValue?: string; // Base64签名数据
|
||||
modelValue?: string; // base64图片数据
|
||||
width?: number;
|
||||
height?: number;
|
||||
lineWidth?: number;
|
||||
lineColor?: string;
|
||||
showSaveButton?: boolean; // 是否显示保存按钮
|
||||
autoSave?: boolean; // 是否自动保存(绘制时实时保存)
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: '',
|
||||
width: 500,
|
||||
height: 200,
|
||||
lineWidth: 2,
|
||||
lineColor: '#000000'
|
||||
width: 400,
|
||||
height: 150,
|
||||
showSaveButton: true,
|
||||
autoSave: true
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string): void;
|
||||
'update:modelValue': [value: string];
|
||||
'save': [value: string];
|
||||
}>();
|
||||
|
||||
// Refs
|
||||
const canvasRef = ref<HTMLCanvasElement>();
|
||||
const hasSignature = ref(false);
|
||||
const isDrawing = ref(false);
|
||||
let ctx: CanvasRenderingContext2D | null = null;
|
||||
let resizeObserver: ResizeObserver | null = null;
|
||||
const lastPoint = ref({ x: 0, y: 0 });
|
||||
|
||||
onMounted(() => {
|
||||
initCanvas();
|
||||
// 如果有初始值,加载到画布上
|
||||
if (props.modelValue) {
|
||||
loadSignature(props.modelValue);
|
||||
// Methods
|
||||
const startDrawing = (event: MouseEvent) => {
|
||||
if (!canvasRef.value) return;
|
||||
|
||||
isDrawing.value = true;
|
||||
const rect = canvasRef.value.getBoundingClientRect();
|
||||
lastPoint.value = {
|
||||
x: event.clientX - rect.left,
|
||||
y: event.clientY - rect.top
|
||||
};
|
||||
};
|
||||
|
||||
const draw = (event: MouseEvent) => {
|
||||
if (!isDrawing.value || !canvasRef.value) return;
|
||||
|
||||
const ctx = canvasRef.value.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const rect = canvasRef.value.getBoundingClientRect();
|
||||
const currentPoint = {
|
||||
x: event.clientX - rect.left,
|
||||
y: event.clientY - rect.top
|
||||
};
|
||||
|
||||
// 绘制线条
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lastPoint.value.x, lastPoint.value.y);
|
||||
ctx.lineTo(currentPoint.x, currentPoint.y);
|
||||
ctx.strokeStyle = '#000';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.stroke();
|
||||
|
||||
lastPoint.value = currentPoint;
|
||||
|
||||
// 自动保存
|
||||
if (props.autoSave) {
|
||||
saveToModel();
|
||||
}
|
||||
};
|
||||
|
||||
// 监听容器大小变化
|
||||
if (canvasRef.value?.parentElement) {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
handleResize();
|
||||
});
|
||||
resizeObserver.observe(canvasRef.value.parentElement);
|
||||
const stopDrawing = () => {
|
||||
isDrawing.value = false;
|
||||
|
||||
// 如果不是自动保存,在停止绘制时保存
|
||||
if (!props.autoSave) {
|
||||
saveToModel();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
// 触摸事件处理
|
||||
const handleTouch = (event: TouchEvent) => {
|
||||
event.preventDefault();
|
||||
const touch = event.touches[0];
|
||||
if (!touch || !canvasRef.value) return;
|
||||
|
||||
const rect = canvasRef.value.getBoundingClientRect();
|
||||
const mouseEvent = new MouseEvent(
|
||||
event.type === 'touchstart' ? 'mousedown' : 'mousemove',
|
||||
{
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY
|
||||
}
|
||||
);
|
||||
|
||||
if (event.type === 'touchstart') {
|
||||
startDrawing(mouseEvent);
|
||||
} else if (event.type === 'touchmove') {
|
||||
draw(mouseEvent);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 监听外部值变化
|
||||
// 清除签名
|
||||
const clearSignature = () => {
|
||||
if (!canvasRef.value) return;
|
||||
|
||||
const ctx = canvasRef.value.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);
|
||||
emit('update:modelValue', '');
|
||||
};
|
||||
|
||||
// 保存到 v-model
|
||||
const saveToModel = () => {
|
||||
if (!canvasRef.value) return;
|
||||
|
||||
try {
|
||||
const dataURL = canvasRef.value.toDataURL('image/png');
|
||||
emit('update:modelValue', dataURL);
|
||||
} catch (error) {
|
||||
console.error('保存签名失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 手动保存(触发save事件)
|
||||
const saveSignature = () => {
|
||||
if (!canvasRef.value) return;
|
||||
|
||||
try {
|
||||
const dataURL = canvasRef.value.toDataURL('image/png');
|
||||
emit('update:modelValue', dataURL);
|
||||
emit('save', dataURL);
|
||||
ElMessage.success('签名已保存');
|
||||
} catch (error) {
|
||||
console.error('保存签名失败:', error);
|
||||
ElMessage.error('保存签名失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 从base64恢复签名
|
||||
const restoreSignature = (base64Image: string) => {
|
||||
if (!canvasRef.value || !base64Image) return;
|
||||
|
||||
const ctx = canvasRef.value.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
if (!canvasRef.value) return;
|
||||
ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);
|
||||
ctx.drawImage(img, 0, 0);
|
||||
};
|
||||
img.src = base64Image;
|
||||
};
|
||||
|
||||
// 监听 modelValue 变化,恢复签名
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal && !hasSignature.value) {
|
||||
loadSignature(newVal);
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
restoreSignature(newValue);
|
||||
} else {
|
||||
clearSignature();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const initCanvas = () => {
|
||||
if (!canvasRef.value) return;
|
||||
|
||||
const canvas = canvasRef.value;
|
||||
const container = canvas.parentElement;
|
||||
|
||||
// 获取容器的实际宽度
|
||||
const containerWidth = container?.clientWidth || props.width;
|
||||
|
||||
// 设置画布的物理尺寸(实际像素)
|
||||
canvas.width = containerWidth;
|
||||
canvas.height = props.height;
|
||||
|
||||
// 同时设置CSS样式,确保显示尺寸与物理尺寸一致
|
||||
canvas.style.width = `${containerWidth}px`;
|
||||
canvas.style.height = `${props.height}px`;
|
||||
|
||||
ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.strokeStyle = props.lineColor;
|
||||
ctx.lineWidth = props.lineWidth;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.lineJoin = 'round';
|
||||
// 组件挂载时恢复签名
|
||||
onMounted(() => {
|
||||
if (props.modelValue) {
|
||||
restoreSignature(props.modelValue);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 处理容器大小变化
|
||||
const handleResize = () => {
|
||||
if (!canvasRef.value) return;
|
||||
|
||||
// 保存当前签名
|
||||
const currentSignature = hasSignature.value ? canvasRef.value.toDataURL('image/png') : '';
|
||||
|
||||
// 重新初始化画布
|
||||
initCanvas();
|
||||
|
||||
// 恢复签名
|
||||
if (currentSignature && hasSignature.value) {
|
||||
nextTick(() => {
|
||||
loadSignature(currentSignature);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const startDrawing = (x: number, y: number) => {
|
||||
if (!ctx || !canvasRef.value) return;
|
||||
|
||||
isDrawing.value = true;
|
||||
const rect = canvasRef.value.getBoundingClientRect();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - rect.left, y - rect.top);
|
||||
};
|
||||
|
||||
const draw = (x: number, y: number) => {
|
||||
if (!isDrawing.value || !ctx || !canvasRef.value) return;
|
||||
|
||||
const rect = canvasRef.value.getBoundingClientRect();
|
||||
ctx.lineTo(x - rect.left, y - rect.top);
|
||||
ctx.stroke();
|
||||
hasSignature.value = true;
|
||||
};
|
||||
|
||||
const stopDrawing = () => {
|
||||
if (!isDrawing.value) return;
|
||||
|
||||
isDrawing.value = false;
|
||||
if (ctx) {
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
// 导出签名数据
|
||||
if (hasSignature.value && canvasRef.value) {
|
||||
const signatureData = canvasRef.value.toDataURL('image/png');
|
||||
emit('update:modelValue', signatureData);
|
||||
}
|
||||
};
|
||||
|
||||
// 鼠标事件处理
|
||||
const handleMouseDown = (e: MouseEvent) => {
|
||||
startDrawing(e.clientX, e.clientY);
|
||||
};
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
draw(e.clientX, e.clientY);
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
stopDrawing();
|
||||
};
|
||||
|
||||
// 触摸事件处理(移动端支持)
|
||||
const handleTouchStart = (e: TouchEvent) => {
|
||||
e.preventDefault();
|
||||
const touch = e.touches[0];
|
||||
startDrawing(touch.clientX, touch.clientY);
|
||||
};
|
||||
|
||||
const handleTouchMove = (e: TouchEvent) => {
|
||||
e.preventDefault();
|
||||
const touch = e.touches[0];
|
||||
draw(touch.clientX, touch.clientY);
|
||||
};
|
||||
|
||||
const handleTouchEnd = (e: TouchEvent) => {
|
||||
e.preventDefault();
|
||||
stopDrawing();
|
||||
};
|
||||
|
||||
// 清空签名
|
||||
const handleClear = () => {
|
||||
if (!canvasRef.value || !ctx) return;
|
||||
|
||||
ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);
|
||||
hasSignature.value = false;
|
||||
emit('update:modelValue', '');
|
||||
};
|
||||
|
||||
// 加载签名
|
||||
const loadSignature = (base64Data: string) => {
|
||||
if (!canvasRef.value || !ctx) return;
|
||||
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
ctx!.clearRect(0, 0, canvasRef.value!.width, canvasRef.value!.height);
|
||||
ctx!.drawImage(img, 0, 0);
|
||||
hasSignature.value = true;
|
||||
};
|
||||
img.src = base64Data;
|
||||
};
|
||||
|
||||
// 获取签名数据
|
||||
const getSignature = (): string => {
|
||||
if (!canvasRef.value || !hasSignature.value) return '';
|
||||
return canvasRef.value.toDataURL('image/png');
|
||||
};
|
||||
|
||||
// 对外暴露方法
|
||||
// 暴露方法供父组件调用
|
||||
defineExpose({
|
||||
clear: handleClear,
|
||||
getSignature,
|
||||
hasSignature: () => hasSignature.value
|
||||
clearSignature,
|
||||
saveSignature
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.signature-pad-wrapper {
|
||||
.signature-pad-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.signature-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
.signature-canvas-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.signature-title {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.signature-canvas {
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 4px;
|
||||
cursor: crosshair;
|
||||
background-color: #fff;
|
||||
touch-action: none; /* 防止触摸时页面滚动 */
|
||||
}
|
||||
|
||||
.signature-canvas-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background-color: #ffffff;
|
||||
overflow: hidden;
|
||||
.signature-canvas:hover {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #c0c4cc;
|
||||
}
|
||||
|
||||
.signature-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
cursor: crosshair;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.signature-placeholder {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #c0c4cc;
|
||||
font-size: 14px;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
.signature-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@@ -325,11 +325,23 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批对话框 -->
|
||||
<el-dialog :title="approvalDialog.title" v-model="approvalDialog.visible" width="600px" append-to-body>
|
||||
<el-dialog :title="approvalDialog.title" v-model="approvalDialog.visible" width="650px" append-to-body>
|
||||
<el-form ref="approvalFormRef" :model="approvalForm" :rules="approvalRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approvalOpinion">
|
||||
<el-input v-model="approvalForm.approvalOpinion" type="textarea" :rows="4" placeholder="请输入审批意见" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 手写签字 -->
|
||||
<el-form-item label="审批签字" prop="approvalSignature">
|
||||
<SignaturePad
|
||||
v-model="approvalForm.approvalSignature"
|
||||
:width="450"
|
||||
:height="120"
|
||||
:auto-save="true"
|
||||
:show-save-button="false"
|
||||
/>
|
||||
<div class="signature-tip">请在上方签字板中手写签名</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
@@ -360,6 +372,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Document, Warning, Paperclip, View, Download } from '@element-plus/icons-vue';
|
||||
import animate from '@/animate';
|
||||
import request from '@/utils/request';
|
||||
import SignaturePad from '@/components/SignaturePad/index.vue';
|
||||
import { getToken } from '@/utils/auth';
|
||||
|
||||
// 响应式数据
|
||||
@@ -400,7 +413,8 @@ const state = reactive({
|
||||
approvalForm: {
|
||||
applicationId: '',
|
||||
approvalStatus: '1',
|
||||
approvalOpinion: ''
|
||||
approvalOpinion: '',
|
||||
approvalSignature: '' // 审批签字
|
||||
} as ApprovalRequest,
|
||||
approvalRules: {
|
||||
approvalOpinion: [{ required: true, message: '请输入审批意见', trigger: 'blur' }]
|
||||
@@ -550,6 +564,7 @@ const handleApprovalPass = (row: OverseasApplicationVO) => {
|
||||
state.approvalForm.applicationId = row.applicationId;
|
||||
state.approvalForm.approvalStatus = '1';
|
||||
state.approvalForm.approvalOpinion = '';
|
||||
state.approvalForm.approvalSignature = ''; // 清空签字
|
||||
state.approvalDialog.title = '通过';
|
||||
state.approvalDialog.visible = true;
|
||||
};
|
||||
@@ -559,6 +574,7 @@ const handleApprovalReject = (row: OverseasApplicationVO) => {
|
||||
state.approvalForm.applicationId = row.applicationId;
|
||||
state.approvalForm.approvalStatus = '2';
|
||||
state.approvalForm.approvalOpinion = '';
|
||||
state.approvalForm.approvalSignature = ''; // 清空签字
|
||||
state.approvalDialog.title = '拒绝';
|
||||
state.approvalDialog.visible = true;
|
||||
};
|
||||
@@ -586,7 +602,8 @@ const cancelApproval = () => {
|
||||
state.approvalForm = {
|
||||
applicationId: '',
|
||||
approvalStatus: '1',
|
||||
approvalOpinion: ''
|
||||
approvalOpinion: '',
|
||||
approvalSignature: ''
|
||||
};
|
||||
};
|
||||
|
||||
@@ -913,4 +930,12 @@ const handlePreviewStampedFile = async (data: OverseasApplicationVO) => {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 签字样式 */
|
||||
.signature-tip {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -257,7 +257,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审批对话框 -->
|
||||
<el-dialog :title="approvalDialog.title" v-model="approvalDialog.visible" width="600px" append-to-body :close-on-click-modal="false">
|
||||
<el-dialog :title="approvalDialog.title" v-model="approvalDialog.visible" width="650px" append-to-body :close-on-click-modal="false">
|
||||
<el-form ref="approvalFormRef" :model="approvalForm" :rules="approvalRules" label-width="100px">
|
||||
<el-form-item label="审批结果" prop="approved">
|
||||
<el-radio-group v-model="approvalForm.approved">
|
||||
@@ -268,6 +268,18 @@
|
||||
<el-form-item label="审批意见" prop="opinion">
|
||||
<el-input v-model="approvalForm.opinion" type="textarea" :rows="4" placeholder="请输入审批意见" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 手写签字 -->
|
||||
<el-form-item label="审批签字">
|
||||
<SignaturePad
|
||||
v-model="approvalForm.signature"
|
||||
:width="450"
|
||||
:height="120"
|
||||
:auto-save="true"
|
||||
:show-save-button="false"
|
||||
/>
|
||||
<div class="signature-tip">请在上方签字板中手写签名</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
@@ -295,6 +307,7 @@ import {
|
||||
type ItineraryChangeVO,
|
||||
type ItineraryChangeApprovalRequest
|
||||
} from '@/api/rs/overseas';
|
||||
import SignaturePad from '@/components/SignaturePad/index.vue';
|
||||
|
||||
// ==================== 响应式数据 ====================
|
||||
|
||||
@@ -346,7 +359,8 @@ const state = reactive({
|
||||
approvalForm: {
|
||||
changeId: 0,
|
||||
approved: true, // true=通过,false=拒绝
|
||||
opinion: ''
|
||||
opinion: '',
|
||||
signature: '' // 审批签字
|
||||
} as ItineraryChangeApprovalRequest,
|
||||
approvalLoading: false,
|
||||
|
||||
@@ -549,4 +563,12 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war
|
||||
.tab-content {
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
/* 签字样式 */
|
||||
.signature-tip {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user