Selaa lähdekoodia

feat: 磁贴组件渲染

kindring 10 kuukautta sitten
vanhempi
commit
0acb8a1d84

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 566 - 23
package-lock.json


+ 6 - 6
package.json

@@ -11,23 +11,23 @@
   },
   "devDependencies": {
     "@types/better-sqlite3": "^7.6.10",
-    "@vitejs/plugin-vue": "^4.2.3",
     "@types/express": "^4.17.21",
-    "express": "^4.19.2",
-    "log4js": "^6.9.1",
+    "@vitejs/plugin-vue": "^4.2.3",
+    "autoprefixer": "^10.4.19",
     "better-sqlite3": "^10.0.0",
     "electron": "^26.0.0",
     "electron-builder": "^24.13.3",
     "electron-rebuild": "^3.2.9",
+    "express": "^4.19.2",
     "fs-extra": "^11.2.0",
     "knex": "^3.1.0",
+    "log4js": "^6.9.1",
+    "postcss": "^8.4.38",
+    "tailwindcss": "^3.4.3",
     "typescript": "^5.0.2",
     "vite": "^4.4.5",
     "vite-plugin-optimizer": "^1.4.2",
     "vue": "^3.3.4",
     "vue-tsc": "^1.8.5"
-  },
-  "dependencies": {
-
   }
 }

+ 12 - 3
src/App.vue

@@ -1,16 +1,23 @@
 <script setup lang="ts">
 // import HelloWorld from './components/HelloWorld.vue'
 
-import { onMounted } from "vue";
+import {onMounted, ref} from "vue";
 import MacWindow from "./components/window/macWindow.vue";
+import MagnetView from "./components/magnetView.vue";
+
 onMounted(() => {
 
 });
+
+
+const title = ref("fc-ele");
+
 </script>
 
 <template>
-  <mac-window :title="'test'" :icon="'home'">
-    测试
+  <mac-window :title="title" :icon="'home'">
+    <magnet-view>
+    </magnet-view>
   </mac-window>
 </template>
 
