21 changed files with 528 additions and 566 deletions
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 14 KiB |
@ -1,20 +1,92 @@ |
|||||
import { ipcMain, dialog } from "electron" |
import Shared from "@main/share" |
||||
|
import setTray from "@main/facilities/tray" |
||||
|
import { app, dialog, ipcMain } from "electron" |
||||
|
import Share from "../share" |
||||
|
|
||||
/** |
/** |
||||
* 格式:@类型:扩展:函数 |
* 格式:@类型:扩展:函数 |
||||
*/ |
*/ |
||||
|
|
||||
// 保存数据
|
/** |
||||
ipcMain.on("@func:buildin:close", data => { |
* @方法:内置:设置窗口位置 |
||||
// dialog.showMessageBox(
|
*/ |
||||
// {
|
ipcMain.on("@func:buildin:setPosition", (event, x, y) => { |
||||
// type: "info",
|
Share.data.floatWindow?.setPosition(x, y) |
||||
// title: "Information",
|
}) |
||||
// defaultId: 0,
|
/** |
||||
// cancelId: 0,
|
* @方法:内置:磁盘化 |
||||
// message: "确定要关闭吗?" + data,
|
*/ |
||||
// buttons: ["没事", "最小化到托盘", "直接退出"],
|
ipcMain.on("@func:buildin:setTray", (event, x, y) => { |
||||
// },
|
if (Shared.data.trayWindow) { |
||||
// index => {}
|
Shared.data.mainWindow?.hide() // 调用 最小化实例方法
|
||||
// )
|
} else { |
||||
|
setTray(Shared.data.mainWindow) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
/** |
||||
|
* @方法:内置:退出或者磁盘化 |
||||
|
*/ |
||||
|
ipcMain.on("@func:buildin:quitOrTray", (event) => { |
||||
|
if (Shared.data.forceClose) { |
||||
|
Shared.data.mainWindow?.destroy() |
||||
|
Shared.data.mainWindow = null |
||||
|
app.quit() |
||||
|
} else if (Shared.data.mainWindow) { |
||||
|
if (Shared.data.lastChoice === 1) { |
||||
|
ipcMain.emit("@func:buildin:setTray") |
||||
|
event.preventDefault() |
||||
|
} else { |
||||
|
const choice = dialog.showMessageBoxSync(Shared.data.mainWindow, { |
||||
|
type: "info", |
||||
|
title: "Information", |
||||
|
defaultId: 0, |
||||
|
cancelId: 0, |
||||
|
message: "确定要关闭吗?", |
||||
|
buttons: ["没事", "最小化到托盘", "直接退出"] |
||||
|
}) |
||||
|
if (choice === 1) { |
||||
|
Shared.data.lastChoice = 1 |
||||
|
ipcMain.emit("@func:buildin:setTray") |
||||
|
event.preventDefault() |
||||
|
} else if (choice === 2) { |
||||
|
Shared.data.mainWindow = null |
||||
|
// app.quit()
|
||||
|
// 不要用quit();试了会弹两次
|
||||
|
Shared.data.forceClose = true |
||||
|
app.quit() // exit()直接关闭客户端,不会执行quit();
|
||||
|
} else { |
||||
|
event.preventDefault() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @方法:内置:退出 |
||||
|
*/ |
||||
|
ipcMain.on("@func:buildin:quit", (event) => { |
||||
|
if (Shared.data.forceClose) { |
||||
|
Shared.data.mainWindow = null |
||||
|
app.quit() |
||||
|
} else if (Shared.data.mainWindow) { |
||||
|
const choice = dialog.showMessageBoxSync({ |
||||
|
type: "info", |
||||
|
title: "Information", |
||||
|
defaultId: 0, |
||||
|
cancelId: 0, |
||||
|
message: "确定要关闭吗?", |
||||
|
buttons: ["没事", "直接退出"] |
||||
|
}) |
||||
|
if (choice === 1) { |
||||
|
Shared.data.mainWindow = null |
||||
|
// app.quit()
|
||||
|
// 不要用quit();试了会弹两次
|
||||
|
Shared.data.forceClose = true |
||||
|
app.quit() // exit()直接关闭客户端,不会执行quit();
|
||||
|
} else { |
||||
|
|
||||
|
} |
||||
|
} |
||||
}) |
}) |
||||
|
@ -0,0 +1,31 @@ |
|||||
|
import { app, BrowserWindow, dialog, ipcMain } from "electron" |
||||
|
import Shared from "@main/share" |
||||
|
import { getFileUrl } from "@main/util" |
||||
|
|
||||
|
function createWindow() { |
||||
|
/** |
||||
|
* Initial window options |
||||
|
*/ |
||||
|
Shared.data.mainWindow = new BrowserWindow({ |
||||
|
height: 95, |
||||
|
useContentSize: true, |
||||
|
width: 260, |
||||
|
resizable: false, |
||||
|
minWidth: 260, |
||||
|
minHeight: 95, |
||||
|
icon: __static + "/icon.png", |
||||
|
show: false, |
||||
|
frame: false, // 去除原生的菜单
|
||||
|
transparent: true, // 背景透明
|
||||
|
alwaysOnTop: true, |
||||
|
webPreferences: { |
||||
|
nodeIntegration: true, |
||||
|
contextIsolation: false |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
Shared.data.mainWindow.loadURL(getFileUrl("")) |
||||
|
Shared.data.mainWindow.on("close", (event: any) => { |
||||
|
ipcMain.emit("@func:buildin:quitOrTray") |
||||
|
}) |
||||
|
} |
@ -1,66 +0,0 @@ |
|||||
import electron from "electron" |
|
||||
|
|
||||
const BrowserWindow = electron.BrowserWindow |
|
||||
const Menu = electron.Menu |
|
||||
const app = electron.app |
|
||||
const dialog = electron.dialog |
|
||||
const ipcMain = electron.ipcMain |
|
||||
|
|
||||
let newwin:electron.BrowserWindow|null = null |
|
||||
|
|
||||
let template = [ |
|
||||
{ |
|
||||
label: "关于", |
|
||||
click: function (item:any, focusedWindow:any) { |
|
||||
// https://www.electronjs.org/docs/api/browser-window#winsetmenubarvisibilityvisible-windows-linux
|
|
||||
if (focusedWindow && !newwin) { |
|
||||
newwin = new BrowserWindow({ |
|
||||
width: 600, |
|
||||
height: 200, |
|
||||
// modal: true,
|
|
||||
show: false, |
|
||||
resizable: true, |
|
||||
parent: focusedWindow, // win是主窗口
|
|
||||
//
|
|
||||
webPreferences: { |
|
||||
// 下面两个必须这么用,看a.md的文档
|
|
||||
nodeIntegration: true, |
|
||||
contextIsolation: false, |
|
||||
// 预加载动画
|
|
||||
// preload: join(__dirname, "../../src/preload/index.js"),
|
|
||||
}, |
|
||||
}) |
|
||||
// 隐藏菜单
|
|
||||
newwin.setMenuBarVisibility(false) |
|
||||
newwin.loadURL(process.env.NODE_ENV === "development" ? `http://localhost:${process.env.PORT}/#/about` : `file://${__dirname}/index.html#/about`); |
|
||||
newwin.on("ready-to-show", () => { |
|
||||
newwin?.show() |
|
||||
}) |
|
||||
newwin.on("closed", () => { |
|
||||
newwin = null |
|
||||
}) |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
] |
|
||||
|
|
||||
// if (process.platform === 'darwin') {
|
|
||||
//
|
|
||||
// }
|
|
||||
//
|
|
||||
// if (process.platform === 'win32') {
|
|
||||
//
|
|
||||
// }
|
|
||||
app.on("ready", function () { |
|
||||
const menu = Menu.buildFromTemplate(template) |
|
||||
Menu.setApplicationMenu(menu) |
|
||||
}) |
|
||||
app.on("browser-window-created", function () { |
|
||||
// let reopenMenuItem = findReopenMenuItem()
|
|
||||
// if (reopenMenuItem) reopenMenuItem.enabled = false
|
|
||||
}) |
|
||||
app.on("window-all-closed", function () { |
|
||||
app.exit() |
|
||||
// let reopenMenuItem = findReopenMenuItem()
|
|
||||
// if (reopenMenuItem) reopenMenuItem.enabled = true
|
|
||||
}) |
|
@ -1,189 +0,0 @@ |
|||||
// const path = require('path')
|
|
||||
import { BrowserWindow } from "electron" |
|
||||
import Shared from "./share" |
|
||||
import { getFileUrl } from "./util" |
|
||||
const electron = require("electron") |
|
||||
const setTray = require("./disk").default |
|
||||
// const BrowserWindow = electron.BrowserWindow
|
|
||||
const Menu = electron.Menu |
|
||||
const app = electron.app |
|
||||
const dialog = electron.dialog |
|
||||
const ipcMain = electron.ipcMain |
|
||||
|
|
||||
let newwin: BrowserWindow | null |
|
||||
|
|
||||
let template = [ |
|
||||
{ |
|
||||
label: "选择保存目录", |
|
||||
click: function (item: any, focusedWindow: BrowserWindow) { |
|
||||
dialog |
|
||||
.showOpenDialog(focusedWindow, { |
|
||||
properties: ["openDirectory"], |
|
||||
}) |
|
||||
.then(result => { |
|
||||
ipcMain.emit("@menu:selectDir", result) |
|
||||
}) |
|
||||
.catch(err => { |
|
||||
throw err |
|
||||
}) |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
label: "置顶", |
|
||||
key: "alwaysTop", |
|
||||
click: function (item: any, focusedWindow: BrowserWindow) { |
|
||||
if (Shared.data.mainWindow.isAlwaysOnTop()) { |
|
||||
Shared.data.mainWindow.setAlwaysOnTop(false) |
|
||||
} else { |
|
||||
Shared.data.mainWindow.setAlwaysOnTop(true) |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
label: "重载", |
|
||||
accelerator: "CmdOrCtrl+R", |
|
||||
click: function (item: any, focusedWindow: BrowserWindow) { |
|
||||
if (focusedWindow) { |
|
||||
// 重载之后, 刷新并关闭所有的次要窗体
|
|
||||
if (focusedWindow.id === 1) { |
|
||||
BrowserWindow.getAllWindows().forEach(function (win) { |
|
||||
if (win.id > 1) { |
|
||||
win.close() |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
focusedWindow.reload() |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
label: "功能", |
|
||||
submenu: [ |
|
||||
{ |
|
||||
label: "悬浮窗", |
|
||||
click: function (item: any, focusedWindow: BrowserWindow) { |
|
||||
ipcMain.emit("showSuspensionWindow") |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
label: "最小化到托盘", |
|
||||
click: function (item: any, focusedWindow: BrowserWindow) { |
|
||||
Shared.data.lastChoice = 1 |
|
||||
if (Shared.data.miniWindow) { |
|
||||
Shared.data.mainWindow.hide() // 调用 最小化实例方法
|
|
||||
} else { |
|
||||
setTray(app, Shared.data.mainWindow) |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
{ |
|
||||
label: "切换全屏", |
|
||||
accelerator: (function () { |
|
||||
if (process.platform === "darwin") { |
|
||||
return "Ctrl+Command+F" |
|
||||
} else { |
|
||||
return "F11" |
|
||||
} |
|
||||
})(), |
|
||||
click: function (item: any, focusedWindow: BrowserWindow) { |
|
||||
if (focusedWindow) { |
|
||||
focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
], |
|
||||
}, |
|
||||
{ |
|
||||
label: "开发者", |
|
||||
submenu: [ |
|
||||
{ |
|
||||
label: "切换开发者工具", |
|
||||
accelerator: (function () { |
|
||||
if (process.platform === "darwin") { |
|
||||
return "Alt+Command+I" |
|
||||
} else { |
|
||||
return "Ctrl+Shift+I" |
|
||||
} |
|
||||
})(), |
|
||||
click: function (item: any, focusedWindow: BrowserWindow) { |
|
||||
if (focusedWindow) { |
|
||||
// @ts-ignore
|
|
||||
focusedWindow.toggleDevTools() |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
], |
|
||||
}, |
|
||||
// {
|
|
||||
// label: '重新启动',
|
|
||||
// click: function(item, focusedWindow) {
|
|
||||
// app.exit()
|
|
||||
// app.relaunch()
|
|
||||
// // app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) })
|
|
||||
// // app.quit()
|
|
||||
// }
|
|
||||
// },
|
|
||||
{ |
|
||||
label: "关于", |
|
||||
click: function (item: any, focusedWindow: BrowserWindow) { |
|
||||
// https://www.electronjs.org/docs/api/browser-window#winsetmenubarvisibilityvisible-windows-linux
|
|
||||
if (focusedWindow && !newwin) { |
|
||||
newwin = new BrowserWindow({ |
|
||||
width: 600, |
|
||||
height: 200, |
|
||||
minimizable: false, |
|
||||
darkTheme: true, |
|
||||
modal: true, |
|
||||
show: false, |
|
||||
resizable: false, |
|
||||
webPreferences: { |
|
||||
nodeIntegration: true, |
|
||||
contextIsolation: false, |
|
||||
}, |
|
||||
// parent: focusedWindow // win是主窗口
|
|
||||
}) |
|
||||
// 隐藏菜单
|
|
||||
newwin.setMenuBarVisibility(false) |
|
||||
// vue是单页面,需要改成多页面才行
|
|
||||
newwin.loadURL(getFileUrl("about")) |
|
||||
newwin.on("ready-to-show", () => { |
|
||||
newwin?.show() |
|
||||
}) |
|
||||
newwin.on("close", () => { |
|
||||
newwin = null |
|
||||
}) |
|
||||
} |
|
||||
}, |
|
||||
}, |
|
||||
] |
|
||||
// function findTopItem() {
|
|
||||
// const menu = Menu.getApplicationMenu()
|
|
||||
// if (!menu) return
|
|
||||
// let reopenMenuItem
|
|
||||
// menu.items.forEach(function(item) {
|
|
||||
// if (item.key === 'alwaysTop') {
|
|
||||
// reopenMenuItem = item
|
|
||||
// }
|
|
||||
// // if (item.submenu) {
|
|
||||
// // item.submenu.items.forEach(function(item) {
|
|
||||
// // if (item.key === 'alwaysTop') {
|
|
||||
// // reopenMenuItem = item
|
|
||||
// // }
|
|
||||
// // })
|
|
||||
// // }
|
|
||||
// })
|
|
||||
// console.log(reopenMenuItem)
|
|
||||
// return reopenMenuItem
|
|
||||
// }
|
|
||||
app.on("ready", function () { |
|
||||
const menu = Menu.buildFromTemplate(<any>template) |
|
||||
Menu.setApplicationMenu(menu) |
|
||||
}) |
|
||||
app.on("browser-window-created", function () { |
|
||||
// let reopenMenuItem = findReopenMenuItem()
|
|
||||
// if (reopenMenuItem) reopenMenuItem.enabled = false
|
|
||||
}) |
|
||||
app.on("window-all-closed", function () { |
|
||||
// let reopenMenuItem = findReopenMenuItem()
|
|
||||
// if (reopenMenuItem) reopenMenuItem.enabled = true
|
|
||||
}) |
|
@ -1,12 +1,25 @@ |
|||||
|
import { BrowserWindow, Tray } from "electron" |
||||
|
|
||||
interface IPayload{ |
interface IPayload{ |
||||
data: { |
data: { |
||||
|
mainWindow: BrowserWindow|null; |
||||
|
floatWindow: BrowserWindow|null; |
||||
|
trayWindow: Tray|null; |
||||
|
forceClose: boolean; |
||||
|
lastChoice: number; |
||||
[propName:string]: any; |
[propName:string]: any; |
||||
}; |
}; |
||||
[propName:string]: any; |
[propName:string]: any; |
||||
} |
} |
||||
|
|
||||
const payload:IPayload = { |
const payload:IPayload = { |
||||
data: {} |
data: { |
||||
|
mainWindow: null, // 主窗口
|
||||
|
floatWindow: null, // 浮动窗口
|
||||
|
trayWindow: null, |
||||
|
forceClose: false, |
||||
|
lastChoice: -1 // 做过的选择
|
||||
|
} |
||||
} |
} |
||||
|
|
||||
export default payload |
export default payload |
@ -0,0 +1,14 @@ |
|||||
|
.clock { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
transform: translate(-50% -50%); |
||||
|
font-size: 40px; |
||||
|
font-weight: bolder; |
||||
|
text-align: center; |
||||
|
line-height: 1.3; |
||||
|
color: #CD1110; |
||||
|
font-family: "pixi"; |
||||
|
user-select: none; |
||||
|
pointer-events: none; |
||||
|
} |
@ -1,49 +1,38 @@ |
|||||
import { addTodo, removeTodo } from "@/store/action/todo" |
import { addTodo, removeTodo } from "@/store/action/todo" |
||||
import React, { FormEvent, useRef, useContext } from "react" |
import React from "react" |
||||
import { connect } from "react-redux" |
import { connect } from "react-redux" |
||||
import { NavLink } from "react-router-dom" |
import style from "./index.module.scss" |
||||
|
import useTime from "./useTime" |
||||
|
import usePositionElectron from "./usePositionElectron" |
||||
|
|
||||
export interface HomeProps { |
export interface HomeProps { |
||||
add(text: string): void |
add(text: string): void |
||||
|
|
||||
todo: ITodo[] |
todo: ITodo[] |
||||
|
|
||||
remove(id: number): void |
remove(id: number): void |
||||
} |
} |
||||
|
|
||||
function Home(props: HomeProps) { |
function Home(props: HomeProps) { |
||||
const { todo, add, remove } = props |
let [nowDate] = useTime() |
||||
const inputRef = useRef<HTMLInputElement>(null) |
usePositionElectron() |
||||
|
|
||||
function addOne(e: FormEvent) { |
|
||||
e.preventDefault() |
|
||||
let text = inputRef.current!.value |
|
||||
if (text) { |
|
||||
inputRef.current!.value = "" |
|
||||
add(text) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
return ( |
||||
<div> |
<div className={style.clock}> |
||||
<NavLink to="/home">首页</NavLink> <br /> |
<div>{nowDate.year}-{nowDate.month}-{nowDate.day}</div> |
||||
<NavLink to="/about">关于</NavLink><br /> |
<div>{nowDate.hour}:{nowDate.minute}:{nowDate.second}</div> |
||||
<NavLink to="/login">登录</NavLink><br /> |
|
||||
<NavLink to="/float">Float</NavLink><br /> |
|
||||
<div> |
|
||||
<img src='__static/icon.png' style={{width:"50px",height: "50px"}} alt=""/> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
) |
) |
||||
} |
} |
||||
|
|
||||
const mapStateToProps = (state: any) => { |
const mapStateToProps = (state: any) => { |
||||
return { |
return { |
||||
todo: state.todo, |
todo: state.todo |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
const mapDispatchToProps = (dispatch: any) => ({ |
const mapDispatchToProps = (dispatch: any) => ({ |
||||
add: (text: string) => dispatch(addTodo(text)), |
add: (text: string) => dispatch(addTodo(text)), |
||||
remove: (id: string | number) => dispatch(removeTodo(id)), |
remove: (id: string | number) => dispatch(removeTodo(id)) |
||||
}) |
}) |
||||
|
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Home) |
export default connect(mapStateToProps, mapDispatchToProps)(Home) |
||||
|
@ -0,0 +1,32 @@ |
|||||
|
import electron from "@/plugins/electron" |
||||
|
import { useEffect } from "react" |
||||
|
|
||||
|
|
||||
|
export default function() { |
||||
|
useEffect(() => { |
||||
|
let biasX = 0 |
||||
|
let biasY = 0 |
||||
|
document.addEventListener("mousedown", function(e) { |
||||
|
switch (e.button) { |
||||
|
case 0: |
||||
|
biasX = e.x |
||||
|
biasY = e.y |
||||
|
document.addEventListener("mousemove", moveEvent) |
||||
|
break |
||||
|
case 2: |
||||
|
electron.ipcRenderer.send("createSuspensionMenu") |
||||
|
break |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
document.addEventListener("mouseup", function() { |
||||
|
biasX = 0 |
||||
|
biasY = 0 |
||||
|
document.removeEventListener("mousemove", moveEvent) |
||||
|
}) |
||||
|
|
||||
|
function moveEvent(e: any) { |
||||
|
electron.ipcRenderer.send("@func:buildin:setPosition", e.screenX - biasX, e.screenY - biasY) |
||||
|
} |
||||
|
}, []) |
||||
|
} |
@ -0,0 +1,55 @@ |
|||||
|
import { Dispatch, SetStateAction, useEffect, useState } from "react" |
||||
|
|
||||
|
type ITime<T = string | number> = { |
||||
|
year?: T; |
||||
|
month?: T; |
||||
|
day?: T; |
||||
|
hour?: T; |
||||
|
minute?: T; |
||||
|
second?: T; |
||||
|
} |
||||
|
|
||||
|
function isLow10(value:string | number) { |
||||
|
if (+value< 10){ |
||||
|
return "0"+value |
||||
|
} |
||||
|
return value |
||||
|
} |
||||
|
|
||||
|
|
||||
|
export default function(isUpdate = true): [ITime, Dispatch<SetStateAction<ITime>>] { |
||||
|
let [nowDate, setNowDate] = useState<ITime>({}) |
||||
|
|
||||
|
function updateTime() { |
||||
|
let date: ITime = {} |
||||
|
let newDate = new Date() |
||||
|
date.year = isLow10(newDate.getFullYear()) |
||||
|
date.month = isLow10(newDate.getMonth() + 1) |
||||
|
date.day = isLow10(newDate.getDate()) |
||||
|
date.hour = isLow10(newDate.getHours()) |
||||
|
date.minute = isLow10(newDate.getMinutes()) |
||||
|
date.second = isLow10(newDate.getSeconds()) |
||||
|
setNowDate(date) |
||||
|
return newDate.getMilliseconds() |
||||
|
} |
||||
|
|
||||
|
if (isUpdate) { |
||||
|
useEffect(() => { |
||||
|
|
||||
|
function cicleCall(millis: number): NodeJS.Timeout { |
||||
|
let timeID = setTimeout(() => { |
||||
|
let millis = 1000 - updateTime() |
||||
|
cicleCall(millis) |
||||
|
}, millis) |
||||
|
return timeID |
||||
|
} |
||||
|
|
||||
|
let millis = 1000 - updateTime() |
||||
|
let timeID = cicleCall(millis) |
||||
|
return () => { |
||||
|
clearTimeout(timeID) |
||||
|
} |
||||
|
}, []) |
||||
|
} |
||||
|
return [nowDate, setNowDate] |
||||
|
} |
Loading…
Reference in new issue