Merge branch 'main' of https://gitlab.guxuan.icu/Leo_Ding/GPU_Web
This commit is contained in:
commit
d440ad0008
@ -35,3 +35,21 @@ export const DelInvoiceTitleInfo = (id:any) => request.delete(`/v1/invoice_title
|
|||||||
|
|
||||||
// 删除镜像
|
// 删除镜像
|
||||||
export const delImageItem = (params:any) => request.delete(`/v1/image/delete_image/${params.image_id}`)
|
export const delImageItem = (params:any) => request.delete(`/v1/image/delete_image/${params.image_id}`)
|
||||||
|
|
||||||
|
// 银行卡管理-添加银行卡
|
||||||
|
export const addBankCard = (params:any) => request.put('/v1/bank_card/add_bank_card',params)
|
||||||
|
|
||||||
|
// 银行卡管理-获取绑定信息
|
||||||
|
export const getBankCardInfo = (params:any) => request.get('/v1/bank_card/bank_card_info',{params})
|
||||||
|
|
||||||
|
// 银行卡管理 - 解绑银行卡
|
||||||
|
export const delBankCard = (params:any) => request.post('/v1/bank_card/delete_bank_card',params)
|
||||||
|
|
||||||
|
// 发票管理-开票记录
|
||||||
|
export const invoiceList = (params:any) => request.get('/v1/order/invoice_list',{params})
|
||||||
|
|
||||||
|
// 发票管理-开发记录详情
|
||||||
|
export const invoiceDetail = (id:any) => request.delete(`/v1/order/invoice_detail/${id}`)
|
||||||
|
|
||||||
|
// 获取订单列表
|
||||||
|
export const getOrderList = (params:any) => request.get('/v1/order/order_list',{params})
|
||||||
@ -14,17 +14,17 @@
|
|||||||
<div class="bank-icon">
|
<div class="bank-icon">
|
||||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg" alt="银联" />
|
<img src="https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg" alt="银联" />
|
||||||
</div>
|
</div>
|
||||||
<div class="card-number">{{ formatCardNumber(boundCard.cardNumber) }}</div>
|
<div class="card-number">{{ formatCardNumber(boundCard.bank_card_no) }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-details">
|
<div class="card-details">
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="label">持卡人姓名:</span>
|
<span class="label">持卡人姓名:</span>
|
||||||
<span class="value">{{ boundCard.realName }}</span>
|
<span class="value">{{ boundCard.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="label">开户行:</span>
|
<span class="label">开户行:</span>
|
||||||
<span class="value">{{ boundCard.bankName }}</span>
|
<span class="value">{{ boundCard.bank_name || '--' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<span class="label">手机号:</span>
|
<span class="label">手机号:</span>
|
||||||
@ -49,48 +49,36 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 添加银行卡对话框 -->
|
<!-- 添加银行卡对话框 -->
|
||||||
<a-modal
|
<a-modal v-model:visible="addModalVisible" title="添加银行卡" @ok="handleAddCard" @cancel="handleAddCancel"
|
||||||
v-model:visible="addModalVisible"
|
:confirm-loading="addLoading" :width="500" :ok-text="'绑定'" :cancel-text="'取消'">
|
||||||
title="添加银行卡"
|
<a-form ref="addFormRef" :model="addFormState" :rules="addFormRules" layout="vertical">
|
||||||
@ok="handleAddCard"
|
|
||||||
@cancel="handleAddCancel"
|
|
||||||
:confirm-loading="addLoading"
|
|
||||||
:width="500"
|
|
||||||
:ok-text="'绑定'"
|
|
||||||
:cancel-text="'取消'"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
ref="addFormRef"
|
|
||||||
:model="addFormState"
|
|
||||||
:rules="addFormRules"
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<a-form-item label="真实姓名" name="realName">
|
<a-form-item label="真实姓名" name="realName">
|
||||||
<a-input
|
<a-input v-model:value="addFormState.realName" placeholder="请输入持卡人真实姓名" size="large" />
|
||||||
v-model:value="addFormState.realName"
|
</a-form-item>
|
||||||
placeholder="请输入持卡人真实姓名"
|
|
||||||
size="large"
|
<a-form-item label="手机号" name="phone">
|
||||||
/>
|
<div class="sms-code-input">
|
||||||
|
<a-input v-model:value="addFormState.phone" placeholder="请输入银行预留手机号" size="large"
|
||||||
|
:maxlength="11" />
|
||||||
|
<a-button type="primary" :disabled="addCountdown > 0" @click="sendAddSmsCode"
|
||||||
|
class="sms-button">
|
||||||
|
{{ addCountdown > 0 ? `${addCountdown}秒后重新获取` : '获取验证码' }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="短信验证码" name="smsCode">
|
||||||
|
<a-input v-model:value="addFormState.smsCode" placeholder="请输入短信验证码" size="large" :maxlength="6" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="银行卡号" name="cardNumber">
|
<a-form-item label="银行卡号" name="cardNumber">
|
||||||
<a-input
|
<a-input v-model:value="addFormState.cardNumber" placeholder="请输入银行卡号" size="large" :maxlength="19"
|
||||||
v-model:value="addFormState.cardNumber"
|
@input="formatCardInput" />
|
||||||
placeholder="请输入银行卡号"
|
|
||||||
size="large"
|
|
||||||
:maxlength="19"
|
|
||||||
@input="formatCardInput"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="开户行" name="bankName">
|
<a-form-item label="开户行" name="bankName">
|
||||||
<a-select
|
<a-select v-model:value="addFormState.bankName" placeholder="请选择开户行" size="large"
|
||||||
v-model:value="addFormState.bankName"
|
:options="bankOptions" show-search />
|
||||||
placeholder="请选择开户行"
|
|
||||||
size="large"
|
|
||||||
:options="bankOptions"
|
|
||||||
show-search
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item name="agreement">
|
<a-form-item name="agreement">
|
||||||
@ -103,62 +91,39 @@
|
|||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<!-- 解绑银行卡对话框 -->
|
<!-- 解绑银行卡对话框 -->
|
||||||
<a-modal
|
<!-- 修复:移除 @ok 事件中的参数传递,改为在方法内部处理 -->
|
||||||
v-model:visible="unbindModalVisible"
|
<a-modal v-model:visible="unbindModalVisible" title="解绑银行卡" @ok="handleUnbindCard"
|
||||||
title="解绑银行卡"
|
@cancel="handleUnbindCancel" :confirm-loading="unbindLoading" :width="500" :ok-text="'确认解绑'"
|
||||||
@ok="handleUnbindCard"
|
:cancel-text="'取消'">
|
||||||
@cancel="handleUnbindCancel"
|
<div v-if="boundCard" class="unbind-info">
|
||||||
:confirm-loading="unbindLoading"
|
|
||||||
:width="500"
|
|
||||||
:ok-text="'确认解绑'"
|
|
||||||
:cancel-text="'取消'"
|
|
||||||
>
|
|
||||||
<div class="unbind-info">
|
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">持卡人姓名:</span>
|
<span class="label">持卡人姓名:</span>
|
||||||
<span class="value">{{ boundCard.realName }}</span>
|
<span class="value">{{ boundCard.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">银行卡号:</span>
|
<span class="label">银行卡号:</span>
|
||||||
<span class="value">{{ formatCardNumber(boundCard.cardNumber) }}</span>
|
<span class="value">{{ formatCardNumber(boundCard.bank_card_no) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="label">开户行:</span>
|
<span class="label">开户行:</span>
|
||||||
<span class="value">{{ boundCard.bankName }}</span>
|
<span class="value">{{ boundCard.bank_name || '--' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<a-divider />
|
<a-divider />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-form
|
<a-form ref="unbindFormRef" :model="unbindFormState" :rules="unbindFormRules" layout="vertical">
|
||||||
ref="unbindFormRef"
|
|
||||||
:model="unbindFormState"
|
|
||||||
:rules="unbindFormRules"
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<a-form-item label="手机号码" name="phone">
|
<a-form-item label="手机号码" name="phone">
|
||||||
<a-input
|
<a-input v-model:value="unbindFormState.phone" placeholder="请输入银行预留手机号" size="large"
|
||||||
v-model:value="unbindFormState.phone"
|
:maxlength="11" />
|
||||||
placeholder="请输入银行预留手机号"
|
|
||||||
size="large"
|
|
||||||
:maxlength="11"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="短信验证码" name="smsCode">
|
<a-form-item label="短信验证码" name="smsCode">
|
||||||
<div class="sms-code-input">
|
<div class="sms-code-input">
|
||||||
<a-input
|
<a-input v-model:value="unbindFormState.smsCode" placeholder="请输入短信验证码" size="large"
|
||||||
v-model:value="unbindFormState.smsCode"
|
:maxlength="6" />
|
||||||
placeholder="请输入短信验证码"
|
<a-button type="primary" :disabled="unbindCountdown > 0" @click="sendUnbindSmsCode"
|
||||||
size="large"
|
class="sms-button">
|
||||||
:maxlength="6"
|
{{ unbindCountdown > 0 ? `${unbindCountdown}秒后重新获取` : '获取验证码' }}
|
||||||
/>
|
|
||||||
<a-button
|
|
||||||
type="primary"
|
|
||||||
:disabled="countdown > 0"
|
|
||||||
@click="sendSmsCode"
|
|
||||||
class="sms-button"
|
|
||||||
>
|
|
||||||
{{ countdown > 0 ? `${countdown}秒后重新获取` : '获取验证码' }}
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@ -174,17 +139,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
|
||||||
import { PlusOutlined } from '@ant-design/icons-vue'
|
import { PlusOutlined } from '@ant-design/icons-vue'
|
||||||
import { message, Modal } from 'ant-design-vue'
|
import { message, Modal } from 'ant-design-vue'
|
||||||
|
import { addBankCard, getBankCardInfo, delBankCard } from '@/apis/admin'
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getBoundCardInfo();
|
||||||
|
})
|
||||||
|
|
||||||
// 已绑定银行卡数据 - 初始为空
|
// 已绑定银行卡数据 - 初始为空
|
||||||
const boundCard = ref(null)
|
const boundCard = ref<any>(null)
|
||||||
|
|
||||||
// 是否已绑定银行卡 - 初始为false
|
// 是否已绑定银行卡 - 初始为false
|
||||||
const hasBoundCard = computed(() => {
|
const hasBoundCard = computed(() => {
|
||||||
return boundCard.value !== null && boundCard.value !== undefined
|
return boundCard.value !== null && boundCard.value !== undefined && boundCard.value.bank_card_no
|
||||||
})
|
})
|
||||||
|
|
||||||
// 银行选项
|
// 银行选项
|
||||||
@ -212,17 +182,30 @@ const addLoading = ref(false)
|
|||||||
const addFormRef = ref()
|
const addFormRef = ref()
|
||||||
const addFormState = reactive({
|
const addFormState = reactive({
|
||||||
realName: '',
|
realName: '',
|
||||||
|
phone: '',
|
||||||
|
smsCode: '',
|
||||||
cardNumber: '',
|
cardNumber: '',
|
||||||
bankName: '',
|
bankName: '',
|
||||||
agreement: false
|
agreement: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 添加银行卡短信验证码倒计时
|
||||||
|
const addCountdown = ref(0)
|
||||||
|
|
||||||
// 添加银行卡表单验证规则
|
// 添加银行卡表单验证规则
|
||||||
const addFormRules = {
|
const addFormRules = {
|
||||||
realName: [
|
realName: [
|
||||||
{ required: true, message: '请输入真实姓名', trigger: 'blur' },
|
{ required: true, message: '请输入真实姓名', trigger: 'blur' },
|
||||||
{ pattern: /^[\u4e00-\u9fa5]{2,10}$/, message: '请输入2-10位中文字符', trigger: 'blur' }
|
{ pattern: /^[\u4e00-\u9fa5]{2,10}$/, message: '请输入2-10位中文字符', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
|
phone: [
|
||||||
|
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
smsCode: [
|
||||||
|
{ required: true, message: '请输入短信验证码', trigger: 'blur' },
|
||||||
|
{ pattern: /^\d{6}$/, message: '验证码为6位数字', trigger: 'blur' }
|
||||||
|
],
|
||||||
cardNumber: [
|
cardNumber: [
|
||||||
{ required: true, message: '请输入银行卡号', trigger: 'blur' },
|
{ required: true, message: '请输入银行卡号', trigger: 'blur' },
|
||||||
{
|
{
|
||||||
@ -253,8 +236,8 @@ const unbindFormState = reactive({
|
|||||||
agreement: false
|
agreement: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// 短信验证码倒计时
|
// 解绑银行卡短信验证码倒计时
|
||||||
const countdown = ref(0)
|
const unbindCountdown = ref(0)
|
||||||
|
|
||||||
// 解绑银行卡表单验证规则
|
// 解绑银行卡表单验证规则
|
||||||
const unbindFormRules = {
|
const unbindFormRules = {
|
||||||
@ -292,10 +275,13 @@ const showAddModal = () => {
|
|||||||
// 重置表单
|
// 重置表单
|
||||||
Object.assign(addFormState, {
|
Object.assign(addFormState, {
|
||||||
realName: '',
|
realName: '',
|
||||||
|
phone: '',
|
||||||
|
smsCode: '',
|
||||||
cardNumber: '',
|
cardNumber: '',
|
||||||
bankName: '',
|
bankName: '',
|
||||||
agreement: false
|
agreement: false
|
||||||
})
|
})
|
||||||
|
addCountdown.value = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示解绑银行卡对话框
|
// 显示解绑银行卡对话框
|
||||||
@ -307,57 +293,195 @@ const showUnbindModal = () => {
|
|||||||
smsCode: '',
|
smsCode: '',
|
||||||
agreement: false
|
agreement: false
|
||||||
})
|
})
|
||||||
|
unbindCountdown.value = 0
|
||||||
|
|
||||||
|
// 如果已绑定银行卡,自动填充手机号
|
||||||
|
if (boundCard.value && boundCard.value.phone) {
|
||||||
|
// 从脱敏的手机号中获取原始手机号(如果有的话)
|
||||||
|
// 注意:这里假设原始手机号保存在其他地方,或者需要用户重新输入
|
||||||
|
// 实际项目中可能需要从服务器获取原始手机号
|
||||||
|
unbindFormState.phone = ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理添加银行卡
|
// 处理添加银行卡
|
||||||
const handleAddCard = () => {
|
const handleAddCard = async () => {
|
||||||
addFormRef.value.validate().then(() => {
|
try {
|
||||||
|
// 验证表单
|
||||||
|
await addFormRef.value.validate()
|
||||||
addLoading.value = true
|
addLoading.value = true
|
||||||
|
|
||||||
// 模拟API请求
|
|
||||||
setTimeout(() => {
|
|
||||||
// 清除空格,保存纯数字
|
// 清除空格,保存纯数字
|
||||||
const cleanCardNumber = addFormState.cardNumber.replace(/\s+/g, '')
|
const cleanCardNumber = addFormState.cardNumber.replace(/\s+/g, '')
|
||||||
|
|
||||||
// 更新已绑定银行卡信息
|
// 构造API请求参数(按照后台要求的字段名)
|
||||||
|
const requestData = {
|
||||||
|
bank_card_no: cleanCardNumber, // 银行卡号
|
||||||
|
bank_name: addFormState.bankName, // 银行名称
|
||||||
|
code: addFormState.smsCode, // 短信验证码
|
||||||
|
name: addFormState.realName, // 真实姓名
|
||||||
|
phone: addFormState.phone // 手机号
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('发送银行卡添加请求:', requestData)
|
||||||
|
|
||||||
|
// 调用API
|
||||||
|
const res = await addBankCard(requestData)
|
||||||
|
|
||||||
|
console.log('添加银行卡结果:', res)
|
||||||
|
|
||||||
|
// 更新本地已绑定银行卡信息
|
||||||
boundCard.value = {
|
boundCard.value = {
|
||||||
realName: addFormState.realName,
|
...requestData,
|
||||||
cardNumber: cleanCardNumber,
|
phone: addFormState.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') // 脱敏显示
|
||||||
bankName: addFormState.bankName,
|
|
||||||
phone: '138****' + cleanCardNumber.substr(cleanCardNumber.length - 4) // 模拟手机号
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addModalVisible.value = false
|
addModalVisible.value = false
|
||||||
addLoading.value = false
|
addLoading.value = false
|
||||||
|
|
||||||
message.success('银行卡绑定成功')
|
message.success('银行卡绑定成功')
|
||||||
}, 1000)
|
getBoundCardInfo();
|
||||||
}).catch(error => {
|
} catch (error: any) {
|
||||||
console.log('验证失败:', error)
|
console.log('银行卡绑定失败:', error)
|
||||||
})
|
addLoading.value = false
|
||||||
|
|
||||||
|
// 显示错误信息
|
||||||
|
if (error.response?.data?.message) {
|
||||||
|
message.error(error.response.data.message)
|
||||||
|
} else if (error.message) {
|
||||||
|
message.error(error.message)
|
||||||
|
} else {
|
||||||
|
message.error('银行卡绑定失败,请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取绑定信息
|
||||||
|
const getBoundCardInfo = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getBankCardInfo()
|
||||||
|
console.log('获取绑定银行卡信息结果:', res)
|
||||||
|
|
||||||
|
// 检查响应是否成功且有数据
|
||||||
|
if (res && res.bank_card_no) {
|
||||||
|
// 格式化电话号码,用于脱敏显示
|
||||||
|
const phoneNumber = res.phone || ''
|
||||||
|
const formattedPhone = phoneNumber ?
|
||||||
|
phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') :
|
||||||
|
''
|
||||||
|
|
||||||
|
boundCard.value = {
|
||||||
|
...res,
|
||||||
|
phone: formattedPhone,
|
||||||
|
// 保存原始手机号用于验证(如果API返回原始手机号)
|
||||||
|
original_phone: res.phone || ''
|
||||||
|
}
|
||||||
|
console.log('已绑定银行卡信息:', boundCard.value)
|
||||||
|
} else {
|
||||||
|
// 没有绑定银行卡
|
||||||
|
boundCard.value = null
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('获取绑定银行卡信息失败:', error)
|
||||||
|
boundCard.value = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理解绑银行卡
|
// 处理解绑银行卡
|
||||||
const handleUnbindCard = () => {
|
const handleUnbindCard = async () => {
|
||||||
unbindFormRef.value.validate().then(() => {
|
try {
|
||||||
|
await unbindFormRef.value.validate()
|
||||||
unbindLoading.value = true
|
unbindLoading.value = true
|
||||||
|
|
||||||
// 模拟API请求
|
// 确保有绑定的银行卡数据
|
||||||
setTimeout(() => {
|
if (!boundCard.value || !boundCard.value.id) {
|
||||||
// 清除已绑定银行卡信息
|
message.error('银行卡信息无效,无法解绑')
|
||||||
boundCard.value = null
|
|
||||||
unbindModalVisible.value = false
|
|
||||||
unbindLoading.value = false
|
unbindLoading.value = false
|
||||||
|
return
|
||||||
message.success('银行卡解绑成功')
|
|
||||||
}, 1000)
|
|
||||||
}).catch(error => {
|
|
||||||
console.log('验证失败:', error)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送短信验证码
|
// 构造解绑银行卡请求参数
|
||||||
const sendSmsCode = () => {
|
const requestData = {
|
||||||
|
code: unbindFormState.smsCode, // 短信验证码
|
||||||
|
id: boundCard.value.id, // 银行卡ID
|
||||||
|
phone: unbindFormState.phone // 手机号(需要用户输入,因为显示的是脱敏的)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('发送银行卡解绑请求:', requestData)
|
||||||
|
|
||||||
|
// 调用解绑银行卡API
|
||||||
|
const res: any = await delBankCard(requestData)
|
||||||
|
console.log('解绑银行卡结果:', res)
|
||||||
|
|
||||||
|
if (res.code === 1) {
|
||||||
|
message.success('解绑成功')
|
||||||
|
// 先关闭模态框
|
||||||
|
unbindModalVisible.value = false
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
Object.assign(unbindFormState, {
|
||||||
|
phone: '',
|
||||||
|
smsCode: '',
|
||||||
|
agreement: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// 等待模态框完全关闭后再更新数据
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// 刷新银行卡信息(会设置为null)
|
||||||
|
await getBoundCardInfo();
|
||||||
|
} else {
|
||||||
|
message.error(res.msg || '解绑失败')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log('银行卡解绑失败:', error)
|
||||||
|
message.error('银行卡解绑失败,请重试')
|
||||||
|
} finally {
|
||||||
|
unbindLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送添加银行卡短信验证码
|
||||||
|
const sendAddSmsCode = async () => {
|
||||||
|
if (!addFormState.phone) {
|
||||||
|
message.warning('请输入手机号码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^1[3-9]\d{9}$/.test(addFormState.phone)) {
|
||||||
|
message.warning('手机号格式不正确')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: 这里需要调用发送短信验证码的API
|
||||||
|
// 假设有一个 sendSmsCode API
|
||||||
|
// const res = await sendSmsCode({
|
||||||
|
// phone: addFormState.phone,
|
||||||
|
// type: 'BIND_CARD' // 发送验证码的类型
|
||||||
|
// })
|
||||||
|
|
||||||
|
// 开始倒计时
|
||||||
|
addCountdown.value = 60
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
addCountdown.value -= 1
|
||||||
|
if (addCountdown.value <= 0) {
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
// 模拟发送验证码(实际为123456)
|
||||||
|
message.success('验证码已发送,请注意查收(演示验证码:123456)')
|
||||||
|
// 注意:实际项目中不应该自动填入验证码,这里只是为了演示
|
||||||
|
// addFormState.smsCode = '123456'
|
||||||
|
} catch (error) {
|
||||||
|
console.log('发送验证码失败:', error)
|
||||||
|
message.error('发送验证码失败,请重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送解绑银行卡短信验证码
|
||||||
|
const sendUnbindSmsCode = async () => {
|
||||||
if (!unbindFormState.phone) {
|
if (!unbindFormState.phone) {
|
||||||
message.warning('请输入手机号码')
|
message.warning('请输入手机号码')
|
||||||
return
|
return
|
||||||
@ -368,32 +492,38 @@ const sendSmsCode = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模拟验证手机号是否匹配
|
try {
|
||||||
const expectedPhone = boundCard.value.phone.replace(/\*/g, '')
|
// 验证手机号是否匹配(这里需要用户输入原始手机号)
|
||||||
const inputPhone = unbindFormState.phone
|
// 因为界面上显示的是脱敏的手机号,所以无法直接比较
|
||||||
const lastFourDigits = boundCard.value.cardNumber.substr(boundCard.value.cardNumber.length - 4)
|
// 可以在这里添加额外的验证逻辑,比如调用API验证手机号是否正确
|
||||||
|
|
||||||
if (inputPhone !== '138' + lastFourDigits) {
|
// TODO: 这里需要调用发送短信验证码的API
|
||||||
message.error('手机号与预留手机号不匹配')
|
// const res = await sendSmsCode({
|
||||||
return
|
// phone: unbindFormState.phone,
|
||||||
}
|
// type: 'UNBIND_CARD'
|
||||||
|
// })
|
||||||
|
|
||||||
// 开始倒计时
|
// 开始倒计时
|
||||||
countdown.value = 60
|
unbindCountdown.value = 60
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
countdown.value -= 1
|
unbindCountdown.value -= 1
|
||||||
if (countdown.value <= 0) {
|
if (unbindCountdown.value <= 0) {
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
// 模拟发送验证码(实际为123456)
|
// 模拟发送验证码
|
||||||
message.success('验证码已发送,请注意查收(演示验证码:123456)')
|
message.success('验证码已发送,请注意查收(演示验证码:123456)')
|
||||||
unbindFormState.smsCode = '123456'
|
// 注意:实际项目中不应该自动填入验证码
|
||||||
|
// unbindFormState.smsCode = '123456'
|
||||||
|
} catch (error) {
|
||||||
|
console.log('发送验证码失败:', error)
|
||||||
|
message.error('发送验证码失败,请重试')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化银行卡号显示
|
// 格式化银行卡号显示
|
||||||
const formatCardNumber = (cardNumber) => {
|
const formatCardNumber = (cardNumber: string) => {
|
||||||
if (!cardNumber) return ''
|
if (!cardNumber) return ''
|
||||||
const cleanNumber = cardNumber.replace(/\s+/g, '')
|
const cleanNumber = cardNumber.replace(/\s+/g, '')
|
||||||
const visibleDigits = 4
|
const visibleDigits = 4
|
||||||
@ -507,9 +637,8 @@ const showUnbindAgreement = () => {
|
|||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-card-section, .bound-card-section {
|
.add-card-section,
|
||||||
/* display: flex;
|
.bound-card-section {
|
||||||
justify-content: center; */
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 320px;
|
min-height: 320px;
|
||||||
}
|
}
|
||||||
@ -683,6 +812,7 @@ const showUnbindAgreement = () => {
|
|||||||
|
|
||||||
.sms-button {
|
.sms-button {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
min-width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-modal-body) {
|
:deep(.ant-modal-body) {
|
||||||
|
|||||||
@ -76,9 +76,14 @@
|
|||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onBeforeMount, computed } from 'vue'
|
import { ref, onBeforeMount, computed } from 'vue'
|
||||||
import { usePagination } from '@/hooks'
|
import { usePagination } from '@/hooks'
|
||||||
|
import {
|
||||||
|
invoiceList,
|
||||||
|
invoiceDetail
|
||||||
|
} from '@/apis/admin'
|
||||||
|
|
||||||
// const listData=ref([ {title:1}])
|
// const listData=ref([ {title:1}])
|
||||||
// const paginationState=ref({
|
// const paginationState=ref({
|
||||||
// total: 0,
|
// total: 0,
|
||||||
@ -134,25 +139,41 @@ const rules = computed(() => ({
|
|||||||
]
|
]
|
||||||
}))
|
}))
|
||||||
const columns = ref([
|
const columns = ref([
|
||||||
{ title: '申请时间', dataIndex: 'flowNum', key: 'flowNum' },
|
{ title: '申请时间', dataIndex: 'invoiceDate', key: 'invoiceDate' },
|
||||||
{ title: '开票内容', dataIndex: 'transactionTime', key: 'transactionTime' },
|
{ title: '开票内容', dataIndex: 'invoiceContent', key: 'invoiceContent' },
|
||||||
{ title: '发票抬头', dataIndex: 'flowNum', key: 'flowNum' },
|
{ title: '发票抬头', dataIndex: 'invoiceTitle', key: 'invoiceTitle' },
|
||||||
{ title: '发票总额', dataIndex: 'flowNum', key: 'flowNum' },
|
{ title: '发票总额', dataIndex: 'invoiceAmount', key: 'invoiceAmount' },
|
||||||
{ title: '开票方式', dataIndex: 'flowNum', key: 'flowNum' },
|
{ title: '开票方式', dataIndex: 'invoiceMethod', key: 'invoiceMethod' },
|
||||||
{ title: '发票性质', dataIndex: 'flowNum', key: 'flowNum' },
|
{ title: '发票类型', dataIndex: 'invoiceType', key: 'invoiceType' },
|
||||||
{ title: '发票状态', dataIndex: 'flowNum', key: 'flowNum' },
|
{ title: '发票状态', dataIndex: 'invoiceStatus', key: 'invoiceStatus' },
|
||||||
{ title: '驳回原因', dataIndex: 'flowNum', key: 'flowNum' },
|
{ title: '驳回原因', dataIndex: 'rejectReason', key: 'rejectReason' },
|
||||||
{ title: '操作', dataIndex: 'flowNum', key: 'flowNum' },
|
{ title: '操作', dataIndex: 'flowNum', key: 'flowNum' },
|
||||||
])
|
])
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
getPageList()
|
getPageList()
|
||||||
|
|
||||||
})
|
})
|
||||||
const getPageList = () => {
|
|
||||||
listData.value = [
|
const getPageList = async () => {
|
||||||
{ flowNum: 1 }
|
try {
|
||||||
]
|
const params = {
|
||||||
|
page_num: 1,
|
||||||
|
page_size: 10, // 获取足够多的数据来统计
|
||||||
|
};
|
||||||
|
|
||||||
|
const response: any = await invoiceList(params);
|
||||||
|
|
||||||
|
if (response.data && Array.isArray(response.data)) {
|
||||||
|
listData.value = response.data
|
||||||
|
console.log('发票列表:', response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取全部消息失败:', error);
|
||||||
|
listData.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* 分页
|
* 分页
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -36,10 +36,11 @@
|
|||||||
<!-- 批量操作区域 -->
|
<!-- 批量操作区域 -->
|
||||||
<div class="batch-operations" style="margin-bottom: 16px;">
|
<div class="batch-operations" style="margin-bottom: 16px;">
|
||||||
<a-space>
|
<a-space>
|
||||||
|
<!-- :disabled="selectedRowKeys.length === 0" -->
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleBatchInvoice"
|
@click="handleBatchInvoice"
|
||||||
:disabled="selectedRowKeys.length === 0"
|
|
||||||
>
|
>
|
||||||
批量去开票
|
批量去开票
|
||||||
</a-button>
|
</a-button>
|
||||||
@ -56,7 +57,7 @@
|
|||||||
:pagination="paginationState"
|
:pagination="paginationState"
|
||||||
:rowSelection="rowSelection"
|
:rowSelection="rowSelection"
|
||||||
@change="onTableChange"
|
@change="onTableChange"
|
||||||
:rowKey="record => record.id || record.serial_number"
|
:rowKey="record => record.id || record.order_number"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'created_at'">
|
<template v-if="column.key === 'created_at'">
|
||||||
@ -66,7 +67,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'billing_end'">
|
<template v-if="column.key === 'billing_end'">
|
||||||
<div>
|
<div>
|
||||||
{{ dayjs(record.billing_start).format('YYYY-MM-DD HH:mm') +"~" +dayjs(record.billing_end).format('YYYY-MM-DD HH:mm') }}
|
{{ record.billing_start ? dayjs(record.billing_start).format('YYYY-MM-DD HH:mm') : '' }}
|
||||||
|
{{ record.billing_start && record.billing_end ? '~' : '' }}
|
||||||
|
{{ record.billing_end ? dayjs(record.billing_end).format('YYYY-MM-DD HH:mm') : '' }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'price'">
|
<template v-if="column.key === 'price'">
|
||||||
@ -76,15 +79,29 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'real_amount'">
|
<template v-if="column.key === 'real_amount'">
|
||||||
<div style="color: #ff4d4f; font-weight: 500;">
|
<div style="color: #ff4d4f; font-weight: 500;">
|
||||||
-¥{{ record.real_amount || '0.00' }}
|
-¥{{ record.real_amount || record.amount || '0.00' }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- 计费方式列 -->
|
||||||
|
<template v-if="column.key === 'billing_method'">
|
||||||
|
<a-tag :color="getBillingMethodColor(record.billing_method)">
|
||||||
|
{{ getBillingMethodText(record.billing_method) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 支付状态列 -->
|
||||||
|
<template v-if="column.key === 'pay_status'">
|
||||||
|
<a-tag :color="getPayStatusColor(record.pay_status)">
|
||||||
|
{{ getPayStatusText(record.pay_status) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
<template v-if="column.key === 'action'">
|
<template v-if="column.key === 'action'">
|
||||||
<a-space>
|
<a-space>
|
||||||
|
<!-- :disabled="!canApplyInvoice(record)" -->
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
size="small"
|
||||||
@click="handleSingleInvoice(record)"
|
@click="handleSingleInvoice(record)"
|
||||||
|
|
||||||
>
|
>
|
||||||
去开票
|
去开票
|
||||||
</a-button>
|
</a-button>
|
||||||
@ -129,8 +146,8 @@
|
|||||||
<div class="preview-title">订单列表:</div>
|
<div class="preview-title">订单列表:</div>
|
||||||
<div class="order-list">
|
<div class="order-list">
|
||||||
<div v-for="(order, index) in selectedRows.slice(0, 3)" :key="order.id" class="order-item">
|
<div v-for="(order, index) in selectedRows.slice(0, 3)" :key="order.id" class="order-item">
|
||||||
<span>{{ order.serial_number }}</span>
|
<span>{{ order.order_number }}</span>
|
||||||
<span>-¥{{ order.real_amount || '0.00' }}</span>
|
<span>-¥{{ order.amount || order.real_amount || '0.00' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectedRows.length > 3" class="order-more">
|
<div v-if="selectedRows.length > 3" class="order-more">
|
||||||
等 {{ selectedRows.length }} 个订单
|
等 {{ selectedRows.length }} 个订单
|
||||||
@ -142,11 +159,19 @@
|
|||||||
<!-- 单个开票显示单个订单 -->
|
<!-- 单个开票显示单个订单 -->
|
||||||
<div class="order-info-item">
|
<div class="order-info-item">
|
||||||
<span class="info-label">订单编号:</span>
|
<span class="info-label">订单编号:</span>
|
||||||
<span class="info-value">{{ currentInvoiceRecord?.serial_number || '-' }}</span>
|
<span class="info-value">{{ currentInvoiceRecord?.order_number || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="order-info-item">
|
||||||
|
<span class="info-label">计费方式:</span>
|
||||||
|
<span class="info-value">{{ getBillingMethodText(currentInvoiceRecord?.billing_method) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="order-info-item">
|
||||||
|
<span class="info-label">支付状态:</span>
|
||||||
|
<span class="info-value">{{ getPayStatusText(currentInvoiceRecord?.pay_status) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="order-info-item">
|
<div class="order-info-item">
|
||||||
<span class="info-label">开票金额:</span>
|
<span class="info-label">开票金额:</span>
|
||||||
<span class="info-value">¥{{ currentInvoiceRecord?.real_amount || '0.00' }}</span>
|
<span class="info-value">¥{{ currentInvoiceRecord?.amount || currentInvoiceRecord?.real_amount || '0.00' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -307,90 +332,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onBeforeMount, computed, reactive, nextTick } from 'vue'
|
import { ref, onBeforeMount, computed, reactive, nextTick } from 'vue'
|
||||||
import { usePagination } from '@/hooks'
|
import { usePagination } from '@/hooks'
|
||||||
import { useList } from '@/apis/admin'
|
import { getOrderList } from '@/apis/admin'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { message, FormInstance } from 'ant-design-vue'
|
import { message, FormInstance } from 'ant-design-vue'
|
||||||
import type { Rule } from 'ant-design-vue/es/form'
|
import type { Rule } from 'ant-design-vue/es/form'
|
||||||
|
|
||||||
// 模拟表格数据
|
|
||||||
const mockData = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
serial_number: 'SN202312270001',
|
|
||||||
created_at: '2023-12-27 10:30:00',
|
|
||||||
host_id: 'HOST-001',
|
|
||||||
host_case_id: 'CASE-001',
|
|
||||||
computing_power_model: 'RTX 4090',
|
|
||||||
gpu_count: 2,
|
|
||||||
billing_start: '2023-12-27 10:00:00',
|
|
||||||
billing_end: '2023-12-27 11:00:00',
|
|
||||||
bill_time: '1小时',
|
|
||||||
price: '50.00',
|
|
||||||
real_amount: '100.00',
|
|
||||||
status: '待开票'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
serial_number: 'SN202312270002',
|
|
||||||
created_at: '2023-12-27 11:45:00',
|
|
||||||
host_id: 'HOST-002',
|
|
||||||
host_case_id: 'CASE-002',
|
|
||||||
computing_power_model: 'RTX 4080',
|
|
||||||
gpu_count: 1,
|
|
||||||
billing_start: '2023-12-27 11:30:00',
|
|
||||||
billing_end: '2023-12-27 12:30:00',
|
|
||||||
bill_time: '1小时',
|
|
||||||
price: '40.00',
|
|
||||||
real_amount: '40.00',
|
|
||||||
status: '待开票'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
serial_number: 'SN202312270003',
|
|
||||||
created_at: '2023-12-27 14:20:00',
|
|
||||||
host_id: 'HOST-003',
|
|
||||||
host_case_id: 'CASE-003',
|
|
||||||
computing_power_model: 'RTX 3090',
|
|
||||||
gpu_count: 4,
|
|
||||||
billing_start: '2023-12-27 14:00:00',
|
|
||||||
billing_end: '2023-12-27 15:00:00',
|
|
||||||
bill_time: '1小时',
|
|
||||||
price: '30.00',
|
|
||||||
real_amount: '120.00',
|
|
||||||
status: '已开票'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
serial_number: 'SN202312270004',
|
|
||||||
created_at: '2023-12-27 16:15:00',
|
|
||||||
host_id: 'HOST-004',
|
|
||||||
host_case_id: 'CASE-004',
|
|
||||||
computing_power_model: 'A100',
|
|
||||||
gpu_count: 2,
|
|
||||||
billing_start: '2023-12-27 16:00:00',
|
|
||||||
billing_end: '2023-12-27 17:00:00',
|
|
||||||
bill_time: '1小时',
|
|
||||||
price: '80.00',
|
|
||||||
real_amount: '160.00',
|
|
||||||
status: '待开票'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '5',
|
|
||||||
serial_number: 'SN202312270005',
|
|
||||||
created_at: '2023-12-27 18:30:00',
|
|
||||||
host_id: 'HOST-005',
|
|
||||||
host_case_id: 'CASE-005',
|
|
||||||
computing_power_model: 'H100',
|
|
||||||
gpu_count: 1,
|
|
||||||
billing_start: '2023-12-27 18:00:00',
|
|
||||||
billing_end: '2023-12-27 19:00:00',
|
|
||||||
bill_time: '1小时',
|
|
||||||
price: '100.00',
|
|
||||||
real_amount: '100.00',
|
|
||||||
status: '待开票'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// 表格数据
|
// 表格数据
|
||||||
const { listData, paginationState, resetPagination, searchFormData } = usePagination()
|
const { listData, paginationState, resetPagination, searchFormData } = usePagination()
|
||||||
|
|
||||||
@ -438,6 +384,59 @@ const invoiceFormData = reactive({
|
|||||||
remark: ''
|
remark: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 计费方式映射
|
||||||
|
const billingMethodMap = {
|
||||||
|
'PayOnTime': { text: '按小时计费', color: 'blue' },
|
||||||
|
'PayOnDay': { text: '按天计费', color: 'green' },
|
||||||
|
'PayOnWeek': { text: '按周计费', color: 'orange' },
|
||||||
|
'PayOnMonth': { text: '按月计费', color: 'purple' },
|
||||||
|
'PayOnYear': { text: '按年计费', color: 'red' },
|
||||||
|
// 备用映射
|
||||||
|
'hourly': { text: '按小时计费', color: 'blue' },
|
||||||
|
'daily': { text: '按天计费', color: 'green' },
|
||||||
|
'weekly': { text: '按周计费', color: 'orange' },
|
||||||
|
'monthly': { text: '按月计费', color: 'purple' },
|
||||||
|
'yearly': { text: '按年计费', color: 'red' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付状态映射
|
||||||
|
const payStatusMap = {
|
||||||
|
0: { text: '未支付', color: 'red' },
|
||||||
|
1: { text: '已支付', color: 'green' },
|
||||||
|
2: { text: '支付中', color: 'orange' },
|
||||||
|
3: { text: '支付失败', color: 'volcano' },
|
||||||
|
4: { text: '已退款', color: 'gray' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取计费方式文本
|
||||||
|
const getBillingMethodText = (method: string) => {
|
||||||
|
if (!method) return '未知'
|
||||||
|
return billingMethodMap[method as keyof typeof billingMethodMap]?.text || method
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取计费方式颜色
|
||||||
|
const getBillingMethodColor = (method: string) => {
|
||||||
|
if (!method) return 'default'
|
||||||
|
return billingMethodMap[method as keyof typeof billingMethodMap]?.color || 'default'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取支付状态文本
|
||||||
|
const getPayStatusText = (status: number) => {
|
||||||
|
if (status === undefined || status === null) return '未知'
|
||||||
|
return payStatusMap[status as keyof typeof payStatusMap]?.text || '未知'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取支付状态颜色
|
||||||
|
const getPayStatusColor = (status: number) => {
|
||||||
|
if (status === undefined || status === null) return 'default'
|
||||||
|
return payStatusMap[status as keyof typeof payStatusMap]?.color || 'default'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否可以申请开票(只有已支付的订单可以开票)
|
||||||
|
const canApplyInvoice = (record: any) => {
|
||||||
|
return record.pay_status === 1
|
||||||
|
}
|
||||||
|
|
||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
const invoiceFormRules = reactive<Record<string, Rule[]>>({
|
const invoiceFormRules = reactive<Record<string, Rule[]>>({
|
||||||
invoiceType: [
|
invoiceType: [
|
||||||
@ -463,17 +462,27 @@ const invoiceFormRules = reactive<Record<string, Rule[]>>({
|
|||||||
|
|
||||||
// 表格列定义
|
// 表格列定义
|
||||||
const columns = ref([
|
const columns = ref([
|
||||||
{ title: '流水号', dataIndex: 'serial_number', key: 'serial_number' },
|
{ title: '流水号', dataIndex: 'order_number', key: 'order_number' },
|
||||||
{ title: '交易时间', dataIndex: 'created_at', key: 'created_at' },
|
{ title: '交易时间', dataIndex: 'created_at', key: 'created_at' },
|
||||||
{ title: '主机ID', dataIndex: 'host_id', key: 'host_id' },
|
{
|
||||||
{ title: '实例ID', dataIndex: 'host_case_id', key: 'host_case_id' },
|
title: '计费方式',
|
||||||
{ title: '算力型号', dataIndex: 'computing_power_model', key: 'computing_power_model' },
|
dataIndex: 'billing_method',
|
||||||
{ title: '算力数量', dataIndex: 'gpu_count', key: 'gpu_count' },
|
key: 'billing_method',
|
||||||
{ title: '计费时间范围', dataIndex: 'billing_end', key: 'billing_end' },
|
width: 120
|
||||||
{ title: '计费时长', dataIndex: 'bill_time', key: 'bill_time' },
|
},
|
||||||
{ title: '单价', dataIndex: 'price', key: 'price' },
|
{
|
||||||
{ title: '交易金额', dataIndex: 'real_amount', key: 'real_amount' },
|
title: '支付状态',
|
||||||
{ title: '状态', dataIndex: 'status', key: 'status' },
|
dataIndex: 'pay_status',
|
||||||
|
key: 'pay_status',
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{ title: '产品名称', dataIndex: 'product_name', key: 'product_name' },
|
||||||
|
{
|
||||||
|
title: '交易金额',
|
||||||
|
dataIndex: 'amount',
|
||||||
|
key: 'amount',
|
||||||
|
width: 120
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
@ -484,54 +493,64 @@ const columns = ref([
|
|||||||
|
|
||||||
// 行选择配置
|
// 行选择配置
|
||||||
const rowSelection = computed(() => ({
|
const rowSelection = computed(() => ({
|
||||||
selectedRowKeys: selectedRowKeys.value,
|
// selectedRowKeys: selectedRowKeys.value,
|
||||||
onChange: (selectedKeys: string[], selectedRows: any[]) => {
|
// onChange: (selectedKeys: string[], selectedRows: any[]) => {
|
||||||
selectedRowKeys.value = selectedKeys
|
// selectedRowKeys.value = selectedKeys
|
||||||
selectedRows.value = selectedRows
|
// selectedRows.value = selectedRows
|
||||||
},
|
// },
|
||||||
getCheckboxProps: (record: any) => ({
|
// getCheckboxProps: (record: any) => ({
|
||||||
disabled: record.status === '已开票'
|
// // 只有已支付的订单可以选择
|
||||||
})
|
// disabled: !canApplyInvoice(record)
|
||||||
|
// }),
|
||||||
|
// // 添加这个配置以确保选择正常工作
|
||||||
|
// type: 'checkbox'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 计算选中记录的总金额
|
// 计算选中记录的总金额
|
||||||
const selectedTotalAmount = computed(() => {
|
const selectedTotalAmount = computed(() => {
|
||||||
return selectedRows.value.reduce((total, record) => {
|
return selectedRows.value.reduce((total, record) => {
|
||||||
const amount = parseFloat(record.real_amount) || 0
|
const amount = parseFloat(record.amount || record.real_amount) || 0
|
||||||
return total + amount
|
return total + amount
|
||||||
}, 0)
|
}, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 初始化模拟数据
|
|
||||||
const initMockData = () => {
|
|
||||||
listData.value = mockData
|
|
||||||
paginationState.total = mockData.length
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
// 初始化模拟数据
|
getPageList();
|
||||||
initMockData()
|
|
||||||
// 如果需要真实数据,可以调用 getPageList()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const getPageList = async () => {
|
const getPageList = async () => {
|
||||||
try {
|
try {
|
||||||
const { pageSize, current } = paginationState
|
const { pageSize, current } = paginationState
|
||||||
const res: any = await useList({ pageSize: pageSize, pageNum: current })
|
const res: any = await getOrderList({
|
||||||
listData.value = res.list
|
page_size: pageSize,
|
||||||
paginationState.total = res.count
|
page_num: current,
|
||||||
|
start_at: '',
|
||||||
|
end_at: '',
|
||||||
|
order_number: ''
|
||||||
|
})
|
||||||
console.log('订单列表:', res)
|
console.log('订单列表:', res)
|
||||||
|
|
||||||
|
if (res.data && Array.isArray(res.data)) {
|
||||||
|
listData.value = res.data
|
||||||
|
paginationState.total = res.total || res.data.length
|
||||||
|
} else {
|
||||||
|
listData.value = []
|
||||||
|
paginationState.total = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('表格数据:', listData.value)
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('产品优势请求失败:', error)
|
console.error('订单列表请求失败:', error)
|
||||||
// 如果API请求失败,使用模拟数据
|
listData.value = []
|
||||||
initMockData()
|
paginationState.total = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页
|
* 分页
|
||||||
*/
|
*/
|
||||||
function onTableChange({ current, pageSize }) {
|
function onTableChange({ current, pageSize }: { current: number, pageSize: number }) {
|
||||||
paginationState.current = current
|
paginationState.current = current
|
||||||
paginationState.pageSize = pageSize
|
paginationState.pageSize = pageSize
|
||||||
getPageList()
|
getPageList()
|
||||||
@ -562,6 +581,12 @@ function handleResetSearch() {
|
|||||||
* 单个去开票
|
* 单个去开票
|
||||||
*/
|
*/
|
||||||
function handleSingleInvoice(record: any) {
|
function handleSingleInvoice(record: any) {
|
||||||
|
// 检查是否可以开票
|
||||||
|
if (!canApplyInvoice(record)) {
|
||||||
|
message.warning('只有已支付的订单可以申请开票')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
currentInvoiceRecord.value = record
|
currentInvoiceRecord.value = record
|
||||||
isBatch.value = false
|
isBatch.value = false
|
||||||
resetInvoiceForm()
|
resetInvoiceForm()
|
||||||
@ -572,12 +597,20 @@ function handleSingleInvoice(record: any) {
|
|||||||
* 批量去开票
|
* 批量去开票
|
||||||
*/
|
*/
|
||||||
function handleBatchInvoice() {
|
function handleBatchInvoice() {
|
||||||
if (selectedRowKeys.value.length === 0) {
|
// if (selectedRowKeys.value.length === 0) {
|
||||||
message.warning('请先选择要开票的记录')
|
// message.warning('请先选择要开票的记录')
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
isBatch.value = true
|
|
||||||
resetInvoiceForm()
|
// // 检查是否所有选中的订单都已支付
|
||||||
|
// const unpaidOrders = selectedRows.value.filter(order => !canApplyInvoice(order))
|
||||||
|
// if (unpaidOrders.length > 0) {
|
||||||
|
// message.warning(`有 ${unpaidOrders.length} 个订单未支付,请选择已支付的订单进行开票`)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// isBatch.value = true
|
||||||
|
// resetInvoiceForm()
|
||||||
singleInvoiceModalVisible.value = true
|
singleInvoiceModalVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,13 +671,17 @@ async function handleSingleInvoiceConfirm() {
|
|||||||
orders: isBatch.value
|
orders: isBatch.value
|
||||||
? selectedRows.value.map(row => ({
|
? selectedRows.value.map(row => ({
|
||||||
id: row.id,
|
id: row.id,
|
||||||
serialNumber: row.serial_number,
|
serialNumber: row.order_number,
|
||||||
amount: row.real_amount
|
amount: row.amount || row.real_amount,
|
||||||
|
billingMethod: row.billing_method,
|
||||||
|
payStatus: row.pay_status
|
||||||
}))
|
}))
|
||||||
: [{
|
: [{
|
||||||
id: currentInvoiceRecord.value.id,
|
id: currentInvoiceRecord.value.id,
|
||||||
serialNumber: currentInvoiceRecord.value.serial_number,
|
serialNumber: currentInvoiceRecord.value.order_number,
|
||||||
amount: currentInvoiceRecord.value.real_amount
|
amount: currentInvoiceRecord.value.amount || currentInvoiceRecord.value.real_amount,
|
||||||
|
billingMethod: currentInvoiceRecord.value.billing_method,
|
||||||
|
payStatus: currentInvoiceRecord.value.pay_status
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,4 +916,40 @@ function handleAddTitleConfirm() {
|
|||||||
:deep(.ant-form-item) {
|
:deep(.ant-form-item) {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 表格样式优化 */
|
||||||
|
:deep(.ant-table-tbody .ant-table-cell) {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标签样式 */
|
||||||
|
:deep(.ant-tag) {
|
||||||
|
margin-right: 0;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
min-width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用按钮样式 */
|
||||||
|
:deep(.ant-btn-link[disabled]) {
|
||||||
|
color: rgba(0, 0, 0, 0.25);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用复选框样式 */
|
||||||
|
:deep(.ant-checkbox-disabled .ant-checkbox-inner) {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-color: #d9d9d9 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-checkbox-disabled .ant-checkbox-inner::after) {
|
||||||
|
border-color: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-checkbox-disabled + span) {
|
||||||
|
color: rgba(0, 0, 0, 0.25);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,49 +1,191 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-card title="我的消息">
|
<a-card title="我的消息">
|
||||||
<a-tabs v-model:activeKey="activeKey">
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
<a-tab-pane key="all" tab="全部"></a-tab-pane>
|
<!-- 使用customRender来渲染带徽章的标签 -->
|
||||||
<a-tab-pane key="money" tab="资金消息" force-render></a-tab-pane>
|
<a-tab-pane key="all">
|
||||||
<a-tab-pane key="secure" tab="安全消息"></a-tab-pane>
|
<template #tab>
|
||||||
<a-tab-pane key="active" tab="活动消息"></a-tab-pane>
|
<span class="tab-label">
|
||||||
|
全部
|
||||||
|
<a-badge
|
||||||
|
v-if="allUnreadCount > 0"
|
||||||
|
:count="allUnreadCount"
|
||||||
|
class="tab-badge"
|
||||||
|
:offset="[5, -2]"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</a-tab-pane>
|
||||||
|
|
||||||
|
<a-tab-pane key="money">
|
||||||
|
<template #tab>
|
||||||
|
<span class="tab-label">
|
||||||
|
资金消息
|
||||||
|
<a-badge
|
||||||
|
v-if="moneyUnreadCount > 0"
|
||||||
|
:count="moneyUnreadCount"
|
||||||
|
class="tab-badge"
|
||||||
|
:offset="[5, -2]"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</a-tab-pane>
|
||||||
|
|
||||||
|
<a-tab-pane key="secure">
|
||||||
|
<template #tab>
|
||||||
|
<span class="tab-label">
|
||||||
|
安全消息
|
||||||
|
<a-badge
|
||||||
|
v-if="secureUnreadCount > 0"
|
||||||
|
:count="secureUnreadCount"
|
||||||
|
class="tab-badge"
|
||||||
|
:offset="[5, -2]"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</a-tab-pane>
|
||||||
|
|
||||||
|
<a-tab-pane key="active">
|
||||||
|
<template #tab>
|
||||||
|
<span class="tab-label">
|
||||||
|
活动消息
|
||||||
|
<a-badge
|
||||||
|
v-if="activeUnreadCount > 0"
|
||||||
|
:count="activeUnreadCount"
|
||||||
|
class="tab-badge"
|
||||||
|
:offset="[5, -2]"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
<a-table :columns="columns" :data-source="billData" :pagination="pagination" @change="handleTableChange"
|
|
||||||
:loading="loading" class="bill-table" :scroll="{ x: 1200 }" bordered>
|
<a-table
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="billData"
|
||||||
|
:pagination="pagination"
|
||||||
|
@change="handleTableChange"
|
||||||
|
:loading="loading"
|
||||||
|
class="bill-table"
|
||||||
|
:scroll="{ x: 1200 }"
|
||||||
|
bordered>
|
||||||
|
|
||||||
|
<!-- 消息类型列自定义渲染 -->
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'messageType'">
|
||||||
|
<span :class="{ 'unread-title': !record.is_read }">
|
||||||
|
{{ getMessageTypeText(record.messageType) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="column.dataIndex === 'title'">
|
||||||
|
<span :class="{ 'unread-title': !record.is_read, 'read-title': record.is_read }">
|
||||||
|
{{ record.title }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="column.dataIndex === 'is_read'">
|
||||||
|
<a-tag :class="record.is_read ? 'status-read' : 'status-unread'">
|
||||||
|
{{ record.is_read ? '已读' : '未读' }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref,onBeforeMount } from 'vue';
|
import { ref, onBeforeMount, watch, computed } from 'vue';
|
||||||
import { getMessageList } from '@/apis/home';
|
import { getMessageList } from '@/apis/home';
|
||||||
const activeKey = ref('1');
|
|
||||||
|
// 当前激活的 Tab
|
||||||
|
const activeKey = ref('all');
|
||||||
|
|
||||||
|
// 加载状态
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const billData = ref([])
|
|
||||||
|
// 表格数据
|
||||||
|
const billData = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 所有消息数据(用于统计未读数量)
|
||||||
|
const allMessages = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 当前筛选的消息类型(传给后端)
|
||||||
|
const messageType = ref<string>('');
|
||||||
|
|
||||||
|
// Tab key 映射到后端 messageType 值
|
||||||
|
const keyToMessageType: Record<string, string> = {
|
||||||
|
all: '',
|
||||||
|
money: 'FinancialMessage',
|
||||||
|
secure: 'SecurityMessage',
|
||||||
|
active: 'ActivityMessage'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 消息类型映射(前端显示用)
|
||||||
|
const messageTypeMap: Record<string, string> = {
|
||||||
|
FinancialMessage: '资金消息',
|
||||||
|
SecurityMessage: '安全消息',
|
||||||
|
ActivityMessage: '活动消息',
|
||||||
|
// 如果还有其他可能的消息类型,可以在这里添加
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取消息类型的中文文本
|
||||||
|
const getMessageTypeText = (type: string) => {
|
||||||
|
return messageTypeMap[type] || type || '未知类型';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算各个标签页的未读数量
|
||||||
|
const allUnreadCount = computed(() => {
|
||||||
|
return allMessages.value.filter(msg => !msg.is_read).length;
|
||||||
|
});
|
||||||
|
|
||||||
|
const moneyUnreadCount = computed(() => {
|
||||||
|
return allMessages.value.filter(msg =>
|
||||||
|
msg.messageType === 'FinancialMessage' && !msg.is_read
|
||||||
|
).length;
|
||||||
|
});
|
||||||
|
|
||||||
|
const secureUnreadCount = computed(() => {
|
||||||
|
return allMessages.value.filter(msg =>
|
||||||
|
msg.messageType === 'SecurityMessage' && !msg.is_read
|
||||||
|
).length;
|
||||||
|
});
|
||||||
|
|
||||||
|
const activeUnreadCount = computed(() => {
|
||||||
|
return allMessages.value.filter(msg =>
|
||||||
|
msg.messageType === 'ActivityMessage' && !msg.is_read
|
||||||
|
).length;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 表格列定义
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '消息类型',
|
title: '消息类型',
|
||||||
dataIndex: 'messageType',
|
dataIndex: 'messageType',
|
||||||
key: 'messageType',
|
key: 'messageType',
|
||||||
width: 200,
|
width: 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '消息内容',
|
title: '消息标题',
|
||||||
dataIndex: 'messageContent',
|
dataIndex: 'title',
|
||||||
key: 'messageContent',
|
key: 'title',
|
||||||
width: 400,
|
width: 400
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '发送时间',
|
title: '发送时间',
|
||||||
dataIndex: 'sendTime',
|
dataIndex: 'created_at',
|
||||||
key: 'sendTime',
|
key: 'created_at',
|
||||||
width: 200,
|
width: 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
dataIndex: 'status',
|
dataIndex: 'is_read',
|
||||||
key: 'status',
|
key: 'is_read',
|
||||||
width: 100,
|
width: 100
|
||||||
},
|
}
|
||||||
];
|
];
|
||||||
const messageType=ref("")
|
|
||||||
|
// 分页配置
|
||||||
const pagination = ref({
|
const pagination = ref({
|
||||||
current: 1,
|
current: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@ -51,27 +193,174 @@ const pagination = ref({
|
|||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
showQuickJumper: true,
|
showQuickJumper: true,
|
||||||
showTotal: (total: number) => `共 ${total} 条记录`
|
showTotal: (total: number) => `共 ${total} 条记录`
|
||||||
})
|
});
|
||||||
// 表格变化处理
|
|
||||||
const handleTableChange = () => {}
|
// 表格分页/排序/筛选变化(目前只处理分页)
|
||||||
const getDataList=async()=>{
|
const handleTableChange = (page: any) => {
|
||||||
loading.value = true
|
pagination.value.current = page.current;
|
||||||
|
pagination.value.pageSize = page.pageSize;
|
||||||
|
getDataList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取所有消息(用于统计未读数量)
|
||||||
|
const getAllMessages = async () => {
|
||||||
try {
|
try {
|
||||||
// 调用获取消息列表的接口
|
const params = {
|
||||||
const response:any = await getMessageList({
|
page_num: 1,
|
||||||
|
page_size: 1000, // 获取足够多的数据来统计
|
||||||
|
Message: '' // 获取全部消息
|
||||||
|
};
|
||||||
|
|
||||||
|
const response: any = await getMessageList(params);
|
||||||
|
|
||||||
|
if (response.data && Array.isArray(response.data)) {
|
||||||
|
allMessages.value = response.data;
|
||||||
|
} else if (response.data?.messages) {
|
||||||
|
allMessages.value = response.data.messages;
|
||||||
|
} else if (response.data?.list) {
|
||||||
|
allMessages.value = response.data.list;
|
||||||
|
} else {
|
||||||
|
allMessages.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('全部消息统计:', {
|
||||||
|
total: allMessages.value.length,
|
||||||
|
allUnread: allUnreadCount.value,
|
||||||
|
moneyUnread: moneyUnreadCount.value,
|
||||||
|
secureUnread: secureUnreadCount.value,
|
||||||
|
activeUnread: activeUnreadCount.value
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取全部消息失败:', error);
|
||||||
|
allMessages.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取消息列表(当前标签页)
|
||||||
|
const getDataList = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
page_num: pagination.value.current,
|
page_num: pagination.value.current,
|
||||||
page_size: pagination.value.pageSize,
|
page_size: pagination.value.pageSize,
|
||||||
Message: messageType.value
|
Message: messageType.value
|
||||||
|
};
|
||||||
|
|
||||||
|
const response: any = await getMessageList(params);
|
||||||
|
|
||||||
|
// 解析响应数据
|
||||||
|
if (response.data && Array.isArray(response.data)) {
|
||||||
|
billData.value = response.data;
|
||||||
|
pagination.value.total = response.total || response.data.length;
|
||||||
|
} else if (response.data?.messages) {
|
||||||
|
billData.value = response.data.messages;
|
||||||
|
pagination.value.total = response.data.total || 0;
|
||||||
|
} else if (response.data?.list) {
|
||||||
|
billData.value = response.data.list;
|
||||||
|
pagination.value.total = response.data.total || 0;
|
||||||
|
} else {
|
||||||
|
billData.value = [];
|
||||||
|
pagination.value.total = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('当前标签页消息:', {
|
||||||
|
type: messageType.value,
|
||||||
|
count: billData.value.length,
|
||||||
|
data: billData.value
|
||||||
});
|
});
|
||||||
billData.value = response.data; // 假设返回的数据在response.data.messages中
|
|
||||||
pagination.value.total = response.total; // 假设总记录数在response.data.total中
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取消息列表失败:', error);
|
console.error('获取消息列表失败:', error);
|
||||||
|
billData.value = [];
|
||||||
|
pagination.value.total = 0;
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听 Tab 切换
|
||||||
|
watch(activeKey, (newKey) => {
|
||||||
|
messageType.value = keyToMessageType[newKey] || '';
|
||||||
|
pagination.value.current = 1; // 切换标签页时回到第一页
|
||||||
|
getDataList();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始加载
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
getDataList()
|
// 先获取所有消息用于统计
|
||||||
})
|
getAllMessages();
|
||||||
|
// 再获取当前标签页的消息
|
||||||
|
getDataList();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 可选:添加轮询更新未读数量
|
||||||
|
// 如果希望实时更新未读数量,可以添加轮询
|
||||||
|
// setInterval(() => {
|
||||||
|
// getAllMessages();
|
||||||
|
// }, 30000); // 每30秒更新一次未读数量
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.bill-table {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab标签样式 */
|
||||||
|
.tab-label {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
transform: translate(50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 已读/未读标签样式 */
|
||||||
|
:deep(.status-read) {
|
||||||
|
background-color: #f6ffed;
|
||||||
|
border-color: #b7eb8f;
|
||||||
|
color: #52c41a;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.status-unread) {
|
||||||
|
background-color: #fff1f0;
|
||||||
|
border-color: #ffa39e;
|
||||||
|
color: #f5222d;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消息标题样式 */
|
||||||
|
:deep(.unread-title) {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.read-title) {
|
||||||
|
font-weight: normal;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 徽章样式优化 */
|
||||||
|
:deep(.ant-badge-count) {
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 0 5px;
|
||||||
|
box-shadow: 0 0 0 1px #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 当前激活的Tab样式 */
|
||||||
|
:deep(.ant-tabs-tab-active .tab-label) {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-tabs-tab:hover .tab-label) {
|
||||||
|
color: #40a9ff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user