feat: 优化

This commit is contained in:
范胜发
2022-03-11 17:22:33 +08:00
parent 9bae8badbd
commit acf83137e1
24 changed files with 453 additions and 193 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@
# production # production
/build /build
/dist
# misc # misc
.DS_Store .DS_Store

BIN
icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

35
main.js
View File

@ -1,35 +1,37 @@
const { app, BrowserWindow, ipcMain } = require('electron'); const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path'); const path = require('path');
const url = require('url'); const url = require('url');
const { install: storeInstall } = require('./store/index');
let win = null; let win = null;
ipcMain.on('size-change', (event, flag) => { ipcMain.on('size-change', (event, flag) => {
win.setOpacity(0);
const [x, y] = win.getPosition(); const [x, y] = win.getPosition();
if (flag) { if (flag) {
win.setSize(500, 300, true); win.setBounds({
win.setPosition(x, y - 300 + 75, true); x,
y: y - 300 + 75,
width: 500,
height: 300,
});
} else { } else {
win.setSize(120, 75, true); win.setBounds({
win.setPosition(x, y + 300 - 75, true); x,
y: y + 300 - 75,
width: 120,
height: 75,
});
} }
setTimeout(() => {
win.setOpacity(1);
}, 300);
}); });
ipcMain.on('pos-change', (event, { x, y }) => { ipcMain.on('pos-change', (event, { x, y }) => {
win.setPosition(x, y, true); win.setPosition(x, y, true);
}); });
const { getTodo, setTodo } = require('./store');
ipcMain.handle('get-todo', () => {
console.log(getTodo());
return getTodo();
});
ipcMain.on('set-todo', (e, value) => {
setTodo(value);
});
function createWindow() { function createWindow() {
win = new BrowserWindow({ win = new BrowserWindow({
width: 120, width: 120,
@ -39,7 +41,7 @@ function createWindow() {
frame: false, frame: false,
transparent: true, transparent: true,
webPreferences: { webPreferences: {
preload: path.join(__dirname, 'preload.js'), preload: path.join(__dirname, 'preload/preload.js'),
}, },
}); });
@ -57,5 +59,6 @@ function createWindow() {
} }
app.whenReady().then(() => { app.whenReady().then(() => {
storeInstall();
createWindow(); createWindow();
}); });

5
package-lock.json generated
View File

@ -4713,6 +4713,11 @@
"whatwg-url": "^8.0.0" "whatwg-url": "^8.0.0"
} }
}, },
"dayjs": {
"version": "1.10.8",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.10.8.tgz",
"integrity": "sha512-wbNwDfBHHur9UOzNUjeKUOJ0fCb0a52Wx0xInmQ7Y8FstyajiV1NmK1e00cxsr9YrE9r7yAChE0VvpuY5Rnlow=="
},
"debounce-fn": { "debounce-fn": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/debounce-fn/-/debounce-fn-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/debounce-fn/-/debounce-fn-4.0.0.tgz",

View File

@ -9,6 +9,7 @@
"@testing-library/jest-dom": "^5.16.2", "@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3", "@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"dayjs": "^1.10.8",
"electron-store": "^8.0.1", "electron-store": "^8.0.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
@ -22,7 +23,7 @@
"eject": "react-scripts eject", "eject": "react-scripts eject",
"make": "node --max-old-space-size=8192 && electron-builder --win --x64", "make": "node --max-old-space-size=8192 && electron-builder --win --x64",
"electron": "electron .", "electron": "electron .",
"electron:build": "electron-packager ./build TimeCat --platform=win32 --arch=x64 --out=./../out --asar --app-version=0.1.0" "electron:build": "electron-packager ./build TimeCat --platform=win32 --download.mirrorOptions.mirror=https://npmmirror.com/mirrors/electron/ --arch=x64 --out=./../out --asar --app-version=0.1.0"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
@ -53,7 +54,8 @@
"win": { "win": {
"target": [ "target": [
"nsis" "nsis"
] ],
"icon": "icon.ico"
}, },
"nsis": { "nsis": {
"oneClick": false, "oneClick": false,

View File

@ -4,5 +4,6 @@ contextBridge.exposeInMainWorld('electron', {
ipcRenderer: { ipcRenderer: {
...ipcRenderer, ...ipcRenderer,
on: ipcRenderer.on, on: ipcRenderer.on,
invoke: ipcRenderer.invoke,
}, },
}); });

View File

@ -1,35 +1,37 @@
const { app, BrowserWindow, ipcMain } = require('electron'); const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path'); const path = require('path');
const url = require('url'); const url = require('url');
const { install: storeInstall } = require('./store/index');
let win = null; let win = null;
ipcMain.on('size-change', (event, flag) => { ipcMain.on('size-change', (event, flag) => {
win.setOpacity(0);
const [x, y] = win.getPosition(); const [x, y] = win.getPosition();
if (flag) { if (flag) {
win.setSize(500, 300, true); win.setBounds({
win.setPosition(x, y - 300 + 75, true); x,
y: y - 300 + 75,
width: 500,
height: 300,
});
} else { } else {
win.setSize(120, 75, true); win.setBounds({
win.setPosition(x, y + 300 - 75, true); x,
y: y + 300 - 75,
width: 120,
height: 75,
});
} }
setTimeout(() => {
win.setOpacity(1);
}, 300);
}); });
ipcMain.on('pos-change', (event, { x, y }) => { ipcMain.on('pos-change', (event, { x, y }) => {
win.setPosition(x, y, true); win.setPosition(x, y, true);
}); });
const { getTodo, setTodo } = require('./store');
ipcMain.handle('get-todo', () => {
console.log(getTodo());
return getTodo();
});
ipcMain.on('set-todo', (e, value) => {
setTodo(value);
});
function createWindow() { function createWindow() {
win = new BrowserWindow({ win = new BrowserWindow({
width: 120, width: 120,
@ -39,13 +41,13 @@ function createWindow() {
frame: false, frame: false,
transparent: true, transparent: true,
webPreferences: { webPreferences: {
preload: path.join(__dirname, 'preload.js'), preload: path.join(__dirname, 'preload/preload.js'),
}, },
}); });
win.loadURL( win.loadURL(
url.format({ url.format({
pathname: path.join(__dirname, './index.html'), pathname: path.join(__dirname, 'build/index.html'),
protocol: 'file:', protocol: 'file:',
slashes: true, slashes: true,
}) })
@ -57,5 +59,6 @@ function createWindow() {
} }
app.whenReady().then(() => { app.whenReady().then(() => {
storeInstall();
createWindow(); createWindow();
}); });

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,52 +0,0 @@
{
"name": "time-cat",
"version": "0.1.0",
"private": true,
"main": "main.js",
"homepage": ".",
"dependencies": {
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^13.5.0",
"electron-drag": "^2.0.0",
"electron-store": "^8.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "5.0.0",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"electron": "electron .",
"electron:build": "electron-packager ./build TimeCat --platform=win32 --arch=x64 --out=./../out --ar --app-version=0.1.0 --electron-version=17.1.0"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"electron": "^17.1.0",
"electron-builder": "^22.14.13",
"electron-packager": "^15.4.0"
},
"build": {
"appId": "time-cat-v0.1.0"
}
}

View File

@ -4,5 +4,6 @@ contextBridge.exposeInMainWorld('electron', {
ipcRenderer: { ipcRenderer: {
...ipcRenderer, ...ipcRenderer,
on: ipcRenderer.on, on: ipcRenderer.on,
invoke: ipcRenderer.invoke,
}, },
}); });

View File

@ -1,20 +0,0 @@
const Store = require('electron-store');
const store = new Store();
function initStore() {
if (!store.has('todo')) {
store.set('todo', []);
}
}
initStore();
module.exports = {
getTodo() {
return store.get('todo');
},
setTodo(value) {
console.log(store.path);
return store.set('todo', value);
},
};

29
public/store/index.js Normal file
View File

@ -0,0 +1,29 @@
const { ipcMain } = require('electron');
const { TodoStore } = require('./store');
const store = new TodoStore();
module.exports.install = function install() {
ipcMain.handle('get-todo', (e) => {
return store.getTodo();
});
ipcMain.handle('set-todo', (e, id, value) => {
store.setTodo(id, value);
return store.getTodo();
});
ipcMain.handle('add-todo', (e, value) => {
store.addTodo(value);
return store.getTodo();
});
ipcMain.handle('del-todo', (e, id) => {
store.delTodo(id);
return store.getTodo();
});
ipcMain.handle('clear-todo', (e, id, status) => {
store.setClear(id, status);
return store.getTodo();
});
};

102
public/store/store.js Normal file
View File

@ -0,0 +1,102 @@
const Store = require('electron-store');
const store = new Store();
const { isDef, isDate } = require('../utils/def');
class Todo {
constructor({ id, title, time, isClear, clearTime }) {
this.id = id;
this.title = title;
this.time = isDate(time) ? new Date(time).getTime() : Date.now();
this.isClear = isDef(isClear) ? isClear : false;
this.clearTime = isDate(clearTime) ? new Date(clearTime).getTime() : 0;
}
}
class TodoStore {
constructor() {
this.init();
}
init() {
if (!store.has('todo')) {
store.set('todo', []);
}
}
getTodo() {
return store.get('todo');
}
setTodo(id, value) {
const list = this.getTodo();
const i = list.findIndex((d) => d.id === id);
if (i > -1) {
list[i] = new Todo({
...value,
id,
});
store.set('todo', list);
return true;
} else {
return false;
}
}
addTodo(value) {
const list = this.getTodo();
const last = list[list.length - 1] || {};
const id = last.id ? `${last.id + 1}` : '1';
const newData = new Todo({
id,
...value,
});
list.push(newData);
store.set('todo', list);
return newData;
}
delTodo(id) {
const list = this.getTodo();
const i = list.findIndex((d) => d.id === id);
if (i > -1) {
list.splice(i, 1);
store.set('todo', list);
return true;
} else {
return false;
}
}
clearAll() {
store.set('todo', []);
return true;
}
setClear(id, status) {
const list = this.getTodo();
const i = list.findIndex((d) => d.id === id);
if (i > -1) {
list[i].isClear = isDef(status) ? status : !list[i].isClear;
if (list[i].isClear) {
list[i].clearTime = Date.now();
} else {
list[i].clearTime = 0;
}
store.set('todo', list);
return true;
} else {
return false;
}
}
}
module.exports.TodoStore = TodoStore;

12
public/utils/def.js Normal file
View File

@ -0,0 +1,12 @@
module.exports.isDef = function isDef(v) {
return v !== undefined && v !== null;
};
module.exports.isUnDef = function isUnDef(v) {
return v === undefined || v === null;
};
module.exports.isDate = function isDate(d) {
const D = new Date(d);
return !D.toString().includes('Invalid');
};

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import './App.css'; import './App.css';
import './styles/index.css'; import './styles/index.css';
@ -8,15 +8,17 @@ import Todo from './components/todo';
import catImg from './assets/img/cat.png'; import catImg from './assets/img/cat.png';
function App() { function App() {
const initData = JSON.parse(localStorage.getItem('todo') || '[]');
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
const [data, setData] = useState(initData); const [data, setData] = useState([]);
const { ipcRenderer } = window.electron; const { ipcRenderer } = window.electron;
console.log(data); useEffect(() => {
ipcRenderer.invoke('get-todo').then((data) => {
setData(data);
});
}, [show]);
function handleMouseDown(e) { function handleMouseDown(e) {
console.log('down');
const mouseX = e.pageX; const mouseX = e.pageX;
const mouseY = e.pageY; const mouseY = e.pageY;
@ -27,7 +29,6 @@ function App() {
function handleMove(e) { function handleMove(e) {
if (!isDrag) { if (!isDrag) {
console.log('move');
const cMouseX = e.pageX; const cMouseX = e.pageX;
const cMouseY = e.pageY; const cMouseY = e.pageY;
@ -35,7 +36,6 @@ function App() {
(cMouseX - mouseX) ** 2 + (cMouseY - mouseY) ** 2 (cMouseX - mouseX) ** 2 + (cMouseY - mouseY) ** 2
); );
if (dis > 10) { if (dis > 10) {
console.log('drag');
isDrag = true; isDrag = true;
} }
} else { } else {
@ -59,8 +59,37 @@ function App() {
} }
function changeSize() { function changeSize() {
setShow(!show);
ipcRenderer.send('size-change', !show); ipcRenderer.send('size-change', !show);
setTimeout(
() => {
setShow(!show);
},
show ? 0 : 300
);
}
function handleAdd(data) {
ipcRenderer.invoke('add-todo', data).then((result) => {
setData(result);
});
}
function handleEdit(data) {
ipcRenderer.invoke('set-todo', data.id, data).then((result) => {
setData(result);
});
}
function handleDel(id) {
ipcRenderer.invoke('del-todo', id).then((result) => {
setData(result);
});
}
function handleClear(id) {
ipcRenderer.invoke('clear-todo', id).then((result) => {
setData(result);
});
} }
return ( return (
@ -72,7 +101,15 @@ function App() {
src={catImg} src={catImg}
onMouseDown={handleMouseDown} onMouseDown={handleMouseDown}
/> />
{show && <Todo data={data} setData={setData}></Todo>} {show && (
<Todo
data={data}
add={handleAdd}
edit={handleEdit}
del={handleDel}
clear={handleClear}
></Todo>
)}
</div> </div>
); );
} }

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -44,4 +44,14 @@
.form-item__input { .form-item__input {
width: 100%; width: 100%;
border: 1px solid #767676;
border-radius: 2px;
outline: none;
box-sizing: border-box;
}
.form-item__input:focus {
border: 1px solid #6a8af1;
} }

View File

@ -7,8 +7,12 @@ export function TodoForm(props) {
function handleSubmit(e) { function handleSubmit(e) {
props.submit({ props.submit({
data: {
...props.data,
title, title,
time, time,
},
status: props.status,
}); });
props.setIsShowForm(false); props.setIsShowForm(false);
e.preventDefault(); e.preventDefault();

View File

@ -1,18 +1,16 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import dayjs from 'dayjs';
import './index.css'; import './index.css';
import { TodoForm } from '../todo-form'; import { TodoForm } from '../todo-form';
function Todo(props) { function Todo(props) {
const [isShowForm, setIsShowForm] = useState(false); const [isShowForm, setIsShowForm] = useState(false);
const [editData, setEditData] = useState({ const [editData, setEditData] = useState({});
title: '', const [status, setStatus] = useState('add');
time: '',
});
const { ipcRenderer } = window.electron;
function handleAdd() { function handleAdd() {
setStatus('add');
setEditData({ setEditData({
title: '', title: '',
time: '', time: '',
@ -20,31 +18,32 @@ function Todo(props) {
setIsShowForm(true); setIsShowForm(true);
} }
function handleEdit({ title, time }, e) { function handleEdit({ id, title, time }, e) {
setStatus('edit');
setEditData({ setEditData({
id,
title, title,
time, time,
}); });
setIsShowForm(true); setIsShowForm(true);
} }
function handleSubmit(data) { function handleSubmit({ data, status }) {
props.setData([...props.data, data]); props[status](data);
ipcRenderer.send('set-todo', [...props.data, data]);
localStorage.setItem('todo', JSON.stringify([...props.data, data]));
} }
function handleDel(index) { function handleDel(id) {
const temp = props.data.filter((item, i) => i !== index); props.del(id);
props.setData(temp); }
ipcRenderer.send('set-todo', temp);
localStorage.setItem('todo', JSON.stringify(temp)); function handleClear(id) {
props.clear(id);
} }
return ( return (
<div className="todo"> <div className="todo">
<header className="todo-header"> <header className="todo-header">
<h1 className="todo-header__title">time cat</h1> <h1 className="todo-header__title"></h1>
<div className="todo-tools"> <div className="todo-tools">
<button <button
className="button-text todo-tools__button" className="button-text todo-tools__button"
@ -55,24 +54,28 @@ function Todo(props) {
</div> </div>
</header> </header>
<ul className="todo-container"> <ul className="todo-container">
{props.data.map((todo, i) => { {props.data
.filter((d) => !d.isClear)
.map((todo, i) => {
return ( return (
<li className="todo-item" key={i}> <li className="todo-item" key={i}>
<div className="todo-item__title">{todo.title}</div> <div className="todo-item__title">{todo.title}</div>
<div className="todo-item__time">{todo.time}</div> <div className="todo-item__time">
{dayjs(todo.time).format('YYYY-MM-DD')}
</div>
<div className="todo-item__buttons"> <div className="todo-item__buttons">
<button <button
className="button-text iconfont icon-setup todo-item__button" className="button-text iconfont icon-setup todo-item__button"
onClick={handleEdit.bind(this, { onClick={handleEdit.bind(this, todo)}
title: todo.title,
time: todo.time,
})}
></button> ></button>
<button <button
className="button-text iconfont icon-guanbi todo-item__button" className="button-text iconfont icon-guanbi todo-item__button"
onClick={handleDel.bind(this, i)} onClick={handleDel.bind(this, todo.id)}
></button>
<button
className="button-text iconfont icon-duihao todo-item__button"
onClick={handleClear.bind(this, todo.id)}
></button> ></button>
<button className="button-text iconfont icon-duihao todo-item__button"></button>
</div> </div>
</li> </li>
); );
@ -81,6 +84,7 @@ function Todo(props) {
{isShowForm && ( {isShowForm && (
<TodoForm <TodoForm
data={editData} data={editData}
status={status}
setIsShowForm={setIsShowForm} setIsShowForm={setIsShowForm}
submit={handleSubmit} submit={handleSubmit}
></TodoForm> ></TodoForm>

View File

@ -3,10 +3,6 @@ import ReactDOM from 'react-dom';
import './index.css'; import './index.css';
import App from './App'; import App from './App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
const { ipcRenderer } = window.electron;
console.log(1);
ipcRenderer.invoke('get-todo').then((value) => {
localStorage.setItem('todo', JSON.stringify(value));
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
@ -19,4 +15,3 @@ ipcRenderer.invoke('get-todo').then((value) => {
// to log results (for example: reportWebVitals(console.log)) // to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals(); reportWebVitals();
});

View File

@ -1,20 +0,0 @@
const Store = require('electron-store');
const store = new Store();
function initStore() {
if (!store.has('todo')) {
store.set('todo', []);
}
}
initStore();
module.exports = {
getTodo() {
return store.get('todo');
},
setTodo(value) {
console.log(store.path);
return store.set('todo', value);
},
};

29
store/index.js Normal file
View File

@ -0,0 +1,29 @@
const { ipcMain } = require('electron');
const { TodoStore } = require('./store');
const store = new TodoStore();
module.exports.install = function install() {
ipcMain.handle('get-todo', (e) => {
return store.getTodo();
});
ipcMain.handle('set-todo', (e, id, value) => {
store.setTodo(id, value);
return store.getTodo();
});
ipcMain.handle('add-todo', (e, value) => {
store.addTodo(value);
return store.getTodo();
});
ipcMain.handle('del-todo', (e, id) => {
store.delTodo(id);
return store.getTodo();
});
ipcMain.handle('clear-todo', (e, id, status) => {
store.setClear(id, status);
return store.getTodo();
});
};

102
store/store.js Normal file
View File

@ -0,0 +1,102 @@
const Store = require('electron-store');
const store = new Store();
const { isDef, isDate } = require('../utils/def');
class Todo {
constructor({ id, title, time, isClear, clearTime }) {
this.id = id;
this.title = title;
this.time = isDate(time) ? new Date(time).getTime() : Date.now();
this.isClear = isDef(isClear) ? isClear : false;
this.clearTime = isDate(clearTime) ? new Date(clearTime).getTime() : 0;
}
}
class TodoStore {
constructor() {
this.init();
}
init() {
if (!store.has('todo')) {
store.set('todo', []);
}
}
getTodo() {
return store.get('todo');
}
setTodo(id, value) {
const list = this.getTodo();
const i = list.findIndex((d) => d.id === id);
if (i > -1) {
list[i] = new Todo({
...value,
id,
});
store.set('todo', list);
return true;
} else {
return false;
}
}
addTodo(value) {
const list = this.getTodo();
const last = list[list.length - 1] || {};
const id = last.id ? `${last.id + 1}` : '1';
const newData = new Todo({
id,
...value,
});
list.push(newData);
store.set('todo', list);
return newData;
}
delTodo(id) {
const list = this.getTodo();
const i = list.findIndex((d) => d.id === id);
if (i > -1) {
list.splice(i, 1);
store.set('todo', list);
return true;
} else {
return false;
}
}
clearAll() {
store.set('todo', []);
return true;
}
setClear(id, status) {
const list = this.getTodo();
const i = list.findIndex((d) => d.id === id);
if (i > -1) {
list[i].isClear = isDef(status) ? status : !list[i].isClear;
if (list[i].isClear) {
list[i].clearTime = Date.now();
} else {
list[i].clearTime = 0;
}
store.set('todo', list);
return true;
} else {
return false;
}
}
}
module.exports.TodoStore = TodoStore;

12
utils/def.js Normal file
View File

@ -0,0 +1,12 @@
module.exports.isDef = function isDef(v) {
return v !== undefined && v !== null;
};
module.exports.isUnDef = function isUnDef(v) {
return v === undefined || v === null;
};
module.exports.isDate = function isDate(d) {
const D = new Date(d);
return !D.toString().includes('Invalid');
};