最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript實(shí)現(xiàn)HTML頁面轉(zhuǎn)換成PDF的技術(shù)方案

 更新時間:2025年11月27日 08:40:45   作者:至簡簡  
本文將深入講解如何使用 snapdom 和 jsPDF 實(shí)現(xiàn)高質(zhì)量的 HTML 轉(zhuǎn) PDF 功能,并通過一個完整的消息列表導(dǎo)出案例,帶你掌握這套方案的核心技術(shù),需要的朋友可以參考下

背景

為什么 HTML 轉(zhuǎn) PDF 如此重要?

在現(xiàn)代 Web 應(yīng)用中,HTML 轉(zhuǎn) PDF 是一個非常常見的需求場景:

  1. 客服系統(tǒng):導(dǎo)出聊天記錄用于存檔或投訴處理
  2. 電商平臺:生成訂單詳情、發(fā)票等 PDF 文檔
  3. 報表系統(tǒng):將可視化圖表和數(shù)據(jù)導(dǎo)出為 PDF 報告
  4. 在線文檔:支持用戶將網(wǎng)頁內(nèi)容離線保存
  5. 合同簽署:生成合同 PDF 用于電子簽名

然而,實(shí)現(xiàn)一個高質(zhì)量的 HTML 轉(zhuǎn) PDF 功能并不簡單。我們面臨以下挑戰(zhàn):

挑戰(zhàn)描述
樣式還原CSS 樣式、字體、漸變等能否完美呈現(xiàn)?
分頁處理長內(nèi)容如何智能分頁,避免內(nèi)容被截斷?
清晰度導(dǎo)出的 PDF 是否足夠清晰,尤其在打印時?
性能大量內(nèi)容(如 1000 條消息)能否快速導(dǎo)出?
兼容性不同瀏覽器表現(xiàn)是否一致?

傳統(tǒng)的 html2canvas + jsPDF 方案雖然能用,但在樣式還原度截圖質(zhì)量上存在明顯不足。

今天筆者介紹一套新解決方案:snapdom + jsPDF。

snapdom 和 jsPDF 基礎(chǔ)理論知識

snapdom 是什么?

SnapDOM 是一個現(xiàn)代化的 DOM 截圖庫,它的核心特點(diǎn)是:

DOM Element → Canvas/PNG/SVG

核心優(yōu)勢

  1. 高保真截圖:完美還原 CSS 樣式,包括 flexbox、grid、漸變、陰影等
  2. 多種輸出格式:支持 Canvas、PNG、SVG 等多種格式
  3. 高清縮放:通過 scale 參數(shù)實(shí)現(xiàn) 2x/3x 高清截圖
  4. 體積小巧:壓縮后僅 ~20KB

基礎(chǔ)用法

import { snapdom } from '@zumer/snapdom';

// 獲取 DOM 元素
const element = document.querySelector('.my-element');

// 截圖
const capture = await snapdom(element, {
  scale: 2,      // 2倍清晰度
  quality: 0.95  // PNG 質(zhì)量
});

// 輸出方式
const canvas = await capture.toCanvas();  // Canvas 元素
const imgEl = await capture.toPng();      // <img> 元素,src 為 data URL
const svgStr = await capture.toSvg();     // SVG 字符串

關(guān)鍵參數(shù)說明

參數(shù)類型默認(rèn)值說明
scalenumber1縮放倍數(shù),2 表示 2 倍清晰度
qualitynumber0.92圖片質(zhì)量,范圍 0-1

jsPDF 是什么?

jsPDF 是最流行的 JavaScript PDF 生成庫,支持在瀏覽器端直接創(chuàng)建 PDF 文件。

核心特點(diǎn)

  1. 純前端方案:無需服務(wù)端,瀏覽器直接生成
  2. 功能豐富:支持文本、圖片、表格、鏈接等
  3. 多種尺寸:A4、Letter 等標(biāo)準(zhǔn)紙張格式
  4. 插件生態(tài):支持 AutoTable 等擴(kuò)展插件

基礎(chǔ)用法

import { jsPDF } from 'jspdf';

