diff --git a/.stylelintrc.js b/.stylelintrc.js index fa95536c1516b40f134db6febda92a232590b0c2..38d55358192140d41c9d90a1eadc44f58b6ca8a3 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -10,6 +10,12 @@ module.exports = { rules: { 'no-empty-source': null, 'selector-class-pattern': '^[a-z][a-zA-Z0-9]+$', + 'selector-pseudo-class-no-unknown': [ + true, + { + ignorePseudoClasses: ['global'] + } + ], 'color-function-notation': 'legacy', 'alpha-value-notation': 'number', // 属性的排序 diff --git a/package-lock.json b/package-lock.json index 55bd3e7c5484d941abe3c7dbd733065fed69191d..64e79c1b40e3b338969703126f6e62d574609d74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4512,11 +4512,6 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, - "classname": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/classname/-/classname-0.0.0.tgz", - "integrity": "sha512-kkhsspEJdUW+VhuvNzb2sQf0KbafDPfd36dB1qf03Uu42dWZwMQzaQuyNkaRr5ir0ZiAN0+TlH/EOOfwb/aaXg==" - }, "classnames": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", diff --git a/package.json b/package.json index c57020e9b9b091b4027f0f662d1eea4558cd6f4b..d689d9abe7ce741eef27535024277bb4f41b6e5f 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "@ant-design/icons": "^4.7.0", "antd": "^4.23.2", "axios": "^0.27.2", - "classname": "0.0.0", + "classnames": "^2.3.2", "lodash": "^4.17.21", "react-router-dom": "^6.4.0", "zustand": "^4.1.1" diff --git a/src/common/constants/index.ts b/src/common/constants/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..3778d6404552fb1eef29a89952c2e4b8b8a5fc09 --- /dev/null +++ b/src/common/constants/index.ts @@ -0,0 +1,8 @@ +// 窗口状态常量 +const WINDOW_STATUS = { + NORMAL: 0, // 正常 + MIN: 1, // 最小化 + MAX: 2 // 最大化 +} + +export { WINDOW_STATUS } diff --git a/src/common/style/vars.scss b/src/common/style/vars.scss index 5de36de16195a2e55b7ed8164715890a9fb1f6fc..ba6a337b294765c8c744ff696022ba31e5ffde56 100644 --- a/src/common/style/vars.scss +++ b/src/common/style/vars.scss @@ -5,10 +5,19 @@ $desk-background2: rgba(255, 255, 255, 0.1); $hover-background1: rgba(255, 255, 255, 0.25); +$window-border-color: #555; +$window-header-height: 32px; +$window-background: #303030; + $border-color1: #eee; $scrollbar-thumb1: #999; +$start-tools-bar-height: 48px; + $start-menu-width: 600px; $start-menu-height: 550px; +:export { + startToolsBarHeight: $start-tools-bar-height +} diff --git a/src/common/utils/getId.ts b/src/common/utils/getId.ts new file mode 100644 index 0000000000000000000000000000000000000000..68e50aecbe5f31c22e3915621c8c24aab3681879 --- /dev/null +++ b/src/common/utils/getId.ts @@ -0,0 +1,9 @@ +import { customAlphabet } from 'nanoid' + +const getId = (size = 32) => { + const dictionary = + '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + return customAlphabet(dictionary, size) +} + +export default getId() diff --git a/src/pages/Desktop/StartToolsBar/index.module.scss b/src/pages/Desktop/StartToolsBar/index.module.scss index 5c41d66b31c252e35b24788e573eee1511c291b2..48c89fd39dd5e72d26715144bdcf0a20ff46cedd 100644 --- a/src/pages/Desktop/StartToolsBar/index.module.scss +++ b/src/pages/Desktop/StartToolsBar/index.module.scss @@ -4,7 +4,7 @@ left: 0; z-index: 9999; width: 100%; - height: 48px; + height: $start-tools-bar-height; background: $desk-background1; backdrop-filter: saturate(3) blur(20px); } diff --git a/src/pages/Desktop/Window/WindowBox/Header/index.module.scss b/src/pages/Desktop/Window/WindowBox/Header/index.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..65bdcb804ddc5ddb1048cf224b211383bf7f187c --- /dev/null +++ b/src/pages/Desktop/Window/WindowBox/Header/index.module.scss @@ -0,0 +1,55 @@ +.headerBox { + display: flex; + flex-direction: row; + border-bottom: 1px solid $window-border-color; + width: 100%; + height: $window-header-height; +} + +.title { + flex-grow: 1; + line-height: 30px; + padding-left: 15px; + font-size: 12px; + user-select: none; + cursor: all-scroll; +} + +.tools { + display: flex; + flex-direction: row; +} + +.tool { + display: flex; + justify-content: center; + align-items: center; + width: 46px; + cursor: pointer; + + &:hover { + background-color: $desk-background2; + } +} + +.full { + :global { + svg { + font-size: 11px; + } + } +} + +.normal { + :global { + svg { + font-size: 13px; + } + } +} + +.close { + &:hover { + background-color: rgb(232, 17, 35) !important; + } +} diff --git a/src/pages/Desktop/Window/WindowBox/Header/index.tsx b/src/pages/Desktop/Window/WindowBox/Header/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5a9959274e5da64e1ce674c053f4ea57bccbaa87 --- /dev/null +++ b/src/pages/Desktop/Window/WindowBox/Header/index.tsx @@ -0,0 +1,51 @@ +import React from 'react' +import { + MinusOutlined, + CloseOutlined, + BorderOutlined, + BlockOutlined +} from '@ant-design/icons' +import classnames from 'classnames' +import styles from './index.module.scss' +import WindowContext from '@/pages/Desktop/Window/WindowContext' +import useStore, { MyState } from '@/store' +import { WINDOW_STATUS } from '@/common/constants' + +const Header: React.FC = () => { + const { window, id: windowId } = React.useContext(WindowContext) + const editWindowStatus = useStore((state: MyState) => state.setWindowStatus) + const closeWindow = useStore((state: MyState) => state.closeWindow) + return ( +
+
{window.title}
+
+
editWindowStatus(WINDOW_STATUS.MIN, windowId)}> + +
+ {window.status === WINDOW_STATUS.NORMAL && ( +
editWindowStatus(WINDOW_STATUS.MAX, windowId)}> + +
+ )} + {window.status === WINDOW_STATUS.MAX && ( +
editWindowStatus(WINDOW_STATUS.NORMAL, windowId)}> + +
+ )} +
closeWindow(windowId)}> + +
+
+
+ ) +} + +export default Header diff --git a/src/pages/Desktop/Window/WindowBox/index.module.scss b/src/pages/Desktop/Window/WindowBox/index.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..193b38b97cff8ec8ac1e65244c9da47c405bb3e7 --- /dev/null +++ b/src/pages/Desktop/Window/WindowBox/index.module.scss @@ -0,0 +1,47 @@ +$drag: 8px; +$shift: -6px; + +.windowBox { + position: absolute; + border: 1px solid $window-border-color; + border-radius: 3px; + background-color: $window-background; +} + +.main { + position: relative; + width: 100%; + height: 100%; +} + +.content { + width: 100%; + height: calc(100% - #{$window-header-height}); +} + +.dragRight { + position: absolute; + top: 32px; + right: $shift; + width: $drag; + height: calc(100% - #{$drag + $shift} - 32px); + cursor: e-resize; +} + +.dragBottom { + position: absolute; + bottom: $shift; + left: 0; + width: calc(100% - #{$drag + $shift}); + height: $drag; + cursor: s-resize; +} + +.dragRightBottom { + position: absolute; + right: $shift; + bottom: $shift; + width: $drag; + height: $drag; + cursor: se-resize; +} diff --git a/src/pages/Desktop/Window/WindowBox/index.tsx b/src/pages/Desktop/Window/WindowBox/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2c601187d03f2d91352f180f4c715abf223f899a --- /dev/null +++ b/src/pages/Desktop/Window/WindowBox/index.tsx @@ -0,0 +1,40 @@ +import React from 'react' +import WindowContext from '../WindowContext' +import styles from './index.module.scss' +import Header from './Header' +import { WINDOW_STATUS } from '@/common/constants' +import vars from '@/common/style/vars.scss' + +interface Props { + children: React.ReactNode +} + +const WindowBox: React.FC = props => { + const { window } = React.useContext(WindowContext) + const windowStyle = React.useMemo(() => { + const cssStyle = { ...window.style } + if (window.status === WINDOW_STATUS.MAX) { + cssStyle.left = 0 + cssStyle.top = 0 + cssStyle.width = '100%' + cssStyle.height = `calc(100% - ${vars.startToolsBarHeight})` + } + if (window.status === WINDOW_STATUS.MIN) { + cssStyle.display = 'none' + } + return cssStyle + }, [window.style, window.status]) + return ( +
+
+
+
{props.children}
+
+
+
+
+
+ ) +} + +export default WindowBox diff --git a/src/pages/Desktop/Window/WindowContext.ts b/src/pages/Desktop/Window/WindowContext.ts new file mode 100644 index 0000000000000000000000000000000000000000..9b68e1b2d740f3d22bbeab712efd92890c6b8fc6 --- /dev/null +++ b/src/pages/Desktop/Window/WindowContext.ts @@ -0,0 +1,14 @@ +import React from 'react' +import type { Window } from '@/store/windowSlice' + +export interface WindowContextType { + window: Window + id: string + index: number +} + +export default React.createContext({ + window: {} as Window, + id: '', + index: 0 +}) diff --git a/src/pages/Desktop/Window/index.module.scss b/src/pages/Desktop/Window/index.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/pages/Desktop/Window/index.tsx b/src/pages/Desktop/Window/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ca982399e3c3f966496d680492d25c1527251390 --- /dev/null +++ b/src/pages/Desktop/Window/index.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import styles from './index.module.scss' +import WindowContext from './WindowContext' +import type { Window as WindowType } from '@/store/windowSlice' +import WindowBox from './WindowBox' + +interface Props { + window: WindowType + index: number +} + +const Window: React.FC = () => { + const { window } = React.useContext(WindowContext) + return ( + +
+
+ ) +} + +const WindowWithContext: React.FC = props => { + const { window, index } = props + return ( + + + + ) +} + +export default WindowWithContext diff --git a/src/pages/Desktop/index.module.scss b/src/pages/Desktop/index.module.scss index 67d5478030b04c13d7839c44077023f691893312..b821bc8262ea9e218818948a47f1cc03fd5ac180 100644 --- a/src/pages/Desktop/index.module.scss +++ b/src/pages/Desktop/index.module.scss @@ -2,4 +2,5 @@ position: relative; width: 100%; height: 100%; + overflow: hidden; } diff --git a/src/pages/Desktop/index.tsx b/src/pages/Desktop/index.tsx index 5f44cb22cb240fb754b1325e12f1c3d2bb110563..a5ded9cda9f6e9d7c45c2f8a626a8d5a20ee98c3 100644 --- a/src/pages/Desktop/index.tsx +++ b/src/pages/Desktop/index.tsx @@ -5,12 +5,17 @@ import StartToolsBar from './StartToolsBar' import styles from './index.module.scss' import StartMenu from './StartMenu' import Mask from './Mask' +import Window from '@/pages/Desktop/Window' const Desktop: React.FC = () => { const showStartMenu = useStore((state: MyState) => state.showStartMenu) + const windowList = useStore((state: MyState) => state.windowList) return (
+ {windowList.map((window, index) => ( + + ))} {showStartMenu && } {showStartMenu && } diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 9a973e4a160258cf77610087f1d095cf13f6f591..3d255405555247d86a1c57899beed75b5bad1676 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -60,6 +60,11 @@ declare module '*.module.css' { export default classes } +declare module '*.scss' { + const classes: { readonly [key: string]: string } + export default classes +} + declare module '*.module.scss' { const classes: { readonly [key: string]: string } export default classes diff --git a/src/store/windowSlice.ts b/src/store/windowSlice.ts index 3b371086c891df4295e976ac9102497edec152cf..35de82b75510db427399e290f91afd82f2566bc3 100644 --- a/src/store/windowSlice.ts +++ b/src/store/windowSlice.ts @@ -1,11 +1,33 @@ import { StoreApi } from 'zustand' import { MyState } from './index' import _ from 'lodash' +import getId from '@utils/getId' + +const win = { + id: getId(), + title: '窗口标题名称', + status: 0, + style: { + display: 'block', + width: 800, + height: 800, + top: 200, + left: 100, + zIndex: 1 + } +} export interface Window { id: string - width: number - height: number + title: string + status: number + style: WindowStyle +} + +export interface WindowStyle { + display: string + width: number | string + height: number | string top: number left: number zIndex: number @@ -14,7 +36,9 @@ export interface Window { export interface WindowSlice { windowList: Window[] setWindowList: (windowList: Window[]) => void - editWindow: (window: Window, index: number) => void + editWindow: (window: Window, id: string) => void + setWindowStatus: (status: number, id: string) => void + closeWindow: (id: string) => void closeWindowAll: () => void } @@ -22,16 +46,38 @@ const windowListSlice = ( set: StoreApi['setState'], get: StoreApi['getState'] ) => ({ - windowList: [], // 窗口列表 + windowList: [win], // 窗口列表 setWindowList: (windowList: Window[]) => { // 设置窗口列表 set(prev => ({ windowList: windowList })) }, - editWindow: (window: Window, index: number) => { + editWindow: (window: Window, id: string) => { // 窗口属性编辑 set(({ windowList }) => { const list = _.cloneDeep(windowList) - list[index] = window + const windowIndex = list.findIndex(row => row.id === id) + list[windowIndex] = window + return { + windowList: list + } + }) + }, + setWindowStatus: (status: number, id: string) => { + set(({ windowList }) => { + const list = _.cloneDeep(windowList) + const windowIndex = list.findIndex(row => row.id === id) + const window = list[windowIndex] + window.status = status + return { + windowList: list + } + }) + }, + closeWindow: (id: string) => { + set(({ windowList }) => { + const list = _.cloneDeep(windowList) + const windowIndex = list.findIndex(row => row.id === id) + list.splice(windowIndex, 1) return { windowList: list }