feat(v3.4.14): mini-user store UX, brand config, client settings

Store list/detail package flow, configurable WeChat mini brand assets and customer service, shop H5 home refresh.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-06 15:21:59 +08:00
parent 53a79d1255
commit a2abbb6169
29 changed files with 1408 additions and 513 deletions
@@ -1,6 +1,10 @@
import { CUSTOMER_SERVICE_PHONE } from '@dukang/shared-types';
import { useEffect, useState } from 'react';
import { track } from '../lib/analytics';
import { openWecomCustomerService } from '../lib/customer-service';
import {
getCustomerServicePhone,
loadCustomerServicePhone,
openWecomCustomerService,
} from '../lib/customer-service';
type ContactCustomerSheetProps = {
orderId?: string;
@@ -9,7 +13,13 @@ type ContactCustomerSheetProps = {
};
export default function ContactCustomerSheet({ orderId, onClose }: ContactCustomerSheetProps) {
const tel = CUSTOMER_SERVICE_PHONE.replace(/-/g, '');
const [phone, setPhone] = useState(getCustomerServicePhone);
useEffect(() => {
void loadCustomerServicePhone().then(setPhone);
}, []);
const tel = phone.replace(/-/g, '');
function openPhone() {
track('cs_contact', { type: 'phone', orderId });
@@ -41,7 +51,7 @@ export default function ContactCustomerSheet({ orderId, onClose }: ContactCustom
</div>
<div className="contact-customer-option-body">
<p className="contact-customer-option-title"></p>
<p className="contact-customer-option-sub">{CUSTOMER_SERVICE_PHONE}</p>
<p className="contact-customer-option-sub">{phone}</p>
</div>
<span className="material-symbols-outlined contact-customer-chevron">chevron_right</span>
</button>
+20
View File
@@ -1,12 +1,31 @@
import { CUSTOMER_SERVICE_PHONE, CUSTOMER_SERVICE_WECOM_URL } from '@dukang/shared-types';
import { fetchClientConfig } from './pay-wechat';
import { isWechatEnv } from './weixin';
let cachedPhone = CUSTOMER_SERVICE_PHONE;
/** 企微客服链接:优先 Vite env,否则 shared-types 默认 */
export function getCustomerServiceWecomUrl(): string {
const fromEnv = import.meta.env.VITE_CS_WECOM_URL?.trim();
return fromEnv || CUSTOMER_SERVICE_WECOM_URL;
}
export function getCustomerServicePhone(): string {
return cachedPhone;
}
/** 从系统设置拉取客服电话(失败则保持默认常量) */
export async function loadCustomerServicePhone(): Promise<string> {
try {
const cfg = await fetchClientConfig();
const phone = cfg.customerServicePhone?.trim();
if (phone) cachedPhone = phone;
} catch {
/* keep fallback */
}
return cachedPhone;
}
/**
* 打开企业微信客服会话(须在微信内;需用户点击手势)。
* @returns true 已跳转;false 非微信环境已提示
@@ -20,4 +39,5 @@ export function openWecomCustomerService(): boolean {
return true;
}
/** @deprecated 请用 getCustomerServicePhone(),保留兼容旧引用 */
export { CUSTOMER_SERVICE_PHONE };
+14 -3
View File
@@ -1,11 +1,22 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import SubPageHeader from '../components/SubPageHeader';
import { CUSTOMER_SERVICE_PHONE, openWecomCustomerService } from '../lib/customer-service';
import {
getCustomerServicePhone,
loadCustomerServicePhone,
openWecomCustomerService,
} from '../lib/customer-service';
import { track } from '../lib/analytics';
export default function CustomerServicePage() {
const navigate = useNavigate();
const tel = CUSTOMER_SERVICE_PHONE.replace(/-/g, '');
const [phone, setPhone] = useState(getCustomerServicePhone);
useEffect(() => {
void loadCustomerServicePhone().then(setPhone);
}, []);
const tel = phone.replace(/-/g, '');
function openOnline() {
track('cs_contact', { type: 'wecom_kf' });
@@ -27,7 +38,7 @@ export default function CustomerServicePage() {
<a className="customer-service-phone-link" href={`tel:${tel}`}>
<span className="material-symbols-outlined">call</span>
{CUSTOMER_SERVICE_PHONE}
{phone}
</a>
</div>
</div>