重构投资计算器和联系人视图,添加投资类型、金额、分红类型和投资日期的输入功能,并优化样式以提升用户体验。

This commit is contained in:
Phoenix 2025-05-26 10:35:11 +08:00
parent bdb29764be
commit c44a1bb1c1
8 changed files with 401 additions and 78 deletions

View File

@ -1,22 +1,63 @@
<script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import { ref } from 'vue'
import { NCard, NRadioGroup, NRadio, NInput, NDatePicker, NButton } from 'naive-ui'
const investmentType = ref('amount')
const amount = ref(10000)
const dividendType = ref('notReinvested')
const investmentDate = ref(null)
const handleSubmit = () => {
}
</script>
<template>
<header className="header">
1440
</header>
<main ref="main">
</main>
<footer>
</footer>
<div class="flex-center min-h-[70vh] animate-bg-move">
<n-card
class="w-[700px] glass-card animate-bounce-in shadow-xl border-none"
:content-style="{padding: '32px 32px'}"
:header-style="{background: 'transparent'}"
>
<div class="flex justify-between gap-6">
<!-- 投资类型 -->
<div class="flex-1">
<div class="text-lg font-bold mb-3">Investment Type</div>
<n-radio-group v-model:value="investmentType" name="investmentType">
<n-radio value="amount">Amount invested (in dollars)</n-radio>
<n-radio value="shares">Number of shares purchased</n-radio>
</n-radio-group>
</div>
<!-- 金额与分红 -->
<div class="flex-1">
<div class="text-lg font-bold mb-3">Amount to Calculate</div>
<n-input v-model:value="amount" type="number" class="mb-2" size="medium" placeholder="Enter amount" />
<n-radio-group v-model:value="dividendType" name="dividendType">
<n-radio value="reinvested">Dividends reinvested</n-radio>
<n-radio value="notReinvested">Dividends not reinvested</n-radio>
</n-radio-group>
</div>
<!-- 投资日期 -->
<div class="flex-1">
<div class="text-lg font-bold mb-3">Investment Date</div>
<n-date-picker v-model:value="investmentDate" type="date" class="w-full" size="medium" placeholder="Select date" />
</div>
</div>
<div class="flex justify-end mt-8">
<n-button type="primary" size="medium" class="px-8 py-2 rounded-full animate-bounce-in hover:scale-105 transition-transform duration-300 shadow-lg" @click="handleSubmit">
Submit
</n-button>
</div>
</n-card>
</div>
</template>
<style scoped lang="scss">
<style scoped>
.glass-card {
background: rgba(255,255,255,0.18);
box-shadow: 0 6px 24px 0 rgba(31, 38, 135, 0.25);
backdrop-filter: blur(10px);
border-radius: 24px;
border: 1px solid rgba(255,255,255,0.18);
}
</style>

View File

@ -1,22 +1,63 @@
<script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import { ref } from 'vue'
import { NCard, NRadioGroup, NRadio, NInput, NDatePicker, NButton } from 'naive-ui'
const investmentType = ref('amount')
const amount = ref(10000)
const dividendType = ref('notReinvested')
const investmentDate = ref(null)
const handleSubmit = () => {
}
</script>
<template>
<header className="header">
768
</header>
<main ref="main">
</main>
<footer>
</footer>
<div class="flex-center h-70vh px-4 py-8 animate-bg-move">
<n-card
class="w-full max-w-[900px] glass-card animate-bounce-in shadow-xl border-none"
:content-style="{padding: '32px 24px'}"
:header-style="{background: 'transparent'}"
>
<div class="flex flex-col gap-8">
<!-- 投资类型 -->
<div>
<div class="text-lg font-bold mb-3">Investment Type</div>
<n-radio-group v-model:value="investmentType" name="investmentType">
<n-radio value="amount">Amount invested (in dollars)</n-radio>
<n-radio value="shares">Number of shares purchased</n-radio>
</n-radio-group>
</div>
<!-- 金额与分红 -->
<div>
<div class="text-lg font-bold mb-3">Amount to Calculate</div>
<n-input v-model:value="amount" type="number" class="mb-3" size="large" placeholder="Enter amount" />
<n-radio-group v-model:value="dividendType" name="dividendType">
<n-radio value="reinvested">Dividends reinvested</n-radio>
<n-radio value="notReinvested">Dividends not reinvested</n-radio>
</n-radio-group>
</div>
<!-- 投资日期 -->
<div>
<div class="text-lg font-bold mb-3">Investment Date</div>
<n-date-picker v-model:value="investmentDate" type="date" class="w-full" size="large" placeholder="Select date" />
</div>
</div>
<div class="flex justify-end mt-8">
<n-button type="primary" size="large" class="px-8 py-2 rounded-full animate-bounce-in hover:scale-105 transition-transform duration-300 shadow" @click="handleSubmit">
Submit
</n-button>
</div>
</n-card>
</div>
</template>
<style scoped lang="scss">
<style scoped>
.glass-card {
background: rgba(255,255,255,0.18);
box-shadow: 0 6px 24px 0 rgba(31, 38, 135, 0.25);
backdrop-filter: blur(10px);
border-radius: 24px;
border: 1px solid rgba(255,255,255,0.18);
}
</style>

