Compare commits

...

11 Commits

Author SHA1 Message Date
scout
8b144a270c Merge branch 'qb' of https://gitea-inner.fontree.cn/scout666/liveh5-nuxt 2025-01-23 14:15:24 +08:00
xingyy
f80f9c1651 feat(styles): 初始化默认主题样式并支持暗黑模式
- 新增 default-theme.css 文件,定义基础样式和暗黑模式样式
-移除 index.css 文件中的重复样式
- 在 nuxt.config.js 中引入 default-theme.css
- 优化了颜色变量和布局样式
2025-01-23 14:10:15 +08:00
xingyy
e541d0b21d refactor(app): 重构应用配置和样式
- 移除 colorMode 相关代码
- 删除全局样式文件
- 更新 nuxt 配置:
  - 添加 runtimeConfig
  - 更新 css 配置
  - 优化 vite构建配置
  - 新增 image 模块配置
- 更新路由配置
- 调整组件实现
- 更新环境变量加载方式
2025-01-23 14:04:34 +08:00
xingyy
3b8bd623c0 refactor(app): 重构应用配置和样式
- 移除 colorMode 相关代码
- 删除全局样式文件
- 更新 nuxt 配置:
  - 添加 runtimeConfig
  - 更新 css 配置
  - 优化 vite构建配置
  - 新增 image 模块配置
- 更新路由配置
- 调整组件实现
- 更新环境变量加载方式
2025-01-23 13:56:18 +08:00
xingyy
fb8a72b47a feat(ItemList): 优化下拉刷新组件
- 添加刷新成功状态显示
- 设置刷新成功状态持续时间为 700ms
- 在刷新成功时显示图标和提示文本
2025-01-23 12:05:13 +08:00
xingyy
a423a7f801 feat(home): 重构首页布局并添加瀑布流布局
- 移除 Column 组件,使用 vue-masonry-wall 实现瀑布流布局- 更新 ItemList 组件,集成瀑布流布局和新的详情弹窗
- 修改 DetailPopup 组件,使用新的详情信息结构
- 更新 itemDetail 组件,适配新的详情信息数据
- 在项目中添加 vue-masonry-wall 依赖
2025-01-23 11:08:54 +08:00
xingyy
e6fdd0354a perf(home): 为 Column 组件中的图片添加懒加载
- 在 Column 组件的图片标签中添加 loading="lazy" 属性
- 这个修改可以提高页面加载性能,实现图片的懒加载
2025-01-22 16:59:00 +08:00
xingyy
3225d91ecb refactor(components): 重构弹窗组件和详情页展示逻辑
- 修改实名认证详情页布局- 优化 item 详情展示方式
- 调整弹窗组件样式
- 重命名 ItemDetailSheet 为 DetailPopup
- 更新相关组件引用
2025-01-22 16:56:44 +08:00
xingyy
a36a98c576 style(x-image): 添加 object-fit: cover 样式
- 在 x-image 组件的 img 标签中添加 object-fit: cover 样式
- 确保图片在容器中等比例填充,保持图片的宽高比
2025-01-22 16:33:53 +08:00
xingyy
d3cb4d55b4 refactor(components): 优化多个组件的结构和功能
- 为 x-button 和 x-popup 组件添加注释说明
- 在 x-image 组件中添加 lazy 加载属性
- 优化 profile 页面的我的拍品列表展示
- 更新 tang
2025-01-22 16:23:48 +08:00
xingyy
331b4a73b2 refactor(app): 重构 LiveRoom 组件
- 将 LiveRoom 相关组件和文件重命名,统一使用小写开头
- 新增 x-button、x-image 和 x-popup 组件,替代原有 PressableButton 和 ImagePreview
-优化 SideButton 组件,使用新的 x-button 和 tangPopup 组件- 更新 LiveRoom 组件中的引用和使用方式
- 调整 tangPopup 组件,使用 goodStore 替代静态数据
2025-01-22 15:44:50 +08:00
33 changed files with 1081 additions and 516 deletions

View File

@ -29,4 +29,4 @@ export async function userArtworks(data) {
method: 'POST',
data
})
}
}

View File

