modify ui

This commit is contained in:
Rogee
2025-05-14 17:42:44 +08:00
parent 2f8f7c6072
commit a8a41b8382
9 changed files with 352 additions and 134 deletions

View File

@@ -1,5 +1,21 @@
<template>
<div class="mx-auto max-w-[480px] w-full min-h-screen bg-white">
<router-view />
<div class="mx-auto max-w-[480px] w-full min-h-screen bg-gray-50 font-sans antialiased">
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@@ -30,49 +30,39 @@ const showArticle = (article) => {
</script>
<template>
<div class="bg-white rounded-lg shadow overflow-hidden hover:shadow transition-all cursor-pointer"
<div class="bg-white rounded-xl shadow-sm overflow-hidden hover:shadow-md transition-shadow duration-200 cursor-pointer"
@click="showArticle(article)">
<div v-if="article.head_images && article.head_images.length > 0" class="w-full h-32 bg-gray-200">
<img :src="article.head_images[0]" class="mask-b-from-50% w-full h-full object-cover brightness-50"
<div v-if="article.head_images && article.head_images.length > 0"
class="relative w-full aspect-video bg-gray-100 overflow-hidden">
<img :src="article.head_images[0]"
class="w-full h-full object-cover transition-transform duration-300 hover:scale-105"
:alt="article.title" />
<div class="absolute inset-0 bg-gradient-to-b from-transparent to-black/30"></div>
</div>
<div class="p-4">
<h3 class="text-lg break-all font-medium text-gray-800 line-clamp-2">
<div class="p-4 space-y-3">
<h3 class="text-lg font-semibold text-gray-800 line-clamp-2">
{{ article.title }}
</h3>
<p v-if="article.description" class="text-gray-600 text-sm mb-2 line-clamp-2">
<p v-if="article.description"
class="text-gray-600 text-sm line-clamp-2">
{{ article.description }}
</p>
<div class="py-2 flex items-center gap-2">
<div class="flex flex-wrap gap-2">
<span v-for="tag in article.tags" :key="tag"
class="px-1.5 py-0.5 text-xs bg-gray-100 text-gray-600 rounded">
<span class="mr-1">#</span>{{ tag }}
class="px-2 py-1 text-xs bg-blue-50 text-blue-600 rounded-full">
{{ tag }}
</span>
</div>
<div class="flex items-center justify-between mt-auto text-sm">
<div class="flex flex-wrap items-center gap-3">
<span class="flex items-center gap-1 text-gray-700">
<AiOutlineEye class="w-3.5 h-3.5" />
{{ article.views }}
</span>
<!-- <span class="flex items-center gap-1 text-gray-700">
<AiOutlineLike class="w-3.5 h-3.5" />
{{ article.likes }}
</span> -->
<div class="flex items-center justify-between pt-2 text-sm">
<div class="flex items-center gap-2 text-gray-500">
<AiOutlineEye class="w-4 h-4" />
<span>{{ article.view_count || 0 }}</span>
</div>
<div class="flex items-center gap-2 ml-4 flex-shrink-0 font-mono">
<span v-if="article.bought" class="px-2 py-0.5 text-xs bg-green-50 text-green-600 rounded">
已购买
</span>
<template v-else>
<span v-if="article.discount < 100" class="text-xs line-through text-gray-500">
¥{{ (article.price / 100).toFixed(2) }}
</span>
<span class="text-xl text-orange-600">&yen;{{ discountPrice }}</span>
</template>
<div class="text-primary-600 font-medium">
¥{{ discountPrice }}
</div>
</div>
</div>

View File

@@ -22,22 +22,26 @@ const switchTab = (index, route) => {
<div class="h-screen flex flex-col bg-gray-50">
<div class="flex-1 overflow-hidden">
<router-view v-slot="{ Component }">
<keep-alive>
<transition name="fade" mode="out-in">
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
</div>
<nav class="flex-none bg-white border-t border-gray-200">
<div class="flex justify-around items-center h-14">
<div v-for="(tab, index) in tabs" :key="index"
class="flex flex-col items-center justify-center w-full h-full py-1 focus:outline-none" :class="[
<nav class="flex-none bg-white border-t border-gray-200 shadow-[0_-1px_3px_rgba(0,0,0,0.05)]">
<div class="flex justify-around items-center h-14 max-w-[480px] mx-auto px-2">
<div v-for="(tab, index) in tabs"
:key="index"
class="flex flex-col items-center justify-center w-full h-full py-1 select-none"
:class="[
activeTab === index
? 'text-blue-600'
: 'text-gray-600 hover:text-blue-600 active:text-blue-700'
]" @click="switchTab(index, tab.route)">
<component :is="tab.icon" class="w-6 h-6" />
<span class="mt-1 text-xs">{{ tab.label }}</span>
? 'text-primary-600'
: 'text-gray-500 hover:text-primary-500 active:text-primary-700'
]"
@click="switchTab(index, tab.route)">
<component :is="tab.icon"
class="w-6 h-6 transition-colors duration-200" />
<span class="mt-1 text-xs font-medium">{{ tab.label }}</span>
</div>
</div>
</nav>
@@ -45,5 +49,13 @@ const switchTab = (index, route) => {
</template>
<style scoped>
/* Remove all existing styles */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@@ -1,6 +1,37 @@
@import "tailwindcss";
@import "tailwindcss/preflight";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
html, body {
@layer base {
html, body {
touch-action: manipulation;
-webkit-touch-callout: none;
}
}
@layer components {
.btn {
@apply px-4 py-2 rounded-lg font-medium transition-colors duration-200;
}
.btn-primary {
@apply bg-primary-500 text-white hover:bg-primary-600 active:bg-primary-700;
}
.btn-secondary {
@apply bg-secondary-100 text-secondary-900 hover:bg-secondary-200 active:bg-secondary-300;
}
.input {
@apply px-3 py-2 border border-secondary-200 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent;
}
.card {
@apply bg-white rounded-xl shadow-lg p-6;
}
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
.text-pretty {
text-wrap: pretty;
}
}

View File

@@ -69,29 +69,54 @@ onMounted(() => {
</script>
<template>
<div class="h-full flex flex-col">
<div class="flex-none bg-white border-b border-gray-200 z-50 shadow">
<div class="h-full flex flex-col bg-gray-50">
<div class="flex-none bg-white border-b border-gray-200 sticky top-0 z-40 shadow-sm">
<div class="p-4">
<input type="search" v-model="searchInput" @keyup="handleKeyup" placeholder="搜索"
class="w-full px-4 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<div class="relative">
<input type="search"
v-model="searchInput"
@keyup="handleKeyup"
placeholder="搜索曲谱..."
class="w-full pl-10 pr-4 py-2.5 text-gray-600 bg-gray-50 border border-gray-200 rounded-full
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent
placeholder:text-gray-400">
<span class="absolute inset-y-0 left-0 flex items-center pl-3 text-gray-400">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</span>
</div>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<div class="p-4">
<ArticleListItem v-for="article in articles" :key="article.id" :article="article" class="mb-4" />
<div class="container max-w-2xl mx-auto p-4">
<div class="grid gap-4">
<ArticleListItem v-for="article in articles"
:key="article.id"
:article="article" />
</div>
<div ref="loadingTrigger" class="py-4 text-center" v-show="hasMore || loading">
<div ref="loadingTrigger"
class="py-4 text-center"
v-show="hasMore || loading">
<div v-if="loading"
class="animate-spin rounded-full h-8 w-8 border-4 border-gray-200 border-t-blue-600 mx-auto">
class="w-8 h-8 mx-auto border-2 border-gray-200 border-t-primary-500 rounded-full animate-spin">
</div>
<div v-else class="h-8">
<!-- 空白占位保持触发器可见 -->
<!-- Empty space for intersection observer -->
</div>
</div>
<div v-if="!hasMore && articles.length > 0" class="text-center text-gray-500 py-4">
没有更多了
<div v-if="!hasMore && articles.length > 0"
class="text-center text-gray-500 py-4">
没有更多曲谱了
</div>
<div v-if="!hasMore && articles.length === 0"
class="text-center text-gray-500 py-12">
暂无曲谱
</div>
</div>
</div>

View File

@@ -35,30 +35,55 @@ const menuGroups = [
</script>
<template>
<div class="bg-white p-4 shadow-lg border-b border-gray-300">
<div class="flex items-center gap-4 p-4 border-gray-100">
<div class="w-16 h-16 border rounded-full border-gray-200 bg-gray-200 overflow-hidden">
<img :src="userInfo.avatar" :alt="userInfo.username" class="w-full h-full object-cover" />
</div>
<div class="min-h-screen bg-gray-50">
<!-- 用户信息卡片 -->
<div class="bg-white shadow-sm">
<div class="max-w-2xl mx-auto">
<div class="flex items-center gap-4 p-6">
<div class="w-20 h-20 rounded-full border-2 border-white shadow-lg overflow-hidden bg-gray-100">
<img :src="userInfo.avatar"
:alt="userInfo.username"
class="w-full h-full object-cover" />
</div>
<div>
<h3 class="text-xl font-medium">{{ userInfo.username }}</h3>
<div class="text-gray-500">用户编号: {{ userInfo.id }}</div>
<div class="text-gray-500">账户余额: {{ (userInfo.balance / 100).toFixed(2) }}</div>
<div class="space-y-1">
<h3 class="text-xl font-semibold text-gray-800">
{{ userInfo.username }}
</h3>
<div class="text-gray-500 text-sm">
用户编号: {{ userInfo.id }}
</div>
<div class="text-primary-600 font-medium">
账户余额: ¥{{ (userInfo.balance / 100).toFixed(2) }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="menus space-y-4 mt-4 px-4 hidden">
<div v-for="(group, groupIndex) in menuGroups" :key="groupIndex" class="bg-white rounded-lg overflow-hidden">
<div class="px-4 py-2 text-sm text-gray-500">{{ group.title }}</div>
<div class="divide-y divide-gray-100">
<div v-for="(item, itemIndex) in group.items" :key="itemIndex"
class="flex items-center px-4 py-3 hover:bg-gray-50 active:bg-gray-100 cursor-pointer">
<component :is="item.icon" class="mr-3 text-xl text-gray-600" />
<span class="flex-1">{{ item.label }}</span>
<span class="text-gray-400"></span>
<!-- 菜单列表 -->
<div class="max-w-2xl mx-auto px-4 py-6 space-y-4">
<div v-for="(group, groupIndex) in menuGroups"
:key="groupIndex"
class="bg-white rounded-xl overflow-hidden shadow-sm">
<div class="px-4 py-2 text-sm font-medium text-gray-500 bg-gray-50">
{{ group.title }}
</div>
<div class="divide-y divide-gray-100">
<div v-for="(item, itemIndex) in group.items"
:key="itemIndex"
class="flex items-center px-4 py-3.5 hover:bg-gray-50 active:bg-gray-100
cursor-pointer transition-colors duration-150">
<component :is="item.icon"
class="mr-3 text-xl text-gray-500" />
<span class="flex-1 text-gray-700">{{ item.label }}</span>
<span class="text-gray-400">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 5l7 7-7 7" />
</svg>
</span>
</div>
</div>
</div>
</div>