add update
This commit is contained in:
parent
6f7313b770
commit
601ae5e340
33 changed files with 1041 additions and 291 deletions
1
CHANGELOG.md
Normal file
1
CHANGELOG.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
## 变更记录
|
||||||
|
|
@ -133,6 +133,9 @@ English | [简体中文](README.md)
|
||||||
- Support standard kanban, a must-have tool for project management, which can quickly create kanban and place it in different folders
|
- Support standard kanban, a must-have tool for project management, which can quickly create kanban and place it in different folders
|
||||||
<img src="./docs/img/kanban.png" width="600" />
|
<img src="./docs/img/kanban.png" width="600" />
|
||||||
|
|
||||||
|
### 22、 Process management
|
||||||
|
- Support process management, can view process list, kill processes
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
### Build
|
### Build
|
||||||
- Front end construction
|
- Front end construction
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@
|
||||||
<img src="./docs/img/setting-store.png" width="600" />
|
<img src="./docs/img/setting-store.png" width="600" />
|
||||||
|
|
||||||
### 十五、应用商店
|
### 十五、应用商店
|
||||||
- 应用商店管理,可添加外部应用。
|
- 应用商店管理,丰富的外部接口,可导入/添加/下载外部应用。
|
||||||
|
|
||||||
### 十六、截图
|
### 十六、截图
|
||||||
- 一个简单的截图工具。截图后文件存到本地。
|
- 一个简单的截图工具。截图后文件存到本地。
|
||||||
|
|
@ -148,6 +148,9 @@
|
||||||
- 支持标准看板,项目管理必备工具,可快速创建看板并放置到不同的文件夹
|
- 支持标准看板,项目管理必备工具,可快速创建看板并放置到不同的文件夹
|
||||||
<img src="./docs/img/kanban.png" width="600" />
|
<img src="./docs/img/kanban.png" width="600" />
|
||||||
|
|
||||||
|
### 二十二、进程管理
|
||||||
|
- 支持进程管理,可以查看进程列表,杀死进程
|
||||||
|
|
||||||
## 开发
|
## 开发
|
||||||
### 构建
|
### 构建
|
||||||
- 前端构建
|
- 前端构建
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ type InstallInfo struct {
|
||||||
CheckProgress bool `json:"checkProgress"` // 标志位,表示是否显示启动和停止。
|
CheckProgress bool `json:"checkProgress"` // 标志位,表示是否显示启动和停止。
|
||||||
HasRestart bool `json:"hasRestart"` // 标志位,表示安装后是否需要重启。
|
HasRestart bool `json:"hasRestart"` // 标志位,表示安装后是否需要重启。
|
||||||
Setting bool `json:"setting"` // 标志位,表示是否需要配置。
|
Setting bool `json:"setting"` // 标志位,表示是否需要配置。
|
||||||
|
Dependencies []Item `json:"dependencies"` // 依赖项。
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
9
frontend/components.d.ts
vendored
9
frontend/components.d.ts
vendored
|
|
@ -7,6 +7,7 @@ export {}
|
||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
Ad: typeof import('./src/components/desktop/Ad.vue')['default']
|
||||||
AddApp: typeof import('./src/components/store/AddApp.vue')['default']
|
AddApp: typeof import('./src/components/store/AddApp.vue')['default']
|
||||||
AppIcon: typeof import('./src/components/taskbar/AppIcon.vue')['default']
|
AppIcon: typeof import('./src/components/taskbar/AppIcon.vue')['default']
|
||||||
AppIconGroup: typeof import('./src/components/taskbar/AppIconGroup.vue')['default']
|
AppIconGroup: typeof import('./src/components/taskbar/AppIconGroup.vue')['default']
|
||||||
|
|
@ -42,6 +43,8 @@ declare module 'vue' {
|
||||||
ElBadge: typeof import('element-plus/es')['ElBadge']
|
ElBadge: typeof import('element-plus/es')['ElBadge']
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
ElCard: typeof import('element-plus/es')['ElCard']
|
ElCard: typeof import('element-plus/es')['ElCard']
|
||||||
|
ElCarousel: typeof import('element-plus/es')['ElCarousel']
|
||||||
|
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
|
||||||
ElCol: typeof import('element-plus/es')['ElCol']
|
ElCol: typeof import('element-plus/es')['ElCol']
|
||||||
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
|
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
|
||||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
|
|
@ -51,6 +54,7 @@ declare module 'vue' {
|
||||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||||
ElInput: typeof import('element-plus/es')['ElInput']
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
|
ElLink: typeof import('element-plus/es')['ElLink']
|
||||||
ElOption: typeof import('element-plus/es')['ElOption']
|
ElOption: typeof import('element-plus/es')['ElOption']
|
||||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||||
|
|
@ -58,6 +62,8 @@ declare module 'vue' {
|
||||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||||
ElSpace: typeof import('element-plus/es')['ElSpace']
|
ElSpace: typeof import('element-plus/es')['ElSpace']
|
||||||
|
ElTable: typeof import('element-plus/es')['ElTable']
|
||||||
|
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||||
ElText: typeof import('element-plus/es')['ElText']
|
ElText: typeof import('element-plus/es')['ElText']
|
||||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||||
Error: typeof import('./src/components/taskbar/Error.vue')['default']
|
Error: typeof import('./src/components/taskbar/Error.vue')['default']
|
||||||
|
|
@ -92,6 +98,7 @@ declare module 'vue' {
|
||||||
OsImage: typeof import('./src/components/builtin/OsImage.vue')['default']
|
OsImage: typeof import('./src/components/builtin/OsImage.vue')['default']
|
||||||
PdfViewer: typeof import('./src/components/builtin/PdfViewer.vue')['default']
|
PdfViewer: typeof import('./src/components/builtin/PdfViewer.vue')['default']
|
||||||
PictureStore: typeof import('./src/components/builtin/PictureStore.vue')['default']
|
PictureStore: typeof import('./src/components/builtin/PictureStore.vue')['default']
|
||||||
|
ProcessManager: typeof import('./src/components/setting/ProcessManager.vue')['default']
|
||||||
QuickLink: typeof import('./src/components/computer/QuickLink.vue')['default']
|
QuickLink: typeof import('./src/components/computer/QuickLink.vue')['default']
|
||||||
RectChosen: typeof import('./src/components/builtin/RectChosen.vue')['default']
|
RectChosen: typeof import('./src/components/builtin/RectChosen.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
|
@ -106,11 +113,13 @@ declare module 'vue' {
|
||||||
SetSystem: typeof import('./src/components/setting/SetSystem.vue')['default']
|
SetSystem: typeof import('./src/components/setting/SetSystem.vue')['default']
|
||||||
Setting: typeof import('./src/components/setting/Setting.vue')['default']
|
Setting: typeof import('./src/components/setting/Setting.vue')['default']
|
||||||
SetUpdate: typeof import('./src/components/setting/SetUpdate.vue')['default']
|
SetUpdate: typeof import('./src/components/setting/SetUpdate.vue')['default']
|
||||||
|
Sponsors: typeof import('./src/components/desktop/Sponsors.vue')['default']
|
||||||
StartMenu: typeof import('./src/components/taskbar/StartMenu.vue')['default']
|
StartMenu: typeof import('./src/components/taskbar/StartMenu.vue')['default']
|
||||||
StartOption: typeof import('./src/components/taskbar/StartOption.vue')['default']
|
StartOption: typeof import('./src/components/taskbar/StartOption.vue')['default']
|
||||||
StateIcon: typeof import('./src/components/taskbar/StateIcon.vue')['default']
|
StateIcon: typeof import('./src/components/taskbar/StateIcon.vue')['default']
|
||||||
Store: typeof import('./src/components/store/Store.vue')['default']
|
Store: typeof import('./src/components/store/Store.vue')['default']
|
||||||
Taskbar: typeof import('./src/components/taskbar/Taskbar.vue')['default']
|
Taskbar: typeof import('./src/components/taskbar/Taskbar.vue')['default']
|
||||||
|
Upgrade: typeof import('./src/components/desktop/Upgrade.vue')['default']
|
||||||
UpPopover: typeof import('./src/components/computer/UpPopover.vue')['default']
|
UpPopover: typeof import('./src/components/computer/UpPopover.vue')['default']
|
||||||
UrlBrowser: typeof import('./src/components/builtin/UrlBrowser.vue')['default']
|
UrlBrowser: typeof import('./src/components/builtin/UrlBrowser.vue')['default']
|
||||||
Version: typeof import('./src/components/builtin/Version.vue')['default']
|
Version: typeof import('./src/components/builtin/Version.vue')['default']
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,12 @@
|
||||||
<div class="content unicode" style="display: block;">
|
<div class="content unicode" style="display: block;">
|
||||||
<ul class="icon_lists dib-box">
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">progress</div>
|
||||||
|
<div class="code-name">&#xe625;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">zip</div>
|
<div class="name">zip</div>
|
||||||
|
|
@ -696,9 +702,9 @@
|
||||||
<pre><code class="language-css"
|
<pre><code class="language-css"
|
||||||
>@font-face {
|
>@font-face {
|
||||||
font-family: 'iconfont';
|
font-family: 'iconfont';
|
||||||
src: url('iconfont.woff2?t=1722214929182') format('woff2'),
|
src: url('iconfont.woff2?t=1722391811007') format('woff2'),
|
||||||
url('iconfont.woff?t=1722214929182') format('woff'),
|
url('iconfont.woff?t=1722391811007') format('woff'),
|
||||||
url('iconfont.ttf?t=1722214929182') format('truetype');
|
url('iconfont.ttf?t=1722391811007') format('truetype');
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||||
|
|
@ -724,6 +730,15 @@
|
||||||
<div class="content font-class">
|
<div class="content font-class">
|
||||||
<ul class="icon_lists dib-box">
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont icon-progress"></span>
|
||||||
|
<div class="name">
|
||||||
|
progress
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.icon-progress
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont icon-zip"></span>
|
<span class="icon iconfont icon-zip"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
|
|
@ -1687,6 +1702,14 @@
|
||||||
<div class="content symbol">
|
<div class="content symbol">
|
||||||
<ul class="icon_lists dib-box">
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-progress"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">progress</div>
|
||||||
|
<div class="code-name">#icon-progress</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#icon-zip"></use>
|
<use xlink:href="#icon-zip"></use>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 4412427 */
|
font-family: "iconfont"; /* Project id 4412427 */
|
||||||
src: url('iconfont.woff2?t=1722214929182') format('woff2'),
|
src: url('iconfont.woff2?t=1722391811007') format('woff2'),
|
||||||
url('iconfont.woff?t=1722214929182') format('woff'),
|
url('iconfont.woff?t=1722391811007') format('woff'),
|
||||||
url('iconfont.ttf?t=1722214929182') format('truetype');
|
url('iconfont.ttf?t=1722391811007') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
|
|
@ -13,6 +13,10 @@
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-progress:before {
|
||||||
|
content: "\e625";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-zip:before {
|
.icon-zip:before {
|
||||||
content: "\e623";
|
content: "\e623";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -5,6 +5,13 @@
|
||||||
"css_prefix_text": "icon-",
|
"css_prefix_text": "icon-",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "39799840",
|
||||||
|
"name": "progress",
|
||||||
|
"font_class": "progress",
|
||||||
|
"unicode": "e625",
|
||||||
|
"unicode_decimal": 58917
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "30862364",
|
"icon_id": "30862364",
|
||||||
"name": "zip",
|
"name": "zip",
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
108
frontend/src/components/desktop/Ad.vue
Normal file
108
frontend/src/components/desktop/Ad.vue
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
<template>
|
||||||
|
<div class="ad-container" v-show="state.ad.isShow" @click="onSponsorsClick">
|
||||||
|
<el-carousel height="240px" indicator-position="none" :arrow="setCarouselShow" @change="onCarouselChange">
|
||||||
|
<el-carousel-item v-for="(v, k) in upgradeStore.adList" :key="k">
|
||||||
|
<img :src="v.url" v-if="v.url" class="ad-img" />
|
||||||
|
<div class="ad-text" v-html="v.text"></div>
|
||||||
|
</el-carousel-item>
|
||||||
|
</el-carousel>
|
||||||
|
<div class="ad-close">
|
||||||
|
<el-icon :size="20" @click.stop="onCloseSponsors">
|
||||||
|
<CircleCloseFilled />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="layoutSponsors">
|
||||||
|
import { reactive, computed, onMounted } from 'vue';
|
||||||
|
import { useUpgradeStore } from '@/stores/upgrade';
|
||||||
|
const upgradeStore = useUpgradeStore();
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const state = reactive({
|
||||||
|
ad: {
|
||||||
|
isShow: false,
|
||||||
|
index: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置轮播图箭头显示
|
||||||
|
const setCarouselShow = computed(() => {
|
||||||
|
return upgradeStore.adList.length <= 1 ? 'never' : 'hover';
|
||||||
|
});
|
||||||
|
// 关闭赞助商
|
||||||
|
const onCloseSponsors = () => {
|
||||||
|
state.ad.isShow = false;
|
||||||
|
};
|
||||||
|
// 轮播图改变时
|
||||||
|
const onCarouselChange = (e: number) => {
|
||||||
|
state.ad.index = e;
|
||||||
|
};
|
||||||
|
// 当前项内容点击
|
||||||
|
const onSponsorsClick = () => {
|
||||||
|
const link = upgradeStore.adList[state.ad.index]?.link
|
||||||
|
if(link){
|
||||||
|
window.open(link)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 延迟显示,防止影响其它界面加载
|
||||||
|
const delayShow = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
state.ad.isShow = true;
|
||||||
|
}, 3000);
|
||||||
|
};
|
||||||
|
// 页面加载时
|
||||||
|
onMounted(() => {
|
||||||
|
delayShow();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.ad-container {
|
||||||
|
position: fixed;
|
||||||
|
top: calc(50vh - 175px);
|
||||||
|
left: calc(50vw - 175px);
|
||||||
|
z-index: 3;
|
||||||
|
width: 350px;
|
||||||
|
height: 350px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
.ad-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
.ad-text {
|
||||||
|
padding: 10px;
|
||||||
|
color: #303133;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.ad-close {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 100%;
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
right: -30px;
|
||||||
|
top: -30px;
|
||||||
|
:deep(i) {
|
||||||
|
position: absolute;
|
||||||
|
left: 9px;
|
||||||
|
bottom: 9px;
|
||||||
|
color: #afafaf;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
:deep(i) {
|
||||||
|
color: #409EFF;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -17,6 +17,9 @@
|
||||||
<NotifyGroup></NotifyGroup>
|
<NotifyGroup></NotifyGroup>
|
||||||
<MessageCenterPop></MessageCenterPop>
|
<MessageCenterPop></MessageCenterPop>
|
||||||
<Chosen></Chosen>
|
<Chosen></Chosen>
|
||||||
|
<Sponsors v-if="upgradeStore.hasNotice"></Sponsors>
|
||||||
|
<Upgrade v-if="upgradeStore.hasUpgrade"></Upgrade>
|
||||||
|
<Ad v-if="upgradeStore.hasAd"></Ad>
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<Taskbar></Taskbar>
|
<Taskbar></Taskbar>
|
||||||
|
|
@ -31,10 +34,12 @@ import { useFileDrag } from "@/hook/useFileDrag";
|
||||||
import { Rect, useRectChosen } from "@/hook/useRectChosen";
|
import { Rect, useRectChosen } from "@/hook/useRectChosen";
|
||||||
import { useSystem } from "@/system";
|
import { useSystem } from "@/system";
|
||||||
import { onErrorCaptured } from "vue";
|
import { onErrorCaptured } from "vue";
|
||||||
|
import { useUpgradeStore } from '@/stores/upgrade';
|
||||||
|
|
||||||
const { createDesktopContextMenu } = useContextMenu();
|
const { createDesktopContextMenu } = useContextMenu();
|
||||||
const { choseStart, chosing, choseEnd, getRect, Chosen } = useRectChosen();
|
const { choseStart, chosing, choseEnd, getRect, Chosen } = useRectChosen();
|
||||||
const system = useSystem();
|
const system = useSystem();
|
||||||
|
const upgradeStore = useUpgradeStore();
|
||||||
const { dragFileToDrop } = useFileDrag(system);
|
const { dragFileToDrop } = useFileDrag(system);
|
||||||
|
|
||||||
let chosenCallback: (rect: Rect) => void = () => {
|
let chosenCallback: (rect: Rect) => void = () => {
|
||||||
|
|
@ -87,6 +92,7 @@ function handleRightClick(e: MouseEvent) {
|
||||||
onErrorCaptured((err) => {
|
onErrorCaptured((err) => {
|
||||||
system.emitError(err.message.toString());
|
system.emitError(err.message.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.desktop {
|
.desktop {
|
||||||
|
|
|
||||||
114
frontend/src/components/desktop/Sponsors.vue
Normal file
114
frontend/src/components/desktop/Sponsors.vue
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
<template>
|
||||||
|
<div class="sponsors-container" v-show="state.sponsors.isShow" @click="onSponsorsClick">
|
||||||
|
<el-carousel height="240px" indicator-position="none" :arrow="setCarouselShow" @change="onCarouselChange">
|
||||||
|
<el-carousel-item v-for="(v, k) in upgradeStore.noticeList" :key="k">
|
||||||
|
<img :src="v.url" v-if="v.url" class="sponsors-img" />
|
||||||
|
<div class="sponsors-text" v-html="v.text"></div>
|
||||||
|
</el-carousel-item>
|
||||||
|
</el-carousel>
|
||||||
|
<div class="sponsors-close">
|
||||||
|
<el-icon :size="20" @click.stop="onCloseSponsors">
|
||||||
|
<CircleCloseFilled />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="layoutSponsors">
|
||||||
|
import { reactive, computed, onMounted } from 'vue';
|
||||||
|
import { useUpgradeStore } from '@/stores/upgrade';
|
||||||
|
const upgradeStore = useUpgradeStore();
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const state = reactive({
|
||||||
|
sponsors: {
|
||||||
|
// list: [
|
||||||
|
// {
|
||||||
|
// url: "",
|
||||||
|
// text: "",
|
||||||
|
// link: '',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
isShow: false,
|
||||||
|
index: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置轮播图箭头显示
|
||||||
|
const setCarouselShow = computed(() => {
|
||||||
|
return upgradeStore.noticeList.length <= 1 ? 'never' : 'hover';
|
||||||
|
});
|
||||||
|
// 关闭赞助商
|
||||||
|
const onCloseSponsors = () => {
|
||||||
|
state.sponsors.isShow = false;
|
||||||
|
};
|
||||||
|
// 轮播图改变时
|
||||||
|
const onCarouselChange = (e: number) => {
|
||||||
|
state.sponsors.index = e;
|
||||||
|
};
|
||||||
|
// 当前项内容点击
|
||||||
|
const onSponsorsClick = () => {
|
||||||
|
const link = upgradeStore.noticeList[state.sponsors.index]?.link
|
||||||
|
if(link){
|
||||||
|
window.open(link)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 延迟显示,防止影响其它界面加载
|
||||||
|
const delayShow = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
state.sponsors.isShow = true;
|
||||||
|
}, 3000);
|
||||||
|
};
|
||||||
|
// 页面加载时
|
||||||
|
onMounted(() => {
|
||||||
|
delayShow();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.sponsors-container {
|
||||||
|
position: fixed;
|
||||||
|
right: 6px;
|
||||||
|
bottom: 48px;
|
||||||
|
z-index: 3;
|
||||||
|
width: 200px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
.sponsors-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
.sponsors-text {
|
||||||
|
padding: 10px;
|
||||||
|
color: #303133;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.sponsors-close {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 100%;
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
right: -30px;
|
||||||
|
bottom: -30px;
|
||||||
|
:deep(i) {
|
||||||
|
position: absolute;
|
||||||
|
left: 9px;
|
||||||
|
top: 9px;
|
||||||
|
color: #afafaf;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
:deep(i) {
|
||||||
|
color: #409EFF;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
143
frontend/src/components/desktop/Upgrade.vue
Normal file
143
frontend/src/components/desktop/Upgrade.vue
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
<template>
|
||||||
|
<div class="upgrade-dialog">
|
||||||
|
<el-dialog
|
||||||
|
v-model="state.isUpgrade"
|
||||||
|
width="300px"
|
||||||
|
destroy-on-close
|
||||||
|
:show-close="false"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
>
|
||||||
|
<div class="upgrade-title">
|
||||||
|
<div class="upgrade-title-warp">
|
||||||
|
<span class="upgrade-title-warp-txt">{{ $t('upgrade.title') }}</span>
|
||||||
|
<span class="upgrade-title-warp-version">v{{ upgradeStore.versionTag }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="upgrade-content">
|
||||||
|
GodoOS {{ $t('upgrade.msg') }}
|
||||||
|
<div class="mt5">
|
||||||
|
<el-link type="primary" class="font12" href="https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md" target="_black">
|
||||||
|
CHANGELOG.md
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
<div class="upgrade-content-desc mt5">{{ $t('upgrade.desc') }}</div>
|
||||||
|
<div class="upgrade-content-desc" v-if="upgradeStore.progress > 0">
|
||||||
|
<el-progress :text-inside="true" :stroke-width="20" :percentage="upgradeStore.progress" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="upgrade-btn">
|
||||||
|
<el-button round size="default" type="info" text @click="onCancel">{{ $t('upgrade.btnOne') }}</el-button>
|
||||||
|
<el-button type="primary" round size="default" @click="onUpgrade" :loading="state.isLoading">{{ state.btnTxt }}</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="layoutUpgrade">
|
||||||
|
import { reactive, onMounted } from 'vue';
|
||||||
|
import { t } from '@/i18n';
|
||||||
|
import { useUpgradeStore } from '@/stores/upgrade';
|
||||||
|
const upgradeStore = useUpgradeStore();
|
||||||
|
const state = reactive({
|
||||||
|
isUpgrade: false,
|
||||||
|
isLoading: false,
|
||||||
|
btnTxt: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 残忍拒绝
|
||||||
|
const onCancel = () => {
|
||||||
|
state.isUpgrade = false;
|
||||||
|
};
|
||||||
|
// 马上更新
|
||||||
|
const onUpgrade = () => {
|
||||||
|
state.isLoading = true;
|
||||||
|
state.btnTxt = t('upgrade.btnTwoLoading');
|
||||||
|
setTimeout(async () => {
|
||||||
|
await upgradeStore.update();
|
||||||
|
state.isLoading = false;
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
// 延迟显示,防止刷新时界面显示太快
|
||||||
|
const delayShow = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
state.isUpgrade = true;
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
// 页面加载时
|
||||||
|
onMounted(() => {
|
||||||
|
delayShow();
|
||||||
|
setTimeout(() => {
|
||||||
|
state.btnTxt = t('upgrade.btnTwo');
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.upgrade-dialog {
|
||||||
|
:deep(.el-dialog) {
|
||||||
|
padding: 0 !important;
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.el-dialog__header {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.upgrade-title {
|
||||||
|
text-align: center;
|
||||||
|
height: 100px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
background-color: #2882dc;
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
border-bottom-left-radius: 100%;
|
||||||
|
border-bottom-right-radius: 100%;
|
||||||
|
}
|
||||||
|
.upgrade-title-warp {
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
.upgrade-title-warp-txt {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 22px;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
}
|
||||||
|
.upgrade-title-warp-version {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #2882dc;
|
||||||
|
font-size: 12px;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
top: -2px;
|
||||||
|
right: -50px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.upgrade-content {
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 22px;
|
||||||
|
.upgrade-content-desc {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.upgrade-btn {
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
padding: 15px 20px;
|
||||||
|
.el-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
58
frontend/src/components/setting/ProcessManager.vue
Normal file
58
frontend/src/components/setting/ProcessManager.vue
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, inject } from 'vue';
|
||||||
|
import { getSystemKey } from "@/system/config";
|
||||||
|
import {t} from "@/i18n";
|
||||||
|
import { BrowserWindow } from '@/system/window/BrowserWindow';
|
||||||
|
const win = inject<BrowserWindow>('browserWindow');
|
||||||
|
const processList = ref([]);
|
||||||
|
const apiUrl = getSystemKey("apiUrl");
|
||||||
|
let timerId: ReturnType<typeof setTimeout>; // 用于保存定时器ID
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchProcesses();
|
||||||
|
});
|
||||||
|
win?.on("close", () => {
|
||||||
|
//console.log(timerId)
|
||||||
|
clearTimeout(timerId);
|
||||||
|
})
|
||||||
|
const fetchProcesses = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(apiUrl + '/store/listport');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
processList.value = data.processes;
|
||||||
|
timerId = setTimeout(() => {
|
||||||
|
fetchProcesses()
|
||||||
|
}, 3000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching processes:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const killProcess = async (name: string) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(apiUrl + '/store/killport?name=' + name);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
fetchProcesses(); // 刷新列表
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error killing process:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<el-table :data="processList" stripe style="width: 96%;margin:auto;border:none" max-height="550">
|
||||||
|
<el-table-column prop="port" :label="t('process.port')" sortable />
|
||||||
|
<el-table-column prop="pid" :label="t('process.pid')" sortable />
|
||||||
|
<el-table-column prop="name" :label="t('process.name')" />
|
||||||
|
<el-table-column prop="proto" :label="t('process.proto')" />
|
||||||
|
<el-table-column :label="t('process.action')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button circle icon="Remove" @click="killProcess(row.name)" style="width: 35px;height: 35px;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref, defineProps } from "vue";
|
||||||
import { OpenDirDialog } from "@/util/goutil";
|
import { OpenDirDialog } from "@/util/goutil";
|
||||||
import { getSystemKey, parseJson } from "@/system/config";
|
import { getSystemKey, parseJson } from "@/system/config";
|
||||||
const apiUrl = getSystemKey("apiUrl");
|
const apiUrl = getSystemKey("apiUrl");
|
||||||
const installed = getSystemKey("intstalledPlugins");
|
|
||||||
import { notifySuccess, notifyError } from "@/util/msg";
|
import { notifySuccess, notifyError } from "@/util/msg";
|
||||||
import { t } from "@/i18n";
|
import { t } from "@/i18n";
|
||||||
|
const props = defineProps({
|
||||||
|
install: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
importType: 'download',
|
importType: 'download',
|
||||||
url: '',
|
url: '',
|
||||||
|
|
@ -17,10 +22,10 @@ const addType = [
|
||||||
'name': '远程下载',
|
'name': '远程下载',
|
||||||
'type': 'download'
|
'type': 'download'
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
'name': '本地导入',
|
// 'name': '本地导入',
|
||||||
'type': 'local'
|
// 'type': 'local'
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
'name': '开发模式',
|
'name': '开发模式',
|
||||||
'type': 'dev'
|
'type': 'dev'
|
||||||
|
|
@ -63,6 +68,8 @@ async function addAppByDownload() {
|
||||||
if (res.done) {
|
if (res.done) {
|
||||||
notifySuccess(t("store.downloadSuccess"))
|
notifySuccess(t("store.downloadSuccess"))
|
||||||
progress.value = 0
|
progress.value = 0
|
||||||
|
const pluginName = res.path.split("/").pop().split(".")[0]
|
||||||
|
await installPlugin(pluginName)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +79,20 @@ async function addAppByImport() {
|
||||||
|
|
||||||
}
|
}
|
||||||
async function addAppByDev() {
|
async function addAppByDev() {
|
||||||
|
if(formData.value.devPath && formData.value.devPath != ""){
|
||||||
|
await installPlugin(formData.value.devPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function installPlugin(pluginName: string) {
|
||||||
|
const completion = await fetch(apiUrl + '/store/installInfo?name=' + pluginName)
|
||||||
|
if (!completion.ok) {
|
||||||
|
notifyError(t("store.installError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const item = await completion.json()
|
||||||
|
//console.log(item)
|
||||||
|
item.isOut = true
|
||||||
|
await props.install(item.data)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -1,82 +1,38 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject, onMounted, ref } from "vue";
|
import { inject, onMounted } from "vue";
|
||||||
import { System } from "@/system";
|
import { System } from "@/system";
|
||||||
import { notifySuccess, notifyError } from "@/util/msg";
|
import { notifySuccess, notifyError } from "@/util/msg";
|
||||||
|
import { useStoreStore } from "@/stores/store";
|
||||||
|
import { setSystemKey,parseJson } from "@/system/config";
|
||||||
import { t } from "@/i18n";
|
import { t } from "@/i18n";
|
||||||
import storeInitList from "@/assets/store.json";
|
|
||||||
import { getSystemKey, setSystemKey, parseJson } from "@/system/config";
|
|
||||||
const sys: any = inject<System>("system");
|
const sys: any = inject<System>("system");
|
||||||
const currentCateId = ref(0)
|
const store = useStoreStore()
|
||||||
const currentTitle = ref(t("store.hots"))
|
|
||||||
const currentCate = ref('hots')
|
|
||||||
const categoryList = ['hots', 'work', 'development', 'games', 'education', 'news', 'shopping', 'social', 'utilities', 'others', 'add']
|
|
||||||
const categoryIcon = ['HomeFilled', 'Odometer', 'Postcard', 'TrendCharts', 'School', 'HelpFilled', 'ShoppingCart', 'ChatLineRound', 'MessageBox', 'Ticket', 'CirclePlusFilled']
|
|
||||||
const isready = ref(false);
|
|
||||||
const installed = getSystemKey("intstalledPlugins");
|
|
||||||
const apiUrl = getSystemKey("apiUrl");
|
|
||||||
|
|
||||||
const installedList: any = ref(installed);
|
|
||||||
const storeList: any = ref([])
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getList();
|
await store.getList();
|
||||||
|
|
||||||
});
|
});
|
||||||
async function getList() {
|
|
||||||
if (currentCate.value == 'add') return;
|
|
||||||
storeList.value = storeInitList
|
|
||||||
|
|
||||||
// const apiUrl = getSystemKey("apiUrl");
|
|
||||||
// const storeUrl = apiUrl + '/store/storelist?cate=' + currentCate.value
|
|
||||||
// const res = await fetch(storeUrl)
|
|
||||||
// if (!res.ok) {
|
|
||||||
// notifyError(t('store.getStoreListError'))
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// const data = await res.json()
|
|
||||||
// //console.log(data)
|
|
||||||
// storeList.value = data
|
|
||||||
await checkProgress()
|
|
||||||
isready.value = true;
|
|
||||||
}
|
|
||||||
async function checkProgress() {
|
|
||||||
const completion: any = await fetch(apiUrl + '/store/listporgress')
|
|
||||||
if (!completion.ok) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let res: any = await completion.json()
|
|
||||||
if (!res || res.length < 1) {
|
|
||||||
res = []
|
|
||||||
}
|
|
||||||
storeList.value.forEach((item: any, index: number) => {
|
|
||||||
const pitem: any = res.find((i: any) => i.name == item.name)
|
|
||||||
console.log(pitem)
|
|
||||||
if (pitem) {
|
|
||||||
storeList.value[index].isRuning = pitem.running
|
|
||||||
} else {
|
|
||||||
storeList.value[index].isRuning = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
async function changeCate(index: number, item: string) {
|
|
||||||
currentCateId.value = index
|
|
||||||
currentCate.value = item
|
|
||||||
currentTitle.value = t("store." + item)
|
|
||||||
await getList()
|
|
||||||
}
|
|
||||||
function setCache() {
|
function setCache() {
|
||||||
setSystemKey("intstalledPlugins", installedList.value);
|
setSystemKey("intstalledPlugins", store.installedList);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
sys.refershAppList();
|
sys.refershAppList();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
async function install(item: any) {
|
async function install(item: any) {
|
||||||
|
if (item.isOut) {
|
||||||
|
item.progress = 0
|
||||||
|
const flag = store.addOutList(item)
|
||||||
|
if(!flag){
|
||||||
|
notifyError(t("store.installError"))
|
||||||
|
}
|
||||||
|
}
|
||||||
if (item.needDownload && !item.isDev) {
|
if (item.needDownload && !item.isDev) {
|
||||||
await download(item)
|
await download(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.needInstall) {
|
if (item.needInstall) {
|
||||||
item.progress = 0
|
item.progress = 0
|
||||||
const completion = await fetch(apiUrl + '/store/install?name=' + item.name)
|
const completion = await fetch(store.apiUrl + '/store/install?name=' + item.name)
|
||||||
if (!completion.ok) {
|
if (!completion.ok) {
|
||||||
notifyError(t("store.installError"))
|
notifyError(t("store.installError"))
|
||||||
return
|
return
|
||||||
|
|
@ -87,22 +43,10 @@ async function install(item: any) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
item.icon = res.data
|
item.icon = res.data.icon
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item.isOut) {
|
|
||||||
item.progress = 0
|
|
||||||
const completion = await fetch(apiUrl + '/store/installOut?url=' + item.url)
|
|
||||||
if (!completion.ok) {
|
|
||||||
notifyError(t("store.installError"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const res = await completion.json()
|
|
||||||
if (res.code && res.code < 0) {
|
|
||||||
notifyError(res.message)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.webUrl) {
|
if (item.webUrl) {
|
||||||
sys.fs.writeFile(
|
sys.fs.writeFile(
|
||||||
`${sys._options.userLocation}Desktop/${item.name}.url`,
|
`${sys._options.userLocation}Desktop/${item.name}.url`,
|
||||||
|
|
@ -110,18 +54,18 @@ async function install(item: any) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifySuccess(t("install.success"))
|
notifySuccess(t("store.installSuccess"))
|
||||||
installedList.value.push(item.name);
|
store.installedList.push(item.name);
|
||||||
await checkProgress()
|
await store.checkProgress()
|
||||||
setCache();
|
setCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uninstall(item: any) {
|
async function uninstall(item: any) {
|
||||||
if (item.needInstall) {
|
if (item.needInstall) {
|
||||||
item.progress = 0
|
item.progress = 0
|
||||||
const completion = await fetch(apiUrl + '/store/uninstall?name=' + item.name)
|
const completion = await fetch(store.apiUrl + '/store/uninstall?name=' + item.name)
|
||||||
if (!completion.ok) {
|
if (!completion.ok) {
|
||||||
notifyError(t("store.installError"))
|
notifyError(t("store.hasSameName"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const res = await completion.json()
|
const res = await completion.json()
|
||||||
|
|
@ -131,14 +75,14 @@ async function uninstall(item: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sys.fs.unlink(`${sys._options.userLocation}Desktop/${item.name}.url`);
|
sys.fs.unlink(`${sys._options.userLocation}Desktop/${item.name}.url`);
|
||||||
notifySuccess(t("uninstall.success"))
|
notifySuccess(t("store.uninstallSuccess"))
|
||||||
delete installedList.value[installedList.value.indexOf(item.name)];
|
delete store.installedList[store.installedList.indexOf(item.name)];
|
||||||
setCache();
|
setCache();
|
||||||
}
|
}
|
||||||
async function download(item: any) {
|
async function download(item: any) {
|
||||||
//console.log(item)
|
//console.log(item)
|
||||||
if (item.progress) return;
|
if (item.progress) return;
|
||||||
const completion = await fetch(apiUrl + '/store/download?url=' + item.url)
|
const completion = await fetch(store.apiUrl + '/store/download?url=' + item.url)
|
||||||
if (!completion.ok) {
|
if (!completion.ok) {
|
||||||
notifyError(t("store.downloadError"))
|
notifyError(t("store.downloadError"))
|
||||||
}
|
}
|
||||||
|
|
@ -156,7 +100,7 @@ async function download(item: any) {
|
||||||
const json = await new TextDecoder().decode(value);
|
const json = await new TextDecoder().decode(value);
|
||||||
//console.log(json)
|
//console.log(json)
|
||||||
const res = parseJson(json)
|
const res = parseJson(json)
|
||||||
console.log(res)
|
//console.log(res)
|
||||||
if (res) {
|
if (res) {
|
||||||
if (res.progress) {
|
if (res.progress) {
|
||||||
item.progress = res.progress
|
item.progress = res.progress
|
||||||
|
|
@ -170,27 +114,27 @@ async function download(item: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function pauseApp(item: any) {
|
async function pauseApp(item: any) {
|
||||||
const res: any = await fetch(apiUrl + '/store/stop/' + item.name)
|
const res: any = await fetch(store.apiUrl + '/store/stop/' + item.name)
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const msg = await res.text()
|
const msg = await res.text()
|
||||||
notifyError(msg)
|
notifyError(msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await checkProgress()
|
await store.checkProgress()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
}
|
}
|
||||||
async function restartApp(item: any) {
|
async function restartApp(item: any) {
|
||||||
await fetch(apiUrl + '/store/restart/' + item.name)
|
await fetch(store.apiUrl + '/store/restart/' + item.name)
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await checkProgress()
|
await store.checkProgress()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
async function startApp(item: any) {
|
async function startApp(item: any) {
|
||||||
await fetch(apiUrl + '/store/start/' + item.name)
|
await fetch(store.apiUrl + '/store/start/' + item.name)
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await checkProgress()
|
await store.checkProgress()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -198,27 +142,27 @@ async function startApp(item: any) {
|
||||||
<div class="outer">
|
<div class="outer">
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="left-icon" v-for="(item, key) in categoryList" @click="changeCate(key, item)">
|
<div class="left-icon" v-for="(item, key) in store.categoryList" @click="store.changeCate(key, item)">
|
||||||
<div class="icon-derc" v-if="key == currentCateId"></div>
|
<div class="icon-derc" v-if="key == store.currentCateId"></div>
|
||||||
<el-tooltip class="box-item" effect="dark" :content="t('store.' + item)" placement="right">
|
<el-tooltip class="box-item" effect="dark" :content="t('store.' + item)" placement="right">
|
||||||
<el-icon size="22">
|
<el-icon size="22">
|
||||||
<component :is="categoryIcon[key]" />
|
<component :is="store.categoryIcon[key]" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="store">
|
<div class="store">
|
||||||
<div v-if="isready" class="store-top">
|
<div v-if="store.isready" class="store-top">
|
||||||
<div class="right-main">
|
<div class="right-main">
|
||||||
<div class="main-title">
|
<div class="main-title">
|
||||||
<span class="sub-title">{{ currentTitle }} </span>
|
<span class="sub-title">{{ store.currentTitle }} </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="main-app">
|
<div class="main-app">
|
||||||
<div v-for="item in storeList" v-if="currentCate != 'add'" class="store-item" :key="item.name">
|
<div v-for="item in store.storeList" v-if="store.currentCate != 'add'" class="store-item" :key="item.name">
|
||||||
<AppItem :item="item" :installed-list="installedList" :install="install" :uninstall="uninstall"
|
<AppItem :item="item" :installed-list="store.installedList" :install="install" :uninstall="uninstall"
|
||||||
:pause="pauseApp" :start="startApp" :restart="restartApp" />
|
:pause="pauseApp" :start="startApp" :restart="restartApp" />
|
||||||
</div>
|
</div>
|
||||||
<AddApp v-else />
|
<AddApp v-else :install="install" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
"hello": "hello",
|
"hello": "hello",
|
||||||
"startMenu.power": "Power",
|
"startMenu.power": "Power",
|
||||||
"setting": "Setting",
|
"setting": "Setting",
|
||||||
|
"progressmanger": "Progress",
|
||||||
"startMenu.shutdown": "Shutdown",
|
"startMenu.shutdown": "Shutdown",
|
||||||
"startMenu.recover": "Recover",
|
"startMenu.recover": "Recover",
|
||||||
"windows.setting": "Windows Setting",
|
"windows.setting": "Windows Setting",
|
||||||
|
|
@ -146,5 +147,21 @@
|
||||||
"others": "Others",
|
"others": "Others",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"errorList": "Cannot get the list of applications"
|
"errorList": "Cannot get the list of applications"
|
||||||
|
},
|
||||||
|
"process":{
|
||||||
|
"title":"Process Manager",
|
||||||
|
"port":"Port",
|
||||||
|
"pid":"PID",
|
||||||
|
"name":"Name",
|
||||||
|
"proto":"Protocol",
|
||||||
|
"action":"Actions"
|
||||||
|
},
|
||||||
|
"upgrade": {
|
||||||
|
"title": "New version",
|
||||||
|
"msg": "The new version is available, please update it now! Dont worry, the update is fast!",
|
||||||
|
"desc": "Prompt: Update will restore the default configuration",
|
||||||
|
"btnOne": "Cruel refusal",
|
||||||
|
"btnTwo": "Update now",
|
||||||
|
"btnTwoLoading": "Updating"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +147,29 @@
|
||||||
"utilities": "实用工具",
|
"utilities": "实用工具",
|
||||||
"others": "其他",
|
"others": "其他",
|
||||||
"add":"添加应用",
|
"add":"添加应用",
|
||||||
"errorList":"获取列表失败!"
|
"errorList":"获取列表失败!",
|
||||||
|
"urlEmpty":"网址为空",
|
||||||
|
"hasSameName":"存在相同名称的应用!",
|
||||||
|
"installError":"安装失败!",
|
||||||
|
"installSuccess":"安装成功!",
|
||||||
|
"uninstallSuccess":"卸载成功",
|
||||||
|
"downloadError":"下载失败!",
|
||||||
|
"cantStream":"暂不支持流下载!"
|
||||||
|
},
|
||||||
|
"process":{
|
||||||
|
"title":"进程管理",
|
||||||
|
"port":"端口",
|
||||||
|
"pid":"PID",
|
||||||
|
"name":"名称",
|
||||||
|
"proto":"类型",
|
||||||
|
"action":"操作"
|
||||||
|
},
|
||||||
|
"upgrade": {
|
||||||
|
"title": "新版本升级",
|
||||||
|
"msg": "新版本来啦,马上更新尝鲜吧!不用担心,更新很快的哦!",
|
||||||
|
"desc": "提示:更新会还原默认配置",
|
||||||
|
"btnOne": "残忍拒绝",
|
||||||
|
"btnTwo": "马上更新",
|
||||||
|
"btnTwoLoading": "更新中"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
108
frontend/src/stores/store.ts
Normal file
108
frontend/src/stores/store.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { t } from "@/i18n";
|
||||||
|
// import storeInitList from "@/assets/store.json";
|
||||||
|
import { getSystemKey } from "@/system/config";
|
||||||
|
export const useStoreStore = defineStore('storeStore', () => {
|
||||||
|
const currentCateId = ref(0)
|
||||||
|
const currentTitle = ref(t("store.hots"))
|
||||||
|
const currentCate = ref('hots')
|
||||||
|
const categoryList = ['hots', 'work', 'development', 'games', 'education', 'news', 'shopping', 'social', 'utilities', 'others', 'add']
|
||||||
|
const categoryIcon = ['HomeFilled', 'Odometer', 'Postcard', 'TrendCharts', 'School', 'HelpFilled', 'ShoppingCart', 'ChatLineRound', 'MessageBox', 'Ticket', 'CirclePlusFilled']
|
||||||
|
const isready = ref(false);
|
||||||
|
const installed = getSystemKey("intstalledPlugins");
|
||||||
|
const apiUrl = getSystemKey("apiUrl");
|
||||||
|
const installedList: any = ref(installed);
|
||||||
|
const storeList: any = ref([])
|
||||||
|
const outList: any = ref([])
|
||||||
|
async function getList() {
|
||||||
|
if (currentCate.value == 'add') return;
|
||||||
|
//storeList.value = storeInitList
|
||||||
|
const storeUrl = apiUrl + '/store/storelist?cate=' + currentCate.value
|
||||||
|
const res = await fetch(storeUrl)
|
||||||
|
if (!res.ok) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let list:any = await res.json()
|
||||||
|
//console.log(data)
|
||||||
|
if (outList.value.length > 0 && currentCate.value == 'hots') {
|
||||||
|
const names = list.value.map((item : any) => item.name)
|
||||||
|
const adds:any = []
|
||||||
|
outList.value.forEach((item : any) => {
|
||||||
|
if (!names.includes(item.name)) {
|
||||||
|
adds.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
list = adds.concat(list)
|
||||||
|
}
|
||||||
|
storeList.value = list
|
||||||
|
await checkProgress()
|
||||||
|
isready.value = true;
|
||||||
|
}
|
||||||
|
async function addOutList(item:any) {
|
||||||
|
const has = outList.value.find((i:any) => i.name === item.name)
|
||||||
|
if(!has) {
|
||||||
|
item.isOut = true
|
||||||
|
outList.value.push(item)
|
||||||
|
await getList()
|
||||||
|
return true
|
||||||
|
}else{
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function changeCate(index: number, item: string) {
|
||||||
|
currentCateId.value = index
|
||||||
|
currentCate.value = item
|
||||||
|
currentTitle.value = t("store." + item)
|
||||||
|
await getList()
|
||||||
|
}
|
||||||
|
async function checkProgress() {
|
||||||
|
const completion: any = await fetch(apiUrl + '/store/listporgress')
|
||||||
|
if (!completion.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let res: any = await completion.json()
|
||||||
|
if (!res || res.length < 1) {
|
||||||
|
res = []
|
||||||
|
}
|
||||||
|
storeList.value.forEach((item: any, index: number) => {
|
||||||
|
const pitem: any = res.find((i: any) => i.name == item.name)
|
||||||
|
//console.log(pitem)
|
||||||
|
if (pitem) {
|
||||||
|
storeList.value[index].isRuning = pitem.running
|
||||||
|
} else {
|
||||||
|
storeList.value[index].isRuning = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentCateId,
|
||||||
|
categoryIcon,
|
||||||
|
currentTitle,
|
||||||
|
currentCate,
|
||||||
|
categoryList,
|
||||||
|
isready,
|
||||||
|
installedList,
|
||||||
|
storeList,
|
||||||
|
outList,
|
||||||
|
apiUrl,
|
||||||
|
changeCate,
|
||||||
|
getList,
|
||||||
|
addOutList,
|
||||||
|
checkProgress
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
persist: {
|
||||||
|
enabled: true,
|
||||||
|
strategies: [
|
||||||
|
{
|
||||||
|
storage: localStorage,
|
||||||
|
paths: [
|
||||||
|
"outList"
|
||||||
|
]
|
||||||
|
}, // name 字段用localstorage存储
|
||||||
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
88
frontend/src/stores/upgrade.ts
Normal file
88
frontend/src/stores/upgrade.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { getSystemKey, setSystemKey, parseJson, getSystemConfig } from '@/system/config'
|
||||||
|
import { RestartApp } from '@/util/goutil';
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
export const useUpgradeStore = defineStore('upgradeStore', () => {
|
||||||
|
const hasUpgrade = ref(false);
|
||||||
|
const hasNotice = ref(false);
|
||||||
|
const hasAd = ref(false);
|
||||||
|
const updateUrl = ref('');
|
||||||
|
const versionTag = ref(0)
|
||||||
|
const currentVersion = ref(0)
|
||||||
|
const progress = ref(0)
|
||||||
|
const noticeList:any = ref([])
|
||||||
|
const adList:any = ref([])
|
||||||
|
async function checkUpdate() {
|
||||||
|
const config = getSystemConfig();
|
||||||
|
currentVersion.value = config.version;
|
||||||
|
const releaseRes = await fetch(`${config.apiUrl}/system/updateInfo`)
|
||||||
|
if (!releaseRes.ok) return;
|
||||||
|
const releaseData = await releaseRes.json()
|
||||||
|
versionTag.value = releaseData.version
|
||||||
|
if(versionTag.value > config.version){
|
||||||
|
hasUpgrade.value = true
|
||||||
|
updateUrl.value = releaseData.url
|
||||||
|
}
|
||||||
|
if (releaseData.noticeList && releaseData.noticeList.length > 0) {
|
||||||
|
hasNotice.value = true
|
||||||
|
noticeList.value = releaseData.noticeList
|
||||||
|
}
|
||||||
|
if (!hasUpgrade.value && releaseData.adList && releaseData.adList.length > 0) {
|
||||||
|
hasAd.value = true
|
||||||
|
adList.value = releaseData.adList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function update() {
|
||||||
|
const apiUrl = getSystemKey('apiUrl')
|
||||||
|
const upUrl = `${apiUrl}/system/update?url=${updateUrl.value}`
|
||||||
|
const upRes = await fetch(upUrl)
|
||||||
|
if (!upRes.ok) return;
|
||||||
|
const reader: any = upRes.body?.getReader();
|
||||||
|
if (!reader) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: "the system has not stream!"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
reader.releaseLock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const rawjson = new TextDecoder().decode(value);
|
||||||
|
const json = parseJson(rawjson);
|
||||||
|
//console.log(json)
|
||||||
|
if (json) {
|
||||||
|
if (json.progress) {
|
||||||
|
progress.value = json.progress
|
||||||
|
}
|
||||||
|
if (json.updateCompleted) {
|
||||||
|
hasUpgrade.value = false
|
||||||
|
progress.value = 0
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: "update completed!"
|
||||||
|
})
|
||||||
|
setSystemKey('version', versionTag.value)
|
||||||
|
currentVersion.value = versionTag.value
|
||||||
|
RestartApp()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
hasUpgrade,
|
||||||
|
hasNotice,
|
||||||
|
hasAd,
|
||||||
|
versionTag,
|
||||||
|
updateUrl,
|
||||||
|
noticeList,
|
||||||
|
adList,
|
||||||
|
progress,
|
||||||
|
checkUpdate,
|
||||||
|
update
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -57,6 +57,19 @@ export const appList = [
|
||||||
isMagnet: true,
|
isMagnet: true,
|
||||||
isMenuList: true,
|
isMenuList: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'process.title',
|
||||||
|
appIcon: "progress",
|
||||||
|
content: "ProcessManager",
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
frame: true,
|
||||||
|
center: true,
|
||||||
|
resizable: false,
|
||||||
|
isDeskTop: false,
|
||||||
|
isMagnet: true,
|
||||||
|
isMenuList: true,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "create.shortcut",
|
name: "create.shortcut",
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import { Tray, TrayOptions } from './menu/Tary';
|
||||||
import { InitSystemFile, InitUserFile } from './core/SystemFileConfig';
|
import { InitSystemFile, InitUserFile } from './core/SystemFileConfig';
|
||||||
import { createInitFile } from './core/createInitFile';
|
import { createInitFile } from './core/createInitFile';
|
||||||
import { getSystemConfig, getSystemKey, setSystemKey, setSystemConfig, clearSystemConfig } from './config'
|
import { getSystemConfig, getSystemKey, setSystemKey, setSystemConfig, clearSystemConfig } from './config'
|
||||||
import { checkUpdate } from '@/util/update';
|
import { useUpgradeStore } from '@/stores/upgrade';
|
||||||
import { RestartApp } from '@/util/goutil';
|
import { RestartApp } from '@/util/goutil';
|
||||||
|
|
||||||
export type OsPlugin = (system: System) => void;
|
export type OsPlugin = (system: System) => void;
|
||||||
|
|
@ -115,7 +115,8 @@ export class System {
|
||||||
if (this._rootState.magnet?.length < 1) {
|
if (this._rootState.magnet?.length < 1) {
|
||||||
this.refershAppList()
|
this.refershAppList()
|
||||||
}
|
}
|
||||||
checkUpdate();
|
const upgradeStore = useUpgradeStore();
|
||||||
|
upgradeStore.checkUpdate()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this._rootState.magnet?.length < 1) {
|
if (this._rootState.magnet?.length < 1) {
|
||||||
this.recover()
|
this.recover()
|
||||||
|
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
import { getSystemConfig,setSystemKey,parseJson } from '@/system/config'
|
|
||||||
import { RestartApp } from './goutil';
|
|
||||||
import { Dialog } from '@/system';
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
export async function checkUpdate() {
|
|
||||||
const config = getSystemConfig();
|
|
||||||
const updateGiteeUrl = `${config.apiUrl}/system/updateInfo`
|
|
||||||
const releaseRes = await fetch(updateGiteeUrl)
|
|
||||||
if (!releaseRes.ok) return;
|
|
||||||
const releaseData = await releaseRes.json()
|
|
||||||
const versionTag = releaseData.version;
|
|
||||||
if (!versionTag) return;
|
|
||||||
if (versionTag <= config.version) return;
|
|
||||||
const updateUrl = releaseData.url
|
|
||||||
if (!updateUrl || updateUrl == '') return;
|
|
||||||
const dialogRes: any = await Dialog.showMessageBox({
|
|
||||||
title: '更新提示',
|
|
||||||
message: `发现新版本:${versionTag},是否更新?`
|
|
||||||
})
|
|
||||||
//console.log(dialogRes)
|
|
||||||
if (dialogRes.response !== -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { setProgress,dialogwin } = Dialog.showProcessDialog({
|
|
||||||
message: '正在更新',
|
|
||||||
});
|
|
||||||
const upUrl = `${config.apiUrl}/system/update?url=${updateUrl}`
|
|
||||||
const upRes = await fetch(upUrl)
|
|
||||||
if (!upRes.ok) return;
|
|
||||||
const reader: any = upRes.body?.getReader();
|
|
||||||
if (!reader) {
|
|
||||||
ElMessage({
|
|
||||||
type: 'error',
|
|
||||||
message: "the system has not stream!"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = await reader.read();
|
|
||||||
if (done) {
|
|
||||||
reader.releaseLock();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const rawjson = new TextDecoder().decode(value);
|
|
||||||
const json = parseJson(rawjson);
|
|
||||||
console.log(json)
|
|
||||||
if(json){
|
|
||||||
if(json.progress){
|
|
||||||
setProgress(json.progress)
|
|
||||||
}
|
|
||||||
if(json.updateCompleted){
|
|
||||||
dialogwin.close()
|
|
||||||
ElMessage({
|
|
||||||
type: 'success',
|
|
||||||
message: "update completed!"
|
|
||||||
})
|
|
||||||
setSystemKey('version',versionTag)
|
|
||||||
RestartApp()
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -39,14 +39,16 @@ func OsStart() {
|
||||||
progressRouter.HandleFunc("/stopall", store.StopAll).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/stopall", store.StopAll).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/restart/{name}", store.ReStartProcess).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/restart/{name}", store.ReStartProcess).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/listporgress", store.Status).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/listporgress", store.Status).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/listport", store.ListPortsHandler).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/listport", store.ListAllProcessesHandler).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/killport", store.KillPortHandler).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/killport", store.KillProcessByNameHandler).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/storelist", store.GetStoreListHandler).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/storelist", store.GetStoreListHandler).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/download", store.DownloadHandler).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/download", store.DownloadHandler).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/install", store.InstallHandler).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/install", store.InstallHandler).Methods(http.MethodGet)
|
||||||
|
progressRouter.HandleFunc("/installInfo", store.GetInstallInfoHandler).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/installOut", store.RunOutHandler).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/installOut", store.RunOutHandler).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/uninstall", store.UnInstallHandler).Methods(http.MethodGet)
|
progressRouter.HandleFunc("/uninstall", store.UnInstallHandler).Methods(http.MethodGet)
|
||||||
progressRouter.HandleFunc("/setting", store.StoreSettingHandler).Methods(http.MethodPost)
|
progressRouter.HandleFunc("/setting", store.StoreSettingHandler).Methods(http.MethodPost)
|
||||||
|
progressRouter.HandleFunc("/upload", store.UploadHandler).Methods(http.MethodPost)
|
||||||
|
|
||||||
router.HandleFunc("/system/updateInfo", sys.GetUpdateUrlHandler).Methods(http.MethodGet)
|
router.HandleFunc("/system/updateInfo", sys.GetUpdateUrlHandler).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/system/update", sys.UpdateAppHandler).Methods(http.MethodGet)
|
router.HandleFunc("/system/update", sys.UpdateAppHandler).Methods(http.MethodGet)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ type APIResponse struct {
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeJSONResponse(w http.ResponseWriter, res APIResponse, status int) {
|
func WriteJSONResponse(w http.ResponseWriter, res APIResponse, status int) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
json.NewEncoder(w).Encode(res)
|
json.NewEncoder(w).Encode(res)
|
||||||
|
|
@ -20,11 +20,11 @@ func writeJSONResponse(w http.ResponseWriter, res APIResponse, status int) {
|
||||||
|
|
||||||
// HTTPError 返回带有JSON错误消息的HTTP错误
|
// HTTPError 返回带有JSON错误消息的HTTP错误
|
||||||
func HTTPError(w http.ResponseWriter, status int, message string) {
|
func HTTPError(w http.ResponseWriter, status int, message string) {
|
||||||
writeJSONResponse(w, APIResponse{Message: message, Code: -1}, status)
|
WriteJSONResponse(w, APIResponse{Message: message, Code: -1}, status)
|
||||||
}
|
}
|
||||||
func ErrorMsg(w http.ResponseWriter, message string) {
|
func ErrorMsg(w http.ResponseWriter, message string) {
|
||||||
writeJSONResponse(w, APIResponse{Message: message, Code: -1}, 200)
|
WriteJSONResponse(w, APIResponse{Message: message, Code: -1}, 200)
|
||||||
}
|
}
|
||||||
func SuccessMsg(w http.ResponseWriter, data any, message string) {
|
func SuccessMsg(w http.ResponseWriter, data any, message string) {
|
||||||
writeJSONResponse(w, APIResponse{Message: message, Data: data, Code: 0}, 200)
|
WriteJSONResponse(w, APIResponse{Message: message, Data: data, Code: 0}, 200)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,21 @@ func InstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
libs.ErrorMsg(w, "the install.json is error:"+err.Error())
|
libs.ErrorMsg(w, "the install.json is error:"+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(installInfo.Dependencies) > 0 {
|
||||||
|
var needInstalls []Item
|
||||||
|
for _, item := range installInfo.Dependencies {
|
||||||
|
info, err := GetInstallInfo(item.Value.(string))
|
||||||
|
if err != nil {
|
||||||
|
needInstalls = append(needInstalls, item)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if info.Version != installInfo.Version {
|
||||||
|
needInstalls = append(needInstalls, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
libs.WriteJSONResponse(w, libs.APIResponse{Message: "you need install apps!", Data: needInstalls, Code: -1}, 200)
|
||||||
|
return
|
||||||
|
}
|
||||||
if pluginName != installInfo.Name {
|
if pluginName != installInfo.Name {
|
||||||
libs.ErrorMsg(w, "the app name must equal the install.json!")
|
libs.ErrorMsg(w, "the app name must equal the install.json!")
|
||||||
return
|
return
|
||||||
|
|
@ -78,7 +93,6 @@ func InstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
libs.ErrorMsg(w, "install the app is error!")
|
libs.ErrorMsg(w, "install the app is error!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var res string
|
|
||||||
//复制static目录
|
//复制static目录
|
||||||
staticPath := filepath.Join(exePath, "static")
|
staticPath := filepath.Join(exePath, "static")
|
||||||
if libs.PathExists(staticPath) {
|
if libs.PathExists(staticPath) {
|
||||||
|
|
@ -91,12 +105,12 @@ func InstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
iconPath := filepath.Join(targetPath, storeInfo.Icon)
|
iconPath := filepath.Join(targetPath, storeInfo.Icon)
|
||||||
if libs.PathExists(iconPath) {
|
if libs.PathExists(iconPath) {
|
||||||
res = "http://localhost:56780/static/" + pluginName + "/" + storeInfo.Icon
|
installInfo.Icon = "http://localhost:56780/static/" + pluginName + "/" + storeInfo.Icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
libs.SuccessMsg(w, res, "install the app success!")
|
libs.SuccessMsg(w, installInfo, "install the app success!")
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnInstallHandler(w http.ResponseWriter, r *http.Request) {
|
func UnInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,34 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PortRangeResponse struct {
|
type ProcessSystemInfo struct {
|
||||||
Start int `json:"start"`
|
PID int `json:"pid"`
|
||||||
End int `json:"end"`
|
Port int `json:"port"`
|
||||||
EnabledPorts []int `json:"enabled_ports"`
|
Proto string `json:"proto"`
|
||||||
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProcessIdsOnPort(port int) ([]string, error) {
|
type AllProcessesResponse struct {
|
||||||
|
Processes []ProcessSystemInfo `json:"processes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var processInfoRegex = regexp.MustCompile(`(\d+)\s+.*:\s*(\d+)\s+.*LISTEN\s+.*:(\d+)`)
|
||||||
|
|
||||||
|
func listAllProcesses() ([]ProcessSystemInfo, error) {
|
||||||
osType := runtime.GOOS
|
osType := runtime.GOOS
|
||||||
|
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
|
|
@ -27,55 +37,115 @@ func getProcessIdsOnPort(port int) ([]string, error) {
|
||||||
|
|
||||||
switch osType {
|
switch osType {
|
||||||
case "darwin", "linux":
|
case "darwin", "linux":
|
||||||
cmd = exec.Command("lsof", "-ti", fmt.Sprintf("tcp:%d", port))
|
cmd = exec.Command("lsof", "-i", "-n", "-P")
|
||||||
case "windows":
|
case "windows":
|
||||||
cmd = exec.Command("powershell", "-Command", "Get-Process | Where-Object {$_.Id -eq "+strconv.Itoa(port)+"} | Select-Object -ExpandProperty Id")
|
cmd = exec.Command("netstat", "-ano")
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported operating system")
|
return nil, fmt.Errorf("unsupported operating system")
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err = cmd.CombinedOutput()
|
output, err = cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if exitError, ok := err.(*exec.ExitError); ok {
|
return nil, fmt.Errorf("failed to list all processes: %v", err)
|
||||||
// 如果lsof或powershell命令找不到任何进程,它会返回非零退出代码,这是正常情况
|
|
||||||
if exitError.ExitCode() != 1 {
|
|
||||||
return nil, fmt.Errorf("failed to list processes on port %d: %v", port, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("failed to list processes on port %d: %v", port, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pids := strings.Fields(strings.TrimSpace(string(output)))
|
processes := make([]ProcessSystemInfo, 0)
|
||||||
return pids, nil
|
|
||||||
}
|
|
||||||
func listEnabledPorts(portRangeStart, portRangeEnd int) ([]int, error) {
|
|
||||||
var usedPorts []int
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for i := portRangeStart; i <= portRangeEnd; i++ {
|
// 解析输出
|
||||||
currentPort := i // 创建一个新的变量来绑定当前的i值
|
switch osType {
|
||||||
wg.Add(1)
|
case "darwin", "linux":
|
||||||
go func() { // 注意这里不再直接传入port,而是使用currentPort
|
scanner := bufio.NewScanner(bytes.NewBuffer(output)) // 使用bufio.Scanner
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
pids, err := getProcessIdsOnPort(currentPort)
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
matches := processInfoRegex.FindStringSubmatch(line)
|
||||||
|
if matches != nil {
|
||||||
|
pid, _ := strconv.Atoi(matches[1])
|
||||||
|
port, _ := strconv.Atoi(matches[3])
|
||||||
|
processName, err := getProcessName(osType, pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error checking port %d: %v", currentPort, err)
|
log.Printf("Failed to get process name for PID %d: %v", pid, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
processes = append(processes, ProcessSystemInfo{
|
||||||
|
PID: pid,
|
||||||
|
Port: port,
|
||||||
|
Proto: matches[2],
|
||||||
|
Name: processName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "windows":
|
||||||
|
scanner := bufio.NewScanner(bytes.NewBuffer(output))
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
// 需要针对Windows的netstat输出格式进行解析
|
||||||
|
// 示例:TCP 0.0.0.0:80 0.0.0.0:* LISTENING 1234
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 4 && fields[3] == "LISTENING" {
|
||||||
|
_, port, err := net.SplitHostPort(fields[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to parse port: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pid, _ := strconv.Atoi(fields[4])
|
||||||
|
processName, err := getProcessName(osType, pid)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to get process name for PID %d: %v", pid, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
portInt, err := strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to convert port to integer: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
processes = append(processes, ProcessSystemInfo{
|
||||||
|
PID: pid,
|
||||||
|
Port: portInt,
|
||||||
|
Proto: fields[0],
|
||||||
|
Name: processName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(pids) > 0 {
|
return processes, nil
|
||||||
usedPorts = append(usedPorts, currentPort)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
func getProcessName(osType string, pid int) (string, error) {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
var output []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
return usedPorts, nil
|
switch osType {
|
||||||
|
case "darwin", "linux":
|
||||||
|
cmd = exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "comm=")
|
||||||
|
case "windows":
|
||||||
|
cmd = exec.Command("tasklist", "/FI", fmt.Sprintf("PID eq %d", pid), "/NH")
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported operating system")
|
||||||
}
|
}
|
||||||
|
|
||||||
func killProcess(pid int) error {
|
output, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get process name: %v", err)
|
||||||
|
}
|
||||||
|
// log.Printf("output: %s", output)
|
||||||
|
switch osType {
|
||||||
|
case "darwin", "linux":
|
||||||
|
return strings.TrimSpace(string(output)), nil
|
||||||
|
case "windows":
|
||||||
|
parts := strings.Fields(string(output))
|
||||||
|
if len(parts) >= 1 {
|
||||||
|
return parts[0], nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no process name found in output")
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("unknown error getting process name")
|
||||||
|
}
|
||||||
|
|
||||||
|
func killProcessByName(name string) error {
|
||||||
osType := runtime.GOOS
|
osType := runtime.GOOS
|
||||||
|
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
|
|
@ -83,90 +153,42 @@ func killProcess(pid int) error {
|
||||||
|
|
||||||
switch osType {
|
switch osType {
|
||||||
case "darwin", "linux":
|
case "darwin", "linux":
|
||||||
cmd = exec.Command("kill", "-9", strconv.Itoa(pid))
|
cmd = exec.Command("pkill", name)
|
||||||
case "windows":
|
case "windows":
|
||||||
cmd = exec.Command("taskkill", "/F", "/PID", strconv.Itoa(pid)) // /F 表示强制结束
|
cmd = exec.Command("taskkill", "/IM", name, "/F") // /F 表示强制结束
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported operating system")
|
return fmt.Errorf("unsupported operating system")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to kill process with PID %d: %v", pid, err)
|
log.Printf("Failed to kill process with name %s: %v", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func killAllProcessesOnPort(port int, w http.ResponseWriter) {
|
func KillProcessByNameHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
pids, err := getProcessIdsOnPort(port)
|
name := r.URL.Query().Get("name")
|
||||||
|
if err := killProcessByName(name); err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Failed to kill process: %v", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "Process '%s' has been killed", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListAllProcessesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
processes, err := listAllProcesses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Failed to list processes", http.StatusInternalServerError)
|
http.Error(w, "Failed to list all processes", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pidStr := range pids {
|
response := AllProcessesResponse{
|
||||||
if pidStr == "" {
|
Processes: processes,
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pidInt, err := strconv.Atoi(pidStr)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to convert PID to integer: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := killProcess(pidInt); err != nil {
|
|
||||||
log.Printf("Failed to kill process with PID %d: %v", pidInt, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "All processes on port %d have been killed", port)
|
|
||||||
}
|
|
||||||
func KillPortHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
portStr := r.URL.Query().Get("port")
|
|
||||||
port, err := strconv.Atoi(portStr)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Invalid port number", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
killAllProcessesOnPort(port, w)
|
|
||||||
}
|
|
||||||
func ListPortsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
startStr := r.URL.Query().Get("start")
|
|
||||||
endStr := r.URL.Query().Get("end")
|
|
||||||
|
|
||||||
// 设置默认值
|
|
||||||
start := 56711
|
|
||||||
end := 56730
|
|
||||||
|
|
||||||
// 如果参数存在,则尝试转换为整数,否则使用默认值
|
|
||||||
if startStr != "" {
|
|
||||||
start, _ = strconv.Atoi(startStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if endStr != "" {
|
|
||||||
end, _ = strconv.Atoi(endStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
ports, err := listEnabledPorts(start, end)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Failed to list ports", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构造JSON响应结构体
|
|
||||||
response := PortRangeResponse{
|
|
||||||
Start: start,
|
|
||||||
End: end,
|
|
||||||
EnabledPorts: ports,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置响应内容类型为JSON
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
// 编码并写入响应体
|
|
||||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
http.Error(w, "Failed to encode response as JSON", http.StatusInternalServerError)
|
http.Error(w, "Failed to encode response as JSON", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,19 @@ func GetStoreListHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func GetInstallInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
name := r.URL.Query().Get("name")
|
||||||
|
if name == "" {
|
||||||
|
libs.ErrorMsg(w, "name is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info, err := GetInstallInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
libs.ErrorMsg(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
libs.SuccessMsg(w, info, "")
|
||||||
|
}
|
||||||
func StoreSettingHandler(w http.ResponseWriter, r *http.Request) {
|
func StoreSettingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var req map[string]any
|
var req map[string]any
|
||||||
err := json.NewDecoder(r.Body).Decode(&req)
|
err := json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ type InstallInfo struct {
|
||||||
CheckProgress bool `json:"checkProgress"` // 标志位,表示是否显示启动和停止。
|
CheckProgress bool `json:"checkProgress"` // 标志位,表示是否显示启动和停止。
|
||||||
HasRestart bool `json:"hasRestart"` // 标志位,表示安装后是否需要重启。
|
HasRestart bool `json:"hasRestart"` // 标志位,表示安装后是否需要重启。
|
||||||
Setting bool `json:"setting"` // 标志位,表示是否需要配置。
|
Setting bool `json:"setting"` // 标志位,表示是否需要配置。
|
||||||
|
Dependencies []Item `json:"dependencies"` // 依赖项。
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoreInfo 维护了应用程序商店的信息。
|
// StoreInfo 维护了应用程序商店的信息。
|
||||||
|
|
|
||||||
66
godo/store/upload.go
Normal file
66
godo/store/upload.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"godo/files"
|
||||||
|
"godo/libs"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UploadHandler 处理上传的HTTP请求
|
||||||
|
func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
// 解析上传的文件
|
||||||
|
err := r.ParseMultipartForm(10000 << 20) // 限制最大上传大小为1000MB
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "上传文件过大"+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, header, err := r.FormFile("files") // 表单字段名为"files"
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "没有找到文件", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// 读取文件内容
|
||||||
|
fileBytes, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("读取文件内容出错: %v", err)
|
||||||
|
http.Error(w, "读取文件内容出错", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cachePath := libs.GetCacheDir()
|
||||||
|
baseName := filepath.Base(header.Filename)
|
||||||
|
filenameNoExt := strings.TrimSuffix(baseName, filepath.Ext(baseName))
|
||||||
|
|
||||||
|
savePath := filepath.Join(cachePath, baseName)
|
||||||
|
|
||||||
|
out, err := os.Create(savePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("创建文件出错: %v", err)
|
||||||
|
http.Error(w, "保存文件出错", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
// 将文件内容写入到服务器上的文件
|
||||||
|
_, err = out.Write(fileBytes)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("写入文件出错: %v", err)
|
||||||
|
http.Error(w, "写入文件出错", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
runDir := libs.GetRunDir()
|
||||||
|
err = files.HandlerFile(savePath, runDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error moving file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
libs.SuccessMsg(w, filenameNoExt, "File already exists and is of correct size")
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue