Compare commits

..

3 Commits

Author SHA1 Message Date
25abd81d9f style(index): 优化首页样式与布局
- 重新设计首页背景色与容器安全区适配
- 优化Hero Banner区域增加圆角遮罩效果
- 调整Hero Grid菜单区样式,去除背景色实现透明效果
- 新增公告条样式,自定义样式替代NutUI NoticeBar
- 优化底部购买按钮样式,调整高度及阴影
- 统一首页通用section的左右边距,融合TrustSection和ContactSection样式
2026-04-09 13:09:45 +08:00
0be67bdb91 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	src/pages/index/index.tsx
2026-04-09 12:24:30 +08:00
969bc00b53 feat(customer): 新增接待人员选择功能
- 为 ShopDealerApply 模型增加接待人员 ID 和姓名字段
- dealer/customer/add 页面引入 Popup、SearchBar 及图标组件
- 实现接待人员选择弹层及搜索功能
- 表单中新增接待人员展示及清除操作
- 编辑模式下回填接待人员信息
- 提交表单时携带接待人员相关字段

feat(index): 新增首页品牌画册展示组件

- 创建 CatalogShowcase 组件及样式文件
- 展示品牌画册封面及标题说明
- “点击查看”按钮复制链接并提示用户打开浏览器查看
- 在首页主视图添加 CatalogShowcase 组件显示

fix(webview): 优化 webview 页面 URL 获取逻辑

- 使用 useRouter Hook 获取参数替代直接调用 Taro.getCurrentPages
-
2026-04-09 12:24:07 +08:00
16 changed files with 1060 additions and 473 deletions

View File

@@ -0,0 +1,17 @@
{
"version": 2,
"sessions": {
"8469bdd5894b41e1ba171a40f33d6a83": [
{
"expertId": "UiDesigner",
"name": "Sam",
"profession": "UI设计师",
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/01-Design/UiDesigner/UiDesigner.png",
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/01-Design/UiDesigner/UiDesigner_zh.md",
"usedAt": 1775709039214,
"industryId": "all"
}
]
},
"lastUpdated": 1775711001059
}

View File

@@ -0,0 +1,33 @@
# 2026-04-08 工作日志
## dealer/customer/add 新增接待人员选择功能
-`src/api/shop/shopDealerApply/model/index.ts` 中为 `ShopDealerApply` 接口新增了 `receptionistId`接待人员用户ID`receptionistName`(接待人员姓名)字段
-`src/dealer/customer/add.tsx` 中实现了接待人员选择功能:
- 引入 `Popup``SearchBar``ArrowRight``Del` 组件
- 引入 `pageShopDealerUser` API
- 添加接待人员相关状态(`showReceptionistPicker``receptionistList``selectedReceptionist` 等)
- 在表单手机号字段后新增 `Cell` 显示已选接待人员,支持清除
- 底部 `Popup` 弹出层,含搜索框 + 分销商用户列表选择
- 编辑模式下自动回填已保存的接待人员信息
- 提交时携带 `receptionistId``receptionistName` 字段
## 首页新增品牌画册区域
- 创建 `src/pages/index/CatalogShowcase.tsx` 组件:
- 展示品牌画册封面预览
- "点击查看"按钮,点击后复制链接并提示用户到浏览器打开
- 链接地址https://book.yunzhan365.com/mdfy/tjcs/mobile/index.html
- 创建 `src/pages/index/CatalogShowcase.scss` 样式文件
-`src/pages/index/index.tsx` 中引入并添加 `CatalogShowcase` 组件,位于 `CaseShowcase``ContactSection` 之间
## pages/webview/index.tsx 修复
- 原代码 `getUrl()` 在渲染阶段直接调用 `Taro.getCurrentPages()`,数据可能未就绪导致 URL 取不到
- 改为 `useRouter()` Hook + `useState` 获取 `params.url`,确保参数可靠后再渲染 web-view
- URL 无效时直接 return null 并 toast 提示后返回
## pages/index/CatalogShowcase.tsx 图标名修复
- `@nutui/icons-react-taro` 中不存在 `FileText` 图标,正确名称为 `File`
- `React error #130`Element type is undefined即因此导致已修复

View File

