zjk
18 小时以前 04756d85147a249adf8f2b9cdf9b1a52c3f9b954
feat(contact): 新增联系我们页动态获取联系方式功能

后端新增联系方式查询接口与ContactInfoVo类,从平台设置表获取对应配置数据
前端封装api调用方法,优化联系我们页面逻辑,支持未登录跳过请求、请求失败使用默认兜底文案
更新设计文档对应章节的实现说明
2个文件已添加
3个文件已修改
151 ■■■■■ 已修改文件
doc/design/用户端_首页与导航_20261012.md 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hugray-server/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysContactInfoController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hugray-server/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ContactInfoVo.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hugray-user-miniapp/api/index.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hugray-user-miniapp/pages/contact/index.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/design/用户端_首页与导航_20261012.md
@@ -317,7 +317,7 @@
| 联系方式列表 | x20,列表项纵向间距 44 | `.info-list`(`padding: 140rpx 40rpx 0`) |
| 单行信息 | 标签列宽 110pt,右侧数值 | `.info-label` 16pt 600 `#1F2937`;`.info-value` 15pt `#6B7280`;固定标签列宽保证各行数值纵向对齐 |
| 平台客服按钮 | h30 R15,**pen 标准「微信按钮 WeChat」** 渐变(90°)`#07C160→#05A352` + 投影 `#07C16040` | `<button open-type="contact">`(微信客服会话)+ 内嵌 `/static/wechat-icon.png` + 白字 13pt 600;`::after { border: none }` 重置小程序默认边框 |
| 热线文案 | `城市招商热线` / `合伙人招募` 均 `400-451-666` | 客服接口未就绪,先以文件内常量 `CITY_INVEST_HOTLINE` / `PARTNER_RECRUIT_HOTLINE` 维护,接口就绪后替换 |
| 热线文案 | `城市招商热线` / `合伙人招募` 取自平台设置 | 进入页面调 `getContactInfo()`(`GET /miniapp/system/contact-info`)回填 `cityHotline` / `partnerRecruit`;后端未配置或请求失败时回退文件内常量 `CITY_INVEST_HOTLINE` / `PARTNER_RECRUIT_HOTLINE`(`400-451-666`)。未登录(无 token)时跳过请求直接用兜底常量,避免在公开页触发 401 跳登录 |
| 返回行为 | `uni.navigateBack()` | 无上级页面(如直接进入)时兜底 `uni.switchTab('/pages/profile/index')` |
### 9.7 选择地址页(`pages/address/list.vue`)
hugray-server/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysContactInfoController.java
New file
@@ -0,0 +1,52 @@
package com.ruoyi.system.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.domain.SysPlatformSetting;
import com.ruoyi.system.domain.vo.ContactInfoVo;
import com.ruoyi.system.service.ISysPlatformSettingService;
/**
 * C端「联系我们」联系方式 查询接口
 *
 * 数据源自平台设置单行表 sys_platform_setting(由管理后台「平台设置」维护),
 * 此处只读暴露其中的联系方式(招商热线 / 合伙人招募)供小程序「联系我们」页回填。
 *
 * 网关路由 /miniapp/system/** 经 StripPrefix=2 剥掉前两段后转发到本服务,
 * 故外部实际路径为 /miniapp/system/contact-info;与后台 /system/platform 不冲突。
 *
 * 未配置权限注解:这是 C 端只读接口,由网关统一鉴权,不走后台菜单权限体系。
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/contact-info")
public class SysContactInfoController
{
    @Autowired
    private ISysPlatformSettingService platformSettingService;
    /**
     * 查询联系方式(招商热线 / 合伙人招募)
     *
     * 平台设置从未保存过时(表无行)返回两个字段均为 null 的对象,
     * 由小程序端回退到默认文案,不做 500 处理。
     *
     * @return 联系方式(cityHotline / partnerRecruit)
     */
    @GetMapping
    public R<ContactInfoVo> getContactInfo()
    {
        ContactInfoVo vo = new ContactInfoVo();
        SysPlatformSetting setting = platformSettingService.selectPlatformSetting();
        if (setting != null)
        {
            vo.setCityHotline(setting.getCityHotline());
            vo.setPartnerRecruit(setting.getPartnerRecruit());
        }
        return R.ok(vo);
    }
}
hugray-server/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ContactInfoVo.java
New file
@@ -0,0 +1,42 @@
package com.ruoyi.system.domain.vo;
import java.io.Serializable;
/**
 * C端「联系我们」联系方式 出参
 *
 * 只暴露联系方式两项,不返回平台设置中的其它内部参数(如订单取消时长、售后天数等),
 * 避免把仅后端使用的配置泄露给小程序端。
 *
 * @author ruoyi
 */
