a7c5fca8 by lttnew

开票

1 parent 900be9be
......@@ -1821,6 +1821,15 @@ export const outputInvoiceNo = (data) => {
})
}
// 非税开票
export const outputInvoiceNoFeisui = (data) => {
return request({
url: `/common/order/outputInvoiceNoFeisui/${data.id}?taxno=${data.taxno || ''}&name=${data.name}&type=${data.type}`,
method: 'post',
params: data
})
}
export function getAssoPers(perId) {
return request({
url: '/person/info/getRoleListByPerId/' + perId,
......
......@@ -66,11 +66,14 @@ function showError(msg) {
loadingCount = 0
uni.hideLoading()
}
uni.showToast({
title: msg || '请求失败',
icon: 'none',
duration: 3000
})
// 延迟一点显示 Toast,避免在 hideLoading 动画过程中 Toast 被中断
setTimeout(() => {
uni.showToast({
title: msg || '请求失败',
icon: 'none',
duration: 4000
})
}, 50)
}
const request = function (req) {
......
......@@ -174,7 +174,7 @@
<view class="card-header">
<view class="date">
<view class="data-header">
<text class="member-label">{{ tabs[currentTab].label }}·</text>
<text class="member-label">{{ tabs.find(t => t.type === currentTab)?.label }}·</text>
<text class="value ">{{ item.orderName || '——' }}</text>
</view>
<text :class="{
......@@ -271,7 +271,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="makeInvoiceFN(item)">
@click.stop="makeInvoiceFN(item)">
开票
</button>
</template>
......@@ -647,10 +647,16 @@ const makeInvoiceFN = (item) => {
// }
// 设置刷新标记,从开票页面返回时刷新列表
needRefresh.value = true;
// 跳转到开票页面
uni.navigateTo({
url: `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}`
});
// 根据tab类型决定跳转页面:个人/单位会员开非税票,级位/段位/越段考试开增值税票
let url = '';
if (currentTab.value === '0' || currentTab.value === '1') {
// 个人/单位会员 - 非税开票
url = `/pages/invoice/applyFeisui?orderId=${item.id}&amount=${item.price}`;
} else {
// 级位/段位/越段考试 - 增值税开票
url = `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}`;
}
uni.navigateTo({ url });
};
// 查看发票
......
......@@ -36,7 +36,14 @@
{
"path": "pages/invoice/apply",
"style": {
"navigationBarTitleText": "开具发票",
"navigationBarTitleText": "开具增值税发票",
"enablePullDownRefresh": false
}
},
{
"path": "pages/invoice/applyFeisui",
"style": {
"navigationBarTitleText": "开具非税发票",
"enablePullDownRefresh": false
}
},
......
......@@ -5,19 +5,18 @@
<view 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-info">
<text class="type-name">普通发</text>
<text class="type-name">非税</text>
<text class="type-desc">个人/非企业单位</text>
</view>
</view>
</view> -->
<view
v-if="type==0"
:class="{ active: form.invoiceType === '1' }"
class="type-option"
@click="form.invoiceType = '1'"
......@@ -106,7 +105,7 @@ const submitting = ref(false);
const type = ref(0) //1个人订单只开普票
// 表单数据(与PC端字段完全对齐)
const form = reactive({
invoiceType: '2', // 1=企业 2=个人
invoiceType: '1', // 1=企业 2=个人
deliveryMethod: '1', // 接收方式:1=电子发票
name: '', // 发票抬头
taxno: '', // 纳税人识别号
......
<template>
<view class="invoice-apply">
<view class="content">
<!-- 发票信息标题 -->
<view class="section-header">
<text class="section-title">发票信息</text>
<text class="section-hint">请填写开票信息</text>
</view>
<!-- 发票类型 -->
<view class="form-item">
<text class="label">发票类型</text>
<view class="type-select">
<view
:class="{ active: form.type === '1' }"
class="type-option"
@click="form.type = '1'"
>
<view class="type-icon">
<text class="icon-text"></text>
</view>
<view class="type-info">
<text class="type-name">个人</text>
</view>
</view>
<view
:class="{ active: form.type === '0' }"
class="type-option"
@click="form.type = '0'"
>
<view class="type-icon enterprise">
<text class="icon-text"></text>
</view>
<view class="type-info">
<text class="type-name">企业</text>
</view>
</view>
</view>
</view>
<!-- 发票抬头 -->
<view class="form-item column">
<text class="label">发票抬头</text>
<input
v-model="form.name"
class="input"
placeholder="请输入公司全称或个人姓名"
/>
<text class="hint">请确保发票抬头与公司营业执照或个人身份证上的名称一致</text>
</view>
<!-- 纳税人识别号(企业才显示) -->
<view v-if="form.type === '0'" class="form-item column">
<text class="label">纳税人识别号</text>
<input
v-model="form.taxno"
class="input"
maxlength="20"
placeholder="请输入纳税人识别号"
/>
<text class="hint">企业税务登记证上的号码,一般为 15、18 或 20 位</text>
</view>
<!-- 开票金额(只读) -->
<!-- <view class="form-item">
<text class="label">开票金额</text>
<text class="amount">¥ {{ (Number(form.amount || 0)).toFixed(2) }}</text>
</view> -->
</view>
<!-- 提交按钮 -->
<view class="btn-wrap">
<view :class="{ loading: submitting }" class="submit-btn" @click="handleSubmit">
{{ submitting ? '提交中...' : '提交申请' }}
</view>
</view>
</view>
</template>
<script setup>
import {ref, reactive} from 'vue';
import {onLoad} from '@dcloudio/uni-app';
import {outputInvoiceNoFeisui} from '@/common/api.js';
const submitting = ref(false);
// 表单数据
const form = reactive({
id: '', // 订单ID
name: '', // 发票抬头
taxno: '', // 纳税人识别号
type: '1', // 发票类型:1=个人 0=企业
amount: 0 // 金额
});
// 页面加载
onLoad((options) => {
if (options.id || options.orderId) {
form.id = options.id || options.orderId;
form.amount = options.amount || 0;
}
console.log('非税开票参数:', options)
});
// 表单验证
const validateForm = () => {
// 发票抬头校验
if (!form.name) {
uni.showToast({title: '请输入发票抬头', icon: 'none'});
return false;
}
if (form.name.length < 2 || form.name.length > 100) {
uni.showToast({title: '发票抬头长度在2-100个字符之间', icon: 'none'});
return false;
}
// 企业必须填纳税人识别号
if (form.type === '0' && !form.taxno) {
uni.showToast({title: '请输入纳税人识别号', icon: 'none'});
return false;
}
// 纳税人识别号格式校验
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'});
return false;
}
}
return true;
};
// 提交非税发票申请
const handleSubmit = async () => {
if (submitting.value) return;
if (!validateForm()) return;
submitting.value = true;
console.log('提交非税开票表单数据:', form);
try {
await outputInvoiceNoFeisui(form);
uni.showToast({
title: '开票申请提交成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
} catch (error) {
console.log('非税开票失败:', error);
submitting.value = false;
} finally {
submitting.value = false;
}
};
</script>
<style lang="scss" scoped>
.invoice-apply {
min-height: 100vh;
background: #f5f7fa;
padding-bottom: 140rpx;
}
.content {
padding: 20rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
margin-bottom: 10rpx;
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #303133;
}
.section-hint {
font-size: 24rpx;
color: #909399;
padding: 6rpx 16rpx;
background: #f5f5f5;
border-radius: 8rpx;
}
}
.form-item {
background: #fff;
padding: 24rpx;
margin-bottom: 20rpx;
border-radius: 16rpx;
border: 1rpx solid #e9ecef;
&.column {
display: flex;
flex-direction: column;
}
.label {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 16rpx;
}
.input {
width: 100%;
font-size: 28rpx;
color: #333;
padding: 0 24rpx;
box-sizing: border-box;
background: transparent;
min-width: 0;
text-align: left;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
height: 80rpx;
line-height: 80rpx;
&::placeholder {
color: #999;
font-size: 28rpx;
}
}
.hint {
font-size: 24rpx;
color: #909399;
margin-top: 8rpx;
}
.amount {
font-size: 36rpx;
color: #AD181F;
font-weight: 600;
}
}
/* 发票类型选择 */
.type-select {
display: flex;
gap: 20rpx;
margin-top: 10rpx;
}
.type-option {
flex: 1;
display: flex;
align-items: center;
padding: 20rpx;
border: 2rpx solid #e4e7ed;
border-radius: 12rpx;
background: #fafafa;
transition: all 0.3s;
&.active {
border-color: #409eff;
background: #f0f6ff;
}
.type-icon {
width: 60rpx;
height: 60rpx;
background: #f5f7ff;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
.icon-text {
font-size: 28rpx;
font-weight: 600;
color: #409eff;
}
&.enterprise {
background: #f0f6ff;
.icon-text {
color: #1561CB;
}
}
}
&.active .type-icon {
background: #409eff;
.icon-text {
color: #fff;
}
&.enterprise {
background: #1561CB;
}
}
.type-info {
display: flex;
flex-direction: column;
.type-name {
font-size: 28rpx;
font-weight: 600;
color: #303133;
}
}
}
/* 提交按钮 */
.btn-wrap {
width: 100%;
background-color: #fff;
padding: 30rpx;
position: fixed;
bottom: 0;
left: 0;
right: 0;
box-sizing: border-box;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.submit-btn {
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
width: 100%;
background: linear-gradient(135deg, #AD181F 0%, #c42a2a 100%);
color: #fff;
font-size: 32rpx;
text-align: center;
font-weight: 500;
&.loading {
background: #c0c4cc;
}
}
</style>
\ No newline at end of file
<template>
<view class="invoice-apply">
<view class="content">
<!-- 发票类型 -->
<view class="form-item">
<text class="label">发票类型</text>
<view class="type-select">
<!-- <view
:class="{ active: form.invoiceType === '2' }"
class="type-option"
@click="form.invoiceType = '2'"
>
<view class="type-icon"></view>
<view class="type-info">
<text class="type-name">非税票</text>
<text class="type-desc">个人/非企业单位</text>
</view>
</view> -->
<view
:class="{ active: form.invoiceType === '1' }"
class="type-option"
@click="form.invoiceType = '1'"
>
<view class="type-icon enterprise"></view>
<view class="type-info">
<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="请输入公司全称或个人姓名"
/>
<text class="hint">请确保发票抬头与公司营业执照或个人身份证上的名称一致。</text>
</view>
<!-- 纳税人识别号(企业才显示) -->
<view v-if="form.invoiceType === '1'" class="form-item column">
<text class="label">纳税人识别号</text>
<input
v-model="form.taxno"
class="input"
maxlength="20"
placeholder="请输入纳税人识别号"
/>
<text class="hint">企业税务登记证上的号码,一般为 15、18 或 20 位</text>
</view>
<!-- 接收方式 -->
<view class="form-item">
<text class="label">接收方式</text>
<view class="method-select">
<view class="method-option active">
<view class="method-icon"></view>
<view class="method-info">
<text class="method-name">电子发票</text>
<text class="method-desc">发送至邮箱</text>
</view>
<view class="method-tag">推荐</view>
</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>
<input
v-model="form.phone"
class="input"
placeholder="请输入接收发票的邮箱号码"
type="text"
/>
<text class="hint">电子发票将在 3-5 个工作日内发送至该邮箱</text>
</view>
</view>
<!-- 提交按钮 -->
<view class="btn-wrap">
<view :class="{ loading: submitting }" class="submit-btn" @click="handleSubmit">
{{ submitting ? '提交中...' : '提交申请' }}
</view>
</view>
</view>
</template>
<script setup>
import {ref, reactive} from 'vue';
import {onLoad} from '@dcloudio/uni-app';
import {outputInvoiceNo} from '@/common/api.js';
const submitting = ref(false);
const type = ref(0) //1个人订单只开普票
// 表单数据(与PC端字段完全对齐)
const form = reactive({
invoiceType: '1', // 1=企业 2=个人
deliveryMethod: '1', // 接收方式:1=电子发票
name: '', // 发票抬头
taxno: '', // 纳税人识别号
phone: '', // 邮箱
amount: 0, // 金额
id: '' // 订单ID
});
// 页面加载(接收PC端传来的参数)
onLoad((options) => {
if (options.id || options.orderId) {
form.id = options.id || options.orderId;
form.amount = options.amount;
}
if (options.invoiceType) {
form.invoiceType = options.invoiceType;
}
type.value = options.type ?? 0
console.log(options)
});
// 表单验证
const validateForm = () => {
// 发票抬头校验
if (!form.name) {
uni.showToast({title: '请输入发票抬头', icon: 'none'});
return false;
}
if (form.name.length < 2 || form.name.length > 100) {
uni.showToast({title: '发票抬头长度在2-100个字符之间', icon: 'none'});
return false;
}
// 企业必须填纳税人识别号
if (form.invoiceType === '1' && !form.taxno) {
uni.showToast({title: '请输入纳税人识别号', icon: 'none'});
return false;
}
// 纳税人识别号格式校验(同PC)
if (form.invoiceType === '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'});
return false;
}
const phoneReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!phoneReg.test(form.phone)) {
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);
} catch (error) {
console.log(error)
submitting.value = false;
// 错误已由 request.js 处理
} finally {
submitting.value = false;
}
};
</script>
<style lang="scss" scoped>
.invoice-apply {
min-height: 100vh;
background: #f5f7fa;
}
.content {
padding: 20rpx;
}
.form-item {
background: #fff;
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;
color: #333;
padding: 0 24rpx;
box-sizing: border-box;
background: transparent;
min-width: 0;
text-align: left;
border: 1rpx solid #ccc;
border-radius: 8rpx;
height: 80rpx;
line-height: 80rpx;
&::placeholder {
color: #999;
font-size: 28rpx;
line-height: 80rpx;
}
}
.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;
margin-top: 10rpx;
}
.type-option {
flex: 1;
display: flex;
align-items: center;
padding: 20rpx;
border: 2rpx solid #e4e7ed;
border-radius: 12rpx;
background: #fafafa;
&.active {
border-color: #AD181F;
background: #FFF5F5;
}
.type-icon {
width: 60rpx;
height: 60rpx;
background: #f5f7ff;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
color: #409eff;
font-weight: 600;
margin-right: 16rpx;
&.enterprise {
background: #f0f6ff;
color: #1561CB;
}
}
&.active .type-icon {
background: #AD181F;
color: #fff;
}
.type-info {
display: flex;
flex-direction: column;
.type-name {
font-size: 28rpx;
font-weight: 600;
color: #303133;
}
.type-desc {
font-size: 22rpx;
color: #909399;
margin-top: 4rpx;
}
}
}
/* 接收方式选择 */
.method-select {
margin-top: 10rpx;
}
.method-option {
display: flex;
align-items: center;
padding: 20rpx;
border: 2rpx solid #e4e7ed;
border-radius: 12rpx;
background: #fafafa;
&.active {
border-color: #AD181F;
background: #FFF5F5;
}
.method-icon {
width: 60rpx;
height: 60rpx;
background: #f5f7ff;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
color: #409eff;
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;
background: #f0f9f0;
padding: 4rpx 12rpx;
border-radius: 6rpx;
}
}
/* 提交按钮 */
.btn-wrap {
width: 100%;
background-color: #fff;
padding: 30rpx;
position: fixed;
bottom: 0;
left: 0;
right: 0;
box-sizing: border-box;
}
.submit-btn {
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
width: 100%;
background: #AD181F;
color: #fff;
font-size: 32rpx;
text-align: center;
font-weight: 500;
&.loading {
background: #c0c4cc;
}
}
</style>
......@@ -432,7 +432,7 @@ const handlePay = async (item) => {
const makeInvoiceFN = (item) => {
needRefresh.value = true;
uni.navigateTo({
url: `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}&type=1`
url: `/pages/invoice/applyFeisui?orderId=${item.id}&amount=${item.price}&type=1`
});
};
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!