mirror of
https://github.com/usememos/memos.git
synced 2024-12-21 10:11:42 +03:00
feat: change main language to english
This commit is contained in:
parent
321183db52
commit
226e9c156a
@ -13,7 +13,7 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
<>
|
||||
<div className="dialog-header-container">
|
||||
<p className="title-text">
|
||||
<span className="icon-text">🤠</span>关于 <b>Memos</b>
|
||||
<span className="icon-text">🤠</span>About <b>Memos</b>
|
||||
</p>
|
||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
||||
<img className="icon-img" src="/icons/close.svg" />
|
||||
@ -21,9 +21,9 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
</div>
|
||||
<div className="dialog-content-container">
|
||||
<p>
|
||||
把玩 <a href="https://flomoapp.com">flomo</a> 后有感而作的开源项目
|
||||
Memos is an open source, self-hosted alternative to <a href="https://flomoapp.com">flomo</a>.
|
||||
</p>
|
||||
<p>特点:精美且细节的视觉样式、体验优良的交互逻辑</p>
|
||||
<p>Built with `Golang` and `React`.</p>
|
||||
<br />
|
||||
<p>
|
||||
🏗 This project is working in progress, <br /> and very pleasure to welcome your{" "}
|
||||
|
@ -44,19 +44,19 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
|
||||
const handleSaveBtnClick = async () => {
|
||||
if (oldPassword === "" || newPassword === "" || newPasswordAgain === "") {
|
||||
toastHelper.error("密码不能为空");
|
||||
toastHelper.error("Please fill in all fields.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword !== newPasswordAgain) {
|
||||
toastHelper.error("新密码两次输入不一致");
|
||||
toastHelper.error("New passwords do not match.");
|
||||
setNewPasswordAgain("");
|
||||
return;
|
||||
}
|
||||
|
||||
const passwordValidResult = validate(newPassword, validateConfig);
|
||||
if (!passwordValidResult.result) {
|
||||
toastHelper.error("密码 " + passwordValidResult.reason);
|
||||
toastHelper.error("Password " + passwordValidResult.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -64,13 +64,13 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
const isValid = await userService.checkPasswordValid(oldPassword);
|
||||
|
||||
if (!isValid) {
|
||||
toastHelper.error("旧密码不匹配");
|
||||
toastHelper.error("Old password is invalid.");
|
||||
setOldPassword("");
|
||||
return;
|
||||
}
|
||||
|
||||
await userService.updatePassword(newPassword);
|
||||
toastHelper.info("密码修改成功!");
|
||||
toastHelper.info("Password changed.");
|
||||
handleCloseBtnClick();
|
||||
} catch (error: any) {
|
||||
toastHelper.error(error);
|
||||
@ -80,30 +80,30 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<div className="dialog-header-container">
|
||||
<p className="title-text">修改密码</p>
|
||||
<p className="title-text">Change Password</p>
|
||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
||||
<img className="icon-img" src="/icons/close.svg" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="dialog-content-container">
|
||||
<label className="form-label input-form-label">
|
||||
<span className={"normal-text " + (oldPassword === "" ? "" : "not-null")}>旧密码</span>
|
||||
<span className={"normal-text " + (oldPassword === "" ? "" : "not-null")}>Old password</span>
|
||||
<input type="password" value={oldPassword} onChange={handleOldPasswordChanged} />
|
||||
</label>
|
||||
<label className="form-label input-form-label">
|
||||
<span className={"normal-text " + (newPassword === "" ? "" : "not-null")}>新密码</span>
|
||||
<span className={"normal-text " + (newPassword === "" ? "" : "not-null")}>New passworld</span>
|
||||
<input type="password" value={newPassword} onChange={handleNewPasswordChanged} />
|
||||
</label>
|
||||
<label className="form-label input-form-label">
|
||||
<span className={"normal-text " + (newPasswordAgain === "" ? "" : "not-null")}>再次输入新密码</span>
|
||||
<span className={"normal-text " + (newPasswordAgain === "" ? "" : "not-null")}>New password again</span>
|
||||
<input type="password" value={newPasswordAgain} onChange={handleNewPasswordAgainChanged} />
|
||||
</label>
|
||||
<div className="btns-container">
|
||||
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
|
||||
取消
|
||||
Cancel
|
||||
</span>
|
||||
<span className="btn confirm-btn" onClick={handleSaveBtnClick}>
|
||||
保存
|
||||
Save
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,29 +27,31 @@ const ConfirmResetOpenIdDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
try {
|
||||
await userService.resetOpenId();
|
||||
} catch (error) {
|
||||
toastHelper.error("请求重置 Open API 失败");
|
||||
toastHelper.error("Request reset open API failed.");
|
||||
return;
|
||||
}
|
||||
toastHelper.success("重置成功!");
|
||||
toastHelper.success("Reset open API succeeded.");
|
||||
handleCloseBtnClick();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="dialog-header-container">
|
||||
<p className="title-text">重置 Open API</p>
|
||||
<p className="title-text">Reset Open API</p>
|
||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
||||
<img className="icon-img" src="/icons/close.svg" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="dialog-content-container">
|
||||
<p className="warn-text">⚠️ 现有 API 将失效,并生成新的 API,确定要重置吗?</p>
|
||||
<p className="warn-text">
|
||||
⚠️ The existing API will be invalidated and a new one will be generated, are you sure you want to reset?
|
||||
</p>
|
||||
<div className="btns-container">
|
||||
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
|
||||
取消
|
||||
Cancel
|
||||
</span>
|
||||
<span className={`btn confirm-btn ${resetBtnClickLoadingState.isLoading ? "loading" : ""}`} onClick={handleConfirmBtnClick}>
|
||||
确定重置!
|
||||
Reset!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -40,7 +40,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
|
||||
const handleSaveBtnClick = async () => {
|
||||
if (!title) {
|
||||
toastHelper.error("标题不能为空!");
|
||||
toastHelper.error("Title is required");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
if (filters.length > 0) {
|
||||
const lastFilter = filters[filters.length - 1];
|
||||
if (lastFilter.value.value === "") {
|
||||
toastHelper.info("先完善上一个过滤器吧");
|
||||
toastHelper.info("Please fill in previous filter value");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -90,7 +90,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
<div className="dialog-header-container">
|
||||
<p className="title-text">
|
||||
<span className="icon-text">🔖</span>
|
||||
{shortcutId ? "编辑检索" : "创建检索"}
|
||||
{shortcutId ? "Edit Shortcut" : "Create Shortcut"}
|
||||
</p>
|
||||
<button className="btn close-btn" onClick={destroy}>
|
||||
<img className="icon-img" src="/icons/close.svg" />
|
||||
@ -98,11 +98,11 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
</div>
|
||||
<div className="dialog-content-container">
|
||||
<div className="form-item-container input-form-container">
|
||||
<span className="normal-text">标题</span>
|
||||
<span className="normal-text">Title</span>
|
||||
<input className="title-input" type="text" value={title} onChange={handleTitleInputChange} />
|
||||
</div>
|
||||
<div className="form-item-container filter-form-container">
|
||||
<span className="normal-text">过滤器</span>
|
||||
<span className="normal-text">Filter</span>
|
||||
<div className="filters-wrapper">
|
||||
{filters.map((f, index) => {
|
||||
return (
|
||||
@ -116,7 +116,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
);
|
||||
})}
|
||||
<div className="create-filter-btn" onClick={handleAddFilterBenClick}>
|
||||
添加筛选条件
|
||||
New Filter
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -125,10 +125,10 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
<div></div>
|
||||
<div className="btns-container">
|
||||
<span className={`tip-text ${filters.length === 0 && "hidden"}`}>
|
||||
符合条件的 Memo 有 <strong>{shownMemoLength}</strong> 条
|
||||
<strong>{shownMemoLength}</strong> eligible memo
|
||||
</span>
|
||||
<button className={`btn save-btn ${requestState.isLoading ? "requesting" : ""}`} onClick={handleSaveBtnClick}>
|
||||
保存
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -106,11 +106,11 @@ const DailyMemoDiaryDialog: React.FC<Props> = (props: Props) => {
|
||||
/>
|
||||
{loadingState.isLoading ? (
|
||||
<div className="tip-container">
|
||||
<p className="tip-text">努力加载中...</p>
|
||||
<p className="tip-text">Loading...</p>
|
||||
</div>
|
||||
) : memos.length === 0 ? (
|
||||
<div className="tip-container">
|
||||
<p className="tip-text">空空如也</p>
|
||||
<p className="tip-text">Oops, there is nothing.</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="dailymemos-wrapper">
|
||||
|
@ -40,7 +40,7 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
|
||||
try {
|
||||
await memoService.restoreMemoById(memo.id);
|
||||
handleDeletedMemoAction(memo.id);
|
||||
toastHelper.info("恢复成功");
|
||||
toastHelper.info("Restored successfully");
|
||||
} catch (error: any) {
|
||||
toastHelper.error(error.message);
|
||||
}
|
||||
@ -55,7 +55,7 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
|
||||
return (
|
||||
<div className={`memo-wrapper ${"memos-" + memo.id}`} onMouseLeave={handleMouseLeaveMemoWrapper}>
|
||||
<div className="memo-top-wrapper">
|
||||
<span className="time-text">删除于 {memo.deletedAtStr}</span>
|
||||
<span className="time-text">Deleted at {memo.deletedAtStr}</span>
|
||||
<div className="btns-container">
|
||||
<span className="btn more-action-btn">
|
||||
<img className="icon-img" src="/icons/more.svg" />
|
||||
@ -63,10 +63,10 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
|
||||
<div className="more-action-btns-wrapper">
|
||||
<div className="more-action-btns-container">
|
||||
<span className="btn restore-btn" onClick={handleRestoreMemoClick}>
|
||||
恢复
|
||||
Restore
|
||||
</span>
|
||||
<span className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`} onClick={handleDeleteMemoClick}>
|
||||
{showConfirmDeleteBtn ? "确定删除!" : "完全删除"}
|
||||
{showConfirmDeleteBtn ? "Delete!" : "Delete"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -176,12 +176,12 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
||||
<div className="btns-container">
|
||||
<Only when={showCancelBtn}>
|
||||
<button className="action-btn cancel-btn" onClick={handleCommonCancelBtnClick}>
|
||||
撤销修改
|
||||
Cancel editting
|
||||
</button>
|
||||
</Only>
|
||||
<Only when={showConfirmBtn}>
|
||||
<button className="action-btn confirm-btn" disabled={!editorRef.current?.value} onClick={handleCommonConfirmBtnClick}>
|
||||
记下<span className="icon-text">✍️</span>
|
||||
Save <span className="icon-text">✍️</span>
|
||||
</button>
|
||||
</Only>
|
||||
</div>
|
||||
|
@ -93,19 +93,19 @@ const Memo: React.FC<Props> = (props: Props) => {
|
||||
<div className="more-action-btns-wrapper">
|
||||
<div className="more-action-btns-container">
|
||||
<span className="btn" onClick={handleShowMemoStoryDialog}>
|
||||
查看详情
|
||||
View Story
|
||||
</span>
|
||||
<span className="btn" onClick={handleMarkMemoClick}>
|
||||
Mark
|
||||
</span>
|
||||
<span className="btn" onClick={handleGenMemoImageBtnClick}>
|
||||
分享
|
||||
Share
|
||||
</span>
|
||||
<span className="btn" onClick={handleEditMemoClick}>
|
||||
编辑
|
||||
Edit
|
||||
</span>
|
||||
<span className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`} onClick={handleDeleteMemoClick}>
|
||||
{showConfirmDeleteBtn ? "确定删除!" : "删除"}
|
||||
{showConfirmDeleteBtn ? "Delete!" : "Delete"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -151,7 +151,7 @@ export function formatMemoContent(content: string) {
|
||||
.replace(LINK_REG, "<a class='link' target='_blank' rel='noreferrer' href='$1'>$1</a>")
|
||||
.replace(MEMO_LINK_REG, "<span class='memo-link-text' data-value='$2'>$1</span>");
|
||||
|
||||
// 中英文之间加空格
|
||||
// Add space in english and chinese
|
||||
if (shouldSplitMemoWord) {
|
||||
content = content
|
||||
.replace(/([\u4e00-\u9fa5])([A-Za-z0-9?.,;[\]]+)/g, "$1 $2")
|
||||
|
@ -148,7 +148,7 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
||||
</div>
|
||||
{linkMemos.length > 0 ? (
|
||||
<div className="linked-memos-wrapper">
|
||||
<p className="normal-text">关联了 {linkMemos.length} 个 MEMO</p>
|
||||
<p className="normal-text">{linkMemos.length} related MEMO</p>
|
||||
{linkMemos.map((m) => {
|
||||
const rawtext = parseHtmlToRawText(formatMemoContent(m.content)).replaceAll("\n", " ");
|
||||
return (
|
||||
@ -162,7 +162,7 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
||||
) : null}
|
||||
{linkedMemos.length > 0 ? (
|
||||
<div className="linked-memos-wrapper">
|
||||
<p className="normal-text">{linkedMemos.length} 个链接至此的 MEMO</p>
|
||||
<p className="normal-text">{linkedMemos.length} linked MEMO</p>
|
||||
{linkedMemos.map((m) => {
|
||||
const rawtext = parseHtmlToRawText(formatMemoContent(m.content)).replaceAll("\n", " ");
|
||||
return (
|
||||
|
@ -140,7 +140,7 @@ const MemoEditor: React.FC<Props> = () => {
|
||||
|
||||
const handleSaveBtnClick = useCallback(async (content: string) => {
|
||||
if (content === "") {
|
||||
toastHelper.error("内容不能为空呀");
|
||||
toastHelper.error("Content can't be empty");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -270,7 +270,7 @@ const MemoEditor: React.FC<Props> = () => {
|
||||
() => ({
|
||||
className: "memo-editor",
|
||||
initialContent: getEditorContentCache(),
|
||||
placeholder: "现在的想法是...",
|
||||
placeholder: "Any thoughts...",
|
||||
showConfirmBtn: true,
|
||||
showCancelBtn: showEditStatus,
|
||||
onConfirmBtnClick: handleSaveBtnClick,
|
||||
@ -282,7 +282,7 @@ const MemoEditor: React.FC<Props> = () => {
|
||||
|
||||
return (
|
||||
<div className={"memo-editor-wrapper " + (showEditStatus ? "edit-ing" : "")}>
|
||||
<p className={"tip-text " + (showEditStatus ? "" : "hidden")}>正在修改中...</p>
|
||||
<p className={"tip-text " + (showEditStatus ? "" : "hidden")}>Editting...</p>
|
||||
<Editor
|
||||
ref={editorRef}
|
||||
{...editorConfig}
|
||||
|
@ -18,7 +18,7 @@ const MemoFilter: React.FC<FilterProps> = () => {
|
||||
|
||||
return (
|
||||
<div className={`filter-query-container ${showFilter ? "" : "hidden"}`}>
|
||||
<span className="tip-text">筛选:</span>
|
||||
<span className="tip-text">Filter:</span>
|
||||
<div
|
||||
className={"filter-item-container " + (queryFilter ? "" : "hidden")}
|
||||
onClick={() => {
|
||||
|
@ -83,7 +83,7 @@ const MemoList: React.FC<Props> = () => {
|
||||
setFetchStatus(false);
|
||||
})
|
||||
.catch(() => {
|
||||
toastHelper.error("😭 请求数据失败了");
|
||||
toastHelper.error("😭 Refresh failed, please try again later.");
|
||||
});
|
||||
}, []);
|
||||
|
||||
@ -111,7 +111,13 @@ const MemoList: React.FC<Props> = () => {
|
||||
))}
|
||||
<div className="status-text-container">
|
||||
<p className="status-text">
|
||||
{isFetching ? "努力请求数据中..." : shownMemos.length === 0 ? "空空如也" : showMemoFilter ? "" : "所有数据加载完啦 🎉"}
|
||||
{isFetching
|
||||
? "Fetching data..."
|
||||
: shownMemos.length === 0
|
||||
? "Oops, there is nothing"
|
||||
: showMemoFilter
|
||||
? ""
|
||||
: "Fetching completed 🎉"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -49,16 +49,16 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
|
||||
return (
|
||||
<div className={`menu-btns-popup ${shownStatus ? "" : "hidden"}`} ref={popupElRef}>
|
||||
<button className="btn action-btn" onClick={handleMyAccountBtnClick}>
|
||||
<span className="icon">👤</span> 账号与设置
|
||||
<span className="icon">👤</span> Settings
|
||||
</button>
|
||||
<button className="btn action-btn" onClick={handleMemosTrashBtnClick}>
|
||||
<span className="icon">🗑️</span> 回收站
|
||||
<span className="icon">🗑️</span> Recycle Bin
|
||||
</button>
|
||||
<button className="btn action-btn" onClick={handleAboutBtnClick}>
|
||||
<span className="icon">🤠</span> 关于
|
||||
<span className="icon">🤠</span> About
|
||||
</button>
|
||||
<button className="btn action-btn" onClick={handleSignOutBtnClick}>
|
||||
<span className="icon">👋</span> 退出
|
||||
<span className="icon">👋</span> Sign out
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
@ -30,7 +30,7 @@ const MyAccountSection: React.FC<Props> = () => {
|
||||
|
||||
const handleConfirmEditUsernameBtnClick = async () => {
|
||||
if (user.name === "guest") {
|
||||
toastHelper.info("🈲 不要修改我的用户名");
|
||||
toastHelper.info("Do not change my username");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ const MyAccountSection: React.FC<Props> = () => {
|
||||
|
||||
const usernameValidResult = validate(username, validateConfig);
|
||||
if (!usernameValidResult.result) {
|
||||
toastHelper.error("用户名 " + usernameValidResult.reason);
|
||||
toastHelper.error("Username " + usernameValidResult.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -48,13 +48,13 @@ const MyAccountSection: React.FC<Props> = () => {
|
||||
const isUsable = await userService.checkUsernameUsable(username);
|
||||
|
||||
if (!isUsable) {
|
||||
toastHelper.error("用户名无法使用");
|
||||
toastHelper.error("Username is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
await userService.updateUsername(username);
|
||||
await userService.doSignIn();
|
||||
toastHelper.info("修改成功~");
|
||||
toastHelper.info("Username changed");
|
||||
} catch (error: any) {
|
||||
toastHelper.error(error.message);
|
||||
}
|
||||
@ -62,7 +62,7 @@ const MyAccountSection: React.FC<Props> = () => {
|
||||
|
||||
const handleChangePasswordBtnClick = () => {
|
||||
if (user.name === "guest") {
|
||||
toastHelper.info("🈲 不要修改我的密码");
|
||||
toastHelper.info("Do not change my password");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -81,21 +81,21 @@ const MyAccountSection: React.FC<Props> = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="section-container account-section-container">
|
||||
<p className="title-text">基本信息</p>
|
||||
<p className="title-text">Account Information</p>
|
||||
<label className="form-label input-form-label">
|
||||
<span className="normal-text">ID:</span>
|
||||
<span className="normal-text">ID:</span>
|
||||
<span className="normal-text">{user.id}</span>
|
||||
</label>
|
||||
<label className="form-label input-form-label">
|
||||
<span className="normal-text">创建时间:</span>
|
||||
<span className="normal-text">Created at:</span>
|
||||
<span className="normal-text">{utils.getDateString(user.createdAt)}</span>
|
||||
</label>
|
||||
<label className="form-label input-form-label username-label">
|
||||
<span className="normal-text">账号:</span>
|
||||
<span className="normal-text">Username:</span>
|
||||
<input type="text" value={username} onChange={handleUsernameChanged} />
|
||||
<div className={`btns-container ${username === user.name ? "hidden" : ""}`} onClick={handlePreventDefault}>
|
||||
<span className="btn confirm-btn" onClick={handleConfirmEditUsernameBtnClick}>
|
||||
保存
|
||||
Save
|
||||
</span>
|
||||
<span
|
||||
className="btn cancel-btn"
|
||||
@ -103,25 +103,25 @@ const MyAccountSection: React.FC<Props> = () => {
|
||||
setUsername(user.name);
|
||||
}}
|
||||
>
|
||||
撤销
|
||||
Cancel
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<label className="form-label password-label">
|
||||
<span className="normal-text">密码:</span>
|
||||
<span className="normal-text">Password:</span>
|
||||
<span className="btn" onClick={handleChangePasswordBtnClick}>
|
||||
修改密码
|
||||
Change It
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="section-container openapi-section-container">
|
||||
<p className="title-text">Open API(实验性功能)</p>
|
||||
<p className="title-text">Open API (Experimental feature)</p>
|
||||
<p className="value-text">{openAPIRoute}</p>
|
||||
<span className="reset-btn" onClick={handleResetOpenIdBtnClick}>
|
||||
重置 API
|
||||
Reset API
|
||||
</span>
|
||||
<div className="usage-guide-container">
|
||||
<p className="title-text">使用方法:</p>
|
||||
<p className="title-text">Usage guide:</p>
|
||||
<pre>{`POST ${openAPIRoute}\nContent-type: application/json\n{\n "content": "Hello, #memos ${window.location.origin}"\n}`}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@ const PreferencesSection: React.FC<Props> = () => {
|
||||
const { globalState } = useContext(appContext);
|
||||
const { useTinyUndoHistoryCache, shouldHideImageUrl, shouldSplitMemoWord, shouldUseMarkdownParser } = globalState;
|
||||
|
||||
const demoMemoContent = "👋 你好呀~欢迎使用memos!\n* ✨ **开源项目**;\n* 😋 精美且细节的视觉样式;\n* 📑 体验优良的交互逻辑;";
|
||||
const demoMemoContent = "👋 Hiya, welcome to memos!\n* ✨ **Open source project**;\n* 😋 What do you think;\n* 📑 Tell me something plz;";
|
||||
|
||||
const handleOpenTinyUndoChanged = () => {
|
||||
globalStateService.setAppSetting({
|
||||
@ -65,29 +65,29 @@ const PreferencesSection: React.FC<Props> = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="section-container preferences-section-container">
|
||||
<p className="title-text">Memo 显示相关</p>
|
||||
<p className="title-text">Memo Display</p>
|
||||
<div
|
||||
className="demo-content-container memo-content-text"
|
||||
dangerouslySetInnerHTML={{ __html: formatMemoContent(demoMemoContent) }}
|
||||
></div>
|
||||
<label className="form-label checkbox-form-label" onClick={handleSplitWordsValueChanged}>
|
||||
<span className="normal-text">中英文内容自动间隔</span>
|
||||
<label className="form-label checkbox-form-label hidden" onClick={handleSplitWordsValueChanged}>
|
||||
<span className="normal-text">Auto-space in English and Chinese</span>
|
||||
<img className="icon-img" src={shouldSplitMemoWord ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
||||
</label>
|
||||
<label className="form-label checkbox-form-label" onClick={handleUseMarkdownParserChanged}>
|
||||
<span className="normal-text">部分 markdown 格式解析</span>
|
||||
<span className="normal-text">Partial markdown format parsing</span>
|
||||
<img className="icon-img" src={shouldUseMarkdownParser ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
||||
</label>
|
||||
<label className="form-label checkbox-form-label" onClick={handleHideImageUrlValueChanged}>
|
||||
<span className="normal-text">隐藏图片链接地址</span>
|
||||
<span className="normal-text">Hide image url</span>
|
||||
<img className="icon-img" src={shouldHideImageUrl ? "/icons/checkbox-active.svg" : "/icons/checkbox.svg"} />
|
||||
</label>
|
||||
</div>
|
||||
<div className="section-container preferences-section-container">
|
||||
<p className="title-text">编辑器</p>
|
||||
<p className="title-text">Editor Extensions</p>
|
||||
<label className="form-label checkbox-form-label" onClick={handleOpenTinyUndoChanged}>
|
||||
<span className="normal-text">
|
||||
启用{" "}
|
||||
Use{" "}
|
||||
<a target="_blank" href="https://github.com/boojack/tiny-undo" onClick={(e) => e.stopPropagation()} rel="noreferrer">
|
||||
tiny-undo
|
||||
</a>
|
||||
@ -96,13 +96,13 @@ const PreferencesSection: React.FC<Props> = () => {
|
||||
</label>
|
||||
</div>
|
||||
<div className="section-container">
|
||||
<p className="title-text">其他</p>
|
||||
<p className="title-text">Others</p>
|
||||
<div className="w-full flex flex-row justify-start items-center">
|
||||
<button className="px-2 py-1 border rounded text-base hover:opacity-80" onClick={handleExportBtnClick}>
|
||||
导出数据(JSON)
|
||||
Export data as JSON
|
||||
</button>
|
||||
<button className="btn format-btn hidden" onClick={handleFormatMemosBtnClick}>
|
||||
格式化数据
|
||||
Format Data
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -43,8 +43,8 @@ const PreviewImageDialog: React.FC<Props> = ({ destroy, imgUrl }: Props) => {
|
||||
|
||||
<div className="img-container">
|
||||
<img className={imgWidth <= 0 ? "hidden" : ""} ref={imgRef} width={imgWidth + "%"} src={imgUrl} />
|
||||
<span className={"loading-text " + (imgWidth === -1 ? "" : "hidden")}>图片加载中...</span>
|
||||
<span className={"loading-text " + (imgWidth === 0 ? "" : "hidden")}>😟 图片加载失败,可能是无效的链接</span>
|
||||
<span className={"loading-text " + (imgWidth === -1 ? "" : "hidden")}>Loading image...</span>
|
||||
<span className={"loading-text " + (imgWidth === 0 ? "" : "hidden")}>😟 Failed to load image</span>
|
||||
</div>
|
||||
|
||||
<div className="action-btns-container">
|
||||
|
@ -36,7 +36,7 @@ const SearchBar: React.FC<Props> = () => {
|
||||
<div className="quickly-action-container">
|
||||
<p className="title-text">QUICKLY FILTER</p>
|
||||
<div className="section-container types-container">
|
||||
<span className="section-text">类型:</span>
|
||||
<span className="section-text">Type:</span>
|
||||
<div className="values-container">
|
||||
{memoSpecialTypes.map((t, idx) => {
|
||||
return (
|
||||
|
@ -65,7 +65,7 @@ const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
||||
<>
|
||||
<div className="dialog-header-container">
|
||||
<p className="title-text">
|
||||
<span className="icon-text">🥰</span>分享 Memo 图片
|
||||
<span className="icon-text">🥰</span>Share Memo
|
||||
</p>
|
||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
||||
<img className="icon-img" src="/icons/close.svg" />
|
||||
@ -73,7 +73,7 @@ const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
||||
</div>
|
||||
<div className="dialog-content-container">
|
||||
<div className={`tip-words-container ${shortcutImgUrl ? "finish" : "loading"}`}>
|
||||
<p className="tip-text">{shortcutImgUrl ? "右键或长按即可保存图片 👇" : "图片生成中..."}</p>
|
||||
<p className="tip-text">{shortcutImgUrl ? "Right click or long press to save image 👇" : "Generating the screenshot..."}</p>
|
||||
</div>
|
||||
<div className="memo-container" ref={memoElRef}>
|
||||
<Only when={shortcutImgUrl !== ""}>
|
||||
|
@ -37,7 +37,7 @@ const ShortcutList: React.FC<Props> = () => {
|
||||
return (
|
||||
<div className="shortcuts-wrapper">
|
||||
<p className="title-text">
|
||||
<span className="normal-text">快速检索</span>
|
||||
<span className="normal-text">Shortcuts</span>
|
||||
<span className="btn" onClick={() => showCreateQueryDialog()}>
|
||||
+
|
||||
</span>
|
||||
@ -45,7 +45,7 @@ const ShortcutList: React.FC<Props> = () => {
|
||||
<Only when={loadingState.isSucceed && sortedShortcuts.length === 0}>
|
||||
<div className="create-shortcut-btn-container">
|
||||
<span className="btn" onClick={() => showCreateQueryDialog()}>
|
||||
创建检索
|
||||
New shortcut
|
||||
</span>
|
||||
</div>
|
||||
</Only>
|
||||
@ -147,17 +147,17 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
|
||||
<div className={`action-btns-wrapper ${showActionBtns ? "" : "hidden"}`} onMouseLeave={handleActionBtnContainerMouseLeave}>
|
||||
<div className="action-btns-container">
|
||||
<span className="btn" onClick={handlePinQueryBtnClick}>
|
||||
{shortcut.rowStatus === "ARCHIVED" ? "取消置顶" : "置顶"}
|
||||
{shortcut.rowStatus === "ARCHIVED" ? "Unpin" : "Pin"}
|
||||
</span>
|
||||
<span className="btn" onClick={handleEditQueryBtnClick}>
|
||||
编辑
|
||||
Edit
|
||||
</span>
|
||||
<span
|
||||
className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`}
|
||||
onClick={handleDeleteMemoClick}
|
||||
onMouseLeave={handleDeleteBtnMouseLeave}
|
||||
>
|
||||
{showConfirmDeleteBtn ? "确定删除!" : "删除"}
|
||||
{showConfirmDeleteBtn ? "Delete!" : "Delete"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -70,14 +70,14 @@ const TagList: React.FC<Props> = () => {
|
||||
|
||||
return (
|
||||
<div className="tags-wrapper">
|
||||
<p className="title-text">常用标签</p>
|
||||
<p className="title-text">Tags</p>
|
||||
<div className="tags-container">
|
||||
{tags.map((t, idx) => (
|
||||
<TagItemContainer key={t.text + "-" + idx} tag={t} tagQuery={tagQuery} />
|
||||
))}
|
||||
<Only when={tags.length < 5 && memoService.initialized}>
|
||||
<p className="tag-tip-container">
|
||||
输入<span className="code-text"># Tag </span>来创建标签吧~
|
||||
Enter <span className="code-text"># Tag </span> to create a tag
|
||||
</p>
|
||||
</Only>
|
||||
</div>
|
||||
|
@ -58,7 +58,7 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
||||
<img className="icon-img" src="/icons/arrow-left.svg" />
|
||||
</span>
|
||||
<span className="normal-text">
|
||||
{firstDate.getFullYear()} 年 {firstDate.getMonth() + 1} 月
|
||||
{firstDate.getFullYear()}/{firstDate.getMonth() + 1}
|
||||
</span>
|
||||
<span className="btn-text" onClick={() => handleChangeMonthBtnClick(1)}>
|
||||
<img className="icon-img" src="/icons/arrow-right.svg" />
|
||||
@ -66,13 +66,13 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
|
||||
</div>
|
||||
<div className="date-picker-day-container">
|
||||
<div className="date-picker-day-header">
|
||||
<span className="day-item">周一</span>
|
||||
<span className="day-item">周二</span>
|
||||
<span className="day-item">周三</span>
|
||||
<span className="day-item">周四</span>
|
||||
<span className="day-item">周五</span>
|
||||
<span className="day-item">周六</span>
|
||||
<span className="day-item">周日</span>
|
||||
<span className="day-item">Mon</span>
|
||||
<span className="day-item">Tue</span>
|
||||
<span className="day-item">Web</span>
|
||||
<span className="day-item">Thu</span>
|
||||
<span className="day-item">Fri</span>
|
||||
<span className="day-item">Sat</span>
|
||||
<span className="day-item">Sun</span>
|
||||
</div>
|
||||
|
||||
{dayList.map((d) => {
|
||||
|
@ -15,7 +15,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const nullItem = {
|
||||
text: "请选择",
|
||||
text: "Select",
|
||||
value: "",
|
||||
};
|
||||
|
||||
|
@ -1,23 +1,23 @@
|
||||
// 移动端样式适配额外类名
|
||||
// mobile style addtion classname
|
||||
export const SHOW_SIDERBAR_MOBILE_CLASSNAME = "mobile-show-sidebar";
|
||||
|
||||
// 默认动画持续时长
|
||||
// default animation duration
|
||||
export const ANIMATION_DURATION = 200;
|
||||
|
||||
// toast 动画持续时长
|
||||
// toast animation duration
|
||||
export const TOAST_ANIMATION_DURATION = 400;
|
||||
|
||||
// 一天的毫秒数
|
||||
// millisecond in a day
|
||||
export const DAILY_TIMESTAMP = 3600 * 24 * 1000;
|
||||
|
||||
// 标签 正则
|
||||
// tag regex
|
||||
export const TAG_REG = /#\s?(.+?)\s/g;
|
||||
|
||||
// URL 正则
|
||||
// URL regex
|
||||
export const LINK_REG = /(https?:\/\/[^\s<\\*>']+)/g;
|
||||
|
||||
// 图片 正则
|
||||
// image regex
|
||||
export const IMAGE_URL_REG = /([^\s<\\*>']+\.(jpeg|jpg|gif|png|svg))/g;
|
||||
|
||||
// memo 关联正则
|
||||
// linked memo regex
|
||||
export const MEMO_LINK_REG = /\[@(.+?)\]\((.+?)\)/g;
|
||||
|
@ -1,68 +1,68 @@
|
||||
import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "./consts";
|
||||
|
||||
export const relationConsts = [
|
||||
{ text: "且", value: "AND" },
|
||||
{ text: "或", value: "OR" },
|
||||
{ text: "And", value: "AND" },
|
||||
{ text: "Or", value: "OR" },
|
||||
];
|
||||
|
||||
export const filterConsts = {
|
||||
TAG: {
|
||||
text: "Tag",
|
||||
value: "TAG",
|
||||
text: "标签",
|
||||
operators: [
|
||||
{
|
||||
text: "包括",
|
||||
text: "Contains",
|
||||
value: "CONTAIN",
|
||||
},
|
||||
{
|
||||
text: "排除",
|
||||
text: "Does not contain",
|
||||
value: "NOT_CONTAIN",
|
||||
},
|
||||
],
|
||||
},
|
||||
TYPE: {
|
||||
text: "Type",
|
||||
value: "TYPE",
|
||||
text: "类型",
|
||||
operators: [
|
||||
{
|
||||
text: "Is",
|
||||
value: "IS",
|
||||
text: "是",
|
||||
},
|
||||
{
|
||||
text: "Is not",
|
||||
value: "IS_NOT",
|
||||
text: "不是",
|
||||
},
|
||||
],
|
||||
values: [
|
||||
{
|
||||
text: "Connected",
|
||||
value: "CONNECTED",
|
||||
text: "有关联",
|
||||
},
|
||||
{
|
||||
text: "No tags",
|
||||
value: "NOT_TAGGED",
|
||||
text: "无标签",
|
||||
},
|
||||
{
|
||||
text: "Has links",
|
||||
value: "LINKED",
|
||||
text: "有超链接",
|
||||
},
|
||||
{
|
||||
text: "Has images",
|
||||
value: "IMAGED",
|
||||
text: "有图片",
|
||||
},
|
||||
],
|
||||
},
|
||||
TEXT: {
|
||||
text: "Text",
|
||||
value: "TEXT",
|
||||
text: "文本",
|
||||
operators: [
|
||||
{
|
||||
text: "Contain",
|
||||
value: "CONTAIN",
|
||||
text: "包括",
|
||||
},
|
||||
{
|
||||
text: "Does not contain",
|
||||
value: "NOT_CONTAIN",
|
||||
text: "排除",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -4,13 +4,10 @@ import { InputAction } from "tiny-undo";
|
||||
* Define storage data type
|
||||
*/
|
||||
interface StorageData {
|
||||
// 编辑器输入缓存内容
|
||||
// Editor content cache
|
||||
editorContentCache: string;
|
||||
// 分词开关
|
||||
shouldSplitMemoWord: boolean;
|
||||
// 是否隐藏图片链接地址
|
||||
shouldHideImageUrl: boolean;
|
||||
// markdown 解析开关
|
||||
shouldUseMarkdownParser: boolean;
|
||||
|
||||
// Editor setting
|
||||
|
@ -1,15 +1,15 @@
|
||||
// 验证器
|
||||
// * 主要用于验证表单
|
||||
// Validator
|
||||
// * use for validating form data
|
||||
const chineseReg = /[\u3000\u3400-\u4DBF\u4E00-\u9FFF]/;
|
||||
|
||||
export interface ValidatorConfig {
|
||||
// 最小长度
|
||||
// min length
|
||||
minLength: number;
|
||||
// 最大长度
|
||||
// max length
|
||||
maxLength: number;
|
||||
// 无空格
|
||||
// no space
|
||||
noSpace: boolean;
|
||||
// 无中文
|
||||
// no chinese
|
||||
noChinese: boolean;
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ export function validate(text: string, config: Partial<ValidatorConfig>): { resu
|
||||
if (text.length < config.minLength) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "长度过短",
|
||||
reason: "Too short",
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -27,7 +27,7 @@ export function validate(text: string, config: Partial<ValidatorConfig>): { resu
|
||||
if (text.length > config.maxLength) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "长度超出",
|
||||
reason: "Too long",
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -35,14 +35,14 @@ export function validate(text: string, config: Partial<ValidatorConfig>): { resu
|
||||
if (config.noSpace && text.includes(" ")) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "不应含有空格",
|
||||
reason: "Don't allow space",
|
||||
};
|
||||
}
|
||||
|
||||
if (config.noChinese && chineseReg.test(text)) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "不应含有中文字符",
|
||||
reason: "Don't allow chinese",
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@ export interface Store<S extends State, A extends Action> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单实现的 Redux
|
||||
* @param preloadedState 初始 state
|
||||
* Toy-Redux
|
||||
* @param preloadedState initial state
|
||||
* @param reducer reducer pure function
|
||||
* @returns store
|
||||
*/
|
||||
|
@ -114,13 +114,13 @@ const MemoTrash: React.FC<Props> = () => {
|
||||
<img className="icon-img" src="/icons/menu.svg" alt="menu" />
|
||||
</button>
|
||||
</Only>
|
||||
<span className="normal-text">回收站</span>
|
||||
<span className="normal-text">Recycle Bin</span>
|
||||
</div>
|
||||
</div>
|
||||
<MemoFilter />
|
||||
{loadingState.isLoading ? (
|
||||
<div className="tip-text-container">
|
||||
<p className="tip-text">努力请求数据中...</p>
|
||||
<p className="tip-text">fetching data...</p>
|
||||
</div>
|
||||
) : deletedMemos.length === 0 ? (
|
||||
<div className="tip-text-container">
|
||||
|
@ -30,7 +30,7 @@ const Setting: React.FC<Props> = () => {
|
||||
<img className="icon-img" src="/icons/menu.svg" alt="menu" />
|
||||
</button>
|
||||
</Only>
|
||||
<span className="normal-text">账号与设置</span>
|
||||
<span className="normal-text">Settings</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -55,13 +55,13 @@ const Signin: React.FC<Props> = () => {
|
||||
|
||||
const usernameValidResult = validate(username, validateConfig);
|
||||
if (!usernameValidResult.result) {
|
||||
toastHelper.error("用户名 " + usernameValidResult.reason);
|
||||
toastHelper.error("Username: " + usernameValidResult.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
const passwordValidResult = validate(password, validateConfig);
|
||||
if (!passwordValidResult.result) {
|
||||
toastHelper.error("密码 " + passwordValidResult.reason);
|
||||
toastHelper.error("Password: " + passwordValidResult.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ const Signin: React.FC<Props> = () => {
|
||||
if (user) {
|
||||
locationService.replaceHistory("/");
|
||||
} else {
|
||||
toastHelper.error("😟 登录失败");
|
||||
toastHelper.error("😟 Login failed");
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
@ -107,7 +107,7 @@ const Signin: React.FC<Props> = () => {
|
||||
if (user) {
|
||||
locationService.replaceHistory("/");
|
||||
} else {
|
||||
toastHelper.error("😟 登录失败");
|
||||
toastHelper.error("😟 Login failed");
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
@ -121,7 +121,7 @@ const Signin: React.FC<Props> = () => {
|
||||
<div className="page-container">
|
||||
<div className="page-header-container">
|
||||
<p className="title-text">
|
||||
登录 Memos <span className="icon-text">✍️</span>
|
||||
Login to Memos <span className="icon-text">✍️</span>
|
||||
</p>
|
||||
</div>
|
||||
{showAutoSigninAsGuest ? (
|
||||
@ -132,13 +132,13 @@ const Signin: React.FC<Props> = () => {
|
||||
className={`btn guest-signin ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||
onClick={handleAutoSigninAsGuestBtnClick}
|
||||
>
|
||||
👉 快速登录进行体验
|
||||
👉 Login as Guest quickly
|
||||
</div>
|
||||
<div
|
||||
className={`btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||
onClick={handleSwitchAccountSigninBtnClick}
|
||||
>
|
||||
已有账号,我要自己登录
|
||||
I have an account
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@ -146,11 +146,11 @@ const Signin: React.FC<Props> = () => {
|
||||
<>
|
||||
<div className="page-content-container">
|
||||
<div className="form-item-container input-form-container">
|
||||
<span className={"normal-text " + (username === "" ? "" : "not-null")}>账号</span>
|
||||
<span className={"normal-text " + (username === "" ? "" : "not-null")}>Usernmae</span>
|
||||
<input type="text" autoComplete="off" value={username} onChange={handleUsernameInputChanged} />
|
||||
</div>
|
||||
<div className="form-item-container input-form-container">
|
||||
<span className={"normal-text " + (password === "" ? "" : "not-null")}>密码</span>
|
||||
<span className={"normal-text " + (password === "" ? "" : "not-null")}>Password</span>
|
||||
<input type="password" autoComplete="off" value={password} onChange={handlePasswordInputChanged} />
|
||||
</div>
|
||||
</div>
|
||||
@ -161,14 +161,14 @@ const Signin: React.FC<Props> = () => {
|
||||
className={`btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||
onClick={handleAutoSigninAsGuestBtnClick}
|
||||
>
|
||||
体验一下
|
||||
Login as Guest
|
||||
</button>
|
||||
<span className="split-text">/</span>
|
||||
<button
|
||||
className={`btn signin-btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||
onClick={() => handleSigninBtnsClick("signup")}
|
||||
>
|
||||
注册
|
||||
Sign up
|
||||
</button>
|
||||
<span className="split-text">/</span>
|
||||
<button
|
||||
@ -176,7 +176,7 @@ const Signin: React.FC<Props> = () => {
|
||||
className={`btn signin-btn ${signinBtnsClickLoadingState.isLoading ? "requesting" : ""}`}
|
||||
onClick={() => handleSigninBtnsClick("signin")}
|
||||
>
|
||||
登录
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@ class ResourceService {
|
||||
const { name: filename, size } = file;
|
||||
|
||||
if (size > 5 << 20) {
|
||||
return Promise.reject("超过最大文件大小 5Mb");
|
||||
return Promise.reject("overload max size: 5Mb");
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
2
web/src/types/basic.d.ts
vendored
2
web/src/types/basic.d.ts
vendored
@ -1,9 +1,7 @@
|
||||
type BasicType = undefined | null | boolean | number | string | Record<string, unknown> | Array<BasicType>;
|
||||
|
||||
// 日期戳
|
||||
type DateStamp = number;
|
||||
|
||||
// 时间戳
|
||||
type TimeStamp = number;
|
||||
|
||||
type FunctionType = (...args: unknown[]) => unknown;
|
||||
|
Loading…
Reference in New Issue
Block a user