first commit

This commit is contained in:
编码猿
2024-09-27 01:23:51 +08:00
commit 72ab70b6fd
212 changed files with 30296 additions and 0 deletions

1
src/core/.pydio Normal file
View File

@@ -0,0 +1 @@
85ed9a37-2367-442e-ad9b-0524edf9f844

View File

@@ -0,0 +1 @@
d087137f-1bfb-4d06-96ef-473d70be7e66

View File

@@ -0,0 +1,63 @@
import Config from "@config/Index.config";
import Utils from "@utils/Index.utils";
import {Http} from "@net/Http.net";
/**
* @Ipc()
* 实现居中IPC的注册用在controller方法上
* @param IpcParams 传入需要被注册的ipc类
* @constructor
*/
export function Ipc(IpcParams: { new (...args: any[]): {}; }[] ) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
/**
* 因为TS没有提供像java那种可以直接提取某个类中所有方法的系统方法
* 这边原本使用 Object.getOwnPropertyNames 能够正常提取类中方法,但是如果类中有被 @Inject() 依赖注入的属性
* 那么这个属性也会被 (new IpcParams())[val](),这样是肯定报错的,所以做下面约定!
* 在IPC 类中,如果在类的属性上使用 @Inject() 注入。那么请在类的属性名前加上 _例如:
* @Inject()
* private readonly _Net!: Http;
*/
IpcParams.forEach((val: { new (): any; } ) => {
Object.getOwnPropertyNames(val.prototype).splice(1)
.filter(f => !f.includes("_"))
.forEach(v => (new val())[v]())
})
}
}
/**
* @CreateApplicationIpc()
* 实现全局IPC的注册
* 能够自动实例化传入的类数组中的所有类,并自动调用类中的所有的方法
* @param IpcParams 类数组,实例:[ classIpc1, classIpc2, classIpc3 ]
* @constructor
*/
export function CreateApplicationIpc(IpcParams: { new (...args: any[]): {}; }[]): any {
return (_constructor: { new (...args: any[]): {}; } ) => {
IpcParams.forEach((val: { new (): any; } ) => {
Object.getOwnPropertyNames(val.prototype).splice(1)
.filter(f => !f.includes("_"))
.forEach(v => (new val())[v]())
})
}
}
/**
* @Render()
* 创建窗口
* @param templateName 要渲染的模板名称
* @constructor
*/
export function Render(templateName?: string) {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
/**
* 如果被打上注解的类中某个方法是配置文件中的默认启动窗口,那么调用这个方法来创建窗体
* 其他的方法一律不管由js前端渲染进程触发ipc创建
*/
if (propertyKey.includes((Config.StartPage.split("/"))[1])) {
// 如果没有传参数,那么采用方法名来创建窗体!
templateName == undefined ? Utils.startWindows(target,propertyKey) : Utils.startWindows(target,templateName)
}
}
}

View File

@@ -0,0 +1,40 @@
import Config from "@config/Index.config";
import {Http} from "@net/Http.net";
/**
* @GET()
* 用在service类的方法上。
* 如果不传参数将自动根据方法名获取接口请求的地址
* 如果传参了,那么使用用户传入的地址去发送网络请求
*
* 如果 useHandle 为 true 那么方法将获得ajax请求的数据
* 否则 不会将数据给方法
* @param RequestParams
* @constructor
*/
export function GET (RequestParams?: { url?: string, useHandle?: boolean, header?: { [index: string]: any } }) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
let url: string = Config.ApiUrl.ApiList[propertyKey],
handle: boolean = false,
header: object = {};
(RequestParams && RequestParams.url)
? url = RequestParams.url
: (RequestParams && RequestParams.useHandle)
? handle = RequestParams.useHandle
: (RequestParams && RequestParams.header)
? header = RequestParams.header
: '';
let OldMethods = descriptor.value;
if (handle) {
descriptor.value = async (data: { [key:string]: any }) =>
await OldMethods.apply(target, [ await (new Http).GET({ url: url, data: data, header: header }) ])
return descriptor
} else {
descriptor.value = async (data: { [key:string]: any }) =>
await (new Http).GET({ url: url, data: data, header: header })
return descriptor
}
}
}

View File

