账户设置

This commit is contained in:
Leo_Ding 2025-12-31 15:32:05 +08:00
parent 2c08ca7a73
commit 5915ece47e
12 changed files with 803 additions and 181 deletions

BIN
src/assets/camer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/assets/card1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
src/assets/card2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
src/assets/yyzz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -174,6 +174,12 @@ const routes: RouteRecordRaw[] = [
name: "History", name: "History",
component: () => import("@/views/admin/account/history/index.vue"), component: () => import("@/views/admin/account/history/index.vue"),
}, },
{
path: "accountSet",
name: "accountSet",
component: () =>
import("@/views/admin/account/accountSet/index.vue"),
},
{ {
path: "security", path: "security",
name: "Security", name: "Security",
@ -191,6 +197,18 @@ const routes: RouteRecordRaw[] = [
component: () => component: () =>
import("@/views/admin/account/enterRealAuth/index.vue"), import("@/views/admin/account/enterRealAuth/index.vue"),
}, },
{
path: "personalRealAuth",
name: "PersonalRealAuth",
component: () =>
import("@/views/admin/account/personalRealAuth/index.vue"),
},
{
path: "myCertificate",
name: "MyCertificate",
component: () =>
import("@/views/admin/account/myCertificate/index.vue"),
},
{ {
path: "image", path: "image",
name: "Image", name: "Image",

View File

@ -0,0 +1,124 @@
<template>
<div>
<a-row :gutter="[16, 16]">
<a-col :span="24">
<a-card title="基本设置">
<div style="margin:0 auto; width: 128px; padding-bottom: 20px;">
<a-upload v-model:file-list="fileList" name="avatar" list-type="picture-card"
class="avatar-uploader" :show-upload-list="false"
action="https://www.mocky.io/v2/5cc8019d300000980a055e76" :before-upload="beforeUpload"
@change="handleChange">
<img v-if="imageUrl" :src="imageUrl" alt="avatar" width="100%" height="100%" />
<div v-else>
<loading-outlined v-if="loading"></loading-outlined>
<plus-outlined v-else></plus-outlined>
<div class="ant-upload-text">Upload</div>
</div>
</a-upload>
</div>
<div class="info"><span>账户</span><span>17044054908</span></div>
<div class="info"><span>账户ID</span><span>123456789123456789</span></div>
<div class="info" style="display: flex;justify-content: space-between;">
<div>
<span style="margin-right: 25px;">认证类型</span><span style="color: red;">未实名认证</span>
</div>
<div style="color:#1677ff;">前往认证</div>
</div>
</a-card>
</a-col>
<a-col :span="24">
<a-card title="安全设置">
<div class="info"><span>手机号</span><span>17044054908</span></div>
<div class="info" style="display: flex;justify-content: space-between;">
<div>
<span style="margin-right: 25px;">绑定微信</span><span style="color: #666;">未绑定微信</span>
</div>
<div style="color:#1677ff;">绑定</div>
</div>
<div class="info" style="display: flex;justify-content: space-between;">
<div>
<span style="margin-right: 25px;">登录密码</span><span>********</span>
</div>
<div style="color:#1677ff;">修改</div>
</div>
</a-card>
</a-col>
</a-row>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons-vue';
const loading = ref(false);
const imageUrl = ref<string | null>("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
const fileList = ref<any[]>([{ url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png' }]);
const beforeUpload = (file: File) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
alert('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
alert('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
};
const handleChange = (info: any) => {
if (info.file.status === 'uploading') {
loading.value = true;
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
const reader = new FileReader();
reader.addEventListener('load', () => {
imageUrl.value = reader.result as string;
loading.value = false;
});
reader.readAsDataURL(info.file.originFileObj);
}
};
</script>
<style scoped lang="scss">
.avatar-uploader .ant-upload {
width: 128px;
height: 128px;
border-radius: 50% !important;
/* 改为 50% */
overflow: hidden;
/* 防止内部图片溢出 */
border: none !important;
background: none !important;
}
.avatar-uploader img {
width: 100%;
height: 100%;
object-fit: cover;
/* 保持比例并填满 */
border-radius: 50%;
/* 图片本身也要圆 */
}
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
.info {
display: flex;
justify-content: flex-start;
padding: 10px 20px;
border-bottom: 1px solid #f0f0f0;
// gap: 50px;
span:first-child {
width: 80px;
}
}
</style>

View File

@ -2,206 +2,133 @@
<template> <template>
<div class="enterprise-real-name-auth"> <div class="enterprise-real-name-auth">
<!-- 步骤条 --> <!-- 步骤条 -->
<a-steps :current="0" class="steps"> <div style="width: 300px;display: flex;align-items: center;gap:16px;">
<a-step title="填写证件信息" /> <div style="color:#666;cursor: pointer;" @click="router.back()">
<a-step title="后台审核" /> <ArrowLeftOutlined />&nbsp;&nbsp;返回
<a-step title="完成认证" /> </div>
</a-steps> <div style="color:#666;">|</div>
<div style="font-size: 18px;">企业认证</div>
</div>
<div style="color: #666;margin: 30px 0;">Fast亼算云严格遵守国家相关个人信息隐私保护规定并不存储使用您的个人信息请完成相关认证</div>
<div style="width: 100%;height: 1px;background: #c9c9c9;margin-bottom: 30px;"></div>
<!-- 表单 --> <!-- 表单 -->
<a-form <a-form ref="formRef" :model="formState" layout="horizontal" class="auth-form" style="max-width: 600px;">
ref="formRef" <a-form-item label="企业证件类型:" name="certType" :rules="[{ required: true, message: '请选择企业证件类型' }]">
:model="formState" <a-select v-model:value="formState.certType" placeholder="请选择">
:label-col="{ span: 4 }"
:wrapper-col="{ span: 20 }"
layout="horizontal"
class="auth-form"
>
<!-- 企业信息 -->
<div class="section-title">企业信息</div>
<a-alert
message="企业信息、法人/被授权人信息仅用于进行实名认证,不会泄露您的信息"
type="warning"
show-icon
style="margin-bottom: 24px"
/>
<a-form-item
label="企业证件类型:"
name="certType"
:rules="[{ required: true, message: '请选择企业证件类型' }]"
>
<a-select
v-model:value="formState.certType"
placeholder="请选择"
style="width: 400px"
>
<a-select-option value="business_license">营业执照</a-select-option> <a-select-option value="business_license">营业执照</a-select-option>
<!-- 可扩展其他类型 --> <!-- 可扩展其他类型 -->
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label="上传证件照:" :rules="[{ required: true, validator: validateBusinessLicense }]">
<a-form-item <div class="upload-area" style="display: flex;justify-items: flex-start;align-items: center;">
label="上传企业证件附件:" <div>
:rules="[{ required: true, validator: validateBusinessLicense }]" <a-upload name="file" list-type="picture-card" :show-upload-list="false" :before-upload="beforeUpload"
> @change="handleBusinessLicenseChange" accept="image/png,image/jpeg" class="avatar-uploader3">
<div class="upload-area">
<a-upload
name="file"
list-type="picture-card"
:show-upload-list="false"
:before-upload="beforeUpload"
@change="handleBusinessLicenseChange"
accept="image/png,image/jpeg"
>
<div class="upload-placeholder"> <div class="upload-placeholder">
<UserOutlined /> <img src="@/assets/camer.png" alt="" srcset="" width="30px">
<div style="margin-top: 8px">企业法人营业执照</div> <div style="margin-top: 8px">上传营业执照</div>
</div> </div>
</a-upload> </a-upload>
<div v-if="formState.businessLicenseUrl" class="upload-preview"> <div v-if="formState.businessLicenseUrl" class="upload-preview">
<img :src="formState.businessLicenseUrl" alt="营业执照" /> <img :src="formState.businessLicenseUrl" alt="营业执照" />
</div> </div>
</div> </div>
</div>
<div class="upload-tips">支持 jpgjpegpng 格式图片大小不超过 10M</div> <div class="upload-tips">支持 jpgjpegpng 格式图片大小不超过 10M</div>
</a-form-item> </a-form-item>
<a-form-item label="企业名称" name="enterpriseName" :rules="[{ required: true, message: '请输入企业名称' }]">
<a-form-item <a-input v-model:value="formState.enterpriseName" placeholder="请输入企业名称" />
label="企业名称:"
name="enterpriseName"
:rules="[{ required: true, message: '请输入企业名称' }]"
>
<a-input
v-model:value="formState.enterpriseName"
placeholder="请输入企业名称"
style="width: 400px"
/>
</a-form-item> </a-form-item>
<a-form-item label="统一社会信用代码" name="creditCode" :rules="[{ required: true, message: '请输入统一社会信用代码' }]">
<a-form-item <a-input v-model:value="formState.creditCode" placeholder="请输入统一社会信用代码" />
label="统一社会信用代码:"
name="creditCode"
:rules="[
{ required: true, message: '请输入统一社会信用代码' },
{ pattern: /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/, message: '请输入有效的统一社会信用代码' }
]"
>
<a-input
v-model:value="formState.creditCode"
placeholder="请输入统一社会信用代码"
style="width: 400px"
/>
</a-form-item> </a-form-item>
<hr class="dashed-line" style="border-top: 1px dashed #c9c9c9; margin: 20px 0;">
<!-- 法人/被授权人信息 --> <a-form-item label="您的身份" name="identity" :rules="[{ required: true, message: '请输入真实姓名' }]">
<div class="section-title">法人/被授权人信息</div> <a-radio-group v-model:value="formState.identity" name="identity">
<a-radio value="1">企业法人</a-radio>
<a-form-item <a-radio value="2">被授权人</a-radio>
label="请选择您的身份:"
name="identity"
:rules="[{ required: true, message: '请选择身份' }]"
>
<a-radio-group v-model:value="formState.identity">
<a-radio value="legal_representative">法定代表人</a-radio>
<a-radio value="authorized_person">被授权人</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item label="真实姓名" name="realName" :rules="[{ required: true, message: '请输入真实姓名' }]">
<!-- 身份证正反面 --> <a-input v-model:value="formState.realName" placeholder="请输入真实姓名" />
<a-form-item </a-form-item>
label="上传身份证件附件:" <a-form-item label="企业证件类型:" name="certType" :rules="[{ required: true, message: '请选择企业证件类型' }]">
:rules="[ <a-select v-model:value="formState.certType" placeholder="请选择">
{ required: true, validator: validateIdCardFront }, <a-select-option value="business_license">中国大陆二代居民身份证</a-select-option>
{ required: true, validator: validateIdCardBack } <!-- 可扩展其他类型 -->
]" </a-select>
> </a-form-item>
<div class="id-card-uploads"> <a-form-item label="上传证件照:" :rules="[{ required: true, validator: validateBusinessLicense }]">
<!-- 正面 --> <div class="upload-area" style="display: flex;justify-items: flex-start;align-items: center;">
<div class="upload-area"> <div>
<a-upload <a-upload name="file" list-type="picture-card" :show-upload-list="false" :before-upload="beforeUpload"
name="file" @change="handleBusinessLicenseChange" accept="image/png,image/jpeg" class="avatar-uploader">
list-type="picture-card"
:show-upload-list="false"
:before-upload="beforeUpload"
@change="handleIdCardFrontChange"
accept="image/png,image/jpeg"
>
<div class="upload-placeholder"> <div class="upload-placeholder">
<IdcardOutlined /> <img src="@/assets/camer.png" alt="" srcset="" width="30px">
<div style="margin-top: 8px">身份证正面</div> <div style="margin-top: 8px">拍摄身份证正面</div>
</div> </div>
</a-upload> </a-upload>
<div v-if="formState.idCardFrontUrl" class="upload-preview"> <div v-if="formState.businessLicenseUrl" class="upload-preview">
<img :src="formState.idCardFrontUrl" alt="身份证正面" /> <img :src="formState.businessLicenseUrl" alt="身份证正面" />
</div> </div>
</div> </div>
<div>
<!-- 背面 --> <a-upload name="file" list-type="picture-card" :show-upload-list="false" :before-upload="beforeUpload"
<div class="upload-area"> @change="handleBusinessLicenseChange" accept="image/png,image/jpeg" class="avatar-uploader2">
<a-upload
name="file"
list-type="picture-card"
:show-upload-list="false"
:before-upload="beforeUpload"
@change="handleIdCardBackChange"
accept="image/png,image/jpeg"
>
<div class="upload-placeholder"> <div class="upload-placeholder">
<IdcardOutlined /> <img src="@/assets/camer.png" alt="" srcset="" width="30px">
<div style="margin-top: 8px">身份证件背</div> <div style="margin-top: 8px">拍摄身份证正面</div>
</div> </div>
</a-upload> </a-upload>
<div v-if="formState.idCardBackUrl" class="upload-preview"> <div v-if="formState.businessLicenseUrl" class="upload-preview">
<img :src="formState.idCardBackUrl" alt="身份证背面" /> <img :src="formState.businessLicenseUrl" alt="身份证正面" />
</div> </div>
</div> </div>
</div> </div>
<div class="upload-tips">支持 jpgjpegpng 格式图片大小不超过 10M</div> <div class="upload-tips">支持 jpgjpegpng 格式图片大小不超过 10M</div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item label="身份证号" name="enterpriseName"
label="姓名:" :rules="[{ required: true, message: '请输入身份证号' }, { pattern: /^\d{17}[\dXx]$/, message: '请输入有效的18位身份证号码' }]">
name="name" <a-input v-model:value="formState.enterpriseName" placeholder="请输入身份证号" />
:rules="[{ required: true, message: '请输入姓名' }]"
>
<a-input
v-model:value="formState.name"
placeholder="请输入姓名"
style="width: 400px"
/>
</a-form-item> </a-form-item>
<a-form-item <a-form-item label="证件有效期" name="creditCode"
label="证件号码:" :rules="[{ required: true, message: '请选择证件有效期' }, { type: 'array', message: '请选择证件有效期' }]">
name="idNumber" <a-range-picker v-model:value="formState.creditCode" style="width: 100%;" />
:rules="[ </a-form-item>
{ required: true, message: '请输入证件号码' }, <a-form-item label="授权书" name="book" :rules="[{ required: true, message: '请上传授权书' }]">
{ pattern: /^\d{17}[\dXx]$/, message: '请输入有效的18位身份证号码' } <a-upload name="file" list-type="picture-card" :show-upload-list="false" :before-upload="beforeUpload"
]" @change="handleBusinessLicenseChange" accept="image/png,image/jpeg">
> <div class="upload-placeholder">
<a-input <div style="margin-top: 8px">上传授权书</div>
v-model:value="formState.idNumber" </div>
placeholder="请输入证件号码" </a-upload>
style="width: 400px" <div v-if="formState.businessLicenseUrl" class="upload-preview">
/> <img :src="formState.businessLicenseUrl" alt="授权书" />
</div>
<div style="color:#1677ff;">
<DownloadOutlined />
授权书下载
</div>
</a-form-item>
<a-form-item name="phone" label="手机号" :rules="[{ required: true, message: '请输入手机号!' }]">
<SmsCodeInput v-model:value="formState.phone" />
</a-form-item>
<a-form-item name="code" label="验证码" :rules="[{ required: true, message: '请输入验证码!' }]">
<a-input v-model:value="formState.code" placeholder="请输入验证码" />
</a-form-item> </a-form-item>
<!-- 协议同意 --> <!-- 协议同意 -->
<a-form-item :wrapper-col="{ offset: 4 }"> <div>
<a-checkbox v-model:checked="formState.agreed"> <a-checkbox v-model:checked="formState.agreed">
同意 您理解并同意
<a href="/docs/real_name_cert/" target="_blank">AutoDL实名认证服务协议</a> <a href="/docs/real_name_cert/" target="_blank">实名认证协议</a>
<a href="/docs/privacy_policy/" target="_blank">隐私政策</a> Fast亼算云有权自行或委托第三方审查您在实名认证时提供的信息是否真实准确及有效若提供虚假信息由此带来的后果全部由您承担
请务必提供真实信息AutoDL算力云有权自行或委托第三方审查您提供的信息是否真实/有效若提供虚假信息由此带来的后果由您承担
</a-checkbox> </a-checkbox>
</a-form-item> </div>
<div style="margin-top: 20px;text-align: center;">
<!-- 操作按钮 --> <a-button type="primary" @click="handleSubmit" block>立即认证</a-button>
<a-form-item :wrapper-col="{ offset: 4 }"> </div>
<a-button type="primary" @click="handleSubmit">确认提交</a-button>
<a-button style="margin-left: 12px" @click="handleCancel">取消</a-button>
</a-form-item>
</a-form> </a-form>
</div> </div>
</template> </template>
@ -220,9 +147,14 @@ import {
} from 'ant-design-vue'; } from 'ant-design-vue';
import { import {
UserOutlined, UserOutlined,
IdcardOutlined IdcardOutlined,
} from '@ant-design/icons-vue'; ArrowLeftOutlined,
DownloadOutlined
} from '@ant-design/icons-vue';
import { useRouter } from 'vue-router';
import SmsCodeInput from '@/components/SmsCodeInput.vue';
const router = useRouter();
// //
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
@ -238,6 +170,9 @@ interface FormState {
name: string; name: string;
idNumber: string; idNumber: string;
agreed: boolean; agreed: boolean;
phone: string;
code: string;
realName: string;
} }
const formState = reactive<FormState>({ const formState = reactive<FormState>({
@ -250,7 +185,10 @@ const formState = reactive<FormState>({
idCardBackUrl: null, idCardBackUrl: null,
name: '', name: '',
idNumber: '', idNumber: '',
agreed: false agreed: false,
phone: '',
code: '',
realName: '',
}); });
// 10MB // 10MB
@ -297,7 +235,7 @@ const handleIdCardBackChange = (info: UploadChangeParam<UploadFile>) => {
// //
const validateBusinessLicense = () => { const validateBusinessLicense = () => {
if (!formState.businessLicenseUrl) { if (!formState.businessLicenseUrl) {
return Promise.reject(new Error('请上传企业证件')); return Promise.reject(new Error('请上传证件'));
} }
return Promise.resolve(); return Promise.resolve();
}; };
@ -342,7 +280,7 @@ const handleCancel = () => {
.enterprise-real-name-auth { .enterprise-real-name-auth {
padding: 24px; padding: 24px;
background-color: #fff; background-color: #fff;
min-height: 100vh; /* min-height: 100vh; */
} }
.steps { .steps {
@ -365,7 +303,7 @@ const handleCancel = () => {
.upload-placeholder { .upload-placeholder {
text-align: center; text-align: center;
color: #999; color: #666;
} }
.upload-placeholder i { .upload-placeholder i {
@ -413,7 +351,31 @@ const handleCancel = () => {
margin-right: 0; margin-right: 0;
} }
} }
::v-deep .avatar-uploader .ant-upload.ant-upload-select {
width: 200px;
overflow: hidden;
border: none !important;
background: url("@/assets/card1.png") no-repeat center;
background-size: contain;
/* 或者 'contain' 根据需求选择 */
}
::v-deep .avatar-uploader2 .ant-upload.ant-upload-select {
width: 200px;
overflow: hidden;
border: none !important;
background: url("@/assets/card2.png") no-repeat center;
background-size: contain;
/* 或者 'contain' 根据需求选择 */
}
::v-deep .avatar-uploader3 .ant-upload.ant-upload-select {
width: 200px;
overflow: hidden;
border: none !important;
background: url("@/assets/yyzz.png") no-repeat center;
background-size: contain;
/* 或者 'contain' 根据需求选择 */
}
</style> </style>

View File

@ -0,0 +1,66 @@
<template>
<a-card title="算力券管理">
<div style="width: 300px;border: 1px solid #e8e8e8;line-height: 45px;border-radius: 5px;">
<div
style="background: linear-gradient(90deg, rgba(240, 245, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);height: 45px;line-height: 45px;padding: 0 10px;">
可用算力券</div>
<div style="font-size:18px;font-weight: bold;padding: 0 10px;">0.00</div>
</div>
<!-- 账单表格 -->
<div style="margin-top: 20px;">
<a-table :columns="columns" :data-source="billData" :pagination="pagination" @change="handleTableChange"
:loading="loading" class="bill-table" :scroll="{ x: 1200 }" bordered>
</a-table>
</div>
</a-card>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const loading = ref(false);
const billData = ref([])
const columns = [
{
title: '算力券名称',
dataIndex: 'couponId',
key: 'couponId',
width: 200,
},
{
title: '适用范围',
dataIndex: 'value',
key: 'value',
width: 100,
},
{
title: '生效时间',
dataIndex: 'acquisitionMethod',
key: 'acquisitionMethod',
width: 150,
},
{
title: '失效时间',
dataIndex: 'status',
key: 'status',
width: 100,
},
{
title: '余额',
dataIndex: 'validityPeriod',
key: 'validityPeriod',
width: 200,
},
];
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number) => `${total} 条记录`
})
//
const handleTableChange = () => {
}
</script>

View File

@ -0,0 +1,323 @@
<!-- src/views/EnterpriseRealNameAuth.vue -->
<template>
<div class="enterprise-real-name-auth">
<!-- 步骤条 -->
<div style="width: 300px;display: flex;align-items: center;gap:16px;">
<div style="color:#666;cursor: pointer;" @click="router.back()">
<ArrowLeftOutlined />&nbsp;&nbsp;返回
</div>
<div style="color:#666;">|</div>
<div style="font-size: 18px;">个人认证</div>
</div>
<div style="color: #666;margin: 30px 0;">Fast亼算云严格遵守国家相关个人信息隐私保护规定并不存储使用您的个人信息请完成相关认证</div>
<div style="width: 100%;height: 1px;background: #c9c9c9;margin-bottom: 30px;"></div>
<!-- 表单 -->
<a-form ref="formRef" :model="formState" layout="horizontal" class="auth-form" style="max-width: 600px;">
<a-form-item label="真实姓名" name="realName" :rules="[{ required: true, message: '请输入真实姓名' }]">
<a-input v-model:value="formState.realName" placeholder="请输入真实姓名" />
</a-form-item>
<a-form-item label="企业证件类型:" name="certType" :rules="[{ required: true, message: '请选择企业证件类型' }]">
<a-select v-model:value="formState.certType" placeholder="请选择">
<a-select-option value="business_license">中国大陆二代居民身份证</a-select-option>
<!-- 可扩展其他类型 -->
</a-select>
</a-form-item>
<a-form-item label="上传证件照:" :rules="[{ required: true, validator: validateBusinessLicense }]">
<div class="upload-area" style="display: flex;justify-items: flex-start;align-items: center;">
<div>
<a-upload name="file" list-type="picture-card" :show-upload-list="false" :before-upload="beforeUpload"
@change="handleBusinessLicenseChange" accept="image/png,image/jpeg" class="avatar-uploader">
<div class="upload-placeholder">
<img src="@/assets/camer.png" alt="" srcset="" width="30px">
<div style="margin-top: 8px">拍摄身份证正面</div>
</div>
</a-upload>
<div v-if="formState.businessLicenseUrl" class="upload-preview">
<img :src="formState.businessLicenseUrl" alt="身份证正面" />
</div>
</div>
<div>
<a-upload name="file" list-type="picture-card" :show-upload-list="false" :before-upload="beforeUpload"
@change="handleBusinessLicenseChange" accept="image/png,image/jpeg" class="avatar-uploader2">
<div class="upload-placeholder">
<img src="@/assets/camer.png" alt="" srcset="" width="30px">
<div style="margin-top: 8px">拍摄身份证正面</div>
</div>
</a-upload>
<div v-if="formState.businessLicenseUrl" class="upload-preview">
<img :src="formState.businessLicenseUrl" alt="身份证正面" />
</div>
</div>
</div>
<div class="upload-tips">支持 jpgjpegpng 格式图片大小不超过 10M</div>
</a-form-item>
<a-form-item label="身份证号" name="enterpriseName"
:rules="[{ required: true, message: '请输入身份证号' }, { pattern: /^\d{17}[\dXx]$/, message: '请输入有效的18位身份证号码' }]">
<a-input v-model:value="formState.enterpriseName" placeholder="请输入身份证号" />
</a-form-item>
<a-form-item label="证件有效期" name="creditCode"
:rules="[{ required: true, message: '请选择证件有效期' }, { type: 'array', message: '请选择证件有效期' }]">
<a-range-picker v-model:value="formState.creditCode" style="width: 100%;" />
</a-form-item>
<!-- <a-form-item label="手机号" name="mobile"
:rules="[{ required: true, message: '请输入手机号' }, { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码' }]">
<a-input v-model:value="formState.mobile" placeholder="请输入手机号" />
</a-form-item> -->
<a-form-item name="phone" label="手机号" :rules="[{ required: true, message: '请输入手机号!' }]">
<SmsCodeInput v-model:value="formState.phone" />
</a-form-item>
<a-form-item name="code" label="验证码" :rules="[{ required: true, message: '请输入验证码!' }]">
<a-input v-model:value="formState.code" placeholder="请输入验证码" />
</a-form-item>
<!-- 协议同意 -->
<div>
<a-checkbox v-model:checked="formState.agreed">
您理解并同意
<a href="/docs/real_name_cert/" target="_blank">实名认证协议</a>
Fast亼算云有权自行或委托第三方审查您在实名认证时提供的信息是否真实准确及有效若提供虚假信息由此带来的后果全部由您承担
</a-checkbox>
</div>
<div style="margin-top: 20px;text-align: center;">
<a-button type="primary" @click="handleSubmit" block>立即认证</a-button>
</div>
</a-form>
</div>
</template>
<script setup lang="ts">
import {
ref,
reactive,
UploadChangeParam,
UploadFile
} from 'vue';
import {
message,
FormInstance,
UploadProps
} from 'ant-design-vue';
import {
UserOutlined,
IdcardOutlined,
ArrowLeftOutlined
} from '@ant-design/icons-vue';
import { useRouter } from 'vue-router';
import SmsCodeInput from '@/components/SmsCodeInput.vue';
const router = useRouter();
//
const formRef = ref<FormInstance>();
//
interface FormState {
certType: string | null;
businessLicenseUrl: string | null;
enterpriseName: string;
creditCode: string;
identity: 'legal_representative' | 'authorized_person' | null;
idCardFrontUrl: string | null;
idCardBackUrl: string | null;
name: string;
idNumber: string;
agreed: boolean;
phone: string;
code: string;
}
const formState = reactive<FormState>({
certType: null,
businessLicenseUrl: null,
enterpriseName: '',
creditCode: '',
identity: null,
idCardFrontUrl: null,
idCardBackUrl: null,
name: '',
idNumber: '',
agreed: false,
phone: '',
code: '',
});
// 10MB
const MAX_FILE_SIZE = 10 * 1024 * 1024;
//
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
const isLt10M = file.size < MAX_FILE_SIZE;
if (!isLt10M) {
message.error('文件大小不能超过 10MB!');
}
const isValidType = ['image/jpeg', 'image/png'].includes(file.type);
if (!isValidType) {
message.error('仅支持 JPG/PNG 格式!');
}
return isLt10M && isValidType;
};
//
const handleBusinessLicenseChange = (info: UploadChangeParam<UploadFile>) => {
if (info.file.status === 'done') {
// URL
const url = URL.createObjectURL(info.file.originFileObj!);
formState.businessLicenseUrl = url;
}
};
//
const handleIdCardFrontChange = (info: UploadChangeParam<UploadFile>) => {
if (info.file.status === 'done') {
const url = URL.createObjectURL(info.file.originFileObj!);
formState.idCardFrontUrl = url;
}
};
//
const handleIdCardBackChange = (info: UploadChangeParam<UploadFile>) => {
if (info.file.status === 'done') {
const url = URL.createObjectURL(info.file.originFileObj!);
formState.idCardBackUrl = url;
}
};
//
const validateBusinessLicense = () => {
if (!formState.businessLicenseUrl) {
return Promise.reject(new Error('请上传证件照'));
}
return Promise.resolve();
};
const validateIdCardFront = () => {
if (!formState.idCardFrontUrl) {
return Promise.reject(new Error('请上传身份证正面'));
}
return Promise.resolve();
};
const validateIdCardBack = () => {
if (!formState.idCardBackUrl) {
return Promise.reject(new Error('请上传身份证背面'));
}
return Promise.resolve();
};
//
const handleSubmit = async () => {
try {
await formRef.value?.validateFields();
if (!formState.agreed) {
message.error('请阅读并同意协议');
return;
}
message.success('提交成功!');
// TODO: API
} catch (error) {
console.log('Validation failed:', error);
}
};
//
const handleCancel = () => {
// TODO:
message.info('已取消');
};
</script>
<style scoped>
.enterprise-real-name-auth {
padding: 24px;
background-color: #fff;
/* min-height: 100vh; */
}
.steps {
max-width: 800px;
margin-bottom: 32px;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #1f2937;
margin: 24px 0 16px;
}
.upload-area {
display: inline-block;
margin-right: 24px;
position: relative;
}
.upload-placeholder {
text-align: center;
color: #666;
}
.upload-placeholder i {
font-size: 24px;
margin-bottom: 8px;
}
.upload-preview {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.upload-preview img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 4px;
}
.id-card-uploads {
display: flex;
gap: 24px;
}
.upload-tips {
margin-top: 8px;
font-size: 12px;
color: #666;
}
/* 响应式 */
@media (max-width: 768px) {
.auth-form :deep(.ant-form-item-label) {
text-align: left;
}
.id-card-uploads {
flex-direction: column;
}
.upload-area {
margin-right: 0;
}
}
::v-deep .avatar-uploader .ant-upload.ant-upload-select {
width: 200px;
overflow: hidden;
border: none !important;
background: url("@/assets/card1.png") no-repeat center;
background-size: contain;
/* 或者 'contain' 根据需求选择 */
}
::v-deep .avatar-uploader2 .ant-upload.ant-upload-select {
width: 200px;
overflow: hidden;
border: none !important;
background: url("@/assets/card2.png") no-repeat center;
background-size: contain;
/* 或者 'contain' 根据需求选择 */
}
</style>

View File

@ -0,0 +1,124 @@
<template>
<div>
<a-row :gutter="[16, 16]">
<a-col :span="24">
<a-card title="基本设置">
<div style="margin:0 auto; width: 128px; padding-bottom: 20px;">
<a-upload v-model:file-list="fileList" name="avatar" list-type="picture-card"
class="avatar-uploader" :show-upload-list="false"
action="https://www.mocky.io/v2/5cc8019d300000980a055e76" :before-upload="beforeUpload"
@change="handleChange">
<img v-if="imageUrl" :src="imageUrl" alt="avatar" width="100%" height="100%" />
<div v-else>
<loading-outlined v-if="loading"></loading-outlined>
<plus-outlined v-else></plus-outlined>
<div class="ant-upload-text">Upload</div>
</div>
</a-upload>
</div>
<div class="info"><span>账户</span><span>17044054908</span></div>
<div class="info"><span>账户ID</span><span>123456789123456789</span></div>
<div class="info" style="display: flex;justify-content: space-between;">
<div>
<span style="margin-right: 25px;">认证类型</span><span style="color: red;">未实名认证</span>
</div>
<div style="color:#1677ff;">前往认证</div>
</div>
</a-card>
</a-col>
<a-col :span="24">
<a-card title="安全设置">
<div class="info"><span>手机号</span><span>17044054908</span></div>
<div class="info" style="display: flex;justify-content: space-between;">
<div>
<span style="margin-right: 25px;">绑定微信</span><span style="color: #666;">未绑定微信</span>
</div>
<div style="color:#1677ff;">绑定</div>
</div>
<div class="info" style="display: flex;justify-content: space-between;">
<div>
<span style="margin-right: 25px;">登录密码</span><span>********</span>
</div>
<div style="color:#1677ff;">修改</div>
</div>
</a-card>
</a-col>
</a-row>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons-vue';
const loading = ref(false);
const imageUrl = ref<string | null>("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
const fileList = ref<any[]>([{ url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png' }]);
const beforeUpload = (file: File) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
alert('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
alert('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
};
const handleChange = (info: any) => {
if (info.file.status === 'uploading') {
loading.value = true;
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
const reader = new FileReader();
reader.addEventListener('load', () => {
imageUrl.value = reader.result as string;
loading.value = false;
});
reader.readAsDataURL(info.file.originFileObj);
}
};
</script>
<style scoped lang="scss">
.avatar-uploader .ant-upload {
width: 128px;
height: 128px;
border-radius: 50% !important;
/* 改为 50% */
overflow: hidden;
/* 防止内部图片溢出 */
border: none !important;
background: none !important;
}
.avatar-uploader img {
width: 100%;
height: 100%;
object-fit: cover;
/* 保持比例并填满 */
border-radius: 50%;
/* 图片本身也要圆 */
}
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
.info {
display: flex;
justify-content: flex-start;
padding: 10px 20px;
border-bottom: 1px solid #f0f0f0;
// gap: 50px;
span:first-child {
width: 80px;
}
}
</style>

View File

@ -38,7 +38,7 @@
</div> </div>
</template> </template>
<template #extra> <template #extra>
<span class="extra-text">支持对公打款认证证件认证</span> <!-- <span class="extra-text">支持对公打款认证证件认证</span> -->
</template> </template>
<div class="auth-content"> <div class="auth-content">
@ -50,13 +50,13 @@
<div class="option-desc">营业执照等企业资质认证</div> <div class="option-desc">营业执照等企业资质认证</div>
</div> </div>
</div> </div>
<div class="option-item disabled"> <!-- <div class="option-item disabled">
<ClockCircleOutlined class="option-icon" /> <ClockCircleOutlined class="option-icon" />
<div class="option-info"> <div class="option-info">
<div class="option-title">银行对公账户认证</div> <div class="option-title">银行对公账户认证</div>
<div class="option-desc">即将上线敬请期待</div> <div class="option-desc">即将上线敬请期待</div>
</div> </div>
</div> </div> -->
</div> </div>
</div> </div>
</a-card> </a-card>
@ -82,7 +82,7 @@
<!-- 姓名输入 --> <!-- 姓名输入 -->
<div style="margin-bottom: 24px; display: flex; align-items: center; gap: 8px;"> <div style="margin-bottom: 24px; display: flex; align-items: center; gap: 8px;">
<span style="color: red;">*</span> <span style="color: red;">*</span>
<label style="font-size: 14px; color: #333;">真实姓名</label> <label style="font-size: 14px; color: #333;"> </label>
<a-input v-model:value="personalForm.realName" placeholder="请输入您的姓名" style="flex: 1;" /> <a-input v-model:value="personalForm.realName" placeholder="请输入您的姓名" style="flex: 1;" />
</div> </div>
@ -112,7 +112,8 @@ import {
CheckCircleOutlined, CheckCircleOutlined,
ClockCircleOutlined ClockCircleOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import router from '../../../../router'; import { useRouter } from 'vue-router';
const router = useRouter();
// //
const personalAuthModalVisible = ref(false); const personalAuthModalVisible = ref(false);
@ -125,7 +126,8 @@ const personalForm = reactive({
// //
const openPersonalAuthModal = () => { const openPersonalAuthModal = () => {
personalAuthModalVisible.value = true; // personalAuthModalVisible.value = true;
router.push('/layout/admin/personalRealAuth');
}; };
// //

View File

@ -80,6 +80,9 @@ const menuItems: MenuItem[] = [
children: [ children: [
{ path: '/layout/admin/security', name: '账号安全', visible: true }, { path: '/layout/admin/security', name: '账号安全', visible: true },
{ path: '/layout/admin/history', name: '访问记录', visible: true }, { path: '/layout/admin/history', name: '访问记录', visible: true },
{ path: '/layout/admin/accountSet', name: '账户设置', visible: true },
{ path: '/layout/admin/realnameAuth', name: '实名认证', visible: true },
{ path: '/layout/admin/myCertificate', name: '我的算力券', visible: true },
], ],
}, },
]; ];