parent
							
								
									f387fa8993
								
							
						
					
					
						commit
						c543fe894a
					
				| @ -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,103 @@ | ||||
| /* | ||||
|  * @Author: 卜启缘 | ||||
|  * @Date: 2021-06-24 23:19:48 | ||||
|  * @LastEditTime: 2021-06-24 23:35:57 | ||||
|  * @LastEditors: 卜启缘 | ||||
|  * @Description: axios简单的封装 | ||||
|  * @FilePath: \vite-vue3-lowcode\src\utils\http\request.ts | ||||
|  */ | ||||
| import axios, { AxiosRequestConfig } from 'axios' | ||||
| import qs from 'qs' | ||||
| import store from '@/store' | ||||
| import { Toast } from 'vant' | ||||
| import router from '@/router' | ||||
| import { ContentTypeEnum } from './httpEnum' | ||||
| 
 | ||||
| // create an axios instance
 | ||||
| const service = axios.create({ | ||||
|   baseURL: import.meta.env.VITE_API_URL as string, // url = base api url + request url
 | ||||
|   withCredentials: true, // send cookies when cross-domain requests
 | ||||
|   timeout: 10000 // request timeout
 | ||||
| }) | ||||
| interface CustomAxiosRequestConfig extends AxiosRequestConfig { | ||||
|   hideLoading?: boolean | ||||
| } | ||||
| 
 | ||||
| interface BaseResponse<T = any> { | ||||
|   code: number | ||||
|   data: T | ||||
|   msg: string | ||||
| } | ||||
| 
 | ||||
| // request拦截器 request interceptor
 | ||||
| service.interceptors.request.use( | ||||
|   (config: CustomAxiosRequestConfig) => { | ||||
|     // 不传递默认开启loading
 | ||||
|     if (!config.hideLoading) { | ||||
|       // loading
 | ||||
|       Toast.loading({ | ||||
|         forbidClick: true | ||||
|       }) | ||||
|     } | ||||
|     console.log(store.getters, "store.getters['user']") | ||||
|     if (store.getters['user/token']) { | ||||
|       config.headers['X-Token'] = store.getters['user/token'] | ||||
|     } | ||||
|     const contentType = config.headers['content-type'] || config.headers['Content-Type'] | ||||
|     const data = config.data | ||||
|     if (config.method?.toLocaleUpperCase() == 'POST' && data) { | ||||
|       if (ContentTypeEnum.FORM_DATA == contentType) { | ||||
|         const fd = new FormData() | ||||
|         Object.keys(data).forEach((key) => fd.append(key, data[key])) | ||||
|         config.data = fd | ||||
|       } else if (ContentTypeEnum.FORM_URLENCODED == contentType) { | ||||
|         config.data = qs.stringify(config.data) | ||||
|       } | ||||
|     } | ||||
|     return config | ||||
|   }, | ||||
|   (error) => { | ||||
|     // do something with request error
 | ||||
|     console.log(error) // for debug
 | ||||
|     return Promise.reject(error) | ||||
|   } | ||||
| ) | ||||
| // respone拦截器
 | ||||
| service.interceptors.response.use( | ||||
|   (response) => { | ||||
|     Toast.clear() | ||||
|     const res = response.data | ||||
|     if (res.code && res.code !== 0) { | ||||
|       // 登录超时,重新登录
 | ||||
|       if (res.code === 401) { | ||||
|         // store.dispatch('FedLogOut').then(() => {
 | ||||
|         //   location.reload()
 | ||||
|         // })
 | ||||
|         router.replace('/error') | ||||
|       } else { | ||||
|         Toast(res.msg || '服务器访问出错了~') | ||||
|       } | ||||
|       return Promise.reject(res || 'error') | ||||
|     } else { | ||||
|       return Promise.resolve(response) | ||||
|     } | ||||
|   }, | ||||
|   (error: Error) => { | ||||
|     if (error.message?.includes('timeout')) { | ||||
|       Toast('请求超时!') | ||||
|     } | ||||
|     console.log('err' + error) // for debug
 | ||||
|     return Promise.reject(error) | ||||
|   } | ||||
| ) | ||||
| 
 | ||||
