Explorar o código

Add product locale support in admin

Your Name hai 2 semanas
pai
achega
a5005b523a

+ 85 - 0
app/assets/javascripts/rails_admin/custom/ui.js

@@ -1 +1,86 @@
 //= require rails_admin/custom/ckeditor_ajax
+
+(function($) {
+  function nearestField($input) {
+    return $input.closest('.form-group, .control-group');
+  }
+
+  function buildProductLocaleTabs() {
+    var $form = $('form').filter(function() {
+      return $(this).find('[name="product[name]"]').length > 0;
+    }).first();
+    if ($form.length === 0) {
+      return;
+    }
+
+    var fieldMap = [
+      {
+        key: 'zh-CN',
+        label: '简体中文',
+        nameField: nearestField($form.find('[name="product[name]"]')),
+        detailField: nearestField($form.find('[name="product[detail]"]'))
+      },
+      {
+        key: 'en',
+        label: 'English',
+        nameField: nearestField($form.find('[name="product[name_en]"]')),
+        detailField: nearestField($form.find('[name="product[detail_en]"]'))
+      },
+      {
+        key: 'ru',
+        label: 'Русский',
+        nameField: nearestField($form.find('[name="product[name_ru]"]')),
+        detailField: nearestField($form.find('[name="product[detail_ru]"]'))
+      },
+      {
+        key: 'zh-TW',
+        label: '繁體中文',
+        nameField: nearestField($form.find('[name="product[name_tw]"]')),
+        detailField: nearestField($form.find('[name="product[detail_tw]"]'))
+      }
+    ];
+
+    if ($form.find('.product-locale-tabs').length > 0) {
+      return;
+    }
+
+    var hasAllFields = true;
+    $.each(fieldMap, function(_, group) {
+      if (group.nameField.length === 0 || group.detailField.length === 0) {
+        hasAllFields = false;
+        return false;
+      }
+    });
+    if (!hasAllFields) {
+      return;
+    }
+
+    var $anchor = fieldMap[0].nameField;
+    var $tabs = $('<div class="product-locale-tabs"></div>');
+    var $header = $('<div class="product-locale-tabs-header"><div><h4>商品多语言内容</h4><p>名称与详情按语言分别维护,未填写时接口默认回退到简体中文。</p></div></div>');
+    var $nav = $('<ul class="nav nav-tabs product-locale-tabs-nav" role="tablist"></ul>');
+    var $content = $('<div class="tab-content product-locale-tabs-content"></div>');
+
+    $.each(fieldMap, function(index, group) {
+      var tabId = 'product-locale-tab-' + group.key.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
+      var activeClass = index === 0 ? 'active' : '';
+      var $navItem = $('<li role="presentation" class="' + activeClass + '"></li>');
+      var $link = $('<a data-toggle="tab" role="tab"></a>');
+      $link.attr('href', '#' + tabId);
+      $link.append($('<span class="product-locale-tab-title"></span>').text(group.label));
+      $link.append($('<span class="product-locale-tab-code"></span>').text(group.key));
+      $navItem.append($link);
+      $nav.append($navItem);
+
+      var $pane = $('<div role="tabpanel" class="tab-pane product-locale-pane ' + activeClass + '" id="' + tabId + '"></div>');
+      $pane.append(group.nameField);
+      $pane.append(group.detailField);
+      $content.append($pane);
+    });
+
+    $tabs.append($header).append($nav).append($content);
+    $anchor.before($tabs);
+  }
+
+  $(document).on('ready page:load turbolinks:load', buildProductLocaleTabs);
+})(jQuery);

+ 132 - 1
app/assets/stylesheets/rails_admin/rails_admin.scss.erb

@@ -105,5 +105,136 @@
 @import "rails_admin/themes/<%= theme %>/theming";
 @import "rails_admin/custom/theming";
 
