FLEX:module和components用法上的区别

很多人分不清module和components具体有什么区别,今天就来讲一下
首先,module和components都可以把一个比较大的程序分成几个“部分”,这样每个部分可以单独开发和测试,更符合软件工程的“分而治之”思 想,也会大大减少开发的难度和尽可能的减少认为错误,而且还都可以复用。


module是可以动态加载和卸载的,也就是说它不是编译到主程序中的,可以做到想要的时候拿来,不想要的时候卸掉。因为module是预先编译的,但是 module不可以独立执行,一定要嵌到一个Application中才可以运行。注意:用它的时候用的是.swf,也就是编译之后的代码。简单的应用可 以用<mx:ModuleLoader>标签,在里面把url属性赋值就可以了,例如url="******.swf",复杂一点的就要用 moduleManager类来进行管理和控制了,跟其他的Manager是差不多,详细的根据Api吧。


component是静态的,它的用于自定义一个组件(类似于Button、dataGrid等),只要包含了定义好component的包,在Flex Builder中的Design模式下就可以像拖拽Button一样来应用它,会非常的方便。它在应用的时候相当于把自己嵌进Application中, 要随着Application进行编译。这从某种意义上说有点像import和include的区别。在代码应用上,在Application中的 xmlns属性中引入定义好的component所在的包就可以了,例如:xmlns:something="components.*",标签中可以这 样引用<something:yourComponent>,yourComponent就像一个自定义的button。


建立module和component只需要在IDE中右键,其余的就自然明白了。下面趁热打铁,说一说通信上的事情。
首先,调用主程序的方法或者属性,都可以用parentApplication.sth来调用。理论上来说只要module可以调用主程序中的方法,那么 module和Application之间的所有数据交互任务就都可以完成了,只是实现起来会比较麻烦,我这样做过,包括module和module之间 通信,可以通过Application作为桥。而更为高级一点的技术是通过自定义事件和接口契约来进行更为随意的通信。比如说Application要和 Module通信,那么首先要明确module要提供出什么方法A(),然后定义一个接口,这个接口要继承IEventDispatcher,然后此接口 声明module想要提供的方法A(),之后module要实现这个接口,把A()方法实现,还要声明一个特定类型的事件,然后Application中 声明一个ModuleLoader,要通信的Module跟其关联,然后通过child as 接口的名字获得接口就可以监听特定事件,也可通过那个接口调用Module的方法了。听起来挺复杂的,其实确实挺复杂的。要比较了解接口和自定义事件,要 不挺难整明白到底怎么回事的。总之就是通过接口
和自定义事件来完成很随意的通信,不过实现起来还真是挺有难度的。
上面听起来挺蒙的,缕一下,就是通过一个接口(Module实现它,主程序通过Module获得它),来监听Module的事件和调用Module的方 法。对,就是这个意思。


