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, flag: boolean = false, extraStatus: Ref = 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>({}); // 额外验证参数 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(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 } | Record) => { 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)[key]; } formErrors[key] = message; }; /** 捕获整个表单校验信息 */ const catchFormErrors = (conf: { errors: Record }) => { const { errors } = conf; for (const key in errors) { const obj: Record = {}; 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 | null, source: Record }) => { 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) => Promise, 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) => { 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 }; }