Browse Source

change choosefile

master v1.0.0
godo 10 months ago
parent
commit
f2753de7b6
  1. 97
      README.md
  2. 220
      frontend/src/components/builtin/FileList.vue
  3. 14
      frontend/src/components/computer/Computer.vue
  4. 8
      frontend/src/stores/choose.ts

97
README.md

@ -5,7 +5,7 @@
<h1 align="center">GodoOS</h1>
<div align="center">
一款高效的内网办公平台,内含word/excel/ppt/pdf/内网聊天/白板/思维导图等多个办公系统工具,支持原生文件存储,满足多样化需求。平台界面精仿windows风格,操作简便,同时保持低资源消耗和高性能运行。无需注册即可自动连接内网用户,实现即时通讯和文件共享。
一款高效的内网办公平台,内含word/excel/ppt/pdf/内网聊天/白板/思维导图等多个办公系统工具,支持原生文件存储。平台界面精仿windows风格,操作简便,同时保持低资源消耗和高性能运行。无需注册即可自动连接内网用户,实现即时通讯和文件共享。
[![license][license-image]][license-url]
@ -26,31 +26,110 @@
[Windows-image]: ./docs/img/Windows.svg
[Windows-url]: https://github.com/josStorer/RWKV-Runner/blob/master/build/windows/Readme_Install.txt
[Windows-url]: https://
[MacOS-image]: ./docs/img/MacOS.svg
[MacOS-url]: https://github.com/josStorer/RWKV-Runner/blob/master/build/darwin/Readme_Install.txt
[MacOS-url]: https://
[Linux-image]: ./docs/img/Linux.svg
[Linux-url]: https://github.com/josStorer/RWKV-Runner/blob/master/build/linux/Readme_Install.txt
[Linux-url]: https://
</div>
## 功能说明
一、系统桌面
### 一、系统桌面
- 精仿windows风格
- 桌面文件管理
- 支持原生文件拖拽
- 支持原生文件拖拽上传
<img src="./docs/img/home.png" width="600" />
二、文件管理
- 文件上传下载
### 二、文件管理
- 文件拖拽上传
- 文件搜索
- 原生文件存储
<img src="./docs/img/file.png" width="600" />
三、内网聊天
### 三、内网聊天
- 无需繁琐的注册流程,只需在同一局域网内,即可自动发现并列出所有可用的聊天对象,即刻开启即时通讯。支持基于局域网的即时消息传输、文件传输等功能,促进团队内部的无缝沟通与协作。
<img src="./docs/img/localchat.png" width="600" />
### 四、文档
- 简便的word编辑器,原生存储,支持二维码、手写签名,导入导出
<img src="./docs/img/doc.png" width="600" />
### 五、表格
- 原生存储,Excel编辑器,支持导入、导出,支持图片、公式
<img src="./docs/img/excel.png" width="600" />
### 六、markdown
- 原生存储,采用vditor,支持导入、导出,支持大纲、实时预览
<img src="./docs/img/markdown.png" width="600" />
### 七、思维导图
- 内置多种主题;支持快捷键;节点内容支持图片、图标、超链接、备注、标签。
<img src="./docs/img/mind.png" width="600" />
### 八、演示文稿
- 原生存储,采用pptist,支持文字、图片、形状、线条、图表、表格、视频、公式等。
<img src="./docs/img/ppt.png" width="600" />
### 九、文件编辑器
- 原生存储,支持打开text/html/css/js/svg/xml/md等,可以当作一个简单的在线editplus。
<img src="./docs/img/fileeditor.png" width="600" />
### 十、白板
- 集自由布局、画笔、便签多种创意表达能力于一体,激发团队创造力,随时随地,围绕一块白板沟通。
<img src="./docs/img/baiban.png" width="600" />
### 十一、图片编辑
- 一个小型的photoshop,原生存储,支持搜索图片,支持图片裁剪、旋转、缩放、滤镜等功能
<img src="./docs/img/pic.png" width="600" />
### 十二、甘特图
- 项目管理必备工具,支持自定义项目人员和角色,支持拖拽/管理分配(资源、角色、工作)等。
<img src="./docs/img/gant.png" width="600" />
### 十三、浏览器
- 一款简单的内置浏览器
<img src="./docs/img/ie.png" width="600" />
### 十四、系统设置
- 可在这里切换存储方式,可切换系统背景。
<img src="./docs/img/setting-store.png" width="600" />
### 十五、应用商店
- 应用商店管理,可添加外部应用。
### 十六、截图
- 一个简单的截图工具。截图后文件存到本地。
### 十七、录屏
- 一个简单的录屏工具。录屏后录后文件存到本地。
### 十八、计算器
- 一个仿windows10的计算器,支持历史记录。
<img src="./docs/img/cal.png" width="600" />
### 十九、音乐库
- 一个简单的声音存储库,支持播放音乐。
### 二十、图片库
- 一个简单的图片存储库,支持查看图片
### 二十一、看板
- 支持标准看板,项目管理必备工具,可快速创建看板并放置到不同的文件夹
<img src="./docs/img/kanban.png" width="600" />
## 开发
## 感谢
- [element-plus](http://element-plus.org/)
- [vue3](https://v3.cn.vuejs.org/)
- [wails](https://wails.io/)
- [pptist](https://github.com/pipipi-pikachu/PPTist)
- [vditor](https://github.com/Vanessa219/vditor)
- [mind-map](https://github.com/wanglin2/mind-map)

220
frontend/src/components/builtin/FileList.vue

@ -17,62 +17,36 @@
</div>
</div>
</template>
<div
draggable="true"
class="file-item"
:class="{
chosen: chosenIndexs.includes(index),
'no-chosen': !chosenIndexs.includes(index),
'mode-icon': mode === 'icon',
'mode-list': mode === 'list',
'mode-big': mode === 'big',
'mode-middle': mode === 'middle',
'mode-detail': mode === 'detail',
'drag-over': hoverIndex === index,
}"
:style="{
<div draggable="true" class="file-item" :class="{
chosen: chosenIndexs.includes(index),
'no-chosen': !chosenIndexs.includes(index),
'mode-icon': mode === 'icon',
'mode-list': mode === 'list',
'mode-big': mode === 'big',
'mode-middle': mode === 'middle',
'mode-detail': mode === 'detail',
'drag-over': hoverIndex === index,
}" :style="{
'--theme-color': theme === 'light' ? '#ffffff6b' : '#3bdbff3d',
}"
v-for="(item, index) in fileList"
:key="item.path"
@dblclick="handleOnOpen(item)"
@touchstart.passive="doubleTouch($event, item)"
@contextmenu.stop.prevent="handleRightClick($event, item, index)"
@drop="hadnleDrop($event, item.path)"
@dragenter.prevent="handleDragEnter(index)"
@dragover.prevent
@dragleave="handleDragLeave()"
@dragstart.stop="startDragApp($event, item)"
@click="handleClick(index)"
@mousedown.stop
:ref="
(ref:any) => {
}" v-for="(item, index) in fileList" :key="item.path" @dblclick="handleOnOpen(item)"
@touchstart.passive="doubleTouch($event, item)" @contextmenu.stop.prevent="handleRightClick($event, item, index)"
@drop="hadnleDrop($event, item.path)" @dragenter.prevent="handleDragEnter(index)" @dragover.prevent
@dragleave="handleDragLeave()" @dragstart.stop="startDragApp($event, item)" @click="handleClick(index)"
@mousedown.stop :ref="(ref: any) => {
if (ref) {
appPositions[index] = markRaw(ref as Element);
}
}
"
>
">
<div class="file-item_img">
<FileIcon :file="item" />
</div>
<span v-if="editIndex !== index" class="file-item_title">
{{ getName(item) }}
</span>
<textarea
autofocus
draggable="false"
@dragover.stop
@dragstart.stop
@dragenter.stop
@mousedown.stop
@dblclick.stop
@click.stop
@blur="onEditNameEnd"
v-if="editIndex === index"
class="file-item_title file-item_editing"
v-model="editName"
></textarea>
<textarea autofocus draggable="false" @dragover.stop @dragstart.stop @dragenter.stop @mousedown.stop @dblclick.stop
@click.stop @blur="onEditNameEnd" v-if="editIndex === index" class="file-item_title file-item_editing"
v-model="editName"></textarea>
<template v-if="mode === 'detail'">
<div class="file-item_type">
<span>{{ item.isDirectory ? "-" : dealSize(item.size) }}</span>
@ -90,11 +64,9 @@
</div>
</template>
<script lang="ts" setup>
import { useSystem,basename, dirname, join,OsFileWithoutContent } from '@/system/index.ts';
import { useSystem, basename, dirname, join, OsFileWithoutContent } from '@/system/index.ts';
import { emitEvent, mountEvent } from '@/system/event';
import { useContextMenu } from '@/hook/useContextMenu.ts';
// import { basename, dirname, join } from '@/system/core/Path';
// import { OsFileWithoutContent } from '@/system/core/FileSystem';
import { i18n, dealSystemName } from '@/i18n';
import { useFileDrag } from '@/hook/useFileDrag';
import { useAppMenu } from '@/hook/useAppMenu';
@ -103,9 +75,11 @@ import { Rect } from '@/hook/useRectChosen';
import { throttle } from '@/util/debounce';
import { dealSize } from '@/util/file';
import { Menu } from '@/system/menu/Menu';
const { openPropsWindow, copyFile, createLink,deleteFile } = useContextMenu();
import { useChooseStore } from "@/stores/choose";
const { openPropsWindow, copyFile, createLink, deleteFile } = useContextMenu();
const sys = useSystem();
const { startDrag, folderDrop } = useFileDrag(sys);
const choose = useChooseStore()
const props = defineProps({
onChosen: {
type: Function,
@ -137,20 +111,26 @@ const props = defineProps({
},
});
function getName(item:any){
function getName(item: any) {
const name = dealSystemName(basename(item.path))
// console.log(name)
// console.log(item.path)
if(name.endsWith('.exe')){
return i18n(name.replace('.exe',""))
}else{
if (name.endsWith('.exe')) {
return i18n(name.replace('.exe', ""))
} else {
return name
}
}
function handleOnOpen(item: OsFileWithoutContent) {
chosenIndexs.value = [];
props.onOpen(item);
emitEvent('desktop.app.open');
if (choose.ifShow && !item.isDirectory) {
choose.path.push(item.path)
choose.close()
} else {
props.onOpen(item);
emitEvent('desktop.app.open');
}
}
function hadnleDrop(mouse: DragEvent, path: string) {
hoverIndex.value = -1;
@ -267,73 +247,73 @@ function handleRightClick(mouse: MouseEvent, item: OsFileWithoutContent, index:
// },
];
//console.log(item.parentPath)
//let filePath = item.path;
// if(filePath.length > 1) {
// if(filePath.charAt(1) === "B") {
// menuArr.push({
// label: "",
// click: async () => {
// //console.log(item);
// await Promise.all(
// chosenIndexs.value.map((index) => {
// const item = props.fileList[index];
// return backFile(item);
// })
// );
// chosenIndexs.value = [];
// props.onRefresh();
// },
// })
// }
// }
if (choose.ifShow) {
menuArr.push({
label: i18n('selected'),
click: () => {
const paths: any = []
chosenIndexs.value.forEach((index) => {
const item = props.fileList[index];
if (!item.isDirectory) {
paths.push(item.path)
}
})
if (paths.length > 0) {
choose.path = paths
choose.close()
}
chosenIndexs.value = [];
},
})
}
// eslint-disable-next-line prefer-const
let extMenus = useAppMenu(item, index);
if(extMenus && extMenus.length > 0) {
if (extMenus && extMenus.length > 0) {
// eslint-disable-next-line prefer-spread
menuArr.push.apply(menuArr,extMenus)
menuArr.push.apply(menuArr, extMenus)
}
if(ext != 'exe') {
if (ext != 'exe') {
const fileMenus = [
{
label: i18n('rename'),
click: () => {
editIndex.value = index;
editName.value = basename(item.path);
chosenIndexs.value = [];
label: i18n('rename'),
click: () => {
editIndex.value = index;
editName.value = basename(item.path);
chosenIndexs.value = [];
},
},
},
{
label: i18n('copy'),
click: () => {
//if(["/","/B"].includes(item.path)) return;
copyFile(chosenIndexs.value.map((index) => props.fileList[index]));
chosenIndexs.value = [];
},
},
{
label: i18n('delete'),
click: async () => {
// await Promise.all(
// chosenIndexs.value.map((index) => {
// const item = props.fileList[index];
// deleteFile(item);
// props.onRefresh();
// return true;
// })
// );
for(let i = 0; i < chosenIndexs.value.length; i++){
await deleteFile(props.fileList[chosenIndexs.value[i]]);
}
chosenIndexs.value = [];
props.onRefresh();
sys.refershAppList()
label: i18n('copy'),
click: () => {
//if(["/","/B"].includes(item.path)) return;
copyFile(chosenIndexs.value.map((index) => props.fileList[index]));
chosenIndexs.value = [];
},
},
}
{
label: i18n('delete'),
click: async () => {
// await Promise.all(
// chosenIndexs.value.map((index) => {
// const item = props.fileList[index];
// deleteFile(item);
// props.onRefresh();
// return true;
// })
// );
for (let i = 0; i < chosenIndexs.value.length; i++) {
await deleteFile(props.fileList[chosenIndexs.value[i]]);
}
chosenIndexs.value = [];
props.onRefresh();
sys.refershAppList()
},
}
];
// eslint-disable-next-line prefer-spread
menuArr.push.apply(menuArr,fileMenus)
menuArr.push.apply(menuArr, fileMenus)
}
const sysEndMenu = [
@ -357,7 +337,7 @@ function handleRightClick(mouse: MouseEvent, item: OsFileWithoutContent, index:
}
];
// eslint-disable-next-line prefer-spread
menuArr.push.apply(menuArr,sysEndMenu)
menuArr.push.apply(menuArr, sysEndMenu)
//console.log(item)
//console.log(ext)
@ -391,17 +371,21 @@ function handleDragLeave() {
padding-top: 4px;
border: 1px solid transparent;
margin: 6px;
.file-item_img {
width: 60%;
height: 60%;
pointer-events: none;
}
.file-item_type {
display: none;
}
.file-item_title {
pointer-events: none;
}
.file-item_editing {
display: inline-block !important;
outline: none;
@ -419,10 +403,12 @@ function handleDragLeave() {
.file-item:hover {
background-color: #b1f1ff4c;
}
.chosen {
border: 1px dashed #3bdbff3d;
// background-color: #ffffff6b;
background-color: var(--theme-color);
.file-item_title {
overflow: hidden;
text-overflow: ellipsis;
@ -432,6 +418,7 @@ function handleDragLeave() {
-webkit-line-clamp: 2;
}
}
.no-chosen {
.file-item_title {
overflow: hidden;
@ -442,11 +429,13 @@ function handleDragLeave() {
-webkit-line-clamp: 2;
}
}
.drag-over {
border: 1px dashed #3bdbff3d;
// background-color: #ffffff6b;
background-color: var(--theme-color);
}
.mode-icon {
.file-item_img {
width: 60%;
@ -486,18 +475,22 @@ function handleDragLeave() {
word-break: break-all;
}
}
.mode-icon {
width: var(--desk-item-size);
height: var(--desk-item-size);
}
.mode-big {
width: calc(var(--desk-item-size) * 2.5);
height: calc(var(--desk-item-size) * 2.5);
}
.mode-middle {
width: calc(var(--desk-item-size) * 1.5);
height: calc(var(--desk-item-size) * 1.5);
}
.mode-detail {
display: flex;
flex-direction: row;
@ -505,9 +498,11 @@ function handleDragLeave() {
height: var(--menulist-item-height);
width: 100%;
margin: 2px;
.file-item_img {
width: 30px;
}
.file-item_title {
width: 40%;
display: flex;
@ -517,6 +512,7 @@ function handleDragLeave() {
text-overflow: ellipsis;
white-space: nowrap;
}
.file-item_type {
display: block;
color: var(--color-dark-hover);

14
frontend/src/components/computer/Computer.vue

@ -126,7 +126,19 @@ const { refersh, createFolder, backFolder, openFolder, onComputerMount } = useCo
},
setFileList(list) {
//console.log(list)
currentList.value = list;
//currentList.value = list;
if(config.ext && config.ext instanceof Array && config.ext.length > 0) {
const res:any = []
list.forEach((d : any) => {
if(config.ext.includes(d.ext) || d.isDirectory){
res.push(d)
}
})
currentList.value = res;
}else{
currentList.value = list;
}
},
openFile(path) {
system?.openFile(path);

8
frontend/src/stores/choose.ts

@ -3,11 +3,12 @@ import { BrowserWindow } from "@/system";
import { ref } from 'vue';
export const useChooseStore = defineStore('chooseStore', () => {
const win:any = ref()
const path = ref([])
const path:any = ref([])
const ifShow = ref(false)
const select = (title = '选择文件', fileExt:any) => {
win.value = new BrowserWindow({
title,
content: "FileList",
content: "Computer",
config: {
ext: fileExt,
path: '/'
@ -22,14 +23,17 @@ export const useChooseStore = defineStore('chooseStore', () => {
resizable: true,
});
win.value.show()
ifShow.value = true
}
const close = () => {
ifShow.value = false
win.value.close()
}
return {
win,
path,
ifShow,
select,
close
}

Loading…
Cancel
Save