使用MapLibre@5.24.0实现绘制标尺功能

✅ 每次绘制 → 保留在地图上
✅ 新绘制不会影响旧图形
✅ 【清除】→ 清除 所有​ 历史绘制
✅ 【标尺工具】→ 开启新一轮绘制

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>MapLibre 标尺工具(历史保留版)</title>

    <script src="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js"></script>
    <link href="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css" rel="stylesheet" />

    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: system-ui, sans-serif;
        }

        #map {
            width: 100%;
            height: 100vh;
        }

        .toolbar {
            position: absolute;
            top: 20px;
            left: 20px;
            z-index: 1000;
            display: flex;
            gap: 10px;
        }

        .toolbar button {
            padding: 10px 18px;
            background: #fff;
            border: 2px solid #007cbf;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 600;
            color: #007cbf;
            box-shadow: 0 2px 8px rgba(0, 0, 0, .15);
        }

        .toolbar button.active {
            background: #007cbf;
            color: #fff;
        }

        .toolbar button.clear-btn {
            border-color: #e74c3c;
            color: #e74c3c;
        }

        .toolbar button.clear-btn:hover {
            background: #e74c3c;
            color: #fff;
        }

        .hint {
            position: absolute;
            top: 80px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 1000;
            background: rgba(0, 0, 0, .7);
            color: #fff;
            padding: 8px 16px;
            border-radius: 20px;
            font-size: 13px;
            display: none;
        }

        .hint.show {
            display: block;
        }

        .coord-info {
            position: absolute;
            bottom: 80px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 1000;
            background: rgba(255, 255, 255, .95);
            padding: 8px 14px;
            border-radius: 8px;
            font-size: 12px;
            display: none;
        }

        .result-panel {
            position: absolute;
            bottom: 30px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 1000;
            background: #fff;
            padding: 15px 25px;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, .2);
            display: none;
        }

        .result-panel.show {
            display: block;
        }

        .result-panel .distance {
            font-size: 24px;
            font-weight: bold;
            color: #007cbf;
        }

        .ruler-cursor {
            cursor: crosshair;
        }
    </style>
</head>

