From cd0e8b7c5d4f87c0172319874e678c3434fd9118 Mon Sep 17 00:00:00 2001 From: wangfukang <15630117759@163.com> Date: Wed, 27 May 2026 11:09:50 +0800 Subject: [PATCH] 1 --- package1/ieBrowser/chat.vue | 334 ++++++++++++++++++++++++++-- package1/ieBrowser/profileSetup.vue | 88 +++++++- package1/ieBrowser/settings.vue | 140 +++++++++--- package1/ieBrowser/videoPreview.vue | 42 ++++ 4 files changed, 561 insertions(+), 43 deletions(-) create mode 100644 package1/ieBrowser/videoPreview.vue diff --git a/package1/ieBrowser/chat.vue b/package1/ieBrowser/chat.vue index 88fa165..736c667 100644 --- a/package1/ieBrowser/chat.vue +++ b/package1/ieBrowser/chat.vue @@ -22,6 +22,10 @@ 电台陪伴中 你们已经安静陪伴了 {{ silentMinutes }} 分钟 + + 加入黑名单 + 举报 + @@ -51,6 +55,16 @@ + + + + + + 点击播放视频 + + {{ item.mediaDuration }}s + + {{ item.content }} @@ -74,26 +88,42 @@ - {{ voiceMode ? '键' : '语' }} - {{ voiceMode ? '键' : '语' }} + {{ voiceHoldText }} - - 发送 + + 发送 安全与退出 - 举报不适内容 + 举报不适内容 拉黑这个对象 提前结束陪伴 + + + 举报这个对象 + + {{ item.label }} + + + + 取消 + 提交举报 + + + + @@ -141,6 +171,8 @@ pollTimer: null, draft: '', showSafety: false, + showReportPanel: false, + isBlocked: false, showTargetProfile: false, targetProfile: {}, showEmoji: false, @@ -169,6 +201,16 @@ sendingMessage: false, emojis: ['🙂', '😄', '🥲', '😭', '😴', '🙌', '🌙', '☁️', '🍃', '✨', '💛', '🫶'], presenceActions: ['我在', '听着呢', '慢慢说', '抱一下空气'], + reportReasons: [ + { key: 'harassment', label: '骚扰不适' }, + { key: 'abuse', label: '攻击辱骂' }, + { key: 'fraud', label: '疑似欺诈' }, + { key: 'other', label: '其他问题' } + ], + reportForm: { + reasonType: 'other', + reasonText: '' + }, silentModes: ['一起听歌', '一起倒计时', '一起自习', '一起失眠'], companions: { i: { @@ -314,6 +356,7 @@ mediaDuration: item.mediaDuration, mediaSize: item.mediaSize, mediaFormat: item.mediaFormat, + poster: item.poster || item.thumbTempFilePath || '', mine, pending: false, blocked: item.isBlocked === 1 @@ -400,9 +443,20 @@ this.sendChatContent(text, 1) }, toggleEmoji() { + if (this.isBlocked) { + uni.showToast({ title: '已加入黑名单,无法继续发送', icon: 'none' }) + return + } this.voiceMode = false this.showEmoji = !this.showEmoji }, + toggleVoiceMode() { + if (this.isBlocked) { + uni.showToast({ title: '已加入黑名单,无法继续发送', icon: 'none' }) + return + } + this.voiceMode = !this.voiceMode + }, sendEmoji(emoji) { this.showEmoji = false this.sendChatContent(emoji, 3) @@ -425,6 +479,10 @@ uni.showToast({ title: '房间信息缺失', icon: 'none' }) return } + if (this.isBlocked) { + uni.showToast({ title: '已加入黑名单,无法继续发送', icon: 'none' }) + return + } if (this.needWaitReply()) { uni.showToast({ title: '先等等对方回复吧,首次破冰最多发送 3 条', icon: 'none' }) return @@ -438,6 +496,7 @@ mediaDuration: media.mediaDuration, mediaSize: media.mediaSize, mediaFormat: media.mediaFormat, + poster: media.poster || '', mine: true, pending: true }) @@ -471,11 +530,13 @@ msg.content = '发送失败,请稍后再试' } } catch (e) { + const message = (e && e.message) || '' + if (message.indexOf('黑名单') >= 0) this.isBlocked = true const msg = this.messages.find(item => item.clientMsgId === payload.clientMsgId) if (msg) { msg.pending = false msg.blocked = true - msg.content = (e && e.message) || '发送失败,请稍后再试' + msg.content = message || '发送失败,请稍后再试' } } }, @@ -491,8 +552,33 @@ msg.blocked = ack.isBlocked === 1 msg.content = msg.blocked ? '这句话没有送出,请换一种更温柔的说法。' : (ack.content || msg.content) }, - chooseImage() { + chooseMedia() { + if (this.isBlocked) { + uni.showToast({ title: '已加入黑名单,无法继续发送', icon: 'none' }) + return + } if (this.uploadingImage) return + if (!uni.chooseMedia) { + this.chooseImageCompat() + return + } + uni.chooseMedia({ + count: 1, + mediaType: ['image', 'video'], + sourceType: ['album', 'camera'], + camera: 'back', + success: async (res) => { + const file = res.tempFiles && res.tempFiles[0] + if (!file || !file.tempFilePath) return + if (res.type === 'video' || file.fileType === 'video') { + await this.uploadAndSendVideo(file) + return + } + await this.uploadAndSendImage(file.tempFilePath, file.size) + } + }) + }, + chooseImageCompat() { uni.chooseImage({ count: 1, sizeType: ['compressed'], @@ -504,7 +590,7 @@ } }) }, - async uploadAndSendImage(filePath) { + async uploadAndSendImage(filePath, fileSize) { this.uploadingImage = true try { const result = await tui.uploadFile('/upload/file', filePath) @@ -514,7 +600,30 @@ return } this.showEmoji = false - this.sendChatContent(imageUrl, 2) + this.sendChatContent(imageUrl, 2, { + mediaSize: fileSize, + mediaFormat: 'image' + }) + } finally { + this.uploadingImage = false + } + }, + async uploadAndSendVideo(file) { + this.uploadingImage = true + try { + const result = await tui.uploadFile('/upload/file', file.tempFilePath) + const videoUrl = typeof result === 'string' ? result : (result.url || result.fileUrl || result.path || result.fullPath || result) + if (!videoUrl || typeof videoUrl !== 'string') { + uni.showToast({ title: '视频上传失败', icon: 'none' }) + return + } + this.showEmoji = false + this.sendChatContent(videoUrl, 5, { + mediaDuration: file.duration ? Math.round(file.duration) : undefined, + mediaSize: file.size, + mediaFormat: 'video', + poster: file.thumbTempFilePath || '' + }) } finally { this.uploadingImage = false } @@ -523,6 +632,12 @@ if (!url) return uni.previewImage({ urls: [url], current: url }) }, + previewVideo(url) { + if (!url) return + uni.navigateTo({ + url: '/package1/ieBrowser/videoPreview?url=' + encodeURIComponent(url) + }) + }, initRecorder() { if (!uni.getRecorderManager) return this.recorderManager = uni.getRecorderManager() @@ -564,6 +679,10 @@ return touch ? touch.clientY || touch.pageY || 0 : 0 }, startRecord(event) { + if (this.isBlocked) { + uni.showToast({ title: '已加入黑名单,无法继续发送', icon: 'none' }) + return + } if (!this.recorderManager || this.uploadingVoice) return this.showEmoji = false this.recording = true @@ -681,15 +800,45 @@ prompt: profile.intro || this.companion.prompt } }, - async report() { + openReportPanel() { this.showSafety = false - await reportIeRoom(this.roomId, { reportedUserId: this.targetUserId, reasonType: 'other', reasonText: '聊天内容不适' }) + this.reportForm.reasonType = 'other' + this.reportForm.reasonText = '' + this.showReportPanel = true + }, + async submitReport() { + if (!this.roomId || !this.targetUserId) { + uni.showToast({ title: '举报对象缺失', icon: 'none' }) + return + } + await reportIeRoom(this.roomId, { + reportedUserId: this.targetUserId, + reasonType: this.reportForm.reasonType, + reasonText: this.reportForm.reasonText || '聊天内容不适' + }) + this.showReportPanel = false uni.showToast({ title: '已收到举报', icon: 'none' }) }, async block() { this.showSafety = false - if (this.targetUserId) await blockIeUser(this.targetUserId, '聊天中拉黑') - uni.showToast({ title: '已拉黑', icon: 'none' }) + if (!this.targetUserId) { + uni.showToast({ title: '拉黑对象缺失', icon: 'none' }) + return + } + uni.showModal({ + title: '加入黑名单?', + content: '加入后你们不能继续互相发送消息,也不会再次随机匹配到对方。', + confirmText: '加入', + confirmColor: '#e85d75', + success: async (res) => { + if (!res.confirm) return + await blockIeUser(this.targetUserId, '聊天中拉黑') + this.isBlocked = true + this.showEmoji = false + this.voiceMode = false + uni.showToast({ title: '已加入黑名单', icon: 'none' }) + } + }) }, async finishRoom() { if (this.finishing) return @@ -925,6 +1074,8 @@ .radio-copy { position: relative; z-index: 1; + flex: 1; + min-width: 0; } .radio-title { @@ -938,6 +1089,31 @@ font-size: 22rpx; } + .safe-actions { + position: relative; + z-index: 1; + display: flex; + gap: 12rpx; + margin-left: 18rpx; + } + + .safe-btn { + height: 52rpx; + line-height: 52rpx; + padding: 0 18rpx; + border-radius: 999rpx; + color: #6c69d8; + background: rgba(139,124,255,.1); + font-size: 21rpx; + font-weight: 800; + white-space: nowrap; + } + + .safe-btn.warn { + color: #e85d75; + background: rgba(232,93,117,.1); + } + .silent-modes { position: relative; z-index: 1; @@ -1098,6 +1274,13 @@ background: rgba(255, 255, 255, 0.72); } + .video-bubble { + padding: 0; + border: none; + background: transparent; + overflow: hidden; + } + .chat-image { display: block; width: 260rpx; @@ -1106,6 +1289,71 @@ background: rgba(22, 27, 46, 0.06); } + .video-card { + position: relative; + width: 300rpx; + height: 390rpx; + border-radius: 28rpx; + overflow: hidden; + background: + radial-gradient(circle at 22% 16%, rgba(169,255,231,.36), transparent 130rpx), + radial-gradient(circle at 78% 80%, rgba(139,124,255,.32), transparent 150rpx), + linear-gradient(145deg, #151a2d, #2c3350); + box-shadow: 0 18rpx 44rpx rgba(96,112,160,.18); + } + + .mine .video-card { + box-shadow: 0 18rpx 44rpx rgba(169,255,231,.24); + } + + .video-poster, + .video-placeholder { + width: 100%; + height: 100%; + } + + .video-placeholder { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: rgba(255,255,255,.92); + } + + .video-play { + width: 86rpx; + height: 86rpx; + border-radius: 50%; + text-align: center; + line-height: 86rpx; + color: #151a2d; + background: rgba(255,255,255,.9); + box-shadow: 0 12rpx 32rpx rgba(0,0,0,.18); + font-size: 34rpx; + font-weight: 900; + } + + .video-text { + margin-top: 18rpx; + color: rgba(255,255,255,.78); + font-size: 24rpx; + font-weight: 700; + } + + .video-meta { + position: absolute; + right: 16rpx; + bottom: 16rpx; + height: 42rpx; + line-height: 42rpx; + padding: 0 16rpx; + border-radius: 999rpx; + color: #fff; + background: rgba(0,0,0,.38); + font-size: 21rpx; + font-weight: 800; + } + .emoji-bubble { min-width: 72rpx; text-align: center; @@ -1254,6 +1502,10 @@ font-weight: 800; } + .send.disabled { + opacity: .46; + } + .safety-mask { position: fixed; left: 0; @@ -1295,6 +1547,62 @@ color: #6c69d8; } + .report-reason { + height: 76rpx; + line-height: 76rpx; + margin-top: 12rpx; + padding: 0 24rpx; + border-radius: 24rpx; + color: rgba(22,27,46,.66); + background: rgba(238,244,255,.72); + font-size: 25rpx; + font-weight: 800; + } + + .report-reason.active { + color: #151a2d; + background: #a9ffe7; + } + + .report-textarea { + width: 100%; + height: 150rpx; + margin-top: 18rpx; + padding: 22rpx; + border-radius: 24rpx; + box-sizing: border-box; + color: #151a2d; + background: rgba(238,244,255,.72); + font-size: 24rpx; + } + + .report-actions { + display: flex; + gap: 16rpx; + margin-top: 20rpx; + } + + .report-cancel, + .report-submit { + flex: 1; + height: 78rpx; + line-height: 78rpx; + border-radius: 999rpx; + text-align: center; + font-size: 25rpx; + font-weight: 800; + } + + .report-cancel { + color: rgba(22,27,46,.62); + background: rgba(22,27,46,.06); + } + + .report-submit { + color: #11162a; + background: #a9ffe7; + } + .profile-mask { position: fixed; left: 0; diff --git a/package1/ieBrowser/profileSetup.vue b/package1/ieBrowser/profileSetup.vue index cb95152..10eee39 100644 --- a/package1/ieBrowser/profileSetup.vue +++ b/package1/ieBrowser/profileSetup.vue @@ -4,10 +4,10 @@ {{ isEdit ? '编辑 i/e 资料' : '先认识一下你' }} - {{ step }}/4 + {{ step }}/5 - + Your Energy @@ -52,6 +52,22 @@ + Persona Cards + 上传你的人格卡片 + 最多 5 张,像个人主页的形象展示。别人点击头像时可以看到。 + + + + × + + + + 添加图片 + + + + + Match Target 你现在想遇见哪类人? @@ -68,7 +84,7 @@ 上一步 - {{ step === 4 ? '进入 i/e 此刻' : '继续' }} + {{ step === 5 ? '进入 i/e 此刻' : '继续' }} @@ -94,7 +110,8 @@ avatarUrl: '', gender: 'unknown', intro: '', - interestTags: [] + interestTags: [], + personaImages: [] }, tagOptions: ['慢热', '爱听歌', '夜跑', '自习', '想聊天', '想安静', '情绪低电量', '散步', '电影', '游戏', '摄影', '干饭'], targetOptions: [ @@ -142,6 +159,7 @@ this.form.gender = profile.gender || 'unknown' this.form.intro = profile.intro || '' this.form.interestTags = profile.interestTags || [] + this.form.personaImages = profile.personaImages || [] }, toggleTag(tag) { const index = this.form.interestTags.indexOf(tag) @@ -208,6 +226,59 @@ // uni.hideLoading(); }, 1000) }, + choosePersonaImages() { + const remain = 5 - this.form.personaImages.length + if (remain <= 0) { + uni.showToast({ title: '最多上传 5 张', icon: 'none' }) + return + } + uni.chooseMedia({ + count: remain, + mediaType: ['image'], + sourceType: ['camera', 'album'], + camera: 'back', + success: async (res) => { + const files = res.tempFiles || [] + for (const file of files) { + if (this.form.personaImages.length >= 5) break + const url = await this.uploadPersonaFile(file.tempFilePath) + if (url) this.form.personaImages.push(url) + } + } + }) + }, + uploadPersonaFile(path) { + if (!path) return Promise.resolve('') + const hiverToken = uni.getStorageSync('hiver_token') + uni.showLoading({ title: '上传中...', mask: true }) + return new Promise((resolve) => { + uni.uploadFile({ + url: this.tui.interfaceUrl() + '/upload/file', + filePath: path, + name: 'file', + header: { + 'content-type': 'multipart/form-data', + 'accessToken': hiverToken + }, + success: (uploadFileRes) => { + try { + const data = JSON.parse(uploadFileRes.data) + resolve(data.result || '') + } catch (e) { + resolve('') + } + }, + fail: () => resolve(''), + complete: () => uni.hideLoading() + }) + }) + }, + removePersonaImage(index) { + this.form.personaImages.splice(index, 1) + }, + previewPersona(index) { + uni.previewImage({ urls: this.form.personaImages, current: this.form.personaImages[index] }) + }, validateStep() { if (this.step === 2 && !this.form.anonymousName.trim()) { uni.showToast({ title: '先起一个昵称吧', icon: 'none' }) @@ -223,7 +294,7 @@ async next() { if (this.submitting) return if (!this.validateStep()) return - if (this.step < 4) { + if (this.step < 5) { this.step += 1 return } @@ -264,6 +335,7 @@ .card { margin-top: 36rpx; padding: 38rpx 32rpx; border-radius: 46rpx; border: 1rpx solid rgba(255,255,255,.82); background: rgba(255,255,255,.64); box-shadow: 0 26rpx 80rpx rgba(96,112,160,.14); backdrop-filter: blur(26rpx); } .eyebrow { color: rgba(21,26,45,.42); font-size: 22rpx; letter-spacing: 5rpx; text-transform: uppercase; } .headline { margin-top: 16rpx; margin-bottom: 28rpx; font-size: 46rpx; line-height: 60rpx; font-weight: 800; } + .card-desc { margin: -10rpx 0 24rpx; color: rgba(21,26,45,.52); font-size: 24rpx; line-height: 38rpx; } .mode-card, .target-card { display: flex; align-items: center; margin-top: 22rpx; padding: 28rpx; border-radius: 34rpx; background: rgba(238,244,255,.68); border: 2rpx solid transparent; } .mode-card.active, .target-card.active { border-color: #a9ffe7; background: rgba(223,254,244,.7); transform: scale(1.01); } .mark { width: 82rpx; height: 82rpx; margin-right: 22rpx; border-radius: 28rpx; text-align: center; line-height: 82rpx; font-size: 46rpx; font-weight: 800; } @@ -288,6 +360,12 @@ .tag-grid { display: flex; flex-wrap: wrap; } .tag { margin: 0 14rpx 18rpx 0; padding: 16rpx 24rpx; border-radius: 999rpx; color: rgba(21,26,45,.62); background: rgba(238,244,255,.82); font-size: 24rpx; } .tag.active { color: #11162a; background: #a9ffe7; font-weight: 800; } + .persona-grid { display: flex; flex-wrap: wrap; gap: 18rpx; } + .persona-item, .persona-add { position: relative; width: 184rpx; height: 224rpx; border-radius: 30rpx; overflow: hidden; background: rgba(238,244,255,.82); } + .persona-item image { width: 100%; height: 100%; display: block; } + .persona-delete { position: absolute; right: 12rpx; top: 12rpx; width: 42rpx; height: 42rpx; border-radius: 50%; text-align: center; line-height: 38rpx; color: #fff; background: rgba(21,26,45,.56); font-size: 34rpx; } + .persona-add { display: flex; flex-direction: column; align-items: center; justify-content: center; color: rgba(21,26,45,.46); border: 2rpx dashed rgba(108,105,216,.22); box-sizing: border-box; font-size: 23rpx; } + .persona-add .plus { margin-bottom: 10rpx; color: #6c69d8; font-size: 44rpx; font-weight: 800; } .actions { position: fixed; left: 32rpx; right: 32rpx; bottom: 42rpx; display: flex; gap: 18rpx; } .ghost, .solid { flex: 1; height: 96rpx; border-radius: 999rpx; text-align: center; line-height: 96rpx; font-size: 28rpx; font-weight: 800; } .ghost { color: rgba(21,26,45,.58); background: rgba(255,255,255,.68); } diff --git a/package1/ieBrowser/settings.vue b/package1/ieBrowser/settings.vue index c337ac2..6c1e6a3 100644 --- a/package1/ieBrowser/settings.vue +++ b/package1/ieBrowser/settings.vue @@ -3,64 +3,154 @@ - 设置 + 安全 - - {{ group.title }} - - - {{ item.name }} - {{ item.desc }} + + 安全中心 + 这里只有黑名单和举报记录,方便你随时管理边界。 + + + 黑名单 + 举报记录 + + + + 已拉黑对象 + + + 用户 {{ item.blockedUserId }} + {{ item.reason || '已加入黑名单,不会再次出现在推荐中。' }} + {{ item.createTime || '' }} + + 解除 + + 还没有拉黑任何人。 + {{ loading ? '正在加载...' : (hasMore ? '上滑加载更多' : '没有更多了') }} + + + + 我的举报记录 + + + {{ reasonText(item.reasonType) }} + {{ item.reasonText || '已提交安全反馈。' }} + 对象 {{ item.reportedUserId || '-' }} · {{ statusText(item.status) }} · {{ item.createTime || '' }} + {{ item.handleResult }} - + 还没有举报记录。 + {{ loading ? '正在加载...' : (hasMore ? '上滑加载更多' : '没有更多了') }} diff --git a/package1/ieBrowser/videoPreview.vue b/package1/ieBrowser/videoPreview.vue new file mode 100644 index 0000000..0659149 --- /dev/null +++ b/package1/ieBrowser/videoPreview.vue @@ -0,0 +1,42 @@ + + + + +