View File

@ -2,21 +2,44 @@
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue";
function copyEmail() {
navigator.clipboard.writeText('fiee@dlkadvisory.com');
}
</script>
<template>
<header className="header">
1440
</header>
<main ref="main">
<main ref="main" class="flex-center min-h-80vh bg-[url('@/assets/image/bg-pc.png')] rounded-3xl to-accent w-100vw animate-fade-in bg-[url('@/assets/image/bg-pc.png')]" >
<div class="w-full flex flex-col items-center gap-5 py-12 px-6">
<h1 class="text-4xl font-bold text-primary animate-fade-in-down animate-delay-0">Investor Contacts</h1>
<div class="text-2xl font-semibold text-gray-800 animate-fade-in-down animate-delay-200">FiEE Inc.</div>
<div class="text-xl text-secondary animate-fade-in-down animate-delay-400">Investor Relations</div>
<div class="text-lg text-gray-600 flex items-center gap-2 animate-fade-in-down animate-delay-600">
<span>Email:</span>
<span class="transition-colors duration-300 cursor-pointer text-accent hover:text-primary active:text-secondary select-all" @click="copyEmail">fiee@dlkadvisory.com</span>
</div>
</div>
</main>
<footer>
</footer>
</template>
<style scoped lang="scss">
/**** UnoCSS 动画补充(如未全局引入可在 uno.config.js 添加)****/
@keyframes fade-in-down {
0% {
opacity: 0;
transform: translateY(-30px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-down {
animation: fade-in-down 0.8s cubic-bezier(0.23, 1, 0.32, 1) both;
}
.animate-delay-0 { animation-delay: 0s; }
.animate-delay-200 { animation-delay: 0.2s; }
.animate-delay-400 { animation-delay: 0.4s; }
.animate-delay-600 { animation-delay: 0.6s; }
</style>

View File

@ -1,22 +1,41 @@
<script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue";
function copyEmail() {
navigator.clipboard.writeText('fiee@dlkadvisory.com');
}
</script>
<template>
<header className="header">
768
</header>
<main ref="main">
<main ref="main" class="flex flex-col items-center from-primary to-accent w-[100vw] mt-12 animate-fade-in px-6 py-10 pt-500px">
<div class="w-full flex flex-col items-center gap-5 px-4">
<h1 class="text-3xl font-bold text-primary animate-fade-in-down animate-delay-0">Investor Contacts</h1>
<div class="text-xl font-semibold text-gray-800 animate-fade-in-down animate-delay-200">FiEE Inc.</div>
<div class="text-lg text-secondary animate-fade-in-down animate-delay-400">Investor Relations</div>
<div class="text-base text-gray-600 flex items-center gap-2 animate-fade-in-down animate-delay-600">
<span>Email:</span>
<span class="transition-colors duration-300 cursor-pointer text-accent hover:text-primary active:text-secondary select-all" @click="copyEmail">fiee@dlkadvisory.com</span>
</div>
</div>
</main>
<footer>
</footer>
</template>
<style scoped lang="scss">
@keyframes fade-in-down {
0% {
opacity: 0;
transform: translateY(-20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-down {
animation: fade-in-down 0.8s cubic-bezier(0.23, 1, 0.32, 1) both;
}
.animate-delay-0 { animation-delay: 0s; }
.animate-delay-200 { animation-delay: 0.2s; }
.animate-delay-400 { animation-delay: 0.4s; }
.animate-delay-600 { animation-delay: 0.6s; }
</style>

View File

@ -1,22 +1,78 @@
<script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import { ref } from "vue";
const form = ref({
firstName: "",
lastName: "",
email: "",
company: "",
phone: "",
alertType: "all"
});
const submitted = ref(false);
function handleSubmit(e) {
e.preventDefault();
submitted.value = true;
}
</script>
<template>
<header className="header">
1440
</header>
<main ref="main">
<main ref="main" class="relative min-h-[80vh] flex-center bg-[url('@/assets/image/bg-pc.png')] overflow-hidden">
<!-- 粒子背景 -->
<div class="absolute inset-0 z-0 pointer-events-none animate-bg-move"></div>
<!-- 表单卡片/提交成功卡片 -->
<div class="relative z-10 w-[420px] max-w-[90vw] p-8 bg-white/80 rounded-2xl shadow-xl backdrop-blur-md animate-bounce-in">
<template v-if="!submitted">
<h2 class="text-2xl font-bold text-#8A5AFB mb-2 tracking-wide">E-Mail Alerts</h2>
<p class="text-sm text-gray-500 mb-5">* Required Fields</p>
<form class="space-y-3" @submit="handleSubmit">
<div>
<label class="block text-gray-700 font-semibold mb-1">* First Name</label>
<input v-model="form.firstName" type="text" class="w-full px-3 py-2 rounded-lg border border-gray-300 ring-2 ring-#8A5AFB/20) transition-all duration-300 outline-none bg-white/90" />
</div>
<div>
<label class="block text-gray-700 font-semibold mb-1">* Last Name</label>
<input v-model="form.lastName" type="text" class="w-full px-3 py-2 rounded-lg border border-gray-300 ring-2 ring-#8A5AFB/20) transition-all duration-300 outline-none bg-white/90" />
</div>
<div>
<label class="block text-gray-700 font-semibold mb-1">* Email</label>
<input v-model="form.email" type="email" class="w-full px-3 py-2 rounded-lg border border-gray-300 ring-2 ring-#8A5AFB/20) transition-all duration-300 outline-none bg-white/90" />
</div>
<div>
<label class="block text-gray-700 font-semibold mb-1">* Company</label>
<input v-model="form.company" type="text" class="w-full px-3 py-2 rounded-lg border border-gray-300 ring-2 ring-#8A5AFB/20) transition-all duration-300 outline-none bg-white/90" />
</div>
<div>
<label class="block text-gray-700 font-semibold mb-1">Phone</label>
<input v-model="form.phone" type="tel" class="w-full px-3 py-2 rounded-lg border border-gray-300 ring-2 ring-#8A5AFB/20) transition-all duration-300 outline-none bg-white/90" />
</div>
<button type="submit" class="w-full py-2.5 rounded-xl bg-#8A5AFB text-white font-bold text-base active:scale-95 transition-all duration-200 animate-bounce-in animate-delay-200">
Submit
</button>
</form>
</template>
<template v-else>
<div class="flex flex-col items-center justify-center min-h-[280px] animate-bounce-in">
<span class="i-mdi:check-circle-outline text-green-500 text-4xl mb-3"></span>
<h2 class="text-xl font-bold text-#8A5AFB mb-2">Submitted successfully!</h2>
<div class="text-gray-700 text-sm mb-3">The information you submitted is as follows:</div>
<div class="w-full bg-white/80 rounded-xl shadow p-3 space-y-2 text-gray-800 text-sm">
<div><span class="font-semibold">First Name</span>{{ form.firstName }}</div>
<div><span class="font-semibold">Last Name</span>{{ form.lastName }}</div>
<div><span class="font-semibold">Email</span>{{ form.email }}</div>
<div><span class="font-semibold">Company</span>{{ form.company }}</div>
<div><span class="font-semibold">Phone</span>{{ form.phone || 'Not filled in' }}</div>
<div><span class="font-semibold">Alert Type</span>{{ form.alertType === 'all' ? 'All Alerts' : 'Customize Alerts' }}</div>
</div>
</div>
</template>
</div>
</main>
<footer>
</footer>
</template>
<style scoped lang="scss">
/* 可选:自定义粒子或渐变动画背景 */
</style>

View File

@ -2,21 +2,78 @@
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue";
const form = ref({
firstName: '',
lastName: '',
email: '',
company: '',
phone: '',
alertType: 'all',
});
const submitted = ref(false);
function handleSubmit(e) {
e.preventDefault();
submitted.value = true;
}
</script>
<template>
<header className="header">
768
</header>
<main ref="main">
<main class="min-h-70vh flex flex-col items-center justify-center relative px-6 py-10">
<div class="w-[640px] max-w-90vw p-6 bg-white/95 rounded-2xl shadow-lg animate-bounce-in">
<template v-if="!submitted">
<h2 class="text-2xl font-bold text-#8A5AFB mb-3 text-center tracking-wide">E-Mail Alerts</h2>
<p class="text-sm text-gray-500 mb-5 text-center">* Required Fields</p>
<form class="flex flex-col gap-4" @submit="handleSubmit">
<div>
<label class="block text-gray-700 font-semibold mb-1.5 text-base">* First Name</label>
<input v-model="form.firstName" type="text" class="w-full px-4 py-2.5 rounded-lg border border-gray-300 outline-none bg-white/90 text-base" />
</div>
<div>
<label class="block text-gray-700 font-semibold mb-1.5 text-base">* Last Name</label>
<input v-model="form.lastName" type="text" class="w-full px-4 py-2.5 rounded-lg border border-gray-300 outline-none bg-white/90 text-base" />
</div>
<div>
<label class="block text-gray-700 font-semibold mb-1.5 text-base">* Email</label>
<input v-model="form.email" type="email" class="w-full px-4 py-2.5 rounded-lg border border-gray-300 outline-none bg-white/90 text-base" />
</div>
<div>
<label class="block text-gray-700 font-semibold mb-1.5 text-base">* Company</label>
<input v-model="form.company" type="text" class="w-full px-4 py-2.5 rounded-lg border border-gray-300 outline-none bg-white/90 text-base" />
</div>
<div>
<label class="block text-gray-700 font-semibold mb-1.5 text-base">Phone</label>
<input v-model="form.phone" type="tel" class="w-full px-4 py-2.5 rounded-lg border border-gray-300 outline-none bg-white/90 text-base" />
</div>
<button type="submit" class="w-full py-3.5 rounded-xl bg-#8A5AFB text-white font-bold text-lg active:scale-95 transition-all duration-200 animate-bounce-in animate-delay-200 mt-3">
Submit
</button>
</form>
</template>
<template v-else>
<div class="flex flex-col items-center justify-center min-h-[240px] animate-bounce-in">
<span class="i-mdi:check-circle-outline text-green-500 text-5xl mb-4"></span>
<h2 class="text-xl font-bold text-#8A5AFB mb-3">Submitted successfully!</h2>
<div class="text-gray-700 text-base mb-4">The information you submitted is as follows:</div>
<div class="w-full bg-white/90 rounded-xl shadow p-4 space-y-2 text-gray-800 text-base">
<div><span class="font-semibold">First Name</span>{{ form.firstName }}</div>
<div><span class="font-semibold">Last Name</span>{{ form.lastName }}</div>
<div><span class="font-semibold">Email</span>{{ form.email }}</div>
<div><span class="font-semibold">Company</span>{{ form.company }}</div>
<div><span class="font-semibold">Phone</span>{{ form.phone || '(Not filled)' }}</div>
<div><span class="font-semibold">Alert Type</span>{{ form.alertType === 'all' ? 'All Alerts' : 'Customize Alerts' }}</div>
</div>
</div>
</template>
</div>
</main>
</main>
<footer>
</footer>
</template>
<style scoped lang="scss">
/* Keep tablet background simple */
</style>

View File

@ -1,22 +1,62 @@
<script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import {useStockQuote} from '@/store/stock-quote/index.js'
const {getStockQuate,stockQuote,formatted}=useStockQuote()
getStockQuate()
</script>
<template>
<header className="header">
1440
</header>
<main ref="main">
<main ref="main" class="flex pt-80px flex-col md:flex-row justify-center items-center gap-24 rounded-3xl">
<!-- 左侧大号价格 -->
<section class="flex flex-col items-center justify-center glass-card p-24 rounded-2xl shadow-xl">
<div class="text-8xl font-extrabold text-#8A5AFB animate-bg-move select-none drop-shadow-lg">${{ stockQuote.change?.[0].slice(0,4) }}</div>
<div class="mt-8 text-2xl text-gray-500 font-semibold tracking-widest mb-8px">NASDAQ: <span class="text-black">MINM</span></div>
<div class="text-gray-500">{{ formatted }}</div>
</section>
<!-- 右侧信息卡片 -->
<section class="grid grid-cols-2 gap-12">
<div class="info-card">
<div class="text-base text-gray-400">Open</div>
<div class="text-2xl font-bold">{{ stockQuote.Open }}</div>
</div>
<div class="info-card">
<div class="text-base text-gray-400">Change</div>
<div class="text-2xl font-bold text-red-500">{{ stockQuote.change?.join('') }}</div>
</div>
<div class="info-card">
<div class="text-base text-gray-400">Day's Range</div>
<div class="text-2xl font-bold">{{ stockQuote.DaysRange }}</div>
</div>
<div class="info-card">
<div class="text-base text-gray-400">52-Week Range</div>
<div class="text-2xl font-bold">{{ stockQuote.Week52Range }}</div>
</div>
<div class="info-card">
<div class="text-base text-gray-400">Volume</div>
<div class="text-2xl font-bold">{{ stockQuote.Volume }}</div>
</div>
<div class="info-card">
<div class="text-base text-gray-400">Market Cap</div>
<div class="text-2xl font-bold">{{ stockQuote.MarketCap }}</div>
</div>
</section>
</main>
<footer>
</footer>
</template>
<style scoped lang="scss">
/* 玻璃拟态和卡片动画可用 UnoCSS 快捷方式实现,若未配置可加如下样式 */
.glass-card {
backdrop-filter: blur(16px);
background: rgba(255,255,255,0.6);
border: 1px solid rgba(255,255,255,0.3);
box-shadow: 0 8px 32px 0 rgba(31,38,135,0.18);
}
.info-card {
@apply glass-card p-5 rounded-xl flex flex-col items-start gap-1 hover:scale-105 transition-transform duration-300;
}
</style>

View File

@ -1,22 +1,68 @@
<script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import { useStockQuote } from '@/store/stock-quote/index.js';
const { getStockQuate, stockQuote ,formatted} = useStockQuote();
getStockQuate();
</script>
<template>
<header className="header">
768
</header>
<main ref="main">
<main class="min-h-60vh flex flex-col items-center justify-start px-4 py-6 pt-500px">
<!-- 价格卡片 -->
<section class="w-full max-w-80vw flex flex-col items-center justify-center glass-card p-6 rounded-2xl shadow mb-6">
<div class="text-5xl font-extrabold text-#8A5AFB animate-bg-move select-none drop-shadow-lg">${{ stockQuote.change?.[0].slice(0,4) }}</div>
<div class="mt-3 text-base text-gray-500 font-semibold tracking-widest mb-0px">NASDAQ: <span class="text-black">MINM</span></div>
<div class="text-gray-500 text-70px">{{ formatted }}</div>
</section>
<!-- 信息卡片列表 -->
<section class="w-full max-w-80vw grid grid-cols-3 gap-4">
<div class="info-card">
<div class="text-sm text-gray-400">Open</div>
<div class="text-xl font-bold">{{ stockQuote.Open }}</div>
</div>
<div class="info-card">
<div class="text-sm text-gray-400">Change</div>
<div class="text-xl font-bold text-red-500">{{ stockQuote.change?.join('') }}</div>
</div>
<div class="info-card">
<div class="text-sm text-gray-400">Day's Range</div>
<div class="text-xl font-bold">{{ stockQuote.DaysRange }}</div>
</div>
<div class="info-card">
<div class="text-sm text-gray-400">52-Week Range</div>
<div class="text-xl font-bold">{{ stockQuote.Week52Range }}</div>
</div>
<div class="info-card">
<div class="text-sm text-gray-400">Volume</div>
<div class="text-xl font-bold">{{ stockQuote.Volume }}</div>
</div>
<div class="info-card">
<div class="text-sm text-gray-400">Market Cap</div>
<div class="text-xl font-bold">{{ stockQuote.MarketCap }}</div>
</div>
</section>
</main>
</main>
<footer>
</footer>
</template>
<style scoped lang="scss">
.glass-card {
backdrop-filter: blur(10px);
background: rgba(255,255,255,0.92);
border: 1px solid rgba(200,200,255,0.18);
box-shadow: 0 3px 12px 0 rgba(31,38,135,0.08);
}
.info-card {
background: rgba(255,255,255,0.95);
border-radius: 16px;
box-shadow: 0 2px 6px 0 rgba(31,38,135,0.06);
padding: 16px 14px;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
</style>