This commit is contained in:
zyronon 2024-03-28 03:01:20 +08:00
parent 6a629dd65b
commit 68b225d839
13 changed files with 294 additions and 146 deletions

View File

@ -21,14 +21,14 @@
"vue": "3.4.21", "vue": "3.4.21",
"vue-masonry": "0.16.0", "vue-masonry": "0.16.0",
"vue-router": "4.3.0", "vue-router": "4.3.0",
"vue-switches": "2.0.1" "vue-switches": "2.0.1",
"axios-mock-adapter": "^1.22.0"
}, },
"devDependencies": { "devDependencies": {
"@iconify/vue": "^4.1.1", "@iconify/vue": "^4.1.1",
"@types/lodash-es": "^4.17.9", "@types/lodash-es": "^4.17.9",
"@vitejs/plugin-vue": "4.0.0", "@vitejs/plugin-vue": "4.0.0",
"@vitejs/plugin-vue-jsx": "3.0.0", "@vitejs/plugin-vue-jsx": "3.0.0",
"axios-mock-adapter": "^1.22.0",
"less": "4.1.3", "less": "4.1.3",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"rollup-plugin-visualizer": "^5.9.2", "rollup-plugin-visualizer": "^5.9.2",

View File

@ -135,8 +135,6 @@ export default {
</style> </style>
<style scoped lang="less"> <style scoped lang="less">
.call-float { .call-float {
transition-property: all; transition-property: all;
z-index: 9; z-index: 9;

View File

@ -32,7 +32,7 @@ export default {
&.full { &.full {
z-index: 999; z-index: 999;
position: fixed; position: absolute;
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);

View File

@ -7,11 +7,13 @@
<Loading :is-full-screen="false" :style="pullUpStyle"/> <Loading :is-full-screen="false" :style="pullUpStyle"/>
<div class="scroll-content" :style="pullUpStyle"> <div class="scroll-content" :style="pullUpStyle">
<slot></slot> <slot></slot>
<Loading v-if="loading" :is-full-screen="false"/>
</div> </div>
</div> </div>
<div v-else class="scroll-wrapper scroll Scroll" ref="wrapper" @scroll="scroll"> <div v-else class="scroll-wrapper scroll Scroll" ref="wrapper" @scroll="scroll">
<div class="scroll-content"> <div class="scroll-content">
<slot></slot> <slot></slot>
<Loading v-if="loading" :is-full-screen="fullLoading"/>
</div> </div>
</div> </div>
</template> </template>
@ -32,6 +34,14 @@ export default {
useRefresh: { useRefresh: {
type: Boolean, type: Boolean,
default: false default: false
},
loading: {
type: Boolean,
default: false
},
fullLoading: {
type: Boolean,
default: false
} }
}, },
data() { data() {
@ -110,5 +120,9 @@ export default {
.scroll-wrapper { .scroll-wrapper {
overflow: auto; overflow: auto;
.scroll-content {
padding-bottom: 30rem;
}
} }
</style> </style>

View File

@ -0,0 +1,67 @@
<template>
<Scroll
:loading="state.loading"
:full-loading="!state.list.length"
@pulldown="loadData">
<slot :list="state.list"></slot>
<NoMore v-if="state.total !== 0 && state.total === state.list.length"/>
</Scroll>
</template>
<script setup>
import {onMounted, reactive} from "vue";
import {_notice} from "@/utils";
import Scroll from "@/components/Scroll.vue";
import NoMore from "@/components/NoMore.vue";
const props = defineProps({
api: {
type: Function,
default() {
return () => void 0
}
}
})
const state = reactive({
list: [],
total: 0,
pageNo: 0,
pageSize: 10,
loading: false
})
function loadData() {
if (state.loading) return
state.pageNo++
getData()
}
async function getData(refresh = false) {
if (refresh) {
state.pageNo = 0
} else {
if (state.total !== 0 && state.total === state.list.length) return
}
if (state.loading) return
state.loading = true
let res = await props.api({pageNo: state.pageNo, pageSize: state.pageSize})
state.loading = false
if (res.success) {
if (refresh) {
state.list = res.data.list
} else {
state.list = state.list.concat(res.data.list)
}
state.total = res.data.total
} else {
_notice('查询失败')
}
}
onMounted(getData)
</script>
<style scoped lang="less">
</style>

View File

@ -0,0 +1,45 @@
<script setup>
import {computed} from "vue";
const props = defineProps({
list: {
type: Array,
default() {
return []
}
}
})
const leftList = computed(() => {
return props.list.filter((v, index) => index % 2 === 0)
})
const rightList = computed(() => {
return props.list.filter((v, index) => index % 2 !== 0)
})
</script>
<template>
<div class="waterfall">
<div class="waterfall-row">
<slot :item="item" v-for="item in leftList"></slot>
</div>
<div class="waterfall-row">
<slot :item="item" v-for="item in rightList"></slot>
</div>
</div>
</template>
<style scoped lang="less">
.waterfall {
display: flex;
gap: 10rem;
.waterfall-row {
width: 50%;
display: flex;
flex-direction: column;
}
}
</style>

View File

@ -6,7 +6,7 @@ import {useBaseStore} from "@/store/pinia";
import axiosInstance from "@/utils/request"; import axiosInstance from "@/utils/request";
import MockAdapter from "axios-mock-adapter"; import MockAdapter from "axios-mock-adapter";
const mock = new MockAdapter(axiosInstance); const mock = new MockAdapter(axiosInstance, {delayResponse: 300});
function getPage2(params) { function getPage2(params) {
let offset = params.pageNo * params.pageSize let offset = params.pageNo * params.pageSize
@ -71,12 +71,6 @@ async function fetchData() {
allRecommendVideos = allRecommendVideos.concat(v) allRecommendVideos = allRecommendVideos.concat(v)
}) })
}) })
fetch(BASE_URL + '/data/posts.json').then(r => {
r.json().then(v => {
allRecommendPosts = v
})
})
} }
export async function startMock() { export async function startMock() {
@ -192,11 +186,16 @@ export async function startMock() {
mock.onGet(/post\/recommended/).reply(async (config) => { mock.onGet(/post\/recommended/).reply(async (config) => {
let page = getPage2(config.params) let page = getPage2(config.params)
if (!allRecommendPosts.length) {
let r = await fetch(BASE_URL + '/data/posts.json')
allRecommendPosts = await r.json()
}
return [200, { return [200, {
data: { data: {
pageNo: page.pageNo, pageNo: page.pageNo,
total: allRecommendPosts.length, total: allRecommendPosts.length,
list: allRecommendPosts.slice(page.offset, page.limit), list: allRecommendPosts.slice(0, 1000).slice(page.offset, page.limit),
}, code: 200, msg: '', }, code: 200, msg: '',
}] }]
}) })

View File

@ -17,13 +17,13 @@
<img v-show="index === 0" src="../../../assets/img/icon/arrow-up-white.png" class="tab1-img"> <img v-show="index === 0" src="../../../assets/img/icon/arrow-up-white.png" class="tab1-img">
</div> </div>
<div class="tab" :class="{active:index === 1}" @click.stop="change(1)"> <div class="tab" :class="{active:index === 1}" @click.stop="change(1)">
<span>社区</span> <span>长视频</span>
</div> </div>
<div class="tab" :class="{active:index === 2}" @click.stop="change(2)"> <div class="tab" :class="{active:index === 2}" @click.stop="change(2)">
<span>关注</span> <span>关注</span>
<img src="../../../assets/img/icon/live.webp" class="tab2-img"> <img src="../../../assets/img/icon/live.webp" class="tab2-img">
</div> </div>
<div class="tab" :class="{active:index === 3}" @click.stop="change(3)"><span>长视频</span> <div class="tab" :class="{active:index === 3}" @click.stop="change(3)"><span>经验</span>
</div> </div>
<div class="tab" :class="{active:index === 4}" @click.stop="change(4)"><span>推荐</span> <div class="tab" :class="{active:index === 4}" @click.stop="change(4)"><span>推荐</span>
</div> </div>

View File

@ -107,11 +107,11 @@
v-model:index="state.navIndex"> v-model:index="state.navIndex">
<Slide0 :active="state.navIndex === 0 && state.baseIndex === 1"/> <Slide0 :active="state.navIndex === 0 && state.baseIndex === 1"/>
<SlideItem> <SlideItem>
<Community :active="state.navIndex === 1 && state.baseIndex === 1"/> <LongVideo :active="state.navIndex === 1 && state.baseIndex === 1"/>
</SlideItem> </SlideItem>
<Slide2 :active="state.navIndex === 2 && state.baseIndex === 1"/> <Slide2 :active="state.navIndex === 2 && state.baseIndex === 1"/>
<SlideItem> <SlideItem>
<LongVideo :active="state.navIndex === 3 && state.baseIndex === 1"/> <Community :active="state.navIndex === 3 && state.baseIndex === 1"/>
</SlideItem> </SlideItem>
<Slide4 :active="state.navIndex === 4 && state.baseIndex === 1"/> <Slide4 :active="state.navIndex === 4 && state.baseIndex === 1"/>
</SlideHorizontal> </SlideHorizontal>
@ -223,7 +223,7 @@ const baseStore = useBaseStore()
const state = reactive({ const state = reactive({
baseIndex: 1, baseIndex: 1,
navIndex: 4, navIndex: 3,
test: '', test: '',
recommendList: [], recommendList: [],
isSharing: false, isSharing: false,

View File

@ -1,45 +1,60 @@
<template> <template>
<div id="Community"> <div id="Community">
<Scroll class="Scroll" <ScrollList class="Scroll"
@pulldown="loadData"> v-if="state.show"
<div v-masonry class="goods-list" :api="recommendedPost"
transition-duration="0s" >
item-selector=".goods"> <template v-slot="{list}">
<div v-masonry-tile class="goods" <div class="search" @click="nav('/home/search')">
@click="nav('album-detail',{},item)" <div class="left">
v-for="(item, index) in state.list"> <Icon class="icon" icon="ion:search" @click.stop="$no()"/>
<div class="card"> <span>壁纸</span>
<!-- <img class="poster" v-lazy="Utils.$imgPreview(item.src)"/>--> </div>
<img class="poster" :src="_checkImgUrl(item.note_card?.cover?.url_default)"/> <div class="right">搜索</div>
<div class="bottom"> </div>
<div class="title"> <WaterfallList :list="list" class="list">
{{ item.note_card?.display_title }} <template v-slot="{item}">
</div> <div class="card"
<div class="b2"> @click="test"
<div class="user"> >
<img class="avatar" :src="_checkImgUrl(item.note_card?.user?.avatar)"/> <img class="poster" v-lazy="_checkImgUrl(item.note_card?.cover?.url_default)"/>
<div class="name">{{ item.note_card?.user?.nickname }}</div> <div class="bottom">
<div class="title">
{{ item.note_card?.display_title }}
</div> </div>
<div class="star"> <div class="b2">
<Icon icon="solar:heart-linear"/> <div class="user">
<div class="num">{{ item.note_card?.interact_info?.liked_count }}</div> <img class="avatar" :src="_checkImgUrl(item.note_card?.user?.avatar)"/>
<div class="name">{{ item.note_card?.user?.nickname }}</div>
</div>
<div class="star">
<Icon icon="solar:heart-linear"/>
<div class="num">{{ item.note_card?.interact_info?.liked_count }}</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </template>
</div> </WaterfallList>
</div> </template>
</Scroll> </ScrollList>
<div class="shadow">
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import {reactive, watch} from "vue"; import {reactive, watch} from "vue";
import {_checkImgUrl, _notice} from "@/utils"; import {$no, _checkImgUrl} from "@/utils";
import Scroll from "@/components/Scroll.vue";
import {recommendedPost} from "@/api/user"; import {recommendedPost} from "@/api/user";
import {useNav} from "@/utils/hooks/useNav"; import {useNav} from "@/utils/hooks/useNav";
import {Icon} from "@iconify/vue"; import {Icon} from "@iconify/vue";
import WaterfallList from "@/components/WaterfallList.vue";
import ScrollList from "@/components/ScrollList.vue";
//@click="nav('album-detail',{},item)"
const nav = useNav() const nav = useNav()
const props = defineProps({ const props = defineProps({
@ -50,42 +65,33 @@ const props = defineProps({
}) })
const state = reactive({ const state = reactive({
list: [], show: false
listEl: null,
total: 0,
pageNo: 0,
pageSize: 10,
}) })
watch(() => props.active, n => { watch(() => props.active, n => {
if (n && !state.list.length) { if (n && !state.show) {
getData(true, true) state.show = true
} }
}) }, {immediate: true})
function loadData() { function test(e) {
state.pageNo++ let rect = e.currentTarget.getBoundingClientRect()
getData() console.log('e', rect)
let s = $('.shadow')
s.empty()
s.append($(e.currentTarget).clone())
s.css('transition', '0s')
s.css('top', rect.top)
s.css('left', rect.left)
s.css('width', rect.width)
setTimeout(() => {
s.css('transition', 'all .3s')
s.css('top', 0)
s.css('left', 0)
s.css('width', '100vw')
})
} }
async function getData(loading = true, refresh = false) {
if (refresh) {
state.pageNo = 0
}
let res = await recommendedPost({pageNo: state.pageNo, pageSize: state.pageSize,})
console.log('res', res)
if (res.success) {
if (refresh) {
state.list = res.data.list
} else {
state.list = state.list.concat(res.data.list)
}
state.total = res.data.total
} else {
_notice('查询失败')
}
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@ -103,66 +109,94 @@ async function getData(loading = true, refresh = false) {
@p: 1rem; @p: 1rem;
.goods-list { .search {
padding: @p; margin-left: 2vw;
width: 96vw;
box-sizing: border-box; box-sizing: border-box;
padding: 10rem;
border: 1px solid #646464;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16rem;
margin-bottom: 10rem;
border-radius: 8rem;
.left {
display: flex;
align-items: center;
gap: 5rem;
color: gray;
svg {
font-size: 16rem;
}
}
} }
.goods { .list {
width: calc(50% - @p); margin-left: 2vw;
padding: 3rem; width: 96vw;
box-sizing: border-box; }
.card { .card {
border-radius: 4rem; border-radius: 4rem;
overflow: hidden; overflow: hidden;
background: var(--main-bg); background: var(--main-bg);
img { img {
width: 100%; width: 100%;
}
.bottom {
color: gainsboro;
padding: 10rem;
.title {
font-size: 14rem;
margin-bottom: 8rem;
} }
.bottom { .b2 {
color: gainsboro; display: flex;
padding: 10rem; justify-content: space-between;
align-items: center;
.title { .user {
font-size: 15rem; display: flex;
margin-bottom: 8rem; font-size: 12rem;
img {
width: 15rem;
border-radius: 50%;
margin-right: 5rem;
}
} }
.b2 { .star {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
gap: 3rem;
.user { svg {
display: flex; font-size: 15rem;
font-size: 12rem;
img {
width: 15rem;
border-radius: 50%;
margin-right: 10rem;
}
} }
.star { .num {
display: flex; font-size: 12rem;
align-items: center;
gap: 3rem;
svg {
font-size: 16rem;
}
.num {
font-size: 13rem;
}
} }
} }
} }
} }
} }
.shadow {
position: absolute;
left: 0;
top: 0;
width: 100vw;
transition: all .3s;
}
} }
</style> </style>

View File

@ -45,6 +45,7 @@
v-else v-else
ref="mainScroll" ref="mainScroll"
:use-refresh="true" :use-refresh="true"
:loading="loadingMore"
@refresh="refresh" @refresh="refresh"
@pulldown="loadData"> @pulldown="loadData">
<div class="messages"> <div class="messages">
@ -110,7 +111,6 @@
<Peoples v-model:list="recommend" <Peoples v-model:list="recommend"
:loading="loadingMore" :loading="loadingMore"
mode="recommend"/> mode="recommend"/>
<Loading :is-full-screen="false" v-if="loadingMore"/>
</Scroll> </Scroll>
</div> </div>
</div> </div>
@ -213,17 +213,6 @@ export default {
<style scoped lang="less"> <style scoped lang="less">
.list-complete-enter-from,
.list-complete-leave-to {
opacity: 0;
}
.list-complete-leave-active {
position: absolute;
}
#AllMessage { #AllMessage {
position: fixed; position: fixed;
left: 0; left: 0;
@ -274,7 +263,7 @@ export default {
} }
.content { .content {
padding: var(--page-padding); padding:0 var(--page-padding);
padding-top: var(--common-header-height); padding-top: var(--common-header-height);
.scroll { .scroll {
@ -297,6 +286,7 @@ export default {
.avatar { .avatar {
width: 48rem; width: 48rem;
height: 48rem;
border-radius: 50%; border-radius: 50%;
} }

View File

@ -215,7 +215,7 @@ export default {
data: '', data: '',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -230,7 +230,7 @@ export default {
}, },
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -255,7 +255,7 @@ export default {
data: new URL('../../../assets/img/poster/1.jpg', import.meta.url).href, data: new URL('../../../assets/img/poster/1.jpg', import.meta.url).href,
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
}, },
loved: [ loved: [
@ -285,7 +285,7 @@ export default {
data: new URL('../../../assets/img/poster/1.jpg', import.meta.url).href, data: new URL('../../../assets/img/poster/1.jpg', import.meta.url).href,
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
}, },
readState: READ_STATE.ARRIVED readState: READ_STATE.ARRIVED
@ -296,7 +296,7 @@ export default {
data: '2021-01-02 21:44', data: '2021-01-02 21:44',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -306,7 +306,7 @@ export default {
data: '2021-01-02 21:44', data: '2021-01-02 21:44',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -316,7 +316,7 @@ export default {
data: '2021-01-02 21:44', data: '2021-01-02 21:44',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -326,7 +326,7 @@ export default {
data: '2021-01-02 21:44', data: '2021-01-02 21:44',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -336,7 +336,7 @@ export default {
data: '2021-01-02 21:44', data: '2021-01-02 21:44',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -346,7 +346,7 @@ export default {
data: '2021-01-02 21:44', data: '2021-01-02 21:44',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -372,7 +372,7 @@ export default {
}, },
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -381,7 +381,7 @@ export default {
data: '又在刷抖音', data: '又在刷抖音',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -390,7 +390,7 @@ export default {
data: '我昨天@你那个视频发给我下', data: '我昨天@你那个视频发给我下',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -408,7 +408,7 @@ export default {
data: '我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了', data: '我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了我也找不到了',
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../assets/img/icon/head-image.jpg' avatar: '../../assets/img/icon/head-image.jpg'
} }
}, },
@ -437,7 +437,7 @@ export default {
}, },
time: '2021-01-02 21:21', time: '2021-01-02 21:21',
user: { user: {
id: '93864497380', id: '2739632844317827',
avatar: '../../../assets/img/icon/head-image.jpg' avatar: '../../../assets/img/icon/head-image.jpg'
} }
}, },

View File

@ -94,6 +94,7 @@
</template> </template>
</div> </div>
</template> </template>
<script> <script>
import {mapState} from "pinia"; import {mapState} from "pinia";
@ -153,7 +154,7 @@ export default {
computed: { computed: {
...mapState(useBaseStore, ['userinfo']), ...mapState(useBaseStore, ['userinfo']),
isMe() { isMe() {
return this.userinfo.id === this.message.user.id return this.userinfo.uid === this.message.user.id
} }
}, },
created() { created() {