开票
Showing
8 changed files
with
800 additions
and
14 deletions
| ... | @@ -1821,6 +1821,15 @@ export const outputInvoiceNo = (data) => { | ... | @@ -1821,6 +1821,15 @@ export const outputInvoiceNo = (data) => { |
| 1821 | }) | 1821 | }) |
| 1822 | } | 1822 | } |
| 1823 | 1823 | ||
| 1824 | // 非税开票 | ||
| 1825 | export const outputInvoiceNoFeisui = (data) => { | ||
| 1826 | return request({ | ||
| 1827 | url: `/common/order/outputInvoiceNoFeisui/${data.id}?taxno=${data.taxno || ''}&name=${data.name}&type=${data.type}`, | ||
| 1828 | method: 'post', | ||
| 1829 | params: data | ||
| 1830 | }) | ||
| 1831 | } | ||
| 1832 | |||
| 1824 | export function getAssoPers(perId) { | 1833 | export function getAssoPers(perId) { |
| 1825 | return request({ | 1834 | return request({ |
| 1826 | url: '/person/info/getRoleListByPerId/' + perId, | 1835 | url: '/person/info/getRoleListByPerId/' + perId, | ... | ... |
| ... | @@ -66,11 +66,14 @@ function showError(msg) { | ... | @@ -66,11 +66,14 @@ function showError(msg) { |
| 66 | loadingCount = 0 | 66 | loadingCount = 0 |
| 67 | uni.hideLoading() | 67 | uni.hideLoading() |
| 68 | } | 68 | } |
| 69 | // 延迟一点显示 Toast,避免在 hideLoading 动画过程中 Toast 被中断 | ||
| 70 | setTimeout(() => { | ||
| 69 | uni.showToast({ | 71 | uni.showToast({ |
| 70 | title: msg || '请求失败', | 72 | title: msg || '请求失败', |
| 71 | icon: 'none', | 73 | icon: 'none', |
| 72 | duration: 3000 | 74 | duration: 4000 |
| 73 | }) | 75 | }) |
| 76 | }, 50) | ||
| 74 | } | 77 | } |
| 75 | 78 | ||
| 76 | const request = function (req) { | 79 | const request = function (req) { | ... | ... |
| ... | @@ -174,7 +174,7 @@ | ... | @@ -174,7 +174,7 @@ |
| 174 | <view class="card-header"> | 174 | <view class="card-header"> |
| 175 | <view class="date"> | 175 | <view class="date"> |
| 176 | <view class="data-header"> | 176 | <view class="data-header"> |
| 177 | <text class="member-label">{{ tabs[currentTab].label }}·</text> | 177 | <text class="member-label">{{ tabs.find(t => t.type === currentTab)?.label }}·</text> |
| 178 | <text class="value ">{{ item.orderName || '——' }}</text> | 178 | <text class="value ">{{ item.orderName || '——' }}</text> |
| 179 | </view> | 179 | </view> |
| 180 | <text :class="{ | 180 | <text :class="{ |
| ... | @@ -271,7 +271,7 @@ | ... | @@ -271,7 +271,7 @@ |
| 271 | <!-- 已缴费:申请开票/已开票(需要审核通过才能开票) --> | 271 | <!-- 已缴费:申请开票/已开票(需要审核通过才能开票) --> |
| 272 | <template v-if="item.payStatus == 1 && item.invoiceStatus != 1&& item.auditStatus == 2 &&item.price>0"> | 272 | <template v-if="item.payStatus == 1 && item.invoiceStatus != 1&& item.auditStatus == 2 &&item.price>0"> |
| 273 | <button :disabled="item.invoiceStatus === 1" class="btn btn-view-invoice" | 273 | <button :disabled="item.invoiceStatus === 1" class="btn btn-view-invoice" |
| 274 | @click="makeInvoiceFN(item)"> | 274 | @click.stop="makeInvoiceFN(item)"> |
| 275 | 开票 | 275 | 开票 |
| 276 | </button> | 276 | </button> |
| 277 | </template> | 277 | </template> |
| ... | @@ -647,10 +647,16 @@ const makeInvoiceFN = (item) => { | ... | @@ -647,10 +647,16 @@ const makeInvoiceFN = (item) => { |
| 647 | // } | 647 | // } |
| 648 | // 设置刷新标记,从开票页面返回时刷新列表 | 648 | // 设置刷新标记,从开票页面返回时刷新列表 |
| 649 | needRefresh.value = true; | 649 | needRefresh.value = true; |
| 650 | // 跳转到开票页面 | 650 | // 根据tab类型决定跳转页面:个人/单位会员开非税票,级位/段位/越段考试开增值税票 |
| 651 | uni.navigateTo({ | 651 | let url = ''; |
| 652 | url: `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}` | 652 | if (currentTab.value === '0' || currentTab.value === '1') { |
| 653 | }); | 653 | // 个人/单位会员 - 非税开票 |
| 654 | url = `/pages/invoice/applyFeisui?orderId=${item.id}&amount=${item.price}`; | ||
| 655 | } else { | ||
| 656 | // 级位/段位/越段考试 - 增值税开票 | ||
| 657 | url = `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}`; | ||
| 658 | } | ||
| 659 | uni.navigateTo({ url }); | ||
| 654 | }; | 660 | }; |
| 655 | 661 | ||
| 656 | // 查看发票 | 662 | // 查看发票 | ... | ... |
| ... | @@ -36,7 +36,14 @@ | ... | @@ -36,7 +36,14 @@ |
| 36 | { | 36 | { |
| 37 | "path": "pages/invoice/apply", | 37 | "path": "pages/invoice/apply", |
| 38 | "style": { | 38 | "style": { |
| 39 | "navigationBarTitleText": "开具发票", | 39 | "navigationBarTitleText": "开具增值税发票", |
| 40 | "enablePullDownRefresh": false | ||
| 41 | } | ||
| 42 | }, | ||
| 43 | { | ||
| 44 | "path": "pages/invoice/applyFeisui", | ||
| 45 | "style": { | ||
| 46 | "navigationBarTitleText": "开具非税发票", | ||
| 40 | "enablePullDownRefresh": false | 47 | "enablePullDownRefresh": false |
| 41 | } | 48 | } |
| 42 | }, | 49 | }, | ... | ... |
| ... | @@ -5,19 +5,18 @@ | ... | @@ -5,19 +5,18 @@ |
| 5 | <view class="form-item"> | 5 | <view class="form-item"> |
| 6 | <text class="label">发票类型</text> | 6 | <text class="label">发票类型</text> |
| 7 | <view class="type-select"> | 7 | <view class="type-select"> |
| 8 | <view | 8 | <!-- <view |
| 9 | :class="{ active: form.invoiceType === '2' }" | 9 | :class="{ active: form.invoiceType === '2' }" |
| 10 | class="type-option" | 10 | class="type-option" |
| 11 | @click="form.invoiceType = '2'" | 11 | @click="form.invoiceType = '2'" |
| 12 | > | 12 | > |
| 13 | <view class="type-icon">个</view> | 13 | <view class="type-icon">个</view> |
| 14 | <view class="type-info"> | 14 | <view class="type-info"> |
| 15 | <text class="type-name">普通发票</text> | 15 | <text class="type-name">非税票</text> |
| 16 | <text class="type-desc">个人/非企业单位</text> | 16 | <text class="type-desc">个人/非企业单位</text> |
| 17 | </view> | 17 | </view> |
| 18 | </view> | 18 | </view> --> |
| 19 | <view | 19 | <view |
| 20 | v-if="type==0" | ||
| 21 | :class="{ active: form.invoiceType === '1' }" | 20 | :class="{ active: form.invoiceType === '1' }" |
| 22 | class="type-option" | 21 | class="type-option" |
| 23 | @click="form.invoiceType = '1'" | 22 | @click="form.invoiceType = '1'" |
| ... | @@ -106,7 +105,7 @@ const submitting = ref(false); | ... | @@ -106,7 +105,7 @@ const submitting = ref(false); |
| 106 | const type = ref(0) //1个人订单只开普票 | 105 | const type = ref(0) //1个人订单只开普票 |
| 107 | // 表单数据(与PC端字段完全对齐) | 106 | // 表单数据(与PC端字段完全对齐) |
| 108 | const form = reactive({ | 107 | const form = reactive({ |
| 109 | invoiceType: '2', // 1=企业 2=个人 | 108 | invoiceType: '1', // 1=企业 2=个人 |
| 110 | deliveryMethod: '1', // 接收方式:1=电子发票 | 109 | deliveryMethod: '1', // 接收方式:1=电子发票 |
| 111 | name: '', // 发票抬头 | 110 | name: '', // 发票抬头 |
| 112 | taxno: '', // 纳税人识别号 | 111 | taxno: '', // 纳税人识别号 | ... | ... |
pages/invoice/applyFeisui.vue
0 → 100644
| 1 | <template> | ||
| 2 | <view class="invoice-apply"> | ||
| 3 | <view class="content"> | ||
| 4 | <!-- 发票信息标题 --> | ||
| 5 | <view class="section-header"> | ||
| 6 | <text class="section-title">发票信息</text> | ||
| 7 | <text class="section-hint">请填写开票信息</text> | ||
| 8 | </view> | ||
| 9 | |||
| 10 | <!-- 发票类型 --> | ||
| 11 | <view class="form-item"> | ||
| 12 | <text class="label">发票类型</text> | ||
| 13 | <view class="type-select"> | ||
| 14 | <view | ||
| 15 | :class="{ active: form.type === '1' }" | ||
| 16 | class="type-option" | ||
| 17 | @click="form.type = '1'" | ||
| 18 | > | ||
| 19 | <view class="type-icon"> | ||
| 20 | <text class="icon-text">个</text> | ||
| 21 | </view> | ||
| 22 | <view class="type-info"> | ||
| 23 | <text class="type-name">个人</text> | ||
| 24 | </view> | ||
| 25 | </view> | ||
| 26 | <view | ||
| 27 | :class="{ active: form.type === '0' }" | ||
| 28 | class="type-option" | ||
| 29 | @click="form.type = '0'" | ||
| 30 | > | ||
| 31 | <view class="type-icon enterprise"> | ||
| 32 | <text class="icon-text">企</text> | ||
| 33 | </view> | ||
| 34 | <view class="type-info"> | ||
| 35 | <text class="type-name">企业</text> | ||
| 36 | </view> | ||
| 37 | </view> | ||
| 38 | </view> | ||
| 39 | </view> | ||
| 40 | |||
| 41 | <!-- 发票抬头 --> | ||
| 42 | <view class="form-item column"> | ||
| 43 | <text class="label">发票抬头</text> | ||
| 44 | <input | ||
| 45 | v-model="form.name" | ||
| 46 | class="input" | ||
| 47 | placeholder="请输入公司全称或个人姓名" | ||
| 48 | /> | ||
| 49 | <text class="hint">请确保发票抬头与公司营业执照或个人身份证上的名称一致</text> | ||
| 50 | </view> | ||
| 51 | |||
| 52 | <!-- 纳税人识别号(企业才显示) --> | ||
| 53 | <view v-if="form.type === '0'" class="form-item column"> | ||
| 54 | <text class="label">纳税人识别号</text> | ||
| 55 | <input | ||
| 56 | v-model="form.taxno" | ||
| 57 | class="input" | ||
| 58 | maxlength="20" | ||
| 59 | placeholder="请输入纳税人识别号" | ||
| 60 | /> | ||
| 61 | <text class="hint">企业税务登记证上的号码,一般为 15、18 或 20 位</text> | ||
| 62 | </view> | ||
| 63 | |||
| 64 | <!-- 开票金额(只读) --> | ||
| 65 | <!-- <view class="form-item"> | ||
| 66 | <text class="label">开票金额</text> | ||
| 67 | <text class="amount">¥ {{ (Number(form.amount || 0)).toFixed(2) }}</text> | ||
| 68 | </view> --> | ||
| 69 | </view> | ||
| 70 | |||
| 71 | <!-- 提交按钮 --> | ||
| 72 | <view class="btn-wrap"> | ||
| 73 | <view :class="{ loading: submitting }" class="submit-btn" @click="handleSubmit"> | ||
| 74 | {{ submitting ? '提交中...' : '提交申请' }} | ||
| 75 | </view> | ||
| 76 | </view> | ||
| 77 | </view> | ||
| 78 | </template> | ||
| 79 | |||
| 80 | <script setup> | ||
| 81 | import {ref, reactive} from 'vue'; | ||
| 82 | import {onLoad} from '@dcloudio/uni-app'; | ||
| 83 | import {outputInvoiceNoFeisui} from '@/common/api.js'; | ||
| 84 | |||
| 85 | const submitting = ref(false); | ||
| 86 | |||
| 87 | // 表单数据 | ||
| 88 | const form = reactive({ | ||
| 89 | id: '', // 订单ID | ||
| 90 | name: '', // 发票抬头 | ||
| 91 | taxno: '', // 纳税人识别号 | ||
| 92 | type: '1', // 发票类型:1=个人 0=企业 | ||
| 93 | amount: 0 // 金额 | ||
| 94 | }); | ||
| 95 | |||
| 96 | // 页面加载 | ||
| 97 | onLoad((options) => { | ||
| 98 | if (options.id || options.orderId) { | ||
| 99 | form.id = options.id || options.orderId; | ||
| 100 | form.amount = options.amount || 0; | ||
| 101 | } | ||
| 102 | console.log('非税开票参数:', options) | ||
| 103 | }); | ||
| 104 | |||
| 105 | // 表单验证 | ||
| 106 | const validateForm = () => { | ||
| 107 | // 发票抬头校验 | ||
| 108 | if (!form.name) { | ||
| 109 | uni.showToast({title: '请输入发票抬头', icon: 'none'}); | ||
| 110 | return false; | ||
| 111 | } | ||
| 112 | if (form.name.length < 2 || form.name.length > 100) { | ||
| 113 | uni.showToast({title: '发票抬头长度在2-100个字符之间', icon: 'none'}); | ||
| 114 | return false; | ||
| 115 | } | ||
| 116 | |||
| 117 | // 企业必须填纳税人识别号 | ||
| 118 | if (form.type === '0' && !form.taxno) { | ||
| 119 | uni.showToast({title: '请输入纳税人识别号', icon: 'none'}); | ||
| 120 | return false; | ||
| 121 | } | ||
| 122 | |||
| 123 | // 纳税人识别号格式校验 | ||
| 124 | if (form.type === '0') { | ||
| 125 | const taxReg = /^[A-Z0-9]{15}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$/; | ||
| 126 | if (!taxReg.test(form.taxno)) { | ||
| 127 | uni.showToast({title: '纳税人识别号格式不正确', icon: 'none'}); | ||
| 128 | return false; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | return true; | ||
| 133 | }; | ||
| 134 | |||
| 135 | // 提交非税发票申请 | ||
| 136 | const handleSubmit = async () => { | ||
| 137 | if (submitting.value) return; | ||
| 138 | if (!validateForm()) return; | ||
| 139 | |||
| 140 | submitting.value = true; | ||
| 141 | console.log('提交非税开票表单数据:', form); | ||
| 142 | try { | ||
| 143 | await outputInvoiceNoFeisui(form); | ||
| 144 | uni.showToast({ | ||
| 145 | title: '开票申请提交成功', | ||
| 146 | icon: 'success' | ||
| 147 | }); | ||
| 148 | setTimeout(() => { | ||
| 149 | uni.navigateBack(); | ||
| 150 | }, 1500); | ||
| 151 | } catch (error) { | ||
| 152 | console.log('非税开票失败:', error); | ||
| 153 | submitting.value = false; | ||
| 154 | } finally { | ||
| 155 | submitting.value = false; | ||
| 156 | } | ||
| 157 | }; | ||
| 158 | </script> | ||
| 159 | |||
| 160 | <style lang="scss" scoped> | ||
| 161 | .invoice-apply { | ||
| 162 | min-height: 100vh; | ||
| 163 | background: #f5f7fa; | ||
| 164 | padding-bottom: 140rpx; | ||
| 165 | } | ||
| 166 | |||
| 167 | .content { | ||
| 168 | padding: 20rpx; | ||
| 169 | } | ||
| 170 | |||
| 171 | .section-header { | ||
| 172 | display: flex; | ||
| 173 | justify-content: space-between; | ||
| 174 | align-items: center; | ||
| 175 | padding: 20rpx 0; | ||
| 176 | margin-bottom: 10rpx; | ||
| 177 | |||
| 178 | .section-title { | ||
| 179 | font-size: 32rpx; | ||
| 180 | font-weight: 600; | ||
| 181 | color: #303133; | ||
| 182 | } | ||
| 183 | |||
| 184 | .section-hint { | ||
| 185 | font-size: 24rpx; | ||
| 186 | color: #909399; | ||
| 187 | padding: 6rpx 16rpx; | ||
| 188 | background: #f5f5f5; | ||
| 189 | border-radius: 8rpx; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | .form-item { | ||
| 194 | background: #fff; | ||
| 195 | padding: 24rpx; | ||
| 196 | margin-bottom: 20rpx; | ||
| 197 | border-radius: 16rpx; | ||
| 198 | border: 1rpx solid #e9ecef; | ||
| 199 | |||
| 200 | &.column { | ||
| 201 | display: flex; | ||
| 202 | flex-direction: column; | ||
| 203 | } | ||
| 204 | |||
| 205 | .label { | ||
| 206 | font-size: 28rpx; | ||
| 207 | color: #333; | ||
| 208 | font-weight: 500; | ||
| 209 | margin-bottom: 16rpx; | ||
| 210 | } | ||
| 211 | |||
| 212 | .input { | ||
| 213 | width: 100%; | ||
| 214 | font-size: 28rpx; | ||
| 215 | color: #333; | ||
| 216 | padding: 0 24rpx; | ||
| 217 | box-sizing: border-box; | ||
| 218 | background: transparent; | ||
| 219 | min-width: 0; | ||
| 220 | text-align: left; | ||
| 221 | border: 1rpx solid #e4e7ed; | ||
| 222 | border-radius: 8rpx; | ||
| 223 | height: 80rpx; | ||
| 224 | line-height: 80rpx; | ||
| 225 | |||
| 226 | &::placeholder { | ||
| 227 | color: #999; | ||
| 228 | font-size: 28rpx; | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | .hint { | ||
| 233 | font-size: 24rpx; | ||
| 234 | color: #909399; | ||
| 235 | margin-top: 8rpx; | ||
| 236 | } | ||
| 237 | |||
| 238 | .amount { | ||
| 239 | font-size: 36rpx; | ||
| 240 | color: #AD181F; | ||
| 241 | font-weight: 600; | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | /* 发票类型选择 */ | ||
| 246 | .type-select { | ||
| 247 | display: flex; | ||
| 248 | gap: 20rpx; | ||
| 249 | margin-top: 10rpx; | ||
| 250 | } | ||
| 251 | |||
| 252 | .type-option { | ||
| 253 | flex: 1; | ||
| 254 | display: flex; | ||
| 255 | align-items: center; | ||
| 256 | padding: 20rpx; | ||
| 257 | border: 2rpx solid #e4e7ed; | ||
| 258 | border-radius: 12rpx; | ||
| 259 | background: #fafafa; | ||
| 260 | transition: all 0.3s; | ||
| 261 | |||
| 262 | &.active { | ||
| 263 | border-color: #409eff; | ||
| 264 | background: #f0f6ff; | ||
| 265 | } | ||
| 266 | |||
| 267 | .type-icon { | ||
| 268 | width: 60rpx; | ||
| 269 | height: 60rpx; | ||
| 270 | background: #f5f7ff; | ||
| 271 | border-radius: 8rpx; | ||
| 272 | display: flex; | ||
| 273 | align-items: center; | ||
| 274 | justify-content: center; | ||
| 275 | margin-right: 16rpx; | ||
| 276 | |||
| 277 | .icon-text { | ||
| 278 | font-size: 28rpx; | ||
| 279 | font-weight: 600; | ||
| 280 | color: #409eff; | ||
| 281 | } | ||
| 282 | |||
| 283 | &.enterprise { | ||
| 284 | background: #f0f6ff; | ||
| 285 | |||
| 286 | .icon-text { | ||
| 287 | color: #1561CB; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | &.active .type-icon { | ||
| 293 | background: #409eff; | ||
| 294 | |||
| 295 | .icon-text { | ||
| 296 | color: #fff; | ||
| 297 | } | ||
| 298 | |||
| 299 | &.enterprise { | ||
| 300 | background: #1561CB; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | .type-info { | ||
| 305 | display: flex; | ||
| 306 | flex-direction: column; | ||
| 307 | |||
| 308 | .type-name { | ||
| 309 | font-size: 28rpx; | ||
| 310 | font-weight: 600; | ||
| 311 | color: #303133; | ||
| 312 | } | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | /* 提交按钮 */ | ||
| 317 | .btn-wrap { | ||
| 318 | width: 100%; | ||
| 319 | background-color: #fff; | ||
| 320 | padding: 30rpx; | ||
| 321 | position: fixed; | ||
| 322 | bottom: 0; | ||
| 323 | left: 0; | ||
| 324 | right: 0; | ||
| 325 | box-sizing: border-box; | ||
| 326 | box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05); | ||
| 327 | } | ||
| 328 | |||
| 329 | .submit-btn { | ||
| 330 | height: 88rpx; | ||
| 331 | line-height: 88rpx; | ||
| 332 | border-radius: 44rpx; | ||
| 333 | width: 100%; | ||
| 334 | background: linear-gradient(135deg, #AD181F 0%, #c42a2a 100%); | ||
| 335 | color: #fff; | ||
| 336 | font-size: 32rpx; | ||
| 337 | text-align: center; | ||
| 338 | font-weight: 500; | ||
| 339 | |||
| 340 | &.loading { | ||
| 341 | background: #c0c4cc; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
pages/invoice/plainInvoice.vue
0 → 100644
| 1 | <template> | ||
| 2 | <view class="invoice-apply"> | ||
| 3 | <view class="content"> | ||
| 4 | <!-- 发票类型 --> | ||
| 5 | <view class="form-item"> | ||
| 6 | <text class="label">发票类型</text> | ||
| 7 | <view class="type-select"> | ||
| 8 | <!-- <view | ||
| 9 | :class="{ active: form.invoiceType === '2' }" | ||
| 10 | class="type-option" | ||
| 11 | @click="form.invoiceType = '2'" | ||
| 12 | > | ||
| 13 | <view class="type-icon">个</view> | ||
| 14 | <view class="type-info"> | ||
| 15 | <text class="type-name">非税票</text> | ||
| 16 | <text class="type-desc">个人/非企业单位</text> | ||
| 17 | </view> | ||
| 18 | </view> --> | ||
| 19 | <view | ||
| 20 | :class="{ active: form.invoiceType === '1' }" | ||
| 21 | class="type-option" | ||
| 22 | @click="form.invoiceType = '1'" | ||
| 23 | > | ||
| 24 | <view class="type-icon enterprise">企</view> | ||
| 25 | <view class="type-info"> | ||
| 26 | <text class="type-name">增值税发票</text> | ||
| 27 | <text class="type-desc">企业/可抵扣</text> | ||
| 28 | </view> | ||
| 29 | </view> | ||
| 30 | </view> | ||
| 31 | </view> | ||
| 32 | |||
| 33 | <!-- 发票抬头 --> | ||
| 34 | <view class="form-item column"> | ||
| 35 | <text class="label">发票抬头</text> | ||
| 36 | <input | ||
| 37 | v-model="form.name" | ||
| 38 | class="input" | ||
| 39 | placeholder="请输入公司全称或个人姓名" | ||
| 40 | /> | ||
| 41 | <text class="hint">请确保发票抬头与公司营业执照或个人身份证上的名称一致。</text> | ||
| 42 | </view> | ||
| 43 | |||
| 44 | <!-- 纳税人识别号(企业才显示) --> | ||
| 45 | <view v-if="form.invoiceType === '1'" class="form-item column"> | ||
| 46 | <text class="label">纳税人识别号</text> | ||
| 47 | <input | ||
| 48 | v-model="form.taxno" | ||
| 49 | class="input" | ||
| 50 | maxlength="20" | ||
| 51 | placeholder="请输入纳税人识别号" | ||
| 52 | /> | ||
| 53 | <text class="hint">企业税务登记证上的号码,一般为 15、18 或 20 位</text> | ||
| 54 | </view> | ||
| 55 | |||
| 56 | <!-- 接收方式 --> | ||
| 57 | <view class="form-item"> | ||
| 58 | <text class="label">接收方式</text> | ||
| 59 | <view class="method-select"> | ||
| 60 | <view class="method-option active"> | ||
| 61 | <view class="method-icon">邮</view> | ||
| 62 | <view class="method-info"> | ||
| 63 | <text class="method-name">电子发票</text> | ||
| 64 | <text class="method-desc">发送至邮箱</text> | ||
| 65 | </view> | ||
| 66 | <view class="method-tag">推荐</view> | ||
| 67 | </view> | ||
| 68 | </view> | ||
| 69 | </view> | ||
| 70 | |||
| 71 | <!-- 开票金额 --> | ||
| 72 | <!-- <view class="form-item"> | ||
| 73 | <text class="label">开票金额</text> | ||
| 74 | <text class="amount">¥ {{ (Number(form.amount)).toFixed(2) }}</text> | ||
| 75 | </view> --> | ||
| 76 | |||
| 77 | <!-- 接收邮箱 --> | ||
| 78 | <view class="form-item column"> | ||
| 79 | <text class="label">接收邮箱号码</text> | ||
| 80 | <input | ||
| 81 | v-model="form.phone" | ||
| 82 | class="input" | ||
| 83 | placeholder="请输入接收发票的邮箱号码" | ||
| 84 | type="text" | ||
| 85 | /> | ||
| 86 | <text class="hint">电子发票将在 3-5 个工作日内发送至该邮箱</text> | ||
| 87 | </view> | ||
| 88 | </view> | ||
| 89 | |||
| 90 | <!-- 提交按钮 --> | ||
| 91 | <view class="btn-wrap"> | ||
| 92 | <view :class="{ loading: submitting }" class="submit-btn" @click="handleSubmit"> | ||
| 93 | {{ submitting ? '提交中...' : '提交申请' }} | ||
| 94 | </view> | ||
| 95 | </view> | ||
| 96 | </view> | ||
| 97 | </template> | ||
| 98 | |||
| 99 | <script setup> | ||
| 100 | import {ref, reactive} from 'vue'; | ||
| 101 | import {onLoad} from '@dcloudio/uni-app'; | ||
| 102 | import {outputInvoiceNo} from '@/common/api.js'; | ||
| 103 | |||
| 104 | const submitting = ref(false); | ||
| 105 | const type = ref(0) //1个人订单只开普票 | ||
| 106 | // 表单数据(与PC端字段完全对齐) | ||
| 107 | const form = reactive({ | ||
| 108 | invoiceType: '1', // 1=企业 2=个人 | ||
| 109 | deliveryMethod: '1', // 接收方式:1=电子发票 | ||
| 110 | name: '', // 发票抬头 | ||
| 111 | taxno: '', // 纳税人识别号 | ||
| 112 | phone: '', // 邮箱 | ||
| 113 | amount: 0, // 金额 | ||
| 114 | id: '' // 订单ID | ||
| 115 | }); | ||
| 116 | |||
| 117 | // 页面加载(接收PC端传来的参数) | ||
| 118 | onLoad((options) => { | ||
| 119 | if (options.id || options.orderId) { | ||
| 120 | form.id = options.id || options.orderId; | ||
| 121 | form.amount = options.amount; | ||
| 122 | } | ||
| 123 | if (options.invoiceType) { | ||
| 124 | form.invoiceType = options.invoiceType; | ||
| 125 | } | ||
| 126 | type.value = options.type ?? 0 | ||
| 127 | console.log(options) | ||
| 128 | }); | ||
| 129 | |||
| 130 | // 表单验证 | ||
| 131 | const validateForm = () => { | ||
| 132 | // 发票抬头校验 | ||
| 133 | if (!form.name) { | ||
| 134 | uni.showToast({title: '请输入发票抬头', icon: 'none'}); | ||
| 135 | return false; | ||
| 136 | } | ||
| 137 | if (form.name.length < 2 || form.name.length > 100) { | ||
| 138 | uni.showToast({title: '发票抬头长度在2-100个字符之间', icon: 'none'}); | ||
| 139 | return false; | ||
| 140 | } | ||
| 141 | |||
| 142 | // 企业必须填纳税人识别号 | ||
| 143 | if (form.invoiceType === '1' && !form.taxno) { | ||
| 144 | uni.showToast({title: '请输入纳税人识别号', icon: 'none'}); | ||
| 145 | return false; | ||
| 146 | } | ||
| 147 | |||
| 148 | // 纳税人识别号格式校验(同PC) | ||
| 149 | if (form.invoiceType === '1') { | ||
| 150 | const taxReg = /^[A-Z0-9]{15}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$/; | ||
| 151 | if (!taxReg.test(form.taxno)) { | ||
| 152 | uni.showToast({title: '纳税人识别号格式不正确', icon: 'none'}); | ||
| 153 | return false; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | // 邮箱校验 | ||
| 158 | if (!form.phone) { | ||
| 159 | uni.showToast({title: '请输入接收邮箱', icon: 'none'}); | ||
| 160 | return false; | ||
| 161 | } | ||
| 162 | const phoneReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; | ||
| 163 | if (!phoneReg.test(form.phone)) { | ||
| 164 | uni.showToast({title: '请输入正确的邮箱地址', icon: 'none'}); | ||
| 165 | return false; | ||
| 166 | } | ||
| 167 | |||
| 168 | return true; | ||
| 169 | }; | ||
| 170 | |||
| 171 | // 提交发票申请(与PC逻辑完全一致) | ||
| 172 | const handleSubmit = async () => { | ||
| 173 | if (submitting.value) return; | ||
| 174 | if (!validateForm()) return; | ||
| 175 | |||
| 176 | submitting.value = true; | ||
| 177 | console.log('提交表单数据:', form); | ||
| 178 | try { | ||
| 179 | await outputInvoiceNo(form); | ||
| 180 | uni.showToast({ | ||
| 181 | title: '开票申请提交成功', | ||
| 182 | icon: 'success' | ||
| 183 | }); | ||
| 184 | setTimeout(() => { | ||
| 185 | uni.navigateBack(); | ||
| 186 | }, 1500); | ||
| 187 | } catch (error) { | ||
| 188 | console.log(error) | ||
| 189 | submitting.value = false; | ||
| 190 | // 错误已由 request.js 处理 | ||
| 191 | } finally { | ||
| 192 | submitting.value = false; | ||
| 193 | } | ||
| 194 | }; | ||
| 195 | </script> | ||
| 196 | |||
| 197 | <style lang="scss" scoped> | ||
| 198 | .invoice-apply { | ||
| 199 | min-height: 100vh; | ||
| 200 | background: #f5f7fa; | ||
| 201 | } | ||
| 202 | |||
| 203 | .content { | ||
| 204 | padding: 20rpx; | ||
| 205 | } | ||
| 206 | |||
| 207 | .form-item { | ||
| 208 | background: #fff; | ||
| 209 | padding: 24rpx; | ||
| 210 | margin-bottom: 20rpx; | ||
| 211 | border-radius: 16rpx; | ||
| 212 | |||
| 213 | &.column { | ||
| 214 | display: flex; | ||
| 215 | flex-direction: column; | ||
| 216 | } | ||
| 217 | |||
| 218 | .label { | ||
| 219 | font-size: 28rpx; | ||
| 220 | color: #333; | ||
| 221 | font-weight: 500; | ||
| 222 | margin-bottom: 16rpx; | ||
| 223 | } | ||
| 224 | |||
| 225 | .input { | ||
| 226 | width: 100%; | ||
| 227 | font-size: 28rpx; | ||
| 228 | color: #333; | ||
| 229 | padding: 0 24rpx; | ||
| 230 | box-sizing: border-box; | ||
| 231 | background: transparent; | ||
| 232 | min-width: 0; | ||
| 233 | text-align: left; | ||
| 234 | border: 1rpx solid #ccc; | ||
| 235 | border-radius: 8rpx; | ||
| 236 | height: 80rpx; | ||
| 237 | line-height: 80rpx; | ||
| 238 | |||
| 239 | &::placeholder { | ||
| 240 | color: #999; | ||
| 241 | font-size: 28rpx; | ||
| 242 | line-height: 80rpx; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | .form-item.column .input { | ||
| 247 | width: 100%; | ||
| 248 | display: block; | ||
| 249 | } | ||
| 250 | |||
| 251 | .hint { | ||
| 252 | font-size: 24rpx; | ||
| 253 | color: #909399; | ||
| 254 | margin-top: 8rpx; | ||
| 255 | } | ||
| 256 | |||
| 257 | .amount { | ||
| 258 | font-size: 32rpx; | ||
| 259 | color: #AD181F; | ||
| 260 | font-weight: 600; | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /* 发票类型选择 */ | ||
| 265 | .type-select { | ||
| 266 | display: flex; | ||
| 267 | gap: 20rpx; | ||
| 268 | margin-top: 10rpx; | ||
| 269 | } | ||
| 270 | |||
| 271 | .type-option { | ||
| 272 | flex: 1; | ||
| 273 | display: flex; | ||
| 274 | align-items: center; | ||
| 275 | padding: 20rpx; | ||
| 276 | border: 2rpx solid #e4e7ed; | ||
| 277 | border-radius: 12rpx; | ||
| 278 | background: #fafafa; | ||
| 279 | |||
| 280 | &.active { | ||
| 281 | border-color: #AD181F; | ||
| 282 | background: #FFF5F5; | ||
| 283 | } | ||
| 284 | |||
| 285 | .type-icon { | ||
| 286 | width: 60rpx; | ||
| 287 | height: 60rpx; | ||
| 288 | background: #f5f7ff; | ||
| 289 | border-radius: 8rpx; | ||
| 290 | display: flex; | ||
| 291 | align-items: center; | ||
| 292 | justify-content: center; | ||
| 293 | font-size: 24rpx; | ||
| 294 | color: #409eff; | ||
| 295 | font-weight: 600; | ||
| 296 | margin-right: 16rpx; | ||
| 297 | |||
| 298 | &.enterprise { | ||
| 299 | background: #f0f6ff; | ||
| 300 | color: #1561CB; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | &.active .type-icon { | ||
| 305 | background: #AD181F; | ||
| 306 | color: #fff; | ||
| 307 | } | ||
| 308 | |||
| 309 | .type-info { | ||
| 310 | display: flex; | ||
| 311 | flex-direction: column; | ||
| 312 | |||
| 313 | .type-name { | ||
| 314 | font-size: 28rpx; | ||
| 315 | font-weight: 600; | ||
| 316 | color: #303133; | ||
| 317 | } | ||
| 318 | |||
| 319 | .type-desc { | ||
| 320 | font-size: 22rpx; | ||
| 321 | color: #909399; | ||
| 322 | margin-top: 4rpx; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | /* 接收方式选择 */ | ||
| 328 | .method-select { | ||
| 329 | margin-top: 10rpx; | ||
| 330 | } | ||
| 331 | |||
| 332 | .method-option { | ||
| 333 | display: flex; | ||
| 334 | align-items: center; | ||
| 335 | padding: 20rpx; | ||
| 336 | border: 2rpx solid #e4e7ed; | ||
| 337 | border-radius: 12rpx; | ||
| 338 | background: #fafafa; | ||
| 339 | |||
| 340 | &.active { | ||
| 341 | border-color: #AD181F; | ||
| 342 | background: #FFF5F5; | ||
| 343 | } | ||
| 344 | |||
| 345 | .method-icon { | ||
| 346 | width: 60rpx; | ||
| 347 | height: 60rpx; | ||
| 348 | background: #f5f7ff; | ||
| 349 | border-radius: 12rpx; | ||
| 350 | display: flex; | ||
| 351 | align-items: center; | ||
| 352 | justify-content: center; | ||
| 353 | font-size: 24rpx; | ||
| 354 | color: #409eff; | ||
| 355 | font-weight: 600; | ||
| 356 | margin-right: 16rpx; | ||
| 357 | } | ||
| 358 | |||
| 359 | &.active .method-icon { | ||
| 360 | background: #AD181F; | ||
| 361 | color: #fff; | ||
| 362 | } | ||
| 363 | |||
| 364 | .method-info { | ||
| 365 | flex: 1; | ||
| 366 | display: flex; | ||
| 367 | flex-direction: column; | ||
| 368 | |||
| 369 | .method-name { | ||
| 370 | font-size: 28rpx; | ||
| 371 | font-weight: 600; | ||
| 372 | color: #303133; | ||
| 373 | } | ||
| 374 | |||
| 375 | .method-desc { | ||
| 376 | font-size: 22rpx; | ||
| 377 | color: #909399; | ||
| 378 | margin-top: 4rpx; | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | .method-tag { | ||
| 383 | font-size: 20rpx; | ||
| 384 | color: #67C23A; | ||
| 385 | background: #f0f9f0; | ||
| 386 | padding: 4rpx 12rpx; | ||
| 387 | border-radius: 6rpx; | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | /* 提交按钮 */ | ||
| 392 | .btn-wrap { | ||
| 393 | width: 100%; | ||
| 394 | background-color: #fff; | ||
| 395 | padding: 30rpx; | ||
| 396 | position: fixed; | ||
| 397 | bottom: 0; | ||
| 398 | left: 0; | ||
| 399 | right: 0; | ||
| 400 | box-sizing: border-box; | ||
| 401 | } | ||
| 402 | |||
| 403 | .submit-btn { | ||
| 404 | height: 88rpx; | ||
| 405 | line-height: 88rpx; | ||
| 406 | border-radius: 44rpx; | ||
| 407 | width: 100%; | ||
| 408 | background: #AD181F; | ||
| 409 | color: #fff; | ||
| 410 | font-size: 32rpx; | ||
| 411 | text-align: center; | ||
| 412 | font-weight: 500; | ||
| 413 | |||
| 414 | &.loading { | ||
| 415 | background: #c0c4cc; | ||
| 416 | } | ||
| 417 | } | ||
| 418 | </style> |
| ... | @@ -432,7 +432,7 @@ const handlePay = async (item) => { | ... | @@ -432,7 +432,7 @@ const handlePay = async (item) => { |
| 432 | const makeInvoiceFN = (item) => { | 432 | const makeInvoiceFN = (item) => { |
| 433 | needRefresh.value = true; | 433 | needRefresh.value = true; |
| 434 | uni.navigateTo({ | 434 | uni.navigateTo({ |
| 435 | url: `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}&type=1` | 435 | url: `/pages/invoice/applyFeisui?orderId=${item.id}&amount=${item.price}&type=1` |
| 436 | }); | 436 | }); |
| 437 | }; | 437 | }; |
| 438 | 438 | ... | ... |
-
Please register or sign in to post a comment