f584c37d by lttnew

开票

1 parent daf1a72d
......@@ -205,12 +205,12 @@
<text class="value ">{{ item.orderName || '——' }}</text>
</view>
<text :class="{
'status-wait': item.auditStatus == 0,
'status-pending': item.auditStatus == 1,
'status-success': item.auditStatus == 2,
'status-danger': item.auditStatus == 3
'status-wait': item.payStatus == 3,
'status-pending': item.payStatus == 0,
'status-success': item.payStatus == 1,
'status-danger': item.payStatus == 2
}"
class="status-tag ">{{ getAuditStatusText(item.auditStatus) }}
class="status-tag ">{{ getStatusText(item.payStatus) }}
</text>
</view>
</view>
......@@ -228,7 +228,6 @@
<view class="date">
<view class="data-header">
<text class="value">
<text class="tradeNo">缴费编号:</text>
{{ item.wfCode || '——' }}
</text>
</view>
......@@ -297,7 +296,7 @@
<template v-if="item.payStatus == 1 && item.invoiceStatus != 1&& item.auditStatus == 2 &&item.price>0">
<button :disabled="item.invoiceStatus === 1" class="btn btn-view-invoice"
@click.stop="makeInvoiceFN(item)">
申请票据
申请开票
</button>
</template>
<!-- 已申请票据:查看票据 -->
......@@ -718,12 +717,15 @@ const makeInvoiceFN = (item) => {
needRefresh.value = true;
// 根据tab类型决定跳转页面:个人/单位会员开非税票,级位/段位/越段考试开增值税票
let url = '';
const ziZhangBu = item.ziZhangBu ? '&ziZhangBu=1' : '';
if (currentTab.value === '0' || currentTab.value === '1') {
// 个人/单位会员 - 非税开票
url = `/pages/invoice/applyFeisui?orderId=${item.id}&amount=${item.price}`;
// 如果是對公转账(ziZhangBu有值),只能开企业发票
url = `/pages/invoice/applyFeisui?orderId=${item.id}&amount=${item.price}${ziZhangBu}`;
} else {
// 级位/段位/越段考试 - 增值税开票
url = `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}`;
url = `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}${ziZhangBu}`;
}
uni.navigateTo({ url });
};
......
<template>
<view class="invoice-apply">
<!-- 成功确认弹框 -->
<view class="success-modal" v-if="showSuccessModal">
<view class="success-mask" @click="closeSuccessModal"></view>
<view class="success-content">
<view class="success-icon"></view>
<view class="success-title">发票申请提交成功!</view>
<view class="success-desc">您的发票申请已成功提交,我们将在1-7个工作日内为您处理。</view>
<button class="success-btn" @click="closeSuccessModal">确定</button>
</view>
</view>
<view class="content">
<!-- 票类型 -->
<!-- 票类型 -->
<view class="form-item">
<text class="label">开票类型</text>
<view class="type-select">
<view
:class="{ active: form.type === '1' }"
class="type-option"
@click="changeInvoiceType('1')"
>
<view class="type-icon enterprise"></view>
<view class="type-info">
<text class="type-name">企业单位</text>
<text class="type-desc">可开增值税发票</text>
</view>
</view>
<view
v-if="showPersonalType"
:class="{ active: form.type === '2' }"
class="type-option"
@click="changeInvoiceType('2')"
>
<view class="type-icon"></view>
<view class="type-info">
<text class="type-name">个人/非企业</text>
<text class="type-desc">仅可开普通发票</text>
</view>
</view>
</view>
</view>
<!-- 发票类型(企业才显示两个选项) -->
<view v-if="form.type === '1'" class="form-item">
<text class="label">发票类型</text>
<view class="type-select">
<!-- <view
<view
:class="{ active: form.invoiceType === '2' }"
class="type-option"
@click="form.invoiceType = '2'"
>
<view class="type-icon"></view>
<view class="type-icon"></view>
<view class="type-info">
<text class="type-name">非税</text>
<text class="type-name">普通发</text>
<text class="type-desc">个人/非企业单位</text>
</view>
</view> -->
</view>
<view
:class="{ active: form.invoiceType === '1' }"
class="type-option"
@click="form.invoiceType = '1'"
>
<view class="type-icon enterprise"></view>
<view class="type-icon enterprise"></view>
<view class="type-info">
<text class="type-name">增值税发票</text>
<text class="type-name">增值税专用票据</text>
<text class="type-desc">企业/可抵扣</text>
</view>
</view>
</view>
</view>
<!-- 发票抬头 -->
<view class="form-item column">
<text class="label">发票抬头</text>
<input
v-model="form.name"
class="input"
placeholder="请输入公司全称或个人姓名"
:disabled="!showPersonalType && form.invoiceType === '1'"
:placeholder="form.type === '1' ? '请输入公司全称' : '请输入发票抬头'"
/>
<text class="hint">请确保发票抬头与公司营业执照或个人身份证上的名称一致。</text>
</view>
<!-- 纳税人识别号(企业才显示) -->
<view v-if="form.invoiceType === '1'" class="form-item column">
<view v-if="form.type === '1' && form.invoiceType === '1'" class="form-item column">
<text class="label">纳税人识别号</text>
<input
v-model="form.taxno"
class="input"
:disabled="!showPersonalType && form.invoiceType === '1'"
maxlength="20"
placeholder="请输入纳税人识别号"
/>
<text class="hint">企业税务登记证上的号码,一般为 15、18 或 20 位</text>
</view>
<!-- 接收方式 -->
<view class="form-item">
<text class="label">接收方式</text>
......@@ -67,13 +110,7 @@
</view>
</view>
</view>
<!-- 开票金额 -->
<!-- <view class="form-item">
<text class="label">开票金额</text>
<text class="amount">¥ {{ (Number(form.amount)).toFixed(2) }}</text>
</view> -->
<!-- 接收邮箱 -->
<view class="form-item column">
<text class="label">接收邮箱号码</text>
......@@ -86,7 +123,7 @@
<text class="hint">电子发票将在 3-5 个工作日内发送至该邮箱</text>
</view>
</view>
<!-- 提交按钮 -->
<view class="btn-wrap">
<view :class="{ loading: submitting }" class="submit-btn" @click="handleSubmit">
......@@ -100,36 +137,66 @@
import {ref, reactive} from 'vue';
import {onLoad} from '@dcloudio/uni-app';
import {outputInvoiceNo} from '@/common/api.js';
import customModal from '@/components/custom-modal.vue';
const app = getApp();
const submitting = ref(false);
const type = ref(0) //1个人订单只开普票
// 表单数据(与PC端字段完全对齐)
const showSuccessModal = ref(false);
// 是否显示个人/非企业选项
const showPersonalType = ref(true);
// form.type: '1'=企业单位 '2'=个人/非企业
// form.invoiceType: '1'=增值税专用票据 '2'=普通发票
const form = reactive({
invoiceType: '1', // 1=企业 2=个人
type: '1', // 开票类型:1=企业单位 2=个人/非企业
invoiceType: '1', // 发票类型:1=增值税专用票据 2=普通发票
deliveryMethod: '1', // 接收方式:1=电子发票
name: '', // 发票抬头
taxno: '', // 纳税人识别号
phone: '', // 邮箱
amount: 0, // 金额
id: '' // 订单ID
name: '', // 发票抬头
taxno: '', // 纳税人识别号
phone: '', // 邮箱
amount: 0, // 金额
id: '' // 订单ID
});
// 页面加载(接收PC端传来的参数)
// 切换开票类型
function changeInvoiceType(val) {
form.type = val
if (val === '2') {
// 个人/非企业,只能选普通发票
form.invoiceType = '2'
form.taxno = ''
form.name = ''
}
}
// 页面加载
onLoad((options) => {
if (options.id || options.orderId) {
form.id = options.id || options.orderId;
form.amount = options.amount;
}
if (options.invoiceType) {
form.invoiceType = options.invoiceType;
// 个人订单只开普票,隐藏个人/非企业选项
if (options.ziZhangBu == '1') {
showPersonalType.value = false
form.type = '1'
form.invoiceType = '1'
}
console.log(333,options)
// 自动填充发票抬头和纳税人识别号
const memberInfo = app.globalData.memberInfo
if (memberInfo && options.ziZhangBu == '1') {
form.name = memberInfo.companyName || ''
form.taxno = memberInfo.creditCode || ''
}
type.value = options.type ?? 0
console.log(options)
console.log('增值税开票参数:', options)
});
// 表单验证
const validateForm = () => {
// 发票抬头校验
if (!form.name) {
uni.showToast({title: '请输入发票抬头', icon: 'none'});
return false;
......@@ -138,22 +205,22 @@ const validateForm = () => {
uni.showToast({title: '发票抬头长度在2-100个字符之间', icon: 'none'});
return false;
}
// 企业必须填纳税人识别号
if (form.invoiceType === '1' && !form.taxno) {
if (form.type === '1' && !form.taxno) {
uni.showToast({title: '请输入纳税人识别号', icon: 'none'});
return false;
}
// 纳税人识别号格式校验(同PC)
if (form.invoiceType === '1') {
// 纳税人识别号格式校验
if (form.type === '1') {
const taxReg = /^[A-Z0-9]{15}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$/;
if (!taxReg.test(form.taxno)) {
uni.showToast({title: '纳税人识别号格式不正确', icon: 'none'});
return false;
}
}
// 邮箱校验
if (!form.phone) {
uni.showToast({title: '请输入接收邮箱', icon: 'none'});
......@@ -164,40 +231,40 @@ const validateForm = () => {
uni.showToast({title: '请输入正确的邮箱地址', icon: 'none'});
return false;
}
return true;
};
// 提交发票申请(与PC逻辑完全一致)
// 提交发票申请
const handleSubmit = async () => {
if (submitting.value) return;
if (!validateForm()) return;
submitting.value = true;
console.log('提交表单数据:', form);
try {
await outputInvoiceNo(form);
uni.showToast({
title: '开票申请提交成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
showSuccessModal.value = true;
} catch (error) {
console.log(error)
submitting.value = false;
// 错误已由 request.js 处理
} finally {
submitting.value = false;
}
};
// 关闭弹框并返回
function closeSuccessModal() {
showSuccessModal.value = false;
uni.navigateBack();
}
</script>
<style lang="scss" scoped>
.invoice-apply {
min-height: 100vh;
background: #f5f7fa;
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
}
.content {
......@@ -209,19 +276,19 @@ const handleSubmit = async () => {
padding: 24rpx;
margin-bottom: 20rpx;
border-radius: 16rpx;
&.column {
display: flex;
flex-direction: column;
}
.label {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 16rpx;
}
.input {
width: 100%;
font-size: 28rpx;
......@@ -235,33 +302,26 @@ const handleSubmit = async () => {
border-radius: 8rpx;
height: 80rpx;
line-height: 80rpx;
&::placeholder {
color: #999;
font-size: 28rpx;
line-height: 80rpx;
}
&[disabled] {
background: #f5f5f5;
color: #999;
}
}
.form-item.column .input {
width: 100%;
display: block;
}
.hint {
font-size: 24rpx;
color: #909399;
margin-top: 8rpx;
}
.amount {
font-size: 32rpx;
color: #AD181F;
font-weight: 600;
}
}
/* 票类型选择 */
/* 票类型选择 */
.type-select {
display: flex;
gap: 20rpx;
......@@ -276,46 +336,51 @@ const handleSubmit = async () => {
border: 2rpx solid #e4e7ed;
border-radius: 12rpx;
background: #fafafa;
&.active {
border-color: #AD181F;
background: #FFF5F5;
}
.type-icon {
width: 60rpx;
height: 60rpx;
background: #f5f7ff;
background: #fbd9d9;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
color: #409eff;
color: #AD181F;
font-weight: 600;
margin-right: 16rpx;
&.enterprise {
background: #f0f6ff;
color: #1561CB;
}
// &.enterprise {
// background: #f0f6ff;
// color: #1561CB;
// }
}
&.active .type-icon {
background: #AD181F;
color: #fff;
&.enterprise {
background: #AD181F;
}
}
.type-info {
flex: 1;
display: flex;
flex-direction: column;
.type-name {
font-size: 28rpx;
font-weight: 600;
color: #303133;
}
.type-desc {
font-size: 22rpx;
color: #909399;
......@@ -336,49 +401,49 @@ const handleSubmit = async () => {
border: 2rpx solid #e4e7ed;
border-radius: 12rpx;
background: #fafafa;
&.active {
border-color: #AD181F;
background: #FFF5F5;
}
.method-icon {
width: 60rpx;
height: 60rpx;
background: #f5f7ff;
background: #fbd9d9;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
color: #409eff;
color: #AD181F;
font-weight: 600;
margin-right: 16rpx;
}
&.active .method-icon {
background: #AD181F;
color: #fff;
}
.method-info {
flex: 1;
display: flex;
flex-direction: column;
.method-name {
font-size: 28rpx;
font-weight: 600;
color: #303133;
}
.method-desc {
font-size: 22rpx;
color: #909399;
margin-top: 4rpx;
}
}
.method-tag {
font-size: 20rpx;
color: #67C23A;
......@@ -410,9 +475,89 @@ const handleSubmit = async () => {
font-size: 32rpx;
text-align: center;
font-weight: 500;
&.loading {
background: #c0c4cc;
}
}
</style>
/* 成功弹框 */
.success-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}
.success-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
}
.success-content {
position: relative;
width: 560rpx;
background: #fff;
border-radius: 24rpx;
padding: 50rpx 40rpx 40rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
z-index: 1;
}
.success-icon {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background: #f0f9f0;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 30rpx;
font-size: 48rpx;
color: #52c41a;
}
.success-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
text-align: center;
}
.success-desc {
font-size: 26rpx;
color: #999;
line-height: 1.6;
text-align: center;
margin-bottom: 40rpx;
}
.success-btn {
width: 100%;
height: 80rpx;
line-height: 80rpx;
background: #AD181F;
color: #fff;
font-size: 30rpx;
border-radius: 40rpx;
border: none;
text-align: center;
}
.success-btn::after {
border: none;
}
</style>
\ No newline at end of file
......
<template>
<view class="invoice-apply">
<!-- 成功确认弹框 -->
<view class="success-modal" v-if="showSuccessModal">
<view class="success-mask" @click="closeSuccessModal"></view>
<view class="success-content">
<view class="success-icon"></view>
<view class="success-title">发票申请提交成功!</view>
<view class="success-desc">您的发票申请已成功提交,我们将在1-7个工作日内为您处理。</view>
<button class="success-btn" @click="closeSuccessModal">确定</button>
</view>
</view>
<view class="content">
<!-- 发票信息标题 -->
<view class="section-header">
......@@ -24,6 +35,7 @@
</view>
</view>
<view
v-if="showIndividualType"
:class="{ active: form.type === '1' }"
class="type-option"
@click="form.type = '1'"
......@@ -45,6 +57,7 @@
<input
v-model="form.name"
class="input"
:disabled="!showIndividualType"
:placeholder="form.type === '0' ? '请输入公司全称' : '请输入发票抬头'"
/>
<text class="hint">请确保发票抬头与公司营业执照或个人身份证上的名称一致</text>
......@@ -58,6 +71,7 @@
class="input"
maxlength="20"
placeholder="请输入纳税人识别号"
:disabled="!showIndividualType"
/>
<text class="hint">企业税务登记证上的号码,一般为 15、18 或 20 位</text>
</view>
......@@ -82,8 +96,12 @@
import {ref, reactive} from 'vue';
import {onLoad} from '@dcloudio/uni-app';
import {outputInvoiceNoFeisui} from '@/common/api.js';
import customModal from '@/components/custom-modal.vue';
const app = getApp();
const submitting = ref(false);
const showSuccessModal = ref(false);
const showIndividualType = ref(true);
// 表单数据
const form = reactive({
......@@ -100,6 +118,23 @@ onLoad((options) => {
form.id = options.id || options.orderId;
form.amount = options.amount || 0;
}
// 自动填充发票抬头和纳税人识别号(与PC端一致)
const memberInfo = app.globalData.memberInfo
// 如果是对公转账(ziZhangBu有值),默认只能开企业发票,隐藏个人选项
if (options.ziZhangBu == '1') {
form.type = '0'
showIndividualType.value = false
} else {
showIndividualType.value = true
}
if (memberInfo && options.ziZhangBu == '1') {
form.name = memberInfo.companyName || ''
form.taxno = memberInfo.creditCode || ''
}
console.log('非税开票参数:', options)
});
......@@ -122,7 +157,7 @@ const validateForm = () => {
}
// 纳税人识别号格式校验
if (form.type === '0') {
if (form.type === '0' ) {
const taxReg = /^[A-Z0-9]{15}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$/;
if (!taxReg.test(form.taxno)) {
uni.showToast({title: '纳税人识别号格式不正确', icon: 'none'});
......@@ -142,13 +177,7 @@ const handleSubmit = async () => {
console.log('提交非税开票表单数据:', form);
try {
await outputInvoiceNoFeisui(form);
uni.showToast({
title: '开票申请提交成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
showSuccessModal.value = true;
} catch (error) {
console.log('非税开票失败:', error);
submitting.value = false;
......@@ -156,6 +185,12 @@ const handleSubmit = async () => {
submitting.value = false;
}
};
// 关闭弹框并返回
function closeSuccessModal() {
showSuccessModal.value = false;
uni.navigateBack();
}
</script>
<style lang="scss" scoped>
......@@ -342,4 +377,84 @@ const handleSubmit = async () => {
background: #c0c4cc;
}
}
/* 成功弹框 */
.success-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}
.success-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
}
.success-content {
position: relative;
width: 560rpx;
background: #fff;
border-radius: 24rpx;
padding: 50rpx 40rpx 40rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
z-index: 1;
}
.success-icon {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background: #f0f9f0;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 30rpx;
font-size: 48rpx;
color: #52c41a;
}
.success-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
text-align: center;
}
.success-desc {
font-size: 26rpx;
color: #999;
line-height: 1.6;
text-align: center;
margin-bottom: 40rpx;
}
.success-btn {
width: 100%;
height: 80rpx;
line-height: 80rpx;
background: #AD181F;
color: #fff;
font-size: 30rpx;
border-radius: 40rpx;
border: none;
text-align: center;
}
.success-btn::after {
border: none;
}
</style>
\ No newline at end of file
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!