<template>
|
<view class="account-detail">
|
<!-- 顶部标题栏 -->
|
<view class="header">
|
<up-icon name="arrow-left" size="20" color="#333" @click="goBack" />
|
<text class="title">审批流程</text>
|
</view>
|
|
<!-- 表单区域 -->
|
<view class="form-section">
|
<van-form ref="formRef" @submit="submitForm" :rules="rules" input-align="right">
|
<van-cell-group inset style="height:auto">
|
<van-field
|
v-model="taxPrice"
|
name="taxPrice"
|
label="姓名"
|
placeholder="请输入姓名"
|
:rules="[{ required: true, message: '姓名不能为空' }]"
|
required
|
readonly
|
/>
|
<van-field
|
v-model="result"
|
readonly
|
name="picker"
|
label="申请部门"
|
placeholder="请选择申请部门"
|
:rules="[{ required: true, message: '请选择申请部门' }]"
|
@click="showPicker = true"
|
required
|
/>
|
<van-popup
|
v-model:show="showPicker"
|
destroy-on-close
|
position="bottom"
|
>
|
<van-picker
|
:columns="columns"
|
:model-value="pickerValue"
|
@confirm="onConfirm"
|
@cancel="showPicker = false"
|
/>
|
</van-popup>
|
<van-field
|
v-model="message"
|
name="message"
|
rows="1"
|
autosize
|
label="申请事由"
|
type="textarea"
|
placeholder="请输入申请事由"
|
height="100"
|
:rules="[{ required: true, message: '申请事由不能为空' }]"
|
required
|
/>
|
</van-cell-group>
|
</van-form>
|
</view>
|
<!-- 审核流程区域 -->
|
<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 approvalSteps" :key="stepIndex" class="approval-step">
|
<view class="step-title">
|
<text>审批人</text>
|
</view>
|
<view class="approvers-container">
|
<view v-for="(approver, approverIndex) in step.approvers" :key="approverIndex" class="approver-item">
|
<view class="approver-avatar"></view>
|
<text class="approver-name">{{ approver.name }}</text>
|
<view class="delete-approver-btn" @click="removeApprover(stepIndex, approverIndex)">×</view>
|
</view>
|
<view class="add-approver-btn" @click="addApprover(stepIndex)">+
|
</view>
|
</view>
|
<view class="step-line" v-if="stepIndex < approvalSteps.length - 1"></view>
|
<view class="delete-step-btn" @click="removeApprovalStep(stepIndex)">删除节点</view>
|
</view>
|
</view>
|
|
<view class="add-step-btn" @click="addApprovalStep">
|
<text>新增节点审核人</text>
|
</view>
|
</view>
|
|
<!-- 底部按钮 -->
|
<view class="footer-btns">
|
<van-button class="cancel-btn" @click="goBack">取消</van-button>
|
<van-button class="save-btn" @click="submitForm">保存</van-button>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
import { ref, onMounted } from "vue";
|
|
export default {
|
setup() {
|
const rules = ref({
|
|
taxPrice: {
|
rules: [{ required: true, errorMessage: '姓名不能为空' }]
|
},
|
result: {
|
rules: [{ required: true, errorMessage: '请选择申请部门' }]
|
},
|
message: {
|
rules: [{ required: true, errorMessage: '申请事由不能为空' }]
|
},
|
});
|
const result = ref("");
|
const pickerValue = ref([]);
|
const showPicker = ref(false);
|
const columns = ref([]);
|
onMounted(async () => {
|
try {
|
// 替换为实际接口地址
|
// const response = await axios.get('/api/getDepartments');
|
columns.value = [
|
{
|
text: "杭州",
|
value: "Hangzhou",
|
},
|
{
|
text: "宁波",
|
value: "Ningbo",
|
},
|
{
|
text: "温州",
|
value: "Wenzhou",
|
},
|
{
|
text: "绍兴",
|
value: "Shaoxing",
|
},
|
{
|
text: "湖州",
|
value: "Huzhou",
|
},
|
];
|
} catch (error) {
|
console.error("获取部门数据失败:", error);
|
}
|
});
|
const onConfirm = ({ selectedValues, selectedOptions }) => {
|
result.value = selectedOptions[0]?.text;
|
pickerValue.value = selectedValues;
|
showPicker.value = false;
|
};
|
const taxPrice = ref("");
|
const contractAmount = ref("");
|
const approvalSteps = ref([
|
{ approvers: [{ name: '卢小敏' }, { name: '卢小敏' }] },
|
{ approvers: [{ name: '卢小敏' }] },
|
{ approvers: [{ name: '卢小敏' }] },
|
{ approvers: [{ name: '卢小敏' }] }
|
]);
|
|
const goBack = () => {
|
uni.navigateBack();
|
};
|
|
const formRef = ref(null);
|
|
const submitForm = () => {
|
formRef.value.validate().then(() => {
|
// 表单校验通过,可以提交数据
|
console.log("表单数据:", {
|
taxPrice: taxPrice.value,
|
department: result.value,
|
message: message.value,
|
approvalSteps: approvalSteps.value
|
});
|
|
uni.showToast({
|
title: "保存成功",
|
icon: "success",
|
});
|
}).catch((error) => {
|
console.error("表单校验失败:", error);
|
// 显示具体的错误信息
|
if (error.length > 0) {
|
const firstError = error[0];
|
uni.showToast({
|
title: firstError.message || '表单校验失败',
|
icon: 'none'
|
});
|
} else {
|
uni.showToast({
|
title: '表单校验失败,请检查必填项',
|
icon: 'none'
|
});
|
}
|
});
|
};
|
|
const message = ref("");
|
|
const addApprover = (stepIndex) => {
|
// 在指定审批步骤添加新的审批人
|
approvalSteps.value[stepIndex].approvers.push({ name: '卢小敏' });
|
};
|
|
const addApprovalStep = () => {
|
// 添加新的审批步骤
|
approvalSteps.value.push({ approvers: [{ name: '卢小敏' }] });
|
};
|
|
const removeApprover = (stepIndex, approverIndex) => {
|
// 确保每个步骤至少保留一个审批人
|
if (approvalSteps.value[stepIndex].approvers.length > 1) {
|
approvalSteps.value[stepIndex].approvers.splice(approverIndex, 1);
|
} else {
|
uni.showToast({
|
title: '每个步骤至少需要一个审批人',
|
icon: 'none'
|
});
|
}
|
};
|
|
const removeApprovalStep = (stepIndex) => {
|
// 确保至少保留一个审批步骤
|
if (approvalSteps.value.length > 1) {
|
approvalSteps.value.splice(stepIndex, 1);
|
} else {
|
uni.showToast({
|
title: '至少需要一个审批步骤',
|
icon: 'none'
|
});
|
}
|
};
|
|
return {
|
rules,
|
removeApprovalStep,
|
removeApprover,
|
result,
|
pickerValue,
|
columns,
|
onConfirm,
|
showPicker,
|
taxPrice,
|
contractAmount,
|
goBack,
|
submitForm,
|
approvalSteps,
|
addApprover,
|
addApprovalStep,
|
formRef,
|
message
|
};
|
},
|
};
|
</script>
|
|
<style scoped lang="scss">
|
.account-detail {
|
min-height: 100vh;
|
background: #f8f9fa;
|
padding-bottom: 80px;
|
}
|
|
.header {
|
display: flex;
|
align-items: center;
|
background: #fff;
|
padding: 16px 20px;
|
border-bottom: 1px solid #f0f0f0;
|
position: sticky;
|
top: 0;
|
z-index: 100;
|
}
|
|
.title {
|
flex: 1;
|
text-align: center;
|
font-size: 18px;
|
font-weight: 600;
|
color: #333;
|
}
|
|
.form-section {
|
margin-top: 16px;
|
}
|
|
.van-field {
|
height: 56px;
|
line-height: 36px;
|
}
|
|
.product-section {
|
background: #fff;
|
margin: 16px;
|
border-radius: 16px;
|
padding: 20px 16px 8px 16px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
}
|
|
.section-header {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
margin-bottom: 12px;
|
}
|
|
.section-title {
|
font-size: 16px;
|
font-weight: 600;
|
color: #333;
|
}
|
|
.add-btn {
|
background: #2979ff;
|
color: #fff;
|
border-radius: 8px;
|
padding: 4px 16px;
|
font-size: 14px;
|
}
|
|
.product-card {
|
background: #f8f9fa;
|
border-radius: 12px;
|
padding: 12px;
|
margin-bottom: 16px;
|
box-shadow: 0 1px 4px rgba(41, 121, 255, 0.06);
|
position: relative;
|
}
|
|
.product-row {
|
display: flex;
|
align-items: center;
|
margin-bottom: 8px;
|
}
|
|
.product-label {
|
min-width: 60px;
|
color: #888;
|
font-size: 13px;
|
}
|
|
.del-row {
|
justify-content: flex-end;
|
}
|
|
.del-btn {
|
background: #ff4d4f;
|
color: #fff;
|
border-radius: 8px;
|
padding: 4px 16px;
|
font-size: 13px;
|
margin-top: 4px;
|
}
|
|
.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: 16px;
|
position: relative;
|
}
|
|
.approval-step {
|
position: relative;
|
margin-bottom: 20px;
|
}
|
|
.step-title {
|
margin-bottom: 12px;
|
}
|
|
.step-title text {
|
font-size: 14px;
|
color: #666;
|
background: #f0f0f0;
|
padding: 2px 8px;
|
border-radius: 4px;
|
}
|
|
.approvers-container {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 12px;
|
margin-bottom: 8px;
|
}
|
|
.approver-item {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
width: 60px;
|
}
|
|
.approver-avatar {
|
width: 40px;
|
height: 40px;
|
background: #e6f7ff;
|
border-radius: 50%;
|
margin-bottom: 4px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.approver-avatar::after {
|
content: '👤';
|
font-size: 20px;
|
}
|
|
.approver-name {
|
font-size: 12px;
|
color: #333;
|
text-align: center;
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
margin-bottom: 2px;
|
}
|
|
.delete-approver-btn {
|
font-size: 12px;
|
color: #ff4d4f;
|
background: rgba(255, 77, 79, 0.1);
|
width: 16px;
|
height: 16px;
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
margin-top: 2px;
|
}
|
|
.delete-step-btn {
|
margin-top: 8px;
|
color: #ff4d4f;
|
font-size: 12px;
|
background: rgba(255, 77, 79, 0.1);
|
padding: 2px 8px;
|
border-radius: 4px;
|
display: inline-block;
|
}
|
|
.add-approver-btn {
|
width: 40px;
|
height: 40px;
|
border: 1px dashed #ccc;
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
font-size: 20px;
|
color: #999;
|
margin-top: 8px;
|
}
|
|
.step-line {
|
position: absolute;
|
left: 20px;
|
top: 100%;
|
width: 1px;
|
height: 30px;
|
background: #e0e0e0;
|
}
|
|
.add-step-btn {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
margin-top: 16px;
|
color: #006cfb;
|
font-size: 14px;
|
padding: 8px 0;
|
border: 1px dashed #006cfb;
|
border-radius: 8px;
|
}
|
|
.footer-btns {
|
position: fixed;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
background: #fff;
|
display: flex;
|
justify-content: space-around;
|
align-items: center;
|
padding: 12px 0;
|
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
|
z-index: 1000;
|
}
|
|
.cancel-btn {
|
font-weight: 400;
|
font-size: 16px;
|
color: #ffffff;
|
width: 102px;
|
background: #c7c9cc;
|
box-shadow: 0px 4px 10px 0px rgba(3, 88, 185, 0.2);
|
border-radius: 40px 40px 40px 40px;
|
}
|
|
.save-btn {
|
font-weight: 400;
|
font-size: 16px;
|
color: #ffffff;
|
width: 224px;
|
background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
|
box-shadow: 0px 4px 10px 0px rgba(3, 88, 185, 0.2);
|
border-radius: 40px 40px 40px 40px;
|
}
|
</style>
|