Browse Source

feat(stock): 实现股票行情接口和动态刷新功能

- 新增 StockController 类,提供股票行情数据接口
- 在前端实现股票信息的定时刷新,支持局部更新
- 更新投资页面和页脚组件,整合股票信息展示,添加数据属性以支持动态更新
- 移除冗余的定时刷新脚本,优化代码结构
master
peijunlei 2 weeks ago
parent
commit
f77ea22045
  1. 35
      app/controller/frontend/StockController.php
  2. 84
      public/themes/dist_static/static/js/common.js
  3. 46
      public/themes/website/1/zh/demo/components/footer.html
  4. 64
      public/themes/website/1/zh/demo/investment.html

35
app/controller/frontend/StockController.php

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace app\controller\frontend;
/**
* 股票行情接口(供页脚 / 投资者关系局部刷新)
*/
class StockController extends BaseController
{
public function quote(): \think\response\Json
{
$code = (string)$this->request->param('code', '603933');
if (!preg_match('/^\d{6}$/', $code)) {
$code = '603933';
}
$stock = getStockInfo($code);
$change = (string)($stock['change'] ?? '--');
$color = '';
if ($change !== '--' && $change !== '') {
if ($change[0] === '+') {
$color = '#ef4335';
} elseif ($change[0] === '-') {
$color = '#00b894';
}
}
$stock['color'] = $color;
$market = $code[0] === '6' ? 'sh' : 'sz';
$stock['chart'] = "https://image.sinajs.cn/newchart/min/n/{$market}{$code}.gif";
return jsonReturn(0, 'success', $stock);
}
}

84
public/themes/dist_static/static/js/common.js

@ -242,5 +242,89 @@ $(function() {
if ($("body").hasClass("is-mask-open")) return; if ($("body").hasClass("is-mask-open")) return;
unlockPageScroll(); unlockPageScroll();
}); });
// 股票行情局部刷新(页脚 + 投资者关系页),不整页 reload
(function initStockQuoteRefresh() {
const INTERVAL = 60 * 1000;
let timer = null;
const setText = (el, text) => {
if (el) el.textContent = text;
};
const setColor = (el, color) => {
if (!el) return;
if (color) el.style.color = color;
else el.style.removeProperty("color");
};
const applyQuote = (data) => {
if (!data) return;
const color = data.color || "";
const changeText = `${data.change || "--"}\u00a0\u00a0${data.changePercent || "--"}`;
document.querySelectorAll("[data-stock-quote]").forEach((root) => {
const priceEl = root.querySelector('[data-stock="price"]');
const unitEl = root.querySelector('[data-stock="unit"]');
const changeEl = root.querySelector('[data-stock="change"]');
setText(priceEl, data.price ?? "--");
setText(changeEl, changeText);
setColor(priceEl, color);
setColor(unitEl, color);
setColor(changeEl, color);
});
setText(document.querySelector('[data-stock="high"]'), data.high ?? "--");
setText(document.querySelector('[data-stock="low"]'), data.low ?? "--");
setText(document.querySelector('[data-stock="volume"]'), data.volume ?? "--");
setText(document.querySelector('[data-stock="amount"]'), data.amount ?? "--");
const timeEl = document.querySelector('[data-stock="time"]');
if (timeEl && data.updateTime) {
timeEl.textContent = `截止 ${data.updateTime}*报价有十五分钟或以上延迟。`;
}
const chartEl = document.querySelector('[data-stock="chart"]');
if (chartEl && data.chart) {
chartEl.src = `${data.chart}${data.chart.includes("?") ? "&" : "?"}t=${Date.now()}`;
}
};
const fetchQuote = () => {
const code =
document.querySelector("[data-stock-quote]")?.getAttribute("data-stock-code") || "603933";
return fetch(`/stock/quote?code=${encodeURIComponent(code)}`, {
credentials: "same-origin",
headers: { Accept: "application/json" }
})
.then((res) => (res.ok ? res.json() : null))
.then((json) => {
if (!json || (json.code !== 0 && json.code !== 200)) return;
applyQuote(json.data || json);
})
.catch(() => {});
};
const start = () => {
if (timer) return;
timer = setInterval(fetchQuote, INTERVAL);
};
const stop = () => {
if (!timer) return;
clearInterval(timer);
timer = null;
};
if (!document.querySelector("[data-stock-quote], [data-stock='chart']")) return;
start();
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
stop();
} else {
fetchQuote();
start();
}
});
})();
}); });
//#endregion //#endregion

