ER图测试,域账号登录测试,修改数据查询的展示问题

This commit is contained in:
暮光:城中城
2021-08-02 21:18:58 +08:00
parent 83ca189598
commit 51f74f60c3
34 changed files with 1795 additions and 89 deletions

View File

@@ -1 +0,0 @@
.about-zyplayer-doc{text-align:left;line-height:normal}.about-zyplayer-doc .el-dialog__body{padding:20px}#app,.el-container,.el-menu{height:100%}.el-header{background-color:#1d4e89!important}.database-list-tree{overflow-x:auto;min-height:150px}.database-list-tree,.database-list-tree .el-loading-mask{background-color:#fafafa}.database-list-tree .el-tree-node>.el-tree-node__children{overflow:unset}.el-tree-node__content .el-icon-more{margin-left:5px;color:#606266;font-size:12px;display:none;padding:2px 5px}.el-tree-node__content:hover .el-icon-more{display:inline-block}.login-container{border-radius:5px;-moz-border-radius:5px;background-clip:padding-box;margin:0 auto;width:350px;padding:35px 35px 15px 35px;background:#fff;border:1px solid #eaeaea;-webkit-box-shadow:0 0 25px #cac6c6;box-shadow:0 0 25px #cac6c6}.title{margin:0 auto 40px auto;text-align:center;color:#505458}.remember{margin:0 0 35px 0}.my-info-vue .box-card{margin:10px}.table-info-vue{padding:0 20px}.table-info-vue .el-dialog__body{padding:0 20px 10px}.table-info-vue .el-form-item{margin-bottom:5px}.table-info-vue .edit-table-desc{cursor:pointer;color:#409eff}.table-info-vue .description{cursor:pointer;min-height:23px}.table-info-vue .el-table td,.table-info-vue .el-table th{padding:5px 0}.table-info-vue .status-info-row{padding:8px 0}.table-info-vue .status-info-row .label{width:80px;display:inline-block;text-align:right;color:#606266}.table-database-vue .el-table td,.table-database-vue .el-table th{padding:5px 0}.table-database-vue .label{width:140px;text-align:right}.table-database-vue .el-table th,.table-procedure-edit-vue .el-table td,.table-procedure-vue .el-table td{padding:5px 0}body,html{margin:0;padding:0;height:100%}.header-right-user-name{color:#fff;padding-right:5px}.el-menu-vertical{border-right:0}.el-menu-vertical,.el-menu-vertical .el-menu{background:#fafafa}.el-header{background-color:#409eff;color:#333;line-height:40px;text-align:right;height:40px!important}.data-executor-vue .ace-monokai .ace_print-margin{display:none}.data-executor-vue .el-card__body{padding:10px}.data-executor-vue .sql-params .el-input-group{width:auto;margin:10px 10px 0 0}.data-executor-vue .sql-params .el-input__inner{width:200px}.data-executor-vue .el-table td,.el-table th{padding:6px 0}.data-executor-vue .execute-result-table .el-input__inner{height:25px;line-height:25px;padding:0 5px}.data-executor-vue .execute-result-table .el-textarea__inner{height:27px;min-height:27px;line-height:25px;padding:0 5px;resize:none}.data-executor-vue .sql-content-line{margin:0;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.data-executor-vue .execute-use-time{font-size:12px;margin-right:10px}.data-executor-vue-out .el-tabs__nav-scroll{padding-left:20px}.data-executor-vue-out .el-button+.el-button{margin-left:0}.data-executor-vue-out .el-table__body-wrapper{height:calc(100vh - 180px);overflow-y:auto}.data-transfer-vue .el-button+.el-button{margin-left:4px}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=favicon-db.png><title>数据库文档管理</title><link href=css/chunk-vendors.8924efc6.css rel=preload as=style><link href=css/index.ae7c4106.css rel=preload as=style><link href=js/chunk-vendors.22b87709.js rel=preload as=script><link href=js/index.9af4c217.js rel=preload as=script><link href=css/chunk-vendors.8924efc6.css rel=stylesheet><link href=css/index.ae7c4106.css rel=stylesheet></head><body><noscript><strong>We're sorry but zyplayer-db-ui doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.22b87709.js></script><script src=js/index.9af4c217.js></script></body></html>
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=favicon-db.png><title>数据库文档管理</title><link href=css/chunk-vendors.8924efc6.css rel=preload as=style><link href=css/index.bb15836f.css rel=preload as=style><link href=js/chunk-vendors.c63802ce.js rel=preload as=script><link href=js/index.73fd47e8.js rel=preload as=script><link href=css/chunk-vendors.8924efc6.css rel=stylesheet><link href=css/index.bb15836f.css rel=stylesheet></head><body><noscript><strong>We're sorry but zyplayer-db-ui doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.c63802ce.js></script><script src=js/index.73fd47e8.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -40,6 +40,10 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
<!-- 在线文档解析页面 -->
<!--zyplayer-doc-core-->
<dependency>

View File

@@ -0,0 +1,30 @@
package com.zyplayer.doc.manage.framework.config;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
@Configurable
public class LdapConfig {
String url = "ldap://10.0.1.1:10389";
String base = "dc=xx,dc=net";
String userDn = "cn=Manager,dc=xx,dc=net";
String password = "MKDSHYDNIS";
/**
* 初始化
*/
@Bean
public LdapTemplate getLdapTemplate() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(url);
contextSource.setBase(base);
contextSource.setUserDn(userDn);
contextSource.setPassword(password);
contextSource.setPooled(false);
contextSource.afterPropertiesSet();
return new LdapTemplate(contextSource);
}
}

View File

