在前面隨筆《基於SqlSugar的開發框架循序漸進介紹(12)-- 拆分頁面模塊內容為組件,實現分而治之的處理》中我們已經介紹過,對於相關的業務表的界面代碼,我們已經儘可能把不同的業務邏輯封裝在不同的頁面組件中,隔離變化的差異,因此界面組件化後,就可以利用代碼生成工具進行統一的界面代碼的生成了,而且... ...
在前面隨筆《基於SqlSugar的開發框架循序漸進介紹(12)-- 拆分頁面模塊內容為組件,實現分而治之的處理》中我們已經介紹過,對於相關的業務表的界面代碼,我們已經儘可能把不同的業務邏輯封裝在不同的頁面組件中,隔離變化的差異,因此界面組件化後,就可以利用代碼生成工具進行統一的界面代碼的生成了,而且由於變化的隔離處理,我們實際上維護的代碼變得更加方便維護了。本篇隨筆介紹在整合代碼生成工具進行前端界面的生成的一些思路和實際的界面代碼的生成。
1、頁面的模塊化處理
在前面隨筆《基於SqlSugar的開發框架循序漸進介紹(12)-- 拆分頁面模塊內容為組件,實現分而治之的處理》中我們已經介紹過,常規頁麵包含有列表界面,新增、編輯、查看、導入等界面,除了列表頁面,其他內容以彈出層對話框的方式進行處理,如下界面示意圖所示。
根據以上的頁面劃分,我們把一個頁面分為search.vue、edit.vue、import.vue、view.vue、index.vue,其中index.vue為整合各個組件的主頁面,在視圖中如下所示。我們每個業務模塊都是如此統一划分,因此比較統一,同時也是為後續的代碼生成工具批量生成做好準備。
因此在index.vue頁面中,我們整合了幾個組件頁面即可,如下所示。
<template> <div class="main"> <!--條件及列表展示--> <Search ref="searchRef" @show-import="showImport" @show-add="showAdd" @show-view="showView" @show-edit="showEdit" /> <!--查看詳細組件界面--> <view-data ref="viewRef" /> <!--新增、編輯組件界面--> <edit-data ref="editRef" @submit="refreshData" /> <!--模板導入信息--> <import-data ref="importRef" @finish="finishImport" /> </div> </template> <script setup lang="ts"> import { reactive, ref, onMounted } from 'vue'; import Search from './search.vue'; import ViewData from './view.vue'; import EditData from './edit.vue'; import ImportData from './import.vue';
1)查看視圖頁面
我們先以view.vue查看頁面為例進行介紹,它是一個查看明細的界面,因此也是一個彈出對話框頁面,我們把它的代碼處理如下所示。
<template> <el-dialog v-if="isVisible" v-model="isVisible" title="查看信息" append-to-body @close="closeDialog"> <el-form ref="viewRef" :model="viewForm" label-width="100px"> <el-tabs type="border-card"> <el-tab-pane label="基本信息"> <el-descriptions title="" :column="2" border> <el-descriptions-item label="顯示名稱"> {{ viewForm.name }} </el-descriptions-item> <el-descriptions-item label="Web地址"> {{ viewForm.url }} </el-descriptions-item> <el-descriptions-item label="Web圖標"> <!-- {{ viewForm.webIcon }} --> <icon :icon="viewForm.webIcon" /> </el-descriptions-item> <el-descriptions-item label="排序"> {{ viewForm.seq }} </el-descriptions-item> <el-descriptions-item label="可見"> <el-tag v-if="viewForm.visible" type="success" effect="dark">可見</el-tag> <el-tag v-else type="danger" effect="dark">隱藏</el-tag> </el-descriptions-item> <el-descriptions-item label="展開"> <el-tag v-if="viewForm.expand" type="success" effect="dark">展開</el-tag> <el-tag v-else type="" effect="dark">收縮</el-tag> </el-descriptions-item> <el-descriptions-item label="創建時間"> <el-date-picker v-model="viewForm.createTime" align="right" type="datetime" placeholder="選擇日期" value-format="YYYY-MM-DD HH:mm" disabled /> </el-descriptions-item> <el-descriptions-item label="特殊標簽"> {{ viewForm.tag }} </el-descriptions-item> </el-descriptions> </el-tab-pane> </el-tabs> </el-form> <template #footer> <span class="dialog-footer"> <el-button @click="closeDialog">關閉</el-button> </span> </template> </el-dialog> </template>
其他的js代碼採用tyepscript語法,我們把它放在
<script setup lang="ts"> //邏輯代碼 </script>
然後我們在js代碼中拋出show的方法,便於父組件的調用。
//顯示視窗 const show = (id: string | number) => { if (!$u.test.isNullOrUnDef(id)) { menu.Get(id).then((data) => { Object.assign(viewForm, data); isVisible.value = true; //顯示對話框 }); } }; //暴露組件屬性和方法 defineExpose({ show, });
同時在頁面裡面,也定義一個表單的對象引用,便於上面模板組件的顯示。
const viewRef = ref<FormInstance>(); //表單引用 // 表單屬性定義 let viewForm = reactive({ iD: '', pid: '', name: '', icon: '', seq: '', functionId: '', visible: 0, expand: 0, winformType: '', url: '', webIcon: '', creator: '', createTime: '', tag: '', });
如果是查看詳細的視圖中有樹形列表,我們還可以在onMounted的處理中,添加獲取樹列表的數據即可,如下代碼所示。
//掛載的時候初始化數據 onMounted(() => { // 設置預設值 getTree(); }); // 初始化樹列表 let treedata = ref([]); const getTree = () => { // 樹列表數據獲取 menu.GetAll().then((data) => { treedata.value = []; // 樹列表清空 var list = data?.items; if (list) { var newTreedata = $u.util.getJsonTree(list, { id: 'id', pid: 'pid', label: 'name', children: 'children', }); treedata.value = newTreedata; } }); };
而查看視圖的觸發,往往在列表的操作按鈕中,或者雙擊表格行進行觸發,界面如下所示。
界面如下代碼所示。
而調用查看詳細頁面的事件,傳遞對應的id並調用組件實例拋出的方法即可。如下代碼所示。
// 顯示查看對話框處理 const viewRef = ref<InstanceType<typeof ViewData>>(); function showView(id) { viewRef.value.show(id); }
至此,一個獨立的視圖頁面組件,以及如何觸發調用就完成了,視圖頁面單獨維護,便於代碼的管理,同時也隔離了複雜的頁面邏輯。
視圖頁面效果如下所示。
2)新增編輯視圖頁面
在常規的處理中,往往編輯和新增的界面是差不多的,差異不同的地方,我們可以通過條件 if 的方式進行處理即可。因此可以把兩者放在一個組件中實現對話框內容和邏輯處理。
剛纔我們提到了Index.vue頁面,是對幾個組件的統籌處理,如下代碼所示。
<template> <div class="main"> <!--條件及列表展示--> <Search ref="searchRef" @show-import="showImport" @show-add="showAdd" @show-view="showView" @show-edit="showEdit" /> <!--查看詳細組件界面--> <view-data ref="viewRef" /> <!--新增、編輯組件界面--> <edit-data ref="editRef" @submit="refreshData" /> <!--模板導入信息--> <import-data ref="importRef" @finish="finishImport" /> </div> </template>
從上面代碼我們看到,在HTML代碼中,我們引入對應的組件,併在主查詢頁面中觸發事件即可,如下所示。
其中showAdd和ShowEdit類似,都是調用編輯/新增的對話框,不同的是,通過傳遞id來辨別是否為新增,如果需要傳入pid的父節點信息,那麼我們也可以創建一個showAdd的方法。
//新增、編輯表單引用 const editRef = ref<InstanceType<typeof EditData>>(); //顯示新增對話框 function showAdd(pid?: string | number) { editRef.value.showAdd(pid); } // 顯示編輯對話框 function showEdit(id) { editRef.value.show(id); } //新增/更新後刷新 function refreshData() { searchRef.value.getlist(); }
編輯業務數據的對話框和查看詳細的類似,不過這裡是需要使用輸入控制項進行內容編輯修改處理的。
對於一些複雜控制項,我們可以自定義組件來簡化在界面上的使用,儘可能的快捷、簡單。
我們在組件中定義showAdd和show的方法,便於父組件的調用即可,如果傳遞了id值,我們根據業務對象的get方法獲取詳細的數據,賦值到表單對象上就可以正常顯示了。
//預設標題為[編輯信息],當show傳入id為空的時候,為[新建信息] let title = ref('編輯信息'); let isAdd = ref(false); //是否新增狀態 //顯示視窗(編輯/新建) const showAdd = async (pid?: string | number) => { title.value = '新建信息'; isAdd.value = true; resetFields(); //清空表單 editForm.pid = pid + ''; isVisible.value = true; //顯示對話框 }; const show = async (id?: string | number) => { if (!$u.test.isNullOrUnDef(id)) { title.value = '編輯信息'; isAdd.value = false; await menu.Get(id).then((data) => { Object.assign(editForm, data); }); } else { title.value = '新建信息'; isAdd.value = true; resetFields(); //清空表單 } isVisible.value = true; //顯示對話框 };
拋出這兩個實例的方法,供外面調用。
//暴露組件屬性和方法 defineExpose({ show, showAdd, });
為了承載表單的數據模型,我們創建相關的業務對象
const editRef = ref<FormInstance>(); //表單引用 // 表單屬性定義(初始化) let editForm = reactive({ id: '', pid: '', name: '', icon: '', functionId: '', winformType: '', url: '', seq: '001', isTop: false, expand: 1, visible: 1, webIcon: '', tag: 'web', // Web專用 });
保存數據的時候,我們判斷表單的rules規則,如果符合通過,那麼提交數據並提示用戶即可。
// 保存數據處理 async function submitData() { var formEl = editRef.value; if (!formEl) return; await formEl.validate(async (valid) => { if (valid) { //驗證成功,執行下麵方法 var result = false; if (isAdd.value) { result = await menu.Create(editForm); //新增保存 } else { result = await menu.Update(editForm); //編輯保存 } if (result) { $u.success('操作成功!'); // 提示信息 emit('submit'); // 提示刷新數據 closeDialog(); // 重置視窗狀態 } else { $u.error('操作失敗'); } } }); }
編輯界面的效果如下所示。
3)導入界面的處理
導入界面,一般我們分為幾個步驟,一個是提供導入模板下載,然後上傳文件並顯示數據,然後確認提交即可。
由於導入數據的邏輯上大致類似,不同的是他們的業務數據和驗證規則,因此我們通過自定義組件的方式,來簡化相關的處理。
我通過改造ele-import 的第三方組件,讓它支持Vue3+Typescript語法,實現對業務數據的上傳操作。
<template> <div class="main"> <!--條件及列表展示--> <Search ref="searchRef" @show-import="showImport" @show-add="showAdd" @show-view="showView" @show-edit="showEdit" /> <import-data ref="importRef" @finish="finishImport" /> </div> </template>
而在import.vue頁面裡面,我們的代碼是使用ele-import來處理即可。
<template> <div> <!-- 模板導入信息 --> <ele-import :fields="importForm.fields" :filepath="importForm.filepath" :append="importForm.append" :formatter="importForm.formatter" :rules="importForm.rules" :tips="importForm.tips" :title="importForm.title" :visible="isVisible" :request-fn="saveImport" @close="close" @finish="finishImport" /> </div> </template>
通過傳入對應的數據,如導入欄位,以及規則,以及下載文件等相關參數,就可以實現了文件的上傳處理了。
// 請求服務端處理上傳數據,返回一個Promise對象 const saveImport = async (data) => { // console.log(data); const result = await menu.SaveImport(data); // console.log(result); if (result) { return Promise.resolve(); } else { return Promise.reject(); } };
同時,這也是一個彈出視窗,因此也需要暴露show方法給外部調用。
//顯示視窗 const show = () => { isVisible.value = true; //顯示對話框 }; //暴露組件屬性和方法 defineExpose({ show, });
而這個組件的相關數據信息,定義在importForm中,我們來看看對應的數據格式。
const importForm = reactive({ // Excel 模板導入數據 // 彈出層標題 title: '功能菜單導入', // 提示信息 tips: [], // ['商品編號 必填', '產品類型 必填', '商品名稱 必填'], // 欄位名稱參照表 fields: { // 欄位根據需要裁剪 pid: '父ID', name: '顯示名稱', icon: '圖標', seq: '排序', url: 'Url地址', webIcon: '菜單圖標', systemType_ID: '系統編號', tag: '特殊標簽', },// 附加數據, 在每條記錄上都會加這兩個欄位和值 append: { // company: '廣州愛奇迪', // leader: '伍華聰' }, // 參數校檢, 和 element-ui 中 form表單中傳遞的rules一樣, 都是使用的 async-validator 庫 // https://element.eleme.cn/#/zh-CN/component/form#biao-dan-yan-zheng rules: { pid: { type: 'string', required: true, message: '父ID必填' }, name: { type: 'string', required: true, message: '顯示名稱必填' }, url: { type: 'string', required: true, message: 'Url地址必填' }, webIcon: { type: 'string', required: true, message: '菜單圖標必填' }, systemType_ID: { type: 'string', required: true, message: '系統編號必填' }, tag: { type: 'string', required: true, message: '特殊標簽必填' }, }, // Excel模板下載地址。註意, 只能是.xlsx的文件, .xls或者.cvs都會報錯 filepath: 'http://localhost:5043/UploadFiles/template/功能菜單.xlsx', });
整個導入模塊,會通過這個對象的數據格式,進行不同的顯示和校驗處理等操作。界面效果如下所示。
其中第一步提示信息,並提供模板文件下載
第二步提供文件上傳處理。
第三步確認數據並完成處理。
這種統一的操作,我們通過封裝,隔離了邏輯步驟的協調處理,只需要在業務組件中生成相關的數據即可,便於使用。
2、利用代碼生成工具快速生成
通過上面的介紹,我們瞭解到整個頁面的組件代碼結構,因此可以利用它們和數據表之間的關係,生成對應的頁面組件,利用代碼生成工具Database2Sharp強大的資料庫元數據和模板引擎,我們構建了對應的框架代碼生成規則,因此統一生成即可,提高了代碼開發的效能,同時也統一了代碼的結構,便於大項目的維護。
對於SQLSugar的項目框架,我們為了方便,分別單獨提供後端代碼和Web API代碼的生成、Winform界面代碼的生成,以及前面介紹到的Vue3+TypeScript+ElementPlus的代碼生成操作。
代碼生成工具的界面效果如下所示,通過入口菜單,可以實現不同部分的代碼快速生成。
通過隔離頁面組件的內容變化,實現變化不同通過資料庫表關係生成,固定部分採用規定模板預置內容,實現了代碼的快速生成操作。
系列文章:
《基於SqlSugar的開發框架的循序漸進介紹(1)--框架基礎類的設計和使用》
《基於SqlSugar的開發框架循序漸進介紹(2)-- 基於中間表的查詢處理》
《基於SqlSugar的開發框架循序漸進介紹(3)-- 實現代碼生成工具Database2Sharp的整合開發》
《基於SqlSugar的開發框架循序漸進介紹(4)-- 在數據訪問基類中對GUID主鍵進行自動賦值處理 》
《基於SqlSugar的開發框架循序漸進介紹(5)-- 在服務層使用介面註入方式實現IOC控制反轉》
《基於SqlSugar的開發框架循序漸進介紹(6)-- 在基類介面中註入用戶身份信息介面 》
《基於SqlSugar的開發框架循序漸進介紹(7)-- 在文件上傳模塊中採用選項模式【Options】處理常規上傳和FTP文件上傳》
《基於SqlSugar的開發框架循序漸進介紹(8)-- 在基類函數封裝實現用戶操作日誌記錄》
《基於SqlSugar的開發框架循序漸進介紹(9)-- 結合Winform控制項實現欄位的許可權控制》
《基於SqlSugar的開發框架循序漸進介紹(10)-- 利用axios組件的封裝,實現對後端API數據的訪問和基類的統一封裝處理》
《基於SqlSugar的開發框架循序漸進介紹(11)-- 使用TypeScript和Vue3的Setup語法糖編寫頁面和組件的總結》
《基於SqlSugar的開發框架循序漸進介紹(12)-- 拆分頁面模塊內容為組件,實現分而治之的處理》
《基於SqlSugar的開發框架循序漸進介紹(13)-- 基於ElementPlus的上傳組件進行封裝,便於項目使用》
《基於SqlSugar的開發框架循序漸進介紹(14)-- 基於Vue3+TypeScript的全局對象的註入和使用》
專註於代碼生成工具、.Net/.NetCore 框架架構及軟體開發,以及各種Vue.js的前端技術應用。著有Winform開發框架/混合式開發框架、微信開發框架、Bootstrap開發框架、ABP開發框架、SqlSugar開發框架等框架產品。
轉載請註明出處:撰寫人:伍華聰 http://www.iqidi.com