AssetManager.UniApp/pages/strategies/strategies.vue
niannian zheng 4ea7c5c2d0 refactor(页面): 优化页面生命周期和组件逻辑
重构页面生命周期钩子,统一使用uni-app的onShow替代vue的onShow
移除重复请求检查逻辑,简化数据获取函数
在配置页面自动填充策略参数并禁用相关输入
移除不必要的添加/删除股票行功能
2026-03-02 19:16:14 +08:00

231 lines
6.5 KiB
Vue

<template>
<view class="page-container">
<view class="header-section">
<view class="status-bar-height"></view>
<view class="flex-row justify-between items-start">
<view>
<text class="title-lg">策略实验室</text>
<text class="subtitle">选择或创建预设规则模型</text>
</view>
<view class="add-btn-box" @click="goToAdd">
<uni-icons type="plus" size="24" color="#064E3B"></uni-icons>
</view>
</view>
</view>
<view class="strategy-list">
<view
class="strategy-card"
v-for="(item, index) in strategies"
:key="index"
>
<view class="watermark" v-if="index === 0">
<uni-icons type="bars" size="120" color="#000" style="opacity:0.03"></uni-icons>
</view>
<view class="flex-row items-center gap-3 mb-4 card-header-row">
<view class="icon-box" :class="item.bgClass">
<text class="icon-text">{{ item.iconChar }}</text>
</view>
<view class="flex-col">
<text class="card-head">{{ item.title }}</text>
<text class="card-tag" :class="item.tagClass">{{ item.tag }}</text>
</view>
</view>
<text class="desc-text">{{ item.desc }}</text>
<view class="footer-row">
<view class="flex-row gap-2">
<view class="tag-gray" v-for="(tag, tIndex) in item.tags" :key="tIndex">
<text class="tag-text">{{ tag }}</text>
</view>
</view>
<button
:class="item.btnClass"
@click="handleAction(item)"
>
{{ item.btnText }}
</button>
</view>
</view>
<view style="height: 100rpx;"></view>
</view>
</view>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue';
import { onShow } from '@dcloudio/uni-app';
const { proxy } = getCurrentInstance();
const api = proxy.$api;
const strategies = ref([]);
// 防止重复请求的标志
let isFetching = false;
const fetchStrategies = async () => {
try {
const response = await api.strategies.getStrategies();
if (response.code === 200) {
strategies.value = response.data.map(item => ({
id: item.id,
title: item.name,
tag: item.tags?.[0] || '策略',
desc: item.description,
tags: item.tags || [],
riskLevel: item.riskLevel,
bgClass: item.type === 'ma_trend' ? 'bg-emerald-900' : item.type === 'risk_parity' ? 'bg-blue-600' : 'bg-orange-500',
tagClass: item.type === 'ma_trend' ? 'text-emerald-700' : item.type === 'risk_parity' ? 'text-blue-700' : 'text-orange-700',
iconChar: item.name?.charAt(0) || 'S',
btnClass: 'btn-primary',
btnText: '配置'
}));
}
} catch (error) {
console.error('获取策略数据失败:', error);
}
};
onShow(async () => {
isFetching = true;
await fetchStrategies();
isFetching = false;
});
const goToAdd = () => {
uni.navigateTo({ url: '/pages/strategies/edit/edit' });
};
const handleAction = (item) => {
console.log('点击策略:', item.title);
uni.navigateTo({ url: `/pages/strategies/edit/edit?id=${item.id}` });
};
</script>
<style scoped>
/* 全局布局 */
.page-container {
min-height: 100vh;
background-color: #F9FAFB;
}
.header-section {
padding: 40rpx 40rpx 20rpx 40rpx;
background-color: #F9FAFB;
position: sticky;
top: 0;
z-index: 10;
}
.strategy-list {
padding: 0 40rpx;
}
/* 状态栏适配 */
.status-bar-height { height: var(--status-bar-height); }
/* 文字样式 */
.title-lg { font-size: 48rpx; font-weight: 800; color: #111827; display: block; }
.subtitle { font-size: 28rpx; color: #6B7280; margin-top: 8rpx; display: block; }
/* === 新增:添加按钮样式 === */
.add-btn-box {
width: 88rpx;
height: 88rpx;
background-color: #FFFFFF;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.05);
border: 1rpx solid #E5E7EB;
}
.add-btn-box:active { background-color: #F3F4F6; }
/* 策略卡片 */
.strategy-card {
background-color: #FFFFFF;
border-radius: 32rpx;
padding: 40rpx;
margin-bottom: 32rpx;
position: relative;
overflow: hidden;
box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.06);
border: 1rpx solid #E5E7EB;
transition: all 0.2s ease;
}
.strategy-card:active {
transform: translateY(2rpx);
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.04);
}
.watermark { position: absolute; right: -20rpx; top: -20rpx; z-index: 0; pointer-events: none; }
.card-header-row { position: relative; z-index: 1; }
/* 图标盒子 */
.icon-box {
width: 80rpx; height: 80rpx; border-radius: 20rpx;
display: flex; align-items: center; justify-content: center;
}
.bg-emerald-900 { background-color: #064e3b; }
.bg-blue-600 { background-color: #2563EB; }
.bg-orange-500 { background-color: #F97316; }
.icon-text { color: #fff; font-weight: bold; font-size: 32rpx; }
/* 卡片文本 */
.card-head { font-size: 32rpx; font-weight: 700; color: #111827; display: block;}
.card-tag { font-size: 22rpx; font-weight: 600; margin-top: 4rpx; }
.text-emerald-700 { color: #047857; }
.text-blue-700 { color: #1D4ED8; }
.text-orange-700 { color: #C2410C; }
.desc-text {
font-size: 26rpx;
color: #4B5563;
line-height: 1.6;
margin-bottom: 32rpx;
display: block;
margin-top: 24rpx;
position: relative;
z-index: 1;
}
/* 底部操作区 */
.footer-row {
display: flex; justify-content: space-between; align-items: center;
padding-top: 32rpx;
border-top: 1rpx solid #F9FAFB;
position: relative;
z-index: 1;
}
.tag-gray { background-color: #F3F4F6; padding: 6rpx 16rpx; border-radius: 12rpx; margin-right: 12rpx; }
.tag-text { font-size: 22rpx; color: #6B7280; font-weight: 500; }
/* 按钮样式 */
.btn-primary {
background-color: #064e3b; color: #fff; font-size: 24rpx; font-weight: 600;
padding: 0 32rpx; height: 64rpx; line-height: 64rpx; border-radius: 999rpx; margin: 0;
}
.btn-secondary {
background-color: #F3F4F6; color: #4B5563; font-size: 24rpx; font-weight: 600;
padding: 0 32rpx; height: 64rpx; line-height: 64rpx; border-radius: 999rpx; margin: 0;
}
/* 工具类 */
.flex-row { display: flex; flex-direction: row; }
.flex-col { display: flex; flex-direction: column; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.justify-between { justify-content: space-between; }
.gap-3 { gap: 24rpx; }
.gap-2 { gap: 16rpx; }
.mb-4 { margin-bottom: 32rpx; }
</style>