如何在d3js中为文本元素添加href并使用缩放功能

huangapple go评论99阅读模式
英文:

How to give href to text element in d3js with zoom function

问题

我有一个带有缩放功能的d3.js图表。图表上有一些标签,我希望这些标签可以像href链接一样可点击。似乎href与缩放功能发生了冲突。只有href或缩放中的一个有效。两者不能一起工作。只有当我注释掉缩放代码时,href才有效。只有当我注释掉href代码时,缩放才有效。如何使href和缩放同时有效?

  1. var margin = {top: 10, right: 10, bottom: 20, left: 40},
  2. width = 450 - margin.left - margin.right,
  3. height = 380 - margin.top - margin.bottom;
  4. let data = [
  5. [95,'0.7142914222463218','#0000FF','(535/535)',0,"www.a.net"],
  6. [97,'1.5753396297416553','#00FF00','(267/267)',0,"www.b.net"],
  7. [97,'3.965827978600963','#808080','(14/14)',0,"www.c.net"],
  8. [100,'5.340348252548648','#FFAFAF','(7/8)',0,"www.d.net"],
  9. [100,'5.699038509168024','#FFAFAF','(1/8)',0,"www.e.net"],
  10. [77,'19.506535109626878','#FF0000','(5/5)',0,"www.f.net"],
  11. [77,'19.506535109626878','#FF0000','(2/2)',42,"www.g.net"]
  12. ];
  13. var SVG = d3.select("body")
  14. .append("svg")
  15. .attr("xmlns", "http://www.w3.org/2000/svg")
  16. .attr("xlink", "http://www.w3.org/1999/xlink")
  17. .attr("width", width + margin.left + margin.right)
  18. .attr("height", height + margin.top + margin.bottom)
  19. SVG.append("g")
  20. .attr("transform",
  21. "translate(" + margin.left + "," + margin.top + ")");
  22. // Add X axis
  23. var x = d3.scaleLinear()
  24. .domain([1, 120])
  25. .range([ 0, width ]);
  26. var xAxis = SVG.append("g")
  27. .attr("transform", "translate(0," + height + ")")
  28. .call(d3.axisBottom(x));
  29. // Add Y axis
  30. var y = d3.scaleLinear()
  31. .domain([0, 39])
  32. .range([ height, 0 ]);
  33. var yAxis = SVG.append("g")
  34. .call(d3.axisLeft(y));
  35. // Add a clipPath: everything out of this area won't be drawn.
  36. var clip = SVG.append("defs").append("clipPath")
  37. .attr("id", "clip")
  38. .append("rect")
  39. .attr("width", width )
  40. .attr("height", height )
  41. .attr("x", 0)
  42. .attr("y", 0);
  43. // Create the scatter variable: where both the circles and the brush take place
  44. var scatter = SVG.append('g')
  45. .attr("clip-path", "url(#clip)")
  46. scatter
  47. .selectAll("circle")
  48. .data(data)
  49. .enter()
  50. .append("circle")
  51. .attr("cx", function (d) { return x(d[0]); } )
  52. .attr("cy", function (d) { return y(d[1]); } )
  53. .attr("r", 5)
  54. .style("fill", function (d) { return "black"; } )
  55. .style("opacity", 0.5);
  56. scatter.selectAll("a")
  57. .data(data)
  58. .enter().append("a")
  59. //.attr({"xlink:href": "#"})
  60. .on("click", function() { window.open("https://google.com"); })
  61. .append("text")
  62. .attr("x", (d) => x(d[0])+d[4])
  63. .attr("y", (d) => y(d[1]))
  64. .text((d) => d[3])
  65. .style("fill", (d) => d[2]);
  66. // Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
  67. var zoom = d3.zoom()
  68. .scaleExtent([.5, 1000]) // This control how much you can unzoom (x0.5) and zoom (x20)
  69. .extent([[0, 0], [width, height]])
  70. .on("zoom", updateChart);
  71. // This add an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
  72. SVG.append("rect")
  73. .attr("width", width)
  74. .attr("height", height)
  75. .style("fill", "none")
  76. .style("pointer-events", "all")
  77. .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
  78. .call(zoom);
  79. // now the user can zoom and it will trigger the function called updateChart
  80. // A function that updates the chart when the user zoom and thus new boundaries are available
  81. function updateChart() {
  82. // recover the new scale
  83. var newX = d3.event.transform.rescaleX(x);
  84. var newY = d3.event.transform.rescaleY(y);
  85. // update axes with these new boundaries
  86. xAxis.call(d3.axisBottom(newX))
  87. yAxis.call(d3.axisLeft(newY))
  88. // update circle position
  89. scatter
  90. .selectAll("circle")
  91. .attr('cx', function(d) {return newX(d[0])})
  92. .attr('cy', function(d) {return newY(d[1])});
  93. scatter.selectAll("text")
  94. .attr("x", (d) => newX(d[0])+d[4])
  95. .attr("y", (d) => newY(d[1]))
  96. }
  97. //})
  1. <head>
  2. <!-- Load d3.js -->
  3. <script src="https://d3js.org/d3.v4.js"></script>
  4. </head>
  5. <body></body>