// 創(chuàng)建 PDF 實(shí)例
const pdf = new jsPDF({
  orientation: 'portrait',  // 縱向
  unit: 'mm',               // 單位:毫米
  format: 'a4',             // A4 紙張
  compress: true            // 啟用壓縮
});

// 添加圖片
pdf.addImage(
  imageDataUrl,  // Base64 圖片數(shù)據(jù)
  'PNG',         // 圖片格式
  10,            // X 坐標(biāo)(mm)
  10,            // Y 坐標(biāo)(mm)
  190,           // 寬度(mm)
  100            // 高度(mm)
);

// 添加新頁面
pdf.addPage();

// 保存文件
pdf.save('output.pdf');

A4 尺寸常量

// A4 標(biāo)準(zhǔn)尺寸(單位:mm)
const A4_WIDTH_MM = 210;
const A4_HEIGHT_MM = 297;

// 頁面邊距
const MARGIN_MM = 10;

// 可用內(nèi)容區(qū)域
const CONTENT_WIDTH_MM = 190;   // 210 - 10*2
const CONTENT_HEIGHT_MM = 277;  // 297 - 10*2

snapdom + jsPDF 組合的優(yōu)勢

案例講述

筆者寫一個IM產(chǎn)品中 MessageList 消息導(dǎo)出DEMO。接下來,我們通過一個完整的客服消息列表導(dǎo)出案例,講解如何使用 snapdom + jsPDF 實(shí)現(xiàn) HTML 轉(zhuǎn) PDF。

項目結(jié)構(gòu)

src/
├── components/
│   ├── MessageList.tsx      # 消息列表組件
│   └── MessageList.css      # 消息列表樣式
├── services/
│   └── messageExportService.ts  # PDF 導(dǎo)出服務(wù)(核心)
└── App.tsx

核心流程

整個導(dǎo)出過程分為 4 個步驟

Step 1:DOM 截圖(snapdom)

第一步,使用 snapdom 將整個消息列表 DOM 轉(zhuǎn)換為高清 PNG 圖片。

// messageExportService.ts

import { snapdom } from '@zumer/snapdom';

// 圖片質(zhì)量配置
const IMAGE_QUALITY = 0.95;
const IMAGE_FORMAT = 'image/png' as const;

/**
 * 將 DOM 元素轉(zhuǎn)換為圖片
 */
export async function captureElementToImage(
  element: HTMLElement,
  quality: number = IMAGE_QUALITY
): Promise<string> {
  console.log('開始截圖...');

  // 保存原始樣式
  const originalOverflow = element.style.overflow;
  const originalHeight = element.style.height;
  const originalMaxHeight = element.style.maxHeight;

  // 臨時設(shè)置樣式,確保完整截圖
  element.style.overflow = 'visible';
  element.style.height = 'auto';
  element.style.maxHeight = 'none';

  try {
    // 核心:使用 snapdom 進(jìn)行截圖
    const capture = await snapdom(element, {
      scale: 2,        // 2倍清晰度
      quality: quality
    });

    // 優(yōu)先使用 toPng()
    const imgElement = await capture.toPng();
    const dataUrl = imgElement.src;

    // 驗證數(shù)據(jù)有效性
    if (!dataUrl || dataUrl.length < 100) {
      console.log('toPng 返回?zé)o效,嘗試 toCanvas...');
      const canvas = await capture.toCanvas();
      return canvas.toDataURL(IMAGE_FORMAT, quality);
    }

    console.log('截圖成功,大小:', (dataUrl.length / 1024).toFixed(2), 'KB');
    return dataUrl;

  } finally {
    // 恢復(fù)原始樣式
    element.style.overflow = originalOverflow;
    element.style.height = originalHeight;
    element.style.maxHeight = originalMaxHeight;
  }
}

關(guān)鍵點(diǎn)解析

  1. 臨時修改樣式:將 overflowheight、maxHeight 臨時設(shè)置為可見狀態(tài),確保截取完整內(nèi)容
  2. scale: 2:2 倍縮放提高清晰度,打印時效果更佳
  3. 降級處理toPng() 失敗時自動回退到 toCanvas()
  4. 樣式恢復(fù):截圖完成后恢復(fù)原始樣式

Step 2:圖片分頁(Canvas)

長圖片需要按照 A4 頁面高度進(jìn)行分割,這是最復(fù)雜的一步。