@@ -0,0 +1,39 @@
import 'reflect-metadata';
import IocModel from "@model/Ioc.model";
/**
* 收集类依赖
* @constructor
*/
export function Injectable() {
return (_constructor: { new (...args: any[]): {}; }) => {
if(IocModel.classPool.indexOf(_constructor) !== -1) {
throw new Error('无需重复收集类');
} else {
//注册
IocModel.classPool = [_constructor]
}
}
}
/**
* 将类依赖实例化然后注入到被装饰的属性中
* @constructor
*/
export function Inject() {
return function (target: any, propertyName: string) {
/**
* 使用 reflect-metadata 提供的内置 类型元数据键 design:type 通过反射拿到被装饰属性的类型
* 也就是 类属性要实例化 的 service 类
*/
const propertyType: any = Reflect.getMetadata('design:type', target, propertyName);
console.log("类属性要实例化 propertyType: ", propertyType)
if (IocModel.classPool.indexOf(propertyType) == -1) {
throw new Error('被装饰的属性所属的变量类型类,没有被装饰器@Injectable()注入,请检查!');
} else {
// 从存取器的数组中通过下标取出被装饰属性对应的service类然后实例化这个类在放入被装饰的属性中
target[propertyName] = new (IocModel.classPool[IocModel.classPool.indexOf(propertyType)])()
}
}
}

View File

@@ -0,0 +1,80 @@
import Utils from "@utils/Index.utils";
import {ApplicationMenu} from "@interactive/ApplicationMenu.interactive";
import {Touchbar} from "@interactive/Touchbar.interactive";
import {TrayInteractive} from "@interactive/Tray.interactive";
import { Application } from "@ipc/Application.ipc";
export function AutoLoadWindow(): any {
return (_constructor: {new(...args:any[]):{}} ) => {
return class extends _constructor {
constructor() {
// 这里只要动态 require 导入类即可,然后类上的装饰器就会运行
Utils.GetController()
super();
}
}
}
}
export function CreateApplicationMenu(): any {
return (_constructor: {new(...args:any[]):{}}) => {
return class extends _constructor {
constructor() {
// 创建顶部菜单
new ApplicationMenu()
super();
}
}
}
}
export function CreateTouchbar(): any {
return (_constructor: {new(...args:any[]):{}}) => {
return class extends _constructor {
constructor() {
/**
* 创建Touchbar
* 这边做延时300毫秒的原因解释起来有点复杂
* 1因为我设计了 @Inject() 和 @Injectable() 注解,而在 controller 中
* 被注解的属性 VideoImplService 存放的是 请求中间层而请求中间层是调用的Http请求方法GET而这个方法
* 为了拿到返回值所以设计成async异步的这就导致使用请求中间层的controller方法也是async。
* 2而使用的方法如果是 async 那么就会导致 注解 @Render() 内部 也就是 startWindows 方法内,在自动调用方法
* 获取传递给模板的方法返回数值的时候必须 await也就变成了await target[name]()。
* 而这个注解 @CreateTouchbar() 运行时间 和 注解 @AutoLoadWindow() 基本同时间跑,
* 所以会导致 @CreateTouchbar() 运行的时候太快了,尼玛币 await target[name]() 还没跑完,所以导致
* 存取器里面Windows.CurrentBrowserWindow 没有数值,所以 @CreateTouchbar() 所依赖的 Windows.CurrentBrowserWindow
* 为空所以touchbar菜单无法 setTouchBar 上。
* 3这里等300毫秒是等 await target[name]() 完成,窗口也创建完成,然后 Windows.CurrentBrowserWindow
* 再去创建 touchbar菜单。
* 好了,说完了....同学不要睡了!
*/
setTimeout(()=> new Touchbar(),3000)
super();
}
}
}
}
export function CreateTray(): any {
return (_constructor: {new(...args:any[]):{}}) => {
return class extends _constructor {
constructor() {
// 创建Mac系统顶部图标
new TrayInteractive()
super();
}
}
}
}
// export function CreateApplicationIpc(): any {
// return (_constructor: {new(...args:any[]):{}}) => {
// return class extends _constructor {
// constructor() {
// // 开启应用级ipc监听
// new Application()
// super();
// }
// }
// }
// }

1
src/core/config/.pydio Normal file
View File

@@ -0,0 +1 @@
b9f49d6d-a018-4c87-8f77-433ade2c60aa

View File

