初始化

This commit is contained in:
2025-08-19 14:56:38 +08:00
parent 83b8ee11de
commit 9c9387f6c2
45 changed files with 4567 additions and 8 deletions

18
src/App.vue Normal file
View File

@@ -0,0 +1,18 @@
<template>
<div id="root" ref="root-dom" class="w-100vw h-100vh"></div>
</template>
<script setup lang="ts">
import XSystem from '@/core/XSystem.ts'
import { onMounted, useTemplateRef } from 'vue'
const dom = useTemplateRef<HTMLDivElement>('root-dom')
onMounted(() => {
XSystem.instance.initialization(dom.value!);
})
</script>
<style scoped>
</style>

63
src/core/XSystem.ts Normal file
View File

@@ -0,0 +1,63 @@
import ProcessManages from './process/ProcessManages.ts'
import AppProcess from './process/AppProcess.ts'
import type { AppProcessInfo } from '@/core/process/AppProcessInfo.ts'
import { isUndefined } from 'lodash'
import { BasicSystemProcess } from '@/core/system/BasicSystemProcess.ts'
import { DesktopProcess } from '@/core/desktop/DesktopProcess.ts'
import type { IAllEvent } from '@/core/events/EventTypes.ts'
import type { IEventBuilder } from '@/core/events/IEventBuilder.ts'
import { EventBuilderImpl } from '@/core/events/impl/EventBuilderImpl.ts'
export default class XSystem {
private static _instance: XSystem = new XSystem()
private _processManages: ProcessManages = new ProcessManages()
private _eventManages: IEventBuilder<IAllEvent> = new EventBuilderImpl()
constructor() {
console.log('XSystem')
}
public static get instance() {
return this._instance
}
public get processManages() {
return this._processManages
}
public get eventManages() {
return this._eventManages
}
public initialization(dom: HTMLDivElement) {
this.run('basic-system', BasicSystemProcess).then(() => {
this.run('desktop', DesktopProcess).then((proc) => {
console.log(proc)
proc.mount(dom)
})
})
}
// 运行进程
public async run<T extends AppProcess = AppProcess>(
proc: string | AppProcessInfo,
constructor?: new (info: AppProcessInfo) => T,
): Promise<T> {
let info = typeof proc === 'string' ? this._processManages.findProcessInfoByName(proc) : proc
if (isUndefined(info)) {
throw new Error(`未找到进程信息:${proc}`)
}
// 是单例应用
if (info.singleton) {
let process = this._processManages.findProcessByName(info.name)
if (process) {
return process as T
}
}
// 创建进程
let process = isUndefined(constructor) ? new AppProcess(info) : new constructor(info)
return process as T
}
}

View File

@@ -0,0 +1,18 @@
{
"name": "setting",
"title": "设置",
"description": "设置",
"icon": "iconfont icon-setting",
"startName": "main",
"singleton": true,
"isJustProcess": false,
"windowFormConfigs": [
{
"name": "main",
"title": "设置",
"icon": "iconfont icon-setting",
"width": 800,
"height": 600
}
]
}

View File

@@ -0,0 +1,9 @@
<template>
<div>
设置APP页面
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>

View File

@@ -0,0 +1,10 @@
import {
create,
NButton,
NCard,
NConfigProvider,
} from 'naive-ui'
export const naiveUi = create({
components: [NButton, NCard, NConfigProvider]
})

View File

@@ -0,0 +1,21 @@
import { createDiscreteApi } from 'naive-ui'
import { configProviderProps } from './theme.ts'
const { message, notification, dialog, loadingBar, modal } = createDiscreteApi(
['message', 'dialog', 'notification', 'loadingBar', 'modal'],
{
configProviderProps: configProviderProps,
notificationProviderProps: {
placement: 'bottom-right',
max: 3
}
}
)
export const { messageApi, notificationApi, dialogApi, loadingBarApi, modalApi } = {
messageApi: message,
notificationApi: notification,
dialogApi: dialog,
loadingBarApi: loadingBar,
modalApi: modal
}