// 尺寸常量
const A4_WIDTH_MM = 210;
const A4_HEIGHT_MM = 297;
const PDF_MARGIN_MM = 10;
const PDF_CONTENT_WIDTH_MM = A4_WIDTH_MM - PDF_MARGIN_MM * 2;   // 190mm
const PDF_CONTENT_HEIGHT_MM = A4_HEIGHT_MM - PDF_MARGIN_MM * 2; // 277mm

// 1mm = 3.7795275590551 像素(96 DPI)
const MM_TO_PX = 3.7795275590551;

// 分頁后的圖片數(shù)據(jù)
interface PageImageData {
  dataUrl: string;
  width: number;
  height: number;
}

/**
 * 將長圖片分割成多個 A4 頁面
 */
export async function splitImageIntoPages(
  imageDataUrl: string
): Promise<PageImageData[]> {

  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';

    img.onload = () => {
      const pages: PageImageData[] = [];
      const originalWidth = img.width;
      const originalHeight = img.height;

      // 將 A4 內(nèi)容區(qū)域轉(zhuǎn)換為像素(考慮 scale=2)
      const pageContentHeightPx = Math.floor(
        PDF_CONTENT_HEIGHT_MM * MM_TO_PX * 2  // scale=2
      );
      const pageContentWidthPx = Math.floor(
        PDF_CONTENT_WIDTH_MM * MM_TO_PX * 2
      );

      // 計算縮放比例(圖片寬度適配頁面寬度)
      const widthScale = pageContentWidthPx / originalWidth;
      const scaledHeight = originalHeight * widthScale;

      // 計算總頁數(shù)
      const totalPages = Math.ceil(scaledHeight / pageContentHeightPx);

      console.log(`原始尺寸: ${originalWidth}x${originalHeight}px`);
      console.log(`縮放后高度: ${scaledHeight}px, 總頁數(shù): ${totalPages}`);

      // 逐頁裁剪
      for (let pageIndex = 0; pageIndex < totalPages; pageIndex++) {
        const startY = pageIndex * pageContentHeightPx;
        const endY = Math.min(startY + pageContentHeightPx, scaledHeight);
        const currentPageHeight = Math.floor(endY - startY);

        // 計算源圖片對應(yīng)的區(qū)域
        const sourceStartY = startY / widthScale;
        const sourceHeight = currentPageHeight / widthScale;

        // 創(chuàng)建新 Canvas
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d')!;

        canvas.width = pageContentWidthPx;
        canvas.height = currentPageHeight;

        // 高質(zhì)量渲染
        ctx.imageSmoothingEnabled = true;
        ctx.imageSmoothingQuality = 'high';

        // 繪制當(dāng)前頁內(nèi)容
        ctx.drawImage(
          img,
          0, sourceStartY,           // 源圖片起始位置
          originalWidth, sourceHeight, // 源圖片尺寸
          0, 0,                        // 目標(biāo)起始位置
          pageContentWidthPx, currentPageHeight // 目標(biāo)尺寸
        );

        // 轉(zhuǎn)換為 data URL
        const pageDataUrl = canvas.toDataURL(IMAGE_FORMAT, IMAGE_QUALITY);

        pages.push({
          dataUrl: pageDataUrl,
          width: pageContentWidthPx,
          height: currentPageHeight
        });

        console.log(`第 ${pageIndex + 1}/${totalPages} 頁處理完成`);
      }

      resolve(pages);
    };

    img.onerror = () => reject(new Error('圖片加載失敗'));
    img.src = imageDataUrl;
  });
}

分頁算法圖解

原始長圖 (假設(shè) 5000px 高)
┌───────────────────┐
│                   │ ─┐
│      Page 1       │  │ 1046px (277mm × 3.78 × 2)
│                   │ ─┘
├───────────────────┤
│                   │ ─┐
│      Page 2       │  │ 1046px
│                   │ ─┘
├───────────────────┤
│                   │ ─┐
│      Page 3       │  │ 1046px
│                   │ ─┘
├───────────────────┤
│                   │ ─┐
│      Page 4       │  │ 1046px
│                   │ ─┘
├───────────────────┤
│      Page 5       │ ── 剩余 816px
│                   │
└───────────────────┘

