avo 2.13.6.pre.2 → 2.14.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of avo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -3
- data/README.md +7 -11
- data/app/components/avo/button_component.rb +1 -1
- data/app/components/avo/fields/common/progress_bar_component.html.erb +8 -0
- data/app/components/avo/fields/common/progress_bar_component.rb +15 -0
- data/app/components/avo/fields/common/single_file_viewer_component.html.erb +1 -2
- data/app/components/avo/fields/common/single_file_viewer_component.rb +12 -0
- data/app/components/avo/fields/progress_bar_field/index_component.html.erb +1 -6
- data/app/components/avo/fields/progress_bar_field/show_component.html.erb +1 -6
- data/app/components/avo/filters_component.html.erb +1 -1
- data/app/components/avo/filters_component.rb +11 -1
- data/app/components/avo/index/resource_table_component.html.erb +37 -2
- data/app/components/avo/index/resource_table_component.rb +15 -1
- data/app/components/avo/item_switcher_component.rb +1 -1
- data/app/components/avo/tab_group_component.html.erb +4 -4
- data/app/components/avo/tab_group_component.rb +7 -1
- data/app/components/avo/tab_switcher_component.html.erb +28 -8
- data/app/components/avo/tab_switcher_component.rb +3 -1
- data/app/components/avo/views/resource_index_component.html.erb +3 -6
- data/app/components/avo/views/resource_index_component.rb +3 -2
- data/app/controllers/avo/actions_controller.rb +17 -4
- data/app/helpers/avo/resources_helper.rb +1 -1
- data/app/javascript/js/controllers/action_controller.js +11 -2
- data/app/javascript/js/controllers/item_select_all_controller.js +42 -4
- data/app/javascript/js/controllers/tabs_controller.js +13 -9
- data/app/views/avo/actions/show.html.erb +2 -1
- data/app/views/avo/base/index.html.erb +1 -0
- data/app/views/avo/partials/_table_header.html.erb +1 -1
- data/app/views/avo/partials/_view_toggle_button.html.erb +2 -2
- data/lib/avo/concerns/has_fields.rb +2 -2
- data/lib/avo/configuration.rb +2 -0
- data/lib/avo/fields/base_field.rb +1 -1
- data/lib/avo/fields/concerns/is_required.rb +15 -1
- data/lib/avo/services/encryption_service.rb +47 -0
- data/lib/avo/tab_group.rb +3 -1
- data/lib/avo/tab_group_builder.rb +4 -4
- data/lib/avo/version.rb +1 -1
- data/lib/generators/avo/templates/initializer/avo.tt +1 -0
- data/lib/generators/avo/templates/locales/avo.en.yml +5 -1
- data/lib/generators/avo/templates/locales/avo.fr.yml +4 -0
- data/lib/generators/avo/templates/locales/avo.nb-NO.yml +5 -0
- data/lib/generators/avo/templates/locales/avo.pt-BR.yml +5 -0
- data/lib/generators/avo/templates/locales/avo.ro.yml +6 -0
- data/public/avo-assets/avo.css +44 -4
- data/public/avo-assets/avo.js +43 -43
- data/public/avo-assets/avo.js.map +2 -2
- metadata +7 -5
- data/app/views/avo/partials/_tabs_toggle.html.erb +0 -20
@@ -3,7 +3,7 @@ import { Controller } from '@hotwired/stimulus'
|
|
3
3
|
import { castBoolean } from '../helpers/cast_boolean'
|
4
4
|
|
5
5
|
export default class extends Controller {
|
6
|
-
static targets = ['
|
6
|
+
static targets = ['tabPanel'];
|
7
7
|
|
8
8
|
static values = {
|
9
9
|
view: String,
|
@@ -11,13 +11,17 @@ export default class extends Controller {
|
|
11
11
|
};
|
12
12
|
|
13
13
|
get currentTab() {
|
14
|
-
return this.
|
14
|
+
return this.tabPanelTargets.find(
|
15
15
|
(element) => element.dataset.tabId === this.activeTabValue,
|
16
16
|
)
|
17
17
|
}
|
18
18
|
|
19
19
|
targetTab(id) {
|
20
|
-
return this.tabTargets.find((element) => element.dataset.
|
20
|
+
return this.tabTargets.find((element) => element.dataset.tabsIdParam === id)
|
21
|
+
}
|
22
|
+
|
23
|
+
targetTabPanel(id) {
|
24
|
+
return this.tabPanelTargets.find((element) => element.dataset.tabId === id)
|
21
25
|
}
|
22
26
|
|
23
27
|
changeTab(e) {
|
@@ -46,17 +50,17 @@ export default class extends Controller {
|
|
46
50
|
}
|
47
51
|
|
48
52
|
// We don't need to add a height to this panel because it was loaded before
|
49
|
-
if (castBoolean(this.
|
53
|
+
if (castBoolean(this.targetTabPanel(id).dataset.loaded)) {
|
50
54
|
return
|
51
55
|
}
|
52
56
|
|
53
57
|
// Get the height of the active panel
|
54
58
|
const { height } = this.currentTab.getBoundingClientRect()
|
55
59
|
// Set it to the target panel
|
56
|
-
this.
|
60
|
+
this.targetTabPanel(id).style.height = `${height}px`
|
57
61
|
|
58
62
|
// Wait until the panel loaded it's content and then remove the forced height
|
59
|
-
const observer = new AttributeObserver(this.
|
63
|
+
const observer = new AttributeObserver(this.targetTabPanel(id), 'busy', {
|
60
64
|
elementUnmatchedAttribute: () => {
|
61
65
|
// The content is not available in an instant so delay the height reset a bit.
|
62
66
|
setTimeout(() => {
|
@@ -69,11 +73,11 @@ export default class extends Controller {
|
|
69
73
|
}
|
70
74
|
|
71
75
|
markTabLoaded(id) {
|
72
|
-
this.
|
76
|
+
this.targetTabPanel(id).dataset.loaded = true
|
73
77
|
}
|
74
78
|
|
75
79
|
showTab(id) {
|
76
|
-
this.
|
80
|
+
this.tabPanelTargets.forEach((element) => {
|
77
81
|
if (element.dataset.tabId === id) {
|
78
82
|
element.classList.remove('hidden')
|
79
83
|
}
|
@@ -81,6 +85,6 @@ export default class extends Controller {
|
|
81
85
|
}
|
82
86
|
|
83
87
|
hideTabs() {
|
84
|
-
this.
|
88
|
+
this.tabPanelTargets.map((element) => element.classList.add('hidden'))
|
85
89
|
}
|
86
90
|
}
|
@@ -20,7 +20,8 @@
|
|
20
20
|
<div class="flex-1 flex">
|
21
21
|
<%= @action.message %>
|
22
22
|
</div>
|
23
|
-
<%= form.hidden_field :
|
23
|
+
<%= form.hidden_field :avo_resource_ids, value: params[:resource_ids], 'data-action-target': 'resourceIds' %>
|
24
|
+
<%= form.hidden_field :avo_selected_query, 'data-action-target': 'selectedAllQuery' %>
|
24
25
|
<% if @action.get_fields.present? %>
|
25
26
|
<div class="mt-4">
|
26
27
|
<% @action.get_fields.each_with_index do |field, index| %>
|
@@ -44,7 +44,7 @@
|
|
44
44
|
""
|
45
45
|
end
|
46
46
|
%>
|
47
|
-
<th class="text-left uppercase px-3 py-
|
47
|
+
<th class="text-left uppercase px-3 py-3 whitespace-nowrap rounded-l" data-control="resource-field-th">
|
48
48
|
<% if field.sortable %>
|
49
49
|
<%= link_to params.permit!.merge(sort_by: sort_by, sort_direction: sort_direction), class: "flex items-center #{classes} #{'cursor-pointer' if field.sortable}", 'data-turbo-frame': params[:turbo_frame] do %>
|
50
50
|
<%= field.name %>
|
@@ -20,10 +20,10 @@
|
|
20
20
|
|
21
21
|
<%= a_link url_for(params.permit!.merge(view_type: type)).to_s,
|
22
22
|
icon: info[type][:new_icon],
|
23
|
-
color: is_active_view ? :
|
23
|
+
color: is_active_view ? :primary : :gray,
|
24
24
|
rounded: false,
|
25
25
|
size: :sm,
|
26
|
-
class: is_active_view ?
|
26
|
+
class: is_active_view ? "z-20" : "bg-gray-100 border-gray-300",
|
27
27
|
title: t('avo.switch_to_view', view_type: type),
|
28
28
|
data: {
|
29
29
|
tippy: 'tooltip',
|
@@ -26,10 +26,10 @@ module Avo
|
|
26
26
|
items_holder.panel name, **args, &block
|
27
27
|
end
|
28
28
|
|
29
|
-
def tabs(&block)
|
29
|
+
def tabs(**args, &block)
|
30
30
|
ensure_items_holder_initialized
|
31
31
|
|
32
|
-
items_holder.tabs Avo::TabGroupBuilder.parse_block(&block)
|
32
|
+
items_holder.tabs Avo::TabGroupBuilder.parse_block(**args, &block)
|
33
33
|
end
|
34
34
|
|
35
35
|
def tool(klass, **args)
|
data/lib/avo/configuration.rb
CHANGED
@@ -34,6 +34,7 @@ module Avo
|
|
34
34
|
attr_accessor :buttons_on_form_footers
|
35
35
|
attr_accessor :main_menu
|
36
36
|
attr_accessor :profile_menu
|
37
|
+
attr_accessor :tabs_style
|
37
38
|
|
38
39
|
def initialize
|
39
40
|
@root_path = "/avo"
|
@@ -78,6 +79,7 @@ module Avo
|
|
78
79
|
@buttons_on_form_footers = false
|
79
80
|
@main_menu = nil
|
80
81
|
@profile_menu = nil
|
82
|
+
@tabs_style = :tabs
|
81
83
|
end
|
82
84
|
|
83
85
|
def current_user_method(&block)
|
@@ -61,7 +61,7 @@ module Avo
|
|
61
61
|
@name = args[:name]
|
62
62
|
@translation_key = args[:translation_key]
|
63
63
|
@block = block
|
64
|
-
@required = args
|
64
|
+
@required = args.dig(:required) # Value if :required present on args, nil otherwise
|
65
65
|
@readonly = args[:readonly] || false
|
66
66
|
@sortable = args[:sortable] || false
|
67
67
|
@nullable = args[:nullable] || false
|
@@ -8,9 +8,23 @@ module Avo
|
|
8
8
|
if required.respond_to? :call
|
9
9
|
Avo::Hosts::ViewRecordHost.new(block: required, record: model, view: view).handle
|
10
10
|
else
|
11
|
-
required
|
11
|
+
required.nil? ? required_from_validators : required
|
12
12
|
end
|
13
13
|
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def required_from_validators
|
18
|
+
return false if model.nil?
|
19
|
+
|
20
|
+
validators.any? do |validator|
|
21
|
+
validator.kind_of? ActiveModel::Validations::PresenceValidator
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def validators
|
26
|
+
model.class.validators_on(id)
|
27
|
+
end
|
14
28
|
end
|
15
29
|
end
|
16
30
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Avo
|
2
|
+
module Services
|
3
|
+
class EncryptionService
|
4
|
+
attr_reader :message
|
5
|
+
attr_reader :purpose
|
6
|
+
attr_reader :crypt
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def encrypt(message:, purpose:)
|
10
|
+
new(message: message, purpose: purpose).encrypt
|
11
|
+
end
|
12
|
+
|
13
|
+
def decrypt(message:, purpose:)
|
14
|
+
new(message: message, purpose: purpose).decrypt
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(message:, purpose:)
|
19
|
+
@message = message
|
20
|
+
@purpose = purpose
|
21
|
+
@crypt = ActiveSupport::MessageEncryptor.new(encryption_key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def encrypt
|
25
|
+
crypt.encrypt_and_sign(message, purpose: purpose)
|
26
|
+
end
|
27
|
+
|
28
|
+
def decrypt
|
29
|
+
crypt.decrypt_and_verify(message, purpose: purpose)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def encryption_key
|
35
|
+
secret_key_base[0..31]
|
36
|
+
rescue
|
37
|
+
# This will fail the decryption process.
|
38
|
+
# It's here only to keep Avo from crashing
|
39
|
+
SecureRandom.random_bytes(32)
|
40
|
+
end
|
41
|
+
|
42
|
+
def secret_key_base
|
43
|
+
Rails.application.secrets.secret_key_base || ENV['SECRET_KEY_BASE']
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/avo/tab_group.rb
CHANGED
@@ -7,11 +7,13 @@ class Avo::TabGroup
|
|
7
7
|
attr_reader :view
|
8
8
|
attr_accessor :index
|
9
9
|
attr_accessor :items_holder
|
10
|
+
attr_accessor :style
|
10
11
|
|
11
|
-
def initialize(index: 0, view: nil)
|
12
|
+
def initialize(index: 0, view: nil, style: nil)
|
12
13
|
@index = index
|
13
14
|
@items_holder = Avo::ItemsHolder.new
|
14
15
|
@view = view
|
16
|
+
@style = style
|
15
17
|
end
|
16
18
|
|
17
19
|
def hydrate(view: nil)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Avo::TabGroupBuilder
|
2
2
|
class << self
|
3
|
-
def parse_block(&block)
|
4
|
-
Docile.dsl_eval(new, &block).build
|
3
|
+
def parse_block(**args, &block)
|
4
|
+
Docile.dsl_eval(new(**args), &block).build
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
@@ -9,8 +9,8 @@ class Avo::TabGroupBuilder
|
|
9
9
|
|
10
10
|
delegate :tab, to: :items_holder
|
11
11
|
|
12
|
-
def initialize
|
13
|
-
@group = Avo::TabGroup.new
|
12
|
+
def initialize(style: nil)
|
13
|
+
@group = Avo::TabGroup.new(style: style)
|
14
14
|
@items_holder = Avo::ItemsHolder.new
|
15
15
|
end
|
16
16
|
|
data/lib/avo/version.rb
CHANGED
@@ -23,7 +23,9 @@ en:
|
|
23
23
|
you_missed_something_check_form: 'You might have missed something. Please check the form.'
|
24
24
|
remove_selection: 'Remove selection'
|
25
25
|
select_item: 'Select item'
|
26
|
-
select_all: 'Select all
|
26
|
+
select_all: 'Select all'
|
27
|
+
select_all_matching: 'Select all matching'
|
28
|
+
undo: undo
|
27
29
|
delete_file: 'Delete file'
|
28
30
|
delete: 'delete'
|
29
31
|
delete_item: 'Delete %{item}'
|
@@ -113,3 +115,5 @@ en:
|
|
113
115
|
empty_dashboard_message: Add cards to this dashboard
|
114
116
|
no_cards_present: No cards present
|
115
117
|
no_options_available: No options available
|
118
|
+
x_records_selected_from_a_total_of_x_html: <span class="font-bold text-gray-700">%{selected}</span> records selected on this page from a total of <span class="font-bold text-gray-700">%{count}</span>
|
119
|
+
x_records_selected_from_all_pages_html: <span class="font-bold text-gray-700">%{count}</span> records selected from all pages
|
@@ -24,6 +24,8 @@ fr:
|
|
24
24
|
remove_selection: 'Supprimer la sélection'
|
25
25
|
select_item: 'Sélectionnez un élément'
|
26
26
|
select_all: 'Sélectionner tout sur la page'
|
27
|
+
select_all_matching: 'Sélectionner toutes les correspondances'
|
28
|
+
undo: annuler
|
27
29
|
delete_file: 'Supprimer le fichier'
|
28
30
|
delete: 'supprimer'
|
29
31
|
delete_item: 'Supprimer %{item}'
|
@@ -113,3 +115,5 @@ fr:
|
|
113
115
|
empty_dashboard_message: Ajouter des cartes à ce tableau de bord
|
114
116
|
no_cards_present: Aucune carte présente
|
115
117
|
no_options_available: Aucune option disponible
|
118
|
+
x_records_selected_from_a_total_of_x_html: <span class="font-bold text-gray-700">%{selected}</span> enregistrements sélectionnés sur cette page sur un total de <span class="font-bold text-gray-700">%{count}</span>
|
119
|
+
x_records_selected_from_all_pages_html: <span class="font-bold text-gray-700">%{count}</span> enregistrements sélectionnés dans toutes les pages
|
@@ -23,6 +23,9 @@ nb-NO:
|
|
23
23
|
you_missed_something_check_form: 'Her mangler du noe. Vennligst sjekk skjemaet.'
|
24
24
|
remove_selection: 'Fjern valg'
|
25
25
|
select_item: 'Velg'
|
26
|
+
select_all: 'Velg alle'
|
27
|
+
select_all_matching: 'Velg alle samsvarende'
|
28
|
+
undo: angre
|
26
29
|
delete_file: 'Slett fil'
|
27
30
|
delete: 'slett'
|
28
31
|
delete_item: 'Slett %{item}'
|
@@ -85,4 +88,6 @@ nb-NO:
|
|
85
88
|
empty_dashboard_message: Legg til kort i dette dashbordet
|
86
89
|
no_cards_present: Ingen kort til stede
|
87
90
|
no_options_available: Ingen tilgjengelige alternativer
|
91
|
+
x_records_selected_from_a_total_of_x_html: <span class="font-bold text-gray-700">%{selected}</span> poster valgt på denne siden fra totalt <span class="font-bold text-gray-700">%{count}</span>
|
92
|
+
x_records_selected_from_all_pages_html: <span class="font-bold text-gray-700">%{count}</span> poster valgt fra alle sider
|
88
93
|
|
@@ -23,6 +23,9 @@ pt-BR:
|
|
23
23
|
you_missed_something_check_form: 'Você pode ter esquecido algo. Por favor, cheque o formulário.'
|
24
24
|
remove_selection: 'Remover seleção'
|
25
25
|
select_item: 'Selecionar item'
|
26
|
+
select_all: 'Selecionar tudo'
|
27
|
+
select_all_matching: 'Selecione todas as correspondências'
|
28
|
+
undo: desfazer
|
26
29
|
delete_file: 'Deletar arquivo'
|
27
30
|
delete: 'deletar'
|
28
31
|
delete_item: 'Deletar %{item}'
|
@@ -87,3 +90,5 @@ pt-BR:
|
|
87
90
|
empty_dashboard_message: Adicionar cartões a este painel
|
88
91
|
no_cards_present: Nenhum cartão presente
|
89
92
|
no_options_available: Nenhuma opção disponível
|
93
|
+
x_records_selected_from_a_total_of_x_html: <span class="font-bold text-gray-700">%{selected}</span> registros selecionados nesta página de um total de <span class="font-bold text-gray-700">%{count}</span>
|
94
|
+
x_records_selected_from_all_pages_html: <span class="font-bold text-gray-700">%{count}</span> registros selecionados de todas as páginas
|
@@ -16,6 +16,10 @@ ro:
|
|
16
16
|
resource_created: 'Resursa creata'
|
17
17
|
resource_destroyed: 'Resursa stearsa'
|
18
18
|
remove_selection: 'Sterge selectia'
|
19
|
+
select_item: 'Selecteaza record'
|
20
|
+
select_all: 'Selecteaza totul'
|
21
|
+
select_all_matching: 'Selecteaza toate care se potrivesc'
|
22
|
+
undo: Anuleaza
|
19
23
|
delete_file: 'Sterge fisierul'
|
20
24
|
delete: 'sterge'
|
21
25
|
delete_item: 'Sterge %{item}'
|
@@ -83,3 +87,5 @@ ro:
|
|
83
87
|
empty_dashboard_message: Adauga carduri in acest dashboard
|
84
88
|
no_cards_present: Niciun card disponibil
|
85
89
|
no_options_available: Nicio optiune disponibila
|
90
|
+
x_records_selected_from_a_total_of_x_html: <span class="font-bold text-gray-700">%{selected}</span> selectate dintr-un total de <span class="font-bold text-gray-700">%{count}</span>
|
91
|
+
x_records_selected_from_all_pages_html: <span class="font-bold text-gray-700">%{count}</span> selectate din toate paginile
|
data/public/avo-assets/avo.css
CHANGED
@@ -6315,6 +6315,10 @@ trix-editor .attachment__metadata .attachment__size {
|
|
6315
6315
|
top:-1px
|
6316
6316
|
}
|
6317
6317
|
|
6318
|
+
.-top-\[0\.1rem\]{
|
6319
|
+
top:-0.1rem
|
6320
|
+
}
|
6321
|
+
|
6318
6322
|
.right-3{
|
6319
6323
|
right:0.75rem
|
6320
6324
|
}
|
@@ -6527,6 +6531,26 @@ trix-editor .attachment__metadata .attachment__size {
|
|
6527
6531
|
margin-left:50%
|
6528
6532
|
}
|
6529
6533
|
|
6534
|
+
.ml-10{
|
6535
|
+
margin-left:2.5rem
|
6536
|
+
}
|
6537
|
+
|
6538
|
+
.mt-0\.5{
|
6539
|
+
margin-top:0.125rem
|
6540
|
+
}
|
6541
|
+
|
6542
|
+
.mt-0{
|
6543
|
+
margin-top:0px
|
6544
|
+
}
|
6545
|
+
|
6546
|
+
.mt-1\.5{
|
6547
|
+
margin-top:0.375rem
|
6548
|
+
}
|
6549
|
+
|
6550
|
+
.ml-2{
|
6551
|
+
margin-left:0.5rem
|
6552
|
+
}
|
6553
|
+
|
6530
6554
|
.mr-0\.5{
|
6531
6555
|
margin-right:0.125rem
|
6532
6556
|
}
|
@@ -6547,10 +6571,6 @@ trix-editor .attachment__metadata .attachment__size {
|
|
6547
6571
|
margin-bottom:-0.5rem
|
6548
6572
|
}
|
6549
6573
|
|
6550
|
-
.mt-0{
|
6551
|
-
margin-top:0px
|
6552
|
-
}
|
6553
|
-
|
6554
6574
|
.mt-6{
|
6555
6575
|
margin-top:1.5rem
|
6556
6576
|
}
|
@@ -6687,6 +6707,10 @@ trix-editor .attachment__metadata .attachment__size {
|
|
6687
6707
|
height:2.5rem
|
6688
6708
|
}
|
6689
6709
|
|
6710
|
+
.h-9{
|
6711
|
+
height:2.25rem
|
6712
|
+
}
|
6713
|
+
|
6690
6714
|
.max-h-\[42rem\]{
|
6691
6715
|
max-height:42rem
|
6692
6716
|
}
|
@@ -6795,6 +6819,14 @@ trix-editor .attachment__metadata .attachment__size {
|
|
6795
6819
|
min-width:20px
|
6796
6820
|
}
|
6797
6821
|
|
6822
|
+
.min-w-\[2rem\]{
|
6823
|
+
min-width:2rem
|
6824
|
+
}
|
6825
|
+
|
6826
|
+
.min-w-\[6rem\]{
|
6827
|
+
min-width:6rem
|
6828
|
+
}
|
6829
|
+
|
6798
6830
|
.max-w-168{
|
6799
6831
|
max-width:42rem
|
6800
6832
|
}
|
@@ -7723,6 +7755,10 @@ trix-editor .attachment__metadata .attachment__size {
|
|
7723
7755
|
padding-bottom:75%
|
7724
7756
|
}
|
7725
7757
|
|
7758
|
+
.pt-px{
|
7759
|
+
padding-top:1px
|
7760
|
+
}
|
7761
|
+
|
7726
7762
|
.pr-2{
|
7727
7763
|
padding-right:0.5rem
|
7728
7764
|
}
|
@@ -9181,6 +9217,10 @@ trix-editor {
|
|
9181
9217
|
outline-width:1px
|
9182
9218
|
}
|
9183
9219
|
|
9220
|
+
.active\:outline-0:active{
|
9221
|
+
outline-width:0px
|
9222
|
+
}
|
9223
|
+
|
9184
9224
|
.active\:outline-slate-500:active{
|
9185
9225
|
outline-color:#64748b
|
9186
9226
|
}
|