merge(dev_jacy): mini-user address/share/input UX fixes
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
.share-guide {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 10000;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
padding: 12px 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.share-guide__arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-right: 18px;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-bottom: 14px solid #fff;
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.12));
|
||||
}
|
||||
|
||||
.share-guide__card {
|
||||
margin-top: 0;
|
||||
margin-right: 8px;
|
||||
max-width: 260px;
|
||||
padding: 16px 18px;
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.share-guide__title {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.share-guide__desc {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.share-guide__ok {
|
||||
display: block;
|
||||
margin-top: 14px;
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #a61d24;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { Button, Text, View } from '@tarojs/components';
|
||||
import { handleShareButtonClick, type PageSharePayload } from '../lib/wechat-share';
|
||||
import './ShareGuide.css';
|
||||
|
||||
type ShareNavButtonProps = {
|
||||
payload?: PageSharePayload;
|
||||
@@ -8,9 +10,11 @@ type ShareNavButtonProps = {
|
||||
/**
|
||||
* 顶栏分享:
|
||||
* - 小程序:open-type=share 弹出微信分享面板
|
||||
* - H5 微信:JSSDK 配置后提示点右上角 ···
|
||||
* - H5 微信:JSSDK share / 右上角引导蒙层
|
||||
*/
|
||||
export default function ShareNavButton({ payload }: ShareNavButtonProps) {
|
||||
const [guideVisible, setGuideVisible] = useState(false);
|
||||
|
||||
if (process.env.TARO_ENV === 'weapp') {
|
||||
return (
|
||||
<Button className="page-nav-bar__btn page-nav-bar__share-btn" openType="share" hoverClass="none">
|
||||
@@ -20,13 +24,27 @@ export default function ShareNavButton({ payload }: ShareNavButtonProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
className="page-nav-bar__btn"
|
||||
onClick={() => {
|
||||
void handleShareButtonClick(payload);
|
||||
}}
|
||||
>
|
||||
<Text className="page-nav-bar__icon page-nav-bar__icon--share">⤴</Text>
|
||||
</View>
|
||||
<>
|
||||
<View
|
||||
className="page-nav-bar__btn"
|
||||
onClick={() => {
|
||||
void handleShareButtonClick(payload).then((res) => {
|
||||
if (res.showGuide) setGuideVisible(true);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Text className="page-nav-bar__icon page-nav-bar__icon--share">⤴</Text>
|
||||
</View>
|
||||
{guideVisible ? (
|
||||
<View className="share-guide" onClick={() => setGuideVisible(false)}>
|
||||
<View className="share-guide__arrow" />
|
||||
<View className="share-guide__card">
|
||||
<Text className="share-guide__title">分享给好友</Text>
|
||||
<Text className="share-guide__desc">请点击右上角 ··· 选择「发送给朋友」或「分享到朋友圈」</Text>
|
||||
<Text className="share-guide__ok">我知道了</Text>
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { toast } from './api';
|
||||
import { isWechatEnv, weixinSdk } from './weixin';
|
||||
|
||||
export const DEFAULT_SHARE_TITLE = '你吃饭,杜康买单';
|
||||
export const DEFAULT_SHARE_DESC = '杜康好客 · 买酒享权益,全城门店可用';
|
||||
export const DEFAULT_SHARE_DESC = '杜康好客 · 买美酒,享好礼,全城门店可用';
|
||||
export const WECHAT_SHARE_HINT = '请点击右上角 ··· 分享给好友';
|
||||
|
||||
export function getDefaultShareImageUrl(): string {
|
||||
@@ -51,14 +51,17 @@ export type PageSharePayload = {
|
||||
link?: string;
|
||||
};
|
||||
|
||||
/** 点击「分享」按钮 */
|
||||
export async function handleShareButtonClick(payload?: PageSharePayload): Promise<void> {
|
||||
// 小程序:由 Button open-type="share" 触发,无需在此拉起
|
||||
/**
|
||||
* 点击「分享」按钮。
|
||||
* H5:走微信 JSSDK 配置分享卡片,并尝试 invoke 调起面板;否则返回需展示引导蒙层。
|
||||
*/
|
||||
export async function handleShareButtonClick(
|
||||
payload?: PageSharePayload,
|
||||
): Promise<{ showGuide: boolean }> {
|
||||
if (process.env.TARO_ENV === 'weapp' || isMiniProgram()) {
|
||||
try {
|
||||
await Taro.showShareMenu({ withShareTicket: true, showShareItems: ['shareAppMessage', 'shareTimeline'] });
|
||||
} catch {
|
||||
/* 旧基础库可能不支持 showShareItems */
|
||||
try {
|
||||
await Taro.showShareMenu({ withShareTicket: true });
|
||||
} catch {
|
||||
@@ -66,24 +69,31 @@ export async function handleShareButtonClick(payload?: PageSharePayload): Promis
|
||||
}
|
||||
}
|
||||
toast(WECHAT_SHARE_HINT);
|
||||
return;
|
||||
return { showGuide: false };
|
||||
}
|
||||
|
||||
if (!isWechatEnv()) {
|
||||
toast('请在微信内打开后分享');
|
||||
return;
|
||||
return { showGuide: false };
|
||||
}
|
||||
|
||||
const data = buildDefaultShareData({
|
||||
title: payload?.title,
|
||||
desc: payload?.desc,
|
||||
imgUrl: payload?.imgUrl,
|
||||
link: payload?.link,
|
||||
});
|
||||
|
||||
try {
|
||||
await applyWechatShare({
|
||||
title: payload?.title,
|
||||
desc: payload?.desc,
|
||||
imgUrl: payload?.imgUrl,
|
||||
link: payload?.link,
|
||||
});
|
||||
const result = await weixinSdk.share(data);
|
||||
if (result.invoked) {
|
||||
return { showGuide: false };
|
||||
}
|
||||
toast(WECHAT_SHARE_HINT);
|
||||
} catch {
|
||||
toast('分享配置失败,请刷新后重试');
|
||||
return { showGuide: true };
|
||||
} catch (e) {
|
||||
toast(e instanceof Error ? e.message : '分享配置失败,请刷新后重试');
|
||||
return { showGuide: true };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,12 +166,26 @@ export default function AddressEditPage() {
|
||||
</View>
|
||||
<View className="address-form-field">
|
||||
<Text className="address-form-label">详细地址</Text>
|
||||
<Textarea
|
||||
className="address-form-textarea"
|
||||
placeholder="街道门牌号等"
|
||||
value={form.detail}
|
||||
onInput={(e) => setForm((prev) => ({ ...prev, detail: e.detail.value }))}
|
||||
/>
|
||||
{process.env.TARO_ENV === 'h5' ? (
|
||||
<textarea
|
||||
className="address-form-textarea address-form-textarea--native"
|
||||
placeholder="街道门牌号等"
|
||||
rows={3}
|
||||
value={form.detail}
|
||||
onChange={(e) => {
|
||||
const value = e.currentTarget.value;
|
||||
setForm((prev) => ({ ...prev, detail: value }));
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Textarea
|
||||
className="address-form-textarea"
|
||||
placeholder="街道门牌号等"
|
||||
value={form.detail}
|
||||
maxlength={200}
|
||||
onInput={(e) => setForm((prev) => ({ ...prev, detail: e.detail.value }))}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<View className="address-form-row">
|
||||
<Text>设为默认地址</Text>
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
min-height: 88px;
|
||||
padding: 12px 14px;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-card);
|
||||
box-shadow: var(--shadow-card);
|
||||
@@ -134,6 +135,14 @@
|
||||
color: var(--color-on-surface);
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
resize: vertical;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
/* H5 原生 textarea:避免 Taro Textarea 受控输入卡死 */
|
||||
.address-form-textarea--native {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.address-form-textarea textarea,
|
||||
@@ -144,11 +153,13 @@
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
outline: none !important;
|
||||
background: transparent !important;
|
||||
box-sizing: border-box !important;
|
||||
font-size: 15px !important;
|
||||
line-height: 1.5 !important;
|
||||
color: inherit;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.address-form-row {
|
||||
|
||||
@@ -43,8 +43,31 @@
|
||||
height: 52px;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 52px;
|
||||
color: var(--color-heritage-red);
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.redeem-input input,
|
||||
.redeem-input .taro-input,
|
||||
.redeem-input .weui-input {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
box-sizing: border-box !important;
|
||||
font-size: 24px !important;
|
||||
font-weight: 700 !important;
|
||||
line-height: normal !important;
|
||||
color: inherit;
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.redeem-tips {
|
||||
|
||||
@@ -43,8 +43,27 @@
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface-container-low);
|
||||
font-size: 13px;
|
||||
line-height: 40px;
|
||||
color: var(--color-on-surface);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.store-search input,
|
||||
.store-search .taro-input,
|
||||
.store-search .weui-input {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
min-height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
box-sizing: border-box !important;
|
||||
font-size: 13px !important;
|
||||
line-height: normal !important;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.store-category-tabs {
|
||||
|
||||
@@ -26,6 +26,8 @@ export {
|
||||
setWechatShareData,
|
||||
canUseWechatShare,
|
||||
getWechatShareLink,
|
||||
shareViaWechatSdk,
|
||||
tryInvokeSharePanel,
|
||||
} from './share';
|
||||
export type { WechatShareData } from './share';
|
||||
export {
|
||||
@@ -48,7 +50,7 @@ import { getWechatLocation, getWechatLocationDetailed } from './location';
|
||||
import { scanQrCode } from './scan';
|
||||
import { invokeWechatPay } from './pay';
|
||||
import { chooseWechatImages } from './chooseImage';
|
||||
import { setWechatShareData } from './share';
|
||||
import { setWechatShareData, shareViaWechatSdk } from './share';
|
||||
import {
|
||||
wechatLogin,
|
||||
handleWechatOAuthCallback,
|
||||
@@ -79,5 +81,6 @@ export function createWeixinSdk(config: WeixinSdkConfig) {
|
||||
}),
|
||||
setShare: (data: Parameters<typeof setWechatShareData>[1]) =>
|
||||
setWechatShareData(config, data),
|
||||
share: (data: Parameters<typeof shareViaWechatSdk>[1]) => shareViaWechatSdk(config, data),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,13 +15,24 @@ export function canUseWechatShare(): boolean {
|
||||
|
||||
function applyShareData(data: WechatShareData): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const wx = window.wx;
|
||||
if (!wx?.updateAppMessageShareData || !wx.updateTimelineShareData) {
|
||||
reject(new Error('当前微信版本不支持分享,请升级微信后重试'));
|
||||
const wx = window.wx as
|
||||
| (NonNullable<typeof window.wx> & {
|
||||
onMenuShareAppMessage?: (opts: Record<string, unknown>) => void;
|
||||
onMenuShareTimeline?: (opts: Record<string, unknown>) => void;
|
||||
invoke?: (
|
||||
api: string,
|
||||
params: Record<string, unknown>,
|
||||
cb?: (res: { err_msg?: string; errMsg?: string }) => void,
|
||||
) => void;
|
||||
})
|
||||
| undefined;
|
||||
|
||||
if (!wx) {
|
||||
reject(new Error('微信 JSSDK 不可用'));
|
||||
return;
|
||||
}
|
||||
|
||||
let pending = 2;
|
||||
let pending = 0;
|
||||
let failed = false;
|
||||
const done = (err?: Error) => {
|
||||
if (err && !failed) {
|
||||
@@ -33,22 +44,95 @@ function applyShareData(data: WechatShareData): Promise<void> {
|
||||
if (pending === 0 && !failed) resolve();
|
||||
};
|
||||
|
||||
wx.updateAppMessageShareData({
|
||||
const payload = {
|
||||
title: data.title,
|
||||
desc: data.desc,
|
||||
link: data.link,
|
||||
imgUrl: data.imgUrl,
|
||||
success: () => done(),
|
||||
fail: (res) => done(new Error(res.errMsg || '设置分享给朋友失败')),
|
||||
});
|
||||
};
|
||||
|
||||
wx.updateTimelineShareData({
|
||||
title: data.title,
|
||||
link: data.link,
|
||||
imgUrl: data.imgUrl,
|
||||
success: () => done(),
|
||||
fail: (res) => done(new Error(res.errMsg || '设置分享到朋友圈失败')),
|
||||
});
|
||||
if (wx.updateAppMessageShareData) {
|
||||
pending += 1;
|
||||
wx.updateAppMessageShareData({
|
||||
...payload,
|
||||
success: () => done(),
|
||||
fail: (res) => done(new Error(res.errMsg || '设置分享给朋友失败')),
|
||||
});
|
||||
}
|
||||
|
||||
if (wx.updateTimelineShareData) {
|
||||
pending += 1;
|
||||
wx.updateTimelineShareData({
|
||||
title: data.title,
|
||||
link: data.link,
|
||||
imgUrl: data.imgUrl,
|
||||
success: () => done(),
|
||||
fail: (res) => done(new Error(res.errMsg || '设置分享到朋友圈失败')),
|
||||
});
|
||||
}
|
||||
|
||||
// 兼容旧微信:同时挂旧版菜单分享(设置即生效,无 success 回调也可继续)
|
||||
if (wx.onMenuShareAppMessage) {
|
||||
wx.onMenuShareAppMessage({
|
||||
...payload,
|
||||
success: () => {},
|
||||
cancel: () => {},
|
||||
});
|
||||
}
|
||||
if (wx.onMenuShareTimeline) {
|
||||
wx.onMenuShareTimeline({
|
||||
title: data.title,
|
||||
link: data.link,
|
||||
imgUrl: data.imgUrl,
|
||||
success: () => {},
|
||||
cancel: () => {},
|
||||
});
|
||||
}
|
||||
|
||||
if (pending === 0) {
|
||||
if (wx.onMenuShareAppMessage || wx.onMenuShareTimeline) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
reject(new Error('当前微信版本不支持分享,请升级微信后重试'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 尽量调起系统分享面板(部分微信版本支持;失败则仅完成卡片配置) */
|
||||
export function tryInvokeSharePanel(data: WechatShareData): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const wx = window.wx as
|
||||
| {
|
||||
invoke?: (
|
||||
api: string,
|
||||
params: Record<string, unknown>,
|
||||
cb?: (res: { err_msg?: string; errMsg?: string }) => void,
|
||||
) => void;
|
||||
}
|
||||
| undefined;
|
||||
if (!wx?.invoke) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
wx.invoke(
|
||||
'shareAppMessage',
|
||||
{
|
||||
title: data.title,
|
||||
desc: data.desc,
|
||||
link: data.link,
|
||||
img_url: data.imgUrl,
|
||||
imgUrl: data.imgUrl,
|
||||
},
|
||||
(res) => {
|
||||
const msg = `${res?.err_msg || res?.errMsg || ''}`;
|
||||
resolve(/:ok\b/i.test(msg));
|
||||
},
|
||||
);
|
||||
} catch {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,6 +152,19 @@ export async function setWechatShareData(
|
||||
await applyShareData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置分享并尝试调起分享面板。
|
||||
* @returns invoked=true 表示已弹出系统分享;否则需引导用户点右上角
|
||||
*/
|
||||
export async function shareViaWechatSdk(
|
||||
config: WeixinSdkConfig,
|
||||
data: WechatShareData,
|
||||
): Promise<{ invoked: boolean }> {
|
||||
await setWechatShareData(config, data);
|
||||
const invoked = await tryInvokeSharePanel(data);
|
||||
return { invoked };
|
||||
}
|
||||
|
||||
/** 获取当前页分享链接(去掉 hash、OAuth 回跳参数) */
|
||||
export function getWechatShareLink(rawUrl?: string): string {
|
||||
if (typeof window === 'undefined') return rawUrl ?? '';
|
||||
|
||||
@@ -122,4 +122,6 @@ export const DEFAULT_JS_API_LIST = [
|
||||
'getLocalImgData',
|
||||
'updateAppMessageShareData',
|
||||
'updateTimelineShareData',
|
||||
'onMenuShareAppMessage',
|
||||
'onMenuShareTimeline',
|
||||
] as const;
|
||||
|
||||
Reference in New Issue
Block a user