pk优化版
This commit is contained in:
@@ -16,6 +16,10 @@
|
||||
<!-- 工具栏 -->
|
||||
<div class="p-4 border-b border-gray-100 space-y-3">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button @click="showAddDialog = true"
|
||||
class="px-3 py-1.5 text-sm bg-green-100 text-green-700 hover:bg-green-200 rounded border border-green-300">
|
||||
+ 添加主播
|
||||
</button>
|
||||
<button @click="selectAll"
|
||||
class="px-3 py-1.5 text-sm bg-blue-100 text-blue-700 hover:bg-blue-200 rounded border border-blue-300">全选</button>
|
||||
<button @click="selectNone"
|
||||
@@ -164,6 +168,84 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加主播弹窗 -->
|
||||
<div v-if="showAddDialog" class="fixed inset-0 bg-black/50 flex items-center justify-center"
|
||||
style="z-index: 10000">
|
||||
<div class="bg-white rounded-xl shadow-2xl w-full max-w-lg mx-4">
|
||||
<div class="p-4 border-b border-gray-200 flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold text-gray-600">添加主播</h3>
|
||||
<button @click="closeAddDialog" class="text-gray-700 hover:text-gray-700 text-xl">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="p-4 space-y-4">
|
||||
<!-- 主播ID输入 -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">主播ID(每行一个,支持批量粘贴)</label>
|
||||
<textarea v-model="addForm.idsText" rows="6" placeholder="粘贴主播ID,每行一个 例如: anchor_001 anchor_002 anchor_003"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:border-blue-500 focus:outline-none resize-none font-mono"></textarea>
|
||||
<div class="text-xs text-gray-400 mt-1">
|
||||
已输入 {{ parsedIds.length }} 个ID
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 邀请类型 -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">邀请类型</label>
|
||||
<div class="flex gap-3">
|
||||
<button @click="addForm.invitationType = '1'"
|
||||
:class="['px-4 py-2 rounded-lg text-sm border transition-all', addForm.invitationType === '1' ? 'bg-blue-500 text-white border-blue-500' : 'bg-white text-gray-600 border-gray-300 hover:border-blue-300']">
|
||||
普票
|
||||
</button>
|
||||
<button @click="addForm.invitationType = '2'"
|
||||
:class="['px-4 py-2 rounded-lg text-sm border transition-all', addForm.invitationType === '2' ? 'bg-yellow-500 text-white border-yellow-500' : 'bg-white text-gray-600 border-gray-300 hover:border-yellow-300']">
|
||||
金票
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 国家选择 -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">国家</label>
|
||||
<select v-model="addForm.country"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:border-blue-500 focus:outline-none">
|
||||
<option v-for="c in COUNTRY_OPTIONS" :key="c.value" :value="c.value">{{ c.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 等级选择 -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">主播等级</label>
|
||||
<select v-model="addForm.hostsLevel"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:border-blue-500 focus:outline-none">
|
||||
<optgroup v-for="parent in LEVEL_OPTIONS" :key="parent.value" :label="parent.label + '级'">
|
||||
<option v-for="child in parent.children" :key="child.value" :value="child.value">
|
||||
{{ child.label }}
|
||||
</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<div class="p-4 border-t border-gray-200 flex justify-between items-center">
|
||||
<span v-if="addStatus" :class="['text-sm', addStatus.type === 'success' ? 'text-green-600' : 'text-red-600']">
|
||||
{{ addStatus.message }}
|
||||
</span>
|
||||
<span v-else></span>
|
||||
<div class="flex gap-3">
|
||||
<button @click="closeAddDialog"
|
||||
class="px-4 py-2 text-sm text-gray-600 hover:bg-gray-100 rounded-lg">
|
||||
取消
|
||||
</button>
|
||||
<button @click="handleAddHosts" :disabled="addLoading || parsedIds.length === 0"
|
||||
class="px-4 py-2 text-sm bg-green-500 text-white rounded-lg hover:bg-green-600 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
{{ addLoading ? '导入中...' : `导入 ${parsedIds.length} 个主播` }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
@@ -231,6 +313,57 @@ const maxCount = ref(100)
|
||||
const selectedLevels = ref(new Set())
|
||||
const showLevelDropdown = ref(false)
|
||||
|
||||
// 添加主播弹窗状态
|
||||
const showAddDialog = ref(false)
|
||||
const addLoading = ref(false)
|
||||
const addStatus = ref(null)
|
||||
const addForm = ref({
|
||||
idsText: '',
|
||||
invitationType: '1',
|
||||
country: '美国',
|
||||
hostsLevel: 'A1',
|
||||
})
|
||||
|
||||
// 国家选项
|
||||
const COUNTRY_OPTIONS = [
|
||||
{ label: '美国', value: '美国', eng: 'United States' },
|
||||
{ label: '英国', value: '英国', eng: 'United Kingdom' },
|
||||
{ label: '加拿大', value: '加拿大', eng: 'Canada' },
|
||||
{ label: '澳大利亚', value: '澳大利亚', eng: 'Australia' },
|
||||
{ label: '德国', value: '德国', eng: 'Germany' },
|
||||
{ label: '法国', value: '法国', eng: 'France' },
|
||||
{ label: '日本', value: '日本', eng: 'Japan' },
|
||||
{ label: '韩国', value: '韩国', eng: 'South Korea' },
|
||||
{ label: '巴西', value: '巴西', eng: 'Brazil' },
|
||||
{ label: '印度尼西亚', value: '印度尼西亚', eng: 'Indonesia' },
|
||||
{ label: '墨西哥', value: '墨西哥', eng: 'Mexico' },
|
||||
{ label: '菲律宾', value: '菲律宾', eng: 'Philippines' },
|
||||
{ label: '越南', value: '越南', eng: 'Vietnam' },
|
||||
{ label: '泰国', value: '泰国', eng: 'Thailand' },
|
||||
{ label: '马来西亚', value: '马来西亚', eng: 'Malaysia' },
|
||||
{ label: '沙特阿拉伯', value: '沙特阿拉伯', eng: 'Saudi Arabia' },
|
||||
{ label: '西班牙', value: '西班牙', eng: 'Spain' },
|
||||
{ label: '意大利', value: '意大利', eng: 'Italy' },
|
||||
{ label: '土耳其', value: '土耳其', eng: 'Turkey' },
|
||||
{ label: '埃及', value: '埃及', eng: 'Egypt' },
|
||||
{ label: '尼日利亚', value: '尼日利亚', eng: 'Nigeria' },
|
||||
{ label: '哥伦比亚', value: '哥伦比亚', eng: 'Colombia' },
|
||||
{ label: '阿根廷', value: '阿根廷', eng: 'Argentina' },
|
||||
{ label: '智利', value: '智利', eng: 'Chile' },
|
||||
{ label: '秘鲁', value: '秘鲁', eng: 'Peru' },
|
||||
{ label: '以色列', value: '以色列', eng: 'Israel' },
|
||||
{ label: '伊拉克', value: '伊拉克', eng: 'Iraq' },
|
||||
{ label: '约旦', value: '约旦', eng: 'Jordan' },
|
||||
]
|
||||
|
||||
// 解析输入的主播ID列表
|
||||
const parsedIds = computed(() => {
|
||||
return addForm.value.idsText
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter(id => id.length > 0)
|
||||
})
|
||||
|
||||
// Lifecycle
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal) {
|
||||
@@ -273,6 +406,7 @@ const loadHosts = async () => {
|
||||
if (!isElectron()) return
|
||||
try {
|
||||
const data = await window.electronAPI.loadAnchorData()
|
||||
console.log('加载主播数据:', data)
|
||||
hosts.value = data
|
||||
selected.value = new Set()
|
||||
} catch (e) {
|
||||
@@ -413,22 +547,89 @@ const invertSelect = () => {
|
||||
selected.value = next
|
||||
}
|
||||
|
||||
const deleteSelected = () => {
|
||||
const deleteSelected = async () => {
|
||||
if (!selected.value.size) return
|
||||
if (!confirm(`确认删除选中的 ${selected.value.size} 项吗?`)) return
|
||||
|
||||
const remaining = hosts.value.filter(h => !selected.value.has(h.anchorId))
|
||||
hosts.value = remaining
|
||||
selected.value = new Set()
|
||||
if (isElectron()) {
|
||||
try {
|
||||
const ids = Array.from(selected.value)
|
||||
const result = await window.electronAPI.deleteAnchorData(ids)
|
||||
if (result.success) {
|
||||
console.log('[HostListDialog] 删除成功,重新加载数据')
|
||||
await loadHosts()
|
||||
} else {
|
||||
console.error('[HostListDialog] 删除失败:', result.error)
|
||||
// fallback: 前端本地删除
|
||||
hosts.value = hosts.value.filter(h => !selected.value.has(h.anchorId))
|
||||
selected.value = new Set()
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[HostListDialog] 删除异常:', e)
|
||||
hosts.value = hosts.value.filter(h => !selected.value.has(h.anchorId))
|
||||
selected.value = new Set()
|
||||
}
|
||||
} else {
|
||||
hosts.value = hosts.value.filter(h => !selected.value.has(h.anchorId))
|
||||
selected.value = new Set()
|
||||
}
|
||||
}
|
||||
|
||||
const onClose = () => emit('close')
|
||||
|
||||
const handleSave = async () => {
|
||||
if (isElectron()) {
|
||||
await window.electronAPI.saveAnchorData(JSON.parse(JSON.stringify(hosts.value)))
|
||||
}
|
||||
emit('save', hosts.value)
|
||||
onClose()
|
||||
}
|
||||
|
||||
// 关闭添加弹窗
|
||||
const closeAddDialog = () => {
|
||||
showAddDialog.value = false
|
||||
addForm.value = { idsText: '', invitationType: '1', country: '美国', hostsLevel: 'A1' }
|
||||
addStatus.value = null
|
||||
addLoading.value = false
|
||||
}
|
||||
|
||||
// 批量添加主播
|
||||
const handleAddHosts = async () => {
|
||||
const ids = parsedIds.value
|
||||
if (ids.length === 0) return
|
||||
|
||||
addLoading.value = true
|
||||
addStatus.value = null
|
||||
|
||||
// 查找国家英文名
|
||||
const countryObj = COUNTRY_OPTIONS.find(c => c.value === addForm.value.country)
|
||||
|
||||
// 构造批量记录
|
||||
const records = ids.map(hostsId => ({
|
||||
hostsId,
|
||||
invitationType: addForm.value.invitationType,
|
||||
country: addForm.value.country || null,
|
||||
countryEng: countryObj?.eng || null,
|
||||
hostsLevel: addForm.value.hostsLevel || null,
|
||||
createTime: Date.now(),
|
||||
}))
|
||||
|
||||
if (isElectron()) {
|
||||
try {
|
||||
const result = await window.electronAPI.addAnchorData(records)
|
||||
if (result.success) {
|
||||
addStatus.value = { type: 'success', message: `成功导入 ${ids.length} 个主播` }
|
||||
// 重新加载列表
|
||||
await loadHosts()
|
||||
// 延迟关闭弹窗
|
||||
setTimeout(() => closeAddDialog(), 1000)
|
||||
} else {
|
||||
addStatus.value = { type: 'error', message: result.error || '导入失败' }
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[HostListDialog] 添加主播失败:', e)
|
||||
addStatus.value = { type: 'error', message: '导入异常: ' + String(e) }
|
||||
}
|
||||
} else {
|
||||
addStatus.value = { type: 'error', message: '非 Electron 环境,无法添加' }
|
||||
}
|
||||
addLoading.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
<template>
|
||||
<div v-if="activeNotices.length > 0"
|
||||
:class="['notice-bar', `notice-bar--${currentNotice.type || 'info'}`]">
|
||||
<!-- info / 无 category 的公告:滚动栏显示 title -->
|
||||
<div v-if="infoNotices.length > 0"
|
||||
:class="['notice-bar', 'notice-bar--info']">
|
||||
<!-- 图标 -->
|
||||
<span class="material-icons-round notice-bar__icon">campaign</span>
|
||||
|
||||
<!-- 滚动内容区域 -->
|
||||
<div class="notice-bar__content" ref="wrapRef">
|
||||
<div class="notice-bar__text" ref="textRef" :style="animationStyle">
|
||||
{{ currentNotice.content }}
|
||||
{{ currentNotice.title }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 多条公告时显示计数 -->
|
||||
<span v-if="activeNotices.length > 1" class="notice-bar__count">
|
||||
{{ currentIndex + 1 }}/{{ activeNotices.length }}
|
||||
<span v-if="infoNotices.length > 1" class="notice-bar__count">
|
||||
{{ currentIndex + 1 }}/{{ infoNotices.length }}
|
||||
</span>
|
||||
|
||||
<!-- 关闭按钮 -->
|
||||
@@ -22,6 +23,28 @@
|
||||
<span class="material-icons-round text-base">close</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- danger / warning 的公告:弹窗逐条显示 title + content -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="currentAlert?.title"
|
||||
width="480px"
|
||||
:close-on-click-modal="false"
|
||||
align-center
|
||||
>
|
||||
<div class="alert-notice__content" v-html="currentAlert?.content"></div>
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="alertIndex < alertNotices.length - 1"
|
||||
@click="nextAlert"
|
||||
>
|
||||
下一条 ({{ alertIndex + 1 }}/{{ alertNotices.length }})
|
||||
</el-button>
|
||||
<el-button type="primary" @click="closeAlert">
|
||||
{{ alertIndex < alertNotices.length - 1 ? '全部关闭' : '我知道了' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -37,11 +60,12 @@ const props = defineProps({
|
||||
const { t } = useI18n()
|
||||
const noticeStore = useNoticeStore()
|
||||
|
||||
const activeNotices = computed(() => noticeStore.activeNotices)
|
||||
// ========== info 滚动栏 ==========
|
||||
const infoNotices = computed(() => noticeStore.infoNotices)
|
||||
|
||||
// 当前显示的公告索引(多条轮播)
|
||||
const currentIndex = ref(0)
|
||||
const currentNotice = computed(() => activeNotices.value[currentIndex.value] || {})
|
||||
const currentNotice = computed(() => infoNotices.value[currentIndex.value] || {})
|
||||
|
||||
// 滚动动画
|
||||
const wrapRef = ref(null)
|
||||
@@ -78,10 +102,10 @@ let rotateTimer = null
|
||||
|
||||
const startRotate = () => {
|
||||
stopRotate()
|
||||
if (activeNotices.value.length <= 1) return
|
||||
if (infoNotices.value.length <= 1) return
|
||||
|
||||
rotateTimer = setInterval(() => {
|
||||
currentIndex.value = (currentIndex.value + 1) % activeNotices.value.length
|
||||
currentIndex.value = (currentIndex.value + 1) % infoNotices.value.length
|
||||
}, 8000) // 每 8 秒切换
|
||||
}
|
||||
|
||||
@@ -98,7 +122,7 @@ const handleClose = () => {
|
||||
if (notice && notice.id) {
|
||||
noticeStore.dismissNotice(notice.id)
|
||||
// 调整索引
|
||||
if (currentIndex.value >= activeNotices.value.length) {
|
||||
if (currentIndex.value >= infoNotices.value.length) {
|
||||
currentIndex.value = 0
|
||||
}
|
||||
}
|
||||
@@ -110,7 +134,7 @@ watch(currentNotice, () => {
|
||||
calculateScroll()
|
||||
}, { flush: 'post' })
|
||||
|
||||
watch(() => activeNotices.value.length, (len) => {
|
||||
watch(() => infoNotices.value.length, (len) => {
|
||||
if (len > 1) {
|
||||
startRotate()
|
||||
} else {
|
||||
@@ -118,9 +142,43 @@ watch(() => activeNotices.value.length, (len) => {
|
||||
}
|
||||
})
|
||||
|
||||
// ========== danger / warning 弹窗 ==========
|
||||
const alertNotices = computed(() => noticeStore.alertNotices)
|
||||
const dialogVisible = ref(false)
|
||||
const alertIndex = ref(0)
|
||||
|
||||
const currentAlert = computed(() => alertNotices.value[alertIndex.value] || null)
|
||||
|
||||
// 下一条弹窗公告
|
||||
const nextAlert = () => {
|
||||
// 关闭当前这条
|
||||
if (currentAlert.value) {
|
||||
noticeStore.dismissNotice(currentAlert.value.id)
|
||||
}
|
||||
alertIndex.value++
|
||||
if (alertIndex.value >= alertNotices.value.length) {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭全部弹窗公告
|
||||
const closeAlert = () => {
|
||||
alertNotices.value.forEach(n => noticeStore.dismissNotice(n.id))
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
// 有弹窗类公告时自动弹出
|
||||
watch(alertNotices, (list) => {
|
||||
if (list.length > 0 && !dialogVisible.value) {
|
||||
alertIndex.value = 0
|
||||
dialogVisible.value = true
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// ========== 生命周期 ==========
|
||||
onMounted(() => {
|
||||
calculateScroll()
|
||||
if (activeNotices.value.length > 1) {
|
||||
if (infoNotices.value.length > 1) {
|
||||
startRotate()
|
||||
}
|
||||
})
|
||||
@@ -151,31 +209,6 @@ onUnmounted(() => {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.notice-bar--warning {
|
||||
background-color: #fffbeb;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.notice-bar--warning .notice-bar__icon {
|
||||
color: #f59e0b;
|
||||
}
|
||||
.notice-bar--danger {
|
||||
background-color: rgb(253, 220, 220);
|
||||
color: #f15252;
|
||||
}
|
||||
|
||||
.notice-bar--danger .notice-bar__icon {
|
||||
color: #ff0000;
|
||||
}
|
||||
.notice-bar--urgent {
|
||||
background-color: #fef2f2;
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.notice-bar--urgent .notice-bar__icon {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
/* 图标 */
|
||||
.notice-bar__icon {
|
||||
font-size: 22px;
|
||||
@@ -246,4 +279,19 @@ onUnmounted(() => {
|
||||
opacity: 1;
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* 弹窗公告内容(v-html 渲染,需要用 :deep 穿透 scoped) */
|
||||
.alert-notice__content {
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.alert-notice__content :deep(p) {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.alert-notice__content :deep(p:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -33,9 +33,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||||
import { getMainUserData, setStorage, getStorage } from '@/utils/pk-mini/storage'
|
||||
import { goEasyGetConversations } from '@/utils/pk-mini/goeasy'
|
||||
import { goEasyGetConversations, getPkGoEasy, GoEasy } from '@/utils/pk-mini/goeasy'
|
||||
import { pkUnreadStore } from '@/stores/pk-mini/notice.js'
|
||||
import { signIn } from '@/api/pk-mini'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
@@ -50,7 +51,8 @@ const props = defineProps({
|
||||
|
||||
const defaultAvatar = 'https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/default-avatar.png'
|
||||
const userInfo = ref({})
|
||||
const unreadCount = ref(0)
|
||||
const unreadStore = pkUnreadStore()
|
||||
const unreadCount = computed(() => unreadStore.count)
|
||||
const activeId = ref('pk')
|
||||
|
||||
const navigationModule = [
|
||||
@@ -79,30 +81,42 @@ function handleSettings() {
|
||||
|
||||
function getChatList() {
|
||||
goEasyGetConversations().then((res) => {
|
||||
if (res?.content?.unreadTotal) {
|
||||
unreadCount.value = res.content.unreadTotal
|
||||
}
|
||||
unreadStore.setCount(res?.content?.unreadTotal || 0)
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function onMessageReceived() {
|
||||
unreadStore.setCount(unreadStore.count + 1)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 获取用户信息
|
||||
const userData = getMainUserData()
|
||||
if (userData) {
|
||||
userInfo.value = userData
|
||||
}
|
||||
|
||||
// 获取保存的 activeId
|
||||
const savedId = getStorage('activeId')
|
||||
if (savedId) {
|
||||
activeId.value = savedId
|
||||
}
|
||||
|
||||
// 获取未读消息数
|
||||
setTimeout(() => {
|
||||
getChatList()
|
||||
const goeasy = getPkGoEasy()
|
||||
if (goeasy) {
|
||||
goeasy.im.on(GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, onMessageReceived)
|
||||
}
|
||||
}, 2000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
const goeasy = getPkGoEasy()
|
||||
if (goeasy) {
|
||||
try {
|
||||
goeasy.im.off(GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, onMessageReceived)
|
||||
} catch (e) {}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
@@ -1,76 +1,71 @@
|
||||
<template>
|
||||
<div class="chat-message-mini-pk">
|
||||
<!-- 用户A -->
|
||||
<div class="userA">
|
||||
<div class="Avatar">
|
||||
<img class="AvatarImg" :src="ArticleDetailsA.anchorIcon" alt="" />
|
||||
<div class="name">{{ ArticleDetailsA.anchorId }}</div>
|
||||
</div>
|
||||
<div class="genderAndCountry">
|
||||
<div class="gender" :style="{ background: ArticleDetailsA.sex == 1 ? '#59D8DB' : '#F3876F' }">
|
||||
{{ ArticleDetailsA.sex == 1 ? $t('pkMini.man') : $t('pkMini.woman') }}
|
||||
<div class="chat-message-mini-pk" :class="{ compact: compact }">
|
||||
<!-- 卡片区域(相对定位容器,VS 绝对叠在中间) -->
|
||||
<div class="pk-cards">
|
||||
<!-- 用户A -->
|
||||
<div class="userA">
|
||||
<div class="genderAndCountry">
|
||||
<div class="gender" :style="{ background: ArticleDetailsA.sex == 1 ? '#59D8DB' : '#F3876F' }">
|
||||
{{ ArticleDetailsA.sex == 1 ? $t('pkMini.man') : $t('pkMini.woman') }}
|
||||
</div>
|
||||
<div class="Country">{{ ArticleDetailsA.country }}</div>
|
||||
</div>
|
||||
<div class="Country">{{ ArticleDetailsA.country }}</div>
|
||||
</div>
|
||||
<div class="time">
|
||||
{{ $t('pkMini.PKTime') + TimestamptolocalTime(PkIDInfodata.pkTime * 1000) }}
|
||||
</div>
|
||||
<div class="PKinformation">
|
||||
<div class="gold">
|
||||
<img class="gold-img" src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/gold.png" alt="" />
|
||||
<div class="sessions-content">
|
||||
{{ $t('pkMini.GoldCoin') }}
|
||||
<div class="gold-num">{{ ArticleDetailsA.coin }}K</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sessions">
|
||||
<img class="sessions-img" src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/session.png" alt="" />
|
||||
<div class="sessions-content">
|
||||
{{ $t('pkMini.session') }}
|
||||
<div class="gold-num">{{ PkIDInfodata.pkNumber + $t('pkMini.match') }}</div>
|
||||
<div class="time">
|
||||
{{ $t('pkMini.PKTime') + TimestamptolocalTime(PkIDInfodata.pkTime * 1000) }}
|
||||
</div>
|
||||
<div class="PKinformation">
|
||||
<div class="gold">
|
||||
<img class="gold-img" src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/gold.png" alt="" />
|
||||
<div class="sessions-content">
|
||||
{{ $t('pkMini.GoldCoin') }}
|
||||
<div class="gold-num">{{ ArticleDetailsA.coin }}K</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sessions">
|
||||
<img class="sessions-img" src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/session.png" alt="" />
|
||||
<div class="sessions-content">
|
||||
{{ $t('pkMini.session') }}
|
||||
<div class="gold-num">{{ PkIDInfodata.pkNumber + $t('pkMini.match') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Remarks">{{ $t('pkMini.Note') + ArticleDetailsA.remark }}</div>
|
||||
</div>
|
||||
<div class="Remarks">{{ $t('pkMini.Note') + ArticleDetailsA.remark }}</div>
|
||||
</div>
|
||||
|
||||
<!-- VS -->
|
||||
<div class="messageVS">
|
||||
<img class="messageVS-img" src="@/assets/pk-mini/messageVS.png" alt="" />
|
||||
</div>
|
||||
<!-- VS 叠在两卡片中间缝 -->
|
||||
<div class="messageVS">
|
||||
<img class="messageVS-img" src="@/assets/pk-mini/messageVS.png" alt="" />
|
||||
</div>
|
||||
|
||||
<!-- 用户B -->
|
||||
<div class="userB">
|
||||
<div class="Avatar">
|
||||
<img class="AvatarImg" :src="ArticleDetailsB.anchorIcon" alt="" />
|
||||
<div class="name">{{ ArticleDetailsB.anchorId }}</div>
|
||||
</div>
|
||||
<div class="genderAndCountry">
|
||||
<div class="gender" :style="{ background: ArticleDetailsB.sex == 1 ? '#59D8DB' : '#F3876F' }">
|
||||
{{ ArticleDetailsB.sex == 1 ? $t('pkMini.man') : $t('pkMini.woman') }}
|
||||
<!-- 用户B -->
|
||||
<div class="userB">
|
||||
<div class="genderAndCountry">
|
||||
<div class="gender" :style="{ background: ArticleDetailsB.sex == 1 ? '#59D8DB' : '#F3876F' }">
|
||||
{{ ArticleDetailsB.sex == 1 ? $t('pkMini.man') : $t('pkMini.woman') }}
|
||||
</div>
|
||||
<div class="Country">{{ ArticleDetailsB.country }}</div>
|
||||
</div>
|
||||
<div class="Country">{{ ArticleDetailsB.country }}</div>
|
||||
</div>
|
||||
<div class="time">
|
||||
{{ $t('pkMini.PKTime') + TimestamptolocalTime(PkIDInfodata.pkTime * 1000) }}
|
||||
</div>
|
||||
<div class="PKinformation">
|
||||
<div class="gold">
|
||||
<img class="gold-img" src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/gold.png" alt="" />
|
||||
<div class="sessions-content">
|
||||
{{ $t('pkMini.GoldCoin') }}
|
||||
<div class="gold-num">{{ ArticleDetailsB.coin }}K</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sessions">
|
||||
<img class="sessions-img" src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/session.png" alt="" />
|
||||
<div class="sessions-content">
|
||||
{{ $t('pkMini.session') }}
|
||||
<div class="gold-num">{{ PkIDInfodata.pkNumber + $t('pkMini.match') }}</div>
|
||||
<div class="time">
|
||||
{{ $t('pkMini.PKTime') + TimestamptolocalTime(PkIDInfodata.pkTime * 1000) }}
|
||||
</div>
|
||||
<div class="PKinformation">
|
||||
<div class="gold">
|
||||
<img class="gold-img" src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/gold.png" alt="" />
|
||||
<div class="sessions-content">
|
||||
{{ $t('pkMini.GoldCoin') }}
|
||||
<div class="gold-num">{{ ArticleDetailsB.coin }}K</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sessions">
|
||||
<img class="sessions-img" src="https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/session.png" alt="" />
|
||||
<div class="sessions-content">
|
||||
{{ $t('pkMini.session') }}
|
||||
<div class="gold-num">{{ PkIDInfodata.pkNumber + $t('pkMini.match') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Remarks">{{ $t('pkMini.Note') + ArticleDetailsB.remark }}</div>
|
||||
</div>
|
||||
<div class="Remarks">{{ $t('pkMini.Note') + ArticleDetailsB.remark }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 按钮 -->
|
||||
@@ -123,6 +118,10 @@ const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
compact: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -196,58 +195,43 @@ onMounted(() => {
|
||||
|
||||
<style scoped>
|
||||
.chat-message-mini-pk {
|
||||
width: 325px;
|
||||
height: 820px;
|
||||
width: 560px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 15px 0;
|
||||
}
|
||||
.pk-cards {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.messageVS {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 67px;
|
||||
height: 67px;
|
||||
margin-top: -33.5px;
|
||||
margin-bottom: -33.5px;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
.messageVS-img {
|
||||
width: 67px;
|
||||
height: 67px;
|
||||
}
|
||||
.userA {
|
||||
width: 90%;
|
||||
height: 335px;
|
||||
width: 100%;
|
||||
background-color: #c0e8e8;
|
||||
border-top-left-radius: 20px;
|
||||
border-top-right-radius: 20px;
|
||||
margin-top: 20px;
|
||||
border-radius: 20px 20px 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.Avatar {
|
||||
width: 90%;
|
||||
height: 50px;
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.AvatarImg {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.name {
|
||||
width: calc(100% - 60px);
|
||||
margin-left: 10px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.genderAndCountry {
|
||||
width: 90%;
|
||||
@@ -314,15 +298,13 @@ onMounted(() => {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.userB {
|
||||
width: 90%;
|
||||
height: 315px;
|
||||
width: 100%;
|
||||
background-color: #f8e4e0;
|
||||
border-bottom-left-radius: 20px;
|
||||
border-bottom-right-radius: 20px;
|
||||
padding-top: 20px;
|
||||
border-radius: 0 0 20px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.btn {
|
||||
margin-top: 20px;
|
||||
@@ -424,4 +406,29 @@ onMounted(() => {
|
||||
border-radius: 100px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 紧凑模式:适配 PK 大厅 350px 窄聊天框 */
|
||||
.compact.chat-message-mini-pk {
|
||||
width: 300px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.compact .pk-cards {
|
||||
width: 92%;
|
||||
}
|
||||
.compact .userA {
|
||||
border-radius: 12px 12px 0 0;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.compact .userB {
|
||||
border-radius: 0 0 12px 12px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.compact .messageVS {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.compact .messageVS-img {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<!-- 我的PK记录 -->
|
||||
<div class="pk-record">
|
||||
<el-splitter>
|
||||
<el-splitter-panel>
|
||||
<div class="pk-layout">
|
||||
<div class="left-panel">
|
||||
<div class="demo-panel">
|
||||
<!-- 选项卡 -->
|
||||
<div class="tab-header">
|
||||
@@ -62,10 +62,10 @@
|
||||
|
||||
<div class="empty-tip" v-else>您还没有PK记录!</div>
|
||||
</div>
|
||||
</el-splitter-panel>
|
||||
</div>
|
||||
|
||||
<!-- 右侧详情 -->
|
||||
<el-splitter-panel :size="30" :resizable="false">
|
||||
<div class="right-panel">
|
||||
<div class="detail-panel" v-if="selectedData">
|
||||
<!-- 双方头像 -->
|
||||
<div class="detail-avatars">
|
||||
@@ -110,8 +110,8 @@
|
||||
<div class="empty-detail" v-else>
|
||||
<span>选择右侧的记录,可立即查看详细信息</span>
|
||||
</div>
|
||||
</el-splitter-panel>
|
||||
</el-splitter>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -232,6 +232,28 @@ onMounted(() => {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pk-layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
width: 380px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border-left: 1px solid #03aba82f;
|
||||
}
|
||||
|
||||
.demo-panel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -378,53 +400,54 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-left: 1px solid #03aba82f;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.detail-avatars {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
margin-bottom: 20px;
|
||||
gap: 20px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.detail-avatar {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.detail-total {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.total-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
padding: 15px;
|
||||
padding: 10px;
|
||||
background: linear-gradient(90deg, #e4ffff, #fff, #e4ffff);
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.total-num {
|
||||
font-size: 16px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.total-icon {
|
||||
width: 35px;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.detail-rounds {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -432,9 +455,9 @@ onMounted(() => {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 15px;
|
||||
border-radius: 16px;
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
border-radius: 12px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@@ -449,9 +472,9 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.round-item {
|
||||
padding: 12px 15px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #03aba8;
|
||||
text-align: center;
|
||||
|
||||
Reference in New Issue
Block a user