Step 3:創(chuàng)建 PDF(jsPDF)

將分頁后的圖片逐一添加到 PDF 中。

import { jsPDF } from 'jspdf';

/**
 * 從分頁圖片創(chuàng)建 PDF
 */
export function createPdfFromPages(pages: PageImageData[]): jsPDF {
  const pdf = new jsPDF({
    orientation: 'portrait',
    unit: 'mm',
    format: 'a4',
    compress: true  // 啟用壓縮,減小文件體積
  });

  if (pages.length === 0) {
    throw new Error('沒有可添加的頁面');
  }

  pages.forEach((page, index) => {
    // 第一頁直接用,后續(xù)需要 addPage
    if (index > 0) {
      pdf.addPage();
    }

    // 像素轉(zhuǎn)毫米(考慮 scale=2)
    const scaleFactor = 2;
    const pageHeightMm = page.height / MM_TO_PX / scaleFactor;

    // 圖片適配內(nèi)容區(qū)域?qū)挾?
    const finalWidth = PDF_CONTENT_WIDTH_MM;  // 190mm
    const finalHeight = pageHeightMm;

    // 位置:左上角對齊,保留 10mm 邊距
    const x = PDF_MARGIN_MM;
    const y = PDF_MARGIN_MM;

    console.log(`添加第 ${index + 1} 頁: ${finalWidth}x${finalHeight.toFixed(2)}mm`);

    // 添加圖片到 PDF
    pdf.addImage(page.dataUrl, 'PNG', x, y, finalWidth, finalHeight);
  });

  return pdf;
}

Step 4:主導(dǎo)出函數(shù)

將以上步驟串聯(lián)起來,提供統(tǒng)一的導(dǎo)出接口。

interface ExportConfig {
  targetSelector: string;   // CSS 選擇器
  filename?: string;        // 文件名
  quality?: number;         // 圖片質(zhì)量
}

/**
 * 主導(dǎo)出函數(shù)
 */
export async function exportMessagesToPdf(config: ExportConfig): Promise<void> {
  const {
    targetSelector,
    filename = 'messages.pdf',
    quality = IMAGE_QUALITY
  } = config;

  console.log('=== 開始導(dǎo)出 PDF ===');

  // 1. 獲取目標(biāo)元素
  const element = document.querySelector(targetSelector) as HTMLElement;
  if (!element) {
    throw new Error(`元素未找到: ${targetSelector}`);
  }

  console.log('元素尺寸:', {
    width: element.offsetWidth,
    height: element.scrollHeight
  });

  // 2. DOM 截圖
  const imageDataUrl = await captureElementToImage(element, quality);
  console.log('截圖完成,大小:', (imageDataUrl.length / 1024).toFixed(2), 'KB');

  // 3. 圖片分頁
  const pages = await splitImageIntoPages(imageDataUrl);
  console.log(`分頁完成,共 ${pages.length} 頁`);

  // 4. 創(chuàng)建 PDF
  const pdf = createPdfFromPages(pages);

  // 5. 保存文件
  pdf.save(filename);
  console.log('=== 導(dǎo)出完成 ===');
}

在組件中使用

// MessageList.tsx

import { exportMessagesToPdf } from '../services/messageExportService';

const MessageList: React.FC = () => {
  const messageListRef = useRef<HTMLDivElement>(null);
  const [isExporting, setIsExporting] = useState(false);

  const handleExportToPdf = useCallback(async () => {
    setIsExporting(true);

    try {
      // 生成帶時間戳的文件名
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
      const filename = `messages-${timestamp}.pdf`;

      await exportMessagesToPdf({
        targetSelector: '.message-list-container',
        filename,
        quality: 0.95
      });

    } catch (error) {
      console.error('導(dǎo)出失敗:', error);
      alert('導(dǎo)出失敗,請重試');
    } finally {
      setIsExporting(false);
    }
  }, []);

  return (
    <div className="message-list-container" ref={messageListRef}>
      <div className="message-list-header">
        <h2>消息記錄</h2>
        <button
          className="export-button"
          onClick={handleExportToPdf}
          disabled={isExporting}
        >
          {isExporting ? '導(dǎo)出中...' : '導(dǎo)出 PDF'}
        </button>
      </div>

      <div className="message-list">
        {messages.map(message => (
          <MessageItem key={message.id} message={message} />
        ))}
      </div>
    </div>
  );
};