46
public/themes/website/1/zh/demo/components/footer.html

@ -7,10 +7,10 @@
<img src="{hcTaglib:setting name='dark_logo' type='2' /}" alt="RAYNEN 睿能科技" width="252" height="26" /> <img src="{hcTaglib:setting name='dark_logo' type='2' /}" alt="RAYNEN 睿能科技" width="252" height="26" />
</div> </div>
<p class="site-footer__stock">股票代码: 603933</p> <p class="site-footer__stock">股票代码: 603933</p>
<p class="site-footer__quote" aria-label="股票行情占位">
<span class="site-footer__quote-price" style="{$priceColor}">{$stockInfo.price}</span>
<span class="site-footer__quote-unit" style="{$priceColor}"></span>
<span class="site-footer__quote-change" style="{$priceColor}">{$stockInfo.change}&nbsp;&nbsp;{$stockInfo.changePercent}</span>
<p class="site-footer__quote" aria-label="股票行情" data-stock-quote data-stock-code="603933">
<span class="site-footer__quote-price" data-stock="price" style="{$priceColor}">{$stockInfo.price}</span>
<span class="site-footer__quote-unit" data-stock="unit" style="{$priceColor}"></span>
<span class="site-footer__quote-change" data-stock="change" style="{$priceColor}">{$stockInfo.change}&nbsp;&nbsp;{$stockInfo.changePercent}</span>
</p> </p>
<p class="site-footer__follow">关注我们:</p> <p class="site-footer__follow">关注我们:</p>
<div class="site-footer__qrs"> <div class="site-footer__qrs">
@ -58,41 +58,3 @@
</div> </div>
</div> </div>
</footer> </footer>
<script>
(function() {
// 股票数据刷新 - 使用页面内嵌数据,定时刷新整个页面
var refreshInterval = 60000; // 1分钟
var stockTimer = null;
// 定时刷新页面
function startStockRefresh() {
stockTimer = setInterval(function() {
// 重新加载当前页面
window.location.reload();
}, refreshInterval);
}
// 页面加载完成后开始
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startStockRefresh);
} else {
startStockRefresh();
}
// 页面可见性变化时处理
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
// 页面隐藏时停止定时器
if (stockTimer) {
clearInterval(stockTimer);
stockTimer = null;
}
} else {
// 页面显示时重新开始
if (!stockTimer) {
startStockRefresh();
}
}
});
})();
</script>

64
public/themes/website/1/zh/demo/investment.html

