- 添加uni-ui组件库依赖 - 实现微信静默登录功能 - 创建资产、策略、我的等核心页面 - 添加策略组合配置功能 - 实现持仓详情展示 - 完善用户信息展示 - 添加全局样式和工具类 - 配置小程序项目设置
459 lines
10 KiB
Vue
459 lines
10 KiB
Vue
<template>
|
|
<view class="page-container">
|
|
|
|
<view class="section-card">
|
|
<view class="card-header">
|
|
<view class="header-icon bg-emerald-100">
|
|
<uni-icons type="settings" size="18" color="#064E3B"></uni-icons>
|
|
</view>
|
|
<text class="header-title">基础设置</text>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="label">组合名称</text>
|
|
<input class="input-box" type="text" v-model="form.name" placeholder="给你的组合起个名字 (如: 养老定投)"
|
|
placeholder-class="placeholder-style" />
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="label">选择策略模板</text>
|
|
<picker @change="onStrategyChange" :value="strategyIndex" :range="strategies" range-key="name">
|
|
<view class="picker-box">
|
|
<view class="flex-row items-center gap-2" v-if="selectedStrategy">
|
|
<view class="strategy-dot" :style="{ backgroundColor: selectedStrategy.color }"></view>
|
|
<text class="picker-text">{{ selectedStrategy.name }}</text>
|
|
</view>
|
|
<text class="picker-placeholder" v-else>点击选择策略逻辑</text>
|
|
<uni-icons type="bottom" size="14" color="#9CA3AF"></uni-icons>
|
|
</view>
|
|
</picker>
|
|
<text class="helper-text" v-if="selectedStrategy">{{ selectedStrategy.desc }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="section-card">
|
|
<view class="card-header justify-between">
|
|
<view class="flex-row items-center gap-2">
|
|
<view class="header-icon bg-blue-100">
|
|
<uni-icons type="wallet" size="18" color="#1D4ED8"></uni-icons>
|
|
</view>
|
|
<text class="header-title">初始化持仓</text>
|
|
</view>
|
|
<view class="add-btn" @click="addStockRow">
|
|
<uni-icons type="plus" size="14" color="#064E3B"></uni-icons>
|
|
<text class="add-text">添加标的</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="stock-list">
|
|
<view class="stock-item" v-for="(item, index) in form.stocks" :key="index">
|
|
|
|
<view class="item-header">
|
|
<text class="item-index">标的 #{{ index + 1 }}</text>
|
|
<uni-icons type="trash" size="18" color="#EF4444" @click="removeStockRow(index)"
|
|
v-if="form.stocks.length > 1"></uni-icons>
|
|
</view>
|
|
|
|
<view class="item-grid">
|
|
<view class="grid-col">
|
|
<text class="sub-label">标的名称/代码</text>
|
|
<input class="mini-input" v-model="item.name" placeholder="如 TMF" />
|
|
</view>
|
|
<view class="grid-col">
|
|
<text class="sub-label">买入均价</text>
|
|
<input class="mini-input" type="digit" v-model="item.price" placeholder="0.00" />
|
|
</view>
|
|
<view class="grid-col">
|
|
<text class="sub-label">持有数量</text>
|
|
<input class="mini-input" type="number" v-model="item.amount" placeholder="0" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="date-row">
|
|
<text class="sub-label">建仓日期</text>
|
|
<picker mode="date" :value="item.date" @change="(e) => onDateChange(e, index)">
|
|
<view class="date-picker-display">
|
|
<text>{{ item.date || '请选择日期' }}</text>
|
|
<uni-icons type="calendar" size="16" color="#9CA3AF"></uni-icons>
|
|
</view>
|
|
</picker>
|
|
</view>
|
|
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="footer-area">
|
|
<view class="total-summary">
|
|
<text class="summary-label">预计初始投入</text>
|
|
<text class="summary-val">¥ {{ totalInvestment }}</text>
|
|
</view>
|
|
<button class="submit-btn" @click="submitForm">创建组合</button>
|
|
</view>
|
|
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
|
|
// 策略库数据 (模拟)
|
|
const strategies = [
|
|
{ name: 'HFEA 风险平价', desc: '利用 3x 杠杆股债平衡,适合长期持有。', color: '#10B981' },
|
|
{ name: '双均线趋势跟踪', desc: '金叉买入死叉卖出,捕捉大波段。', color: '#3B82F6' },
|
|
{ name: '网格交易 (Grid)', desc: '震荡行情下的高抛低吸策略。', color: '#F59E0B' },
|
|
{ name: '手动自管', desc: '不使用自动化策略,仅做资产记账。', color: '#6B7280' }
|
|
];
|
|
|
|
const strategyIndex = ref(-1);
|
|
|
|
// 表单数据
|
|
const form = ref({
|
|
name: '',
|
|
stocks: [
|
|
{ name: '', price: '', amount: '', date: '' } // 默认有一行
|
|
]
|
|
});
|
|
|
|
// 计算属性:当前选中的策略对象
|
|
const selectedStrategy = computed(() => {
|
|
if (strategyIndex.value === -1) return null;
|
|
return strategies[strategyIndex.value];
|
|
});
|
|
|
|
// 计算属性:总投入金额
|
|
const totalInvestment = computed(() => {
|
|
let total = 0;
|
|
form.value.stocks.forEach(stock => {
|
|
const p = parseFloat(stock.price) || 0;
|
|
const a = parseFloat(stock.amount) || 0;
|
|
total += p * a;
|
|
});
|
|
// 格式化千分位
|
|
return total.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
});
|
|
|
|
// --- 方法 ---
|
|
|
|
const onStrategyChange = (e) => {
|
|
strategyIndex.value = e.detail.value;
|
|
};
|
|
|
|
const onDateChange = (e, index) => {
|
|
form.value.stocks[index].date = e.detail.value;
|
|
};
|
|
|
|
// 添加一行股票
|
|
const addStockRow = () => {
|
|
form.value.stocks.push({ name: '', price: '', amount: '', date: '' });
|
|
};
|
|
|
|
// 删除一行
|
|
const removeStockRow = (index) => {
|
|
form.value.stocks.splice(index, 1);
|
|
};
|
|
|
|
// 提交
|
|
const submitForm = () => {
|
|
// 简单校验
|
|
if (!form.value.name) return uni.showToast({ title: '请输入组合名称', icon: 'none' });
|
|
if (strategyIndex.value === -1) return uni.showToast({ title: '请选择策略', icon: 'none' });
|
|
|
|
console.log('提交的数据:', {
|
|
...form.value,
|
|
strategy: strategies[strategyIndex.value].name
|
|
});
|
|
|
|
uni.showLoading({ title: '创建中...' });
|
|
setTimeout(() => {
|
|
uni.hideLoading();
|
|
uni.showToast({ title: '创建成功' });
|
|
setTimeout(() => uni.navigateBack(), 1500);
|
|
}, 1000);
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* 通用布局 */
|
|
.page-container {
|
|
min-height: 100vh;
|
|
background-color: #F3F4F6;
|
|
padding-bottom: 200rpx;
|
|
/* 给底部按钮留空 */
|
|
}
|
|
|
|
.flex-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
|
|
.items-center {
|
|
align-items: center;
|
|
}
|
|
|
|
.justify-between {
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.gap-2 {
|
|
gap: 16rpx;
|
|
}
|
|
|
|
/* 导航栏 */
|
|
.nav-bar {
|
|
background-color: #fff;
|
|
padding: var(--status-bar-height) 32rpx 20rpx 32rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
height: 88rpx;
|
|
box-sizing: content-box;
|
|
}
|
|
|
|
.page-title {
|
|
font-size: 34rpx;
|
|
font-weight: 700;
|
|
color: #111827;
|
|
}
|
|
|
|
/* 卡片容器 */
|
|
.section-card {
|
|
background-color: #fff;
|
|
margin: 32rpx;
|
|
padding: 32rpx;
|
|
border-radius: 32rpx;
|
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
|
|
}
|
|
|
|
/* 卡片标题头 */
|
|
.card-header {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 32rpx;
|
|
}
|
|
|
|
.header-icon {
|
|
width: 60rpx;
|
|
height: 60rpx;
|
|
border-radius: 16rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 20rpx;
|
|
}
|
|
|
|
.bg-emerald-100 {
|
|
background-color: #D1FAE5;
|
|
}
|
|
|
|
.bg-blue-100 {
|
|
background-color: #DBEAFE;
|
|
}
|
|
|
|
.header-title {
|
|
font-size: 30rpx;
|
|
font-weight: 700;
|
|
color: #1F2937;
|
|
}
|
|
|
|
/* 添加按钮 */
|
|
.add-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
background-color: #ECFDF5;
|
|
padding: 8rpx 20rpx;
|
|
border-radius: 100rpx;
|
|
gap: 8rpx;
|
|
}
|
|
|
|
.add-text {
|
|
font-size: 24rpx;
|
|
font-weight: 600;
|
|
color: #064E3B;
|
|
}
|
|
|
|
/* 表单项 */
|
|
.form-item {
|
|
margin-bottom: 32rpx;
|
|
}
|
|
|
|
.form-item:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.label {
|
|
font-size: 26rpx;
|
|
font-weight: 600;
|
|
color: #374151;
|
|
margin-bottom: 16rpx;
|
|
display: block;
|
|
}
|
|
|
|
.input-box {
|
|
background-color: #F9FAFB;
|
|
border: 2rpx solid #E5E7EB;
|
|
border-radius: 20rpx;
|
|
height: 96rpx;
|
|
padding: 0 32rpx;
|
|
font-size: 28rpx;
|
|
color: #1F2937;
|
|
}
|
|
|
|
.placeholder-style {
|
|
color: #9CA3AF;
|
|
}
|
|
|
|
/* 选择器样式 */
|
|
.picker-box {
|
|
background-color: #F9FAFB;
|
|
border: 2rpx solid #E5E7EB;
|
|
border-radius: 20rpx;
|
|
height: 96rpx;
|
|
padding: 0 32rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.picker-text {
|
|
font-size: 28rpx;
|
|
color: #1F2937;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.picker-placeholder {
|
|
font-size: 28rpx;
|
|
color: #9CA3AF;
|
|
}
|
|
|
|
.strategy-dot {
|
|
width: 16rpx;
|
|
height: 16rpx;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.helper-text {
|
|
font-size: 22rpx;
|
|
color: #6B7280;
|
|
margin-top: 12rpx;
|
|
display: block;
|
|
margin-left: 8rpx;
|
|
}
|
|
|
|
/* --- 动态持仓列表 --- */
|
|
.stock-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 24rpx;
|
|
}
|
|
|
|
.stock-item {
|
|
background-color: #F9FAFB;
|
|
border: 1rpx solid #E5E7EB;
|
|
border-radius: 24rpx;
|
|
padding: 24rpx;
|
|
}
|
|
|
|
.item-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.item-index {
|
|
font-size: 24rpx;
|
|
font-weight: 700;
|
|
color: #9CA3AF;
|
|
}
|
|
|
|
/* 网格输入布局 */
|
|
.item-grid {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.grid-col {
|
|
flex: 1;
|
|
}
|
|
|
|
.sub-label {
|
|
font-size: 20rpx;
|
|
color: #6B7280;
|
|
margin-bottom: 8rpx;
|
|
display: block;
|
|
}
|
|
|
|
.mini-input {
|
|
background-color: #FFFFFF;
|
|
border: 1rpx solid #E5E7EB;
|
|
border-radius: 16rpx;
|
|
height: 72rpx;
|
|
padding: 0 20rpx;
|
|
font-size: 26rpx;
|
|
color: #1F2937;
|
|
}
|
|
|
|
/* 日期选择 */
|
|
.date-picker-display {
|
|
background-color: #FFFFFF;
|
|
border: 1rpx solid #E5E7EB;
|
|
border-radius: 16rpx;
|
|
height: 72rpx;
|
|
padding: 0 20rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
font-size: 26rpx;
|
|
color: #1F2937;
|
|
}
|
|
|
|
/* 底部固定区 */
|
|
.footer-area {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background-color: #fff;
|
|
padding: 20rpx 32rpx 50rpx 32rpx;
|
|
/* 适配 iPhone X */
|
|
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.total-summary {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0 10rpx;
|
|
}
|
|
|
|
.summary-label {
|
|
font-size: 26rpx;
|
|
color: #6B7280;
|
|
}
|
|
|
|
.summary-val {
|
|
font-size: 36rpx;
|
|
font-weight: 700;
|
|
color: #064E3B;
|
|
font-family: 'DIN Alternate';
|
|
}
|
|
|
|
.submit-btn {
|
|
background-color: #064E3B;
|
|
color: #fff;
|
|
font-weight: 700;
|
|
border-radius: 24rpx;
|
|
height: 96rpx;
|
|
line-height: 96rpx;
|
|
font-size: 30rpx;
|
|
width: 100%;
|
|
}
|
|
|
|
.submit-btn:active {
|
|
opacity: 0.9;
|
|
}
|
|
</style> |