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}
-
${escapeHtml(content)}
+
${escapeHtml(safeContent)}
`;
}
@@ -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() {
-
-
-
-
2025-12-22 21:33:19
-
-
-
@@ -342,6 +289,29 @@ export function generatePhoneHTML() {
${generateTransferPageHTML()}
${generateReceiveTransferPageHTML()}
${generateTransferRefundConfirmHTML()}
+
+
+
+
+
+
裁剪图片
+
+
拖动选择区域,拖动角落调整大小
+
+
+
+
+
diff --git a/style.css b/style.css
index afb244a..39cff25 100644
--- a/style.css
+++ b/style.css
@@ -8767,165 +8767,6 @@
color: rgba(255,255,255,0.7);
}
-/* ===== 时间选择器 ===== */
-.wechat-time-picker {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- background: var(--wechat-white);
- border-top: 1px solid var(--wechat-border);
- z-index: 100;
- animation: slideUp 0.25s ease;
-}
-
-@keyframes slideUp {
- from {
- transform: translateY(100%);
- }
- to {
- transform: translateY(0);
- }
-}
-
-.wechat-time-picker.hidden {
- display: none;
-}
-
-.wechat-time-picker-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 12px 16px;
- border-bottom: 1px solid var(--wechat-border);
-}
-
-.wechat-time-picker-title {
- font-size: 15px;
- font-weight: 600;
- color: var(--wechat-text);
-}
-
-.wechat-time-picker-confirm {
- background: var(--wechat-green);
- color: white;
- border: none;
- border-radius: 6px;
- padding: 6px 16px;
- font-size: 14px;
- cursor: pointer;
- transition: opacity 0.2s;
-}
-
-.wechat-time-picker-confirm:active {
- opacity: 0.8;
-}
-
-.wechat-time-picker-display {
- text-align: center;
- padding: 10px;
- font-size: 14px;
- color: var(--wechat-green);
- font-weight: 500;
- background: var(--wechat-bg);
-}
-
-.wechat-time-picker-columns {
- display: flex;
- height: 180px;
- overflow: hidden;
-}
-
-.wechat-time-picker-column {
- flex: 1;
- height: 100%;
- overflow-y: auto;
- scroll-snap-type: y mandatory;
- -webkit-overflow-scrolling: touch;
-}
-
-/* 隐藏滚动条 */
-.wechat-time-picker-column::-webkit-scrollbar {
- display: none;
-}
-
-.wechat-time-picker-column {
- scrollbar-width: none;
- -ms-overflow-style: none;
-}
-
-.wechat-time-picker-item {
- height: 36px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 14px;
- color: var(--wechat-text-secondary);
- cursor: pointer;
- scroll-snap-align: center;
- transition: all 0.15s;
-}
-
-.wechat-time-picker-item:active {
- background: var(--wechat-bg);
-}
-
-.wechat-time-picker-item.selected {
- color: var(--wechat-text);
- font-weight: 600;
- font-size: 16px;
-}
-
-/* 时间指示器(输入框上方) */
-.wechat-time-indicator {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 8px;
- padding: 6px 12px;
- background: rgba(7, 193, 96, 0.1);
- border-bottom: 1px solid var(--wechat-border);
-}
-
-.wechat-time-indicator-text {
- font-size: 12px;
- color: var(--wechat-green);
- font-weight: 500;
-}
-
-.wechat-time-indicator-clear {
- width: 18px;
- height: 18px;
- border: none;
- background: var(--wechat-text-secondary);
- color: white;
- border-radius: 50%;
- font-size: 10px;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0.6;
- transition: opacity 0.2s;
-}
-
-.wechat-time-indicator-clear:hover {
- opacity: 1;
-}
-
-/* 暗色模式时间选择器 */
-.wechat-dark .wechat-time-picker {
- background: var(--wechat-white);
-}
-
-.wechat-dark .wechat-time-picker-display {
- background: var(--wechat-bg);
-}
-
-.wechat-dark .wechat-time-indicator {
- background: rgba(62, 181, 117, 0.15);
-}
-
/* ========== 一起听功能样式 ========== */
/* 搜索页面 */