View File

@@ -0,0 +1,15 @@
import { type ConfigProviderProps, darkTheme, dateZhCN, type GlobalTheme, lightTheme, zhCN } from 'naive-ui'
const lTheme: GlobalTheme = {
...lightTheme,
common: {
...lightTheme.common,
primaryColor: '#0070f3'
}
}
export const configProviderProps: ConfigProviderProps = {
theme: lTheme,
dateLocale: dateZhCN,
locale: zhCN,
}

View File

@@ -0,0 +1,29 @@
/**
* 版本信息
*/
export interface Version {
/**
* 公司名称
*/
company: string
/**
* 版本号
*/
major: number
/**
* 子版本号
*/
minor: number
/**
* 修订号
*/
build: number
/**
* 私有版本号
*/
private: number
}

View File

@@ -0,0 +1,97 @@
import AppProcess from '@/core/process/AppProcess.ts'
import type { AppProcessInfo } from '@/core/process/AppProcessInfo.ts'
import XSystem from '@/core/XSystem.ts'
import { BasicSystemProcess } from '@/core/system/BasicSystemProcess.ts'
import { createApp, h } from 'vue'
import DesktopComponent from '@/core/desktop/ui/DesktopComponent.vue'
import { naiveUi } from '@/core/common/naive-ui/components.ts'
import { DesktopEventEnum } from '@/core/events/EventTypes.ts'
import { debounce } from 'lodash'
export class DesktopProcess extends AppProcess {
private _desktopRootDom: HTMLElement;
private _isMounted: boolean = false;
private _width: number = 0;
private _height: number = 0;
private _pendingResize: boolean = false;
public get desktopRootDom() {
return this._desktopRootDom;
}
public get isMounted() {
return this._isMounted;
}
public get basicSystemProcess() {
return XSystem.instance.processManages.findProcessByName<BasicSystemProcess>('basic-system')
}
public get width() {
return this._width;
}
public set width(value: number) {
if (this._height === value) return;
if (!this._isMounted) return;
this._width = value;
this._desktopRootDom.style.width = value >= 0 ? `${value}px` : '100%';
this.scheduleResizeEvent()
}
public get height() {
return this._height;
}
public set height(value: number) {
if (this._height === value) return;
if (!this._isMounted) return;
this._height = value;
this._desktopRootDom.style.height = value >= 0 ? `${value}px` : '100%';
this.scheduleResizeEvent()
}
private scheduleResizeEvent() {
if (this._pendingResize) return;
this._pendingResize = true;
Promise.resolve().then(() => {
if (this._pendingResize) {
this._pendingResize = false;
console.log('onDesktopRootDomResize')
this.eventManages.notifyEvent(DesktopEventEnum.onDesktopRootDomResize, this._width, this._height);
}
});
}
private get eventManages() {
return XSystem.instance.eventManages;
}
constructor(info: AppProcessInfo) {
super(info)
console.log('DesktopProcess')
}
public mount(dom: HTMLDivElement) {
if (this._isMounted) return;
this._width = window.innerWidth
this._height = window.innerHeight
window.addEventListener(
'resize',
debounce(() => {
this.width = window.innerWidth
this.height = window.innerHeight
}, 300),
)
dom.style.zIndex = '0';
dom.style.width = `${this._width}px`
dom.style.height = `${this._height}px`
this._desktopRootDom = dom;
const app = createApp(DesktopComponent, { process: this })
app.use(naiveUi)
app.mount(dom)
this._isMounted = true;
}
}

View File

@@ -0,0 +1,15 @@
import { AppProcessInfo } from '@/core/process/AppProcessInfo.ts'
export const DesktopProcessInfo = new AppProcessInfo({
name: 'desktop',
title: '桌面',
version: {
company: 'XZG',
major: 1,
minor: 0,
build: 0,
private: 0
},
singleton: true,
isJustProcess: true
})

View File

@@ -0,0 +1,7 @@
export interface IconType {
name: string;
icon: string;
path: string;
col: number;
row: number;
}

