用Crystal、MongoDB构建持久的在线编辑体验(datanoise / sam0x17)。

由于文件仍然很稀少,我想补充一些。

这是我目前正在建造的东西。

形象

各个字段将可以用在线编辑器进行编辑,这将自动保存到后台--不需要重新加载整个页面。

形象

整个过程的重点是为我的公司提供一个后台,能够自动处理一些流程,计算库存价格,保持库存等。基本上是一个仓库应用程序,为我量身定做

使用的技术

  • 水晶
  • Kemal
  • 管理员2
    • 靴带3
    • 数据表
  • x-editable - 一个非常棒的内联编辑库,可惜已经有一段时间没有人维护了。
    • 见Bootstrap 3的例子 这里
  • 梦之城_梦之城娱乐_梦之城国际娱乐_梦之城国际娱乐平台
  • 用于编辑的Sublime Text 笑一笑
  • 机器人3T 用于MongoDB的GUI

重要位置

形象

MongoORM使用_id作为主索引。 请注意下划线!

_id不是一个字符串。它的类型是 对象Id

因此,它将需要被构建。如果你想从以前的id中构建它,请使用这个代码。

mongo_id = BSON::ObjectId.new my_string_id

其中my_string_id是一个字符串,而mongo_id是一个ObjectId。

准则

config/database.yml

负责设置与你的Mongo DB实例的通信。请参考 萨姆的文件.

stock.cr,负责定义Stockitem、Subitem和适当的Kemal处理程序。

要求 "mongo_orm"
要求 "json"
要求"./macros.cr"
要求"./weee.cr"

模块股票
     包括宏程序

    class Itemgroup < Mongo::ORM::Document #used for grouping, e.g. Pimoroni products, Shipping, etc.
         字段组:字符串
         has_many :stockitems 1TP3注意,stockitem一次只能属于一个ItemGroup。
     结束

    #这也包括其他项目(运输、数字下载等)。
     class Stockitem < Mongo::ORM::Document
         字段sku:字符串
         字段描述:字符串
         field is_alias :Bool
         field alias_of :String 1TP3如果这个字段被设置,不要使用这个项目的任何其他定义,获取别名的主项目。
         字段 is_set : Bool # 如果这个项目是其他项目的一个集合
         embeds_many :subitems #应该只在is_set = true的情况下被设置。
         字段 is_physical_item : Bool
         field weee_applicable : Bool #can be either: paid for someone else, or item is not electronics, ...
         字段重量 : Int32 # in g
         时间戳
     结束

    class Subitem < Mongo::ORM::EmbeddedDocument
         field sku : String #a 对主要库存物品的引用。注意:这可能会以一种更好的方式用 belongs_to 重新设计? 例如, belongs_to :itemref, class_name: Stock::Stockitem
         字段注释:字符串
         字段金额:Int32
     结束

    get "/stock/show" do |env|
         #https://github.com/datanoise/mongo.cr
         sis = Stockitem.all({"is_alias" => {"$eq" => false}})
         名称 = "股票 :: 显示和编辑"
         渲染 "股票"
     结束

    post "/stock/edit/:what" do|env|
         pp "进入编辑,目标是" + env.params.url["什么"]
         pp env.params.body
         如果 env.params.body.has_key?("pk") && env.params.body.has_key?("value")
             pp "内部pk和价值"
             pk = env.params.body["pk"]?.as(String)
             value = env.params.body["value"]?.as(String)
             如果 si = Stockitem.find(BSON::ObjectId.new pk)   
                 case env.params.url["what"]
                 当 "sku"
                     si.sku = value
                 当 "描述"
                     si.description = value
                 当 "is_alias "时
                     si.is_alias = value == "true" ? true : false
                 当 "alias_of "时
                     si.alias_of = value
                 当 "is_set "时
                     si.is_set = value == "true" ? true : false
                 当 "is_physical_item"
                     si.is_physical_item = value == "true" ? true : false
                 当 "weee_applicable "时
                     pp "weee_applicable"
                     si.weee_applicable = value == "true" ? true : false
                 结束
                
                 如果si.save
                     pp "internal si.save"
                     env.response.status_code = 200
                 否则
                     env.response.status_code = 400
                 结束
             否则
                 env.response.status_code = 400   
             结束
         否则
             env.response.status_code = 400
         结束
     结束

    #从WEEE条目中生成
     get "/stock/generate/:year" do |env|
         1TP3走遍整个数据库,收集项目,并显示一个列表
         stock_collection = Hash(String,String).new
         invoices = WEEE::Invoice.all({"年份"=>env.params.url["年份"].to_i32})       
         发票.每个都做|发票|
             #p "处理发票#{发票.号码}"
             1TP3注意,我们可能不会在这里得到绝对所有的发票,因为easybill对日期有不同的看法(进口)。
             发票.项目.各做|项目|
                 if !item.description.nil? && !item.number.nil?
                     key = item.number == "" ? item.description : item.number
                     if !key.nil?
                         if !Stock_collection.has_key?(key)
                             描述 = ""
                             if !item.description.nil?
                                 description = item.description
                             结束                   
                             #stock_collection[key] = item.description
                             if !description.nil?
                                 stock_collection[key] = description
                             结束
                         end # end of !Stock_collection.has_key?
                     end # end of if !"key.nil?
                 结束
             End #end of invoice.items.each do
         结束 #end of invoices.each
         1TP3检查这个问题是否已经存在于我们的数据库中,否则就创建它。
         储存库中的每件物品都会做|sku,description|。
             if !Stockitem.find_by(:sku, sku)
                 si = Stockitem.new
                 si.sku = sku
                 si.is_alias = false
                 si.alias_of = ""
                 si.description = 描述
                 si.is_set = false
                 si.is_physical_item = true
                 si.weee_applicable = true
                 si.weight = 100
                 si.save!
             结束
         结束
     结束#end的获取宏