完整效果

運(yùn)行項目后,點(diǎn)擊「導(dǎo)出 PDF」按鈕:

  1. 控制臺顯示詳細(xì)的導(dǎo)出日志
  2. 自動計算頁數(shù)并分頁
  3. 生成高清 PDF 文件并自動下載
=== 開始導(dǎo)出 PDF ===
目標(biāo)選擇器: .message-list-container
元素尺寸: { width: 600, height: 8500 }
開始截圖...
截圖完成,大小: 2847.65 KB
分頁完成,共 8 頁
添加第 1 頁: 190x277.00mm
添加第 2 頁: 190x277.00mm
...
添加第 8 頁: 190x156.32mm
=== 導(dǎo)出完成 ===

SnapDOM VS html2canvas

為什么選擇 SnapDOM 而不是更流行的 html2canvas?讓我們來對比一下:

詳細(xì)對比表

對比維度SnapDOMhtml2canvas
樣式還原★★★★★ 接近完美★★★☆☆ 部分樣式丟失
Flexbox/Grid? 完美支持?? 部分問題
漸變背景? 完美支持?? 可能失真
陰影效果? 完美支持?? 部分丟失
自定義字體? 支持?? 需要額外處理
SVG 支持? 原生支持?? 有限支持
輸出格式PNG/Canvas/SVGCanvas/PNG
包大小~20KB~60KB
維護(hù)狀態(tài)活躍更新較少更新
API 設(shè)計現(xiàn)代 Promise回調(diào) + Promise

代碼對比

html2canvas 方式:

import html2canvas from 'html2canvas';

// 需要處理各種兼容性問題
const canvas = await html2canvas(element, {
  scale: 2,
  useCORS: true,
  logging: false,
  allowTaint: true,
  foreignObjectRendering: true,  // 可能不生效
  // 還需要處理字體、SVG 等問題...
});

const dataUrl = canvas.toDataURL('image/png');

SnapDOM 方式:

import { snapdom } from '@zumer/snapdom';

// 簡潔的 API,無需額外配置
const capture = await snapdom(element, {
  scale: 2,
  quality: 0.95
});

const dataUrl = (await capture.toPng()).src;

什么時候選擇 html2canvas?

雖然 SnapDOM 在大多數(shù)場景下更優(yōu)秀,但 html2canvas 在以下情況可能更適合:

  1. 項目已在使用:遷移成本較高
  2. 簡單場景:只需截取簡單文本,無復(fù)雜樣式
  3. 團(tuán)隊熟悉度:團(tuán)隊對 html2canvas 更熟悉

總結(jié)

核心要點(diǎn)回顧

  1. SnapDOM 提供高保真的 DOM 截圖能力,通過 scale: 2 實(shí)現(xiàn) 2 倍清晰度
  2. jsPDF 是強(qiáng)大的 PDF 生成庫,支持 A4 紙張、壓縮等特性
  3. 分頁算法 是整個方案的核心難點(diǎn),需要精確計算像素與毫米的轉(zhuǎn)換
  4. SnapDOM 相比 html2canvas 在樣式還原度上有明顯優(yōu)勢

進(jìn)一步優(yōu)化方向

優(yōu)化點(diǎn)說明
Web Worker將分頁計算放到 Worker 中,避免阻塞主線程
分段截圖超長內(nèi)容分段截圖,避免內(nèi)存溢出
加載提示添加進(jìn)度條,提升用戶體驗
PDF 壓縮使用 pdf-lib 進(jìn)一步壓縮 PDF 體積
頁眉頁腳添加頁碼、時間戳等信息

