Merge branch 'master' of https://git.jb18cm.plus/fanfpy/AssetManager.UniApp
This commit is contained in:
commit
7dd5767f76
53
App.vue
53
App.vue
@ -12,6 +12,59 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view id="app">
|
||||
<!-- 全局uView配置 -->
|
||||
<u-config :config="uConfig">
|
||||
<router-view />
|
||||
<!-- 全局toast组件 -->
|
||||
<u-toast ref="uToast" />
|
||||
<!-- 全局modal组件 -->
|
||||
<u-modal ref="uModal" />
|
||||
</u-config>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// uView全局配置
|
||||
const uConfig = {
|
||||
// 主色调匹配项目品牌色
|
||||
primaryColor: '#064e3b',
|
||||
successColor: '#10B981',
|
||||
warningColor: '#f59e0b',
|
||||
errorColor: '#ef4444'
|
||||
}
|
||||
|
||||
// 挂载全局方法
|
||||
uni.$u.toast = (title, options = {}) => {
|
||||
uni.$refs.uToast.show({
|
||||
title,
|
||||
...options
|
||||
})
|
||||
}
|
||||
uni.$u.toast.success = (title) => {
|
||||
uni.$refs.uToast.show({
|
||||
title,
|
||||
type: 'success'
|
||||
})
|
||||
}
|
||||
uni.$u.toast.error = (title) => {
|
||||
uni.$refs.uToast.show({
|
||||
title,
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
uni.$u.toast.loading = (title) => {
|
||||
uni.$refs.uToast.show({
|
||||
title,
|
||||
type: 'loading'
|
||||
})
|
||||
}
|
||||
uni.$u.showModal = (options) => {
|
||||
uni.$refs.uModal.show(options)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 每个页面公共css */
|
||||
page {
|
||||
|
||||
8
main.js
8
main.js
@ -14,7 +14,7 @@ app.$mount()
|
||||
// #ifdef VUE3
|
||||
import { createSSRApp } from 'vue'
|
||||
import api from './utils/api'
|
||||
import uviewPlus from 'uview-plus'
|
||||
import uView from 'uview-ui'
|
||||
|
||||
console.log('🚀 应用启动,导入api模块')
|
||||
|
||||
@ -25,9 +25,9 @@ export function createApp() {
|
||||
app.config.globalProperties.$api = api
|
||||
console.log('✅ api已全局注册为 $api')
|
||||
|
||||
// 注册uView Plus
|
||||
app.use(uviewPlus)
|
||||
console.log('✅ uView Plus 已全局注册')
|
||||
// 注册uView UI 2.0
|
||||
app.use(uView)
|
||||
console.log('✅ uView UI 2.0 已全局注册')
|
||||
|
||||
return {
|
||||
app
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
"easycom": {
|
||||
"autoscan": true,
|
||||
"custom": {
|
||||
"^u-(.*)": "uview-plus/components/u-$1/u-$1.vue"
|
||||
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,7 +47,12 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="strategy-info-card" v-if="portfolioData.logicModel">
|
||||
<u-card
|
||||
v-if="portfolioData.logicModel"
|
||||
:border="false"
|
||||
:shadow="false"
|
||||
class="strategy-info-card"
|
||||
>
|
||||
<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>
|
||||
@ -66,7 +71,7 @@
|
||||
<text class="st-status-text">{{ portfolioData.logicModelStatus || '监控中' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-card>
|
||||
</view>
|
||||
|
||||
<view class="section-container">
|
||||
@ -76,7 +81,13 @@
|
||||
</view>
|
||||
|
||||
<view class="position-list">
|
||||
<view class="position-card" v-for="(item, index) in positions" :key="item.id || index">
|
||||
<u-card
|
||||
v-for="(item, index) in positions"
|
||||
:key="item.id || index"
|
||||
:border="false"
|
||||
:shadow="false"
|
||||
class="position-card"
|
||||
>
|
||||
<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'">
|
||||
@ -116,7 +127,7 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-card>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -175,14 +186,15 @@
|
||||
</view>
|
||||
|
||||
<view class="form-content">
|
||||
<view class="form-item relative">
|
||||
<view class="form-item">
|
||||
<text class="form-label">{{ transactionType === 'sell' ? '选择持仓' : '股票代码' }}</text>
|
||||
<input
|
||||
<u-search
|
||||
v-model="transactionForm.stockCode"
|
||||
class="form-input"
|
||||
:placeholder="transactionType === 'sell' ? '请选择要卖出的持仓' : '请输入股票代码'"
|
||||
:disabled="transactionType === 'sell'"
|
||||
@input="transactionType === 'buy' ? searchStock(e.detail.value) : () => {}"
|
||||
:show-action="false"
|
||||
:border="false"
|
||||
@input="transactionType === 'buy' ? searchStock($event) : () => {}"
|
||||
@click="transactionType === 'sell' ? (searchResults = (positions.value || []).map(pos => ({
|
||||
ticker: pos.stockCode,
|
||||
stockName: pos.stockName,
|
||||
@ -191,43 +203,42 @@
|
||||
amount: pos.amount,
|
||||
exchange: ''
|
||||
}))) : () => {}"
|
||||
/>
|
||||
<!-- 搜索下拉列表/持仓列表 -->
|
||||
<view class="search-dropdown" v-if="searchResults.length > 0">
|
||||
<view
|
||||
class="dropdown-item"
|
||||
>
|
||||
<!-- 搜索下拉列表 -->
|
||||
<view class="search-dropdown" v-if="searchResults.length > 0" slot="suffix">
|
||||
<u-dropdown>
|
||||
<u-dropdown-item
|
||||
v-for="(result, idx) in searchResults"
|
||||
:key="idx"
|
||||
:title="`${result.ticker || result.stockCode} ${result.name || result.stockName}`"
|
||||
@click="selectStock(result)"
|
||||
>
|
||||
<view class="item-left">
|
||||
<text class="item-ticker">{{ result.ticker || result.stockCode }}</text>
|
||||
<text class="item-name">{{ result.name || result.stockName }}</text>
|
||||
<text class="item-type" v-if="result.assetType">{{ result.assetType }}</text>
|
||||
</view>
|
||||
<text class="item-exchange">{{ result.exchange || '' }}</text>
|
||||
</u-dropdown-item>
|
||||
</u-dropdown>
|
||||
</view>
|
||||
</view>
|
||||
</u-search>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">数量{{ transactionType === 'sell' && maxSellAmount > 0 ? `(最多可卖 ${maxSellAmount} 份)` : '' }}</text>
|
||||
<input
|
||||
<u-input
|
||||
v-model="transactionForm.amount"
|
||||
class="form-input"
|
||||
type="number"
|
||||
:placeholder="transactionType === 'sell' && maxSellAmount > 0 ? `请输入数量,不超过 ${maxSellAmount}` : '请输入数量'"
|
||||
:border="false"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">价格</text>
|
||||
<input
|
||||
<u-input
|
||||
v-model="transactionForm.price"
|
||||
class="form-input"
|
||||
type="number"
|
||||
step="0.01"
|
||||
placeholder="请输入价格"
|
||||
:border="false"
|
||||
/>
|
||||
</view>
|
||||
|
||||
@ -235,20 +246,25 @@
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">交易时间</text>
|
||||
<picker mode="date" @change="onDateChange" :value="transactionForm.transactionDate">
|
||||
<u-datetime-picker
|
||||
v-model="showDatePicker"
|
||||
mode="date"
|
||||
:value="transactionForm.transactionDate"
|
||||
@confirm="onDateChange"
|
||||
>
|
||||
<view class="form-select">
|
||||
<text>{{ transactionForm.transactionDate }}</text>
|
||||
<uni-icons type="bottom" size="14" color="#9CA3AF"></uni-icons>
|
||||
<u-icon name="arrow-down" size="14" color="#9CA3AF"></u-icon>
|
||||
</view>
|
||||
</picker>
|
||||
</u-datetime-picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">备注</text>
|
||||
<input
|
||||
<u-input
|
||||
v-model="transactionForm.remark"
|
||||
class="form-input"
|
||||
placeholder="请输入备注"
|
||||
:border="false"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
@ -322,6 +338,8 @@ const transactionForm = ref({
|
||||
});
|
||||
// 当前选中持仓的最大可卖数量
|
||||
const maxSellAmount = ref(0);
|
||||
// 日期选择器显示状态
|
||||
const showDatePicker = ref(false);
|
||||
|
||||
// 货币选择相关
|
||||
const currencyList = ref([
|
||||
@ -337,7 +355,9 @@ const onCurrencyChange = (e) => {
|
||||
};
|
||||
|
||||
const onDateChange = (e) => {
|
||||
transactionForm.value.transactionDate = e.detail.value;
|
||||
// u-datetime-picker返回格式为YYYY-MM-DD
|
||||
transactionForm.value.transactionDate = e.value;
|
||||
showDatePicker.value = false;
|
||||
};
|
||||
|
||||
// 股票搜索相关
|
||||
@ -410,7 +430,7 @@ const fetchPortfolioData = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取投资组合数据失败:', error);
|
||||
uni.showToast({ title: '加载失败,请重试', icon: 'none' });
|
||||
uni.$u.toast.error('加载失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
@ -430,7 +450,7 @@ const fetchTransactions = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取交易记录失败:', error);
|
||||
uni.showToast({ title: '加载交易记录失败', icon: 'none' });
|
||||
uni.$u.toast.error('加载交易记录失败');
|
||||
}
|
||||
};
|
||||
|
||||
@ -488,18 +508,18 @@ const resetTransactionForm = () => {
|
||||
const submitTransaction = async () => {
|
||||
// 表单验证
|
||||
if (!transactionForm.value.stockCode) {
|
||||
return uni.showToast({ title: transactionType.value === 'sell' ? '请选择要卖出的持仓' : '请输入股票代码', icon: 'none' });
|
||||
return uni.$u.toast.error(transactionType.value === 'sell' ? '请选择要卖出的持仓' : '请输入股票代码');
|
||||
}
|
||||
const amount = parseFloat(transactionForm.value.amount);
|
||||
if (!amount || amount <= 0) {
|
||||
return uni.showToast({ title: '请输入有效的数量', icon: 'none' });
|
||||
return uni.$u.toast.error('请输入有效的数量');
|
||||
}
|
||||
// 卖出时校验数量不超过持仓
|
||||
if (transactionType.value === 'sell' && amount > maxSellAmount.value) {
|
||||
return uni.showToast({ title: `卖出数量不能超过持仓数量 ${maxSellAmount.value}`, icon: 'none' });
|
||||
return uni.$u.toast.error(`卖出数量不能超过持仓数量 ${maxSellAmount.value}`);
|
||||
}
|
||||
if (!transactionForm.value.price || parseFloat(transactionForm.value.price) <= 0) {
|
||||
return uni.showToast({ title: '请输入有效的价格', icon: 'none' });
|
||||
return uni.$u.toast.error('请输入有效的价格');
|
||||
}
|
||||
|
||||
const transactionData = {
|
||||
@ -513,13 +533,12 @@ const submitTransaction = async () => {
|
||||
remark: transactionForm.value.remark
|
||||
};
|
||||
|
||||
uni.showLoading({ title: '提交中...' });
|
||||
uni.$u.toast.loading('提交中...');
|
||||
|
||||
try {
|
||||
const response = await api.assets.createTransaction(transactionData);
|
||||
if (response.code === 200) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '交易提交成功', icon: 'success' });
|
||||
uni.$u.toast.success('交易提交成功');
|
||||
showTransactionForm.value = false;
|
||||
|
||||
// 重新获取交易记录
|
||||
@ -529,19 +548,20 @@ const submitTransaction = async () => {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建交易失败:', error);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '提交失败,请重试', icon: 'none' });
|
||||
uni.$u.toast.error('提交失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 删除组合
|
||||
const deletePortfolio = async () => {
|
||||
uni.showModal({
|
||||
uni.$u.showModal({
|
||||
title: '确认删除',
|
||||
content: '删除后所有持仓和交易记录都会丢失,确定要删除这个组合吗?',
|
||||
confirmText: '删除',
|
||||
confirmColor: '#EF4444',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({ title: '删除中' });
|
||||
uni.$u.toast.loading('删除中');
|
||||
try {
|
||||
// 调用删除组合接口
|
||||
const response = await uni.request({
|
||||
@ -553,15 +573,13 @@ const deletePortfolio = async () => {
|
||||
});
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '删除成功', icon: 'success' });
|
||||
uni.$u.toast.success('删除成功');
|
||||
setTimeout(() => uni.switchTab({ url: '/pages/index/index' }), 1500);
|
||||
} else {
|
||||
throw new Error('删除失败');
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '删除失败,请重试', icon: 'none' });
|
||||
uni.$u.toast.error('删除失败,请重试');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -639,14 +657,11 @@ const deletePortfolio = async () => {
|
||||
|
||||
/* 策略信息卡片 */
|
||||
.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 {
|
||||
@ -680,12 +695,9 @@ const deletePortfolio = async () => {
|
||||
|
||||
/* 持仓卡片 */
|
||||
.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; }
|
||||
@ -837,67 +849,7 @@ const deletePortfolio = async () => {
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
/* 搜索下拉列表 */
|
||||
.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;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
width: 100%;
|
||||
|
||||
3
uni.scss
3
uni.scss
@ -76,4 +76,5 @@ $uni-color-paragraph: #3F536E; // 文章段落颜色
|
||||
$uni-font-size-paragraph:15px;
|
||||
|
||||
/* 引入uView样式 */
|
||||
@import "uview-plus/index.scss";
|
||||
@import "uview-ui/theme.scss";
|
||||
@import "uview-ui/index.scss";
|
||||
|
||||
@ -17,10 +17,7 @@ let loginLock = null;
|
||||
let loadingCount = 0;
|
||||
const showLoading = () => {
|
||||
if (loadingCount === 0) {
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
});
|
||||
uni.$u.toast.loading('加载中...');
|
||||
}
|
||||
loadingCount++;
|
||||
};
|
||||
@ -28,7 +25,7 @@ const hideLoading = () => {
|
||||
loadingCount--;
|
||||
if (loadingCount <= 0) {
|
||||
loadingCount = 0;
|
||||
uni.hideLoading();
|
||||
uni.$u.toast.hide();
|
||||
}
|
||||
};
|
||||
|
||||
@ -199,7 +196,7 @@ const requestWithRetry = async (url, method = 'GET', data = {}, headers = {}, re
|
||||
return await requestWithRetry(url, method, data, headers, retryCount + 1);
|
||||
} else {
|
||||
console.error('❌ 达到最大重试次数');
|
||||
uni.showToast({ title: '系统异常,请稍后重试', icon: 'none', duration: 2000 });
|
||||
uni.$u.toast.error('系统异常,请稍后重试');
|
||||
throw new Error('登录已过期,重试次数超限');
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -4,6 +4,6 @@ import uni from '@dcloudio/vite-plugin-uni'
|
||||
export default defineConfig({
|
||||
plugins: [uni()],
|
||||
optimizeDeps: {
|
||||
include: ['uview-plus']
|
||||
include: ['uview-ui']
|
||||
}
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user