@@ -0,0 +1,226 @@
import { PagePath, PageSize } from "@type/PageConfig.types";
import { TrayConfig } from "@type/Tray.types";
import { ApiUrlConfig } from "@type/ApiConfig";
import { app, Menu, shell, MenuItemConstructorOptions, MenuItem, TouchBarConstructorOptions, TouchBar } from "electron";
import Windows from "@/core/model/Windows.model";
const { TouchBarLabel, TouchBarButton,TouchBarSpacer } = TouchBar;
import { join } from "path";
import { JadeOptions } from "jade";
export default class Config {
/**
* 启动页面默认为 Home.controller/Home
* 程序内部根据这个配置自动载入Home.controller.ts文件并自动调用类里面的Home()方法实现窗口创建
* 可以改成其他,例如 PlayVideo.controller/Theme
*/
public static StartPage: string = 'Home.controller/Home';
// 应用版本号
public static CurrentAppVersion: String = '0.0.1';
// 是否为Mac系统
public static isMac: Boolean = process.platform === 'darwin';
public static LogsPath: string = "../../../logs/";
// ajax 请求地址
public static ApiUrl: ApiUrlConfig = {
BaseUrl: 'http://www.bmycode.com:3000/api',
ApiList: {
PlayList: '/list', // 获取视频列表
PalyVideo: '/play', // 获取视频播放地址
VideoComment: '/CommentList', // 获取视频评论
}
};
// jade 模板引擎配置,更多参数自行阅读声明文件
public static jadeCompile0ptions: JadeOptions = {
pretty: true, // 编译输出后是否保留源码格式true 保持
globals: {
css: [
'http://mdui-aliyun.cdn.w3cbus.com/source/dist/css/mdui.min.css',
'http://at.alicdn.com/t/font_1934749_hhc110df87a.css',
],
js: [
'http://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js',
'http://mdui-aliyun.cdn.w3cbus.com/source/dist/js/mdui.min.js'
]
}
};
// 应用程序的页面地址,地址为打包后的相对路径
public static PagePath: PagePath = {
Home: join(__dirname, '../../application/page/Home/Home.html'),
PlayVideo: join(__dirname, '../../application/page/PlayVideo/PlayVideo.html'),
};
// 所有页面窗体配置
public static PageSize: PageSize = {
Home: {
width: 1200,
height: 760,
frame: false,
backgroundColor: '#fff',
titleBarStyle: 'hiddenInset',
transparent: true,
webPreferences: {
webSecurity: false,
nodeIntegration: true,
webviewTag: true,
}
},
PlayVideo: {
width: 1084,
height: 610,
frame: false,
backgroundColor: '#000',
titleBarStyle: 'hidden',
modal: true,
show: false,
resizable: true,
transparent: true,
webPreferences: {
webSecurity: false,
nodeIntegration: true,
webviewTag: true
}
}
};
// Mac系统顶部全局菜单 右边图标
public static TrayConfig: TrayConfig = {
TopMenuRightImage: join(__dirname, '../../application/assets/img/pug.png'),
TopMenuRightDropdown:[
{
label: '显示主窗口',
click: ()=> {
Windows.CurrentBrowserWindow.show();
}
},
{
icon: join(__dirname, '../../application/assets/img/pug.png'),
label: '下拉菜单测试',
type: 'checkbox',
checked: true,
click: (menuItem: any, browserWindow: any)=> {
console.log("menuItem: ", menuItem);
}
},
{
label: '菜单',
submenu: [
{
label: '子菜单1'
},
{
label: '子菜单2'
}
],
},
{
role: 'quit',
label: '退出'
},
],
TopMenuRightTips: '测试提醒'
};
// Mac or win 系统顶部全局菜单
public static TemplateMenu: Array<(MenuItemConstructorOptions) | (MenuItem)>= [
{
label: app.name,
submenu: [
{ label: `关于 ${app.name}`, role: 'about' },
{ type: 'separator' },
{ label: '服务', role: 'services' },
{ type: 'separator' },
{ label: `隐藏 ${app.name}`, role: 'hide' },
{ role: 'hideOthers' },
{ label: '隐藏其他', role: 'unhide' },
{ type: 'separator' },
{ label: `退出${app.name}`, role: 'quit' }
]
},
{
label: '文件',
},
{
label: '编辑',
submenu: [
{ label: '撤销', role: 'undo' },
{ label: '恢复', role: 'redo' },
{type: 'separator' },
{ label: '剪切', role: 'cut' },
{ label: '复制', role: 'copy' },
{ label: '粘贴', role: 'paste' },
{type: 'separator' },
{ label: '粘贴保留样式', role: 'pasteAndMatchStyle' },
{ label: '删除', role: 'delete' },
{ label: '全选', role: 'selectAll' },
{ type: 'separator' },
{
label: '听写',
submenu: [
{ label: '开始听写', role: 'startSpeaking' },
{ label: '停止听写', role: 'stopSpeaking' }
]
}
]
},
{
label: '视图',
submenu: [
{ label: '刷新', role: 'reload' },
{ label: '重置', role: 'resetZoom' },
{ label: '放大', role: 'zoomIn' },
{ label: '缩小', role: 'zoomOut' },
{ type: 'separator' },
{ label: '全屏', role: 'togglefullscreen' },
{ label: '切换开发人员工具', role: 'toggleDevTools' },
]
},
{
label: '窗口',
submenu: [
{ label: '最小化', role: 'minimize' },
{ label: '最大化',role: 'zoom' },
{ label: '关闭', role: 'close' }
]
},
{
label: '帮助',
role: 'help',
submenu: [
{
label: '了解更多',
click: async () => {
await shell.openExternal('https://electronjs.org')
}
},
{
label: 'GitHub主页',
click: async () => {
}
}
]
}
];
// Mac touchbar 菜单
public static TouchBarConfig: TouchBarConstructorOptions = {
items: [
new TouchBarSpacer({ size: 'small' }),
new TouchBarButton({
label: '测试1',
backgroundColor: '#3a3a3c',
click: () => {}
}),
new TouchBarSpacer({ size: 'small' }),
new TouchBarButton({
label: '测试2',
backgroundColor: '#3a3a3c',
click: () => {}
}),
]
};
}

