|
@@ -40,7 +40,6 @@
|
|
|
|
|
|
|
|
<!-- 2. 周期切换 Tab -->
|
|
<!-- 2. 周期切换 Tab -->
|
|
|
<nav class="time-tabs">
|
|
<nav class="time-tabs">
|
|
|
- <!-- 常用周期 -->
|
|
|
|
|
<div
|
|
<div
|
|
|
v-for="tab in visibleTabs"
|
|
v-for="tab in visibleTabs"
|
|
|
:key="tab"
|
|
:key="tab"
|
|
@@ -50,18 +49,13 @@
|
|
|
{{ getTabLabel(tab) }}
|
|
{{ getTabLabel(tab) }}
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <!-- 更多按钮 (下拉菜单) -->
|
|
|
|
|
- <div class="tab-item icon-btn" @click.stop="toggleMore">
|
|
|
|
|
- <span :class="{ 'active-text': isMoreActive }">更多</span>
|
|
|
|
|
- <span class="triangle">◢</span>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 下拉菜单 -->
|
|
|
|
|
|
|
+ <div class="tab-item icon-btn" @click.stop="toggleMore" :style="isMoreActive ? { backgroundColor: '#F6465D', color: '#fff' } : {}">
|
|
|
|
|
+ <span :class="{ 'active-text': isMoreActive }">{{ isMoreActive ? getTabLabel(currentTab) : '更多' }}</span>
|
|
|
|
|
+ <span class="triangle" :style="isMoreActive ? { color: '#fff' } : {}">◢</span>
|
|
|
<div class="dropdown-menu" v-show="showMoreMenu">
|
|
<div class="dropdown-menu" v-show="showMoreMenu">
|
|
|
<div
|
|
<div
|
|
|
- v-for="mt in moreTabs"
|
|
|
|
|
- :key="mt"
|
|
|
|
|
- class="drop-item"
|
|
|
|
|
- :class="{ active: currentTab === mt }"
|
|
|
|
|
|
|
+ v-for="mt in moreTabs" :key="mt"
|
|
|
|
|
+ class="drop-item" :class="{ active: currentTab === mt }"
|
|
|
@click.stop="switchPeriod(mt)">
|
|
@click.stop="switchPeriod(mt)">
|
|
|
{{ getTabLabel(mt) }}
|
|
{{ getTabLabel(mt) }}
|
|
|
</div>
|
|
</div>
|
|
@@ -70,29 +64,36 @@
|
|
|
|
|
|
|
|
<!-- 指标按钮 -->
|
|
<!-- 指标按钮 -->
|
|
|
<div class="tab-item icon-btn" @click.stop="toggleIndicators">
|
|
<div class="tab-item icon-btn" @click.stop="toggleIndicators">
|
|
|
|
|
+ <!-- 保持你的图标路径 -->
|
|
|
<img src="@/assets/icon/bitcoin/lishidingdan.svg" alt="" />
|
|
<img src="@/assets/icon/bitcoin/lishidingdan.svg" alt="" />
|
|
|
</div>
|
|
</div>
|
|
|
</nav>
|
|
</nav>
|
|
|
|
|
|
|
|
- <!-- 3. 指标设置面板 (点击指标图标弹出) -->
|
|
|
|
|
|
|
+ <!-- 3. 指标设置面板 -->
|
|
|
<div class="indicator-panel" v-show="showIndicatorMenu" @click.stop>
|
|
<div class="indicator-panel" v-show="showIndicatorMenu" @click.stop>
|
|
|
|
|
+ <!-- 主图设置 (单选) -->
|
|
|
<div class="panel-section">
|
|
<div class="panel-section">
|
|
|
- <div class="section-title">主图</div>
|
|
|
|
|
|
|
+ <div class="section-title">主图指标</div>
|
|
|
<div class="btn-group">
|
|
<div class="btn-group">
|
|
|
- <div class="idx-btn" :class="{ active: mainIdx === 'MA' }" @click="changeMain('MA')">MA</div>
|
|
|
|
|
- <div class="idx-btn" :class="{ active: mainIdx === 'BOLL' }" @click="changeMain('BOLL')">BOLL</div>
|
|
|
|
|
|
|
+ <div v-for="m in ['MA', 'EMA', 'BOLL', 'SAR']" :key="m"
|
|
|
|
|
+ class="idx-btn" :class="{ active: mainIdx === m }"
|
|
|
|
|
+ @click="changeMain(m)">
|
|
|
|
|
+ {{ m }}
|
|
|
|
|
+ </div>
|
|
|
<div class="idx-btn" :class="{ active: mainIdx === 'Hide' }" @click="changeMain('Hide')">隐藏</div>
|
|
<div class="idx-btn" :class="{ active: mainIdx === 'Hide' }" @click="changeMain('Hide')">隐藏</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 副图设置 (多选) -->
|
|
|
<div class="panel-section">
|
|
<div class="panel-section">
|
|
|
- <div class="section-title">副图</div>
|
|
|
|
|
|
|
+ <div class="section-title">副图指标 (可多选)</div>
|
|
|
<div class="btn-group">
|
|
<div class="btn-group">
|
|
|
- <div class="idx-btn" :class="{ active: subIdx === 'VOL' }" @click="changeSub('VOL')">VOL</div>
|
|
|
|
|
- <div class="idx-btn" :class="{ active: subIdx === 'MACD' }" @click="changeSub('MACD')">MACD</div>
|
|
|
|
|
- <div class="idx-btn" :class="{ active: subIdx === 'KDJ' }" @click="changeSub('KDJ')">KDJ</div>
|
|
|
|
|
- <div class="idx-btn" :class="{ active: subIdx === 'RSI' }" @click="changeSub('RSI')">RSI</div>
|
|
|
|
|
- <div class="idx-btn" :class="{ active: subIdx === 'WR' }" @click="changeSub('WR')">WR</div>
|
|
|
|
|
- <div class="idx-btn" :class="{ active: subIdx === 'Hide' }" @click="changeSub('Hide')">隐藏</div>
|
|
|
|
|
|
|
+ <div v-for="s in ['VOL', 'MACD', 'KDJ', 'RSI', 'WR', 'CCI', 'OBV']" :key="s"
|
|
|
|
|
+ class="idx-btn" :class="{ active: subIdx.includes(s) }"
|
|
|
|
|
+ @click="toggleSub(s)">
|
|
|
|
|
+ {{ s }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="idx-btn" :class="{ active: subIdx.length === 0 }" @click="clearSub">隐藏</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -102,11 +103,11 @@
|
|
|
<KlineChart
|
|
<KlineChart
|
|
|
ref="klineRef"
|
|
ref="klineRef"
|
|
|
:data="kLineData"
|
|
:data="kLineData"
|
|
|
- height="100%"
|
|
|
|
|
|
|
+ height="70vh"
|
|
|
:precision="{ price: getPricePrecision(marketInfo.price), volume: 2 }" />
|
|
:precision="{ price: getPricePrecision(marketInfo.price), volume: 2 }" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <!-- 5. 底部挂单/成交 -->
|
|
|
|
|
|
|
+ <!-- 5. 底部 -->
|
|
|
<div class="notifi-classifi">
|
|
<div class="notifi-classifi">
|
|
|
<div class="pf600 fs14" :class="current === 'entrustingOrder' ? 'fc121212' : 'fcA8A8A8'" @click="messageChange('entrustingOrder')">委托挂单</div>
|
|
<div class="pf600 fs14" :class="current === 'entrustingOrder' ? 'fc121212' : 'fcA8A8A8'" @click="messageChange('entrustingOrder')">委托挂单</div>
|
|
|
<div class="sys-notifi pf600 fs14" :class="current === 'latestTransactions' ? 'fc121212' : 'fcA8A8A8'" @click="messageChange('latestTransactions')">最新成交</div>
|
|
<div class="sys-notifi pf600 fs14" :class="current === 'latestTransactions' ? 'fc121212' : 'fcA8A8A8'" @click="messageChange('latestTransactions')">最新成交</div>
|
|
@@ -117,7 +118,7 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
|
|
|
|
|
|
|
+import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from "vue";
|
|
|
import { useRoute } from "vue-router";
|
|
import { useRoute } from "vue-router";
|
|
|
import { GetCandlestickChart } from "@/api/index.js";
|
|
import { GetCandlestickChart } from "@/api/index.js";
|
|
|
import EntrustingOrder from "./EntrustingOrder.vue";
|
|
import EntrustingOrder from "./EntrustingOrder.vue";
|
|
@@ -127,42 +128,58 @@ import KlineChart from "@/views/bitcoin/lever/components/KLineChart.vue";
|
|
|
const route = useRoute();
|
|
const route = useRoute();
|
|
|
const symbolId = computed(() => route.query.id || "6");
|
|
const symbolId = computed(() => route.query.id || "6");
|
|
|
|
|
|
|
|
-// --- 周期设置 ---
|
|
|
|
|
|
|
+// 周期
|
|
|
const currentTab = ref("1d");
|
|
const currentTab = ref("1d");
|
|
|
-// 外面显示的周期
|
|
|
|
|
const visibleTabs = ["15m", "1h", "4h", "1d"];
|
|
const visibleTabs = ["15m", "1h", "4h", "1d"];
|
|
|
-// 下拉菜单里的周期
|
|
|
|
|
const moreTabs = ["1m", "5m", "30m", "1w", "1M"];
|
|
const moreTabs = ["1m", "5m", "30m", "1w", "1M"];
|
|
|
const isMoreActive = computed(() => moreTabs.includes(currentTab.value));
|
|
const isMoreActive = computed(() => moreTabs.includes(currentTab.value));
|
|
|
|
|
+const getTabLabel = (t) => ({ '1m':'1分', '5m':'5分', '15m':'15分', '30m':'30分', '1h':'1小时', '4h':'4小时', '1d':'日线', '1w':'周线', '1M':'月线' }[t] || t);
|
|
|
|
|
|
|
|
-const getTabLabel = (t) => {
|
|
|
|
|
- const map = { '1m':'1分', '5m':'5分', '15m':'15分', '30m':'30分', '1h':'1小时', '4h':'4小时', '1d':'日线', '1w':'周线', '1M':'月线' };
|
|
|
|
|
- return map[t] || t;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-// --- UI 状态 ---
|
|
|
|
|
|
|
+// --- 指标管理 (升级版) ---
|
|
|
const showMoreMenu = ref(false);
|
|
const showMoreMenu = ref(false);
|
|
|
const showIndicatorMenu = ref(false);
|
|
const showIndicatorMenu = ref(false);
|
|
|
|
|
+const klineRef = ref(null);
|
|
|
|
|
+
|
|
|
|
|
+// 主图状态 (单选)
|
|
|
const mainIdx = ref('MA');
|
|
const mainIdx = ref('MA');
|
|
|
-const subIdx = ref('VOL');
|
|
|
|
|
-const klineRef = ref(null); // 引用子组件
|
|
|
|
|
|
|
|
|
|
-// --- 交互方法 ---
|
|
|
|
|
-const toggleMore = () => {
|
|
|
|
|
- showIndicatorMenu.value = false;
|
|
|
|
|
- showMoreMenu.value = !showMoreMenu.value;
|
|
|
|
|
|
|
+// 副图状态 (多选,默认选两个)
|
|
|
|
|
+const subIdx = ref(['VOL', 'MACD']);
|
|
|
|
|
+
|
|
|
|
|
+// 切换主图
|
|
|
|
|
+const changeMain = (name) => {
|
|
|
|
|
+ mainIdx.value = name;
|
|
|
|
|
+ if (klineRef.value) klineRef.value.setMainIndicator(name);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const toggleIndicators = () => {
|
|
|
|
|
- showMoreMenu.value = false;
|
|
|
|
|
- showIndicatorMenu.value = !showIndicatorMenu.value;
|
|
|
|
|
|
|
+// 切换副图 (多选逻辑)
|
|
|
|
|
+const toggleSub = (name) => {
|
|
|
|
|
+ const index = subIdx.value.indexOf(name);
|
|
|
|
|
+ if (index > -1) {
|
|
|
|
|
+ // 已经选中 -> 取消选中
|
|
|
|
|
+ subIdx.value.splice(index, 1);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 未选中 -> 添加选中 (限制最多3个防止太挤,或者不做限制)
|
|
|
|
|
+ if (subIdx.value.length >= 3) {
|
|
|
|
|
+ subIdx.value.shift(); // 移除最早选的一个,保持最多3个
|
|
|
|
|
+ }
|
|
|
|
|
+ subIdx.value.push(name);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 更新图表
|
|
|
|
|
+ if (klineRef.value) klineRef.value.setSubIndicators(subIdx.value);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const closePopups = () => {
|
|
|
|
|
- showMoreMenu.value = false;
|
|
|
|
|
- showIndicatorMenu.value = false;
|
|
|
|
|
|
|
+// 隐藏所有副图
|
|
|
|
|
+const clearSub = () => {
|
|
|
|
|
+ subIdx.value = [];
|
|
|
|
|
+ if (klineRef.value) klineRef.value.setSubIndicators([]);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// --- 交互 ---
|
|
|
|
|
+const toggleMore = () => { showIndicatorMenu.value = false; showMoreMenu.value = !showMoreMenu.value; };
|
|
|
|
|
+const toggleIndicators = () => { showMoreMenu.value = false; showIndicatorMenu.value = !showIndicatorMenu.value; };
|
|
|
|
|
+const closePopups = () => { showMoreMenu.value = false; showIndicatorMenu.value = false; };
|
|
|
|
|
+
|
|
|
const switchPeriod = (period) => {
|
|
const switchPeriod = (period) => {
|
|
|
if (currentTab.value === period) return;
|
|
if (currentTab.value === period) return;
|
|
|
currentTab.value = period;
|
|
currentTab.value = period;
|
|
@@ -171,25 +188,13 @@ const switchPeriod = (period) => {
|
|
|
getKlineData();
|
|
getKlineData();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const changeMain = (name) => {
|
|
|
|
|
- mainIdx.value = name;
|
|
|
|
|
- if (klineRef.value) klineRef.value.setMainIndicator(name);
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const changeSub = (name) => {
|
|
|
|
|
- subIdx.value = name;
|
|
|
|
|
- if (klineRef.value) klineRef.value.setSubIndicator(name);
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-// --- 下面是 WebSocket 和数据逻辑 (保持你原有的逻辑) ---
|
|
|
|
|
|
|
+// --- 数据/WS (保持不变) ---
|
|
|
const kLineData = ref([]);
|
|
const kLineData = ref([]);
|
|
|
const socket = ref(null);
|
|
const socket = ref(null);
|
|
|
const WS_BASE_URL = "ws://backend.66linknow.com/ws/kline/";
|
|
const WS_BASE_URL = "ws://backend.66linknow.com/ws/kline/";
|
|
|
const HEARTBEAT_INTERVAL = 15000;
|
|
const HEARTBEAT_INTERVAL = 15000;
|
|
|
const RECONNECT_DELAY = 3000;
|
|
const RECONNECT_DELAY = 3000;
|
|
|
-let heartbeatTimer = null;
|
|
|
|
|
-let reconnectTimer = null;
|
|
|
|
|
-let isUnmounted = false;
|
|
|
|
|
|
|
+let heartbeatTimer = null, reconnectTimer = null, isUnmounted = false;
|
|
|
|
|
|
|
|
const marketInfo = ref({ price: "0.00", fiatPrice: "0.00", change: 0.0, high: "0.00", low: "0.00", vol: "0", amount: "0" });
|
|
const marketInfo = ref({ price: "0.00", fiatPrice: "0.00", change: 0.0, high: "0.00", low: "0.00", vol: "0", amount: "0" });
|
|
|
const orderPlacement = ref();
|
|
const orderPlacement = ref();
|
|
@@ -213,12 +218,9 @@ const getKlineData = async () => {
|
|
|
|
|
|
|
|
if (marketInfo.value.price === "0.00") {
|
|
if (marketInfo.value.price === "0.00") {
|
|
|
const lastBar = formattedData[formattedData.length - 1];
|
|
const lastBar = formattedData[formattedData.length - 1];
|
|
|
- let totalVol = 0;
|
|
|
|
|
- formattedData.forEach(i => totalVol += i.volume);
|
|
|
|
|
- marketInfo.value = {
|
|
|
|
|
- price: lastBar.close, fiatPrice: lastBar.close, change: 0,
|
|
|
|
|
- high: lastBar.high, low: lastBar.low, vol: totalVol, amount: totalVol * lastBar.close
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ let totalVol = 0; formattedData.forEach(i => totalVol += i.volume);
|
|
|
|
|
+ const changeRate = formattedData[0].open ? ((lastBar.close - formattedData[0].open) / formattedData[0].open) * 100 : 0;
|
|
|
|
|
+ marketInfo.value = { price: lastBar.close, fiatPrice: lastBar.close, change: changeRate.toFixed(2), high: lastBar.high, low: lastBar.low, vol: totalVol, amount: totalVol * lastBar.close };
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
} catch (error) { console.error("API Error", error); }
|
|
} catch (error) { console.error("API Error", error); }
|
|
@@ -230,15 +232,9 @@ const connectWebSocket = () => {
|
|
|
const url = `${WS_BASE_URL}?symbol=${symbolStr}`;
|
|
const url = `${WS_BASE_URL}?symbol=${symbolStr}`;
|
|
|
try {
|
|
try {
|
|
|
socket.value = new WebSocket(url);
|
|
socket.value = new WebSocket(url);
|
|
|
- socket.value.onopen = () => { startHeartbeat(); };
|
|
|
|
|
- socket.value.onmessage = (event) => { handleSocketMessage(event.data); };
|
|
|
|
|
- socket.value.onclose = () => {
|
|
|
|
|
- stopHeartbeat();
|
|
|
|
|
- if (!isUnmounted) {
|
|
|
|
|
- clearTimeout(reconnectTimer);
|
|
|
|
|
- reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY);
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ socket.value.onopen = () => startHeartbeat();
|
|
|
|
|
+ socket.value.onmessage = (event) => handleSocketMessage(event.data);
|
|
|
|
|
+ socket.value.onclose = () => { stopHeartbeat(); if (!isUnmounted) { clearTimeout(reconnectTimer); reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY); } };
|
|
|
} catch (e) { if (!isUnmounted) reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY); }
|
|
} catch (e) { if (!isUnmounted) reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY); }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -251,7 +247,6 @@ const handleSocketMessage = (msgStr) => {
|
|
|
if (msgStr === "pong") return;
|
|
if (msgStr === "pong") return;
|
|
|
const rawData = JSON.parse(msgStr);
|
|
const rawData = JSON.parse(msgStr);
|
|
|
const msg = rawData.data || rawData;
|
|
const msg = rawData.data || rawData;
|
|
|
-
|
|
|
|
|
if (msg.e === "kline") {
|
|
if (msg.e === "kline") {
|
|
|
const k = msg.k;
|
|
const k = msg.k;
|
|
|
if (k.i !== currentTab.value) return;
|
|
if (k.i !== currentTab.value) return;
|
|
@@ -269,7 +264,7 @@ const handleSocketMessage = (msgStr) => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const updateKlineData = (newBar) => {
|
|
const updateKlineData = (newBar) => {
|
|
|
- if (!kLineData.value?.length) return;
|
|
|
|
|
|
|
+ if (!kLineData.value?.length) { kLineData.value = [newBar]; return; }
|
|
|
const lastIndex = kLineData.value.length - 1;
|
|
const lastIndex = kLineData.value.length - 1;
|
|
|
const lastBar = kLineData.value[lastIndex];
|
|
const lastBar = kLineData.value[lastIndex];
|
|
|
if (newBar.timestamp === lastBar.timestamp) kLineData.value.splice(lastIndex, 1, newBar);
|
|
if (newBar.timestamp === lastBar.timestamp) kLineData.value.splice(lastIndex, 1, newBar);
|
|
@@ -277,7 +272,6 @@ const updateKlineData = (newBar) => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleVisibilityChange = () => { if (document.visibilityState === "visible" && socket.value?.readyState !== 1) connectWebSocket(); };
|
|
const handleVisibilityChange = () => { if (document.visibilityState === "visible" && socket.value?.readyState !== 1) connectWebSocket(); };
|
|
|
-
|
|
|
|
|
onMounted(() => { isUnmounted = false; getKlineData(); connectWebSocket(); document.addEventListener("visibilitychange", handleVisibilityChange); });
|
|
onMounted(() => { isUnmounted = false; getKlineData(); connectWebSocket(); document.addEventListener("visibilitychange", handleVisibilityChange); });
|
|
|
onBeforeUnmount(() => { isUnmounted = true; closeWebSocket(); document.removeEventListener("visibilitychange", handleVisibilityChange); });
|
|
onBeforeUnmount(() => { isUnmounted = true; closeWebSocket(); document.removeEventListener("visibilitychange", handleVisibilityChange); });
|
|
|
watch(symbolId, () => { kLineData.value = []; getKlineData(); connectWebSocket(); });
|
|
watch(symbolId, () => { kLineData.value = []; getKlineData(); connectWebSocket(); });
|
|
@@ -294,100 +288,49 @@ const getUpDownClass = (c) => (c >= 0 ? "fc45B26B" : "fcF6465D");
|
|
|
.fcF6465D { color: #f6465d !important; }
|
|
.fcF6465D { color: #f6465d !important; }
|
|
|
.fc1F2937 { color: #1f2937; }
|
|
.fc1F2937 { color: #1f2937; }
|
|
|
|
|
|
|
|
-/* 周期切换栏 */
|
|
|
|
|
.time-tabs {
|
|
.time-tabs {
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
- padding: 10px 15px 0 15px;
|
|
|
|
|
- position: relative;
|
|
|
|
|
- z-index: 20;
|
|
|
|
|
|
|
+ display: flex; align-items: center; justify-content: space-between;
|
|
|
|
|
+ width: 100%; box-sizing: border-box; padding: 0 15px 0 15px; position: relative; z-index: 20;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
.tab-item {
|
|
.tab-item {
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: #929aa5;
|
|
|
|
|
- padding: 4px 8px;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- position: relative;
|
|
|
|
|
|
|
+ font-size: 14px; color: #929aa5; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-weight: 500; display: flex; align-items: center; position: relative;
|
|
|
}
|
|
}
|
|
|
.tab-item.icon-btn { padding: 4px 4px; }
|
|
.tab-item.icon-btn { padding: 4px 4px; }
|
|
|
-.active-text { color: #1F2937; font-weight: 600; }
|
|
|
|
|
|
|
+.active-text { color: #fff; font-weight: 600; }
|
|
|
.triangle { font-size: 8px; margin-left: 2px; transform: scale(0.9); }
|
|
.triangle { font-size: 8px; margin-left: 2px; transform: scale(0.9); }
|
|
|
.tab-item img { display: block; height: 16px; width: auto; }
|
|
.tab-item img { display: block; height: 16px; width: auto; }
|
|
|
|
|
|
|
|
-/* 更多周期下拉菜单 */
|
|
|
|
|
.dropdown-menu {
|
|
.dropdown-menu {
|
|
|
- position: absolute;
|
|
|
|
|
- top: 30px;
|
|
|
|
|
- left: -20px;
|
|
|
|
|
- width: 80px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- padding: 5px 0;
|
|
|
|
|
- z-index: 100;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
-}
|
|
|
|
|
-.drop-item {
|
|
|
|
|
- padding: 8px 15px;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
- color: #666;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
|
|
+ position: absolute; top: 30px; left: -20px; width: 80px; background: #fff;
|
|
|
|
|
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15); border-radius: 6px; padding: 5px 0;
|
|
|
|
|
+ z-index: 100; display: flex; flex-direction: column;
|
|
|
}
|
|
}
|
|
|
|
|
+.drop-item { padding: 8px 15px; font-size: 13px; color: #666; text-align: center; }
|
|
|
.drop-item.active { color: #F6465D; background: #fff5f5; }
|
|
.drop-item.active { color: #F6465D; background: #fff5f5; }
|
|
|
|
|
|
|
|
-/* 指标面板 */
|
|
|
|
|
.indicator-panel {
|
|
.indicator-panel {
|
|
|
- position: absolute;
|
|
|
|
|
- top: 155px; /* 调整此值以对齐 K 线图顶部 */
|
|
|
|
|
- left: 15px;
|
|
|
|
|
- right: 15px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- padding: 15px;
|
|
|
|
|
- z-index: 99;
|
|
|
|
|
|
|
+ position: absolute; top: 155px; left: 15px; right: 15px; background: #fff;
|
|
|
|
|
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1); border-radius: 8px; padding: 15px; z-index: 99;
|
|
|
}
|
|
}
|
|
|
.panel-section { margin-bottom: 15px; }
|
|
.panel-section { margin-bottom: 15px; }
|
|
|
.section-title { font-size: 12px; color: #999; margin-bottom: 8px; }
|
|
.section-title { font-size: 12px; color: #999; margin-bottom: 8px; }
|
|
|
.btn-group { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
.btn-group { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
|
-.idx-btn {
|
|
|
|
|
- padding: 4px 12px; border: 1px solid #eee; border-radius: 14px; font-size: 12px; color: #666; cursor: pointer;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+.idx-btn { padding: 4px 12px; border: 1px solid #eee; border-radius: 14px; font-size: 12px; color: #666; cursor: pointer; }
|
|
|
.idx-btn.active { border-color: #F6465D; color: #F6465D; background-color: #fff5f5; }
|
|
.idx-btn.active { border-color: #F6465D; color: #F6465D; background-color: #fff5f5; }
|
|
|
|
|
|
|
|
-/* 容器布局 */
|
|
|
|
|
-.market-conditions {
|
|
|
|
|
- display: flex; flex-direction: column; align-items: center; margin-bottom: 50px; width: 100%; position: relative;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+.market-conditions { display: flex; flex-direction: column; align-items: center; margin-bottom: 50px; width: 100%; position: relative; }
|
|
|
.market-price {
|
|
.market-price {
|
|
|
- display: flex; justify-content: space-between; margin-top: 8px; width: 100%; height: 73px; padding: 0 15px; box-sizing: border-box;
|
|
|
|
|
- .price-left {
|
|
|
|
|
- display: flex; flex-direction: column; width: 144px; height: 69px;
|
|
|
|
|
|
|
+ display: flex; justify-content: space-between; margin-top: 8px;
|
|
|
|
|
+ width: 100%; padding: 0 15px; box-sizing: border-box;
|
|
|
|
|
+ .price-left { display: flex; flex-direction: column; width: 144px; line-height: 20px;
|
|
|
|
|
+ height: 69px;
|
|
|
.left-price { display: flex; align-items: center; height: 18px; }
|
|
.left-price { display: flex; align-items: center; height: 18px; }
|
|
|
.left-number { margin-top: 5px; }
|
|
.left-number { margin-top: 5px; }
|
|
|
- .left-appro { display: flex; align-items: center; margin-top: 3px; .appro { margin-left: 9px; } }
|
|
|
|
|
- }
|
|
|
|
|
- .price-right {
|
|
|
|
|
- display: flex; flex-direction: column; height: 100%;
|
|
|
|
|
- .right-number-top, .right-number-bottom {
|
|
|
|
|
- display: flex; justify-content: flex-end; width: 100%; height: 32px;
|
|
|
|
|
- .right-number-top-price, .right-number-top-number { margin-left: 10px; text-align: right; div { height: 16px; line-height: 16px; text-align: end; } }
|
|
|
|
|
- }
|
|
|
|
|
- .right-number-bottom { margin-top: 9px; }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-.k-line-main { height: 50vh; min-height: 350px; width: 100%; padding: 0 15px; margin-top: 10px; }
|
|
|
|
|
-.notifi-classifi {
|
|
|
|
|
- display: flex; align-items: flex-end; margin-top: 15px; width: 100%; padding: 0 15px; box-sizing: border-box; height: 24px; border-bottom: 1px solid #f5f5f5; padding-bottom: 5px;
|
|
|
|
|
- .sys-notifi { margin-left: 47px; }
|
|
|
|
|
|
|
+ .left-appro { display: flex; align-items: center;margin-top: 3px;
|
|
|
|
|
+ .appro { margin-left: 9px; } } }
|
|
|
|
|
+ .price-right { display: flex; flex-direction: column; height: 100%; line-height: 20px;
|
|
|
|
|
+ .right-number-top, .right-number-bottom { display: flex; justify-content: flex-end; width: 100%; height: 32px; .right-number-top-price, .right-number-top-number { margin-left: 10px; text-align: right; div { height: 16px; line-height: 16px; text-align: end; } } } .right-number-bottom { } }
|
|
|
}
|
|
}
|
|
|
|
|
+.k-line-main { height: 50vh; min-height: 350px; width: 100%; padding: 0 15px; }
|
|
|
|
|
+.notifi-classifi { display: flex; align-items: flex-end; margin-top: 15px; width: 100%; padding: 0 15px; box-sizing: border-box; height: 24px; border-bottom: 1px solid #f5f5f5; padding-bottom: 5px; .sys-notifi { margin-left: 47px; } }
|
|
|
</style>
|
|
</style>
|