mirror of
https://github.com/hi2shark/nazhua.git
synced 2026-01-11 14:40:43 +08:00
✨ 新增列表的排序功能
This commit is contained in:
parent
93f66cb42c
commit
586f1dd063
5187
package-lock.json
generated
Normal file
5187
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -11,12 +11,12 @@
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.7",
|
||||
"axios": "^1.13.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"echarts": "^5.5.1",
|
||||
"flag-icons": "^7.2.3",
|
||||
"font-logos": "^1.3.0",
|
||||
"remixicon": "^4.6.0",
|
||||
"remixicon": "^4.7.0",
|
||||
"uniqolor": "^1.1.1",
|
||||
"vue": "^3.5.12",
|
||||
"vue-echarts": "^7.0.3",
|
||||
@ -24,19 +24,19 @@
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.9",
|
||||
"@babel/core": "^7.28.5",
|
||||
"@babel/eslint-parser": "^7.24.8",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"@vitejs/plugin-vue": "^5.2.4",
|
||||
"@vue/eslint-config-airbnb": "^7.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-plugin-vue": "^9.9.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-plugin-vue": "^9.33.0",
|
||||
"sass": "^1.81.0",
|
||||
"vite": "^5.4.10",
|
||||
"vite-plugin-babel": "^1.2.0",
|
||||
"vite": "^6.4.1",
|
||||
"vite-plugin-babel": "^1.3.2",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-svg-loader": "^5.1.0"
|
||||
},
|
||||
|
||||
@ -11,7 +11,7 @@ window.$$nazhuaConfig = {
|
||||
// showLantern: true, // 是否显示灯笼
|
||||
enableInnerSearch: true, // 启用内部搜索
|
||||
// listServerItemTypeToggle: true, // 服务器列表项类型切换
|
||||
listServerItemType: 'row', // 服务器列表项类型 card/row/status row列表模式移动端自动切换至card
|
||||
// listServerItemType: 'server-status', // 服务器列表项类型 card/row/server-status row列表模式移动端自动切换至card
|
||||
// serverStatusColumnsTpl: null, // 服务器状态列配置模板
|
||||
// listServerStatusType: 'progress', // 服务器状态类型--列表
|
||||
// listServerRealTimeShowLoad: true, // 列表显示服务器实时负载
|
||||
@ -31,6 +31,7 @@ window.$$nazhuaConfig = {
|
||||
// hideListItemBill: false, // 隐藏列表项的账单信息
|
||||
hideListItemLink: true, // 隐藏列表项的购买链接
|
||||
// hideFilter: false, // 隐藏筛选
|
||||
// hideSort: false, // 隐藏排序
|
||||
// hideTag: false, // 隐藏标签
|
||||
// hideDotBG: true, // 隐藏框框里面的点点背景
|
||||
// monitorRefreshTime: 10, // 监控刷新时间间隔,单位s(秒), 0为不刷新,为保证不频繁请求源站,最低生效值为10s
|
||||
|
||||
@ -30,7 +30,7 @@ function useCdnCss(item) {
|
||||
if (import.meta.env.VITE_USE_CDN) {
|
||||
Object.entries({
|
||||
remixicon: {
|
||||
jsdelivr: 'https://cdn.jsdelivr.net/npm/remixicon@4.6.0/fonts/remixicon.css',
|
||||
jsdelivr: 'https://cdn.jsdelivr.net/npm/remixicon@4.7.0/fonts/remixicon.css',
|
||||
cdnjs: 'https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.2.0/remixicon.css',
|
||||
},
|
||||
flagIcons: {
|
||||
|
||||
341
src/views/components/server-list/server-sort-box.vue
Normal file
341
src/views/components/server-list/server-sort-box.vue
Normal file
@ -0,0 +1,341 @@
|
||||
<template>
|
||||
<div
|
||||
class="server-sort-box"
|
||||
:class="{
|
||||
'server-sort-box--light-background': lightBackground,
|
||||
'server-sort-box--mobile-hide': !mobileShow,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
ref="triggerRef"
|
||||
class="sort-select-wrapper"
|
||||
@click="toggleDropdown"
|
||||
>
|
||||
<div class="sort-select-selected">
|
||||
<span class="sort-select-selected-value">{{ selectedLabel }}</span>
|
||||
<span
|
||||
class="sort-select-selected-icon"
|
||||
@click.stop="toggleOrder"
|
||||
>
|
||||
<span
|
||||
v-if="activeValue.order === 'desc'"
|
||||
class="ri-arrow-down-line"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
class="ri-arrow-up-line"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下拉菜单 -->
|
||||
<Teleport to="body">
|
||||
<server-sort-dropdown-menu
|
||||
ref="dropdownMenuRef"
|
||||
:visible="isDropdownOpen"
|
||||
:options="options"
|
||||
:active-value="activeValue.prop"
|
||||
:dropdown-style="dropdownStyle"
|
||||
:light-background="lightBackground"
|
||||
:is-mobile="isMobile"
|
||||
@select="handleSelectItem"
|
||||
/>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 过滤栏
|
||||
*/
|
||||
import {
|
||||
computed,
|
||||
ref,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
nextTick,
|
||||
} from 'vue';
|
||||
import config from '@/config';
|
||||
import ServerSortDropdownMenu from './server-sort-dropdown-menu.vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
prop: 'DisplayIndex',
|
||||
order: 'desc',
|
||||
}),
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
acceptEmpty: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
mobileShow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits([
|
||||
'update:modelValue',
|
||||
'change',
|
||||
]);
|
||||
|
||||
const lightBackground = computed(() => config.nazhua.lightBackground);
|
||||
|
||||
// 设备检测(用于判断是否小屏,小屏时居中显示)
|
||||
const isMobile = ref(window.innerWidth < 768);
|
||||
|
||||
// PC端下拉菜单相关
|
||||
const isDropdownOpen = ref(false);
|
||||
const triggerRef = ref(null);
|
||||
const dropdownMenuRef = ref(null);
|
||||
const dropdownStyle = ref({});
|
||||
|
||||
const activeValue = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => {
|
||||
emits('update:modelValue', val);
|
||||
emits('change', val);
|
||||
},
|
||||
});
|
||||
|
||||
// 获取当前选中项的label
|
||||
const selectedLabel = computed(() => {
|
||||
const selectedOption = props.options.find((opt) => opt.value === activeValue.value.prop);
|
||||
return selectedOption ? selectedOption.label : '排序';
|
||||
});
|
||||
|
||||
// 更新下拉菜单位置
|
||||
function updateDropdownPosition() {
|
||||
if (!triggerRef.value || !dropdownMenuRef.value) return;
|
||||
|
||||
// 使用 nextTick 确保 DOM 已更新
|
||||
nextTick(() => {
|
||||
const dropdownRef = dropdownMenuRef.value?.dropdownRef;
|
||||
|
||||
if (!dropdownRef) return;
|
||||
|
||||
// 小屏设备:居中显示
|
||||
if (isMobile.value) {
|
||||
dropdownStyle.value = {
|
||||
position: 'fixed',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
visibility: 'visible',
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// 大屏设备:相对定位
|
||||
const triggerRect = triggerRef.value.getBoundingClientRect();
|
||||
|
||||
// 先设置一个初始位置,确保元素在视口中可见
|
||||
let top = triggerRect.bottom + 8;
|
||||
let { left } = triggerRect;
|
||||
|
||||
// 设置初始位置
|
||||
dropdownStyle.value = {
|
||||
position: 'fixed',
|
||||
top: `${top}px`,
|
||||
left: `${left}px`,
|
||||
visibility: 'hidden', // 先隐藏,避免闪烁
|
||||
};
|
||||
|
||||
// 再次使用 nextTick 确保样式已应用
|
||||
nextTick(() => {
|
||||
const dropdownRect = dropdownRef.getBoundingClientRect();
|
||||
|
||||
// 防止超出右边界
|
||||
if (left + dropdownRect.width > window.innerWidth) {
|
||||
left = window.innerWidth - dropdownRect.width - 10;
|
||||
}
|
||||
|
||||
// 防止超出下边界,如果超出则向上展开
|
||||
if (top + dropdownRect.height > window.innerHeight) {
|
||||
top = triggerRect.top - dropdownRect.height - 8;
|
||||
}
|
||||
|
||||
// 防止超出左边界
|
||||
if (left < 10) {
|
||||
left = 10;
|
||||
}
|
||||
|
||||
// 更新最终位置并显示
|
||||
dropdownStyle.value = {
|
||||
position: 'fixed',
|
||||
top: `${top}px`,
|
||||
left: `${left}px`,
|
||||
visibility: 'visible',
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 切换下拉菜单显示状态
|
||||
function toggleDropdown(event) {
|
||||
event.stopPropagation(); // 阻止事件冒泡,防止立即被 handleDocumentClick 关闭
|
||||
isDropdownOpen.value = !isDropdownOpen.value;
|
||||
if (isDropdownOpen.value) {
|
||||
nextTick(() => {
|
||||
updateDropdownPosition();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 切换升序/降序
|
||||
function toggleOrder(event) {
|
||||
event.stopPropagation(); // 阻止事件冒泡,避免触发下拉菜单
|
||||
if (!activeValue.value.prop) return; // 如果没有选中排序字段,则不切换
|
||||
|
||||
activeValue.value = {
|
||||
prop: activeValue.value.prop,
|
||||
order: activeValue.value.order === 'desc' ? 'asc' : 'desc',
|
||||
};
|
||||
emits('change', activeValue.value);
|
||||
}
|
||||
|
||||
// PC端选择项
|
||||
function handleSelectItem(item) {
|
||||
if (activeValue.value.prop === item.value) {
|
||||
if (props.acceptEmpty) {
|
||||
activeValue.value = {
|
||||
prop: '',
|
||||
order: 'desc',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
activeValue.value = {
|
||||
prop: item.value,
|
||||
order: activeValue.value.order || 'desc',
|
||||
};
|
||||
}
|
||||
isDropdownOpen.value = false;
|
||||
emits('change', activeValue.value);
|
||||
}
|
||||
|
||||
// 点击外部关闭下拉菜单
|
||||
function handleDocumentClick(event) {
|
||||
if (!isDropdownOpen.value) return;
|
||||
|
||||
const dropdownRef = dropdownMenuRef.value?.dropdownRef;
|
||||
|
||||
if (
|
||||
triggerRef.value
|
||||
&& !triggerRef.value.contains(event.target)
|
||||
&& dropdownRef
|
||||
&& !dropdownRef.contains(event.target)
|
||||
) {
|
||||
isDropdownOpen.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 窗口resize处理
|
||||
function handleResize() {
|
||||
isMobile.value = window.innerWidth < 768;
|
||||
|
||||
// 如果下拉菜单打开,更新位置
|
||||
if (isDropdownOpen.value) {
|
||||
nextTick(() => {
|
||||
updateDropdownPosition();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
document.addEventListener('click', handleDocumentClick);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
document.removeEventListener('click', handleDocumentClick);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.server-sort-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 var(--list-padding);
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
&--mobile-hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// PC端触发元素
|
||||
.sort-select-wrapper {
|
||||
position: relative;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-select-selected {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 36px;
|
||||
padding: 0 15px;
|
||||
line-height: 1.2;
|
||||
border-radius: 6px;
|
||||
background: rgba(#000, 0.3);
|
||||
transition: all 0.3s linear;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
height: 30px;
|
||||
padding: 0 10px;
|
||||
border-radius: 3px;
|
||||
background-color: rgba(#000, 0.8);
|
||||
}
|
||||
|
||||
.sort-select-selected-value {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sort-select-selected-icon {
|
||||
margin-left: 8px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
transition: all 0.2s linear;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: rgba(#fff, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba(#fff, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PC端浅色背景样式
|
||||
&--light-background {
|
||||
.sort-select-selected {
|
||||
background: rgba(#000, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
145
src/views/components/server-list/server-sort-dropdown-menu.vue
Normal file
145
src/views/components/server-list/server-sort-dropdown-menu.vue
Normal file
@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div
|
||||
v-show="visible"
|
||||
ref="dropdownRef"
|
||||
class="server-sort-select-dropdown"
|
||||
:class="{
|
||||
'server-sort-select-dropdown--light-background': lightBackground,
|
||||
'server-sort-select-dropdown--mobile': isMobile,
|
||||
}"
|
||||
:style="dropdownStyle"
|
||||
>
|
||||
<div class="sort-select-options">
|
||||
<div
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
class="server-sort-item"
|
||||
:class="{
|
||||
active: activeValue === item.value,
|
||||
}"
|
||||
:title="item?.title || false"
|
||||
@click.stop="handleSelect(item, $event)"
|
||||
>
|
||||
<span class="option-label">{{ item.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
activeValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
dropdownStyle: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
lightBackground: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isMobile: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['select']);
|
||||
|
||||
const dropdownRef = ref(null);
|
||||
|
||||
function handleSelect(item, event) {
|
||||
event.stopPropagation();
|
||||
emits('select', item);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
dropdownRef,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.server-sort-select-dropdown {
|
||||
z-index: 500;
|
||||
background: rgba(#000, 0.8);
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
min-width: 150px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
|
||||
// 小屏居中显示样式
|
||||
&--mobile {
|
||||
min-width: 280px;
|
||||
max-width: 90vw;
|
||||
max-height: 70vh;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.sort-select-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.server-sort-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 36px;
|
||||
padding: 0 15px;
|
||||
line-height: 1.2;
|
||||
border-radius: 6px;
|
||||
background: rgba(#000, 0.3);
|
||||
transition: all 0.3s linear;
|
||||
cursor: pointer;
|
||||
|
||||
.option-label {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.option-label {
|
||||
color: var(--option-high-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--option-high-color-active);
|
||||
|
||||
.option-label {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 浅色背景样式
|
||||
.server-sort-select-dropdown--light-background {
|
||||
.server-sort-item {
|
||||
background: rgba(#000, 0.5);
|
||||
|
||||
&:hover {
|
||||
background: rgba(#000, 0.8);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--option-high-color-active);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
128
src/views/composable/server-sort.js
Normal file
128
src/views/composable/server-sort.js
Normal file
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 服务器排序选项
|
||||
*/
|
||||
export const serverSortOptions = () => [{
|
||||
label: '排序值',
|
||||
value: 'DisplayIndex',
|
||||
}, {
|
||||
label: '主机名称',
|
||||
value: 'Name',
|
||||
}, {
|
||||
label: '国家地区',
|
||||
value: 'Host.CountryCode',
|
||||
}, {
|
||||
label: '系统平台',
|
||||
value: 'Host.Platform',
|
||||
}, {
|
||||
label: '在线时长',
|
||||
value: 'Host.BootTime',
|
||||
}, {
|
||||
label: '入网速度',
|
||||
value: 'State.NetInSpeed',
|
||||
}, {
|
||||
label: '出网速度',
|
||||
value: 'State.NetOutSpeed',
|
||||
}, {
|
||||
label: '入网流量',
|
||||
value: 'State.NetInTransfer',
|
||||
}, {
|
||||
label: '出网流量',
|
||||
value: 'State.NetOutTransfer',
|
||||
}, {
|
||||
label: '合计流量',
|
||||
value: '$.TotalTransfer',
|
||||
}, {
|
||||
label: 'TCP连接',
|
||||
value: 'State.TcpConnCount',
|
||||
}, {
|
||||
label: 'UDP连接',
|
||||
value: 'State.UdpConnCount',
|
||||
}, {
|
||||
label: '总连接数',
|
||||
value: '$.TotalConnCount',
|
||||
}, {
|
||||
label: '1分钟负载',
|
||||
value: 'State.Load1',
|
||||
}, {
|
||||
label: 'CPU占用',
|
||||
value: 'State.CPU',
|
||||
}, {
|
||||
label: '核心数量',
|
||||
value: '$.CPU',
|
||||
}, {
|
||||
label: '内存占用',
|
||||
value: 'State.MemUsed',
|
||||
}, {
|
||||
label: '内存大小',
|
||||
value: 'Host.MemTotal',
|
||||
}, {
|
||||
label: '交换占用',
|
||||
value: 'State.SwapUsed',
|
||||
}, {
|
||||
label: '交换大小',
|
||||
value: 'Host.SwapTotal',
|
||||
}, {
|
||||
label: '硬盘占用',
|
||||
value: 'State.DiskUsed',
|
||||
}, {
|
||||
label: '硬盘大小',
|
||||
value: 'Host.DiskTotal',
|
||||
}];
|
||||
|
||||
/**
|
||||
* 服务器排序处理
|
||||
*/
|
||||
export function serverSortHandler(a, b, sortby, order) {
|
||||
let aValue;
|
||||
let bValue;
|
||||
const hasDot = sortby.includes('.');
|
||||
if (!hasDot) {
|
||||
aValue = a[sortby];
|
||||
bValue = b[sortby];
|
||||
} else {
|
||||
const [sortby1, sortby2] = sortby.split('.');
|
||||
if (sortby1 !== '$') {
|
||||
switch (sortby2) {
|
||||
case 'BootTime':
|
||||
{
|
||||
const currentTime = Date.now();
|
||||
aValue = currentTime - a.Host.BootTime * 1000;
|
||||
bValue = currentTime - b.Host.BootTime * 1000;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
aValue = a[sortby1][sortby2];
|
||||
bValue = b[sortby1][sortby2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (sortby2) {
|
||||
case 'TotalTransfer':
|
||||
{
|
||||
aValue = a.State.NetInTransfer + a.State.NetOutTransfer;
|
||||
bValue = b.State.NetInTransfer + b.State.NetOutTransfer;
|
||||
break;
|
||||
}
|
||||
case 'TotalConnCount':
|
||||
{
|
||||
aValue = a.State.TcpConnCount + a.State.UdpConnCount;
|
||||
bValue = b.State.TcpConnCount + b.State.UdpConnCount;
|
||||
break;
|
||||
}
|
||||
case 'CPU':
|
||||
{
|
||||
aValue = a.Host.CPU.length;
|
||||
bValue = b.Host.CPU.length;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
if (order === 'desc') {
|
||||
return bValue - aValue;
|
||||
}
|
||||
return aValue - bValue;
|
||||
}
|
||||
@ -30,6 +30,11 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="right-box">
|
||||
<server-sort-box
|
||||
v-if="showSort"
|
||||
v-model="sortData"
|
||||
:options="sortOptions"
|
||||
/>
|
||||
<server-option-box
|
||||
v-if="onlineOptions.length"
|
||||
v-model="filterFormData.online"
|
||||
@ -122,11 +127,17 @@ import validate from '@/utils/validate';
|
||||
|
||||
import WorldMap from '@/components/world-map/world-map.vue';
|
||||
import ServerOptionBox from './components/server-list/server-option-box.vue';
|
||||
import ServerSortBox from './components/server-list/server-sort-box.vue';
|
||||
import ServerListWarp from './components/server-list/server-list-warp.vue';
|
||||
import ServerCardItem from './components/server-list/card/server-list-item.vue';
|
||||
import ServerRowItem from './components/server-list/row/server-list-item.vue';
|
||||
import ServerStatusMain from './components/server-list/server-status/main.vue';
|
||||
|
||||
import {
|
||||
serverSortOptions,
|
||||
serverSortHandler,
|
||||
} from './composable/server-sort';
|
||||
|
||||
const store = useStore();
|
||||
const worldMapWidth = ref();
|
||||
const windowWidth = ref(window.innerWidth);
|
||||
@ -272,6 +283,16 @@ const listTypeOptions = computed(() => [{
|
||||
icon: 'ri-server-line',
|
||||
}]);
|
||||
|
||||
/**
|
||||
* 排序处理
|
||||
*/
|
||||
const showSort = computed(() => config.nazhua.hideSort !== true);
|
||||
const sortData = ref({
|
||||
prop: 'DisplayIndex',
|
||||
order: 'desc',
|
||||
});
|
||||
const sortOptions = computed(() => serverSortOptions());
|
||||
|
||||
const filterServerList = computed(() => {
|
||||
const fields = {};
|
||||
const locationMap = {};
|
||||
@ -332,6 +353,7 @@ const filterServerList = computed(() => {
|
||||
|
||||
return true;
|
||||
});
|
||||
list.sort((a, b) => serverSortHandler(a, b, sortData.value.prop, sortData.value.order));
|
||||
return {
|
||||
fields,
|
||||
list,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user