View File

@@ -0,0 +1 @@
1352edee-3a21-42cf-bf29-5ca3b2846b11

View File

@@ -0,0 +1,47 @@
import { Render, Ipc } from "@annotation/Creted.annotation";
import { VideoImplService } from "@service/impl/Video.impl.service";
import { Inject } from "@annotation/Ioc.annotation";
import { FileIpc } from "@ipc/module/File.ipc";
export class HomeController {
@Inject()
public readonly VideoImplService!: VideoImplService;
@Render()
public async Home(test: string) {
let res = await this.VideoImplService.PlayList({});
return {
header: true,
nav: true,
title: '首页页面窗口-测试传给模板的数据',
desc: `点我发送ipc打开窗口代码实现分别在src/application/assets/js/page/Home.js
和 src/core/ipc/Application.ipc.ts 和 @CreateApplicationIpc 装饰器中!!!`,
list: res.data
};
}
@Render()
@Ipc([ FileIpc ])
// 注意 PlayVideo() 方法将被前端发起的全局ipc自动调用ipc内部会根据这个方法名去自动创建同名的 视频播放窗口,
// 并IPC会自动动调用这个 PlayVideo() 方法,并把前端的参数向传入方法的 params方法内部无脑的去用即可
public async PlayVideo(params?: { [key:string]:any } ) {
console.log("参数:", params)
if ((params as object).hasOwnProperty("list")) {
return { // 用户点击图片组
header: false,
nav: false,
img: true,
video: params
}
} else {
return { // 用户点击视频
header: false,
nav: false,
img: false,
video: await this.VideoImplService.PalyVideo(params),
Comment: await this.VideoImplService.VideoComment({ photoId: (params as any).photoId })
}
}
}
}

View File

@@ -0,0 +1,27 @@
import { BaseControllerTypes } from "@type/BaseController.types";
import { Render, Ipc } from "@annotation/Creted.annotation";
import {FileIpc} from "@ipc/module/File.ipc";
/**
* 首页窗体
* @Render() 注解:
* 用在类的方法上,注意方法名 和 页面模板名称一致。
* 作用:表示使用该方法创建页面窗口,方法返回的数据需要是个对象,返回的数据会传给模板
* 用法:
* 1@Render()不传参数会根据方法名自动载入对应的jade页面文件来创建窗口
* 2@Render('PlayVideo')传入参数会根据传入的参数去寻找对应的jade页面来创建窗口
* 注意:传入参数后不仅会使用自定义页面,还会抛弃配置文件中的 StartPage 主窗窗口配置
*
* @Ipc() 注解:
* 用在类的方法上,接受一个未实例化的类作为参数
* 作用Ipc注解会自动运行这个类里面的所有方法请在方法中放置 ipcMain.on 代码!
*/
export class SettingController {
@Render()
public Setting() {
return {
title: '设置页面窗口-测试传给模板的数据',
desc: '介绍'
};
}
}

View File

@@ -0,0 +1 @@
ca1b2ba3-e8cd-4c29-9ca1-f27abf9f8252

View File

@@ -0,0 +1,16 @@
import {app, Menu, shell, MenuItemConstructorOptions, MenuItem} from "electron";
import Config from "@config/Index.config";
export class ApplicationMenu {
private AppMenu!: Menu;
constructor() {
this.buildFromTemplate();
}
buildFromTemplate(): void {
this.AppMenu = Menu.buildFromTemplate(Config.TemplateMenu);
Menu.setApplicationMenu(this.AppMenu);
}
}

View File