<body>
    <div id="map"></div>

    <div class="toolbar">
        <button id="rulerBtn" onclick="toggleRuler()">📏 标尺工具</button>
        <button class="clear-btn" onclick="clearAll()">🗑️ 清除</button>
    </div>

    <div class="hint" id="hint"></div>
    <div class="coord-info" id="coordInfo"></div>

    <div class="result-panel" id="resultPanel">
        <div style="text-align:center">
            <div class="distance" id="distanceText">0 m</div>
            <div class="unit">直线距离</div>
        </div>
    </div>

    <script>
        /* ========= 地图 ========= */
        const map = new maplibregl.Map({
            container: 'map',
            style: 'https://demotiles.maplibre.org/style.json',
            center: [116.397, 39.908],
            zoom: 12
        });

        /* ========= 状态 ========= */
        const state = {
            active: false,
            finished: false,
            points: [],

            // ✅ 历史记录(用于清除)
            history: {
                markers: [],
                layerIds: [],
                sourceIds: []
            }
        };

        const rulerBtn = document.getElementById('rulerBtn');
        const hint = document.getElementById('hint');
        const coordInfo = document.getElementById('coordInfo');
        const resultPanel = document.getElementById('resultPanel');
        const distanceText = document.getElementById('distanceText');

        /* ========= 工具函数 ========= */
        function toRad(d) {
            return d * Math.PI / 180;
        }

        function calcDistance([lng1, lat1], [lng2, lat2]) {
            const R = 6371000;
            const φ1 = toRad(lat1);
            const φ2 = toRad(lat2);
            const Δφ = toRad(lat2 - lat1);
            const Δλ = toRad(lng2 - lng1);

            const a =
                Math.sin(Δφ / 2) ** 2 +
                Math.cos(φ1) * Math.cos(φ2) *
                Math.sin(Δλ / 2) ** 2;

            return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        }

        function fmtDistance(m) {
            return m < 1000
                ? `${m.toFixed(2)} m`
                : `${(m / 1000).toFixed(3)} km`;
        }

        /* ========= 标尺控制 ========= */
        function toggleRuler() {
            if (!state.active) {
                startNewRuler();
            } else if (state.finished) {
                startNewRuler();
            } else {
                cancelCurrent();
            }
        }

        function startNewRuler() {
            state.active = true;
            state.finished = false;
            state.points = [];

            rulerBtn.classList.add('active');
            map.getCanvas().classList.add('ruler-cursor');

            hint.textContent = '点击地图选择第一个点';
            hint.classList.add('show');

            map.on('click', onClick);
            map.on('mousemove', onMouseMove);
        }

        function cancelCurrent() {
            state.active = false;
            state.finished = false;

            rulerBtn.classList.remove('active');
            map.getCanvas().classList.remove('ruler-cursor');

            hint.classList.remove('show');
            coordInfo.classList.remove('show');
            resultPanel.classList.remove('show');

            map.off('click', onClick);
            map.off('mousemove', onMouseMove);
        }

        /* ========= 清除(全部) ========= */
        function clearAll() {
            // 移除所有 marker
            state.history.markers.forEach(m => m.remove());

            // 移除所有 layer / source
            state.history.layerIds.forEach(id => {
                if (map.getLayer(id)) map.removeLayer(id);
            });
            state.history.sourceIds.forEach(id => {
                if (map.getSource(id)) map.removeSource(id);
            });

            // 重置
            state.history = { markers: [], layerIds: [], sourceIds: [] };
            state.active = false;
            state.finished = false;
            state.points = [];

            rulerBtn.classList.remove('active');
            map.getCanvas().classList.remove('ruler-cursor');
            hint.classList.remove('show');
            coordInfo.classList.remove('show');
            resultPanel.classList.remove('show');
        }

        /* ========= 事件 ========= */
        function onClick(e) {
            if (state.finished || state.points.length >= 2) return;

            const p = [e.lngLat.lng, e.lngLat.lat];
            state.points.push(p);
            addMarker(p, state.points.length);

            if (state.points.length === 1) {
                hint.textContent = '点击地图选择第二个点';
                coordInfo.textContent = `起点: ${p[0].toFixed(6)}, ${p[1].toFixed(6)}`;
                coordInfo.classList.add('show');
            }

            if (state.points.length === 2) {
                drawLine();
                showResult();
                finishDrawing();
            }
        }

        function onMouseMove(e) {
            if (state.points.length !== 1) return;

            const p = [e.lngLat.lng, e.lngLat.lat];
            coordInfo.textContent =
                `起点: ${state.points[0][0].toFixed(6)}, ${state.points[0][1].toFixed(6)}  |  鼠标: ${p[0].toFixed(6)}, ${p[1].toFixed(6)}`;

            state.tempMarker
                ? state.tempMarker.setLngLat(p)
                : state.tempMarker = new maplibregl.Marker({ color: '#007cbf', scale: 0.8 })
                    .setLngLat(p).addTo(map);

            updatePreview(p);
        }

        /* ========= 绘制 ========= */
        let tempMarker = null;

        function addMarker(coord, idx) {
            const el = document.createElement('div');
            el.style.cssText = `
        width:18px;height:18px;border-radius:50%;
        background:${idx === 1 ? '#2ecc71' : '#e74c3c'};
        border:3px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.3);
    `;
            const m = new maplibregl.Marker(el).setLngLat(coord).addTo(map);
            state.history.markers.push(m);
        }

        function updatePreview(p) {
            const id = 'preview-line';
            if (!map.getSource(id)) {
                map.addSource(id, {
                    type: 'geojson',
                    data: { type: 'Feature', geometry: { type: 'LineString', coordinates: [] } }
                });
                map.addLayer({
                    id, type: 'line', source: id,
                    paint: { 'line-color': '#007cbf', 'line-width': 3, 'line-dasharray': [2, 2] }
                });
            }
            map.getSource(id).setData({
                type: 'Feature',
                geometry: { type: 'LineString', coordinates: [state.points[0], p] }
            });
        }

        function drawLine() {
            const uid = Date.now();
            const sourceId = `ruler-source-${uid}`;
            const layerId = `ruler-layer-${uid}`;

            map.addSource(sourceId, {
                type: 'geojson',
                data: {
                    type: 'Feature',
                    geometry: { type: 'LineString', coordinates: state.points }
                }
            });

            map.addLayer({
                id: layerId,
                type: 'line',
                source: sourceId,
                paint: {
                    'line-color': '#e74c3c',
                    'line-width': 4
                }
            });

            state.history.sourceIds.push(sourceId);
            state.history.layerIds.push(layerId);

            // 清除临时预览线
            if (map.getSource('preview-line')) {
                map.removeLayer('preview-line');
                map.removeSource('preview-line');
            }
        }

        function showResult() {
            const d = calcDistance(state.points[0], state.points[1]);
            distanceText.textContent = fmtDistance(d);
            resultPanel.classList.add('show');

            const mid = [
                (state.points[0][0] + state.points[1][0]) / 2,
                (state.points[0][1] + state.points[1][1]) / 2
            ];

            const el = document.createElement('div');
            el.innerHTML = `
        <div style="
            background:#007cbf;color:#fff;
            padding:6px 12px;border-radius:6px;
            font-size:14px;font-weight:bold;
            box-shadow:0 2px 8px rgba(0,0,0,.2);
        ">${fmtDistance(d)}</div>
    `;

            const m = new maplibregl.Marker({ element: el }).setLngLat(mid).addTo(map);
            state.history.markers.push(m);
        }

        /* ========= 完成绘制 ========= */
        function finishDrawing() {
            state.finished = true;
            map.getCanvas().classList.remove('ruler-cursor');

            hint.textContent = '点击“标尺工具”继续绘制,或点击“清除”清除所有图形';

            map.off('click', onClick);
            map.off('mousemove', onMouseMove);

            tempMarker?.remove();
            tempMarker = null;
        }
    </script>
