c18acc6e by lttnew
2 parents 3915c951 9a38e7d0
......@@ -1780,7 +1780,7 @@ export function cancelPay(id) {
export const outputInvoiceNo = (data) => {
return request({
url: `/common/order/outputInvoiceNo/${data.id}`,
url: `/common/order/outputInvoiceNo/${data.id}?taxno=${data.taxno}&phone=${data.phone}&name=${data.name}&invoiceType=${data.invoiceType}&amount=${data.amount}`,
method: 'post',
params: data
})
......
// dev
// const baseUrl_api = 'http://192.168.1.125:8787'
const baseUrl_api = 'https://tk001.wxjylt.com/stage-api/'
const baseUrl_api = 'http://47.98.186.233:8787'
// const baseUrl_api = 'https://tk001.wxjylt.com/stage-api/'
const loginImage_api = 'https://tk001.wxjylt.com/stage-api'
const payUrl = 'https://wxpay.cmbc.com.cn/mobilePlatform/appserver/lcbpPay.do'
......
<template>
<view class="order-page" :class="{ 'no-scroll': isPopupOpen }">
<view :class="{ 'no-scroll': isPopupOpen }" class="order-page">
<!-- 顶部标签栏 -->
<view class="tab-bar">
<view
v-for="(tab, index) in tabs"
:key="index"
class="tab-item"
:class="{ active: currentTab === tab.type }"
class="tab-item"
@click="switchTab(tab.type)"
>
{{ tab.name }}
</view>
</view>
<!-- 订单列表 -->
<scroll-view
scroll-y
class="order-list-scroll"
:show-scrollbar="false"
:scroll-enabled="!isPopupOpen"
@scrolltolower="loadMore"
:show-scrollbar="false"
class="order-list-scroll"
lower-threshold="200"
scroll-y
@scrolltolower="loadMore"
>
<view class="order-list">
<!-- 有数据才循环 -->
......@@ -36,7 +36,7 @@
<view class="date">
<!-- <image :src="config.baseUrl_api + '/fs/static/calendar@2x.png'" mode="widthFix" style="width:30rpx;height:30rpx;"/> -->
<!-- -->
<text class="value text-primary" >{{ item.wfCode || '——' }}</text>
<text class="value text-primary">{{ item.wfCode || '——' }}</text>
</view>
<view class="status-tags">
<!-- <view
......@@ -50,25 +50,25 @@
{{ getStatusText(item.payStatus) }}
</view> -->
<view
class="status-tag ml-10"
:class="{
'status-wait': item.auditStatus == 0,
'status-pending': item.auditStatus == 1,
'status-success': item.auditStatus == 2,
'status-danger': item.auditStatus == 3
}"
class="status-tag ml-10"
>
{{ getAuditStatusText(item.auditStatus) }}
</view>
</view>
</view>
<!-- 订单编号、缴费编号 -->
<view class="info-row">
<text class="label">订单编号:</text>
<text class="value">{{ item.tradeNo || '——' }}</text>
</view>
<view class="info-row" v-if="item.orderName">
<view v-if="item.orderName" class="info-row">
<text class="label">缴费名称:</text>
<text class="value">{{ item.orderName || '' }}</text>
</view>
......@@ -76,29 +76,29 @@
<text class="label">缴费编号:</text>
</view> -->
<!-- 核心:前2tab仅展示缴费年限,后2tab仅展示人数合计 -->
<view class="info-section flex f-j-s" v-if="item.content">
<view v-if="item.content" class="info-section flex f-j-s">
<!-- 个人/单位会员(仅缴费年限) -->
<view v-if="currentTab === '0' || currentTab === '1'" class="single-info">
<view class="label">缴费年限:</view>
<view class="value">{{ item.content.yearCount || 0 }}</view>
</view>
<view class="line" v-if="currentTab === '0' || currentTab === '1'"></view>
<view v-if="currentTab === '0' || currentTab === '1'" class="line"></view>
<!-- 级位/段位考试(仅人数合计) -->
<view v-if="currentTab === '2' || currentTab === '3' || currentTab === '4'" class="single-info">
<view class="label">人数合计</view>
<view class="value">{{ item.content.personCount || 0 }}</view>
</view>
<view class="line" v-if="currentTab === '2' || currentTab === '3' || currentTab === '4'"></view>
<view v-if="currentTab === '2' || currentTab === '3' || currentTab === '4'" class="line"></view>
<view class="single-info">
<view class="label">订单状态</view>
<view class="value" :class="item.effect == 1 ? 'text-success' : 'text-warning'">
<view :class="item.effect == 1 ? 'text-success' : 'text-warning'" class="value">
{{ item.effect == 1 ? '已生效' : '未生效' }}
</view>
</view>
</view>
<!-- 费用合计 + 缴费方式 -->
<view class="price-section">
<view class="price-row">
......@@ -110,13 +110,13 @@
<text class="price-value">民生付</text>
</view>
</view>
<!-- 按钮组:靠右紧凑展示 -->
<view class="btn-group">
<!-- 已缴费:申请开票/已开票(需要审核通过才能开票) -->
<template v-if="item.payStatus == 1 && item.invoiceStatus != 1&& item.auditStatus == 2">
<button class="btn btn-view-invoice" @click="makeInvoiceFN(item)" :disabled="item.invoiceStatus === 1">
开票
<button :disabled="item.invoiceStatus === 1" class="btn btn-view-invoice" @click="makeInvoiceFN(item)">
开票
</button>
</template>
<!-- 已开票:查看发票 -->
......@@ -131,19 +131,19 @@
</view>
</view>
</view>
<!-- 空状态 -->
<view v-else class="empty">
<image class="empty-img" mode="aspectFit" :src="config.baseUrl_api + '/fs/static/nodata.png'"></image>
<image :src="config.baseUrl_api + '/fs/static/nodata.png'" class="empty-img" mode="aspectFit"></image>
<text class="empty-text">暂无订单记录</text>
</view>
<!-- 加载/无更多提示 -->
<view class="loading-tip" v-if="loading">加载中...</view>
<view class="no-more" v-if="!loading && !hasMore && list.length">没有更多了</view>
<view v-if="loading" class="loading-tip">加载中...</view>
<view v-if="!loading && !hasMore && list.length" class="no-more">没有更多了</view>
</view>
</scroll-view>
<!-- 发票查看弹窗 -->
<view v-if="showInvoicePopup" class="invoice-popup-mask" @click="closeInvoicePopup">
<view class="invoice-popup-content" @click.stop>
......@@ -153,19 +153,19 @@
</view>
<view class="invoice-popup-body">
<view class="invoice-info-list">
<view class="invoice-info-row">
<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 :class="{ 'vat-type': invoiceData.invoiceType == 2 }" class="invoice-type-badge">
{{ 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">
<view v-if="invoiceData.invoiceBuyerTaxno" class="invoice-info-row">
<text class="invoice-info-label">纳税人识别号</text>
<text class="invoice-info-value">{{ invoiceData.invoiceBuyerTaxno }}</text>
</view>
......@@ -173,11 +173,19 @@
<text class="invoice-info-label">接收邮箱</text>
<text class="invoice-info-value">{{ invoiceData.invoicePushPhone || '—' }}</text>
</view>
<view class="invoice-info-row">
<text class="invoice-info-label">开票时间</text>
<text class="invoice-info-value">{{ invoiceData.invoiceTime || '—' }}</text>
</view>
<view class="invoice-info-row">
<text class="invoice-info-label">开票金额</text>
<text class="invoice-info-value">¥{{ invoiceData.price || '—' }}</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>
......@@ -189,7 +197,7 @@
</view>
</view>
</view>
<!-- 自定义取消订单确认弹窗 -->
<view v-if="showCancelPopup" class="popup-mask" @touchmove.stop.prevent @click.stop="closeCancelPopup">
<view class="custom-modal" @click.stop>
......@@ -205,17 +213,17 @@
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
import {
onShow,
onLoad
} from '@dcloudio/uni-app'
import {ref, reactive, onMounted, computed} from 'vue';
import {
onShow,
onLoad
} from '@dcloudio/uni-app'
import * as api from '@/common/api.js'
import config from '@/config.js'
// 获取deptType值(初始值为0,在onMounted中设置实际值)
const deptType = ref(0);
const goToDetail = (item) => {
uni.navigateTo({ url: `/pages/rank/applyDetail?examId=${item.sourceId || item.id}&type=${queryParams.type}` });
uni.navigateTo({url: `/pages/rank/applyDetail?examId=${item.sourceId || item.id}&type=${queryParams.type}`});
}
// 标签栏配置(根据deptType动态生成)
......@@ -226,25 +234,25 @@ const tabs = computed(() => {
if (dt === 6) {
console.log('返回3个tab: 个人会员、单位会员、级位考试');
return [
{ name: '个人会员', type: '0' },
{ name: '单位会员', type: '1' },
{ name: '级位考试', type: '2' }
{name: '个人会员', type: '0'},
{name: '单位会员', type: '1'},
{name: '级位考试', type: '2'}
];
} else if (dt === 2) {
console.log('返回3个tab: 单位会员、段位考试、越段考试');
return [
// { name: '单位会员', type: '1' },
{ name: '段位考试', type: '3' },
{ name: '越段考试', type: '4' }
{name: '段位考试', type: '3'},
{name: '越段考试', type: '4'}
];
} else {
console.log('返回默认5个tab, dt值为:', dt);
return [
{ name: '个人会员', type: '0' },
{ name: '单位会员', type: '1' },
{ name: '级位考试', type: '2' },
{ name: '段位考试', type: '3' },
{ name: '越段考试', type: '4' }
{name: '个人会员', type: '0'},
{name: '单位会员', type: '1'},
{name: '级位考试', type: '2'},
{name: '段位考试', type: '3'},
{name: '越段考试', type: '4'}
];
}
});
......@@ -287,7 +295,7 @@ onLoad((option) => {
deptType.value = Number(app.globalData?.deptType || 0);
const firstType = tabs.value[0]?.type ?? '0';
currentTab.value = option.type || firstType;
queryParams.type = option.type ||firstType;
queryParams.type = option.type || firstType;
initData();
});
......@@ -349,11 +357,11 @@ const getAuditStatusText = (status) => {
const initData = async () => {
loading.value = true;
queryParams.pageNum = pageNum.value;
try {
const res = await api.orderList(queryParams);
console.log("接口返回:", res);
if (!res || !res.rows || res.rows.length === 0) {
hasMore.value = false;
loading.value = false;
......@@ -379,7 +387,7 @@ const initData = async () => {
hasMore.value = res.rows.length === pageSize.value;
} catch (e) {
console.error('订单加载异常:', e);
uni.showToast({ title: '加载失败', icon: 'none' });
uni.showToast({title: '加载失败', icon: 'none'});
hasMore.value = false;
} finally {
loading.value = false;
......@@ -399,13 +407,13 @@ const confirmDel = async () => {
if (!currentOrder.value) return;
try {
await api.deleteOrder(currentOrder.value.id);
uni.showToast({ title: '删除成功', icon: 'success' });
uni.showToast({title: '删除成功', icon: 'success'});
pageNum.value = 1;
list.value = [];
initData();
closeDelPopup();
} catch (e) {
uni.showToast({ title: '删除失败', icon: 'error' });
uni.showToast({title: '删除失败', icon: 'error'});
}
};
......@@ -420,10 +428,10 @@ const closeDelPopup = () => {
const handlePay = async (item) => {
if (item.payStatus !== 0) return;
try {
await api.goPay({ id: item.id });
uni.navigateTo({ url: `/pages/pay/pay?orderId=${item.id}` });
await api.goPay({id: item.id});
uni.navigateTo({url: `/pages/pay/pay?orderId=${item.id}`});
} catch (e) {
uni.showToast({ title: '发起支付失败', icon: 'none' });
uni.showToast({title: '发起支付失败', icon: 'none'});
}
};
......@@ -447,7 +455,9 @@ const viewInvoice = (item) => {
invoiceType: item.invoiceType || 1,
invoiceBuyerName: item.invoiceTitle || item.invoiceBuyerName || '—',
invoiceBuyerTaxno: item.invoiceTaxno || item.invoiceBuyerTaxno || '',
invoicePushPhone: item.invoiceEmail || item.invoicePushPhone || '—'
invoicePushPhone: item.invoiceEmail || item.invoicePushPhone || '—',
price: item.price || '-',
invoiceTime: item.invoiceTime || '—'
};
showInvoicePopup.value = true;
isPopupOpen.value = true;
......@@ -472,13 +482,13 @@ const confirmCancel = async () => {
if (!currentOrder.value) return;
try {
await api.cancelPay(currentOrder.value.id);
uni.showToast({ title: '取消成功', icon: 'success' });
uni.showToast({title: '取消成功', icon: 'success'});
pageNum.value = 1;
list.value = [];
initData();
closeCancelPopup();
} catch (e) {
uni.showToast({ title: '取消失败', icon: 'error' });
uni.showToast({title: '取消失败', icon: 'error'});
}
};
......@@ -496,7 +506,7 @@ const closeCancelPopup = () => {
height: 100vh;
display: flex;
flex-direction: column;
&.no-scroll {
overflow: hidden;
height: 100vh;
......@@ -571,65 +581,65 @@ const closeCancelPopup = () => {
padding-bottom: 20rpx;
// margin-bottom: 20rpx;
// border-bottom: 1rpx dashed #eee;
.date {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 26rpx;
.date-text {
color: #666;
}
}
.status-tags {
display: flex;
gap: 10rpx;
}
.status-tag {
font-size: 22rpx;
padding: 6rpx 16rpx;
border-radius: 20rpx;
&.success {
background: #e6f7ef;
color: #52c41a;
}
&.danger {
background: #fff1f0;
color: #ff4d4f;
}
&.pending {
background: #f5f5f5;
color: #999;
}
&.ml-10 {
margin-left: 10rpx;
}
&.status-wait {
background: #f0f5ff;
color: #597ef7;
border: 1rpx solid rgba(89, 126, 247, 0.3);
}
&.status-pending {
background: #fff7e6;
color: #faad14;
border: 1rpx solid rgba(250, 173, 20, 0.3);
}
&.status-success {
background: #e6f7ef;
color: #52c41a;
border: 1rpx solid rgba(82, 196, 26, 0.3);
}
&.status-danger {
background: #fff1f0;
color: #ff4d4f;
......@@ -665,11 +675,13 @@ const closeCancelPopup = () => {
justify-content: space-around;
margin: 20rpx 0;
}
.line{
.line {
width: 1rpx;
height: 90%;
background: #eee;
}
.single-info {
padding: 16rpx 20rpx;
border-radius: 8rpx;
......@@ -679,17 +691,17 @@ const closeCancelPopup = () => {
color: #999;
text-align: center;
}
.value {
color: #333;
font-weight: 500;
text-align: center;
margin-top: 10rpx;
&.text-success {
color: #52c41a;
}
&.text-warning {
color: #faad14;
}
......@@ -701,26 +713,26 @@ const closeCancelPopup = () => {
// border-top: 1rpx dashed #eee;
// padding-top: 16rpx;
margin-top: 8rpx;
.price-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8rpx 0;
.price-label {
font-size: 26rpx;
color: #333;
}
.price-value {
font-size: 26rpx;
color: #666;
&.text-success {
color: #52c41a;
}
&.text-warning {
color: #faad14;
}
......@@ -731,12 +743,12 @@ const closeCancelPopup = () => {
// 按钮组
.btn-group {
display: flex;
justify-content: flex-end;
justify-content: flex-end;
align-items: center;
gap: 16rpx;
width: 100%;
margin-top: 20rpx;
.btn {
// 固定宽度,所有按钮一样大
width: 160rpx;
......@@ -750,10 +762,12 @@ const closeCancelPopup = () => {
background: transparent;
text-align: center;
margin: 0;
&::after {
&::after {
border: none;
display: none; // 关键:隐藏伪元素
display: none; // 关键:隐藏伪元素
}
&.btn-delete {
background: #fff;
color: #e4393c;
......@@ -765,7 +779,7 @@ const closeCancelPopup = () => {
color: #e4393c;
border: 1rpx solid #e4393c;
}
&.btn-view-invoice {
background: linear-gradient(90deg, #FF755A, #F51722);
color: #fff;
......@@ -879,11 +893,12 @@ const closeCancelPopup = () => {
border: none;
}
}
.code-text {
font-size: 28rpx;
font-weight: 600;
color: #e8341d;
letter-spacing: 1rpx;
font-size: 28rpx;
font-weight: 600;
color: #e8341d;
letter-spacing: 1rpx;
}
// 发票弹窗样式
......@@ -914,13 +929,13 @@ const closeCancelPopup = () => {
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;
......@@ -945,7 +960,7 @@ const closeCancelPopup = () => {
border-radius: 30rpx;
font-size: 24rpx;
font-weight: 500;
&.vat-type {
background: linear-gradient(135deg, #6aaaf2 0%, #178cd7 100%);
}
......@@ -958,17 +973,17 @@ const closeCancelPopup = () => {
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;
......@@ -978,4 +993,4 @@ const closeCancelPopup = () => {
}
}
}
</style>
\ No newline at end of file
</style>
......
......@@ -6,9 +6,9 @@
<text class="label">发票类型</text>
<view class="type-select">
<view
:class="{ active: form.invoiceType === '2' }"
class="type-option"
:class="{ active: form.invoiceType === '1' }"
@click="form.invoiceType = '1'"
@click="form.invoiceType = '2'"
>
<view class="type-icon"></view>
<view class="type-info">
......@@ -17,9 +17,10 @@
</view>
</view>
<view
v-if="type==0"
:class="{ active: form.invoiceType === '1' }"
class="type-option"
:class="{ active: form.invoiceType === '2' }"
@click="form.invoiceType = '2'"
@click="form.invoiceType = '1'"
>
<view class="type-icon enterprise"></view>
<view class="type-info">
......@@ -29,30 +30,30 @@
</view>
</view>
</view>
<!-- 发票抬头 -->
<view class="form-item column">
<text class="label">发票抬头</text>
<input
class="input"
v-model="form.name"
class="input"
placeholder="请输入公司全称或个人姓名"
/>
<text class="hint">请确保发票抬头与公司营业执照或个人身份证上的名称一致。</text>
</view>
<!-- 纳税人识别号(企业才显示) -->
<view class="form-item column" v-if="form.invoiceType === '2'">
<view v-if="form.invoiceType === '1'" class="form-item column">
<text class="label">纳税人识别号</text>
<input
class="input"
v-model="form.taxno"
placeholder="请输入纳税人识别号"
class="input"
maxlength="20"
placeholder="请输入纳税人识别号"
/>
<text class="hint">企业税务登记证上的号码,一般为 15、18 或 20 位</text>
</view>
<!-- 接收方式 -->
<view class="form-item">
<text class="label">接收方式</text>
......@@ -67,29 +68,29 @@
</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 column">
<text class="label">接收邮箱号码</text>
<input
class="input"
v-model="form.phone"
class="input"
placeholder="请输入接收发票的邮箱号码"
type="text"
/>
<text class="hint">电子发票将在 3-5 个工作日内发送至该邮箱</text>
</view>
</view>
<!-- 提交按钮 -->
<view class="btn-wrap">
<view class="submit-btn" :class="{ loading: submitting }" @click="handleSubmit">
<view :class="{ loading: submitting }" class="submit-btn" @click="handleSubmit">
{{ submitting ? '提交中...' : '提交申请' }}
</view>
</view>
......@@ -97,15 +98,15 @@
</template>
<script setup>
import { ref, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { outputInvoiceNo } from '@/common/api.js';
import {ref, reactive} from 'vue';
import {onLoad} from '@dcloudio/uni-app';
import {outputInvoiceNo} from '@/common/api.js';
const submitting = ref(false);
const type = ref(0) //1个人订单只开普票
// 表单数据(与PC端字段完全对齐)
const form = reactive({
invoiceType: '1', // 1=个人 2=企业
invoiceType: '2', // 1=企业 2=个人
deliveryMethod: '1', // 接收方式:1=电子发票
name: '', // 发票抬头
taxno: '', // 纳税人识别号
......@@ -123,46 +124,48 @@ onLoad((options) => {
if (options.invoiceType) {
form.invoiceType = options.invoiceType;
}
type.value = options.type ?? 0
console.log(options)
});
// 表单验证
const validateForm = () => {
// 发票抬头校验
if (!form.name) {
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' });
uni.showToast({title: '发票抬头长度在2-100个字符之间', icon: 'none'});
return false;
}
// 企业必须填纳税人识别号
if (form.invoiceType === '2' && !form.taxno) {
uni.showToast({ title: '请输入纳税人识别号', icon: 'none' });
if (form.invoiceType === '1' && !form.taxno) {
uni.showToast({title: '请输入纳税人识别号', icon: 'none'});
return false;
}
// 纳税人识别号格式校验(同PC)
if (form.invoiceType === '2') {
if (form.invoiceType === '1') {
const taxReg = /^[A-Z0-9]{15}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$/;
if (!taxReg.test(form.taxno)) {
uni.showToast({ title: '纳税人识别号格式不正确', icon: 'none' });
uni.showToast({title: '纳税人识别号格式不正确', icon: 'none'});
return false;
}
}
// 邮箱校验
if (!form.phone) {
uni.showToast({ title: '请输入接收邮箱', icon: 'none' });
uni.showToast({title: '请输入接收邮箱', icon: 'none'});
return false;
}
const phoneReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!phoneReg.test(form.phone)) {
uni.showToast({ title: '请输入正确的邮箱地址', icon: 'none' });
uni.showToast({title: '请输入正确的邮箱地址', icon: 'none'});
return false;
}
return true;
};
......@@ -170,7 +173,7 @@ const validateForm = () => {
const handleSubmit = async () => {
if (submitting.value) return;
if (!validateForm()) return;
submitting.value = true;
console.log('提交表单数据:', form);
try {
......@@ -183,6 +186,7 @@ const handleSubmit = async () => {
uni.navigateBack();
}, 1500);
} catch (error) {
console.log(error)
submitting.value = false;
// 错误已由 request.js 处理
} finally {
......@@ -206,19 +210,19 @@ const handleSubmit = async () => {
padding: 24rpx;
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: 100%;
font-size: 28rpx;
......@@ -239,18 +243,18 @@ const handleSubmit = async () => {
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;
......@@ -273,12 +277,12 @@ const handleSubmit = async () => {
border: 2rpx solid #e4e7ed;
border-radius: 12rpx;
background: #fafafa;
&.active {
border-color: #AD181F;
background: #FFF5F5;
}
.type-icon {
width: 60rpx;
height: 60rpx;
......@@ -291,28 +295,28 @@ const handleSubmit = async () => {
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;
font-weight: 600;
color: #303133;
}
.type-desc {
font-size: 22rpx;
color: #909399;
......@@ -333,12 +337,12 @@ const handleSubmit = async () => {
border: 2rpx solid #e4e7ed;
border-radius: 12rpx;
background: #fafafa;
&.active {
border-color: #AD181F;
background: #FFF5F5;
}
.method-icon {
width: 60rpx;
height: 60rpx;
......@@ -352,30 +356,30 @@ const handleSubmit = async () => {
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;
......@@ -407,7 +411,7 @@ const handleSubmit = async () => {
font-size: 32rpx;
text-align: center;
font-weight: 500;
&.loading {
background: #c0c4cc;
}
......
<template>
<view class="container">
<view class="content">
<view class="card">
<view class="yearRow">
<view class="label">缴费年限</view>
<view class="control">
<image class="icon" @click="minusYear" :src="config.baseUrl_api + '/fs/static/dd_02.png'" mode="widthFix"
v-if="form.payYear > 1"></image>
<image class="icon" :src="config.baseUrl_api + '/fs/static/dd_02_g.png'" mode="widthFix" v-else></image>
<text class="num">{{ form.payYear }}</text>
<image class="icon" :src="config.baseUrl_api + '/fs/static/btn_03.png'" mode="widthFix" @click="plusYear"
v-if="form.payYear < 5"></image>
<image class="icon" :src="config.baseUrl_api + '/fs/static/btn_03_g.png'" mode="widthFix" v-else></image>
</view>
</view>
</view>
<view class="card ">
<view class="row ">
<text class="label">费用合计</text>
<text class="value red">{{ form.payYear * memberFee }}</text>
</view>
</view>
<view class="payRow ">
<radio-group @change="onPayTypeChange">
<label class="radioItem">
<radio value="1" :checked="payType === '1'" class="custom-radio" />
<view class="payInfo">
<image class="icon" :src="config.baseUrl_api + '/fs/static/min.png'" mode="widthFix"></image>
<text>民生付</text>
</view>
</label>
</radio-group>
</view>
<view class="totalRow ">
<text class="label">支付费用合计</text>
<text class="value redBig">{{ memberTotalFee }}</text>
</view>
</view>
<view class="bottomBtn">
<button class="payBtn" @click="handelPay" :loading="isPaying">立即支付 ¥{{ memberTotalFee }}</button>
</view>
</view>
<view class="container">
<view class="content">
<view class="card">
<view class="yearRow">
<view class="label">缴费年限</view>
<view class="control">
<image v-if="form.payYear > 1" :src="config.baseUrl_api + '/fs/static/dd_02.png'" class="icon"
mode="widthFix"
@click="minusYear"></image>
<image v-else :src="config.baseUrl_api + '/fs/static/dd_02_g.png'" class="icon" mode="widthFix"></image>
<text class="num">{{ form.payYear }}</text>
<image v-if="form.payYear < 5" :src="config.baseUrl_api + '/fs/static/btn_03.png'" class="icon"
mode="widthFix"
@click="plusYear"></image>
<image v-else :src="config.baseUrl_api + '/fs/static/btn_03_g.png'" class="icon" mode="widthFix"></image>
</view>
</view>
</view>
<view class="card ">
<view class="row ">
<text class="label">费用合计</text>
<text class="value red">{{ form.payYear * memberFee }}</text>
</view>
</view>
<view class="payRow ">
<radio-group @change="onPayTypeChange">
<label class="radioItem">
<radio :checked="payType === '1'" class="custom-radio" value="1"/>
<view class="payInfo">
<image :src="config.baseUrl_api + '/fs/static/min.png'" class="icon" mode="widthFix"></image>
<text>民生付</text>
</view>
</label>
</radio-group>
</view>
<view class="totalRow ">
<text class="label">支付费用合计</text>
<text class="value redBig">{{ memberTotalFee }}</text>
</view>
</view>
<view class="bottomBtn">
<button :loading="isPaying" class="payBtn" @click="handelPay">立即支付 ¥{{ memberTotalFee }}</button>
</view>
</view>
</template>
<script setup>
import {
ref,
computed,
onMounted
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app';
import to from 'await-to-js'
import * as api from '@/common/api.js'
import {
minShengPay
} from '@/common/pay.js'
import {
ref,
computed,
onMounted
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app';
import to from 'await-to-js'
import * as api from '@/common/api.js'
import {
minShengPay
} from '@/common/pay.js'
import config from '@/config.js'
const form = ref({
payYear: 1
})
// 支付方式
const payType = ref('1')
const isPaying = ref(false)
// 费用与优惠
const memberFee = ref(0)
const memberTotalFee = computed(() => {
return memberFee.value * form.value.payYear
})
onLoad((options) => {
if (options.baseFormData) {
const data = JSON.parse(decodeURIComponent(options.baseFormData))
form.value = {
...data,
payYear: 1 // 年限默认1
}
}
// 初始化接口
getMyMemberCertUnitFeeApi()
})
// 减年限
const minusYear = () => {
if (form.value.payYear > 1) {
form.value.payYear--
}
}
// 加年限(最大 5 年)
const plusYear = () => {
if (form.value.payYear < 5) {
form.value.payYear++
}
}
// 支付方式切换
const onPayTypeChange = (e) => {
payType.value = e.detail.value
}
const handelPay = async () => {
if (memberTotalFee.value <= 0) {
uni.showToast({
title: '支付金额异常',
icon: 'none'
})
return
}
// 显示 loading
uni.showLoading({
title: '创建订单...',
mask: true
})
isPaying.value = true
// 拼接完整参数
const postData = {
...form.value,
payYear: form.value.payYear,
payType: payType.value,
totalFee: memberTotalFee.value
}
// 创建订单
const [orderErr, orderRes] = await to(api.insertSinglePay(postData))
uni.hideLoading()
if (orderErr) {
isPaying.value = false
// uni.showToast({
// title: '创建订单失败',
// icon: 'none'
// })
return
}
if (!orderRes.data?.orderId) {
isPaying.value = false
uni.showToast({
title: '订单创建异常',
icon: 'none'
})
return
}
// 调起支付
const [payErr] = await to(minShengPay(orderRes.data.orderId, orderRes.data.payResult.encryptedData))
isPaying.value = false
// 支付失败不跳转
if (payErr) {
return
}
// 支付成功,跳转页面
uni.redirectTo({
url: `/personal/sucPay?orderId=${orderRes.data.orderId}`
})
}
// 获取会员费
async function getMyMemberCertUnitFeeApi() {
const res = await api.getZtxFeeConfig()
memberFee.value = Number(res.data.personMemberFee || 1500)
}
const form = ref({
payYear: 1
})
// 支付方式
const payType = ref('1')
const isPaying = ref(false)
// 费用与优惠
const memberFee = ref(0)
const memberTotalFee = computed(() => {
return memberFee.value * form.value.payYear
})
onLoad((options) => {
if (options.baseFormData) {
const data = JSON.parse(decodeURIComponent(options.baseFormData))
form.value = {
...data,
payYear: 1 // 年限默认1
}
}
// 初始化接口
getMyMemberCertUnitFeeApi()
})
// 减年限
const minusYear = () => {
if (form.value.payYear > 1) {
form.value.payYear--
}
}
// 加年限(最大 5 年)
const plusYear = () => {
if (form.value.payYear < 5) {
form.value.payYear++
}
}
// 支付方式切换
const onPayTypeChange = (e) => {
payType.value = e.detail.value
}
const handelPay = async () => {
if (memberTotalFee.value <= 0) {
uni.showToast({
title: '支付金额异常',
icon: 'none'
})
return
}
// 显示 loading
uni.showLoading({
title: '创建订单...',
mask: true
})
isPaying.value = true
form.value.validityDate = undefined
// 拼接完整参数
const postData = {
...form.value,
payYear: form.value.payYear,
payType: payType.value,
totalFee: memberTotalFee.value,
}
// 创建订单
const [orderErr, orderRes] = await to(api.insertSinglePay(postData))
uni.hideLoading()
if (orderErr) {
isPaying.value = false
// uni.showToast({
// title: '创建订单失败',
// icon: 'none'
// })
return
}
if (!orderRes.data?.orderId) {
isPaying.value = false
uni.showToast({
title: '订单创建异常',
icon: 'none'
})
return
}
// 调起支付
const [payErr] = await to(minShengPay(orderRes.data.orderId, orderRes.data.payResult.encryptedData))
isPaying.value = false
// 支付失败不跳转
if (payErr) {
return
}
// 支付成功,跳转页面
uni.redirectTo({
url: `/personal/sucPay?orderId=${orderRes.data.orderId}`
})
}
// 获取会员费
async function getMyMemberCertUnitFeeApi() {
const res = await api.getZtxFeeConfig()
memberFee.value = Number(res.data.personMemberFee || 1500)
}
</script>
<style scoped>
.container {
min-height: 100vh;
background-color: #f7f7f7;
}
.content {
padding: 20rpx 20rpx 120rpx;
}
.card {
background: #fff;
border-radius: 8rpx;
padding: 25rpx 20rpx;
margin-bottom: 20rpx;
}
.yearRow {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
.yearRow .label {
font-size: 28rpx;
color: #333;
}
.yearRow .control {
display: flex;
align-items: center;
}
.control image {
width: 50rpx;
height: 50rpx;
}
.yearRow .num {
font-size: 28rpx;
color: #333;
min-width: 80rpx;
text-align: center;
margin: 0 10rpx;
}
.row {
display: flex;
justify-content: space-between;
align-items: center;
}
.row .label {
font-size: 28rpx;
color: #333;
}
.row .value {
font-size: 30rpx;
color: #C4121B;
font-weight: 500;
}
.hintRow {
display: flex;
align-items: flex-start;
font-size: 24rpx;
line-height: 1.4;
}
.hintRow .hintText {
color: #FF8124;
flex: 1;
margin-top: 10rpx;
}
.deductRow {
background: #fff;
padding: 20rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
border-radius: 8rpx;
}
.deductRow .label {
font-size: 28rpx;
color: #333;
}
.deductRow .value {
font-size: 30rpx;
color: #C4121B;
}
.payRow {
background: #fff;
border-radius: 8rpx;
padding: 20rpx 20rpx;
margin-bottom: 20rpx;
}
.radioItem {
display: flex;
align-items: center;
}
.payInfo {
display: flex;
align-items: center;
margin-left: 15rpx;
}
.payInfo .icon {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
.payInfo text {
font-size: 28rpx;
color: #333;
}
.totalRow {
background: #fff;
border-radius: 8rpx;
padding: 20rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10rpx;
}
.totalRow .label {
font-size: 28rpx;
color: #333;
}
.redBig {
font-size: 32rpx;
color: #C4121B;
font-weight: bold;
}
.bottomBtn {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 20rpx;
background: #fff;
border-top: 1rpx solid #eee;
}
.payBtn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #C4121B;
color: #fff;
border-radius: 8rpx;
font-size: 32rpx;
text-align: center;
border: none;
}
.payBtn[disabled] {
background-color: #ccc;
color: #999;
}
.red {
color: #C4121B;
}
.icon {
width: 30px;
}
::v-deep .custom-radio .wx-radio-input {
width: 30rpx;
height: 30rpx;
border-radius: 50%;
border: 2rpx solid #ccc;
}
::v-deep .custom-radio .wx-radio-input.wx-radio-input-checked {
border-color: #C4121B !important;
background: #C4121B !important;
}
</style>
\ No newline at end of file
.container {
min-height: 100vh;
background-color: #f7f7f7;
}
.content {
padding: 20rpx 20rpx 120rpx;
}
.card {
background: #fff;
border-radius: 8rpx;
padding: 25rpx 20rpx;
margin-bottom: 20rpx;
}
.yearRow {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
.yearRow .label {
font-size: 28rpx;
color: #333;
}
.yearRow .control {
display: flex;
align-items: center;
}
.control image {
width: 50rpx;
height: 50rpx;
}
.yearRow .num {
font-size: 28rpx;
color: #333;
min-width: 80rpx;
text-align: center;
margin: 0 10rpx;
}
.row {
display: flex;
justify-content: space-between;
align-items: center;
}
.row .label {
font-size: 28rpx;
color: #333;
}
.row .value {
font-size: 30rpx;
color: #C4121B;
font-weight: 500;
}
.hintRow {
display: flex;
align-items: flex-start;
font-size: 24rpx;
line-height: 1.4;
}
.hintRow .hintText {
color: #FF8124;
flex: 1;
margin-top: 10rpx;
}
.deductRow {
background: #fff;
padding: 20rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
border-radius: 8rpx;
}
.deductRow .label {
font-size: 28rpx;
color: #333;
}
.deductRow .value {
font-size: 30rpx;
color: #C4121B;
}
.payRow {
background: #fff;
border-radius: 8rpx;
padding: 20rpx 20rpx;
margin-bottom: 20rpx;
}
.radioItem {
display: flex;
align-items: center;
}
.payInfo {
display: flex;
align-items: center;
margin-left: 15rpx;
}
.payInfo .icon {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
.payInfo text {
font-size: 28rpx;
color: #333;
}
.totalRow {
background: #fff;
border-radius: 8rpx;
padding: 20rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10rpx;
}
.totalRow .label {
font-size: 28rpx;
color: #333;
}
.redBig {
font-size: 32rpx;
color: #C4121B;
font-weight: bold;
}
.bottomBtn {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 20rpx;
background: #fff;
border-top: 1rpx solid #eee;
}
.payBtn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #C4121B;
color: #fff;
border-radius: 8rpx;
font-size: 32rpx;
text-align: center;
border: none;
}
.payBtn[disabled] {
background-color: #ccc;
color: #999;
}
.red {
color: #C4121B;
}
.icon {
width: 30px;
}
::v-deep .custom-radio .wx-radio-input {
width: 30rpx;
height: 30rpx;
border-radius: 50%;
border: 2rpx solid #ccc;
}
::v-deep .custom-radio .wx-radio-input.wx-radio-input-checked {
border-color: #C4121B !important;
background: #C4121B !important;
}
</style>
......
......@@ -141,6 +141,7 @@ const queryParams = reactive({
pageNum: 1,
pageSize: 10,
type: '0', // 0表示个人会员
subType: '1', //0道馆 1个人
// queryType: '1',
// payStatus: '',
perId: ''
......@@ -269,7 +270,7 @@ const handlePay = async (item) => {
// 申请开票
const makeInvoiceFN = (item) => {
uni.navigateTo({url: `/pages/invoice/apply?orderId=${item.id}amount=${item.price}`});
uni.navigateTo({url: `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}&type=1`});
};
// 取消订单
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!