12451 b553c4e519 feat: API 统一 + 设备详情补全 + H5 导航优化
- API 路由统一: 移除 /api/h5/,统一 /api/pms/ 入口
- 二进制改名: api-server -> pms-api(与服务名一致)
- nginx: 合并 /api/ + /pms/api/ 为 /api/pms/
- 新增 docs/API.md 接口文档
- 后端: CabinetDetail 增加 rssi/pow_fail/address/soc 等字段
- Redis 管道批量读取仓体实时数据(SOC/电压/电流/功率)
- DB: 所有 TIMESTAMP 列改为 DATETIME(修复类型不匹配)
- 用户管理: 新增创建时间/修改时间列,状态改用 Switch
- 角色管理/用户管理: 操作按钮改为图标
- PC 设备详情: 基本信息补全 RSSI/ICCID/电源/地址
- PC 卡片: 通道统计改用 Lucide 图标,颜色统一
- PC 二维码: canvas 渲染,含编号文字,支持右键下载
- H5: 按 uni-app 规范修正导航(navigateTo 替代 reLaunch)
- H5: 详情页加返回导航条
- H5: 扫码支持摄像头(jsQR)+ ?id= 参数
- H5: 底部 tabBar 在所有页面常显
- H5: 首页统计改为 6 项(设备/通道/空闲/充电/充满/故障)
- H5: 下拉刷新 / 上拉加载
- 产品首页: 添加业务联系
- 文档: 清理过时文件,补充缺失规范章节
2026-07-04 04:03:02 +08:00

3.5 KiB

name, description
name description
build-ssr Server-side rendering setup and configuration with Vite

Server-Side Rendering (SSR)

Low-level API for framework authors. For applications, use higher-level tools from Awesome Vite SSR.

Project Structure

├── index.html
├── server.js              # Express/Node server
└── src/
    ├── main.js            # Universal app code
    ├── entry-client.js    # Mounts app to DOM
    └── entry-server.js    # Renders app with SSR API

index.html

<!DOCTYPE html>
<html>
  <body>
    <div id="app"><!--ssr-outlet--></div>
    <script type="module" src="/src/entry-client.js"></script>
  </body>
</html>

Development Server

// server.js
import express from 'express'
import { createServer as createViteServer } from 'vite'

async function createServer() {
  const app = express()

  const vite = await createViteServer({
    server: { middlewareMode: true },
    appType: 'custom'
  })

  app.use(vite.middlewares)

  app.use('*all', async (req, res, next) => {
    const url = req.originalUrl

    try {
      // 1. Read index.html
      let template = fs.readFileSync(
        path.resolve(__dirname, 'index.html'),
        'utf-8'
      )

      // 2. Apply Vite transforms
      template = await vite.transformIndexHtml(url, template)

      // 3. Load server entry
      const { render } = await vite.ssrLoadModule('/src/entry-server.js')

      // 4. Render app HTML
      const appHtml = await render(url)

      // 5. Inject into template
      const html = template.replace('<!--ssr-outlet-->', appHtml)

      res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
    } catch (e) {
      vite.ssrFixStacktrace(e)
      next(e)
    }
  })

  app.listen(5173)
}

createServer()

Conditional Logic

if (import.meta.env.SSR) {
  // Server-only code (tree-shaken on client)
}

Production Build

{
  "scripts": {
    "build:client": "vite build --outDir dist/client",
    "build:server": "vite build --outDir dist/server --ssr src/entry-server.js"
  }
}

Production Server

// Differences from dev:
// 1. Use dist/client/index.html as template
// 2. Use import('./dist/server/entry-server.js') instead of ssrLoadModule
// 3. Serve static files from dist/client

SSR Manifest

For preload directives:

vite build --outDir dist/client --ssrManifest

Generates dist/client/.vite/ssr-manifest.json with module-to-chunk mappings.

SSR Externals

Dependencies are externalized by default. To transform with Vite:

export default defineConfig({
  ssr: {
    noExternal: ['package-that-needs-transform'],
    external: ['package-to-externalize']
  }
})

SSR-specific Plugin Logic

export function mySSRPlugin() {
  return {
    name: 'my-ssr',
    transform(code, id, options) {
      if (options?.ssr) {
        // SSR-specific transform
      }
    }
  }
}

SSR Target

export default defineConfig({
  ssr: {
    target: 'node',      // Default
    // target: 'webworker'  // For edge runtimes
  }
})

SSR Bundle

Bundle all dependencies (for workers):

export default defineConfig({
  ssr: {
    noExternal: true  // Bundle everything
  }
})

Resolve Conditions

export default defineConfig({
  ssr: {
    resolve: {
      conditions: ['node'],
      externalConditions: ['node']
    }
  }
})

Pre-Rendering / SSG

Pre-render routes with known data into static HTML at build time.