@ -145,33 +145,33 @@
<div class="iv-stock__body"> <div class="iv-stock__body">
<div class="iv-stock__data"> <div class="iv-stock__data">
<p class="iv-stock__code">股票代码:{$stockInfo.code}</p> <p class="iv-stock__code">股票代码:{$stockInfo.code}</p>
<div class="iv-stock__quote">
<span class="iv-stock__price" id="stock-price" style="{$priceColor}">{$stockInfo.price}</span>
<span class="iv-stock__unit" style="{$priceColor}"></span>
<span class="iv-stock__change" id="stock-change" style="{$priceColor}">{$stockInfo.change}&nbsp;&nbsp;{$stockInfo.changePercent}</span>
<div class="iv-stock__quote" data-stock-quote data-stock-code="603933">
<span class="iv-stock__price" id="stock-price" data-stock="price" style="{$priceColor}">{$stockInfo.price}</span>
<span class="iv-stock__unit" data-stock="unit" style="{$priceColor}"></span>
<span class="iv-stock__change" id="stock-change" data-stock="change" style="{$priceColor}">{$stockInfo.change}&nbsp;&nbsp;{$stockInfo.changePercent}</span>
</div> </div>
<ul class="iv-stock__metrics"> <ul class="iv-stock__metrics">
<li> <li>
<span class="iv-stock__metric-label">最高(元)</span> <span class="iv-stock__metric-label">最高(元)</span>
<strong class="iv-stock__metric-value" id="stock-high">{$stockInfo.high}</strong>
<strong class="iv-stock__metric-value" id="stock-high" data-stock="high">{$stockInfo.high}</strong>
</li> </li>
<li> <li>
<span class="iv-stock__metric-label">最低(元)</span> <span class="iv-stock__metric-label">最低(元)</span>
<strong class="iv-stock__metric-value" id="stock-low">{$stockInfo.low}</strong>
<strong class="iv-stock__metric-value" id="stock-low" data-stock="low">{$stockInfo.low}</strong>
</li> </li>
<li> <li>
<span class="iv-stock__metric-label">成交量(万手)</span> <span class="iv-stock__metric-label">成交量(万手)</span>
<strong class="iv-stock__metric-value" id="stock-volume">{$stockInfo.volume}</strong>
<strong class="iv-stock__metric-value" id="stock-volume" data-stock="volume">{$stockInfo.volume}</strong>
</li> </li>
<li> <li>
<span class="iv-stock__metric-label">成交额(万元)</span> <span class="iv-stock__metric-label">成交额(万元)</span>
<strong class="iv-stock__metric-value" id="stock-amount">{$stockInfo.amount}</strong>
<strong class="iv-stock__metric-value" id="stock-amount" data-stock="amount">{$stockInfo.amount}</strong>
</li> </li>
</ul> </ul>
<p class="iv-stock__note" id="stock-time">截止 {$stockInfo.updateTime}*报价有十五分钟或以上延迟。</p>
<p class="iv-stock__note" id="stock-time" data-stock="time">截止 {$stockInfo.updateTime}*报价有十五分钟或以上延迟。</p>
</div> </div>
<div class="iv-stock__chart"> <div class="iv-stock__chart">
<img id="stock-chart" src="https://image.sinajs.cn/newchart/min/n/sh603933.gif" alt="睿能科技分时走势图" width="720" height="450" />
<img id="stock-chart" data-stock="chart" src="https://image.sinajs.cn/newchart/min/n/sh603933.gif" alt="睿能科技分时走势图" width="720" height="450" />
</div> </div>
</div> </div>
</section> </section>
@ -274,50 +274,6 @@
<script src="https://cdn.jsdelivr.net.cn/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net.cn/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.min.js"></script>
<script src="https://unpkg.com/counterup2@2.0.2/dist/index.js"></script> <script src="https://unpkg.com/counterup2@2.0.2/dist/index.js"></script>
<script>
(function() {
// 股票图表定时刷新(只刷新图片,不刷新整个页面)
var refreshInterval = 60000; // 1分钟
var stockTimer = null;
var chartImg = document.getElementById('stock-chart');
// 刷新图片
function refreshChartImage() {
if (chartImg) {
var timestamp = new Date().getTime();
chartImg.src = 'https://image.sinajs.cn/newchart/min/n/sh603933.gif?t=' + timestamp;
}
}
// 开始定时刷新
function startChartRefresh() {
stockTimer = setInterval(refreshChartImage, refreshInterval);
}
// 页面加载完成后开始
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startChartRefresh);
} else {
startChartRefresh();
}
// 页面可见性变化时处理
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
if (stockTimer) {
clearInterval(stockTimer);
stockTimer = null;
}
} else {
if (!stockTimer) {
refreshChartImage(); // 立即刷新一次
startChartRefresh();
}
}
});
})();
</script>
</body> </body>
</html> </html>
Loading…
Cancel
Save