Compare commits
2 Commits
7f5781d4f1
...
2154db01cf
| Author | SHA1 | Date | |
|---|---|---|---|
| 2154db01cf | |||
| 6d5ae6c604 |
47
README.md
47
README.md
@@ -1,8 +1,31 @@
|
||||
# X-Agents
|
||||
|
||||
Vue 3 + Vite + TypeScript + Tailwind CSS 项目
|
||||
前后端分离项目,前端使用 Vue 3 + Vite,后端支持 Python/Java
|
||||
|
||||
## 技术栈
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
X-Agents/
|
||||
├── web/ # 前端项目 (Vue 3 + Vite + TypeScript + Tailwind CSS)
|
||||
│ ├── src/ # 前端源代码
|
||||
│ │ ├── components/ # Vue 组件
|
||||
│ │ ├── views/ # 页面视图
|
||||
│ │ ├── router/ # 路由配置
|
||||
│ │ ├── App.vue # 根组件
|
||||
│ │ ├── main.ts # 入口文件
|
||||
│ │ └── style.css # 全局样式
|
||||
│ ├── public/ # 静态资源
|
||||
│ ├── index.html # 入口 HTML
|
||||
│ ├── package.json # 前端依赖
|
||||
│ ├── vite.config.ts # Vite 配置
|
||||
│ ├── tailwind.config.js # Tailwind 配置
|
||||
│ └── tsconfig.json # TypeScript 配置
|
||||
│
|
||||
└── src/ # 后端代码 (Python/Java)
|
||||
└── (待开发)
|
||||
```
|
||||
|
||||
## 前端技术栈
|
||||
|
||||
- Vue 3
|
||||
- Vite
|
||||
@@ -11,8 +34,15 @@ Vue 3 + Vite + TypeScript + Tailwind CSS 项目
|
||||
- Pinia (状态管理)
|
||||
- Vue Router
|
||||
- ECharts
|
||||
- Font Awesome
|
||||
|
||||
## 基础操作
|
||||
## 前端操作
|
||||
|
||||
### 进入前端目录
|
||||
|
||||
```bash
|
||||
cd web
|
||||
```
|
||||
|
||||
### 安装依赖
|
||||
|
||||
@@ -40,13 +70,6 @@ npm run build
|
||||
npm run preview
|
||||
```
|
||||
|
||||
## 项目结构
|
||||
## 后端开发
|
||||
|
||||
```
|
||||
├── src/ # 源代码
|
||||
├── web/ # Web 资源
|
||||
├── index.html # 入口 HTML
|
||||
├── vite.config.ts # Vite 配置
|
||||
├── tailwind.config.js # Tailwind 配置
|
||||
└── tsconfig.json # TypeScript 配置
|
||||
```
|
||||
后端代码请放在 `src/` 目录下,支持 Python 或 Java。
|
||||
|
||||
25
package.json
25
package.json
@@ -1,26 +1,11 @@
|
||||
{
|
||||
"name": "x-agent-dashboard",
|
||||
"name": "x-agents",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"echarts": "^6.0.0",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.38",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.4.3",
|
||||
"vite": "^5.2.8",
|
||||
"vue-tsc": "^2.0.7"
|
||||
"dev": "cd web && npm run dev",
|
||||
"build": "cd web && npm run build",
|
||||
"preview": "cd web && npm run preview",
|
||||
"install:web": "cd web && npm install"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-dark-900 text-gray-100 font-sans;
|
||||
}
|
||||
|
||||
/* 柱状图增长动画 */
|
||||
@keyframes bar-grow {
|
||||
from {
|
||||
height: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-bar {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
animation: bar-grow 2.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
}
|
||||
|
||||
/* 进度条增长动画 */
|
||||
@keyframes progress-grow {
|
||||
from {
|
||||
width: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
width: var(--target-width);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
animation: progress-grow 2.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>X-Agent Dashboard</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
187
package-lock.json → web/package-lock.json
generated
187
package-lock.json → web/package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.13.3",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
@@ -82,6 +83,24 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ctrl/tinycolor": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
|
||||
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@element-plus/icons-vue": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
|
||||
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
||||
@@ -473,6 +492,31 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.5",
|
||||
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.5.tgz",
|
||||
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.6.tgz",
|
||||
"integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.5",
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.11",
|
||||
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.11.tgz",
|
||||
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
@@ -549,6 +593,17 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"name": "@sxzz/popperjs-es",
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.8.tgz",
|
||||
"integrity": "sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
|
||||
@@ -906,6 +961,28 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.24",
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.24.tgz",
|
||||
"integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash-es": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.20",
|
||||
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
||||
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
|
||||
@@ -1091,6 +1168,42 @@
|
||||
"integrity": "sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "10.11.1",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-10.11.1.tgz",
|
||||
"integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.20",
|
||||
"@vueuse/metadata": "10.11.1",
|
||||
"@vueuse/shared": "10.11.1",
|
||||
"vue-demi": ">=0.14.8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "10.11.1",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.11.1.tgz",
|
||||
"integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "10.11.1",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.11.1.tgz",
|
||||
"integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue-demi": ">=0.14.8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/alien-signals": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-1.0.13.tgz",
|
||||
@@ -1126,6 +1239,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async-validator": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
|
||||
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.27",
|
||||
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.27.tgz",
|
||||
@@ -1352,6 +1471,12 @@
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.19",
|
||||
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
|
||||
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/de-indent": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
|
||||
@@ -1390,6 +1515,31 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/element-plus": {
|
||||
"version": "2.13.3",
|
||||
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.13.3.tgz",
|
||||
"integrity": "sha512-RwLVtFpeHjZ4UCtHxVi1/sGR2cr2xOL7hqWa7qJc/+gdO6QavVG8Nw1C647obCb3tIg2ztMhNbIIjZUv+6z1og==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^3.4.1",
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"@floating-ui/dom": "^1.0.1",
|
||||
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"async-validator": "^4.2.5",
|
||||
"dayjs": "^1.11.19",
|
||||
"lodash": "^4.17.23",
|
||||
"lodash-es": "^4.17.23",
|
||||
"lodash-unified": "^1.0.3",
|
||||
"memoize-one": "^6.0.0",
|
||||
"normalize-wheel-es": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
|
||||
@@ -1678,6 +1828,31 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.23.tgz",
|
||||
"integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash-unified": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
|
||||
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/lodash-es": "*",
|
||||
"lodash": "*",
|
||||
"lodash-es": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -1687,6 +1862,12 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
|
||||
@@ -1781,6 +1962,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-wheel-es": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
|
||||
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
|
||||
27
web/package.json
Normal file
27
web/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "x-agent-dashboard",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.13.3",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.38",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.4.3",
|
||||
"vite": "^5.2.8",
|
||||
"vue-tsc": "^2.0.7"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ElConfigProvider } from 'element-plus'
|
||||
import Sidebar from '@/components/Sidebar.vue'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -8,15 +9,17 @@ const showSidebar = computed(() => route.path !== '/')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex min-h-screen bg-dark-900">
|
||||
<!-- 左侧侧边栏 (除了登录页) -->
|
||||
<Sidebar v-if="showSidebar" />
|
||||
<ElConfigProvider :z-index="3000">
|
||||
<div class="flex min-h-screen bg-dark-900">
|
||||
<!-- 左侧侧边栏 (除了登录页) -->
|
||||
<Sidebar v-if="showSidebar" />
|
||||
|
||||
<!-- 右侧内容区 -->
|
||||
<main :class="showSidebar ? 'ml-64' : ''" class="flex-1 min-h-screen">
|
||||
<router-view class="page-content" />
|
||||
</main>
|
||||
</div>
|
||||
<!-- 右侧内容区 -->
|
||||
<main :class="showSidebar ? 'ml-64' : ''" class="flex-1 min-h-screen">
|
||||
<router-view class="page-content" />
|
||||
</main>
|
||||
</div>
|
||||
</ElConfigProvider>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@@ -18,7 +18,8 @@ const mainMenu: MenuItem[] = [
|
||||
{ name: 'Agents', icon: 'fa-robot', badge: 3, path: '/agents' },
|
||||
{ name: 'Skills', icon: 'fa-wand-magic-sparkles', badge: 21, path: '/mcp' },
|
||||
{ name: 'Tools', icon: 'fa-tools', badge: 13, path: '/model-apis' },
|
||||
{ name: 'MCP', icon: 'fa-network-wired', badge: 1 },
|
||||
{ name: 'Database', icon: 'fa-database', path: '/database' },
|
||||
{ name: 'Knowledge', icon: 'fa-book' },
|
||||
]
|
||||
|
||||
const bottomMenu: MenuItem[] = [
|
||||
@@ -1,5 +1,7 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import router from './router'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
@@ -8,5 +10,6 @@ const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
app.use(ElementPlus)
|
||||
|
||||
app.mount('#app')
|
||||
@@ -4,6 +4,7 @@ import Login from '@/views/Login.vue'
|
||||
import Agents from '@/views/Agents.vue'
|
||||
import MCP from '@/views/MCP.vue'
|
||||
import ModelAPIs from '@/views/ModelAPIs.vue'
|
||||
import Database from '@/views/Database.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
@@ -32,6 +33,11 @@ const router = createRouter({
|
||||
path: '/model-apis',
|
||||
name: 'model-apis',
|
||||
component: ModelAPIs
|
||||
},
|
||||
{
|
||||
path: '/database',
|
||||
name: 'database',
|
||||
component: Database
|
||||
}
|
||||
]
|
||||
})
|
||||
117
web/src/style.css
Normal file
117
web/src/style.css
Normal file
@@ -0,0 +1,117 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-dark-900 text-gray-100 font-sans;
|
||||
}
|
||||
|
||||
/* Element Plus 暗色主题 */
|
||||
html.dark {
|
||||
--el-bg-color: #171922;
|
||||
--el-bg-color-overlay: #171922;
|
||||
--el-text-color-primary: #ffffff;
|
||||
--el-text-color-regular: #a1a1aa;
|
||||
--el-border-color: #2a2c36;
|
||||
--el-border-color-light: #2a2c36;
|
||||
--el-fill-color-blank: #171922;
|
||||
--el-color-primary: #ff9500;
|
||||
}
|
||||
|
||||
html.dark .el-select {
|
||||
--el-select-input-focus-border-color: #ff9500;
|
||||
}
|
||||
|
||||
html.dark .el-select .el-input__wrapper {
|
||||
background-color: #171922;
|
||||
box-shadow: 0 0 0 1px #2a2c36 inset;
|
||||
}
|
||||
|
||||
html.dark .el-select .el-input__wrapper:hover {
|
||||
box-shadow: 0 0 0 1px #ff9500 inset;
|
||||
}
|
||||
|
||||
html.dark .el-select .el-input__wrapper.is-focus {
|
||||
box-shadow: 0 0 0 1px #ff9500 inset;
|
||||
}
|
||||
|
||||
html.dark .el-select .el-input__inner {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
html.dark .el-select-dropdown {
|
||||
background-color: #171922;
|
||||
border: 1px solid #2a2c36;
|
||||
}
|
||||
|
||||
html.dark .el-select-dropdown__item {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
html.dark .el-select-dropdown__item:hover {
|
||||
background-color: #1a1c25;
|
||||
}
|
||||
|
||||
html.dark .el-select-dropdown__item.is-selected {
|
||||
color: #ff9500;
|
||||
font-weight: 600;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
html.dark .el-popper.is-light {
|
||||
background: #171922;
|
||||
border: 1px solid #2a2c36;
|
||||
}
|
||||
|
||||
html.dark .el-popper.is-light .el-popper__arrow::before {
|
||||
background: #171922;
|
||||
border-color: #2a2c36;
|
||||
}
|
||||
|
||||
/* 柱状图增长动画 */
|
||||
@keyframes bar-grow {
|
||||
from {
|
||||
height: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-bar {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
animation: bar-grow 2.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
}
|
||||
|
||||
/* 进度条增长动画 */
|
||||
@keyframes progress-grow {
|
||||
from {
|
||||
width: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
width: var(--target-width);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
animation: progress-grow 2.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
}
|
||||
378
web/src/views/Database.vue
Normal file
378
web/src/views/Database.vue
Normal file
@@ -0,0 +1,378 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
interface Database {
|
||||
id: number
|
||||
name: string
|
||||
type: string
|
||||
subtables: number
|
||||
status: 'connected' | 'disconnected' | 'error'
|
||||
createdAt: string
|
||||
description: string
|
||||
}
|
||||
|
||||
// 数据库数据
|
||||
const databases = ref<Database[]>([
|
||||
{ id: 1, name: 'production-db', type: 'MySQL', subtables: 24, status: 'connected', createdAt: '2025-04-10', description: 'Main production database' },
|
||||
{ id: 2, name: 'staging-db', type: 'MySQL', subtables: 18, status: 'connected', createdAt: '2025-04-08', description: 'Staging environment database' },
|
||||
{ id: 3, name: 'analytics-db', type: 'ClickHouse', subtables: 56, status: 'connected', createdAt: '2025-04-05', description: 'Analytics and reporting database' },
|
||||
{ id: 4, name: 'cache-db', type: 'Redis', subtables: 8, status: 'connected', createdAt: '2025-04-12', description: 'Cache layer database' },
|
||||
{ id: 5, name: 'test-db', type: 'MySQL', subtables: 12, status: 'disconnected', createdAt: '2025-04-11', description: 'Testing database' },
|
||||
])
|
||||
|
||||
// 编辑状态
|
||||
const editingDb = ref<Database | null>(null)
|
||||
const isEditing = ref(false)
|
||||
const isCreating = ref(false)
|
||||
const searchQuery = ref('')
|
||||
const filterStatus = ref<string>('all')
|
||||
|
||||
const dbTypes = ['MySQL']
|
||||
|
||||
// 新建 Database 表单
|
||||
const newDbForm = ref({
|
||||
name: '',
|
||||
type: 'MySQL',
|
||||
description: '',
|
||||
})
|
||||
|
||||
// 打开新建弹窗
|
||||
const openCreate = () => {
|
||||
newDbForm.value = {
|
||||
name: '',
|
||||
type: 'MySQL',
|
||||
description: '',
|
||||
}
|
||||
isCreating.value = true
|
||||
}
|
||||
|
||||
// 关闭新建弹窗
|
||||
const closeCreate = () => {
|
||||
isCreating.value = false
|
||||
}
|
||||
|
||||
// 保存新建
|
||||
const saveNewDb = () => {
|
||||
const newId = Math.max(...databases.value.map(d => d.id)) + 1
|
||||
databases.value.push({
|
||||
id: newId,
|
||||
name: newDbForm.value.name || 'Untitled Database',
|
||||
type: newDbForm.value.type,
|
||||
subtables: 0,
|
||||
status: 'disconnected',
|
||||
createdAt: new Date().toISOString().split('T')[0],
|
||||
description: newDbForm.value.description,
|
||||
})
|
||||
isCreating.value = false
|
||||
}
|
||||
|
||||
// 编辑表单数据
|
||||
const editForm = ref({
|
||||
name: '',
|
||||
type: '',
|
||||
description: '',
|
||||
})
|
||||
|
||||
// 打开编辑弹窗
|
||||
const openEdit = (db: Database) => {
|
||||
editingDb.value = db
|
||||
editForm.value = {
|
||||
name: db.name,
|
||||
type: db.type,
|
||||
description: db.description,
|
||||
}
|
||||
isEditing.value = true
|
||||
}
|
||||
|
||||
// 保存编辑
|
||||
const saveEdit = () => {
|
||||
if (editingDb.value) {
|
||||
const index = databases.value.findIndex(d => d.id === editingDb.value!.id)
|
||||
if (index !== -1) {
|
||||
databases.value[index] = {
|
||||
...databases.value[index],
|
||||
...editForm.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
isEditing.value = false
|
||||
}
|
||||
|
||||
// 取消编辑
|
||||
const cancelEdit = () => {
|
||||
isEditing.value = false
|
||||
editingDb.value = null
|
||||
}
|
||||
|
||||
// 切换状态
|
||||
const toggleStatus = (db: Database) => {
|
||||
if (db.status === 'connected') {
|
||||
db.status = 'disconnected'
|
||||
} else if (db.status === 'disconnected') {
|
||||
db.status = 'connected'
|
||||
}
|
||||
}
|
||||
|
||||
// 删除 Database
|
||||
const deleteDb = (id: number) => {
|
||||
databases.value = databases.value.filter(d => d.id !== id)
|
||||
}
|
||||
|
||||
// 过滤后的 Databases
|
||||
const filteredDatabases = () => {
|
||||
return databases.value.filter(db => {
|
||||
const matchSearch = db.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
||||
db.type.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
const matchStatus = filterStatus.value === 'all' || db.status === filterStatus.value
|
||||
return matchSearch && matchStatus
|
||||
})
|
||||
}
|
||||
|
||||
// 状态颜色
|
||||
const statusClass = (status: string) => {
|
||||
switch (status) {
|
||||
case 'connected': return 'bg-primary-success'
|
||||
case 'disconnected': return 'bg-gray-500'
|
||||
case 'error': return 'bg-primary-danger'
|
||||
default: return 'bg-gray-500'
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 主内容区域 -->
|
||||
<div class="p-6 min-h-screen">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fa-solid fa-database text-gray-400"></i>
|
||||
<span class="font-medium">Database</span>
|
||||
</div>
|
||||
<button @click="openCreate" class="bg-gradient-to-r from-primary-orange to-red-500 hover:from-orange-500 hover:to-red-600 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2 transition-all">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
New Connection
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和筛选 -->
|
||||
<div class="flex gap-4 mb-6">
|
||||
<div class="flex-1 relative">
|
||||
<i class="fa-solid fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="Search databases..."
|
||||
class="w-full bg-dark-600 border border-dark-500 rounded-lg py-2 pl-10 pr-4 text-white placeholder-gray-500 focus:outline-none focus:border-primary-orange"
|
||||
>
|
||||
</div>
|
||||
<el-select v-model="filterStatus" placeholder="Select" class="w-40" size="large">
|
||||
<el-option label="All Status" value="all" />
|
||||
<el-option label="Connected" value="connected" />
|
||||
<el-option label="Disconnected" value="disconnected" />
|
||||
<el-option label="Error" value="error" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<!-- Database 列表 -->
|
||||
<div class="bg-dark-700 rounded-xl overflow-hidden">
|
||||
<table class="w-full">
|
||||
<thead class="bg-dark-600">
|
||||
<tr>
|
||||
<th class="text-left px-5 py-3 text-sm font-medium text-gray-400">Database Name</th>
|
||||
<th class="text-center px-5 py-3 text-sm font-medium text-gray-400">Type</th>
|
||||
<th class="text-center px-5 py-3 text-sm font-medium text-gray-400">Subtables</th>
|
||||
<th class="text-center px-5 py-3 text-sm font-medium text-gray-400">Status</th>
|
||||
<th class="text-center px-5 py-3 text-sm font-medium text-gray-400">Created</th>
|
||||
<th class="text-center px-5 py-3 text-sm font-medium text-gray-400">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="db in filteredDatabases()" :key="db.id" class="border-t border-dark-600 hover:bg-dark-600/50 transition-colors">
|
||||
<td class="px-5 py-4">
|
||||
<div class="font-medium">{{ db.name }}</div>
|
||||
<div class="text-sm text-gray-500">{{ db.description }}</div>
|
||||
</td>
|
||||
<td class="px-5 py-4 text-center">
|
||||
<span class="bg-dark-500 px-2 py-1 rounded text-sm">{{ db.type }}</span>
|
||||
</td>
|
||||
<td class="px-5 py-4 text-center">
|
||||
<span class="text-primary-cyan">{{ db.subtables }}</span>
|
||||
</td>
|
||||
<td class="px-5 py-4 text-center">
|
||||
<span
|
||||
class="px-3 py-1 rounded-full text-xs capitalize"
|
||||
:class="{
|
||||
'bg-green-500/20 text-green-400': db.status === 'connected',
|
||||
'bg-gray-500/20 text-gray-400': db.status === 'disconnected',
|
||||
'bg-red-500/20 text-red-400': db.status === 'error'
|
||||
}"
|
||||
>{{ db.status }}</span>
|
||||
</td>
|
||||
<td class="px-5 py-4 text-center text-gray-400 text-sm">{{ db.createdAt }}</td>
|
||||
<td class="px-5 py-4">
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<button
|
||||
@click="toggleStatus(db)"
|
||||
class="p-2 rounded-lg hover:bg-dark-500 transition-colors"
|
||||
:title="db.status === 'connected' ? 'Disconnect' : 'Connect'"
|
||||
>
|
||||
<i :class="['fa-solid', db.status === 'connected' ? 'fa-stop' : 'fa-play', 'text-gray-400 hover:text-white']"></i>
|
||||
</button>
|
||||
<button
|
||||
@click="openEdit(db)"
|
||||
class="p-2 rounded-lg hover:bg-dark-500 transition-colors"
|
||||
title="Edit"
|
||||
>
|
||||
<i class="fa-solid fa-pen text-gray-400 hover:text-white"></i>
|
||||
</button>
|
||||
<button
|
||||
@click="deleteDb(db.id)"
|
||||
class="p-2 rounded-lg hover:bg-dark-500 transition-colors"
|
||||
title="Delete"
|
||||
>
|
||||
<i class="fa-solid fa-trash text-gray-400 hover:text-primary-danger"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-if="filteredDatabases().length === 0" class="py-12 text-center text-gray-500">
|
||||
<i class="fa-solid fa-database text-4xl mb-3"></i>
|
||||
<p>No databases found</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<Teleport to="body">
|
||||
<div v-if="isEditing" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50" @click.self="cancelEdit">
|
||||
<div class="bg-dark-700 rounded-2xl w-full max-w-lg border border-dark-500 shadow-2xl">
|
||||
<!-- 弹窗头部 -->
|
||||
<div class="flex items-center justify-between p-5 border-b border-dark-500">
|
||||
<h3 class="text-lg font-semibold">Edit Database</h3>
|
||||
<button @click="cancelEdit" class="text-gray-400 hover:text-white transition-colors">
|
||||
<i class="fa-solid fa-xmark text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 弹窗内容 -->
|
||||
<div class="p-5 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">Database Name</label>
|
||||
<input
|
||||
v-model="editForm.name"
|
||||
type="text"
|
||||
class="w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white focus:outline-none focus:border-primary-orange"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">Type</label>
|
||||
<el-select v-model="editForm.type" placeholder="Select" class="w-full" size="large">
|
||||
<el-option v-for="type in dbTypes" :key="type" :label="type" :value="type" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">Description</label>
|
||||
<textarea
|
||||
v-model="editForm.description"
|
||||
rows="3"
|
||||
class="w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white focus:outline-none focus:border-primary-orange resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 弹窗底部 -->
|
||||
<div class="flex items-center justify-end gap-3 p-5 border-t border-dark-500">
|
||||
<button
|
||||
@click="cancelEdit"
|
||||
class="px-4 py-2 rounded-lg bg-dark-600 text-gray-300 hover:bg-dark-500 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
@click="saveEdit"
|
||||
class="px-4 py-2 rounded-lg bg-gradient-to-r from-primary-orange to-red-500 text-white hover:from-orange-500 hover:to-red-600 transition-all"
|
||||
>
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
||||
<!-- 新建 Database 弹窗 -->
|
||||
<Teleport to="body">
|
||||
<div v-if="isCreating" class="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4" @click.self="closeCreate">
|
||||
<div class="bg-dark-800 rounded-2xl w-full max-w-lg border border-dark-600 shadow-2xl overflow-hidden animate-modal-in">
|
||||
<!-- 弹窗头部 -->
|
||||
<div class="flex items-center justify-between p-5 border-b border-dark-600 bg-dark-700/50">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-primary-orange to-red-500 flex items-center justify-center">
|
||||
<i class="fa-solid fa-database text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold text-white">Create New Connection</h3>
|
||||
<p class="text-sm text-gray-400">Configure your database connection</p>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="closeCreate" class="text-gray-400 hover:text-white transition-all p-2 hover:bg-dark-600 rounded-lg">
|
||||
<i class="fa-solid fa-xmark text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 弹窗内容 -->
|
||||
<div class="p-5 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">Database Name</label>
|
||||
<input
|
||||
v-model="newDbForm.name"
|
||||
type="text"
|
||||
placeholder="Enter database name..."
|
||||
class="w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white placeholder-gray-500 focus:outline-none focus:border-primary-orange"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">Type</label>
|
||||
<el-select v-model="newDbForm.type" placeholder="Select" class="w-full" size="large">
|
||||
<el-option v-for="type in dbTypes" :key="type" :label="type" :value="type" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">Description</label>
|
||||
<textarea
|
||||
v-model="newDbForm.description"
|
||||
rows="3"
|
||||
placeholder="Describe this database..."
|
||||
class="w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white placeholder-gray-500 focus:outline-none focus:border-primary-orange resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="flex items-center justify-end gap-3 p-5 border-t border-dark-600 bg-dark-700/50">
|
||||
<button
|
||||
@click="closeCreate"
|
||||
class="px-4 py-2 rounded-lg bg-dark-600 text-gray-300 hover:bg-dark-500 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
@click="saveNewDb"
|
||||
class="px-4 py-2 rounded-lg bg-gradient-to-r from-primary-orange to-red-500 text-white hover:from-orange-500 hover:to-red-600 transition-all flex items-center gap-2"
|
||||
>
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
Create Database
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
}
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
sans: ['Quicksand', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
},
|
||||
},
|
||||
Reference in New Issue
Block a user