added frontend and changed design

This commit is contained in:
rulingcom 2025-05-27 22:38:04 +08:00
parent 2fea8b3600
commit 36c31c0240
16 changed files with 633 additions and 227 deletions

View File

@ -1,4 +1,10 @@
import { __version__, logger, EventType, Direction, LogLevel } from './jsmind.common.js'
import {
__version__,
logger,
EventType,
Direction,
LogLevel,
} from './jsmind.common.js'
import { merge_option } from './jsmind.option.js'
import { Mind } from './jsmind.mind.js'
import { Node } from './jsmind.node.js'
@ -6,7 +12,11 @@ import { DataProvider } from './jsmind.data_provider.js'
import { LayoutProvider } from './jsmind.layout_provider.js'
import { ViewProvider } from './jsmind.view_provider.js'
import { ShortcutProvider } from './jsmind.shortcut_provider.js'
import { Plugin, register as _register_plugin, apply as apply_plugins } from './jsmind.plugin.js'
import {
Plugin,
register as _register_plugin,
apply as apply_plugins,
} from './jsmind.plugin.js'
import { format } from './jsmind.format.js'
import { $ } from './jsmind.dom.js'
import { util as _util } from './jsmind.util.js'
@ -48,7 +58,8 @@ export default class jsMind {
container: this.options.container,
support_html: this.options.support_html,
engine: this.options.view.engine,
enable_device_pixel_ratio: this.options.view.enable_device_pixel_ratio,
enable_device_pixel_ratio:
this.options.view.enable_device_pixel_ratio,
hmargin: this.options.view.hmargin,
vmargin: this.options.view.vmargin,
line_width: this.options.view.line_width,
@ -56,7 +67,8 @@ export default class jsMind {
line_style: this.options.view.line_style,
custom_line_render: this.options.view.custom_line_render,
draggable: this.options.view.draggable,
hide_scrollbars_when_draggable: this.options.view.hide_scrollbars_when_draggable,
hide_scrollbars_when_draggable:
this.options.view.hide_scrollbars_when_draggable,
node_overflow: this.options.view.node_overflow,
zoom: this.options.view.zoom,
custom_node_render: this.options.view.custom_node_render,
@ -99,11 +111,15 @@ export default class jsMind {
}
// options are 'mousedown', 'click', 'dblclick', 'mousewheel'
enable_event_handle(event_handle) {
this.options.default_event_handle['enable_' + event_handle + '_handle'] = true
this.options.default_event_handle[
'enable_' + event_handle + '_handle'
] = true
}
// options are 'mousedown', 'click', 'dblclick', 'mousewheel'
disable_event_handle(event_handle) {
this.options.default_event_handle['enable_' + event_handle + '_handle'] = false
this.options.default_event_handle[
'enable_' + event_handle + '_handle'
] = false
}
set_theme(theme) {
var theme_old = this.options.theme
@ -164,7 +180,10 @@ export default class jsMind {
// Use [Ctrl] + Mousewheel, to zoom in/out.
mousewheel_handle(e) {
// Test if mousewheel option is enabled and Ctrl key is pressed.
if (!this.options.default_event_handle['enable_mousewheel_handle'] || !e.ctrlKey) {
if (
!this.options.default_event_handle['enable_mousewheel_handle'] ||
!e.ctrlKey
) {
return
}
var evt = e || event
@ -318,9 +337,16 @@ export default class jsMind {
var the_parent_node = this.get_node(parent_node)
var dir = Direction.of(direction)
if (dir === undefined) {
dir = this.layout.calculate_next_child_direction(the_parent_node)
dir =
this.layout.calculate_next_child_direction(the_parent_node)
}
var node = this.mind.add_node(the_parent_node, node_id, topic, data, dir)
var node = this.mind.add_node(
the_parent_node,
node_id,
topic,
data,
dir
)
if (!!node) {
this.view.add_node(node)
this.layout.layout()
@ -344,9 +370,17 @@ export default class jsMind {
var the_node_before = this.get_node(node_before)
var dir = Direction.of(direction)
if (dir === undefined) {
dir = this.layout.calculate_next_child_direction(the_node_before.parent)
dir = this.layout.calculate_next_child_direction(
the_node_before.parent
)
}
var node = this.mind.insert_node_before(the_node_before, node_id, topic, data, dir)
var node = this.mind.insert_node_before(
the_node_before,
node_id,
topic,
data,
dir
)
if (!!node) {
this.view.add_node(node)
this.layout.layout()
@ -368,9 +402,17 @@ export default class jsMind {
var the_node_after = this.get_node(node_after)
var dir = Direction.of(direction)
if (dir === undefined) {
dir = this.layout.calculate_next_child_direction(the_node_after.parent)
dir = this.layout.calculate_next_child_direction(
the_node_after.parent
)
}
var node = this.mind.insert_node_after(the_node_after, node_id, topic, data, dir)
var node = this.mind.insert_node_after(
the_node_after,
node_id,
topic,
data,
dir
)
if (!!node) {
this.view.add_node(node)
this.layout.layout()
@ -453,7 +495,12 @@ export default class jsMind {
move_node(node_id, before_id, parent_id, direction) {
if (this.get_editable()) {
var node = this.get_node(node_id)
var updated_node = this.mind.move_node(node, before_id, parent_id, direction)
var updated_node = this.mind.move_node(
node,
before_id,
parent_id,
direction
)
if (!!updated_node) {
this.view.update_node(updated_node)
this.layout.layout()
@ -484,7 +531,11 @@ export default class jsMind {
}
this.mind.selected = node
this.view.select_node(node)
this.invoke_event_handle(EventType.select, { evt: 'select_node', data: [], node: node.id })
this.invoke_event_handle(EventType.select, {
evt: 'select_node',
data: [],
node: node.id,
})
}
get_selected_node() {
if (!!this.mind) {

View File

@ -44,14 +44,21 @@ Mind.prototype.add_node = function (
parent_node,
node_id,
topic,
data = {},
data = undefined,
direction,
expanded,
idx
) {
for (let style of ['leading-line-color', 'background-color', 'foreground-color']) {
if (parent_node.data?.[style]) {
data[style] = parent_node.data[style]
if (data == undefined) {
data = {}
for (let style of [
'leading-line-color',
'background-color',
'foreground-color',
]) {
if (data[style] == undefined && parent_node.data?.[style]) {
data[style] = parent_node.data[style]
}
}
}
arguments[3] = data

View File

@ -1,13 +1,29 @@
class Admin::MindMapsController < OrbitAdminController
def landing_page
table = UTable.find(params[:id])
if table.mind_map.nil?
@mind_map = MindMap.new
@mind_map.u_table = table
@mind_map.save
else
@mind_map = table.mind_map
end
def index
@table_fields = ["universal_table.mind_map","universal_table.created_time"]
@table = UTable.find(params[:id])
@mind_maps = Kaminari.paginate_array(@table.mind_maps).page(params[:page]).per(10)
end
def new
@table = UTable.find(params[:table])
@mind_map = MindMap.new
end
def edit
uid = params[:id].split("-").last
@mind_map = MindMap.where(:uid => uid).first
@table = @mind_map.u_table
end
def create
mind_map = MindMap.new
mind_params = mind_map_params
mind_params[:mind_map_data] = JSON.parse(mind_params[:mind_map_data])
mind_map.update_attributes(mind_map_params)
mind_map.save
redirect_to "/admin/universal_table/#{mind_map.u_table.id.to_s}/mind_maps"
end
def update
@ -16,9 +32,17 @@ class Admin::MindMapsController < OrbitAdminController
mind_params[:mind_map_data] = JSON.parse(mind_params[:mind_map_data])
mind_map.update_attributes(mind_map_params)
mind_map.save
redirect_to admin_universal_tables_path
redirect_to "/admin/universal_table/#{mind_map.u_table.id.to_s}/mind_maps"
end
def destroy
uid = params[:id].split("-").last
mind_map = MindMap.where(:uid => uid).first
table = mind_map.u_table
mind_map.destroy
redirect_to "/admin/universal_table/#{table.id.to_s}/mind_maps"
end
private
def mind_map_params

View File

@ -1,180 +1,13 @@
class UniversalTablesController < ApplicationController
include Admin::UniversalTablesHelper
FrontendMethods = ["mind_map"]
def index
params = OrbitHelper.params
table = UTable.where(:category_id => OrbitHelper.page_categories.first).first rescue nil
page = OrbitHelper.page rescue Page.where(:page_id => params[:page_id]).first
searchable_columns = []
if !table.nil?
reset = "hide"
csrf_value = (0...46).map { ('a'..'z').to_a[rand(26)] }.join
params_q = params["q"].to_h.each{|_, v| v.gsub!("\"", '')} rescue {}
params_no = params["page_no"].to_s.gsub("\"",'')
if params_q.present?
query_string = '&' + params_q.map{|k, v| ["q[#{k}]",v]}.to_h.to_query
else
query_string = ''
end
query_string += "&page_no=#{params_no}" if params_no.present?
# csrf_input = "<input type=\"hidden\" name=\"authenticity_token\" value=\"#{csrf_value}\">"
csrf_input = ""
have_serial_number = (page.layout != 'index1')
table_heads = table.table_columns.where(:display_in_index => true).asc(:order).collect do |tc|
field_key = tc.key
field_value = params_q[field_key]
field_value = nil if field_value.blank?
search = ""
sort_class = "sort"
sort = ""
form_field = "#{csrf_input}<input type=\"search\" class=\"form-control\" name=\"q[#{field_key}]\" value=\"#{field_value}\" placeholder=\"Search keyword\">"
sort_url = "/#{I18n.locale.to_s}#{page.url}?sortcolumn=#{field_key}&sort=asc#{query_string}"
title_class = ""
case tc.type
when "date"
search = "hide"
when "period","image"
sort_class = "sort hide"
search = "hide"
sort_url = "#"
sort = "hide"
when "editor"
sort_url = "#"
sort = "hide"
sort_class = "sort hide"
when "text"
if tc.make_categorizable
select_values = tc.column_entries.distinct("text.#{I18n.locale.to_s}")
form_field = "#{csrf_input}<select class=\"form-control\" name=\"q[#{field_key}]\">"
if field_value.nil?
select_values.insert(0, "")
end
select_values.each do |sv|
if field_value && sv == field_value
selected = " selected"
else
selected = ""
end
form_field += "<option value=\"#{sv}\"#{selected}>#{sv}</option>"
end
form_field += "</select>"
end
when "integer"
if tc.make_categorizable
select_values = tc.column_entries.distinct(:number)
form_field = "#{csrf_input}<select class=\"form-control\" name=\"q[#{field_key}]\">"
if field_value.nil?
select_values.insert(0, "")
end
select_values.each do |sv|
if field_value && sv == field_value
selected = " selected"
else
selected = ""
end
form_field += "<option value=\"#{sv}\"#{selected}>#{sv}</option>"
end
form_field += "</select>"
end
end
if params["sortcolumn"] == field_key
sort_class = params["sort"] == "asc" ? "sort-desc" : "sort-asc"
sort_url = "/#{I18n.locale.to_s}#{page.url}?sortcolumn=#{field_key}&sort=#{params["sort"] == "asc" ? "desc" : "asc"}#{query_string}"
end
title_class = title_class + "no-sort" if sort == "sort"
title_class = title_class + " no-search" if search == "hide"
col_class = 'col-md-4 col-xs-12'
col = {
"title" => tc.title,
"type" => tc.type,
"key" => field_key,
"search" => search,
"form-field" => form_field,
"sort" => sort,
"sort-class" => sort_class,
"sort-url" => sort_url,
"title-class" => title_class,
"col-class" => col_class
}
if tc.is_searchable
searchable_columns << col
end
col
end
tablecolumns = table.table_columns.where(:display_in_index => true).asc(:order)
rows = []
entries = get_entries(params, table, page)
total_pages = entries.total_pages
if have_serial_number
page_no_offset = (params_no.present? ? [0, params_no.to_i - 1].max : 0)
serial_number_count = page_no_offset * OrbitHelper.page_data_count
table_heads.insert(0, {
"title" => "No.",
"type" => "",
"key" => "",
"search" => "hide",
"form-field" => "",
"sort" => "hide",
"sort-class" => "sort hide",
"sort-url" => "",
"title-class" => " no-search"
})
end
entries.each do |te|
cols = []
sort_value = ""
if have_serial_number
serial_number_count += 1
cols << {"text" => serial_number_count.to_s}
end
tablecolumns.each do |column|
ce = te.column_entries.where(:table_column_id => column.id).first rescue nil
if !ce.nil?
text = ce.get_frontend_text(column)
if column.is_link_to_show
text = "<a href='#{OrbitHelper.url_to_show("-" + te.uid)}'>#{text}</a>"
end
cols << {"text" => text}
else
cols << {"text" => ""}
end
end
rows << {
"columns" => cols
}
end
if params[:layout_type] == "mindmap"
index_mind_maps
else
index_entries
end
export_button = ""
if get_query(params).present?
reset = ""
if !OrbitHelper.current_user.nil?
p = {}
params.each do |k,v|
p[k] = v if ["q","column","sort","sortcolumn"].include?(k)
end
export_button = "<a href='/xhr/universal_table/export.xlsx?cat=#{OrbitHelper.page_categories.first}&page_id=#{page.page_id}&#{p.to_param}'>Export</a>"
end
total_entries = t("universal_table.total_number_of_entries", :total_number => entries.total_count)
elsif params["sortcolumn"].present?
reset = ""
end
{
"searchable-columns" => searchable_columns,
"head-columns" => table_heads,
"rows" => rows,
"total_pages" => total_pages,
"extras" => {
"total_entries" => total_entries,
"table-name" => table.title,
"export_button" => export_button,
"reset" => reset,
"url" => "/#{I18n.locale.to_s}#{page.url}"
}
}
end
def export_filtered
@ -352,7 +185,210 @@ class UniversalTablesController < ApplicationController
end
end
def mind_map
def index_entries
params = OrbitHelper.params
table = UTable.where(:category_id => OrbitHelper.page_categories.first).first rescue nil
page = OrbitHelper.page rescue Page.where(:page_id => params[:page_id]).first
searchable_columns = []
if !table.nil?
reset = "hide"
csrf_value = (0...46).map { ('a'..'z').to_a[rand(26)] }.join
params_q = params["q"].to_h.each{|_, v| v.gsub!("\"", '')} rescue {}
params_no = params["page_no"].to_s.gsub("\"",'')
if params_q.present?
query_string = '&' + params_q.map{|k, v| ["q[#{k}]",v]}.to_h.to_query
else
query_string = ''
end
query_string += "&page_no=#{params_no}" if params_no.present?
# csrf_input = "<input type=\"hidden\" name=\"authenticity_token\" value=\"#{csrf_value}\">"
csrf_input = ""
have_serial_number = (page.layout != 'index1')
table_heads = table.table_columns.where(:display_in_index => true).asc(:order).collect do |tc|
field_key = tc.key
field_value = params_q[field_key]
field_value = nil if field_value.blank?
search = ""
sort_class = "sort"
sort = ""
form_field = "#{csrf_input}<input type=\"search\" class=\"form-control\" name=\"q[#{field_key}]\" value=\"#{field_value}\" placeholder=\"Search keyword\">"
sort_url = "/#{I18n.locale.to_s}#{page.url}?sortcolumn=#{field_key}&sort=asc#{query_string}"
title_class = ""
case tc.type
when "date"
search = "hide"
when "period","image"
sort_class = "sort hide"
search = "hide"
sort_url = "#"
sort = "hide"
when "editor"
sort_url = "#"
sort = "hide"
sort_class = "sort hide"
when "text"
if tc.make_categorizable
select_values = tc.column_entries.distinct("text.#{I18n.locale.to_s}")
form_field = "#{csrf_input}<select class=\"form-control\" name=\"q[#{field_key}]\">"
if field_value.nil?
select_values.insert(0, "")
end
select_values.each do |sv|
if field_value && sv == field_value
selected = " selected"
else
selected = ""
end
form_field += "<option value=\"#{sv}\"#{selected}>#{sv}</option>"
end
form_field += "</select>"
end
when "integer"
if tc.make_categorizable
select_values = tc.column_entries.distinct(:number)
form_field = "#{csrf_input}<select class=\"form-control\" name=\"q[#{field_key}]\">"
if field_value.nil?
select_values.insert(0, "")
end
select_values.each do |sv|
if field_value && sv == field_value
selected = " selected"
else
selected = ""
end
form_field += "<option value=\"#{sv}\"#{selected}>#{sv}</option>"
end
form_field += "</select>"
end
end
if params["sortcolumn"] == field_key
sort_class = params["sort"] == "asc" ? "sort-desc" : "sort-asc"
sort_url = "/#{I18n.locale.to_s}#{page.url}?sortcolumn=#{field_key}&sort=#{params["sort"] == "asc" ? "desc" : "asc"}#{query_string}"
end
title_class = title_class + "no-sort" if sort == "sort"
title_class = title_class + " no-search" if search == "hide"
col_class = 'col-md-4 col-xs-12'
col = {
"title" => tc.title,
"type" => tc.type,
"key" => field_key,
"search" => search,
"form-field" => form_field,
"sort" => sort,
"sort-class" => sort_class,
"sort-url" => sort_url,
"title-class" => title_class,
"col-class" => col_class
}
if tc.is_searchable
searchable_columns << col
end
col
end
tablecolumns = table.table_columns.where(:display_in_index => true).asc(:order)
rows = []
entries = get_entries(params, table, page)
total_pages = entries.total_pages
if have_serial_number
page_no_offset = (params_no.present? ? [0, params_no.to_i - 1].max : 0)
serial_number_count = page_no_offset * OrbitHelper.page_data_count
table_heads.insert(0, {
"title" => "No.",
"type" => "",
"key" => "",
"search" => "hide",
"form-field" => "",
"sort" => "hide",
"sort-class" => "sort hide",
"sort-url" => "",
"title-class" => " no-search"
})
end
entries.each do |te|
cols = []
sort_value = ""
if have_serial_number
serial_number_count += 1
cols << {"text" => serial_number_count.to_s}
end
tablecolumns.each do |column|
ce = te.column_entries.where(:table_column_id => column.id).first rescue nil
if !ce.nil?
text = ce.get_frontend_text(column)
if column.is_link_to_show
text = "<a href='#{OrbitHelper.url_to_show("-" + te.uid)}'>#{text}</a>"
end
cols << {"text" => text}
else
cols << {"text" => ""}
end
end
rows << {
"columns" => cols
}
end
end
export_button = ""
if get_query(params).present?
reset = ""
if !OrbitHelper.current_user.nil?
p = {}
params.each do |k,v|
p[k] = v if ["q","column","sort","sortcolumn"].include?(k)
end
export_button = "<a href='/xhr/universal_table/export.xlsx?cat=#{OrbitHelper.page_categories.first}&page_id=#{page.page_id}&#{p.to_param}'>Export</a>"
end
total_entries = t("universal_table.total_number_of_entries", :total_number => entries.total_count)
elsif params["sortcolumn"].present?
reset = ""
end
{
"searchable-columns" => searchable_columns,
"head-columns" => table_heads,
"rows" => rows,
"total_pages" => total_pages,
"extras" => {
"total_entries" => total_entries,
"table-name" => table.title,
"export_button" => export_button,
"reset" => reset,
"url" => "/#{I18n.locale.to_s}#{page.url}"
}
}
end
def index_mind_maps
params = OrbitHelper.params
table = UTable.where(:category_id => OrbitHelper.page_categories.first).first rescue nil
page = OrbitHelper.page rescue Page.where(:page_id => params[:page_id]).first
mindmaps = []
mms = Kaminari.paginate_array(table.mind_maps).page(params["page_no"]).per(OrbitHelper.page_data_count)
mms.each do |mindmap|
mindmaps << {
"title" => mindmap.title,
"url" => OrbitHelper.url_to_show("-" + mindmap.uid) + "?method=mind_map"
}
end
{
"mindmaps" => mindmaps,
"total_pages" => mms.total_pages,
"extras" => {
"table-name" => table.title
}
}
end
def mind_map
params = OrbitHelper.params
mindmap = MindMap.where(:uid => params["uid"]).first
{
"title" => mindmap.title,
"mind_map_data" => mindmap.mind_map_data
}
end
end

View File

@ -1,15 +0,0 @@
class MindMapNode
include Mongoid::Document
include Mongoid::Timestamps
field :node_id
field :parent_node_id
field :text, type: String
field :expanded, type: Boolean
field :background_color, type: String
field :foreground_color, type: String
field :leading_line_color, type: String
field :link, type: String
belongs_to :mind_map
end

View File

@ -14,7 +14,7 @@ class UTable
has_many :table_columns, :dependent => :destroy
has_many :table_entries, :dependent => :destroy
has_one :mind_map, :dependent => :destroy
has_many :mind_maps, :dependent => :destroy
accepts_nested_attributes_for :table_columns, :allow_destroy => true

View File

@ -1,6 +1,6 @@
<fieldset class="utable-heading-wrap">
<div class="utable-heading-header">
<h4><%= t("universal_table.table_name") %> - <%= @mind_map.u_table.title %></h4>
<h4><%= t("universal_table.table_name") %> - <%= @table.title %></h4>
</div>
<div class="control-group">
<div class="controls">
@ -38,7 +38,8 @@
</fieldset>
<fieldset class="utable-content">
<div class="form-actions">
<%= f.hidden_field :mind_map_data, id: "mind_map_data_field", value: "" %>
<%= f.hidden_field :mind_map_data, id: "mind_map_data_field", value: "[]" %>
<%= f.hidden_field :u_table_id, value: @table.id %>
<input class="btn btn-primary pull-right" name="commit" type="submit" value="<%= t("save") %>">
</div>
</fieldset>

View File

@ -0,0 +1,33 @@
<table class="table main-list">
<thead>
<tr class="sort-header">
<% @table_fields.each do |f| %>
<%= thead(f) %>
<% end %>
</tr>
</thead>
<tbody>
<% @mind_maps.each do |mindmap| %>
<tr id="mindmap_<%= mindmap.id.to_s %>">
<td>
<a href="<%= admin_mind_map_path(mindmap) %>"><%= mindmap.title %></a>
<div class="quick-edit">
<ul class="nav nav-pills">
<li><a href="<%= edit_admin_mind_map_path(mindmap) %>"><%= t(:edit) %></a></li>
<li><a href="<%= admin_mind_map_path(mindmap) %>" class="delete text-error" data-method="delete" data-confirm="Are you sure?"><%= t(:delete_) %></a></li>
</ul>
</div>
</td>
<td>
<%= mindmap.created_at.strftime("%Y-%m-%d") %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%=
content_tag :div, class: "bottomnav clearfix" do
content_tag(:div, paginate(@mind_maps), class: "pagination pagination-centered") +
content_tag(:div, link_to(t(:new_),new_admin_mind_map_path(:table => @table.id.to_s), :class=>"btn btn-primary"), class: "pull-right")
end
%>

View File

@ -21,15 +21,11 @@
// 心智圖初始數據
// Initial mind map data
<% if @mind_map.mind_map_data.empty? %>
let mind = INITIAL_MIND
<% else %>
let mind = {
meta: {},
format: 'node_array',
data: <%= raw @mind_map.mind_map_data.to_json %>
}
<% end %>
// 心智圖自訂選項(可參考 jsmind 官方文檔)
// Custom options for the mind map (refer to the jsmind official documentation)
@ -38,7 +34,7 @@
editable: isEditable,
theme: 'primary',
mode: 'full',
tableUID: '<%= @mind_map.u_table.uid %>',
tableUID: '<%= @table.uid %>',
text: {
addNode: "<%= t("universal_table.add_node") %>",
deleteNode: "<%= t("universal_table.delete_node") %>",

View File

@ -0,0 +1,6 @@
<% content_for :page_specific_javascript do %>
<%= javascript_include_tag "lib/jquery.form" %>
<% end %>
<div id="index_table">
<%= render 'index'%>
</div>

View File

@ -0,0 +1,89 @@
<% content_for :page_specific_css do %>
<%= stylesheet_link_tag "universal_table/universal-table" %>
<%= stylesheet_link_tag "mind_map/mindmap" %>
<% end %>
<%= form_for @mind_map, url: admin_mind_maps_path, html: {class: "form-horizontal main-forms", id: "mind_map_form"} do |f| %>
<%= render :partial => "form", locals: {f: f} %>
<% end %>
<script type="module">
import '/assets/mind_map/utils/custom.overrides.js'
import '/assets/mind_map/jsmind/plugins/jsmind.draggable-node.js'
import { initJsmind, getJsmindData } from '/assets/mind_map/utils/custom.main.js'
import { INITIAL_MIND } from '/assets/mind_map/utils/custom.config.js'
// 操控心智圖是否可編輯
// Control whether the mind map is editable
let isEditable = true
// 心智圖實例
// Mind map instance
let jm
// 心智圖初始數據
// Initial mind map data
let mind = INITIAL_MIND
// 心智圖自訂選項(可參考 jsmind 官方文檔)
// Custom options for the mind map (refer to the jsmind official documentation)
const options = {
container: 'jsmind_container',
editable: isEditable,
theme: 'primary',
mode: 'full',
tableUID: '<%= @table.uid %>',
text: {
addNode: "<%= t("universal_table.add_node") %>",
deleteNode: "<%= t("universal_table.delete_node") %>",
strokeColor: "<%= t("universal_table.stroke_color") %>",
bgColor: "<%= t("universal_table.bg_color") %>",
textColor: "<%= t("universal_table.text_color") %>"
},
view: {
engine: 'svg',
draggable: true,
node_overflow: 'wrap',
},
shortcut: {
mapping: {
// 避免與 Toolbar 按下 Enter 事件衝突
// Avoid conflicts with the Enter key event in the Toolbar
addbrother: 2048 + 13,
},
},
}
// 初始化心智圖並掛載實例
// Initialize the mind map and attach the instance
jm = initJsmind(mind, options, isEditable)
// 儲存當前數據
// Save the current data
// document.getElementById('save_mind_map').addEventListener('click', (e) => {
// e.preventDefault();
// e.stopPropagation();
// let data = getJsmindData(jm);
// console.log(data);
// })
// 調整可編輯狀態
// Toggle the editable state
document.getElementById('toggle_editable').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
isEditable = !isEditable;
e.target.innerHTML = isEditable ? '<%= t("universal_table.disable_editing") %>' : '<%= t("universal_table.enable_editing") %>';
mind = getJsmindData(jm);
jm = initJsmind(mind, options, isEditable);
return false;
})
document.addEventListener("DOMContentLoaded", function () {
const form = document.getElementById("mind_map_form");
const hiddenField = document.getElementById("mind_map_data_field");
form.addEventListener("submit", function (e) {
const mindMapData = getJsmindData(jm);
console.log(mindMapData);
hiddenField.value = JSON.stringify(mindMapData.data);
});
});
</script>

View File

@ -16,7 +16,7 @@
<ul class="nav nav-pills">
<% if can_edit %>
<li><a href="<%= edit_admin_universal_table_path(table) %>"><%= t(:edit) %></a></li>
<li><a href="<%= "/admin/universal_table/#{table.id.to_s}/mind_map" %>"><%= t("universal_table.mind_map") %></a></li>
<li><a href="<%= "/admin/universal_table/#{table.id.to_s}/mind_maps" %>"><%= t("universal_table.mind_map") %></a></li>
<% if table.ordered_with_sort_number %>
<li><a href="<%= admin_universal_table_edit_sort_path(table) %>"><%= t('universal_table.edit_sort') %></a></li>
<% end %>

View File

@ -0,0 +1,63 @@
<%
data = action_data
OrbitHelper.render_css_in_head(["mind_map/mindmap"])
%>
<h2><%= data["title"] %></h2>
<div id="jsmind_container"></div>
<script type="module">
import '/assets/mind_map/utils/custom.overrides.js'
import '/assets/mind_map/jsmind/plugins/jsmind.draggable-node.js'
import { initJsmind, getJsmindData } from '/assets/mind_map/utils/custom.main.js'
import { INITIAL_MIND } from '/assets/mind_map/utils/custom.config.js'
// 操控心智圖是否可編輯
// Control whether the mind map is editable
let isEditable = false
// 心智圖實例
// Mind map instance
let jm
// 心智圖初始數據
// Initial mind map data
let mind = {
meta: {},
format: 'node_array',
data: <%= raw data["mind_map_data"].to_json %>
}
// 心智圖自訂選項(可參考 jsmind 官方文檔)
// Custom options for the mind map (refer to the jsmind official documentation)
const options = {
container: 'jsmind_container',
editable: isEditable,
theme: 'primary',
mode: 'full',
tableUID: '',
text: {
addNode: "<%= t("universal_table.add_node") %>",
deleteNode: "<%= t("universal_table.delete_node") %>",
strokeColor: "<%= t("universal_table.stroke_color") %>",
bgColor: "<%= t("universal_table.bg_color") %>",
textColor: "<%= t("universal_table.text_color") %>"
},
view: {
engine: 'svg',
draggable: true,
node_overflow: 'wrap',
},
shortcut: {
mapping: {
// 避免與 Toolbar 按下 Enter 事件衝突
// Avoid conflicts with the Enter key event in the Toolbar
addbrother: 2048 + 13,
},
},
}
// 初始化心智圖並掛載實例
// Initialize the mind map and attach the instance
jm = initJsmind(mind, options, isEditable)
</script>

View File

@ -22,7 +22,7 @@ Rails.application.routes.draw do
patch "/universal_tables/update_entry", to: 'universal_tables#update_entry'
post "/universal_tables/import_data_from_excel", to: 'universal_tables#import_data_from_excel'
get "universal_tables/checkforthread", to: "universal_tables#checkforthread"
get "/universal_table/:id/mind_map", to: "mind_maps#landing_page"
get "/universal_table/:id/mind_maps", to: "mind_maps#index"
resources :universal_tables do
get "new_entry"
delete "delete_entry"

View File

@ -24,6 +24,14 @@
},
"thumbnail" : "thumb.png",
"default": true
},
{
"filename" : "mindmap",
"name" : {
"zh_tw" : "6. Mind Maps",
"en" : "6. Mind Maps"
},
"thumbnail" : "thumb.png"
}
]
}

View File

@ -0,0 +1,107 @@
<style>
tr>th:first-child{
display: none!important;
}
/* tr>td:first-child{
display: none!important;
} */
.universal-table-index3{
table-layout: auto!important;
}
.universal-dropdown-menu {
padding: 15px 18px;
white-space: nowrap;
}
.universal-th-text{
white-space: pre!important;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
white-space: normal;
}
.universal-dropdown-menu {
padding: 15px 18px;
white-space: nowrap;
}
.universal-th-text {
padding: 8px 0 0 0;
display: inline;
margin-right: 5px;
}
.universal-dropdown {
display: inline-block;
}
a.universal-btn {
vertical-align: baseline;
color: #fff;
}
.universal-table-index {
border-collapse: collapse;
border: 1px solid #eee;
table-layout: fixed;
word-wrap: break-word;
}
.universal-table-index h3 {
float: left;
margin: 0;
}
.universal-table-index.table td{
padding: 15px 18px;
}
.universal-table-index thead th:last-child .dropdown-menu {
left: auto;
right: 0;
}
/* .universal-table-index tbody {
counter-reset: item;
} */
.universal-th-icon {
border: 1px solid #eee;
padding: 5px 8px;
margin-right: 5px;
color: gray;
cursor: pointer;
}
.universal-th-text.no-sort.no-search {
position: relative;
top: -6px;
}
.image-preview {
width: 120px;
}
</style>
<form class="form-inline universal-form-inline universal-form-inline5" action="{{url}}" method="get">
<table class="table table-hover table-striped universal-table-index universal-table-index3">
<caption>
<h3>{{table-name}}</h3>
</caption>
<tbody>
<tr class="tdken" data-level="0" data-list="mindmaps">
<td><a href="{{url}}">{{title}}</a></td>
</tr>
</tbody>
</table>
</form>
{{pagination_goes_here}}
<script>
$(document).ready(function(){
$('.tdken>td:nth-child(4)').prepend($('.col-ken:nth-child(4)>.universal-th-text '));
$('.tdken>td:nth-child(3)').prepend($('.col-ken:nth-child(3)>.universal-th-text '));
$(".universal-th-text").append(" :");
$(".tdken").append('<i class="fa-solid fa-thumbtack"></i>');
});
// $(document).ready(function(){
// $("tr>th:first-child").removeClass("col-md-3");
// $("tr>th:first-child").addClass("col-md-1");
// $("tr>th:nth-child(2)").removeClass("col-md-3");
// $("tr>th:nth-child(2)").addClass("col-md-6");
// $("tr>th:nth-child(3)").removeClass("col-md-3");
// $("tr>th:nth-child(3)").addClass("col-md-3");
// $("tr>th:nth-child(4)").removeClass("col-md-3");
// $("tr>th:nth-child(4)").addClass("col-md-2");
// $("tr>th:nth-child(5)").removeClass("col-md-3");
// $("tr>th:nth-child(5)").addClass("col-md-3");
// });
// $('.universal-table-index thead tr').prepend('<th></th>')
// $('.universal-table-index tbody tr').prepend('<td></td>')
</script>