@ -1,7 +1,6 @@
<script setup>
import { useI18n } from 'vue-i18n'
import {message} from '@/components/x-message/useMessage.js'
// message.success('success')
useHead({
title: useI18n().t('appSetting.appName'),
meta: [
@ -9,9 +8,8 @@ useHead({
{ name: 'keywords', content: useI18n().t('appSetting.appKeyWords') },
],
})
const color = useColorMode()
const mode = computed(() => {
return color.value
return ''
})
//
@ -42,7 +40,7 @@ provide('slideDirection', slideDirection)
</script>
<template>
<VanConfigProvider :theme="mode">
<VanConfigProvider theme="light">
<NuxtLoadingIndicator
color="repeating-linear-gradient(to right,var(--c-primary) 0%,var(--c-primary-active) 100%)" />
<NuxtLayout>

View File

@ -1,9 +1,8 @@
<script setup>
import { useAppFooterRouteNames as names } from '~/config/index.js'
import MyIcon from "~/components/icons/MyIcon.vue";
import HomeIcon from "~/components/icons/HomeIcon.vue";
import { useAppFooterRouteNames as names } from '@/config/index.js'
import MyIcon from "@/components/icons/MyIcon.vue";
import HomeIcon from "@/components/icons/HomeIcon.vue";
const route = useRoute()
const active = ref(0)
const show = computed(() => {
if (route.name && names.includes(route.name))
@ -13,7 +12,6 @@ const show = computed(() => {
const initData=()=>{
active.value=route.path==='/profile'?1:0
}
onMounted(()=>{
initData()
})
@ -21,7 +19,6 @@ onMounted(()=>{
<template>
<div v-if="show">
<van-tabbar v-model="active" route placeholder fixed>
<van-tabbar-item replace to="/">
<span>{{ $t('tabbar.home') }}</span>

View File

@ -1,5 +1,13 @@
<script setup>
import { ImagePreview } from 'vant';
import { showImagePreview } from 'vant';
import xImage from '@/components/x-image/index.vue'
const props = defineProps({
detailInfo: {
type: Object,
default: null
}
})
const images = [
'https://e-cdn.fontree.cn/fonchain-main/prod/file/default/setting/637d95b4-2ae9-4a74-bd60-a3a9d2ca2ca0.png',
'https://e-cdn.fontree.cn/fonchain-main/prod/file/default/setting/f7b65e23-ce21-41b4-8e58-9e6dc6950727.png',
@ -15,7 +23,10 @@ const clickSwipe=(index)=>{
<template>
<div>
<van-swipe style="height: 188px" indicator-color="#B4B4B4" lazy-render >
<div class="flex justify-center">
<xImage class="h-188px" :src="detailInfo?.artwork?.hdPic"></xImage>
</div>
<!-- <van-swipe style="height: 188px" indicator-color="#B4B4B4" lazy-render >
<van-swipe-item v-for="(image,index) in images" :key="image" @click="clickSwipe(index)">
<van-image
fit="contain"
@ -24,25 +35,25 @@ const clickSwipe=(index)=>{
:src="image"
/>
</van-swipe-item>
</van-swipe>
</van-swipe>-->
<div class="px-[16px] bg-[#fff] pt-[11px] mb-6px">
<div class="text-[#000] text-[16px] mb-[12px]">日出而作日落而息</div>
<div class="text-[#000] text-[16px] mb-[12px]">{{detailInfo?.artworkTitle}}</div>
<div class="text-#575757 text-[14px] pb-8px">
<div class="flex mb-[4px]">
<div class="w-[70px]">作者</div>
<div>张天赐</div>
<div>{{detailInfo?.artwork?.artistName??'-'}}</div>
</div>
<div class="flex mb-[4px]">
<div class="w-[70px] flex-shrink-0">总平尺数</div>
<div></div>
<div>{{detailInfo?.artwork?.ruler??'-'}}</div>
</div>
<div class="flex mb-[4px]">
<div class="w-[70px] flex-shrink-0">*</div>
<div>100*200cm</div>
<div>{{detailInfo?.artwork?.length}}*{{detailInfo?.artwork?.width}}cm</div>
</div>
<div class="flex mb-[4px]">
<div class="w-[70px] flex-shrink-0">画作简介</div>
<div>是打卡时间到卡上即可点击卡拉斯科健康就阿斯科利打卡时间到卡萨带手机的啊科技是打卡时</div>
<div>{{detailInfo?.artwork?.abstract??'-'}}</div>
</div>
</div>
</div>

View File

@ -1,4 +1,7 @@
<script setup>
/*
* 此组件的目的是使用该组件包裹的内容具有按压状态效果
* */
import {ref, defineEmits} from "vue";
const emit = defineEmits(["click"]);

View File

@ -0,0 +1,56 @@
<script setup>
import { showImagePreview } from 'vant';
import { NuxtImg } from '#components'
const props = defineProps({
src: {
type: String,
default: ''
},
preview: {
type: Boolean,
default: true
},
//
sizes: {
type: Array,
default: () => [320, 640, 768, 1024]
},
//
format: {
type: String,
default: 'webp'
},
//
quality: {
type: Number,
default: 80
}
})
const showImage = () => {
if (props.preview) {
showImagePreview([props.src]);
}
}
</script>
<template>
<nuxt-img
loading="lazy"
v-bind="{ ...props, ...$attrs }"
style="object-fit: cover"
@click="showImage"
:src="src"
:sizes="sizes"
:format="format"
:quality="quality"
placeholder
/>
</template>
<style scoped>
:deep(img) {
width: 100%;
height: 100%;
}
</style>

View File

@ -1,4 +1,7 @@
<script setup>
/*
* 封装一个带标题栏的弹窗
* */
const props = defineProps({
show: {
type: Boolean,
@ -15,27 +18,32 @@ const close=()=>{
<template>
<van-popup
:show="show"
teleport="#__nuxt"
position="bottom"
:style="{ height: '74%' }"
v-bind="{...$attrs,...$props}"
@close="close"
>
<div class="flex flex-col h-full">
<!-- 固定的标题栏 -->
<div class="flex items-center pl-16px pr-19px h-40px border-b-1px border-b-[#D3D3D3] shrink-0">
<template v-if="$slots.title">
<slot name="title"></slot>
</template>
<template v-else>
<div class="text-#000 text-16px">{{title}}</div>
</template>
<div class="ml-auto flex items-center">
<van-icon size="20" name="cross" color="#939393" @click="close"/>
</div>
<!-- 标题栏 -->
<div class="flex items-center pl-16px pr-19px h-40px border-b-1px border-gray-300 shrink-0 relative w-full">
<slot v-if="$slots.title" name="title">
</slot>
<div v-else class="text-black text-16px text-center flex-grow-1">{{ title }}</div>
<van-icon
style="position: absolute"
class="right-19px"
size="20"
name="cross"
color="#939393"
@click="close"
/>
</div>
<!-- 可滚动的内容区域 -->
<div class="flex-1 overflow-y-auto ">
<slot/>
<!-- 内容区域 -->
<div class="flex-1 px-16px py-18px overflow-hidden relative">
<div class="h-full overflow-y-auto relative">
<slot/>
</div>
</div>
</div>
</van-popup>

View File

@ -1,114 +0,0 @@
<script setup>
import xPopup from '@/components/x-popup/index.vue'
const props = defineProps({
show: {
type: Boolean,
default: false
}
})
</script>
<template>
<x-popup :show="show">
<template #title>
<div class="text-#000 text-16px">拍品列表</div>
<div class="text-#939393 text-16px ml-14px">共188个拍品</div>
</template>
<div class="px-16px py-18px">
<div class="flex mb-21px">
<div class="mr-10px flex-shrink-0 rounded-4px overflow-hidden">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div>
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
张天赐 | 日出而作日落而息撒打算撒打算撒打决赛多久啊是世奥兰日落而息撒打算撒打算撒打决赛多久啊是世奥兰
</div>
<div class="text-14px text-#575757">起拍价RMB 1,000</div>
<div class="text-14px text-#B58047">成交价等待更新</div>
</div>
</div>
<div class="flex mb-21px">
<div class="mr-10px flex-shrink-0 rounded-4px overflow-hidden">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div>
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
张天赐 | 日出而作日落而息撒打算撒打算撒打决赛多久啊是世奥兰日落而息撒打算撒打算撒打决赛多久啊是世奥兰
</div>
<div class="text-14px text-#575757">起拍价RMB 1,000</div>
<div class="text-14px text-#B58047">成交价等待更新</div>
</div>
</div>
<div class="flex mb-21px">
<div class="mr-10px flex-shrink-0 rounded-4px overflow-hidden">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div>
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
张天赐 | 日出而作
</div>
<div class="text-14px text-#575757">起拍价RMB 1,000</div>
<div class="text-14px text-#B58047">成交价等待更新</div>
</div>
</div>
<div class="flex mb-21px">
<div class="mr-10px flex-shrink-0 rounded-4px overflow-hidden">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div>
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
张天赐 | 日出而作
</div>
<div class="text-14px text-#575757">起拍价RMB 1,000</div>
<div class="text-14px text-#B58047">成交价等待更新</div>
</div>
</div>
<div class="flex mb-21px">
<div class="mr-10px flex-shrink-0 rounded-4px overflow-hidden">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div>
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
张天赐 | 日出而作
</div>
<div class="text-14px text-#575757">起拍价RMB 1,000</div>
<div class="text-14px text-#B58047">成交价等待更新</div>
</div>
</div>
<div class="flex mb-21px">
<div class="mr-10px flex-shrink-0 rounded-4px overflow-hidden">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div>
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
张天赐 | 日出而作
</div>
<div class="text-14px text-#575757">起拍价RMB 1,000</div>
<div class="text-14px text-#B58047">成交价等待更新</div>
</div>
</div>
<div class="flex mb-21px">
<div class="mr-10px flex-shrink-0 rounded-4px overflow-hidden">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div>
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
张天赐 | 日出而作
</div>
<div class="text-14px text-#575757">起拍价RMB 1,000</div>
<div class="text-14px text-#B58047">成交价等待更新</div>
</div>
</div>
</div>
</x-popup>
</template>
<style scoped>
</style>

View File

@ -1,53 +0,0 @@
<script setup>
const props = defineProps({
items: Array,
colIndex: {
type: Number,
default: 0
}
});
const emit = defineEmits(['openShow']);
const openShow = (item,index) => {
emit('openShow', item,index);
};
</script>
<template>
<div class="flex flex-1 flex-col gap-[16px]">
<div
v-for="(item, index) in items"
:key="index"
class="w-full"
@click="openShow(item,index)"
>
<div class="relative w-full">
<img
:src="item.artwork?.hdPic"
class="w-full object-cover rounded-4px"
/>
<div
class="absolute left-[8px] top-[8px] h-[17px] w-[45px] flex items-center justify-center bg-[#2b53ac] text-[12px] text-[#fff]"
>
LOT{{ item.index+1 }}
</div>
</div>
<div class="pt-[8px]">
<div class="text-[14px] text-[#000000] leading-[20px]">
{{ item.name }}
</div>
<div class="mt-[4px] text-[12px] text-[#575757]">
起拍价{{ item?.startPrice??0 }}
</div>
<div
v-if="item.soldPrice"
class="mt-[4px] text-[12px] text-[#b58047]"
>
成交价{{ item?.startPrice??0 }}
</div>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,24 @@
<script setup>
import xPopup from '@/components/x-popup/index.vue'
import ItemDetail from "@/components/itemDetail/index.vue";
import {goodStore} from "~/stores/goods/index.js";
const {
artWorkDetail
} = goodStore()
const props = defineProps({
show: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:show'])
const handleClose = () => {
emit('update:show', false)
}
</script>
<template>
<xPopup :show="show" title="拍品详情" @update:show="handleClose">
<ItemDetail :detailInfo="artWorkDetail" />
</xPopup>
</template>

View File

@ -1,96 +0,0 @@
<template>
<van-popup
v-model:show="props.show"
position="bottom"
:style="{ height: props.height }"
round
closeable
@closed="handleClose"
>
<div class="p-[16px] overflow-y-auto h-full">
<!-- 商品基本信息 -->
<div class="flex flex-col gap-[12px]">
<img
:src="props.detail?.artwork?.hdPic"
class="w-full rounded-[4px] object-cover"
:alt="props.detail?.name"
/>
<div class="text-[16px] font-medium text-[#000]">
{{ props.detail?.name }}
</div>
<!-- 价格信息 -->
<div class="flex flex-col gap-[8px]">
<div class="text-[14px] text-[#575757]">
起拍价{{ formatPrice(props.detail?.startPrice) }}
</div>
<div v-if="props.detail?.soldPrice" class="text-[14px] text-[#b58047]">
成交价{{ formatPrice(props.detail?.soldPrice) }}
</div>
</div>
<!-- 商品详情 -->
<div class="mt-[16px]">
<div class="text-[16px] font-medium mb-[12px]">商品详情</div>
<div class="flex flex-col gap-[8px]">
<div class="flex justify-between text-[14px]">
<span class="text-[#575757]">尺寸</span>
<span>{{ props.detail?.artwork?.size || '-' }}</span>
</div>
<div class="flex justify-between text-[14px]">
<span class="text-[#575757]">年代</span>
<span>{{ props.detail?.artwork?.year || '-' }}</span>
</div>
<div class="flex justify-between text-[14px]">
<span class="text-[#575757]">材质</span>
<span>{{ props.detail?.artwork?.material || '-' }}</span>
</div>
</div>
</div>
<!-- 作品描述 -->
<div v-if="props.detail?.artwork?.description" class="mt-[16px]">
<div class="text-[16px] font-medium mb-[12px]">作品描述</div>
<div class="text-[14px] text-[#575757] whitespace-pre-wrap">
{{ props.detail?.artwork?.description }}
</div>
</div>
</div>
</div>
</van-popup>
</template>
<script setup>
import { formatPrice } from '~/utils/format'
const props = defineProps({
show: {
type: Boolean,
default: false
},
height: {
type: String,
default: '90vh'
},
detail: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['update:show'])
const handleClose = () => {
emit('update:show', false)
}
</script>
<style scoped>
:deep(.van-popup) {
max-height: 90vh;
}
:deep(.van-popup__close-icon) {
color: #000;
}
</style>

View File

@ -2,9 +2,8 @@
import { ref, computed } from 'vue'
import { useRect } from "@vant/use"
import { goodStore } from "@/stores/goods"
import Column from "../Column/index.vue"
import ItemDetail from "@/components/itemDetail/index.vue"
import DetailPopup from '../DetailPopup/index.vue'
import MasonryWall from '@yeger/vue-masonry-wall'
const {
itemList,
pageRef,
@ -23,16 +22,6 @@ const localState = ref({
showDetail: false,
showHeight: ''
})
//
const columns = computed(() => {
const result = [[], []]
itemList.value.forEach((item, index) => {
result[index % 2].push({ ...item, index })
})
return result
})
//
const loadMore = async () => {
pageRef.value.page++
@ -51,18 +40,11 @@ const onRefresh = async () => {
localState.value.refreshing = false
}
}
//
const openShow = async (item) => {
localState.value.showDetail = true
currentItem.value = item
await getArtworkDetail(item.uuid)
const rect = useRect(liveRef.value.$el)
localState.value.showHeight = rect.height
nextTick(() => {
localState.value.showDetail = true
})
getArtworkDetail(item.uuid)
}
</script>
@ -71,8 +53,12 @@ const openShow = async (item) => {
<van-pull-refresh
v-model="localState.refreshing"
success-text="刷新成功"
:success-duration="700"
@refresh="onRefresh"
>
<template #success>
<van-icon name="success" /> 刷新成功
</template>
<van-list
v-model:loading="storeLoading"
:finished="localState.finished"
@ -80,30 +66,45 @@ const openShow = async (item) => {
@load="loadMore"
>
<div class="w-full flex gap-[16px]">
<Column
v-for="(column, colIndex) in columns"
:key="colIndex"
:colIndex="colIndex"
:items="column"
@openShow="openShow"
/>
<masonry-wall :items="itemList" :ssr-columns="2" :maxColumns="2" :minColumns="2" :gap="5">
<template #default="{ item, index }">
<div
@click="openShow(item)"
class="w-full"
>
<div class="relative w-full">
<img
:src="item.artwork?.hdPic"
class="w-full object-cover rounded-4px"
loading="lazy"
/>
<div
class="absolute rounded-2px overflow-hidden line-height-12px left-[8px] top-[8px] h-[17px] w-[45px] flex items-center justify-center bg-[#2b53ac] text-[12px] text-[#fff]"
>
LOT{{ index+1 }}
</div>
</div>
<div class="pt-[8px]">
<div class="text-[14px] text-[#000000] leading-[20px]">
{{ item.name }}
</div>
<div class="mt-[4px] text-[12px] text-[#575757]">
起拍价{{ item?.startPrice??0 }}
</div>
<div
v-if="item.soldPrice"
class="mt-[4px] text-[12px] text-[#b58047]"
>
成交价{{ item?.startPrice??0 }}
</div>
</div>
</div>
</template>
</masonry-wall>
</div>
</van-list>
</van-pull-refresh>
<van-action-sheet
teleport="#__nuxt"
:round="false"
v-model:show="localState.showDetail"
title="拍品详情"
>
<div
class="content bg-[#F0F0F0]"
:style="`height: calc(100vh - ${localState.showHeight + 85}px)`"
>
<ItemDetail />
</div>
</van-action-sheet>
<DetailPopup v-model:show="localState.showDetail"></DetailPopup>
</div>
</template>

View File

@ -1,5 +1,5 @@
<script setup>
import LiveRoom from '@/pages/LiveRoom/index.client.vue';
import liveRoom from '@/pages/liveRoom/index.client.vue';
import {goodStore} from "@/stores/goods/index.js";
import ItemList from './components/ItemList/index.vue'
import Cescribe from './components/Cescribe/index.vue'
@ -10,19 +10,19 @@ import {ref} from "vue";
const {fullLive, getAuctionDetail, auctionDetail, itemList, pageRef, liveRef} = goodStore();
definePageMeta({
layout: 'default',
title: '主页',
i18n: 'menu.home',
})
const changeLive = () => {
fullLive.value = true;
};
const showBottom = ref(true)
</script>
<template>
<div class="flex-grow-1">
<client-only>
<LiveRoom @click="changeLive" :class="['changeLive', fullLive ? 'expanded' : 'collapsed']" ref="liveRef"
<liveRoom @click="changeLive" :class="['changeLive', fullLive ? 'expanded' : 'collapsed']" ref="liveRef"
:fullLive="fullLive"/>
</client-only>
<div v-show="!fullLive" class="bg-#fff">
@ -72,7 +72,7 @@ const showBottom = ref(true)
.changeLive {
width: 100%;
overflow: hidden;
transition: height 0.5s ease, transform 0.5s ease;
transition: height 0.4s ease, transform 0.4s ease;
}
.changeLive.collapsed {

7
app/pages/index.vue Normal file
View File

@ -0,0 +1,7 @@
<script setup>
import Home from './home/index.vue'
</script>
<template>
<Home/>
</template>

View File

@ -1,24 +1,31 @@
<script setup>
import { ref } from "vue";
import lockClosed from "~/static/images/lockdfd@2x.png";
import lockOpen from "~/static/images/lock4@2x.png";
import { liveStore } from "~/stores/live/index.js";
import PressableButton from './PressableButton.vue'
const { quoteStatus, changeStatus,show,show1 } = liveStore();
import lockClosed from "@/static/images/lockdfd@2x.png";
import lockOpen from "@/static/images/lock4@2x.png";
import { liveStore } from "@/stores/live/index.js";
import xButton from '@/components/x-button/index.vue'
import tangPopup from './tangPopup.vue'
import {goodStore} from "~/stores/goods/index.js";
const { quoteStatus, changeStatus,show } = liveStore();
const {pageRef} = goodStore();
const showTang=ref(false)
const openOne=()=>{
showTang.value=true
}
</script>
<template>
<div class="bg-white w-60px rounded-l-4px overflow-hidden">
<!-- 拍品信息 -->
<PressableButton>
<x-button @click="openOne">
<div class="w-60px h-60px text-center border-b border-gray-300 flex flex-col justify-center items-center text-#7D7D7F text-12px">
<div>拍品</div>
<div>(1/188)</div>
<div>(1/{{pageRef.itemCount??0}})</div>
</div>
</PressableButton>
</x-button>
<tangPopup v-model:show="showTang"></tangPopup>
<!-- 出价开关 -->
<PressableButton @click="changeStatus">
<x-button @click="changeStatus">
<div class="w-60px h-60px text-center border-b border-gray-300 flex flex-col justify-center items-center">
<div class="mb-1">
<img
@ -31,15 +38,15 @@ const { quoteStatus, changeStatus,show,show1 } = liveStore();
{{ quoteStatus ? '关闭出价' : '开启出价' }}
</div>
</div>
</PressableButton>
</x-button>
<!-- 支付 -->
<PressableButton @click="show = true">
<div class="w-60px h-60px text-center border-b border-gray-300 flex flex-col justify-center items-center text-yellow-600">
<x-button @click="show = true">
<div class="w-60px h-60px text-center flex flex-col justify-center items-center text-yellow-600">
<div class="text-10px">RMB</div>
<div class="text-12px">5,000</div>
<div class="text-10px">去支付</div>
</div>
</PressableButton>
</x-button>
</div>
</template>

View File

@ -0,0 +1,61 @@
<script setup>
import xPopup from '@/components/x-popup/index.vue'
import {goodStore} from "~/stores/goods/index.js";
import xImage from '@/components/x-image/index.vue'
const {pageRef,itemList} = goodStore();
const props = defineProps({
show: Boolean,
title: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:show'])
const close = () => emit('update:show', false);
</script>
<template>
<x-popup :show="show" @update:show="close">
<template #title>
<div class="text-#000 text-16px">拍品列表</div>
<div class="text-#939393 text-16px ml-14px">{{ pageRef.itemCount }}个拍品</div>
</template>
<div>
<div
v-for="(item,index) of itemList"
:key="item.uuid"
class="flex mb-21px"
>
<div
class="mr-10px flex-shrink-0 rounded-4px overflow-hidden cursor-pointer"
>
<xImage
class="w-80px h-80px"
:src="item.artwork?.hdPic"
:alt="item?.artworkTitle"
loading="lazy"
/>
</div>
<div>
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
{{ item.artworkTitle }}
</div>
<div class="text-14px text-#575757">起拍价RMB 1,000</div>
<div class="text-14px text-#B58047">成交价等待更新</div>
</div>
</div>
</div>
</x-popup>
</template>
<style scoped>
.ellipsis {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -2,13 +2,12 @@
import {ref, onMounted, onBeforeUnmount} from 'vue'
import Aliplayer from 'aliyun-aliplayer'
import 'aliyun-aliplayer/build/skins/default/aliplayer-min.css'
import sideButton from '~/pages/LiveRoom/components/SideButton/index.vue'
import broadcast from '~/pages/LiveRoom/components/Broadcast/index.vue'
import sideButton from '~/pages/liveRoom/components/SideButton/index.vue'
import broadcast from '~/pages/liveRoom/components/Broadcast/index.vue'
import {liveStore} from "~/stores/live/index.js";
import paymentResults from '~/pages/LiveRoom/components/PaymentResults/index.vue'
import paymentInput from '~/pages/LiveRoom/components/PaymentInput/index.vue'
import PressableButton from '~/pages/LiveRoom/components/SideButton/PressableButton.vue'
import paymentResults from '~/pages/liveRoom/components/PaymentResults/index.vue'
import paymentInput from '~/pages/liveRoom/components/PaymentInput/index.vue'
import xButton from '@/components/x-button/index.vue'
const player = ref(null)
const {quoteStatus, changeStatus, show, playerId, show1} = liveStore()
const isPlayerReady = ref(false)
@ -116,12 +115,12 @@ watch(()=>{
下口价RMB
<van-rolling-text class="my-rolling-text1" :start-num="0" :duration="0.5" :target-num="3500" direction="up"/>
</div>
<PressableButton>
<x-button>
<div
:class="`w-344px h-[40px] ${quoteStatus ? 'bg-#FFB25F' : 'bg-#D6D6D8'} rounded-4px ${quoteStatus ? 'text-#fff' : 'text-#7D7D7F'} text-14px flex justify-center items-center mt-10px mb-10px`">
{{ quoteStatus ? '确认出价 RMB 3,000' : '点击"开启出价",即刻参与竞拍 ' }}
</div>
</PressableButton>
</x-button>
<broadcast></broadcast>
</div>
<paymentInput v-model:show="show"/>

View File

@ -158,7 +158,7 @@ const goLogin =async () => {
<div />
</div>
<div class="mt-[55px]">
<van-button :loading="loadingRef.loading1" v-if="phoneNum" :loading-text="$t('login.getCode')" type="primary" block style="height: 48px" @click="getCode">{{ $t('login.getCode')
<van-button :loading="loadingRef.loading1" v-if="phoneNum" :loading-text="$t('login.getCode')" color="#2B53AC" block style="height: 48px" @click="getCode">{{ $t('login.getCode')
}}</van-button>
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">{{ $t('login.getCode')
}}</van-button>

View File

@ -1,17 +1,36 @@
<script setup>
import {userArtworks} from "~/api/goods/index.js";
import {authStore} from "~/stores/auth/index.js";
import xImage from '@/components/x-image/index.vue'
definePageMeta({
layout: 'default',
title: '我的',
i18n: 'menu.profile',
})
const myList=ref([])
const showMyList=ref([])
const {userInfo}= authStore()
const groupAndSortByDate=(data)=> {
if (!Array.isArray(data)) {
return
}
return Object.values(data.reduce((acc, curr) => {
if (!acc[curr.userCreatedAt]) {
acc[curr.userCreatedAt] = {
userCreatedAt: curr.userCreatedAt,
list: []
};
}
acc[curr.userCreatedAt].list.push(curr);
return acc;
}, {}))
.sort((a, b) => new Date(b.userCreatedAt) - new Date(a.userCreatedAt));
}
const initData=async ()=>{
const res=await userArtworks({})
if (res.status===0){
myList.value=res.data.data
showMyList.value=groupAndSortByDate(myList.value)
}
}
initData()
@ -28,55 +47,22 @@ initData()
<div class="text-#575757 text-14px">{{userInfo.telNum}}</div>
</div>
</div>
<div class="flex-grow-1">
<div class="flex-grow-1 ">
<div class="border-b-1px border-b-#D3D3D3 px-16px flex">
<div class="text-#000 text-16px border-b-3 border-b-#2B53AC h-36px">我的拍品</div>
</div>
<van-pull-refresh>
<van-pull-refresh @refresh="initData">
<van-list
finished-text="没有更多了"
>
<div class="px-16px pt-14px">
<div class="text-#575757 text-14px mb-3px">2025.01.12</div>
<div class="flex mb-22px">
<div class="flex-shrink-0 mr-10px">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
<div class="px-16px pt-14px" v-for="(item,index) of showMyList">
<div class="text-#575757 text-14px mb-3px">{{item.userCreatedAt}}</div>
<div class="flex mb-22px" v-for="(item1,index1) of item.list">
<div class="flex-shrink-0 mr-10px rounded-4px overflow-hidden">
<x-image class="w-80px h-80px" :src="item1?.auctionArtworkInfo?.artwork?.hdPic" alt=""/>
</div>
<div class="flex flex-col justify-between">
<div class="text-#000 text-16px ellipsis line-height-21px">张天赐 | 日出而作日落而息撒打算撒打算撒打决赛多久啊是世奥兰</div>
<div class="text-#575757 text-14px line-height-none ">起拍价RMB 1,000</div>
<div class="text-#B58047 text-14px line-height-none">成交价RMB 10,000</div>
</div>
</div>
<div class="flex">
<div class="flex-shrink-0 mr-10px">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div class="flex flex-col justify-between">
<div class="text-#000 text-16px ellipsis line-height-21px">张天赐 | 日出而作日落而息撒打算撒打算撒打决赛多久啊是世奥兰</div>
<div class="text-#575757 text-14px line-height-none ">起拍价RMB 1,000</div>
<div class="text-[#fdc181ff] line-height-none">成交价RMB 10,000</div>
</div>
</div>
</div>
<div class="px-16px pt-14px">
<div class="text-#575757 text-14px mb-3px">2025.01.12</div>
<div class="flex mb-22px">
<div class="flex-shrink-0 mr-10px">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div class="flex flex-col justify-between">
<div class="text-#000 text-16px ellipsis line-height-21px">张天赐 | 日出而作日落而息撒打算撒打算撒打决赛多久啊是世奥兰</div>
<div class="text-#575757 text-14px line-height-none ">起拍价RMB 1,000</div>
<div class="text-#B58047 text-14px line-height-none">成交价RMB 10,000</div>
</div>
</div>
<div class="flex">
<div class="flex-shrink-0 mr-10px">
<img class="w-80px h-80px" src="@/static/images/ddfdcaer.png" alt="">
</div>
<div class="flex flex-col justify-between">
<div class="text-#000 text-16px ellipsis line-height-21px">张天赐 | 日出而作日落而息撒打算撒打算撒打决赛多久啊是世奥兰</div>
<div class="text-#000 text-16px ellipsis line-height-21px">{{item1?.auctionArtworkInfo?.artworkTitle}}</div>
<div class="text-#575757 text-14px line-height-none ">起拍价RMB 1,000</div>
<div class="text-#B58047 text-14px line-height-none">成交价RMB 10,000</div>
</div>

View File

@ -14,7 +14,7 @@ const {userInfo}= authStore()
<template>
<div class="text-#1A1A1A text-16px">
<template v-if="type===0">
<div class="flex mb-20px" >
<div class="flex mb-20px">
<div class="mr-10px">姓名</div>
<div>{{userInfo.realName}}</div>
</div>

View File

@ -0,0 +1,24 @@
<script setup lang="ts">
import MasonryWall from '@yeger/vue-masonry-wall'
const items = [
{
title: 'First',
description: 'The first item.',
},
{
title: 'Second',
description: 'The second item.',
},
]
</script>
<template>
<masonry-wall :items="items" :ssr-columns="2" :minColumns="2" :gap="16">
<template #default="{ item, index }">
<div :style="{ height: `${(index + 1) * 100}px` }">
<h1>{{ item.title }}</h1>
<span>{{ item.description }}</span>
</div>
</template>
</masonry-wall>
</template>

View File

@ -1,3 +1,10 @@
:root:root {
--c-primary: #3554AF;
--c-primary-active: #3554AF;
--van-primary-color: var(--c-primary);
--van-cell-group-inset-padding: 0;
}
#__nuxt {
margin: 0;
padding: 0;

View File

View File

View File

@ -25,7 +25,6 @@ export const goodStore = createGlobalState(() => {
pageRef.value.page = 1
pageRef.value.pageSize=5
pageRef.value.itemCount = 0
itemList.value = []
}
// 获取拍卖详情

View File

@ -1,4 +0,0 @@
:root:root {
--van-primary-color: var(--c-primary);
--van-cell-group-inset-padding: 0;
}

View File

@ -1,4 +0,0 @@
:root {
--c-primary: #3554AF;
--c-primary-active: #3554AF;
}

View File

@ -1,61 +1,58 @@
// 导入环境变量处理库
import dotenv from 'dotenv'
// 导入 Node.js 进程模块
import process from 'node:process'
// 导入预加载工具函数
import preload from './app/utils/preload'
// 导入国际化语言配置
import { currentLocales } from './i18n/i18n'
// 设置环境变量文件路径,默认使用 .env.test
const envFile = process.env.ENV_FILE || '.env.test'
// 加载环境变量配置
dotenv.config({ path: `./env/${envFile}` })
// 过滤出以 NUXT_PUBLIC_ 开头的环境变量作为公共配置
const publicConfig = Object.entries(process.env)
.filter(([key]) => key.startsWith('NUXT_PUBLIC_'))
.reduce((config, [key, value]) => {
config[key] = value
return config
}, {})
.filter(([key]) => key.startsWith('NUXT_PUBLIC_'))
.reduce((config, [key, value]) => {
config[key] = value
return config
}, {})
export default defineNuxtConfig({
hooks: {
'pages:extend'(pages) {
const indexPage = pages.findIndex(page => page.path === '/')
if (indexPage !== -1) {
pages.splice(indexPage, 1)
}
pages.push({
name: 'home',
path: '/',
file: '@/pages/home/index.vue'
})
}
},
// 注册 Nuxt 模块
modules: [
'@vant/nuxt',
'@unocss/nuxt',
'@nuxtjs/color-mode',
'@nuxtjs/i18n',
'@nuxt/image', // 图片优化模块
'@vant/nuxt', // Vant UI 组件库
'@unocss/nuxt', // 原子化 CSS 框架
'@nuxtjs/i18n', // 国际化模块
],
// 运行时配置
runtimeConfig: {
// 私有配置,只有在服务端可用
// 私有配置,只在服务端可用
apiSecret: process.env.NUXT_API_SECRET || 'default_secret',
// 公共配置,客户端和服务端都可用
public: publicConfig,
},
// CSS 配置
css: [
'@unocss/reset/tailwind.css',
'./app/styles/vars.css',
'./app/styles/global.css',
'./app/styles/default-theme.css',
'@unocss/reset/tailwind.css', // 重置默认样式
'@/static/styles/default-theme.css',
],
// PostCSS 配置
postcss: {
plugins: {
'autoprefixer': {},
// https://github.com/wswmsword/postcss-mobile-forever
'autoprefixer': {}, // 自动添加 CSS 前缀
// 移动端适配插件配置
'postcss-mobile-forever': {
appSelector: '#__nuxt',
viewportWidth: 375,
maxDisplayWidth: 600,
// devtools excluded
exclude: /@nuxt/,
border: true,
appSelector: '#__nuxt', // 根选择器
viewportWidth: 375, // 设计稿宽度
maxDisplayWidth: 600, // 最大显示宽度
exclude: /@nuxt/, // 排除的文件
border: true, // 显示边框
rootContainingBlockSelectorList: [
'van-tabbar',
'van-popup',
@ -64,89 +61,153 @@ export default defineNuxtConfig({
},
},
colorMode: {
classSuffix: '',
preference: 'system',
fallback: 'light',
storageKey: 'nuxt-color-mode',
},
// 国际化配置
i18n: {
locales: currentLocales,
lazy: true,
strategy: 'no_prefix',
locales: currentLocales, // 支持的语言列表
lazy: true, // 懒加载语言包
strategy: 'no_prefix', // URL 策略:不添加语言前缀
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_redirected',
redirectOn: 'root',
alwaysRedirect: true,
fallbackLocale: 'zh-CN'
useCookie: true, // 使用 cookie 存储语言选择
cookieKey: 'i18n_redirected', // cookie 键名
redirectOn: 'root', // 仅在根路径重定向
alwaysRedirect: true, // 总是重定向
fallbackLocale: 'zh-CN' // 默认语言
},
langDir: 'locales',
defaultLocale: 'zh-CN',
vueI18n: './i18n/i18n.config.ts',
langDir: 'locales', // 语言文件目录
defaultLocale: 'zh-CN', // 默认语言
vueI18n: './i18n/i18n.config.ts', // Vue I18n 配置文件
},
// 应用配置
app: {
layoutTransition: {
// 布局过渡动画
layoutTransition: {
name: 'layout',
mode: 'out-in'
},
// 头部配置
head: {
// 视口配置
viewport: 'width=device-width,initial-scale=1,viewport-fit=cover',
// 链接配置
link: [
{ rel: 'icon', href: '/favicon.ico', sizes: 'any' },
{ rel: 'icon', href: '/favicon.ico', sizes: 'any' }, // 网站图标
{ rel: 'preload', as: 'style', href: '/critical.css' }, // 预加载关键 CSS
{ rel: 'preconnect', href: '你的API域名' }, // 预连接 API 域名
{ rel: 'dns-prefetch', href: '你的API域名' }, // DNS 预解析
],
// Meta 标签配置
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1, viewport-fit=cover,user-scalable=no' },
{ name: 'apple-mobile-web-app-capable', content: 'yes' },
{ name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
{ name: 'theme-color', media: '(prefers-color-scheme: light)', content: '#ffffff' },
{ name: 'theme-color', media: '(prefers-color-scheme: dark)', content: '#222222' },
{ name: 'apple-mobile-web-app-capable', content: 'yes' }, // iOS 应用模式
{ name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' }, // iOS 状态栏样式
{ name: 'theme-color', media: '(prefers-color-scheme: light)', content: '#ffffff' }, // 浅色主题色
{ name: 'theme-color', media: '(prefers-color-scheme: dark)', content: '#222222' }, // 深色主题色
{ name: 'description', content: '你的网站描述' }, // SEO 描述
{ 'http-equiv': 'x-dns-prefetch-control', content: 'on' }, // DNS 预解析控制
],
// 脚本配置
script: [
{ innerHTML: preload(), type: 'text/javascript', tagPosition: 'head' },
{ innerHTML: preload(), type: 'text/javascript', tagPosition: 'head' }, // 预加载脚本
// 性能监控脚本
{
innerHTML: `
window.performance.mark('app-start');
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('FCP:', entry.startTime);
}
}).observe({entryTypes: ['paint']});
`,
type: 'text/javascript',
tagPosition: 'head'
}
],
},
},
// Vite 构建配置
vite: {
build: {
target: 'esnext',
target: 'esnext', // 构建目标
// Rollup 配置
rollupOptions: {
output: {
manualChunks: {
'vant': ['vant'], // Vant 单独打包
'vendor': ['vue', 'vue-router'] // 核心库单独打包
}
}
},
minify: 'terser', // 使用 terser 压缩
terserOptions: {
compress: {
drop_console: true, // 移除 console
drop_debugger: true // 移除 debugger
}
}
},
// 依赖优化配置
optimizeDeps: {
include: [
'@intlify/core-base',
'@intlify/shared',
'is-https',
'@intlify/core-base', // I18n 核心
'@intlify/shared', // I18n 共享库
'is-https', // HTTPS 检测
'vant', // Vant UI
'@vant/use', // Vant Hooks
'@vueuse/core', // VueUse 工具集
],
},
},
// 实验性功能
experimental: {
typedPages: true,
typedPages: true, // 启用页面类型
},
// 开发工具
devtools: {
enabled: true,
enabled: true, // 启用开发工具
},
// TypeScript 配置
typescript: {
shim: false,
shim: false, // 禁用 shim
},
// 功能特性配置
features: {
// For UnoCSS
inlineStyles: false,
inlineStyles: false, // 禁用内联样式
},
// 未来特性配置
future: {
compatibilityVersion: 4,
compatibilityVersion: 4, // 兼容性版本
},
// 指定 Nuxt 应用程序的兼容性日期,确保应用程序在未来的 Nuxt 版本中保持稳定性
compatibilityDate: '2025-01-09',
// Nuxt 兼容性日期
compatibilityDate: '2025-01-09', // 确保未来版本兼容性
// 开发服务器配置
devServer: {
host: '0.0.0.0', // Set the host to 'localhost'
port: 3000, // Set the port to 3000 or any other port you prefer
host: '0.0.0.0', // 主机地址
port: 3000, // 端口号
},
// 图片优化配置
image: {
provider: 'ipx', // 图片处理提供者
// 响应式断点
screens: {
xs: 320, // 超小屏幕
sm: 640, // 小屏幕
md: 768, // 中等屏幕
lg: 1024, // 大屏幕
xl: 1280, // 超大屏幕
xxl: 1536, // 特大屏幕
},
quality: 80, // 图片质量
format: ['webp', 'jpg'], // 支持的格式
placeholder: true, // 启用占位图
blur: 3 // 模糊加载效果
}
})

View File

@ -21,6 +21,7 @@
"@nuxtjs/color-mode": "^3.5.2",
"@nuxtjs/i18n": "^9.1.1",
"@vueuse/core": "^12.4.0",
"@yeger/vue-masonry-wall": "^5.0.17",
"aliyun-aliplayer": "^2.28.5",
"axios": "^1.7.9",
"dotenv": "^16.4.7",
@ -33,6 +34,7 @@
},
"devDependencies": {
"@iconify-json/carbon": "^1.2.5",
"@nuxt/image": "^1.9.0",
"@unocss/nuxt": "0.65.2",
"@unocss/preset-rem-to-px": "0.65.2",
"@vant/nuxt": "^1.0.6",

File diff suppressed because it is too large Load Diff