1573 lines
83 KiB
Markdown
1573 lines
83 KiB
Markdown
- 📋 ReinLoop V1.0 界面像素级视觉审计与重构规范(PySide / QSS 全套)
|
||
|
||
-- 页面1
|
||
|
||
1. 全局色彩画布(Color Palette)
|
||
当前界面采用现代工业科技感的高级冷白调,对比度柔和,避免长时间操作视觉疲劳。
|
||
|
||
主背景色(App Background):#F8FAFC(柔和的微冷超浅灰蓝,作为整个窗口的基底色)。
|
||
|
||
卡片/区域背景(Card Background):#FFFFFF(纯白,用于 Modbus TCP 和 RTU 的分组容器)。
|
||
|
||
主品牌色/高亮蓝(Brand Accent):#0960D1(用于选中 Tab 的下划线、高亮图标以及分组左侧的小装饰块)。
|
||
|
||
按钮安全绿(Success Green):#0F955D(用于“连接设备”按钮)。
|
||
|
||
状态警示红(Error/Disconnect Red):#FF2424(用于右下角“未连接”状态点)。
|
||
|
||
文本颜色(Typography Colors):
|
||
|
||
主标题/表单标签:#1A1A1A(高清晰度深灰)。
|
||
|
||
次级信息/状态栏/占位符:#757575(中灰)。
|
||
|
||
表单边框色(Border Color):#E2E8F0(极细的浅灰线)。
|
||
|
||
2. 顶部导航栏与带有图标的 Tab 栏(Top Header & Tab Bar)
|
||
整体高度与内边距:Header 整体高度约 70px,布局容器(如 QHBoxLayout)设置 setContentsMargins(24, 12, 24, 12)。
|
||
|
||
左侧 LOGO 与主标题:
|
||
|
||
LOGO 使用 logo.svg,右侧有约 16px 的垂直分割线(可使用 QFrame,设置 width: 1px; background-color: #E2E8F0;)。
|
||
|
||
“ReinLoop V1.0” 字体:font-size: 22px; font-weight: bold; color: #1A1A1A;。
|
||
|
||
Tab 栏图标位置与间距控制(核心补充):
|
||
|
||
图标位置:图标必须优雅地垂直居中,放置在标签文字的正左侧。
|
||
|
||
代码级注入:在 PySide 中限制图标的显示尺寸为 16x16px 或 18x18px,并在添加标签页时绑定 src文件夹中的对应svg文件:
|
||
|
||
Python
|
||
tab_widget.setIconSize(QSize(18, 18))
|
||
tab_widget.addTab(tab_connect, QIcon("link_icon.svg"), "连接设置")
|
||
tab_widget.addTab(tab_control, QIcon("control_icon.svg"), "控制设置")
|
||
tab_widget.addTab(tab_debug, QIcon("debug_icon.svg"), "模型调试")
|
||
* **QSS 样式与防贴边微调**:
|
||
```css
|
||
QTabBar::tab {
|
||
background: transparent;
|
||
padding: 12px 24px 12px 16px; /* 上、右、下、左,为左侧图标留出呼吸感 */
|
||
font-size: 14px;
|
||
color: #64748B;
|
||
}
|
||
QTabBar::tab:!selected {
|
||
opacity: 0.65; /* 未选中时,SVG 图标与文字统一半透明隐去 */
|
||
}
|
||
QTabBar::tab:selected {
|
||
color: #0960D1;
|
||
font-weight: bold;
|
||
opacity: 1.0;
|
||
border-bottom: 3px solid #0960D1; /* 底部高亮蓝下划线 */
|
||
}
|
||
3. 分组卡片容器(Group Cards - Modbus TCP & RTU)
|
||
基本样式:
|
||
|
||
CSS
|
||
QFrame#GroupCard {
|
||
background-color: #FFFFFF;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 12px; /* 优雅的大圆角,严禁直角 */
|
||
}
|
||
* **阴影效果(QGraphicsDropShadowEffect)**:
|
||
为这两个卡片容器绑定一个微弱的模糊阴影:`color = QColor(0, 0, 0, 12)`(透明度约 5%),`blurRadius = 16`,`offset = (0, 4)`。
|
||
* **左侧装饰条标题**:
|
||
每个卡片左上角的蓝色竖线,可以用一个宽 `4px`,高 `16px`,`background-color: #0960D1; border-radius: 2px;` 的小组件来实现。
|
||
|
||
---
|
||
|
||
## 4. 表单与输入框组件(Form Layout & QLineEdit)
|
||
* **标签(QLabel)**:字体大小 `14px`,颜色 `#333333`,加粗。
|
||
* **输入框(QLineEdit)**:
|
||
* 高度固定:`min-height: 36px; max-height: 36px;`。
|
||
* QSS 样式:
|
||
```css
|
||
QLineEdit {
|
||
background-color: #FFFFFF;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 6px;
|
||
padding-left: 12px;
|
||
color: #333333;
|
||
font-size: 14px;
|
||
}
|
||
QLineEdit:focus {
|
||
border: 1px solid #0960D1; /* 聚焦时激活品牌蓝 */
|
||
}
|
||
|
||
|
||
5. 底部控制与状态栏(Bottom Action & Status Bar)
|
||
“刷新”按钮(QPushButton):
|
||
background-color: #FFFFFF; border: 1px solid #CCDBF0; color: #0960D1; border-radius: 6px; padding: 8px 20px; font-weight: bold;
|
||
|
||
“连接设备”按钮(QPushButton):
|
||
background-color: #0F955D; border: none; color: #FFFFFF; border-radius: 6px; padding: 8px 24px; font-weight: bold; font-size: 14px;
|
||
QPushButton:hover { background-color: #0D8250; }
|
||
|
||
底层状态栏(Status Bar):
|
||
|
||
左侧日志文本:“14:27:09-模型列表刷新成功”,字体 12px,颜色 #757575。
|
||
|
||
右侧状态:“● 未连接”,其中圆点和文字高亮为红色(#FF2424)。
|
||
|
||
|
||
“请根据这份整合了 image_fa96ff.png 界面风格与 image_049067.png 图标资产的像素级视觉规范,为我的 PySide 代码重构布局和 QSS 样式表。
|
||
特别注意:
|
||
|
||
Tab 栏的图标文件请严格对应 link_icon.svg、control_icon.svg 和 debug_icon.svg,且图标尺寸限定为 18x18 像素。
|
||
|
||
为了防止 Qt 默认的图标与文字贴得太近,请在 addTab() 传入字符串时,主动在文本前补一个半角空格(如 " 连接设置"),确保图标与文字之间有优雅的间距。
|
||
|
||
不要删除或修改我原本的业务逻辑和信号槽绑定。”
|
||
|
||
|
||
|
||
# 任务:ReinLoop V1.0 顶部导航与 Tab 栏结构性修复(第 2 轮)
|
||
在上一轮修改后,输入框和按钮的尺寸已经好多了。但目前的顶部导航和 Tab 栏结构严重错位(参考 `image_0561c8.png`)。它与我们的设计图(`image_05629d.png`)存在巨大的方向性偏差。
|
||
|
||
请严格按照以下 5 点,对顶部的布局结构和 QSS 进行彻底修复:
|
||
|
||
## 🛠️ 必须修复的视觉缺陷清单
|
||
|
||
1. 顶部布局结构大重构(修复 LOGO 遮挡与 Tab 移位)
|
||
- 【现状】:你错误地把 Tab 栏和 LOGO 塞进了同一行,导致整体高度严重不足,LOGO 下半部分被截断挤压。
|
||
- 【设计图要求】:整个顶部应该是【上下两层】的垂直布局(QVBoxLayout)。
|
||
* 上层(Layer 1):整行只有左侧的 LOGO、“ReinLoop V1.0” 标题,以及最右侧的设置图标按钮。这一行背景为纯白,不含任何 Tab 标签。
|
||
* 下层(Layer 2):独立的一行,用来放置完整的 Tab 栏。
|
||
- 【代码修复】:请将顶部整体调整为一个 `QVBoxLayout`,将 LOGO 整行(QHBoxLayout)作为第一个子布局,将 Tab 栏(QTabWidget/QTabBar)作为第二个子布局。
|
||
|
||
2. 补齐两行之间的灰色分割线与 Tab 背景色
|
||
- 【设计图要求】:LOGO 行与 Tab 栏行之间、以及 Tab 栏行与下方主体卡片之间,都有通栏的极细灰色分割线。并且,【整个 Tab 栏这一行】的背景色应该是一体化的浅灰色。
|
||
- 【QSS 修复】:
|
||
* 为包含 Tab 栏的这一层容器(或者 QTabWidget 的 TabBar 区域)设置背景色为 `#F8FAFC`(或与大背景融为一体的微冷浅灰)。
|
||
* 通过 QSS 或在布局间添加 `QFrame` 分割线,确保 LOGO 行下方有一条颜色为 `#E2E8F0` 的 `1px` 水平线。
|
||
|
||
3. 修复 Tab 栏选中状态的颜色逻辑
|
||
- 【现状】:目前所有的 Tab 文字和下划线都是死板的蓝色。
|
||
- 【设计图要求】:
|
||
* 【未选中状态】:文字和图标必须是【中灰色】(颜色为 `#64748B`),且【没有】蓝色底部下划线。
|
||
* 【选中状态】:只有当前被激活的 Tab,其文字、图标以及底部下划线才会一起变成【品牌蓝】(`#0960D1`)。
|
||
- 【QSS 修复】:请严格分离 `QTabBar::tab`(未选中默认样式)与 `QTabBar::tab:selected`(选中高亮样式)的颜色控制。
|
||
|
||
4. 彻底找回丢失的 SVG 图标(Icon)
|
||
- 【现状】:目前的界面上图标完全消失了。
|
||
- 【修复】:
|
||
* 确保在 PySide 代码中,使用 `QSize(18, 18)` 显式为 Tab 栏注入对应的图标资产:`link_icon.svg`(连接设置)、`control_icon.svg`(控制设置)、`debug_icon.svg`(模型调试)。
|
||
* 为了让图标在未选中时呈现灰色、选中时呈现蓝色,请检查你使用的 SVG 源码或直接在 `QTabBar::tab:!selected` 中加入 `opacity: 0.6;` 降低未选中图标的明度,使其趋近于灰色。
|
||
|
||
5. 移除输入框在未激活时的过粗蓝色边框
|
||
- 【现状】:目前即使没有点击输入框,部分输入框(如 PLC 地址)也自带了显眼的蓝色边框。
|
||
- 【修复】:请确保 `QLineEdit` 的默认状态边框为极细的淡灰色(`1px solid #E2E8F0`),【只有】在聚焦状态下(`QLineEdit:focus`)才允许变蓝。
|
||
|
||
---
|
||
|
||
## 💻 交付要求
|
||
请在完全不破坏后端逻辑、信号槽和既有变量名(如各输入框的 ObjectName)的前提下,重新梳理顶部布局结构并重写 QSS 样式表。请输出【完整重构后】的代码。
|
||
|
||
|
||
|
||
|
||
|
||
# 专项修复:解决 Tab 栏点击不亮、文字和图标始终为灰色的 Bug
|
||
|
||
目前点击 Tab 栏时,文字和图标完全没有变成蓝色,依然保持灰色。
|
||
这是因为:
|
||
1. `QTabBar` 不支持 `QIcon.Selected` 状态,它始终在使用 `QIcon.Normal` 的灰色图标。
|
||
2. QSS 样式在 `QTabBar` 独立使用时,`:selected` 伪状态由于属性权重问题可能失效了。
|
||
|
||
请你针对 `main_window.py` 进行如下修改,使用显式代码逻辑实现 100% 稳定的高亮切换:
|
||
|
||
## 1. 简化初始化图标代码
|
||
请将 `_setup_ui` 中原本复杂的 `QIcon.addFile` 逻辑删掉,直接将灰色和蓝色的文件路径保存为类属性,方便动态调用。例如:
|
||
```python
|
||
# 在 _setup_ui 中定义好 6 个资产路径
|
||
self._icons_gray = [
|
||
os.path.normpath(os.path.join(_src, "link_icon_gray.svg")),
|
||
os.path.normpath(os.path.join(_src, "control_icon_gray.svg")),
|
||
os.path.normpath(os.path.join(_src, "debug_icon_gray.svg"))
|
||
]
|
||
self._icons_blue = [
|
||
os.path.normpath(os.path.join(_src, "link_icon.svg")),
|
||
os.path.normpath(os.path.join(_src, "control_icon.svg")),
|
||
os.path.normpath(os.path.join(_src, "debug_icon.svg"))
|
||
]
|
||
|
||
# 初始时先填入图标(默认全灰)
|
||
self.tab_bar.addTab(QIcon(self._icons_gray[0]), " 连接设置")
|
||
self.tab_bar.addTab(QIcon(self._icons_gray[1]), " 控制设置")
|
||
self.tab_bar.addTab(QIcon(self._icons_gray[2]), " 模型调试")
|
||
|
||
|
||
|
||
与 Gemini 对话
|
||
/**
|
||
|
||
* 5. 上传用户许可证信息到 user_info 集合(按 user name 去重)
|
||
|
||
* @param {Object} event - 云函数参数
|
||
|
||
* @param {string} event._id - (必填) 数据唯一标识符,这里用户名代替
|
||
|
||
* @param {string} event.username - (必填) 用户名,如 "XX公司"
|
||
|
||
* @param {string} event.issued - (必填) 签发日,如 "2026-06-23"
|
||
|
||
* @param {string} event.expiry - (必填) 到期日,如 "2027-06-23"
|
||
|
||
* @param {string} event.license - (必填) 许可证内容(长字符串)
|
||
|
||
* @returns {Promise<Object>} 返回操作结果
|
||
|
||
*/
|
||
|
||
async function uploadUserInfo(event) {
|
||
|
||
const { _id, username, issued, expiry, license } = event;
|
||
|
||
// 参数校验
|
||
|
||
if (!_id || !username || !issued || !expiry || !license) {
|
||
|
||
return { success: false, errMsg: "缺少必填字段: _id, username, issued, expiry, license" };
|
||
|
||
}
|
||
|
||
|
||
|
||
try {
|
||
|
||
// 明确构造对象,避免任何歧义
|
||
|
||
const recordData = {
|
||
|
||
username: username,
|
||
|
||
issued: issued,
|
||
|
||
expiry: expiry,
|
||
|
||
license: license,
|
||
|
||
updateTime: new Date() // 或 db.serverDate(),但用 new Date() 更稳定
|
||
|
||
};
|
||
|
||
|
||
|
||
// 查询是否已存在该 username
|
||
|
||
const existed = await db.collection('user_info')
|
||
|
||
.where({ username: username })
|
||
|
||
.get();
|
||
|
||
|
||
|
||
if (existed.data.length > 0) {
|
||
|
||
// 存在则更新第一条(并删除多余同名记录)
|
||
|
||
const keepId = existed.data[0]._id;
|
||
|
||
await db.collection('user_info').doc(keepId).update({
|
||
|
||
data:{
|
||
|
||
issued: issued,
|
||
|
||
expiry: expiry,
|
||
|
||
license: license,
|
||
|
||
updateTime: db.serverDate()
|
||
|
||
}
|
||
|
||
});
|
||
|
||
// 删除多余的重复记录(如果有)
|
||
|
||
for (let i = 1; i < existed.data.length; i++) {
|
||
|
||
await db.collection('user_info').doc(existed.data[i]._id).remove();
|
||
|
||
}
|
||
|
||
return { success: true, action: 'updated', username };
|
||
|
||
} else {
|
||
|
||
// 不存在则新增
|
||
|
||
await db.collection('user_info').add({ data: recordData });
|
||
|
||
return { success: true, action: 'inserted', username };
|
||
|
||
}
|
||
|
||
} catch (err) {
|
||
|
||
console.error("上传用户信息失败:", err);
|
||
|
||
return { success: false, errMsg: err.message };
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
为什么数据库中的_id没有和用户名相同
|
||
|
||
你发现的这个现象,原因在于云函数的代码逻辑并没有把传入的 _id 赋值给新增的文档。
|
||
|
||
具体原因可以通过分析你提供的云函数代码和微信小程序云数据库的默认行为来解开:
|
||
|
||
1. 核心原因:代码未在 add() 时指定 _id
|
||
在你的 uploadUserInfo 云函数中,虽然你在最开始从 event 里解构出了 _id:
|
||
|
||
JavaScript
|
||
const { _id, username, issued, expiry, license } = event;
|
||
但是在执行新增数据(else 分支)时,你传入 add 方法的 recordData 结构如下:
|
||
|
||
JavaScript
|
||
const recordData = {
|
||
username: username,
|
||
issued: issued,
|
||
expiry: expiry,
|
||
license: license,
|
||
updateTime: new Date()
|
||
};
|
||
|
||
// ...
|
||
|
||
// 这里直接使用了 recordData,里面并不包含 _id 字段
|
||
await db.collection('user_info').add({ data: recordData });
|
||
2. 数据库的默认行为
|
||
在微信小程序云开发(或 UniCloud / 腾讯云开发)中,如果你使用 db.collection().add() 方法向数据库插入一条新记录:
|
||
|
||
如果传入的 data 对象中没有 _id 字段,数据库系统会自动为你生成一个 28位/32位的随机唯一字符串(即你在图片中看到的 "57fc8e266a3b85600ea88e53d6ff6bc")作为该记录的 _id。
|
||
|
||
如果你希望它的 _id 就是用户名(比如 "程序测试1"),你必须在 data 对象中显式地把 _id 放进去。
|
||
|
||
🛠️ 如何修改代码?
|
||
如果你希望数据库中的 _id 和用户名(username)完全保持一致,你需要修改云函数中 recordData 的定义,将 _id 显式写入。修改如下:
|
||
|
||
JavaScript
|
||
// 明确构造对象,避免任何歧义
|
||
const recordData = {
|
||
_id: username, // 👈 在这里显式指定 _id 为用户名(或者用传入的 _id 变量)
|
||
username: username,
|
||
issued: issued,
|
||
expiry: expiry,
|
||
license: license,
|
||
updateTime: new Date()
|
||
};
|
||
⚠️ 温馨提示:将 _id 设为自定义字符串(如用户名)后,该集合的 _id 就具备了唯一索引的特性。如果未来有同名用户直接触发 add() 操作,数据库会直接抛出 dup key(键重复)错误,这本身也是一种非常好的防重手段。
|
||
|
||
为什么我上传含有相同用户名的数据时没有二次确认
|
||
|
||
这也是由你当前的云函数代码逻辑决定的。
|
||
|
||
简单来说:因为你的云函数在发现用户名重复时,直接在后台静默执行了“更新旧数据、删除多余数据”的操作,并没有返回任何可以让客户端拦截并进行二次确认的信号。
|
||
|
||
我们可以对比一下你的 Python 脚本(license_gen.py)和云函数(JavaScript)的逻辑差异:
|
||
|
||
1. 根本原因:云函数直接把“防呆”给做完了
|
||
在你的云函数代码中,处理流程是这样的:
|
||
|
||
JavaScript
|
||
// 1. 查询是否已存在该 username
|
||
const existed = await db.collection('user_info')
|
||
.where({ username: username })
|
||
.get();
|
||
|
||
if (existed.data.length > 0) {
|
||
// 2. ❗如果存在,直接无条件更新第一条,并删掉其余同名数据
|
||
const keepId = existed.data[0]._id;
|
||
await db.collection('user_info').doc(keepId).update({ ... });
|
||
|
||
// ... 删除多余记录 ...
|
||
|
||
// 3. 直接返回成功,action 标记为 'updated'
|
||
return { success: true, action: 'updated', username };
|
||
}
|
||
|
||
运行
|
||
因为云函数一旦匹配到 existed.data.length > 0,就直接调用了 update 和 remove 写入数据库,所以不管你的 Python 客户端怎么发请求,云数据库都已经瞬间被覆盖了。
|
||
|
||
2. Python 脚本里设计的“两阶段确认”失效了
|
||
你在 Python 脚本中其实写了非常完美的防呆机制:
|
||
|
||
第一次请求不带 force_update。
|
||
|
||
如果云端返回 result.get("conflict"),就会在本地终端弹出 是否覆盖更新?[y/N]: 的二次确认。
|
||
|
||
用户输入 y 后,才会带上 force_update: true 重新发起请求。
|
||
|
||
但是,你的云函数完全没有配合这个机制。 云函数既没有检查 event.force_update 这个参数,也没有在发生冲突时返回 { conflict: true, existingData: ... }。
|
||
|
||
🛠️ 如何修改云函数以支持二次确认?
|
||
为了让 Python 脚本的二次确认功能生效,你需要让云函数在发现重复时“先不更新,而是举手报警”。
|
||
|
||
请将云函数中的 if (existed.data.length > 0) 部分修改为如下逻辑:
|
||
|
||
JavaScript
|
||
// 查询是否已存在该 username
|
||
const existed = await db.collection('user_info')
|
||
.where({ username: username })
|
||
.get();
|
||
|
||
if (existed.data.length > 0) {
|
||
// 💡 核心修改:检查客户端是否传了 force_update 标记
|
||
// 如果没有传,说明是第一次尝试,直接拦截并返回冲突信息给 Python 客户端
|
||
if (!event.force_update) {
|
||
return {
|
||
success: false,
|
||
conflict: true,
|
||
existingData: {
|
||
issued: existed.data[0].issued,
|
||
expiry: existed.data[0].expiry
|
||
}
|
||
};
|
||
}
|
||
|
||
// 如果传了 force_update: true,说明用户已经在客户端敲了 "y" 确认覆盖,此时再执行更新
|
||
const keepId = existed.data[0]._id;
|
||
await db.collection('user_info').doc(keepId).update({
|
||
data:{
|
||
issued: issued,
|
||
expiry: expiry,
|
||
license: license,
|
||
updateTime: db.serverDate()
|
||
}
|
||
});
|
||
|
||
// 删除多余的重复记录(如果有)
|
||
for (let i = 1; i < existed.data.length; i++) {
|
||
await db.collection('user_info').doc(existed.data[i]._id).remove();
|
||
}
|
||
return { success: true, action: 'updated', username };
|
||
}
|
||
|
||
运行
|
||
💡 修改后的效果
|
||
这样改完后,当你再次上传同名数据时:
|
||
|
||
云函数查到重复 → 发现没有 force_update → 返回 conflict: true 终止。
|
||
|
||
Python 脚本收到响应,触发 input() 并在终端拦截,打印出旧证书的签发和到期时间,询问你是否覆盖。
|
||
|
||
你输入 y 后,Python 携带 force_update: true 二次请求,云函数才会真正修改数据库。
|
||
|
||
https://github.com/Leonxlnx/taste-skill
|
||
|
||
|
||
|
||
查看项目,解释这个项目有什么功能,如何使用
|
||
|
||
Leonxlnx/taste-skill(也称为 Taste Skill)是一个专门为 AI 编程智能体(AI Coding Agents) 打造的开源前端设计规则与指令集框架。
|
||
Taste Skill
|
||
|
||
它的核心口号是:“Give your AI good taste, stop generating boring, generic slop”(让你的 AI 具备良好的审美,停止生成无聊、千篇一律的工业垃圾/AI水货代码)。
|
||
GitHub
|
||
|
||
以下是该项目的具体功能介绍以及使用方法:
|
||
|
||
一、 核心功能与痛点解决
|
||
虽然像 Cursor, Claude Code, Devin, Lovable 等 AI 编程工具能以极快的速度写出代码,但默认生成的 UI/UX 界面往往极具“AI味”——布局单调、字体缩放死板、间距不讲究、动画生硬。
|
||
Taste Skill
|
||
|
||
taste-skill 通过提供高度优化的指令文件(包含一套严格的视觉心理学、排版、间距和动效规范),让 AI 像资深设计师一样去思考。其主要功能特点包括:
|
||
GitHub
|
||
|
||
摆脱模版化设计:包含独特的反重复规则和严格的预检(Pre-flight check)机制,禁止 AI 生成“大厂样板房”式的 UI。
|
||
GitHub
|
||
|
||
多款参数微调(Dials):每个技能文件顶部有 1-10 的数字“旋钮”,可以通过直接改数字控制 AI 生成的代码:
|
||
GitHub
|
||
|
||
DESIGN_VARIANCE:布局实验度(低:传统整洁;高:现代不对称)。
|
||
GitHub
|
||
|
||
MOTION_INTENSITY:动效深度(低:简单悬停;高:复杂的滚动和磁性动画)。
|
||
GitHub
|
||
|
||
VISUAL_DENSITY:视觉密度(低:宽敞留白;高:紧凑的数据看板)。
|
||
GitHub
|
||
|
||
丰富的技能变体(Variants):
|
||
|
||
design-taste-frontend (v2):默认的主力技能。它能自动推断你项目的行业背景、受众和情绪,匹配最适合的暗黑模式、设计系统或动效框架(如 GSAP)。
|
||
GitHub
|
||
|
||
redesign-skill:用于已有项目的重构和视觉审计。
|
||
Taste Skill
|
||
|
||
image-to-code-skill:配合图片生成能力,实现“生成参考图 → 分析风格 → 编写高还原度前端代码”的组合工作流。
|
||
GitHub
|
||
|
||
minimalist-skill / brutalist-skill:直接锁定极简风或新野兽主义视觉风格。
|
||
Taste Skill
|
||
|
||
output-skill:强迫 AI 必须完整输出代码,拒绝生成中断或使用 // 你的其余代码... 等敷衍占位符。
|
||
Taste Skill
|
||
|
||
二、 如何使用
|
||
该项目不挑前端框架(无论你用 React, Vue, Svelte 还是原生 HTML+Tailwind 均可),它作为一种“外挂技能指令(SKILL.md)”插入到你现有的 AI 编程流中。
|
||
GitHub
|
||
|
||
使用方法通常有以下几种:
|
||
|
||
方法 1:通过 CLI 命令行工具自动安装(推荐)
|
||
如果你使用的是支持特定技能扩展的现代 AI 客户端(如 Claude Code 或其他原生集成环境),可以在项目根目录下直接运行 npx 脚本:
|
||
|
||
安装默认的前端审美技能:
|
||
|
||
Bash
|
||
npx skills add https://github.com/Leonxlnx/taste-skill --skill "design-taste-frontend"
|
||
* **如果你需要指定其他子技能(例如极简风):**
|
||
```bash
|
||
npx skills add https://github.com/Leonxlnx/taste-skill --skill "minimalist-skill"
|
||
方法 2:手动复制到项目中(适用于 Cursor / VS Code / Claude Code)
|
||
打开该 GitHub 仓库,进入 skills/ 文件夹。
|
||
|
||
找到你需要赋予 AI 的能力文件(如 skills/taste-skill/SKILL.md)。
|
||
|
||
将该 SKILL.md 文件(或者里面的文本内容)直接下载或复制到你本地的项目根目录下。
|
||
|
||
触发 AI:现在的编程 Agent(如 Cursor 或 Claude)在感知到目录下的新指令文件后,在后续的代码生成中就会自动加载、遵循其中的“高级审美规范”。
|
||
|
||
方法 3:直接粘贴进提示词对话(适用于 Web 版 ChatGPT / Claude / Gemini)
|
||
如果你在网页端使用大模型写前端,直接把对应的 SKILL.md 里的文本全选复制,然后作为前置条件发送给 AI:
|
||
|
||
“以下是我的项目的审美和代码规范,请严格遵循。在这个规范下,帮我用 React+Tailwind 写一个……”
|
||
|
||
三、 总结建议
|
||
如果你想通过 AI 开发一个看起来高级、有大牌质感、拥有丝滑动画的产品界面(而非粗制滥造的 demo),建议首先通过 CLI 安装它的主技能:
|
||
|
||
Bash
|
||
npx skills add https://github.com/Leonxlnx/taste-skill
|
||
装好后,你甚至可以通过修改文件顶部的 MOTION_INTENSITY: 8 这一行字,来直接指挥 AI 给你的页面加上炫酷的滚动动效。
|
||
|
||
redesign-skill:用于已有项目的重构和视觉审计。
|
||
|
||
image-to-code-skill:配合图片生成能力,实现“生成参考图 $\rightarrow$ 分析风格 $\rightarrow$ 编写高还原度前端代码”的组合工作流。
|
||
|
||
|
||
|
||
详细解释这两个用法
|
||
|
||
在 Leonxlnx/taste-skill 框架中,redesign-skill 和 image-to-code-skill 是两个极具实战价值的高级变体(Variants)。它们将普通的 AI 重构和生图转换,升级为了具备设计师视角和严苛审美标准的半自动化工作流。
|
||
|
||
以下是这两个用法的详细运作机制、深度解析以及具体的使用步骤:
|
||
|
||
1. redesign-skill(老旧项目的重构与视觉审计)
|
||
普通的 AI 在面对“重构”请求时,通常只是帮你重写一下过时的组件语法(例如把 Class 组件换成 Hooks),或者单纯做代码瘦身。而 redesign-skill 的核心在于“视觉审计(Visual Audit)”——它像一个挑剔的前端设计总监,先给你的既有页面“挑刺”,再动手重构。
|
||
|
||
⚙️ 运作机制与功能特点
|
||
深度视觉破产审查(Visual Bankruptcy Review):AI 会扫描你原有的前端 HTML/CSS/Tailwind 代码,无情地指出其中不合理的设计。例如:间距不一致(一会儿用 p-4 一会儿用 p-5)、缺乏排版层级、颜色搭配死板(如直接使用纯黑 #000000 或高饱和度纯蓝)、缺乏微动效导致页面死气沉沉。
|
||
|
||
无破坏性重构:它在重构时,会严格保证你原有的业务逻辑、API 数据绑定、状态管理(如 useState/Redux)不被破坏,仅对 DOM 结构、CSS 类名、布局容器和交互动画进行局部手術式改造。
|
||
|
||
注入高级感(The "Taste" Injection):根据你设置的旋钮参数(如布局自由度、动效强度),它会自动引入诸如“毛玻璃效果(Backdrop-blur)”、“非对称优雅布局”、“微调排版比例”等现代前端设计语言。
|
||
|
||
🛠️ 怎么使用?
|
||
启用该技能:
|
||
在你的项目根目录下使用 CLI 加载,或者把对应的 SKILL.md 指令喂给你的 AI Agent(如 Cursor / Claude Code)。
|
||
|
||
Bash
|
||
npx skills add https://github.com/Leonxlnx/taste-skill --skill "redesign-skill"
|
||
设定旋钮参数(在技能文件顶部微调,或直接在 Prompt 中指定):
|
||
|
||
“保持 VISUAL_DENSITY: 5(保持舒适留白),将 MOTION_INTENSITY 设为 6(加入优雅的过渡动效)。”
|
||
|
||
发送重构指令:
|
||
将你需要重构的老旧组件代码选中,发送给 AI:
|
||
|
||
Prompt 示例: > “这是我们现有的用户看板组件代码。它目前看起来非常具有‘老旧管理后台’的呆板感。请启动 redesign-skill 对其进行视觉审计,找出至少 3 个视觉痛点,然后在不破坏现有数据绑定(userData)和点击事件的前提下,将其重构成符合 Vercel/Linear 风格的现代暗黑模式界面。”
|
||
|
||
2. image-to-code-skill(图片驱动的高还原度开发流)
|
||
在多模态时代,我们经常把一张好看的网页截图发给 AI 说:“帮我实现这个界面”。但普通 AI 往往只能模仿一个大概的轮廓,细节(如边框渐变、阴影深度、字体粗细、响应式间距)通常是一塌糊涂。
|
||
|
||
image-to-code-skill 解决的就是“如何让 AI 像素级还原一张设计图/参考图”。
|
||
|
||
⚙️ 运作机制(三阶段组合工作流)
|
||
该技能强制 AI 遵循一个三步走的思考链(Chain of Thought),严禁直接盲目写代码:
|
||
|
||
第一阶段:分析风格(Deconstruction & Moodboarding) AI 接收到图片后,首先将其转化为一套具体的“前端设计资产清单”。它会输出一段设计分析,明确指出:图片的色彩系统(主色、辅助色、渐变色)、排版规则(字体权重、行高)、图形特征(如 border-radius: 24px 的大圆角、多层柔和阴影)、以及潜在的交互预期。
|
||
|
||
第二阶段:对齐规范(Framework Alignment) AI 会将分析出的设计风格,完美映射到你指定的前端技术栈(例如 Tailwind CSS、Framer Motion、GSAP)。它会规划好哪些地方该用 Flex 布局,哪些地方该用 Grid,如何用 Tailwind 组合出图片里的高质感滤镜。
|
||
|
||
第三阶段:编写高还原度代码(High-Fidelity Code Gen) 最后一步,AI 才会开始编写代码。此时它会启动 output-skill 的防敷衍机制,为你输出结构清晰、带有详尽注释(说明如何用 CSS 还原了图片某处细节)的完整代码。
|
||
|
||
🛠️ 怎么使用?
|
||
准备参考图:可以是你在 Dribbble、Linear 官网看到的优秀设计截图,甚至是利用 Midjourney/DALL-E 3 生成的前端 UI 概念图。
|
||
|
||
加载技能并上传图片:
|
||
在支持多模态的 AI 聊天界面(如 Cursor Chat、Claude 3.5 Sonnet 或网页端)中,上传你的截图,并附带以下 Prompt:
|
||
|
||
Prompt 示例: > “我想用 React + Tailwind CSS 实现截图中的这个卡片悬停效果。请启动 image-to-code-skill:
|
||
|
||
先帮我深度拆解这张图的视觉密码(阴影层级、渐变色值、字体排版)。
|
||
|
||
将 DESIGN_VARIANCE 设为 8,允许你在实现时加入一些符合整体高档风格的现代交互细节。
|
||
|
||
请完整输出代码,不要省略任何 Tailwind 类名。”
|
||
|
||
💡 总结
|
||
如果你手里有一个已经能跑、但长得很丑的系统,用 redesign-skill 可以帮你一键洗稿,让它焕然一新且不破坏功能。
|
||
|
||
如果你手里有好看的视觉稿或竞品截图,用 image-to-code-skill 能逼迫 AI 像一个像素眼(Pixel-perfect)前端工程师一样,把图片高还原度地变成能用的代码。
|
||
|
||
针对你的需求——“拿着现有的 Figma 设计图,让 AI 按图优化已写好的界面代码”——这个 image-to-code 技能可能无法直接满足你的期望。它的工作方式和你的需求之间存在一些关键的错位。
|
||
|
||
🎯 image-to-code 技能的实际工作方式
|
||
|
||
这个技能的核心是 “AI 自生成,自消化” 的闭环流程。它的标准步骤是:
|
||
|
||
AI 自己生成设计图:它会先根据你的文字描述,利用图像生成能力“凭空”画出几张它认为符合“好品味”的网页设计参考图。
|
||
|
||
AI 分析自己的图:然后,它会深入分析自己生成的这些图片,提取其中的布局、配色、排版等信息。
|
||
|
||
AI 编写代码:最后,基于它对自产图片的分析,来编写最终的前端代码。
|
||
|
||
这个流程和你“拿着现成的 Figma 设计图去优化代码”的场景,完全是两个方向
|
||
|
||
|
||
|
||
为什么她说的和你说的不一样
|
||
|
||
你提到的这位朋友对项目的理解非常深入且敏锐,她指出了一个很多人在使用 AI 编程时最容易踩到的“预期差”坑。
|
||
|
||
其实我们两个人说的都没错,只是观察这个技能的维度和所处的“工程流阶段”不同。之所以会产生这种“不一样”的感觉,原因在于 taste-skill 框架本身是面向“全自动 AI Agent(如 Claude Code/Devin)”设计的,它的理想闭学流确实如她所说;但在日常开发中,我们可以把它降维当成“普通的提示词外挂”来用。
|
||
|
||
为了让你彻底搞懂,我们来把这两种视角的本质拆开来看:
|
||
|
||
1. 她说的“AI自生成、自消化”:是针对“全自动 Agent 工作流”的本质视角
|
||
她所描述的,是 taste-skill 仓库里 image-to-code-skill 在完全自动化运行(Autonomous Mode)下的标准闭环。
|
||
|
||
在这种模式下,你给 AI 一个需求(比如:“帮我做一个极简风的音乐播放器”),AI 并不是直接去写代码,而是:
|
||
|
||
AI 自己生图:调用多模态模型(或生图插件)先画一张 UI 概念图。
|
||
|
||
AI 视觉审计:AI 自己用“像素眼”去审查这张图好不好看、间距对不对、有没有工业垃圾的感觉。
|
||
|
||
AI 把图变代码:审查通过后,AI 再把这张图据为己有,翻译成高质量的前端代码。
|
||
|
||
在这个全自动闭环里,确实没有给“人类提供 Figma 设计图”留下现成的输入接口。所以她非常精准地提醒你:如果你想直接把 Figma 链接丢给它,让它完美重构你写了一半的烂代码,这个技能的原生设计跟你想的完全是两个方向。
|
||
|
||
2. 我说的“像素级还原截图”:是我们在“日常对话/Cursor 开发”中的实用主义视角
|
||
而我前面提到的用法,是在我们把这个技能“降维”应用在日常聊天(如 Cursor Chat、Claude 网页端)中的实战变通。
|
||
|
||
当我们不再让 AI “全自动瞎跑”,而是由人类来主导控制流时,这个技能的思考框架依然可以被我们“白嫖”:
|
||
|
||
人类提供图和代码:你把你现有的代码,以及 Figma 的高保真设计稿截图一起喂给 AI。
|
||
|
||
借用它的思考链(Mindset):虽然这个技能原本是让 AI 审计自己的图,但当它加载了 image-to-code-skill 后,它会被强行注入一套“高级设计师的像素眼规范”。此时,它会用这套规范去对比你的 Figma 截图和你现有的烂代码。
|
||
|
||
高还原度优化:它会指出:“你的 Figma 图里阴影是柔和的 shadow-xl 感觉,但你的代码里写死了一个很丑的粗边框,我帮你优化成……”
|
||
|
||
所以,我说的用法是把这个技能当成了 AI 的“审美补丁”。
|
||
|
||
📋 总结:你该听谁的?
|
||
你们两个人的观点结合起来,才是这个技能最完整、最避坑的真相:
|
||
|
||
大方向上,听她的:千万不要以为装了这个技能,AI 就能像个真正的 UI 实习生一样,完全读懂你们复杂的 Figma 组件树和既有业务逻辑,然后天衣无缝地帮你改好代码。直接丢复杂项目进去,一定会因为两者的错位而翻车。
|
||
|
||
具体操作上,用我说的:如果你真的想让它帮你按 Figma 优化,你需要人工把任务切碎。不要给它一整个大项目,而是把“某一个卡片”或“某一个导航栏”的代码和 Figma 局部截图发给它,利用该技能的审美规范,让它帮你做局部洗稿。
|
||
|
||
她帮你踩了“高估 AI Agent 自动化流程”的坑,而我给了你一个“把大炮拆成手枪用”的压榨 AI 偏方。
|
||
|
||
https://github.com/addyosmani/agent-skills
|
||
|
||
|
||
|
||
解释这个项目有什么功能
|
||
|
||
addyosmani/agent-skills 是由 Google Chrome 团队工程主管 Addy Osmani 主导并开源的一个专门为 AI 编程智能体(AI Coding Agents)打造的高标准工程化技能/工作流框架。
|
||
knightli.com
|
||
|
||
它不是一个新的编程框架,也不是简单的提示词(Prompt)片段,而是一套用纯 Markdown 编写的、将资深软件工程师的“工作纪律和最佳实践”代码化的指令集。它的核心功能是给容易“偷懒”的 AI 盖上质量闸门,逼迫 AI 像大型科技公司的资深开发者一样去规范地写代码、测试和交付。
|
||
knightli.com
|
||
+ 1
|
||
|
||
该项目的核心功能和机制可以归纳为以下几点:
|
||
|
||
一、 核心功能与技术亮点
|
||
1. 覆盖全生命周期的 7 个斜杠命令(Slash Commands)
|
||
该项目将整个软件开发生命周期(SDLC)拆解为 7 个明确的阶段命令,让 AI 在每个阶段只专心做一件事,避免因上下文过长而逻辑混乱:
|
||
knightli.com
|
||
|
||
/spec(定义):在写任何代码前,必须先理清需求,输出涵盖目标、范围边界和技术栈的 PRD/设计文档。
|
||
|
||
/plan(规划):严禁横向大面积铺开写代码,强迫 AI 将任务拆解为极小的、垂直切片的原子任务。
|
||
GitHub
|
||
|
||
/build(构建):增量式开发,一次只解决一个垂直切片,写完即提交。
|
||
GitHub
|
||
|
||
/test(测试):推行测试驱动开发(TDD),测试通过才是功能完成的唯一铁证。
|
||
GitHub
|
||
|
||
/review(评审):从代码健康度、可读性等维度进行类似 Google 内部标准的严格审计(如控制单次 PR 在 100 行左右)。
|
||
GitHub
|
||
|
||
/code-simplify(代码简化):在不改变核心业务行为的前提下,精简和重构代码(遵循切斯特顿栅栏原则)。
|
||
knightli.com
|
||
|
||
/ship(交付):完成最后的发布前检查(包括功能标帜开关、CI/CD 管道验证等)。
|
||
knightli.com
|
||
|
||
2. 独特的“反借口/反狡辩表”(Anti-Rationalization Tables)
|
||
AI 编程智能体(如 Claude 或 Cursor)有一个通病:极度注重效率,因此经常走捷径(例如偷懒不写测试,或者用 // 你的其余代码... 来敷衍)。
|
||
该项目的每个技能文件(SKILL.md)都内置了“反向防呆表”。如果 AI 试图找借口说 “这只是个小改动,不需要写单元测试”,系统会有一套严厉的对立论据直接把它“拍回去”,强制它遵循测试金字塔。
|
||
Agensi
|
||
+ 2
|
||
|
||
3. 24 个工业级核心技能(Skills)
|
||
除了上述命令,该仓库还包含 24 个细分工程技能,把大厂沉淀的软件工程经验固化成了 AI 的工作步骤,例如:
|
||
knightli.com
|
||
|
||
Doubt-driven Development(怀疑驱动开发):逼迫 AI 对自己的假设保持警惕。
|
||
|
||
Source-driven Development(源码/官方文档驱动):强制 AI 每一段技术决策都必须引用官方最新文档,拒绝幻觉。
|
||
GitHub
|
||
|
||
Security & Hardening(安全加固):对 OWASP Top 10 漏洞、鉴权隔离和敏感密钥管理进行强制审查。
|
||
GitHub
|
||
|
||
Web Performance & Browser Debugging:利用 Chrome DevTools 协议(MCP)深入 runtime 进行 DOM、内存和网络加载(LCP、INP、CLS)的真实审计。
|
||
GitHub
|
||
|
||
4. 渐进式呈现(Progressive Disclosure)
|
||
为了防止一次性喂给 AI 太多规则导致其“脑容量”(上下文窗口)爆炸、Token 消耗过大,该项目采用路由机制。AI 默认只加载总入口技能(using-agent-skills),只有在被触发特定指令或特定场景时,才会动态按需加载对应的子技能文档。
|
||
GitHub
|
||
+ 1
|
||
|
||
二、 它适合怎么使用?
|
||
|
||
knightli.com
|
||
+ 2
|
||
这个项目是语言无关、框架无关的(不管是写 Python, Go 还是 React 都能用),它可以无缝外挂到当前主流的 AI 编程工具中:
|
||
GitHub
|
||
|
||
搭配全自动 Agent(如 Claude Code、Gemini CLI):
|
||
这类工具原生支持 MCP 或外部技能包加载,可以直接通过项目提供的脚本一键安装到智能体的技能树中。
|
||
knightli.com
|
||
|
||
搭配 IDE 插件(如 Cursor、Windsurf、VS Code Copilot):
|
||
你可以把该项目中的 SKILL.md 文件或核心工作流,直接放入项目根目录的 .cursor/rules/ 文件夹下,作为 AI 的全局行为准则(System Prompts)。
|
||
AI Native Landscape - Jimmy Song
|
||
|
||
人类开发团队的 Wiki 规范:
|
||
由于其将大厂(如 Google 内部)的工程规范提炼得非常精简骨感,很多团队甚至直接把它打印出来,作为人类工程师与 AI 协同开发时的项目军规。
|
||
AI Native Landscape - Jimmy Song
|
||
|
||
一句话总结:addyosmani/agent-skills 就像是给你的 AI 程序员外接了一个严厉、刻板、极具强迫症的“高级技术总监监工”,把 AI 辅助编程从“靠运气写玩具 Demo”提升到了“工业级生产线”的标准。
|
||
AI Native Landscape - Jimmy Song
|
||
|
||
对于taste- skill,我把设计图给ai,让它改界面代码,该怎么用skill,告诉我prompt
|
||
|
||
要让 AI 拿着你的 Figma 设计图(截图) 去优化已有的界面代码,你不能直接把整个大项目丢给它。正如前面所说,我们需要“把大炮拆成手枪用”——采用局部喂食、强制审计的策略。
|
||
|
||
这里为你准备了一套完整的操作步骤和开箱即用的 Prompt(提示词)模板。
|
||
|
||
🛠️ 第一步:准备工作(切碎任务)
|
||
截取局部图:不要截整个大网页,只截你想修改的那一个组件(例如:一个登录卡片、一个导航栏、或一个数据图表网格)。
|
||
|
||
复制局部代码:把这个组件对应的现有前端代码(HTML/Tailwind/React/Vue 等)复制下来。
|
||
|
||
📝 第二步:调教 AI 的 Prompt 模板
|
||
在 Cursor Chat、Claude 3.5 Sonnet 或 ChatGPT 中,先上传你的设计图截图,然后复制并修改以下 Prompt 发送给 AI:
|
||
|
||
Markdown
|
||
# 角色与技能启动
|
||
你现在是一位拥有像素级强迫症(Pixel-perfect)的高级前端专家。请启动 `taste-skill` 中的 `image-to-code-skill` 和 `redesign-skill` 核心思维。
|
||
|
||
# 任务目标
|
||
我上传了一张 Figma 的设计稿截图,并在下方提供了我目前已经写好的组件代码。由于“AI味”太浓、细节不到位,目前的代码和设计稿有明显的视觉差距。请帮我“洗稿”重构这段代码,使其高还原度地匹配设计图。
|
||
|
||
# 旋钮参数设定(Taste Dials)
|
||
- DESIGN_VARIANCE: 2 (严格对齐设计图的排版,不要自己瞎编乱造布局)
|
||
- MOTION_INTENSITY: 5 (如果设计图有明显的交互暗示,加入优雅微弱的 Tailwind/CSS 过渡动效)
|
||
- VISUAL_DENSITY: 5 (严格保持设计图中的微观留白与间距)
|
||
|
||
# 你的工作流(请分步执行)
|
||
1. 视觉审计(Visual Audit):对比设计图和我的代码,指出至少 3 个目前代码里最丑、最不还原的细节(如阴影、边框、颜色饱和度、字体粗细、内边距等)。
|
||
2. 代码重构(Refactoring):在【绝对不破坏】现有业务逻辑、数据绑定和点击事件的前提下,对界面代码进行视觉升级。
|
||
3. 拒绝敷衍:必须输出【完整】的代码,严禁使用 `// 你的其余代码...` 等占位符。
|
||
|
||
---
|
||
|
||
## 💾 我现有的组件代码如下:
|
||
```json
|
||
// 👈 在这里粘贴你现有的烂代码
|
||
请先输出你的视觉审计分析,然后给出重构后的完整代码。
|
||
|
||
|
||
---
|
||
|
||
### 💡 为什么这个 Prompt 能发挥作用?
|
||
|
||
1. **限定了 `DESIGN_VARIANCE: 2`**:这是关键。默认的 taste-skill 会让 AI 放飞自我去搞“艺术创作”。我们把它调低到 2,就是死命令:**“老老实实照着我给的 Figma 图抄,别搞非对称和艺术抽象”**。
|
||
2. **设定了安全边界**:明确要求“绝对不破坏现有业务逻辑和数据绑定”,防止 AI 把你的按钮点击事件、组件传参(Props)给改丢了。
|
||
3. **给 AI 装上像素眼**:迫使它先做“视觉审计”,AI 会主动去吸取设计图里的阴影深度(如 `shadow-[0_8px_30px_rgb(0,0,0,0.12)]`)、渐变色值、毛玻璃滤镜,吐出来的代码质量会比直接问“帮我改改”高出好几个档次。
|
||
能否给你界面设计图片,你来转化成文字描述形式的文件,我再把文件喂给ai来编辑代码?
|
||
|
||
当然可以,这其实是一个非常聪明的“曲线救国”方案!
|
||
|
||
现在的很多 AI 编程工具(比如部分版本的 Cursor 或低配的开源 Agent)在对话时不能直接接收图片,或者对图片的视觉解析力经常“抓大放小”。如果你让我先把图片翻译成纯文本的高清设计规范(Design Spec),你再把这个文本喂给代码 AI,反而能实现极高的还原度。
|
||
|
||
你可以直接把设计图(截图)发给我,我会为你输出一份“像素级前端转译描述文件”。
|
||
|
||
为了确保吐出来的文本能完美对接 taste-skill 等 AI 框架,我会严格按照以下结构为你描述:
|
||
|
||
📝 我将为你输出的描述文件结构:
|
||
全局调性(Design System Overview)
|
||
|
||
色彩画布:精确到 Hex 色值(如背景色、主色调、次级文本、渐变色、暗黑模式映射)。
|
||
|
||
字体排版:标题、正文的字重(Font Weight)、行高(Line Height)和视觉层级。
|
||
|
||
微观布局与间距(Layout & Spacing)
|
||
|
||
采用的布局模式(Flex 还是 Grid),各元素的对齐方式。
|
||
|
||
外部留白(Margin)与内部填充(Padding)的体感描述(换算成 Tailwind 或标准 CSS 比例)。
|
||
|
||
视觉细节与质感(Visual Fidelity)
|
||
|
||
圆角大小(如大卡片圆角、按钮圆角)。
|
||
|
||
阴影深度(是扁平风、微弱阴影,还是高档的扩散多层软阴影)。
|
||
|
||
滤镜与边框(是否有毛玻璃 backdrop-blur、极细的半透明边框渐变线等)。
|
||
|
||
交互动效暗示(Interactive Clues)
|
||
|
||
悬停(Hover)、点击(Active)时应该发生怎样的平滑过渡。
|
||
|
||
🛠️ 怎么操作?
|
||
发图给我:现在就把你需要还原或参考的 Figma 界面截图 贴到对话框里发给我。
|
||
|
||
补充说明(可选):顺便告诉我你打算让代码 AI 用什么技术栈(比如:React + Tailwind CSS 或者是 Vue + Element Plus),这样我写描述文件时,会直接用该技术栈的术语(如 Tailwind 类名)来描述,让代码 AI 更好消化。
|
||
|
||
现在,请把你的设计图发给我吧!
|
||
|
||
我用的是pyside
|
||
|
||
收到!既然你使用的是 PySide(PySide2 或 PySide6) 来开发这个工业控制系统软件(ReinLoop V1.0),那么我会完全基于 QSS(Qt Style Sheets)、QWidget 布局逻辑以及 Qt 的视觉层级为你转译这张 image_fa96ff.png 的设计规范。
|
||
|
||
这份文本不仅分析了当前界面的“设计质感缺陷”,还将设计参数直接映射成了 PySide 开发者能听懂的代码术语(如 margin, padding, border-radius 以及颜色代码)。你待会把这段文本喂给负责写 PySide 的 AI 代码工具(例如 Cursor)时,它能秒懂并写出像素级还原的高质量 QSS。
|
||
|
||
📋 ReinLoop V1.0 界面像素级视觉审计与重构规范(PySide / QSS)
|
||
1. 全局色彩画布(Color Palette)
|
||
当前界面采用现代工业科技感的高级冷白调,对比度柔和,避免长时间操作视觉疲劳。
|
||
|
||
主背景色(App Background):#F8FAFC(非常柔和的微冷超浅灰蓝,作为整个窗口的基底色)。
|
||
|
||
卡片/区域背景(Card Background):#FFFFFF(纯白,用于 Modbus TCP 和 RTU 的分组容器)。
|
||
|
||
主品牌色/高亮蓝(Brand Accent):#0056C1 或 #0960D1(用于左上角 LOGO、顶部选中 Tab 的下划线、以及分组左侧的小装饰块)。
|
||
|
||
按钮安全绿(Success Green):#0F955D(用于“连接设备”按钮)。
|
||
|
||
状态警示红(Error/Disconnect Red):#FF2424(用于右下角“未连接”状态点)。
|
||
|
||
文本颜色(Typography Colors):
|
||
|
||
主标题/表单标签:#1A1A1A 或 #333333(高清晰度深灰)。
|
||
|
||
次级信息/状态栏/占位符:#757575(中灰)。
|
||
|
||
表单边框色(Border Color):#E2E8F0(极细的浅灰线)。
|
||
|
||
2. 顶部导航栏与标题区(Top Header & Tab Bar)
|
||
整体高度与内边距:Header 整体高度约 70px,QVBoxLayout 或 QHBoxLayout 的 setContentsMargins(24, 12, 24, 12)。
|
||
|
||
左侧 LOGO 与主标题:
|
||
|
||
LOGO 图片与右侧标题中间有约 16px 的垂直分割线(可以使用 QFrame,设置 width: 1px; background-color: #E2E8F0;)。
|
||
|
||
“ReinLoop V1.0” 字体:font-size: 22px; font-weight: bold; color: #1A1A1A;。
|
||
|
||
顶部 Tab 栏(QTabBar / QTabWidget 样式):
|
||
|
||
背景透明,去除自带的边框:QTabBar::tab { background: transparent; padding: 12px 20px; font-size: 14px; color: #64748B; }
|
||
|
||
选中状态(连接设置):字体变蓝 color: #0960D1; font-weight: bold;。下方有粗细约 3px 的蓝色下划线。可以通过 QTabBar::tab:selected 配合下边框实现:border-bottom: 3px solid #0960D1;。
|
||
|
||
3. 分组卡片容器(Group Cards - Modbus TCP & RTU)
|
||
这是目前界面最容易产生“呆板粗糙感”的地方。要消除 AI 味,必须严格控制圆角和极其微弱的阴影。
|
||
|
||
基本样式:
|
||
|
||
CSS
|
||
QFrame#GroupCard {
|
||
background-color: #FFFFFF;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 12px; /* 💡 必须是优雅的大圆角,严禁直角 */
|
||
}
|
||
* **阴影效果(QGraphicsDropShadowEffect)**:
|
||
卡片千万不能用死黑阴影。必须为这两个卡片容器绑定一个非常淡的阴影:
|
||
`color = QColor(0, 0, 0, 15)`(透明度仅 5% 左右),`blurRadius = 16`,`offset = (0, 4)`。
|
||
* **左侧装饰条标题**:
|
||
每个卡片左上角的蓝色竖线,可以用一个极窄的 `QWidget`(宽 `4px`,高 `16px`,`background-color: #0960D1; border-radius: 2px;`)和标签文本放入一个 `QHBoxLayout`。
|
||
|
||
---
|
||
|
||
## 4. 表单与输入框组件(Form Layout & QLineEdit)
|
||
当前界面的输入框显得过长、过空,重构时必须优化其尺寸与间距。
|
||
* **标签(QLabel)**:字体大小 `14px`,颜色 `#333333`,加粗。
|
||
* **输入框(QLineEdit)**:
|
||
* 高度固定:`min-height: 36px; max-height: 36px;`。
|
||
* QSS 样式:
|
||
```css
|
||
QLineEdit {
|
||
background-color: #FFFFFF;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 6px; /* 柔和的小圆角 */
|
||
padding-left: 12px;
|
||
color: #333333;
|
||
font-size: 14px;
|
||
}
|
||
QLineEdit:focus {
|
||
border: 1px solid #0960D1; /* 聚焦时变蓝 */
|
||
}
|
||
|
||
运行
|
||
多列并排布局(站号/数据位/停止位):
|
||
这一行应该使用嵌套的 QHBoxLayout。每个标签和输入框组合后,设置合理的伸缩因子(Stretch Factor),防止输入框在窗口放大时被拉得过宽变形。
|
||
|
||
5. 底部控制与状态栏(Bottom Action & Status Bar)
|
||
按钮区布局:左下角的“刷新”和“连接设备”按钮需要靠左对齐,放入 QHBoxLayout 并在最右侧加一个 addStretch()。
|
||
|
||
“刷新”按钮(QPushButton):
|
||
|
||
样式为浅色白底蓝字。background-color: #FFFFFF; border: 1px solid #CCDBF0; color: #0960D1; border-radius: 6px; padding: 8px 20px; font-weight: bold;
|
||
|
||
“连接设备”按钮(QPushButton):
|
||
|
||
样式为高亮绿底白字。background-color: #0F955D; border: none; color: #FFFFFF; border-radius: 6px; padding: 8px 24px; font-weight: bold; font-size: 14px;
|
||
|
||
QPushButton:hover { background-color: #0D8250; }(添加平滑的悬停深色反馈)。
|
||
|
||
底层状态栏(Status Bar):
|
||
|
||
左侧日志文本:“14:27:09-模型列表刷新成功”,字体 12px,颜色 #757575。
|
||
|
||
右侧状态:“● 未连接”,其中圆点和文字为红色(颜色 #FF2424),靠最右侧对齐。
|
||
|
||
💡 你接下来的操作:
|
||
你可以把上面这段长文本直接作为需求,连同你已有的 PySide 代码一起喂给你的 AI 工具(例如 Cursor),并附带这句话:
|
||
|
||
给 AI 的指令:
|
||
“请阅读上面这份针对 image_fa96ff.png 设计图转译的 PySide 视觉规范。请在不修改我原有业务逻辑、信号与槽(Signal & Slot)连接、变量命名的情况下,重构我的 QWidget 布局布局参数(margins/spacing),并编写一套高质感、无‘AI水货味’的全局 QSS 样式表来洗稿我的界面。”
|
||
|
||
补充tab栏 icon的位置
|
||
|
||
在 PySide 中,为顶部 Tab 栏(QTabBar / QTabWidget)添加图标并精细控制其样式和位置,主要通过 QIcon 传参和 QSS(Qt 样式表)的伪状态与子控件选择器来实现。
|
||
|
||
根据 image_fa96ff.png 的视觉调性,图标应该优雅地居中放置在文字的左侧,并保持适当的间距。以下是为转译文件补充的 Tab 栏 Icon 像素级技术规范:
|
||
|
||
5. Tab 栏图标视觉与位置规范(Icon Placement)
|
||
5.1 代码级图标注入(PySide 代码)
|
||
在构建 QTabWidget 时,图标需要显式传入,并建议通过 QSize 强制限制其图标分辨率,避免大图导致的模糊或布局撑开:
|
||
|
||
Python
|
||
from PySide6.QtGui import QIcon
|
||
from PySide6.QtCore import QSize
|
||
|
||
# 设置图标尺寸(建议 16x16px 或 18x18px,配合 14px 字体最优雅)
|
||
tab_widget.setIconSize(QSize(18, 18))
|
||
|
||
# 注入对应的矢量或免版权高质感彩色/单色图标
|
||
tab_widget.addTab(tab_connect, QIcon(":/icons/link.png"), "连接设置")
|
||
tab_widget.addTab(tab_control, QIcon(":/icons/settings.png"), "控制设置")
|
||
tab_widget.addTab(tab_debug, QIcon(":/icons/chart.png"), "模型调试")
|
||
5.2 QSS 像素级位置与间距控制
|
||
为了完美契合 image_fa96ff.png 中“连接设置”等标签的精致感,必须消除 Qt 默认图标和文字粘连的问题,调整高亮状态:
|
||
|
||
CSS
|
||
/* 1. 全局定义 Tab 标签的内边距,为左侧图标和文字留出呼吸感 */
|
||
QTabBar::tab {
|
||
background: transparent;
|
||
padding: 12px 24px 12px 20px; /* 上、右、下、左 */
|
||
font-size: 14px;
|
||
color: #64748B;
|
||
font-family: "Segoe UI", "Microsoft YaHei";
|
||
}
|
||
|
||
/* 2. 控制 Tab 内部 Icon 的位置与右侧文字的间距 */
|
||
QTabBar::tab i {
|
||
/* 注:在标准 QSS 中,QTabBar 内部子控件选择器对 icon 的微调较为有限,
|
||
标准做法是通过 padding-left 间接控制,或直接依赖 Qt 默认的左侧居中排列。
|
||
若需强制拉开图标与文字的距离,可以通过控制 subcontrol-position(如果适用)
|
||
或者在 PySide 实例化时在文本前手动加一个半角空格(如 " 连接设置")作为最稳定的 Native 变通方案。*/
|
||
}
|
||
|
||
/* 3. 未选中状态下的图标样式(可选用半透明度让界面更有层次) */
|
||
QTabBar::tab:!selected {
|
||
opacity: 0.7; /* 让未选中的图标和文字一起变淡 */
|
||
}
|
||
|
||
/* 4. 选中状态下的图标与整体样式 */
|
||
QTabBar::tab:selected {
|
||
color: #0960D1;
|
||
font-weight: bold;
|
||
opacity: 1.0; /* 恢复完全不透明 */
|
||
border-bottom: 3px solid #0960D1; /* 底部高亮蓝下划线 */
|
||
}
|
||
💡 补充给 AI 的微调补丁:
|
||
当你把这个补充喂给 AI 时,可以加上这句大白话:
|
||
|
||
“请确保在 PySide 代码中,QTabWidget 调用的 setIconSize 严格限制在 18x18 像素。如果发现图标和文字贴得太近,请直接在 addTab 传递字符串参数时,在文字前面手动补一个空格(例如:" 连接设置"),这是规避 QSS 无法完美控制 Qt 标签内 icon margin 缺陷的最优雅、最轻量化的工业级偏方。”
|
||
|
||
重新回答上面的问题,补充tab栏 icon的位置
|
||
|
||
太棒了,你提供了具体的 SVG 图标资产文件名(image_049067.png),这让代码的衔接度可以直接拉满!
|
||
|
||
现在我将重新整合并输出一份最完整的 PySide / QSS 转译描述文件。这次会把 image_049067.png 中具体的 link_icon.svg、control_icon.svg 和 debug_icon.svg 完美融合进顶部 Tab 栏的设计规范中,并精细定义其位置。
|
||
|
||
📋 ReinLoop V1.0 界面像素级视觉审计与重构规范(PySide / QSS 全套)
|
||
1. 全局色彩画布(Color Palette)
|
||
当前界面采用现代工业科技感的高级冷白调,对比度柔和,避免长时间操作视觉疲劳。
|
||
|
||
主背景色(App Background):#F8FAFC(柔和的微冷超浅灰蓝,作为整个窗口的基底色)。
|
||
|
||
卡片/区域背景(Card Background):#FFFFFF(纯白,用于 Modbus TCP 和 RTU 的分组容器)。
|
||
|
||
主品牌色/高亮蓝(Brand Accent):#0960D1(用于选中 Tab 的下划线、高亮图标以及分组左侧的小装饰块)。
|
||
|
||
按钮安全绿(Success Green):#0F955D(用于“连接设备”按钮)。
|
||
|
||
状态警示红(Error/Disconnect Red):#FF2424(用于右下角“未连接”状态点)。
|
||
|
||
文本颜色(Typography Colors):
|
||
|
||
主标题/表单标签:#1A1A1A(高清晰度深灰)。
|
||
|
||
次级信息/状态栏/占位符:#757575(中灰)。
|
||
|
||
表单边框色(Border Color):#E2E8F0(极细的浅灰线)。
|
||
|
||
2. 顶部导航栏与带有图标的 Tab 栏(Top Header & Tab Bar)
|
||
整体高度与内边距:Header 整体高度约 70px,布局容器(如 QHBoxLayout)设置 setContentsMargins(24, 12, 24, 12)。
|
||
|
||
左侧 LOGO 与主标题:
|
||
|
||
LOGO 使用 logo.svg,右侧有约 16px 的垂直分割线(可使用 QFrame,设置 width: 1px; background-color: #E2E8F0;)。
|
||
|
||
“ReinLoop V1.0” 字体:font-size: 22px; font-weight: bold; color: #1A1A1A;。
|
||
|
||
Tab 栏图标位置与间距控制(核心补充):
|
||
|
||
图标位置:图标必须优雅地垂直居中,放置在标签文字的正左侧。
|
||
|
||
代码级注入:在 PySide 中限制图标的显示尺寸为 16x16px 或 18x18px,并在添加标签页时绑定 image_049067.png 中的对应文件:
|
||
|
||
Python
|
||
tab_widget.setIconSize(QSize(18, 18))
|
||
tab_widget.addTab(tab_connect, QIcon("link_icon.svg"), "连接设置")
|
||
tab_widget.addTab(tab_control, QIcon("control_icon.svg"), "控制设置")
|
||
tab_widget.addTab(tab_debug, QIcon("debug_icon.svg"), "模型调试")
|
||
* **QSS 样式与防贴边微调**:
|
||
```css
|
||
QTabBar::tab {
|
||
background: transparent;
|
||
padding: 12px 24px 12px 16px; /* 上、右、下、左,为左侧图标留出呼吸感 */
|
||
font-size: 14px;
|
||
color: #64748B;
|
||
}
|
||
QTabBar::tab:!selected {
|
||
opacity: 0.65; /* 未选中时,SVG 图标与文字统一半透明隐去 */
|
||
}
|
||
QTabBar::tab:selected {
|
||
color: #0960D1;
|
||
font-weight: bold;
|
||
opacity: 1.0;
|
||
border-bottom: 3px solid #0960D1; /* 底部高亮蓝下划线 */
|
||
}
|
||
3. 分组卡片容器(Group Cards - Modbus TCP & RTU)
|
||
基本样式:
|
||
|
||
CSS
|
||
QFrame#GroupCard {
|
||
background-color: #FFFFFF;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 12px; /* 优雅的大圆角,严禁直角 */
|
||
}
|
||
* **阴影效果(QGraphicsDropShadowEffect)**:
|
||
为这两个卡片容器绑定一个微弱的模糊阴影:`color = QColor(0, 0, 0, 12)`(透明度约 5%),`blurRadius = 16`,`offset = (0, 4)`。
|
||
* **左侧装饰条标题**:
|
||
每个卡片左上角的蓝色竖线,可以用一个宽 `4px`,高 `16px`,`background-color: #0960D1; border-radius: 2px;` 的小组件来实现。
|
||
|
||
---
|
||
|
||
## 4. 表单与输入框组件(Form Layout & QLineEdit)
|
||
* **标签(QLabel)**:字体大小 `14px`,颜色 `#333333`,加粗。
|
||
* **输入框(QLineEdit)**:
|
||
* 高度固定:`min-height: 36px; max-height: 36px;`。
|
||
* QSS 样式:
|
||
```css
|
||
QLineEdit {
|
||
background-color: #FFFFFF;
|
||
border: 1px solid #E2E8F0;
|
||
border-radius: 6px;
|
||
padding-left: 12px;
|
||
color: #333333;
|
||
font-size: 14px;
|
||
}
|
||
QLineEdit:focus {
|
||
border: 1px solid #0960D1; /* 聚焦时激活品牌蓝 */
|
||
}
|
||
|
||
运行
|
||
5. 底部控制与状态栏(Bottom Action & Status Bar)
|
||
“刷新”按钮(QPushButton):
|
||
background-color: #FFFFFF; border: 1px solid #CCDBF0; color: #0960D1; border-radius: 6px; padding: 8px 20px; font-weight: bold;
|
||
|
||
“连接设备”按钮(QPushButton):
|
||
background-color: #0F955D; border: none; color: #FFFFFF; border-radius: 6px; padding: 8px 24px; font-weight: bold; font-size: 14px;
|
||
QPushButton:hover { background-color: #0D8250; }
|
||
|
||
底层状态栏(Status Bar):
|
||
|
||
左侧日志文本:“14:27:09-模型列表刷新成功”,字体 12px,颜色 #757575。
|
||
|
||
右侧状态:“● 未连接”,其中圆点和文字高亮为红色(#FF2424)。
|
||
|
||
💡 怎么把它喂给代码 AI?
|
||
直接复制上面整篇规范丢给你的 Cursor 或其他编程 AI,并附上这一段提示:
|
||
|
||
“请根据这份整合了 image_fa96ff.png 界面风格与 image_049067.png 图标资产的像素级视觉规范,为我的 PySide 代码重构布局和 QSS 样式表。
|
||
特别注意:
|
||
|
||
Tab 栏的图标文件请严格对应 link_icon.svg、control_icon.svg 和 debug_icon.svg,且图标尺寸限定为 18x18 像素。
|
||
|
||
为了防止 Qt 默认的图标与文字贴得太近,请在 addTab() 传入字符串时,主动在文本前补一个半角空格(如 " 连接设置"),确保图标与文字之间有优雅的间距。
|
||
|
||
不要删除或修改我原本的业务逻辑和信号槽绑定。”
|
||
|
||
这是ai根据规范改的,下一步该如何告诉它继续修改这些不一样的地方
|
||
|
||
从 image_04fcab.png 的截图来看,AI 已经把基本的调性、圆角和背景框架搭起来了,整体质感提升了很多。但它在细节处理上依然犯了 AI 编程的通病——组件拼凑生硬、间距失控、输入框错位。
|
||
|
||
下一步我们应该启动 redesign-skill 的“精准找茬(Visual Defect Review)”机制,把大任务拆解为具体的局部对齐指令。
|
||
|
||
以下是为你整理好的第二轮重构 Prompt,你可以直接复制丢给 AI:
|
||
|
||
📝 第二轮微调优化 Prompt
|
||
Markdown
|
||
# 任务:ReinLoop V1.0 界面细节打磨(第 2 轮)
|
||
你刚才调整的 PySide 基础框架和 QSS 样式已经初具质感,但在小细节上和我们要求的工业级品质还有较大差距(参考当前截图 `image_04fcab.png`)。请针对以下 5 个暴露出来的视觉缺陷,进行专项代码调整和 QSS 打磨。
|
||
|
||
## 🛠️ 当前需要修复的缺陷清单
|
||
|
||
1. 顶部 Tab 栏选中项下划线缺陷
|
||
- 【现状】:选中“连接设置”时,蓝色下划线直接顶满了整个 Tab 标签的底部,显得非常笨重。
|
||
- 【优化】:下划线应该是有内边距的。请通过调整 QSS 中 `QTabBar::tab:selected` 的 `margin-left` 和 `margin-right`(例如各设 `8px`),使蓝色下划线只缩在文字和图标的下方,不要顶满边缘。
|
||
|
||
2. 表单输入框(QLineEdit)宽度失控与错位
|
||
- 【现状】:Modbus TCP 和 RTU 下的大部分输入框被无限制地拉得太长(特别是“波特率”和“校验位”),且右侧莫名其妙多出了空白边缘或嵌套边框。
|
||
- 【优化】:
|
||
* 请为输入框统一设置最大宽度限制 `max-width: 450px;`(或将它们放入一个固定宽度的局部布局中),防止其随窗口放大而无限横向拉伸。
|
||
* 检查布局嵌套,确保输入框右侧没有多余的隐藏空白小控件。
|
||
* 【重要】“站号/数据位/停止位”这一行,输入框和标签贴得太死、大小不一,且没有和上方的输入框左对齐。请重新用 `QHBoxLayout` 梳理,设置合理的 `spacing(10)`,并确保“站号”输入框的起始位置与上方的输入框完全左对齐。
|
||
|
||
3. 底部“连接设备”按钮文字被遮挡
|
||
- 【现状】:“连接设备”按钮由于内边距或高度没有自适应,导致文字上半部分被截断,只露出了下半部分,且“刷新”按钮和它大小不统一。
|
||
- 【优化】:请为底部的两个按钮统一设定固定高度:`min-height: 32px; max-height: 32px;`,并确保 QSS 中没有写死导致文字移位的 `padding-top`。同时把按钮里的文本垂直对齐方式设置为居中。
|
||
|
||
4. 底部状态栏(QStatusBar)元素堆叠错位
|
||
- 【现状】:底部的日志文本、绿色的麦克风图标(或圆形按钮)以及“● 未连接”状态在垂直方向上没有对齐,高矮不一,且绿色圆圈图标压到了最底部的灰色线条。
|
||
- 【优化】:将底部的状态栏改用一个干净的 `QHBoxLayout` 进行托管,设置 `setAlignment(Qt.AlignVCenter)` 确保所有文本和图标在【垂直方向上绝对居中对齐】。
|
||
|
||
5. 消除多余的边框嵌套
|
||
- 【现状】:在“读取压力存储器地址”输入框被选中时,由于你叠加了 focus 样式,导致原本外层包裹的未知容器也露出了隐约的蓝色双重边框。
|
||
- 【优化】:检查底层布局结构,确保 `QLineEdit` 外层没有包裹多余的带边框 `QFrame`。如果包裹了,请设置外层容器 QSS:`border: none; background: transparent;`。
|
||
|
||
---
|
||
|
||
## 💻 请开始重构
|
||
请根据上述 5 点,精准修改相关的 `QWidget` 布局参数和 QSS 样式表。必须保证不修改任何信号与槽、变量名称等业务逻辑,并输出【完整修改后】的代码。
|
||
|
||
运行
|
||
💡 为什么这么提问?
|
||
AI 对于“输入框错位”、“文字截断”这种空间几何问题是非常迟钝的。这个 Prompt 强制它从 “最大宽度(max-width)”、“垂直居中(AlignVCenter)” 以及 “边框重叠” 等具体的 Qt 开发术语去思考,它就能迅速定位到是自己把 Layout 嵌套错了,改出来的效果就会非常精准。
|
||
|
||
前两张图片是ai根据规范改后的界面,第三张图片是设计图,下一步该如何告诉它继续修改和设计图不一样的地方
|
||
|
||
1. 导航栏的logo下面被遮挡了
|
||
|
||
2. tab栏和logo行有灰色分割线,并且tab栏是灰色背景
|
||
|
||
3. tab栏的icon和标签文字应在选中后才变为蓝色
|
||
|
||
4. icon缺失
|
||
|
||
从你提供的新一轮运行截图来看,AI 已经对输入框宽度和按钮截断做出了很好的修正,但顶部的 Header 和 Tab 栏完全变形了。特别是它错误地把整个 Tab 栏上移到了 LOGO 那一行,而且把 QTabBar 的原生样式给彻底搞崩了。
|
||
|
||
为了像素级逼近第三张设计图(image_05629d.png),这次我们需要强制它做“结构性大手术”。以下是为你整理好的第三轮精确调教 Prompt:
|
||
|
||
📝 第三轮结构修正 Prompt
|
||
Markdown
|
||
# 任务:ReinLoop V1.0 顶部导航与 Tab 栏结构性修复(第 3 轮)
|
||
在上一轮修改后,输入框和按钮的尺寸已经好多了。但目前的顶部导航和 Tab 栏结构严重错位(参考 `image_0561c8.png`)。它与我们的设计图(`image_05629d.png`)存在巨大的方向性偏差。
|
||
|
||
请严格按照以下 5 点,对顶部的布局结构和 QSS 进行彻底修复:
|
||
|
||
## 🛠️ 必须修复的视觉缺陷清单
|
||
|
||
1. 顶部布局结构大重构(修复 LOGO 遮挡与 Tab 移位)
|
||
- 【现状】:你错误地把 Tab 栏和 LOGO 塞进了同一行,导致整体高度严重不足,LOGO 下半部分被截断挤压。
|
||
- 【设计图要求】:整个顶部应该是【上下两层】的垂直布局(QVBoxLayout)。
|
||
* 上层(Layer 1):整行只有左侧的 LOGO、“ReinLoop V1.0” 标题,以及最右侧的设置图标按钮。这一行背景为纯白,不含任何 Tab 标签。
|
||
* 下层(Layer 2):独立的一行,用来放置完整的 Tab 栏。
|
||
- 【代码修复】:请将顶部整体调整为一个 `QVBoxLayout`,将 LOGO 整行(QHBoxLayout)作为第一个子布局,将 Tab 栏(QTabWidget/QTabBar)作为第二个子布局。
|
||
|
||
2. 补齐两行之间的灰色分割线与 Tab 背景色
|
||
- 【设计图要求】:LOGO 行与 Tab 栏行之间、以及 Tab 栏行与下方主体卡片之间,都有通栏的极细灰色分割线。并且,【整个 Tab 栏这一行】的背景色应该是一体化的浅灰色。
|
||
- 【QSS 修复】:
|
||
* 为包含 Tab 栏的这一层容器(或者 QTabWidget 的 TabBar 区域)设置背景色为 `#F8FAFC`(或与大背景融为一体的微冷浅灰)。
|
||
* 通过 QSS 或在布局间添加 `QFrame` 分割线,确保 LOGO 行下方有一条颜色为 `#E2E8F0` 的 `1px` 水平线。
|
||
|
||
3. 修复 Tab 栏选中状态的颜色逻辑
|
||
- 【现状】:目前所有的 Tab 文字和下划线都是死板的蓝色。
|
||
- 【设计图要求】:
|
||
* 【未选中状态】:文字和图标必须是【中灰色】(颜色为 `#64748B`),且【没有】蓝色底部下划线。
|
||
* 【选中状态】:只有当前被激活的 Tab,其文字、图标以及底部下划线才会一起变成【品牌蓝】(`#0960D1`)。
|
||
- 【QSS 修复】:请严格分离 `QTabBar::tab`(未选中默认样式)与 `QTabBar::tab:selected`(选中高亮样式)的颜色控制。
|
||
|
||
4. 彻底找回丢失的 SVG 图标(Icon)
|
||
- 【现状】:目前的界面上图标完全消失了。
|
||
- 【修复】:
|
||
* 确保在 PySide 代码中,使用 `QSize(18, 18)` 显式为 Tab 栏注入对应的图标资产:`link_icon.svg`(连接设置)、`control_icon.svg`(控制设置)、`debug_icon.svg`(模型调试)。
|
||
* 为了让图标在未选中时呈现灰色、选中时呈现蓝色,请检查你使用的 SVG 源码或直接在 `QTabBar::tab:!selected` 中加入 `opacity: 0.6;` 降低未选中图标的明度,使其趋近于灰色。
|
||
|
||
5. 移除输入框在未激活时的过粗蓝色边框
|
||
- 【现状】:目前即使没有点击输入框,部分输入框(如 PLC 地址)也自带了显眼的蓝色边框。
|
||
- 【修复】:请确保 `QLineEdit` 的默认状态边框为极细的淡灰色(`1px solid #E2E8F0`),【只有】在聚焦状态下(`QLineEdit:focus`)才允许变蓝。
|
||
|
||
---
|
||
|
||
## 💻 交付要求
|
||
请在完全不破坏后端逻辑、信号槽和既有变量名(如各输入框的 ObjectName)的前提下,重新梳理顶部布局结构并重写 QSS 样式表。请输出【完整重构后】的代码。
|
||
|
||
运行
|
||
💡 为什么这样能纠正它?
|
||
AI 在写 Qt 布局时,很容易因为贪图省事把多个小容器直接 addWidget 到同一个行布局里,从而导致大面积挤压变形。
|
||
这个 Prompt 的核心是帮它理清“两层空间结构”(上层 LOGO,下层 Tab 栏),并利用 QTabBar::tab:!selected 和 QTabBar::tab:selected 这对绝对对立的伪状态强迫它交替颜色。这样改完,顶部的精细度就会瞬间恢复。
|
||
|
||
tab栏选中状态不仅文字要变色,icon也要变色的,
|
||
|
||
收到!这是一个非常关键的细节。在 PySide (Qt) 中,如果要实现 “选中 Tab 时,不仅文字变蓝,Icon 图标也同步由灰变蓝”,普通的 QSS 对 QTabBar::tab:selected 的 color 设置只能影响文字,无法直接改变图片文件的颜色。
|
||
|
||
要完美达到设计图(image_05629d.png)的效果,有以下两种工业级的标准解决方案。你可以根据你目前图标资产的形式(是纯 SVG 源码字符串,还是两个独立的图片文件),让 AI 选择其中一种去实现:
|
||
|
||
💡 方案一:为 AI 提供“双态图标”机制(最稳妥、最容易看懂)
|
||
既然选中和未选中状态颜色不同,最标准的做法是准备两套图标(或者让 AI 用代码逻辑在切换 Tab 时动态更换图标)。
|
||
|
||
你可以让 AI 编写一个信号槽监听函数,当 Tab 切换时,动态更换图标:
|
||
|
||
给 AI 的专项补充提示词(方案一):
|
||
|
||
Markdown
|
||
# 6. 联动修改:Tab 选中时 Icon 变色逻辑
|
||
在 QSS 中,`border-bottom` 和 `color` 只能改变下划线和文字,无法直接修改 `QIcon` 图片的颜色。为了让图标(Icon)在选中时也变成蓝色,请在 PySide 代码中加入 `currentChanged` 信号槽监听:
|
||
|
||
1. 准备两套状态(如果只有一套,请让 AI 用逻辑或准备蓝色的 `link_icon_active.svg` 等)。
|
||
2. 在初始化时,编写一个动态更新图标的函数:
|
||
```python
|
||
def update_tab_icons(index):
|
||
# 默认全设为灰色图标
|
||
tab_widget.setTabIcon(0, QIcon("link_icon.svg"))
|
||
tab_widget.setTabIcon(1, QIcon("control_icon.svg"))
|
||
tab_widget.setTabIcon(2, QIcon("debug_icon.svg"))
|
||
|
||
# 将当前选中的项换成蓝色高亮图标(或者通过代码动态染色)
|
||
if index == 0:
|
||
tab_widget.setTabIcon(0, QIcon("link_icon_blue.svg")) # 或者是高亮资产
|
||
elif index == 1:
|
||
tab_widget.setTabIcon(1, QIcon("control_icon_blue.svg"))
|
||
elif index == 2:
|
||
tab_widget.setTabIcon(2, QIcon("debug_icon_blue.svg"))
|
||
|
||
# 绑定信号
|
||
tab_widget.currentChanged.connect(update_tab_icons)
|
||
# 首次运行初始化
|
||
update_tab_icons(0)
|
||
💡 方案二:利用 QSS 伪状态 + 属性改变(全 QSS 纯净流)
|
||
如果你不想在 Python 代码里写一堆 if-else 来换图片,Qt 还允许我们在 QSS 中利用 qproperty-icon 属性直接在“选中伪状态”下强行替换图标路径。
|
||
|
||
给 AI 的专项补充提示词(方案二):
|
||
|
||
CSS
|
||
/* 默认状态(未选中):使用原图,并降低透明度使其呈灰色调 */
|
||
QTabBar::tab {
|
||
background: transparent;
|
||
color: #64748B;
|
||
padding: 12px 24px;
|
||
}
|
||
|
||
/* 核心技巧:利用 Qt 样式表属性在选中状态下直接覆盖图标文件 */
|
||
/* 假设你让 AI 或者是你自己生成了对应的蓝色版本图标,直接在 QSS 里换掉它 */
|
||
QTabBar::tab:selected {
|
||
color: #0960D1;
|
||
font-weight: bold;
|
||
border-bottom: 3px solid #0960D1;
|
||
}
|
||
|
||
/* 如果你的 AI 能够将 SVG 渲染为 QIcon,可以在具体的 tab 识别器或伪状态中强制指定属性 */
|
||
/* 注:原生 QTabBar 对单个 tab 的 qproperty-icon 支持有限,
|
||
如果直接支持,可让 AI 尝试以下 QSS 扩展(需要指定 objectName 或位置):*/
|
||
QTabBar::tab:selected[text="连接设置"] {
|
||
/* 如果有对应的蓝色SVG,可以直接在这里切换 */
|
||
qproperty-icon: url(link_icon_blue.svg);
|
||
}
|
||
📋 针对你的这个需求,你可以直接对 AI 这样说:
|
||
“补充一条刚需:当 Tab 栏切换时,选中的 Tab 连同文字和 Icon 图标必须同时变蓝;未选中的则图标和文字同时变灰。
|
||
|
||
请你在 Python 代码中为 QTabWidget (或 QTabBar) 绑定 currentChanged 信号。当用户点击不同的 Tab 时,动态地为当前选中的 Tab 切换成蓝色的高亮版 SVG 图标(例如 link_icon_blue.svg),而让未选中的 Tab 保持默认的灰色线条图标。请把这个变色逻辑完整实现在重构后的代码中。”
|
||
|
||
为什么tab栏还是不能点击从灰色变深色
|
||
|
||
你的 Tab 栏之所以点击后文字和图标依然“死”在灰色、没有任何变化,是因为 AI 在编写 main_window.py 时犯了两个 Qt 机制上的常识性错误:
|
||
|
||
核心原因分析
|
||
图标(Icon)永远是灰色的原因:
|
||
代码里 AI 写的是:
|
||
|
||
Python
|
||
link_icon.addFile(_link_gray, QSize(18, 18), QIcon.Normal, QIcon.Off)
|
||
link_icon.addFile(_link_blue, QSize(18, 18), QIcon.Selected, QIcon.On)
|
||
这是完全错误的。 在 Qt 中,QIcon.Selected 状态只对 QListView 或 QTreeView 这种列表视图的选中项有效。QTabBar 在绘制选中的标签时,永远只会去读取 QIcon.Normal(或 QIcon.Active)状态的图片。因此,不论你点哪个 Tab,它渲染出来的永远是第一行指定的灰色图标。
|
||
|
||
文字依然是灰色的原因:
|
||
因为你的 Tab 栏设置了动态自定义属性:self.tab_bar.setProperty("cssClass", "mainTab")。AI 在后台写的 QSS 样式表可能只简单写了 QTabBar::tab:selected,导致样式选择器的优先级(权重)不够,被默认的置灰样式覆盖了;或者是因为点击切换时,QTabBar 没能自动触发样式的重绘刷新(Polish)。
|
||
|
||
💡 怎么告诉 AI 一步到位修复?
|
||
不用在 QSS 里面去猜复杂的伪状态优先级了,既然我们在 main_window.py 里已经有了统一的切换函数 _switch_tab(self, index),最工业级、100% 稳定的解法就是在切换事件里用代码动态更新图标和样式。
|
||
|
||
你可以把下面这段话原封不动复制给 AI:
|
||
|
||
Markdown
|
||
# 专项修复:解决 Tab 栏点击不亮、文字和图标始终为灰色的 Bug
|
||
|
||
目前点击 Tab 栏时,文字和图标完全没有变成蓝色,依然保持灰色。
|
||
这是因为:
|
||
1. `QTabBar` 不支持 `QIcon.Selected` 状态,它始终在使用 `QIcon.Normal` 的灰色图标。
|
||
2. QSS 样式在 `QTabBar` 独立使用时,`:selected` 伪状态由于属性权重问题可能失效了。
|
||
|
||
请你针对 `main_window.py` 进行如下修改,使用显式代码逻辑实现 100% 稳定的高亮切换:
|
||
|
||
## 1. 简化初始化图标代码
|
||
请将 `_setup_ui` 中原本复杂的 `QIcon.addFile` 逻辑删掉,直接将灰色和蓝色的文件路径保存为类属性,方便动态调用。例如:
|
||
```python
|
||
# 在 _setup_ui 中定义好 6 个资产路径
|
||
self._icons_gray = [
|
||
os.path.normpath(os.path.join(_src, "link_icon_gray.svg")),
|
||
os.path.normpath(os.path.join(_src, "control_icon_gray.svg")),
|
||
os.path.normpath(os.path.join(_src, "debug_icon_gray.svg"))
|
||
]
|
||
self._icons_blue = [
|
||
os.path.normpath(os.path.join(_src, "link_icon.svg")),
|
||
os.path.normpath(os.path.join(_src, "control_icon.svg")),
|
||
os.path.normpath(os.path.join(_src, "debug_icon.svg"))
|
||
]
|
||
|
||
# 初始时先填入图标(默认全灰)
|
||
self.tab_bar.addTab(QIcon(self._icons_gray[0]), " 连接设置")
|
||
self.tab_bar.addTab(QIcon(self._icons_gray[1]), " 控制设置")
|
||
self.tab_bar.addTab(QIcon(self._icons_gray[2]), " 模型调试")
|
||
|
||
|
||
2. 升级 _switch_tab 切换函数
|
||
请将 _switch_tab 函数重写,当 Tab 切换时,动态把当前选中的项换成蓝色图标,其余换回灰色图标,并强制触发 style().polish() 刷新 QSS 样式:
|
||
|
||
def _switch_tab(self, index: int):
|
||
"""切换标签页,并动态刷新图标与 QSS 状态"""
|
||
self.stack.setCurrentIndex(index)
|
||
|
||
# 阻断信号,防止改变时死循环
|
||
self.tab_bar.blockSignals(True)
|
||
self.tab_bar.setCurrentIndex(index)
|
||
self.tab_bar.blockSignals(False)
|
||
|
||
# 动态轮询,更新图标
|
||
for i in range(self.tab_bar.count()):
|
||
if i == index:
|
||
# 选中的 tab 换成蓝色高亮图标
|
||
self.tab_bar.setTabIcon(i, QIcon(self._icons_blue[i]))
|
||
else:
|
||
# 未选中的 tab 换成灰色图标
|
||
self.tab_bar.setTabIcon(i, QIcon(self._icons_gray[i]))
|
||
|
||
# 强制让控件重新polish样式,确保 QSS 中的 :selected 样式立即生效
|
||
self.tab_bar.style().unpolish(self.tab_bar)
|
||
self.tab_bar.style().polish(self.tab_bar)
|
||
|
||
3. 检查你的全局 QSS 样式表
|
||
请确保你写给 mainTab 的 QSS 选择器严格区分了状态,并具有足够的权重(带上属性名):
|
||
|
||
CSS
|
||
/* 默认未选中状态:文字灰色 */
|
||
QTabBar[cssClass="mainTab"]::tab {
|
||
background: transparent;
|
||
color: #64748B;
|
||
font-weight: normal;
|
||
border-bottom: 3px solid transparent;
|
||
}
|
||
|
||
/* 选中状态:文字变成品牌蓝,并展示底部深蓝/高亮蓝下划线 */
|
||
QTabBar[cssClass="mainTab"]::tab:selected {
|
||
color: #0960D1;
|
||
font-weight: bold;
|
||
border-bottom: 3px solid #0960D1;
|
||
}
|
||
请在不破坏我原本任何业务逻辑和变量名的前提下,完成对 main_window.py 这一部分的重构。
|
||
|
||
|
||
|
||
# 任务:修复底部“刷新”与“连接设备”按钮的视觉错位与多余嵌套
|
||
|
||
目前界面的底部按钮(参考 `image_164e00.png`)存在严重的视觉 Bug:
|
||
1. 两个按钮被一个错误带蓝色激活边框的容器包裹了,属于多余的视觉嵌套。
|
||
2. 按钮的高度不足,导致“连接设备”的文字上下被无情截断。
|
||
3. 两个按钮的布局没有和上方的卡片对齐。
|
||
|
||
请针对 `connection_tab.py` 和你的全局 QSS 样式表进行以下精准重构:
|
||
|
||
## 🛠️ 核心修改要求
|
||
|
||
1. 剥离按钮外层多余的 QFrame
|
||
- 【现状】:你可能在按钮外层包裹了一个设置了 `cssClass` 或者是默认带有输入框聚焦样式的 `QFrame`。
|
||
- 【修改】:请在 `connection_tab.py` 的底部布局中,【删掉】这个包裹按钮的容器。让这两个按钮直接并排暴露在主布局的最下方,它们的背景应该是透明或跟随大背景的,严禁有外层边框。
|
||
|
||
2. 统一按钮的固定高度与内边距(彻底修复文字截断)
|
||
- 【修改】:请在代码或 QSS 中,为这两个按钮统一指定高度和内边距,确保文字上下绝对垂直居中。
|
||
```css
|
||
/* 刷新按钮样式 */
|
||
QPushButton#refresh_btn {
|
||
background-color: #FFFFFF;
|
||
border: 1px solid #CCDBF0;
|
||
color: #0960D1;
|
||
border-radius: 6px;
|
||
min-height: 34px;
|
||
max-height: 34px;
|
||
padding: 0px 20px;
|
||
font-weight: bold;
|
||
font-size: 14px;
|
||
}
|
||
QPushButton#refresh_btn:hover {
|
||
background-color: #F0F4FA;
|
||
}
|
||
|
||
/* 连接设备按钮样式 */
|
||
QPushButton#connect_btn {
|
||
background-color: #0F955D;
|
||
border: none;
|
||
color: #FFFFFF;
|
||
border-radius: 6px;
|
||
min-height: 34px;
|
||
max-height: 34px;
|
||
padding: 0px 24px;
|
||
font-weight: bold;
|
||
font-size: 14px;
|
||
}
|
||
QPushButton#connect_btn:hover {
|
||
background-color: #0D8250;
|
||
}
|
||
|
||
|
||
|
||
# 任务:根据设计图重构“模型调试(DebugTab)”页面组件与布局
|
||
参考connection_tab的页面设计,请对 `DebugTab` 页面(即第三个标签页的内容区)进行完整的 UI 梳理与 QSS 样式编写。保持一贯的工业科技感、12px卡片圆角和浅灰色柔和基调。
|
||
|
||
页面主体由【上下两个 Section 卡片】组成,均采用带有蓝色左侧竖线装饰的纯白卡片样式:
|
||
- **卡片 1(上)**:系统辨识(包含多行密集表单项与控制按钮)
|
||
- **卡片 2(下)**:高级设置(包含两行双列输入框)
|
||
|
||
### 1. 卡片一:系统辨识(System Identification Card)
|
||
* **标题**:`蓝竖线装饰条` + `系统辨识` 文本。
|
||
* **布局逻辑**:建议使用 `QGridLayout` 处理前 4 行的参数项。最后一行“序列”独立作为一个布局或整合进网格中。
|
||
* **表单项明细**:
|
||
* **第 1 行**:
|
||
* 左列:标签“压力上限:”,右接带单位内嵌文本的输入框,默认值 `200`,右侧灰色内嵌文字 `kPa`。
|
||
* 右列:标签“过程升温:”,右接带单位内嵌文本的输入框,默认值 `30`,右侧灰色内嵌文字 `°C`。
|
||
* **第 2 行**:
|
||
* 左列:标签“约束上界”,右接标准输入框,默认值 `200`。
|
||
* 右列:标签“下界:”,右接标准输入框,默认值 `50`。
|
||
* **第 3 行**:
|
||
* 左列:标签“容积”,右接带单位内嵌文本的输入框,空初始值,右侧灰色内嵌文字 `L`。
|
||
* 右列:放置一个**“测试”按钮**,样式为:`background-color: #0960D1;` (品牌蓝),白色文字。
|
||
* **第 4 行**:
|
||
* 左列:标签“周期:”,右接带单位内嵌文本的输入框,默认值 `2.5`,右侧灰色内嵌文字 `s`。
|
||
* 右列:标签“阶数:”,右接标准输入框,默认值 `6`。
|
||
* **第 5 行(通栏/长输入框行)**:
|
||
* 标签“序列:”,右接一个超长输入框,带浅灰色占位符文本(Placeholder): `如10,20,30,40,50,60,70,80`。
|
||
* **核心动作按钮**:在这行输入框的右侧,放置一个 **“开始辨识” 按钮**。样式为:`background-color: #0960D1;` (品牌蓝),白色文字,左侧带一个白色的播放 ▶ 图标。
|
||
|
||
---
|
||
|
||
### 2. 卡片二:高级设置(Advanced Settings Card)
|
||
* **标题**:`蓝竖线装饰条` + `高级设置` 文本。
|
||
* **布局逻辑**:双列并排,共 2 行。
|
||
* **表单项明细**:
|
||
* **第 1 行**:
|
||
* 左列:标签“死区:”,右接输入框(带浅灰占位符 `默认2...`)。
|
||
* 右列:标签“单步限幅:”,右接标准空输入框。
|
||
* **第 2 行**:
|
||
* 左列:标签“总限幅:”,右接标准空输入框。
|
||
* 右列:标签“最大脉冲数:”,右接标准输入框,默认值 `85000`。
|
||
|
||
|
||
|
||
系统状态栏
|
||
🧱 布局结构(Layout)
|
||
容器样式:横向并排的三个独立卡片(QFrame),嵌入在 QHBoxLayout 布局中。
|
||
|
||
卡片基础特质:纯白背景(#FFFFFF),带有 12px 的柔和圆角(border-radius: 12px;),四周有极浅的微弱模糊阴影(QGraphicsDropShadowEffect)以提供悬浮层级感。
|
||
|
||
内部对齐:每个卡片内部采用横向布局,左侧为圆形背景图标,右侧为上下堆叠的指标文本与数值。
|
||
|
||
🎨 三栏卡片像素级规范
|
||
1. 左侧卡片:当前系统压力(安全绿主题)
|
||
视觉定位:代表实时反馈的监控状态。
|
||
|
||
左侧图标:一个正圆形、极浅绿色背景(#E2F5ED)的容器,中心高亮展示绿色仪表盘线条图标图标均在src中,请选择对应的svg文件(#0F955D)。
|
||
|
||
右侧内容:
|
||
|
||
上方为小字标签:“当前系统压力”,颜色为中灰(#555555)。
|
||
|
||
下方为巨大的实时数值:0.0,字体加粗(font-weight: bold; font-size: 32px;),颜色为安全绿(#0F955D),右侧紧跟同色小字单位 kPa。
|
||
|
||
右上角点缀:卡片右上角带有一个小的安全绿静态状态圆点。
|
||
|
||
2. 中间卡片:设置目标压力(品牌蓝主题)
|
||
视觉定位:代表用户输入的控制靶点。
|
||
|
||
左侧图标:一个正圆形、极浅蓝色背景(#EBF3FE)的容器,中心高亮展示蓝色准心/目标线条图标(#0960D1)。
|
||
|
||
右侧内容:
|
||
|
||
上方为小字标签:“设置目标压力”,颜色为中灰(#555555)。
|
||
|
||
下方为巨大的设定数值:0.0,字体加粗,颜色为品牌蓝(#0960D1),右侧紧跟同色小字单位 kPa。
|
||
|
||
右上角点缀:卡片右上角带有一个小的品牌蓝静态状态圆点。
|
||
|
||
3. 右侧卡片:控制阀门开度(警示橙主题)
|
||
视觉定位:代表执行机构的输出状态。
|
||
|
||
左侧图标:一个正圆形、极浅橙色背景(#FFF2E8)的容器,中心高亮展示橙色波纹/阀门象征线条图标(#E67E22)。
|
||
|
||
右侧内容:
|
||
|
||
上方为小字标签:“控制阀门开度”,颜色为中灰(#555555)。
|
||
|
||
下方为巨大的输出数值:0.0,字体加粗,颜色为执行橙(#E67E22),右侧紧跟同色小字单位 %。
|
||
|
||
右上角点缀:卡片右上角带有一个小的执行橙静态状态圆点。 |