feat(user): 统一VIP概念为开发者并优化开发者登录及申请
- 将VIP相关文案、图标改为开发者,涉及用户中心、门店中心、审核页等 - 优化登录弹窗UI,改为红色系,提升视觉统一性和交互体验 - 关于我们页面品牌和公司信息内容更新,展示最新企业介绍和业务范围 - VIP升级页新增“所在城市”选择,使用微信原生地区选择器,完善地址信息 - 用户申请表单中新增城市字段及校验,提交时携带城市信息 - 优化审核页面文案,统称开发者身份,明确申请审核内容 - 修复UserRoleController.save接口bug,避免userId被无条件覆盖导致角色绑错 - 动态查询VIP角色ID,避免硬编码,支持多租户环境正确绑定角色
This commit is contained in:
34
.idea/workspace.xml
generated
34
.idea/workspace.xml
generated
@@ -4,10 +4,16 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="2de94e4f-0587-4c9b-8bfd-e87e0ae95b81" name="更改" comment="feat(user): 改为动态查询 VIP 角色ID,避免写死角色ID - RoleParam 类型新增 tenantId 字段,支持按租户过滤角色 - 新增 getCurrentTenantId 获取当前租户ID,支持登录态和默认租户回退 - 实现 getVipRoleId 函数,按 roleCode 与 tenantId 查询 VIP 角色ID - assignVipRole 中动态获取 VIP 角色ID,避免硬编码 - 添加用户角色时带上 tenantId,确保多租户角色绑定正确 - 移除写">
|
||||
<list default="true" id="2de94e4f-0587-4c9b-8bfd-e87e0ae95b81" name="更改" comment="feat(user): 动态查询VIP角色ID,避免硬编码绑定问题 - RoleParam新增tenantId字段,支持按租户过滤角色 - 新增getCurrentTenantId函数,兼容登录态及默认租户回退 - 实现getVipRoleId函数,按roleCode与tenantId查询VIP角色ID - assignVipRole动态获取VIP角色ID,移除写死的角色ID - 添加用户角色时传">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.workbuddy/memory/2026-07-21.md" beforeDir="false" afterPath="$PROJECT_DIR$/.workbuddy/memory/2026-07-21.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.workbuddy/memory/2026-07-22.md" beforeDir="false" afterPath="$PROJECT_DIR$/.workbuddy/memory/2026-07-22.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/api/shop/shopDealerApply/model/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/api/shop/shopDealerApply/model/index.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/store/center/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/store/center/index.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/user/about/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/user/about/index.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/user/user.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/user/user.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/user/vip-review/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/user/vip-review/index.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/user/vip-upgrade/index.config.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/user/vip-upgrade/index.config.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/user/vip-upgrade/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/user/vip-upgrade/index.tsx" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -119,15 +125,7 @@
|
||||
<workItem from="1784131049625" duration="2304000" />
|
||||
<workItem from="1784133736973" duration="4614000" />
|
||||
<workItem from="1784585070286" duration="3047000" />
|
||||
<workItem from="1784638429525" duration="1386000" />
|
||||
</task>
|
||||
<task id="LOCAL-00009" summary="fix(invite): 修复邀请加入请求头TenantId传递问题 - 从配置中引入TenantId常量 - 将请求header中的TenantId字段由硬编码改为动态变量 - 确保GET请求和加密数据请求均正确带上TenantId头信息 - 提升接口调用的一致性和维护性">
|
||||
<option name="closed" value="true" />
|
||||
<created>1775905047844</created>
|
||||
<option name="number" value="00009" />
|
||||
<option name="presentableId" value="LOCAL-00009" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1775905047844</updated>
|
||||
<workItem from="1784638429525" duration="7110000" />
|
||||
</task>
|
||||
<task id="LOCAL-00010" summary="fix(config): 修正环境配置中的API_BASE_URL地址 - 将开发环境、生产环境和测试环境的API_BASE_URL统一更新为新的域名websopy-api.websoft.top - 更新SimpleQRCodeModal组件中二维码请求地址,使用新的API域名 - 在邀请信息请求函数fetchInviteInfo中添加详细的请求和响应日志 - 增加接口错误日志打印,提高捕获异常时的信息丰富度">
|
||||
<option name="closed" value="true" />
|
||||
@@ -513,7 +511,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1784639229566</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="58" />
|
||||
<task id="LOCAL-00058" summary="feat(user): 动态查询VIP角色ID,避免硬编码绑定问题 - RoleParam新增tenantId字段,支持按租户过滤角色 - 新增getCurrentTenantId函数,兼容登录态及默认租户回退 - 实现getVipRoleId函数,按roleCode与tenantId查询VIP角色ID - assignVipRole动态获取VIP角色ID,移除写死的角色ID - 添加用户角色时传入tenantId,确保多租户环境中角色绑定正确 - 修复后端UserRoleController.save接口bug,避免userId被无条件覆盖 - 用户角色绑定时正确使用申请人userId,解决VIP审核角色误绑定问题">
|
||||
<option name="closed" value="true" />
|
||||
<created>1784653464775</created>
|
||||
<option name="number" value="00058" />
|
||||
<option name="presentableId" value="LOCAL-00058" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1784653464775</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="59" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
@@ -533,7 +539,6 @@
|
||||
<component name="VcsManagerConfiguration">
|
||||
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
||||
<option name="CHECK_NEW_TODO" value="false" />
|
||||
<MESSAGE value="feat(passport-invite): 优化新用户扫码邀请加入流程 - 扫码进入邀请页面时保存邀请 token 到本地存储 - 新增登录状态检测,未登录用户点击快速加入时保存邀请信息并跳转登录页 - 登录成功后返回邀请页自动执行加入应用操作,提升用户体验 - 支持已登录用户直接加入应用,简化流程 - 增加清理 pending 邀请数据的方法,防止数据残留 - 监听页面显示事件,处理登录返回后自动操作 - 调整接口请求逻辑,确保携带最新的邀请和授权数据 - 更新项目配置,注册邀请页面路径信息" />
|
||||
<MESSAGE value="feat(passport): 重写登录与邀请加入流程,支持微信手机号一键登录 - 完全重写 passport/login.tsx,实现微信手机号一键登录功能 - 支持登录页面携带 redirect 参数,登录后自动跳转回原页面 - 登录成功后自动处理邀请加入应用,支持邀请关系绑定 - 重写 passport/invite/index.tsx,实现扫码邀请加入应用完整流程 - 支持调用 wx.login 获取 code,使用 loginByOpenId 判断注册状态 - 未注册用户自动跳转登录页完成微信手机号登录再加入应用 - 已登录用户直接执行加入应用操作,加入接口支持 Authorization 头 -" />
|
||||
<MESSAGE value="feat(invite): 重构邀请加入流程,实现已登录用户免授权直接加入 - 完全重写邀请页逻辑,区分已登录与未注册用户按钮显示 - 已登录用户显示“确认加入”按钮,点击用 wx.login 获取 code 加入 - 未注册用户显示“微信手机号快速加入”按钮,授权手机号后完成注册登录再加入 - 移除跳转登录页面逻辑,所有流程在邀请页内完成 - 加入接口统一使用 Authorization 头携带 token,确保身份双重验证 - 优化背景、粒子和扫描线效果,提升界面视觉体验 - 增加错误处理提示,加入失败时显示详细原因并可返回首页 - 协议勾选仅在未注册用户显示,确保合规授权流程" />
|
||||
<MESSAGE value="feat(passport): 优化已登录用户加入时协议勾选逻辑 - 已登录用户点击「确认加入」时不再强制勾选协议 - 移除 handleConfirmJoin 中对协议勾选的检查 - 未注册用户仍需勾选协议后才能授权手机号 - 确保登录状态异常时有提示信息显示" />
|
||||
@@ -558,7 +563,8 @@
|
||||
<MESSAGE value="feat(passport): 迁移并修复小程序支付页面访问 websopy API 问题 - 迁移 src/passport/pay 相关代码至当前项目,无需替换文件 - 新增环境变量 WEBSOPY_API_BASE_URL,提供 websopy API 基础地址 - 在 config/app.ts 导出 WebsopyBaseUrl,供代码引用 - 配置 env.d.ts 类型定义,消除 TypeScript 隐式 any 错误 - request 请求方法支持自定义 baseUrl 以调用不同接口地址 - passport/pay 页面调用订阅接口统一传入 WebsopyBaseUrl - 修正 tenantId 类型为 number,调整登录用户类型断言 - 确认编译成功,dist 目录生成对应文件及正确请求地址 - 说明 type-check 依赖库问题不影响正常构建和运行" />
|
||||
<MESSAGE value="feat(passport): 优化支付成功后页面跳转逻辑 - 修改支付成功后的自动跳转目标,从已购产品页面改为我的页面 - 使用 switchTab 替代 navigateTo 保证在 tabBar 页顺利跳转 - 移除多余的跳转失败回退逻辑,简化跳转流程 - 调整跳转延迟时间为 1500 毫秒,提升用户体验" />
|
||||
<MESSAGE value="feat(user): 改为动态查询 VIP 角色ID,避免写死角色ID - RoleParam 类型新增 tenantId 字段,支持按租户过滤角色 - 新增 getCurrentTenantId 获取当前租户ID,支持登录态和默认租户回退 - 实现 getVipRoleId 函数,按 roleCode 与 tenantId 查询 VIP 角色ID - assignVipRole 中动态获取 VIP 角色ID,避免硬编码 - 添加用户角色时带上 tenantId,确保多租户角色绑定正确 - 移除写" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="feat(user): 改为动态查询 VIP 角色ID,避免写死角色ID - RoleParam 类型新增 tenantId 字段,支持按租户过滤角色 - 新增 getCurrentTenantId 获取当前租户ID,支持登录态和默认租户回退 - 实现 getVipRoleId 函数,按 roleCode 与 tenantId 查询 VIP 角色ID - assignVipRole 中动态获取 VIP 角色ID,避免硬编码 - 添加用户角色时带上 tenantId,确保多租户角色绑定正确 - 移除写" />
|
||||
<MESSAGE value="feat(user): 动态查询VIP角色ID,避免硬编码绑定问题 - RoleParam新增tenantId字段,支持按租户过滤角色 - 新增getCurrentTenantId函数,兼容登录态及默认租户回退 - 实现getVipRoleId函数,按roleCode与tenantId查询VIP角色ID - assignVipRole动态获取VIP角色ID,移除写死的角色ID - 添加用户角色时传" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="feat(user): 动态查询VIP角色ID,避免硬编码绑定问题 - RoleParam新增tenantId字段,支持按租户过滤角色 - 新增getCurrentTenantId函数,兼容登录态及默认租户回退 - 实现getVipRoleId函数,按roleCode与tenantId查询VIP角色ID - assignVipRole动态获取VIP角色ID,移除写死的角色ID - 添加用户角色时传" />
|
||||
<option name="NON_MODAL_COMMIT_POSTPONE_SLOW_CHECKS" value="false" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,8 +1,36 @@
|
||||
# 2026-07-22
|
||||
# 2026-07-22 工作日志
|
||||
|
||||
## 修复 UserRoleController.save 覆盖 userId 的 bug
|
||||
- 文件:`com.gxwebsoft.core/.../system/controller/UserRoleController.java`
|
||||
- 问题:原 `save` 用 `getLoginUser().getUserId()` **无条件覆盖**请求体 userId,导致 VIP 审核通过时角色绑到店员而非申请人。
|
||||
- 修复:改为 `if (userRole.getUserId() == null && loginUser != null) userRole.setUserId(loginUser.getUserId());`(仅当请求体未传 userId 时回退到登录用户)。
|
||||
- 影响:VIP 审核 `addUserRole({ userId: item.userId, ... })` 现在能正确把角色绑给申请人;其它不传 userId 的调用方行为不变。
|
||||
- 注意:UserRole 的 `tenantId` 由 MyBatis-Plus 多租户插件按请求头自动写入 INSERT,前端传的 tenantId 仅作冗余。
|
||||
## 开发者登录弹窗 UI 美化
|
||||
- 文件:`src/pages/user/about/index.tsx`
|
||||
- 美化了 Input(圆角12px、高度48px、柔和背景色)、Button(蓝色渐变、阴影、圆角14px)、验证码按钮(与输入框等高、倒计时优化)
|
||||
- 弹窗整体:圆角加大、内边距增加、宽度收窄
|
||||
|
||||
## 开发者登录弹窗样式改为红色系
|
||||
- 文件:`src/pages/user/about/index.tsx`
|
||||
- Input 边框:`transparent` → `#e53e3e`(红色),背景 `#f7f8fa` → `#fff5f5`(淡红)
|
||||
- 获取验证码按钮:激活态添加 `backgroundColor: #e53e3e`、`borderColor: #e53e3e`
|
||||
- 登录按钮:渐变从蓝色 `#4f8cff→#3b6fd4` 改为红色 `#fc5c5c→#e53e3e`,阴影同步改为红色
|
||||
|
||||
## 关于我们页面介绍内容改写
|
||||
- 文件:`src/pages/user/about/index.tsx`
|
||||
- 公司:南宁市网宿信息科技有限公司,品牌:威数谱软件(websopy)
|
||||
- 重写品牌故事(三个段落)、核心业务(6项软件相关)、联系我们(公司名称/总部/电话/邮箱/官网)、版权信息
|
||||
- 头部 Logo 文字「鑫」→「威」,版本号显示 websopy v1.0.0
|
||||
|
||||
## VIP 概念替换为「开发者」(文案统一)
|
||||
- 起因:用户要求把 `pages/user/vip-upgrade/index` 的 VIP 概念换成「开发者」。后台审核页 `vip-review` 已定义 `VIP_ROLE_NAME='开发者'`,概念本就一致。
|
||||
- 改动文件与要点:
|
||||
- `vip-upgrade/index.tsx` + `index.config.ts`:导航栏/分享标题/主副标题/状态说明/温馨提示/权益区块标题及权益项描述,全部「VIP会员」→「开发者」;顶部图标 👑→🔰;表单标签「门店名称/门店地址」→「开发团队/公司地址」(placeholder、toast 同步),底层字段 realName/address 不变。
|
||||
- `user.tsx`:用户中心入口「升级VIP会员」👑→「申请开发者」🔰。
|
||||
- `store/center/index.tsx`:门店中心入口「VIP会员审核/审核客户VIP会员申请」👑→「开发者审核/审核客户开发者申请」🔰。
|
||||
- `vip-review/index.tsx`:导航栏标题、通过/驳回弹窗文案、审核说明、列表字段标签(门店名称/门店地址→开发团队/公司地址)统一为「开发者」。
|
||||
- 注意:仅改可见文案与图标,**未重命名**页面路由/文件夹(vip-upgrade、vip-review)、变量名(VIP_ROLE_CODE、pendingVipCount 等)及定价逻辑(dealerPrice/VIP 状态判断仍保留原标识符,因对应后端角色与缓存 key)。
|
||||
- 用户确认方式:先给文案对照表,经 AskUserQuestion 确认表单标签与图标及范围后执行。
|
||||
|
||||
## VIP 升级页新增「所在城市」选择
|
||||
- 文件:`src/pages/user/vip-upgrade/index.tsx`、`src/api/shop/shopDealerApply/model/index.ts`
|
||||
- 在「办公地址」上方新增「所在城市 *」字段:复用微信原生 `Picker mode='region'`(省市区三级),与 `pages/user/address-edit.tsx` 风格一致。
|
||||
- 状态:`cityValue: string[]` + 派生 `cityText`(以空格连接);`Picker` 的 `disabled` 跟随 `isReadonly`(审核中/已通过只读)。
|
||||
- `ShopDealerApply` 模型新增 `city?: string`(注释:省 市 区,空格分隔)。
|
||||
- 提交时 `addShopDealerApply` 增加 `city: cityText`;读取历史申请时 `setCityValue(latest.city.split(' '))`;保存校验增加「请选择所在城市」。
|
||||
- 注意:仅前端扩展字段,后端是否落库 `city` 存疑(模型此前只有 address),需后端 schema 支持才能真正持久化。
|
||||
|
||||
@@ -32,6 +32,8 @@ export interface ShopDealerApply {
|
||||
updateTime?: string;
|
||||
// 门店名称
|
||||
merchantName?: string;
|
||||
// 所在城市(省 市 区,以空格分隔)
|
||||
city?: string;
|
||||
// 门店地址
|
||||
address?: string;
|
||||
}
|
||||
|
||||
@@ -45,9 +45,9 @@ const FEATURE_CARDS = [
|
||||
},
|
||||
{
|
||||
key: 'vip-review',
|
||||
icon: '👑',
|
||||
title: 'VIP会员审核',
|
||||
desc: '审核客户VIP会员申请',
|
||||
icon: '🔰',
|
||||
title: '开发者审核',
|
||||
desc: '审核客户开发者身份的申请',
|
||||
url: '/pages/user/vip-review/index',
|
||||
color: '#7c3aed',
|
||||
bgColor: '#ede9fe',
|
||||
|
||||
@@ -132,23 +132,29 @@ const AboutPage: React.FC = () => {
|
||||
{/* 头部品牌区 */}
|
||||
<View className='bg-white py-10 flex flex-col items-center'>
|
||||
<View className='w-20 h-20 rounded-2xl bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center shadow-lg mb-4'>
|
||||
<Text className='text-white text-4xl font-bold'>鑫</Text>
|
||||
<Text className='text-white text-4xl font-bold'>威</Text>
|
||||
</View>
|
||||
<Text className='text-xl font-bold text-gray-800'>威硕谱软件</Text>
|
||||
<Text className='text-sm text-gray-400 mt-1'>威硕谱软件 v1.0.0</Text>
|
||||
<Text className='text-xl font-bold text-gray-800'>威数谱软件</Text>
|
||||
<Text className='text-sm text-gray-400 mt-1'>websopy v1.0.0</Text>
|
||||
</View>
|
||||
|
||||
{/* 公司简介 */}
|
||||
{/* 公司简介 / 品牌故事 */}
|
||||
<View className='bg-white mx-3 mt-3 rounded-xl p-4' style={{ boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }}>
|
||||
<Text className='text-base font-medium text-gray-800 mb-3 block'>公司简介</Text>
|
||||
<Text className='text-base font-medium text-gray-800 mb-3 block'>品牌故事</Text>
|
||||
<Text className='text-sm text-gray-600 leading-relaxed'>
|
||||
威硕谱软件是一家专注于家用电器销售、安装及维修服务的本地经营部。
|
||||
我们提供冰箱、洗衣机、空调、电视、厨房电器、生活小家电等多种品类,涵盖售前咨询、
|
||||
售后安装、维护保养及故障维修等一站式家电服务。
|
||||
威数谱软件(websopy)是南宁市网宿信息科技有限公司旗下的数字化服务品牌,
|
||||
致力于为中小企业提供一站式软件产品与信息化解决方案。我们以"让每一家企业
|
||||
都能轻松用上专业的软件"为使命,依托云计算、移动互联网与大数据技术,
|
||||
打造覆盖企业管理、营销获客到客户服务的全链路软件矩阵。
|
||||
</Text>
|
||||
<Text className='text-sm text-gray-600 leading-relaxed mt-2'>
|
||||
经营部秉承"诚信经营、服务至上"的理念,以优质的产品和贴心的服务赢得了本地客户的信赖。
|
||||
我们致力于让每一位顾客都能买得放心、用得安心,享受便捷的家电生活体验。
|
||||
自创立以来,威数谱始终秉持"专业、务实、共赢"的价值观,已为零售、家电、
|
||||
本地生活等多个行业的数百家客户提供服务。我们相信,技术的价值在于落地——
|
||||
用更简单的产品、更透明的服务,帮助客户把生意做得更好。
|
||||
</Text>
|
||||
<Text className='text-sm text-gray-600 leading-relaxed mt-2'>
|
||||
未来,威数谱将继续以产品力为核心,与合作伙伴携手同行,让数字化能力成为
|
||||
中小企业稳健增长的底座。
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -156,19 +162,19 @@ const AboutPage: React.FC = () => {
|
||||
<View className='bg-white mx-3 mt-3 rounded-xl p-4' style={{ boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }}>
|
||||
<Text className='text-base font-medium text-gray-800 mb-3 block'>核心业务</Text>
|
||||
{[
|
||||
{ icon: '📺', title: '家电零售', desc: '电视、冰箱、洗衣机等全品类家电销售' },
|
||||
{ icon: '❄️', title: '空调服务', desc: '空调销售、安装、加氟、清洗及维修' },
|
||||
{ icon: '🍳', title: '厨房电器', desc: '油烟机、燃气灶、热水器等厨卫电器' },
|
||||
{ icon: '🛠️', title: '安装维修', desc: '专业师傅上门安装、检测及故障维修' },
|
||||
{ icon: '🔧', title: '保养维护', desc: '定期清洗保养,延长家电使用寿命' },
|
||||
{ icon: '✅', title: '品质保障', desc: '正品货源、透明报价、售后无忧' },
|
||||
{ icon: '🖥️', title: '企业管理软件', desc: 'ERP / CRM / OA 等数字化管理工具' },
|
||||
{ icon: '🌐', title: '品牌官网建设', desc: '企业官网、品牌站、营销落地页定制' },
|
||||
{ icon: '📱', title: '移动应用开发', desc: '微信小程序、跨端 APP 定制开发' },
|
||||
{ icon: '🔗', title: '系统集成对接', desc: '第三方平台 API 对接与数据打通' },
|
||||
{ icon: '📊', title: '数据智能分析', desc: '经营数据可视化与业务洞察' },
|
||||
{ icon: '✅', title: '技术服务支持', desc: '部署运维、培训陪跑、售后无忧' },
|
||||
].map((item, idx) => (
|
||||
<View
|
||||
key={idx}
|
||||
className={`flex items-start gap-3 py-3 ${idx < 5 ? 'border-b border-gray-50' : ''}`}
|
||||
>
|
||||
<Text className='text-2xl mt-0.5'>{item.icon}</Text>
|
||||
<View className='flex-1'>
|
||||
<View className='flex-1 flex flex-col'>
|
||||
<Text className='text-sm font-medium text-gray-700'>{item.title}</Text>
|
||||
<Text className='text-xs text-gray-400 mt-0.5'>{item.desc}</Text>
|
||||
</View>
|
||||
@@ -181,10 +187,12 @@ const AboutPage: React.FC = () => {
|
||||
<Text className='text-base font-medium text-gray-800 mb-3 block'>联系我们</Text>
|
||||
<View className='flex flex-col gap-2'>
|
||||
{[
|
||||
{ label: '经营部名称', value: '威硕谱软件' },
|
||||
{ label: '经营地址', value: '大新里南718号' },
|
||||
{ label: '联系电话', value: '18269229683' },
|
||||
{ label: '营业时间', value: '9:00 ~ 18:00' },
|
||||
{ label: '公司名称', value: '南宁市网宿信息科技有限公司' },
|
||||
{ label: '品牌名称', value: '威数谱软件(websopy)' },
|
||||
{ label: '公司总部', value: '南宁市良庆区五象大道401号五象航洋城(总部基地)1号楼1226室' },
|
||||
{ label: '联系电话', value: '0771-5386339' },
|
||||
{ label: '电子邮箱', value: 'websopy@163.com' },
|
||||
{ label: '官方网站', value: 'websoft.top / websopy.com' },
|
||||
].map((item, idx) => (
|
||||
<View key={idx} className='flex items-start justify-between'>
|
||||
<Text className='text-sm text-gray-500 flex-shrink-0'>{item.label}</Text>
|
||||
@@ -196,78 +204,131 @@ const AboutPage: React.FC = () => {
|
||||
|
||||
{/* 底部版权 */}
|
||||
<View className='flex flex-col items-center py-8' onClick={handleCopyrightClick}>
|
||||
<Text className='text-xs text-gray-400'>Copyright © 2024 威硕谱软件</Text>
|
||||
<Text className='text-xs text-gray-400 mt-1'>All Rights Reserved</Text>
|
||||
<Text className='text-xs text-gray-400'>Copyright © 2024 南宁市网宿信息科技有限公司</Text>
|
||||
<Text className='text-xs text-gray-400 mt-1'>威数谱软件 websopy · All Rights Reserved</Text>
|
||||
</View>
|
||||
|
||||
{/* 隐藏的开发者登录弹窗 */}
|
||||
{showLogin && (
|
||||
<View
|
||||
className='fixed inset-0 z-[9999] flex items-center justify-center'
|
||||
style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}
|
||||
style={{ backgroundColor: 'rgba(0,0,0,0.55)' }}
|
||||
onClick={handleCloseLogin}
|
||||
catchMove
|
||||
>
|
||||
<View
|
||||
className='rounded-2xl bg-white'
|
||||
style={{ width: '340px', padding: '24px 20px' }}
|
||||
className='rounded-3xl bg-white'
|
||||
style={{ width: '320px', padding: '32px 24px' }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<View className='mb-3 flex items-center justify-between'>
|
||||
<Text className='text-lg font-bold text-gray-800'>开发者登录</Text>
|
||||
{/* 标题栏 */}
|
||||
<View className='mb-2 flex items-center justify-between'>
|
||||
<Text className='text-xl font-bold text-gray-800'>开发者登录</Text>
|
||||
<Text
|
||||
className='text-lg text-gray-400'
|
||||
style={{ padding: '4px 8px' }}
|
||||
className='text-xl text-gray-300 leading-none cursor-pointer active:opacity-60'
|
||||
style={{ padding: '6px', lineHeight: 1 }}
|
||||
onClick={handleCloseLogin}
|
||||
>
|
||||
✕
|
||||
</Text>
|
||||
</View>
|
||||
<Text className='mb-5 block text-xs text-gray-400'>
|
||||
<Text className='mb-6 block text-xs text-gray-400 leading-relaxed'>
|
||||
仅限开发测试使用,通过手机号+验证码登录指定账号
|
||||
</Text>
|
||||
|
||||
<View className='mb-3'>
|
||||
{/* 手机号输入框 */}
|
||||
<View className='mb-4'>
|
||||
<Input
|
||||
type='number'
|
||||
placeholder='请输入手机号码'
|
||||
maxLength={11}
|
||||
value={formData.phone || ''}
|
||||
onChange={(val) => setFormData({ ...formData, phone: val })}
|
||||
style={{ backgroundColor: '#f5f5f5', borderRadius: '8px' }}
|
||||
style={{
|
||||
backgroundColor: '#fff5f5',
|
||||
borderRadius: '12px',
|
||||
height: '48px',
|
||||
paddingLeft: '16px',
|
||||
paddingRight: '16px',
|
||||
fontSize: '15px',
|
||||
border: '1.5px solid #e53e3e',
|
||||
boxSizing: 'border-box',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View className='mb-6 flex items-center bg-white rounded-lg pr-2'>
|
||||
<Input
|
||||
type='number'
|
||||
placeholder='请输入6位验证码'
|
||||
maxLength={6}
|
||||
value={formData.code || ''}
|
||||
onChange={(val) => setFormData({ ...formData, code: val })}
|
||||
style={{ backgroundColor: '#f5f5f5', borderRadius: '8px' }}
|
||||
/>
|
||||
{/* 验证码输入 + 获取按钮 */}
|
||||
<View className='mb-8 flex items-center gap-3'>
|
||||
<View className='flex-1'>
|
||||
<Input
|
||||
type='number'
|
||||
placeholder='请输入6位验证码'
|
||||
maxLength={6}
|
||||
value={formData.code || ''}
|
||||
onChange={(val) => setFormData({ ...formData, code: val })}
|
||||
style={{
|
||||
backgroundColor: '#fff5f5',
|
||||
borderRadius: '12px',
|
||||
height: '48px',
|
||||
paddingLeft: '16px',
|
||||
paddingRight: '12px',
|
||||
fontSize: '15px',
|
||||
border: '1.5px solid #e53e3e',
|
||||
boxSizing: 'border-box',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<Button
|
||||
size='small'
|
||||
type={countdown > 0 ? 'default' : 'primary'}
|
||||
loading={sendingCode}
|
||||
disabled={sendingCode || countdown > 0}
|
||||
onClick={handleSendCode}
|
||||
style={{
|
||||
height: '48px',
|
||||
minWidth: '110px',
|
||||
borderRadius: '12px',
|
||||
fontSize: '13px',
|
||||
fontWeight: 500,
|
||||
flexShrink: 0,
|
||||
...(countdown > 0 ? {
|
||||
backgroundColor: '#f0f0f0',
|
||||
color: '#999',
|
||||
} : {
|
||||
backgroundColor: '#e53e3e',
|
||||
borderColor: '#e53e3e',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{countdown > 0 ? `${countdown}s` : sendingCode ? '发送中' : '获取验证码'}
|
||||
{countdown > 0 ? `${countdown}s 后重发` : sendingCode ? '发送中' : '获取验证码'}
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
{/* 登录按钮 */}
|
||||
<View className='flex justify-center'>
|
||||
<Button
|
||||
block
|
||||
type='primary'
|
||||
size='large'
|
||||
className='w-full rounded-lg p-2'
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
onClick={handleLogin}
|
||||
style={{
|
||||
height: '50px',
|
||||
borderRadius: '14px',
|
||||
fontSize: '17px',
|
||||
fontWeight: 600,
|
||||
letterSpacing: '2px',
|
||||
background: loading
|
||||
? '#c0c0c0'
|
||||
: 'linear-gradient(135deg, #fc5c5c 0%, #e53e3e 100%)',
|
||||
border: 'none',
|
||||
boxShadow: loading
|
||||
? 'none'
|
||||
: '0 6px 20px rgba(229, 62, 62, 0.35)',
|
||||
}}
|
||||
>
|
||||
{loading ? '登录中...' : '登录'}
|
||||
{loading ? '登录中...' : '登 录'}
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -101,8 +101,8 @@ const UserPage: React.FC = () => {
|
||||
})
|
||||
|
||||
const menuItems = [
|
||||
// 升级VIP会员 - 醒目样式
|
||||
{ icon: '👑', label: '升级VIP会员', url: '/pages/user/vip-upgrade/index', highlight: true, requireAuth: true },
|
||||
// 申请成为开发者 - 醒目样式
|
||||
{ icon: '🔰', label: '申请成为开发者', url: '/pages/user/vip-upgrade/index', highlight: true, requireAuth: true },
|
||||
// 门店中心:仅门店店员/店长显示(通过 /shop/shop-store-user/my 判断)
|
||||
...(storeInfo ? [{ icon: '🏪', label: '门店中心', url: '/pages/store/center/index', requireAuth: true }] : []),
|
||||
{ icon: '💰', label: '我的钱包', url: '/pages/user/wallet', requireAuth: true },
|
||||
|
||||
@@ -9,7 +9,7 @@ import { TenantId } from '@/config/app'
|
||||
import { getMyClerk } from '@/api/shop/shopStoreUser'
|
||||
|
||||
definePageConfig({
|
||||
navigationBarTitleText: 'VIP会员审核',
|
||||
navigationBarTitleText: '开发者审核',
|
||||
})
|
||||
|
||||
// Tab 配置
|
||||
@@ -24,7 +24,7 @@ const TABS: { key: TabKey; label: string; status: number }[] = [
|
||||
// VIP 角色信息(对应 system/role 表)
|
||||
// 角色 ID 不写死,按 roleCode + 当前租户ID 动态查询
|
||||
const VIP_ROLE_CODE = 'developer'
|
||||
const VIP_ROLE_NAME = '认证开发者'
|
||||
const VIP_ROLE_NAME = '开发者'
|
||||
|
||||
// 获取当前租户 ID(登录时写入 storage,未登录时回退到默认租户)
|
||||
function getCurrentTenantId(): number | string | undefined {
|
||||
@@ -181,7 +181,7 @@ const VipReviewPage: React.FC = () => {
|
||||
const handleApprove = (item: ShopDealerApply) => {
|
||||
Taro.showModal({
|
||||
title: '确认通过',
|
||||
content: `确认通过「${item.realName || item.merchantName || '该用户'}」的VIP会员申请?\n\n请确认已实地考察核实门店名称和地址。`,
|
||||
content: `确认通过「${item.realName || item.merchantName || '该用户'}」的开发者申请?\n\n请确认已实地考察核实开发团队名称与公司地址。`,
|
||||
confirmColor: '#22c55e',
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return
|
||||
@@ -216,7 +216,7 @@ const VipReviewPage: React.FC = () => {
|
||||
const handleReject = (item: ShopDealerApply) => {
|
||||
Taro.showModal({
|
||||
title: '驳回申请',
|
||||
content: `确认驳回「${item.realName || item.merchantName || '该用户'}」的VIP会员申请?`,
|
||||
content: `确认驳回「${item.realName || item.merchantName || '该用户'}」的开发者申请?`,
|
||||
confirmColor: '#ef4444',
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return
|
||||
@@ -291,12 +291,12 @@ const VipReviewPage: React.FC = () => {
|
||||
{renderStatusTag(item.applyStatus!)}
|
||||
</View>
|
||||
|
||||
{/* 门店信息 */}
|
||||
{/* 开发团队及公司信息 */}
|
||||
<View className='bg-gray-50 rounded-lg p-3 mb-3'>
|
||||
<View className='flex items-start gap-2 mb-2'>
|
||||
<Text className='text-xs text-gray-400 mt-0.5'>🏪</Text>
|
||||
<View className='flex-1'>
|
||||
<Text className='text-xs text-gray-400 block'>门店名称</Text>
|
||||
<Text className='text-xs text-gray-400 block'>开发团队</Text>
|
||||
<Text className='text-sm text-gray-700 block mt-0.5'>
|
||||
{item.merchantName || item.realName || '-'}
|
||||
</Text>
|
||||
@@ -305,7 +305,7 @@ const VipReviewPage: React.FC = () => {
|
||||
<View className='flex items-start gap-2'>
|
||||
<Text className='text-xs text-gray-400 mt-0.5'>📍</Text>
|
||||
<View className='flex-1'>
|
||||
<Text className='text-xs text-gray-400 block'>门店地址</Text>
|
||||
<Text className='text-xs text-gray-400 block'>公司地址</Text>
|
||||
<Text className='text-sm text-gray-700 block mt-0.5'>
|
||||
{item.address || '-'}
|
||||
</Text>
|
||||
@@ -371,7 +371,7 @@ const VipReviewPage: React.FC = () => {
|
||||
<View className='flex-1'>
|
||||
<Text className='text-xs text-green-700 block font-medium'>审核说明</Text>
|
||||
<Text className='text-xs text-green-600 block mt-1 leading-relaxed'>
|
||||
请实地考察核实申请人的门店名称和地址是否与实际一致,确认无误后给予通过,升级为VIP会员。
|
||||
请实地考察核实申请人的开发团队名称与公司地址是否与实际一致,确认无误后给予通过,升级为开发者。
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
navigationBarTitleText: '升级VIP会员',
|
||||
navigationBarTitleText: '申请成为开发者',
|
||||
enableShareAppMessage: true,
|
||||
enableShareTimeline: true,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { View, Text, Input } from '@tarojs/components'
|
||||
import { View, Text, Input, Picker } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useUser } from '@/hooks/useUser'
|
||||
import { useShare } from '@/hooks/useShare'
|
||||
@@ -7,7 +7,7 @@ import { addShopDealerApply, listShopDealerApply } from '@/api/shop/shopDealerAp
|
||||
import type { ShopDealerApply } from '@/api/shop/shopDealerApply/model'
|
||||
|
||||
definePageConfig({
|
||||
navigationBarTitleText: '升级VIP会员',
|
||||
navigationBarTitleText: '申请成为开发者',
|
||||
enableShareAppMessage: true,
|
||||
enableShareTimeline: true,
|
||||
})
|
||||
@@ -16,11 +16,14 @@ const VipUpgradePage: React.FC = () => {
|
||||
const { user, isLoggedIn } = useUser()
|
||||
// 分享 / 朋友圈
|
||||
useShare({
|
||||
title: '升级VIP会员,享专属优惠价',
|
||||
title: '成为认证开发者,享专属权益',
|
||||
path: '/pages/user/vip-upgrade',
|
||||
})
|
||||
const [realName, setRealName] = useState('')
|
||||
const [address, setAddress] = useState('')
|
||||
// 所在城市(省市区三级,使用微信原生地区选择器)
|
||||
const [cityValue, setCityValue] = useState<string[]>([])
|
||||
const cityText = cityValue.join(' ')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [existingApply, setExistingApply] = useState<ShopDealerApply | null>(null)
|
||||
|
||||
@@ -30,7 +33,7 @@ const VipUpgradePage: React.FC = () => {
|
||||
return
|
||||
}
|
||||
if (user) {
|
||||
// 门店名称和地址统一从 shop-dealer-apply 表读取
|
||||
// 开发团队名称与公司地址统一从 shop-dealer-apply 表读取
|
||||
listShopDealerApply({ userId: (user as any)?.userId || (user as any)?.id })
|
||||
.then(data => {
|
||||
if (!data || data.length === 0) return
|
||||
@@ -40,6 +43,7 @@ const VipUpgradePage: React.FC = () => {
|
||||
setExistingApply(latest)
|
||||
setRealName(latest.realName || '')
|
||||
setAddress(latest.address || '')
|
||||
setCityValue(latest.city ? String(latest.city).split(' ').filter(Boolean) : [])
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
@@ -48,11 +52,15 @@ const VipUpgradePage: React.FC = () => {
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!realName.trim()) {
|
||||
Taro.showToast({ title: '请输入门店名称', icon: 'none' })
|
||||
Taro.showToast({ title: '请输入开发团队名称', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!cityText) {
|
||||
Taro.showToast({ title: '请选择所在城市', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!address.trim()) {
|
||||
Taro.showToast({ title: '请输入门店地址', icon: 'none' })
|
||||
Taro.showToast({ title: '请输入公司地址', icon: 'none' })
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
@@ -60,6 +68,7 @@ const VipUpgradePage: React.FC = () => {
|
||||
await addShopDealerApply({
|
||||
userId: (user as any)?.userId || (user as any)?.id,
|
||||
realName: realName.trim(),
|
||||
city: cityText,
|
||||
address: address.trim(),
|
||||
applyType: 10, // 需后台审核
|
||||
applyStatus: 10, // 待审核
|
||||
@@ -91,11 +100,11 @@ const VipUpgradePage: React.FC = () => {
|
||||
<View className='absolute -bottom-6 -left-6 w-24 h-24 rounded-full opacity-15' style={{ background: 'radial-gradient(circle, #ffffff, transparent)' }} />
|
||||
<View className='relative z-10 flex flex-col items-center'>
|
||||
<View className='w-16 h-16 rounded-full bg-white bg-opacity-20 flex items-center justify-center mb-3'>
|
||||
<Text className='text-3xl'>👑</Text>
|
||||
<Text className='text-3xl'>🔰</Text>
|
||||
</View>
|
||||
<Text className='text-white text-xl font-bold'>升级VIP会员</Text>
|
||||
<Text className='text-white text-xl font-bold'>申请开发者</Text>
|
||||
<Text className='text-white text-opacity-80 text-sm mt-1'>
|
||||
{existingApply?.applyStatus === 20 ? '恭喜您已是VIP会员' : '填写门店信息,申请VIP会员资格'}
|
||||
{existingApply?.applyStatus === 20 ? '恭喜您已成为开发者' : '填写开发团队及公司地址,申请成为开发者身份'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -115,8 +124,8 @@ const VipUpgradePage: React.FC = () => {
|
||||
</Text>
|
||||
<Text className='text-gray-500 text-xs mt-1 block leading-relaxed'>
|
||||
{existingApply.applyStatus === 10
|
||||
? '您的VIP申请正在审核中,请耐心等待。审核结果将通过消息通知您。'
|
||||
: '恭喜!您的VIP会员申请已审核通过,现在可以享受全部VIP专属权益。'}
|
||||
? '您的开发者申请正在审核中,请耐心等待。审核结果将通过消息通知您。'
|
||||
: '恭喜!您的开发者申请已审核通过,现在可以享受全部开发者专属权益。'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -133,7 +142,7 @@ const VipUpgradePage: React.FC = () => {
|
||||
<View className='flex-1'>
|
||||
<Text className='text-gray-800 text-sm font-medium block'>温馨提示</Text>
|
||||
<Text className='text-gray-500 text-xs mt-1 block leading-relaxed'>
|
||||
请填写您的门店信息,提交后管理员将进行审核。审核通过后即可享受VIP会员专属权益,包括更高佣金、专属折扣等特权。
|
||||
请填写您的开发团队及公司地址信息,提交后管理员将进行审核。审核通过后即可享受开发者专属权益,包括更高佣金、专属折扣等特权。
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -142,12 +151,12 @@ const VipUpgradePage: React.FC = () => {
|
||||
|
||||
{/* 表单 */}
|
||||
<View className={`${existingApply ? 'mt-3' : ''} mx-3 mt-3 bg-white rounded-xl overflow-hidden`}>
|
||||
{/* 门店名称 */}
|
||||
{/* 开发团队 */}
|
||||
<View className='px-4 py-4 border-b border-gray-50'>
|
||||
<Text className='text-sm text-gray-700 font-medium block mb-2'>门店名称 <Text className='text-red-500'>*</Text></Text>
|
||||
<Text className='text-sm text-gray-700 font-medium block mb-2'>开发团队 <Text className='text-red-500'>*</Text></Text>
|
||||
<Input
|
||||
className='w-full text-sm text-gray-800 bg-gray-50 rounded-lg px-3 py-3'
|
||||
placeholder='请输入您的门店名称'
|
||||
placeholder='请输入您的开发团队名称'
|
||||
value={realName}
|
||||
onInput={(e: any) => setRealName(e.detail.value)}
|
||||
maxlength={50}
|
||||
@@ -155,12 +164,25 @@ const VipUpgradePage: React.FC = () => {
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 门店地址 */}
|
||||
{/* 所在城市 */}
|
||||
<Picker mode='region' value={cityValue} onChange={(e: any) => setCityValue(e.detail.value || [])} disabled={isReadonly}>
|
||||
<View className='px-4 py-4 border-b border-gray-50'>
|
||||
<Text className='text-sm text-gray-700 font-medium block mb-2'>所在城市 <Text className='text-red-500'>*</Text></Text>
|
||||
<View className='w-full text-sm bg-gray-50 rounded-lg px-3 py-3 flex items-center justify-between'>
|
||||
<Text className={cityText ? 'text-gray-800' : 'text-gray-400'}>
|
||||
{cityText || '请选择所在城市'}
|
||||
</Text>
|
||||
<Text className='text-gray-300 text-xs'>▶</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Picker>
|
||||
|
||||
{/* 公司地址 */}
|
||||
<View className='px-4 py-4'>
|
||||
<Text className='text-sm text-gray-700 font-medium block mb-2'>门店地址 <Text className='text-red-500'>*</Text></Text>
|
||||
<Text className='text-sm text-gray-700 font-medium block mb-2'>办公地址 <Text className='text-red-500'>*</Text></Text>
|
||||
<Input
|
||||
className='w-full text-sm text-gray-800 bg-gray-50 rounded-lg px-3 py-3'
|
||||
placeholder='请输入您的门店详细地址'
|
||||
placeholder='请输入您的办公地址'
|
||||
value={address}
|
||||
onInput={(e: any) => setAddress(e.detail.value)}
|
||||
maxlength={100}
|
||||
@@ -169,15 +191,15 @@ const VipUpgradePage: React.FC = () => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* VIP权益预览 */}
|
||||
{/* 开发者权益预览 */}
|
||||
<View className='mx-3 mt-4'>
|
||||
<Text className='text-base font-medium text-gray-800 mb-3 block'>VIP会员专属权益</Text>
|
||||
<Text className='text-base font-medium text-gray-800 mb-3 block'>开发者专属权益</Text>
|
||||
<View className='grid grid-cols-2 gap-3'>
|
||||
{[
|
||||
{ icon: '💎', title: '更高佣金', desc: '享受高额团队佣金' },
|
||||
{ icon: '🎁', title: '专属折扣', desc: '购物享VIP专属价' },
|
||||
{ icon: '🎁', title: '专属折扣', desc: '购物享开发者专属价' },
|
||||
{ icon: '⚡', title: '优先发货', desc: '订单优先处理配送' },
|
||||
{ icon: '🎧', title: '专属客服', desc: '一对一VIP服务' },
|
||||
{ icon: '🎧', title: '专属客服', desc: '一对一开发者服务' },
|
||||
].map(item => (
|
||||
<View key={item.title} className='bg-white rounded-xl p-4 flex items-center gap-3'>
|
||||
<View className='w-10 h-10 rounded-full bg-green-50 flex items-center justify-center flex-shrink-0'>
|
||||
|
||||
Reference in New Issue
Block a user