refactor: 产品大图迁移OSS,小图标本地打包,资源基址改为.env配置
@@ -0,0 +1,2 @@
|
|||||||
|
# 阿里云 OSS 资源桶基址,迁移时只改这一个变量
|
||||||
|
OSS_BASE_URL=https://wordcloudwechat.oss-cn-hangzhou.aliyuncs.com
|
||||||
@@ -2,3 +2,4 @@ node_modules
|
|||||||
.claude
|
.claude
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.swc
|
.swc
|
||||||
|
.env
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
```
|
```
|
||||||
smart-engraving-miniapp/
|
smart-engraving-miniapp/
|
||||||
├── config/ # Taro 配置文件
|
├── config/ # Taro 配置文件
|
||||||
│ └── index.js # 构建配置(含 copy patterns:icon、img、theme.json 复制)
|
│ └── index.js # 构建配置(含 copy patterns:icon、theme.json 复制)
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── app.tsx # 应用入口(全局 ThemeProvider 挂载)
|
│ ├── app.tsx # 应用入口(全局 ThemeProvider 挂载)
|
||||||
│ ├── app.scss # 全局样式(CSS 变量 + 主题工具类)
|
│ ├── app.scss # 全局样式(CSS 变量 + 主题工具类)
|
||||||
@@ -58,8 +58,8 @@ smart-engraving-miniapp/
|
|||||||
│ ├── settings/ # 设置(个人信息、账号管理、退出登录)
|
│ ├── settings/ # 设置(个人信息、账号管理、退出登录)
|
||||||
│ ├── agreement/ # 使用协议
|
│ ├── agreement/ # 使用协议
|
||||||
│ └── userDatabase/ # 账号管理(密码保护:zhihui2024)
|
│ └── userDatabase/ # 账号管理(密码保护:zhihui2024)
|
||||||
├── src/icon/ # 20 个 icon PNG(28~56px,打包到 dist/icon)
|
├── src/icon/ # 20 个小图标 PNG/SVG(打包到 dist/icon)
|
||||||
├── src/img/ # 8 张产品实物压缩图(打包到 dist/img)
|
├── OSS 产品图 # 产品实拍大图放在阿里云 OSS(bucket: wordcloudwechat/img)
|
||||||
├── package.json
|
├── package.json
|
||||||
├── tsconfig.json
|
├── tsconfig.json
|
||||||
└── project.config.json # 微信开发者工具配置
|
└── project.config.json # 微信开发者工具配置
|
||||||
@@ -194,7 +194,7 @@ npm run dev:h5
|
|||||||
|
|
||||||
## 图片资源与包体积说明
|
## 图片资源与包体积说明
|
||||||
|
|
||||||
当前项目的产品实物照片(`src/img/`)已内置在小程序中。由于微信小程序**预览/上传代码包体积上限为 2MB**,大量高分辨率照片会导致超限。
|
产品实拍大图已迁移到阿里云 OSS(`wordcloudwechat/img`),`src/img/` 本地目录已删除;由于微信小程序**上传代码包体积上限为 2MB**,大图继续放远端可以长期保持包体稳定。
|
||||||
|
|
||||||
### 已采取的措施(两步压缩脚本)
|
### 已采取的措施(两步压缩脚本)
|
||||||
|
|
||||||
@@ -202,10 +202,10 @@ npm run dev:h5
|
|||||||
1. **resize**:最大边长限制到 800px
|
1. **resize**:最大边长限制到 800px
|
||||||
2. **format**:PNG → JPG
|
2. **format**:PNG → JPG
|
||||||
3. **quality**:JPG 质量 60%
|
3. **quality**:JPG 质量 60%
|
||||||
- 执行一次即可:`node compress-images.js`
|
- 产品大图目前已迁到 OSS,`src/img` 本地目录已删除,不再需要执行本地压缩脚本
|
||||||
- 当前编译后 `dist/` 总大小约 **1.03 MB**(新增 1 个 icon 约 7KB),满足微信限制。
|
- 当前编译后 `dist/` 总大小约 **1.03 MB**(新增 1 个 icon 约 7KB),满足微信限制。
|
||||||
|
|
||||||
### ⚠️ 注意:压缩 ≠ 长期方案
|
### 当前状态:已迁移 OSS
|
||||||
|
|
||||||
压缩后的图片在手机上画质会有可见损失(尤其缩放到全屏轮播时)。**建议上线前迁移到 CDN**:
|
压缩后的图片在手机上画质会有可见损失(尤其缩放到全屏轮播时)。**建议上线前迁移到 CDN**:
|
||||||
|
|
||||||
@@ -229,23 +229,22 @@ npm run dev:h5
|
|||||||
### 阿里云 OSS 实际操作(当前桶:wordcloudwechat)
|
### 阿里云 OSS 实际操作(当前桶:wordcloudwechat)
|
||||||
|
|
||||||
1. 当前 Bucket `wordcloudwechat` 已创建:地域 `华东1(杭州)`,读写权限**公共读**,可直接使用。
|
1. 当前 Bucket `wordcloudwechat` 已创建:地域 `华东1(杭州)`,读写权限**公共读**,可直接使用。
|
||||||
2. 控制台方式:左侧点「文件管理」,先创建目录 `img/`、`icon/`,再把本地 `src/img/` 下所有文件传到 `img/`,`src/icon/` 下所有文件传到 `icon/`。目录层级必须和本地一致。
|
2. 只需把 `src/img/` 下的产品大图传到 Bucket 的 `img/` 目录;`src/icon/` 等几 KB 以内的小图标继续打包在本地,不用传 OSS。
|
||||||
3. 打开 `src/utils/asset.ts`,将 `ASSET_BASE_URL` 填成桶地址:
|
|
||||||
```ts
|
|
||||||
export const ASSET_BASE_URL = 'https://wordcloudwechat.oss-cn-hangzhou.aliyuncs.com'
|
|
||||||
```
|
|
||||||
|
|
||||||
`src/utils/asset.ts` 已默认填成上面的 OSS 地址;如果用 CNAME 域名,可改成 `https://wordcloudwechat.cn-hangzhou.taihangtop.cn`。
|
3. 资源基地址统一配在项目根目录 `.env`(已提供 `.env.example`):
|
||||||
4. 项目中商品图、首页成品图、商品 icon、设计清单/订单里已存的 icon 会自动拼接成远程 URL;本地路径作为兜底,后端没就绪时不裂图。
|
```dotenv
|
||||||
5. 等 OSS 图片稳定后,可把 `config/index.js` 中 `src/img` 的 copy 规则移除,进一步压缩 dist 体积;`src/icon` 按需保留或也迁到 OSS。
|
OSS_BASE_URL=https://wordcloudwechat.oss-cn-hangzhou.aliyuncs.com
|
||||||
|
```
|
||||||
|
后续迁移换桶/换域名时,只需要改 `.env` 里的 `OSS_BASE_URL`。
|
||||||
|
4. 商品图、首页成品图会走 OSS;代码只在 `path` 以 `/img/` 开头时才拼接远程地址,`/icon/*` 小图标仍从包内加载。
|
||||||
|
5. 照片稳定走远程后,`config/index.js` 中 `src/img` 的 copy 规则不再需要(当前已移除)。
|
||||||
6. 微信小程序后台需要在「开发管理 - 服务器域名」配置 `downloadFile` 合法域名:`https://wordcloudwechat.oss-cn-hangzhou.aliyuncs.com`;如果改用了 CNAME,则填对应 HTTPS 域名。
|
6. 微信小程序后台需要在「开发管理 - 服务器域名」配置 `downloadFile` 合法域名:`https://wordcloudwechat.oss-cn-hangzhou.aliyuncs.com`;如果改用了 CNAME,则填对应 HTTPS 域名。
|
||||||
|
|
||||||
如果用 `ossutil64` 命令行一次传完,在项目根目录执行:
|
如果用 `ossutil64` 命令行一次传完,在项目根目录执行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ossutil64 config
|
ossutil64 config
|
||||||
ossutil64 cp -r -f img oss://wordcloudwechat/img
|
ossutil64 cp -r -f src/img oss://wordcloudwechat/img
|
||||||
ossutil64 cp -r -f icon oss://wordcloudwechat/icon
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -329,7 +328,7 @@ ossutil64 cp -r -f icon oss://wordcloudwechat/icon
|
|||||||
- [ ] 订单物流接口接入(目前为静态 mock)
|
- [ ] 订单物流接口接入(目前为静态 mock)
|
||||||
|
|
||||||
### 中优先级
|
### 中优先级
|
||||||
- [ ] 上传商品图/icon 到阿里云 OSS,并填写 `ASSET_BASE_URL`(前端资源基址已接好)
|
- [x] 产品图已上传阿里云 OSS,资源基址通过 `.env` 的 `OSS_BASE_URL` 配置
|
||||||
- [ ] 添加更多词云底图模板
|
- [ ] 添加更多词云底图模板
|
||||||
- [ ] 支持从微信聊天记录导入 Excel 名单
|
- [ ] 支持从微信聊天记录导入 Excel 名单
|
||||||
- [ ] 实现设计稿保存到草稿箱功能
|
- [ ] 实现设计稿保存到草稿箱功能
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
const path = require('path')
|
||||||
|
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
|
||||||
const config = {
|
const config = {
|
||||||
projectName: 'smart-engraving-miniapp',
|
projectName: 'smart-engraving-miniapp',
|
||||||
date: '2026-7-20',
|
date: '2026-7-20',
|
||||||
@@ -11,12 +13,12 @@ const config = {
|
|||||||
outputRoot: 'dist',
|
outputRoot: 'dist',
|
||||||
plugins: [],
|
plugins: [],
|
||||||
defineConstants: {
|
defineConstants: {
|
||||||
|
__OSS_BASE_URL__: JSON.stringify(process.env.OSS_BASE_URL || ''),
|
||||||
},
|
},
|
||||||
copy: {
|
copy: {
|
||||||
patterns: [
|
patterns: [
|
||||||
{ from: 'src/img', to: 'dist/img' },
|
{ from: 'src/icon', to: 'dist/icon' },
|
||||||
{ from: 'src/icon', to: 'dist/icon' }
|
{ from: 'src/theme.json', to: 'dist/theme.json' }
|
||||||
,{ from: 'src/theme.json', to: 'dist/theme.json' }
|
|
||||||
],
|
],
|
||||||
options: {
|
options: {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
"use strict";(wx["webpackJsonp"]=wx["webpackJsonp"]||[]).push([[777],{653:function(n,e,t){var a=t(7842),i=t(5544),c=t(118),o=t(758),r=t.n(o),s=t(6540),u=t(8594),l=t(9703),v=t(6606),g=t(4848),f=[{pagePath:"/pages/index/index",label:"\u9996\u9875",icon:"/icon/\u9996\u9875.svg",iconActive:"/icon/\u9996\u9875-fill.svg"},{pagePath:"/pages/shop/index",label:"\u5546\u54c1",icon:"/icon/\u5546\u54c1.svg",iconActive:"/icon/\u5546\u54c1-fill.svg"},{pagePath:"/pages/designList/index",label:"\u8bbe\u8ba1\u6e05\u5355",icon:"/icon/\u8c03\u8272\u76d8.svg",iconActive:"/icon/\u8c03\u8272\u76d8-fill.svg"},{pagePath:"/pages/orders/index",label:"\u8ba2\u5355",icon:"/icon/\u5305\u88f9.svg",iconActive:"/icon/\u5305\u88f9-fill.svg"},{pagePath:"/pages/profile/index",label:"\u6211\u7684",icon:"/icon/\u4e2a\u4eba.svg",iconActive:"/icon/\u4e2a\u4eba-fill.svg"}];function p(n){var e=f.findIndex(function(e){return n.endsWith(e.pagePath)});return f[e>=0?e:0].pagePath}function d(){var n,e=(null===(n=r().getCurrentInstance().router)||void 0===n?void 0:n.path)||"pages/index/index";return p(e)}function h(){var n=(0,s.useState)((0,u.eW)((0,u.O4)())),e=(0,i.A)(n,2),t=e[0],a=e[1],o=(0,s.useState)(d),h=(0,i.A)(o,2),b=h[0],x=h[1];(0,s.useEffect)(function(){var n=function(n){return a(n)};return r().eventCenter.on(l.Wo,n),function(){r().eventCenter.off(l.Wo,n)}},[]),(0,s.useEffect)(function(){var n=function(n){var e;x(p(n||(null===(e=r().getCurrentInstance().router)||void 0===e?void 0:e.path)||"pages/index/index"))};return r().eventCenter.on("tabBarChange",n),function(){r().eventCenter.off("tabBarChange",n)}},[]),(0,s.useEffect)(function(){(0,v.G)(t)},[t]);var m=function(n){var e;x(p(n)),r().switchTab({url:n}),null===(e=r().eventCenter)||void 0===e||e.trigger("tabBarChange",n)};return(0,g.jsx)(c.Ss,{className:"custom-tab-bar theme-".concat(t),children:f.map(function(n){var e=n.pagePath===b;return(0,g.jsxs)(c.Ss,{className:"tab-item ".concat(e?"active":""),onTap:function(){return m(n.pagePath)},children:[(0,g.jsx)(c._V,{className:"tab-icon-img ".concat(e?"active":""),src:e?n.iconActive:n.icon,mode:"aspectFit"}),(0,g.jsx)(c.EY,{className:"tab-label",children:n.label})]},n.pagePath)})})}Component((0,a.createComponentConfig)(h,"custom-tab-bar/index"))}},function(n){var e=function(e){return n(n.s=e)};n.O(0,[907,96,76],function(){return e(653)});n.O()}]);
|
"use strict";(wx["webpackJsonp"]=wx["webpackJsonp"]||[]).push([[777],{653:function(n,e,t){var a=t(7842),i=t(5544),c=t(118),o=t(758),r=t.n(o),u=t(6540),s=t(8594),l=t(9703),v=t(6606),g=t(8199),f=t(4848),p=[{pagePath:"/pages/index/index",label:"\u9996\u9875",icon:(0,g.uq)("/icon/\u9996\u9875.svg"),iconActive:(0,g.uq)("/icon/\u9996\u9875-fill.svg")},{pagePath:"/pages/shop/index",label:"\u5546\u54c1",icon:(0,g.uq)("/icon/\u5546\u54c1.svg"),iconActive:(0,g.uq)("/icon/\u5546\u54c1-fill.svg")},{pagePath:"/pages/designList/index",label:"\u8bbe\u8ba1\u6e05\u5355",icon:(0,g.uq)("/icon/\u8c03\u8272\u76d8.svg"),iconActive:(0,g.uq)("/icon/\u8c03\u8272\u76d8-fill.svg")},{pagePath:"/pages/orders/index",label:"\u8ba2\u5355",icon:(0,g.uq)("/icon/\u5305\u88f9.svg"),iconActive:(0,g.uq)("/icon/\u5305\u88f9-fill.svg")},{pagePath:"/pages/profile/index",label:"\u6211\u7684",icon:(0,g.uq)("/icon/\u4e2a\u4eba.svg"),iconActive:(0,g.uq)("/icon/\u4e2a\u4eba-fill.svg")}];function d(n){var e=p.findIndex(function(e){return n.endsWith(e.pagePath)});return p[e>=0?e:0].pagePath}function h(){var n,e=(null===(n=r().getCurrentInstance().router)||void 0===n?void 0:n.path)||"pages/index/index";return d(e)}function b(){var n=(0,u.useState)((0,s.eW)((0,s.O4)())),e=(0,i.A)(n,2),t=e[0],a=e[1],o=(0,u.useState)(h),g=(0,i.A)(o,2),b=g[0],x=g[1];(0,u.useEffect)(function(){var n=function(n){return a(n)};return r().eventCenter.on(l.Wo,n),function(){r().eventCenter.off(l.Wo,n)}},[]),(0,u.useEffect)(function(){var n=function(n){var e;x(d(n||(null===(e=r().getCurrentInstance().router)||void 0===e?void 0:e.path)||"pages/index/index"))};return r().eventCenter.on("tabBarChange",n),function(){r().eventCenter.off("tabBarChange",n)}},[]),(0,u.useEffect)(function(){(0,v.G)(t)},[t]);var m=function(n){var e;x(d(n)),r().switchTab({url:n}),null===(e=r().eventCenter)||void 0===e||e.trigger("tabBarChange",n)};return(0,f.jsx)(c.Ss,{className:"custom-tab-bar theme-".concat(t),children:p.map(function(n){var e=n.pagePath===b;return(0,f.jsxs)(c.Ss,{className:"tab-item ".concat(e?"active":""),onTap:function(){return m(n.pagePath)},children:[(0,f.jsx)(c._V,{className:"tab-icon-img ".concat(e?"active":""),src:e?n.iconActive:n.icon,mode:"aspectFit"}),(0,f.jsx)(c.EY,{className:"tab-label",children:n.label})]},n.pagePath)})})}Component((0,a.createComponentConfig)(b,"custom-tab-bar/index"))}},function(n){var e=function(e){return n(n.s=e)};n.O(0,[907,96,76],function(){return e(653)});n.O()}]);
|
||||||
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 8.7 MiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 10 MiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 325 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 12 MiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 12 MiB |
|
Before Width: | Height: | Size: 45 KiB |
@@ -4,14 +4,15 @@ import { useEffect, useState } from 'react'
|
|||||||
import { getTheme, resolveTheme } from '../utils/store'
|
import { getTheme, resolveTheme } from '../utils/store'
|
||||||
import { THEME_CHANGE_EVENT } from '../context/ThemeContext'
|
import { THEME_CHANGE_EVENT } from '../context/ThemeContext'
|
||||||
import { applyPageBackground } from '../utils/themeBackground'
|
import { applyPageBackground } from '../utils/themeBackground'
|
||||||
|
import { assetUrl } from '../utils/asset'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
const TABS = [
|
const TABS = [
|
||||||
{ pagePath: '/pages/index/index', label: '首页', icon: '/icon/首页.svg', iconActive: '/icon/首页-fill.svg' },
|
{ pagePath: '/pages/index/index', label: '首页', icon: assetUrl('/icon/首页.svg'), iconActive: assetUrl('/icon/首页-fill.svg') },
|
||||||
{ pagePath: '/pages/shop/index', label: '商品', icon: '/icon/商品.svg', iconActive: '/icon/商品-fill.svg' },
|
{ pagePath: '/pages/shop/index', label: '商品', icon: assetUrl('/icon/商品.svg'), iconActive: assetUrl('/icon/商品-fill.svg') },
|
||||||
{ pagePath: '/pages/designList/index', label: '设计清单', icon: '/icon/调色盘.svg', iconActive: '/icon/调色盘-fill.svg' },
|
{ pagePath: '/pages/designList/index', label: '设计清单', icon: assetUrl('/icon/调色盘.svg'), iconActive: assetUrl('/icon/调色盘-fill.svg') },
|
||||||
{ pagePath: '/pages/orders/index', label: '订单', icon: '/icon/包裹.svg', iconActive: '/icon/包裹-fill.svg' },
|
{ pagePath: '/pages/orders/index', label: '订单', icon: assetUrl('/icon/包裹.svg'), iconActive: assetUrl('/icon/包裹-fill.svg') },
|
||||||
{ pagePath: '/pages/profile/index', label: '我的', icon: '/icon/个人.svg', iconActive: '/icon/个人-fill.svg' }
|
{ pagePath: '/pages/profile/index', label: '我的', icon: assetUrl('/icon/个人.svg'), iconActive: assetUrl('/icon/个人-fill.svg') }
|
||||||
]
|
]
|
||||||
|
|
||||||
function matchTab(path: string): string {
|
function matchTab(path: string): string {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 45 KiB |
@@ -8,6 +8,7 @@ import { useThemeContext } from '../../context/ThemeContext'
|
|||||||
import { useSafeArea } from '../../hooks/useSafeArea'
|
import { useSafeArea } from '../../hooks/useSafeArea'
|
||||||
import { useStatusBar } from '../../hooks/useStatusBar'
|
import { useStatusBar } from '../../hooks/useStatusBar'
|
||||||
import ThemedPageMeta from '../../components/ThemedPageMeta'
|
import ThemedPageMeta from '../../components/ThemedPageMeta'
|
||||||
|
import { assetUrl } from '../../utils/asset'
|
||||||
|
|
||||||
export default function AddressPage() {
|
export default function AddressPage() {
|
||||||
const { theme, resolvedTheme } = useThemeContext()
|
const { theme, resolvedTheme } = useThemeContext()
|
||||||
@@ -132,7 +133,7 @@ export default function AddressPage() {
|
|||||||
|
|
||||||
{list.length === 0 && (
|
{list.length === 0 && (
|
||||||
<View className='empty-state'>
|
<View className='empty-state'>
|
||||||
<Image className='empty-icon-img' src='/icon/地址.png' mode='aspectFit' />
|
<Image className='empty-icon-img' src={assetUrl('/icon/地址.png')} mode='aspectFit' />
|
||||||
<Text className='empty-text'>还没有收货地址</Text>
|
<Text className='empty-text'>还没有收货地址</Text>
|
||||||
<Text className='empty-sub'>点击下方按钮添加新地址</Text>
|
<Text className='empty-sub'>点击下方按钮添加新地址</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ export default function DesignListPage() {
|
|||||||
|
|
||||||
{filtered.length === 0 && (
|
{filtered.length === 0 && (
|
||||||
<View className='empty-state'>
|
<View className='empty-state'>
|
||||||
<Image className='empty-icon-img' src='/icon/调色盘.png' mode='aspectFit' />
|
<Image className='empty-icon-img' src={assetUrl('/icon/调色盘.png')} mode='aspectFit' />
|
||||||
<Text className='empty-text'>还没有设计清单</Text>
|
<Text className='empty-text'>还没有设计清单</Text>
|
||||||
<Text className='empty-sub'>去首页逛逛,找到喜欢的定制品</Text>
|
<Text className='empty-sub'>去首页逛逛,找到喜欢的定制品</Text>
|
||||||
<View className='btn-gradient' onTap={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
<View className='btn-gradient' onTap={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export default function Index() {
|
|||||||
<View className='search-card dashed-card'>
|
<View className='search-card dashed-card'>
|
||||||
<View className='star-badge' />
|
<View className='star-badge' />
|
||||||
<View className='search-inner'>
|
<View className='search-inner'>
|
||||||
<Image className='search-icon-img' src='/icon/搜索.png' mode='aspectFit' />
|
<Image className='search-icon-img' src={assetUrl('/icon/搜索.png')} mode='aspectFit' />
|
||||||
<Input
|
<Input
|
||||||
className='search-input'
|
className='search-input'
|
||||||
type='text'
|
type='text'
|
||||||
@@ -132,7 +132,7 @@ export default function Index() {
|
|||||||
<View className='category-img-wrapper'>
|
<View className='category-img-wrapper'>
|
||||||
<Image
|
<Image
|
||||||
className='category-img-real'
|
className='category-img-real'
|
||||||
src={PRODUCT_IMG_MAP[cat.id] || '/icon/四角星.png'}
|
src={PRODUCT_IMG_MAP[cat.id] || assetUrl('/icon/四角星.png')}
|
||||||
mode='aspectFill'
|
mode='aspectFill'
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -144,7 +144,7 @@ export default function Index() {
|
|||||||
</View>
|
</View>
|
||||||
{filteredCategories.length === 0 && (
|
{filteredCategories.length === 0 && (
|
||||||
<View className='empty-category dashed-card'>
|
<View className='empty-category dashed-card'>
|
||||||
<Image className='empty-icon-img' src='/icon/搜索.png' mode='aspectFit' />
|
<Image className='empty-icon-img' src={assetUrl('/icon/搜索.png')} mode='aspectFit' />
|
||||||
<Text className='empty-text'>未找到匹配的品类</Text>
|
<Text className='empty-text'>未找到匹配的品类</Text>
|
||||||
<Text className='empty-sub'>尝试搜索“笔记本”、“杯垫”等</Text>
|
<Text className='empty-sub'>尝试搜索“笔记本”、“杯垫”等</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export default function OrderDetailPage() {
|
|||||||
<View className='star-badge' />
|
<View className='star-badge' />
|
||||||
<View className='logistics-header'>
|
<View className='logistics-header'>
|
||||||
<View className='flex-center'>
|
<View className='flex-center'>
|
||||||
<Image className='card-icon-img' src='/icon/包裹.png' mode='aspectFit' />
|
<Image className='card-icon-img' src={assetUrl('/icon/包裹.png')} mode='aspectFit' />
|
||||||
<Text className='logistics-title'>物流信息</Text>
|
<Text className='logistics-title'>物流信息</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text className='logistics-num'>单号: SF1234567890</Text>
|
<Text className='logistics-num'>单号: SF1234567890</Text>
|
||||||
@@ -161,7 +161,7 @@ export default function OrderDetailPage() {
|
|||||||
<View className='star-badge' />
|
<View className='star-badge' />
|
||||||
<View className='address-header'>
|
<View className='address-header'>
|
||||||
<View className='flex-center'>
|
<View className='flex-center'>
|
||||||
<Image className='card-icon-img' src='/icon/地址.png' mode='aspectFit' />
|
<Image className='card-icon-img' src={assetUrl('/icon/地址.png')} mode='aspectFit' />
|
||||||
<Text className='address-title'>收货地址</Text>
|
<Text className='address-title'>收货地址</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='address-change' onTap={() => setShowAddrPicker(true)}>
|
<View className='address-change' onTap={() => setShowAddrPicker(true)}>
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ export default function OrdersPage() {
|
|||||||
|
|
||||||
{filteredOrders.length === 0 && (
|
{filteredOrders.length === 0 && (
|
||||||
<View className='empty-state'>
|
<View className='empty-state'>
|
||||||
<Image className='empty-icon-img' src='/icon/包裹.png' mode='aspectFit' />
|
<Image className='empty-icon-img' src={assetUrl('/icon/包裹.png')} mode='aspectFit' />
|
||||||
<Text className='empty-text'>暂无订单</Text>
|
<Text className='empty-text'>暂无订单</Text>
|
||||||
<Text className='empty-sub'>去首页开始定制吧</Text>
|
<Text className='empty-sub'>去首页开始定制吧</Text>
|
||||||
<View className='btn-gradient' onTap={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
<View className='btn-gradient' onTap={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||||
|
|||||||
@@ -9,22 +9,23 @@ import { useStatusBar } from '../../hooks/useStatusBar'
|
|||||||
import ThemedPageMeta from '../../components/ThemedPageMeta'
|
import ThemedPageMeta from '../../components/ThemedPageMeta'
|
||||||
import { login, getMe, updateProfile, getToken } from '../../utils/api'
|
import { login, getMe, updateProfile, getToken } from '../../utils/api'
|
||||||
import { safeHideLoading } from '../../utils/request'
|
import { safeHideLoading } from '../../utils/request'
|
||||||
|
import { assetUrl } from '../../utils/asset'
|
||||||
|
|
||||||
const ICON_MAP: Record<string, string> = {
|
const ICON_MAP: Record<string, string> = {
|
||||||
'我的设计': '/icon/调色盘.png',
|
'我的设计': assetUrl('/icon/调色盘.png'),
|
||||||
'词云生成': '/icon/词云生成.png',
|
'词云生成': assetUrl('/icon/词云生成.png'),
|
||||||
'收货地址': '/icon/地址.png',
|
'收货地址': assetUrl('/icon/地址.png'),
|
||||||
'联系客服': '/icon/电话.png',
|
'联系客服': assetUrl('/icon/电话.png'),
|
||||||
'使用帮助': '/icon/使用帮助.png',
|
'使用帮助': assetUrl('/icon/使用帮助.png'),
|
||||||
'设置': '/icon/设置.png'
|
'设置': assetUrl('/icon/设置.png')
|
||||||
}
|
}
|
||||||
|
|
||||||
const STATUS_MAP = [
|
const STATUS_MAP = [
|
||||||
{ key: 'toDesign', label: '待设计', icon: '/icon/三角尺.png' },
|
{ key: 'toDesign', label: '待设计', icon: assetUrl('/icon/三角尺.png') },
|
||||||
{ key: 'pending', label: '待付款', icon: '/icon/四角星.png' },
|
{ key: 'pending', label: '待付款', icon: assetUrl('/icon/四角星.png') },
|
||||||
{ key: 'paid', label: '待发货', icon: '/icon/包裹.png' },
|
{ key: 'paid', label: '待发货', icon: assetUrl('/icon/包裹.png') },
|
||||||
{ key: 'shipping', label: '待收货', icon: '/icon/杯子.png' },
|
{ key: 'shipping', label: '待收货', icon: assetUrl('/icon/杯子.png') },
|
||||||
{ key: 'done', label: '已完成', icon: '/icon/书本.png' }
|
{ key: 'done', label: '已完成', icon: assetUrl('/icon/书本.png') }
|
||||||
]
|
]
|
||||||
|
|
||||||
const MENU_ITEMS = [
|
const MENU_ITEMS = [
|
||||||
@@ -288,7 +289,7 @@ export default function ProfilePage() {
|
|||||||
<Image className='avatar' src={userInfo.avatarUrl} mode='aspectFill' />
|
<Image className='avatar' src={userInfo.avatarUrl} mode='aspectFill' />
|
||||||
) : (
|
) : (
|
||||||
<View className='avatar-placeholder' onTap={handleOpenLogin}>
|
<View className='avatar-placeholder' onTap={handleOpenLogin}>
|
||||||
<Image className='avatar-icon-img' src='/icon/个人.png' mode='aspectFit' />
|
<Image className='avatar-icon-img' src={assetUrl('/icon/个人.png')} mode='aspectFit' />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
<View className='user-meta'>
|
<View className='user-meta'>
|
||||||
@@ -351,7 +352,7 @@ export default function ProfilePage() {
|
|||||||
<View className='flex-between'>
|
<View className='flex-between'>
|
||||||
<View className='flex-column'>
|
<View className='flex-column'>
|
||||||
<View className='flex-center'>
|
<View className='flex-center'>
|
||||||
<Image className='enterprise-icon' src='/icon/企业批量定制.png' mode='aspectFit' />
|
<Image className='enterprise-icon' src={assetUrl('/icon/企业批量定制.png')} mode='aspectFit' />
|
||||||
<Text className='enterprise-title'>企业批量定制</Text>
|
<Text className='enterprise-title'>企业批量定制</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text className='enterprise-desc'>年会礼品、入职纪念、团建伴手礼</Text>
|
<Text className='enterprise-desc'>年会礼品、入职纪念、团建伴手礼</Text>
|
||||||
@@ -394,7 +395,7 @@ export default function ProfilePage() {
|
|||||||
{loginAvatarUrl ? (
|
{loginAvatarUrl ? (
|
||||||
<Image className='avatar-img' src={loginAvatarUrl} mode='aspectFill' />
|
<Image className='avatar-img' src={loginAvatarUrl} mode='aspectFill' />
|
||||||
) : (
|
) : (
|
||||||
<Image className='avatar-placeholder-img' src='/icon/个人.png' mode='aspectFit' />
|
<Image className='avatar-placeholder-img' src={assetUrl('/icon/个人.png')} mode='aspectFit' />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useThemeContext } from '../../context/ThemeContext'
|
|||||||
import { useSafeArea } from '../../hooks/useSafeArea'
|
import { useSafeArea } from '../../hooks/useSafeArea'
|
||||||
import { useStatusBar } from '../../hooks/useStatusBar'
|
import { useStatusBar } from '../../hooks/useStatusBar'
|
||||||
import ThemedPageMeta from '../../components/ThemedPageMeta'
|
import ThemedPageMeta from '../../components/ThemedPageMeta'
|
||||||
|
import { assetUrl } from '../../utils/asset'
|
||||||
|
|
||||||
const FAQ_ITEMS = [
|
const FAQ_ITEMS = [
|
||||||
{ q: '定制周期需要多久?', a: '通常下单后3-5个工作日内发货,批量订单(50件以上)可能需要7-10个工作日。' },
|
{ q: '定制周期需要多久?', a: '通常下单后3-5个工作日内发货,批量订单(50件以上)可能需要7-10个工作日。' },
|
||||||
@@ -30,7 +31,7 @@ export default function ServicePage() {
|
|||||||
{/* 客服入口 */}
|
{/* 客服入口 */}
|
||||||
<View className='service-hero dashed-card mt-20'>
|
<View className='service-hero dashed-card mt-20'>
|
||||||
<View className='star-badge' />
|
<View className='star-badge' />
|
||||||
<Image className='service-hero-icon' src='/icon/电话.png' mode='aspectFit' />
|
<Image className='service-hero-icon' src={assetUrl('/icon/电话.png')} mode='aspectFit' />
|
||||||
<Text className='service-title'>智绘微刻客服中心</Text>
|
<Text className='service-title'>智绘微刻客服中心</Text>
|
||||||
<Text className='service-desc'>工作日 09:00 - 18:00 在线为您服务</Text>
|
<Text className='service-desc'>工作日 09:00 - 18:00 在线为您服务</Text>
|
||||||
<View className='btn-gradient service-btn' onTap={() => Taro.showToast({ title: '跳转客服会话', icon: 'none' })}>
|
<View className='btn-gradient service-btn' onTap={() => Taro.showToast({ title: '跳转客服会话', icon: 'none' })}>
|
||||||
@@ -44,7 +45,7 @@ export default function ServicePage() {
|
|||||||
<View className='contact-list'>
|
<View className='contact-list'>
|
||||||
<View className='contact-item dashed-card'>
|
<View className='contact-item dashed-card'>
|
||||||
<View className='star-badge' />
|
<View className='star-badge' />
|
||||||
<Image className='contact-icon-img' src='/icon/电话.png' mode='aspectFit' />
|
<Image className='contact-icon-img' src={assetUrl('/icon/电话.png')} mode='aspectFit' />
|
||||||
<View className='contact-info'>
|
<View className='contact-info'>
|
||||||
<Text className='contact-label'>客服电话</Text>
|
<Text className='contact-label'>客服电话</Text>
|
||||||
<Text className='contact-value'>400-XXX-XXXX</Text>
|
<Text className='contact-value'>400-XXX-XXXX</Text>
|
||||||
@@ -52,7 +53,7 @@ export default function ServicePage() {
|
|||||||
</View>
|
</View>
|
||||||
<View className='contact-item dashed-card' onTap={() => Taro.setClipboardData({ data: 'smart_engraving' })}>
|
<View className='contact-item dashed-card' onTap={() => Taro.setClipboardData({ data: 'smart_engraving' })}>
|
||||||
<View className='star-badge' />
|
<View className='star-badge' />
|
||||||
<Image className='contact-icon-img' src='/icon/词云生成.png' mode='aspectFit' />
|
<Image className='contact-icon-img' src={assetUrl('/icon/词云生成.png')} mode='aspectFit' />
|
||||||
<View className='contact-info'>
|
<View className='contact-info'>
|
||||||
<Text className='contact-label'>客服微信</Text>
|
<Text className='contact-label'>客服微信</Text>
|
||||||
<Text className='contact-value'>smart_engraving(点击复制)</Text>
|
<Text className='contact-value'>smart_engraving(点击复制)</Text>
|
||||||
@@ -60,7 +61,7 @@ export default function ServicePage() {
|
|||||||
</View>
|
</View>
|
||||||
<View className='contact-item dashed-card'>
|
<View className='contact-item dashed-card'>
|
||||||
<View className='star-badge' />
|
<View className='star-badge' />
|
||||||
<Image className='contact-icon-img' src='/icon/使用帮助.png' mode='aspectFit' />
|
<Image className='contact-icon-img' src={assetUrl('/icon/使用帮助.png')} mode='aspectFit' />
|
||||||
<View className='contact-info'>
|
<View className='contact-info'>
|
||||||
<Text className='contact-label'>商务邮箱</Text>
|
<Text className='contact-label'>商务邮箱</Text>
|
||||||
<Text className='contact-value'>biz@smart-engraving.com</Text>
|
<Text className='contact-value'>biz@smart-engraving.com</Text>
|
||||||
@@ -85,7 +86,7 @@ export default function ServicePage() {
|
|||||||
<View className='enterprise-service dashed-card mt-20 mb-20'>
|
<View className='enterprise-service dashed-card mt-20 mb-20'>
|
||||||
<View className='star-badge' />
|
<View className='star-badge' />
|
||||||
<View className='flex-center'>
|
<View className='flex-center'>
|
||||||
<Image className='enterprise-icon' src='/icon/企业批量定制.png' mode='aspectFit' />
|
<Image className='enterprise-icon' src={assetUrl('/icon/企业批量定制.png')} mode='aspectFit' />
|
||||||
<Text className='enterprise-title'>企业专属客户经理</Text>
|
<Text className='enterprise-title'>企业专属客户经理</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text className='enterprise-desc'>批量订单(50件以上)享专属报价、优先排产、专人对接</Text>
|
<Text className='enterprise-desc'>批量订单(50件以上)享专属报价、优先排产、专人对接</Text>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useSafeArea } from '../../hooks/useSafeArea'
|
|||||||
import { useStatusBar } from '../../hooks/useStatusBar'
|
import { useStatusBar } from '../../hooks/useStatusBar'
|
||||||
import ThemedPageMeta from '../../components/ThemedPageMeta'
|
import ThemedPageMeta from '../../components/ThemedPageMeta'
|
||||||
import { getUserInfo, setUserInfo, clearUserInfo, getAddressList, getDefaultAddress, type AddressItem } from '../../utils/store'
|
import { getUserInfo, setUserInfo, clearUserInfo, getAddressList, getDefaultAddress, type AddressItem } from '../../utils/store'
|
||||||
|
import { assetUrl } from '../../utils/asset'
|
||||||
|
|
||||||
const THEME_OPTIONS: { label: string; value: 'light' | 'dark' | 'auto' }[] = [
|
const THEME_OPTIONS: { label: string; value: 'light' | 'dark' | 'auto' }[] = [
|
||||||
{ label: '浅色模式', value: 'light' },
|
{ label: '浅色模式', value: 'light' },
|
||||||
@@ -15,10 +16,10 @@ const THEME_OPTIONS: { label: string; value: 'light' | 'dark' | 'auto' }[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const MENU = [
|
const MENU = [
|
||||||
{ label: '个人信息', icon: '/icon/个人.png', action: (s: any) => s.setShowEdit(true) },
|
{ label: '个人信息', icon: assetUrl('/icon/个人.png'), action: (s: any) => s.setShowEdit(true) },
|
||||||
{ label: '账号管理', icon: '/icon/账号管理.png', action: () => Taro.navigateTo({ url: '/pages/userDatabase/index' }) },
|
{ label: '账号管理', icon: assetUrl('/icon/账号管理.png'), action: () => Taro.navigateTo({ url: '/pages/userDatabase/index' }) },
|
||||||
{ label: '定制协议', icon: '/icon/定制协议.png', action: () => Taro.navigateTo({ url: '/pages/agreement/index' }) },
|
{ label: '定制协议', icon: assetUrl('/icon/定制协议.png'), action: () => Taro.navigateTo({ url: '/pages/agreement/index' }) },
|
||||||
{ label: '关于我们', icon: '/icon/关于我们.png', action: () => Taro.showToast({ title: '智绘微刻 v1.0', icon: 'none' }) }
|
{ label: '关于我们', icon: assetUrl('/icon/关于我们.png'), action: () => Taro.showToast({ title: '智绘微刻 v1.0', icon: 'none' }) }
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function SettingsPage() {
|
export default function SettingsPage() {
|
||||||
@@ -87,7 +88,7 @@ export default function SettingsPage() {
|
|||||||
<Image className='settings-avatar' src={user.avatarUrl} mode='aspectFill' />
|
<Image className='settings-avatar' src={user.avatarUrl} mode='aspectFill' />
|
||||||
) : (
|
) : (
|
||||||
<View className='settings-avatar-placeholder'>
|
<View className='settings-avatar-placeholder'>
|
||||||
<Image className='settings-avatar-icon' src='/icon/个人.png' mode='aspectFit' />
|
<Image className='settings-avatar-icon' src={assetUrl('/icon/个人.png')} mode='aspectFit' />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
<View style={{ marginLeft: '20px' }}>
|
<View style={{ marginLeft: '20px' }}>
|
||||||
@@ -150,7 +151,7 @@ export default function SettingsPage() {
|
|||||||
{editAvatar ? (
|
{editAvatar ? (
|
||||||
<Image className='avatar-img' src={editAvatar} mode='aspectFill' />
|
<Image className='avatar-img' src={editAvatar} mode='aspectFill' />
|
||||||
) : (
|
) : (
|
||||||
<Image className='avatar-placeholder-img' src='/icon/个人.png' mode='aspectFit' />
|
<Image className='avatar-placeholder-img' src={assetUrl('/icon/个人.png')} mode='aspectFit' />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
* 留空表示继续使用小程序包内本地资源(/icon、/img);
|
* 留空表示继续使用小程序包内本地资源(/icon、/img);
|
||||||
* 配置阿里云 OSS / CDN 后,所有经 assetUrl 处理的资源会自动拼接远程地址。
|
* 配置阿里云 OSS / CDN 后,所有经 assetUrl 处理的资源会自动拼接远程地址。
|
||||||
*/
|
*/
|
||||||
export const ASSET_BASE_URL = 'https://wordcloudwechat.oss-cn-hangzhou.aliyuncs.com'
|
declare const __OSS_BASE_URL__: string | undefined
|
||||||
|
|
||||||
|
export const ASSET_BASE_URL = typeof __OSS_BASE_URL__ !== 'undefined' ? __OSS_BASE_URL__ : ''
|
||||||
|
|
||||||
/** 把本地资源路径转成远程 URL;已经是 http(s) 或 wxfile/data 的保持不变 */
|
/** 把本地资源路径转成远程 URL;已经是 http(s) 或 wxfile/data 的保持不变 */
|
||||||
export function assetUrl(path?: string | null): string {
|
export function assetUrl(path?: string | null): string {
|
||||||
@@ -12,6 +14,8 @@ export function assetUrl(path?: string | null): string {
|
|||||||
if (/^(https?:|wxfile:|data:)/i.test(path)) return path
|
if (/^(https?:|wxfile:|data:)/i.test(path)) return path
|
||||||
if (!ASSET_BASE_URL) return path
|
if (!ASSET_BASE_URL) return path
|
||||||
const base = ASSET_BASE_URL.replace(/\/+$/, '')
|
const base = ASSET_BASE_URL.replace(/\/+$/, '')
|
||||||
|
// 小图标(/icon/*)保持本地打包,只有 /img/* 大图走 OSS
|
||||||
|
if (!path.startsWith('/img/')) return path
|
||||||
const normalized = path.startsWith('/') ? path : `/${path}`
|
const normalized = path.startsWith('/') ? path : `/${path}`
|
||||||
return `${base}${normalized}`
|
return `${base}${normalized}`
|
||||||
}
|
}
|
||||||
|
|||||||