View File

@@ -0,0 +1,101 @@
<template>
<n-config-provider :config-provider-props="configProviderProps" class="w-full h-full pos-relative">
<div ref="desktopRootDom" class="desktop-root">
<div class="desktop-container">
<div v-for="icon in iconArr" class="icon-container">{{ icon.icon }}</div>
</div>
<div class="task-bar"></div>
</div>
</n-config-provider>
</template>
<script setup lang="ts">
import type { DesktopProcess } from '@/core/desktop/DesktopProcess.ts'
import XSystem from '@/core/XSystem.ts'
import { notificationApi } from '@/core/common/naive-ui/discrete-api.ts'
import { configProviderProps } from '@/core/common/naive-ui/theme.ts'
import { DesktopEventEnum } from '@/core/events/EventTypes.ts'
import { useIconDrag } from '@/core/desktop/utils/useIconDrag.ts'
import { onMounted } from 'vue'
import type { IconType } from '@/core/desktop/types/IconType.ts'
import { useDesktopInit } from '@/core/desktop/ui/useDesktopInit.ts'
const props = defineProps<{process: DesktopProcess}>()
console.log(props.process)
const iconArr: IconType[] = [
{
name: '文件管理器',
icon: '🗂',
path: '/',
col: 1,
row: 1
},
{
name: '浏览器',
icon: '🌐',
path: '/',
col: 1,
row: 2
},
{
name: '记事本',
icon: '📄',
path: '/',
col: 1,
row: 3
},
{
name: '音乐播放器',
icon: '🎵',
path: '/',
col: 1,
row: 4
}
]
XSystem.instance.eventManages.addEventListener(DesktopEventEnum.onDesktopRootDomResize, (width, height) => {
console.log(width, height)
notificationApi.create({ title: '桌面通知', content: `桌面尺寸变化${width}x${height}}`, duration: 2000 })
})
const iconsInit = () => {
const icons = document.querySelectorAll<HTMLDivElement>('div.icon-container')
const container = document.querySelector<HTMLDivElement>('.desktop-container')!
icons.forEach((icon: HTMLDivElement) => {
useIconDrag(icon, container)
})
}
onMounted(() => {
const container = document.querySelector<HTMLDivElement>('.desktop-container')!
console.log(container.getBoundingClientRect())
// iconsInit()
const { col, row } = useDesktopInit(container)
console.log(col.value, row.value)
})
</script>
<style lang="scss" scoped>
$icon-width: 80px;
$icon-height: 110px;
.desktop-root {
@apply w-full h-full flex flex-col;
.desktop-container {
@apply w-full flex-1 grid gap-4 grid-auto-flow-col p-4 pos-relative;
grid-template-columns: repeat(auto-fill, minmax($icon-width, 1fr));
grid-template-rows: repeat(auto-fill, minmax($icon-height, 1fr));
.icon-container {
width: $icon-width;
height: $icon-height;
@apply flex flex-col items-center justify-center rounded text-white bg-gray-200;
}
}
.task-bar {
@apply w-full h-[40px] bg-gray-200;
}
}
</style>

View File

@@ -0,0 +1,44 @@
import XSystem from '@/core/XSystem.ts'
import type { IconType } from '@/core/desktop/types/IconType.ts'
import { nextTick, onUnmounted, reactive, toRefs } from 'vue'
import { DesktopEventEnum } from '@/core/events/EventTypes.ts'
import { useDraggable } from '@vueuse/core'
export function useDesktopInit(container: HTMLElement) {
const gridTemplate = reactive({
cellWidth: 90,
cellHeight: 110,
gap: 10,
col: 1,
row: 1
})
const ro = new ResizeObserver(entries => {
const entry= entries[0]
const containerRect = entry.contentRect
gridTemplate.col = Math.floor(containerRect.width / gridTemplate.cellWidth);
gridTemplate.row = Math.floor(containerRect.height / (gridTemplate.cellHeight));
console.log(1111)
})
ro.observe(container)
onUnmounted(() => {
ro.unobserve(container)
})
// 有桌面图标的app
const apps = XSystem.instance.processManages.processInfos.filter(processInfo => !processInfo.isJustProcess)
console.log(apps)
const icons: IconType[] = apps.map(processInfo => {
return {
name: processInfo.name,
icon: processInfo.icon,
path: processInfo.startName,
col: 0,
row: 0
}
})
return {
...toRefs(gridTemplate),
}
}

