added mind map

This commit is contained in:
rulingcom 2025-05-23 23:17:06 +08:00
parent ed5c0a4223
commit f55873a425
12 changed files with 132 additions and 38 deletions

View File

@ -29,14 +29,29 @@ export const INITIAL_MIND = {
// 模擬打 API
// Simulate an API call to search based on the query
export async function mockSearchApi(query) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{ text: `${query} 建議1`, link: `https://example.com/${query}1` },
{ text: `${query} 建議2`, link: `https://example.com/${query}2` },
{ text: `${query} 建議3`, link: `https://example.com/${query}3` },
])
}, 500)
})
export async function mockSearchApi(query, tableUID) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", '/admin/universal_tables/get_entries?uid=' + tableUID + "&q=" + query + "&links=true");
xhr.setRequestHeader("Accept", "application/json");
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
try {
const data = JSON.parse(xhr.responseText);
resolve(data); // success
} catch (e) {
reject(new Error("Invalid JSON response"));
}
} else {
reject(new Error(`Request failed with status ${xhr.status}`));
}
};
xhr.onerror = function () {
reject(new Error("Network error"));
};
xhr.send();
});
}

View File

@ -33,8 +33,8 @@ export function initJsmind(mind, options, isEditable) {
// 掛載附加模組(遠程搜尋 & 工具列)
// Attach additional modules (Remote search & Toolbar)
if (isEditable) {
new JsmindSearch(jm, mockSearchApi)
new JsmindToolbar(jm)
new JsmindSearch(jm, mockSearchApi, options.tableUID);
new JsmindToolbar(jm, options)
}
return jm

View File

@ -14,13 +14,14 @@ export class JsmindSearch {
* Constructor for search
* @param {Object} jm - jsMind 實例 (jsMind instance)
* @param {Function} searchAPI - 遠程搜尋 API 函式 (Remote search API function)
* @param {string} tableUID
*/
constructor(jm, searchAPI) {
constructor(jm, searchAPI, tableUID) {
this.jm = jm
this.searchAPI = searchAPI
this.container = document.getElementById(jm.options.container)
this.suggestionBox = null
this.tableUID = tableUID
this.init()
}
@ -77,8 +78,9 @@ export class JsmindSearch {
async onInput(node, e) {
const query = e.target.value.trim()
if (!query) return
await new Promise(resolve => setTimeout(resolve, 500));
try {
const results = await this.searchAPI(query)
const results = await this.searchAPI(query, this.tableUID)
this.showSuggestion(node, e.target, results)
} catch (error) {
// Search API error handling

View File

@ -13,8 +13,9 @@ export class JsmindToolbar {
* 建構工具列
* Constructor for toolbar
* @param {Object} jm - jsMind 實例 (jsMind instance)
* @param {Object} options - jsMind 實例 (options)
*/
constructor(jm) {
constructor(jm, options) {
this.jm = jm
this.container = document.getElementById(jm.options.container)
this.toolbarNodeId = null
@ -22,7 +23,7 @@ export class JsmindToolbar {
this.bgColorPalette = null
this.strokeColorPalette = null
this.textColorPalette = null
this.options = options
this.init()
}
@ -78,21 +79,21 @@ export class JsmindToolbar {
// 建立工具列按鈕
// Create toolbar buttons
const buttons = [
{ id: 'toolbar-add-child-btn', text: '新增', onClick: this.handleAddChild.bind(this) },
{ id: 'toolbar-delete-btn', text: '刪除', onClick: this.handleDelete.bind(this) },
{ id: 'toolbar-add-child-btn', text: this.options.text.addNode, onClick: this.handleAddChild.bind(this) },
{ id: 'toolbar-delete-btn', text: this.options.text.deleteNode, onClick: this.handleDelete.bind(this) },
{
id: 'toolbar-stroke-color-btn',
text: '線條顏色',
text: this.options.text.strokeColor,
onClick: this.handleStrokeColor.bind(this),
},
{
id: 'toolbar-bg-color-btn',
text: '背景顏色',
text: this.options.text.bgColor,
onClick: this.handleBgColor.bind(this),
},
{
id: 'toolbar-text-color-btn',
text: '文字顏色',
text: this.options.text.textColor,
onClick: this.handleTextColor.bind(this),
},
]

View File

@ -9,4 +9,20 @@ class Admin::MindMapsController < OrbitAdminController
@mind_map = table.mind_map
end
end
def update
mind_map = MindMap.find(params[:id])
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_tables_path
end
private
def mind_map_params
params.require(:mind_map).permit!
end
end

View File

@ -53,11 +53,20 @@ class Admin::UniversalTablesController < OrbitAdminController
data = []
if !table.nil?
enteries = search_data(table, 50)
ma = ModuleApp.find_by_key("universal_table")
enteries.each do |entry|
data << {
"id" => entry.id.to_s,
"text" => entry.column_entries.first.text
}
if params["links"].present?
data << {
"id" => entry.id.to_s,
"text" => entry.column_entries.first.text,
"link" => OrbitHelper.cal_url_to_show(ma,entry)
}
else
data << {
"id" => entry.id.to_s,
"text" => entry.column_entries.first.text
}
end
end
end
render :json => data.to_json

View File

@ -351,4 +351,8 @@ class UniversalTablesController < ApplicationController
render :file => "#{Rails.root}/app/views/errors/404.html", :layout => false, :status => :not_found
end
end
def mind_map
end
end

View File

@ -4,7 +4,8 @@ class MindMap
include Slug
field :title, as: :slug_title, localize: true
field :mind_map_data, type: Array, default: []
belongs_to :u_table
has_many :mind_map_nodes, :dependent => :destroy
# has_many :mind_map_nodes, :dependent => :destroy
end

View File

@ -31,9 +31,15 @@
</div>
<div class="control-group">
<div class="controls">
<button id="toggle_editable">Disable Editing</button>
<button id="save_mind_map">Save Mind Map</button>
<button id="toggle_editable"><%= t("universal_table.disable_editing") %></button>
<button id="save_mind_map"><%= t("universal_table.save_mind_map") %></button>
</div>
<div id="jsmind_container"></div>
</div>
</fieldset>
<fieldset class="utable-content">
<div class="form-actions">
<%= f.hidden_field :mind_map_data, id: "mind_map_data_field", value: "" %>
<input class="btn btn-primary pull-right" name="commit" id="save_mind_map" type="submit" value="<%= t("save") %>">
</div>
</fieldset>

View File

@ -2,7 +2,7 @@
<%= stylesheet_link_tag "universal_table/universal-table" %>
<%= stylesheet_link_tag "mind_map/mindmap" %>
<% end %>
<%= form_for @mind_map, url: admin_mind_map_path(@mind_map), html: {class: "form-horizontal main-forms"} do |f| %>
<%= form_for @mind_map, url: admin_mind_map_path(@mind_map.id), html: {class: "form-horizontal main-forms", id: "mind_map_form"} do |f| %>
<%= render :partial => "form", locals: {f: f} %>
<% end %>
<script type="module">
@ -21,7 +21,15 @@
// 心智圖初始數據
// 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)
@ -30,6 +38,14 @@
editable: isEditable,
theme: 'primary',
mode: 'full',
tableUID: '<%= @mind_map.u_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,
@ -53,8 +69,8 @@
document.getElementById('save_mind_map').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
let data = getJsmindData(jm)
console.log(data)
let data = getJsmindData(jm);
console.log(data);
})
// 調整可編輯狀態
@ -62,11 +78,19 @@
document.getElementById('toggle_editable').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
isEditable = !isEditable
e.target.innerHTML = isEditable ? 'Disable Editing' : 'Enable Editing'
isEditable = !isEditable;
e.target.innerHTML = isEditable ? '<%= t("universal_table.disable_editing") %>' : '<%= t("universal_table.enable_editing") %>';
mind = getJsmindData(jm)
jm = initJsmind(mind, options, isEditable)
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);
hiddenField.value = JSON.stringify(mindMapData.data);
});
});
</script>

View File

@ -21,4 +21,12 @@ en:
drag_file_to_here: Drag file to here
show_lang: Language
downloaded_times: Downloaded Times
mind_map: Mind Map
mind_map: Mind Map
add_node: Add Node
delete_node: Delete Node
stroke_color: Line Color
bg_color: Background Color
text_color: Text Color
disable_editing: Disable editing
enable_editing: Enable editing
save_mind_map: Save mind map

View File

@ -21,4 +21,12 @@ zh_tw:
drag_file_to_here: 拖移檔案到此
show_lang: 呈現語系
downloaded_times: 下載次數
mind_map: Mind Map
mind_map: Mind Map
add_node: 新增
delete_node: 刪除
stroke_color: 線條顏色
bg_color: 背景顏色
text_color: 文字顏色
disable_editing: Disable editing
enable_editing: Enable editing
save_mind_map: Save mind map