You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

673 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="direct-payment-container">
<!-- 页面头部 -->
<div class="page-header">
<h1 class="page-title">
<el-icon :size="24"><Lightning /></el-icon>
直接支付无审批流程
</h1>
</div>
<!-- 向导容器 -->
<div class="wizard-container">
<!-- 步骤指示器 -->
<el-steps :active="currentStep - 1" finish-status="success" align-center>
<el-step title="选择类型" />
<el-step title="选择金额" />
<el-step title="上传材料" />
<el-step title="填写信息" />
<el-step title="确认提交" />
</el-steps>
<!-- 步骤1: 选择支付类型 -->
<div v-show="currentStep === 1" class="step-content">
<h2 class="step-title">选择支付类型</h2>
<p class="step-description">请选择本次支付的类型</p>
<div class="option-cards">
<div
v-for="option in paymentTypes"
:key="option.id"
class="option-card"
:class="{ selected: formData.type === option.id }"
@click="selectType(option.id)"
>
<div class="option-card-icon">
<el-icon :size="32"><component :is="getIcon(option.icon)" /></el-icon>
</div>
<div class="option-card-title">{{ option.title }}</div>
</div>
</div>
</div>
<!-- 步骤2: 选择金额 -->
<div v-show="currentStep === 2" class="step-content">
<h2 class="step-title">输入支付金额</h2>
<p class="step-description">请输入本次支付的金额,系统将自动匹配金额范围</p>
<el-alert
v-if="processHint.length > 0"
:title="`所需材料:`"
type="info"
:closable="false"
style="margin-bottom: 20px"
>
<ul class="hint-steps">
<li v-for="(hint, index) in processHint" :key="index">
<el-icon><Check /></el-icon>
{{ hint }}
</li>
</ul>
</el-alert>
<el-form-item label="支付金额" required>
<el-input-number
v-model="formData.inputAmount"
:min="0"
:precision="2"
:step="100"
style="width: 400px"
placeholder="请输入金额"
@change="handleAmountInput"
>
<template #prefix>
<el-icon><Money /></el-icon>
</template>
</el-input-number>
<div class="form-tip">请输入数字金额,系统将自动匹配到对应区间</div>
</el-form-item>
<el-card v-if="amountRangeDisplay.show" class="range-card" shadow="never">
<div class="range-content">
<el-icon :size="32" color="#67c23a"><CircleCheck /></el-icon>
<div class="range-info">
<div class="range-title">{{ amountRangeDisplay.title }}</div>
<div class="range-desc">{{ amountRangeDisplay.desc }}</div>
</div>
</div>
</el-card>
</div>
<!-- 步骤3: 上传材料 -->
<div v-show="currentStep === 3" class="step-content">
<h2 class="step-title">上传所需材料</h2>
<p class="step-description">请根据流程要求上传相应的材料</p>
<el-alert
v-if="uploadRequirements.length > 0"
:title="`需要上传的材料:`"
type="info"
:closable="false"
style="margin-bottom: 20px"
>
<ul class="hint-steps">
<li v-for="(req, index) in uploadRequirements" :key="index">
<el-icon><Document /></el-icon>
{{ req }}
</li>
</ul>
</el-alert>
<el-form :model="uploadForm" label-width="180px">
<el-form-item
v-for="(field, index) in uploadFields"
:key="index"
:label="field.label"
>
<el-upload
v-model:file-list="field.fileList"
:accept="field.accept"
:limit="1"
:auto-upload="false"
>
<el-button type="primary">
<el-icon><Upload /></el-icon>
选择文件
</el-button>
<template #tip>
<div class="el-upload__tip">
支持 {{ field.accept }} 格式
</div>
</template>
</el-upload>
</el-form-item>
</el-form>
</div>
<!-- 步骤4: 填写信息 -->
<div v-show="currentStep === 4" class="step-content">
<h2 class="step-title">填写支付信息</h2>
<p class="step-description">请填写详细的支付信息</p>
<el-form :model="formData" :rules="formRules" ref="paymentFormRef" label-width="120px">
<el-form-item label="基本信息" class="section-title" />
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="支付事项" prop="paymentItem" required>
<el-input v-model="formData.paymentItem" placeholder="请输入支付事项" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="支付金额" prop="paymentAmount" required>
<el-input-number
v-model="formData.paymentAmount"
:min="0"
:precision="2"
style="width: 100%"
placeholder="请输入支付金额"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="收款单位" prop="payee" required>
<el-input v-model="formData.payee" placeholder="请输入收款单位" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="收款账号" prop="accountNumber" required>
<el-input v-model="formData.accountNumber" placeholder="请输入收款账号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="开户银行" prop="bankName" required>
<el-input v-model="formData.bankName" placeholder="请输入开户银行" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="支付方式">
<el-select v-model="formData.paymentMethod" style="width: 100%">
<el-option label="银行转账" value="transfer" />
<el-option label="支票" value="check" />
<el-option label="其他" value="other" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="详细信息" class="section-title" />
<el-form-item label="支付说明" prop="paymentDescription" required>
<el-input
v-model="formData.paymentDescription"
type="textarea"
:rows="4"
placeholder="请输入支付说明"
/>
</el-form-item>
<el-form-item
v-if="formData.amountRange === 'over-30'"
label="招标类型"
>
<el-select v-model="formData.biddingType" style="width: 100%">
<el-option label="公开招标" value="public" />
<el-option label="非公开招标" value="non-public" />
</el-select>
</el-form-item>
</el-form>
</div>
<!-- 步骤5: 确认提交 -->
<div v-show="currentStep === 5" class="step-content">
<h2 class="step-title">确认支付信息</h2>
<p class="step-description">请确认以下信息无误后提交</p>
<el-descriptions title="支付信息" :column="2" border>
<el-descriptions-item label="支付类型">{{ getTypeName(formData.type) }}</el-descriptions-item>
<el-descriptions-item label="金额范围">{{ getAmountRangeName(formData.amountRange) }}</el-descriptions-item>
<el-descriptions-item label="支付事项">{{ formData.paymentItem }}</el-descriptions-item>
<el-descriptions-item label="支付金额">¥{{ formData.paymentAmount }}</el-descriptions-item>
<el-descriptions-item label="收款单位">{{ formData.payee }}</el-descriptions-item>
<el-descriptions-item label="收款账号">{{ formData.accountNumber }}</el-descriptions-item>
<el-descriptions-item label="开户银行">{{ formData.bankName }}</el-descriptions-item>
<el-descriptions-item label="支付方式">{{ getPaymentMethodName(formData.paymentMethod) }}</el-descriptions-item>
<el-descriptions-item label="支付说明" :span="2">{{ formData.paymentDescription }}</el-descriptions-item>
</el-descriptions>
</div>
<!-- 按钮区域 -->
<div class="wizard-actions">
<el-button v-if="currentStep > 1" @click="prevStep">
<el-icon><ArrowLeft /></el-icon>
上一步
</el-button>
<div></div>
<el-button
v-if="currentStep < 5"
type="primary"
@click="nextStep"
>
下一步
<el-icon><ArrowRight /></el-icon>
</el-button>
<el-button
v-if="currentStep === 5"
type="primary"
@click="submitPayment"
>
<el-icon><Check /></el-icon>
提交支付
</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
import {
Lightning,
Money,
Check,
Document,
Upload,
CircleCheck,
ArrowLeft,
ArrowRight,
User,
WalletFilled,
Document as DocIcon
} from '@element-plus/icons-vue'
const currentStep = ref(1)
const paymentFormRef = ref(null)
const formData = ref({
type: '',
amountRange: '',
inputAmount: 0,
biddingType: '',
paymentItem: '',
paymentAmount: 0,
payee: '',
accountNumber: '',
bankName: '',
paymentMethod: 'transfer',
paymentDescription: ''
})
const uploadForm = ref({})
const uploadFields = ref([])
const paymentTypes = [
{ id: 'utilities', title: '水电费、邮电费、食堂经费、体检费', icon: 'Lightning' },
{ id: 'union', title: '工会经费', icon: 'User' },
{ id: 'salary', title: '人员工资、独生子女费', icon: 'WalletFilled' },
{ id: 'children', title: '儿童统筹、幼托费', icon: 'User' },
{ id: 'tax', title: '税费', icon: 'Document' },
{ id: 'contract', title: '历史合同款项', icon: 'Document' }
]
const amountRangeDisplay = ref({
show: false,
title: '',
desc: ''
})
const processHint = computed(() => {
const type = formData.value.type
const range = formData.value.amountRange
if (type === 'utilities' || type === 'contract') {
if (range === 'under-5') {
return ['年度计划', '会议纪要']
} else if (range === '5-10') {
return ['中心主任会会议纪要', '申请', '支付']
} else if (range === '10-30') {
return ['中心主任会会议纪要', '支委会会议纪要', '申请', '支付']
} else if (range === 'over-30') {
return ['中心主任会会议纪要', '支委会会议纪要', '申请', '支付', '政府采购省厅审批表(非公开招标时)']
}
} else if (type === 'union') {
return ['年度计划', '会议纪要']
}
return []
})
const uploadRequirements = computed(() => {
const type = formData.value.type
const range = formData.value.amountRange
if (type === 'utilities' || type === 'contract') {
if (range === 'under-5') {
return ['年度计划', '会议纪要']
} else if (range === '5-10') {
return ['中心主任会会议纪要']
} else if (range === '10-30') {
return ['中心主任会会议纪要', '支委会会议纪要']
} else if (range === 'over-30') {
const reqs = ['中心主任会会议纪要', '支委会会议纪要']
if (formData.value.biddingType === 'non-public') {
reqs.push('政府采购省厅审批表')
}
return reqs
}
} else if (type === 'union') {
return ['年度计划', '会议纪要']
}
return []
})
const formRules = {
paymentItem: [{ required: true, message: '请输入支付事项', trigger: 'blur' }],
paymentAmount: [{ required: true, message: '请输入支付金额', trigger: 'blur' }],
payee: [{ required: true, message: '请输入收款单位', trigger: 'blur' }],
accountNumber: [{ required: true, message: '请输入收款账号', trigger: 'blur' }],
bankName: [{ required: true, message: '请输入开户银行', trigger: 'blur' }],
paymentDescription: [{ required: true, message: '请输入支付说明', trigger: 'blur' }]
}
const getIcon = (iconName) => {
const iconMap = {
Lightning,
User,
WalletFilled,
Document: DocIcon
}
return iconMap[iconName] || DocIcon
}
const getTypeName = (type) => {
const typeMap = {
utilities: '水电费、邮电费、食堂经费、体检费',
union: '工会经费',
salary: '人员工资、独生子女费',
children: '儿童统筹、幼托费',
tax: '税费',
contract: '历史合同款项'
}
return typeMap[type] || '-'
}
const getAmountRangeName = (range) => {
const rangeMap = {
'under-5': '不超过5万',
'5-10': '5-10万不含',
'10-30': '10-30万不含',
'over-30': '30万元以上',
direct: '直接支付'
}
return rangeMap[range] || '-'
}
const getPaymentMethodName = (method) => {
const methodMap = {
transfer: '银行转账',
check: '支票',
other: '其他'
}
return methodMap[method] || '-'
}
const selectType = (type) => {
formData.value.type = type
// 对于直接流程的类型,跳过某些步骤
if (['salary', 'children', 'tax'].includes(type)) {
formData.value.amountRange = 'direct'
setTimeout(() => {
currentStep.value = 3
}, 300)
} else {
setTimeout(() => {
currentStep.value = 2
}, 300)
}
}
const handleAmountInput = () => {
const amount = formData.value.inputAmount
if (!amount || amount <= 0) {
amountRangeDisplay.value.show = false
formData.value.amountRange = ''
return
}
let range = ''
let title = ''
let desc = ''
if (amount <= 50000) {
range = 'under-5'
title = '不超过5万'
desc = '≤ ¥50,000'
} else if (amount > 50000 && amount < 100000) {
range = '5-10'
title = '5-10万不含'
desc = '¥50,000 - ¥100,000'
} else if (amount >= 100000 && amount < 300000) {
range = '10-30'
title = '10-30万不含'
desc = '¥100,000 - ¥300,000'
} else {
range = 'over-30'
title = '30万元以上'
desc = '≥ ¥300,000'
}
amountRangeDisplay.value = {
show: true,
title,
desc
}
formData.value.amountRange = range
formData.value.paymentAmount = amount
}
const updateUploadFields = () => {
const requirements = uploadRequirements.value
uploadFields.value = requirements.map(req => ({
label: req,
accept: '.pdf,.doc,.docx,.xls,.xlsx',
fileList: []
}))
}
watch(() => formData.value.amountRange, () => {
if (currentStep.value === 3) {
updateUploadFields()
}
})
const nextStep = async () => {
if (currentStep.value === 2) {
if (!formData.value.inputAmount || formData.value.inputAmount <= 0) {
ElMessage.warning('请输入有效的支付金额')
return
}
} else if (currentStep.value === 4) {
if (!paymentFormRef.value) return
try {
await paymentFormRef.value.validate()
} catch (error) {
ElMessage.warning('请填写完整的表单信息')
return
}
}
if (currentStep.value === 3) {
updateUploadFields()
}
currentStep.value++
}
const prevStep = () => {
if (currentStep.value > 1) {
currentStep.value--
}
}
const submitPayment = () => {
ElMessage.success('支付申请提交成功!')
// 重置表单
formData.value = {
type: '',
amountRange: '',
inputAmount: 0,
biddingType: '',
paymentItem: '',
paymentAmount: 0,
payee: '',
accountNumber: '',
bankName: '',
paymentMethod: 'transfer',
paymentDescription: ''
}
amountRangeDisplay.value = { show: false, title: '', desc: '' }
currentStep.value = 1
}
</script>
<style scoped>
.direct-payment-container {
padding: 20px;
background: #f5f7fa;
min-height: 100%;
}
.page-header {
background: white;
padding: 25px 30px;
border-radius: 10px;
margin-bottom: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.page-title {
font-size: 24px;
font-weight: 600;
color: #333;
margin: 0;
display: flex;
align-items: center;
gap: 10px;
}
.wizard-container {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.step-content {
margin: 40px 0;
min-height: 400px;
}
.step-title {
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 10px;
}
.step-description {
color: #666;
margin-bottom: 30px;
}
.option-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-top: 30px;
}
.option-card {
padding: 30px;
border: 2px solid #e5e7eb;
border-radius: 10px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.option-card:hover {
border-color: #667eea;
background-color: #f0f0ff;
transform: translateY(-2px);
}
.option-card.selected {
border-color: #667eea;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.option-card-icon {
margin-bottom: 15px;
}
.option-card-title {
font-weight: 500;
font-size: 14px;
}
.hint-steps {
list-style: none;
padding: 0;
margin: 10px 0 0 0;
}
.hint-steps li {
padding: 5px 0;
display: flex;
align-items: center;
gap: 8px;
}
.range-card {
margin-top: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.range-content {
display: flex;
align-items: center;
gap: 15px;
}
.range-info {
flex: 1;
}
.range-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 5px;
}
.range-desc {
font-size: 14px;
opacity: 0.9;
}
.form-tip {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin: 20px 0 15px 0;
padding-bottom: 10px;
border-bottom: 1px solid #e5e7eb;
}
.wizard-actions {
display: flex;
justify-content: space-between;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e5e7eb;
}
</style>