569 lines
14 KiB
Vue
569 lines
14 KiB
Vue
<script setup lang="ts">
|
|
import { commonApi } from '@/api/common'
|
|
import EditUserInfo from '@/components/EditUserInfo.vue'
|
|
import { useUserStore } from '@/stores/user'
|
|
import { formatDate } from '@/utils/index.ts'
|
|
import { NConfigProvider, NMessageProvider } from 'naive-ui'
|
|
import { nextTick, onMounted, onUnmounted, ref } from 'vue'
|
|
|
|
const loading = ref(false)
|
|
const finished = ref(false)
|
|
const total = ref(0) // 总条数
|
|
const loadingTrigger = ref(null)
|
|
|
|
const observer = ref<IntersectionObserver | null>(null)
|
|
definePageMeta({
|
|
layout: 'default',
|
|
})
|
|
interface UserInfo {
|
|
nickName?: string // 使用 ? 表示 nickName 是可选的
|
|
avatar?: string
|
|
name?: string
|
|
brief?: string
|
|
}
|
|
const userStore = useUserStore()
|
|
const userInfo: UserInfo = userStore.userInfo
|
|
|
|
// 当前是发布还是点赞?
|
|
const currentState = ref('mallProduct')
|
|
// 当前是模型还是工作流还是图片?
|
|
const currentType = ref('0')
|
|
|
|
const orderOptions = ref([
|
|
{
|
|
dictLabel: '最新',
|
|
dictValue: 'create_time',
|
|
},
|
|
{
|
|
dictLabel: '最热',
|
|
dictValue: 'like_num',
|
|
},
|
|
])
|
|
|
|
const stateList = ref([
|
|
{ id: 'mallProduct', title: '发布' },
|
|
{ id: 'like', title: '点赞' },
|
|
])
|
|
const typeList = ref([
|
|
{ id: '0', title: '模型' },
|
|
{ id: '1', title: '工作流' },
|
|
{ id: '2', title: '图片' },
|
|
])
|
|
|
|
// 发布的form查询条件
|
|
const publishParams = ref({
|
|
pageNum: 1,
|
|
pageSize: 12,
|
|
status: '0',
|
|
orderByColumn: 'create_time',
|
|
date: null,
|
|
endTime: '',
|
|
startTime: '',
|
|
})
|
|
|
|
function initPublishParams() {
|
|
publishParams.value = {
|
|
pageNum: 1,
|
|
pageSize: 12,
|
|
status: '0',
|
|
orderByColumn: 'create_time',
|
|
date: null,
|
|
endTime: '',
|
|
startTime: '',
|
|
}
|
|
}
|
|
|
|
// 点赞form的查询条件
|
|
const likesParams = ref({
|
|
pageNum: 1,
|
|
pageSize: 12,
|
|
orderByColumn: 'create_time',
|
|
})
|
|
|
|
function initLikesParams() {
|
|
likesParams.value = {
|
|
pageNum: 1,
|
|
pageSize: 12,
|
|
orderByColumn: 'create_time',
|
|
}
|
|
}
|
|
|
|
const urlList = ref({
|
|
mallProduct: {
|
|
0: '/model/selectByUserIdModel',
|
|
1: '/model/selectByUserIdWorkFlow',
|
|
2: '/model/selectByUserIdImage',
|
|
},
|
|
like: {
|
|
0: '/model/likeModel',
|
|
1: '/model/likeWorkFlow',
|
|
2: '/model/likeImage',
|
|
},
|
|
})
|
|
|
|
// 获取数据字典
|
|
|
|
const statusOptions = ref([])
|
|
async function getDictType() {
|
|
try {
|
|
const res = await commonApi.dictType({
|
|
type: 'mall_product_status',
|
|
})
|
|
if (res.code === 200 && res.data.length > 0) {
|
|
statusOptions.value = res.data
|
|
publishParams.value.status = res.data[0].dictValue
|
|
}
|
|
}
|
|
catch (error) {
|
|
console.log(error)
|
|
}
|
|
}
|
|
getDictType()
|
|
|
|
// 编辑用户信息
|
|
interface EditUserInfoType {
|
|
isVisible: boolean
|
|
}
|
|
const editUserInfoRef = ref<EditUserInfoType | null>(null)
|
|
function onEditInfo() {
|
|
if (editUserInfoRef.value) {
|
|
editUserInfoRef.value.isVisible = true
|
|
}
|
|
}
|
|
|
|
// 实名认证
|
|
interface AuthComponentType {
|
|
isVisible: boolean
|
|
}
|
|
const authenticationRef = ref<AuthComponentType | null>(null)
|
|
function onAuth() {
|
|
if (authenticationRef.value) {
|
|
authenticationRef.value.isVisible = true
|
|
}
|
|
}
|
|
|
|
function initChangeParams() {
|
|
if (currentState.value === 'mallProduct') {
|
|
initPublishParams()
|
|
}
|
|
else {
|
|
initLikesParams()
|
|
}
|
|
finished.value = false // 重置加载完成状态
|
|
getList()
|
|
}
|
|
// 切换发布/点赞
|
|
function changeTabs(id: string) {
|
|
currentState.value = id
|
|
currentType.value = '0'
|
|
initChangeParams()
|
|
}
|
|
|
|
// 切换模型/工作流/图片
|
|
function changeType(id: string) {
|
|
currentType.value = id
|
|
initChangeParams()
|
|
}
|
|
|
|
function initPageNUm() {
|
|
if (currentState.value === 'mallProduct') {
|
|
publishParams.value.pageNum = 1
|
|
}
|
|
else {
|
|
likesParams.value.pageNum = 1
|
|
}
|
|
finished.value = false // 重置加载完成状态
|
|
getList()
|
|
}
|
|
|
|
// 切换select全部状态/已发布/
|
|
function changeStatus(value: string) {
|
|
publishParams.value.status = value
|
|
initPageNUm()
|
|
}
|
|
|
|
// 切换发布的最热/最新
|
|
function changeOrder(value: string) {
|
|
publishParams.value.orderByColumn = value
|
|
initPageNUm()
|
|
}
|
|
|
|
// 切换点赞的最热/最新
|
|
function changeLikeOrder(value: string) {
|
|
likesParams.value.orderByColumn = value
|
|
initPageNUm()
|
|
}
|
|
|
|
// 切换日期
|
|
async function changeDate(value: string[]) {
|
|
publishParams.value.startTime = `${await formatDate(value[0] as string)} 00:00:00`
|
|
publishParams.value.endTime = `${await formatDate(value[1] as string)} 23:59:59`
|
|
initPageNUm()
|
|
}
|
|
|
|
// 获取用户点赞/粉丝/关注数量
|
|
interface SelectUserInfo {
|
|
likeCount: number
|
|
bean: number
|
|
download: number
|
|
attention: number
|
|
}
|
|
const selectUserInfo = ref<SelectUserInfo>({
|
|
likeCount: 0,
|
|
bean: 0,
|
|
download: 0,
|
|
attention: 0,
|
|
})
|
|
async function getAttention() {
|
|
try {
|
|
const res = await request.get('/attention/selectUserInfo')
|
|
if (res.code === 200) {
|
|
selectUserInfo.value = res.data
|
|
}
|
|
}
|
|
catch (err) {
|
|
console.log(err)
|
|
}
|
|
}
|
|
getAttention()
|
|
|
|
// Banner 样式
|
|
const bannerStyle = {
|
|
backgroundImage:
|
|
'url(\'https://liblibai-web-static.liblib.cloud/liblibai_v4_online/static/_next/static/images/defaultBgImg.381282c0f2b01780c83d8fe6dc0aa90a.png\')',
|
|
}
|
|
|
|
// 定义响应接口
|
|
interface ApiResponse<T> {
|
|
code: number
|
|
rows: T[]
|
|
message: string
|
|
}
|
|
// 定义数据接口
|
|
interface UserData {
|
|
id: number
|
|
name: string
|
|
email: string
|
|
status: number
|
|
}
|
|
// 查询发布模型接口
|
|
const dataList = ref([])
|
|
async function getList() {
|
|
if (loading.value || finished.value)
|
|
return
|
|
|
|
loading.value = true
|
|
let params = {}
|
|
if (currentState.value === 'mallProduct') {
|
|
params = publishParams.value
|
|
}
|
|
else {
|
|
params = likesParams.value
|
|
}
|
|
|
|
const url = urlList.value[currentState.value][currentType.value]
|
|
try {
|
|
const res = await request.post<ApiResponse<UserData>>(url, params)
|
|
if (res.code === 200) {
|
|
// 如果是第一页,直接赋值,否则追加数据
|
|
if (params.pageNum === 1) {
|
|
dataList.value = res.rows
|
|
}
|
|
else {
|
|
dataList.value = [...dataList.value, ...res.rows]
|
|
}
|
|
|
|
total.value = res.total // 假设接口返回了总条数
|
|
|
|
// 判断是否加载完所有数据
|
|
if (dataList.value.length >= total.value) {
|
|
finished.value = true
|
|
}
|
|
|
|
// 自动增加页码
|
|
if (currentState.value === 'mallProduct') {
|
|
publishParams.value.pageNum++
|
|
}
|
|
else {
|
|
likesParams.value.pageNum++
|
|
}
|
|
}
|
|
}
|
|
catch (err) {
|
|
dataList.value = []
|
|
finished.value = true
|
|
console.log(err)
|
|
}
|
|
finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
getList()
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('scroll', topedRefresh)
|
|
observer.value = new IntersectionObserver(([entry]) => {
|
|
if (entry.isIntersecting && !loading.value && !finished.value) {
|
|
getList()
|
|
}
|
|
}, {
|
|
threshold: 0.1,
|
|
})
|
|
|
|
if (loadingTrigger.value) {
|
|
observer.value.observe(loadingTrigger.value)
|
|
}
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('scroll', topedRefresh)
|
|
if (observer.value) {
|
|
observer.value.disconnect()
|
|
}
|
|
})
|
|
|
|
async function topedRefresh() {
|
|
if (import.meta.client) {
|
|
await nextTick()
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth',
|
|
})
|
|
}
|
|
initPageNUm()
|
|
}
|
|
|
|
// 关注/粉丝列表模态框
|
|
const currentAttentionType = ref<string>('attention')
|
|
const attentionIsVisible = ref<boolean>(false)
|
|
// function onShowAttentionModel(type: string) {
|
|
// attentionIsVisible.value = true
|
|
// }
|
|
function onCloseAttentionModel() {
|
|
attentionIsVisible.value = false
|
|
}
|
|
function onClearDate(){
|
|
publishParams.value.startTime = '',
|
|
publishParams.value.endTime = '',
|
|
initPageNUm()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="mx-auto container">
|
|
<!-- Banner Section -->
|
|
<div class="banner-content h-32 bg-blue bg-cover bg-center" :style="bannerStyle" />
|
|
|
|
<!-- User Info Section -->
|
|
<div class="info-content mt-[-50px] p-5">
|
|
<div class="edit-info-content flex items-center">
|
|
<!-- Avatar -->
|
|
|
|
<div class="mc-head mr-5 h-20 w-20 rounded-full bg-white shadow-lg flex items-center justify-center">
|
|
<div class="mc-head-inner h-18 w-18 m-1 rounded-full bg-blue-200">
|
|
<client-only>
|
|
<img
|
|
class="head-img m-1 h-16 w-16 rounded-full bg-white"
|
|
:src="userStore.userInfo.avatar"
|
|
alt="User Avatar"
|
|
>
|
|
</client-only>
|
|
<!-- {{ userStore.userInfo.avatar }} -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Info Button -->
|
|
<div
|
|
class="edit-info mr-2 cursor-pointer rounded-full bg-white px-5 py-2 shadow-md"
|
|
@click="onEditInfo()"
|
|
>
|
|
编辑资料
|
|
</div>
|
|
<!-- Real Name Verification -->
|
|
<div
|
|
v-if="userInfo.name"
|
|
class="edit-info rounded-full bg-white px-5 py-2 shadow-md"
|
|
>
|
|
已经实名
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="edit-info cursor-pointer rounded-full bg-white px-5 py-2 shadow-md"
|
|
@click="onAuth()"
|
|
>
|
|
去实名
|
|
</div>
|
|
</div>
|
|
|
|
<!-- User Details -->
|
|
<div class="user-info mt-4">
|
|
<div v-if="userStore.userInfo" class="nickname text-2xl font-bold">
|
|
{{ userStore.userInfo.nickName }}
|
|
<!-- {{ userInfo.nickName }} -->
|
|
</div>
|
|
<div v-if="userStore.userInfo.brief" class="info-desc mt-1 text-sm text-gray-700">
|
|
{{ userStore.userInfo.brief }}
|
|
</div>
|
|
<div class="production-state mt-4 flex text-sm text-gray-700">
|
|
<div class="production-state-item mr-5 cursor-pointer" @click="onShowAttentionModel('bean')">
|
|
<span class="production-state-number font-bold">{{
|
|
selectUserInfo.bean ? selectUserInfo.bean : 0
|
|
}}</span>
|
|
粉丝
|
|
</div>
|
|
<div class="production-state-item mr-5 cursor-pointer" @click="onShowAttentionModel('attention')">
|
|
<span class="production-state-number font-bold">{{
|
|
selectUserInfo.attention ? selectUserInfo.attention : 0
|
|
}}</span>
|
|
关注
|
|
</div>
|
|
<div class="production-state-item mr-5">
|
|
<span class="production-state-number font-bold">{{
|
|
selectUserInfo.download ? selectUserInfo.download : 0
|
|
}}</span>
|
|
作品被使用次数
|
|
</div>
|
|
<div class="production-state-item">
|
|
<span class="production-state-number font-bold">{{
|
|
selectUserInfo.likeCount ? selectUserInfo.likeCount : 0
|
|
}}</span>
|
|
作品被点赞次数
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mc-tabs flex px-5 pb-3" style="border-bottom: 1px solid #e0e0e0">
|
|
<div
|
|
v-for="(item, index) in stateList"
|
|
:key="index"
|
|
class="mc-tabs-btn mr-2 cursor-pointer rounded-full px-5 py-1"
|
|
:class="{
|
|
'bg-black text-white': currentState === item.id,
|
|
'bg-white text-black': currentState !== item.id,
|
|
}"
|
|
@click="changeTabs(item.id)"
|
|
>
|
|
{{ item.title }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="select-content mt-4 flex items-center justify-between px-5">
|
|
<div class="flex items-center rounded-full bg-gray-100">
|
|
<div
|
|
v-for="(item, index) in typeList"
|
|
:key="index"
|
|
class="m-1 mr-2 cursor-pointer rounded-full px-4 py-1 text-sm"
|
|
:class="{
|
|
'bg-white': item.id === currentType,
|
|
}"
|
|
@click="changeType(item.id)"
|
|
>
|
|
{{ item.title }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Published Works Filters -->
|
|
<div v-if="currentState === 'mallProduct'" class="flex items-center">
|
|
<div>
|
|
<n-select
|
|
v-model:value="publishParams.status"
|
|
:options="statusOptions"
|
|
label-field="dictLabel"
|
|
value-field="dictValue"
|
|
placeholder="请选择"
|
|
style="width: 180px;"
|
|
class="mr-2"
|
|
@update:value="changeStatus"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<n-select
|
|
v-model:value="publishParams.orderByColumn"
|
|
:options="orderOptions"
|
|
label-field="dictLabel"
|
|
value-field="dictValue"
|
|
placeholder="请选择"
|
|
class="mr-2 w-28"
|
|
@update:value="changeOrder"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<n-date-picker
|
|
v-model:value="publishParams.date"
|
|
type="daterange"
|
|
style="width: 230px;"
|
|
format="yyyy-MM-dd"
|
|
value-format="yyyy.MM.dd"
|
|
range-separator="至"
|
|
start-placeholder="开始日期"
|
|
end-placeholder="结束日期"
|
|
:on-clear="onClearDate"
|
|
clearable
|
|
@update:value="changeDate"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Liked Works Filters -->
|
|
<div v-if="currentState === 'like'" class="flex items-center">
|
|
<NSelect
|
|
v-model:value="likesParams.orderByColumn"
|
|
:options="orderOptions"
|
|
label-field="dictLabel"
|
|
value-field="dictValue"
|
|
placeholder="请选择"
|
|
class="w-32"
|
|
@update:value="changeLikeOrder"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<NConfigProvider>
|
|
<NMessageProvider>
|
|
<Authentication ref="authenticationRef" />
|
|
<EditUserInfo ref="editUserInfoRef" />
|
|
</NMessageProvider>
|
|
</NConfigProvider>
|
|
<!-- Dialog Components -->
|
|
|
|
<div class="login-content my-4 grid grid-cols-4 gap-4 px-5">
|
|
<PersonalCenterCard
|
|
v-for="(item, index) in dataList"
|
|
:key="index"
|
|
:item="item"
|
|
:current-type="currentType"
|
|
:current-state="currentState"
|
|
@toped-refresh="topedRefresh"
|
|
/>
|
|
</div>
|
|
<div ref="loadingTrigger" class="h-10">
|
|
<div v-if="loading" class="text-center text-gray-500">
|
|
加载中...
|
|
</div>
|
|
<div v-if="finished" class="text-center text-gray-500">
|
|
没有更多数据了
|
|
</div>
|
|
</div>
|
|
<NModal
|
|
v-model:show="attentionIsVisible"
|
|
:on-after-leave="onCloseAttentionModel"
|
|
preset="card"
|
|
style="width: 400px"
|
|
:mask-closable="false"
|
|
>
|
|
<div>
|
|
<div class="">
|
|
<div>粉丝</div>
|
|
<div>关注</div>
|
|
</div>
|
|
</div>
|
|
</NModal>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.edit-info {
|
|
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2);
|
|
}
|
|
</style>
|