View File

@@ -0,0 +1,64 @@
import { useDraggable } from '@vueuse/core'
import { ref } from 'vue'
export function useIconDrag(el: HTMLElement, container: HTMLElement) {
let offsetX = 0
let offsetY = 0
let containerRect = container.getBoundingClientRect()
el.addEventListener('mousedown', (e) => {
el.classList.add('dragging')
let rect = el.getBoundingClientRect()
console.log(rect)
offsetX = e.clientX - rect.left
offsetY = e.clientY - rect.top
// 临时脱离 grid用绝对定位移动
el.style.position = "absolute";
el.style.left = rect.left - containerRect.left + "px";
el.style.top = rect.top - containerRect.top + "px";
el.style.gridRow = "auto";
el.style.gridColumn = "auto";
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
})
function onMouseMove(e: MouseEvent) {
if (!el) return;
el.style.left = e.clientX - containerRect.left - offsetX + "px";
el.style.top = e.clientY - containerRect.top - offsetY + "px";
}
function onMouseUp(e: MouseEvent) {
if (!el) return;
const cellWidth = 90 + 16; // 图标宽度 + gap
const cellHeight = 110 + 16;
// 计算所在行列
let col = Math.round((e.clientX - containerRect.left) / cellWidth) + 1;
let row = Math.round((e.clientY - containerRect.top) / cellHeight) + 1;
// 限制在 grid 内
const maxCols = Math.floor(containerRect.width / cellWidth);
const maxRows = Math.floor(containerRect.height / cellHeight);
col = Math.max(1, Math.min(maxCols, col));
row = Math.max(1, Math.min(maxRows, row));
console.log(col, row)
// 放回 grid
el.style.position = "relative";
el.style.left = "";
el.style.top = "";
el.style.gridRow = `${row}`;
el.style.gridColumn = `${col}`;
el.classList.remove("dragging");
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
}
}

View File

@@ -0,0 +1,32 @@
import type { IEventMap } from '@/core/events/IEventBuilder.ts'
export enum DesktopEventEnum {
onDesktopRootDomResize = 'onDesktopRootDomResize'
}
export type DesktopEventParams = {
[DesktopEventEnum.onDesktopRootDomResize]: (width: number, height: number) => void
}
export enum BasicSystemEventEnum {
onAuthChange = 'onAuthChange',
onThemeChange = 'onThemeChange'
}
export type BasicSystemEventParams = {
[BasicSystemEventEnum.onAuthChange]: () => {},
[BasicSystemEventEnum.onThemeChange]: (theme: string) => void
}
export type AllEventParams = BasicSystemEventParams & DesktopEventParams
export interface IDesktopEvent extends IEventMap {
[DesktopEventEnum.onDesktopRootDomResize]: (width: number, height: number) => void
}
export interface IBasicSystemEvent extends IEventMap {
[BasicSystemEventEnum.onAuthChange]: () => {},
[BasicSystemEventEnum.onThemeChange]: (theme: string) => void
}
export interface IAllEvent extends IDesktopEvent, IBasicSystemEvent {}

View File

@@ -0,0 +1,45 @@
/**
* 事件定义
* @interface IEventMap 事件定义 键是事件名称,值是事件处理函数
*/
export interface IEventMap {
[key: string]: (...args: any[]) => void
}
/**
* 事件管理器接口定义
*/
export interface IEventBuilder<Events extends IEventMap> {
/**
* 添加事件监听
* @param eventName 事件名称
* @param handler 事件处理函数
* @param options 配置项 { immediate: 立即执行一次 immediateArgs: 立即执行的参数 once: 只监听一次 }
* @returns void
*/
addEventListener<E extends keyof Events, F extends Events[E]>(
eventName: E,
handler: F,
options?: {
immediate?: boolean
immediateArgs?: Parameters<F>
once?: boolean
},
): void
/**
* 移除事件监听
* @param eventName 事件名称
* @param handler 事件处理函数
* @returns void
*/
removeEventListener<E extends keyof Events, F extends Events[E]>(eventName: E, handler: F): void
/**
* 触发事件
* @param eventName 事件名称
* @param args 参数
* @returns void
*/
notifyEvent<E extends keyof Events, F extends Events[E]>(eventName: E, ...args: Parameters<F>): void
}

View File

@@ -0,0 +1,94 @@
import type { IEventBuilder, IEventMap } from '@/core/events/IEventBuilder.ts'
interface HandlerWrapper<T extends (...args: any[]) => any> {
fn: T
once: boolean
}
export class EventBuilderImpl<Events extends IEventMap>
implements IEventBuilder<Events>
{
private _eventHandlers = new Map<keyof Events, Set<HandlerWrapper<Events[keyof Events]>>>()
/**
* 添加事件监听器
* @param eventName 事件名称
* @param handler 监听器
* @param options { immediate: 立即执行一次 immediateArgs: 立即执行的参数 once: 只监听一次 }
* @example
* eventBus.addEventListener('noArgs', () => {})
* eventBus.addEventListener('greet', name => {}, { immediate: true, immediateArgs: ['abc'] })
* eventBus.addEventListener('onResize', (w, h) => {}, { immediate: true, immediateArgs: [1, 2] })
*/
addEventListener<E extends keyof Events, F extends Events[E]>(
eventName: E,
handler: F,
options?: {
immediate?: boolean;
immediateArgs?: Parameters<F>;
once?: boolean;
},
) {
if (!handler) return
if (!this._eventHandlers.has(eventName)) {
this._eventHandlers.set(eventName, new Set<HandlerWrapper<F>>())
}
const set = this._eventHandlers.get(eventName)!
if (![...set].some((wrapper) => wrapper.fn === handler)) {
set.add({ fn: handler, once: options?.once ?? false })
}
if (options?.immediate) {
try {
handler(...(options.immediateArgs ?? []))
} catch (e) {
console.error(e)
}
}
}
/**
* 移除事件监听器
* @param eventName 事件名称
* @param handler 监听器
* @example
* eventBus.removeEventListener('noArgs', () => {})
*/
removeEventListener<E extends keyof Events, F extends Events[E]>(eventName: E, handler: F) {
const set = this._eventHandlers.get(eventName)
if (!set) return
for (const wrapper of set) {
if (wrapper.fn === handler) {
set.delete(wrapper)
}
}
}
/**
* 通知事件
* @param eventName 事件名称
* @param args 参数
* @example
* eventBus.notifyEvent('noArgs')
* eventBus.notifyEvent('greet', 'Alice')
* eventBus.notifyEvent('onResize', 1, 2)
*/
notifyEvent<E extends keyof Events, F extends Events[E]>(eventName: E, ...args: Parameters<F>) {
if (!this._eventHandlers.has(eventName)) return
const set = this._eventHandlers.get(eventName)!
for (const wrapper of set) {
try {
wrapper.fn(...args)
} catch (e) {
console.error(e)
}
if (wrapper.once) {
set.delete(wrapper)
}
}
}
}

View File

