# 前端開發流程 ### 頁面介紹 ![](https://i.imgur.com/898hP6K.png) 基本的功能會有State.js、Page.vue、Main.vue、CRU.vue > Page > ![](https://i.imgur.com/XT5eanl.png) > Main > ![](https://i.imgur.com/MbspVpS.png) > CRU > ![](https://i.imgur.com/uT6CwAH.png) --- ### State.js 基本的設定檔,主要放功能共用的物件,除了頁簽的url需要修改外其餘基本上都不需要修改 ```javascript=37 let baseAppPath = 'pages/SYS/M1/M1002/'; const originTab = () => [defaultTabObject('常用.主單', baseAppPath + 'CRU')] const mainTab = ref(originTab()) ``` baseAppPath:功能資料夾路徑 origin:頁簽名稱跟vue的名稱 其他已經寫好的物件都有寫上註解,可以看程式決定要不要留著或修改 ![](https://i.imgur.com/N2bjGyk.png) --- ### Page.vue 最外圍的整個頁面,包含了查詢條件及filter 流程: * 改標題名稱 * 調用Main的元件 ```htmlembedded=4 <Main ref="refMain" /> ``` ```javascript=49 import Main from "./Main.vue"; ``` ```javascript=56 export default { components: { DataTable, ToolTip, Main, }, ``` * 新增查詢條件 ```htmlembedded=3 <div class="display-1">公佈欄類別功能維護</div> <Main ref="refMain" /> <v-card color="transparent" tag="div" outlined> <v-card-text> <v-row> <Textbox col="4" xs="6" filled v-model="search.announcementTypeName" :label=" columnAttributeSet['announcementTypeName'] .displayname " /> </v-row> </v-card-text> <v-card-actions> <v-spacer /> <Button color="primary" class="px-2" @click="filter.searchData()" > <v-icon left>mdi-magnify</v-icon>{{$t("常用.查詢") }}</Button > <Button color="grey darken-1" class="ma-2 mr-16" outlined @click="searchClear()" >{{ $t("常用.清除") }}</Button > </v-card-actions> </v-card> ``` textbox是底層已經封好的元件,使用方法與v-textfeild差不多 label的寫法是為了要實現多國語系 announcementTypeName為欄位名稱,filter為Datatable的ref名稱 * datatable的ref名稱跟api ```htmlembedded=38 <DataTable ref="filter" :searchCondition="search" api_path="api/SYSM1001/filter" > </DataTable> ``` * 引用元件 ```javascript=90 import DataTable from "@/components/common/DataTable"; import ToolTip from "@/components/common/ToolTip"; import { ApiResponseResolution } from "@/services/ApiResponseResolutionService"; import { ref, watch } from "vue"; import Main from "./Main.vue"; import State from "./State"; import api from "@/api/https"; import { defaultRequestInput, defaultColumnAttributeSetObject, } from "@/services/CommonService"; ``` ref、watch:vue的元件,詳細可以參照官方文件 defaultRequestInput:統一CRUD 功能 取得資料時使用 defaultColumnAttributeSetObject:依照傳入的item的property 產生對應的metaData 預設屬性 寫在底層的原件引用跟使用方式與上述Main.vue一樣(ex.DataTable、ToolTip、Main) * api路徑(取得查詢的label) ```javascript=139 //取搜尋欄位多國語系的label const columnAttributeSet = ref( defaultColumnAttributeSetObject(defaultItem()) ); /*API 資料方法 */ //取得columnAttribute const getRule = async () => { let apiResponse = await api( "post", "api/SYSM1001/get", defaultRequestInput("create", "") ); let itemProperty = ApiResponseResolution(apiResponse); //從回傳的API 寫驗證到columnAttributeSet 驗證集合 Object.assign( columnAttributeSet.value, itemProperty.columnAttribute ); //欄位屬性 }; ``` api串接方法為api(傳輸方法(get、post、put、delete),api路徑,回傳值) 傳接完的資料columnAttributeSet,實現查詢欄位label的多國語系 --- ### Main.vue 顯示CRUD的Dialog,主要是做分辨CURD狀態的邏輯,刪除也是寫在Main裡面 流程: * 引用state ```javascript=95 import State from "./State"; ``` ```javascript=111 const { changeProgramStatus, programState, selectedItem, programStatus, } = State(); ``` * api路徑(delete) ```javascript=96 import api from "@/api/https"; ``` ```javascript=96 const deleteItemConfirm = async () => { let apiResponse = await api("put", "api/SYSM1001/deactivate", { ID: selectedItem.value.announcementTypeID, }); let itemProperty = ApiResponseResolution(apiResponse); oMainCRUDUISet.value.responseMessages = itemProperty.responseMessages; if ( !ifCertainTypeMessageExist( oMainCRUDUISet.value.responseMessages, "error" ) ) { addDataUpdateCount(); closeDelete(); } }; ``` api串接方法為api(傳輸方法(get、post、put、delete),api路徑,回傳值) --- ### CRU.vue 顯示CRU頁簽的內容,依照Main給的狀態個別顯示CRU的頁面 流程: * 新增要修改或檢視的欄位 ```htmlembedded=5 <v-form ref="DetailForm" lazy-validation v-model="oMainCRUDUISet.formValid" :readonly="!oMainCRUDUISet.editable" > <v-row align="center"> <Textbox xs="6" v-model="editedItem.announcementTypeName" :rules="Rule.announcementTypeName" :label=" columnAttributeSet['announcementTypeName'] .displayname " /> <Textbox xs="6" v-model="editedItem.sort" :rules="Rule.sort" :label="columnAttributeSet['sort'].displayname" /> </v-row> <v-row align="center"> <v-col cols="6"> <v-switch v-model="editedItem.state" :label=" columnAttributeSet['state'].displayname + ':' + (editedItem.state ? $t('公佈欄類別維護.已啟用') : $t('公佈欄類別維護.未啟用')) " ></v-switch> </v-col> </v-row> </v-form> ``` * api(接欄位的資料-新增) ```javascript=96 /*API 資料方法 */ //取得FORM 驗證規則 const getRule = async () => { let apiResponse = await api( "post", "api/SYSM1001/get", defaultRequestInput(programState.value.status, "") ); let itemProperty = ApiResponseResolution(apiResponse); //從回傳的API 寫驗證到columnAttributeSet 驗證集合 Object.assign( columnAttributeSet.value, itemProperty.columnAttribute ); //欄位屬性 await rulesGenerator( editedItem.value, columnAttributeSet.value, Rule.value ); //產生rules的結果陣列放到rule的物件 }; ``` * api(接欄位的資料-查詢、檢視) ```javascript=126 //資照資料唯一流水號取得資料與驗證規則 const getItemData = async () => { let apiResponse = await api( "post", "api/SYSM1001/get", defaultRequestInput( programState.value.status, editedItem.value.announcementTypeID ) ); let itemProperty = ApiResponseResolution(apiResponse); Object.assign( columnAttributeSet.value, itemProperty.columnAttribute ); //欄位屬性 Object.assign(editedItem.value, itemProperty.data); rulesGenerator( editedItem.value, columnAttributeSet.value, Rule.value ); //產生rules的結果陣列放到rule的物件 }; ``` * api(儲存) ```javascript=148 //儲存 const save = async () => { let apiGetResponse = {}; if (DetailForm.value.validate()) { let inputApiItem = { ...editedItem.value, }; if (programState.value.status === programStatus.edit.code) { apiGetResponse = await api( "put", "api/SYSM1001/put", inputApiItem ); } else if ( programState.value.status === programStatus.create.code ) { apiGetResponse = await api( "post", "api/SYSM1001/post", inputApiItem ); } let dataResult = await ApiResponseResolution(apiGetResponse); //拆資料 Object.assign(oMainCRUDUISet.value, dataResult); if ( !ifCertainTypeMessageExist( oMainCRUDUISet.value.responseMessages, "error" ) ) { addDataUpdateCount(); //新增/編輯 會觸發dataUpdate if ( programState.value.status === programStatus.create.code ) { resetMainCRUDUISet(); } } } }; ``` * 欄位類別 1. textbox:一般的輸入框 ```htmlembedded= <Textbox xs="6" v-model="editedItem.announcementTypeName" :rules="Rule.announcementTypeName" :label=" columnAttributeSet['announcementTypeName'] .displayname " /> ``` 2. switch:類似checkbox,通常做1or0的欄位 ```htmlembedded= <v-col cols="6"> <v-switch v-model="editedItem.state" :label=" columnAttributeSet['state'].displayname + ':' + (editedItem.state ? $t('公佈欄類別維護.已啟用') : $t('公佈欄類別維護.未啟用')) " ></v-switch> </v-col> ``` 3. combobox:也就是autocomplete ,可以搜尋的下拉選單,需要自己在打一支api ```htmlembedded= <Combobox xs="6" :readonly="publishStateIfNotPublish" v-model="editedItem.announcementTypeID" :items="typeIDItem" :rules="Rule.announcementTypeID" :label=" columnAttributeSet['announcementTypeID'].displayname " /> ``` 4. datetimepicker:日期時間選擇器,type可以選擇只要日期或時間,預設是日期時間都有,檢視需要自己寫readonly條件 ```htmlembedded= <Datetimepicker :readonly="publishStateIfNotPublish" :date_time_val.sync="editedItem.publishSTime" :rules="Rule.publishSTime" :label="columnAttributeSet['publishSTime'].displayname" prepend-icon="mdi-calendar" ></Datetimepicker> ``` 5. vueEditor:公佈欄的content輸入框,檢視一樣需要自己寫readonly條件 ```htmlembedded= <v-col cols="12"> <VueEditor :disabled="publishStateIfNotPublish" model="editedItem.announcementContent" :rules="Rule.announcementContent" :label=" columnAttributeSet['announcementContent'] .displayname " /> </v-col> ``` ![](https://i.imgur.com/6chxcnl.png) ![](https://i.imgur.com/G0X9j7e.png) ![](https://i.imgur.com/Bh8vSmr.png) ![](https://i.imgur.com/VNCY7Ee.png) ![](https://i.imgur.com/C7iib8z.png)