- config.vue: 替换原生picker为u-picker,统一toast为u-toast,按钮为u-button - detail.vue: 替换交易表单input为u-input,所有按钮为u-button,toast为u-toast - 清理已替换组件的CSS样式 - 添加todo.md记录后续升级计划
781 lines
19 KiB
Vue
Executable File
781 lines
19 KiB
Vue
Executable File
<template>
|
||
<view class="page-container">
|
||
<!-- uView Toast 组件 -->
|
||
<u-toast ref="uToastRef" />
|
||
|
||
<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>
|
||
<u-input
|
||
v-model="form.name"
|
||
placeholder="给你的组合起个名字 (如: 养老定投)"
|
||
:border="false"
|
||
:customStyle="{ backgroundColor: '#F9FAFB', borderRadius: '20rpx', height: '96rpx', padding: '0 32rpx' }"
|
||
/>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label">选择逻辑模板</text>
|
||
<u-picker
|
||
:show="showStrategyPicker"
|
||
:columns="[strategies.map(s => s.name)]"
|
||
keyName="name"
|
||
@confirm="onStrategyPickerConfirm"
|
||
@cancel="showStrategyPicker = false"
|
||
>
|
||
<view class="picker-box" @click="showStrategyPicker = true">
|
||
<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>
|
||
<u-icon name="arrow-down" size="14" color="#9CA3AF"></u-icon>
|
||
</view>
|
||
</u-picker>
|
||
<text class="helper-text" v-if="selectedStrategy">{{ selectedStrategy.desc }}</text>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label">组合币种</text>
|
||
<u-picker
|
||
:show="showCurrencyPicker"
|
||
:columns="[currencyList.map(c => c.name)]"
|
||
keyName="name"
|
||
@confirm="onCurrencyPickerConfirm"
|
||
@cancel="showCurrencyPicker = false"
|
||
>
|
||
<view class="picker-box" @click="showCurrencyPicker = true">
|
||
<text class="picker-text">{{ currencyList[currencyIndex].name }}</text>
|
||
<u-icon name="arrow-down" size="14" color="#9CA3AF"></u-icon>
|
||
</view>
|
||
</u-picker>
|
||
<text class="helper-text">创建后币种不可修改,所有交易只能使用该币种</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="section-card">
|
||
<view class="card-header">
|
||
<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>
|
||
|
||
<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>
|
||
</view>
|
||
|
||
<view class="item-grid">
|
||
<view class="grid-col relative">
|
||
<text class="sub-label">单元名称/代码</text>
|
||
<u-input
|
||
v-model="item.name"
|
||
placeholder="如 TMF"
|
||
:disabled="selectedStrategy?.type === 'risk_parity'"
|
||
:border="false"
|
||
:customStyle="{ backgroundColor: '#FFFFFF', borderRadius: '16rpx', height: '72rpx', padding: '0 20rpx' }"
|
||
@input="(e) => searchStock(e, index)"
|
||
@focus="() => { activeSearchIndex.value = -1; searchResults.value = []; }"
|
||
/>
|
||
<!-- 搜索下拉列表 -->
|
||
<view class="search-dropdown" v-if="searchResults.length > 0 && activeSearchIndex.value === index">
|
||
<view
|
||
class="dropdown-item"
|
||
v-for="(result, idx) in searchResults.filter(r => r.stockIndex === index)"
|
||
:key="idx"
|
||
@click="selectStock(result)"
|
||
>
|
||
<view class="item-left">
|
||
<text class="item-ticker">{{ result.ticker }}</text>
|
||
<text class="item-type" v-if="result.assetType">{{ result.assetType }}</text>
|
||
</view>
|
||
<text class="item-exchange">{{ result.exchange }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="grid-col">
|
||
<text class="sub-label">买入均价</text>
|
||
<u-input
|
||
v-model="item.price"
|
||
type="number"
|
||
placeholder="0.00"
|
||
:border="false"
|
||
:customStyle="{ backgroundColor: '#FFFFFF', borderRadius: '16rpx', height: '72rpx', padding: '0 20rpx' }"
|
||
/>
|
||
</view>
|
||
<view class="grid-col">
|
||
<text class="sub-label">持有数量</text>
|
||
<u-input
|
||
v-model="item.amount"
|
||
type="number"
|
||
placeholder="0"
|
||
:border="false"
|
||
:customStyle="{ backgroundColor: '#FFFFFF', borderRadius: '16rpx', height: '72rpx', padding: '0 20rpx' }"
|
||
/>
|
||
</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>
|
||
<u-icon name="calendar" size="16" color="#9CA3AF"></u-icon>
|
||
</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>
|
||
<u-button
|
||
class="btn-submit"
|
||
@click="submitForm"
|
||
:customStyle="{
|
||
backgroundColor: '#064E3B',
|
||
color: '#fff',
|
||
fontWeight: '700',
|
||
borderRadius: '24rpx',
|
||
height: '96rpx',
|
||
fontSize: '30rpx',
|
||
width: '100%',
|
||
border: 'none'
|
||
}"
|
||
>
|
||
创建组合
|
||
</u-button>
|
||
</view>
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, watch, getCurrentInstance } from 'vue';
|
||
import { onShow } from '@dcloudio/uni-app';
|
||
import { api } from '../../utils/api';
|
||
|
||
// 获取 u-toast 实例
|
||
const { proxy } = getCurrentInstance();
|
||
const uToastRef = ref();
|
||
|
||
const strategies = ref([]);
|
||
const strategyIndex = ref(-1);
|
||
// 币种选择
|
||
const currencyList = ref([
|
||
{ name: '人民币 CNY', code: 'CNY' },
|
||
{ name: '美元 USD', code: 'USD' },
|
||
{ name: '港币 HKD', code: 'HKD' }
|
||
]);
|
||
const currencyIndex = ref(0); // 默认CNY
|
||
|
||
// Picker 控制变量
|
||
const showStrategyPicker = ref(false);
|
||
const showCurrencyPicker = ref(false);
|
||
|
||
// 防止重复请求的标志
|
||
let isFetching = false;
|
||
|
||
const form = ref({
|
||
name: '',
|
||
stocks: [
|
||
{ name: '', price: '', amount: '', date: '' }
|
||
]
|
||
});
|
||
|
||
// 股票搜索相关
|
||
const searchResults = ref([]);
|
||
const activeSearchIndex = ref(-1);
|
||
const searchTimer = ref(null);
|
||
const searchStock = async (keyword, stockIndex) => {
|
||
// 防抖
|
||
if (searchTimer.value) clearTimeout(searchTimer.value);
|
||
// 赋值当前激活的搜索下标
|
||
activeSearchIndex.value = stockIndex;
|
||
|
||
if (!keyword || keyword.length < 1) {
|
||
searchResults.value = [];
|
||
activeSearchIndex.value = -1;
|
||
return;
|
||
}
|
||
|
||
searchTimer.value = setTimeout(async () => {
|
||
try {
|
||
const res = await api.ticker.search(keyword);
|
||
if (res.code === 200) {
|
||
searchResults.value = res.data.map(item => ({
|
||
...item,
|
||
stockIndex: stockIndex
|
||
}));
|
||
}
|
||
} catch (err) {
|
||
console.error('搜索股票失败:', err);
|
||
searchResults.value = [];
|
||
activeSearchIndex.value = -1;
|
||
}
|
||
}, 300);
|
||
};
|
||
|
||
const selectStock = (result) => {
|
||
const stock = form.value.stocks[result.stockIndex];
|
||
if (stock) {
|
||
stock.name = result.ticker;
|
||
// 可以在这里额外保存资产类型等信息
|
||
stock.assetType = result.assetType;
|
||
stock.currency = result.priceCurrency;
|
||
}
|
||
searchResults.value = [];
|
||
activeSearchIndex.value = -1;
|
||
};
|
||
|
||
const selectedStrategy = computed(() => {
|
||
if (strategyIndex.value === -1) return null;
|
||
return strategies.value[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 fetchStrategies = async () => {
|
||
try {
|
||
const response = await api.strategies.getStrategies();
|
||
if (response.code === 200) {
|
||
strategies.value = response.data.map(item => {
|
||
let parameters = {};
|
||
if (item.config) {
|
||
try {
|
||
let config = JSON.parse(item.config);
|
||
// 如果解析后还是字符串,再解析一次(处理双层转义)
|
||
if (typeof config === 'string') {
|
||
config = JSON.parse(config);
|
||
}
|
||
parameters = config;
|
||
} catch (e) {
|
||
console.error('解析策略配置失败:', e);
|
||
parameters = {};
|
||
}
|
||
}
|
||
return {
|
||
id: item.id,
|
||
name: item.name,
|
||
desc: item.description,
|
||
type: item.type,
|
||
parameters: parameters,
|
||
color: '#10B981'
|
||
};
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('获取策略列表失败:', error);
|
||
}
|
||
};
|
||
|
||
const onStrategyChange = (e) => {
|
||
strategyIndex.value = e.detail.value;
|
||
const strategy = strategies.value[strategyIndex.value];
|
||
|
||
console.log('选择的策略:', strategy);
|
||
console.log('策略参数:', strategy?.parameters);
|
||
|
||
if (strategy && strategy.parameters && strategy.parameters.assets) {
|
||
console.log('找到assets,开始填充:', strategy.parameters.assets);
|
||
form.value.stocks = strategy.parameters.assets.map(asset => ({
|
||
name: asset.symbol,
|
||
price: '',
|
||
amount: '',
|
||
date: ''
|
||
}));
|
||
console.log('填充后的form.stocks:', form.value.stocks);
|
||
} else {
|
||
console.log('条件判断失败:', {
|
||
hasStrategy: !!strategy,
|
||
hasParameters: !!(strategy && strategy.parameters),
|
||
hasAssets: !!(strategy && strategy.parameters && strategy.parameters.assets)
|
||
});
|
||
form.value.stocks = [{ name: '', price: '', amount: '', date: '' }];
|
||
console.log('未找到标的配置,使用默认值');
|
||
}
|
||
};
|
||
|
||
const onDateChange = (e, index) => {
|
||
form.value.stocks[index].date = e.detail.value;
|
||
};
|
||
|
||
// u-picker 确认方法
|
||
const onStrategyPickerConfirm = (e) => {
|
||
const { value, index } = e;
|
||
strategyIndex.value = index[0];
|
||
showStrategyPicker.value = false;
|
||
|
||
// 调用原有的策略变更逻辑
|
||
const strategy = strategies.value[strategyIndex.value];
|
||
if (strategy && strategy.parameters && strategy.parameters.assets) {
|
||
form.value.stocks = strategy.parameters.assets.map(asset => ({
|
||
name: asset.symbol,
|
||
price: '',
|
||
amount: '',
|
||
date: ''
|
||
}));
|
||
} else {
|
||
form.value.stocks = [{ name: '', price: '', amount: '', date: '' }];
|
||
}
|
||
};
|
||
|
||
const onCurrencyPickerConfirm = (e) => {
|
||
const { value, index } = e;
|
||
currencyIndex.value = index[0];
|
||
showCurrencyPicker.value = false;
|
||
};
|
||
|
||
const submitForm = async () => {
|
||
if (!form.value.name) {
|
||
proxy?.$refs.uToastRef?.show({
|
||
type: 'warning',
|
||
message: '请输入组合名称',
|
||
icon: 'warning'
|
||
});
|
||
return;
|
||
}
|
||
if (strategyIndex.value === -1) {
|
||
proxy?.$refs.uToastRef?.show({
|
||
type: 'warning',
|
||
message: '请选择策略',
|
||
icon: 'warning'
|
||
});
|
||
return;
|
||
}
|
||
|
||
const selected = strategies.value[strategyIndex.value];
|
||
|
||
// 风险平价策略权重校验
|
||
if (selected.type === 'risk_parity' && selected.parameters?.assets) {
|
||
let totalWeight = 0;
|
||
const targetAssets = selected.parameters.assets;
|
||
|
||
for (let i = 0; i < form.value.stocks.length; i++) {
|
||
const stock = form.value.stocks[i];
|
||
const target = targetAssets.find(a => a.symbol === stock.name);
|
||
if (!target) continue;
|
||
|
||
// 计算实际持仓市值 = 价格 * 数量
|
||
const marketValue = (parseFloat(stock.price) || 0) * (parseFloat(stock.amount) || 0);
|
||
totalWeight += marketValue;
|
||
}
|
||
|
||
// 校验每个标的的实际权重和目标权重偏差不超过5%
|
||
for (let i = 0; i < form.value.stocks.length; i++) {
|
||
const stock = form.value.stocks[i];
|
||
const target = targetAssets.find(a => a.symbol === stock.name);
|
||
if (!target) continue;
|
||
|
||
const marketValue = (parseFloat(stock.price) || 0) * (parseFloat(stock.amount) || 0);
|
||
const actualWeight = totalWeight > 0 ? marketValue / totalWeight : 0;
|
||
const deviation = Math.abs(actualWeight - target.targetWeight);
|
||
|
||
if (deviation > 0.05) {
|
||
proxy?.$refs.uToastRef?.show({
|
||
type: 'error',
|
||
message: `${stock.name} 权重偏差超过5%,目标${(target.targetWeight*100).toFixed(0)}%,实际${(actualWeight*100).toFixed(0)}%`,
|
||
duration: 3000
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
const selectedCurrency = currencyList.value[currencyIndex.value].code;
|
||
const requestData = {
|
||
name: form.value.name,
|
||
strategyId: selected.id,
|
||
currency: selectedCurrency,
|
||
stocks: form.value.stocks.map(stock => ({
|
||
name: stock.name,
|
||
code: stock.name,
|
||
price: parseFloat(stock.price) || 0,
|
||
amount: parseFloat(stock.amount) || 0,
|
||
date: stock.date,
|
||
currency: selectedCurrency
|
||
}))
|
||
};
|
||
|
||
uni.showLoading({ title: '创建中...' });
|
||
|
||
try {
|
||
const response = await api.assets.createPortfolio(requestData);
|
||
if (response.code === 200) {
|
||
uni.hideLoading();
|
||
proxy?.$refs.uToastRef?.show({
|
||
type: 'success',
|
||
message: '创建成功',
|
||
icon: 'success'
|
||
});
|
||
setTimeout(() => uni.navigateBack(), 1500);
|
||
}
|
||
} catch (error) {
|
||
console.error('创建投资组合失败:', error);
|
||
uni.hideLoading();
|
||
proxy?.$refs.uToastRef?.show({
|
||
type: 'error',
|
||
message: '创建失败,请重试',
|
||
icon: 'error'
|
||
});
|
||
}
|
||
};
|
||
|
||
onShow(async () => {
|
||
isFetching = true;
|
||
await fetchStrategies();
|
||
isFetching = false;
|
||
});
|
||
</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;
|
||
}
|
||
|
||
/* 表单项 */
|
||
.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;
|
||
}
|
||
|
||
.mini-input[disabled] {
|
||
background-color: #F3F4F6;
|
||
color: #6B7280;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
/* 搜索下拉列表 */
|
||
.relative {
|
||
position: relative;
|
||
}
|
||
|
||
.search-dropdown {
|
||
position: absolute;
|
||
top: 100%;
|
||
left: 0;
|
||
right: 0;
|
||
background-color: #FFFFFF;
|
||
border: 1rpx solid #E5E7EB;
|
||
border-radius: 12rpx;
|
||
margin-top: 4rpx;
|
||
max-height: 300rpx;
|
||
overflow-y: auto;
|
||
z-index: 100;
|
||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.dropdown-item {
|
||
padding: 16rpx 20rpx;
|
||
border-bottom: 1rpx solid #F3F4F6;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.dropdown-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.dropdown-item:active {
|
||
background-color: #F3F4F6;
|
||
}
|
||
|
||
.item-left {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.item-ticker {
|
||
font-size: 26rpx;
|
||
font-weight: 600;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.item-type {
|
||
font-size: 18rpx;
|
||
color: #064E3B;
|
||
background-color: #D1FAE5;
|
||
padding: 2rpx 8rpx;
|
||
border-radius: 4rpx;
|
||
}
|
||
|
||
.item-exchange {
|
||
font-size: 22rpx;
|
||
color: #9CA3AF;
|
||
}
|
||
|
||
/* 日期选择 */
|
||
.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';
|
||
}
|
||
|
||
/* .btn-submit 样式已通过 u-button 的 customStyle 设置,此处保留空类避免冲突 */
|
||
.btn-submit {
|
||
/* 样式已内联设置 */
|
||
}
|
||
</style> |