新手问题 再谈重复代码消除 (跟风帖)

small_fish__ · 2014年03月19日 · 最后由 small_fish__ 回复于 2014年03月19日 · 1945 次阅读

http://ruby-china.org/topics/17978 帖子后,由于昨天开始在写一个 excel 打印的东西,感觉和楼主遇到了相同的问题。

功能分析:

  1. 针对同一个 model,并提供 4 种 excel 打印模版。
  2. 其中不同模版打印字段不同,但是又有交集,有些字段需要做特殊处理(格式化)。
  3. 打印模版具有相同的头部和尾部。
  4. 4 种模版其实又大致分为 2 类,1 类只有 1 个 header,另外一类又 2 级 header(意思就是打印的 columns 具有 group 的概念,不同的 group 能够有不同的背景色区分。)
  5. 其中有 2 种 excel 能够打印图片。

目前我使用的是 axlsx 这个 gem. 目前没有任何结构的代码

module Printer

   COLUMN_NAMES = {
     :image => _("Image"),
     :brand_name => _("Manufacturer Name"),
     :name => _('Product Name'),
     :dimension => _("Dimensions"),
     :csi_category_code => _('CSI Category'),
     :vendor => _('Vendor Info'),
     :remark => _('notes'),
     :updated_at => _('Last Updated'),
     :symbol => _('Symbol'),
     :description => _('Description'),
     :area => _('Space'),
     :application => _('Application'),
     :manufacturer_website => _('Manufacturer Website'),
     :made_in => _('Made In'),
     :other_brand => _('Acceptable Brands'),
     :lbc_material_title => _("LBC Material Title"),
     :lbc_status => _("LBC Status"),
     :team_member => _("Team Member"),
     :zone => _("Zone"),
     :zone_radius => _("Radius (km)"),
     :sourcing_row_material => _("Sourcing Raw Materials (km)"),
     :red_list_free => _("Red List Free? (y/n)"),
     :product_content => _("Primary Contents"),
     :product_location => _("Product Location"),
     :product_distance => _("Distance from MNFR (km)"),
     :product_responsible_industry => _("Responsible Industry"),
     :product_appiled => _("Exception Applied (y/n)"),
     :manufacturer_location => _("manufacturer Location"),
     :manufacturer_distance => _("Distance from Site (km)"),
     :red_list => _("Red List (y/n)"),
     :app_sourcing => _("App. Sourcing (y/n)"),
     :fsc_certified => _("FSC Certified (y/n)"),
     :lbc_notes => _("LBC Notes"),
     :floor_area => _("Floor Area (m^2)"),
     :height => _("Height (m)"),
     :floor_area => _("Space Volume (m^3)"),
     :iaq_certification => _("IAQ Certification"),
     :conversion_factor => _("Conversion Factor"),
     :test_result => _("Test Result / Limit Value (µg/m^3)"),
     :iaq_notes => _("IAQ Notes"),
     :material_quantity => _("Material Quantity"),
     :formaldehyde => _("Formaldehyde (µg/m^3)"),
     :formaldehyde_projected_emission => _("Formaldehyde Projected Emissions (µg/m^3)"),
     :tvoc => _("TVOC (µg/m^3)"),
     :tvoc_projected_emission => _("TVOC Projected Emissions (µg/m^3)")

   }

   XLSX_STYLES = {
     :title => {
       :bg_color => "ff",
       :fg_color => "00",
       :sz => 14, :b => true,
       :border => { :style => :thin, :color => "00" },
       :alignment => { :horizontal=> :center, :vertical => :center }
     },
     :header => {
       :bg_color => "ff",
       :fg_color => "00",
       :sz => 14,
       :b => true,
       :border => { :style => :thin, :color => "00" },
       :alignment => { :horizontal=> :center, :vertical => :center }
     },
     :center => {
       :alignment => {
         :horizontal=> :center,
         :vertical => :center
       }
     }
   }

   CATEGORT_COLUMNS = {
     :basic => %w(image brand_name name dimension csi_category_code vendor remark updated_at),

     :schedule => %w(symbol description area application image ,

     :lbc => %w(csi_category_code lbc_material_title lbc_status team_member zone),

     :voc => %w(area floor_area height floor_area csi_category_code brand_name )
   }

   def self.print!(records, options={})
     p = Axlsx::Package.new
     p.use_shared_strings = true
     wb = p.workbook
     wb.styles do |s|
       title_style = s.add_style XLSX_STYLES[:title]
       header_style = s.add_style XLSX_STYLES[:header]
       center_cell = s.add_style XLSX_STYLES[:center]
       sheet = wb.add_worksheet

       #render title
       # title = Axlsx::RichText.new
       # title.add_run("#{options[:workspace]}\n", :b => true)
       # title.add_run("#{_('Project:')} #{options[:project]}\n", :b => true)
       # title.add_run("#{_('Date:')} #{Time.now.to_s(:db)}", :b => true)
       # sheet.add_row [title], :height => 40, :style => Array.new( 1, title_style)
       # #sheet.merge_cells("A1:G1")

       #render header
       mode = (options[:mode] || options['mode']).to_sym
       mode = :basic unless CATEGORT_COLUMNS.has_key? mode

       columns = CATEGORT_COLUMNS[mode]
       headers = columns.map{|key| COLUMN_NAMES[key.to_sym]}
       sheet.add_row headers, :height => 30, :style => Array.new( headers.size, header_style)

       #render records content.
       #self.send("print_#{mode}", records, sheet, s)


       records.each_with_index do |material, index|
         values = material.attributes.values_at(*columns)

         #render image
         if columns.index('image') && material.image
           _index = columns.index('image')
           values[_index] = ''
           img = "#{Rails.root}/public#{material.image.file.url.gsub(/\?\d+$/,"")}"
           sheet.add_image(:image_src => img, :noSelect => false, :noMove => false) do |image|
             image.width = 100
             image.height = 100
             image.start_at _index, index+1
           end
         end

         #render vendor info
         if columns.index('vendor') && material.vendor
           _index = columns.index('vendor')
           values[_index] = values[_index].values.join("\n")
         end

         # format updated_at
         if columns.index('updated_at')
           _index = columns.index('updated_at')
           values[_index] = values[_index].localtime.to_s(:db)
         end
         sheet.add_row values, :height => 78, :style => Array.new(columns.size, center_cell)
       end
     end
     p.to_stream.read
   end
 end

目前感觉这代码就不够稳健,无法高度适应 excel 打印样式的自定义,需要重构,我看到上个帖子有兄台提到了模版模式,我也准备采用,带式打印的基本模版,我发现我还没理清。

对代码结构涉及能力不是很强,希望大家能给点意见和建议。。谢谢。

1 楼 已删除

谢谢。#1 楼 @neverlandxy_naix 一开始我也是用 send 来写的,感觉也不是一个清晰的逻辑。。我感觉问题的核心是能将打印的逻辑清晰化,他人阅读代码不那么费力。

现在纠结是不是需要搞一个 pinter 的继承关系来处理

其实我更想用原生的 xml 来创建了,这样直接通过 view 就搞定了。。只是 xml 创建不知道如何插入图片和表格样式。。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号