Browse Source

add:移动端适配

master
tiantian 6 months ago
parent
commit
8fad22cbea
  1. 1
      frontend/package.json
  2. 973
      frontend/src/components/computer/Computer.vue
  3. 57
      frontend/src/components/desktop/DeskItem.vue
  4. 141
      frontend/src/components/desktop/Desktop.vue
  5. 4
      frontend/src/components/window/WindowNode.vue
  6. 702
      frontend/src/components/window/WindowTemplate.vue
  7. 26
      frontend/src/main.ts

1
frontend/package.json

@ -23,6 +23,7 @@
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pinia-plugin-persist": "^1.0.0", "pinia-plugin-persist": "^1.0.0",
"swiper": "^11.1.15", "swiper": "^11.1.15",
"vant": "^4.9.15",
"vue": "^3.4.31", "vue": "^3.4.31",
"vue-i18n": "^9.13.1", "vue-i18n": "^9.13.1",
"vue-router": "^4.4.0", "vue-router": "^4.4.0",

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

File diff suppressed because it is too large

57
frontend/src/components/desktop/DeskItem.vue

@ -8,17 +8,30 @@
<swiper-slide class="swiper-slide"> <swiper-slide class="swiper-slide">
<FileList :on-chosen="props.onChosen" :on-open="openapp" :file-list="appList"></FileList> <FileList :on-chosen="props.onChosen" :on-open="openapp" :file-list="appList"></FileList>
</swiper-slide> </swiper-slide>
<swiper-slide></swiper-slide> <swiper-slide>
<div @click.stop="handle(item)" class="magnet-item" :style="{
animationDelay: `${Math.floor(index / 4) * 0.02}s`,
animationDuration: `${Math.floor(index / 4) * 0.04 + 0.1}s`,
}" v-for="(item, index) in menulist" v-glowing :key="basename(item.path)">
<FileIcon class="magnet-item_img" :file="item" />
<span class="magnet-item_title">{{ getName(item) }}</span>
</div>
</swiper-slide>
</swiper> </swiper>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { mountEvent } from "@/system/event"; import { mountEvent } from "@/system/event";
import { useSystem } from "@/system/index.ts"; import { useSystem, BrowserWindow } from "@/system/index.ts";
import { useAppOpen } from "@/hook/useAppOpen"; import { useAppOpen } from "@/hook/useAppOpen";
import { onMounted } from "vue"; import { onMounted } from "vue";
import { isMobileDevice } from "@/util/device"; import { isMobileDevice } from "@/util/device";
import { Swiper, SwiperSlide } from 'swiper/vue' import { Swiper, SwiperSlide } from 'swiper/vue'
import { basename } from "@/system/core/Path";
import { emitEvent } from "@/system/event";
import { t } from "@/i18n";
// swiper // swiper
import 'swiper/swiper-bundle.css'; import 'swiper/swiper-bundle.css';
// swiper // swiper
@ -26,6 +39,7 @@ import { Pagination } from 'swiper/modules'
// modules使 // modules使
const modules = [Pagination] const modules = [Pagination]
const { openapp, appList } = useAppOpen("apps"); const { openapp, appList } = useAppOpen("apps");
const { appList: menulist } = useAppOpen("menulist")
const props = defineProps({ const props = defineProps({
onChosen: { onChosen: {
type: Function, type: Function,
@ -37,6 +51,31 @@ onMounted(() => {
useSystem().initAppList(); useSystem().initAppList();
}); });
}); });
function handle(item: any) {
emitEvent("magnet.item.click", item);
const sys = useSystem();
const winopt = sys._rootState.windowMap["Menulist"].get(item.title);
if (winopt) {
if (winopt._hasShow) {
return;
} else {
winopt._hasShow = true;
const win = new BrowserWindow(winopt.window);
win.show();
win.on("close", () => {
winopt._hasShow = false;
});
}
}
}
function getName(item: any) {
const name = basename(item.path);
if (name.endsWith(".exe")) {
return t(name.replace(".exe", ""));
} else {
return name;
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.desk-group { .desk-group {
@ -62,7 +101,7 @@ onMounted(() => {
.swiperBox { .swiperBox {
width: 100vw; width: 100vw;
padding-top: vh(20); padding-top: vh(10);
// position: absolute; // position: absolute;
// top:0; // top:0;
// left: 0; // left: 0;
@ -74,4 +113,16 @@ onMounted(() => {
grid-template-rows: repeat(5, vh(100)) grid-template-rows: repeat(5, vh(100))
} }
} }
.magnet-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.magnet-item_img {
width: vw(40);
height: vh(40);
}
}
</style> </style>

141
frontend/src/components/desktop/Desktop.vue

@ -19,6 +19,15 @@
<div class="bottom"> <div class="bottom">
<Taskbar></Taskbar> <Taskbar></Taskbar>
</div> </div>
<div class="bottom-bar" v-if="isMobileDevice()">
<div @click.stop="handle(item)" class="magnet-item" :style="{
animationDelay: `${Math.floor(index / 4) * 0.02}s`,
animationDuration: `${Math.floor(index / 4) * 0.04 + 0.1}s`,
}" v-for="(item, index) in bottomApp" v-glowing :key="basename(item.path)">
<FileIcon class="magnet-item_img" :file="item" />
<span class="magnet-item_title">{{ getName(item) }}</span>
</div>
</div>
<ContextMenu></ContextMenu> <ContextMenu></ContextMenu>
</div> </div>
</template> </template>
@ -27,10 +36,113 @@ import { emitEvent } from "@/system/event";
import { useContextMenu } from "@/hook/useContextMenu"; import { useContextMenu } from "@/hook/useContextMenu";
import { useFileDrag } from "@/hook/useFileDrag"; import { useFileDrag } from "@/hook/useFileDrag";
import { Rect, useRectChosen } from "@/hook/useRectChosen"; import { Rect, useRectChosen } from "@/hook/useRectChosen";
import { useSystem } from "@/system"; import { useSystem, BrowserWindow } from "@/system";
import { onErrorCaptured } from "vue"; import { onErrorCaptured } from "vue";
import { useUpgradeStore } from '@/stores/upgrade'; import { useUpgradeStore } from '@/stores/upgrade';
import { isMobileDevice } from "@/util/device";
import { basename } from "@/system/core/Path";
import { t } from "@/i18n";
import { useAppOpen } from "@/hook/useAppOpen";
const { openapp } = useAppOpen("menulist");
function handle(item: any) {
if (item.name.includes('localchat')) {
openapp(item);
emitEvent("desktop.app.open");
} else {
emitEvent("magnet.item.click", item);
}
const sys = useSystem();
const winopt = sys._rootState.windowMap["Menulist"].get(item.title);
if (winopt) {
if (winopt._hasShow) {
return;
} else {
winopt._hasShow = true;
winopt.window.fullscreen = true
const win = new BrowserWindow(winopt.window);
win.show();
win.on("close", () => {
winopt._hasShow = false;
});
}
}
}
function getName(item: any) {
const name = basename(item.path);
if (name.endsWith(".exe")) {
return t(name.replace(".exe", ""));
} else {
return name;
}
}
const bottomApp = [
{
"isFile": true,
"isDirectory": false,
"isSymlink": false,
"size": 32,
"modTime": "2024-12-16T09:17:18.7214789+08:00",
"atime": "2024-12-16T09:17:18.7214789+08:00",
"birthtime": "2024-12-16T09:17:18.7214789+08:00",
"mtime": "2024-12-16T09:17:18.7214789+08:00",
"rdev": 0,
"mode": 511,
"name": "computer.exe",
"path": "/C/Users/Menulist/computer.exe",
"oldPath": "/C/Users/Menulist/computer.exe",
"parentPath": "/C/Users/Menulist",
"content": "link::Desktop::computer::diannao",
"ext": "exe",
"title": "computer",
"id": 1,
"isPwd": false
},
{
"isFile": true,
"isDirectory": false,
"isSymlink": false,
"size": 31,
"modTime": "2024-12-16T09:17:18.7214789+08:00",
"atime": "2024-12-16T09:17:18.7214789+08:00",
"birthtime": "2024-12-16T09:17:18.7214789+08:00",
"mtime": "2024-12-16T09:17:18.7214789+08:00",
"rdev": 0,
"mode": 511,
"name": "setting.exe",
"path": "/C/Users/Menulist/setting.exe",
"oldPath": "/C/Users/Menulist/setting.exe",
"parentPath": "/C/Users/Menulist",
"content": "link::Desktop::setting::setting",
"ext": "exe",
"title": "setting",
"id": 15,
"isPwd": false
},
{
"isFile": true,
"isDirectory": false,
"isSymlink": false,
"size": 30,
"modTime": "2024-12-16T09:17:18.7214789+08:00",
"atime": "2024-12-16T09:17:18.7214789+08:00",
"birthtime": "2024-12-16T09:17:18.7214789+08:00",
"mtime": "2024-12-16T09:17:18.7214789+08:00",
"rdev": 0,
"mode": 511,
"name": "localchat.exe",
"path": "/C/Users/Desktop/localchat.exe",
"oldPath": "/C/Users/Desktop/localchat.exe",
"parentPath": "/C/Users/Desktop",
"content": "link::Desktop::localchat::chat",
"ext": "exe",
"title": "localchat",
"id": 3,
"isPwd": false
}
];
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();
@ -119,5 +231,32 @@ onErrorCaptured((err) => {
.bottom { .bottom {
display: none; display: none;
} }
.bottom-bar {
display: flex;
justify-content: space-evenly;
position: absolute;
width: vw(340);
left: 50%;
transform: translateX(-50%);
bottom: vh(15);
height: vh(80);
border-radius: vw(50);
color: #e5e3e3d5;
background-color: rgba(255, 255, 255, .2);
backdrop-filter: blur(15px);
.magnet-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.magnet-item_img {
width: vw(40);
height: vh(40);
}
}
}
} }
</style> </style>

4
frontend/src/components/window/WindowNode.vue

@ -1,13 +1,11 @@
<template> <template>
<Transition name="windowanimate" appear> <Transition name="windowanimate" appear>
<WindowTemplate v-if="window.windowInfo.isCreated && !isMobileDevice()" :browser-window="window"></WindowTemplate> <WindowTemplate v-if="window.windowInfo.isCreated" :browser-window="window"></WindowTemplate>
<MobileTemplate v-else></MobileTemplate>
</Transition> </Transition>
<WindowNode v-for="node in window.children" :key="node.id" :window="node" /> <WindowNode v-for="node in window.children" :key="node.id" :window="node" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { BrowserWindow } from "@/system/window/BrowserWindow"; import { BrowserWindow } from "@/system/window/BrowserWindow";
import { isMobileDevice } from "@/util/device";
import { UnwrapNestedRefs } from "vue"; import { UnwrapNestedRefs } from "vue";
defineProps<{ defineProps<{
window: UnwrapNestedRefs<BrowserWindow>; window: UnwrapNestedRefs<BrowserWindow>;

702
frontend/src/components/window/WindowTemplate.vue

@ -1,399 +1,419 @@
<template> <template>
<div <div class="wintmp_outer dragwin" :class="{
class="wintmp_outer dragwin" topwin: istop,
:class="{ max: windowInfo.state == WindowStateEnum.maximize,
topwin: istop, min: windowInfo.state == WindowStateEnum.minimize,
max: windowInfo.state == WindowStateEnum.maximize, fullscreen: windowInfo.state == WindowStateEnum.fullscreen,
min: windowInfo.state == WindowStateEnum.minimize, noframe: !windowInfo.frame,
fullscreen: windowInfo.state == WindowStateEnum.fullscreen, disable: windowInfo.disable,
noframe: !windowInfo.frame, }" :style="customerStyle" @touchstart.passive="onFocus" @mousedown="onFocus" ref="$win_outer" v-dragable>
disable: windowInfo.disable,
}"
:style="customerStyle"
@touchstart.passive="onFocus"
@mousedown="onFocus"
ref="$win_outer"
v-dragable
>
<!-- 窗口标题栏 --> <!-- 窗口标题栏 -->
<div <div class="wintmp_uper" @contextmenu.prevent v-if="!isMobileDevice()">
class="wintmp_uper"
@contextmenu.prevent
>
<MenuBar :browser-window="browserWindow"></MenuBar> <MenuBar :browser-window="browserWindow"></MenuBar>
</div> </div>
<van-nav-bar v-else left-text="返回" left-arrow @click-left="onClickLeft" fixed>
<template #title>
<div class="title">
<FileIcon :icon="browserWindow.windowInfo.icon" />
{{ browserWindow.windowInfo.title }}
</div>
</template>
</van-nav-bar>
<div <div class="wintmp_main"
class="wintmp_main" :class="{ resizeing: resizemode != 'null', 'saveFileMain': browserWindow.windowInfo.footer }"
:class="{ resizeing: resizemode != 'null', 'saveFileMain':browserWindow.windowInfo.footer}" @mousedown.stop="predown" @touchstart.stop.passive="predown" @contextmenu.stop.prevent>
@mousedown.stop="predown" <div class="content-mask" v-if="!istop && typeof browserWindow.content === 'string'"></div>
@touchstart.stop.passive="predown"
@contextmenu.stop.prevent
>
<div
class="content-mask"
v-if="!istop && typeof browserWindow.content === 'string'"
></div>
<WindowInner :win="browserWindow"></WindowInner> <WindowInner :win="browserWindow"></WindowInner>
</div> </div>
<!-- 使用 v-for 生成拖拽边界 --> <!-- 使用 v-for 生成拖拽边界 -->
<div <div v-for="border in dragBorders" :key="border.type" :class="[
v-for="border in dragBorders" border.class,
:key="border.type" 'win_drag_border',
:class="[ { isChoseMode: resizemode == border.type },
border.class, border.cursorClass,
'win_drag_border', ]" v-if="resizable" draggable="false" @mousedown.stop.prevent="startScale($event, border.type)"
{ isChoseMode: resizemode == border.type }, @touchstart.stop.passive="startScale($event, border.type)"></div>
border.cursorClass, <div class="wintmp_footer" v-if="browserWindow.windowInfo.footer">
]" <MenuFooter :browser-window="browserWindow" @translateSavePath="translateSavePath"></MenuFooter>
v-if="resizable" </div>
draggable="false"
@mousedown.stop.prevent="startScale($event, border.type)"
@touchstart.stop.passive="startScale($event, border.type)"
></div>
<div class="wintmp_footer" v-if="browserWindow.windowInfo.footer">
<MenuFooter :browser-window="browserWindow" @translateSavePath="translateSavePath"></MenuFooter>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useSystem } from "@/system"; import { useSystem } from "@/system";
import { emitEvent } from "@/system/event";"" import { emitEvent } from "@/system/event"; ""
import { import {
BrowserWindow, BrowserWindow,
WindowStateEnum, WindowStateEnum,
} from "@/system/window/BrowserWindow"; } from "@/system/window/BrowserWindow";
import { ScaleElement } from "@/system/window/dom/ScaleElement"; import { ScaleElement } from "@/system/window/dom/ScaleElement";
import { vDragable } from "@/system/window/MakeDragable"; import { vDragable } from "@/system/window/MakeDragable";
import { import {
computed, computed,
onMounted, onMounted,
onUnmounted, onUnmounted,
provide, provide,
ref, ref,
UnwrapNestedRefs, UnwrapNestedRefs,
} from "vue"; } from "vue";
import { useChooseStore } from "@/stores/choose"; import { useChooseStore } from "@/stores/choose";
import eventBus from '@/system/event/eventBus' import eventBus from '@/system/event/eventBus'
import { isMobileDevice } from "@/util/device";
const sys = useSystem();
const props = defineProps<{ const onClickLeft = () => {
browserWindow: UnwrapNestedRefs<BrowserWindow>; if (choose.isExist(props.browserWindow.windowInfo.componentID)) {
}>(); choose.closeSaveFile(props.browserWindow.windowInfo.componentID)
const browserWindow = props.browserWindow;
const windowInfo = browserWindow.windowInfo;
// const temp = reactive(browserWindow)
provide("browserWindow", browserWindow);
provide("system", sys);
const choose = useChooseStore()
function translateSavePath (path: string, name?: string) {
if (browserWindow.windowInfo.footer) {
const pos = choose.saveFileContent.findIndex((item: any) => {
return item.componentID == browserWindow.windowInfo.componentID
})
if (pos == -1) return
if (path && path !== '') {
choose.saveFileContent[pos].filePath = path
} else if (name && name !== '') {
choose.saveFileContent[pos].fileName = name
eventBus.emit('saveFile', choose.saveFileContent[pos] )
}
}
}
provide('translateSavePath', translateSavePath)
function predown() {
browserWindow.moveTop();
emitEvent("window.content.click", browserWindow);
} }
props.browserWindow.destroy();
}
const customerStyle = ref<NonNullable<unknown>>({}); const sys = useSystem();
const props = defineProps<{
browserWindow: UnwrapNestedRefs<BrowserWindow>;
}>();
function onFocus(e: MouseEvent | TouchEvent): void {
browserWindow?.moveTop(); const browserWindow = props.browserWindow;
if (windowInfo.state === WindowStateEnum.maximize) { if (isMobileDevice()) browserWindow.maximize()
if (e instanceof MouseEvent) { const windowInfo = browserWindow.windowInfo;
e.preventDefault(); console.log(windowInfo, 'windowInfo')
e.stopPropagation(); // const temp = reactive(browserWindow)
} provide("browserWindow", browserWindow);
provide("system", sys);
const choose = useChooseStore()
function translateSavePath(path: string, name?: string) {
if (browserWindow.windowInfo.footer) {
const pos = choose.saveFileContent.findIndex((item: any) => {
return item.componentID == browserWindow.windowInfo.componentID
})
if (pos == -1) return
if (path && path !== '') {
choose.saveFileContent[pos].filePath = path
} else if (name && name !== '') {
choose.saveFileContent[pos].fileName = name
eventBus.emit('saveFile', choose.saveFileContent[pos])
}
}
}
provide('translateSavePath', translateSavePath)
function predown() {
browserWindow.moveTop();
emitEvent("window.content.click", browserWindow);
}
const customerStyle = ref<NonNullable<unknown>>({});
function onFocus(e: MouseEvent | TouchEvent): void {
browserWindow?.moveTop();
if (windowInfo.state === WindowStateEnum.maximize) {
if (e instanceof MouseEvent) {
e.preventDefault();
e.stopPropagation();
} }
} }
}
const istop = computed(() => windowInfo.istop); const istop = computed(() => windowInfo.istop);
onMounted(() => { onMounted(() => {
customerStyle.value = { customerStyle.value = {
width: computed(() => windowInfo.width + "px"), width: computed(() => windowInfo.width + "px"),
height: computed(() => windowInfo.height + "px"), height: computed(() => windowInfo.height + "px"),
left: computed(() => windowInfo.x + "px"), left: computed(() => windowInfo.x + "px"),
top: computed(() => windowInfo.y + "px"), top: computed(() => windowInfo.y + "px"),
zIndex: computed(() => { zIndex: computed(() => {
if (windowInfo.alwaysOnTop) { if (windowInfo.alwaysOnTop) {
return 9999; return 9999;
}
return windowInfo.zindex;
}),
backgroundColor: computed(() => windowInfo.backgroundColor),
};
});
const resizable = ref(windowInfo.resizable);
const resizemode = ref("null");
let scaleAble: ScaleElement;
onMounted(() => {
scaleAble = new ScaleElement(
resizemode,
windowInfo.width,
windowInfo.height,
windowInfo.x,
windowInfo.y
);
scaleAble.onResize(
(width: number, height: number, x: number, y: number) => {
windowInfo.width = width || windowInfo.width;
windowInfo.height = height || windowInfo.height;
windowInfo.x = x || windowInfo.x;
windowInfo.y = y || windowInfo.y;
browserWindow.emit(
"resize",
windowInfo.width,
windowInfo.height
);
} }
); return windowInfo.zindex;
}); }),
backgroundColor: computed(() => windowInfo.backgroundColor),
};
});
const resizable = ref(windowInfo.resizable);
const resizemode = ref("null");
let scaleAble: ScaleElement;
function startScale(e: MouseEvent | TouchEvent, dire: string) { onMounted(() => {
console.log(e); scaleAble = new ScaleElement(
if (windowInfo.disable) { resizemode,
return; windowInfo.width,
windowInfo.height,
windowInfo.x,
windowInfo.y
);
scaleAble.onResize(
(width: number, height: number, x: number, y: number) => {
windowInfo.width = width || windowInfo.width;
windowInfo.height = height || windowInfo.height;
windowInfo.x = x || windowInfo.x;
windowInfo.y = y || windowInfo.y;
browserWindow.emit(
"resize",
windowInfo.width,
windowInfo.height
);
} }
scaleAble?.startScale( );
e, });
dire,
windowInfo.x, function startScale(e: MouseEvent | TouchEvent, dire: string) {
windowInfo.y, console.log(e);
windowInfo.width, if (windowInfo.disable) {
windowInfo.height return;
);
} }
scaleAble?.startScale(
e,
dire,
windowInfo.x,
windowInfo.y,
windowInfo.width,
windowInfo.height
);
}
onUnmounted(() => {
scaleAble.unMount();
});
onUnmounted(() => { const dragBorders = [
scaleAble.unMount(); { type: "r", class: "right_border", cursorClass: "ew-resize" },
}); { type: "b", class: "bottom_border", cursorClass: "ns-resize" },
{ type: "l", class: "left_border", cursorClass: "ew-resize" },
const dragBorders = [ { type: "t", class: "top_border", cursorClass: "ns-resize" },
{ type: "r", class: "right_border", cursorClass: "ew-resize" }, {
{ type: "b", class: "bottom_border", cursorClass: "ns-resize" }, type: "rb",
{ type: "l", class: "left_border", cursorClass: "ew-resize" }, class: "right_bottom_border",
{ type: "t", class: "top_border", cursorClass: "ns-resize" }, cursorClass: "nwse-resize",
{ },
type: "rb", { type: "lb", class: "left_bottom_border", cursorClass: "nesw-resize" },
class: "right_bottom_border", { type: "lt", class: "left_top_border", cursorClass: "nwse-resize" },
cursorClass: "nwse-resize", { type: "rt", class: "right_top_border", cursorClass: "nesw-resize" },
}, ];
{ type: "lb", class: "left_bottom_border", cursorClass: "nesw-resize" },
{ type: "lt", class: "left_top_border", cursorClass: "nwse-resize" },
{ type: "rt", class: "right_top_border", cursorClass: "nesw-resize" },
];
</script> </script>
<style> <style>
.dragwin { .dragwin {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>
<style scoped lang="scss"> <style scoped lang="scss">
.wintmp_outer { .wintmp_outer {
position: absolute; position: absolute;
padding: 0; padding: 0;
margin: 0; margin: 0;
// left: 0; // left: 0;
// top: 0; // top: 0;
// min-width: 800px; // min-width: 800px;
width: max-content; width: max-content;
height: max-content; height: max-content;
// min-height: 650px; // min-height: 650px;
border-radius: 10px; border-radius: 10px;
overflow: hidden;
background-color: #fff;
border: var(--window-border);
display: flex;
flex-direction: column;
// box-shadow: var(--window-box-shadow);
// border-radius: var(--window-border-radius);
.wintmp_main {
position: relative;
width: 100%;
height: 100%;
// background-color: rgb(255, 255, 255);
overflow: hidden; overflow: hidden;
background-color: #fff; contain: content;
border: var(--window-border);
display: flex;
flex-direction: column;
// box-shadow: var(--window-box-shadow);
// border-radius: var(--window-border-radius);
.wintmp_main {
position: relative;
width: 100%;
height: 100%;
// background-color: rgb(255, 255, 255);
overflow: hidden;
contain: content;
}
.wintmp_footer {
position: relative;
}
}
.saveFileMain {
:deep(.main) {
height: calc(100% - 60px);
}
}
.topwin {
// border: 1px solid #0078d7;
// box-shadow: var(--window-top-box-shadow);
} }
.icon { .wintmp_footer {
width: 12px; position: relative;
height: 12px;
} }
}
.max { .saveFileMain {
position: absolute; :deep(.main) {
left: 0 !important; height: calc(100% - 60px);
top: 0 !important;
width: 100% !important;
height: 100% !important;
transition: left 0.1s ease-in-out, top 0.1s ease-in-out,
width 0.1s ease-in-out, height 0.1s ease-in-out;
}
.disable {
.wintmp_footer,
.wintmp_uper,
.wintmp_main {
pointer-events: none;
user-select: none;
box-shadow: none;
}
}
.min {
visibility: hidden;
display: none;
} }
}
.fullscreen { .topwin {
// // border: 1px solid #0078d7;
position: fixed; // box-shadow: var(--window-top-box-shadow);
left: 0 !important; }
top: 0 !important;
width: 100% !important; .icon {
height: 100% !important; width: 12px;
z-index: 205 !important; height: 12px;
border: none; }
.wintmp_uper {
display: none; .max {
} position: absolute;
} left: 0 !important;
top: 0 !important;
width: 100% !important;
height: 100% !important;
transition: left 0.1s ease-in-out, top 0.1s ease-in-out,
width 0.1s ease-in-out, height 0.1s ease-in-out;
}
.noframe { .disable {
border: none;
.wintmp_footer,
.wintmp_uper,
.wintmp_main {
pointer-events: none;
user-select: none;
box-shadow: none; box-shadow: none;
.wintmp_uper {
display: none;
}
} }
}
.transparent { .min {
background-color: transparent; visibility: hidden;
display: none;
}
.wintmp_main { .fullscreen {
background-color: transparent; //
} position: fixed;
left: 0 !important;
top: 0 !important;
width: 100% !important;
height: 100% !important;
z-index: 205 !important;
border: none;
.wintmp_uper { .wintmp_uper {
background-color: rgba(255, 255, 255, 0.774); display: none;
}
} }
}
.win_drag_border { .noframe {
position: absolute; border: none;
background-color: rgba(0, 0, 0, 0); box-shadow: none;
}
.right_border { .wintmp_uper {
cursor: ew-resize; display: none;
right: -12px;
width: 16px;
height: calc(100% - 4px);
} }
}
.bottom_border { .transparent {
cursor: ns-resize; background-color: transparent;
bottom: -12px;
width: calc(100% - 4px);
height: 16px;
}
.left_border { .wintmp_main {
cursor: ew-resize; background-color: transparent;
left: -12px;
width: 16px;
height: calc(100% - 4px);
} }
.top_border { .wintmp_uper {
cursor: ns-resize; background-color: rgba(255, 255, 255, 0.774);
top: -12px;
width: calc(100% - 4px);
height: 16px;
} }
}
.left_top_border { .win_drag_border {
cursor: nwse-resize; position: absolute;
left: -12px; background-color: rgba(0, 0, 0, 0);
top: -12px; }
width: 16px;
height: 16px;
}
.right_top_border { .right_border {
cursor: nesw-resize; cursor: ew-resize;
right: -12px; right: -12px;
top: -12px; width: 16px;
width: 16px; height: calc(100% - 4px);
height: 16px; }
}
.left_bottom_border { .bottom_border {
cursor: nesw-resize; cursor: ns-resize;
left: -12px; bottom: -12px;
bottom: -12px; width: calc(100% - 4px);
width: 16px; height: 16px;
height: 16px; }
}
.right_bottom_border { .left_border {
cursor: nwse-resize; cursor: ew-resize;
right: -12px; left: -12px;
bottom: -12px; width: 16px;
width: 16px; height: calc(100% - 4px);
height: 16px; }
}
.isChoseMode { .top_border {
width: 100vw; cursor: ns-resize;
height: 100vh; top: -12px;
position: fixed; width: calc(100% - 4px);
left: 0; height: 16px;
top: 0; }
}
.resizeing { .left_top_border {
user-select: none; cursor: nwse-resize;
pointer-events: none; left: -12px;
top: -12px;
width: 16px;
height: 16px;
}
.right_top_border {
cursor: nesw-resize;
right: -12px;
top: -12px;
width: 16px;
height: 16px;
}
.left_bottom_border {
cursor: nesw-resize;
left: -12px;
bottom: -12px;
width: 16px;
height: 16px;
}
.right_bottom_border {
cursor: nwse-resize;
right: -12px;
bottom: -12px;
width: 16px;
height: 16px;
}
.isChoseMode {
width: 100vw;
height: 100vh;
position: fixed;
left: 0;
top: 0;
}
.resizeing {
user-select: none;
pointer-events: none;
}
.content-mask {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0);
z-index: 100;
}
@media screen and (max-width: 768px) {
.wintmp_outer {
height: vh(100);
padding-top: vh(40);
} }
.content-mask { .title {
position: absolute; height: vh(46);
width: 100%; display: flex;
height: 100%; align-items: center;
background-color: rgba(0, 0, 0, 0);
z-index: 100; .icon {
width: 1em;
height: 1em;
}
} }
}
</style> </style>

26
frontend/src/main.ts

@ -1,24 +1,26 @@
import * as ElementPlusIconsVue from '@element-plus/icons-vue'; import * as ElementPlusIconsVue from "@element-plus/icons-vue"
//svg插件需要配置代码 //svg插件需要配置代码
import ElementPlus from 'element-plus'; import ElementPlus from "element-plus"
import 'element-plus/dist/index.css'; import "element-plus/dist/index.css"
import { createApp } from "vue"; import { createApp } from "vue"
import screenShort from "vue-web-screen-shot"; import screenShort from "vue-web-screen-shot"
import App from "./App.vue"; import App from "./App.vue"
import './assets/windows10.scss'; import "./assets/windows10.scss"
import { i18n } from './i18n/index.ts'; import { i18n } from "./i18n/index.ts"
import pinia from './stores/index.ts'; import pinia from "./stores/index.ts"
import router from './system/router'; import router from "./system/router"
import { NavBar } from "vant"
import "vant/lib/index.css"
const app = createApp(App) const app = createApp(App)
app.use(router) app.use(router)
app.use(ElementPlus) app.use(ElementPlus)
app.use(pinia) app.use(pinia)
app.use(i18n) app.use(i18n)
app.use(NavBar)
app.use(screenShort, { enableWebRtc: true }) app.use(screenShort, { enableWebRtc: true })
for (const [key, component] of Object.entries(ElementPlusIconsVue)) { for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component) app.component(key, component)
} }
app.mount("#app"); app.mount("#app")

Loading…
Cancel
Save