2026-04-09 13:36:03 +08:00
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
|
|
|
|
import { VueDraggable } from "vue-draggable-plus"
|
2026-04-10 14:40:48 +08:00
|
|
|
import contentItem from "./contentItem.vue"
|
|
|
|
|
import selectMenu from '@/component/modules/selectMenu.vue'
|
|
|
|
|
|
2026-04-09 13:36:03 +08:00
|
|
|
|
|
|
|
|
//const props = defineProps({
|
|
|
|
|
//})
|
|
|
|
|
//const emit = defineEmits([
|
|
|
|
|
//])
|
|
|
|
|
let data = reactive({
|
|
|
|
|
showDrafts: false,
|
|
|
|
|
})
|
|
|
|
|
const list = ref([
|
|
|
|
|
{ id: "1" },
|
|
|
|
|
{ id: "2" },
|
|
|
|
|
{ id: "3" },
|
|
|
|
|
{ id: "4" },
|
|
|
|
|
{ id: "5" },
|
|
|
|
|
{ id: "6" },
|
|
|
|
|
{ id: "7" },
|
|
|
|
|
{ id: "8" },
|
|
|
|
|
{ id: "9" },
|
|
|
|
|
{ id: "9" },
|
|
|
|
|
{ id: "9" },
|
|
|
|
|
{ id: "9" },
|
|
|
|
|
{ id: "9" },
|
|
|
|
|
{ id: "9" },
|
|
|
|
|
{ id: "10" }
|
|
|
|
|
])
|
|
|
|
|
const list2 = ref([
|
|
|
|
|
{ id: "1" },
|
|
|
|
|
{ id: "2" },
|
|
|
|
|
{ id: "3" },
|
|
|
|
|
{ id: "4" },
|
|
|
|
|
{ id: "5" },
|
|
|
|
|
{ id: "6" },
|
|
|
|
|
{ id: "7" },
|
|
|
|
|
{ id: "8" },
|
|
|
|
|
{ id: "9" },
|
|
|
|
|
{ id: "10" }
|
|
|
|
|
])
|
|
|
|
|
const config = ref({
|
|
|
|
|
"data-container-type": "root",
|
|
|
|
|
"data-parent-id": "null",
|
|
|
|
|
animation: 250,
|
|
|
|
|
handle: ".item", // 可拖动的元素
|
|
|
|
|
"ghost-class": "ghost", // 拖动时的类名
|
|
|
|
|
"chosen-class": "chosen", // 选中时的类名
|
|
|
|
|
"drag-class": "drag", // 拖动时的类名
|
|
|
|
|
"swap-threshold": 0.5,
|
|
|
|
|
"empty-insert-threshold": 5,
|
|
|
|
|
"force-fallback": false,
|
|
|
|
|
"fallback-tolerance": 3,
|
|
|
|
|
"scroll-sensitivity": 100,
|
|
|
|
|
"scroll-speed": 10,
|
|
|
|
|
onEnd: (e) => {}
|
|
|
|
|
})
|
2026-04-10 14:40:48 +08:00
|
|
|
const domSize = ref('Small')
|
|
|
|
|
const domSizeList = ref([
|
|
|
|
|
{
|
|
|
|
|
label:'Small',
|
|
|
|
|
value:'Small',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label:'Medium',
|
|
|
|
|
value:'Medium',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label:'Large',
|
|
|
|
|
value:'Large',
|
|
|
|
|
},
|
|
|
|
|
])
|
|
|
|
|
const listingsBoxRef = ref(null) as any
|
|
|
|
|
let resizeObserver = null as any
|
|
|
|
|
|
|
|
|
|
const gap = ref({
|
|
|
|
|
Small: '16px',
|
|
|
|
|
Medium: '24px',
|
|
|
|
|
Large: '30px',
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//根据宽度设置列表宽度
|
|
|
|
|
let upDataDomWidthTime = null
|
|
|
|
|
const setDomSize = (width: number)=>{
|
|
|
|
|
if(!listingsBoxRef.value)return
|
|
|
|
|
let listDom = listingsBoxRef.value.querySelector('.list')
|
|
|
|
|
let listItemDom = listDom.querySelector('.item')
|
|
|
|
|
let offsetWidth = listItemDom.getBoundingClientRect().width
|
|
|
|
|
let lineNum = Math.floor(width / offsetWidth)
|
|
|
|
|
let itemNum = Math.floor((width - (lineNum - 1) * parseInt(gap.value[domSize.value])) / offsetWidth)
|
|
|
|
|
listDom.style.maxWidth = ((itemNum - 1) * parseInt(gap.value[domSize.value]) + itemNum * (offsetWidth)) + 'px'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const changeDomSize = ()=>{
|
|
|
|
|
setTimeout(()=>{
|
|
|
|
|
setDomSize(listingsBoxRef.value.clientWidth)
|
|
|
|
|
},350)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const unfoldDrafits = ()=>{
|
|
|
|
|
data.showDrafts = !data.showDrafts
|
|
|
|
|
}
|
2026-04-09 13:36:03 +08:00
|
|
|
onMounted(()=>{
|
2026-04-10 14:40:48 +08:00
|
|
|
// 创建观察器
|
|
|
|
|
nextTick(()=>{
|
|
|
|
|
resizeObserver = new ResizeObserver((entries) => {
|
|
|
|
|
for (const entry of entries) {
|
|
|
|
|
// 获取元素尺寸
|
|
|
|
|
const width = entry.contentRect.width
|
|
|
|
|
const height = entry.contentRect.height
|
|
|
|
|
const borderBoxSize = entry.borderBoxSize[0] // 包含边框
|
|
|
|
|
const contentBoxSize = entry.contentBoxSize[0] // 内容区域
|
|
|
|
|
if(upDataDomWidthTime)clearTimeout(upDataDomWidthTime)
|
|
|
|
|
upDataDomWidthTime = setTimeout(()=>{
|
|
|
|
|
setDomSize(width)
|
|
|
|
|
},200)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
// 开始监听
|
|
|
|
|
if(resizeObserver)resizeObserver.observe(listingsBoxRef.value)
|
|
|
|
|
})
|
2026-04-09 13:36:03 +08:00
|
|
|
})
|
|
|
|
|
onUnmounted(()=>{
|
2026-04-10 14:40:48 +08:00
|
|
|
// 停止监听
|
|
|
|
|
if(listingsBoxRef?.value)resizeObserver?.unobserve(listingsBoxRef?.value)
|
2026-04-09 13:36:03 +08:00
|
|
|
})
|
|
|
|
|
defineExpose({})
|
|
|
|
|
const { showDrafts } = toRefs(data);
|
|
|
|
|
</script>
|
|
|
|
|
<template>
|
|
|
|
|
<div class="listings">
|
|
|
|
|
<div class="listingsBox listingsBox1">
|
2026-04-10 14:40:48 +08:00
|
|
|
<div class="box" :class="domSize" ref="listingsBoxRef">
|
2026-04-09 13:36:03 +08:00
|
|
|
<div class="title">
|
|
|
|
|
<div class="left">
|
|
|
|
|
<i class="fi fi-rs-comments"></i>
|
|
|
|
|
<span>Active Listings</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="right">
|
2026-04-10 14:40:48 +08:00
|
|
|
<div class="generalModel_state">
|
|
|
|
|
<div>
|
|
|
|
|
<selectMenu
|
|
|
|
|
:selectList="domSizeList"
|
|
|
|
|
@change="changeDomSize"
|
|
|
|
|
:isBtnOpen='true'
|
|
|
|
|
:style="{
|
|
|
|
|
'border-radius':'0rem',
|
|
|
|
|
'border':'none',
|
|
|
|
|
'font-weight': '900',
|
|
|
|
|
'border-right':'2px solid rgba(0,0,0,.2)',
|
|
|
|
|
'line-height': '3rem',
|
|
|
|
|
'height': '3rem',
|
|
|
|
|
'background': 'rgba(0,0,0,0)',
|
|
|
|
|
}"
|
|
|
|
|
v-model:select="domSize"
|
|
|
|
|
>
|
|
|
|
|
<template v-slot:btnText>
|
|
|
|
|
{{ $t('Header.Filter') }}
|
|
|
|
|
</template>
|
|
|
|
|
</selectMenu>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<selectMenu
|
|
|
|
|
:selectList="domSizeList"
|
|
|
|
|
@change="changeDomSize"
|
|
|
|
|
:isBtnOpen='true'
|
|
|
|
|
:style="{
|
|
|
|
|
'border-radius':'0rem',
|
|
|
|
|
'border':'none',
|
|
|
|
|
'font-weight': '900',
|
|
|
|
|
'line-height': '3rem',
|
|
|
|
|
'height': '3rem',
|
|
|
|
|
'background': 'rgba(0,0,0,0)',
|
|
|
|
|
}"
|
|
|
|
|
v-model:select="domSize"
|
|
|
|
|
>
|
|
|
|
|
<template v-slot:btnText>
|
|
|
|
|
{{ $t('Header.Size') }}
|
|
|
|
|
</template>
|
|
|
|
|
</selectMenu>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-04-09 13:36:03 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<VueDraggable
|
|
|
|
|
v-model="list"
|
|
|
|
|
class="list"
|
2026-04-10 14:40:48 +08:00
|
|
|
:style="{gap: gap[domSize] || '1.6rem'}"
|
2026-04-09 13:36:03 +08:00
|
|
|
v-bind="config"
|
|
|
|
|
:group="{
|
|
|
|
|
name: 'sortable',
|
|
|
|
|
pull: false,
|
|
|
|
|
put: true
|
|
|
|
|
}"
|
|
|
|
|
>
|
2026-04-10 14:40:48 +08:00
|
|
|
|
|
|
|
|
<contentItem v-for="v in list" :key="v.id" type="listings" :domSize="domSize"/>
|
2026-04-09 13:36:03 +08:00
|
|
|
</VueDraggable>
|
|
|
|
|
</div>
|
2026-04-10 14:40:48 +08:00
|
|
|
<div class="openOrCloseDrafts" :class="{'active': showDrafts}" @click="unfoldDrafits">
|
2026-04-09 13:36:03 +08:00
|
|
|
<span class="icon iconfont icon-xiala"></span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-04-10 14:40:48 +08:00
|
|
|
<div class="listingsBox listingsBox2" :class="{'active': showDrafts}">
|
|
|
|
|
<div class="box Small">
|
|
|
|
|
<div class="title">
|
|
|
|
|
<div class="left">
|
|
|
|
|
<i class="fi fi-rs-comments"></i>
|
|
|
|
|
<span>Drafts</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-04-09 13:36:03 +08:00
|
|
|
<VueDraggable
|
|
|
|
|
v-model="list2"
|
2026-04-10 14:40:48 +08:00
|
|
|
class="list2"
|
2026-04-09 13:36:03 +08:00
|
|
|
v-bind="config"
|
|
|
|
|
:group="{
|
|
|
|
|
name: 'sortable',
|
|
|
|
|
pull: true,
|
|
|
|
|
put: true
|
|
|
|
|
}"
|
|
|
|
|
>
|
2026-04-10 14:40:48 +08:00
|
|
|
<contentItem v-for="v in list2" :key="v.id" domSize="Small" type="drafts"/>
|
2026-04-09 13:36:03 +08:00
|
|
|
</VueDraggable>
|
|
|
|
|
</div>
|
2026-04-10 14:40:48 +08:00
|
|
|
</div>
|
2026-04-09 13:36:03 +08:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<style lang="less" scoped>
|
|
|
|
|
.listings{
|
2026-04-09 13:48:46 +08:00
|
|
|
position: absolute;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
2026-04-09 13:36:03 +08:00
|
|
|
display: flex;
|
|
|
|
|
gap: 2rem;
|
|
|
|
|
.listingsBox{
|
|
|
|
|
background-color: #f9fafa;
|
|
|
|
|
border-radius: 2rem;
|
|
|
|
|
position: relative;
|
2026-04-09 13:48:46 +08:00
|
|
|
// overflow: hidden;
|
2026-04-09 13:36:03 +08:00
|
|
|
display: flex;
|
2026-04-10 14:40:48 +08:00
|
|
|
transition: all .3s;
|
2026-04-09 13:36:03 +08:00
|
|
|
.box{
|
|
|
|
|
width: 100%;
|
2026-04-10 14:40:48 +08:00
|
|
|
padding: 2.4rem 0;
|
2026-04-09 13:36:03 +08:00
|
|
|
padding-bottom: 0;
|
|
|
|
|
flex-direction: column;
|
2026-04-10 14:40:48 +08:00
|
|
|
overflow: hidden;
|
|
|
|
|
display: flex;
|
2026-04-09 13:36:03 +08:00
|
|
|
flex: 1;
|
2026-04-10 14:40:48 +08:00
|
|
|
padding: 2.4rem 4rem;
|
2026-04-09 13:36:03 +08:00
|
|
|
> .title{
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 3rem;
|
|
|
|
|
> .left{
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
font-family: pingfang_heavy;
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
font-size: 1.8rem;
|
|
|
|
|
line-height: 130%;
|
|
|
|
|
letter-spacing: 0%;
|
|
|
|
|
gap: 1.2rem;
|
2026-04-10 14:40:48 +08:00
|
|
|
align-content: flex-start;
|
2026-04-09 13:36:03 +08:00
|
|
|
> i{
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
font-size: 2.4rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-10 14:40:48 +08:00
|
|
|
.list2{
|
|
|
|
|
gap: 1.6rem;
|
|
|
|
|
.item{
|
|
|
|
|
width: 19.2rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.list,.list2{
|
2026-04-09 13:36:03 +08:00
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
2026-04-10 14:40:48 +08:00
|
|
|
// flex: 1;
|
|
|
|
|
margin: auto;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
&::-webkit-scrollbar {
|
|
|
|
|
display: none;
|
2026-04-09 13:36:03 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.listingsBox1{
|
2026-04-10 14:40:48 +08:00
|
|
|
width: 100%;
|
2026-04-09 13:36:03 +08:00
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
.listingsBox2{
|
|
|
|
|
width: 48.8rem;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
&.active{
|
|
|
|
|
width: 0;
|
|
|
|
|
}
|
|
|
|
|
.box{
|
|
|
|
|
width: 48.8rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.openOrCloseDrafts{
|
|
|
|
|
position: absolute;
|
|
|
|
|
width: 3.4rem;
|
|
|
|
|
height: 8rem;
|
|
|
|
|
border-top-right-radius: 2rem;
|
|
|
|
|
border-bottom-right-radius: 2rem;
|
|
|
|
|
border-width: 1.5px, 1.5px, 1.5px, 0px;
|
|
|
|
|
border-style: solid;
|
|
|
|
|
border-color: #000000;
|
|
|
|
|
right: 1.5px;
|
|
|
|
|
top: 50%;
|
|
|
|
|
transform: translate(100%,-50%);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.3s ease-in-out;
|
|
|
|
|
border-left: none;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
> span{
|
|
|
|
|
transform: rotate(-90deg);
|
|
|
|
|
transition: all .3s;
|
|
|
|
|
}
|
|
|
|
|
&.active{
|
|
|
|
|
> span{
|
|
|
|
|
transform: rotate(90deg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
&:hover{
|
|
|
|
|
width: 4.5rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|