This commit is contained in:
wujiawei
2024-01-14 21:02:11 +08:00
parent a85125d96a
commit 5d61854491
148 changed files with 57450 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
FROM nginx:1.13.0-alpine
COPY dist /usr/share/nginx/html/
##将nginx配置模板复制过去到指定目录
COPY default.template /etc/nginx/conf.d/
#将docker环境下的命令行路径切换到/etc/nginx/conf.d下执行
WORKDIR /etc/nginx/conf.d
#添加环境变量的写入
ENTRYPOINT envsubst < default.template > default.conf && cat default.conf && nginx -g 'daemon off;'

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Asa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,281 @@
![logo](https://i.bmp.ovh/imgs/2021/08/f828888bb4064c64.png)
# lazy-ui
**[<font color=#FF0000>✈ 国内加速链接</font>](https://gitee.com/asaasa/lazy-ui)**
**[<font color=#FF0000>✈ 效果预览</font>](https://lazy-ui.vercel.app/)**
**[<font color=#FF0000>✈ 效果预览(备用地址)</font>](http://asaasa.gitee.io/xujianhua)**
**走过路过的老铁,帮忙点个小 ⭐⭐⭐⭐⭐,🤝🤝🤝🤝🤝,🙏🙏🙏🙏🙏**
## 项目简介
基于**vue3**和**element-plus**开发的企业后台管理模板.
---
![登录页](https://i.bmp.ovh/imgs/2021/08/d9cc587a8b230dec.png)
![404](https://i.bmp.ovh/imgs/2021/08/c8d46b772369167d.png)
![用户管理](https://i.bmp.ovh/imgs/2021/08/6ea6b416eebca641.png)
![菜单管理](https://i.bmp.ovh/imgs/2021/08/682c150eef16bf17.png)
![角色管理](https://i.bmp.ovh/imgs/2021/08/7ff5cda434a2000b.png)
![iframe页面](https://i.bmp.ovh/imgs/2021/08/a101fec1b5769d7b.png)
## 功能特性
项目使用了最新的**vue3 全家桶**+**element-plus**+**mockjs**+**axios**+**eChart5**.项目继成了**mockServe**,可脱离后端自主开发测试
对**axios**深度封装,采用动态路由,路由配置更简单,**mockServe**独立开发测试时可在 nodework 直观查看接口数据
基于 node 实现自动化开发
## 环境依赖
**node 14+**, **vueCli 4+**
## 部署步骤
**npm i**
**npm run serve**
**yarn build / npm run build**
**yarn build --report**
## 目录结构描述
```
│ .browserslistrc 浏览器兼容配置
│ .eslintrc.js eslint配置文件
│ .gitignore git配置文件
│ babel.config.js babel配置文件
│ jsconfig.json js配置文件
│ LICENSE 开源认证
│ package-lock.json
│ package.json
│ README.md 项目说明
│ vue.config.js vue配置文件
├─.vscode vscode配置文件
│ settings.json
├─node_modules
├─public
│ favicon.ico
│ index.html
└─src
│ App.vue
│ main.js
│ config.js
├─api api管理模块
│ │ index.js api管理入口文件
│ │ mock-server.js mock服务配置文件
│ │
│ └─modules api分模块管理
│ system.js 模块api文件
├─assets 静态文件
│ logo.png
├─components 公共组件目录
│ │ Common.vue
│ │ FunctionPage.vue
│ │
│ ├─dashboard
│ │ LiveChart.vue
│ │ Shortcuts.vue
│ │
│ └─layout
│ │ NavigateBar.vue
│ │ SideBar.vue
│ │
│ └─components
│ Breadcrumb.vue
│ Hamburger.vue
│ Logo.vue
│ Personal.vue
│ SlideMenu.vue
├─directives 自定义指令目录
│ │ index.js 自定义指令入口文件
│ │
│ └─modules 自定义指令模块目录
│ permission.js
│ resize.js
├─plugins 插件目录
│ axios.js
│ element.js
│ mock.js
│ permission.js
├─router router目录
│ globalRoutes.js
│ index.js
│ mainRoutes.js
├─store vuex目录
│ │ getters.js
│ │ index.js
│ │
│ └─modules vuex模块目录
│ app.js
├─styles 样式目录
│ common.scss
│ variables.scss.js
├─utils 公共方法
│ index.js
│ validate.js
└─views
│ 404.vue
│ AppMain.vue
│ Home.vue
│ IFrame.vue
│ Login.vue
└─layoutpages
│ common.js
├─leisure
│ Game.vue
└─system
│ Menus.vue
│ Roles.vue
│ Users.vue
└─components
MenuEdit.vue
RoleEdit.vue
UsersEdit.vue
UsersEditRoute.vue
```
## 使用文档
### 自定义指令
**v-permission="[array]"**
自定义权限指令,参数为一个数组,数组元素为按钮所对应的 key 值
```js
<el-button
v-permission="['add']"
size="small"
type="primary"
@click="handleEdit(buttons.add.name)"
>{{ buttons.add.name }}
</el-button>
```
**v-resize="myChart"**
监听 echart 容器的自定义指令,参数为 echart 实例
```js
<div
v-resize="myChart"
style="height:400px;margin-top:20px"
ref="liveChart"
></div>
```
### 动态路由
动态路由的配置可查看 [src\plugins\permission.js](src\plugins\permission.js)
主要原理就是根据后端接口返回的树形菜单数据,动态生成路由表并挂载.具体需求字段可在[src\plugins\permission.js](src\plugins\permission.js)中的**fnAddDynamicMenuRoutes**方法中进行配置修改
```js
let route = {
path: menuList[i].url.replace(/\//g, "-") + `-${menuList[i].id}`,
component: null,
name: menuList[i].url.replace(/\//g, "-") + `-${menuList[i].id}`,
// meta: {
// }
};
// url以http[s]://开头, 通过iframe展示
if (menuList[i].iframe == 1) {
route["path"] = `i-${menuList[i].id}`;
route["name"] = `i-${menuList[i].id}`;
route["props"] = { url: menuList[i].url };
route["component"] = () => import("@/views/IFrame.vue");
} else {
const l = "views/layoutpages/" + menuList[i].url;
route["component"] = () => import("@/" + l + ".vue");
}
routes.push(route);
```
根据需求可以添加更多路由配置或定制化字段,如路由别名等
### 接口配置
项目中对 axios 做了封装配置中添加**Global**字段用来控制是否显示全屏 load,默认为 true,如需修改添加 axios 配置可在[src\plugins\axios.js](src\plugins\axios.js)中进行
#### 添加接口
本项目对 mock 做了深度集成,在使用其他项目时,mock 接口和项目接口往往都是分开维护很不方便.所以就放在了一起.只用在一处添加即可.接口目录为[src\api\modules](src\api\modules)**不要修改此目录名称**.在该目录下添加对应的接口文件.
```js
module.exports = {
login: {
//接口名称 必须
url: "/login", //接口地址 必须
type: "post", //请求类型 必须
mock: true, //mock细粒度控制开关,非必须,不填则为false
response: (opt) => {
//启用mock时的返回数据 opt为请求数据头
const {
body: { userName, pwd },
} = opt;
let data = {
code: "00",
message: "登录成功!",
token: new Date().getTime(),
uname: userName,
};
if (userName == "Administrator") {
if (pwd != "123456") {
data = {
code: "01",
message: "密码错误",
};
}
}
return data;
},
},
};
```
必须使用**module.exports**导出
#### 接口的使用
项目中已将封装后 axios 实例挂载到自定义字段 window.VE_API 上.调用方式为:
```js
VE_API [ fileName ][ portName ] (params,[config]) //有全局loading
VE_API [ fileName ][ portName ] (params,{Global:false) //没有全局loading
```
### 菜单配置
项目中的菜单时根据后端返回的树形结构数据动态生成,具体可查看[src\components\layout\components\SlideMenu.vue](src\components\layout\components\SlideMenu.vue)
## 声明
个人开发维护! 欢迎交流学习!
```shell
yarn run build
```
docker login --username=1207537021@qq.com registry.cn-hangzhou.aliyuncs.com
```shell
docker build -t registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-smart-acw-server:ui-master .
docker push registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-smart-acw-server:ui-master
```

View File

@@ -0,0 +1,4 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
plugins: ["@vue/babel-plugin-jsx"],
};

View File

@@ -0,0 +1,45 @@
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location / {
root /usr/share/nginx/html;
add_header version '${vue_http_response_version}';
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/*"]
}

24510
wu-lazy-cloud-network-ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
{
"name": "lazy-ui",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@icon-park/vue-next": "^1.4.2",
"@vueuse/core": "^10.2.1",
"axios": "^1.4.0",
"clipboard": "^2.0.11",
"core-js": "^3.20.3",
"dayjs": "^1.11.9",
"echarts": "^5.4.3",
"element-plus": "^2.3.8",
"normalize.css": "^8.0.1",
"nprogress": "^0.2.0",
"qs": "^6.11.2",
"sortablejs": "^1.15.0",
"uuid": "^9.0.1",
"vue": "^3.3.4",
"vue-clipboard3": "^2.0.0",
"vue-router": "^4.2.4",
"vue3-json-viewer": "^2.2.2",
"vuex": "^4.1.0",
"xe-utils": "^3.5.11",
"zdog": "^1.1.3"
},
"devDependencies": {
"@babel/eslint-parser": "^7.22.9",
"@vue/babel-plugin-jsx": "^1.1.5",
"@vue/cli-plugin-babel": "^5.0.8",
"@vue/cli-plugin-eslint": "^5.0.8",
"@vue/cli-plugin-router": "^5.0.8",
"@vue/cli-plugin-vuex": "^5.0.8",
"@vue/cli-service": "^5.0.8",
"@vue/compiler-sfc": "^3.3.4",
"@vue/eslint-config-prettier": "^8.0.0",
"compression-webpack-plugin": "^10.0.0",
"eslint": "^8.46.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.16.0",
"lint-staged": "^13.2.3",
"mockjs": "^1.1.0",
"prettier": "^3.0.0",
"sass": "^1.64.1",
"sass-loader": "^13.3.2",
"terser-webpack-plugin": "^5.3.9",
"vue-cli-plugin-axios": "^0.0.4",
"vue-cli-plugin-element-plus": "^0.0.13",
"webpack": "5.78.0"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,vue}": [
"vue-cli-service lint --fix"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- <meta name="viewport" content="width=device-width,initial-scale=1.0" /> -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't
work properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -0,0 +1,6 @@
<template>
<router-view></router-view>
</template>
<script setup></script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,41 @@
const path = require("path");
const fs = require("fs");
const getPathInfo = (p) => path.parse(p);
/**
* @description // 递归读取文件类似于webpack的require.context()
*
* @param {String} directory 文件目录
* @param {Boolean} useSubdirectories 是否查询子目录默认false
* @param {array} extList 查询文件后缀,默认 ['.js']
*
*/
function autoLoadFile(directory, useSubdirectories = false, extList = [".js"]) {
const filesList = {};
// 递归读取文件
function readFileList(directory, useSubdirectories, extList) {
const files = fs.readdirSync(directory);
files.forEach((item) => {
const fullPath = path.join(directory, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory() && useSubdirectories) {
readFileList(
path.join(directory, item),
useSubdirectories,
extList,
);
} else {
const info = getPathInfo(fullPath);
if (extList.includes(info.ext)) {
filesList[info.name] = require(fullPath);
}
}
});
}
readFileList(directory, useSubdirectories, extList);
return filesList;
}
module.exports = autoLoadFile(path.join(__dirname, "./modules"));

View File

@@ -0,0 +1,103 @@
// const bodyParser = require("body-parser");
const express = require("express");
const chokidar = require("chokidar");
const chalk = require("chalk");
const path = require("path");
const Mock = require("mockjs");
const apiDir = path.join(process.cwd(), "src/api");
// 注册mock接口路径
function registerRoutes(app) {
let mockLastIndex;
let mocksForServer = new Array();
const api = require("./index.js");
Object.keys(api).map((route) => {
Object.keys(api[route]).map((item) => {
api[route][item].mock &&
mocksForServer.push(
responseFake(
api[route][item].url,
api[route][item].type,
api[route][item].response,
),
);
});
});
// 注册接口
for (const mock of mocksForServer) {
app[mock.type](mock.url, mock.response);
mockLastIndex = app._router.stack.length;
// console.log(app._router.stack[12])
}
// 获取接口的长度
const mockRoutesLength = mocksForServer.length;
// 注意mockRoutesLength并不等于定于路由路径的数量还包括其他路由
// console.log(mockRoutesLength,mockLastIndex)
return {
mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength,
};
}
// 模拟mock server
const responseFake = (url, type, respond) => {
return {
url: url,
type: type || "get",
response: (req, res) => {
console.log(chalk.red("请求", req.path));
res.json(
Mock.mock(
respond instanceof Function ? respond(req, res) : respond,
),
);
},
};
};
// 移除路由
function unregisterRoutes() {
Object.keys(require.cache).forEach((i) => {
console.log(apiDir, i);
if (i.includes(apiDir)) {
delete require.cache[require.resolve(i)];
}
});
}
// 导出服务器app
module.exports = (app) => {
// 解析post数据
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
}),
);
// 注册路由表到app上
const mockRoutes = registerRoutes(app);
let mockRoutesLength = mockRoutes.mockRoutesLength;
let mockStartIndex = mockRoutes.mockStartIndex;
//* 观察mock下的文件变化不包括mock-server.js热更新文件这样添加数据路由就不用重启了
chokidar
.watch(apiDir, {
ignored: /mock-server/,
ignoreInitial: true,
})
.on("all", (event, path) => {
try {
// 先移除之前的路由
app._router.stack.splice(mockStartIndex, mockRoutesLength);
// 清除缓冲
unregisterRoutes();
// 重新注册路由
const mockRoutes = registerRoutes(app);
mockRoutesLength = mockRoutes.mockRoutesLength;
mockStartIndex = mockRoutes.mockStartIndex;
console.log(
chalk.magentaBright(`\n > 接口更新成功 --> 详情 ${path}`),
);
} catch (err) {
console.log(chalk.redBright(err));
}
});
};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

View File

@@ -0,0 +1,506 @@
<template>
<div class="ve_404">
<!-- partial:index.partial.html -->
<div class="moon"></div>
<div class="moon__crater moon__crater1"></div>
<div class="moon__crater moon__crater2"></div>
<div class="moon__crater moon__crater3"></div>
<div class="star star1"></div>
<div class="star star2"></div>
<div class="star star3"></div>
<div class="star star4"></div>
<div class="star star5"></div>
<slot>
<div class="error">
<div class="error__title">404</div>
<div class="error__subtitle">🐱🐱🐱(_⓿)🐱🐱🐱</div>
<div class="error__description">看来你是迷路了......</div>
<router-link to="/">
<button class="error__button error__button--active">
回到首页
</button>
</router-link>
<!-- <button class="error__button">CONTACT</button> -->
</div>
</slot>
<div class="astronaut" v-resize="{ resize: draw3dAstronaut }">
<canvas ref="cav"></canvas>
</div>
</div>
</template>
<script setup>
import Zdog from "zdog";
import { ref, onUnmounted, onMounted } from "vue";
const cav = ref(null);
let timer = null;
onMounted(() => {
draw3dAstronaut();
});
/**
* @description: 画3d太空人
* @param {*}
* @return {*}
*/
const draw3dAstronaut = () => {
if (!cav.value || !cav.value.parentNode) {
console.log("cav.value.parentNode is null" + cav.value);
return;
}
cav.value.width = cav.value.parentNode.clientWidth;
cav.value.height = cav.value.parentNode.clientHeight;
// colours
let dark_navy = "#131e38";
let orange = "#fe9642";
let cream = "#FFF8E7";
let light_purple = "#7f3f98";
let dark_purple = "#563795";
let cheese = "#fbc715";
// create illo
let illo = new Zdog.Illustration({
// set canvas with selector
element: cav.value,
dragRotate: true,
zoom: 0.65,
});
/** Body **/
// Body
let body = new Zdog.RoundedRect({
addTo: illo,
width: 200,
height: 220,
color: "white",
fill: true,
cornerRadius: 16,
stroke: 60,
});
// Backpack
new Zdog.RoundedRect({
addTo: body,
width: 180,
height: 310,
color: orange,
fill: true,
cornerRadius: 24,
stroke: 120,
translate: { z: -85, y: -60 },
});
/** arm **/
let arm = new Zdog.RoundedRect({
addTo: body,
height: 30,
width: 28,
stroke: 60,
fill: true,
color: dark_purple,
translate: { x: -140, y: -64 },
cornerRadius: 0.05,
});
new Zdog.RoundedRect({
addTo: arm,
height: 120,
width: 12,
stroke: 60,
fill: true,
color: cream,
translate: { y: 120 },
cornerRadius: 0.05,
});
// bubble_arm
let bubble_arm = new Zdog.Shape({
addTo: arm,
path: [{ x: -20 }, { x: 20 }],
stroke: 32,
color: light_purple,
translate: { y: 210 },
});
bubble_arm.copy({
color: dark_purple,
translate: { y: 230 },
});
// hand
new Zdog.RoundedRect({
addTo: bubble_arm,
height: 32,
width: 22,
translate: { x: -8, y: 60 },
fill: true,
color: cheese,
stroke: 30,
});
new Zdog.RoundedRect({
addTo: bubble_arm,
height: 24,
width: 0,
translate: { x: 24, y: 50 },
fill: true,
color: orange,
stroke: 20,
});
arm.copyGraph({
translate: { x: 140, y: -64 },
rotate: { y: Zdog.TAU / 2 },
});
/** Leg **/
let leg = new Zdog.RoundedRect({
addTo: body,
height: 160,
width: 28,
stroke: 60,
fill: true,
color: cream,
translate: { x: -56, y: 230 },
cornerRadius: 0.05,
});
// bubble_leg
let bubble_leg = new Zdog.Shape({
addTo: leg,
path: [{ x: -28 }, { x: 28 }],
stroke: 32,
color: light_purple,
translate: { y: 100 },
});
bubble_leg.copy({
color: dark_purple,
translate: { y: 124 },
});
// foot
new Zdog.RoundedRect({
addTo: leg,
width: 96,
height: 24,
stroke: 40,
fill: true,
color: cheese,
translate: { x: -24, y: 170 },
cornerRadius: 24,
});
leg.copyGraph({
translate: { x: 56, y: 230 },
rotate: { y: Zdog.TAU / 2 },
});
/** Head **/
// Head
let head = new Zdog.RoundedRect({
addTo: body,
width: 216,
height: 180,
depth: 40,
cornerRadius: 80,
stroke: 60,
color: cream,
fill: true,
translate: { y: -300 },
});
//add helmet
let helmet = new Zdog.RoundedRect({
addTo: head,
width: 210,
height: 165,
cornerRadius: 64,
color: dark_navy,
fill: true,
backface: false,
translate: { z: 20 },
});
//add refletion
new Zdog.Rect({
addTo: helmet,
width: 48,
height: 2,
stroke: 10,
translate: { x: 24, y: -24, z: 10 },
color: "white",
backface: false,
});
// add ear
let ear = new Zdog.RoundedRect({
addTo: head,
width: 36,
height: 72,
cornerRadius: 80,
stroke: 20,
color: orange,
fill: true,
translate: { x: -140 },
});
ear.copy({
translate: { x: 140 },
});
// neck
let neck = new Zdog.Shape({
addTo: head,
path: [{ x: -110 }, { x: 110 }],
translate: { y: 120 },
stroke: 40,
color: light_purple,
});
neck.copy({
translate: { y: 160 },
color: dark_purple,
});
/** extra **/
let stripe_1 = new Zdog.Shape({
addTo: body,
path: [{ x: -20 }, { x: 20 }],
stroke: 10,
translate: { x: 200, z: 200 },
color: cheese,
});
stripe_1.copy({
translate: { x: 320, y: 200, z: -400 },
color: cheese,
});
stripe_1.copy({
translate: { x: -220, y: 300, z: -400 },
color: "white",
});
stripe_1.copy({
translate: { x: -100, y: 400, z: -280 },
color: light_purple,
});
stripe_1.copy({
translate: { x: 50, y: -60, z: 150 },
color: orange,
});
stripe_1.copy({
translate: { x: -250, y: 80, z: 300 },
color: light_purple,
});
stripe_1.copy({
translate: { x: -350, y: -280, z: 175 },
color: dark_purple,
});
stripe_1.copy({
translate: { x: 250, y: -380, z: -175 },
color: "white",
});
// update & render
illo.updateRenderGraph();
function animate() {
// rotate illo each frame
illo.rotate.y += 0.005;
illo.rotate.x += 0.005;
illo.rotate.z += 0.005;
illo.updateRenderGraph();
// animate next frame
timer = requestAnimationFrame(animate);
}
// start animation
animate();
};
onUnmounted(() => {
cancelAnimationFrame(timer);
timer = null;
});
</script>
<style lang="scss" scoped>
.ve_404 {
height: 100vh;
width: 100vw;
position: relative;
overflow: hidden;
background: linear-gradient(90deg, #2f3640 23%, #181b20 100%);
}
.moon {
background: linear-gradient(90deg, #d0d0d0 48%, #919191 100%);
position: absolute;
top: -30vh;
left: -80vh;
width: 160vh;
height: 160%;
content: "";
border-radius: 50%;
box-shadow: 0px 0px 30px -4px rgba(0, 0, 0, 0.5);
}
.moon__crater {
position: absolute;
content: "";
border-radius: 100%;
background: linear-gradient(90deg, #7a7a7a 38%, #c3c3c3 100%);
opacity: 0.6;
}
.moon__crater1 {
top: 250px;
left: 500px;
width: 60px;
height: 180px;
}
.moon__crater2 {
top: 650px;
left: 340px;
width: 40px;
height: 80px;
transform: rotate(55deg);
}
.moon__crater3 {
top: -20px;
left: 40px;
width: 65px;
height: 120px;
transform: rotate(250deg);
}
.star {
color: grey;
position: absolute;
width: 10px;
height: 10px;
content: "";
border-radius: 100%;
transform: rotate(250deg);
opacity: 0.4;
animation-name: shimmer;
animation-duration: 1.5s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
@keyframes shimmer {
from {
opacity: 0;
}
to {
opacity: 0.7;
}
}
.star1 {
top: 40%;
left: 50%;
animation-delay: 1s;
}
.star2 {
top: 60%;
left: 90%;
animation-delay: 3s;
}
.star3 {
top: 10%;
left: 70%;
animation-delay: 2s;
}
.star4 {
top: 90%;
left: 40%;
}
.star5 {
top: 20%;
left: 30%;
animation-delay: 0.5s;
}
.astronaut {
position: absolute;
width: 60vw;
height: 100vh;
top: 0;
right: 0;
z-index: 0;
}
.error {
position: absolute;
left: 100px;
top: 400px;
transform: translateY(-60%);
font-family: "Righteous", cursive;
color: #363e49;
z-index: 1;
}
.error__title {
font-size: 10em;
font-weight: bold;
color: #d0d0d0;
text-shadow: -5px -5px 0 rgba(0, 0, 0, 0.7);
background-image: linear-gradient(90deg, #d0d0d0 48%, #919191 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.error__subtitle {
font-size: 2em;
}
.error__description {
opacity: 0.5;
}
.error__button {
min-width: 7em;
margin-top: 3em;
margin-right: 0.5em;
padding: 0.5em 2em;
outline: none;
border: 2px solid #2f3640;
background-color: transparent;
border-radius: 8em;
color: #576375;
cursor: pointer;
transition-duration: 0.2s;
font-size: 0.75em;
font-family: "Righteous", cursive;
}
.error__button:hover {
color: #21252c;
}
.error__button--active {
background-color: $base-color;
border: 2px solid $base-color;
color: white;
}
.error__button--active:hover {
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.5);
color: white;
}
</style>

View File

@@ -0,0 +1,30 @@
<template>
<div>
<el-page-header
@back="$router.back()"
:content="title"
></el-page-header>
<el-alert
style="margin-top: 20px"
type="info"
description="当前为动态权限页面!离开后将无法访问!刷新页面请使用本系统自带刷新按钮!!!"
show-icon
close-text="知道了"
></el-alert>
<el-divider></el-divider>
<slot />
</div>
</template>
<script setup>
import { toRefs, defineProps } from "vue";
const props = defineProps({
title: {
type: String,
require: true,
},
});
const { title } = toRefs(props);
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,111 @@
<template>
<div
v-resize="myChart"
style="height: 400px; margin-top: 20px"
ref="liveChart"
></div>
</template>
<script setup>
import * as echarts from "echarts/core";
import {
TitleComponent,
TooltipComponent,
GridComponent,
} from "echarts/components";
import { BarChart } from "echarts/charts";
import { CanvasRenderer } from "echarts/renderers";
import * as dayjs from "dayjs";
import { unwarp } from "@/utils";
import { onMounted, ref, onUnmounted } from "vue";
/**
* @description: 生成20条数据
* @param {*}
* @return {*}
*/
const dataList = () => {
let date = [];
let num = [];
let nowTime = new Date().getTime();
for (let i = 0; i < 30; i++) {
date.push(dayjs(nowTime - i * 1000).format("HH:mm:ss"));
num.push((Math.random() * 10).toFixed(0) * 1);
}
return {
date: date.reverse(),
num,
};
};
echarts.use([
TitleComponent,
TooltipComponent,
GridComponent,
BarChart,
CanvasRenderer,
]);
const liveChart = ref(null);
const myChart = ref(null);
let timer = null;
let $_dataList = dataList();
let option = {
title: {
text: "用户访问量",
},
grid: {
left: "0",
right: "0",
top: "10%",
bottom: "0",
containLabel: true,
},
tooltip: {
trigger: "axis",
},
xAxis: {
data: $_dataList.date,
},
yAxis: {
type: "value",
},
series: [
{
data: $_dataList.num,
type: "bar",
showBackground: true,
backgroundStyle: {
color: "rgba(180, 180, 180, 0.2)",
},
},
],
};
/**
* @description: 动态数据
* @param {*}
* @return {*}
*/
const getNewData = (myChart) => {
unwarp(myChart.value).setOption(option);
timer = setInterval(() => {
$_dataList.date.shift();
$_dataList.num.shift();
$_dataList.date.push(dayjs(new Date().getTime()).format("HH:mm:ss"));
$_dataList.num.push((Math.random() * 10).toFixed(0) * 1);
unwarp(myChart.value).setOption(option);
}, 1000);
};
onMounted(() => {
myChart.value = echarts.init(liveChart.value);
getNewData(myChart);
});
onUnmounted(() => {
unwarp(myChart.value).dispose();
clearInterval(timer);
timer = null;
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,381 @@
<template>
<el-row :gutter="20" style="padding-bottom: 10px">
<!-- <el-col :span="12">-->
<!-- <div class="ve-card ve_card1">-->
<!-- <el-icon>-->
<!-- <football />-->
<!-- </el-icon>-->
<!-- <div>-->
<!-- <p>菜单数量</p>-->
<!-- <span>{{ shortcutsData.menuNum }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-col>-->
<el-col :span="12">
<div class="ve-card ve_card2">
<el-icon>
<user />
</el-icon>
<div>
<p>用户数量</p>
<span>{{ shortcutsData.userNum }}</span>
</div>
</div>
</el-col>
<el-col :span="12">
<div class="ve-card ve_card1">
<el-icon>
<grape />
</el-icon>
<div>
<p>数据库实例数量</p>
<span>{{ shortcutsData.instanceNum }}</span>
</div>
</div>
</el-col>
<el-col style="padding: 20px">
<!-- 内存 -->
<el-card
shadow="always"
class="box-card"
style="
width: 100%;
/*flex-wrap: wrap;*/
/*float: left;*/
/*position: fixed;*/
/*display: flex;*/
/*flex-wrap: wrap;*/
"
>
<el-progress
type="circle"
:width="140"
:stroke-width="15"
:color="colors"
:percentage="
Number(
shortcutsData.useMemory / shortcutsData.totalMemory,
) * 100
"
>
<span class="percentage-value"
>{{
shortcutsData.useMemory +
"/" +
shortcutsData.totalMemory
}}MB</span
>
<span class="percentage-label">JVM内存使用</span>
<span class="percentage-label">
<el-button @click="gc()">GC</el-button>
</span>
</el-progress>
<!-- cpu -->
<el-progress
type="circle"
:width="140"
:stroke-width="15"
:color="colors"
:percentage="cpuData.used"
>
<span class="percentage-value">{{ cpuData.used }}%</span>
<span class="percentage-label">当前线程CPU使用</span>
<span class="percentage-label">
<el-button @click="getCpu()">cpu</el-button>
</span>
</el-progress>
<el-progress
type="circle"
:width="140"
:stroke-width="15"
:color="colors"
:percentage="cpuData.sys"
>
<span class="percentage-value">{{ cpuData.sys }}%</span>
<span class="percentage-label">当前系统CPU使用</span>
<span class="percentage-label">
<el-button @click="getCpu()">cpu</el-button>
</span>
</el-progress>
<el-progress>
<span class="percentage-value"
>CUP名称: {{ cpuData.name }}</span
>
<span class="percentage-value"
>CUP架构: {{ cpuData.arch }}</span
>
<span class="percentage-value"
>CUP版本: {{ cpuData.version }}</span
>
<span class="percentage-value"
>CUP核心数: {{ cpuData.cpuNum }}</span
>
</el-progress>
</el-card>
</el-col>
<!-- <el-col :span="12">-->
<!-- <div class="ve-card ve_card3">-->
<!-- <el-icon>-->
<!-- <ice-cream />-->
<!-- </el-icon>-->
<!-- <div>-->
<!-- <p>角色数量</p>-->
<!-- <span>{{ shortcutsData.roleNum }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<!-- <div class="ve-card ve_card4">-->
<!-- <el-icon>-->
<!-- <food />-->
<!-- </el-icon>-->
<!-- <div>-->
<!-- <p>资料数量</p>-->
<!-- <span>{{ shortcutsData.menuNum }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-col>-->
</el-row>
<!-- <el-row :gutter="20" style="padding-top: 10px">-->
<!-- <el-col :span="12">-->
<!-- <div class="ve-card ve_card1">-->
<!-- <el-icon>-->
<!-- <grape />-->
<!-- </el-icon>-->
<!-- <div>-->
<!-- <p>数据库实例数量</p>-->
<!-- <span>{{ shortcutsData.instanceNum }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<!-- <div class="ve-card ve_card2">-->
<!-- <el-icon>-->
<!-- <histogram />-->
<!-- </el-icon>-->
<!-- <div>-->
<!-- <p>数据库数量</p>-->
<!-- <span>{{ shortcutsData.schemaNum }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<!-- <div class="ve-card ve_card3">-->
<!-- <el-icon>-->
<!-- <management />-->
<!-- </el-icon>-->
<!-- <div>-->
<!-- <p>表数量</p>-->
<!-- <span>{{ shortcutsData.tableNum }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<!-- <div class="ve-card ve_card4">-->
<!-- <el-icon>-->
<!-- <position />-->
<!-- </el-icon>-->
<!-- <div>-->
<!-- <p>表列数量</p>-->
<!-- <span>{{ shortcutsData.tableColumnNum }}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-col>-->
<!-- </el-row>-->
</template>
<script setup>
import { onMounted, onUnmounted, ref } from "vue";
const shortcutsData = ref({
menuNum: 0,
roleNum: 0,
userNum: 0,
instanceNum: 0,
schemaNum: 0,
tableNum: 0,
tableColumnNum: 0,
maxMemory: 0,
totalMemory: 1,
freeMemory: 0,
useMemory: 0,
});
const cpuData = ref({
cpuNum: "0",
total: "0",
sys: "0.00",
used: "0.00",
wait: "0.00",
free: "0.00",
name: "",
arch: "",
version: "",
});
const colors = [
{ color: "#f56c6c", percentage: 100 },
{ color: "#e6a23c", percentage: 80 },
{ color: "#5cb87a", percentage: 60 },
{ color: "#1989fa", percentage: 40 },
{ color: "#6f7ad3", percentage: 20 },
];
const timer = ref();
/**
* 获取jvm信息
* @returns {Promise<void>}
*/
const getJvmInfo = async () => {
const { code, data } = await VE_API.system.jvmRunTimeMemory();
if (code === 0) {
console.log(data);
shortcutsData.value.maxMemory = data.maxMemory;
shortcutsData.value.totalMemory = data.totalMemory;
shortcutsData.value.freeMemory = data.freeMemory;
shortcutsData.value.useMemory = data.useMemory;
}
};
/**
* gc
* @returns {Promise<void>}
*/
const gc = async () => {
const { code, data } = await VE_API.system.jvmGC();
if (code === 0) {
console.log(data);
await getJvmInfo();
}
};
/**
* gc
* @returns {Promise<void>}
*/
const getCpu = async () => {
const { code, data } = await VE_API.system.jvmCPU();
if (code === 0) {
cpuData.value = data;
console.log(data);
}
};
onMounted(async () => {
const { code, data } = await VE_API.system.shortcutsData();
if (code === 0) {
console.log(data);
shortcutsData.value = data;
}
await getJvmInfo();
await getCpu();
timer.value = setTimeout(() => {
//需要定时执行的代码
getJvmInfo();
getCpu();
}, 3000);
});
onUnmounted(() => {
clearTimeout(timer.value);
timer.value = "";
});
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.el-row {
height: 50%;
padding-right: 10px;
}
.ve-card {
border-radius: 10px;
height: 100%;
display: flex;
align-items: center;
transition: all 500ms;
color: #fff;
&:hover {
box-shadow: 3px 3px 6px 1px rgba(0, 0, 0, 0.2);
background: #fff;
}
i {
font-size: 100px;
margin: 0 20px;
}
div {
flex: 1;
padding-right: 12px;
p {
margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
span {
font-size: 60px;
font-weight: bold;
}
}
}
.ve_card1 {
background: #409eff;
&:hover {
color: #409eff;
}
}
.ve_card2 {
background: #67c23a;
&:hover {
color: #67c23a;
}
}
.ve_card3 {
background: #e6a23c;
&:hover {
color: #e6a23c;
}
}
.ve_card4 {
background: #f56c6c;
&:hover {
color: #f56c6c;
}
}
.percentage-value {
display: block;
margin-top: 10px;
font-size: 14px;
}
.percentage-label {
display: block;
margin-top: 10px;
font-size: 12px;
}
.el-progress--line {
margin-bottom: 15px;
width: 350px;
}
.el-progress--circle {
margin-right: 15px;
}
</style>

View File

@@ -0,0 +1,19 @@
<template>
<div class="ve_nav_bar">
<hamburger></hamburger>
<breadcrumb></breadcrumb>
<personal></personal>
</div>
</template>
<script setup>
import Hamburger from "./components/Hamburger.vue";
import Breadcrumb from "./components/Breadcrumb.vue";
import Personal from "./components/Personal.vue";
</script>
<style lang="scss" scoped>
.ve_nav_bar {
display: flex;
align-items: center;
height: 100%;
}
</style>

View File

@@ -0,0 +1,61 @@
<template>
<router-link to="/">
<logo />
</router-link>
<div class="ve_el_menu">
<el-scrollbar>
<el-menu
:default-active="defaultActive"
:collapse="opened"
:collapseTransition="false"
unique-opened
:background-color="sideBgColor"
:text-color="sideTextColor"
:active-text-color="sideActiveTextColor"
>
<slide-menu
v-for="item in menus"
:key="item.id"
:menu="item"
></slide-menu>
</el-menu>
</el-scrollbar>
</div>
</template>
<script setup>
import {
nav_height,
sideBgColor,
sideTextColor,
sideActiveTextColor,
} from "@/styles/variables.scss.js";
import { computed } from "vue";
import { useStore } from "vuex";
import { useRoute } from "vue-router";
import Logo from "./components/Logo";
import SlideMenu from "./components/SlideMenu";
const store = useStore();
const route = useRoute();
const opened = computed(() => store.getters.opened);
const menus = computed(() => store.getters.menuList);
const defaultActive = computed(() => {
let i = route.name.indexOf("/");
if (i < 0) {
return "/" + route.name;
} else {
return "/" + route.name.slice(0, i);
}
});
</script>
<style lang="scss" scoped>
a:active {
color: $base-color;
}
.ve_el_menu {
background: $sideBgColor;
height: calc(100vh - v-bind(nav_height));
}
</style>

View File

@@ -0,0 +1,33 @@
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item v-for="(item, index) in list" :key="index">
{{ item }}
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup>
import { treeFindPath } from "@/utils";
import { useStore } from "vuex";
import { useRoute } from "vue-router";
import { computed } from "vue";
const store = useStore();
const route = useRoute();
const menuList = computed(() => store.getters.menuList).value;
const list = computed(() => {
let name = route.name;
let i = route.name.indexOf("/");
if (i > -1) {
name = route.name.slice(0, i);
}
let j = name.lastIndexOf("-");
return treeFindPath(
menuList,
(data) => data.id === name.slice(j + 1) * 1,
"name",
);
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,31 @@
<template>
<div class="ve_slider_menu" @click="fn_slider_menu">
<el-icon>
<component :is="opened ? 'expand' : 'fold'" />
</el-icon>
</div>
</template>
<script setup>
import { TOGGLE_SLIDER } from "@/store/modules/app/type";
import { computed } from "vue";
import { useStore } from "vuex";
const store = useStore();
const opened = computed(() => store.getters.opened);
const fn_slider_menu = () => {
store.dispatch(`app/${TOGGLE_SLIDER}`);
};
</script>
<style lang="scss" scoped>
.ve_slider_menu {
cursor: pointer;
margin-right: 10px;
i {
font-size: 40px;
}
&:hover i {
color: $base-color;
}
}
</style>

View File

@@ -0,0 +1,57 @@
<template>
<el-tooltip :content="title" placement="right-end" effect="dark">
<div class="ve_menu_logo">
<div class="ve_logo_img">
<el-image
style="height: 100%"
:src="require('../../../assets/logo.png')"
fit="contain"
></el-image>
</div>
<!-- content to trigger tooltip here -->
<h3 v-show="!opened" class="ve_logo_title">
{{ title }}
</h3>
</div>
</el-tooltip>
</template>
<script setup>
import { computed } from "vue";
import { useStore } from "vuex";
const store = useStore();
const opened = computed(() => store.getters.opened);
const title = "lazy-ui";
</script>
<style lang="scss" scoped>
.ve_menu_logo {
width: 100%;
height: $nav-height;
white-space: nowrap;
overflow: hidden;
.ve_logo_img {
height: 100%;
text-align: center;
// 和side收缩后的宽度一致
width: $side-close-width;
// padding: 10px;
display: inline-block;
box-sizing: border-box;
vertical-align: middle;
}
.ve_logo_title {
width: $side-open-width;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
margin: 0;
vertical-align: middle;
color: #000;
&:hover {
color: $base-color;
}
}
}
</style>

View File

@@ -0,0 +1,88 @@
<template>
<div class="ve_personal">
<el-button-group>
<el-button
title="刷新"
style="border: none; font-size: 20px"
circle
plain
@click="reload()"
>
<el-icon :size="20" style="vertical-align: middle">
<Refresh />
</el-icon>
</el-button>
<el-button
title="全屏"
style="border: none; font-size: 20px"
circle
plain
@click="toggle()"
>
<el-icon :size="14" style="vertical-align: middle">
<full-screen />
</el-icon>
</el-button>
<el-button
:title="dark ? '夜间模式' : '明亮模式'"
style="border: none; font-size: 20px"
circle
plain
@click="toggleTheme()"
>
<el-icon :size="14" style="vertical-align: middle">
<component :is="dark ? 'moon' : 'sunny'" />
</el-icon>
</el-button>
</el-button-group>
<el-divider direction="vertical"></el-divider>
<el-dropdown @command="handleCommand">
<span class="ve_nav_dropdown">
你好!{{ uname }}
<el-icon>
<arrow-down-bold />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="{ name: 'Login' }">
退出登录
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- <router-link :to="{ name: 'Login' }">
<el-link :underline="false">退出</el-link>
</router-link> -->
</div>
</template>
<script setup>
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { inject, computed, ref } from "vue";
import { useFullscreen } from "@vueuse/core";
const { toggle } = useFullscreen();
const store = useStore();
const router = useRouter();
const uname = computed(() => store.getters.uname);
const reload = inject("reload");
const handleCommand = (command) => {
router.push(command);
};
const dark = ref(false);
const toggleTheme = () => {
dark.value = !dark.value;
document.documentElement.classList.toggle("dark");
};
</script>
<style lang="scss" scoped>
.ve_personal {
flex: 1;
text-align: right;
.ve_nav_dropdown {
font-weight: bold;
}
}
</style>

View File

@@ -0,0 +1,92 @@
<template>
<el-sub-menu
:index="menu.code + ''"
v-if="menu.type == 0 && filerMenus(menu.children)"
>
<template #title>
<el-icon :size="16" style="margin-right: 6px">
<component :is="menu.icon" />
</el-icon>
<span class="el-menu-item-text">{{ menu.name }}</span>
</template>
<slide-menu
v-for="child in menu.children"
:key="child.code"
:menu="child"
></slide-menu>
</el-sub-menu>
<el-menu-item
v-else-if="menu.type == 1"
:index="setIndex(menu)"
@click="clickMenu(menu)"
>
<template #title>
<el-icon :size="16" style="margin-right: 6px">
<component :is="menu.icon" />
</el-icon>
<span class="el-menu-item-text">{{ menu.name }}</span>
</template>
</el-menu-item>
</template>
<script setup>
import { useRouter } from "vue-router";
import { defineProps, toRefs } from "vue";
const props = defineProps(["menu"]);
const { menu } = toRefs(props);
// const reload = inject("reload");
const router = useRouter();
const clickMenu = (menu) => {
let name = menu.url.replace(/\//g, "-") + `-${menu.code}`;
if (menu.iframe === 1) {
name = `i-${menu.code}`;
}
router.push({
name,
});
};
const setIndex = (menu) => {
let index = `/${menu.url.replace(/\//g, "-")}-${menu.code}`;
if (menu.iframe === 1) {
index = `/i-${menu.code}`;
}
return index;
};
/**
* @description:过滤空目录
* @param {*}
* @return {*}
*/
const filerMenus = (menus) => {
if (menus && menus.length > 0) {
let _menus = XE.toTreeArray(menus);
return _menus.some((item) => item.type == 1);
} else {
return false;
}
};
</script>
<style lang="scss">
li.el-menu-item.is-active {
background-color: darken($sideBgColor, 15%) !important;
}
.el-menu-item .el-icon svg {
vertical-align: unset;
}
.el-menu-item {
//border: 2px solid #ccc;
//background: #eef4fe;
}
.el-sub-menu__title .el-icon svg {
vertical-align: unset;
}
.el-menu-item-text {
//color: #303133;
}
// .el-sub-menu.is-active:not(.is-opened) .el-sub-menu__title i {
// color: $base-color;
// }
</style>

View File

@@ -0,0 +1,179 @@
<template>
<div v-addClass class="ve_table">
<!-- table工具条 -->
<el-row v-delete class="ve_header_row_class_name ve_p_10">
<slot name="tool_bar"></slot>
</el-row>
<div class="ve_table_page">
<!-- 列表 -->
<div class="ve_table_content" ref="tableBox">
<el-row
:gutter="20"
style="margin-right: 15px; margin-left: -5px"
type="flex"
v-loading="loading"
>
<el-col
v-for="(item, index) in $attrs.table"
:key="index"
:span="6"
>
<el-card
class="box-card"
shadow="always"
:body-style="{ padding: '0px' }"
@row-click="
(row, column, event) =>
(ve_rowIndex = rowClick(event))
"
:row-class-name="
({ rowIndex }) =>
rowClassName(rowIndex, ve_rowIndex)
"
:cell-class-name="
({ rowIndex }) =>
cellClassName(rowIndex, ve_rowIndex)
"
>
<!-- 展示具体数据 -->
</el-card>
</el-col>
<slot></slot>
<template #append>
<slot name="append"></slot>
</template>
<template #empty>
<el-empty description="暂无数据"></el-empty>
</template>
</el-row>
<el-table
ref="elTable"
height="100%"
stripe
border
highlight-current-row
header-row-class-name="ve_header_row_class_name"
header-cell-class-name="ve_header_cell_class_name"
style="width: 100%"
v-bind="$attrs.table"
@selection-change="selectionChange"
>
<slot></slot>
<template #append>
<slot name="append"></slot>
</template>
<template #empty>
<el-empty description="暂无数据"></el-empty>
</template>
</el-table>
</div>
<!-- 分页 -->
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[
10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000,
]"
:hide-on-single-page="false"
v-bind="$attrs.pagination"
>
<slot name="pagination"></slot>
</el-pagination>
</div>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "CardTable",
inheritAttrs: false,
directives: {
delete: (el) => {
const length = el.children.length;
if (length <= 0) {
el.parentNode && el.parentNode.removeChild(el);
}
},
addClass: (el) => {
el.parentNode && el.parentNode.classList.add("ve_flex_col");
},
},
setup(props, context) {
const { emit } = context;
const elTable = ref(null);
const ve_rowIndex = ref(null);
const tableBox = ref(null);
/**
* @description:行点击事件
* @return {*}
* @param event
*/
const rowClick = (event) => {
// return event.currentTarget.rowIndex;
return event;
};
/**
* @description:高亮当前行
* @return {*}
* @param rowIndex
* @param ve_rowIndex
*/
const rowClassName = (rowIndex, ve_rowIndex) => {
if (rowIndex === ve_rowIndex) {
return "ve_row_class_name";
} else {
return "";
}
};
/**
* @description:高亮单元格
* @return {*}
* @param rowIndex
* @param ve_rowIndex
*/
const cellClassName = (rowIndex, ve_rowIndex) => {
if (rowIndex === ve_rowIndex) {
return "ve_cell_class_name";
} else {
return "";
}
};
const selectionChange = (val) => {
// console.log(val);
emit("selectionChange", val);
};
return {
elTable,
tableBox,
ve_rowIndex,
cellClassName,
rowClassName,
rowClick,
selectionChange,
};
},
};
</script>
<style lang="scss" scoped>
.ve_table {
flex: 1;
display: flex;
flex-direction: column;
.ve_table_page {
flex: 1;
display: flex;
flex-direction: column;
.ve_table_content {
flex: 1;
}
}
}
</style>

View File

@@ -0,0 +1,142 @@
<template>
<div v-addClass class="ve_table">
<!-- table工具条 -->
<el-row v-delete class="ve_header_row_class_name ve_p_10">
<slot name="tool_bar"></slot>
</el-row>
<div class="ve_table_page">
<!-- 列表 -->
<div class="ve_table_content" ref="tableBox">
<el-table
ref="elTable"
height="100%"
stripe
border
highlight-current-row
@row-click="
(row, column, event) => (ve_rowIndex = rowClick(event))
"
:row-class-name="
({ rowIndex }) => rowClassName(rowIndex, ve_rowIndex)
"
:cell-class-name="
({ rowIndex }) => cellClassName(rowIndex, ve_rowIndex)
"
header-row-class-name="ve_header_row_class_name"
header-cell-class-name="ve_header_cell_class_name"
style="width: 100%"
v-bind="$attrs.table"
@selection-change="selectionChange"
>
<slot></slot>
<template #append><slot name="append"></slot></template>
<template #empty>
<el-empty description="暂无数据"></el-empty>
</template>
</el-table>
</div>
<!-- 分页 -->
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[
10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000,
]"
:hide-on-single-page="false"
v-bind="$attrs.pagination"
>
<slot name="pagination"></slot>
</el-pagination>
</div>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "VeTable",
inheritAttrs: false,
directives: {
delete: (el) => {
const length = el.children.length;
if (length <= 0) {
el.parentNode && el.parentNode.removeChild(el);
}
},
addClass: (el) => {
el.parentNode && el.parentNode.classList.add("ve_flex_col");
},
},
setup(props, context) {
const { emit } = context;
const elTable = ref(null);
const ve_rowIndex = ref(null);
const tableBox = ref(null);
/**
* @description:行点击事件
* @param {*}
* @return {*}
*/
const rowClick = (event) => {
// return event.currentTarget.rowIndex;
return event;
};
/**
* @description:高亮当前行
* @param {*}
* @return {*}
*/
const rowClassName = (rowIndex, ve_rowIndex) => {
if (rowIndex == ve_rowIndex) {
return "ve_row_class_name";
} else {
return "";
}
};
/**
* @description:高亮单元格
* @param {*}
* @return {*}
*/
const cellClassName = (rowIndex, ve_rowIndex) => {
if (rowIndex == ve_rowIndex) {
return "ve_cell_class_name";
} else {
return "";
}
};
const selectionChange = (val) => {
// console.log(val);
emit("selectionChange", val);
};
return {
elTable,
tableBox,
ve_rowIndex,
cellClassName,
rowClassName,
rowClick,
selectionChange,
};
},
};
</script>
<style lang="scss" scoped>
.ve_table {
flex: 1;
display: flex;
flex-direction: column;
.ve_table_page {
flex: 1;
display: flex;
flex-direction: column;
.ve_table_content {
flex: 1;
}
}
}
</style>

View File

@@ -0,0 +1,18 @@
export default {
install: (app) => {
const files = require.context(
"@/components/veBaseComponents",
false,
/\.vue$/,
);
files.keys().forEach((key) => {
// 获取组件配置
const componentConfig = files(key);
// 全局注册组件
app.component(
componentConfig.default.name,
componentConfig.default,
);
});
},
};

View File

@@ -0,0 +1,4 @@
module.exports = {
dev_mock: false, //开发环境启用mock true:启用
pro_mock: false, //生产环境启用mock true:启用
};

View File

@@ -0,0 +1,12 @@
export default {
install: (app, { router, store }) => {
const files = require.context("@/directives/modules", false, /\.js$/);
files.keys().forEach((key) => {
let name = key.replace(/(\.\/|\.js)/g, "");
let method = files(key).default;
app.directive(name, (el, binding) =>
method(el, binding, app, router, store),
);
});
},
};

View File

@@ -0,0 +1,23 @@
const permission = (el, binding, app, router, store) => {
const { value } = binding;
function checkArray(permission) {
let path = app.config.globalProperties.$route.name;
let _permission = permission.map((element) => {
let url = path.replace(/-/g, "/") + "/" + element;
return url;
});
let arr = store.getters.permissionList;
return _permission.some((key) => arr.includes(key));
}
if (value && value.length > 0) {
let hasPermission = checkArray(value);
if (!hasPermission) {
// 没有权限 移除Dom元素
el.parentNode && el.parentNode.removeChild(el);
}
}
};
export default permission;

View File

@@ -0,0 +1,14 @@
import { useElementSize, debouncedWatch } from "@vueuse/core";
const resize = (el, binding) => {
const { width } = useElementSize(el);
if (width.value === 0) return;
const { value } = binding;
debouncedWatch(
width,
() => {
value && value.resize();
},
{ debounce: 500 },
);
};
export default resize;

View File

@@ -0,0 +1,27 @@
import { createApp } from "vue";
import axios from "@/plugins/axios";
import App from "@/App.vue";
import router from "@/router";
import store from "@/store";
import installElementPlus from "@/plugins/element";
import elementIcon from "@/plugins/svgicon";
import permission from "@/plugins/permission";
import mock from "@/plugins/mock";
import directives from "@/directives";
import veBaseComponents from "@/components/veBaseComponents";
// import JsonViewer from 'vue-json-viewer';
import "normalize.css/normalize.css";
import "nprogress/nprogress.css";
import "@/styles/common.scss";
const app = createApp(App);
app.use(mock)
.use(elementIcon)
.use(veBaseComponents)
.use(store)
.use(router)
.use(installElementPlus)
.use(axios, { router, store, opt: "VE_API" })
.use(permission, { router, store })
.use(directives, { router, store })
.mount("#app");

View File

@@ -0,0 +1,231 @@
/* eslint-disable indent */
"use strict";
import axios from "axios";
// import Qs from "qs";
import NProgress from "nprogress";
import { SET_TOKEN } from "@/store/modules/app/type";
// Full config: https://github.com/axios/axios#request-config
// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
const install = (app, { router, store, opt }) => {
let config = {
Global: true,
// baseURL: process.env.baseURL || process.env.apiUrl || ""
// timeout: 60 * 1000, // Timeout
// withCredentials: true, // Check cross-site Access-Control
// {"userName":"Administrator","pwd":"123456"}
// userName=Administrator&pwd=123456
};
// if (VE_ENV.MODE === "production") {
// console.log("VE_ENV.MODE:" + VE_ENV.MODE);
// config.transformRequest = [(data) => Qs.parse(data)];
// }
const _axios = axios.create(config);
let ve_loading;
let ve_message = null;
let loadingCount = 0;
// 请求拦截
_axios.interceptors.request.use(
(config) => {
NProgress.done();
if (config.Global) {
NProgress.start();
ve_loading = app.config.globalProperties.$loading({
lock: true,
text: "Loading",
spinner: "el-icon-loading",
background: "rgba(0,0,0,0.1)",
});
}
loadingCount++;
//*请求头添加token
const token = store.getters.token;
const access_token = store.getters.access_token;
const refresh_token = store.getters.refresh_token;
const zone_id = store.getters.zone_id;
// debugger;
token && (config.headers.Authorization = token);
if (access_token) {
config.headers.access_token = access_token;
}
if (refresh_token) {
config.headers.refresh_token = refresh_token;
}
if (zone_id) {
config.headers.zone_id = zone_id;
}
// Do something before request is sent
return config;
},
(error) => {
// Do something with request error
return Promise.reject(error);
},
);
// Add a response interceptor
// 响应拦截
_axios.interceptors.response.use(
(response) => {
// TODO 根据响应头更新token
store.dispatch(`app/${SET_TOKEN}`, new Date().getTime());
loadingCount--;
if (loadingCount <= 0) {
NProgress.done();
ve_loading.close();
}
// eslint-disable-next-line no-unused-vars
let contentType = response.headers["content-type"];
if (
"application/octet-stream" === contentType &&
response.status === 200
) {
return response;
}
let type = "success";
if (response.data.code !== 0) {
type = "error";
}
if (ve_message) {
ve_message.close();
ve_message = null;
}
ve_message = app.config.globalProperties.$message({
type,
message: response.data.message,
});
// Do something with response data
return response.data;
},
(error) => {
loadingCount--;
if (loadingCount <= 0) {
NProgress.done();
ve_loading.close();
}
if (error && error.response) {
let message = "";
switch (error.response.status) {
case 400:
message = "请求错误";
break;
case 401: {
message = "未授权,请登录";
router.replace({
name: "Login",
});
break;
}
case 403:
message = "没有权限,拒绝访问";
break;
case 404:
message = `请求地址出错`;
break;
case 408:
message = "请求超时";
break;
case 500:
message = "服务器内部错误";
break;
case 501:
message = "服务未实现";
break;
case 502:
message = "网关错误";
break;
case 503:
message = "服务不可用";
break;
case 504:
message = "网关超时";
break;
case 505:
message = "HTTP版本不受支持";
break;
default:
break;
}
if (ve_message) {
ve_message.close();
ve_message = null;
}
ve_message = app.config.globalProperties.$message({
message,
type: "error",
});
}
// Do something with response error
return Promise.reject(error);
},
);
/**
* 字符串替换
*
*
*
var data = {
name:"张三",
age:23,
sex:"男"
}
var text = "我叫{{name}},我今年{{age}}岁,我的性别是{{sex}}!".format(data);
console.log(text);
*
*
*
* @returns {String}
*/
String.prototype.format = function () {
if (arguments.length === 0) return this;
var obj = arguments[0];
var s = this;
for (var key in obj) {
s = s.replace(new RegExp("\\{\\{" + key + "\\}\\}", "g"), obj[key]);
}
return s;
};
const method = {
post: (url, p, config) => _axios.post(url, p, config),
put: (url, p, config) => _axios.put(url, p, config),
getUrl: (url, p, config) =>
_axios.get(url.format(p), { params: p, data: p }, config),
get: (url, p, config) =>
_axios.get(url, Object.assign(config, { params: p })),
delete: (url, p, config) =>
_axios.delete(url.format(p), { params: p, data: p }, config),
deleteUrl: (url, p, config) =>
_axios.delete(url.format(p), { params: p, data: p }, config),
deleteBody: (url, p, config) => _axios.delete(url, { data: p }, config),
// patch: (url, p, config) =>
// _axios.patch(url, Object.assign(config, { params: p })),
patch: (url, p, config) => _axios.patch(url, p, config),
patchUrl: (url, p, config) =>
_axios.patch(url.format(p), { params: p, data: p }, config),
};
let api = {};
const files = require.context("@/api/modules", false, /\.js$/);
files.keys().forEach((key) => {
const fileName = key.replace(/(\.\/|\.js)/g, "");
api[fileName] = {};
let obj = files(key);
Object.keys(obj).forEach((item) => {
api[fileName][item] = (p, config = {}) =>
method[obj[item].type](obj[item].url, p, config);
});
});
window[opt] = api;
app.config.globalProperties[opt] = api;
};
export default { install };

View File

@@ -0,0 +1,13 @@
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
// import zhCn from "element-plus/es/locale/lang/zh-cn";
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
import "dayjs/locale/zh-cn";
export default {
install: (app) => {
app.use(ElementPlus, {
locale: zhCn,
size: "default",
});
},
};

View File

@@ -0,0 +1,20 @@
export default {
install: () => {
const config = require("@/config");
if (config.pro_mock && VE_ENV.MODE === "production") {
const Mock = require("mockjs"); //引入
const files = require.context("@/api/modules", false, /\.js$/);
files.keys().forEach((key) => {
let obj = files(key);
Object.keys(obj).forEach((item) => {
Mock.mock(
obj[item].url,
obj[item].type,
obj[item].response,
);
});
});
}
},
};

View File

@@ -0,0 +1,387 @@
import { SET_MENU_LIST, SET_PERMISSION_LIST } from "@/store/modules/app/type";
import globalRoutes from "@/router/globalRoutes";
import mainRoutes from "@/router/mainRoutes";
import NProgress from "nprogress";
/**
* 判断当前路由类型, global: 全局路由, main: 主入口路由
* @param {*} route 当前路由
*/
function fnCurrentRouteType(route, globalRoutes = []) {
let temp = [];
for (let i = 0; i < globalRoutes.length; i++) {
if (route.name === globalRoutes[i].name) {
return "global";
} else if (
globalRoutes[i].children &&
globalRoutes[i].children.length >= 1
) {
temp = temp.concat(globalRoutes[i].children);
}
}
return temp.length >= 1 ? fnCurrentRouteType(route, temp) : "main";
}
export default {
install: (app, { router, store }) => {
// let router = opt;
router.beforeEach(async (to, from, next) => {
const token = store.getters.token;
if (
router.options.isAddDynamicMenuRoutes ||
fnCurrentRouteType(to, globalRoutes) === "global"
) {
//* 1. 已经添加 or 全局路由, 直接访问
if (to.meta.title) {
document.title = to.meta.title;
}
NProgress.start();
next();
} else {
// let token = sessionStorage.getItem("token");
if (!token || !/\S/.test(token)) {
next({ name: "Login" });
} else {
let data = await VE_API.system.userMenuList();
// let data = await VE_API.system.menuList();
if (data && data.code === 0) {
// const uname = store.getters.uname;
// if ("admin" === uname && data.data.length === 0) {
// data.data = data.data.concat(menuList);
// }
if (data.data.length === 0) {
data.data = data.data.concat(menuList);
}
let _list = XE.clone(data.data, true);
data.data = XE.mapTree(
XE.toArrayTree(_list, {
sortKey: "sort",
key: "code",
parentKey: "parentCode",
}),
(item) => {
if (
item.children &&
item.children.length <= 0
) {
delete item.children;
}
return item;
},
);
await fnAddDynamicMenuRoutes(data.data);
router.options.isAddDynamicMenuRoutes = true;
await store.dispatch(`app/${SET_MENU_LIST}`, data.data);
await store.dispatch(
`app/${SET_PERMISSION_LIST}`,
data.data,
);
NProgress.start();
next({ ...to, replace: true });
} else {
next({ name: "Login" });
}
}
}
});
router.afterEach(() => {
NProgress.done();
});
/**
* 添加动态(菜单)路由
* @param {*} menuList 菜单列表
* @param {*} routes 递归创建的动态(菜单)路由
*/
// eslint-disable-next-line no-unused-vars
const fnAddDynamicMenuRoutes = async (menuList = [], routes = []) => {
let temp = [];
for (let i = 0; i < menuList.length; i++) {
if (
menuList[i].type === 0 &&
menuList[i].children &&
menuList[i].children.length >= 1
) {
temp = temp.concat(menuList[i].children);
} else if (menuList[i].type === 1) {
// } else if (menuList[i].type==1 && /\S/.test(menuList[i].url)) {
// const url = menuList[i].url.replace(/\//g, "_");
let route = {
path:
menuList[i].url.replace(/\//g, "-") +
`-${menuList[i].code}`,
component: null,
name:
menuList[i].url.replace(/\//g, "-") +
`-${menuList[i].code}`,
// meta: {
// }
};
// url以http[s]://开头, 通过iframe展示
if (menuList[i].iframe === 1) {
route["path"] = `i-${menuList[i].code}`;
route["name"] = `i-${menuList[i].code}`;
route["props"] = { url: menuList[i].url };
route["component"] = () => import("@/views/IFrame.vue");
} else {
const l = "views/layoutpages/" + menuList[i].url;
route["component"] = () => import("@/" + l + ".vue");
}
routes.push(route);
}
}
if (temp.length >= 1) {
fnAddDynamicMenuRoutes(temp, routes);
} else {
mainRoutes.children = mainRoutes.children.concat(routes);
// mainRoutes.children = routes;
console.log(
"控制台打印--> ~ file: permission.js ~ line 127 ~ fnAddDynamicMenuRoutes ~ mainRoutes.children",
mainRoutes.children,
);
await router.addRoute(mainRoutes);
await router.addRoute({
path: "/:w+",
redirect: { name: "404" },
});
}
};
/**
* 默认菜单
* @type {[{name: string, icon: string, id: number, sort: number, iframe: number, menu: string, type: number, parentCode: number, url: string}, {name: string, icon: string, id: number, sort: number, iframe: number, menu: string, type: number, parentCode: number, url: string}, {name: string, icon: string, id: number, sort: number, iframe: number, menu: string, type: number, parentCode: number, url: string}, {name: string, icon: string, id: number, sort: number, iframe: number, menu: string, type: number, parentCode: number, url: string}, {name: string, icon: string, id: number, sort: number, iframe: number, menu: string, type: number, parentCode: number, url: string}, null, null, null, null, null, null, null, null, null, null, null, null, null]}
*/
const menuList = [
{
parentCode: -1,
code: 100,
id: 100,
name: "系统设置", //看官网这个名字是3-5之间的
url: "",
menu: "",
type: 0,
icon: "Setting",
sort: 1,
iframe: 1,
},
{
parentCode: 100,
code: 1,
id: 1,
name: "用户管理", //看官网这个名字是3-5之间的
url: "system/Users", //这个类似上面的id一个只是初始值是从100开始的
menu: "",
type: 1,
icon: "UserFilled",
sort: 2,
iframe: 0,
},
{
parentCode: 1,
code: 2,
id: 2,
name: "查询", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "search",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 1,
code: 3,
id: 3,
name: "添加", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "add",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 1,
code: 4,
id: 4,
name: "编辑", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "edit",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 100,
code: 5,
id: 5,
name: "菜单管理", //看官网这个名字是3-5之间的
url: "system/Menus", //这个类似上面的id一个只是初始值是从100开始的
menu: "",
type: 1,
icon: "Menu",
sort: 1,
iframe: 0,
},
{
parentCode: 5,
code: "restore",
id: 51,
name: "恢复出厂设置", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "restore",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 5,
code: 6,
id: 6,
name: "查询", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "search",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 5,
code: 7,
id: 7,
name: "添加", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "add",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 5,
code: 8,
id: 8,
name: "编辑", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "edit",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 5,
code: 9,
id: 9,
name: "添加子级", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "addChild",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 5,
code: 10,
id: 10,
name: "添加按钮", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "addBtn",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 100,
code: 11,
id: 11,
name: "角色管理", //看官网这个名字是3-5之间的
url: "system/Roles", //这个类似上面的id一个只是初始值是从100开始的
menu: "",
type: 1,
icon: "HelpFilled",
sort: 3,
iframe: 0,
},
{
parentCode: 11,
code: 12,
id: 12,
name: "查询", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "search",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 11,
code: 13,
id: 13,
name: "添加", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "add",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: 11,
code: 14,
id: 14,
name: "编辑", //看官网这个名字是3-5之间的
url: "", //这个类似上面的id一个只是初始值是从100开始的
menu: "edit",
type: 2,
icon: "",
sort: 1,
iframe: 1,
},
{
parentCode: -1,
code: 15,
id: 15,
name: "参考资料", //看官网这个名字是3-5之间的
url: "",
menu: "",
type: 0,
icon: "DocumentCopy",
sort: 1,
iframe: 1,
},
{
parentCode: 15,
code: 16,
id: 16,
name: "vue3.0", //看官网这个名字是3-5之间的
url: "https://www.vue3js.cn/docs/zh/", //这个类似上面的id一个只是初始值是从100开始的
menu: "",
type: 1,
icon: "Promotion",
sort: 1,
iframe: 1,
},
{
parentCode: 15,
code: 17,
id: 17,
name: "element-plus", //看官网这个名字是3-5之间的
url: "https://element-plus.org/#/zh-CN", //这个类似上面的id一个只是初始值是从100开始的
menu: "",
type: 1,
icon: "ElemeFilled",
sort: 1,
iframe: 1,
},
];
},
};

View File

@@ -0,0 +1,9 @@
import * as components from "@element-plus/icons-vue";
export default {
install: (app) => {
for (const key in components) {
const componentConfig = components[key];
app.component(componentConfig.name, componentConfig);
}
},
};

View File

@@ -0,0 +1,17 @@
export default [
{
path: "/login",
name: "Login",
component: () => import("@/views/Login.vue"),
},
{
path: "/register",
name: "Register",
component: () => import("@/views/Register.vue"),
},
{
path: "/404",
name: "404",
component: () => import("@/views/404.vue"),
},
];

View File

@@ -0,0 +1,11 @@
import { createRouter, createWebHashHistory } from "vue-router";
import globalRoutes from "./globalRoutes";
import mainRoutes from "./mainRoutes";
const router = createRouter({
history: createWebHashHistory(),
scrollBehavior: () => ({ y: 0 }),
isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由
routes: globalRoutes.concat(mainRoutes),
});
export default router;

View File

@@ -0,0 +1,13 @@
export default {
path: "/",
name: "AppMain",
component: () => import("@/views/AppMain.vue"),
redirect: { name: "Home" },
children: [
{
path: "home",
name: "Home",
component: () => import("@/views/Home.vue"),
},
],
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 992 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -0,0 +1,10 @@
export default {
opened: (state) => state.app.slider.opened,
token: (state) => state.app.token,
access_token: (state) => state.app.access_token,
refresh_token: (state) => state.app.refresh_token,
zone_id: (state) => state.app.zone_id,
uname: (state) => state.app.uname,
menuList: (state) => state.app.menuList,
permissionList: (state) => state.app.permissionList,
};

View File

@@ -0,0 +1,13 @@
let modules = {};
const files = require.context("./modules", true, /index.js$/);
files.keys().forEach((key) => {
const fileName = key.split("/")[1];
modules[fileName] = files(key).default;
});
import { createStore } from "vuex";
import getters from "./getters";
export default createStore({
getters,
modules,
});

View File

@@ -0,0 +1,101 @@
import {
TOGGLE_SLIDER,
SET_TOKEN,
SET_UNAME,
SET_MENU_LIST,
SET_PERMISSION_LIST,
SET_REFRESH_TOKEN,
SET_ACCESS_TOKEN,
SET_ZONE_ID,
} from "./type.js";
export default {
namespaced: true,
state: {
slider: {
opened: JSON.parse(sessionStorage.getItem("opened")),
},
token: sessionStorage.getItem("token") || "",
access_token: sessionStorage.getItem("access_token") || "",
refresh_token: sessionStorage.getItem("refresh_token") || "",
zone_id: sessionStorage.getItem("zone_id") || "",
menuList: null,
permissionList: [],
uname: sessionStorage.getItem("uname") || "",
},
mutations: {
[TOGGLE_SLIDER](state) {
state.slider.opened = !state.slider.opened;
sessionStorage.setItem(
"opened",
JSON.stringify(state.slider.opened),
);
},
[SET_TOKEN](state, token) {
state.token = token;
sessionStorage.setItem("token", state.token);
},
[SET_UNAME](state, uname) {
state.uname = uname;
sessionStorage.setItem("uname", state.uname);
},
[SET_ACCESS_TOKEN](state, access_token) {
state.access_token = access_token;
sessionStorage.setItem("access_token", state.access_token);
},
[SET_REFRESH_TOKEN](state, refresh_token) {
state.refresh_token = refresh_token;
sessionStorage.setItem("refresh_token", state.refresh_token);
},
[SET_ZONE_ID](state, zone_id) {
state.zone_id = zone_id;
sessionStorage.setItem("zone_id", state.zone_id);
},
[SET_MENU_LIST](state, menuList) {
state.menuList = menuList;
},
[SET_PERMISSION_LIST](state, permissionList) {
state.permissionList = permissionList;
},
},
actions: {
[TOGGLE_SLIDER]({ commit }) {
commit(TOGGLE_SLIDER);
},
[SET_TOKEN]({ commit }, token) {
commit(SET_TOKEN, token);
},
[SET_ACCESS_TOKEN]({ commit }, access_token) {
commit(SET_ACCESS_TOKEN, access_token);
},
[SET_REFRESH_TOKEN]({ commit }, refresh_token) {
commit(SET_REFRESH_TOKEN, refresh_token);
},
[SET_ZONE_ID]({ commit }, zone_id) {
commit(SET_ZONE_ID, zone_id);
},
[SET_UNAME]({ commit }, uname) {
commit(SET_UNAME, uname);
},
[SET_MENU_LIST]({ commit }, menuList) {
commit(SET_MENU_LIST, menuList);
},
[SET_PERMISSION_LIST]({ commit }, menuList) {
let allMenus = XE.filterTree(menuList, (item) => item.type === 1, {
sortKey: "sort",
key: "code",
parentKey: "parentCode",
});
let permissionList = [];
allMenus.forEach((item) => {
if (item.children && item.children.length > 0) {
item.children.forEach((menu) => {
permissionList.push(
`${item.url}/${item.code}/${menu.menu}`,
);
});
}
});
commit(SET_PERMISSION_LIST, permissionList);
},
},
};

View File

@@ -0,0 +1,8 @@
export const TOGGLE_SLIDER = "TOGGLE_SLIDER";
export const SET_TOKEN = "SET_TOKEN";
export const SET_ACCESS_TOKEN = "access_token";
export const SET_REFRESH_TOKEN = "refresh_token";
export const SET_UNAME = "SET_UNAME";
export const SET_ZONE_ID = "ZONE_ID"; // 空间区域ID
export const SET_MENU_LIST = "SET_MENU_LIST";
export const SET_PERMISSION_LIST = "SET_PERMISSION_LIST";

View File

@@ -0,0 +1,65 @@
html {
background: #fff;
transition: color 300ms, background-color 300ms;
&.dark {
filter: contrast(100%) invert(100%);
img {
filter: hue-rotate(180deg);
}
}
}
.ve_header_row_class_name,
.el-table__fixed-right-patch {
background: $main-bg-color !important;
}
.ve_header_cell_class_name {
background: $main-bg-color !important;
}
.ve_cell_class_name {
background: $base-color !important;
border-color: $base-color !important;
}
.ve_row_class_name {
background: $base-color !important;
}
.ve_p_10 {
padding: 10px;
}
//滚动条的宽度
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
//滚动条的滑块
::-webkit-scrollbar-thumb {
background-color: rgba(144, 147, 153, 0.3);
border-radius: 3px;
}
.ve_select_option_slot {
font-size: 12px;
float: left;
line-height: initial;
padding-bottom: 10px;
opacity: 0.7;
}
.ve_option_box {
width: calc(50% - 132px);
}
.size-watch {
width: 100%;
height: 100%;
// display: none;
position: absolute;
top: 0;
z-index: -1;
visibility: hidden;
margin: 0;
padding: 0;
border: 0;
}
.ve_flex_col {
display: flex;
flex-direction: column;
height: calc(100vh - #{$nav-height} - 80px);
}

View File

@@ -0,0 +1,11 @@
const variables = {
main_bg_color: "#f5f5f5",
base_color: "#409EFF",
nav_height: "50px",
side_close_width: "65px",
side_open_width: "160px",
sideBgColor: "#545c64",
sideTextColor: "#fff",
sideActiveTextColor: "#ffd04b",
};
module.exports = variables;

View File

@@ -0,0 +1,106 @@
/**
* @description:树形结构转一维数组
* @param {*} nodes
* @return {*}
*/
export function jsonToArray(nodes) {
let pid = -1;
const toArray = (nodes) => {
let r = [];
if (Array.isArray(nodes)) {
for (let i = 0, l = nodes.length; i < l; i++) {
nodes[i].pid = pid;
r.push(nodes[i]); // 取每项数据放入一个新数组
if (
Array.isArray(nodes[i]["children"]) &&
nodes[i]["children"].length > 0
) {
// 若存在children则递归调用把数据拼接到新数组中并且删除该children
pid = nodes[i].id;
r = r.concat(toArray(nodes[i]["children"]));
delete nodes[i]["children"];
}
}
}
return r;
};
return toArray(nodes);
}
/**
* @description:一维数组转树形结构
* @param {*} treeArray
* @return {*}
*/
export function arrayToJson(treeArray) {
var r = [];
var tmpMap = {};
for (var i = 0, l = treeArray.length; i < l; i++) {
//* 以每条数据的id作为obj的key值数据作为value值存入到一个临时对象里面
tmpMap[treeArray[i]["id"]] = treeArray[i];
}
for (i = 0, l = treeArray.length; i < l; i++) {
var key = tmpMap[treeArray[i]["pid"]];
//*循环每一条数据的pid假如这个临时对象有这个key值就代表这个key对应的数据有children需要Push进去
//*如果这一项数据属于哪个数据的子级
if (key) {
// *如果这个数据没有children
if (!key["children"]) {
key["children"] = [];
key["children"].push(treeArray[i]);
//* 如果这个数据有children
} else {
key["children"].push(treeArray[i]);
}
} else {
//*如果没有这个Key值就代表找不到属于哪个数据那就代表没有父级,直接放在最外层
r.push(treeArray[i]);
}
}
return r;
}
/**
* @description 获取节点的所有父节点
* @param {*} tree
* @param {*} func
* @param {*} path
* @return {*}
*/
export const treeFindPath = (tree, func, name = "id", path = []) => {
if (!tree) return [];
for (const data of tree) {
//* 这里按照你的需求来存放最后返回的内容吧
path.push(data[name]);
if (func(data)) return path;
if (data.children) {
const findChildren = treeFindPath(data.children, func, name, path);
if (findChildren.length) return findChildren;
}
path.pop();
}
return [];
};
/**
* @description: 拆箱函数,解决tooltip显示问题
* @param {*} obj
* @return {*}
*/
export const unwarp = (obj) => obj && (obj.__v_raw || obj.valueOf() || obj);
/**
* @description:获取所有的el-svg-icon组件名
* @param {*}
* @return {*}
*/
export const icons = () => {
const components = require("@element-plus/icons-vue");
console.log("🚀 ~ file: index.js ~ line 107 ~ icons ~ e", components);
const names = [];
for (const key in components) {
names.push(components[key].name);
}
return names;
};

View File

@@ -0,0 +1,57 @@
/**
* 邮箱
* @param {* s
*/
export const isEmail = (s) =>
/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3){1,2)$/.test(s);
/**
* 手机号码
* @param {* s
*/
export const isMobile = (s) => /^1[3-8][0-9]{9$/.test(s);
/**
* 电话号码
* @param {* s
*/
export const isPhone = (s) => /^([0-9]{3,4-)?[0-9]{7,8$/.test(s);
/**
* URL地址
* @param {* s
*/
export const isURL = (s) => /^http[s]?:\/\/.*/.test(s);
/**
* ip地址
* @param {* s
*/
export const isIP = (s) =>
/^(25[0-5]|2[0-4]\d|[0-1]\d{2|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2|[1-9]?\d)$/.test(
s,
);
/**
* 字符串
* @param {* s
*/
export const isString = (s) => /^[A-Za-z0-9_\-\u4e00-\u9fa5]+$/.test(s);
/**
* @description:
* @param {type
* @: exp
* 非负浮点数字
*/
export const isNumber = (s) => /^\d+(\.\d+)?$/.test(s);
/**
* @description:
* @param {type
* @:
* 银行卡正则
*/
export const isBank = (s) =>
/^([1-9]{1)(\d{11|\d{15|\d{16|\d{17|\d{18)$/.test(s);

View File

@@ -0,0 +1,9 @@
<template>
<common></common>
</template>
<script setup>
import Common from "@/components/Common";
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,50 @@
<template>
<el-container>
<el-aside width="auto"><side-bar></side-bar></el-aside>
<el-container>
<el-header :height="nav_height">
<navigate-bar></navigate-bar>
</el-header>
<el-main>
<el-scrollbar
style="
padding: 20px;
box-sizing: border-box;
background: #fff;
"
>
<router-view v-slot="{ Component }">
<transition name="el-zoom-in-top" mode="out-in">
<component :key="routerAlive" :is="Component" />
</transition>
</router-view>
</el-scrollbar>
</el-main>
</el-container>
</el-container>
</template>
<script setup>
import { nav_height } from "@/styles/variables.scss.js";
import NavigateBar from "@/components/layout/NavigateBar.vue";
import SideBar from "@/components/layout/SideBar.vue";
import { provide, ref, watchEffect } from "vue";
import { useRoute } from "vue-router";
const route = useRoute();
const routerAlive = ref(null);
watchEffect(() => {
routerAlive.value = route.name;
});
provide("reload", () => {
routerAlive.value = Math.random() + "_" + Math.random();
});
</script>
<style lang="scss" scoped>
.el-main {
height: calc(100vh - v-bind(nav_height));
background: $main-bg-color;
:deep(.el-scrollbar__bar.is-horizontal) {
visibility: hidden;
}
}
</style>

View File

@@ -0,0 +1,37 @@
<template>
<div class="ve_home">
<el-row>
<el-col :span="16"><dashboard /></el-col>
<el-col :span="8">
<el-calendar class="ve_calendar"></el-calendar>
</el-col>
</el-row>
<live-chart />
</div>
</template>
<script setup>
import dashboard from "@/components/dashboard/Shortcuts";
import LiveChart from "@/components/dashboard/LiveChart";
</script>
<style lang="scss" scoped>
.ve_calendar {
background: #f56c6c;
border-radius: 10px;
&:hover {
box-shadow: 3px 3px 6px 1px rgba(0, 0, 0, 0.2);
}
:deep(.el-calendar__body) {
padding-bottom: 12px !important;
.el-calendar-day {
height: auto !important;
}
.el-calendar-table td {
border: none;
text-align: center;
}
}
}
</style>

View File

@@ -0,0 +1,42 @@
<template>
<div
v-loading="load"
element-loading-text="拼命加载中"
element-loading-spinner="el-icon-loading"
>
<iframe ref="iframe" :src="props.url" frameborder="0"></iframe>
</div>
</template>
<script setup>
import { defineProps, onMounted, ref } from "vue";
import { nav_height } from "@/styles/variables.scss.js";
const props = defineProps({
url: { type: String },
});
const load = ref(true);
const iframe = ref(null);
const setLoad = () => {
const $onLoad = () => {
load.value = false;
};
if (iframe.value.attachEvent) {
iframe.value.attachEvent("onload", $onLoad);
} else {
iframe.value.onload = $onLoad;
}
};
onMounted(() => {
setLoad();
});
</script>
<style lang="scss" scoped>
iframe {
width: 100%;
height: calc(100vh - v-bind(nav_height) - 83px);
}
</style>

View File

@@ -0,0 +1,148 @@
<template>
<common>
<div class="ve_container">
<el-card :body-style="{ background: 'rgba(0,0,0,0.15)' }">
<router-link style="float: right" :to="{ path: 'register' }"
>注册</router-link
>
<h1>Lazy-UI</h1>
<transition name="el-fade-in-linear">
<el-form
:model="form"
:rules="rules"
v-show="!success"
class="ve_form"
ref="ref_form"
:inline="false"
@keyup.enter="onSubmit"
>
<el-form-item prop="username">
<el-input
v-model.trim="username"
placeholder="用户名"
>
<template #prepend>
<el-icon :size="20"><Avatar /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model.trim="password"
show-password
placeholder="密码"
>
<template #prepend>
<el-icon :size="20"><Key /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-button
class="ve_submit"
type="primary"
@click="onSubmit"
>
登录
</el-button>
</el-form-item>
</el-form>
</transition>
</el-card>
</div>
</common>
</template>
<script setup>
import {
SET_ACCESS_TOKEN,
SET_REFRESH_TOKEN,
SET_TOKEN,
SET_UNAME,
SET_ZONE_ID,
} from "@/store/modules/app/type";
import Common from "@/components/Common";
import { ref, reactive, toRefs } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
const rules = {
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
};
const store = useStore();
const router = useRouter();
const form = reactive({
username: "",
password: "",
});
const { username, password } = toRefs(form);
const ref_form = ref(null);
const success = ref(false);
sessionStorage.clear();
store.dispatch(`app/${SET_TOKEN}`, "");
router.options.isAddDynamicMenuRoutes = false;
/**
* 登录
*/
const onSubmit = () => {
ref_form.value.validate(async (valid) => {
if (valid) {
const data = await VE_API.system.login(form);
if (data.code === 0) {
await store.dispatch(`app/${SET_TOKEN}`, data.data);
await store.dispatch(
`app/${SET_ACCESS_TOKEN}`,
data.data.accessToken,
);
await store.dispatch(
`app/${SET_REFRESH_TOKEN}`,
data.data.refreshToken,
);
await store.dispatch(`app/${SET_UNAME}`, form.username);
// 获取用户信息
const userResult = await VE_API.system.resolvingUser({
accessToken: data.data.accessToken,
});
console.log(userResult);
if (userResult && userResult.id) {
await store.dispatch(`app/${SET_ZONE_ID}`, userResult.id);
}
success.value = true;
router.push({ name: "AppMain" });
}
} else {
return;
}
});
};
</script>
<style lang="scss" scoped>
.ve_container {
position: absolute;
z-index: 1;
width: 400px;
top: 50%;
left: 100px;
transform: translateY(-50%);
transition: all 1s;
min-height: 273px;
text-align: center;
h1 {
font-size: 24px;
transition: all 1s;
font-weight: bold;
margin-bottom: 36px;
}
.ve_form {
.ve_submit {
width: 100%;
}
:deep(.el-input-group__prepend) {
padding: 0 10px;
}
}
}
</style>

View File

@@ -0,0 +1,144 @@
<template>
<common>
<div class="ve_container">
<el-card :body-style="{ background: 'rgba(0,0,0,0.15)' }">
<router-link style="float: right" :to="{ path: 'login' }"
>登录</router-link
>
<h1>Lazy-UI</h1>
<transition name="el-fade-in-linear">
<el-form
:model="form"
:rules="rules"
v-show="!success"
class="ve_form"
ref="ref_form"
:inline="false"
@keyup.enter="onSubmit"
>
<el-form-item prop="username" label="注册账号">
<el-input
v-model.trim="username"
placeholder="注册账号"
>
<template #prepend>
<el-icon :size="20"><Avatar /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password" label="账号密码">
<el-input
v-model.trim="password"
show-password
placeholder="账号密码"
>
<template #prepend>
<el-icon :size="20"><Key /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="checkPassword" label="密码确认">
<el-input
v-model.trim="checkPassword"
show-password
placeholder="密码确认"
>
<template #prepend>
<el-icon :size="20"><Key /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-button
class="ve_submit"
type="primary"
@click="onSubmit"
>
注册
</el-button>
</el-form-item>
</el-form>
</transition>
</el-card>
</div>
</common>
</template>
<script setup>
import { SET_TOKEN, SET_UNAME } from "@/store/modules/app/type";
import Common from "@/components/Common";
import { ref, reactive, toRefs, getCurrentInstance } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
const rules = {
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
checkPassword: [{ required: true, message: "密码确认", trigger: "blur" }],
};
const { proxy } = getCurrentInstance();
const store = useStore();
const router = useRouter();
const form = reactive({
username: "",
password: "",
checkPassword: "",
});
const { username, password, checkPassword } = toRefs(form);
const ref_form = ref(null);
const success = ref(false);
sessionStorage.clear();
store.dispatch(`app/${SET_TOKEN}`, "");
router.options.isAddDynamicMenuRoutes = false;
const onSubmit = () => {
// 验证两个密码是否一样
if (form.password === form.checkPassword) {
ref_form.value.validate(async (valid) => {
if (valid) {
const data = await VE_API.system.register(form);
if (data.code === 0) {
store.dispatch(`app/${SET_TOKEN}`, data.data);
store.dispatch(`app/${SET_UNAME}`, form.username);
success.value = true;
router.push({ name: "AppMain" });
}
} else {
return;
}
});
} else {
proxy.$message({
type: "error",
message: "请确认,两次输入密码是否一致!",
});
}
};
</script>
<style lang="scss" scoped>
.ve_container {
position: absolute;
z-index: 1;
width: 400px;
top: 50%;
left: 100px;
transform: translateY(-50%);
transition: all 1s;
min-height: 273px;
text-align: center;
h1 {
font-size: 24px;
transition: all 1s;
font-weight: bold;
margin-bottom: 36px;
}
.ve_form {
.ve_submit {
width: 100%;
}
:deep(.el-input-group__prepend) {
padding: 0 10px;
}
}
}
</style>

View File

@@ -0,0 +1,8 @@
const acwMenu = {
description: "数据库管理平台",
// type 0:目录 1菜单 2按钮
type: "1",
icon: "DataLine",
name: "数据库管理平台",
};
export default acwMenu;

View File

@@ -0,0 +1,241 @@
<template>
<div class="ve_container">
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="instanceId"
placeholder="数据库实例"
filterable
@change="(val) => getSchemaList(val)"
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库库名" prop="schemaName">
<el-select
v-model="schema"
filterable
placeholder="数据库实例"
@change="findInstanceSchemaColumnList"
>
<el-option
v-for="item in schemaList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="文件数据" prop="data">
<el-upload
name="data"
:multiple="false"
:show-file-list="true"
:on-change="onChange"
limit="1"
v-model="data"
placeholder=""
clearable
:auto-upload="false"
>
<!-- <el-button size="medium" type="primary">上传文件</el-button>-->
<div style="padding: 10px 0">
<Icon
type="ios-cloud-upload"
size="52"
style="#3399ff"
></Icon>
<p style="margin-top: 10px; font-size: 14px">
点击或拖拽文件至此即可上传文件
</p>
<p
style="
margin-top: 20px;
font-size: 14px;
color: red;
"
>
请上传10GB以内的待测对象
</p>
</div>
</el-upload>
</el-form-item>
</el-form>
<span>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "数据库信息导入",
buttons: {
search: { name: "查询" },
add: { name: "添加" },
edit: { name: "编辑" },
del: { name: "删除" },
analysisSchema: { name: "解析schema" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "MagicStick",
name: "数据库信息导入",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import { reactive, toRefs, onMounted, ref } from "vue";
const rules = {
name: [
{
required: true,
message: "请输入文件名称",
trigger: "blur",
},
],
data: [
{
required: true,
message: "请上传文件",
trigger: "blur",
},
],
type: [
{
required: true,
message: "请选择文件类型",
trigger: "blur",
},
],
};
const formRef = ref(null);
const form = reactive({
schema: "",
instanceId: "",
data: [],
});
const { schema, instanceId, data } = toRefs(form);
const serverList = ref(null);
const schemaList = ref(null);
/**
* 获取数据库实例
*/
const getInstanceList = () => {
// 查询数据库实例
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
};
const getSchemaList = (databaseInstance = null) => {
console.log(databaseInstance);
// 查询数据schema信息
VE_API.system
.schemaList({
instanceId: instanceId.value,
})
.then((res) => {
res.data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaList.value = res.data ? res.data : [];
});
};
onMounted(async () => {
getInstanceList();
});
/**
* 计算文件大小
* @param fileSize
* @returns {string}
*/
const formatFileSize = (fileSize) => {
if (fileSize < 1024) {
return fileSize + "B";
} else if (fileSize < 1024 * 1024) {
let temp = fileSize / 1024;
temp = temp.toFixed(2);
return temp + "KB";
} else if (fileSize < 1024 * 1024 * 1024) {
let temp = fileSize / (1024 * 1024);
temp = temp.toFixed(2);
return temp + "MB";
} else {
let temp = fileSize / (1024 * 1024 * 1024);
temp = temp.toFixed(2);
return temp + "GB";
}
};
/**
* 文件更改
* */
const onChange = (file) => {
var name = file.name;
var size = file.size;
form.name = name;
form.uid = file.uid;
if (size != null) {
form.length = formatFileSize(size);
}
form.data = file.raw;
if (name !== null) {
var split = name.split(".");
form.type = split[split.length - 1];
}
};
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let fd = new FormData();
fd.append("multipartFileList", form.data); //传文件
fd.append("instanceId", form.instanceId);
fd.append("schema", form.schema);
let res = await VE_API.system.aceSchemaImportData(fd);
const { code } = res;
if (code === 0) {
console.log(code);
}
} else {
console.log("error submit!!");
return false;
}
});
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,289 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="类型(1:自动产生的、2:手动添加的)" prop="type">
<el-select
v-model="type"
placeholder="类型(1:自动产生的、2:手动添加的)"
clearable
@change="getDataList"
>
<el-option label="自动产生的" value="1" />
<el-option label="手动添加的" value="2" />
</el-select>
</el-form-item>
<el-form-item label="原始表" prop="sourceTable">
<el-input
clearable
v-model="sourceTable"
placeholder="原始表"
></el-input>
</el-form-item>
<el-form-item label="原始表字段" prop="sourceTableColumn">
<el-input
clearable
v-model="sourceTableColumn"
placeholder="原始表字段"
></el-input>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
>
<template #tool_bar>
<el-button
v-permission="['add']"
size="small"
type="primary"
@click="handleEdit(buttons.add.name)"
>
{{ buttons.add.name }}
</el-button>
<el-button
v-permission="['analysisSchema']"
size="small"
type="primary"
@click="handleAnalysisSchemaEdit()"
>
{{ buttons.analysisSchema.name }}
</el-button>
</template>
<el-table-column
prop="instanceId"
label="数据库实例ID"
></el-table-column>
<el-table-column
prop="schema"
label="数据库实例schema"
></el-table-column>
<el-table-column
prop="sourceTable"
label="原始表"
></el-table-column>
<el-table-column prop="sourceTableColumn" label="原始表字段">
</el-table-column>
<el-table-column
prop="relationTable"
label="关系表"
></el-table-column>
<el-table-column
prop="relationTableColumn"
label="关系表字段"
></el-table-column>
<el-table-column prop="relation" label="关系"></el-table-column>
<el-table-column
prop="type"
label="类型(1:自动产生的、2:手动添加的)"
></el-table-column>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['edit']"
@click.prevent="handleEdit(buttons.edit.name, row)"
type="primary"
size="small"
>
{{ buttons.edit.name }}
</el-button>
<el-button
v-permission="['del']"
@click.prevent="handleDel(row.id)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!-- 编辑组件 -->
<acw-table-association-relation-edit
v-if="showDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showDialog"
@closeDialog="handelDialog($event)"
/>
<acw-table-association-relation-analysis-schema-edit
v-if="showAnalysisSchemaDialog"
:showDialog="showAnalysisSchemaDialog"
@closeDialog="handleAnalysisSchemaDialog($event)"
/>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "表关联关系",
buttons: {
search: { name: "查询" },
add: { name: "添加" },
edit: { name: "编辑" },
del: { name: "删除" },
analysisSchema: { name: "解析schema" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Flag",
name: "表关联关系管理",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
import AcwTableAssociationRelationEdit from "@/views/layoutpages/acw/components/AcwTableAssociationRelationEdit.vue";
import AcwTableAssociationRelationAnalysisSchemaEdit from "@/views/layoutpages/acw/components/AcwTableAssociationRelationAnalysisSchemaEdit.vue";
const { proxy } = getCurrentInstance();
const rowData = ref(null);
const dialogTitle = ref("");
const showDialog = ref(false);
const showAnalysisSchemaDialog = ref(false);
const queryForm = ref(null);
const tableData = ref([]);
const params = reactive({
type: "2",
sourceTable: "",
sourceTableColumn: "",
size: 10,
current: 1,
total: 0,
});
const { type, sourceTable, sourceTableColumn, size, current, total } =
toRefs(params);
/**
* 左侧服务器树
* @type {Ref<UnwrapRef<*[]>>}
*/
const leftServerTree = ref([]);
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showDialog.value = true;
dialogTitle.value = title;
rowData.value = row;
};
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handelDialog = (e) => {
showDialog.value = e;
getDataList();
};
const handleAnalysisSchemaEdit = () => {
showAnalysisSchemaDialog.value = true;
};
const handleAnalysisSchemaDialog = (e) => {
showAnalysisSchemaDialog.value = e;
getDataList();
};
/**删除行数据
* @description:
* @param {*}
* @return {*}
*/
const handleDel = (id) => {
proxy
.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
const { code } =
await VE_API.system.acwTableAssociationRelationDelete({ id });
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } =
await VE_API.system.acwTableAssociationRelationPage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
record.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
leftServerTree.value = record;
}
};
onMounted(async () => {
await getDataList();
// maxHeight(pagination, queryForm, toolBar, ve_max_height);
});
</script>
<style lang="scss" scoped>
.prefix {
color: var(--el-color-primary);
margin-right: 10px;
}
.prefix.is-leaf {
color: var(--el-color-success);
}
</style>

View File

@@ -0,0 +1,239 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="接口分组" prop="interfaceGroup">
<el-input
clearable
v-model="interfaceGroup"
placeholder="接口分组"
></el-input>
</el-form-item>
<el-form-item label="接口描述" prop="interfaceDescribe">
<el-input
clearable
v-model="interfaceDescribe"
placeholder="接口描述"
></el-input>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
>
<template #tool_bar>
<el-button
v-permission="['add']"
size="small"
type="primary"
@click="handleEdit(buttons.add.name)"
>
{{ buttons.add.name }}
</el-button>
</template>
<el-table-column prop="tag" label="接口分组"></el-table-column>
<el-table-column
prop="description"
label="接口描述"
></el-table-column>
<el-table-column prop="method" label="接口类型"></el-table-column>
<el-table-column prop="path" label="接口地址"></el-table-column>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['edit']"
@click.prevent="handleEdit(buttons.edit.name, row)"
type="primary"
size="small"
>
{{ buttons.edit.name }}
</el-button>
<el-button
v-permission="['del']"
@click.prevent="handleDel(row.id)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
<el-button
v-permission="['info']"
@click.prevent="handleInfo('详情', row)"
type="primary"
size="small"
>
{{ buttons.info.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!-- 编辑组件 -->
<api-factory-edit
v-if="showDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showDialog"
@closeDialog="handelDialog($event)"
/>
<!-- 编辑组件 -->
<api-factory-edit
v-if="showInfo"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showInfo"
@closeDialog="handelInfo($event)"
/>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "接口工厂查询与设置",
buttons: {
search: { name: "查询" },
add: { name: "添加" },
edit: { name: "编辑" },
del: { name: "删除" },
info: { name: "详情" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Histogram",
name: "接口工厂管理",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import ApiFactoryEdit from "@/views/layoutpages/acw/components/ApiFactoryEdit";
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
const { proxy } = getCurrentInstance();
const rowData = ref(null);
const dialogTitle = ref("");
const showDialog = ref(false);
const showInfo = ref(false);
const queryForm = ref(null);
const tableData = ref([]);
const params = reactive({
interfaceGroup: "",
interfaceDescribe: "",
size: 10,
current: 1,
total: 0,
});
const { interfaceGroup, interfaceDescribe, size, current, total } =
toRefs(params);
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showDialog.value = true;
dialogTitle.value = title;
rowData.value = row;
};
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handelDialog = (e) => {
showDialog.value = e;
getDataList();
};
/**删除行数据
* @description:
* @param {*}
* @return {*}
*/
const handleDel = (id) => {
proxy
.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
const { code } = await VE_API.system.apiDelete({ id });
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**
* 行信息
*/
const handleInfo = (title, row = null) => {
showInfo.value = true;
dialogTitle.value = title;
rowData.value = row;
};
const handelInfo = (e) => {
showInfo.value = e;
getDataList();
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } = await VE_API.system.apiPage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
}
};
onMounted(async () => {
await getDataList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,207 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="应用名" prop="applicationName">
<el-input
clearable
v-model="applicationName"
placeholder="应用名"
></el-input>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
>
<template #tool_bar>
<el-button
v-permission="['add']"
size="small"
type="primary"
@click="handleEdit(buttons.add.name)"
>
{{ buttons.add.name }}
</el-button>
</template>
<el-table-column
prop="projectName"
label="项目名"
></el-table-column>
<el-table-column
prop="applicationName"
label="应用名"
></el-table-column>
<el-table-column
prop="schemaName"
label="数据库名称"
></el-table-column>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['edit']"
@click.prevent="handleEdit(buttons.edit.name, row)"
type="primary"
size="small"
>
{{ buttons.edit.name }}
</el-button>
<el-button
v-permission="['del']"
@click.prevent="handleDel(row.applicationId)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!-- 编辑组件 -->
<application-edit
v-if="showDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showDialog"
@closeDialog="handelDialog($event)"
/>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "acw应用查询与设置",
buttons: {
search: { name: "查询" },
add: { name: "添加" },
edit: { name: "编辑" },
del: { name: "删除" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Histogram",
name: "ACW应用管理",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import ApplicationEdit from "./components/ApplicationEdit";
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
const { proxy } = getCurrentInstance();
const rowData = ref(null);
const dialogTitle = ref("");
const showDialog = ref(false);
const queryForm = ref(null);
const tableData = ref([]);
const params = reactive({
applicationName: "",
size: 10,
current: 1,
total: 0,
});
const { applicationName, size, current, total } = toRefs(params);
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showDialog.value = true;
dialogTitle.value = title;
rowData.value = row;
};
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handelDialog = (e) => {
showDialog.value = e;
getDataList();
};
/**删除行数据
* @description:
* @param {*}
* @return {*}
*/
const handleDel = (applicationId) => {
console.log(applicationId);
proxy
.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
const { code } = await VE_API.system.applicationDelete({
id: applicationId,
});
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } = await VE_API.system.applicationPage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
}
};
onMounted(async () => {
await getDataList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,344 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="服务器名称" prop="instanceName">
<el-input
clearable
v-model="instanceName"
placeholder="服务器名称"
></el-input>
</el-form-item>
<el-form-item
label="是否初始化数据库到本地"
prop="initializeToLocal"
>
<el-select
v-model="initializeToLocal"
placeholder="是否初始化数据库到本地"
clearable
@change="getDataList"
>
<el-option label="初始化数据库到本地" value="true" />
<el-option label="不初始化数据库到本地" value="false" />
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
>
<template #tool_bar>
<el-button
v-permission="['add']"
size="small"
type="primary"
@click="handleEdit(buttons.add.name)"
>
{{ buttons.add.name }}
</el-button>
</template>
<el-table-column
prop="instanceName"
label="服务器名称"
></el-table-column>
<el-table-column
prop="driverClassName"
label="驱动程序类名"
></el-table-column>
<el-table-column prop="username" label="登录名"></el-table-column>
<el-table-column prop="password" label="登陆密码">
<template v-slot="{ row }">
<span>
{{
row.password &&
row.password
.split("")
.fill("*", 1, -1)
.join()
.replace(/\,/g, "")
}}
</span>
</template>
</el-table-column>
<el-table-column prop="host" label="host"></el-table-column>
<el-table-column prop="port" label="端口"></el-table-column>
<el-table-column prop="status" label="状态"></el-table-column>
<el-table-column prop="sort" label="排序"></el-table-column>
<el-table-column label="数据源类型">
<template v-slot="{ row }">
<el-select
v-model="row.lazyDataSourceType"
placeholder="数据源类型"
disabled
>
<el-option label="MySQL" value="MySQL" />
<el-option label="H2" value="H2" />
<el-option label="CLICK_HOUSE" value="CLICK_HOUSE" />
<el-option label="POSTGRESQL" value="POSTGRESQL" />
</el-select>
</template>
</el-table-column>
<el-table-column label="是否初始化数据库到本地">
<template v-slot="{ row }">
<el-select
v-model="row.initializeToLocal"
placeholder="是否初始化数据库到本地"
disabled
>
<el-option label="初始化数据库到本地" :value="true" />
<el-option
label="不初始化数据库到本地"
:value="false"
/>
</el-select>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['edit']"
@click.prevent="handleEdit(buttons.edit.name, row)"
type="primary"
size="small"
>
{{ buttons.edit.name }}
</el-button>
<el-button
v-permission="['del']"
@click.prevent="handleDel(row.id)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
<el-button
v-permission="['reload']"
@click="handleDatabaseInstanceReload(row.id)"
type="primary"
size="small"
>
{{ buttons.reload.name }}
</el-button>
<el-button
v-permission="['backups']"
@click="handleBackUpsRoute(row.id)"
type="primary"
size="small"
>
{{ buttons.backups.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!-- 编辑组件 -->
<database-instance-edit
v-if="showDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showDialog"
@closeDialog="handelDialog($event)"
/>
<!-- schema组件 -->
<database-schema-edit
v-if="showschemaDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showschemaDialog"
@closeDialog="handelDialog($event)"
/>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "数据库服务器查询与设置",
buttons: {
search: { name: "查询" },
add: { name: "添加" },
edit: { name: "编辑" },
del: { name: "删除" },
reload: { name: "初始化服务器" },
backups: { name: "实例备份" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Coin",
name: "数据库实例管理",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import databaseInstanceEdit from "./components/DatabaseInstanceEdit";
import DatabaseSchemaEdit from "./components/DatabaseSchemaEdit";
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
getAsyncRouteName,
} from "@/views/layoutpages/common";
import { useRoute, useRouter } from "vue-router";
const { proxy } = getCurrentInstance();
const route = useRoute();
const router = useRouter();
const rowData = ref(null);
const dialogTitle = ref("");
const showDialog = ref(false);
const showschemaDialog = ref(false);
const queryForm = ref(null);
const tableData = ref([]);
const params = reactive({
instanceName: "",
initializeToLocal: "",
size: 10,
current: 1,
total: 0,
});
const { instanceName, initializeToLocal, size, current, total } =
toRefs(params);
/**
* 左侧服务器树
* @type {Ref<UnwrapRef<*[]>>}
*/
const leftServerTree = ref([]);
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showDialog.value = true;
dialogTitle.value = title;
rowData.value = row;
};
/**
* @description: 数据库实例备份信息
* @param {*}
* @return {*}
*/
const handleBackUpsRoute = async (title) => {
let path = "acw/DatabaseInstanceBackUps";
const toName = await getAsyncRouteName(title, path, "add", {
router,
route,
});
router.push({ name: toName });
};
/**
* 重新加载实例
*/
const handleDatabaseInstanceReload = async (id) => {
// 重新加载服务器
console.log("重新加载服务器:" + id);
const { code } = await VE_API.system.databaseInstanceReload({ id });
if (code === 0) {
getDataList();
}
};
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handelDialog = (e) => {
showDialog.value = e;
getDataList();
};
/**删除行数据
* @description:
* @param {*}
* @return {*}
*/
const handleDel = (id) => {
proxy
.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
const { code } = await VE_API.system.databaseInstanceDelete({ id });
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } = await VE_API.system.databaseInstancePage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
record.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
leftServerTree.value = record;
}
};
onMounted(async () => {
await getDataList();
// maxHeight(pagination, queryForm, toolBar, ve_max_height);
});
</script>
<style lang="scss" scoped>
.prefix {
color: var(--el-color-primary);
margin-right: 10px;
}
.prefix.is-leaf {
color: var(--el-color-success);
}
</style>

View File

@@ -0,0 +1,273 @@
<template>
<div class="ve_container">
<!-- <el-descriptions border>-->
<!-- <el-descriptions-item label="实例名称"-->
<!-- >实例名称-->
<!-- </el-descriptions-item>-->
<!-- <el-descriptions-item label="实例数据库数量"-->
<!-- >实例数据库数量-->
<!-- </el-descriptions-item>-->
<!-- <el-descriptions-item label="实例表数量"-->
<!-- >实例表数量-->
<!-- </el-descriptions-item>-->
<!-- </el-descriptions>-->
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="params.instanceId"
placeholder="数据库实例"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
>
<template #tool_bar>
<el-button
v-permission="['backups']"
size="small"
type="primary"
@click="handleEdit(buttons.backups.name)"
>
{{ buttons.backups.name }}
</el-button>
</template>
<el-table-column prop="id" label="记录ID"></el-table-column>
<el-table-column
prop="instanceName"
label="实例名称"
></el-table-column>
<el-table-column
prop="schemaNum"
label="数据库数量"
></el-table-column>
<el-table-column prop="status" label="状态"></el-table-column>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['del']"
@click.prevent="handleDel(row.id)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<database-instance-back-ups-add
v-if="showsBackUpsDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showsBackUpsDialog"
@closeDialog="handelDialog($event)"
/>
</div>
</template>
<script>
import DatabaseInstanceBackUpsAdd from "@/views/layoutpages/acw/components/DatabaseInstanceBackUpsAdd";
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
components: { DatabaseInstanceBackUpsAdd },
data: () => ({
description: "数据库实例备份",
buttons: {
search: { name: "查询" },
del: { name: "删除数据库实例备份" },
backups: { name: "实例备份" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Guide",
name: "数据库实例备份",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import { reactive, toRefs, ref, defineProps, onMounted } from "vue";
import databaseInstanceBackUpsAdd from "./components/DatabaseInstanceBackUpsAdd";
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const { rowData } = toRefs(props);
const queryForm = ref(null);
const tableData = ref([]);
const showsBackUpsDialog = ref(false);
const serverList = ref(null);
const params = reactive({
instanceName: "",
initializeToLocal: "",
size: 10,
current: 1,
total: 0,
});
const form = reactive({
name: "",
userName: "",
password: "",
role: "",
status: 1,
});
const { size, current, total } = toRefs(params);
const { userName, name, password, role, status } = toRefs(form);
const roleList = ref([]);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((name.value = rowData.value.name),
(userName.value = rowData.value.userName),
(password.value = rowData.value.password),
(role.value = rowData.value.role),
(status.value = rowData.value.status));
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showsBackUpsDialog.value = true;
rowData.value = row;
};
/**
* 删除数据库事例备份
*/
const handleDel = async (id) => {
const { code } = await VE_API.system.databaseInstanceBackUpDelete({
id: id,
});
if (code === 0) {
getDataList();
}
};
/**
* @description: 获取角色列表
* @param {*}
* @return {*}
*/
const getRoleList = async () => {
const { code, data } = await VE_API.system.roleList(
{
page: 1,
size: 10,
},
{ Global: false },
);
if (code === "0") {
const { list } = data;
roleList.value = list;
}
};
getRoleList();
/**
* 隐藏弹框
* */
const handelDialog = (e) => {
showsBackUpsDialog.value = e;
getDataList();
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } =
await VE_API.system.databaseInstanceBackUpPage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
record.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
}
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
return serverList.value;
});
};
onMounted(async () => {
await getServerInstanceList();
await getDataList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,88 @@
<template>
<el-tabs
v-model="currentConsoleTableKey"
type="card"
closable
@tab-remove="closeCurrentConsoleTable(currentConsoleTableKey)"
>
<el-tab-pane
v-for="item in consoleTableList"
:key="item.index"
:label="item.title"
:name="item.name"
>
<database-query-console />
</el-tab-pane>
<el-tab-pane
key="lastConsoleTable"
label="新增页签"
name="新增页签name"
@onclick="addConsoleTable(currentConsoleTableKey)"
>
<el-button
size="small"
@click="addConsoleTable(currentConsoleTableKey)"
>
新增页签
</el-button>
</el-tab-pane>
</el-tabs>
</template>
<script>
import DatabaseQueryConsole from "@/views/layoutpages/acw/components/DatabaseQueryConsole.vue";
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "数据库多表复杂查询控制台",
buttons: {
search: { name: "查询" },
downLoad: { name: "导出查询结果为Excel" },
downLoadUpsert: { name: "Upsert下载查询结果" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "IceCream",
name: "数据库多表复杂查询控制台",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import { ref, onMounted } from "vue";
// 当前页
const currentConsoleTableKey = ref(0);
// 查询sql的所有列
const consoleTableList = ref([
{
index: 0,
name: 0,
title: "执行查询",
},
]);
/**
* 添加页签
*/
const addConsoleTable = (currentIndex) => {
console.log("当前标签页🏷️" + currentIndex);
currentConsoleTableKey.value = consoleTableList.value.length + 1;
consoleTableList.value.push({
name: currentConsoleTableKey.value,
title: "执行查询 (" + currentConsoleTableKey.value + ")",
});
};
/**
* 关闭当前table
*/
const closeCurrentConsoleTable = (currentIndex) => {
console.log("删除页签" + currentIndex);
consoleTableList.value.splice(currentIndex - 1, 1);
};
onMounted(async () => {});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,626 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="数据库实例" property="instanceId">
<el-select
v-model="instanceId"
@change="getDataList"
placeholder="数据库实例"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库库名" prop="schemaName">
<el-input
clearable
v-model="schemaName"
placeholder="数据库库名"
></el-input>
</el-form-item>
<el-form-item
label="是否初始化数据库到本地"
prop="initializeToLocal"
>
<el-select
v-model="initializeToLocal"
placeholder="是否初始化数据库到本地"
clearable
@change="getDataList"
>
<el-option label="初始化数据库到本地" value="true" />
<el-option label="不初始化数据库到本地" value="false" />
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
border
style="width: 80%"
@selectionChange="handleSelectionChange"
ref="multipleTableRef"
>
>
<template #tool_bar>
<el-button
v-permission="['add']"
size="small"
type="primary"
@click="handleEdit(buttons.add.name)"
>
{{ buttons.add.name }}
</el-button>
<el-button
v-permission="['batchDel']"
size="small"
type="danger"
@click="batchDel"
>
{{ buttons.batchDel.name }}
</el-button>
<el-button
v-permission="['batchExportData']"
size="small"
type="primary"
@click="batchExportData"
>
{{ buttons.batchExportData.name }}
</el-button>
<el-button
v-permission="['schemaDeriveView']"
@click="
handleSchemaDeriveView(
buttons.schemaDeriveView.name,
row,
)
"
type="primary"
size="small"
>
{{ buttons.schemaDeriveView.name }}
</el-button>
</template>
<el-table-column type="selection" width="55" />
<!-- <el-table-column prop="id" label="ID"></el-table-column>-->
<el-table-column
prop="instanceName"
label="数据库实例"
></el-table-column>
<el-table-column
prop="schemaName"
label="数据库库名"
></el-table-column>
<el-table-column label="是否初始化数据库到本地">
<template v-slot="{ row }">
<el-select
v-model="row.initializeToLocal"
placeholder="是否初始化数据库到本地"
disabled
>
<el-option label="初始化数据库到本地" :value="true" />
<el-option
label="不初始化数据库到本地"
:value="false"
/>
</el-select>
</template>
</el-table-column>
<el-table-column
prop="dataLength"
label="数据大小"
></el-table-column>
<el-table-column
prop="indexLength"
label="索引大小"
></el-table-column>
<!-- <el-table-column-->
<!-- prop="characterSet"-->
<!-- label="字符集"-->
<!-- ></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="sortingRules"-->
<!-- label="排序规则"-->
<!-- ></el-table-column>-->
<!-- <el-table-column prop="ext" label="ext"></el-table-column>-->
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['edit']"
@click.prevent="handleEdit(buttons.edit.name, row)"
type="primary"
size="small"
>
{{ buttons.edit.name }}
</el-button>
<el-button
v-permission="['exportData']"
@click.prevent="handleExportData(row)"
type="primary"
size="small"
>
{{ buttons.exportData.name }}
</el-button>
<el-button
v-permission="['exportStructure']"
@click.prevent="handleExportStructure(row)"
type="primary"
size="small"
>
{{ buttons.exportStructure.name }}
</el-button>
<el-button
v-permission="['exportStructureExcel']"
@click.prevent="handleExportStructureExcel(row)"
type="primary"
size="small"
>
{{ buttons.exportStructureExcel.name }}
</el-button>
<el-button
v-permission="['del']"
@click.prevent="handleDel(row)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
<el-button
v-permission="['reload']"
@click="handleDatabaseSchemaReload(row)"
type="primary"
size="small"
>
{{ buttons.reload.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!-- 编辑组件 -->
<database-schema-edit
v-if="showDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showDialog"
@closeDialog="handelDialog($event)"
/>
<!-- 数据库衍生视图 -->
<database-schema-derive-view
v-if="showSchemaDeriveViewDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showSchemaDeriveViewDialog"
@closeDialog="handelDialog($event)"
/>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "数据库信息查询与设置",
buttons: {
search: { name: "查询" },
add: { name: "添加" },
edit: { name: "编辑" },
del: { name: "删除" },
exportData: { name: "导出数据" },
exportStructure: { name: "导出结构为SQL" },
exportStructureExcel: { name: "导出结构为Excel" },
batchDel: { name: "批量删除" },
batchExportData: { name: "批量导出数据" },
reload: { name: "初始化schema" },
schemaDeriveView: { name: "数据库衍生视图" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "IceDrink",
name: "数据库信息管理",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import databaseSchemaEdit from "./components/DatabaseSchemaEdit";
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
import DatabaseSchemaDeriveView from "@/views/layoutpages/acw/components/DatabaseSchemaDeriveView.vue";
const { proxy } = getCurrentInstance();
const rowData = ref(null);
const dialogTitle = ref("");
const showDialog = ref(false);
const showSchemaDeriveViewDialog = ref(false);
const queryForm = ref(null);
const tableData = ref([]);
const serverList = ref(null);
// 批量删除选中的 schema
const batchSelectDatabaseSchemaList = ref([]);
const params = reactive({
instanceId: "",
schemaName: "",
initializeToLocal: false,
size: 10,
current: 1,
total: 0,
});
const { instanceId, schemaName, initializeToLocal, size, current, total } =
toRefs(params);
const multipleTableRef = ref(null);
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showDialog.value = true;
dialogTitle.value = title;
rowData.value = row;
};
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handelDialog = (e) => {
showDialog.value = e;
getDataList();
};
/**
*
* 批量导出数据
*/
const batchExportData = () => {
if (batchSelectDatabaseSchemaList.value.length == 0) {
proxy.$message({
type: "info",
message: "请选择一个schema",
});
return;
}
proxy
.$confirm("此操作将批量导出数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
let databaseSchemaDeleteCommandList =
batchSelectDatabaseSchemaList.value.map((item) => {
return {
instanceName: item.instanceName,
schema: item.schemaName,
};
});
let res = await VE_API.system.batchExportSchemaData(
databaseSchemaDeleteCommandList,
{
responseType: "blob",
},
);
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
})
.finally(() => {
getDataList();
});
};
/**
*
* 批量删除
*/
const batchDel = () => {
if (batchSelectDatabaseSchemaList.value.length === 0) {
proxy.$message({
type: "info",
message: "请选择一个schema",
});
return;
}
proxy
.$confirm(
`此操作将批量永久删除【${batchSelectDatabaseSchemaList.value.length}】个数据库, 是否继续?`,
"提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
},
)
.then(async () => {
let databaseSchemaDeleteCommandList =
batchSelectDatabaseSchemaList.value.map((item) => {
return {
instanceName: item.instanceName,
instanceId: item.instanceId,
schema: item.schemaName,
};
});
const { code } = await VE_API.system.batchDeleteInstanceSchema(
databaseSchemaDeleteCommandList,
);
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**删除行数据
* @description:
* @param {*}
* @return {*}
*/
const handleDel = (row) => {
proxy
.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
if (row === undefined) {
getDataList();
} else {
let databaseSchemaDeleteCommandList = [
{
instanceName: row.instanceName,
instanceId: row.instanceId,
schema: row.schemaName,
},
];
const { code } = await VE_API.system.batchDeleteInstanceSchema(
databaseSchemaDeleteCommandList,
);
if (code === 0) {
getDataList();
}
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**导出数据
* @description:
* @param {*}
* @return {*}
*/
const handleExportData = (row) => {
proxy
.$confirm("此操作将导出数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
if (row == undefined) {
getDataList();
} else {
let databaseSchemaDeleteCommandList = [
{
instanceName: row.instanceName,
schema: row.schemaName,
},
];
let res = await VE_API.system.batchExportSchemaData(
databaseSchemaDeleteCommandList,
{
responseType: "blob",
},
);
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
})
.finally(() => {
getDataList();
});
};
/**
* 导出结构
*/
const handleExportStructure = async (row) => {
let res = await VE_API.system.exportTableStructureSql(row, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
};
/**
* 导出结构
*/
const handleExportStructureExcel = async (row) => {
let res = await VE_API.system.exportTableStructureExcel(row, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
};
/**
* 全选事件
*
*/
const handleSelectionChange = (val) => {
batchSelectDatabaseSchemaList.value = val;
console.log(batchSelectDatabaseSchemaList.value);
};
/**
* 数据库衍生视图
* @param row
*/
const handleSchemaDeriveView = (title, row) => {
console.log(row);
showSchemaDeriveViewDialog.value = true;
dialogTitle.value = title;
rowData.value = row;
};
/**
* 初始化数据库schema
* **/
const handleDatabaseSchemaReload = async (row) => {
const { code, data } = await VE_API.system.schemaReload({
instanceId: row.instanceId,
schema: row.schemaName,
});
if (code === 0) {
console.log("success");
}
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } = await VE_API.system.schemaPage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
}
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
if (serverList.value.length == 0) {
return;
}
params.instanceId = serverList.value[0].id;
getDataList();
});
};
onMounted(async () => {
await getServerInstanceList();
});
</script>
<style lang="scss" scoped>
.ve_container {
::v-deep
.el-table__header
.el-table-column--selection
.cell
.el-checkbox:after {
color: #606266;
content: "全选";
font-size: 12px;
margin-left: 2px;
font-weight: bold;
}
}
</style>

View File

@@ -0,0 +1,348 @@
<template>
<div class="ve_container">
<!-- <el-descriptions border>-->
<!-- <el-descriptions-item label="实例名称"-->
<!-- >实例名称-->
<!-- </el-descriptions-item>-->
<!-- <el-descriptions-item label="实例数据库数量"-->
<!-- >实例数据库数量-->
<!-- </el-descriptions-item>-->
<!-- <el-descriptions-item label="实例表数量"-->
<!-- >实例表数量-->
<!-- </el-descriptions-item>-->
<!-- </el-descriptions>-->
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="params.instanceId"
@change="getSchemaList"
placeholder="数据库实例"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库名" prop="schemaName">
<el-select
clearable
filterable
@change="getDataList"
v-model="params.schemaName"
placeholder="数据库名"
>
<el-option
v-for="item in schemaList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
>
<template #tool_bar>
<el-button
v-permission="['backups']"
size="small"
type="primary"
@click="handleEdit(buttons.backups.name)"
>
{{ buttons.backups.name }}
</el-button>
</template>
<el-table-column prop="id" label="记录ID"></el-table-column>
<el-table-column
prop="instanceName"
label="实例名称"
></el-table-column>
<el-table-column
prop="schemaName"
label="数据库名称"
></el-table-column>
<el-table-column
prop="tableNum"
label="数据库表数量"
:sortable="true"
></el-table-column>
<el-table-column prop="status" label="状态"></el-table-column>
<el-table-column prop="path" label="备份数据库地址"
><template #default="scope">
<a
style="color: #7099f9"
@click="downloadBackUps(scope.row)"
>{{ scope.row.path }}
</a>
</template></el-table-column
>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['down']"
@click.prevent="downloadBackUps(row)"
type="primary"
size="small"
>
{{ buttons.down.name }}
</el-button>
<el-button
v-permission="['del']"
@click.prevent="handleDel(row.id)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<database-schema-back-ups-add
v-if="showsBackUpsDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showsBackUpsDialog"
@closeDialog="handelDialog($event)"
/>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "数据库备份",
buttons: {
search: { name: "查询" },
down: { name: "下载" },
del: { name: "删除" },
backups: { name: "数据库备份" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Ship",
name: "数据库备份",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import { reactive, toRefs, ref, defineProps, onMounted } from "vue";
import databaseSchemaBackUpsAdd from "./components/DatabaseSchemaBackUpsAdd";
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const { rowData } = toRefs(props);
const queryForm = ref(null);
const tableData = ref([]);
const showsBackUpsDialog = ref(false);
const serverList = ref(null);
const schemaList = ref(null);
const params = reactive({
instanceId: "",
// status: 1,
schemaName: "",
size: 10,
current: 1,
total: 0,
});
const { size, current, total } = toRefs(params);
const form = reactive({
instanceId: "",
status: 1,
});
const { instanceId, status } = toRefs(form);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((instanceId.value = rowData.value.instanceId),
(status.value = rowData.value.status));
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showsBackUpsDialog.value = true;
rowData.value = row;
};
/**
* 删除备份记录
* @param id
*/
const handleDel = async (id) => {
const { code } = await VE_API.system.databaseSchemaBackUpRemove({ id: id });
if (code === 0) {
getDataList();
}
};
/**
*@param row 行数据
* 下载备份数据
*
* */
const downloadBackUps = async (row) => {
// console.log(row);
let res = await VE_API.system.downLocalFile(
{ path: row.path },
{
responseType: "blob",
},
);
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
};
/**
* 隐藏弹框
* */
const handelDialog = (e) => {
showsBackUpsDialog.value = e;
getDataList();
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } = await VE_API.system.databaseSchemaBackUpPage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
record.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
}
};
/**
* 获取 数据库
* @param instanceId 数据库实例ID
* @returns {Promise<void>}
*/
const getSchemaList = async (instanceId) => {
let res;
if (instanceId == null) {
return;
} else {
res = await VE_API.system.schemaList({
instanceId,
});
}
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaList.value = data;
if (schemaList.value) {
// 默认第一个实例的第一个数据库
params.schemaName = schemaList.value[0].schemaName;
}
return schemaList.value;
}
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
// 默认第一个实例
if (serverList.value && serverList.value.length > 0) {
params.instanceId = serverList.value[0].id;
getSchemaList(params.instanceId);
}
return serverList.value;
});
};
onMounted(async () => {
await getServerInstanceList();
await getDataList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,88 @@
<template>
<el-tabs
v-model="currentConsoleTableKey"
type="card"
closable
@tab-remove="closeCurrentConsoleTable(currentConsoleTableKey)"
>
<el-tab-pane
v-for="item in consoleTableList"
:key="item.index"
:label="item.title"
:name="item.name"
>
<database-table-console />
</el-tab-pane>
<el-tab-pane
key="lastConsoleTable"
label="新增页签"
name="新增页签name"
@onclick="addConsoleTable(currentConsoleTableKey)"
>
<el-button
size="small"
@click="addConsoleTable(currentConsoleTableKey)"
>
新增单表查询页签
</el-button>
</el-tab-pane>
</el-tabs>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "单表查询控制台",
buttons: {
search: { name: "查询" },
downLoad: { name: "导出查询结果为Excel" },
downLoadUpsert: { name: "Upsert下载查询结果" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Finished",
name: "单表查询控制台",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import { ref, onMounted } from "vue";
import DatabaseTableConsole from "@/views/layoutpages/acw/components/DatabaseTableConsole.vue";
// 当前页
const currentConsoleTableKey = ref(0);
// 查询sql的所有列
const consoleTableList = ref([
{
index: 0,
name: 0,
title: "执行查询",
},
]);
/**
* 添加页签
*/
const addConsoleTable = (currentIndex) => {
console.log("当前标签页🏷️" + currentIndex);
currentConsoleTableKey.value = consoleTableList.value.length + 1;
consoleTableList.value.push({
name: currentConsoleTableKey.value,
title: "执行查询 (" + currentConsoleTableKey.value + ")",
});
};
/**
* 关闭当前table
*/
const closeCurrentConsoleTable = (currentIndex) => {
console.log("删除页签" + currentIndex);
consoleTableList.value.splice(currentIndex - 1, 1);
};
onMounted(async () => {});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,480 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="数据库实例" property="instanceId">
<el-select
v-model="params.instanceId"
@change="getSchemaList"
placeholder="数据库实例"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库名" prop="schemaName">
<el-select
clearable
v-model="params.schemaName"
placeholder="数据库名"
@change="getDataList"
filterable
>
<el-option
v-for="item in schemaNameList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="表名" prop="tableName">
<el-input
clearable
v-model="tableName"
placeholder="表名"
></el-input>
</el-form-item>
<el-form-item
label="是否初始化数据库到本地"
prop="initializeToLocal"
>
<el-select
v-model="initializeToLocal"
placeholder="是否初始化数据库到本地"
clearable
@change="getDataList"
>
<el-option label="初始化数据库到本地" value="true" />
<el-option label="不初始化数据库到本地" value="false" />
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
@selectionChange="handleSelectionChange"
>
<template #tool_bar>
<el-button
v-permission="['add']"
size="small"
type="primary"
@click="handleEditRoute(buttons.add.name)"
>
{{ buttons.add.name }}
</el-button>
<el-button
v-permission="['batchDel']"
size="small"
type="danger"
@click="batchDelTable"
>
{{ buttons.batchDel.name }}
</el-button>
</template>
<el-table-column type="selection" width="55" />
<el-table-column
prop="schemaName"
label="数据库库名"
></el-table-column>
<el-table-column prop="tableName" label="表名称"> </el-table-column>
<el-table-column
prop="tableComment"
label="表描述"
width="120"
></el-table-column>
<el-table-column prop="tableType" label="表类型"></el-table-column>
<el-table-column
prop="tableRows"
sortable
label="表行数"
></el-table-column>
<!-- <el-table-column-->
<!-- prop="avgRowLength"-->
<!-- label="avgRowLength"-->
<!-- ></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="autoIncrement"-->
<!-- label="autoIncrement"-->
<!-- ></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="checkTime"-->
<!-- label="checkTime"-->
<!-- ></el-table-column>-->
<!-- <el-table-column prop="checksum" label="checksum"></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="createOptions"-->
<!-- label="createOptions"-->
<!-- ></el-table-column>-->
<!-- <el-table-column prop="dataFree" label="dataFree"></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="dataLength"-->
<!-- label="dataLength"-->
<!-- ></el-table-column>-->
<el-table-column prop="engine" label="表引擎"></el-table-column>
<el-table-column
prop="dataLength"
label="数据大小"
></el-table-column>
<el-table-column
prop="indexLength"
label="索引大小"
></el-table-column>
<!-- <el-table-column-->
<!-- prop="maxDataLength"-->
<!-- label="maxDataLength"-->
<!-- ></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="rowFormat"-->
<!-- label="rowFormat"-->
<!-- ></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="tableCatalog"-->
<!-- label="tableCatalog"-->
<!-- ></el-table-column>-->
<!-- <el-table-column-->
<!-- prop="tableCollation"-->
<!-- label="tableCollation"-->
<!-- ></el-table-column>-->
<el-table-column
prop="createTime"
label="创建时间"
></el-table-column>
<el-table-column prop="version" label="版本"></el-table-column>
<el-table-column
prop="updateTime"
label="更新时间"
></el-table-column>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['edit']"
@click.prevent="handleEditRoute(buttons.edit.name, row)"
type="primary"
size="small"
>
{{ buttons.edit.name }}
</el-button>
<el-button
v-permission="['del']"
@click.prevent="handleDel(row)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
<el-button
v-permission="['more']"
@click.prevent="handleShowMore(buttons.more.name, row)"
type="primary"
size="small"
>
{{ buttons.more.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!-- 编辑组件 -->
<database-table-edit
v-if="showDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showDialog"
@closeDialog="handleDialog($event)"
/>
<!-- 更多 -->
<database-table-more
v-if="showMore"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showMore"
@closeDialog="handleShowMoreDialog($event)"
/>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "数据库表查询与设置",
buttons: {
search: { name: "查询" },
add: { name: "添加表" },
edit: { name: "编辑" },
// crud: { name: "表crud操作" },
del: { name: "删除" },
batchDel: { name: "批量删除" },
more: { name: "更多" },
java_code: { name: "Java代码本地生成" },
export_insert_sql: { name: "导出insert-sql" },
export_upsert_sql: { name: "导出upsert-sql" },
export_table_excel: { name: "导出表结构.excel" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "CopyDocument",
name: "数据库表管理",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import DatabaseTableEdit from "./components/DatabaseTableEdit";
import DatabaseTableMore from "./components/DatabaseTableMore";
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
getAsyncRouteName,
} from "@/views/layoutpages/common";
import { useRoute, useRouter } from "vue-router";
const { proxy } = getCurrentInstance();
const route = useRoute();
const router = useRouter();
const rowData = ref(null);
const dialogTitle = ref("");
const showDialog = ref(false);
const showMore = ref(false);
const showJavaCodeDialog = ref(false);
const showDocumentDialog = ref(false);
const showTableColumnDialog = ref(false);
const queryForm = ref(null);
const tableData = ref([]);
const schemaNameList = ref(null);
const serverList = ref(null);
const batchSelectDatabaseSchemaTableList = ref([]);
const params = reactive({
tableName: "",
schemaName: "",
instanceId: "",
initializeToLocal: false,
size: 10,
current: 1,
total: 0,
});
const { tableName, initializeToLocal, size, current, total } = toRefs(params);
/**
* @description: 添加页面路由式
* @param {*}
* @return {*}
*/
const handleEditRoute = async (title, row = null) => {
let path = "acw/components/DatabaseTableEdit";
const toName = await getAsyncRouteName(title, path, "add", {
router,
route,
});
router.push({
name: toName,
query: row,
});
};
/**
* 展示更多
*
* */
const handleShowMore = (title, row = null) => {
console.log(showMore.value);
showMore.value = true;
dialogTitle.value = title;
rowData.value = row;
};
const handleShowMoreDialog = (e) => {
showMore.value = e;
getDataList();
};
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handleDialog = (e) => {
showDialog.value = e;
showJavaCodeDialog.value = e;
showDocumentDialog.value = e;
showTableColumnDialog.value = e;
getDataList();
};
/**
* 全选事件
*
*/
const handleSelectionChange = (val) => {
batchSelectDatabaseSchemaTableList.value = val;
console.log(batchSelectDatabaseSchemaTableList.value);
};
/**
*
* 批量删除
*/
const batchDelTable = () => {
if (batchSelectDatabaseSchemaTableList.value.length == 0) {
proxy.$message({
type: "info",
message: "请选择一个表",
});
return;
}
proxy
.$confirm("此操作将批量永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
let databaseSchemaDeleteCommandList =
batchSelectDatabaseSchemaTableList.value.map((item) => {
return {
instanceId: item.instanceId,
schemaName: item.schemaName,
tableName: item.tableName,
};
});
const { code } = await VE_API.system.batchDeleteTableDelete(
databaseSchemaDeleteCommandList,
);
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**删除行数据
* @description:
* @param {*}
* @return {*}
*/
const handleDel = (row) => {
proxy
.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
const { code } = await VE_API.system.tableDelete({
instanceId: row.instanceId,
schemaName: row.schemaName,
tableName: row.tableName,
});
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
// console.log(params);
const { code, data } = await VE_API.system.tablePage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
}
};
/**
* 获取 数据库
* @param instanceId 数据库实例ID
* @returns {Promise<void>}
*/
const getSchemaList = async (instanceId) => {
let res;
if (instanceId == null) {
return;
} else {
res = await VE_API.system.schemaList({
instanceId,
});
}
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaNameList.value = data;
if (schemaNameList.value.list !== 0) {
params.schemaName = schemaNameList.value[0].schemaName;
}
}
await getDataList();
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then(async (res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
if (serverList.value.length !== 0) {
// 设置默认instance
params.instanceId = serverList.value[0].id;
getSchemaList(params.instanceId);
}
});
};
onMounted(async () => {
await getServerInstanceList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,381 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="数据库实例" property="instanceId">
<el-select
v-model="params.instanceId"
@change="getSchemaList"
placeholder="数据库实例"
filterable
clearable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库名" prop="schemaNameId">
<el-select
clearable
v-model="params.schemaNameId"
placeholder="数据库名"
@change="getDataList"
filterable
>
<el-option
v-for="item in schemaNameList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="表名" prop="tableName">
<el-input
clearable
v-model="tableName"
placeholder="表名"
></el-input>
</el-form-item>
<el-form-item label="填充状态" prop="status">
<el-select
v-model="status"
placeholder="填充状态"
clearable
@change="getDataList"
>
<el-option label="成功" :value="true" />
<el-option label="失败" :value="false" />
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
@selectionChange="handleSelectionChange"
>
<template #tool_bar>
<el-button
v-permission="['add']"
size="small"
type="primary"
@click="handleEdit(buttons.add.name)"
>
{{ buttons.add.name }}
</el-button>
<el-button
v-permission="['batchDel']"
size="small"
type="danger"
@click="batchDelDatabaseTableAutoStuffed"
>
{{ buttons.batchDel.name }}
</el-button>
</template>
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="填充记录ID"> </el-table-column>
<el-table-column
prop="instanceName"
label="数据库实例"
></el-table-column>
<el-table-column
prop="schemaName"
label="数据库库名"
></el-table-column>
<el-table-column prop="tableName" label="表名称"> </el-table-column>
<el-table-column
prop="autoStuffedNum"
label="自动填充数量"
></el-table-column>
<el-table-column prop="status" label="状态">
<template v-slot="{ row }">
<el-select
v-model="row.status"
placeholder="数据填充状态"
disabled
>
<el-option label="成功" :value="true" />
<el-option label="失败" :value="false" />
</el-select>
</template>
</el-table-column>
<el-table-column
prop="createTime"
label="创建时间"
></el-table-column>
<el-table-column
prop="updateTime"
label="更新时间"
></el-table-column>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['edit']"
@click.prevent="handleEdit(buttons.edit.name, row)"
type="primary"
size="small"
>
{{ buttons.edit.name }}
</el-button>
<el-button
v-permission="['del']"
@click.prevent="handleDel(row.id)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!-- 编辑组件 -->
<database-table-auto-stuffed-edit
v-if="showDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showDialog"
@closeDialog="handleDialog($event)"
/>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "数据库表数据自动填充查询与设置",
buttons: {
search: { name: "查询" },
add: { name: "添加自动填充记录" },
edit: { name: "编辑自动填充记录" },
del: { name: "删除自动填充记录" },
batchDel: { name: "批量删除自动填充记录" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "PictureRounded",
name: "数据库表数据自动填充",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import DatabaseTableAutoStuffedEdit from "./components/DatabaseTableAutoStuffedEdit";
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
const { proxy } = getCurrentInstance();
const rowData = ref(null);
const dialogTitle = ref("");
const showDialog = ref(false);
const showJavaCodeDialog = ref(false);
const showDocumentDialog = ref(false);
const showTableColumnDialog = ref(false);
const queryForm = ref(null);
const tableData = ref([]);
const schemaNameList = ref(null);
const serverList = ref(null);
const batchSelectDatabaseTableAutoStuffedList = ref(null);
const params = reactive({
tableName: "",
schemaNameId: "",
instanceId: "",
status: true,
size: 10,
current: 1,
total: 0,
});
const { tableName, status, size, current, total } = toRefs(params);
/**
* 全选事件
*
*/
const handleSelectionChange = (val) => {
batchSelectDatabaseTableAutoStuffedList.value = val;
console.log(batchSelectDatabaseTableAutoStuffedList.value);
};
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showDialog.value = true;
dialogTitle.value = title;
rowData.value = row;
};
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handleDialog = (e) => {
showDialog.value = e;
showJavaCodeDialog.value = e;
showDocumentDialog.value = e;
showTableColumnDialog.value = e;
getDataList();
};
/**
*
* 批量删除
*/
const batchDelDatabaseTableAutoStuffed = () => {
if (batchSelectDatabaseTableAutoStuffedList.value.length == 0) {
proxy.$message({
type: "info",
message: "请选择一个填充记录",
});
return;
}
proxy
.$confirm("此操作将批量永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
let ids = batchSelectDatabaseTableAutoStuffedList.value.map(
(item) => {
return item.id;
},
);
console.log(ids);
const { code } =
await VE_API.system.tableAutoStuffedRecordBatchDelete(ids);
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**删除行数据
* @description:
* @param {*}
* @return {*}
*/
const handleDel = (id) => {
proxy
.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
const { code } = await VE_API.system.tableAutoStuffedRecordDelete({
id,
});
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } =
await VE_API.system.tableAutoStuffedRecordPage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
}
};
/**
* 获取 数据库
* @param instanceId 数据库实例ID
* @returns {Promise<void>}
*/
const getSchemaList = async (instanceId) => {
let res;
if (instanceId == null) {
res = await VE_API.system.schemaList();
} else {
res = await VE_API.system.schemaList({
instanceId,
});
}
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaNameList.value = data;
}
await getDataList();
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
};
onMounted(async () => {
await getServerInstanceList();
await getDataList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,210 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="项目名称" prop="projectName">
<el-input
clearable
v-model="projectName"
placeholder="项目名称"
></el-input>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
>
<template #tool_bar>
<el-button
v-permission="['add']"
size="small"
type="primary"
@click="handleEdit(buttons.add.name)"
>
{{ buttons.add.name }}
</el-button>
</template>
<el-table-column
prop="projectName"
label="项目名称"
></el-table-column>
<el-table-column
prop="instanceName"
label="数据库实例"
></el-table-column>
<el-table-column prop="version" label="version"></el-table-column>
<el-table-column
prop="ormFrameEnums"
label="ORM框架"
></el-table-column>
<el-table-column
prop="uiFrameEnums"
label="UI框架"
></el-table-column>
<el-table-column prop="owner" label="项目拥有者"></el-table-column>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['edit']"
@click.prevent="handleEdit(buttons.edit.name, row)"
type="primary"
size="small"
>
{{ buttons.edit.name }}
</el-button>
<el-button
v-permission="['del']"
@click.prevent="handleDel(row.id)"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!-- 编辑组件 -->
<project-edit
v-if="showDialog"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showDialog"
@closeDialog="handelDialog($event)"
/>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "acw项目查询与设置",
buttons: {
search: { name: "查询" },
add: { name: "添加" },
edit: { name: "编辑" },
del: { name: "删除" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Mug",
name: "ACW项目管理",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import ProjectEdit from "./components/ProjectEdit";
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
const { proxy } = getCurrentInstance();
const rowData = ref(null);
const dialogTitle = ref("");
const showDialog = ref(false);
const queryForm = ref(null);
const tableData = ref([]);
const params = reactive({
projectName: "",
size: 10,
current: 1,
total: 0,
});
const { projectName, size, current, total } = toRefs(params);
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showDialog.value = true;
dialogTitle.value = title;
rowData.value = row;
};
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handelDialog = (e) => {
showDialog.value = e;
getDataList();
};
/**删除行数据
* @description:
* @param {*}
* @return {*}
*/
const handleDel = (id) => {
proxy
.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
const { code } = await VE_API.system.projectDelete({ id });
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } = await VE_API.system.projectPage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
}
};
onMounted(async () => {
await getDataList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,643 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="服务器名称" prop="instanceName">
<el-input
clearable
v-model="instanceName"
placeholder="服务器名称"
></el-input>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<el-button
v-permission="['addInstance']"
size="small"
type="primary"
@click="handleEdit(buttons.addInstance.name)"
>
{{ buttons.addInstance.name }}
</el-button>
<el-row v-if="tableData && tableData.length > 0">
<el-col span="8" style="margin-right: 10%">
<el-tree
:props="resourceProps"
:load="loadNode"
:data="tableData"
@node-click="checkNode"
lazy
show-checkbox
:filter-node-method="filterNode"
>
<template #default="{ data }">
<span class="custom-tree-node">
<img
v-if="data.type === 'instance'"
src="../../../../src/static/redis.png"
style="width: 16px; height: 16px"
/>
<img
v-if="data.type === 'database'"
src="../../../../src/static/database.png"
style="width: 16px; height: 16px"
/>
<img
v-if="data.type === 'key'"
src="../../../../src/static/key.png"
style="width: 16px; height: 16px"
/>
<!-- 实例展示 -->
<span v-if="data.type === 'instance'">
{{ data.name }}
</span>
<el-button
v-if="data.type === 'instance'"
v-permission="['editInstance']"
size="small"
type="primary"
@click="
handleEdit(buttons.editInstance.name, data)
"
>
{{ buttons.editInstance.name }}
</el-button>
<el-button
v-if="data.type === 'instance'"
v-permission="['delInstance']"
size="small"
type="primary"
@click="handleDel(data.id)"
>
{{ buttons.delInstance.name }}
</el-button>
<!-- 数据库展示 -->
<span v-if="data.type === 'database'">
{{ data.name }} ({{ data.keysNum }})
</span>
<!-- key展示 -->
<span v-if="data.type === 'key'">
{{ data.name }}
</span>
<el-button
v-if="data.type === 'key'"
v-permission="['delKey']"
@click.prevent="handleDelKey(data)"
type="danger"
size="small"
>
{{ buttons.delKey.name }}
</el-button>
</span>
</template>
</el-tree>
</el-col>
<el-col
span="16"
v-if="
currentClickRowData &&
(currentClickRowData.type === 'database' ||
currentClickRowData.type === 'key')
"
style="position: absolute; right: 20px"
>
<!-- 搜索key -->
<!-- <el-form ref="queryForm" :inline="true" :model="params">-->
<!-- <el-form-item label="key" prop="key">-->
<!-- <el-input-->
<!-- clearable-->
<!-- v-model="instanceName"-->
<!-- placeholder="key"-->
<!-- ></el-input>-->
<!-- </el-form-item>-->
<!-- <el-form-item>-->
<!-- <el-button-->
<!-- type="primary"-->
<!-- @click="onSubmit(params, getDataList)"-->
<!-- >-->
<!-- {{ buttons.search.name }}-->
<!-- </el-button>-->
<!-- <el-button-->
<!-- @click="resetForm(queryForm, params, getDataList)"-->
<!-- >-->
<!-- 重置-->
<!-- </el-button>-->
<!-- </el-form-item>-->
<!-- </el-form>-->
<!-- 展示database 下多有的keys -->
<ve-table
:table="{
data: currentShowData.redisInstanceConsoleFindKeysValues,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(
val,
params,
findKeysValues(currentClickRowData.value),
),
onCurrentChange: (val) =>
handleCurrentChange(
val,
params,
findKeysValues(currentClickRowData.value),
),
currentPage: current,
pageSize: size,
total: total,
}"
>
<template #tool_bar>
<el-button
v-permission="['addKey']"
size="small"
type="primary"
@click="handleKeyEdit({})"
>
{{ buttons.addKey.name }}
</el-button>
</template>
<el-table-column prop="key" label="key"></el-table-column>
<el-table-column
prop="value"
label="value"
></el-table-column>
<el-table-column fixed="right" label="操作">
<template v-slot:default="{ row }">
<el-button
v-permission="['editKey']"
@click.prevent="handleKeyEdit(row)"
type="primary"
size="small"
>
{{ buttons.editKey.name }}
</el-button>
<el-button
v-permission="['delKey']"
@click.prevent="handleDelKey(row)"
type="danger"
size="small"
>
{{ buttons.delKey.name }}
</el-button>
</template>
</el-table-column>
</ve-table>
<!-- 展示database 的keys结束 -->
</el-col>
</el-row>
<el-empty v-show="tableData.length === 0" />
<!-- 列表 -->
<!-- <ve-table-->
<!-- :table="{-->
<!-- data: tableData,-->
<!-- }"-->
<!-- :pagination="{-->
<!-- onSizeChange: (val) =>-->
<!-- handleSizeChange(val, params, getDataList),-->
<!-- onCurrentChange: (val) =>-->
<!-- handleCurrentChange(val, params, getDataList),-->
<!-- currentPage: current,-->
<!-- pageSize: size,-->
<!-- total: total,-->
<!-- }"-->
<!-- >-->
<!-- <template #tool_bar>-->
<!-- <el-button-->
<!-- v-permission="['add']"-->
<!-- size="small"-->
<!-- type="primary"-->
<!-- @click="handleEdit(buttons.add.name)"-->
<!-- >-->
<!-- {{ buttons.add.name }}-->
<!-- </el-button>-->
<!-- </template>-->
<!-- <el-table-column-->
<!-- prop="instanceName"-->
<!-- label="服务器名称"-->
<!-- ></el-table-column>-->
<!-- <el-table-column prop="username" label="登录名"></el-table-column>-->
<!-- <el-table-column prop="password" label="登陆密码">-->
<!-- <template v-slot="{ row }">-->
<!-- <span>-->
<!-- {{-->
<!-- row.password &&-->
<!-- row.password-->
<!-- .split("")-->
<!-- .fill("*", 1, -1)-->
<!-- .join()-->
<!-- .replace(/\,/g, "")-->
<!-- }}-->
<!-- </span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column prop="host" label="host"></el-table-column>-->
<!-- <el-table-column prop="port" label="端口"></el-table-column>-->
<!-- <el-table-column prop="status" label="状态"></el-table-column>-->
<!-- <el-table-column prop="sort" label="排序"></el-table-column>-->
<!-- <el-table-column fixed="right" label="操作">-->
<!-- <template v-slot:default="{ row }">-->
<!-- <el-button-->
<!-- v-permission="['edit']"-->
<!-- @click.prevent="handleEdit(buttons.edit.name, row)"-->
<!-- type="primary"-->
<!-- size="small"-->
<!-- >-->
<!-- {{ buttons.edit.name }}-->
<!-- </el-button>-->
<!-- <el-button-->
<!-- v-permission="['del']"-->
<!-- @click.prevent="handleDel(row.id)"-->
<!-- type="danger"-->
<!-- size="small"-->
<!-- >-->
<!-- {{ buttons.del.name }}-->
<!-- </el-button>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- </ve-table>-->
<!-- 编辑实例组件 -->
<redis-instance-edit
v-if="showDialog.instanceEdit"
:rowData="rowData"
:title="dialogTitle"
:showDialog="showDialog.instanceEdit"
@closeDialog="handelDialog($event)"
/>
<!-- 编辑key -->
<el-dialog
:title="新增或者修改key"
append-to-body
destroy-on-close
:model-value="showDialog.keyEdit"
@close="handelKeyDialog($event)"
>
<!-- 表单 -->
<el-form
:model="redisKeyData"
ref="formRef"
label-width="120px"
:inline="false"
>
<el-form-item label="数据库的key" prop="redisKeyData.key">
<el-input
v-model="redisKeyData.key"
placeholder=""
clearable
></el-input>
</el-form-item>
<el-form-item label="value" prop="redisKeyData.value">
<el-input
v-model="redisKeyData.value"
placeholder=""
clearable
></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="handelKeyDialog(false)">取消</el-button>
<el-button type="primary" @click="handelAddKey()"
>确定</el-button
>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "Redis实例管理",
buttons: {
search: { name: "查询" },
addInstance: { name: "添加Redis实例" },
addKey: { name: "添加Redis key" },
add: { name: "添加实例" },
editInstance: { name: "编辑redis实例" },
editKey: { name: "编辑Redis key" },
delKey: { name: "删除Redis key" },
delInstance: { name: "删除Redis实例" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Collection",
name: "Redis实例管理",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import redisInstanceEdit from "./components/RedisInstanceEdit.vue";
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
const { proxy } = getCurrentInstance();
const rowData = ref(null);
const dialogTitle = ref("");
const showDialog = ref({});
const queryForm = ref(null);
const tableData = ref([]);
const redisKeyData = ref({});
// 当前node节点对应的数据
const currentClickRowData = ref({});
const currentShowData = ref({}); // 当前展示的所有数据都在这里
const params = reactive({
instanceName: "",
size: 10,
current: 1,
total: 0,
});
const { instanceName, size, current, total } = toRefs(params);
/**
* 左侧服务器树
* @type {Ref<UnwrapRef<*[]>>}
*/
const leftServerTree = ref([]);
/**
* @description:添加or编辑事件
* @param {*}
* @return {*}
*/
const handleEdit = (title, row = null) => {
showDialog.value.instanceEdit = true;
dialogTitle.value = title;
rowData.value = row;
};
/**
* 编辑key
*/
const handleKeyEdit = (row) => {
showDialog.value.keyEdit = true;
redisKeyData.value = row;
};
/**
* 删除key
*/
const handleDelKey = (row) => {
VE_API.system.redisInstanceConsoleRemoveKey(row);
};
/**
* @description: dialog事件
* @param {*}
* @return {*}
*/
const handelDialog = (e) => {
showDialog.value.instanceEdit = e;
getDataList();
};
/**
*
* @param e
*/
const handelKeyDialog = (e) => {
showDialog.value.keyEdit = e;
getDataList();
};
/**
* 添加key
*/
const handelAddKey = async () => {
redisKeyData.value.instanceId = currentClickRowData.value.instanceId;
redisKeyData.value.database = currentClickRowData.value.database;
const { code } = await VE_API.system.redisInstanceConsoleSetKey(
redisKeyData.value,
);
if (code === 0) {
handelKeyDialog(false);
}
};
/**删除行数据
* @description:
* @param {*}
* @return {*}
*/
const handleDel = (id) => {
proxy
.$confirm("此操作将永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
const { code } = await VE_API.system.redisInstanceDelete({ id });
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } = await VE_API.system.redisInstancePage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
record.map((item) => {
item.label = item.instanceName;
item.name = item.instanceName;
item.value = item.id;
item.instanceId = item.id;
});
tableData.value = record;
leftServerTree.value = record;
}
};
onMounted(async () => {
await getDataList();
// maxHeight(pagination, queryForm, toolBar, ve_max_height);
});
const resourceProps = {
label: "name",
children: "children",
isLeaf: "leaf",
};
const loadNode = async (resourceNode, resolve) => {
console.log(resourceNode);
if (resourceNode.level === 0) {
return resolve([]);
}
resourceNode.loaded = true;
const resourceNodeData = resourceNode.data;
currentClickRowData.value = resourceNodeData.data;
if (resourceNodeData.isFile) {
return resolve([]);
}
console.log(resourceNodeData);
let res;
if (resourceNodeData) {
if (resourceNodeData && resourceNodeData.type === "instance") {
// 查询database
res = await VE_API.system.redisInstanceConsoleFindDataBases({
instanceId: resourceNodeData.id,
});
if (res.code === 0) {
res.data.record.map((item) => {
item.name = item.database;
});
}
} else if (resourceNodeData.type === "database") {
// 查询key
res = await VE_API.system.redisInstanceConsoleFindKeys({
instanceId: resourceNodeData.instanceId,
database: resourceNodeData.database,
});
if (res.code === 0) {
res.data.record.map((item) => {
item.name = item.key;
});
}
} else if (resourceNodeData.type === "key") {
// 查询数据
}
if (res) {
const { code, data } = res;
if (code === 0) {
if (data == null) {
return resolve([]);
}
// resourceNode.childNodes = data;
console.log(resourceNode);
return resolve(data.record);
}
} else {
return resolve([]);
}
}
};
/**
* 点击 node节点
*/
const checkNode = (node) => {
currentClickRowData.value = node;
if (currentClickRowData.value.type === "database") {
findKeysValues(currentClickRowData.value);
}
findKeyValue(currentClickRowData.value);
console.log(currentClickRowData.value);
};
/**
* 过滤node
* @param node
*/
const filterNode = (value, tree) => {
if (!value) return true;
return tree.label.includes(value);
};
/**
* 获取所有的keys
* @param currentClickRowData
* @returns {Promise<void>}
*/
const findKeysValues = async (currentClickRowData) => {
if (currentClickRowData.type === "database") {
const res =
await VE_API.system.redisInstanceConsoleFindKeysValues(
currentClickRowData,
);
const { code, data } = res;
if (code === 0) {
currentShowData.value.redisInstanceConsoleFindKeysValues =
data.record;
console.log(currentShowData.value);
} else {
console.log(res);
}
}
};
/**
* 获取key对应的value
* @param currentClickRowData
* @returns {Promise<void>}
*/
const findKeyValue = async (currentClickRowData) => {
if (currentClickRowData.type === "key") {
const res =
await VE_API.system.redisInstanceConsoleFindKeyValue(
currentClickRowData,
);
const { code, data } = res;
if (code === 0) {
currentShowData.value.redisInstanceConsoleFindKeysValues =
data.record;
console.log(currentShowData.value);
} else {
console.log(res);
}
}
};
/**
* 初始化加载数据
*/
onMounted(async () => {
await getDataList();
});
</script>
<style lang="scss" scoped>
.prefix {
color: var(--el-color-primary);
margin-right: 10px;
}
.prefix.is-leaf {
color: var(--el-color-success);
}
</style>

View File

@@ -0,0 +1,327 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form ref="queryForm" :inline="true" :model="params">
<el-form-item label="数据库实例" property="instanceId">
<el-select
v-model="params.instanceId"
@change="getSchemaList"
placeholder="数据库实例"
filterable
clearable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库名" prop="schemaNameId">
<el-select
clearable
v-model="params.schemaNameId"
placeholder="数据库名"
@change="getDataList"
>
<el-option
v-for="item in schemaNameList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="表名" prop="tableName">
<el-input
clearable
v-model="tableName"
placeholder="表名"
></el-input>
</el-form-item>
<el-form-item label="sql类型" prop="sqlType">
<el-select
v-model="sqlType"
placeholder="sql类型"
@change="getDataList"
>
<el-option label="查询" value="SELECT" />
<el-option label="更新" value="UPDATE" />
<el-option label="插入" value="INSERT" />
<el-option label="删除" value="DELETE" />
<el-option label="创建" value="CREATE" />
<el-option label="DDL" value="DDL" />
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="onSubmit(params, getDataList)"
>
{{ buttons.search.name }}
</el-button>
<el-button @click="resetForm(queryForm, params, getDataList)">
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
@selectionChange="handleSelectionChange"
>
<template #tool_bar>
<el-button
v-permission="['batchDel']"
size="small"
type="danger"
@click="batchDelSqlAudit"
>
{{ buttons.batchDel.name }}
</el-button>
<el-button
v-permission="['batchExport']"
size="small"
type="primary"
@click="batchExportSqlAudit"
>
{{ buttons.batchExport.name }}
</el-button>
</template>
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="sql执行ID" show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="applicationName"
label="应用名称"
></el-table-column>
<el-table-column prop="instanceId" label="实例ID">
<template v-slot="scope">
<el-select
v-model="scope.row.instanceId"
placeholder="实例ID"
filterable
:disabled="true"
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="sqlType" label="执行类型"></el-table-column>
<el-table-column prop="schema" label="数据库"> </el-table-column>
<el-table-column prop="ip" label="ip"></el-table-column>
<el-table-column
prop="tableList"
label="操作的表"
></el-table-column>
<el-table-column
prop="executeSql"
label="执行sql"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="requestId"
label="请求ID"
show-overflow-tooltip
></el-table-column>
<el-table-column
prop="createTime"
label="创建时间"
></el-table-column>
<el-table-column
prop="updateTime"
label="更新时间"
></el-table-column>
</ve-table>
</div>
</template>
<script>
import acwMenu from "@/views/layoutpages/acw/AcwMenu";
export default {
data: () => ({
description: "SQL审计",
buttons: {
search: { name: "查询" },
batchDel: { name: "批量删除SQL审计" },
batchExport: { name: "批量导出" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "DataLine",
name: "SQL审计",
parentMenu: acwMenu,
}),
};
</script>
<script setup>
import { reactive, toRefs, ref, onMounted, getCurrentInstance } from "vue";
//?导入公共查询方法
import {
onSubmit,
resetForm,
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
const { proxy } = getCurrentInstance();
const queryForm = ref(null);
const tableData = ref([]);
const schemaNameList = ref(null);
const serverList = ref(null);
const batchSelectSQLAuditList = ref(null);
const params = reactive({
tableName: "",
schemaNameId: "",
instanceId: "",
status: true,
sqlType: "",
size: 10,
current: 1,
total: 0,
});
const { tableName, size, sqlType, current, total } = toRefs(params);
/**
* 全选事件
*
*/
const handleSelectionChange = (val) => {
batchSelectSQLAuditList.value = val;
console.log(batchSelectSQLAuditList.value);
};
/**
* 批量导出
*/
const batchExportSqlAudit = async () => {
let res = await VE_API.system.slqAuditExport(params, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
};
/**
*
* 批量删除
*/
const batchDelSqlAudit = () => {
if (batchSelectSQLAuditList.value.length == 0) {
proxy.$message({
type: "info",
message: "请选择一个SQL记录",
});
return;
}
proxy
.$confirm("此操作将批量永久删除该数据, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
let ids = batchSelectSQLAuditList.value.map((item) => {
return item.id;
});
const { code } = await VE_API.system.batchDeleteSlqAudit(ids);
if (code === 0) {
getDataList();
}
})
.catch(() => {
proxy.$message({
type: "info",
message: "已取消删除",
});
});
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getDataList = async () => {
const { code, data } = await VE_API.system.slqAuditPage(params);
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
}
};
/**
* 获取 数据库
* @param instanceId 数据库实例ID
* @returns {Promise<void>}
*/
const getSchemaList = async (instanceId) => {
schemaNameList.value = [];
let res;
if (instanceId == null || instanceId === "") {
return;
} else {
res = await VE_API.system.schemaList({
instanceId,
});
}
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaNameList.value = data;
}
await getDataList();
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
};
onMounted(async () => {
await getServerInstanceList();
await getDataList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,239 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="instanceId"
placeholder="数据库实例"
filterable
@change="(val) => getSchemaList(val)"
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库库名" prop="schemaName">
<el-select
v-model="schema"
filterable
placeholder="数据库实例"
@change="findInstanceSchemaColumnList"
>
<el-option
v-for="item in schemaList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="忽略解析字段" prop="ignoreFieldList">
<el-select
v-model="ignoreFieldList"
filterable
multiple
placeholder="忽略解析字段"
>
<el-option
v-for="item in tableColumnList"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span style="float: left"
>{{ item.label }}{{ item.columnComment }}</span
>
<span
style="
float: right;
color: var(--el-text-color-secondary);
font-size: 13px;
"
>{{ item.tableName }}</span
></el-option
>
</el-select>
</el-form-item>
<el-form-item label="解析阀值" prop="relationThreshold">
<el-input
v-model="relationThreshold"
placeholder="500"
clearable
></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
onMounted,
ref,
defineProps,
defineEmits,
} from "vue";
const rules = {
databaseSchemaId: [
{
required: true,
message: "请输入数据库库名",
trigger: "blur",
},
],
instanceId: [
{
required: true,
message: "请输入数据库服务器",
trigger: "blur",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
schema: "",
instanceId: "",
relationThreshold: 99,
ignoreFieldList: [],
});
const { schema, instanceId, relationThreshold, ignoreFieldList } = toRefs(form);
const serverList = ref(null);
const schemaList = ref(null);
const tableColumnList = ref([]);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((schema.value = rowData.value.schema),
(instanceId.value = rowData.value.instanceId),
(relationThreshold.value = rowData.value.relationThreshold));
onMounted(async () => {
getInstanceList();
});
/**
* 获取当前表对应的字段
* @returns {Promise<void>}
*/
const findInstanceSchemaColumnList = async () => {
// 设置表备注
tableColumnList.value = [];
let res = await VE_API.system.findInstanceSchemaColumnList({
instanceId: instanceId.value,
schemaName: schema.value,
});
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.columnName;
item.value = item.columnName;
});
tableColumnList.value = data ? data : [];
}
};
/**
* 获取数据库实例
*/
const getInstanceList = () => {
// 查询数据库实例
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
};
const getSchemaList = (databaseInstance = null) => {
console.log(databaseInstance);
// 查询数据schema信息
VE_API.system
.schemaList({
instanceId: instanceId.value,
})
.then((res) => {
res.data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaList.value = res.data ? res.data : [];
});
};
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
console.log(form);
res =
await VE_API.system.acwTableAssociationRelationAnalysisSchema(
form,
);
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,358 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="120px"
:inline="false"
>
<el-form-item label="数据库实例ID" prop="instanceId">
<el-select
v-model="instanceId"
@change="getSchemaList"
placeholder="数据库实例"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库实例schema" prop="schema">
<el-select
v-model="schema"
placeholder="数据库名"
@change="getTableList"
filterable
>
<el-option
v-for="item in schemaList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="原始表" prop="sourceTable">
<el-select
v-model="sourceTable"
placeholder="数据库表名"
@change="getDatabaseTablesColumnList(sourceTable)"
filterable
>
<el-option
v-for="item in tableList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="原始表字段" prop="sourceColumn">
<el-select
v-model="sourceColumn"
placeholder="原始表字段"
filterable
>
<el-option
v-for="item in tableColumnList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="关系表" prop="relationTable">
<el-select
v-model="relationTable"
placeholder="关系表"
@change="getDatabaseTablesColumnList(relationTable)"
filterable
>
<el-option
v-for="item in tableList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="关系表字段" prop="relationTableColumn">
<el-select
v-model="relationTableColumn"
placeholder="关系表字段"
filterable
>
<el-option
v-for="item in tableColumnList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="类型(1:自动产生的、2:手动添加的)" prop="type">
<el-select
v-model="type"
placeholder="类型(1:自动产生的、2:手动添加的)"
clearable
>
<el-option label="自动产生的" :value="1" />
<el-option label="手动添加的" :value="2" />
</el-select>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
ref,
defineProps,
defineEmits,
onMounted,
} from "vue";
const rules = {
instanceId: [
{
required: true,
message: "请输入服务器名称",
trigger: "blur",
},
],
schema: [
{
required: true,
message: "请输入账户",
trigger: "blur",
},
],
sourceTable: [
{
required: true,
message: "请输入密码",
trigger: "blur",
},
],
sourceColumn: [
{
required: true,
message: "host不能为空",
trigger: "change",
},
],
relationTable: [
{
required: true,
message: "端口不能为空",
trigger: "change",
},
],
relationTableColumn: [
{
required: true,
message: "driverClassName",
trigger: "change",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
instanceId: "",
schema: "",
sourceTable: "",
sourceColumn: "",
relationTable: "",
relationTableColumn: "",
type: 2,
});
const {
instanceId,
schema,
sourceTable,
sourceColumn,
relationTable,
relationTableColumn,
type,
} = toRefs(form);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((instanceId.value = rowData.value.instanceId),
(schema.value = rowData.value.schema),
(sourceTable.value = rowData.value.sourceTable),
(sourceColumn.value = rowData.value.sourceColumn),
(relationTable.value = rowData.value.relationTable),
(relationTableColumn.value = rowData.value.relationTableColumn),
(type.value = rowData.value.type));
const schemaList = ref(null);
const tableList = ref(null);
const tableColumnList = ref([]);
const serverList = ref(null);
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
};
/**
* 选择数据库实例
* @param serverInstanceId
* @returns {Promise<void>}
*/
const getSchemaList = async () => {
// 查询数据库
let res = await VE_API.system.schemaList({
instanceId: instanceId.value,
});
const { code } = res;
if (code === 0) {
if (res.data) {
res.data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaList.value = res.data ? res.data : [];
}
}
};
/**
* 获取 数据库表
* @param instanceId 数据库实例ID
* @param schemaName 数据库名称
* @returns {Promise<void>}
*/
const getTableList = async () => {
let res;
if (instanceId.value == null) {
return;
} else {
res = await VE_API.system.tableList({
instanceId: instanceId.value,
schemaName: schema.value,
});
}
const { code, data } = res;
if (code === 0) {
if (data) {
data.map((item) => {
item.label = item.tableName;
item.value = item.tableName;
});
tableList.value = data;
}
}
};
/**
* 获取当前表对应的字段
* @param tableIds
* @returns {Promise<void>}
*/
const getDatabaseTablesColumnList = async (table) => {
// 设置表备注
tableColumnList.value = [];
let res = await VE_API.system.findDatabaseTableColumnList({
instanceId: instanceId.value,
schemaName: schema.value,
tableName: table,
});
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.columnName;
item.value = item.columnName;
});
tableColumnList.value = data ? data : [];
}
};
/**
* 页面初始化方法
*/
onMounted(async () => {
await getServerInstanceList();
});
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value === "添加") {
res =
await VE_API.system.acwTableAssociationRelationStory(form);
} else {
res = await VE_API.system.acwTableAssociationRelationStory({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,590 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
:width="'80%'"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<el-form-item label="应用名称" prop="applicationId">
<el-select
v-model="applicationId"
placeholder="应用名称"
@change="changeApplication"
clearable
>
<el-option
v-for="item in applicationList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="应用表" prop="tableNameList">
<el-select
v-model="tableNameList"
placeholder="应用表"
@change="changeTable"
multiple
clearable
>
<el-option
v-for="item in tableList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="接口分组" prop="tag">
<el-input v-model="tag" placeholder="" clearable></el-input>
</el-form-item>
<el-form-item label="接口描述" prop="description">
<el-input
v-model="description"
placeholder=""
clearable
></el-input>
</el-form-item>
<el-form-item label="接口类型" prop="method">
<el-select v-model="method" placeholder="" clearable>
<el-option label="GET" value="GET" />
<el-option label="HEAD" value="HEAD" />
<el-option label="POST" value="POST" />
<el-option label="PUT" value="PUT" />
<el-option label="PATCH" value="PATCH" />
<el-option label="DELETE" value="DELETE" />
<el-option label="OPTIONS" value="OPTIONS" />
<el-option label="TRACE" value="TRACE" />
</el-select>
</el-form-item>
<el-form-item label="接口地址" prop="path">
<el-input v-model="path" placeholder="" clearable></el-input>
</el-form-item>
<div style="text-align: center" class="api_transfer_result">
<el-transfer
v-model="apiFactoryTransferData.leftValue"
style="text-align: left; display: inline-block"
filterable
:left-default-checked="[2, 3]"
:right-default-checked="[1]"
:titles="['原始数据', '接口返回数据']"
:button-texts="['移除返回数据', '添加返回数据']"
:data="tableColumnList"
:props="{ key: 'value', label: 'desc' }"
:format="{
noChecked: '${total}',
hasChecked: '${checked}/${total}',
}"
@change="handleTransferChange"
>
<template #default="{ option }">
<span style="float: left"
>{{ option.label }} {{ option.columnComment }}
</span>
<span style="float: right">
{{ option.tableName }}
</span>
</template>
<template #left-footer>
<el-button class="transfer-footer" size="small"
>Operation</el-button
>
</template>
<template #right-footer>
<el-button class="transfer-footer" size="small"
>Operation</el-button
>
</template>
</el-transfer>
</div>
<div>
<span>路径参数</span>
<el-button
@click="addItem(pathParamColumnIdList, 'PATH_PARAM_TYPE')"
type="primary"
style="float: right"
>增加
</el-button>
<el-empty v-show="pathParamColumnIdList.length === 0">
</el-empty>
<el-form-item
v-for="(it, index) in pathParamColumnIdList"
:key="index"
>
<el-select
v-model="pathParamColumnIdList[index].columnName"
placeholder="路径参数选择"
>
<el-option
v-for="item in tableColumnList"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span style="float: left"
>{{ item.label }}{{
item.columnComment
}}</span
>
<span
style="
float: right;
color: var(--el-text-color-secondary);
font-size: 13px;
"
>{{ item.tableName }}</span
></el-option
>
</el-select>
<el-select
v-model="pathParamColumnIdList[index].term"
placeholder="条件(大于、等于、模糊)"
>
<el-option label="小等于" value="<" />
<el-option label="等于" value="=" />
<el-option label="大于等于" value=">=" />
<el-option label="小于等于" value="<=" />
<el-option label="不等于" value="!=" />
<el-option label="模糊查询" value="like" />
<el-option label="in" value="in" />
</el-select>
<el-button
@click="removeItem(pathParamColumnIdList, index)"
type="danger"
style="float: right"
>删除
</el-button>
</el-form-item>
</div>
<div>
<span>请求参数</span>
<el-button
@click="
addItem(requestParamColumnIdList, 'REQUEST_PARAM_TYPE')
"
style="float: right"
type="primary"
>增加
</el-button>
<el-empty
v-show="requestParamColumnIdList.length === 0"
></el-empty>
<el-form-item
v-for="(it, index) in requestParamColumnIdList"
:key="index"
>
<el-select
v-model="requestParamColumnIdList[index].columnName"
placeholder="请求参数选择"
>
<el-option
v-for="item in tableColumnList"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span style="float: left"
>{{ item.label }}{{
item.columnComment
}}</span
>
<span
style="
float: right;
color: var(--el-text-color-secondary);
font-size: 13px;
"
>{{ item.tableName }}</span
></el-option
>
</el-select>
<el-select
v-model="requestParamColumnIdList[index].term"
placeholder="条件(大于、等于、模糊)"
>
<el-option label="小等于" value="<" />
<el-option label="等于" value="=" />
<el-option label="大于等于" value=">=" />
<el-option label="小于等于" value="<=" />
<el-option label="不等于" value="!=" />
<el-option label="模糊查询" value="like" />
<el-option label="in" value="in" />
</el-select>
<el-button
@click="removeItem(requestParamColumnIdList, index)"
style="float: right"
type="danger"
>删除
</el-button>
</el-form-item>
</div>
<div>
<span>请求体参数</span>
<el-button
@click="
addItem(
requestBodyParamColumnIdList,
'REQUEST_BODY_PARAM_TYPE',
)
"
style="float: right"
type="primary"
>增加
</el-button>
<el-empty
v-show="requestBodyParamColumnIdList.length === 0"
></el-empty>
<el-form-item
v-for="(it, index) in requestBodyParamColumnIdList"
:key="index"
>
<el-select
v-model="requestBodyParamColumnIdList[index].columnName"
placeholder="请选择"
>
<el-option
v-for="item in tableColumnList"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span style="float: left"
>{{ item.label }}{{
item.columnComment
}}</span
>
<span
style="
float: right;
color: var(--el-text-color-secondary);
font-size: 13px;
"
>{{ item.tableName }}</span
></el-option
>
</el-select>
<el-select
v-model="requestBodyParamColumnIdList[index].term"
placeholder="条件(大于、等于、模糊)"
>
<el-option label="大于" value=">" />
<el-option label="小等于" value="<" />
<el-option label="等于" value="=" />
<el-option label="大于等于" value=">=" />
<el-option label="小于等于" value="<=" />
<el-option label="不等于" value="!=" />
<el-option label="模糊查询" value="like" />
<el-option label="in" value="in" />
</el-select>
<el-button
@click="removeItem(requestBodyParamColumnIdList, index)"
type="danger"
style="float: right"
>删除
</el-button>
</el-form-item>
</div>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
ref,
defineProps,
defineEmits,
onMounted,
} from "vue";
const rules = {
applicationId: [
{
required: true,
message: "请选择应用名称",
trigger: "blur",
},
],
tableNameList: [
{
required: true,
message: "请选择表",
trigger: "blur",
},
],
tag: [
{
required: true,
message: "请输入接口分组",
trigger: "blur",
},
],
description: [
{
required: true,
message: "请输入接口描述",
trigger: "change",
},
],
method: [
{
required: true,
message: "请选择接口类型",
trigger: "change",
},
],
path: [
{
required: true,
message: "请选择接口地址",
trigger: "change",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const applicationList = ref(null);
const tableList = ref(null);
const tableColumnList = ref([]);
// api 工厂穿梭数据
const apiFactoryTransferData = ref({
leftValue: [],
});
const requestBodyParamColumnIdList = ref([]);
const pathParamColumnIdList = ref([]);
const requestParamColumnIdList = ref([]);
const application = ref();
const form = reactive({
applicationId: "",
tableNameList: [],
tag: "",
description: "",
method: "",
path: "",
apiParamQoList: [],
});
const {
applicationId,
tableNameList,
tag,
description,
method,
path,
apiParamQoList,
} = toRefs(form);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((applicationId.value = rowData.value.applicationId),
(tag.value = rowData.value.tag),
(description.value = rowData.value.description),
(method.value = rowData.value.method),
(path.value = rowData.value.path),
(apiParamQoList.value = rowData.value.apiParamQoList),
(tableNameList.value = rowData.value.tableNameList));
const handleTransferChange = (transferItem) => {
console.log(transferItem);
};
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
let apiParamQoList = [];
apiParamQoList = apiParamQoList.concat(pathParamColumnIdList.value);
apiParamQoList = apiParamQoList.concat(requestBodyParamColumnIdList.value);
apiParamQoList = apiParamQoList.concat(requestParamColumnIdList.value);
apiParamQoList = JSON.parse(JSON.stringify(apiParamQoList));
form.apiParamQoList = apiParamQoList;
form.apiResultList = apiFactoryTransferData.value.leftValue;
console.log(apiFactoryTransferData);
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value === "添加") {
res = await VE_API.system.apiAdd(form);
} else {
res = await VE_API.system.apiEdit({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
onMounted(async () => {
VE_API.system.applicationList().then((res) => {
res.data.map((item) => {
item.label = item.applicationName;
item.value = item.applicationId;
});
applicationList.value = res.data ? res.data : [];
let application = res.data.find(
(item) => item.applicationId === applicationId.value,
);
if (application) {
findTable(application.applicationId);
findTableColumn(form.tableNameList);
}
});
if (apiParamQoList.value !== null && apiParamQoList.value.length > 0) {
pathParamColumnIdList.value = apiParamQoList.value.filter(
(item) => item.type === "PATH_PARAM_TYPE",
);
requestBodyParamColumnIdList.value = apiParamQoList.value.filter(
(item) => item.type === "REQUEST_BODY_PARAM_TYPE",
);
requestParamColumnIdList.value = apiParamQoList.value.filter(
(item) => item.type === "REQUEST_PARAM_TYPE",
);
}
// 选择字段列
await findTableColumn(tableNameList);
});
const changeApplication = (applicationId) => {
let applicationOne = applicationList.value.find(
(item) => item.applicationId === applicationId,
);
application.value = applicationOne;
console.log(applicationOne);
findTable(applicationOne.applicationId);
};
const changeTable = (tableNameList) => {
if (tableNameList == null || tableNameList.length === 0) {
return;
}
findTableColumn(tableNameList);
};
const findTable = async (applicationId) => {
if (null == applicationId) return;
VE_API.system
.applicationFindTables({ applicationId: applicationId })
.then((res) => {
res.data.map((item) => {
item.label = item.tableName;
item.value = item.tableName;
});
tableList.value = res.data ? res.data : [];
});
};
const findTableColumn = async (tableNameList) => {
console.log(tableNameList);
console.log(application.value);
if (
tableNameList === undefined ||
tableNameList.length === 0 ||
tableNameList.length === undefined ||
application.value === undefined
) {
return;
}
let tableNames = tableNameList.join();
VE_API.system
.databaseTablesColumnList({
instanceId: application.value.instanceId,
schemaName: application.value.schemaName,
tableNameList: tableNames,
})
.then((res) => {
res.data.map((item) => {
item.label = item.columnName;
item.value = item.columnName;
});
tableColumnList.value = res.data ? res.data : [];
});
tableNameList.value = tableNameList;
console.log(tableColumnList);
};
const addItem = (paramColumnList, type = null) => {
// 1。这里为什么改变list的大小就能实现动态增加呢因为 el-form-item 遍历的是 list,list 中的每一项都是一个 el-form-item
// 也就是说因为刚开始 list:[{"oneId":''}] 中,只有一个对象,所以才会只出现一个 el-form-item
// 不信可以自己在初始化时 list 中多加入几个对象进行尝试(一定要理解,这里 list 集合的大小与 el-form-item 之间的关系)
// 2、第二个问题:el-form-item 是动态增加了,但是如果 el-select 那里写的是 v-model="oneId" 呢?会发生什么?结果你会发现,只要增加一项 el-form-item ,每一项绑定的值都是你所选中的那一个值.为什么呢?因为每一项的 el-option的 :value 值都绑定在 el-select 的 v-model 上,但这是一个全局唯一值,当下一个 el-form-item 产生后,它里面的 el-select 中绑定的 v-model 还是那个 oneId 的值,因此才会出现这样的问题.好了,我们既然找到了原因,那就要来解决一下了,怎么解决呢?很简单:因为我前面说了,每一个 list 的遍历对象,都是一项 el-form-item,即 el-form-item 项数是和 list 的下标(里面存的对象的索引下标)相关联的,而这个下标,在每一个 el-form-item 中肯定是不一样的,因此我们只需要将 oneId 与这个 下标(即此处的 index) 发生关系即可,因此我们这里将 oneId 声明为了一个数组,当你每选中一个 option 时,都将这个 option 的value放入 oneId[当前el-form-item项数下标] 数组中
console.log(type);
console.log(pathParamColumnIdList.value);
console.log(requestBodyParamColumnIdList.value);
console.log(requestParamColumnIdList.value);
if ("PATH_PARAM_TYPE" === type) {
pathParamColumnIdList.value.push({ type: type });
} else if ("REQUEST_BODY_PARAM_TYPE" === type) {
requestBodyParamColumnIdList.value.push({ type: type });
} else if ("REQUEST_PARAM_TYPE" === type) {
requestParamColumnIdList.value.push({ type: type });
}
// paramColumnList.push({ type: type });
};
const removeItem = (paramColumnList, index) => {
// 删除时,我们带两个参数,这个 it 可用可不用,因为我当时只是想看到删除的这个对象的信息,故而带上了; index 是 list 中该对象对应的下标,也是 el-form-item 的项数
// 根据这个 index 下标删除 list 中 的该对象
paramColumnList.splice(index, 1);
};
</script>
<style scoped>
/* 必需有scoped */
.api_transfer_result >>> .el-transfer-panel {
width: 450px;
}
</style>

View File

@@ -0,0 +1,202 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<el-form-item label="项目名" prop="projectId">
<el-select
v-model="projectId"
placeholder="项目名"
@change="changeProject"
clearable
>
<el-option
v-for="item in projectList"
:key="item.value"
:label="item.label"
:value="item.value"
:serverId="item"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库" prop="schemaName">
<el-select v-model="schemaName" placeholder="数据库" clearable>
<el-option
v-for="item in serverSchemaList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="应用名" prop="applicationName">
<el-input
v-model="applicationName"
placeholder=""
clearable
></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
ref,
defineProps,
defineEmits,
onMounted,
} from "vue";
const rules = {
projectId: [
{
required: true,
message: "请输入选择项目",
trigger: "blur",
},
],
schemaName: [
{
required: true,
message: "请输入选择数据库",
trigger: "blur",
},
],
applicationName: [
{
required: true,
message: "请输入应用名",
trigger: "blur",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const projectList = ref(null);
const serverSchemaList = ref(null);
const form = reactive({
applicationName: "",
projectId: "",
applicationId: "",
instanceId: "",
schemaName: "",
});
const { projectId, applicationName, applicationId, instanceId, schemaName } =
toRefs(form);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((projectId.value = rowData.value.projectId),
(applicationId.value = rowData.value.applicationId),
(instanceId.value = rowData.value.instanceId),
(schemaName.value = rowData.value.schemaName),
(applicationName.value = rowData.value.applicationName));
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value === "添加") {
res = await VE_API.system.applicationAdd(form);
} else {
res = await VE_API.system.applicationEdit({
applicationId: rowData.value.applicationId,
...form,
});
}
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
onMounted(async () => {
VE_API.system.projectList().then((res) => {
res.data.map((item) => {
item.label = item.projectName;
item.value = item.id;
});
projectList.value = res.data ? res.data : [];
});
});
const findSchema = async (instanceId = null) => {
if (instanceId == null) {
return;
}
// 查询数据库
let res = await VE_API.system.schemaList({
instanceId,
});
const { code } = res;
if (code === 0) {
res.data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
serverSchemaList.value = res.data ? res.data : [];
// databaseSchemaId.value = serverSchemaList.value[0].id;
}
};
const changeProject = (projectId) => {
console.log(projectId);
if (projectId) {
let project = projectList.value.find((item) => item.id === projectId);
findSchema(project.instanceId);
} else {
serverSchemaList.value = [];
}
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,135 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="120px"
:inline="false"
>
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="instanceId"
placeholder="数据库实例"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
ref,
defineProps,
defineEmits,
onMounted,
} from "vue";
const rules = {
instanceId: [
{
required: true,
message: "请选择数据库实例",
trigger: "blur",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const serverList = ref(null);
const form = reactive({
instanceId: "",
});
const { instanceId } = toRefs(form);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value && (instanceId.value = rowData.value.instanceId);
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
res = await VE_API.system.databaseInstanceBackUp({
instanceId: instanceId.value,
});
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
return serverList.value;
});
};
onMounted(async () => {
await getServerInstanceList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,254 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="120px"
:inline="false"
>
<el-form-item label="服务器名称" prop="instanceName">
<el-input
v-model="instanceName"
placeholder=""
clearable
></el-input>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input
v-model="username"
placeholder="root"
clearable
></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="password"
placeholder=""
clearable
></el-input>
</el-form-item>
<el-form-item label="host" prop="host">
<el-input
v-model="host"
placeholder="127.0.0.1"
clearable
></el-input>
</el-form-item>
<el-form-item label="端口" prop="port">
<el-input
v-model="port"
placeholder="3306"
clearable
></el-input>
</el-form-item>
<el-form-item label="驱动程序类名" prop="driverClassName">
<el-input
v-model="driverClassName"
placeholder="com.mysql.cj.jdbc.Driver"
clearable
></el-input>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-input v-model="status" placeholder="" clearable></el-input>
</el-form-item>
<el-form-item label="数据源类型" prop="lazyDataSourceType">
<el-select
v-model="lazyDataSourceType"
placeholder="数据源类型"
clearable
>
<el-option label="MySQL" value="MySQL" />
<el-option label="H2" value="H2" />
<el-option label="CLICK_HOUSE" value="CLICK_HOUSE" />
<el-option label="POSTGRESQL" value="POSTGRESQL" />
</el-select>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="sort" placeholder="" clearable></el-input>
</el-form-item>
<el-form-item
label="是否初始化数据库到本地"
prop="initializeToLocal"
>
<el-select
v-model="initializeToLocal"
placeholder="是否初始化数据库到本地"
clearable
>
<el-option label="初始化数据库到本地" :value="true" />
<el-option label="不初始化数据库到本地" :value="false" />
</el-select>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button style="float: left" @click="testConnection()"
>测试连接</el-button
>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { reactive, toRefs, ref, defineProps, defineEmits } from "vue";
const rules = {
instanceName: [
{
required: true,
message: "请输入服务器名称",
trigger: "blur",
},
],
username: [
{
required: true,
message: "请输入账户",
trigger: "blur",
},
],
password: [
{
required: true,
message: "请输入密码",
trigger: "blur",
},
],
host: [
{
required: true,
message: "host不能为空",
trigger: "change",
},
],
port: [
{
required: true,
message: "端口不能为空",
trigger: "change",
},
],
driverClassName: [
{
required: true,
message: "driverClassName",
trigger: "change",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
instanceName: "",
driverClassName: "com.mysql.cj.jdbc.Driver",
username: "root",
password: "",
host: "127.0.0.1",
port: 3306,
status: "",
lazyDataSourceType: "MySQL",
sort: 1,
initializeToLocal: false,
});
const {
instanceName,
driverClassName,
username,
password,
host,
port,
status,
lazyDataSourceType,
sort,
initializeToLocal,
} = toRefs(form);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((instanceName.value = rowData.value.instanceName),
(username.value = rowData.value.username),
(password.value = rowData.value.password),
(driverClassName.value = rowData.value.driverClassName),
(host.value = rowData.value.host),
(port.value = rowData.value.port),
(status.value = rowData.value.status),
(lazyDataSourceType.value = rowData.value.lazyDataSourceType),
(sort.value = rowData.value.sort),
(initializeToLocal.value = rowData.value.initializeToLocal));
/**
* 测试连接
*/
const testConnection = async () => {
let res = await VE_API.system.databaseInstanceTest(form);
const { code } = res;
if (code === 0) {
console.log("连接成功");
}
};
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value === "添加") {
res = await VE_API.system.databaseInstanceAdd(form);
} else {
res = await VE_API.system.databaseInstanceEdit({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,392 @@
<template>
<div class="ve_container">
<!-- 搜索 -->
<el-form
ref="queryForm"
:inline="false"
:model="params"
:rules="rules"
label-width="100px"
>
<el-form-item>
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="params.instanceId"
@change="getSchemaList"
placeholder="数据库实例"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库名" prop="schemaName">
<el-select
clearable
filterable
v-model="params.schemaName"
@change="
getTableList(params.instanceId, params.schemaName)
"
placeholder="数据库名"
>
<el-option
v-for="item in schemaList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
</el-form-item>
<el-row :gutter="24">
<el-col :span="4">
<el-card>
<el-tree
:props="{
children: 'children',
label: 'tableName',
}"
:data="schemaTableList"
></el-tree>
</el-card>
</el-col>
<el-col :span="20">
<el-card>
<div class="grid-content ep-bg-purple-light">
<el-form-item label="执行的sql" prop="sql">
<el-input
type="textarea"
:rows="20"
v-model="sql"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="sqlConsole()">
{{ buttons.search.name }}
</el-button>
</el-form-item>
<el-form-item>
<el-button @click="downLoad()" type="text">
{{ buttons.downLoad.name }}
</el-button>
<el-button
@click="downLoadUpsert()"
type="text"
>
{{ buttons.downLoadUpsert.name }}
</el-button>
</el-form-item>
<template
v-for="(tableData, index) in tableDataList"
:key="index"
>
<el-check-tag
:checked="tableData.checked"
@change="changeResultDataTag(index)"
>{{ index + 1 }}个结果集合</el-check-tag
>
</template>
<div
v-for="(tableData, index) in tableDataList"
:key="index"
v-show="tableData.checked"
>
<!-- 列表 -->
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(
val,
params,
getDataList,
),
onCurrentChange: (val) =>
handleCurrentChange(
val,
params,
getDataList,
),
currentPage: current,
pageSize: size,
total: total,
}"
>
<el-table-column
:prop="item"
:label="item"
v-for="(
item, index
) in tableData.tableHeader"
:key="index"
sortable
>
<template v-slot="scope">
<el-input
v-model="scope.row[item]"
placeholder="数据"
></el-input>
</template>
</el-table-column>
</ve-table>
</div>
</div>
</el-card>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script>
export default {
data: () => ({
description: "数据库查询控制台",
buttons: {
search: { name: "查询" },
downLoad: { name: "导出查询结果为Excel" },
downLoadUpsert: { name: "Upsert下载查询结果" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "IceCream",
name: "数据库查询管理",
}),
};
</script>
<script setup>
import { reactive, toRefs, ref, onMounted } from "vue";
const rules = {
instanceId: [
{
required: true,
message: "请选择数据库实例",
trigger: "blur",
},
],
schemaName: [
{
required: true,
message: "请选择数据库",
trigger: "blur",
},
],
sql: [
{
required: true,
message: "执行SQL不能为空",
trigger: "blur",
},
],
};
//?导入公共查询方法
import {
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
const queryForm = ref(null);
const tableDataList = ref([]);
const schemaList = ref(null);
const schemaTableList = ref(null);
const serverList = ref(null);
// 当前选择的表
// const currentTableName = ref(null);
const params = reactive({
schemaName: "",
instanceId: "",
instanceName: "",
sql: "show tables",
size: 10,
current: 1,
total: 0,
});
const { size, current, total, sql } = toRefs(params);
/**
* 点击结果集tag 事件
*/
const changeResultDataTag = async (index) => {
tableDataList.value.forEach((item, itemIndex) => {
if (index == itemIndex) {
item.checked = true;
} else {
item.checked = false;
}
});
console.log(index);
};
/**
* 文件下载
*/
const downLoad = async () => {
queryForm.value.validate(async (valid) => {
if (valid) {
let res = await VE_API.system.sqlConsoleExport(params, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
} else {
return false;
}
});
};
/**
* 文件下载
*/
const downLoadUpsert = async () => {
queryForm.value.validate(async (valid) => {
if (valid) {
let res = await VE_API.system.sqlConsoleUpsertExport(params, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
} else {
return false;
}
});
};
/**
* 执行sql
*/
const sqlConsole = async () => {
queryForm.value.validate(async (valid) => {
if (valid) {
let res = await VE_API.system.sqlConsole(params);
const { code } = res;
if (code === 0) {
tableDataList.value = res.data;
if (res.data.length !== 0) {
tableDataList.value.map((item) => {
item.tableHeader = Object.keys(item[0]);
item.checked = false;
});
tableDataList.value[0].checked = true;
// tableHeader.value = Object.keys(res.data[0]);
// tableHeaderList.value = Object.keys(res.data[0]);
}
console.log(res);
}
} else {
return false;
}
});
};
/**
* 获取 数据库表
* @param instanceId 数据库实例ID
* @param schemaName 数据库名称
* @returns {Promise<void>}
*/
const getTableList = async (instanceId, schemaName) => {
let res;
if (instanceId == null) {
return;
} else {
res = await VE_API.system.tableList({
instanceId: instanceId,
schemaName: schemaName,
});
}
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaTableList.value = data;
return schemaList.value;
}
};
/**
* 获取 数据库
* @param instanceId 数据库实例ID
* @returns {Promise<void>}
*/
const getSchemaList = async (instanceId) => {
let res;
if (instanceId == null) {
return;
} else {
res = await VE_API.system.schemaList({
instanceId,
});
}
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaList.value = data;
if (schemaList.value) {
// 默认第一个实例的第一个数据库
params.schemaName = schemaList.value[0].schemaName;
await getTableList(instanceId, params.schemaName);
}
return schemaList.value;
}
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
// 默认第一个实例
if (serverList.value && serverList.value.length > 0) {
params.instanceId = serverList.value[0].id;
getSchemaList(params.instanceId);
}
return serverList.value;
});
};
onMounted(async () => {
await getServerInstanceList();
});
</script>
<style lang="scss" scoped>
.el-tabs__content.el-tab-pane {
display: block;
background: #409eff;
}
</style>

View File

@@ -0,0 +1,217 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="120px"
:inline="false"
>
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="instanceId"
placeholder="数据库实例"
@change="getSchemaList"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库名" prop="schemaName">
<el-select
clearable
filterable
v-model="schemaName"
placeholder="数据库名"
>
<el-option
v-for="item in schemaList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
</el-form>
<el-progress
:text-inside="true"
:stroke-width="24"
:percentage="backUpsSchemaProgress.percentage"
:hidden="backUpsSchemaProgress.hidden"
:status="backUpsSchemaProgress.status"
>
<span>备份数据库</span>
</el-progress>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
ref,
defineProps,
defineEmits,
onMounted,
} from "vue";
const rules = {
instanceId: [
{
required: true,
message: "请选择数据库实例",
trigger: "blur",
},
],
schemaName: [
{
required: true,
message: "请选择数据库",
trigger: "blur",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "数据库备份",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const serverList = ref(null);
const schemaList = ref(null);
const instanceId = ref(null);
const schemaName = ref(null);
const backUpsSchemaProgress = reactive({
hidden: true,
percentage: 0,
status: "success",
});
const form = reactive({
instanceId: "",
schemaName: "",
});
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value && (instanceId.value = rowData.value.instanceId);
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
form.schemaName = schemaName.value;
form.instanceId = instanceId.value;
formRef.value.validate(async (valid) => {
if (valid) {
backUpsSchemaProgress.hidden = false;
// 修改进度条
setInterval(() => {
backUpsSchemaProgress.percentage =
(backUpsSchemaProgress.percentage % 100) + 10;
}, 500);
let res = await VE_API.system.databaseSchemaBackUp({
instanceId: instanceId.value,
schemaName: schemaName.value,
});
const { code } = res;
if (code === 0) {
backUpsSchemaProgress.status = "success";
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
/**
* 获取 数据库
* @param instanceId 数据库实例ID
* @returns {Promise<void>}
*/
const getSchemaList = async (instanceId) => {
let res;
if (instanceId == null) {
return;
} else {
res = await VE_API.system.schemaList({
instanceId,
});
}
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaList.value = data;
if (schemaList.value) {
// 默认第一个实例的第一个数据库
schemaName.value = schemaList.value[0].schemaName;
}
return schemaList.value;
}
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
// 默认第一个实例
if (serverList.value && serverList.value.length > 0) {
instanceId.value = serverList.value[0].id;
getSchemaList(instanceId.value);
}
return serverList.value;
});
};
onMounted(async () => {
await getServerInstanceList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,260 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="instanceId"
placeholder="数据库实例"
@change="getSchemaList"
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库库名" prop="schemaName">
<el-select
clearable
v-model="schemaName"
placeholder="数据库名"
@change="getTableList"
filterable
>
<el-option
v-for="item in schemaList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="数据库表名" prop="sortingRules">
<el-select
clearable
v-model="tableName"
placeholder="数据库表名"
@change="getDatabaseTablesColumnList"
filterable
>
<el-option
v-for="item in tableList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="字段名称" prop="columnName">
<el-select
v-model="columnName"
placeholder="字段名称"
filterable
>
<el-option
v-for="item in tableColumnList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
onMounted,
ref,
defineProps,
defineEmits,
} from "vue";
const rules = {
schemaName: [
{
required: true,
message: "请输入数据库库名",
trigger: "blur",
},
],
instanceId: [
{
required: true,
message: "请输入数据库服务器",
trigger: "blur",
},
],
tableName: [
{
required: true,
message: "请输入字符集",
trigger: "blur",
},
],
columnName: [
{
required: false,
message: "请输入排序规则",
trigger: "blur",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const tableColumnList = ref(null);
const form = reactive({
schemaName: "",
instanceId: "",
tableName: "",
columnName: "",
});
const { schemaName, instanceId, tableName, columnName } = toRefs(form);
const serverList = ref(null);
const schemaList = ref(null);
const tableList = ref(null);
/**
* 获取当前表对应的字段
* @param tableIds
* @returns {Promise<void>}
*/
const getDatabaseTablesColumnList = async () => {
// 获取数据
let res = await VE_API.system.findDatabaseTableColumnList({
instanceId: form.instanceId,
schemaName: form.schemaName,
tableName: form.tableName,
});
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.columnName;
item.value = item.columnName;
});
tableColumnList.value = data ? data : [];
}
};
/**
* 获取 数据库表
* @param instanceId 数据库实例ID
* @param schemaName 数据库名称
* @returns {Promise<void>}
*/
const getTableList = async () => {
let res;
if (instanceId.value == null) {
return;
} else {
res = await VE_API.system.tableList({
instanceId: form.instanceId,
schemaName: form.schemaName,
});
}
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.tableName;
item.value = item.tableName;
});
tableList.value = data;
}
};
/**
* 选择数据库实例
* @param serverInstanceId
* @returns {Promise<void>}
*/
const getSchemaList = async () => {
// 查询数据库
let res = await VE_API.system.schemaList({
instanceId: form.instanceId,
});
const { code } = res;
if (code === 0) {
res.data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaList.value = res.data ? res.data : [];
}
};
onMounted(async () => {
// 查询数据库实例
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
});
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res = await VE_API.system.schemaDeriveView(form);
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,255 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<el-form-item label="数据库实例" prop="instanceId">
<el-select v-model="instanceId" placeholder="数据库实例">
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库库名" prop="schemaName">
<el-input
v-model="schemaName"
placeholder="数据库库名"
clearable
></el-input>
</el-form-item>
<el-form-item label="字符集" prop="characterSet">
<el-select
v-model="characterSet"
placeholder="字符集"
filterable
>
<el-option label="armscii8" value="armscii8" />
<el-option label="ascii" value="ascii" />
<el-option label="big5" value="big5" />
<el-option label="binary" value="binary" />
<el-option label="cp850" value="cp850" />
<el-option label="cp852" value="cp852" />
<el-option label="cp866" value="cp866" />
<el-option label="cp932" value="cp932" />
<el-option label="cp1250" value="cp1250" />
<el-option label="cp1251" value="cp1251" />
<el-option label="cp1256" value="cp1256" />
<el-option label="cp1257" value="cp1257" />
<el-option label="dec8" value="dec8" />
<el-option label="eucjpms" value="eucjpms" />
<el-option label="euckr" value="euckr" />
<el-option label="gb2312" value="gb2312" />
<el-option label="gb18030" value="gb18030" />
<el-option label="gbk" value="gbk" />
<el-option label="geostd8" value="geostd8" />
<el-option label="greek" value="greek" />
<el-option label="hebrew" value="hebrew" />
<el-option label="hp8" value="hp8" />
<el-option label="keybcs2" value="keybcs2" />
<el-option label="koi8r" value="koi8r" />
<el-option label="koi8u" value="koi8u" />
<el-option label="latin1" value="latin1" />
<el-option label="latin2" value="latin2" />
<el-option label="latin5" value="latin5" />
<el-option label="latin7" value="latin7" />
<el-option label="macce" value="macce" />
<el-option label="macroman" value="macroman" />
<el-option label="sjis" value="sjis" />
<el-option label="swe7" value="swe7" />
<el-option label="tis620" value="tis620" />
<el-option label="ucs2" value="ucs2" />
<el-option label="ujis" value="ujis" />
<el-option label="utf8" value="utf8" />
<el-option label="utf8mb4" value="utf8mb4" />
<el-option label="utf16" value="utf16" />
<el-option label="utf16le" value="utf16le" />
<el-option label="utf32" value="utf32" />
</el-select>
</el-form-item>
<el-form-item label="排序规则" prop="sortingRules">
<el-input
v-model="sortingRules"
placeholder=""
clearable
></el-input>
</el-form-item>
<el-form-item label="ext" prop="ext">
<el-input v-model="ext" placeholder="" clearable></el-input>
</el-form-item>
<el-form-item
label="是否初始化数据库到本地"
prop="initializeToLocal"
>
<el-select
v-model="initializeToLocal"
placeholder="是否初始化数据库到本地"
clearable
>
<el-option label="初始化数据库到本地" :value="true" />
<el-option label="不初始化数据库到本地" :value="false" />
</el-select>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
onMounted,
ref,
defineProps,
defineEmits,
} from "vue";
const rules = {
schemaName: [
{
required: true,
message: "请输入数据库库名",
trigger: "blur",
},
],
instanceId: [
{
required: true,
message: "请输入数据库服务器",
trigger: "blur",
},
],
characterSet: [
{
required: true,
message: "请输入字符集",
trigger: "blur",
},
],
sortingRules: [
{
required: false,
message: "请输入排序规则",
trigger: "blur",
},
],
ext: [
{
required: false,
message: "ext",
trigger: "change",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
schemaName: "",
instanceId: "",
ext: "",
characterSet: "",
sortingRules: "",
initializeToLocal: "",
});
const {
schemaName,
instanceId,
ext,
characterSet,
sortingRules,
initializeToLocal,
} = toRefs(form);
const serverList = ref(null);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((schemaName.value = rowData.value.schemaName),
(instanceId.value = rowData.value.instanceId),
(characterSet.value = rowData.value.characterSet),
(sortingRules.value = rowData.value.sortingRules),
(ext.value = rowData.value.ext),
(initializeToLocal.value = rowData.value.initializeToLocal));
onMounted(async () => {
// 查询数据库实例
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
});
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value == "添加") {
res = await VE_API.system.schemaAdd(form);
} else {
res = await VE_API.system.schemaEdit({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,279 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="instanceId"
placeholder="数据库实例"
filterable
@change="(val) => getSchemaList(val)"
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="数据库库名" prop="schemaName">
<el-select
v-model="schemaName"
filterable
placeholder="数据库实例"
@change="(val) => getTableList(val)"
>
<el-option
v-for="item in schemaList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="表名称" prop="tableNameList">
<el-select
v-model="tableNameList"
placeholder="表名称"
@change="chooseTable"
filterable
multiple
>
<el-checkbox
v-model="checked"
:indeterminate="
tableNameList.length !== tableList.length
"
@change="selectAll()"
style="margin-left: 20px"
>
全选
</el-checkbox>
<el-option
v-for="item in tableList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="自动填充数量" prop="autoStuffedNum">
<el-input
v-model="autoStuffedNum"
placeholder=""
clearable
></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
onMounted,
ref,
defineProps,
defineEmits,
} from "vue";
const rules = {
databaseSchemaId: [
{
required: true,
message: "请输入数据库库名",
trigger: "blur",
},
],
instanceId: [
{
required: true,
message: "请输入数据库服务器",
trigger: "blur",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
schemaName: "",
databaseSchemaId: "",
instanceId: "",
instanceName: "",
autoStuffedNum: "",
status: false,
tableNameList: [],
id: "",
});
const {
id,
schemaName,
databaseSchemaId,
instanceId,
instanceName,
autoStuffedNum,
status,
tableNameList,
} = toRefs(form);
const serverList = ref(null);
const schemaList = ref(null);
const tableList = ref([]);
const checked = ref(false);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((id.value = rowData.value.id),
(schemaName.value = rowData.value.schemaName),
(databaseSchemaId.value = rowData.value.databaseSchemaId),
(instanceId.value = rowData.value.instanceId),
(instanceName.value = rowData.value.instanceName),
(autoStuffedNum.value = rowData.value.autoStuffedNum),
(status.value = rowData.value.status),
(tableNameList.value = [rowData.value.tableName]));
onMounted(async () => {
getInstanceList();
// getSchemaList();
// getTableList();
});
const getInstanceList = () => {
// 查询数据库实例
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
};
const getSchemaList = (databaseInstance = null) => {
console.log(databaseInstance);
// 查询数据schema信息
VE_API.system
.schemaList({
instanceId: instanceId.value,
})
.then((res) => {
res.data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaList.value = res.data ? res.data : [];
});
};
const getTableList = () => {
// 查询数据表信息
VE_API.system
.tableList({
instanceId: instanceId.value,
schemaName: schemaName.value,
})
.then((res) => {
if (res.data) {
res.data.map((item) => {
item.label = item.tableName;
item.value = item.tableName;
});
tableList.value = res.data ? res.data : [];
}
});
};
/**
* 选择表事件
*/
const chooseTable = () => {
let table = tableList.value.find(
(item) => item.schemaNameId === databaseSchemaId.value,
);
console.log(table);
if (table) {
instanceName.value = table.instanceName;
schemaName.value = table.schemaName;
}
};
const selectAll = () => {
console.log(checked.value);
if (checked.value) {
checked.value = false;
tableNameList.value = [];
} else {
checked.value = true;
tableNameList.value = tableList.value.map((d) => d.tableName);
}
};
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value === "添加自动填充记录") {
console.log(form);
res = await VE_API.system.tableAutoStuffedRecordBatchAdd(form);
} else {
res = await VE_API.system.tableAutoStuffedRecordEdit({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,847 @@
<template>
<el-descriptions title="数据库表信息" :column="4" border>
<el-descriptions-item
label="数据库实例"
label-align="right"
align="center"
property="instanceId"
>
<el-select
v-model="params.instanceId"
@change="getSchemaList"
placeholder="数据库实例"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-descriptions-item>
<el-descriptions-item
label="数据库名"
label-align="right"
align="center"
width="150px"
property="schemaName"
>
<el-select
v-model="params.schemaName"
placeholder="数据库名"
@change="getTableList"
filterable
>
<el-option
v-for="item in schemaList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-descriptions-item>
<el-descriptions-item
label="数据库表名"
label-align="right"
align="center"
>
<el-select
v-model="params.tableName"
placeholder="数据库表名"
@change="getDatabaseTablesColumnList"
filterable
>
<el-option
v-for="item in tableList"
:key="item.value"
:label="item.label"
:value="item.value"
><el-row>
<el-col :span="12">
<span style="float: left">{{ item.label }}</span>
</el-col>
<el-col :span="12">
<span
style="
float: right;
color: #8492a6;
font-size: 13px;
"
>{{ item.tableComment }}
</span>
</el-col>
</el-row></el-option
>
</el-select>
</el-descriptions-item>
<el-descriptions-item label="表引擎" label-align="right" align="center">
<el-select
v-model="params.engine"
placeholder="表引擎"
filterable
:disabled="true"
>
<el-option label="ARCHIVE" value="ARCHIVE" />
<el-option label="BLACKHOLE" value="BLACKHOLE" />
<el-option label="CSV" value="CSV" />
<el-option label="InnoDB" value="InnoDB" />
<el-option label="MEMORY" value="MEMORY" />
<el-option label="MRG_MYISAM" value="MRG_MYISAM" />
<el-option label="MyISAM" value="MyISAM" />
<el-option
label="PERFORMANCE_SCHEMA"
value="PERFORMANCE_SCHEMA"
/>
</el-select>
</el-descriptions-item>
<el-descriptions-item label="表描述" label-align="right" align="center">
<el-input
v-model="tableComment"
placeholder="tableComment"
:disabled="true"
></el-input>
</el-descriptions-item>
<el-descriptions-item
label="查询字段"
label-align="right"
align="center"
>
<el-select
v-model="selectColumnList"
value-key="columnName"
placeholder="查询字段"
filterable
multiple
collapse-tags
style="width: 240px"
@change="selectColumnOnChange"
>
<el-checkbox
v-model="params.checked"
:indeterminate="
selectColumnList.length !== tableColumnList.length
"
@change="selectAllFields()"
style="margin-left: 20px"
>
全选
</el-checkbox>
<el-option
v-for="item in tableColumnList"
:key="item.value"
:label="item.label"
:value="item"
>
<el-row>
<el-col :span="12">
<span style="float: left">{{ item.label }}</span>
</el-col>
<el-col :span="12">
<span
style="
float: right;
color: #8492a6;
font-size: 13px;
"
>{{ item.columnComment }}
</span>
</el-col>
</el-row>
</el-option>
</el-select>
</el-descriptions-item>
<el-descriptions-item label="表头展示模式" prop="status">
<el-radio-group v-model="params.showTitleMode">
<el-radio-button label="columnName">字段名称</el-radio-button>
<el-radio-button label="columnComment"
>字段描述</el-radio-button
>
</el-radio-group>
</el-descriptions-item>
</el-descriptions>
<el-row>
<el-button @click="addColumnItem()" style="float: left" type="primary"
>添加搜索参数
</el-button>
<el-button @click="getDataList" style="float: left" type="primary"
>执行
</el-button>
<!-- <el-button-->
<!-- v-permission="['batchDel']"-->
<!-- type="danger"-->
<!-- @click="batchDelRow"-->
<!-- style="float: left"-->
<!-- >-->
<!-- {{ buttons.batchDel.name }}-->
<!-- </el-button>-->
<el-button type="danger" @click="batchDelRow" style="float: left">
{{ buttons.batchDel.name }}
</el-button>
<el-button
@click="addRowData"
style="float: left"
type="primary"
:hidden="!params.checked"
>添加数据
</el-button>
<el-button
@click="handleExportUpsertSql"
style="float: left"
type="primary"
>导出查询的upsert语句
</el-button>
<el-button
@click="handleExportResult2Md"
style="float: left"
type="primary"
>导出查询结果为.MD
</el-button>
</el-row>
<div style="padding: 10px">
<el-row
v-for="(it, index) in params.queryCriteriaColumnList"
:key="index"
>
<el-col span="3">
<el-checkbox v-model="it.checked" label=" " size="large" />
</el-col>
<el-col span="6">
<el-select
v-model="it.columnName"
placeholder="字段名称"
filterable
@change="getOnlyColumnValue(it)"
>
<el-option
v-for="item in tableColumnList"
:key="item.value"
:label="item.label"
:value="item.value"
>
<el-row>
<el-col :span="12">
<span style="float: left">{{
item.label
}}</span>
</el-col>
<el-col :span="12">
<span
style="
float: right;
color: #8492a6;
font-size: 13px;
"
>{{ item.columnComment }}
</span>
</el-col>
</el-row>
</el-option>
</el-select>
</el-col>
<el-col span="6">
<el-select
v-model="it.condition"
placeholder="条件(大于、等于、模糊)"
filterable
>
<el-option label="小等于" value="<" />
<el-option label="等于" value="=" />
<el-option label="大于等于" value=">=" />
<el-option label="小于等于" value="<=" />
<el-option label="不等于" value="!=" />
<el-option label="模糊查询" value="like" />
<el-option label="in" value="in" />
</el-select>
</el-col>
<el-col span="6">
<el-select
v-model="it.data"
style="width: 230px"
placeholder="数据"
filterable
clearable
allow-create
>
<el-option
v-for="columnUnitValue in it.columnUnitValueList"
:key="columnUnitValue"
:label="columnUnitValue"
:value="columnUnitValue"
/>
</el-select>
</el-col>
<el-col span="6">
<el-button
@click="removeColumnItem(index)"
style="float: right"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button>
</el-col>
</el-row>
</div>
<!-- 列表 -->
<div style="height: 120%">
<ve-table
:table="{
data: tableData,
}"
:pagination="{
onSizeChange: (val) =>
handleSizeChange(val, params, getDataList),
onCurrentChange: (val) =>
handleCurrentChange(val, params, getDataList),
currentPage: current,
pageSize: size,
total: total,
}"
@selectionChange="handleSelectionChange"
:key="tableKey"
>
<!-- 复选框 -->
<el-table-column
v-if="tableData.length !== 0"
type="selection"
width="55"
/>
<!-- 所有的数据列表 -->
<!-- <el-table-column-->
<!-- v-for="(item, index) in tableColumnList"-->
<!-- :prop="item.columnName"-->
<!-- :label="item.columnName"-->
<!-- :key="index"-->
<!-- sortable-->
<!-- >-->
<!-- <template v-slot="scope">-->
<!-- <el-input-->
<!-- v-model="scope.row[item.columnName]"-->
<!-- placeholder="数据"-->
<!-- @change="changeRow(scope.row)"-->
<!-- ></el-input>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- 选取的返回结果 -->
<!-- 展示字段描述 -->
<el-table-column
v-for="(item, index) in selectColumnList"
:prop="dynamicColumnList[index].columnName"
:label="
params.showTitleMode === 'columnName'
? item.columnName
: item.columnComment === ''
? item.columnName
: item.columnComment
"
:key="`col_${index}`"
sortable
>
<template v-slot="scope">
<el-input
v-model="scope.row[item.columnName]"
placeholder="数据"
@change="changeRow(scope.row)"
></el-input>
</template>
</el-table-column>
</ve-table>
<div style="position: relative">
<el-input
type="textarea"
v-model="tableData.sql"
readonly
></el-input>
<el-icon
@click="copyAnswer(tableData.sql)"
style="float: right; position: absolute; right: 10px; top: 10px"
>
<DocumentCopy
/></el-icon>
</div>
</div>
<!-- <el-button @click="closeDialog()">取消</el-button>-->
<!-- <el-button type="primary" @click="onSubmit()">确定</el-button>-->
</template>
<script>
export default {
data: () => ({
description: "数据库表快捷查询",
buttons: {
del: { name: "删除字段" },
batchDel: { name: "批量删除数据" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Finished",
name: "数据库表快捷查询",
}),
};
</script>
<script setup>
//导入公共查询方法
import {
handleSizeChange,
handleCurrentChange,
} from "@/views/layoutpages/common";
import { reactive, toRefs, ref, defineProps, onMounted } from "vue";
import { DocumentCopy } from "@element-plus/icons-vue";
import useClipboard from "vue-clipboard3";
const { toClipboard } = useClipboard();
import Sortable from "sortablejs";
import { useRoute } from "vue-router";
import { ElMessage } from "element-plus";
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const { rowData } = toRefs(props);
// 表字段
const tableColumnList = ref([]);
// 返回结果
const selectColumnList = ref([]);
const tableKey = ref(1);
const params = reactive({
schemaName: "",
instanceId: "",
tableComment: "",
tableName: "",
engine: "",
tableCatalog: "",
checked: true,
showTitleMode: "columnName",
// 查询条件字段
queryCriteriaColumnList: [],
//返回结果
selectColumnList: [],
current: 1,
size: 10,
total: 0,
});
const {
schemaName,
instanceId,
tableComment,
tableName,
engine,
size,
current,
total,
} = toRefs(params);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((schemaName.value = rowData.value.schemaName),
(instanceId.value = rowData.value.instanceId),
(tableName.value = rowData.value.tableName));
const schemaList = ref(null);
const tableList = ref(null);
const tableData = ref([]);
const serverList = ref(null);
const batchSelectRowList = ref(null);
// const oldList = ref(JSON.parse(JSON.stringify(selectColumnList.value)));
// 动态列
const dynamicColumnList = ref([]);
//
// /**
// * 表头预览
// * @param h
// * @param column
// * @returns {*}
// */
// const renderColumnHeader = (h) => {
// // // h即为cerateElement的简写具体可看vue官方文档
// // return h("div", [
// // h("span", "column.label"),
// // h("i", {
// // class: "el-icon-question",
// // }),
// // ]);
// };
/**
* 选择行改变事件
*/
const selectColumnOnChange = () => {
console.log("selectColumnList: " + selectColumnList.value);
console.log("dynamicColumnList: " + dynamicColumnList.value);
dynamicColumnList.value = selectColumnList.value;
};
/**
* 全选字段
*
*/
const selectAllFields = () => {
console.log(params.checked);
if (params.checked) {
params.checked = true;
// selectColumnList.value = tableColumnList.value;
initColumnList(tableColumnList.value);
} else {
params.checked = false;
// selectColumnList.value = [];
initColumnList([]);
}
};
/**
* 获取惟一数据
* @returns {Promise<void>}
*/
const getOnlyColumnValue = async (rowColumn) => {
const columnName = rowColumn.columnName;
console.log(rowColumn);
let res = await VE_API.system.tableColumnSqlConsole({
instanceId: params.instanceId,
schemaName: params.schemaName,
tableName: params.tableName,
column: columnName,
current: 1,
size: 100,
});
const { code, data } = res;
if (code === 0) {
rowColumn.columnUnitValueList = data.record ? data.record : [];
// console.log(res);
}
};
/**
* 批量删除数据
*/
const batchDelRow = async () => {
console.log(batchSelectRowList.value);
let deleteBatchRow = {
schemaName: params.schemaName,
instanceId: params.instanceId,
tableName: params.tableName,
tableRowList: [],
};
for (let row of batchSelectRowList.value) {
deleteBatchRow.tableRowList.push(row);
}
console.log(deleteBatchRow);
let res = await VE_API.system.tableRowBatchDelete(deleteBatchRow);
const { code, data } = res;
if (code === 0) {
console.log(data); //
}
await getDataList();
};
/**
* 全选事件
*
*/
const handleSelectionChange = (val) => {
batchSelectRowList.value = val;
console.log(batchSelectRowList.value);
};
/**
* 行数据变更
* @param row 行数据
* @returns {Promise<void>}
*/
const changeRow = async (row) => {
let storyRow = {
schemaName: params.schemaName,
instanceId: params.instanceId,
tableName: params.tableName,
tableRow: row,
};
console.log(storyRow);
let res = await VE_API.system.tableRowStory(storyRow);
const { code, data } = res;
if (code === 0) {
console.log(data); //
await getDataList();
}
};
/**
* 新增一行数据
*/
const addRowData = () => {
tableData.value.push({});
};
/**
* 获取当前表数据
*/
const getDataList = async () => {
console.log(selectColumnList.value);
console.log(selectColumnList.value.length);
params.selectColumnList = selectColumnList.value.map((d) => d.columnName);
let res = await VE_API.system.tableSqlConsole(params);
const { code, data, ext } = res;
if (code === 0) {
const { size, current, total, record } = data;
params.size = size;
params.current = current;
params.total = total;
tableData.value = record;
tableData.value.tableHeader = ext.tableHeader;
tableData.value.sql = ext.sql;
if (record.length === 0) {
return;
}
console.log(ext);
// tableData.value.tableHeader = Object.keys(tableData.value[0]);
// console.log(tableData.value);
}
};
/**
* 导出查询的 upsert 数据
*/
const handleExportUpsertSql = async () => {
params.selectColumnList = selectColumnList.value.map((d) => d.columnName);
let res = await VE_API.system.tableSqlConsoleUpsertExport(params, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
};
/**
* 导出结果为md
* @returns {Promise<void>}
*/
const handleExportResult2Md = async () => {
params.selectColumnList = selectColumnList.value.map((d) => d.columnName);
let res = await VE_API.system.tableSqlConsoleMdExport(params, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
};
// 添加表字段
const addColumnItem = () => {
params.queryCriteriaColumnList.push({
condition: "=",
checked: true,
});
};
// 删除字段
const removeColumnItem = (index) => {
// 删除时,我们带两个参数,这个 it 可用可不用,因为我当时只是想看到删除的这个对象的信息,故而带上了; index 是 list 中该对象对应的下标,也是 el-form-item 的项数
// 根据这个 index 下标删除 list 中 的该对象
params.queryCriteriaColumnList.splice(index, 1);
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
};
/**
* 选择数据库实例
* @param serverInstanceId
* @returns {Promise<void>}
*/
const getSchemaList = async () => {
// 查询数据库
let res = await VE_API.system.schemaList({
instanceId: params.instanceId,
});
const { code } = res;
if (code === 0) {
if (res.data) {
res.data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaList.value = res.data ? res.data : [];
}
}
};
/**
* 获取 数据库表
* @param instanceId 数据库实例ID
* @param schemaName 数据库名称
* @returns {Promise<void>}
*/
const getTableList = async () => {
tableName.value = null;
tableComment.value = null;
let res;
if (instanceId.value == null) {
return;
} else {
res = await VE_API.system.tableList({
instanceId: instanceId.value,
schemaName: schemaName.value,
});
}
const { code, data } = res;
if (code === 0) {
if (data) {
data.map((item) => {
item.label = item.tableName;
item.value = item.tableName;
});
tableList.value = data;
}
}
};
/**
* 获取当前表对应的字段
* @param tableIds
* @returns {Promise<void>}
*/
const getDatabaseTablesColumnList = async () => {
// 设置表备注
let table = tableList.value.find(
(item) => item.tableName === tableName.value,
);
tableColumnList.value = [];
params.queryCriteriaColumnList = [];
if (table != null) {
tableComment.value = table.tableComment;
engine.value = table.engine;
} else {
return;
}
let res = await VE_API.system.findDatabaseTableColumnList({
instanceId: instanceId.value,
schemaName: schemaName.value,
tableName: tableName.value,
});
const { code, data } = res;
if (code === 0) {
data.map((item) => {
item.label = item.columnName;
item.value = item.columnName;
});
tableColumnList.value = data ? data : [];
// 默认全选数据
initColumnList(tableColumnList.value);
}
// 获取数据
await getDataList();
};
/**
* 初始化列、动态列
*/
const initColumnList = (tableColumnList) => {
selectColumnList.value = tableColumnList;
dynamicColumnList.value = JSON.parse(JSON.stringify(tableColumnList));
//
rowDrop();
colDrop();
};
// 列表 **行拖拽
const rowDrop = async () => {
// 此时找到的元素是要拖拽元素的父容器
const tbody = document.querySelector(".el-table__body-wrapper tbody");
Sortable.create(tbody, {
// 指定父元素下可被拖拽的子元素
draggable: ".el-table__row",
onEnd({ newIndex, oldIndex }) {
const currRow = tableData.value.splice(oldIndex, 1)[0];
tableData.value.splice(newIndex, 0, currRow);
},
});
};
// 列表 **列拖拽
const colDrop = async () => {
const wrapperTr = document.querySelector(".el-table__header-wrapper tr");
Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
onEnd: (evt) => {
const empty = 1;
// 跳过显示的列数量如开头我们用了一个多选框h和序号
const oldItem = dynamicColumnList.value[evt.oldIndex - empty];
dynamicColumnList.value.splice(evt.oldIndex - empty, 1);
dynamicColumnList.value.splice(evt.newIndex - empty, 0, oldItem);
// 每一次拖拽后都要重绘一次
reDrawTable();
},
});
};
const copyAnswer = async (copyData) => {
console.log("copyData", copyData);
if (copyData === "") {
ElMessage.warning("请输入文本再复制");
return;
}
try {
await toClipboard(copyData);
ElMessage.success(`复制: ${copyData} 成功!`);
} catch (error) {
ElMessage.warning(`复制失败: ${error} `);
console.error(error);
}
};
/**
* 触发表格重绘
*/
const reDrawTable = () => {
tableKey.value = Math.random();
};
/**
* 页面初始化方法
*/
onMounted(async () => {
let route = useRoute();
let row = route.query;
// await initTableData(row);
await getServerInstanceList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,128 @@
<template>
<el-drawer
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<el-table ref="filterTable" :data="tableData" style="width: 100%">
<el-table-column
prop="date"
label="日期"
sortable
width="180"
column-key="date"
:filters="[
{ text: '2016-05-01', value: '2016-05-01' },
{ text: '2016-05-02', value: '2016-05-02' },
{ text: '2016-05-03', value: '2016-05-03' },
{ text: '2016-05-04', value: '2016-05-04' },
]"
:filter-method="filterHandler"
>
</el-table-column>
<el-table-column prop="name" label="姓名" width="180">
</el-table-column>
<el-table-column prop="address" label="地址" :formatter="formatter">
</el-table-column>
<el-table-column
prop="tag"
label="标签"
width="100"
:filters="[
{ text: '家', value: '家' },
{ text: '公司', value: '公司' },
]"
:filter-method="filterTag"
filter-placement="bottom-end"
>
<template v-slot="scope">
<el-tag
:type="scope.row.tag === '家' ? 'primary' : 'success'"
disable-transitions
>{{ scope.row.tag }}</el-tag
>
</template>
</el-table-column>
</el-table>
</el-drawer>
</template>
<script>
export default {
data() {
return {
tableData: [
{
date: "2016-05-02",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
tag: "家",
},
{
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1517 弄",
tag: "公司",
},
{
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1519 弄",
tag: "家",
},
{
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1516 弄",
tag: "公司",
},
],
};
},
methods: {
resetDateFilter() {
this.$refs.filterTable.clearFilter("date");
},
clearFilter() {
this.$refs.filterTable.clearFilter();
},
formatter(row, column) {
console.log(column);
return row.address;
},
filterTag(value, row) {
return row.tag === value;
},
filterHandler(value, row, column) {
const property = column["property"];
return row[property] === value;
},
},
};
</script>
<script setup>
import { defineEmits, defineProps, onMounted, toRefs } from "vue";
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "数据库表文档",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
onMounted(async () => {});
</script>

View File

@@ -0,0 +1,737 @@
<template>
<el-descriptions title="数据库表信息" :column="4" border>
<el-descriptions-item
label="数据库实例"
label-align="right"
align="center"
property="instanceId"
>
<el-select
v-model="instanceId"
@change="changedatabaseInstance"
placeholder="数据库实例"
filterable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/> </el-select
></el-descriptions-item>
<el-descriptions-item
label="数据库名"
label-align="right"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<el-select
clearable
v-model="schemaName"
placeholder="数据库名"
@change="getDataList"
filterable
>
<el-option
v-for="item in schemaNameList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-descriptions-item>
<el-descriptions-item label="表名称" label-align="right" align="center">
<el-input
v-model="tableName"
placeholder="tableName"
clearable
></el-input>
</el-descriptions-item>
<el-descriptions-item label="表引擎" label-align="right" align="center">
<el-select v-model="engine" placeholder="表引擎" filterable>
<el-option label="ARCHIVE" value="ARCHIVE" />
<el-option label="BLACKHOLE" value="BLACKHOLE" />
<el-option label="CSV" value="CSV" />
<el-option label="InnoDB" value="InnoDB" />
<el-option label="MEMORY" value="MEMORY" />
<el-option label="MRG_MYISAM" value="MRG_MYISAM" />
<el-option label="MyISAM" value="MyISAM" />
<el-option
label="PERFORMANCE_SCHEMA"
value="PERFORMANCE_SCHEMA"
/>
</el-select>
</el-descriptions-item>
<el-descriptions-item label="表描述" label-align="right" align="center">
<el-input
v-model="tableComment"
placeholder="tableComment"
clearable
></el-input
></el-descriptions-item>
</el-descriptions>
<el-tabs
v-model="activeName"
type="card"
class="demo-tabs"
@tab-click="handleClick"
>
<el-tab-pane label="字段" name="first">
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<ve-table
:table="{
data: form.tableColumnList,
}"
:rules="rules"
style="width: 100%"
:row-class-name="tableRowClassName"
>
<el-table-column
prop="columnName"
label="字段名称"
sortable
>
<template v-slot:default="{ row }">
<el-input
v-model="row.columnName"
placeholder="columnName"
clearable
></el-input>
</template>
</el-table-column>
<el-table-column prop="dataType" label="类型">
<template v-slot:default="{ row }">
<el-select
v-model="row.dataType"
placeholder="字段类型"
filterable
>
<el-option label="bigint" value="bigint" />
<el-option label="binary" value="binary" />
<el-option label="bit" value="bit" />
<el-option label="blob" value="blob" />
<el-option label="char" value="char" />
<el-option label="date" value="date" />
<el-option label="datetime" value="datetime" />
<el-option label="decimal" value="decimal" />
<el-option label="double" value="double" />
<el-option label="enum" value="enum" />
<el-option label="float" value="float" />
<el-option label="geometry" value="geometry" />
<el-option
label="geometrycollection"
value="geometrycollection"
/>
<el-option label="int" value="int" />
<el-option label="integer" value="integer" />
<el-option label="json" value="json" />
<el-option
label="linestring"
value="linestring"
/>
<el-option label="longblob" value="longblob" />
<el-option label="longtext" value="longtext" />
<el-option
label="mediumblob"
value="mediumblob"
/>
<el-option
label="mediumint"
value="mediumint"
/>
<el-option
label="mediumtext"
value="mediumtext"
/>
<el-option
label="multilinestring"
value="multilinestring"
/>
<el-option
label="multipoint"
value="multipoint"
/>
<el-option
label="multipolygon"
value="multipolygon"
/>
<el-option label="numeric" value="numeric" />
<el-option label="point" value="point" />
<el-option label="polygon" value="polygon" />
<el-option label="real" value="real" />
<el-option label="set" value="set" />
<el-option label="smallint" value="smallint" />
<el-option label="text" value="text" />
<el-option label="time" value="time" />
<el-option
label="timestamp"
value="timestamp"
/>
<el-option label="tinyblob" value="tinyblob" />
<el-option label="tinyint" value="tinyint" />
<el-option label="tinytext" value="tinytext" />
<el-option
label="varbinary"
value="varbinary"
/>
<el-option label="varchar" value="varchar" />
<el-option label="year" value="year" />
</el-select>
</template>
</el-table-column>
<el-table-column
prop="characterMaximumLength"
label="字段长度"
>
<template v-slot:default="{ row }">
<el-input
v-model="row.characterMaximumLength"
placeholder="characterMaximumLength"
clearable
></el-input
></template>
</el-table-column>
<el-table-column
prop="columnType"
label="字段类型"
sortable
>
<template v-slot:default="{ row }">
<el-input
v-model="row.columnType"
placeholder="columnType"
clearable
></el-input>
</template>
</el-table-column>
<el-table-column prop="generationExpression" label="小数点">
<template v-slot:default="{ row }">
<el-input
v-model="row.generationExpression"
placeholder="generationExpression"
clearable
></el-input
></template>
</el-table-column>
<el-table-column prop="isNullable" label="不是null">
<template v-slot:default="{ row }">
<el-select
v-model="row.isNullable"
placeholder="不是null"
>
<el-option label="YES" value="YES" />
<el-option label="NO" value="NO" />
</el-select>
</template>
</el-table-column>
<el-table-column prop="numericPrecision" label="虚拟">
<template v-slot:default="{ row }">
<el-input
v-model="row.numericPrecision"
placeholder="numericPrecision"
clearable
></el-input
></template>
</el-table-column>
<el-table-column prop="numericScale" label="键">
<template v-slot:default="{ row }">
<el-input
v-model="row.numericScale"
placeholder="numericScale"
clearable
></el-input
></template>
</el-table-column>
<el-table-column prop="columnComment" label="描述">
<template v-slot:default="{ row }">
<el-input
v-model="row.columnComment"
placeholder="columnComment"
clearable
></el-input
></template>
</el-table-column>
<template #tool_bar>
<el-button
@click="addColumnItem()"
style="float: right"
type="primary"
>增加</el-button
>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()"
>确定</el-button
>
</template>
<el-table-column fixed="right" label="操作">
<template v-slot="{ $index }">
<el-button
@click="removeColumnItem($index)"
style="float: right"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button></template
>
</el-table-column>
</ve-table>
</el-form>
</el-tab-pane>
<el-tab-pane label="索引" name="second">
<ve-table
:table="{
data: form.tableColumnIndexList,
}"
:rules="rules"
style="width: 100%"
:row-class-name="tableRowClassName"
>
<el-table-column prop="indexName" label="索引名称" sortable>
<template v-slot:default="{ row }">
<el-input
v-model="row.indexName"
placeholder="indexName"
clearable
></el-input>
</template>
</el-table-column>
<el-table-column prop="columnNameList" label="索引字段">
<template v-slot:default="{ row }">
<el-select
v-model="row.columnNameList"
placeholder="索引字段"
filterable
multiple
>
<el-option
v-for="item in form.tableColumnList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column prop="indexType" label="索引类型">
<template v-slot:default="{ row }">
<el-select
v-model="row.indexType"
placeholder="索引类型"
filterable
>
<el-option label="FULLTEXT" value="FULLTEXT" />
<el-option label="NORMAL" value="NORMAL" />
<el-option label="SPATIAL" value="SPATIAL" />
<el-option
label="UNIQUE"
value="UNIQUE"
/> </el-select
></template>
</el-table-column>
<el-table-column prop="indexType" label="索引方法">
<template v-slot:default="{ row }">
<el-select
v-model="row.indexType"
placeholder="索引方法"
filterable
>
<el-option label="BTREE" value="BTREE " />
<el-option label="HASH" value="HASH" /> </el-select
></template>
</el-table-column>
<el-table-column prop="indexComment" label="注释">
<template v-slot:default="{ row }">
<el-input
v-model="row.indexComment"
placeholder="索引注释"
/>
</template>
</el-table-column>
<template #tool_bar>
<el-button
@click="addColumnIndexItem()"
style="float: right"
type="primary"
>增加</el-button
>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()"
>确定</el-button
>
</template>
<el-table-column fixed="right" label="操作">
<template v-slot="{ $index }">
<el-button
@click="removeColumnIndexItem($index)"
style="float: right"
type="danger"
size="small"
>
{{ buttons.del.name }}
</el-button></template
>
</el-table-column>
</ve-table>
</el-tab-pane>
<!-- <el-tab-pane label="外键" name="third">外键</el-tab-pane>-->
<!-- <el-tab-pane label="触发器" name="fourth">触发器</el-tab-pane>-->
<!-- <el-tab-pane label="选项" name="five">选项</el-tab-pane>-->
<!-- <el-tab-pane label="注释" name="six">注释</el-tab-pane>-->
<!-- <el-tab-pane label="SQL预览" name="seven">SQL预览</el-tab-pane>-->
</el-tabs>
</template>
<script>
export default {
data: () => ({
description: "数据库表字段设置",
buttons: {
del: { name: "删除字段" },
},
}),
};
</script>
<script setup>
import { reactive, toRefs, ref, defineProps, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
const rules = {
instanceId: [
{
required: true,
message: "请选择数据库实例",
trigger: "blur",
},
],
characterSet: [
{
required: true,
message: "请输入字符集",
trigger: "blur",
},
],
sortingRules: [
{
required: true,
message: "请输入排序规则",
trigger: "blur",
},
],
ext: [
{
required: true,
message: "ext",
trigger: "change",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const { title, rowData } = toRefs(props);
const closeDialog = () => {
// router.go(-1);
router.back();
};
const formRef = ref(null);
const form = reactive({
id: "",
schemaName: "",
instanceName: "",
instanceId: "",
engine: "InnoDB",
autoIncrement: "",
tableComment: "",
tableName: "",
tableCatalog: "",
tableRows: "",
tableCollation: "",
maxDataLength: "",
indexLength: "",
dataLength: "",
avgRowLength: "",
createOptions: "",
checkTime: "",
checksum: "",
dataFree: "",
rowFormat: "",
tableType: "",
createTime: "",
updateTime: "",
version: "",
tableColumnList: ref([]),
tableColumnIndexList: ref([]),
});
const {
id,
schemaName,
instanceName,
instanceId,
engine,
autoIncrement,
tableComment,
tableName,
tableCatalog,
tableRows,
tableCollation,
maxDataLength,
indexLength,
dataLength,
avgRowLength,
createOptions,
checkTime,
checksum,
dataFree,
rowFormat,
tableType,
createTime,
updateTime,
version,
} = toRefs(form);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((schemaName.value = rowData.value.schemaName),
(id.value = rowData.value.id),
(instanceName.value = rowData.value.instanceName),
(instanceId.value = rowData.value.instanceId),
(engine.value = rowData.value.engine),
(autoIncrement.value = rowData.value.autoIncrement),
(tableComment.value = rowData.value.tableComment),
(tableName.value = rowData.value.tableName),
(tableCatalog.value = rowData.value.tableCatalog),
(tableRows.value = rowData.value.tableRows),
(tableCollation.value = rowData.value.tableCollation),
(maxDataLength.value = rowData.value.maxDataLength),
(indexLength.value = rowData.value.indexLength),
(dataLength.value = rowData.value.dataLength),
(avgRowLength.value = rowData.value.avgRowLength),
(createOptions.value = rowData.value.createOptions),
(checkTime.value = rowData.value.checkTime),
(checksum.value = rowData.value.checksum),
(dataFree.value = rowData.value.dataFree),
(rowFormat.value = rowData.value.rowFormat),
(tableType.value = rowData.value.tableType),
(createTime.value = rowData.value.createTime),
(updateTime.value = rowData.value.updateTime),
(version.value = rowData.value.version));
const schemaNameList = ref(null);
const serverList = ref(null);
const router = useRouter();
const activeName = ref("first");
/**
* 点标签
* @param tab 标签
* @param event 事件
* @returns {Promise<void>}
*/
const handleClick = async (tab, event) => {
console.log(tab, event);
};
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
let databaseInstance = serverList.value.find(
(item) => item.id === form.instanceId,
);
form.instanceName = databaseInstance.instanceName;
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value === "添加") {
res = await VE_API.system.tableAdd(form);
} else {
res = await VE_API.system.tableAdd({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code === 0) {
router.go(-1);
}
} else {
console.log("error submit!!");
return false;
}
});
};
// 添加表字段
const addColumnItem = () => {
form.tableColumnList.push({});
};
// 删除字段
const removeColumnItem = (index) => {
// 删除时,我们带两个参数,这个 it 可用可不用,因为我当时只是想看到删除的这个对象的信息,故而带上了; index 是 list 中该对象对应的下标,也是 el-form-item 的项数
// 根据这个 index 下标删除 list 中 的该对象
form.tableColumnList.splice(index, 1);
};
// 添加表索引
const addColumnIndexItem = () => {
form.tableColumnIndexList.push({});
};
// 删除索引
const removeColumnIndexItem = (index) => {
// 删除时,我们带两个参数,这个 it 可用可不用,因为我当时只是想看到删除的这个对象的信息,故而带上了; index 是 list 中该对象对应的下标,也是 el-form-item 的项数
// 根据这个 index 下标删除 list 中 的该对象
form.tableColumnIndexList.splice(index, 1);
};
// 查询数据库实例
const getServerInstanceList = async () => {
VE_API.system.databaseInstanceList().then((res) => {
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
});
serverList.value = res.data ? res.data : [];
});
};
/**
* 选择数据库实例
* @param serverInstanceId
* @returns {Promise<void>}
*/
const changedatabaseInstance = async (instanceId) => {
if (instanceId == null) {
return;
}
// 查询数据库
let res = await VE_API.system.schemaList({
instanceId,
});
const { code } = res;
if (code === 0) {
res.data.map((item) => {
item.label = item.schemaName;
item.value = item.schemaName;
});
schemaNameList.value = res.data ? res.data : [];
}
};
const initTableData = async (row) => {
let instanceId = row.instanceId;
if (instanceId !== "" && instanceId !== undefined) {
form.schemaName = row.schemaName;
form.instanceId = row.instanceId;
form.instanceName = row.instanceName;
form.engine = row.engine;
form.autoIncrement = row.autoIncrement;
form.tableComment = row.tableComment;
form.tableName = row.tableName;
form.tableCatalog = row.tableCatalog;
form.tableRows = row.tableRows;
form.tableCollation = row.tableCollation;
form.maxDataLength = row.maxDataLength;
form.indexLength = row.indexLength;
form.dataLength = row.dataLength;
form.avgRowLength = row.avgRowLength;
form.createOptions = row.createOptions;
form.checkTime = row.checkTime;
form.checksum = row.checksum;
form.dataFree = row.dataFree;
form.rowFormat = row.rowFormat;
form.tableType = row.tableType;
form.createTime = row.createTime;
form.updateTime = row.updateTime;
form.version = row.version;
// 判断数据库实例
form.instanceId = instanceId;
// 获取当前表选择的数据库实例数据库列表
if (
form.instanceId !== null &&
form.instanceId !== "" &&
form.instanceId !== undefined &&
!isNaN(form.instanceId)
) {
await changedatabaseInstance(form.instanceId);
}
// 查询当前表字段
if (
form.tableName !== null &&
form.tableName !== "" &&
form.tableName !== undefined
) {
await getDatabaseTablesColumnList(
form.instanceId,
form.schemaName,
form.tableName,
);
}
}
};
/**
* 获取当前表对应的字段
* @param tableIds
* @returns {Promise<void>}
*/
const getDatabaseTablesColumnList = async (
instanceId,
schemaName,
tableName,
) => {
let res = await VE_API.system.databaseTableColumnList({
instanceId: instanceId,
schemaName: schemaName,
tableName: tableName,
});
const { code, data } = res;
if (code === 0) {
const { acwTableColumnDTOList, acwTableColumnIndexDTOList } = data;
if (acwTableColumnDTOList && acwTableColumnDTOList.length > 0) {
acwTableColumnDTOList.map((item) => {
item.label = item.columnName;
item.value = item.columnName;
});
form.tableColumnList = acwTableColumnDTOList
? acwTableColumnDTOList
: [];
form.tableColumnIndexList = acwTableColumnIndexDTOList;
}
}
};
const tableRowClassName = async ({ row, rowIndex }) => {
// 把每一行的索引放进row
row.index = rowIndex;
};
onMounted(async () => {
let route = useRoute();
let row = route.query;
await initTableData(row);
await getServerInstanceList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,293 @@
<template>
<el-drawer
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<el-descriptions class="margin-top" :column="3" border title="表详情">
<el-descriptions-item
label="id"
property="id"
class="contentClassName"
>
{{ this.rowData.id }}
</el-descriptions-item>
<el-descriptions-item label="数据库服务器ID">
<el-tag size="small"> {{ this.rowData.instanceId }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="数据库服务器名称">
<el-tag size="small"> {{ this.rowData.instanceName }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="数据库名称">
{{ this.rowData.schemaName }}</el-descriptions-item
>
<el-descriptions-item label="数据库ID">
{{ this.rowData.schemaNameId }}</el-descriptions-item
>
<el-descriptions-item label="表引擎">
{{ this.rowData.engine }}</el-descriptions-item
>
<el-descriptions-item label="表名称">
{{ this.rowData.tableName }}</el-descriptions-item
>
<el-descriptions-item label="表名描述">
{{ this.rowData.tableComment }}</el-descriptions-item
>
</el-descriptions>
<el-descriptions border>
<el-descriptions-item label="表字段">
<span v-for="(item, index) in tableColumnList" :key="index"
>{{ item.columnName }} <el-divider direction="vertical"
/></span>
</el-descriptions-item>
</el-descriptions>
<el-checkbox-group v-model="checkedCities" :min="1" :max="2">
<el-checkbox v-for="city in cities" :key="city" :label="city">{{
city
}}</el-checkbox>
</el-checkbox-group>
<el-timeline>
<el-timeline-item timestamp="实体" placement="top">
<el-card>
<h4>表对应的实体</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaEntity }}
</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="实体对应xml" placement="top">
<el-card>
<h4>实体对应xml</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaMapperXml }}
</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="控制层" placement="top">
<el-card>
<h4>表对应的控制层</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaController }}
</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="业务接口" placement="top">
<el-card>
<h4>表对应的业务接口</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaService }}
</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="业务接口实现" placement="top">
<el-card>
<h4>表对应的业务接口实现</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaServiceImpl }}
</p>
</el-card>
</el-timeline-item>
</el-timeline>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-drawer>
</template>
<script setup>
import {
reactive,
toRefs,
ref,
defineProps,
defineEmits,
onMounted,
} from "vue";
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const checkedCities = ref(["swagger"]);
const cities = ["swagger", "mybatis", "lazy", "Shenzhen"];
/**
* private StringBuilder javaController;
private StringBuilder javaEntity;
private StringBuilder javaService;
private StringBuilder javaServiceImpl;
private StringBuilder javaMapper;
private StringBuilder javaMapperXml;
*/
const javaModel = ref({});
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
id: "",
schemaName: "",
schemaNameId: "",
instanceName: "",
instanceId: "",
engine: "",
autoIncrement: "",
tableComment: "",
tableName: "",
tableCatalog: "",
tableRows: "",
tableCollation: "",
maxDataLength: "",
indexLength: "",
dataLength: "",
avgRowLength: "",
createOptions: "",
checkTime: "",
checksum: "",
dataFree: "",
rowFormat: "",
tableType: "",
createTime: "",
updateTime: "",
version: "",
});
const {
id,
schemaName,
schemaNameId,
instanceName,
instanceId,
engine,
autoIncrement,
tableComment,
tableName,
tableCatalog,
tableRows,
tableCollation,
maxDataLength,
indexLength,
dataLength,
avgRowLength,
createOptions,
checkTime,
checksum,
dataFree,
rowFormat,
tableType,
createTime,
updateTime,
version,
} = toRefs(form);
const tableColumnList = ref(null);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((schemaName.value = rowData.value.schemaName),
(id.value = rowData.value.id),
(schemaNameId.value = rowData.value.schemaNameId),
(instanceName.value = rowData.value.instanceName),
(instanceId.value = rowData.value.instanceId),
(engine.value = rowData.value.engine),
(autoIncrement.value = rowData.value.autoIncrement),
(tableComment.value = rowData.value.tableComment),
(tableName.value = rowData.value.tableName),
(tableCatalog.value = rowData.value.tableCatalog),
(tableRows.value = rowData.value.tableRows),
(tableCollation.value = rowData.value.tableCollation),
(maxDataLength.value = rowData.value.maxDataLength),
(indexLength.value = rowData.value.indexLength),
(dataLength.value = rowData.value.dataLength),
(avgRowLength.value = rowData.value.avgRowLength),
(createOptions.value = rowData.value.createOptions),
(checkTime.value = rowData.value.checkTime),
(checksum.value = rowData.value.checksum),
(dataFree.value = rowData.value.dataFree),
(rowFormat.value = rowData.value.rowFormat),
(tableType.value = rowData.value.tableType),
(createTime.value = rowData.value.createTime),
(updateTime.value = rowData.value.updateTime),
(version.value = rowData.value.version));
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value === "添加") {
res = await VE_API.system.schemaAdd(form);
} else {
res = await VE_API.system.schemaEdit({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code === "00") {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
/**
* 获取表字段
* @param tableIds
* @returns {Promise<void>}
*/
const getDatabaseTablesColumnList = async (tableIds) => {
let res = await VE_API.system.databaseTablesColumnList({
tableIds,
});
const { code } = res;
if (code === 0) {
res.data.map((item) => {
item.label = item.columnName;
item.value = item.columnName;
});
tableColumnList.value = res.data ? res.data : [];
}
};
onMounted(async () => {
VE_API.system.generateJavaModel({ id: rowData.value.id }).then((res) => {
const { code } = res;
console.log(code);
if (code === 0) {
javaModel.value = res.data;
}
});
await getDatabaseTablesColumnList(rowData.value.id);
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,526 @@
<template>
<el-drawer
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<el-form ref="form">
<el-descriptions
class="margin-top"
:column="3"
border
title="表详情"
>
<!-- <el-descriptions-item-->
<!-- label="id"-->
<!-- property="id"-->
<!-- class="contentClassName"-->
<!-- >-->
<!-- {{ this.rowData.id }}-->
<!-- </el-descriptions-item>-->
<el-descriptions-item label="数据库服务器ID">
<el-tag size="small"> {{ this.rowData.instanceId }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="数据库服务器名称">
<el-tag size="small">
{{ this.rowData.instanceName }}</el-tag
>
</el-descriptions-item>
<el-descriptions-item label="数据库名称">
{{ this.rowData.schemaName }}</el-descriptions-item
>
<!-- <el-descriptions-item label="数据库ID">-->
<!-- {{ this.rowData.schemaNameId }}</el-descriptions-item-->
<!-- >-->
<el-descriptions-item label="表引擎">
{{ this.rowData.engine }}</el-descriptions-item
>
<el-descriptions-item label="表名称">
{{ this.rowData.tableName }}</el-descriptions-item
>
<el-descriptions-item label="表名描述">
{{ this.rowData.tableComment }}</el-descriptions-item
>
</el-descriptions>
<el-descriptions border>
<el-descriptions-item label="表字段">
<span v-for="(item, index) in tableColumnList" :key="index"
>{{ item.columnName }} <el-divider direction="vertical"
/></span>
</el-descriptions-item>
<el-descriptions-item label="生成本地Java代码地址">
<el-tree-select
v-model="javaLocalPath"
:data="resourceData"
lazy
:load="loadNode"
:props="resourceProps"
filterable
check-strictly
:render-after-expand="false"
show-checkbox
check-on-click-node
/>
</el-descriptions-item>
</el-descriptions>
<el-form-item>
<el-button
v-permission="['java_code']"
@click.prevent="handleJavaCode(buttons.java_code.name, row)"
type="primary"
size="small"
>
{{ buttons.java_code.name }}
</el-button>
<el-button
v-permission="['export_insert_sql']"
@click.prevent="
exportInsertSql(buttons.export_insert_sql.name, row)
"
type="primary"
size="small"
>
{{ buttons.export_insert_sql.name }}
</el-button>
<el-button
v-permission="['export_upsert_sql']"
@click.prevent="
exportUpsertSql(buttons.export_upsert_sql.name, row)
"
type="primary"
size="small"
>
{{ buttons.export_upsert_sql.name }}
</el-button>
<el-button
v-permission="['export_table_excel']"
@click.prevent="
exporTableExcel(buttons.export_table_excel.name, row)
"
type="primary"
size="small"
>
{{ buttons.export_table_excel.name }}
</el-button>
</el-form-item>
<el-timeline>
<el-timeline-item timestamp="实体" placement="top">
<el-card>
<h4>表对应的实体</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaEntity }}
</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="实体对应xml" placement="top">
<el-card>
<h4>实体对应xml</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaMapperXml }}
</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="控制层" placement="top">
<el-card>
<h4>表对应的控制层</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaController }}
</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="业务接口" placement="top">
<el-card>
<h4>表对应的业务接口</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaService }}
</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="业务接口实现" placement="top">
<el-card>
<h4>表对应的业务接口实现</h4>
<p style="white-space: pre-wrap">
{{ javaModel.javaServiceImpl }}
</p>
</el-card>
</el-timeline-item>
</el-timeline>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()"
>确定</el-button
>
</span>
</template>
</el-form>
</el-drawer>
</template>
<script>
export default {
data: () => ({
description: "数据库表查询与设置",
buttons: {
search: { name: "查询" },
add: { name: "添加表" },
java_code: { name: "Java代码生成" },
edit: { name: "编辑" },
del: { name: "删除" },
more: { name: "更多" },
export_insert_sql: { name: "导出insert-sql" },
export_upsert_sql: { name: "导出upsert-sql" },
export_table_excel: { name: "导出表结构.excel" },
},
// type 0:目录 1菜单 2按钮
type: "1",
icon: "CopyDocument",
name: "数据库表查询与设置更多",
}),
};
</script>
<script setup>
import {
reactive,
toRefs,
ref,
defineProps,
defineEmits,
onMounted,
} from "vue";
const javaLocalPath = ref();
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
/**
*
private StringBuilder javaController;
private StringBuilder javaEntity;
private StringBuilder javaService;
private StringBuilder javaServiceImpl;
private StringBuilder javaMapper;
private StringBuilder javaMapperXml;
*/
const javaModel = ref({});
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
id: "",
schemaName: "",
schemaNameId: "",
instanceName: "",
instanceId: "",
engine: "",
autoIncrement: "",
tableComment: "",
tableName: "",
tableCatalog: "",
tableRows: "",
tableCollation: "",
maxDataLength: "",
indexLength: "",
dataLength: "",
avgRowLength: "",
createOptions: "",
checkTime: "",
checksum: "",
dataFree: "",
rowFormat: "",
tableType: "",
createTime: "",
updateTime: "",
version: "",
});
const {
id,
schemaName,
schemaNameId,
instanceName,
instanceId,
engine,
autoIncrement,
tableComment,
tableName,
tableCatalog,
tableRows,
tableCollation,
maxDataLength,
indexLength,
dataLength,
avgRowLength,
createOptions,
checkTime,
checksum,
dataFree,
rowFormat,
tableType,
createTime,
updateTime,
version,
} = toRefs(form);
const tableColumnList = ref(null);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((schemaName.value = rowData.value.schemaName),
(id.value = rowData.value.id),
(schemaNameId.value = rowData.value.schemaNameId),
(instanceName.value = rowData.value.instanceName),
(instanceId.value = rowData.value.instanceId),
(engine.value = rowData.value.engine),
(autoIncrement.value = rowData.value.autoIncrement),
(tableComment.value = rowData.value.tableComment),
(tableName.value = rowData.value.tableName),
(tableCatalog.value = rowData.value.tableCatalog),
(tableRows.value = rowData.value.tableRows),
(tableCollation.value = rowData.value.tableCollation),
(maxDataLength.value = rowData.value.maxDataLength),
(indexLength.value = rowData.value.indexLength),
(dataLength.value = rowData.value.dataLength),
(avgRowLength.value = rowData.value.avgRowLength),
(createOptions.value = rowData.value.createOptions),
(checkTime.value = rowData.value.checkTime),
(checksum.value = rowData.value.checksum),
(dataFree.value = rowData.value.dataFree),
(rowFormat.value = rowData.value.rowFormat),
(tableType.value = rowData.value.tableType),
(createTime.value = rowData.value.createTime),
(updateTime.value = rowData.value.updateTime),
(version.value = rowData.value.version));
/**
* handleJavaCode 事件
* @param title
* @param row
*/
const handleJavaCode = (title, row = rowData.value) => {
// showJavaCodeDialog.value = true;
// dialogTitle.value = title;
rowData.value = row;
VE_API.system
.generateLocalJava({
instanceId: rowData.value.instanceId,
schemaName: rowData.value.schemaName,
tableList: [rowData.value.tableName],
absolutePath: javaLocalPath.value,
})
.then((res) => {
const { code } = res;
if (code === 0) {
console.log(code);
}
});
};
/***
* 导出insert sql
* */
const exportInsertSql = async (title, row = rowData.value) => {
let res = await VE_API.system.tableExportInsertSql(row, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
};
/***
* 导出 upsert sql
* */
const exportUpsertSql = async (title, row = rowData.value) => {
let res = await VE_API.system.tableExportUpsertSql(row, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
};
/**
* 导出表结构
* */
const exporTableExcel = async (title, row = rowData.value) => {
let res = await VE_API.system.exportTableStructureExcel(row, {
responseType: "blob",
});
let fileName = res.headers["file-name"];
// 获取文件名
fileName = decodeURIComponent(fileName);
let url = window.URL.createObjectURL(new Blob([res.data]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// eslint-disable-next-line no-undef
link.setAttribute("download", fileName); //指定下载后的文件名,防跳转
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(link.href);
};
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value === "添加") {
res = await VE_API.system.schemaAdd(form);
} else {
res = await VE_API.system.schemaEdit({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
/**
* 获取表字段
* @returns {Promise<void>}
*/
const getDatabaseTablesColumnList = async () => {
let res = await VE_API.system.databaseTablesColumnList({
instanceId: rowData.value.instanceId,
schemaName: rowData.value.schemaName,
tableName: rowData.value.tableName,
});
const { code } = res;
if (code === 0) {
res.data.map((item) => {
item.label = item.columnName;
item.value = item.columnName;
});
tableColumnList.value = res.data ? res.data : [];
}
};
const resourceData = ref([]);
const params = reactive({
path: "/",
});
const resourceProps = {
label: "name",
children: "children",
isLeaf: "leaf",
};
const loadNode = async (resourceNode, resolve) => {
if (resourceNode.level === 0) {
return resolve([]);
}
resourceNode.loaded = true;
var resourceNodeData = resourceNode.data;
if (resourceNodeData.isFile) {
return resolve([]);
}
console.log(resourceNodeData);
const { code, data } = await VE_API.system.playFileResourceList({
path:
"/" === resourceNodeData.rootPath
? "/" + resourceNodeData.name
: resourceNodeData.rootPath + "/" + resourceNodeData.name,
});
if (code === 0) {
if (data == null) {
return resolve([]);
}
data.map((item) => {
item.value = item.absolutePath;
});
// resourceNode.childNodes = data;
console.log(resourceNode);
return resolve(data);
}
};
/**
* @description: 获取列表数据
* @param {*}
* @return {*}
*/
const getFileList = async () => {
const { code, data } = await VE_API.system.playFileResourceList(params);
if (code === 0) {
data.map((item) => {
item.value = item.absolutePath;
});
resourceData.value = data;
}
};
onMounted(async () => {
await getFileList();
VE_API.system
.generateJavaModel({
instanceId: rowData.value.instanceId,
schemaName: rowData.value.schemaName,
tableName: rowData.value.tableName,
})
.then((res) => {
const { code } = res;
console.log(code);
if (code === 0) {
javaModel.value = res.data;
}
});
await getDatabaseTablesColumnList();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,214 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- <span>{{ rowData }}</span> -->
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="80px"
:inline="false"
>
<el-form-item label="项目名称" prop="projectName">
<el-input
v-model="projectName"
placeholder=""
clearable
></el-input>
</el-form-item>
<el-form-item label="数据库实例" prop="instanceId">
<el-select
v-model="instanceId"
placeholder="数据库实例"
clearable
>
<el-option
v-for="item in serverList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="项目拥有者" prop="owner">
<el-input v-model="owner" placeholder="" clearable></el-input>
</el-form-item>
<el-form-item label="项目版本" prop="version">
<el-input v-model="version" placeholder="" clearable></el-input>
</el-form-item>
<el-form-item label="ORM框架" prop="ormFrameEnums">
<el-select
v-model="ormFrameEnums"
placeholder="ORM框架"
clearable
>
<el-option label="Lazy" value="UPSERT" />
<el-option label="mybatis" value="MYBATIS" />
<el-option label="JPA" value="JPA" />
</el-select>
</el-form-item>
<el-form-item label="UI框架" prop="uiFrameEnums">
<el-select
v-model="uiFrameEnums"
placeholder="UI框架"
clearable
>
<el-option label="VUE" value="VUE" />
</el-select>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {
reactive,
toRefs,
ref,
defineProps,
defineEmits,
onMounted,
} from "vue";
const rules = {
projectName: [
{
required: true,
message: "请输入服务器名称",
trigger: "blur",
},
],
instanceId: [
{
required: true,
message: "请选择数据库服务器",
trigger: "blur",
},
],
version: [
{
required: true,
message: "请输入项目版本",
trigger: "blur",
},
],
ormFrameEnums: [
{
required: true,
message: "请选择ORM框架",
trigger: "change",
},
],
owner: [
{
required: true,
message: "请输入项目拥有者",
trigger: "change",
},
],
uiFrameEnums: [
{
required: true,
message: "请输入项目UI框架",
trigger: "change",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
projectName: "",
instanceId: "",
version: "",
ormFrameEnums: "",
uiFrameEnums: "",
owner: "",
});
const { projectName, instanceId, version, ormFrameEnums, uiFrameEnums, owner } =
toRefs(form);
const serverList = ref(null);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((projectName.value = rowData.value.projectName),
(instanceId.value = rowData.value.instanceId),
(version.value = rowData.value.version),
(ormFrameEnums.value = rowData.value.ormFrameEnums),
(owner.value = rowData.value.owner),
(uiFrameEnums.value = rowData.value.uiFrameEnums));
onMounted(async () => {
// 查询数据库实例
let res = await VE_API.system.databaseInstanceList();
res.data.map((item) => {
item.label = item.instanceName;
item.value = item.id;
serverList.value = res.data ? res.data : [];
});
});
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value == "添加") {
res = await VE_API.system.projectAdd(form);
} else {
res = await VE_API.system.projectEdit({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code == "00") {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,198 @@
<template>
<el-dialog
:title="title"
append-to-body
destroy-on-close
:model-value="showDialog"
@close="closeDialog()"
>
<!-- 表单 -->
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="120px"
:inline="false"
>
<el-form-item label="服务器名称" prop="instanceName">
<el-input
v-model="instanceName"
placeholder=""
clearable
></el-input>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input
v-model="username"
placeholder="root"
clearable
></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="password"
placeholder=""
clearable
></el-input>
</el-form-item>
<el-form-item label="host" prop="host">
<el-input
v-model="host"
placeholder="127.0.0.1"
clearable
></el-input>
</el-form-item>
<el-form-item label="端口" prop="port">
<el-input
v-model="port"
placeholder="6379"
clearable
></el-input>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-input v-model="status" placeholder="" clearable></el-input>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="sort" placeholder="" clearable></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<span>
<el-button style="float: left" @click="testConnection()"
>测试连接</el-button
>
<el-button @click="closeDialog()">取消</el-button>
<el-button type="primary" @click="onSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { reactive, toRefs, ref, defineProps, defineEmits } from "vue";
const rules = {
instanceName: [
{
required: true,
message: "请输入服务器名称",
trigger: "blur",
},
],
username: [
{
required: true,
message: "请输入账户",
trigger: "blur",
},
],
host: [
{
required: true,
message: "host不能为空",
trigger: "change",
},
],
port: [
{
required: true,
message: "端口不能为空",
trigger: "change",
},
],
driverClassName: [
{
required: true,
message: "driverClassName",
trigger: "change",
},
],
};
const props = defineProps({
showDialog: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "添加",
},
rowData: {
type: Object,
default: null,
},
});
const emit = defineEmits(["closeDialog"]);
const { title, rowData } = toRefs(props);
const closeDialog = () => {
emit("closeDialog", false);
};
const formRef = ref(null);
const form = reactive({
instanceName: "",
username: "root",
password: "",
host: "127.0.0.1",
port: 6379,
status: "",
sort: 1,
});
const { instanceName, username, password, host, port, status, sort } =
toRefs(form);
/**
* @description: 初始化
* @param {*}
* @return {*}
*/
rowData.value &&
((instanceName.value = rowData.value.instanceName),
(username.value = rowData.value.username),
(password.value = rowData.value.password),
(host.value = rowData.value.host),
(port.value = rowData.value.port),
(status.value = rowData.value.status),
(sort.value = rowData.value.sort));
/**
* 测试连接
*/
const testConnection = async () => {
let res = await VE_API.system.redisInstanceTest(form);
const { code } = res;
if (code === 0) {
console.log("连接成功");
}
};
/**
* @description:提交
* @param {*}
* @return {*}
*/
const onSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
let res;
if (title.value === "添加Redis实例") {
res = await VE_API.system.redisInstanceStory(form);
} else {
res = await VE_API.system.redisInstanceStory({
id: rowData.value.id,
...form,
});
}
const { code } = res;
if (code === 0) {
closeDialog();
}
} else {
console.log("error submit!!");
return false;
}
});
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,8 @@
const acwClientMenu = {
description: "自动生成代码客户端管理",
// type 0:目录 1菜单 2按钮
type: "1",
icon: "Lollipop",
name: "自动生成代码客户端管理",
};
export default acwClientMenu;

Some files were not shown because too many files have changed in this diff Show More