@@ -0,0 +1,33 @@
import { dialog, Notification } from "electron";
import Windows from "@model/Windows.model"
export class Dialog {
/**
* 错误弹窗
* @param title
* @param content
* @constructor
*/
public static async ErrorBox(title: string, content: string): Promise<void> {
await dialog.showErrorBox(title, content)
}
public static async showSaveDialog(title: string, message: string): Promise<any> {
let SaveFile = await dialog.showOpenDialog(Windows.CurrentBrowserWindow, {
title: title,
message: message,
buttonLabel: '亲,点我确认选择!',
filters: [
{ name: 'All', extensions: ['*'] },
],
properties: [
'openDirectory',
'createDirectory'
]
});
if(!SaveFile.canceled) {
return SaveFile.filePaths
}
}
}

View File

@@ -0,0 +1,10 @@
import { app, BrowserWindow, TouchBar, dialog, shell } from "electron";
import Windows from "@/core/model/Windows.model";
import Config from "@config/Index.config";
const { TouchBarLabel, TouchBarButton,TouchBarSpacer } = TouchBar;
export class Touchbar {
constructor() {
Windows.CurrentBrowserWindow.setTouchBar(new TouchBar(Config.TouchBarConfig))
}
}

View File

@@ -0,0 +1,38 @@
import { nativeImage, NativeImage, Menu, Tray } from "electron";
import Config from "@config/Index.config";
export class TrayInteractive {
private tray!: Tray;
constructor() {
this.tray = new Tray(this.setTemplateImage());
this.buildTrayMenu()
this.TrayItemClick()
}
/**
* 全局菜单图标被点击时触发事件
* @constructor
*/
private TrayItemClick() {
this.tray.on('click', () => {
console.log("按钮被点击");
})
}
private buildTrayMenu() {
let contextMenu: Menu = Menu.buildFromTemplate(Config.TrayConfig.TopMenuRightDropdown);
this.tray.setToolTip(Config.TrayConfig.TopMenuRightTips)
this.tray.setContextMenu(contextMenu)
}
/**
* 创建 NativeImage 图片图片会随着Mac系统主题的切换而自定变纯黑或纯白
*/
private setTemplateImage(): NativeImage {
let image: NativeImage = nativeImage.createFromPath(Config.TrayConfig.TopMenuRightImage);
image.setTemplateImage(true)
return image
}
}

View File

1
src/core/ipc/.pydio Normal file
View File

@@ -0,0 +1 @@
cbaff76c-a698-40e1-916a-489df3b0fc0f

View File

@@ -0,0 +1,52 @@
import {ipcMain, shell} from "electron";
import Utils from "@utils/Index.utils";
import Windows from "@model/Windows.model";
import Config from "@config/Index.config";
import {PageSize} from "@type/PageConfig.types";
export class Application {
/**
* 创建非主窗体之外的窗体,
* 在js渲染进程中触发事件
* 1独立窗体
* 2依附在主窗体上的子窗体
* 3依附在独立窗体上的子窗体
* @constructor
*/
CreatedWindow() {
ipcMain.on('openWindow', async (event, arg: any) => {
let ControllerName: string = arg.action.split("/")[0],
MethodsName: string = arg.action.split("/")[1],
data: object = arg.data || {}
// parent false 创建单独的窗体
if (!arg.parent || !arg.hasOwnProperty("parent")) {
let a = require(`../controller/${ControllerName}.js`);
Utils.startWindows(new a[Utils.toUpperCase(ControllerName)](), MethodsName, data)
return false
}
// parent true 创建默认依附主窗口的子窗口
if (arg.hasOwnProperty("parent") || arg.parent) {
// @ts-ignore 合并配置参数,注入 parent: 当前默认启动的父类窗口实例
Object.assign(Config.PageSize[MethodsName], { parent: Windows.CurrentBrowserWindow });
// 实例化子窗口类,创建窗口
let a = require(`../controller/${ControllerName}.js`);
Utils.startWindows(new a[Utils.toUpperCase(ControllerName)](), MethodsName, data)
return false
// 创建默认依附在自定义窗口的子窗口
} else {
console.log("创建默认依附在自定义窗口的子窗口")
}
})
}
/**
* 打开浏览器窗口
*/
shellWindows() {
ipcMain.on("openExternal", async (event, arg:any) => {
await shell.openExternal(arg.url)
});
}
}

View File

@@ -0,0 +1 @@
9f151eb6-361c-4604-80e5-4aacdb861429

View File