@@ -0,0 +1,42 @@
import { v4 as uuidV4 } from 'uuid';
import XSystem from '../XSystem.ts'
import type { AppProcessInfo } from '../process/AppProcessInfo.ts'
import WindowForm from '../window/WindowForm.ts'
/**
* 进程
*/
export default class AppProcess {
private readonly _id: string = uuidV4();
private readonly _processInfo: AppProcessInfo;
// 当前进程的窗体集合
private _windowForms: Map<string, WindowForm> = new Map();
public get id() {
return this._id;
}
public get processInfo() {
return this._processInfo;
}
public get windowForms() {
return this._windowForms;
}
constructor(info: AppProcessInfo) {
console.log(`AppProcess: ${info.name}`)
this._processInfo = info;
const startName = info.startName;
XSystem.instance.processManages.addProcess(this);
// 通过设置 isJustProcess 为 true则不会创建窗体
if (!info.isJustProcess) {
this.openWindowForm(startName)
}
}
public openWindowForm(startName: string) {
const window = new WindowForm(this, startName);
this._windowForms.set(window.id, window);
}
}

View File

@@ -0,0 +1,100 @@
import type { Version } from '../common/types/Version.ts'
import type { AppProcessInfoParams } from '../process/types/AppProcessInfoParams.ts'
import type { WindowFormConfig } from '../window/types/WindowFormConfig.ts'
export class AppProcessInfo {
/**
* 应用进程名称
* @private
*/
private readonly _name: string;
/**
* 应用进程标题
* @private
*/
private readonly _title: string;
/**
* 应用进程描述
* @private
*/
private readonly _description: string;
/**
* 应用进程图标
* @private
*/
private readonly _icon: string;
/**
* 应用进程启动入口
* 对应windowFrom参数name
* @private
*/
private readonly _startName: string;
/**
* 应用版本信息
* @private
*/
private readonly _version: Version;
/**
* 应用是否只存在一个进程
* @private
*/
private readonly _singleton: boolean;
/**
* 是否只是一个进程
* @private
*/
private readonly _isJustProcess: boolean;
/**
* 进程所有的窗口配置信息
* @private
*/
private readonly _windowFormConfigs: Array<WindowFormConfig>;
constructor(info: AppProcessInfoParams) {
this._name = info.name;
this._title = info.title || '';
this._description = info.description || '';
this._icon = <string> info.icon;
this._startName = info.startName || '';
this._version = info.version || { company: 'XZG', major: 1, minor: 0, build: 0, private: 0 };
this._singleton = info.singleton;
this._isJustProcess = info.isJustProcess;
this._windowFormConfigs = info.windowFormConfigs || [];
}
public get name() {
return this._name;
}
public get title() {
return this._title;
}
public get description() {
return this._description;
}
public get icon() {
return this._icon;
}
public get startName() {
return this._startName;
}
public get version() {
return this._version;
}
public get singleton() {
return this._singleton;
}
public get isJustProcess() {
return this._isJustProcess;
}
public get windowFormConfigs() {
return this._windowFormConfigs;
}
}

View File

@@ -0,0 +1,78 @@
import type AppProcess from './AppProcess.ts'
import { AppProcessInfo } from '@/core/process/AppProcessInfo.ts'
import { BasicSystemProcessInfo } from '@/core/system/BasicSystemProcessInfo.ts'
import { DesktopProcessInfo } from '@/core/desktop/DesktopProcessInfo.ts'
import type { AppProcessInfoParams } from '@/core/process/types/AppProcessInfoParams.ts'
/**
* 进程管理
*/
export default class ProcessManages {
private _processPool: Map<string, AppProcess> = new Map<string, AppProcess>();
private _processInfos: AppProcessInfo[] = [];
public get processInfos() {
return this._processInfos;
}
constructor() {
console.log('ProcessManages')
this.loadAppProcessInfos();
}
// TODO 加载所有进程信息
public loadAppProcessInfos() {
console.log('加载所有进程信息')
// 添加内置进程
const apps = import.meta.glob<AppProcessInfoParams>('../apps/**/*.json', { eager: true })
const internalProcessInfos: AppProcessInfo[] = Object.values(apps).map(data => new AppProcessInfo(data))
this._processInfos.push(BasicSystemProcessInfo)
this._processInfos.push(DesktopProcessInfo)
this._processInfos.push(...internalProcessInfos)
}
// 添加进程
public addProcess(process: AppProcess) {
this._processPool.set(process.id, process);
}
/**
* 通过进程id查找进程
* @param id 进程id
*/
public findProcessById(id: string) {
return this._processPool.get(id);
}
/**
* 通过进程名称查找进程
* @param name 进程名称
*/
public findProcessByName<T extends AppProcess = AppProcess>(name: string) {
const pools = [...this._processPool.values()];
return pools.find(proc => proc.processInfo.name === name) as T;
}
/**
* 根据进程id删除进程
* @param id 进程id
*/
public removeProcess(id: string): void;
/**
* 根据进程删除进程
* @param process 进程信息
*/
public removeProcess(process: AppProcess): void;
public removeProcess(params: string | AppProcess) {
const id = typeof params === 'string' ? params : params.id;
this._processPool.delete(id);
}
/**
* 通过进程名称查找进程信息
*/
public findProcessInfoByName(name: string) {
return this._processInfos.find(info => info.name === name);
}
}