通信上总结一下,Module和component都是可以parentApplication来调用主程序的方法和属性的;component可以指定 id,然后Application可以通过id直接调用component中的对外方法;Module貌似是行不通的,我没实现过,有人弄通了指教一下 哈;通过自定义事件是可以进行比较方便的通信,component和Module都可以;如果中间加上接口的话会更好,结构更清晰,解耦性更好,但同时实 现起来也相对麻烦些。
如下述代码1,是一个弹窗的代码页面(如图1所示),调整一下左侧的"SQL查询",当分辨率高变形的有点厉害了,请参照代码2中的样式所示,请优化"SQL查询"的样式,并完整的写出修改后的代码(不改变代码1中的功能,即"sql查询"框中能够输入sql语句不变) ### 代码1:src\views\handle\dataset\add\AddView.vue ```vue <template> <div class="sql-dataset-container"> <el-container class="layout-container"> <!-- 右侧SQL配置表单 --> <el-aside width="40%" class="config-aside"> <div class="config-section"> <div class="section-header"> <h2><el-icon><setting /></el-icon> SQL数据集配置</h2> </div> <el-form :model="form" :rules="rules" label-position="top" class="config-form" ref="formRef" > <!-- 数据集名称 - 修改为行内布局 --> <el-form-item v-if="props.id===''" label="数据集名称" prop="name" class="form-item-card inline-form-item"> <el-input v-model="form.name" placeholder="例如: 用户行为分析数据集" size="large" :prefix-icon="Document" /> </el-form-item> <!-- SQL编辑器 --> <el-form-item label="SQL查询" prop="sql" class="form-item-card sql-editor-item"> <div class="sql-editor-container"> <VAceEditor v-model:value="form.sql" lang="sql" theme="github" style="height: 300px; width: 100%" :options="{ enableBasicAutocompletion: true, enableLiveAutocompletion: true, highlightActiveLine: true, showLineNumbers: true, tabSize: 2, }" /> <div class="sql-tips"> <el-tag type="info" size="small"> <el-icon><info-filled /></el-icon> 提示: 请确保SQL语法正确且符合数据源规范 </el-tag> </div> </div> </el-form-item> </el-form> </div> </el-aside> <!-- 左侧数据预览 --> <el-main class="preview-main"> <div class="preview-section"> <div class="section-header"> <div class="header-left"> <h2><el-icon><data-line /></el-icon> 数据预览</h2> </div> <div class="header-right"> <el-button type="warning" plain size="small" @click="emit('cancel')" :icon="Close">取消</el-button> <el-button type="success" plain size="small" :disabled="!form.sql" @click="fetchPreviewData" :icon="Refresh">执行SQL预览</el-button> <el-button type="primary" plain size="small" @click="handleSave" :icon="Check">保存</el-button> </div> </div> <div class="preview-content"> <zr-table :tableModule="tableModule" /> </div> </div> </el-main> </el-container> </div> </template> <script setup> import {ref, reactive, onMounted, getCurrentInstance} from 'vue' import { VAceEditor } from 'vue3-ace-editor' import '@/components/CodeEdit/ace-config.js' import { Setting, DataLine, Document, Refresh, Check, Close, InfoFilled, Edit, Download } from '@element-plus/icons-vue' import ZrTable from "@/components/ZrTable/index.vue"; import {buildFilterSos} from "@/components/ZrTable/table.js"; import { handleDataSetCreate, handleDataSetGet, handleDataSetPreviewData, handleDataSetReconstruct } from "@/api/handle/dataset.js"; const { proxy } = getCurrentInstance() const props = defineProps({ sceneId:{ type: String, default: '', }, id:{ type: String, default: '', } }) const emit = defineEmits(['saved', 'cancel']) const form = ref({ id:props.id, name: '', sceneId:props.sceneId, type:'view', sql: '', info:'', }) //数据预览 const columnsData= ref([]) const queryData = ref([]) const state = reactive({ columns: columnsData, // 表格配置 query: queryData, // 查询条件配置 queryForm: {}, // 查询form表单 loading: false, // 加载状态 dataList: [], // 列表数据 pages:false, }) const { loading, dataList, columns, pages, query, queryForm } = toRefs(state) const formRef = ref(null) // 预览数据 // 传给子组件的 const tableModule = ref({ callback: fetchPreviewData, // 回调,子组件中可以看到很多调用callback的,这里对应的是获取列表数据的方法 // 以下不说了,上面都给解释了 queryForm, columns, dataList, loading, pages, query, }) const rules = reactive({ name: [ { required: true, message: '请输入数据集名称', trigger: 'blur' }, { max: 50, message: '名称长度不能超过50个字符', trigger: 'blur' } ], sql: [ { required: true, message: '请输入SQL查询语句', trigger: 'blur' }, { validator: (rule, value, callback) => { if (!value || !value.trim()) { callback(new Error('SQL不能为空')) } else if (!value.toLowerCase().includes('select')) { callback(new Error('SQL必须是SELECT查询语句')) } else { callback() } }, trigger: 'blur' } ] }) async function fetchPreviewData() { state.loading = true const valid = await formRef.value.validate() if (!valid) { proxy.$modal.msgError("校验错误,请修改好再次执行") return } // 掉自己的接口,切勿复制粘贴 handleDataSetPreviewData({ sql:form.value.sql }).then((res) => { if (res.success) { if(res.data.pageData.length>0){ for (let key in res.data.pageData[0]) { columnsData.value.push({prop: key, label:key, align: 'center',show:1}) } } state.dataList = res.data.pageData proxy.$modal.msgSuccess('SQL执行成功') } else { proxy.$modal.msgError(res.message) } }) state.loading = false } const handleSave = async () => { const valid = await formRef.value.validate() if (!valid) return form.value.info=JSON.stringify({ sql:form.value.sql }) const method=form.value.id===""?handleDataSetCreate:handleDataSetReconstruct method(form.value).then((res) => { if (res.success) { emit('saved',{id: res.data }) } else { proxy.$modal.msgError(res.message) } }) } watch(() => props.id, (newid, oldid) => { if (newid !== oldid && newid!=="") { // 引用变化时触发 handleDataSetGet(newid).then((res) => { if(res.success){ form.value.sql=JSON.parse(res.data.info)?.sql; } }) } }, { immediate: true } // 注意:不要用 deep: true ); </script> <style scoped lang="scss"> .sql-dataset-container { height: calc(90vh - 60px); padding: 20px; background-color: #f5f7fa; } .layout-container { height: 100%; background: #fff; border-radius: 12px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); overflow: hidden; display: flex; } .config-aside { background: #f9fafc; border-right: 1px solid var(--el-border-color-light); padding: 24px; display: flex; flex-direction: column; } .preview-main { padding: 24px; background: #fff; display: flex; flex-direction: column; } .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; h2 { margin: 0; color: var(--el-text-color-primary); font-weight: 600; font-size: 18px; display: flex; align-items: center; gap: 8px; } .header-left, .header-right { display: flex; align-items: center; } } .preview-content { flex: 1; border: 1px solid var(--el-border-color-light); border-radius: 8px; overflow: hidden; display: flex; flex-direction: column; } .config-form { flex: 1; display: flex; flex-direction: column; :deep(.el-form-item) { margin-bottom: 20px; &.inline-form-item { :deep(.el-form-item__label) { display: inline-flex; align-items: center; width: auto; margin-right: 12px; padding-bottom: 0; } :deep(.el-form-item__content) { display: inline-flex; flex: 1; } } .el-form-item__label { font-weight: 500; padding-bottom: 8px; color: var(--el-text-color-regular); font-size: 14px; } } } .form-item-card { background: #fff; padding: 16px; border-radius: 8px; border-left: 3px solid var(--el-color-primary); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); } .sql-editor-item { flex: 1; display: flex; flex-direction: column; :deep(.el-form-item__content) { flex: 1; display: flex; flex-direction: column; } } .sql-editor-container { flex: 1; display: flex; flex-direction: column; border: 1px solid var(--el-border-color-light); border-radius: 4px; overflow: hidden; } .sql-tips { padding: 8px 12px; background: var(--el-color-info-light-9); border-top: 1px solid var(--el-border-color-light); .el-tag { width: 100%; justify-content: flex-start; .el-icon { margin-right: 6px; } } } .form-actions { margin-top: 24px; padding-top: 16px; display: flex; justify-content: flex-end; gap: 16px; border-top: 1px dashed var(--el-border-color); } .refresh-btn { :deep(.el-icon) { margin-right: 6px; } } @media (max-width: 992px) { .layout-container { flex-direction: column; } .config-aside { width: 100% !important; border-right: none; border-bottom: 1px solid var(--el-border-color-light); } } </style> ``` ### 代码2:src\views\handle\dataset\add\AddView.vue ```vue <template> <div class="sql-dataset-container"> <el-container class="layout-container"> <!-- 左侧:数据集名称SQL查询 --> <el-aside class="config-aside"> <div class="config-section"> <div class="section-header"> <h2><el-icon><setting /></el-icon> SQL数据集配置</h2> </div> <el-form :model="form" :rules="rules" label-position="top" class="config-form" ref="formRef" > <!-- 数据集名称 --> <el-form-item v-if="props.id === ''" label="数据集名称" prop="name" class="form-item-card inline-form-item"> <el-input v-model="form.name" placeholder="例如: 用户行为分析数据集" size="large" :prefix-icon="Document" /> </el-form-item> <!-- SQL编辑器 --> <el-form-item label="SQL查询" prop="sql" class="form-item-card sql-editor-item"> <div class="sql-editor-container"> <VAceEditor v-model:value="form.sql" lang="sql" theme="github" style="height: 100%; width: 100%" :options="{ enableBasicAutocompletion: true, enableLiveAutocompletion: true, highlightActiveLine: true, showLineNumbers: true, tabSize: 2, }" /> <div class="sql-tips"> <el-tag type="info" size="small"> <el-icon><info-filled /></el-icon> 提示: 请确保SQL语法正确且符合数据源规范 </el-tag> </div> </div> </el-form-item> </el-form> </div> </el-aside> <!-- 右侧:数据预览 --> <el-main class="preview-main"> <div class="preview-section"> <div class="section-header"> <div class="header-left"> <h2><el-icon><data-line /></el-icon> 数据预览</h2> </div> <div class="header-right"> <el-button type="warning" plain size="small" @click="emit('cancel')" :icon="Close">取消</el-button> <el-button type="success" plain size="small" :disabled="!form.sql" @click="fetchPreviewData" :icon="Refresh">执行SQL预览</el-button> <el-button type="primary" plain size="small" @click="handleSave" :icon="Check">保存</el-button> </div> </div> <div class="preview-content"> <zr-table :tableModule="tableModule" /> </div> </div> </el-main> </el-container> </div> </template> <script setup> import {ref, reactive, onMounted, getCurrentInstance, watch} from 'vue' import { VAceEditor } from 'vue3-ace-editor' import '@/components/CodeEdit/ace-config.js' import { Setting, DataLine, Document, Refresh, Check, Close, InfoFilled, Edit, Download } from '@element-plus/icons-vue' import ZrTable from "@/components/ZrTable/index.vue"; import {buildFilterSos} from "@/components/ZrTable/table.js"; import { handleDataSetCreate, handleDataSetGet, handleDataSetPreviewData, handleDataSetReconstruct } from "@/api/handle/dataset.js"; const { proxy } = getCurrentInstance() const props = defineProps({ sceneId:{ type: String, default: '', }, id:{ type: String, default: '', } }) const emit = defineEmits(['saved', 'cancel']) const form = ref({ id:props.id, name: '', sceneId:props.sceneId, type:'view', sql: '', info:'', }) //数据预览 const columnsData= ref([]) const queryData = ref([]) const state = reactive({ columns: columnsData, // 表格配置 query: queryData, // 查询条件配置 queryForm: {}, // 查询form表单 loading: false, // 加载状态 dataList: [], // 列表数据 pages:false, }) const { loading, dataList, columns, pages, query, queryForm } = toRefs(state) const formRef = ref(null) // 预览数据 // 传给子组件的 const tableModule = ref({ callback: fetchPreviewData, // 回调,子组件中可以看到很多调用callback的,这里对应的是获取列表数据的方法 // 以下不说了,上面都给解释了 queryForm, columns, dataList, loading, pages, query, }) const rules = reactive({ name: [ { required: true, message: '请输入数据集名称', trigger: 'blur' }, { max: 50, message: '名称长度不能超过50个字符', trigger: 'blur' } ], sql: [ { required: true, message: '请输入SQL查询语句', trigger: 'blur' }, { validator: (rule, value, callback) => { if (!value || !value.trim()) { callback(new Error('SQL不能为空')) } else if (!value.toLowerCase().includes('select')) { callback(new Error('SQL必须是SELECT查询语句')) } else { callback() } }, trigger: 'blur' } ] }) async function fetchPreviewData() { state.loading = true const valid = await formRef.value.validate() if (!valid) { proxy.$modal.msgError("校验错误,请修改好再次执行") return } // 掉自己的接口,切勿复制粘贴 handleDataSetPreviewData({ sql:form.value.sql }).then((res) => { if (res.success) { columnsData.value = []; // 重置列数据 if(res.data.pageData.length>0){ for (let key in res.data.pageData[0]) { columnsData.value.push({prop: key, label:key, align: 'center',show:1}) } } state.dataList = res.data.pageData proxy.$modal.msgSuccess('SQL执行成功') } else { proxy.$modal.msgError(res.message) } }) state.loading = false } const handleSave = async () => { const valid = await formRef.value.validate() if (!valid) return form.value.info=JSON.stringify({ sql:form.value.sql }) const method=form.value.id===""?handleDataSetCreate:handleDataSetReconstruct method(form.value).then((res) => { if (res.success) { emit('saved',{id: res.data }) } else { proxy.$modal.msgError(res.message) } }) } watch(() => props.id, (newid, oldid) => { if (newid !== oldid && newid!=="") { // 引用变化时触发 handleDataSetGet(newid).then((res) => { if(res.success){ form.value.sql=JSON.parse(res.data.info)?.sql; } }) } }, { immediate: true } // 注意:不要用 deep: true ); </script> <style scoped lang="scss"> .sql-dataset-container { height: calc(90vh - 60px); padding: 16px; background-color: #f5f7fa; } .layout-container { height: 100%; background: #fff; border-radius: 8px; box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05); overflow: hidden; display: flex; /* 使用flex横向布局 */ flex-direction: row; /* 横向排列 */ } .config-aside { background: #f9fafc; border-right: 1px solid var(--el-border-color-light); /* 右侧边框分隔 */ padding: 18px; display: flex; flex-direction: column; width: 50%; /* 左侧占50%宽度 */ min-width: 300px; /* 最小宽度限制 */ } .preview-main { padding: 18px; background: #fff; display: flex; flex-direction: column; flex: 1; /* 右侧占剩余宽度 */ min-width: 300px; /* 最小宽度限制 */ } .section-header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; margin-bottom: 16px; gap: 12px; h2 { margin: 0; color: var(--el-text-color-primary); font-weight: 600; font-size: 16px; display: flex; align-items: center; gap: 6px; white-space: nowrap; } } .preview-content { flex: 1; border: 1px solid var(--el-border-color-light); border-radius: 6px; overflow: hidden; display: flex; flex-direction: column; min-height: 250px; } .config-form { flex: 1; display: flex; flex-direction: column; :deep(.el-form-item) { margin-bottom: 16px; } } .form-item-card { background: #fff; padding: 12px; border-radius: 6px; border-left: 2px solid var(--el-color-primary); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03); } .sql-editor-container { flex: 1; display: flex; flex-direction: column; border: 1px solid var(--el-border-color-light); border-radius: 4px; overflow: hidden; min-height: 220px; max-height: 400px; } .sql-tips { padding: 6px 10px; background: var(--el-color-info-light-9); border-top: 1px solid var(--el-border-color-light); .el-tag { width: 100%; justify-content: flex-start; font-size: 11px; padding: 4px 8px; } } /* 响应式调整 */ @media (max-width: 992px) { .layout-container { flex-direction: column; /* 小屏幕下改为纵向布局 */ } .config-aside, .preview-main { width: 100%; /* 占满宽度 */ min-width: auto; /* 取消最小宽度限制 */ } .config-aside { border-right: none; border-bottom: 1px solid var(--el-border-color-light); /* 底部边框分隔 */ max-height: 50%; /* 限制最大高度 */ } .sql-dataset-container { padding: 12px; height: auto; min-height: 100vh; } .section-header { flex-direction: column; align-items: stretch; .header-left, .header-right { width: 100%; } .header-right { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 8px; } } .sql-editor-container { max-height: 250px; min-height: 180px; } } @media (max-width: 768px) { .preview-content { min-height: 200px; } .section-header { gap: 8px; h2 { font-size: 15px; } } .header-right { grid-template-columns: repeat(2, 1fr) !important; .el-button { width: 100%; margin: 0 !important; } .el-button:last-child { grid-column: span 2; } } } @media (max-width: 480px) { .sql-dataset-container { padding: 8px; } .config-aside, .preview-main { padding: 12px; } .sql-editor-container { min-height: 150px; max-height: 200px; } .form-item-card { padding: 8px; } .el-input, .el-button { font-size: 13px !important; } } </style> ```
最新发布
07-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值