333 lines
10 KiB
TypeScript
333 lines
10 KiB
TypeScript
import { computed, ref, reactive, shallowRef, onBeforeUnmount, watchEffect, nextTick } from 'vue';
|
|
import type { FormListProps } from '@/components/Form/types';
|
|
import type { Ref } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import { useAccount } from './useAccount';
|
|
import { Message } from 'vue-devui/message';
|
|
import { emitEvent } from '@/utils/eventBus';
|
|
|
|
export function useFormInteraction(currentForm: Ref<FormListProps[]>, flag: boolean = false, extraStatus: Ref<boolean> = ref(true)) {
|
|
const router = useRouter();
|
|
const { RecordInfo } = useAccount();
|
|
let interval: any = null;
|
|
const cacheForm = reactive<{ mobile: string, verificationcode: string, countdownSecond: number, user_id: string, mask: string }>({
|
|
mobile: '',
|
|
verificationcode: '',
|
|
countdownSecond: 59,
|
|
user_id: '',
|
|
mask: ''
|
|
});
|
|
// 表单错误信息
|
|
const formErrors = reactive<Record<string, string>>({});
|
|
// 额外验证参数
|
|
const extraErrors = reactive<{ agreement: string, requestInfo: string }>({
|
|
agreement: '',
|
|
requestInfo: ''
|
|
});
|
|
// 协议是否抖动
|
|
const AgreementWarn = ref(false);
|
|
// 禁用提交
|
|
const disabled = ref(true);
|
|
// 协议状态
|
|
const status = ref(flag);
|
|
// 表单ref
|
|
const FormRef = shallowRef<any>(null);
|
|
// 按钮loading
|
|
const loading = ref(false);
|
|
|
|
/** 密码框文字间距样式 */
|
|
const PasswordInputSpacing = computed(() => {
|
|
const userAgent = navigator.userAgent || navigator.vendor || (window as any).opera;
|
|
const isIos = /iPad|iPhone|iPod/.test(userAgent) && !(window as any).MSStream;
|
|
return isIos ? '0px' : '-1px';
|
|
});
|
|
|
|
/** 当前表单错误信息 */
|
|
const errorMsg = computed(() => {
|
|
const keys = Object.keys(formErrors);
|
|
if (currentForm.value?.length) {
|
|
const obj = currentForm.value.find(item => keys.includes(item.key) && formErrors[item.key]);
|
|
if (FormRef.value) {
|
|
const filterKeys = keys.filter(key => key !== obj?.key && formErrors[key]);
|
|
filterKeys.length && FormRef.value?.ClearFormFields(filterKeys);
|
|
hackMsgError(obj?.key === 'verificationcode' || obj?.key === 'code');
|
|
}
|
|
if (obj) {
|
|
return formErrors[obj.key];
|
|
}
|
|
return extraErrors.agreement || extraErrors.requestInfo || '';
|
|
} else {
|
|
return extraErrors.agreement || extraErrors.requestInfo || '';
|
|
}
|
|
});
|
|
|
|
/** 设置表单单个字段校验信息 */
|
|
const setFormErrorKey = (conf: { errors: Record<string, any[]> } | Record<string, any>) => {
|
|
let message: string = '';
|
|
let key: string = '';
|
|
if (Object.prototype.hasOwnProperty.call(conf, 'errors')) {
|
|
const { errors } = conf;
|
|
key = Object.keys(errors)[0];
|
|
const formDict = errors[key];
|
|
message = formDict?.length ? formDict[0].message : '';
|
|
} else {
|
|
key = Object.keys(conf)[0];
|
|
message = (conf as Record<string, any>)[key];
|
|
}
|
|
formErrors[key] = message;
|
|
};
|
|
|
|
/** 捕获整个表单校验信息 */
|
|
const catchFormErrors = (conf: { errors: Record<string, any[]> }) => {
|
|
const { errors } = conf;
|
|
for (const key in errors) {
|
|
const obj: Record<string, string> = {};
|
|
obj[key] = errors[key][0].message as string;
|
|
setFormErrorKey(obj);
|
|
}
|
|
};
|
|
/** 清空表单校验信息 */
|
|
const clearFormError = (key?: string) => {
|
|
if (key) {
|
|
formErrors[key] = '';
|
|
} else {
|
|
for (const key in formErrors) {
|
|
formErrors[key] = '';
|
|
}
|
|
for (const key in extraErrors) {
|
|
extraErrors[key as keyof typeof extraErrors] = '';
|
|
}
|
|
}
|
|
};
|
|
/** 表单字段change事件 */
|
|
const handleFormChange = (conf: { key: string, errors: Record<string, any> | null, source: Record<string, any> }) => {
|
|
const { key, errors } = conf;
|
|
if (errors) {
|
|
setFormErrorKey({ errors });
|
|
} else {
|
|
formErrors[key] = '';
|
|
}
|
|
nextTick(() => {
|
|
const keys = Object.keys(formErrors);
|
|
if (currentForm.value?.length) {
|
|
const obj = currentForm.value.find(item => keys.includes(item.key) && formErrors[item.key]);
|
|
if (FormRef.value) {
|
|
const filterKeys = keys.filter(k => k !== obj?.key && formErrors[k]);
|
|
if (filterKeys.length) {
|
|
FormRef.value?.ClearFormFields(filterKeys);
|
|
} else {
|
|
obj && !errors && FormRef.value?.ValidateFormKeys([obj.key]);
|
|
}
|
|
}
|
|
hackMsgError(obj?.key === 'verificationcode' || obj?.key === 'code');
|
|
}
|
|
});
|
|
};
|
|
/** 表单input事件 */
|
|
const handleFormInput = (val: boolean) => {
|
|
disabled.value = val;
|
|
};
|
|
/** 第三方登录 */
|
|
const handleAuthLogin = (type: 'csdn' | 'gitee' | 'github' | 'mobile', callback?: Function, returnUrl? :string) => {
|
|
if (localStorage.getItem('access_token')) {
|
|
callback?.();
|
|
} else {
|
|
if (type === 'mobile') {
|
|
if (returnUrl) {
|
|
router.push('/loginByPhone?returnUrl=' + returnUrl);
|
|
} else {
|
|
router.push('/loginByPhone');
|
|
}
|
|
} else {
|
|
if (!status.value) {
|
|
extraErrors.agreement = `请阅读并同意用户协议以及其隐私政策`;
|
|
AgreementWarn.value = true;
|
|
setTimeout(() => {
|
|
AgreementWarn.value = false;
|
|
}, 500);
|
|
return;
|
|
}
|
|
if (localStorage.getItem('utm_source')) {
|
|
localStorage.removeItem('utm_source');
|
|
}
|
|
// 保存登录前页面地址
|
|
localStorage.setItem('loginReturnUrl', returnUrl || '');
|
|
// window.location.href = 'http://localhost:5173/oauth/callback';
|
|
const url = `${(import.meta as any).env.VITE_API_HOST}${(import.meta as any).env.VITE_PASSPORT_PREFIX || ''}/api/v1/oauth/login/${type}`;
|
|
window.location.href = url;
|
|
}
|
|
}
|
|
};
|
|
/** 提交表单 */
|
|
const handleSubmit = async(callback: (config: Record<string, any>) => Promise<void>, extraInfo: string = '') => {
|
|
if (!status.value) {
|
|
extraErrors.agreement = extraInfo || '请阅读并同意用户协议以及隐私政策';
|
|
AgreementWarn.value = true;
|
|
setTimeout(() => {
|
|
AgreementWarn.value = false;
|
|
}, 500);
|
|
return;
|
|
}
|
|
extraErrors.agreement = '';
|
|
if (FormRef.value) {
|
|
const formData = await FormRef.value.ValidateForm();
|
|
if (formData.type === 'success') {
|
|
loading.value = true;
|
|
clearFormError();
|
|
await callback(formData.forms);
|
|
setTimeout(() => {
|
|
loading.value = false;
|
|
}, 500);
|
|
} else {
|
|
catchFormErrors(formData);
|
|
}
|
|
}
|
|
};
|
|
/** 倒计时 */
|
|
const handleCountDown = async(conf: { key: string, value: any, sourceKey: string }, callback: () => Promise<boolean>) => {
|
|
const formData = await FormRef.value.ValidateFormKeys([conf.key]);
|
|
if (formData.type === 'success') {
|
|
clearFormError(conf.key);
|
|
const status = await callback();
|
|
status && resetMsgStatus(conf.sourceKey);
|
|
} else {
|
|
setFormErrorKey(formData);
|
|
}
|
|
};
|
|
/** gitcode 外链url */
|
|
const links = {
|
|
'agreement': 'https://gitcode.com/Gitcode-offical-team/GitCode-Docs/blob/main/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE%2F%E6%9C%8D%E5%8A%A1%E6%9D%A1%E6%AC%BE.md',
|
|
'privacy': 'https://gitcode.com/Gitcode-offical-team/GitCode-Docs/blob/main/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE%2F%E9%9A%90%E7%A7%81%E6%94%BF%E7%AD%96.md'
|
|
};
|
|
/** 华为云 外链url */
|
|
const links_hw = {
|
|
agreement: 'https://www.huaweicloud.com/declaration/sa_cua.html',
|
|
privacy: 'https://www.huaweicloud.com/declaration/sa_prp.html'
|
|
};
|
|
/** 展示声明 */
|
|
const handleDisplay = (type: 'agreement' | 'privacy', class_type: 'gitcode' | 'hw' = 'gitcode') => {
|
|
const link = class_type === 'gitcode' ? links[type] : links_hw[type];
|
|
window.open(link);
|
|
};
|
|
|
|
/** 重置短信按钮状态 */
|
|
const resetMsgStatus = (key: string, status: boolean = true) => {
|
|
if (currentForm.value?.length) {
|
|
currentForm.value.forEach(item => {
|
|
if (item.key === key) {
|
|
if (item.props) {
|
|
item.props.countdown = status;
|
|
}
|
|
}
|
|
});
|
|
status && toCountDown(key);
|
|
}
|
|
};
|
|
|
|
/** 缓存倒计时间 */
|
|
const toCountDown = (key: string) => {
|
|
if (!interval) {
|
|
if (cacheForm.countdownSecond === 0) {
|
|
cacheForm.countdownSecond = 59;
|
|
}
|
|
interval = setInterval(() => {
|
|
if (cacheForm.countdownSecond >= 1) {
|
|
cacheForm.countdownSecond -= 1;
|
|
} else {
|
|
if (localStorage.getItem('cache_countdown')) {
|
|
localStorage.removeItem('cache_countdown');
|
|
}
|
|
cacheForm.countdownSecond = 59;
|
|
interval && clearInterval(interval);
|
|
interval = null;
|
|
resetMsgStatus(key, false);
|
|
}
|
|
}, 1000);
|
|
}
|
|
};
|
|
/** 存贮用户信息以及跳转 */
|
|
const saveUserInfo = (conf: any, returnUrl?: any) => {
|
|
const { username, email } = conf;
|
|
RecordInfo(conf);
|
|
Message.success({
|
|
message: '欢迎来到Gitcode!',
|
|
onClose: () => {
|
|
emitEvent('notice');
|
|
}
|
|
});
|
|
if (`${username}@gitcode.com` === email) { // 需要修改默认邮箱的弹窗
|
|
localStorage.setItem('validator_email', 'invalid');
|
|
} else {
|
|
localStorage.setItem('validator_email', 'valid');
|
|
}
|
|
if (returnUrl) {
|
|
location.href = returnUrl;
|
|
} else {
|
|
router.replace('/');
|
|
}
|
|
};
|
|
/**
|
|
* redirect绑定华为云
|
|
* @deprecated
|
|
*/
|
|
const redirectBind = (config: { user_id: string, mask: string, mobile: string }) => {
|
|
for (const key in config) {
|
|
cacheForm[key as keyof typeof config] = config[key as keyof typeof config];
|
|
}
|
|
// router.replace({
|
|
// path: '/bindPhone',
|
|
// query: config
|
|
// });
|
|
};
|
|
|
|
/** hack短信弹窗错误样式 */
|
|
const hackMsgError = (show_error: boolean) => {
|
|
const el = document.querySelector('.g-input-button');
|
|
if (el) {
|
|
if (show_error) {
|
|
(el as HTMLElement).style.borderColor = 'var(--devui-danger-line, #f66f6a)';
|
|
} else {
|
|
(el as HTMLElement).style.borderColor = 'rgba(230, 230, 232, 1)';
|
|
}
|
|
}
|
|
};
|
|
|
|
watchEffect(() => {
|
|
if (status.value && extraStatus.value && extraErrors.agreement) {
|
|
extraErrors.agreement = '';
|
|
}
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
if (interval) {
|
|
clearInterval(interval);
|
|
}
|
|
hackMsgError(false);
|
|
});
|
|
|
|
return {
|
|
AgreementWarn,
|
|
status,
|
|
disabled,
|
|
FormRef,
|
|
cacheForm,
|
|
formErrors,
|
|
PasswordInputSpacing,
|
|
extraErrors,
|
|
errorMsg,
|
|
loading,
|
|
setFormErrorKey,
|
|
catchFormErrors,
|
|
clearFormError,
|
|
toCountDown,
|
|
handleFormChange,
|
|
handleFormInput,
|
|
handleAuthLogin,
|
|
handleCountDown,
|
|
handleDisplay,
|
|
handleSubmit,
|
|
saveUserInfo
|
|
// redirectBind
|
|
};
|
|
}
|