parent
c543fe894a
commit
eeb9c839f9
@ -0,0 +1,34 @@ |
|||||||
|
/** |
||||||
|
* @description: 请求结果集 |
||||||
|
*/ |
||||||
|
export enum ResultEnum { |
||||||
|
SUCCESS = 0, |
||||||
|
ERROR = -1, |
||||||
|
TIMEOUT = 10042, |
||||||
|
TYPE = 'success' |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @description: 请求方法 |
||||||
|
*/ |
||||||
|
export enum RequestEnum { |
||||||
|
GET = 'GET', |
||||||
|
POST = 'POST', |
||||||
|
PATCH = 'PATCH', |
||||||
|
PUT = 'PUT', |
||||||
|
DELETE = 'DELETE' |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @description: 常用的contentTyp类型 |
||||||
|
*/ |
||||||
|
export enum ContentTypeEnum { |
||||||
|
// json
|
||||||
|
JSON = 'application/json;charset=UTF-8', |
||||||
|
// json
|
||||||
|
TEXT = 'text/plain;charset=UTF-8', |
||||||
|
// form-data 一般配合qs
|
||||||
|
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', |
||||||
|
// form-data 上传
|
||||||
|
FORM_DATA = 'multipart/form-data;charset=UTF-8' |
||||||
|
} |
@ -0,0 +1,260 @@ |
|||||||
|
<!-- |
||||||
|
* @Author: 卜启缘 |
||||||
|
* @Date: 2021-06-24 18:36:03 |
||||||
|
* @LastEditTime: 2021-06-26 21:34:53 |
||||||
|
* @LastEditors: 卜启缘 |
||||||
|
* @Description: 接口请求 |
||||||
|
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\left-aside\components\data-source\data-fetch.vue |
||||||
|
--> |
||||||
|
<template> |
||||||
|
<div class="!mb-10px"> |
||||||
|
<el-button type="primary" size="small" @click="showModelMoal">添加</el-button> |
||||||
|
<el-button type="warning" size="small" @click="showImportSwaggerJsonModal" |
||||||
|
>导入swagger</el-button |
||||||
|
> |
||||||
|
</div> |
||||||
|
<el-collapse v-model="state.activeNames"> |
||||||
|
<template v-for="item in apis" :key="item.key"> |
||||||
|
<el-collapse-item :title="item.name" :name="item.key"> |
||||||
|
<template #title> |
||||||
|
<div class="model-item-title"> |
||||||
|
<span>{{ item.name }}</span> |
||||||
|
<div class="model-actions"> |
||||||
|
<i class="el-icon-edit" @click="editApiItem(item)"></i> |
||||||
|
<el-popconfirm |
||||||
|
confirm-button-text="确定" |
||||||
|
cancel-button-text="取消" |
||||||
|
icon="el-icon-info" |
||||||
|
icon-color="red" |
||||||
|
title="确定要删除该接口吗?" |
||||||
|
@confirm="deleteFetchApi(item.key)" |
||||||
|
> |
||||||
|
<template #reference> |
||||||
|
<i class="el-icon-delete"></i> |
||||||
|
</template> |
||||||
|
</el-popconfirm> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<div class="low-model-item"> |
||||||
|
<pre class="code">{{ JSON.stringify(item, null, 2) }}</pre> |
||||||
|
</div> |
||||||
|
</el-collapse-item> |
||||||
|
</template> |
||||||
|
</el-collapse> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="tsx"> |
||||||
|
import { reactive, computed } from 'vue' |
||||||
|
import { |
||||||
|
ElForm, |
||||||
|
ElFormItem, |
||||||
|
ElInput, |
||||||
|
ElSelect, |
||||||
|
ElOption, |
||||||
|
ElButton, |
||||||
|
ElMessage, |
||||||
|
ElCascader |
||||||
|
} from 'element-plus' |
||||||
|
import { useVisualData, fieldTypes } from '@/visual-editor/hooks/useVisualData' |
||||||
|
import type { FetchApiItem, VisualEditorModel } from '@/visual-editor/visual-editor.utils' |
||||||
|
import { useModal } from '@/visual-editor/hooks/useModal' |
||||||
|
import { cloneDeep } from 'lodash' |
||||||
|
import { generateUUID } from '@/visual-editor/utils/' |
||||||
|
import { RequestEnum, ContentTypeEnum } from '@/enums/httpEnum' |
||||||
|
|
||||||
|
interface IState { |
||||||
|
activeNames: string[] |
||||||
|
ruleFormRef: any |
||||||
|
ruleForm: FetchApiItem |
||||||
|
} |
||||||
|
|
||||||
|
const { jsonData, incrementFetchApi, updateFetchApi, deleteFetchApi } = useVisualData() |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 接口集合 |
||||||
|
*/ |
||||||
|
const apis = computed(() => cloneDeep(jsonData.actions.fetch.apis)) |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 模型集合 |
||||||
|
*/ |
||||||
|
const models = computed(() => cloneDeep(jsonData.models)) |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 是否处于编辑状态 |
||||||
|
*/ |
||||||
|
const isEdit = computed(() => apis.value.some((item) => item.key == state.ruleForm.key)) |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 创建空的数据接口对象 |
||||||
|
*/ |
||||||
|
const createEmptyApiItem = (): FetchApiItem => ({ |
||||||
|
key: generateUUID(), |
||||||
|
name: '', |
||||||
|
options: { |
||||||
|
url: '', // 请求的url |
||||||
|
method: RequestEnum.GET, // 请求的方法 |
||||||
|
contentType: 'JSON' // 请求的内容类型 |
||||||
|
}, |
||||||
|
data: { |
||||||
|
bind: '', // 请求绑定对应的某个实体 |
||||||
|
recv: '' // 响应的结果绑定到某个实体上 |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
const state = reactive<IState>({ |
||||||
|
activeNames: [], |
||||||
|
ruleFormRef: null, |
||||||
|
ruleForm: createEmptyApiItem() |
||||||
|
}) |
||||||
|
|
||||||
|
const rules = { |
||||||
|
name: [{ required: true, message: '请输入接口名称', trigger: 'change' }], |
||||||
|
'options.url': [{ required: true, message: '请输入接口名称', trigger: 'change' }], |
||||||
|
'options.contentType': [{ required: true, message: '请选择内容类型', trigger: 'change' }] |
||||||
|
} |
||||||
|
|
||||||
|
const handleBindChange = (e: VisualEditorModel[]) => { |
||||||
|
console.log(e, 'kkk') |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 显示添加接口弹窗 |
||||||
|
*/ |
||||||
|
const showModelMoal = () => { |
||||||
|
useModal({ |
||||||
|
title: `${isEdit.value ? '编辑' : '新增'}接口`, |
||||||
|
props: { |
||||||
|
width: 600 |
||||||
|
}, |
||||||
|
content: () => ( |
||||||
|
<ElForm |
||||||
|
model={state.ruleForm} |
||||||
|
ref={(el) => el && (state.ruleFormRef = el)} |
||||||
|
label-width="100px" |
||||||
|
size={'mini'} |
||||||
|
rules={rules} |
||||||
|
class="demo-ruleForm" |
||||||
|
> |
||||||
|
<ElFormItem label="名称" prop="name"> |
||||||
|
<ElInput v-model={state.ruleForm.name} placeholder={'请输入接口名称'}></ElInput> |
||||||
|
</ElFormItem> |
||||||
|
<ElFormItem label="接口" prop={'options.url'}> |
||||||
|
<ElInput v-model={state.ruleForm.options.url} placeholder={'请输入接口地址'}> |
||||||
|
{{ |
||||||
|
prepend: () => ( |
||||||
|
<ElSelect v-model={state.ruleForm.options.method} class={'w-90px'}> |
||||||
|
{Object.keys(RequestEnum).map((key) => ( |
||||||
|
<ElOption key={key} label={key} value={key}></ElOption> |
||||||
|
))} |
||||||
|
</ElSelect> |
||||||
|
) |
||||||
|
}} |
||||||
|
</ElInput> |
||||||
|
</ElFormItem> |
||||||
|
<ElFormItem label="内容类型" prop={'options.contentType'}> |
||||||
|
<ElSelect v-model={state.ruleForm.options.contentType}> |
||||||
|
{Object.keys(ContentTypeEnum).map((key) => ( |
||||||
|
<ElOption key={key} label={key} value={key}></ElOption> |
||||||
|
))} |
||||||
|
</ElSelect> |
||||||
|
</ElFormItem> |
||||||
|
<ElFormItem label="请求数据" prop={'data.bind'}> |
||||||
|
<ElCascader |
||||||
|
clearable={true} |
||||||
|
props={{ |
||||||
|
checkStrictly: true, |
||||||
|
children: 'entitys', |
||||||
|
label: 'name', |
||||||
|
value: 'key', |
||||||
|
expandTrigger: 'hover' |
||||||
|
}} |
||||||
|
placeholder="请选择绑定的请求数据" |
||||||
|
onChange={handleBindChange} |
||||||
|
v-model={state.ruleForm.data.bind} |
||||||
|
options={models.value} |
||||||
|
></ElCascader> |
||||||
|
</ElFormItem> |
||||||
|
</ElForm> |
||||||
|
), |
||||||
|
onConfirm: () => { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
state.ruleFormRef.validate((valid) => { |
||||||
|
if (valid) { |
||||||
|
if (isEdit.value) { |
||||||
|
updateFetchApi(cloneDeep(state.ruleForm)) |
||||||
|
} else { |
||||||
|
incrementFetchApi(cloneDeep(state.ruleForm)) |
||||||
|
} |
||||||
|
ElMessage.success(`${isEdit.value ? '修改' : '新增'}接口成功!`) |
||||||
|
state.ruleForm = createEmptyApiItem() |
||||||
|
resolve('submit!') |
||||||
|
} else { |
||||||
|
reject() |
||||||
|
console.log('error submit!!') |
||||||
|
return false |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
}, |
||||||
|
onCancel: () => (state.ruleForm = createEmptyApiItem()) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 编辑模型 |
||||||
|
*/ |
||||||
|
const editApiItem = (apiItem: FetchApiItem) => { |
||||||
|
console.log(apiItem) |
||||||
|
state.ruleForm = cloneDeep(apiItem) |
||||||
|
showModelMoal() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 显示导入swagger JSON模态框 |
||||||
|
*/ |
||||||
|
const showImportSwaggerJsonModal = () => { |
||||||
|
ElMessage.info('敬请期待!') |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
.code { |
||||||
|
padding: 4px 10px; |
||||||
|
font-size: 12px; |
||||||
|
line-height: 1.4; |
||||||
|
} |
||||||
|
|
||||||
|
.model-item-title { |
||||||
|
flex: 1; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: space-between; |
||||||
|
|
||||||
|
.model-actions { |
||||||
|
i { |
||||||
|
padding: 6px; |
||||||
|
margin: 0 2px; |
||||||
|
font-weight: bold; |
||||||
|
cursor: pointer; |
||||||
|
border-radius: 2px; |
||||||
|
opacity: 0.7; |
||||||
|
transition: all 0.1s; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
background-color: #f1f1f1; |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
|
||||||
|
&.el-icon-delete { |
||||||
|
color: #f44336; |
||||||
|
} |
||||||
|
|
||||||
|
&.el-icon-edit { |
||||||
|
color: #2196f3; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,290 @@ |
|||||||
|
<!-- |
||||||
|
* @Author: 卜启缘 |
||||||
|
* @Date: 2021-06-24 18:36:03 |
||||||
|
* @LastEditTime: 2021-06-26 21:35:13 |
||||||
|
* @LastEditors: 卜启缘 |
||||||
|
* @Description: 数据模型管理 |
||||||
|
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\left-aside\components\data-source\data-model.vue |
||||||
|
--> |
||||||
|
<template> |
||||||
|
<div class="!mb-10px"> |
||||||
|
<el-button type="primary" size="small" @click="showModelMoal">添加</el-button> |
||||||
|
<el-button type="warning" size="small" @click="showImportSwaggerJsonModal" |
||||||
|
>导入swagger</el-button |
||||||
|
> |
||||||
|
</div> |
||||||
|
<el-collapse v-model="state.activeNames"> |
||||||
|
<template v-for="item in models" :key="item.key"> |
||||||
|
<el-collapse-item :title="item.name" :name="item.key"> |
||||||
|
<template #title> |
||||||
|
<div class="model-item-title"> |
||||||
|
<span>{{ item.name }}</span> |
||||||
|
<div class="model-actions"> |
||||||
|
<i class="el-icon-edit" @click="editModel(item)"></i> |
||||||
|
<el-popconfirm |
||||||
|
confirm-button-text="确定" |
||||||
|
cancel-button-text="取消" |
||||||
|
icon="el-icon-info" |
||||||
|
icon-color="red" |
||||||
|
title="确定要删除该模型吗?" |
||||||
|
@confirm="deleteModel(item.key)" |
||||||
|
> |
||||||
|
<template #reference> |
||||||
|
<i class="el-icon-delete"></i> |
||||||
|
</template> |
||||||
|
</el-popconfirm> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<template v-for="entity in item.entitys" :key="entity.key"> |
||||||
|
<div class="low-model-item"> |
||||||
|
<pre class="code">{{ JSON.stringify(entity, null, 2) }}</pre> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
</el-collapse-item> |
||||||
|
</template> |
||||||
|
</el-collapse> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="tsx"> |
||||||
|
import { reactive, computed } from 'vue' |
||||||
|
import { |
||||||
|
ElForm, |
||||||
|
ElFormItem, |
||||||
|
ElInput, |
||||||
|
ElSelect, |
||||||
|
ElOption, |
||||||
|
ElCard, |
||||||
|
ElButton, |
||||||
|
ElMessage |
||||||
|
} from 'element-plus' |
||||||
|
import { useVisualData, fieldTypes } from '@/visual-editor/hooks/useVisualData' |
||||||
|
import type { VisualEditorModel } from '@/visual-editor/visual-editor.utils' |
||||||
|
import { useModal } from '@/visual-editor/hooks/useModal' |
||||||
|
import { cloneDeep } from 'lodash' |
||||||
|
import { generateUUID } from '@/visual-editor/utils/' |
||||||
|
|
||||||
|
interface IState { |
||||||
|
activeNames: string[] |
||||||
|
ruleFormRef: any |
||||||
|
ruleForm: VisualEditorModel |
||||||
|
} |
||||||
|
|
||||||
|
const { jsonData, incrementModel, updateModel, deleteModel } = useVisualData() |
||||||
|
/** |
||||||
|
* @description 模型集合 |
||||||
|
*/ |
||||||
|
const models = computed(() => cloneDeep(jsonData.models)) |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 是否处于编辑状态 |
||||||
|
*/ |
||||||
|
const isEdit = computed(() => models.value.some((item) => item.key == state.ruleForm.key)) |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 创建空的实体对象 |
||||||
|
*/ |
||||||
|
const createEmptyEntity = () => ({ key: '', name: '', type: 'string', value: '' }) |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 创建空的数据模型 |
||||||
|
*/ |
||||||
|
const createEmptyModel = () => ({ |
||||||
|
name: '', |
||||||
|
key: generateUUID(), |
||||||
|
entitys: [createEmptyEntity()] |
||||||
|
}) |
||||||
|
|
||||||
|
const state = reactive<IState>({ |
||||||
|
activeNames: [], |
||||||
|
ruleFormRef: null, |
||||||
|
ruleForm: createEmptyModel() |
||||||
|
}) |
||||||
|
|
||||||
|
/** |
||||||
|
* @param {number} 索引 |
||||||
|
* @description 删除实体项 |
||||||
|
*/ |
||||||
|
const deleteEntityItem = (index: number) => { |
||||||
|
state.ruleForm.entitys.splice(index, 1) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 添加实体项 |
||||||
|
*/ |
||||||
|
const addEntityItem = () => { |
||||||
|
state.ruleForm.entitys.push(createEmptyEntity()) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 显示添加接口弹窗 |
||||||
|
*/ |
||||||
|
const showModelMoal = () => { |
||||||
|
useModal({ |
||||||
|
title: `${isEdit.value ? '编辑' : '新增'}数据源`, |
||||||
|
props: { |
||||||
|
width: 600 |
||||||
|
}, |
||||||
|
content: () => ( |
||||||
|
<ElForm |
||||||
|
model={state.ruleForm} |
||||||
|
ref={(el) => el && (state.ruleFormRef = el)} |
||||||
|
label-width="100px" |
||||||
|
size={'mini'} |
||||||
|
class="demo-ruleForm" |
||||||
|
> |
||||||
|
<ElFormItem |
||||||
|
label="数据源名称" |
||||||
|
prop="name" |
||||||
|
rules={[{ required: true, message: '请输入数据源名称', trigger: 'change' }]} |
||||||
|
> |
||||||
|
<ElInput v-model={state.ruleForm.name} placeholder={'请输入数据源名称'}></ElInput> |
||||||
|
</ElFormItem> |
||||||
|
{!state.ruleForm.entitys.length && ( |
||||||
|
<ElFormItem> |
||||||
|
<ElButton onClick={addEntityItem} type={'primary'} size={'mini'}> |
||||||
|
添加实体 |
||||||
|
</ElButton> |
||||||
|
</ElFormItem> |
||||||
|
)} |
||||||
|
{state.ruleForm.entitys.map((entity, index) => ( |
||||||
|
<ElCard |
||||||
|
key={index} |
||||||
|
shadow={'hover'} |
||||||
|
class={'mt-10px'} |
||||||
|
v-slots={{ |
||||||
|
header: () => ( |
||||||
|
<div class={'flex justify-between'}> |
||||||
|
<ElFormItem |
||||||
|
label="实体名称" |
||||||
|
prop={`entitys.${index}.name`} |
||||||
|
rules={[{ required: true, message: '请输入实体名称', trigger: 'change' }]} |
||||||
|
showMessage={false} |
||||||
|
class={'w-300px !mb-0'} |
||||||
|
> |
||||||
|
<ElInput v-model={entity.name} placeholder={'请输入实体名称'}></ElInput> |
||||||
|
</ElFormItem> |
||||||
|
<div> |
||||||
|
<ElButton onClick={() => deleteEntityItem(index)} type={'danger'} size={'mini'}> |
||||||
|
删除 |
||||||
|
</ElButton> |
||||||
|
<ElButton onClick={addEntityItem} type={'primary'} size={'mini'}> |
||||||
|
添加 |
||||||
|
</ElButton> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
}} |
||||||
|
> |
||||||
|
<ElFormItem |
||||||
|
label="实体字段" |
||||||
|
prop={`entitys.${index}.key`} |
||||||
|
rules={[{ required: true, message: '请输入实体字段', trigger: 'change' }]} |
||||||
|
> |
||||||
|
<ElInput v-model={entity.key} placeholder={'请输入实体字段'}></ElInput> |
||||||
|
</ElFormItem> |
||||||
|
<ElFormItem |
||||||
|
label="数据类型" |
||||||
|
prop={`entitys.${index}.type`} |
||||||
|
rules={[{ required: true, message: '请输入数据类型', trigger: 'change' }]} |
||||||
|
> |
||||||
|
<ElSelect v-model={entity.type}> |
||||||
|
{fieldTypes.map((typeItem) => ( |
||||||
|
<ElOption |
||||||
|
key={typeItem.value} |
||||||
|
label={typeItem.label} |
||||||
|
value={typeItem.value} |
||||||
|
></ElOption> |
||||||
|
))} |
||||||
|
</ElSelect> |
||||||
|
</ElFormItem> |
||||||
|
<ElFormItem label="默认数据" prop={`entitys.${index}.value`}> |
||||||
|
<ElInput |
||||||
|
v-model={entity.value} |
||||||
|
placeholder={'实体默认数据,不填则为对应类型数据'} |
||||||
|
></ElInput> |
||||||
|
</ElFormItem> |
||||||
|
</ElCard> |
||||||
|
))} |
||||||
|
</ElForm> |
||||||
|
), |
||||||
|
onConfirm: () => { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
state.ruleFormRef.validate((valid) => { |
||||||
|
if (valid) { |
||||||
|
if (isEdit.value) { |
||||||
|
updateModel(cloneDeep(state.ruleForm)) |
||||||
|
} else { |
||||||
|
incrementModel(cloneDeep(state.ruleForm)) |
||||||
|
} |
||||||
|
ElMessage.success(`${isEdit.value ? '修改' : '新增'}模型成功!`) |
||||||
|
state.ruleForm = createEmptyModel() |
||||||
|
resolve('submit!') |
||||||
|
} else { |
||||||
|
reject() |
||||||
|
console.log('error submit!!') |
||||||
|
return false |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
}, |
||||||
|
onCancel: () => (state.ruleForm = createEmptyModel()) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 编辑模型 |
||||||
|
*/ |
||||||
|
const editModel = (model: VisualEditorModel) => { |
||||||
|
console.log(model) |
||||||
|
state.ruleForm = cloneDeep(model) |
||||||
|
showModelMoal() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @description 显示导入swagger JSON模态框 |
||||||
|
*/ |
||||||
|
const showImportSwaggerJsonModal = () => { |
||||||
|
ElMessage.info('敬请期待!') |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
.code { |
||||||
|
padding: 4px 10px; |
||||||
|
font-size: 12px; |
||||||
|
line-height: 1.4; |
||||||
|
} |
||||||
|
|
||||||
|
.model-item-title { |
||||||
|
flex: 1; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: space-between; |
||||||
|
|
||||||
|
.model-actions { |
||||||
|
i { |
||||||
|
padding: 6px; |
||||||
|
margin: 0 2px; |
||||||
|
font-weight: bold; |
||||||
|
cursor: pointer; |
||||||
|
border-radius: 2px; |
||||||
|
opacity: 0.7; |
||||||
|
transition: all 0.1s; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
background-color: #f1f1f1; |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
|
||||||
|
&.el-icon-delete { |
||||||
|
color: #f44336; |
||||||
|
} |
||||||
|
|
||||||
|
&.el-icon-edit { |
||||||
|
color: #2196f3; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -1,255 +1,31 @@ |
|||||||
<!-- |
<!-- |
||||||
* @Author: 卜启缘 |
* @Author: 卜启缘 |
||||||
* @Date: 2021-06-24 18:36:03 |
* @Date: 2021-06-24 18:36:03 |
||||||
* @LastEditTime: 2021-06-25 21:38:33 |
* @LastEditTime: 2021-06-26 14:15:33 |
||||||
* @LastEditors: 卜启缘 |
* @LastEditors: 卜启缘 |
||||||
* @Description: 数据源管理 |
* @Description: 数据源管理 |
||||||
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\left-aside\components\data-source\index.vue |
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\left-aside\components\data-source\index.vue |
||||||
--> |
--> |
||||||
<template> |
<template> |
||||||
<el-button class="!my-10px" type="primary" size="small" @click="showModelMoal">添加</el-button> |
<el-tabs type="border-card" stretch> |
||||||
<el-collapse v-model="state.activeNames"> |
<el-tab-pane label="数据模型"> |
||||||
<template v-for="item in models" :key="item.key"> |
<data-model /> |
||||||
<el-collapse-item :title="item.name" :name="item.key"> |
</el-tab-pane> |
||||||
<template #title> |
<el-tab-pane label="数据接口"> |
||||||
<div class="model-item-title"> |
<data-fetch /> |
||||||
<span>{{ item.name }}</span> |
</el-tab-pane> |
||||||
<div class="model-actions"> |
</el-tabs> |
||||||
<i class="el-icon-edit" @click="editModel(item)"></i> |
|
||||||
<i class="el-icon-delete" @click="deleteModel(item.key)"></i> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
<template v-for="entity in item.entitys" :key="entity.key"> |
|
||||||
<div class="low-model-item"> |
|
||||||
<pre class="code">{{ JSON.stringify(entity, null, 2) }}</pre> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
</el-collapse-item> |
|
||||||
</template> |
|
||||||
</el-collapse> |
|
||||||
</template> |
</template> |
||||||
|
|
||||||
<script setup lang="tsx"> |
<script setup lang="tsx"> |
||||||
import { reactive, computed } from 'vue' |
import DataModel from './data-model.vue' |
||||||
import { |
import DataFetch from './data-fetch.vue' |
||||||
ElForm, |
|
||||||
ElFormItem, |
|
||||||
ElInput, |
|
||||||
ElSelect, |
|
||||||
ElOption, |
|
||||||
ElCard, |
|
||||||
ElButton, |
|
||||||
ElMessage |
|
||||||
} from 'element-plus' |
|
||||||
import { useVisualData, fieldTypes } from '@/visual-editor/hooks/useVisualData' |
|
||||||
import type { VisualEditorModel } from '@/visual-editor/visual-editor.utils' |
|
||||||
import { useModal } from '@/visual-editor/hooks/useModal' |
|
||||||
import { cloneDeep } from 'lodash' |
|
||||||
import { generateUUID } from '@/visual-editor/utils/' |
|
||||||
|
|
||||||
interface IState { |
|
||||||
activeNames: string[] |
|
||||||
ruleFormRef: any |
|
||||||
ruleForm: VisualEditorModel |
|
||||||
} |
|
||||||
|
|
||||||
const { jsonData, incrementModel, updateModel, deleteModel } = useVisualData() |
|
||||||
/** |
|
||||||
* @description 模型集合 |
|
||||||
*/ |
|
||||||
const models = computed(() => cloneDeep(jsonData.models)) |
|
||||||
|
|
||||||
/** |
|
||||||
* @description 是否处于编辑状态 |
|
||||||
*/ |
|
||||||
const isEdit = computed(() => models.value.some((item) => item.key == state.ruleForm.key)) |
|
||||||
|
|
||||||
/** |
|
||||||
* @description 创建空的实体对象 |
|
||||||
*/ |
|
||||||
const createEmptyEntity = () => ({ field: '', name: '', type: 'string', value: '' }) |
|
||||||
|
|
||||||
/** |
|
||||||
* @description 创建空的数据模型 |
|
||||||
*/ |
|
||||||
const createEmptyModel = () => ({ |
|
||||||
name: '', |
|
||||||
key: generateUUID(), |
|
||||||
entitys: [createEmptyEntity()] |
|
||||||
}) |
|
||||||
|
|
||||||
const state = reactive<IState>({ |
|
||||||
activeNames: [], |
|
||||||
ruleFormRef: null, |
|
||||||
ruleForm: createEmptyModel() |
|
||||||
}) |
|
||||||
|
|
||||||
/** |
|
||||||
* @param {number} 索引 |
|
||||||
* @description 删除实体项 |
|
||||||
*/ |
|
||||||
const deleteEntityItem = (index: number) => { |
|
||||||
state.ruleForm.entitys.splice(index, 1) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @description 添加实体项 |
|
||||||
*/ |
|
||||||
const addEntityItem = () => { |
|
||||||
state.ruleForm.entitys.push(createEmptyEntity()) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @description 显示添加接口弹窗 |
|
||||||
*/ |
|
||||||
const showModelMoal = () => { |
|
||||||
useModal({ |
|
||||||
title: `${isEdit.value ? '编辑' : '新增'}数据源`, |
|
||||||
content: () => ( |
|
||||||
<ElForm |
|
||||||
model={state.ruleForm} |
|
||||||
ref={(el) => el && (state.ruleFormRef = el)} |
|
||||||
label-width="100px" |
|
||||||
size={'mini'} |
|
||||||
class="demo-ruleForm" |
|
||||||
> |
|
||||||
<ElFormItem |
|
||||||
label="数据源名称" |
|
||||||
prop="name" |
|
||||||
rules={[{ required: true, message: '请输入数据源名称', trigger: 'change' }]} |
|
||||||
> |
|
||||||
<ElInput v-model={state.ruleForm.name} placeholder={'请输入数据源名称'}></ElInput> |
|
||||||
</ElFormItem> |
|
||||||
{state.ruleForm.entitys.map((entity, index) => ( |
|
||||||
<ElCard |
|
||||||
key={index} |
|
||||||
shadow={'hover'} |
|
||||||
v-slots={{ |
|
||||||
header: () => ( |
|
||||||
<div class={'flex justify-between'}> |
|
||||||
<ElFormItem |
|
||||||
label="实体名称" |
|
||||||
prop={`entitys.${index}.name`} |
|
||||||
rules={[{ required: true, message: '请输入实体名称', trigger: 'change' }]} |
|
||||||
showMessage={false} |
|
||||||
class={'w-300px !mb-0'} |
|
||||||
> |
|
||||||
<ElInput v-model={entity.name} placeholder={'请输入实体名称'}></ElInput> |
|
||||||
</ElFormItem> |
|
||||||
<div> |
|
||||||
<ElButton onClick={() => deleteEntityItem(index)} type={'danger'} size={'mini'}> |
|
||||||
删除 |
|
||||||
</ElButton> |
|
||||||
<ElButton onClick={addEntityItem} type={'primary'} size={'mini'}> |
|
||||||
添加 |
|
||||||
</ElButton> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
) |
|
||||||
}} |
|
||||||
> |
|
||||||
<ElFormItem |
|
||||||
label="实体字段" |
|
||||||
prop={`entitys.${index}.field`} |
|
||||||
rules={[{ required: true, message: '请输入实体字段', trigger: 'change' }]} |
|
||||||
> |
|
||||||
<ElInput v-model={entity.field} placeholder={'请输入实体字段'}></ElInput> |
|
||||||
</ElFormItem> |
|
||||||
<ElFormItem |
|
||||||
label="数据类型" |
|
||||||
prop={`entitys.${index}.type`} |
|
||||||
rules={[{ required: true, message: '请输入数据类型', trigger: 'change' }]} |
|
||||||
> |
|
||||||
<ElSelect v-model={entity.type}> |
|
||||||
{fieldTypes.map((typeItem) => ( |
|
||||||
<ElOption |
|
||||||
key={typeItem.value} |
|
||||||
label={typeItem.label} |
|
||||||
value={typeItem.value} |
|
||||||
></ElOption> |
|
||||||
))} |
|
||||||
</ElSelect> |
|
||||||
</ElFormItem> |
|
||||||
<ElFormItem label="默认数据" prop={`entitys.${index}.value`}> |
|
||||||
<ElInput |
|
||||||
v-model={entity.value} |
|
||||||
placeholder={'实体默认数据,不填则为对应类型数据'} |
|
||||||
></ElInput> |
|
||||||
</ElFormItem> |
|
||||||
</ElCard> |
|
||||||
))} |
|
||||||
</ElForm> |
|
||||||
), |
|
||||||
onConfirm: () => { |
|
||||||
return new Promise((resolve, reject) => { |
|
||||||
state.ruleFormRef.validate((valid) => { |
|
||||||
if (valid) { |
|
||||||
if (isEdit.value) { |
|
||||||
updateModel(cloneDeep(state.ruleForm)) |
|
||||||
} else { |
|
||||||
incrementModel(cloneDeep(state.ruleForm)) |
|
||||||
} |
|
||||||
ElMessage.success(`${isEdit.value ? '修改' : '新增'}模型成功!`) |
|
||||||
state.ruleForm = createEmptyModel() |
|
||||||
resolve('submit!') |
|
||||||
} else { |
|
||||||
reject() |
|
||||||
console.log('error submit!!') |
|
||||||
return false |
|
||||||
} |
|
||||||
}) |
|
||||||
}) |
|
||||||
}, |
|
||||||
onCancel: () => (state.ruleForm = createEmptyModel()) |
|
||||||
}) |
|
||||||
} |
|
||||||
/** |
|
||||||
* @description 编辑模型 |
|
||||||
*/ |
|
||||||
const editModel = (model: VisualEditorModel) => { |
|
||||||
console.log(model) |
|
||||||
state.ruleForm = cloneDeep(model) |
|
||||||
showModelMoal() |
|
||||||
} |
|
||||||
</script> |
</script> |
||||||
|
|
||||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||||
.code { |
::v-deep(.el-tabs__header) { |
||||||
padding: 4px 10px; |
position: sticky; |
||||||
font-size: 12px; |
top: 0; |
||||||
line-height: 1.4; |
z-index: 10; |
||||||
} |
|
||||||
|
|
||||||
.model-item-title { |
|
||||||
flex: 1; |
|
||||||
display: flex; |
|
||||||
align-items: center; |
|
||||||
justify-content: space-between; |
|
||||||
|
|
||||||
.model-actions { |
|
||||||
i { |
|
||||||
padding: 6px; |
|
||||||
margin: 0 2px; |
|
||||||
font-weight: bold; |
|
||||||
cursor: pointer; |
|
||||||
border-radius: 2px; |
|
||||||
opacity: 0.7; |
|
||||||
transition: all 0.1s; |
|
||||||
|
|
||||||
&:hover { |
|
||||||
background-color: #f1f1f1; |
|
||||||
opacity: 1; |
|
||||||
} |
|
||||||
|
|
||||||
&.el-icon-delete { |
|
||||||
color: #f44336; |
|
||||||
} |
|
||||||
|
|
||||||
&.el-icon-edit { |
|
||||||
color: #2196f3; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
} |
||||||
</style> |
</style> |
||||||
|
Loading…
Reference in new issue