ken changes

This commit is contained in:
rulingcom 2025-06-13 18:04:44 +08:00
parent 478e6e8ec9
commit e6c7b932aa
5 changed files with 211 additions and 43 deletions

View File

@ -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) =>
`<div class="${SUGGESTION_ITEM_CLASS}" data-link="${item.link}" data-text="${item.text}">${item.text}</div>`
)
.map(item => {
const fieldHtml = item.fields.map(f => {
const txt = f.url
? `<a href="${f.url}" target="_blank">${f.text}</a>`
: f.text;
return `<div class="field-row"><strong>${f.title}:</strong> ${txt}</div>`;
}).join("");
return `
<div class="suggestion-item" data-link="${item.link}">
${fieldHtml}
</div>
`;
})
.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');
};
}
});

View File

@ -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;
}
}

View File

@ -0,0 +1,35 @@
<div class="entry-suggestion">
<strong><%= entry.column_entries.first.try(:text).to_s %></strong>
<% entry.column_entries.each do |ce| %>
<% if ce.type == "file" %>
<ul class="column_entry_files">
<% ce.column_entry_files.desc(:sort_number).each do |file| %>
<% next unless file.choose_lang_display(I18n.locale.to_s) %>
<% file_title = file.get_file_title %>
<% size = number_to_human_size(file.file.size) %>
<% link = file.get_link %>
<% if file.file.content_type.start_with?('audio/') %>
<div class="voice-player">
<span class="voice-title"><%= file_title %></span>
<a class="voice-player" data-content="<%= file.file.url %>" href="#" title="<%= file_title %>">
<i class="fa fa-play" aria-hidden="true"></i>
</a>
</div>
<% else %>
<li class="column_entry_file">
<a class="column_entry_file_link" href="<%= link %>" title="<%= file_title %>" target="_blank">
<%= file_title %>
</a>
<span class="file_size">(<%= size %>)</span>
<span class="view_count">
<i class="fa fa-eye" title="<%= t("universal_table.downloaded_times") %>"></i>
<span class="view-count"><%= file.download_count %></span>
</span>
</li>
<% end %>
<% end %>
</ul>
<% end %>
<% end %>
</div>

View File

@ -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

View File

@ -1,6 +1,6 @@
<%
data = action_data
OrbitHelper.render_css_in_head(["/mind_map/mindmap"])
OrbitHelper.render_css_in_head(["mind_map/mindmap"])
%>
<h2><%= data["title"] %></h2>
@ -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',