[{"data":1,"prerenderedAt":804},["ShallowReactive",2],{"\u002F2026\u002F03\u002Fsteamgamepage":3,"index_posts":299,"surround-\u002F2026\u002F03\u002Fsteamgamepage":799},{"id":4,"title":5,"body":6,"categories":273,"date":275,"description":276,"draft":277,"extension":278,"image":279,"meta":280,"navigation":282,"path":283,"permalink":284,"published":284,"readingTime":285,"recommend":267,"references":284,"seo":290,"sitemap":291,"stem":292,"subtitle":284,"tags":293,"type":296,"updated":297,"__hash__":298},"content\u002Fposts\u002F2026\u002F03\u002FsteamGamePage.md","游戏展示页面",{"type":7,"value":8,"toc":263},"minimark",[9,13,17,30,33,82,85,92,118,123,128,133,181,186,201,206,244,248],[10,11,12],"h2",{"id":12},"核心代码",[14,15,16],"h3",{"id":16},"数据获取",[18,19,26],"pre",{"className":20,"code":22,"filename":23,"language":24,"meta":25},[21],"language-ts","\u002F\u002F composables\u002FuseSteamAPIGet.ts\nimport blogConfig from '~~\u002Fblog.config'\n\n\u002F\u002F ==================== 基础类型定义 ====================\n\nexport type SteamStatus =\n  | 'offline'\n  | 'online'\n  | 'away'\n  | 'snooze'\n  | 'busy'\n  | 'trading'\n  | 'playing'\n\nexport interface SteamAvatar {\n  small: string\n  medium: string\n  large: string\n}\n\nexport interface SteamCurrentGame {\n  appid: number\n  name: string\n}\n\nexport interface SteamPlaytimeStats {\n  totalForever: number\n  totalTwoWeeks: number\n}\n\nexport interface SteamUser {\n  steamid: string\n  username: string\n  profileUrl: string\n  avatar: SteamAvatar\n  status: SteamStatus\n  statusMessage: string\n  currentGame?: SteamCurrentGame\n  playtimeStats: SteamPlaytimeStats\n}\n\nexport interface SteamGameAchievement {\n  total: number\n  unlocked: number\n  percentage: number\n}\n\nexport interface SteamGamePrice {\n  amount: number\n  currency: string\n  displayPrice: string\n}\n\nexport interface SteamGameImages {\n  icon: string\n  logo: string\n  headerImage: string\n  heroImage: string\n  libraryHeroImage: string\n}\n\nexport interface SteamGameDetail {\n  appid: number\n  name: string\n  playtimeForever: number\n  playtimeTwoWeeks: number\n  price: SteamGamePrice\n  images: SteamGameImages\n  releaseDate: string\n  shortDescription: string\n  achievements?: SteamGameAchievement\n}\n\nexport interface SteamGameTwoWeekSummary extends SteamGameDetail {}\nexport interface SteamGameAllTimeSummary extends SteamGameDetail {}\n\nexport interface SteamGamesList {\n  totalCount: number\n  recentCount: number\n  recentGames: SteamGameTwoWeekSummary[]\n  allGames: SteamGameAllTimeSummary[]\n}\n\nexport interface SteamAchievementItem {\n  name: string\n  description: string\n  unlocked: boolean\n  unlockTime: number\n  images: {\n    icon: string\n    iconGray: string\n  }\n}\n\nexport interface SteamGameAchievements {\n  appid: number\n  gameName: string\n  total: number\n  unlocked: number\n  percentage: number\n  items: SteamAchievementItem[]\n}\n\nexport interface SteamAchievementsData {\n  totalCount: number\n  unlockedCount: number\n  unlockedPercentage: number\n  byGame: SteamGameAchievements[]\n}\n\n\u002F\u002F ==================== API 响应类型 ====================\n\nexport interface SteamApiMetadata {\n  cached: boolean\n  cachedAt: string\n  cacheExpiry: string\n  fetchDuration: string\n}\n\nexport interface SteamApiResponse\u003CT> {\n  success: boolean\n  data?: T\n  metadata?: SteamApiMetadata\n  error?: string\n  code?: string\n}\n\nexport interface SteamUserResponse {\n  user: SteamUser\n}\n\nexport interface SteamGamesResponse {\n  games: SteamGamesList\n}\n\nexport interface SteamGameResponse {\n  game: SteamGameDetail\n}\n\nexport interface SteamAchievementsResponse {\n  achievements: SteamAchievementsData\n}\n\n\u002F\u002F ==================== Composable 返回类型 ====================\n\nexport interface SteamDataResult {\n  user?: SteamUser\n  games?: SteamGamesList\n  achievements?: SteamAchievementsData\n}\n\nexport interface SteamAllMetadata {\n  user?: SteamApiMetadata\n  games?: SteamApiMetadata\n  achievements?: SteamApiMetadata\n  gameDetail?: SteamApiMetadata\n}\n\n\u002F\u002F ==================== 常量定义 ====================\n\nexport const steamStatusTextMap: Record\u003CSteamStatus, string> = {\n  offline: '离线',\n  online: '在线',\n  away: '离开',\n  snooze: '打盹',\n  busy: '忙碌',\n  trading: '交易中',\n  playing: '游戏中',\n} as const\n\nexport const steamStatusColorMap: Record\u003CSteamStatus, string> = {\n  offline: '#90a0a6',\n  online: '#4fc951',\n  away: '#ffc72c',\n  snooze: '#ffc72c',\n  busy: '#ff6554',\n  trading: '#6495ed',\n  playing: '#26d07c',\n} as const\n\n\u002F\u002F ==================== 工具函数 ====================\n\nexport const formatPlaytime = (hours: number): string => {\n  return hours \u003C 1 ? '\u003C 1 小时' : `${Math.round(hours)} 小时`\n}\n\nexport const formatSteamTime = (timestamp: number): string => {\n  return new Date(timestamp * 1000).toLocaleDateString('zh-CN')\n}\n\n\u002F\u002F ==================== 内部缓存类型 ====================\n\ntype CacheEntry\u003CT> = {\n  data: SteamApiResponse\u003CT>\n  expiresAt: number\n}\n\ntype FetchOptions = {\n  force?: boolean\n  ttl?: number\n}\n\nconst DEFAULT_TTL = 60 * 1000\nconst GAME_DETAIL_TTL = 5 * 60 * 1000\n\n\u002F\u002F ==================== Composable 主函数 ====================\n\nexport const useSteamAPIGet = () => {\n  \u002F\u002F 共享状态：多个组件调用 composable 时复用同一份状态\n  const loadingCount = useState\u003Cnumber>('steam-loading-count', () => 0)\n  const error = useState\u003CError | null>('steam-error', () => null)\n  const userData = useState\u003CSteamUser | null>('steam-user-data', () => null)\n  const gamesData = useState\u003CSteamGamesList | null>('steam-games-data', () => null)\n  const achievementsData = useState\u003CSteamAchievementsData | null>('steam-achievements-data', () => null)\n  const gameDetailMap = useState\u003CRecord\u003Cnumber, SteamGameDetail>>('steam-game-detail-map', () => ({}))\n  const allMetadata = useState\u003CSteamAllMetadata | null>('steam-all-metadata', () => null)\n\n  \u002F\u002F 内存缓存 + 请求去重\n  const responseCache = useState\u003CRecord\u003Cstring, CacheEntry\u003Cany>>>('steam-response-cache', () => ({}))\n  const pendingRequests = useState\u003CRecord\u003Cstring, Promise\u003Cany>>>('steam-pending-requests', () => ({}))\n\n  const loading = computed(() => loadingCount.value > 0)\n\n  const baseURL = computed(() => {\n    return (blogConfig.Steam.status as string) || 'https:\u002F\u002Fsteam-api-profile-palomiku.netlify.app\u002Fapi'\n  })\n\n  const endpoints = computed(() => ({\n    user: `${baseURL.value}\u002Fsteam-user`,\n    games: `${baseURL.value}\u002Fsteam-games`,\n    game: (appid: number) => `${baseURL.value}\u002Fsteam-game?appid=${appid}`,\n    achievements: `${baseURL.value}\u002Fsteam-achievements`,\n  }))\n\n  const setLoading = (value: boolean) => {\n    if (value) {\n      loadingCount.value += 1\n    } else {\n      loadingCount.value = Math.max(0, loadingCount.value - 1)\n    }\n  }\n\n  const isCacheValid = (key: string) => {\n    const entry = responseCache.value[key]\n    return !!entry && entry.expiresAt > Date.now()\n  }\n\n  const getCache = \u003CT>(key: string): SteamApiResponse\u003CT> | null => {\n    if (!isCacheValid(key)) return null\n    return responseCache.value[key].data as SteamApiResponse\u003CT>\n  }\n\n  const setCache = \u003CT>(key: string, data: SteamApiResponse\u003CT>, ttl = DEFAULT_TTL) => {\n    responseCache.value[key] = {\n      data,\n      expiresAt: Date.now() + ttl,\n    }\n  }\n\n  const clearCache = (key?: string) => {\n    if (key) {\n      delete responseCache.value[key]\n      return\n    }\n    responseCache.value = {}\n  }\n\n  const fetchWithDedupe = async \u003CT>(\n    key: string,\n    url: string,\n    options: FetchOptions = {},\n  ): Promise\u003CSteamApiResponse\u003CT> | null> => {\n    const { force = false, ttl = DEFAULT_TTL } = options\n\n    \u002F\u002F 1. 优先返回缓存\n    if (!force) {\n      const cached = getCache\u003CT>(key)\n      if (cached) return cached\n    }\n\n    \u002F\u002F 2. 有相同请求正在进行时，直接复用 Promise\n    if (!force && pendingRequests.value[key]) {\n      return pendingRequests.value[key] as Promise\u003CSteamApiResponse\u003CT> | null>\n    }\n\n    const requestPromise = (async () => {\n      try {\n        const response = await $fetch\u003CSteamApiResponse\u003CT>>(url, {\n          method: 'GET',\n          headers: {\n            Accept: 'application\u002Fjson',\n          },\n          \u002F\u002F 不再强制 no-store，让服务端\u002FCDN\u002F浏览器仍有机会利用缓存策略\n        })\n\n        if (response?.success) {\n          setCache(key, response, ttl)\n        }\n\n        return response\n      } catch (err) {\n        const message = err instanceof Error ? err.message : String(err)\n        console.error('[Steam] Fetch error:', message)\n        return null\n      } finally {\n        delete pendingRequests.value[key]\n      }\n    })()\n\n    pendingRequests.value[key] = requestPromise\n    return requestPromise\n  }\n\n  const mergeMetadata = (patch: Partial\u003CSteamAllMetadata>) => {\n    allMetadata.value = {\n      ...(allMetadata.value || {}),\n      ...patch,\n    }\n  }\n\n  const fetchUser = async (options: FetchOptions = {}) => {\n    const res = await fetchWithDedupe\u003CSteamUserResponse>(\n      'steam:user',\n      endpoints.value.user,\n      { ttl: DEFAULT_TTL, ...options },\n    )\n\n    if (res?.success && res.data?.user) {\n      userData.value = res.data.user\n      mergeMetadata({ user: res.metadata })\n    }\n\n    return res\n  }\n\n  const fetchGames = async (limit?: number, options: FetchOptions = {}) => {\n    const validLimit = limit && limit > 0 && limit \u003C= 100 ? limit : undefined\n    const url = validLimit\n      ? `${endpoints.value.games}?limit=${validLimit}`\n      : endpoints.value.games\n    const key = `steam:games:${validLimit ?? 'default'}`\n\n    const res = await fetchWithDedupe\u003CSteamGamesResponse>(\n      key,\n      url,\n      { ttl: DEFAULT_TTL, ...options },\n    )\n\n    if (res?.success && res.data?.games) {\n      gamesData.value = res.data.games\n      mergeMetadata({ games: res.metadata })\n    }\n\n    return res\n  }\n\n  const fetchAchievements = async (options: FetchOptions = {}) => {\n    const res = await fetchWithDedupe\u003CSteamAchievementsResponse>(\n      'steam:achievements',\n      endpoints.value.achievements,\n      { ttl: DEFAULT_TTL, ...options },\n    )\n\n    if (res?.success && res.data?.achievements) {\n      achievementsData.value = res.data.achievements\n      mergeMetadata({ achievements: res.metadata })\n    }\n\n    return res\n  }\n\n  const fetchSteamData = async (\n    limit?: number,\n    options: FetchOptions = {},\n  ): Promise\u003CSteamApiResponse\u003CSteamDataResult> & { allMetadata?: SteamAllMetadata }> => {\n    setLoading(true)\n    error.value = null\n\n    try {\n      const [userRes, gamesRes, achievementsRes] = await Promise.all([\n        fetchUser(options),\n        fetchGames(limit, options),\n        fetchAchievements(options),\n      ])\n\n      if (!userRes?.success || !gamesRes?.success) {\n        const errMsg = 'Failed to fetch required Steam data'\n        error.value = new Error(errMsg)\n        return {\n          success: false,\n          error: errMsg,\n          code: 'FETCH_ERROR',\n        }\n      }\n\n      return {\n        success: true,\n        data: {\n          user: userRes.data?.user,\n          games: gamesRes.data?.games,\n          achievements: achievementsRes?.data?.achievements,\n        },\n        allMetadata: allMetadata.value || undefined,\n      }\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err)\n      console.error('[Steam] Fetch error:', message)\n\n      const errorObj = err instanceof Error ? err : new Error(message)\n      error.value = errorObj\n\n      return {\n        success: false,\n        error: `Fetch failed: ${message}`,\n        code: 'FETCH_ERROR',\n      }\n    } finally {\n      setLoading(false)\n    }\n  }\n\n  const fetchGameDetail = async (\n    appid: number,\n    options: FetchOptions = {},\n  ): Promise\u003CSteamApiResponse\u003CSteamGameDetail>> => {\n    setLoading(true)\n\n    try {\n      const res = await fetchWithDedupe\u003CSteamGameResponse>(\n        `steam:game:${appid}`,\n        endpoints.value.game(appid),\n        { ttl: GAME_DETAIL_TTL, ...options },\n      )\n\n      if (res?.success && res.data?.game) {\n        gameDetailMap.value[appid] = res.data.game\n        mergeMetadata({ gameDetail: res.metadata })\n\n        return {\n          success: true,\n          data: res.data.game,\n          metadata: res.metadata,\n        }\n      }\n\n      return {\n        success: false,\n        error: res?.error || 'Failed to fetch game details',\n        code: 'FETCH_ERROR',\n      }\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err)\n      console.error('[Steam] Fetch game detail error:', message)\n\n      return {\n        success: false,\n        error: `Fetch failed: ${message}`,\n        code: 'FETCH_ERROR',\n      }\n    } finally {\n      setLoading(false)\n    }\n  }\n\n  const refreshUser = () => fetchUser({ force: true })\n  const refreshGames = (limit?: number) => fetchGames(limit, { force: true })\n  const refreshAchievements = () => fetchAchievements({ force: true })\n  const refreshGameDetail = (appid: number) => fetchGameDetail(appid, { force: true })\n\n  const getGameDetail = computed(() => {\n    return (appid: number) => gameDetailMap.value[appid] || null\n  })\n\n  return {\n    \u002F\u002F 状态\n    loading: readonly(loading),\n    error: readonly(error),\n    userData: readonly(userData),\n    gamesData: readonly(gamesData),\n    achievementsData: readonly(achievementsData),\n    gameDetailMap: readonly(gameDetailMap),\n    allMetadata: readonly(allMetadata),\n\n    \u002F\u002F 计算\n    endpoints: readonly(endpoints),\n    baseURL: readonly(baseURL),\n    getGameDetail,\n\n    \u002F\u002F 方法\n    fetchSteamData,\n    fetchGameDetail,\n    refreshUser,\n    refreshGames,\n    refreshAchievements,\n    refreshGameDetail,\n    clearCache,\n    formatPlaytime,\n    formatSteamTime,\n\n    \u002F\u002F 常量\n    statusTextMap: steamStatusTextMap,\n    statusColorMap: steamStatusColorMap,\n  }\n}\n\n\u002F\u002F ==================== 向后兼容别名导出 ====================\n\nexport const a = steamStatusTextMap\nexport const f = formatPlaytime\nexport const s = steamStatusColorMap\nexport const u = useSteamAPIGet\n\nexport default useSteamAPIGet\n","useSteamAPI.ts","ts","lang=\"ts\"",[27,28,22],"code",{"__ignoreMap":29},"",[14,31,32],{"id":32},"页面组件",[34,35,37,50,59],"tab",{":tabs":36},"[\"用户面板\", \"信息面板\", \"游戏面板\"]",[38,39,40],"template",{"v-slot:tab1":29},[18,41,48],{"className":42,"code":44,"filename":45,"language":46,"meta":47},[43],"language-vue","\u003Cscript setup lang=\"ts\">\nconst { \n  fetchSteamData, \n  fetchGameDetail,\n  userData, \n  statusTextMap, \n  statusColorMap \n} = useSteamAPIGet()\n\n\u002F\u002F 初始加载数据\nonMounted(async () => {\n  \u002F\u002F 获取基础数据\n  await fetchSteamData(10)\n  \n  \u002F\u002F 如果用户正在游戏中，获取游戏详情\n  if (userData.value?.currentGame) {\n    await fetchGameDetail(userData.value.currentGame.appid)\n  }\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"SteamUser\">\n    \u003Cdiv class=\"SteamUserHeader\">\n      \u003CNuxtImg class=\"UserHeaderAvatar\" :src=\"`${userData?.avatar.large}`\" \u002F>\n      \u003Cdiv class=\"UserHeaderInfo\">\n        \u003Cdiv class=\"HeaderInfoRow\">\n          \u003Ch2 class=\"RowUserName\">\n            {{ userData?.username }}\n          \u003C\u002Fh2>\n          \u003Cdiv class=\"RowBadgeGroup\">\n            \u003Cdiv class=\"RowBadgeCard\" :style=\"`--status-color: ${statusColorMap[userData?.status]}`\">\n              \u003Cspan class=\"BadgeCardDot\" \u002F>\n              {{ statusTextMap[userData?.status] }}\n            \u003C\u002Fdiv>\n          \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cp class=\"StatusInfoText\" v-show=\"userData?.status === 'offline'\">当前该用户已{{ statusTextMap[userData?.status] }}\u003C\u002Fp>\n        \u003Ca class=\"StatusInfoUrl\" :href=\"userData?.profileUrl\" target=\"_blank\"> 访问 Steam 个人资料 → \u003C\u002Fa>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cstyle lang=\"scss\" scoped>\n.SteamUser {\n  background: var(--ld-bg-card);\n  border: 1px solid var(--c-border);\n  border-radius: .8em;\n  padding: 1em;\n  transition: border-color .3s ease;\n  @media (max-width: 767px) {\n    padding: .75em;\n  }\n  .SteamUserHeader {\n    align-items: flex-start;\n    display: flex;\n    gap: 1em;\n    @media (max-width: 767px) {\n      gap: .75em;\n    }\n    .UserHeaderAvatar {\n      border: 2px solid var(--c-primary);\n      border-radius: 50%;\n      flex-shrink: 0;\n      height: 100px;\n      -o-object-fit: cover;\n      object-fit: cover;\n      width: 100px;\n      @media (max-width: 767px) {\n        height: 80px;\n        width: 80px;\n      }\n      @media (max-width: 480px) {\n        height: 70px;\n        width: 70px;\n      }\n    }\n    .UserHeaderInfo {\n      flex: 1;\n      min-width: 0;\n      .HeaderInfoRow {\n        align-items: center;\n        display: flex;\n        flex-wrap: wrap;\n        gap: .75em;\n        margin-bottom: .5em;\n        @media (max-width: 767px) {\n          gap: .5em;\n          margin-bottom: .4em;\n        }\n        .RowUserName {\n          color: var(--c-text);\n          font-size: 1.25em;\n          font-weight: 600;\n          margin: 0;\n          word-break: break-word;\n          @media (max-width: 480px) {\n            font-size: 1em;\n          }\n          @media (max-width: 767px) {\n            font-size: 1.1em;\n          }\n        }\n        .RowBadgeGroup {\n          align-items: center;\n          display: flex;\n          gap: .5em;\n          .RowBadgeCard {\n            align-items: center;\n            background: color-mix(in srgb, var(--status-color) 15%, transparent);\n            border-radius: 1em;\n            color: var(--status-color);\n            display: inline-flex;\n            font-size: .875em;\n            font-weight: 500;\n            gap: .5em;\n            padding: .25em .75em;\n            white-space: nowrap;\n            @media (max-width: 767px) {\n              font-size: .8em;\n              padding: .2em .6em;\n            }\n            .BadgeCardDot {\n              animation: pulse-a9cdcf99 1.5s ease-in-out infinite;\n              background: var(--status-color);\n              border-radius: 50%;\n              display: inline-block;\n              height: 6px;\n              width: 6px;\n            }\n          }\n        }\n      }\n      .StatusInfoText {\n        color: var(--c-text-2);\n        font-size: .85em;\n        font-weight: 400;\n        margin: .125em 0 0;\n        @media (max-width: 480px) {\n          font-size: .8em;\n        }\n      }\n      .StatusInfoUrl {\n        color: var(--c-primary);\n        display: inline-block;\n        font-size: .875em;\n        font-weight: 500;\n        margin-top: .5em;\n        text-decoration: none;\n        transition: opacity .2s;\n      }\n    }\n  }\n}\n\u003C\u002Fstyle>\n","avatar.vue","vue","lang=\"vue\"",[27,49,44],{"__ignoreMap":29},[38,51,52],{"v-slot:tab2":29},[18,53,57],{"className":54,"code":55,"filename":56,"language":46,"meta":47},[43],"\u003Cscript setup lang=\"ts\">\nconst { \n  fetchSteamData, \n  fetchGameDetail,\n  formatPlaytime, \n  formatSteamTime,\n  userData, \n  gamesData, \n  achievementsData,\n  statusTextMap, \n  statusColorMap \n} = useSteamAPIGet()\n\nconst overviewListItemData = [\n  {\n    icon: 'ph:game-controller-bold',\n    label: '游戏总数量',\n    value: computed(() => formatNumber(gamesData.value?.totalCount) || '--'),\n    type: '数量'\n  },{\n    icon: 'ph:timer-bold',\n    label: '游玩总时长',\n    value: computed(() => formatNumber(userData.value?.playtimeStats.totalForever) || '--'),\n    type: '时间'\n  },{\n    icon: 'ph:calendar-fill',\n    label: '两周总时长',\n    value: computed(() => formatNumber(userData.value?.playtimeStats.totalTwoWeeks) || '--'),\n    type: '时间'\n  }\n]\n\n\u002F\u002F 初始加载数据\nonMounted(async () => {\n  \u002F\u002F 获取基础数据\n  await fetchSteamData(10)\n  \n  \u002F\u002F 如果用户正在游戏中，获取游戏详情\n  if (userData.value?.currentGame) {\n    await fetchGameDetail(userData.value.currentGame.appid)\n  }\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"SteamStatus-overviewList\">\n    \u003Cdiv class=\"overviewListItem\" v-for=\"item in overviewListItemData\">\n      \u003CIcon class=\"ItemIcon\" :name=\"item.icon\"\u002F>\n      \u003Cdiv class=\"ItemInfo\">\n        \u003Cdiv class=\"ItemInfoLabel\">\n          {{ item.label }}\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"ItemInfoValue\" v-show=\"item.type === '数量'\">\n          {{ item.value }} 个\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"ItemInfoValue\" v-show=\"item.type === '时间'\">\n          {{ item.value }} 小时\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cstyle lang=\"scss\" scoped>\n.SteamStatus-overviewList{ \n  box-shadow: var(--ld-shadow);\n  display: flex;\n  flex-direction: row;\n  background: var(--ld-bg-card);\n  border: 1px solid var(--c-border);\n  border-radius: 0.8em;\n  gap: 0.8em;\n  padding: 0.8em 1em;\n  transition: box-shadow 0.2s;\n  @media (max-width: 600px) {\n    flex-direction: column;\n    gap: .4em;\n    padding: .8em 1em;\n  }\n  .overviewListItem{\n    align-items: center;\n    display: flex;\n    flex: 1 1 0%;\n    gap: 0.6em;\n    padding: 0.3em 0px;\n    @media (max-width: 600px) {\n      flex: auto;\n      gap: .6em;\n      padding: .3em 0;\n    }\n    .ItemIcon {\n      font-size: 28px;\n      color: var(--c-primary);\n      flex-shrink: 0;\n    }\n    .ItemInfo {\n      display: flex;\n      flex-direction: column;\n      flex: 1 1 0%;\n      gap: 0px;\n      .ItemInfoLabel {\n        color: var(--c-text-2);\n        font-size: 0.75em;\n        font-weight: 600;\n        @media (max-width: 600px) {\n          font-size: 0.7em;\n        }\n      }\n      .ItemInfoValue {\n        color: var(--c-primary);\n        font-size: 0.95em;\n        font-weight: 700;\n        line-height: 1.2;\n        @media (max-width: 600px) {\n          font-size: .85em;\n        }\n      }\n    }\n  }\n}\n.overviewListItem:not(:last-child) {\n  padding-right: 0.8em;\n  border-right: 1px solid var(--c-border);\n  @media (max-width: 600px) {\n    border-bottom: 1px solid var(--c-border);\n    border-right: none;\n    padding-bottom: .4em;\n    padding-right: 0;\n  }\n}\n\u003C\u002Fstyle>\n","overview.vue",[27,58,55],{"__ignoreMap":29},[38,60,61],{"v-slot:tab3":29},[34,62,64,73],{":tabs":63},"[\"最近游玩\", \"全部游戏\"]",[38,65,66],{"v-slot:tab1":29},[18,67,71],{"className":68,"code":69,"filename":70,"language":46,"meta":47},[43],"\u003Cscript setup lang=\"ts\">\nimport GameTitle from '.\u002FgameTitle.vue';\n\nconst { \n  fetchSteamData, \n  fetchGameDetail,\n  formatPlaytime, \n  formatSteamTime,\n  userData, \n  gamesData, \n  achievementsData,\n  statusTextMap, \n  statusColorMap \n} = useSteamAPIGet()\n\n\u002F\u002F 初始加载数据\nonMounted(async () => {\n  \u002F\u002F 获取基础数据\n  await fetchSteamData(10)\n  \n  \u002F\u002F 如果用户正在游戏中，获取游戏详情\n  if (userData.value?.currentGame) {\n    await fetchGameDetail(userData.value.currentGame.appid)\n  }\n})\n\nconst textAPI = gamesData.value?.recentGames\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"SteamGameMain\">\n    \u003CGameTitle title=\"最近游玩\" icon=\"game-controller-bold\" :sub-title=\"`显示最近两周玩的${gamesData?.recentCount}款游戏`\"\u002F>\n    \u003Cdiv class=\"SteamGameList\">\n      \u003Ca class=\"GameListCard\" v-for=\"game in gamesData?.recentGames\" :href=\"`https:\u002F\u002Fsteamcommunity.com\u002Fapp\u002F${game.appid}`\" target=\"_blank\">\n        \u003Cdiv class=\"ListCardHeader\">\n          \u003CNuxtImg class=\"CardHeaderImage\" :src=\"game.images.headerImage\" \u002F>\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"ListCardBody\">\n          \u003Cdiv class=\"CardBodyInfo\">\n            \u003Ch3 class=\"BodyInfoTitle\">{{ game.name }}\u003C\u002Fh3>\n            \u003Cdiv class=\"BodyInfoStatus\">\n              \u003Cdiv class=\"InfoStatusRow\">\n                \u003Cdiv class=\"StatusRowText\">\n                  \u003Cdiv class=\"RowTextLabel\">\n                    总时长\n                  \u003C\u002Fdiv>\n                  \u003Cdiv class=\"RowTextValue\">\n                    {{ game.playtimeForever }}h\n                  \u003C\u002Fdiv>\n                \u003C\u002Fdiv>\n                \u003CBadge class=\"StatusRowBadge\" :text=\"`最近${ game.playtimeTwoWeeks }h`\" \u002F>\n              \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n          \u003C\u002Fdiv>\n          \u003Cdiv class=\"ListCardAchievements\">\n            \u003Cdiv class=\"CardAchievementsInfo\">\n              \u003Cdiv class=\"AchievementsInfoLabel\">\n                \u003CIcon class=\"InfoLabelIcon\" name=\"i-ph:trophy-bold\" :style=\"`color: ${game.achievements?.percentage === game.achievements?.total}`\"\u002F>\n                \u003Cspan class=\"InfoLabelCount\">\n                  {{ game.achievements?.unlocked }} \u002F {{ game.achievements?.total }}\n                \u003C\u002Fspan>\n              \u003C\u002Fdiv>\n              \u003Cspan class=\"GamePriceNumber\">{{ game.price.displayPrice }}\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n            \u003Cdiv class=\"CardAchievementsProgress\">\n              \u003Cdiv class=\"AchievementsProgressContainer\" style=\"height: 6px;\">\n                \u003Cdiv class=\"ProgressCcontainerBar\" :style=\"`width: ${game.achievements?.percentage}%`\" \u002F>\n              \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n          \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n      \u003C\u002Fa>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cstyle lang=\"scss\" scoped>\n.SteamGameMain{\n  .SteamGameList {\n    display: flex;\n    flex-direction: column;\n    gap: .5em;\n    min-width: 0;\n    overflow: auto hidden;\n    padding-bottom: .5em;\n    scroll-behavior: smooth;\n    width: 100%;\n    @media (max-width: 767px) {\n      gap: .4em;\n      padding-bottom: .4em;\n    }\n    .GameListCard {\n      background: var(--ld-bg-card);\n      border: 1px solid var(--c-border);\n      border-radius: 12px;\n      cursor: pointer;\n      display: flex;\n      height: 120px;\n      overflow: hidden;\n      position: relative;\n      transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;\n      width: 100%;\n      @media (max-width: 640px) {\n        flex-direction: column;\n        height: auto;\n      }\n      .ListCardHeader {\n        flex-shrink: 0;\n        max-width: 220px;\n        min-width: 140px;\n        overflow: hidden;\n        position: relative;\n        width: 35%;\n        @media (max-width: 640px) {\n          height: 140px;\n          max-width: none;\n          width: 100%;\n        }\n        .CardHeaderImage {\n          height: 100%;\n          -o-object-fit: cover;\n          object-fit: cover;\n          transition: transform .4s ease;\n          width: 100%;\n        }\n      }\n      .ListCardBody {\n        flex: 1;\n        justify-content: space-between;\n        min-width: 0;\n        padding: 12px 16px;\n        display: flex;\n        flex-direction: column;\n        @media (max-width: 640px) {\n          gap: 16px;\n          padding: 12px;\n        }\n        .CardBodyInfo {\n          display: flex;\n          flex-direction: column;\n          gap: 6px;\n          .BodyInfoTitle {\n            color: var(--c-text);\n            display: -webkit-box;\n            font-size: 1rem;\n            font-weight: 700;\n            -webkit-line-clamp: 1;\n            line-clamp: 1;\n            line-height: 1.3;\n            margin: 0;\n            overflow: hidden;\n            -webkit-box-orient: vertical;\n          }\n          .BodyInfoStatus {\n            color: var(--c-text-2);\n            flex-wrap: wrap;\n            font-size: .85rem;\n            gap: 12px;\n            .InfoStatusRow {\n              align-items: center;\n              display: flex;\n              gap: 6px;\n              .StatusRowText {\n                align-items: baseline;\n                display: flex;\n                gap: 4px;\n                .RowTextLabel {\n                  color: var(--c-text-2);\n                  font-size: .9em;\n                  font-weight: 600;\n                  margin-bottom: .2em;\n                  font-synthesis: weight style;\n                }\n                .RowTextValue {\n                  color: var(--c-text-1);\n                  font-family: var(--font-monospace, monospace);\n                  font-weight: 600;\n                  font-size: 1.5em;\n                }\n              }\n              .StatusRowBadge:not([href]) {\n                background-color: var(--c-primary-soft, color-mix(in srgb, var(--c-primary) 10%, transparent));\n                border-color: var(--c-primary-light, color-mix(in srgb, var(--c-primary) 30%, transparent));\n                color: var(--c-primary);\n              }\n            }\n          }\n        }\n        .ListCardAchievements {\n          margin-top: auto;\n          .CardAchievementsInfo {\n            align-items: center;\n            display: flex;\n            font-size: .8rem;\n            justify-content: space-between;\n            margin-bottom: 6px;\n            .AchievementsInfoLabel {\n              align-items: center;\n              color: var(--c-text-2);\n              display: flex;\n              gap: 6px;\n            }\n          }\n          .CardAchievementsProgress {\n            align-items: center;\n            display: flex;\n            gap: .5rem;\n            width: 100%;\n            .AchievementsProgressContainer {\n              background: var(--c-bg-2);\n              border-radius: 2px;\n              cursor: pointer;\n              flex: 1;\n              min-width: 0;\n              overflow: visible;\n              position: relative;\n              transition: background-color .15s ease;\n              -webkit-user-select: none;\n              -moz-user-select: none;\n              user-select: none;\n              .ProgressCcontainerBar {\n                background: var(--c-primary);\n                border-radius: 2px;\n                height: 100%;\n                position: relative;\n                transition: width .1s linear;\n              } \n            }\n          }\n        }\n      }\n    }\n  }\n}\n.InfoLabelIcon {\n    color: #10b981;\n}\n\u003C\u002Fstyle>\n","recentGames.vue",[27,72,69],{"__ignoreMap":29},[38,74,75],{"v-slot:tab2":29},[18,76,80],{"className":77,"code":78,"filename":79,"language":46,"meta":47},[43],"\u003Cscript setup lang=\"ts\">\nimport GameTitle from '.\u002FgameTitle.vue';\n\nconst { \n  fetchSteamData, \n  fetchGameDetail,\n  formatPlaytime, \n  formatSteamTime,\n  userData, \n  gamesData, \n  achievementsData,\n  statusTextMap, \n  statusColorMap\n} = useSteamAPIGet()\n\n\u002F\u002F 初始加载数据\nonMounted(async () => {\n  \u002F\u002F 获取基础数据\n  await fetchSteamData(10)\n  \n  \u002F\u002F 如果用户正在游戏中，获取游戏详情\n  if (userData.value?.currentGame) {\n    await fetchGameDetail(userData.value.currentGame.appid)\n  }\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"SteamGameMain\">\n    \u003CGameTitle title=\"游戏库\" icon=\"stack-bold\" :sub-title=\"`显示游玩时长最多的${gamesData?.totalCount}款游戏`\"\u002F>\n    \u003Cdiv class=\"SteamGameList\">\n      \u003Ca class=\"GameListCard\" v-for=\"game in gamesData?.allGames\" :href=\"`https:\u002F\u002Fsteamcommunity.com\u002Fapp\u002F${game.appid}`\" target=\"_blank\">\n        \u003Cdiv class=\"ListCardHeader\">\n          \u003CNuxtImg class=\"CardHeaderImage\" :src=\"game.images.headerImage\" \u002F>\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"ListCardBody\">\n          \u003Cdiv class=\"CardBodyInfo\">\n            \u003Ch3 class=\"BodyInfoTitle\">{{ game.name }}\u003C\u002Fh3>\n            \u003Cdiv class=\"BodyInfoStatus\">\n              \u003Cdiv class=\"InfoStatusRow\">\n                \u003Cdiv class=\"StatusRowText\">\n                  \u003Cdiv class=\"RowTextLabel\">\n                    总时长\n                  \u003C\u002Fdiv>\n                  \u003Cdiv class=\"RowTextValue\">\n                    {{ game.playtimeForever }}h\n                  \u003C\u002Fdiv>\n                \u003C\u002Fdiv>\n                \u003CBadge class=\"StatusRowBadge\" :text=\"`最近${ game.playtimeTwoWeeks }h`\" \u002F>\n              \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n          \u003C\u002Fdiv>\n          \u003Cdiv class=\"ListCardAchievements\">\n            \u003Cdiv class=\"CardAchievementsInfo\">\n              \u003Cdiv class=\"AchievementsInfoLabel\">\n                \u003CIcon class=\"InfoLabelIcon\" name=\"i-ph:trophy-bold\" :style=\"`color: ${game.achievements?.percentage === game.achievements?.total}`\"\u002F>\n                \u003Cspan class=\"InfoLabelCount\">\n                  {{ game.achievements?.unlocked }} \u002F {{ game.achievements?.total }}\n                \u003C\u002Fspan>\n              \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n            \u003Cdiv class=\"CardAchievementsProgress\">\n              \u003Cdiv class=\"AchievementsProgressContainer\" style=\"height: 6px;\">\n                \u003Cdiv class=\"ProgressCcontainerBar\" :style=\"`width: ${game.achievements?.percentage}%`\" \u002F>\n              \u003C\u002Fdiv>\n            \u003C\u002Fdiv>\n          \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n      \u003C\u002Fa>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cstyle lang=\"scss\" scoped>\n.SteamGameMain{\n  .SteamGameList {\n    display: flex;\n    flex-direction: column;\n    gap: .5em;\n    min-width: 0;\n    overflow: auto hidden;\n    padding-bottom: .5em;\n    scroll-behavior: smooth;\n    width: 100%;\n    @media (max-width: 767px) {\n      gap: .4em;\n      padding-bottom: .4em;\n    }\n    .GameListCard {\n      background: var(--ld-bg-card);\n      border: 1px solid var(--c-border);\n      border-radius: 12px;\n      cursor: pointer;\n      display: flex;\n      height: 120px;\n      overflow: hidden;\n      position: relative;\n      transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;\n      width: 100%;\n      @media (max-width: 640px) {\n        flex-direction: column;\n        height: auto;\n      }\n      .ListCardHeader {\n        flex-shrink: 0;\n        max-width: 220px;\n        min-width: 140px;\n        overflow: hidden;\n        position: relative;\n        width: 35%;\n        @media (max-width: 640px) {\n          height: 140px;\n          max-width: none;\n          width: 100%;\n        }\n        .CardHeaderImage {\n          height: 100%;\n          -o-object-fit: cover;\n          object-fit: cover;\n          transition: transform .4s ease;\n          width: 100%;\n        }\n      }\n      .ListCardBody {\n        flex: 1;\n        justify-content: space-between;\n        min-width: 0;\n        padding: 12px 16px;\n        display: flex;\n        flex-direction: column;\n        @media (max-width: 640px) {\n          gap: 16px;\n          padding: 12px;\n        }\n        .CardBodyInfo {\n          display: flex;\n          flex-direction: column;\n          gap: 6px;\n          .BodyInfoTitle {\n            color: var(--c-text);\n            display: -webkit-box;\n            font-size: 1rem;\n            font-weight: 700;\n            -webkit-line-clamp: 1;\n            line-clamp: 1;\n            line-height: 1.3;\n            margin: 0;\n            overflow: hidden;\n            -webkit-box-orient: vertical;\n          }\n          .BodyInfoStatus {\n            color: var(--c-text-2);\n            flex-wrap: wrap;\n            font-size: .85rem;\n            gap: 12px;\n            .InfoStatusRow {\n              align-items: center;\n              display: flex;\n              gap: 6px;\n              .StatusRowText {\n                align-items: baseline;\n                display: flex;\n                gap: 4px;\n                .RowTextLabel {\n                  color: var(--c-text-2);\n                  font-size: .9em;\n                  font-weight: 600;\n                  margin-bottom: .2em;\n                  font-synthesis: weight style;\n                }\n                .RowTextValue {\n                  color: var(--c-text-1);\n                  font-family: var(--font-monospace, monospace);\n                  font-weight: 600;\n                  font-size: 1.5em;\n                }\n              }\n              .StatusRowBadge:not([href]) {\n                background-color: var(--c-primary-soft, color-mix(in srgb, var(--c-primary) 10%, transparent));\n                border-color: var(--c-primary-light, color-mix(in srgb, var(--c-primary) 30%, transparent));\n                color: var(--c-primary);\n              }\n            }\n          }\n        }\n        .ListCardAchievements {\n          margin-top: auto;\n          .CardAchievementsInfo {\n            align-items: center;\n            display: flex;\n            font-size: .8rem;\n            justify-content: space-between;\n            margin-bottom: 6px;\n            .AchievementsInfoLabel {\n              align-items: center;\n              color: var(--c-text-2);\n              display: flex;\n              gap: 6px;\n            }\n          }\n          .CardAchievementsProgress {\n            align-items: center;\n            display: flex;\n            gap: .5rem;\n            width: 100%;\n            .AchievementsProgressContainer {\n              background: var(--c-bg-2);\n              border-radius: 2px;\n              cursor: pointer;\n              flex: 1;\n              min-width: 0;\n              overflow: visible;\n              position: relative;\n              transition: background-color .15s ease;\n              -webkit-user-select: none;\n              -moz-user-select: none;\n              user-select: none;\n              .ProgressCcontainerBar {\n                background: var(--c-primary);\n                border-radius: 2px;\n                height: 100%;\n                position: relative;\n                transition: width .1s linear;\n              } \n            }\n          }\n        }\n      }\n    }\n  }\n}\n.InfoLabelIcon {\n    color: #10b981;\n}\n\u003C\u002Fstyle>\n","allGame.vue",[27,81,78],{"__ignoreMap":29},[10,83,84],{"id":84},"更新日志",[86,87,88],"p",{},[89,90,91],"strong",{},"V0.20260327.78999.68.90WER_PRE",[93,94,95,103],"ul",{},[96,97,98,99,102],"li",{},"1.对",[27,100,101],{"code":101},"最近游戏","模块增加滚动功能，可以通过Shift + 空格或者滑动来查看",[96,104,105,106,109,110,113,114,117],{},"2.对",[27,107,108],{"code":108},"顶部标题","模块的右侧新增",[27,111,112],{"code":112},"tip显示","，可以通过写入",[27,115,116],{"code":116},"sub-tip","配置项来进行调用",[86,119,120],{},[89,121,122],{},"V0.20260323.14599.11.0_PRE",[93,124,125],{},[96,126,127],{},"1.优化后端API获取模块中对于链接请求重复过多的问题",[86,129,130],{},[89,131,132],{},"V0.20260322.7688.8.0_PRE",[93,134,135,151,166],{},[96,136,137,138,141,142,141,145,141,147,150],{},"1.优化",[27,139,140],{"code":140},"用户面板","、",[27,143,144],{"code":144},"信息面板",[27,146,101],{"code":101},[27,148,149],{"code":149},"全部游戏","四个模块的移动端不同尺寸的适配",[96,152,153,154,141,156,158,159,161,162,165],{},"2.分离",[27,155,101],{"code":101},[27,157,149],{"code":149},"两个模块的顶部信息栏，添加到",[27,160,108],{"code":108},"的模块中，并且进行特殊化配置项(即",[27,163,164],{"code":164},"defineProps","写法)",[96,167,168,169,141,171,173,174,176,177,180],{},"3.对",[27,170,101],{"code":101},[27,172,149],{"code":149},"的成就显示与具体百分比显示，并且在",[27,175,101],{"code":101},"中新增价格显示(即",[27,178,179],{"code":179},"price","配置)",[86,182,183],{},[89,184,185],{},"V0.20260322.6788.7.0_PRE",[93,187,188],{},[96,189,98,190,193,194,196,197,200],{},[27,191,192],{"code":192},"游戏面板","进行分离模块，新增",[27,195,101],{"code":101},"与",[27,198,199],{"code":199},"全游戏","的模块，并且优化两个逻辑",[86,202,203],{},[89,204,205],{},"V0.20260321.6358.2.0_PRE",[93,207,208,221,238,241],{},[96,209,137,210,141,212,141,214,216,217,220],{},[27,211,140],{"code":140},[27,213,144],{"code":144},[27,215,192],{"code":192},"三个模块的加载与逻辑运算，并且接入后端API获取模块(",[27,218,219],{"code":219},"V0.20260321.6358.1.0_PRE","更新内容第二项)",[96,222,223,224,141,226,141,228,230,231,196,234,237],{},"2.优化",[27,225,140],{"code":140},[27,227,144],{"code":144},[27,229,192],{"code":192},"三个模块的样式，并且重构",[27,232,233],{"code":233},"vue template",[27,235,236],{"code":236},"scss","的具体写法",[96,239,240],{},"3.修复在获取API的过程中出现的样式错乱",[96,242,243],{},"4.优化后端API获取模块中的数据",[86,245,246],{},[89,247,219],{},[93,249,250,260],{},[96,251,252,253,141,255,141,257,259],{},"1.新增",[27,254,140],{"code":140},[27,256,144],{"code":144},[27,258,192],{"code":192},"模块的基础框架（用于测试数据是否正常）",[96,261,262],{},"2.新增后端API获取模块",{"title":29,"searchDepth":264,"depth":264,"links":265},4,[266,272],{"id":12,"depth":267,"text":12,"children":268},2,[269,271],{"id":16,"depth":270,"text":16},3,{"id":32,"depth":270,"text":32},{"id":84,"depth":267,"text":84},[274],"站点魔改","2026-03-21 14:00:00","该文章主要写了对于低价机器的试水，并提醒是超开类型的机器。在测试的过程中发现机器性能较高，且展示出机器的具体价格，并单独列出只有精简版未采用完整版测试。",false,"md","\u002Fimage\u002FPostCover\u002FfooterNuxtMeihua.avif",{"slots":281},{},true,"\u002F2026\u002F03\u002Fsteamgamepage",null,{"text":286,"minutes":287,"time":288,"words":289},"18 min read",17.265,1035900,3453,{"title":5,"description":276},{"loc":283},"posts\u002F2026\u002F03\u002FsteamGamePage",[294,295],"Nuxt","页面","tech","2026-03-22 10:00:00","BK5SKR5-W_0Dh6alsCKv-3SWqR2D3FrOsNZqjP_vCQ4",[300,317,332,346,360,373,386,398,412,424,435,449,464,479,492,509,523,538,553,568,583,599,616,632,644,658,671,683,697,709,719,731,742,746,757,768,781,790],{"categories":301,"date":303,"description":304,"image":284,"path":305,"readingTime":306,"recommend":284,"tags":311,"title":315,"type":296,"updated":316},[302],"技术探索","2023-12-06 12:45:00","本篇转载三种不同方案（已经放好原文章链接）","\u002F2023\u002F03\u002Fessaythree",{"text":307,"minutes":308,"time":309,"words":310},"48 min read",47.34,2840400,9468,[312,313,314],"hexo","butterfly","美化","即刻短文的三种部署方案","2023-12-07 14:09:00",{"categories":318,"date":319,"description":320,"image":321,"path":322,"readingTime":323,"recommend":328,"tags":329,"title":330,"type":296,"updated":331},[274],"2024-04-19 10:00:00","友链页面美化以及添加功能","\u002Fimage\u002FPostCover\u002FflinkPage.avif","\u002F2024\u002F04\u002Fflinkpagemeihua",{"text":324,"minutes":325,"time":326,"words":327},"11 min read",10.8,648000,2160,1,[312,313],"友链魔改","2025-04-19 12:09:00",{"categories":333,"date":334,"description":335,"image":336,"path":337,"readingTime":338,"recommend":328,"tags":343,"title":344,"type":296,"updated":345},[274],"2025-02-05 09:00:00","这篇文章讲述在博客中对导航栏进行修改，并添加多个产品页面地址，且采用安知鱼的导航栏CSS样式表。","\u002Fimage\u002FPostCover\u002FnavMuogai.avif","\u002F2025\u002F02\u002Fbutterflynavadd",{"text":339,"minutes":340,"time":341,"words":342},"9 min read",8.585,515100,1717,[312,313],"butterfly导航栏修改方案","2025-03-05 10:00:00",{"categories":347,"date":348,"description":349,"image":350,"path":351,"readingTime":352,"recommend":328,"tags":357,"title":358,"type":296,"updated":359},[274],"2025-02-27 10:10:55","本篇文章讲述了如何在博客的底部模块进行魔改添加图标、来源图片、返回顶部以及建站时间，对建站时间进行js内联到模块中。","\u002Fimage\u002FPostCover\u002FfooterMeihua.avif","\u002F2025\u002F02\u002Ffooterqcqxstyle",{"text":353,"minutes":354,"time":355,"words":356},"4 min read",3.31,198600,662,[312,313,314],"轻笑底部美化","2025-02-28 10:00:00",{"categories":361,"date":362,"description":363,"image":364,"path":365,"readingTime":366,"recommend":284,"tags":370,"title":371,"type":296,"updated":372},[274],"2025-03-24 08:00:09","这篇简述如何给自己博客中的归档、分类、标签页三个页面的文章卡片加上所属分类和标签，并说明了具体的实践环境，以最大限度的方式进行CSS美化。但也请注意要经常备份以免出现大量错误。","\u002Fimage\u002FPostCover\u002FarchiveMougai.avif","\u002F2025\u002F03\u002Farchivemougai",{"text":353,"minutes":367,"time":368,"words":369},3.08,184800,616,[312,313],"给页面加上所属分类和标签以及美化","2025-03-24 10:56:09",{"categories":374,"date":375,"description":376,"image":377,"path":378,"readingTime":379,"recommend":284,"tags":383,"title":384,"type":296,"updated":385},[274],"2025-03-05 08:00:00","这篇文章讲述如何给自己博客中的归档、分类、标签页三个页面的文章卡片添加数字来进行编排，以及对添加的数字进行CSS美化，但也请注意要经常备份以免出现错误。","https:\u002F\u002Fsourceimage.s3.bitiful.net\u002Fimg\u002Fdefault_cover_29.avif?v=20260104","\u002F2025\u002F03\u002Fartice-sort",{"text":353,"minutes":380,"time":381,"words":382},3.48,208800,696,[312,313],"美化文章卡片显示数字","2025-03-06 18:00:00",{"categories":387,"date":375,"description":388,"image":389,"path":390,"readingTime":391,"recommend":284,"tags":396,"title":397,"type":296,"updated":385},[274],"这篇文章讲述如何在页面中添加模块以及使用CSS添加颜色以及背景图片。","https:\u002F\u002Fsourceimage.s3.bitiful.net\u002Fimg\u002Fdefault_cover_21.avif","\u002F2025\u002F03\u002Fbackgroud-qcqx",{"text":392,"minutes":393,"time":394,"words":395},"1 min read",0.71,42600,142,[312,313],"轻笑风格背景",{"categories":399,"date":400,"description":401,"image":402,"path":403,"readingTime":404,"recommend":284,"tags":409,"title":410,"type":296,"updated":411},[274],"2025-03-24 09:00:00","本文章以安知鱼的页面文件为模版进行大幅度修改，仿轻笑的关于页面的模块摆放以及调整CSS样式来进行细致魔改。","\u002Fimage\u002FPostCover\u002FbtfAboutPage.avif","\u002F2025\u002F03\u002Fbtfaboutpage",{"text":405,"minutes":406,"time":407,"words":408},"24 min read",23.275,1396500,4655,[312,313],"关于页面(butterfly)","2025-03-25 11:20:00",{"categories":413,"date":375,"description":414,"image":415,"path":416,"readingTime":417,"recommend":284,"tags":422,"title":423,"type":296,"updated":385},[274],"这篇文章讲述了如何美化博客侧边栏，并调整各个卡片内容的样式表。","\u002Fimage\u002FPostCover\u002FcardMeihua.avif","\u002F2025\u002F03\u002Fcardmeihua",{"text":418,"minutes":419,"time":420,"words":421},"5 min read",4.77,286200,954,[312,313],"卡片美化",{"categories":425,"date":375,"description":426,"image":427,"path":428,"readingTime":429,"recommend":284,"tags":433,"title":434,"type":296,"updated":385},[274],"从零开始魔改butterfly","\u002Fimage\u002FPostCover\u002FfunctionMeihua.avif","\u002F2025\u002F03\u002Ffunctionmeihua",{"text":392,"minutes":430,"time":431,"words":432},0.345,20700,69,[312,313],"功能美化",{"categories":436,"date":437,"description":438,"image":439,"path":440,"readingTime":441,"recommend":284,"tags":446,"title":447,"type":296,"updated":448},[274],"2025-03-01 10:00:00","本文依照轻笑的部分内容，参考首页banner写法以及对首页banner添加鼠标滚动以及移动端滑动，通过CSS样式进行美化整个首页banner，此篇文章重点讲述了如何解决轻笑给的js内容在首页会出现不断加载的问题。","\u002Fimage\u002FPostCover\u002FrandomPost.avif","\u002F2025\u002F03\u002Frandompost",{"text":442,"minutes":443,"time":444,"words":445},"6 min read",5.595,335700,1119,[312,313,314],"Banner随机文章展示(轻笑同款)","2025-03-05 12:09:00",{"categories":450,"date":451,"description":452,"image":453,"path":454,"readingTime":455,"recommend":459,"tags":460,"title":462,"type":296,"updated":463},[274],"2025-08-11 10:00:00","本篇文章讲述了添加模块代码并在博客的友情链接中添加模块，且使用css美化","\u002Fimage\u002FPostCover\u002FlinkTop.avif","\u002F2025\u002F08\u002Flinktop",{"text":339,"minutes":456,"time":457,"words":458},8.88,532800,1776,11,[294,461,314],"魔改","在友链页面添加滚动头像banner","2025-08-11 20:49:00",{"categories":465,"date":466,"description":467,"image":468,"path":469,"readingTime":470,"recommend":475,"tags":476,"title":477,"type":296,"updated":478},[274],"2025-08-22 10:00:00","近期将博客迁移至Nuxt框架时，重新搭建了「关于页面」。过程中添加了技能展示组件（skillinfo.vue），实现标签分组渲染；新建about.vue页面，整合个人信息、技能、偏好等多模块内容，并通过ts定义数据接口（about.ts\u002Fcreativity.ts）。","\u002Fimage\u002FPostCover\u002FnuxtAboutPage.avif","\u002F2025\u002F08\u002Fnuxtaboutpage",{"text":471,"minutes":472,"time":473,"words":474},"16 min read",15.92,955200,3184,10,[294,461,314],"关于页面(Nuxt)","2025-09-01 20:49:00",{"categories":480,"date":481,"description":482,"image":483,"path":484,"readingTime":485,"recommend":489,"tags":490,"title":491,"type":296,"updated":478},[274],"2025-09-02 10:00:00","本篇文章主要简述了如何给Nuxt框架添加站点详情","https:\u002F\u002Fwww.yjluo.top\u002Fimage\u002FPostCover\u002FsiteInfo.avif","\u002F2025\u002F09\u002Fsitelinkpageadd",{"text":418,"minutes":486,"time":487,"words":488},4.38,262800,876,9,[294,461,314],"添加站点详情页面",{"categories":493,"date":495,"description":496,"image":497,"path":498,"readingTime":499,"recommend":284,"tags":503,"title":506,"type":507,"updated":508},[494],"日志记录","2025-12-28 10:00:00","该文章详细记录了2025年中的上下半年所做的事情，并且还透露出后续计划中会干什么。而在前言中也写到此文为水字数而出，上下半年均写到更换框架与服务有关于的内容。对于未来计划中提到了要对一些网站进行恢复、以及明年将在文章中加入与自创小说有关的内容","\u002Fimage\u002FPostCover\u002FannualSummary.avif","\u002F2025\u002F12\u002Fannualsummary",{"text":353,"minutes":500,"time":501,"words":502},3.855,231300,771,[504,505],"总结",2025,"年度总结：旧去新","story","2025-12-28 20:49:00",{"categories":510,"date":511,"description":512,"image":513,"path":514,"readingTime":515,"recommend":489,"tags":520,"title":521,"type":296,"updated":522},[274],"2025-12-01 10:00:00","该文章详细介绍了基于Vue3+TypeScript开发的豆瓣追更记录系统，采用组件化架构实现两栏筛选菜单、动态加载动画和卡片式作品展示，通过Pinia状态管理+Vite构建工具实现数据流管理，集成防抖加载、虚拟滚动等性能优化方案。","\u002Fimage\u002FPostCover\u002FbanguimPage.avif","\u002F2025\u002F12\u002Fbanguimpageadd",{"text":516,"minutes":517,"time":518,"words":519},"23 min read",22.175,1330500,4435,[294,461,314],"添加追更历史","2025-12-01 20:49:00",{"categories":524,"date":525,"description":526,"image":527,"path":528,"readingTime":529,"recommend":534,"tags":535,"title":536,"type":296,"updated":537},[274],"2025-12-23 10:00:00","该文章介绍Nuxt博客适配中评论模块的Vue组件实现与Artalk评论系统单例管理逻辑，并提供具体的适配评论表情包的Json信息，评论功能有着KaTeX数学公式渲染、图片灯箱、动态监听以及管理逻辑的初始化、计数、暗黑模式切换等方式。","\u002Fimage\u002FPostCover\u002FcommentMeihua.avif","\u002F2025\u002F12\u002Fcommentadd",{"text":530,"minutes":531,"time":532,"words":533},"32 min read",31.72,1903200,6344,8,[294,461,314],"评论优化","2026-03-01 20:49:00",{"categories":539,"date":540,"description":541,"image":542,"path":543,"readingTime":544,"recommend":549,"tags":550,"title":551,"type":296,"updated":552},[274],"2025-12-03 10:05:09","该博客持续维护Nuxt框架博客系统，新增装备页面实现硬件\u002F外设分类展示，支持动态过滤、标签筛选与规格参数渲染，通过TypeScript定义数据接口，结合Vue3响应式布局与SCSS响应式设计，完成设备卡片动态加载、悬停动效及跨端适配优化，集成评论跳转与购买信息展示功能。","\u002Fimage\u002FPostCover\u002FequipmentPage.avif","\u002F2025\u002F12\u002Fequipmentpageadd",{"text":545,"minutes":546,"time":547,"words":548},"8 min read",7.28,436800,1456,7,[294,461,314],"添加装备页面","2025-12-03 20:49:09",{"categories":554,"date":555,"description":556,"image":557,"path":558,"readingTime":559,"recommend":564,"tags":565,"title":566,"type":296,"updated":567},[274],"2025-12-09 10:00:00","该文章记录了项目版本迭代中的UI优化与功能调整，包括增加可后期换配置且内置移动到赞赏总览触发效果的打赏弹窗、优化头部封面移动端预览、增加版权图标虚化及打赏入口、将本地desc预览改ai摘要样式、增加版权卡片及更换头部信息样式。","\u002Fimage\u002FPostCover\u002FpostMeihua.avif","\u002F2025\u002F12\u002Fpostpagexiugai",{"text":560,"minutes":561,"time":562,"words":563},"12 min read",11.165,669900,2233,6,[294,461,314],"文章美化","2025-12-09 20:49:00",{"categories":569,"date":570,"description":571,"image":572,"path":573,"readingTime":574,"recommend":579,"tags":580,"title":581,"type":296,"updated":582},[274],"2025-12-04 10:00:00","该文章记录了项目版本迭代中的UI优化与功能调整，包括站点详情卡片组件化改造（采用Badge组件优化布局）、分类卡片新增文章数量统计功能（重新严重问题）、标签卡片新增文章标签统计功能、博主信息模块的拆分与重构，同时删除了冗余的左侧图片和完整博主卡片，最终形成模块化组件结构（涉及5个核心组件及数据调用逻辑调整）。","\u002Fimage\u002FPostCover\u002FsmallCard.avif","\u002F2025\u002F12\u002Fsmallcardadd",{"text":575,"minutes":576,"time":577,"words":578},"10 min read",9.745,584700,1949,5,[294,461,314],"侧边组件美化","2025-12-04 20:49:00",{"categories":584,"date":585,"description":586,"image":587,"path":588,"readingTime":589,"recommend":284,"tags":594,"title":597,"type":296,"updated":598},[274],"2026-01-27 10:00:00","自定义全局的样式颜色，具有对特定CSS中所具有的自定义样式颜色来进行整合，并且作者还顺便水了一篇文章","\u002Fimage\u002FPostCover\u002FcolorStyleAll.avif","\u002F2026\u002F01\u002Fcolorstyleall",{"text":590,"minutes":591,"time":592,"words":593},"14 min read",13.155,789300,2631,[595,596],"全局颜色","自定义","自定义全局颜色","2026-01-27 20:49:00",{"categories":600,"date":601,"description":602,"image":603,"path":604,"readingTime":605,"recommend":284,"tags":610,"title":614,"type":296,"updated":615},[302],"2026-01-11 10:00:00","这篇文章是一篇实战经验分享，主要讲解了如何利用腾讯云的EdgeOne边缘安全加速平台，为网站字体等静态资源搭建一个自定义的、高性能的镜像加速服务。","\u002Fimage\u002FPostCover\u002FjsdmEdge.avif","\u002F2026\u002F01\u002Fjsdmedge",{"text":606,"minutes":607,"time":608,"words":609},"3 min read",2.72,163200,544,[611,612,613],"镜像","EdgeOne","jsdmirror","使用EdgeOne CDN搭建自用Jsd镜像","2026-01-11 20:49:00",{"categories":617,"date":618,"description":619,"image":620,"path":621,"readingTime":622,"recommend":284,"tags":627,"title":630,"type":296,"updated":631},[302],"2026-01-28 19:00:00","本文是一篇关于 Komari Monitor（一款服务器监控系统）的详细介绍与实用指南。文章以作者个人经验为引，对比了哪吒监控（V0\u002FV1版本）与 Komari 在各方面的差异，并逐步演示了如何部署、配置和使用 Komari。","\u002Fimage\u002FPostCover\u002FkomariMonitor.avif","\u002F2026\u002F01\u002Fkomarimonitor",{"text":623,"minutes":624,"time":625,"words":626},"7 min read",6.86,411600,1372,[628,629],"探针","监控","komari：全新的探针站点","2026-01-29 13:00:00",{"categories":633,"date":634,"description":571,"image":635,"path":636,"readingTime":637,"recommend":264,"tags":641,"title":642,"type":296,"updated":643},[274],"2026-01-01 10:00:00","\u002Fimage\u002FPostCover\u002FNewYear.avif","\u002F2026\u002F01\u002Fnewyear",{"text":606,"minutes":638,"time":639,"words":640},2.15,129000,430,[294,461,314],"踏入2026：目标新方向","2026-01-01 20:49:00",{"categories":645,"date":646,"description":647,"image":648,"path":649,"readingTime":650,"recommend":284,"tags":655,"title":656,"type":507,"updated":657},[274],"2026-01-05 10:00:00","该文章记录了项目中对于字体、图片以及构建产物等静态资源的优化与处理，并且表示自身对于图片的存放位置进行优化。","\u002Fimage\u002FPostCover\u002FsiteAssets.avif","\u002F2026\u002F01\u002Fsiteassets",{"text":651,"minutes":652,"time":653,"words":654},"2 min read",1.82,109200,364,[294,461,314],"站点资源优化","2026-01-07 20:49:00",{"categories":659,"date":661,"description":276,"image":662,"path":663,"readingTime":664,"recommend":284,"tags":668,"title":669,"type":296,"updated":670},[660],"日常随笔","2026-01-12 10:00:00","\u002Fimage\u002FPostCover\u002FvpsTalk.avif","\u002F2026\u002F01\u002Fvpstalk",{"text":471,"minutes":665,"time":666,"words":667},15.545,932700,3109,[294,461,314],"随笔：低价主机试水","2026-01-12 20:49:00",{"categories":672,"date":673,"description":674,"image":662,"path":675,"readingTime":676,"recommend":284,"tags":680,"title":681,"type":296,"updated":682},[660],"2026-01-30 10:00:00","该文章主要写了对于大容量硬盘主机的试水。在测试的过程中发现机器性能较高，且展示出机器的具体价格，并单独列出只有精简版未采用完整版测试。","\u002F2026\u002F01\u002Fvpstalk-2",{"text":590,"minutes":677,"time":678,"words":679},13.965,837900,2793,[294,461,314],"随笔：大容量主机测试","2026-01-30 20:49:00",{"categories":684,"date":685,"description":686,"image":687,"path":688,"readingTime":689,"recommend":328,"tags":694,"title":695,"type":296,"updated":696},[274],"2026-02-20 10:00:00","该文章展示多个以鸣潮为主题的档案组件，包含具体代码、属性表格对应、预览整体组件、写法展示四种类型，并在文章末尾附加更新报告。","\u002Fimage\u002FPostCover\u002FWutheringWavesPostWidget.avif","\u002F2026\u002F02\u002Fwutheringwavespostwidget",{"text":690,"minutes":691,"time":692,"words":693},"100 min read",99.23,5953800,19846,[294,461,314],"【鸣潮】档案文章组件","2026-02-26 10:00:00",{"categories":698,"date":699,"description":276,"image":700,"path":701,"readingTime":702,"recommend":284,"tags":706,"title":707,"type":296,"updated":708},[302],"2026-02-03 10:00:00","\u002Fimage\u002FPostCover\u002FfnosInstall.avif","\u002F2026\u002F02\u002Ffnosinstall",{"text":339,"minutes":703,"time":704,"words":705},8.91,534600,1782,[294,461,314],"给老MAC升级为飞牛OS","2026-02-05 20:49:00",{"categories":710,"date":699,"description":276,"image":711,"path":712,"readingTime":713,"recommend":284,"tags":717,"title":718,"type":296,"updated":708},[274],"\u002Fimage\u002FPostCover\u002FmacBookPerformancTest.avif","\u002F2026\u002F02\u002Fmacbookperformanctest",{"text":606,"minutes":714,"time":715,"words":716},2.47,148200,494,[294,461,314],"【精简】测试老MAC性能",{"categories":720,"date":721,"description":276,"image":722,"path":723,"readingTime":724,"recommend":284,"tags":728,"title":729,"type":296,"updated":730},[274],"2026-02-04 10:00:00","\u002Fimage\u002FPostCover\u002FmusicInstall.avif","\u002F2026\u002F02\u002Fmusicinstall",{"text":339,"minutes":725,"time":726,"words":727},8.19,491400,1638,[294,461,314],"【本地+云端】搭建道理鱼音乐","2026-02-06 20:49:00",{"categories":732,"date":733,"description":276,"image":279,"path":734,"readingTime":735,"recommend":270,"tags":739,"title":740,"type":296,"updated":741},[274],"2026-03-03 10:00:00","\u002F2026\u002F03\u002Ffooternuxtmeihua",{"text":353,"minutes":736,"time":737,"words":738},3.235,194100,647,[294,461,314],"页脚魔改(Nuxt版本)","2026-03-06 10:00:00",{"categories":743,"date":275,"description":276,"image":279,"path":283,"readingTime":744,"recommend":267,"tags":745,"title":5,"type":296,"updated":297},[274],{"text":286,"minutes":287,"time":288,"words":289},[294,295],{"categories":747,"date":748,"description":276,"image":279,"path":749,"readingTime":750,"recommend":328,"tags":754,"title":755,"type":296,"updated":756},[274],"2026-04-11 14:00:00","\u002F2026\u002F04\u002Fessaynuxtpage",{"text":442,"minutes":751,"time":752,"words":753},5.93,355800,1186,[294,295],"说说页面（Nuxt版本）","2026-04-11 22:00:00",{"categories":758,"date":759,"description":276,"image":279,"path":760,"readingTime":761,"recommend":328,"tags":765,"title":766,"type":296,"updated":767},[274],"2026-04-13 14:00:00","\u002F2026\u002F04\u002Fhotnuxtpage",{"text":339,"minutes":762,"time":763,"words":764},8.985,539100,1797,[294,295],"热搜页面（Nuxt版本）","2026-04-13 22:00:00",{"categories":769,"date":771,"description":496,"image":772,"path":773,"readingTime":774,"recommend":284,"tags":778,"title":779,"type":507,"updated":780},[770],"自设记录","2025-12-29 10:00:00","https:\u002F\u002Fsourceimage.s3.bitiful.net\u002Fpost\u002Fimg\u002FannualSummary\u002Fcover.webp","\u002Fnovel\u002Fworld\u002Fharmworld",{"text":651,"minutes":775,"time":776,"words":777},1.31,78600,262,[504,505],"世界志：鸿蒙界","2025-12-29 20:49:00",{"categories":782,"date":771,"description":496,"image":772,"path":783,"readingTime":784,"recommend":284,"tags":788,"title":789,"type":507,"updated":780},[770],"\u002Fnovel\u002Fworld\u002Flmmortalgod",{"text":623,"minutes":785,"time":786,"words":787},6.295,377700,1259,[504,505],"世界志：仙神界",{"categories":791,"date":771,"description":496,"image":772,"path":792,"readingTime":793,"recommend":284,"tags":797,"title":798,"type":507,"updated":780},[770],"\u002Fnovel\u002Fworld\u002Fdh",{"text":651,"minutes":794,"time":795,"words":796},1.65,99000,330,[504,505],"世界志：大荒",[800,802],{"title":740,"path":734,"stem":801,"date":733,"type":296,"children":-1},"posts\u002F2026\u002F03\u002FfooterNuxtMeihua",{"title":755,"path":749,"stem":803,"date":748,"type":296,"children":-1},"posts\u002F2026\u002F04\u002FessayNuxtPage",1776745733657]