@@ -0,0 +1,20 @@
import { ipcMain } from "electron";
import { Dialog } from "@interactive/Dialog.interactive";
import {Inject, Injectable } from "@annotation/Ioc.annotation";
import { Http } from "@net/Http.net";
import Utils from "@utils/Index.utils";
export class FileIpc {
@Inject()
private readonly _Net!: Http;
openFile() {
ipcMain.on('SaveFile', async (event, arg) => {
let SaveFilePath = await Dialog.showSaveDialog("选择路径","选择下载路径");
if (SaveFilePath != undefined) {
Utils.DownFile(arg.url,SaveFilePath)
}
})
}
}

1
src/core/model/.pydio Normal file
View File

@@ -0,0 +1 @@
0afe9a3d-f1e9-43e0-975d-c298d3d7f242

View File

@@ -0,0 +1,13 @@
class IocModel {
private _classPool: Array<{ new (...args: any[]): {}; }> = [];
get classPool(): Array<{ new(...args: any[]): {} }> {
return this._classPool;
}
set classPool(value: Array<{ new(...args: any[]): {} }>) {
this._classPool = [...value, ...this._classPool]
}
}
export default new IocModel();

View File

@@ -0,0 +1,65 @@
import { WebContents, BrowserWindow } from "electron";
class Windows {
private _HomeBrowserWindow!: BrowserWindow;
private _CurrentBrowserWindow!: BrowserWindow;
private _SettingBrowserWindow!: BrowserWindow;
private _CurrentWindowNew!: any;
private _HomeBrowserWindowWebContents!: typeof WebContents;
private _SettingBrowserWindowWebContents!: typeof WebContents;
get CurrentWindowNew(): any {
return this._CurrentWindowNew;
}
set CurrentWindowNew(value: any) {
this._CurrentWindowNew = value;
}
get CurrentBrowserWindow(): Electron.BrowserWindow {
return this._CurrentBrowserWindow;
}
set CurrentBrowserWindow(value: Electron.BrowserWindow) {
this._CurrentBrowserWindow = value;
}
get HomeBrowserWindow(): BrowserWindow {
return this._HomeBrowserWindow;
}
set HomeBrowserWindow(value: BrowserWindow) {
this.CurrentBrowserWindow = value;
this._HomeBrowserWindow = value;
}
get SettingBrowserWindow(): BrowserWindow {
return this._SettingBrowserWindow;
}
set SettingBrowserWindow(value: BrowserWindow) {
this.CurrentBrowserWindow = value;
this._SettingBrowserWindow = value;
}
get HomeBrowserWindowWebContents(): typeof WebContents {
return this._HomeBrowserWindowWebContents;
}
set HomeBrowserWindowWebContents(value: typeof WebContents) {
this._HomeBrowserWindowWebContents = value;
}
get SettingBrowserWindowWebContents(): typeof WebContents {
return this._SettingBrowserWindowWebContents;
}
set SettingBrowserWindowWebContents(value: typeof WebContents) {
this._SettingBrowserWindowWebContents = value;
}
}
export default new Windows();

1
src/core/net/.pydio Normal file
View File

@@ -0,0 +1 @@
32c6526e-0e3a-4ad6-bd4b-56580bfc716f

89
src/core/net/Http.net.ts Normal file
View File

@@ -0,0 +1,89 @@
import Config from "@config/Index.config";
import {Inject, Injectable } from "@annotation/Ioc.annotation";
import { LogsUtils } from '@utils/logs.utils';
import Axios, { AxiosResponse } from "axios";
import Utils from "@utils/Index.utils";
interface RequestParams {
url: string,
data?: { [index: string]: any; },
header?: { [index: string]: any }
}
@Injectable()
export class Http {
private ResponseData!: AxiosResponse<any>;
constructor() {
Axios.defaults.baseURL = Utils.CheckAjaxUrl();
}
@Inject()
private LogsUtils!: LogsUtils;
/**
* GET请求
* @param params
* @constructor
*/
public async GET(params: RequestParams): Promise<any> {
try {
this.ResponseData = await Axios.get(params.url, {
params: params.data,
headers: Object.assign({}, params.header)
});
return this.ResponseData.data;
} catch (e) {
throw new Error(`GET 请求出错:${e.message}`)
}
}
/**
* POST请求
* @param params
* @constructor
*/
public async POST(params: RequestParams): Promise<any> {
try {
this.ResponseData = await Axios.post(params.url, params.data, {
headers: Object.assign({}, params.header)
})
return this.ResponseData.data;
} catch (e) {
throw new Error(`POST 请求出错:${e.message}`)
}
}
/**
* PUT 请求
* @param params
* @constructor
*/
public async PUT(params: RequestParams): Promise<any> {
try {
this.ResponseData = await Axios.put(params.url, params.data, {
headers: Object.assign({}, params.header)
})
return this.ResponseData.data;
} catch (e) {
throw new Error(`PUT 请求出错:${e.message}`)
}
}
/**
* DELETE 请求
* @param params
*/
public async DELETE(params: RequestParams): Promise<any> {
try {
this.ResponseData = await Axios.delete(params.url, {
params: params.data,
headers: Object.assign({}, params.header)
})
return this.ResponseData.data;
} catch (e) {
throw new Error(`DELETE 请求出错:${e.message}`)
}
}
}