@@ -6,41 +6,75 @@ import com.zyplayer.doc.core.json.DocResponseJson;
import com.zyplayer.doc.data.config.security.DocUserDetails;
import com.zyplayer.doc.data.config.security.DocUserUtil;
import com.zyplayer.doc.data.repository.manage.entity.UserInfo;
import com.zyplayer.doc.data.service.manage.AuthInfoService;
import com.zyplayer.doc.data.service.manage.UserAuthService;
import com.zyplayer.doc.data.service.manage.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import com.zyplayer.doc.manage.web.manage.param.LdapPerson;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.DirContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* 用户登录控制器
*/
@RestController
public class LoginController {
@Autowired
@Resource
private UserInfoService userInfoService;
@Autowired
@Resource
private UserAuthService userAuthService;
@Autowired
private AuthInfoService authInfoService;
@Resource
private LdapTemplate ldapTemplate;
// TODO 域账号登录,待测试
@Value("${spring.ldap.domainName:}")
private String ldapDomainName;
@Value("${spring.ldap.urls:}")
private String ldapUrls;
/**
* 用户登录
*/
@PostMapping(value = "/login")
public DocResponseJson<Object> login(String username, String password, HttpServletResponse response) {
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_no", username);
queryWrapper.eq("del_flag", 0);
UserInfo userInfo = userInfoService.getOne(queryWrapper);
// 如果使用域账号登录
if (this.isUseLdapServer()) {
LdapPerson ldapPerson = this.getUserFromLdap(username, password);
if (null == ldapPerson) {
return DocResponseJson.warn("用户名或密码错误");
}
if (userInfo == null) {
userInfo = this.ldapAutoRegister(ldapPerson);
}
} else {
if (userInfo == null) {
return DocResponseJson.warn("用户名'" + username + "'没有找到!");
}
String pwdMd5 = DigestUtils.md5DigestAsHex(password.getBytes());
if (!Objects.equals(userInfo.getPassword(), pwdMd5)) {
return DocResponseJson.warn("密码错误");
return DocResponseJson.warn("用户名或密码错误");
}
}
Set<String> userAuthSet = userAuthService.getUserAuthSet(userInfo.getId());
String accessToken = RandomUtil.simpleUUID();
@@ -52,7 +86,7 @@ public class LoginController {
cookie.setDomain("zyplayer.com");
cookie.setMaxAge(60 * 60 * 24);
response.addCookie(cookie);
// 再搞一份当前路劲的cookie
// 再搞一份当前域名的cookie
cookie = new Cookie("accessToken", accessToken);
cookie.setPath("/");
cookie.setMaxAge(60 * 60 * 24);
@@ -60,9 +94,77 @@ public class LoginController {
return DocResponseJson.ok();
}
/**
* 退出登录
*/
@PostMapping(value = "/logout")
public DocResponseJson<Object> logout() {
DocUserUtil.logout();
return DocResponseJson.ok();
}
/**
* 域账户注册
*/
private UserInfo ldapAutoRegister(LdapPerson ldapPerson) {
UserInfo userInfo = new UserInfo();
userInfo.setEmail(ldapPerson.getEmail());
userInfo.setPassword("LDAP");
userInfo.setUserName(ldapPerson.getName());
userInfo.setUserNo(ldapPerson.getsAMAccountName());
userInfo.setSex(1);
userInfoService.save(userInfo);
return userInfo;
}
/**
* 是否使用域账号登录
*/
public boolean isUseLdapServer() {
return StringUtils.isNotEmpty(ldapUrls);
}
/**
* 鉴别域账号中是否有该用户
*/
public LdapPerson getUserFromLdap(String username, String password) {
if (StringUtils.endsWithIgnoreCase(username, ldapDomainName)) {
username = username.replaceAll("(?i)" + ldapDomainName, "");
}
String userDn = username + ldapDomainName;
DirContext dirContext = null;
try {
dirContext = ldapTemplate.getContextSource().getContext(userDn, password);
List<LdapPerson> search = ldapTemplate.search(
LdapQueryBuilder.query().where("objectClass").is("person").and("sAMAccountName").is(username),
(AttributesMapper<LdapPerson>) attributes -> {
LdapPerson person = new LdapPerson();
person.setName(this.getAttributeValue(attributes.get("cn")));
person.setsAMAccountName(this.getAttributeValue(attributes.get("sAMAccountName")));
person.setEmail(this.getAttributeValue(attributes.get("userPrincipalName")));
return person;
});
if (CollectionUtils.isNotEmpty(search)) {
return search.get(0);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != dirContext) {
LdapUtils.closeContext(dirContext);
}
}
return null;
}
/**
* 取值
*/
private String getAttributeValue(Attribute attribute) throws NamingException {
if (attribute != null) {
Object obj = attribute.get(0);
return obj == null ? null : obj.toString();
}
return null;
}
}

View File

@@ -0,0 +1,74 @@
/*
* <<
* Davinci
* ==
* Copyright (C) 2016 - 2019 EDP
* ==
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* >>
*
*/
package com.zyplayer.doc.manage.web.manage.param;
public class LdapPerson {
/**
* 姓名
*/
private String name;
/**
* 用户名
*/
private String sAMAccountName;
/**
* 邮箱
*/
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getsAMAccountName() {
return sAMAccountName;
}
public void setsAMAccountName(String sAMAccountName) {
this.sAMAccountName = sAMAccountName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LdapPerson(String name, String sAMAccountName, String email) {
this.name = name;
this.sAMAccountName = sAMAccountName;
this.email = email;
}
public LdapPerson() {
}
}

View File

@@ -2,8 +2,8 @@
ENV = 'development'
# base api
VUE_APP_BASE_API = 'http://local.zyplayer.com:8083/zyplayer-doc-manage'
# VUE_APP_BASE_API = 'http://doc.zyplayer.com/zyplayer-doc-manage'
# VUE_APP_BASE_API = 'http://local.zyplayer.com:8083/zyplayer-doc-manage'
VUE_APP_BASE_API = 'http://doc.zyplayer.com/zyplayer-doc-manage'
VUE_CLI_BABEL_TRANSPILE_MODULES = true

View File

@@ -4,6 +4,27 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@antv/x6": {
"version": "1.25.0",
"resolved": "https://registry.nlark.com/@antv/x6/download/@antv/x6-1.25.0.tgz?cache=0&sync_timestamp=1627353635830&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40antv%2Fx6%2Fdownload%2F%40antv%2Fx6-1.25.0.tgz",
"integrity": "sha1-/0nd5n3OIP2EFOHd8luJPOEqdZk=",
"requires": {
"csstype": "^3.0.3",
"jquery": "^3.5.1",
"jquery-mousewheel": "^3.1.13",
"lodash-es": "^4.17.15",
"mousetrap": "^1.6.5",
"utility-types": "^3.10.0"
}
},
"@antv/x6-vue-shape": {
"version": "1.2.8",
"resolved": "https://registry.nlark.com/@antv/x6-vue-shape/download/@antv/x6-vue-shape-1.2.8.tgz",
"integrity": "sha1-4XXUmNvSOM/oRVJnQlOinUpggs8=",
"requires": {
"vue-demi": "^0.7.4"
}
},
"@babel/code-frame": {
"version": "7.5.5",
"resolved": "https://registry.npm.taobao.org/@babel/code-frame/download/@babel/code-frame-7.5.5.tgz",
@@ -1482,6 +1503,21 @@
}
}
},
"@vue/composition-api": {
"version": "1.0.4",
"resolved": "https://registry.nlark.com/@vue/composition-api/download/@vue/composition-api-1.0.4.tgz",
"integrity": "sha1-YWKzujBqiZWv3Olppm4WjmdDcY0=",
"requires": {
"tslib": "^2.3.0"
},
"dependencies": {
"tslib": {
"version": "2.3.0",
"resolved": "https://registry.nlark.com/tslib/download/tslib-2.3.0.tgz",
"integrity": "sha1-gDuM2rPhK6WBpMpByIObuw2ssJ4="
}
}
},
"@vue/preload-webpack-plugin": {
"version": "1.1.1",
"resolved": "https://registry.npm.taobao.org/@vue/preload-webpack-plugin/download/@vue/preload-webpack-plugin-1.1.1.tgz",
@@ -3608,6 +3644,11 @@
"css-tree": "1.0.0-alpha.37"
}
},
"csstype": {
"version": "3.0.8",
"resolved": "https://registry.nlark.com/csstype/download/csstype-3.0.8.tgz",
"integrity": "sha1-0iZqeScp+yJ80hb7Vy9Dco4a00A="
},
"current-script-polyfill": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/current-script-polyfill/download/current-script-polyfill-1.0.0.tgz",
@@ -4039,7 +4080,7 @@
},
"domhandler": {
"version": "2.4.2",
"resolved": "https://registry.npm.taobao.org/domhandler/download/domhandler-2.4.2.tgz",
"resolved": "http://registry.npm.taobao.org/domhandler/download/domhandler-2.4.2.tgz",
"integrity": "sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=",
"dev": true,
"requires": {
@@ -5868,7 +5909,7 @@
},
"htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.10.1.tgz",
"resolved": "http://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.10.1.tgz",
"integrity": "sha1-vWedw/WYl7ajS7EHSchVu1OpOS8=",
"dev": true,
"requires": {
@@ -6481,6 +6522,16 @@
}
}
},
"jquery": {
"version": "3.6.0",
"resolved": "https://registry.npm.taobao.org/jquery/download/jquery-3.6.0.tgz?cache=0&sync_timestamp=1614706801583&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjquery%2Fdownload%2Fjquery-3.6.0.tgz",
"integrity": "sha1-xyoJ8Vwb3OFC9J2/EXC9+K2sJHA="
},
"jquery-mousewheel": {
"version": "3.1.13",
"resolved": "https://registry.nlark.com/jquery-mousewheel/download/jquery-mousewheel-3.1.13.tgz",
"integrity": "sha1-BvAzXxbjU6aV5yBr9QUDy1I6buU="
},
"js-cookie": {
"version": "2.2.1",
"resolved": "https://registry.npm.taobao.org/js-cookie/download/js-cookie-2.2.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-cookie%2Fdownload%2Fjs-cookie-2.2.1.tgz",
@@ -6884,6 +6935,11 @@
"resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.15.tgz",
"integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg="
},
"lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.nlark.com/lodash-es/download/lodash-es-4.17.21.tgz",
"integrity": "sha1-Q+YmxG5lkbd1C+srUBFzkMYJ4+4="
},
"lodash.defaultsdeep": {
"version": "4.6.1",
"resolved": "https://registry.npm.taobao.org/lodash.defaultsdeep/download/lodash.defaultsdeep-4.6.1.tgz",
@@ -7308,6 +7364,11 @@
}
}
},
"mousetrap": {
"version": "1.6.5",
"resolved": "https://registry.nlark.com/mousetrap/download/mousetrap-1.6.5.tgz",
"integrity": "sha1-inZtjCcrCDk9X1YHTgtewYNIW/k="
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "http://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz",
@@ -10720,6 +10781,11 @@
"integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
"dev": true
},
"utility-types": {
"version": "3.10.0",
"resolved": "https://registry.nlark.com/utility-types/download/utility-types-3.10.0.tgz",
"integrity": "sha1-6kFI+adBAV8F7XT9YV4dIOa+2Cs="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "http://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz",
@@ -10789,6 +10855,11 @@
"clipboard": "^2.0.0"
}
},
"vue-demi": {
"version": "0.7.5",
"resolved": "https://registry.nlark.com/vue-demi/download/vue-demi-0.7.5.tgz",
"integrity": "sha1-iN7nSS/Jmg+RH/A/wCxlj6Knmvg="
},
"vue-hljs": {
"version": "1.1.2",
"resolved": "https://registry.npm.taobao.org/vue-hljs/download/vue-hljs-1.1.2.tgz",

View File

@@ -7,6 +7,9 @@
"build": "vue-cli-service build --mode production"
},
"dependencies": {
"@antv/x6": "^1.25.0",
"@antv/x6-vue-shape": "^1.1.6",
"@vue/composition-api": "^1.0.4",
"axios": "^0.19.0",
"brace": "^0.11.1",
"codemirror": "^5.61.1",

View File

@@ -0,0 +1,26 @@
.er-editor-demo-container {
width: 100%;
height: 100%;
background-color: #fafafa;
.minimap-container {
position: absolute;
top: 10px;
right: 10px;
/** X6推荐覆写样式 */
.x6-widget-minimap {
.x6-graph {
box-shadow: 0 0 0 0 #ffffff;
}
.x6-widget-minimap-viewport {
border: 1px solid rgba(0, 0, 0, 0.1);
.x6-widget-minimap-viewport-zoom {
border: 1px solid rgba(0, 0, 0, 0.1);
}
}
}
}
}

View File

@@ -0,0 +1,88 @@
<template>
<div class="er-editor-demo-container">
<div
id="refContainer"
ref="refContainer"
style="width: 100%; height: 100%"/>
<div id="refMinimapContainer" ref="refMinimapContainer" class="minimap-container" />
</div>
</template>
<script lang="js">
import './index.less'
import { BaseGraph } from '../xflow/index' // GraphData, GraphOptions
export default {
name: 'index',
components: {
},
props: {
graphOptions: {
type: Object,
default: null,
required: false
},
graphData: {
type: Object,
default: null,
required: false
}
},
data () {
return {
// graphContainer: document.getElementById('refContainer'),
// minimapContainer: document.getElementById('refMinimapContainer'),
baseGraph: null
}
},
mounted () {
const that = this
setTimeout(() => {
/** 初始化画布 */
this.baseGraph = new BaseGraph({
...this.graphOptions,
container: document.getElementById('refContainer'),
grid: {
visible: false
},
minimap: {
enabled: true,
container: document.getElementById('refMinimapContainer'),
minScale: 0.5,
maxScale: 2
}
})
/** 渲染画布内容 */
this.baseGraph.updateGraph(that.graphData)
}, 100)
},
methods: {
onHandleToolbar (action) {
switch (action) {
case 'in':
console.log('in')
this.baseGraph.zoomGraph(0.1)
break
case 'out':
console.log('out')
this.baseGraph.zoomGraph(-0.1)
break
case 'fit':
console.log('fit')
this.baseGraph.zoomGraph('fit')
break
case 'real':
console.log('real')
this.baseGraph.zoomGraph('real')
break
default:
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,113 @@
.entity-container {
width: calc(100% - 2px);
height: calc(100% - 2px);
border-radius: 2px;
background-color: white;
&.fact {
border: 1px solid #ced4de;
&:hover {
border: 1px solid #1890ff;
}
}
&.dim {
border: 1px solid #cdddfd;
&:hover {
border: 1px solid #1890ff;
}
}
&.other {
border: 1px solid #decfea;
&:hover {
border: 1px solid #1890ff;
}
}
.content {
margin: 1px 1px;
width: calc(100% - 2px);
height: calc(100% - 2px);
display:block;
&.fact {
background-color: #ced4de;
}
&.dim {
background-color: #cdddfd;
}
&.other {
background-color: #decfea;
}
.head {
width: calc(100% - 12px);
height: 38px;
margin-left: 6px;
font-size: 12px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
.type {
padding-right: 8px;
}
.more {
cursor: pointer;
}
}
.body {
width: calc(100% - 12px);
height: calc(100% - 36px - 6px);
margin-left: 6px;
margin-bottom: 6px;
background-color: white;
overflow: auto;
cursor: pointer;
//float:clear;
.body-item {
width: 100%;
height: 28px;
font-size: 12px;
color: #595959;
border-bottom: 1px solid rgba(206, 212, 222, 0.2);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
.name {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
margin-left: 6px;
.pk,
.fk {
width: 12px;
font-family: HelveticaNeue-CondensedBold;
color: #ffd666;
margin-right: 6px;
}
}
.type {
color: #bfbfbf;
font-size: 8px;
margin-right: 8px;
}
}
}
}
}

View File

@@ -0,0 +1,48 @@
<template>
<div class="entity-container fact">
<div class="content other">
<div class="head">
<div>
<a-icon type="bars" class="type"/>
<span>{{ entity.name }}</span>
</div>
<a-icon type="ellipsis" class="more" />
</div>
<div class="body">
<div class="body-item" v-for="(item,index) in entity.properties" :key="index">
<div class="name">
<!-- {{ value.isPK }} && <span class="pk">PK</span>-->
<!-- {{value.isFK }} && <span class="fk">FK</span>-->
{{ item.name }}
</div>
<div class="type">{{ item.propertyType }}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import './Entity.less'
export default {
name: 'Entity',
props: {
entity: {
type: Object,
default: null,
required: true
}
},
mounted () {
console.log(this.entity, 'this.entity')
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,75 @@
<template>
<div style="width: 100%; height: 650px">
<ERGraph :graphData="graphData"/>
</div>
</template>
<script lang="js">
import ERGraph from '../ERGraph'
import { mockEntityData, mockRelationData } from './mock'
import Entity from './Entity'
export default {
name: 'index',
components: {
ERGraph
},
data () {
return {
graphData: {
nodes: '',
edges: ''
}
}
},
mounted () {
this.calRenderData()
},
methods: {
calRenderData () {
this.graphData.nodes = mockEntityData.map((entity) => {
const { entityId, x, y, width, height } = entity
return {
x,
y,
width,
height,
id: entityId,
component: {
template: '<Entity :entity="entity"/>',
data () {
return {
entity: entity
}
},
components: {
Entity
}
}
// data: entity
}
}
)
this.graphData.edges = mockRelationData.map((relation) => {
const { relationId, sourceEntityId, targetEntityId } = relation
return {
id: relationId,
source: sourceEntityId,
target: targetEntityId,
label: '1:N',
// render: (data: RelationCanvasModel) => {
// return null;
// },
data: relation
}
}
)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,193 @@
// import {
// EntityProperty,
// EntityCanvasModel,
// RelationCanvasModel
// } from './interface'
// : EntityProperty[]
export const mockProperties = [
{
propertyId: 'propertyId1',
name: '业务日期',
propertyType: 'string',
isPK: true
},
{
propertyId: 'propertyId2',
name: '交易号1',
propertyType: 'bigint',
isFK: true
},
{
propertyId: 'propertyId3',
name: '最长显示的表单名最长显示的表单名',
propertyType: 'string'
},
{
propertyId: 'propertyId4',
name: '交易支付外键',
propertyType: 'string'
},
{
propertyId: 'propertyId5',
name: '卖家支付日期',
propertyType: 'string'
},
{
propertyId: 'propertyId6',
name: '网商银行',
propertyType: 'string'
},
{
propertyId: 'propertyId7',
name: '业务日期',
propertyType: 'string'
},
{
propertyId: 'propertyId8',
name: '业务日期111',
propertyType: 'string'
},
{
propertyId: 'propertyId9',
name: '业务日期222',
propertyType: 'string'
},
{
propertyId: 'propertyId10',
name: '业务日期333',
propertyType: 'string'
}
]
// : EntityCanvasModel[]
export const mockEntityData = [
{
entityId: 'fact_1',
name: '事实表',
entityType: 'FACT',
properties: mockProperties,
x: 550,
y: 400,
width: 214,
height: 248
},
{
entityId: 'fact_up',
name: '事实表',
entityType: 'FACT',
properties: mockProperties,
x: 100,
y: 100,
width: 214,
height: 248
},
{
entityId: 'dim_up',
name: '维度表',
entityType: 'DIM',
properties: mockProperties,
x: 100,
y: 400,
width: 214,
height: 248
},
{
entityId: 'other_up',
name: '其他表',
entityType: 'OTHER',
properties: mockProperties,
x: 100,
y: 700,
width: 214,
height: 248
},
{
entityId: 'other_down',
name: '其他表',
entityType: 'OTHER',
properties: mockProperties,
x: 900,
y: 0,
width: 214,
height: 248
},
{
entityId: 'fact_down1',
name: '事实表',
entityType: 'FACT',
properties: mockProperties,
x: 900,
y: 280,
width: 214,
height: 248
},
{
entityId: 'dim_down',
name: '维度表',
entityType: 'DIM',
properties: mockProperties,
x: 900,
y: 580,
width: 214,
height: 248
},
{
entityId: 'fact_down2',
name: '事实表',
entityType: 'FACT',
properties: mockProperties,
x: 900,
y: 860,
width: 214,
height: 248
}
]
// : RelationCanvasModel[]
export const mockRelationData = [
{
relationId: 'relationId_1',
sourceEntityId: 'fact_up',
targetEntityId: 'fact_1'
},
{
relationId: 'relationId_2',
sourceEntityId: 'fact_1',
targetEntityId: 'fact_up'
},
{
relationId: 'relationId_1_loop',
sourceEntityId: 'fact_1',
targetEntityId: 'fact_1'
},
{
relationId: 'relationId_2',
sourceEntityId: 'dim_up',
targetEntityId: 'fact_1'
},
{
relationId: 'relationId_3',
sourceEntityId: 'other_up',
targetEntityId: 'fact_1'
},
{
relationId: 'relationId_4',
sourceEntityId: 'fact_1',
targetEntityId: 'other_down'
},
{
relationId: 'relationId_5',
sourceEntityId: 'fact_1',
targetEntityId: 'fact_down1'
},
{
relationId: 'relationId_6',
sourceEntityId: 'fact_1',
targetEntityId: 'dim_down'
},
{
relationId: 'relationId_7',
sourceEntityId: 'fact_1',
targetEntityId: 'fact_down2'
}
]

View File

@@ -0,0 +1,5 @@
.erGraphDemo {
width: 100%;
height: 100vh;
overflow: hidden;
}

View File

@@ -0,0 +1,46 @@
<template>
<div class="styles.erGraphDemo">
<ERGraphDemo />
</div>
</template>
<script lang="js">
import styles from './index.less'
import ERGraphDemo from './ERGraphDemo'
export default {
name: 'index',
components: {
ERGraphDemo
},
data () {
return {
visible: false,
graph: '',
styles: styles
}
},
mounted () {
},
methods: {
showDrawer () {
// setTimeout(() => {
// this.initGraph()
// }, 100)
this.visible = true
},
onClose () {
// this.graph.dispose()
this.visible = false
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,87 @@
<template>
<div style="padding: 10px;">
<div ref="graphContainer"></div>
</div>
</template>
<script>
import {Graph} from '@antv/x6';
export default {
data() {
return {
graphData: {
// 节点
nodes: [
{
id: 'node1', // String可选节点的唯一标识
x: 40, // Number必选节点位置的 x 值
y: 40, // Number必选节点位置的 y 值
width: 80, // Number可选节点大小的 width 值
height: 40, // Number可选节点大小的 height 值
label: 'hello', // String节点标签
},
{
id: 'node2', // String节点的唯一标识
x: 160, // Number必选节点位置的 x 值
y: 180, // Number必选节点位置的 y 值
width: 80, // Number可选节点大小的 width 值
height: 40, // Number可选节点大小的 height 值
label: 'world', // String节点标签
},
],
// 边
edges: [
{
source: 'node1', // String必须起始节点 id
target: 'node2', // String必须目标节点 id
},
],
},
graph: {},
};
},
mounted () {
this.graph = new Graph({
container: this.$refs.graphContainer,
height: 600,
scroller: {
enabled: true,
pannable: true,
pageBreak: false,
},
grid: {
size: 10,
visible: true
}
});
this.graph.fromJSON(this.graphData);
this.graph.centerContent();
},
methods: {
addNode(){
this.graph.createNode({
shape: 'org-node',
attrs: {
'.card': { fill: background },
'.image': { xlinkHref: image },
'.rank': {
fill: textColor,
text: Dom.breakText(rank, { width: 160, height: 45 }),
},
'.name': {
fill: textColor,
text: Dom.breakText(name, { width: 160, height: 45 }),
},
'.btn > circle': { stroke: textColor },
'.btn > text': { fill: textColor, stroke: textColor },
},
})
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,220 @@
// import { Graph } from '@antv/x6'
import { Edge } from '@antv/x6'
//
// import '@antv/x6-react-shape'
// import X6BaseGraph from '../graph/baseGraph'
// import Node from '../graph/node'
// import Edge from '../graph/edge'
// import { NodeConfig, EdgeConfig } from '../interface/graph-core'
import _ from 'lodash'
import '@antv/x6-vue-shape'
const x6VueShape = 'vue-shape'
export default class CellController {
// private graph: Graph;
//
// /** 记录画布中的节点 */
// public nodes: Node[] = [];
// /** 记录画布中的连线 */
// public edges: Edge[] = [];
constructor (x6BaseGraph) {
this.graph = x6BaseGraph.graph
/** 记录画布中的节点 */
this.nodes = []
/** 记录画布中的连线 */
this.edges = []
}
/**
* 批量添加节点
* @template T
* @param {T[]} nodesData 节点数据集合
* @memberof CellController
*/
addNodes (nodesData) {
nodesData.forEach((nodeData) => {
this.addNode(nodeData)
})
}
/**
* 添加单个节点
* @template T
* @param {T} nodeData 节点数据
* @memberof CellController
*/
addNode (nodeData) {
console.log(nodeData, 'nodeData')
const { id, x, y, width, height, component, data, ...rest } = nodeData
const newNode = this.graph.addNode({
id,
x: x || 0,
y: y || 0,
width: width || 100,
height: height || 60,
// data: data || undefined,
shape: x6VueShape,
component: component,
...rest
})
this.nodes.push(newNode)
}
/**
* 更新节点
* @param {Node} node 节点实例
* @param {NodeConfig} newNodeData 节点最新数据
* @memberof CellController
*/
updateNode (node, newNodeData) {
// !!! 现在仅支持data数据不一致才认为更新
if (!_.isEqual(node.data, newNodeData.data)) {
// 重设节点数据, 触发x6更新节点逻辑
node.setData(newNodeData.data)
// node.setPosition(newNodeData.x, newNodeData.y)
// node.setSize(newNodeData.width, newNodeData.height)
}
}
/**
* 批量删除节点
* @param {Node[]} removeNodes
* @memberof CellController
*/
removeNodes (removeNodes) {
if (_.size(removeNodes) > 0) {
this.graph.removeCells(removeNodes)
this.nodes = _.pullAll(this.nodes, removeNodes)
}
}
/**
* 单个删除节点
* @param {(Node | string)} node
* @memberof CellController
*/
removeNode (removeNode) {
if (!removeNode) {
return
}
if (removeNode instanceof Node) {
this.removeNodes([removeNode])
} else {
const findNode = this.findNodeById(removeNode)
if (findNode) {
this.removeNodes([findNode])
}
}
// !!! 待补充: 删除节点时是否需要将与之关联的连线都删除, 这里可以加一个标记
}
/**
* 批量添加连线
* @template T
* @param {T[]} edgesData 边数据集合
* @memberof CellController
*/
addEdges (edgesData) {
edgesData.forEach((edgeData) => {
this.addEdge(edgeData)
})
}
/**
* 添加单个连线
* @template T
* @param {T} edgeData 边数据
* @memberof CellController
*/
addEdge (edgeData) {
const { id, source, target, render, data, ...rest } = edgeData
const sourceNode = _.find(this.nodes, (node) => node.id === source)
const targetNode = _.find(this.nodes, (node) => node.id === target)
if (!source || !target) {
throw new Error('edge must has source and target!')
}
const newEdge = this.graph.addEdge({
id: id || `${source}-${target}`,
data: data || undefined,
source: sourceNode,
target: targetNode,
// label: (edge: Edge) => {
// return render && render(edge && edge.data)
// },
attrs: {
line: {
stroke: '#B4BDCF',
strokeWidth: 1
}
},
...rest
})
this.edges.push(newEdge)
}
/**
* 更新边
* @param {Edge} edge 边实例
* @param {EdgeConfig} newEdgeData 边最新数据
* @memberof CellController
*/
updateEdge (edge, newEdgeData) {
// !!! 现在仅支持data数据不一致才认为更新, 需要扩展
if (!_.isEqual(edge.data, newEdgeData.data)) {
edge.setData(newEdgeData.data)
}
}
/**
* 批量删除边
* @param {Edge[]} removeEdges
* @memberof CellController
*/
removeEdges (removeEdges) {
this.graph.removeCells(removeEdges)
this.edges = _.pullAll(this.edges, removeEdges)
}
/**
* 单个删除边
* @param {(Edge | string)} removeEdge
* @memberof CellController
*/
removeEdge (removeEdge) {
if (!removeEdge) {
return
}
if (removeEdge instanceof Edge) {
this.graph.removeCells([removeEdge])
this.removeEdges([removeEdge])
} else {
const findEdge = this.findEdgeById(removeEdge)
if (findEdge) {
this.removeEdges([findEdge])
}
}
}
/**
* 根据节点id获取节点
* @param {string} id 节点id
* @returns {(Node | undefined)}
* @memberof CellController
*/
findNodeById (id) {
return this.nodes.find((node) => node.id === id)
}
/**
* 根据连线id获取连线
* @param {string} id 边id
* @returns {(Edge | undefined)}
* @memberof CellController
*/
findEdgeById (id) {
return this.edges.find((edge) => edge.id === id)
}
}

View File

@@ -0,0 +1,149 @@
// import { Graph } from '@antv/x6'
// import X6BaseGraph from '../graph/baseGraph'
// import { EventArg } from '../interface/graph-core'
export default class EventController {
// private x6BaseGraph: X6BaseGraph;
// private graph: Graph;
//
// // 是否删除X6自动生成的无实际业务含义的连线(用户在画布上拖拽生成连线, 这条连线是没有业务含义的)
// private isDeleteX6DefaultEdge!: boolean;
constructor (x6BaseGraph) {
this.x6BaseGraph = x6BaseGraph
this.graph = x6BaseGraph.graph
}
registerEvent = (events) => {
events &&
events.forEach((event) => {
switch (event.eventName) {
case 'scale': {
this.graph.on('scale', ({ sx, sy, ox, oy }) => {
const scale = sx
event.handler && event.handler({ scale })
})
break
}
case 'graph:mouseenter': {
this.graph.on('graph:mouseenter', ({ e }) => {
event.handler && event.handler()
})
break
}
case 'graph:mouseleave': {
this.graph.on('graph:mouseleave', ({ e }) => {
event.handler && event.handler()
})
break
}
case 'blank:mouseDown': {
this.graph.on('blank:mousedown', ({ e, x, y }) => {
event.handler && event.handler({ x, y })
})
break
}
case 'blank:mouseUp': {
this.graph.on('blank:mouseup', ({ e, x, y }) => {
event.handler && event.handler({ x, y })
})
break
}
case 'node:added': {
this.graph.on('node:added', ({ node }) => {
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
// event.handler && event.handler({ node });
// }
this.x6BaseGraph.bringNodesToFront([node])
})
break
}
case 'node:removed': {
this.graph.on('node:removed', ({ node }) => {
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
// event.handler && event.handler({ node })
// }
})
break
}
case 'edge:added': {
this.graph.on('edge:added', ({ edge }) => {
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
// event.handler && event.handler({ edge })
// }
// this.x6BaseGraph.bringCellsToBack([edge])
})
break
}
case 'edge:removed': {
this.graph.on('edge:removed', ({ edge }) => {
if (this.isDeleteX6DefaultEdge) {
}
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
// event.handler && event.handler({ edge })
// }
})
break
}
case 'node:mousedown': {
this.graph.on('node:mousedown', ({ e, view, x, y }) => {
event.handler && event.handler({ node: view.cell, x, y })
})
break
}
case 'node:mousemove': {
this.graph.on('node:mousemove', ({ e, view, x, y }) => {
event.handler && event.handler({ node: view.cell, x, y })
})
break
}
case 'node:mouseup': {
this.graph.on('node:mouseup', ({ e, view, x, y }) => {
event.handler && event.handler({ node: view.cell, x, y })
})
break
}
case 'node:click': {
this.graph.on('node:click', ({ view }) => {
event.handler && event.handler({ node: view.cell })
})
break
}
case 'node:dbclick': {
this.graph.on('node:dblclick', ({ view }) => {
event.handler && event.handler({ node: view.cell })
})
break
}
case 'edge:connected': {
this.graph.on('edge:connected', ({ edge }) => {
// if (!this.x6BaseGraph.isDataDrivenUpdate) {
// // 拖拽连线过程中画布会出现一条连线, 该连线无实际业务含义, 需要删除
// if (!edge.data && edge.id.toString().startsWith('cell-', 0)) {
// this.isDeleteX6DefaultEdge = true;
// this.graph.removeCells([edge]);
// this.isDeleteX6DefaultEdge = false;
// }
// event.handler && event.handler({ edge });
// }
})
break
}
case 'selection:changed': {
this.graph.on(
'selection:changed',
({ selected, removed, added }) => {
event.handler && event.handler({ selected, removed, added })
this.x6BaseGraph.bringCellsToFront(selected)
}
)
break
}
default: {
break
}
}
})
};
}

View File

@@ -0,0 +1,4 @@
export { default as CellController } from './cellController'
export { default as EventController } from './eventController'
/** 更多独立功能控制器, 比如快捷方式、布局等 */

View File

@@ -0,0 +1,267 @@
import { Graph } from '@antv/x6'
// import Node from '../graph/node'
// import Edge from '../graph/edge'
import { CellController, EventController } from '../controller/index'
import _ from 'lodash'
export default class X6BaseGraph {
// public graph!: Graph;
// public cellController!: CellController;
// public eventController!: EventController;
constructor (graphOptions) {
/** 创建X6画布实例 */
const defaultCfg = this.getDefaultCfg()
this.graph = new Graph({
...defaultCfg,
...graphOptions
})
this.init()
}
init () {
this.cellController = new CellController(this)
this.eventController = new EventController(this)
}
/**
* 获取画布默认配置项
* @returns {Partial<GraphOptions>}
* @memberof X6BaseGraph
*/
getDefaultCfg () {
const defaultCfg = {
/** 无限画布设置 */
scroller: {
enabled: true,
pageVisible: false,
pageBreak: false,
pannable: true
},
/** 画布网格 */
grid: {
visible: true,
size: 20,
type: 'doubleMesh',
args: [
{
color: '#888',
thickness: 1
}
// {
// color: '#ddd',
// thickness: 1,
// factor: 4,
// },
]
},
/** 全局连线配置 */
// connecting: {
// anchor: 'midSide',
// router: {
// name: 'er',
// },
// // connector: {
// // name: 'smooth',
// // },
// },
connecting: {
connector: {
name: 'rounded'
},
router: {
name: 'er',
args: {
direction: 'H'
}
}
},
/** 对齐线 */
snapline: {
enabled: true
},
/** 滚轮缩放 */
// mousewheel: {
// enabled: true,
// zoomAtMousePosition: false,
// factor: 1.1,
// },
keyboard: {
enabled: true
},
clipboard: {
enabled: true
}
}
return defaultCfg
}
/**
* 更新画布内容
* @param {GraphData} graphData 画布元素数据
* @memberof X6BaseGraph
*/
updateGraph (graphData) {
if (!graphData) {
throw new Error('graphData must be defined first!')
}
const { addNodesData, addEdgesData } = this.graphContentDiff(graphData)
this.graph.batchUpdate('updateGraph', () => {
if (addNodesData && addNodesData.length > 0) {
this.cellController.addNodes(addNodesData)
}
if (addEdgesData && addEdgesData.length > 0) {
this.cellController.addEdges(addEdgesData)
}
})
}
/**
* 画布缩放
* @param {number} factor 缩放比例尺
* @memberof X6BaseGraph
*/
zoomGraph (factor) {
if (typeof factor === 'number') {
this.graph.zoom(factor)
} else if (factor === 'fit') {
this.graph.zoomToFit({ padding: 12 })
} else if (factor) {
this.graph.scale(1)
this.graph.centerContent()
}
}
/**
* 移动节点到画布中央
* @param {(Node | string)} node
* @memberof X6BaseGraph
*/
focusNodeToGraphCenter (node) {
if (node instanceof Node) {
this.graph.centerCell(node)
} else {
const temp = this.cellController.findNodeById(node)
if (temp) {
this.graph.centerCell(temp)
}
}
}
/**
* 将Nodes置于画布最前方
* @param {Node[]} nodes
* @memberof X6BaseGraph
*/
bringNodesToFront (nodes) {
nodes.forEach((node) => {
node.toBack()
})
}
/**
* 将Nodes置于画布最后方
* @param {Node[]} nodess
* @memberof X6BaseGraph
*/
bringNodesToBack (nodes) {
nodes.forEach((node) => {
node.toBack()
})
}
/**
* 清空画布内容
* @memberof X6BaseGraph
*/
clearGraph () {
// todo
}
/**
* 注册监听事件
* @param {EventArg[]} events
* @memberof X6BaseGraph
*/
registerEvent (events) {
this.eventController.registerEvent(events)
}
/** 画布内容Diff */
graphContentDiff (graphData) {
const { nodes: nodesData, edges: edgesData } = graphData
// 新增节点数据
const addNodesData = []
nodesData.forEach((nodeData) => {
const findNode = this.cellController.findNodeById(nodeData.id)
if (!findNode) {
addNodesData.push(nodeData)
}
})
// 保持、更新、移除的节点
const retainNodes = []
const updateNodes = []
const removeNodes = []
this.cellController.nodes.forEach((node) => {
const findNodeData = nodesData.find(
(nodeData) => nodeData.id === node.id
)
if (!findNodeData) {
removeNodes.push(node)
} else {
// !!! 目前仅支持节点data数据变化, 才认为更新
if (_.isEqual(node.data, findNodeData.data)) {
retainNodes.push(node)
} else {
updateNodes.push(node)
this.cellController.updateNode(node, findNodeData)
}
}
})
// 新增连线数据
const addEdgesData = []
edgesData.forEach((edgeData) => {
if (!edgeData.id) {
return
}
const findEdge = this.cellController.findEdgeById(edgeData.id)
if (!findEdge) {
addEdgesData.push(edgeData)
}
})
// 保持、更新、移除的连线
const retainEdges = []
const updateEdges = []
const removeEdges = []
this.cellController.edges.forEach((edge) => {
const findEdgeData = edgesData.find(
(edgeData) => edgeData.id === edge.id
)
if (!findEdgeData) {
removeEdges.push(edge)
} else {
// !!! 目前仅支持连线data数据变化, 才认为更新
if (_.isEqual(edge.data, findEdgeData.data)) {
retainEdges.push(edge)
} else {
updateEdges.push(edge)
this.cellController.updateEdge(edge, findEdgeData)
}
}
})
this.cellController.removeNodes(removeNodes)
this.cellController.removeEdges(removeEdges)
return {
addNodesData,
addEdgesData
}
}
}

View File

@@ -0,0 +1,7 @@
/** 类型定义 */
// export * from './interface/graph-core'
/** BaseGraph、Node、Edge */
export { default as BaseGraph } from './graph/baseGraph'
// export { default as Node } from './graph/node'
// export { default as Edge } from './graph/edge'

View File

@@ -1,4 +1,5 @@
import Home from './views/home/Home.vue'
import Er from './views/home/Er.vue'
import UserLogin from './views/user/Login.vue'
import UserMyInfo from './views/user/MyInfo.vue'
@@ -45,6 +46,7 @@ let routes = [
{path: '/data/transferData', name: '数据互导工具',component: DataTransferData},
{path: '/data/dataPreview', name: '表数据预览',component: DataPreview},
{path: '/user/myInfo', name: '我的信息',component: UserMyInfo},
// {path: '/home/er', name: 'ER图测试',component: Er},
]
}, {
path: '/user',

View File

@@ -177,6 +177,7 @@
},
executorSource: {},
columnMap: {},
primaryKeyColumn: {}
}
},
components: {
@@ -200,9 +201,13 @@
}
this.pageParam = param;
this.executorSource = {sourceId: param.sourceId, dbName: param.dbName, tableName: param.tableName};
let columnMap = {};
columnList.forEach(item => columnMap[item.name] = item);
this.columnMap = columnMap;
this.columnMap = {};
columnList.forEach(item => {
this.columnMap[item.name] = item;
if (item.primaryKey == 1) {
this.primaryKeyColumn = item;
}
});
this.doExecutorSqlCommon();
// this.vueQueryParam = to.query;
// let newName = {key: this.$route.fullPath, val: '数据-'+this.vueQueryParam.tableName};
@@ -267,6 +272,7 @@
conditionSql = conditionSql || "";
this.executeError = "";
this.executeUseTime = "";
this.choiceResultObj = {};
this.executeResultList = [];
this.tableMaxHeight = document.body.clientHeight - 420;
this.nowExecutorId = (new Date()).getTime() + Math.ceil(Math.random() * 1000);
@@ -368,14 +374,19 @@
handleCopyCheckLineCommand(type) {
let choiceData = this.choiceResultObj[this.executeShowTable] || [];
if (choiceData.length > 0) {
this.conditionDataColsChoice = [];
let dataCols = this.executeResultList.find(item => item.name === this.executeShowTable).dataCols;
if (type === 'update') {
// 选择更新的条件列
if (!!this.primaryKeyColumn.name) {
this.conditionDataColsChoice = [this.primaryKeyColumn.name];
} else {
// 没有主键的时候,选择更新的条件列
this.conditionDataCols = dataCols;
this.exportConditionVisible = true;
return;
}
let copyData = copyFormatter.format(type, this.pageParam.dbType, dataCols, choiceData, '', this.pageParam.dbName, this.pageParam.tableName);
}
let copyData = copyFormatter.format(type, this.pageParam.dbType, dataCols, choiceData, this.conditionDataColsChoice, this.pageParam.dbName, this.pageParam.tableName);
this.$copyText(copyData).then(
res => this.$message.success("内容已复制到剪切板!"),
err => this.$message.error("抱歉,复制失败!")
@@ -403,9 +414,15 @@
this.downloadDataVisible = false;
},
downloadTableData() {
let dataRes = this.executeResultList.find(item => item.name === this.executeShowTable);
if (!dataRes || !dataRes.dataList || dataRes.dataList.length <= 0) {
this.$message.warning("当前筛选条件下无数据,请重新筛选后再操作导出");
return;
}
this.downloadDataParam.conditionArr = [];
this.conditionDataCols = this.executeResultList.find(item => item.name === this.executeShowTable).dataCols;
this.conditionDataCols = dataRes.dataCols;
this.downloadDataVisible = true;
},
dropTableFlagChange() {
if (this.downloadDataParam.dropTableFlag === 1) {

View File

@@ -5,8 +5,8 @@
*/
export default {
insert(dataCols, choiceData, dbName, tableName) {
let tableNameRes = (!!dbName) ? '`' + dbName + '`.`' : '';
tableNameRes += (!!tableName) ? tableName : '`table`';
let tableNameRes = (!!dbName) ? dbName + '.' : '';
tableNameRes += (!!tableName) ? tableName : 'table';
// 复制为insert语句
let copyData = '';
let names = '';
@@ -31,8 +31,8 @@ export default {
return copyData;
},
update(dataCols, choiceData, condition=[], dbName, tableName) {
let tableNameRes = (!!dbName) ? '`' + dbName + '`.`' : '';
tableNameRes += (!!tableName) ? tableName : '`table`';
let tableNameRes = (!!dbName) ? dbName + '.' : '';
tableNameRes += (!!tableName) ? tableName : 'table';
// 复制为update语句
let copyData = '';
choiceData.forEach(item => {

View File

@@ -0,0 +1,20 @@
<template>
<div style="padding: 10px;">
<er-graph></er-graph>
</div>
</template>
<script>
import erGraph from '../../components/er/index'
export default {
data() {
return {};
},
components: {erGraph},
mounted() {
},
methods: {}
}
</script>

View File

@@ -1,23 +1,23 @@
<template>
<div style="padding: 10px;">
<div style="max-width: 1200px;margin: 20px auto;">
<div style="text-align: center;">欢迎使用ヾ()" - 在左上角选择一个数据源吧~</div>
<div style="text-align: center;">欢迎使用<span @dblclick="showErGraph">()"</span> - 在左上角选择一个数据源吧~</div>
</div>
</div>
</template>
<script>
var app;
export default {
data() {
return {
};
},
mounted: function () {
app = this;
mounted () {
},
methods: {
showErGraph() {
// this.$router.push({path: '/home/er'});
}
}
}
</script>

View File

@@ -16,5 +16,7 @@ module.exports = {
template: 'public/index.html',
filename: process.env.NODE_ENV === 'production'?'doc-db.html':'index.html',
},
}
},
// 在项目配置的时候,默认 npm 包导出的是运行时构建,即 runtime 版本,不支持编译 template 模板。
runtimeCompiler: true
};