caner 1 year ago
parent
commit
31fd86375a

+ 8 - 5
electron/main.js

@@ -46,12 +46,15 @@ class MainSerivce {
 
 
   createWindow() {
   createWindow() {
     this.mainWin = new BrowserWindow({
     this.mainWin = new BrowserWindow({
-      minWidth: 1300,
+      minWidth: 900,
       minHeight: 760,
       minHeight: 760,
-      width: 1300,
+      width: 900,
       height: 760,
       height: 760,
-      frame: false,
-      transparent: true,
+      titleBarStyle:'hidden',
+      titleBarOverlay:{
+        color:'rgba(0,0,0,0)',
+        height:40
+      },
       webPreferences: {
       webPreferences: {
         contextIsolation: true,
         contextIsolation: true,
         nodeIntegration: true,
         nodeIntegration: true,
@@ -61,7 +64,7 @@ class MainSerivce {
       },
       },
       show: false
       show: false
     })// 创建一个窗口
     })// 创建一个窗口
-
+    this.mainWin.setMenu(null)
     // 不同环境加载不同文件
     // 不同环境加载不同文件
     if (app.isPackaged) {
     if (app.isPackaged) {
       this.mainWin.loadFile(join(__dirname, './index.html'))
       this.mainWin.loadFile(join(__dirname, './index.html'))

+ 1 - 1
index.html

@@ -4,7 +4,7 @@
 <head>
 <head>
   <meta charset="UTF-8" />
   <meta charset="UTF-8" />
   <title>termcap</title>
   <title>termcap</title>
-  <style>*{margin: 0;padding: 0;}</style>
+  <style>*{margin: 0;padding: 0;box-sizing: border-box;}</style>
 </head>
 </head>
 
 
 <body>
 <body>

+ 18 - 14
package.json

@@ -12,26 +12,30 @@
     "build": "vue-tsc --noEmit; vite build; yarn buildElectronFile; yarn buildMoveFile; electron-builder build;",
     "build": "vue-tsc --noEmit; vite build; yarn buildElectronFile; yarn buildMoveFile; electron-builder build;",
     "test": "electron-builder build"
     "test": "electron-builder build"
   },
   },
-  "dependencies": {},
+  "dependencies": {
+    "naive-ui": "^2.40.1",
+    "pinia": "^2.1.7",
+    "pinia-plugin-persist": "^1.0.0",
+    "vue": "^3.3.2"
+  },
   "devDependencies": {
   "devDependencies": {
-    "@types/node": "^18.15.3",
-    "@typescript-eslint/parser": "^5.40.0",
-    "@vitejs/plugin-vue": "^4.2.3",
+    "@types/node": "^20.10.4",
+    "@typescript-eslint/parser": "^7.16.0",
+    "@vitejs/plugin-vue": "^5.0.5",
     "electron": "^25.3.0",
     "electron": "^25.3.0",
     "electron-builder": "^23.6.0",
     "electron-builder": "^23.6.0",
-    "eslint": "^8.40.0",
+    "eslint": "^8.57.0",
     "eslint-config-airbnb-base": "^15.0.0",
     "eslint-config-airbnb-base": "^15.0.0",
-    "eslint-plugin-import": "^2.27.5",
-    "eslint-plugin-vue": "^9.13.0",
-    "sass": "^1.63.3",
-    "typescript": "~5.0.4",
-    "vite": "^4.3.6",
+    "eslint-plugin-import": "^2.29.1",
+    "eslint-plugin-vue": "^9.27.0",
+    "sass": "^1.77.8",
+    "typescript": "^5.6.3",
+    "vite": "^5.3.3",
     "vite-electron-plugin": "^0.8.2",
     "vite-electron-plugin": "^0.8.2",
+    "vite-plugin-compression": "^0.5.1",
     "vite-plugin-eslint": "^1.8.1",
     "vite-plugin-eslint": "^1.8.1",
     "vite-plugin-svg-icons": "^2.0.1",
     "vite-plugin-svg-icons": "^2.0.1",
-    "vue": "^3.3.2",
-    "vue-router": "^4.2.0",
-    "vue-tsc": "^1.6.5"
+    "vue-tsc": "^2.0.26"
   },
   },
   "build": {
   "build": {
     "appId": "com.caner.termcap",
     "appId": "com.caner.termcap",
@@ -52,4 +56,4 @@
     "electronVersion": "25.3.0",
     "electronVersion": "25.3.0",
     "beforePack": "electron/beforePack.js"
     "beforePack": "electron/beforePack.js"
   }
   }
-}
+}

+ 187 - 2
src/App.vue

@@ -1,6 +1,191 @@
 <template>
 <template>
-  <router-view />
+  <n-config-provider
+    preflight-style-disabled
+    inline-theme-disabled
+  >
+    <div class="box">
+      <!-- <Head /> -->
+      <div class="box-top">
+        <n-tabs
+          ref="tabsRef"
+          v-model:value="currentMenu"
+          type="card"
+          closable
+          size="small"
+          :tabs-padding="5"
+          addable
+          @close="close"
+          @add="showAdd = true"
+          @update:value="updateScrollBar"
+        >
+          <template
+            v-for="(item, index) in tabs"
+            :key="index"
+          >
+            <n-tab-pane :name="item.name">
+              <template #tab>
+                <Icon
+                  :name="item.icon"
+                  :size="item.size"
+                />
+                {{ item.label }}
+              </template>
+            </n-tab-pane>
+          </template>
+        </n-tabs>
+      </div>
+
+      <!-- 弹窗 -->
+      <n-modal v-model:show="showAdd">
+        <Add @call-back="addBack" />
+      </n-modal>
+    </div>
+  </n-config-provider>
 </template>
 </template>
 <script setup lang='ts'>
 <script setup lang='ts'>
-window.$electron.send('close-loading')
+import { onMounted, ref } from 'vue'
+import Head from '@/components/head.vue'
+import Add from '@/components/add.vue'
+import { FormData } from '@/components/edit.vue'
+
+const currentMenu = ref('0')
+const tabsRef = ref()
+const tabs = ref([
+  {
+    name: '0', label: 'Home', icon: 'home', size: 15
+  },
+  {
+    name: '1', label: 'Home', icon: 'home', size: 15
+  }, {
+    name: '2', label: 'Home', icon: 'home', size: 15
+  }, {
+    name: '3', label: 'Home', icon: 'home', size: 15
+  }, {
+    name: '4', label: 'Home', icon: 'home', size: 15
+  }, {
+    name: '5', label: 'Home', icon: 'home', size: 15
+  }, {
+    name: '6', label: 'Home', icon: 'home', size: 15
+  }, {
+    name: '7', label: 'Home', icon: 'home', size: 15
+  }, {
+    name: '8', label: 'Home', icon: 'home', size: 15
+  }, {
+    name: '9', label: 'Home', icon: 'home', size: 15
+  }, {
+    name: '10', label: 'Home', icon: 'home', size: 15
+  },
+  {
+    name: '11', label: 'Home', icon: 'home', size: 15
+  },
+  {
+    name: '12', label: 'Home', icon: 'home', size: 15
+  },
+  {
+    name: '13', label: 'Home', icon: 'home', size: 15
+  },
+  {
+    name: '14', label: 'Home', icon: 'home', size: 15
+  },
+  {
+    name: '15', label: 'Home', icon: 'home', size: 15
+  }
+])
+const showAdd = ref(false)
+
+function close(name: string) {
+  if (name === '0') return
+  const id = tabs.value.findIndex((v) => name === v.name)
+  tabs.value.splice(id, 1)
+  if (currentMenu.value === name) currentMenu.value = tabs.value[id - 1].name
+}
+
+// 更新工具条位置
+function updateScrollBar(params: string) {
+  tabsRef.value.xScrollInstRef.scrollTo({ top: 0, left: +params * 110 - 110, behavior: 'smooth' })
+  console.log(123, tabsRef.value, +params * 110)
+}
+
+function addBack(params: FormData) {
+  console.log('双击返回', params)
+  tabs.value.push({
+    name: `${tabs.value.length + 1}`, label: params.label, icon: 'host', size: 13
+  })
+  currentMenu.value = tabs.value[tabs.value.length - 1].name
+  showAdd.value = false
+  setTimeout(() => {
+    updateScrollBar(currentMenu.value)
+  }, 0)
+}
+
+onMounted(() => {
+  window.$electron.send('close-loading')
+})
 </script>
 </script>
+<style lang="scss" scoped>
+.box {
+  width: 100vw;
+  height: 100vh;
+
+  &-top {
+    background: var(--background-color);
+    -webkit-app-region: drag;
+
+    :deep(.n-tabs) {
+      --n-tab-border-radius: 8px;
+      --n-tab-gap: 5px;
+      --n-tab-text-color: var(--font-color);
+      // height: 100%;
+      // width: 100%;
+      margin: 0 auto;
+      -webkit-app-region: no-drag;
+      // padding: 0 70px;
+      padding-left: 70px;
+
+      .n-tabs-tab {
+        min-width: 100px;
+        text-indent: 5px;
+        padding: 3px 5px !important;
+        overflow: hidden;
+        user-select: none;
+
+        &--active {
+          position: relative;
+          background: var(--background-color) !important;
+          color: var(--font-color) !important;
+        }
+
+        &--addable {
+          min-width: auto;
+          text-indent: 0;
+        }
+
+        &-wrapper {
+          padding-top: 5px;
+        }
+
+      }
+
+      // .n-tab-pane {
+      //   height: calc(100% - 34px);
+      //   padding: 0;
+      // }
+
+      .n-tabs-pad {
+        -webkit-app-region: drag;
+      }
+
+      .n-tabs-wrapper{
+        max-width: 80%;
+      }
+
+      // .n-tabs-nav-scroll-content {
+      //   padding-left: 70px;
+      //   // margin-right: 70px;
+      //   // min-width: unset;
+      //   // width: 100%;
+      // }
+    }
+  }
+}
+</style>

BIN
src/assets/GLJ.ttf


+ 1 - 0
src/assets/icons/add.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729586836528" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24661" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M472 472V120a8 8 0 0 1 8-8h64a8 8 0 0 1 8 8v352h352a8 8 0 0 1 8 8v64a8 8 0 0 1-8 8H552v352a8 8 0 0 1-8 8h-64a8 8 0 0 1-8-8V552H120a8 8 0 0 1-8-8v-64a8 8 0 0 1 8-8h352z" fill="#dbdbdb" p-id="24662"></path></svg>

+ 1 - 0
src/assets/icons/cmd.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729564655246" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="21246" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M924 75H100C44.8 75 0 119.8 0 175v674c0 55.2 44.8 100 100 100h824c55.2 0 100-44.8 100-100V175c0-55.2-44.8-100-100-100z m0 819H100c-24.8 0-45-20.2-45-45V332h914v517c0 24.8-20.2 45-45 45zM670.5 206c0 22.3-18.1 40.4-40.4 40.4-22.3 0-40.4-18.1-40.4-40.4s18.1-40.4 40.4-40.4c22.3 0 40.4 18.1 40.4 40.4z m249.2 0c0 22.3-18.1 40.4-40.4 40.4-22.3 0-40.4-18.1-40.4-40.4s18.1-40.4 40.4-40.4c22.3 0 40.4 18.1 40.4 40.4z m-124.6 0c0 22.3-18.1 40.4-40.4 40.4-22.3 0-40.4-18.1-40.4-40.4s18.1-40.4 40.4-40.4c22.3 0 40.4 18.1 40.4 40.4zM186 783.5c11.9 10.2 31.3 10.2 43.2 0l152.5-130.8c6-5.1 8.9-11.9 8.9-18.6 0-6.7-3-13.5-8.9-18.6L229.2 484.7c-11.9-10.2-31.3-10.2-43.2 0-11.9 10.2-11.9 26.8 0 37l131 112.4-131 112.3c-11.9 10.2-11.9 26.8 0 37.1z m513.2 5H464.6c-15.2 0-27.5-12.3-27.5-27.5s12.3-27.5 27.5-27.5h234.6c15.2 0 27.5 12.3 27.5 27.5s-12.3 27.5-27.5 27.5z" p-id="21247" fill="#dbdbdb"></path></svg>

+ 1 - 0
src/assets/icons/file.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729235962129" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15715" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M321.792 51.2c27.648 0 54.1696 10.752 73.728 30.0032l125.8496 123.5456 350.208 0.0512c57.6 0 104.2944 45.824 104.2944 102.4 0 4.5056-0.3072 9.0624-0.9216 13.5168l-6.656 48.9472a101.7856 101.7856 0 0 0-40.448-11.0592C981.6064 362.752 1024 406.9376 1024 460.8c0 5.632-0.512 11.264-1.4336 16.8448l-69.5296 409.6c-8.3456 49.3568-51.8656 85.5552-102.8608 85.5552H133.632a103.9872 103.9872 0 0 1-100.864-76.3904A102.4 102.4 0 0 0 131.6864 972.8h-27.4432C46.6944 972.8 0 926.976 0 870.4V153.6c0-56.576 46.6944-102.4 104.2944-102.4h217.4976z m597.9136 368.64H237.824c-19.0976 0-35.84 12.8-40.448 31.0272l-104.2944 409.6a41.3184 41.3184 0 0 0 40.448 50.8928h716.6464a41.472 41.472 0 0 0 41.1648-34.2016l69.5296-409.6a41.3184 41.3184 0 0 0-41.1648-47.7184z m-597.9136-307.2H104.2944a41.3184 41.3184 0 0 0-41.728 40.96v573.2864l74.0864-290.9184A103.936 103.936 0 0 1 237.824 358.4h668.8256l6.2464-45.7728a41.3184 41.3184 0 0 0-41.3696-46.3872l-350.208-0.0512h-25.9072L351.2832 124.6208a42.0864 42.0864 0 0 0-29.4912-11.9808z" p-id="15716"></path></svg>

+ 1 - 0
src/assets/icons/form.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729579471201" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="23461" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M847.530667 34.133333H176.469333C133.461333 34.133333 98.474667 66.389333 98.474667 106.325333v811.52c0 39.765333 34.986667 72.192 77.994666 72.192h671.061334c43.008 0 77.994667-32.256 77.994666-72.192V106.325333c0-39.936-34.986667-72.192-77.994666-72.192z m-525.653334 252.586667c13.653333-12.970667 35.328-12.629333 48.298667 1.024l80.213333 83.968 204.8-191.488c13.824-12.8 35.328-12.117333 48.298667 1.536 12.8 13.824 12.117333 35.328-1.536 48.298667L484.522667 433.493333c-9.728 9.045333-22.186667 13.653333-34.816 13.653334-13.482667 0-26.794667-5.290667-36.864-15.701334l-92.16-96.426666c-12.970667-13.653333-12.458667-35.328 1.194666-48.298667zM716.8 804.181333H307.2c-18.773333 0-34.133333-15.36-34.133333-34.133333s15.36-34.133333 34.133333-34.133333h409.6c18.773333 0 34.133333 15.36 34.133333 34.133333s-15.36 34.133333-34.133333 34.133333z m0-162.986666H307.2c-18.773333 0-34.133333-15.36-34.133333-34.133334s15.36-34.133333 34.133333-34.133333h409.6c18.773333 0 34.133333 15.36 34.133333 34.133333s-15.36 34.133333-34.133333 34.133334z" fill="#dbdbdb" p-id="23462"></path></svg>

+ 4 - 0
src/assets/icons/home.svg

@@ -0,0 +1,4 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1729235761489" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6385" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink">
+<path d="M946.5 505L560.1 118.8l-25.9-25.9c-12.3-12.2-32.1-12.2-44.4 0L77.5 505c-12.3 12.3-18.9 28.6-18.8 46 0.4 35.2 29.7 63.3 64.9 63.3h42.5V940h691.8V614.3h43.4c17.1 0 33.2-6.7 45.3-18.8 12.1-12.1 18.7-28.2 18.7-45.3 0-17-6.7-33.1-18.8-45.2zM568 868H456V664h112v204z m217.9-325.7V868H632V640c0-22.1-17.9-40-40-40H432c-22.1 0-40 17.9-40 40v228H238.1V542.3h-96l370-369.7 23.1 23.1L882 542.3h-96.1z" ></path>
+</svg>

+ 1 - 0
src/assets/icons/host.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729560824467" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14652" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M921.261301 91.448732 105.561191 91.448732c-44.595369 0-80.723264 33.869901-80.723264 80.723264l0 561.111356c0 44.595369 33.869901 80.723264 80.723264 80.723264l276.039691 0c0 5.644983-1.693495 10.725469-1.693495 16.370452-1.693495 16.370452-10.725469 33.869901-10.725469 33.869901-1.693495 1.693495-5.644983 7.338479-5.644983 7.338479-7.338479 7.338479-10.725469 16.370452-10.725469 27.095921 0 21.450937 16.370452 33.869901 33.869901 33.869901l257.975744 0c18.063947 0 30.482911-16.370452 30.482911-33.869901 0-10.725469-1.693495-18.063947-10.725469-27.095921 0-1.693495-1.693495-1.693495-1.693495-1.693495-5.644983-5.080485-10.725469-10.725469-16.370452-57.578831l270.394708 0c44.595369 0 80.723264-33.869901 80.723264-80.723264L997.468578 168.785006C1001.984564 127.576626 965.85667 91.448732 921.261301 91.448732zM939.325248 591.594267c0 7.338479-7.338479 16.370452-16.370452 16.370452L105.561191 607.964719c-7.338479 0-16.370452-7.338479-16.370452-16.370452L89.190739 161.446527c0-7.338479 7.338479-16.370452 16.370452-16.370452l819.0871 0c7.338479 0 16.370452 7.338479 16.370452 16.370452l0 430.14774L939.325248 591.594267z" fill="#dbdbdb" p-id="14653"></path></svg>

+ 1 - 0
src/assets/icons/split.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729235864820" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10185" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M880.64 0H143.36A146.5344 146.5344 0 0 0 0 143.36v737.28A146.5344 146.5344 0 0 0 143.36 1024h737.28a146.5344 146.5344 0 0 0 143.36-143.36V143.36A143.36 143.36 0 0 0 880.64 0zM76.8 880.64V153.6h399.36v793.6H148.48a68.9664 68.9664 0 0 1-71.68-66.56z m870.4 0a67.7888 67.7888 0 0 1-66.56 66.56h-327.68V153.6h399.36v727.04z" p-id="10186"></path></svg>

+ 31 - 0
src/assets/plugin.ts

@@ -0,0 +1,31 @@
+import {
+  create,
+  NConfigProvider,
+  NModal,
+  NScrollbar,
+  NTooltip,
+  NTabs,
+  NTabPane,
+  NButton,
+  NForm,
+  NFormItem,
+  NInput
+} from 'naive-ui'
+
+const naive = create({
+  components: [
+    NConfigProvider,
+    NModal,
+    NScrollbar,
+    NTooltip,
+    NTabs,
+    NTabPane,
+    NButton,
+    NForm,
+    NFormItem,
+    NInput
+
+  ]
+})
+
+export default naive

+ 31 - 0
src/assets/theme.scss

@@ -0,0 +1,31 @@
+* {
+  box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+  user-select: none;
+}
+
+@font-face {
+  font-family: 'GLJ';
+  src: url("@/assets/GLJ.ttf");
+  font-style: normal;
+}
+
+
+@media (prefers-color-scheme: dark) {
+  :root {
+    --background-color: #1d1d1d;
+    --font-color: #ccc;
+
+  }
+
+}
+
+
+@media (prefers-color-scheme: light) {
+  :root {
+    --background-color: white;
+    --font-color: #1d1d1d;
+
+  }
+}

+ 218 - 0
src/components/add.vue

@@ -0,0 +1,218 @@
+<template>
+  <div class="add">
+    <div class="add-top">
+      <div>
+        <Icon
+          name="home"
+          :size="20"
+        />
+        <span>Home</span>
+      </div>
+
+      <n-button
+        size="tiny"
+        text
+        @click="showEdit = true; editData = undefined;"
+      >
+        <Icon name="add" />
+      </n-button>
+    </div>
+    <div class="add-content">
+      <div class="add-content-title">
+        History_
+      </div>
+      <n-scrollbar style="height: calc(100% - 30px);">
+        <div class="add-content-items">
+          <template
+            v-for="(item, index) in history"
+            :key="index"
+          >
+            <div class="add-content-items-item">
+              <Icon
+                name="cmd"
+                :size="30"
+              />
+              <div>
+                <p>{{ item.label }}</p>
+                <p>ssh {{ item.userName }}@{{ item.host }}:{{ item.port }}</p>
+              </div>
+              <div
+                class="add-content-items-item-edit"
+                @click.stop="edit(item)"
+              >
+                <Icon
+                  name="close"
+                  color="white"
+                  :size="15"
+                  @click.stop="history.splice(index, 1)"
+                />
+              </div>
+            </div>
+          </template>
+        </div>
+      </n-scrollbar>
+    </div>
+    <div
+      v-if="showEdit"
+      class="add-edit"
+    >
+      <Edit
+        :data="editData"
+        @call-back="editBack"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup lang='ts'>
+import { reactive, ref } from 'vue'
+import Edit, { FormData } from '@/components/edit.vue'
+
+const emit = defineEmits<{(evt: 'callBack', value: FormData): void}>()
+const history = ref([
+  {
+    label: 'orange', userName: 'root', host: 'caner.top', port: '49657', password: ''
+  }
+] as FormData[])
+const click = reactive({
+  num: 0,
+  delay: 200,
+  timer: null as any
+})
+const showEdit = ref(false)
+const editData = ref(undefined as FormData | undefined)
+
+function edit(item: FormData) {
+  click.num++
+  if (click.num === 1) {
+    click.timer = setTimeout(() => {
+      editData.value = { ...item }
+      showEdit.value = true
+      click.num = 0
+    }, click.delay)
+  } else {
+    clearTimeout(click.timer)
+    emit('callBack', { ...item })
+    click.num = 0
+  }
+}
+
+function editBack(params: null | FormData) {
+  if (params) {
+  // 判断是编辑还是新增
+    const index = history.value.findIndex((el) => el.host === params.host && el.port === params.port && el.userName === params.userName)
+    if (index !== -1) {
+      console.log('修改', params)
+      history.value[index] = { ...params }
+    } else {
+      console.log('新增')
+      history.value.push({ ...params })
+    }
+  }
+  showEdit.value = false
+}
+</script>
+<style lang="scss" scoped>
+.add {
+  width: 90vw;
+  height: 60vh;
+  border-radius: 10px;
+  font-size: 14px;
+  overflow: hidden;
+  background: var(--background-color);
+
+  &-top {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 15px;
+    font-size: 15px;
+
+    &>div:first-child {
+      display: flex;
+      align-items: center;
+
+      &>span {
+        margin-left: 5px;
+        font-weight: bold;
+      }
+    }
+  }
+
+  &-content {
+    height: calc(100% - 42px);
+    padding: 0 10px 10px 10px;
+
+    &-title {
+      font-weight: bold;
+      margin-bottom: 10px;
+      text-indent: 10px;
+      font-size: 14px;
+    }
+
+    &-items {
+      padding: 0 20px;
+      display: flex;
+      flex-wrap: wrap;
+      align-items: flex-start;
+
+      &-item {
+        display: flex;
+        align-items: center;
+        border: solid 1px #ccc;
+        border-radius: 10px;
+        padding: 10px;
+        cursor: pointer;
+        position: relative;
+        overflow: hidden;
+        margin-bottom: 10px;
+        width: 240px;
+
+        &>div:nth-child(2) {
+          margin-left: 10px;
+          font-size: 13px;
+
+          &>p:last-child {
+            color: #ccc;
+          }
+        }
+
+        &-edit {
+          position: absolute;
+          width: 100%;
+          height: 100%;
+          background: rgba($color: #000000, $alpha: 0.2);
+          display: none;
+          align-items: flex-start;
+          justify-content: flex-end;
+          left: 0;
+          top: 0;
+          margin: 0;
+          padding: 5px;
+        }
+
+        &:hover &-edit {
+          display: flex;
+        }
+        &:not(:nth-child(3n)){
+          margin-right: 10px;
+        }
+      }
+    }
+  }
+
+  &-edit {
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    background: rgba($color: #000000, $alpha: 0.5);
+    top: 0;
+    left: 0;
+    z-index: 9999;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+  }
+}
+</style>

+ 236 - 0
src/components/edit.vue

@@ -0,0 +1,236 @@
+<template>
+  <div class="edit">
+    <div class="edit-top">
+      <span>Host Setting</span>
+      <Icon
+        name="close"
+        :size="20"
+        @click="callBack(false)"
+      />
+    </div>
+    <div class="edit-content">
+      <Icon
+        name="form"
+        :size="240"
+      />
+
+      <div>
+        <n-form
+          ref="formRef"
+          :model="formData"
+          :rules="rules"
+        >
+          <div>
+            <n-form-item
+              label="Address"
+              path="host"
+              inline
+            >
+              <n-input
+                v-model:value="formData.host"
+                placeholder="请输入"
+                clearable
+              />
+            </n-form-item>
+            <n-form-item
+              label="Port"
+              path="port"
+              inline
+            >
+              <n-input
+                v-model:value="formData.port"
+                placeholder="请输入"
+                clearable
+              />
+            </n-form-item>
+          </div>
+
+          <n-form-item
+            label="Label"
+            path="label"
+          >
+            <n-input
+              v-model:value="formData.label"
+              placeholder="请输入"
+              clearable
+            />
+          </n-form-item>
+          <n-form-item
+            label="UserName"
+            path="userName"
+          >
+            <n-input
+              v-model:value="formData.userName"
+              placeholder="请输入"
+              clearable
+            />
+          </n-form-item>
+          <n-form-item
+            label="Passord"
+            path="password"
+          >
+            <n-input
+              v-model:value="formData.password"
+              placeholder="请输入"
+              clearable
+            />
+          </n-form-item>
+        </n-form>
+        <div>
+          <n-button
+            ghost
+            type="tertiary"
+            @click="callBack(false)"
+          >
+            Cancel
+          </n-button>
+          <n-button
+            type="info"
+            ghost
+            @click="callBack(true)"
+          >
+            Save
+          </n-button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang='ts'>
+import { FormItemRule } from 'naive-ui'
+import { ref } from 'vue'
+
+export interface FormData {
+  host: string,
+  port: string,
+  label: string,
+  userName: string,
+  password: string
+}
+const props = withDefaults(defineProps<{
+  data?:FormData
+}>(), {
+  data: () => ({
+    host: '',
+    port: '',
+    label: '',
+    userName: '',
+    password: ''
+  })
+})
+const emit = defineEmits<{(evt: 'callBack', value: FormData | null): void}>()
+const formRef = ref()
+class FormOldData {
+  host: string
+
+  port: string
+
+  label: string
+
+  userName: string
+
+  password: string
+
+  constructor() {
+    this.host = ''
+    this.port = ''
+    this.label = ''
+    this.userName = ''
+    this.password = ''
+  }
+}
+const formData = ref(props.data || new FormOldData())
+const rules = ref({
+  host: {
+    required: true, message: '请输入', validator: (_: FormItemRule, value: string) => !!value
+  },
+  port: {
+    required: true, message: '请输入', validator: (_: FormItemRule, value: string) => !!(/^[0-9]*$/.test(value)) && !!value
+  },
+  label: {
+    required: true, message: '请输入', validator: (_: FormItemRule, value: string) => !!value
+  },
+  userName: {
+    required: true, message: '请输入', validator: (_: FormItemRule, value: string) => !!value
+  },
+  password: {
+    required: true, message: '请输入', validator: (_: FormItemRule, value: string) => !!value
+  }
+})
+
+async function callBack(type:boolean) {
+  const data = type ? { ...formData.value } : null
+  if (type) await formRef.value.validate()
+  emit('callBack', data)
+  formData.value = new FormOldData()
+}
+</script>
+
+<style lang="scss" scoped>
+.edit {
+  width: 90vw;
+  height: 60vh;
+  background: var(--background-color);
+  border-radius: 10px;
+  font-size: 14px;
+  overflow: hidden;
+
+  &-top {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-weight: bold;
+    padding: 10px 15px;
+
+    &>svg {
+      cursor: pointer;
+    }
+  }
+
+  &-content {
+    padding: 10px 15px 0 0;
+    display: flex;
+    align-items: center;
+
+    &>svg {
+      margin-right: 10px;
+    }
+
+    &>div {
+      width: 100%;
+
+      :deep(.n-form) {
+        .n-input {
+          --n-border-focus: rgba(0, 0, 0, 0);
+          --n-border-hover: rgba(0, 0, 0, 0);
+          --n-box-shadow-focus: 0 0 0 2px rgba(0, 0, 0, .2);
+        }
+        &>div:first-child{
+          display: flex;
+          &>div:first-child{
+            width: 78%;
+            margin-right: 2%;
+          }
+        }
+      }
+
+      &>div:last-child {
+        display: flex;
+        align-items: center;
+        justify-content: flex-end;
+
+        &>button:last-child {
+          margin-left: 15px;
+        }
+        :deep(.n-button){
+          --n-text-color-hover:none;
+          --n-border-focus:1px solid #0000;
+          --n-border-hover:1px solid #0000;
+        }
+      }
+    }
+
+  }
+}
+</style>

+ 17 - 0
src/components/head.vue

@@ -0,0 +1,17 @@
+<template>
+  <div class="head" />
+</template>
+
+<style lang="scss" scoped>
+.head {
+    width: 100%;
+    height: 40px;
+    user-select: none;
+    text-shadow: 0px -2px -4px var(--shadow-color);
+    -webkit-app-region: drag;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: #353739;
+}
+</style>

+ 8 - 17
src/main.ts

@@ -1,23 +1,14 @@
 import { createApp } from 'vue'
 import { createApp } from 'vue'
-import { createRouter, RouteRecordRaw, createWebHistory } from 'vue-router'
+import { createPinia } from 'pinia'
+import piniaPersist from 'pinia-plugin-persist'
 import App from './App.vue'
 import App from './App.vue'
 import Icon from '@/components/icon.vue'
 import Icon from '@/components/icon.vue'
+import naive from '@/assets/plugin'
 import 'virtual:svg-icons-register'
 import 'virtual:svg-icons-register'
-import '@/services/rem'
-// 动态路由
-const routes = Object.values(import.meta.glob('./views/*/route.ts', { eager: true, import: 'default' })) as unknown as RouteRecordRaw[]
-routes.push({ path: '/:path(.*)', redirect: '/' })
+import '@/assets/theme.scss'
 
 
-const app = createApp(App)
-const router = createRouter({
-  history: createWebHistory(),
-  routes
-})
-app.component('Icon', Icon)
-// 路由守卫
-// router.beforeEach((to, from, next) => {
-//   // do something
-//   next()
-// })
+const store = createPinia()
+store.use(piniaPersist)
 
 
-app.use(router).mount('#app')
+createApp(App).use(store).use(naive).component('Icon', Icon)
+  .mount('#app')

+ 0 - 19
src/services/rem.ts

@@ -1,19 +0,0 @@
-// 18fontsize = (18/100)rem
-(function (doc, win) {
-  const docEl = doc.documentElement
-  const head = docEl.getElementsByTagName('head')[0]
-  const resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize'
-  const metaEl = doc.createElement('meta')
-  metaEl.name = 'viewport'
-  metaEl.content = 'initial-scale=1,maximum-scale=1, minimum-scale=1'
-  head.appendChild(metaEl)
-  const recalc = function () {
-    // 1rem =100px
-    const width = docEl.clientWidth
-    docEl.style.fontSize = `${100 * (width / 1920)}px`
-  }
-  recalc()
-  if (!doc.addEventListener) { return }
-  win.addEventListener(resizeEvt, recalc, false)
-  console.log('rem自适应')
-}(document, window))

+ 17 - 0
src/store/index.ts

@@ -0,0 +1,17 @@
+import { defineStore } from 'pinia'
+
+// id必填,且需要唯一
+const useStore = defineStore('index', {
+  state: () => ({
+    loading: false,
+  }),
+  actions: {
+    setLoading(data: boolean) {
+      this.loading = data
+    }
+  },
+  persist: {
+    enabled: true // true 表示开启持久化保存
+  }
+})
+export default useStore

+ 0 - 16
src/views/index/index.vue

@@ -1,16 +0,0 @@
-<script setup lang="ts">
-</script>
-<template>
-  <div>
-    <icon
-      name="close"
-      :size="20"
-    />
-  </div>
-</template>
-<style lang="scss" scoped>
-  div{
-    background: red;
-    -webkit-app-region: drag;
-  }
-</style>

+ 0 - 9
src/views/index/route.ts

@@ -1,9 +0,0 @@
-import home from './index.vue'
-
-export default {
-  path: '/',
-  meta: {
-    authorize: true
-  },
-  component: home
-}

+ 16 - 19
tsconfig.json

@@ -1,24 +1,21 @@
 {
 {
   "compilerOptions": {
   "compilerOptions": {
-      "target": "ES2020",
-      "useDefineForClassFields": true,
-      "module": "ESNext",
-      "lib": ["ES2020", "DOM", "DOM.Iterable"],
-      "skipLibCheck": true,
-  
-      /* Bundler mode */
-      "moduleResolution": "bundler",
-      "allowImportingTsExtensions": true,
-      "resolveJsonModule": true,
-      "isolatedModules": true,
-      "noEmit": true,
-      "jsx": "preserve",
-  
-      /* Linting */
-      "strict": true,
-      "noUnusedLocals": true,
-      "noUnusedParameters": true,
-      "noFallthroughCasesInSwitch": true
+    "target": "ESNext",
+    "useDefineForClassFields": true,
+    "experimentalDecorators": true,
+    "module": "ESNext",
+    "moduleResolution": "Node",
+    "strict": true,
+    "jsx": "preserve",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "esModuleInterop": true,
+    "lib": ["ESNext", "DOM"],
+    "skipLibCheck": true,
+    "noEmit": true,
+    "paths": {
+      "@/*": ["./src/*"]
+    }
   },
   },
   "include": [
   "include": [
     "*.d.ts",
     "*.d.ts",

+ 1 - 2
vite.config.ts

@@ -29,8 +29,7 @@ export default () => defineConfig({
     host: '0.0.0.0',
     host: '0.0.0.0',
     port: 6547,
     port: 6547,
     open: false,
     open: false,
-    strictPort: false,
-    https: false,
+    strictPort: false
   },
   },
   // esbuild: {
   // esbuild: {
   //   drop: ['console', 'debugger'] // build 移除打印
   //   drop: ['console', 'debugger'] // build 移除打印