千千块模板|全国各省市旅游足迹地图点亮模版

千千块模板 | 全国各省市旅游足迹地图点亮模版

国庆节到了,看见论坛没有一个完美的旅游足迹,于是自己做了一个。

可以点击全国各省市的旅游足迹,并且可以自定义设置,数据会自动保存

功能

1.可以设置点亮每个地方都足迹

2.可以支持拉伸地图大小

3.可以自定义配色设置

4.数据会自动保存

将 map 文件夹放到 笔记工作区\data\assets

链接:https://pan.quark.cn/s/54825f81b15a

因为思源笔记机制,需要先做文档下面随便建一个 /chart 图表,然后就可以在上面的普通块使用了

1.png

2.png

3.png

RGB 在线网站:https://tools.jb51.net/static/colorpicker/

自定义块 css

/* @webpack */
.qianqian-map-block{min-height:200px;width:100%;position:relative;overflow:hidden;border:1px solid #dcdfe6;border-radius:4px}.qianqian-map-block .map-container{width:100%;height:100%;cursor:grab;user-select:none}.qianqian-map-block .map-container:active{cursor:grabbing}.qianqian-map-block .map-selector{position:absolute;top:10px;left:10px;z-index:10;cursor:pointer}.qianqian-map-block .back-button{padding:4px 8px;border-radius:4px;border:1px solid #ccc;background-color:#f0f0f0;cursor:pointer}.qianqian-map-block .back-button:hover{background-color:#e0e0e0}.qianqian-map-block .settings-panel{padding:20px;box-sizing:border-box}.qianqian-map-block .settings-panel .color-input-group{display:flex;align-items:center;justify-content:center;margin-bottom:10px}.qianqian-map-block .settings-panel .color-input-group label{margin-right:15px;font-weight:500;white-space:nowrap}.qianqian-map-block .settings-panel .color-input-group div{display:inline-flex;align-items:center}.qianqian-map-block .settings-panel .color-input-group input[type=number]{width:70px;padding:6px;font-size:1em;margin:0 8px 0 4px;text-align:center;border:1px solid #ccc;border-radius:4px}.qianqian-map-block .settings-panel button{margin-top:20px;padding:8px 16px;border-radius:4px;border:1px solid #ccc;background-color:#4477cb;color:#fff;cursor:pointer}.qianqian-map-block .settings-panel button:hover{background-color:#3a68b4}.qianqian-map-block .error-msg{display:flex;justify-content:center;align-items:center;height:100%;color:#f56c6c}

自定义块 js

/******/ "use strict";

var __async = (__this, __arguments, generator) => {
  return new Promise((resolve, reject) => {
    var fulfilled = (value) => {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    };
    var rejected = (value) => {
      try {
        step(generator.throw(value));
      } catch (e) {
        reject(e);
      }
    };
    var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
    step((generator = generator.apply(__this, __arguments)).next());
  });
};

function initializeApp(blockElement, echarts) {
  const blockId = blockElement.dataset.nodeId;
  let mapHeight = 600;
  const colors = {
    background: "rgb(255, 255, 255)",
    areaColor: "rgb(231, 232, 234)",
    litColor: "rgb(244, 208, 63)"
  };
  const registeredMapsCache = /* @__PURE__ */ new Map();
  const litRegionsSet = /* @__PURE__ */ new Set();
  const litProvincesSet = /* @__PURE__ */ new Set();
  let currentMapPath = "js/china";
  function loadAndRegisterMap(mapPathFragment) {
    return __async(this, null, function* () {
      if (registeredMapsCache.has(mapPathFragment)) {
        const cachedName = registeredMapsCache.get(mapPathFragment);
        return Promise.resolve(cachedName);
      }
      const scriptSrc = `/assets/map/${mapPathFragment}.js?v=${Date.now()}`;
      const originalRegisterMap = echarts.registerMap;
      let capturedName = null;
      return new Promise((resolve, reject) => {
        echarts.registerMap = (name, data) => {
          originalRegisterMap.call(echarts, name, data);
          if (echarts.getMap(name)) {
            capturedName = name;
          }
        };
        const script = document.createElement("script");
        script.src = scriptSrc;
        script.onload = () => {
          echarts.registerMap = originalRegisterMap;
          if (capturedName) {
            registeredMapsCache.set(mapPathFragment, capturedName);
            resolve(capturedName);
          } else {
            const mapNameFromFile = mapPathFragment.split("/").pop();
            reject(new Error(`\u672A\u80FD\u6355\u83B7\u5730\u56FE [${mapNameFromFile}] \u7684\u6CE8\u518C\u3002`));
          }
        };
        script.onerror = () => {
          echarts.registerMap = originalRegisterMap;
          reject(new Error(`\u52A0\u8F7D\u5730\u56FE\u811A\u672C [${scriptSrc}] \u5931\u8D25\u3002`));
        };
        document.head.appendChild(script);
      });
    });
  }
  blockElement.innerHTML = "";
  blockElement.className = "qianqian-map-block";
  blockElement.style.position = "relative";
  const container = document.createElement("div");
  container.className = "map-container";
  const selector = document.createElement("select");
  selector.className = "map-selector";
  const backButton = document.createElement("button");
  backButton.className = "back-button";
  backButton.textContent = "\u8FD4\u56DE";
  backButton.style.display = "none";
  backButton.style.position = "absolute";
  backButton.style.top = "10px";
  backButton.style.right = "10px";
  backButton.style.zIndex = "10";
  const settingsPanel = document.createElement("div");
  settingsPanel.className = "settings-panel";
  settingsPanel.style.display = "none";
  blockElement.append(selector, backButton, container, settingsPanel);
  const mapOptions = {
    "\u4E2D\u56FD": "js/china",
    "\u8BBE\u7F6E": "special/settings",
    "\u5B89\u5FBD": "js/province/anhui",
    "\u6FB3\u95E8": "js/province/aomen",
    "\u5317\u4EAC": "js/province/beijing",
    "\u91CD\u5E86": "js/province/chongqing",
    "\u798F\u5EFA": "js/province/fujian",
    "\u7518\u8083": "js/province/gansu",
    "\u5E7F\u4E1C": "js/province/guangdong",
    "\u5E7F\u897F": "js/province/guangxi",
    "\u8D35\u5DDE": "js/province/guizhou",
    "\u6D77\u5357": "js/province/hainan",
    "\u6CB3\u5317": "js/province/hebei",
    "\u9ED1\u9F99\u6C5F": "js/province/heilongjiang",
    "\u6CB3\u5357": "js/province/henan",
    "\u6E56\u5317": "js/province/hubei",
    "\u6E56\u5357": "js/province/hunan",
    "\u6C5F\u82CF": "js/province/jiangsu",
    "\u6C5F\u897F": "js/province/jiangxi",
    "\u5409\u6797": "js/province/jilin",
    "\u8FBD\u5B81": "js/province/liaoning",
    "\u5185\u8499\u53E4": "js/province/neimenggu",
    "\u5B81\u590F": "js/province/ningxia",
    "\u9752\u6D77": "js/province/qinghai",
    "\u5C71\u4E1C": "js/province/shandong",
    "\u4E0A\u6D77": "js/province/shanghai",
    "\u5C71\u897F": "js/province/shanxi",
    "\u9655\u897F": "js/province/shanxi1",
    "\u56DB\u5DDD": "js/province/sichuan",
    "\u53F0\u6E7E": "js/province/taiwan",
    "\u5929\u6D25": "js/province/tianjin",
    "\u9999\u6E2F": "js/province/xianggang",
    "\u65B0\u7586": "js/province/xinjiang",
    "\u897F\u85CF": "js/province/xizang",
    "\u4E91\u5357": "js/province/yunnan",
    "\u6D59\u6C5F": "js/province/zhejiang"
  };
  Object.entries(mapOptions).forEach(([displayName, pathFragment]) => {
    const option = document.createElement("option");
    option.value = pathFragment;
    option.textContent = displayName;
    selector.appendChild(option);
  });
  container.style.width = "100%";
  container.style.height = "100%";
  const chart = echarts.init(container, null, { renderer: "svg" });
  function showLoading(message) {
    chart.clear();
    chart.showLoading("default", {
      text: message,
      color: "#4477cb",
      textColor: "#333",
      maskColor: "rgba(255, 255, 255, 0.8)",
      zlevel: 0
    });
  }
  function parseRgb(rgbString) {
    var _a;
    return ((_a = rgbString.match(/\d+/g)) == null ? void 0 : _a.map(Number)) || [0, 0, 0];
  }
  function createColorInputGroup(label, colorKey) {
    const group = document.createElement("div");
    group.className = "color-input-group";
    const labelEl = document.createElement("label");
    labelEl.textContent = label;
    group.appendChild(labelEl);
    const inputsContainer = document.createElement("div");
    const [r, g, b] = parseRgb(colors[colorKey]);
    const rInput = document.createElement("input");
    rInput.type = "number";
    rInput.min = "0";
    rInput.max = "255";
    rInput.value = String(r);
    const gInput = document.createElement("input");
    gInput.type = "number";
    gInput.min = "0";
    gInput.max = "255";
    gInput.value = String(g);
    const bInput = document.createElement("input");
    bInput.type = "number";
    bInput.min = "0";
    bInput.max = "255";
    bInput.value = String(b);
    inputsContainer.append("R:", rInput, "G:", gInput, "B:", bInput);
    group.appendChild(inputsContainer);
    const updateColor = () => {
      colors[colorKey] = `rgb(${rInput.value}, ${gInput.value}, ${bInput.value})`;
      if (colorKey === "background") {
        container.style.backgroundColor = colors.background;
      }
    };
    rInput.addEventListener("input", updateColor);
    gInput.addEventListener("input", updateColor);
    bInput.addEventListener("input", updateColor);
    return group;
  }
  function createHeightInputGroup() {
    const group = document.createElement("div");
    group.className = "color-input-group";
    const labelEl = document.createElement("label");
    labelEl.textContent = "\u9AD8\u5EA6 (px)";
    group.appendChild(labelEl);
    const input = document.createElement("input");
    input.type = "number";
    input.min = "200";
    input.step = "10";
    input.value = String(mapHeight);
    input.style.width = "158px";
    input.addEventListener("input", () => {
      const newHeight = parseInt(input.value, 10);
      if (!isNaN(newHeight) && newHeight >= 200) {
        mapHeight = newHeight;
        blockElement.style.height = `${mapHeight}px`;
        chart.resize();
      }
    });
    group.appendChild(input);
    return group;
  }
  function buildSettingsPanel() {
    settingsPanel.innerHTML = "";
    settingsPanel.appendChild(createHeightInputGroup());
    settingsPanel.appendChild(createColorInputGroup("\u80CC\u666F\u989C\u8272", "background"));
    settingsPanel.appendChild(createColorInputGroup("\u5730\u56FE\u989C\u8272", "areaColor"));
    settingsPanel.appendChild(createColorInputGroup("\u70B9\u4EAE\u989C\u8272", "litColor"));
    const saveButton = document.createElement("button");
    saveButton.textContent = "\u4FDD\u5B58\u8BBE\u7F6E";
    saveButton.onclick = () => __async(null, null, function* () {
      if (!blockId) return;
      try {
        yield fetch("/api/attr/setBlockAttrs", {
          method: "POST",
          body: JSON.stringify({
            id: blockId,
            attrs: {
              "custom-map-colors": JSON.stringify(colors),
              "custom-map-height": String(mapHeight)
            }
          })
        });
        yield fetch("/api/notification/pushMsg", {
          method: "POST",
          body: JSON.stringify({ msg: "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58" })
        });
      } catch (e) {
        console.error("\u4FDD\u5B58\u8BBE\u7F6E\u5931\u8D25:", e);
      }
    });
    settingsPanel.appendChild(saveButton);
  }
  function saveLitData() {
    return __async(this, null, function* () {
      if (!blockId) return;
      try {
        const dataToSave = {
          regions: Array.from(litRegionsSet),
          provinces: Array.from(litProvincesSet)
        };
        yield fetch("/api/attr/setBlockAttrs", {
          method: "POST",
          body: JSON.stringify({
            id: blockId,
            attrs: { "custom-map-lit-data": JSON.stringify(dataToSave) }
          })
        });
        console.log("\u6210\u529F\u4FDD\u5B58\u70B9\u4EAE\u6570\u636E\u3002");
      } catch (e) {
        console.error("\u4FDD\u5B58\u70B9\u4EAE\u6570\u636E\u5931\u8D25:", e);
      }
    });
  }
  function renderChart(mapPathFragment) {
    return __async(this, null, function* () {
      currentMapPath = mapPathFragment;
      showLoading("\u6B63\u5728\u52A0\u8F7D\u5730\u56FE...");
      try {
        const registeredMapName = yield loadAndRegisterMap(mapPathFragment);
        const isChinaMap = mapPathFragment === "js/china";
        const activeSet = isChinaMap ? litProvincesSet : litRegionsSet;
        const mapData = echarts.getMap(registeredMapName).geoJSON.features.map((feature) => {
          const name = feature.properties.name;
          return { name, selected: activeSet.has(name) };
        });
        chart.hideLoading();
        const option = {
          tooltip: { trigger: "item", formatter: "{b}" },
          geo: {
            map: registeredMapName,
            roam: true,
            selectedMode: "multiple",
            itemStyle: {
              areaColor: colors.areaColor,
              borderColor: "#ADADAD"
            },
            emphasis: {
              focus: "none",
              itemStyle: { areaColor: "#a9d0fa" }
            },
            select: {
              itemStyle: {
                areaColor: colors.litColor
              }
            },
            regions: mapData
          },
          series: []
        };
        chart.setOption(option, true);
        backButton.style.display = isChinaMap ? "none" : "block";
      } catch (e) {
        console.error(`\u6E32\u67D3\u5730\u56FE [${mapPathFragment}] \u5931\u8D25:`, e);
        chart.hideLoading();
        container.innerHTML = `<div class="error-msg">\u5730\u56FE\u6E32\u67D3\u5931\u8D25\uFF0C\u8BF7\u6309F12\u67E5\u770B\u63A7\u5236\u53F0\u3002</div>`;
      }
    });
  }
  chart.on("click", (params) => {
    var _a;
    if (!params.name) return;
    const clickedName = params.name;
    if (currentMapPath === "js/china") {
      const targetPath = (_a = Object.entries(mapOptions).find(([displayName]) => displayName === clickedName)) == null ? void 0 : _a[1];
      if (targetPath) {
        selector.value = targetPath;
        renderChart(targetPath);
      }
      return;
    }
    const currentRegisteredMapName = registeredMapsCache.get(currentMapPath);
    if (!currentRegisteredMapName) return;
    if (litRegionsSet.has(clickedName)) {
      litRegionsSet.delete(clickedName);
      const provinceGeoJSON = echarts.getMap(currentRegisteredMapName).geoJSON;
      const citiesInProvince = provinceGeoJSON.features.map((f) => f.properties.name);
      const hasOtherLitCities = citiesInProvince.some((city) => litRegionsSet.has(city));
      if (!hasOtherLitCities) {
        litProvincesSet.delete(currentRegisteredMapName);
      }
    } else {
      litRegionsSet.add(clickedName);
      litProvincesSet.add(currentRegisteredMapName);
    }
    saveLitData();
    renderChart(currentMapPath);
  });
  backButton.addEventListener("click", () => {
    selector.value = "js/china";
    renderChart("js/china");
  });
  selector.addEventListener("change", (e) => {
    const value = e.target.value;
    if (value === "special/settings") {
      container.style.display = "none";
      backButton.style.display = "none";
      settingsPanel.style.display = "flex";
      settingsPanel.style.flexDirection = "column";
      settingsPanel.style.justifyContent = "center";
      settingsPanel.style.alignItems = "center";
      settingsPanel.style.height = "100%";
      settingsPanel.style.gap = "15px";
      buildSettingsPanel();
    } else {
      container.style.display = "block";
      settingsPanel.style.display = "none";
      renderChart(value);
    }
  });
  const resizeObserver = new ResizeObserver(() => chart.resize());
  resizeObserver.observe(container);
  function initialLoad() {
    return __async(this, null, function* () {
      if (blockId) {
        try {
          const response = yield fetch("/api/attr/getBlockAttrs", {
            method: "POST",
            body: JSON.stringify({ id: blockId })
          });
          const res = yield response.json();
          if (res.code === 0) {
            if (res.data["custom-map-colors"]) {
              const savedColors = JSON.parse(res.data["custom-map-colors"]);
              Object.assign(colors, savedColors);
              console.log("\u6210\u529F\u52A0\u8F7D\u5DF2\u4FDD\u5B58\u7684\u989C\u8272\u8BBE\u7F6E\u3002");
            }
            if (res.data["custom-map-height"]) {
              const savedHeight = parseInt(res.data["custom-map-height"], 10);
              if (!isNaN(savedHeight) && savedHeight >= 200) {
                mapHeight = savedHeight;
                console.log("\u6210\u529F\u52A0\u8F7D\u5DF2\u4FDD\u5B58\u7684\u9AD8\u5EA6\u8BBE\u7F6E\u3002");
              }
            }
            if (res.data["custom-map-lit-data"]) {
              const savedLitData = JSON.parse(res.data["custom-map-lit-data"]);
              if (savedLitData.regions) {
                savedLitData.regions.forEach((region) => litRegionsSet.add(region));
              }
              if (savedLitData.provinces) {
                savedLitData.provinces.forEach((province) => litProvincesSet.add(province));
              }
              console.log("\u6210\u529F\u52A0\u8F7D\u5DF2\u4FDD\u5B58\u7684\u70B9\u4EAE\u6570\u636E\u3002");
            }
          }
        } catch (e) {
          console.error("\u52A0\u8F7D\u5DF2\u4FDD\u5B58\u7684\u5C5E\u6027\u5931\u8D25:", e);
        }
      }
      blockElement.style.height = `${mapHeight}px`;
      container.style.backgroundColor = colors.background;
      renderChart("js/china");
    });
  }
  initialLoad();
}
const checkInterval = 100;
const timeout = 1e3;
let elapsedTime = 0;
const self = this;
const intervalId = setInterval(() => {
  const echartsInstance = window.echarts;
  if (echartsInstance && typeof echartsInstance.init === "function") {
    clearInterval(intervalId);
    try {
      initializeApp(self, echartsInstance);
    } catch (e) {
      console.error("\u521D\u59CB\u5316\u5730\u56FE\u5757\u5931\u8D25:", e);
      self.innerHTML = `<div class="error-msg">\u521D\u59CB\u5316\u5730\u56FE\u5757\u5931\u8D25\uFF0C\u8BE6\u60C5\u8BF7\u67E5\u770B\u63A7\u5236\u53F0\u3002</div>`;
    }
  } else {
    elapsedTime += checkInterval;
    if (elapsedTime > timeout) {
      clearInterval(intervalId);
      console.error("\u7B49\u5F85\u539F\u751F ECharts \u5E93\u52A0\u8F7D\u8D85\u65F6\u3002");
      self.innerHTML = `<div class="error-msg">\u539F\u751F ECharts \u5E93\u52A0\u8F7D\u8D85\u65F6\u3002\u8BF7\u786E\u4FDD\u6587\u6863\u4E2D\u5B58\u5728\u4E00\u4E2A\u5B98\u65B9\u56FE\u8868\u5757\u3002</div>`;
    }
  }
}, checkInterval);


在线地图版自定义块 js (可以不用下载 map)

/******/ "use strict";

var __async = (__this, __arguments, generator) => {
  return new Promise((resolve, reject) => {
    var fulfilled = (value) => {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    };
    var rejected = (value) => {
      try {
        step(generator.throw(value));
      } catch (e) {
        reject(e);
      }
    };
    var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
    step((generator = generator.apply(__this, __arguments)).next());
  });
};

function initializeApp(blockElement, echarts) {
  const blockId = blockElement.dataset.nodeId;
  let mapHeight = 600;
  const colors = {
    background: "rgb(255, 255, 255)",
    areaColor: "rgb(231, 232, 234)",
    litColor: "rgb(244, 208, 63)"
  };
  const registeredMapsCache = /* @__PURE__ */ new Map();
  const litRegionsSet = /* @__PURE__ */ new Set();
  const litProvincesSet = /* @__PURE__ */ new Set();
  let currentMapPath = "js/china";
  function loadAndRegisterMap(mapPathFragment) {
    return __async(this, null, function* () {
      if (registeredMapsCache.has(mapPathFragment)) {
        const cachedName = registeredMapsCache.get(mapPathFragment);
        return Promise.resolve(cachedName);
      }
      const localSrc = `/assets/map/${mapPathFragment}.js?v=${Date.now()}`;
      const cdnSrc = `https://cdn.jsdelivr.net/npm/echarts/map/${mapPathFragment}.js`;
      const originalRegisterMap = echarts.registerMap;
      let capturedName = null;
      return new Promise((resolve, reject) => {
        echarts.registerMap = (name, data) => {
          originalRegisterMap.call(echarts, name, data);
          if (echarts.getMap(name)) {
            capturedName = name;
          }
        };
        const tryLoad = (src, isFallback = false) => {
          const script = document.createElement("script");
          script.src = src;
          script.onload = () => {
            echarts.registerMap = originalRegisterMap;
            document.head.removeChild(script);
            if (capturedName) {
              registeredMapsCache.set(mapPathFragment, capturedName);
              resolve(capturedName);
            } else {
              const mapNameFromFile = mapPathFragment.split("/").pop();
              reject(new Error(`\u672A\u80FD\u6355\u83B7\u5730\u56FE [${mapNameFromFile}] \u7684\u6CE8\u518C\u3002`));
            }
          };
          script.onerror = () => {
            document.head.removeChild(script);
            if (!isFallback) {
              console.warn(`\u672C\u5730\u5730\u56FE [${localSrc}] \u52A0\u8F7D\u5931\u8D25, \u6B63\u5728\u5C1D\u8BD5\u4ECE CDN \u52A0\u8F7D...`);
              tryLoad(cdnSrc, true);
            } else {
              echarts.registerMap = originalRegisterMap;
              reject(new Error(`\u52A0\u8F7D\u5730\u56FE\u811A\u672C [${src}] \u5931\u8D25\u3002`));
            }
          };
          document.head.appendChild(script);
        };
        tryLoad(localSrc);
      });
    });
  }
  blockElement.innerHTML = "";
  blockElement.className = "qianqian-map-block";
  blockElement.style.position = "relative";
  const container = document.createElement("div");
  container.className = "map-container";
  const selector = document.createElement("select");
  selector.className = "map-selector";
  const backButton = document.createElement("button");
  backButton.className = "back-button";
  backButton.textContent = "\u8FD4\u56DE";
  backButton.style.display = "none";
  backButton.style.position = "absolute";
  backButton.style.top = "10px";
  backButton.style.right = "10px";
  backButton.style.zIndex = "10";
  const settingsPanel = document.createElement("div");
  settingsPanel.className = "settings-panel";
  settingsPanel.style.display = "none";
  blockElement.append(selector, backButton, container, settingsPanel);
  const mapOptions = {
    "\u4E2D\u56FD": "js/china",
    "\u8BBE\u7F6E": "special/settings",
    "\u5B89\u5FBD": "js/province/anhui",
    "\u6FB3\u95E8": "js/province/aomen",
    "\u5317\u4EAC": "js/province/beijing",
    "\u91CD\u5E86": "js/province/chongqing",
    "\u798F\u5EFA": "js/province/fujian",
    "\u7518\u8083": "js/province/gansu",
    "\u5E7F\u4E1C": "js/province/guangdong",
    "\u5E7F\u897F": "js/province/guangxi",
    "\u8D35\u5DDE": "js/province/guizhou",
    "\u6D77\u5357": "js/province/hainan",
    "\u6CB3\u5317": "js/province/hebei",
    "\u9ED1\u9F99\u6C5F": "js/province/heilongjiang",
    "\u6CB3\u5357": "js/province/henan",
    "\u6E56\u5317": "js/province/hubei",
    "\u6E56\u5357": "js/province/hunan",
    "\u6C5F\u82CF": "js/province/jiangsu",
    "\u6C5F\u897F": "js/province/jiangxi",
    "\u5409\u6797": "js/province/jilin",
    "\u8FBD\u5B81": "js/province/liaoning",
    "\u5185\u8499\u53E4": "js/province/neimenggu",
    "\u5B81\u590F": "js/province/ningxia",
    "\u9752\u6D77": "js/province/qinghai",
    "\u5C71\u4E1C": "js/province/shandong",
    "\u4E0A\u6D77": "js/province/shanghai",
    "\u5C71\u897F": "js/province/shanxi",
    "\u9655\u897F": "js/province/shanxi1",
    "\u56DB\u5DDD": "js/province/sichuan",
    "\u53F0\u6E7E": "js/province/taiwan",
    "\u5929\u6D25": "js/province/tianjin",
    "\u9999\u6E2F": "js/province/xianggang",
    "\u65B0\u7586": "js/province/xinjiang",
    "\u897F\u85CF": "js/province/xizang",
    "\u4E91\u5357": "js/province/yunnan",
    "\u6D59\u6C5F": "js/province/zhejiang"
  };
  Object.entries(mapOptions).forEach(([displayName, pathFragment]) => {
    const option = document.createElement("option");
    option.value = pathFragment;
    option.textContent = displayName;
    selector.appendChild(option);
  });
  container.style.width = "100%";
  container.style.height = "100%";
  const chart = echarts.init(container, null, { renderer: "svg" });
  function showLoading(message) {
    chart.clear();
    chart.showLoading("default", {
      text: message,
      color: "#4477cb",
      textColor: "#333",
      maskColor: "rgba(255, 255, 255, 0.8)",
      zlevel: 0
    });
  }
  function parseRgb(rgbString) {
    var _a;
    return ((_a = rgbString.match(/\d+/g)) == null ? void 0 : _a.map(Number)) || [0, 0, 0];
  }
  function createColorInputGroup(label, colorKey) {
    const group = document.createElement("div");
    group.className = "color-input-group";
    const labelEl = document.createElement("label");
    labelEl.textContent = label;
    group.appendChild(labelEl);
    const inputsContainer = document.createElement("div");
    const [r, g, b] = parseRgb(colors[colorKey]);
    const rInput = document.createElement("input");
    rInput.type = "number";
    rInput.min = "0";
    rInput.max = "255";
    rInput.value = String(r);
    const gInput = document.createElement("input");
    gInput.type = "number";
    gInput.min = "0";
    gInput.max = "255";
    gInput.value = String(g);
    const bInput = document.createElement("input");
    bInput.type = "number";
    bInput.min = "0";
    bInput.max = "255";
    bInput.value = String(b);
    inputsContainer.append("R:", rInput, "G:", gInput, "B:", bInput);
    group.appendChild(inputsContainer);
    const updateColor = () => {
      colors[colorKey] = `rgb(${rInput.value}, ${gInput.value}, ${bInput.value})`;
      if (colorKey === "background") {
        container.style.backgroundColor = colors.background;
      }
    };
    rInput.addEventListener("input", updateColor);
    gInput.addEventListener("input", updateColor);
    bInput.addEventListener("input", updateColor);
    return group;
  }
  function createHeightInputGroup() {
    const group = document.createElement("div");
    group.className = "color-input-group";
    const labelEl = document.createElement("label");
    labelEl.textContent = "\u9AD8\u5EA6 (px)";
    group.appendChild(labelEl);
    const input = document.createElement("input");
    input.type = "number";
    input.min = "200";
    input.step = "10";
    input.value = String(mapHeight);
    input.style.width = "158px";
    input.addEventListener("input", () => {
      const newHeight = parseInt(input.value, 10);
      if (!isNaN(newHeight) && newHeight >= 200) {
        mapHeight = newHeight;
        blockElement.style.height = `${mapHeight}px`;
        chart.resize();
      }
    });
    group.appendChild(input);
    return group;
  }
  function buildSettingsPanel() {
    settingsPanel.innerHTML = "";
    settingsPanel.appendChild(createHeightInputGroup());
    settingsPanel.appendChild(createColorInputGroup("\u80CC\u666F\u989C\u8272", "background"));
    settingsPanel.appendChild(createColorInputGroup("\u5730\u56FE\u989C\u8272", "areaColor"));
    settingsPanel.appendChild(createColorInputGroup("\u70B9\u4EAE\u989C\u8272", "litColor"));
    const saveButton = document.createElement("button");
    saveButton.textContent = "\u4FDD\u5B58\u8BBE\u7F6E";
    saveButton.onclick = () => __async(null, null, function* () {
      if (!blockId) return;
      try {
        yield fetch("/api/attr/setBlockAttrs", {
          method: "POST",
          body: JSON.stringify({
            id: blockId,
            attrs: {
              "custom-map-colors": JSON.stringify(colors),
              "custom-map-height": String(mapHeight)
            }
          })
        });
        yield fetch("/api/notification/pushMsg", {
          method: "POST",
          body: JSON.stringify({ msg: "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58" })
        });
      } catch (e) {
        console.error("\u4FDD\u5B58\u8BBE\u7F6E\u5931\u8D25:", e);
      }
    });
    settingsPanel.appendChild(saveButton);
  }
  function saveLitData() {
    return __async(this, null, function* () {
      if (!blockId) return;
      try {
        const dataToSave = {
          regions: Array.from(litRegionsSet),
          provinces: Array.from(litProvincesSet)
        };
        yield fetch("/api/attr/setBlockAttrs", {
          method: "POST",
          body: JSON.stringify({
            id: blockId,
            attrs: { "custom-map-lit-data": JSON.stringify(dataToSave) }
          })
        });
        console.log("\u6210\u529F\u4FDD\u5B58\u70B9\u4EAE\u6570\u636E\u3002");
      } catch (e) {
        console.error("\u4FDD\u5B58\u70B9\u4EAE\u6570\u636E\u5931\u8D25:", e);
      }
    });
  }
  function renderChart(mapPathFragment) {
    return __async(this, null, function* () {
      currentMapPath = mapPathFragment;
      showLoading("\u6B63\u5728\u52A0\u8F7D\u5730\u56FE...");
      try {
        const registeredMapName = yield loadAndRegisterMap(mapPathFragment);
        const isChinaMap = mapPathFragment === "js/china";
        const activeSet = isChinaMap ? litProvincesSet : litRegionsSet;
        const mapData = echarts.getMap(registeredMapName).geoJSON.features.map((feature) => {
          const name = feature.properties.name;
          return { name, selected: activeSet.has(name) };
        });
        chart.hideLoading();
        const option = {
          tooltip: { trigger: "item", formatter: "{b}" },
          geo: {
            map: registeredMapName,
            roam: true,
            selectedMode: "multiple",
            itemStyle: {
              areaColor: colors.areaColor,
              borderColor: "#ADADAD"
            },
            emphasis: {
              focus: "none",
              itemStyle: { areaColor: "#a9d0fa" }
            },
            select: {
              itemStyle: {
                areaColor: colors.litColor
              }
            },
            regions: mapData
          },
          series: []
        };
        chart.setOption(option, true);
        backButton.style.display = isChinaMap ? "none" : "block";
      } catch (e) {
        console.error(`\u6E32\u67D3\u5730\u56FE [${mapPathFragment}] \u5931\u8D25:`, e);
        chart.hideLoading();
        container.innerHTML = `<div class="error-msg">\u5730\u56FE\u6E32\u67D3\u5931\u8D25\uFF0C\u8BF7\u6309F12\u67E5\u770B\u63A7\u5236\u53F0\u3002</div>`;
      }
    });
  }
  chart.on("click", (params) => {
    var _a;
    if (!params.name) return;
    const clickedName = params.name;
    if (currentMapPath === "js/china") {
      const targetPath = (_a = Object.entries(mapOptions).find(([displayName]) => displayName === clickedName)) == null ? void 0 : _a[1];
      if (targetPath) {
        selector.value = targetPath;
        renderChart(targetPath);
      }
      return;
    }
    const currentRegisteredMapName = registeredMapsCache.get(currentMapPath);
    if (!currentRegisteredMapName) return;
    if (litRegionsSet.has(clickedName)) {
      litRegionsSet.delete(clickedName);
      const provinceGeoJSON = echarts.getMap(currentRegisteredMapName).geoJSON;
      const citiesInProvince = provinceGeoJSON.features.map((f) => f.properties.name);
      const hasOtherLitCities = citiesInProvince.some((city) => litRegionsSet.has(city));
      if (!hasOtherLitCities) {
        litProvincesSet.delete(currentRegisteredMapName);
      }
    } else {
      litRegionsSet.add(clickedName);
      litProvincesSet.add(currentRegisteredMapName);
    }
    saveLitData();
    renderChart(currentMapPath);
  });
  backButton.addEventListener("click", () => {
    selector.value = "js/china";
    renderChart("js/china");
  });
  selector.addEventListener("change", (e) => {
    const value = e.target.value;
    if (value === "special/settings") {
      container.style.display = "none";
      backButton.style.display = "none";
      settingsPanel.style.display = "flex";
      settingsPanel.style.flexDirection = "column";
      settingsPanel.style.justifyContent = "center";
      settingsPanel.style.alignItems = "center";
      settingsPanel.style.height = "100%";
      settingsPanel.style.gap = "15px";
      buildSettingsPanel();
    } else {
      container.style.display = "block";
      settingsPanel.style.display = "none";
      renderChart(value);
    }
  });
  const resizeObserver = new ResizeObserver(() => chart.resize());
  resizeObserver.observe(container);
  function initialLoad() {
    return __async(this, null, function* () {
      if (blockId) {
        try {
          const response = yield fetch("/api/attr/getBlockAttrs", {
            method: "POST",
            body: JSON.stringify({ id: blockId })
          });
          const res = yield response.json();
          if (res.code === 0) {
            if (res.data["custom-map-colors"]) {
              const savedColors = JSON.parse(res.data["custom-map-colors"]);
              Object.assign(colors, savedColors);
              console.log("\u6210\u529F\u52A0\u8F7D\u5DF2\u4FDD\u5B58\u7684\u989C\u8272\u8BBE\u7F6E\u3002");
            }
            if (res.data["custom-map-height"]) {
              const savedHeight = parseInt(res.data["custom-map-height"], 10);
              if (!isNaN(savedHeight) && savedHeight >= 200) {
                mapHeight = savedHeight;
                console.log("\u6210\u529F\u52A0\u8F7D\u5DF2\u4FDD\u5B58\u7684\u9AD8\u5EA6\u8BBE\u7F6E\u3002");
              }
            }
            if (res.data["custom-map-lit-data"]) {
              const savedLitData = JSON.parse(res.data["custom-map-lit-data"]);
              if (savedLitData.regions) {
                savedLitData.regions.forEach((region) => litRegionsSet.add(region));
              }
              if (savedLitData.provinces) {
                savedLitData.provinces.forEach((province) => litProvincesSet.add(province));
              }
              console.log("\u6210\u529F\u52A0\u8F7D\u5DF2\u4FDD\u5B58\u7684\u70B9\u4EAE\u6570\u636E\u3002");
            }
          }
        } catch (e) {
          console.error("\u52A0\u8F7D\u5DF2\u4FDD\u5B58\u7684\u5C5E\u6027\u5931\u8D25:", e);
        }
      }
      blockElement.style.height = `${mapHeight}px`;
      container.style.backgroundColor = colors.background;
      renderChart("js/china");
    });
  }
  initialLoad();
}
const checkInterval = 100;
const timeout = 1e3;
let elapsedTime = 0;
const self = this;
const intervalId = setInterval(() => {
  const echartsInstance = window.echarts;
  if (echartsInstance && typeof echartsInstance.init === "function") {
    clearInterval(intervalId);
    try {
      initializeApp(self, echartsInstance);
    } catch (e) {
      console.error("\u521D\u59CB\u5316\u5730\u56FE\u5757\u5931\u8D25:", e);
      self.innerHTML = `<div class="error-msg">\u521D\u59CB\u5316\u5730\u56FE\u5757\u5931\u8D25\uFF0C\u8BE6\u60C5\u8BF7\u67E5\u770B\u63A7\u5236\u53F0\u3002</div>`;
    }
  } else {
    elapsedTime += checkInterval;
    if (elapsedTime > timeout) {
      clearInterval(intervalId);
      console.error("\u7B49\u5F85\u539F\u751F ECharts \u5E93\u52A0\u8F7D\u8D85\u65F6\u3002");
      self.innerHTML = `<div class="error-msg">\u539F\u751F ECharts \u5E93\u52A0\u8F7D\u8D85\u65F6\u3002\u8BF7\u786E\u4FDD\u6587\u6863\u4E2D\u5B58\u5728\u4E00\u4E2A\u5B98\u65B9\u56FE\u8868\u5757\u3002</div>`;
    }
  }
}, checkInterval);


开始记录你的旅游足迹吧!

  • 千千插件

    千千块(自定义块 css 和 js)
    可以用 ai 提示词来无限创作思源笔记

    32 引用 • 69 回帖
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    28446 引用 • 119783 回帖
  • 千千块模板
    8 引用 • 18 回帖
  • 千千块
    25 引用 • 61 回帖
3 操作
lovexmm521 在 2025-09-30 18:36:02 更新了该帖
lovexmm521 在 2025-09-30 18:19:16 更新了该帖
lovexmm521 在 2025-09-30 17:53:22 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
lovexmm521 MOD
窈窕淑女,君子好逑 爱发电:https://afdian.com/a/QianQian517

推荐标签 标签

  • Ubuntu

    Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。

    127 引用 • 169 回帖
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    215 引用 • 358 回帖
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    524 引用 • 4602 回帖 • 731 关注
  • 电影

    这是一个不能说的秘密。

    125 引用 • 610 回帖
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    695 引用 • 538 回帖 • 1 关注
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖 • 2 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 693 关注
  • WiFiDog

    WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。

    1 引用 • 7 回帖 • 633 关注
  • Sillot

    Insights(注意当前设置 master 为默认分支)

    汐洛彖夲肜矩阵(Sillot T☳Converbenk Matrix),致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点。其中汐洛绞架(Sillot-Gibbet)基于自思源笔记(siyuan-note),前身是思源笔记汐洛版(更早是思源笔记汐洛分支),是智慧新录乄终端(多端融合,移动端优先)。

    主仓库地址:Hi-Windom/Sillot

    文档地址:sillot.db.sc.cn

    注意事项:

    1. ⚠️ 汐洛仍在早期开发阶段,尚不稳定
    2. ⚠️ 汐洛并非面向普通用户设计,使用前请了解风险
    3. ⚠️ 汐洛绞架基于思源笔记,开发者尽最大努力与思源笔记保持兼容,但无法实现 100% 兼容
    29 引用 • 25 回帖 • 152 关注
  • Facebook

    Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。

    4 引用 • 15 回帖 • 443 关注
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 702 关注
  • 千千插件

    千千块(自定义块 css 和 js)
    可以用 ai 提示词来无限创作思源笔记

    32 引用 • 69 回帖
  • Bootstrap

    Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。

    18 引用 • 33 回帖 • 646 关注
  • 博客

    记录并分享人生的经历。

    274 引用 • 2393 回帖 • 1 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    167 引用 • 408 回帖 • 494 关注
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 67 回帖 • 436 关注
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    99 引用 • 367 回帖 • 1 关注
  • 又拍云

    又拍云是国内领先的 CDN 服务提供商,国家工信部认证通过的“可信云”,乌云众测平台认证的“安全云”,为移动时代的创业者提供新一代的 CDN 加速服务。

    20 引用 • 37 回帖 • 577 关注
  • 百度

    百度(Nasdaq:BIDU)是全球最大的中文搜索引擎、最大的中文网站。2000 年 1 月由李彦宏创立于北京中关村,致力于向人们提供“简单,可依赖”的信息获取方式。“百度”二字源于中国宋朝词人辛弃疾的《青玉案·元夕》词句“众里寻他千百度”,象征着百度对中文信息检索技术的执著追求。

    63 引用 • 785 回帖 • 46 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 838 关注
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 3 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    950 引用 • 1460 回帖 • 2 关注
  • SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。

    4 引用 • 7 回帖
  • 小薇

    小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。

    由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!

    35 引用 • 468 回帖 • 768 关注
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    40 引用 • 157 回帖
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 3 关注