-
+.product-locale-tabs {
+  margin: 8px 0 24px;
+  border: 1px solid #d9dee7;
+  border-radius: 8px;
+  background: #f8fafc;
+  overflow: hidden;
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .8);
+}
+
+.product-locale-tabs-header {
+  padding: 14px 18px 8px;
+  border-bottom: 1px solid #e6ebf2;
+  background: linear-gradient(180deg, #ffffff 0%, #f6f8fb 100%);
+}
+
+.product-locale-tabs-header h4 {
+  margin: 0 0 4px;
+  font-size: 16px;
+  font-weight: 600;
+  color: #243447;
+}
+
+.product-locale-tabs-header p {
+  margin: 0;
+  color: #6b7a8c;
+  font-size: 12px;
+  line-height: 1.6;
+}
+
+.product-locale-tabs-nav {
+  margin: 0;
+  padding: 12px 12px 0;
+  border-bottom: 0;
+  background: #f6f8fb;
+}
+
+.product-locale-tabs-nav > li {
+  margin: 0 8px 0 0;
+}
+
+.product-locale-tabs-nav > li > a {
+  min-width: 132px;
+  padding: 10px 14px;
+  border: 1px solid #d6dce5;
+  border-radius: 8px 8px 0 0;
+  background: #edf2f7;
+  color: #546375;
+  font-weight: 600;
+  transition: all .15s ease-in-out;
+}
+
+.product-locale-tabs-nav > li > a:hover,
+.product-locale-tabs-nav > li > a:focus {
+  border-color: #c5d0dc;
+  background: #ffffff;
+  color: #243447;
+}
+
+.product-locale-tabs-nav > li.active > a,
+.product-locale-tabs-nav > li.active > a:hover,
+.product-locale-tabs-nav > li.active > a:focus {
+  border-color: #cfd7e3;
+  border-bottom-color: #ffffff;
+  background: #ffffff;
+  color: #1f2d3d;
+}
+
+.product-locale-tab-title,
+.product-locale-tab-code {
+  display: block;
+}
+
+.product-locale-tab-title {
+  font-size: 13px;
+  line-height: 1.3;
+}
+
+.product-locale-tab-code {
+  margin-top: 3px;
+  font-size: 11px;
+  font-weight: 500;
+  color: #7d8a99;
+  text-transform: uppercase;
+  letter-spacing: .04em;
+}
+
+.product-locale-tabs-content {
+  padding: 0 12px 12px;
+  background: #f6f8fb;
+}
+
+.product-locale-pane {
+  padding: 18px 18px 6px;
+  border: 1px solid #cfd7e3;
+  border-radius: 0 8px 8px 8px;
+  background: #ffffff;
+}
+
+.product-locale-pane .control-label,
+.product-locale-pane .col-sm-2 {
+  font-weight: 600;
+  color: #334155;
+}
+
+.product-locale-pane input[type="text"],
+.product-locale-pane textarea {
+  border-color: #cfd7e3;
+  box-shadow: none;
+}
+
+.product-locale-pane .cke_chrome {
+  border-color: #cfd7e3 !important;
+  box-shadow: none !important;
+}
+
+.product-locale-pane .cke_top {
+  background: #f8fafc !important;
+  border-bottom-color: #e2e8f0 !important;
+}
+
+.product-locale-pane .form-group,
+.product-locale-pane .control-group {
+  margin-left: 0;
+  margin-right: 0;
+  padding-bottom: 6px;
+}
+
+.product-locale-pane .help-block,
+.product-locale-pane .help-inline {
+  color: #7d8a99;
+  font-size: 12px;
+}
 

+ 52 - 15
app/models/product.rb

@@ -37,7 +37,7 @@ class Product < ActiveRecord::Base
     after_create :after_create
     before_save :before_save
     after_destroy :del_picture
-    after_update :clear_product_cache
+    after_commit :clear_product_cache_after_commit
 
     def after_create
         #主商品默认关联商品
@@ -68,12 +68,21 @@ class Product < ActiveRecord::Base
         end
     end
 
-    def clear_product_cache
+    def self.notify_product_cache(product_id)
+        return if product_id.blank?
         #清空商品缓存
-        url = "#{CONFIG_FILE["api_host"]}/railsadmin/clean_cache/product/#{self.id}"
+        url = "#{CONFIG_FILE["api_host"]}/railsadmin/clean_cache/product/#{product_id}"
         p url
         open(url)
     end
+
+    def clear_product_cache
+        Product.notify_product_cache(self.id)
+    end
+
+    def clear_product_cache_after_commit
+        clear_product_cache
+    end
     TYPE_ENUM = [["直营","direct_sale"],["店铺专区","shop_sale"],["积分专区","cent_sale"]]
     SIZE_ENUM =  []
     COLOR_ENUM =  []
@@ -192,7 +201,12 @@ class Product < ActiveRecord::Base
                 end
             end
             #field :robo_balance_price
-            field :user_sale_price
+            field :user_sale_price do
+                label "零售价(元)"
+                formatted_value do
+                    value.to_f / 100
+                end
+            end
             # field :buy_price
             #field :gross_interest_rate
             field :count
@@ -221,7 +235,11 @@ class Product < ActiveRecord::Base
             field :color_name
             field :relate_product_id
             field :show_flag
-            field :live
+            # field :live
+            field :allow_app
+            # field :product_cycle
+            # field :stock_cycle
+            # field :min_purchase
             field :sale_nums
             field :single_purch_limit
             field :package
@@ -243,6 +261,9 @@ class Product < ActiveRecord::Base
             field :merchant_id
             field :merchant
             field :name
+            field :name_en
+            field :name_ru
+            field :name_tw
             # field :ptype, :enum do
             #   enum do
             #     TYPE_ENUM
@@ -253,6 +274,9 @@ class Product < ActiveRecord::Base
             field :product_cat
             field :product_sale_type
             field :detail
+            field :detail_en
+            field :detail_ru
+            field :detail_tw
             field :price do
                 label "现金价格(元)"
                 formatted_value do # used in form views
@@ -261,7 +285,12 @@ class Product < ActiveRecord::Base
             end
             field :robo_balance_price
             field :buy_price
-            field :user_sale_price
+            field :user_sale_price do
+                label "零售价(元)"
+                formatted_value do
+                    value.to_f / 100
+                end
+            end
             field :count
             field :recommend
             field :status
@@ -296,12 +325,13 @@ class Product < ActiveRecord::Base
             field :color_name
             field :relate_product_id
             field :show_flag
-            field :live
+            # field :live
+            field :allow_app
             field :single_purch_limit
             field :package
-            field :product_cycle
-            field :stock_cycle
-            field :min_purchase
+            # field :product_cycle
+            # field :stock_cycle
+            # field :min_purchase
             field :pv do
                 label "pv%"
                 formatted_value do # used in form views
@@ -324,6 +354,9 @@ class Product < ActiveRecord::Base
                 end
             end
             field :name
+            field :name_en
+            field :name_ru
+            field :name_tw
             # field :ptype, :enum do
             #   enum do
             #     TYPE_ENUM
@@ -335,6 +368,9 @@ class Product < ActiveRecord::Base
             field :product_cat
             field :product_sale_type
             field :detail, :ck_editor
+            field :detail_en, :ck_editor
+            field :detail_ru, :ck_editor
+            field :detail_tw, :ck_editor
             field :price
             field :robo_balance_price
             field :buy_price
@@ -378,12 +414,13 @@ class Product < ActiveRecord::Base
 
             field :relate_product_id
             field :show_flag
-            field :live
+            # field :live
+            field :allow_app
             field :single_purch_limit
             field :package
-            field :product_cycle
-            field :stock_cycle
-            field :min_purchase
+            # field :product_cycle
+            # field :stock_cycle
+            # field :min_purchase
             field :pv do
                 label "pv%"
             end
@@ -396,4 +433,4 @@ class Product < ActiveRecord::Base
 
     end
 
-end
+end

+ 5 - 4
app/models/product_item.rb

@@ -2,6 +2,7 @@
 class ProductItem < ActiveRecord::Base
   has_paper_trail
   self.table_name = "product_items"
+  after_commit :clear_product_cache
   validates :product_id,:item_id,:nums, presence: true
   validate :product_validation
 
@@ -26,10 +27,10 @@ class ProductItem < ActiveRecord::Base
         self.item_title=prd.name
       end
     end
-    #清空套装缓存
-    url = "#{CONFIG_FILE["api_host"]}/railsadmin/clean_cache/package/#{self.product_id}"
-    p url
-    open(url)
+  end
+
+  def clear_product_cache
+    Product.notify_product_cache(self.product_id)
   end
 
   rails_admin do

+ 6 - 1
app/models/product_picture.rb

@@ -2,6 +2,7 @@ require 'uuid'
 class ProductPicture < ActiveRecord::Base
     has_paper_trail
     self.table_name = "product_pictures"
+    after_commit :clear_product_cache
     after_destroy :after_destroy
     belongs_to :product
     validates :product_id, presence: true
@@ -98,4 +99,8 @@ class ProductPicture < ActiveRecord::Base
     def after_destroy
         clear_img
     end
-end
+
+    def clear_product_cache
+        Product.notify_product_cache(self.product_id)
+    end
+end

+ 7 - 0
config/initializers/rails_admin.rb

@@ -231,3 +231,10 @@ RailsAdmin.config do |config|
     nestable
   end
 end
+
+# When class reloading is enabled (cache_classes = false), RailsAdmin keeps the
+# model config registry between reloads, which can lead to "can\x27t modify frozen Array"
+# errors when redefining fields. Reset Product config before each request.
+Rails.application.config.to_prepare do
+  RailsAdmin::Config.reset_model("Product")
+end

+ 8 - 1
config/locales/models/product.yml

@@ -6,6 +6,9 @@ zh-CN:
       product:
         id: 商品ID
         name: 商品名称
+        name_en: 商品名称(英文)
+        name_ru: 商品名称(俄文)
+        name_tw: 商品名称(繁体中文)
         ptype: 销售类型
         sale_nums: 销量
         topic_test: 测试
@@ -14,6 +17,9 @@ zh-CN:
         category_id: 类别ID
         product_cat: 商品分类
         detail: 商品详情
+        detail_en: 商品详情(英文)
+        detail_ru: 商品详情(俄文)
+        detail_tw: 商品详情(繁体中文)
         price: 现金价格(分)
         robo_balance_price: 提货券价格(提货券)
         buy_price: 采购价格(分)
@@ -54,6 +60,7 @@ zh-CN:
         relate_product_id: 关联主商品
         show_flag: 主商品标记
         live: 是否直播
+        allow_app: 允许APP销售
         single_purch_limit: 单次限购量
         package: 是否套装
         product_cycle: 生产周期
@@ -66,4 +73,4 @@ zh-CN:
         use_quan: 可用提货券
         sale_zone: 专区ID
         product_sale_type: 专区名称
-        depart_record: 部门
+        depart_record: 部门

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 2176 - 67
db/schema.rb