feat: 🚀 业务组件
This commit is contained in:
32
README.md
32
README.md
@@ -53,3 +53,35 @@ dev(开发分支,用于开环境、解决冲突,来源于其他开发分支)
|
||||
分支拉取请从 pro 分支进行拉取!
|
||||
|
||||
冲突请在 dev 分支解决,解决冲突后再合并测试分支
|
||||
|
||||
# EpicDesigner
|
||||
|
||||
├── index.ts # 入口文件,导出核心组件并初始化
|
||||
├── index.less # 全局样式
|
||||
├── components/ # 核心组件目录
|
||||
│ ├── builder/ # 构建器组件(EBuilder)
|
||||
│ ├── designer/ # 设计器主组件(EDesigner)
|
||||
│ │ └── src/
|
||||
│ │ ├── modules/ # 设计器模块(组件面板、属性面板等)
|
||||
│ ├── node/ # 节点组件(ENode,用于渲染设计元素)
|
||||
│ └── tree/ # 树形组件(ETree,用于展示结构大纲)
|
||||
├── extensions/ # 扩展组件目录
|
||||
│ ├── EInputSize/ # 输入尺寸组件
|
||||
│ ├── MonacoEditor/ # 代码编辑器组件
|
||||
│ ├── Page/ # 页面组件
|
||||
│ └── index.ts # 扩展组件注册逻辑
|
||||
├── ui/ # 基础 UI 组件目录
|
||||
│ ├── dept/ # 部门/人员选择组件
|
||||
│ ├── form/ # 表单组件
|
||||
│ ├── input/ # 输入框组件
|
||||
│ ├── row/ # 栅格布局组件
|
||||
│ ├── upload-file/ # 文件上传组件
|
||||
│ └── upload-image/ # 图片上传组件
|
||||
├── utils/ # 工具函数目录
|
||||
│ ├── manager/ # 管理器(插件、页面状态等)
|
||||
│ ├── common/ # 通用工具(数据处理、DOM 操作等)
|
||||
│ └── shareStore/ # 共享状态管理
|
||||
├── types/ # 类型定义目录
|
||||
│ └── epic-designer.ts # 核心类型(组件结构、设计器状态等)
|
||||
├── theme/ # 主题相关(推测)
|
||||
└── static/ # 静态资源(推测)
|
||||
|
||||
4
src/components.d.ts
vendored
4
src/components.d.ts
vendored
@@ -7,6 +7,7 @@ export {}
|
||||
|
||||
declare module "vue" {
|
||||
export interface GlobalComponents {
|
||||
Dept: typeof import("./components/EpicDesigner/extensions/Dept/index.vue")["default"];
|
||||
ElAside: typeof import("element-plus/es")["ElAside"];
|
||||
ElAutocomplete: typeof import("element-plus/es")["ElAutocomplete"];
|
||||
ElAvatar: typeof import("element-plus/es")["ElAvatar"];
|
||||
@@ -65,5 +66,8 @@ declare module "vue" {
|
||||
IEpSwitchButton: typeof import("~icons/ep/switch-button")["default"];
|
||||
RouterLink: typeof import("vue-router")["RouterLink"];
|
||||
RouterView: typeof import("vue-router")["RouterView"];
|
||||
TagInput: typeof import("./components/EpicDesigner/extensions/TagInput/index.vue")["default"];
|
||||
Test: typeof import("./components/EpicDesigner/businessUi/test/index.vue")["default"];
|
||||
User: typeof import("./components/EpicDesigner/ui/dept/user.vue")["default"];
|
||||
}
|
||||
}
|
||||
|
||||
56
src/components/EpicDesigner/businessUi/Test/index.ts
Normal file
56
src/components/EpicDesigner/businessUi/Test/index.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import $Bus from "@/utils/mittBus";
|
||||
export default {
|
||||
component: () => import("./index.vue"),
|
||||
groupName: "扩展组件", //分组
|
||||
icon: "epic-icon-dept", //图标
|
||||
defaultSchema: {
|
||||
label: "部门", //组件名称
|
||||
type: "epic-dept-select", //组件类型
|
||||
field: "epic-dept-select",
|
||||
input: true,
|
||||
componentProps: {
|
||||
placeholder: "请选择人员"
|
||||
}
|
||||
},
|
||||
config: {
|
||||
attribute: [
|
||||
{
|
||||
label: "字段名",
|
||||
type: "input",
|
||||
field: "field"
|
||||
},
|
||||
{
|
||||
label: "标签文字",
|
||||
type: "input",
|
||||
field: "label"
|
||||
},
|
||||
{
|
||||
label: "占位内容",
|
||||
type: "input",
|
||||
field: "componentProps.placeholder"
|
||||
},
|
||||
{
|
||||
label: "弹窗择值",
|
||||
type: "button",
|
||||
field: "componentProps.openModal",
|
||||
componentProps: {
|
||||
onClick: () => {
|
||||
$Bus.emit("trigger-dept-select-modal");
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// event: [
|
||||
// {
|
||||
// type: "change",
|
||||
// describe: "选择值变化时"
|
||||
// },
|
||||
// // 添加按钮点击事件定义
|
||||
// {
|
||||
// type: "buttonClick", // 自定义事件名,避免冲突
|
||||
// describe: "点击选择人员按钮时"
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
} as any;
|
||||
345
src/components/EpicDesigner/businessUi/Test/index.vue
Normal file
345
src/components/EpicDesigner/businessUi/Test/index.vue
Normal file
@@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<div class="user-select-component">
|
||||
<el-input
|
||||
v-model:value="displayValue"
|
||||
placeholder="请选择组织"
|
||||
:disabled="componentSchema?.disabled"
|
||||
readonly
|
||||
:class="{ selected: isSelected }"
|
||||
/>
|
||||
|
||||
<!-- 用户选择弹窗 -->
|
||||
<el-dialog v-model="isModalVisible" title="选择组织" width="800px" @close="handleModalClose">
|
||||
<div class="user-select-container">
|
||||
<!-- 部门树(带复选框) -->
|
||||
<div class="dept-tree">
|
||||
<el-input
|
||||
v-model="searchQuery"
|
||||
placeholder="输入关键字按enter键查询"
|
||||
class="search-input"
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tree
|
||||
ref="deptTree"
|
||||
:data="deptData"
|
||||
:props="deptProps"
|
||||
:default-expanded-keys="defaultExpandedKeys"
|
||||
:default-checked-keys="selectedDeptIds"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
@check="handleDeptCheck"
|
||||
class="tree"
|
||||
>
|
||||
<template #default="{ node }">
|
||||
<span class="custom-tree-node">
|
||||
<el-icon v-if="node.children && node.children.length">
|
||||
<Fold v-if="node.expanded" />
|
||||
<Expand v-else />
|
||||
</el-icon>
|
||||
{{ node.label }}
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
|
||||
<!-- 已选用户列表 -->
|
||||
<div class="user-transfer">
|
||||
<div
|
||||
style="display: flex; align-items: center; justify-content: space-between; padding: 8px 0"
|
||||
v-for="item in selectDeptData"
|
||||
:key="item.id"
|
||||
>
|
||||
<div>{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
|
||||
import { inject } from "vue";
|
||||
import { Designer } from "epic-designer";
|
||||
import $Bus from "@/utils/mittBus";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { Search, Fold, Expand } from "@element-plus/icons-vue";
|
||||
// import { useMsg } from "@/hooks/useMsg";
|
||||
|
||||
// 接收props
|
||||
const props = defineProps({
|
||||
componentSchema: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
placeholder: "请选择组织",
|
||||
value: null,
|
||||
disabled: false,
|
||||
componentProps: {}
|
||||
})
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
// 事件发射(补充v-model更新事件)
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
// 状态管理
|
||||
const isModalVisible = ref(false);
|
||||
const selectedDept = ref<any>(null);
|
||||
const isSelected = ref(false);
|
||||
const defaultExpandedKeys = ref<number[]>([]); // 用于默认展开部门节点
|
||||
|
||||
// 设计器实例
|
||||
const designer = inject("designer") as Designer;
|
||||
|
||||
// 部门数据
|
||||
const deptData = ref([
|
||||
{
|
||||
id: 1,
|
||||
label: "销售部",
|
||||
type: "deptP", // 明确标记为部门
|
||||
children: [
|
||||
{ id: 2151111, label: "销售部1组", type: "dept" },
|
||||
{ id: 2141111, label: "销售部2组", type: "dept" },
|
||||
{ id: 2131111, label: "销售部3组", type: "dept" },
|
||||
{ id: 2121111, label: "销售部4组", type: "dept" },
|
||||
{ id: 2111111, label: "销售部5组", type: "dept" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: "市场部",
|
||||
type: "deptP",
|
||||
children: [
|
||||
{ id: 2151111, label: "市场部1组", type: "dept" },
|
||||
{ id: 2141111, label: "市场部2组", type: "dept" },
|
||||
{ id: 2131111, label: "市场部3组", type: "dept" },
|
||||
{ id: 2121111, label: "市场部4组", type: "dept" },
|
||||
{ id: 2111111, label: "市场部5组", type: "dept" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
label: "开发部",
|
||||
type: "deptP",
|
||||
children: [
|
||||
{ id: 215111, label: "开发部1组", type: "dept" },
|
||||
{ id: 214111, label: "开发部2组", type: "dept" },
|
||||
{ id: 213111, label: "开发部3组", type: "dept" },
|
||||
{ id: 212111, label: "开发部4组", type: "dept" },
|
||||
{ id: 211111, label: "开发部5组", type: "dept" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
label: "运维部",
|
||||
type: "deptP",
|
||||
children: [
|
||||
{ id: 21511, label: "运维部1组", type: "dept" },
|
||||
{ id: 21411, label: "运维部2组", type: "dept" },
|
||||
{ id: 21311, label: "运维部3组", type: "dept" },
|
||||
{ id: 21211, label: "运维部4组", type: "dept" },
|
||||
{ id: 21111, label: "运维部5组", type: "dept" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
label: "财务部",
|
||||
type: "deptP",
|
||||
children: [
|
||||
{ id: 2151, label: "财务部1组", type: "dept" },
|
||||
{ id: 2141, label: "财务部2组", type: "dept" },
|
||||
{ id: 2131, label: "财务部3组", type: "dept" },
|
||||
{ id: 2121, label: "财务部4组", type: "dept" },
|
||||
{ id: 2111, label: "财务部5组", type: "dept" }
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
id: 14,
|
||||
label: "IT部",
|
||||
type: "deptP",
|
||||
children: [
|
||||
{ id: 215, label: "IT部1组", type: "dept" },
|
||||
{ id: 214, label: "IT部2组", type: "dept" },
|
||||
{ id: 213, label: "IT部3组", type: "dept" },
|
||||
{ id: 212, label: "IT部4组", type: "dept" },
|
||||
{ id: 211, label: "IT部5组", type: "dept" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
// 部门树配置
|
||||
const deptProps = {
|
||||
children: "children",
|
||||
label: "label"
|
||||
};
|
||||
|
||||
// 搜索查询
|
||||
const searchQuery = ref("");
|
||||
const deptTree = ref<any>(null);
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = () => {
|
||||
deptTree.value?.filter(searchQuery.value);
|
||||
};
|
||||
|
||||
// 关闭弹窗回调(清空临时数据)
|
||||
const handleModalClose = () => {
|
||||
selectDeptData.value = [];
|
||||
selectedDeptIds.value = [];
|
||||
};
|
||||
|
||||
// 部门树复选事件(核心修改)
|
||||
const handleDeptCheck = (data: any, checked: any) => {
|
||||
selectDeptData.value = checked.checkedNodes.filter((item: any) => {
|
||||
return item.type === "dept";
|
||||
});
|
||||
selectDeptData.value.forEach((item: any) => {
|
||||
selectedDeptIds.value.push(item.id);
|
||||
});
|
||||
};
|
||||
|
||||
//选择的部门
|
||||
const selectDeptData = ref<any[]>([]);
|
||||
const selectedDeptIds = ref<number[]>([]);
|
||||
|
||||
// 确认选择
|
||||
const handleConfirm = () => {
|
||||
if (selectedDeptIds.value.length === 0) {
|
||||
ElMessage.warning("请选择至少一个部门");
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理选择的组织
|
||||
const selectedDepts: any = selectDeptData.value;
|
||||
// 拼接用户名
|
||||
const deptNames: any = selectedDepts.map((item: any) => item.label).join(",");
|
||||
selectedDept.value = deptNames;
|
||||
|
||||
// 更新设计器数据(双向同步)
|
||||
if (designer?.state?.checkedNode) {
|
||||
designer.state.checkedNode.componentProps.defaultValue = deptNames;
|
||||
designer.state.checkedNode.componentProps.defaultValueClone = selectedDepts;
|
||||
}
|
||||
// 触发v-model更新(确保表单能获取到值)
|
||||
emit("update:modelValue", deptNames);
|
||||
|
||||
isModalVisible.value = false;
|
||||
// 清空临时数据,避免下次打开残留
|
||||
selectDeptData.value = [];
|
||||
selectedDeptIds.value = [];
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const handleClose = () => {
|
||||
isModalVisible.value = false;
|
||||
selectDeptData.value = [];
|
||||
selectedDeptIds.value = [];
|
||||
};
|
||||
|
||||
// 显示在输入框的文本
|
||||
const displayValue = computed(() => {
|
||||
return selectedDept.value || "";
|
||||
});
|
||||
console.log(designer?.state, "= designer?.state=");
|
||||
// 监听设计器选中状态变化
|
||||
watch(
|
||||
() => designer?.state.checkedNode?.field,
|
||||
newField => {
|
||||
isSelected.value = newField === props.componentSchema.field;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 监听modelValue变化(外部更新时同步)
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
newValue => {
|
||||
selectedDept.value = newValue;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 监听按钮触发的全局事件
|
||||
const handleGlobalTrigger = () => {
|
||||
if (isSelected.value && !props.componentSchema.disabled) {
|
||||
isModalVisible.value = true;
|
||||
// 初始化已选数据(从设计器中读取)
|
||||
|
||||
const savedUsers = designer?.state?.checkedNode?.componentProps?.defaultValueClone || [];
|
||||
if (savedUsers.length) {
|
||||
selectDeptData.value = savedUsers;
|
||||
selectedDeptIds.value = savedUsers.map((item: any) => item.id);
|
||||
// 默认展开包含已选用户的部门(优化体验)
|
||||
const deptIds = selectDeptData.value.map((dept: any) => dept.id);
|
||||
defaultExpandedKeys.value = deptIds;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 事件监听
|
||||
onMounted(() => {
|
||||
$Bus.on("trigger-dept-select-modal", handleGlobalTrigger);
|
||||
});
|
||||
|
||||
// 清理事件监听
|
||||
onUnmounted(() => {
|
||||
$Bus.off("trigger-dept-select-modal", handleGlobalTrigger);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.user-select-component {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// .el-input__inner {
|
||||
// cursor: pointer;
|
||||
// }
|
||||
|
||||
/* 选中状态样式 */
|
||||
.selected {
|
||||
// .el-input__inner {
|
||||
// border-color: red;
|
||||
// box-shadow: 0 0 0 2px rgb(64 158 255 / 20%);
|
||||
// }
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
.user-select-container {
|
||||
display: flex;
|
||||
height: 400px;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
border: 1px solid #eeeeee;
|
||||
}
|
||||
.dept-tree {
|
||||
width: 300px;
|
||||
padding-right: 20px;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #eeeeeeee;
|
||||
}
|
||||
.search-input {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.user-transfer {
|
||||
flex: 1;
|
||||
padding-left: 20px;
|
||||
overflow-y: auto; /* 增加滚动,避免用户过多溢出 */
|
||||
}
|
||||
</style>
|
||||
@@ -42,7 +42,6 @@ import EIcon from "../../../../icon";
|
||||
const hideRightMain = ref(false);
|
||||
|
||||
const rightSidebars = computed(() => {
|
||||
console.log(pluginManager.viewsContainers.rightSidebars.value);
|
||||
return pluginManager.viewsContainers.rightSidebars.value.filter(item => item.visible);
|
||||
});
|
||||
|
||||
|
||||
@@ -313,7 +313,6 @@ async function initComponent() {
|
||||
|
||||
return;
|
||||
}
|
||||
console.log(innerSchema.type, "=innerSchema.type=");
|
||||
// 内置组件
|
||||
const cmp = pluginManager.getComponent(innerSchema.type);
|
||||
// 内部不存在组件
|
||||
|
||||
@@ -72,7 +72,7 @@ const getTreeData = computed({
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(getTreeData, "=getTreeData=");
|
||||
/**
|
||||
* 通过label 过滤节点
|
||||
* @param tree 节点树
|
||||
|
||||
@@ -50,6 +50,7 @@ function handleAdd() {
|
||||
label: "",
|
||||
value: ""
|
||||
};
|
||||
console.log("1232323");
|
||||
modelValueComputed.value?.push(option);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -2,6 +2,10 @@ import { type PluginManager } from "@/components/EpicDesigner/utils";
|
||||
import MonacoEditor from "./MonacoEditor";
|
||||
import Page from "./Page";
|
||||
|
||||
import Test from "../businessUi/Test";
|
||||
|
||||
// import TagInput from "./TagInput/index.vue";
|
||||
// 1. 注册【配置类组件】:用于属性面板,不渲染到画布
|
||||
export function setupComponent(pluginManager: PluginManager): void {
|
||||
pluginManager.component("EInputSize", async () => await import("./EInputSize/index.vue"));
|
||||
pluginManager.component("EColEditor", async () => await import("./EColEditor/index.vue"));
|
||||
@@ -9,7 +13,6 @@ export function setupComponent(pluginManager: PluginManager): void {
|
||||
pluginManager.component("ERuleEditor", async () => await import("./ERuleEditor/index.vue"));
|
||||
pluginManager.component("EOptionsEditor", async () => await import("./EOptionsEditor/index.vue"));
|
||||
pluginManager.component("ENode", async () => await import("../components/node"));
|
||||
|
||||
// 左侧菜单初始化
|
||||
pluginManager.registerActivitybar({
|
||||
id: "component_view",
|
||||
@@ -54,8 +57,7 @@ export function setupComponent(pluginManager: PluginManager): void {
|
||||
// component: async () => await import("../components/designer/src/modules/attributeView/dataView.vue")
|
||||
// });
|
||||
|
||||
const componentArray = [MonacoEditor, Page];
|
||||
console.log(Page, "=============>");
|
||||
const componentArray = [MonacoEditor, Page, Test];
|
||||
componentArray.forEach(item => {
|
||||
pluginManager.registerComponent(item);
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ export default defineComponent({
|
||||
const componentProps: Record<string, any> = {
|
||||
...props.componentSchema?.componentProps
|
||||
};
|
||||
|
||||
console.log(componentProps, "=========componentProps============");
|
||||
return h(ElButton, componentProps, {
|
||||
default: () => renderSlot(slots, "default", {}, () => [props.componentSchema?.label])
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ export default defineComponent({
|
||||
header: props.componentSchema?.label ?? ""
|
||||
} as ComponentSchema;
|
||||
console.log(attrs);
|
||||
|
||||
const children = componentSchema.children ?? [];
|
||||
delete componentSchema.children;
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ import Modal from "./modal";
|
||||
import { ElFormItem, ElTabs, ElTabPane, ElCollapse, ElCollapseItem } from "element-plus";
|
||||
|
||||
export function setupElementPlus(pluginManager: PluginManager = pManager): void {
|
||||
// console.log(pluginManager, "=pluginManager=");
|
||||
console.log(1232323);
|
||||
pluginManager.component("FormItem", ElFormItem);
|
||||
pluginManager.component("Tabs", ElTabs);
|
||||
pluginManager.component("TabPane", ElTabPane);
|
||||
@@ -63,7 +61,6 @@ export function setupElementPlus(pluginManager: PluginManager = pManager): void
|
||||
pluginManager.registerComponent(item);
|
||||
pluginManager.addBaseComponentTypes(item.defaultSchema.type);
|
||||
});
|
||||
console.log(1232323);
|
||||
// ui初始化完成。
|
||||
pluginManager.setInitialized(true);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,15 @@ const pageSchema = ref({
|
||||
label: "输入框",
|
||||
type: "input",
|
||||
id: "input_4wujh0i3"
|
||||
},
|
||||
{
|
||||
id: "epic-dept-select_qwefi7l4",
|
||||
type: "input",
|
||||
label: "人员选择11",
|
||||
componentProps: {
|
||||
placeholder: "请输入"
|
||||
},
|
||||
field: "epic-dept-select_qwefi7l4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user