Browse Source

Merge remote-tracking branch 'origin/master'feat 磁贴存储

kindring 9 months ago
parent
commit
1041786893

BIN
build/Release/better_sqlite3.node


+ 14 - 11
src/apis/baseApi.ts

@@ -7,7 +7,7 @@ import {randomId} from "@/util/random.ts";
 interface CallItem {
     callId: string;
     action: string;
-    resolve: (value: ResponseData) => void;
+    resolve: (value: ResponseData<any>) => void;
     reject: (reason: any) => void;
     endTime: number;
     // 是否在初始化前尝试发送
@@ -38,7 +38,8 @@ export type ListenerFunction = (channel: string, listener: (event: any, ...args:
 
 
 export class ApiController {
-    sign = ''
+    type = ''
+    signId = ''
     calls: Calls = {}
     notifyMap: NotifyMap = {}
     // 最近的一个过期时间
@@ -60,9 +61,9 @@ export class ApiController {
     logFn: (...args: any[]) => void = console.log
     constructor() {
     }
-    init(sign: string, sendCallback: SendFunction, listenerCallback: ListenerFunction, sendKey: string, listerKey: string){
+    init(type: string, sendCallback: SendFunction, listenerCallback: ListenerFunction, sendKey: string, listerKey: string){
         this.isInit = true
-        this.sign = sign
+        this.type = type
         this.sendCallback = sendCallback
         this.listenerCallback = listenerCallback
         this.sendKey = sendKey
@@ -105,7 +106,7 @@ export class ApiController {
         }
         return this.buildNotifyId(action)
     }
-    private apiControllerHandler = (_:any, data: ResponseData | NotifyData) =>
+    private apiControllerHandler = (_:any, data: ResponseData<any> | NotifyData) =>
     {
         switch(data.type){
             case ApiType.res:
@@ -121,7 +122,7 @@ export class ApiController {
                 break;
         }
     }
-    private callResponseHandle = (call: CallItem, responseData: ResponseData) => {
+    private callResponseHandle = (call: CallItem, responseData: ResponseData<any>) => {
         if( this.lastCheckId === call.callId){
             // 取消检测当前函数的定时器, 防止多次触发
             if(this.checkTimer){
@@ -260,22 +261,24 @@ export class ApiController {
 
     /**
      * 修改当前总api的签名
-     * @param sign
+     * @param signId
      */
-    changeSign(sign: string) {
-        this.sign = sign
+    changeSign(signId: string) {
+        this.signId = signId
     }
 
+
     /**
      * 调用ipc 获取数据
      * @param action
      * @param params
      * @param timeout
      */
-    sendQuery(action: string, params: any, timeout: number = 5000): [callId: string, Promise<ResponseData>] {
+    sendQuery(action: string, params: any, timeout: number = 5000): [callId: string, Promise<ResponseData<any>>] {
         let callId = this.buildCallId()
         let requestData: RequestData = {
             type: ApiType.req,
+            signId: this.signId,
             action: action,
             data: params,
             callId: callId,
@@ -296,7 +299,7 @@ export class ApiController {
         // 加上通信超时时间
         let endTime = timeStamp + timeout + 200
 
-        let promise: Promise<ResponseData> = new Promise((resolve, reject) => {
+        let promise: Promise<ResponseData<any>> = new Promise((resolve, reject) => {
             this.calls[callId] = {
                 action: action,
                 callId: callId,

+ 9 - 6
src/apis/magnetControl.ts

@@ -1,16 +1,17 @@
 import api from "./baseApi.ts"
 import {Magnet_Actions} from "./ApiAction.ts";
 import {ErrorCode, ResponseData} from "@/types/apiTypes.ts";
-import {Magnet, SavedMagnet} from "@/types/magnetType.ts";
+import {ChangeSaveMagnet, Magnet, SavedMagnet} from "@/types/magnetType.ts";
 import {savedMagnets2Magnets} from "@/components/magnets/magnetInfo.ts";
 
-function _magnet2savedMagnet(magnet: Magnet): SavedMagnet {
+function _magnet2savedMagnet(magnet: Magnet): ChangeSaveMagnet {
    return {
       id: magnet.id,
       type: magnet.type,
       size: magnet.size,
       x: magnet.x,
       y: magnet.y,
+      isAdd: magnet.isAdd
    }
 }
 
@@ -39,13 +40,15 @@ export async function fetchMagnetList(): Promise< ResponseData<Magnet[]> >
  */
 export async function changeMagnets(magnetList: Magnet[])
 {
-   let savedMagnets: SavedMagnet[] = magnetList.map(_magnet2savedMagnet);
-   let [_callId, promise] = api.sendQuery(Magnet_Actions.magnet_batch_update, {
-      magnetList: savedMagnets
-   });
+   let savedMagnets: ChangeSaveMagnet[] = magnetList.map(_magnet2savedMagnet);
+   let [_callId, promise] = api.sendQuery(
+       Magnet_Actions.magnet_batch_update,
+       savedMagnets
+      );
    return await promise;
 }
 
+
 /**
  * 删除磁贴信息
  * @param magnetId

+ 69 - 0
src/assets/base.css

@@ -45,6 +45,7 @@
     width: 20px;
     height: 20px;
     margin: 0 5px;
+    padding: 0;
     border-radius: 50%;
     cursor: pointer;
     position: relative;
@@ -209,6 +210,7 @@
     display: flex;
     justify-content: center;
     align-items: center;
+    z-index: 1000;
 }
 .dialog-mask::before{
     content: '';
@@ -285,8 +287,75 @@
     left: 0;
 }
 
+.tabs-w {
+    width: 100%;
+    height: calc(100% - 70px);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+.tabs-w .item{
+    flex-shrink: 0;
+}
+.tabs-w .tabs-control{
+    width: auto;
+    height: 30px;
+    display: flex;
+    align-items: center;
+}
+
+.tabs-w .tabs-control .item{
+    width: 20px;
+    height: 20px;
+    border-radius: 50%;
+    cursor: pointer;
+    background-color: #2ecc71;
+    margin: 0 8px;
+}
+.tabs-w .tabs-control .item:hover{
+    background-color: #2980b9;
+}
+.tabs-w .tabs-control .active{
+    background-color: #2980b9;
+}
+
+.tabs-w .tabs-list{
+    width: 100%;
+    height: calc(100% - 30px);
+    padding: 0 5px;
+    display: flex;
+    overflow: auto;
+}
+.tabs-w .tabs-list .item{
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: relative;
+}
+.tabs-w .tabs-list .item .drop{
+    width: 100%;
+    height: 100%;
+    position: absolute;
+}
 
 
+.btn-group{
+    width: 100%;
+    height: 40px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+.btn{
+    height: 2em;
+    display: flex;
+    padding: 0 2em;
+    align-items: center;
+    border-radius: 5px;
+    cursor: pointer;
+}
 .close-btn{
     width: 20px;
     height: 20px;

+ 3 - 0
src/common/appConfig.ts

@@ -111,5 +111,8 @@ export function initData(): AppConfig {
     return _config;
 }
 
+export function getConfig(): AppConfig {
+    return _config!;
+}
 
 

+ 46 - 0
src/common/db/magnetDb.ts

@@ -0,0 +1,46 @@
+import {db} from "./db.ts"
+import {SavedMagnet} from "@/types/magnetType.ts";
+
+function initData() {
+    // 1. 判断数据表是否存在
+    db?.schema.createTable('magnets', (table) => {
+        table.string('id').primary()
+        table.integer('x')
+        table.integer('y')
+        table.string('type')
+        table.string('size')
+    })
+}
+initData();
+
+
+export async function getMagnetList(): Promise<SavedMagnet[]> {
+    return db?.select(
+        'id',
+        'x',
+        'y',
+        'type',
+        'size'
+    ).from('magnets')
+}
+
+// 批量更改
+export async function changeMagnets(magnetList: SavedMagnet[]) {
+    await db?.transaction(async (trx) => {
+        for (const magnet of magnetList) {
+            await trx('magnets').where('id', magnet.id).update(magnet)
+        }
+    })
+}
+
+// 新增数据
+export async function addMagnet(magnet: SavedMagnet) {
+    await db?.insert(magnet).into('magnets')
+}
+
+// 删除数据
+export async function deleteMagnet(magnetId: string) {
+    await db?.delete().from('magnets').where('id', magnetId)
+}
+
+

+ 59 - 12
src/components/magnets/magnetInfo.ts

@@ -1,7 +1,7 @@
 
 // import {App} from "vue";
 
-import {Magnet, MagnetInfo, MagnetSize, SavedMagnet, showMagnetInfo} from "@/types/magnetType.ts";
+import {Magnet, MagnetInfo, MagnetSize, SavedMagnet, ShowMagnetInfo} from "@/types/magnetType.ts";
 
 
 
@@ -13,37 +13,46 @@ export const MagnetEvent = "magnet"
 // 时间组件
 export const timeMagnetInfo: MagnetInfo =
 {
+    title: '日历组件',
     type: 'TimeMagnet',
     event: 'daySelect',
     defaultSize: MagnetSize.medium,
     sizes: {
         small: {
+            title: "本周日历",
+            description: '展示本周的日历列表',
             width: 7,
             height: 3,
         },
         medium: {
+            title: '',
+            description: '',
             width: 7,
             height: 6,
         },
     },
-    component: import('@/components/magnets/timeMagnet.vue')
 }
 
+const TypeComponent: {[key:string]: any} = {
+    [timeMagnetInfo.type]: null,
+}
+
+
 
 
 
 export const magnetInfos: MagnetInfo[] =
 [
     timeMagnetInfo,
-    {...timeMagnetInfo, defaultSize: MagnetSize.small, component: import('@/components/magnets/timeMagnet.vue')},
-    {...timeMagnetInfo, defaultSize: MagnetSize.small, component: import('@/components/magnets/timeMagnet.vue')},
-    {...timeMagnetInfo, defaultSize: MagnetSize.small, component: import('@/components/magnets/timeMagnet.vue')},
-    {...timeMagnetInfo, defaultSize: MagnetSize.small, component: import('@/components/magnets/timeMagnet.vue')},
-    {...timeMagnetInfo, defaultSize: MagnetSize.small, component: import('@/components/magnets/timeMagnet.vue')},
+    {...timeMagnetInfo, defaultSize: MagnetSize.small, },
+    {...timeMagnetInfo, defaultSize: MagnetSize.small, },
+    {...timeMagnetInfo, defaultSize: MagnetSize.small, },
+    {...timeMagnetInfo, defaultSize: MagnetSize.small, },
+    {...timeMagnetInfo, defaultSize: MagnetSize.small, },
 ]
 
 
-export function getShowMagnetInfo() : showMagnetInfo[]{
+export function getShowMagnetInfo() : ShowMagnetInfo[]{
     return magnetInfos.map(magnetInfo=>{
         let size = magnetInfo.sizes[magnetInfo.defaultSize]
         if (!size) {
@@ -56,13 +65,50 @@ export function getShowMagnetInfo() : showMagnetInfo[]{
     })
 }
 
+export function findMagnetInfo(type: string): MagnetInfo | undefined
+{
+    return magnetInfos.find(magnetInfo => magnetInfo.type === type)
+}
+
+// 寻找空位置
+export function findEmptyPosition(magnets: Magnet[], width: number, height: number, maxWidth: number): {x: number, y: number}
+{
+    let x = 0;
+    let y = 0;
+    for (let i = 0; i < magnets.length; i++) {
+        // 遍历所有的磁贴组件, 找到最下方的坐标
+        let magnet = magnets[i];
+        if (magnet.x + magnet.width > x) {
+            x = magnet.x + magnet.width;
+        }
+        // 尝试看是否能够摆下元素
+        if (magnet.y + magnet.height > y) {
+            y = magnet.y + magnet.height;
+        }
+    }
+    if (x + width > maxWidth) {
+        x = 0;
+        y += height;
+    }
+    // 防止 磁贴位置超出边界
+    return {x, y}
+}
+
 /**
- * 初始化时间磁贴组件
+ * 初始化磁贴对应的组件
  * @param component
  */
-export function initTimeMagnetInfo(component: any): MagnetInfo{
-    timeMagnetInfo.component = component
-    return timeMagnetInfo
+export function initTypeComponent(type: string , component: any){
+    TypeComponent[type] = component
+}
+
+export function getComponent(type: string) : any
+{
+    let component = TypeComponent[type]
+    if (!component) {
+        throw new Error(`component not found: ${type}`)
+    }
+    return component
 }
 
 function _findMagnetInfo(type: string): MagnetInfo
@@ -91,6 +137,7 @@ function  _savedMagnet2Magnet(savedMagnet: SavedMagnet) : Magnet
         event: magnetInfo.event,
         selected: false,
         changed: false,
+        isAdd: false
     }
 }
 

+ 93 - 9
src/components/magnets/magnetTable.vue

@@ -1,14 +1,28 @@
 <script setup lang="ts">
 import {ref} from "vue";
-import { computeStyle, getShowMagnetInfo} from "@/components/magnets/magnetInfo.ts";
-import {TransitionRoot} from "@headlessui/vue";
+import {
+  computeStyle,
+  getComponent,
+  getShowMagnetInfo,
+  initTypeComponent,
+  timeMagnetInfo
+} from "@/components/magnets/magnetInfo.ts";
+import {Tab, TabGroup, TabList, TabPanel, TabPanels, TransitionRoot} from "@headlessui/vue";
+import {AddMagnetInfo, MagnetSize, ShowMagnetInfo} from "@/types/magnetType.ts";
 
+import TimeManger from "@/components/magnets/timeMagnet.vue"
 
+initComponents()
+function initComponents() {
+  initTypeComponent(timeMagnetInfo.type, TimeManger)
+}
 const magnetTable = ref()
 const showMagnetInfos = getShowMagnetInfo()
 
-console.log(showMagnetInfos)
 
+const emit = defineEmits<{
+  (e: 'addMagnet', addMagnetInfo: AddMagnetInfo): void
+}>()
 
 
 // a, a , b, b
@@ -18,6 +32,35 @@ let isSelect = ref(false)
 let setIsSelect = (flag: boolean) => {
   isSelect.value = flag
 }
+
+const selectMagnetInfo = ref<ShowMagnetInfo>(showMagnetInfos[0])
+function selectMagnetHandle(showMagnetInfo:  ShowMagnetInfo)
+{
+  selectMagnetInfo.value = showMagnetInfo;
+  setIsSelect(true);
+  changeTab(0)
+}
+
+const selectedTab = ref(0)
+function changeTab(index: number) {
+  selectedTab.value = index
+}
+
+function addMagnetHandle(){
+  // 获取磁贴类型和磁贴大小
+  let type = selectMagnetInfo.value.type
+  let sizeKeys = Object.keys(selectMagnetInfo.value.sizes)
+  let sizeKey = sizeKeys[selectedTab.value] as MagnetSize
+  let addMagnetInfo = {
+    type: type,
+    size: sizeKey
+  }
+  emit('addMagnet', addMagnetInfo)
+  // 传递选择事件给父组件
+}
+
+
+
 </script>
 
 <template>
@@ -28,9 +71,11 @@ let setIsSelect = (flag: boolean) => {
              v-for="(showMagnetInfo, index) in showMagnetInfos"
              :style="computeStyle(showMagnetInfo.size.width, showMagnetInfo.size.height, 0, 0)"
              :key="`magnet-${index}`"
+             @click="selectMagnetHandle(showMagnetInfo)"
         >
+
           <component
-              :is="showMagnetInfo.component"
+              :is="getComponent(showMagnetInfo.type)"
               :size="showMagnetInfo.defaultSize"
           ></component>
           <div class="event-mask"></div>
@@ -52,10 +97,47 @@ let setIsSelect = (flag: boolean) => {
         leaveTo="translate-y-full"
     >
       <div className="site-content-bottom">
-        测试
-        <button class="close-btn" @click="setIsSelect(false)">
-          X
-        </button>
+        <div class="dialog-title">
+          {{ selectMagnetInfo?selectMagnetInfo.title:'' }}
+          <button class="close-btn" @click="setIsSelect(false)">
+            X
+          </button>
+        </div>
+
+        <TabGroup class="tabs-w"
+                  :selectedIndex="selectedTab"
+                  @change="changeTab"
+                  as="div"
+        >
+          <TabPanels class="tabs-list">
+            <TabPanel v-for="(size, key) in selectMagnetInfo.sizes"
+                      :key="`com-${size?.title}-${key}`"
+                      class="item"
+                      as="div"
+            >
+              <component
+                  :is="getComponent(selectMagnetInfo.type)"
+                  :size="key"
+              ></component>
+              <div class="drop">
+
+              </div>
+            </TabPanel>
+          </TabPanels>
+          <TabList class="tabs-control">
+            <Tab v-for="(size, _key, i) in selectMagnetInfo.sizes"
+                 :key="size?.title"
+                 :class="`item ${i === selectedTab ? 'active' : ''}`"
+                 as="div"
+            ></Tab>
+          </TabList>
+        </TabGroup>
+
+        <div class="btn-group ">
+          <div class="btn bg-red-500 text-white" @click="addMagnetHandle">
+            添加组件
+          </div>
+        </div>
       </div>
     </TransitionRoot >
   </div>
@@ -73,7 +155,8 @@ let setIsSelect = (flag: boolean) => {
   height: 100%;
   box-sizing: border-box;
   display: flex;
-  flex-wrap: wrap;
+  flex-direction: column;
+  align-items: center;
 }
 .magnet-item-box{
   position: relative;
@@ -83,6 +166,7 @@ let setIsSelect = (flag: boolean) => {
   align-items: center;
   padding: 5px;
   border-radius: 5px;
+  margin-top: 10px;
   border: 1px solid lightskyblue;
 }
 </style>

+ 91 - 36
src/components/magnets/magnetView.vue

@@ -1,24 +1,30 @@
 <script setup lang="ts">
-import {onBeforeMount, reactive, ref, watch} from "vue";
-import { Dialog, DialogPanel, DialogTitle, TransitionRoot  } from '@headlessui/vue'
-import TimeMagnet from "@/components/magnets/timeMagnet.vue";
+import {nextTick, onBeforeMount, reactive, ref, watch} from "vue";
+// import { Dialog, DialogPanel, DialogTitle, TransitionRoot  } from '@headlessui/vue'
 import vueDrag, {MoveInfo} from "@/components/public/vueDrag.vue";
-import {Magnet, MagnetEmit} from "@/types/magnetType.ts";
-import {comMaxWidth, computeMagnetStyle, comXY, initTimeMagnetInfo} from "@/components/magnets/magnetInfo.ts";
+import {AddMagnetInfo, Magnet, MagnetEmit} from "@/types/magnetType.ts";
+import {
+  comMaxWidth,
+  computeMagnetStyle,
+  comXY, findEmptyPosition, findMagnetInfo, getComponent, initTypeComponent,
+  timeMagnetInfo,
+} from "@/components/magnets/magnetInfo.ts";
 
 import {Calendar} from "@/util/time.ts";
 import {CollisionDirection, CollisionResult, detectCollisionDirection, Drag, Rect} from "@/util/domDrag.ts";
 import IconSvg from "@/components/public/icon/iconSvg.vue";
-import {fetchMagnetList} from "@/apis/magnetControl.ts";
+import {changeMagnets, fetchMagnetList} from "@/apis/magnetControl.ts";
 import {ErrorCode, ResponseData} from "@/types/apiTypes.ts";
 import MagnetTable from "@/components/magnets/magnetTable.vue";
+import KuiDialog from "@/components/public/kui-dialog.vue";
 
-const timeMagnetInfo = initTimeMagnetInfo(TimeMagnet)
 
-const typeComponent = {
-  [timeMagnetInfo.type]: timeMagnetInfo.component
-}
+import TimeManger from "@/components/magnets/timeMagnet.vue"
 
+initComponents()
+function initComponents() {
+  initTypeComponent(timeMagnetInfo.type, TimeManger)
+}
 
 
 const magnetItems = reactive([]) as Magnet[]
@@ -65,6 +71,7 @@ const props = defineProps({
 
 const emit = defineEmits(['editModeChange'])
 
+const myEditMode = ref(props.editMode)
 // 转换为坐标值
 
 const moveTipStyle = ref()
@@ -201,6 +208,11 @@ function moveHandle(magnet: Magnet, moveInfo: MoveInfo){
   }, collidingWaitTime)
 }
 
+function moveStartHandle(magnet: Magnet)
+{
+  magnet.selected = true
+}
+
 function moveEndHandle(magnet: Magnet, moveInfo: MoveInfo)
 {
   console.log(magnet, moveInfo)
@@ -213,6 +225,7 @@ function moveEndHandle(magnet: Magnet, moveInfo: MoveInfo)
 }
 
 watch(()=>props.editMode, (val)=>{
+  myEditMode.value = val
   if(!val){
     saveMagnet()
   }
@@ -249,6 +262,49 @@ function setIsSite(value: boolean = false) {
   isSite.value = value
 }
 
+async function addMagnetHandle(addMagnetInfo: AddMagnetInfo)
+{
+  console.log(`add magnet type:${addMagnetInfo.type} size:${addMagnetInfo.size}`)
+  setIsSite(false)
+  setIsOpen(false)
+  // 生成新的组件
+  let magnetInfo = findMagnetInfo(addMagnetInfo.type)
+  if (!magnetInfo){
+    return console.error(`no match type ${addMagnetInfo.type}`)
+  }
+  let size = magnetInfo.sizes[addMagnetInfo.size]
+  if (!size){
+    return console.error(`no match size ${addMagnetInfo.size}`)
+  }
+  // 暂时接管编辑模式
+  myEditMode.value = false;
+  let {x, y} = findEmptyPosition(magnetItems, size.width, size.height, maxWidth)
+  let magnet: Magnet = {
+    id: `tmpId-${magnetItems.length}`,
+    type: addMagnetInfo.type,
+    size: addMagnetInfo.size,
+    event: magnetInfo.event,
+    x: x,
+    y: y,
+    width: size.width,
+    height: size.height,
+    selected: false,
+    changed: false,
+    isAdd: true,
+  }
+  magnetItems.push(magnet);
+  // 等待渲染完成, 再次开启编辑模式
+  await nextTick()
+  myEditMode.value = true;
+}
+
+async function saveMagnets(){
+  let result = await changeMagnets(magnetItems);
+  console.log('保存完成');
+  console.log(result);
+  emit('editModeChange')
+}
+
 </script>
 
 <template>
@@ -263,18 +319,18 @@ function setIsSite(value: boolean = false) {
        v-for="magnet in magnetItems"
        :key="magnet.id"
        :style="computeMagnetStyle(magnet)"
-       :open-drag="editMode"
+       :open-drag="myEditMode"
        :move-hide="true"
        :y-limit="false"
-              :hide-move="true"
+       :hide-move="true"
        @init="moveInitHandle"
-       @move-start="()=>{magnet.selected = true}"
+       @move-start="(_moveInfo)=>{moveStartHandle(magnet)}"
        @move="(moveInfo)=>{moveHandle(magnet, moveInfo)}"
        @move-end="(moveInfo)=>{moveEndHandle(magnet, moveInfo)}"
     >
       <div class="drag-content" >
         <component
-            :is="typeComponent[magnet.type]"
+            :is="getComponent(magnet.type)"
             :size="magnet.size"
             @magnet="eventHandler"
         ></component>
@@ -292,36 +348,35 @@ function setIsSite(value: boolean = false) {
       <div class="apple-btn mx-0.5" @click="setIsOpen(true)">
         <icon-svg icon-name="add"></icon-svg>
       </div>
-      <div class="apple-btn mx-0.5" @click="()=>{emit('editModeChange')}"> 完成</div>
+      <div class="apple-btn mx-0.5" @click="()=>{saveMagnets()}"> 完成</div>
     </div>
 
 
-    <TransitionRoot
+    <kui-dialog
         :show="isOpen"
-        as="span"
-        enter="transition duration-300 ease-out"
-        enter-from="opacity-0"
-        enter-to="opacity-100"
-        leave="transition duration-200 ease-in"
-        leave-from="opacity-100"
-        leave-to="opacity-0"
+        class-name="dialog dialog-mask"
     >
-    <Dialog class="dialog">
-      <DialogPanel class="dialog-content">
-        <DialogTitle class="dialog-title">
-          <span>新增磁贴</span>
-          <button class="close-btn" @click="setIsOpen(false)">
+
+      <div class="dialog-content">
+
+        <div class="dialog-title">
+          <div class="dialog-title-text">
+            添加磁贴
+          </div>
+          <div
+              class="close-btn"
+              @click="setIsOpen(false)">
             X
-          </button>
-        </DialogTitle>
+          </div>
+        </div>
+
         <div class="dialog-show">
-<!--          磁贴组件选择列表-->
-          <magnet-table></magnet-table>
+          <!--          磁贴组件选择列表-->
+          <magnet-table @add-magnet="addMagnetHandle" />
         </div>
-        <button @click="setIsOpen(false)">Deactivate</button>
-      </DialogPanel>
-    </Dialog>
-  </TransitionRoot>
+
+      </div>
+    </kui-dialog>
   </div>
 </template>
 

+ 56 - 0
src/components/public/kui-dialog.vue

@@ -0,0 +1,56 @@
+<script setup lang="ts">
+defineProps({
+  show: {
+    type: Boolean,
+    default: false
+  },
+  className: {
+    type: String,
+    default: ''
+  }
+
+})
+defineEmits(['close'])
+
+// function closeHandle() {
+//   emit('closeEvent');
+//   console.log('closeHandle')
+// }
+// function findDom (t = 0 ){
+//   let dom = document.getElementById('#kui-dialog')
+//   if(!dom && t < 10)
+//   {
+//     setTimeout(() => {
+//       return findDom(t++)
+//     }, 1000)
+//   }
+//   // 尝试获取body
+//   dom = document.body
+//   return dom
+// }
+//
+// function eventBind(){
+//   window.addEventListener('click', (e) => {
+//   console.log(e.target)
+//   let parentDom = findDom();
+//   if (e.target != parentDom){
+//     e.preventDefault()
+//     e.stopPropagation()
+//   }
+//   }, true)
+// }
+// 给插入位置的dom组件添加事件, 屏蔽所有事件
+
+</script>
+
+<template>
+  <Teleport to="#kui-dialog" v-if="show">
+    <div :class="className">
+      <slot></slot>
+    </div>
+  </Teleport>
+</template>
+
+<style scoped>
+
+</style>

+ 1 - 1
src/components/window/macWindow.vue

@@ -89,7 +89,7 @@ onMounted(()=>{
           </div>
         </div>
       </div>
-      <div class="window-content" id="headlessui-dialog">
+      <div class="window-content" id="kui-dialog">
         <slot></slot>
       </div>
     </div>

+ 2 - 4
src/main/AppControl.ts

@@ -10,7 +10,6 @@ import BrowserWindowConstructorOptions = Electron.BrowserWindowConstructorOption
 import {initIpc} from "./tools/ipcInit.ts";
 import {initHook} from "./tools/hookInit.ts";
 import hook from "@/util/hook.ts";
-import path from "path";
 import Path from "path";
 
 let logger = Logger.logger('controlWindow', 'info');
@@ -74,7 +73,6 @@ function findWin(sign: string): AppWindow | null {
 
 function findWinByType(type: string): AppWindow[] {
     // 同一个类型可能有多个窗口
-
     return _winArr.filter(value => value.type === type);
 }
 
@@ -192,7 +190,7 @@ function registerHotKey(hotKey: HotKeyConfig, mainWin: AppWindow){
     return true
 }
 function _createTray(app: Electron.App, mainWin: AppWindow){
-    const appPath = app.isPackaged ? path.dirname(app.getPath('exe')) : app.getAppPath();
+    const appPath = app.isPackaged ? Path.dirname(app.getPath('exe')) : app.getAppPath();
 
     logger.info(`[托盘挂载] appPath:${appPath}`);
     // 创建系统托盘
@@ -221,7 +219,7 @@ function _createTray(app: Electron.App, mainWin: AppWindow){
 
 function _createMainWindow(){
     let MainUrl : string = `app://index.html`
-    // let  preloadPath = path.join(__dirname, 'preload.js');
+    // let  preloadPath = Path.join(__dirname, 'preload.js');
     if (process.argv[2]) {
         console.log(process.argv[2])
         MainUrl = process.argv[2];

+ 31 - 0
src/main/control/api_router.ts

@@ -0,0 +1,31 @@
+import {ApiType, ErrorCode, RequestData, ResponseData} from "@/types/apiTypes.ts";
+import {Magnet_Actions} from "@/apis/ApiAction.ts";
+import {c_fetchMagnetList, c_magnet_batch_update} from "@/main/control/magnet/magnet.ts";
+
+export async function apiRouter(requestData: RequestData){
+    // 生成callId
+    let responseData: ResponseData<any>
+    switch (requestData.action)
+    {
+        case Magnet_Actions.magnet_list:
+            responseData = await c_fetchMagnetList(requestData);
+            break;
+        case Magnet_Actions.magnet_batch_update:
+            responseData = await c_magnet_batch_update(requestData);
+            break;
+        case Magnet_Actions.magnet_delete:
+        default:
+            responseData = {
+                type: ApiType.res,
+                code: ErrorCode.params,
+                callId: requestData.callId,
+                action: requestData.action,
+                msg: 'action not found',
+                data: null,
+            }
+    }
+    return responseData;
+}
+
+
+

+ 76 - 0
src/main/control/magnet/magnet.ts

@@ -0,0 +1,76 @@
+import {addMagnet, changeMagnets, getMagnetList} from "@/common/db/magnetDb.ts";
+import {ApiType, ErrorCode, RequestData, ResponseData} from "@/types/apiTypes.ts";
+import {ChangeSaveMagnet, SavedMagnet} from "@/types/magnetType.ts";
+import {handle} from "@/util/promiseHandle.ts";
+import Logger from "@/util/logger.ts";
+
+let logger = Logger.logger('magnet_control', 'info');
+
+export async function c_fetchMagnetList(requestData: RequestData){
+    let [err, result] = await handle(getMagnetList());
+    let responseData: ResponseData<any>
+    if (err) {
+        err = err as Error;
+        responseData = {
+            type: ApiType.res,
+            code: ErrorCode.db,
+            callId: requestData.callId,
+            action: requestData.action,
+            msg: err.message,
+            data: null,
+        }
+        return responseData;
+    }
+    responseData = {
+        type: ApiType.res,
+        code: ErrorCode.success,
+        callId: requestData.callId,
+        action: requestData.action,
+        msg: '',
+        data: result,
+    }
+    return responseData;
+}
+
+
+export async function c_magnet_batch_update(requestData: RequestData){
+    let responseData: ResponseData<any>
+    // 区分出新增的项和原本就有的项
+    let saveMagnets: ChangeSaveMagnet[] = requestData.data as ChangeSaveMagnet[];
+    // 分类
+    let addMagnets: SavedMagnet[] = []
+    let updateMagnets: SavedMagnet[] = []
+    for (let i = 0; i < saveMagnets.length; i++) {
+        let magnet = saveMagnets[i];
+        if (magnet.isAdd) {
+            addMagnets.push(magnet);
+        }else{
+            updateMagnets.push(magnet);
+        }
+    }
+    let changePromise = changeMagnets(updateMagnets)
+    let addPromises = addMagnets.map(magnet => addMagnet(magnet))
+    let [err, _result] = await handle(Promise.all([changePromise, ...addPromises]))
+    if (err) {
+        err = err as Error;
+        logger.error(`[更新数据失败] ${err.message}`)
+        responseData = {
+            type: ApiType.res,
+            code: ErrorCode.db,
+            callId: requestData.callId,
+            action: requestData.action,
+            msg: err.message,
+            data: null
+        }
+        return responseData;
+    }
+    responseData = {
+        type: ApiType.res,
+        code: ErrorCode.success,
+        callId: requestData.callId,
+        action: requestData.action,
+        msg: '',
+        data: null,
+    }
+    return responseData;
+}

+ 2 - 0
src/main/server/router/r_index.ts

@@ -5,4 +5,6 @@ router.get('/test', keyCheck, (req: Request, res: Response) => {
     res.send(`this is a test ${req.query.name}`);
 });
 
+
+
 export default router;

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

@@ -13,6 +13,7 @@ import {
 } from "./doWindowAction.ts";
 import appControl from "../AppControl.ts";
 import Logger from "../../util/logger.ts";
+import {ipcRouter} from "@/main/tools/ipcRouter.ts";
 
 let logger = Logger.logger('ipcInit', 'info');
 
@@ -51,4 +52,5 @@ export function initHook(){
     hookBind(actionMap.exitApp, appControl.exit);
     hookBind(actionMap.enableIgnoreMouse, enableIgnoreMouse);
     hookBind(actionMap.disableIgnoreMouse, disableIgnoreMouse);
+    hookBind(actionMap.apiControl, ipcRouter)
 }

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

@@ -12,7 +12,7 @@ function bindAction(action: IpcAction, bindReplay: boolean = false) {
         // console.log(event);
         logger.info(`${code}-${action.title},参数:${arg}`);
         let [err,res] = await handle(
-            hook.runHook(code,arg)
+            hook.runHook(code, arg)
         );
         if(err){
             logger.error(err);
@@ -48,5 +48,6 @@ export function initIpc() {
     bindAction(actionMap.exitApp);
     bindAction(windowAction.enableIgnoreMouse);
     bindAction(windowAction.disableIgnoreMouse);
+    bindAction(actionMap.apiControl);
 }
 

+ 52 - 0
src/main/tools/ipcRouter.ts

@@ -0,0 +1,52 @@
+import {apiRouter} from "@/main/control/api_router.ts";
+import {NotifyData, RequestData, ResponseData} from "@/types/apiTypes.ts";
+import AppControl from "@/main/AppControl.ts";
+import Logger from "@/util/logger.ts";
+import {actionMap} from "@/tools/IpcCmd.ts";
+
+let logger = Logger.logger('ipcRouter', 'info');
+function sendDataByType(type: string,  data: ResponseData<any> | NotifyData): boolean
+{
+    // 还需要知道往哪个窗口发送数据
+    let winArr = AppControl.findWinByType(type)
+    logger.info(`[发送数据至前端] 寻找${type}类型的窗口`);
+    if (winArr.length === 0) {
+        logger.error(`[发送数据] 未找到${type}类型的窗口`);
+        return false;
+    }
+    winArr.forEach(winObj => {
+        winObj.win?.webContents.send(actionMap.apiControl.resCode, data)
+    });
+    return true;
+}
+
+function sendDataBySign(signId: string, data: ResponseData<any> | NotifyData): boolean
+{
+    let win = AppControl.findWin(signId)
+    if(!win){
+        logger.error(`[发送数据] 未找到id为${signId}的窗口`);
+        return false;
+    }
+    win.win?.webContents.send(actionMap.apiControl.resCode, data)
+    return true;
+}
+const sendToMain = sendDataByType.bind(null, 'main')
+
+
+/**
+ * 处理窗口发来的api请求
+ * @param requestData
+ */
+export async function ipcRouter(requestData: RequestData): Promise<boolean>
+{
+    let signId = requestData.signId;
+    let responseData: ResponseData<any> = await apiRouter(requestData)
+    if(signId){
+        return sendDataBySign(signId, responseData)
+    }else
+    {
+        console.log(`[返回数据] 前端未配置窗口id, 尝试返回给main窗口`)
+        return sendToMain(responseData)
+    }
+}
+

+ 3 - 3
src/test/baseApi.test.ts

@@ -1,7 +1,7 @@
 import { ApiController } from '@/apis/baseApi.ts';
 import {ApiType, ErrorCode, NotifyData, RequestData, ResponseData} from "@/types/apiTypes.ts";
 
-let listenFn: (event:any , data: ResponseData | NotifyData) => void = () => {}
+let listenFn: (event:any , data: ResponseData<any> | NotifyData) => void = () => {}
 function sendFn(action: string, params: RequestData){
     // 随机延迟
     // console.log(`sendFn`)
@@ -27,7 +27,7 @@ describe('ApiController', () => {
     beforeEach(() => {
         apiController = new ApiController();
         // 初始化发送以及监听函数
-        apiController.init('testSign', sendFn, (_action: string, callback: (event:any , data: ResponseData | NotifyData) => void) => {
+        apiController.init('testSign', sendFn, (_action: string, callback: (event:any , data: ResponseData<any> | NotifyData) => void) => {
             // console.log(`callback init listen ${action}`)
             listenFn = callback
         }, sendActionKey, listenerActionKey);
@@ -105,7 +105,7 @@ describe('ApiController', () => {
 
         apiController.changeSign(newSign);
 
-        expect(apiController.sign).toBe(newSign);
+        expect(apiController.signId).toBe(newSign);
     });
 
     it('should set a log function', () => {

+ 1 - 1
src/tools/IpcCmd.ts

@@ -6,7 +6,7 @@ export interface IpcAction {
 }
 
 
-export const actionMap: { [key: string]: IpcAction } = {
+export const actionMap = {
     min: {
         title: '最小化',
         icon: 'minimize',

+ 5 - 1
src/types/apiTypes.ts

@@ -8,7 +8,10 @@ export enum ErrorCode {
     timeout,
     // 被取消请求, 一般由发起者触发, 也有可能是由其他客户端将其取消
     cancel,
-
+    // 数据库错误, 读写数据库失败
+    db,
+    // IO错误, 读写文件失败
+    io,
 
 }
 
@@ -25,6 +28,7 @@ export enum ApiType {
 export interface RequestData
 {
     type: ApiType.req;
+    signId: string;
     callId: string;
     // 请求的具体操作
     action: string;

+ 19 - 5
src/types/magnetType.ts

@@ -37,6 +37,13 @@ export interface Magnet {
     selected: boolean,
     // 磁贴是否被改变, 用于判断是否需要保存
     changed: boolean,
+    // 是否为新增
+    isAdd: boolean
+}
+
+export interface AddMagnetInfo {
+    type: string,
+    size: MagnetSize,
 }
 
 /**
@@ -51,9 +58,16 @@ export interface SavedMagnet
     size: MagnetSize,
 }
 
+export interface ChangeSaveMagnet extends SavedMagnet
+{
+    isAdd: boolean
+}
+
 
 
-interface size {
+interface Size {
+    title: string,
+    description: string,
     width: number,
     height: number,
 }
@@ -63,19 +77,19 @@ interface size {
  * 磁贴大小定义
  */
 export interface MagnetInfo  {
+    title: string,
     type: string,
     event: string,
     // 磁贴可以有多个可选尺寸, 不同尺寸, 对应不同宽高
     sizes:
-        { [key in MagnetSize]?: size}
+        { [key in MagnetSize]?: Size}
     ,
     defaultSize: MagnetSize,
     // 磁贴内容, 确定磁贴中的具体内容
-    component: any
 }
 
-export interface showMagnetInfo extends MagnetInfo{
-    size: size
+export interface ShowMagnetInfo extends MagnetInfo{
+    size: Size
 }
 
 

+ 10 - 9
src/util/pageHandle.ts

@@ -1,20 +1,20 @@
 import {App} from "vue";
 
-// import {ipcRenderer} from "electron";
+import {ipcRenderer} from "electron";
 import {actionMap, IpcAction, windowAction} from "../tools/IpcCmd.ts";
 import {registerWindowData} from "../types/appConfig.ts";
 import baseApi from "@/apis/baseApi.ts";
 
 // 判断是否为 webMode
 
-const ipcRenderer = {
-    on: (code: string, _: Function)=>{
-        console.log(code)
-    },
-    send: (code: string, data: any)=>{
-        console.log(code, data)
-    },
-};
+// 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;
@@ -67,6 +67,7 @@ export function windowInit(app: App, type: string){
             console.warn(`未知的窗口绑定 ${data.type}`);
             return;
         }
+        baseApi.changeSign(data.signId);
         app.config.globalProperties.$winHandle = registerWinHandle(ipcRenderer, data.signId);
         console.log(   `窗口绑定:${windowAction.bindSignId.resCode}` );
         ipcRenderer.send(windowAction.bindSignId.resCode, data.signId);

+ 3 - 2
src/util/promiseHandle.ts

@@ -1,5 +1,6 @@
-// 将promise 转为
-export function handle<T>(promise: Promise<T>): Promise<[Error | null | T, T | null | undefined]> {
+export type handleResult<T> = [Error | null | any, T | null | undefined]
+export type PromiseResult<T> = Promise<handleResult<T>>
+export function handle<T>(promise: Promise<T>): PromiseResult<T> {
     return new Promise<[Error | T | null , T | null | undefined]>(resolve => {
         promise.then(val => {
             resolve([null, val]);