以上就是JavaScript實(shí)現(xiàn)HTML頁面轉(zhuǎn)換成PDF的技術(shù)方案的詳細(xì)內(nèi)容,更多關(guān)于JavaScript HTML轉(zhuǎn)PDF的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • ??一文帶你了解JavaScript多文件混淆加密

    ??一文帶你了解JavaScript多文件混淆加密

    JavaScript?代碼多文件混淆加密可以有效保護(hù)源代碼不被他人輕易盜取,這篇文章主要為大家介紹了JavaScript中混淆加密的實(shí)現(xiàn),需要的小伙伴可以參考下
    2023-11-11
  • JavaScript中的繼承方式詳解

    JavaScript中的繼承方式詳解

    這篇文章主要介紹了JavaScript中的繼承方式詳解,本文講解了js繼承的概念、原型式繼承與類式繼承、原型鏈繼承、類式繼承、組合繼承、原型式繼承等內(nèi)容,需要的朋友可以參考下
    2015-02-02
  • ArtEditor富文本編輯器增加表單提交功能

    ArtEditor富文本編輯器增加表單提交功能

    artEditor是一款基于jQuery的移動端富文本編輯器,支持插入圖片,后續(xù)完善其他功能。接下來通過本文給大家介紹ArtEditor富文本編輯器增加表單提交功能,對arteditor編輯器相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧
    2016-04-04
  • Js實(shí)現(xiàn)復(fù)選框的全選、全不選反選功能代碼實(shí)例

    Js實(shí)現(xiàn)復(fù)選框的全選、全不選反選功能代碼實(shí)例

    這篇文章主要介紹了Js實(shí)現(xiàn)復(fù)選框的全選、全不選和反選,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • JavaScript高級程序設(shè)計 閱讀筆記(二十一) JavaScript中的XML

    JavaScript高級程序設(shè)計 閱讀筆記(二十一) JavaScript中的XML

    雖然XML和DOM已經(jīng)變成Web開發(fā)的重要組成部分,但目前僅IE跟Mozilla支持客戶端的XML處理
    2012-09-09
  • JS 如何獲取radio選中后的值及不選擇取radio的值

    JS 如何獲取radio選中后的值及不選擇取radio的值

    獲取radio選中后的值,這在提交頁面經(jīng)常會使用到的,在本文為大家介紹下不選擇也能獲取radio的值,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下
    2013-10-10
  • JavaScript中的模塊化開發(fā)ES6:import與export詳解

    JavaScript中的模塊化開發(fā)ES6:import與export詳解

    JavaScript模塊化通過ES6的import/export實(shí)現(xiàn),將代碼拆分為獨(dú)立模塊,提升可維護(hù)性與復(fù)用性,支持命名導(dǎo)出、默認(rèn)導(dǎo)出及混合導(dǎo)出,模塊異步加載且只執(zhí)行一次,需嚴(yán)格模式和正確路徑,打包工具如Webpack可優(yōu)化加載和代碼分割
    2025-07-07
  • js前端技巧之圖片格式轉(zhuǎn)換(File、Blob、base64)

    js前端技巧之圖片格式轉(zhuǎn)換(File、Blob、base64)

    這篇文章主要給大家介紹了關(guān)于js前端技巧之圖片格式轉(zhuǎn)換(File、Blob、base64)的相關(guān)資料,主要記錄一下比較常見的圖片格式(File、Blob、base64)在不同的場景他們之間的相互轉(zhuǎn)換的方法,需要的朋友可以參考下
    2023-04-04
  • uni-app表單組件(form表單)用法舉例

    uni-app表單組件(form表單)用法舉例

    平時我們經(jīng)常會使用到表單,下面這篇文章主要給大家介紹了關(guān)于uni-app表單組件(form表單)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • 微信小程序3種位置API的使用方法詳解

    微信小程序3種位置API的使用方法詳解

    這篇文章主要介紹了微信小程序3種位置API的使用方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-08-08

最新評論

随州市| 江油市| 平阳县| 桃园县| 汉川市| 铜梁县| 香港 | 鄂托克前旗| 军事| 遵化市| 天镇县| 嘉峪关市| 镇沅| 会泽县| 芒康县| 威海市| 铜山县| 沁源县| 嘉峪关市| 丁青县| 四子王旗| 潞城市| 滨州市| 普安县| 漠河县| 丰城市| 石渠县| 顺义区| 屏南县| 江油市| 平塘县| 灵武市| 蒙城县| 衡南县| 阳山县| 贵阳市| 清镇市| 潞西市| 丹棱县| 玛曲县| 万源市|