本文介绍创建Vue3项目的流程:首先通过npm create vue@latest命令执行项目创建,过程中可按需配置TypeScript、JSX、Vue Router、Pinia、Vitest等官方支持的配套工具,未配置的功能后续可补充添加,项目创建完成后,需执行npm install安装依赖,再通过npm run dev启动开发服务器,文中还说明了Vue3项目的目录结构,包括存放源码的src、存放静态资源的public等核心目录的作用,帮助开发者快速了解项目框架。
在区块链应用开发领域,数字钱包是用户管理链上数字资产的核心入口,imToken作为全球知名的多链数字钱包工具,其极简的界面设计、流畅的交互体验与严谨的隐私保护逻辑,一直是前端开发者搭建Web3应用的参考标杆,本文将详细分享如何基于现代前端技术栈,从零实现一个仿imToken的数字钱包界面,覆盖核心功能模块的设计思路、代码实现与体验优化方案。
技术选型与项目初始化
搭建仿imToken前端界面时,我们优先选择轻量、高效且生态完善的技术栈,兼顾开发效率与运行性能:
- 框架:Vue3 + Vite(Vue3的组合式API提升代码可维护性,Vite的热更新能力大幅加快开发迭代速度)
- 样式方案:Tailwind CSS(原子化CSS框架,通过预设类名快速构建响应式界面,减少自定义CSS代码量)
- 状态管理:Pinia(轻量直观的状态管理库,用于统一管理钱包账户、资产数据、用户偏好等全局状态)
- 路由管理:Vue Router(实现钱包首页、资产页、收款页、转账页等多页面的跳转与参数传递)
- 工具库:qrcode(生成收款二维码)、@vueuse/core(提供触摸事件、剪贴板操作等通用工具函数)
项目初始化的完整步骤如下:
# 创建项目文件夹并进入 mkdir imtoken-clone && cd imtoken-clone # 初始化npm项目 npm init -y # 安装核心依赖 npm install vue vue-router pinia tailwindcss postcss autoprefixer qrcode @vueuse/core # 安装Vite与Vue插件 npm install -D vite @vitejs/plugin-vue # 初始化Tailwind CSS配置文件 npx tailwindcss init -p
初始化完成后,需在tailwind.config.js扫描路径,确保Tailwind能识别项目中的类名:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
核心界面模块实现
仿imToken的核心界面围绕用户资产管理流程设计,主要包含钱包首页、资产列表页、收款页、转账页四大模块,以下是各模块的关键实现细节:
钱包首页布局
钱包首页是用户打开应用的首屏,核心目标是快速展示资产概况与高频操作入口,我们采用「上下分层+左右分栏」的布局逻辑:顶部为账户切换与功能入口栏,中部为资产总览卡片,底部为快捷操作按钮组,整体遵循imToken的「信息层级优先」设计原则。
<!-- src/views/Home.vue -->
<template>
<div class="min-h-screen bg-gray-50 pb-8">
<!-- 顶部账户栏 -->
<div class="bg-gradient-to-r from-blue-600 to-blue-500 text-white p-4 flex justify-between items-center shadow-sm">
<div class="flex items-center cursor-pointer hover:opacity-90 transition-opacity">
<img src="/assets/avatar.png" alt="用户头像" class="w-8 h-8 rounded-full mr-2 border border-white/20">
<span class="font-medium">我的钱包</span>
<svg class="ml-1 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</div>
<svg class="w-6 h-6 cursor-pointer hover:opacity-90 transition-opacity" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z"></path>
</svg>
</div>
<!-- 资产总览卡片 -->
<div class="p-4 mt-2">
<div class="bg-white rounded-xl p-6 shadow-sm border border-gray-100">
<div class="flex items-center justify-between">
<p class="text-gray-500 text-sm">总资产(CNY)</p>
<button @click="toggleAssetVisibility" class="text-gray-400 hover:text-gray-600 transition-colors">
<svg v-if="isAssetVisible" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
<svg v-else class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>
</svg>
</button>
</div>
<p class="text-3xl font-bold mt-2 text-gray-900">
{{ isAssetVisible ? '¥ 12,345.67' : '¥ ****' }}
</p>
<div class="flex items-center mt-3 text-sm">
<span class="text-green-500 font-medium">+ ¥ 123.45(1.01%)</span>
<span class="text-gray-400 ml-2">近7日</span>
</div>
</div>
</div>
<!-- 快捷操作按钮组 -->
<div class="px-4 mt-4">
<div class="grid grid-cols-4 gap-3 bg-white rounded-xl p-4 shadow-sm border border-gray-100">
<div class="flex flex-col items-center cursor-pointer hover:bg-gray-50 p-2 rounded-lg transition-colors">
<svg class="w-7 h-7 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
<span class="text-xs mt-1 text-gray-700">收款</span>
</div>
<div class="flex flex-col items-center cursor-pointer hover:bg-gray-50 p-2 rounded-lg transition-colors">
<svg class="w-7 h-7 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path>
</svg>
<span class="text-xs mt-1 text-gray-700">转账</span>
</div>
<div class="flex flex-col items-center cursor-pointer hover:bg-gray-50 p-2 rounded-lg transition-colors">
<svg class="w-7 h-7 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"></path>
</svg>
<span class="text-xs mt-1 text-gray-700">兑换</span>
</div>
<div class="flex flex-col items-center cursor-pointer hover:bg-gray-50 p-2 rounded-lg transition-colors">
<svg class="w-7 h-7 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h.01M12 12h.01M19 12h.01M6 12a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z"></path>
</svg>
<span class="text-xs mt-1 text-gray-700">更多</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
// 资产显示状态
const isAssetVisible = ref(true)
const toggleAssetVisibility = () => {
isAssetVisible.value = !isAssetVisible.value
}
</script>
资产列表页
资产列表页用于展示用户持有的所有代币详情,包括代币名称、余额、法币价值与涨跌幅,我们通过模拟数据渲染列表,并补充搜索、下拉刷新等交互,贴近真实钱包的使用场景。
<!-- src/views/Assets.vue -->
<template>
<div class="min-h-screen bg-gray-50 p-4">
<!-- 页面头部 -->
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-bold text-gray-900">我的资产</h2>
<div class="relative">
<input
type="text"
placeholder="搜索代币"
class="pl-8 pr-3 py-1.5 rounded-lg border border-gray-200 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500"
v-model="searchKeyword"
>
<svg class="w-4 h-4 text-gray-400 absolute left-2.5 top-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</div>
</div>
<!-- 资产列表 -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<div
v-for="asset in filteredAssetList"
:key="asset.symbol"
class="flex items-center justify-between p-4 border-b border-gray-100 last:border-b-0 hover:bg-gray-50 transition-colors cursor-pointer"
>
<div class="flex items-center">
<img :src="asset.logo" :alt="asset.name" class="w-10 h-10 rounded-full mr-3">
<div>
<p class="font-medium text-gray-900">{{ asset.name }}</p>
<p class="text-xs text-gray-500">{{ asset.balance }} {{ asset.symbol }}</p>
</div>
</div>
<div class="text-right">
<p class="font-medium text-gray-900">¥ {{ asset.value.toFixed(2) }}</p>
<p :class="asset.change >= 0 ? 'text-green-500 text-xs' : 'text-red-500 text-xs'">
{{ asset.change >= 0 ? '+' : '' }}{{ asset.change }}%
</p>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
// 搜索关键词
const searchKeyword = ref('')
// 模拟资产数据
const assetList = ref([
{ name: '比特币', symbol: 'BTC', balance: 0.05, value: 14500, change: 2.3, logo: '/assets/btc.png' },
{ name: '以太坊', symbol: 'ETH', balance: 1.2, value: 9600, change: -1.2, logo: '/assets/eth.png' },
{ name: '泰达币', symbol: 'USDT', balance: 1000, value: 7200, change: 0.01, logo: '/assets/usdt.png' },
{ name: '币安币', symbol: 'BNB', balance: 2.5, value: 3125, change: 1.8, logo: '/assets/bnb.png' }
])
// 过滤后的资产列表
const filteredAssetList = computed(() => {
if (!searchKeyword.value) return assetList.value
return assetList.value.filter(asset =>
asset.name.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
asset.symbol.toLowerCase().includes(searchKeyword.value.toLowerCase())
)
})
</script>
收款页实现
收款页的核心功能是展示用户的钱包地址与对应二维码,方便他人扫码转账,我们使用qrcode库生成高清二维码,并补充地址复制、二维码保存等实用功能,同时
转载请注明出处:qbadmin,如有疑问,请联系()。
本文地址:https://www.sdyyyy.net/swxk/8144.html
