Feature/0723/zr #8
@@ -13,10 +13,25 @@ import {
|
||||
Spin,
|
||||
} from 'ant-design-vue';
|
||||
import { JUMP_TYPE_OPTIONS } from '../model/useBannerModel';
|
||||
import {
|
||||
TITLE_MAX,
|
||||
PATH_MAX,
|
||||
URL_MAX,
|
||||
titleRules,
|
||||
imageUrlRules,
|
||||
typeRules,
|
||||
linkUrlRules,
|
||||
miniProgramPathRules,
|
||||
areaRules,
|
||||
startTimeRules,
|
||||
endTimeRules,
|
||||
sortRules,
|
||||
} from '../model/form';
|
||||
import { getProvinceCityCascaderOptions, cityNameToCascaderPath } from '@/utils/areaData';
|
||||
import { ImageCropper } from '@yp-component/root';
|
||||
import { FileImageOutlined } from '@ant-design/icons-vue';
|
||||
import { uploadFile, OssUploadType } from '@/utils/oss';
|
||||
import { isImageFile } from '@/utils/form';
|
||||
import { getEventList, getEventDetail, type TournamentAdminVO } from '../../list/model/services';
|
||||
import dayjs from 'dayjs';
|
||||
import styles from './BannerFormModal.module.less';
|
||||
@@ -40,11 +55,6 @@ const getDefaultForm = () => ({
|
||||
sort: 1,
|
||||
});
|
||||
|
||||
/** 表单字段最大长度限制 */
|
||||
const TITLE_MAX = 50;
|
||||
const PATH_MAX = 200;
|
||||
const URL_MAX = 500;
|
||||
|
||||
/** 图片上传限制:最大 10MB */
|
||||
const IMAGE_MAX_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
@@ -351,7 +361,7 @@ export default defineComponent({
|
||||
target.value = '';
|
||||
|
||||
if (!file) return;
|
||||
if (!/^image\//.test(file.type)) {
|
||||
if (!isImageFile(file)) {
|
||||
message.error('请选择图片文件');
|
||||
return;
|
||||
}
|
||||
@@ -468,14 +478,7 @@ export default defineComponent({
|
||||
requiredMark
|
||||
>
|
||||
{/* ===== Banner 标题 ===== */}
|
||||
<Form.Item
|
||||
label="Banner标题"
|
||||
name="title"
|
||||
rules={[
|
||||
{ required: true, message: '请输入' },
|
||||
{ max: TITLE_MAX, message: `标题最多 ${TITLE_MAX} 个字符` },
|
||||
]}
|
||||
>
|
||||
<Form.Item label="Banner标题" name="title" rules={titleRules}>
|
||||
<Input
|
||||
placeholder="请输入标题"
|
||||
maxlength={TITLE_MAX}
|
||||
@@ -488,23 +491,10 @@ export default defineComponent({
|
||||
<Form.Item
|
||||
label="上传图片"
|
||||
name="imageUrl"
|
||||
rules={[
|
||||
{ required: true, message: '请上传 Banner 图片' },
|
||||
{
|
||||
validator: async () => {
|
||||
if (formData.imageStatus === 'uploading') {
|
||||
return Promise.reject(new Error('图片仍在上传中,请稍候'));
|
||||
}
|
||||
if (
|
||||
formData.imageUrl?.startsWith('blob:') ||
|
||||
formData.imageUrl?.startsWith('data:')
|
||||
) {
|
||||
return Promise.reject(new Error('请等待图片上传完成'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
rules={imageUrlRules(
|
||||
() => formData.imageStatus,
|
||||
|
|
||||
() => formData.imageUrl,
|
||||
)}
|
||||
>
|
||||
<div class={styles.uploader}>
|
||||
<input
|
||||
@@ -549,11 +539,7 @@ export default defineComponent({
|
||||
</Form.Item>
|
||||
|
||||
{/* ===== 跳转类型 ===== */}
|
||||
<Form.Item
|
||||
label="跳转类型"
|
||||
name="type"
|
||||
rules={[{ required: true, message: '请选择跳转类型' }]}
|
||||
>
|
||||
<Form.Item label="跳转类型" name="type" rules={typeRules}>
|
||||
<Select
|
||||
options={JUMP_TYPE_OPTIONS as any}
|
||||
placeholder="请选择"
|
||||
@@ -584,14 +570,7 @@ export default defineComponent({
|
||||
|
||||
{/* ===== 联动字段:自定义链接 ===== */}
|
||||
{formData.type === '2' && (
|
||||
<Form.Item
|
||||
label="跳转地址"
|
||||
name="linkUrl"
|
||||
rules={[
|
||||
{ required: true, message: '请输入跳转地址' },
|
||||
{ max: URL_MAX, message: `链接最多 ${URL_MAX} 个字符` },
|
||||
]}
|
||||
>
|
||||
<Form.Item label="跳转地址" name="linkUrl" rules={linkUrlRules}>
|
||||
<Input
|
||||
placeholder="https://xxx"
|
||||
maxlength={URL_MAX}
|
||||
@@ -603,14 +582,7 @@ export default defineComponent({
|
||||
|
||||
{/* ===== 联动字段:小程序页面 ===== */}
|
||||
{formData.type === '3' && (
|
||||
<Form.Item
|
||||
label="小程序路径"
|
||||
name="linkUrl"
|
||||
rules={[
|
||||
{ required: true, message: '请输入小程序页面路径' },
|
||||
{ max: PATH_MAX, message: `路径最多 ${PATH_MAX} 个字符` },
|
||||
]}
|
||||
>
|
||||
<Form.Item label="小程序路径" name="linkUrl" rules={miniProgramPathRules}>
|
||||
<Input
|
||||
placeholder="/pages/index/index"
|
||||
maxlength={PATH_MAX}
|
||||
@@ -621,11 +593,7 @@ export default defineComponent({
|
||||
)}
|
||||
|
||||
{/* ===== 所属区域(省市树种选择 + 全国) ===== */}
|
||||
<Form.Item
|
||||
label="所属区域"
|
||||
name="area"
|
||||
rules={[{ required: true, message: '请选择所属区域' }]}
|
||||
>
|
||||
<Form.Item label="所属区域" name="area" rules={areaRules}>
|
||||
<Cascader
|
||||
v-model:value={areaPath.value}
|
||||
options={areaCascaderOptions}
|
||||
|
chenzhen
commented
this v-model this v-model
|
||||
@@ -641,16 +609,7 @@ export default defineComponent({
|
||||
label="开始时间"
|
||||
name="displayStartTime"
|
||||
class={styles.timeItem}
|
||||
rules={[
|
||||
{
|
||||
validator: async () => {
|
||||
if (!formData.displayTimeRange?.[0]) {
|
||||
return Promise.reject(new Error('请选择开始时间'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
rules={startTimeRules(() => formData.displayTimeRange)}
|
||||
>
|
||||
<DatePicker
|
||||
showTime
|
||||
@@ -679,16 +638,7 @@ export default defineComponent({
|
||||
label="结束时间"
|
||||
name="displayEndTime"
|
||||
class={styles.timeItem}
|
||||
rules={[
|
||||
{
|
||||
validator: async () => {
|
||||
if (!formData.displayTimeRange?.[1]) {
|
||||
return Promise.reject(new Error('请选择结束时间'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
rules={endTimeRules(() => formData.displayTimeRange)}
|
||||
>
|
||||
<DatePicker
|
||||
showTime
|
||||
@@ -715,14 +665,7 @@ export default defineComponent({
|
||||
</div>
|
||||
|
||||
{/* ===== 排序 ===== */}
|
||||
<Form.Item
|
||||
label="排序"
|
||||
name="sort"
|
||||
rules={[
|
||||
{ required: true, message: '请输入排序' },
|
||||
{ type: 'number', min: 1, max: 999, message: '排序范围 1-999' },
|
||||
]}
|
||||
>
|
||||
<Form.Item label="排序" name="sort" rules={sortRules}>
|
||||
<InputNumber
|
||||
placeholder="数字越小越靠前"
|
||||
min={1}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Banner 表单域控制器
|
||||
*
|
||||
* 集中管理 Banner 新增/编辑弹窗的所有表单规则校验与常量。
|
||||
* 各规则数组 / 工厂函数可直接传给 antd Form.Item 的 rules prop。
|
||||
*/
|
||||
import type { Rule } from 'ant-design-vue/es/form';
|
||||
|
||||
// ============================================================
|
||||
// 常量
|
||||
// ============================================================
|
||||
|
||||
/** 标题最大长度 */
|
||||
export const TITLE_MAX = 50;
|
||||
/** 小程序路径最大长度 */
|
||||
export const PATH_MAX = 200;
|
||||
/** 跳转链接最大长度 */
|
||||
export const URL_MAX = 500;
|
||||
|
||||
// ============================================================
|
||||
// 表单规则
|
||||
// ============================================================
|
||||
|
||||
/** Banner 标题:必填 + 50 字符上限 */
|
||||
export const titleRules: Rule[] = [
|
||||
{ required: true, message: '请输入' },
|
||||
{ max: TITLE_MAX, message: `标题最多 ${TITLE_MAX} 个字符` },
|
||||
];
|
||||
|
||||
/**
|
||||
* Banner 图片:必填 + 上传状态校验
|
||||
*
|
||||
* @param getImageStatus 获取当前上传状态的 getter
|
||||
* @param getImageUrl 获取当前图片 URL 的 getter
|
||||
*/
|
||||
export function imageUrlRules(getImageStatus: () => string, getImageUrl: () => string): Rule[] {
|
||||
return [
|
||||
{ required: true, message: '请上传 Banner 图片' },
|
||||
{
|
||||
validator: async () => {
|
||||
if (getImageStatus() === 'uploading') {
|
||||
return Promise.reject(new Error('图片仍在上传中,请稍候'));
|
||||
}
|
||||
const url = getImageUrl();
|
||||
if (url.startsWith('blob:') || url.startsWith('data:')) {
|
||||
return Promise.reject(new Error('请等待图片上传完成'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 跳转类型:必选 */
|
||||
export const typeRules: Rule[] = [{ required: true, message: '请选择跳转类型' }];
|
||||
|
||||
/** 跳转地址(自定义链接 scene=2):必填 + 最大长度 */
|
||||
export const linkUrlRules: Rule[] = [
|
||||
{ required: true, message: '请输入跳转地址' },
|
||||
{ max: URL_MAX, message: `链接最多 ${URL_MAX} 个字符` },
|
||||
];
|
||||
|
||||
/** 小程序页面路径(scene=3):必填 + 最大长度 */
|
||||
export const miniProgramPathRules: Rule[] = [
|
||||
{ required: true, message: '请输入小程序页面路径' },
|
||||
{ max: PATH_MAX, message: `路径最多 ${PATH_MAX} 个字符` },
|
||||
];
|
||||
|
||||
/** 所属区域:必选 */
|
||||
export const areaRules: Rule[] = [{ required: true, message: '请选择所属区域' }];
|
||||
|
||||
/** 排序:必填 + 1-999 整数 */
|
||||
export const sortRules: Rule[] = [
|
||||
{ required: true, message: '请输入排序' },
|
||||
{ type: 'number', min: 1, max: 999, message: '排序范围 1-999' },
|
||||
];
|
||||
|
||||
/**
|
||||
* 展示开始时间规则:校验 displayTimeRange[0] 非空
|
||||
*
|
||||
* @param getDisplayTimeRange 获取展示时间范围的 getter
|
||||
*/
|
||||
export function startTimeRules(getDisplayTimeRange: () => [string, string] | null): Rule[] {
|
||||
return [
|
||||
{
|
||||
validator: async () => {
|
||||
const range = getDisplayTimeRange();
|
||||
if (!range?.[0]) {
|
||||
return Promise.reject(new Error('请选择开始时间'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示结束时间规则:校验 displayTimeRange[1] 非空
|
||||
*
|
||||
* @param getDisplayTimeRange 获取展示时间范围的 getter
|
||||
*/
|
||||
export function endTimeRules(getDisplayTimeRange: () => [string, string] | null): Rule[] {
|
||||
return [
|
||||
{
|
||||
validator: async () => {
|
||||
const range = getDisplayTimeRange();
|
||||
if (!range?.[1]) {
|
||||
return Promise.reject(new Error('请选择结束时间'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
import { Modal, Descriptions, Image, Radio, Input, Form } from 'ant-design-vue';
|
||||
import { Modal, Descriptions, Image, Radio, Input, Form, message } from 'ant-design-vue';
|
||||
import { validateEventAuditSubmit } from '../model/form';
|
||||
import styles from './EventAuditModal.module.less';
|
||||
|
||||
const { TextArea } = Input;
|
||||
@@ -58,11 +59,13 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const handleOk = () => {
|
||||
if (!auditForm.comment.trim()) {
|
||||
// 不通过时强制要求填写审核内容
|
||||
if (!auditForm.approved) {
|
||||
// 这里交给校验,简化版本不做强制校验
|
||||
}
|
||||
const check = validateEventAuditSubmit({
|
||||
approved: auditForm.approved,
|
||||
comment: auditForm.comment,
|
||||
});
|
||||
if (!check.valid) {
|
||||
message.warning(check.error!);
|
||||
return;
|
||||
}
|
||||
if (props.onSubmit) {
|
||||
props.onSubmit({ approved: auditForm.approved, comment: auditForm.comment });
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 赛事审核 - 表单域控制器
|
||||
*
|
||||
* 集中管理赛事审核弹窗的提交校验逻辑。
|
||||
*/
|
||||
|
||||
// ============================================================
|
||||
// 提交校验
|
||||
// ============================================================
|
||||
|
||||
export interface EventAuditForm {
|
||||
approved: boolean;
|
||||
comment: string;
|
||||
}
|
||||
|
||||
export interface EventAuditSubmitCheckResult {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验赛事审核提交数据
|
||||
*
|
||||
* @param form 审核表单 { approved, comment }
|
||||
*/
|
||||
export function validateEventAuditSubmit(form: EventAuditForm): EventAuditSubmitCheckResult {
|
||||
// 不通过时强制要求填写审核内容
|
||||
if (!form.approved && !form.comment.trim()) {
|
||||
return { valid: false, error: '审核不通过时请填写审核内容' };
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
@@ -50,24 +50,24 @@ export function useLogColumns() {
|
||||
width: 200,
|
||||
align: 'center' as const,
|
||||
},
|
||||
{
|
||||
title: '操作对象',
|
||||
dataIndex: 'obj',
|
||||
key: 'obj',
|
||||
width: 240,
|
||||
align: 'center' as const,
|
||||
},
|
||||
// {
|
||||
// title: '操作对象',
|
||||
// dataIndex: 'obj',
|
||||
// key: 'obj',
|
||||
// width: 240,
|
||||
// align: 'center' as const,
|
||||
// },
|
||||
{
|
||||
title: '操作内容',
|
||||
dataIndex: 'content',
|
||||
key: 'content',
|
||||
dataIndex: 'obj',
|
||||
key: 'obj',
|
||||
width: 360,
|
||||
align: 'center' as const,
|
||||
},
|
||||
{
|
||||
title: '操作时间',
|
||||
dataIndex: 'createDate',
|
||||
key: 'createDate',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime',
|
||||
width: 170,
|
||||
align: 'center' as const,
|
||||
},
|
||||
|
||||
@@ -6,11 +6,15 @@ import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { uploadFile, OssUploadType } from '@/utils/oss';
|
||||
import { getWithdrawInfo, postWithdrawAudit, type WithdrawInfoVO } from '../model/services';
|
||||
import { getAuditStatusText, getPayStatusText } from '../model/useWithdrawModel';
|
||||
import {
|
||||
MAX_IMAGE_COUNT,
|
||||
MAX_IMAGE_SIZE,
|
||||
validateImageFile,
|
||||
validateWithdrawAuditSubmit,
|
||||
} from '../model/form';
|
||||
import styles from './WithdrawDetailModal.module.less';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const MAX_IMAGE_COUNT = 3;
|
||||
const MAX_IMAGE_SIZE = 5 * 1024 * 1024;
|
||||
|
||||
interface WithdrawDetailModalProps {
|
||||
visible: boolean;
|
||||
@@ -67,16 +71,9 @@ export default defineComponent({
|
||||
const beforeUpload = async (file: UploadFile) => {
|
||||
const rawFile = file as unknown as File;
|
||||
|
||||
if (imageList.value.length >= MAX_IMAGE_COUNT) {
|
||||
message.warning(`最多上传 ${MAX_IMAGE_COUNT} 张图片`);
|
||||
return false;
|
||||
}
|
||||
if (!/^image\//.test(rawFile.type)) {
|
||||
message.error('请选择图片文件');
|
||||
return false;
|
||||
}
|
||||
if (rawFile.size >= MAX_IMAGE_SIZE) {
|
||||
message.error('图片大小不能超过 5MB');
|
||||
const check = validateImageFile(rawFile, imageList.value.length);
|
||||
if (!check.valid) {
|
||||
message.error(check.error!);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,21 +111,16 @@ export default defineComponent({
|
||||
|
||||
// ===== 提交审核 =====
|
||||
const handleSubmit = async () => {
|
||||
// 驳回:审核内容必填
|
||||
if (!auditForm.pass && !auditForm.remark.trim()) {
|
||||
message.warning('驳回时请填写审核内容');
|
||||
return;
|
||||
}
|
||||
// 通过:必须上传图片
|
||||
if (auditForm.pass) {
|
||||
const hasDoneImage = imageList.value.some((item) => item.status === 'done' && item.url);
|
||||
if (!hasDoneImage) {
|
||||
message.warning('审核通过时必须上传审核图片');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (imageList.value.some((item) => item.status === 'uploading')) {
|
||||
message.warning('图片仍在上传中,请稍候');
|
||||
// 提交前校验
|
||||
const hasDoneImage = imageList.value.some((item) => item.status === 'done' && item.url);
|
||||
const isUploading = imageList.value.some((item) => item.status === 'uploading');
|
||||
const check = validateWithdrawAuditSubmit(
|
||||
{ pass: auditForm.pass, remark: auditForm.remark },
|
||||
hasDoneImage,
|
||||
isUploading,
|
||||
);
|
||||
if (!check.valid) {
|
||||
message.warning(check.error!);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 提现审核 - 表单域控制器
|
||||
*
|
||||
* 集中管理提现审核弹窗的图片上传校验、提交校验逻辑与常量。
|
||||
*/
|
||||
import { isImageFile } from '@/utils/form';
|
||||
|
||||
// ============================================================
|
||||
// 常量
|
||||
// ============================================================
|
||||
|
||||
/** 审核图片最大上传张数 */
|
||||
export const MAX_IMAGE_COUNT = 3;
|
||||
/** 审核图片单张最大体积:5MB */
|
||||
export const MAX_IMAGE_SIZE = 5 * 1024 * 1024;
|
||||
|
||||
// ============================================================
|
||||
// 图片上传校验
|
||||
// ============================================================
|
||||
|
||||
export interface ImageUploadCheckResult {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验待上传的审核图片
|
||||
* @param file 待上传的文件对象
|
||||
* @param currentCount 当前已上传的图片数量
|
||||
* @returns 校验结果
|
||||
*/
|
||||
export function validateImageFile(file: File, currentCount: number): ImageUploadCheckResult {
|
||||
if (currentCount >= MAX_IMAGE_COUNT) {
|
||||
return { valid: false, error: `最多上传 ${MAX_IMAGE_COUNT} 张图片` };
|
||||
}
|
||||
if (!isImageFile(file)) {
|
||||
return { valid: false, error: '请选择图片文件' };
|
||||
}
|
||||
if (file.size >= MAX_IMAGE_SIZE) {
|
||||
return { valid: false, error: '图片大小不能超过 5MB' };
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 提交审核校验
|
||||
// ============================================================
|
||||
|
||||
export interface WithdrawAuditForm {
|
||||
pass: boolean;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface WithdrawAuditSubmitCheckResult {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验提现审核提交数据
|
||||
*
|
||||
* @param form 审核表单 { pass, remark }
|
||||
* @param hasDoneImage 是否有上传成功的图片
|
||||
* @param isUploading 是否有图片仍在上传中
|
||||
*/
|
||||
export function validateWithdrawAuditSubmit(
|
||||
form: WithdrawAuditForm,
|
||||
hasDoneImage: boolean,
|
||||
isUploading: boolean,
|
||||
): WithdrawAuditSubmitCheckResult {
|
||||
// 驳回:审核内容必填
|
||||
if (!form.pass && !form.remark.trim()) {
|
||||
return { valid: false, error: '驳回时请填写审核内容' };
|
||||
}
|
||||
// 通过:必须上传图片
|
||||
if (form.pass && !hasDoneImage) {
|
||||
return { valid: false, error: '审核通过时必须上传审核图片' };
|
||||
}
|
||||
// 图片上传未完成
|
||||
if (isUploading) {
|
||||
return { valid: false, error: '图片仍在上传中,请稍候' };
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { auth } from '@/hooks/useAuth';
|
||||
import { useEffect, useState } from '@/hooks';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { login as loginApi } from '@/api/login';
|
||||
import { loginFormRules } from './model/form';
|
||||
import styles from './index.module.less';
|
||||
|
||||
interface LoginForm {
|
||||
@@ -40,11 +41,6 @@ export default defineComponent({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const rules = {
|
||||
username: [{ required: true, message: '请输入用户名' }],
|
||||
password: [{ required: true, message: '请输入密码' }],
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!form.phone || !form.password) {
|
||||
message.warning('请输入用户名和密码');
|
||||
@@ -214,7 +210,7 @@ export default defineComponent({
|
||||
<Form
|
||||
class={styles.loginForm}
|
||||
model={form}
|
||||
rules={rules}
|
||||
rules={loginFormRules}
|
||||
layout="vertical"
|
||||
onFinish={handleSubmit}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 登录页 - 表单域控制器
|
||||
*
|
||||
* 集中管理登录表单的校验规则。
|
||||
*/
|
||||
import type { Rule } from 'ant-design-vue/es/form';
|
||||
|
||||
// ============================================================
|
||||
// 表单规则
|
||||
// ============================================================
|
||||
|
||||
/** 登录表单的整体规则(form-level rules,key 对应 FormItem 的 name) */
|
||||
export const loginFormRules: Record<string, Rule[]> = {
|
||||
phone: [{ required: true, message: '请输入用户名' }],
|
||||
password: [{ required: true, message: '请输入密码' }],
|
||||
};
|
||||
@@ -27,6 +27,12 @@ export const LOGIN_PASSWORD_REGEX = /^(?=.*[A-Za-z])(?=.*\d)[\w!@#$%^&*()_+\-=]{
|
||||
*/
|
||||
export const REAL_NAME_REGEX = /^[\u4e00-\u9fa5A-Za-z0-9·]{1,8}$/;
|
||||
|
||||
/**
|
||||
* 图片 MIME 类型前缀
|
||||
* - 用于上传前校验文件是否为图片
|
||||
*/
|
||||
export const IMAGE_TYPE_REGEX = /^image\//;
|
||||
|
||||
// ============================================================
|
||||
// 校验函数
|
||||
// ============================================================
|
||||
@@ -45,3 +51,8 @@ export function isValidPassword(value: unknown): boolean {
|
||||
export function isValidRealName(value: unknown): boolean {
|
||||
return typeof value === 'string' && value.length > 0 && REAL_NAME_REGEX.test(value);
|
||||
}
|
||||
|
||||
/** 校验文件是否为图片类型 */
|
||||
export function isImageFile(file: { type: string }): boolean {
|
||||
return IMAGE_TYPE_REGEX.test(file.type);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user
this