</body>

</html>
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>MapLibre 标尺工具</title>

    <script src="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js"></script>
    <link href="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css" rel="stylesheet" />

    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: system-ui, sans-serif;
        }

        #map {
            width: 100%;
            height: 100vh;
        }

        .toolbar {
            position: absolute;
            top: 20px;
            left: 20px;
            z-index: 1000;
            display: flex;
            gap: 10px;
        }

        .toolbar button {
            padding: 10px 18px;
            background: #fff;
            border: 2px solid #007cbf;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 600;
            color: #007cbf;
            box-shadow: 0 2px 8px rgba(0, 0, 0, .15);
        }

        .toolbar button.active {
            background: #007cbf;
            color: #fff;
        }

        .toolbar button.clear-btn {
            border-color: #e74c3c;
            color: #e74c3c;
        }

        .toolbar button.clear-btn:hover {
            background: #e74c3c;
            color: #fff;
        }

        .hint {
            position: absolute;
            top: 80px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 1000;
            background: rgba(0, 0, 0, .7);
            color: #fff;
            padding: 8px 16px;
            border-radius: 20px;
            font-size: 13px;
            display: none;
        }

        .hint.show {
            display: block;
        }

        .coord-info {
            position: absolute;
            bottom: 80px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 1000;
            background: rgba(255, 255, 255, .95);
            padding: 8px 14px;
            border-radius: 8px;
            font-size: 12px;
            display: none;
        }

        .result-panel {
            position: absolute;
            bottom: 30px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 1000;
            background: #fff;
            padding: 15px 25px;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, .2);
            display: none;
        }

        .result-panel.show {
            display: block;
        }

        .result-panel .distance {
            font-size: 24px;
            font-weight: bold;
            color: #007cbf;
        }

        .ruler-cursor {
            cursor: crosshair;
        }
    </style>
