<template>
|
<view class="account-detail">
|
<PageHeader title="审批流程"
|
@back="goBack" />
|
<!-- 表单区域 -->
|
<u-form ref="formRef"
|
@submit="submitForm"
|
:rules="rules"
|
:model="form"
|
label-width="140rpx">
|
<u-form-item prop="approveReason"
|
label="流程编号">
|
<u-input v-model="form.approveId"
|
disabled
|
placeholder="自动编号" />
|
</u-form-item>
|
<u-form-item prop="approveReason"
|
:label="approveType === 5 ? '采购事由' : '申请事由'"
|
required>
|
<u-input v-model="form.approveReason"
|
type="textarea"
|
rows="2"
|
auto-height
|
maxlength="200"
|
:placeholder="approveType === 5 ? '请输入采购事由' : '请输入申请事由'"
|
show-word-limit />
|
</u-form-item>
|
<u-form-item prop="approveDeptName"
|
label="申请部门"
|
required>
|
<u-input v-model="form.approveDeptName"
|
placeholder="请选择申请部门" />
|
<!-- <u-input v-model="form.approveDeptName"
|
readonly
|
placeholder="请选择申请部门"
|
@click="showPicker = true" />
|
<template #right>
|
<up-icon name="arrow-right"
|
@click="showPicker = true"></up-icon>
|
</template> -->
|
</u-form-item>
|
<u-form-item prop="approveUser"
|
label="申请人"
|
required>
|
<u-input v-model="form.approveUserName"
|
placeholder="请输入申请人"
|
readonly />
|
</u-form-item>
|
<u-form-item prop="approveTime"
|
label="申请日期"
|
required>
|
<u-input v-model="form.approveTime"
|
readonly
|
placeholder="请选择"
|
@click="showDatePicker" />
|
<template #right>
|
<up-icon name="arrow-right"
|
@click="showDatePicker"></up-icon>
|
</template>
|
</u-form-item>
|
<!-- approveType=2 请假相关字段 -->
|
<template v-if="approveType === 2">
|
<u-form-item prop="startDate"
|
label="开始时间"
|
required>
|
<u-input v-model="form.startDate"
|
readonly
|
placeholder="请假开始时间"
|
@click="showStartDatePicker" />
|
<template #right>
|
<up-icon name="arrow-right"
|
@click="showStartDatePicker"></up-icon>
|
</template>
|
</u-form-item>
|
<u-form-item prop="endDate"
|
label="结束时间"
|
required>
|
<u-input v-model="form.endDate"
|
readonly
|
placeholder="请假结束时间"
|
@click="showEndDatePicker" />
|
<template #right>
|
<up-icon name="arrow-right"
|
@click="showEndDatePicker"></up-icon>
|
</template>
|
</u-form-item>
|
</template>
|
<!-- approveType=3 出差相关字段 -->
|
<u-form-item v-if="approveType === 3"
|
prop="location"
|
label="出差地点"
|
required>
|
<u-input v-model="form.location"
|
placeholder="请输入出差地点"
|
clearable />
|
</u-form-item>
|
<!-- approveType=4 报销相关字段 -->
|
<u-form-item v-if="approveType === 4"
|
prop="price"
|
label="报销金额"
|
required>
|
<u-input v-model="form.price"
|
type="number"
|
placeholder="请输入报销金额"
|
clearable />
|
</u-form-item>
|
</u-form>
|
<!-- 选择器弹窗 -->
|
<up-action-sheet :show="showPicker"
|
:actions="productOptions"
|
title="选择部门"
|
@select="onConfirm"
|
@close="showPicker = false" />
|
<!-- 日期选择器 -->
|
<up-popup :show="showDate"
|
mode="bottom"
|
@close="showDate = false">
|
<up-datetime-picker :show="true"
|
v-model="currentDate"
|
@confirm="onDateConfirm"
|
@cancel="showDate = false"
|
mode="date" />
|
</up-popup>
|
<!-- 请假开始时间选择器 -->
|
<up-popup :show="showStartDate"
|
mode="bottom"
|
@close="showStartDate = false">
|
<up-datetime-picker :show="true"
|
v-model="startDateValue"
|
@confirm="onStartDateConfirm"
|
@cancel="showStartDate = false"
|
mode="date" />
|
</up-popup>
|
<!-- 请假结束时间选择器 -->
|
<up-popup :show="showEndDate"
|
mode="bottom"
|
@close="showEndDate = false">
|
<up-datetime-picker :show="true"
|
v-model="endDateValue"
|
@confirm="onEndDateConfirm"
|
@cancel="showEndDate = false"
|
mode="date" />
|
</up-popup>
|
<!-- 审核流程区域 -->
|
<view class="approval-process">
|
<view class="approval-header">
|
<text class="approval-title">审核流程</text>
|
<text class="approval-desc">每个步骤只能选择一个审批人</text>
|
</view>
|
<view class="approval-steps">
|
<view v-for="(step, stepIndex) in approverNodes"
|
:key="stepIndex"
|
class="approval-step">
|
<view class="step-dot"></view>
|
<view class="step-title">
|
<text>审批人</text>
|
</view>
|
<view class="approver-container">
|
<view v-if="step.nickName"
|
class="approver-item">
|
<view class="approver-avatar">
|
<text class="avatar-text">{{ step.nickName.charAt(0) }}</text>
|
<view class="status-dot"></view>
|
</view>
|
<view class="approver-info">
|
<text class="approver-name">{{ step.nickName }}</text>
|
</view>
|
<view class="delete-approver-btn"
|
@click="removeApprover(stepIndex)">×</view>
|
</view>
|
<view v-else
|
class="add-approver-btn"
|
@click="addApprover(stepIndex)">
|
<view class="add-circle">+</view>
|
<text class="add-label">选择审批人</text>
|
</view>
|
</view>
|
<view class="step-line"
|
v-if="stepIndex < approverNodes.length - 1"></view>
|
<view class="delete-step-btn"
|
v-if="approverNodes.length > 1"
|
@click="removeApprovalStep(stepIndex)">删除节点</view>
|
</view>
|
</view>
|
<view class="add-step-btn">
|
<u-button icon="plus"
|
plain
|
type="primary"
|
style="width: 100%"
|
@click="addApprovalStep">新增节点</u-button>
|
</view>
|
</view>
|
<!-- 底部按钮 -->
|
<view class="footer-btns">
|
<u-button class="cancel-btn"
|
@click="goBack">取消</u-button>
|
<u-button class="save-btn"
|
@click="submitForm">保存</u-button>
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
import PageHeader from "@/components/PageHeader.vue";
|
import useUserStore from "@/store/modules/user";
|
import { formatDateToYMD } from "@/utils/ruoyi";
|
import {
|
getDept,
|
approveProcessGetInfo,
|
approveProcessAdd,
|
approveProcessUpdate,
|
} from "@/api/collaborativeApproval/approvalProcess";
|
const showToast = message => {
|
uni.showToast({
|
title: message,
|
icon: "none",
|
});
|
};
|
import { userListNoPageByTenantId } from "@/api/system/user";
|
|
const data = reactive({
|
form: {
|
approveTime: "",
|
approveId: "",
|
approveUser: "",
|
approveUserName: "",
|
approveDeptName: "",
|
approveDeptId: "",
|
approveReason: "",
|
checkResult: "",
|
tempFileIds: [],
|
approverList: [], // 新增字段,存储所有节点的审批人id
|
startDate: "",
|
endDate: "",
|
location: "",
|
price: "",
|
},
|
rules: {
|
approveTime: [{ required: false, message: "请输入", trigger: "change" }],
|
approveId: [{ required: false, message: "请输入", trigger: "blur" }],
|
approveDeptId: [{ required: true, message: "请输入", trigger: "blur" }],
|
approveReason: [{ required: true, message: "请输入", trigger: "blur" }],
|
checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
|
startDate: [
|
{ required: false, message: "请选择开始时间", trigger: "change" },
|
],
|
endDate: [
|
{ required: false, message: "请选择结束时间", trigger: "change" },
|
],
|
location: [{ required: false, message: "请输入出差地点", trigger: "blur" }],
|
price: [{ required: false, message: "请输入报销金额", trigger: "blur" }],
|
},
|
});
|
const { form, rules } = toRefs(data);
|
const result = ref("");
|
const showPicker = ref(false);
|
const productOptions = ref([]);
|
const operationType = ref("");
|
const currentApproveStatus = ref("");
|
const approverNodes = ref([]);
|
const userList = ref([]);
|
const formRef = ref(null);
|
const message = ref("");
|
const showDate = ref(false);
|
const currentDate = ref(Date.now());
|
const showStartDate = ref(false);
|
const startDateValue = ref(Date.now());
|
const showEndDate = ref(false);
|
const endDateValue = ref(Date.now());
|
const userStore = useUserStore();
|
const approveType = ref(0);
|
|
const getProductOptions = () => {
|
getDept().then(res => {
|
productOptions.value = res.data.map(item => ({
|
value: item.deptId,
|
name: item.deptName,
|
}));
|
});
|
};
|
const fileList = ref([]);
|
let nextApproverId = 2;
|
const getCurrentinfo = () => {
|
userStore.getInfo().then(res => {
|
form.value.approveDeptId = res.user.tenantId;
|
console.log(res.user.tenantId, "res.user.tenantId");
|
});
|
};
|
onMounted(async () => {
|
try {
|
getProductOptions();
|
userListNoPageByTenantId().then(res => {
|
userList.value = res.data;
|
});
|
form.value.approveUser = userStore.id;
|
form.value.approveUserName = userStore.nickName;
|
form.value.approveTime = getCurrentDate();
|
getCurrentinfo();
|
// 从本地存储获取参数
|
operationType.value = uni.getStorageSync("operationType") || "add";
|
approveType.value = uni.getStorageSync("approveType") || 0;
|
|
// 如果是编辑模式,从本地存储获取数据
|
if (operationType.value === "edit") {
|
const storedData = uni.getStorageSync("invoiceLedgerEditRow");
|
if (storedData) {
|
const row = JSON.parse(storedData);
|
fileList.value = row.commonFileList || [];
|
form.value.tempFileIds = fileList.value.map(file => file.id);
|
currentApproveStatus.value = row.approveStatus;
|
|
approveProcessGetInfo({ id: row.approveId, approveReason: "1" }).then(
|
res => {
|
form.value = { ...res.data };
|
// 反显审批人
|
if (res.data && res.data.approveUserIds) {
|
const userIds = res.data.approveUserIds.split(",");
|
approverNodes.value = userIds.map((userId, idx) => {
|
const userIdNum = parseInt(userId.trim());
|
// 从userList中找到对应的用户信息
|
const userInfo = userList.value.find(
|
user => user.userId === userIdNum
|
);
|
return {
|
id: idx + 1,
|
userId: userIdNum,
|
nickName: userInfo ? userInfo.nickName : null,
|
};
|
});
|
nextApproverId = userIds.length + 1;
|
} else {
|
// 新增模式,初始化一个空的审批节点
|
approverNodes.value = [{ id: 1, userId: null, nickName: null }];
|
nextApproverId = 2;
|
}
|
}
|
);
|
}
|
} else {
|
// 新增模式,初始化一个空的审批节点
|
approverNodes.value = [{ id: 1, userId: null }];
|
}
|
|
// 监听联系人选择事件
|
uni.$on("selectContact", handleSelectContact);
|
} catch (error) {
|
console.error("获取部门数据失败:", error);
|
}
|
});
|
|
onUnmounted(() => {
|
// 移除事件监听
|
uni.$off("selectContact", handleSelectContact);
|
});
|
|
const onConfirm = item => {
|
// 设置选中的部门
|
form.value.approveDeptName = item.name;
|
// 确保设置的是字符串类型的部门ID
|
form.value.approveDeptId = String(item.value || "");
|
console.log("部门选择后的值:", {
|
approveDeptId: form.value.approveDeptId,
|
approveDeptName: form.value.approveDeptName,
|
});
|
showPicker.value = false;
|
};
|
|
const goBack = () => {
|
// 清除本地存储的数据
|
uni.removeStorageSync("operationType");
|
uni.removeStorageSync("invoiceLedgerEditRow");
|
uni.removeStorageSync("approveType");
|
uni.navigateBack();
|
};
|
|
const submitForm = () => {
|
// 检查每个审批步骤是否都有审批人
|
const hasEmptyStep = approverNodes.value.some(step => !step.nickName);
|
if (hasEmptyStep) {
|
showToast("请为每个审批步骤选择审批人");
|
return;
|
}
|
|
// 手动检查必填字段,防止因数据类型问题导致的校验失败
|
if (!form.value.approveReason || !form.value.approveReason.trim()) {
|
showToast("请输入申请事由");
|
return;
|
}
|
|
if (
|
!form.value.approveDeptId ||
|
String(form.value.approveDeptId).trim() === ""
|
) {
|
showToast("请选择申请部门");
|
return;
|
}
|
|
if (!form.value.approveTime) {
|
showToast("请选择申请日期");
|
return;
|
}
|
|
formRef.value
|
.validate()
|
.then(valid => {
|
if (valid) {
|
// 表单校验通过,可以提交数据
|
// 收集所有节点的审批人id
|
console.log("approverNodes---", approverNodes.value);
|
form.value.approveUserIds = approverNodes.value
|
.map(node => node.userId)
|
.join(",");
|
form.value.approveType = approveType.value;
|
form.value.approveDeptId = Number(form.value.approveDeptId);
|
// const submitForm = {
|
// approveDeptId: form.value.approveDeptId,
|
// approveDeptName: form.value.approveDeptName,
|
// approveReason: form.value.approveReason,
|
// approveTime: form.value.approveTime,
|
// approveType: form.value.approveType,
|
// approveUser: form.value.approveUser,
|
// approveUserIds: form.value.approveUserIds,
|
// endDate: form.value.endDate,
|
// startDate: form.value.startDate,
|
// };
|
// console.log("form.value---", form.value);
|
// console.log("submitForm", submitForm);
|
|
if (operationType.value === "add" || currentApproveStatus.value == 3) {
|
approveProcessAdd(form.value).then(res => {
|
showToast("提交成功");
|
goBack();
|
});
|
} else {
|
approveProcessUpdate(form.value).then(res => {
|
showToast("提交成功");
|
goBack();
|
});
|
}
|
}
|
})
|
.catch(error => {
|
console.error("表单校验失败:", error);
|
// 尝试获取具体的错误字段
|
if (error && error.errors) {
|
const firstError = error.errors[0];
|
if (firstError) {
|
uni.showToast({
|
title: firstError.message || "表单校验失败,请检查必填项",
|
icon: "none",
|
});
|
return;
|
}
|
}
|
// 显示通用错误信息
|
uni.showToast({
|
title: "表单校验失败,请检查必填项",
|
icon: "none",
|
});
|
});
|
};
|
|
// 处理联系人选择结果
|
const handleSelectContact = data => {
|
const { stepIndex, contact } = data;
|
// 将选中的联系人设置为对应审批步骤的审批人
|
approverNodes.value[stepIndex].userId = contact.userId;
|
approverNodes.value[stepIndex].nickName = contact.nickName;
|
};
|
|
const addApprover = stepIndex => {
|
// 跳转到联系人选择页面
|
uni.setStorageSync("stepIndex", stepIndex);
|
uni.navigateTo({
|
url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect",
|
});
|
};
|
|
const addApprovalStep = () => {
|
// 添加新的审批步骤
|
approverNodes.value.push({ userId: null, nickName: null });
|
};
|
|
const removeApprover = stepIndex => {
|
// 移除审批人
|
approverNodes.value[stepIndex].userId = null;
|
approverNodes.value[stepIndex].nickName = null;
|
};
|
|
const removeApprovalStep = stepIndex => {
|
// 确保至少保留一个审批步骤
|
if (approverNodes.value.length > 1) {
|
approverNodes.value.splice(stepIndex, 1);
|
} else {
|
uni.showToast({
|
title: "至少需要一个审批步骤",
|
icon: "none",
|
});
|
}
|
};
|
// 显示日期选择器
|
const showDatePicker = () => {
|
showDate.value = true;
|
};
|
|
// 确认日期选择
|
const onDateConfirm = e => {
|
form.value.approveTime = formatDateToYMD(e.value);
|
currentDate.value = formatDateToYMD(e.value);
|
showDate.value = false;
|
};
|
|
// 显示请假开始时间选择器
|
const showStartDatePicker = () => {
|
showStartDate.value = true;
|
};
|
|
// 确认请假开始时间选择
|
const onStartDateConfirm = e => {
|
form.value.startDate = formatDateToYMD(e.value);
|
showStartDate.value = false;
|
};
|
|
const showEndDatePicker = () => {
|
showEndDate.value = true;
|
};
|
|
// 确认请假结束时间选择
|
const onEndDateConfirm = e => {
|
form.value.endDate = formatDateToYMD(e.value);
|
showEndDate.value = false;
|
};
|
|
// 获取当前日期并格式化为 YYYY-MM-DD
|
function getCurrentDate() {
|
const today = new Date();
|
const year = today.getFullYear();
|
const month = String(today.getMonth() + 1).padStart(2, "0"); // 月份从0开始
|
const day = String(today.getDate()).padStart(2, "0");
|
return `${year}-${month}-${day}`;
|
}
|
</script>
|
|
<style scoped lang="scss">
|
@import "@/static/scss/form-common.scss";
|
|
.approval-process {
|
background: #fff;
|
margin: 16px;
|
border-radius: 16px;
|
padding: 16px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
}
|
|
.approval-header {
|
margin-bottom: 16px;
|
}
|
|
.approval-title {
|
font-size: 16px;
|
font-weight: 600;
|
color: #333;
|
display: block;
|
margin-bottom: 4px;
|
}
|
|
.approval-desc {
|
font-size: 12px;
|
color: #999;
|
}
|
|
/* 样式增强为“简洁小圆圈风格” */
|
.approval-steps {
|
padding-left: 22px;
|
position: relative;
|
|
&::before {
|
content: "";
|
position: absolute;
|
left: 11px;
|
top: 40px;
|
bottom: 40px;
|
width: 2px;
|
background: linear-gradient(
|
to bottom,
|
#e6f7ff 0%,
|
#bae7ff 50%,
|
#91d5ff 100%
|
);
|
border-radius: 1px;
|
}
|
}
|
|
.approval-step {
|
position: relative;
|
margin-bottom: 24px;
|
|
&::before {
|
content: "";
|
position: absolute;
|
left: -18px;
|
top: 14px; // 从 8px 调整为 14px,与文字中心对齐
|
width: 12px;
|
height: 12px;
|
background: #fff;
|
border: 3px solid #006cfb;
|
border-radius: 50%;
|
z-index: 2;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
}
|
}
|
|
.step-title {
|
top: 12px;
|
margin-bottom: 12px;
|
position: relative;
|
margin-left: 6px;
|
}
|
|
.step-title text {
|
font-size: 14px;
|
color: #666;
|
background: #f0f0f0;
|
padding: 4px 12px;
|
border-radius: 12px;
|
position: relative;
|
line-height: 1.4; // 确保文字行高一致
|
}
|
|
.approver-item {
|
display: flex;
|
align-items: center;
|
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
border-radius: 16px;
|
padding: 16px;
|
gap: 12px;
|
position: relative;
|
border: 1px solid #e6f7ff;
|
box-shadow: 0 4px 12px rgba(0, 108, 251, 0.08);
|
transition: all 0.3s ease;
|
}
|
|
.approver-avatar {
|
width: 48px;
|
height: 48px;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
position: relative;
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
}
|
|
.avatar-text {
|
color: #fff;
|
font-size: 18px;
|
font-weight: 600;
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
}
|
|
.approver-info {
|
flex: 1;
|
position: relative;
|
}
|
|
.approver-name {
|
display: block;
|
font-size: 16px;
|
color: #333;
|
font-weight: 500;
|
position: relative;
|
}
|
|
.approver-dept {
|
font-size: 12px;
|
color: #999;
|
background: rgba(0, 108, 251, 0.05);
|
padding: 2px 8px;
|
border-radius: 8px;
|
display: inline-block;
|
position: relative;
|
|
&::before {
|
content: "";
|
position: absolute;
|
left: 4px;
|
top: 50%;
|
transform: translateY(-50%);
|
width: 2px;
|
height: 2px;
|
background: #006cfb;
|
border-radius: 50%;
|
}
|
}
|
|
.delete-approver-btn {
|
font-size: 16px;
|
color: #ff4d4f;
|
background: linear-gradient(
|
135deg,
|
rgba(255, 77, 79, 0.1) 0%,
|
rgba(255, 77, 79, 0.05) 100%
|
);
|
width: 28px;
|
height: 28px;
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
transition: all 0.3s ease;
|
position: relative;
|
}
|
|
.add-approver-btn {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%);
|
border: 2px dashed #006cfb;
|
border-radius: 16px;
|
padding: 20px;
|
color: #006cfb;
|
font-size: 14px;
|
position: relative;
|
transition: all 0.3s ease;
|
|
&::before {
|
content: "";
|
position: absolute;
|
left: 50%;
|
top: 50%;
|
transform: translate(-50%, -50%);
|
width: 32px;
|
height: 32px;
|
border: 2px solid #006cfb;
|
border-radius: 50%;
|
opacity: 0;
|
transition: all 0.3s ease;
|
}
|
}
|
|
.delete-step-btn {
|
color: #ff4d4f;
|
font-size: 12px;
|
background: linear-gradient(
|
135deg,
|
rgba(255, 77, 79, 0.1) 0%,
|
rgba(255, 77, 79, 0.05) 100%
|
);
|
padding: 6px 12px;
|
border-radius: 12px;
|
display: inline-block;
|
position: relative;
|
transition: all 0.3s ease;
|
|
&::before {
|
content: "";
|
position: absolute;
|
left: 6px;
|
top: 50%;
|
transform: translateY(-50%);
|
width: 4px;
|
height: 4px;
|
background: #ff4d4f;
|
border-radius: 50%;
|
}
|
}
|
|
.step-line {
|
display: none; // 隐藏原来的线条,使用伪元素代替
|
}
|
|
.add-step-btn {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
.footer-btns {
|
position: fixed;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
background: #fff;
|
display: flex;
|
justify-content: space-around;
|
align-items: center;
|
padding: 0.75rem 0;
|
box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05);
|
z-index: 1000;
|
}
|
|
.cancel-btn {
|
font-weight: 400;
|
font-size: 1rem;
|
color: #ffffff;
|
width: 6.375rem;
|
background: #c7c9cc;
|
box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2);
|
border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
|
}
|
|
.save-btn {
|
font-weight: 400;
|
font-size: 1rem;
|
color: #ffffff;
|
width: 14rem;
|
background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
|
box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2);
|
border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
|
}
|
|
// 动画定义
|
@keyframes pulse {
|
0% {
|
transform: scale(1);
|
opacity: 1;
|
}
|
50% {
|
transform: scale(1.2);
|
opacity: 0.7;
|
}
|
100% {
|
transform: scale(1);
|
opacity: 1;
|
}
|
}
|
|
@keyframes rotate {
|
0% {
|
transform: rotate(0deg);
|
}
|
100% {
|
transform: rotate(360deg);
|
}
|
}
|
|
@keyframes ripple {
|
0% {
|
transform: translate(-50%, -50%) scale(0.8);
|
opacity: 1;
|
}
|
100% {
|
transform: translate(-50%, -50%) scale(1.6);
|
opacity: 0;
|
}
|
}
|
|
/* 如果已有 .step-line,这里更精准定位到左侧与小圆点对齐 */
|
.step-line {
|
position: absolute;
|
left: 4px;
|
top: 48px;
|
width: 2px;
|
height: calc(100% - 48px);
|
background: #e5e7eb;
|
}
|
|
.approver-container {
|
display: flex;
|
align-items: center;
|
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
border-radius: 16px;
|
gap: 12px;
|
padding: 10px 0;
|
background: transparent;
|
border: none;
|
box-shadow: none;
|
}
|
|
.approver-item {
|
display: flex;
|
align-items: center;
|
gap: 12px;
|
padding: 8px 10px;
|
background: transparent;
|
border: none;
|
box-shadow: none;
|
border-radius: 0;
|
}
|
|
.approver-avatar {
|
position: relative;
|
width: 40px;
|
height: 40px;
|
border-radius: 50%;
|
background: #f3f4f6;
|
border: 2px solid #e5e7eb;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
animation: none; /* 禁用旋转等动画,回归简洁 */
|
}
|
|
.avatar-text {
|
font-size: 14px;
|
color: #374151;
|
font-weight: 600;
|
}
|
|
.add-approver-btn {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
background: transparent;
|
border: none;
|
box-shadow: none;
|
padding: 0;
|
}
|
|
.add-approver-btn .add-circle {
|
width: 40px;
|
height: 40px;
|
border: 2px dashed #a0aec0;
|
border-radius: 50%;
|
color: #6b7280;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
font-size: 22px;
|
line-height: 1;
|
}
|
|
.add-approver-btn .add-label {
|
color: #3b82f6;
|
font-size: 14px;
|
}
|
</style>
|