蔚縣做網(wǎng)站/云資源軟文發(fā)布平臺(tái)
查詢方案功能實(shí)現(xiàn)
上面實(shí)現(xiàn)了自定義查詢功能框架,從用戶角度出發(fā),有些條件組合可以形成特定的查詢方案,對(duì)應(yīng)著業(yè)務(wù)查詢場(chǎng)景。諸多查詢條件的組合,不能每次都讓用戶來(lái)設(shè)置,而是應(yīng)該保存下來(lái),下次可以直接使用或者在其基礎(chǔ)上按需調(diào)整,也就是將查詢方案持久化。
?
實(shí)體配置
使用平臺(tái)的低代碼配置功能來(lái)實(shí)現(xiàn)查詢方案的實(shí)體配置。
考慮到自定義查詢屬于通用性功能,因此放到了平臺(tái)架構(gòu)體系中位于底層的系統(tǒng)管理模塊下。
實(shí)體配置如下:
實(shí)體模型配置如下:
查詢方案內(nèi)置于自定義查詢功能內(nèi)部的,因此無(wú)需配置相關(guān)視圖。
使用平臺(tái)功能生成庫(kù)表和代碼、拷貝代碼、編譯、配置權(quán)限項(xiàng)。
功能整合
首先上整體效果圖,有個(gè)直觀的了解,如下圖:
在原有基礎(chǔ)上,頂部增加了查詢方案的下拉列表,用戶可以通過(guò)下拉選擇快速加載先前保存的查詢方案,并可以通過(guò)修改按鈕來(lái)修改名稱、通過(guò)刪除按鈕來(lái)刪除方案。
查詢方案的主體,仍是由篩選器組件來(lái)承擔(dān),集成整合過(guò)程中增加了交互。
底部通過(guò)保存按鈕來(lái)保存查詢方案;通過(guò)另存為按鈕來(lái)拷貝新增方案,點(diǎn)擊查詢按鈕后,關(guān)閉對(duì)話框,將查詢方案對(duì)應(yīng)查詢條件組合,傳遞給父窗口(自定義查詢功能頁(yè)面),發(fā)起查詢操作。
?
查詢方案下拉列表實(shí)現(xiàn)
在查詢方案目錄下,新增一個(gè)select.vue的頁(yè)面,源碼如下:
<template><el-selectv-model="selectedValue":size="size"clearable:disabled="readonly"style="width: 200px; margin: 20 auto"@change="change"><el-optionv-for="item in dictionaryItemList":key="item.id":value="item.id":label="item.name"/></el-select></template><script>
export default {name: 'QueryPlanSelect',label: '查詢方案下拉',props: {modelValue: {type: String,required: false,default: ''},code: {type: String,default: ''},readonly: {type: Boolean,required: false,default: false},size: {type: String,default: ''}},data() {return {dictionaryItemList: [],selectedValue: ''}},watch: {modelValue: {immediate: true,handler: 'setSelected'}},mounted() {this.loadData()},methods: {change(value) {let rowData = nullthis.dictionaryItemList.forEach((item) => {if (item.id === value) {rowData = itemreturn}})// 更新綁定值this.$emit('update:modelValue', value)// 注意,此處若命令為change,則可能會(huì)與底層實(shí)現(xiàn)沖突,導(dǎo)致執(zhí)行兩次this.$emit('my-change', value, rowData)},setSelected() {this.selectedValue = this.modelValue},loadData() {this.dictionaryItemList = []this.$api.system.queryPlan.list().then((res) => {this.dictionaryItemList = res.dataif (this.dictionaryItemList.length == 1) {this.selectedValue = this.dictionaryItemList[0].idthis.$emit('my-change', this.dictionaryItemList[0].id, this.dictionaryItemList[0])} else {this.selectedValue = ''this.$emit('my-change', '', null)}})}}
}
</script>
就是把查詢方案實(shí)體的列表數(shù)據(jù),轉(zhuǎn)換為下拉列表的數(shù)據(jù)格式,并對(duì)傳入值以及下拉選擇變更后,將數(shù)據(jù)通過(guò)事件觸發(fā)返回給父頁(yè)面,也就是自定義查詢頁(yè)面。
?
查詢方案下拉列表選中項(xiàng)變更
自定義查詢頁(yè)面會(huì)監(jiān)聽查詢方案下拉列表的選擇的變更,可能有兩種情況,一是變更選擇項(xiàng),二是清空選擇,對(duì)應(yīng)的事件處理如下:
// 查詢方案變更queryPlanChanged(value, data) {// 保存為當(dāng)前查詢方案this.currentQueryPlan = dataif (data) {//加載條件this.$refs.everrightFilter.setData(JSON.parse(data.content))} else {this.$refs.everrightFilter.clearData()}}
注意當(dāng)查詢方案內(nèi)容不為空時(shí),調(diào)用篩選器的setData方法,來(lái)填充條件組合,便于用戶查看或在其基礎(chǔ)上調(diào)整。
?
查詢方案改名
點(diǎn)擊下拉列表右側(cè)的“修改”按鈕,彈出輸入框,這里實(shí)際是用于改名,系統(tǒng)會(huì)彈出對(duì)話框,默認(rèn)填充當(dāng)前選中的查詢方案名稱,用戶修改后點(diǎn)擊確定按鈕后保存,如下圖:
功能實(shí)現(xiàn)使用了element plus的MessageBox的Prompt模式,代碼如下:
// 修改方案名稱modify() {if (this.currentQueryPlan == null) {this.$message.warning('請(qǐng)先選擇要改名的查詢方案')return}//獲取查詢方案內(nèi)容,并做非空判斷const content = JSON.stringify(this.$refs.everrightFilter.getData(), null, '\t')if (this.checkContentIsNull(content)) {return}// 輸入新的方案名稱ElMessageBox.prompt('請(qǐng)輸入新的方案名稱', '修改查詢方案', {confirmButtonText: '確定',cancelButtonText: '取消',inputValue: this.currentQueryPlan.name,inputPattern: /.+/,inputErrorMessage: '不能為空'}).then(({ value }) => {this.currentQueryPlan.name = valuethis.currentQueryPlan.content = contentthis.$api.system.queryPlan.modify(this.currentQueryPlan).then((res) => {//刷新查詢方案列表this.refreshQuerPlanList()})})},
查詢方案刪除
點(diǎn)擊下拉列表右側(cè)的“刪除”按鈕,彈出確認(rèn)框,確定后刪除,如下:
源碼實(shí)現(xiàn)如下:
// 刪除remove() {if (this.currentQueryPlan == null) {this.$message.warning('請(qǐng)先選擇要?jiǎng)h除的查詢方案')return}this.$confirm('確定要?jiǎng)h除該查詢方案嗎?', '提示', {confirmButtonText: '確定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.$api.system.queryPlan.remove(this.currentQueryPlan.id).then((res) => {// 刷新查詢方案列表this.refreshQuerPlanList()})})}
查詢方案新增/修改
在查詢方案下拉列表無(wú)選中項(xiàng)的情況下,通過(guò)篩選器配置查詢條件組合后,點(diǎn)擊保存按鈕,彈出對(duì)話框,輸入方案名稱后,新增查詢方案,如下:
在查詢方案下拉列表有選中項(xiàng)的情況下,點(diǎn)擊保存按鈕,直接調(diào)用后端服務(wù),保存數(shù)據(jù),實(shí)際更新的是條件組合。
?
代碼如下:
// 保存查詢方案save() {//獲取查詢方案內(nèi)容,并做非空判斷const content = JSON.stringify(this.$refs.everrightFilter.getData(), null, '\t')if (this.checkContentIsNull(content)) {return}if (this.currentQueryPlan) {// 如當(dāng)前查詢方案不會(huì)空,更新數(shù)據(jù)this.currentQueryPlan.content = contentthis.$api.system.queryPlan.modify(this.currentQueryPlan)} else {// 如當(dāng)前查詢方案為空,新增數(shù)據(jù)ElMessageBox.prompt('請(qǐng)輸入方案名稱', '新增查詢方案', {confirmButtonText: '確定',cancelButtonText: '取消',inputPattern: /.+/,inputErrorMessage: '不能為空'}).then(({ value }) => {const data = {name: value,entityModelCode: this.entityModelCode,content: content}this.$api.system.queryPlan.add(data).then((res) => {//刷新查詢方案列表this.refreshQuerPlanList()})})}}
查詢方案另存為
用戶往往需要基于某個(gè)現(xiàn)有的查詢方案調(diào)整,另存為另一個(gè)查詢方案,通過(guò)功能按鈕實(shí)現(xiàn)。
這里沒有根據(jù)當(dāng)前是否有選中的查詢方案來(lái)控制另存為按鈕的可用,而是在邏輯上進(jìn)行了處理,如當(dāng)前查詢方案為空,新增數(shù)據(jù),否則參照當(dāng)前數(shù)據(jù)新增(即另存),實(shí)現(xiàn)如下:
//另存為saveAs() {//獲取查詢方案內(nèi)容,并做非空判斷const content = JSON.stringify(this.$refs.everrightFilter.getData(), null, '\t')if (this.checkContentIsNull(content)) {return}if (this.currentQueryPlan == null) {// 如當(dāng)前查詢方案為空,新增數(shù)據(jù)ElMessageBox.prompt('請(qǐng)輸入方案名稱', '新增查詢方案', {confirmButtonText: '確定',cancelButtonText: '取消',inputPattern: /.+/,inputErrorMessage: '不能為空'}).then(({ value }) => {const data = {name: value,entityModelCode: this.entityModelCode,content: content}this.$api.system.queryPlan.add(data).then((res) => {//刷新查詢方案列表this.refreshQuerPlanList()})})} else {// 如當(dāng)前查詢方案不為空,參照當(dāng)前數(shù)據(jù)新增ElMessageBox.prompt('請(qǐng)輸入方案名稱', '查詢方案另存為', {confirmButtonText: '確定',cancelButtonText: '取消',inputPattern: /.+/,inputErrorMessage: '不能為空'}).then(({ value }) => {const data = {name: value,entityModelCode: this.entityModelCode,content: content}this.$api.system.queryPlan.add(data).then((res) => {//刷新查詢方案列表this.refreshQuerPlanList()})})}}
輔助方法
有幾個(gè)公共輔助方法,在這里也簡(jiǎn)單列一下:
保存數(shù)據(jù)前需要判斷是否已添加了條件,即進(jìn)行數(shù)據(jù)驗(yàn)證,如下:
// 判斷方案內(nèi)容是否為空checkContentIsNull(content) {if (content == '{}') {this.$message.warning('請(qǐng)先添加至少一個(gè)查詢條件')return true}return false}
在對(duì)方案新增、修改、刪除時(shí),需要刷新下拉列表,如下:
// 刷新查詢方案列表refreshQuerPlanList() {this.$refs.queryPlanSelect.loadData()}
后端處理
先前搭建框架的時(shí)候,僅實(shí)現(xiàn)了單組單條件,接下來(lái),就來(lái)完善該部分邏輯以及其他細(xì)節(jié)。
單組多條件
配置查詢方案條件如下:
后端邏輯完善如下:
// 轉(zhuǎn)換數(shù)據(jù)DataFilterRuleVO dataFilterRule = JSON.parseObject(customQueryString, DataFilterRuleVO.class);// 獲取組集合List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();// 遍歷組集合for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {// 獲取條件集合List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();// 組內(nèi)條件邏輯關(guān)系String logicalOperator = dataFilterGroup.getLogicalOperator();if (CollectionUtils.isNotEmpty(conditionList)) {// 遍歷條件集合for (DataFilterConditionVO condition : conditionList) {if (logicalOperator.equals(OR)) {queryWrapper = queryWrapper.or();}// 獲取字段名,命名風(fēng)格駝峰轉(zhuǎn)換成下劃線String fieldName = condition.getProperty();Object value = condition.getValue();// 獲取操作String operator = condition.getOperator();QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);addEasyQuery(queryWrapper, fieldName, queryRule, value);}}}
運(yùn)行,查看生成SQL,如下:
SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? AND content LIKE ?)
將組內(nèi)邏輯關(guān)系調(diào)整為或,如下:
運(yùn)行,查看生成SQL,如下:
SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? OR content LIKE ?)
多組多條件
配置查詢方案條件如下:
后端邏輯增加組間邏輯關(guān)系處理,調(diào)整后如下:
// 轉(zhuǎn)換數(shù)據(jù)DataFilterRuleVO dataFilterRule = JSON.parseObject(customQueryString, DataFilterRuleVO.class);// 獲取組集合List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();// 組間條件邏輯關(guān)系String logicalOperatorGroup = dataFilterRule.getLogicalOperator();// 遍歷組集合for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {if (logicalOperatorGroup.equals(OR)) {queryWrapper = queryWrapper.or();}// 獲取條件集合List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();// 組內(nèi)條件邏輯關(guān)系String logicalOperator = dataFilterGroup.getLogicalOperator();if (CollectionUtils.isNotEmpty(conditionList)) {// 遍歷條件集合for (DataFilterConditionVO condition : conditionList) {if (logicalOperator.equals(OR)) {queryWrapper = queryWrapper.or();}// 獲取字段名,命名風(fēng)格駝峰轉(zhuǎn)換成下劃線String fieldName = condition.getProperty();Object value = condition.getValue();// 獲取操作String operator = condition.getOperator();QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);addEasyQuery(queryWrapper, fieldName, queryRule, value);}}}
?
運(yùn)行,查看生成SQL,如下:
==> Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? AND content LIKE ? AND name LIKE ? AND name LIKE ?)
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)
看上去,SQL從最終查詢結(jié)果而言是正確的,但是沒有分組,如果組內(nèi)關(guān)系是or,推測(cè)應(yīng)該會(huì)出問題,我們繼續(xù)驗(yàn)證下。
?
將組間邏輯關(guān)系調(diào)整為或,如下:
運(yùn)行,查看生成SQL,如下:
==> Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? AND content LIKE ? OR name LIKE ? AND name LIKE ?)
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)
可以看出來(lái),果然出了問題,我們希望各條件組相對(duì)獨(dú)立,用小括號(hào)包裹,然后再參與組間邏輯運(yùn)算。
基于上述需求,對(duì)邏輯轉(zhuǎn)換代碼進(jìn)行調(diào)整,如下:
private static <E, VO> void build(QueryWrapper<E> queryWrapper, Class<E> entityClass, String customQueryString, SortInfo sortInfo) {// 轉(zhuǎn)換數(shù)據(jù)DataFilterRuleVO dataFilterRule = JSON.parseObject(customQueryString, DataFilterRuleVO.class);// 獲取組集合List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();// 組間條件邏輯關(guān)系String logicalOperatorGroup = dataFilterRule.getLogicalOperator();// 遍歷組集合for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {if (logicalOperatorGroup.equals(OR)) {queryWrapper.or(x ->generateQueryWrapper(x, dataFilterGroup));} else {queryWrapper.and(x ->generateQueryWrapper(x, dataFilterGroup));}}// 附加排序if (sortInfo != null && StringUtils.isNotBlank(sortInfo.getField())) {// 此處未使用注解,而是按照約定的規(guī)則,將駝峰命名轉(zhuǎn)換為下劃線,從而獲取到數(shù)據(jù)庫(kù)表字段名String orderField = CommonUtil.camelToUnderline(sortInfo.getField());if (sortInfo.getAscType()) {queryWrapper.orderByAsc(orderField);} else {queryWrapper.orderByDesc(orderField);}}}private static <E> QueryWrapper<E> generateQueryWrapper(QueryWrapper<E> queryWrapper, DataFilterGroupVO dataFilterGroup) {// 獲取條件集合List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();// 組內(nèi)條件邏輯關(guān)系String logicalOperator = dataFilterGroup.getLogicalOperator();if (CollectionUtils.isNotEmpty(conditionList)) {// 遍歷條件集合for (DataFilterConditionVO condition : conditionList) {if (logicalOperator.equals(OR)) {queryWrapper = queryWrapper.or();}// 獲取字段名,命名風(fēng)格駝峰轉(zhuǎn)換成下劃線String fieldName = condition.getProperty();Object value = condition.getValue();// 獲取操作String operator = condition.getOperator();QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);addEasyQuery(queryWrapper, fieldName, queryRule, value);}}return queryWrapper;}
運(yùn)行,查看生成SQL,如下:
==> Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND ((name = ? AND content LIKE ?) OR (name LIKE ? AND name LIKE ?))
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)
可以看到,條件組外層使用了小括號(hào)包裹,組間的邏輯運(yùn)算也是正確的。
日期類型的處理
前面提到過(guò),在做篩選器組件前端功能驗(yàn)證時(shí),發(fā)現(xiàn)日期類型的屬性,生成查詢規(guī)則與其他類型不同,如下:
value屬性不是一個(gè)值,而是對(duì)應(yīng)了一個(gè)對(duì)象,并且也不是日期字符串,而是時(shí)間戳,這種情況下,需要后端另行解析處理。
設(shè)置測(cè)試條件如下:
增加邏輯處理如下:
運(yùn)行,查看生成SQL,如下:
==> Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND ((name = ? AND content LIKE ?) OR (name LIKE ? AND name LIKE ?))
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)
?
開源平臺(tái)資料
平臺(tái)名稱:一二三開發(fā)平臺(tái)
簡(jiǎn)介: 企業(yè)級(jí)通用開發(fā)平臺(tái)
設(shè)計(jì)資料:[csdn專欄]
開源地址:[Gitee]
開源協(xié)議:MIT
如果您在閱讀本文時(shí)獲得了幫助或受到了啟發(fā),希望您能夠喜歡并收藏這篇文章,為它點(diǎn)贊~
請(qǐng)?jiān)谠u(píng)論區(qū)與我分享您的想法和心得,一起交流學(xué)習(xí),不斷進(jìn)步,遇見更加優(yōu)秀的自己!