前端死代码清理

This commit is contained in:
yml
2026-06-14 18:42:06 +08:00
parent 965832c3cb
commit 2dc5a8121e
18 changed files with 0 additions and 4864 deletions
@@ -1,89 +0,0 @@
<script setup lang="ts">
import { onBeforeUnmount, ref, watch } from 'vue'
import { fetchAdminFileBlob, fetchFileBlobByURL } from '@/shared/api/files'
const props = defineProps<{
source: string
admin?: boolean
}>()
const objectURL = ref('')
const failed = ref(false)
function extractObjectKey(value: string) {
try {
const parsed = new URL(value, window.location.origin)
return parsed.searchParams.get('key') || ''
} catch {
return ''
}
}
function revokeCurrentURL() {
if (!objectURL.value) return
URL.revokeObjectURL(objectURL.value)
objectURL.value = ''
}
async function loadImage() {
revokeCurrentURL()
failed.value = false
if (!props.source) {
failed.value = true
return
}
try {
const key = extractObjectKey(props.source)
const blob =
props.admin && key ? await fetchAdminFileBlob(key) : await fetchFileBlobByURL(props.source)
objectURL.value = URL.createObjectURL(blob)
} catch {
failed.value = true
}
}
function openImage() {
if (!objectURL.value) return
window.open(objectURL.value, '_blank')
}
watch(() => [props.source, props.admin] as const, loadImage, { immediate: true })
onBeforeUnmount(revokeCurrentURL)
</script>
<template>
<button v-if="objectURL" class="chat-image-button" type="button" @click="openImage">
<img :src="objectURL" alt="聊天图片" loading="lazy" decoding="async" />
</button>
<span v-else class="chat-image-fallback">{{ failed ? '图片加载失败' : '图片加载中' }}</span>
</template>
<style scoped>
.chat-image-button {
display: block;
max-width: 220px;
padding: 0;
overflow: hidden;
border: 0;
border-radius: 8px;
background: transparent;
cursor: zoom-in;
}
.chat-image-button img {
display: block;
width: 100%;
max-height: 260px;
object-fit: cover;
}
.chat-image-fallback {
display: inline-block;
padding: 8px 10px;
border-radius: 8px;
background: #eef2f7;
color: #6b7280;
font-size: 12px;
}
</style>
@@ -1,137 +0,0 @@
<script setup lang="ts">
import { useRoute, RouterLink } from 'vue-router'
import { useChatUnreadCount } from '@/features/chats/composables/useChatUnreadCount'
const route = useRoute()
const { unreadCount, unreadLabel } = useChatUnreadCount(route)
function isNavActive(path: string) {
if (path === '/m') return route.path === '/m'
return route.path.startsWith(path)
}
</script>
<template>
<nav class="bottom-nav">
<RouterLink to="/m" class="nav-item" :class="{ active: isNavActive('/m') }">
<van-icon name="home-o" :size="22" />
<span>首页</span>
</RouterLink>
<RouterLink to="/m/messages" class="nav-item" :class="{ active: isNavActive('/m/messages') }">
<span class="nav-icon-wrap">
<van-icon name="chat-o" :size="22" />
<em v-if="unreadCount > 0" class="nav-badge">{{ unreadLabel }}</em>
</span>
<span>消息</span>
</RouterLink>
<RouterLink to="/m/seller/listings/create" class="nav-item nav-publish">
<div class="publish-pill">+</div>
<span>发布</span>
</RouterLink>
<RouterLink to="/m/orders" class="nav-item" :class="{ active: isNavActive('/m/orders') }">
<van-icon name="orders-o" :size="22" />
<span>订单</span>
</RouterLink>
<RouterLink to="/m/profile" class="nav-item" :class="{ active: isNavActive('/m/profile') }">
<van-icon name="manager-o" :size="22" />
<span>我的</span>
</RouterLink>
</nav>
</template>
<style scoped>
.bottom-nav {
position: fixed;
left: 0;
right: 0;
bottom: -1px;
z-index: 100;
display: flex;
background: #fff;
border-top: 1px solid #eee;
padding-bottom: env(safe-area-inset-bottom);
height: calc(52px + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.bottom-nav::after {
position: absolute;
right: 0;
bottom: -18px;
left: 0;
height: 18px;
background: #fff;
content: '';
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
color: #999;
font-size: 10px;
text-decoration: none;
}
.nav-item.active {
color: #1477ff;
}
.nav-item span {
font-weight: 600;
}
.nav-icon-wrap {
position: relative;
display: grid;
width: 24px;
height: 24px;
place-items: center;
}
.nav-badge {
position: absolute;
top: -5px;
right: -10px;
min-width: 16px;
height: 16px;
padding: 0 4px;
box-sizing: border-box;
border: 2px solid #fff;
border-radius: 999px;
background: #ef4444;
color: #fff;
font-size: 10px;
font-style: normal;
font-weight: 800;
line-height: 12px;
text-align: center;
}
.publish-pill {
width: 36px;
height: 26px;
display: grid;
place-items: center;
border-radius: 13px;
background: #ff6a00;
color: #fff;
font-size: 18px;
font-weight: 900;
}
.nav-publish span {
color: #ff6a00;
}
/* 响应式适配 */
@media (min-width: 520px) {
.bottom-nav {
left: calc((100vw - 430px) / 2);
right: calc((100vw - 430px) / 2);
}
}
</style>
-1
View File
@@ -1,4 +1,3 @@
// 通用 Composables
export { useMoney } from './useMoney'
export { useSmsCountdown } from './useSmsCountdown'
export { usePricingCalculator } from './usePricingCalculator'
@@ -1,5 +0,0 @@
import { formatMoneyWithSymbol } from '@/shared/utils/money'
export function useMoney() {
return (value: number | undefined | null) => formatMoneyWithSymbol(value)
}
File diff suppressed because it is too large Load Diff
-49
View File
@@ -1,49 +0,0 @@
:root {
color: #1f2933;
background: #f6f8fb;
font-family:
Inter,
'PingFang SC',
'Microsoft YaHei',
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
sans-serif;
font-synthesis: none;
text-rendering: optimizeLegibility;
}
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
min-width: 320px;
min-height: 100vh;
max-width: 100%;
overflow-x: clip;
}
#app {
max-width: 100%;
overflow-x: clip;
}
a {
color: inherit;
text-decoration: none;
}
/* ── Vant Toast 保护规则 ──────────────────────────────────
.van-popup 声明 background: var(--van-popup-background) = #fff (白色)
.van-toast 声明 background: var(--van-toast-background) = rgba(0,0,0,.7) (黑色)
两个类同优先级,但 .van-popup 在 vant/lib/index.css 中后声明 → 覆盖黑色背景
导致 Toast 白底白字看不见。用双类选择器提升优先级强制使用黑色背景。
────────────────────────────────────────────────────────── */
.van-popup.van-toast {
background: var(--van-toast-background) !important;
color: var(--van-toast-text-color) !important;
}
-230
View File
@@ -1,230 +0,0 @@
/* 筛选器相关样式 */
.horizontal-filter-card {
padding: 24px;
border: 1px solid #eef1f5;
border-radius: 16px;
background: #ffffff;
box-shadow: 0 4px 12px rgba(23, 35, 61, 0.03);
}
.filter-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.filter-title {
display: flex;
align-items: center;
gap: 12px;
}
.filter-title strong {
font-size: 18px;
font-weight: 800;
color: #17233d;
}
.filter-title span {
padding: 4px 10px;
border-radius: 6px;
background: #f1f5f9;
font-size: 13px;
font-weight: 700;
color: #64748b;
}
.filter-chip-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.filter-chip {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 14px;
border: 2px solid #e2e8f0;
border-radius: 8px;
background: #ffffff;
font-size: 13px;
font-weight: 600;
color: #334155;
cursor: pointer;
transition: all 0.2s;
}
.filter-chip:hover {
border-color: #ff6a00;
background: #fff7ed;
}
.filter-chip.active {
border-color: #ff6a00;
background: #fff7ed;
color: #ff6a00;
}
.filter-chip.wide {
min-width: 140px;
}
/* 筛选器弹窗样式 */
:global(.home-filter-popover) {
padding: 8px !important;
border-radius: 12px !important;
}
.filter-menu,
.range-menu {
display: flex;
flex-direction: column;
gap: 4px;
}
.filter-menu button,
.range-menu button {
padding: 10px 14px;
border: none;
border-radius: 8px;
background: transparent;
text-align: left;
font-size: 13px;
font-weight: 600;
color: #334155;
cursor: pointer;
transition: all 0.15s;
}
.filter-menu button:hover,
.range-menu button:hover {
background: #f1f5f9;
}
.filter-menu button.active,
.range-menu button.active {
background: #fff7ed;
color: #ff6a00;
font-weight: 700;
}
.range-manual {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
border-top: 1px solid #e2e8f0;
margin-top: 4px;
}
.range-manual :deep(.el-input-number) {
flex: 1;
}
.range-manual span {
color: #94a3b8;
font-weight: 700;
}
/* 皮肤筛选器 */
.skin-filter-menu {
display: flex;
flex-direction: column;
gap: 16px;
max-height: 400px;
overflow-y: auto;
}
.skin-reset {
padding: 10px 14px;
border: none;
border-radius: 8px;
background: transparent;
text-align: left;
font-size: 13px;
font-weight: 600;
color: #334155;
cursor: pointer;
transition: all 0.15s;
}
.skin-reset:hover {
background: #f1f5f9;
}
.skin-reset.active {
background: #fff7ed;
color: #ff6a00;
font-weight: 700;
}
.skin-filter-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.skin-filter-title {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 4px;
}
.skin-filter-title strong {
font-size: 13px;
font-weight: 700;
color: #17233d;
}
.skin-filter-title button {
padding: 4px 8px;
border: none;
border-radius: 6px;
background: transparent;
font-size: 11px;
font-weight: 600;
color: #64748b;
cursor: pointer;
transition: all 0.15s;
}
.skin-filter-title button:hover {
background: #f1f5f9;
}
.skin-filter-title button.active {
background: #fff7ed;
color: #ff6a00;
}
.skin-filter-options {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 4px;
}
.skin-filter-options button {
padding: 8px 12px;
border: none;
border-radius: 6px;
background: transparent;
text-align: left;
font-size: 12px;
font-weight: 600;
color: #334155;
cursor: pointer;
transition: all 0.15s;
}
.skin-filter-options button:hover {
background: #f1f5f9;
}
.skin-filter-options button.active {
background: #fff7ed;
color: #ff6a00;
font-weight: 700;
}
File diff suppressed because it is too large Load Diff
@@ -1,36 +0,0 @@
/* 性能优化相关样式 */
.lazy-image {
background: #f1f5f9;
min-height: 180px;
}
.component-loading,
.component-error {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
color: #94a3b8;
font-size: 14px;
}
.component-error {
color: #ef4444;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-position: -200px 0;
}
100% {
background-position: calc(200px + 100%) 0;
}
}
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200px 100%;
animation: skeleton-loading 1.5s ease-in-out infinite;
}