| const request = <T = any>(config: CustomAxiosRequestConfig): Promise<BaseResponse<T>> => { | ||||
|   return new Promise((resolve, reject) => { | ||||
|     service | ||||
|       .request<BaseResponse<T>>(config) | ||||
|       .then((res) => resolve(res.data)) | ||||
|       .catch((err) => reject(err)) | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| export default request | ||||
| @ -0,0 +1,255 @@ | ||||
| <!-- | ||||
|  * @Author: 卜启缘 | ||||
|  * @Date: 2021-06-24 18:36:03 | ||||
|  * @LastEditTime: 2021-06-25 21:38:33 | ||||
|  * @LastEditors: 卜启缘 | ||||
|  * @Description: 数据源管理 | ||||
|  * @FilePath: \vite-vue3-lowcode\src\visual-editor\components\left-aside\components\data-source\index.vue | ||||
| --> | ||||
| <template> | ||||
|   <el-button class="!my-10px" type="primary" size="small" @click="showModelMoal">添加</el-button> | ||||
|   <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> | ||||
|               <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> | ||||
| 
 | ||||
| <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 = () => ({ 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> | ||||
| 
 | ||||
| <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,11 @@ | ||||
| /* | ||||
|  * @Author: 卜启缘 | ||||
|  * @Date: 2021-06-12 22:18:48 | ||||
|  * @LastEditTime: 2021-06-24 18:31:54 | ||||
|  * @LastEditors: 卜启缘 | ||||
|  * @Description: 统一导出组件 | ||||
|  * @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\attr-editor\components\index.ts | ||||
|  */ | ||||
| 
 | ||||
| export { CrossSortableOptionsEditor } from './cross-sortable-options-editor/cross-sortable-options-editor' | ||||
| export { TablePropEditor } from './table-prop-editor/table-prop-editor' | ||||
| @ -1,6 +1,6 @@ | ||||
| import { VisualEditorProps } from '../../../../visual-editor.props' | ||||
| import { VisualEditorProps } from '@/visual-editor/visual-editor.props' | ||||
| import { defineComponent, getCurrentInstance, onMounted, PropType, reactive, createApp } from 'vue' | ||||
| import { defer } from '../../../../utils/defer' | ||||
| import { defer } from '@/visual-editor/utils/defer' | ||||
| import { ElButton, ElDialog, ElTable, ElTableColumn, ElInput } from 'element-plus' | ||||
| import { cloneDeep } from 'lodash' | ||||
| 
 | ||||
| @ -0,0 +1,19 @@ | ||||
| /* | ||||
|  * @Author: 卜启缘 | ||||
|  * @Date: 2021-06-24 11:01:45 | ||||
|  * @LastEditTime: 2021-06-24 11:12:56 | ||||
|  * @LastEditors: 卜启缘 | ||||
|  * @Description: 事件-动作 | ||||
|  * @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\event-action\index.tsx | ||||
|  */ | ||||
| import { defineComponent } from 'vue' | ||||
| 
 | ||||
| export const EventAction = defineComponent({ | ||||
|   setup() { | ||||
|     return () => ( | ||||
|       <> | ||||
|         <div>事件流</div> | ||||
|       </> | ||||
|     ) | ||||
|   } | ||||
| }) | ||||
| @ -1,14 +1,13 @@ | ||||
| /* | ||||
|  * @Author: 卜启缘 | ||||
|  * @Date: 2021-06-12 22:18:48 | ||||
|  * @LastEditTime: 2021-06-23 22:17:38 | ||||
|  * @LastEditTime: 2021-06-24 18:30:44 | ||||
|  * @LastEditors: 卜启缘 | ||||
|  * @Description: | ||||
|  * @Description: 统一导出组件 | ||||
|  * @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\index.ts | ||||
|  */ | ||||
| 
 | ||||
| export { TablePropEditor } from './table-prop-editor/table-prop-editor' | ||||
| export { AttrEditor } from './attr-editor/AttrEditor' | ||||
| export { Animate } from './animate/Animate' | ||||
| export { CrossSortableOptionsEditor } from './cross-sortable-options-editor/cross-sortable-options-editor' | ||||
| export { PageSetting } from './page-setting/pageSetting' | ||||
| export { EventAction } from './event-action/' | ||||
|  | ||||
| @ -1,8 +1,30 @@ | ||||
| /* | ||||
|  * @Author: 卜启缘 | ||||
|  * @Date: 2021-06-01 09:45:21 | ||||
|  * @LastEditTime: 2021-06-24 21:57:31 | ||||
|  * @LastEditors: 卜启缘 | ||||
|  * @Description: 公用的工具函数 | ||||
|  * @FilePath: \vite-vue3-lowcode\src\visual-editor\utils\index.ts | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * @name: index | ||||
|  * @author: 卜启缘 | ||||
|  * @date: 2021/5/6 0:04 | ||||
|  * @description:index | ||||
|  * @update: 2021/5/6 0:04 | ||||
|  * @description 部署应用时的基本URL | ||||
|  */ | ||||
| export const BASE_URL = import.meta.env.BASE_URL | ||||
| 
 | ||||
| /** | ||||
|  * @description 生成UUID | ||||
|  * @param {boolean} [noSymbol=false] 是否需要 - 分隔符 | ||||
|  * @returns {string} | ||||
|  */ | ||||
| export function generateUUID(noSymbol = false) { | ||||
|   let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||||
|     const r = (Math.random() * 16) | 0, | ||||
|       v = c == 'x' ? r : (r & 0x3) | 0x8 | ||||
|     return v.toString(16) | ||||
|   }) | ||||
|   if (noSymbol) { | ||||
|     uuid = uuid.replace(/-/g, '') | ||||
|   } | ||||
|   return uuid | ||||
| } | ||||
|  | ||||
					Loading…
					
					
				
		Reference in new issue