@@ -29,4 +36,6 @@ onMounted(() => {
 .logo.vue:hover {
   filter: drop-shadow(0 0 2em #42b883aa);
 }
+
+
 </style>

+ 3 - 12
src/assets/base.css

@@ -4,15 +4,7 @@
 .no-drag{
     -webkit-app-region: no-drag;
 }
-.window{
-    width: 100%;
-    height: 100%;
-    background: #fff;
-    border-radius: 3px;
-    box-shadow: 0 0 3px #000;
-    position: relative;
-    overflow: hidden;
-}
+
 .top-bar{
     width: 100%;
     height: 30px;
@@ -103,6 +95,5 @@
     /*    允许被点击*/
 }
 
-.ml-1\.5{
-    margin-left: 1.5rem;
-}
+
+

+ 0 - 14
src/components/magnet.vue

@@ -1,14 +0,0 @@
-<script setup lang="ts">
-
-</script>
-
-<template>
-<!-- 磁帖 布局组件, -->
-<div class="magnet">
-
-</div>
-</template>
-
-<style scoped>
-
-</style>

+ 80 - 0
src/components/magnetView.vue

@@ -0,0 +1,80 @@
+<script setup lang="ts">
+import { ref} from "vue";
+import {Magnet, MagnetSize} from "@/types/magnetType.ts";
+import {computeMagnetStyle, initTimeMagnetInfo} from "@/components/magnets/magnetInfo.ts";
+
+import TimeMagnet from "@/components/magnets/timeMagnet.vue";
+
+const timeMagnetInfo = initTimeMagnetInfo(TimeMagnet)
+
+interface vueMagnet extends Magnet{
+  component: any
+}
+const magnetItemInfos: vueMagnet[] = [
+  {
+    id: `1`,
+    type: 'TimeMagnet',
+    x: 0,
+    y: 0,
+    width: timeMagnetInfo.sizes.medium?.width??0,
+    height: timeMagnetInfo.sizes.medium?.height??0,
+    size: MagnetSize.medium,
+    editMode: false,
+    selected: false,
+    component: timeMagnetInfo.component
+  },
+  {
+    id: `233`,
+    type: timeMagnetInfo.type,
+    x: 6,
+    y: 6,
+    width: timeMagnetInfo.sizes.medium?.width??0,
+    height: timeMagnetInfo.sizes.medium?.height??0,
+    size: MagnetSize.medium,
+    editMode: false,
+    selected: false,
+    component: timeMagnetInfo.component
+  }
+];
+
+
+
+const magnetItems = ref(magnetItemInfos)
+
+
+
+
+
+</script>
+
+<template>
+<!-- 磁帖 布局组件, -->
+<div class="magnet">
+
+  <!--    磁贴组件布局 -->
+  <div class="magnet-item"
+       v-for="magnet in magnetItems"
+       :key="magnet.id"
+       :style="computeMagnetStyle(magnet)"
+  >
+    <component :is="magnet.component" v-bind:size="magnet.size"></component>
+  </div>
+
+</div>
+</template>
+
+<style scoped>
+.magnet {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  position: relative;
+}
+
+.magnet-item {
+  position: absolute;
+  border-radius: 10px;
+  box-shadow: 0px 1px 2px #ccc;
+}
+
+</style>

+ 71 - 0
src/components/magnets/magnetInfo.ts

@@ -0,0 +1,71 @@
+
+// import {App} from "vue";
+
+import {Magnet, MagnetInfo, MagnetSize} from "@/types/magnetType.ts";
+
+
+const cellWidth = 50;
+const cellMargin = 10;
+
+const timeMagnetInfo: MagnetInfo =
+{
+    type: 'TimeMagnet',
+    defaultSize: MagnetSize.medium,
+    sizes: {
+        medium: {
+            width: 5,
+            height: 3,
+        },
+    },
+    component: null
+}
+
+export function initTimeMagnetInfo(component: any): MagnetInfo{
+    timeMagnetInfo.component = component
+    return timeMagnetInfo
+}
+
+
+
+export function computeMagnetStyle(magnet: Magnet) {
+    // console.log(magnet)
+    // 计算磁贴样式 元素宽度为 50px, 间距为 5px
+    return computeStyle(magnet.width, magnet.height, magnet.x, magnet.y);
+}
+
+
+export function computeStyle(w: number, h: number, x: number, y: number, sub: boolean = false) {
+    let _w = cellWidth * w + (cellMargin * w - cellMargin);
+    let _h = cellWidth * h + (cellMargin * h  - cellMargin);
+    // 计算元素位置 起始位置为 0 .
+    let _x = cellWidth * x + (cellMargin * x + cellMargin);
+    let _y = cellWidth * y + (cellMargin * y + cellMargin);
+    // console.log(w, h, x, y)
+    if (sub) {
+        return {
+            width: `${_w}px`,
+            height: `${_h}px`,
+        }
+    }
+    return {
+        width: `${_w}px`,
+        height: `${_h}px`,
+        left: `${_x}px`,
+        top: `${_y}px`
+    }
+}
+
+
+
+
+
+
+
+// 定义vue组件
+
+
+
+export default {
+    timeMagnetInfo,
+}
+

+ 89 - 0
src/components/magnets/timeMagnet.vue

@@ -0,0 +1,89 @@
+<script setup lang="ts">
+import {defineComponent, ref} from "vue";
+// import {MagnetSize} from "@/types/magnetType.ts";
+import {timeFormat} from "@/util/time.ts";
+import magnetInfos, { computeStyle} from "@/components/magnets/magnetInfo.ts";
+
+defineComponent({
+  name: "timeMagnet"
+})
+let props = defineProps({
+  size: {
+    default: magnetInfos.timeMagnetInfo.defaultSize
+  }
+})
+
+let _size = props.size;
+let sizeItem = magnetInfos.timeMagnetInfo.sizes[_size];
+// 监听 size 值的变化
+if (!sizeItem){
+  // size 属性值不存在,则使用默认值
+  _size = magnetInfos.timeMagnetInfo.defaultSize;
+  sizeItem = magnetInfos.timeMagnetInfo.sizes[_size];
+  // 获取默认值
+  if(!sizeItem){
+    throw new Error('size 属性值不存在');
+  }
+}
+
+// 计算样式
+const itemStyle = computeStyle(sizeItem.width, sizeItem.height, 0, 0, true);
+
+const hourFormat = 'HH:mm:ss'
+const yearFormat = 'yyyy年M月dd日'
+// 获取 时分秒 16:53:46
+const hourStr = ref(timeFormat(new Date(), hourFormat));
+// 获取 年月日 2021-08-31
+const yearStr = ref(timeFormat(new Date(), yearFormat));
+
+// 定时器
+function updateTime(){
+  setTimeout(()=>{
+    let newDate = new Date()
+    hourStr.value = timeFormat(newDate, hourFormat)
+    yearStr.value = timeFormat(newDate, yearFormat)
+    updateTime()
+  }, 500)
+}
+updateTime()
+</script>
+
+<template>
+  <div :class="`time-box sty-${_size}`" :style="itemStyle">
+    <div class="now-info">
+<!--      时 -->
+      <div class="hour">
+        <span>{{hourStr}}</span>
+      </div>
+<!--        年月日 -->
+      <div class="year">
+        <span>{{yearStr}}</span>
+      </div>
+    </div>
+<!--    当前日程 -->
+
+  </div>
+</template>
+
+<style scoped>
+.time-box {
+  border-radius: 5px;
+  box-sizing: border-box;
+  padding: 5px;
+}
+.now-info {
+  border-bottom: 1px solid #eee;
+}
+.sty-medium .now-info {
+  width: 100%;
+  height: 80px;
+}
+.sty-medium .now-info .hour {
+  font-size: 30px;
+}
+.sty-medium .now-info .year {
+  margin-top: 5px;
+  font-size: 16px;
+}
+
+</style>

+ 49 - 25
src/components/window/macWindow.vue

@@ -30,38 +30,62 @@ const btnClickHandel = (action: IpcAction) => {
 </script>
 
 <template>
-  <div class="window">
-    <div class="top-bar">
-      <div class="drag top-title" >
-        <slot name="top">
-          <icon-svg  :class-name="icon"/>
-          <span class="ml-1.5 ">{{title}}</span>
-        </slot>
+  <div :class="[isFull? 'max_window': 'min_window']">
+    <div class="window">
+      <div class="top-bar">
+        <div class="drag top-title" >
+          <slot name="top">
+            <icon-svg  :class-name="icon" :icon-class="icon"/>
+            <span class="ml-1.5 ">{{title}}</span>
+          </slot>
 
-      </div>
-
-      <div class="control-box">
-        <slot name="top"></slot>
-        <div :class="`no-drag btn ding ${isDing?'ding-is':''}`"  @click="switchDingHandle">
-          <span class="showTip">{{isDing?"取消置顶":"置顶"}}</span>
-        </div>
-        <div class="no-drag btn min" @click="btnClickHandel(windowAction.min)">
-          <span class="showTip">最小化</span>
         </div>
-        <div :class="`no-drag btn full ${isFull?'full-is':''}`"  @click="switchFullHandle">
-          <span class="showTip">{{isFull?'取消全屏':'全屏'}}</span>
-        </div>
-        <div class="no-drag btn close"  @click="btnClickHandel(windowAction.close)">
-          <span class="showTip">关闭窗口</span>
+
+        <div class="control-box">
+          <slot name="top"></slot>
+          <div :class="`no-drag btn ding ${isDing?'ding-is':''}`"  @click="switchDingHandle">
+            <span class="showTip">{{isDing?"取消置顶":"置顶"}}</span>
+          </div>
+          <div class="no-drag btn min" @click="btnClickHandel(windowAction.min)">
+            <span class="showTip">最小化</span>
+          </div>
+          <div :class="`no-drag btn full ${isFull?'full-is':''}`"  @click="switchFullHandle">
+            <span class="showTip">{{isFull?'取消全屏':'全屏'}}</span>
+          </div>
+          <div class="no-drag btn close"  @click="btnClickHandel(windowAction.close)">
+            <span class="showTip">关闭窗口</span>
+          </div>
         </div>
       </div>
-    </div>
-    <div class="window-content">
-      <slot></slot>
+      <div class="window-content">
+        <slot></slot>
+      </div>
     </div>
   </div>
 </template>
 
-<style scoped>
+<style>
+
+.min_window{
+  width: calc( 100% - 10px);
+  height:  calc( 100% - 10px);
+  margin: 5px;
+  box-sizing: border-box;
+}
+.max_window{
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+}
+
+.window{
+  width: 100%;
+  height: 100%;
+  background: #fff;
+  border-radius: 3px;
+  box-shadow: 0 0 3px #000;
+  position: relative;
+  overflow: hidden;
+}
 
 </style>

+ 5 - 1
src/main.ts

@@ -3,11 +3,15 @@ import './style.css'
 import AppPage from './App.vue'
 import {windowInit} from "./util/pageHandle.ts";
 import {bindIconSvg} from "@/components/public/icon/iconSvg.ts";
+import magnetInfos from "@/components/magnets/magnetInfo.ts";
+import TimeMagnet from "@/components/magnets/timeMagnet.vue";
 
 
 const app: App = createApp(AppPage)
 
-windowInit(app);
+windowInit(app, "main");
 bindIconSvg(app);
 
+
 app.mount('#app');
+app.component(magnetInfos.timeMagnetInfo.type, TimeMagnet);

+ 12 - 3
src/main/AppControl.ts

@@ -72,6 +72,12 @@ function findWin(sign: string): AppWindow | null {
     return null;
 }
 
+function findWinByType(type: string): AppWindow[] {
+    // 同一个类型可能有多个窗口
+
+    return _winArr.filter(value => value.type === type);
+}
+
 
 function removeWin(sign: string){
     let winObj = findWin(sign);
@@ -94,7 +100,7 @@ function removeWin(sign: string){
 /**
  * 遍历绑定窗口的句柄
  */
-function winTryConnect(): void {
+export function winTryConnect(): void {
     if (checkTimer) {
         clearTimeout(checkTimer);
         // 清除计时器
@@ -114,6 +120,7 @@ function winTryConnect(): void {
                         signId: item.sign,
                         baseUrl: `${BaseUrl}:${WebPort}`,
                         key: _webServer? _webServer.$serverKey : '',
+                        type: item.type,
                     });
                 } else {
                     logger.error(`窗口 ${item.sign} 的窗口对象不存在`);
@@ -189,7 +196,9 @@ function _createTray(app: Electron.App, mainWin: AppWindow){
 
     logger.info(`[托盘挂载] appPath:${appPath}`);
     // 创建系统托盘
-    const iconPath = Path.resolve( appPath,`/logo.ico`);
+    const iconPath = Path.resolve( appPath, `./logo.ico`);
+
+    logger.info(`[路径配置] iconPath: ${iconPath}`)
     mainWin.tray = new Tray(iconPath);
     mainWin.tray.setToolTip('fc-ele');
     const contextMenu = Menu.buildFromTemplate([
@@ -309,7 +318,6 @@ export async function initApp(appConfig: AppConfig, app: Electron.App) : Promise
         title: '主进程窗口',
         win: mainWindow,
         isMain: true,
-
     });
 
     // 绑定主进程
@@ -334,6 +342,7 @@ export async function initApp(appConfig: AppConfig, app: Electron.App) : Promise
 export default {
     isExitAppTask,
     findWin,
+    findWinByType,
     removeWin,
     exit
 }

+ 16 - 1
src/main/tools/doWindowAction.ts

@@ -1,4 +1,4 @@
-import AppControl from "../AppControl.ts";
+import AppControl, {winTryConnect} from "../AppControl.ts";
 import Logger from "../../util/logger.ts";
 import {AppWindow} from "../../types/appConfig.ts";
 
@@ -12,6 +12,21 @@ export async function connectedWin(sign: string){
     }
 }
 
+export async function tryConnectWin(type: string){
+    // 寻找窗口
+    let winArr = AppControl.findWinByType(type)
+    logger.info(`[前端链接窗口] 寻找${type}类型的窗口`);
+    if (winArr.length === 0) {
+        logger.error(`[前端链接窗口] 未找到${type}类型的窗口,尝试创建 ....`);
+        return;
+    }
+    // 循环修改窗口连接状态
+    winArr.forEach(winObj => {
+        winObj.isConnected = false;
+    });
+    winTryConnect();
+}
+
 
 /**
  * 关闭指定窗口

+ 3 - 2
src/main/tools/hookInit.ts

@@ -7,7 +7,7 @@ import {
     maxWin,
     minWin,
     restoreWin,
-    showWin, topWin,
+    showWin, topWin, tryConnectWin,
     unMaxWin,
     unTopWin
 } from "./doWindowAction.ts";
@@ -37,6 +37,8 @@ function hookBind(action: IpcAction, fn: HookFn<any>, bindReplay = false){
 export function initHook(){
     logger.info('initHook');
     hookBind(actionMap.bindSignId, connectedWin,true);
+    // 前端发给主进程的指令, 尝试绑定窗口
+    hookBind(actionMap.bindSignId, tryConnectWin);
     hookBind(actionMap.close, closeWin);
     hookBind(actionMap.min, minWin);
     hookBind(actionMap.max, maxWin);
@@ -47,5 +49,4 @@ export function initHook(){
     hookBind(actionMap.hide, hideWin);
     hookBind(actionMap.show, showWin);
     hookBind(actionMap.exitApp, appControl.exit);
-
 }

+ 1 - 0
src/main/tools/ipcInit.ts

@@ -36,6 +36,7 @@ export function initIpc() {
     logger.info('初始化ipc事件');
     // 绑定ipc事件
     bindAction(windowAction.bindSignId,true);
+    bindAction(windowAction.bindSignId);
     bindAction(windowAction.close);
     bindAction(windowAction.min);
     bindAction(windowAction.max);

+ 6 - 3
src/style.css

@@ -1,3 +1,6 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
 :root {
   font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
   line-height: 1.5;
@@ -25,10 +28,10 @@ html , body{
 }
 
 #app {
-  width: calc( 100% - 10px);
-  height:  calc( 100% - 10px);
+  width: 100%;
+  height:  100%;
   box-sizing: border-box;
-  margin: 5px;
+  /*margin: 5px;*/
 }
 
 a {

+ 1 - 0
src/types/appConfig.ts

@@ -64,5 +64,6 @@ export interface registerWindowData {
     signId: string;
     baseUrl: string;
     key: string;
+    type: string;
 }
 

+ 58 - 0
src/types/magnetType.ts

@@ -0,0 +1,58 @@
+/**
+ * 磁贴定义
+ * 1. 磁贴坐标抽象为格子坐标
+ * 2. 磁贴宽高不允许自定义, 只能根据组件的内容来确定可选的宽高
+ * 3. 磁贴内容, 确定磁贴中的具体内容
+ */
+
+/**
+ * 磁贴大小
+ */
+export enum MagnetSize {
+    small = 'small',
+    medium = 'medium',
+    large = 'large',
+    xLarge = 'xLarge',
+}
+
+/**
+ * 磁贴类型
+ */
+export interface Magnet {
+    // 磁贴id, 用于存储再数据库中
+    id: string,
+    // 磁贴坐标
+    x: number,
+    y: number,
+    // 磁贴宽高, 用于再更改时碰撞检测
+    width: number,
+    height: number,
+    // 磁贴内容, 确定磁贴中的具体内容
+    type: string,
+    // 磁贴大小, 用于确定磁贴的样式
+    size: MagnetSize,
+    // 编辑模式, 用于判断是否可以拖动
+    editMode: boolean,
+    // 磁贴是否被选中, 用于判断是否可以拖动
+    selected: boolean,
+
+}
+
+interface size {
+    width: number,
+    height: number,
+}
+
+/**
+ * 磁贴大小定义
+ */
+export interface MagnetInfo  {
+    type: string,
+    // 磁贴可以有多个可选尺寸, 不同尺寸, 对应不同宽高
+    sizes:
+        { [key in MagnetSize]?: size}
+    ,
+    defaultSize: MagnetSize,
+    // 磁贴内容, 确定磁贴中的具体内容
+    component: any
+}

+ 46 - 12
src/util/pageHandle.ts

@@ -4,32 +4,66 @@ import {ipcRenderer} from "electron";
 import {IpcAction, windowAction} from "../tools/IpcCmd.ts";
 import {registerWindowData} from "../types/appConfig.ts";
 
-function winHandle(ipc: Electron.IpcRenderer, windowName: string, action: IpcAction){
+// 判断是否为 webMode
+
+// const ipcRenderer = {
+//     on: (code: string, _: Function)=>{
+//         console.log(code)
+//     },
+//     send: (code: string, data: any)=>{
+//         console.log(code, data)
+//     },
+// };
+
+function winHandle(ipc: Electron.IpcRenderer, windowName: string, action: IpcAction): boolean{
     let sendCode = action.code;
-    ipc.send(sendCode, windowName);
+    try {
+        ipc.send(sendCode, windowName);
+        return true;
+    }catch (e){
+        return false;
+    }
 }
 
-function registerWinHandle(ipc: Electron.IpcRenderer, windowName: string): (action: IpcAction)=>void
+function registerWinHandle(ipc: Electron.IpcRenderer, windowName: string): (action: IpcAction)=> boolean
 {
     windowName = windowName.toString();
     return winHandle.bind(null, ipc, windowName);
 }
 
+
+function tryBindWindow(ipc: Electron.IpcRenderer, type: string): (action: IpcAction)=>void
+{
+    return (_: IpcAction): boolean =>{
+        console.log(`未绑定窗口, 等待绑定`);
+        ipc.send(windowAction.bindSignId.code, type)
+        return false;
+    }
+}
+
+
 /**
  * 初始化页面 挂载$winHandle为全局函数. 操作窗口
  * @param app
  *
  */
-export function windowInit(app: App){
+export function windowInit(app: App, type: string){
+    // 先将验证窗口绑定到全局, 接收到 绑定消息后再进行绑定
+    app.config.globalProperties.$winHandle = tryBindWindow(ipcRenderer, type)
     ipcRenderer.on(
-        windowAction.bindSignId.code,
-        (_: Electron.IpcRendererEvent , data: registerWindowData)=>
-        {
-            console.log(`获取到窗口id${data.signId}`);
-            app.config.globalProperties.$winHandle = registerWinHandle(ipcRenderer, data.signId);
-            console.log(   `窗口绑定:${windowAction.bindSignId.resCode}` );
-            ipcRenderer.send(windowAction.bindSignId.resCode, data.signId);
-        });
+    windowAction.bindSignId.code,
+    (_: Electron.IpcRendererEvent , data: registerWindowData)=>
+    {
+        console.log(`获取到窗口id${data.signId}`);
+        if(type !== data.type){
+            console.warn(`未知的窗口绑定 ${data.type}`);
+            return;
+        }
+        app.config.globalProperties.$winHandle = registerWinHandle(ipcRenderer, data.signId);
+        console.log(   `窗口绑定:${windowAction.bindSignId.resCode}` );
+        ipcRenderer.send(windowAction.bindSignId.resCode, data.signId);
+    });
+
     // todo 初始化axios
     // if(axios){
     //     axios.initAxios(

+ 88 - 0
src/util/time.ts

@@ -0,0 +1,88 @@
+/**
+ * 时间戳转时间字符串
+ * @param timestamp - 时间戳
+ * @returns {string}
+ */
+export function timestampToTime(timestamp: number): string {
+    timestamp = timestamp.toString().length === 10 ? timestamp * 1000 : timestamp;
+    let date = new Date(timestamp);
+    let Y = date.getFullYear() + '-';
+    let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+    let D = date.getDate() + ' ';
+    let h = date.getHours() + ':';
+    let m = date.getMinutes() + ':';
+    let s = date.getSeconds();
+    return Y + M + D + h + m + s;
+}
+
+/**
+ * 时间字符串转时间戳
+ * @param timeStr - 时间字符串
+ * @param isSecond - 是否返回秒级时间戳
+ * @returns {number} - 时间戳
+ */
+export function timeStrToTimeStamp(timeStr: string, isSecond: boolean): number {
+    let date = new Date(timeStr);
+    if (isSecond) {
+        return date.getTime() / 1000;
+    } else {
+        return date.getTime();
+    }
+}
+
+
+
+
+
+/**
+ * 时间格式化显示
+ * @param t - 时间数据格式
+ * @param format - 格式字符串
+ * @returns {string} - 格式化后的时间字符串
+ */
+export function timeFormat(t: Date, format: string): string {
+    let tf = function(i: number) {
+        return (i < 10 ? '0' : '') + i;
+    };
+
+    return format.replace(/yyyy|MM|M|dd|HH|mm|m|ss/g, function(a) {
+        switch (a) {
+            case 'yyyy':
+                return tf(t.getFullYear());
+            case 'MM':
+                return tf(t.getMonth() + 1);
+            case 'M':
+                return t.getMonth() + 1;
+            case 'mm':
+                return tf(t.getMinutes());
+            case 'm':
+                return t.getMinutes();
+            case 'dd':
+                return tf(t.getDate());
+            case 'HH':
+                return tf(t.getHours());
+            case 'ss':
+                return tf(t.getSeconds());
+            default:
+                return '';
+        }
+    });
+}
+
+/**
+ * 时间戳格式化显示
+ * @param timestamp - 时间戳
+ * @param format - 格式字符串
+ * @returns {string} - 格式化后的时间字符串
+ */
+export function timestampFormat(timestamp: number, format: string): string {
+    return timeFormat(new Date(timestamp), format);
+}
+
+/**
+ * 获取当前时间的Unix时间戳
+ * @returns {number} - Unix时间戳
+ */
+export function getUnixTimeStamp(): number {
+    return Math.round(new Date().getTime() / 1000);
+}

+ 2 - 1
vite.config.ts

@@ -20,11 +20,12 @@ export default defineConfig({
     },
     resolve: {
         alias: {
-            "@": "./src",
+            "@": "/src",
         },
     },
 
     server: {
+        hmr:true,
         host: '127.0.0.1',
     },
 

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä