Browse Source

Merge branch 'wangyi/feature-20251022162725-启动页登录注册' of http://39.101.133.168:8807/qimaohong/deepChartVueApp into dongqian/feature-20251022181325-deepmate简版

wangyi/feature-20251022162725-启动页登录注册
wangyetao 1 month ago
parent
commit
52add52df0
  1. 9
      .hbuilderx/launch.json
  2. 198
      components/DeepMate.vue
  3. 385
      components/MarketOverview.vue
  4. 192
      components/footerBar-cn.vue
  5. 52
      components/footerBar.vue
  6. 55
      components/login-prompt.vue
  7. 13
      manifest.json
  8. 116
      pages/deepMate/deepMate.vue
  9. 699
      pages/home/home.vue
  10. 541
      pages/start/Registration/Registration.vue
  11. 134
      pages/start/components/login-prompt/login-prompt.vue
  12. 1341
      pages/start/login/list.js
  13. 580
      pages/start/login/login.vue
  14. BIN
      static/flag/ad.png
  15. BIN
      static/flag/ae.png
  16. BIN
      static/flag/af.png
  17. BIN
      static/flag/ag.png
  18. BIN
      static/flag/ai.png
  19. BIN
      static/flag/al.png
  20. BIN
      static/flag/am.png
  21. BIN
      static/flag/an.png
  22. BIN
      static/flag/ao.png
  23. BIN
      static/flag/aq.png
  24. BIN
      static/flag/ar.png
  25. BIN
      static/flag/as.png
  26. BIN
      static/flag/at.png
  27. BIN
      static/flag/au.png
  28. BIN
      static/flag/aw.png
  29. BIN
      static/flag/ax.png
  30. BIN
      static/flag/az.png
  31. BIN
      static/flag/ba.png
  32. BIN
      static/flag/bb.png
  33. BIN
      static/flag/bd.png
  34. BIN
      static/flag/be.png
  35. BIN
      static/flag/bf.png
  36. BIN
      static/flag/bg.png
  37. BIN
      static/flag/bh.png
  38. BIN
      static/flag/bi.png
  39. BIN
      static/flag/bj.png
  40. BIN
      static/flag/bl.png
  41. BIN
      static/flag/bm.png
  42. BIN
      static/flag/bn.png
  43. BIN
      static/flag/bo.png
  44. BIN
      static/flag/bq.png
  45. BIN
      static/flag/br.png
  46. BIN
      static/flag/bs.png
  47. BIN
      static/flag/bt.png
  48. BIN
      static/flag/bv.png
  49. BIN
      static/flag/bw.png
  50. BIN
      static/flag/by.png
  51. BIN
      static/flag/bz.png
  52. BIN
      static/flag/ca.png
  53. BIN
      static/flag/cc.png
  54. BIN
      static/flag/cd.png
  55. BIN
      static/flag/cf.png
  56. BIN
      static/flag/cg.png
  57. BIN
      static/flag/ch.png
  58. BIN
      static/flag/ci.png
  59. BIN
      static/flag/ck.png
  60. BIN
      static/flag/cl.png
  61. BIN
      static/flag/cm.png
  62. BIN
      static/flag/cn.png
  63. BIN
      static/flag/co.png
  64. BIN
      static/flag/cr.png
  65. BIN
      static/flag/cu.png
  66. BIN
      static/flag/cv.png
  67. BIN
      static/flag/cw.png
  68. BIN
      static/flag/cx.png
  69. BIN
      static/flag/cy.png
  70. BIN
      static/flag/cz.png
  71. BIN
      static/flag/de.png
  72. BIN
      static/flag/dj.png
  73. BIN
      static/flag/dk.png
  74. BIN
      static/flag/dm.png
  75. BIN
      static/flag/do.png
  76. BIN
      static/flag/dz.png
  77. BIN
      static/flag/ec.png
  78. BIN
      static/flag/ee.png
  79. BIN
      static/flag/eg.png
  80. BIN
      static/flag/eh.png
  81. BIN
      static/flag/er.png
  82. BIN
      static/flag/es.png
  83. BIN
      static/flag/et.png
  84. BIN
      static/flag/eu.png
  85. BIN
      static/flag/fi.png
  86. BIN
      static/flag/fj.png
  87. BIN
      static/flag/fk.png
  88. BIN
      static/flag/fm.png
  89. BIN
      static/flag/fo.png
  90. BIN
      static/flag/fr.png
  91. BIN
      static/flag/ga.png
  92. BIN
      static/flag/gb-eng.png
  93. BIN
      static/flag/gb-nir.png
  94. BIN
      static/flag/gb-sct.png
  95. BIN
      static/flag/gb-wls.png
  96. BIN
      static/flag/gb.png
  97. BIN
      static/flag/gd.png
  98. BIN
      static/flag/ge.png
  99. BIN
      static/flag/gf.png
  100. BIN
      static/flag/gg.png

9
.hbuilderx/launch.json

@ -0,0 +1,9 @@
{
"version" : "1.0",
"configurations" : [
{
"playground" : "standard",
"type" : "uni-app:app-ios"
}
]
}

198
components/DeepMate.vue

@ -0,0 +1,198 @@
<template>
<view class="deepmate">
<view class="deepmate-container">
<view class="deepmate-header">
<view class="title-container">
<view class="title-left">
<text class="deepmate-title">DeepMate</text>
<text class="deepmate-subtitle">您的市场最佳顾问~</text>
</view>
<view class="title-right">
<image class="deepmate-icon" src="https://d31zlh4on95l9h.cloudfront.net/images/7faa683450cc071bcc746fea8191ff6b.png" mode="aspectFit"></image>
</view>
</view>
</view>
<view class="deepmate-content">
<view class="deepmate-hotspots">
<view class="deepmate-question">
<text class="question-text">今日股票策略晨报</text>
</view>
<view class="hotspot-item">
<text>热门股票分析</text>
</view>
<view class="hotspot-item">
<text>行业趋势预测</text>
</view>
<view class="hotspot-item">
<text>市场风险提示</text>
</view>
<view class="hotspot-item">
<text>投资策略建议</text>
</view>
<view class="hotspot-item">
<text>财经新闻解读</text>
</view>
</view>
<view class="deepmate-action">
<input class="stock-input" type="text" placeholder="请输入股票代码/名称,获取AI洞察" />
<view class="send-button-container">
<image class="send-button" src="https://d31zlh4on95l9h.cloudfront.net/images/3da018821a5c82b06a1d6ddc81b960ac.png" mode="aspectFit"></image>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'DeepMate',
data() {
return {
}
}
}
</script>
<style>
/* DeepMate样式 */
.deepmate-container {
background-color: #ffe6e6;
border-radius: 10px;
padding: 15px 15px 15px 15px;
margin: 0;
width: 100%;
box-sizing: border-box;
overflow: hidden;
}
.deepmate-header {
margin-bottom: 10px;
}
.title-container {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.title-left {
width: 50%;
display: flex;
flex-direction: column;
}
.title-right {
width: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.deepmate-title {
font-size: 18px;
font-weight: bold;
color: #ff4d4f;
display: block;
}
.deepmate-icon {
width: 50px;
height: 50px;
margin-left: 0;
}
.deepmate-subtitle {
font-size: 12px;
color: #666;
display: block;
margin-top: 5px;
}
.deepmate-hotspots {
margin: 10px 0;
background-color: #ffffff;
border-radius: 10px;
padding: 10px;
}
.deepmate-question {
display: flex;
align-items: center;
margin-bottom: 10px;
padding-left: 10px;
position: relative;
}
.deepmate-question:before {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: #ff4d4f;
border-radius: 2px;
}
.question-text {
font-size: 16px;
font-weight: bold;
color: #333;
}
.hotspot-item {
background-color: #f5f5f5;
padding: 8px 12px;
border-radius: 6px;
margin-bottom: 8px;
}
.hotspot-item text {
font-size: 14px;
color: #333;
}
.deepmate-action {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #ff4d4f;
padding: 8px 15px;
border-radius: 25px;
margin-top: 10px;
border: none;
}
.stock-input {
flex: 1;
height: 36px;
font-size: 14px;
color: #ffffff;
padding: 0 10px;
border: none;
background-color: transparent;
}
.stock-input::placeholder {
color: rgba(255, 255, 255, 0.8);
}
.send-button-container {
display: flex;
justify-content: center;
align-items: center;
width: 36px;
height: 36px;
background-color: #ffffff;
border-radius: 50%;
margin-left: 10px;
}
.send-button {
width: 20px;
height: 20px;
}
</style>

385
components/MarketOverview.vue

@ -0,0 +1,385 @@
<template>
<view>
<!-- 第一行白色背景标题和按钮 -->
<view class="market-header">
<text class="section-title">今日市场概览</text>
<text class="more-btn" @click="showMarketSelector">{{ buttonText }}</text>
</view>
<!-- 第二行灰色圆角背景市场数据 -->
<view class="market-content">
<!-- 默认显示图片 -->
<view class="selected-market" v-if="!showForexMarket">
<image class="market-image" src="https://d31zlh4on95l9h.cloudfront.net/images/dee46373b5593d5f315d91675a38fb61.png" mode="widthFix"></image>
</view>
<!-- 外汇市场样式 -->
<view class="selected-market" v-if="showForexMarket">
<view class="forex-market">
<!-- 上部分三个汇率容器 -->
<view class="forex-rates">
<view class="forex-rate-item up">
<text class="forex-pair">美元/日元</text>
<text class="forex-value">151.13</text>
<text class="forex-change">+1.62%</text>
</view>
<view class="forex-rate-item down">
<text class="forex-pair">美元/韩元</text>
<text class="forex-value">1424.900</text>
<text class="forex-change">-2.92%</text>
</view>
<view class="forex-rate-item up">
<text class="forex-pair">美元/英镑</text>
<text class="forex-value">0.730</text>
<text class="forex-change">+2.92%</text>
</view>
</view>
<!-- 中部分智能解读标题 -->
<view class="forex-analysis-title">
<text class="analysis-title">智能解读</text>
</view>
<!-- 下部分智能解读内容 -->
<view class="forex-analysis-content">
<view class="analysis-icon-container">
<image class="analysis-brain-icon" src="https://d31zlh4on95l9h.cloudfront.net/images/8f35e54c52412467101370aa70d8fdb2.png" mode="aspectFit"></image>
</view>
<view class="analysis-items">
<view class="analysis-item">
<text class="analysis-dot orange"></text>
<text class="analysis-text">今日市场情报: 偏乐观</text>
</view>
<view class="analysis-item">
<text class="analysis-dot blue"></text>
<text class="analysis-text">市场风险评级: 需警惕潜在风险</text>
</view>
<view class="analysis-item">
<text class="analysis-dot green"></text>
<text class="analysis-text">早盘解析: 今日高开, 芯片稀土公共</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 市场选择对话框 -->
<uni-popup ref="marketPopup" type="center" :mask-click="true" @change="popupChange">
<view class="market-dialog">
<view class="dialog-title">
<text>选择市场</text>
</view>
<view class="market-list">
<view class="market-option" v-for="(market, index) in marketOptions" :key="index" @click="selectMarket(market.id)">
<view class="market-flag">
<image :src="market.flag" mode="aspectFit" class="flag-image"></image>
</view>
<view class="market-name-container">
<text class="market-option-name">{{ market.name }}</text>
</view>
<view class="market-checkbox">
<radio :checked="selectedMarket === market.id" color="#4080ff" />
</view>
</view>
</view>
<view class="dialog-buttons">
<button class="confirm-btn" @click="confirmMarketSelection">确认</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
export default {
name: 'MarketOverview',
data() {
return {
selectedMarket: 'forex',
tempSelectedMarket: 'forex', //
previousMarket: null,
buttonText: '切换市场',
showSelector: false,
showForexMarket: false, //
marketOptions: [
{ id: 'forex', name: '外汇市场', flag: '/static/c2.png', value: '+1.62%', trend: 'up' }
],
marketData: {
'forex': {
name: '外汇市场',
value: '+1.62%',
trend: 'up',
indicators: [
{ name: '美元/日元', value: '151.13 +1.62%', trend: 'up' },
{ name: '美元/韩元', value: '1424.900 -2.92%', trend: 'down' },
{ name: '美元/英镑', value: '0.79 +2.92%', trend: 'up' }
]
}
}
}
},
methods: {
showMarketSelector() {
//
this.showForexMarket = !this.showForexMarket;
//
this.buttonText = this.showForexMarket ? '外汇市场' : '切换市场';
},
popupChange(e) {
//
if (!e.show && this.tempSelectedMarket !== this.selectedMarket) {
this.tempSelectedMarket = this.selectedMarket;
}
},
selectMarket(marketId) {
//
this.tempSelectedMarket = marketId;
},
confirmMarketSelection() {
//
this.selectedMarket = this.tempSelectedMarket;
this.showSelector = false;
this.$refs.marketPopup.close();
},
getSelectedMarketName() {
if (!this.selectedMarket) return '';
return this.marketData[this.selectedMarket].name;
},
getSelectedMarketValue() {
if (!this.selectedMarket) return '';
return this.marketData[this.selectedMarket].value;
},
getSelectedMarketTrend() {
if (!this.selectedMarket) return '';
return this.marketData[this.selectedMarket].trend;
},
getSelectedMarketIndicators() {
if (!this.selectedMarket) return [];
return this.marketData[this.selectedMarket].indicators;
}
}
}
</script>
<style>
/* 市场概览样式 */
.market-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background-color: #ffffff;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
}
.more-btn {
font-size: 14px;
color: #4080ff;
}
.market-content {
margin: 0 0 15px;
background-color: #f5f7fa;
border-radius: 8px;
overflow: hidden;
}
/* 外汇市场样式 */
.forex-market {
padding: 15px;
}
.forex-rates {
display: flex;
justify-content: space-between;
gap: 10px;
margin-bottom: 15px;
}
.forex-rate-item {
width: 30%;
padding: 10px;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
}
.forex-rate-item.up {
background-color: rgba(82, 196, 26, 0.1);
border: 1px solid #52c41a;
}
.forex-rate-item.down {
background-color: rgba(245, 34, 45, 0.1);
border: 1px solid #f5222d;
}
.forex-pair {
font-size: 12px;
color: #666;
margin-bottom: 5px;
}
.forex-value {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 5px;
}
.forex-change {
font-size: 12px;
color: #52c41a;
}
.forex-rate-item.down .forex-change {
color: #f5222d;
}
.forex-analysis-title {
display: flex;
align-items: center;
margin-bottom: 10px;
border-left: 3px solid #f5222d;
padding-left: 8px;
}
.analysis-title {
font-size: 14px;
font-weight: bold;
color: #333;
}
.forex-analysis-content {
display: flex;
background-color: #fff;
border-radius: 6px;
padding: 12px;
}
.analysis-icon-container {
width: 40px;
height: 40px;
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
align-self: center;
}
.analysis-brain-icon {
width: 100%;
height: 100%;
}
.analysis-items {
flex: 1;
}
.analysis-item {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.analysis-item:last-child {
margin-bottom: 0;
}
.analysis-dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
}
.analysis-dot.orange {
background-color: #fa8c16;
}
.analysis-dot.blue {
background-color: #1890ff;
}
.analysis-dot.green {
background-color: #52c41a;
}
.analysis-text {
font-size: 12px;
color: #333;
line-height: 1.4;
}
/* 市场选择对话框样式 */
.market-dialog {
width: 300px;
background-color: #fff;
border-radius: 10px;
overflow: hidden;
}
.dialog-title {
padding: 15px;
text-align: center;
border-bottom: 1px solid #eee;
}
.market-list {
max-height: 300px;
overflow-y: auto;
}
.market-option {
display: flex;
align-items: center;
padding: 12px 15px;
border-bottom: 1px solid #f5f5f5;
}
.market-flag {
width: 24px;
height: 24px;
margin-right: 10px;
}
.flag-image {
width: 100%;
height: 100%;
border-radius: 50%;
}
.market-name-container {
flex: 1;
}
.market-option-name {
font-size: 14px;
color: #333;
}
.dialog-buttons {
padding: 10px 15px 15px;
display: flex;
justify-content: center;
}
.confirm-btn {
width: 80%;
height: 40px;
line-height: 40px;
text-align: center;
background-color: #4080ff;
color: #fff;
border-radius: 20px;
font-size: 14px;
}
</style>

192
components/footerBar-cn.vue

@ -0,0 +1,192 @@
<template>
<view class="static-footer-bar" :style="{ 'padding-bottom': safeAreaInsets.bottom + 'px' }">
<view class="static-footer-li" @click="tabChange(1)">
<image src="../static/footBar-image/home.png" class="static-footer-li-icon" v-if="type != 'home'"></image>
<image src="../static/footBar-image/home-selected.png" class="static-footer-li-icon" v-if="type == 'home'"></image>
<view :class="type == 'home' ? 'static-footer-li-title1' : 'static-footer-li-title'">
首页</view>
</view>
<view class="static-footer-li" @click="tabChange(2)">
<image src="../static/footBar-image/marketSituation.png" class="static-footer-li-icon" v-if="type != 'marketSituation'">
</image>
<image src="../static/footBar-image/marketSituation-selected.png" class="static-footer-li-icon"
v-if="type == 'marketSituation'"></image>
<view :class="type == 'marketSituation' ? 'static-footer-li-title1' : 'static-footer-li-title'">
行情</view>
</view>
<view class="static-footer-li static-footer-li-special" @click="tabChange(3)">
<image src="../static/footBar-image/deepMate.png" class="static-footer-li-icon static-footer-li-icon-special" v-if="type != 'deepMate'"></image>
<image src="../static/footBar-image/deepMate-selected.png" class="static-footer-li-icon static-footer-li-icon-special" v-if="type == 'deepMate'">
</image>
<view :class="type == 'deepMate' ? 'static-footer-li-title1' : 'static-footer-li-title'">
DeepMate</view>
</view>
<view class="static-footer-li" @click="tabChange(4)">
<image src="../static/footBar-image/deepExploration.png" class="static-footer-li-icon" v-if="type != 'deepExploration'">
</image>
<image src="../static/footBar-image/deepExploration-selected.png" class="static-footer-li-icon"
v-if="type == 'deepExploration'"></image>
<view :class="type == 'deepExploration' ? 'static-footer-li-title1' : 'static-footer-li-title'">
深度探索</view>
</view>
<view class="static-footer-li" @click="tabChange(5)">
<image src="../static/footBar-image/member.png" class="static-footer-li-icon" v-if="type != 'member'"></image>
<image src="../static/footBar-image/member-selected.png" class="static-footer-li-icon" v-if="type == 'member'"></image>
<view :class="type == 'member' ? 'static-footer-li-title1' : 'static-footer-li-title'">
我的</view>
</view>
</view>
</template>
<script setup>
import { computed, onMounted } from 'vue'
// props
const props = defineProps({
type: {
type: String,
default: ''
}
})
//
const safeAreaInsets = computed(() => {
//
const systemInfo = uni.getSystemInfoSync()
return {
bottom: systemInfo.safeAreaInsets?.bottom || 0
}
})
//
const tabChange = (value) => {
// console.log(value)
if (value == 1) { //
uni.redirectTo({
url: '/pages/home/home',
animationType: 'fade-in'
})
} else if (value == 2) { //
uni.redirectTo({
url: '/pages/home/marketSituation',
animationType: 'fade-in'
})
} else if (value == 3) { //DeepMate
uni.redirectTo({
url: '/pages/deepMate/deepMate',
animationType: 'fade-in'
})
} else if (value == 4) { //
if (props.type == 'deepExploration') return;
uni.redirectTo({
url: '/pages/home/deepExploration',
animationType: 'fade-in'
})
} else if (value == 5) { //
if (props.type == 'member') return;
uni.redirectTo({
url: '/pages/home/member',
animationType: 'fade-in'
})
}
}
//
onMounted(() => {
//
})
</script>
<style scoped>
.static-footer-bar {
width: 100%;
height: 120rpx;
border-top: 1px solid #E2E2E2;
background: #fff;
position: relative;
}
.static-footer-li {
display: inline-block;
width: 20%;
height: 100%;
text-align: center;
position: relative;
transform: translateY(-12rpx);
}
.static-footer-li-icon {
width: 46rpx;
height: 46rpx;
margin: 12rpx 0;
}
/* 中间导航项的特殊样式 */
.static-footer-li-special {
position: relative;
z-index: 10;
}
.static-footer-li-icon-special {
width: 95rpx !important;
height: 95rpx !important;
margin: 0rpx 0 !important;
border-radius: 50rpx;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.2);
transform: translateY(-12rpx);
transition: all 0.3s ease;
}
.static-footer-li-title {
width: 100%;
height: 40rpx;
line-height: 40rpx;
font-size: 24rpx;
text-align: center;
margin-top: -20rpx;
color: gray;
}
.static-footer-li-title1 {
width: 100%;
height: 40rpx;
line-height: 40rpx;
font-size: 24rpx;
text-align: center;
margin-top: -20rpx;
color: black;
}
.unreadNum {
position: absolute;
right: 15%;
background-color: #f00;
color: #fff;
font-size: 24rpx;
padding: 0 6rpx;
height: 36rpx;
line-height: 36rpx;
min-width: 36rpx;
border-radius: 18rpx;
z-index: 9;
}
.taskNew {
position: absolute;
right: 15%;
height: 30rpx;
z-index: 9;
}
.homeWorkUnRead {
position: absolute;
right: 30%;
top: 10%;
background-color: #f00;
color: #fff;
height: 12rpx;
width: 12rpx;
border-radius: 6rpx;
z-index: 9;
}
</style>

52
components/footerBar.vue

@ -1,37 +1,37 @@
<template>
<view class="static-footer-bar" :style="{ 'padding-bottom': safeAreaInsets.bottom + 'px' }">
<view class="static-footer-li ellipsis" @click="tabChange(1)">
<image src="../static/footBar-image/c1.png" class="static-footer-li-icon" v-if="type != 'home'"></image>
<image src="../static/footBar-image/logo.png" class="static-footer-li-icon" v-if="type == 'home'"></image>
<view class="static-footer-li" @click="tabChange(1)">
<image src="../static/footBar-image/home.png" class="static-footer-li-icon" v-if="type != 'home'"></image>
<image src="../static/footBar-image/home-selected.png" class="static-footer-li-icon" v-if="type == 'home'"></image>
<view :class="type == 'home' ? 'static-footer-li-title1' : 'static-footer-li-title'">
{{ $t('components.footerBar.homepage') }}</view>
</view>
<view class="static-footer-li ellipsis" @click="tabChange(2)">
<image src="../static/footBar-image/c2.png" class="static-footer-li-icon" v-if="type != 'marketSituation'">
<view class="static-footer-li" @click="tabChange(2)">
<image src="../static/footBar-image/marketSituation.png" class="static-footer-li-icon" v-if="type != 'marketSituation'">
</image>
<image src="../static/footBar-image/logo.png" class="static-footer-li-icon"
<image src="../static/footBar-image/marketSituation-selected.png" class="static-footer-li-icon"
v-if="type == 'marketSituation'"></image>
<view :class="type == 'marketSituation' ? 'static-footer-li-title1' : 'static-footer-li-title'">
{{ $t('components.footerBar.marketSituation') }}</view>
</view>
<view class="static-footer-li ellipsis" @click="tabChange(3)">
<image src="../static/footBar-image/logo.png" class="static-footer-li-icon" v-if="type != 'deepMate'"></image>
<image src="../static/footBar-image/logo.png" class="static-footer-li-icon" v-if="type == 'deepMate'">
<view class="static-footer-li static-footer-li-special" @click="tabChange(3)">
<image src="../static/footBar-image/deepMate.png" class="static-footer-li-icon static-footer-li-icon-special" v-if="type != 'deepMate'"></image>
<image src="../static/footBar-image/deepMate-selected.png" class="static-footer-li-icon static-footer-li-icon-special" v-if="type == 'deepMate'">
</image>
<view :class="type == 'deepMate' ? 'static-footer-li-title1' : 'static-footer-li-title'">
{{ $t('components.footerBar.deepMate') }}</view>
</view>
<view class="static-footer-li ellipsis" @click="tabChange(4)">
<image src="../static/footBar-image/c4.png" class="static-footer-li-icon" v-if="type != 'deepExploration'">
<view class="static-footer-li" @click="tabChange(4)">
<image src="../static/footBar-image/deepExploration.png" class="static-footer-li-icon" v-if="type != 'deepExploration'">
</image>
<image src="../static/footBar-image/logo.png" class="static-footer-li-icon"
<image src="../static/footBar-image/deepExploration-selected.png" class="static-footer-li-icon"
v-if="type == 'deepExploration'"></image>
<view :class="type == 'deepExploration' ? 'static-footer-li-title1' : 'static-footer-li-title'">
{{ $t('components.footerBar.deepExploration') }}</view>
</view>
<view class="static-footer-li ellipsis" @click="tabChange(5)">
<image src="../static/footBar-image/c5.png" class="static-footer-li-icon" v-if="type != 'member'"></image>
<image src="../static/footBar-image/logo.png" class="static-footer-li-icon" v-if="type == 'member'"></image>
<view class="static-footer-li" @click="tabChange(5)">
<image src="../static/footBar-image/member.png" class="static-footer-li-icon" v-if="type != 'member'"></image>
<image src="../static/footBar-image/member-selected.png" class="static-footer-li-icon" v-if="type == 'member'"></image>
<view :class="type == 'member' ? 'static-footer-li-title1' : 'static-footer-li-title'">
{{ $t('components.footerBar.member') }}</view>
</view>
@ -73,7 +73,7 @@ const tabChange = (value) => {
})
} else if (value == 3) { //DeepMate
uni.redirectTo({
url: '/pages/home/deepMate',
url: '/pages/deepMate/deepMate',
animationType: 'fade-in'
})
} else if (value == 4) { //
@ -100,9 +100,10 @@ onMounted(() => {
<style scoped>
.static-footer-bar {
width: 100%;
height: 100rpx;
height: 120rpx;
border-top: 1px solid #E2E2E2;
background: #fff;
position: relative;
}
.static-footer-li {
@ -111,6 +112,7 @@ onMounted(() => {
height: 100%;
text-align: center;
position: relative;
transform: translateY(-12rpx);
}
.static-footer-li-icon {
@ -119,6 +121,22 @@ onMounted(() => {
margin: 12rpx 0;
}
/* 中间导航项的特殊样式 */
.static-footer-li-special {
position: relative;
z-index: 10;
}
.static-footer-li-icon-special {
width: 95rpx !important;
height: 95rpx !important;
margin: 0rpx 0 !important;
border-radius: 50rpx;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.2);
transform: translateY(-12rpx);
transition: all 0.3s ease;
}
.static-footer-li-title {
width: 100%;
height: 40rpx;

55
components/login-prompt.vue

@ -1,16 +1,22 @@
<template>
<view class="login-prompt" v-if="showPrompt">
<view class="mask" :class="{ 'mask-show': showAnimation }" @click="hide"></view>
<view
class="mask"
:class="{ 'mask-show': showAnimation }"
></view>
<view class="prompt-content" :class="{ 'slide-up': showAnimation }">
<text class="prompt-title">登录以获得更好的体验</text>
<button class="login-btn" @click="goLogin">登录(推荐)</button>
<button class="visitor-btn" @click="continueAsVisitor">以访客身份继续</button>
<button class="login-btn" @click="goLogin">登录</button>
<button class="visitor-btn" @click="continueAsVisitor">
以访客身份继续
</button>
<text class="prompt-title-small" @click="goRegister">如果您还没有账号点击注册</text>
</view>
</view>
</template>
<script setup>
import { ref, nextTick } from 'vue';
import { ref, nextTick } from "vue";
//
const showPrompt = ref(false);
@ -39,7 +45,14 @@ const hide = () => {
//
const goLogin = () => {
uni.navigateTo({
url: '/pages/login/login'
url: "/pages/start/login/login",
});
hide();
};
//
const goRegister = () => {
uni.navigateTo({
url: "/pages/start/Registration/Registration",
});
hide();
};
@ -47,14 +60,14 @@ const goLogin = () => {
// 访
const continueAsVisitor = () => {
// 访
uni.setStorageSync('visitorMode', true);
uni.setStorageSync("visitorMode", true);
hide();
};
// 使
defineExpose({
show,
hide
hide,
});
</script>
@ -88,10 +101,10 @@ defineExpose({
bottom: 0;
left: 0;
right: 0;
height: 300rpx;
height: 400rpx;
border-radius: 20rpx 20rpx 0 0;
background-color: white;
padding: 20rpx 30rpx;
padding: 20rpx 70rpx;
transform: translateY(100%);
transition: transform 0.3s ease;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.3);
@ -103,18 +116,18 @@ defineExpose({
.prompt-title {
display: block;
text-align: left;
font-size: 28rpx;
text-align: center;
font-size: 40rpx;
font-weight: 700;
color: #333;
margin-top: 10rpx;
margin-bottom: 30rpx;
color: #000000;
margin-top: 30rpx;
margin-bottom: 40rpx;
}
.login-btn {
width: 100%;
height: 80rpx;
background-color:rgb(35, 84, 230);
background-color: rgb(0, 0, 0);
color: white;
font-size: 32rpx;
line-height: 80rpx;
@ -131,4 +144,16 @@ defineExpose({
line-height: 80rpx;
border-radius: 40rpx;
}
.prompt-title-small {
display: block;
text-align: center;
font-size: 24rpx;
color: #6a6a6a;
margin-top: 30rpx;
margin-bottom: 40rpx;
line-height: 15.5px;
letter-spacing: 0.3px;
font-style: normal;
font-family: "PingFang SC";
}
</style>

13
manifest.json

@ -16,7 +16,9 @@
"autoclose" : true,
"delay" : 0
},
"modules" : {},
"modules" : {
"OAuth" : {}
},
/* */
"distribute" : {
/* */
@ -42,7 +44,14 @@
},
"ios" : {},
/* ios */
"sdkConfigs" : {}
"sdkConfigs" : {
"oauth" : {
"apple" : {},
"google" : {
"clientid" : "135"
}
}
}
}
},
/* SDK */

116
pages/deepMate/deepMate.vue

@ -3,14 +3,23 @@
<!-- 顶部导航栏 -->
<view class="header" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
<view class="header-left">
<image src="https://d31zlh4on95l9h.cloudfront.net/images/f91e09b5987802185e7679055dafd272.svg" class="icon"></image>
<image
src="https://d31zlh4on95l9h.cloudfront.net/images/f91e09b5987802185e7679055dafd272.svg"
class="icon"
></image>
</view>
<view class="header-center">
<text class="title">DeepMate</text>
</view>
<view class="header-right">
<image src="https://d31zlh4on95l9h.cloudfront.net/images/d7c4e74201213a25dd9574e908233928.svg" class="icon"></image>
<image src="https://d31zlh4on95l9h.cloudfront.net/images/099903c4aabf5713488b5cb60815e3f7.svg" class="icon"></image>
<image
src="https://d31zlh4on95l9h.cloudfront.net/images/d7c4e74201213a25dd9574e908233928.svg"
class="icon"
></image>
<image
src="https://d31zlh4on95l9h.cloudfront.net/images/099903c4aabf5713488b5cb60815e3f7.svg"
class="icon"
></image>
<!-- 新增新会话按钮
<button class="new-chat-button" @click="newChat">
<text class="new-chat-text">新会话</text>
@ -59,11 +68,10 @@
<text class="section-title">- 您可能感兴趣 -</text>
<view class="topics-list">
<view class="topic-item" v-for="topic in hotTopics" :key="topic.id">
<image
:src="topic.icon"
class="tag-icon"
></image>
<text class="topic-text" @click="sendMessageList(topic.text)">{{ topic.text }}</text>
<image :src="topic.icon" class="tag-icon"></image>
<text class="topic-text" @click="sendMessageList(topic.text)">{{
topic.text
}}</text>
</view>
</view>
</view> -->
@ -127,7 +135,12 @@
>
</view>
<image class="back-to-top" src="https://d31zlh4on95l9h.cloudfront.net/images/ba357635d2bb480241952bb1cabacd73.svg" @click="scrollToTop"></image>
<image
class="back-to-top"
src="https://d31zlh4on95l9h.cloudfront.net/images/ba357635d2bb480241952bb1cabacd73.svg"
@click="scrollToTop"
></image>
<footerBar class="static-footer" :type="type"></footerBar>
</view>
</template>
@ -136,6 +149,8 @@ const { safeAreaInsets } = uni.getSystemInfoSync();
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from "vue";
const type = ref('member')
const inputMessage = ref("");
const isSending = ref(false);
const uuid = ref("");
@ -143,24 +158,24 @@ const messages = ref([]);
const hotTopics = ref([
{
id: 1,
text: '英伟达(NVDA)股票情绪温度?',
icon: 'https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg'
text: "英伟达(NVDA)股票情绪温度?",
icon: "https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg",
},
{
id: 2,
text: '博通(AVGO)明天还能涨吗?',
icon: 'https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg'
text: "博通(AVGO)明天还能涨吗?",
icon: "https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg",
},
{
id: 3,
text: '为什么Fluence Energy(FLNC)会暴涨?',
icon: 'https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg'
text: "为什么Fluence Energy(FLNC)会暴涨?",
icon: "https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg",
},
{
id: 4,
text: '为什么Fluence Energy(FLNC)会暴涨?',
icon: 'https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg'
}
text: "为什么Fluence Energy(FLNC)会暴涨?",
icon: "https://d31zlh4on95l9h.cloudfront.net/images/7ed58be0f4b81aeb398d9ba2534a624b.svg",
},
]);
// tabs
@ -238,19 +253,19 @@ onUnmounted(() => { stopFrame(tabsTickId); });
// UUID
const initUUID = () => {
let storedUUID = uni.getStorageSync('user_uuid');
let storedUUID = uni.getStorageSync("user_uuid");
if (!storedUUID) {
storedUUID = generateUUID();
uni.setStorageSync('user_uuid', storedUUID);
uni.setStorageSync("user_uuid", storedUUID);
}
uuid.value = storedUUID;
};
// UUID
const generateUUID = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
};
@ -258,14 +273,14 @@ const generateUUID = () => {
//
const newChat = () => {
messages.value = [];
uni.removeStorageSync('user_uuid');
uni.removeStorageSync("user_uuid");
initUUID();
};
//
const goBlank = () => {
uni.navigateTo({
url: '/pages/blank/blank'
url: "/pages/blank/blank",
});
};
@ -277,7 +292,7 @@ const sendMessage = () => {
content: inputMessage.value,
isUser: true,
isThinking: false,
isTyping: false
isTyping: false,
};
messages.value.push(userMessage);
@ -292,18 +307,15 @@ const sendMessage = () => {
simulateBotResponse(userMessage.content);
};
//
const sendMessageList = (listMessage) => {
console.log(listMessage);
const userMessage = {
content: listMessage,
isUser: true,
isThinking: false,
isTyping: false
isTyping: false,
};
messages.value.push(userMessage);
@ -327,7 +339,7 @@ const simulateBotResponse = (userMessage) => {
content: "",
isUser: false,
isTyping: true,
isThinking: false
isThinking: false,
};
messages.value.push(botMsg);
@ -338,7 +350,7 @@ const simulateBotResponse = (userMessage) => {
});
//
let responseText = `我已经收到您的消息: "${userMessage}"。作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?`;
let responseText = `我已经收到您的消息: "${userMessage}"。作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?作为您的股市顾问,我可以为您提供专业的投资建议。请问您想了解哪方面的信息?`;
let index = 0;
const typeWriter = () => {
@ -362,14 +374,14 @@ const simulateBotResponse = (userMessage) => {
//
const scrollToBottom = () => {
const query = uni.createSelectorQuery();
query.select('#messageList').boundingClientRect();
query.select("#messageList").boundingClientRect();
query.selectViewport().scrollOffset();
query.exec((res) => {
if (res[0] && res[1]) {
uni.pageScrollTo({
scrollTop: res[0].height,
duration: 100
duration: 100,
});
}
});
@ -385,7 +397,8 @@ const scrollToTop = () => {
flex-direction: column;
height: 100vh;
background-color: #ffffff;
padding: 20rpx;
padding: 20rpx 0rpx;
}
.header {
@ -394,6 +407,7 @@ const scrollToTop = () => {
align-items: center;
padding: 20rpx 30rpx;
background-color: #ffffff;
}
.header-left,
@ -410,8 +424,6 @@ const scrollToTop = () => {
}
.header-center .title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
@ -502,7 +514,7 @@ const scrollToTop = () => {
}
.recommend-card {
background: url('https://d31zlh4on95l9h.cloudfront.net/images/4da1d629a55c307c3605ca15bf15189a.svg');
background: url("https://d31zlh4on95l9h.cloudfront.net/images/4da1d629a55c307c3605ca15bf15189a.svg");
background-repeat: no-repeat;
background-size: 100% 100%;
/* border-radius: 20rpx; */
@ -553,14 +565,13 @@ const scrollToTop = () => {
}
.arrow-icon {
background: url('https://d31zlh4on95l9h.cloudfront.net/images/40d94054644f6e3f1c366751f07f0010.svg');
background-repeat: no-repeat;
left: 0.5rem;
top: 1.8rem;
background-size: 100% 100%;
width: 60rpx;
height: 60rpx;
background: url("https://d31zlh4on95l9h.cloudfront.net/images/40d94054644f6e3f1c366751f07f0010.svg");
background-repeat: no-repeat;
left: 0.5rem;
top: 1.8rem;
background-size: 100% 100%;
width: 60rpx;
height: 60rpx;
}
.interest-section {
@ -731,7 +742,7 @@ height: 60rpx;
padding: 15rpx 20rpx;
background-color: rgb(220, 31, 29);
border-radius: 100rpx;
display: flex;
display: flex;
align-items: center;
justify-content: center;
@ -763,7 +774,7 @@ height: 60rpx;
}
.send-button {
background: url('https://d31zlh4on95l9h.cloudfront.net/images/95f1ea2262e9157db13c93c0dc1c5d96.svg');
background: url("https://d31zlh4on95l9h.cloudfront.net/images/95f1ea2262e9157db13c93c0dc1c5d96.svg");
background-repeat: no-repeat;
background-size: 100% 100%;
height: 50rpx;
@ -792,6 +803,9 @@ height: 60rpx;
overflow: hidden; /* 让圆角和内部层剪裁一致 */
border-radius: 15rpx;
}
.panelShow{
height: 12%;
}
.pray-banner {
position: absolute;
@ -802,6 +816,8 @@ height: 60rpx;
border-radius: 15rpx;
z-index: 1; /* 在灰底之上、内容之下 */
}
.contain {
margin: 0 20rpx;
gap: 5rpx;
@ -823,4 +839,8 @@ height: 60rpx;
.back-to-top:active {
transform: scale(0.96);
}
.static-footer {
position: fixed;
bottom: 0;
}
</style>

699
pages/home/home.vue

@ -2,27 +2,708 @@
<view class="main">
<!-- 顶部状态栏占位 -->
<view class="top" :style="{height:iSMT+'px'}"></view>
<view>首页</view>
<!-- 头部导航 -->
<view class="header">
<view class="headphone-icon">
<image src="https://d31zlh4on95l9h.cloudfront.net/images/bef2edba6cc0c85671fde07cfab5270d.png" class="header-icon-image"></image>
</view>
<view class="title">DeepChart</view>
<view class="notification-icon">
<image src="https://d31zlh4on95l9h.cloudfront.net/images/2554c84b91712d2a6cb6b00380e63bac.png" class="header-icon-image"></image>
</view>
</view>
<!-- 内容区域 - 使用滚动视图 -->
<scroll-view scroll-y class="content-container">
<!-- 1. 今日市场概览 -->
<market-overview></market-overview>
<!-- 间隔 -->
<view class="section-gap"></view>
<!-- 新增欢迎部分 -->
<view class="section welcome-section">
<!-- 轮播图 -->
<swiper class="welcome-swiper" circular autoplay interval="3000" duration="500" indicator-dots indicator-active-color="#4080ff">
<swiper-item v-for="(item, index) in 5" :key="index">
<image class="swiper-image" src="https://d31zlh4on95l9h.cloudfront.net/images/e4272cc034fa2a3d1ca588ef84e51ab0.png" mode="aspectFill"></image>
</swiper-item>
</swiper>
</view>
<!-- 2. DeepMate -->
<view class="section deepmate-section">
<DeepMate />
</view>
<!-- 3. 深度探索 -->
<view class="section deep-exploration">
<!-- 上部分标题和查看更多按钮 -->
<view class="section-header-container">
<view class="section-header">
<view class="header-left">
<text class="section-title">深度探索</text>
</view>
<view class="header-right">
<text class="more-btn">查看更多</text>
</view>
</view>
</view>
<!-- 下部分四个图标 -->
<view class="exploration-container">
<view class="exploration-content">
<view class="exploration-item">
<image class="exploration-icon" src="https://d31zlh4on95l9h.cloudfront.net/images/199472b0ee90a1c897f7c87b85accd84.png" mode="aspectFit" lazy-load="true"></image>
<text class="exploration-text">主力追踪</text>
</view>
<view class="exploration-item">
<image class="exploration-icon" src="https://d31zlh4on95l9h.cloudfront.net/images/c25ca5e176efc961dabfa5d0d1b486b0.png" mode="aspectFit" lazy-load="true"></image>
<text class="exploration-text">主力资达</text>
</view>
<view class="exploration-item">
<image class="exploration-icon" src="https://d31zlh4on95l9h.cloudfront.net/images/c064d7066dc8129a7df7b052762f82cf.png" mode="aspectFit" lazy-load="true"></image>
<text class="exploration-text">主力解码</text>
</view>
<view class="exploration-item">
<image class="exploration-icon" src="https://d31zlh4on95l9h.cloudfront.net/images/9d69cceee9c515911477078af6f68d88.png" mode="aspectFit" lazy-load="true"></image>
<text class="exploration-text">主力资金流</text>
</view>
</view>
</view>
</view>
<!-- 4. 我的自选 -->
<view class="section my-selection">
<!-- 上部分标题和查看更多按钮 -->
<view class="section-header-container">
<view class="section-header">
<text class="section-title">我的自选</text>
<text class="more-btn">添加自选股</text>
</view>
</view>
<!-- 下部分股票列表 -->
<view class="stock-container">
<view class="stock-list">
<view class="stock-item" v-for="(item, index) in myStocks" :key="item.code">
<view class="stock-info">
<view class="name-container">
<text class="stock-name">{{item.name}}</text>
<text class="stock-code-label">{{item.code}}</text>
</view>
<view class="price-container">
<text class="stock-price">{{item.price}}</text>
<text class="stock-change" :class="{'stock-up': item.change > 0, 'stock-down': item.change < 0}">{{item.change > 0 ? '+' : ''}}{{item.change}}%</text>
</view>
</view>
<view class="stock-chart">
<image :src="item.chartImg" mode="aspectFit" class="chart-image"></image>
</view>
</view>
</view>
<!-- 机构动向简报数据 -->
<view class="institutional-reports">
<view class="section-title-container">
<text class="section-title-text">机构动向简报</text>
</view>
<view class="text-gap"></view>
<view class="report-item" v-for="(report, index) in institutionalReports" :key="index">
<view class="report-stock">{{report.stock}}</view>
<view class="report-status">{{report.status}}</view>
</view>
<view class="view-more">
<text>查看更多 >></text>
</view>
</view>
</view>
</view>
<!-- 5. 今日市场看点 -->
<view class="section-header highlights-title-container">
<text class="section-title">今日市场核心看点</text>
</view>
<view class="highlights-image-container">
<image src="https://d31zlh4on95l9h.cloudfront.net/images/8d5365af968402a18cedb120c09460b0.png" mode="aspectFit" class="highlights-image"></image>
</view>
<!-- 底部空间 - 为底部导航腾出空间 -->
<view class="bottom-space"></view>
</scroll-view>
<!-- 底部导航 -->
<footerBar class="static-footer" :type="type"></footerBar>
</view>
</template>
<script setup>
import { ref,onMounted } from 'vue'
<script>
import footerBar from '../../components/footerBar.vue'
import MarketOverview from '../../components/MarketOverview.vue'
import DeepMate from '../../components/DeepMate.vue'
export default {
components: {
footerBar,
MarketOverview,
DeepMate
},
data() {
return {
type: 'home',
iSMT: 0,
//
explorationItems: [
{ title: '主力追踪', icon: '/static/c1.png' },
{ title: '主力资金', icon: '/static/c2.png' },
{ title: '主力解码', icon: '/static/c3.png' },
{ title: '主力资金流', icon: '/static/c4.png' }
],
//
myStocks: [
{ name: '特斯拉', code: 'TSLA', price: '482.00', change: 2.80, chartImg: '/static/c5.png' },
{ name: '英伟达', code: 'NVDA', price: '189.800', change: -2.92, chartImg: '/static/c6.png' },
{ name: '苹果', code: 'AAPL', price: '256.430', change: 2.60, chartImg: '/static/c7.png' }
],
//
institutionalReports: [
{ stock: '特斯拉', status: '当前市场多头资金占比,且多头资金持续流入。' },
{ stock: '英伟达', status: '当前市场多头资金占比,且多头资金持续流入。' },
{ stock: '苹果', status: '当前市场多头资金占比,且多头资金持续流入。' }
],
const type = ref('home')
const iSMT = ref(0)
//
debounceTimer: null
}
},
onMounted(() => {
//
iSMT.value = uni.getSystemInfoSync().statusBarHeight;
})
// Vue 2
mounted() {
//
this.iSMT = uni.getSystemInfoSync().statusBarHeight;
//
this.myStocks.forEach(stock => {
// 使uni.getImageInfoImage
uni.getImageInfo({
src: stock.chartImg,
success: function(res) {
//
console.log('图片预加载成功:', stock.name)
},
fail: function(err) {
console.log('图片预加载失败:', err)
}
})
})
},
methods: {
//
debounce(fn, delay = 300) {
if (this.debounceTimer) clearTimeout(this.debounceTimer)
this.debounceTimer = setTimeout(() => {
fn()
this.debounceTimer = null
}, delay)
}
}
}
</script>
<style scoped>
.main {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #ffffff;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background-color: #ffffff;
}
.title {
font-size: 22px;
font-weight: bold;
text-align: center;
flex: 1;
}
.headphone-icon, .notification-icon {
width: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.header-icon-image {
width: 24px;
height: 24px;
object-fit: contain;
}
.content-container {
flex: 1;
padding: 10px;
width: 100%;
box-sizing: border-box;
overflow-x: hidden;
}
.section {
margin-bottom: 15px;
/* background-color: #f5f5f5; */
background-color: #ffffff;
border-radius: 8px;
padding: 15px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
}
.section-title {
font-size: 18px;
font-weight: bold;
color: #000000;
}
.section-title-text{
font-size: 14px;
}
.text-gap{
height: 10px;
}
.more-btn {
font-size: 12px;
font-weight: bold;
color: #ffffff;
background-color: #000000;
padding: 4px 8px;
border-radius: 4px;
}
/* 市场概览样式 */
.market-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background-color: #ffffff;
margin-bottom: 10px;
}
.market-content {
background-color: #f5f5f5;
border-radius: 8px;
padding: 15px;
margin: 0 15px;
}
.market-image {
margin-bottom: 15px;
display: flex;
justify-content: center;
}
.overview-image {
width: 100%;
border-radius: 8px;
}
.market-data {
display: flex;
flex-direction: column;
gap: 10px;
}
/* 间隔样式 */
.section-gap {
height: 20px;
}
.market-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
.down {
color: #ff4d4f;
}
.up {
color: #52c41a;
}
/* DeepMate样式 */
.deepmate-container {
background-color: #ffe6e6;
border-radius: 10px;
padding: 15px;
margin: 0 15px;
}
.deepmate-header {
margin-bottom: 10px;
}
.title-container {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.title-left {
width: 50%;
}
.title-right {
width: 50%;
display: flex;
justify-content: flex-end;
}
.deepmate-title {
font-size: 18px;
font-weight: bold;
color: #ff4d4f;
}
.deepmate-icon {
width: 60px;
height: 60px;
margin-left: 0;
}
.deepmate-subtitle {
font-size: 12px;
color: #666;
margin-left: 5px;
}
.deepmate-hotspots {
margin: 10px 0;
}
.hotspot-item {
background-color: #f5f5f5;
padding: 8px 12px;
border-radius: 6px;
margin-bottom: 8px;
}
.hotspot-item text {
font-size: 14px;
color: #333;
}
.deepmate-action {
display: flex;
justify-content: center;
align-items: center;
background-color: #ffffff;
border-radius: 20px;
padding: 10px;
margin-top: 10px;
}
/* 欢迎部分样式 */
.welcome-section {
margin-bottom: 15px;
padding: 0;
}
.welcome-swiper {
width: 100%;
height: 150px;
border-radius: 0;
overflow: hidden;
}
.deepmate-section {
padding: 0;
}
.swiper-image {
width: 100%;
height: 100%;
border-radius: 8px;
object-fit: contain;
}
/* 深度探索样式 */
.deep-exploration {
margin-top: 15px;
padding: 0; /* 移除内边距,让子容器自己控制 */
}
.section-header-container {
margin-bottom: 10px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background-color: #ffffff;
}
.header-left {
display: flex;
align-items: center;
}
.header-right {
display: flex;
align-items: center;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
}
.more-btn {
font-size: 12px;
color: #ffffff;
}
.exploration-container {
border-radius: 8px;
overflow: hidden;
}
.exploration-content {
display: flex;
justify-content: space-between;
padding: 15px;
background-color: #f5f5f5;
border-radius: 8px;
}
.exploration-item {
display: flex;
flex-direction: column;
align-items: center;
width: 22%;
background-color: #ffffff;
border-radius: 8px;
padding: 10px 0;
}
.exploration-icon {
width: 50px;
height: 50px;
margin-bottom: 8px;
}
.exploration-text {
font-size: 12px;
color: #333;
}
.icon-text {
font-size: 12px;
}
/* 我的自选样式 */
.my-selection {
padding: 0; /* 移除内边距,让子容器自己控制 */
}
.stock-container {
border-radius: 8px;
overflow: hidden;
background-color: #f5f5f5;
padding: 15px;
box-sizing: border-box;
}
.stock-list {
display: flex;
flex-direction: row;
justify-content: center;
background-color: #f8f8f8;
border-radius: 8px;
padding: 15px;
gap: 10px; /* 添加卡片之间的间距 */
}
.stock-item {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 3px;
background-color: #ffffff;
border-radius: 8px;
width: 30%;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
will-change: transform;
transform: translateZ(0);
}
.stock-info {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 5px;
}
.stock-chart {
display: flex;
align-items: center;
justify-content: center;
}
.name-container {
display: flex;
align-items: center;
gap: 5px;
}
.stock-name {
font-size: 12px;
font-weight: bold;
color: #333;
}
.stock-code-label {
font-size: 12px;
color: #666;
background-color: #f5f5f5;
padding: 0 4px;
border-radius: 3px;
}
.stock-price {
font-size: 12px;
color: #666;
}
.price-container {
display: flex;
align-items: center;
gap: 5px;
}
.stock-change {
font-size: 12px;
}
.stock-up {
color: #4cd964;
}
.stock-down {
color: #ff3b30;
}
.chart-image {
width: 100px;
height: 40px;
}
/* 市场看点样式 */
.highlights-title-container {
background-color: #ffffff;
margin-bottom: 10px;
}
.highlights-image-container {
background-color: #f5f5f5;
border-radius: 8px;
padding: 10px;
margin-bottom: 15px;
}
.highlights-image {
width: 100%;
height: 150px;
border-radius: 4px;
}
/* 机构动向简报样式 */
.institutional-reports {
margin-top: 15px;
background-color: #f8f8f8;
border-radius: 8px;
padding: 10px;
}
.section-title-container {
position: relative;
padding-left: 10px;
margin-bottom: 5px;
}
.section-title-container:before {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: #ff4d4f;
border-radius: 2px;
}
.report-item {
background-color: #ffffff;
border-radius: 8px;
padding: 10px;
margin-bottom: 8px;
}
.report-stock {
font-size: 14px;
font-weight: bold;
color: #e74c3c;
margin-bottom: 5px;
}
.report-status {
font-size: 12px;
color: #333;
}
.view-more {
text-align: center;
color: #1890ff;
font-size: 12px;
padding: 5px;
}
/* 底部空间 */
.bottom-space {
height: 60px;
}
/* 底部导航 */
.static-footer {
position: fixed;
bottom: 0;
width: 100%;
}
</style>

541
pages/start/Registration/Registration.vue

@ -1,108 +1,216 @@
<template>
<view class="login-registration-container">
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
<view class="nav-left">
<text class="back-btn" @click="goToLogin"></text>
</view>
<view
class="custom-navbar"
:style="{ paddingTop: safeAreaInsets?.top + 'px' }"
>
<!-- <view class="nav-left">
<text class="back-btn" @click="goToIndex"></text>
</view> -->
<view class="nav-right">
<text class="headphone-btn">🎧</text>
<image
class="icons"
src="../../../static/icons/Headset.png"
alt="联系客服"
/>
<image
class="icons"
@click="goToIndex"
src="../../../static/icons/Frame.png"
alt="返回首页"
/>
</view>
</view>
<!-- Logo -->
<image class="logo" src="/static/logo.png" mode="aspectFit"></image>
<!-- <image class="logo" src="/static/logo.png" mode="aspectFit"></image> -->
<!-- 欢迎语 -->
<text class="welcome-text">欢迎来到 DeepChart</text>
<text class="welcome-text">欢迎来到DeepChart</text>
<!-- 邮箱/手机号切换 -->
<view class="switch-container">
<text
class="switch-item"
:class="{ active: switchType === 'Email' }"
@click="switchEmail">邮箱</text>
@click="switchEmail"
>邮箱</text
>
<text
class="switch-item"
:class="{ active: switchType === 'Phone' }"
@click="switchPhone">手机号</text>
@click="switchPhone"
>手机号</text
>
</view>
<!-- 输入框 -->
<view class="input-container">
<input
v-if="switchType === 'Email'"
class="input-field"
type="text"
placeholder="输入邮箱地址"
v-model="email"
/>
<input
v-else
class="input-field"
type="text"
placeholder="输入手机号"
v-model="phone"
/>
<view v-if="switchType === 'Email'">
<!-- 修改邮箱输入框容器将图标包含在内 -->
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Mail.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入邮箱"
v-model="email"
/>
<view>
<button
class="send-code-btn-email"
:disabled="isCodeBtnDisabled"
:class="{ 'send-code-btn-disabled': isCodeBtnDisabled }"
@click="sendCode"
>
<text
class="send-code-text"
:class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }"
>{{ codeBtnText }}</text
>
</button>
</view>
</view>
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Text-recognition.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入验证码"
v-model="password"
/>
</view>
</view>
<view v-if="switchType === 'Phone'" class="phone-input-container">
<view class="country-code-selector" @click="showCountryPicker">
<image
class="country-flag-img"
src="../../../static/icons/Iphone.png"
mode="aspectFit"
></image>
<text class="country-code">{{ selectedCountry.code }}</text>
<!-- <text class="arrow-down"></text> -->
</view>
<input
class="input-field phone-input"
type="number"
placeholder="输入手机号"
v-model="phone"
@input="onPhoneInput"
/>
<view>
<button
class="send-code-btn"
:disabled="isCodeBtnDisabled"
:class="{ 'send-code-btn-disabled': isCodeBtnDisabled }"
@click="sendCode"
>
<text
class="send-code-text"
:class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }"
>{{ codeBtnText }}</text
>
</button>
</view>
</view>
<view v-if="switchType === 'Phone'" class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Text-recognition.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入验证码"
v-model="password"
/>
</view>
</view>
<!-- 用户协议 -->
<view class="agreement-container">
<checkbox class="checkbox" @click="checkboxClick"/>
<text class="agreement-text"
<view @click="changeCheckbox" class="agreement-container-one">
<image class="checkbox" :src="checkboxUrl"></image>
<text class="agreement-text-one"
>接受 <text class="link" @click="openAgreement">用户协议</text>
<text class="link" @click="openPrivacy">隐私政策</text></text
>
</view>
<!-- 注册按钮 -->
<button class="register-btn" @click="register">注册</button>
<!-- 或者 -->
<text class="or-text">或者</text>
<text class="or-text" @click="goToLogin"
>已有账号登录
</text>
<!-- 第三方登录 -->
<view class="third-party-login">
<view class="third-party-btn" @click="loginWithApple">
<image
class="apple-icon"
src="/static/apple-icon.png"
mode="aspectFit"
></image>
<text class="third-party-text">通过 Apple 继续</text>
</view>
<footerBar class="static-footer" :type="type"></footerBar>
<view class="third-party-btn" @click="loginWithGoogle">
<image
class="google-icon"
src="/static/google-icon.png"
mode="aspectFit"
></image>
<text class="third-party-text">通过 Google 继续</text>
</view>
</view>
<!-- 已有账号 -->
<view class="existing-account">
<text class="account-text">已有账号</text>
<text class="login-link" @click="goToLogin">登录</text>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { ref } from "vue";
//
import countryList from "../login/list";
import footerBar from '../../../components/footerBar-cn.vue'
const type = ref('member')
const email = ref("");
const password = ref("");
const phone = ref("");
const agreed = ref(false);
const switchType = ref("Email"); //
const { safeAreaInsets } = uni.getSystemInfoSync()
const { safeAreaInsets } = uni.getSystemInfoSync();
const codeBtnText = ref("获取验证码");
const isCodeBtnDisabled = ref(false); //
const checkboxUrl = ref("../../../static/icons/Check-one-false.png");
// 使list.js
const countries = ref(
countryList.list.map((item) => ({
name: item.name,
code: `+${item.tel}`,
flag: item.flag,
short: item.short,
en: item.en,
}))
);
//
const selectedCountry = ref(
countries.value.find((country) => country.short === "CN") ||
countries.value[0]
);
//
function showCountryPicker() {
uni.showActionSheet({
itemList: countries.value.map(
(country) => `${country.name} ${country.code}`
),
success: function (res) {
selectedCountry.value = countries.value[res.tapIndex];
},
});
}
function goBack() {
function goToIndex() {
//
uni.navigateBack(-1);
uni.navigateTo({
url: "/pages/start/index/index",
});
}
function switchEmail() {
@ -115,8 +223,76 @@ function switchPhone() {
switchType.value = "Phone";
}
function checkboxClick() {
agreed.value = !agreed.value;
function register() {
if (switchType.value === "Email") {
//
if (!email.value) {
uni.showToast({
title: "请输入邮箱地址",
icon: "none",
});
return;
}
//
console.log("登录:", email.value);
}
if (switchType.value === "Phone") {
//
if (!phone.value) {
uni.showToast({
title: "请输入手机号码",
icon: "none",
});
return;
}
//
console.log("登录:", phone.value);
}
}
function goToLogin() {
//
uni.navigateTo({
url: "/pages/start/login/login",
});
}
function onPhoneInput(e) {
//
const value = e.detail.value;
// 使 isNaN
if (isNaN(value)) {
phone.value = "";
} else {
phone.value = value;
}
}
function sendCode() {
//
if (isCodeBtnDisabled.value) return;
//
isCodeBtnDisabled.value = true;
codeBtnText.value = "重新发送";
let time = 6;
const timer = setInterval(() => {
time--;
codeBtnText.value = "重新发送 " + time + "s";
if (time <= 0) {
clearInterval(timer);
codeBtnText.value = "重新发送";
//
isCodeBtnDisabled.value = false;
}
}, 1000);
return;
}
function openAgreement() {
@ -130,59 +306,17 @@ function openAgreement() {
function openPrivacy() {
//
console.log("打开隐私政策");
uni.navigateTo({
uni.navigateTo({
url: "/pages/start/privacy/privacy",
});
}
function register() {
//
if (switchType.value === "Email" && !email.value) {
uni.showToast({
title: "请输入邮箱地址",
icon: "none",
});
return;
}
if (switchType.value === "Phone" && !phone.value) {
uni.showToast({
title: "请输入手机号",
icon: "none",
});
return;
}
if (!agreed.value) {
uni.showToast({
title: "请同意用户协议和隐私政策",
icon: "none",
});
return;
}
//
if (switchType.value === "Email") {
console.log("邮箱注册:", email.value);
} else {
console.log("手机号注册:", phone.value);
}
}
function loginWithApple() {
// Apple
console.log("通过Apple登录");
}
function loginWithGoogle() {
// Google
console.log("通过Google登录");
}
function goToLogin() {
//
uni.navigateTo({
url: "/pages/start/login/login",
});
function changeCheckbox() {
agreed.value = !agreed.value;
checkboxUrl.value = agreed.value
? "../../../static/icons/Check-one-true.png"
: "../../../static/icons/Check-one-false.png";
}
</script>
@ -192,7 +326,7 @@ function goToLogin() {
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 40rpx;
padding: 0 70rpx;
height: 100vh;
background-color: #ffffff;
}
@ -222,6 +356,13 @@ function goToLogin() {
justify-content: flex-end;
}
.icons {
margin: 20rpx;
width: 40rpx;
height: 40rpx;
/* margin-right: 10rpx; */
}
.back-btn,
.headphone-btn {
font-size: 36rpx;
@ -242,9 +383,10 @@ function goToLogin() {
font-weight: bold;
color: #333333;
margin-bottom: 60rpx;
text-align: left;
align-self: flex-start;
/* text-align: left; */
/* align-self: flex-start; */
}
.switch-container {
display: flex;
margin-bottom: 40rpx;
@ -259,12 +401,13 @@ function goToLogin() {
}
.switch-item::after {
content: '';
content: "";
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60%; /* 控制边框宽度 */
width: 60%;
/* 控制边框宽度 */
height: 2rpx;
background-color: transparent;
}
@ -275,47 +418,165 @@ function goToLogin() {
}
.switch-item.active::after {
content: '';
content: "";
position: absolute;
top: 60rpx;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 30%; /* 控制边框宽度 */
width: 30%;
/* 控制边框宽度 */
height: 7rpx;
background-color: #333333;
}
.input-container {
width: 100%;
margin-bottom: 40rpx;
}
/* 添加图标输入框样式 */
.input-with-icon {
display: flex;
align-items: center;
width: 100%;
height: 80rpx;
border-bottom: 2rpx solid #e5e5e5;
margin-bottom: 20rpx;
}
.input-icon {
width: 40rpx;
height: 40rpx;
margin: 0 20rpx;
}
.input-field {
width: 90%;
flex: 1;
height: 80rpx;
border-radius: 20rpx;
border: 2rpx solid #e5e5e5;
padding: 0 30rpx;
padding: 15rpx 0;
font-size: 28rpx;
color: #333333;
border: none;
background-color: transparent;
}
.phone-input-container {
display: flex;
align-items: center;
width: 95.8%;
height: 80rpx;
/* border-radius: 20rpx; */
/* border: 2rpx solid #e5e5e5; */
/* background-color: #f5f5f5; */
padding: 0 10rpx;
border-bottom: 2rpx solid #e5e5e5;
margin-bottom: 20rpx;
}
.country-code-selector {
display: flex;
align-items: center;
padding: 0 10rpx;
padding-bottom: 1rpx;
height: 100%;
/* border-right: 2rpx solid #e5e5e5; */
/* background-color: #f5f5f5; */
border-radius: 20rpx 0 0 20rpx;
}
.country-code {
font-size: 28rpx;
color: #333333;
background-color: #f5f5f5;
margin-right: 10rpx;
}
.country-flag-img {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
.arrow-down {
font-size: 20rpx;
color: #999999;
}
.phone-input {
flex: 1;
width: auto;
height: 100%;
border: none;
background-color: transparent;
padding: 0 0rpx;
}
.send-code-btn {
width: 200rpx;
height: 60rpx;
display: inline-flex;
padding: 0rpx 10rpx;
justify-content: center;
align-items: center;
gap: 10px;
border-radius: 4px;
background: #000;
}
.send-code-btn-email {
width: 200rpx;
height: 60rpx;
display: inline-flex;
padding: 0rpx 10rpx;
justify-content: center;
align-items: center;
gap: 10px;
border-radius: 4px;
background: #000;
margin-right: 15rpx;
}
.send-code-btn-disabled {
background: #e6e6e6;
/* 禁用状态下的灰色背景 */
}
.send-code-btn-disabled-text {
color: #999999 !important;
}
.send-code-text {
color: #fff;
font-size: 28rpx;
}
.agreement-container-one {
display: flex;
align-items: center;
align-self: flex-start;
margin-bottom: 80rpx;
}
.agreement-container {
/* display: flex; */
display: flex;
align-items: center;
margin-bottom: 40rpx;
margin-top: -75.5rpx;
align-self: flex-start;
}
.checkbox {
width: 10rpx;
height: 10rpx;
margin-right: 30rpx;
width: 30rpx;
height: 30rpx;
margin-left: 20rpx;
/* flex: content; */
}
.agreement-text-one {
font-size: 22rpx;
color: #666666;
text-align: center;
margin-left: 10rpx;
}
.agreement-text {
margin-left: 20rpx;
font-size: 24rpx;
@ -331,7 +592,7 @@ function goToLogin() {
.register-btn {
width: 100%;
height: 80rpx;
background-color: #333333;
background-color: #000000;
color: white;
font-size: 32rpx;
font-weight: bold;
@ -340,25 +601,29 @@ function goToLogin() {
}
.or-text {
flex-direction: column;
font-size: 24rpx;
color: #999999;
margin-bottom: 40rpx;
margin-bottom: 100rpx;
}
.third-party-login {
width: 100%;
margin-bottom: 60rpx;
}
.third-party-text {
color: #ffffff;
font-weight: bold;
white-space: pre;
}
.third-party-btn {
width: 100%;
height: 80rpx;
background-color: white;
border: 2rpx solid rgb(249, 249, 249);
border-radius: 20rpx;
background-color: rgb(0, 0, 0);
border: 2rpx solid #e5e5e5;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
@ -367,15 +632,10 @@ function goToLogin() {
color: #333333;
}
.google-icon,
.apple-icon {
width: 30rpx;
height: 30rpx;
margin-right: 20rpx;
}
.google-icon {
width: 30rpx;
height: 30rpx;
width: 60rpx;
height: 60rpx;
margin-right: 20rpx;
}
@ -396,4 +656,9 @@ function goToLogin() {
margin-left: 10rpx;
text-decoration: underline;
}
.static-footer {
position: fixed;
bottom: 0;
}
</style>

134
pages/start/components/login-prompt/login-prompt.vue

@ -1,134 +0,0 @@
<template>
<view class="login-prompt" v-if="showPrompt">
<view class="mask" :class="{ 'mask-show': showAnimation }" @click="hide"></view>
<view class="prompt-content" :class="{ 'slide-up': showAnimation }">
<text class="prompt-title">登录以获得更好的体验</text>
<button class="login-btn" @click="goLogin">登录(推荐)</button>
<button class="visitor-btn" @click="continueAsVisitor">以访客身份继续</button>
</view>
</view>
</template>
<script setup>
import { ref, nextTick } from 'vue';
//
const showPrompt = ref(false);
const showAnimation = ref(false);
//
const show = () => {
showPrompt.value = true;
//
nextTick(() => {
setTimeout(() => {
showAnimation.value = true;
}, 10);
});
};
//
const hide = () => {
showAnimation.value = false;
//
setTimeout(() => {
showPrompt.value = false;
}, 300);
};
//
const goLogin = () => {
uni.navigateTo({
url: '/pages/login/login'
});
hide();
};
// 访
const continueAsVisitor = () => {
// 访
uni.setStorageSync('visitorMode', true);
hide();
};
// 使
defineExpose({
show,
hide
});
</script>
<style scoped>
.login-prompt {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
}
.mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.8);
opacity: 0;
transition: opacity 0.3s ease;
}
.mask.mask-show {
opacity: 1;
}
.prompt-content {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 300rpx;
border-radius: 20rpx 20rpx 0 0;
background-color: white;
padding: 20rpx 30rpx;
transform: translateY(100%);
transition: transform 0.3s ease;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.3);
}
.prompt-content.slide-up {
transform: translateY(0);
}
.prompt-title {
display: block;
text-align: left;
font-size: 28rpx;
font-weight: 700;
color: #333;
margin-top: 10rpx;
margin-bottom: 30rpx;
}
.login-btn {
width: 100%;
height: 80rpx;
background-color:rgb(35, 84, 230);
color: white;
font-size: 32rpx;
line-height: 80rpx;
border-radius: 40rpx;
margin-bottom: 20rpx;
}
.visitor-btn {
width: 100%;
height: 80rpx;
background-color: #f5f5f5;
color: #333;
font-size: 32rpx;
line-height: 80rpx;
border-radius: 40rpx;
}
</style>

1341
pages/start/login/list.js
File diff suppressed because it is too large
View File

580
pages/start/login/login.vue

@ -1,113 +1,283 @@
<template>
<view
class="login-registration-container"
>
<view class="login-registration-container">
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
<view class="nav-left">
<view
class="custom-navbar"
:style="{ paddingTop: safeAreaInsets?.top + 'px' }"
>
<!-- <view class="nav-left">
<text class="back-btn" @click="goToIndex"></text>
</view>
</view> -->
<view class="nav-right">
<text class="headphone-btn">🎧</text>
<image
class="icons"
src="../../../static/icons/Headset.png"
alt="联系客服"
/>
<image
class="icons"
@click="goToIndex"
src="../../../static/icons/Frame.png"
alt="返回首页"
/>
</view>
</view>
<!-- Logo -->
<image class="logo" src="/static/logo.png" mode="aspectFit"></image>
<!-- <image class="logo" src="/static/logo.png" mode="aspectFit"></image> -->
<!-- 欢迎语 -->
<text class="welcome-text">登录</text>
<text class="welcome-text">登录DeepChart</text>
<!-- 邮箱/手机号切换 -->
<view class="switch-container">
<text
class="switch-item"
:class="{ active: switchType === 'Email' }"
@click="switchEmail"
>邮箱/用户名</text
:class="{ active: switchType === 'User' }"
@click="switchUser"
>用户名</text
>
<text
class="switch-item"
:class="{ active: switchType === 'Phone' }"
@click="switchPhone"
>手机号</text
><text
class="switch-item"
:class="{ active: switchType === 'Email' }"
@click="switchEmail"
>邮箱</text
>
</view>
<!-- 输入框 -->
<view class="input-container">
<input
v-if="switchType === 'Email'"
class="input-field"
type="text"
placeholder="输入邮箱/用户名"
v-model="email"
/>
<input
v-else
class="input-field"
type="text"
placeholder="输入手机号"
v-model="phone"
/>
<view v-if="switchType === 'User'">
<!-- 修改邮箱输入框容器将图标包含在内 -->
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/People-safe.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入DeepChart ID"
v-model="email"
/>
</view>
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Unlock.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入密码"
v-model="password"
/>
</view>
</view>
<view v-if="switchType === 'Email'">
<!-- 修改邮箱输入框容器将图标包含在内 -->
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Mail.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入邮箱"
v-model="email"
/>
<view>
<button
class="send-code-btn-email"
:disabled="isCodeBtnDisabled"
:class="{ 'send-code-btn-disabled': isCodeBtnDisabled }"
@click="sendCode"
>
<text
class="send-code-text"
:class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }"
>{{ codeBtnText }}</text
>
</button>
</view>
</view>
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Text-recognition.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入验证码"
v-model="password"
/>
</view>
</view>
<view v-if="switchType === 'Phone'" class="phone-input-container">
<view class="country-code-selector" @click="showCountryPicker">
<image
class="country-flag-img"
src="../../../static/icons/Iphone.png"
mode="aspectFit"
></image>
<text class="country-code">{{ selectedCountry.code }}</text>
<!-- <text class="arrow-down"></text> -->
</view>
<input
class="input-field phone-input"
type="number"
placeholder="输入手机号"
v-model="phone"
@input="onPhoneInput"
/>
<view>
<button
class="send-code-btn"
:disabled="isCodeBtnDisabled"
:class="{ 'send-code-btn-disabled': isCodeBtnDisabled }"
@click="sendCode"
>
<text
class="send-code-text"
:class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }"
>{{ codeBtnText }}</text
>
</button>
</view>
</view>
<view v-if="switchType === 'Phone'" class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Text-recognition.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入验证码"
v-model="password"
/>
</view>
</view>
<!-- 用户协议 -->
<!-- <view class="agreement-container">
<checkbox class="checkbox" v-model="agreed" />
<text class="agreement-text"
>接受 <text class="link">用户协议</text>
<text class="link">隐私政策</text></text
<view @click="changeCheckbox" class="agreement-container-one">
<image class="checkbox" :src="checkboxUrl"></image>
<text class="agreement-text-one"
>接受 <text class="link" @click="openAgreement">用户协议</text>
<text class="link" @click="openPrivacy">隐私政策</text></text
>
</view> -->
</view>
<view v-if="switchType === 'User'" class="agreement-container">
<text class="agreement-text"
><text @click="recoverPassword">忘记ID/密码</text>
</text>
</view>
<view v-else class="agreement-container">
<text class="agreement-text">
<!-- 添加占位元素防止页面变形 -->
<text style="visibility: hidden">占位符</text>
</text>
</view>
<!-- 注册按钮 -->
<button class="register-btn" @click="register">下一步</button>
<button class="register-btn" @click="register">登录</button>
<!-- 或者 -->
<text class="or-text">或者</text>
<text class="or-text" @click="goToRegistration"
>如果您还没有账号点击注册 >
</text>
<!-- 第三方登录 -->
<view class="third-party-login">
<view class="third-party-btn" @click="loginWithApple">
<image
class="apple-icon"
src="/static/apple-icon.png"
src="../../../static/icons/appleIcons.png"
mode="aspectFit"
></image>
<text class="third-party-text">通过 Apple 继续</text>
<text class="third-party-text">通过 Apple 登录 </text>
</view>
<view class="third-party-btn" @click="loginWithGoogle">
<image
class="google-icon"
src="/static/google-icon.png"
src="../../../static/icons/GoogleIcons.png"
mode="aspectFit"
></image>
<text class="third-party-text">通过 Google 继续</text>
<text class="third-party-text">通过 Google 登录</text>
</view>
</view>
<!-- 已有账号 -->
<view class="existing-account">
<text class="account-text">未注册账号</text>
<text class="login-link" @click="goToRegistration">注册</text>
</view>
<footerBar class="static-footer" :type="type"></footerBar>
</view>
</template>
<script setup>
import { ref } from "vue";
//
import countryList from "./list.js";
import footerBar from '../../../components/footerBar-cn'
const type = ref('member')
const email = ref("");
const password = ref("");
const phone = ref("");
const agreed = ref(false);
const switchType = ref("Email"); //
const { safeAreaInsets } = uni.getSystemInfoSync();
const codeBtnText = ref("获取验证码");
const isCodeBtnDisabled = ref(false); //
const checkboxUrl = ref("../../../static/icons/Check-one-false.png");
// 使list.js
const countries = ref(
countryList.list.map((item) => ({
name: item.name,
code: `+${item.tel}`,
flag: item.flag,
short: item.short,
en: item.en,
}))
);
//
const selectedCountry = ref(
countries.value.find((country) => country.short === "CN") ||
countries.value[0]
);
//
function showCountryPicker() {
uni.showActionSheet({
itemList: countries.value.map(
(country) => `${country.name} ${country.code}`
),
success: function (res) {
selectedCountry.value = countries.value[res.tapIndex];
},
});
}
function goToIndex() {
//
uni.navigateTo({ url: '/pages/start/index/index' });
uni.navigateTo({
url: "/pages/start/index/index",
});
}
function switchUser() {
//
switchType.value = "User";
}
function switchEmail() {
@ -121,33 +291,77 @@ function switchPhone() {
}
function register() {
//
if (!email.value) {
uni.showToast({
title: "请输入邮箱地址",
icon: "none",
});
return;
if (switchType.value === "Email") {
//
if (!email.value) {
uni.showToast({
title: "请输入邮箱地址",
icon: "none",
});
return;
}
//
console.log("登录:", email.value);
}
if (!agreed.value) {
uni.showToast({
title: "请同意用户协议和隐私政策",
icon: "none",
});
return;
if (switchType.value === "Phone") {
//
if (!phone.value) {
uni.showToast({
title: "请输入手机号码",
icon: "none",
});
return;
}
//
console.log("登录:", phone.value);
}
//
console.log("注册:", email.value);
}
function loginWithApple() {
// Apple
console.log("通过Apple登录");
uni.login({
provider: "apple",
success: function (loginRes) {
//
uni.getUserInfo({
provider: "apple",
success: function (info) {
console.log(info);
},
});
},
fail: function (err) {
//
// err.code`(code)`
console.log(err);
},
});
}
function loginWithGoogle() {
// Google
console.log("通过Google登录");
uni.login({
provider: "google",
success: function (loginRes) {
//
uni.getUserInfo({
provider: "google",
success: function (info) {
console.log(info);
},
});
},
fail: function (err) {
//
// err.code
console.log(err);
},
});
}
function goToRegistration() {
@ -156,6 +370,67 @@ function goToRegistration() {
url: "/pages/start/Registration/Registration",
});
}
function onPhoneInput(e) {
//
const value = e.detail.value;
// 使 isNaN
if (isNaN(value)) {
phone.value = "";
} else {
phone.value = value;
}
}
function sendCode() {
//
if (isCodeBtnDisabled.value) return;
//
isCodeBtnDisabled.value = true;
codeBtnText.value = "重新发送";
let time = 6;
const timer = setInterval(() => {
time--;
codeBtnText.value = "重新发送 " + time + "s";
if (time <= 0) {
clearInterval(timer);
codeBtnText.value = "重新发送";
//
isCodeBtnDisabled.value = false;
}
}, 1000);
return;
}
function openAgreement() {
//
console.log("打开用户协议");
uni.navigateTo({
url: "/pages/start/agreement/agreement",
});
}
function openPrivacy() {
//
console.log("打开隐私政策");
uni.navigateTo({
url: "/pages/start/privacy/privacy",
});
}
function recoverPassword() {
//
console.log("忘记密码");
}
function changeCheckbox() {
agreed.value = !agreed.value;
checkboxUrl.value = agreed.value
? "../../../static/icons/Check-one-true.png"
: "../../../static/icons/Check-one-false.png";
}
</script>
<style scoped>
@ -164,7 +439,7 @@ function goToRegistration() {
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 40rpx;
padding: 0 70rpx;
height: 100vh;
background-color: #ffffff;
}
@ -194,6 +469,13 @@ function goToRegistration() {
justify-content: flex-end;
}
.icons {
margin: 20rpx;
width: 40rpx;
height: 40rpx;
/* margin-right: 10rpx; */
}
.back-btn,
.headphone-btn {
font-size: 36rpx;
@ -214,9 +496,10 @@ function goToRegistration() {
font-weight: bold;
color: #333333;
margin-bottom: 60rpx;
text-align: left;
align-self: flex-start;
/* text-align: left; */
/* align-self: flex-start; */
}
.switch-container {
display: flex;
margin-bottom: 40rpx;
@ -236,7 +519,8 @@ function goToRegistration() {
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60%; /* 控制边框宽度 */
width: 60%;
/* 控制边框宽度 */
height: 2rpx;
background-color: transparent;
}
@ -253,45 +537,164 @@ function goToRegistration() {
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 30%; /* 控制边框宽度 */
width: 30%;
/* 控制边框宽度 */
height: 7rpx;
background-color: #333333;
}
.input-container {
width: 100%;
margin-bottom: 40rpx;
}
/* 添加图标输入框样式 */
.input-with-icon {
display: flex;
align-items: center;
width: 100%;
height: 80rpx;
border-bottom: 2rpx solid #e5e5e5;
margin-bottom: 20rpx;
}
.input-icon {
width: 40rpx;
height: 40rpx;
margin: 0 20rpx;
}
.input-field {
width: 90%;
flex: 1;
height: 80rpx;
border-radius: 20rpx;
border: 2rpx solid #e5e5e5;
padding: 0 30rpx;
padding: 15rpx 0;
font-size: 28rpx;
color: #333333;
border: none;
background-color: transparent;
}
.phone-input-container {
display: flex;
align-items: center;
width: 95.8%;
height: 80rpx;
/* border-radius: 20rpx; */
/* border: 2rpx solid #e5e5e5; */
/* background-color: #f5f5f5; */
padding: 0 10rpx;
border-bottom: 2rpx solid #e5e5e5;
margin-bottom: 20rpx;
}
.country-code-selector {
display: flex;
align-items: center;
padding: 0 10rpx;
padding-bottom: 1rpx;
height: 100%;
/* border-right: 2rpx solid #e5e5e5; */
/* background-color: #f5f5f5; */
border-radius: 20rpx 0 0 20rpx;
}
.country-code {
font-size: 28rpx;
color: #333333;
background-color: #f5f5f5;
margin-right: 10rpx;
}
.country-flag-img {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
.arrow-down {
font-size: 20rpx;
color: #999999;
}
.phone-input {
flex: 1;
width: auto;
height: 100%;
border: none;
background-color: transparent;
padding: 0 0rpx;
}
.send-code-btn {
width: 200rpx;
height: 60rpx;
display: inline-flex;
padding: 0rpx 10rpx;
justify-content: center;
align-items: center;
gap: 10px;
border-radius: 4px;
background: #000;
}
.send-code-btn-email {
width: 200rpx;
height: 60rpx;
display: inline-flex;
padding: 0rpx 10rpx;
justify-content: center;
align-items: center;
gap: 10px;
border-radius: 4px;
background: #000;
margin-right: 15rpx;
}
.send-code-btn-disabled {
background: #e6e6e6;
/* 禁用状态下的灰色背景 */
}
.send-code-btn-disabled-text {
color: #999999 !important;
}
.send-code-text {
color: #fff;
font-size: 28rpx;
}
.agreement-container-one {
display: flex;
align-items: center;
align-self: flex-start;
margin-bottom: 80rpx;
}
.agreement-container {
/* display: flex; */
display: flex;
align-items: center;
margin-bottom: 40rpx;
margin-bottom: 30rpx;
margin-top: -60rpx;
align-self: flex-start;
}
.checkbox {
width: 10rpx;
height: 10rpx;
margin-right: 30rpx;
width: 30rpx;
height: 30rpx;
margin-left: 20rpx;
/* flex: content; */
}
.agreement-text-one {
font-size: 22rpx;
color: #666666;
text-align: center;
margin-left: 10rpx;
}
.agreement-text {
margin-left: 20rpx;
font-size: 24rpx;
color: #666666;
white-space: nowrap;
}
.link {
@ -303,7 +706,7 @@ function goToRegistration() {
.register-btn {
width: 100%;
height: 80rpx;
background-color: #333333;
background-color: #000000;
color: white;
font-size: 32rpx;
font-weight: bold;
@ -321,14 +724,17 @@ function goToRegistration() {
width: 100%;
margin-bottom: 60rpx;
}
.third-party-text {
color: #ffffff;
font-weight: bold;
white-space: pre;
}
.third-party-btn {
width: 100%;
height: 80rpx;
background-color: white;
background-color: rgb(0, 0, 0);
border: 2rpx solid #e5e5e5;
border-radius: 40rpx;
display: flex;
@ -339,15 +745,10 @@ function goToRegistration() {
color: #333333;
}
.google-icon,
.apple-icon {
width: 30rpx;
height: 30rpx;
margin-right: 20rpx;
}
.google-icon {
width: 30rpx;
height: 30rpx;
width: 60rpx;
height: 60rpx;
margin-right: 20rpx;
}
@ -368,4 +769,9 @@ function goToRegistration() {
margin-left: 10rpx;
text-decoration: underline;
}
.static-footer {
position: fixed;
bottom: 0;
}
</style>

BIN
static/flag/ad.png

After

Width: 100  |  Height: 70  |  Size: 2.1 KiB

BIN
static/flag/ae.png

After

Width: 100  |  Height: 50  |  Size: 170 B

BIN
static/flag/af.png

After

Width: 100  |  Height: 67  |  Size: 2.6 KiB

BIN
static/flag/ag.png

After

Width: 100  |  Height: 67  |  Size: 2.0 KiB

BIN
static/flag/ai.png

After

Width: 100  |  Height: 50  |  Size: 1.3 KiB

BIN
static/flag/al.png

After

Width: 100  |  Height: 71  |  Size: 1.7 KiB

BIN
static/flag/am.png

After

Width: 100  |  Height: 50  |  Size: 122 B

BIN
static/flag/an.png

After

Width: 100  |  Height: 67  |  Size: 610 B

BIN
static/flag/ao.png

After

Width: 100  |  Height: 67  |  Size: 1.4 KiB

BIN
static/flag/aq.png

After

Width: 100  |  Height: 100  |  Size: 6.0 KiB

BIN
static/flag/ar.png

After

Width: 100  |  Height: 63  |  Size: 991 B

BIN
static/flag/as.png

After

Width: 100  |  Height: 50  |  Size: 2.0 KiB

BIN
static/flag/at.png

After

Width: 100  |  Height: 67  |  Size: 133 B

BIN
static/flag/au.png

After

Width: 100  |  Height: 50  |  Size: 1.1 KiB

BIN
static/flag/aw.png

After

Width: 100  |  Height: 67  |  Size: 658 B

BIN
static/flag/ax.png

After

Width: 100  |  Height: 65  |  Size: 279 B

BIN
static/flag/az.png

After

Width: 100  |  Height: 50  |  Size: 451 B

BIN
static/flag/ba.png

After

Width: 100  |  Height: 50  |  Size: 741 B

BIN
static/flag/bb.png

After

Width: 100  |  Height: 67  |  Size: 791 B

BIN
static/flag/bd.png

After

Width: 100  |  Height: 60  |  Size: 367 B

BIN
static/flag/be.png

After

Width: 100  |  Height: 87  |  Size: 168 B

BIN
static/flag/bf.png

After

Width: 100  |  Height: 67  |  Size: 560 B

BIN
static/flag/bg.png

After

Width: 100  |  Height: 60  |  Size: 106 B

BIN
static/flag/bh.png

After

Width: 100  |  Height: 60  |  Size: 303 B

BIN
static/flag/bi.png

After

Width: 100  |  Height: 60  |  Size: 1.3 KiB

BIN
static/flag/bj.png

After

Width: 100  |  Height: 67  |  Size: 169 B

BIN
static/flag/bl.png

After

Width: 100  |  Height: 67  |  Size: 6.2 KiB

BIN
static/flag/bm.png

After

Width: 100  |  Height: 50  |  Size: 2.0 KiB

BIN
static/flag/bn.png

After

Width: 100  |  Height: 50  |  Size: 2.3 KiB

BIN
static/flag/bo.png

After

Width: 100  |  Height: 68  |  Size: 1.4 KiB

BIN
static/flag/bq.png

After

Width: 100  |  Height: 67  |  Size: 159 B

BIN
static/flag/br.png

After

Width: 100  |  Height: 70  |  Size: 2.0 KiB

BIN
static/flag/bs.png

After

Width: 100  |  Height: 50  |  Size: 553 B

BIN
static/flag/bt.png

After

Width: 100  |  Height: 67  |  Size: 4.0 KiB

BIN
static/flag/bv.png

After

Width: 100  |  Height: 73  |  Size: 260 B

BIN
static/flag/bw.png

After

Width: 100  |  Height: 67  |  Size: 172 B

BIN
static/flag/by.png

After

Width: 100  |  Height: 50  |  Size: 1.4 KiB

BIN
static/flag/bz.png

After

Width: 100  |  Height: 67  |  Size: 5.6 KiB

BIN
static/flag/ca.png

After

Width: 100  |  Height: 50  |  Size: 765 B

BIN
static/flag/cc.png

After

Width: 100  |  Height: 50  |  Size: 1.3 KiB

BIN
static/flag/cd.png

After

Width: 100  |  Height: 75  |  Size: 796 B

BIN
static/flag/cf.png

After

Width: 100  |  Height: 67  |  Size: 554 B

BIN
static/flag/cg.png

After

Width: 100  |  Height: 67  |  Size: 354 B

BIN
static/flag/ch.png

After

Width: 100  |  Height: 100  |  Size: 172 B

BIN
static/flag/ci.png

After

Width: 100  |  Height: 67  |  Size: 165 B

BIN
static/flag/ck.png

After

Width: 100  |  Height: 50  |  Size: 1.9 KiB

BIN
static/flag/cl.png

After

Width: 100  |  Height: 67  |  Size: 517 B

BIN
static/flag/cm.png

After

Width: 100  |  Height: 67  |  Size: 482 B

BIN
static/flag/cn.png

After

Width: 100  |  Height: 67  |  Size: 763 B

BIN
static/flag/co.png

After

Width: 100  |  Height: 67  |  Size: 158 B

BIN
static/flag/cr.png

After

Width: 100  |  Height: 60  |  Size: 109 B

BIN
static/flag/cu.png

After

Width: 100  |  Height: 50  |  Size: 793 B

BIN
static/flag/cv.png

After

Width: 100  |  Height: 59  |  Size: 1.0 KiB

BIN
static/flag/cw.png

After

Width: 100  |  Height: 67  |  Size: 633 B

BIN
static/flag/cx.png

After

Width: 100  |  Height: 50  |  Size: 1.5 KiB

BIN
static/flag/cy.png

After

Width: 100  |  Height: 67  |  Size: 1.6 KiB

BIN
static/flag/cz.png

After

Width: 100  |  Height: 67  |  Size: 394 B

BIN
static/flag/de.png

After

Width: 100  |  Height: 60  |  Size: 106 B

BIN
static/flag/dj.png

After

Width: 100  |  Height: 67  |  Size: 1.1 KiB

BIN
static/flag/dk.png

After

Width: 100  |  Height: 76  |  Size: 200 B

BIN
static/flag/dm.png

After

Width: 100  |  Height: 50  |  Size: 1.2 KiB

BIN
static/flag/do.png

After

Width: 100  |  Height: 67  |  Size: 700 B

BIN
static/flag/dz.png

After

Width: 100  |  Height: 67  |  Size: 891 B

BIN
static/flag/ec.png

After

Width: 100  |  Height: 67  |  Size: 2.5 KiB

BIN
static/flag/ee.png

After

Width: 100  |  Height: 64  |  Size: 158 B

BIN
static/flag/eg.png

After

Width: 100  |  Height: 67  |  Size: 1.1 KiB

BIN
static/flag/eh.png

After

Width: 100  |  Height: 50  |  Size: 656 B

BIN
static/flag/er.png

After

Width: 100  |  Height: 50  |  Size: 1.4 KiB

BIN
static/flag/es.png

After

Width: 100  |  Height: 67  |  Size: 1.7 KiB

BIN
static/flag/et.png

After

Width: 100  |  Height: 50  |  Size: 1.3 KiB

BIN
static/flag/eu.png

After

Width: 100  |  Height: 67  |  Size: 1.4 KiB

BIN
static/flag/fi.png

After

Width: 100  |  Height: 61  |  Size: 186 B

BIN
static/flag/fj.png

After

Width: 100  |  Height: 50  |  Size: 1.8 KiB

BIN
static/flag/fk.png

After

Width: 100  |  Height: 50  |  Size: 2.2 KiB

BIN
static/flag/fm.png

After

Width: 100  |  Height: 53  |  Size: 595 B

BIN
static/flag/fo.png

After

Width: 100  |  Height: 73  |  Size: 260 B

BIN
static/flag/fr.png

After

Width: 100  |  Height: 67  |  Size: 165 B

BIN
static/flag/ga.png

After

Width: 100  |  Height: 75  |  Size: 109 B

BIN
static/flag/gb-eng.png

After

Width: 100  |  Height: 60  |  Size: 99 B

BIN
static/flag/gb-nir.png

After

Width: 100  |  Height: 50  |  Size: 545 B

BIN
static/flag/gb-sct.png

After

Width: 100  |  Height: 60  |  Size: 468 B

BIN
static/flag/gb-wls.png

After

Width: 100  |  Height: 60  |  Size: 5.6 KiB

BIN
static/flag/gb.png

After

Width: 100  |  Height: 50  |  Size: 545 B

BIN
static/flag/gd.png

After

Width: 100  |  Height: 60  |  Size: 1.5 KiB

BIN
static/flag/ge.png

After

Width: 100  |  Height: 67  |  Size: 750 B

BIN
static/flag/gf.png

After

Width: 100  |  Height: 67  |  Size: 738 B

BIN
static/flag/gg.png

After

Width: 100  |  Height: 67  |  Size: 306 B

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save