From e6c7b932aaf78122f37dac524e65e623d5019c77 Mon Sep 17 00:00:00 2001 From: rulingcom Date: Fri, 13 Jun 2025 18:04:44 +0800 Subject: [PATCH] ken changes --- .../mind_map/utils/custom.search.js | 112 ++++++++++++++++-- app/assets/stylesheets/mind_map/mindmap.css | 32 ++++- app/controllers/admin/_entry_summary.html.rb | 35 ++++++ .../admin/universal_tables_controller.rb | 72 +++++++---- app/views/universal_tables/mind_map.html.erb | 3 +- 5 files changed, 211 insertions(+), 43 deletions(-) create mode 100755 app/controllers/admin/_entry_summary.html.rb diff --git a/app/assets/javascripts/mind_map/utils/custom.search.js b/app/assets/javascripts/mind_map/utils/custom.search.js index 9110096..ec4e3f3 100644 --- a/app/assets/javascripts/mind_map/utils/custom.search.js +++ b/app/assets/javascripts/mind_map/utils/custom.search.js @@ -67,6 +67,32 @@ export class JsmindSearch { // Ensure input event is not bound multiple times inputField.removeEventListener('input', this.onInput) inputField.addEventListener('input', this.onInput.bind(this, node)) + + inputField.removeEventListener('keydown', this.onKeyDown) + inputField.addEventListener('keydown', this.onKeyDown.bind(this, node)) + } + /** + * 處理 Enter 鍵完成輸入 + * Handle Enter key to finalize input + * @param {Object} node - 當前節點 + * @param {KeyboardEvent} e - 鍵盤事件 + */ + onKeyDown(node, e) { + if (e.key === 'Enter') { + e.preventDefault() + const input = e.target.value.trim() + if (input) { + // 更新節點文字 + node.data.text = input + this.jm.end_edit() + this.jm.update_node(node.id, input) + + // 隱藏 suggestion box(避免未選建議但仍留下) + if (this.suggestionBox) { + this.suggestionBox.style.display = 'none' + } + } + } } /** @@ -106,10 +132,22 @@ export class JsmindSearch { // 更新建議框內容 // Update suggestion box content this.suggestionBox.innerHTML = results - .map( - (item) => - `
${item.text}
` - ) +.map(item => { + const fieldHtml = item.fields.map(f => { + const txt = f.url + ? `${f.text}` + : f.text; + return `
${f.title}: ${txt}
`; + }).join(""); + + return ` +
+ ${fieldHtml} +
+ `; +}) + + .join('') this.suggestionBox.style.left = `${left}px` @@ -145,20 +183,72 @@ export class JsmindSearch { * @param {Object} node - 當前選中節點 (Selected node) * @param {Event} e - 點擊事件 (Click event) */ + // onSuggestionClick(node, e) { + // e.preventDefault() + + // const text = e.target.getAttribute('data-text') + // const link = e.target.getAttribute('data-link') + + // node.data.text = text + // node.data.link = link + + // this.jm.end_edit() + // this.jm.update_node(node.id, text) + + // // 選擇後隱藏建議框 + // // Hide suggestions after selection + // this.suggestionBox.style.display = 'none' + // } onSuggestionClick(node, e) { e.preventDefault() - const text = e.target.getAttribute('data-text') - const link = e.target.getAttribute('data-link') + const item = e.currentTarget // 確保抓到整個 .suggestion-item DIV + const html = item.innerHTML // 取得完整 HTML 當作 topic - node.data.text = text - node.data.link = link + node.data.text = html + node.data.link = item.getAttribute('data-link') this.jm.end_edit() - this.jm.update_node(node.id, text) + this.jm.update_node(node.id, html) - // 選擇後隱藏建議框 - // Hide suggestions after selection this.suggestionBox.style.display = 'none' } + } +// ✅ 新增播放語音事件委派,支援動態插入的 voice-player +let audio; + +document.addEventListener('click', function(e) { + const target = e.target.closest('.voice-player'); + if (!target) return; + + e.preventDefault(); + + let status = target.getAttribute('status'); + if (audio) { + audio.pause(); + audio.currentTime = 0; + } + + if (status === 'playing') { + target.setAttribute('status', ''); + const icon = target.querySelector('i'); + icon?.classList.remove('fa-pause'); + icon?.classList.add('fa-play'); + } else { + let mp3_url = target.getAttribute('data-content'); + audio = new Audio(mp3_url); + audio.play(); + + target.setAttribute('status', 'playing'); + const icon = target.querySelector('i'); + icon?.classList.remove('fa-play'); + icon?.classList.add('fa-pause'); + + audio.onended = function() { + target.setAttribute('status', ''); + icon?.classList.remove('fa-pause'); + icon?.classList.add('fa-play'); + }; + } +}); \ No newline at end of file diff --git a/app/assets/stylesheets/mind_map/mindmap.css b/app/assets/stylesheets/mind_map/mindmap.css index ff01463..b65fb0a 100644 --- a/app/assets/stylesheets/mind_map/mindmap.css +++ b/app/assets/stylesheets/mind_map/mindmap.css @@ -79,7 +79,7 @@ jmnode { color: #333; border-radius: 5px; box-shadow: 1px 1px 1px #666; - font: 1.4em/1.125 Verdana, Arial, Helvetica, sans-serif; + font: 1em/1.125 Verdana, Arial, Helvetica, sans-serif; } jmnode:hover { @@ -96,8 +96,7 @@ jmnode.selected { } jmnode.root { - font-size:1.6em; - font-size:2em; + font-size:1.2em; /* 展開/收合按鈕 */ border-color: gray; } @@ -113,12 +112,12 @@ jmexpander:hover { jmnode { padding: 5px; border-radius: 3px; - font-size: 1.6em; + font-size: 1.2em; } jmnode.root { /* font-size: 21px; */ - font-size: 1.8em; + font-size: 1.2em; } } @@ -153,7 +152,7 @@ jmexpander:hover { ============================ */ .jsmind-suggestions { position: absolute; - height: 200px; + height: fit-content; width: 200px; background: white; border: 1px solid #ccc; @@ -170,3 +169,24 @@ jmexpander:hover { .suggestion-item:hover { background: #f0f0f0; } +.field-row{ + .column_entry_files{ + margin-top: 1em; + } + a{ + color: unset; + } + strong{ + display: none; + } + &:nth-child(2){ + a{ + font-weight: 500; + font-size: 1.5em; + } + + } + &:nth-child(4){ + display: none; + } +} \ No newline at end of file diff --git a/app/controllers/admin/_entry_summary.html.rb b/app/controllers/admin/_entry_summary.html.rb new file mode 100755 index 0000000..bd062d5 --- /dev/null +++ b/app/controllers/admin/_entry_summary.html.rb @@ -0,0 +1,35 @@ +
+ <%= entry.column_entries.first.try(:text).to_s %> + + <% entry.column_entries.each do |ce| %> + <% if ce.type == "file" %> + + <% end %> + <% end %> +
diff --git a/app/controllers/admin/universal_tables_controller.rb b/app/controllers/admin/universal_tables_controller.rb index dd6a210..a45b273 100644 --- a/app/controllers/admin/universal_tables_controller.rb +++ b/app/controllers/admin/universal_tables_controller.rb @@ -48,31 +48,53 @@ class Admin::UniversalTablesController < OrbitAdminController end end - def get_entries - table = UTable.where(:uid => params["uid"]).first rescue nil - data = [] - if !table.nil? - if params[:q].present? - enteries = search_data(table, 50) - ma = ModuleApp.find_by_key("universal_table") - enteries.each do |entry| - 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 - end - render :json => data.to_json - end +def get_entries + table = UTable.where(:uid => params["uid"]).first rescue nil + data = [] + + if table && params[:q].present? + entries = search_data(table, 50) + ma = ModuleApp.find_by_key("universal_table") + + entries.each do |entry| + rows = [] + entry.column_entries.each do |ce| + ct = ce.table_column + next if ct.nil? + next if ct.display_in_index == false + + text = ce.get_frontend_text(ct) + next if text.blank? + + # 包含連結的欄位處理 + url = ct.is_link_to_show ? OrbitHelper.cal_url_to_show(ma, entry) : nil + rows << { + "title" => ct.title, + "text" => text, + "url" => url + } + end + + # 加入 hashtags 欄位 + # rows << { + # "title" => I18n.t("universal_table.hashtags"), + # "text" => entry.tags_for_frontend, + # "url" => nil + # } + + # 加入主輸出結構 + data << { + "id" => entry.id.to_s, + "link" => OrbitHelper.cal_url_to_show(ma, entry), + "text" => entry.column_entries.map(&:text).compact.reject(&:blank?).join(" / "), + "fields" => rows + } + end + end + + render json: data +end + def get_mindmaps utable = UTable.where(:uid => params['table']).first diff --git a/app/views/universal_tables/mind_map.html.erb b/app/views/universal_tables/mind_map.html.erb index 30a1b23..7f5bb8a 100644 --- a/app/views/universal_tables/mind_map.html.erb +++ b/app/views/universal_tables/mind_map.html.erb @@ -1,6 +1,6 @@ <% data = action_data - OrbitHelper.render_css_in_head(["/mind_map/mindmap"]) + OrbitHelper.render_css_in_head(["mind_map/mindmap"]) %>

<%= data["title"] %>

@@ -31,6 +31,7 @@ // Custom options for the mind map (refer to the jsmind official documentation) const options = { container: 'jsmind_container', + support_html: true, editable: isEditable, theme: 'primary', mode: 'full',