f05cc2b8 by lttnew

开票

1 parent 8a216ef1
......@@ -2106,3 +2106,38 @@ export function memberAuditList(params) {
params: params
})
}
// 不再显示考点弹框
export function noDisplay(data) {
return request({
url: '/system/user/noDisplay',
method: 'put',
params: data
})
}
// 修改手机号
export function editPhone(data) {
return request({
url: '/system/user/editPhone',
method: 'post',
params: data
})
}
// 图形验证码
export function getCodeImg() {
return request({
url: '/captchaImage',
method: 'get'
})
}
// 短信验证码
export function getSmsCode(data) {
return request({
url: '/captchaSmsWithCaptchaImageForMiniApp',
method: 'post',
params: data
})
}
......
......@@ -111,6 +111,7 @@ function getInfo() {
uni.removeStorageSync('webUserName')
userStore.setUser(user)
app.globalData.userInfo = user
app.globalData.deptType = user.dept.deptType
app.globalData.genFlag = user.dept.genFlag
app.globalData.changePassFlag = user.changePassFlag
......
......@@ -2,7 +2,7 @@
<view class="exam-point-list">
<!-- 顶部申请按钮 -->
<view class="apply-btn-box">
<button class="apply-btn" :disabled="memberInfo.isPoints==0&&formInfo.auditStatus==2" @click="goApply">申请考点</button>
<button class="apply-btn" :disabled="memberInfo.isPoints==0||formInfo.auditStatus==2||formInfo.auditStatus==1" @click="goApply">申请考点</button>
</view>
<!-- 列表 -->
......@@ -51,7 +51,7 @@
<script setup>
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { getMyRecent } from '@/common/api'
import { getMyRecentExam } from '@/common/api'
const app = getApp()
const list = ref([])
const loading = ref(false)
......@@ -68,7 +68,7 @@ function loadData() {
if (loading.value) return
loading.value = true
getMyRecent().then(res => {
getMyRecentExam().then(res => {
formInfo.value = res.data
if (res.data && res.data.auditLogs) {
try {
......@@ -101,7 +101,7 @@ function goApply() {
function getStatusClass(status) {
return {
'status-1': status == 0,
'status-2': status == 1,
'status-2': status == 9,
'status-3': status == 3
}
}
......
......@@ -84,7 +84,7 @@
<!-- 级位/段位考试(仅人数合计) -->
<view v-if="currentTab === '2' || currentTab === '3' || currentTab === '4'" class="single-info">
<view class="label">人数合计</view>
<view class="value">{{ item.content.allPersonCount || 0 }}</view>
<view class="value">{{ item.content.personCount || 0 }}</view>
</view>
<view class="line" v-if="currentTab === '2' || currentTab === '3' || currentTab === '4'"></view>
<view class="single-info">
......@@ -111,10 +111,14 @@
<view class="btn-group">
<!-- 已缴费:申请开票/已开票(需要审核通过才能开票) -->
<template v-if="item.payStatus == 1 && item.invoiceStatus != 1&& item.auditStatus == 2">
<button class="btn btn-invoice" @click="makeInvoiceFN(item)" :disabled="item.invoiceStatus === 1">
<button class="btn btn-view-invoice" @click="makeInvoiceFN(item)" :disabled="item.invoiceStatus === 1">
开票
</button>
</template>
<!-- 已开票:查看发票 -->
<template v-if="item.invoiceStatus == 1">
<button class="btn btn-invoice" @click.stop="viewInvoice(item)">查看发票</button>
</template>
<!-- 未缴费:去缴费 + 取消订单 -->
<!-- <template v-if="item.payStatus == 0">
<button class="btn btn-cancel" @click="handleCancel(item)">取消订单</button>
......@@ -136,6 +140,40 @@
</view>
</scroll-view>
<!-- 发票查看弹窗 -->
<view v-if="showInvoicePopup" class="invoice-popup-mask" @click="closeInvoicePopup">
<view class="invoice-popup-content" @click.stop>
<view class="invoice-popup-header">
<text class="invoice-popup-title">发票信息</text>
<view class="invoice-popup-close" @click="closeInvoicePopup"></view>
</view>
<view class="invoice-popup-body">
<view class="invoice-info-list">
<view class="invoice-info-row">
<view class="invoice-info-label">发票类型</view>
<view class="invoice-type-badge" :class="{ 'vat-type': invoiceData.invoiceType == 2 }">
{{ invoiceData.invoiceType == 1 ? '普通发票' : '增值税专用发票' }}
</view>
</view>
<view class="invoice-info-row">
<text class="invoice-info-label">发票抬头</text>
<text class="invoice-info-value">{{ invoiceData.invoiceBuyerName || '—' }}</text>
</view>
<view class="invoice-info-row" v-if="invoiceData.invoiceBuyerTaxno">
<text class="invoice-info-label">纳税人识别号</text>
<text class="invoice-info-value">{{ invoiceData.invoiceBuyerTaxno }}</text>
</view>
<view class="invoice-info-row">
<text class="invoice-info-label">接收邮箱</text>
<text class="invoice-info-value">{{ invoiceData.invoicePushPhone || '—' }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 自定义删除确认弹窗 -->
<view v-if="showDelPopup" class="popup-mask" @touchmove.stop.prevent @click.stop="closeDelPopup">
<view class="custom-modal" @click.stop>
......@@ -228,6 +266,8 @@ const queryParams = reactive({
const showDelPopup = ref(false);
const showCancelPopup = ref(false);
const isPopupOpen = ref(false);
const showInvoicePopup = ref(false);
const invoiceData = ref({});
// 弹窗内容
const delModalContent = ref('');
......@@ -397,6 +437,24 @@ const makeInvoiceFN = (item) => {
});
};
// 查看发票
const viewInvoice = (item) => {
invoiceData.value = {
invoiceType: item.invoiceType || 1,
invoiceBuyerName: item.invoiceTitle || item.invoiceBuyerName || '—',
invoiceBuyerTaxno: item.invoiceTaxno || item.invoiceBuyerTaxno || '',
invoicePushPhone: item.invoiceEmail || item.invoicePushPhone || '—'
};
showInvoicePopup.value = true;
isPopupOpen.value = true;
};
// 关闭发票弹窗
const closeInvoicePopup = () => {
showInvoicePopup.value = false;
isPopupOpen.value = false;
};
// 取消订单
const handleCancel = (item) => {
currentOrder.value = item;
......@@ -492,6 +550,8 @@ const closeCancelPopup = () => {
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
border-radius: 12rpx;
display: flex;
flex-direction: column;
// border-top: 6rpx solid transparent;
// background-clip: padding-box, border-box;
// background-origin: padding-box, border-box;
......@@ -671,23 +731,25 @@ const closeCancelPopup = () => {
align-items: center;
gap: 16rpx;
width: 100%;
margin-top: 20rpx;
.btn {
padding: 12rpx 32rpx;
// 固定宽度,所有按钮一样大
width: 160rpx;
height: 70rpx;
line-height: 70rpx;
padding: 0;
border-radius: 40rpx;
font-size: 24rpx;
line-height: 1.5;
white-space: nowrap;
display: inline-block;
margin: 0;
border: none;
width: 80px;
background: transparent;
text-align: center;
margin: 0;
&::after {
border: none;
display: none; // 关键:隐藏伪元素
}
&.btn-delete {
background: #fff;
color: #e4393c;
......@@ -700,6 +762,12 @@ const closeCancelPopup = () => {
border: 1rpx solid #e4393c;
}
&.btn-view-invoice {
background: linear-gradient(90deg, #FF755A, #F51722);
color: #fff;
border: none;
}
&.btn-cancel {
background: #fff;
color: #666;
......@@ -720,7 +788,6 @@ const closeCancelPopup = () => {
}
// 加载/无更多提示
.loading-tip, .no-more {
text-align: center;
......@@ -814,4 +881,97 @@ const closeCancelPopup = () => {
color: #e8341d;
letter-spacing: 1rpx;
}
// 发票弹窗样式
.invoice-popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.invoice-popup-content {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.2);
}
.invoice-popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
background: linear-gradient(135deg, #AD181F 0%, #E4393C 100%);
.invoice-popup-title {
font-size: 32rpx;
font-weight: 600;
color: #fff;
}
.invoice-popup-close {
width: 44rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
}
}
.invoice-popup-body {
padding: 30rpx;
}
.invoice-type-badge {
display: inline-flex;
align-items: center;
padding: 8rpx 24rpx;
background: linear-gradient(135deg, #FF755A 0%, #F51722 100%);
color: #fff;
border-radius: 30rpx;
font-size: 24rpx;
font-weight: 500;
&.vat-type {
background: linear-gradient(135deg, #6aaaf2 0%, #178cd7 100%);
}
}
.invoice-info-list {
.invoice-info-row {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 24rpx 0;
border-bottom: 1rpx dashed #eee;
&:last-child {
border-bottom: none;
}
.invoice-info-label {
font-size: 26rpx;
color: #999;
flex-shrink: 0;
}
.invoice-info-value {
font-size: 26rpx;
color: #333;
text-align: right;
word-break: break-all;
max-width: 340rpx;
}
}
}
</style>
\ No newline at end of file
......
......@@ -7,7 +7,7 @@
<view class="loginOutIcon2" @click="goPath('/myCenter/index')">
<image :src="config.loginImage_api + '/fs/static/dg/icon01@3x.png'" class="switch-icon"></image>
</view>
<view class="welcome1" @click="goPath('/myCenter/index')">
<view class="welcome1 mt30" @click="goPath('/myCenter/index')">
<view class="flex f-a-c">
<!-- <view> -->
<text class="title-border"></text>
......@@ -16,7 +16,7 @@
<!-- <image :src="config.loginImage_api + '/fs/static/dg/icon013@x.png'" class="switch-icon"></image> -->
</view>
<view class="sub-title">
<view class="mt10">会员名称&nbsp;&nbsp;会员所属道馆</view>
<view class="mt10">您好!{{ memberInfo.name }}</view>
<view class="mt10">欢迎使用中跆协会员管理系统!</view>
</view>
</view>
......@@ -488,6 +488,58 @@
</view>
</uni-section>
</view>
<!-- 绑定手机号弹框 -->
<uni-popup ref="bindingPhonePopup" type="center" :mask-click="false">
<view class="dialog-wrapper">
<view class="dialog-close" @click="closeBindingPhoneDialog"></view>
<view class="dialog-icon">
<uni-icons type="phone" size="48" color="#AD181F"></uni-icons>
</view>
<view class="dialog-title">绑定手机号</view>
<view class="dialog-content">
<view class="form-item">
<input class="form-input" v-model="bindingForm.phone" placeholder="请输入手机号" maxlength="11" type="number" />
</view>
<view class="form-item captcha-row">
<input class="form-input captcha-input" v-model="bindingForm.captcha" placeholder="图形验证码" maxlength="4" />
<image class="captcha-img" :src="captchaUrl" @click="refreshCaptcha" mode="aspectFit"></image>
</view>
<view class="form-item captcha-row">
<input class="form-input sms-input" v-model="bindingForm.code" placeholder="短信验证码" maxlength="6" type="number" />
<button class="sms-btn" :disabled="smsCountdown > 0" @click="sendSmsCode">
{{ smsCountdown > 0 ? smsCountdown + 's' : '获取验证码' }}
</button>
</view>
</view>
<view class="dialog-footer">
<button class="dialog-btn cancel" @click="closeBindingPhoneDialog">取消</button>
<button class="dialog-btn confirm" @click="submitBindingPhone">确定</button>
</view>
</view>
</uni-popup>
<!-- 申请成为考点弹框 -->
<uni-popup ref="examPointPopup" type="center" :mask-click="false">
<view class="dialog-wrapper exam-dialog">
<view class="dialog-close" @click="closeExamPointDialog"></view>
<view class="dialog-icon success-icon">
<uni-icons type="checkmark" size="48" color="#29c490"></uni-icons>
</view>
<view class="dialog-title">申请成为考点</view>
<view class="dialog-message">
<text>恭喜您成为中国跆拳道协会团体会员!</text>
<text>根据协会考点管理办法,需成为考点单位才能进行考级业务的办理。</text>
</view>
<view class="dialog-footer">
<button class="dialog-btn cancel" @click="closeExamPointDialog">取消</button>
<button class="dialog-btn confirm" @click="goExamPointApply">去申请</button>
</view>
<view class="no-display">
<text @click="handleNoDisplay">不再显示</text>
</view>
</view>
</uni-popup>
</view>
</template>
......@@ -531,6 +583,21 @@ const newsList = ref([])
const isInit = ref(false)
const isBlack = ref(0)
// 绑定手机号弹框相关
const bindingPhonePopup = ref(null)
const bindingForm = ref({
phone: '',
captcha: '',
code: '',
uuid: ''
})
const captchaUrl = ref('')
const smsCountdown = ref(0)
let smsTimer = null
// 申请考点弹框相关
const examPointPopup = ref(null)
onShow(() => {
if (app.globalData.isLogin) {
init()
......@@ -723,7 +790,7 @@ function init() {
})
}
checkDialogs()
uni.hideLoading();
})
......@@ -752,6 +819,116 @@ function goNewsDetail(n) {
url: `/pages/index/newsDetail?noteId=${n.noteId}`
});
}
// 绑定手机号弹框方法
function refreshCaptcha() {
bindingForm.value.uuid = 'uuid-' + Date.now()
captchaUrl.value = config.baseUrl_api + '/captchaImage?uuid=' + bindingForm.value.uuid + '&t=' + Date.now()
}
function sendSmsCode() {
const phone = bindingForm.value.phone
const strTemp = /^1[2|3|4|5|6|7|8|9][0-9]{9}$/
if (!phone) {
uni.showToast({ title: '请输入手机号', icon: 'none' })
return
}
if (!strTemp.test(phone)) {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
return
}
if (!bindingForm.value.captcha) {
uni.showToast({ title: '请输入图形验证码', icon: 'none' })
return
}
api.getSmsCode({
uuid: bindingForm.value.uuid,
telNo: phone,
captcha: bindingForm.value.captcha
}).then(res => {
uni.showToast({ title: '发送成功', icon: 'success' })
smsCountdown.value = 60
smsTimer = setInterval(() => {
smsCountdown.value--
if (smsCountdown.value <= 0) {
clearInterval(smsTimer)
smsTimer = null
}
}, 1000)
}).catch(() => {
refreshCaptcha()
})
}
function closeBindingPhoneDialog() {
bindingPhonePopup.value.close()
}
async function submitBindingPhone() {
const { phone, captcha, code, uuid } = bindingForm.value
if (!phone) {
uni.showToast({ title: '请输入手机号', icon: 'none' })
return
}
if (!captcha) {
uni.showToast({ title: '请输入图形验证码', icon: 'none' })
return
}
if (!code) {
uni.showToast({ title: '请输入短信验证码', icon: 'none' })
return
}
try {
await api.editPhone({ phone, captcha, code, uuid })
uni.showToast({ title: '修改成功', icon: 'success' })
closeBindingPhoneDialog()
setTimeout(() => {
uni.reLaunch({ url: '/pages/index/home' })
}, 1500)
} catch (e) {
// 错误已由 request.js 处理
}
}
// 申请考点弹框方法
function closeExamPointDialog() {
examPointPopup.value.close()
}
function goExamPointApply() {
closeExamPointDialog()
uni.navigateTo({ url: '/myCenter/examPointApplyList' })
}
async function handleNoDisplay() {
await api.noDisplay()
closeExamPointDialog()
}
// 检查弹框显示条件
function checkDialogs() {
const user = app.globalData.userInfo || {}
const memberInfoData = app.globalData.memberInfo || {}
// 绑定手机号条件: changePassFlag='1' && activeStatus=1 && authenticationStatus=2 && phonenumber为空 && checkFlag=1
if (app.globalData.changePassFlag === '1' &&
memberInfoData.activeStatus == 1 &&
app.globalData.authenticationStatus == 2 &&
!user.phonenumber &&
user.checkFlag == 1) {
refreshCaptcha()
bindingPhonePopup.value.open()
}
// 申请考点条件: activeStatus=1 && authenticationStatus=2 && hintFlag=1 && deptType=6 && isPoints=1
if (memberInfoData.activeStatus == 1 &&
app.globalData.authenticationStatus == 2 &&
user.hintFlag == 1 &&
app.globalData.deptType == 6 &&
memberInfoData.isPoints == 1) {
examPointPopup.value.open()
}
}
</script>
<style lang="scss" scoped>
:deep(.uni-section) {
......@@ -1021,3 +1198,154 @@ function goNewsDetail(n) {
}
</style>
<style lang="scss" scoped>
/* 弹框样式 */
.dialog-wrapper {
width: 600rpx;
background: linear-gradient(135deg, #fff 0%, #f8f9fc 100%);
border-radius: 24rpx;
padding: 48rpx 40rpx 40rpx;
position: relative;
box-sizing: border-box;
}
.dialog-close {
position: absolute;
top: 20rpx;
right: 24rpx;
font-size: 32rpx;
color: #999;
z-index: 10;
}
.dialog-icon {
text-align: center;
margin-bottom: 24rpx;
}
.dialog-title {
font-size: 40rpx;
font-weight: 600;
color: #303133;
text-align: center;
margin-bottom: 24rpx;
}
.dialog-content {
padding: 0 10rpx;
margin-bottom: 32rpx;
}
.form-item {
margin-bottom: 24rpx;
}
.form-input {
height: 80rpx;
background: #f5f7fa;
border-radius: 16rpx;
padding: 0 24rpx;
font-size: 28rpx;
width: 100%;
box-sizing: border-box;
}
.captcha-row {
display: flex;
align-items: center;
gap: 16rpx;
}
.captcha-input {
flex: 1;
}
.captcha-img {
width: 180rpx;
height: 72rpx;
border-radius: 12rpx;
background: #f5f7fa;
}
.sms-input {
flex: 1;
}
.sms-btn {
width: 200rpx;
height: 72rpx;
line-height: 72rpx;
background: #AD181F;
color: #fff;
font-size: 24rpx;
border-radius: 16rpx;
padding: 0;
margin: 0;
&::after {
border: none;
}
}
.sms-btn[disabled] {
background: #c0c4cc;
color: #fff;
}
.dialog-footer {
display: flex;
justify-content: center;
gap: 40rpx;
}
.dialog-btn {
width: 220rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
font-size: 28rpx;
font-weight: 500;
padding: 0;
margin: 0;
&::after {
border: none;
}
}
.dialog-btn.cancel {
background: #f5f7fa;
color: #606266;
border: 1rpx solid #dcdfe6;
}
.dialog-btn.confirm {
background: #AD181F;
color: #fff;
box-shadow: 0 4rpx 16rpx rgba(21, 97, 203, 0.3);
}
/* 考点弹框样式 */
.exam-dialog {
.dialog-message {
font-size: 28rpx;
color: #606266;
line-height: 1.8;
text-align: left;
margin-bottom: 40rpx;
display: flex;
flex-direction: column;
gap: 12rpx;
}
}
.no-display {
text-align: right;
margin-top: 24rpx;
font-size: 24rpx;
color: #909399;
}
.success-icon {
color: #29c490;
}
</style>
......
......@@ -4,61 +4,94 @@
<!-- 发票类型 -->
<view class="form-item">
<text class="label">发票类型</text>
<text class="value">{{ form.invoiceType === '2' ? '普通发票(企业)' : '普通发票(个人)' }}</text>
<view class="type-select">
<view
class="type-option"
:class="{ active: form.invoiceType === '1' }"
@click="form.invoiceType = '1'"
>
<view class="type-icon"></view>
<view class="type-info">
<text class="type-name">普通发票</text>
<text class="type-desc">个人/非企业单位</text>
</view>
</view>
<view
class="type-option"
:class="{ active: form.invoiceType === '2' }"
@click="form.invoiceType = '2'"
>
<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">
<view class="form-item column">
<text class="label">发票抬头</text>
<input
class="input"
v-model="form.name"
placeholder="请输入公司全称或个人姓名"
placeholder-style="color: #999;"
/>
<text class="hint">请确保发票抬头与公司营业执照或个人身份证上的名称一致。</text>
</view>
<!-- 纳税人识别号(企业才显示) -->
<view class="form-item" v-if="form.invoiceType === '2'">
<view class="form-item column" v-if="form.invoiceType === '2'">
<text class="label">纳税人识别号</text>
<input
class="input"
v-model="form.taxno"
placeholder="请输入纳税人识别号"
placeholder-style="color: #999;"
maxlength="20"
/>
</view>
<!-- 开票金额 -->
<view class="form-item">
<text class="label">开票金额</text>
<text class="amount">¥ {{ (Number(form.amount)).toFixed(2) }}</text>
<text class="hint">企业税务登记证上的号码,一般为 15、18 或 20 位</text>
</view>
<!-- 接收方式 -->
<view class="form-item">
<text class="label">接收方式</text>
<text class="value">电子邮箱</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">
<text class="label">接收邮箱</text>
<view class="form-item column">
<text class="label">接收邮箱号码</text>
<input
class="input"
v-model="form.email"
placeholder="请输入接收发票的邮箱(必填)"
placeholder-style="color: #999;"
v-model="form.phone"
placeholder="请输入接收发票的邮箱号码"
type="text"
/>
<text class="hint">电子发票将在 3-5 个工作日内发送至该邮箱</text>
</view>
</view>
<view class="hint">电子发票将在3-5个工作日内发送至该邮箱</view>
<!-- 提交按钮 -->
<view class="btn-wrap" @click="submitInvoice">
<view class="submit-btn">提交申请</view>
<view class="btn-wrap">
<view class="submit-btn" :class="{ loading: submitting }" @click="handleSubmit">
{{ submitting ? '提交中...' : '提交申请' }}
</view>
</view>
</view>
</template>
......@@ -66,14 +99,17 @@
<script setup>
import { ref, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { outputInvoiceNo } from '@/common/api.js'; // 与PC端接口一致
import { outputInvoiceNo } from '@/common/api.js';
const submitting = ref(false);
// 表单数据(与PC端字段完全对齐)
const form = reactive({
invoiceType: '1', // 1=个人 2=企业
deliveryMethod: '1', // 接收方式:1=电子发票
name: '', // 发票抬头
taxno: '', // 纳税人识别号
email: '', // 邮箱
phone: '', // 邮箱
amount: 0, // 金额
id: '' // 订单ID
});
......@@ -81,73 +117,76 @@ const form = reactive({
// 页面加载(接收PC端传来的参数)
onLoad((options) => {
if (options.id || options.orderId) {
form.id = options.id|| options.orderId;
form.id = options.id || options.orderId;
form.amount = options.amount;
console.log(33,form.amount);
}
if (options.invoiceType) {
form.invoiceType = options.invoiceType;
}
// getOrderInfo();
});
// 获取订单金额
// const getOrderInfo = async () => {
// try {
// // 这里替换成你真实获取订单金额的接口
// = 1500;
// } catch (error) {
// uni.showToast({ title: '获取订单信息失败', icon: 'none' });
// }
// };
// 提交发票申请(与PC逻辑完全一致)
const submitInvoice = async () => {
// 1. PC端逻辑:个人不允许开票
if (form.invoiceType === '1') {
return uni.showToast({ title: '暂不支持个人开票', icon: 'none' });
}
// 2. 抬头校验
// 表单验证
const validateForm = () => {
// 发票抬头校验
if (!form.name) {
return uni.showToast({ title: '请输入发票抬头', icon: 'none' });
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;
}
// 3. 企业必须填纳税人识别号
// 企业必须填纳税人识别号
if (form.invoiceType === '2' && !form.taxno) {
return uni.showToast({ title: '请输入纳税人识别号', icon: 'none' });
uni.showToast({ title: '请输入纳税人识别号', icon: 'none' });
return false;
}
// 4. 纳税人识别号格式校验(同PC)
// 纳税人识别号格式校验(同PC)
if (form.invoiceType === '2') {
const taxReg = /^[A-Z0-9]{15}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$/;
if (form.invoiceType === '2' && !taxReg.test(form.taxno)) {
return uni.showToast({ title: '纳税人识别号格式不正确', icon: 'none' });
if (!taxReg.test(form.taxno)) {
uni.showToast({ title: '纳税人识别号格式不正确', icon: 'none' });
return false;
}
}
// 5. 邮箱校验
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!form.email) {
return uni.showToast({ title: '请输入接收邮箱', icon: 'none' });
// 邮箱校验
if (!form.phone) {
uni.showToast({ title: '请输入接收邮箱', icon: 'none' });
return false;
}
if (!emailReg.test(form.email)) {
return uni.showToast({ title: '请输入正确的邮箱地址', icon: 'none' });
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 {
// 调用PC端同一个接口:outputInvoiceNo
await outputInvoiceNo(form);
uni.showToast({
title: '发票申请提交成功!',
icon: 'success',
duration: 2000
title: '开票申请提交成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack();
}, 2000);
}, 1500);
} catch (error) {
uni.showToast({ title: '提交失败,请检查信息', icon: 'none' });
submitting.value = false;
// 错误已由 request.js 处理
} finally {
submitting.value = false;
}
};
</script>
......@@ -156,52 +195,198 @@ const submitInvoice = async () => {
.invoice-apply {
min-height: 100vh;
background: #f5f7fa;
}
.content {
.content {
padding: 20rpx;
}
.form-item {
display: flex;
justify-content: space-between;
align-items: center;
.form-item {
background: #fff;
padding: 24rpx;
border-bottom: 1rpx solid #eee;
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: 80%;
width: 100%;
font-size: 28rpx;
color: #333;
padding: 16rpx 0;
text-align: right;
}
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;
.value {
&::placeholder {
color: #999;
font-size: 28rpx;
color: #333;
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;
color: #e4393c;
font-weight: 500;
font-weight: 600;
color: #303133;
}
.type-desc {
font-size: 22rpx;
color: #909399;
margin-top: 4rpx;
}
}
}
.hint {
font-size: 26rpx;
color: #B6BCC0;
margin-top: 20rpx;
text-align: center;
/* 接收方式选择 */
.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 {
/* 提交按钮 */
.btn-wrap {
width: 100%;
background-color: #fff;
padding: 30rpx;
......@@ -209,18 +394,22 @@ const submitInvoice = async () => {
bottom: 0;
left: 0;
right: 0;
}
box-sizing: border-box;
}
.submit-btn {
height: 70rpx;
line-height: 70rpx;
border-radius: 35rpx;
width: 90%;
margin: 0 auto;
.submit-btn {
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
width: 100%;
background: #AD181F;
color: #fff;
font-size: 28rpx;
font-size: 32rpx;
text-align: center;
font-weight: 500;
&.loading {
background: #c0c4cc;
}
}
</style>
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!