Просмотр исходного кода

'添加electron-storez支持关闭客户端后仍能保存数据'

main
郑州 3 лет назад
Родитель
Сommit
8837db3e3d
11 измененных файлов: 190 добавлений и 14 удалений
  1. +5
    -0
      .gitignore
  2. +4
    -0
      electron/main.js
  3. +7
    -2
      electron/preload.js
  4. +68
    -0
      electron/storage.js
  5. +2
    -0
      package.json
  6. +6
    -3
      src/app.ts
  7. +3
    -0
      src/components/AppHeader/components/userCenter/UserCenter.tsx
  8. +4
    -0
      src/global.d.ts
  9. +25
    -0
      src/pages/login/index.tsx
  10. +17
    -4
      src/services/system.ts
  11. +49
    -5
      src/utils/storage.ts

+ 5
- 0
.gitignore Просмотреть файл

@@ -18,3 +18,8 @@
/src/.umi-production
/src/.umi-test
/.env.local


#
/logs
/文档

+ 4
- 0
electron/main.js Просмотреть файл

@@ -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();



+ 7
- 2
electron/preload.js Просмотреть файл

@@ -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事件处理
*/

+ 68
- 0
electron/storage.js Просмотреть файл

@@ -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();
// });
};

+ 2
- 0
package.json Просмотреть файл

@@ -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"
},


+ 6
- 3
src/app.ts Просмотреть файл

@@ -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,


+ 3
- 0
src/components/AppHeader/components/userCenter/UserCenter.tsx Просмотреть файл

@@ -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();


+ 4
- 0
src/global.d.ts Просмотреть файл

@@ -3,5 +3,9 @@ import { IpcRenderer } from 'electron';
declare global {
interface Window {
ipcRenderer: IpcRenderer;
initialStorage?: {
keyList: string[];
[key: string]: any;
};
}
}

+ 25
- 0
src/pages/login/index.tsx Просмотреть файл

@@ -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}


+ 17
- 4
src/services/system.ts Просмотреть файл

@@ -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 });
}),
},
};



+ 49
- 5
src/utils/storage.ts Просмотреть файл

@@ -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;

Загрузка…
Отмена
Сохранить