diff --git a/chat-func-panel.js b/chat-func-panel.js index 30bca39..a3a213a 100644 --- a/chat-func-panel.js +++ b/chat-func-panel.js @@ -677,10 +677,6 @@ function handleFuncItemClick(func) { showTransferPage(); } return; - case 'time': - hideFuncPanel(); - showTimePicker(); - return; case 'listen': hideFuncPanel(); // 群聊不支持一起听 @@ -758,194 +754,5 @@ export function initFuncPanel() { // 初始化音乐面板事件 initMusicEvents(); initMusicShareListener(); - initTimePickerEvents(); initListenTogether(); } - -// ============ 时间选择器相关 ============ - -// 存储选择的时间(null 表示使用当前时间) -let selectedTime = null; -let timePickerInited = false; - -// 时间选择器当前选中的值 -let pickerValues = { - year: new Date().getFullYear(), - month: new Date().getMonth() + 1, - day: new Date().getDate(), - hour: new Date().getHours(), - minute: new Date().getMinutes(), - second: new Date().getSeconds() -}; - -// 获取选择的时间(供 chat.js 使用) -export function getSelectedTime() { - return selectedTime; -} - -// 清除选择的时间 -export function clearSelectedTime() { - selectedTime = null; - updateTimeIndicator(); -} - -// 显示时间选择器 -function showTimePicker() { - const picker = document.getElementById('wechat-time-picker'); - if (!picker) return; - - // 初始化为当前时间 - const now = new Date(); - pickerValues = { - year: now.getFullYear(), - month: now.getMonth() + 1, - day: now.getDate(), - hour: now.getHours(), - minute: now.getMinutes(), - second: now.getSeconds() - }; - - renderTimePickerColumns(); - updateTimePickerDisplay(); - picker.classList.remove('hidden'); -} - -// 隐藏时间选择器 -function hideTimePicker() { - const picker = document.getElementById('wechat-time-picker'); - picker?.classList.add('hidden'); -} - -// 渲染时间选择器列 -function renderTimePickerColumns() { - const currentYear = new Date().getFullYear(); - - // 年份:前后5年 - renderPickerColumn('year', currentYear - 5, currentYear + 5, pickerValues.year, '年'); - // 月份:1-12 - renderPickerColumn('month', 1, 12, pickerValues.month, '月'); - // 日期:根据年月动态计算 - const daysInMonth = new Date(pickerValues.year, pickerValues.month, 0).getDate(); - renderPickerColumn('day', 1, daysInMonth, pickerValues.day, '日'); - // 小时:0-23 - renderPickerColumn('hour', 0, 23, pickerValues.hour, '时'); - // 分钟:0-59 - renderPickerColumn('minute', 0, 59, pickerValues.minute, '分'); - // 秒:0-59 - renderPickerColumn('second', 0, 59, pickerValues.second, '秒'); -} - -// 渲染单个列 -function renderPickerColumn(type, min, max, selected, suffix) { - const container = document.getElementById(`wechat-time-picker-${type}`); - if (!container) return; - - let html = ''; - for (let i = min; i <= max; i++) { - const value = type === 'year' ? i : i.toString().padStart(2, '0'); - const isSelected = i === selected; - html += `
${value}${suffix}
`; - } - container.innerHTML = html; - - // 滚动到选中项 - setTimeout(() => { - const selectedItem = container.querySelector('.selected'); - if (selectedItem) { - container.scrollTop = selectedItem.offsetTop - container.offsetHeight / 2 + selectedItem.offsetHeight / 2; - } - }, 0); -} - -// 更新显示的时间 -function updateTimePickerDisplay() { - const display = document.getElementById('wechat-time-picker-display'); - if (!display) return; - - const { year, month, day, hour, minute, second } = pickerValues; - display.textContent = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')} ${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`; -} - -// 更新输入框旁的时间指示器 -function updateTimeIndicator() { - let indicator = document.getElementById('wechat-time-indicator'); - - if (!selectedTime) { - indicator?.remove(); - return; - } - - if (!indicator) { - const inputArea = document.querySelector('.wechat-chat-input-area'); - if (!inputArea) return; - - indicator = document.createElement('div'); - indicator.id = 'wechat-time-indicator'; - indicator.className = 'wechat-time-indicator'; - inputArea.insertBefore(indicator, inputArea.firstChild); - } - - const date = new Date(selectedTime); - const month = date.getMonth() + 1; - const day = date.getDate(); - const hour = date.getHours().toString().padStart(2, '0'); - const minute = date.getMinutes().toString().padStart(2, '0'); - - indicator.innerHTML = ` - ${month}月${day}日 ${hour}:${minute} - - `; - - // 绑定清除按钮 - document.getElementById('wechat-time-indicator-clear')?.addEventListener('click', (e) => { - e.stopPropagation(); - clearSelectedTime(); - }); -} - -// 初始化时间选择器事件 -function initTimePickerEvents() { - if (timePickerInited) return; - timePickerInited = true; - - // 监听列项点击 - document.addEventListener('click', (e) => { - const item = e.target.closest('.wechat-time-picker-item'); - if (!item) return; - - const column = item.closest('.wechat-time-picker-column'); - if (!column) return; - - const type = column.dataset.type; - const value = parseInt(item.dataset.value); - - // 更新选中值 - pickerValues[type] = value; - - // 更新选中样式 - column.querySelectorAll('.wechat-time-picker-item').forEach(el => { - el.classList.toggle('selected', parseInt(el.dataset.value) === value); - }); - - // 如果改变了年或月,需要重新渲染日期列 - if (type === 'year' || type === 'month') { - const daysInMonth = new Date(pickerValues.year, pickerValues.month, 0).getDate(); - if (pickerValues.day > daysInMonth) { - pickerValues.day = daysInMonth; - } - renderPickerColumn('day', 1, daysInMonth, pickerValues.day, '日'); - } - - updateTimePickerDisplay(); - }); - - // 确认按钮 - document.getElementById('wechat-time-picker-confirm')?.addEventListener('click', () => { - const { year, month, day, hour, minute, second } = pickerValues; - selectedTime = new Date(year, month - 1, day, hour, minute, second).getTime(); - - hideTimePicker(); - updateTimeIndicator(); - showToast('已设置发送时间', '⏰'); - }); -} diff --git a/chat.js b/chat.js index 1c13ae8..b4bc9ef 100644 --- a/chat.js +++ b/chat.js @@ -17,7 +17,6 @@ import { startVoiceCall } from './voice-call.js'; import { startVideoCall } from './video-call.js'; import { showOpenRedPacket, generateRedPacketId } from './red-packet.js'; import { showReceiveTransferPage, generateTransferId } from './transfer.js'; -import { getSelectedTime, clearSelectedTime } from './chat-func-panel.js'; // 当前聊天的联系人索引 export let currentChatIndex = -1; @@ -1393,6 +1392,7 @@ export async function sendMessage(messageText, isMultipleMessages = false, isVoi const now = new Date(); const timeStr = `${now.getFullYear()}-${(now.getMonth()+1).toString().padStart(2,'0')}-${now.getDate().toString().padStart(2,'0')} ${now.getHours().toString().padStart(2,'0')}:${now.getMinutes().toString().padStart(2,'0')}`; + const msgTimestamp = Date.now(); let messagesToSend = []; if (isMultipleMessages && Array.isArray(messageText)) { @@ -1413,8 +1413,6 @@ export async function sendMessage(messageText, isMultipleMessages = false, isVoi // 清除引用 clearQuote(); - const msgTimestamp = Date.now(); - for (let i = 0; i < messagesToSend.length; i++) { const msg = messagesToSend[i]; // 只有第一条消息带引用 @@ -1453,15 +1451,6 @@ export async function sendMessage(messageText, isMultipleMessages = false, isVoi ? `[用户发送了语音消息,内容是:${messagesToSend.join('\n')}]` : messagesToSend.join('\n'); - // 如果有选择的时间,添加时间上下文 - const selectedTime = getSelectedTime(); - if (selectedTime) { - const timeDate = new Date(selectedTime); - const timeStr = `${timeDate.getFullYear()}年${timeDate.getMonth() + 1}月${timeDate.getDate()}日 ${timeDate.getHours().toString().padStart(2, '0')}:${timeDate.getMinutes().toString().padStart(2, '0')}`; - combinedMessage = `[当前时间:${timeStr}]\n${combinedMessage}`; - clearSelectedTime(); - } - // 如果有引用,添加引用上下文 if (quote) { let quoteDesc; @@ -1981,15 +1970,6 @@ export async function sendStickerMessage(stickerUrl, description = '') { ? `[用户发送了一个表情包:${description}]` : '[用户发送了一个表情包]'; - // 如果有选择的时间,添加时间上下文 - const selectedTime = getSelectedTime(); - if (selectedTime) { - const timeDate = new Date(selectedTime); - const timeStr = `${timeDate.getFullYear()}年${timeDate.getMonth() + 1}月${timeDate.getDate()}日 ${timeDate.getHours().toString().padStart(2, '0')}:${timeDate.getMinutes().toString().padStart(2, '0')}`; - aiPrompt = `[当前时间:${timeStr}]\n${aiPrompt}`; - clearSelectedTime(); - } - const aiResponse = await callAI(contact, aiPrompt); // 只有用户还在当前聊天时才隐藏打字指示器 @@ -2371,15 +2351,6 @@ export async function sendPhotoMessage(description) { const { callAI } = await import('./ai.js'); let aiPrompt = `[用户发送了一张照片,图片描述:${polishedDescription}]`; - // 如果有选择的时间,添加时间上下文 - const selectedTime = getSelectedTime(); - if (selectedTime) { - const timeDate = new Date(selectedTime); - const timeStr = `${timeDate.getFullYear()}年${timeDate.getMonth() + 1}月${timeDate.getDate()}日 ${timeDate.getHours().toString().padStart(2, '0')}:${timeDate.getMinutes().toString().padStart(2, '0')}`; - aiPrompt = `[当前时间:${timeStr}]\n${aiPrompt}`; - clearSelectedTime(); - } - const aiResponse = await callAI(contact, aiPrompt); // 只有用户还在当前聊天时才隐藏打字指示器 @@ -2861,15 +2832,6 @@ export async function sendBatchMessages(messages) { const { callAI } = await import('./ai.js'); let combinedPrompt = promptParts.join('\n'); - // 如果有选择的时间,添加时间上下文 - const selectedTime = getSelectedTime(); - if (selectedTime) { - const timeDate = new Date(selectedTime); - const timeStr = `${timeDate.getFullYear()}年${timeDate.getMonth() + 1}月${timeDate.getDate()}日 ${timeDate.getHours().toString().padStart(2, '0')}:${timeDate.getMinutes().toString().padStart(2, '0')}`; - combinedPrompt = `[当前时间:${timeStr}]\n${combinedPrompt}`; - clearSelectedTime(); - } - const aiResponse = await callAI(contact, combinedPrompt); // 只有用户还在当前聊天时才隐藏打字指示器 diff --git a/group-chat.js b/group-chat.js index efba3c8..d778287 100644 --- a/group-chat.js +++ b/group-chat.js @@ -10,6 +10,7 @@ import { escapeHtml, sleep, formatMessageTime, calculateVoiceDuration, bindImage import { getUserAvatarHTML, refreshChatList, getUserPersonaFromST } from './ui.js'; import { getSTChatContext, HAKIMI_HEADER } from './ai.js'; import { playMusic as kugouPlayMusic } from './music.js'; +import { showMessageMenu } from './message-menu.js'; // 当前群聊的索引 export let currentGroupChatIndex = -1; @@ -813,25 +814,29 @@ function renderGroupChatHistory(groupChat, members, chatHistory) { // 生成群聊静态语音气泡 function generateGroupVoiceBubbleStatic(content, isSelf) { - const seconds = calculateVoiceDuration(content); - const width = Math.min(50 + seconds * 3, 180); + const safeContent = (content || '').toString(); + const seconds = calculateVoiceDuration(safeContent); + const width = Math.min(60 + seconds * 4, 200); const voiceId = 'voice_' + Math.random().toString(36).substring(2, 9); - // 用户消息波形朝右,角色消息波形朝左 - const wavesSvg = isSelf - ? `` - : ``; - // 用户消息:波形在左,秒数在右 - // 角色消息:秒数在左,波形在右 + // WiFi信号样式的三条弧线图标(与单聊保持一致) + const wavesSvg = ` + + + + `; + + // 用户消息:时长在左,波形在右 + // 角色消息:波形在左,时长在右 const bubbleInner = isSelf - ? `${wavesSvg}${seconds}` - : `${seconds}${wavesSvg}`; + ? `${seconds}"${wavesSvg}` + : `${wavesSvg}${seconds}"`; return ` -
+
${bubbleInner}
- + `; } @@ -866,17 +871,43 @@ function generateGroupMusicCardStatic(musicInfo) { `; } -// 绑定群聊语音气泡点击事件 +// 绑定群聊语音气泡点击事件(播放动画 + 显示上方菜单,与单聊保持一致) function bindGroupVoiceBubbleEvents(container) { const voiceBubbles = container.querySelectorAll('.wechat-voice-bubble:not([data-bound])'); voiceBubbles.forEach(bubble => { bubble.setAttribute('data-bound', 'true'); - bubble.addEventListener('click', () => { - const voiceId = bubble.dataset.voiceId; - const textEl = document.getElementById(voiceId); - if (textEl) { - textEl.classList.toggle('hidden'); + + // 获取父消息元素 + const messageEl = bubble.closest('.wechat-message'); + + // 计算消息索引 + const allMessages = Array.from(container.querySelectorAll('.wechat-message')); + const msgIndex = allMessages.indexOf(messageEl); + + // 点击事件:播放动画 + 显示上方菜单 + bubble.addEventListener('click', (e) => { + e.stopPropagation(); + + // 切换播放状态 + const isPlaying = bubble.classList.contains('playing'); + if (isPlaying) { + bubble.classList.remove('playing'); + } else { + // 停止其他正在播放的语音 + document.querySelectorAll('.wechat-voice-bubble.playing').forEach(b => { + b.classList.remove('playing'); + }); + bubble.classList.add('playing'); + + // 模拟播放时间后停止 + const duration = parseInt(bubble.querySelector('.wechat-voice-duration')?.textContent) || 3; + setTimeout(() => { + bubble.classList.remove('playing'); + }, duration * 1000); } + + // 显示上方菜单 + showMessageMenu(bubble, msgIndex, e); }); }); } diff --git a/main.js b/main.js index 9c205a2..c8db17d 100644 --- a/main.js +++ b/main.js @@ -1634,8 +1634,8 @@ function init() { } if (typeof jQuery === 'function') { - jQuery(() => init()); + jQuery(() => setTimeout(init, 500)); } else { - document.addEventListener('DOMContentLoaded', init, { once: true }); + document.addEventListener('DOMContentLoaded', () => setTimeout(init, 500), { once: true }); } diff --git a/phone-html.js b/phone-html.js index aaeef95..5d11aab 100644 --- a/phone-html.js +++ b/phone-html.js @@ -205,28 +205,6 @@ export function generatePhoneHTML() {
- -
@@ -248,7 +226,6 @@ export function generatePhoneHTML() {
语音输入
-
时间
一起听
音乐
@@ -270,36 +247,6 @@ export function generatePhoneHTML() {
- -