diff --git a/docs/composables/useLightweightChart.md b/docs/composables/useLightweightChart.md index 4032210..5f1da01 100644 --- a/docs/composables/useLightweightChart.md +++ b/docs/composables/useLightweightChart.md @@ -26,7 +26,7 @@ const chart = createLineChart(containerEl, { width: 400, height: 320 }) const series = addLineSeries(chart, { color: '#2563eb', priceFormat: { type: 'percent', precision: 1 } }) series.setData(lwcData) -// 实时追加/更新单点时,使用 update() 替代 setData() 以获得过渡动画 +// 实时追加/更新单点时,使用 update() 替代 setData() 以获得更平滑的过渡效果 series.update(toLwcPoint([Date.now(), 52])) ``` diff --git a/docs/core/App.md b/docs/core/App.md index 538dab8..76e31a9 100644 --- a/docs/core/App.md +++ b/docs/core/App.md @@ -8,8 +8,9 @@ ## 核心能力 -- 顶部导航栏:返回、PolyMarket 标题、Login 或余额+用户名+头像菜单 +- 顶部导航栏:返回、TestMarket 标题、Login 或余额+用户名+头像菜单 - 多语言入口:右侧地球图标(`mdi-earth`)+ 当前语言文案,点击打开语言选择菜单 +- 移动端底部导航:Home / Search / Mine(三个 tab 等分屏宽,与路由联动;Mine 未登录跳转 Login;选中态仅加粗、无底色;未选中项图标与文字偏灰;底部导航上方有淡投影) - 登录态:`userStore.isLoggedIn` 控制展示 - 用户名:`nickName` 或 `userName` 显示在头像左侧(有值时) - 挂载时与 `isLoggedIn` 变为 true 时:拉取用户信息与余额(`router.isReady()` + `nextTick` 后执行),确保钱包登录、刷新页面后头像和用户名正确显示 diff --git a/docs/core/router.md b/docs/core/router.md index ec4cc1f..361612e 100644 --- a/docs/core/router.md +++ b/docs/core/router.md @@ -11,6 +11,7 @@ Vue Router 配置,定义路由表与滚动行为。 | path | name | component | |------|------|-----------| | / | home | Home | +| /search | search | Search | | /trade | trade | Trade | | /login | login | Login | | /trade-detail/:id | trade-detail | TradeDetail | diff --git a/docs/core/vuetify.md b/docs/core/vuetify.md index 9edd9a0..86a48ac 100644 --- a/docs/core/vuetify.md +++ b/docs/core/vuetify.md @@ -10,6 +10,11 @@ Vuetify 4 插件配置,注册组件、指令、主题。引入 VPullToRefresh - `light`:默认主题,含 primary、secondary、accent 等 - `dark`:暗色主题 +- `background`:全局背景色统一为 `#FCFCFC`(rgb(252,252,252)) + +## 全局默认行为 + +- `ripple`:全局关闭点击水波纹效果(`defaults.global.ripple = false`) ## 扩展方式 diff --git a/docs/views/Home.md b/docs/views/Home.md index 7cef247..57658c1 100644 --- a/docs/views/Home.md +++ b/docs/views/Home.md @@ -13,6 +13,11 @@ - **搜索**:可按关键词搜索事件 - **分类筛选**:选中分类后,自动提取所有层级节点的 `tagIds` 进行事件筛选;切换语言时重新请求分类接口并刷新列表 +## UI 细节 +- **卡片阴影不裁剪**:列表容器 `.home-list-scroll` 不使用 `overflow-x: hidden`,避免卡片两侧阴影被裁剪 + +- **分类行分隔**:`.home-category-layer1-row` 底部有淡色投影,增强与下方内容的层次感 + ## 数据流 ``` diff --git a/index.html b/index.html index df3f429..64a5f64 100644 --- a/index.html +++ b/index.html @@ -1,9 +1,9 @@ - + - - - + + + TestMarket diff --git a/scripts/deploy.mjs b/scripts/deploy.mjs index 19ad06d..99484eb 100644 --- a/scripts/deploy.mjs +++ b/scripts/deploy.mjs @@ -43,11 +43,23 @@ function deploy() { } console.log(`🔌 正在通过 SSH 部署到 ${target} ...`) + + // 目标服务器 SSH 配置可能较老/策略更严格:在 KEX/HostKey/公钥算法上做一点兼容。 + // 如果远端没有这些算法,仍会失败;但能显著提高协商成功率并给出更可读的错误信息。 + const sshCmd = + 'ssh -o ServerAliveInterval=15 -o ServerAliveCountMax=3 -o ConnectTimeout=20 -o KexAlgorithms=curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha1 -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa -vvv' + console.log(`rsync ssh command: ${sshCmd}`) + const result = spawnSync( 'rsync', [ '-avz', + '-vv', '--delete', + '--progress', + '--stats', + '-e', + sshCmd, '--exclude=.DS_Store', `${distDir}/`, `${target}/`, diff --git a/src/App.vue b/src/App.vue index 9b9f6da..a954b8a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,6 +2,7 @@ import { computed, onMounted, watch, nextTick } from 'vue' import { useRoute, useRouter } from 'vue-router' import { useI18n } from 'vue-i18n' +import { useDisplay } from 'vuetify' import { useUserStore } from './stores/user' import { useLocaleStore } from './stores/locale' import Toast from './components/Toast.vue' @@ -11,6 +12,7 @@ const { t } = useI18n() const localeStore = useLocaleStore() const router = useRouter() const userStore = useUserStore() +const display = useDisplay() const currentRoute = computed(() => route.path) const currentLocaleLabel = computed(() => { @@ -20,6 +22,22 @@ const currentLocaleLabel = computed(() => { ) }) +const showBottomNav = computed(() => display.smAndDown.value) +const mineTargetPath = computed(() => (userStore.isLoggedIn ? '/wallet' : '/login')) +const bottomNavValue = computed({ + get() { + const p = currentRoute.value + if (p.startsWith('/wallet')) return '/wallet' + if (p.startsWith('/login')) return '/wallet' // Mine 入口在登录页也保持高亮 + if (p.startsWith('/search')) return '/search' + return '/' + }, + set(v: string) { + if (v === '/wallet') router.push(mineTargetPath.value) + else router.push(v) + }, +}) + async function refreshUserData() { if (!userStore.isLoggedIn) return await router.isReady() @@ -54,7 +72,7 @@ watch( > mdi-arrow-left - PolyMarket + TestMarket @@ -126,6 +144,28 @@ watch( + + + + mdi-home-outline + Home + + + mdi-magnify + Search + + + mdi-account-outline + Mine + + + @@ -179,4 +219,49 @@ watch( text-overflow: ellipsis; white-space: nowrap; } + +/* 底部导航:整条栏上方淡投影 */ +:deep(.v-bottom-navigation) { + box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.06); +} +/* 底部导航:三个入口等分屏幕宽度 */ +:deep(.v-bottom-navigation__content) { + width: 100%; +} +:deep(.v-bottom-navigation__content > .v-btn) { + flex: 1 1 0; + min-width: 0; +} + +/* 底部导航未选中态:图标与文字偏灰 */ +:deep(.v-bottom-navigation__content > .v-btn:not(.v-btn--selected):not(.v-btn--active)) { + color: rgba(0, 0, 0, 0.45); +} +:deep(.v-bottom-navigation__content > .v-btn:not(.v-btn--selected):not(.v-btn--active) .v-icon) { + color: rgba(0, 0, 0, 0.45); +} + +/* 底部导航选中态:加粗、无底色 */ +:deep(.v-bottom-navigation__content > .v-btn.v-btn--selected), +:deep(.v-bottom-navigation__content > .v-btn.v-btn--active) { + font-weight: 700; + background: transparent !important; +} +:deep(.v-bottom-navigation__content > .v-btn .v-btn__overlay), +:deep(.v-bottom-navigation__content > .v-btn .v-btn__underlay) { + display: none !important; +} + +:deep(.v-bottom-navigation__content .v-ripple__container) { + display: none !important; +} + +/* 全局背景:统一为 rgb(252,252,252) */ +:global(html), +:global(body) { + background: rgb(252, 252, 252); +} +:global(.v-application) { + background: rgb(252, 252, 252); +} diff --git a/src/composables/useLightweightChart.ts b/src/composables/useLightweightChart.ts index af8a8c9..b7fc37f 100644 --- a/src/composables/useLightweightChart.ts +++ b/src/composables/useLightweightChart.ts @@ -11,7 +11,9 @@ import { type IChartApi, type ISeriesApi, type UTCTimestamp, + LastPriceAnimationMode, } from 'lightweight-charts' +import { trunkSelectStrategy } from 'vuetify/lib/composables/nested/selectStrategies.mjs' /** 将 UTC 秒时间戳转为「本地时间当作 UTC」的秒时间戳,使图表时间轴显示用户当地时间 */ function timeToLocal(utcSeconds: number): UTCTimestamp { @@ -62,7 +64,7 @@ export function toLwcPoint(point: [number, number]): { time: UTCTimestamp; value /** 创建折线图实例 */ export function createLineChart( container: HTMLElement, - options?: { width?: number; height?: number } + options?: { width?: number; height?: number }, ): IChartApi { const { width = container.clientWidth, height = 320 } = options ?? {} return createChart(container, { @@ -98,7 +100,7 @@ export function createLineChart( /** 添加折线系列 */ export function addLineSeries( chart: IChartApi, - options?: { color?: string; priceFormat?: { type: 'percent' | 'price'; precision?: number } } + options?: { color?: string; priceFormat?: { type: 'percent' | 'price'; precision?: number } }, ): ISeriesApi<'Line'> { return chart.addSeries(LineSeries, { color: options?.color ?? '#2563eb', @@ -109,13 +111,14 @@ export function addLineSeries( options?.priceFormat?.type === 'percent' ? { type: 'percent', precision: options.priceFormat.precision ?? 1 } : { type: 'price', precision: 2 }, + lastPriceAnimation: LastPriceAnimationMode.Continuous, }) } /** 添加面积系列(带渐变) */ export function addAreaSeries( chart: IChartApi, - options?: { color?: string; topColor?: string; bottomColor?: string } + options?: { color?: string; topColor?: string; bottomColor?: string }, ): ISeriesApi<'Area'> { const color = options?.color ?? '#2563eb' const topColor = options?.topColor ?? color + '40' diff --git a/src/plugins/vuetify.ts b/src/plugins/vuetify.ts index 76b2d55..be7556c 100644 --- a/src/plugins/vuetify.ts +++ b/src/plugins/vuetify.ts @@ -11,6 +11,11 @@ export default createVuetify({ VPullToRefresh, }, directives, + defaults: { + global: { + ripple: false, + }, + }, theme: { defaultTheme: 'light', themes: { @@ -25,7 +30,7 @@ export default createVuetify({ success: '#34A853', warning: '#FBBC05', surface: '#FFFFFF', - background: '#F5F5F5', + background: '#FCFCFC', }, }, dark: { diff --git a/src/router/index.ts b/src/router/index.ts index 7464580..55e6341 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -5,6 +5,7 @@ import Login from '../views/Login.vue' import TradeDetail from '../views/TradeDetail.vue' import EventMarkets from '../views/EventMarkets.vue' import Wallet from '../views/Wallet.vue' +import Search from '../views/Search.vue' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), @@ -14,6 +15,11 @@ const router = createRouter({ name: 'home', component: Home, }, + { + path: '/search', + name: 'search', + component: Search, + }, { path: '/trade', name: 'trade', diff --git a/src/views/Home.vue b/src/views/Home.vue index f0c6838..1b2da4a 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -21,7 +21,7 @@ :aria-label="t('common.search')" @click="expandSearch" > - mdi-magnify + mdi-magnify - mdi-close + mdi-close
@@ -95,7 +95,7 @@ :aria-label="t('common.delete')" @click.stop="searchHistory.remove(idx)" > - mdi-close + mdi-close @@ -225,6 +225,14 @@ +