View File

@@ -0,0 +1,14 @@
/**
* 进程的事件
* <p>onProcessExit - 进程退出</p>
* <p>onProcessWindowFormOpen - 进程的窗体打开</p>
* <p>onProcessWindowFormExit - 进程的窗体退出</p>
* <p>onProcessWindowFormFocus - 进程的窗体获取焦点</p>
*
*/
type AppProcessEvent =
'onProcessExit' |
'onProcessWindowFormOpen' |
'onProcessWindowFormExit' |
'onProcessWindowFormFocus' |
'onProcessWindowFormBlur'

View File

@@ -0,0 +1,26 @@
import type { Version } from '../../common/types/Version.ts'
import type { WindowFormConfig } from '../../window/types/WindowFormConfig.ts'
/**
* 应用进程入参信息
*/
export interface AppProcessInfoParams {
/** 应用进程名称 */
name: string;
/** 应用进程标题 */
title?: string;
/** 应用进程描述 */
description?: string;
/** 应用进程图标 */
icon?: string;
/** 应用进程启动入口 */
startName?: string;
/** 应用版本信息 */
version?: Version;
/** 应用是否只存在一个进程 */
singleton: boolean;
/** 是否只是一个进程, 没有UI */
isJustProcess: boolean;
/** 进程所有的窗口配置信息 */
windowFormConfigs?: WindowFormConfig[];
}

View File

@@ -0,0 +1,18 @@
import AppProcess from '../process/AppProcess.ts'
import { AppProcessInfo } from '@/core/process/AppProcessInfo.ts'
/**
* 基础系统进程
*/
export class BasicSystemProcess extends AppProcess{
private _isMounted: boolean = false;
public get isMounted() {
return this._isMounted;
}
constructor(info: AppProcessInfo) {
super(info)
console.log('BasicSystemProcess')
}
}

View File

@@ -0,0 +1,18 @@
import { AppProcessInfo } from '@/core/process/AppProcessInfo.ts'
/**
* 基础系统进程信息
*/
export const BasicSystemProcessInfo = new AppProcessInfo({
name: 'basic-system',
title: '基础系统进程',
isJustProcess: true,
version: {
company: 'XZG',
major: 1,
minor: 0,
build: 0,
private: 0
},
singleton: true
});

View File

@@ -0,0 +1,17 @@
/** 单例模式
* 确保一个类只有一个实例,并提供一个全局访问点
* @param constructor
* @constructor
*/
export function Singleton<T extends { new (...args: any[]): any }>(constructor: T): T {
let instance: any;
return new Proxy(constructor, {
construct(target, argsList, newTarget) {
if (!instance) {
instance = Reflect.construct(target, argsList, newTarget);
}
return instance;
},
});
}

View File

@@ -0,0 +1,22 @@
import { v4 as uuidV4 } from 'uuid';
import type AppProcess from '../process/AppProcess.ts'
import XSystem from '../XSystem.ts'
import type { AppProcessInfo } from '../process/AppProcessInfo.ts'
export default class WindowForm {
private readonly _id: string = uuidV4();
private readonly _procId: string;
public get id() {
return this._id;
}
public get proc() {
return XSystem.instance.processManages.findProcessById(this._procId)
}
constructor(proc: AppProcess, startName: string) {
this._procId = proc.id;
console.log('WindowForm')
}
}

