update front ui
This commit is contained in:
parent
75ca055772
commit
dfbe88cb5a
9
Makefile
9
Makefile
|
@ -3,7 +3,14 @@ DATETIME:=$(shell date "+%Y-%m-%d %H:%M:%S")
|
|||
PKGNAME:="git.nobla.cn/golang/moto"
|
||||
GIT_VERSION=$(shell git rev-parse --short HEAD)
|
||||
|
||||
.PHONY: build
|
||||
.PHONY: build font-build dev-serve
|
||||
|
||||
|
||||
dev-serve:
|
||||
cd web && npm run dev
|
||||
|
||||
font-build:
|
||||
cd web && npm run build
|
||||
|
||||
build:
|
||||
go mod tidy
|
||||
|
|
12
api.go
12
api.go
|
@ -2,6 +2,9 @@ package moto
|
|||
|
||||
import (
|
||||
"embed"
|
||||
httpkg "net/http"
|
||||
"strconv"
|
||||
|
||||
"git.nobla.cn/golang/kos"
|
||||
"git.nobla.cn/golang/kos/entry/http"
|
||||
"git.nobla.cn/golang/kos/util/arrays"
|
||||
|
@ -13,8 +16,6 @@ import (
|
|||
"git.nobla.cn/golang/rest"
|
||||
restTypes "git.nobla.cn/golang/rest/types"
|
||||
"gorm.io/gorm"
|
||||
httpkg "net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//go:embed web/release
|
||||
|
@ -151,8 +152,11 @@ func (svr *Server) handleSaveSchema(ctx *http.Context) (err error) {
|
|||
schemas[i].Domain = domainName
|
||||
}
|
||||
if err = db.WithContext(ctx.Request().Context()).Transaction(func(tx *gorm.DB) (errTx error) {
|
||||
for _, scm := range schemas {
|
||||
if errTx = tx.Save(scm).Error; errTx != nil {
|
||||
for _, row := range schemas {
|
||||
if row.Domain == "" {
|
||||
row.Domain = "localhost"
|
||||
}
|
||||
if errTx = tx.Save(row).Error; errTx != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
database:
|
||||
address: "192.168.9.199:3306"
|
||||
address: "192.168.1.200:3306"
|
||||
username: "root"
|
||||
password: "root"
|
||||
database: "test2"
|
||||
password: "smile1210"
|
||||
database: "test"
|
||||
|
||||
adminUsers:
|
||||
- "1000"
|
||||
|
|
4
go.mod
4
go.mod
|
@ -3,8 +3,8 @@ module git.nobla.cn/golang/moto
|
|||
go 1.22.9
|
||||
|
||||
require (
|
||||
git.nobla.cn/golang/kos v0.1.32
|
||||
git.nobla.cn/golang/rest v0.0.4
|
||||
git.nobla.cn/golang/kos v0.1.34
|
||||
git.nobla.cn/golang/rest v0.0.5
|
||||
github.com/disintegration/letteravatar v0.0.0-20160912210445-1a457b860450
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/google/uuid v1.6.0
|
||||
|
|
8
go.sum
8
go.sum
|
@ -1,9 +1,9 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
git.nobla.cn/golang/kos v0.1.32 h1:sFVCA7vKc8dPUd0cxzwExOSPX2mmMh2IuwL6cYS1pBc=
|
||||
git.nobla.cn/golang/kos v0.1.32/go.mod h1:35Z070+5oB39WcVrh5DDlnVeftL/Ccmscw2MZFe9fUg=
|
||||
git.nobla.cn/golang/rest v0.0.4 h1:i8hD/z5UAgoKjRA0ED83yK0q0IuYJ+xiiUZ1nGdn8PY=
|
||||
git.nobla.cn/golang/rest v0.0.4/go.mod h1:tGDOul2GGJtxk6fAeu+YLpMt/Up/TsBonTkygymN/wE=
|
||||
git.nobla.cn/golang/kos v0.1.34 h1:GkyU9ya9tAMaq2SMbfV+yd8AAWLCWvyJUiYNVhP5IR4=
|
||||
git.nobla.cn/golang/kos v0.1.34/go.mod h1:35Z070+5oB39WcVrh5DDlnVeftL/Ccmscw2MZFe9fUg=
|
||||
git.nobla.cn/golang/rest v0.0.5 h1:dgEuGLt2xYdyNxXKdQ5/k27O2zJFbrMBX7s4PRlyduk=
|
||||
git.nobla.cn/golang/rest v0.0.5/go.mod h1:4viDk7VujDokpUeHQGbnSp2bkkVZEoIkWQIs/l/TTPQ=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
|
@ -12,20 +12,21 @@
|
|||
"axios": "^1.7.9",
|
||||
"color": "^4.2.3",
|
||||
"dayjs": "^1.11.13",
|
||||
"element-plus": "^2.9.0",
|
||||
"element-plus": "^2.9.6",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pinia": "^2.2.6",
|
||||
"pinia-plugin-persistedstate": "^4.1.3",
|
||||
"pluralize": "^8.0.0",
|
||||
"query-string": "^9.1.1",
|
||||
"sass": "^1.82.0",
|
||||
"screenfull": "^6.0.2",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.4.5",
|
||||
"vue3-puzzle-vcode": "^1.1.7"
|
||||
"vue3-puzzle-vcode": "^1.1.7",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"sass": "^1.82.0",
|
||||
"vite": "^6.0.1",
|
||||
"vite-plugin-vue-devtools": "^7.6.5"
|
||||
}
|
||||
|
@ -1101,6 +1102,7 @@
|
|||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.0.tgz",
|
||||
"integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
|
@ -1140,6 +1142,7 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1160,6 +1163,7 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1180,6 +1184,7 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1200,6 +1205,7 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1220,6 +1226,7 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1240,6 +1247,7 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1260,6 +1268,7 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1280,6 +1289,7 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1300,6 +1310,7 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1320,6 +1331,7 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1340,6 +1352,7 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1360,6 +1373,7 @@
|
|||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -1380,6 +1394,7 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -2424,6 +2439,7 @@
|
|||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
|
@ -2452,9 +2468,9 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/element-plus": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.9.0.tgz",
|
||||
"integrity": "sha512-ccOFXKsauo2dtokAr4OX7gZsb7TuAoVxA2zGRZo5o2yyDDBLBaZxOoFQPoxITSLcHbBfQuNDGK5Iag5hnyKkZA==",
|
||||
"version": "2.9.7",
|
||||
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.9.7.tgz",
|
||||
"integrity": "sha512-6vjZh5SXBncLhUwJGTVKS5oDljfgGMh6J4zVTeAZK3YdMUN76FgpvHkwwFXocpJpMbii6rDYU3sgie64FyPerQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^3.4.1",
|
||||
|
@ -2932,6 +2948,7 @@
|
|||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.0.3.tgz",
|
||||
"integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
|
@ -3407,6 +3424,7 @@
|
|||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
|
@ -3955,6 +3973,7 @@
|
|||
"version": "1.82.0",
|
||||
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.82.0.tgz",
|
||||
"integrity": "sha512-j4GMCTa8elGyN9A7x7bEglx0VgSpNUG4W4wNedQ33wSMdnkqQCT8HTwOaVSV4e6yQovcu/3Oc4coJP/l0xhL2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
|
@ -4067,6 +4086,12 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/sortablejs": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz",
|
||||
"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
|
@ -4587,6 +4612,18 @@
|
|||
"resolved": "https://registry.npmmirror.com/vue3-puzzle-vcode/-/vue3-puzzle-vcode-1.1.7.tgz",
|
||||
"integrity": "sha512-mW780dz7HKjrElnE60CeYSeHGidKBKHoMjTDYfqF21330rTkFOsfDK1FQKZ22MktgMtTEoS/imfpEDlM1cxY/g=="
|
||||
},
|
||||
"node_modules/vuedraggable": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",
|
||||
"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sortablejs": "1.14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-virtual-modules": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
|
|
|
@ -13,20 +13,21 @@
|
|||
"axios": "^1.7.9",
|
||||
"color": "^4.2.3",
|
||||
"dayjs": "^1.11.13",
|
||||
"element-plus": "^2.9.0",
|
||||
"element-plus": "^2.9.6",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pinia": "^2.2.6",
|
||||
"pinia-plugin-persistedstate": "^4.1.3",
|
||||
"pluralize": "^8.0.0",
|
||||
"query-string": "^9.1.1",
|
||||
"sass": "^1.82.0",
|
||||
"screenfull": "^6.0.2",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.4.5",
|
||||
"vue3-puzzle-vcode": "^1.1.7"
|
||||
"vue3-puzzle-vcode": "^1.1.7",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"sass": "^1.82.0",
|
||||
"vite": "^6.0.1",
|
||||
"vite-plugin-vue-devtools": "^7.6.5"
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
|
@ -7,8 +7,8 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MOTO</title>
|
||||
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_3832926_ya7vh14nje.css">
|
||||
<script type="module" crossorigin src="/static/index-4E04Ax0l.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/static/index-BhBbL-Pz.css">
|
||||
<script type="module" crossorigin src="/static/index-CttRSJAf.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/static/index-DXC70i67.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
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
|
@ -1,4 +1,3 @@
|
|||
|
||||
import axios from "axios";
|
||||
|
||||
|
||||
|
@ -10,7 +9,7 @@ export function getBaseHost() {
|
|||
if (process.env.NODE_ENV === 'production') {
|
||||
return location.host;
|
||||
} else {
|
||||
return 'api-dev.echo.me'
|
||||
return '//devapi-local.tryfly.eu.org'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +18,7 @@ export function getBaseUrl() {
|
|||
if (process.env.NODE_ENV === 'production') {
|
||||
return location.protocol + '//' + host;
|
||||
} else {
|
||||
return location.protocol + '//' + host
|
||||
return location.protocol + '//' + host;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import request from './request'
|
||||
|
||||
export function getModules() {
|
||||
return request.get('/rest/schemas')
|
||||
}
|
||||
|
||||
export function getSchemas(tableName) {
|
||||
return request.get(`/rest/schema/${tableName}`)
|
||||
}
|
||||
|
||||
export function saveSchemas(tableName, schemas) {
|
||||
return request.put(`/rest/schema/${tableName}`, schemas)
|
||||
}
|
||||
|
||||
export function deleteSchema(id) {
|
||||
return request.delete(`/rest/schema/${id}`)
|
||||
}
|
|
@ -149,6 +149,12 @@ body,
|
|||
z-index: 999;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-container {
|
||||
.el-tabs__header {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
|
|
|
@ -39,7 +39,7 @@ import { encode, decode } from './libs/codec'
|
|||
import FormItem from './parts/FormItem.vue';
|
||||
import Action from './parts/Action.vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { generateSchemaRule, checkSchemaVisible } from './libs/form'
|
||||
import { generateSchemaRule, checkSchemaVisible } from './libs/form';
|
||||
|
||||
const props = defineProps({
|
||||
size: {
|
||||
|
|
|
@ -246,6 +246,10 @@ const formTitle = computed(() => {
|
|||
return actionText + (title || '')
|
||||
})
|
||||
|
||||
/**
|
||||
* 判断是否有权限
|
||||
* @param s 权限标识符
|
||||
*/
|
||||
const hasPermission = (s) => {
|
||||
if (props.disablePermission) {
|
||||
return true
|
||||
|
|
|
@ -82,13 +82,24 @@ export function encode(raw, schemas, scenario) {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (['multiSelect'].indexOf(schema.format) > -1) {
|
||||
} else if (['multiSelect'].indexOf(schema.format) > -1 || (scenario === "search" && schema.attribute.multiple_for_search)) {
|
||||
try {
|
||||
if (typeof raw[schema.column] === 'object') {
|
||||
data[schema.column] = mustMarshal(
|
||||
JSON.stringify(raw[schema.column]),
|
||||
schema.type
|
||||
)
|
||||
if (Array.isArray(raw[schema.column]) && raw[schema.column].length > 0) {
|
||||
if (scenario === "search") {
|
||||
let vs = [];
|
||||
for (let i in raw[schema.column]) {
|
||||
vs.push(mustMarshal(
|
||||
raw[schema.column][i],
|
||||
schema.type
|
||||
))
|
||||
}
|
||||
data[schema.column] = vs
|
||||
} else {
|
||||
data[schema.column] = mustMarshal(
|
||||
JSON.stringify(raw[schema.column]),
|
||||
schema.type
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e) { }
|
||||
} else if (['date', 'datetime', 'timestamp'].indexOf(schema.format) > -1) {
|
||||
|
|
|
@ -17,7 +17,9 @@ export function clearSearchModel(model, schemas) {
|
|||
continue
|
||||
}
|
||||
}
|
||||
ps[schema.column] = model[schema.column]
|
||||
if (model[schema.column]) {
|
||||
ps[schema.column] = model[schema.column]
|
||||
}
|
||||
}
|
||||
return ps;
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
</template>
|
||||
<template v-else-if="isVisible('dropdown', schema)">
|
||||
<el-select clearable v-if="!isDropdownMulitLevel(schema)" v-model="model[schema.column]"
|
||||
:multiple="schema.format === 'multiSelect'" :disabled="isDisabled(schema)"
|
||||
:placeholder="getPlaceholder(schema)" :allow-create="isDropdownCreated(schema)"
|
||||
:filterable="isDropdownFilterable(schema)" :default-first-option="isDropdownDefaultFirst(schema)">
|
||||
:multiple="isMulutSelete(schema)" :disabled="isDisabled(schema)" :placeholder="getPlaceholder(schema)"
|
||||
:allow-create="isDropdownCreated(schema)" :filterable="isDropdownFilterable(schema)"
|
||||
:default-first-option="isDropdownDefaultFirst(schema)">
|
||||
<template v-for="(v, k) in schema.attribute.values" :key="k">
|
||||
<el-option v-if="typeof v === 'object'" :label="v.label" :value="v.value"></el-option>
|
||||
<el-option v-else :label="v" :value="k"></el-option>
|
||||
|
@ -101,6 +101,16 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
const isMulutSelete = (schema) => {
|
||||
if (schema.format == "multiSelect") {
|
||||
return true
|
||||
}
|
||||
if (schema.attribute.multiple_for_search && props.scenario == 'search') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const isDropdownCreated = (schema) => {
|
||||
if (props.scenario === 'search') {
|
||||
return false;
|
||||
|
|
|
@ -19,6 +19,7 @@ const menu = [
|
|||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
label: "组织架构",
|
||||
icon: "org",
|
||||
|
@ -87,6 +88,20 @@ const menu = [
|
|||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "系统设置",
|
||||
icon: "connect",
|
||||
hidden: false,
|
||||
route: '/setting',
|
||||
children: [
|
||||
{
|
||||
label: "表单设置",
|
||||
hidden: false,
|
||||
access: 'allow',
|
||||
route: "/setting/schemas"
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default menu;
|
|
@ -3,7 +3,7 @@
|
|||
<div class="flex-shrink">
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="text-right text-muted">{{ copyright }}</div>
|
||||
<div class="text-right text-muted" style="font-size: .8rem;">{{ copyright }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { createRouter, createWebHashHistory } from 'vue-router'
|
|||
import LayoutView from '@/layouts/default/Layout.vue'
|
||||
import { getMenus, buildRoutes } from '@/assets/js/menu'
|
||||
|
||||
const components = import.meta.glob("../views/**/**.vue")
|
||||
const components = import.meta.glob("../views/**/**.vue");
|
||||
|
||||
const childRoutes = buildRoutes(getMenus(), components);
|
||||
|
||||
|
@ -32,5 +32,4 @@ const router = createRouter({
|
|||
]
|
||||
})
|
||||
|
||||
|
||||
export default router
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</el-form-item>
|
||||
<el-form-item label="个人简介" prop="confirmPassword">
|
||||
<el-input v-model="basicModel.description" type="textarea" autocomplete="off"
|
||||
rows="10" placeholder="请介绍下自己" />
|
||||
:rows="10" placeholder="请介绍下自己" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleUpdateProfile">修改</el-button>
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
<template>
|
||||
<div class="card box">
|
||||
<div class="card-header">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h3>字段配置</h3>
|
||||
</div>
|
||||
<div class="flex-shrink">
|
||||
<el-button type="primary" @click="handleSubmit" round>保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-horizontal" v-loading="loading">
|
||||
<div class="flex-shrink auto-scroll" style="width:280px" v-if="enableAddSchema">
|
||||
<div class="schema-form-elements">
|
||||
<draggable v-model="formElements" :group="elementGroup" itemKey="name" :clone="handleCloneSchema">
|
||||
<template #item="{ element, index }">
|
||||
<div class="form-element">
|
||||
<div class="form-element-inner">
|
||||
<icon :name="element.icon"></icon>
|
||||
<span>{{ element.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-shrink px-1 border-right border-left schemas-build-form auto-scroll"
|
||||
:style="{ width: enableAddSchema ? '600px' : '60%' }">
|
||||
<el-form label-width="120px">
|
||||
<el-empty v-if="schemas.length <= 0"></el-empty>
|
||||
<draggable v-else v-model="schemas" handle=".drag-move" :group="fieldGroup" itemKey="column"
|
||||
@add="handleAddSchema">
|
||||
<template #item="{ element, index }">
|
||||
<column :schema="element" @click="handleSelected" @delete="handleDeleteSchema"
|
||||
:class="schema && schema.column == element.column ? 'active' : ''">
|
||||
</column>
|
||||
</template>
|
||||
</draggable>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="flex-fill px-1 auto-scroll">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基础配置" name="attr">
|
||||
<attribute v-if="schema.column" :schema="schema" :schemas="schemas"></attribute>
|
||||
<el-empty v-else></el-empty>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="校验规则" name="rule">
|
||||
<rule v-if="schema.column" :schema="schema"></rule>
|
||||
<el-empty v-else></el-empty>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="显示条件" name="visible">
|
||||
<visible v-if="schema.column" :schema="schema" :schemas="schemas"></visible>
|
||||
<el-empty v-else></el-empty>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getSchemas, saveSchemas } from '@/apis/schema'
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { onMounted, computed, ref } from 'vue';
|
||||
import draggable from 'vuedraggable'
|
||||
import Column from './parts/Column.vue'
|
||||
import Attribute from './parts/Attribute.vue'
|
||||
import Visible from './parts/Visible.vue';
|
||||
import Rule from './parts/Rule.vue'
|
||||
import { useRoute } from 'vue-router';
|
||||
import Icon from '@/components/widgets/Icon.vue';
|
||||
import { nanoid } from 'nanoid'
|
||||
import { getSupportedElements, grantElementSchema } from './schema'
|
||||
|
||||
const schemas = ref([]);
|
||||
|
||||
const schema = ref({});
|
||||
|
||||
const activeTab = ref('attr')
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const tableName = route.query['table'] || '';
|
||||
|
||||
const enableAddSchema = ref(false)
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
let cloneID = '';
|
||||
|
||||
const formElements = computed(() => {
|
||||
return getSupportedElements()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query['enable_schema_add'] == "true") {
|
||||
enableAddSchema.value = true
|
||||
}
|
||||
getSchemas(tableName).then(res => {
|
||||
schemas.value = [];
|
||||
for (let v of res) {
|
||||
schemas.value.push(v)
|
||||
}
|
||||
loading.value = false
|
||||
}).catch(e => {
|
||||
ElMessage.error(e.message)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const handleSelected = (e) => {
|
||||
schema.value = e
|
||||
activeTab.value = 'attr'
|
||||
}
|
||||
|
||||
const handleDeleteSchema = (e) => {
|
||||
for (let i in schemas.value) {
|
||||
if (schemas.value[i].column == e.column) {
|
||||
schemas.value.splice(i, 1)
|
||||
if (e.column === schema.value.column) {
|
||||
if (i > 0) {
|
||||
schema.value = schema.value[i - 1]
|
||||
} else {
|
||||
schema.value = schemas.value[0]
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleCloneSchema = (e) => {
|
||||
cloneID = nanoid(12).toString()
|
||||
let schema = Object.assign(grantElementSchema(e), {
|
||||
namespace: "default",
|
||||
table_name: tableName,
|
||||
column: cloneID,
|
||||
shadow: cloneID,
|
||||
label: e.label,
|
||||
})
|
||||
return schema
|
||||
}
|
||||
|
||||
const handleAddSchema = (e) => {
|
||||
for (let i in schemas.value) {
|
||||
if (schemas.value[i].column === cloneID) {
|
||||
schema.value = schemas.value[i]
|
||||
activeTab.value = 'attr'
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const elementGroup = computed(() => {
|
||||
return {
|
||||
name: 'schema',
|
||||
pull: 'clone',
|
||||
put: false,
|
||||
}
|
||||
})
|
||||
|
||||
const fieldGroup = computed(() => {
|
||||
return {
|
||||
name: 'schema',
|
||||
};
|
||||
})
|
||||
|
||||
const columnOptions = computed(() => {
|
||||
return {
|
||||
animation: 200,
|
||||
disabled: false,
|
||||
ghostClass: "ghost"
|
||||
}
|
||||
})
|
||||
|
||||
const handleSubmit = () => {
|
||||
let data = [];
|
||||
let index = 0;
|
||||
for (let i in schemas.value) {
|
||||
let row = Object.assign({}, schemas.value[i])
|
||||
row.position = ++index;
|
||||
if (row.attribute.live.enable) {
|
||||
row.attribute.values = [];
|
||||
}
|
||||
//影子ID,用于新添加的字段允许设置列的名字
|
||||
if (row.shadow) {
|
||||
row.column = row.shadow
|
||||
}
|
||||
data.push(row)
|
||||
}
|
||||
saveSchemas(tableName, data).then(res => {
|
||||
ElMessage.success("保存成功")
|
||||
}).catch(e => {
|
||||
ElMessage.error(`保存失败: ${e.message}`)
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.schemas-build-form {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
.schema-form-elements {
|
||||
list-style: none;
|
||||
padding: .38rem;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.form-element {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
|
||||
.form-element-inner {
|
||||
cursor: move;
|
||||
box-sizing: border-box;
|
||||
border: 1px dotted transparent;
|
||||
padding: .5rem 0 .5rem .5rem;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
margin-right: .38rem;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: .8rem;
|
||||
color: var(--el-text-color-regular);
|
||||
transition: all .5s;
|
||||
|
||||
&:hover {
|
||||
color: var(--text-color-second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,303 @@
|
|||
<template>
|
||||
<el-form label-position="top" label-width="100px" :model="schema">
|
||||
<el-form-item label="字段标识">
|
||||
<template v-if="schema.native == 0 && !schema.id">
|
||||
<el-input v-model="schema.shadow" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-input v-model="schema.column" readonly />
|
||||
</template>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段名称">
|
||||
<el-input v-model="schema.label" />
|
||||
</el-form-item>
|
||||
<el-form-item label="启用状态">
|
||||
<el-switch v-model="schema.enable" :active-value="1" :inactive-value="0"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="启用排序">
|
||||
<el-switch v-model="schema.attribute.sort"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="匹配模式">
|
||||
<el-select v-model="schema.attribute.match">
|
||||
<el-option v-for="(v, k) in mtchOptioons" :key="k" :label="v" :value="k"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段类型">
|
||||
<template v-if="isNormalFormat(schema.format)">
|
||||
<el-select v-model="schema.format">
|
||||
<el-option v-for="(v, k) in allowFormats" :key="k" :label="v" :value="k"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>自定义类型</span>
|
||||
</template>
|
||||
</el-form-item>
|
||||
<el-form-item label="搜索多选" v-if="schema.format == 'dropdown'">
|
||||
<el-switch v-model="schema.attribute.multiple_for_search"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="只读场景">
|
||||
<el-checkbox-group v-model="schema.attribute.readonly">
|
||||
<el-checkbox-button :label="k" v-for="(v, k) in formScenarios" :value="k">{{ v }}</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="禁用场景">
|
||||
<el-checkbox-group v-model="schema.attribute.disable">
|
||||
<el-checkbox-button :label="k" v-for="(v, k) in formScenarios" :value="k">{{ v }}</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="显示场景">
|
||||
<el-checkbox-group v-model="schema.scenarios">
|
||||
<el-checkbox-button :label="k" v-for="(v, k) in allowScenarios" :value="k">{{ v }}</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="isDatasourceFile(schema.format)">
|
||||
<el-tabs v-model="datasourceTabValue" @tab-change="hangleDatasourceTabChange">
|
||||
<el-tab-pane label="自定义" name="inner">
|
||||
<el-form-item>
|
||||
<draggable class="d-flex flex-vertical" v-model="schema.attribute.values"
|
||||
handle=".schema-datasource-handle" itemKey="id">
|
||||
<template #item="{ element, index }">
|
||||
<div class="d-flex w-100 schema-datasource-row">
|
||||
<el-col :span="9" style="padding-right:5px">
|
||||
<el-input placeholder="请输入字段名称" v-model="element.value"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="9" style="padding-right:5px">
|
||||
<el-input placeholder="请输入显示名称" v-model="element.label"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<el-color-picker v-model="element.color" show-alpha />
|
||||
</el-col>
|
||||
<el-col :span="3" class="text-right">
|
||||
<el-tooltip content="移动">
|
||||
<icon class="schema-datasource-handle" name="icon-move1"></icon>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除">
|
||||
<icon @click="handleDelDatasourceItem(index)" name="ashbin"></icon>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
<div class="w-100 py-1 ">
|
||||
<small @click="handleAddDatasourceItem">
|
||||
<span class="text-color-primary cursor-pointer">添加</span>
|
||||
</small>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="接口数据" name="live">
|
||||
<div class="inline-form-item">
|
||||
<label class="el-form-item__label">数据类型</label>
|
||||
<el-select v-model="schema.attribute.live.type">
|
||||
<el-option label="下拉" value="dropdown"></el-option>
|
||||
<el-option label="级联" value="cascader"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="inline-form-item">
|
||||
<label class="el-form-item__label">接口类型</label>
|
||||
<el-select v-model="schema.attribute.live.method">
|
||||
<el-option v-for="row in requestMethod" :label="row.label" :value="row.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="inline-form-item">
|
||||
<label class="el-form-item__label">接口地址</label>
|
||||
<el-input v-model="schema.attribute.live.url"></el-input>
|
||||
</div>
|
||||
<div class="inline-form-item" v-if="schema.attribute.live.method != 'get'">
|
||||
<label class="el-form-item__label">数据类型</label>
|
||||
<el-select v-model="schema.attribute.live.content_type">
|
||||
<el-option v-for="row in requestContentType" :label="row.label"
|
||||
:value="row.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="inline-form-item" v-if="schema.attribute.live.method != 'get'">
|
||||
<label class="el-form-item__label">请求数据</label>
|
||||
<el-input v-model="schema.attribute.live.body"></el-input>
|
||||
</div>
|
||||
<div class="inline-form-item" v-if="schema.attribute.live.type == 'cascader'">
|
||||
<label class="el-form-item__label">数据列</label>
|
||||
<el-select v-model="schema.attribute.live.columns" :multiple="true">
|
||||
<el-option v-for="row in schemas" :label="row.label" :value="row.column"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-divider>
|
||||
</el-divider>
|
||||
</template>
|
||||
|
||||
<el-form-item label="默认值">
|
||||
<el-input v-model="schema.attribute.default_value" />
|
||||
</el-form-item>
|
||||
<el-form-item label="占位文本">
|
||||
<el-input v-model="schema.attribute.tooltip" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字段解释">
|
||||
<el-input type="textarea" v-model="schema.attribute.description" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
.schema-datasource-row {
|
||||
margin-bottom: .5rem;
|
||||
|
||||
.iconfont {
|
||||
margin-right: .5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-move1 {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
|
||||
.inline-form-item {
|
||||
margin-bottom: .5rem;
|
||||
|
||||
.label {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
max-width: 280px;
|
||||
--el-select-width: 80%;
|
||||
}
|
||||
|
||||
.el-input {
|
||||
max-width: 280px;
|
||||
--el-input-height: 29px;
|
||||
--el-input-width: 80%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script setup>
|
||||
import Icon from '@/components/widgets/Icon.vue'
|
||||
import draggable from 'vuedraggable'
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
schema: {
|
||||
type: Object,
|
||||
},
|
||||
schemas: {
|
||||
type: Array
|
||||
}
|
||||
})
|
||||
|
||||
const mtchOptioons = {
|
||||
'exactly': '精确匹配',
|
||||
'fuzzy': '模糊匹配'
|
||||
}
|
||||
|
||||
const allowFormats = {
|
||||
'integer': '整数',
|
||||
'decimal': '小数',
|
||||
'string': '文本',
|
||||
'boolean': '是否',
|
||||
'textarea': '多行文本',
|
||||
'date': '日期',
|
||||
'time': '时间',
|
||||
'datetime': '日期时间',
|
||||
'dropdown': '单项选择',
|
||||
'multiSelect': '多项选择'
|
||||
};
|
||||
|
||||
|
||||
const allowScenarios = {
|
||||
create: '创建',
|
||||
update: '更新',
|
||||
search: '查询',
|
||||
export: '导出',
|
||||
list: '列表',
|
||||
view: '详情',
|
||||
};
|
||||
|
||||
const formScenarios = {
|
||||
create: '创建',
|
||||
update: '更新',
|
||||
}
|
||||
|
||||
const requestMethod = computed(() => {
|
||||
return [
|
||||
{ label: 'GET', value: 'get' },
|
||||
{ label: 'POST', value: 'post' }
|
||||
]
|
||||
})
|
||||
|
||||
const requestContentType = computed(() => {
|
||||
return [
|
||||
{ label: 'JSON', value: 'application/json' },
|
||||
{ label: 'XML', value: 'application/xml' }
|
||||
]
|
||||
})
|
||||
|
||||
const datasourceTabValue = ref('inner')
|
||||
|
||||
const isDatasourceFile = (v) => {
|
||||
return ['dropdown', 'multiSelect'].indexOf(v) > -1;
|
||||
}
|
||||
|
||||
const isNormalFormat = (v) => {
|
||||
for (let k in allowFormats) {
|
||||
if (k.toUpperCase() === v.toUpperCase()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const hangleDatasourceTabChange = (v) => {
|
||||
if (v === 'live') {
|
||||
props.schema.attribute.live.enable = true;
|
||||
if (!props.schema.attribute.live.type) {
|
||||
props.schema.attribute.live.type = 'dropdown'
|
||||
}
|
||||
if (!props.schema.attribute.live.method) {
|
||||
props.schema.attribute.live.method = 'get'
|
||||
}
|
||||
if (!props.schema.attribute.live.content_type) {
|
||||
props.schema.attribute.live.content_type = 'application/json'
|
||||
}
|
||||
if (!props.schema.attribute.live.body) {
|
||||
props.schema.attribute.live.body = ''
|
||||
}
|
||||
} else {
|
||||
props.schema.attribute.live.enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelDatasourceItem = (index) => {
|
||||
props.schema.attribute.values.splice(index, 1)
|
||||
}
|
||||
|
||||
const handleAddDatasourceItem = () => {
|
||||
if (!Array.isArray(props.schema.attribute.values)) {
|
||||
props.schema.attribute.values = [];
|
||||
}
|
||||
let hasEmpty = false;
|
||||
for (let v of props.schema.attribute.values) {
|
||||
if (v.label === '' && v.value === '' && v.color === '') {
|
||||
hasEmpty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasEmpty) {
|
||||
props.schema.attribute.values.push({ label: '', value: '', color: '', id: props.schema.attribute.values.length + 1 })
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
watch(() => props.schema, (v) => {
|
||||
if (v.attribute.live.enable) {
|
||||
datasourceTabValue.value = 'live'
|
||||
} else {
|
||||
datasourceTabValue.value = 'inner'
|
||||
}
|
||||
}, { deep: true })
|
||||
})
|
||||
|
||||
</script>
|
|
@ -0,0 +1,204 @@
|
|||
<template>
|
||||
<div class="schema-build-item"
|
||||
:class="schema.enable === 1 ? `schema-column-${schema.column}` : `schema-column-${schema.column} disabled`"
|
||||
@click="handleClick">
|
||||
<div class="drag-move">
|
||||
<icon name="icon-move"></icon>
|
||||
</div>
|
||||
<div class="schema-build-item-mask">
|
||||
<div class="schema-build-toolbar" v-if="schema.native === 0">
|
||||
<el-tooltip content="删除字段">
|
||||
<icon name="ashbin" @click="handleDeleteSchema"></icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<el-form-item :label="schema.label">
|
||||
<el-time-picker v-if="isVisible('time', schema)" :placeholder="getPlaceholder(schema)"></el-time-picker>
|
||||
<el-date-picker v-else-if="isVisible('date', schema)"
|
||||
:placeholder="getPlaceholder(schema)"></el-date-picker>
|
||||
<el-date-picker v-else-if="isVisible('datetime', schema)" :placeholder="getPlaceholder(schema)">
|
||||
</el-date-picker>
|
||||
<el-select v-else-if="isVisible('dropdown', schema)" clearable :placeholder="getPlaceholder(schema)">
|
||||
<template v-for="(v, k) in schema.attribute.values" :key="k">
|
||||
<el-option v-if="typeof v === 'object'" :label="v.label" :value="v.value"></el-option>
|
||||
<el-option v-else :label="v" :value="k"></el-option>
|
||||
</template>
|
||||
</el-select>
|
||||
<el-cascader v-else-if="isVisible('cascader', schema)" :options="schema.attribute.values" filterable
|
||||
clearable :placeholder="getPlaceholder(schema)"></el-cascader>
|
||||
<el-switch v-else-if="isVisible('boolean', schema)" :inactive-value="0" :active-value="1"></el-switch>
|
||||
<el-input v-else-if="isVisible('password', schema)" show-password :readonly="true"
|
||||
:placeholder="getPlaceholder(schema)"></el-input>
|
||||
<el-input v-else-if="isVisible('multistr', schema)" type="textarea" :readonly="true"
|
||||
:placeholder="getPlaceholder(schema)"></el-input>
|
||||
<el-input v-else-if="isVisible('number', schema)" :placeholder="getPlaceholder(schema)" :readonly="true">
|
||||
<template v-if="schema.attribute.suffix" #append>
|
||||
{{ schema.attribute.suffix }}
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input v-else :placeholder="getPlaceholder(schema)" :readonly="true">
|
||||
<template v-if="schema.attribute.suffix" #append>
|
||||
{{ schema.attribute.suffix }}
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.schema-build-item {
|
||||
display: block;
|
||||
padding: .8rem 1rem;
|
||||
border: 2px solid transparent;
|
||||
position: relative;
|
||||
|
||||
.el-input {
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: var(--el-color-primary);
|
||||
|
||||
.drag-move {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.schema-build-toolbar {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&.ghost {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
.schema-build-item-mask {
|
||||
background-color: var(--el-mask-color-extra-light);
|
||||
}
|
||||
}
|
||||
|
||||
.schema-build-item-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 8;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.schema-build-toolbar {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
color: white;
|
||||
box-sizing: border-box;
|
||||
padding: .5rem .8rem;
|
||||
background-color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
z-index: 99;
|
||||
|
||||
.iconfont {
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.drag-move {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
display: none;
|
||||
cursor: move;
|
||||
|
||||
.iconfont {
|
||||
font-size: 1.38rem;
|
||||
color: var(--el-color-primary);
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
import Icon from '@/components/widgets/Icon.vue'
|
||||
|
||||
const props = defineProps({
|
||||
schema: {
|
||||
type: Object,
|
||||
},
|
||||
scenario: {
|
||||
type: String,
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(["click", "delete"])
|
||||
|
||||
onMounted(() => { })
|
||||
|
||||
const handleClick = (e) => {
|
||||
emit('click', props.schema)
|
||||
}
|
||||
|
||||
const isVisible = (type, schema) => {
|
||||
let visible = false
|
||||
switch (type) {
|
||||
case 'number':
|
||||
visible = ['integer', 'decimal'].indexOf(schema.format) > -1
|
||||
break
|
||||
case 'password':
|
||||
visible = ['password', 'pass'].indexOf(schema.format) > -1
|
||||
break
|
||||
case 'time':
|
||||
visible = ['time'].indexOf(schema.format) > -1
|
||||
break
|
||||
case 'date':
|
||||
visible = ['date'].indexOf(schema.format) > -1
|
||||
break
|
||||
case 'datetime':
|
||||
visible = ['datetime', 'timestamp'].indexOf(schema.format) > -1
|
||||
break
|
||||
case 'dropdown':
|
||||
visible = ['dropdown', 'multiSelect'].indexOf(schema.format) > -1
|
||||
break
|
||||
case 'cascader':
|
||||
visible = ['cascader'].indexOf(schema.format) > -1
|
||||
break
|
||||
case 'multistr':
|
||||
if (props.scenario !== 'search') {
|
||||
visible = ['textarea'].indexOf(schema.format) > -1
|
||||
}
|
||||
break
|
||||
case 'boolean':
|
||||
if (
|
||||
props.scenario !== 'search' &&
|
||||
['bool', 'boolean'].indexOf(schema.format) > -1
|
||||
) {
|
||||
visible = true
|
||||
} else {
|
||||
visible = false
|
||||
}
|
||||
break
|
||||
case 'string':
|
||||
visible = ['string', 'text'].indexOf(schema.format) > -1
|
||||
break
|
||||
}
|
||||
return visible
|
||||
}
|
||||
|
||||
const getPlaceholder = (schema) => {
|
||||
if (schema.attribute.tooltip) {
|
||||
return schema.attribute.tooltip
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteSchema = (e) => {
|
||||
emit('delete', props.schema)
|
||||
}
|
||||
|
||||
</script>
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<el-form label-position="top" label-width="100px" :model="schema">
|
||||
<el-form-item :label="maxLabel(schema.type)">
|
||||
<el-input type="number" v-model.number="schema.rule.max" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="minLabel(schema.type)">
|
||||
<el-input type="number" v-model.number="schema.rule.min" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否唯一">
|
||||
<el-switch v-model="schema.rule.unique"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="必填场景">
|
||||
<el-checkbox-group v-model="schema.rule.required">
|
||||
<el-checkbox-button :label="k" v-for="(v, k) in formScenarios" :value="k">{{ v }}</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="schema.type === 'string'" label="校验规则">
|
||||
<el-input v-model="schema.rule.regular" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
schema: {
|
||||
type: Object,
|
||||
}
|
||||
})
|
||||
|
||||
const formScenarios = {
|
||||
create: '创建',
|
||||
update: '更新',
|
||||
}
|
||||
|
||||
const maxLabel = (v) => {
|
||||
if (['string'].indexOf(v) > -1) {
|
||||
return '最大长度'
|
||||
} else if (['integer', 'double'].indexOf(v) > -1) {
|
||||
return '最大值'
|
||||
}
|
||||
}
|
||||
|
||||
const minLabel = (v) => {
|
||||
if (['string'].indexOf(v) > -1) {
|
||||
return '最小长度'
|
||||
} else if (['integer', 'double'].indexOf(v) > -1) {
|
||||
return '最小值'
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -0,0 +1,135 @@
|
|||
<template>
|
||||
<el-alert title="温馨提示" type="info" effect="dark">
|
||||
显示条件可以配置字段的显示条件
|
||||
<ul>
|
||||
<li>只有当表单的值全部满足用户配置的条件时候,字段才会显示出来</li>
|
||||
</ul>
|
||||
</el-alert>
|
||||
<template v-if="schema.attribute.visible.length > 0">
|
||||
<el-table :data="schema.attribute.visible">
|
||||
<el-table-column prop="column" label="字段名称">
|
||||
<template #default="scope">
|
||||
{{ formatSchemaLabel(scope.row.column) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="column" label="匹配值">
|
||||
<template #default="scope">
|
||||
<el-tag v-for="tag in scope.row.values" class="mr-1">
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="column">
|
||||
<template #default="scope">
|
||||
<icon name="ashbin" class="text-color-danger cursor-pointer"
|
||||
@click="handleDeleteCondition(scope.index)"></icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-empty></el-empty>
|
||||
</template>
|
||||
<el-divider>添加选项</el-divider>
|
||||
<el-form :model="model">
|
||||
<el-form-item>
|
||||
<el-select v-model="model.column" class="mr-1">
|
||||
<template v-for="(v, k) in allowFields" :key="k">
|
||||
<el-option :label="v.label" :value="v.value"></el-option>
|
||||
</template>
|
||||
</el-select>
|
||||
<div class="d-flex" v-if="model.column">
|
||||
<div class="flex-shrink">
|
||||
<el-tag v-for="tag in model.values" closable class="mr-1" :disable-transitions="false"
|
||||
@close="handleRemoveTag(tag)">
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<el-input v-model="inputValue" round placeholder="请输入值,按回车键确定" @keyup.enter="handleInputConfirm"
|
||||
@blur="handleInputConfirm" />
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" round @click="handleCreateCondition">添加</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import Icon from '@/components/widgets/Icon.vue';
|
||||
|
||||
const props = defineProps({
|
||||
schema: {
|
||||
type: Object,
|
||||
},
|
||||
schemas: {
|
||||
type: Array
|
||||
}
|
||||
})
|
||||
|
||||
const model = ref({
|
||||
column: '',
|
||||
values: []
|
||||
})
|
||||
|
||||
const inputVisible = ref(false);
|
||||
|
||||
const inputValue = ref('');
|
||||
|
||||
const allowFields = computed(() => {
|
||||
let vs = [];
|
||||
for (let schema of props.schemas) {
|
||||
if (schema.column === props.schema.column) {
|
||||
continue
|
||||
}
|
||||
vs.push({
|
||||
label: schema.label,
|
||||
value: schema.column
|
||||
})
|
||||
}
|
||||
return vs
|
||||
})
|
||||
|
||||
const formatSchemaLabel = (v) => {
|
||||
for (let schema of props.schemas) {
|
||||
if (schema.column === v) {
|
||||
return schema.label;
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
const handleRemoveTag = (tag) => {
|
||||
let index = model.value.values.indexOf(tag);
|
||||
if (index > -1) {
|
||||
model.value.values.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const handleInputConfirm = (e) => {
|
||||
if (inputValue.value !== '') {
|
||||
model.value.values.push(inputValue.value)
|
||||
inputValue.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteCondition = (e) => {
|
||||
props.schema.attribute.visible.splice(e, 1)
|
||||
}
|
||||
|
||||
const handleCreateCondition = (e) => {
|
||||
props.schema.attribute.visible.push({
|
||||
column: model.value.column,
|
||||
values: model.value.values
|
||||
})
|
||||
model.value.column = '';
|
||||
model.value.values = [];
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,118 @@
|
|||
export function getDefaultSchema() {
|
||||
return {
|
||||
namespace: "default",
|
||||
module_name: '',
|
||||
table_name: '',
|
||||
enable: 1,
|
||||
column: '',
|
||||
label: '',
|
||||
type: "string",
|
||||
format: 'string',
|
||||
native: 0,
|
||||
is_primary_key: 0,
|
||||
expression: "",
|
||||
scenarios: ["create", "update", "view"],
|
||||
rule: {
|
||||
min: 0,
|
||||
max: 0,
|
||||
type: "",
|
||||
unique: true,
|
||||
required: [],
|
||||
regular: ""
|
||||
},
|
||||
attribute: {
|
||||
match: "fuzzy",
|
||||
primary_key: true,
|
||||
default_value: "",
|
||||
readonly: [],
|
||||
disable: [],
|
||||
visible: [],
|
||||
values: [],
|
||||
live: {
|
||||
enable: false,
|
||||
type: "",
|
||||
url: "",
|
||||
method: "",
|
||||
body: "",
|
||||
content_type: "",
|
||||
columns: []
|
||||
},
|
||||
icon: "",
|
||||
sort: false,
|
||||
suffix: "",
|
||||
tooltip: "",
|
||||
description: ""
|
||||
},
|
||||
position: 1
|
||||
}
|
||||
}
|
||||
|
||||
export function getSupportedElements() {
|
||||
return [
|
||||
{
|
||||
name: 'string',
|
||||
icon: 'input',
|
||||
label: '文本'
|
||||
},
|
||||
{
|
||||
name: 'integer',
|
||||
icon: 'number-input',
|
||||
label: '数字'
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
icon: 'miyue',
|
||||
label: '密码'
|
||||
},
|
||||
{
|
||||
name: 'textarea',
|
||||
icon: 'textarea',
|
||||
label: '多行文本'
|
||||
},
|
||||
{
|
||||
name: 'boolean',
|
||||
icon: 'boolean',
|
||||
label: '是否'
|
||||
},
|
||||
{
|
||||
name: 'time',
|
||||
icon: 'timer',
|
||||
label: '时间'
|
||||
},
|
||||
{
|
||||
name: 'date',
|
||||
icon: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
name: 'datetime',
|
||||
icon: 'datetime',
|
||||
label: '时间日期'
|
||||
},
|
||||
{
|
||||
name: 'dropdown',
|
||||
icon: 'dropdown',
|
||||
label: '单项选择'
|
||||
},
|
||||
{
|
||||
name: 'multiSelect',
|
||||
icon: 'select',
|
||||
label: '多项选择'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export function getTypeFromFormat(format) {
|
||||
if (['boolean', 'number'].indexOf(format) > -1) {
|
||||
return 'integer'
|
||||
} else {
|
||||
return 'string'
|
||||
}
|
||||
}
|
||||
|
||||
export function grantElementSchema(e) {
|
||||
let schema = getDefaultSchema();
|
||||
schema.format = e.name
|
||||
schema.type = getTypeFromFormat(e.name)
|
||||
return schema
|
||||
}
|
|
@ -15,13 +15,18 @@ export default defineConfig({
|
|||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 18081,
|
||||
host: '0.0.0.0',
|
||||
allowedHosts: ['dev-local.tryfly.eu.org'],
|
||||
},
|
||||
build: {
|
||||
outDir: 'release',
|
||||
assetsDir: 'static',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
return 'notebook'
|
||||
return 'moto'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue