供应商
This commit is contained in:
120
src/components/CompanyInfo/component/BankInfoTab.tsx
Normal file
120
src/components/CompanyInfo/component/BankInfoTab.tsx
Normal file
@ -0,0 +1,120 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Table } from 'antd';
|
||||
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
|
||||
import { bank } from '../services';
|
||||
import { useIntl } from 'umi';
|
||||
|
||||
interface BankInfo {
|
||||
id: string;
|
||||
interbankNumber: string;
|
||||
bank: string;
|
||||
accountName: string;
|
||||
account: string;
|
||||
currency: string;
|
||||
nation: string;
|
||||
province: string;
|
||||
city: string;
|
||||
updateTime: string;
|
||||
}
|
||||
// 表格头部
|
||||
const columns: ColumnsType<BankInfo> = [
|
||||
{
|
||||
title: 'page.workbench.bank.index',
|
||||
dataIndex: 'index',
|
||||
key: 'index',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
render: (_: any, __: any, index: number) => index + 1
|
||||
},
|
||||
{
|
||||
title: 'page.workbench.bank.interbankNumber',
|
||||
dataIndex: 'interbankNumber',
|
||||
key: 'interbankNumber',
|
||||
},
|
||||
{
|
||||
title: 'page.workbench.bank.bank',
|
||||
dataIndex: 'bank',
|
||||
key: 'bank',
|
||||
},
|
||||
{
|
||||
title: 'page.workbench.bank.accountName',
|
||||
dataIndex: 'accountName',
|
||||
key: 'accountName',
|
||||
},
|
||||
{
|
||||
title: 'page.workbench.bank.account',
|
||||
dataIndex: 'account',
|
||||
key: 'account',
|
||||
},
|
||||
{
|
||||
title: 'page.workbench.bank.currency',
|
||||
dataIndex: 'currency',
|
||||
key: 'currency',
|
||||
},
|
||||
{
|
||||
title: 'page.workbench.bank.nation',
|
||||
dataIndex: 'nation',
|
||||
key: 'nation',
|
||||
},
|
||||
{
|
||||
title: 'page.workbench.bank.province',
|
||||
dataIndex: 'province',
|
||||
key: 'province',
|
||||
},
|
||||
{
|
||||
title: 'page.workbench.bank.city',
|
||||
dataIndex: 'city',
|
||||
key: 'city',
|
||||
},
|
||||
{
|
||||
title: 'page.workbench.bank.updateTime',
|
||||
dataIndex: 'updateTime',
|
||||
key: 'updateTime',
|
||||
},
|
||||
];
|
||||
const BankInfoTab: React.FC = () => {
|
||||
//双语
|
||||
const intl = useIntl();
|
||||
//列表渲染数据
|
||||
const [data, setData] = useState<BankInfo[]>([]);
|
||||
//列表加载
|
||||
const [loading, setLoading] = useState(false);
|
||||
//列表分页
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({ current: 1, pageSize: 10, total: 0 });
|
||||
//列表方法
|
||||
const getList = async (page: number = 1, pageSize: number = 10) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { code, data, total } = await bank({page, pageSize});
|
||||
if (code === 200) {
|
||||
setData(data);
|
||||
setPagination({ current: page, pageSize, total });
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
//初始化
|
||||
useEffect(() => {
|
||||
getList();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ padding: '0 30px 0 0' }}>
|
||||
<Table
|
||||
rowKey="id"
|
||||
className="custom-table"
|
||||
columns={columns.map(column => ({
|
||||
...column,
|
||||
title: intl.formatMessage({ id: column.title as string })
|
||||
}))}
|
||||
dataSource={data}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
onChange={(pagination) => getList(pagination.current!, pagination.pageSize!)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BankInfoTab;
|
106
src/components/CompanyInfo/component/BaseInfoTab.tsx
Normal file
106
src/components/CompanyInfo/component/BaseInfoTab.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Descriptions } from 'antd';
|
||||
import { coscoSupplier } from '../services';
|
||||
import { useIntl } from 'umi';
|
||||
|
||||
const BaseInfoTab: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const [registerInfo, setRegisterInfo] = useState<any>({ base: {} });
|
||||
|
||||
const fetchData = async () => {
|
||||
const res = await coscoSupplier({});
|
||||
if (res.code === 200) {
|
||||
setRegisterInfo(res.data);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
//供应商信息
|
||||
fetchData()
|
||||
}, []);
|
||||
|
||||
if (!registerInfo?.base) return <div>{intl.formatMessage({ id: 'component.globalModal.loading' })}...</div>;
|
||||
|
||||
return (
|
||||
<div style={{ padding: '0 30px 0 0' }}>
|
||||
<Descriptions
|
||||
bordered
|
||||
column={2}
|
||||
size="middle"
|
||||
style={{ background: '#fff', padding: '16px 0 0' }}
|
||||
>
|
||||
{registerInfo.base.supplierType === 'dvs' && (
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.supplierIdentityType' })}>
|
||||
{intl.formatMessage({ id: 'component.globalModal.domesticEnterprise' })}
|
||||
</Descriptions.Item>
|
||||
)}
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.enterpriseName' })}>
|
||||
{registerInfo.base.name}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.enterpriseEnglishName' })}>
|
||||
{registerInfo.base.nameEn}
|
||||
</Descriptions.Item>
|
||||
{registerInfo.base.supplierType !== 'dvs' && (
|
||||
<>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.foreignCountryRegion' })}>
|
||||
{registerInfo.base.nation}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.foreignVAT' })}>
|
||||
{registerInfo.base.vat}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.foreignTaxpayerId' })}>
|
||||
{registerInfo.base.taxpayerId}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.foreignCurrency' })}>
|
||||
{registerInfo.base.currency}
|
||||
</Descriptions.Item>
|
||||
</>
|
||||
)}
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.creditCode' })}>
|
||||
{registerInfo.base.socialCreditCode}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.businessScope' })}>
|
||||
{registerInfo.base.range}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.registerAddress' })}>
|
||||
{registerInfo.base.regAddress}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.officeAddress' })}>
|
||||
{registerInfo.base.workAddress}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.parentCompanyInfo' })}>
|
||||
{registerInfo.base.parentCompanyInvestor}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.legalPerson' })}>
|
||||
{registerInfo.base.legalPerson}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.idCardNumber' })}>
|
||||
{registerInfo.base.idCard}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.registeredCapital' })}>
|
||||
{registerInfo.base.capital}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.supplierType' })}>
|
||||
{registerInfo.base.enterpriseType}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.contactName' })}>
|
||||
{registerInfo.base.contactsName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.contactMobile' })}>
|
||||
{registerInfo.base.contactsPhone}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.contactIdType' })}>
|
||||
{registerInfo.base.contactsType}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.contactEmail' })}>
|
||||
{registerInfo.base.contactsEmail}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={intl.formatMessage({ id: 'component.globalModal.contactPhone' })}>
|
||||
{registerInfo.base.telephone}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaseInfoTab;
|
117
src/components/CompanyInfo/component/ContactsInfoTab.tsx
Normal file
117
src/components/CompanyInfo/component/ContactsInfoTab.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Table } from 'antd';
|
||||
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
|
||||
import { coscoSupplier } from '../services';
|
||||
import { useIntl } from 'umi';
|
||||
|
||||
// 联系人信息接口
|
||||
interface Contact {
|
||||
id: string;
|
||||
name: string;
|
||||
department: string;
|
||||
position: string;
|
||||
mobile: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
updateTime: string;
|
||||
}
|
||||
|
||||
const ContactsInfoTab: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const [data, setData] = useState<Contact[]>([]);
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const fetchContacts = async (page: number = 1, pageSize: number = 10) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await coscoSupplier(page);
|
||||
if (res.code === 200) {
|
||||
setData(res.contacts || []);
|
||||
setPagination({
|
||||
current: page,
|
||||
pageSize,
|
||||
total: res.totalContacts || (res.contacts?.length || 0),
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchContacts();
|
||||
}, []);
|
||||
|
||||
const handleTableChange = (pagination: TablePaginationConfig) => {
|
||||
fetchContacts(pagination.current!, pagination.pageSize!);
|
||||
};
|
||||
|
||||
const columns: ColumnsType<Contact> = [
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.contacts.index' }),
|
||||
dataIndex: 'index',
|
||||
key: 'index',
|
||||
width: 60,
|
||||
align: 'center',
|
||||
render: (_: any, __: any, index: number) =>
|
||||
(pagination.current! - 1) * pagination.pageSize! + index + 1,
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.contacts.name' }),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.contacts.department' }),
|
||||
dataIndex: 'department',
|
||||
key: 'department',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.contacts.position' }),
|
||||
dataIndex: 'position',
|
||||
key: 'position',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.contacts.mobile' }),
|
||||
dataIndex: 'mobile',
|
||||
key: 'mobile',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.contacts.phone' }),
|
||||
dataIndex: 'phone',
|
||||
key: 'phone',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.contacts.email' }),
|
||||
dataIndex: 'email',
|
||||
key: 'email',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.contacts.updateTime' }),
|
||||
dataIndex: 'updateTime',
|
||||
key: 'updateTime',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ padding: '0 30px 0 0' }}>
|
||||
<Table
|
||||
className="custom-table"
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
pagination={pagination}
|
||||
onChange={handleTableChange}
|
||||
bordered
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactsInfoTab;
|
83
src/components/CompanyInfo/component/InvoiceTab.tsx
Normal file
83
src/components/CompanyInfo/component/InvoiceTab.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Table } from 'antd';
|
||||
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
|
||||
import { invoice } from '../services';
|
||||
import { useIntl } from 'umi';
|
||||
|
||||
interface InvoiceInfo {
|
||||
id: string;
|
||||
taxpayerType: string;
|
||||
taxpayerCode: string;
|
||||
head: string;
|
||||
address: string;
|
||||
phone: string;
|
||||
bank: string;
|
||||
account: string;
|
||||
updateTime: string;
|
||||
certificateUrl: string;
|
||||
}
|
||||
|
||||
const columns: ColumnsType<InvoiceInfo> = [
|
||||
{ title: 'page.workbench.invoice.index', dataIndex: 'index', width: 80, key: 'index', render: (_: any, __: any, index: number) => index + 1 },
|
||||
{ title: 'page.workbench.invoice.taxpayerType', dataIndex: 'taxpayerType' },
|
||||
{ title: 'page.workbench.invoice.taxpayerCode', dataIndex: 'taxpayerCode' },
|
||||
{ title: 'page.workbench.invoice.head', dataIndex: 'head' },
|
||||
{ title: 'page.workbench.invoice.address', dataIndex: 'address' },
|
||||
{ title: 'page.workbench.invoice.phone', dataIndex: 'phone' },
|
||||
{ title: 'page.workbench.invoice.bank', dataIndex: 'bank' },
|
||||
{ title: 'page.workbench.invoice.account', dataIndex: 'account' },
|
||||
{ title: 'page.workbench.invoice.updateTime', dataIndex: 'updateTime' },
|
||||
{
|
||||
title: 'page.workbench.invoice.qualificationCertificate',
|
||||
width:'180px',
|
||||
dataIndex: 'qualificationCertificate',
|
||||
render: (val: string) => (val ? <a href={val} target="_blank" rel="noreferrer">查看附件</a> : '-'),
|
||||
},
|
||||
];
|
||||
|
||||
const InvoiceTab: React.FC = () => {
|
||||
//语言切换
|
||||
const intl = useIntl();
|
||||
//列表渲染数据
|
||||
const [data, setData] = useState<InvoiceInfo[]>([]);
|
||||
//列表加载
|
||||
const [loading, setLoading] = useState(false);
|
||||
//列表分页
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({ current: 1, pageSize: 10, total: 0 });
|
||||
//列表方法
|
||||
const getList = async (page = 1, pageSize = 10) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { code, data, total } = await invoice({page, pageSize});
|
||||
if (code === 200) {
|
||||
setData(data);
|
||||
setPagination({ current: page, pageSize, total });
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
//初始化
|
||||
useEffect(() => {
|
||||
getList();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ padding: '0 30px 0 0' }}>
|
||||
<Table
|
||||
className="custom-table"
|
||||
rowKey="id"
|
||||
columns={columns.map(column => ({
|
||||
...column,
|
||||
title: intl.formatMessage({ id: column.title as string })
|
||||
}))}
|
||||
dataSource={data}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
onChange={(pagination) => getList(pagination.current!, pagination.pageSize!)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InvoiceTab;
|
74
src/components/CompanyInfo/component/OtherAttachmentsTab.tsx
Normal file
74
src/components/CompanyInfo/component/OtherAttachmentsTab.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Table } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { coscoSupplier } from '../services';
|
||||
import { useIntl } from 'umi';
|
||||
|
||||
interface AttachmentItem {
|
||||
id: string;
|
||||
fileName: string;
|
||||
fileType: string;
|
||||
uploadTime: string;
|
||||
uploader: string;
|
||||
fileUrl: string;
|
||||
}
|
||||
|
||||
const OtherAttachmentsTab: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const [data, setData] = useState<AttachmentItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
coscoSupplier(1).then((res: any) => {
|
||||
setLoading(false);
|
||||
if (res.code === 200) {
|
||||
setData(res.otherAttachments || []);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const columns: ColumnsType<AttachmentItem> = [
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.attachments.index' }),
|
||||
render: (_: any, __: any, index: number) => index + 1,
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.attachments.fileName' }),
|
||||
dataIndex: 'fileName',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.attachments.fileType' }),
|
||||
dataIndex: 'fileType',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.attachments.uploadTime' }),
|
||||
dataIndex: 'uploadTime',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.attachments.uploader' }),
|
||||
dataIndex: 'uploader',
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'page.workbench.attachments.action' }),
|
||||
dataIndex: 'fileUrl',
|
||||
render: (val: string) => (val ? <a href={val} target="_blank" rel="noreferrer">下载</a> : '-'),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ padding: '0 30px 0 0' }}>
|
||||
<Table
|
||||
className="custom-table"
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
loading={loading}
|
||||
pagination={{ pageSize: 10 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OtherAttachmentsTab;
|
75
src/components/CompanyInfo/component/QualificationTab.tsx
Normal file
75
src/components/CompanyInfo/component/QualificationTab.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Table } from 'antd';
|
||||
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
|
||||
import { qualifications } from '../services';
|
||||
import { useIntl } from 'umi';
|
||||
|
||||
interface Qualification {
|
||||
id: string;
|
||||
certificateType: string;
|
||||
name: string;
|
||||
code: string;
|
||||
typeLevel: string;
|
||||
authority: string;
|
||||
dateTime: string;
|
||||
termOfValidity: string;
|
||||
updateTime: string;
|
||||
}
|
||||
// 列表头部信息
|
||||
const columns: ColumnsType<Qualification> = [
|
||||
{ title: 'page.workbench.certificateType', dataIndex: 'certificateType' },
|
||||
{ title: 'page.workbench.certificateName', dataIndex: 'name' },
|
||||
{ title: 'page.workbench.certificateCode', dataIndex: 'code' },
|
||||
{ title: 'page.workbench.typeLevel', dataIndex: 'typeLevel' },
|
||||
{ title: 'page.workbench.authority', dataIndex: 'authority' },
|
||||
{ title: 'page.workbench.dateTime', dataIndex: 'dateTime' },
|
||||
{ title: 'page.workbench.termOfValidity', dataIndex: 'termOfValidity' },
|
||||
{ title: 'page.workbench.updateTime', dataIndex: 'updateTime' },
|
||||
];
|
||||
|
||||
const QualificationTab: React.FC = () => {
|
||||
//语言切换
|
||||
const intl = useIntl();
|
||||
//列表渲染数据
|
||||
const [data, setData] = useState<Qualification[]>([]);
|
||||
//列表加载
|
||||
const [loading, setLoading] = useState(false);
|
||||
//列表分页
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({ current: 1, pageSize: 10, total: 0 });
|
||||
//列表方法
|
||||
const getList = async (page = 1, pageSize = 10) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { code, data, total } = await qualifications({page, pageSize});
|
||||
if (code === 200) {
|
||||
setData(data);
|
||||
setPagination({ current: page, pageSize, total });
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
//初始化
|
||||
useEffect(() => {
|
||||
getList();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ padding: '0 30px 0 0' }}>
|
||||
<Table
|
||||
rowKey="id"
|
||||
className="custom-table"
|
||||
columns={columns.map(column => ({
|
||||
...column,
|
||||
title: intl.formatMessage({ id: column.title as string })
|
||||
}))}
|
||||
dataSource={data}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
onChange={(pagination) => getList(pagination.current!, pagination.pageSize!)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default QualificationTab;
|
Reference in New Issue
Block a user