Files
lucky_shop/pages_tool/order/invoice.vue
ZF sun 783c10fc62 refactor: 统一项目内国际化key的命名空间格式
本次提交对全项目的国际化调用进行了规范化重构:
1. 为中文/英文语言包新增了avatar和gender的别名映射,兼容旧有headImg/sex写法
2. 修正了lang.js中的注释路径错误
3. 将所有页面和组件内的$lang调用从短路径改为带业务模块前缀的完整命名空间路径,例如将$lang('title')改为$lang('article.list.title')
4. 优化了i18n-lazy.js的注释说明,补充了正确的key命名格式示例
5. 统一了分销、订单、会员等模块的国际化key层级,确保所有语言调用路径一致
2026-07-04 09:24:35 +08:00

519 lines
13 KiB
Vue

<template>
<view :style="themeColor" class="invoice-container">
<view class="invoice-header">
<view class="header-title">{{ $lang('order.invoice.header.title') }}</view>
<view class="header-desc">{{ $lang('order.invoice.header.desc') }}</view>
</view>
<view class="invoice-section">
<view class="section-title">{{ $lang('order.invoice.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('order.invoice.invoiceType.paper') }}</view>
<view class="type-desc">{{ $lang('order.invoice.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('order.invoice.invoiceType.electronic') }}</view>
<view class="type-desc">{{ $lang('order.invoice.invoiceType.electronicDesc') }}</view>
</view>
</view>
</view>
<view class="invoice-section">
<view class="section-title">{{ $lang('order.invoice.invoiceTitle.title') }}</view>
<view class="title-type-switch">
<view
class="switch-item"
:class="{ active: formData.invoice_title_type == 1 }"
@click="selectTitleType(1)"
>
{{ $lang('order.invoice.invoiceTitle.personal') }}
</view>
<view
class="switch-item"
:class="{ active: formData.invoice_title_type == 2 }"
@click="selectTitleType(2)"
>
{{ $lang('order.invoice.invoiceTitle.company') }}
</view>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('order.invoice.invoiceTitle.label') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.invoice_title"
:placeholder="formData.invoice_title_type == 1 ? $lang('order.invoice.invoiceTitle.personalPlaceholder') : $lang('order.invoice.invoiceTitle.companyPlaceholder')"
/>
</view>
<view class="form-item" v-if="formData.invoice_title_type == 2">
<view class="form-label">
<text class="required">*</text>
{{ $lang('order.invoice.invoiceTitle.taxpayerId') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.taxpayer_id"
:placeholder="$lang('order.invoice.invoiceTitle.taxpayerIdPlaceholder')"
/>
</view>
</view>
<view class="invoice-section">
<view class="section-title">{{ $lang('order.invoice.invoiceContent.title') }}</view>
<view class="form-item">
<view class="form-label">{{ $lang('order.invoice.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('order.invoice.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('order.invoice.mailInfo.title') }}</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('order.invoice.mailInfo.receiver') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.receiver_name"
:placeholder="$lang('order.invoice.mailInfo.receiverPlaceholder')"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('order.invoice.mailInfo.mobile') }}
</view>
<input
class="form-input"
type="number"
v-model="formData.receiver_mobile"
:placeholder="$lang('order.invoice.mailInfo.mobilePlaceholder')"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('order.invoice.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('order.invoice.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('order.invoice.mailInfo.detailAddress') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.address"
:placeholder="$lang('order.invoice.mailInfo.detailAddressPlaceholder')"
/>
</view>
</view>
<view class="invoice-section" v-if="formData.invoice_type == 2">
<view class="section-title">{{ $lang('order.invoice.emailInfo.title') }}</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
{{ $lang('order.invoice.emailInfo.receiverEmail') }}
</view>
<input
class="form-input"
type="text"
v-model="formData.invoice_email"
:placeholder="$lang('order.invoice.emailInfo.emailPlaceholder')"
/>
</view>
</view>
<view class="invoice-fee" v-if="invoiceData && invoiceData.invoice_money > 0">
<view class="fee-item">
<text class="fee-label">{{ $lang('order.invoice.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('order.invoice.fee.deliveryFee') }}</text>
<text class="fee-value">¥{{ invoiceData.invoice_delivery_money }}</text>
</view>
</view>
<view class="invoice-footer">
<view class="footer-tip">{{ $lang('order.invoice.footer.tip', { days: 3 }) }}</view>
<view class="submit-btn" @click="submitInvoice">{{ $lang('order.invoice.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('order.invoice.api.getConfigFailed')
});
}
}
});
},
validateForm() {
if (!this.formData.invoice_title) {
this.$util.showToast({
title: this.$lang('order.invoice.validation.pleaseEnterTitle')
});
return false;
}
if (this.formData.invoice_title_type == 2 && !this.formData.taxpayer_id) {
this.$util.showToast({
title: this.$lang('order.invoice.validation.pleaseEnterTaxpayerId')
});
return false;
}
if (!this.formData.invoice_content) {
this.$util.showToast({
title: this.$lang('order.invoice.validation.pleaseSelectContent')
});
return false;
}
if (this.formData.invoice_type == 1) {
if (!this.formData.receiver_name) {
this.$util.showToast({
title: this.$lang('order.invoice.validation.pleaseEnterReceiver')
});
return false;
}
if (!this.formData.receiver_mobile) {
this.$util.showToast({
title: this.$lang('order.invoice.validation.pleaseEnterMobile')
});
return false;
}
if (!this.formData.region_id) {
this.$util.showToast({
title: this.$lang('order.invoice.validation.pleaseSelectRegion')
});
return false;
}
if (!this.formData.address) {
this.$util.showToast({
title: this.$lang('order.invoice.validation.pleaseEnterAddress')
});
return false;
}
}
if (this.formData.invoice_type == 2) {
if (!this.formData.invoice_email) {
this.$util.showToast({
title: this.$lang('order.invoice.validation.pleaseEnterEmail')
});
return false;
}
const emailReg = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
if (!emailReg.test(this.formData.invoice_email)) {
this.$util.showToast({
title: this.$lang('order.invoice.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('order.invoice.api.submitSuccess')
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
this.$util.showToast({
title: res.message || this.$lang('order.invoice.api.submitFailed')
});
}
},
fail: () => {
this.$refs.loadingCover.hide();
this.$util.showToast({
title: this.$lang('order.invoice.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>