init push

This commit is contained in:
2026-05-21 19:52:45 +08:00
commit e3f75311ab
1280 changed files with 179173 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
export { useBindCaptcha } from './useBindCaptcha';
export { useCommitPendingInput } from './useCommitPendingInput';
export { useURLSearchParams } from './useURLSearchParams';
export {
useFeatureValue,
useFeatureValueSupported,
useVersionInfo,
} from './useVersionFeature';

View File

@@ -0,0 +1,67 @@
import { message } from '@ctzhian/ui';
import { useEffect, useRef, useState } from 'react';
export function useBindCaptcha(
id: string,
{
init = false,
businessId = '0195ea3c-ab47-73f3-9f8e-e72b8fd7f089',
}: { init: boolean; businessId?: string },
) {
const captcha = useRef<any>({});
const resolveRef = useRef<any>(null);
const [load, setLoad] = useState(false);
const [token, setToken] = useState<string>();
const initCaptcha = () => {
captcha.current = new (window as any).SCaptcha({
businessid: businessId,
action: 'pow',
position: 'mask',
});
captcha.current!.bind(
('#' + id).replace(/:/g, '\\:'),
(action: any, data: any) => {
if (action === 'finished') {
captcha.current.reset();
if (data) {
setToken(data);
resolveRef.current(data);
} else {
message.error('验证失败');
}
}
},
);
const oldStart = captcha.current.start.bind(captcha.current);
captcha.current.start = (e: any) => {
oldStart(e);
return new Promise(resolve => {
resolveRef.current = resolve;
});
};
};
const loadCaptcha = () => {
const script = document.createElement('script');
script.src =
'https://0195ea3c-ab47-73f3-9f8e-e72b8fd7f089.safepoint.s-captcha-r1.com/v1/static/web.js';
document.body.appendChild(script);
script.onload = () => {
setLoad(true);
};
};
useEffect(() => {
if (init) {
if (!load) {
loadCaptcha();
} else {
initCaptcha();
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [init, load]);
return [captcha, token] as [any, string];
}

View File

@@ -0,0 +1,37 @@
import { useRef, useState } from 'react';
export function useCommitPendingInput<T>({
value,
setValue,
}: {
value: T[];
setValue: (v: T[]) => void;
}) {
const [inputValue, setInputValue] = useState('');
// 用于同步获取最新值(解决闭包问题)
const valueRef = useRef(value);
valueRef.current = value;
// 提交未完成的输入
const commit = () => {
const trimmed = inputValue.trim();
if (trimmed) {
const newValue = [...valueRef.current, trimmed as T];
setValue(newValue);
setInputValue('');
}
};
return {
/** 已提交的值 */
value,
/** 设置已提交的值(用于外部修改) */
setValue,
/** 当前输入框中的临时值 */
inputValue,
/** 设置临时值 */
setInputValue,
/** 提交未完成的输入 */
commit,
};
}

View File

@@ -0,0 +1,26 @@
import { useAppDispatch } from '@/store';
import { setAppPreviewData } from '@/store/slices/config';
import { debounce } from 'lodash-es';
import { useEffect, useMemo } from 'react';
const useDebounceAppPreviewData = () => {
const dispatch = useAppDispatch();
const debouncedDispatch = useMemo(
() =>
debounce((data: any) => {
dispatch(setAppPreviewData(data));
}, 500),
[dispatch],
);
useEffect(() => {
return () => {
debouncedDispatch.cancel();
};
}, [debouncedDispatch]);
return debouncedDispatch;
};
export default useDebounceAppPreviewData;

View File

@@ -0,0 +1,28 @@
import { filterEmpty } from '@/utils';
import { useEffect, useState } from 'react';
import { useLocation, useSearchParams } from 'react-router-dom';
export const useURLSearchParams = (): [
URLSearchParams,
(other: Record<string, string> | null) => void,
] => {
const { search } = useLocation();
const [searchParams, setSearchParams] = useSearchParams();
const [params, setParams] = useState<Record<string, string>>({});
const setURLSearchParams = (other: Record<string, string> | null) => {
if (other === null) setSearchParams({});
else setSearchParams(filterEmpty({ ...params, ...other }));
};
useEffect(() => {
const obj: Record<string, string> = {};
searchParams.forEach((value, key) => {
obj[key] = value;
});
setParams(obj);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [search]);
return [searchParams, setURLSearchParams];
};

View File

@@ -0,0 +1,34 @@
import {
FeatureStatus,
VersionInfoMap,
VersionInfo,
getFeatureValue,
} from '@/constant/version';
import { ConstsLicenseEdition } from '@/request/types';
import { useAppSelector } from '@/store';
export const useFeatureValue = <K extends keyof VersionInfo['features']>(
key: K,
): VersionInfo['features'][K] => {
const { license } = useAppSelector(state => state.config);
return getFeatureValue(license.edition!, key);
};
export const useFeatureValueSupported = (
key: keyof VersionInfo['features'],
) => {
const { license } = useAppSelector(state => state.config);
return (
getFeatureValue(license.edition!, key) === FeatureStatus.SUPPORTED ||
getFeatureValue(license.edition!, key) === FeatureStatus.ADVANCED
);
};
export const useVersionInfo = () => {
const { license } = useAppSelector(state => state.config);
return (
VersionInfoMap[
license.edition ?? ConstsLicenseEdition.LicenseEditionFree
] || VersionInfoMap[ConstsLicenseEdition.LicenseEditionFree]
);
};