mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-30 04:03:48 +02:00
ui improve
This commit is contained in:
@@ -68,7 +68,7 @@
|
||||
<el-table-column width="20"></el-table-column>
|
||||
<el-table-column
|
||||
:label="t('table.friendList.no')"
|
||||
width="70"
|
||||
width="100"
|
||||
prop="$friendNumber"
|
||||
:sortable="true"
|
||||
fixed="left">
|
||||
@@ -141,22 +141,25 @@
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('table.friendList.bioLink')" width="130" prop="bioLinks">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip v-for="(link, index) in row.bioLinks.filter(Boolean)" :key="index">
|
||||
<template #content>
|
||||
<span v-text="link"></span>
|
||||
</template>
|
||||
<img
|
||||
:src="getFaviconUrl(link)"
|
||||
style="
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
cursor: pointer;
|
||||
"
|
||||
@click.stop="openExternalLink(link)"
|
||||
loading="lazy" />
|
||||
</el-tooltip>
|
||||
<div class="flex items-center">
|
||||
<el-tooltip v-for="(link, index) in row.bioLinks.filter(Boolean)" :key="index">
|
||||
<template #content>
|
||||
<span v-text="link"></span>
|
||||
</template>
|
||||
|
||||
<img
|
||||
:src="getFaviconUrl(link)"
|
||||
style="
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
cursor: pointer;
|
||||
"
|
||||
@click.stop="openExternalLink(link)"
|
||||
loading="lazy" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
</div>
|
||||
|
||||
<DataTable v-bind="friendLogTable" :data="friendLogDisplayData">
|
||||
<el-table-column width="20"></el-table-column>
|
||||
<el-table-column :label="t('table.friendLog.date')" prop="created_at" width="200">
|
||||
<template #default="scope">
|
||||
<el-tooltip placement="right">
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
</div>
|
||||
|
||||
<DataTable v-bind="gameLogTable" :data="gameLogDisplayData">
|
||||
<el-table-column width="20"></el-table-column>
|
||||
<el-table-column :label="t('table.gameLog.date')" prop="created_at" width="140">
|
||||
<template #default="scope">
|
||||
<el-tooltip placement="right">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="x-container" ref="moderationRef">
|
||||
<!-- 工具栏 -->
|
||||
<div class="tool-slot">
|
||||
<el-select
|
||||
v-model="playerModerationTable.filters[0].value"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
:data="notificationDisplayData"
|
||||
ref="notificationTableRef"
|
||||
class="notification-table">
|
||||
<el-table-column :label="t('table.notification.date')" prop="created_at" width="110">
|
||||
<el-table-column :label="t('table.notification.date')" prop="created_at" width="130">
|
||||
<template #default="scope">
|
||||
<el-tooltip placement="right">
|
||||
<template #content>
|
||||
|
||||
@@ -125,9 +125,6 @@
|
||||
}}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="options-container">
|
||||
<ThemePicker />
|
||||
</div>
|
||||
<div class="options-container">
|
||||
<span class="header">{{ t('view.settings.appearance.timedate.header') }}</span>
|
||||
<div class="options-container-item">
|
||||
@@ -394,7 +391,6 @@
|
||||
import { getLanguageName, languageCodes } from '../../../../localization';
|
||||
|
||||
import SimpleSwitch from '../SimpleSwitch.vue';
|
||||
import ThemePicker from '../ThemePicker.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
<template>
|
||||
<div class="theme-picker">
|
||||
<div class="theme-picker__header">
|
||||
<div>
|
||||
<span class="header">{{ t('view.settings.appearance.theme_color.header') }}</span>
|
||||
</div>
|
||||
<div class="theme-picker__current ml-25">
|
||||
<span class="theme-picker__chip" :style="{ backgroundColor: currentPrimary }"></span>
|
||||
<button type="button" class="theme-picker__toggle" @click="isOpen = !isOpen">
|
||||
{{ isOpen ? 'Collapse' : 'Expand' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="isOpen" class="theme-picker__panel">
|
||||
<div class="theme-picker__grid">
|
||||
<button
|
||||
v-for="color in colorFamilies"
|
||||
:key="color.name"
|
||||
type="button"
|
||||
class="theme-picker__item"
|
||||
:class="{ 'is-active': color.base === currentPrimary }"
|
||||
:disabled="isApplying"
|
||||
@click="selectColor(color)">
|
||||
<span class="theme-picker__swatch" :style="{ backgroundColor: color.base }"></span>
|
||||
<span class="theme-picker__badge">{{ color.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import colors from 'tailwindcss/colors';
|
||||
|
||||
import { useElementTheme } from '../../../composables/useElementTheme';
|
||||
|
||||
// Tailwind indigo-500
|
||||
const defaultPrimary = 'oklch(58.5% 0.233 277.117)';
|
||||
const { currentPrimary, isApplying, applyPrimaryColor, initPrimaryColor } = useElementTheme(defaultPrimary);
|
||||
const { t } = useI18n();
|
||||
|
||||
const invalidKeys = new Set([
|
||||
'inherit',
|
||||
'current',
|
||||
'transparent',
|
||||
'black',
|
||||
'white',
|
||||
'lightBlue',
|
||||
'warmGray',
|
||||
'trueGray',
|
||||
'coolGray',
|
||||
'blueGray'
|
||||
]);
|
||||
|
||||
const isOpen = ref(false);
|
||||
|
||||
const colorFamilies = computed(() =>
|
||||
Object.entries(colors)
|
||||
.filter(([name, palette]) => {
|
||||
return !invalidKeys.has(name) && palette && typeof palette === 'object' && palette['500'];
|
||||
})
|
||||
.map(([name, palette]) => {
|
||||
const base = palette['500'];
|
||||
const light = palette['300'];
|
||||
const vivid = palette['600'];
|
||||
const dark = palette['700'];
|
||||
return {
|
||||
name,
|
||||
base,
|
||||
light,
|
||||
vivid,
|
||||
dark,
|
||||
palette
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
);
|
||||
|
||||
const selectColor = async (color) => {
|
||||
await applyPrimaryColor(color.base, color.palette);
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await initPrimaryColor(defaultPrimary);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.theme-picker {
|
||||
padding: 6px 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.theme-picker__header {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.theme-picker__current {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: transparent;
|
||||
color: var(--el-text-color-primary);
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.theme-picker__toggle {
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.theme-picker__toggle:hover {
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.theme-picker__chip {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--color-zinc-100);
|
||||
}
|
||||
|
||||
.theme-picker__panel {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.theme-picker__grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
max-height: 360px;
|
||||
gap: 10px 18px;
|
||||
}
|
||||
|
||||
.theme-picker__item {
|
||||
all: unset;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
justify-content: space-between;
|
||||
width: calc(50% - 9px);
|
||||
min-width: 0;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 10px;
|
||||
padding: 8px 12px;
|
||||
background: var(--el-bg-color);
|
||||
transition: border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.theme-picker__item:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.theme-picker__item.is-active {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.theme-picker__item:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.theme-picker__swatch {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--color-zinc-100);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.theme-picker__badge {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
text-transform: capitalize;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.theme-picker__header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.theme-picker__current {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="x-friend-item" @click="$emit('click')">
|
||||
<div class="x-friend-item" @click="showUserDialog(friend.id)">
|
||||
<template v-if="friend.ref">
|
||||
<div
|
||||
class="avatar"
|
||||
@@ -63,7 +63,7 @@
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAppearanceSettingsStore, useFriendStore } from '../../../stores';
|
||||
import { useAppearanceSettingsStore, useFriendStore, useUserStore } from '../../../stores';
|
||||
import { userImage, userStatusClass } from '../../../shared/utils';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -71,10 +71,11 @@
|
||||
isGroupByInstance: Boolean
|
||||
});
|
||||
|
||||
defineEmits(['click', 'confirm-delete-friend']);
|
||||
defineEmits(['confirm-delete-friend']);
|
||||
|
||||
const { hideNicknames } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { isRefreshFriendsLoading } = storeToRefs(useFriendStore());
|
||||
const { showUserDialog } = useUserStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const isFriendTraveling = computed(() => props.friend.ref?.location === 'traveling');
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
v-for="friend in group"
|
||||
:key="friend.id"
|
||||
:friend="friend"
|
||||
@click="showUserDialog(friend.id)"
|
||||
@confirm-delete-friend="confirmDeleteFriend"></friend-item>
|
||||
</div>
|
||||
</div>
|
||||
@@ -73,7 +72,6 @@
|
||||
v-for="friend in vipFriendsByGroupStatus"
|
||||
:key="friend.id"
|
||||
:friend="friend"
|
||||
@click="showUserDialog(friend.id)"
|
||||
@confirm-delete-friend="confirmDeleteFriend">
|
||||
</friend-item>
|
||||
</template>
|
||||
@@ -91,7 +89,7 @@
|
||||
|
||||
<div v-show="!isSidebarGroupByInstanceCollapsed">
|
||||
<div v-for="friendArr in friendsInSameInstance" :key="friendArr[0].ref.$location.tag">
|
||||
<div style="margin-bottom: 3px">
|
||||
<div class="mb-1 flex items-center">
|
||||
<Location class="extra" :location="getFriendsLocations(friendArr)" style="display: inline" />
|
||||
<span class="extra" style="margin-left: 5px">{{ `(${friendArr.length})` }}</span>
|
||||
</div>
|
||||
@@ -102,7 +100,6 @@
|
||||
:friend="friend"
|
||||
is-group-by-instance
|
||||
:style="{ 'margin-bottom': idx === friendArr.length - 1 ? '5px' : undefined }"
|
||||
@click="showUserDialog(friend.id)"
|
||||
@confirm-delete-friend="confirmDeleteFriend">
|
||||
</friend-item>
|
||||
</div>
|
||||
@@ -126,7 +123,6 @@
|
||||
v-for="friend in onlineFriendsByGroupStatus"
|
||||
:key="friend.id"
|
||||
:friend="friend"
|
||||
@click="showUserDialog(friend.id)"
|
||||
@confirm-delete-friend="confirmDeleteFriend" />
|
||||
</div>
|
||||
<div
|
||||
@@ -144,7 +140,6 @@
|
||||
v-for="friend in activeFriends"
|
||||
:key="friend.id"
|
||||
:friend="friend"
|
||||
@click="showUserDialog(friend.id)"
|
||||
@confirm-delete-friend="confirmDeleteFriend"></friend-item>
|
||||
</div>
|
||||
<div
|
||||
@@ -162,7 +157,6 @@
|
||||
v-for="friend in offlineFriends"
|
||||
:key="friend.id"
|
||||
:friend="friend"
|
||||
@click="showUserDialog(friend.id)"
|
||||
@confirm-delete-friend="confirmDeleteFriend"></friend-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="gallery-page x-container">
|
||||
<div class="gallery-page__header">
|
||||
<el-button text size="small" :icon="ArrowLeft" class="gallery-page__back" @click="goBack">
|
||||
<el-button text :icon="ArrowLeft" class="gallery-page__back" @click="goBack">
|
||||
{{ t('nav_tooltip.tools') }}
|
||||
</el-button>
|
||||
<span class="header">{{ t('dialog.gallery_icons.header') }}</span>
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.el-icon-arrow-right {
|
||||
|
||||
@@ -194,108 +194,132 @@
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
border-radius: 8px;
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
}
|
||||
&.grouped-card {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.grid-card {
|
||||
flex: 0 0 280px;
|
||||
max-width: 280px;
|
||||
}
|
||||
&.group-dialog-grid-card {
|
||||
flex: 0 0 320px;
|
||||
max-width: 320px;
|
||||
}
|
||||
:deep(.el-card__body) {
|
||||
overflow: visible;
|
||||
}
|
||||
.banner {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 8px 8px 0 0;
|
||||
.timeline-view & {
|
||||
height: 125px;
|
||||
}
|
||||
.grid-view & {
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
.following-badge {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -9px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--el-text-color-regular);
|
||||
color: var(--el-bg-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
box-shadow: var(--el-box-shadow-lighter);
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
}
|
||||
.is-following {
|
||||
background-color: var(--group-calendar-badge-following, var(--el-color-success));
|
||||
}
|
||||
.event-content {
|
||||
font-size: 12px;
|
||||
.timeline-view & {
|
||||
padding: 4px 12px 12px 12px;
|
||||
}
|
||||
.grid-view & {
|
||||
padding: 8px 12px 12px 12px;
|
||||
}
|
||||
.event-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.grid-view & {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.event-group-name {
|
||||
cursor: pointer;
|
||||
.grid-view & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.event-title-content {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
.timeline-view & {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.event-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.timeline-view & > :first-child {
|
||||
font-size: 14px;
|
||||
}
|
||||
.grid-view & {
|
||||
font-size: 11px;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
.event-time {
|
||||
font-weight: 500;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.event-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
}
|
||||
|
||||
.event-card.grouped-card {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.event-card.grid-card {
|
||||
flex: 0 0 280px;
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
.event-card.group-dialog-grid-card {
|
||||
flex: 0 0 320px;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.event-card :deep(.el-card__body) {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.event-card .banner {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.timeline-view .event-card .banner {
|
||||
height: 125px;
|
||||
}
|
||||
|
||||
.grid-view .event-card .banner {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.event-card .following-badge {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -9px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--el-text-color-regular);
|
||||
color: var(--el-bg-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
box-shadow: var(--el-box-shadow-lighter);
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.event-card .following-badge.is-following {
|
||||
background-color: var(--group-calendar-badge-following, var(--el-color-success));
|
||||
}
|
||||
|
||||
.event-card .event-content {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.timeline-view .event-card .event-content {
|
||||
padding: 4px 12px 12px 12px;
|
||||
}
|
||||
|
||||
.grid-view .event-card .event-content {
|
||||
padding: 8px 12px 12px 12px;
|
||||
}
|
||||
|
||||
.event-card .event-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.grid-view .event-card .event-title {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.event-card .event-group-name {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.grid-view .event-card .event-group-name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.event-card .event-title-content {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.timeline-view .event-card .event-title-content {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.event-card .event-title-content:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.event-card .event-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.timeline-view .event-card .event-info > :first-child {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.grid-view .event-card .event-info {
|
||||
font-size: 11px;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
.event-card .event-time {
|
||||
font-weight: 500;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
:deep(.el-card) {
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user