#111 Advanced Search Form

本文介绍了一种使用Rails构建高级搜索功能的方法,通过POST请求而非GET请求处理包含多个字段的复杂搜索表单,展示了如何创建Search资源并自定义搜索条件。
If you need to create an advanced search with a lot of fields, it may not be ideal to use a GET request as I showed in episode 37. In this episode I will show you how to handle this by creating a Search resource.
<!-- views/searches/new.html.erb -->
<% form_for @search do |f| %>
<p>
<%= f.label :keywords %><br />
<%= f.text_field :keywords %>
</p>
<p>
<%= f.label :category_id %><br />
<%= f.collection_select :category_id, Category.all, :id, :name, :include_blank => true %>
</p>
<p>
Price Range<br />
<%= f.text_field :minimum_price, :size => 7 %> -
<%= f.text_field :maximum_price, :size => 7 %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>

# models/search.rb
def products
@products ||= find_products
end

private

def find_products
Product.find(:all, :conditions => conditions)
end

def keyword_conditions
["products.name LIKE ?", "%#{keywords}%"] unless keywords.blank?
end

def minimum_price_conditions
["products.price >= ?", minimum_price] unless minimum_price.blank?
end

def maximum_price_conditions
["products.price <= ?", maximum_price] unless maximum_price.blank?
end

def category_conditions
["products.category_id = ?", category_id] unless category_id.blank?
end

def conditions
[conditions_clauses.join(' AND '), *conditions_options]
end

def conditions_clauses
conditions_parts.map { |condition| condition.first }
end

def conditions_options
conditions_parts.map { |condition| condition[1..-1] }.flatten
end