View File

@@ -0,0 +1,60 @@
/**
* 窗体配置
*/
export interface WindowFormConfig {
/**
* 窗体名称
*/
name: string;
/**
* 窗体标题
*/
title?: string;
/**
* 窗体图标
*/
icon?: string;
top?: number;
left?: number;
/**
* 窗体宽度
*/
width?: number;
widthAuto?: boolean;
/**
* 窗体高度
*/
height?: number;
heightAuto?: boolean;
/**
* 窗体最小宽度
*/
minWidth?: number;
/**
* 窗体最小高度
*/
minHeight?: number;
/**
* 窗体最大宽度
*/
maxWidth?: number;
/**
* 窗体最大高度
*/
maxHeight?: number;
/**
* 窗体透明度
*/
opacity?: number;
windowStyle?: string;
windowState?: number;
resizeMode?: number;
topMost?: boolean;
/**
* 是否显示在任务栏
*/
showInTaskbar?: boolean;
showTitleBarIcon?: boolean;
showTitleBarText?: boolean;
hideTitleBar?: boolean;
}

135
src/css/basic.css Normal file
View File

@@ -0,0 +1,135 @@
/* ===== 基础重置 ===== */
*,
*::before,
*::after {
box-sizing: border-box; /* 使用更直观的盒模型 */
margin: 0;
padding: 0;
}
/* ===== 根元素设置 ===== */
:root {
/* 字体设置 */
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
/* 颜色变量 */
--color-primary: #3a86ff;
--color-secondary: #8338ec;
--color-accent: #ff006e;
--color-text: #333;
--color-light: #f8f9fa;
--color-dark: #212529;
--color-gray: #6c757d;
/* 响应式基础大小 */
--base-font-size: 16px;
--spacing-unit: 1rem;
/* 过渡动画 */
--transition-speed: 0.3s;
}
/* ===== 基础HTML元素样式 ===== */
html {
scroll-behavior: smooth; /* 平滑滚动 */
font-size: var(--base-font-size);
line-height: 1.6;
}
body {
font-family: var(--font-sans);
color: var(--color-text);
background-color: var(--color-light);
-webkit-font-smoothing: antialiased; /* 字体抗锯齿 */
text-rendering: optimizeLegibility;
overflow-x: hidden;
}
/* ===== 排版元素 ===== */
h1, h2, h3, h4, h5, h6 {
line-height: 1.2;
margin-bottom: var(--spacing-unit);
font-weight: 700;
}
p {
margin-bottom: var(--spacing-unit);
}
a {
color: var(--color-primary);
text-decoration: none;
transition: color var(--transition-speed);
}
a:hover {
color: var(--color-secondary);
text-decoration: underline;
}
/* ===== 表单元素 ===== */
input,
button,
textarea,
select {
font: inherit; /* 继承字体设置 */
margin: 0;
}
button {
cursor: pointer;
background-color: transparent;
border: none;
}
/* ===== 图片和媒体 ===== */
img,
picture,
video,
canvas,
svg {
display: block;
max-width: 100%;
height: auto;
}
/* ===== 实用工具类 ===== */
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 calc(var(--spacing-unit) * 1.5);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* ===== 响应式媒体查询 ===== */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
@media (min-width: 768px) {
:root {
--base-font-size: 16px;
}
}

16
src/main.ts Normal file
View File

@@ -0,0 +1,16 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import 'virtual:uno.css'
import './css/basic.css'
// import App from './App.vue'
// const app = createApp(App)
//
// app.use(createPinia())
//
// app.mount('#app')
import XSystem from '@/core/XSystem.ts'
XSystem.instance.initialization(document.querySelector('#app')!);

12
src/stores/counter.ts Normal file
View File

@@ -0,0 +1,12 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})