修复:将 Alpine.js 名片弹窗组件提取为命名函数
- 将整个组件逻辑从 x-data 属性提取到 userCardComponent() 函数 - x-data 改为引用函数名,彻底解决 HTML 属性引号冲突问题 - 提取 _headers() 通用方法减少代码重复 - 礼物数据仍通过 window.__gifts 全局变量注入
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
包含:
|
||||
1. switchTarget() — 单击用户名切换聊天目标
|
||||
2. openUserCard() — 双击用户名打开名片弹窗
|
||||
3. 用户名片弹窗 Alpine.js 组件(资料查看 + 管理操作)
|
||||
3. 用户名片弹窗 Alpine.js 组件(资料查看 + 送花 + 管理操作)
|
||||
|
||||
从 scripts.blade.php 和 frame.blade.php 中抽取,保持代码职责清晰。
|
||||
|
||||
@@ -63,194 +63,233 @@
|
||||
|
||||
{{-- ═══════════ 用户名片弹窗 (Alpine.js) ═══════════ --}}
|
||||
@php $gifts = \App\Models\Gift::activeList(); @endphp
|
||||
|
||||
<script>
|
||||
// 礼物数据注入(避免 JSON 破坏 x-data 属性解析)
|
||||
/**
|
||||
* 礼物数据注入(避免 JSON 破坏 x-data 属性解析)
|
||||
*/
|
||||
window.__gifts = {!! Js::from($gifts) !!};
|
||||
window.__defaultGiftId = {{ $gifts->first()?->id ?? 0 }};
|
||||
|
||||
/**
|
||||
* 用户名片弹窗 Alpine.js 组件定义
|
||||
* 提取到 script 标签避免 HTML 属性中的引号冲突
|
||||
*/
|
||||
function userCardComponent() {
|
||||
return {
|
||||
showUserModal: false,
|
||||
userInfo: {},
|
||||
isMuting: false,
|
||||
muteDuration: 5,
|
||||
showWhispers: false,
|
||||
whisperList: [],
|
||||
showAnnounce: false,
|
||||
announceText: '',
|
||||
gifts: window.__gifts || [],
|
||||
selectedGiftId: window.__defaultGiftId || 0,
|
||||
giftCount: 1,
|
||||
sendingGift: false,
|
||||
|
||||
/** 获取用户资料 */
|
||||
async fetchUser(username) {
|
||||
try {
|
||||
const res = await fetch('/user/' + encodeURIComponent(username));
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.userInfo = data.data;
|
||||
this.showUserModal = true;
|
||||
this.isMuting = false;
|
||||
this.showWhispers = false;
|
||||
this.whisperList = [];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
|
||||
/** 踢出用户 */
|
||||
async kickUser() {
|
||||
const reason = prompt('踢出原因(可留空):', '违反聊天室规则');
|
||||
if (reason === null) return;
|
||||
try {
|
||||
const res = await fetch('/command/kick', {
|
||||
method: 'POST',
|
||||
headers: this._headers(),
|
||||
body: JSON.stringify({
|
||||
username: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
reason: reason || '违反聊天室规则'
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
} else {
|
||||
alert('操作失败:' + data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络异常');
|
||||
}
|
||||
},
|
||||
|
||||
/** 禁言用户 */
|
||||
async muteUser() {
|
||||
try {
|
||||
const res = await fetch('/command/mute', {
|
||||
method: 'POST',
|
||||
headers: this._headers(),
|
||||
body: JSON.stringify({
|
||||
username: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
duration: this.muteDuration
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
} else {
|
||||
alert('操作失败:' + data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络异常');
|
||||
}
|
||||
},
|
||||
|
||||
/** 警告用户 */
|
||||
async warnUser() {
|
||||
const reason = prompt('警告原因:', '请注意言行');
|
||||
if (reason === null) return;
|
||||
try {
|
||||
const res = await fetch('/command/warn', {
|
||||
method: 'POST',
|
||||
headers: this._headers(),
|
||||
body: JSON.stringify({
|
||||
username: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
reason: reason || '请注意言行'
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
} else {
|
||||
alert('操作失败:' + data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络异常');
|
||||
}
|
||||
},
|
||||
|
||||
/** 冻结用户 */
|
||||
async freezeUser() {
|
||||
if (!confirm('确定要冻结 ' + this.userInfo.username + ' 的账号吗?冻结后将无法登录!')) return;
|
||||
const reason = prompt('冻结原因:', '严重违规');
|
||||
if (reason === null) return;
|
||||
try {
|
||||
const res = await fetch('/command/freeze', {
|
||||
method: 'POST',
|
||||
headers: this._headers(),
|
||||
body: JSON.stringify({
|
||||
username: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
reason: reason || '严重违规'
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
} else {
|
||||
alert('操作失败:' + data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络异常');
|
||||
}
|
||||
},
|
||||
|
||||
/** 查看私信记录 */
|
||||
async loadWhispers() {
|
||||
try {
|
||||
const res = await fetch('/command/whispers/' + encodeURIComponent(this.userInfo.username), {
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.whisperList = data.messages;
|
||||
this.showWhispers = true;
|
||||
} else {
|
||||
alert(data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络异常');
|
||||
}
|
||||
},
|
||||
|
||||
/** 发送全服公告 */
|
||||
async sendAnnounce() {
|
||||
if (!this.announceText.trim()) return;
|
||||
try {
|
||||
const res = await fetch('/command/announce', {
|
||||
method: 'POST',
|
||||
headers: this._headers(),
|
||||
body: JSON.stringify({
|
||||
content: this.announceText,
|
||||
room_id: window.chatContext.roomId,
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.announceText = '';
|
||||
this.showAnnounce = false;
|
||||
} else {
|
||||
alert(data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络异常');
|
||||
}
|
||||
},
|
||||
|
||||
/** 送礼物 */
|
||||
async sendGift() {
|
||||
if (this.sendingGift || !this.selectedGiftId) return;
|
||||
this.sendingGift = true;
|
||||
try {
|
||||
const res = await fetch('/gift/flower', {
|
||||
method: 'POST',
|
||||
headers: this._headers(),
|
||||
body: JSON.stringify({
|
||||
to_user: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
gift_id: this.selectedGiftId,
|
||||
count: this.giftCount
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
alert(data.message);
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
this.giftCount = 1;
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络异常');
|
||||
}
|
||||
this.sendingGift = false;
|
||||
},
|
||||
|
||||
/** 通用请求头 */
|
||||
_headers() {
|
||||
return {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<div id="user-modal-container" x-data="{
|
||||
showUserModal: false,
|
||||
userInfo: {},
|
||||
isMuting: false,
|
||||
muteDuration: 5,
|
||||
showWhispers: false,
|
||||
whisperList: [],
|
||||
showAnnounce: false,
|
||||
announceText: '',
|
||||
gifts: window.__gifts || [],
|
||||
selectedGiftId: window.__defaultGiftId || 0,
|
||||
giftCount: 1,
|
||||
sendingGift: false,
|
||||
|
||||
async fetchUser(username) {
|
||||
try {
|
||||
const res = await fetch('/user/' + encodeURIComponent(username));
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.userInfo = data.data;
|
||||
this.showUserModal = true;
|
||||
this.isMuting = false;
|
||||
this.showWhispers = false;
|
||||
this.whisperList = [];
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
},
|
||||
|
||||
async kickUser() {
|
||||
const reason = prompt('踢出原因(可留空):', '违反聊天室规则');
|
||||
if (reason === null) return;
|
||||
try {
|
||||
const res = await fetch('/command/kick', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
reason: reason || '违反聊天室规则'
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
} else {
|
||||
alert('操作失败:' + data.message);
|
||||
}
|
||||
} catch (e) { alert('网络异常'); }
|
||||
},
|
||||
|
||||
async muteUser() {
|
||||
try {
|
||||
const res = await fetch('/command/mute', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
duration: this.muteDuration
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
} else {
|
||||
alert('操作失败:' + data.message);
|
||||
}
|
||||
} catch (e) { alert('网络异常'); }
|
||||
},
|
||||
|
||||
async warnUser() {
|
||||
const reason = prompt('警告原因:', '请注意言行');
|
||||
if (reason === null) return;
|
||||
try {
|
||||
const res = await fetch('/command/warn', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
reason: reason || '请注意言行'
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
} else {
|
||||
alert('操作失败:' + data.message);
|
||||
}
|
||||
} catch (e) { alert('网络异常'); }
|
||||
},
|
||||
|
||||
async freezeUser() {
|
||||
if (!confirm('确定要冻结 ' + this.userInfo.username + ' 的账号吗?冻结后将无法登录!')) return;
|
||||
const reason = prompt('冻结原因:', '严重违规');
|
||||
if (reason === null) return;
|
||||
try {
|
||||
const res = await fetch('/command/freeze', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId,
|
||||
reason: reason || '严重违规'
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.showUserModal = false;
|
||||
} else {
|
||||
alert('操作失败:' + data.message);
|
||||
}
|
||||
} catch (e) { alert('网络异常'); }
|
||||
},
|
||||
|
||||
async loadWhispers() {
|
||||
try {
|
||||
const res = await fetch('/command/whispers/' + encodeURIComponent(this.userInfo.username), {
|
||||
headers: { 'Accept': 'application/json' }
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.whisperList = data.messages;
|
||||
this.showWhispers = true;
|
||||
} else {
|
||||
alert(data.message);
|
||||
}
|
||||
} catch (e) { alert('网络异常'); }
|
||||
},
|
||||
|
||||
async sendAnnounce() {
|
||||
if (!this.announceText.trim()) return;
|
||||
try {
|
||||
const res = await fetch('/command/announce', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: this.announceText,
|
||||
room_id: window.chatContext.roomId,
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.status === 'success') {
|
||||
this.announceText = '';
|
||||
this.showAnnounce = false;
|
||||
} else {
|
||||
alert(data.message);
|
||||
}
|
||||
} catch (e) { alert('网络异常'); }
|
||||
},
|
||||
|
||||
async sendGift() {
|
||||
if (this.sendingGift || !this.selectedGiftId) return;
|
||||
this.sendingGift = true;
|
||||
try {
|
||||
const res = await fetch('/gift/flower', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type'
|
||||
: 'application/json' , 'Accept' : 'application/json' }, body: JSON.stringify({ to_user: this.userInfo.username,
|
||||
room_id: window.chatContext.roomId, gift_id: this.selectedGiftId, count: this.giftCount }) }); const data=await
|
||||
res.json(); alert(data.message); if (data.status === 'success') { this.showUserModal=false; this.giftCount=1; } }
|
||||
catch (e) { alert('网络异常'); } this.sendingGift=false; } }">
|
||||
<div id="user-modal-container" x-data="userCardComponent()">
|
||||
<div x-show="showUserModal" style="display: none;" class="modal-overlay" x-on:click.self="showUserModal = false">
|
||||
<div class="modal-card" x-transition>
|
||||
{{-- 弹窗头部 --}}
|
||||
|
||||
Reference in New Issue
Block a user