From 969bc00b537f2f1578340917bc1811c986477dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Thu, 9 Apr 2026 12:24:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(customer):=20=E6=96=B0=E5=A2=9E=E6=8E=A5?= =?UTF-8?q?=E5=BE=85=E4=BA=BA=E5=91=98=E9=80=89=E6=8B=A9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 ShopDealerApply 模型增加接待人员 ID 和姓名字段 - dealer/customer/add 页面引入 Popup、SearchBar 及图标组件 - 实现接待人员选择弹层及搜索功能 - 表单中新增接待人员展示及清除操作 - 编辑模式下回填接待人员信息 - 提交表单时携带接待人员相关字段 feat(index): 新增首页品牌画册展示组件 - 创建 CatalogShowcase 组件及样式文件 - 展示品牌画册封面及标题说明 - “点击查看”按钮复制链接并提示用户打开浏览器查看 - 在首页主视图添加 CatalogShowcase 组件显示 fix(webview): 优化 webview 页面 URL 获取逻辑 - 使用 useRouter Hook 获取参数替代直接调用 Taro.getCurrentPages - --- .workbuddy/memory/2026-04-08.md | 33 +++++ src/api/shop/shopDealerApply/model/index.ts | 4 + src/dealer/customer/add.tsx | 142 +++++++++++++++++++- src/pages/index/CatalogShowcase.scss | 109 +++++++++++++++ src/pages/index/CatalogShowcase.tsx | 71 ++++++++++ src/pages/index/index.tsx | 2 + 6 files changed, 357 insertions(+), 4 deletions(-) create mode 100644 .workbuddy/memory/2026-04-08.md create mode 100644 src/pages/index/CatalogShowcase.scss create mode 100644 src/pages/index/CatalogShowcase.tsx diff --git a/.workbuddy/memory/2026-04-08.md b/.workbuddy/memory/2026-04-08.md new file mode 100644 index 0000000..b18be8d --- /dev/null +++ b/.workbuddy/memory/2026-04-08.md @@ -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)即因此导致,已修复 diff --git a/src/api/shop/shopDealerApply/model/index.ts b/src/api/shop/shopDealerApply/model/index.ts index 05353c6..0fef919 100644 --- a/src/api/shop/shopDealerApply/model/index.ts +++ b/src/api/shop/shopDealerApply/model/index.ts @@ -48,6 +48,10 @@ export interface ShopDealerApply { nickName?: string; // 推荐人名称 refereeName?: string; + // 接待人员用户ID + receptionistId?: number; + // 接待人员姓名 + receptionistName?: string; } /** diff --git a/src/dealer/customer/add.tsx b/src/dealer/customer/add.tsx index f84c24c..45465ae 100644 --- a/src/dealer/customer/add.tsx +++ b/src/dealer/customer/add.tsx @@ -1,6 +1,6 @@ import {useEffect, useState, useRef} from "react"; -import {Loading, CellGroup, Cell, Input, Form, Calendar} from '@nutui/nutui-react-taro' -import {Edit, Calendar as CalendarIcon} from '@nutui/icons-react-taro' +import {Loading, CellGroup, Cell, Input, Form, Calendar, Popup, SearchBar} from '@nutui/nutui-react-taro' +import {Edit, Calendar as CalendarIcon, ArrowRight, Del} from '@nutui/icons-react-taro' import Taro from '@tarojs/taro' import {useRouter} from '@tarojs/taro' import {View, Text} from '@tarojs/components' @@ -15,7 +15,7 @@ import { extractDateForCalendar, formatDateForDisplay } from "@/utils/dateUtils"; import {ShopDealerUser} from "@/api/shop/shopDealerUser/model"; -import {getShopDealerUser} from "@/api/shop/shopDealerUser"; +import {getShopDealerUser, pageShopDealerUser} from "@/api/shop/shopDealerUser"; const AddShopDealerApply = () => { const {params} = useRouter(); @@ -62,6 +62,13 @@ const AddShopDealerApply = () => { const [applyTime, setApplyTime] = useState('') const [contractTime, setContractTime] = useState('') + // 接待人员选择状态 + const [showReceptionistPicker, setShowReceptionistPicker] = useState(false) + const [receptionistSearch, setReceptionistSearch] = useState('') + const [receptionistList, setReceptionistList] = useState([]) + const [receptionistLoading, setReceptionistLoading] = useState(false) + const [selectedReceptionist, setSelectedReceptionist] = useState(null) + // 获取审核状态文字 const getApplyStatusText = (status?: number) => { switch (status) { @@ -133,6 +140,15 @@ const AddShopDealerApply = () => { setContractTime(extractDateForCalendar(dealerApply.contractTime)) } + // 回填接待人员 + if (dealerApply.receptionistId) { + setSelectedReceptionist({ + userId: dealerApply.receptionistId, + dealerName: dealerApply.receptionistName || '', + realName: dealerApply.receptionistName || '', + }) + } + Taro.setNavigationBarTitle({title: '签约'}) } } 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天后) const calculateExpirationTime = (): string => { @@ -455,7 +508,10 @@ const AddShopDealerApply = () => { expirationTime: expirationTime, // 确保日期数据正确提交(使用数据库格式) 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 = () => { + {/* 接待人员选择 */} + + {selectedReceptionist ? ( + + + {selectedReceptionist.realName || selectedReceptionist.dealerName || '已选择'} + + { e.stopPropagation(); handleClearReceptionist(); }} + className="flex items-center px-1" + > + + + + ) : ( + 请选择 + )} + + + } + onClick={openReceptionistPicker} + /> {isEditMode && ( <> @@ -628,6 +709,59 @@ const AddShopDealerApply = () => { onConfirm={handleContractTimeConfirm} /> + {/* 接待人员选择弹出层 */} + setShowReceptionistPicker(false)} + style={{height: '70%'}} + > + + {/* 标题栏 */} + + 选择接待人员 + setShowReceptionistPicker(false)}> + 取消 + + + {/* 搜索框 */} + + + + {/* 列表 */} + + {receptionistLoading ? ( + + 加载中 + + ) : receptionistList.length === 0 ? ( + + 暂无数据 + + ) : ( + receptionistList.map((user) => ( + 已选 + ) : null + } + onClick={() => handleSelectReceptionist(user)} + /> + )) + )} + + + + {/* 审核状态显示(仅在编辑模式下显示) */} {isEditMode && ( diff --git a/src/pages/index/CatalogShowcase.scss b/src/pages/index/CatalogShowcase.scss new file mode 100644 index 0000000..ea5c916 --- /dev/null +++ b/src/pages/index/CatalogShowcase.scss @@ -0,0 +1,109 @@ +.catalog-showcase { + margin: 16px; + padding: 16px; + background: #ffffff; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); +} + +.catalog-header { + margin-bottom: 16px; +} + +.catalog-title-bar { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 4px; +} + +.catalog-icon { + width: 20px; + height: 20px; + color: #3b82f6; +} + +.catalog-title { + font-size: 16px; + font-weight: 600; + color: #1f2937; +} + +.catalog-subtitle { + font-size: 12px; + color: #6b7280; + margin-left: 28px; +} + +.catalog-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} + +.catalog-preview { + width: 100%; + display: flex; + justify-content: center; +} + +.catalog-cover { + width: 140px; + height: 180px; + background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); + border-radius: 8px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); +} + +.cover-icon { + width: 48px; + height: 48px; + color: rgba(255, 255, 255, 0.9); +} + +.cover-text { + font-size: 14px; + font-weight: 600; + color: #ffffff; + text-align: center; + padding: 0 12px; +} + +.cover-subtext { + font-size: 11px; + color: rgba(255, 255, 255, 0.7); +} + +.view-catalog-btn { + width: 100%; + height: 44px; + background: linear-gradient(to right, #3b82f6, #2563eb); + border-radius: 22px; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + border: none; + + &::after { + border: none; + } + + .btn-text { + font-size: 15px; + font-weight: 500; + color: #ffffff; + } + + .btn-icon { + width: 16px; + height: 16px; + color: #ffffff; + } +} diff --git a/src/pages/index/CatalogShowcase.tsx b/src/pages/index/CatalogShowcase.tsx new file mode 100644 index 0000000..75a357f --- /dev/null +++ b/src/pages/index/CatalogShowcase.tsx @@ -0,0 +1,71 @@ +import { View, Text } from '@tarojs/components' +import Taro from '@tarojs/taro' +import { Button } from '@nutui/nutui-react-taro' +import { File, 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 ( + + + + + 品牌画册 + + 了解南南佐顿门窗的产品与服务 + + + + + + + 南南佐顿门窗 + 品牌电子画册 + + + + + + + ) +} + +export default CatalogShowcase diff --git a/src/pages/index/index.tsx b/src/pages/index/index.tsx index 88d0e52..d6eb843 100644 --- a/src/pages/index/index.tsx +++ b/src/pages/index/index.tsx @@ -14,6 +14,7 @@ import PopUpAd from "@/pages/index/PopUpAd"; import TrustSection from "./TrustSection"; import CaseShowcase from "./CaseShowcase"; import ContactSection from "./ContactSection"; +import CatalogShowcase from "./CatalogShowcase"; import {configWebsiteField} from "@/api/cms/cmsWebsiteField"; import type {Config} from "@/api/cms/cmsWebsiteField/model"; @@ -123,6 +124,7 @@ function Home() { +