先看看效果展示,实现鼠标点击提示框链接进入跑步详情页面,整体效果 跑步总结

为了实现提示框可以点击,来回折腾浪费了很长时间,记录下来以备不时之需。
在ECharts的示例编辑里的tooltip配置项内容:

"tooltip": {
  "triggerOn": "click",
  "enterable": true,
  "formatter":function (params) {
    if (params.value[1] <= 3) {
      return '';
      } else {
        return (
          '<a href="/posts/run/' + params.value[2] + '/" target="_blank\">' +
          params.value[0].slice(-5) +
          '</a> <br>' +
          Number(params.value[1]).toFixed(2) +
          'km '
          );
      }
  }
}

Hugo编译时报如下错误:

Error: error building site: "xx/content/posts/run/run_stats.md:135:1": "xx/layouts/_default/_markup/render-codeblock-echarts.html:6:37": execute of template failed: template: _default/_markup/render-codeblock-echarts.html:6:37: executing "_default/_markup/render-codeblock-echarts.html" at <transform.Unmarshal>: error calling Unmarshal: "_stream.json:5:1": unmarshal failed: invalid character 'u' in literal false (expecting 'a')

Hugo编译时会使用transform.Unmarshal校验json内容,formmater的参数内容不符合规范内容。

有事问OpenAIjs 对象包含函数的序列化,得到如下的答案。

const obj = {
  name: "Alice",
  value: {
    greet: function() {
      console.log("Hello!");
    }
  }
};

// 自定义序列化函数
function serialize(obj) {
  return JSON.stringify(obj, function(key, value) {
    if (typeof value === 'function') {
      return value.toString();
    }
    return value;
  });
}

// 自定义反序列化函数
function deserialize(serialized) {
  return JSON.parse(serialized, function(key, value) {
    if (typeof value === 'string' && value.startsWith('function')) {
      return eval('(' + value + ')');
    }
    return value;
  });
}

const serialized = serialize(obj);
console.log(serialized);
// 输出: {"name":"Alice","value":{"greet":"function() {\n      console.log(\"Hello!\");\n    }"}}

const deserialized = deserialize(serialized);
console.log(deserialized);
deserialized.value.greet(); // 输出: Hello!

于是将formmater的函数内容序列化成字符串。

"tooltip": {
  "triggerOn": "click",
  "enterable": true,
  "formatter":"function (params) {\\n        if (params.value[1] <= 3) {\\n          return '';\\n        } else {\\n          return (\\n            '<a href=\\"/posts/run/' + params.value[2] + '/\\" target=\\"_blank\\">' +\\n            params.value[0].slice(-5) +\\n            '</a> <br>' +\\n            Number(params.value[1]).toFixed(2) +\\n            'km '\\n          );\\n        }\\n      }"
}

接下来就是修改Hugo集成EChart插件的内容,配置参考Hugo集成ECharts生成心仪的图表
修改/assets/mods/echarts/index.ts文件,在declare global下方加入如下代码:

declare global {
  interface Window {
    echarts: any
  }
}

function deserialize(serialized: string): any {
  return JSON.parse(serialized, (key: string, value: any) => {
    if (typeof value === 'string' && value.startsWith('function')) {
      return eval('(' + value + ')');
    }
    return value;
  });
}

(() => {
  ...

代码可以实现将函数反序列化成对象设置到配置项。需要注意的是序列化和反序列化函数时,要小心安全性问题,尤其是在处理不受信任的输入时,使用 eval 函数可能会带来安全风险。

修改后重新运行Hugo的编译命令。