diff --git a/app/controller/frontend/StockController.php b/app/controller/frontend/StockController.php
new file mode 100644
index 0000000..1b05e30
--- /dev/null
+++ b/app/controller/frontend/StockController.php
@@ -0,0 +1,35 @@
+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);
+ }
+}
diff --git a/public/themes/dist_static/static/js/common.js b/public/themes/dist_static/static/js/common.js
index 5c9dd1d..2bd5ab8 100644
--- a/public/themes/dist_static/static/js/common.js
+++ b/public/themes/dist_static/static/js/common.js
@@ -242,5 +242,89 @@ $(function() {
if ($("body").hasClass("is-mask-open")) return;
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
diff --git a/public/themes/website/1/zh/demo/components/footer.html b/public/themes/website/1/zh/demo/components/footer.html
index 9d759ec..fba9225 100644
--- a/public/themes/website/1/zh/demo/components/footer.html
+++ b/public/themes/website/1/zh/demo/components/footer.html
@@ -7,10 +7,10 @@
股票代码:{$stockInfo.code}
-截止 {$stockInfo.updateTime}*报价有十五分钟或以上延迟。
+截止 {$stockInfo.updateTime}*报价有十五分钟或以上延迟。
+