Merge branch 'main' of https://gitlab.guxuan.icu/Leo_Ding/GPU_Web
This commit is contained in:
commit
b8c5436584
BIN
src/assets/renzheng.png
Normal file
BIN
src/assets/renzheng.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 272 KiB |
@ -191,6 +191,18 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () =>
|
||||
import("@/views/admin/account/realnameAuth/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "realnameAuthTrue",
|
||||
name: "realnameAuthTrue",
|
||||
component: () =>
|
||||
import("@/views/admin/account/realnameAuthTrue/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "realnameAuthBusiness",
|
||||
name: "realnameAuthBusiness",
|
||||
component: () =>
|
||||
import("@/views/admin/account/realnameAuthBusiness/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "enterRealAuth",
|
||||
name: "EnterRealAuth",
|
||||
@ -220,6 +232,18 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () =>
|
||||
import("@/views/admin/account/msgCenter/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "bankCard",
|
||||
name: "bankCard",
|
||||
component: () =>
|
||||
import("@/views/admin/account/bankCard/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "invoices",
|
||||
name: "invoices",
|
||||
component: () =>
|
||||
import("@/views/admin/account/invoices/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "image",
|
||||
name: "Image",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
712
src/views/admin/account/bankCard/index.vue
Normal file
712
src/views/admin/account/bankCard/index.vue
Normal file
@ -0,0 +1,712 @@
|
||||
<template>
|
||||
<div class="bank-card-container">
|
||||
<!-- 页面标题 -->
|
||||
<div class="header">
|
||||
<h2>我的银行卡</h2>
|
||||
<span class="subtitle">只能绑定一张银行卡</span>
|
||||
</div>
|
||||
|
||||
<!-- 状态判断:已绑定银行卡或未绑定 -->
|
||||
<div v-if="hasBoundCard" class="bound-card-section">
|
||||
<!-- 已绑定银行卡展示区域 -->
|
||||
<div class="card-display">
|
||||
<div class="card-header">
|
||||
<div class="bank-icon">
|
||||
<img src="https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg" alt="银联" />
|
||||
</div>
|
||||
<div class="card-number">{{ formatCardNumber(boundCard.cardNumber) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-details">
|
||||
<div class="detail-item">
|
||||
<span class="label">持卡人姓名:</span>
|
||||
<span class="value">{{ boundCard.realName }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">开户行:</span>
|
||||
<span class="value">{{ boundCard.bankName }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">手机号:</span>
|
||||
<span class="value">{{ boundCard.phone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-actions">
|
||||
<a-button type="link" danger @click="showUnbindModal">解绑</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 未绑定银行卡时显示添加银行卡区域 -->
|
||||
<div v-else class="add-card-section">
|
||||
<div class="add-card-box" @click="showAddModal">
|
||||
<div class="add-icon">
|
||||
<plus-outlined />
|
||||
</div>
|
||||
<div class="add-text">添加银行卡</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加银行卡对话框 -->
|
||||
<a-modal
|
||||
v-model:visible="addModalVisible"
|
||||
title="添加银行卡"
|
||||
@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-input
|
||||
v-model:value="addFormState.realName"
|
||||
placeholder="请输入持卡人真实姓名"
|
||||
size="large"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="银行卡号" name="cardNumber">
|
||||
<a-input
|
||||
v-model:value="addFormState.cardNumber"
|
||||
placeholder="请输入银行卡号"
|
||||
size="large"
|
||||
:maxlength="19"
|
||||
@input="formatCardInput"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="开户行" name="bankName">
|
||||
<a-select
|
||||
v-model:value="addFormState.bankName"
|
||||
placeholder="请选择开户行"
|
||||
size="large"
|
||||
:options="bankOptions"
|
||||
show-search
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="agreement">
|
||||
<a-checkbox v-model:checked="addFormState.agreement">
|
||||
绑定银行卡前,我已阅读并确认
|
||||
<a href="#" @click.prevent="showAgreement">《银行卡绑定协议》</a>
|
||||
</a-checkbox>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- 解绑银行卡对话框 -->
|
||||
<a-modal
|
||||
v-model:visible="unbindModalVisible"
|
||||
title="解绑银行卡"
|
||||
@ok="handleUnbindCard"
|
||||
@cancel="handleUnbindCancel"
|
||||
:confirm-loading="unbindLoading"
|
||||
:width="500"
|
||||
:ok-text="'确认解绑'"
|
||||
:cancel-text="'取消'"
|
||||
>
|
||||
<div class="unbind-info">
|
||||
<div class="info-item">
|
||||
<span class="label">持卡人姓名:</span>
|
||||
<span class="value">{{ boundCard.realName }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">银行卡号:</span>
|
||||
<span class="value">{{ formatCardNumber(boundCard.cardNumber) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">开户行:</span>
|
||||
<span class="value">{{ boundCard.bankName }}</span>
|
||||
</div>
|
||||
<a-divider />
|
||||
</div>
|
||||
|
||||
<a-form
|
||||
ref="unbindFormRef"
|
||||
:model="unbindFormState"
|
||||
:rules="unbindFormRules"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item label="手机号码" name="phone">
|
||||
<a-input
|
||||
v-model:value="unbindFormState.phone"
|
||||
placeholder="请输入银行预留手机号"
|
||||
size="large"
|
||||
:maxlength="11"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="短信验证码" name="smsCode">
|
||||
<div class="sms-code-input">
|
||||
<a-input
|
||||
v-model:value="unbindFormState.smsCode"
|
||||
placeholder="请输入短信验证码"
|
||||
size="large"
|
||||
:maxlength="6"
|
||||
/>
|
||||
<a-button
|
||||
type="primary"
|
||||
:disabled="countdown > 0"
|
||||
@click="sendSmsCode"
|
||||
class="sms-button"
|
||||
>
|
||||
{{ countdown > 0 ? `${countdown}秒后重新获取` : '获取验证码' }}
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="agreement">
|
||||
<a-checkbox v-model:checked="unbindFormState.agreement">
|
||||
解绑银行卡前,我已阅读并确认
|
||||
<a href="#" @click.prevent="showUnbindAgreement">《银行卡解绑协议》</a>
|
||||
</a-checkbox>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { PlusOutlined } from '@ant-design/icons-vue'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
|
||||
// 已绑定银行卡数据 - 初始为空
|
||||
const boundCard = ref(null)
|
||||
|
||||
// 是否已绑定银行卡 - 初始为false
|
||||
const hasBoundCard = computed(() => {
|
||||
return boundCard.value !== null && boundCard.value !== undefined
|
||||
})
|
||||
|
||||
// 银行选项
|
||||
const bankOptions = ref([
|
||||
{ label: '中国工商银行', value: '中国工商银行' },
|
||||
{ label: '中国建设银行', value: '中国建设银行' },
|
||||
{ label: '中国农业银行', value: '中国农业银行' },
|
||||
{ label: '中国银行', value: '中国银行' },
|
||||
{ label: '交通银行', value: '交通银行' },
|
||||
{ label: '招商银行', value: '招商银行' },
|
||||
{ label: '中国邮政储蓄银行', value: '中国邮政储蓄银行' },
|
||||
{ label: '中信银行', value: '中信银行' },
|
||||
{ label: '光大银行', value: '光大银行' },
|
||||
{ label: '华夏银行', value: '华夏银行' },
|
||||
{ label: '民生银行', value: '民生银行' },
|
||||
{ label: '兴业银行', value: '兴业银行' },
|
||||
{ label: '浦发银行', value: '浦发银行' },
|
||||
{ label: '平安银行', value: '平安银行' },
|
||||
{ label: '广发银行', value: '广发银行' },
|
||||
])
|
||||
|
||||
// 添加银行卡对话框状态
|
||||
const addModalVisible = ref(false)
|
||||
const addLoading = ref(false)
|
||||
const addFormRef = ref()
|
||||
const addFormState = reactive({
|
||||
realName: '',
|
||||
cardNumber: '',
|
||||
bankName: '',
|
||||
agreement: false
|
||||
})
|
||||
|
||||
// 添加银行卡表单验证规则
|
||||
const addFormRules = {
|
||||
realName: [
|
||||
{ required: true, message: '请输入真实姓名', trigger: 'blur' },
|
||||
{ pattern: /^[\u4e00-\u9fa5]{2,10}$/, message: '请输入2-10位中文字符', trigger: 'blur' }
|
||||
],
|
||||
cardNumber: [
|
||||
{ required: true, message: '请输入银行卡号', trigger: 'blur' },
|
||||
{
|
||||
validator: (_, value) => {
|
||||
const cleanValue = value.replace(/\s+/g, '')
|
||||
return /^[0-9]{16,19}$/.test(cleanValue) ?
|
||||
Promise.resolve() :
|
||||
Promise.reject('银行卡号格式不正确(16-19位数字)')
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
bankName: [
|
||||
{ required: true, message: '请选择开户行', trigger: 'change' }
|
||||
],
|
||||
agreement: [
|
||||
{ validator: (_, value) => value ? Promise.resolve() : Promise.reject('请阅读并同意协议'), trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 解绑银行卡对话框状态
|
||||
const unbindModalVisible = ref(false)
|
||||
const unbindLoading = ref(false)
|
||||
const unbindFormRef = ref()
|
||||
const unbindFormState = reactive({
|
||||
phone: '',
|
||||
smsCode: '',
|
||||
agreement: false
|
||||
})
|
||||
|
||||
// 短信验证码倒计时
|
||||
const countdown = ref(0)
|
||||
|
||||
// 解绑银行卡表单验证规则
|
||||
const unbindFormRules = {
|
||||
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' }
|
||||
],
|
||||
agreement: [
|
||||
{ validator: (_, value) => value ? Promise.resolve() : Promise.reject('请阅读并同意协议'), trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 格式化银行卡号输入(每4位加空格)
|
||||
const formatCardInput = (e) => {
|
||||
let value = e.target.value.replace(/\s+/g, '').replace(/[^\d]/g, '')
|
||||
let formattedValue = ''
|
||||
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (i > 0 && i % 4 === 0) {
|
||||
formattedValue += ' '
|
||||
}
|
||||
formattedValue += value[i]
|
||||
}
|
||||
|
||||
addFormState.cardNumber = formattedValue
|
||||
}
|
||||
|
||||
// 显示添加银行卡对话框
|
||||
const showAddModal = () => {
|
||||
addModalVisible.value = true
|
||||
// 重置表单
|
||||
Object.assign(addFormState, {
|
||||
realName: '',
|
||||
cardNumber: '',
|
||||
bankName: '',
|
||||
agreement: false
|
||||
})
|
||||
}
|
||||
|
||||
// 显示解绑银行卡对话框
|
||||
const showUnbindModal = () => {
|
||||
unbindModalVisible.value = true
|
||||
// 重置表单
|
||||
Object.assign(unbindFormState, {
|
||||
phone: '',
|
||||
smsCode: '',
|
||||
agreement: false
|
||||
})
|
||||
}
|
||||
|
||||
// 处理添加银行卡
|
||||
const handleAddCard = () => {
|
||||
addFormRef.value.validate().then(() => {
|
||||
addLoading.value = true
|
||||
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 清除空格,保存纯数字
|
||||
const cleanCardNumber = addFormState.cardNumber.replace(/\s+/g, '')
|
||||
|
||||
// 更新已绑定银行卡信息
|
||||
boundCard.value = {
|
||||
realName: addFormState.realName,
|
||||
cardNumber: cleanCardNumber,
|
||||
bankName: addFormState.bankName,
|
||||
phone: '138****' + cleanCardNumber.substr(cleanCardNumber.length - 4) // 模拟手机号
|
||||
}
|
||||
|
||||
addModalVisible.value = false
|
||||
addLoading.value = false
|
||||
|
||||
message.success('银行卡绑定成功')
|
||||
}, 1000)
|
||||
}).catch(error => {
|
||||
console.log('验证失败:', error)
|
||||
})
|
||||
}
|
||||
|
||||
// 处理解绑银行卡
|
||||
const handleUnbindCard = () => {
|
||||
unbindFormRef.value.validate().then(() => {
|
||||
unbindLoading.value = true
|
||||
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 清除已绑定银行卡信息
|
||||
boundCard.value = null
|
||||
unbindModalVisible.value = false
|
||||
unbindLoading.value = false
|
||||
|
||||
message.success('银行卡解绑成功')
|
||||
}, 1000)
|
||||
}).catch(error => {
|
||||
console.log('验证失败:', error)
|
||||
})
|
||||
}
|
||||
|
||||
// 发送短信验证码
|
||||
const sendSmsCode = () => {
|
||||
if (!unbindFormState.phone) {
|
||||
message.warning('请输入手机号码')
|
||||
return
|
||||
}
|
||||
|
||||
if (!/^1[3-9]\d{9}$/.test(unbindFormState.phone)) {
|
||||
message.warning('手机号格式不正确')
|
||||
return
|
||||
}
|
||||
|
||||
// 模拟验证手机号是否匹配
|
||||
const expectedPhone = boundCard.value.phone.replace(/\*/g, '')
|
||||
const inputPhone = unbindFormState.phone
|
||||
const lastFourDigits = boundCard.value.cardNumber.substr(boundCard.value.cardNumber.length - 4)
|
||||
|
||||
if (inputPhone !== '138' + lastFourDigits) {
|
||||
message.error('手机号与预留手机号不匹配')
|
||||
return
|
||||
}
|
||||
|
||||
// 开始倒计时
|
||||
countdown.value = 60
|
||||
const timer = setInterval(() => {
|
||||
countdown.value -= 1
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
// 模拟发送验证码(实际为123456)
|
||||
message.success('验证码已发送,请注意查收(演示验证码:123456)')
|
||||
unbindFormState.smsCode = '123456'
|
||||
}
|
||||
|
||||
// 格式化银行卡号显示
|
||||
const formatCardNumber = (cardNumber) => {
|
||||
if (!cardNumber) return ''
|
||||
const cleanNumber = cardNumber.replace(/\s+/g, '')
|
||||
const visibleDigits = 4
|
||||
const maskedPart = '*'.repeat(cleanNumber.length - visibleDigits * 2)
|
||||
const part1 = cleanNumber.substring(0, visibleDigits)
|
||||
const part2 = cleanNumber.substring(cleanNumber.length - visibleDigits)
|
||||
return `${part1} ${maskedPart} ${part2}`
|
||||
}
|
||||
|
||||
// 取消添加银行卡
|
||||
const handleAddCancel = () => {
|
||||
addModalVisible.value = false
|
||||
}
|
||||
|
||||
// 取消解绑银行卡
|
||||
const handleUnbindCancel = () => {
|
||||
unbindModalVisible.value = false
|
||||
}
|
||||
|
||||
// 显示协议(示例)
|
||||
const showAgreement = () => {
|
||||
Modal.info({
|
||||
title: '银行卡绑定协议',
|
||||
content: `
|
||||
<div style="max-height: 400px; overflow-y: auto;">
|
||||
<h4>一、总则</h4>
|
||||
<p>1. 本协议旨在明确用户绑定银行卡的相关权利和义务。</p>
|
||||
<p>2. 用户绑定银行卡前,应仔细阅读并理解本协议的全部内容。</p>
|
||||
|
||||
<h4>二、用户义务</h4>
|
||||
<p>1. 用户应确保绑定的银行卡为本人名下有效的银行卡。</p>
|
||||
<p>2. 用户应妥善保管银行卡信息,不得泄露给他人。</p>
|
||||
<p>3. 用户应确保提供的所有信息真实、准确、完整。</p>
|
||||
|
||||
<h4>三、平台责任</h4>
|
||||
<p>1. 平台应保障用户银行卡信息的安全。</p>
|
||||
<p>2. 平台不得未经用户同意擅自使用银行卡信息。</p>
|
||||
|
||||
<h4>四、风险提示</h4>
|
||||
<p>1. 用户应自行承担因银行卡信息泄露造成的损失。</p>
|
||||
<p>2. 如发现异常情况,应立即联系平台客服。</p>
|
||||
</div>
|
||||
`,
|
||||
width: 600,
|
||||
okText: '我已阅读并同意'
|
||||
})
|
||||
}
|
||||
|
||||
// 显示解绑协议(示例)
|
||||
const showUnbindAgreement = () => {
|
||||
Modal.info({
|
||||
title: '银行卡解绑协议',
|
||||
content: `
|
||||
<div style="max-height: 400px; overflow-y: auto;">
|
||||
<h4>一、解绑须知</h4>
|
||||
<p>1. 解绑银行卡将解除该银行卡与账户的绑定关系。</p>
|
||||
<p>2. 解绑后,该银行卡将无法用于账户的相关操作。</p>
|
||||
|
||||
<h4>二、解绑条件</h4>
|
||||
<p>1. 账户内无正在进行中的交易。</p>
|
||||
<p>2. 账户余额为零(如有余额需先提现)。</p>
|
||||
<p>3. 无未完成的订单或服务。</p>
|
||||
|
||||
<h4>三、解绑流程</h4>
|
||||
<p>1. 验证持卡人身份信息。</p>
|
||||
<p>2. 输入正确的短信验证码。</p>
|
||||
<p>3. 确认解绑操作。</p>
|
||||
|
||||
<h4>四、注意事项</h4>
|
||||
<p>1. 解绑操作不可撤销,请谨慎操作。</p>
|
||||
<p>2. 解绑后如需重新绑定,需重新验证身份。</p>
|
||||
<p>3. 如遇问题,请联系客服协助处理。</p>
|
||||
</div>
|
||||
`,
|
||||
width: 600,
|
||||
okText: '我已阅读并理解'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bank-card-container {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 32px;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.header h2 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header .subtitle {
|
||||
margin-left: 12px;
|
||||
color: #1890ff;
|
||||
font-size: 14px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.add-card-section, .bound-card-section {
|
||||
/* display: flex;
|
||||
justify-content: center; */
|
||||
align-items: center;
|
||||
min-height: 320px;
|
||||
}
|
||||
|
||||
.add-card-box {
|
||||
width: 320px;
|
||||
height: 200px;
|
||||
border: 2px dashed #d9d9d9;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.add-card-box:hover {
|
||||
border-color: #1890ff;
|
||||
border-style: solid;
|
||||
background-color: #f0f8ff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.1);
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
font-size: 56px;
|
||||
color: #bfbfbf;
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.add-card-box:hover .add-icon {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.add-text {
|
||||
font-size: 20px;
|
||||
color: #8c8c8c;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.add-card-box:hover .add-text {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.card-display {
|
||||
width: 450px;
|
||||
height: 240px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 16px;
|
||||
padding: 28px;
|
||||
color: white;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-display::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -50%;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 36px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.bank-icon img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-number {
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 3px;
|
||||
font-family: 'Courier New', monospace;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.card-details {
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
margin-bottom: 14px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.detail-item .label {
|
||||
opacity: 0.85;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.detail-item .value {
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
position: absolute;
|
||||
bottom: 24px;
|
||||
right: 28px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
:deep(.ant-btn-link) {
|
||||
color: rgba(255, 255, 255, 0.9) !important;
|
||||
font-size: 16px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:deep(.ant-btn-link:hover) {
|
||||
color: white !important;
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.unbind-info {
|
||||
background-color: #f6f8fa;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
margin-bottom: 12px;
|
||||
font-size: 15px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.info-item .label {
|
||||
color: #666;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.info-item .value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sms-code-input {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sms-code-input :deep(.ant-input) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sms-button {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(.ant-modal-body) {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
:deep(.ant-input-lg) {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
:deep(.ant-select-lg) {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-wrapper) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||
background-color: #1890ff;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
</style>
|
||||
779
src/views/admin/account/invoices/index.vue
Normal file
779
src/views/admin/account/invoices/index.vue
Normal file
@ -0,0 +1,779 @@
|
||||
<template>
|
||||
<div class="invoice-page">
|
||||
<!-- 头部区域 -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">
|
||||
我的发票抬头
|
||||
</h1>
|
||||
<div class="header-actions">
|
||||
<a-button type="primary" @click="showAddModal" size="large">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
新增抬头
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 发票抬头列表 -->
|
||||
<div class="invoice-list-container">
|
||||
<a-alert
|
||||
v-if="invoiceHeaders.length === 0"
|
||||
message="暂无发票抬头"
|
||||
description="点击上方按钮添加您的第一个发票抬头"
|
||||
type="info"
|
||||
show-icon
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
|
||||
<a-row :gutter="[16, 16]">
|
||||
<a-col
|
||||
v-for="header in sortedHeaders"
|
||||
:key="header.id"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
:md="12"
|
||||
:lg="8"
|
||||
>
|
||||
<a-card
|
||||
:class="['invoice-card', { 'default-card': header.isDefault }]"
|
||||
:bordered="true"
|
||||
>
|
||||
<template #actions>
|
||||
<span @click="editHeader(header)">
|
||||
<EditOutlined />
|
||||
编辑
|
||||
</span>
|
||||
<span
|
||||
@click="setDefaultHeader(header.id)"
|
||||
:class="{ disabled: header.isDefault }"
|
||||
>
|
||||
<StarOutlined />
|
||||
{{ header.isDefault ? '已是默认' : '设为默认' }}
|
||||
</span>
|
||||
<span @click="showDeleteConfirm(header)">
|
||||
<DeleteOutlined />
|
||||
删除
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<a-card-meta>
|
||||
<template #title>
|
||||
<div class="card-title">
|
||||
<span class="header-name">{{ header.name }}</span>
|
||||
<a-tag
|
||||
:color="getTypeColor(header.type)"
|
||||
size="small"
|
||||
>
|
||||
{{ getTypeText(header.type) }}
|
||||
</a-tag>
|
||||
<a-tag
|
||||
:color="getInvoiceTypeColor(header.invoiceType)"
|
||||
size="small"
|
||||
>
|
||||
{{ getInvoiceTypeText(header.invoiceType) }}
|
||||
</a-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #description>
|
||||
<div class="card-content">
|
||||
<!-- 默认标识 -->
|
||||
<div v-if="header.isDefault" class="default-badge">
|
||||
<CheckCircleFilled style="color: #52c41a; margin-right: 4px;" />
|
||||
<span>默认抬头</span>
|
||||
</div>
|
||||
|
||||
<!-- 详细信息 -->
|
||||
<div class="info-section">
|
||||
<div v-if="header.taxNumber" class="info-row">
|
||||
<span class="info-label">税号:</span>
|
||||
<span class="info-value">{{ header.taxNumber }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="header.bankName" class="info-row">
|
||||
<span class="info-label">开户银行:</span>
|
||||
<span class="info-value">{{ header.bankName }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="header.bankAccount" class="info-row">
|
||||
<span class="info-label">银行账号:</span>
|
||||
<span class="info-value">{{ header.bankAccount }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="header.companyAddress" class="info-row">
|
||||
<span class="info-label">企业地址:</span>
|
||||
<span class="info-value">{{ header.companyAddress }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="header.companyPhone" class="info-row">
|
||||
<span class="info-label">企业电话:</span>
|
||||
<span class="info-value">{{ header.companyPhone }}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-row create-time">
|
||||
<span class="info-label">创建时间:</span>
|
||||
<span class="info-value">{{ header.createTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-card-meta>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
width="600px"
|
||||
:maskClosable="false"
|
||||
:keyboard="false"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
:confirm-loading="confirmLoading"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 18 }"
|
||||
layout="horizontal"
|
||||
>
|
||||
<!-- 发票抬头名称 -->
|
||||
<a-form-item label="发票抬头" name="name" required>
|
||||
<a-input
|
||||
v-model:value="formData.name"
|
||||
placeholder="请输入发票抬头名称"
|
||||
:maxlength="100"
|
||||
show-count
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 抬头类型 -->
|
||||
<a-form-item label="抬头类型" name="type" required>
|
||||
<a-radio-group
|
||||
v-model:value="formData.type"
|
||||
button-style="solid"
|
||||
@change="handleTypeChange"
|
||||
>
|
||||
<a-radio-button value="personal">个人</a-radio-button>
|
||||
<a-radio-button value="enterprise">企业</a-radio-button>
|
||||
<a-radio-button value="institution">事业单位</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 发票类型 -->
|
||||
<a-form-item label="发票类型" name="invoiceType" required>
|
||||
<a-select
|
||||
v-model:value="formData.invoiceType"
|
||||
placeholder="请选择发票类型"
|
||||
>
|
||||
<a-select-option value="vat">增值税专用发票</a-select-option>
|
||||
<a-select-option value="normal">增值税普通发票</a-select-option>
|
||||
<a-select-option value="electronic">电子普通发票</a-select-option>
|
||||
<a-select-option value="special">专用发票</a-select-option>
|
||||
<a-select-option value="general">通用发票</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 税号(非个人时显示) -->
|
||||
<a-form-item
|
||||
v-if="formData.type !== 'personal'"
|
||||
label="税号"
|
||||
name="taxNumber"
|
||||
required
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.taxNumber"
|
||||
placeholder="请输入纳税人识别号/统一社会信用代码"
|
||||
:maxlength="30"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 开户银行(非个人时显示) -->
|
||||
<a-form-item
|
||||
v-if="formData.type !== 'personal'"
|
||||
label="开户银行"
|
||||
name="bankName"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.bankName"
|
||||
placeholder="请输入开户银行名称"
|
||||
:maxlength="100"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 银行账号(非个人时显示) -->
|
||||
<a-form-item
|
||||
v-if="formData.type !== 'personal'"
|
||||
label="银行账号"
|
||||
name="bankAccount"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.bankAccount"
|
||||
placeholder="请输入银行账号"
|
||||
:maxlength="30"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 企业地址(非个人时显示) -->
|
||||
<a-form-item
|
||||
v-if="formData.type !== 'personal'"
|
||||
label="企业地址"
|
||||
name="companyAddress"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.companyAddress"
|
||||
placeholder="请输入企业注册地址"
|
||||
:maxlength="200"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 企业电话(非个人时显示) -->
|
||||
<a-form-item
|
||||
v-if="formData.type !== 'personal'"
|
||||
label="企业电话"
|
||||
name="companyPhone"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData.companyPhone"
|
||||
placeholder="请输入企业联系电话"
|
||||
:maxlength="20"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 设为默认 -->
|
||||
<a-form-item name="isDefault" :wrapper-col="{ offset: 6 }">
|
||||
<a-checkbox v-model:checked="formData.isDefault">
|
||||
设为默认抬头
|
||||
</a-checkbox>
|
||||
<div class="form-tip">设为默认后,下单时将自动选择此抬头</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import {
|
||||
PlusOutlined,
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
StarOutlined,
|
||||
FileTextOutlined,
|
||||
CheckCircleFilled,
|
||||
ExclamationCircleOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
|
||||
// 初始数据
|
||||
const initialData = [
|
||||
{
|
||||
id: 1,
|
||||
name: '张三科技有限公司',
|
||||
type: 'enterprise',
|
||||
invoiceType: 'vat',
|
||||
taxNumber: '91370100MA3C4BQ123',
|
||||
bankName: '中国工商银行北京分行',
|
||||
bankAccount: '6222021234567890123',
|
||||
companyAddress: '北京市海淀区中关村软件园一期1号楼',
|
||||
companyPhone: '010-12345678',
|
||||
isDefault: true,
|
||||
createTime: '2024-01-15 10:30:45'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '李四个人',
|
||||
type: 'personal',
|
||||
invoiceType: 'normal',
|
||||
taxNumber: '',
|
||||
bankName: '',
|
||||
bankAccount: '',
|
||||
companyAddress: '',
|
||||
companyPhone: '',
|
||||
isDefault: false,
|
||||
createTime: '2024-01-10 14:20:15'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '某某研究所',
|
||||
type: 'institution',
|
||||
invoiceType: 'electronic',
|
||||
taxNumber: '123456789012345',
|
||||
bankName: '中国建设银行上海分行',
|
||||
bankAccount: '6217001234567890123',
|
||||
companyAddress: '上海市浦东新区张江高科技园区',
|
||||
companyPhone: '021-87654321',
|
||||
isDefault: false,
|
||||
createTime: '2024-01-05 09:15:30'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '王五网络有限公司',
|
||||
type: 'enterprise',
|
||||
invoiceType: 'vat',
|
||||
taxNumber: '91370200MA3D4CQ456',
|
||||
bankName: '招商银行深圳分行',
|
||||
bankAccount: '6225881234567890',
|
||||
companyAddress: '深圳市南山区科技园',
|
||||
companyPhone: '0755-66889900',
|
||||
isDefault: false,
|
||||
createTime: '2024-01-20 16:45:22'
|
||||
}
|
||||
]
|
||||
|
||||
// 响应式数据
|
||||
const invoiceHeaders = ref([...initialData])
|
||||
const modalVisible = ref(false)
|
||||
const modalMode = ref('add') // 'add' | 'edit'
|
||||
const confirmLoading = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
id: null,
|
||||
name: '',
|
||||
type: 'personal',
|
||||
invoiceType: 'normal',
|
||||
taxNumber: '',
|
||||
bankName: '',
|
||||
bankAccount: '',
|
||||
companyAddress: '',
|
||||
companyPhone: '',
|
||||
isDefault: false
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
name: [
|
||||
{ required: true, message: '请输入发票抬头名称', trigger: 'blur' },
|
||||
{ min: 2, max: 100, message: '抬头名称长度为2-100个字符', trigger: 'blur' }
|
||||
],
|
||||
type: [
|
||||
{ required: true, message: '请选择抬头类型', trigger: 'change' }
|
||||
],
|
||||
invoiceType: [
|
||||
{ required: true, message: '请选择发票类型', trigger: 'change' }
|
||||
],
|
||||
taxNumber: [
|
||||
{
|
||||
required: formData.type !== 'personal',
|
||||
message: '请输入税号',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
pattern: /^[A-Z0-9]{15,20}$/,
|
||||
message: '税号格式不正确(15-20位字母或数字)',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
bankAccount: [
|
||||
{
|
||||
pattern: /^[0-9]{12,20}$/,
|
||||
message: '银行账号格式不正确(12-20位数字)',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
companyPhone: [
|
||||
{
|
||||
pattern: /^(\d{3,4}-)?\d{7,8}$/,
|
||||
message: '电话号码格式不正确(例:010-12345678)',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 计算属性
|
||||
const modalTitle = computed(() => {
|
||||
return modalMode.value === 'add' ? '新增发票抬头' : '编辑发票抬头'
|
||||
})
|
||||
|
||||
const sortedHeaders = computed(() => {
|
||||
return [...invoiceHeaders.value].sort((a, b) => {
|
||||
// 默认的排在最前面
|
||||
if (a.isDefault && !b.isDefault) return -1
|
||||
if (!a.isDefault && b.isDefault) return 1
|
||||
// 其次按创建时间倒序
|
||||
return new Date(b.createTime) - new Date(a.createTime)
|
||||
})
|
||||
})
|
||||
|
||||
// 方法
|
||||
const showAddModal = () => {
|
||||
modalMode.value = 'add'
|
||||
resetForm()
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const editHeader = (header) => {
|
||||
modalMode.value = 'edit'
|
||||
Object.assign(formData, { ...header })
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
Object.assign(formData, {
|
||||
id: null,
|
||||
name: '',
|
||||
type: 'personal',
|
||||
invoiceType: 'normal',
|
||||
taxNumber: '',
|
||||
bankName: '',
|
||||
bankAccount: '',
|
||||
companyAddress: '',
|
||||
companyPhone: '',
|
||||
isDefault: false
|
||||
})
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate()
|
||||
}
|
||||
}
|
||||
|
||||
const handleTypeChange = () => {
|
||||
// 类型切换时,如果是个人类型,清除非必填字段
|
||||
if (formData.type === 'personal') {
|
||||
formData.taxNumber = ''
|
||||
formData.bankName = ''
|
||||
formData.bankAccount = ''
|
||||
formData.companyAddress = ''
|
||||
formData.companyPhone = ''
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
formRef.value.validate().then(() => {
|
||||
confirmLoading.value = true
|
||||
|
||||
// 模拟API调用延迟
|
||||
setTimeout(() => {
|
||||
if (modalMode.value === 'add') {
|
||||
addNewHeader()
|
||||
} else {
|
||||
updateHeader()
|
||||
}
|
||||
|
||||
confirmLoading.value = false
|
||||
modalVisible.value = false
|
||||
}, 500)
|
||||
}).catch(error => {
|
||||
console.log('表单验证失败:', error)
|
||||
})
|
||||
}
|
||||
|
||||
const addNewHeader = () => {
|
||||
const newHeader = {
|
||||
...formData,
|
||||
id: Date.now(), // 生成唯一ID
|
||||
createTime: new Date().toLocaleString('zh-CN')
|
||||
}
|
||||
|
||||
// 如果设置为默认,清除其他默认设置
|
||||
if (newHeader.isDefault) {
|
||||
invoiceHeaders.value.forEach(header => {
|
||||
header.isDefault = false
|
||||
})
|
||||
}
|
||||
|
||||
invoiceHeaders.value.unshift(newHeader)
|
||||
message.success('新增发票抬头成功')
|
||||
}
|
||||
|
||||
const updateHeader = () => {
|
||||
const index = invoiceHeaders.value.findIndex(item => item.id === formData.id)
|
||||
if (index !== -1) {
|
||||
const oldHeader = invoiceHeaders.value[index]
|
||||
|
||||
// 如果设置为默认,清除其他默认设置
|
||||
if (formData.isDefault && !oldHeader.isDefault) {
|
||||
invoiceHeaders.value.forEach(header => {
|
||||
header.isDefault = false
|
||||
})
|
||||
}
|
||||
|
||||
// 更新数据
|
||||
Object.assign(invoiceHeaders.value[index], formData, {
|
||||
createTime: oldHeader.createTime // 保留创建时间
|
||||
})
|
||||
|
||||
message.success('修改发票抬头成功')
|
||||
}
|
||||
}
|
||||
|
||||
const setDefaultHeader = (id) => {
|
||||
invoiceHeaders.value.forEach(header => {
|
||||
header.isDefault = header.id === id
|
||||
})
|
||||
message.success('设置默认抬头成功')
|
||||
}
|
||||
|
||||
const showDeleteConfirm = (header) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
icon: ExclamationCircleOutlined, // 这里直接传递图标组件
|
||||
content: `确定要删除发票抬头"${header.name}"吗?此操作不可恢复。`,
|
||||
okText: '删除',
|
||||
okType: 'danger',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
deleteHeader(header.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const deleteHeader = (id) => {
|
||||
const index = invoiceHeaders.value.findIndex(item => item.id === id)
|
||||
if (index !== -1) {
|
||||
const isDefault = invoiceHeaders.value[index].isDefault
|
||||
invoiceHeaders.value.splice(index, 1)
|
||||
|
||||
// 如果删除的是默认抬头,且还有其他抬头,设置第一个为默认
|
||||
if (isDefault && invoiceHeaders.value.length > 0) {
|
||||
invoiceHeaders.value[0].isDefault = true
|
||||
message.success('已自动设置第一个抬头为默认')
|
||||
}
|
||||
|
||||
message.success('删除发票抬头成功')
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
modalVisible.value = false
|
||||
resetForm()
|
||||
}
|
||||
|
||||
// 辅助函数
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
personal: '个人',
|
||||
enterprise: '企业',
|
||||
institution: '事业单位'
|
||||
}
|
||||
return typeMap[type] || '未知'
|
||||
}
|
||||
|
||||
const getTypeColor = (type) => {
|
||||
const colorMap = {
|
||||
personal: 'blue',
|
||||
enterprise: 'green',
|
||||
institution: 'orange'
|
||||
}
|
||||
return colorMap[type] || 'default'
|
||||
}
|
||||
|
||||
const getInvoiceTypeText = (type) => {
|
||||
const typeMap = {
|
||||
vat: '增值税专用',
|
||||
normal: '增值税普通',
|
||||
electronic: '电子普通',
|
||||
special: '专用发票',
|
||||
general: '通用发票'
|
||||
}
|
||||
return typeMap[type] || '未知'
|
||||
}
|
||||
|
||||
const getInvoiceTypeColor = (type) => {
|
||||
const colorMap = {
|
||||
vat: 'red',
|
||||
normal: 'purple',
|
||||
electronic: 'cyan',
|
||||
special: 'magenta',
|
||||
general: 'gray'
|
||||
}
|
||||
return colorMap[type] || 'default'
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
console.log('发票抬头管理页面已加载')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.invoice-page {
|
||||
/* padding: 10px; */
|
||||
/* background-color: #f5f5f5; */
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
/* color: #1890ff; */
|
||||
font-size: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.page-title .anticon {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.invoice-list-container {
|
||||
background: white;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.invoice-card {
|
||||
height: 100%;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.invoice-card:hover {
|
||||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.2);
|
||||
border-color: #1890ff;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.invoice-card.default-card {
|
||||
border: 2px solid #52c41a;
|
||||
background: linear-gradient(135deg, #f6ffed 0%, #e6ffd1 100%);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.header-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.default-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
background: #f6ffed;
|
||||
border: 1px solid #b7eb8f;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #52c41a;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #666;
|
||||
min-width: 70px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.create-time {
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #f0f0f0;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
:deep(.ant-card-actions) {
|
||||
border-top: 1px solid #f0f0f0;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
:deep(.ant-card-actions > li) {
|
||||
margin: 12px 0;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
:deep(.ant-card-actions > li:hover) {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
:deep(.ant-card-actions > li.disabled) {
|
||||
color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
:deep(.ant-card-actions > li span) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
:deep(.ant-modal-body) {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
:deep(.ant-form .ant-form-item .ant-form-item-label) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
:deep(.ant-form .ant-form-item .ant-form-item-control) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
782
src/views/admin/account/realnameAuthBusiness/index.vue
Normal file
782
src/views/admin/account/realnameAuthBusiness/index.vue
Normal file
@ -0,0 +1,782 @@
|
||||
<template>
|
||||
<div class="real-name-auth">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-title-area">
|
||||
<h2 class="page-title">实名认证</h2>
|
||||
<p class="page-description">
|
||||
Fast亼算云严格遵守国家相关个人信息隐私保护规定,并不存储使用您的个人信息,
|
||||
<a href="#" class="protocol-link">查看实名认证协议</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 认证信息卡片 -->
|
||||
<div class="auth-card">
|
||||
<div class="card-content">
|
||||
<!-- 左侧信息区 -->
|
||||
<div class="info-left">
|
||||
<!-- 卡片标题 -->
|
||||
<div class="card-title">
|
||||
<div class="auth-status">
|
||||
<span class="status-dot"></span>
|
||||
<span class="status-text">已认证</span>
|
||||
</div>
|
||||
<h3 class="auth-type">企业认证</h3>
|
||||
</div>
|
||||
|
||||
<!-- 信息网格 -->
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">认证类型</div>
|
||||
<div class="info-value">企业认证</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">认证时间</div>
|
||||
<div class="info-value">2025-11-18</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">证件类型</div>
|
||||
<div class="info-value">身份证</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">身份证号</div>
|
||||
<div class="info-value">
|
||||
<span>150************859</span>
|
||||
<a-button type="link" size="small" @click="handleCopy">复制</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">认证人</div>
|
||||
<div class="info-value">*如</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧图片区 -->
|
||||
<div class="image-right">
|
||||
<img src="../../../../assets/renzheng.png" alt="认证" class="auth-image" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onUnmounted } from 'vue';
|
||||
import {
|
||||
Button as AButton,
|
||||
Table as ATable,
|
||||
Modal as AModal,
|
||||
Checkbox as ACheckbox,
|
||||
Input as AInput,
|
||||
message
|
||||
} from 'ant-design-vue';
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '变更类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '变更时间',
|
||||
dataIndex: 'time',
|
||||
key: 'time',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '变更状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
slots: { customRender: 'status' },
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
dataIndex: 'operator',
|
||||
key: 'operator',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
ellipsis: true,
|
||||
},
|
||||
];
|
||||
|
||||
// 表格数据
|
||||
const dataSource = ref([
|
||||
{
|
||||
key: '1',
|
||||
type: '个人认证',
|
||||
time: '2025-11-18 14:30:25',
|
||||
status: '成功',
|
||||
operator: '系统',
|
||||
remark: '首次实名认证',
|
||||
},
|
||||
]);
|
||||
|
||||
// 对话框状态
|
||||
const changeModalVisible = ref(false);
|
||||
const isAgreeWarning = ref(false);
|
||||
|
||||
// 身份验证相关状态
|
||||
const verifyModalVisible = ref(false);
|
||||
const verificationCode = ref('');
|
||||
const countdown = ref(0);
|
||||
const sendingCode = ref(false);
|
||||
const sendStatus = ref('');
|
||||
const codeError = ref('');
|
||||
const verifying = ref(false);
|
||||
|
||||
// 用户绑定的手机号(实际应该从用户信息获取)
|
||||
const userPhone = '13800138000';
|
||||
|
||||
// 计算属性
|
||||
const maskedPhone = computed(() => {
|
||||
if (userPhone.length > 7) {
|
||||
return userPhone.slice(0, 3) + '****' + userPhone.slice(7);
|
||||
}
|
||||
return userPhone;
|
||||
});
|
||||
|
||||
const countdownText = computed(() => {
|
||||
if (countdown.value > 0) {
|
||||
return `${countdown.value}秒后重试`;
|
||||
}
|
||||
return '获取验证码';
|
||||
});
|
||||
|
||||
const isVerificationValid = computed(() => {
|
||||
return verificationCode.value.length === 6 && /^\d+$/.test(verificationCode.value);
|
||||
});
|
||||
|
||||
// 倒计时定时器
|
||||
let timer = null;
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
});
|
||||
|
||||
const getStatusClass = (status) => {
|
||||
const statusMap = {
|
||||
'成功': 'status-success',
|
||||
'失败': 'status-failed',
|
||||
'处理中': 'status-processing',
|
||||
};
|
||||
return statusMap[status] || '';
|
||||
};
|
||||
|
||||
const handleCopy = () => {
|
||||
message.success('身份证号已复制');
|
||||
};
|
||||
|
||||
// 显示变更确认对话框
|
||||
const showChangeConfirm = () => {
|
||||
// 重置确认状态
|
||||
isAgreeWarning.value = false;
|
||||
changeModalVisible.value = true;
|
||||
};
|
||||
|
||||
// 确认变更,打开身份验证对话框
|
||||
const handleConfirmChange = () => {
|
||||
if (!isAgreeWarning.value) {
|
||||
message.warning('请先确认已阅读并理解相关提示');
|
||||
return;
|
||||
}
|
||||
|
||||
// 关闭变更确认对话框
|
||||
changeModalVisible.value = false;
|
||||
|
||||
// 打开身份验证对话框
|
||||
showVerifyModal();
|
||||
};
|
||||
|
||||
// 取消变更
|
||||
const handleCancelChange = () => {
|
||||
changeModalVisible.value = false;
|
||||
isAgreeWarning.value = false;
|
||||
};
|
||||
|
||||
// 显示身份验证对话框
|
||||
const showVerifyModal = () => {
|
||||
// 重置验证状态
|
||||
verificationCode.value = '';
|
||||
codeError.value = '';
|
||||
sendStatus.value = '';
|
||||
verifyModalVisible.value = true;
|
||||
};
|
||||
|
||||
// 取消身份验证
|
||||
const handleCancelVerify = () => {
|
||||
verifyModalVisible.value = false;
|
||||
// 重置倒计时
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
countdown.value = 0;
|
||||
};
|
||||
|
||||
// 验证码输入处理
|
||||
const handleCodeInput = (e) => {
|
||||
const value = e.target?.value || e;
|
||||
// 只允许数字
|
||||
const numbersOnly = value.replace(/\D/g, '');
|
||||
verificationCode.value = numbersOnly.slice(0, 6);
|
||||
|
||||
// 清除错误提示
|
||||
if (codeError.value) {
|
||||
codeError.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
// 发送验证码
|
||||
const sendVerificationCode = async () => {
|
||||
if (countdown.value > 0 || sendingCode.value) return;
|
||||
|
||||
sendingCode.value = true;
|
||||
sendStatus.value = '发送中...';
|
||||
codeError.value = '';
|
||||
|
||||
try {
|
||||
// 模拟API调用
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// 假设发送成功
|
||||
sendStatus.value = '发送成功';
|
||||
countdown.value = 60;
|
||||
|
||||
// 开始倒计时
|
||||
timer = setInterval(() => {
|
||||
countdown.value--;
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
message.success('验证码已发送');
|
||||
} catch (error) {
|
||||
codeError.value = '发送失败,请稍后重试';
|
||||
sendStatus.value = '';
|
||||
} finally {
|
||||
sendingCode.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 执行验证
|
||||
const handleVerify = async () => {
|
||||
if (!isVerificationValid.value) {
|
||||
codeError.value = '请输入有效的6位验证码';
|
||||
return;
|
||||
}
|
||||
|
||||
verifying.value = true;
|
||||
|
||||
try {
|
||||
// 模拟API验证
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
|
||||
// 假设验证成功
|
||||
message.success('身份验证成功');
|
||||
|
||||
// 关闭验证对话框
|
||||
verifyModalVisible.value = false;
|
||||
|
||||
// 执行实际的变更操作
|
||||
message.info('跳转至企业认证页面');
|
||||
|
||||
// 这里可以添加跳转逻辑或API调用
|
||||
// router.push('/enterprise-auth');
|
||||
|
||||
} catch (error) {
|
||||
codeError.value = '验证码错误,请重新输入';
|
||||
verificationCode.value = '';
|
||||
} finally {
|
||||
verifying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleExport = () => {
|
||||
message.info('导出变更记录');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.real-name-auth {
|
||||
padding: 24px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background-color: #fafafa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 页面标题区域 */
|
||||
.page-title-area {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
margin: 0;
|
||||
color: #8c8c8c;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.protocol-link {
|
||||
color: #1890ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.protocol-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 认证卡片 */
|
||||
.auth-card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 卡片内容区域 - 左右布局 */
|
||||
.card-content {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* 左侧信息区 */
|
||||
.info-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.auth-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 8px;
|
||||
background: #f6ffed;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #52c41a;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
color: #52c41a;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.auth-type {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
/* 信息网格 */
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #595959cf;
|
||||
font-size: 16px;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #262626;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 右侧图片区 */
|
||||
.image-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 150px;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.auth-image {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: contain;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.card-actions {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.card-actions .ant-btn {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: #f0f0f0;
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
/* 变更记录区域 */
|
||||
.record-section {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.section-actions .ant-btn-link {
|
||||
color: #1890ff;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.change-record-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.change-record-table :deep(.ant-table-thead > tr > th) {
|
||||
background: #fafafa;
|
||||
color: #595959;
|
||||
font-weight: 500;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding: 12px 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.change-record-table :deep(.ant-table-tbody > tr > td) {
|
||||
padding: 12px 16px;
|
||||
font-size: 13px;
|
||||
color: #262626;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.change-record-table :deep(.ant-table-tbody > tr:hover > td) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
/* 状态标签 */
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.status-failed {
|
||||
background: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.status-processing {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
padding: 48px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 12px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
color: #bfbfbf;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 变更确认对话框样式 */
|
||||
.change-confirm-content {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.warning-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.warning-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: #fff7e6;
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid #faad14;
|
||||
}
|
||||
|
||||
.warning-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
font-size: 18px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
flex: 1;
|
||||
color: #262626;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.confirm-agreement {
|
||||
padding: 16px;
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.confirm-agreement :deep(.ant-checkbox-wrapper) {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
color: #595959;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* 身份验证对话框样式 */
|
||||
.verify-content {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.security-tip {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
background: #f6f9ff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e6f0ff;
|
||||
}
|
||||
|
||||
.security-icon {
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.security-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.security-title {
|
||||
color: #1890ff;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.security-subtitle {
|
||||
color: #595959;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.phone-info {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.phone-label {
|
||||
color: #595959;
|
||||
font-size: 13px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.phone-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.phone-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.phone-text {
|
||||
color: #262626;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.verification-code {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.code-label {
|
||||
color: #262626;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.code-input-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.code-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.code-input :deep(.ant-input) {
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.send-code-btn {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.send-code-btn:disabled {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.code-error {
|
||||
color: #ff4d4f;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.send-status {
|
||||
color: #595959;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.send-success {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.real-name-auth {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.image-right {
|
||||
width: 100%;
|
||||
order: -1;
|
||||
}
|
||||
|
||||
.auth-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.auth-card,
|
||||
.record-section {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.code-input-group {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.send-code-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
916
src/views/admin/account/realnameAuthTrue/index.vue
Normal file
916
src/views/admin/account/realnameAuthTrue/index.vue
Normal file
@ -0,0 +1,916 @@
|
||||
<template>
|
||||
<div class="real-name-auth">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-title-area">
|
||||
<h2 class="page-title">实名认证</h2>
|
||||
<p class="page-description">
|
||||
Fast亼算云严格遵守国家相关个人信息隐私保护规定,并不存储使用您的个人信息,
|
||||
<a href="#" class="protocol-link">查看实名认证协议</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 认证信息卡片 -->
|
||||
<div class="auth-card">
|
||||
<div class="card-content">
|
||||
<!-- 左侧信息区 -->
|
||||
<div class="info-left">
|
||||
<!-- 卡片标题 -->
|
||||
<div class="card-title">
|
||||
<div class="auth-status">
|
||||
<span class="status-dot"></span>
|
||||
<span class="status-text">已认证</span>
|
||||
</div>
|
||||
<h3 class="auth-type">个人认证</h3>
|
||||
</div>
|
||||
|
||||
<!-- 信息网格 -->
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">认证类型</div>
|
||||
<div class="info-value">个人认证</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">认证时间</div>
|
||||
<div class="info-value">2025-11-18</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">证件类型</div>
|
||||
<div class="info-value">身份证</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">身份证号</div>
|
||||
<div class="info-value">
|
||||
<span>150************859</span>
|
||||
<a-button type="link" size="small" @click="handleCopy">复制</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">认证人</div>
|
||||
<div class="info-value">*如</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧图片区 -->
|
||||
<div class="image-right">
|
||||
<img src="../../../../assets/renzheng.png" alt="认证" class="auth-image" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作区域 -->
|
||||
<div class="card-actions">
|
||||
<a-button type="primary" size="large" @click="showChangeConfirm">
|
||||
变更为企业认证
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- 变更记录 -->
|
||||
<div class="record-section">
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">变更记录</h3>
|
||||
<!-- <div class="section-actions">
|
||||
<a-button type="link" size="small" @click="handleExport">导出记录</a-button>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<a-table :columns="columns" :data-source="dataSource" :pagination="false" :bordered="true"
|
||||
class="change-record-table">
|
||||
<template #empty>
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">📄</div>
|
||||
<p class="empty-text">暂无变更记录</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #status="{ text }">
|
||||
<span :class="['status-tag', getStatusClass(text)]">
|
||||
{{ text }}
|
||||
</span>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 变更确认对话框 -->
|
||||
<a-modal
|
||||
v-model:visible="changeModalVisible"
|
||||
title="确认变更认证类型"
|
||||
@ok="handleConfirmChange"
|
||||
@cancel="handleCancelChange"
|
||||
:maskClosable="false"
|
||||
:width="520"
|
||||
:ok-button-props="{ disabled: !isAgreeWarning }"
|
||||
>
|
||||
<div class="change-confirm-content">
|
||||
<!-- 警告提示 -->
|
||||
<div class="warning-section">
|
||||
<div class="warning-item">
|
||||
<span class="warning-icon">⚠️</span>
|
||||
<div class="warning-text">
|
||||
您正在进行个人实名认证变更为企业实名认证操作,变更后账号以及账号下资产将属于新的认证企业。
|
||||
</div>
|
||||
</div>
|
||||
<div class="warning-item">
|
||||
<span class="warning-icon">⚠️</span>
|
||||
<div class="warning-text">
|
||||
请知悉升级企业实名认证后,该账号将无法再变更回个人实名认证,请您谨慎操作。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 确认条款 -->
|
||||
<div class="confirm-agreement">
|
||||
<a-checkbox v-model:checked="isAgreeWarning">
|
||||
<span class="agreement-text">
|
||||
我已阅读并理解以上提示,确认继续变更
|
||||
</span>
|
||||
</a-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 身份验证对话框 -->
|
||||
<a-modal
|
||||
v-model:visible="verifyModalVisible"
|
||||
title="身份验证"
|
||||
@ok="handleVerify"
|
||||
@cancel="handleCancelVerify"
|
||||
:maskClosable="false"
|
||||
:width="480"
|
||||
:ok-button-props="{ disabled: !isVerificationValid }"
|
||||
:confirmLoading="verifying"
|
||||
>
|
||||
<div class="verify-content">
|
||||
<!-- 安全提示 -->
|
||||
<div class="security-tip">
|
||||
<!-- <div class="security-icon">🔒</div> -->
|
||||
<div class="security-text">
|
||||
<div class="security-title">为确保账号安全,请您本人操作进行身份验证</div>
|
||||
<div class="security-subtitle">我们将向您绑定的手机发送验证码</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 手机号信息 -->
|
||||
<div class="phone-info">
|
||||
<div class="phone-label">验证手机号</div>
|
||||
<div class="phone-number">
|
||||
<!-- <span class="phone-icon">📱</span> -->
|
||||
<span class="phone-text">{{ maskedPhone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 验证码输入 -->
|
||||
<div class="verification-code">
|
||||
<div class="code-label">验证码</div>
|
||||
<div class="code-input-group">
|
||||
<a-input
|
||||
v-model:value="verificationCode"
|
||||
placeholder="请输入6位验证码"
|
||||
:maxlength="6"
|
||||
@input="handleCodeInput"
|
||||
class="code-input"
|
||||
/>
|
||||
<a-button
|
||||
type="link"
|
||||
@click="sendVerificationCode"
|
||||
:disabled="countdown > 0 || sendingCode"
|
||||
class="send-code-btn"
|
||||
>
|
||||
{{ countdownText }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div v-if="codeError" class="code-error">{{ codeError }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 发送状态 -->
|
||||
<div v-if="sendStatus" class="send-status" :class="{ 'send-success': sendStatus === '发送成功' }">
|
||||
{{ sendStatus }}
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onUnmounted } from 'vue';
|
||||
import {
|
||||
Button as AButton,
|
||||
Table as ATable,
|
||||
Modal as AModal,
|
||||
Checkbox as ACheckbox,
|
||||
Input as AInput,
|
||||
message
|
||||
} from 'ant-design-vue';
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '变更类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '变更时间',
|
||||
dataIndex: 'time',
|
||||
key: 'time',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '变更状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
slots: { customRender: 'status' },
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
dataIndex: 'operator',
|
||||
key: 'operator',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
ellipsis: true,
|
||||
},
|
||||
];
|
||||
|
||||
// 表格数据
|
||||
const dataSource = ref([
|
||||
{
|
||||
key: '1',
|
||||
type: '个人认证',
|
||||
time: '2025-11-18 14:30:25',
|
||||
status: '成功',
|
||||
operator: '系统',
|
||||
remark: '首次实名认证',
|
||||
},
|
||||
]);
|
||||
|
||||
// 对话框状态
|
||||
const changeModalVisible = ref(false);
|
||||
const isAgreeWarning = ref(false);
|
||||
|
||||
// 身份验证相关状态
|
||||
const verifyModalVisible = ref(false);
|
||||
const verificationCode = ref('');
|
||||
const countdown = ref(0);
|
||||
const sendingCode = ref(false);
|
||||
const sendStatus = ref('');
|
||||
const codeError = ref('');
|
||||
const verifying = ref(false);
|
||||
|
||||
// 用户绑定的手机号(实际应该从用户信息获取)
|
||||
const userPhone = '13800138000';
|
||||
|
||||
// 计算属性
|
||||
const maskedPhone = computed(() => {
|
||||
if (userPhone.length > 7) {
|
||||
return userPhone.slice(0, 3) + '****' + userPhone.slice(7);
|
||||
}
|
||||
return userPhone;
|
||||
});
|
||||
|
||||
const countdownText = computed(() => {
|
||||
if (countdown.value > 0) {
|
||||
return `${countdown.value}秒后重试`;
|
||||
}
|
||||
return '获取验证码';
|
||||
});
|
||||
|
||||
const isVerificationValid = computed(() => {
|
||||
return verificationCode.value.length === 6 && /^\d+$/.test(verificationCode.value);
|
||||
});
|
||||
|
||||
// 倒计时定时器
|
||||
let timer = null;
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
});
|
||||
|
||||
const getStatusClass = (status) => {
|
||||
const statusMap = {
|
||||
'成功': 'status-success',
|
||||
'失败': 'status-failed',
|
||||
'处理中': 'status-processing',
|
||||
};
|
||||
return statusMap[status] || '';
|
||||
};
|
||||
|
||||
const handleCopy = () => {
|
||||
message.success('身份证号已复制');
|
||||
};
|
||||
|
||||
// 显示变更确认对话框
|
||||
const showChangeConfirm = () => {
|
||||
// 重置确认状态
|
||||
isAgreeWarning.value = false;
|
||||
changeModalVisible.value = true;
|
||||
};
|
||||
|
||||
// 确认变更,打开身份验证对话框
|
||||
const handleConfirmChange = () => {
|
||||
if (!isAgreeWarning.value) {
|
||||
message.warning('请先确认已阅读并理解相关提示');
|
||||
return;
|
||||
}
|
||||
|
||||
// 关闭变更确认对话框
|
||||
changeModalVisible.value = false;
|
||||
|
||||
// 打开身份验证对话框
|
||||
showVerifyModal();
|
||||
};
|
||||
|
||||
// 取消变更
|
||||
const handleCancelChange = () => {
|
||||
changeModalVisible.value = false;
|
||||
isAgreeWarning.value = false;
|
||||
};
|
||||
|
||||
// 显示身份验证对话框
|
||||
const showVerifyModal = () => {
|
||||
// 重置验证状态
|
||||
verificationCode.value = '';
|
||||
codeError.value = '';
|
||||
sendStatus.value = '';
|
||||
verifyModalVisible.value = true;
|
||||
};
|
||||
|
||||
// 取消身份验证
|
||||
const handleCancelVerify = () => {
|
||||
verifyModalVisible.value = false;
|
||||
// 重置倒计时
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
countdown.value = 0;
|
||||
};
|
||||
|
||||
// 验证码输入处理
|
||||
const handleCodeInput = (e) => {
|
||||
const value = e.target?.value || e;
|
||||
// 只允许数字
|
||||
const numbersOnly = value.replace(/\D/g, '');
|
||||
verificationCode.value = numbersOnly.slice(0, 6);
|
||||
|
||||
// 清除错误提示
|
||||
if (codeError.value) {
|
||||
codeError.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
// 发送验证码
|
||||
const sendVerificationCode = async () => {
|
||||
if (countdown.value > 0 || sendingCode.value) return;
|
||||
|
||||
sendingCode.value = true;
|
||||
sendStatus.value = '发送中...';
|
||||
codeError.value = '';
|
||||
|
||||
try {
|
||||
// 模拟API调用
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// 假设发送成功
|
||||
sendStatus.value = '发送成功';
|
||||
countdown.value = 60;
|
||||
|
||||
// 开始倒计时
|
||||
timer = setInterval(() => {
|
||||
countdown.value--;
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
message.success('验证码已发送');
|
||||
} catch (error) {
|
||||
codeError.value = '发送失败,请稍后重试';
|
||||
sendStatus.value = '';
|
||||
} finally {
|
||||
sendingCode.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 执行验证
|
||||
const handleVerify = async () => {
|
||||
if (!isVerificationValid.value) {
|
||||
codeError.value = '请输入有效的6位验证码';
|
||||
return;
|
||||
}
|
||||
|
||||
verifying.value = true;
|
||||
|
||||
try {
|
||||
// 模拟API验证
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
|
||||
// 假设验证成功
|
||||
message.success('身份验证成功');
|
||||
|
||||
// 关闭验证对话框
|
||||
verifyModalVisible.value = false;
|
||||
|
||||
// 执行实际的变更操作
|
||||
message.info('跳转至企业认证页面');
|
||||
|
||||
// 这里可以添加跳转逻辑或API调用
|
||||
// router.push('/enterprise-auth');
|
||||
|
||||
} catch (error) {
|
||||
codeError.value = '验证码错误,请重新输入';
|
||||
verificationCode.value = '';
|
||||
} finally {
|
||||
verifying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleExport = () => {
|
||||
message.info('导出变更记录');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.real-name-auth {
|
||||
padding: 24px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background-color: #fafafa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 页面标题区域 */
|
||||
.page-title-area {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
margin: 0;
|
||||
color: #8c8c8c;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.protocol-link {
|
||||
color: #1890ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.protocol-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 认证卡片 */
|
||||
.auth-card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 卡片内容区域 - 左右布局 */
|
||||
.card-content {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* 左侧信息区 */
|
||||
.info-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.auth-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 8px;
|
||||
background: #f6ffed;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #52c41a;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
color: #52c41a;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.auth-type {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
/* 信息网格 */
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #595959cf;
|
||||
font-size: 16px;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #262626;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 右侧图片区 */
|
||||
.image-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 150px;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.auth-image {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: contain;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.card-actions {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.card-actions .ant-btn {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: #f0f0f0;
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
/* 变更记录区域 */
|
||||
.record-section {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.section-actions .ant-btn-link {
|
||||
color: #1890ff;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.change-record-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.change-record-table :deep(.ant-table-thead > tr > th) {
|
||||
background: #fafafa;
|
||||
color: #595959;
|
||||
font-weight: 500;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding: 12px 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.change-record-table :deep(.ant-table-tbody > tr > td) {
|
||||
padding: 12px 16px;
|
||||
font-size: 13px;
|
||||
color: #262626;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.change-record-table :deep(.ant-table-tbody > tr:hover > td) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
/* 状态标签 */
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.status-failed {
|
||||
background: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.status-processing {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
padding: 48px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 12px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
color: #bfbfbf;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 变更确认对话框样式 */
|
||||
.change-confirm-content {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.warning-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.warning-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: #fff7e6;
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid #faad14;
|
||||
}
|
||||
|
||||
.warning-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
font-size: 18px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
flex: 1;
|
||||
color: #262626;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.confirm-agreement {
|
||||
padding: 16px;
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.confirm-agreement :deep(.ant-checkbox-wrapper) {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
color: #595959;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* 身份验证对话框样式 */
|
||||
.verify-content {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.security-tip {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
background: #f6f9ff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e6f0ff;
|
||||
}
|
||||
|
||||
.security-icon {
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.security-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.security-title {
|
||||
color: #1890ff;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.security-subtitle {
|
||||
color: #595959;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.phone-info {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.phone-label {
|
||||
color: #595959;
|
||||
font-size: 13px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.phone-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.phone-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.phone-text {
|
||||
color: #262626;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.verification-code {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.code-label {
|
||||
color: #262626;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.code-input-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.code-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.code-input :deep(.ant-input) {
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.send-code-btn {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.send-code-btn:disabled {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.code-error {
|
||||
color: #ff4d4f;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.send-status {
|
||||
color: #595959;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.send-success {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.real-name-auth {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.image-right {
|
||||
width: 100%;
|
||||
order: -1;
|
||||
}
|
||||
|
||||
.auth-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.auth-card,
|
||||
.record-section {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.code-input-group {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.send-code-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -90,6 +90,8 @@ const menuItems: MenuItem[] = [
|
||||
{ path: '/layout/admin/myCertificate', name: '我的算力券', visible: true },
|
||||
{ path: '/layout/admin/myInvite', name: '我的邀请', visible: true },
|
||||
{ path: '/layout/admin/msgCenter', name: '消息中心', visible: true },
|
||||
{ path: '/layout/admin/bankCard', name: '银行卡管理', visible: true },
|
||||
{ path: '/layout/admin/invoices', name: '发票抬头管理', visible: true },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user