11 files modified
1 files deleted
| | |
| | | <meta name="theme-color" content="#131A2E"> |
| | | <meta name="apple-mobile-web-app-status-bar-style" content="#131A2E" /> |
| | | <link rel="apple-touch-icon" href="./favicon.ico"> |
| | | <title>Crypto</title> |
| | | <title>Bitget</title> |
| | | </head> |
| | | |
| | | <body> |
| | |
| | | "scripts": { |
| | | "lint": "eslint --ext .vue,.js ./", |
| | | "lint:fix": "eslint --fix --ext .vue,.js ./", |
| | | "dev": "vite", |
| | | "build": "vite build", |
| | | "build:visualizer": "NODE_ENV=production VISUALIZER=show vite build && node ./fixDistPath.js", |
| | | "preview": "vite preview" |
| | | "dev": "node ./node_modules/vite/bin/vite.js", |
| | | "build": "node ./node_modules/vite/bin/vite.js build", |
| | | "build:visualizer": "set NODE_ENV=production&& set VISUALIZER=show&& node ./node_modules/vite/bin/vite.js build && node ./fixDistPath.js", |
| | | "preview": "node ./node_modules/vite/bin/vite.js preview" |
| | | }, |
| | | "dependencies": { |
| | | "@vueuse/components": "9.4.0", |
| | |
| | | </template> |
| | | </van-tabbar-item> |
| | | |
| | | <!-- <van-tabbar-item name="fund" to="/cryptos/fund"> |
| | | <van-tabbar-item name="fund" to="/cryptos/fund"> |
| | | <template #icon> |
| | | <img :src="active == 'fund' ? icon.fund.active : icon.fund.inactive" alt="fund" /> |
| | | </template> |
| | | </van-tabbar-item> --> |
| | | </van-tabbar-item> |
| | | |
| | | <van-tabbar-item name="personal" to="/personal"> |
| | | <template #icon> |
| | |
| | | |
| | | // const ENV_DEV = '127.0.0.1' // dev |
| | | // const ENV_DEV = '192.168.10.11:8848' // dev |
| | | const ENV_DEV = 'api.dpcex.com' // dev |
| | | const ENV_DEV = 'zhapi.bitget-jp-us.cyou' // dev |
| | | |
| | | // const ENV_PRO = window.location.hostname // 接口域名跟随 H5 |
| | | // const ENV_PRO = "127.0.0.1" |
| | | // const ENV_PRO = "192.168.10.11:8848" |
| | | const ENV_PRO = "api.dpcex.com" |
| | | const ENV_PRO = "zhapi.bitget-jp-us.cyou" |
| | | // 避免打包出错务必把 app域名的注释要放在在本地ENV_PRO的下面 |
| | | // const ENV_PRO = 'foilwm.com' // app域名 |
| | | |
| | |
| | | } |
| | | export const BASE_URL = base_url |
| | | export const WS_URL = ws_url |
| | | export const IMG_PATH = 'https://img.dpcex.com' |
| | | export const IMG_PATH = 'https://zhimg.bitget-jp-us.cyou' |
| | | export const HOST_URL = host_url |
| | | |
| | | export default { |
| | |
| | | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' |
| | | pinia.use(piniaPluginPersistedstate) |
| | | |
| | | // 生产环境且为 PC 端时跳转到 PC 站 |
| | | const PC_SITE_URL = 'https://pc.dpcex.com' |
| | | function isPc() { |
| | | if (typeof navigator === 'undefined') return false |
| | | const ua = navigator.userAgent || '' |
| | | const mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile/i.test(ua) |
| | | return !mobile |
| | | } |
| | | if (import.meta.env.PROD && isPc()) { |
| | | window.location.href = PC_SITE_URL |
| | | } else { |
| | | const app = createApp(App) |
| | | const title = import.meta.env.VITE_APP__TITLE |
| | | app.config.globalProperties.$title = title |
| | |
| | | app.use(store) |
| | | |
| | | app.mount('#app') |
| | | } |
| | |
| | | <div class="header"> |
| | | <div class="header-left"> |
| | | <img src="@/assets/image/login_logo.png" alt="Wealthinfra" class="logo" /> |
| | | <span class="logo-text">1m</span> |
| | | <span class="logo-text">Bitget</span> |
| | | </div> |
| | | <div class="header-right"> |
| | | <div class="lang-selector" @click="$router.push('/language')"> |
| | |
| | | </div> |
| | | <span>{{ $t('withdraw') }}</span> |
| | | </div> |
| | | <div class="quick-item" @click="$router.push('/cryptos/loan')"> |
| | | <div class="quick-icon"> |
| | | <img src="@/assets/image/dk.png" alt="loan" /> |
| | | </div> |
| | | <span>{{ $t('home.loan') }}</span> |
| | | </div> |
| | | <div class="quick-item" @click="$router.push('/customerService')"> |
| | | <div class="quick-icon"> |
| | | <img src="@/assets/image/lxkf.png" alt="service" /> |
| | |
| | | |
| | | <!-- Trading Instruments --> |
| | | <div class="trading-section"> |
| | | <div class="trading-tabs trading-tabs--single"> |
| | | <div class="tab-item active"> |
| | | {{ $t('加密货币') }} |
| | | <div class="trading-tabs"> |
| | | <div class="tab-item" v-for="tab in marketTabs" :key="tab.value" |
| | | :class="{ active: activeTab === tab.value }" @click="switchTab(tab.value)"> |
| | | {{ $t(tab.label) }} |
| | | </div> |
| | | </div> |
| | | <div class="trading-pairs"> |
| | |
| | | <div class="pair-symbol"> |
| | | <img v-if="getPairIconUrl(pair)" :src="getPairIconUrl(pair)" alt="" |
| | | class="pair-symbol-icon" /> |
| | | {{ pair.symboltxt.toUpperCase() }} |
| | | {{ (pair.symboltxt || pair.symbol || '').toUpperCase() }} |
| | | </div> |
| | | <div class="pair-change" :class="pair.change >= 0 ? 'up' : 'down'"> |
| | | {{ pair.change >= 0 ? '+' : '' }}{{ pair.change.toFixed(4) }}% |
| | |
| | | if (!symbol) return |
| | | router.push({ |
| | | path: '/trade/options', |
| | | query: { symbol, activeTab: type || 'cryptos' } |
| | | query: { symbol, activeTab: type || activeTab.value } |
| | | }) |
| | | } |
| | | |
| | |
| | | }) |
| | | |
| | | const tradingPairs = ref([]) |
| | | const activeTab = ref('cryptos') |
| | | const marketTabs = [ |
| | | { label: '加密货币', value: 'cryptos' }, |
| | | { label: '股票', value: 'US-stocks' }, |
| | | { label: 'ETF', value: 'indices' }, |
| | | { label: '外汇', value: 'forex' }, |
| | | ] |
| | | // 新闻列表,用于横向滚动轮播(与 news/index.vue 同源:_getUsHeadNews) |
| | | const newsList = ref([]) |
| | | |
| | |
| | | |
| | | function getPairIconUrl(pair) { |
| | | if (!pair) return '' |
| | | if (activeTab.value === 'US-stocks' || activeTab.value === 'indices') return '' |
| | | return pair.iconImg ? `${IMG_PATH}/symbol/${pair.iconImg}.png` : '' |
| | | } |
| | | |
| | | // 获取交易数据(仅数字货币) |
| | | function switchTab(tab) { |
| | | if (activeTab.value === tab) return |
| | | activeTab.value = tab |
| | | fetchTradingData() |
| | | } |
| | | |
| | | // 获取交易数据 |
| | | const fetchTradingData = async () => { |
| | | const type = 'cryptos' |
| | | const type = activeTab.value |
| | | |
| | | try { |
| | | const params = { |
| | |
| | | const spread = basePrice * 0.0001 // 很小的价差 |
| | | const sellPrice = (basePrice - spread).toFixed(4) |
| | | const buyPrice = (basePrice + spread).toFixed(4) |
| | | const symboltxt = item.enName |
| | | const symboltxt = item.enName || item.name || item.symbol |
| | | const symbolStr = item.symbol || '--' |
| | | const iconImg = item.symbol_data || (symbolStr.includes('/') ? symbolStr.split('/')[0].toLowerCase() : symbolStr.replace(/USDT$/i, '').toLowerCase()) || symbolStr.toLowerCase() |
| | | return { |
| | | symboltxt: symboltxt, |
| | | symboltxt, |
| | | symbol: symbolStr, |
| | | type: type, |
| | | iconImg: iconImg, |
| | | type, |
| | | iconImg, |
| | | price: basePrice.toFixed(4), |
| | | change: changeRatio, |
| | | sellPrice: sellPrice, |
| | | buyPrice: buyPrice, |
| | | klineData: klineData |
| | | sellPrice, |
| | | buyPrice, |
| | | klineData |
| | | } |
| | | }) |
| | | } else { |
| | |
| | | <template> |
| | | <div class="quotes-market-page"> |
| | | <!-- Top Tabs --> |
| | | <div class="market-tabs market-tabs--single"> |
| | | <div class="tab-item active"> |
| | | {{ $t('加密货币') }} |
| | | <div class="market-tabs"> |
| | | <div class="tab-item" v-for="tab in marketTabs" :key="tab.value" |
| | | :class="{ active: activeTab === tab.value }" @click="switchTab(tab.value)"> |
| | | {{ $t(tab.label) }} |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | <van-list v-model:loading="marketLoading" :finished="marketFinished" :immediate-check="false" |
| | | :scroll-target="marketListRef" :finished-text="$t('没有更多了') || '没有更多了'" @load="loadMoreMarket"> |
| | | <div class="pair-item" v-for="pair in tradingPairs" :key="pair.symbol" |
| | | @click="goToOptions(pair.symbol)"> |
| | | @click="goToOptions(pair.symbol, pair.type)"> |
| | | <div class="pair-header"> |
| | | <div class="pair-symbol"> |
| | | <img v-if="getPairIconUrl(pair)" :src="getPairIconUrl(pair)" alt="" |
| | | class="pair-symbol-icon" /> |
| | | {{ pair.symboltxt.toUpperCase() }} |
| | | {{ (pair.symboltxt || pair.symbol || '').toUpperCase() }} |
| | | </div> |
| | | <div class="pair-change" :class="pair.change >= 0 ? 'up' : 'down'"> |
| | | {{ pair.change >= 0 ? '+' : '' }}{{ pair.change.toFixed(4) }}% |
| | |
| | | const useStore = useUserStore() |
| | | const quotesStore = useQuotesStore() |
| | | |
| | | const activeTab = ref('crypto') |
| | | const activeTab = ref('cryptos') |
| | | const marketTabs = [ |
| | | { label: '加密货币', value: 'cryptos' }, |
| | | { label: '股票', value: 'US-stocks' }, |
| | | { label: 'ETF', value: 'indices' }, |
| | | { label: '外汇', value: 'forex' }, |
| | | ] |
| | | const tradingPairs = ref([]) |
| | | const interval = ref(null) |
| | | const marketPage = ref(1) |
| | |
| | | function getPairIconUrl(pair) { |
| | | if (!pair) return '' |
| | | const tab = activeTab.value |
| | | if (tab === 'US-stocks' || tab === 'indices') return '' |
| | | if (tab === 'optional' && (pair.type === 'US-stocks' || pair.type === 'indices')) return '' |
| | | return pair.iconImg ? `${IMG_PATH}/symbol/${pair.iconImg}.png` : '' |
| | | } |
| | | |
| | | function switchTab(tab) { |
| | | if (activeTab.value === tab) return |
| | | activeTab.value = tab |
| | | fetchData() |
| | | } |
| | | |
| | | // 根据当前价与涨跌幅生成小型 K 线数据,与首页一致 |
| | |
| | | |
| | | // 获取交易数据;pageNo 页码,append 是否追加 |
| | | const fetchTradingData = async (pageNo = 1, append = false) => { |
| | | const type = 'cryptos' |
| | | const type = activeTab.value |
| | | |
| | | try { |
| | | const params = { |
| | |
| | | const basePrice = parseFloat(item.close || item.lastPrice || 0) |
| | | const changeRatio = item.changeRatio || 0 |
| | | const symbolStr = item.symbol || '--' |
| | | const symboltxt = item.enName |
| | | const symboltxt = item.enName || item.name || symbolStr |
| | | const iconImg = item.symbol_data || (symbolStr.includes('/') ? symbolStr.split('/')[0].toLowerCase() : symbolStr.replace(/USDT$/i, '').toLowerCase()) || symbolStr.toLowerCase() |
| | | const klineData = generateMiniKlineData(basePrice, changeRatio) |
| | | const spread = basePrice * 0.0001 |
| | |
| | | |
| | | return { |
| | | symbol: symbolStr, |
| | | symboltxt: symboltxt, |
| | | symboltxt, |
| | | price: basePrice.toFixed(4), |
| | | change: changeRatio, |
| | | sellPrice: sellPrice, |
| | | buyPrice: buyPrice, |
| | | klineData: klineData, |
| | | symbol: symbolStr, |
| | | type: type, |
| | | iconImg: iconImg, |
| | | price: basePrice.toFixed(4), |
| | | change: changeRatio, |
| | | sellPrice: sellPrice, |
| | | buyPrice: buyPrice, |
| | | klineData: klineData |
| | | sellPrice, |
| | | buyPrice, |
| | | klineData, |
| | | type, |
| | | iconImg |
| | | } |
| | | }) |
| | | if (append) { |
| | |
| | | } |
| | | |
| | | // 跳转到交易页 Options,与首页一致:/trade/options?symbol=xxx&activeTab=xxx |
| | | function goToOptions(symbol) { |
| | | function goToOptions(symbol, type) { |
| | | if (!symbol) return |
| | | router.push({ |
| | | path: '/trade/options', |
| | | query: { symbol, activeTab: 'cryptos' } |
| | | query: { symbol, activeTab: type || activeTab.value } |
| | | }) |
| | | } |
| | | |
| | |
| | | <!-- Modal Tabs:同一 activeTab 的 label 与 value 统一配置 --> |
| | | <div v-if="modalTabs.length > 1" class="modal-tabs"> |
| | | <div class="tab-item" v-for="tab in modalTabs" :key="tab.value" |
| | | :class="{ active: activeTab === tab.value }" @click="activeTab = tab.value"> |
| | | :class="{ active: activeTab === tab.value }" @click="switchTab(tab.value)"> |
| | | {{ $t(tab.label) }} |
| | | </div> |
| | | </div> |
| | |
| | | @click="selectSymbol(item)"> |
| | | <div class="symbol-left"> |
| | | <div class="symbol-info"> |
| | | <div class="symbol-name">{{ (item.symbol).toUpperCase() }}</div> |
| | | <div class="symbol-name">{{ getSymbolDisplayName(item) }}</div> |
| | | </div> |
| | | </div> |
| | | <div class="symbol-change" :class="item.change_ratio >= 0 ? 'up' : 'down'"> |
| | |
| | | const symbolListRef = ref(null) |
| | | const symbolInitialLoading = ref(false) // tab 切换时首屏请求中,避免 @load 重复请求 pageNo=1 |
| | | const SYMBOL_PAGE_SIZE = 10 |
| | | const TAB_TYPES = ['cryptos', 'US-stocks', 'indices', 'forex'] |
| | | const DEFAULT_SYMBOL = { |
| | | cryptos: 'btc', |
| | | 'US-stocks': 'AAPL', |
| | | indices: 'GlobalETF500', |
| | | forex: 'EURUSD', |
| | | } |
| | | |
| | | // 从路由 path(params) 或 query 同步 symbol(品种固定为数字货币) |
| | | // 从路由 path(params) 或 query 同步 symbol、activeTab |
| | | function applyFromRoute() { |
| | | const p = route.params || {} |
| | | const q = route.query || {} |
| | | const symbol = q.symbol ?? p.symbol |
| | | const tab = q.activeTab ?? p.activeTab |
| | | if (tab && TAB_TYPES.includes(String(tab))) { |
| | | activeTab.value = String(tab) |
| | | } |
| | | if (symbol != null && String(symbol).trim()) { |
| | | currentSymbol.value = String(symbol).trim() |
| | | } |
| | | activeTab.value = 'cryptos' |
| | | } |
| | | |
| | | const modalTabs = [ |
| | | { label: '加密货币', value: 'cryptos' }, |
| | | { label: '股票', value: 'US-stocks' }, |
| | | { label: 'ETF', value: 'indices' }, |
| | | { label: '外汇', value: 'forex' }, |
| | | ] |
| | | |
| | | // 头部显示:关联选择项的 name,无则用 symbol |
| | |
| | | |
| | | // 嵌入合约:1=永续(合约交易),2=交割(期权交易) |
| | | const embedSelectIndex = computed(() => tradeType.value === 'contract' ? 1 : 2) |
| | | // 嵌入合约品种类型(value 已与接口类型统一,仅 optional 需映射) |
| | | const embedType = computed(() => 'cryptos') |
| | | // 嵌入合约品种类型 |
| | | const embedType = computed(() => activeTab.value) |
| | | |
| | | function getSymbolDisplayName(item) { |
| | | if (!item) return '' |
| | | const name = item.name || item.enName || item.symbol || '' |
| | | if (activeTab.value === 'forex') return String(name).toUpperCase() |
| | | if (activeTab.value === 'cryptos') return String(item.symbol || name).toUpperCase() |
| | | return name |
| | | } |
| | | |
| | | function mapSymbolItem(item) { |
| | | return { |
| | | symbol: item.symbol, |
| | | name: item.enName || item.name || item.symbol, |
| | | close: item.close ?? item.lastPrice ?? '--', |
| | | change_ratio: item.changeRatio ?? item.change_ratio ?? 0, |
| | | } |
| | | } |
| | | |
| | | function switchTab(tab) { |
| | | if (activeTab.value === tab) return |
| | | activeTab.value = tab |
| | | currentSymbol.value = DEFAULT_SYMBOL[tab] || 'btc' |
| | | fetchData() |
| | | } |
| | | |
| | | // 图标路径 |
| | | const starIcon = new URL('@/assets/image/icon-star.png', import.meta.url).href |
| | |
| | | // 获取交易数据;pageNo 页码,append 是否追加 |
| | | const fetchTradingData = async (pageNo = 1, append = false) => { |
| | | try { |
| | | const type = activeTab.value |
| | | const params = { |
| | | type: 'cryptos', |
| | | type, |
| | | pageNo: pageNo, |
| | | pageSize: SYMBOL_PAGE_SIZE |
| | | } |
| | |
| | | |
| | | if (data && Array.isArray(data)) { |
| | | const symbols = data.map(item => item.symbol).join(',') |
| | | let list = [] |
| | | if (symbols) { |
| | | const homeData = await _getHomeList(symbols) |
| | | if (homeData && Array.isArray(homeData)) { |
| | | if (append) { |
| | | symbolList.value = [...symbolList.value, ...homeData] |
| | | if (homeData && Array.isArray(homeData) && homeData.length) { |
| | | list = homeData |
| | | } else { |
| | | symbolList.value = homeData |
| | | list = data.map(mapSymbolItem) |
| | | } |
| | | } else if (!append) { |
| | | symbolList.value = [] |
| | | } else { |
| | | list = data.map(mapSymbolItem) |
| | | } |
| | | } else if (!append) { |
| | | symbolList.value = [] |
| | | if (append) { |
| | | symbolList.value = [...symbolList.value, ...list] |
| | | } else { |
| | | symbolList.value = list |
| | | } |
| | | if (data.length <= 0) { |
| | | symbolFinished.value = true |
| | |
| | | const keyword = searchKeyword.value.toLowerCase() |
| | | return symbolList.value.filter(item => |
| | | item.symbol.toLowerCase().includes(keyword) || |
| | | (item.name && item.name.toLowerCase().includes(keyword)) |
| | | (item.name && item.name.toLowerCase().includes(keyword)) || |
| | | (item.enName && item.enName.toLowerCase().includes(keyword)) |
| | | ) |
| | | }) |
| | | |
| | |
| | | showSymbolModal.value = false |
| | | searchKeyword.value = '' |
| | | } |
| | | |
| | | // 查询当前交易对是否已收藏(与 add-currency / trade-head 一致) |
| | | function checkFavorite() { |
| | | if (!useStore.userInfo?.token || !currentSymbol.value) { |
| | | isFavorite.value = false |
| | |
| | | }) |
| | | |
| | | // 监听路由 query 变化(同一页带不同 query 时同步) |
| | | watch(() => route.query, () => applyFromRoute(), { deep: true }) |
| | | watch(() => route.query, () => { |
| | | applyFromRoute() |
| | | fetchData() |
| | | }, { deep: true }) |
| | | |
| | | // 组件挂载时:先从 path/query 取 symbol、activeTab,再拉数据并刷新收藏状态 |
| | | onMounted(() => { |
| | |
| | | // 关键:用正则表达式匹配所有含/api的路径(忽略大小写) |
| | | '^/api.*': { // 匹配以/api开头的任何路径(如/api、/api/xxx、/api?xx等) |
| | | // target: 'http://154.23.189.28:8086', |
| | | target:'https://api.dpcex.com', |
| | | // target:'https://api.dpcex.com', |
| | | target:'https://zhapi.bitget-jp-us.cyou', |
| | | // target:'https://zhapi.bitget-jp-us.cyou', |
| | | // target: 'https://by2.cccxxx.cc', |
| | | changeOrigin: true, |
| | | secure: false, |