结束1TP3模块的结束

注意事项。

  • 萨姆的文档解释了这段代码的部分内容,我只谈谈其中的绊脚石。
  • sis = Stockitem.all({"is_alias" => {"$eq" => false}) - 此代码搜索Stockitems(Mongo ORM将为你处理DB名称等)。
  • si = Stockitem.find(BSON::ObjectId.new pk)
    • 注意,这里BSON::ObjectId.new是用来从传递的密钥pk中重建id的。
  • 成功时返回代码200,失败时返回代码400,以便x-editable知道编辑是否成功。

si.save的行为

  • si.save!如果无法保存,将引发一个错误。
  • si.save如果可以保存,将返回true,如果不可以,将返回false。

stock.ecr,该HTML模板


     。
结束 %


<div class="”col-md-12″">
     <div class="”box" box-primary”>
         <div class="box-header" with-border”>
             <h3 class="”box-title”"><%= name %></h3>
         </div>
         <div class="”box-body”">
             请注意:别名的项目不会从数据库中加载。
            


                 <thead>
                     <tr role="”row”">
                         <th class="”sorting_asc”" tabindex="”0″">种类</th>
                         <th>描述</th>
                         <th>is_alias?</th>
                         <th>别名_of</th>
                         <th>is_set?</th>
                         <th>子项目</th>
                         <th>is_physical_item?</th>
                         <th>weee_applicable?</th>
                         <th>重量(g)</th>
                     </tr>
                 </thead>
                 。
                
                    
                    
                    
                    
                    
                    
                    
                    
                    
                
                 结束 %

           

tbd.<a href="#"
                             id=".weee_applicable"
                             class="weee_applicable editable"
                             data-type="select"
                             data-pk=""
                             data-value="true"
                             data-source="[{value: 'true', text: 'true'}, {value: 'false', text: 'false'}] "
                             data-url="/stock/edit/weee_applicable"
                             data-title="WEEE: "
                         >>%= si.weee_applicable ?"true" : "false" %>
g

         </div>
     </div>
</div>
<script
     //https://datatables.net/examples/styling/bootstrap
     //demo.js x-editable
     window.onload = function() {
         $(document).ready(function (){
             $("#stock_items").DataTable()。
             $.fn.editable.defaults.mode = 'popup'。
             $(".editable").editable()。
             $(".editable").on('hidden', function(e, reason){
                 if(reason === 'save' || reason === 'nochange'){
                     var $next = $(this).closest('tr').next().find('.editable')。
                     setTimeout(function(){
                         $next.editable('show')。
                     }, 300);
                 }
             });
         });
     }
</script>
结束 %

注意事项。

  • 因为我们在这之后添加了我们的JS脚本(我应该重新设计,在最后添加一个自定义脚本部分),我们必须将它们包裹在window.onload函数中--否则$就不会被定义(jQuery快捷键)。
  • 请注意,我们在这里使用了一个函数,在保存成功后跳到下一个可编辑的地方。
  • si._id.to_s.rchop - BSON id被翻译成一个字符串,然后最后一个奇怪的字符(null terminator?)被砍掉。这是一个重要的绊脚石!

layout.ecr(摘录)。

<!
<html>
<标题
   <meta charset=”utf-8″>
   。
   TaxGod |
   <meta content=”width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no” name=”viewport”>
   。
   。
   。
   。
   。
   。
   。
   <link rel=”stylesheet” href=”/tg_css/bootstrap-editable.css”>
   。
   。 
   。
   <script src=""https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js”>
   <script src=""https://oss.maxcdn.com/respond/1.4.2/respond.min.js”>
   <!

  <链接rel="样式表"
         href=""https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic”>
</head
<body class="”hold-transition" skin-blue sidebar-mini”>
<div class="”wrapper”">

(...)

  <!– Content Wrapper. Contains page content –>
   <div class="”content-wrapper”">
     <!– Content Header (Page header) –>
    


       <h1>
         TaxGod ::。
         做上帝是很有趣的。
       </h1>
    

    <主要内容
    

     <%= yield_content “main” %>

   


     <!– /.content –>
   </div>
   。

(...)
</div>
<!

<!– REQUIRED JS SCRIPTS –>

<!

<!– Bootstrap 3.3.7 –>

<!
<script src=”/danielm_uploader/js/jquery.dm-uploader.min.js”></script>
<script src=”/bower_components/datatables.net/js/jquery.dataTables.min.js”></script>


/script>
<script src=”/tg_js/dropzone.min.js”></script>
</body>
</html>

更多参考资料