1
src/core/run/.pydio Normal file
View File

@@ -0,0 +1 @@
b7bb9238-8c04-4671-a65c-4880baa9ff7d

27
src/core/run/Init.run.ts Normal file
View File

@@ -0,0 +1,27 @@
import { AutoLoadWindow, CreateApplicationMenu, CreateTouchbar, CreateTray }
from "@annotation/Run.annotation";
import { CreateApplicationIpc } from "@annotation/Creted.annotation";
import { Application } from "@ipc/Application.ipc";
import { BaseControllerTypes } from "@type/BaseController.types";
/**
* 启动类
* 作用:注册全局事件
* V2版本开发新的注解支持
* @AutoLoadWindow() 注解 根据 Index.config.ts 中的 StartPage 自动寻找controller文件夹下的ts文件然后作为主窗体启动
* @CreateApplicationMenu() 创建菜单
* @CreateTouchbar() 创建mac键盘上的Touchbar
* @CreateTray() 创建mac顶部全局菜单图标
* @CreateApplicationIpc() 注册全局ipc
*/
@AutoLoadWindow()
@CreateApplicationMenu()
@CreateTouchbar()
@CreateTray()
@CreateApplicationIpc([ Application ])
export class Run implements BaseControllerTypes {
event(): void {}
ipc(): void {}
monitor(): void {}
ui(): void {}
}

1
src/core/service/.pydio Normal file
View File

@@ -0,0 +1 @@
1310afb9-bc18-41af-84b1-88d4f9e46e90

View File

@@ -0,0 +1,4 @@
export interface VideoService {
PlayList(params: object): Promise<any>;
PalyVideo(params: object | undefined): Promise<any>;
}

View File

@@ -0,0 +1 @@
1de04712-d1ce-4518-8326-6afa1ed4af55

View File

@@ -0,0 +1,32 @@
import { VideoService } from "@service/Video.service";
import { GET } from "@annotation/Http.annotation";
import { Injectable } from "@annotation/Ioc.annotation";
@Injectable()
export class VideoImplService implements VideoService {
/**
* @param params
* url: 请求地址(可选参数)
* 1如果不传方法名将自动作为请求的地址
* 2如果传了采用传入的地址作为请求地址
* useHandle: 是否将注解通过ajax获得的数据还给方法可选参数
* 1如果不传数据将直接返回给页面的方法调用者service的方法不需要retrun
* 2如果传了service方法的参数即是接口的请求参数在请求完成后将会保存ajax返回结果
* 所以根据实际业务可以传入true对ajax返回的数据做一些数据处理然后再返回给页面使用
*/
@GET({ useHandle: true })
public async PlayList(params: object): Promise<any> {
/**
* useHandle 为 true 的方法会被调用两次一次是页面调用params存储的是ajax请求参数
* 第二次是注解内部调用params 存储的是ajax请求到的具体数据
*/
return params
}
@GET()
public async PalyVideo(params: object | undefined): Promise<any> {}
@GET()
public async VideoComment(params: object | undefined): Promise<any> {}
}

1
src/core/types/.pydio Normal file
View File

@@ -0,0 +1 @@
bfc4a79a-fb3d-479d-917b-3f98d337341c

View File

@@ -0,0 +1,6 @@
export interface ApiUrlConfig {
BaseUrl: string,
ApiList: {
[key: string]: any
}
}

View File

@@ -0,0 +1,7 @@
export interface BaseControllerTypes {
// render(): { [ key:string ]: any };
ipc(): void;
ui(): void;
event(): void;
monitor(): void;
}

View File

@@ -0,0 +1,13 @@
import { BrowserWindowConstructorOptions } from "electron";
export interface PagePath {
Home?: String,
PlayVideo?: String
}
export interface PageSize {
// 系统首页 窗体的配置
Home?: BrowserWindowConstructorOptions,
// 设置页面 窗体的配置
PlayVideo?: BrowserWindowConstructorOptions
}

View File

