refactor(mp-harmony): 简化店铺卡片逻辑,统一使用应用名作为卡片名称

1. 移除ShopInfo接口和默认数据中的shopName字段,不再返回店铺名称
2. 重构WidgetCard页面布局,移除营业状态显示和底部快捷按钮
3. 重构EntryFormAbility,仅保留2x2店铺卡片,使用应用资源名作为卡片名称
4. 注释暂时保留的店铺名称解析逻辑,后续需要时可重新启用
This commit is contained in:
2026-07-08 15:50:46 +08:00
parent fd4fd61399
commit a7dd0b302e
3 changed files with 82 additions and 170 deletions

View File

@@ -8,13 +8,11 @@ const CACHE_KEY_SHOP = 'shop_info_v1';
const CACHE_TTL_MS = 30 * 60 * 1000;
export interface ShopInfo {
shopName: string;
shopLogo: string;
shopStatus: 'open' | 'closed';
}
const DEFAULT_SHOP_INFO: ShopInfo = {
shopName: '店铺',
shopLogo: '',
shopStatus: 'open'
};
@@ -70,8 +68,9 @@ function parseShopInfo(shopInfoRaw: Object | undefined, siteInfoRaw: Object | un
const shopObj: ShopInfoRaw = (shopInfoRaw as ShopInfoRaw) ?? {};
const siteObj: ShopInfoRaw = (siteInfoRaw as ShopInfoRaw) ?? {};
// 店铺名称:优先 shop_info.name,兜底 site_info.site_name
const name: string = shopObj.name ?? siteObj.site_name ?? shopObj.shop_name ?? DEFAULT_SHOP_INFO.shopName;
// Soft-cut: 保留 shopName 解析逻辑(不返回),未来需要"卡片副标题"或"真实店名"时直接 re-enable
// 优先级: data.shop_info.name > data.site_info.site_name > data.shop_info.shop_name
const _shopName: string = shopObj.name ?? siteObj.site_name ?? shopObj.shop_name ?? '';
// logo:优先 site_info.logo(完整URL),兜底 shop_info.avatar
const logo: string = siteObj.logo ?? shopObj.avatar ?? shopObj.shop_logo ?? shopObj.image ?? '';
// 营业状态:shop_status (1=营业, 0=休息)
@@ -80,7 +79,7 @@ function parseShopInfo(shopInfoRaw: Object | undefined, siteInfoRaw: Object | un
if (statusRaw === 0 || statusRaw === 'closed' || statusRaw === '0') {
status = 'closed';
}
return { shopName: name, shopLogo: logo, shopStatus: status };
return { shopLogo: logo, shopStatus: status };
}
export async function getCachedShopInfo(context: common.Context): Promise<ShopInfo | null> {

View File

@@ -1,12 +1,13 @@
import { formBindingData, formProvider, FormExtensionAbility, formInfo } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
import { getShopInfo, ShopInfo } from '../common/ShopInfoService';
import { getProductCards, ProductItem } from '../common/ProductCardService';
import { getActivePromotion, Promotion } from '../common/PromotionService';
const FORM_DIM_2X2 = '2*2';
const FORM_DIM_2X4 = '2*4';
const FORM_DIM_4X4 = '4*4';
// 卡片 shopName 始终用元服务名(string.json 的 EntryAbility_label)
// 保证桌面图标名 ↔ 卡片名 ↔ 启动后首页标题完全一致
const APP_NAME_RESOURCE = 'EntryAbility_label';
const APP_NAME_FALLBACK = '店铺';
interface ShopCardData {
cardType: string;
@@ -15,52 +16,21 @@ interface ShopCardData {
shopStatus: string;
}
interface ProductCardData {
cardType: string;
products: ProductItem[];
}
interface PromoCardData {
cardType: string;
title: string;
image: string;
endTime: number;
link: string;
}
const formDimensions: Map<string, string> = new Map();
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want: Want) {
// want.parameters 是 Record<string, Object>,ArkTS 1.1 不允许 obj['key'] 动态索引
// 通过 Record<string, string> 强转使字段被识别为索引签名,再 bracket 访问
const params: Record<string, string> = want.parameters as Record<string, string>;
const dimension: string = params['ohos.extra.param.key.form_dimension'] ?? FORM_DIM_2X2;
const formId: string = params['ohos.extra.param.key.form_identity'] ?? '';
formDimensions.set(formId, dimension);
if (dimension === FORM_DIM_2X2) {
const data: ShopCardData = {
cardType: 'shop',
shopName: '店铺',
shopLogo: '',
shopStatus: 'open'
};
return formBindingData.createFormBindingData(data);
// 当前只启用 2x2;非 2x2 dimension 兜底返回 2x2 数据
if (dimension !== FORM_DIM_2X2) {
// 后续如需开启其他尺寸,在 form_config.json 注册后再分发
}
if (dimension === FORM_DIM_2X4) {
const data: ProductCardData = {
cardType: 'product',
products: []
};
return formBindingData.createFormBindingData(data);
}
const data: PromoCardData = {
cardType: 'promo',
title: '限时活动',
image: '',
endTime: 0,
link: ''
const data: ShopCardData = {
cardType: 'shop',
shopName: this.getAppLabelSync(),
shopLogo: '',
shopStatus: 'open'
};
return formBindingData.createFormBindingData(data);
}
@@ -78,44 +48,51 @@ export default class EntryFormAbility extends FormExtensionAbility {
}
onRemoveForm(formId: string) {
formDimensions.delete(formId);
// 单一 2x2 形态,无需清理
}
onAcquireFormState(want: Want) {
return formInfo.FormState.READY;
}
private async refreshCard(formId: string): Promise<void> {
const dimension: string = formDimensions.get(formId) ?? FORM_DIM_2X2;
private getAppLabelSync(): string {
try {
if (dimension === FORM_DIM_2X2) {
return this.context.resourceManager.getStringByNameSync(APP_NAME_RESOURCE);
} catch (e) {
return APP_NAME_FALLBACK;
}
}
private async getAppLabelAsync(): Promise<string> {
try {
const name: string = await this.context.resourceManager.getStringByName(APP_NAME_RESOURCE);
return name;
} catch (e) {
return APP_NAME_FALLBACK;
}
}
private async refreshCard(formId: string): Promise<void> {
try {
const appLabel: string = await this.getAppLabelAsync();
let logo: string = '';
let status: string = 'open';
try {
const info: ShopInfo = await getShopInfo(this.context);
const data: ShopCardData = {
cardType: 'shop',
shopName: info.shopName,
shopLogo: info.shopLogo,
shopStatus: info.shopStatus
};
await formProvider.updateForm(formId, formBindingData.createFormBindingData(data));
return;
logo = info.shopLogo;
status = info.shopStatus;
} catch (e) {
// API 失败时使用默认值,卡面仍能显示 app 名
}
if (dimension === FORM_DIM_2X4) {
const products: ProductItem[] = await getProductCards(this.context);
const data: ProductCardData = { cardType: 'product', products };
await formProvider.updateForm(formId, formBindingData.createFormBindingData(data));
return;
}
const promo: Promotion = await getActivePromotion(this.context);
const data: PromoCardData = {
cardType: 'promo',
title: promo.title,
image: promo.image,
endTime: promo.endTime,
link: promo.link
const data: ShopCardData = {
cardType: 'shop',
shopName: appLabel,
shopLogo: logo,
shopStatus: status
};
await formProvider.updateForm(formId, formBindingData.createFormBindingData(data));
} catch (e) {
// 拉取失败保持卡片当前内容,等下次刷新
// 整体失败时,卡片保持 onAddForm 时的初始 binding
}
}
}

View File

@@ -1,7 +1,7 @@
// 卡片 UI:店铺信息卡 (2*2)
// 数据通过 formBindingData 由 EntryFormAbility 注入
// 卡片通过 @LocalStorageProp 从 Form 框架的 LocalStorage 读取
// 底部两个快捷动作:分类 / 活动(用户选择)
// 布局:垂直居中,Logo + 名称,点击唤起元服务
const storageShop: LocalStorage = new LocalStorage();
@@ -11,108 +11,44 @@ struct WidgetCard {
@LocalStorageProp('cardType') cardType: string = 'shop';
@LocalStorageProp('shopName') shopName: string = '加载中...';
@LocalStorageProp('shopLogo') shopLogo: string = '';
@LocalStorageProp('shopStatus') shopStatus: string = 'open';
build() {
Stack() {
// 顶层 Row:Logo + 名称 + 状态
Row() {
if (this.shopLogo) {
Image(this.shopLogo)
.width('48vp')
.height('48vp')
.borderRadius('24vp')
.objectFit(ImageFit.Cover)
.alt($r('app.media.app_icon'))
} else {
Image($r('app.media.app_icon'))
.width('48vp')
.height('48vp')
.borderRadius('24vp')
.objectFit(ImageFit.Cover)
}
Column() {
Text(this.shopName)
.fontSize('14fp')
.fontWeight(FontWeight.Medium)
.fontColor('#182431')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width('100%')
Row() {
Circle({ width: '6vp', height: '6vp' })
.fill(this.shopStatus === 'open' ? '#FF4F14' : '#999999')
Text(this.shopStatus === 'open' ? '营业中' : '休息中')
.fontSize('10fp')
.fontColor(this.shopStatus === 'open' ? '#FF4F14' : '#999999')
.margin({ left: '4vp' })
}
.margin({ top: '6vp' })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
.margin({ left: '10vp' })
Column() {
if (this.shopLogo) {
Image(this.shopLogo)
.width('64vp')
.height('64vp')
.borderRadius('32vp')
.objectFit(ImageFit.Cover)
.alt($r('app.media.app_icon'))
} else {
Image($r('app.media.app_icon'))
.width('64vp')
.height('64vp')
.borderRadius('32vp')
.objectFit(ImageFit.Cover)
}
.width('100%')
.alignItems(VerticalAlign.Center)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { message: 'open_shop_card' }
});
})
// 底部两个快捷动作
Row() {
this.ActionButton('分类', '\u{e6c4}', '#FF4F14', () => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { message: 'open_category' }
});
})
this.ActionButton('活动', '\u{e6c5}', '#F7B500', () => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { message: 'open_promotion' }
});
})
}
.width('100%')
.position({ x: 0, y: '74%' })
.padding({ left: '12vp', right: '12vp' })
.justifyContent(FlexAlign.SpaceBetween)
Text(this.shopName)
.fontSize('14fp')
.fontWeight(FontWeight.Medium)
.fontColor('#182431')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: '10vp' })
}
.width('100%')
.height('100%')
.padding({ top: '10vp' })
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center)
.backgroundColor('#FFFFFF')
.borderRadius('16vp')
}
@Builder
ActionButton(label: string, iconChar: string, color: string, onTap: () => void) {
Column() {
Text(iconChar)
.fontSize('20fp')
.fontColor(color)
.width('32vp')
.height('32vp')
.textAlign(TextAlign.Center)
.borderRadius('16vp')
.backgroundColor('#F5F6F8')
Text(label)
.fontSize('9fp')
.fontColor('#666666')
.margin({ top: '2vp' })
}
.alignItems(HorizontalAlign.Center)
.onClick(onTap)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { message: 'open_shop_card' }
});
})
}
}