refactor: 重构策略编辑页面,支持创建和更新操作 fix: 修复投资组合详情页的数据显示问题 perf: 优化API请求处理,添加PUT和DELETE方法 docs: 完善API接口文档注释 style: 统一代码格式和命名规范
664 lines
21 KiB
Vue
664 lines
21 KiB
Vue
<template>
|
|
<view class="page-container">
|
|
|
|
|
|
<view class="header-section">
|
|
<view class="asset-card">
|
|
<view class="card-watermark">
|
|
<uni-icons type="vip-filled" size="120" color="rgba(255,255,255,0.05)"></uni-icons>
|
|
</view>
|
|
|
|
<view class="card-top">
|
|
<text class="label-text">组合总额 (NV)</text>
|
|
<view class="status-badge">
|
|
<text class="status-text">账本追踪中</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="card-main">
|
|
<text class="currency">¥</text>
|
|
<text class="big-number">{{ (portfolioData.portfolioValue || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}</text>
|
|
</view>
|
|
|
|
<view class="card-bottom">
|
|
<view class="stat-item">
|
|
<text class="stat-label">历史变化</text>
|
|
<text class="stat-val" :class="(portfolioData.historicalChange || 0) >= 0 ? 'text-red' : 'text-green'">{{ (portfolioData.historicalChange || 0) >= 0 ? '+' : '' }}{{ (portfolioData.historicalChange || 0).toFixed(2) }}%</text>
|
|
</view>
|
|
<view class="stat-item align-right">
|
|
<text class="stat-label">日内波动</text>
|
|
<text class="stat-val" :class="(portfolioData.dailyVolatility || 0) >= 0 ? 'text-red' : 'text-green'">{{ (portfolioData.dailyVolatility || 0) >= 0 ? '+' : '' }}¥{{ (portfolioData.dailyVolatility || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="section-container pb-0">
|
|
<view class="section-header">
|
|
<text class="section-title">当前逻辑模型</text>
|
|
<view class="flex-row items-center" @click="goStrategyConfig">
|
|
<text class="section-sub text-brand">参数配置</text>
|
|
<uni-icons type="right" size="12" color="#064E3B"></uni-icons>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="strategy-info-card" v-if="portfolioData.logicModel">
|
|
<view class="st-left">
|
|
<view class="st-icon-box bg-green-100">
|
|
<text class="st-icon-text text-green">{{ portfolioData.logicModel?.charAt(0) || 'S' }}</text>
|
|
</view>
|
|
<view class="flex-col gap-1">
|
|
<text class="st-name">{{ portfolioData.logicModel || '未设置策略' }}</text>
|
|
<view class="flex-row gap-2">
|
|
<text class="st-tag" v-if="portfolioData.logicModelDescription">{{ portfolioData.logicModelDescription }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="st-right">
|
|
<view class="flex-row items-center gap-1">
|
|
<view class="status-dot pulsing"></view>
|
|
<text class="st-status-text">{{ portfolioData.logicModelStatus || '监控中' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="section-container">
|
|
<view class="section-header">
|
|
<text class="section-title">当前记录项 ({{ portfolioData.totalItems || positions.length }})</text>
|
|
<text class="section-sub">占比 {{ portfolioData.totalRatio || 100 }}%</text>
|
|
</view>
|
|
|
|
<view class="position-list">
|
|
<view class="position-card" v-for="(item, index) in positions" :key="item.id || index">
|
|
<view class="pos-top">
|
|
<view class="flex-row items-center gap-2">
|
|
<view class="stock-icon" :class="index % 2 === 0 ? 'bg-blue-100 text-blue' : 'bg-orange-100 text-orange'">
|
|
<text class="icon-char">{{ item.stockName?.charAt(0) || item.stockCode?.charAt(0) || 'S' }}</text>
|
|
</view>
|
|
<view class="flex-col">
|
|
<text class="stock-name">{{ item.stockName || item.stockCode }}</text>
|
|
<text class="stock-code">{{ item.stockCode }} · {{ item.amount }}份</text>
|
|
</view>
|
|
</view>
|
|
<view class="flex-col align-right">
|
|
<text class="market-val">¥{{ (item.totalValue || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}</text>
|
|
<text class="weight-tag">比例 {{ (item.ratio || 0).toFixed(1) }}%</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="divider"></view>
|
|
|
|
<view class="pos-bottom">
|
|
<view class="pnl-item">
|
|
<text class="pnl-label">变动额</text>
|
|
<text class="pnl-val" :class="(item.changeAmount || 0) >= 0 ? 'text-red' : 'text-green'">
|
|
{{ (item.changeAmount || 0) >= 0 ? '+' : '' }}¥{{ (item.changeAmount || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}
|
|
</text>
|
|
</view>
|
|
<view class="pnl-item align-right">
|
|
<text class="pnl-label">偏离比例</text>
|
|
<text class="pnl-val" :class="(item.deviationRatio || 0) >= 0 ? 'text-red' : 'text-green'">
|
|
{{ (item.deviationRatio || 0) >= 0 ? '+' : '' }}{{ (item.deviationRatio || 0).toFixed(2) }}%
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="section-container">
|
|
<view class="section-header">
|
|
<text class="section-title">最近交易记录</text>
|
|
</view>
|
|
|
|
<view class="timeline-box">
|
|
<view class="timeline-item" v-for="(log, k) in logs" :key="k">
|
|
<view class="tl-left">
|
|
<text class="tl-date">{{ log.Date }}</text>
|
|
<text class="tl-time">{{ log.Time }}</text>
|
|
</view>
|
|
|
|
<view class="tl-line">
|
|
<view class="tl-dot" :class="log.Type === 'buy' ? 'bg-red' : 'bg-green'"></view>
|
|
<view class="tl-dash" v-if="k !== logs.length - 1"></view>
|
|
</view>
|
|
|
|
<view class="tl-right">
|
|
<text class="tl-title">{{ log.Title }}</text>
|
|
<text class="tl-desc">{{ log.Type === 'buy' ? '录入增加' : '结出减少' }} {{ log.Amount }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="action-section fixed-bottom">
|
|
<button class="action-btn btn-buy" @click="handleBuy">
|
|
<uni-icons type="download" size="20" color="#FFFFFF"></uni-icons>
|
|
<text class="btn-text">录入增加 / 比例校准</text>
|
|
</button>
|
|
<button class="action-btn btn-sell" @click="handleSell">
|
|
<uni-icons type="upload" size="20" color="#064E3B"></uni-icons>
|
|
<text class="btn-text">结出减少 / 调减</text>
|
|
</button>
|
|
</view>
|
|
|
|
<!-- 交易表单弹窗 -->
|
|
<view v-if="showTransactionForm" class="transaction-modal">
|
|
<view class="modal-content">
|
|
<view class="modal-header">
|
|
<text class="modal-title">{{ transactionType === 'buy' ? '录入增加' : '结出减少' }}</text>
|
|
<view class="close-btn" @click="showTransactionForm = false">
|
|
<uni-icons type="close" size="20" color="#6B7280"></uni-icons>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-content">
|
|
<view class="form-item">
|
|
<text class="form-label">股票代码</text>
|
|
<input
|
|
v-model="transactionForm.stockCode"
|
|
class="form-input"
|
|
placeholder="请输入股票代码"
|
|
/>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label">数量</text>
|
|
<input
|
|
v-model="transactionForm.amount"
|
|
class="form-input"
|
|
type="number"
|
|
placeholder="请输入数量"
|
|
/>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label">价格</text>
|
|
<input
|
|
v-model="transactionForm.price"
|
|
class="form-input"
|
|
type="number"
|
|
step="0.01"
|
|
placeholder="请输入价格"
|
|
/>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label">货币</text>
|
|
<view class="form-select">
|
|
<text>{{ transactionForm.currency }}</text>
|
|
<uni-icons type="bottom" size="14" color="#9CA3AF"></uni-icons>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label">备注</text>
|
|
<input
|
|
v-model="transactionForm.remark"
|
|
class="form-input"
|
|
placeholder="请输入备注"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="modal-footer">
|
|
<button class="cancel-btn" @click="showTransactionForm = false">取消</button>
|
|
<button class="confirm-btn" @click="submitTransaction">确认</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, getCurrentInstance } from 'vue';
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
const api = proxy.$api;
|
|
|
|
const portfolioId = ref('');
|
|
const portfolioData = ref({
|
|
id: '',
|
|
name: '',
|
|
currency: 'CNY',
|
|
status: '',
|
|
portfolioValue: 0,
|
|
totalReturn: 0,
|
|
todayProfit: 0,
|
|
todayProfitCurrency: 'CNY',
|
|
historicalChange: 0,
|
|
dailyVolatility: 0,
|
|
logicModel: '',
|
|
logicModelStatus: '',
|
|
logicModelDescription: '',
|
|
totalItems: 0,
|
|
totalRatio: 100,
|
|
strategy: null
|
|
});
|
|
|
|
const positions = ref([]);
|
|
const logs = ref([]);
|
|
|
|
// 交易表单
|
|
const showTransactionForm = ref(false);
|
|
const transactionType = ref('buy'); // buy 或 sell
|
|
const transactionForm = ref({
|
|
stockCode: '',
|
|
amount: '',
|
|
price: '',
|
|
currency: 'CNY',
|
|
remark: ''
|
|
});
|
|
|
|
const fetchPortfolioData = async () => {
|
|
try {
|
|
const pages = getCurrentPages();
|
|
const currentPage = pages[pages.length - 1];
|
|
const id = currentPage.options?.id;
|
|
|
|
if (!id) {
|
|
console.error('缺少投资组合ID');
|
|
return;
|
|
}
|
|
|
|
portfolioId.value = id;
|
|
const response = await api.assets.getPortfolio(id);
|
|
|
|
if (response.code === 200) {
|
|
portfolioData.value = response.data;
|
|
positions.value = response.data.positions || [];
|
|
console.log('投资组合数据获取成功:', response.data);
|
|
}
|
|
} catch (error) {
|
|
console.error('获取投资组合数据失败:', error);
|
|
}
|
|
};
|
|
|
|
const fetchTransactions = async () => {
|
|
try {
|
|
if (!portfolioId.value) return;
|
|
|
|
const response = await api.assets.getTransactions({
|
|
portfolioId: portfolioId.value,
|
|
limit: 10,
|
|
offset: 0
|
|
});
|
|
|
|
if (response.code === 200) {
|
|
logs.value = response.data.items || [];
|
|
console.log('交易记录获取成功:', response.data);
|
|
}
|
|
} catch (error) {
|
|
console.error('获取交易记录失败:', error);
|
|
}
|
|
};
|
|
|
|
onMounted(async () => {
|
|
await fetchPortfolioData();
|
|
await fetchTransactions();
|
|
});
|
|
|
|
const goStrategyConfig = () => {
|
|
if (portfolioData.value.strategy?.Id) {
|
|
uni.navigateTo({ url: `/pages/strategies/edit/edit?id=${portfolioData.value.strategy.Id}` });
|
|
}
|
|
};
|
|
|
|
const handleBuy = () => {
|
|
transactionType.value = 'buy';
|
|
resetTransactionForm();
|
|
showTransactionForm.value = true;
|
|
};
|
|
|
|
const handleSell = () => {
|
|
transactionType.value = 'sell';
|
|
resetTransactionForm();
|
|
showTransactionForm.value = true;
|
|
};
|
|
|
|
const resetTransactionForm = () => {
|
|
transactionForm.value = {
|
|
stockCode: '',
|
|
amount: '',
|
|
price: '',
|
|
currency: 'CNY',
|
|
remark: ''
|
|
};
|
|
};
|
|
|
|
const submitTransaction = async () => {
|
|
// 表单验证
|
|
if (!transactionForm.value.stockCode) {
|
|
return uni.showToast({ title: '请输入股票代码', icon: 'none' });
|
|
}
|
|
if (!transactionForm.value.amount || parseFloat(transactionForm.value.amount) <= 0) {
|
|
return uni.showToast({ title: '请输入有效的数量', icon: 'none' });
|
|
}
|
|
if (!transactionForm.value.price || parseFloat(transactionForm.value.price) <= 0) {
|
|
return uni.showToast({ title: '请输入有效的价格', icon: 'none' });
|
|
}
|
|
|
|
const transactionData = {
|
|
portfolioId: portfolioId.value,
|
|
type: transactionType.value,
|
|
stockCode: transactionForm.value.stockCode,
|
|
amount: parseFloat(transactionForm.value.amount),
|
|
price: parseFloat(transactionForm.value.price),
|
|
currency: transactionForm.value.currency,
|
|
remark: transactionForm.value.remark
|
|
};
|
|
|
|
uni.showLoading({ title: '提交中...' });
|
|
|
|
try {
|
|
const response = await api.assets.createTransaction(transactionData);
|
|
if (response.code === 200) {
|
|
uni.hideLoading();
|
|
uni.showToast({ title: '交易提交成功', icon: 'success' });
|
|
showTransactionForm.value = false;
|
|
|
|
// 重新获取交易记录
|
|
await fetchTransactions();
|
|
// 重新获取投资组合数据
|
|
await fetchPortfolioData();
|
|
}
|
|
} catch (error) {
|
|
console.error('创建交易失败:', error);
|
|
uni.hideLoading();
|
|
uni.showToast({ title: '提交失败,请重试', icon: 'none' });
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* 基础设置 */
|
|
.page-container {
|
|
min-height: 100vh;
|
|
background-color: #F9FAFB;
|
|
/* 关键:底部留出空间,防止内容被固定按钮遮挡 */
|
|
padding-bottom: 180rpx;
|
|
}
|
|
.flex-row { display: flex; flex-direction: row; }
|
|
.flex-col { display: flex; flex-direction: column; }
|
|
.items-center { align-items: center; }
|
|
.justify-between { justify-content: space-between; }
|
|
.gap-1 { gap: 8rpx; }
|
|
.gap-2 { gap: 16rpx; }
|
|
.align-right { align-items: flex-end; }
|
|
.pb-0 { padding-bottom: 0 !important; }
|
|
|
|
/* 颜色工具 */
|
|
.text-red { color: #EF4444; }
|
|
.text-green { color: #10B981; }
|
|
.text-brand { color: #064E3B; }
|
|
.bg-blue-100 { background-color: #EFF6FF; }
|
|
.text-blue { color: #2563EB; }
|
|
.bg-orange-100 { background-color: #FFF7ED; }
|
|
.text-orange { color: #EA580C; }
|
|
.bg-green-100 { background-color: #ECFDF5; }
|
|
.bg-red { background-color: #EF4444; }
|
|
.bg-green { background-color: #10B981; }
|
|
|
|
/* 1. 导航栏 */
|
|
.nav-bar {
|
|
background-color: #fff;
|
|
padding: var(--status-bar-height) 32rpx 20rpx 32rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
}
|
|
.page-title { font-size: 34rpx; font-weight: 700; color: #111827; }
|
|
|
|
/* 2. 头部深色卡片 */
|
|
.header-section { padding: 20rpx 32rpx; }
|
|
.asset-card {
|
|
background-color: #064E3B;
|
|
border-radius: 40rpx;
|
|
padding: 40rpx;
|
|
position: relative;
|
|
overflow: hidden;
|
|
box-shadow: 0 10rpx 30rpx rgba(6, 78, 59, 0.25);
|
|
color: #fff;
|
|
}
|
|
.card-watermark { position: absolute; right: -20rpx; top: -20rpx; opacity: 0.1; transform: rotate(15deg); }
|
|
.card-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; }
|
|
.label-text { font-size: 26rpx; opacity: 0.8; }
|
|
.status-badge { background-color: rgba(255,255,255,0.2); padding: 4rpx 16rpx; border-radius: 20rpx; }
|
|
.status-text { font-size: 22rpx; font-weight: 600; }
|
|
.card-main { display: flex; align-items: baseline; margin-bottom: 40rpx; }
|
|
.currency { font-size: 40rpx; font-weight: 700; margin-right: 8rpx; }
|
|
.big-number { font-size: 64rpx; font-weight: 800; font-family: 'DIN Alternate'; }
|
|
.card-bottom { display: flex; justify-content: space-between; }
|
|
.stat-item { display: flex; flex-direction: column; gap: 8rpx; }
|
|
.stat-label { font-size: 24rpx; opacity: 0.7; }
|
|
.stat-val { font-size: 32rpx; font-weight: 700; font-family: 'DIN Alternate'; }
|
|
|
|
/* 策略信息卡片 */
|
|
.strategy-info-card {
|
|
background-color: #FFFFFF;
|
|
border-radius: 24rpx;
|
|
padding: 24rpx;
|
|
border: 1rpx solid #F3F4F6;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02);
|
|
}
|
|
.st-left { display: flex; align-items: center; gap: 20rpx; }
|
|
.st-icon-box {
|
|
width: 80rpx; height: 80rpx;
|
|
border-radius: 20rpx;
|
|
display: flex; align-items: center; justify-content: center;
|
|
}
|
|
.st-icon-text { font-size: 36rpx; font-weight: 800; }
|
|
.st-name { font-size: 28rpx; font-weight: 700; color: #1F2937; }
|
|
.st-tag {
|
|
font-size: 20rpx; color: #6B7280;
|
|
background-color: #F3F4F6; padding: 2rpx 10rpx; border-radius: 8rpx;
|
|
}
|
|
.st-status-text { font-size: 24rpx; font-weight: 600; color: #059669; }
|
|
.status-dot {
|
|
width: 12rpx; height: 12rpx; border-radius: 50%;
|
|
background-color: #10B981;
|
|
}
|
|
.pulsing { animation: pulse 2s infinite; }
|
|
@keyframes pulse {
|
|
0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
|
|
70% { box-shadow: 0 0 0 10rpx rgba(16, 185, 129, 0); }
|
|
100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
|
|
}
|
|
|
|
/* 通用容器 */
|
|
.section-container { padding: 20rpx 32rpx; }
|
|
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24rpx; }
|
|
.section-title { font-size: 30rpx; font-weight: 800; color: #1F2937; border-left: 8rpx solid #064E3B; padding-left: 16rpx; line-height: 1; }
|
|
.section-sub { font-size: 24rpx; color: #9CA3AF; margin-right: 4rpx; }
|
|
|
|
/* 持仓卡片 */
|
|
.position-card {
|
|
background-color: #fff;
|
|
border-radius: 24rpx;
|
|
padding: 32rpx;
|
|
margin-bottom: 24rpx;
|
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.02);
|
|
border: 1rpx solid #F3F4F6;
|
|
}
|
|
.stock-icon { width: 80rpx; height: 80rpx; border-radius: 20rpx; display: flex; align-items: center; justify-content: center; }
|
|
.icon-char { font-size: 32rpx; font-weight: 800; }
|
|
.stock-name { font-size: 30rpx; font-weight: 700; color: #1F2937; }
|
|
.stock-code { font-size: 24rpx; color: #9CA3AF; margin-top: 4rpx; }
|
|
.market-val { font-size: 32rpx; font-weight: 700; color: #1F2937; }
|
|
.weight-tag { font-size: 22rpx; color: #6B7280; background-color: #F3F4F6; padding: 2rpx 12rpx; border-radius: 8rpx; margin-top: 8rpx; }
|
|
.divider { height: 1rpx; background-color: #F3F4F6; margin: 24rpx 0; }
|
|
.pos-bottom { display: flex; justify-content: space-between; }
|
|
.pnl-label { font-size: 22rpx; color: #9CA3AF; margin-bottom: 4rpx; }
|
|
.pnl-val { font-size: 28rpx; font-weight: 700; }
|
|
|
|
/* 交易明细 */
|
|
.timeline-box { padding: 0 16rpx; }
|
|
.timeline-item { display: flex; margin-bottom: 0; min-height: 120rpx; }
|
|
.tl-left { width: 80rpx; text-align: right; padding-right: 20rpx; display: flex; flex-direction: column; }
|
|
.tl-date { font-size: 26rpx; font-weight: 600; color: #374151; }
|
|
.tl-time { font-size: 22rpx; color: #9CA3AF; margin-top: 4rpx; }
|
|
.tl-line { width: 40rpx; display: flex; flex-direction: column; align-items: center; position: relative; }
|
|
.tl-dot { width: 16rpx; height: 16rpx; border-radius: 50%; z-index: 2; margin-top: 10rpx; }
|
|
.tl-dash { width: 2rpx; flex: 1; background-color: #E5E7EB; margin-top: 8rpx; }
|
|
.tl-right { flex: 1; padding-left: 20rpx; padding-bottom: 40rpx; }
|
|
.tl-title { font-size: 28rpx; font-weight: 600; color: #1F2937; }
|
|
.tl-desc { font-size: 24rpx; color: #6B7280; margin-top: 8rpx; }
|
|
|
|
/* 底部固定操作栏 */
|
|
.fixed-bottom {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background-color: #FFFFFF;
|
|
display: flex;
|
|
gap: 24rpx;
|
|
padding: 20rpx 32rpx 50rpx 32rpx; /* 适配 iPhone X */
|
|
box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.05);
|
|
z-index: 999;
|
|
}
|
|
|
|
.action-btn {
|
|
flex: 1;
|
|
height: 96rpx;
|
|
border-radius: 24rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 12rpx;
|
|
font-size: 30rpx;
|
|
font-weight: 700;
|
|
border: none;
|
|
}
|
|
.btn-buy { background-color: #064E3B; color: #FFFFFF; box-shadow: 0 8rpx 20rpx rgba(6, 78, 59, 0.2); }
|
|
.btn-buy:active { background-color: #047857; }
|
|
.btn-sell { background-color: #FFFFFF; color: #064E3B; border: 2rpx solid #064E3B; }
|
|
.btn-sell:active { background-color: #ECFDF5; }
|
|
|
|
/* 交易表单弹窗 */
|
|
.transaction-modal {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.modal-content {
|
|
background-color: #fff;
|
|
border-radius: 24rpx 24rpx 0 0;
|
|
width: 100%;
|
|
max-height: 80vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.modal-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 32rpx;
|
|
border-bottom: 1rpx solid #E5E7EB;
|
|
}
|
|
|
|
.modal-title {
|
|
font-size: 32rpx;
|
|
font-weight: 600;
|
|
color: #111827;
|
|
}
|
|
|
|
.close-btn {
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.form-content {
|
|
padding: 32rpx;
|
|
}
|
|
|
|
.form-item {
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.form-label {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
font-weight: 500;
|
|
color: #374151;
|
|
margin-bottom: 12rpx;
|
|
}
|
|
|
|
.form-input {
|
|
width: 100%;
|
|
height: 72rpx;
|
|
border: 1rpx solid #E5E7EB;
|
|
border-radius: 12rpx;
|
|
padding: 0 20rpx;
|
|
font-size: 24rpx;
|
|
color: #111827;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.form-input::placeholder {
|
|
color: #9CA3AF;
|
|
}
|
|
|
|
.form-select {
|
|
width: 100%;
|
|
height: 72rpx;
|
|
border: 1rpx solid #E5E7EB;
|
|
border-radius: 12rpx;
|
|
padding: 0 20rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
font-size: 24rpx;
|
|
color: #111827;
|
|
}
|
|
|
|
.modal-footer {
|
|
padding: 32rpx;
|
|
border-top: 1rpx solid #E5E7EB;
|
|
display: flex;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
.cancel-btn,
|
|
.confirm-btn {
|
|
flex: 1;
|
|
height: 80rpx;
|
|
border-radius: 12rpx;
|
|
font-size: 24rpx;
|
|
font-weight: 600;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: none;
|
|
}
|
|
|
|
.cancel-btn {
|
|
background-color: #F3F4F6;
|
|
color: #6B7280;
|
|
}
|
|
|
|
.confirm-btn {
|
|
background-color: #064E3B;
|
|
color: #fff;
|
|
}
|
|
</style> |