refactor(mp-harmony): 简化店铺卡片逻辑,统一使用应用名作为卡片名称
1. 移除ShopInfo接口和默认数据中的shopName字段,不再返回店铺名称 2. 重构WidgetCard页面布局,移除营业状态显示和底部快捷按钮 3. 重构EntryFormAbility,仅保留2x2店铺卡片,使用应用资源名作为卡片名称 4. 注释暂时保留的店铺名称解析逻辑,后续需要时可重新启用
This commit is contained in:
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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' }
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user