def conditions_parts
private_methods(false).grep(/_conditions$/).map { |m| send(m) }.compact
end
<template> <div v-loading="loading" class="content"> <div class="title"> <div class="text"> {{ $t(&#39;产品模型&#39;) }} </div> <div class="date"> <DatePicker ref="datePicker" @changeDate="changeDate"></DatePicker> </div> </div> <MyCard class="data_list"> <MoreSearch :defaultTip="$t(&#39;名称搜索&#39;)" :defaultExpandForm="true" @expandMore="expandMore" @enterSearch="enterSearch" > <template #btnList> <el-row type="flex"> <el-col :span="24"> <el-button v-preventReClick type="primary" @click="add"> {{ $t(&#39;Add&#39;) }} </el-button> </el-col> </el-row> </template> </MoreSearch> <SearchForm v-show="showForm" ref="formModel" type="Virtual_Model_List_Config" @changeSearch="getSearchForm" ></SearchForm> <UserTable ref="userTable" class="table_data" type="Virtual_Model_List_Config" :tableData="tableData" :needIndex="true" :needAction="true" :needExpand="true" :tableWidth="tableWidth" :fixeds="fixeds" :needShowImg="[&#39;sync_status&#39;]" :imgAddress="imgAddress" :dictionTypes="[&#39;Virtual_Model_List_Config&#39;]" :dynamicSlotName="[&#39;name_cn&#39;, &#39;action&#39;]" > <template #expand="props"> <TreeTableLoad ref="treeTableLoad" class="table_expand" instanceType="model" :instanceId="props.row.id" :hasAction="true" :Wstyles="true" citype="Virtual_Bom_Instance_Info_Head" :propsRow="props.row" :tableExpand="true" ></TreeTableLoad> </template> <template #name_cn="props"> <el-button class="btn_link_select" link @click="viewLog(props.row)"> {{ props.row.name_cn }} </el-button> </template> <template #action="props"> <el-button link @click="edit(props.row)"> {{ $t(&#39;Edit&#39;) }} </el-button> <el-button link @click="syncData(props.row)"> {{ $t(&#39;sync&#39;) }} </el-button> <el-button link @click="deleteData(props.row)"> {{ $t(&#39;delete&#39;) }} </el-button> </template> </UserTable> <Pagination ref="Pagination" :total="total" @onChange="tableChange" /> </MyCard> <el-dialog v-model="showDialog" class="dialog" :title="$t(&#39;delete&#39;)" width="30%"> <div> <el-input v-model.trim="textareas" type="textarea" maxlength="500" :rows="2" :autosize="{ minRows: 2, maxRows: 4 }" :placeholder="applyLang(&#39;请输入&#39;, &#39;Please enter&#39;)" show-word-limit ></el-input> </div> <template #footer> <div class="dialog_footer"> <el-button v-preventReClick type="primary" @click="submit"> {{ $t(&#39;确定&#39;) }} </el-button> <el-button v-preventReClick @click="closeDialog"> {{ $t(&#39;Cancels&#39;) }} </el-button> </div> </template> </el-dialog> <el-dialog v-model="addTypeDialog" :title="$t(&#39;新增类型&#39;)" width="20%"> <div class="add_dialog"> <el-button @click="addModel(&#39;ILaunch&#39;)"> {{ $t(&#39;iLaunch模型&#39;) }} </el-button> <el-button @click="addModel(&#39;PDM&#39;)"> {{ $t(&#39;PDM模型&#39;) }} </el-button> </div> </el-dialog> </div> </template> <script> import MyCard from &#39;../components/MyCard.vue&#39;; import UserTable from &#39;../supplyData/userTable.vue&#39;; import Pagination from &#39;../components/Pagination.vue&#39;; import DatePicker from &#39;@/components/DatePicker&#39;; import TreeTableLoad from &#39;../components/treeTableLoad.vue&#39;; import SearchForm from &#39;../components/SearchForm.vue&#39;; import MoreSearch from &#39;../components/moreSearch.vue&#39;; import dayjs from &#39;dayjs&#39;; import { getModelListAPI, syncModelInfoAPI, deleteModelAPI } from &#39;@/apis/ptp&#39;; import pinia from &#39;@/store/pinia&#39;; import { useProductMgmtStore } from &#39;@/store/modules/productMgmt&#39;; export default { components: { MyCard, UserTable, Pagination, DatePicker, TreeTableLoad, SearchForm, MoreSearch, }, setup() { const productStore = useProductMgmtStore(pinia); return { productStore, }; }, data() { return { loading: false, searchForm: {}, page: { pageNo: 1, pageSize: 10, }, total: 0, textareas: &#39;&#39;, currentData: {}, searchDate: [], tableData: [], tableWidth: { action: { zh: 160, en: 235 }, }, fixeds: [ { field: &#39;action&#39;, fixed: &#39;right&#39;, }, ], imgAddress: { &#39;sync_status&#39;: { completed: new URL(&#39;../images/Status/completed.png&#39;, import.meta.url).href, error: new URL(&#39;../images/Status/error.png&#39;, import.meta.url).href, waiting: new URL(&#39;../images/Status/waiting.png&#39;, import.meta.url).href, executing: new URL(&#39;../images/Status/executing.png&#39;, import.meta.url).href, }, }, showDialog: false, addTypeDialog: false, showForm: true, }; }, computed: { }, activated() { this.updateResetPage ? this.resetPage() : this.getTableData(); }, created() { window.FURION && window.FURION.reportCustomTime(&#39;rtti&#39;, &#39;1E444AE648FC4C9E9B581B6A35093A05&#39;, 1); this.productStore.setBomCode(&#39;&#39;); }, methods: { expandMore(val) { this.showForm = val; }, // enterSearch(val, flag) { // if (!flag) { // this.$refs.formModel.resetForm(); // this.searchForm = {}; // this.resetPage(); // return; // } // const advancedSearch = this.$refs?.formModel?.formData || {}; // const finalSearch = { // ...advancedSearch, // name_cn: val ? val.trim() : undefined, // }; // this.handleSearchForm(finalSearch); // }, enterSearch(val, flag) { this.searchForm[&#39;name_cn&#39;] = val; if (!flag) { this.$refs.formModel.resetForm(); return; } let search = this.$refs?.formModel?.formData || {}; console.log("search1", search); this.handleSearchForm(search); }, handleSearchForm(search) { console.log("search2", search); this.productStore.setBomCode(search?.bom_code); console.log("this.searchForm", this.searchForm); Object.assign(this.searchForm, search); console.log("this.searchForm2", this.searchForm); console.log("search3", search); this.resetPage(); }, changeDate(val) { this.searchDate = val; this.resetPage(); }, getSearchForm(val) { let search = val || {}; this.handleSearchForm(search); }, resetPage() { this.$refs.Pagination.resetPage(); this.page.pageNo = 1; this.getTableData(); }, tableChange(pageNo, pageSize) { this.page = { pageNo, pageSize, }; this.getTableData(); }, getDateFormat() { let startDate = dayjs(this.searchDate[0]).valueOf(); let endDate = dayjs(`${this.searchDate[1]} 23:59:59`).valueOf(); return { isFuzzy: &#39;N&#39;, value: { $gte: startDate, $lt: endDate, }, }; }, getTableData() { let params = { search: { type: &#39;RD_Model_Info&#39;, &#39;is_valid&#39;: &#39;Y&#39;, ...this.searchForm, &#39;created_by_date&#39;: this.searchDate && this.searchDate.length > 0 ? this.getDateFormat() : &#39;&#39;, }, page: this.page, }; console.log("params",this.params); this.loading = true; getModelListAPI(params) .then(res => { if (res.success && res?.data) { this.tableData = res.data || []; this.total = res.total || 0; } }) .catch(() => { // catch留空 }) .finally(() => { this.loading = false; }); }, closeDialog() { this.currentData = {}; this.textareas = &#39;&#39;; this.showDialog = false; }, deleteData(row) { this.currentData = row; this.showDialog = true; }, submit() { let params = { id: this.currentData.id, reason: this.textareas, }; this.closeDialog(); this.loading = true; deleteModelAPI(params) .then(res => { if (res.success) { this.getTableData(); this.$message.success(this.$t(&#39;actionSuccess&#39;)); } }) .catch(resData => { this.$message.error(resData.message); }) .finally(() => { this.loading = false; }); }, syncData(row) { let params = { &#39;task_type&#39;: &#39;SyncModelTree&#39;, &#39;task_param&#39;: JSON.stringify({ &#39;model_id&#39;: row.id }), &#39;task_name&#39;: row.name_cn, }; this.loading = true; syncModelInfoAPI(params) .then(res => { if (res.success) { this.getTableData(); this.$message.success(this.$t(&#39;同步成功,请到任务管理查看任务进度&#39;)); } }) .catch(resData => { this.$message.error(resData.message); }) .finally(() => { this.loading = false; }); }, add() { this.addTypeDialog = true; }, addModel(val) { this.addTypeDialog = false; const url = { &#39;ILaunch&#39;: &#39;/productMgmt/model/ILaunchModel&#39;, &#39;PDM&#39;: &#39;/productMgmt/model/add&#39;, }; this.$router.push(url[val]); }, edit(row) { let url = row.bom_source === &#39;PDM&#39; ? &#39;/productMgmt/model/edit&#39; : &#39;/productMgmt/model/ILaunchModel&#39;; this.$router.push({ path: url, query: { type: &#39;RD_Model_Info&#39;, id: row.id, }, }); }, viewLog(row) { this.$router.push({ path: &#39;/productMgmt/model/log&#39;, query: { type: &#39;RD_Model_Info&#39;, id: row.id, }, }); }, }, }; </script> <style lang="less" scoped> .content { display: flex; flex-direction: column; height: 100% !important; overflow: auto; padding-bottom: 30px; } .title { display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: center; padding: 0 20px; height: 54px; line-height: 54px; .text { font-size: 16px; color: #252b3a; font-weight: Bold; } } .data_list { flex: 1; margin: 0 20px 20px; } .table_data { margin-top: 20px; } .table_expand { position: sticky; left: 20px; width: 1600px; margin-left: 20px; border-left: 2px solid #5e7ce0; box-shadow: -20px 0 #f2f5fc; } .dialog { .dialog_footer { text-align: center; } } .add_dialog { text-align: center; } </style> 这里打印params为undefined,但是预期应该有输入的search?.bom_code
10-25
基于51单片机,实现对直流电机的调速、测速以及正反转控制。项目包含完整的仿真文件、源程序、原理图和PCB设计文件,适合学习和实践51单片机在电机控制方面的应用。 功能特点 调速控制:通过按键调整PWM占空比,实现电机的速度调节。 测速功能:采用霍尔传感器非接触式测速,实时显示电机转速。 正反转控制:通过按键切换电机的正转和反转状态。 LCD显示:使用LCD1602液晶显示屏,显示当前的转速和PWM占空比。 硬件组成 主控制器:STC89C51/52单片机(与AT89S51/52、AT89C51/52通用)。 测速传感器:霍尔传感器,用于非接触式测速。 显示模块:LCD1602液晶显示屏,显示转速和占空比。 电机驱动:采用双H桥电路,控制电机的正反转和调速。 软件设计 编程语言:C语言。 开发环境:Keil uVision。 仿真工具:Proteus。 使用说明 液晶屏显示: 第一行显示电机转速(单位:转/分)。 第二行显示PWM占空比(0~100%)。 按键功能: 1键:加速键,短按占空比加1,长按连续加。 2键:减速键,短按占空比减1,长按连续减。 3键:反转切换键,按下后电机反转。 4键:正转切换键,按下后电机正转。 5键:开始暂停键,按一下开始,再按一下暂停。 注意事项 磁铁和霍尔元件的距离应保持在2mm左右,过近可能会在电机转动时碰到霍尔元件,过远则可能导致霍尔元件无法检测到磁铁。 资源文件 仿真文件:Proteus仿真文件,用于模拟电机控制系统的运行。 源程序:Keil uVision项目文件,包含完整的C语言源代码。 原理图:电路设计原理图,详细展示了各模块的连接方式。 PCB设计:PCB布局文件,可用于实际电路板的制作。
【四旋翼无人机】具备螺旋桨倾斜机构的全驱动四旋翼无人机:建模与控制研究(Matlab代码、Simulink仿真实现)内容概要:本文围绕具备螺旋桨倾斜机构的全驱动四旋翼无人机展开研究,重点进行了系统建模与控制策略的设计与仿真验证。通过引入螺旋桨倾斜机构,该无人机能够实现全向力矢量控制,从而具备更强的姿态调节能力和六自由度全驱动特性,克服传统四旋翼欠驱动限制。研究内容涵盖动力学建模、控制系统设计(如PID、MPC等)、Matlab/Simulink环境下的仿真验证,并可能涉及轨迹跟踪、抗干扰能力及稳定性分析,旨在提升无人机在复杂环境下的机动性与控制精度。; 适合人群:具备一定控制理论基础和Matlab/Simulink仿真能力的研究生、科研人员及从事无人机系统开发的工程师,尤其适合研究先进无人机控制算法的技术人员。; 使用场景及目标:①深入理解全驱动四旋翼无人机的动力学建模方法;②掌握基于Matlab/Simulink的无人机控制系统设计与仿真流程;③复现硕士论文级别的研究成果,为科研项目或学术论文提供技术支持与参考。; 阅读建议:建议结合提供的Matlab代码与Simulink模型进行实践操作,重点关注建模推导过程与控制器参数调优,同时可扩展研究不同控制算法的性能对比,以深化对全驱动系统控制机制的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值