prompt项目
This commit is contained in:
133
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_binance_account.md
Normal file
133
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_binance_account.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# 页面地址 [BinanceAccountView.vue](TonyMask/src/pages/BinanceAccountView.vue)
|
||||
|
||||
# 页面设计
|
||||
|
||||
- 需要在 Header 右侧显示当前 用户名(加粗,加大显示)
|
||||
- 从已登录信息中获取当前用户名
|
||||
|
||||
# 页面功能
|
||||
|
||||
## 接口信息
|
||||
|
||||
- 接口地址 GET /api/binance/account/info
|
||||
- 接口返回的结构体
|
||||
|
||||
```go
|
||||
// Account define account info
|
||||
type Account struct {
|
||||
MakerCommission int64 `json:"makerCommission"`
|
||||
TakerCommission int64 `json:"takerCommission"`
|
||||
BuyerCommission int64 `json:"buyerCommission"`
|
||||
SellerCommission int64 `json:"sellerCommission"`
|
||||
CommissionRates CommissionRates `json:"commissionRates"`
|
||||
CanTrade bool `json:"canTrade"`
|
||||
CanWithdraw bool `json:"canWithdraw"`
|
||||
CanDeposit bool `json:"canDeposit"`
|
||||
UpdateTime uint64 `json:"updateTime"`
|
||||
AccountType string `json:"accountType"`
|
||||
Balances []Balance `json:"balances"`
|
||||
Permissions []string `json:"permissions"`
|
||||
UID int64 `json:"uid"`
|
||||
}
|
||||
|
||||
type CommissionRates struct {
|
||||
Maker string `json:"maker"`
|
||||
Taker string `json:"taker"`
|
||||
Buyer string `json:"buyer"`
|
||||
Seller string `json:"seller"`
|
||||
}
|
||||
|
||||
// Balance define user balance of your account
|
||||
type Balance struct {
|
||||
Asset string `json:"asset"`
|
||||
Free string `json:"free"`
|
||||
Locked string `json:"locked"`
|
||||
}
|
||||
```
|
||||
|
||||
## 账户支持的交易对信息
|
||||
|
||||
### 账户支持的交易对模块
|
||||
|
||||
- 查看状态
|
||||
- 使用隐藏的输入框,包含胶囊按钮状交易对信息
|
||||
- 输入框隐藏,不可见
|
||||
-
|
||||
- 账户支持的交易对信息,通过如下接口获取
|
||||
- 接口地址 GET /api/binance/account/symbols/get
|
||||
- 胶囊按钮状显示每一个支持的交易对信息
|
||||
- 包含 交易对图标 简称
|
||||
- 图标信息通过[CryptoIcon.vue](TonyMask/src/components/CryptoIcon.vue)获取
|
||||
- 右侧有编辑按钮,点击编辑之后进入编辑模式
|
||||
- 隐藏输入框出现
|
||||
- 胶囊按钮出现删除按键
|
||||
- 右侧编辑按键变为保存按键
|
||||
|
||||
### 添加账户支持加密货币
|
||||
|
||||
- 账户支持的交易对模块进入编辑模式后
|
||||
- 空白侧支持点击
|
||||
- 点击之后,可以使用[CurrencySelector.vue](/TonyMask/src/components/CurrencySelector.vue)进行交易对选择
|
||||
- 选择之后,立即调用添加接口
|
||||
- 接口地址 POST /api/binance/account/symbols/add
|
||||
- 当点击保存按钮之后,退出编辑模式
|
||||
|
||||
### 删除账户支持的加密货币
|
||||
|
||||
- 账户支持的交易对模块进入编辑模式后
|
||||
- 可以通过胶囊按钮的删除键进行删除
|
||||
- 点击删除按键后,立刻调用删除接口
|
||||
- 接口地址 POST /api/binance/account/symbols/delete
|
||||
- 当点击保存按钮之后,退出编辑模式
|
||||
|
||||
## 账户信息模块
|
||||
- 使用矩形长条状展示账户信息
|
||||
- 账户UID
|
||||
- 账户类型
|
||||
- 能否交易
|
||||
- 能否提款
|
||||
- 能够充值
|
||||
- 交易手续费
|
||||
- 账户权限
|
||||
|
||||
## 资产详情模块
|
||||
|
||||
- 使用表格的形式展示当前用户的资产信息
|
||||
|
||||
### 表格设计
|
||||
|
||||
- 表格头从左到右依次展示
|
||||
|
||||
- 文字输入框-筛选功能-快速根据币种简称 币种名称筛选到币种
|
||||
- 刷新按钮
|
||||
- 用于刷新调用币种实时价格
|
||||
- 汇总计算-当前账户总资产-累加每一行的资产总计
|
||||
- 加粗显示
|
||||
|
||||
- 表格内容展示
|
||||
|
||||
- 统一采用四舍五入保存两位小数
|
||||
- 统一不显示单位
|
||||
|
||||
- 按照如下的顺序展示列信息
|
||||
- 币种图标
|
||||
- 不显示表头
|
||||
- 币种大写名称
|
||||
- 表头显示名称
|
||||
- 币种实时价格
|
||||
- 表头显示实时价格
|
||||
- 币种总数量
|
||||
- 表头显示总数量
|
||||
- 币种可使用数量
|
||||
- 表头显示可使用数量
|
||||
- 币种冻结数量
|
||||
- 表头显示冻结数量
|
||||
- 币种总计
|
||||
- 表头显示币种总计
|
||||
- 该行币种总数量与币种的实时价格相乘的结果
|
||||
|
||||
### 币种实时价格列展示
|
||||
|
||||
- 使用 symbols 参数,将 account_info 接口中的所有币种,一次查询
|
||||
- 查询币种的实时价格 接口地址为 POST /api/binance/market/ticker/price
|
||||
- 具体函数为 [binance_market_handler.go](TonyCryptoOctopus\handler\binance_market_handler.go)中的 GetPriceTickerHandler 方法
|
||||
46
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_binance_order.md
Normal file
46
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_binance_order.md
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
# 页面地址 [BinanceOrder.vue](TonyMask/src/pages/BinanceOrder.vue)
|
||||
- 页面在侧边栏的名称为 交易订单
|
||||
- 在主内容区的页面Header部分 靠右侧显示 交易订单
|
||||
- 展示特定交易对,特定查询条件下的订单情况
|
||||
|
||||
|
||||
# 页面设计
|
||||
## 订单查询栏
|
||||
- 接口参数为 [binance_order_handler.go](TonyCryptoOctopus\handler\binance_order_handler.go) 中的MyTradesRequest
|
||||
- 接口参数说明: 前端不呈现limit,固定为1000
|
||||
- 忽略OrderID和FromID,不需要此参数
|
||||
- 默认起始时间为 2025-01-01 08:00:00
|
||||
- 新增固定时间段,最近三个月,最近一个月,最近一年,最近三年,当点击查询时候,根据当前时间作为endTime推算起始时间
|
||||
- 取消固定时间选择之后,恢复到默认时间
|
||||
|
||||
### 币种选择器
|
||||
- 使用通用组件中的CurrencySelector
|
||||
- 根据页面样式,合理调整大小
|
||||
|
||||
### 订单接口数据
|
||||
- 接口地址 /api/binance/order/trades/save 查询
|
||||
- 具体接口为 [binance_order_handler.go](TonyCryptoOctopus\handler\binance_order_handler.go)中的GetMyTradesHandler
|
||||
|
||||
### 文件保存地址栏
|
||||
- 需要显示返回接口中的的message字段内容
|
||||
- message字段的内容是本地保存的订单数据文件地址
|
||||
- 点击查询之后,在订单查询栏最下面出现
|
||||
- 居中显示
|
||||
|
||||
## 订单展示区
|
||||
|
||||
### 时间区域展示
|
||||
- 根据订单的cst_time,展示时间轴
|
||||
- 每笔订单在时间轴上是一个小点,点击可以筛选到该笔订单
|
||||
- 时间轴可以划取范围,通过时间段筛选订单
|
||||
- 时间轴的范围自动根据最早和最晚交易时间进行修改
|
||||
- 时间轴的间距需要根据时间跨度,自动设置,便于美观
|
||||
|
||||
### 订单列表区
|
||||
- 支持按照时间顺序排序,支持倒序或者正序排列
|
||||
- 可以筛选卖出和买入方向的订单
|
||||
- 设置分页展示
|
||||
- 分页默认大小为20
|
||||
- 分页的可选大小为20 50 100
|
||||
32
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_login_page.md
Normal file
32
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_login_page.md
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
# 页面设计
|
||||
- 传统的登录页面,即打开前端网址出现的页面
|
||||
- 居中显示登录模块
|
||||
- 最上面显示 图标 ProjectTonyStack
|
||||
- 下面依次显示 用户名 用户名输入栏 密码 密码输入栏
|
||||
-
|
||||
### 密码输入栏
|
||||
- 密码输入之后应该默认是隐藏的
|
||||
- 密码输入栏最右边有隐藏显示图标,点击之后可以切换密码隐藏或明文显示
|
||||
|
||||
## 加密传输
|
||||
- 登录的密码向后端传递时应该采用加密的方式
|
||||
- 请在前后端之间设计一种加密传输用户密码的方式
|
||||
- 可以假设密钥不会外泄
|
||||
- 非对称加密的方式可行,但是有点复杂
|
||||
|
||||
## 页面功能
|
||||
- 当用户在调用其他接口时,ApiResponse后端标准返回体,code为10003的情况时,给出报错之后,2秒钟后重新跳转到登录页
|
||||
- 用户可以输入用户名,密码
|
||||
- 用户名 密码是必填项
|
||||
- 用户可以点击登录按钮进行登录
|
||||
- 将用户名 密码加密向后端传输,调用登录接口
|
||||
- 登录接口地址 POST /api/user/login
|
||||
|
||||
### 登录成功
|
||||
- 登录成功后,后端返回的ApiResponse的code为0时,ApiResponse中的data即为token
|
||||
- 需要将Token附加到后续所有请求的Header中, BearToken Auth的方式
|
||||
-
|
||||
### 登录失败
|
||||
- 当ApiResponse的code不为0时,直接红字提示 用户名或密码错误!
|
||||
37
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_profit-loss.md
Normal file
37
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_profit-loss.md
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
|
||||
# 盈亏大屏页面 [ProfitAndLoss.vue](mdc:TonyMask/src/pages/ProfitAndLoss.vue)
|
||||
- 与后端进行 盈亏分析 数据的请求为 [profit_loss.ts](mdc:TonyMask/src/api/profit_loss.ts)
|
||||
- 请求选定的时间,默认为东八区的时间,起始时间修正为00:00:00,终止时间修正为 23:59:59
|
||||
|
||||
## 数据接口及格式
|
||||
- 数据接口 /api/profit-loss/aggregation
|
||||
- 数据格式为 [profit_and_loss.go](mdc:TonyProfitLoss/models/profit_and_loss.go)
|
||||
- 数据统一不显示单位,不显示美元标识符
|
||||
|
||||
## 页面设计
|
||||
- 按照下面的顺序依次呈现
|
||||
|
||||
### 币种选择
|
||||
- 币种选择栏 下拉框点击之后,币种图标出现在选择框中,具有删除按键
|
||||
- 下拉框出现,各个币种的图标及名称,点击的方式选取需要计算盈亏的币种
|
||||
- 选择框右边出现计算按钮
|
||||
|
||||
### 总体盈亏情况
|
||||
|
||||
- 使用绿色代表盈亏大于0,红色代表盈亏小于0
|
||||
|
||||
- 展示已实现盈亏,未实现盈亏,总盈亏
|
||||
|
||||
- 确保下列柱状图的纵坐标具有可变性,符合人类的观看习惯,纵坐标轴只显示整数数值,纵坐标可能出现负数
|
||||
- 按照柱状图,按照从大到小的顺序,展示查询币种的收益率
|
||||
- 按照柱状图,按照从大到小的顺序,展示查询币种的年化收益率
|
||||
- 按照柱状图,按照从大到小的顺序,展示查询币种的总盈亏数据
|
||||
- 按照柱状图,按照从大到小的顺序,展示查询币种的已实现盈亏数据
|
||||
- 按照柱状图,按照从大到小的顺序,展示查询币种的未实现盈亏数据
|
||||
|
||||
|
||||
<!-- ### 单一币种盈亏情况
|
||||
- 页面下方使用横条展示 币种图标及名称 点击即可跳转单一币种盈亏分析页面 [SingleProfitAndLoss.vue](mdc:TonyMask/src/pages/SingleProfitAndLoss.vue)
|
||||
- 数据来源接口 /api/profit-loss/aggregation 中包括此部分数据
|
||||
- 跳转时,附带单一币种的盈亏数据,传递给 单一币种盈亏分析页面 -->
|
||||
42
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_single-pnl.md
Normal file
42
3.1-ProjectTonyStack-前端/1-原始设计/1-页面/mask_single-pnl.md
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
# 单一币种盈亏分析大屏 [SingleProfitAndLoss.vue](TonyMask/src/pages/SingleProfitAndLoss.vue)
|
||||
|
||||
## 数据接口及格式
|
||||
- 数据接口 POST /api/profit-loss/single
|
||||
- 数据返回格式 [profit_and_loss.go](TonyProfitLoss/models/profit_and_loss.go)
|
||||
- 数据统一不显示单位,不显示美元标识符
|
||||
|
||||
## 页面设计
|
||||
|
||||
## 头部
|
||||
- 显示文字 单一币种盈亏分析
|
||||
- 靠右显示
|
||||
|
||||
### 币种选择器
|
||||
- 使用通用组件中的CurrencySelector
|
||||
- 根据页面样式,合理调整大小
|
||||
|
||||
## 单一币种盈亏分析展示
|
||||
- 使用绿色代表盈亏大于0,红色代表盈亏小于0
|
||||
|
||||
- 下面的每条内容,展示在同一行中
|
||||
- 交易时间,最早交易时间,最晚交易时间,跨度天数(day_duration)
|
||||
- 总盈亏(total_pnl)
|
||||
- 已实现盈亏,未实现盈亏
|
||||
- 展示收益率(total_pnl_percentage)
|
||||
- 年化收益率(annualized_pnl_percentage) 相对年化收益率(annualized_pnl_percentage_real)
|
||||
- 总交易次数 总交易金额 总交易数量(total_trade_quantity)
|
||||
- 手续费 设置列表展示,小图标+手续费金额
|
||||
|
||||
|
||||
## 买入方向数据
|
||||
- 买入数量
|
||||
- 买入总金额
|
||||
- 买入订单数量
|
||||
- 平均买入价格
|
||||
|
||||
## 卖出方向数据
|
||||
- 卖出数量
|
||||
- 卖出总金额
|
||||
- 卖出订单数量
|
||||
- 平均卖出价格
|
||||
47
3.1-ProjectTonyStack-前端/1-原始设计/2-组件/mask_crypto_icon.md
Normal file
47
3.1-ProjectTonyStack-前端/1-原始设计/2-组件/mask_crypto_icon.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 前端图标模组
|
||||
- 此模组作为前端项目的公共模组,可以供其他页面使用
|
||||
|
||||
## 样式说明
|
||||
- 图标只需要满足明亮模式
|
||||
- 图标的大小是可以调整的
|
||||
- 图标应该采用svg的格式
|
||||
|
||||
## 图标管理
|
||||
|
||||
### 图标获取
|
||||
- 通过从[TradingView所有加密货币](https://cn.tradingview.com/markets/cryptocurrencies/prices-all/) 或者从稳定、公开的加密货币API(例如 CoinGecko 或 CoinMarketCap 的免费API)通过手动执行,获取前1000名加密货币的数据。
|
||||
- 按照市值排序,获取排名前一千的加密货币
|
||||
- 加密货币的全称 和 简称同样从此页面获取
|
||||
|
||||
### 图标存储
|
||||
- 常见加密货币创建svg的图标数据存储起来,便其他的页面调用
|
||||
- 存储结构体包含
|
||||
- 加密货币全称 Bitcoin
|
||||
- 加密货币大写全称 BITCOIN
|
||||
- 加密货币简称 BTC
|
||||
- 加密货币描述 Bitcoin is balabala
|
||||
- 加密货币svg图标内容
|
||||
- 应该有未知货币的图标
|
||||
- 应该设计类似Map的结构,可以快速通过加密货币名称获取到对应图标
|
||||
|
||||
### 图标查询
|
||||
- 设计一个函数,能够通过交易对,前缀匹配,大写名称,加密货币简称均可查询到加密货币全称,即多对一的查询函数
|
||||
- 查询速度应该在O(n)以下
|
||||
|
||||
### 交易对图标
|
||||
- 交易对图标,仿照[TradingView交易对页面](https://cn.tradingview.com/symbols/BTCUSDT/?exchange=BINANCE)页面中图标放置的样式,将两种加密货币的图标进行叠放显示
|
||||
- 构建查用的交易对图标缓存Map,可以通过交易对名称快速获取到对应的图标
|
||||
- 常用交易对如下 BTCUSDT ETHUSDT BNBUSDT NEXOUSDT XRPUSDT LINKUSDT SOLUSDT
|
||||
- 此函数需要考虑后续能够手动添加其他的交易对
|
||||
|
||||
|
||||
## 加密货币名称
|
||||
### 前端后端接口-加密货币名称
|
||||
- 后端的接口返回的是交易对名称或者币种简称
|
||||
- 交易对名称构成为 SOLUSDT LINKUSDT
|
||||
- 后端接口的symbol均改为 交易对名称
|
||||
- 在前后端的接口中,需要设计函数解析接口中的参数
|
||||
|
||||
### 加密货币全称
|
||||
- 前端应该优先展示加密货币大写全称
|
||||
- 前端必须要同时展示加密货币的图标和名称
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
# 组件名称 币种选择器 CurrencySelector
|
||||
|
||||
## 组件功能
|
||||
- 通用组件
|
||||
- 用于其他页面中的加密货交易对选择器
|
||||
- 输入框交易对选择器
|
||||
|
||||
## 组件参数说明
|
||||
- 输入框存在参数设置,支持单选和多选
|
||||
- 输入框可选择的币种选项支持配置
|
||||
- 加密货币币种选项
|
||||
- 交易对选项
|
||||
- 输入框默认币种或交易对名称
|
||||
- 默认参数为 点击选择币种
|
||||
- 有参数时,查找默认币种图标显示
|
||||
- 币种选项的展示数量
|
||||
- 默认为6个
|
||||
- 可以通过参数调节,通过滑动块展示选项
|
||||
|
||||
## 样式
|
||||
- 可以通过参数进行自定义长度 宽度和高度,以适应各个页面使用
|
||||
- 匹配明亮和黑暗模式
|
||||
- 币种图标不需要颜色变换
|
||||
- 输入框选项,均显示为图标 简称
|
||||
- 币种选项- 图标 BNB
|
||||
- 交易对选项- 图标 BNBUSDT
|
||||
- 输入框在页面中的层级处于最高层级
|
||||
- 点击输入框选择时候,文字输入框和选项不会被其他内容遮挡
|
||||
|
||||
## 功能设计
|
||||
- 输入框点击输入框空白处之后,出现文字输入框及币种选项
|
||||
- 文字输入框按照币种名称快速查找交易对
|
||||
- 文字输入框高度与交易对选项保持一致
|
||||
|
||||
- 当点击选项后,币种图标 名称 删除键 出现在输入框
|
||||
- 样式为胶囊按钮状
|
||||
- 胶囊按钮状颜色不同于输入框的颜色
|
||||
|
||||
- 币种选项部分有滑动功能
|
||||
- 当鼠标处于币种选项部分时候,可以通过滑轮控制币种选项的滑动
|
||||
- 币种选项部分右侧 有拖动滑动块,也可以通过滑动块控制选项展示
|
||||
|
||||
- 已选择选项有删除键,点击删除键,输入框中去除此选项
|
||||
- 当输入框无选项时,自动显示默认的文字或者选项
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
20
3.1-ProjectTonyStack-前端/1-原始设计/2-组件/mask_layout.md
Normal file
20
3.1-ProjectTonyStack-前端/1-原始设计/2-组件/mask_layout.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# 页面设计
|
||||
|
||||
## 页面总体布局设计
|
||||
### 现代化布局
|
||||
- 采用经典的“侧边导航 + 顶部应用栏 + 主内容区”布局
|
||||
- 主内容区最上面显示Header,显示当前处于的主功能区介绍
|
||||
|
||||
### 可收缩侧边栏
|
||||
- 左侧的功能栏可以通过点击按钮在“完整模式”和“图标模式”之间平滑切换,有效节省屏幕空间。
|
||||
|
||||
### 页面切换
|
||||
- 点击侧边栏的不同菜单项,主内容区会流畅地切换到对应的页面组件,实现了单页面应用(SPA)的核心导航功能。
|
||||
- 侧边栏高亮展示当前功能页面
|
||||
|
||||
### 明亮/暗色主题
|
||||
- 左侧功能栏最下面提供了一个主题切换按钮,可以一键在明亮(Light)和暗色(Dark)模式间切换。
|
||||
|
||||
### 响应式设计
|
||||
- 整个布局基于 Vuetify 的栅格系统,无论在桌面、平板还是手机上都能提供良好的视觉和操作体验。
|
||||
|
||||
28
3.1-ProjectTonyStack-前端/1-原始设计/3-工具/utils_time_exchange.md
Normal file
28
3.1-ProjectTonyStack-前端/1-原始设计/3-工具/utils_time_exchange.md
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
# 时间工具类
|
||||
|
||||
## 工具统一内容
|
||||
1 前后端之间的接口参数,时间统一使用"2025-01-01 08:00:00"的格式
|
||||
2 前后端之间接口的时间字符串,统一都为东八区时间
|
||||
|
||||
## 函数功能
|
||||
|
||||
### 获取一个月前的时间字符串
|
||||
- 当调用之后,根据当前之间,推算一个月前的时间,返回时间字符串
|
||||
- 格式为"2025-01-01 08:00:00"
|
||||
|
||||
### 获取三个月前的时间字符串
|
||||
- 当调用之后,根据当前之间,推算三个月前的时间,返回时间字符串
|
||||
- 格式为"2025-01-01 08:00:00"
|
||||
|
||||
### 获取一年前的时间字符串
|
||||
- 当调用之后,根据当前之间,推算一年前的时间,返回时间字符串
|
||||
- 格式为"2025-01-01 08:00:00"
|
||||
|
||||
### 获取三年前的时间字符串
|
||||
- 当调用之后,根据当前之间,推算一年前的时间,返回时间字符串
|
||||
- 格式为"2025-01-01 08:00:00"
|
||||
|
||||
### 解析时间字符串为时间
|
||||
- 传入为格式为"2025-01-01 08:00:00"的时间字符串
|
||||
- 使用东八区时区解析,转换为时间对象
|
||||
120
3.1-ProjectTonyStack-前端/1-原始设计/vue3-typescript-stye.md
Normal file
120
3.1-ProjectTonyStack-前端/1-原始设计/vue3-typescript-stye.md
Normal file
@@ -0,0 +1,120 @@
|
||||
**背景 (Context):**
|
||||
|
||||
你是一名资深前端架构师,负责搭建前端框架和制定开发规范。该项目要求极高的代码质量、无缝的用户体验(跨设备、跨主题)以及长期的可维护性。
|
||||
|
||||
**核心指令 (Core Directive):**
|
||||
|
||||
请你以架构师的身份,严格遵循并执行以下所有规范来生成代码、组件、模块和提供解决方案。你的任何产出都必须成为团队的代码典范。
|
||||
|
||||
**1. 语言与语法 (Language & Syntax):**
|
||||
|
||||
* **TypeScript 至上 (TypeScript Supremacy):**
|
||||
|
||||
* 项目 **必须** 完全基于 TypeScript (`<script setup lang="ts">`)。**严禁** 任何 `.js` 文件或 JavaScript 语法的混入。
|
||||
* **杜绝 `any`:** 除非在第三方库类型定义缺失且无法补充的极端情况下,否则**严禁**使用 `any`。所有变量、函数参数/返回值、Props、Emits、Pinia State/Actions **必须** 拥有精确的类型或接口(Interface/Type)。
|
||||
* **善用高级类型:** 积极使用泛型(Generics)、条件类型(Conditional Types)、映射类型(Mapped Types)和类型守卫(Type Guards)来创建灵活且类型安全的代码。
|
||||
* **类型组织:** 全局共享的类型定义在 `@/types` 目录下,按模块划分文件(如 `user.d.ts`, `order.d.ts`)。
|
||||
|
||||
* **纯粹的 Vue 3 (Pure Vue 3):**
|
||||
|
||||
* **组合式API (`<script setup>`)**: 这是项目中**唯一**允许的组件逻辑组织方式。
|
||||
* **响应式核心:** 精准使用 `ref`、`reactive`、`computed` 和 `watchEffect`。理解 `shallowRef` 和 `readonly` 等API的适用场景,并在需要时使用它们来优化性能。
|
||||
* **生命周期:** **必须** 使用 `onMounted`, `onUnmounted` 等组合式API钩子,**严禁** 混用任何Vue 2选项式API。
|
||||
* **依赖注入:** 复杂场景下,善用 `provide` 和 `inject` 进行跨层级组件通信,并为注入的值提供明确的类型定义和默认值。
|
||||
* **状态管理:** 唯一的状态管理方案是 **Pinia**。Store的定义必须模块化,包含清晰的 `state`, `getters`, `actions`,并全部进行严格的类型标注。
|
||||
|
||||
**2. UI框架与设计 (UI Framework & Design):**
|
||||
|
||||
* **Vuetify 3 & Material Design:**
|
||||
* 所有UI界面和组件**必须**基于 [Vuetify 3](https://vuetifyjs.com/en/) 构建。
|
||||
* 深度贯彻 [Google Material Design 3 (MD3)](https://m3.material.io/) 设计哲学。
|
||||
* **主题适配 (Theming):**
|
||||
* 所有组件和自定义样式**必须**能完美适配**明亮 (Light)** 和**黑暗 (Dark)** 两种主题模式。
|
||||
* **严禁**硬编码颜色值(如 `#FFFFFF` 或 `black`)。**必须**使用Vuetify提供的主题颜色变量(如 `rgb(var(--v-theme-surface))` 或在SASS中使用 `v-theme(on-surface)`)。
|
||||
* 自定义样式需利用CSS变量或SASS函数来响应主题切换。
|
||||
* **响应式设计 (Responsive Design):**
|
||||
* 所有页面和组件**必须**在不同尺寸的设备上提供卓越的体验,包括手机、iPad(横屏/竖屏)和桌面端。
|
||||
* 积极使用Vuetify的栅格系统 (`v-row`, `v-col`) 和响应式断点工具类(如 `hidden-sm-and-down`)。
|
||||
* 对于复杂布局,使用 Vuetify 的 `useDisplay` 组合式函数来动态调整组件渲染和行为。
|
||||
|
||||
**3. 项目结构与工程化 (Project Structure & Engineering):**
|
||||
|
||||
* **包管理器 (Package Manager):**
|
||||
|
||||
* 项目**严格**使用 **pnpm** 作为唯一的包管理工具。熟悉并利用其特性(如 `pnpm workspace`)。
|
||||
|
||||
* **模块化结构:** 遵循以下清晰、可扩展的目录结构:
|
||||
|
||||
```
|
||||
src/
|
||||
├── api/ # API层
|
||||
│ ├── modules/ # 按业务模块划分
|
||||
│ ├── interceptors.ts # 统一拦截器
|
||||
│ └── index.ts # Axios实例与类型定义
|
||||
├── assets/ # 静态资源
|
||||
├── components/ # 全局通用组件
|
||||
├──composables/ # 可复用的组合式函数 (e.g., usePagination.ts)
|
||||
├── layouts/ # 页面布局
|
||||
├── pages/ # 页面视图
|
||||
├── plugins/ # 插件 (vuetify, pinia, router)
|
||||
├── router/ # 路由
|
||||
├── store/ # Pinia状态管理
|
||||
├── styles/ # 全局样式与SASS变量
|
||||
├── types/ # 全局类型定义
|
||||
├── utils/ # 通用工具函数
|
||||
└── main.ts # 应用入口
|
||||
```
|
||||
|
||||
**4. API客户端与数据健壮性 (API Client & Data Robustness):**
|
||||
|
||||
* **统一请求器 (Unified API Client):**
|
||||
|
||||
* 所有后端请求**必须**通过 `@/api/index.ts` 中封装的Axios实例发起。
|
||||
* API按业务模块封装在 `@/api/modules/` 下,函数签名必须清晰,包含类型化的参数和返回值。
|
||||
|
||||
* **统一响应与错误处理:**
|
||||
|
||||
* **响应格式:** 后端标准响应格式为:
|
||||
```typescript
|
||||
interface ApiResponse<T = any> {
|
||||
code: number;
|
||||
status: number;
|
||||
timestamp: string;
|
||||
data: T;
|
||||
message?: string; // Make message optional
|
||||
error?: string;
|
||||
}
|
||||
```
|
||||
* **响应代码:** 后端标准代码的含义如下:
|
||||
```typescript
|
||||
// 常见错误码定义
|
||||
export enum ApiErrorCode {
|
||||
CodeSuccess = 0 // 成功
|
||||
CodeServerError = 10001 // 服务器内部错误
|
||||
CodeParamError = 10002 // 参数错误
|
||||
CodeUnauthorized = 10003 // 未授权
|
||||
CodeForbidden = 10004 // 禁止访问
|
||||
CodeNotFound = 10005 // 请求的数据不存在
|
||||
CodeTimeout = 10006 // 请求超时
|
||||
CodeValidationFail = 10007 // 验证失败
|
||||
CodeBusiness = 20001 // 业务逻辑错误
|
||||
}
|
||||
```
|
||||
* **统一拦截器:** 在响应拦截器 (`@/api/interceptors.ts`) 中,实现全局错误处理逻辑:
|
||||
1. **业务成功:** `code` 为 `0`,直接解析并返回 `data`。
|
||||
2. **业务失败:** `code` 不为 `0`,提取 `message`,通过全局通知系统(如Vuetify的Snackbar)展示给用户,并 `Promise.reject` 一个带有`data`的Error对象。
|
||||
3. **HTTP错误:** 根据状态码 (401, 403, 404, 500+) 进行统一处理,如401则跳转登录页。
|
||||
4. **网络/超时错误:** 提示用户检查网络连接。
|
||||
|
||||
* **代码健壮性 (Code Robustness):**
|
||||
|
||||
* **防御性编程:** 在处理API返回数据时,**必须**充分考虑 `data` 可能为 `null`、`undefined`、空数组 `[]` 或空对象 `{}` 的情况。
|
||||
* **安全访问:** 使用可选链 (`?.`) 和空值合并运算符 (`??`) 来安全地访问嵌套对象的属性和提供默认值。
|
||||
* **数据校验:** 在渲染前,对关键数据进行必要的格式校验或存在性检查,避免因数据格式错误导致页面崩溃。例如,渲染列表前检查`Array.isArray(list) && list.length > 0`。
|
||||
* 在模板中,使用 `v-if` 或 `v-else` 来处理数据为空时的占位符或提示信息,提升用户体验。
|
||||
|
||||
-----
|
||||
|
||||
**总结指令:**
|
||||
|
||||
现在,你就是这位前端架构师。当我提出任何开发需求时(例如:“创建一个支持CRUD的用户管理页面”),请严格依据上述所有规范,为我提供完整、高质量、可直接用于生产环境的代码方案。你的代码不仅要能工作,更要体现出深厚的设计思想和对工程卓越的追求。
|
||||
0
3.1-ProjectTonyStack-前端/1-原始设计/前端重构.md
Normal file
0
3.1-ProjectTonyStack-前端/1-原始设计/前端重构.md
Normal file
72
3.1-ProjectTonyStack-前端/2-AI优化设计/1-页面/agi_login_page.md
Normal file
72
3.1-ProjectTonyStack-前端/2-AI优化设计/1-页面/agi_login_page.md
Normal file
@@ -0,0 +1,72 @@
|
||||
### **主题:用户认证模块 - 登录页面技术设计方案 (v2.0)**
|
||||
|
||||
**1. 总体目标 (Overall Objective)**
|
||||
|
||||
构建一个安全、健壮、响应式且符合 Material Design 3 (MD3) 规范的用户登录页面。该页面需完全遵循项目既定的 `vue3-typescript-stye.md` 架构规范,并实现客户端密码加密、全局状态管理和统一的API错误处理逻辑。
|
||||
|
||||
**2. 页面级组件设计 (`Login.vue`)**
|
||||
|
||||
* **文件位置:** `src/pages/Login.vue`
|
||||
* **技术栈:** Vue 3 (`<script setup lang="ts">`), Vuetify 3
|
||||
* **布局与样式:**
|
||||
* **居中布局:** 必须使用 Vuetify 的栅格系统 (`v-container`, `v-row`, `v-col`) 实现页面内容在所有视口下(移动端、平板、桌面端)的垂直与水平居中。
|
||||
* **登录卡片:** 主体容器使用 `v-card` 组件,并设置 `elevation` 属性以符合 MD3 的视觉深度。
|
||||
* **标题栏:** 卡片顶部使用 `v-toolbar` 组件,颜色绑定为 `color="primary"`,内容为项目图标(使用 `v-icon`)与 "ProjectTonyStack" 文本。
|
||||
* **主题化:** 所有组件颜色、背景、文本颜色严禁硬编码。必须使用 Vuetify 主题变量(如 `primary`, `error`, `surface`),确保在明亮/黑暗模式下无缝切换。
|
||||
* **表单设计 (`v-form`):**
|
||||
* **表单实例:** 使用 `v-form` 组件包裹所有输入域,并通过 `ref` 引用实例以调用其 `validate` 方法。
|
||||
* **输入字段 (`v-text-field`):**
|
||||
* **用户名:** `label` 为 "用户名",`prepend-inner-icon` 设置为 `mdi-account-outline`。
|
||||
* **密码:** `label` 为 "密码",`prepend-inner-icon` 设置为 `mdi-lock-outline`。
|
||||
* **密码可见性:** 字段类型 (`type`) 动态绑定为 `'text'` 或 `'password'`。使用 `append-inner-icon` 属性,并根据状态动态切换 `mdi-eye` 和 `mdi-eye-off` 图标,点击该图标可切换密码的明文/密文显示。
|
||||
* **校验规则:** 用户名和密码均为必填项。必须使用 Vuetify 的 `:rules` 属性进行客户端校验,提供明确的错误提示信息(如:"用户名为必填项")。
|
||||
* **用户交互:**
|
||||
* **登录按钮 (`v-btn`):** 按钮应为块级元素 (`block`),颜色为 `primary`。必须绑定 `:loading` 属性到组件的布尔状态,在API请求期间显示加载指示器,并禁用按钮。
|
||||
* **错误提示:** 在表单内、登录按钮上方,使用 `v-alert` 组件。通过 `v-if` 指令控制其显示。`type` 属性绑定为 `error`,使其颜色与主题的错误色保持一致。Alert 中显示的错误文本来自组件状态。
|
||||
|
||||
**3. 密码加密传输方案**
|
||||
|
||||
* **选型:** 鉴于 "密钥不会外泄" 的假设及对简化实现的追求,我们采用**对称加密算法 AES (CBC模式, PKCS7填充)**。
|
||||
* **实现:**
|
||||
* **依赖库:** 项目引入 `crypto-js` 库进行加密操作。
|
||||
* **工具模块:** 在 `src/utils/crypto.ts` 中封装一个 `encryptAES` 函数。此函数接收明文密码,使用预设的16字节密钥(KEY)和16字节初始化向量(IV)进行加密,并返回 Base64 编码的密文字符串。
|
||||
* **数据流:**
|
||||
1. 前端:在发起登录请求前,**仅对密码字段**调用 `encryptAES` 函数。
|
||||
2. 传输:向后端 `POST /api/user/login` 接口发送 JSON 体,其中 `username` 为明文,`password` 为加密后的密文。
|
||||
3. 后端:使用相同的 KEY 和 IV 解密密码,然后进行哈希比对验证。
|
||||
|
||||
**4. 状态管理 (Pinia)**
|
||||
|
||||
* **模块位置:** `src/store/auth.ts`
|
||||
* **职责:** 统一管理用户认证状态(Token)及相关业务逻辑(登录、登出),作为全应用认证信息的唯一可信源。
|
||||
* **State:** 定义 `AuthState` 接口,包含 `token: string | null`。初始状态尝试从 `localStorage` 读取 `token` 以支持持久化登录。
|
||||
* **Actions:**
|
||||
* `login(credentials: UserCredentials): Promise<void>`:
|
||||
1. 接收包含明文密码的 `credentials` 对象。
|
||||
2. 调用 `src/utils/crypto.ts` 中的 `encryptAES` 加密密码。
|
||||
3. 调用 `src/api/modules/user.ts` 中的 `loginApi` 方法发起请求。
|
||||
4. **成功时 (`code: 0`):** 从 API 响应中提取 `token`,调用内部 `setToken` action。
|
||||
5. **失败时:** 捕获 API 层抛出的错误,调用 `clearToken` action,然后将错误继续向上抛出,由UI层处理显示。
|
||||
* `setToken(token: string)`: 将 `token` 同时存入 Pinia state 和 `localStorage`。
|
||||
* `logout()`: 清理 state 和 `localStorage` 中的 `token`,并可在此触发路由跳转至登录页。
|
||||
* **Getters:**
|
||||
* `isAuthenticated: (state) => boolean`: 根据 `state.token` 是否存在返回一个布尔值,便于在应用各处快速判断登录状态。
|
||||
|
||||
**5. API 层设计与全局错误处理**
|
||||
|
||||
* **API 模块:**
|
||||
* **文件位置:** `src/api/modules/user.ts`
|
||||
* **函数定义:** 导出一个 `loginApi(data: LoginPayload): Promise<LoginResponse>` 函数。参数和返回类型必须从 `@/types/user.d.ts` 导入,确保类型安全。
|
||||
* **类型定义:**
|
||||
* **文件位置:** `@/types/user.d.ts`
|
||||
* **接口:** 定义 `UserCredentials` (明文)、`LoginPayload` (加密后)、`LoginResponse` (包含 `token` 字段) 等精确的接口。
|
||||
* **统一拦截器 (Critical Refinement):**
|
||||
* **文件位置:** `@/api/interceptors.ts`
|
||||
* **请求拦截器:** 自动从 `authStore` 获取 `token`,如果存在,则附加到每个请求的 `Authorization` Header 中 (`Bearer ${token}`)。
|
||||
* **响应拦截器:**
|
||||
1. **业务成功 (`code === 0`):** 解析响应体,直接 `resolve(response.data.data)`。
|
||||
2. **业务失败 (`code !== 0`):** `reject(new Error(response.data.message))`。错误信息将由页面级的 `catch` 块捕获并显示在 `v-alert` 中。
|
||||
3. **未授权处理 (`code === 10003`):** 这是全局的强制下线逻辑。拦截器必须执行以下原子操作:
|
||||
* 通过全局通知组件(如 Snackbar)向用户显示 "登录状态已过期,请重新登录" 的提示。
|
||||
* 调用 `authStore.logout()` 清理所有本地认证凭据。
|
||||
* 延迟2秒后,使用 `router.push('/login')` 强制将页面跳转到登录页。**此逻辑严禁在任何页面组件中单独实现**。
|
||||
455
3.1-ProjectTonyStack-前端/2-AI优化设计/1-页面/api_binance_account.md
Normal file
455
3.1-ProjectTonyStack-前端/2-AI优化设计/1-页面/api_binance_account.md
Normal file
@@ -0,0 +1,455 @@
|
||||
**背景 (Context):**
|
||||
|
||||
你是一名资深前端架构师,严格遵循我们团队定义的 `vue3-typescript-stye.md` 开发规范。现在,你需要基于一份初步的产品需求 `mask_binance_account.md`,设计并实现一个功能完整、体验卓越、代码健壮的币安账户信息页面 (`BinanceAccountView.vue`)。
|
||||
|
||||
**核心指令 (Core Directive):**
|
||||
|
||||
请为 `BinanceAccountView.vue` 页面提供一个完整、高质量、可直接用于生产环境的代码实现方案。方案必须包含Pinia状态管理、API层封装、组件模板和逻辑脚本的全部细节,并严格遵循下述所有设计规范。
|
||||
|
||||
-----
|
||||
|
||||
### **第一部分:架构与状态管理设计 (Pinia Store)**
|
||||
|
||||
为了实现清晰的数据流和逻辑分离,我们将为该页面创建一个专属的Pinia Store:`@/store/binanceAccount.ts`。
|
||||
|
||||
**1. 类型定义 (Type Definitions):**
|
||||
|
||||
首先,在 `@/types/binance.d.ts` 中定义所有相关的数据结构,确保类型安全。
|
||||
|
||||
```typescript
|
||||
// @/types/binance.d.ts
|
||||
|
||||
// API标准响应体 (遵循规范)
|
||||
export interface ApiResponse<T = any> {
|
||||
code: number;
|
||||
status: number;
|
||||
timestamp: string;
|
||||
data: T;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
// 币种余额
|
||||
export interface Balance {
|
||||
asset: string;
|
||||
free: string;
|
||||
locked: string;
|
||||
}
|
||||
|
||||
// 费率
|
||||
export interface CommissionRates {
|
||||
maker: string;
|
||||
taker: string;
|
||||
buyer: string;
|
||||
seller: string;
|
||||
}
|
||||
|
||||
// 账户完整信息
|
||||
export interface AccountInfo {
|
||||
makerCommission: int64;
|
||||
takerCommission: int64;
|
||||
buyerCommission: int64;
|
||||
sellerCommission: int64;
|
||||
commissionRates: CommissionRates;
|
||||
canTrade: boolean;
|
||||
canWithdraw: boolean;
|
||||
canDeposit: boolean;
|
||||
updateTime: uint64;
|
||||
accountType: string;
|
||||
balances: Balance[];
|
||||
permissions: string[];
|
||||
uid: int64;
|
||||
}
|
||||
|
||||
// 币种价格
|
||||
export interface TickerPrice {
|
||||
symbol: string;
|
||||
price: string;
|
||||
}
|
||||
|
||||
// 资产详情表格行项目 (优化设计)
|
||||
// 将账户余额、实时价格和其他计算属性聚合到一个对象中,便于表格渲染
|
||||
export interface AssetDetail extends Balance {
|
||||
icon: string; // 币种图标 (通过组件动态获取)
|
||||
price: number;
|
||||
totalAmount: number; // free + locked
|
||||
totalValue: number; // price * totalAmount
|
||||
}
|
||||
```
|
||||
|
||||
**2. Pinia Store (`useBinanceAccountStore`):**
|
||||
|
||||
```typescript
|
||||
// @/store/binanceAccount.ts
|
||||
import { defineStore } from 'pinia';
|
||||
import { getAccountInfo, getSupportedSymbols, updateSupportedSymbols, getTickerPrices } from '@/api/modules/binance';
|
||||
import type { AccountInfo, AssetDetail, Balance, TickerPrice } from '@/types/binance';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
export const useBinanceAccountStore = defineStore('binanceAccount', () => {
|
||||
// === State ===
|
||||
const accountInfo = ref<AccountInfo | null>(null);
|
||||
const supportedSymbols = ref<string[]>([]);
|
||||
const assetPrices = ref<Map<string, number>>(new Map()); // 使用Map优化查询性能
|
||||
const isLoading = ref<boolean>(false);
|
||||
const error = ref<string | null>(null);
|
||||
|
||||
// === Getters (Computed) ===
|
||||
const assetDetails = computed<AssetDetail[]>(() => {
|
||||
if (!accountInfo.value?.balances) return [];
|
||||
|
||||
// 聚合余额、价格和计算属性
|
||||
return accountInfo.value.balances
|
||||
.map(balance => {
|
||||
const price = assetPrices.value.get(`${balance.asset}USDT`) ?? 0;
|
||||
const freeAmount = parseFloat(balance.free);
|
||||
const lockedAmount = parseFloat(balance.locked);
|
||||
const totalAmount = freeAmount + lockedAmount;
|
||||
|
||||
return {
|
||||
...balance,
|
||||
price,
|
||||
totalAmount,
|
||||
totalValue: totalAmount * price,
|
||||
};
|
||||
})
|
||||
.filter(asset => asset.totalAmount > 0); // 过滤掉数量为0的资产
|
||||
});
|
||||
|
||||
const totalAssetValue = computed<number>(() => {
|
||||
return assetDetails.value.reduce((sum, asset) => sum + asset.totalValue, 0);
|
||||
});
|
||||
|
||||
// === Actions ===
|
||||
async function fetchInitialData() {
|
||||
isLoading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
// 并行获取账户信息和支持的交易对,提升加载速度
|
||||
const [info, symbols] = await Promise.all([
|
||||
getAccountInfo(),
|
||||
getSupportedSymbols(),
|
||||
]);
|
||||
accountInfo.value = info;
|
||||
supportedSymbols.value = symbols;
|
||||
|
||||
// 获取资产后,根据资产列表获取价格
|
||||
if (info.balances && info.balances.length > 0) {
|
||||
await fetchAllTickerPrices();
|
||||
}
|
||||
} catch (e: any) {
|
||||
error.value = e.message || '获取账户数据失败';
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAllTickerPrices() {
|
||||
if (!accountInfo.value?.balances) return;
|
||||
const symbolsToFetch = accountInfo.value.balances.map(b => `${b.asset}USDT`);
|
||||
if (symbolsToFetch.length === 0) return;
|
||||
|
||||
const prices = await getTickerPrices(symbolsToFetch);
|
||||
const priceMap = new Map<string, number>();
|
||||
prices.forEach(p => priceMap.set(p.symbol, parseFloat(p.price)));
|
||||
assetPrices.value = priceMap;
|
||||
}
|
||||
|
||||
// 优化点:采用单一接口进行全量更新,保证数据事务性
|
||||
async function saveSupportedSymbols(symbols: string[]) {
|
||||
// 这里可以添加乐观更新的UI逻辑
|
||||
await updateSupportedSymbols(symbols);
|
||||
supportedSymbols.value = symbols; // 更新成功后同步状态
|
||||
}
|
||||
|
||||
return {
|
||||
accountInfo,
|
||||
supportedSymbols,
|
||||
isLoading,
|
||||
error,
|
||||
assetDetails,
|
||||
totalAssetValue,
|
||||
fetchInitialData,
|
||||
fetchAllTickerPrices,
|
||||
saveSupportedSymbols,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
### **第二部分:API层设计 (`@/api/modules/binance.ts`)**
|
||||
|
||||
所有API请求必须通过此模块进行封装,并提供完整的类型定义。
|
||||
|
||||
```typescript
|
||||
// @/api/modules/binance.ts
|
||||
import request from '@/api';
|
||||
import type { ApiResponse, AccountInfo, TickerPrice } from '@/types/binance';
|
||||
|
||||
// 获取账户信息
|
||||
export function getAccountInfo(): Promise<AccountInfo> {
|
||||
return request.get<ApiResponse<AccountInfo>>('/api/binance/account/info').then(res => res.data);
|
||||
}
|
||||
|
||||
// 获取账户支持的交易对
|
||||
export function getSupportedSymbols(): Promise<string[]> {
|
||||
return request.get<ApiResponse<string[]>>('/api/binance/account/symbols/get').then(res => res.data);
|
||||
}
|
||||
|
||||
// 优化设计:将 add 和 delete 合并为一个 update 接口,实现原子化操作
|
||||
// 后端需要提供一个接收字符串数组的全量更新接口
|
||||
export function updateSupportedSymbols(symbols: string[]): Promise<void> {
|
||||
return request.post<ApiResponse<void>>('/api/binance/account/symbols/update', { symbols }).then(res => res.data);
|
||||
}
|
||||
|
||||
// 获取指定交易对的实时价格
|
||||
export function getTickerPrices(symbols: string[]): Promise<TickerPrice[]> {
|
||||
return request.post<ApiResponse<TickerPrice[]>>('/api/binance/market/ticker/price', { symbols }).then(res => res.data);
|
||||
}
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
### **第三部分:页面组件实现 (`BinanceAccountView.vue`)**
|
||||
|
||||
这是最终呈现给用户的视图,需要包含完整的加载、错误、空状态处理,并提供流畅的交互体验。
|
||||
|
||||
#### **`<script setup lang="ts">`**
|
||||
|
||||
```typescript
|
||||
import { ref, onMounted, computed, watch } from 'vue';
|
||||
import { useBinanceAccountStore } from '@/store/binanceAccount';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import CryptoIcon from '@/components/CryptoIcon.vue';
|
||||
import CurrencySelector from '@/components/CurrencySelector.vue'; // 假设该组件存在
|
||||
|
||||
// 1. 初始化Store和状态
|
||||
const binanceStore = useBinanceAccountStore();
|
||||
const {
|
||||
accountInfo,
|
||||
supportedSymbols,
|
||||
isLoading,
|
||||
error,
|
||||
assetDetails,
|
||||
totalAssetValue,
|
||||
} = storeToRefs(binanceStore);
|
||||
|
||||
// 2. 本地UI状态
|
||||
const isEditingSymbols = ref(false);
|
||||
const localSymbols = ref<string[]>([]); // 用于编辑时的临时状态,避免直接修改store
|
||||
const filterText = ref(''); // 资产表格筛选文本
|
||||
|
||||
// 3. 生命周期钩子
|
||||
onMounted(() => {
|
||||
binanceStore.fetchInitialData();
|
||||
});
|
||||
|
||||
// 4. 交互逻辑
|
||||
watch(supportedSymbols, (newVal) => {
|
||||
// 当store中的数据变化时(例如,初始加载),同步到本地
|
||||
localSymbols.value = [...newVal];
|
||||
});
|
||||
|
||||
function handleEditSymbols() {
|
||||
isEditingSymbols.value = true;
|
||||
localSymbols.value = [...supportedSymbols.value]; // 进入编辑模式,拷贝一份数据
|
||||
}
|
||||
|
||||
async function handleSaveSymbols() {
|
||||
await binanceStore.saveSupportedSymbols(localSymbols.value);
|
||||
isEditingSymbols.value = false;
|
||||
}
|
||||
|
||||
function handleCancelEdit() {
|
||||
isEditingSymbols.value = false;
|
||||
localSymbols.value = [...supportedSymbols.value]; // 取消编辑,恢复原始数据
|
||||
}
|
||||
|
||||
function addSymbol(symbol: string) {
|
||||
if (!localSymbols.value.includes(symbol)) {
|
||||
localSymbols.value.push(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
function removeSymbol(symbol: string) {
|
||||
localSymbols.value = localSymbols.value.filter(s => s !== symbol);
|
||||
}
|
||||
|
||||
// 5. 表格数据与表头
|
||||
const headers = [
|
||||
{ title: '', key: 'icon', sortable: false, width: '60px' },
|
||||
{ title: '名称', key: 'asset', align: 'start' },
|
||||
{ title: '实时价格 (USDT)', key: 'price', align: 'end' },
|
||||
{ title: '总数量', key: 'totalAmount', align: 'end' },
|
||||
{ title: '可使用数量', key: 'free', align: 'end' },
|
||||
{ title: '冻结数量', key: 'locked', align: 'end' },
|
||||
{ title: '币种总计 (USDT)', key: 'totalValue', align: 'end' },
|
||||
];
|
||||
|
||||
const filteredAssetDetails = computed(() => {
|
||||
if (!filterText.value) {
|
||||
return assetDetails.value;
|
||||
}
|
||||
const query = filterText.value.toUpperCase();
|
||||
return assetDetails.value.filter(asset => asset.asset.toUpperCase().includes(query));
|
||||
});
|
||||
|
||||
// 格式化函数
|
||||
const formatNumber = (value: number) => value.toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
```
|
||||
|
||||
#### **`<template>`**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<div class="d-flex justify-end align-center">
|
||||
<span class="text-h6 font-weight-bold">欢迎,{{ accountInfo?.uid ?? '...' }}</span>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-if="isLoading">
|
||||
<v-col>
|
||||
<v-skeleton-loader type="card, table"></v-skeleton-loader>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-else-if="error">
|
||||
<v-col>
|
||||
<v-alert type="error" closable title="加载失败">{{ error }}</v-alert>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-else-if="accountInfo">
|
||||
<v-col cols="12" md="4">
|
||||
<v-card class="mb-4" title="账户信息">
|
||||
<v-list density="compact">
|
||||
<v-list-item title="账户UID" :subtitle="accountInfo.uid"></v-list-item>
|
||||
<v-list-item title="账户类型" :subtitle="accountInfo.accountType"></v-list-item>
|
||||
<v-list-item title="能否交易" :subtitle="accountInfo.canTrade ? '是' : '否'"></v-list-item>
|
||||
<v-list-item title="能否提款" :subtitle="accountInfo.canWithdraw ? '是' : '否'"></v-list-item>
|
||||
<v-list-item title="能否充值" :subtitle="accountInfo.canDeposit ? '是' : '否'"></v-list-item>
|
||||
<v-list-item title="Maker费率" :subtitle="accountInfo.commissionRates.maker"></v-list-item>
|
||||
<v-list-item title="Taker费率" :subtitle="accountInfo.commissionRates.taker"></v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
|
||||
<v-card>
|
||||
<v-card-title class="d-flex justify-space-between align-center">
|
||||
支持的交易对
|
||||
<div v-if="!isEditingSymbols">
|
||||
<v-btn icon="mdi-pencil" variant="text" size="small" @click="handleEditSymbols"></v-btn>
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-btn color="primary" variant="tonal" size="small" @click="handleSaveSymbols">保存</v-btn>
|
||||
<v-btn variant="text" size="small" @click="handleCancelEdit">取消</v-btn>
|
||||
</div>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="d-flex flex-wrap ga-2">
|
||||
<v-chip
|
||||
v-for="symbol in localSymbols"
|
||||
:key="symbol"
|
||||
:closable="isEditingSymbols"
|
||||
@click:close="removeSymbol(symbol)"
|
||||
label
|
||||
variant="outlined"
|
||||
>
|
||||
<template #prepend>
|
||||
<CryptoIcon :symbol="symbol.replace('USDT', '')" size="20" />
|
||||
</template>
|
||||
{{ symbol }}
|
||||
</v-chip>
|
||||
<CurrencySelector v-if="isEditingSymbols" @select="addSymbol">
|
||||
<template #activator="{ props }">
|
||||
<v-btn v-bind="props" icon="mdi-plus" variant="tonal" size="small"></v-btn>
|
||||
</template>
|
||||
</CurrencySelector>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="8">
|
||||
<v-card>
|
||||
<v-toolbar flat>
|
||||
<v-text-field
|
||||
v-model="filterText"
|
||||
label="筛选币种"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
hide-details
|
||||
class="ma-2"
|
||||
></v--text-field>
|
||||
<v-btn icon="mdi-refresh" @click="binanceStore.fetchAllTickerPrices"></v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<div class="px-4">
|
||||
<span class="text-subtitle-1">总资产: </span>
|
||||
<span class="text-h6 font-weight-bold">{{ formatNumber(totalAssetValue) }} USDT</span>
|
||||
</div>
|
||||
</v-toolbar>
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="filteredAssetDetails"
|
||||
:loading="isLoading"
|
||||
item-value="asset"
|
||||
density="compact"
|
||||
>
|
||||
<template #item.icon="{ item }">
|
||||
<CryptoIcon :symbol="item.raw.asset" />
|
||||
</template>
|
||||
<template #item.price="{ value }">{{ formatNumber(value) }}</template>
|
||||
<template #item.totalAmount="{ value }">{{ formatNumber(value) }}</template>
|
||||
<template #item.free="{ value }">{{ formatNumber(parseFloat(value)) }}</template>
|
||||
<template #item.locked="{ value }">{{ formatNumber(parseFloat(value)) }}</template>
|
||||
<template #item.totalValue="{ value }">
|
||||
<span class="font-weight-bold">{{ formatNumber(value) }}</span>
|
||||
</template>
|
||||
<template #no-data>
|
||||
<v-alert type="info" class="ma-4">暂无资产数据</v-alert>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
### **总结:设计优化点**
|
||||
|
||||
1. **架构层面:**
|
||||
|
||||
* 引入了 **Pinia** 进行中心化状态管理,将数据、业务逻辑与视图彻底分离,提高了代码的可维护性和可测试性。
|
||||
* 定义了清晰的 **TypeScript 类型** (`AccountInfo`, `AssetDetail`等),贯穿API、Store和组件,实现端到端的类型安全。
|
||||
|
||||
2. **API与数据流:**
|
||||
|
||||
* **并行请求**:`fetchInitialData` 中使用 `Promise.all` 并行获取账户信息和交易对,缩短了首屏加载时间。
|
||||
* **原子化更新**:将原始设计中的 `add` 和 `delete` 接口合并为单一的 `update` 接口,这使得前端的 "保存" 操作成为一个原子事务,避免了多次请求可能导致的部分成功或失败,保证了数据一致性。
|
||||
* **数据聚合**:在Pinia的Getter (`assetDetails`) 中,将分散的余额数据 (`balances`) 和价格数据 (`assetPrices`) 聚合成统一的视图模型,简化了组件层的渲染逻辑。
|
||||
|
||||
3. **用户体验 (UX):**
|
||||
|
||||
* **完整的状态反馈**:全面设计了 **加载中 (Loading)**、**错误 (Error)** 和 **空数据 (No Data)** 状态的UI展示,使用 `v-skeleton-loader`、`v-alert` 等组件提供清晰的用户反馈。
|
||||
* **优化的编辑模式**:修正了原始设计中 "立即调用接口" 的问题。交易对的增删操作现在在本地临时状态 (`localSymbols`) 中进行,用户可以自由修改,只有点击 "保存" 按钮时才发起一次API请求。增加了 "取消" 按钮,允许用户放弃修改,极大提升了操作的容错性和友好性。
|
||||
* **即时筛选与刷新**:资产表格提供了客户端筛选功能和手动刷新价格的按钮,增强了数据的可操作性。
|
||||
|
||||
4. **代码健壮性:**
|
||||
|
||||
* **防御性编程**:在 `computed` 属性和模板中,对可能为空的 `accountInfo.value` 进行了安全检查。
|
||||
* **数据格式化**:对所有数值显示进行了统一的格式化处理,保证了UI的一致性。
|
||||
* **性能考量**:使用 `Map` 结构存储币种价格,使得在聚合 `assetDetails` 时查找价格的时间复杂度从O(N)降低到O(1),在资产数量较多时性能更优。
|
||||
|
||||
这份设计方案将一个初步的想法,转化为了一个专业、严谨且可直接投入生产的工程实现蓝图。
|
||||
136
3.1-ProjectTonyStack-前端/2-AI优化设计/1-页面/api_binance_order.md
Normal file
136
3.1-ProjectTonyStack-前端/2-AI优化设计/1-页面/api_binance_order.md
Normal file
@@ -0,0 +1,136 @@
|
||||
**背景 (Context):**
|
||||
|
||||
你是一名资深前端架构师,严格遵循 `vue3-typescript-stye.md` 中定义的所有开发规范。你的任务是为一个企业级加密货币交易分析平台,开发一个功能完备的“交易订单”查询与分析页面。
|
||||
|
||||
**核心指令 (Core Directive):**
|
||||
|
||||
请根据以下详细设计方案,生成完整的、生产环境级别的Vue 3、TypeScript和Vuetify 3代码。你需要创建所有必要的Pinia Store、API模块、页面和组件。
|
||||
|
||||
---
|
||||
|
||||
### **功能模块一:Pinia状态管理 (`src/store/binanceOrder.ts`)**
|
||||
|
||||
为“交易订单”页面创建一个专属的Pinia store,用于统一管理所有状态。
|
||||
|
||||
1. **Store ID:** `'binanceOrder'`
|
||||
|
||||
2. **State (`BinanceOrderState`):**
|
||||
* `trades`: `Ref<Trade[]>` - 存储从API获取的原始订单列表,默认为 `[]`。
|
||||
* `filters`: `Reactive<OrderFilters>` - 存储所有查询和筛选条件。
|
||||
* `symbol`: `string | null` - 交易对,如 'BTCUSDT'。
|
||||
* `startTime`: `number | null` - 查询起始时间戳。
|
||||
* `endTime`: `number | null` - 查询结束时间戳。
|
||||
* `side`: `'BUY' | 'SELL' | 'ALL'` - 交易方向,默认 'ALL'。
|
||||
* `timeRangeSelection`: `string | null` - 用于高亮显示快捷时间段按钮,如 '3m', '1y'。
|
||||
* `pagination`: `Reactive<Pagination>` - 分页状态。
|
||||
* `page`: `number` - 当前页码,默认 1。
|
||||
* `itemsPerPage`: `number` - 每页数量,默认 20。
|
||||
* `totalItems`: `number` - 总条目数,默认 0。
|
||||
* `isLoading`: `Ref<boolean>` - 页面级加载状态,控制骨架屏或加载指示器。
|
||||
* `error`: `Ref<string | null>` - 存储API请求错误信息。
|
||||
|
||||
3. **Getters:**
|
||||
* `displayTrades`: 计算属性,根据 `filters.side` 和 `pagination` 从 `trades` 派生出当前页面应显示的订单列表。
|
||||
* `timelineData`: 计算属性,将 `trades` 转换为时间轴组件所需的数据结构 `[{timestamp: number, side: 'BUY' | 'SELL'}]`。
|
||||
* `isDefaultDateRange`: 计算属性,判断当前`startTime`是否为初始默认值 `2025-01-01 08:00:00`。
|
||||
|
||||
4. **Actions:**
|
||||
* `fetchTrades()`:
|
||||
* 设置 `isLoading` 为 `true`,清空 `error`。
|
||||
* 调用API模块获取订单数据,请求参数基于 `filters` state(`symbol`, `startTime`, `endTime`),`limit` 固定为 `1000`。
|
||||
* 成功后,更新 `trades` 和 `pagination.totalItems`。
|
||||
* 失败后,填充 `error` 状态。
|
||||
* 最后设置 `isLoading` 为 `false`。
|
||||
* `setFilter(payload: Partial<OrderFilters>)`: 更新 `filters` 对象。
|
||||
* `setPage(page: number)`: 更新 `pagination.page`。
|
||||
* `setItemsPerPage(size: number)`: 更新 `pagination.itemsPerPage` 并将 `page` 重置为 1。
|
||||
* `applyTimeRange(range: '3m' | '1m' | '1y' | '3y')`:
|
||||
* 根据`range`计算新的 `startTime` (以当前时间为`endTime`)。
|
||||
* 更新 `filters.startTime`, `filters.endTime` 和 `filters.timeRangeSelection`。
|
||||
* `resetTimeRange()`: 恢复`startTime`和`endTime`至默认值,并清空`timeRangeSelection`。
|
||||
* `exportToCSV()`:
|
||||
* 实现一个将 `trades` state中的数据转换为CSV格式并触发浏览器下载的逻辑。
|
||||
* CSV列应包括:`Time (CST)`, `Symbol`, `Side`, `Price`, `Quantity`, `QuoteQty`, `Commission`, `CommissionAsset`。
|
||||
|
||||
---
|
||||
|
||||
### **功能模块二:API层 (`src/api/modules/binance.ts` & `src/types/binance.d.ts`)**
|
||||
|
||||
1. **类型定义 (`binance.d.ts`):**
|
||||
* `MyTradesRequest`: 定义API请求参数类型。
|
||||
* `Trade`: 定义单笔订单的数据结构,所有字段必须有精确类型。
|
||||
|
||||
2. **API函数 (`binance.ts`):**
|
||||
* 创建一个 `getMyTrades(params: MyTradesRequest): Promise<ApiResponse<Trade[]>>` 函数。
|
||||
* 该函数通过封装的Axios实例,向 `POST /api/binance/order/trades` 发送请求。
|
||||
|
||||
---
|
||||
|
||||
### **功能模块三:页面与组件实现**
|
||||
|
||||
#### **1. 主页面 (`src/pages/BinanceOrder.vue`)**
|
||||
|
||||
* **布局:**
|
||||
* 使用 `v-container(fluid)` 作为根元素。
|
||||
* 顶部应用栏 (`v-app-bar`) 的 `v-toolbar-title` 显示 "交易订单"。
|
||||
* 页面主体使用 `v-row` 和 `v-col` 组织布局。
|
||||
* **逻辑 (`<script setup lang="ts">`):**
|
||||
* 实例化 `binanceOrder` store。
|
||||
* 使用 `onMounted` 触发一次初始数据加载 `store.fetchTrades()`。
|
||||
* 将store的state和actions传递给子组件。
|
||||
* 使用 `v-if="store.isLoading"` 显示一个覆盖整个内容区的 `v-progress-linear` 加载指示器。
|
||||
* 监听 `store.error`,当其有值时,通过全局Snackbar服务显示错误信息。
|
||||
|
||||
#### **2. 订单查询栏组件 (`src/components/binance/OrderFilterBar.vue`)**
|
||||
|
||||
* **UI:** 使用 `v-card` 和 `v-card-text` 构建。
|
||||
* **交易对选择:** 使用 `CurrencySelector` 全局组件。
|
||||
* **时间选择:**
|
||||
* 使用两个 `v-text-field` (label: "开始时间", "结束时间") 并配合 `v-menu` 和 `v-date-picker` + 时间选择器实现。`v-model` 双向绑定到 store中的 `filters.startTime` 和 `filters.endTime`(需要转换器)。
|
||||
* **快捷时间段:** 使用 `v-chip-group` 或 `v-btn-toggle` 展示“最近三个月”、“最近一个月”、“最近一年”、“最近三年”。点击时调用 `store.applyTimeRange(range)`。`v-model` 绑定到`store.filters.timeRangeSelection`以实现状态高亮。
|
||||
* **重置时间:** 提供一个 `v-btn` (icon: `mdi-restore`),`@click`="store.resetTimeRange()"`,仅在 `!isDefaultDateRange` 时可见。
|
||||
* **操作按钮:**
|
||||
* `v-btn` "查询",`@click="store.fetchTrades()"`,颜色为 `primary`。
|
||||
* `v-btn` "导出CSV",`@click="store.exportToCSV()"`,颜色为 `secondary`。
|
||||
* **响应式:** 在 `xs` 断点下,所有表单项垂直堆叠。
|
||||
|
||||
#### **3. 订单时间轴组件 (`src/components/binance/OrderTimeline.vue`)**
|
||||
|
||||
* **技术选型:** 自定义、响应式的SVG组件。
|
||||
* **Props:**
|
||||
* `data: {timestamp: number, side: 'BUY' | 'SELL'}[]` - 从 `store.timelineData` getter 传入。
|
||||
* **Events:**
|
||||
* `@range-selected="(startTime: number, endTime: number) => void"` - 当用户通过拖拽(brushing)选择一个范围后触发。
|
||||
* `@dot-clicked="(trade: Trade) => void"` - 当用户点击某个订单点时触发。
|
||||
* **功能:**
|
||||
* 根据`data`中的时间戳范围,动态计算X轴的比例尺。
|
||||
* 在SVG上渲染代表每个订单的圆点(`<circle>`),`BUY`为绿色,`SELL`为红色。
|
||||
* 实现鼠标悬停在圆点上时显示Tooltip(使用Vuetify的 `v-tooltip`),展示订单关键信息。
|
||||
* 支持鼠标按下并拖拽以创建一个矩形选区(brush),释放鼠标后,发出 `@range-selected` 事件。
|
||||
* 整个SVG的尺寸应能响应容器宽度变化。
|
||||
|
||||
#### **4. 订单列表区组件 (`src/components/binance/OrderDataTable.vue`)**
|
||||
|
||||
* **UI:**
|
||||
* 使用 Vuetify 的 `v-data-table-server` 组件,因为它能更好地处理潜在的大量数据和分页逻辑。
|
||||
* **Props & State绑定:**
|
||||
* `:items`: `store.displayTrades`
|
||||
* `:headers`: 定义表头,包括时间、交易对、方向、价格、数量、成交额、手续费等。
|
||||
* `:loading`: `store.isLoading`
|
||||
* `:page`: `store.pagination.page`
|
||||
* `:items-per-page`: `store.pagination.itemsPerPage`
|
||||
* `:items-length`: `store.pagination.totalItems`
|
||||
* `@update:page`: `(p) => store.setPage(p)`
|
||||
* `@update:items-per-page`: `(size) => store.setItemsPerPage(size)`
|
||||
* **功能:**
|
||||
* **表头:** 在 `v-data-table` 上方使用 `v-toolbar` 添加筛选控件。
|
||||
* **方向筛选:** `v-btn-toggle` (选项: 全部, 买入, 卖出),`v-model` 绑定 `store.filters.side`。
|
||||
* **单元格渲染:**
|
||||
* **方向:** 使用 `v-chip` 根据买卖方向显示不同颜色(`success` for BUY, `error` for SELL)。
|
||||
* **时间:** 格式化为 `YYYY-MM-DD HH:mm:ss`。
|
||||
* **空状态:** 当 `store.trades.length === 0 && !store.isLoading` 时,在表格中显示 "暂无数据" 的提示。
|
||||
* **分页选项:** `items-per-page-options` 设置为 `[20, 50, 100]`。
|
||||
|
||||
**总结指令:**
|
||||
|
||||
请严格按照上述四个模块的设计,生成所有代码文件,并确保它们完全符合 `vue3-typescript-stye.md` 中定义的每一条规范,包括但不限于:完全的TypeScript、组合式API、Vuetify 3组件和主题变量的使用、Pinia的最佳实践以及健壮的API数据处理。
|
||||
165
3.1-ProjectTonyStack-前端/2-AI优化设计/2-组件/api_crypto_icon.md
Normal file
165
3.1-ProjectTonyStack-前端/2-AI优化设计/2-组件/api_crypto_icon.md
Normal file
@@ -0,0 +1,165 @@
|
||||
**SUBJECT:** **企业级加密货币图标系统(Crypto Icon System)详细设计与实现指令**
|
||||
|
||||
**背景 (Context):**
|
||||
|
||||
你将为我们的企业级SaaS应用构建一个高性能、类型安全且完全符合设计规范的加密货币图标系统。此系统不仅需要展示单个货币图标,还需优雅地处理交易对的堆叠图标样式。该模块必须严格遵守项目 `vue3-typescript-stye.md` 中定义的所有架构原则、编码规范和技术选型。
|
||||
|
||||
**核心指令 (Core Directive):**
|
||||
|
||||
请根据以下详细设计,实现一个完整的、可复用的、生产级的加密货币图标系统。该系统主要由一个核心的组合式函数(Composable)用于数据管理,以及一个UI组件用于渲染构成。
|
||||
|
||||
-----
|
||||
|
||||
### **1. 体系结构与设计哲学 (Architecture & Philosophy)**
|
||||
|
||||
此图标系统旨在将图标数据管理与UI渲染完全分离,以实现最大程度的内聚和可复用性。
|
||||
|
||||
* **数据层 (Data Layer):** 通过一个**组合式函数 `useCrypto.ts`** 进行管理。该函数在应用启动时加载并索引一次静态图标数据,提供高效、响应式的查询接口供任何组件使用。数据是静态的,在构建时生成,确保了运行时的高性能和零API依赖。
|
||||
* **表现层 (Presentation Layer):** 一个**单一职责组件 `CryptoIcon.vue`** 负责根据传入的标识符(单个货币或交易对)从数据层获取SVG数据并完成渲染。该组件将完全兼容Vuetify的尺寸、响应式和主题系统。
|
||||
|
||||
-----
|
||||
|
||||
### **2. 数据管理模块 (`composables/useCrypto.ts`)**
|
||||
|
||||
这是系统的核心逻辑,负责图标数据的加载、索引和查询。
|
||||
|
||||
**2.1. 数据源与生成 (Data Source & Generation)**
|
||||
|
||||
* **数据来源:**
|
||||
* **元数据:** 使用python3构建脚本,放置于`ProjectTonyStack/TonyMask/scripts/`中,用于从[TradingView](https://tradingview.com/markets/cryptocurrencies/prices-all/) 或者从稳定、公开的加密货币API(例如 CoinGecko 或 CoinMarketCap 的免费API)通过手动执行,获取前1000名加密货币的图标数据。
|
||||
* **SVG图标:** 不要使用开源库 **`cryptocurrency-icons`**。该库的内容过时,无法满足需求,请设计爬虫脚本爬取svg图标数据。
|
||||
* **数据静态化:**
|
||||
* 该脚本将整合后的元数据和SVG内容生成一个静态的 `crypto-data.ts` 文件,存放于 `src/assets/` 目录下。此文件将导出一个类型为 `CryptoCurrencyInfo[]` 的常量数组。这种方式避免了客户端的运行时API请求,极大地提升了性能和稳定性。
|
||||
* 对于未在 `cryptocurrency-icons` 库中找到的图标,脚本应自动关联一个预定义的“未知货币”SVG图标。
|
||||
|
||||
**2.2. 数据结构定义 (`types/crypto.d.ts`)**
|
||||
|
||||
```typescript
|
||||
// src/types/crypto.d.ts
|
||||
|
||||
/**
|
||||
* @interface CryptoCurrencyInfo
|
||||
* @description 定义了单个加密货币的完整信息结构。
|
||||
*/
|
||||
export interface CryptoCurrencyInfo {
|
||||
/** 货币的唯一ID (e.g., "bitcoin") */
|
||||
id: string;
|
||||
/** 货币简称/符号 (e.g., "BTC") */
|
||||
symbol: string;
|
||||
/** 货币全称 (e.g., "Bitcoin") */
|
||||
name: string;
|
||||
/** 货币全称的大写形式 (e.g., "BITCOIN") */
|
||||
nameUpperCase: string;
|
||||
/** 货币的SVG图标原始内容 (作为字符串) */
|
||||
svg: string;
|
||||
/** 市值排名 */
|
||||
marketCapRank: number;
|
||||
}
|
||||
```
|
||||
|
||||
**2.3. Composable 实现 (`useCrypto.ts`)**
|
||||
|
||||
* **状态管理:**
|
||||
|
||||
* 使用 `shallowRef` 来存储从 `crypto-data.ts` 导入的完整货币列表,因为该数据在应用生命周期内是只读的,无需深度响应。
|
||||
* 在首次调用时,一次性构建多个 `Map` 对象用于快速索引,并将其存储在模块作用域的常量中,实现单例模式,避免重复计算。
|
||||
|
||||
* **高效查询机制:**
|
||||
|
||||
* **O(1) 查找:** 创建三个独立的 `Map` 来满足不同的查询需求:
|
||||
1. `symbolMap: Map<string, CryptoCurrencyInfo>` (通过简称查询, e.g., 'BTC')
|
||||
2. `nameMap: Map<string, CryptoCurrencyInfo>` (通过全称小写查询, e.g., 'bitcoin')
|
||||
3. `idMap: Map<string, CryptoCurrencyInfo>` (通过ID查询, e.g., 'bitcoin')
|
||||
* **查询函数 `findCryptoInfo`:**
|
||||
* 设计一个签名清晰的函数 `const findCryptoInfo = (identifier: string): CryptoCurrencyInfo | undefined => { ... }`。
|
||||
* 该函数内部将尝试按以下顺序和逻辑进行高效查找:
|
||||
1. 将输入 `identifier` 转换为小写。
|
||||
2. 优先在 `symbolMap` 中查找。
|
||||
3. 若未找到,则在 `nameMap` 中查找。
|
||||
4. 若仍未找到,则在 `idMap` 中查找。
|
||||
5. 返回找到的 `CryptoCurrencyInfo` 对象,否则返回 `undefined`。
|
||||
* **交易对解析函数 `parseTradingPair`:**
|
||||
* 设计一个函数 `const parseTradingPair = (pair: string): { base: CryptoCurrencyInfo | undefined, quote: CryptoCurrencyInfo | undefined } => { ... }`。
|
||||
* 该函数负责解析常见的交易对格式(如 `BTCUSDT`, `ETHUSDT`)。它会尝试从已知的稳定币后缀(`USDT`, `USDC`, `BUSD`, `DAI`等)分割基础货币和计价货币,然后分别调用 `findCryptoInfo` 进行查找。
|
||||
|
||||
-----
|
||||
|
||||
### **3. UI组件 (`components/CryptoIcon.vue`)**
|
||||
|
||||
此组件是图标的最终渲染载体。
|
||||
|
||||
**3.1. 组件API设计 (Props)**
|
||||
|
||||
```typescript
|
||||
// CryptoIcon.vue
|
||||
import type { CryptoCurrencyInfo } from '@/types/crypto.d.ts';
|
||||
|
||||
interface Props {
|
||||
/** 单个货币标识符 (简称、全称或ID),与`pair`属性互斥 */
|
||||
symbol?: string;
|
||||
/** 交易对字符串 (e.g., "BTCUSDT"),与`symbol`属性互斥 */
|
||||
pair?: string;
|
||||
/**
|
||||
* 图标尺寸,可以是数字(px)或Vuetify预设的尺寸字符串
|
||||
* @default 24
|
||||
*/
|
||||
size?: string | number;
|
||||
/**
|
||||
* 是否仅显示图标,不显示旁边的名称
|
||||
* @default false
|
||||
*/
|
||||
iconOnly?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
* 组件逻辑中必须包含对 `symbol` 和 `pair` 互斥性的校验。
|
||||
|
||||
**3.2. 渲染逻辑 (`<template>`)**
|
||||
|
||||
* **根元素:** 使用 `div` 作为容器,并应用 `d-inline-flex align-center` 以确保图标和文字垂直对齐。
|
||||
* **图标容器:**
|
||||
* **单个图标:** 直接渲染一个 `div`,使用 `v-html` 绑定从 `useCrypto` composable中查找到的SVG字符串。
|
||||
* **交易对图标:**
|
||||
* 创建一个相对定位的容器 `div` (`position: relative`)。
|
||||
* 内部渲染两个绝对定位的 `div`,分别用于基础货币和计价货币。
|
||||
* **样式:** 仿照TradingView,基础货币图标(左)尺寸为100%,计价货币图标(右下)尺寸约为60%,通过 `bottom: -10%; right: -10%;` 进行偏移,并设置更高的 `z-index` 和一个小的白色边框(使用Vuetify主题变量 `rgb(var(--v-theme-surface))`)以创建堆叠效果。
|
||||
* **健壮性:**
|
||||
* 当 `findCryptoInfo` 或 `parseTradingPair` 返回 `undefined` 时,必须优雅地降级,显示预定义的“未知货币”图标。
|
||||
* 使用可选链 (`?.`) 和空值合并运算符 (`??`) 安全地访问数据。
|
||||
* **名称展示:**
|
||||
* 当 `iconOnly` 为 `false` 时,在图标右侧渲染货币全称大写 (`nameUpperCase`)。使用 `v-if` 控制其显示。
|
||||
* 使用 `v-text` 绑定名称,并应用Vuetify的排版类 (`text-subtitle-2` 或类似)。
|
||||
|
||||
**3.3. 样式与主题 (Styling & Theming)**
|
||||
|
||||
* **严禁硬编码颜色。** SVG内部的 `fill` 或 `stroke` 属性,如果需要跟随主题变化(例如在单色图标版本中),应设置为 `currentColor`。
|
||||
* 组件的所有样式必须能完美适配**明亮 (Light)** 和**黑暗 (Dark)** 两种主题。边框、背景等颜色必须使用Vuetify主题变量,如 `border-color: rgb(var(--v-theme-surface));`。
|
||||
|
||||
**3.4. 无障碍 (Accessibility - a11y)**
|
||||
|
||||
* 图标的根元素需添加 `role="img"`。
|
||||
* 动态绑定 `aria-label` 属性,其内容为货币的全称(例如 `aria-label="Bitcoin"`)或交易对的名称(例如 `aria-label="Bitcoin / Tether"`),为屏幕阅读器用户提供清晰的上下文。
|
||||
|
||||
-----
|
||||
|
||||
### **4. 项目结构整合 (Directory Structure)**
|
||||
|
||||
```
|
||||
src/
|
||||
├── assets/
|
||||
│ └── crypto-data.ts # (构建时生成) 静态图标数据
|
||||
├── components/
|
||||
│ └── CryptoIcon.vue # UI渲染组件
|
||||
├── composables/
|
||||
│ └── useCrypto.ts # 数据逻辑与查询核心
|
||||
├── types/
|
||||
│ └── crypto.d.ts # TypeScript类型定义
|
||||
scripts/
|
||||
└── generate-crypto-data.ts # 用于在构建时生成数据的Node.js脚本
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
### **5. 最终实现指令 (Actionable Prompt for Implementation)**
|
||||
|
||||
现在,请严格遵循以上这份详尽的架构设计方案,完成`CryptoIcon.vue`组件、`useCrypto.ts`组合式函数、`crypto.d.ts`类型定义文件以及`generate-crypto-data.ts`构建脚本的编码工作。确保最终产出的代码是生产级的,完全符合`vue3-typescript-stye.md`中定义的所有规范,特别是在TypeScript强类型、组合式API、Vuetify 3集成、主题适配和代码健壮性方面达到典范水平。
|
||||
243
3.1-ProjectTonyStack-前端/2-AI优化设计/2-组件/api_currency_selector.md
Normal file
243
3.1-ProjectTonyStack-前端/2-AI优化设计/2-组件/api_currency_selector.md
Normal file
@@ -0,0 +1,243 @@
|
||||
**角色**: 你是一位资深前端架构师,精通 Vue 3、TypeScript 和 Vuetify 3,严格遵循 Material Design 3 设计哲学和企业级代码规范。
|
||||
|
||||
**任务**: 根据以下详细设计方案,创建一个名为 `CurrencySelector` 的通用、高性能、主题自适应的加密货币/交易对选择器组件。
|
||||
|
||||
-----
|
||||
|
||||
#### **1. 组件核心概述 (Component Overview)**
|
||||
|
||||
`CurrencySelector` 是一个基于 Vuetify 3 的高级表单控件。它旨在提供一个功能强大且用户友好的界面,用于从一个可配置的数据源中单选或多选加密货币或交易对。组件内部将封装数据加载、搜索过滤、状态管理以及复杂的UI渲染逻辑,同时对外暴露简洁、类型安全的API。
|
||||
|
||||
-----
|
||||
|
||||
#### **2. 核心技术栈与规范 (Core Tech Stack & Specifications)**
|
||||
|
||||
* **框架 (Framework):** Vue 3.x
|
||||
* **脚本 (Script):** `<script setup lang="ts">` (组合式API)
|
||||
* **语言 (Language):** TypeScript (严格模式,**严禁 `any`** )
|
||||
* **UI库 (UI Library):** Vuetify 3.x
|
||||
* **状态管理 (State Management):** Pinia
|
||||
* **设计规范 (Design System):** Google Material Design 3 (MD3)
|
||||
* **强制遵循**: `vue3-typescript-stye.md` 中的所有规范。
|
||||
|
||||
-----
|
||||
|
||||
#### **3. 组件API设计 (Props & Emits)**
|
||||
|
||||
为了实现组件的高度可复用性和灵活性,其API必须经过精心设计,并提供完整的TypeScript类型定义。
|
||||
|
||||
##### **3.1 类型定义 (Type Definitions)**
|
||||
|
||||
首先,在 `@/types/currency.d.ts` 中定义核心数据结构:
|
||||
|
||||
```typescript
|
||||
// @/types/currency.d.ts
|
||||
|
||||
/**
|
||||
* @interface CurrencyItem
|
||||
* @description 代表一个可选择的币种或交易对的基础数据结构
|
||||
*/
|
||||
export interface CurrencyItem {
|
||||
id: string | number; // 唯一标识符
|
||||
name: string; // 完整名称, e.g., "Bitcoin"
|
||||
symbol: string; // 简称/符号, e.g., "BTC"
|
||||
pair?: string; // 交易对名称, e.g., "BTCUSDT"
|
||||
iconUrl?: string; // 图标的URL地址
|
||||
}
|
||||
```
|
||||
|
||||
##### **3.2 Props**
|
||||
|
||||
```typescript
|
||||
// CurrencySelector.vue
|
||||
|
||||
import type { CurrencyItem } from '@/types/currency';
|
||||
|
||||
interface Props {
|
||||
// v-model 绑定值,支持单选或多选
|
||||
modelValue: string | number | (string | number)[] | null;
|
||||
|
||||
// 选项列表数据源
|
||||
items: CurrencyItem[];
|
||||
|
||||
// 是否支持多选
|
||||
multiple?: boolean;
|
||||
|
||||
// 占位符文本
|
||||
placeholder?: string;
|
||||
|
||||
// 是否处于加载状态
|
||||
loading?: boolean;
|
||||
|
||||
// 是否禁用
|
||||
disabled?: boolean;
|
||||
|
||||
// 返回对象的属性,决定了 modelValue 的值
|
||||
itemValue?: keyof CurrencyItem; // 默认为 'id'
|
||||
|
||||
// 自定义清除按钮图标
|
||||
clearable?: boolean;
|
||||
clearIcon?: string;
|
||||
|
||||
// 错误信息和状态
|
||||
error?: boolean;
|
||||
errorMessages?: string | string[];
|
||||
|
||||
// Vuetify v-field 的 density 和 variant 属性
|
||||
density?: 'default' | 'comfortable' | 'compact';
|
||||
variant?: 'outlined' | 'filled' | 'underlined' | 'solo' | 'plain';
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
multiple: false,
|
||||
placeholder: '点击选择币种',
|
||||
loading: false,
|
||||
disabled: false,
|
||||
itemValue: 'id',
|
||||
clearable: true,
|
||||
density: 'compact',
|
||||
variant: 'outlined'
|
||||
});
|
||||
```
|
||||
|
||||
##### **3.3 Emits**
|
||||
|
||||
```typescript
|
||||
// CurrencySelector.vue
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string | number | (string | number)[] | null): void;
|
||||
}>();
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
#### **4. 内部实现与架构决策 (Internal Implementation & Architecture)**
|
||||
|
||||
##### **4.1 基础组件选用**
|
||||
|
||||
* **核心**: 使用 Vuetify 的 `VAutocomplete` 组件作为基础。它原生支持搜索、单选/多选、异步加载以及丰富的插槽,能完美满足需求。**不要手动实现下拉菜单的显示/隐藏逻辑和层级管理**,`VAutocomplete` 内置的 `v-menu` 会自动处理。
|
||||
|
||||
##### **4.2 UI/UX 实现细节**
|
||||
|
||||
* **已选项渲染 (胶囊按钮)**:
|
||||
|
||||
* 利用 `VAutocomplete` 的 `#selection` 插槽。
|
||||
* 在插槽内,使用 `VChip` 组件来渲染已选项,以实现“胶囊按钮”效果。
|
||||
* `VChip` 应包含币种图标 (`VAvatar` 或 `VIcon`)、简称 (`item.raw.symbol` 或 `item.raw.pair`),并自带删除按钮 (`closable=true`)。
|
||||
* `VChip` 的颜色应使用主题变量,例如 `color="primary"`, 使其与输入框背景色区分。
|
||||
|
||||
* **下拉选项渲染**:
|
||||
|
||||
* 利用 `VAutocomplete` 的 `#item` 插槽。
|
||||
* 在插槽内,使用 `VListItem` 组件。
|
||||
* `VListItem` 包含 `VListItemAvatar` (用于图标) 和 `VListItemTitle` (用于显示简称或交易对名称)。
|
||||
|
||||
* **主题适配 (Theming)**:
|
||||
|
||||
* **严禁**任何硬编码的颜色值。
|
||||
* 组件所有背景、文字、边框颜色**必须**使用 Vuetify 的主题系统。例如,`VChip` 的颜色应设置为 `primary` 或 `secondary` 等主题色,输入框的背景将自动适应主题。
|
||||
* 币种图标本身是图片,无需颜色变换,符合原始要求。
|
||||
|
||||
* **滚动与显示数量**:
|
||||
|
||||
* `VAutocomplete` 的下拉菜单默认就是可滚动的。
|
||||
* 要控制显示高度,可以通过 CSS `var(--v-overlay-max-height)` 变量或为 `menu-props` 设置样式 `{ maxHeight: '300px' }` 来实现,而非通过参数配置固定的`n`个选项。这更符合响应式设计。
|
||||
|
||||
##### **4.3 逻辑与功能**
|
||||
|
||||
* **搜索功能**:
|
||||
|
||||
* `VAutocomplete` 自带文本搜索功能。
|
||||
* 为了更精准地匹配 `name`, `symbol`, `pair`,可以提供一个自定义的 `custom-filter` 函数。
|
||||
```typescript
|
||||
const currencyFilter = (itemTitle: string, queryText: string, item: { raw: CurrencyItem }): boolean => {
|
||||
const query = queryText.toLowerCase();
|
||||
const { name, symbol, pair } = item.raw;
|
||||
return (
|
||||
name.toLowerCase().includes(query) ||
|
||||
symbol.toLowerCase().includes(query) ||
|
||||
(pair && pair.toLowerCase().includes(query))
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
* **数据管理**:
|
||||
|
||||
* 如果币种列表是全局共享且不常变动的,**推荐**使用 Pinia Store (`@/store/currency.ts`) 进行管理。
|
||||
* Store 应包含 state (存储列表)、getters (用于快速查找) 和 actions (用于异步获取数据并缓存)。
|
||||
* 组件在 `onMounted` 时,检查 Store 中是否已有数据,若无则 dispatch action 获取。
|
||||
|
||||
* **响应式设计**:
|
||||
|
||||
* 组件的尺寸不应由内部 prop 控制。应遵循 Vuetify 的栅格系统,将其放置在 `VCol` 中,由父级布局决定其宽度。这保证了组件在不同屏幕尺寸下的适应性。
|
||||
|
||||
-----
|
||||
|
||||
#### **5. 非功能性需求 (Non-Functional Requirements)**
|
||||
|
||||
* **性能 (Performance)**:
|
||||
|
||||
* 对于超大数据量的选项列表 (e.g., \> 1000),应考虑使用 `VVirtualScroll` 组件包裹在下拉菜单中,以提高渲染性能。
|
||||
* 搜索输入的 debounce 由 `VAutocomplete` 内置处理。
|
||||
|
||||
* **可访问性 (Accessibility - A11Y)**:
|
||||
|
||||
* 通过使用 `VAutocomplete` 和 `VChip` 等标准 Vuetify 组件,大部分 WAI-ARIA 属性会自动得到支持。
|
||||
* 确保所有可交互元素都有清晰的焦点状态,并支持键盘导航。
|
||||
|
||||
* **错误处理 (Error Handling)**:
|
||||
|
||||
* 当 `loading` 为 `true` 时,显示 `VProgressLinear` 或 `VProgressCircular`。
|
||||
* 当 `items` 数组为空时,应在下拉菜单中显示提示信息,如“无匹配数据”。
|
||||
* 当接收到 `error` prop 时,`VAutocomplete` 边框应变为错误颜色,并显示 `error-messages`。
|
||||
|
||||
-----
|
||||
|
||||
#### **6. 示例用法 (Example Usage)**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<CurrencySelector
|
||||
v-model="selectedSingle"
|
||||
:items="currencyStore.list"
|
||||
:loading="currencyStore.loading"
|
||||
label="选择单个币种"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<CurrencySelector
|
||||
v-model="selectedMultiple"
|
||||
:items="currencyStore.list"
|
||||
:loading="currencyStore.loading"
|
||||
label="选择多个交易对"
|
||||
multiple
|
||||
placeholder="搜索并选择交易对"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useCurrencyStore } from '@/store/currency';
|
||||
import CurrencySelector from '@/components/CurrencySelector.vue';
|
||||
|
||||
const currencyStore = useCurrencyStore();
|
||||
|
||||
const selectedSingle = ref<string | null>(null);
|
||||
const selectedMultiple = ref<string[]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
currencyStore.fetchCurrencies();
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
**最终指令**: 请根据以上这份详尽、严谨的架构设计方案,生成 `CurrencySelector.vue` 组件的完整代码。代码必须是生产级别的,并体现出卓越的工程实践。
|
||||
69
3.1-ProjectTonyStack-前端/2-AI优化设计/2-组件/api_mask_layout.md
Normal file
69
3.1-ProjectTonyStack-前端/2-AI优化设计/2-组件/api_mask_layout.md
Normal file
@@ -0,0 +1,69 @@
|
||||
**角色 (Role):** 你是一位精通 Vue 3、TypeScript 和 Vuetify 的资深前端架构师。你的任务是构建基础布局框架。
|
||||
|
||||
**背景 (Context):** 你必须严格遵守所提供的 `vue3-typescript-stye.md` 风格指南中的所有原则。最终目标是产出一个健壮、可维护、生产就绪的应用外壳,它将作为未来所有页面的实现蓝图。此布局必须是完全响应式、支持主题切换,并完全基于 Vue 3 组合式 API 和 Vuetify 3 组件构建。
|
||||
|
||||
**核心任务 (Core Task):**
|
||||
|
||||
在 `src/layouts/` 目录下,创建一个名为 `Default.vue` 的主应用布局组件。该组件需实现一个经典的三段式结构:一个持久化的导航抽屉、一个顶部应用栏和一个用于承载路由视图的主内容区。所有与布局 UI 相关的状态(例如,侧边栏的收缩状态、当前的主题)必须在一个专用的 Pinia store 中进行集中管理。
|
||||
|
||||
---
|
||||
|
||||
### **1. 状态管理:布局 Store (Pinia)**
|
||||
|
||||
在创建组件之前,首先定义状态管理模块。
|
||||
|
||||
* **文件位置:** `src/store/layout.ts`
|
||||
* **Store 名称:** `useLayoutStore`
|
||||
* **State:**
|
||||
* `isSidebarMini: Ref<boolean>`: 管理导航抽屉的收缩/展开状态,默认为 `false`。
|
||||
* `theme: Ref<'light' | 'dark'>`: 存储当前的主题名称,默认为 `'light'`。
|
||||
* **Actions:**
|
||||
* `toggleSidebar()`: 用于翻转 `isSidebarMini`布尔值的 action。
|
||||
* `setTheme(newTheme: 'light' | 'dark')`: 用于更新 `theme` 状态的 action。
|
||||
|
||||
**约束:** 所有的 state 属性和函数签名都必须拥有精确的 TypeScript 类型。
|
||||
|
||||
### **2. 组件拆解: `Default.vue`**
|
||||
|
||||
#### **a. Script 部分 (`<script setup lang="ts">`)**
|
||||
|
||||
* **导入:**
|
||||
* 从 Vue 和 Vue Router 中导入必要的模块 (`ref`, `watch`, `useRoute`)。
|
||||
* 从 `@/store/layout.ts` 中导入 `useLayoutStore`。
|
||||
* 从 Vuetify 中导入 `useTheme` 和 `useDisplay` 组合式函数。
|
||||
* **Store 实例化:**
|
||||
* 实例化布局 store: `const layoutStore = useLayoutStore();`
|
||||
* 实例化 Vuetify theme 组合式函数: `const theme = useTheme();`
|
||||
* 实例化 Vuetify display 组合式函数: `const display = useDisplay();`
|
||||
* **响应式逻辑:**
|
||||
* 为导航抽屉的可见性创建一个本地 `ref`,`const drawer = ref(true);`。
|
||||
* 使用 `watch` 监听 `useDisplay` 返回的 `display.mobile`。如果视口变为移动端 (`display.mobile.value` 为 `true`),则默认将导航抽屉设置为临时隐藏 (`drawer.value = false`)。
|
||||
* 实现一个 `toggleTheme()` 函数,该函数调用 Pinia store 中的 `setTheme` action,并通过 `theme.global.name.value = layoutStore.theme;` 来更新 Vuetify 的主题。此函数将绑定到一个 UI 控件上。
|
||||
|
||||
#### **b. 模板部分 (`<template>`)**
|
||||
|
||||
* **根元素:** 使用 `<v-layout>` 组件作为最外层包裹容器。
|
||||
* **导航抽屉 (`<v-navigation-drawer>`):**
|
||||
* 将其可见性与 `drawer` ref 进行双向绑定 (`v-model="drawer"`)。
|
||||
* 将其“图标模式”状态与 Pinia store 中的 `isSidebarMini` 进行绑定 (`:rail="layoutStore.isSidebarMini"`)。
|
||||
* 实现一个占位用的 `<v-list>`,包含数个 `<v-list-item>` 用于导航。确保其中一个项目拥有 `active` 属性,以展示单页应用中高亮当前路由的样式。
|
||||
* 在抽屉底部,添加一个 `<template v-slot:append>` 区块。在其中放置一个 `<v-btn>` 用于切换主题。该按钮的图标应根据当前主题变化(例如,暗色主题下为 'mdi-weather-sunny',明亮主题下为 'mdi-weather-night')。将其 `@click` 事件绑定到 `toggleTheme()` 方法。
|
||||
* **应用栏 (`<v-app-bar>`):**
|
||||
* 包含一个 `<v-app-bar-nav-icon>` 按钮,用于切换 `drawer` ref 的值 (`@click="drawer = !drawer"`)。
|
||||
* 包含一个 `<v-toolbar-title>`,用于动态显示当前页面的标题占位符。
|
||||
* 在最右侧添加一个按钮,其图标为 `mdi-dots-vertical`。
|
||||
* **主内容区 (`<v-main>`):**
|
||||
* 主内容区必须是流式的,能自适应剩余空间。
|
||||
* 在 `<v-main>` 内部,渲染 `<router-view />` 组件,使得该布局能够根据当前路由托管不同的页面组件。
|
||||
* **页面 Header:** 在 `<v-main>` 内部,但在 `<router-view>` 之前,添加一个容器(例如,一个带有特定 class 的 `div`)作为页面 Header。在此处显示当前路由的名称作为占位符(例如,使用 `useRoute().name`)。
|
||||
|
||||
### **3. 样式与主题**
|
||||
|
||||
* **禁止硬编码样式:** 绝不使用任何硬编码的颜色值 (例如 `#FFF`, `rgb(0,0,0)`)。
|
||||
* **Vuetify 主题变量:** 所有的自定义样式(如有)必须使用 Vuetify 的主题系统。例如,使用 CSS 变量 `background-color: rgb(var(--v-theme-surface));` 或 SASS 函数 `color: v-theme(on-surface);`。
|
||||
* **主题响应:** 整个布局,包括所有文本、图标和表面,都必须在明亮和暗色主题之间无缝切换,不能有任何视觉瑕疵。
|
||||
|
||||
### **4. 响应式设计**
|
||||
|
||||
* **移动端:** 在移动端视口(由 `useDisplay` 判断),导航抽屉应默认关闭,并以临时/模态模式运行。`<v-app-bar-nav-icon>` 成为打开它的主要方式。
|
||||
* **平板/桌面端:** 在更大的屏幕上,导航抽屉应默认可见。用于切换其 `rail` 状态(完整模式与图标模式)的按钮应位于 `<v-app-bar>` 中。为此,在 `<v-app-bar-nav-icon>` 之后添加一个 `<v-btn icon="mdi-menu" @click.stop="layoutStore.toggleSidebar()"></v-btn>`。
|
||||
@@ -0,0 +1,6 @@
|
||||
不要使用开源库 **`cryptocurrency-icons`**。该库的内容过时,无法满足需求。使用python3构建脚本,放置于`ProjectTonyStack/TonyMask/scripts/`中,用于从[TradingView](https://tradingview.com/markets/cryptocurrencies/prices-all/) 爬取排名前100的加密货币信息,包含吗svg图标,全名称,简称,大写名称,排名等.该脚本将整合后的元数据和SVG内容生成一个静态的 `crypto-data.ts` 文件,存放于 `src/assets/` 目录下。此文件将导出一个类型为 `CryptoCurrencyInfo[]` 的常量数组。
|
||||
请根据上述的要求,设计一个爬取的python3脚本,注意是在无浏览器环境运行脚本
|
||||
|
||||
|
||||
包含加密货币信息的网页保存在[tradingview-save-704.html](ProjectTonyStack/TonyMask/scripts/tradingview-save-704.html)包含svg图标,加密货币简称,加密货币全称, 请实现一个python3脚本解析其中包含的信息.并且调用CoinGecko API获取加密货币的大写名称,描述,市场排名信息,注意是在无浏览器环境运行脚本
|
||||
该脚本将整合后的元数据和SVG内容生成一个静态的 `crypto-data.ts` 文件,存放于 `src/assets/` 目录下。此文件将导出一个类型为 `CryptoCurrencyInfo[]` 的常量数组。
|
||||
@@ -0,0 +1,12 @@
|
||||
请遵守前端设计规范@/root/ProjectTonyStack/gemini_cli_prompt/TonyMaskPrompt/vue3-typescript-stye.md,针对@/root/ProjectTonyStack/TonyMask/src/components/CurrencySelector.vue 进行如下的修改
|
||||
1 修改图标加载方式,当打开页面后需要即刻展示图标
|
||||
2 当选中币种之后,图标和名称显示不正常,请修改此bug
|
||||
1 当前显示的是object Object 请修改为胶囊按钮状显示图标及名称
|
||||
3 下拉选项,增大图标和名称之间的间隔
|
||||
|
||||
|
||||
请继续@/root/ProjectTonyStack/TonyMask/src/components/CurrencySelector.vue 进行如下的修改
|
||||
1 选中币种需要用胶囊按钮包含展示图标以及名称,现在无图标
|
||||
2 选中币种需要有删除按键,点击取消选择,现在没有
|
||||
3 输入框有删除按键,点击删除全部选中
|
||||
4 输入栏,默认高度可以增高一些
|
||||
@@ -0,0 +1,74 @@
|
||||
**背景 (Context):**
|
||||
|
||||
你是一名资深前端架构师,负责为我们的 Vue 3 + TypeScript + Vuetify 3 项目构建基础工具库。该项目要求极高的代码质量、健壮性、可维护性和开发效率。当前任务是设计并实现一个专业、可靠且覆盖全面的时间处理工具模块。
|
||||
W
|
||||
**核心指令 (Core Directive):**
|
||||
|
||||
请严格遵循 `vue3-typescript-stye.md` 中定义的所有规范,创建一个位于 `src/utils/time.ts` 的时间工具模块。此模块必须基于业界公认的最佳实践,封装所有与时间相关的操作,确保前端应用在处理时间数据时的一致性、准确性和健朗性。放弃手动计算时间的原始思路,全面拥抱成熟的第三方库来保证专业性。
|
||||
|
||||
---
|
||||
|
||||
### **技术架构与设计规范 (Technical Architecture & Design Specification)**
|
||||
|
||||
**1. 核心依赖 (Core Dependency):**
|
||||
|
||||
* **唯一指定库:** 项目中所有的时间/日期操作**必须**使用 `date-fns` 和 `date-fns-tz` 库。严禁手动进行复杂的日期计算(如月份加减、年份推算),以规避闰年、月份天数不均等边界问题。
|
||||
* **安装命令:** 使用 pnpm 进行安装:`pnpm add date-fns date-fns-tz`。
|
||||
|
||||
**2. 模块设计与常量定义 (Module Design & Constants):**
|
||||
|
||||
* **文件路径:** `src/utils/time.ts`
|
||||
* **核心常量:**
|
||||
* `API_DATETIME_FORMAT`: 定义一个字符串常量,值为 `"yyyy-MM-dd HH:mm:ss"`,用于统一前后端接口的时间格式。
|
||||
* `TIMEZONE`: 定义一个字符串常量,值为 `"Asia/Shanghai"`,代表所有时间转换的目标时区(东八区)。
|
||||
|
||||
**3. 函数接口定义 (Function Interface Definitions):**
|
||||
|
||||
模块需要导出以下经过精心设计的函数,所有函数必须包含完整的 JSDoc 注释,并拥有精确的 TypeScript 类型签名。
|
||||
|
||||
* **`formatDate(date: Date | number, formatStr: string = API_DATETIME_FORMAT): string`**
|
||||
* **描述:** 将给定的 `Date` 对象或时间戳(`number`)格式化为指定格式的字符串。
|
||||
* **核心逻辑:** 使用 `date-fns-tz` 的 `formatInTimeZone` 函数,确保输出的字符串始终基于我们定义的 `TIMEZONE` (`Asia/Shanghai`)。
|
||||
* **参数:**
|
||||
* `date`: 必选,要格式化的 `Date` 对象或时间戳。
|
||||
* `formatStr`: 可选,目标格式字符串,默认为 `API_DATETIME_FORMAT`。
|
||||
* **返回值:** `string` - 格式化后的时间字符串。
|
||||
|
||||
* **`parseDate(dateStr: string, formatStr: string = API_DATETIME_FORMAT): Date`**
|
||||
* **描述:** 将符合标准格式的时间字符串解析为 `Date` 对象。
|
||||
* **核心逻辑:** 使用 `date-fns` 的 `parse` 函数进行解析。必须健壮地处理无效的输入字符串,如果解析失败,应返回一个无效的 `Date` 对象 (可以通过 `isValid` from `date-fns` 进行检查)。
|
||||
* **参数:**
|
||||
* `dateStr`: 必选,要解析的时间字符串,如 "2025-01-01 08:00:00"。
|
||||
* `formatStr`: 可选,源字符串的格式,默认为 `API_DATETIME_FORMAT`。
|
||||
* **返回值:** `Date` - 解析后的 `Date` 对象。
|
||||
|
||||
* **`getRelativeDate(options: { years?: number; months?: number; days?: number }, fromDate: Date = new Date()): Date`**
|
||||
* **描述:** 一个通用的、获取相对日期的核心函数。可根据当前时间或指定时间,推算过去或未来的某个时间点。
|
||||
* **核心逻辑:** 组合使用 `date-fns` 的 `add` 或 `sub` 系列函数(如 `subYears`, `subMonths`, `subDays`)来实现。操作必须是不可变的(immutable),即不修改原始 `fromDate` 对象。
|
||||
* **参数:**
|
||||
* `options`: 必选,一个包含 `years?`, `months?`, `days?` 的对象,值为负数表示过去,正数表示未来。
|
||||
* `fromDate`: 可选,计算的基准日期,默认为当前时间 `new Date()`。
|
||||
* **返回值:** `Date` - 计算得出的新 `Date` 对象。
|
||||
|
||||
* **便捷函数 (Convenience Functions):**
|
||||
* **描述:** 基于 `getRelativeDate` 和 `formatDate` 封装一系列便捷函数,以满足业务中的常见需求。这些函数使业务代码更具可读性。
|
||||
* **函数列表:**
|
||||
* `getMonthAgo(fromDate?: Date): string` - 获取一个月前的时间字符串。
|
||||
* `getThreeMonthsAgo(fromDate?: Date): string` - 获取三个月前的时间字符串。
|
||||
* `getYearAgo(fromDate?: Date): string` - 获取一年前的时间字符串。
|
||||
* `getThreeYearsAgo(fromDate?: Date): string` - 获取三年前的时间字符串。
|
||||
* **实现细节:** 每个函数内部调用 `getRelativeDate` 计算出 `Date` 对象,然后调用 `formatDate` 格式化为标准 API 字符串。例如,`getMonthAgo` 内部应调用 `getRelativeDate({ months: -1 }, fromDate)`。
|
||||
|
||||
**4. 非功能性需求 (Non-Functional Requirements):**
|
||||
|
||||
* **代码健壮性:**
|
||||
* 所有函数在接收参数时,应考虑 `null` 或 `undefined` 的可能性,并进行适当的防御性编程。
|
||||
* `parseDate` 函数是与外部数据交互的关键入口,其对异常输入的处理能力至关重要。
|
||||
* **性能:**
|
||||
* **必须** 采用 Tree-shaking 友好的方式从 `date-fns` 和 `date-fns-tz` 导入函数,例如:`import { format } from 'date-fns'`,而不是 `import * as dateFns from 'date-fns'`。
|
||||
* **不可变性 (Immutability):**
|
||||
* 所有对 `Date` 对象的操作都不能直接修改原始对象,而应返回一个新的 `Date` 实例。`date-fns` 默认遵循此模式,需确保在实现中维持这一特性。
|
||||
|
||||
**5. 最终产出 (Final Output):**
|
||||
|
||||
请根据以上所有规范,生成 `src/utils/time.ts` 文件的完整 TypeScript 代码。代码必须是生产级别的,包含完整的 JSDoc 注释、精确的类型定义,并体现出卓越的工程实践。
|
||||
@@ -0,0 +1,9 @@
|
||||
gemini -m gemini-2.5-flash
|
||||
|
||||
|
||||
统一使用[time.ts](TonyMask/src/utils/time.ts)中预定义的方法,修改[binanceOrder.ts](TonyMask/src/store/binanceOrder.ts),修改其中的时间获取方法,修改startTime为string类型
|
||||
|
||||
|
||||
针对@/root/ProjectTonyStack/TonyMask/src/components/binance/OrderDataTable.vue 进行如下的修改
|
||||
1 使用[time.ts](TonyMask/src/utils/time.ts)中预定义的方法修改时间处理方法,表格中统一使用字符串展示时间
|
||||
2 @/root/ProjectTonyStack/TonyMask/tmp/binance_order_response.json 是实际的订单返回数据,修改页面能够正确的处理并显示订单数据,目前无法显示
|
||||
90
3.1-ProjectTonyStack-前端/3-google_stich/stich_prompt.md
Normal file
90
3.1-ProjectTonyStack-前端/3-google_stich/stich_prompt.md
Normal file
@@ -0,0 +1,90 @@
|
||||
我没有前端设计相关的经验,请你作为前端UI设计的大师,利用Google Stich和Figma设计前端的页面,请根据我关于项目的大体设计,给出Stich的系统设计描述
|
||||
|
||||
|
||||
您好!很高兴能以UI设计大师的身份,与您一同规划这个项目。您的设计思路非常清晰,包含了现代前端设计的核心要素。这是一个绝佳的起点。
|
||||
|
||||
您提到的 “Google Stitch” 是一个很有趣的叫法。在设计界,我们通常会把一套统一的设计规范、原则和可复用组件的集合称为**设计系统(Design System)**。为了方便,我们就将我们这次项目的设计系统命名为 **‘Stitch’**,寓意它将所有界面元素无缝地“缝合”在一起,形成统一、协调的用户体验。
|
||||
|
||||
接下来,我将为您详细描述 ‘Stitch’ 设计系统在Figma中应如何规划与设计,以便前端团队能够利用Vuetify高效地实现它。
|
||||
|
||||
##### **a. 色彩 (Colors)**
|
||||
|
||||
我们将定义一个双主题调色板,确保在明亮和暗色模式下都有出色的可读性和视觉效果。
|
||||
|
||||
* **主色 (Primary):**
|
||||
* `primary-light`: `#1976D2` (用于按钮、高亮、活动状态等)
|
||||
* `primary-dark`: `#64B5F6`
|
||||
* **背景色 (Background):**
|
||||
* `background-light`: `#F5F5F5` (非常浅的灰色,避免纯白刺眼)
|
||||
* `background-dark`: `#121212` (经典的深邃黑)
|
||||
* **表面色 (Surface):**
|
||||
* `surface-light`: `#FFFFFF` (卡片、侧边栏、应用栏的背景)
|
||||
* `surface-dark`: `#1E1E1E` (比背景稍亮的深灰色)
|
||||
* **文本/图标色 (On-Color):**
|
||||
* `on-surface-light`: `#212121` (深灰色正文)
|
||||
* `on-surface-dark`: `#E0E0E0` (浅灰色正文)
|
||||
* `on-primary-light`: `#FFFFFF`
|
||||
* `on-primary-dark`: `#000000`
|
||||
* **辅助色 (Accent/Error/Success):** 将为其他状态(如错误提示、成功信息)定义相应的颜色。
|
||||
|
||||
##### **b. 字体 (Typography)**
|
||||
|
||||
选择一款现代、易读的无衬线字体(如 `Inter` 或 `Roboto`)。在Figma中创建文本样式。
|
||||
|
||||
* `H1 - Header`: 24px, Bold (用于主内容区的标题)
|
||||
* `H2 - Subheader`: 18px, Bold
|
||||
* `Body 1`: 16px, Regular (正文内容)
|
||||
* `Body 2`: 14px, Regular (次要信息)
|
||||
* `Button`: 14px, Medium
|
||||
* `Nav Item`: 16px, Medium (侧边栏菜单项)
|
||||
|
||||
##### **c. 间距与网格 (Spacing & Grid)**
|
||||
|
||||
* **基础单位:** `8px`。所有元素的内外边距、间距都应是8的倍数(如 `8, 16, 24, 32px`),形成和谐的垂直和水平韵律。
|
||||
* **栅格系统:** 遵循Vuetify的**12列栅格系统**。在Figma中,我们会为主要断点(桌面、平板、手机)创建不同的布局网格(Layout Grid),以确保响应式设计是可预测的。
|
||||
|
||||
#### **3. 组件库 (Component Library)**
|
||||
|
||||
我们将把您的布局要求拆解为一系列可复用的Figma组件。关键是利用Figma的 **自动布局(Auto Layout)** 和 **变体(Variants)** 功能。
|
||||
|
||||
##### **a. 侧边导航栏 (Side Navigation)**
|
||||
|
||||
这是核心组件,我们将创建两个主要的**变体**:`Collapsed=False` 和 `Collapsed=True`。
|
||||
|
||||
* **完整模式 (`Collapsed=False`):**
|
||||
* **结构:** 垂直 `Auto Layout` 容器。
|
||||
* **内容:**
|
||||
* 顶部:Logo + 应用名称。
|
||||
* 中部:导航项列表。每个导航项都是一个组件,包含 `Icon + Text`,并有 `State=Active` 和 `State=Default` 两个变体。活动状态的项会有不同的背景色(`primary-light/dark`)和文本颜色。
|
||||
* 底部:主题切换器。
|
||||
* **图标模式 (`Collapsed=True`):**
|
||||
* **结构:** 隐藏所有文本标签,只显示Logo图标和导航项图标。
|
||||
* **交互:** 当鼠标悬停(Hover)在图标上时,可以显示一个浮动的提示(Tooltip)来展示功能名称。
|
||||
|
||||
##### **b. 顶部应用栏 (Top App Bar)**
|
||||
|
||||
* **结构:** 水平 `Auto Layout` 容器。
|
||||
* **内容:**
|
||||
* 左侧(可选):一个汉堡菜单按钮,用于在小屏幕上控制侧边栏的显示/隐藏。
|
||||
* 右侧:可以放置用户头像、消息通知、搜索框等。
|
||||
|
||||
##### **c. 主内容区 (Main Content Area)**
|
||||
|
||||
* **结构:** 这是一个插槽(Slot)或框架(Frame)。
|
||||
* **内容:**
|
||||
* **页面标题 (Header):** 一个简单的组件,包含一个 `H1` 样式的标题和一段可选的描述性文字。
|
||||
* **内容容器 (Content Container):** 实际页面内容将在这里展示,例如卡片、表格、表单等。
|
||||
|
||||
#### **4. Figma 实现流程与原型**
|
||||
|
||||
1. **建立基础样式:** 在Figma中,首先创建并发布所有颜色样式和文本样式。
|
||||
2. **创建原子组件:** 从最小的元素开始,如按钮、图标、输入框。
|
||||
3. **组合分子组件:** 使用原子组件构建更复杂的组件,如上文描述的导航项、侧边导航栏等。大量使用 **Auto Layout** 和 **Variants** 来管理不同状态。
|
||||
4. **页面布局组装:**
|
||||
* 创建一个桌面尺寸的画板(Frame)。
|
||||
* 应用12列栅格。
|
||||
* 将“侧边导航栏”、“顶部应用栏”和“主内容区”组件实例拖入画板,并使用约束(Constraints)让它们固定在正确的位置。
|
||||
5. **创建交互原型 (Prototyping):**
|
||||
* **页面切换:** 将侧边栏的每个导航项链接到不同的页面画板,使用“智能动画”(Smart Animate)可以让内容区的切换显得平滑。
|
||||
* **侧边栏收缩:** 将“展开”侧边栏的折叠按钮链接到包含“收缩”侧边栏的页面副本,反之亦然。同样使用“智能动画”来实现平滑的过渡效果。
|
||||
* **主题切换:** 复制所有页面,并使用Figma插件(如 "Themer")或手动方式将所有组件切换到它们的暗色变体。然后将主题切换按钮链接到对应的明/暗色页面。
|
||||
Reference in New Issue
Block a user