</head>

<body>
    <div id="map"></div>

    <div class="toolbar">
        <button id="rulerBtn" onclick="toggleRuler()">📏 标尺工具</button>
        <button class="clear-btn" onclick="clearAll()">🗑️ 清除</button>
    </div>

    <div class="hint" id="hint"></div>
    <div class="coord-info" id="coordInfo"></div>

    <div class="result-panel" id="resultPanel">
        <div style="text-align:center">
            <div class="distance" id="distanceText">0 m</div>
            <div class="unit">直线距离</div>
        </div>
    </div>

    <script>
        const map = new maplibregl.Map({
            container: 'map',
            style: 'https://demotiles.maplibre.org/style.json',
            center: [116.397, 39.908],
            zoom: 12
        });

        const state = {
            active: false,
            finished: false,
            points: [],
            history: {
                markers: [],
                layerIds: [],
                sourceIds: []
            }
        };

        const rulerBtn = document.getElementById('rulerBtn');
        const hint = document.getElementById('hint');
        const coordInfo = document.getElementById('coordInfo');
        const resultPanel = document.getElementById('resultPanel');
        const distanceText = document.getElementById('distanceText');

        function toRad(d) {
            return d * Math.PI / 180;
        }

        function calcDistance([lng1, lat1], [lng2, lat2]) {
            const R = 6371000;
            const φ1 = toRad(lat1);
            const φ2 = toRad(lat2);
            const Δφ = toRad(lat2 - lat1);
            const Δλ = toRad(lng2 - lng1);

            const a =
                Math.sin(Δφ / 2) ** 2 +
                Math.cos(φ1) * Math.cos(φ2) *
                Math.sin(Δλ / 2) ** 2;

            return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        }

        function fmtDistance(m) {
            return m < 1000
                ? `${m.toFixed(2)} m`
                : `${(m / 1000).toFixed(3)} km`;
        }

        function toggleRuler() {
            if (!state.active) {
                startNewRuler();
            } else if (state.finished) {
                startNewRuler();
            } else {
                cancelCurrent();
            }
        }

        function startNewRuler() {
            state.active = true;
            state.finished = false;
            state.points = [];

            rulerBtn.classList.add('active');
            map.getCanvas().classList.add('ruler-cursor');

            hint.textContent = '点击地图选择第一个点';
            hint.classList.add('show');

            map.on('click', onClick);
            map.on('mousemove', onMouseMove);
        }

        function cancelCurrent() {
            state.active = false;
            state.finished = false;

            rulerBtn.classList.remove('active');
            map.getCanvas().classList.remove('ruler-cursor');

            hint.classList.remove('show');
            coordInfo.classList.remove('show');
            resultPanel.classList.remove('show');

            map.off('click', onClick);
            map.off('mousemove', onMouseMove);
        }

        function clearAll() {
            state.history.markers.forEach(m => m.remove());

            state.history.layerIds.forEach(id => {
                if (map.getLayer(id)) map.removeLayer(id);
            });

            state.history.sourceIds.forEach(id => {
                if (map.getSource(id)) map.removeSource(id);
            });

            if (map.getSource('preview-line')) {
                map.removeLayer('preview-line');
                map.removeSource('preview-line');
            }

            state.history = { markers: [], layerIds: [], sourceIds: [] };
            state.active = false;
            state.finished = false;
            state.points = [];

            rulerBtn.classList.remove('active');
            map.getCanvas().classList.remove('ruler-cursor');
            hint.classList.remove('show');
            coordInfo.classList.remove('show');
            resultPanel.classList.remove('show');
        }

        function onClick(e) {
            if (state.finished || state.points.length >= 2) return;

            const p = [e.lngLat.lng, e.lngLat.lat];
            state.points.push(p);
            addMarker(p, state.points.length);

            if (state.points.length === 1) {
                hint.textContent = '点击地图选择第二个点';
                coordInfo.textContent = `起点: ${p[0].toFixed(6)}, ${p[1].toFixed(6)}`;
                coordInfo.classList.add('show');
            }

            if (state.points.length === 2) {
                drawLine();
                showResult();
                finishDrawing();
            }
        }

        function onMouseMove(e) {
            if (state.points.length !== 1) return;

            const p = [e.lngLat.lng, e.lngLat.lat];
            coordInfo.textContent =
                `起点: ${state.points[0][0].toFixed(6)}, ${state.points[0][1].toFixed(6)}  |  鼠标: ${p[0].toFixed(6)}, ${p[1].toFixed(6)}`;

            updatePreview(p);
        }

        function addMarker(coord, idx) {
            const uid = Date.now() + Math.random();
            const sourceId = `point-${uid}`;
            const layerId = `point-layer-${uid}`;

            map.addSource(sourceId, {
                type: 'geojson',
                data: {
                    type: 'Feature',
                    geometry: { type: 'Point', coordinates: coord }
                }
            });

            map.addLayer({
                id: layerId,
                type: 'circle',
                source: sourceId,
                paint: {
                    'circle-radius': idx === 1 ? 4 : 3,
                    'circle-color': idx === 1 ? '#2ecc71' : '#e74c3c',
                    'circle-stroke-color': '#fff',
                    'circle-stroke-width': 2
                }
            });

            state.history.sourceIds.push(sourceId);
            state.history.layerIds.push(layerId);
        }

        function updatePreview(p) {
            const id = 'preview-line';
            if (!map.getSource(id)) {
                map.addSource(id, {
                    type: 'geojson',
                    data: { type: 'Feature', geometry: { type: 'LineString', coordinates: [] } }
                });
                map.addLayer({
                    id,
                    type: 'line',
                    source: id,
                    paint: {
                        'line-color': '#007cbf',
                        'line-width': 2,
                        'line-dasharray': [2, 2]
                    }
                });
            }
            map.getSource(id).setData({
                type: 'Feature',
                geometry: { type: 'LineString', coordinates: [state.points[0], p] }
            });
        }

        function drawLine() {
            const uid = Date.now();
            const sourceId = `ruler-source-${uid}`;
            const layerId = `ruler-layer-${uid}`;

            map.addSource(sourceId, {
                type: 'geojson',
                data: {
                    type: 'Feature',
                    geometry: { type: 'LineString', coordinates: state.points }
                }
            });

            map.addLayer({
                id: layerId,
                type: 'line',
                source: sourceId,
                paint: {
                    'line-color': '#e74c3c',
                    'line-width': 2,
                    'line-dasharray': [2, 2]
                }
            });

            state.history.sourceIds.push(sourceId);
            state.history.layerIds.push(layerId);

            if (map.getSource('preview-line')) {
                map.removeLayer('preview-line');
                map.removeSource('preview-line');
            }
        }

        function showResult() {
            const d = calcDistance(state.points[0], state.points[1]);
            distanceText.textContent = fmtDistance(d);
            resultPanel.classList.add('show');

            const mid = [
                (state.points[0][0] + state.points[1][0]) / 2,
                (state.points[0][1] + state.points[1][1]) / 2
            ];

            const el = document.createElement('div');
            el.innerHTML = `
                <div style="
                    background:#007cbf;color:#fff;
                    padding:6px 12px;border-radius:6px;
                    font-size:14px;font-weight:bold;
                    box-shadow:0 2px 8px rgba(0,0,0,.2);
                ">${fmtDistance(d)}</div>
            `;

            const m = new maplibregl.Marker({ element: el }).setLngLat(mid).addTo(map);
            state.history.markers.push(m);
        }

        function finishDrawing() {
            state.finished = true;
            map.getCanvas().classList.remove('ruler-cursor');

            hint.textContent = '点击“标尺工具”继续绘制,或点击“清除”清除所有图形';

            map.off('click', onClick);
            map.off('mousemove', onMouseMove);

            // ✅ 只把“线”从实线改成虚线,不碰点
            state.history.layerIds.forEach(layerId => {
                if (layerId.startsWith('ruler-layer-') && map.getLayer(layerId)) {
                    map.setPaintProperty(layerId, 'line-dasharray', [6, 4]);
                }
            });
        }
    </script>