public class ContactInfoVo implements Serializable
{
    private static final long serialVersionUID = 1L;
    /** 城市招商热线(取自平台设置 city_hotline;未配置为 null) */
    private String cityHotline;
    /** 合伙人招募(取自平台设置 partner_recruit;未配置为 null) */
    private String partnerRecruit;
    public String getCityHotline()
    {
        return cityHotline;
    }
    public void setCityHotline(String cityHotline)
    {
        this.cityHotline = cityHotline;
    }
    public String getPartnerRecruit()
    {
        return partnerRecruit;
    }
    public void setPartnerRecruit(String partnerRecruit)
    {
        this.partnerRecruit = partnerRecruit;
    }
}
hugray-user-miniapp/api/index.js
@@ -241,6 +241,16 @@
    return del('/miniapp/account/user/addresses/' + id)
}
// ======================== 系统信息 ========================
/**
 * 查询联系方式(「联系我们」页回填用)
 * 返回 data 含 { cityHotline, partnerRecruit },未配置时对应字段为 null
 */
export function getContactInfo() {
    return get('/miniapp/system/contact-info')
}
// ======================== 订单相关 ========================
/**
hugray-user-miniapp/pages/contact/index.vue
@@ -18,7 +18,7 @@
        <!-- 联系方式列表:热线为纯展示文案,平台客服为绿色胶囊按钮 -->
        <view class="info-list">
            <view v-for="item in CONTACT_LIST" :key="item.key" class="info-row">
            <view v-for="item in contactList" :key="item.key" class="info-row">
                <text class="info-label">{{ item.label }}</text>
                <!-- 有 value 渲染文案,否则渲染「联系客服」按钮 -->
                <text v-if="item.value" class="info-value">{{ item.value }}</text>
@@ -32,19 +32,52 @@
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getToken } from '@/utils/request.js'
import { getContactInfo } from '@/api/index.js'
/** 状态栏高度:自定义导航栏下用于顶部留白 */
const statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 0
/** 招商 / 合伙人热线:客服接口未就绪,先以常量维护(接口就绪后替换) */
const CITY_INVEST_HOTLINE = '400-451-666'
const PARTNER_RECRUIT_HOTLINE = '400-451-666'
/** 招商 / 合伙人热线兜底文案:后端未配置或请求失败时使用 */
const CITY_INVEST_HOTLINE = '-'
const PARTNER_RECRUIT_HOTLINE = '-'
/** 联系方式列表:value 为空表示该项渲染为客服按钮 */
const CONTACT_LIST = [
const contactList = ref([
    { key: 'invest', label: '城市招商热线', value: CITY_INVEST_HOTLINE },
    { key: 'partner', label: '合伙人招募', value: PARTNER_RECRUIT_HOTLINE },
    { key: 'service', label: '平台客服' }
]
])
/** 单项回填:后端有值才覆盖,为空则保留兜底文案 */
const applyHotline = (key, value) => {
    if (!value) return
    const item = contactList.value.find((it) => it.key === key)
    if (item) item.value = value
}
/**
 * 拉取平台联系方式并回填
 * 未登录(无 token)时跳过请求,直接使用兜底文案,避免在此页触发 401 跳登录;
 * 请求失败仅记录日志,不影响页面正常展示
 */
const loadContactInfo = async () => {
    if (!getToken()) return
    try {
        const res = await getContactInfo()
        const data = res.data || {}
        applyHotline('invest', data.cityHotline)
        applyHotline('partner', data.partnerRecruit)
    } catch (e) {
        console.error('[CONTACT_INFO] 获取联系方式失败,使用默认文案', e)
    }
}
onLoad(() => {
    loadContactInfo()
})
/** 返回上一页:无上级页面(如直接进入)时回到个人中心 */
const handleBack = () => {