first commit
This commit is contained in:
1
src/.pydio
Normal file
1
src/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
e8bd96a5-c939-4b6a-a198-0353177e7e79
|
||||
12
src/App.ts
Normal file
12
src/App.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
require('module-alias/register')
|
||||
import { app } from "electron";
|
||||
import { Run } from "@run/Init.run";
|
||||
app.on('ready', () => {
|
||||
new Run()
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
});
|
||||
1
src/application/.pydio
Normal file
1
src/application/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
9af67781-5f56-4703-8a0f-36bf92325403
|
||||
1
src/application/assets/.pydio
Normal file
1
src/application/assets/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
e161dad5-8741-4503-ab79-a3680cb3acd1
|
||||
1
src/application/assets/img/.pydio
Normal file
1
src/application/assets/img/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
5870d722-0824-4515-9172-8397f1276c1e
|
||||
BIN
src/application/assets/img/pug.png
Normal file
BIN
src/application/assets/img/pug.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 859 B |
1
src/application/assets/js/.pydio
Normal file
1
src/application/assets/js/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
48ab38da-fa2d-4cf5-bcc5-075c76854ef8
|
||||
1
src/application/assets/js/lib/.pydio
Normal file
1
src/application/assets/js/lib/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
ece44459-a089-49b6-8666-6ba7b161a6e0
|
||||
62
src/application/assets/js/lib/http.js
Normal file
62
src/application/assets/js/lib/http.js
Normal file
@@ -0,0 +1,62 @@
|
||||
class Utils {
|
||||
/**
|
||||
* 发送GET 请求
|
||||
* @param URL 地址
|
||||
* @param params 参数
|
||||
* @param callback 回调
|
||||
* @constructor
|
||||
*/
|
||||
Http(URL, params,callback) {
|
||||
$.ajax({
|
||||
url: `http://www.bmycode.com:3000/api${URL}`,
|
||||
type: 'GET',
|
||||
data: params,
|
||||
success: (res)=> {
|
||||
callback(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间戳返回多久之前的时间
|
||||
* @param timespan 时间戳
|
||||
* @returns {string}
|
||||
*/
|
||||
formatTime (timespan) {
|
||||
var dateTime = new Date(timespan);
|
||||
var year = dateTime.getFullYear();
|
||||
var month = dateTime.getMonth() + 1;
|
||||
var day = dateTime.getDate();
|
||||
var hour = dateTime.getHours();
|
||||
var minute = dateTime.getMinutes();
|
||||
var second = dateTime.getSeconds();
|
||||
var now = new Date();
|
||||
var now_new = Date.parse(now.toDateString()); //typescript转换写法
|
||||
|
||||
var milliseconds = 0;
|
||||
var timeSpanStr;
|
||||
|
||||
milliseconds = now_new - timespan;
|
||||
|
||||
if (milliseconds <= 1000 * 60 * 1) {
|
||||
timeSpanStr = '刚刚';
|
||||
}
|
||||
else if (1000 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60) {
|
||||
timeSpanStr = Math.round((milliseconds / (1000 * 60))) + '分钟前';
|
||||
}
|
||||
else if (1000 * 60 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24) {
|
||||
timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60)) + '小时前';
|
||||
}
|
||||
else if (1000 * 60 * 60 * 24 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24 * 15) {
|
||||
timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60 * 24)) + '天前';
|
||||
}
|
||||
else if (milliseconds > 1000 * 60 * 60 * 24 * 15 && year == now.getFullYear()) {
|
||||
timeSpanStr = month + '月' + day + '号' + hour + ':' + minute;
|
||||
} else {
|
||||
timeSpanStr = year + '年' + month + '月' + day + '号' + hour + ':' + minute;
|
||||
}
|
||||
return timeSpanStr;
|
||||
};
|
||||
}
|
||||
|
||||
window.$Utils = new Utils()
|
||||
10872
src/application/assets/js/lib/jquery.js
vendored
Normal file
10872
src/application/assets/js/lib/jquery.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
src/application/assets/js/lib/zui/.pydio
Normal file
1
src/application/assets/js/lib/zui/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
c5132154-4147-4be9-89d3-dda9ca159cd6
|
||||
1322
src/application/assets/js/lib/zui/zui-theme.css
Normal file
1322
src/application/assets/js/lib/zui/zui-theme.css
Normal file
File diff suppressed because it is too large
Load Diff
6
src/application/assets/js/lib/zui/zui.min.css
vendored
Normal file
6
src/application/assets/js/lib/zui/zui.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
16
src/application/assets/js/lib/zui/zui.min.js
vendored
Normal file
16
src/application/assets/js/lib/zui/zui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/application/assets/js/page/.pydio
Normal file
1
src/application/assets/js/page/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
780209c9-bb5e-4d40-a8b6-a2bd47c2eab3
|
||||
108
src/application/assets/js/page/Home.js
Normal file
108
src/application/assets/js/page/Home.js
Normal file
@@ -0,0 +1,108 @@
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
class Home {
|
||||
constructor() {
|
||||
this.playVideo()
|
||||
this.GotoList()
|
||||
this.page = $(".video_list").attr("page")
|
||||
this.loadMore()
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击播放视频,发送ipc通知主进程创建独立的 PlayVideo 窗口
|
||||
* 然后向 PlayVideo 方法传入 datat 参数
|
||||
*/
|
||||
playVideo() {
|
||||
$(".abbreviation_f").off("click").on('click',function (e) {
|
||||
if ($(this).attr("workType") == "video") {
|
||||
ipcRenderer.send('openWindow', {
|
||||
action: 'Home.controller/PlayVideo',
|
||||
data: {
|
||||
principalId: $(this).attr("principalId"),
|
||||
photoId: $(this).attr("photoId")
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ipcRenderer.send('openWindow', {
|
||||
action: 'Home.controller/PlayVideo',
|
||||
data: {
|
||||
caption: $(this).next().text(),
|
||||
list: JSON.parse( $(this).find(".imgUrls").text() )
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击使用默认浏览器打开主播主页
|
||||
* @constructor
|
||||
*/
|
||||
GotoList() {
|
||||
$(".info").off("click").on('click',function (e) {
|
||||
ipcRenderer.send('openExternal', {
|
||||
url: `https://live.kuaishou.com/profile/${$(this).prev().attr("principalId")}`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动加载更多数据
|
||||
*/
|
||||
loadMore() {
|
||||
let self = this;
|
||||
$(window).scroll(function () {
|
||||
let scrollTop = $(this).scrollTop();
|
||||
let scrollHeight = $(document).height();
|
||||
let windowHeight = $(this).height();
|
||||
if (scrollTop + windowHeight == scrollHeight) {
|
||||
self.getVideoList()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到底部后发送请求获取数据
|
||||
*/
|
||||
getVideoList() {
|
||||
$Utils.Http("/list", { page: this.page }, res=> {
|
||||
let MMList = res.data;
|
||||
|
||||
// 更新页面上的page页码
|
||||
$(".video_list").attr({ page: MMList.pcursor })
|
||||
// 保存页面到变量
|
||||
this.page = $(".video_list").attr("page")
|
||||
// 遍历新数据到页面
|
||||
MMList.list.forEach(function (val,index) {
|
||||
$(".video_list").append(`
|
||||
<li>
|
||||
<div class="abbreviation_f" principalId="${val.user.id}" photoId="${val.id}" workType="${val.workType}">
|
||||
<div style="background:url('${val.thumbnailUrl}');background-position: center center;" class="zz"></div>
|
||||
<img src="${val.thumbnailUrl}" class="thumbnailUrl" />
|
||||
<div class="${val.workType == 'video' ? 'tips red':'tips green'}">
|
||||
${val.workType == 'video' ? '视频':'图片'}
|
||||
${
|
||||
val.imgUrls.length != 0
|
||||
? `<div class="imgUrls" style="display:none">${JSON.stringify(val.imgUrls)}</div>`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<img src="${val.user.avatar}" />
|
||||
<p>${val.caption}</p>
|
||||
</div>
|
||||
</li>
|
||||
`);
|
||||
});
|
||||
|
||||
this.playVideo()
|
||||
this.GotoList()
|
||||
// 因为页面数据变多了,所以为所有的dom重新监听点击播放,进入主页事件
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
new Home();
|
||||
84
src/application/assets/js/page/PlayVideo.js
Normal file
84
src/application/assets/js/page/PlayVideo.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
class PlayVideo {
|
||||
constructor() {
|
||||
this.isShowMenu = false
|
||||
this.downVideo()
|
||||
this.initSwiper()
|
||||
this.ShowComment()
|
||||
this.ShowCommentList()
|
||||
this.SwiperIndex = 0;
|
||||
}
|
||||
|
||||
initSwiper() {
|
||||
let self = this;
|
||||
new Swiper ('.swiper-container', {
|
||||
direction: 'horizontal', // 垂直切换选项
|
||||
loop: false, // 循环模式选项
|
||||
// 如果需要分页器
|
||||
pagination: {
|
||||
el: '.swiper-pagination',
|
||||
},
|
||||
|
||||
// 如果需要前进后退按钮
|
||||
navigation: {
|
||||
nextEl: '.swiper-button-next',
|
||||
prevEl: '.swiper-button-prev',
|
||||
},
|
||||
on: {
|
||||
slideChange: function () {
|
||||
self.SwiperIndex = this.realIndex;
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
downVideo() {
|
||||
let self = this;
|
||||
$(".down_video").click(function () {
|
||||
// 当前播放的是图片
|
||||
if ($("#my-video").length == 0) {
|
||||
let allImg = $(".swiper-wrapper .swiper-slide img").eq(self.SwiperIndex)
|
||||
ipcRenderer.send('SaveFile', {
|
||||
url: allImg.attr("src")
|
||||
})
|
||||
// 当前播放的是视频
|
||||
} else {
|
||||
console.log("下载视频")
|
||||
ipcRenderer.send('SaveFile', {
|
||||
url: $("#my-video").attr("src")
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 展开评论弹窗
|
||||
* @constructor
|
||||
*/
|
||||
ShowComment() {
|
||||
$(".down_comment").click(() => {
|
||||
if (this.isShowMenu) {
|
||||
$(".comment_list").animate({ right: '-256px'})
|
||||
this.isShowMenu = false
|
||||
} else {
|
||||
$(".comment_list").animate({ right: 0})
|
||||
this.isShowMenu = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看更多评论
|
||||
* @constructor
|
||||
*/
|
||||
ShowCommentList() {
|
||||
$(".view_other").click(function () {
|
||||
$(this).next().toggle({
|
||||
'display': 'block'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
new PlayVideo();
|
||||
1
src/application/assets/less/.pydio
Normal file
1
src/application/assets/less/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
577aeb56-0023-42f0-b7e8-2d595cbabaa3
|
||||
73
src/application/assets/less/Home.less
Normal file
73
src/application/assets/less/Home.less
Normal file
@@ -0,0 +1,73 @@
|
||||
.Home{
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
li {
|
||||
margin-left: 15px;
|
||||
width: 15%;
|
||||
margin-bottom: 18px;
|
||||
&:hover {
|
||||
|
||||
}
|
||||
.abbreviation_f {
|
||||
width: 100%;
|
||||
height: 178px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
.zz {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
background: #000;
|
||||
filter: blur(10px);
|
||||
}
|
||||
.thumbnailUrl {
|
||||
width: 103px;
|
||||
height: 178px;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
left: 50%;
|
||||
margin-left: -51.5px;
|
||||
}
|
||||
.tips {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
color: #fff;
|
||||
padding: 2px 5px;
|
||||
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
.red {
|
||||
background: red;
|
||||
}
|
||||
.green {
|
||||
background: green;
|
||||
}
|
||||
}
|
||||
.info {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
img {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
p {
|
||||
font-size: 13px;
|
||||
width: 93%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
margin-left: 10px;
|
||||
color: #bababa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
128
src/application/assets/less/PlayVideo.less
Normal file
128
src/application/assets/less/PlayVideo.less
Normal file
@@ -0,0 +1,128 @@
|
||||
html, body, .PlayVideo, #my-video{
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
outline: none !important;
|
||||
}
|
||||
.swiper-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.swiper-slide {
|
||||
text-align: center;
|
||||
}
|
||||
img {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.PlayVideo {
|
||||
position: fixed;
|
||||
.title {
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
text-align: center;
|
||||
line-height: 45px;
|
||||
color: #fff;
|
||||
position: fixed;
|
||||
z-index: 99;
|
||||
i {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
font-size: 25px;
|
||||
cursor: pointer;
|
||||
&:before {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.down_video {
|
||||
right: 60px;
|
||||
}
|
||||
.down_comment {
|
||||
cursor: pointer;
|
||||
right: 20px;
|
||||
top: 2px;
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
.comment_list {
|
||||
position: absolute;
|
||||
right: -256px;
|
||||
z-index: 90;
|
||||
width: 256px;
|
||||
background: #202020;
|
||||
height: calc(100% - 32px);
|
||||
top: 0;
|
||||
overflow-y: scroll;
|
||||
padding-top: 32px;
|
||||
.showComment {
|
||||
display: none;
|
||||
.sub_comment {
|
||||
padding: 0 !important;
|
||||
margin-top: 15px;
|
||||
border-bottom: none !important;
|
||||
img {
|
||||
width: 24px !important;
|
||||
height: 24px !important;
|
||||
}
|
||||
.list_item {
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
.item_content {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.item_comment {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #303030;
|
||||
&:hover {
|
||||
background: #2d2b2b;
|
||||
cursor: pointer;
|
||||
}
|
||||
img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
.list_item {
|
||||
margin-left: 13px;
|
||||
width: 100%;
|
||||
.item_title {
|
||||
margin-top: 6px;
|
||||
color: #576b95;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.item_content{
|
||||
color: #909090;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.icon-like {
|
||||
color: #606060;
|
||||
font-size: 13px;
|
||||
.numbe {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.view_other {
|
||||
margin-left: 12px;
|
||||
span {
|
||||
|
||||
}
|
||||
.icon-cc-down {
|
||||
margin-left: 3px;
|
||||
}
|
||||
span,
|
||||
.icon-cc-down {
|
||||
color: #909090;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
22
src/application/assets/less/clear.less
Normal file
22
src/application/assets/less/clear.less
Normal file
@@ -0,0 +1,22 @@
|
||||
html,body{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
-webkit-app-region: drag
|
||||
}
|
||||
|
||||
ul,li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
float: left;
|
||||
width: calc(100% - 200px);
|
||||
margin-left: 200px;
|
||||
margin-top: 50px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
46
src/application/assets/less/header.less
Normal file
46
src/application/assets/less/header.less
Normal file
@@ -0,0 +1,46 @@
|
||||
.action {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
.left_action {
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
background: #f6f6f6;
|
||||
float: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 6px;
|
||||
box-sizing: border-box;
|
||||
.icon-houtui {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
.right_menu {
|
||||
width: calc(100% - 200px);
|
||||
height: 100%;
|
||||
background: #f9f9f9;
|
||||
float: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.menu_text {
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
margin-left: 32px;
|
||||
}
|
||||
.menu_action {
|
||||
i {
|
||||
margin-right: 20px;
|
||||
font-size: 22px;
|
||||
}
|
||||
.icon-yifu {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/application/assets/less/nav.less
Normal file
32
src/application/assets/less/nav.less
Normal file
@@ -0,0 +1,32 @@
|
||||
.nav_menu {
|
||||
width: 200px;
|
||||
height: calc(100% - 50px);
|
||||
background: #ededed;
|
||||
float: left;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 50px;
|
||||
.title {
|
||||
margin-left: 20px;
|
||||
color: #888888;
|
||||
margin-bottom: 7px;
|
||||
padding-top: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item_menu {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding-left: 20px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #e1e1e1;
|
||||
}
|
||||
.icon-ziyuan {
|
||||
font-size: 15px;
|
||||
}
|
||||
span {
|
||||
color: #2f2f2f;
|
||||
margin-left: 9px;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/application/components/.pydio
Normal file
1
src/application/components/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
13877692-fec3-4c9a-a8d9-56ee0f825f6e
|
||||
1
src/application/components/footer.jade
Normal file
1
src/application/components/footer.jade
Normal file
@@ -0,0 +1 @@
|
||||
h2 底部
|
||||
10
src/application/components/header.jade
Normal file
10
src/application/components/header.jade
Normal file
@@ -0,0 +1,10 @@
|
||||
.action
|
||||
.left_action
|
||||
i(class="iconfont icon-houtui")
|
||||
i(class="iconfont icon-qianjin")
|
||||
.right_menu
|
||||
.menu_text iTunes 音乐
|
||||
.menu_action
|
||||
i(class="iconfont icon-iconzhengli_youjian")
|
||||
i(class="iconfont icon-yifu")
|
||||
i(class="iconfont icon-shezhi")
|
||||
39
src/application/components/nav.jade
Normal file
39
src/application/components/nav.jade
Normal file
@@ -0,0 +1,39 @@
|
||||
.nav_menu
|
||||
.title 我的音乐
|
||||
.item_menu
|
||||
i(class="iconfont icon-yinle")
|
||||
span iTunes音乐
|
||||
.item_menu
|
||||
i(class="iconfont icon-changyongicon-")
|
||||
span 下载管理
|
||||
.item_menu
|
||||
i(class="iconfont icon-yun")
|
||||
span 我的音乐盘
|
||||
.item_menu
|
||||
i(class="iconfont icon-shoucang")
|
||||
span 我的收藏
|
||||
|
||||
.title 我的云盘
|
||||
.item_menu
|
||||
i(class="iconfont icon-tupian")
|
||||
span 精彩照片
|
||||
.item_menu
|
||||
i(class="iconfont icon-shipin1")
|
||||
span 视频记忆
|
||||
.item_menu
|
||||
i(class="iconfont icon-wenjianjia1")
|
||||
span 重要文件
|
||||
.item_menu
|
||||
i(class="iconfont icon-qita")
|
||||
span 其他资料
|
||||
|
||||
.title 在线娱乐
|
||||
.item_menu
|
||||
i(class="iconfont icon-ziyuan")
|
||||
span 短视频
|
||||
.item_menu
|
||||
i(class="iconfont icon-zhibo")
|
||||
span 娱乐直播
|
||||
.item_menu
|
||||
i(class="iconfont icon-xinwen")
|
||||
span 新闻资讯
|
||||
1
src/application/layout/.pydio
Normal file
1
src/application/layout/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
e26a9949-8bf3-45a8-a658-213dec0a0a26
|
||||
30
src/application/layout/index.jade
Normal file
30
src/application/layout/index.jade
Normal file
@@ -0,0 +1,30 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
meta(charset="utf-8")
|
||||
meta(http-equiv="X-UA-Compatible" content="IE=edge")
|
||||
meta(name="viewport" content="width=device-width, initial-scale=1")
|
||||
meta(name="renderer" content="webkit")
|
||||
meta(http-equiv="Cache-Control" content="no-siteapp")
|
||||
link(rel='stylesheet', href='../../assets/css/header.css')
|
||||
link(rel='stylesheet', href='../../assets/css/nav.css')
|
||||
script(src="../../assets/js/lib/http.js")
|
||||
// - 从 Index.config.ts 中渲染公共 css 和 js
|
||||
each val,index in globals.css
|
||||
link(rel='stylesheet', href=`${val}`)
|
||||
each val,index in globals.js
|
||||
script(src=`${val}`)
|
||||
script.
|
||||
if (typeof module === 'object') {
|
||||
window.jQuery = window.$ = module.exports;
|
||||
};
|
||||
|
||||
block title
|
||||
body
|
||||
- if (header)
|
||||
include ../components/header
|
||||
- if (nav)
|
||||
include ../components/nav
|
||||
block content
|
||||
|
||||
block script
|
||||
1
src/application/page/.pydio
Normal file
1
src/application/page/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
eac2e5cf-4383-4f4b-b53b-fd2f393a49ac
|
||||
1
src/application/page/Home/.pydio
Normal file
1
src/application/page/Home/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
4a111cfc-b7cf-40d4-929b-b16936294b4b
|
||||
28
src/application/page/Home/Home.jade
Normal file
28
src/application/page/Home/Home.jade
Normal file
@@ -0,0 +1,28 @@
|
||||
extends ../../layout/index
|
||||
|
||||
block title
|
||||
|
||||
title #{title}
|
||||
link(rel='stylesheet', href='../../assets/css/clear.css')
|
||||
link(rel='stylesheet', href='../../assets/css/Home.css')
|
||||
|
||||
//- 网站的主体内容区域
|
||||
block content
|
||||
.content
|
||||
.Home
|
||||
ul.video_list(page="#{list.pcursor}")
|
||||
each val,index in list.list
|
||||
li
|
||||
.abbreviation_f(principalId="#{val.user.id}" photoId="#{val.id}" workType="#{val.workType}")
|
||||
.zz(style="background: url(#{val.thumbnailUrl});background-position: center center;")
|
||||
img(class="thumbnailUrl" src="#{val.thumbnailUrl}")
|
||||
div(class="#{val.workType == 'video' ? 'tips red':'tips green'}")
|
||||
| #{val.workType == 'video' ? '视频':'图片'}
|
||||
- if (val.imgUrls.length != 0)
|
||||
div(class="imgUrls" style="display:none") #{JSON.stringify(val.imgUrls)}
|
||||
.info
|
||||
img(src="#{val.user.avatar}")
|
||||
p #{val.caption}
|
||||
|
||||
block script
|
||||
script(src="../../assets/js/page/Home.js")
|
||||
1
src/application/page/PlayVideo/.pydio
Normal file
1
src/application/page/PlayVideo/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
287a9417-e25d-47c0-a1e6-77e2cd2fc62b
|
||||
54
src/application/page/PlayVideo/PlayVideo.jade
Normal file
54
src/application/page/PlayVideo/PlayVideo.jade
Normal file
@@ -0,0 +1,54 @@
|
||||
extends ../../layout/index
|
||||
|
||||
block title
|
||||
|
||||
title 播放页面
|
||||
link(rel='stylesheet', href='../../assets/css/clear.css')
|
||||
link(rel='stylesheet', href='../../assets/css/PlayVideo.css')
|
||||
link(rel='stylesheet', href='https://cdn.bootcdn.net/ajax/libs/Swiper/5.4.5/css/swiper.min.css')
|
||||
|
||||
//- 网站的主体内容区域
|
||||
block content
|
||||
.PlayVideo
|
||||
.title
|
||||
span #{video.caption}
|
||||
i(class="iconfont down_video icon-changyongicon-")
|
||||
i(class="iconfont down_comment icon-comment")
|
||||
.comment_list
|
||||
each val,index in Comment.commentList
|
||||
.item_comment
|
||||
img(src="#{val.headurl}")
|
||||
.list_item
|
||||
.item_title #{val.authorName}
|
||||
.item_content #{val.content}
|
||||
i(class="iconfont icon-like")
|
||||
span.numbe #{val.likedCount}
|
||||
span.view_other
|
||||
- if (val.subComments.length != 0)
|
||||
span 查看#{val.subCommentCount}条评论
|
||||
i(class="iconfont icon-cc-down")
|
||||
.showComment
|
||||
each v,i in val.subComments
|
||||
.item_comment.sub_comment
|
||||
img(src="#{v.headurl}")
|
||||
.list_item
|
||||
.item_title #{v.authorName}
|
||||
.item_content #{v.content}
|
||||
|
||||
- if (img == true)
|
||||
.swiper-container
|
||||
.swiper-wrapper
|
||||
- each val,index in video.list
|
||||
.swiper-slide
|
||||
img(src="#{val}")
|
||||
.swiper-pagination
|
||||
.swiper-button-prev
|
||||
.swiper-button-next
|
||||
- else
|
||||
video#my-video(controlsList="nodownload" loop controls autoplay
|
||||
poster='#{video.poster}'
|
||||
src="#{video.playUrl}")
|
||||
|
||||
block script
|
||||
script(src="https://cdn.bootcdn.net/ajax/libs/Swiper/5.4.5/js/swiper.min.js")
|
||||
script(src="../../assets/js/page/PlayVideo.js")
|
||||
1
src/core/.pydio
Normal file
1
src/core/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
85ed9a37-2367-442e-ad9b-0524edf9f844
|
||||
1
src/core/annotation/.pydio
Normal file
1
src/core/annotation/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
d087137f-1bfb-4d06-96ef-473d70be7e66
|
||||
63
src/core/annotation/Creted.annotation.ts
Normal file
63
src/core/annotation/Creted.annotation.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/core/annotation/Http.annotation.ts
Normal file
40
src/core/annotation/Http.annotation.ts
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/core/annotation/Ioc.annotation.ts
Normal file
39
src/core/annotation/Ioc.annotation.ts
Normal 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)])()
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/core/annotation/Run.annotation.ts
Normal file
80
src/core/annotation/Run.annotation.ts
Normal 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
1
src/core/config/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
b9f49d6d-a018-4c87-8f77-433ade2c60aa
|
||||
226
src/core/config/Index.config.ts
Normal file
226
src/core/config/Index.config.ts
Normal 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: () => {}
|
||||
}),
|
||||
]
|
||||
};
|
||||
}
|
||||
1
src/core/controller/.pydio
Normal file
1
src/core/controller/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
1352edee-3a21-42cf-bf29-5ca3b2846b11
|
||||
47
src/core/controller/Home.controller.ts
Normal file
47
src/core/controller/Home.controller.ts
Normal 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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/core/controller/Setting.controller.ts
Normal file
27
src/core/controller/Setting.controller.ts
Normal 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: '介绍'
|
||||
};
|
||||
}
|
||||
}
|
||||
1
src/core/interactive/.pydio
Normal file
1
src/core/interactive/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
ca1b2ba3-e8cd-4c29-9ca1-f27abf9f8252
|
||||
16
src/core/interactive/ApplicationMenu.interactive.ts
Normal file
16
src/core/interactive/ApplicationMenu.interactive.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
33
src/core/interactive/Dialog.interactive.ts
Normal file
33
src/core/interactive/Dialog.interactive.ts
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
0
src/core/interactive/ProgressBar.interactive.ts
Normal file
0
src/core/interactive/ProgressBar.interactive.ts
Normal file
10
src/core/interactive/Touchbar.interactive.ts
Normal file
10
src/core/interactive/Touchbar.interactive.ts
Normal 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))
|
||||
}
|
||||
}
|
||||
38
src/core/interactive/Tray.interactive.ts
Normal file
38
src/core/interactive/Tray.interactive.ts
Normal 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
|
||||
}
|
||||
}
|
||||
0
src/core/interactive/dock.interactive.ts
Normal file
0
src/core/interactive/dock.interactive.ts
Normal file
1
src/core/ipc/.pydio
Normal file
1
src/core/ipc/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
cbaff76c-a698-40e1-916a-489df3b0fc0f
|
||||
52
src/core/ipc/Application.ipc.ts
Normal file
52
src/core/ipc/Application.ipc.ts
Normal 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)
|
||||
});
|
||||
}
|
||||
}
|
||||
1
src/core/ipc/module/.pydio
Normal file
1
src/core/ipc/module/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
9f151eb6-361c-4604-80e5-4aacdb861429
|
||||
20
src/core/ipc/module/File.ipc.ts
Normal file
20
src/core/ipc/module/File.ipc.ts
Normal 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
1
src/core/model/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
0afe9a3d-f1e9-43e0-975d-c298d3d7f242
|
||||
13
src/core/model/Ioc.model.ts
Normal file
13
src/core/model/Ioc.model.ts
Normal 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();
|
||||
65
src/core/model/Windows.model.ts
Normal file
65
src/core/model/Windows.model.ts
Normal 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
1
src/core/net/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
32c6526e-0e3a-4ad6-bd4b-56580bfc716f
|
||||
89
src/core/net/Http.net.ts
Normal file
89
src/core/net/Http.net.ts
Normal 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
1
src/core/run/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
b7bb9238-8c04-4671-a65c-4880baa9ff7d
|
||||
27
src/core/run/Init.run.ts
Normal file
27
src/core/run/Init.run.ts
Normal 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
1
src/core/service/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
1310afb9-bc18-41af-84b1-88d4f9e46e90
|
||||
4
src/core/service/Video.service.ts
Normal file
4
src/core/service/Video.service.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface VideoService {
|
||||
PlayList(params: object): Promise<any>;
|
||||
PalyVideo(params: object | undefined): Promise<any>;
|
||||
}
|
||||
1
src/core/service/impl/.pydio
Normal file
1
src/core/service/impl/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
1de04712-d1ce-4518-8326-6afa1ed4af55
|
||||
32
src/core/service/impl/Video.impl.service.ts
Normal file
32
src/core/service/impl/Video.impl.service.ts
Normal 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
1
src/core/types/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
bfc4a79a-fb3d-479d-917b-3f98d337341c
|
||||
6
src/core/types/ApiConfig.ts
Normal file
6
src/core/types/ApiConfig.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface ApiUrlConfig {
|
||||
BaseUrl: string,
|
||||
ApiList: {
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
7
src/core/types/BaseController.types.ts
Normal file
7
src/core/types/BaseController.types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface BaseControllerTypes {
|
||||
// render(): { [ key:string ]: any };
|
||||
ipc(): void;
|
||||
ui(): void;
|
||||
event(): void;
|
||||
monitor(): void;
|
||||
}
|
||||
13
src/core/types/PageConfig.types.ts
Normal file
13
src/core/types/PageConfig.types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { BrowserWindowConstructorOptions } from "electron";
|
||||
|
||||
export interface PagePath {
|
||||
Home?: String,
|
||||
PlayVideo?: String
|
||||
}
|
||||
|
||||
export interface PageSize {
|
||||
// 系统首页 窗体的配置
|
||||
Home?: BrowserWindowConstructorOptions,
|
||||
// 设置页面 窗体的配置
|
||||
PlayVideo?: BrowserWindowConstructorOptions
|
||||
}
|
||||
7
src/core/types/Tray.types.ts
Normal file
7
src/core/types/Tray.types.ts
Normal 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
1
src/core/utils/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
0d2b0ed4-6fd4-46e2-9c76-c4515be3f00e
|
||||
119
src/core/utils/Index.utils.ts
Normal file
119
src/core/utils/Index.utils.ts
Normal 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路径是否正确!")
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/core/utils/logs.utils.ts
Normal file
31
src/core/utils/logs.utils.ts
Normal 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}`)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user