英文:

I have a d3 js chart with zoom function. There are some labels on the chart. I want those labels to be clickable like href links. It looks like href is interfering with zoom function. Only either of href or zoom is working. Both are not working together. Only if I comment zoom code, href is working. And only if I comment href code, zoom is working. How to make both href and zoom working together?

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

  1. var margin = {top: 10, right: 10, bottom: 20, left: 40},
  2. width = 450 - margin.left - margin.right,
  3. height = 380 - margin.top - margin.bottom;
  4. let data = [
  5. [95,&#39;0.7142914222463218&#39;,&#39;#0000FF&#39;,&#39;(535/535)&#39;,0,&quot;www.a.net&quot;],[97,&#39;1.5753396297416553&#39;,&#39;#00FF00&#39;,&#39;(267/267)&#39;,0,&quot;www.b.net&quot;],[97,&#39;3.965827978600963&#39;,&#39;#808080&#39;,&#39;(14/14)&#39;,0,&quot;www.c.net&quot;],[100,&#39;5.340348252548648&#39;,&#39;#FFAFAF&#39;,&#39;(7/8)&#39;,0,&quot;www.d.net&quot;],[100,&#39;5.699038509168024&#39;,&#39;#FFAFAF&#39;,&#39;(1/8)&#39;,0,&quot;www.e.net&quot;],[77,&#39;19.506535109626878&#39;,&#39;#FF0000&#39;,&#39;(5/5)&#39;,0,&quot;www.f.net&quot;],[77,&#39;19.506535109626878&#39;,&#39;#FF0000&#39;,&#39;(2/2)&#39;,42,&quot;www.g.net&quot;]]
  6. var SVG = d3.select(&quot;body&quot;)
  7. .append(&quot;svg&quot;)
  8. .attr(&quot;xmlns&quot;, &quot;http://www.w3.org/2000/svg&quot;)
  9. .attr(&quot;xlink&quot;, &quot;http://www.w3.org/1999/xlink&quot;)
  10. .attr(&quot;width&quot;, width + margin.left + margin.right)
  11. .attr(&quot;height&quot;, height + margin.top + margin.bottom)
  12. SVG.append(&quot;g&quot;)
  13. .attr(&quot;transform&quot;,
  14. &quot;translate(&quot; + margin.left + &quot;,&quot; + margin.top + &quot;)&quot;);
  15. // Add X axis
  16. var x = d3.scaleLinear()
  17. .domain([1, 120])
  18. .range([ 0, width ]);
  19. var xAxis = SVG.append(&quot;g&quot;)
  20. .attr(&quot;transform&quot;, &quot;translate(0,&quot; + height + &quot;)&quot;)
  21. .call(d3.axisBottom(x));
  22. // Add Y axis
  23. var y = d3.scaleLinear()
  24. .domain([0, 39])
  25. .range([ height, 0]);
  26. var yAxis = SVG.append(&quot;g&quot;)
  27. .call(d3.axisLeft(y));
  28. // Add a clipPath: everything out of this area won&#39;t be drawn.
  29. var clip = SVG.append(&quot;defs&quot;).append(&quot;SVG:clipPath&quot;)
  30. .attr(&quot;id&quot;, &quot;clip&quot;)
  31. .append(&quot;SVG:rect&quot;)
  32. .attr(&quot;width&quot;, width )
  33. .attr(&quot;height&quot;, height )
  34. .attr(&quot;x&quot;, 0)
  35. .attr(&quot;y&quot;, 0);
  36. // Create the scatter variable: where both the circles and the brush take place
  37. var scatter = SVG.append(&#39;g&#39;)
  38. .attr(&quot;clip-path&quot;, &quot;url(#clip)&quot;)
  39. scatter
  40. .selectAll(&quot;circle&quot;)
  41. .data(data)
  42. .enter()
  43. .append(&quot;circle&quot;)
  44. .attr(&quot;cx&quot;, function (d) { return x(d[0]); } )
  45. .attr(&quot;cy&quot;, function (d) { return y(d[1]); } )
  46. .attr(&quot;r&quot;, 5)
  47. .style(&quot;fill&quot;, function (d) { &quot;black&quot;; } )
  48. .style(&quot;opacity&quot;, 0.5);
  49. scatter.selectAll(&quot;a&quot;)
  50. .data(data)
  51. .enter().append(&quot;a&quot;)
  52. //.attr({&quot;xlink:href&quot;: &quot;#&quot;})
  53. .on(&quot;click&quot;, function() { window.open(&quot;https://google.com&quot;); })
  54. .append(&quot;text&quot;)
  55. .attr(&quot;x&quot;, (d) =&gt; x(d[0])+d[4])
  56. .attr(&quot;y&quot;, (d) =&gt; y(d[1]))
  57. .text((d) =&gt; d[3])
  58. .style(&quot;fill&quot;, (d) =&gt; d[2]);
  59. // Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
  60. var zoom = d3.zoom()
  61. .scaleExtent([.5, 1000]) // This control how much you can unzoom (x0.5) and zoom (x20)
  62. .extent([[0, 0], [width, height]])
  63. .on(&quot;zoom&quot;, updateChart);
  64. // This add an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
  65. SVG.append(&quot;rect&quot;)
  66. .attr(&quot;width&quot;, width)
  67. .attr(&quot;height&quot;, height)
  68. .style(&quot;fill&quot;, &quot;none&quot;)
  69. .style(&quot;pointer-events&quot;, &quot;all&quot;)
  70. .attr(&#39;transform&#39;, &#39;translate(&#39; + margin.left + &#39;,&#39; + margin.top + &#39;)&#39;)
  71. .call(zoom);
  72. // now the user can zoom and it will trigger the function called updateChart
  73. // A function that updates the chart when the user zoom and thus new boundaries are available
  74. function updateChart() {
  75. // recover the new scale
  76. var newX = d3.event.transform.rescaleX(x);
  77. var newY = d3.event.transform.rescaleY(y);
  78. // update axes with these new boundaries
  79. xAxis.call(d3.axisBottom(newX))
  80. yAxis.call(d3.axisLeft(newY))
  81. // update circle position
  82. scatter
  83. .selectAll(&quot;circle&quot;)
  84. .attr(&#39;cx&#39;, function(d) {return newX(d[0])})
  85. .attr(&#39;cy&#39;, function(d) {return newY(d[1])});
  86. scatter.selectAll(&quot;text&quot;)
  87. .attr(&quot;x&quot;, (d) =&gt; newX(d[0])+d[4])
  88. .attr(&quot;y&quot;, (d) =&gt; newY(d[1]))
  89. }
  90. //})

<!-- language: lang-html -->

  1. &lt;head&gt;
  2. &lt;!-- Load d3.js --&gt;
  3. &lt;script src=&quot;https://d3js.org/d3.v4.js&quot;&gt;&lt;/script&gt;
  4. &lt;/head&gt;
  5. &lt;body&gt;&lt;/body&gt;

<!-- end snippet -->

答案1

得分: 1

以下是您要翻译的内容:

  1. <!-- begin snippet: js hide: false console: true babel: false -->
  2. <!-- language: lang-js -->
  3. var margin = {top: 10, right: 10, bottom: 20, left: 40},
  4. width = 450 - margin.left - margin.right,
  5. height = 380 - margin.top - margin.bottom;
  6. let data = [
  7. [95,'0.7142914222463218','#0000FF','(535/535)',0,"www.a.net"],[97,'1.5753396297416553','#00FF00','(267/267)',0,"www.b.net"],[97,'3.965827978600963','#808080','(14/14)',0,"www.c.net"],[100,'5.340348252548648','#FFAFAF','(7/8)',0,"www.d.net"],[100,'5.699038509168024','#FFAFAF','(1/8)',0,"www.e.net"],[77,'19.506535109626878','#FF0000','(5/5)',0,"www.f.net"],[77,'19.506535109626878','#FF0000','(2/2)',42,"www.g.net"]]
  8. var SVG = d3.select("body")
  9. .append("svg")
  10. .attr("xmlns", "http://www.w3.org/2000/svg")
  11. .attr("xlink", "http://www.w3.org/1999/xlink")
  12. .attr("width", width + margin.left + margin.right)
  13. .attr("height", height + margin.top + margin.bottom)
  14. SVG.append("g")
  15. .attr("transform",
  16. "translate(" + margin.left + "," + margin.top + ")");
  17. // Add X axis
  18. var x = d3.scaleLinear()
  19. .domain([1, 120])
  20. .range([ 0, width ]);
  21. var xAxis = SVG.append("g")
  22. .attr("transform", "translate(0," + height + ")")
  23. .call(d3.axisBottom(x));
  24. // Add Y axis
  25. var y = d3.scaleLinear()
  26. .domain([0, 39])
  27. .range([ height, 0 ]);
  28. var yAxis = SVG.append("g")
  29. .call(d3.axisLeft(y));
  30. // Add a clipPath: everything out of this area won't be drawn.
  31. var clip = SVG.append("defs").append("clipPath")
  32. .attr("id", "clip")
  33. .append("rect")
  34. .attr("width", width )
  35. .attr("height", height )
  36. .attr("x", 0)
  37. .attr("y", 0);
  38. // Create the scatter variable: where both the circles and the brush take place
  39. var scatter = SVG.append('g')
  40. .attr("clip-path", "url(#clip)")
  41. scatter
  42. .selectAll("circle")
  43. .data(data)
  44. .enter()
  45. .append("circle")
  46. .attr("cx", function (d) { return x(d[0]); } )
  47. .attr("cy", function (d) { return y(d[1]); } )
  48. .attr("r", 5)
  49. .style("fill", function (d) { "black"; } )
  50. .style("opacity", 0.5);
  51. scatter.selectAll(".textg")
  52. .data(data)
  53. .enter().append("g")
  54. .attr('class','textg')
  55. .style('cursor','pointer')
  56. //.attr({"xlink:href": "#"})
  57. .on("click", function() { window.open("https://google.com"); })
  58. .append("text")
  59. // .attr({x: (d) => x(d[0])+d[4], y: (d) => y(d[1])})
  60. .attr("x", (d) => x(d[0])+d[4])
  61. .attr("y", (d) => y(d[1]))
  62. .text((d) => d[3])
  63. .style("fill", (d) => d[2]);
  64. // Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
  65. var zoom = d3.zoom()
  66. .scaleExtent([.5, 1000]) // This control how much you can unzoom (x0.5) and zoom (x20)
  67. .extent([[0, 0], [width, height]])
  68. .on("zoom", updateChart);
  69. // This add an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
  70. SVG.append("rect")
  71. .attr("width", width)
  72. .attr("height", height)
  73. .style("fill", "none")
  74. .style("pointer-events", "all")
  75. .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
  76. .call(zoom);
  77. scatter.raise()
  78. // now the user can zoom and it will trigger the function called updateChart
  79. // A function that updates the chart when the user zoom and thus new boundaries are available
  80. function updateChart() {
  81. // recover the new scale
  82. var newX = d3.event.transform.rescaleX(x);
  83. var newY = d3.event.transform.rescaleY(y);
  84. // update axes with these new boundaries
  85. xAxis.call(d3.axisBottom(newX))
  86. yAxis.call(d3.axisLeft(newY))
  87. // update circle position
  88. scatter
  89. .selectAll("circle")
  90. .attr('cx', function(d) {return newX(d[0])})
  91. .attr('cy', function(d) {return newY(d[1])});
  92. scatter.selectAll("text")
  93. .attr("x", (d) => newX(d[0])+d[4])
  94. .attr("y", (d) => newY(d[1]))
  95. scatter.raise()
  96. }
  97. //})
  98. <!-- language: lang-html -->
  99. <head>
  100. <!-- Load d3.js -->
  101. <script src="https://d3js.org/d3.v4.js"></script>
  102. </head>
  103. <body></body>
  104. <!-- end snippet -->

请注意,上述内容包括HTML和JavaScript代码,已根据您的要求进行翻译。如果您需要进一步的帮助,请随时提问。

英文:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

  1. var margin = {top: 10, right: 10, bottom: 20, left: 40},
  2. width = 450 - margin.left - margin.right,
  3. height = 380 - margin.top - margin.bottom;
  4. let data = [
  5. [95,&#39;0.7142914222463218&#39;,&#39;#0000FF&#39;,&#39;(535/535)&#39;,0,&quot;www.a.net&quot;],[97,&#39;1.5753396297416553&#39;,&#39;#00FF00&#39;,&#39;(267/267)&#39;,0,&quot;www.b.net&quot;],[97,&#39;3.965827978600963&#39;,&#39;#808080&#39;,&#39;(14/14)&#39;,0,&quot;www.c.net&quot;],[100,&#39;5.340348252548648&#39;,&#39;#FFAFAF&#39;,&#39;(7/8)&#39;,0,&quot;www.d.net&quot;],[100,&#39;5.699038509168024&#39;,&#39;#FFAFAF&#39;,&#39;(1/8)&#39;,0,&quot;www.e.net&quot;],[77,&#39;19.506535109626878&#39;,&#39;#FF0000&#39;,&#39;(5/5)&#39;,0,&quot;www.f.net&quot;],[77,&#39;19.506535109626878&#39;,&#39;#FF0000&#39;,&#39;(2/2)&#39;,42,&quot;www.g.net&quot;]]
  6. var SVG = d3.select(&quot;body&quot;)
  7. .append(&quot;svg&quot;)
  8. .attr(&quot;xmlns&quot;, &quot;http://www.w3.org/2000/svg&quot;)
  9. .attr(&quot;xlink&quot;, &quot;http://www.w3.org/1999/xlink&quot;)
  10. .attr(&quot;width&quot;, width + margin.left + margin.right)
  11. .attr(&quot;height&quot;, height + margin.top + margin.bottom)
  12. SVG.append(&quot;g&quot;)
  13. .attr(&quot;transform&quot;,
  14. &quot;translate(&quot; + margin.left + &quot;,&quot; + margin.top + &quot;)&quot;);
  15. // Add X axis
  16. var x = d3.scaleLinear()
  17. .domain([1, 120])
  18. .range([ 0, width ]);
  19. var xAxis = SVG.append(&quot;g&quot;)
  20. .attr(&quot;transform&quot;, &quot;translate(0,&quot; + height + &quot;)&quot;)
  21. .call(d3.axisBottom(x));
  22. // Add Y axis
  23. var y = d3.scaleLinear()
  24. .domain([0, 39])
  25. .range([ height, 0]);
  26. var yAxis = SVG.append(&quot;g&quot;)
  27. .call(d3.axisLeft(y));
  28. // Add a clipPath: everything out of this area won&#39;t be drawn.
  29. var clip = SVG.append(&quot;defs&quot;).append(&quot;SVG:clipPath&quot;)
  30. .attr(&quot;id&quot;, &quot;clip&quot;)
  31. .append(&quot;SVG:rect&quot;)
  32. .attr(&quot;width&quot;, width )
  33. .attr(&quot;height&quot;, height )
  34. .attr(&quot;x&quot;, 0)
  35. .attr(&quot;y&quot;, 0);
  36. // Create the scatter variable: where both the circles and the brush take place
  37. var scatter = SVG.append(&#39;g&#39;)
  38. .attr(&quot;clip-path&quot;, &quot;url(#clip)&quot;)
  39. scatter
  40. .selectAll(&quot;circle&quot;)
  41. .data(data)
  42. .enter()
  43. .append(&quot;circle&quot;)
  44. .attr(&quot;cx&quot;, function (d) { return x(d[0]); } )
  45. .attr(&quot;cy&quot;, function (d) { return y(d[1]); } )
  46. .attr(&quot;r&quot;, 5)
  47. .style(&quot;fill&quot;, function (d) { &quot;black&quot;; } )
  48. .style(&quot;opacity&quot;, 0.5);
  49. scatter.selectAll(&quot;.textg&quot;)
  50. .data(data)
  51. .enter().append(&quot;g&quot;)
  52. .attr(&#39;class&#39;,&#39;textg&#39;)
  53. .style(&#39;cursor&#39;,&#39;pointer&#39;)
  54. //.attr({&quot;xlink:href&quot;: &quot;#&quot;})
  55. .on(&quot;click&quot;, function() { window.open(&quot;https://google.com&quot;); })
  56. .append(&quot;text&quot;)
  57. // .attr({x: (d) =&gt; x(d[0])+d[4], y: (d) =&gt; y(d[1])})
  58. .attr(&quot;x&quot;, (d) =&gt; x(d[0])+d[4])
  59. .attr(&quot;y&quot;, (d) =&gt; y(d[1]))
  60. .text((d) =&gt; d[3])
  61. .style(&quot;fill&quot;, (d) =&gt; d[2]);
  62. // Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
  63. var zoom = d3.zoom()
  64. .scaleExtent([.5, 1000]) // This control how much you can unzoom (x0.5) and zoom (x20)
  65. .extent([[0, 0], [width, height]])
  66. .on(&quot;zoom&quot;, updateChart);
  67. // This add an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
  68. SVG.append(&quot;rect&quot;)
  69. .attr(&quot;width&quot;, width)
  70. .attr(&quot;height&quot;, height)
  71. .style(&quot;fill&quot;, &quot;none&quot;)
  72. .style(&quot;pointer-events&quot;, &quot;all&quot;)
  73. .attr(&#39;transform&#39;, &#39;translate(&#39; + margin.left + &#39;,&#39; + margin.top + &#39;)&#39;)
  74. .call(zoom);
  75. scatter.raise()
  76. // now the user can zoom and it will trigger the function called updateChart
  77. // A function that updates the chart when the user zoom and thus new boundaries are available
  78. function updateChart() {
  79. // recover the new scale
  80. var newX = d3.event.transform.rescaleX(x);
  81. var newY = d3.event.transform.rescaleY(y);
  82. // update axes with these new boundaries
  83. xAxis.call(d3.axisBottom(newX))
  84. yAxis.call(d3.axisLeft(newY))
  85. // update circle position
  86. scatter
  87. .selectAll(&quot;circle&quot;)
  88. .attr(&#39;cx&#39;, function(d) {return newX(d[0])})
  89. .attr(&#39;cy&#39;, function(d) {return newY(d[1])});
  90. scatter.selectAll(&quot;text&quot;)
  91. .attr(&quot;x&quot;, (d) =&gt; newX(d[0])+d[4])
  92. .attr(&quot;y&quot;, (d) =&gt; newY(d[1]))
  93. scatter.raise()
  94. }
  95. //})

<!-- language: lang-html -->

  1. &lt;head&gt;
  2. &lt;!-- Load d3.js --&gt;
  3. &lt;script src=&quot;https://d3js.org/d3.v4.js&quot;&gt;&lt;/script&gt;
  4. &lt;/head&gt;
  5. &lt;body&gt;&lt;/body&gt;

<!-- end snippet -->

Hello, i think your problem is that d3.zoom is putting e rect to capture mouse events.

So one solution is to put the &lt;g/&gt; on top of the rect so you can click your elements.

Luckily d3 has an easy way to bring an element to the top:
scatter.raise()

Click doesn't work on sniper because you can't open a new window on snippet sandbox, however you can see the cursor change on text over

huangapple
  • 本文由 发表于 2023年8月5日 00:08:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76837583.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定