</body>

</html>

源码链接: https://pan.quark.cn/s/fa13cd6c6c8d Chrome浏览器作为一款备受青睐的网页浏览器,凭借其出色的稳定性和运行速度获得了广泛认可。 然而出于安全考量,Chrome系统默认不兼容ActiveX插件,因为ActiveX技术主要应用于Internet Explorer,它赋予网页内容与用户本地系统交互的能力,但同时也可能引发潜在的安全隐患。 不过在某些特定工作场景下,比如在企业内部网络环境或需要与老旧应用程序整合时,可能仍需在Chrome中启用ActiveX控件。 为此我们必须掌握在Chrome浏览器下加载和运用ActiveX的方法。 首先需要明确ActiveX的本质。 ActiveX是由微软设计的一种技术框架,旨在开发可在网页环境中运行的控件,这些控件能够完成多种功能,包括视频播放、应用程序组件运行或与硬件设备通信等。 ActiveX控件多以OCX(OLE控件)格式发布。 在Chrome浏览器中启用ActiveX需要采取额外措施,因为该浏览器本身并不支持此项技术。 以下是几种常见的解决方案: 1. **应用Chrome的兼容性设置**:部分Chrome版本提供了" --enable-internal-activex"命令行参数,可通过此参数使浏览器具备加载ActiveX控件的能力。 用户可在启动Chrome时,于快捷方式的目标路径后附加该参数来激活此功能。 例如:"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --enable-internal-activex。 2. **安装第三方插件**:市面上存在一些第三方插件,例如"IE Tab"或"ActiveX Con...
标题SpringBoot与微信小程序结合的健康饮食平台研究AI更换标题第1章引言介绍健康饮食平台的研究背景、意义、国内外研究现状、论文方法及创新点。1.1研究背景与意义阐述健康饮食平台在当前社会的重要性及其市场需求。1.2国内外研究现状分析国内外健康饮食平台的发展现状及趋势。1.3研究方法及创新点概述本文采用的研究方法和技术创新点。第2章相关理论总结健康饮食、SpringBoot及微信小程序的相关理论。2.1健康饮食理论介绍健康饮食的基本原则和营养学知识。2.2SpringBoot框架阐述SpringBoot框架的特点、优势及在项目中的应用。2.3微信小程序技术介绍微信小程序的开发技术、特点及其用户群体。第3章健康饮食平台设计详细介绍健康饮食平台的设计方案,包括前端和后端设计。3.1平台架构设计给出平台的整体架构、模块划分及交互流程。3.2数据库设计介绍数据库的设计思路、表结构及数据关系。3.3前后端交互设计阐述前后端数据交互的方式、接口设计及安全性考虑。第4章微信小程序实现介绍微信小程序的具体实现过程,包括页面设计、功能实现等。4.1页面设计与布局给出微信小程序的页面设计思路、布局及交互效果。4.2功能实现与测试详细介绍微信小程序各项功能实现过程及测试方法。4.3用户体验优化阐述如何提升微信小程序的用户体验,包括界面优化、性能优化等。第5章平台测试与优化对健康饮食平台进行测试,并根据测试结果进行优化。5.1测试环境与数据介绍测试环境、测试数据及测试方法。5.2测试结果分析从功能、性能、用户体验等方面对测试结果进行详细分析。5.3平台优化策略根据测试结果提出平台优化策略,包括代码优化、功能改进等。第6章结论与展望总结本文的研究成果,并展望未来的研究方向。6.1研究结论概括本文的主要研究结论和平台实现效果。6.2展望指出本文研究的不足之处以及未来研究的方向和改进点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值