@@ -0,0 +1,7 @@
import { MenuItem, MenuItemConstructorOptions } from "electron";
export interface TrayConfig {
TopMenuRightImage: string;
TopMenuRightDropdown: Array<(MenuItemConstructorOptions) | (MenuItem)>;
TopMenuRightTips: string;
}

1
src/core/utils/.pydio Normal file
View File

@@ -0,0 +1 @@
0d2b0ed4-6fd4-46e2-9c76-c4515be3f00e

View File

@@ -0,0 +1,119 @@
import { join } from "path";
import Config from "@config/Index.config";
import {BrowserWindow, Notification, NotificationConstructorOptions, shell} from "electron";
import {mkdirSync, writeFileSync, existsSync, PathLike} from "fs";
import {renderFile} from "jade";
import Windows from "@model/Windows.model";
export default class Utils {
public static CheckAjaxUrl(): string {
return Config.ApiUrl.BaseUrl
}
/**
* 返回相对路径
* @param path 路径
* @constructor
*/
public static GetFilePath(path: string): string {
return join(__dirname, path)
}
/**
* 根据配置返回对应的controller类require后的类
* @constructor
*/
public static GetController(): any {
return require(`../controller/${(Config.StartPage.split("/"))[0]}.js`)
}
/**
* Home.controller 拆分为数组然后controller的首字母C大写
* 然后返回 HomeController 的类名
*/
public static toUpperCase(cont: string): string {
let Controller = cont.split(".")
return Controller[0]+ Controller[1].charAt(0).toUpperCase() + Controller[1].slice(1);
}
/**
* 系统通知
* @param parmas
* @constructor
*/
public static Notification(parmas: NotificationConstructorOptions): void {
new Notification(parmas).show();
}
/**
* 下载资源
* @param url 需要下载的资源地址
* @param path 下载后需要保存的路径
* @constructor
*/
public static DownFile(url: string, path: PathLike) {
Windows.CurrentBrowserWindow.webContents.downloadURL(url)
Windows.CurrentBrowserWindow.webContents.session.on(
"will-download",
(event: any, item: any, webContents: any) => {
item.setSavePath(`${path}/${item.getFilename()}`);
item.once('done', (event: any, state: any) => {
if (state === 'completed') {
// 下载成功后显示通知
Utils.Notification({
title: '下载完成',
body: `您的视频 ${item.getFilename()} 已成功下载!`,
silent: true,
})
}
})
}
)
}
/**
* 创建文件夹
* @param name 文件夹
* @param html html
*/
public static mkdir(name: string, html: string): void {
let path = join(__dirname, `../../application/page/${name}`);
existsSync(path) ? null : mkdirSync(path)
Utils.mkFile(path, name, html)
}
/**
* 生成文件并写入数据
* @param path 路径
* @param name html文件名
* @param html html数据
*/
public static mkFile(path: string, name: string, html: string): void {
writeFileSync(`${path}/${name}.html`, html)
}
/**
* 创建启动窗口
* @param target
* @param name
*/
public static async startWindows(target: any, name: string, params?: object) {
let data: { [ p: string ]: any } = await target[name](params),
Dom: string = renderFile(
Utils.GetFilePath(`../../../src/application/page/${name}/${name}.jade`),
Object.assign(Config.jadeCompile0ptions, data),
);
// 在dist/ 创建并生成对应的html文件
Utils.mkdir(name, Dom)
try {
// @ts-ignore
Windows.CurrentBrowserWindow = new BrowserWindow(Config.PageSize[name]);
// @ts-ignore
Windows.CurrentBrowserWindow.loadFile(<string>Config.PagePath[name]).then(r => {});
Windows.CurrentBrowserWindow.show();
} catch (e) {
throw new Error("创建窗体失败请检查配置文件窗体的html路径是否正确")
}
}
}

View File

@@ -0,0 +1,31 @@
import moment from "moment"
import { appendFileSync } from "fs"
import { join } from "path";
import Config from "@config/Index.config";
import { Injectable } from "@annotation/Ioc.annotation";
import os from "os"
@Injectable()
export class LogsUtils {
/**
* 返回日志文件名 application_2020-07-16_21-03-21.log
* @param level 日志类型
* @constructor
*/
public GetTime(level: string = "application"): string {
return `${level}_${moment().format("YYYY-MM-DD-HH")}.log`;
}
/**
* 传入数据生成日志
* @param data 数据
* @param level 日志类型
*/
public logs(data: string, level: string = "application"): void {
appendFileSync(join(
__dirname,
`${Config.LogsPath}/${this.GetTime(level)}`), `${moment().format("YYYY/MM/DD HH:mm:ss")}${data}${os.EOL}${os.EOL}`)
}
}