module Aio::Base::Toolkit module ExcelOffice class WorkBook include Aio::Ui::Verbose @@worksheets_name = [] def initialize(encoding = "utf-8") if Aio::Base::Toolkit::OS.windows? require "win32ole" else print_error "只有Windows系统才能使用Excel模块" exit 0 end @excel = WIN32OLE.new("excel.Application") @excel.visible = false @workbook = @excel.workbooks.add @encoding = encoding create_style end # 切换到分页预览视图 def window_pagebreak @excel.ActiveWindow.View = 2 end # 切换到普通视图 def window_normal @excel.ActiveWindow.View = 1 end # 警告提示开关 def display_alerts=(bool) @excel.DisplayAlerts = bool end def add_worksheet(name) while @@worksheets_name.include?(name) name += "1" end @@worksheets_name << name worksheet = @workbook.worksheets.add worksheet.activate worksheet.name = name return WorkSheet.new(worksheet) end def create_style sty = @workbook.styles.add("NormalStyle") self.class.normal_style(sty) sty = @workbook.styles.add("BoldStyle") self.class.bold_style(sty) sty = @workbook.styles.add("TitleStyle") self.class.title_style(sty) end def self.normal_style(sty) sty.font.size = 9 sty.HorizontalAlignment = -4108 end def self.bold_style(sty) sty.font.size = 9 sty.font.bold = true sty.HorizontalAlignment = -4108 end def self.title_style(sty) sty.font.size = 20 sty.font.bold = true sty.HorizontalAlignment = -4108 end def show @excel.visible = true end def save(path) path = Aio::Base::Toolkit::String.safe_path(path) @workbook.saveas(path) end def close @workbook.close @excel.quit end end # class WorkBook class WorkSheet IMAGE_ROW_NUM = 56 @@worksheets_name = [] def initialize(worksheet) @row_count = 1 @worksheet = worksheet @nil_space = [] end # 增加一个空行 def add_space_line(n=1) return if n < 1 @row_count += n end # 对列进行合并 def merge(range1, range2) @worksheet.range("#{range1}:#{range2}").merge end # 产生 ::Range 类 def range(str) @worksheet.range(str) end # 添加标题行 def add_title(name) add_row.add_cell(name, false, "BoldStyle") end # 设置列的宽度 def width(col, width) @worksheet.Columns("#{col}:#{col}").ColumnWidth = width end def height(height) @row_height = height end # 增加Row类 def add_row(&block) @current_row = Row.new(@worksheet, @row_count) @current_row.height = @row_height if @row_height @row_count += 1 yield @current_row if block @current_row.merge! begin @current_row.real_row.borders.linestyle = 1 rescue Exception retry end return @current_row end # 返回此时的Row类 def current_row return @current_row end # 返回此时的行索引 def current_row_id return @current_row.row_id end # 添加图像 def add_image(image_path) return unless File.exist?(image_path) add_space_line add_row cell_name = current_row.first_cell @worksheet.Range(cell_name).Select @worksheet.Pictures.Insert(image_path) add_space_line IMAGE_ROW_NUM end # 判断是否有垂直分页符存在 def has_pagebreak? @worksheet.VPageBreaks.count > 0 end # 对列修改分页符 def pagebreak_left(num) #@worksheet.VPageBreaks(1).Location = @worksheet.columns(col) @worksheet.VPageBreaks(1).DragOff("-4161", num.to_i) end # 返回::WorkSheet def worksheet @worksheet end # 添加图表 def add_chart(&block) ch = @worksheet.Shapes.AddChart active = ch.chart # 占位符 block.call(Chart.new(active)) ch.copy end end class Row FILL_TYPE = 4 attr_reader :row_id @@cell_map = ("A".."Z").to_a def initialize(worksheet, row_id) @row_id = row_id @cell_count = 0 @worksheet = worksheet @nil_space = [] @merge_cell = [] end # 此时的单元格 def curent_cell return cell_name(@cell_count) end # 第一个单元格 def first_cell return cell_name(0) end # 设置行高 def height=(height) begin @worksheet.rows(@row_id).RowHeight = height rescue Exception retry end end # 增加单元格 def add_cell(value, auto_fit = false, style = "NormalStyle") range = @worksheet.Range(cell_name(@cell_count)) begin range.Value = value.to_s range.Style = style rescue Exception retry end range.Columns.AutoFit if auto_fit @cell_count += 1 while(@nil_space.include?(to_letter(@cell_count))) do range = @worksheet.Range(cell_name(@cell_count)) range.Value = "" range.Style = style @cell_count += 1 end end # 通过索引变成对应的字母 def to_letter(index) @@cell_map.at(index) end # 特别注意,由于Toolkit中加入了Array,String的模块 # 所以判断的时候特别注意要是 def << (arr) case arr when ::Array arr.size.times do |t| add_cell(arr[t]) end when ::String add_cell(arr) end end # 获得单元格的名字(通过索引) def cell_name(index) second = index % 26 first = (index - second) / 26 if first == 0 return @@cell_map[second] + @row_id.to_s end first -= 1 return @@cell_map[first] + @@cell_map[second] + @row_id.to_s end # 获得单元格的名字(通过字母) def cell_name!(letter) return letter + @row_id.to_s end def set_cell(index, value, auto_fit = false, style = "NormalStyle") range = @worksheet.Range(cell_name(index)) range.Value = value.to_s range.Style = style range.Columns.AutoFit if auto_fit end # 设置单元格风格 def set_style(letter, style = "NormalStyle") range = @worksheet.range(cell_name!(letter)) range.Style = style end # 合并一行中的单元格, 这里仅仅是记录 def merge(idx_begin, idx_end) cell_begin = "#{idx_begin}#{@row_id}" cell_end = "#{idx_end}#{@row_id}" @merge_cell << [cell_begin, cell_end] tmp = ((idx_begin.upcase)..(idx_end.upcase)).to_a tmp.shift @nil_space = (@nil_space | tmp).sort end def merge! return if @merge_cell.empty? @merge_cell.each do |cell_begin, cell_end| range = @worksheet.Range("#{cell_begin}:#{cell_end}") range.merge end end # 开启自动换行 def wraptext(letter) range = @worksheet.range(cell_name!(letter)) range.WrapText = true end # 对此时的行的下方下分页符 def pagebreak begin @worksheet.rows(@row_id+1).PageBreak = 1 rescue Exception retry end end # 返回此时的::Row类 def real_row #@worksheet.rows(@row_id) @worksheet.range("A#{@row_id}:#{cell_name(@cell_count - 1)}") end alias :style :set_style end # 图表 class Chart # 图形类型 ColumnClustered = 51 # 簇状柱形图 ColumnStacked = 52 # 堆积柱状图 Doughnut = -4120 # 圆环图 Line = 4 # 折线图 Pie = 5 # 饼图 BarClustered = 57 # 簇状条形图 # 标签数据 DataLabelsShowLabel = 4 # 数据点所属的分类 DataLabelsShowLabelAndPercent = 5 # 占比百分比以及所属分类,仅饼图 DataLabelsShowPercent = 3 # 百分比, 仅饼图 DataLabelsShowValue = 2 # 默认值 def initialize(active) @chart = active end def chart_work @chart end # 修改标题 def title=(name) @chart.HasTitle = true @chart.ChartTitle.Characters.Text = name end # 这是原数据地址, 按列生成数据 def source=(range) @chart.SetSourceData(range, 2) end # 更改图形类型 def type=(c_type) @chart.ChartType = c_type end # 设置X轴名称, 只用于条形图 def axes_x=(name) @chart.Axes(1,1).HasTitle = true @chart.Axes(1,1).AxisTitle.Characters.Text = name end # 设置Y轴名称, 只用于条形图 def axes_y=(name) @chart.Axes(2,1).HasTitle = true @chart.Axes(2,1).AxisTitle.Characters.Text = name end # 修改样式 # 通过录制宏可以查看样式编号 # 条形图中203 比较好看 # 饼图中 251, 254 比较好看 def style=(int) @chart.ChartStyle = int data_label end # 添加饼图的百分比 def data_label(type=DataLabelsShowLabelAndPercent) # 应用标签选项 @chart.ApplyDataLabels(type) # 取消标签选项的系列名称 now = @chart.SeriesCollection(1).DataLabels now.ShowSeriesName = false # 将图例放到右边 now = @chart.Legend now.Position = -4152 end end end end