@@ -18,3 +18,8 @@ | |||
/src/.umi-production | |||
/src/.umi-test | |||
/.env.local | |||
# | |||
/logs | |||
/文档 |
@@ -1,6 +1,7 @@ | |||
const { app, BrowserWindow, dialog, ipcMain, shell } = require('electron'); | |||
const path = require('path'); | |||
const url = require('url'); | |||
const { initialStorageEvents } = require('./storage'); | |||
let mainWindow; | |||
@@ -84,6 +85,9 @@ ipcMain.handle('open-browser', (event, url) => { | |||
shell.openExternal(url); | |||
}); | |||
// 初始化electron-store相关API | |||
initialStorageEvents(ipcMain); | |||
app.on('ready', () => { | |||
createWindow(); | |||
@@ -1,6 +1,11 @@ | |||
const electron = require('electron'); | |||
const { contextBridge, ipcRenderer } = electron; | |||
// console.log(electron.ipcRenderer.on); | |||
const { storage } = require('./storage'); | |||
contextBridge.exposeInMainWorld('ipcRenderer', ipcRenderer); | |||
contextBridge.exposeInMainWorld('initialStorage', storage.getAllItem()); | |||
/** | |||
* todo: | |||
* websocket事件处理 | |||
*/ |
@@ -0,0 +1,68 @@ | |||
const ElectronStore = require('electron-store'); | |||
const eStore = new ElectronStore(); | |||
const PERSIST_KEY_NAME = '_persistKeys'; | |||
const keyList = { | |||
add(key) { | |||
const keys = this.get(); | |||
if (this.has(key)) { | |||
return; | |||
} | |||
keys.push(key); | |||
eStore.set(PERSIST_KEY_NAME, keys.join(',')); | |||
}, | |||
get() { | |||
return eStore | |||
.get(PERSIST_KEY_NAME, '') | |||
.split(',') | |||
.filter((a) => a); | |||
}, | |||
has(key, keys) { | |||
return (keys || this.get()).find((iKey) => iKey === key); | |||
}, | |||
remove(key) { | |||
const keys = this.get(); | |||
eStore.set(PERSIST_KEY_NAME, keys.filter((iKey) => iKey !== key).join(',')); | |||
}, | |||
}; | |||
const storage = { | |||
getItem(key) { | |||
return eStore.get(key); | |||
}, | |||
setItem(key, value) { | |||
if (key === PERSIST_KEY_NAME) return; | |||
keyList.add(key); | |||
return eStore.set(key, value); | |||
}, | |||
removeItem(key) { | |||
keyList.remove(key); | |||
return eStore.delete(key); | |||
}, | |||
getAllItem() { | |||
const keys = keyList.get(); | |||
const hash = { | |||
keyList: keys, | |||
}; | |||
keys.forEach((key) => { | |||
hash[key] = eStore.get(key); | |||
}); | |||
return hash; | |||
}, | |||
}; | |||
module.exports.storage = storage; | |||
module.exports.initialStorageEvents = function initialStorageEvents(ipcMain) { | |||
ipcMain.handle('storage:set', (_, { key, value }) => { | |||
storage.setItem(key, value); | |||
}); | |||
ipcMain.handle('storage:remove', (_, { key }) => { | |||
storage.removeItem(key); | |||
}); | |||
// ipcMain.handle('storage:getAll', () => { | |||
// return storage.getAllItem(); | |||
// }); | |||
}; |
@@ -27,6 +27,7 @@ | |||
"dependencies": { | |||
"@ant-design/icons": "^4.6.2", | |||
"@ant-design/pro-layout": "^6.5.0", | |||
"@ant-design/pro-table": "^2.43.4", | |||
"@types/lodash": "^4.14.170", | |||
"@umijs/preset-react": "1.x", | |||
"ahooks": "^2.10.6", | |||
@@ -36,6 +37,7 @@ | |||
"dayjs": "^1.10.5", | |||
"electron": "^13.1.4", | |||
"electron-packager": "^15.2.0", | |||
"electron-store": "^8.0.0", | |||
"lodash": "^4.17.21", | |||
"umi": "^3.4.25" | |||
}, | |||
@@ -4,6 +4,7 @@ import { notification } from 'antd'; | |||
import { firstCharToLowerCase, handleRequest } from './utils/tool'; | |||
import { isObject } from 'lodash'; | |||
import { logout, queryCurrent } from './services/user'; | |||
import storage from './utils/storage'; | |||
const codeMessage = { | |||
200: '服务器成功返回请求的数据。', | |||
@@ -102,6 +103,11 @@ export async function getInitialState(): Promise<{ | |||
currentUser?: DATA.User | null; | |||
}> { | |||
async function fetchUserInfo() { | |||
const accountId = storage.get('accountId'); | |||
if (!accountId) { | |||
logout(); | |||
return null; | |||
} | |||
const res = await queryCurrent(); | |||
handleRequest(res) | |||
.error(() => logout()) | |||
@@ -116,9 +122,6 @@ export async function getInitialState(): Promise<{ | |||
history.location.pathname !== '/~docs' | |||
) { | |||
const currentUser = await fetchUserInfo(); | |||
if (!currentUser) { | |||
logout(); | |||
} | |||
return { | |||
fetchUserInfo, | |||
isLogin: !!currentUser, | |||
@@ -13,6 +13,7 @@ import styles from './UserCenter.less'; | |||
import defaultAvatorImg from '@/assets/avator_default.svg'; | |||
import FolderSetModal from './FolderSetModal'; | |||
import { isClient } from '@/services/system'; | |||
import storage from '@/utils/storage'; | |||
interface UserCenterProps { | |||
className?: string; | |||
@@ -66,6 +67,8 @@ function PopContent(props: PopContentProps) { | |||
const tryLogout = useCallback(() => { | |||
if (hidePop) hidePop(); | |||
storage.remove('account'); | |||
storage.remove('password'); | |||
confirm({ | |||
onOk() { | |||
logout(); | |||
@@ -3,5 +3,9 @@ import { IpcRenderer } from 'electron'; | |||
declare global { | |||
interface Window { | |||
ipcRenderer: IpcRenderer; | |||
initialStorage?: { | |||
keyList: string[]; | |||
[key: string]: any; | |||
}; | |||
} | |||
} |
@@ -13,6 +13,10 @@ import classNames from 'classnames'; | |||
import SmsInputer from './SmsInputer'; | |||
import { memoize } from 'lodash'; | |||
import { Rule } from 'antd/lib/form'; | |||
import { isClient } from '@/services/system'; | |||
import storage from '@/utils/storage'; | |||
import { useRef } from 'react'; | |||
import { useLayoutEffect } from 'react'; | |||
export default function Login() { | |||
const [errText, setErrText] = useState(''); | |||
@@ -21,6 +25,7 @@ export default function Login() { | |||
const [loading, setLoading] = useState(false); | |||
const [regModalVisible, setRegModalVisible] = useState(false); | |||
const { refresh } = useModel('@@initialState'); | |||
const buttonRef = useRef<HTMLElement>(null); | |||
// const { loading, signin } = useModel('useAuthModel'); | |||
const onLogin = useCallback(async () => { | |||
@@ -40,6 +45,11 @@ export default function Login() { | |||
setErrText(res.message!); | |||
}) | |||
.success(() => { | |||
// 记录账号密码 | |||
if (isClient) { | |||
storage.set('account', account, true); | |||
storage.set('password', password, true); | |||
} | |||
// history.push('/'); | |||
// window.location.href = '/'; | |||
history.replace('/'); | |||
@@ -52,6 +62,20 @@ export default function Login() { | |||
setErrText(''); | |||
}, [account, password]); | |||
useLayoutEffect(() => { | |||
if (isClient) { | |||
const iAccount = storage.get('account'); | |||
const iPass = storage.get('password'); | |||
if (iAccount && iPass) { | |||
setAccount(iAccount); | |||
setPassword(iPass); | |||
setTimeout(() => { | |||
buttonRef.current?.click(); | |||
}, 500); | |||
} | |||
} | |||
}, [buttonRef]); | |||
return ( | |||
<div className={styles.login}> | |||
<iframe | |||
@@ -85,6 +109,7 @@ export default function Login() { | |||
<div className={styles.errText}>{errText}</div> | |||
<div className={styles.btnGroup}> | |||
<Button | |||
ref={buttonRef} | |||
loading={loading} | |||
type="primary" | |||
className={styles.btn} | |||
@@ -1,21 +1,34 @@ | |||
export const isClient = !!window.ipcRenderer; // process.env.IS_CLIENT; | |||
const safeCall = (f: (...args: any[]) => any) => (isClient ? f : () => {}); | |||
const ipcRenderer = window.ipcRenderer; | |||
const system = { | |||
closeWindow() { | |||
window.ipcRenderer.invoke('manipulate-window', { action: 'close' }); | |||
ipcRenderer.invoke('manipulate-window', { action: 'close' }); | |||
}, | |||
zoomWindow() { | |||
window.ipcRenderer.invoke('manipulate-window', { action: 'zoom' }); | |||
ipcRenderer.invoke('manipulate-window', { action: 'zoom' }); | |||
}, | |||
minimizeWindow() { | |||
window.ipcRenderer.invoke('manipulate-window', { action: 'minimize' }); | |||
ipcRenderer.invoke('manipulate-window', { action: 'minimize' }); | |||
}, | |||
openUrl(url: string) { | |||
if (!isClient) { | |||
window.open(url, 'blank'); | |||
return; | |||
} | |||
window.ipcRenderer.invoke('open-browser', url); | |||
ipcRenderer.invoke('open-browser', url); | |||
}, | |||
storage: { | |||
set: safeCall((key: string, value: any) => { | |||
ipcRenderer.invoke('storage:set', { key, value }); | |||
}), | |||
remove: safeCall((key: string) => { | |||
ipcRenderer.invoke('storage:remove', { key }); | |||
}), | |||
}, | |||
}; | |||
@@ -1,15 +1,59 @@ | |||
import system, { isClient } from '@/services/system'; | |||
import { identity } from 'lodash'; | |||
const keepSaveKeyMap = new Map(); | |||
const persistKeyName = '_persistKeys'; // key1,key2 | |||
const storage = { | |||
get(key: string) { | |||
return sessionStorage.getItem(key); | |||
return localStorage.getItem(key); | |||
}, | |||
set(key: string, value: string, keep = false) { | |||
if (keep) { | |||
system.storage.set(key, value); | |||
// keepSaveKeyMap.set(key, true); | |||
} | |||
localStorage.setItem(key, value); | |||
}, | |||
set(key: string, value: string) { | |||
sessionStorage.setItem(key, value); | |||
remove(key: string) { | |||
system.storage.remove(key); | |||
keepSaveKeyMap.delete(key); | |||
return localStorage.removeItem(key); | |||
}, | |||
clear() { | |||
sessionStorage.clear(); | |||
} | |||
// 保留keep过的属性 | |||
const tempStorage: { [key: string]: any } = {}; | |||
keepSaveKeyMap.forEach((_, key) => { | |||
tempStorage[key] = storage.get(key); | |||
}); | |||
localStorage.clear(); | |||
keepSaveKeyMap.forEach((_, key) => { | |||
localStorage.setItem(key, tempStorage[key]); | |||
}); | |||
}, | |||
init() { | |||
if (window.initialStorage) { | |||
const hash = window.initialStorage || {}; | |||
const kList = hash.keyList || []; | |||
kList.forEach((key: string) => { | |||
keepSaveKeyMap.set(key, true); | |||
storage.set(key, hash[key]); | |||
}); | |||
} | |||
}, | |||
}; | |||
if (window.initialStorage) { | |||
storage.clear(); | |||
const hash = window.initialStorage || {}; | |||
const kList = hash.keyList || []; | |||
kList.forEach((key: string) => { | |||
keepSaveKeyMap.set(key, true); | |||
storage.set(key, hash[key]); | |||
}); | |||
} | |||
export default storage; |