Compare commits

..
Author SHA1 Message Date
Codex c63bef311c feat: release news and store experience updates 2026-07-26 11:09:11 +08:00
李宝儿的Macbook 2b37713791 feat: refine responsive automotive storefront 2026-07-25 11:15:29 +08:00
WorkBuddy 4f070d4677 feat: release B2B Trade theme 2.1.0 2026-07-24 22:25:29 +08:00
WorkBuddy 13a35c7ec7 fix: FAQ accordion 背景透明化,消除左右色差
Bootstrap accordion 默认白色背景与 FAQ 左侧说明区域的暖白
背景形成色差。将 accordion 各层级背景设为 transparent,
保持整段 FAQ 区域颜色一致。
2026-07-24 18:25:24 +08:00
149 changed files with 5138 additions and 1987 deletions
+4
View File
@@ -1,5 +1,9 @@
<?php <?php
/** Not-found template. @package Fragrance_Trade */ /** Not-found template. @package Fragrance_Trade */
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
get_header(); get_header();
?> ?>
<main id="primary" class="site-main container py-5 fragrance-content-area"> <main id="primary" class="site-main container py-5 fragrance-content-area">
+46 -208
View File
@@ -1,225 +1,63 @@
# 代码清理方案 # B2B Trade 2.1.0 清理记录
> 项目:B2B Trade 主题(fragrance-trade 本文记录 2.1.0 已执行的清理和必须保留的兼容边界,后续维护不得重新引入已删除的运行路径。
> 基准:2026-07-24 全量代码扫描
> 原则:分阶段、可回滚、每步可验证
--- ## 已完成
## 清理范围总览 ### 联系流程
| 类别 | 数量 | 风险 | 说明 | - 联系页只展示联系方式、营业时间和地图导航。
|------|------|------|------| - 不注册前端提交处理、邮件发送、成功/失败状态或防重放字段。
| 死模板文件 | 7 个 | 低 | 多页面重构后遗留,无任何 `get_template_part()` 引用 | - 服务入口统一跳转 `/contact/`,不拼接服务查询参数。
| 死 JS 库 | 4 个 | 中 | plugins.js 中 6 个库仅 1 个实际使用 | - 旧的附加查询参数不会影响页面访问,但主题不消费它。
| 死 CSS 样式 | ~60% vendor.css | 低 | Jarallax + AOS 样式,对应功能未使用 |
| 死图片资源 | 13 个 | 极低 | 旧模板遗留素材,全项目零引用 |
| 死表单处理器 | 1 个 | 中 | inquiry.php 仍挂在 admin_post 但无表单提交 |
| CDN 安全缺失 | 2 处 | 中 | Bootstrap 无 SRI 完整性校验 |
| jQuery 冗余依赖 | 1 处 | 中 | 仅为 Colorbox 一个函数加载整个 jQuery |
--- ### 前端依赖
## 阶段一:死代码删除(零风险) - Bootstrap 5.3.8 固定在 `assets/vendor/bootstrap/`,包含 CSS、bundle JS 和许可证。
- `theme.js` 使用原生 DOM API。
- 外部视频只在新标签页打开,不解析视频域名、不生成 iframe。
- 字体使用系统英文/中文字体栈。
- 删除无消费者的通用重置包、弹窗样式与脚本聚合文件。
> 删除后不影响任何已上线页面。每步删完刷新全站确认无报错。 ### 数据与初始化
### 1.1 删除 7 个死模板文件 - 初始化由 `fragrance_trade_schema_version` 控制。
- 7 个默认服务来自 `fragrance_trade_service_seeds()`,种子标识写入 `_fragrance_seed_key`
- 写入失败会记录后台错误,当前迁移版本不会推进。
- 约定页面会复用已有内容和状态,只补齐页面模板。
- 用户已有的非发布页面不会被自动发布或恢复。
- 旧产品外观设置仅在旧站迁移时读取。
| 文件 | 原用途 | 替代方案 | ### 资源与样式
|------|--------|----------|
| `template-parts/home/products.php` | 旧 B2B 产品展示 | 已被 CPT `service` + `service-teaser.php` 替代 |
| `template-parts/home/services.php` | 旧服务总览 | 已被 `service-teaser.php` 替代 |
| `template-parts/home/quality.php` | 旧质检流程 | 首页不再展示,内容可在关于页补充 |
| `template-parts/home/process.php` | 旧施工流程 | 首页不再展示 |
| `template-parts/home/projects.php` | 旧项目案例 | 已被 CPT `case` + `case-teaser.php` 替代 |
| `template-parts/home/customization.php` | 旧定制服务 | 首页不再展示 |
| `template-parts/home/rfq.php` | 旧预约表单 | 联系页已改为联系方式卡片,无表单 |
**验证方法**:删除后逐页访问首页 / 关于 / FAQ / 联系页 / 服务列表 / 案例列表,确认无 500 错误、无模板缺失警告 - 删除 7 张未被模板引用的旧产品位图
- 删除未引用的 Logo 占位图。
- 保留汽车贴膜服务、门店、视频和案例的中性 SVG 占位图。
- 删除没有 PHP 或 JS 消费者的质量、流程、项目和定制板块配置与 CSS。
- 首页两个展示查询使用 `no_found_rows`
- 默认值数组在单次请求内缓存。
### 1.2 删除 13 个死图片资源 ### 安全与直接访问
| 文件 | 说明 | - 所有主题 PHP 入口、模块和模板片段均检查 `ABSPATH`
|------|------| - 输出 URL、属性、纯文本和富文本继续按上下文转义。
| `assets/images/main-logo.png` | 旧 logo,已用 `placeholder-logo.svg` + WP custom-logo | - 视频新窗口链接使用 `noopener noreferrer`
| `assets/images/product-item1.jpg` | 旧产品图 ×3 | - 地图链接优先使用 `store_map_url`,否则仅根据非空地址生成。
| `assets/images/product-item2.jpg` | |
| `assets/images/product-item3.jpg` | |
| `assets/images/insta-item1.jpg` | 旧 Instagram 展示图 ×6 |
| `assets/images/insta-item2.jpg` | |
| `assets/images/insta-item3.jpg` | |
| `assets/images/insta-item4.jpg` | |
| `assets/images/insta-item5.jpg` | |
| `assets/images/insta-item6.jpg` | |
| `assets/images/single-image1.png` | 旧单图素材 |
| `assets/images/factory-workflow-illustration.webp` | 旧流程插图 |
**验证方法**:全项目 grep 这些文件名,确认零引用(已验证)。 ## 保留的兼容接口
--- - 文本域:`fragrance-trade`
- PHP 函数前缀:`fragrance_trade_`
- CSS 前缀:`b2b-`
- 地图设置键:`store_map_url`
- 服务旧图片元数据:`_service_image`
- 旧安装识别选项:`fragrance_trade_setup_done`
## 阶段二:JS 依赖瘦身(中等风险) 这些标识符涉及已有站点数据或第三方定制,未经正式迁移方案不得全局改名或删除。
> 当前 plugins.js 拼接了 6 个库,但只有 Colorbox 真正在用。 ## 后续资源替换原则
> AOS 虽然在 theme.js 中初始化,但没有任何模板使用 `data-aos` 属性,属于空转。
### 2.1 库使用情况核实 - 运营图片应通过特色图片或外观配置上传,不写入主题默认数据。
- 默认主题包只提供中性占位素材。
| 库 | plugins.js 中 | theme.js 调用 | 模板中使用 | 结论 | - 新前端依赖必须本地打包、记录版本与许可证,并经过无外部请求扫描。
|----|--------------|---------------|-----------|------| - 开发验证工具不应作为主题运行依赖进入生产请求路径。
| jQuery Easing | 有 | 无 | 无 | **删除** |
| Jarallax v2.0.2 | 有 | 无 | 无 | **删除** |
| Colorbox 1.6.4 | 有 | `$.fn.colorbox()` | `class="youtube"` 在 video.php | **保留** |
| AOS | 有 | `AOS.init()` | 无 `data-aos` 属性 | **删除** |
| anime.js | 有 | 无 | 无 | **删除** |
| HC-Sticky | 有 | 无 | 无 | **删除** |
### 2.2 操作步骤
**步骤 A — 清理 theme.js 中的 AOS 调用**
删除 `initAnimations()` 函数及其调用(第 19-22 行、第 42 行)。AOS 在空转,删了不影响任何可见效果。
**步骤 B — 重写 plugins.js**
将 plugins.js 从 6 库拼接缩减为仅保留 Colorbox 1.6.4(约 4KB)。其余 5 个库全部移除。
**步骤 C — 清理 vendor.css**
删除 vendor.css 中的 Jarallax 样式(第 6-36 行)和 AOS 样式(第 89 行至文件末尾的大段压缩 CSS)。仅保留 Colorbox 样式(第 39-85 行)。
**步骤 D — 评估 jQuery 去留**
Colorbox 依赖 jQuery。两个选项:
| 方案 | 改动量 | 效果 |
|------|--------|------|
| **A. 保留 jQuery**(推荐当前阶段) | 无 | 维持现状,Colorbox 正常工作 |
| B. 用原生 lightbox 替代 Colorbox | 中等 | 彻底去掉 jQuery 依赖,减少 ~90KB 加载 |
> 方案 B 需要找一个不依赖 jQuery 的 lightbox 库(如 GLightbox)并改写 theme.js 的 `initVideoLightbox()`。建议放到独立迭代中做,不在本次清理范围内。
**验证方法**
1. 打开关于门店页,点击施工视频缩略图,确认 Colorbox 弹窗正常弹出
2. 检查浏览器控制台无 JS 报错
3. 对比清理前后页面加载时间(预计减少 ~80KB JS 传输)
---
## 阶段三:死表单处理器处理(中等风险)
### 3.1 现状
`inc/inquiry.php` 注册了 `admin_post_fragrance_rfq``admin_post_nopriv_fragrance_rfq` 两个 hook,但联系页已无表单,这些 hook 永远不会被触发。
### 3.2 两个选项
| 方案 | 操作 | 理由 |
|------|------|------|
| **A. 保留但标注弃用**(推荐) | 在文件头加 `@deprecated` 注释,保留 hook 注册 | 未来可能恢复预约表单功能,代码本身安全合规,留着不碍事 |
| B. 完全删除 | 删除 inquiry.php + 从 functions.php 移除加载 | 彻底清理,但未来恢复需重写 |
> 推荐 Ainquiry.php 的安全实现(nonce + honeypot + 速率限制 + 邮件头注入防护)是项目中的优秀范例,删了可惜。标注弃用即可。
---
## 阶段四:CDN 安全加固(中等风险)
### 4.1 现状
`inc/setup.php` 第 47-54 行从 `cdn.bootcdn.net` 加载 Bootstrap CSS/JS,无 `integrity` 属性(SRI),无本地回退。如果 CDN 被劫持,攻击者可注入恶意代码。
### 4.2 操作步骤
**步骤 A — 添加 SRI 完整性校验**
为 Bootstrap CSS 和 JS 添加 `integrity` + `crossorigin` 属性。需要从 [BootCDN](https://www.bootcdn.cn/) 获取对应版本的 SRI 哈希。
```php
// 修改前
wp_enqueue_style( 'fragrance-bootstrap', 'https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0-alpha3/css/bootstrap.min.css', array(), '5.3.0-alpha3' );
// 修改后(需填入实际哈希)
wp_enqueue_style( 'fragrance-bootstrap', 'https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0-alpha3/css/bootstrap.min.css', array(), '5.3.0-alpha3' );
// WP 的 wp_enqueue_style 不直接支持 integrity,需用 style_loader_tag 过滤器追加属性
```
由于 WordPress 的 `wp_enqueue_*` 函数不直接支持 `integrity` / `crossorigin` 属性,需要通过 `style_loader_tag``script_loader_tag` 过滤器注入:
```php
// 在 inc/setup.php 中添加
function fragrance_trade_resource_integrity( $html, $handle ) {
$sri = array(
'fragrance-bootstrap' => 'sha384-XXXXX', // 替换为实际哈希
);
if ( isset( $sri[ $handle ] ) ) {
$html = str_replace( '/>', ' integrity="' . $sri[ $handle ] . '" crossorigin="anonymous" />', $html );
}
return $html;
}
add_filter( 'style_loader_tag', 'fragrance_trade_resource_integrity', 10, 2 );
```
> **注意**Google Fonts 不支持 SRI(动态生成 CSS),保持现状即可。
**步骤 B — 可选:本地回退**
将 Bootstrap 下载到 `assets/vendor/bootstrap/`,CDN 加载失败时自动回退。此步骤为可选项,当前 CDN 可用性已较高。
**验证方法**:浏览器 DevTools → Network → 确认 Bootstrap 资源加载请求携带 `integrity` 头。
---
## 阶段五:Customizer 配置清理(低风险)
### 5.1 现状
`inc/customizer.php` 中仍有大量为已删除模板配置的 Customizer 字段(products、services、quality、process、projects、customization 等)。这些字段在后台 Customizer 面板中可见但对应的模板已不存在,用户会困惑。
### 5.2 操作步骤
1. 逐个移除已删除模板对应的 Customizer section + 字段注册代码
2. 对应的 `inc/defaults.php` 默认值也一并清理
3. 保留仍在使用的 sectionhero、trust、store_info、contact_social、video、documents、faq
> **注意**:此阶段改动量大且涉及 Customizer 面板结构,建议在测试环境验证后再上线。
---
## 执行顺序与预估
| 阶段 | 内容 | 文件改动数 | 风险 | 建议优先级 |
|------|------|-----------|------|-----------|
| 一 | 删死模板 + 死图片 | 删 20 个文件 | 极低 | 立即执行 |
| 二 | JS 库瘦身 | 改 3 个文件 | 中 | 紧随其后 |
| 三 | 表单处理器标注 | 改 1 个文件 | 极低 | 随手做 |
| 四 | CDN SRI 加固 | 改 1 个文件 | 低 | 本周内 |
| 五 | Customizer 清理 | 改 2 个文件 | 中 | 下次迭代 |
---
## 每阶段完成后的检查清单
- [ ] 全站逐页访问无 500 / 404
- [ ] 浏览器控制台无 JS 报错
- [ ] WP Debug Log 无 PHP warning/notice
- [ ] Customizer 面板无残留无效 section
- [ ] Git 提交信息标注 `[cleanup]` + 阶段编号
- [ ] 推送到 Gitea 前本地验证通过
---
## 回滚方案
每个阶段独立提交,如果发现问题:
```bash
# 查看提交历史
git log --oneline
# 回退到指定提交
git revert <commit-hash>
```
阶段一和阶段二各自独立 commit,确保可单独回滚。
+99 -227
View File
@@ -1,266 +1,138 @@
# 代码审查标准与流程 # B2B Trade 发布审查标准
> 适用项目B2B Trade 主题(fragrance-trade 适用范围B2B Trade 2.1.xWordPress 6.8+PHP 8.1+。
> 技术栈:WordPress 经典主题 / PHP 7.4+ / Bootstrap 5.3 / jQuery
> 版本:v1.0 | 2026-07-24
--- ## 1. 自动检查
## 一、审查原则 每次发布必须满足:
1. **安全优先** — WordPress 主题的 XSS / SQL 注入 / CSRF 是红线 - `git diff --check` 无输出;
2. **就事论事** — 评论针对代码,不针对人 - 所有 PHP 文件在 PHP 8.1 CLI 下通过 `php -l`
3. **说清楚为什么** — 不只说"改这个",要解释原因和潜在风险 - PHPCS 通过 WordPress Coding Standards 与 PHPCompatibilityWP
4. **给方向不给答案** — 引导作者自己思考,除非是明确的安全漏洞 - `node --check assets/js/theme.js` 通过;
5. **赞美好代码** — 发现优秀实践要明确指出,让团队学习 - CSS 花括号配对,所有 `url(...)` 本地资源存在;
- PHP、CSS 和 JS 不包含脚本、样式或字体 CDN 请求;
- 主题引用的本地静态资源全部存在;
- 仓库不包含已清退的旧业务词、旧视频弹窗库或旧产品位图。
--- ## 2. 初始化与数据安全
## 二、优先级体系 在空数据库中:
每个审查意见必须标注以下级别之一:
| 级别 | 标记 | 含义 | 处理要求 |
|------|------|------|----------|
| 🔴 阻断 | Blocker | 安全漏洞 / 数据丢失 / 破坏功能 | **必须修复后才能合并** |
| 🟡 建议 | Suggestion | 输入验证缺失 / 性能问题 / 可维护性 | **应当修复,可协商** |
| 💭 细节 | Nit | 命名 / 风格 / 文档 | 修不修都行,不阻塞 |
---
## 三、WordPress 主题专项审查清单 1. 安装 WordPress 并激活主题;
2. 确认创建 7 个带种子标识的服务;
3. 确认创建 `home``about``faq``contact`
4. 再运行两次初始化;
5. 确认服务数和页面数不增加;
6. 确认 `fragrance_trade_schema_version` 仅在无错误时推进。
### 🔴 阻断级检查项(必须全部通过) 在已有数据库中:
#### 3.1 安全性 - 已有页面正文、标题和状态不被覆盖;
- 草稿、私密或回收站页面不被自动发布;
- 对应页面模板被补齐;
- 后台显示非发布状态提醒;
- 已有 `_service_image``store_map_url` 保持有效。
- [ ] **直接访问防护** — 所有 PHP 文件以 `if ( ! defined( 'ABSPATH' ) ) exit;` 开头 需要注入一次 `wp_insert_post()` 错误,确认错误被记录、管理员可见且迁移版本不推进。
- [ ] **SQL 注入** — 禁止拼接 SQL;必须使用 `$wpdb->prepare()` 或 WP API`get_posts()`, `WP_Query`
- [ ] **XSS 防护** — 所有输出必须经过转义:
- HTML 文本 → `esc_html()`
- URL → `esc_url()`
- 属性 → `esc_attr()`
- 富文本 → `wp_kses_post()`
- [ ] **CSRF 防护** — 所有表单提交 / POST 处理必须有 `wp_nonce_field()` + `check_admin_referer()`
- [ ] **输入净化** — 所有 `$_POST` / `$_GET` / `$_SERVER` 必须用 `wp_unslash()` + 对应 `sanitize_*` 函数
- [ ] **邮件头注入** — 从用户输入构造邮件头时,必须剥离 `\r\n`
- [ ] **文件包含** — 禁止用户输入直接传入 `include` / `require` / `get_template_part()`
- [ ] **权限检查** — 任何管理员操作必须有 `current_user_can()` 检查
#### 3.2 功能正确性
- [ ] **关键路径错误处理** — 表单提交失败有 fallback(非空白白屏) ## 3. 页面冒烟
- [ ] **数据完整性** — 不会因空值 / 缺失字段导致 fatal error
- [ ] **重定向安全** — 使用 `wp_safe_redirect()` + `wp_validate_redirect()`,禁止直接 `header('Location:')`
### 🟡 建议级检查项 以下页面必须返回预期状态,并且 PHP 日志无 Warning、Deprecated、Parse 或 Fatal
#### 3.3 输入验证与数据 | 页面 | 预期 |
| --- | --- |
| 首页 | 200 |
| 服务列表 | 200 |
| 服务详情 | 200 |
| 案例列表 | 200 |
| 案例详情 | 200 |
| 关于门店 | 200 |
| 常见问题 | 200 |
| 联系我们 | 200 |
| 普通文章 | 200 |
| 搜索结果 | 200 |
| 不存在路径 | 404 |
- [ ] Customizer 字段是否有对应类型的 `sanitize_callback` 浏览器控制台不得出现 JavaScript 错误,Network 面板不得出现主题资源 404。
- [ ] `get_theme_mod()` 取值后是否有默认值兜底
- [ ] 用户上传的 URL 是否用 `esc_url_raw()` 存入、`esc_url()` 输出
#### 3.4 性能 ## 4. 业务分支
- [ ] **N+1 查询** — 循环内不执行数据库查询,提前预取 ### 服务图片
- [ ] **资源加载** — 脚本用 `wp_enqueue_script`,不硬编码 `<script src>`
- [ ] **图片优化** — 使用响应式 `srcset` / `loading="lazy"`
- [ ] **Transient 缓存** — 耗时操作(远程请求、复杂查询)用 transient 缓存
#### 3.5 可维护性 分别构造并确认:
- [ ] **死代码** — 新增模板文件必须有对应的 `get_template_part()` 调用;废弃文件及时删除 1. 有特色图片时优先显示特色图片;
- [ ] **命名规范** — 函数 / 变量统一 `fragrance_trade_` 前缀,蛇形命名 2. 无特色图片、有 `_service_image` 时显示旧 URL
- [ ] **函数长度** — 单函数不超过 80 行,超过则考虑拆分 3. 两者都没有时显示对应 SVG 占位图;
- [ ] **重复代码** — 相同逻辑出现 3 次以上应提取为 helper 函数 4. 首页和列表输出 `fragrance-capability`
- [ ] **文件组织** — 新功能按职责归入 `inc/` 对应模块,不堆在 `functions.php` 5. 详情输出 `large`
#### 3.6 前端质量 ### 地图
- [ ] **CDN 资源** — 第三方 CDN 必须有 SRI 完整性哈希 + 本地回退 分别确认:
- [ ] **第三方库** — 禁止将多个库的压缩代码拼接到单文件;用 npm + 构建流程管理
- [ ] **无障碍** — 图片有 `alt`,表单有 `label`,按钮有可读文本
- [ ] **响应式** — 移动端布局不溢出,触控目标不小于 44×44px
- [ ] `prefers-reduced-motion` — 动画尊重用户偏好
### 💭 细节级检查项 1. 显式地图 URL 优先于地址;
2. 只有地址时生成百度地图导航;
3. 地址和 URL 都为空时不渲染地图卡片;
4. 页面不出现任何硬编码门店地址。
- [ ] 文本域统一为 `fragrance-trade`,所有字符串包裹 `__()` / `esc_html_e()` ### 导航
- [ ] 注释说明"为什么"而非"做了什么"
- [ ] 长行拆分(单行 PHP echo 不超过 120 字符)
- [ ] CSS 自定义属性命名遵循 `--token-name` 格式
--- - 未分配菜单时输出 6 项默认导航;
- 少于两个菜单项时后台显示提醒;
- 桌面导航与移动 Offcanvas 均可键盘操作;
- 当前菜单不会造成横向滚动。
## 四、审查流程 ### 视频与 FAQ
``` - 有视频 URL 时只产生普通新窗口链接;
┌─────────────────────────────────────────────────────────────┐ - 链接包含 `target="_blank"``rel="noopener noreferrer"`
│ 代码审查流程 │ - 无视频 URL 时显示联系门店入口;
├─────────────────────────────────────────────────────────────┤ - FAQ 手风琴可点击和键盘操作,ARIA 状态随展开更新。
│ │
│ 1. 提交前自检 │
│ 作者按 §三 清单自检 → 确认无 🔴 项 │
│ ↓ │
│ 2. 提交审查 │
│ Gitea PR / 提交说明 → 附上改动摘要和测试方式 │
│ ↓ │
│ 3. 审查者检查 │
│ 按 §三 清单逐项 → 标注 🔴🟡💭 → 写明原因和建议方向 │
│ ↓ │
│ 4. 分支处理 │
│ ├─ 有 🔴 → 打回,作者修复后重新提交 │
│ ├─ 有 🟡 → 讨论确认,修复或记录为后续任务 │
│ └─ 仅 💭 → 可选修复,直接通过 │
│ ↓ │
│ 5. 合并 │
│ 审查者 Approve → 作者合并 → 删除分支 │
│ ↓ │
│ 6. 回顾(每周) │
│ 汇总本周审查发现的共性问题 → 更新本标准 │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 4.1 提交规范 ### 联系
提交 PR / 提交审查时,标题格式: - 联系页不存在提交表单;
- 服务入口不生成服务查询参数;
- 电话、邮箱与 WhatsApp 链接按配置显示;
- 主题不注册前端提交或邮件发送 Hook。
``` ## 5. 兼容矩阵
[类型] 简述 — 具体改动
类型:feat / fix / refactor / style / security / docs / chore 至少完成:
```
示例: | 环境 | 用途 |
``` | --- | --- |
[security] RFQ 表单 — 增加 nonce 验证 | WordPress 7.0 系列 + PHP 8.3 | 当前版本主回归 |
[feat] 案例详情页 — 添加施工前后对比图 | WordPress 6.8 系列 + PHP 8.2 | 最低 WordPress 分支 |
[refactor] inc/defaults.php — 按区块分组重组配置键 | PHP 8.1 CLI | 最低 PHP 语法检查 |
```
### 4.2 审查时间线 兼容环境可由维护者现有的 WordPress 测试站点提供,不要求主题仓库附带容器配置。
| 阶段 | 时限 | 说明 | ## 6. 视觉与无障碍
|------|------|------|
| 初次审查 | 提交后 24h 内 | 审查者完成首轮检查 |
| 修复后复审 | 作者修复后 12h 内 | 审查者确认修复 |
| 超时升级 | 超 48h 未回应 | @项目负责人介入协调 |
--- 桌面至少检查 1440 × 1000,移动至少检查 390 × 844
## 五、审查评论模板 - 首页、服务列表、服务详情、案例列表、关于、FAQ、联系;
- 无水平滚动、布局跳动、遮挡或断裂;
- 图片具有明确尺寸与替代文本;
- 可交互元素有可见焦点;
- 文本与背景保持可读对比度;
- Offcanvas 打开后焦点可进入,关闭后可返回触发按钮;
- 新窗口链接的可访问名称能表达用途。
### 🔴 阻断级 主题预览图必须来自通过桌面验收的中性演示首页,不得包含过时业务内容、品牌承诺或虚构真实门店照片。
``` ## 7. 发布元数据
🔴 [安全/XSS] 第 42 行:用户输入直接输出到 HTML
原因 发布前确认 `style.css`
$subtitle 变量来自 $_POST,未经 esc_html() 转义直接 echo
攻击者可注入 <script> 标签执行任意代码。
建议: - `Version: 2.1.0`
echo esc_html( $subtitle ); - `Requires at least: 6.8`
``` - `Tested up to: 7.0`
- `Requires PHP: 8.1`
- Theme URI 指向实际仓库
- 文本域为 `fragrance-trade`
### 🟡 建议级 同时确认 `languages/fragrance-trade.pot` 已按当前源码重新生成,本地 Bootstrap 许可证随主题包发布。
```
🟡 [性能/N+1] 第 18-25 行:循环内执行 get_post_meta()
原因:
在 while 循环内逐条查询 post_meta10 篇文章 = 10 次查询,
页面加载时会明显变慢。
建议:
循环前用 get_post_meta( $post_ids, 'key', false ) 批量预取,
或在循环内缓存结果。
```
### 💭 细节级
```
💭 [命名] 第 8 行:$d 变量名过于简短
考虑改为 $display_date 或 $post_date6 个月后回来看能秒懂。
```
---
## 六、当前项目待修复清单
基于 2026-07-24 代码扫描结果,以下问题应优先处理:
| 优先级 | 问题 | 文件 | 说明 |
|--------|------|------|------|
| 🟡 | 11 个死模板文件 | template-parts/home/ | products, services, quality, process, faq, rfq, factory, video, customization, documents, projects 未被引用 |
| 🟡 | plugins.js 拼接 6 个库 | assets/js/plugins.js | jQuery Easing + Jarallax + Colorbox + AOS + anime.js + HC-Sticky 混在一起,无版本标记,无法维护 |
| 🟡 | CDN 无 SRI | inc/setup.php:47-54 | Bootstrap + Google Fonts 缺少 integrity 哈希,无本地回退 |
| 🟡 | 零测试 | 全项目 | 无 phpunit / 无 .phpcs.xml / 无 CI |
| 💭 | defaults.php 扁平数组 | inc/defaults.php | ~170 个键未分组,可按区块拆分 |
| 💭 | jQuery 冗余依赖 | assets/js/theme.js | 仅 colorbox 用 jQuery,其余为原生 DOM,可考虑去掉 jQuery |
---
## 七、工具辅助
### 静态检查(建议引入)
```bash
# PHP 代码规范检查 — WordPress 标准
composer create-project wp-coding-standards/wpcs
phpcs --standard=WordPress --extensions=php inc/ template-parts/
# 自动修复可修复的问题
phpcbf --standard=WordPress --extensions=php inc/ template-parts/
```
### Git Pre-commit Hook(建议配置)
```bash
#!/bin/bash
# .git/hooks/pre-commit
# 提交前自动检查 PHP 语法 + 基本安全扫描
ERRORS=0
# 1. PHP 语法检查
for file in $(git diff --cached --name-only --diff-filter=ACM | grep "\.php$"); do
php -l "$file" > /dev/null 2>&1 || { echo "语法错误: $file"; ERRORS=1; }
done
# 2. 检查是否包含危险函数
for file in $(git diff --cached --name-only --diff-filter=ACM | grep "\.php$"); do
grep -n 'eval\s*(' "$file" && { echo "⚠️ eval() 检测到: $file"; ERRORS=1; }
grep -n 'exec\s*(' "$file" && { echo "⚠️ exec() 检测到: $file"; ERRORS=1; }
grep -n 'system\s*(' "$file" && { echo "⚠️ system() 检测到: $file"; ERRORS=1; }
done
exit $ERRORS
```
---
## 八、审查者轮值
| 角色 | 职责 |
|------|------|
| 主审 | 按 §三 清单逐项检查,出具审查意见 |
| 复审 | 对主审意见复核,确认无遗漏(可选) |
| 作者 | 回复意见,修复 🔴🟡 项,说明 💭 项处理决定 |
> 小团队(≤3人)可轮流担任主审,无需固定复审。
---
## 附录:本项目已有的优秀实践(请保持)
- ✅ 所有文件 `ABSPATH` 守卫
- ✅ RFQ 表单完整安全链:nonce + honeypot + 速率限制 + 邮件头注入防护
- ✅ Customizer 字段全部配置 `sanitize_callback`
- ✅ 统一 `fragrance_trade_` 函数前缀
- ✅ CSS 使用自定义属性设计令牌系统
- ✅ JS 尊重 `prefers-reduced-motion` + passive 事件监听
-`antispambot()` 邮件地址混淆
+109 -177
View File
@@ -1,193 +1,125 @@
# B2B Trade — 汽车贴膜门店 WordPress 主题 # B2B Trade
专业汽车贴膜门店(面向终端车主)的经典 WordPress 主题。支持服务项目展示、施工案例作品集、在线预约联系,适配移动端 B2B Trade 是一个面向汽车贴膜门店的 WordPress 经典主题。主题提供服务项目、施工案例、新闻资讯、门店介绍、常见问题和联系方式;联系页只展示门店对外信息、营业时间与地图导航,不包含提交表单、邮件发送处理器或 SMTP 依赖
## 项目说明 ## 运行要求
本主题面向汽车贴膜门店(TPU 保护膜 / 彩色改色膜 / 隔热防爆膜等),不是 B2B 代工厂模板。站点采用多页面架构 + 自定义文章类型,首页为落地页,引导车主浏览服务、查看案例并预约到店。 - WordPress 6.8 或更高版本
- PHP 8.1 或更高版本
- 当前发布版:2.2.0
- 已针对 WordPress 7.0 系列配置回归环境
> 仓库名 `fragrance-trade` 为历史遗留(原是香水贸易模板),代码标识符(文本域 `fragrance-trade`、CSS 前缀 `fragrance-`、PHP 函数前缀 `fragrance_trade_`)保持不变,不影响功能 前端依赖仅包含主题内置的 Bootstrap 5.3.8 CSS 和 bundle JS。字体使用操作系统字体栈,不会向字体、脚本或样式 CDN 发起请求
## 环境要求 首页信任栏、首页咨询区、关于页资料区和联系行动区使用主题内置的汽车施工场景背景图;图片均随主题本地加载,不依赖外部图床。
- WordPress 6.5+ ## 页面与内容结构
- PHP 7.4+
- 生产环境建议使用 HTTPS 和认证 SMTP 首页固定显示 5 个板块:
1. 首屏定位与双按钮
2. 四项信任指标
3. 服务项目轮播(最多 6 项,桌面每屏 2 项)
4. 案例作品轮播(最多 6 项,桌面每屏 2 项)
5. 联系门店行动区
关于页承载门店介绍、信任指标、环境图片、外部视频链接和服务文档。FAQ 页复用 8 项手风琴内容;联系页按配置展示门店信息和地图卡片。新闻资讯使用 WordPress 原生文章和分类,列表页每页显示 9 篇,支持分类、标签、日期归档、搜索、RSS 和独立详情页。
主题注册:
- `service` 服务项目文章类型,归档路径 `/services/`
- `case` 案例文章类型,归档路径 `/cases/`
- `case_type` 案例分类法
- `fragrance-capability` 图片尺寸(760 × 720,硬裁切)
服务卡片图片的显示优先级为:
1. 自定义横版封面 `_fragrance_service_cover_id`
2. 产品初始化数据中的主题横版封面
3. WordPress 特色图片
4. 旧站点的 `_service_image` URL
5. 主题内置的中性 SVG 占位图
首页和列表优先使用 4:3 横版封面;服务详情页仍使用 `large` 尺寸的特色图片,可完整展示产品参数海报。
## 首次激活
激活主题后会运行版本化且可重试的初始化:
- 创建 7 个默认服务,并使用 `_fragrance_seed_key` 防止重复;
- 创建或复用 `home``about``faq``contact` 页面;
- 未配置文章页时创建或复用 `news` 页面,并将其设置为 WordPress 文章页;
- 已配置文章页时保留现有页面与 URL,不强制覆盖;
- 为约定页面补齐模板,但不修改已有正文;
- 不会自动发布或恢复用户已有的草稿、私密页或回收站页面;
- 将已发布的 `home` 设置为静态首页;
- 仅在数据库写入全部成功后推进 `fragrance_trade_schema_version`
重复激活和重复执行初始化不会增加服务或页面数量。失败信息和未发布页面状态会在后台显示管理员通知。
未分配主导航时,主题输出:首页、服务项目、案例作品、新闻资讯、关于门店、常见问题、联系我们。已分配菜单时,主题不会自动增加新闻入口;如果菜单缺少 WordPress 文章页,后台会提示管理员手动添加。
## 新闻资讯
- 后台通过 WordPress“文章”发布新闻,不注册额外文章类型;
- 新站点的新闻列表默认位于 `/news/`,升级站点保留已有文章页地址;
- 列表卡片显示特色图片、分类、日期、标题和摘要,无图片时使用主题内置占位图;
- 分类、标签、日期和作者归档复用新闻卡片样式;
- 新闻详情包含上一篇、下一篇和联系门店行动区,不显示评论区;
- 主题不修改全站固定链接规则,也不会自动创建新闻分类或示例新闻。
## 服务与案例一键初始化
主题随包提供 5 个 KDX 隐形车衣产品服务、6 篇汽车改色案例和 62 张图片,但不会在主题激活时自动写入。管理员可进入“外观 → 初始化内容”,点击“一键初始化服务与案例”后将内容复制到当前站点的文章与媒体库。
导入器使用稳定的内容和图片标识防止重复;已有同名服务或案例会被保留且不会被覆盖。中途因写入失败留下的草稿可以再次执行导入以继续补齐,已发布内容不会重复生成。
首页的服务与案例区域使用原生响应式轮播,支持箭头、分页圆点、触摸滑动和 9 秒自动播放;鼠标悬停、键盘聚焦或触摸操作时会暂停,系统启用“减少动态效果”时自动播放关闭。
## 外观配置
“外观 → 自定义 → 门店首页”包含 6 个配置板块:
- 首屏定位
- 信任栏
- 门店环境与参观
- 文档支持
- 常见问题
- 联系方式与社交链接
主题已预置康得新合肥新创贴膜中心的对外名称、主营品牌、授权级别、所在城市、门店联系人、电话、地址与营业时间。这些信息仍可在“联系方式与社交链接”板块中逐项修改。页脚 ICP 备案号及备案查询链接也在此配置;备案号留空时前台不会显示。
视频链接不会嵌入页面或打开弹窗;配置后会在新标签页直接打开,并带有 `noopener noreferrer`
地图与导航按以下顺序工作:
1. “地图导航链接”用于覆盖“打开地图导航”按钮的目标地址;
2. 已配置有效的百度地图经纬度时,内嵌地图固定显示该坐标,并以门店名称和详细地址作为标注内容;
3. 未配置坐标时,使用门店名称检索并将结果限制在所在城市;门店名称为空时回退使用门店地址,检索词也为空时隐藏地图卡片。
Hero 按钮的站内默认路径会通过 `home_url()` 解析,支持 WordPress 子目录安装与多站点路径。
## 安装 ## 安装
1. `fragrance-trade` 文件夹复制到 `wp-content/themes/` 1.主题目录复制到 WordPress 的 `wp-content/themes/`
2. **外观 → 主题** 中激活 **B2B Trade** 2.外观 → 主题中激活 B2B Trade。
3. 主题激活时自动完成初始化: 3. 在“外观 → 菜单”中创建并分配主导航;未分配时主题会显示默认导航。
- 注册 `service`(服务项目)和 `case`(案例作品)两个自定义文章类型 4. 在“外观 → 自定义 → 门店首页”中核对并按需更新门店信息。
- 将 Customizer 中的 7 个产品数据迁移为 `service` 文章 5. 在服务项目和案例后台上传实际图片与内容,并通过“文章”发布新闻资讯。
- 自动创建首页、关于门店、常见问题、预约/联系四个页面
- 将首页设置为静态首页
4. **重要:** 激活后进入 **设置 → 固定链接**,点击"保存更改"刷新路由规则。
5.**外观 → 自定义 → 站点身份** 中设置站点标题和 Logo。
6.**外观 → 菜单** 中创建导航菜单并分配到 **主导航**
7. 打开 **外观 → 自定义 → 门店首页**,将所有演示文案替换为真实门店信息。
## 站点架构 发布前应在 WordPress 6.8 或更高版本、PHP 8.1 或更高版本环境中检查首页、服务、案例、新闻列表与详情、新闻归档、关于、FAQ、联系、搜索与 404 页面,并确认桌面和移动端没有资源 404、控制台错误或横向滚动。
### 页面结构 详细验收项见 `CODE_REVIEW_STANDARD.md`,清理范围与兼容策略见 `CODE_CLEANUP_PLAN.md`
| 页面 | 模板 | 说明 | ## 数据兼容
|------|------|------|
| 首页 | `front-page.php` | 落地页:Hero + 信任栏 + 服务项目预览 + 案例预览 + CTA |
| 服务项目列表 | `archive-service.php` | 展示全部服务项目(膜品) |
| 服务项目详情 | `single-service.php` | 单个膜品详情 |
| 案例作品列表 | `archive-case.php` | 施工案例作品集,支持按分类筛选 |
| 案例详情 | `single-case.php` | 单个施工案例 |
| 关于门店 | `page-about.php` | 门店介绍、环境展示、视频、服务保障 |
| 常见问题 | `page-faq.php` | FAQ 手风琴 |
| 预约/联系 | `page-contact.php` | 门店信息 + 联系方式 + 百度地图导航 |
### 首页板块(5 个模板片段) - 保留 `fragrance-trade` 文本域和 `fragrance_trade_*` 函数前缀。
- 保留 `store_map_url` 设置键。
`front-page.php` 组装以下片段(来自 `template-parts/home/`): - 保留 `_service_image` 只读回退,不自动改写或删除旧元数据。
- 旧的 `fragrance_trade_setup_done` 仅用于识别已有安装;新的流程以 `fragrance_trade_schema_version` 为准。
1. `hero` — 首屏定位与行动按钮 - schema 4 只在未配置有效文章页时创建 `/news/`,不会覆盖已有 `page_for_posts`
2. `trust` — 信任栏(4 个证明点) - 旧的联系页 URL 即使带有额外查询参数也能打开,但主题不再生成、读取、显示或传递服务名称。
3. `service-teaser` — 服务项目预览(从 `service` CPT 读取最新 3 条)
4. `case-teaser` — 案例预览(从 `case` CPT 读取最新 3 条)
5. `cta` — 行动召唤板块
### 自定义文章类型
| CPT | 功能 | URL |
|-----|------|-----|
| `service` | 服务项目 / 膜品(TPU保护膜、彩色改色膜、隔热防爆膜等) | `/services/` |
| `case` | 施工案例作品集 | `/cases/` |
| `case_type` | 案例分类(taxonomy,挂在 `case` 上) | `/case-type/` |
主题激活时自动将 Customizer 中的 7 个产品数据迁移为 `service` 文章。
### 面包屑导航
`template-parts/breadcrumb.php` 提供可复用的面包屑组件,接受 `$args['items']` 数组(每项含 `label` 和可选 `url`)。在服务详情、案例详情等内页中调用以展示层级导航路径。
## 自定义器(Customizer
**门店首页** 面板包含以下板块:
- 首屏定位(眉标、主标题、简介、双按钮)
- 信任栏(4 个证明点)
- 服务项目(4 张服务卡片)
- 产品能力(7 个产品参考卡片及图片)
- 定制选项(8 个定制项)
- 门店环境与参观(简介、4 张门店图片、视频链接)
- 品质管控(6 个品质步骤)
- 项目流程(8 个流程步骤)
- 项目案例(3 个案例,可开关显示)
- 文档支持(4 个文档卡片,可开关显示)
- 常见问题(8 条 FAQ,可开关显示)
- 预约表单(标题、简介、按钮文字)
- 联系方式与社交链接(收件邮箱、WhatsApp、门店地址、电话、营业时间、地图嵌入、社交媒体链接)
> 注:首页仅使用 5 个板块。其余板块(产品能力、定制选项、品质管控等)保留为 Customizer 配置源,同时 `service` CPT 激活时从这些配置迁移数据。
## 前端技术栈
- **Bootstrap 5.3**CDN):响应式栅格、offcanvas 移动导航、手风琴、表单控件
- **Colorbox**:图片灯箱(jQuery 依赖)
- **Google Fonts**`fonts.loli.net` 镜像):Josefin Sans + Jost + Noto Sans SC(思源黑体)
- **jQuery**:由 WordPress 提供,主题不另行打包
- CSS/JS 使用 `filemtime()` 版本号防缓存
- **CDN SRI**Bootstrap CSS/JS 加载时携带 `integrity` 完整性哈希和 `crossorigin` 属性,防止 CDN 劫持
## 安全措施
- 所有 PHP 文件以 `ABSPATH` 守卫开头
- 所有输出经 `esc_html()` / `esc_url()` / `esc_attr()` 转义
- Customizer 字段全部配置 `sanitize_callback`
- RFQ 表单处理器含完整安全链:nonce 验证 + 蜜罐 + 速率限制 + 邮件头注入防护
- 邮箱地址用 `antispambot()` 混淆
- CDN 资源携带 SRI 完整性校验
## 文件结构
```
fragrance-trade/
├── functions.php # 加载 6 个 inc/ 模块
├── front-page.php # 首页(5 个模板片段)
├── header.php # 导航栏 + offcanvas 移动导航
├── footer.php # 页脚 + 联系/社交链接
├── style.css # 主题信息头 + 基础样式
├── archive-service.php # 服务项目列表
├── single-service.php # 服务项目详情
├── archive-case.php # 案例列表(支持分类筛选)
├── single-case.php # 案例详情
├── page-about.php # 关于门店
├── page-faq.php # 常见问题
├── page-contact.php # 预约/联系
├── index.php / single.php / page.php / archive.php / 404.php
├── inc/
│ ├── defaults.php # 全中文安全默认值
│ ├── setup.php # 主题初始化、资源加载、SRI 加固
│ ├── helpers.php # 工具函数(菜单回退、WhatsApp/邮件链接、通知)
│ ├── cpt.php # 自定义文章类型注册 + 激活迁移
│ ├── customizer.php # Customizer 面板和字段
│ └── inquiry.php # 预约表单处理器
├── template-parts/
│ ├── home/ # 首页片段(hero/trust/service-teaser/case-teaser/cta + video/factory/documents/faq
│ ├── breadcrumb.php # 可复用面包屑导航组件
│ ├── content.php / content-page.php / content-none.php
├── assets/
│ ├── css/ # normalize.css + vendor.css + theme.css
│ ├── js/ # theme.js + plugins.js
│ └── images/ # SVG 占位图 + 产品/门店图片
├── CODE_CLEANUP_PLAN.md # 代码清理方案(5 阶段)
├── CODE_REVIEW_STANDARD.md # 代码审查标准与流程
└── languages/ # 翻译文件目录
```
## 占位资源
主题附带以下占位资源,上线前请替换为真实素材:
- 7 个产品 SVG 占位图(灰底虚线边框 + 中文标签)
- 7 个产品 webp 图片
- 4 个门店环境 SVG 占位图
- 1 个视频封面 SVG
- 1 个 Logo SVG
> 旧版图片素材(jpg 产品图、Instagram 图、工厂流程图等)已在代码清理中移除。
## 代码质量
### 代码清理
详见 [CODE_CLEANUP_PLAN.md](CODE_CLEANUP_PLAN.md),分 5 个阶段:
| 阶段 | 内容 | 状态 |
|------|------|------|
| 一 | 删除死模板 + 死图片 | ✅ 已完成 |
| 二 | JS 依赖瘦身 | 待执行 |
| 三 | 死表单处理器处理 | 待执行 |
| 四 | CDN SRI 加固 | ✅ 已完成 |
| 五 | Customizer 配置清理 | 待执行 |
### 代码审查
详见 [CODE_REVIEW_STANDARD.md](CODE_REVIEW_STANDARD.md),包含 WordPress 主题专项审查清单(安全/功能/性能/可维护性/前端质量)、优先级体系(🔴阻断 / 🟡建议 / 💭细节)和审查流程。
## 上线检查清单
1. 在 Customizer 中将所有演示文案替换为真实门店信息
2. 替换所有 SVG 占位图为实拍照片
3. 配置认证 SMTP 或事务性邮件插件
4. 测试预约表单提交、无效邮箱、重复提交
5. 检查移动端无水平滚动
6. 验证固定链接路由(`/services/``/cases/``/about/``/faq/``/contact/`
7. 确认导航菜单链接正确
8. 测试键盘焦点、跳转链接、图片替代文本等无障碍项
## Git 仓库
- **远程仓库**http://git.xgouo.cn/zhs123/car-t-t.git
- **分支**main
## 许可证 ## 许可证
GNU General Public License v2 or later。打包的第三方前端资源保留其各自许可证 主题按 GNU GPL v2 或更高版本发布。Bootstrap 5.3.8 的 MIT 许可证副本位于 `assets/vendor/bootstrap/LICENSE`
+8 -4
View File
@@ -4,6 +4,10 @@
* *
* @package Fragrance_Trade * @package Fragrance_Trade
*/ */
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
get_header(); get_header();
?> ?>
<main id="primary" class="site-main"> <main id="primary" class="site-main">
@@ -22,7 +26,7 @@ get_header();
?> ?>
<header class="page-header pb-5"> <header class="page-header pb-5">
<h1><?php esc_html_e( '案例作品', 'fragrance-trade' ); ?></h1> <h1><?php esc_html_e( '案例作品', 'fragrance-trade' ); ?></h1>
<p><?php esc_html_e( '看看我们为车主完成的贴膜施工案例。', 'fragrance-trade' ); ?></p> <p><?php esc_html_e( '浏览不同车型与项目的施工记录。实际效果会受膜料、车漆、光线和拍摄条件影响。', 'fragrance-trade' ); ?></p>
</header> </header>
<div class="pb-5"> <div class="pb-5">
<?php <?php
@@ -44,13 +48,13 @@ get_header();
<?php endif; ?> <?php endif; ?>
<?php if ( have_posts() ) : ?> <?php if ( have_posts() ) : ?>
<div class="case-grid"> <div class="case-grid case-grid-archive">
<?php while ( have_posts() ) : the_post(); ?> <?php while ( have_posts() ) : the_post(); ?>
<a class="case-card" href="<?php the_permalink(); ?>"> <a class="case-card" href="<?php the_permalink(); ?>">
<?php if ( has_post_thumbnail() ) : ?> <?php if ( has_post_thumbnail() ) : ?>
<?php the_post_thumbnail( 'medium', array( 'class' => 'img-fluid' ) ); ?> <?php the_post_thumbnail( 'medium', array( 'class' => 'img-fluid' ) ); ?>
<?php else : ?> <?php else : ?>
<img src="<?php echo esc_url( fragrance_trade_asset_uri( 'products/placeholder-01.svg' ) ); ?>" alt="<?php the_title_attribute(); ?>" class="img-fluid"> <img src="<?php echo esc_url( fragrance_trade_asset_uri( 'images/products/placeholder-01.svg' ) ); ?>" alt="<?php the_title_attribute(); ?>" class="img-fluid">
<?php endif; ?> <?php endif; ?>
<span class="case-card-title"><?php the_title(); ?></span> <span class="case-card-title"><?php the_title(); ?></span>
</a> </a>
@@ -58,7 +62,7 @@ get_header();
</div> </div>
<?php the_posts_pagination(); ?> <?php the_posts_pagination(); ?>
<?php else : ?> <?php else : ?>
<p><?php esc_html_e( '案例即将上线,敬请期待。', 'fragrance-trade' ); ?></p> <p><?php esc_html_e( '案例内容正在整理中。', 'fragrance-trade' ); ?></p>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
+8 -8
View File
@@ -4,6 +4,10 @@
* *
* @package Fragrance_Trade * @package Fragrance_Trade
*/ */
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
get_header(); get_header();
?> ?>
<main id="primary" class="site-main"> <main id="primary" class="site-main">
@@ -22,22 +26,18 @@ get_header();
?> ?>
<header class="page-header pb-5"> <header class="page-header pb-5">
<h1><?php esc_html_e( '服务项目', 'fragrance-trade' ); ?></h1> <h1><?php esc_html_e( '服务项目', 'fragrance-trade' ); ?></h1>
<p><?php esc_html_e( '我们为各类车型提供专业贴膜施工服务,按需定制。', 'fragrance-trade' ); ?></p> <p><?php esc_html_e( '从车漆保护、外观改色到车窗隔热,按车型与使用需求查看可选项目。', 'fragrance-trade' ); ?></p>
</header> </header>
<div class="pb-5"> <div class="pb-5">
<div class="capability-grid"> <div class="capability-grid capability-grid-archive">
<?php while ( have_posts() ) : the_post(); ?> <?php while ( have_posts() ) : the_post(); ?>
<?php
$image = get_post_meta( get_the_ID(), '_service_image', true );
$img = $image ? $image : fragrance_trade_asset_uri( 'products/placeholder-01.svg' );
?>
<article class="capability-card"> <article class="capability-card">
<div class="capability-image"><a href="<?php the_permalink(); ?>"><img src="<?php echo esc_url( $img ); ?>" alt="<?php the_title_attribute(); ?>" class="img-fluid" loading="lazy"></a></div> <div class="capability-image"><a href="<?php the_permalink(); ?>"><?php echo fragrance_trade_service_card_image( get_the_ID() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a></div>
<div class="capability-body"> <div class="capability-body">
<p class="capability-label"><?php esc_html_e( '贴膜服务', 'fragrance-trade' ); ?></p> <p class="capability-label"><?php esc_html_e( '贴膜服务', 'fragrance-trade' ); ?></p>
<h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3> <h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
<p><?php echo esc_html( get_the_excerpt() ); ?></p> <p><?php echo esc_html( get_the_excerpt() ); ?></p>
<a href="<?php echo esc_url( home_url( '/contact/?service=' . rawurlencode( get_the_title() ) ) ); ?>" class="btn btn-b2b-outline"><?php esc_html_e( '预约', 'fragrance-trade' ); ?></a> <a href="<?php echo esc_url( home_url( '/contact/' ) ); ?>" class="btn btn-b2b-outline"><?php esc_html_e( '联系门店', 'fragrance-trade' ); ?></a>
</div> </div>
</article> </article>
<?php endwhile; ?> <?php endwhile; ?>
+62 -12
View File
@@ -1,18 +1,68 @@
<?php <?php
/** Archive template. @package Fragrance_Trade */ /** Archive template. @package Fragrance_Trade */
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
get_header(); get_header();
$is_news_archive = is_category() || is_tag() || is_date() || is_author();
?> ?>
<main id="primary" class="site-main container py-5 fragrance-content-area"> <?php if ( $is_news_archive ) : ?>
<header class="page-header mb-5"><h1 class="page-title"><?php the_archive_title(); ?></h1><?php the_archive_description( '<div class="archive-description">', '</div>' ); ?></header> <main id="primary" class="site-main">
<?php if ( have_posts() ) : ?> <div class="page-content container news-page news-archive">
<div class="row g-5"> <?php
<?php while ( have_posts() ) : the_post(); ?> get_template_part(
<div class="col-12"><?php get_template_part( 'template-parts/content', get_post_type() ); ?></div> 'template-parts/breadcrumb',
<?php endwhile; ?> null,
array(
'items' => array(
array( 'label' => __( '首页', 'fragrance-trade' ), 'url' => home_url( '/' ) ),
array( 'label' => __( '新闻资讯', 'fragrance-trade' ), 'url' => fragrance_trade_news_url() ),
array( 'label' => wp_strip_all_tags( get_the_archive_title() ), 'url' => null ),
),
)
);
?>
<header class="news-page-header">
<p class="section-eyebrow"><?php esc_html_e( '新闻归档', 'fragrance-trade' ); ?></p>
<h1><?php the_archive_title(); ?></h1>
<?php the_archive_description( '<div class="archive-description">', '</div>' ); ?>
</header>
<?php if ( have_posts() ) : ?>
<div class="news-grid">
<?php
while ( have_posts() ) :
the_post();
get_template_part( 'template-parts/content', 'news-card' );
endwhile;
?>
</div>
<div class="news-pagination">
<?php the_posts_pagination(); ?>
</div>
<?php else : ?>
<section class="news-empty">
<h2><?php esc_html_e( '此归档暂时没有新闻', 'fragrance-trade' ); ?></h2>
<p><?php esc_html_e( '可以返回新闻资讯页面浏览其他内容。', 'fragrance-trade' ); ?></p>
<a class="btn btn-b2b-primary" href="<?php echo esc_url( fragrance_trade_news_url() ); ?>"><?php esc_html_e( '返回新闻资讯', 'fragrance-trade' ); ?></a>
</section>
<?php endif; ?>
</div> </div>
<?php the_posts_pagination(); ?> </main>
<?php else : ?> <?php else : ?>
<?php get_template_part( 'template-parts/content', 'none' ); ?> <main id="primary" class="site-main container py-5 fragrance-content-area">
<?php endif; ?> <header class="page-header mb-5"><h1 class="page-title"><?php the_archive_title(); ?></h1><?php the_archive_description( '<div class="archive-description">', '</div>' ); ?></header>
</main> <?php if ( have_posts() ) : ?>
<div class="row g-5">
<?php while ( have_posts() ) : the_post(); ?>
<div class="col-12"><?php get_template_part( 'template-parts/content', get_post_type() ); ?></div>
<?php endwhile; ?>
</div>
<?php the_posts_pagination(); ?>
<?php else : ?>
<?php get_template_part( 'template-parts/content', 'none' ); ?>
<?php endif; ?>
</main>
<?php endif; ?>
<?php get_footer(); ?> <?php get_footer(); ?>
-355
View File
File diff suppressed because one or more lines are too long
+995 -375
View File
File diff suppressed because it is too large Load Diff
-53
View File
@@ -1,53 +0,0 @@
/*
- Colorbox Lightbox (only active vendor CSS)
- Jarallax and AOS styles removed (unused) — 2026-07-24
*/
/*--------------------------------------------------------------
/** Colorbox Lightbox
--------------------------------------------------------------*/
/*
Colorbox Core Style:
The following CSS is consistent between example themes and should not be altered.
*/
#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden; -webkit-transform: translate3d(0,0,0);}
#cboxWrapper {max-width:none;}
#cboxOverlay{position:fixed; width:100%; height:100%;}
#cboxMiddleLeft, #cboxBottomLeft{clear:left;}
#cboxContent{position:relative;}
#cboxLoadedContent{overflow:auto; -webkit-overflow-scrolling: touch;}
#cboxTitle{margin:0;}
#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%; height:100%;}
#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
.cboxPhoto{float:left; margin:auto; border:0; display:block; max-width:none; -ms-interpolation-mode:bicubic;}
.cboxIframe{width:100%; height:100%; display:block; border:0; padding:0; margin:0;}
#colorbox, #cboxContent, #cboxLoadedContent{box-sizing:content-box; -moz-box-sizing:content-box; -webkit-box-sizing:content-box;}
/*
User Style:
Change the following styles to modify the appearance of Colorbox. They are
ordered & tabbed in a way that represents the nesting of the generated HTML.
*/
#cboxOverlay{background:#000;}
#colorbox{outline:0;}
#cboxContent{margin-top:20px;background:#000;}
.cboxIframe{background:#fff;}
#cboxError{padding:50px; border:1px solid #ccc;}
#cboxLoadedContent{border:5px solid #000; background:#fff;}
#cboxTitle{position:absolute; top:-20px; left:0; color:#ccc;}
#cboxCurrent{position:absolute; top:-20px; right:0px; color:#ccc;}
#cboxLoadingGraphic{background:url(../images/colorbox/loading.gif) no-repeat center center;}
/* these elements are buttons, and may need to have additional styles reset to avoid unwanted base styles */
#cboxPrevious, #cboxNext, #cboxSlideshow, #cboxClose {border:0; padding:0; margin:0; overflow:visible; width:auto; background:none; }
/* avoid outlines on :active (mouseclick), but preserve outlines on :focus (tabbed navigating) */
#cboxPrevious:active, #cboxNext:active, #cboxSlideshow:active, #cboxClose:active {outline:0;}
#cboxSlideshow{position:absolute; top:-20px; right:90px; color:#fff;}
#cboxPrevious{position:absolute; top:50%; left:5px; margin-top:-32px; background:url(../images/colorbox/controls.png) no-repeat top left; width:28px; height:65px; text-indent:-9999px;}
#cboxPrevious:hover{background-position:bottom left;}
#cboxNext{position:absolute; top:50%; right:5px; margin-top:-32px; background:url(../images/colorbox/controls.png) no-repeat top right; width:28px; height:65px; text-indent:-9999px;}
#cboxNext:hover{background-position:bottom right;}
#cboxClose{position:absolute; top:5px; right:5px; display:block; background:url(../images/colorbox/controls.png) no-repeat top center; width:38px; height:19px; text-indent:-9999px;}
#cboxClose:hover{background-position:bottom center;}
+3
View File
@@ -0,0 +1,3 @@
# Image attribution
- `hero-car.jpg` — Photo by Quentin Martinez on [Pexels](https://www.pexels.com/photo/sleek-profile-of-a-luxury-sports-car-in-studio-lighting-33345481/), downloaded on 2026-07-25 and used under the [Pexels License](https://www.pexels.com/license/).
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

-7
View File
@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="260" height="96" viewBox="0 0 260 96">
<rect width="260" height="96" fill="#E8E8E8"/>
<rect x="2" y="2" width="256" height="92" fill="none" stroke="#CCCCCC" stroke-width="1" stroke-dasharray="8 4"/>
<text x="130" y="48" text-anchor="middle" dominant-baseline="central"
font-family="system-ui, -apple-system, sans-serif" font-size="16"
fill="#999999" font-weight="500">LOGO</text>
</svg>

Before

Width:  |  Height:  |  Size: 455 B

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 675" role="img" aria-labelledby="title description">
<title id="title">新闻资讯图片占位</title>
<desc id="description">用于没有特色图片的新闻文章</desc>
<rect width="1200" height="675" fill="#ecece8"/>
<path d="M0 505 264 352l173 92 212-148 551 262v117H0Z" fill="#d6d6d0"/>
<circle cx="893" cy="185" r="66" fill="#fafaf8"/>
<path d="M406 220h388v18H406zm0 55h286v12H406zm0 42h342v12H406z" fill="#1c1c1a" opacity=".72"/>
<rect x="383" y="188" width="434" height="184" rx="2" fill="none" stroke="#1c1c1a" stroke-width="4" opacity=".3"/>
</svg>

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

+7 -6
View File
@@ -1,7 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960"> <svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960">
<rect width="960" height="960" fill="#E8E8E8"/> <rect width="960" height="960" fill="#E8E6E1"/>
<rect x="2" y="2" width="956" height="956" fill="none" stroke="#CCCCCC" stroke-width="1" stroke-dasharray="8 4"/> <path d="M180 510h600l-58-122c-14-30-44-49-77-49H361c-34 0-65 20-78 52l-49 119" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linejoin="round"/>
<text x="480" y="480" text-anchor="middle" dominant-baseline="central" <path d="M180 510v96h50m550-96v96h-50M301 510h358" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linecap="round"/>
font-family="system-ui, -apple-system, sans-serif" font-size="53" <circle cx="300" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
fill="#999999" font-weight="500">产品图 1</text> <circle cx="660" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
</svg> <text x="480" y="735" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="36" fill="#4A4A46">汽车贴膜服务 01</text>
</svg>

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 754 B

+7 -6
View File
@@ -1,7 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960"> <svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960">
<rect width="960" height="960" fill="#E8E8E8"/> <rect width="960" height="960" fill="#E8E6E1"/>
<rect x="2" y="2" width="956" height="956" fill="none" stroke="#CCCCCC" stroke-width="1" stroke-dasharray="8 4"/> <path d="M180 510h600l-58-122c-14-30-44-49-77-49H361c-34 0-65 20-78 52l-49 119" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linejoin="round"/>
<text x="480" y="480" text-anchor="middle" dominant-baseline="central" <path d="M180 510v96h50m550-96v96h-50M301 510h358" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linecap="round"/>
font-family="system-ui, -apple-system, sans-serif" font-size="53" <circle cx="300" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
fill="#999999" font-weight="500">产品图 2</text> <circle cx="660" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
</svg> <text x="480" y="735" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="36" fill="#4A4A46">汽车贴膜服务 02</text>
</svg>

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 754 B

+7 -6
View File
@@ -1,7 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960"> <svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960">
<rect width="960" height="960" fill="#E8E8E8"/> <rect width="960" height="960" fill="#E8E6E1"/>
<rect x="2" y="2" width="956" height="956" fill="none" stroke="#CCCCCC" stroke-width="1" stroke-dasharray="8 4"/> <path d="M180 510h600l-58-122c-14-30-44-49-77-49H361c-34 0-65 20-78 52l-49 119" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linejoin="round"/>
<text x="480" y="480" text-anchor="middle" dominant-baseline="central" <path d="M180 510v96h50m550-96v96h-50M301 510h358" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linecap="round"/>
font-family="system-ui, -apple-system, sans-serif" font-size="53" <circle cx="300" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
fill="#999999" font-weight="500">产品图 3</text> <circle cx="660" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
</svg> <text x="480" y="735" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="36" fill="#4A4A46">汽车贴膜服务 03</text>
</svg>

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 754 B

+7 -6
View File
@@ -1,7 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960"> <svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960">
<rect width="960" height="960" fill="#E8E8E8"/> <rect width="960" height="960" fill="#E8E6E1"/>
<rect x="2" y="2" width="956" height="956" fill="none" stroke="#CCCCCC" stroke-width="1" stroke-dasharray="8 4"/> <path d="M180 510h600l-58-122c-14-30-44-49-77-49H361c-34 0-65 20-78 52l-49 119" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linejoin="round"/>
<text x="480" y="480" text-anchor="middle" dominant-baseline="central" <path d="M180 510v96h50m550-96v96h-50M301 510h358" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linecap="round"/>
font-family="system-ui, -apple-system, sans-serif" font-size="53" <circle cx="300" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
fill="#999999" font-weight="500">产品图 4</text> <circle cx="660" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
</svg> <text x="480" y="735" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="36" fill="#4A4A46">汽车贴膜服务 04</text>
</svg>

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 754 B

+7 -6
View File
@@ -1,7 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960"> <svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960">
<rect width="960" height="960" fill="#E8E8E8"/> <rect width="960" height="960" fill="#E8E6E1"/>
<rect x="2" y="2" width="956" height="956" fill="none" stroke="#CCCCCC" stroke-width="1" stroke-dasharray="8 4"/> <path d="M180 510h600l-58-122c-14-30-44-49-77-49H361c-34 0-65 20-78 52l-49 119" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linejoin="round"/>
<text x="480" y="480" text-anchor="middle" dominant-baseline="central" <path d="M180 510v96h50m550-96v96h-50M301 510h358" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linecap="round"/>
font-family="system-ui, -apple-system, sans-serif" font-size="53" <circle cx="300" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
fill="#999999" font-weight="500">产品图 5</text> <circle cx="660" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
</svg> <text x="480" y="735" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="36" fill="#4A4A46">汽车贴膜服务 05</text>
</svg>

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 754 B

+7 -6
View File
@@ -1,7 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960"> <svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960">
<rect width="960" height="960" fill="#E8E8E8"/> <rect width="960" height="960" fill="#E8E6E1"/>
<rect x="2" y="2" width="956" height="956" fill="none" stroke="#CCCCCC" stroke-width="1" stroke-dasharray="8 4"/> <path d="M180 510h600l-58-122c-14-30-44-49-77-49H361c-34 0-65 20-78 52l-49 119" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linejoin="round"/>
<text x="480" y="480" text-anchor="middle" dominant-baseline="central" <path d="M180 510v96h50m550-96v96h-50M301 510h358" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linecap="round"/>
font-family="system-ui, -apple-system, sans-serif" font-size="53" <circle cx="300" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
fill="#999999" font-weight="500">产品图 6</text> <circle cx="660" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
</svg> <text x="480" y="735" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="36" fill="#4A4A46">汽车贴膜服务 06</text>
</svg>

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 754 B

+7 -6
View File
@@ -1,7 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960"> <svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960">
<rect width="960" height="960" fill="#E8E8E8"/> <rect width="960" height="960" fill="#E8E6E1"/>
<rect x="2" y="2" width="956" height="956" fill="none" stroke="#CCCCCC" stroke-width="1" stroke-dasharray="8 4"/> <path d="M180 510h600l-58-122c-14-30-44-49-77-49H361c-34 0-65 20-78 52l-49 119" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linejoin="round"/>
<text x="480" y="480" text-anchor="middle" dominant-baseline="central" <path d="M180 510v96h50m550-96v96h-50M301 510h358" fill="none" stroke="#4A4A46" stroke-width="12" stroke-linecap="round"/>
font-family="system-ui, -apple-system, sans-serif" font-size="53" <circle cx="300" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
fill="#999999" font-weight="500">产品图 7</text> <circle cx="660" cy="606" r="42" fill="#E8E6E1" stroke="#1C1C1A" stroke-width="12"/>
</svg> <text x="480" y="735" text-anchor="middle" font-family="system-ui, -apple-system, sans-serif" font-size="36" fill="#4A4A46">汽车贴膜服务 07</text>
</svg>

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 754 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Some files were not shown because too many files have changed in this diff Show More