数据模型完成
This commit is contained in:
@@ -2,18 +2,25 @@
|
|||||||
<ai-detail class="dmAdd">
|
<ai-detail class="dmAdd">
|
||||||
<ai-title slot="title" :title="pageTitle" isShowBottomBorder isShowBack @back="back"/>
|
<ai-title slot="title" :title="pageTitle" isShowBottomBorder isShowBack @back="back"/>
|
||||||
<template #content>
|
<template #content>
|
||||||
<el-form size="small" :model="form" label-width="60px" :rules="rules" ref="DataModelForm">
|
<el-form size="small" :model="form" label-width="80px" :rules="rules" ref="DataModelForm">
|
||||||
<ai-card title="主表实体">
|
<ai-card title="主表实体">
|
||||||
<el-form-item label="主表" prop="tableName">
|
<div flex>
|
||||||
<ai-select v-model="form.tableName" :selectList="entries" placeholder="指定主表实体模型,更换主表会清空设计模型" filterable @select="initMainModel" :disabled="isEdit"/>
|
<el-form-item label="主表" prop="name" class="fill">
|
||||||
|
<ai-select v-model="form.name" :selectList="entries" placeholder="指定主表实体模型,更换主表会清空设计模型" filterable @select="initMainModel" :disabled="isEdit"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="模型别名" prop="alias" class="fill mar-l16">
|
||||||
|
<el-input v-model="form.alias" clearable placeholder="用于更好的辨别关联模型,并通过别名可以索引该模型"/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
</ai-card>
|
</ai-card>
|
||||||
<ai-card title="设计模型" panel>
|
<ai-card title="设计模型" panel>
|
||||||
<el-form-item prop="json" label-width="0" class="diagram">
|
<el-button slot="right" type="text" @click="fullscreen=true">全屏</el-button>
|
||||||
|
<el-form-item prop="json" label-width="0" class="diagram" :class="{fullscreen}">
|
||||||
<div ref="DataModel" class="dataModel"/>
|
<div ref="DataModel" class="dataModel"/>
|
||||||
<div class="dndPanel pad-8">
|
<div class="dndPanel pad-8">
|
||||||
<div class="iconfont iconxinxiguanli pad-h8" v-text="`表实体`" @mousedown="handleAddModel"/>
|
<div class="iconfont iconxinxiguanli pad-h8" v-text="`表实体`" @mousedown="handleAddModel"/>
|
||||||
</div>
|
</div>
|
||||||
|
<el-button class="fullscreenBtn" v-if="fullscreen" icon="el-icon-close" @click="fullscreen=false"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</ai-card>
|
</ai-card>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -25,12 +32,9 @@
|
|||||||
<ai-select v-model="current.id" :selectList="entries" placeholder="请选择实体对象" filterable @select="changeModel"/>
|
<ai-select v-model="current.id" :selectList="entries" placeholder="请选择实体对象" filterable @select="changeModel"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="实体属性">
|
<el-form-item label="实体属性">
|
||||||
<ai-table :tableData="current.props" :colConfigs="currentCols" :is-show-pagination="false" tableSize="small"/>
|
<ai-table :tableData="current.props" :colConfigs="currentCols" :is-show-pagination="false" tableSize="small" height="70vh"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="current.type=='polyline'">
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
<div class="footBar">
|
<div class="footBar">
|
||||||
<el-button @click="drawer=false">取消</el-button>
|
<el-button @click="drawer=false">取消</el-button>
|
||||||
@@ -62,19 +66,20 @@ export default {
|
|||||||
entries: [],
|
entries: [],
|
||||||
form: {},
|
form: {},
|
||||||
rules: {
|
rules: {
|
||||||
tableName: {required: true, message: "请选择主表模型"},
|
name: {required: true, message: "请选择主表模型"},
|
||||||
},
|
},
|
||||||
diagram: null,
|
diagram: null,
|
||||||
drawer: false,
|
drawer: false,
|
||||||
current: {},
|
current: {},
|
||||||
currentCols: [
|
currentCols: [
|
||||||
{label: "属性", prop: "columnName"},
|
{label: "属性", prop: "field"},
|
||||||
{label: "属性名", prop: "columnComment"},
|
{label: "属性名", prop: "fieldName"},
|
||||||
]
|
],
|
||||||
|
fullscreen: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isEdit: v => !!v.$route.query.tableName,
|
isEdit: v => !!v.$route.query.name,
|
||||||
pageTitle: v => v.isEdit ? "编辑数据关联模型" : "新增数据关联模型",
|
pageTitle: v => v.isEdit ? "编辑数据关联模型" : "新增数据关联模型",
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -84,14 +89,14 @@ export default {
|
|||||||
this.$router.push({})
|
this.$router.push({})
|
||||||
},
|
},
|
||||||
getDetail() {
|
getDetail() {
|
||||||
const {tableName} = this.$route.query
|
const {id} = this.$route.query
|
||||||
tableName && this.instance.get("/relation/list", {params: {tableName}}).then(res => {
|
id && this.instance.post("/app/appdatamodelconfig/queryDetailById", null, {params: {id}}).then(res => {
|
||||||
if (res?.data) {
|
if (res?.data) {
|
||||||
const json = JSON.parse(res.data.json)
|
const json = JSON.parse(res.data.json)
|
||||||
this.form = {...res.data, json, tableName}
|
this.form = {...res.data, json}
|
||||||
this.$load(this.diagram).then(() => {
|
this.$load(this.diagram).then(() => {
|
||||||
this.diagram.render(json)
|
this.diagram.render(json)
|
||||||
this.diagram.focusOn({id: tableName})
|
this.diagram.focusOn({id: this.form.name})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -100,9 +105,9 @@ export default {
|
|||||||
const jsonData = this.diagram.getGraphData()
|
const jsonData = this.diagram.getGraphData()
|
||||||
this.form.relationNodes = jsonData.edges.map(e => e.properties)
|
this.form.relationNodes = jsonData.edges.map(e => e.properties)
|
||||||
this.$refs.DataModelForm.validate()
|
this.$refs.DataModelForm.validate()
|
||||||
.then(() => this.instance.post("/relation/setting", {...this.form, jsonData: JSON.stringify(jsonData)}))
|
.then(() => this.instance.post("/app/appdatamodelconfig/addOrUpdate", {...this.form, config: JSON.stringify(jsonData)}))
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res?.code == 200) {
|
if (res?.code == 0) {
|
||||||
this.$message.success("提交成功")
|
this.$message.success("提交成功")
|
||||||
this.back()
|
this.back()
|
||||||
}
|
}
|
||||||
@@ -137,7 +142,7 @@ export default {
|
|||||||
this.diagram.dnd.startDrag({type: "model"})
|
this.diagram.dnd.startDrag({type: "model"})
|
||||||
},
|
},
|
||||||
onNodeClick({data}) {
|
onNodeClick({data}) {
|
||||||
if (data.id != this.form.tableName) {
|
if (data.id != this.form.name) {
|
||||||
const {id: oldId, type} = data
|
const {id: oldId, type} = data
|
||||||
this.current = this.$copy({props: [], ...data.properties, oldId, type})
|
this.current = this.$copy({props: [], ...data.properties, oldId, type})
|
||||||
this.drawer = true
|
this.drawer = true
|
||||||
@@ -153,7 +158,7 @@ export default {
|
|||||||
initMainModel(v) {
|
initMainModel(v) {
|
||||||
this.diagram.clearData()
|
this.diagram.clearData()
|
||||||
if (!!v?.id) {
|
if (!!v?.id) {
|
||||||
this.getTableFields(v.id).then(list => {
|
this.getTableFields(v).then(list => {
|
||||||
this.diagram.addNode({
|
this.diagram.addNode({
|
||||||
type: 'main',
|
type: 'main',
|
||||||
x: 200,
|
x: 200,
|
||||||
@@ -163,28 +168,27 @@ export default {
|
|||||||
})
|
})
|
||||||
this.diagram.focusOn({id: v.id})
|
this.diagram.focusOn({id: v.id})
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeModel(v) {
|
changeModel(v) {
|
||||||
if (!!v?.id) {
|
if (!!v?.id) {
|
||||||
this.current.name = v.id
|
this.current.name = v.id
|
||||||
this.getTableFields(v.id).then((list = []) => this.current.props = list)
|
this.getTableFields(v).then((list = []) => this.current.props = list)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getEntries() {
|
getEntries() {
|
||||||
this.instance.get("/relation/table").then(res => {
|
this.instance.get("/app/v2/api-docs").then(res => {
|
||||||
if (res?.data) {
|
if (res?.definitions) {
|
||||||
this.entries = res.data.map(e => ({...e, id: e.name, label: [e.entityName, e.des].filter(Boolean).join(" | ") || e.name}))
|
this.entries = Object.entries(res.definitions).filter(([id]) => id?.startsWith("App"))?.map(([id, e]) => ({...e, id, label: id})) || []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getTableFields(tableName) {
|
getTableFields(entry) {
|
||||||
return this.instance.get(`/relation/fields`, {params: {tableName}}).then(res => {
|
const props = []
|
||||||
if (res?.data) {
|
for (const i in entry.properties) {
|
||||||
return res.data
|
props.push({field: i, fieldName: entry.properties[i]?.description})
|
||||||
}
|
}
|
||||||
})
|
return Promise.resolve(props)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -199,12 +203,31 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.dmAdd {
|
.dmAdd {
|
||||||
|
|
||||||
|
|
||||||
|
.diagram {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.dataModel {
|
.dataModel {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diagram {
|
&.fullscreen {
|
||||||
position: relative;
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
.dataModel {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
.dndPanel{
|
||||||
|
left: 16px;
|
||||||
|
top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dndPanel {
|
.dndPanel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -232,13 +255,19 @@ export default {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fullscreenBtn {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.modelElement) {
|
:deep(.modelElement) {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-width: 200px;
|
min-width: 300px;
|
||||||
|
|
||||||
& > b {
|
& > b {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -264,6 +293,10 @@ export default {
|
|||||||
.content {
|
.content {
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
|
||||||
|
.w160 {
|
||||||
|
max-width: 160px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
<ai-table :tableData="tableData" :isShowPagination="false" :col-configs="colConfigs" :dict="dict">
|
<ai-table :tableData="tableData" :isShowPagination="false" :col-configs="colConfigs" :dict="dict">
|
||||||
<el-table-column slot="options" label="操作" fixed="right" align="center" width="200">
|
<el-table-column slot="options" label="操作" fixed="right" align="center" width="200">
|
||||||
<template slot-scope="{row}">
|
<template slot-scope="{row}">
|
||||||
<el-button type="text" @click="handleAdd(row.tableName)">编辑</el-button>
|
<el-button type="text" @click="handleAdd(row.id)">编辑</el-button>
|
||||||
<el-button type="text" @click="handleDelete(row.tableName)">删除</el-button>
|
<el-button type="text" @click="handleDelete(row.id)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</ai-table>
|
</ai-table>
|
||||||
@@ -55,14 +55,13 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleAdd(tableName) {
|
handleAdd(id) {
|
||||||
tableName = tableName?.split(":").at(-1)
|
id && this.$router.push({hash: "#add", query: {id}})
|
||||||
this.$router.push({hash: "#add", query: {tableName}})
|
|
||||||
},
|
},
|
||||||
@confirm("是否要删除该模型?")
|
@confirm("是否要删除该模型?")
|
||||||
handleDelete(tableName) {
|
handleDelete(ids) {
|
||||||
this.instance.post("/app/appdatamodelconfig/delete", null, {
|
this.instance.post("/app/appdatamodelconfig/delete", null, {
|
||||||
params: {tableName}
|
params: {ids}
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (res?.code == 0) {
|
if (res?.code == 0) {
|
||||||
this.$message.success("删除成功")
|
this.$message.success("删除成功")
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class DataView extends HtmlNode {
|
|||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
<b>${id}</b>
|
<b>${id}</b>
|
||||||
<div class="content pad-8">
|
<div class="content pad-8">
|
||||||
${props.map(e => `<div flex><b class="fill sherk">${e.columnName}</b><span class="mar-l8">${e.columnComment}</span></div>`).join('')}
|
${props.map(e => `<div flex><b class="fill sherk">${e.field}</b><span class="mar-l8 w160 nowrap-text">${e.fieldName||"-"}</span></div>`).join('')}
|
||||||
</div>`;
|
</div>`;
|
||||||
rootEl.setAttribute('class', "pad-8")
|
rootEl.setAttribute('class', "pad-8")
|
||||||
rootEl.style.boxSizing = 'border-box'
|
rootEl.style.boxSizing = 'border-box'
|
||||||
@@ -58,7 +58,7 @@ class DataModel extends HtmlNodeModel {
|
|||||||
anchors.push({
|
anchors.push({
|
||||||
x: x - width / 2 + 6,
|
x: x - width / 2 + 6,
|
||||||
y: y - height / 2 + 58 + index * 20,
|
y: y - height / 2 + 58 + index * 20,
|
||||||
id: `${id}@${field.columnName}`,
|
id: `${id}@${field.field}`,
|
||||||
edgeAddable: false,
|
edgeAddable: false,
|
||||||
type: "left"
|
type: "left"
|
||||||
});
|
});
|
||||||
@@ -90,7 +90,7 @@ class MainModel extends DataModel {
|
|||||||
anchors.push({
|
anchors.push({
|
||||||
x: x + width / 2 - 6,
|
x: x + width / 2 - 6,
|
||||||
y: y - height / 2 + 58 + index * 20,
|
y: y - height / 2 + 58 + index * 20,
|
||||||
id: `${id}@${field.columnName}`,
|
id: `${id}@${field.field}`,
|
||||||
type: "right",
|
type: "right",
|
||||||
field
|
field
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -64,6 +64,15 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts';
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.w100 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h100 {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
不换行文本
|
不换行文本
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,32 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ai-select">
|
<div class="ai-select">
|
||||||
<el-select
|
<el-select clearable class="w100"
|
||||||
style="width: 100%;"
|
|
||||||
clearable
|
|
||||||
:value="value"
|
:value="value"
|
||||||
:size="$attrs.size || 'small'"
|
:size="$attrs.size || 'small'"
|
||||||
:filterable="isAction"
|
:filterable="isAction||filterable"
|
||||||
|
:disabled="disabled"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
v-on="$listeners">
|
v-on="$listeners">
|
||||||
<template v-if="isAction">
|
<template v-if="isAction">
|
||||||
<el-option v-for="op in actionOps" :key="op.id"
|
<el-option v-for="op in actionOps" :key="op.id" :label="op.label" :value="op[actionProp.value]"/>
|
||||||
:label="op[actionProp.label]" :value="op[actionProp.value]"/>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-option
|
<el-option v-for="(item, index) in ops" :key="index"
|
||||||
v-for="(item, index) in selectList"
|
:label="item.dictName||item[actionProp.label]"
|
||||||
:key="index"
|
:value="item.dictValue>-1?item.dictValue:item[actionProp.value]"
|
||||||
:label="item.dictName"
|
:disabled="check(item[actionProp.value])"/>
|
||||||
:value="item.dictValue"/>
|
|
||||||
</template>
|
</template>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Dict from "../../lib/js/dict";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AiSelect',
|
name: 'AiSelect',
|
||||||
|
|
||||||
model: {
|
model: {
|
||||||
prop: 'value',
|
prop: 'value',
|
||||||
event: 'change'
|
event: 'change'
|
||||||
@@ -38,9 +36,11 @@ export default {
|
|||||||
v && this.isAction && !this.options.toString() && this.getOptions()
|
v && this.isAction && !this.options.toString() && this.getOptions()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
action() {
|
||||||
|
this.isAction && this.getOptions()
|
||||||
|
},
|
||||||
value(v) {
|
value(v) {
|
||||||
this.$emit("select", this.isAction ? this.options.find(e => e[this.actionProp.value] == v) :
|
this.handleSelect(v)
|
||||||
this.selectList.find(e => e.dictValue == v))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@@ -48,35 +48,35 @@ export default {
|
|||||||
selectList: {
|
selectList: {
|
||||||
type: Array
|
type: Array
|
||||||
},
|
},
|
||||||
|
|
||||||
width: {
|
width: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '216'
|
default: '216'
|
||||||
},
|
},
|
||||||
|
dict: String,
|
||||||
instance: Function,
|
instance: Function,
|
||||||
action: {default: ""},
|
action: {default: ""},
|
||||||
prop: {
|
prop: {
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
},
|
||||||
|
filterable: Boolean,
|
||||||
|
disabled: Boolean
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
options: [],
|
options: [],
|
||||||
filter: ""
|
filter: "",
|
||||||
|
Dict
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
selectWidth() {
|
isAction: v => !!v.action,
|
||||||
if (this.width.indexOf('px') > -1) {
|
|
||||||
return this.width
|
|
||||||
}
|
|
||||||
return `${this.width}px`
|
|
||||||
},
|
|
||||||
isAction() {
|
|
||||||
return !!this.action
|
|
||||||
},
|
|
||||||
actionOps() {
|
actionOps() {
|
||||||
return this.options.filter(e => !this.filter || e[this.actionProp.label].indexOf(this.filter) > -1)
|
const {filter} = this
|
||||||
|
const LABEL = this.actionProp.label
|
||||||
|
return this.options.map(e => ({
|
||||||
|
...e,
|
||||||
|
label: typeof LABEL == "function" ? LABEL?.(e) : e[LABEL]
|
||||||
|
})).filter(e => !filter || e.label.indexOf(filter) > -1)
|
||||||
},
|
},
|
||||||
actionProp() {
|
actionProp() {
|
||||||
return {
|
return {
|
||||||
@@ -84,7 +84,8 @@ export default {
|
|||||||
value: 'id',
|
value: 'id',
|
||||||
...this.prop
|
...this.prop
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
ops: v => v.dict ? v.Dict.getDict(v.dict) : v.selectList
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getOptions() {
|
getOptions() {
|
||||||
@@ -94,7 +95,14 @@ export default {
|
|||||||
if (res?.data) {
|
if (res?.data) {
|
||||||
this.options = res.data.records || res.data
|
this.options = res.data.records || res.data
|
||||||
}
|
}
|
||||||
})
|
}).then(() => this.handleSelect())
|
||||||
|
},
|
||||||
|
handleSelect(v = this.value) {
|
||||||
|
this.disabled || this.$emit("select", this.isAction ? this.options.find(e => e[this.actionProp.value] == v) :
|
||||||
|
this.ops.find(e => this.dict ? e.dictValue == v : e[this.actionProp.value] == v))
|
||||||
|
},
|
||||||
|
check(v) {
|
||||||
|
return this.actionProp.disabled && [this.actionProp.disabled].flat().includes(v)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -102,9 +110,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
:deep( .ai-select .el-select ) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user