Commit 76422e6d by lijiabin

【需求 20331】 styles: 基本上是一些样式优化

parent e5a127c9
// 企业文化接口
export * from './task'
export * from './sign'
export * from './article'
export * from './shop'
export * from './tag'
export * from './article'
export * from './user'
export * from './case'
export * from './home'
......@@ -19,7 +17,6 @@ export * from './dailyLottery'
// 导出类型
export * from './task/types'
export * from './shop/types'
export * from './article/types'
export * from './tag/types'
export * from './article/types'
export * from './user/types'
......
......@@ -76,6 +76,10 @@ export const articleTypeListOptionsForReal: { label: string; value: ArticleTypeE
label: '专访',
value: ArticleTypeEnum.INTERVIEW,
},
{
label: '问吧',
value: ArticleTypeEnum.QUESTION,
},
]
export const articleTypeListOptionsForNotReal: { label: string; value: ArticleTypeEnum }[] = [
......@@ -88,11 +92,6 @@ export const articleTypeListOptionsForNotReal: { label: string; value: ArticleTy
value: ArticleTypeEnum.VIDEO,
},
{
label: '问吧',
value: ArticleTypeEnum.QUESTION,
},
{
label: '专栏',
value: ArticleTypeEnum.COLUMN,
},
......
<template>
<Draggable
v-show="showOnlineTime"
:initial-value="{ x: x, y: y }"
storage-key="vueuse-draggable"
storage-type="session"
class="fixed"
class="fixed z-2"
>
<div class="cursor-pointer online-time flex flex-col items-center z-10050">
<!-- 图片容器 -->
<div
class="group flex items-center gap-2.5 py-2.5 pr-4 pl-3 bg-#fff rounded-xl shadow-[inset_0_2px_6px_rgba(0,0,0,0.08)] cursor-pointer transition-all duration-250 relative shadow-[inset_0_2px_8px_rgba(0,0,0,0.12)]"
>
<!-- 在线指示灯 -->
<div
class="mb-4 w-24 h-24 bg-white rounded-full shadow-xl flex items-center justify-center hover:scale-110 transition-transform cursor-pointer"
>
<img src="@/assets/img/culture/ask.png" draggable="false" alt="吉祥物" class="w-20 h-20" />
<!-- 如果没有图片,用emoji代替 -->
<!-- <span class="text-6xl">🎯</span> -->
class="w-1.5 h-1.5 rounded-full bg-green-500 shrink-0 animate-pulse shadow-[0_0_6px_rgba(34,197,94,0.4)]"
></div>
<!-- 时长内容 -->
<div class="flex flex-col leading-tight">
<span class="text-base text-gray-400 tracking-wider font-medium">今日在线时长</span>
<span class="text-base font-bold text-blue-600 tabular-nums tracking-wider">
{{ formatSeconds }}
</span>
</div>
<!-- 时长显示卡片 -->
<!-- 关闭按钮 -->
<div
class="w-24 md:w-28 lg:w-32 h-16 md:h-18 lg:h-20 rounded-lg bg-gradient-to-br from-white via-blue-50 to-purple-50 shadow-lg hover:shadow-xl border border-white/50 flex flex-col justify-center items-center transition-all duration-300 ease-out hover:scale-105 hover:-translate-y-1 backdrop-blur-sm relative overflow-hidden group transform-gpu backface-hidden will-change-transform"
class="absolute -top-1.5 -right-1.5 w-4 h-4 rounded-full bg-white text-gray-400 shadow-sm flex items-center justify-center opacity-0 scale-60 transition-all duration-200 cursor-pointer group-hover:opacity-100 group-hover:scale-100 hover:!bg-red-500 hover:!text-white"
title="关闭"
@click.stop="showOnlineTime = false"
>
<!-- 其他内容保持不变 -->
<div
class="absolute inset-0 bg-gradient-to-r from-blue-400/5 via-purple-400/5 to-pink-400/5 opacity-0 group-hover:opacity-100 transition-opacity duration-500"
></div>
<div
class="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 opacity-80"
></div>
<div
class="absolute top-1 left-0 right-0 h-1 bg-gradient-to-r from-blue-300 via-purple-300 to-pink-300 opacity-60"
></div>
<div class="text-center px-3 relative">
<div class="text-xs md:text-sm text-gray-700 font-medium mb-1 tracking-wide">
今日在线时长
</div>
<div
class="text-center text-2xl font-bold bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent"
>
{{ formatSeconds }}
</div>
</div>
<div class="absolute bottom-2 left-3 right-3 h-1 bg-gray-200 rounded-full overflow-hidden">
<div
class="h-full bg-gradient-to-r from-blue-400 to-purple-400 rounded-full animate-pulse"
:style="{
width: widthRate + '%',
}"
></div>
</div>
<div class="absolute top-3 right-3 w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
<div
class="absolute bottom-3 left-3 w-1.5 h-1.5 bg-blue-400 rounded-full animate-pulse"
style="animation-delay: 0.8s"
></div>
<div
class="absolute left-0 top-4 bottom-4 w-0.5 bg-gradient-to-b from-transparent via-purple-300 to-transparent opacity-50"
></div>
<div
class="absolute right-0 top-4 bottom-4 w-0.5 bg-gradient-to-b from-transparent via-blue-300 to-transparent opacity-50"
></div>
<svg viewBox="0 0 12 12" class="w-2.5 h-2.5">
<path
d="M1.5.4L6 4.9 10.5.4l1.1 1.1L7.1 6l4.5 4.5-1.1 1.1L6 7.1 1.5 11.6.4 10.5 4.9 6 .4 1.5z"
fill="currentColor"
/>
</svg>
</div>
</div>
</Draggable>
</template>
<script setup lang="ts">
import { UseDraggable as Draggable } from '@vueuse/components'
import { useWindowSize } from '@vueuse/core'
import { getTodayOnlineSeconds, heartbeat } from '@/api'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import { useOnlineTimeStore } from '@/stores'
import { storeToRefs } from 'pinia'
dayjs.extend(utc)
const onlineTimeStore = useOnlineTimeStore()
const { showOnlineTime } = storeToRefs(onlineTimeStore)
const { height } = useWindowSize()
const CONTAINER_HEIGHT = 170,
GAP = 30
// CONTAINER_WIDTH = 130
const maxSeconds = 60 * 30 // 半小时
const x = GAP
const y = height.value - CONTAINER_HEIGHT - GAP
const currentSeconds = ref(0)
// 进度条的比例
const widthRate = computed(() => {
if (currentSeconds.value >= maxSeconds) {
return 100
} else {
return (currentSeconds.value / maxSeconds) * 100
}
})
// 在线时长格式化 将秒级 格式化为 00:00
// 如果大于一个小时的话,则显示小时:分钟:秒
const formatSeconds = computed(() => {
if (currentSeconds.value >= 60 * 60) {
return dayjs.utc(currentSeconds.value * 1000).format('hh:mm:ss')
......
......@@ -5,3 +5,4 @@ export * from './interview'
export * from './video'
export * from './question'
export * from './yabi'
export * from './onlineTime'
import { defineStore } from 'pinia'
export const useOnlineTimeStore = defineStore('onlineTime', () => {
const showOnlineTime = ref(true)
return {
showOnlineTime,
}
})
......@@ -47,7 +47,7 @@ export const useUserStore = defineStore('user', () => {
const { data } = await refreshTokenApi(refreshToken.value)
setUserInfoAndToken(data)
}
const setUserInfoAndToken = async (data: LoginResponseDto) => {
const setUserInfoAndToken = (data: LoginResponseDto) => {
setUserInfo(data)
setToken(data.token)
setRefreshToken(data.refreshToken)
......
import { app_config } from '@/config'
import Axois from './axios'
import Axios from './axios'
// 'http://192.168.2.168:8089'
const baseUrl = app_config[import.meta.env.MODE]?.baseUrl
console.log('baseUrl', baseUrl)
export default new Axois({
export default new Axios({
baseURL: baseUrl,
timeout: 1000 * 60,
})
......@@ -13,6 +13,15 @@
/>
</el-form-item>
<el-form-item label="自动关闭" prop="autoCloseWeekend">
<!-- 加一个tips -->
<template #label>
<div class="flex items-center gap-1">
<span>自动关闭</span>
<el-tooltip content="如果开启,则会在周末自动关闭抽奖" placement="top">
<el-icon size="16"><IEpInfoFilled /></el-icon>
</el-tooltip>
</div>
</template>
<el-switch
v-model="form.autoCloseWeekend"
:active-value="1"
......
......@@ -5,27 +5,30 @@
<div
v-for="item in list"
:key="item.id"
class="relative group bg-white rounded-lg p-6 cursor-pointer transition-all duration-300 hover:shadow-lg hover:shadow-gray-100 hover:-translate-y-1 border border-gray-100 hover:border-gray-200 mb-3 sm:mb-4"
class="relative group bg-white rounded-lg p-4 cursor-pointer transition-all duration-300 hover:shadow-lg hover:shadow-gray-100 hover:-translate-y-1 border border-gray-100 hover:border-gray-200 mb-3 sm:mb-4"
@click="jumpToArticleDetailPage({ type: item.type, id: item.id })"
>
<div class="flex gap-3 justify-between">
<!-- 推荐标签 -->
<div v-if="item.isRecommend" class="absolute left-0 top-5">
<div
v-if="item.isRecommend"
class="absolute top-0 left-0 w-13 h-6 bg-#FFF9B9 flex items-center justify-center border-2px border-solid border-#f4f0eb rounded-tl-lg rounded-br-lg"
class="h-5.5 pl-2 pr-2.5 flex items-center gap-1 bg-gradient-to-r from-#6366f1 to-#818cf8 rounded-r-full text-white text-11px font-medium shadow-sm"
>
<img class="w-6" src="@/assets/img/culture/recommend.png" alt="" />
<div class="text-12px text-#000 line-height-12px">推荐</div>
<svg viewBox="0 0 16 16" class="w-3 h-3 fill-current">
<path d="M8 1.3l2 4.1 4.5.7-3.2 3.1.8 4.5L8 11.5l-4.1 2.2.8-4.5L1.5 6.1l4.5-.7z" />
</svg>
推荐
</div>
<!-- 内容区域 -->
</div>
<div class="flex gap-3 justify-between">
<div class="flex-1 min-w-0 flex flex-col justify-between h-24">
<!-- 标题 -->
<h2
class="text-xl font-semibold text-gray-900 line-clamp-1 group-hover:text-blue-600 transition-colors duration-200 leading-tight"
:class="{ 'pl-12': item.isRecommend }"
>
{{ item.title }}
</h2>
<!-- 内容摘要 -->
<div v-if="!item.content?.includes('</')" class="my-2 space-y-1">
<p
class="text-gray-600 text-sm sm:text-base leading-relaxed line-clamp-1 break-all"
......@@ -34,14 +37,11 @@
</p>
</div>
<!-- 统计信息 -->
<div class="flex items-center gap-2 xl:gap-4 text-gray-500 text-xs lg:text-sm">
<!-- 发布人名称和头像 -->
<div class="flex items-center gap-2">
<el-avatar :size="24" :src="item.showAvatar" />
<span class="text-sm text-gray-500">{{ item.showName }}</span>
</div>
<!-- 时间 -->
<span class="text-gray-500 font-medium ml-0">
<span class="hidden sm:inline">{{
dayjs(item.createTime * 1000).format('YYYY-MM-DD HH:mm')
......@@ -50,7 +50,6 @@
<span class="text-sm text-gray-500">{{
articleTypeListOptions.find((i) => i.value === item.type)?.label
}}</span>
<!-- 分隔符 -->
<div class="hidden sm:block w-1 h-1 bg-gray-300 rounded-full"></div>
<div class="flex items-center gap-1 hover:text-blue-500 transition-colors">
<el-icon class="text-sm"><IEpView /></el-icon>
......@@ -71,7 +70,6 @@
</div>
</div>
<!-- 图片区域 -->
<div v-show="item.faceUrl" class="relative flex-shrink-0 w-30 h-20 xl:w-36 xl:h-24">
<el-image
:src="item.faceUrl + '?x-oss-process=image/format,webp'"
......@@ -81,7 +79,6 @@
class="w-full h-full object-cover rounded-lg sm:rounded-lg group-hover:scale-105 transition-transform duration-300"
loading="lazy"
/>
<!-- 图片遮罩效果 -->
<div
class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-5 rounded-lg sm:rounded-lg transition-all duration-300"
></div>
......@@ -94,12 +91,10 @@
<div class="bottom-pagination backdrop-blur-8 border-t border-gray-200">
<div class="max-w-7xl mx-auto py-4">
<div class="flex items-center justify-between">
<!-- 左侧:回到顶部按钮 -->
<div class="left">
<ScrollTopComp />
</div>
<!-- 右侧:分页器 -->
<div class="right">
<div
class="pagination-wrapper bg-white rounded-lg shadow-sm border border-gray-100 p-2"
......
......@@ -5,94 +5,91 @@
<div
v-for="(item, index) in list"
:key="index"
class="bg-white rounded-lg shadow-sm mb-6 overflow-hidden"
class="bg-white rounded-xl shadow-sm mb-6 overflow-hidden border border-gray-100"
:style="{ '--dynamic-color': item.color }"
>
<!-- 专栏标题栏 -->
<div
class="flex items-center justify-between pr-4 pl-4 pt-2 pb-2 bg-green-50 border-b border-green-100"
:style="{ backgroundColor: item.color, '--dynamic-color': item.color }"
class="flex items-center justify-between px-5 py-3"
:style="{ backgroundColor: item.color }"
>
<h3 class="text-lg font-medium text-gray-800 flex items-center">
<span class="w-1 h-5 mr-2 bg-#444"></span>
<h3 class="text-base font-semibold text-gray-800 flex items-center gap-2">
<span class="w-1 h-5 rounded-full bg-gray-700/60"></span>
{{ item.title }}
</h3>
<div
class="flex items-center cursor-pointer"
class="flex items-center cursor-pointer text-13px text-gray-500 hover:text-indigo-500 transition-colors duration-200"
@click="
router.push({
path: `/columnSearchList/${item.id}`,
query: {
columnTitle: item.title,
},
query: { columnTitle: item.title },
})
"
>
<span class="mr-1 text-14px color-#606266">查看更多 >></span>
查看更多 >>
</div>
</div>
<div class="p-4">
<!-- 内容区 -->
<div class="p-5">
<div
v-if="item.yaColumnVoList.length"
class="grid grid-cols-1 grid-cols-2 lg:grid-cols-3 gap-4"
class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5"
>
<div
v-for="i in item.yaColumnVoList"
:key="i.articleId"
class="group cursor-pointer"
class="group cursor-pointer rounded-lg overflow-hidden border border-transparent hover:border-gray-200 hover:shadow-md transition-all duration-300"
@click="jumpToArticleDetailPage({ type: i.type, id: i.articleId })"
>
<div class="relative mb-3 overflow-hidden rounded-lg">
<!-- 封面图 -->
<div class="relative overflow-hidden">
<img
:src="i.faceUrl"
class="w-full aspect-[5/3] object-cover group-hover:scale-105 transition-transform duration-300"
/>
<!-- 推荐角标 -->
<div
v-if="i.isRecommend"
class="absolute top--1 left--1 w-15 h-7 z-1000 bg-#FFF9B9 flex items-center justify-center border-2px border-solid border-#f4f0eb rounded-tl-lg rounded-br-lg"
class="absolute top-0 left-0 w-15 h-7 bg-#FFF9B9 flex items-center justify-center border-2px border-solid border-#f4f0eb rounded-tl-lg rounded-br-lg"
>
<img class="w-6" src="@/assets/img/culture/recommend.png" alt="" />
<div class="text-12px text-#000 line-height-12px">推荐</div>
</div>
<!-- 底部渐变遮罩 -->
<div
class="absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-black/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"
></div>
</div>
<!-- 屏幕变小 标题变小 -->
<div
class="font-medium text-gray-800 mb-2 transition-colors line-clamp-1 text-sm xl:text-lg"
>
{{ i.title }}
</div>
<!-- 因为是富文本 暂时不显示 -->
<!-- <p class="text-sm text-gray-500 mb-3 line-clamp-1">
{{ i.content }}
</p> -->
<div class="flex items-center justify-between text-xs text-gray-500">
<div class="flex items-center gap-2">
<span class="flex items-center">
<el-icon class="mr-1">
<IEpView />
</el-icon>
{{ i.viewCount }}
</span>
<span class="flex items-center">
<el-icon class="mr-1">
<IEpChatDotRound />
</el-icon>
{{ i.replyCount }}
</span>
<span class="flex items-center">
<el-icon class="mr-1">
<IEpStar />
</el-icon>
{{ i.collectCount }}
</span>
<!-- 文字信息 -->
<div class="p-3">
<div
class="font-semibold text-gray-800 mb-2.5 line-clamp-1 text-base group-hover:text-indigo-600 transition-colors duration-200"
>
{{ i.title }}
</div>
<span>
{{
<div class="flex items-center justify-between text-xs text-gray-500">
<div class="flex items-center gap-3">
<span class="flex items-center gap-0.5">
<el-icon><IEpView /></el-icon>
{{ i.viewCount }}
</span>
<span class="flex items-center gap-0.5">
<el-icon><IEpChatDotRound /></el-icon>
{{ i.replyCount }}
</span>
<span class="flex items-center gap-0.5">
<el-icon><IEpStar /></el-icon>
{{ i.collectCount }}
</span>
</div>
<span class="text-gray-400">{{
smallerThanXl
? dayjs(i.createTime * 1000).format('YYYY-MM-DD')
: dayjs(i.createTime * 1000).format('YYYY-MM-DD HH:mm:ss')
}}
</span>
}}</span>
</div>
</div>
</div>
</div>
......@@ -151,7 +148,6 @@ import dayjs from 'dayjs'
import { useRouter } from 'vue-router'
import { jumpToArticleDetailPage } from '@/utils'
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
console.log(breakpointsTailwind, 'breakpointsTailwind')
const breakpoints = useBreakpoints(breakpointsTailwind)
const smallerThanXl = breakpoints.smaller('xl')
......
......@@ -5,46 +5,39 @@
<div
v-for="(item, index) in list"
:key="index"
class="bg-white rounded-lg shadow-sm mb-6 overflow-hidden"
class="bg-white rounded-xl shadow-sm mb-6 overflow-hidden border border-gray-100"
:style="{ '--dynamic-color': item.color }"
>
<!-- 专栏标题栏 -->
<div
class="flex items-center justify-between pr-4 pl-4 pt-2 pb-2 bg-green-50 border-b border-green-100"
:style="{ backgroundColor: item.color, '--dynamic-color': item.color }"
class="flex items-center justify-between px-5 py-3"
:style="{ backgroundColor: item.color }"
>
<h3 class="text-lg font-medium text-gray-800 flex items-center">
<span class="w-1 h-5 mr-2 bg-#444"></span>
<h3 class="text-base font-semibold text-gray-800 flex items-center gap-2">
<span class="w-1 h-5 rounded-full bg-gray-700/60"></span>
{{ item.title }}
</h3>
<div
class="flex items-center cursor-pointer hover:text-[var(--dynamic-color)]"
@click="
router.push({
path: '/searchPage',
query: {
type: ArticleTypeEnum.INTERVIEW,
},
})
"
></div>
</div>
<div class="p-4">
<!-- 内容区 -->
<div class="p-5">
<div
v-if="item.yaColumnVoList.length"
class="grid grid-cols-1 grid-cols-2 lg:grid-cols-3 gap-4"
class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5"
>
<div
v-for="i in item.yaColumnVoList"
:key="i.articleId"
class="group cursor-pointer"
class="group cursor-pointer rounded-lg overflow-hidden border border-transparent hover:border-gray-200 hover:shadow-md transition-all duration-300"
@click="jumpToArticleDetailPage({ type: i.type, id: i.articleId })"
>
<div class="relative mb-3 overflow-hidden rounded-lg">
<!-- 封面图 -->
<div class="relative overflow-hidden">
<img
:src="i.faceUrl"
class="w-full aspect-[5/3] object-cover group-hover:scale-105 transition-transform duration-300"
/>
<!-- 推荐角标 -->
<div
v-if="i.isRecommend"
class="absolute top-0 left-0 w-15 h-7 bg-#FFF9B9 flex items-center justify-center border-2px border-solid border-#f4f0eb rounded-tl-lg rounded-br-lg"
......@@ -52,42 +45,40 @@
<img class="w-6" src="@/assets/img/culture/recommend.png" alt="" />
<div class="text-12px text-#000 line-height-12px">推荐</div>
</div>
<!-- 底部渐变遮罩 -->
<div
class="absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-black/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"
></div>
</div>
<div
class="font-medium text-gray-800 mb-2 transition-colors line-clamp-1 text-sm xl:text-lg"
>
{{ i.title }}
</div>
<!-- 因为是富文本 暂时不显示 -->
<!-- <p class="text-xs text-gray-500 mb-3 line-clamp-1">
{{ i.content }}
</p> -->
<div class="flex items-center justify-between text-xs text-gray-400">
<div class="flex items-center space-x-4">
<span class="flex items-center">
<el-icon class="mr-1">
<IEpView />
</el-icon>
{{ i.viewCount }}
</span>
<span class="flex items-center">
<el-icon class="mr-1">
<IEpChatDotRound />
</el-icon>
{{ i.replyCount }}
</span>
<span class="flex items-center">
<el-icon class="mr-1">
<IEpStar />
</el-icon>
{{ i.collectCount }}
</span>
<!-- 文字信息 -->
<div class="p-3">
<div
class="font-semibold text-gray-800 mb-2.5 line-clamp-1 text-base group-hover:text-indigo-600 transition-colors duration-200"
>
{{ i.title }}
</div>
<div class="flex items-center justify-between text-xs text-gray-500">
<div class="flex items-center gap-3">
<span class="flex items-center gap-0.5">
<el-icon><IEpView /></el-icon>
{{ i.viewCount }}
</span>
<span class="flex items-center gap-0.5">
<el-icon><IEpChatDotRound /></el-icon>
{{ i.replyCount }}
</span>
<span class="flex items-center gap-0.5">
<el-icon><IEpStar /></el-icon>
{{ i.collectCount }}
</span>
</div>
<span class="text-gray-400">{{
smallerThanXl
? dayjs(i.createTime * 1000).format('YYYY-MM-DD')
: dayjs(i.createTime * 1000).format('YYYY-MM-DD HH:mm:ss')
}}</span>
</div>
<span>{{
smallerThanXl
? dayjs(i.createTime * 1000).format('YYYY-MM-DD')
: dayjs(i.createTime * 1000).format('YYYY-MM-DD HH:mm:ss')
}}</span>
</div>
</div>
</div>
......@@ -142,14 +133,12 @@
<script setup lang="ts">
import { getInterviewList } from '@/api'
import { usePageSearch, useScrollTop } from '@/hooks'
import { TABS_REF_KEY, ArticleTypeEnum } from '@/constants'
import { useRouter } from 'vue-router'
import { TABS_REF_KEY } from '@/constants'
import dayjs from 'dayjs'
import { jumpToArticleDetailPage } from '@/utils'
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
const breakpoints = useBreakpoints(breakpointsTailwind)
const smallerThanXl = breakpoints.smaller('xl')
const router = useRouter()
const tabsRef = inject(TABS_REF_KEY)
const { handleBackTop, ScrollTopComp } = useScrollTop(tabsRef!)
......
......@@ -101,9 +101,11 @@
</div>
</div>
<!-- 内容区域 -->
<div class="bg-white rounded-lg shadow-sm">
<div class="bg-gray-50/80 rounded-lg shadow-sm border border-gray-100">
<!-- 最新标题 -->
<div class="flex items-center justify-between p-4 border-b border-gray-100">
<div
class="flex items-center justify-between p-4 bg-white rounded-t-lg border-b border-gray-100"
>
<div class="flex items-center gap-2">
<div class="w-1 h-6 bg-red-500 rounded"></div>
<h2 class="text-lg font-medium">
......@@ -126,66 +128,59 @@
查看更多 >>
</div>
</div>
<div class="divide-y bg-#fff">
<div class="space-y-3 p-4">
<div
@click="jumpToArticleDetailPage({ type: ArticleTypeEnum.PRACTICE, id: item.id })"
v-for="item in list"
:key="item.id"
class="p-4 hover:bg-gray-50 transition-colors cursor-pointer pl-8"
class="group bg-white rounded-lg p-5 cursor-pointer transition-all duration-300 shadow-sm hover:shadow-lg hover:shadow-gray-100 hover:-translate-y-1 border border-gray-100 hover:border-gray-200"
@click="jumpToArticleDetailPage({ type: ArticleTypeEnum.PRACTICE, id: item.id })"
>
<div class="flex gap-3 items-center h-100%" style="border-bottom: 1.5px solid #ddd">
<!-- 左侧内容 -->
<div class="flex-1">
<h1 class="font-medium text-gray-800 mb-2 leading-relaxed line-clamp-1 text-lg">
<div class="flex gap-4 justify-between">
<div class="flex-1 min-w-0">
<h2
class="text-lg font-semibold text-gray-900 line-clamp-1 group-hover:text-blue-600 transition-colors duration-200 mb-2"
>
{{ item.title }}
</h1>
</h2>
<!-- 带图片的内容 -->
<div class="flex gap-3 mb-2 align-center">
<img
<div class="flex gap-3 mb-3">
<el-image
v-if="item.faceUrl"
:src="item.faceUrl"
:alt="item.title"
class="w-40 h-25 object-cover rounded-lg flex-shrink-0"
fit="cover"
:lazy="true"
class="w-36 h-22 rounded-lg flex-shrink-0 object-cover group-hover:scale-105 transition-transform duration-300 overflow-hidden"
loading="lazy"
/>
<div class="flex-1 mr-4">
<div class="text-gray-500 text-base leading-relaxed line-clamp-3">
{{ item.content }}
</div>
</div>
<p class="text-gray-500 text-sm leading-relaxed line-clamp-3 flex-1">
{{ item.content }}
</p>
</div>
<!-- 互动数据 -->
<div class="flex items-center gap-5 text-gray-400 text-sm mb-2">
<div class="flex items-center gap-1">
<el-icon class="text-sm">
<IEpView />
</el-icon>
<span>{{ item.viewCount }}</span>
<div class="flex items-center gap-2 xl:gap-4 text-gray-500 text-xs lg:text-sm">
<div class="flex items-center gap-2">
<el-avatar :size="22" :src="item.showAvatar" />
<span class="text-sm text-gray-500">{{ item.showName }}</span>
</div>
<div class="flex items-center gap-1">
<el-icon class="text-sm">
<IEpChatDotRound />
</el-icon>
<span>{{ item.replyCount }}</span>
<span class="hidden sm:inline text-gray-400">
{{ dayjs(item.createTime * 1000).format('YYYY-MM-DD HH:mm') }}
</span>
<div class="hidden sm:block w-1 h-1 bg-gray-300 rounded-full"></div>
<div class="flex items-center gap-1 hover:text-blue-500 transition-colors">
<el-icon class="text-sm"><IEpView /></el-icon>
<span class="font-medium">{{ item.viewCount }}</span>
</div>
<div class="flex items-center gap-1 mr-2">
<el-icon class="text-sm">
<IEpStar />
</el-icon>
<span>{{ item.praiseCount }}</span>
<div class="flex items-center gap-1 hover:text-red-500 transition-colors">
<el-icon class="text-sm"><IEpChatDotRound /></el-icon>
<span class="font-medium">{{ item.replyCount }}</span>
</div>
<div class="flex items-center gap-1 hover:text-yellow-500 transition-colors">
<el-icon class="text-sm"><IEpStar /></el-icon>
<span class="font-medium">{{ item.praiseCount }}</span>
</div>
<div>{{ dayjs(item.createTime * 1000).format('YYYY-MM-DD HH:mm') }}</div>
</div>
</div>
<!-- 加一个el-divider -->
<el-divider class="my-1! h-100%!" direction="vertical" />
<!-- 右侧头像 -->
<div class="flex flex-col items-center gap-1 flex-shrink-0 pr-8">
<el-avatar :size="45" :src="item.showAvatar" />
<div class="text-xs text-gray-600 mt-2">{{ item.showName }}</div>
</div>
</div>
</div>
</div>
......
......@@ -17,43 +17,35 @@ export default function ExchangeContent(
{ item, modelValue }: ExchangeContentProps,
context: SetupContext<ExchangeContentEvents>,
) {
const totalPrice = computed(() => item.price * (modelValue.num || 1))
return (
<div class="exchange-content py-6 px-4">
{/* 商品图片区域 */}
<div class="flex justify-center mb-8">
<div class="relative">
<div class="w-32 h-32 bg-gradient-to-br from-orange-100 to-pink-100 rounded-3xl flex items-center justify-center shadow-lg">
<div class="w-20 h-20 bg-white rounded-lg flex items-center justify-center shadow-sm">
<img src={item.imageUrl} alt={item.name} class="w-16 h-16 object-contain" />
</div>
<div class="py-4">
{/* 商品信息卡片 */}
<div class="flex gap-4 p-4 bg-gray-50 rounded-xl mb-5">
<div class="w-24 h-24 bg-white rounded-xl overflow-hidden shadow-sm shrink-0
flex items-center justify-center p-2">
<img src={item.imageUrl} alt={item.name}
class="max-w-full max-h-full object-contain rounded-lg" />
</div>
<div class="flex flex-col justify-center min-w-0 gap-1.5">
<div class="text-base font-semibold text-gray-800 line-clamp-2">{item.name}</div>
<div class="flex items-baseline gap-1">
<span class="text-xs text-orange-400">¥</span>
<span class="text-2xl font-bold text-orange-500">{item.price}</span>
<span class="text-xs text-gray-400 ml-1">积分/件</span>
</div>
<div class="absolute -top-2 -right-2 w-7 h-7 bg-blue-500 rounded-full flex items-center justify-center shadow-md">
<span class="text-white text-sm font-medium">{item.stock}</span>
<div class="text-12px text-gray-400">
剩余库存:<span class="text-gray-600 font-medium">{item.stock}</span>
</div>
</div>
</div>
{/* 商品信息 */}
<div class="space-y-3 mb-8">
<div class="flex items-center gap-3 px-4">
<div class="w-1.5 h-1.5 bg-gray-400 rounded-full flex-shrink-0"></div>
<span class="text-gray-600 text-sm min-w-12">名称:</span>
<span class="font-medium text-gray-900 flex-1">{item.name}</span>
</div>
<div class="flex items-center gap-3 px-4">
<div class="w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0"></div>
<span class="text-gray-600 text-sm min-w-12">积分:</span>
<span class="font-semibold text-orange-500 text-lg">{item.price}YA币</span>
</div>
</div>
{/* 办公点选择和数量 */}
{/* 表单区域 */}
{item.itemType === ShopGoodsTypeEnum.REAL_GOODS && (
<div class=" rounded-lg px-5 mx-2 space-y-4">
{/* 办公点选择 */}
<div class="space-y-4 mb-5">
<div>
<label class="text-gray-700 text-sm font-medium mb-2 block">办公点</label>
<label class="text-13px text-gray-600 font-medium mb-2 block">配送办公点</label>
<el-select
modelValue={modelValue.deliveryInfo}
onUpdate:modelValue={(value: string) =>
......@@ -68,9 +60,8 @@ export default function ExchangeContent(
</el-select>
</div>
{/* 数量选择 */}
<div>
<label class="text-gray-700 text-sm font-medium mb-2 block">选择数量</label>
<label class="text-13px text-gray-600 font-medium mb-2 block">兑换数量</label>
<el-input-number
min={1}
max={item.stock}
......@@ -78,12 +69,29 @@ export default function ExchangeContent(
onUpdate:modelValue={(value: number) =>
context.emit('update:modelValue', { ...modelValue, num: value })
}
class="w-full"
class="w-full!"
controls-position="right"
/>
</div>
</div>
)}
{/* 费用汇总 */}
<div class="border-t border-dashed border-gray-200 pt-4 mt-4">
<div class="flex items-center justify-between">
<span class="text-sm text-gray-500">
合计扣除
{modelValue.num > 1 && (
<span class="text-gray-400 text-xs ml-1">({item.price} × {modelValue.num})</span>
)}
</span>
<div class="flex items-baseline gap-0.5">
<span class="text-sm text-orange-400">¥</span>
<span class="text-2xl font-bold text-orange-500">{totalPrice.value}</span>
<span class="text-xs text-gray-400 ml-1">积分</span>
</div>
</div>
</div>
</div>
)
}
......
......@@ -15,30 +15,40 @@
</div>
<template v-if="virtualGoodsList.length">
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4">
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
<div
v-for="item in virtualGoodsList"
:key="item.id"
class="group bg-white rounded-lg p-4 flex flex-col items-center shadow-md transition-all duration-200 cursor-pointer border border-gray-200 hover:border-blue-300"
class="goods-card group relative bg-white rounded-xl overflow-hidden cursor-pointer border border-gray-200/80 shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all duration-300"
@click="onExchangeGoods(item)"
>
<div
class="w-20 h-20 mb-3 flex items-center justify-center bg-blue-50/50 rounded-lg p-2"
class="aspect-square bg-gradient-to-br from-gray-50 to-gray-100/80 overflow-hidden p-5 flex items-center justify-center"
>
<img
:src="item.imageUrl"
alt=""
class="rounded-lg w-full h-full object-contain"
class="max-w-full max-h-full object-contain rounded-lg transition-transform duration-300 group-hover:scale-110"
/>
</div>
<div
class="text-sm text-gray-700 mb-3 font-medium text-center line-clamp-2 min-h-[40px]"
>
{{ item.name }}
</div>
<div class="bg-blue-600 text-white text-xs px-4 py-1.5 rounded font-medium">
{{ item.price }} 积分
<div class="p-3.5 border-t border-gray-100">
<div
class="text-13px text-gray-800 font-medium line-clamp-2 min-h-[36px] leading-snug"
>
{{ item.name }}
</div>
<div class="mt-2.5 flex items-center justify-between">
<div class="flex items-baseline gap-0.5">
<span class="text-xs text-orange-400 font-medium">¥</span>
<span class="text-xl font-bold text-orange-500">{{ item.price }}</span>
<span class="text-11px text-gray-400 ml-0.5">积分</span>
</div>
<span class="text-11px text-gray-300">库存 {{ item.stock }}</span>
</div>
</div>
<div
class="absolute inset-x-0 bottom-0 h-0.75 bg-gradient-to-r from-indigo-400 to-purple-400 scale-x-0 group-hover:scale-x-100 transition-transform duration-300 origin-left"
></div>
</div>
</div>
......@@ -91,30 +101,40 @@
</div>
<template v-if="realGoodsList.length">
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4">
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
<div
v-for="item in realGoodsList"
:key="item.id"
class="group bg-white rounded-lg p-4 flex flex-col items-center shadow-md transition-all duration-200 cursor-pointer border border-gray-200 hover:border-blue-300"
class="goods-card group relative bg-white rounded-xl overflow-hidden cursor-pointer border border-gray-200/80 shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all duration-300"
@click="onExchangeGoods(item)"
>
<div
class="w-20 h-20 mb-3 flex items-center justify-center bg-blue-50/50 rounded-lg p-2"
class="aspect-square bg-gradient-to-br from-gray-50 to-gray-100/80 overflow-hidden p-5 flex items-center justify-center"
>
<img
:src="item.imageUrl"
alt=""
class="rounded-lg w-full h-full object-contain"
class="max-w-full max-h-full object-contain rounded-lg transition-transform duration-300 group-hover:scale-110"
/>
</div>
<div
class="text-sm text-gray-700 mb-3 font-medium text-center line-clamp-2 min-h-[40px]"
>
{{ item.name }}
</div>
<div class="bg-blue-600 text-white text-xs px-4 py-1.5 rounded font-medium">
{{ item.price }} 积分
<div class="p-3.5 border-t border-gray-100">
<div
class="text-13px text-gray-800 font-medium line-clamp-2 min-h-[36px] leading-snug"
>
{{ item.name }}
</div>
<div class="mt-2.5 flex items-center justify-between">
<div class="flex items-baseline gap-0.5">
<span class="text-xs text-orange-400 font-medium">¥</span>
<span class="text-xl font-bold text-orange-500">{{ item.price }}</span>
<span class="text-11px text-gray-400 ml-0.5">积分</span>
</div>
<span class="text-11px text-gray-300">库存 {{ item.stock }}</span>
</div>
</div>
<div
class="absolute inset-x-0 bottom-0 h-0.75 bg-gradient-to-r from-indigo-400 to-purple-400 scale-x-0 group-hover:scale-x-100 transition-transform duration-300 origin-left"
></div>
</div>
</div>
......@@ -287,17 +307,14 @@ const onExchangeGoods = async (item: BackendShopItemDto) => {
await ElMessageBox({
title: 'YA币兑换',
customStyle: {
width: '1000px',
},
customClass: 'exchange-dialog',
message: () => (
// @ts-ignore
<ExchangeContent item={item} v-model={form.value}></ExchangeContent>
),
confirmButtonText: '确认兑换',
cancelButtonText: '取消',
cancelButtonText: '再想想',
showCancelButton: true,
center: true,
beforeClose: async (action, instance, done) => {
if (action === 'cancel') return done()
if (yabiData.value.currentValue < item.price * form.value.num)
......@@ -320,3 +337,44 @@ const onExchangeGoods = async (item: BackendShopItemDto) => {
})
}
</script>
<style>
.exchange-dialog {
border-radius: 16px !important;
width: 320px !important;
overflow: hidden;
}
.exchange-dialog .el-message-box__header {
padding: 20px 24px 0;
}
.exchange-dialog .el-message-box__title {
font-size: 16px;
font-weight: 600;
}
.exchange-dialog .el-message-box__content {
padding: 8px 24px;
}
.exchange-dialog .el-message-box__btns {
padding: 8px 24px 20px;
}
.exchange-dialog .el-message-box__btns .el-button--primary {
background: linear-gradient(to right, #6366f1, #8b5cf6);
border: none;
border-radius: 8px;
padding: 8px 28px;
}
.exchange-dialog .el-message-box__btns .el-button--primary:hover {
opacity: 0.9;
}
.exchange-dialog .el-message-box__btns .el-button:not(.el-button--primary) {
border-radius: 8px;
padding: 8px 28px;
}
</style>
......@@ -12,6 +12,9 @@
@click="handleClearCache"
>清除缓存</el-button
>
<el-button type="info" plain size="small" @click="showOnlineTime = !showOnlineTime"
>展示/隐藏在线时长</el-button
>
<el-button
v-if="officialAccountList.length"
type="info"
......@@ -153,7 +156,6 @@
</template>
<script lang="tsx" setup>
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'
import EditUserInfo from './components/editUserInfo.vue'
import { generateLoginKey, hasOfficialAccount } from '@/api'
......@@ -162,6 +164,9 @@ import { wxLogin } from '@/utils/wxUtil'
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'
import type { TabPaneName } from 'element-plus'
import { IS_REAL_KEY } from '@/constants/symbolKey'
import { useOnlineTimeStore, useUserStore } from '@/stores'
const { showOnlineTime } = storeToRefs(useOnlineTimeStore())
const router = useRouter()
const route = useRoute()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment