1. 新增大量中英文语言包文件,覆盖自定义组件、页面、工具类的文案 2. 替换硬编码文本为多语言调用,统一全局文案管理 3. 优化语言包结构,拆分公共模块与业务模块 4. 修复部分语言加载与国际化适配问题
519 lines
12 KiB
Vue
519 lines
12 KiB
Vue
<template>
|
|
<view :style="themeColor" class="invoice-container">
|
|
<view class="invoice-header">
|
|
<view class="header-title">{{ $lang('header.title') }}</view>
|
|
<view class="header-desc">{{ $lang('header.desc') }}</view>
|
|
</view>
|
|
|
|
<view class="invoice-section">
|
|
<view class="section-title">{{ $lang('invoiceType.title') }}</view>
|
|
<view class="invoice-type-list">
|
|
<view
|
|
class="type-item"
|
|
:class="{ active: formData.invoice_type == 1 }"
|
|
@click="selectType(1)"
|
|
>
|
|
<view class="type-icon">
|
|
<text class="iconfont icon-zhifubao"></text>
|
|
</view>
|
|
<view class="type-name">{{ $lang('invoiceType.paper') }}</view>
|
|
<view class="type-desc">{{ $lang('invoiceType.paperDesc') }}</view>
|
|
</view>
|
|
<view
|
|
class="type-item"
|
|
:class="{ active: formData.invoice_type == 2 }"
|
|
@click="selectType(2)"
|
|
>
|
|
<view class="type-icon">
|
|
<text class="iconfont icon-weixinzhifu"></text>
|
|
</view>
|
|
<view class="type-name">{{ $lang('invoiceType.electronic') }}</view>
|
|
<view class="type-desc">{{ $lang('invoiceType.electronicDesc') }}</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="invoice-section">
|
|
<view class="section-title">{{ $lang('invoiceTitle.title') }}</view>
|
|
<view class="title-type-switch">
|
|
<view
|
|
class="switch-item"
|
|
:class="{ active: formData.invoice_title_type == 1 }"
|
|
@click="selectTitleType(1)"
|
|
>
|
|
{{ $lang('invoiceTitle.personal') }}
|
|
</view>
|
|
<view
|
|
class="switch-item"
|
|
:class="{ active: formData.invoice_title_type == 2 }"
|
|
@click="selectTitleType(2)"
|
|
>
|
|
{{ $lang('invoiceTitle.company') }}
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<view class="form-label">
|
|
<text class="required">*</text>
|
|
{{ $lang('invoiceTitle.label') }}
|
|
</view>
|
|
<input
|
|
class="form-input"
|
|
type="text"
|
|
v-model="formData.invoice_title"
|
|
:placeholder="formData.invoice_title_type == 1 ? $lang('invoiceTitle.personalPlaceholder') : $lang('invoiceTitle.companyPlaceholder')"
|
|
/>
|
|
</view>
|
|
|
|
<view class="form-item" v-if="formData.invoice_title_type == 2">
|
|
<view class="form-label">
|
|
<text class="required">*</text>
|
|
{{ $lang('invoiceTitle.taxpayerId') }}
|
|
</view>
|
|
<input
|
|
class="form-input"
|
|
type="text"
|
|
v-model="formData.taxpayer_id"
|
|
:placeholder="$lang('invoiceTitle.taxpayerIdPlaceholder')"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="invoice-section">
|
|
<view class="section-title">{{ $lang('invoiceContent.title') }}</view>
|
|
<view class="form-item">
|
|
<view class="form-label">{{ $lang('invoiceContent.label') }}</view>
|
|
<picker mode="selector" :range="invoiceContentList" range-key="name" @change="onContentChange">
|
|
<view class="picker-value">
|
|
<text v-if="formData.invoice_content">{{ formData.invoice_content }}</text>
|
|
<text class="placeholder" v-else>{{ $lang('invoiceContent.placeholder') }}</text>
|
|
<text class="iconfont icon-youjiantou"></text>
|
|
</view>
|
|
</picker>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="invoice-section" v-if="formData.invoice_type == 1">
|
|
<view class="section-title">{{ $lang('mailInfo.title') }}</view>
|
|
<view class="form-item">
|
|
<view class="form-label">
|
|
<text class="required">*</text>
|
|
{{ $lang('mailInfo.receiver') }}
|
|
</view>
|
|
<input
|
|
class="form-input"
|
|
type="text"
|
|
v-model="formData.receiver_name"
|
|
:placeholder="$lang('mailInfo.receiverPlaceholder')"
|
|
/>
|
|
</view>
|
|
<view class="form-item">
|
|
<view class="form-label">
|
|
<text class="required">*</text>
|
|
{{ $lang('mailInfo.mobile') }}
|
|
</view>
|
|
<input
|
|
class="form-input"
|
|
type="number"
|
|
v-model="formData.receiver_mobile"
|
|
:placeholder="$lang('mailInfo.mobilePlaceholder')"
|
|
/>
|
|
</view>
|
|
<view class="form-item">
|
|
<view class="form-label">
|
|
<text class="required">*</text>
|
|
{{ $lang('mailInfo.address') }}
|
|
</view>
|
|
<pick-regions :level="3" @finish="onRegionFinish">
|
|
<view class="picker-value">
|
|
<text v-if="formData.region">{{ formData.region }}</text>
|
|
<text class="placeholder" v-else>{{ $lang('mailInfo.regionPlaceholder') }}</text>
|
|
<text class="iconfont icon-youjiantou"></text>
|
|
</view>
|
|
</pick-regions>
|
|
</view>
|
|
<view class="form-item">
|
|
<view class="form-label">
|
|
<text class="required">*</text>
|
|
{{ $lang('mailInfo.detailAddress') }}
|
|
</view>
|
|
<input
|
|
class="form-input"
|
|
type="text"
|
|
v-model="formData.address"
|
|
:placeholder="$lang('mailInfo.detailAddressPlaceholder')"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="invoice-section" v-if="formData.invoice_type == 2">
|
|
<view class="section-title">{{ $lang('emailInfo.title') }}</view>
|
|
<view class="form-item">
|
|
<view class="form-label">
|
|
<text class="required">*</text>
|
|
{{ $lang('emailInfo.receiverEmail') }}
|
|
</view>
|
|
<input
|
|
class="form-input"
|
|
type="text"
|
|
v-model="formData.invoice_email"
|
|
:placeholder="$lang('emailInfo.emailPlaceholder')"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="invoice-fee" v-if="invoiceData && invoiceData.invoice_money > 0">
|
|
<view class="fee-item">
|
|
<text class="fee-label">{{ $lang('fee.taxFee') }}</text>
|
|
<text class="fee-value">¥{{ invoiceData.invoice_money }}</text>
|
|
</view>
|
|
<view class="fee-item" v-if="formData.invoice_type == 1 && invoiceData.invoice_delivery_money > 0">
|
|
<text class="fee-label">{{ $lang('fee.deliveryFee') }}</text>
|
|
<text class="fee-value">¥{{ invoiceData.invoice_delivery_money }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="invoice-footer">
|
|
<view class="footer-tip">{{ $lang('footer.tip', { days: 3 }) }}</view>
|
|
<view class="submit-btn" @click="submitInvoice">{{ $lang('footer.submit') }}</view>
|
|
</view>
|
|
|
|
<loading-cover ref="loadingCover"></loading-cover>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
orderId: 0,
|
|
formData: {
|
|
invoice_type: 2,
|
|
invoice_title_type: 1,
|
|
invoice_title: '',
|
|
taxpayer_id: '',
|
|
invoice_content: '',
|
|
receiver_name: '',
|
|
receiver_mobile: '',
|
|
region: '',
|
|
region_id: '',
|
|
address: '',
|
|
invoice_email: ''
|
|
},
|
|
invoiceContentList: [],
|
|
invoiceData: null
|
|
};
|
|
},
|
|
onLoad(options) {
|
|
if (options.order_id) {
|
|
this.orderId = options.order_id;
|
|
}
|
|
this.getInvoiceConfig();
|
|
},
|
|
methods: {
|
|
selectType(type) {
|
|
this.formData.invoice_type = type;
|
|
},
|
|
selectTitleType(type) {
|
|
this.formData.invoice_title_type = type;
|
|
this.formData.invoice_title = '';
|
|
this.formData.taxpayer_id = '';
|
|
},
|
|
onContentChange(e) {
|
|
const index = e.detail.value;
|
|
if (this.invoiceContentList[index]) {
|
|
this.formData.invoice_content = this.invoiceContentList[index].name;
|
|
}
|
|
},
|
|
onRegionFinish(region) {
|
|
this.formData.region = region.name;
|
|
this.formData.region_id = region.id;
|
|
},
|
|
getInvoiceConfig() {
|
|
this.$api.sendRequest({
|
|
url: '/api/orderinvoice/config',
|
|
data: {
|
|
order_id: this.orderId
|
|
},
|
|
success: res => {
|
|
if (res.code == 0 && res.data) {
|
|
this.invoiceData = res.data;
|
|
this.invoiceContentList = res.data.content_list || [];
|
|
if (res.data.invoice_info) {
|
|
Object.assign(this.formData, res.data.invoice_info);
|
|
}
|
|
} else {
|
|
this.$util.showToast({
|
|
title: res.message || this.$lang('api.getConfigFailed')
|
|
});
|
|
}
|
|
}
|
|
});
|
|
},
|
|
validateForm() {
|
|
if (!this.formData.invoice_title) {
|
|
this.$util.showToast({
|
|
title: this.$lang('validation.pleaseEnterTitle')
|
|
});
|
|
return false;
|
|
}
|
|
if (this.formData.invoice_title_type == 2 && !this.formData.taxpayer_id) {
|
|
this.$util.showToast({
|
|
title: this.$lang('validation.pleaseEnterTaxpayerId')
|
|
});
|
|
return false;
|
|
}
|
|
if (!this.formData.invoice_content) {
|
|
this.$util.showToast({
|
|
title: this.$lang('validation.pleaseSelectContent')
|
|
});
|
|
return false;
|
|
}
|
|
if (this.formData.invoice_type == 1) {
|
|
if (!this.formData.receiver_name) {
|
|
this.$util.showToast({
|
|
title: this.$lang('validation.pleaseEnterReceiver')
|
|
});
|
|
return false;
|
|
}
|
|
if (!this.formData.receiver_mobile) {
|
|
this.$util.showToast({
|
|
title: this.$lang('validation.pleaseEnterMobile')
|
|
});
|
|
return false;
|
|
}
|
|
if (!this.formData.region_id) {
|
|
this.$util.showToast({
|
|
title: this.$lang('validation.pleaseSelectRegion')
|
|
});
|
|
return false;
|
|
}
|
|
if (!this.formData.address) {
|
|
this.$util.showToast({
|
|
title: this.$lang('validation.pleaseEnterAddress')
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
if (this.formData.invoice_type == 2) {
|
|
if (!this.formData.invoice_email) {
|
|
this.$util.showToast({
|
|
title: this.$lang('validation.pleaseEnterEmail')
|
|
});
|
|
return false;
|
|
}
|
|
const emailReg = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
|
|
if (!emailReg.test(this.formData.invoice_email)) {
|
|
this.$util.showToast({
|
|
title: this.$lang('validation.invalidEmail')
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
submitInvoice() {
|
|
if (!this.validateForm()) return;
|
|
this.$refs.loadingCover.show();
|
|
this.$api.sendRequest({
|
|
url: '/api/orderinvoice/submit',
|
|
data: {
|
|
order_id: this.orderId,
|
|
...this.formData
|
|
},
|
|
success: res => {
|
|
this.$refs.loadingCover.hide();
|
|
if (res.code >= 0) {
|
|
this.$util.showToast({
|
|
title: this.$lang('api.submitSuccess')
|
|
});
|
|
setTimeout(() => {
|
|
uni.navigateBack();
|
|
}, 1500);
|
|
} else {
|
|
this.$util.showToast({
|
|
title: res.message || this.$lang('api.submitFailed')
|
|
});
|
|
}
|
|
},
|
|
fail: () => {
|
|
this.$refs.loadingCover.hide();
|
|
this.$util.showToast({
|
|
title: this.$lang('api.networkError')
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.invoice-container {
|
|
min-height: 100vh;
|
|
background: #f5f5f5;
|
|
padding-bottom: 200rpx;
|
|
}
|
|
.invoice-header {
|
|
background: #fff;
|
|
padding: 40rpx 30rpx;
|
|
margin-bottom: 20rpx;
|
|
.header-title {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
.header-desc {
|
|
font-size: 26rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
.invoice-section {
|
|
background: #fff;
|
|
margin-bottom: 20rpx;
|
|
padding: 30rpx;
|
|
.section-title {
|
|
font-size: 30rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
}
|
|
.invoice-type-list {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
.type-item {
|
|
flex: 1;
|
|
border: 2rpx solid #e5e5e5;
|
|
border-radius: 12rpx;
|
|
padding: 30rpx 20rpx;
|
|
text-align: center;
|
|
transition: all 0.3s;
|
|
&.active {
|
|
border-color: #ff6a3d;
|
|
background: #fff5f0;
|
|
}
|
|
.type-icon {
|
|
font-size: 48rpx;
|
|
margin-bottom: 10rpx;
|
|
color: #ff6a3d;
|
|
}
|
|
.type-name {
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
font-weight: bold;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
.type-desc {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
}
|
|
.title-type-switch {
|
|
display: flex;
|
|
border: 2rpx solid #e5e5e5;
|
|
border-radius: 8rpx;
|
|
margin-bottom: 30rpx;
|
|
overflow: hidden;
|
|
.switch-item {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 20rpx 0;
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
&.active {
|
|
background: #ff6a3d;
|
|
color: #fff;
|
|
}
|
|
}
|
|
}
|
|
.form-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 24rpx 0;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.form-label {
|
|
width: 200rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
flex-shrink: 0;
|
|
.required {
|
|
color: #ff4d4f;
|
|
margin-right: 4rpx;
|
|
}
|
|
}
|
|
.form-input {
|
|
flex: 1;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
text-align: right;
|
|
}
|
|
.picker-value {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
.placeholder {
|
|
color: #ccc;
|
|
}
|
|
.icon-youjiantou {
|
|
font-size: 24rpx;
|
|
color: #ccc;
|
|
margin-left: 10rpx;
|
|
}
|
|
}
|
|
}
|
|
.invoice-fee {
|
|
background: #fff;
|
|
padding: 30rpx;
|
|
margin-bottom: 20rpx;
|
|
.fee-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 10rpx 0;
|
|
.fee-label {
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
}
|
|
.fee-value {
|
|
font-size: 28rpx;
|
|
color: #ff4d4f;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
}
|
|
.invoice-footer {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: #fff;
|
|
padding: 20rpx 30rpx;
|
|
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
|
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
.footer-tip {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
text-align: center;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
.submit-btn {
|
|
background: linear-gradient(90deg, #ff8a3d, #ff6a3d);
|
|
color: #fff;
|
|
font-size: 32rpx;
|
|
text-align: center;
|
|
padding: 24rpx 0;
|
|
border-radius: 40rpx;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
</style>
|