main
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Vue</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "web",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.7.1",
|
||||
"@codemirror/lang-javascript": "^6.1.9",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@element-plus/icons": "^0.0.11",
|
||||
"axios": "^1.4.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"element-plus": "^2.3.5",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"jsplumb": "^2.15.6",
|
||||
"mitt": "^3.0.0",
|
||||
"pinia": "^2.1.3",
|
||||
"sass": "^1.62.1",
|
||||
"vform3-builds": "^3.0.10",
|
||||
"vue": "^3.2.47",
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-router": "4",
|
||||
"vuedraggable": "^2.24.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.1.0",
|
||||
"unplugin-auto-import": "^0.16.2",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite": "^4.3.9"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,5 @@
|
|||
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 149 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 43 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
After Width: | Height: | Size: 496 B |
|
@ -0,0 +1,207 @@
|
|||
import * as SFCCompiler from '@vue/compiler-sfc';
|
||||
|
||||
const COMP_IDENTIFIER = `__sfc__`
|
||||
|
||||
export function compileFile(filename, code, compiled) {
|
||||
if (!code.trim()) {
|
||||
compiled.errors = []
|
||||
return
|
||||
}
|
||||
|
||||
if (!filename.endsWith('.vue')) {
|
||||
compiled.js = compiled.ssr = code
|
||||
compiled.errors = []
|
||||
return
|
||||
}
|
||||
|
||||
const id = hashId(filename)
|
||||
const { errors, descriptor } = SFCCompiler.parse(code, {
|
||||
filename,
|
||||
sourceMap: true
|
||||
})
|
||||
// console.log(descriptor)
|
||||
if (errors.length) {
|
||||
compiled.errors = errors
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
(descriptor.script && descriptor.script.lang) ||
|
||||
(descriptor.scriptSetup && descriptor.scriptSetup.lang) ||
|
||||
descriptor.styles.some((s) => s.lang) ||
|
||||
(descriptor.template && descriptor.template.lang)
|
||||
) {
|
||||
compiled.errors = [
|
||||
'lang="x" pre-processors are not supported in the in-browser playground.'
|
||||
]
|
||||
return
|
||||
}
|
||||
|
||||
const hasScoped = descriptor.styles.some((s) => s.scoped)
|
||||
let clientCode = ''
|
||||
let ssrCode = ''
|
||||
|
||||
const appendSharedCode = (code) => {
|
||||
clientCode += code
|
||||
ssrCode += code
|
||||
}
|
||||
|
||||
const clientScriptResult = doCompileScript(descriptor, id, false)
|
||||
if (!clientScriptResult) {
|
||||
return
|
||||
}
|
||||
const [clientScript, bindings] = clientScriptResult
|
||||
clientCode += clientScript
|
||||
|
||||
// script ssr only needs to be performed if using <script setup> where
|
||||
// the render fn is inlined.
|
||||
if (descriptor.scriptSetup) {
|
||||
const ssrScriptResult = doCompileScript(descriptor, id, true)
|
||||
if (!ssrScriptResult) {
|
||||
return
|
||||
}
|
||||
ssrCode += ssrScriptResult[0]
|
||||
} else {
|
||||
// when no <script setup> is used, the script result will be identical.
|
||||
ssrCode += clientScript
|
||||
}
|
||||
|
||||
// template
|
||||
// only need dedicated compilation if not using <script setup>
|
||||
if (descriptor.template && !descriptor.scriptSetup) {
|
||||
const clientTemplateResult = doCompileTemplate(
|
||||
descriptor,
|
||||
id,
|
||||
bindings,
|
||||
false
|
||||
)
|
||||
if (!clientTemplateResult) {
|
||||
return
|
||||
}
|
||||
clientCode += clientTemplateResult
|
||||
|
||||
const ssrTemplateResult = doCompileTemplate(descriptor, id, bindings, true)
|
||||
if (!ssrTemplateResult) {
|
||||
return
|
||||
}
|
||||
ssrCode += ssrTemplateResult
|
||||
}
|
||||
|
||||
if (hasScoped) {
|
||||
appendSharedCode(
|
||||
`\n${COMP_IDENTIFIER}.__scopeId = ${JSON.stringify(`data-v-${id}`)}`
|
||||
)
|
||||
}
|
||||
|
||||
if (clientCode || ssrCode) {
|
||||
appendSharedCode(
|
||||
`\n${COMP_IDENTIFIER}.__file = ${JSON.stringify(filename)}` +
|
||||
`\nexport default ${COMP_IDENTIFIER}`
|
||||
)
|
||||
compiled.js = clientCode.trimStart()
|
||||
compiled.ssr = ssrCode.trimStart()
|
||||
}
|
||||
|
||||
// styles
|
||||
let css = ''
|
||||
for (const style of descriptor.styles) {
|
||||
if (style.module) {
|
||||
compiled.errors = [`<style module> is not supported in the playground.`]
|
||||
return
|
||||
}
|
||||
|
||||
const styleResult = SFCCompiler.compileStyle({
|
||||
source: style.content,
|
||||
filename,
|
||||
id,
|
||||
scoped: style.scoped,
|
||||
modules: !!style.module
|
||||
})
|
||||
if (styleResult.errors.length) {
|
||||
// postcss uses pathToFileURL which isn't polyfilled in the browser
|
||||
// ignore these errors for now
|
||||
if (!styleResult.errors[0].message.includes('pathToFileURL')) {
|
||||
compiled.errors = styleResult.errors
|
||||
}
|
||||
// proceed even if css compile errors
|
||||
} else {
|
||||
css += styleResult.code + '\n'
|
||||
}
|
||||
}
|
||||
if (css) {
|
||||
compiled.css = css.trim()
|
||||
} else {
|
||||
compiled.css = '/* No <style> tags present */'
|
||||
}
|
||||
|
||||
// clear errors
|
||||
compiled.errors = []
|
||||
}
|
||||
|
||||
function doCompileScript(descriptor, id, ssr) {
|
||||
if (descriptor.script || descriptor.scriptSetup) {
|
||||
try {
|
||||
const compiledScript = SFCCompiler.compileScript(descriptor, {
|
||||
id,
|
||||
refSugar: true,
|
||||
inlineTemplate: true,
|
||||
templateOptions: {
|
||||
ssr,
|
||||
ssrCssVars: descriptor.cssVars
|
||||
}
|
||||
})
|
||||
let code = ''
|
||||
if (compiledScript.bindings) {
|
||||
code += `\n/* Analyzed bindings: ${JSON.stringify(
|
||||
compiledScript.bindings,
|
||||
null,
|
||||
2
|
||||
)} */`
|
||||
}
|
||||
code +=
|
||||
`\n` +
|
||||
SFCCompiler.rewriteDefault(compiledScript.content, COMP_IDENTIFIER)
|
||||
// console.log( SFCCompiler.rewriteDefault(compiledScript.content, COMP_IDENTIFIER))
|
||||
return [code, compiledScript.bindings]
|
||||
} catch (e) {
|
||||
console.log('recordFileErrors',e)
|
||||
recordFileErrors([e])
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return [`\nconst ${COMP_IDENTIFIER} = {}`, undefined]
|
||||
}
|
||||
}
|
||||
|
||||
function doCompileTemplate(descriptor, id, bindingMetadata, ssr) {
|
||||
const templateResult = SFCCompiler.compileTemplate({
|
||||
source: descriptor.template && descriptor.template.content,
|
||||
filename: descriptor.filename,
|
||||
id,
|
||||
scoped: descriptor.styles.some(s => s.scoped),
|
||||
slotted: descriptor.slotted,
|
||||
ssr,
|
||||
ssrCssVars: descriptor.cssVars,
|
||||
isProd: false,
|
||||
compilerOptions: {
|
||||
bindingMetadata
|
||||
}
|
||||
})
|
||||
if (templateResult.errors.length) {
|
||||
recordFileErrors(templateResult.errors)
|
||||
return
|
||||
}
|
||||
|
||||
const fnName = ssr ? `ssrRender` : `render`
|
||||
|
||||
return (
|
||||
`\n${templateResult.code.replace(
|
||||
/\nexport (function|const) (render|ssrRender)/,
|
||||
`$1 ${fnName}`
|
||||
)}` + `\n${COMP_IDENTIFIER}.${fnName} = ${fnName}`
|
||||
)
|
||||
}
|
||||
|
||||
function hashId(filename) {
|
||||
return btoa(filename).slice(0, 8)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
export const table = `
|
||||
<template>
|
||||
<el-table :data="tableData" style="width: 100%">
|
||||
<el-table-column type="index" :index="indexMethod" label="序列" width="100" />
|
||||
<el-table-column prop="cgPerson" label="名称" />
|
||||
<el-table-column prop="status" label="状态" />
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script setup >
|
||||
import { ref,getCurrentInstance } from 'vue'
|
||||
const { proxy } =getCurrentInstance();
|
||||
const indexMethod = (index) => {
|
||||
return index + 1
|
||||
}
|
||||
const tableData = ref([])
|
||||
const params = ref({
|
||||
page:1,
|
||||
pageSize:10,
|
||||
orderNo:''
|
||||
})
|
||||
|
||||
const getList = ()=>{
|
||||
proxy.$http.get('/api/data/table/CgOrder',params.value).then(res=>{
|
||||
if(res.code == 200){
|
||||
console.log('res',res.data)
|
||||
tableData.value = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getList()
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bg1 {
|
||||
font-size: 30px;
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
`;
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<div class="dept_content">
|
||||
<el-icon class="add" :size="30" color="#409eff" @click="clickAdd"><CirclePlus /></el-icon>
|
||||
<div class="dept_list">
|
||||
<el-tag
|
||||
v-for="(tag,index) in selectDepartment"
|
||||
:key="tag"
|
||||
class="mx-1"
|
||||
closable
|
||||
:disable-transitions="false"
|
||||
@close="handleClose(index)"
|
||||
>
|
||||
{{ tag.label }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="outerVisible" title="选择部门">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="options"
|
||||
show-checkbox
|
||||
node-key="key"
|
||||
@check-change="handleCheckChange"
|
||||
:default-checked-keys="defaultCheckedKeys"
|
||||
/>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="outerVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit"
|
||||
>确定</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref ,toRefs,nextTick} from 'vue';
|
||||
const props = defineProps({
|
||||
|
||||
options:{
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
model: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
name:{
|
||||
type:String,
|
||||
default:''
|
||||
}
|
||||
});
|
||||
|
||||
const {options,model,name} = toRefs(props)
|
||||
const outerVisible = ref(false)
|
||||
const defaultCheckedKeys= ref([])
|
||||
const treeRef = ref()
|
||||
|
||||
const selectDepartment = ref(model.value[name.value+'Array']||[])
|
||||
|
||||
const clickAdd = ()=>{
|
||||
let checkedKeys = []
|
||||
console.log(selectDepartment.value)
|
||||
selectDepartment.value.map(item=>{
|
||||
checkedKeys.push(item.key)
|
||||
})
|
||||
outerVisible.value = true
|
||||
nextTick(()=>{
|
||||
treeRef.value.setCheckedKeys(checkedKeys, false)
|
||||
})
|
||||
}
|
||||
|
||||
// const defaultCheckedKeys = (item)=>{
|
||||
// return model.value.split(',')
|
||||
// }
|
||||
|
||||
const submit = ()=>{
|
||||
let checkedNodes = treeRef.value.getCheckedNodes(true)
|
||||
selectDepartment.value = checkedNodes
|
||||
let checkedKeys= treeRef.value.getCheckedKeys(true)
|
||||
model.value[name.value] = checkedKeys.toString()
|
||||
outerVisible.value = false
|
||||
}
|
||||
|
||||
const handleCheckChange = (data, checked)=>{
|
||||
console.log(data,checked)
|
||||
}
|
||||
|
||||
const handleClose = (index)=>{
|
||||
selectDepartment.value.splice(index,1)
|
||||
let checkedKeys = []
|
||||
selectDepartment.value.map(item=>{
|
||||
checkedKeys.push(item.key)
|
||||
})
|
||||
model.value[name.value] = checkedKeys.toString()
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.dept_content{
|
||||
line-height: 1;
|
||||
.add{
|
||||
cursor: pointer;
|
||||
}
|
||||
.dept_list{
|
||||
margin-top: 5px;
|
||||
span{
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,40 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
msg: String,
|
||||
})
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button type="button" @click="count++">count is {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test HMR
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out
|
||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||
>create-vue</a
|
||||
>, the official Vue + Vite starter
|
||||
</p>
|
||||
<p>
|
||||
Install
|
||||
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
|
||||
in your IDE for a better DX
|
||||
</p>
|
||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||
</template>
|
||||
|
||||
<style scoped >
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,202 @@
|
|||
<template>
|
||||
<el-input
|
||||
class="ydool_input"
|
||||
:name="data.name"
|
||||
v-if="data.type == 'text'"
|
||||
v-model="model[data.name]"
|
||||
:autocomplete="data.autocomplete"
|
||||
clearable
|
||||
:placeholder="data.label"
|
||||
:readonly="data.readonly"
|
||||
/>
|
||||
<el-input
|
||||
class="ydool_input"
|
||||
v-else-if="data.type == 'textarea'"
|
||||
type="textarea"
|
||||
v-model="model[data.name]"
|
||||
:rows="4"
|
||||
clearable
|
||||
:placeholder="data.label"
|
||||
/>
|
||||
<el-input-number
|
||||
class="ydool_input"
|
||||
v-else-if="data.type == 'number'"
|
||||
v-model="model[data.name]"
|
||||
:placeholder="data.label"
|
||||
/>
|
||||
<el-select
|
||||
class="ydool_select"
|
||||
v-else-if="data.type == 'select'"
|
||||
clearable
|
||||
v-model="model[data.name]"
|
||||
:placeholder="data.label"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in data.options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
class="ydool_select"
|
||||
v-else-if="data.type == 'mapping'"
|
||||
clearable
|
||||
v-model="model[data.name]"
|
||||
:placeholder="data.label"
|
||||
>
|
||||
<el-option
|
||||
:label="model[`${data.name}_empty`]"
|
||||
value=""
|
||||
v-if="model[`${data.name}_empty`]"
|
||||
/>
|
||||
<el-option
|
||||
v-for="item in model[`${data.name}_mapping`]"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-radio-group v-else-if="data.type == 'bool'" v-model="model[data.name]">
|
||||
<el-radio :label="true">{{ data.dict.true }}</el-radio>
|
||||
<el-radio :label="false">{{ data.dict.false }}</el-radio>
|
||||
</el-radio-group>
|
||||
<el-select
|
||||
class="ydool_select"
|
||||
v-else-if="data.type == 'dill'"
|
||||
clearable
|
||||
v-model="model[data.name]"
|
||||
:placeholder="data.label"
|
||||
@change="dillChange(data)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in data.options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-radio-group v-else-if="data.type == 'radio'" v-model="model[data.name]">
|
||||
<el-radio
|
||||
:label="option.value"
|
||||
v-for="option in data.options"
|
||||
:key="option.value"
|
||||
>{{ option.label }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
<el-radio-group
|
||||
v-else-if="data.type == 'radio-group'"
|
||||
v-model="model[data.name]"
|
||||
>
|
||||
<el-radio-button
|
||||
:label="option.value"
|
||||
v-for="option in data.options"
|
||||
:key="option.value"
|
||||
>{{ option.label }}</el-radio-button
|
||||
>
|
||||
</el-radio-group>
|
||||
<el-date-picker
|
||||
class="ydool_input"
|
||||
v-else-if="data.type == 'date'"
|
||||
v-model="model[data.name]"
|
||||
type="date"
|
||||
:placeholder="data.label"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
<el-date-picker
|
||||
class="ydool_input"
|
||||
v-else-if="data.type == 'datetime'"
|
||||
v-model="model[data.name]"
|
||||
type="datetime"
|
||||
:placeholder="data.label"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD H:m:s"
|
||||
/>
|
||||
<el-autocomplete
|
||||
clearable
|
||||
v-else-if="data.type == 'autocomplete'"
|
||||
v-model="model[data.name]"
|
||||
:fetch-suggestions="
|
||||
(queryString, cb) => handleFilter(data.options, queryString, cb)
|
||||
"
|
||||
class="inline-input ydool_input"
|
||||
:placeholder="data.placeholder"
|
||||
@select="(option) => hanleOnSelect(option, data.onSelect)"
|
||||
/>
|
||||
<el-tree-select
|
||||
class="ydool_input"
|
||||
v-model="model[data.name]"
|
||||
:data="data.options"
|
||||
clearable
|
||||
v-else-if="data.type == 'tree-select'"
|
||||
placeholder="请选择"
|
||||
check-strictly
|
||||
/>
|
||||
<div v-else-if="data.type == 'tree-dialog'" class="dept_content">
|
||||
<Division :name="data.name" :model="model" :options="data.options" />
|
||||
</div>
|
||||
<Upload v-else-if="data.type == 'image'"
|
||||
v-model="model[data.name]"> </Upload>
|
||||
<Uploads v-else-if="data.type == 'images'"
|
||||
v-model="model[data.name]"> </Uploads>
|
||||
<File v-else-if="data.type == 'file'" v-model="model[data.name]"></File>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { toRefs } from "vue";
|
||||
import http from "../../utils/request";
|
||||
import emitter from "@/plugins/Bus";
|
||||
import Division from "@/components/Division";
|
||||
import Upload from '@/components/Upload';
|
||||
import Uploads from '@/components/Upload/Images.vue';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
model: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
|
||||
const { data, model } = toRefs(props);
|
||||
|
||||
const dillChange = (item) => {
|
||||
http
|
||||
.get(`/api/data/dill/${item.grapeName}`, {
|
||||
id: model.value[data.value.name],
|
||||
})
|
||||
.then((res) => {
|
||||
console.log(res.data);
|
||||
emitter.emit(item.dillName, res.data);
|
||||
});
|
||||
};
|
||||
emitter.on(data.value.name, (e) => {
|
||||
data.value.options = e;
|
||||
});
|
||||
|
||||
const calendarChange = () => {
|
||||
console.log("1212");
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.ydool_input,
|
||||
.ydool_select {
|
||||
width: 100% !important;
|
||||
}
|
||||
.dept_content {
|
||||
line-height: 1;
|
||||
.dept_list {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<el-table :data="tableData.data" border style="width: 100%">
|
||||
|
||||
<el-table-column :label="item.label" :width="item.width" v-for="(item,index) in tableData.column" :key="index">
|
||||
<template #default="scope">
|
||||
<div style="display: flex; align-items: center">
|
||||
<span style="margin-left: 10px">{{ scope.row[item.name]?scope.row[item.name]:'暂无' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref,toRefs} from 'vue'
|
||||
const props = defineProps({
|
||||
tableData: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
});
|
||||
|
||||
const {tableData} = toRefs(props)
|
||||
|
||||
|
||||
</script>
|
|
@ -0,0 +1,193 @@
|
|||
<template>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:accept="accept"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:http-request="request"
|
||||
>
|
||||
<el-button type="primary">附件上传</el-button>
|
||||
<div class="fileBox" v-for="(item, index) in fileList" :key="index" @click.stop="openPath(item)">
|
||||
<span>{{ handleName(item) }}</span>
|
||||
<el-icon
|
||||
class="fileBoxDel"
|
||||
:size="18"
|
||||
color="red"
|
||||
@click.stop="delImageUrl(index)"
|
||||
><CircleCloseFilled
|
||||
/></el-icon>
|
||||
</div>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, watch, watchEffect, toRefs } from "vue";
|
||||
import http from "../../utils/request";
|
||||
export default {
|
||||
name: "Upload",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
default: "image/gif, image/jpeg, image/png",
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, { emit }) {
|
||||
const fileList = ref([]);
|
||||
|
||||
const toStr = (arr) => {
|
||||
let str = arr.join(",");
|
||||
return str;
|
||||
};
|
||||
const toArr = (str) => {
|
||||
console.log(str);
|
||||
let _arr = [];
|
||||
if (str == null || str == "") {
|
||||
_arr = [];
|
||||
} else {
|
||||
if (Array.isArray(str)) {
|
||||
str.map((item) => {
|
||||
_arr.push(item.preview);
|
||||
});
|
||||
} else {
|
||||
_arr = str.split(",");
|
||||
}
|
||||
}
|
||||
|
||||
return _arr;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [...fileList.value],
|
||||
(data) => {
|
||||
emit("update:modelValue", toStr(fileList.value));
|
||||
}
|
||||
); // 监听富文本输入值变动
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
fileList.value = toArr(props.modelValue);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
const request = (param) => {
|
||||
let file = param.file;
|
||||
const data = new FormData();
|
||||
data.append("file", file);
|
||||
http.post("/api/attachment/upload", data).then((res) => {
|
||||
if (res.code == 200) {
|
||||
fileList.value.push(res.data.path);
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleAvatarSuccess = () => {};
|
||||
const handleRemove = () => {};
|
||||
|
||||
const delImageUrl = (index) => {
|
||||
fileList.value.splice(index, 1);
|
||||
};
|
||||
const handleName = (path)=>{
|
||||
let Arr = path.split('/')
|
||||
return Arr[Arr.length-1]
|
||||
}
|
||||
const openPath = (path)=>{
|
||||
window.open(path)
|
||||
}
|
||||
return {
|
||||
fileList,
|
||||
request,
|
||||
...toRefs(props),
|
||||
handleAvatarSuccess,
|
||||
handleRemove,
|
||||
delImageUrl,
|
||||
handleName,
|
||||
openPath
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.avatar-uploader {
|
||||
line-height: 1;
|
||||
}
|
||||
.avatar-uploader .el-upload {
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
line-height: 1;
|
||||
font-size: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
text-align: center;
|
||||
border: 1px dashed var(--el-border-color);
|
||||
}
|
||||
.avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
.el-upload-list__item-actions {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.fileBox {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
padding:10px 10px;
|
||||
&:hover{
|
||||
background-color: rgba($color: #000000, $alpha: 0.1);
|
||||
.fileBoxDel{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.fileBox {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-size: 0;
|
||||
line-height: 1;
|
||||
span{
|
||||
width: 80%;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.avatar-uploader{
|
||||
width: 100%;
|
||||
}
|
||||
.fileBoxDel{
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,167 @@
|
|||
<template>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:accept="accept"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:http-request="request"
|
||||
>
|
||||
<div class="imgBox" v-for="(item, index) in fileList" :key="index" @click.stop="openPath(item)" >
|
||||
<el-image :src="item" class="avatar" />
|
||||
<el-icon
|
||||
class="del"
|
||||
:size="18"
|
||||
color="red"
|
||||
@click.stop="delImageUrl(index)"
|
||||
><CircleCloseFilled
|
||||
/></el-icon>
|
||||
</div>
|
||||
<el-icon class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, watch, watchEffect, toRefs } from "vue";
|
||||
import http from "../../utils/request";
|
||||
export default {
|
||||
name: "Upload",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
default: "image/gif, image/jpeg, image/png",
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, { emit }) {
|
||||
const fileList = ref([]);
|
||||
|
||||
const toStr = (arr) => {
|
||||
|
||||
let str = arr.join(",");
|
||||
return str;
|
||||
};
|
||||
const toArr = (str) => {
|
||||
console.log(str);
|
||||
let _arr = [];
|
||||
if (str == null || str == "") {
|
||||
_arr = [];
|
||||
} else {
|
||||
if(Array.isArray(str)){
|
||||
str.map(item=>{
|
||||
_arr.push(item.preview)
|
||||
})
|
||||
|
||||
}else{
|
||||
_arr = str.split(",");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return _arr;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [...fileList.value],
|
||||
(data) => {
|
||||
emit("update:modelValue", toStr(fileList.value));
|
||||
}
|
||||
); // 监听富文本输入值变动
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
fileList.value = toArr(props.modelValue);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
const request = (param) => {
|
||||
let file = param.file;
|
||||
const data = new FormData();
|
||||
data.append("file", file);
|
||||
http.post("/api/attachment/upload", data).then((res) => {
|
||||
if (res.code == 200) {
|
||||
fileList.value.push(res.data.path);
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleAvatarSuccess = () => {};
|
||||
const handleRemove = () => {};
|
||||
|
||||
const delImageUrl = (index) => {
|
||||
fileList.value.splice(index, 1);
|
||||
};
|
||||
const openPath = (path)=>{
|
||||
window.open(path)
|
||||
}
|
||||
return {
|
||||
fileList,
|
||||
request,
|
||||
...toRefs(props),
|
||||
handleAvatarSuccess,
|
||||
handleRemove,
|
||||
delImageUrl,
|
||||
openPath
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.avatar-uploader {
|
||||
line-height: 1;
|
||||
}
|
||||
.avatar-uploader .el-upload {
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
line-height: 1;
|
||||
font-size: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
text-align: center;
|
||||
border: 1px dashed var(--el-border-color);
|
||||
}
|
||||
.avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
.el-upload-list__item-actions {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.del {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transform: translate(30%, -30%);
|
||||
}
|
||||
.imgBox {
|
||||
position: relative;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
border: 1px dashed var(--el-border-color);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,149 @@
|
|||
<template>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:accept="accept"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:http-request="request"
|
||||
>
|
||||
<div v-if="imageUrl" @click.stop="openPath(imageUrl)" style="position: relative;">
|
||||
<el-image :src="imageUrl" class="avatar" />
|
||||
<el-icon class="del" :size="18" color="red" @click.stop="delImageUrl"
|
||||
><CircleCloseFilled
|
||||
/></el-icon>
|
||||
</div>
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, watch, watchEffect, toRefs } from "vue";
|
||||
import http from "../../utils/request";
|
||||
export default {
|
||||
name: "Upload",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
default: "image/gif, image/jpeg, image/png",
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, { emit }) {
|
||||
const imageUrl = ref(props.modelValue);
|
||||
|
||||
const toArr = (str) => {
|
||||
console.log(str);
|
||||
let path = "";
|
||||
if (str == null || str == "") {
|
||||
path = "";
|
||||
} else {
|
||||
console.log(typeof str);
|
||||
if (typeof str == "object") {
|
||||
console.log(str);
|
||||
path = str.preview;
|
||||
} else {
|
||||
path = str;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("path", path);
|
||||
|
||||
return path;
|
||||
};
|
||||
watch(
|
||||
() => imageUrl.value,
|
||||
(data) => {
|
||||
emit("update:modelValue", imageUrl.value);
|
||||
}
|
||||
); // 监听富文本输入值变动
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
console.log("imageUrl", props.modelValue);
|
||||
imageUrl.value = toArr(props.modelValue);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
const request = (param) => {
|
||||
let file = param.file;
|
||||
console.log(file);
|
||||
const data = new FormData();
|
||||
data.append("file", file);
|
||||
http.post("/api/attachment/upload", data).then((res) => {
|
||||
if (res.code == 200) {
|
||||
imageUrl.value = res.data.path;
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleAvatarSuccess = () => {};
|
||||
const handleRemove = () => {};
|
||||
|
||||
const delImageUrl = () => {
|
||||
imageUrl.value = "";
|
||||
};
|
||||
const openPath = (path) => {
|
||||
window.open(path);
|
||||
};
|
||||
return {
|
||||
imageUrl,
|
||||
request,
|
||||
...toRefs(props),
|
||||
handleAvatarSuccess,
|
||||
handleRemove,
|
||||
delImageUrl,
|
||||
openPath
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.avatar-uploader {
|
||||
line-height: 1;
|
||||
}
|
||||
.avatar-uploader .el-upload {
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
line-height: 1;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
text-align: center;
|
||||
border: 1px dashed var(--el-border-color);
|
||||
}
|
||||
.avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
.el-upload-list__item-actions {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.del {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transform: translate(30%, -30%);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,10 @@
|
|||
import { defineAsyncComponent } from 'vue';
|
||||
|
||||
const components = import.meta.glob('./**/*.vue')
|
||||
|
||||
export default function install(app) {
|
||||
for (const [key, value] of Object.entries(components)) {
|
||||
const name = key.substring(key.lastIndexOf('/') + 1, key.lastIndexOf('.'))
|
||||
app.component(name, defineAsyncComponent(value))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import { createApp } from "vue";
|
||||
import "./style.css";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import { createPinia } from "pinia";
|
||||
|
||||
import ElementPlus from "element-plus"; //引入element-plus库
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
import VForm3 from "vform3-builds"; //引入VForm3库
|
||||
|
||||
import "element-plus/dist/index.css"; //引入element-plus样式
|
||||
import "vform3-builds/dist/designer.style.css"; //引入VForm3样式
|
||||
|
||||
import { appComponent } from "@/scripts/dynamicComponent";
|
||||
import "@/scripts/grape-import";
|
||||
import { table } from "@/compiler/testCode";
|
||||
import http from "@/utils/request.js";
|
||||
const app = createApp(App);
|
||||
|
||||
var loadDynamicComponent = false;
|
||||
var loadInfo = false;
|
||||
|
||||
const viewModules = import.meta.glob("./views/**/**.vue");
|
||||
console.log(viewModules);
|
||||
|
||||
router.beforeEach(async (to, from) => {
|
||||
if (!loadDynamicComponent) {
|
||||
loadDynamicComponent = true;
|
||||
await initDynamicComponent();
|
||||
}
|
||||
});
|
||||
|
||||
const initDynamicComponent = () => {
|
||||
// 解析创建组件
|
||||
appComponent(app, { name: "table", code: table });
|
||||
appComponent(app, { name: "table2", code: table });
|
||||
};
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
if (loadInfo) {
|
||||
next();
|
||||
} else {
|
||||
// 第一次创建路由
|
||||
|
||||
let listRouter = [
|
||||
{
|
||||
path: "/table",
|
||||
meta: {
|
||||
title: "测试1",
|
||||
name: "table",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/table2",
|
||||
meta: {
|
||||
title: "测试2",
|
||||
name: "table2",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
listRouter.map((item) => {
|
||||
item.component = loadView("/common/component");
|
||||
item.props = { name: item.meta.name, title: item.meta.title };
|
||||
router.addRoute("layout", item);
|
||||
});
|
||||
|
||||
console.log("layout", router);
|
||||
loadInfo = true;
|
||||
next({ ...to, replace: true });
|
||||
}
|
||||
});
|
||||
|
||||
export const loadView = (view) => {
|
||||
view = view.substring(0, 1) === "/" ? view : "/" + view;
|
||||
console.log(`./views/${view}.vue`);
|
||||
return viewModules[/* @vite-ignore */ `./views${view}.vue`];
|
||||
};
|
||||
|
||||
app.config.globalProperties.$http = http;
|
||||
|
||||
|
||||
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component)
|
||||
}
|
||||
|
||||
|
||||
app.use(ElementPlus).use(VForm3).use(createPinia()).use(router).mount("#app");
|
|
@ -0,0 +1,4 @@
|
|||
import mitt from 'mitt';
|
||||
const emitter = mitt()
|
||||
|
||||
export default emitter
|
|
@ -0,0 +1 @@
|
|||
module.exports = file => require('../views/' + file + '.vue').default
|
|
@ -0,0 +1,160 @@
|
|||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
|
||||
import tools from "@/utils/tools.js";
|
||||
const modules = import.meta.glob("../views/**/**.vue");
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/login",
|
||||
name: "login",
|
||||
mete: {
|
||||
title: "登录",
|
||||
},
|
||||
component: () => import("../views/login/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
component: () => import("../views/layout/index.vue"),
|
||||
name: "layout",
|
||||
redirect: {
|
||||
name: "home",
|
||||
},
|
||||
meta: {
|
||||
title: "主页",
|
||||
showOnly: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "home",
|
||||
component: () => import("../views/home/index.vue"),
|
||||
name: "home",
|
||||
meta: { title: "欢迎" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/formDesigner",
|
||||
name: "formDesigner",
|
||||
mete: {
|
||||
title: "VForm 3",
|
||||
},
|
||||
component: () => import("../views/formDesigner/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "/codemirror",
|
||||
name: "codemirror",
|
||||
mete: {
|
||||
title: "编辑器",
|
||||
},
|
||||
component: () => import("../views/codemirror/index.vue"),
|
||||
},
|
||||
// {
|
||||
// path: "/data-flow-editor",
|
||||
// name: "data-flow-editor",
|
||||
// mete: {
|
||||
// title: "数据中台",
|
||||
// },
|
||||
// component: () => import("../views/data-flow-editor/index.vue"),
|
||||
// },
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
var isGetApiRouter = false;
|
||||
|
||||
router.beforeEach((to, form, next) => {
|
||||
if (to.path === "/login") {
|
||||
isGetApiRouter = false;
|
||||
next();
|
||||
return false;
|
||||
}
|
||||
|
||||
let user = tools.data.get("user");
|
||||
|
||||
if (!user) {
|
||||
next({
|
||||
path: "/login",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isGetApiRouter) {
|
||||
var apiRouter = filterAsyncRouter(user.menus);
|
||||
apiRouter = flatAsyncRoutes(apiRouter);
|
||||
|
||||
console.log("apiRouter", apiRouter);
|
||||
apiRouter.forEach((item) => {
|
||||
router.addRoute("layout", item);
|
||||
});
|
||||
isGetApiRouter = true;
|
||||
next({ ...to, replace: true });
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
//路由扁平化
|
||||
function flatAsyncRoutes(routes, breadcrumb = []) {
|
||||
let res = [];
|
||||
routes.forEach((route) => {
|
||||
const tmp = { ...route };
|
||||
if (tmp.children) {
|
||||
let childrenBreadcrumb = [...breadcrumb];
|
||||
childrenBreadcrumb.push(route);
|
||||
let tmpRoute = { ...route };
|
||||
tmpRoute.meta.breadcrumb = childrenBreadcrumb;
|
||||
delete tmpRoute.children;
|
||||
res.push(tmpRoute);
|
||||
let childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb);
|
||||
childrenRoutes.map((item) => {
|
||||
res.push(item);
|
||||
});
|
||||
} else {
|
||||
let tmpBreadcrumb = [...breadcrumb];
|
||||
tmpBreadcrumb.push(tmp);
|
||||
tmp.meta.breadcrumb = tmpBreadcrumb;
|
||||
res.push(tmp);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
//转换
|
||||
function filterAsyncRouter(routerMap) {
|
||||
const accessedRouters = [];
|
||||
routerMap.forEach((item) => {
|
||||
item.meta = item.meta ? item.meta : {};
|
||||
if (JSON.stringify(item.meta) != "{}") {
|
||||
item.meta.key = item.path;
|
||||
}
|
||||
//处理外部链接特殊路由
|
||||
if (item.meta.type == "iframe") {
|
||||
item.meta.url = item.path;
|
||||
item.path = `/i/${item.name}`;
|
||||
}
|
||||
//MAP转路由对象
|
||||
var route = {
|
||||
path: item.path,
|
||||
name: item.name,
|
||||
meta: item.meta,
|
||||
redirect: item.redirect,
|
||||
children: item.children ? filterAsyncRouter(item.children) : null,
|
||||
component: loadComponent(item.component),
|
||||
};
|
||||
accessedRouters.push(route);
|
||||
});
|
||||
return accessedRouters;
|
||||
}
|
||||
|
||||
function loadComponent(component) {
|
||||
if (component) {
|
||||
return modules[/* @vite-ignore */ `../views/${component}`];
|
||||
} else {
|
||||
return modules[/* @vite-ignore */ `../views/other/empty.vue`];
|
||||
}
|
||||
}
|
||||
|
||||
export default router;
|
|
@ -0,0 +1,58 @@
|
|||
import { babelParse } from "@vue/compiler-sfc";
|
||||
import { compileFile } from "../compiler/sfc-compiler.js";
|
||||
import { ElMessage } from 'element-plus'
|
||||
export function appComponent(app, item) {
|
||||
var compiled = {};
|
||||
compileFile("TestCode.vue", item.code, compiled);
|
||||
if (compiled.errors.length > 0) {
|
||||
|
||||
|
||||
ElMessage({
|
||||
type: "error",
|
||||
duration: 0,
|
||||
showClose: true,
|
||||
message: `
|
||||
组件“${item.name}”发生错误:
|
||||
${compiled.errors[0]}
|
||||
`,
|
||||
});
|
||||
console.error(`组件“${item.name}”发生错误`);
|
||||
console.error(compiled.errors[0]);
|
||||
throw compiled.errors[0];
|
||||
} else {
|
||||
var code = compiled.js;
|
||||
var ast = babelParse(code, {
|
||||
sourceType: "module",
|
||||
});
|
||||
var replaceCode = (node, subCode) =>
|
||||
code.substring(0, node.start) + subCode + code.substring(node.end);
|
||||
for (var i = ast.program.body.length - 1; i >= 0; i--) {
|
||||
var node = ast.program.body[i];
|
||||
if (node.type === "ImportDeclaration") {
|
||||
code = replaceCode(
|
||||
node,
|
||||
node.specifiers
|
||||
.map(
|
||||
(it) =>
|
||||
`const ${
|
||||
it.local?.name || it.imported?.name || "*"
|
||||
} = ___grape__import__('${node.source.value}', '${
|
||||
it.imported?.name || "*"
|
||||
}');`
|
||||
)
|
||||
.join("\r\n")
|
||||
);
|
||||
} else if (node.type === "ExportDefaultDeclaration") {
|
||||
code = replaceCode(node, `return ${node.declaration.name}`);
|
||||
}
|
||||
}
|
||||
code = `(function(){
|
||||
${code}
|
||||
})()`;
|
||||
var componentStyle = document.createElement("style");
|
||||
componentStyle.innerHTML = compiled.css;
|
||||
document.head.appendChild(componentStyle);
|
||||
app.component(item.name, eval(code));
|
||||
console.log(item.name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import * as vue from "vue";
|
||||
import ElementPlus from "element-plus";
|
||||
|
||||
const libs = {
|
||||
vue,
|
||||
'element-plus': ElementPlus
|
||||
}
|
||||
|
||||
window.___grape__import__ = function (lib, name) {
|
||||
if (Object.prototype.toString.call(libs[lib]) != '[object Module]' && name == '*') {
|
||||
return libs[lib]
|
||||
}
|
||||
return (libs[lib] || {})[name]
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const useCar = defineStore("test", {
|
||||
state: () => {
|
||||
return {
|
||||
title: "云朵技术中台",
|
||||
menuBar: "left", //菜单栏展示类型 左侧left 头部 top
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import JSEncrypt from 'jsencrypt';
|
||||
|
||||
/**
|
||||
* 加密
|
||||
* @param {*} value 要加密的字符串
|
||||
*/
|
||||
const publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCcKv/egiS7sozK3HLWNJTa5UyL1IpI9G145k62OrK8MxgPR4gurGj58Dq8q8E6gMv0EwpylvxHnqciZeta+MJSE1NLD2wmO7oD2w9oN3KMBz6aH2+ESTkH1fhD5Szpw662Gwj/GemIQ+j+p5bX0cS9JMj/zRl+wlGojIl2bIUzFQIDAQAB';
|
||||
export function encrypt(value) {
|
||||
if (value == null || value === '') return null;
|
||||
let encrypt = new JSEncrypt();
|
||||
encrypt.setPublicKey(publicKey);
|
||||
return encodeURIComponent(encrypt.encrypt(value));
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import tools from '@/utils/tools.js'
|
||||
let user = tools.data.get('user');
|
||||
|
||||
|
||||
export const hasRole = {
|
||||
install: (app) => {
|
||||
app.directive('hasRole', {
|
||||
mounted(el, binding) {
|
||||
const value = binding.value
|
||||
const buttons = user.buttons
|
||||
|
||||
console.log(buttons)
|
||||
if (!buttons.includes(value)) {
|
||||
el.parentNode.removeChild(el)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
import axios from "axios";
|
||||
import tools from "@/utils/tools";
|
||||
|
||||
// axios.defaults.baseURL = '/api'
|
||||
axios.defaults.timeout = 10000;
|
||||
|
||||
// HTTP request 拦截器
|
||||
axios.interceptors.request.use(
|
||||
(config) => {
|
||||
let user = tools.data.get("user");
|
||||
if (user) {
|
||||
config.headers["x-token"] = "Bearer " + user.token;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// HTTP response 拦截器
|
||||
axios.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
if (error.response) {
|
||||
if (error.response.status == 401) {
|
||||
this.$message.error("请重新登录!");
|
||||
// router.replace({
|
||||
// path: '/login'
|
||||
// });
|
||||
} else if (error.response.status == 404) {
|
||||
this.$message.error("Status:404,正在请求不存在的服务器记录!");
|
||||
} else if (error.response.status == 500) {
|
||||
this.$message.error({
|
||||
title: "请求错误",
|
||||
message: "Status:500,服务器发生错误!",
|
||||
});
|
||||
} else {
|
||||
this.$message.error(`Status:${error.response.status},未知错误!`);
|
||||
}
|
||||
} else {
|
||||
this.$message.error("请求服务器无响应!");
|
||||
}
|
||||
return Promise.reject(error.response);
|
||||
}
|
||||
);
|
||||
|
||||
var http = {
|
||||
/** get 请求
|
||||
* @param {接口地址} url
|
||||
* @param {请求参数} params
|
||||
*/
|
||||
get: function (url, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.get(url, {
|
||||
params: params,
|
||||
})
|
||||
.then((response) => {
|
||||
resolve(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/** post 请求
|
||||
* @param {接口地址} url
|
||||
* @param {请求参数} params
|
||||
*/
|
||||
post: function (url, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post(url, params)
|
||||
.then((response) => {
|
||||
resolve(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
put: function (url, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.put(url, params)
|
||||
.then((response) => {
|
||||
resolve(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
delete: function (url, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.delete(url, params)
|
||||
.then((response) => {
|
||||
resolve(response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
download: function (url) {
|
||||
let user = tools.data.get("user");
|
||||
let token = "x-token=" + user.token;
|
||||
url = url + (url.indexOf("?") > 0 ? "&" : "?") + token;
|
||||
window.open(url);
|
||||
},
|
||||
|
||||
export: function (url, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post(url, params, {
|
||||
responseType: "blob",
|
||||
})
|
||||
.then((response) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default http;
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
var tool = {}
|
||||
|
||||
/* localStorage */
|
||||
tool.data = {
|
||||
set(table, settings) {
|
||||
var _set = JSON.stringify(settings)
|
||||
return localStorage.setItem(table, _set);
|
||||
},
|
||||
get(table) {
|
||||
var data = localStorage.getItem(table);
|
||||
try {
|
||||
data = JSON.parse(data)
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
return data;
|
||||
},
|
||||
remove(table) {
|
||||
return localStorage.removeItem(table);
|
||||
},
|
||||
clear() {
|
||||
return localStorage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
tool.url = function (url, params) {
|
||||
var hasParams = url.indexOf("?") > 0;
|
||||
for (var key in params) {
|
||||
url = url + (hasParams ? '&' : '?') + key + '=' + params[key];
|
||||
hasParams = true;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
export default tool
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
<template>
|
||||
<div>
|
||||
<codemirror
|
||||
v-model="welcomeCode"
|
||||
placeholder="Code goes here..."
|
||||
:style="{ height: '400px' }"
|
||||
:autofocus="true"
|
||||
:indent-with-tab="true"
|
||||
:tab-size="2"
|
||||
:extensions="extensions"
|
||||
@blur="change()"
|
||||
/>
|
||||
|
||||
<component v-bind:is="currentTabComponent"></component>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { Codemirror } from "vue-codemirror";
|
||||
import { javascript } from "@codemirror/lang-javascript";
|
||||
import { oneDark } from "@codemirror/theme-one-dark";
|
||||
import { compileFile } from "@/compiler/sfc-compiler.js";
|
||||
import { babelParse } from "@vue/compiler-sfc";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { table } from "@/compiler/testCode";
|
||||
|
||||
// 这个 id 是 scopeId,用于 css scope,保证唯一即可
|
||||
const id = Date.now().toString();
|
||||
const scopeId = `data-v-${id}`;
|
||||
|
||||
const welcomeCode = ref(table);
|
||||
|
||||
const extensions = [javascript(), oneDark];
|
||||
|
||||
const change = (e) => {
|
||||
console.log("change", e);
|
||||
generateComponent(e);
|
||||
};
|
||||
|
||||
const currentTabComponent = ref(eval(""));
|
||||
|
||||
const generateComponent = () => {
|
||||
let compiled = {};
|
||||
compileFile("TestCode.vue", welcomeCode.value, compiled);
|
||||
|
||||
if (compiled.errors.length > 0) {
|
||||
ElMessage({
|
||||
type: "error",
|
||||
duration: 0,
|
||||
showClose: true,
|
||||
message: `
|
||||
发生错误:
|
||||
${compiled.errors[0]}
|
||||
`,
|
||||
});
|
||||
console.error(`发生错误`);
|
||||
console.error(compiled.errors[0]);
|
||||
throw compiled.errors[0];
|
||||
} else {
|
||||
var code = compiled.js;
|
||||
var ast = babelParse(code, {
|
||||
sourceType: "module",
|
||||
});
|
||||
var replaceCode = (node, subCode) =>
|
||||
code.substring(0, node.start) + subCode + code.substring(node.end);
|
||||
for (var i = ast.program.body.length - 1; i >= 0; i--) {
|
||||
var node = ast.program.body[i];
|
||||
if (node.type === "ImportDeclaration") {
|
||||
code = replaceCode(
|
||||
node,
|
||||
node.specifiers
|
||||
.map(
|
||||
(it) =>
|
||||
`const ${
|
||||
it.local?.name || it.imported?.name || "*"
|
||||
} = ___grape__import__('${node.source.value}', '${
|
||||
it.imported?.name || "*"
|
||||
}');`
|
||||
)
|
||||
.join("\r\n")
|
||||
);
|
||||
} else if (node.type === "ExportDefaultDeclaration") {
|
||||
code = replaceCode(node, `return ${node.declaration.name}`);
|
||||
}
|
||||
}
|
||||
code = `(function(){
|
||||
${code}
|
||||
})()`;
|
||||
|
||||
var componentStyle = document.createElement("style");
|
||||
componentStyle.innerHTML = compiled.css;
|
||||
document.head.appendChild(componentStyle);
|
||||
currentTabComponent.value = eval(code);
|
||||
// app.component(item.name, eval(code));
|
||||
// console.log(code);
|
||||
}
|
||||
};
|
||||
generateComponent()
|
||||
</script>
|
||||
<style>
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,12 @@
|
|||
<script setup>
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="props.name"></component>
|
||||
</template>
|
|
@ -0,0 +1,417 @@
|
|||
<!-- 算法公式 -->
|
||||
|
||||
<template>
|
||||
<a-tabs
|
||||
default-active-key="1"
|
||||
@change="tabsChange"
|
||||
v-if="conArrData.length > 0"
|
||||
>
|
||||
<a-tab-pane key="1" title="节点配置">
|
||||
<div class="dataExtend_content">
|
||||
<div class="left">
|
||||
<div>
|
||||
<span class="add" @click="handleAdd">添加字段</span>
|
||||
<div class="fieldList">
|
||||
<div
|
||||
class="fieldList_item"
|
||||
v-for="(item, index) in fieldList"
|
||||
:key="index"
|
||||
>
|
||||
<a-input
|
||||
v-model="item.value"
|
||||
size="small"
|
||||
class="fieldList_item_input"
|
||||
:readonly="item.reactive"
|
||||
/>
|
||||
<span v-if="!item.reactive" @click="sure(item)">确定</span>
|
||||
<div v-else>
|
||||
<span @click="item.reactive = false">编辑</span>
|
||||
<span @click="deploy(item)">配置</span>
|
||||
<span @click="handleDelete(index)">删除</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div v-else class="dissatisfy">
|
||||
<div class="dissatisfy_box">
|
||||
<img src="@/assets/imgs/oneNode.f1997e1e.png" alt="" />
|
||||
<p>请将1个节点连接至本节点</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 配置 -->
|
||||
<a-modal
|
||||
width="auto"
|
||||
v-model:visible="visible"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
class="a-modal"
|
||||
>
|
||||
<template #title> 函数配置 </template>
|
||||
<div class="deploy_content">
|
||||
<div style="width: 1000px">
|
||||
<codemirror
|
||||
v-if="codemirrorIf"
|
||||
:initCode="code"
|
||||
ref="codemirrorRef"
|
||||
></codemirror>
|
||||
</div>
|
||||
<div class="deploy_deploy">
|
||||
<div class="left deploy_deploy_box">
|
||||
<p class="title">参数列表</p>
|
||||
<ul>
|
||||
<li
|
||||
v-for="item in conArrData.length > 0
|
||||
? conArrData[0].columnList
|
||||
: []"
|
||||
@click="clickDeploy(item.ename)"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="right deploy_deploy_box">
|
||||
<p class="title">函数列表</p>
|
||||
<div>
|
||||
<a-collapse :default-active-key="[0]" accordion>
|
||||
<a-collapse-item
|
||||
:header="item.title"
|
||||
:key="index"
|
||||
v-for="(item, index) in mainList"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
v-for="item2 in item.list"
|
||||
@click="clickDeploy(item2.value)"
|
||||
>
|
||||
{{ item2.label }}
|
||||
</li>
|
||||
</ul>
|
||||
</a-collapse-item>
|
||||
</a-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import codemirror from "../../../components/flowEditor/codemirror.vue";
|
||||
import { ref, reactive, nextTick, toRefs } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
//子组件接收父组件传递过来的值
|
||||
nodeValue: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lineList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
conArr: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const mainList = [
|
||||
{
|
||||
title: "基础函数 - 数学函数",
|
||||
list: [
|
||||
{
|
||||
label: "SUM",
|
||||
value: "${SUM}()",
|
||||
},
|
||||
{
|
||||
label: "ADD",
|
||||
value: "${ADD}()",
|
||||
},
|
||||
{
|
||||
label: "SUBTRACT",
|
||||
value: "${SUBTRACT}()",
|
||||
},
|
||||
{
|
||||
label: "MULTIPLY",
|
||||
value: "${MULTIPLY}()",
|
||||
},
|
||||
{
|
||||
label: "DIVIDE",
|
||||
value: "${DIVIDE}()",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "基础函数 - 文字函数",
|
||||
list: [
|
||||
{
|
||||
label: "CONTRACT",
|
||||
value: "${CONTRACT}()",
|
||||
},
|
||||
{
|
||||
label: "CONCATENAT",
|
||||
value: "${CONCATENAT}()",
|
||||
},
|
||||
{
|
||||
label: "LEFT",
|
||||
value: "${LEFT}()",
|
||||
},
|
||||
{
|
||||
label: "RIGHT",
|
||||
value: "${RIGHT}()",
|
||||
},
|
||||
{
|
||||
label: "LOWER",
|
||||
value: "${LOWER}()",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "基础函数 - 逻辑函数",
|
||||
list: [
|
||||
{
|
||||
label: "NUMBERCOMP",
|
||||
value: "${NUMBERCOMP}()",
|
||||
},
|
||||
{
|
||||
label: "EQ",
|
||||
value: "${EQ}()",
|
||||
},
|
||||
{
|
||||
label: "IF",
|
||||
value: "${IF}()",
|
||||
},
|
||||
{
|
||||
label: "ISEMPTY",
|
||||
value: "${ISEMPTY}()",
|
||||
},
|
||||
{
|
||||
label: "NE",
|
||||
value: "${NE}()",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "基础函数 - 集合函数",
|
||||
list: [
|
||||
{
|
||||
label: "INTERSECTI",
|
||||
value: "${INTERSECTI}()",
|
||||
},
|
||||
{
|
||||
label: "UNIONSET",
|
||||
value: "${UNIONSET}()",
|
||||
},
|
||||
{
|
||||
label: "DIFFERENCE",
|
||||
value: "${DIFFERENCE}()",
|
||||
},
|
||||
{
|
||||
label: "SUBSET",
|
||||
value: "${SUBSET}()",
|
||||
},
|
||||
{
|
||||
label: "ARRAYGET",
|
||||
value: "${ARRAYGET}()",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
//使用父组件传递过来的值
|
||||
const { nodeValue, lineList, conArr } = toRefs(props);
|
||||
|
||||
const passIf = ref(true);
|
||||
|
||||
const getDataStream = () => {
|
||||
http
|
||||
.post("/api/metadata/data_stream/exec", {
|
||||
lineList: lineList.value,
|
||||
nodeList: conArr.value,
|
||||
streamId: 1,
|
||||
rootNodeId: nodeValue.value.id,
|
||||
})
|
||||
.then((resp) => {
|
||||
columnList.value = [];
|
||||
resp.data.columnList.map((item) => {
|
||||
columnList.value.push({
|
||||
title: item.name,
|
||||
dataIndex: item.keyword,
|
||||
});
|
||||
});
|
||||
|
||||
columnListData.value = resp.data.dataList;
|
||||
});
|
||||
};
|
||||
|
||||
const tabsChange = (key) => {
|
||||
console.log(key);
|
||||
switch (key) {
|
||||
case "2":
|
||||
if (nodeValue.value.columnList) {
|
||||
getDataStream();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const fieldList = ref([]);
|
||||
|
||||
const visible = ref(false);
|
||||
const codemirrorIf = ref(false);
|
||||
const handleAdd = () => {
|
||||
fieldList.value.push({
|
||||
value: "",
|
||||
readonly: false,
|
||||
});
|
||||
};
|
||||
const handleDelete = (index) => {
|
||||
fieldList.value.splice(index, 1);
|
||||
};
|
||||
const sure = (item) => {
|
||||
item.reactive = true;
|
||||
};
|
||||
const deploy = () => {
|
||||
visible.value = true;
|
||||
nextTick(() => {
|
||||
codemirrorIf.value = true;
|
||||
});
|
||||
};
|
||||
const handleOk = () => {};
|
||||
const handleCancel = () => {};
|
||||
|
||||
const code = ref("");
|
||||
|
||||
const lineListData = ref([]);
|
||||
const conArrData = ref([]);
|
||||
const disposeLineList = () => {
|
||||
console.log("nodeValue-------------", nodeValue.value);
|
||||
console.log("lineList-------------", lineList.value);
|
||||
lineListData.value = [];
|
||||
lineList.value.map((item) => {
|
||||
if (item.toStr == nodeValue.value.id) {
|
||||
lineListData.value.push(item);
|
||||
}
|
||||
});
|
||||
console.log(lineListData.value);
|
||||
lineListData.value.map((lineListDataItem) => {
|
||||
conArr.value.map((conArrItem) => {
|
||||
if (lineListDataItem.fromStr == conArrItem.id) {
|
||||
conArrData.value.push(conArrItem);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log("lineListData", lineListData.value);
|
||||
console.log("conArrData", conArrData.value);
|
||||
};
|
||||
|
||||
disposeLineList();
|
||||
|
||||
const codemirrorRef = ref(null);
|
||||
const clickDeploy = (value) => {
|
||||
codemirrorRef.value.replaceSelection(value)
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dataExtend_content {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
margin-left: 10px;
|
||||
.left {
|
||||
flex: 0 0 300px;
|
||||
.add {
|
||||
color: #3471ff;
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin: 0px 6px 8px;
|
||||
}
|
||||
.fieldList {
|
||||
.fieldList_item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
.fieldList_item_input {
|
||||
width: 165px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
span {
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.deploy_deploy {
|
||||
display: flex;
|
||||
font-size: 0;
|
||||
.left {
|
||||
flex: 0 0 300px;
|
||||
margin-right: 15px;
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 300px;
|
||||
li {
|
||||
font-size: 12px;
|
||||
padding: 6px 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.4s;
|
||||
&:hover {
|
||||
background-color: rgba(126, 134, 142, 0.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
flex: auto;
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
li {
|
||||
padding: 3px 0;
|
||||
cursor: pointer;
|
||||
transition: all 0.4s;
|
||||
&:hover {
|
||||
color: #165dff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.deploy_deploy_box {
|
||||
border: 1px solid rgba(17, 31, 44, 0.08);
|
||||
border-radius: 8px;
|
||||
.title {
|
||||
padding: 8px 16px;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid rgba(17, 31, 44, 0.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
.a-modal .arco-modal-body {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cm-component {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,348 @@
|
|||
<!-- 字段设置 -->
|
||||
|
||||
<template>
|
||||
<a-tabs
|
||||
default-active-key="1"
|
||||
@change="tabsChange"
|
||||
v-if="conArrData.length > 0"
|
||||
>
|
||||
<a-tab-pane key="1" title="字段配置">
|
||||
<draggable
|
||||
v-model="data.fieldSettingList"
|
||||
class="fun-classify"
|
||||
@end="() => {}"
|
||||
@start="(e) => move(e, 'baseArray')"
|
||||
item-key="id"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="fun-item" :data-type="element.type">
|
||||
<div class="field-item-head">
|
||||
<span>{{ element.name }}</span>
|
||||
<div class="head-action" @click="clickSet(element)">
|
||||
<icon-settings />
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-item-cell">
|
||||
<div class="type-item">
|
||||
<span>类型:</span>
|
||||
<div class="type-item-content">
|
||||
<span class="select-option-type string">数据类型</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div v-else class="dissatisfy">
|
||||
<div class="dissatisfy_box">
|
||||
<img src="@/assets/imgs/oneNode.f1997e1e.png" alt="" />
|
||||
<p>请将1个节点连接至本节点</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-modal
|
||||
width="auto"
|
||||
v-model:visible="visible"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<template #title> 字段设置 </template>
|
||||
<a-form :model="form" :style="{ width: '500px' }" label-align="left">
|
||||
<a-form-item field="name" label="字段名:">
|
||||
<a-input v-model="form.name" placeholder="请输入字段名" />
|
||||
</a-form-item>
|
||||
<a-form-item field="type" label="类型:">
|
||||
<a-select placeholder="请选择数据类型">
|
||||
<a-option>字符串</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="type" label="值替换:">
|
||||
<a-button type="outline" @click="addList">新增替换</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-form
|
||||
:model="form"
|
||||
:style="{ width: '500px' }"
|
||||
label-align="left"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-row
|
||||
class="replace-item"
|
||||
:gutter="12"
|
||||
v-for="(item, index) in form.list"
|
||||
:key="index"
|
||||
>
|
||||
<a-col :span="7">
|
||||
<a-form-item label="替换类型:">
|
||||
<a-select placeholder="未选择" v-model="item.type">
|
||||
<a-option value="null">空值</a-option>
|
||||
<a-option value="custom">自定义值</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="7" v-if="item.type == 'custom'">
|
||||
<a-form-item field="name" label="原来值:">
|
||||
<a-input
|
||||
placeholder="请输入原来的值"
|
||||
allow-clear
|
||||
v-model="item.oldVal"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="7" v-if="item.type == 'custom' || item.type == 'null'">
|
||||
<a-form-item field="name" label="替换值:">
|
||||
<a-input
|
||||
placeholder="请输入替换值"
|
||||
allow-clear
|
||||
v-model="item.newVal"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="3">
|
||||
<a-form-item>
|
||||
<div class="del" @click="clickDel(index)"><icon-delete /></div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, toRefs } from "vue";
|
||||
import draggable from "vuedraggable";
|
||||
import { IconSettings, IconDelete } from "@arco-design/web-vue/es/icon";
|
||||
import http from "@/scripts/request";
|
||||
|
||||
const data = reactive({
|
||||
fieldSettingList: [],
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
//子组件接收父组件传递过来的值
|
||||
nodeValue: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lineList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
conArr: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
//使用父组件传递过来的值
|
||||
const { nodeValue, lineList, conArr } = toRefs(props);
|
||||
|
||||
const lineListData = ref([]);
|
||||
const conArrData = ref([]);
|
||||
const disposeLineList = () => {
|
||||
lineListData.value = [];
|
||||
lineList.value.map((item) => {
|
||||
if (item.toStr == nodeValue.value.id) {
|
||||
lineListData.value.push(item);
|
||||
}
|
||||
});
|
||||
console.log(lineListData.value);
|
||||
lineListData.value.map((lineListDataItem) => {
|
||||
conArr.value.map((conArrItem) => {
|
||||
if (lineListDataItem.fromStr == conArrItem.id) {
|
||||
conArrData.value.push(conArrItem);
|
||||
|
||||
let columnList = JSON.parse(JSON.stringify(conArrItem.columnList));
|
||||
columnList.map((item) => {
|
||||
item.nodeId = nodeValue.value.id;
|
||||
});
|
||||
|
||||
data.fieldSettingList = JSON.parse(JSON.stringify(columnList));
|
||||
|
||||
nodeValue.value.columnList = columnList;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log("lineListData", lineListData.value);
|
||||
console.log("conArrData", conArrData.value);
|
||||
};
|
||||
|
||||
disposeLineList();
|
||||
|
||||
const move = () => {};
|
||||
|
||||
//tab 切换
|
||||
|
||||
const tabsChange = () => {};
|
||||
|
||||
// 设置字段
|
||||
const visible = ref(false);
|
||||
const form = reactive({
|
||||
name: "",
|
||||
type: "",
|
||||
id: "",
|
||||
list: [],
|
||||
});
|
||||
|
||||
const clickSet = (el) => {
|
||||
console.log(nodeValue.value);
|
||||
form.name = el.name;
|
||||
form.id = el.keyword;
|
||||
form.list = [];
|
||||
let nodeValueData = nodeValue.value.data
|
||||
? JSON.parse(nodeValue.value.data)
|
||||
: [];
|
||||
console.log("nodeValue", nodeValue.value.data, nodeValueData);
|
||||
|
||||
if (nodeValueData instanceof Array) {
|
||||
nodeValueData.map((item) => {
|
||||
if (item.columnName == el.keyword) {
|
||||
form.list.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
console.log(form);
|
||||
let dataList = [];
|
||||
form.list.map((item) => {
|
||||
console.log(item);
|
||||
dataList.push({
|
||||
columnName: form.id,
|
||||
type: item.type,
|
||||
oldVal: item.oldVal,
|
||||
newVal: item.newVal,
|
||||
});
|
||||
});
|
||||
console.log("dataList", form);
|
||||
console.log(nodeValue.value);
|
||||
let nodeValueData = [];
|
||||
if (nodeValue.value.data !== "") {
|
||||
nodeValueData = JSON.parse(nodeValue.value.data);
|
||||
}
|
||||
|
||||
nodeValueData.map((item) => {
|
||||
if (item.columnName != form.id) {
|
||||
dataList.push(item);
|
||||
}
|
||||
});
|
||||
console.log("dataList", dataList);
|
||||
nodeValue.value.data = JSON.stringify(dataList);
|
||||
};
|
||||
const handleCancel = () => {};
|
||||
|
||||
// 新增值替换
|
||||
const addList = () => {
|
||||
form.list.push({
|
||||
type: "",
|
||||
oldVal: "",
|
||||
newVal: "",
|
||||
});
|
||||
};
|
||||
|
||||
const clickDel = (index) => {
|
||||
form.list.splice(index, 1);
|
||||
};
|
||||
|
||||
// const getMetadataColumn = () => {
|
||||
// http.get("/api/metadata/dict", {}).then((resp) => {
|
||||
// console.log(resp);
|
||||
// });
|
||||
// };
|
||||
|
||||
// getMetadataColumn();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.arco-tabs-content) {
|
||||
padding-top: 0;
|
||||
}
|
||||
.fun-classify {
|
||||
height: 360px;
|
||||
}
|
||||
.fun-item {
|
||||
width: 180px;
|
||||
height: calc(100% - 2px);
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
border-color: #e9e9e9;
|
||||
border-style: solid;
|
||||
border-width: 1px 1px 1px 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
cursor: move;
|
||||
.field-item-head {
|
||||
border-color: #e9e9e9;
|
||||
border-style: solid;
|
||||
border-width: 0 0 1px 0;
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 115px;
|
||||
}
|
||||
.head-action {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
.field-item-cell {
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
.type-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 25px;
|
||||
span {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
}
|
||||
.type-item-content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.string {
|
||||
color: #1485f6;
|
||||
background: rgba(20, 133, 246, 0.15);
|
||||
}
|
||||
.select-option-type {
|
||||
float: left;
|
||||
flex: none;
|
||||
min-width: 44px;
|
||||
height: 18px;
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
border-radius: 40px;
|
||||
box-sizing: border-box;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.replace-item {
|
||||
position: relative;
|
||||
.del {
|
||||
cursor: pointer;
|
||||
margin-top: 26px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,293 @@
|
|||
<!-- 数据筛选 -->
|
||||
|
||||
<template>
|
||||
<a-tabs
|
||||
default-active-key="1"
|
||||
@change="tabsChange"
|
||||
v-if="conArrData.length > 0"
|
||||
>
|
||||
<a-tab-pane key="1" title="节点配置">
|
||||
<div class="data-filter-box">
|
||||
<div>
|
||||
<span>筛选出符合以下</span>
|
||||
<a-select
|
||||
:style="{ width: '80px', margin: '0 10px' }"
|
||||
placeholder="Select"
|
||||
:trigger-props="{ autoFitPopupMinWidth: true }"
|
||||
v-model="data.type"
|
||||
>
|
||||
<a-option value="and">所有</a-option>
|
||||
<a-option value="or">任一</a-option>
|
||||
</a-select>
|
||||
<span>条件的数据</span>
|
||||
</div>
|
||||
<div class="data-filter-add">
|
||||
<a-dropdown @select="handleSelect" position="bl">
|
||||
<a-button type="primary" size="small">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
<template #default>添加过滤条件</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<a-doption
|
||||
v-for="item in columnList"
|
||||
:key="item.id"
|
||||
:value="item.keyword"
|
||||
>{{ item.name }}</a-doption
|
||||
>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
<div class="filter-add-list">
|
||||
<div
|
||||
class="filter-add-list-box"
|
||||
v-for="(item, index) in data.conditionList"
|
||||
:key="index"
|
||||
>
|
||||
<a-input
|
||||
class="item"
|
||||
:style="{ width: '200px' }"
|
||||
v-model="item.columnText"
|
||||
placeholder="Please enter something"
|
||||
readonly
|
||||
/>
|
||||
<a-select
|
||||
class="item"
|
||||
v-model="item.condition"
|
||||
:style="{ width: '150px', marginRight: '15px' }"
|
||||
placeholder="请选择条件"
|
||||
:trigger-props="{ autoFitPopupMinWidth: true }"
|
||||
@change="update"
|
||||
>
|
||||
<a-option value="eq">等于</a-option>
|
||||
<a-option value="ne">不等于</a-option>
|
||||
</a-select>
|
||||
<div>
|
||||
<a-input
|
||||
class="item"
|
||||
:style="{ width: '200px' }"
|
||||
v-model="item.val"
|
||||
placeholder="请输入筛选条件"
|
||||
allow-clear
|
||||
@blur="update"
|
||||
/>
|
||||
<!-- <a-tag
|
||||
v-for="(tag, index) of tags"
|
||||
:key="tag"
|
||||
:closable="index !== 0"
|
||||
@close="handleRemove(tag)"
|
||||
>
|
||||
{{ tag }}
|
||||
</a-tag>
|
||||
|
||||
<a-input
|
||||
v-if="showInput"
|
||||
ref="inputRef"
|
||||
:style="{ width: '90px' }"
|
||||
size="mini"
|
||||
v-model.trim="inputVal"
|
||||
@keyup.enter="handleAdd"
|
||||
@blur="handleAdd"
|
||||
/>
|
||||
<a-tag
|
||||
v-else
|
||||
:style="{
|
||||
width: '90px',
|
||||
backgroundColor: 'var(--color-fill-2)',
|
||||
border: '1px dashed var(--color-fill-3)',
|
||||
cursor: 'pointer',
|
||||
}"
|
||||
@click="handleEdit"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
添加
|
||||
</a-tag> -->
|
||||
</div>
|
||||
<div class="del" @click="clickDel(index)"><icon-delete /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div v-else class="dissatisfy">
|
||||
<div class="dissatisfy_box">
|
||||
<img src="@/assets/imgs/oneNode.f1997e1e.png" alt="" />
|
||||
<p>请将1个节点连接至本节点</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, toRefs } from "vue";
|
||||
import { IconPlus, IconDelete } from "@arco-design/web-vue/es/icon";
|
||||
|
||||
const data = reactive({
|
||||
type: "and",
|
||||
conditionList: [],
|
||||
});
|
||||
|
||||
const columnList = ref([]);
|
||||
|
||||
const props = defineProps({
|
||||
//子组件接收父组件传递过来的值
|
||||
nodeValue: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lineList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
conArr: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
//使用父组件传递过来的值
|
||||
const { nodeValue, lineList, conArr } = toRefs(props);
|
||||
|
||||
const lineListData = ref([]);
|
||||
const conArrData = ref([]);
|
||||
const disposeLineList = () => {
|
||||
console.log("nodeValue-------------", nodeValue.value);
|
||||
console.log("lineList-------------", lineList.value);
|
||||
lineListData.value = [];
|
||||
lineList.value.map((item) => {
|
||||
if (item.toStr == nodeValue.value.id) {
|
||||
lineListData.value.push(item);
|
||||
}
|
||||
});
|
||||
console.log(lineListData.value);
|
||||
lineListData.value.map((lineListDataItem) => {
|
||||
conArr.value.map((conArrItem) => {
|
||||
if (lineListDataItem.fromStr == conArrItem.id) {
|
||||
conArrData.value.push(conArrItem);
|
||||
|
||||
columnList.value = JSON.parse(JSON.stringify(conArrItem.columnList));
|
||||
|
||||
columnList.value.map(item=>{
|
||||
item.nodeId = nodeValue.value.id
|
||||
})
|
||||
|
||||
nodeValue.value.columnList = JSON.parse(
|
||||
JSON.stringify(columnList.value)
|
||||
);
|
||||
|
||||
if (nodeValue.value.data) {
|
||||
let nodeValueData = JSON.parse(nodeValue.value.data);
|
||||
data.type = nodeValueData.type;
|
||||
data.conditionList = nodeValueData.conditionList;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log("lineListData", lineListData.value);
|
||||
console.log("conArrData", conArrData.value);
|
||||
};
|
||||
|
||||
disposeLineList();
|
||||
|
||||
//切换tab
|
||||
|
||||
const tabsChange = () => {};
|
||||
|
||||
// tab
|
||||
|
||||
const tags = ref(["Tag 1", "Tag 2", "Tag 3"]);
|
||||
const inputRef = ref(null);
|
||||
const showInput = ref(false);
|
||||
const inputVal = ref("");
|
||||
|
||||
const handleEdit = () => {
|
||||
showInput.value = true;
|
||||
|
||||
nextTick(() => {
|
||||
if (inputRef.value) {
|
||||
inputRef.value.focus();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
if (inputVal.value) {
|
||||
tags.value.push(inputVal.value);
|
||||
inputVal.value = "";
|
||||
}
|
||||
showInput.value = false;
|
||||
};
|
||||
|
||||
const handleRemove = (key) => {
|
||||
tags.value = tags.value.filter((tag) => tag !== key);
|
||||
};
|
||||
|
||||
// end tab
|
||||
|
||||
const handleSelect = (val) => {
|
||||
columnList.value.map((item) => {
|
||||
if (item.keyword == val) {
|
||||
data.conditionList.push({
|
||||
columnText: item.name,
|
||||
columnName: item.keyword,
|
||||
condition: "",
|
||||
val: "",
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 更新数据
|
||||
|
||||
const update = () => {
|
||||
let nodeValueDate = {
|
||||
type: data.type,
|
||||
conditionList: data.conditionList,
|
||||
};
|
||||
nodeValue.value.data = JSON.stringify(nodeValueDate);
|
||||
};
|
||||
|
||||
|
||||
const clickDel = (index)=>{
|
||||
data.conditionList.splice(index,1)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.data-filter-box {
|
||||
padding: 0 16px;
|
||||
.data-filter-add {
|
||||
margin: 6px 0;
|
||||
}
|
||||
}
|
||||
.filter-add-list {
|
||||
.filter-add-list-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
.item {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.item-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #efefef;
|
||||
padding: 3px 3px 0;
|
||||
border-radius: 4px;
|
||||
max-width: 300px;
|
||||
flex-wrap: wrap;
|
||||
span {
|
||||
margin-left: 5px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
}
|
||||
.del {
|
||||
cursor: pointer;
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,334 @@
|
|||
<template>
|
||||
<a-tabs
|
||||
default-active-key="1"
|
||||
@change="tabsChange"
|
||||
v-if="conArrData.length > 0"
|
||||
>
|
||||
<a-tab-pane key="1" title="节点配置">
|
||||
<div class="grouping-content">
|
||||
<div class="grouping-content-left">
|
||||
<div class="group-title"><span class="label">分组字段</span></div>
|
||||
<a-dropdown @select="handleSelect" position="bl">
|
||||
<a-button type="primary" size="small">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
<template #default>添加分组字段</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<a-doption
|
||||
v-for="item in columnList"
|
||||
:key="item.id"
|
||||
:value="item.keyword"
|
||||
>{{ item.name }}</a-doption
|
||||
>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<div class="grouping-list">
|
||||
<div class="grouping-list-item" v-for="item in leftGroupingList">
|
||||
<span class="name">{{ item.name }}</span>
|
||||
|
||||
<div class="del" @click="clickLeftDel(index)">
|
||||
<icon-delete />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grouping-content-right">
|
||||
<div class="group-title">
|
||||
<span class="label">汇总字段</span>
|
||||
<div>
|
||||
<span>是否保留原字段:</span>
|
||||
<a-switch v-model="nodeValueData.isRetain" @change="initData">
|
||||
<template #checked> 是 </template>
|
||||
<template #unchecked> 否 </template>
|
||||
</a-switch>
|
||||
</div>
|
||||
</div>
|
||||
<a-dropdown @select="addSummary" position="bl">
|
||||
<a-button type="primary" size="small">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
<template #default>添加汇总字段</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<a-doption
|
||||
v-for="item in columnList"
|
||||
:key="item.id"
|
||||
:value="item.keyword"
|
||||
>{{ item.name }}</a-doption
|
||||
>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<div class="grouping-list">
|
||||
<div class="grouping-list-item" v-for="item in rightGroupingList">
|
||||
<span class="name">{{ item.name }}</span>
|
||||
<a-select
|
||||
:style="{ width: '160px' }"
|
||||
v-model="item.type"
|
||||
placeholder="请选择汇总方式"
|
||||
@change="initData()"
|
||||
>
|
||||
<a-option value="count">总数</a-option>
|
||||
</a-select>
|
||||
<div class="del" @click="clickRightDel(index)">
|
||||
<icon-delete />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" title="查看数据">
|
||||
<a-table
|
||||
:columns="dataColumnList"
|
||||
:data="columnListData"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div v-else class="dissatisfy">
|
||||
<div class="dissatisfy_box">
|
||||
<img src="@/assets/imgs/oneNode.f1997e1e.png" alt="" />
|
||||
<p>请将1个节点连接至本节点</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup >
|
||||
import http from "@/scripts/request";
|
||||
import { ref, reactive, nextTick, toRefs } from "vue";
|
||||
import { IconPlus, IconDelete } from "@arco-design/web-vue/es/icon";
|
||||
|
||||
const columnList = ref([]);
|
||||
|
||||
const leftGroupingList = ref([]); //left 数据列表
|
||||
const rightGroupingList = ref([]); //left 数据列表
|
||||
let nodeValueData = ref({
|
||||
isRetain: false,
|
||||
groupColumnNameList: [],
|
||||
collectColumnList: [],
|
||||
});
|
||||
const props = defineProps({
|
||||
//子组件接收父组件传递过来的值
|
||||
nodeValue: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lineList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
conArr: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
//使用父组件传递过来的值
|
||||
const { nodeValue, lineList, conArr } = toRefs(props);
|
||||
|
||||
const lineListData = ref([]);
|
||||
const conArrData = ref([]);
|
||||
const disposeLineList = () => {
|
||||
console.log("nodeValue-------------", nodeValue.value);
|
||||
console.log("lineList-------------", lineList.value);
|
||||
lineListData.value = [];
|
||||
lineList.value.map((item) => {
|
||||
if (item.toStr == nodeValue.value.id) {
|
||||
lineListData.value.push(item);
|
||||
}
|
||||
});
|
||||
console.log(lineListData.value);
|
||||
lineListData.value.map((lineListDataItem) => {
|
||||
conArr.value.map((conArrItem) => {
|
||||
if (lineListDataItem.fromStr == conArrItem.id) {
|
||||
conArrData.value.push(conArrItem);
|
||||
|
||||
columnList.value = JSON.parse(JSON.stringify(conArrItem.columnList));
|
||||
|
||||
columnList.value.map((item) => {
|
||||
item.nodeId = nodeValue.value.id;
|
||||
});
|
||||
|
||||
nodeValue.value.columnList = JSON.parse(
|
||||
JSON.stringify(columnList.value)
|
||||
);
|
||||
|
||||
console.log(nodeValue.value.data);
|
||||
if (nodeValue.value.data) {
|
||||
nodeValueData.value = JSON.parse(nodeValue.value.data);
|
||||
leftGroupingList.value = [];
|
||||
nodeValueData.value.groupColumnNameList.map((item) => {
|
||||
columnList.value.map((groupColumnNameListItem) => {
|
||||
if (groupColumnNameListItem.keyword == item) {
|
||||
leftGroupingList.value.push(groupColumnNameListItem);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
rightGroupingList.value = [];
|
||||
nodeValueData.value.collectColumnList.map((item) => {
|
||||
columnList.value.map((columnListItem) => {
|
||||
if (columnListItem.keyword == item.columnName) {
|
||||
rightGroupingList.value.push({
|
||||
name: columnListItem.name,
|
||||
keyword: columnListItem.keyword,
|
||||
type: item.type,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log("不存在");
|
||||
nodeValueData.value = {
|
||||
groupColumnNameList: [],
|
||||
collectColumnList: [],
|
||||
isRetain: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log("lineListData", lineListData.value);
|
||||
console.log("conArrData", conArrData.value);
|
||||
};
|
||||
|
||||
disposeLineList();
|
||||
|
||||
const tabsChange = (key) => {
|
||||
console.log(key);
|
||||
switch (key) {
|
||||
case "2":
|
||||
if (nodeValue.value.columnList) {
|
||||
getDataStream();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// left 列表数据处理
|
||||
|
||||
const handleSelect = (keyword) => {
|
||||
columnList.value.map((item) => {
|
||||
if (item.keyword == keyword) {
|
||||
leftGroupingList.value.push(item);
|
||||
}
|
||||
});
|
||||
initData();
|
||||
};
|
||||
const clickLeftDel = (index) => {
|
||||
leftGroupingList.value.splice(index, 1);
|
||||
initData();
|
||||
};
|
||||
const clickRightDel = (index) => {
|
||||
rightGroupingList.value.splice(index, 1);
|
||||
initData();
|
||||
};
|
||||
|
||||
const addSummary = (keyword) => {
|
||||
columnList.value.map((item) => {
|
||||
if (item.keyword == keyword) {
|
||||
rightGroupingList.value.push({
|
||||
name: item.name,
|
||||
keyword: item.keyword,
|
||||
type: "",
|
||||
});
|
||||
}
|
||||
});
|
||||
initData();
|
||||
};
|
||||
|
||||
// 保存数据
|
||||
const initData = () => {
|
||||
nodeValueData.value.groupColumnNameList = [];
|
||||
leftGroupingList.value.map((item) => {
|
||||
nodeValueData.value.groupColumnNameList.push(item.keyword);
|
||||
});
|
||||
|
||||
nodeValueData.value.collectColumnList = [];
|
||||
rightGroupingList.value.map((item) => {
|
||||
nodeValueData.value.collectColumnList.push({
|
||||
columnName: item.keyword,
|
||||
type: item.type,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
nodeValue.value.data = JSON.stringify(nodeValueData.value);
|
||||
};
|
||||
|
||||
//查看数据
|
||||
const dataColumnList = ref([])
|
||||
const columnListData = ref([])
|
||||
const getDataStream = () => {
|
||||
http
|
||||
.post("/api/metadata/data_stream/exec", {
|
||||
lineList: lineList.value,
|
||||
nodeList: conArr.value,
|
||||
streamId: 1,
|
||||
rootNodeId: nodeValue.value.id,
|
||||
})
|
||||
.then((resp) => {
|
||||
dataColumnList.value = [];
|
||||
resp.data.columnList.map((item) => {
|
||||
dataColumnList.value.push({
|
||||
title: item.name,
|
||||
dataIndex: item.keyword,
|
||||
});
|
||||
});
|
||||
|
||||
columnListData.value = resp.data.dataList;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.grouping-content {
|
||||
display: flex;
|
||||
.grouping-content-left {
|
||||
flex: 0 0 260px;
|
||||
border-right: 1px solid #e9e9e9;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.group-title {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
.label {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
.grouping-content-right {
|
||||
flex: auto;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.grouping-list {
|
||||
.grouping-list-item {
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.name {
|
||||
flex: 0 0 200px;
|
||||
}
|
||||
.del {
|
||||
flex: 0 0 20px;
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,243 @@
|
|||
<!-- 数据连接 -->
|
||||
<template>
|
||||
<a-tabs default-active-key="1" @change="tabsChange" v-if="passIf">
|
||||
<a-tab-pane key="1" :title="nodeValue.name">
|
||||
<div id="join_content">
|
||||
<div class="left">
|
||||
<h4>1.设置连接方式</h4>
|
||||
<a-radio-group v-model="data.joinType" :options="options" />
|
||||
</div>
|
||||
<div class="right">
|
||||
<h4>2.添加连接方式</h4>
|
||||
<div class="content">
|
||||
<div>
|
||||
<h5>左侧表单:</h5>
|
||||
<a-select
|
||||
v-model="data.leftColumnName"
|
||||
:style="{ width: '220px' }"
|
||||
placeholder="请选择左侧表单字段"
|
||||
@change="setData"
|
||||
>
|
||||
<a-option
|
||||
v-for="item in conArrData[0].columnList"
|
||||
:value="item.keyword"
|
||||
>{{ item.name }}</a-option
|
||||
>
|
||||
</a-select>
|
||||
</div>
|
||||
<div>
|
||||
<h5>右侧表单:</h5>
|
||||
<a-select
|
||||
v-model="data.rightColumnName"
|
||||
:style="{ width: '220px' }"
|
||||
placeholder="请选择右侧表单字段"
|
||||
@change="setData"
|
||||
>
|
||||
<a-option
|
||||
v-for="item in conArrData[1].columnList"
|
||||
:value="item.keyword"
|
||||
>{{ item.name }}</a-option
|
||||
>
|
||||
</a-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" title="查看数据">
|
||||
<a-table
|
||||
:columns="columnList"
|
||||
:data="columnListData"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div v-else class="dissatisfy">
|
||||
<div class="dissatisfy_box">
|
||||
<img
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAcAAAACQCAMAAACoAksCAAAB71BMVEUAAACMsdufveF1oNJ1oNF2p9h3o9N0odN7pNR4o9N7pdR0odN4otL/eXf/eHb/eXf/e3v/eXb/enf/eXf/eXb/fnh1oNL/e3mZud95o9P/jIzkoqr8kZL/eXf/hYX/eXh2odL/enj/eniFrub/eXj/eXj/end2oNP/enj/enj/fHn/fn5/qtyGrNmBqNf/e3n/eHb/eXd1odN3odT8lpf9jIz/e3n/eHf/eXf/eXf/eXd4odV5otZ4odf+gX93odN3odP/enl3otN2odN2oNL/eXf/eHZ1oNL/eXf/eXf/eXj/gID7oKH/eXf/eXZ1oNN1oNJ0oNL/eXd3odL/fn78m5v/eHZ3o9P/eHZ1oNN2oNL4fH3jhIzYhpOykKt4otKNnMTy+P/N4v50n9H/eHauyOfr9P3l8P/c6ffw9//Y6P/O4/+sxubq8//T5v/d6//P5P/05Oro8v79hoXt9f/z7/b20td6o9Ty9Pv4ur3+gIDf6/iwyuidvOF/p9b3xMj+e3rQ4PO50Ov03uSkweOStd2MsNr2ys7y9/74vsH5sLP6pqjW6P/S5f/l7vvV4/Xz5+62zurj7//f7f/a6v/I2vHC1u690uynxOX12t/5trn7qavh7PrZ5/f4t7r5rK60vtu4utXIqr/Ro7TokJcHc7DKAAAAYXRSTlMA+P2fWRo6f/nF+Vy99pm4NMRa18se/Sj9+wYC/v4JSvVEOwask3JOTjcxEw39+fCznWZE/v38wI+EaiwfFvvp4t7WtKilo5GMeGQN/urQm31xVD4Z++PCfnNb+u7n1cjEP8ysLwAAB8xJREFUeNrs3XdTE0EYx/EH+6hBgiQhhIBUEVHE3nvvvbfncopHQCGFqhRBQbD33l+oe7k1CTjqBBeze3k+zsjPG//7GgjJuYCp7NiU9Ju/Hsi4+I7MC3hnpJvXHdi2AUjqfLO9rx5i+l3ty5q3DkjKzni7UA5G/tYyICmqmfcKZfHQOwtIipYFZPj8yWVNBpKiKV6Ux+vZQFI0aybKo5ECUsBMQwEVRwEVRwEVRwEVRwEVRwEVRwEVRwEVRwEV90vApuvBZA+eYhLjeXNQoOYGgwIKDmgEtTGaMOG5JlgDBRQcsF4b6wYmPNAEC2JctKOwd+WW3S4gExewWRMNf2rtDL9tbe1Y4dgLRMGA/b0jUWTa3/pzgSgXsH3FCHIdtceBqBbwXmcUuaEVdUBUC7h8GON6avflxABAUY7lONsuvn0ABXy6ki4XjL3sKoIMIEnA8CDG9evFegwAeHTLGrZ38M26uPhcDQDZfC8EWMhnNgCsZh+dawvA7iQJ2DkRAZm5C8DmJAlY+BbjBh0Lc2MAYF+upZrtUr59AEVJlxfwbX4K5dOsVp27NvuArjvsXlCSgB/D7fjT8nIQpGiNXm73T6KSBIyGh5HrcXhAmFIf2JwkAbHV3zEUG4MrK4GoFxB7egsHW1paT+i7fSCUbyHYmTQBsWWkU9cdc0pBrLraJWBnYwNGtLFCmNCgCXYfkwx9vlwAoq3SD4Kd8YAJT4NasuCNUXnva0Jdj0z4+4Fz9LlgZzxgMiMZjmEIhUgB6Z4YCigNCkgBKWAKlAi4e9VOsDPbB7Q7Cqg4Cqg42wcsXnsU7Mz2ASfiWWjZumnpt54CjlPNrnkBGWyeD8zZGSgPJQJO3bz4ZdfVtAs9uuKeHDtqK4TSyJ8MIH3A2VlXUQ6P3LMANrhfoCyuepfKH3C+W5bTHRGfzCsD2LVYmofgwOYaANkDHspCabwPHAMo27q4D2XwMN99DABkD7jtNcrDOwUAag4FvFnpNzOwdR1MgJziUhBpdiPKY+YsMK1benh62h1ZpsZxvTIGJBQwc1DA/2znyVUUUGXsWSgFVBkFVBwFVBwFVBwFVBwFVFxFXnn6AobuXxepIZKBAZm0BWzSBGs2KOC4FTn37Esx4DNNtBAFHDeXvrJ2T1FKAW9ootVnXsCS3CpRAfvvbaytpIB/J+ezUJfegu0dG899a8dRXgzcunVr0kvEvth4ZVBAeQMiS9gZvtee3C+w6O7du4sCT/oC3bHRSAFlDogYHZ3w1iI05d9p7DbMMdBNAeUOaCXsYQl5OfabYXQ96npkmAWvtFFAwQEdeTEeAFhlzQo2c/Ms1ebfsWY2m1X88gK2l1hzJ5vFeXkHzYBc9GNvOJoIeCXA3aGAID4glwMADmvOYbNSt5SyfcCaTjb36pZitrdY86CZ2xyJgK2FvZ+GEgG7bt++/YT9ut1HARnhj0BnjMf8gzVXs3nUaalme4k1K9is4pcXsF1uzTo2S5zOPL0lke/dY3PwgKbGO1fuWoO+BopVWlUi7Gsgz+dn+eKeBF7fvHmzsW3gZWDAHN35FFBOLKCVb4R9TIgMdLe1tXXfCmGjOdryH1JAObGAZr4LX+iVGDW59J5C/w4PvZT2n2XPXSIqoL/cA5BKwHpNtEjmBRT2jbyv0pPyG7oNmljPMvAN3fTeUhFpEsnIxFsq6J4YxVFAxVFAxVFAxbFvIyggoYC2QQEVJ2XAsg1T064GJkbp0RK7B5x2yh2QwOYjPrDQs9CUAh52T3r54Vra9d30XtoAABQwlYBnAZZJc1ja+0XbAaQPuD0fpXHVPR98m+T5F/XBvQxA9oBnZhooiz73VJgWkOfwPczaBSB7wA2bbqIkIosmA0zxojxebwOQPSDMdzdGUAZdWZtq6Mjl8RTc5M2alH6L3bOnZsKZ2SW5xSBYzdJdk9Pu0PRlmXHoud1RQMVRQMXZPqB5zIid2T4g/fzA36OAEqCAiqOAiqOAivsloHGjIdnzJkxW3yDUUwooOqDxQBstGMGEZ5pgNyY8YN1JJ9jZ2IChP/6PGFZXrGZMeDP8/Xx2lQ/IvwSs/+ODpFkTDX96vNy/fHj4xMq51UAUDBgNr+iPdSx05ABRL+D+wnZrDO2fWwDCeFYtAHuTJOBgbwty0Y2nxfXbovtt/oCWJOD+EYzr2AJCFOVUOHS9AuxNkoDhHox7o5c4LGsBYIk1D7BZzS9Xsr2DbzY9fJ5mu47vIgCXbtpt92e1kgTsHMS4fr1Kt1QmzjozS+Xolj1sl/NtBuRzDds7+eYBnSVgd5IEXHEP41r9nmxLCQBUWrOCTRe/XMz2Wr7ZPM5nFdt7+S4AWLhmrwfsT5KAn5Zj3Dt7v3Riz4AtvT3IvfEXA1EtIN7rfMNThuuAqBdwaEQffmyeZrzSuQ+IegERe8J650a9dk8BkH8I2KSN9RQT7muCPcAk/V8vllK+cQfknmuj3R+VN6gJFQzRfaGiA2JTfbImHMUI1QsUMujGXronJsNRQMVRQMVRQMVRQMVRQMVRQMVRQMVRQMX92GgEDnEwGoFDHKiLDY5jh8CAyYVhFJAIbJL7EwcN0AtgGAWkAh7GQXP6XpOCDcMoIBWwKkgUJQ4KMF3MiGEUkA6snSQlclIHHDDpKYzGH3lA2cglnHPAgTP7aPlJBgAAYcggW3jZ838AAAAASUVORK5CYII="
|
||||
/>
|
||||
<p>请将2个节点连接至本节点</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import http from "@/scripts/request";
|
||||
import { reactive, toRefs, watch, ref } from "vue";
|
||||
const data = ref({
|
||||
joinType: "inner",
|
||||
rightNodeId: "",
|
||||
rightColumnName: "",
|
||||
leftNodeId: "",
|
||||
leftColumnName: "",
|
||||
});
|
||||
|
||||
const lineListData = ref([]); //连接对象
|
||||
const conArrData = ref([]);
|
||||
const options = [
|
||||
{ label: "内连接", value: "inner" },
|
||||
{ label: "左连接", value: "left" },
|
||||
{ label: "右连接", value: "right" },
|
||||
];
|
||||
|
||||
const columnList = ref([]);
|
||||
const columnListData = ref([]);
|
||||
const passIf = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
nodeValue: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lineList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
conArr: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
//使用父组件传递过来的值
|
||||
const { nodeValue, lineList, conArr } = toRefs(props);
|
||||
|
||||
const disposeLineList = () => {
|
||||
console.log("nodeValue-------------", nodeValue.value);
|
||||
console.log("lineList-------------", lineList.value);
|
||||
lineListData.value = [];
|
||||
conArrData.value = [];
|
||||
lineList.value.map((item) => {
|
||||
if (item.toStr == nodeValue.value.id) {
|
||||
lineListData.value.push(item);
|
||||
}
|
||||
});
|
||||
console.log(lineListData.value);
|
||||
lineListData.value.map((lineListDataItem) => {
|
||||
conArr.value.map((conArrItem) => {
|
||||
if (lineListDataItem.fromStr == conArrItem.id) {
|
||||
conArrData.value.push(JSON.parse(JSON.stringify(conArrItem)));
|
||||
}
|
||||
});
|
||||
});
|
||||
console.log("conArrData", conArrData.value);
|
||||
if (conArrData.value.length == 2) {
|
||||
data.value.leftNodeId = conArrData.value[0].id;
|
||||
data.value.rightNodeId = conArrData.value[1].id;
|
||||
|
||||
passIf.value = true;
|
||||
// if (nodeValue.value.data) {
|
||||
// data.value = JSON.parse(nodeValue.value.data);
|
||||
// }
|
||||
} else {
|
||||
passIf.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
disposeLineList();
|
||||
|
||||
watch(
|
||||
() => lineList.value.length,
|
||||
(newVal, oldVal) => {
|
||||
disposeLineList();
|
||||
}
|
||||
);
|
||||
|
||||
const setData = () => {
|
||||
conArr.value.map((item) => {
|
||||
console.log(item.id);
|
||||
console.log(nodeValue.value.id);
|
||||
|
||||
if (item.id == nodeValue.value.id) {
|
||||
item.data = JSON.stringify(data.value);
|
||||
conArrData.value.map((item) => {
|
||||
item.columnList.map((item2) => {
|
||||
item2.nodeId = nodeValue.value.id;
|
||||
});
|
||||
});
|
||||
item.columnList = [
|
||||
...conArrData.value[0].columnList,
|
||||
...conArrData.value[1].columnList,
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
console.log(conArr.value);
|
||||
};
|
||||
|
||||
const getDataStream = () => {
|
||||
http
|
||||
.post("/api/metadata/data_stream/exec", {
|
||||
lineList: lineList.value,
|
||||
nodeList: conArr.value,
|
||||
streamId: 1,
|
||||
rootNodeId: nodeValue.value.id,
|
||||
})
|
||||
.then((resp) => {
|
||||
columnList.value = [];
|
||||
resp.data.columnList.map((item) => {
|
||||
columnList.value.push({
|
||||
title: item.name,
|
||||
dataIndex: item.keyword,
|
||||
});
|
||||
});
|
||||
|
||||
columnListData.value = resp.data.dataList;
|
||||
});
|
||||
};
|
||||
|
||||
const tabsChange = (key) => {
|
||||
console.log(key);
|
||||
switch (key) {
|
||||
case "2":
|
||||
if (nodeValue.value.columnList) {
|
||||
getDataStream();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#join_content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
padding: 0px 16px;
|
||||
.left {
|
||||
flex: 0 0 300px;
|
||||
}
|
||||
.right {
|
||||
flex: auto;
|
||||
.content {
|
||||
display: flex;
|
||||
& > div {
|
||||
flex: 1;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dissatisfy {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 40px;
|
||||
.dissatisfy_box {
|
||||
max-width: 300px;
|
||||
text-align: center;
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,109 @@
|
|||
<template>
|
||||
<div id="output" v-if="LinkedDataIf">
|
||||
<a-table
|
||||
:columns="columnList"
|
||||
:data="columnListData"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="dissatisfy">
|
||||
<div class="dissatisfy_box">
|
||||
<img src="@/assets/imgs/oneNode.f1997e1e.png" alt="" />
|
||||
<p>{{ LinkedDataText }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, toRefs } from "vue";
|
||||
import http from "@/scripts/request";
|
||||
const props = defineProps({
|
||||
nodeValue: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lineList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
conArr: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
//使用父组件传递过来的值
|
||||
const { nodeValue, lineList, conArr } = toRefs(props);
|
||||
const columnList = ref([]);
|
||||
const columnListData = ref([]);
|
||||
|
||||
const getDataStream = () => {
|
||||
http
|
||||
.post("/api/metadata/data_stream/exec", {
|
||||
lineList: lineList.value,
|
||||
nodeList: conArr.value,
|
||||
streamId: 1,
|
||||
rootNodeId: nodeValue.value.id,
|
||||
})
|
||||
.then((resp) => {
|
||||
columnList.value = [];
|
||||
resp.data.columnList.map((item) => {
|
||||
columnList.value.push({
|
||||
title: item.name,
|
||||
dataIndex: item.keyword,
|
||||
});
|
||||
});
|
||||
columnListData.value = resp.data.dataList;
|
||||
});
|
||||
};
|
||||
|
||||
const LinkedDataIf = ref(false);
|
||||
const LinkedDataText = ref("请将1个节点连接至本节点");
|
||||
|
||||
const verify = () => {
|
||||
console.log(lineList.value);
|
||||
let LinkedData = {};
|
||||
LinkedDataIf.value = false;
|
||||
lineList.value.map((item) => {
|
||||
console.log(item);
|
||||
if (item.toStr == nodeValue.value.id) {
|
||||
conArr.value.map((item2) => {
|
||||
if (item.fromStr == item2.id) {
|
||||
LinkedDataIf.value = true;
|
||||
LinkedData = JSON.parse(JSON.stringify(item2));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!LinkedDataIf.value) {
|
||||
LinkedDataText.value = "请将1个节点连接至本节点";
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(LinkedData.columnList);
|
||||
if (LinkedData.columnList) {
|
||||
LinkedData.columnList.map((item) => {
|
||||
item.nodeId = nodeValue.value.id;
|
||||
});
|
||||
conArr.value.map((item) => {
|
||||
if (item.id == nodeValue.value.id) {
|
||||
item.columnList = LinkedData.columnList;
|
||||
}
|
||||
});
|
||||
console.log("conArr", conArr.value);
|
||||
getDataStream();
|
||||
} else {
|
||||
LinkedDataText.value = "请设置前置数据";
|
||||
LinkedDataIf.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
verify();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#output {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<div class="disposition_header">
|
||||
<h2>{{ nodeValue.name }}</h2>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<div class="disposition_left">
|
||||
<div class="title">
|
||||
<h4>输入源</h4>
|
||||
<span @click="clickChange">更改输入源</span>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="item in columnList">
|
||||
<span>{{ item.title }}</span>
|
||||
<span
|
||||
class="type"
|
||||
:style="{
|
||||
backgroundColor: item.typeColor,
|
||||
}"
|
||||
>{{ item.typeStr }}</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="disposition_right">
|
||||
<a-table
|
||||
:columns="columnList"
|
||||
:data="columnListData"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { toRefs } from "vue";
|
||||
const emit = defineEmits(["alertTable"]);
|
||||
const props = defineProps({
|
||||
//子组件接收父组件传递过来的值
|
||||
columnList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
nodeValue: {
|
||||
type: Object,
|
||||
default: () => [],
|
||||
},
|
||||
columnListData: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
//使用父组件传递过来的值
|
||||
const { columnList, nodeValue, columnListData } = toRefs(props);
|
||||
|
||||
const clickChange = () => {
|
||||
console.log(nodeValue.value);
|
||||
emit("alertTable");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.disposition_left {
|
||||
flex: 0 0 300px;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
.title {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
span {
|
||||
color: #0db3a6;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 5px 0 5px;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
.type {
|
||||
padding: 3px 6px;
|
||||
color: #ffffff;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.disposition_right {
|
||||
flex: 1;
|
||||
}
|
||||
.disposition_header {
|
||||
padding: 8px 16px;
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dissatisfy {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.dissatisfy_box {
|
||||
max-width: 300px;
|
||||
text-align: center;
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,251 @@
|
|||
<template>
|
||||
<a-tabs
|
||||
default-active-key="1"
|
||||
@change="tabsChange"
|
||||
v-if="conArrData.length == 0"
|
||||
>
|
||||
<a-tab-pane key="1" title="节点配置">
|
||||
<div class="grouping-content">
|
||||
<div class="grouping-content-left">
|
||||
<div class="group-title"><span class="label">结构解析字段</span></div>
|
||||
<a-dropdown @select="handleSelect" position="bl">
|
||||
<a-button type="primary" size="small">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
<template #default>添加结构解析字段</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<a-doption
|
||||
v-for="item in columnList"
|
||||
:key="item.id"
|
||||
:value="item.keyword"
|
||||
>{{ item.name }}</a-doption
|
||||
>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<div class="grouping-list">
|
||||
<div class="grouping-list-item" v-for="item in leftGroupingList">
|
||||
<span class="name">{{ item.name }}</span>
|
||||
|
||||
<div class="del" @click="clickLeftDel(index)">
|
||||
<icon-delete />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" title="查看数据">
|
||||
<a-table
|
||||
:columns="dataColumnList"
|
||||
:data="columnListData"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div v-else class="dissatisfy">
|
||||
<div class="dissatisfy_box">
|
||||
<img src="@/assets/imgs/oneNode.f1997e1e.png" alt="" />
|
||||
<p>请将1个节点连接至本节点</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import http from "@/scripts/request";
|
||||
import { ref, reactive, nextTick, toRefs } from "vue";
|
||||
import { IconPlus, IconDelete } from "@arco-design/web-vue/es/icon";
|
||||
|
||||
const columnList = ref([]);
|
||||
|
||||
const leftGroupingList = ref([]); //left 数据列表
|
||||
let nodeValueData = ref({
|
||||
type: false,
|
||||
groupColumnNameList: [],
|
||||
collectColumnList: [],
|
||||
});
|
||||
const props = defineProps({
|
||||
//子组件接收父组件传递过来的值
|
||||
nodeValue: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
lineList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
conArr: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
//使用父组件传递过来的值
|
||||
const { nodeValue, lineList, conArr } = toRefs(props);
|
||||
|
||||
const lineListData = ref([]);
|
||||
const conArrData = ref([]);
|
||||
const disposeLineList = () => {
|
||||
console.log("nodeValue-------------", nodeValue.value);
|
||||
console.log("lineList-------------", lineList.value);
|
||||
lineListData.value = [];
|
||||
lineList.value.map((item) => {
|
||||
if (item.toStr == nodeValue.value.id) {
|
||||
lineListData.value.push(item);
|
||||
}
|
||||
});
|
||||
console.log(lineListData.value);
|
||||
lineListData.value.map((lineListDataItem) => {
|
||||
conArr.value.map((conArrItem) => {
|
||||
if (lineListDataItem.fromStr == conArrItem.id) {
|
||||
conArrData.value.push(conArrItem);
|
||||
|
||||
columnList.value = JSON.parse(JSON.stringify(conArrItem.columnList));
|
||||
|
||||
columnList.value.map((item) => {
|
||||
item.nodeId = nodeValue.value.id;
|
||||
});
|
||||
|
||||
nodeValue.value.columnList = JSON.parse(
|
||||
JSON.stringify(columnList.value)
|
||||
);
|
||||
|
||||
console.log(nodeValue.value.data);
|
||||
if (nodeValue.value.data) {
|
||||
nodeValueData.value = JSON.parse(nodeValue.value.data);
|
||||
leftGroupingList.value = [];
|
||||
nodeValueData.value.groupColumnNameList.map((item) => {
|
||||
columnList.value.map((groupColumnNameListItem) => {
|
||||
if (groupColumnNameListItem.keyword == item) {
|
||||
leftGroupingList.value.push(groupColumnNameListItem);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
console.log("不存在");
|
||||
nodeValueData.value = {
|
||||
groupColumnNameList: [],
|
||||
collectColumnList: [],
|
||||
isRetain: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log("lineListData", lineListData.value);
|
||||
console.log("conArrData", conArrData.value);
|
||||
};
|
||||
|
||||
disposeLineList();
|
||||
|
||||
const tabsChange = (key) => {
|
||||
console.log(key);
|
||||
switch (key) {
|
||||
case "2":
|
||||
if (nodeValue.value.columnList) {
|
||||
getDataStream();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// left 列表数据处理
|
||||
|
||||
const handleSelect = (keyword) => {
|
||||
columnList.value.map((item) => {
|
||||
if (item.keyword == keyword) {
|
||||
leftGroupingList.value.push(item);
|
||||
}
|
||||
});
|
||||
initData();
|
||||
};
|
||||
const clickLeftDel = (index) => {
|
||||
leftGroupingList.value.splice(index, 1);
|
||||
initData();
|
||||
};
|
||||
|
||||
// 保存数据
|
||||
const initData = () => {
|
||||
nodeValueData.value.groupColumnNameList = [];
|
||||
leftGroupingList.value.map((item) => {
|
||||
nodeValueData.value.groupColumnNameList.push(item.keyword);
|
||||
});
|
||||
|
||||
|
||||
nodeValue.value.data = JSON.stringify(nodeValueData.value);
|
||||
console.log(nodeValue.value);
|
||||
};
|
||||
|
||||
//查看数据
|
||||
const dataColumnList = ref([])
|
||||
const columnListData = ref([])
|
||||
const getDataStream = () => {
|
||||
http
|
||||
.post("/api/metadata/data_stream/exec", {
|
||||
lineList: lineList.value,
|
||||
nodeList: conArr.value,
|
||||
streamId: 1,
|
||||
rootNodeId: nodeValue.value.id,
|
||||
})
|
||||
.then((resp) => {
|
||||
dataColumnList.value = [];
|
||||
resp.data.columnList.map((item) => {
|
||||
dataColumnList.value.push({
|
||||
title: item.name,
|
||||
dataIndex: item.keyword,
|
||||
});
|
||||
});
|
||||
|
||||
columnListData.value = resp.data.dataList;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.grouping-content {
|
||||
display: flex;
|
||||
.grouping-content-left {
|
||||
flex: 0 0 260px;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.group-title {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
.label {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
.grouping-content-right {
|
||||
flex: auto;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.grouping-list {
|
||||
.grouping-list-item {
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.name {
|
||||
flex: 0 0 200px;
|
||||
}
|
||||
.del {
|
||||
flex: 0 0 20px;
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,205 @@
|
|||
<template>
|
||||
<div class="usedatabase">
|
||||
<div class="usedatabase_left">
|
||||
<a-tree :data="treeData" :load-more="loadMore" @select="select" />
|
||||
</div>
|
||||
<div class="usedatabase_right">
|
||||
<div v-if="fieldList.length != 0">
|
||||
<div>
|
||||
<a-checkbox
|
||||
:model-value="checkedAll"
|
||||
:indeterminate="indeterminate"
|
||||
@change="handleChangeAll"
|
||||
>全选
|
||||
</a-checkbox>
|
||||
</div>
|
||||
<a-checkbox-group
|
||||
v-model="data"
|
||||
@change="handleChange"
|
||||
direction="vertical"
|
||||
>
|
||||
<a-checkbox :value="item.id" v-for="item in fieldList">{{
|
||||
item.name
|
||||
}}</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
<div v-else class="empty">
|
||||
<a-empty />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
import http from "@/scripts/request";
|
||||
import { del } from "vue-demi";
|
||||
|
||||
const fieldList = ref([]);
|
||||
|
||||
const treeData = ref([]);
|
||||
const metadataId = ref(""); //选中的表单id
|
||||
const metadataName = ref(""); //选中的表单id
|
||||
|
||||
//查询库
|
||||
const getProject = () => {
|
||||
http.get("/api/metadata/project").then((resp) => {
|
||||
let arr = [];
|
||||
resp.data.records.map((item) => {
|
||||
arr.push({
|
||||
title: item.name,
|
||||
key: item.id,
|
||||
children: [],
|
||||
isLeaf: false,
|
||||
});
|
||||
});
|
||||
|
||||
treeData.value = arr;
|
||||
});
|
||||
};
|
||||
|
||||
//查询字段接口
|
||||
const getMetadata = (id) => {
|
||||
http
|
||||
.get("/api/metadata/metadataColumn", {
|
||||
metadataId: id,
|
||||
})
|
||||
.then((resp) => {
|
||||
fieldList.value = resp.data.records;
|
||||
|
||||
// 默认全选
|
||||
checkedAll.value = true;
|
||||
data.value = [];
|
||||
resp.data.records.map((item) => {
|
||||
data.value.push(item.id);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getProject();
|
||||
});
|
||||
|
||||
const select = (arr, nodeData) => {
|
||||
if (nodeData.node.isLeaf) {
|
||||
console.log(nodeData.node);
|
||||
metadataName.value = nodeData.node.title;
|
||||
metadataId.value = nodeData.node.key;
|
||||
getMetadata(nodeData.node.key);
|
||||
}
|
||||
};
|
||||
|
||||
const loadMore = (nodeData) => {
|
||||
console.log(nodeData);
|
||||
return new Promise((resolve) => {
|
||||
http
|
||||
.get("api/metadata/metadata", {
|
||||
projectId: nodeData.key,
|
||||
})
|
||||
.then((resp) => {
|
||||
let arr = [];
|
||||
resp.data.records.map((item) => {
|
||||
arr.push({
|
||||
title: item.name,
|
||||
key: item.id,
|
||||
isLeaf: true,
|
||||
});
|
||||
});
|
||||
nodeData.children = arr;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const indeterminate = ref(false);
|
||||
const checkedAll = ref(false);
|
||||
const data = ref([]);
|
||||
|
||||
const handleChangeAll = (value) => {
|
||||
indeterminate.value = false;
|
||||
if (value) {
|
||||
checkedAll.value = true;
|
||||
let valueData = [];
|
||||
fieldList.value.map((item) => {
|
||||
valueData.push(item.id);
|
||||
});
|
||||
data.value = valueData;
|
||||
} else {
|
||||
checkedAll.value = false;
|
||||
data.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (values) => {
|
||||
if (values.length === fieldList.value.length) {
|
||||
checkedAll.value = true;
|
||||
indeterminate.value = false;
|
||||
} else if (values.length === 0) {
|
||||
checkedAll.value = false;
|
||||
indeterminate.value = false;
|
||||
} else {
|
||||
checkedAll.value = false;
|
||||
indeterminate.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const getColumnList = (clickEfNodeId) => {
|
||||
let filterData = [];
|
||||
fieldList.value.map((item) => {
|
||||
if (data.value.includes(item.id)) {
|
||||
delete item.createdAt;
|
||||
delete item.updatedAt;
|
||||
filterData.push({
|
||||
keyword: `${item.ename}_${clickEfNodeId}`,
|
||||
nodeId: clickEfNodeId,
|
||||
name: item.name,
|
||||
ename: item.ename,
|
||||
type: item.type,
|
||||
sortNo: item.sortNo,
|
||||
type: item.type,
|
||||
typeStr: item.typeStr,
|
||||
typeColor:item.typeColor
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
name: metadataName.value,
|
||||
metadataId: metadataId.value,
|
||||
filterData: filterData,
|
||||
};
|
||||
};
|
||||
|
||||
const assignInit = (nodeValue) => {
|
||||
let metadataId = JSON.parse(nodeValue.data).metadataId;
|
||||
console.log(metadataId);
|
||||
getMetadata(metadataId);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
getColumnList,
|
||||
assignInit,
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.usedatabase {
|
||||
width: 800px;
|
||||
height: 400px;
|
||||
display: flex;
|
||||
.usedatabase_left {
|
||||
flex: 0 0 200px;
|
||||
padding-right: 20px;
|
||||
margin-right: 20px;
|
||||
border-right: 1px solid rgba(229, 230, 235, 1);
|
||||
}
|
||||
.usedatabase_right {
|
||||
flex: 1;
|
||||
.empty {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,8 @@
|
|||
<template>
|
||||
<v-form-designer></v-form-designer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<el-dialog :title="title"
|
||||
v-model="visible"
|
||||
:width="width"
|
||||
destroy-on-close
|
||||
@closed="$emit('closed')">
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
:action="api"
|
||||
:on-error="handlerError"
|
||||
:before-upload="handlerBefore"
|
||||
:on-success="handlerSuccess"
|
||||
:show-file-list="false"
|
||||
:headers="uploadHeader"
|
||||
>
|
||||
<el-button size="small" type="primary" >选择文件上传</el-button>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">仅允许导入"xls"或者"xlsx"格式文件, 点击<a href="javascript:;" @click="download" style="color: red;">【这里】</a>下载模板</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import http from "@/utils/request";
|
||||
import { ElLoading } from 'element-plus'
|
||||
import tools from "@/utils/tools";
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
|
||||
data () {
|
||||
return {
|
||||
title: '',
|
||||
width: 400,
|
||||
visible: false,
|
||||
grape: '',
|
||||
api: '',
|
||||
loading: false,
|
||||
loadingInstance: {},
|
||||
uploadHeader:{
|
||||
'x-token':'1212'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
handlerBefore(){
|
||||
this.loadingInstance = ElLoading.service({
|
||||
text: '导入中...'
|
||||
})
|
||||
},
|
||||
handlerSuccess(resp) {
|
||||
this.loadingInstance.close();
|
||||
if(resp.code == 200) {
|
||||
this.$alert(resp.message, '导入结果', {
|
||||
confirmButtonText: '确定'
|
||||
});
|
||||
}
|
||||
},
|
||||
handlerError() {
|
||||
this.loadingInstance.close();
|
||||
},
|
||||
download() {
|
||||
http.download(`/api/data/excel/${this.grape}/template`);
|
||||
},
|
||||
open (title, width = 400, grape) {
|
||||
|
||||
let user = tools.data.get("user");
|
||||
this.uploadHeader["x-token"] = `Bearer ${user.token}`
|
||||
console.log(user)
|
||||
this.title = title;
|
||||
this.width = width;
|
||||
this.visible = true;
|
||||
this.grape = grape;
|
||||
this.api = `/api/data/excel/${this.grape}/import`;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<el-tree :data="data.treeData"
|
||||
:selectable="false"
|
||||
show-checkbox
|
||||
ref="treeRef"
|
||||
node-key="key"
|
||||
:check-strictly="checkStrictly"
|
||||
:default-checked-keys="data.checkedKeys"
|
||||
empty-text="暂无数据"
|
||||
v-loading="data.loading"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive} from 'vue';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import http from "@/utils/request.js";
|
||||
|
||||
const emits = defineEmits(['hideShow'])
|
||||
const data = reactive({
|
||||
treeData:[],
|
||||
loading: true,
|
||||
checkedKeys:[],
|
||||
params:{}
|
||||
})
|
||||
const checkStrictly = ref(false)
|
||||
const initTreeData = (url, params)=>{
|
||||
console.log(params)
|
||||
data.params = params;
|
||||
|
||||
http.get(url,params).then(resp => {
|
||||
if(resp.code == 200){
|
||||
data.loading = false;
|
||||
data.treeData = resp.data
|
||||
console.log(resp.checked)
|
||||
let checked = JSON.parse(JSON.stringify(resp.checked))
|
||||
diguiTree(resp.data,checked)
|
||||
console.log(checked)
|
||||
data.checkedKeys = checked
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const treeRef = ref();
|
||||
|
||||
const postSubmit = (dialog)=>{
|
||||
|
||||
let checkedNodes = []
|
||||
treeRef.value.getCheckedNodes().map(item=>{
|
||||
checkedNodes.push(item.key)
|
||||
})
|
||||
|
||||
// 补全半选状态
|
||||
treeRef.value.getHalfCheckedKeys().map(item=>{
|
||||
checkedNodes.push(item)
|
||||
})
|
||||
dialog.loading = true;
|
||||
let getCheckedKeys = checkedNodes.join(',')
|
||||
|
||||
data.params.ids = getCheckedKeys;
|
||||
http.post(dialog.action.postUrl, data.params).then(resp=>{
|
||||
if(resp.code == 200){
|
||||
ElMessage.success("提交成功")
|
||||
dialog.visibled = false;
|
||||
dialog.loading = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const diguiTree = (data,checked)=>{
|
||||
for (let i = 0; i < data.length ; i++) {
|
||||
const item = data[i]
|
||||
if (item.children && item.children.length > 0) {
|
||||
if(checked.indexOf(item.value)!= -1){
|
||||
checked.splice(checked.indexOf(item.value),1)
|
||||
}
|
||||
diguiTree(item.children,checked)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initTreeData,
|
||||
postSubmit
|
||||
})
|
||||
|
||||
</script>
|
|
@ -0,0 +1,240 @@
|
|||
<template>
|
||||
<div class="form-box" v-loading="loading">
|
||||
<el-form
|
||||
:model="formData"
|
||||
:rules="data.ruleData"
|
||||
:labelWidth="grape.labelWidth"
|
||||
label-position="right"
|
||||
ref="formRef"
|
||||
>
|
||||
<el-row :gutter="10" v-if="data.formList">
|
||||
<template v-for="formColumn in data.formList" :key="formColumn.name">
|
||||
<el-col
|
||||
:span="formColumn.span"
|
||||
v-if="formColumn.showOn ? evalExpr(formColumn.showOn) : true"
|
||||
>
|
||||
<el-form-item :label="formColumn.label" :prop="formColumn.name">
|
||||
<Input :data="formColumn" :model="formData" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
<el-input type="hidden" v-model="formData[grape.primaryKey]" />
|
||||
</el-form>
|
||||
<el-row
|
||||
type="flex"
|
||||
justify="end"
|
||||
v-if="!dialog.isDialog"
|
||||
style="margin-top: 10px"
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="router.go(-1)"
|
||||
v-if="dialog.mode == 'edit'"
|
||||
plain
|
||||
>
|
||||
返回</el-button
|
||||
>
|
||||
<el-button type="primary" @click="submitFormData()" v-loading="saving">
|
||||
提交</el-button
|
||||
>
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, toRefs, onMounted, reactive } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import emitter from "@/plugins/Bus.js";
|
||||
import Input from "@/components/Input";
|
||||
import tabTable from "./tabTable.vue";
|
||||
import http from "@/utils/request";
|
||||
|
||||
const props = defineProps({
|
||||
grape: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
dialog: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const { grape, dialog } = toRefs(props);
|
||||
|
||||
|
||||
const data = reactive({
|
||||
formList: null,
|
||||
ruleData: {},
|
||||
tabTable: null,
|
||||
saving: false,
|
||||
});
|
||||
const formData = ref({ id: "" });
|
||||
const loading = ref(true);
|
||||
let url = `/api/build/${grape.value.name}/${dialog.value.mode}`;
|
||||
|
||||
const getGrapeFormData = () => {
|
||||
http.get(url, { id: dialog.value.id }).then((resp) => {
|
||||
if (resp.code == 200) {
|
||||
formData.value = resp.data.data;
|
||||
data.formList = resp.data.formList;
|
||||
data.ruleData = resp.data.ruleList;
|
||||
data.tabTable = resp.data.tabTable;
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getData = () => {
|
||||
return formData.value;
|
||||
};
|
||||
|
||||
const formRef = ref();
|
||||
const tabTableRef = ref();
|
||||
const tableData = ref([]);
|
||||
|
||||
const submitFormData = (tableParams) => {
|
||||
formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (grape.value.name == "RkOrder") {
|
||||
tableData.value = tabTableRef.value.getTabTableData();
|
||||
tableData.value.map(item=>{
|
||||
item.num = item.price * item.amount
|
||||
item.wzSpecId_mapping.map(item1=>{
|
||||
if(item.wzSpecId == item1.value){
|
||||
item.wzSpecTitle = item1.label
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log(tableData.value);
|
||||
console.log(data.tabTable,'数据');
|
||||
} else {
|
||||
data.saving = true;
|
||||
let url = `/api/modify/${grape.value.name}`;
|
||||
let formParams = {
|
||||
data: formData.value,
|
||||
};
|
||||
|
||||
if (dialog.value.mode == "add") {
|
||||
let params = {};
|
||||
for (let key in grape.value.params) {
|
||||
params[grape.value.params[key]] = tableParams[key];
|
||||
}
|
||||
formParams.data = { ...formParams.data, ...params };
|
||||
}
|
||||
|
||||
if (data.tabTable) {
|
||||
formParams["tabData"] = tabTableRef.value.getTabTableData();
|
||||
}
|
||||
|
||||
http[dialog.value.mode == "add" ? "post" : "put"](url, formParams).then(
|
||||
(resp) => {
|
||||
if (resp.code == 200) {
|
||||
ElMessage.success("操作成功");
|
||||
if (!dialog.value.isDialog && grape.value.formSubmitUrl) {
|
||||
router.push({
|
||||
path: grape.value.formSubmitUrl,
|
||||
query: { id: resp.data.id },
|
||||
});
|
||||
} else {
|
||||
emitter.emit(grape.value.name, {});
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(resp.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const secondary = () => {
|
||||
let url = `/api/modify/${grape.value.name}`;
|
||||
let formParams = {
|
||||
data: formData.value,
|
||||
};
|
||||
|
||||
if (dialog.value.mode == "add") {
|
||||
let params = {};
|
||||
for (let key in grape.value.params) {
|
||||
params[grape.value.params[key]] = tableParams[key];
|
||||
}
|
||||
formParams.data = { ...formParams.data, ...params };
|
||||
}
|
||||
|
||||
if (data.tabTable) {
|
||||
formParams["tabData"] = tabTableRef.value.getTabTableData();
|
||||
}
|
||||
|
||||
http[dialog.value.mode == "add" ? "post" : "put"](url, formParams).then(
|
||||
(resp) => {
|
||||
if (resp.code == 200) {
|
||||
ElMessage.success("操作成功");
|
||||
if (!dialog.value.isDialog && grape.value.formSubmitUrl) {
|
||||
router.push({
|
||||
path: grape.value.formSubmitUrl,
|
||||
query: { id: resp.data.id },
|
||||
});
|
||||
} else {
|
||||
emitter.emit(grape.value.name, {});
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(resp.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const evalExpr = (expr) => {
|
||||
return eval(expr);
|
||||
// return true;
|
||||
};
|
||||
|
||||
const getSummaries = (param) => {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = "合计";
|
||||
return;
|
||||
}
|
||||
const values = data.map((item) => Number(item[column.property]));
|
||||
if (index === 2 || index === 3 || index === 4) {
|
||||
sums[index] = ` ${values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!Number.isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 0)}`;
|
||||
}else {
|
||||
sums[index] = "";
|
||||
}
|
||||
});
|
||||
|
||||
sums[4] = sums[4] + "元";
|
||||
sums[2] = sums[2] + '元'
|
||||
return sums;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getGrapeFormData();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
getData,
|
||||
submitFormData,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.dialog{
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,200 @@
|
|||
<template>
|
||||
<el-card class="box-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ tabTable.label }}明细</span>
|
||||
<el-button
|
||||
class="button"
|
||||
text
|
||||
v-if="tabTable.selectGrape"
|
||||
@click="lookup"
|
||||
>选择{{ tabTable.label }}</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
:data="data.rows"
|
||||
border
|
||||
:emptyText="data.emptyText"
|
||||
:show-summary="data.showSummary"
|
||||
:summary-method="getSummaryMethod"
|
||||
>
|
||||
<template v-for="column in tabTable.formList" :key="column.name">
|
||||
<el-table-column
|
||||
:label="column.label"
|
||||
:prop="column.name"
|
||||
:width="column.width"
|
||||
>
|
||||
<template #default="scope">
|
||||
<Input :data="column" :model="data.rows[scope.$index]" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<el-table-column label="操作" width="80">
|
||||
<template #default="scope">
|
||||
<el-popconfirm
|
||||
title="确定删除吗?"
|
||||
confirm-button-text="确定"
|
||||
cancel-button-text="取消"
|
||||
@confirm="delTabTableData(scope.row, scope.$index)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button type="text" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<el-dialog
|
||||
v-model="data.dialog.visibled"
|
||||
destroy-on-close
|
||||
v-if="tabTable.selectGrape"
|
||||
:title="data.dialog.title"
|
||||
:width="data.dialog.width"
|
||||
>
|
||||
<tableData
|
||||
:grapeData="tabTable.selectGrape"
|
||||
:params="{ parentGrape: tabTable.grape.name }"
|
||||
:checkbox="true"
|
||||
v-if="tabTable.selectGrape"
|
||||
ref="tableDataRef"
|
||||
></tableData>
|
||||
<template #footer>
|
||||
<el-button @click="handleCloseDialog">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSelectedData" :loading="loading"
|
||||
>确 定</el-button
|
||||
>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, toRefs, onMounted, reactive } from "vue";
|
||||
import Input from "@/components/Input";
|
||||
import tableData from "./tableData.vue";
|
||||
import http from "@/utils/request";
|
||||
|
||||
const props = defineProps({
|
||||
tabTable: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
|
||||
const { tabTable } = toRefs(props);
|
||||
|
||||
const data = reactive({
|
||||
rows: tabTable.value.data || [],
|
||||
emptyText: `请选择${tabTable.value.label}`,
|
||||
showSummary: tabTable.value.showSummary,
|
||||
dialog: {
|
||||
visibled: false,
|
||||
title: "选择物资",
|
||||
width: "800px",
|
||||
loading: false,
|
||||
},
|
||||
});
|
||||
|
||||
const lookup = () => {
|
||||
data.dialog.visibled = true;
|
||||
};
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
data.dialog.visibled = false;
|
||||
};
|
||||
|
||||
const tableDataRef = ref();
|
||||
|
||||
const handleSelectedData = () => {
|
||||
data.dialog.loading = true;
|
||||
var selectedData = tableDataRef.value.getSelectedData();
|
||||
if (tabTable.value.selectGrape.url) {
|
||||
var ids = selectedData.map((item) => {
|
||||
return item[tabTable.value.selectGrape.grape.primaryKey];
|
||||
});
|
||||
|
||||
http
|
||||
.get(tabTable.value.selectGrape.url, { ids: ids.join(",") })
|
||||
.then((resp) => {
|
||||
if (resp.code == 200 && resp.data) {
|
||||
data.rows = [...data.rows, ...resp.data];
|
||||
}
|
||||
data.dialog.loading = false;
|
||||
data.dialog.visibled = false;
|
||||
});
|
||||
} else {
|
||||
data.rows.concat(selectedData);
|
||||
data.dialog.loading = false;
|
||||
data.dialog.visibled = false;
|
||||
}
|
||||
};
|
||||
|
||||
const delTabTableData = (row, index) => {
|
||||
data.rows.splice(index, 1);
|
||||
};
|
||||
|
||||
const getTabTableData = () => {
|
||||
return data.rows;
|
||||
};
|
||||
|
||||
const getSummaryMethod = (param) => {
|
||||
const sums = [];
|
||||
// tabTable.sums = [6,7];
|
||||
if (tabTable.value.sums) {
|
||||
const { columns, data } = param;
|
||||
columns.forEach((column, index) => {
|
||||
if (index == 0) {
|
||||
sums[0] = tabTable.value.summaryText;
|
||||
} else if (tabTable.value.sums.indexOf(index) > -1) {
|
||||
const values = data.map((item) => Number(item[column.property]));
|
||||
if (values.length > 0) {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
sums[index] = "";
|
||||
}
|
||||
} else if (index == 8) {
|
||||
let n = 0
|
||||
data.map(item=>{
|
||||
let s = []
|
||||
tabTable.value.sums.map(item2=>{
|
||||
s.push(item[columns[item2].property])
|
||||
})
|
||||
|
||||
let m = 1
|
||||
s.map(item=>{
|
||||
m = m*item
|
||||
})
|
||||
n += m
|
||||
})
|
||||
sums[8] = n+ "元";
|
||||
} else {
|
||||
sums[index] = "";
|
||||
}
|
||||
});
|
||||
|
||||
sums[6] = sums[6] + "元";
|
||||
}
|
||||
|
||||
return sums;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
getTabTableData,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,545 @@
|
|||
<template>
|
||||
<div class="table-box" v-loading="data.initLoading">
|
||||
<el-row>
|
||||
<el-col :span="grape.treeLink.span" v-if="grapeData.grape.treeLink">
|
||||
<tree-link :grape="grape"></tree-link>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="grape.treeLink ? 24 - grapeData.grape.treeLink.span : 24">
|
||||
<el-form class="searchContent" @keyup.enter.native="refreshTableData">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6" v-for="formColumn in grapeData.filterList" :key="formColumn.name">
|
||||
<Input :data="initFormColumn(formColumn)" :model="data.search" />
|
||||
</el-col>
|
||||
<el-col :span="6" class="flex-box col_box" :offset="offset(grapeData.filterList.length,4)">
|
||||
<el-button @click="refreshTableData" v-if="grapeData.filterList.length >0" type="primary" plain
|
||||
icon="Search">搜索</el-button>
|
||||
<el-button @click="addFormData" v-if="power.add" type="primary" icon="Plus">新建</el-button>
|
||||
<el-button type="primary" @click="clickDialogImport" v-if="power.imp">导入</el-button>
|
||||
<el-button type="primary" @click="handleExportData" v-if="power.exp">导出</el-button>
|
||||
<el-button @click.stop="handleAction(toolbar, scope)" type="primary"
|
||||
v-for="toolbar in grapeData.toolbarList" :key="toolbar.name" v-if="grapeData.toolbarList.length> 0">
|
||||
{{toolbar.label}}</el-button>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-table :data="data.rows" v-loading="data.tableLoading" stripe header-cell-class-name="table-header-gray"
|
||||
ref="tableRef" empty-text="暂无数据" @selection-change="handleSelectionChange" row-key="id"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}">
|
||||
<el-table-column :reserve-selection="true" type="selection" v-if="checkbox" />
|
||||
<template v-for="column in grapeData.columnList" :key="column.name">
|
||||
<el-table-column v-if="column.type == 'bool' || column.type == 'image' || column.type == 'images'"
|
||||
:label="column.label" :prop="column.name" :sortable="column.sortable ? 'custom' : false"
|
||||
:width="column.width">
|
||||
<template #default="scopeBool" v-if="column.type == 'bool'">
|
||||
<el-tag size="mini" :type="scopeBool.row[column.name] ? 'success' : 'danger'">
|
||||
{{scopeBool.row[column.name] ? column.dict[true] : column.dict[false]}}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #default="scopeImage" v-else-if="column.type == 'image'">
|
||||
<el-image :src="scopeImage.row[column.name].preview" :fit="fit" style="width: 60px; height: 60px"
|
||||
v-if="scopeImage.row[column.name] && scopeImage.row[column.name].preview"></el-image>
|
||||
</template>
|
||||
<template #default="scopeImages" v-else-if="column.type == 'images'">
|
||||
<el-image v-for="(item,key) in scopeImages.row[column.name].slice(0,2)" :key="key" :src="item.preview"
|
||||
style="width: 60px; height: 60px;margin-right:10px"></el-image>
|
||||
<div class="images_more" v-if="scopeImages.row[column.name].length > 2"><i class="el-icon-more"></i>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-else :label="column.label" :prop="column.name" :width="column.width">
|
||||
<template #default="scope">
|
||||
<div style="display: inline-block;">
|
||||
<div style="display: flex; align-items: center">
|
||||
{{scope.row[column.name]}}
|
||||
<el-tooltip placement="top" effect="light" v-if="scope.row[`${column.name}__tips`]">
|
||||
<template #content>
|
||||
<div v-html="scope.row[`${column.name}__tips`]"></div>
|
||||
</template>
|
||||
<el-icon style="margin-left: 4px;">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<el-table-column label="操作" :width="grape.actionWidth" v-if="actionShow()">
|
||||
<template #default="scope">
|
||||
<span v-if="power.edit" style="margin-right:12px ;">
|
||||
<el-button type="text" size="small" @click="editFormData(scope.row)"
|
||||
v-if="filterShowOn(power.editShowOn,scope.row)">编辑</el-button>
|
||||
</span>
|
||||
<template v-for="action in grapeData.actionList" :key="action.label">
|
||||
<span v-if="action.confirm">
|
||||
<el-popconfirm v-if="filterHandleAction(action, scope.row)" :title="action.confirm"
|
||||
confirm-button-text="确定" cancel-button-text="取消" @confirm="handleAction(action, scope)">
|
||||
<template #reference>
|
||||
<el-button style="margin:0 12px 0 0 ;" type="text" size="small">{{action.label}}</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</span>
|
||||
<el-button style="margin:0 12px 0 0 ;" v-else-if="filterHandleAction(action, scope.row)" type="text"
|
||||
size="small" @click.stop="handleAction(action, scope)">{{action.label}}</el-button>
|
||||
</template>
|
||||
<el-popconfirm title="确定删除吗?" confirm-button-text="确定" cancel-button-text="取消"
|
||||
@confirm="delGrapeData(scope.row, scope.$index)"
|
||||
v-if="power.delete && filterShowOn(power.deleteShowOn,scope.row)">
|
||||
<template #reference>
|
||||
<el-button style="margin:0 " type="text" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination-box" v-if="grape.isPage">
|
||||
<el-pagination small layout="prev, pager, next" @current-change="currentChange"
|
||||
:current-page="data.search.page" :page-size="data.search.pageSize" :total="data.tableTotal" />
|
||||
</div>
|
||||
<el-dialog v-model="data.dialog.visibled" :title="data.dialog.title" :width="data.dialog.width" destroy-on-close
|
||||
v-if="power.add || power.edit">
|
||||
<formData :grape="grape" :dialog="data.dialog" ref="formDataRef"></formData>
|
||||
<template #footer>
|
||||
<el-button @click="handleCloseDialog">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmitFormData">确 定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 动态组件 -->
|
||||
<el-dialog v-model="data.actionDialog.visibled" :title="data.actionDialog.title"
|
||||
:width="data.actionDialog.width" destroy-on-close>
|
||||
<component :is="data.actionDialog.type" ref="actionDialogRef"></component>
|
||||
<template #footer>
|
||||
<el-button @click="handleCloseActionDialog">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmitActionDialog" v-loading="data.actionDialog.loading">确认
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- end 动态组件 -->
|
||||
|
||||
<div class="dialogTable" v-if="dialogTableData.show">
|
||||
<img class="close" src="@/assets/images/close.png" alt="" @click="dialogTableData.show = false">
|
||||
<tableData :grapeData="dialogTableData.grapeData" :params="dialogTableData.params"></tableData>
|
||||
</div>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<GrapeDialogImport v-if="dialogImport" ref="grapeDialogImport" :grapeWidget="grapeWidget" @closed="dialog=false"/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Input from '@/components/Input';
|
||||
import { ref, toRefs, onMounted, reactive, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import emitter from '@/plugins/Bus';
|
||||
import formData from './formData.vue';
|
||||
import treeLink from './treeLink.vue';
|
||||
import dialogTree from './dialogTree.vue';
|
||||
import tableData from './tableData.vue'
|
||||
import http from '@/utils/request';
|
||||
import { useRouter } from 'vue-router'
|
||||
import GrapeDialogImport from './dialogImport.vue';
|
||||
|
||||
import tools from "@/utils/tools";
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
grapeData: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
checkbox: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
params: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
});
|
||||
|
||||
const { grapeData, parentGrape, checkbox, params } = toRefs(props);
|
||||
const grape = grapeData.value.grape;
|
||||
const power = grapeData.value.power;
|
||||
const formDataRef = ref();
|
||||
|
||||
console.log(grape.name)
|
||||
const data = reactive({
|
||||
rows: [],
|
||||
grape: grapeData.value.grape,
|
||||
initLoading: true,
|
||||
tableLoading: false,
|
||||
dialog: {
|
||||
title: `新建 ${grape.title}`,
|
||||
visibled: false,
|
||||
mode: 'add',
|
||||
width: grape.formWidth,
|
||||
type: 'dialog-tree',
|
||||
isDialog: true,
|
||||
id: ''
|
||||
},
|
||||
actionDialog: {
|
||||
title: '',
|
||||
visibled: false,
|
||||
width: 600,
|
||||
action: {},
|
||||
loading: false
|
||||
},
|
||||
tableTotal: 0,
|
||||
search: {
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
selectedData: [],
|
||||
})
|
||||
|
||||
const getGrapeTableData = () => {
|
||||
if (!data.initLoading) data.tableLoading = true;
|
||||
http.get(`/api/data/table/${grape.name}`, { ...params.value, ...data.search }).then(resp => {
|
||||
data.tableLoading = false
|
||||
if (resp.code == 200) {
|
||||
data.rows = resp.data;
|
||||
data.tableTotal = resp.count
|
||||
}
|
||||
data.initLoading = false;
|
||||
})
|
||||
}
|
||||
|
||||
//分页
|
||||
const currentChange = (page) => {
|
||||
data.search.page = page
|
||||
getGrapeTableData()
|
||||
}
|
||||
|
||||
const addFormData = () => {
|
||||
data.dialog = {
|
||||
title: `新建 ${grape.title}`,
|
||||
mode: 'add',
|
||||
width: grape.formWidth,
|
||||
id: '',
|
||||
visibled: true,
|
||||
isDialog: true
|
||||
}
|
||||
}
|
||||
|
||||
const refreshTableData = () => {
|
||||
getGrapeTableData();
|
||||
}
|
||||
|
||||
const editFormData = (row) => {
|
||||
data.dialog = {
|
||||
title: `编辑 ${grape.title}`,
|
||||
mode: 'edit',
|
||||
width: grape.formWidth,
|
||||
id: row[grape.primaryKey],
|
||||
visibled: true,
|
||||
isDialog: true
|
||||
}
|
||||
}
|
||||
|
||||
const delGrapeData = (row, index) => {
|
||||
http.delete(`/api/modify/${grape.name}/${row[grape.primaryKey]}`).then(resp => {
|
||||
if (resp.code == 200) {
|
||||
data.rows.splice(index, 1);
|
||||
ElMessage.success("删除成功")
|
||||
} else {
|
||||
ElMessage.error(resp.message);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
data.dialog.visibled = false;
|
||||
}
|
||||
|
||||
const handleSubmitFormData = () => {
|
||||
formDataRef.value.submitFormData(params.value);
|
||||
}
|
||||
|
||||
const offset = (i, n) => {
|
||||
let m = i < n ? i : i % n
|
||||
return (n - m - 1) * 6
|
||||
}
|
||||
|
||||
const tableRef = ref();
|
||||
|
||||
const handleSelectionChange = (val) => {
|
||||
data.selectedData = val;
|
||||
}
|
||||
|
||||
const getSelectedData = () => {
|
||||
return data.selectedData;
|
||||
}
|
||||
|
||||
const initFormColumn = (formColumn) => {
|
||||
switch (formColumn.type) {
|
||||
case 'radio-group':
|
||||
formColumn.type = 'select'
|
||||
break;
|
||||
case 'bool':
|
||||
formColumn.type = 'select'
|
||||
formColumn.options = []
|
||||
for (const key in formColumn.dict) {
|
||||
if (Object.hasOwnProperty.call(formColumn.dict, key)) {
|
||||
const element = formColumn.dict[key];
|
||||
formColumn.options.push({
|
||||
label: element,
|
||||
value: key,
|
||||
key: key
|
||||
})
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return formColumn
|
||||
}
|
||||
|
||||
//操作actionList
|
||||
|
||||
const dialogTableData = reactive({
|
||||
show: false,
|
||||
grapeData: {},
|
||||
params: {}
|
||||
})
|
||||
|
||||
const openDialogTable = (grapeName, params) => {
|
||||
http.get(`/api/build/${grapeName}`).then(res => {
|
||||
dialogTableData.params = params
|
||||
dialogTableData.grapeData = res.data
|
||||
dialogTableData.show = true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const actionDialogRef = ref();
|
||||
|
||||
const handleAction = (action, scope) => {
|
||||
let params = {};
|
||||
for (let key in action.params) {
|
||||
params[key] = scope.row[action.params[key]];
|
||||
}
|
||||
switch (action.type) {
|
||||
case 'ajax':
|
||||
if (action.getUrl != '') {
|
||||
http.get(action.getUrl, params).then(res => {
|
||||
if (res.code == 200) {
|
||||
if (res.message && res.message != '') {
|
||||
ElMessage.success(res.message)
|
||||
}
|
||||
if (action.reload) {
|
||||
getGrapeTableData()
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
} else if (action.postUrl != '') {
|
||||
http.post(action.postUrl, params).then(res => {
|
||||
if (res.code == 200) {
|
||||
if (res.message && res.message != '') {
|
||||
ElMessage.success(res.message)
|
||||
}
|
||||
if (action.reload) {
|
||||
getGrapeTableData()
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
}
|
||||
break;
|
||||
case 'dialog-tree':
|
||||
data.actionDialog = {
|
||||
title: action.label,
|
||||
width: action.width,
|
||||
type: dialogTree,
|
||||
visibled: true,
|
||||
action: action
|
||||
}
|
||||
nextTick(() => {
|
||||
actionDialogRef.value.initTreeData(action.getUrl, params)
|
||||
})
|
||||
break;
|
||||
|
||||
case 'dialog-table':
|
||||
openDialogTable(action.grapeName, params)
|
||||
break;
|
||||
case 'jump':
|
||||
router.push({
|
||||
path: action.getUrl,
|
||||
query: params
|
||||
})
|
||||
break;
|
||||
case 'open':
|
||||
var url = action.getUrl;
|
||||
http.download(url);
|
||||
break;
|
||||
default:
|
||||
console.log('其他', action.type)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmitActionDialog = () => {
|
||||
actionDialogRef.value.postSubmit(data.actionDialog)
|
||||
}
|
||||
|
||||
const handleCloseActionDialog = () => {
|
||||
data.actionDialog = {
|
||||
visibled: false
|
||||
}
|
||||
}
|
||||
|
||||
emitter.on(grape.name, e => {
|
||||
data.search = { ...data.search, ...e };
|
||||
data.dialog.visibled = false
|
||||
getGrapeTableData()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getGrapeTableData();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
getSelectedData
|
||||
});
|
||||
|
||||
const filterHandleAction = (action, row) => {
|
||||
if (action.power) {
|
||||
var actionPower = power[action.power];
|
||||
if (actionPower != undefined && !actionPower) return false;
|
||||
}
|
||||
|
||||
if (action.self) {
|
||||
if (row.self != undefined && !row.self) return false;
|
||||
}
|
||||
|
||||
if (action.showOn) {
|
||||
return filterShowOn(action.showOn, row);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const handleExportData = () => {
|
||||
var url = `/api/data/excel/${grape.name}/export`;
|
||||
url = tools.url(url, data.search);
|
||||
http.download(url);
|
||||
}
|
||||
|
||||
|
||||
//判断操作
|
||||
|
||||
const actionShow = () => {
|
||||
console.log('power', power)
|
||||
let actionShowIf = false
|
||||
if (power.delete || power.edit) {
|
||||
return true
|
||||
}
|
||||
if (grapeData.value.actionList.length > 0) {
|
||||
grapeData.value.actionList.map(item => {
|
||||
if (item.power) {
|
||||
if (power[item.power] == undefined || power[item.power]) {
|
||||
actionShowIf = true
|
||||
}
|
||||
} else {
|
||||
actionShowIf = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return actionShowIf;
|
||||
}
|
||||
|
||||
const filterShowOn = (expr, row) => {
|
||||
|
||||
if (!row.id) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!expr) return true;
|
||||
return eval(expr);
|
||||
// return true;
|
||||
};
|
||||
|
||||
//导入
|
||||
const dialogImport = ref(false)
|
||||
const grapeDialogImport = ref(null)
|
||||
const clickDialogImport = () => {
|
||||
dialogImport.value = true;
|
||||
console.log(data.grape.name)
|
||||
nextTick(() => {
|
||||
grapeDialogImport.value.open("导入数据", 400, grape.name);
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.flex-box {
|
||||
display: flex !important;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.col_box {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.ydool_input {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.dialogTable {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ffffff;
|
||||
z-index: 99;
|
||||
box-sizing: content-box;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: -25px;
|
||||
top: -20px;
|
||||
width: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog {
|
||||
margin: 0 !important;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.table-box .el-button--small {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
.content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<el-card class="box-card" shadow="never" style="margin-right: 10px;">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{grape.treeLink.nav}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-tree
|
||||
:data="treeData"
|
||||
@nodeClick="handleChange"
|
||||
empty-text="暂无数据"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import {onMounted, toRefs, ref} from 'vue'
|
||||
import emitter from '@/plugins/Bus.js'
|
||||
import http from '@/utils/request';
|
||||
|
||||
const props = defineProps({
|
||||
grape:{
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
})
|
||||
|
||||
const {grape}= toRefs(props)
|
||||
|
||||
const treeData = ref([]);
|
||||
|
||||
const getTreeData = ()=>{
|
||||
|
||||
http.get(`/api/data/tree/${grape.value.treeLink.grapeClass}`).then(resp => {
|
||||
if(resp.code == 200){
|
||||
treeData.value = resp.data;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleChange = (newValue)=> {
|
||||
var name = grape.value.treeLink.pid;
|
||||
var params = {};
|
||||
params[name] = newValue.value;
|
||||
emitter.emit(grape.value.name, params);
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
getTreeData()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,318 @@
|
|||
<template>
|
||||
<div id="treeTableData">
|
||||
<div class="treeTableData_left">
|
||||
<el-button
|
||||
class="search_but"
|
||||
type="primary"
|
||||
icon="Plus"
|
||||
@click="clickAdd"
|
||||
style="width: 100%;
|
||||
margin-bottom: 10px;"
|
||||
v-if="power.add"
|
||||
>新增</el-button
|
||||
>
|
||||
<el-input v-model="filterText" placeholder="搜索" />
|
||||
|
||||
<el-scrollbar style="height: calc(100% - 72px); width: 100%">
|
||||
<el-tree
|
||||
v-loading="data.initLoading"
|
||||
ref="treeRef"
|
||||
class="filter-tree"
|
||||
:data="TreeData"
|
||||
:props="defaultProps"
|
||||
|
||||
:filter-node-method="filterNode"
|
||||
:expand-on-click-node="false"
|
||||
@node-click="nodeClick"
|
||||
/>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<div class="treeTableData_right">
|
||||
|
||||
<div
|
||||
class="form_content scroll-container"
|
||||
v-loading="formGrape.isDialog"
|
||||
v-if="dialog.isDialog"
|
||||
>
|
||||
<el-form
|
||||
:model="formData"
|
||||
:rules="formGrape.ruleData"
|
||||
:labelWidth="grape.labelWidth"
|
||||
label-position="right"
|
||||
ref="formRef"
|
||||
>
|
||||
<el-row :gutter="10" v-if="formGrape.formList">
|
||||
<template
|
||||
v-for="formColumn in formGrape.formList"
|
||||
:key="formColumn.name"
|
||||
>
|
||||
<el-col
|
||||
:span="formColumn.span"
|
||||
v-if="formColumn.showOn ? evalExpr(formColumn.showOn) : true"
|
||||
>
|
||||
<el-form-item :label="formColumn.label" :prop="formColumn.name">
|
||||
<Input :data="formColumn" :model="formData" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
<el-input type="hidden" v-model="formData[grape.primaryKey]" />
|
||||
</el-form>
|
||||
<el-row
|
||||
type="flex"
|
||||
justify="center"
|
||||
style="margin-top: 10px; margin-bottom: 40px"
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="submitFormData()"
|
||||
v-loading="saving"
|
||||
v-if="power.add || power.edit"
|
||||
>
|
||||
提交</el-button
|
||||
>
|
||||
<el-button
|
||||
@click="clickDel()"
|
||||
v-loading="saving"
|
||||
v-if="power.edit && dialog.mode == 'edit'"
|
||||
>
|
||||
删除</el-button
|
||||
>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="" v-else>
|
||||
<el-empty description="请选择对象" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, reactive, toRefs, onMounted } from "vue";
|
||||
import http from "@/utils/request";
|
||||
import emitter from "@/plugins/Bus.js";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
const props = defineProps({
|
||||
grapeData: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
initLoading: true,
|
||||
});
|
||||
const TreeData = ref([]);
|
||||
|
||||
const { grapeData, grapeName } = toRefs(props);
|
||||
const grape = grapeData.value.grape;
|
||||
const power = grapeData.value.power;
|
||||
|
||||
console.log("grapeData", grapeData.value);
|
||||
|
||||
const getGrapeTableData = () => {
|
||||
if (!data.initLoading) data.tableLoading = true;
|
||||
http.get(`/api/data/table/${grape.name}`).then((resp) => {
|
||||
data.tableLoading = false;
|
||||
if (resp.code == 200) {
|
||||
console.log(resp.data);
|
||||
TreeData.value = resp.data;
|
||||
}
|
||||
data.initLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const filterText = ref("");
|
||||
const treeRef = ref();
|
||||
|
||||
const defaultProps = {
|
||||
children: "children",
|
||||
label: "title",
|
||||
};
|
||||
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value.filter(val);
|
||||
});
|
||||
|
||||
const filterNode = (value, data) => {
|
||||
if (!value) return true;
|
||||
return data.title.includes(value);
|
||||
};
|
||||
|
||||
const dialog = reactive({
|
||||
id: "",
|
||||
mode: "add",
|
||||
visibled: true,
|
||||
isDialog: false,
|
||||
});
|
||||
const formData = ref({});
|
||||
const formGrape = reactive({
|
||||
formList: [],
|
||||
ruleData: [],
|
||||
tabTable: [],
|
||||
isDialog: true,
|
||||
});
|
||||
const getGrapeFormData = () => {
|
||||
formGrape.isDialog = true;
|
||||
let url = `/api/build/${grape.name}/${dialog.mode}`;
|
||||
http.get(url, { id: dialog.id }).then((resp) => {
|
||||
if (resp.code == 200) {
|
||||
formData.value = resp.data.data;
|
||||
formGrape.formList = resp.data.formList;
|
||||
formGrape.ruleData = resp.data.ruleList;
|
||||
formGrape.tabTable = resp.data.tabTable;
|
||||
formGrape.isDialog = false;
|
||||
dialog.isDialog = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
const evalExpr = (expr) => {
|
||||
return eval(expr);
|
||||
// return true;
|
||||
};
|
||||
|
||||
//tree 当节点被点击的时候触发
|
||||
const nodeClick = (node) => {
|
||||
console.log(node.id);
|
||||
dialog.node = node;
|
||||
dialog.id = node.id;
|
||||
dialog.mode = "edit";
|
||||
dialog.isDialog = true;
|
||||
formGrape.isDialog = true;
|
||||
getGrapeFormData();
|
||||
};
|
||||
|
||||
const clickAdd = () => {
|
||||
dialog.node = {};
|
||||
dialog.id = "";
|
||||
dialog.mode = "add";
|
||||
dialog.isDialog = true;
|
||||
getGrapeFormData();
|
||||
};
|
||||
|
||||
const formRef = ref();
|
||||
const submitFormData = (tableParams) => {
|
||||
formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
data.saving = true;
|
||||
let url = `/api/modify/${grape.name}`;
|
||||
let formParams = {
|
||||
data: formData.value,
|
||||
};
|
||||
|
||||
if (dialog.mode == "add") {
|
||||
let params = {};
|
||||
for (let key in grape.params) {
|
||||
params[grape.params[key]] = tableParams[key];
|
||||
}
|
||||
formParams.data = { ...formParams.data, ...params };
|
||||
}
|
||||
|
||||
if (data.tabTable) {
|
||||
formParams["tabData"] = tabTableRef.value.getTabTableData();
|
||||
}
|
||||
|
||||
http[dialog.mode == "add" ? "post" : "put"](url, formParams).then(
|
||||
(resp) => {
|
||||
if (resp.code == 200) {
|
||||
ElMessage.success("操作成功");
|
||||
dialog.isDialog = false;
|
||||
dialog.id = ''
|
||||
dialog.mode = 'add'
|
||||
|
||||
getGrapeTableData();
|
||||
} else {
|
||||
ElMessage.error(resp.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const clickDel = () => {
|
||||
ElMessageBox.confirm("确定是否删除?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
http
|
||||
.delete(`/api/modify/${grape.name}/${dialog.node[grape.primaryKey]}`)
|
||||
.then((resp) => {
|
||||
if (resp.code == 200) {
|
||||
ElMessage.success("删除成功");
|
||||
getGrapeTableData();
|
||||
dialog.isDialog = false;
|
||||
} else {
|
||||
ElMessage.error(resp.message);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
console.log("取消");
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getGrapeFormData();
|
||||
getGrapeTableData();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
#treeTableData {
|
||||
display: flex;
|
||||
height: calc(100vh - 100px);
|
||||
.treeTableData_left {
|
||||
flex: 0 0 250px;
|
||||
padding: 20px;
|
||||
border: 1px solid #efefef;
|
||||
border-radius: 4px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.treeTableData_right {
|
||||
flex: auto;
|
||||
|
||||
padding: 20px;
|
||||
border: 1px solid #efefef;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.search_content {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
.search_content_item {
|
||||
width: 200px;
|
||||
}
|
||||
.search_but {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.treeTableData_right {
|
||||
.form_content {
|
||||
height: calc(100% - 35px);
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.scroll-container::-webkit-scrollbar-track {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.scroll-container::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 2px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.scroll-container::-webkit-scrollbar-thumb {
|
||||
background-color: #cfcfcf;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<div class="content">
|
||||
<treeTableData
|
||||
:grapeData="grapeData"
|
||||
v-if="type == 'treeForm' && grapeData"
|
||||
></treeTableData>
|
||||
<tableData :grapeData="grapeData"
|
||||
v-if="type == 'crud' && grapeData"></tableData>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import tableData from "./components/tableData.vue";
|
||||
import formData from "./components/formData.vue";
|
||||
import treeTableData from "./components/treeTableData.vue";
|
||||
import http from "@/utils/request";
|
||||
|
||||
const route = useRoute();
|
||||
const grapeName = route.meta.grape;
|
||||
const type = ref();
|
||||
|
||||
const grapeData = ref();
|
||||
|
||||
const dialog = {
|
||||
id: route.query.id,
|
||||
mode: route.query.id ? "edit" : "add",
|
||||
title: route.query.id ? "修改" : "新建",
|
||||
visibled: true,
|
||||
isDialog: false,
|
||||
};
|
||||
|
||||
http.get(`/api/build/${grapeName}`).then((resp) => {
|
||||
if (resp.code == 200) {
|
||||
grapeData.value = resp.data;
|
||||
type.value = resp.data.grape.pageType
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content {
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
min-height: calc(100vh - 100px);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<div>
|
||||
首页
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<el-breadcrumb-item :to="{name: 'welcome'}" :replace="true">
|
||||
<el-icon :size="14">
|
||||
<HomeFilled />
|
||||
</el-icon> 主页
|
||||
</el-breadcrumb-item>
|
||||
<transition-group name="breadcrumb" mode="out-in">
|
||||
<template v-for="item in breadList" :key="item.title" >
|
||||
<el-breadcrumb-item v-if="item.path!='/' && !item.meta.hiddenBreadcrumb" :key="item.meta.title">{{item.meta.title}}</el-breadcrumb-item>
|
||||
</template>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter();
|
||||
|
||||
const breadList = ref(route.meta.breadcrumb);
|
||||
|
||||
router.beforeEach((to, form, next) => {
|
||||
console.log(to);
|
||||
breadList.value = to.meta.breadcrumb;
|
||||
next();
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
margin-left: 10px;
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<el-menu-item
|
||||
ref="item"
|
||||
:index="menu.path"
|
||||
:key="menu.name"
|
||||
v-if="
|
||||
menu.children == null ||
|
||||
menu.children.length <= 0 ||
|
||||
metaHidden(menu.children)
|
||||
"
|
||||
>
|
||||
<template #title>
|
||||
<el-icon v-if="menu.meta.icon" style="margin-right: 5px">
|
||||
<component :is="menu.meta.icon" style="width: 18px; height: 18px" />
|
||||
</el-icon>
|
||||
<span>{{ menu.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
<el-sub-menu v-else :index="menu.name">
|
||||
<template #title>
|
||||
<el-icon v-if="menu.meta.icon" style="margin-right: 5px">
|
||||
<component :is="menu.meta.icon" style="width: 18px; height: 18px" />
|
||||
</el-icon>
|
||||
<span>{{ menu.meta.title }}</span>
|
||||
</template>
|
||||
<template v-for="child in menu.children" :key="child.name">
|
||||
<menu-item
|
||||
:class="['nest-menu', route.path == child.name ? 'on' : '']"
|
||||
:menu="child"
|
||||
:isChild="true"
|
||||
v-if="!child.meta.hidden"
|
||||
/>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRoute } from "vue-router";
|
||||
import { toRefs } from "vue";
|
||||
const props = defineProps({
|
||||
menu: Object,
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const { menu } = toRefs(props);
|
||||
|
||||
const metaHidden = (menu) => {
|
||||
let hiddenIf = true;
|
||||
|
||||
menu.map((item) => {
|
||||
if (!item.meta.hidden) {
|
||||
hiddenIf = false;
|
||||
}
|
||||
});
|
||||
|
||||
return hiddenIf;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.grape-app .on .el-menu-item {
|
||||
background-color: hsla(0deg, 0%, 100%, 0.3) !important;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,346 @@
|
|||
<template>
|
||||
<el-container v-if="store.menuBar == 'left'">
|
||||
<el-aside :class="['aside', isCollapse ? 'isCollapse' : '']">
|
||||
<div class="NavMenu_title">
|
||||
<el-icon :size="28">
|
||||
<eleme />
|
||||
</el-icon>
|
||||
<span class="title" v-if="!isCollapse">{{ store.title }}</span>
|
||||
</div>
|
||||
|
||||
<el-scrollbar
|
||||
wrap-class="scrollbar_dropdown__wrap"
|
||||
style="height: calc(100vh - 60px)"
|
||||
>
|
||||
<el-menu
|
||||
:uniqueOpened="true"
|
||||
class="el-menu-vertical-demo"
|
||||
background-color="#086dd9"
|
||||
text-color="#fff"
|
||||
active-text-color="#ffffff"
|
||||
:collapse="isCollapse"
|
||||
:router="true"
|
||||
:default-active="route.path"
|
||||
>
|
||||
<template v-for="menu in user.menus" :key="menu.name">
|
||||
<menu-item :menu="menu" />
|
||||
</template>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
<el-container>
|
||||
<el-header class="yd_header">
|
||||
<div style="display: flex; align-items: center">
|
||||
<el-icon :size="24" class="icon" @click="isCollapse = !isCollapse">
|
||||
<expand v-if="isCollapse" />
|
||||
<fold v-else />
|
||||
</el-icon>
|
||||
<breadcrumb style="margin-left: 20px;" />
|
||||
</div>
|
||||
|
||||
<el-space wrap :size="25">
|
||||
<el-icon :size="24" class="icon" @click="screen">
|
||||
<full-screen />
|
||||
</el-icon>
|
||||
<el-badge is-dot class="item" style="font-size: 0">
|
||||
<el-icon :size="24" class="icon">
|
||||
<bell-filled />
|
||||
</el-icon>
|
||||
</el-badge>
|
||||
<el-dropdown class="userCenter">
|
||||
<div class="el-dropdown-link">
|
||||
<el-avatar
|
||||
src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
|
||||
:size="24"
|
||||
></el-avatar>
|
||||
<span class="name">admin</span>
|
||||
<el-icon>
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Avatar">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item icon="SwitchButton">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-space>
|
||||
</el-header>
|
||||
|
||||
<el-main>
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</router-view>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
|
||||
<el-container v-else-if="store.menuBar == 'top'">
|
||||
<el-header class="yd_header_top">
|
||||
<div class="yd_header_top_left">
|
||||
<div class="yd_header_top_title">
|
||||
<el-icon :size="28">
|
||||
<eleme />
|
||||
</el-icon>
|
||||
<span class="title" v-if="!isCollapse">{{ store.title }}</span>
|
||||
</div>
|
||||
<el-menu
|
||||
class="el-menu-demo"
|
||||
mode="horizontal"
|
||||
@select="handleSelect"
|
||||
text-color="#fff"
|
||||
active-text-color="#ffffff"
|
||||
:router="true"
|
||||
:default-active="route.path"
|
||||
>
|
||||
<template v-for="menu in user.menus" :key="menu.name">
|
||||
<menu-item :menu="menu" />
|
||||
</template>
|
||||
</el-menu>
|
||||
</div>
|
||||
<el-space wrap :size="25">
|
||||
<el-icon :size="24" class="icon" @click="screen">
|
||||
<full-screen />
|
||||
</el-icon>
|
||||
<el-badge is-dot class="item" style="font-size: 0">
|
||||
<el-icon :size="24" class="icon">
|
||||
<bell-filled />
|
||||
</el-icon>
|
||||
</el-badge>
|
||||
<el-dropdown class="userCenter">
|
||||
<div class="el-dropdown-link">
|
||||
<el-avatar
|
||||
src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
|
||||
:size="24"
|
||||
></el-avatar>
|
||||
<span class="name">admin</span>
|
||||
<el-icon>
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Avatar">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item icon="Avatar" @click="handleUserMenu"
|
||||
>退出</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-space></el-header
|
||||
>
|
||||
<div class="breadcrumb">
|
||||
<breadcrumb />
|
||||
</div>
|
||||
<el-container>
|
||||
<el-main>
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</router-view>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from "vue";
|
||||
import { useCar } from "@/store";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import emitter from "@/plugins/Bus";
|
||||
import tools from "@/utils/tools";
|
||||
|
||||
import menuItem from "./components/menu/menu-item.vue";
|
||||
import breadcrumb from "./components/breadcrumb/index.vue";
|
||||
|
||||
const user = tools.data.get("user");
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
let store = useCar();
|
||||
|
||||
const isCollapse = ref(false);
|
||||
|
||||
//全屏
|
||||
const flag = ref(false);
|
||||
const screen = () => {
|
||||
let element = document.documentElement;
|
||||
// 不全屏是null,返回false,
|
||||
flag.value = document.fullscreenElement === null ? false : true;
|
||||
// false是进入全屏状态
|
||||
if (flag.value) {
|
||||
// 退出全屏
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
} else {
|
||||
// 全屏
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
}
|
||||
}
|
||||
// 切换文本状态(只是用在文本上,文本不是动态可以忽略)
|
||||
flag.value = !flag.value;
|
||||
};
|
||||
|
||||
const data = reactive({
|
||||
dialog: {
|
||||
visibled: false,
|
||||
},
|
||||
});
|
||||
|
||||
emitter.on("closeModifyPassword", (e) => {
|
||||
data.dialog.visibled = false;
|
||||
});
|
||||
|
||||
const handleUserMenu = (command) => {
|
||||
if ("modifyPassword" == command) {
|
||||
data.dialog.visibled = true;
|
||||
} else if ("logout" == command) {
|
||||
ElMessageBox.confirm("确定要退出当前系统吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
tools.data.clear();
|
||||
Router.push("/login");
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.aside {
|
||||
width: 220px;
|
||||
background-color: rgb(8, 109, 217);
|
||||
transition: all 0.4s;
|
||||
&.isCollapse {
|
||||
width: 64px;
|
||||
}
|
||||
}
|
||||
.NavMenu_title {
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #ffffff;
|
||||
overflow: hidden;
|
||||
.title {
|
||||
font-size: 18px;
|
||||
margin-left: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.el-container {
|
||||
height: 100vh;
|
||||
.el-header {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
}
|
||||
.yd_header_top {
|
||||
.el-popper.is-pure {
|
||||
background-color: #1890ff !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
background-color: transparent;
|
||||
border-right: none;
|
||||
}
|
||||
.el-menu-item {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
}
|
||||
.el-menu-item.is-active {
|
||||
background-color: #1890ff !important;
|
||||
}
|
||||
.yd_header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
.icon {
|
||||
color: #1890ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
}
|
||||
.el-dropdown-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.el-dropdown-link {
|
||||
outline: none;
|
||||
}
|
||||
.userCenter {
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.yd_header {
|
||||
.el-space__item:last-child {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
.el-dropdown-link {
|
||||
.name {
|
||||
margin: 0 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.yd_header_top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #1890ff !important;
|
||||
overflow: hidden;
|
||||
color: #ffffff;
|
||||
.yd_header_top_left {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
.yd_header_top_title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #ffffff;
|
||||
overflow: hidden;
|
||||
width: 220px;
|
||||
.title {
|
||||
font-size: 18px;
|
||||
margin-left: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.el-menu--horizontal .el-menu-item:not(.is-disabled):hover {
|
||||
background-color: #096dd9;
|
||||
}
|
||||
.el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
background-color: #096dd9;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dropdown-link {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.el-menu--horizontal .el-menu .el-menu-item.is-active,
|
||||
.el-menu--horizontal .el-menu .el-sub-menu.is-active > .el-sub-menu__title {
|
||||
color: #1890ff !important;
|
||||
background-color: #e6f7ff !important;
|
||||
}
|
||||
.el-menu--horizontal .el-menu .el-menu-item,
|
||||
.el-menu--horizontal .el-menu .el-sub-menu__title {
|
||||
color: #595959 !important;
|
||||
}
|
||||
|
||||
.is-light .el-menu--horizontal .el-menu-item:not(.is-disabled):focus,
|
||||
.is-light .el-menu--horizontal .el-menu-item:not(.is-disabled):hover {
|
||||
color: #1890ff !important;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
padding: 20px 10px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,252 @@
|
|||
<template>
|
||||
<div class="main">
|
||||
<div class="login">
|
||||
<div class="bd5_left"></div>
|
||||
<div class="bd5_right"></div>
|
||||
<div class="login_image"></div>
|
||||
<div class="auto">
|
||||
<span class="auto_title"> 登录 </span>
|
||||
<el-form
|
||||
:model="data"
|
||||
size="medium"
|
||||
ref="formRef"
|
||||
@keyup.enter.native="handleSubmit"
|
||||
>
|
||||
<el-row :span="22">
|
||||
<el-form-item prop="user">
|
||||
<el-input
|
||||
v-model="data.user"
|
||||
style="width: 100%; margin-bottom: 20px"
|
||||
placeholder="帐号"
|
||||
prefix-icon="User"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row :span="22">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="data.password"
|
||||
style="width: 100%; margin-bottom: 48px"
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
prefix-icon="Lock"
|
||||
show-password
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-button
|
||||
class="login-btn-submit"
|
||||
type="primary"
|
||||
@click="handleSubmit()"
|
||||
:loading="loading"
|
||||
>
|
||||
<span class="info1">登 录</span>
|
||||
</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import tools from "@/utils/tools";
|
||||
import http from "@/utils/request.js";
|
||||
import { encrypt } from "@/utils";
|
||||
|
||||
const Router = useRouter();
|
||||
|
||||
const data = reactive({
|
||||
user: "",
|
||||
password: "",
|
||||
});
|
||||
const loading = ref(false);
|
||||
const formRef = ref();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
var params = {
|
||||
user: encrypt(data.user),
|
||||
password: encrypt(data.password),
|
||||
};
|
||||
http.post("/api/auth/login", params).then((resp) => {
|
||||
if (resp.code == 200) {
|
||||
var user = {
|
||||
token: resp.data.token,
|
||||
name: resp.data.name,
|
||||
menus: resp.data.menus,
|
||||
buttons: resp.data.buttons,
|
||||
accountType: resp.data.accountType,
|
||||
};
|
||||
tools.data.set("user", user);
|
||||
ElMessage.success("登录成功");
|
||||
//loading.value = false;
|
||||
Router.push("/");
|
||||
// 跳转到 首页路由
|
||||
} else {
|
||||
ElMessage.error(resp.message);
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.main {
|
||||
.login {
|
||||
.auto {
|
||||
.el-row{
|
||||
width: 100%;
|
||||
.el-form-item{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.el-input__inner {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.el-input__wrapper {
|
||||
border: 1px solid #a8abb2;
|
||||
background-color: #fff;
|
||||
box-shadow: none;
|
||||
}
|
||||
.el-form-item__content {
|
||||
-webkit-user-select: none; /*谷歌 /Chrome*/
|
||||
-moz-user-select: none; /*火狐/Firefox*/
|
||||
-ms-user-select: none; /*IE 10+*/
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
|
||||
.login {
|
||||
position: relative;
|
||||
background: url(@/assets/images/login_bg_3.png);
|
||||
background-size: 80% 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
.login_image {
|
||||
z-index: 31;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 150px;
|
||||
width: 45%;
|
||||
height: 599px;
|
||||
background: url(@/assets/images/login_bg_1.png) -1px 0px no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.bd5_left {
|
||||
z-index: 313;
|
||||
position: absolute;
|
||||
left: -38px;
|
||||
bottom: 0;
|
||||
width: 297px;
|
||||
height: 113px;
|
||||
background: url(@/assets/images/login_bg_4.png) -1px 0px no-repeat;
|
||||
}
|
||||
|
||||
.bd5_right {
|
||||
z-index: 267;
|
||||
position: absolute;
|
||||
right: 20%;
|
||||
top: 0;
|
||||
width: 297px;
|
||||
height: 113px;
|
||||
background: url(@/assets/images/login_bg_2.png) 0px 0px no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.auto {
|
||||
z-index: 4;
|
||||
height: 350px;
|
||||
width: 436px;
|
||||
position: absolute;
|
||||
padding: 58px 32px;
|
||||
left: 50%;
|
||||
transform: translateY(-50%);
|
||||
top: 50%;
|
||||
background: #fefefe;
|
||||
box-shadow: 10px 10px 0px 0px #e4e8ec;
|
||||
border-radius: 2px;
|
||||
backdrop-filter: blur(50px);
|
||||
|
||||
.auto_title {
|
||||
display: block;
|
||||
width: 60px;
|
||||
height: 33px;
|
||||
font-size: 30px;
|
||||
margin-bottom: 48px;
|
||||
font-family: PingFangSC-Medium, PingFang SC;
|
||||
font-weight: 500;
|
||||
color: #0081eb;
|
||||
line-height: 33px;
|
||||
}
|
||||
|
||||
.login-btn-submit {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: #0081eb;
|
||||
border-radius: 4px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.info1 {
|
||||
width: 52px;
|
||||
height: 24px;
|
||||
font-size: 22px;
|
||||
font-family: PingFangSC-Semibold, PingFang SC;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 996px) {
|
||||
.main .login .login_image {
|
||||
width: 80%;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -60%);
|
||||
}
|
||||
.main .auto {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 99;
|
||||
}
|
||||
.main .login {
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 662px) {
|
||||
.main .login .login_image {
|
||||
width: 100%;
|
||||
transform: translate(-56%, -60%);
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 420px) {
|
||||
.main .auto {
|
||||
width: 100%;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,36 @@
|
|||
import { defineConfig } from "vite";
|
||||
|
||||
import { fileURLToPath, URL } from 'url'
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import AutoImport from "unplugin-auto-import/vite";
|
||||
import Components from "unplugin-vue-components/vite";
|
||||
// import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
// resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
// resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue'],
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0', // 解决“vite use `--host` to expose”-- 0.0.0.0 将监听所有地址
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://fywz.btdit.cn/',
|
||||
changeOrigin: true,
|
||||
// ws: true,// 开启webSocket
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|