@@ -0,0 +1,23 @@
# 2026-04-09 工作日志
## 首页UI重构 — 去除"豆腐块"布局
用户反馈首页过于规整的"豆腐块"布局,要求重新设计。完成以下改动:
### 设计思路
- 打破均匀间距,各模块使用差异化 padding/margin
- 去掉公式化的"模块标题+白色卡片"结构
- 引入沉浸式视觉区域和横向交互
### 具体改动
1. **index.tsx** — 重构布局结构,引入 Hero 区域概念Banner+Grid 融为一体
2. **index.scss** — 全局样式重写,浅灰背景(#f5f5f7),自定义公告条样式
3. **TrustSection** — 从均匀三列改为横向滑动渐变卡片,突出数据亮点(10年/15年/98%)
4. **ContactSection** — 从2x2网格改为全宽深色底部(#1e293b#0f172a),沉浸式设计
5. **BestSellers** — 从纵向列表改为横向图文卡片(左图右信息),更紧凑
6. **CatalogShowcase** — 从小卡片改为大面积渐变视觉卡片,带装饰性书本图形
7. **NoticeBar** — 替换NutUI NoticeBar为自定义轻量公告条
### 注意事项
- `Fire` 图标在 @nutui/icons-react-taro 中不存在,改用 `StarFill`
- Banner 组件保留动态数据加载能力,圆角样式通过外层 CSS 控制
- Menu 组件已在原代码中 hidden本次未改动

View File

@@ -20,21 +20,21 @@
- 邀请码:`src/dealer/qrcode/index` - 邀请码:`src/dealer/qrcode/index`
- 导航工具:`src/utils/common.ts` (navTo函数) - 导航工具:`src/utils/common.ts` (navTo函数)
## 首页结构 (2026-04-02 更新) ## 首页结构 (2026-04-09 重大重构)
- Header (吸顶搜索栏) - Header (吸顶搜索栏,深色渐变背景)
- Menu (导航菜单hidden) - **Hero区域** (Banner + Grid 融合,圆角过渡)
- Banner (轮播广告) - **NoticeStrip** (自定义轻量公告条,黄色圆点+文字)
- Grid (功能菜单) - **BestSellers** (热销推荐 — 横向图文卡片,左图右信息)
- NoticeBar (公告栏) - **CatalogShowcase** (品牌画册 — 大面积渐变视觉卡片)
- **BrochureEntry** (品牌画册入口卡片 — 位于公告栏下方) - **TrustSection** (品质信任区 — 横向滑动渐变卡片10年质保/15年经验/98%满意度)
- BestSellers (热销商品) - **ContactSection** (联系我们 — 全宽深色沉浸式底部)
- **TrustSection** (品牌信任区 - 3列水平布局) - PopUpAd (弹窗广告)
- 品质保障10年质保德国进口五金
- 专业团队15年安装经验持证上岗 ### 设计特点
- 客户好评5000+家庭选择98%满意度 - 非豆腐块布局,差异化间距和视觉层次
- ~~CaseShowcase~~ (已注释隐藏,待有真实素材后再恢复) - 页面背景:#f5f5f7
- **ContactSection** (联系方式 - 2x2网格布局) - 底部区域为深色渐变(#1e293b#0f172a)
- 客服热线、在线咨询、门店地址、关注我们 - ~~CaseShowcase~~ (已注释隐藏)
## 图标使用注意事项 ## 图标使用注意事项
- NutUI图标库中不存在的图标 - NutUI图标库中不存在的图标
@@ -45,13 +45,14 @@
- 所有图标必须从 `@nutui/icons-react-taro` 导入 - 所有图标必须从 `@nutui/icons-react-taro` 导入
- 构建前需验证图标名称是否在可用导出列表中 - 构建前需验证图标名称是否在可用导出列表中
## 字体大小规范 ## 字体大小规范 (2026-04-09 更新)
- 微信小程序使用 TailwindCSS 文本类,不使用固定像素值 - 微信小程序端按移动端标准设计,最小辅助文字 13-14px
- 主标题:`text-lg font-semibold text-gray-800` - 标签/辅助14px
- 副标题:`text-sm text-gray-500` - 正文/描述15-16px
- 项目标题:`text-base font-semibold text-gray-800` - 小标题/按钮文字16-17px
- 项目描述:`text-xs text-gray-500` - 区块标题20-24px
- 小文本:`text-xs text-gray-500` - 强调数字价格、数据28-36px
- 所有图标必须从 `@nutui/icons-react-taro` 导入
## 画册页正式版 (2026-04-01) ## 画册页正式版 (2026-04-01)
- 首页已实际挂载 `src/pages/index/BrochureEntry.tsx` 入口,位置在 Banner 下方。 - 首页已实际挂载 `src/pages/index/BrochureEntry.tsx` 入口,位置在 Banner 下方。

View File

@@ -48,6 +48,10 @@ export interface ShopDealerApply {
nickName?: string; nickName?: string;
// 推荐人名称 // 推荐人名称
refereeName?: string; refereeName?: string;
// 接待人员用户ID
receptionistId?: number;
// 接待人员姓名
receptionistName?: string;
} }
/** /**

View File

@@ -1,6 +1,6 @@
import {useEffect, useState, useRef} from "react"; import {useEffect, useState, useRef} from "react";
import {Loading, CellGroup, Cell, Input, Form, Calendar} from '@nutui/nutui-react-taro' import {Loading, CellGroup, Cell, Input, Form, Calendar, Popup, SearchBar} from '@nutui/nutui-react-taro'
import {Edit, Calendar as CalendarIcon} from '@nutui/icons-react-taro' import {Edit, Calendar as CalendarIcon, ArrowRight, Del} from '@nutui/icons-react-taro'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import {useRouter} from '@tarojs/taro' import {useRouter} from '@tarojs/taro'
import {View, Text} from '@tarojs/components' import {View, Text} from '@tarojs/components'
@@ -15,7 +15,7 @@ import {
extractDateForCalendar, formatDateForDisplay extractDateForCalendar, formatDateForDisplay
} from "@/utils/dateUtils"; } from "@/utils/dateUtils";
import {ShopDealerUser} from "@/api/shop/shopDealerUser/model"; import {ShopDealerUser} from "@/api/shop/shopDealerUser/model";
import {getShopDealerUser} from "@/api/shop/shopDealerUser"; import {getShopDealerUser, pageShopDealerUser} from "@/api/shop/shopDealerUser";
const AddShopDealerApply = () => { const AddShopDealerApply = () => {
const {params} = useRouter(); const {params} = useRouter();
@@ -62,6 +62,13 @@ const AddShopDealerApply = () => {
const [applyTime, setApplyTime] = useState<string>('') const [applyTime, setApplyTime] = useState<string>('')
const [contractTime, setContractTime] = useState<string>('') const [contractTime, setContractTime] = useState<string>('')
// 接待人员选择状态
const [showReceptionistPicker, setShowReceptionistPicker] = useState<boolean>(false)
const [receptionistSearch, setReceptionistSearch] = useState<string>('')
const [receptionistList, setReceptionistList] = useState<ShopDealerUser[]>([])
const [receptionistLoading, setReceptionistLoading] = useState<boolean>(false)
const [selectedReceptionist, setSelectedReceptionist] = useState<ShopDealerUser | null>(null)
// 获取审核状态文字 // 获取审核状态文字
const getApplyStatusText = (status?: number) => { const getApplyStatusText = (status?: number) => {
switch (status) { switch (status) {
@@ -133,6 +140,15 @@ const AddShopDealerApply = () => {
setContractTime(extractDateForCalendar(dealerApply.contractTime)) setContractTime(extractDateForCalendar(dealerApply.contractTime))
} }
// 回填接待人员
if (dealerApply.receptionistId) {
setSelectedReceptionist({
userId: dealerApply.receptionistId,
dealerName: dealerApply.receptionistName || '',
realName: dealerApply.receptionistName || '',
})
}
Taro.setNavigationBarTitle({title: '签约'}) Taro.setNavigationBarTitle({title: '签约'})
} }
} catch (error) { } catch (error) {
@@ -143,6 +159,43 @@ const AddShopDealerApply = () => {
} }
} }
// 加载接待人员列表
const loadReceptionistList = async (keyword?: string) => {
setReceptionistLoading(true)
try {
const res = await pageShopDealerUser({keywords: keyword || '', limit: 50, page: 1})
setReceptionistList(res?.list || [])
} catch (e) {
console.error('加载接待人员失败:', e)
} finally {
setReceptionistLoading(false)
}
}
// 打开接待人员选择
const openReceptionistPicker = () => {
setReceptionistSearch('')
loadReceptionistList()
setShowReceptionistPicker(true)
}
// 搜索接待人员
const handleReceptionistSearch = (val: string) => {
setReceptionistSearch(val)
loadReceptionistList(val)
}
// 选择接待人员
const handleSelectReceptionist = (user: ShopDealerUser) => {
setSelectedReceptionist(user)
setShowReceptionistPicker(false)
}
// 清除接待人员
const handleClearReceptionist = () => {
setSelectedReceptionist(null)
}
// 提交表单 // 提交表单
// 计算保护期过期时间15天后 // 计算保护期过期时间15天后
const calculateExpirationTime = (): string => { const calculateExpirationTime = (): string => {
@@ -455,7 +508,10 @@ const AddShopDealerApply = () => {
expirationTime: expirationTime, expirationTime: expirationTime,
// 确保日期数据正确提交(使用数据库格式) // 确保日期数据正确提交(使用数据库格式)
applyTime: values.applyTime || (applyTime ? formatDateForDatabase(applyTime) : ''), applyTime: values.applyTime || (applyTime ? formatDateForDatabase(applyTime) : ''),
contractTime: values.contractTime || (contractTime ? formatDateForDatabase(contractTime) : '') contractTime: values.contractTime || (contractTime ? formatDateForDatabase(contractTime) : ''),
// 接待人员
receptionistId: selectedReceptionist?.userId || undefined,
receptionistName: selectedReceptionist ? (selectedReceptionist.realName || selectedReceptionist.dealerName || '') : undefined,
}; };
// 调试信息 // 调试信息
@@ -566,6 +622,31 @@ const AddShopDealerApply = () => {
<Form.Item name="mobile" label="手机号" initialValue={FormData?.mobile} required> <Form.Item name="mobile" label="手机号" initialValue={FormData?.mobile} required>
<Input placeholder="手机号" disabled={isEditMode} maxLength={11}/> <Input placeholder="手机号" disabled={isEditMode} maxLength={11}/>
</Form.Item> </Form.Item>
{/* 接待人员选择 */}
<Cell
title="接待人员"
extra={
<View className="flex items-center">
{selectedReceptionist ? (
<View className="flex items-center">
<Text className="text-sm text-gray-800 mr-2">
{selectedReceptionist.realName || selectedReceptionist.dealerName || '已选择'}
</Text>
<View
onClick={(e) => { e.stopPropagation(); handleClearReceptionist(); }}
className="flex items-center px-1"
>
<Del size={14} color="#999"/>
</View>
</View>
) : (
<Text className="text-sm text-gray-400"></Text>
)}
<ArrowRight size={14} color="#ccc"/>
</View>
}
onClick={openReceptionistPicker}
/>
{isEditMode && ( {isEditMode && (
<> <>
<Form.Item name="money" label="签约价格" initialValue={FormData?.money} required> <Form.Item name="money" label="签约价格" initialValue={FormData?.money} required>
@@ -628,6 +709,59 @@ const AddShopDealerApply = () => {
onConfirm={handleContractTimeConfirm} onConfirm={handleContractTimeConfirm}
/> />
{/* 接待人员选择弹出层 */}
<Popup
visible={showReceptionistPicker}
position="bottom"
round
onClose={() => setShowReceptionistPicker(false)}
style={{height: '70%'}}
>
<View className="flex flex-col h-full">
{/* 标题栏 */}
<View className="flex items-center justify-between px-4 py-3 border-b border-gray-100">
<Text className="text-base font-semibold text-gray-800"></Text>
<View onClick={() => setShowReceptionistPicker(false)}>
<Text className="text-sm text-blue-500"></Text>
</View>
</View>
{/* 搜索框 */}
<View className="px-3 py-2">
<SearchBar
value={receptionistSearch}
placeholder="搜索姓名/手机号"
onChange={handleReceptionistSearch}
/>
</View>
{/* 列表 */}
<View className="flex-1 overflow-y-auto">
{receptionistLoading ? (
<View className="flex justify-center items-center py-8">
<Loading></Loading>
</View>
) : receptionistList.length === 0 ? (
<View className="flex justify-center items-center py-8">
<Text className="text-sm text-gray-400"></Text>
</View>
) : (
receptionistList.map((user) => (
<Cell
key={user.userId}
title={user.realName || user.dealerName || '未知'}
description={user.mobile || user.dealerPhone || ''}
extra={
selectedReceptionist?.userId === user.userId ? (
<Text className="text-sm text-blue-500"></Text>
) : null
}
onClick={() => handleSelectReceptionist(user)}
/>
))
)}
</View>
</View>
</Popup>
{/* 审核状态显示(仅在编辑模式下显示) */} {/* 审核状态显示(仅在编辑模式下显示) */}
{isEditMode && ( {isEditMode && (
<CellGroup> <CellGroup>

View File

@@ -0,0 +1,146 @@
.bestsellers-list {
padding: 0 16px;
}
.bestsellers-item {
display: flex;
background: #ffffff;
border-radius: 14px;
overflow: hidden;
margin-bottom: 12px;
transition: transform 0.15s;
&:active {
transform: scale(0.985);
}
&:last-child {
margin-bottom: 0;
}
/* ═══ 左侧图片 ═══ */
&__img-wrap {
width: 140px;
min-height: 140px;
flex-shrink: 0;
background: #f3f4f6;
}
&__img {
width: 100%;
height: 100%;
display: block;
}
/* ═══ 右侧信息 ═══ */
&__info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 12px 12px 10px 14px;
min-height: 140px;
}
&__name {
font-size: 16px;
font-weight: 600;
color: #1a1a1a;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.4;
}
&__tags {
display: flex;
align-items: center;
gap: 10px;
margin-top: 6px;
}
&__comment {
font-size: 14px;
color: #f59e0b;
background: rgba(245, 158, 11, 0.08);
padding: 2px 8px;
border-radius: 4px;
}
&__sales {
font-size: 14px;
color: #9ca3af;
}
/* ═══ 底部操作栏 ═══ */
&__bottom {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-top: auto;
padding-top: 8px;
}
&__price-wrap {
display: flex;
align-items: baseline;
}
&__price-symbol {
font-size: 15px;
font-weight: 600;
color: #ef4444;
}
&__price-num {
font-size: 28px;
font-weight: 800;
color: #ef4444;
line-height: 1;
}
&__actions {
display: flex;
align-items: center;
gap: 8px;
}
&__share-btn {
width: 34px;
height: 34px;
border-radius: 50%;
background: #f3f4f6;
display: flex;
align-items: center;
justify-content: center;
}
&__buy-btn {
height: 38px;
padding: 0 20px;
background: linear-gradient(135deg, #2563eb, #3b82f6);
border-radius: 100px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3);
Text {
font-size: 15px;
font-weight: 600;
color: #ffffff;
}
}
}
/* ═══ 空状态 ═══ */
.bestsellers-empty {
padding: 40px 0;
text-align: center;
&__text {
font-size: 15px;
color: #9ca3af;
}
}

View File

@@ -18,20 +18,15 @@ const BestSellers = () => {
}) })
} }
// 处理分享点击
const handleShare = (item: ShopGoods) => { const handleShare = (item: ShopGoods) => {
setGoods(item); setGoods(item);
// 显示分享选项菜单
Taro.showActionSheet({ Taro.showActionSheet({
itemList: ['分享给好友', '分享到朋友圈'], itemList: ['分享给好友', '分享到朋友圈'],
success: (res) => { success: (res) => {
if (res.tapIndex === 0) { if (res.tapIndex === 0) {
// 分享给好友 - 触发转发
Taro.showShareMenu({ Taro.showShareMenu({
withShareTicket: true, withShareTicket: true,
success: () => { success: () => {
// 提示用户点击右上角分享
Taro.showToast({ Taro.showToast({
title: '请点击右上角分享给好友', title: '请点击右上角分享给好友',
icon: 'none', icon: 'none',
@@ -40,7 +35,6 @@ const BestSellers = () => {
} }
}); });
} else if (res.tapIndex === 1) { } else if (res.tapIndex === 1) {
// 分享到朋友圈
Taro.showToast({ Taro.showToast({
title: '请点击右上角分享到朋友圈', title: '请点击右上角分享到朋友圈',
icon: 'none', icon: 'none',
@@ -58,32 +52,22 @@ const BestSellers = () => {
reload() reload()
}, []) }, [])
// 分享给好友
useShareAppMessage(() => { useShareAppMessage(() => {
return { return {
title: goods?.name || '精选商品', title: goods?.name || '精选商品',
path: `/shop/goodsDetail/index?id=${goods?.goodsId}`, path: `/shop/goodsDetail/index?id=${goods?.goodsId}`,
imageUrl: goods?.image, // 分享图片 imageUrl: goods?.image,
success: function (res: any) { success: function (res: any) {
console.log('分享成功', res); console.log('分享成功', res);
Taro.showToast({ Taro.showToast({ title: '分享成功', icon: 'success', duration: 2000 });
title: '分享成功',
icon: 'success',
duration: 2000
});
}, },
fail: function (res: any) { fail: function (res: any) {
console.log('分享失败', res); console.log('分享失败', res);
Taro.showToast({ Taro.showToast({ title: '分享失败', icon: 'none', duration: 2000 });
title: '分享失败',
icon: 'none',
duration: 2000
});
} }
}; };
}); });
// 分享到朋友圈
useShareTimeline(() => { useShareTimeline(() => {
return { return {
title: `${goods?.name || '精选商品'} - 南南佐顿门窗`, title: `${goods?.name || '精选商品'} - 南南佐顿门窗`,
@@ -93,49 +77,62 @@ const BestSellers = () => {
}); });
return ( return (
<> <View className="bestsellers-list">
<View className={'py-1 px-4'}> {list?.map((item, index) => (
<View className={'flex flex-col justify-between items-center rounded-lg px-2'}>
{list?.map((item, index) => {
return (
<View key={index} className={'flex flex-col rounded-lg bg-white shadow-sm w-full mb-5'}>
<Image src={item.image} mode={'aspectFit'} lazyLoad={false}
radius="10px 10px 0 0" height="180"
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}/>
<View className={'flex flex-col p-2 rounded-lg'}>
<View>
<View className={'car-no text-sm'}>{item.name}</View>
<View className={'flex justify-between text-xs py-1'}>
<Text className={'text-orange-500'}>{item.comments}</Text>
<Text className={'text-gray-400'}> {item.sales}</Text>
</View>
<View className={'flex justify-between items-center py-2'}>
<View className={'flex text-red-500 text-xl items-baseline'}>
<Text className={'text-xs'}></Text>
<Text className={'font-bold text-2xl'}>{item.price}</Text>
</View>
<View className={'buy-btn'}>
<View className={'cart-icon flex items-center'}>
<View <View
className={'flex flex-col justify-center items-center text-white px-3 gap-1 text-nowrap whitespace-nowrap cursor-pointer'} key={index}
onClick={() => handleShare(item)} className="bestsellers-item"
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}
> >
<Share size={20}/> {/* 左侧商品图 */}
<View className="bestsellers-item__img-wrap">
<Image
src={item.image}
mode="aspectFill"
lazyLoad={false}
className="bestsellers-item__img"
/>
</View> </View>
{/* 右侧信息区 */}
<View className="bestsellers-item__info">
<Text className="bestsellers-item__name">{item.name}</Text>
<View className="bestsellers-item__tags">
<Text className="bestsellers-item__comment">{item.comments}</Text>
<Text className="bestsellers-item__sales"> {item.sales}</Text>
</View> </View>
<Text className={'text-white pl-4 pr-5'}
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}> <View className="bestsellers-item__bottom">
</Text> {/* 价格 */}
<View className="bestsellers-item__price-wrap">
<Text className="bestsellers-item__price-symbol"></Text>
<Text className="bestsellers-item__price-num">{item.price}</Text>
</View>
{/* 操作按钮 */}
<View className="bestsellers-item__actions">
<View
className="bestsellers-item__share-btn"
onClick={(e) => { e.stopPropagation(); handleShare(item) }}
>
<Share size={18} color="#9ca3af" />
</View>
<View className="bestsellers-item__buy-btn">
<Text></Text>
</View> </View>
</View> </View>
</View> </View>
</View> </View>
</View> </View>
) ))}
})}
{list.length === 0 && (
<View className="bestsellers-empty">
<Text className="bestsellers-empty__text"></Text>
</View> </View>
)}
</View> </View>
</>
) )
} }
export default BestSellers export default BestSellers

View File

@@ -0,0 +1,144 @@
.catalog-card {
position: relative;
margin: 24px 16px 8px;
padding: 24px 20px;
background: linear-gradient(135deg, #1e3a5f 0%, #2563eb 50%, #3b82f6 100%);
border-radius: 18px;
overflow: hidden;
box-shadow: 0 8px 32px rgba(37, 99, 235, 0.3);
&:active {
opacity: 0.92;
}
/* ═══ 装饰圆 ═══ */
&__deco {
position: absolute;
border-radius: 50%;
pointer-events: none;
&--1 {
width: 160px;
height: 160px;
top: -40px;
right: -30px;
background: rgba(255, 255, 255, 0.06);
}
&--2 {
width: 100px;
height: 100px;
bottom: -20px;
left: 20%;
background: rgba(255, 255, 255, 0.04);
}
}
/* ═══ 内容区 ═══ */
&__content {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
z-index: 1;
}
&__left {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
padding-right: 16px;
}
&__badge {
font-size: 14px;
font-weight: 700;
color: rgba(255, 255, 255, 0.5);
letter-spacing: 2px;
}
&__title {
font-size: 24px;
font-weight: 700;
color: #ffffff;
margin-bottom: 2px;
}
&__desc {
font-size: 15px;
color: rgba(255, 255, 255, 0.65);
line-height: 1.5;
}
&__cta {
display: inline-flex;
align-items: center;
gap: 4px;
margin-top: 12px;
background: rgba(255, 255, 255, 0.15);
border-radius: 100px;
padding: 8px 16px;
align-self: flex-start;
backdrop-filter: blur(4px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
&__cta-text {
font-size: 15px;
font-weight: 600;
color: #ffffff;
}
/* ═══ 右侧书本图形 ═══ */
&__right {
flex-shrink: 0;
}
&__book {
width: 72px;
height: 90px;
background: rgba(255, 255, 255, 0.12);
border-radius: 4px 10px 10px 4px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1px solid rgba(255, 255, 255, 0.2);
}
&__book-spine {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 6px;
background: rgba(255, 255, 255, 0.15);
border-radius: 4px 0 0 4px;
}
&__book-pages {
display: flex;
gap: 3px;
align-items: flex-end;
}
&__book-page {
width: 4px;
background: rgba(255, 255, 255, 0.25);
border-radius: 2px;
&:nth-child(1) { height: 28px; }
&:nth-child(2) { height: 40px; }
&:nth-child(3) { height: 32px; }
}
&__book-label {
position: absolute;
bottom: 6px;
right: 8px;
font-size: 14px;
font-weight: 700;
color: rgba(255, 255, 255, 0.4);
}
}

View File

@@ -0,0 +1,68 @@
import { View, Text } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { ArrowRight } from '@nutui/icons-react-taro'
import './CatalogShowcase.scss'
const CATALOG_URL = 'https://book.yunzhan365.com/mdfy/tjcs/mobile/index.html'
function CatalogShowcase() {
const handleViewCatalog = () => {
Taro.setClipboardData({
data: CATALOG_URL,
success: () => {
Taro.showToast({
title: '链接已复制',
icon: 'success',
duration: 2000
})
setTimeout(() => {
Taro.showModal({
title: '提示',
content: '链接已复制到剪贴板,请前往浏览器打开查看品牌画册',
showCancel: false,
confirmText: '知道了'
})
}, 2100)
},
fail: () => {
Taro.showToast({ title: '复制失败,请重试', icon: 'none' })
}
})
}
return (
<View className="catalog-card" onClick={handleViewCatalog}>
{/* 装饰性背景元素 */}
<View className="catalog-card__deco catalog-card__deco--1" />
<View className="catalog-card__deco catalog-card__deco--2" />
<View className="catalog-card__content">
<View className="catalog-card__left">
<Text className="catalog-card__badge">BRAND CATALOG</Text>
<Text className="catalog-card__title"></Text>
<Text className="catalog-card__desc">
线
</Text>
<View className="catalog-card__cta">
<Text className="catalog-card__cta-text"></Text>
<ArrowRight size={16} color="#ffffff" />
</View>
</View>
<View className="catalog-card__right">
<View className="catalog-card__book">
<View className="catalog-card__book-spine" />
<View className="catalog-card__book-pages">
<View className="catalog-card__book-page" />
<View className="catalog-card__book-page" />
<View className="catalog-card__book-page" />
</View>
<Text className="catalog-card__book-label">2026</Text>
</View>
</View>
</View>
</View>
)
}
export default CatalogShowcase

View File

@@ -1,143 +1,95 @@
.contact-section { .contact-section {
background: #ffffff;
padding: 24px 16px;
margin: 16px 0;
border-radius: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
&:first-child {
margin-top: 0; margin-top: 0;
} padding: 32px 20px 24px;
background: linear-gradient(180deg, #1e293b 0%, #0f172a 100%);
border-radius: 24px 24px 0 0;
color: #ffffff;
&:last-child { /* ═══ 引言区 ═══ */
margin-bottom: 32px; &__intro {
}
&__title {
text-align: center;
margin-bottom: 24px;
&-text {
display: block;
font-size: 18px;
font-weight: 600;
color: #1e293b;
margin-bottom: 4px;
}
}
&__subtitle {
font-size: 14px;
color: #64748b;
display: block;
}
&__grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 24px; margin-bottom: 24px;
} }
&__item { &__intro-title {
display: flex;
align-items: center;
padding: 16px;
background: #f8fafc;
border-radius: 12px;
&:active {
background: #f1f5f9;
transform: scale(0.98);
transition: transform 0.1s;
}
}
&__icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
flex-shrink: 0;
&--blue {
background: rgba(59, 130, 246, 0.1);
}
&--green {
background: rgba(16, 185, 129, 0.1);
}
&--orange {
background: rgba(245, 158, 11, 0.1);
}
&--cyan {
background: rgba(6, 182, 212, 0.1);
}
}
&__content {
flex: 1;
min-width: 0;
}
&__item-title {
display: block; display: block;
font-size: 14px; font-size: 24px;
color: #64748b; font-weight: 700;
margin-bottom: 4px; color: #ffffff;
margin-bottom: 6px;
} }
&__item-value { &__intro-desc {
display: block; display: block;
font-size: 15px; font-size: 15px;
font-weight: 600; color: rgba(255, 255, 255, 0.5);
color: #1e293b;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
/* ═══ 操作按钮区 ═══ */
&__actions {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 28px;
}
&__action {
display: flex;
align-items: center;
padding: 16px 18px;
border-radius: 14px;
transition: opacity 0.2s;
&:active {
opacity: 0.8;
}
}
&__action--primary {
background: linear-gradient(135deg, #3b82f6, #2563eb);
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.35);
}
&__action--secondary {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.12);
}
&__action-text {
flex: 1;
margin-left: 14px;
display: flex;
flex-direction: column;
}
&__action-label {
font-size: 17px;
font-weight: 600;
color: #ffffff;
margin-bottom: 2px;
}
&__action-value {
font-size: 14px;
color: rgba(255, 255, 255, 0.6);
}
/* ═══ 底部信息 ═══ */
&__footer { &__footer {
text-align: center; text-align: center;
padding-top: 20px; padding-top: 20px;
border-top: 1px solid #e2e8f0; border-top: 1px solid rgba(255, 255, 255, 0.08);
} }
&__footer-text { &__footer-text {
display: block; display: block;
font-size: 12px; font-size: 14px;
color: #64748b; color: rgba(255, 255, 255, 0.4);
line-height: 1.6;
&:first-child {
margin-bottom: 4px; margin-bottom: 4px;
} }
}
}
// 响应式适配 &__footer-sub {
@media (max-width: 375px) { display: block;
.contact-section { font-size: 13px;
padding: 20px 12px; color: rgba(255, 255, 255, 0.25);
margin: 12px 0;
&__grid {
gap: 12px;
}
&__item {
padding: 12px;
}
&__icon {
width: 36px;
height: 36px;
margin-right: 10px;
}
} }
} }

View File

@@ -8,59 +8,6 @@ import {
import './ContactSection.scss' import './ContactSection.scss'
const ContactSection: React.FC = () => { const ContactSection: React.FC = () => {
const contactItems = [
{
icon: <Phone size={20} color="#3b82f6" />,
title: '联系电话',
value: '13367810229',
action: 'call',
colorClass: 'contact-item--blue'
},
{
icon: <Message size={20} color="#10b981" />,
title: '在线咨询',
value: '点击立即咨询',
action: 'chat',
colorClass: 'contact-item--green'
},
// {
// icon: <Location size={20} color="#f59e0b" />,
// title: '门店地址',
// value: '上海市浦东新区XX路888号',
// action: 'map',
// colorClass: 'contact-item--orange'
// },
// {
// icon: <Share size={20} color="#06b6d4" />,
// title: '关注我们',
// value: '南南佐顿门窗',
// action: 'wechat',
// colorClass: 'contact-item--cyan'
// }
]
const handleAction = (action: string) => {
switch (action) {
case 'call':
handleCallPhone()
break
case 'chat':
handleOnlineChat()
break
case 'map':
console.log('查看地图')
// TODO: 跳转到地图导航
break
case 'wechat':
console.log('关注公众号')
// TODO: 显示公众号二维码
break
default:
break
}
}
// 拨打电话功能
const handleCallPhone = () => { const handleCallPhone = () => {
Taro.showModal({ Taro.showModal({
title: '拨打电话', title: '拨打电话',
@@ -76,10 +23,7 @@ const ContactSection: React.FC = () => {
}, },
fail: (err) => { fail: (err) => {
console.error('拨打电话失败:', err) console.error('拨打电话失败:', err)
Taro.showToast({ Taro.showToast({ title: '拨打电话失败', icon: 'none' })
title: '拨打电话失败',
icon: 'none'
})
} }
}) })
} }
@@ -87,29 +31,19 @@ const ContactSection: React.FC = () => {
}) })
} }
// 在线咨询功能
const handleOnlineChat = () => { const handleOnlineChat = () => {
// 检查是否已登录
Taro.getStorage({ Taro.getStorage({
key: 'userInfo', key: 'userInfo',
success: (_) => { success: (_) => {
// 用户已登录,跳转到聊天页面
Taro.navigateTo({ Taro.navigateTo({
url: '/pages/user/chat/conversation/index', url: '/pages/user/chat/conversation/index',
success: () => {
console.log('跳转到聊天页面成功')
},
fail: (err) => { fail: (err) => {
console.error('跳转失败:', err) console.error('跳转失败:', err)
Taro.showToast({ Taro.showToast({ title: '跳转失败,请稍后重试', icon: 'none' })
title: '跳转失败,请稍后重试',
icon: 'none'
})
} }
}) })
}, },
fail: () => { fail: () => {
// 用户未登录,提示登录
Taro.showModal({ Taro.showModal({
title: '登录提示', title: '登录提示',
content: '需要登录后才能在线咨询,是否立即登录?', content: '需要登录后才能在线咨询,是否立即登录?',
@@ -121,10 +55,7 @@ const ContactSection: React.FC = () => {
url: '/pages/passport/login', url: '/pages/passport/login',
fail: (err) => { fail: (err) => {
console.error('跳转到登录页失败:', err) console.error('跳转到登录页失败:', err)
Taro.showToast({ Taro.showToast({ title: '跳转失败', icon: 'none' })
title: '跳转失败',
icon: 'none'
})
} }
}) })
} }
@@ -135,35 +66,40 @@ const ContactSection: React.FC = () => {
} }
return ( return (
<View className="contact-section px-4"> <View className="contact-section">
<View className="contact-section__title flex flex-col"> {/* 顶部引言区 */}
<Text className="text-lg font-semibold text-gray-800"></Text> <View className="contact-section__intro">
<Text className="text-sm text-gray-500"></Text> <Text className="contact-section__intro-title"></Text>
<Text className="contact-section__intro-desc">
</Text>
</View> </View>
<View className="contact-section__grid"> {/* 操作按钮 — 全宽醒目 */}
{contactItems.map((item, index) => ( <View className="contact-section__actions">
<View <View className="contact-section__action contact-section__action--primary" onClick={handleCallPhone}>
key={index} <Phone size={22} color="#ffffff" />
className="contact-section__item" <View className="contact-section__action-text">
onClick={() => handleAction(item.action)} <Text className="contact-section__action-label"></Text>
> <Text className="contact-section__action-value">13367810229</Text>
<View className={`contact-section__icon ${item.colorClass}`}>
{item.icon}
</View> </View>
<View className="contact-section__content flex flex-col">
<Text className="text-xs text-gray-500">{item.title}</Text>
<Text className="text-sm font-semibold text-gray-800 truncate">{item.value}</Text>
</View>
</View>
))}
</View> </View>
<View className="contact-section__action contact-section__action--secondary" onClick={handleOnlineChat}>
<Message size={22} color="#3b82f6" />
<View className="contact-section__action-text">
<Text className="contact-section__action-label">线</Text>
<Text className="contact-section__action-value"></Text>
</View>
</View>
</View>
{/* 底部信息 */}
<View className="contact-section__footer"> <View className="contact-section__footer">
<Text className="text-xs text-gray-500"> <Text className="contact-section__footer-text">
8:30 - 18:00 8:30 - 18:00
</Text> </Text>
<Text className="text-xs text-gray-500"> <Text className="contact-section__footer-sub">
</Text> </Text>
</View> </View>

View File

@@ -1,101 +1,94 @@
.trust-section { .trust-section {
background: #ffffff; padding: 32px 0 24px;
padding: 24px 16px; background: #f5f5f7;
margin: 16px 0;
border-radius: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
&:first-child { &__header {
margin-top: 0; padding: 0 20px;
margin-bottom: 16px;
} }
&:last-child { &__label {
margin-bottom: 32px; display: block;
font-size: 14px;
font-weight: 600;
color: #9ca3af;
letter-spacing: 2px;
text-transform: uppercase;
margin-bottom: 4px;
} }
&__title { &__title {
text-align: center;
margin-bottom: 24px;
&-text {
display: block; display: block;
font-size: 18px; font-size: 22px;
font-weight: 600; font-weight: 700;
color: #1e293b; color: #1a1a1a;
margin-bottom: 4px;
}
} }
&__subtitle { /* 横向滑动容器 */
font-size: 14px; &__scroll {
color: #64748b; width: 100%;
display: block; white-space: nowrap;
} }
&__grid { &__track {
display: flex; display: inline-flex;
justify-content: space-between;
gap: 12px; gap: 12px;
padding: 0 20px 8px;
} }
&__item { /* 单张渐变卡片 */
flex: 1; &__card {
display: flex; display: inline-flex;
flex-direction: column; flex-direction: column;
align-items: center; width: 200px;
text-align: center; padding: 20px 16px;
padding: 8px; border-radius: 16px;
color: #ffffff;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
vertical-align: top;
white-space: normal;
} }
&__icon { &__card-icon {
width: 56px; width: 48px;
height: 56px; height: 48px;
border-radius: 50%; border-radius: 12px;
background: rgba(255, 255, 255, 0.2);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-bottom: 12px; margin-bottom: 14px;
&--blue {
background: rgba(59, 130, 246, 0.1);
} }
&--green { &__card-title {
background: rgba(16, 185, 129, 0.1); font-size: 17px;
}
&--orange {
background: rgba(245, 158, 11, 0.1);
}
}
&__item-title {
font-size: 15px;
font-weight: 600; font-weight: 600;
color: #1e293b; color: rgba(255, 255, 255, 0.9);
margin-bottom: 4px; margin-bottom: 8px;
} }
&__item-desc { &__card-highlight-row {
font-size: 12px; display: flex;
color: #64748b; align-items: baseline;
line-height: 1.4; gap: 6px;
} margin-bottom: 6px;
} }
// 响应式适配 &__card-highlight {
@media (max-width: 375px) { font-size: 36px;
.trust-section { font-weight: 800;
padding: 20px 12px; line-height: 1;
margin: 12px 0; color: #ffffff;
&__grid {
gap: 8px;
} }
&__icon { &__card-desc {
width: 48px; font-size: 15px;
height: 48px; color: rgba(255, 255, 255, 0.75);
} }
&__card-sub {
font-size: 14px;
color: rgba(255, 255, 255, 0.6);
margin-top: 4px;
} }
} }

View File

@@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import { View, Text } from '@tarojs/components' import { View, Text, ScrollView } from '@tarojs/components'
import { import {
ShieldCheck, ShieldCheck,
People, People,
@@ -10,43 +10,60 @@ import './TrustSection.scss'
const TrustSection: React.FC = () => { const TrustSection: React.FC = () => {
const trustItems = [ const trustItems = [
{ {
icon: <ShieldCheck size={24} color="#3b82f6" />, icon: <ShieldCheck size={28} color="#ffffff" />,
title: '品质保障', title: '品质保障',
description: '10年质保,德国进口五金', highlight: '10年',
colorClass: '--blue' description: '质保承诺',
subDescription: '德国进口五金配件',
bg: 'linear-gradient(135deg, #3b82f6, #2563eb)',
}, },
{ {
icon: <People size={24} color="#10b981" />, icon: <People size={28} color="#ffffff" />,
title: '专业团队', title: '专业团队',
description: '15年安装经验,持证上岗', highlight: '15年',
colorClass: '--green' description: '安装经验',
subDescription: '全员持证上岗',
bg: 'linear-gradient(135deg, #10b981, #059669)',
}, },
{ {
icon: <Star size={24} color="#f59e0b" />, icon: <Star size={28} color="#ffffff" />,
title: '客户好评', title: '客户好评',
description: '5000+家庭选择98%满意度', highlight: '98%',
colorClass: '--orange' description: '满意度',
subDescription: '5000+家庭信赖之选',
bg: 'linear-gradient(135deg, #f59e0b, #d97706)',
} }
] ]
return ( return (
<View className="trust-section"> <View className="trust-section">
<View className="trust-section__title flex flex-col"> <View className="trust-section__header">
<Text className="text-lg font-semibold text-gray-800"> · </Text> <Text className="trust-section__label">WHY CHOOSE US</Text>
<Text className="text-sm text-gray-500"></Text> <Text className="trust-section__title"></Text>
</View> </View>
<View className="trust-section__grid"> <ScrollView
className="trust-section__scroll"
scrollX
enhanced
showScrollbar={false}
>
<View className="trust-section__track">
{trustItems.map((item, index) => ( {trustItems.map((item, index) => (
<View key={index} className="trust-section__item"> <View key={index} className="trust-section__card" style={{background: item.bg}}>
<View className={`trust-section__icon trust-section__icon${item.colorClass}`}> <View className="trust-section__card-icon">
{item.icon} {item.icon}
</View> </View>
<Text className="text-base font-semibold text-gray-800">{item.title}</Text> <Text className="trust-section__card-title">{item.title}</Text>
<Text className="text-xs text-gray-500">{item.description}</Text> <View className="trust-section__card-highlight-row">
<Text className="trust-section__card-highlight">{item.highlight}</Text>
<Text className="trust-section__card-desc">{item.description}</Text>
</View>
<Text className="trust-section__card-sub">{item.subDescription}</Text>
</View> </View>
))} ))}
</View> </View>
</ScrollView>
</View> </View>
) )
} }

View File

@@ -1,34 +1,135 @@
page { page {
//background: url('https://oss.wsdns.cn/20250621/33ca4ca532e647bc918a59d01f5d88a9.jpg?x-oss-process=image/resize,m_fixed,w_2000/quality,Q_90') no-repeat top center; background: #f5f5f7;
//background-size: 100%;
background: linear-gradient(to bottom, #3b82f6, #ffffff);
min-height: 100vh; min-height: 100vh;
} }
// 底部内容区域统一间距 /* ═══ 首页容器 ═══ */
.bottom-content-section { .home-page {
margin: 16px 0; padding-bottom: env(safe-area-inset-bottom);
padding-bottom: constant(safe-area-inset-bottom);
&:first-child {
margin-top: 0;
} }
&:last-child { /* ═══ Hero 顶部视觉区 ═══ */
margin-bottom: 32px;
.hero-banner-wrap {
padding: 0 16px;
padding-top: 6px;
/* 让Banner Swiper有圆角 */
.nut-swiper {
border-radius: 16px;
overflow: hidden;
}
.nut-swiper-item {
border-radius: 16px;
overflow: hidden;
} }
} }
/* Hero下方的Grid菜单区 */
.hero-grid {
margin: 0 16px;
margin-top: -12px;
position: relative;
z-index: 2;
border-radius: 16px;
overflow: hidden;
background: #ffffff;
/* 让NutUI Grid的白色背景更好融入 */
.nut-grid {
background: transparent;
}
.nut-grid-item__content {
padding: 10px 0;
}
}
/* ═══ 公告条 — 自定义样式替代NutUI NoticeBar ═══ */
.notice-strip {
display: flex;
align-items: center;
padding: 10px 20px;
margin: 0 16px 8px;
background: rgba(245, 158, 11, 0.08);
border-radius: 8px;
gap: 8px;
&__dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: #f59e0b;
flex-shrink: 0;
}
&__text {
font-size: 15px;
color: #92400e;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
/* ═══ 热销推荐 ═══ */
.section-hot {
padding: 20px 0 4px;
&__header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 16px 14px;
}
&__title-wrap {
display: flex;
align-items: center;
gap: 6px;
}
&__title {
font-size: 20px;
font-weight: 700;
color: #1a1a1a;
letter-spacing: 0.5px;
}
&__more {
display: flex;
align-items: center;
gap: 2px;
font-size: 15px;
color: #9ca3af;
}
}
/* ═══ 通用section间距重置去掉TrustSection/ContactSection自带的margin ═══ */
.home-page .trust-section,
.home-page .contact-section {
margin-left: 0;
margin-right: 0;
}
/* ═══ 底部购买按钮 ═══ */
.buy-btn { .buy-btn {
height: 70px; height: 64px;
background: linear-gradient(to bottom, #2563eb, #3b82f6); background: linear-gradient(135deg, #2563eb, #3b82f6);
border-radius: 100px; border-radius: 100px;
color: #ffffff; color: #ffffff;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-around;
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.35);
.cart-icon { .cart-icon {
background: linear-gradient(to bottom, #60a5fa, #3b82f6); background: rgba(255, 255, 255, 0.15);
border-radius: 100px 0 0 100px; border-radius: 100px 0 0 100px;
height: 70px; height: 64px;
} }
} }

View File

@@ -4,22 +4,22 @@ import Taro from '@tarojs/taro';
import {useShareAppMessage, useShareTimeline} from "@tarojs/taro" import {useShareAppMessage, useShareTimeline} from "@tarojs/taro"
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import {getShopInfo} from "@/api/layout"; import {getShopInfo} from "@/api/layout";
import {NoticeBar} from '@nutui/nutui-react-taro' import {View, Text} from '@tarojs/components'
import {View} from '@tarojs/components'
import Menu from "./Menu";
import Banner from "./Banner";
import './index.scss'
import Grid from "@/pages/index/Grid"; import Grid from "@/pages/index/Grid";
import Banner from "./Banner";
import PopUpAd from "@/pages/index/PopUpAd"; import PopUpAd from "@/pages/index/PopUpAd";
import TrustSection from "./TrustSection"; import TrustSection from "./TrustSection";
import CaseShowcase from "./CaseShowcase";
import ContactSection from "./ContactSection"; import ContactSection from "./ContactSection";
import BrochureEntry from "./BrochureEntry"; import CatalogShowcase from "./CatalogShowcase";
import {configWebsiteField} from "@/api/cms/cmsWebsiteField"; import {configWebsiteField} from "@/api/cms/cmsWebsiteField";
import type {Config} from "@/api/cms/cmsWebsiteField/model"; import type {Config} from "@/api/cms/cmsWebsiteField/model";
import {
ArrowRight,
StarFill,
} from '@nutui/icons-react-taro';
import './index.scss'
function Home() { function Home() {
// 吸顶状态
const [config, setConfig] = useState<Config>() const [config, setConfig] = useState<Config>()
useShareTimeline(() => { useShareTimeline(() => {
@@ -50,7 +50,6 @@ function Home() {
cancelText: '取消', cancelText: '取消',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
// 用户点击确认,打开授权设置页面
openSetting(); openSetting();
} }
} }
@@ -58,14 +57,11 @@ function Home() {
}; };
const openSetting = () => { const openSetting = () => {
// Taro.openSetting调起客户端小程序设置界面返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
Taro.openSetting({ Taro.openSetting({
success: (res) => { success: (res) => {
if (res.authSetting['scope.userInfo']) { if (res.authSetting['scope.userInfo']) {
// 用户授权成功,可以获取用户信息
reload(); reload();
} else { } else {
// 用户拒绝授权,提示授权失败
Taro.showToast({ Taro.showToast({
title: '授权失败', title: '授权失败',
icon: 'none' icon: 'none'
@@ -80,29 +76,21 @@ function Home() {
}; };
useEffect(() => { useEffect(() => {
// 获取站点信息 getShopInfo().then(() => {})
getShopInfo().then(() => {
})
// 获取配置信息
configWebsiteField({}).then(data => { configWebsiteField({}).then(data => {
setConfig(data) setConfig(data)
}) })
// Taro.getSetting获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
Taro.getSetting({ Taro.getSetting({
success: (res) => { success: (res) => {
if (res.authSetting['scope.userInfo']) { if (res.authSetting['scope.userInfo']) {
// 用户已经授权过,可以直接获取用户信息
console.log('用户已经授权过,可以直接获取用户信息') console.log('用户已经授权过,可以直接获取用户信息')
reload(); reload();
} else { } else {
// 用户未授权,需要弹出授权窗口
console.log('用户未授权,需要弹出授权窗口') console.log('用户未授权,需要弹出授权窗口')
showAuthModal(); showAuthModal();
} }
} }
}); });
// 获取用户信息
Taro.getUserInfo({ Taro.getUserInfo({
success: (res) => { success: (res) => {
const avatar = res.userInfo.avatarUrl; const avatar = res.userInfo.avatarUrl;
@@ -113,19 +101,52 @@ function Home() {
return ( return (
<> <>
{/*<Sticky threshold={0} onChange={() => onSticky(arguments)}>*/}
<Header/> <Header/>
{/*</Sticky>*/} <View className={'home-page'}>
<View className={'flex flex-col mt-1'}>
<Menu/> {/* ═══ Hero 区域Banner 融入头部渐变背景 ═══ */}
<View className={'hero-zone my-2'}>
<View className={'hero-banner-wrap'}>
<Banner /> <Banner />
</View>
{/* 功能菜单 — 直接铺在Banner下方无卡片包裹 */}
<View className={'hero-grid my-4'}>
<Grid /> <Grid />
<NoticeBar content={config?.NoticeBar || '南南佐顿门窗专业门窗定制安装服务10年质保德国进口五金5000+家庭选择98%客户满意度'} /> </View>
<BrochureEntry /> </View>
<BestSellers/>
{/* ═══ 滚动公告条 — 轻量嵌入 ═══ */}
<View className={'notice-strip my-4'}>
<View className={'notice-strip__dot'} />
<Text className={'text-sm'}>
{config?.NoticeBar || '南南佐顿门窗专业门窗定制安装服务10年质保德国进口五金5000+家庭选择'}
</Text>
</View>
{/* ═══ 热销推荐 — 标题行内嵌,无卡片边框 ═══ */}
{/*<View className={'section-hot'}>*/}
{/* <View className={'section-hot__header'}>*/}
{/* <View className={'section-hot__title-wrap'}>*/}
{/* <StarFill size={20} color='#ef4444' />*/}
{/* <Text className={'section-hot__title'}>热销推荐</Text>*/}
{/* </View>*/}
{/* <Text className={'section-hot__more'} onClick={() => Taro.navigateTo({url: '/shop/category/index'})}>*/}
{/* 更多 <ArrowRight size={14} color='#9ca3af' />*/}
{/* </Text>*/}
{/* </View>*/}
{/* <BestSellers />*/}
{/*</View>*/}
{/* ═══ 品牌画册 — 沉浸式视觉卡片 ═══ */}
<CatalogShowcase />
{/* ═══ 品质信任区 — 横向滑动卡片 ═══ */}
<TrustSection /> <TrustSection />
{/* <CaseShowcase/> */}
{/* ═══ 联系我们 — 全宽底部 ═══ */}
<ContactSection /> <ContactSection />
</View> </View>
<PopUpAd /> <PopUpAd />
</> </>