mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 22:46:06 +02:00
ui improve
This commit is contained in:
+25
-8
@@ -55,9 +55,9 @@
|
|||||||
--el-bg-color-page: var(--color-zinc-50);
|
--el-bg-color-page: var(--color-zinc-50);
|
||||||
--el-bg-color-overlay: var(--color-white);
|
--el-bg-color-overlay: var(--color-white);
|
||||||
|
|
||||||
--el-text-color-secondary: var(--color-zinc-500);
|
--el-text-color-secondary: var(--color-zinc-700);
|
||||||
--el-text-color-placeholder: var(--color-zinc-400);
|
--el-text-color-placeholder: var(--color-zinc-500);
|
||||||
--el-text-color-disabled: var(--color-zinc-300);
|
--el-text-color-disabled: var(--color-zinc-400);
|
||||||
|
|
||||||
--el-border-color: var(--color-zinc-200);
|
--el-border-color: var(--color-zinc-200);
|
||||||
--el-border-color-light: var(--color-zinc-200);
|
--el-border-color-light: var(--color-zinc-200);
|
||||||
@@ -297,6 +297,10 @@ html.dark,
|
|||||||
color: var(--color-neutral-200);
|
color: var(--color-neutral-200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
background-color: var(--el-bg-color-page);
|
||||||
|
}
|
||||||
|
|
||||||
.el-table .el-table__empty-block {
|
.el-table .el-table__empty-block {
|
||||||
background-color: var(--el-bg-color-page);
|
background-color: var(--el-bg-color-page);
|
||||||
}
|
}
|
||||||
@@ -631,17 +635,30 @@ html.dark .x-friend-item > .detail > .extra,
|
|||||||
width: 2px;
|
width: 2px;
|
||||||
translate: 0 -50%;
|
translate: 0 -50%;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: color-mix(in oklch, var(--el-color-primary) 32%, transparent);
|
background: var(--el-color-primary);
|
||||||
transition:
|
transition:
|
||||||
background-color 0.4s,
|
background-color 0.4s,
|
||||||
left 0.2s;
|
left 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu-item.is-active::before {
|
.el-menu-item.is-active::before {
|
||||||
left: 2px;
|
left: 4px;
|
||||||
transition: left 0.2s cubic-bezier(0.175, 0.885, 0.32, 2.552);
|
transition: left 0.2s cubic-bezier(0.175, 0.885, 0.32, 2.552);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-menu.el-menu--popup .el-menu-item::before {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu.el-menu--popup .el-menu-item.is-active::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu.el-menu--popup .el-menu-item > i {
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.el-sub-menu .el-menu-item.notify::after {
|
.el-sub-menu .el-menu-item.notify::after {
|
||||||
left: 18px;
|
left: 18px;
|
||||||
}
|
}
|
||||||
@@ -922,7 +939,7 @@ hr.x-vertical-divider {
|
|||||||
width: 4px;
|
width: 4px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
content: '';
|
content: '';
|
||||||
background: var(--el-text-color-placeholder);
|
background: var(--el-text-color-secondary);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1273,8 +1290,8 @@ i.x-status-icon.red {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-age-verification {
|
.x-tag-age-verification {
|
||||||
color: var(--el-color-primary-dark-2);
|
color: #3b82f6;
|
||||||
border-color: var(--el-color-primary-dark-2) !important;
|
border-color: #3b82f6 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-border-left {
|
.x-tag-border-left {
|
||||||
|
|||||||
@@ -188,7 +188,6 @@
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.data-table-wrapper {
|
.data-table-wrapper {
|
||||||
margin: 0 3px;
|
|
||||||
font-feature-settings:
|
font-feature-settings:
|
||||||
'tnum' 1,
|
'tnum' 1,
|
||||||
'lnum' 1;
|
'lnum' 1;
|
||||||
|
|||||||
@@ -14,11 +14,7 @@
|
|||||||
@click="handleShowWorldDialog">
|
@click="handleShowWorldDialog">
|
||||||
<el-icon :class="['is-loading']" class="mr-1" v-if="isTraveling"><Loading /></el-icon>
|
<el-icon :class="['is-loading']" class="mr-1" v-if="isTraveling"><Loading /></el-icon>
|
||||||
<span class="min-w-0 truncate">{{ text }}</span>
|
<span class="min-w-0 truncate">{{ text }}</span>
|
||||||
<span
|
<span v-if="groupName" class="ml-0.5 whitespace-nowrap x-link" @click.stop="handleShowGroupDialog">
|
||||||
v-if="groupName"
|
|
||||||
class="ml-0.5 whitespace-nowrap"
|
|
||||||
:class="{ 'x-link': link }"
|
|
||||||
@click.stop="handleShowGroupDialog">
|
|
||||||
({{ groupName }})
|
({{ groupName }})
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -229,7 +225,7 @@
|
|||||||
|
|
||||||
function handleShowGroupDialog() {
|
function handleShowGroupDialog() {
|
||||||
let location = currentInstanceId();
|
let location = currentInstanceId();
|
||||||
if (!location || !props.link) {
|
if (!location) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const L = parseLocation(location);
|
const L = parseLocation(location);
|
||||||
@@ -244,10 +240,4 @@
|
|||||||
.transparent {
|
.transparent {
|
||||||
color: transparent;
|
color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(html.dark .x-location),
|
|
||||||
:global(:root.dark .x-location),
|
|
||||||
:global(:root[data-theme='dark'] .x-location) {
|
|
||||||
color: var(--color-zinc-300);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -134,10 +134,4 @@
|
|||||||
.inline-block {
|
.inline-block {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(html.dark .x-location-world),
|
|
||||||
:global(:root.dark .x-location-world),
|
|
||||||
:global(:root[data-theme='dark'] .x-location-world) {
|
|
||||||
color: var(--color-zinc-100);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+140
-11
@@ -131,7 +131,7 @@
|
|||||||
placement="right"
|
placement="right"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
popper-style="padding:4px;border-radius:8px;"
|
popper-style="padding:4px;border-radius:8px;"
|
||||||
:offset="-10"
|
:offset="6"
|
||||||
:show-arrow="false"
|
:show-arrow="false"
|
||||||
:width="200"
|
:width="200"
|
||||||
:hide-after="0">
|
:hide-after="0">
|
||||||
@@ -158,7 +158,9 @@
|
|||||||
placement="right-start"
|
placement="right-start"
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
popper-style="padding:4px;border-radius:8px;"
|
popper-style="padding:4px;border-radius:8px;"
|
||||||
:width="200">
|
:offset="8"
|
||||||
|
:width="200"
|
||||||
|
:hide-after="0">
|
||||||
<div class="nav-menu-theme">
|
<div class="nav-menu-theme">
|
||||||
<button
|
<button
|
||||||
v-for="theme in themes"
|
v-for="theme in themes"
|
||||||
@@ -170,6 +172,62 @@
|
|||||||
<span class="nav-menu-theme__label">{{ themeDisplayName(theme) }}</span>
|
<span class="nav-menu-theme__label">{{ themeDisplayName(theme) }}</span>
|
||||||
<span v-if="themeMode === theme" class="nav-menu-theme__check">✓</span>
|
<span v-if="themeMode === theme" class="nav-menu-theme__check">✓</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<el-divider></el-divider>
|
||||||
|
|
||||||
|
<el-popover
|
||||||
|
v-model:visible="themeColorMenuVisible"
|
||||||
|
placement="right-start"
|
||||||
|
trigger="hover"
|
||||||
|
popper-style="padding:4px;border-radius:8px;"
|
||||||
|
:offset="8"
|
||||||
|
:width="200"
|
||||||
|
:show-arrow="false"
|
||||||
|
:hide-after="0"
|
||||||
|
:teleported="false">
|
||||||
|
<div class="nav-menu-theme nav-menu-theme--colors">
|
||||||
|
<button
|
||||||
|
v-for="color in colorFamilies"
|
||||||
|
:key="color.name"
|
||||||
|
type="button"
|
||||||
|
class="nav-menu-theme__item"
|
||||||
|
:class="{ 'is-active': currentPrimary === color.base }"
|
||||||
|
:disabled="isApplyingPrimaryColor"
|
||||||
|
@click="handleThemeColorSelect(color)">
|
||||||
|
<span class="nav-menu-theme__label nav-menu-theme__label--swatch">
|
||||||
|
<span
|
||||||
|
class="nav-menu-theme__swatch"
|
||||||
|
:style="{ backgroundColor: color.base }"></span>
|
||||||
|
<span class="nav-menu-theme__label-text">{{ color.name }}</span>
|
||||||
|
</span>
|
||||||
|
<span v-if="currentPrimary === color.base" class="nav-menu-theme__check">
|
||||||
|
✓
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<el-divider></el-divider>
|
||||||
|
|
||||||
|
<div class="nav-menu-theme__custom">
|
||||||
|
<span class="nav-menu-theme__custom-label">{{
|
||||||
|
t('view.settings.appearance.theme_color.header')
|
||||||
|
}}</span>
|
||||||
|
<el-color-picker
|
||||||
|
:model-value="currentPrimary"
|
||||||
|
size="small"
|
||||||
|
:disabled="isApplyingPrimaryColor"
|
||||||
|
:teleported="false"
|
||||||
|
@change="handleCustomThemeColorChange" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<button type="button" class="nav-menu-theme__item" @click.prevent>
|
||||||
|
<span class="nav-menu-theme__label">{{
|
||||||
|
t('view.settings.appearance.theme_color.header')
|
||||||
|
}}</span>
|
||||||
|
<span class="nav-menu-settings__arrow">›</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
</div>
|
</div>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<button type="button" class="nav-menu-settings__item" @click.prevent>
|
<button type="button" class="nav-menu-settings__item" @click.prevent>
|
||||||
@@ -230,12 +288,12 @@
|
|||||||
useAuthStore,
|
useAuthStore,
|
||||||
useSearchStore,
|
useSearchStore,
|
||||||
useUiStore,
|
useUiStore,
|
||||||
useUserStore,
|
|
||||||
useVRCXUpdaterStore
|
useVRCXUpdaterStore
|
||||||
} from '../stores';
|
} from '../stores';
|
||||||
import { THEME_CONFIG, links, navDefinitions } from '../shared/constants';
|
import { THEME_CONFIG, links, navDefinitions } from '../shared/constants';
|
||||||
import { getSentry } from '../plugin';
|
import { getSentry } from '../plugin';
|
||||||
import { openExternalLink } from '../shared/utils';
|
import { openExternalLink } from '../shared/utils';
|
||||||
|
import { useThemePrimaryColor } from '../composables/useElementTheme';
|
||||||
|
|
||||||
import configRepository from '../service/config';
|
import configRepository from '../service/config';
|
||||||
|
|
||||||
@@ -287,12 +345,9 @@
|
|||||||
const { logout } = useAuthStore();
|
const { logout } = useAuthStore();
|
||||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||||
const { themeMode, isNavCollapsed: isCollapsed } = storeToRefs(appearanceSettingsStore);
|
const { themeMode, isNavCollapsed: isCollapsed } = storeToRefs(appearanceSettingsStore);
|
||||||
const userStore = useUserStore();
|
|
||||||
const { currentUser } = storeToRefs(userStore);
|
|
||||||
const { showUserDialog } = userStore;
|
|
||||||
|
|
||||||
const settingsMenuVisible = ref(false);
|
const settingsMenuVisible = ref(false);
|
||||||
const themeMenuVisible = ref(false);
|
const themeMenuVisible = ref(false);
|
||||||
|
const themeColorMenuVisible = ref(false);
|
||||||
const supportMenuVisible = ref(false);
|
const supportMenuVisible = ref(false);
|
||||||
const navMenuRef = ref(null);
|
const navMenuRef = ref(null);
|
||||||
const navLayout = ref([]);
|
const navLayout = ref([]);
|
||||||
@@ -374,6 +429,15 @@
|
|||||||
|
|
||||||
const themes = computed(() => Object.keys(THEME_CONFIG));
|
const themes = computed(() => Object.keys(THEME_CONFIG));
|
||||||
|
|
||||||
|
const {
|
||||||
|
currentPrimary,
|
||||||
|
isApplying: isApplyingPrimaryColor,
|
||||||
|
applyCustomPrimaryColor,
|
||||||
|
initPrimaryColor,
|
||||||
|
colorFamilies,
|
||||||
|
selectPaletteColor
|
||||||
|
} = useThemePrimaryColor();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => activeMenuIndex.value,
|
() => activeMenuIndex.value,
|
||||||
(value) => {
|
(value) => {
|
||||||
@@ -404,10 +468,6 @@
|
|||||||
|
|
||||||
const generateFolderId = () => `nav-folder-${dayjs().toISOString()}-${Math.random().toString().slice(2, 4)}`;
|
const generateFolderId = () => `nav-folder-${dayjs().toISOString()}-${Math.random().toString().slice(2, 4)}`;
|
||||||
|
|
||||||
const showCurrentUserDialog = () => {
|
|
||||||
showUserDialog(currentUser.value?.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const sanitizeLayout = (layout) => {
|
const sanitizeLayout = (layout) => {
|
||||||
const usedKeys = new Set();
|
const usedKeys = new Set();
|
||||||
const normalized = [];
|
const normalized = [];
|
||||||
@@ -485,6 +545,27 @@
|
|||||||
appearanceSettingsStore.saveThemeMode(theme);
|
appearanceSettingsStore.saveThemeMode(theme);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCustomThemeColorChange = async (color) => {
|
||||||
|
if (!color) {
|
||||||
|
await initPrimaryColor();
|
||||||
|
themeColorMenuVisible.value = false;
|
||||||
|
themeMenuVisible.value = false;
|
||||||
|
settingsMenuVisible.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await applyCustomPrimaryColor(color);
|
||||||
|
themeColorMenuVisible.value = false;
|
||||||
|
themeMenuVisible.value = false;
|
||||||
|
settingsMenuVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleThemeColorSelect = async (colorFamily) => {
|
||||||
|
await selectPaletteColor(colorFamily);
|
||||||
|
themeColorMenuVisible.value = false;
|
||||||
|
themeMenuVisible.value = false;
|
||||||
|
settingsMenuVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
const openGithub = () => {
|
const openGithub = () => {
|
||||||
openExternalLink('https://github.com/vrcx-team/VRCX');
|
openExternalLink('https://github.com/vrcx-team/VRCX');
|
||||||
};
|
};
|
||||||
@@ -597,6 +678,7 @@
|
|||||||
settingsMenuVisible.value = false;
|
settingsMenuVisible.value = false;
|
||||||
supportMenuVisible.value = false;
|
supportMenuVisible.value = false;
|
||||||
themeMenuVisible.value = false;
|
themeMenuVisible.value = false;
|
||||||
|
themeColorMenuVisible.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const triggerNavAction = (entry, navIndex = entry?.index) => {
|
const triggerNavAction = (entry, navIndex = entry?.index) => {
|
||||||
@@ -643,6 +725,7 @@
|
|||||||
supportMenuVisible.value = false;
|
supportMenuVisible.value = false;
|
||||||
} else {
|
} else {
|
||||||
themeMenuVisible.value = false;
|
themeMenuVisible.value = false;
|
||||||
|
themeColorMenuVisible.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -699,6 +782,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
await initPrimaryColor();
|
||||||
await loadNavMenuConfig();
|
await loadNavMenuConfig();
|
||||||
|
|
||||||
if (!NIGHTLY || !sentryErrorReporting.value) return;
|
if (!NIGHTLY || !sentryErrorReporting.value) return;
|
||||||
@@ -981,5 +1065,50 @@
|
|||||||
.nav-menu-theme__item.is-active {
|
.nav-menu-theme__item.is-active {
|
||||||
background-color: var(--el-menu-hover-bg-color);
|
background-color: var(--el-menu-hover-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-menu-theme__label--swatch {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu-theme__label-text {
|
||||||
|
min-width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu-theme__swatch {
|
||||||
|
inline-size: 14px;
|
||||||
|
block-size: 14px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu-theme__custom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu-theme__custom-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu-theme--colors {
|
||||||
|
max-height: 360px;
|
||||||
|
overflow: hidden auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
v-model="avatarDialog.visible"
|
v-model="avatarDialog.visible"
|
||||||
:show-close="false"
|
:show-close="false"
|
||||||
top="10vh"
|
top="10vh"
|
||||||
width="930px">
|
width="940px">
|
||||||
<div v-loading="avatarDialog.loading">
|
<div v-loading="avatarDialog.loading">
|
||||||
<div style="display: flex">
|
<div style="display: flex">
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
v-model="groupDialog.visible"
|
v-model="groupDialog.visible"
|
||||||
:show-close="false"
|
:show-close="false"
|
||||||
top="10vh"
|
top="10vh"
|
||||||
width="930px"
|
width="940px"
|
||||||
class="x-dialog x-group-dialog">
|
class="x-dialog x-group-dialog">
|
||||||
<div v-loading="groupDialog.loading" class="group-body">
|
<div v-loading="groupDialog.loading" class="group-body">
|
||||||
<div style="display: flex">
|
<div style="display: flex">
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
clearable></el-input>
|
clearable></el-input>
|
||||||
</div>
|
</div>
|
||||||
<DataTable :loading="loading" v-bind="dataTable" style="margin-top: 10px">
|
<DataTable :loading="loading" v-bind="dataTable" style="margin-top: 10px">
|
||||||
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="110">
|
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="130">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip placement="left">
|
<el-tooltip placement="left">
|
||||||
<template #content>
|
<template #content>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
v-model="userDialog.visible"
|
v-model="userDialog.visible"
|
||||||
:show-close="false"
|
:show-close="false"
|
||||||
top="10vh"
|
top="10vh"
|
||||||
width="930px">
|
width="940px">
|
||||||
<div v-loading="userDialog.loading">
|
<div v-loading="userDialog.loading">
|
||||||
<UserSummaryHeader
|
<UserSummaryHeader
|
||||||
:get-user-state-text="getUserStateText"
|
:get-user-state-text="getUserStateText"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
v-model="isDialogVisible"
|
v-model="isDialogVisible"
|
||||||
top="10vh"
|
top="10vh"
|
||||||
:show-close="false"
|
:show-close="false"
|
||||||
width="930px">
|
width="940px">
|
||||||
<div v-loading="worldDialog.loading">
|
<div v-loading="worldDialog.loading">
|
||||||
<div style="display: flex">
|
<div style="display: flex">
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -5,27 +5,46 @@ import colors from 'tailwindcss/colors';
|
|||||||
import configRepository from '../service/config';
|
import configRepository from '../service/config';
|
||||||
|
|
||||||
// Tailwind indigo-500 in OKLCH
|
// Tailwind indigo-500 in OKLCH
|
||||||
const DEFAULT_PRIMARY = 'oklch(58.5% 0.233 277.117)';
|
export const DEFAULT_PRIMARY_COLOR = 'oklch(58.5% 0.233 277.117)';
|
||||||
const DARK_WEIGHT = 0.2;
|
const DARK_WEIGHT = 0.2;
|
||||||
const CONFIG_KEY = 'VRCX_elPrimaryColor';
|
const CONFIG_KEY = 'VRCX_elPrimaryColor';
|
||||||
const STYLE_ID = 'el-dynamic-theme';
|
const STYLE_ID = 'el-dynamic-theme';
|
||||||
|
|
||||||
let elementThemeInstance = null;
|
let elementThemeInstance = null;
|
||||||
|
|
||||||
|
const INVALID_TAILWIND_COLOR_KEYS = new Set([
|
||||||
|
'inherit',
|
||||||
|
'current',
|
||||||
|
'transparent',
|
||||||
|
'black',
|
||||||
|
'white',
|
||||||
|
'lightBlue',
|
||||||
|
'warmGray',
|
||||||
|
'trueGray',
|
||||||
|
'coolGray',
|
||||||
|
'blueGray'
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep okLCH as-is; otherwise normalize hex; fallback to default.
|
* Normalize a theme color and prevent CSS injection.
|
||||||
* @param {string} color
|
|
||||||
* @param {string} fallback
|
|
||||||
*/
|
*/
|
||||||
function toPrimaryColor(color, fallback = DEFAULT_PRIMARY) {
|
function toPrimaryColor(color, fallback = DEFAULT_PRIMARY_COLOR) {
|
||||||
if (typeof color === 'string' && color.trim()) {
|
if (typeof color !== 'string') {
|
||||||
if (color.trim().startsWith('oklch(')) {
|
|
||||||
return color.trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalized = color.trim();
|
||||||
|
if (!normalized) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CSS?.supports?.('color', normalized)) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update Element Plus CSS variables based on a primary color.
|
* Update Element Plus CSS variables based on a primary color.
|
||||||
* Light colors use Tailwind palette directly; only dark-2 is calculated.
|
* Light colors use Tailwind palette directly; only dark-2 is calculated.
|
||||||
@@ -42,10 +61,9 @@ function setElementPlusColors(primary, palette = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Derive Element Plus light steps either from a palette or by mixing with white.
|
// Derive Element Plus light steps either from a palette or by mixing with white.
|
||||||
const safePalette = palette || null;
|
const lightValues = palette
|
||||||
const lightValues = safePalette
|
|
||||||
? ['400', '300', '200', '100', '50', '50', '50', '50', '50'].map(
|
? ['400', '300', '200', '100', '50', '50', '50', '50', '50'].map(
|
||||||
(key) => safePalette[key] || primary
|
(key) => palette[key] || primary
|
||||||
)
|
)
|
||||||
: Array.from({ length: 9 }, (_, idx) => {
|
: Array.from({ length: 9 }, (_, idx) => {
|
||||||
const whitePercent = (idx + 1) * 10;
|
const whitePercent = (idx + 1) * 10;
|
||||||
@@ -73,25 +91,27 @@ function setElementPlusColors(primary, palette = null) {
|
|||||||
`${darkSelector} {\n --el-color-primary-light-9: ${darkLight9};\n}`;
|
`${darkSelector} {\n --el-color-primary-light-9: ${darkLight9};\n}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findTailwindPalette(primary) {
|
const TAILWIND_COLOR_FAMILIES = Object.entries(colors)
|
||||||
const entries = Object.values(colors);
|
.filter(([name, palette]) => {
|
||||||
for (const palette of entries) {
|
return (
|
||||||
if (
|
!INVALID_TAILWIND_COLOR_KEYS.has(name) &&
|
||||||
palette &&
|
palette &&
|
||||||
typeof palette === 'object' &&
|
typeof palette === 'object' &&
|
||||||
palette['500'] === primary
|
palette['500']
|
||||||
) {
|
);
|
||||||
return palette;
|
})
|
||||||
}
|
.map(([name, palette]) => ({
|
||||||
}
|
name,
|
||||||
return null;
|
base: palette['500'],
|
||||||
}
|
palette
|
||||||
|
}))
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared Element Plus theme controller.
|
* Shared Element Plus theme controller.
|
||||||
* @param {string} defaultColor
|
* @param {string} defaultColor
|
||||||
*/
|
*/
|
||||||
export function useElementTheme(defaultColor = DEFAULT_PRIMARY) {
|
export function useElementTheme(defaultColor = DEFAULT_PRIMARY_COLOR) {
|
||||||
if (elementThemeInstance) {
|
if (elementThemeInstance) {
|
||||||
return elementThemeInstance;
|
return elementThemeInstance;
|
||||||
}
|
}
|
||||||
@@ -102,7 +122,7 @@ export function useElementTheme(defaultColor = DEFAULT_PRIMARY) {
|
|||||||
|
|
||||||
const applyPrimaryColor = async (color, palette = null) => {
|
const applyPrimaryColor = async (color, palette = null) => {
|
||||||
const nextColor = toPrimaryColor(color, currentPrimary.value);
|
const nextColor = toPrimaryColor(color, currentPrimary.value);
|
||||||
const effectivePalette = palette || findTailwindPalette(nextColor);
|
const effectivePalette = palette || null;
|
||||||
isApplying.value = true;
|
isApplying.value = true;
|
||||||
setElementPlusColors(nextColor, effectivePalette);
|
setElementPlusColors(nextColor, effectivePalette);
|
||||||
currentPrimary.value = nextColor;
|
currentPrimary.value = nextColor;
|
||||||
@@ -124,7 +144,7 @@ export function useElementTheme(defaultColor = DEFAULT_PRIMARY) {
|
|||||||
const storedColor =
|
const storedColor =
|
||||||
(await configRepository.getString(CONFIG_KEY)) ||
|
(await configRepository.getString(CONFIG_KEY)) ||
|
||||||
fallbackColor ||
|
fallbackColor ||
|
||||||
DEFAULT_PRIMARY;
|
DEFAULT_PRIMARY_COLOR;
|
||||||
await applyPrimaryColor(storedColor);
|
await applyPrimaryColor(storedColor);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -138,4 +158,31 @@ export function useElementTheme(defaultColor = DEFAULT_PRIMARY) {
|
|||||||
return elementThemeInstance;
|
return elementThemeInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { toPrimaryColor };
|
export function useThemePrimaryColor() {
|
||||||
|
const { currentPrimary, isApplying, applyPrimaryColor, initPrimaryColor } =
|
||||||
|
useElementTheme(DEFAULT_PRIMARY_COLOR);
|
||||||
|
const colorFamilies = TAILWIND_COLOR_FAMILIES;
|
||||||
|
|
||||||
|
const selectPaletteColor = async (colorFamily) => {
|
||||||
|
if (!colorFamily) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await applyPrimaryColor(colorFamily.base, colorFamily.palette);
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyCustomPrimaryColor = async (color) => {
|
||||||
|
if (!color) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await applyPrimaryColor(color);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentPrimary,
|
||||||
|
isApplying,
|
||||||
|
initPrimaryColor,
|
||||||
|
applyCustomPrimaryColor,
|
||||||
|
colorFamilies,
|
||||||
|
selectPaletteColor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
<el-table-column width="20"></el-table-column>
|
<el-table-column width="20"></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="t('table.friendList.no')"
|
:label="t('table.friendList.no')"
|
||||||
width="70"
|
width="100"
|
||||||
prop="$friendNumber"
|
prop="$friendNumber"
|
||||||
:sortable="true"
|
:sortable="true"
|
||||||
fixed="left">
|
fixed="left">
|
||||||
@@ -141,10 +141,12 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="t('table.friendList.bioLink')" width="130" prop="bioLinks">
|
<el-table-column :label="t('table.friendList.bioLink')" width="130" prop="bioLinks">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
<div class="flex items-center">
|
||||||
<el-tooltip v-for="(link, index) in row.bioLinks.filter(Boolean)" :key="index">
|
<el-tooltip v-for="(link, index) in row.bioLinks.filter(Boolean)" :key="index">
|
||||||
<template #content>
|
<template #content>
|
||||||
<span v-text="link"></span>
|
<span v-text="link"></span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
:src="getFaviconUrl(link)"
|
:src="getFaviconUrl(link)"
|
||||||
style="
|
style="
|
||||||
@@ -157,6 +159,7 @@
|
|||||||
@click.stop="openExternalLink(link)"
|
@click.stop="openExternalLink(link)"
|
||||||
loading="lazy" />
|
loading="lazy" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DataTable v-bind="friendLogTable" :data="friendLogDisplayData">
|
<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">
|
<el-table-column :label="t('table.friendLog.date')" prop="created_at" width="200">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip placement="right">
|
<el-tooltip placement="right">
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DataTable v-bind="gameLogTable" :data="gameLogDisplayData">
|
<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">
|
<el-table-column :label="t('table.gameLog.date')" prop="created_at" width="140">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip placement="right">
|
<el-tooltip placement="right">
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="x-container" ref="moderationRef">
|
<div class="x-container" ref="moderationRef">
|
||||||
<!-- 工具栏 -->
|
|
||||||
<div class="tool-slot">
|
<div class="tool-slot">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="playerModerationTable.filters[0].value"
|
v-model="playerModerationTable.filters[0].value"
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
:data="notificationDisplayData"
|
:data="notificationDisplayData"
|
||||||
ref="notificationTableRef"
|
ref="notificationTableRef"
|
||||||
class="notification-table">
|
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">
|
<template #default="scope">
|
||||||
<el-tooltip placement="right">
|
<el-tooltip placement="right">
|
||||||
<template #content>
|
<template #content>
|
||||||
|
|||||||
@@ -125,9 +125,6 @@
|
|||||||
}}</el-button>
|
}}</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="options-container">
|
|
||||||
<ThemePicker />
|
|
||||||
</div>
|
|
||||||
<div class="options-container">
|
<div class="options-container">
|
||||||
<span class="header">{{ t('view.settings.appearance.timedate.header') }}</span>
|
<span class="header">{{ t('view.settings.appearance.timedate.header') }}</span>
|
||||||
<div class="options-container-item">
|
<div class="options-container-item">
|
||||||
@@ -394,7 +391,6 @@
|
|||||||
import { getLanguageName, languageCodes } from '../../../../localization';
|
import { getLanguageName, languageCodes } from '../../../../localization';
|
||||||
|
|
||||||
import SimpleSwitch from '../SimpleSwitch.vue';
|
import SimpleSwitch from '../SimpleSwitch.vue';
|
||||||
import ThemePicker from '../ThemePicker.vue';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
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>
|
<template>
|
||||||
<div class="x-friend-item" @click="$emit('click')">
|
<div class="x-friend-item" @click="showUserDialog(friend.id)">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div
|
<div
|
||||||
class="avatar"
|
class="avatar"
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import { useAppearanceSettingsStore, useFriendStore } from '../../../stores';
|
import { useAppearanceSettingsStore, useFriendStore, useUserStore } from '../../../stores';
|
||||||
import { userImage, userStatusClass } from '../../../shared/utils';
|
import { userImage, userStatusClass } from '../../../shared/utils';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -71,10 +71,11 @@
|
|||||||
isGroupByInstance: Boolean
|
isGroupByInstance: Boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(['click', 'confirm-delete-friend']);
|
defineEmits(['confirm-delete-friend']);
|
||||||
|
|
||||||
const { hideNicknames } = storeToRefs(useAppearanceSettingsStore());
|
const { hideNicknames } = storeToRefs(useAppearanceSettingsStore());
|
||||||
const { isRefreshFriendsLoading } = storeToRefs(useFriendStore());
|
const { isRefreshFriendsLoading } = storeToRefs(useFriendStore());
|
||||||
|
const { showUserDialog } = useUserStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const isFriendTraveling = computed(() => props.friend.ref?.location === 'traveling');
|
const isFriendTraveling = computed(() => props.friend.ref?.location === 'traveling');
|
||||||
|
|||||||
@@ -63,7 +63,6 @@
|
|||||||
v-for="friend in group"
|
v-for="friend in group"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
:friend="friend"
|
:friend="friend"
|
||||||
@click="showUserDialog(friend.id)"
|
|
||||||
@confirm-delete-friend="confirmDeleteFriend"></friend-item>
|
@confirm-delete-friend="confirmDeleteFriend"></friend-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,7 +72,6 @@
|
|||||||
v-for="friend in vipFriendsByGroupStatus"
|
v-for="friend in vipFriendsByGroupStatus"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
:friend="friend"
|
:friend="friend"
|
||||||
@click="showUserDialog(friend.id)"
|
|
||||||
@confirm-delete-friend="confirmDeleteFriend">
|
@confirm-delete-friend="confirmDeleteFriend">
|
||||||
</friend-item>
|
</friend-item>
|
||||||
</template>
|
</template>
|
||||||
@@ -91,7 +89,7 @@
|
|||||||
|
|
||||||
<div v-show="!isSidebarGroupByInstanceCollapsed">
|
<div v-show="!isSidebarGroupByInstanceCollapsed">
|
||||||
<div v-for="friendArr in friendsInSameInstance" :key="friendArr[0].ref.$location.tag">
|
<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" />
|
<Location class="extra" :location="getFriendsLocations(friendArr)" style="display: inline" />
|
||||||
<span class="extra" style="margin-left: 5px">{{ `(${friendArr.length})` }}</span>
|
<span class="extra" style="margin-left: 5px">{{ `(${friendArr.length})` }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,7 +100,6 @@
|
|||||||
:friend="friend"
|
:friend="friend"
|
||||||
is-group-by-instance
|
is-group-by-instance
|
||||||
:style="{ 'margin-bottom': idx === friendArr.length - 1 ? '5px' : undefined }"
|
:style="{ 'margin-bottom': idx === friendArr.length - 1 ? '5px' : undefined }"
|
||||||
@click="showUserDialog(friend.id)"
|
|
||||||
@confirm-delete-friend="confirmDeleteFriend">
|
@confirm-delete-friend="confirmDeleteFriend">
|
||||||
</friend-item>
|
</friend-item>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,7 +123,6 @@
|
|||||||
v-for="friend in onlineFriendsByGroupStatus"
|
v-for="friend in onlineFriendsByGroupStatus"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
:friend="friend"
|
:friend="friend"
|
||||||
@click="showUserDialog(friend.id)"
|
|
||||||
@confirm-delete-friend="confirmDeleteFriend" />
|
@confirm-delete-friend="confirmDeleteFriend" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -144,7 +140,6 @@
|
|||||||
v-for="friend in activeFriends"
|
v-for="friend in activeFriends"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
:friend="friend"
|
:friend="friend"
|
||||||
@click="showUserDialog(friend.id)"
|
|
||||||
@confirm-delete-friend="confirmDeleteFriend"></friend-item>
|
@confirm-delete-friend="confirmDeleteFriend"></friend-item>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -162,7 +157,6 @@
|
|||||||
v-for="friend in offlineFriends"
|
v-for="friend in offlineFriends"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
:friend="friend"
|
:friend="friend"
|
||||||
@click="showUserDialog(friend.id)"
|
|
||||||
@confirm-delete-friend="confirmDeleteFriend"></friend-item>
|
@confirm-delete-friend="confirmDeleteFriend"></friend-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="gallery-page x-container">
|
<div class="gallery-page x-container">
|
||||||
<div class="gallery-page__header">
|
<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') }}
|
{{ t('nav_tooltip.tools') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<span class="header">{{ t('dialog.gallery_icons.header') }}</span>
|
<span class="header">{{ t('dialog.gallery_icons.header') }}</span>
|
||||||
|
|||||||
@@ -330,7 +330,7 @@
|
|||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--el-color-primary-light-8);
|
background-color: var(--el-color-primary-light-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-icon-arrow-right {
|
.el-icon-arrow-right {
|
||||||
|
|||||||
@@ -194,37 +194,47 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
&:hover {
|
}
|
||||||
|
|
||||||
|
.event-card:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: var(--el-box-shadow-light);
|
box-shadow: var(--el-box-shadow-light);
|
||||||
}
|
}
|
||||||
&.grouped-card {
|
|
||||||
|
.event-card.grouped-card {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
&.grid-card {
|
|
||||||
|
.event-card.grid-card {
|
||||||
flex: 0 0 280px;
|
flex: 0 0 280px;
|
||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
}
|
}
|
||||||
&.group-dialog-grid-card {
|
|
||||||
|
.event-card.group-dialog-grid-card {
|
||||||
flex: 0 0 320px;
|
flex: 0 0 320px;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
}
|
}
|
||||||
:deep(.el-card__body) {
|
|
||||||
|
.event-card :deep(.el-card__body) {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
.banner {
|
|
||||||
|
.event-card .banner {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
.timeline-view & {
|
}
|
||||||
|
|
||||||
|
.timeline-view .event-card .banner {
|
||||||
height: 125px;
|
height: 125px;
|
||||||
}
|
}
|
||||||
.grid-view & {
|
|
||||||
|
.grid-view .event-card .banner {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.following-badge {
|
.event-card .following-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -8px;
|
top: -8px;
|
||||||
right: -9px;
|
right: -9px;
|
||||||
@@ -241,61 +251,75 @@
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.is-following {
|
|
||||||
|
.event-card .following-badge.is-following {
|
||||||
background-color: var(--group-calendar-badge-following, var(--el-color-success));
|
background-color: var(--group-calendar-badge-following, var(--el-color-success));
|
||||||
}
|
}
|
||||||
.event-content {
|
|
||||||
|
.event-card .event-content {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
.timeline-view & {
|
}
|
||||||
|
|
||||||
|
.timeline-view .event-card .event-content {
|
||||||
padding: 4px 12px 12px 12px;
|
padding: 4px 12px 12px 12px;
|
||||||
}
|
}
|
||||||
.grid-view & {
|
|
||||||
|
.grid-view .event-card .event-content {
|
||||||
padding: 8px 12px 12px 12px;
|
padding: 8px 12px 12px 12px;
|
||||||
}
|
}
|
||||||
.event-title {
|
|
||||||
|
.event-card .event-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
.grid-view & {
|
}
|
||||||
|
|
||||||
|
.grid-view .event-card .event-title {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.event-group-name {
|
.event-card .event-group-name {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.grid-view & {
|
}
|
||||||
|
|
||||||
|
.grid-view .event-card .event-group-name {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.event-title-content {
|
.event-card .event-title-content {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.timeline-view & {
|
}
|
||||||
|
|
||||||
|
.timeline-view .event-card .event-title-content {
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
&:hover {
|
|
||||||
|
.event-card .event-title-content:hover {
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
.event-card .event-info {
|
||||||
.event-info {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
.timeline-view & > :first-child {
|
}
|
||||||
|
|
||||||
|
.timeline-view .event-card .event-info > :first-child {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
.grid-view & {
|
|
||||||
|
.grid-view .event-card .event-info {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--el-text-color-regular);
|
color: var(--el-text-color-regular);
|
||||||
}
|
}
|
||||||
.event-time {
|
|
||||||
|
.event-card .event-time {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
:deep(.el-card) {
|
:deep(.el-card) {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
Reference in New Issue
Block a user