Files
lucky_shop/pages_tool/order/invoice.vue
ZF sun 4ae21c5d7a refactor: 完成多语言国际化全量替换与补充
1. 新增大量中英文语言包文件,覆盖自定义组件、页面、工具类的文案
2. 替换硬编码文本为多语言调用,统一全局文案管理
3. 优化语言包结构,拆分公共模块与业务模块
4. 修复部分语言加载与国际化适配问题
2026-06-26 17:32:35 +08:00

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>