Initial commit: Flutter 无书应用项目

This commit is contained in:
Developer
2026-03-30 02:35:31 +08:00
commit 9175ff9905
566 changed files with 103261 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
import { UIAbility, Want, AbilityConstant, Configuration } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
interface MenuOption {
id: string;
name: string;
description: string;
icon: string;
action: string;
}
export default class WidgetMenuAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('[WidgetMenuAbility] onCreate');
this.showWidgetMenu();
}
onDestroy(): void {
console.info('[WidgetMenuAbility] onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
console.info('[WidgetMenuAbility] onWindowStageCreate');
}
onWindowStageDestroy(): void {
console.info('[WidgetMenuAbility] onWindowStageDestroy');
}
onForeground(): void {
console.info('[WidgetMenuAbility] onForeground');
}
onBackground(): void {
console.info('[WidgetMenuAbility] onBackground');
}
onConfigurationUpdate(newConfig: Configuration): void {
console.info('[WidgetMenuAbility] onConfigurationUpdate');
}
private showWidgetMenu(): void {
try {
console.info('[WidgetMenuAbility] Show widget menu');
const menuOptions: MenuOption[] = [
{
id: 'open_app',
name: '📱 打开应用',
description: '启动诗词卡片主应用',
icon: '📱',
action: 'open_app'
},
{
id: 'widget_settings',
name: '⚙️ 卡片设置',
description: '进入卡片详细设置页面',
icon: '⚙️',
action: 'open_widget_settings'
},
{
id: 'classic',
name: '🎨 经典风格',
description: '传统诗词卡片样式',
icon: '🎨',
action: 'change_style'
},
{
id: 'modern',
name: '✨ 现代风格',
description: '简洁现代的设计风格',
icon: '✨',
action: 'change_style'
},
{
id: 'minimal',
name: '◽ 极简风格',
description: '最小化设计,突出内容',
icon: '◽',
action: 'change_style'
},
{
id: 'elegant',
name: '💎 优雅风格',
description: '精致优雅的视觉效果',
icon: '💎',
action: 'change_style'
}
];
console.info('[WidgetMenuAbility] Menu options: ' + JSON.stringify(menuOptions));
// 处理菜单选择
this.handleMenuSelection(menuOptions[0]);
} catch (error) {
console.error('[WidgetMenuAbility] showWidgetMenu error: ' + JSON.stringify(error));
}
}
private handleMenuSelection(option: MenuOption): void {
console.info('[WidgetMenuAbility] Selected option: ' + option.id);
if (option.action === 'open_app') {
this.openMainApp();
} else if (option.action === 'open_widget_settings') {
this.openWidgetSettings();
} else if (option.action === 'change_style') {
this.changeCardStyle(option.id);
}
}
private openMainApp(): void {
try {
const want: Want = {
bundleName: this.context.applicationInfo.name,
abilityName: 'EntryAbility'
};
this.context.startAbility(want).then(() => {
console.info('[WidgetMenuAbility] Open main app success');
}).catch((error: BusinessError) => {
console.error('[WidgetMenuAbility] Open main app error: ' + JSON.stringify(error));
});
} catch (error) {
console.error('[WidgetMenuAbility] openMainApp error: ' + JSON.stringify(error));
}
}
private openWidgetSettings(): void {
try {
const want: Want = {
bundleName: this.context.applicationInfo.name,
abilityName: 'EntryAbility',
parameters: {
'route': '/widgets_page'
}
};
this.context.startAbility(want).then(() => {
console.info('[WidgetMenuAbility] Open widget settings success');
}).catch((error: BusinessError) => {
console.error('[WidgetMenuAbility] Open widget settings error: ' + JSON.stringify(error));
});
} catch (error) {
console.error('[WidgetMenuAbility] openWidgetSettings error: ' + JSON.stringify(error));
}
}
private changeCardStyle(style: string): void {
console.info('[WidgetMenuAbility] Change card style to: ' + style);
// 保存样式设置到本地存储
// 触发卡片更新
}
}

View File

@@ -0,0 +1,74 @@
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import { Want, AbilityConstant } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { router, window } from '@kit.ArkUI';
export default class EntryAbility extends FlutterAbility {
private targetPage: string = '';
private formName: string = '';
configureFlutterEngine(flutterEngine: FlutterEngine): void {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
hilog.info(0x0000, 'EntryAbility', 'FlutterEngine configured');
}
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
super.onCreate(want, launchParam);
hilog.info(0x0000, 'EntryAbility', 'onCreate called');
if (want.parameters) {
this.targetPage = want.parameters['targetPage'] as string || '';
this.formName = want.parameters['formName'] as string || '';
hilog.info(0x0000, 'EntryAbility', 'targetPage: ' + this.targetPage + ', formName: ' + this.formName);
}
}
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
super.onNewWant(want, launchParam);
hilog.info(0x0000, 'EntryAbility', 'onNewWant called');
if (want.parameters) {
this.targetPage = want.parameters['targetPage'] as string || '';
this.formName = want.parameters['formName'] as string || '';
hilog.info(0x0000, 'EntryAbility', 'targetPage: ' + this.targetPage + ', formName: ' + this.formName);
if (this.targetPage) {
this.navigateToTargetPage();
}
}
}
onWindowStageCreate(windowStage: window.WindowStage): void {
super.onWindowStageCreate(windowStage);
hilog.info(0x0000, 'EntryAbility', 'onWindowStageCreate called');
if (this.targetPage) {
setTimeout(() => {
this.navigateToTargetPage();
}, 500);
}
}
private navigateToTargetPage(): void {
try {
hilog.info(0x0000, 'EntryAbility', 'Navigating to: ' + this.targetPage);
router.replaceUrl({
url: this.targetPage,
params: {
formName: this.formName
}
}).then(() => {
hilog.info(0x0000, 'EntryAbility', 'Navigation success');
this.targetPage = '';
this.formName = '';
}).catch((err: Error) => {
hilog.error(0x0000, 'EntryAbility', 'Navigation failed: ' + err.message);
});
} catch (error) {
hilog.error(0x0000, 'EntryAbility', 'navigateToTargetPage error: ' + JSON.stringify(error));
}
}
}

View File

@@ -0,0 +1,603 @@
import { FormExtensionAbility, formBindingData, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { preferences } from '@kit.ArkData';
import { http } from '@kit.NetworkKit';
interface FormDataContent {
title: string;
quote: string;
author: string;
dynasty: string;
time: string;
weather: string;
city: string;
style: string;
showTime: boolean;
showWeather: boolean;
showQuote: boolean;
formName: string;
}
interface SettingsData {
style: string;
showTime: boolean;
showWeather: boolean;
showQuote: boolean;
quote: string;
author: string;
dynasty: string;
autoUpdate: boolean;
updateInterval: number;
selectedSource: string;
smallQuote: string;
smallAuthor: string;
smallDynasty: string;
smallShowQuote: boolean;
mediumQuote: string;
mediumAuthor: string;
mediumDynasty: string;
mediumShowQuote: boolean;
largeQuote: string;
largeAuthor: string;
largeDynasty: string;
largeShowQuote: boolean;
}
interface QuoteData {
id: number;
name: string;
url: string;
alias: string;
}
interface ApiResponse {
code: number;
msg: string;
data: QuoteData | null;
}
interface QuoteResult {
name: string;
url: string;
alias: string;
}
interface WeatherData {
city: string;
weather: string;
temp: string;
}
interface WeatherInfo {
city: string;
weather: string;
temp: string;
}
interface CityDZ {
weatherinfo: WeatherInfo;
}
interface DataInner {
cityDZ: CityDZ;
}
interface DataOuter {
data: DataInner;
}
interface WeatherApiResponse {
status: string;
city: string;
data: DataOuter;
}
export default class WidgetFormAbility extends FormExtensionAbility {
async onCreate(want: Want): Promise<formBindingData.FormBindingData> {
console.info('[WidgetFormAbility] onCreate');
const formName = want.parameters?.['formName'] as string || 'WidgetCardSmall';
const settings = await this.loadSettings();
const content = await this.buildFormData(formName, settings);
return formBindingData.createFormBindingData(content);
}
private async buildFormData(formName: string, settings: SettingsData): Promise<FormDataContent> {
let quote: string;
let author: string;
let dynasty: string;
let showQuote: boolean;
switch (formName) {
case 'WidgetCardSmall':
quote = settings.smallQuote;
author = settings.smallAuthor;
dynasty = settings.smallDynasty;
showQuote = settings.smallShowQuote;
break;
case 'WidgetCardMedium':
quote = settings.mediumQuote;
author = settings.mediumAuthor;
dynasty = settings.mediumDynasty;
showQuote = settings.mediumShowQuote;
break;
case 'WidgetCardLarge':
quote = settings.largeQuote;
author = settings.largeAuthor;
dynasty = settings.largeDynasty;
showQuote = settings.largeShowQuote;
break;
default:
quote = settings.smallQuote;
author = settings.smallAuthor;
dynasty = settings.smallDynasty;
showQuote = settings.smallShowQuote;
}
if (settings.selectedSource === 'api') {
const apiQuote = await this.fetchRandomQuote();
if (apiQuote) {
quote = apiQuote.name;
author = apiQuote.url;
dynasty = apiQuote.alias;
await this.saveQuoteToPreferences(formName, quote, author, dynasty);
}
}
let weatherData: WeatherData = {
city: '昆明',
weather: '晴',
temp: '11'
};
if (settings.showWeather) {
const fetchedWeather = await this.fetchWeather();
if (fetchedWeather) {
weatherData = fetchedWeather;
}
}
const content: FormDataContent = {
title: this.getTitleByFormName(formName),
quote: quote,
author: author,
dynasty: dynasty,
time: this.getCurrentTime(),
weather: weatherData.weather + ' ' + weatherData.temp + '°C',
city: weatherData.city,
style: settings.style,
showTime: settings.showTime,
showWeather: settings.showWeather,
showQuote: showQuote,
formName: formName
};
return content;
}
private async loadSettings(): Promise<SettingsData> {
try {
const preference = await preferences.getPreferences(this.context, 'widget_quote_settings');
const result: SettingsData = {
style: await preference.get('style', 'classic') as string,
showTime: Boolean(await preference.get('showTime', true)),
showWeather: Boolean(await preference.get('showWeather', true)),
showQuote: Boolean(await preference.get('showQuote', true)),
quote: await preference.get('quote', '床前明月光,疑是地上霜。') as string,
author: await preference.get('author', '李白《静夜思》') as string,
dynasty: await preference.get('dynasty', '唐代') as string,
autoUpdate: Boolean(await preference.get('autoUpdate', false)),
updateInterval: await preference.get('updateInterval', 60) as number,
selectedSource: await preference.get('selectedSource', 'local') as string,
smallQuote: await preference.get('small_quote', '床前明月光,疑是地上霜。') as string,
smallAuthor: await preference.get('small_author', '李白《静夜思》') as string,
smallDynasty: await preference.get('small_dynasty', '唐代') as string,
smallShowQuote: Boolean(await preference.get('small_showQuote', true)),
mediumQuote: await preference.get('medium_quote', '春眠不觉晓,处处闻啼鸟。') as string,
mediumAuthor: await preference.get('medium_author', '孟浩然《春晓》') as string,
mediumDynasty: await preference.get('medium_dynasty', '唐代') as string,
mediumShowQuote: Boolean(await preference.get('medium_showQuote', true)),
largeQuote: await preference.get('large_quote', '白日依山尽,黄河入海流。') as string,
largeAuthor: await preference.get('large_author', '王之涣《登鹳雀楼》') as string,
largeDynasty: await preference.get('large_dynasty', '唐代') as string,
largeShowQuote: Boolean(await preference.get('large_showQuote', true))
};
console.info('[WidgetFormAbility] Settings loaded - showTime: ' + result.showTime + ', showWeather: ' + result.showWeather);
return result;
} catch (error) {
console.error('[WidgetFormAbility] loadSettings error: ' + JSON.stringify(error));
const result: SettingsData = {
style: 'classic',
showTime: true,
showWeather: true,
showQuote: true,
quote: '床前明月光,疑是地上霜。',
author: '李白《静夜思》',
dynasty: '唐代',
autoUpdate: false,
updateInterval: 60,
selectedSource: 'local',
smallQuote: '床前明月光,疑是地上霜。',
smallAuthor: '李白《静夜思》',
smallDynasty: '唐代',
smallShowQuote: true,
mediumQuote: '春眠不觉晓,处处闻啼鸟。',
mediumAuthor: '孟浩然《春晓》',
mediumDynasty: '唐代',
mediumShowQuote: true,
largeQuote: '白日依山尽,黄河入海流。',
largeAuthor: '王之涣《登鹳雀楼》',
largeDynasty: '唐代',
largeShowQuote: true
};
return result;
}
}
private async saveQuoteToPreferences(formName: string, quote: string, author: string, dynasty: string): Promise<void> {
try {
const preference = await preferences.getPreferences(this.context, 'widget_quote_settings');
await preference.put('quote', quote);
await preference.put('author', author);
await preference.put('dynasty', dynasty);
switch (formName) {
case 'WidgetCardSmall':
await preference.put('small_quote', quote);
await preference.put('small_author', author);
await preference.put('small_dynasty', dynasty);
break;
case 'WidgetCardMedium':
await preference.put('medium_quote', quote);
await preference.put('medium_author', author);
await preference.put('medium_dynasty', dynasty);
break;
case 'WidgetCardLarge':
await preference.put('large_quote', quote);
await preference.put('large_author', author);
await preference.put('large_dynasty', dynasty);
break;
}
await preference.flush();
console.info('[WidgetFormAbility] Quote saved to preferences for ' + formName);
} catch (error) {
console.error('[WidgetFormAbility] saveQuoteToPreferences error: ' + JSON.stringify(error));
}
}
private async fetchWeather(): Promise<WeatherData | null> {
try {
const httpRequest = http.createHttp();
const response = await httpRequest.request('https://yy.vogov.cn/api/tq/api.php', {
method: http.RequestMethod.GET,
connectTimeout: 10000,
readTimeout: 10000
});
httpRequest.destroy();
if (response.responseCode === 200) {
const result = JSON.parse(response.result as string) as WeatherApiResponse;
if (result.status === 'success' && result.data && result.data.data) {
const weatherInfo = result.data.data.cityDZ.weatherinfo;
const weatherData: WeatherData = {
city: weatherInfo.city,
weather: weatherInfo.weather,
temp: weatherInfo.temp
};
console.info('[WidgetFormAbility] Fetched weather: ' + JSON.stringify(weatherData));
return weatherData;
}
}
return null;
} catch (error) {
console.error('[WidgetFormAbility] fetchWeather error: ' + JSON.stringify(error));
return null;
}
}
private async fetchRandomQuote(): Promise<QuoteResult | null> {
try {
const httpRequest = http.createHttp();
const response = await httpRequest.request('https://yy.vogov.cn/api/pms.php', {
method: http.RequestMethod.GET,
connectTimeout: 10000,
readTimeout: 10000
});
httpRequest.destroy();
if (response.responseCode === 200) {
const result = JSON.parse(response.result as string) as ApiResponse;
if (result.code === 0 && result.data) {
const quoteResult: QuoteResult = {
name: result.data.name,
url: result.data.url,
alias: result.data.alias
};
console.info('[WidgetFormAbility] Fetched quote: ' + quoteResult.name);
return quoteResult;
}
}
return null;
} catch (error) {
console.error('[WidgetFormAbility] fetchRandomQuote error: ' + JSON.stringify(error));
return null;
}
}
private getTitleByFormName(formName: string): string {
switch (formName) {
case 'WidgetCardSmall':
return '🎋 诗词短句';
case 'WidgetCardMedium':
return '📖 经典诗词';
case 'WidgetCardLarge':
return '🎨 诗词意境';
default:
return '🎋 诗词短句';
}
}
onUpdate(formId: string): void {
console.info('[WidgetFormAbility] onUpdate, formId: ' + formId);
this.updateForm(formId);
}
onFormEvent(formId: string, message: string): void {
console.info('[WidgetFormAbility] onFormEvent, formId: ' + formId + ', message: ' + message);
if (message === 'refreshQuote') {
console.info('[WidgetFormAbility] Refresh quote requested');
this.refreshQuoteOnly(formId);
} else if (message === 'refreshWeather') {
console.info('[WidgetFormAbility] Refresh weather requested');
this.refreshWeatherOnly(formId);
} else if (message === 'refresh' || message.includes('refresh')) {
console.info('[WidgetFormAbility] Full refresh requested');
this.refreshForm(formId);
}
}
async refreshQuoteOnly(formId: string): Promise<void> {
console.info('[WidgetFormAbility] refreshQuoteOnly start for formId: ' + formId);
try {
const settings = await this.loadSettings();
const formName = await this.getFormNameByFormId(formId);
let quote: string;
let author: string;
let dynasty: string;
let showQuote: boolean;
switch (formName) {
case 'WidgetCardSmall':
quote = settings.smallQuote;
author = settings.smallAuthor;
dynasty = settings.smallDynasty;
showQuote = settings.smallShowQuote;
break;
case 'WidgetCardMedium':
quote = settings.mediumQuote;
author = settings.mediumAuthor;
dynasty = settings.mediumDynasty;
showQuote = settings.mediumShowQuote;
break;
case 'WidgetCardLarge':
quote = settings.largeQuote;
author = settings.largeAuthor;
dynasty = settings.largeDynasty;
showQuote = settings.largeShowQuote;
break;
default:
quote = settings.smallQuote;
author = settings.smallAuthor;
dynasty = settings.smallDynasty;
showQuote = settings.smallShowQuote;
}
if (settings.selectedSource === 'api') {
const apiQuote = await this.fetchRandomQuote();
if (apiQuote) {
quote = apiQuote.name;
author = apiQuote.url;
dynasty = apiQuote.alias;
await this.saveQuoteToPreferences(formName, quote, author, dynasty);
}
}
const content: FormDataContent = {
title: this.getTitleByFormName(formName),
quote: quote,
author: author,
dynasty: dynasty,
time: this.getCurrentTime(),
weather: '',
city: '',
style: settings.style,
showTime: settings.showTime,
showWeather: false,
showQuote: showQuote,
formName: formName
};
const bindingData = formBindingData.createFormBindingData(content);
await formProvider.updateForm(formId, bindingData);
console.info('[WidgetFormAbility] refreshQuoteOnly success');
} catch (error) {
console.error('[WidgetFormAbility] refreshQuoteOnly error: ' + JSON.stringify(error));
}
}
async refreshWeatherOnly(formId: string): Promise<void> {
console.info('[WidgetFormAbility] refreshWeatherOnly start for formId: ' + formId);
try {
const settings = await this.loadSettings();
const formName = await this.getFormNameByFormId(formId);
let quote: string;
let author: string;
let dynasty: string;
let showQuote: boolean;
switch (formName) {
case 'WidgetCardSmall':
quote = settings.smallQuote;
author = settings.smallAuthor;
dynasty = settings.smallDynasty;
showQuote = settings.smallShowQuote;
break;
case 'WidgetCardMedium':
quote = settings.mediumQuote;
author = settings.mediumAuthor;
dynasty = settings.mediumDynasty;
showQuote = settings.mediumShowQuote;
break;
case 'WidgetCardLarge':
quote = settings.largeQuote;
author = settings.largeAuthor;
dynasty = settings.largeDynasty;
showQuote = settings.largeShowQuote;
break;
default:
quote = settings.smallQuote;
author = settings.smallAuthor;
dynasty = settings.smallDynasty;
showQuote = settings.smallShowQuote;
}
let weatherData: WeatherData = {
city: '昆明',
weather: '晴',
temp: '11'
};
const fetchedWeather = await this.fetchWeather();
if (fetchedWeather) {
weatherData = fetchedWeather;
}
const content: FormDataContent = {
title: this.getTitleByFormName(formName),
quote: quote,
author: author,
dynasty: dynasty,
time: this.getCurrentTime(),
weather: weatherData.weather + ' ' + weatherData.temp + '°C',
city: weatherData.city,
style: settings.style,
showTime: settings.showTime,
showWeather: settings.showWeather,
showQuote: showQuote,
formName: formName
};
const bindingData = formBindingData.createFormBindingData(content);
await formProvider.updateForm(formId, bindingData);
console.info('[WidgetFormAbility] refreshWeatherOnly success');
} catch (error) {
console.error('[WidgetFormAbility] refreshWeatherOnly error: ' + JSON.stringify(error));
}
}
async refreshForm(formId: string): Promise<void> {
console.info('[WidgetFormAbility] refreshForm start for formId: ' + formId);
try {
const settings = await this.loadSettings();
console.info('[WidgetFormAbility] Loaded settings, selectedSource: ' + settings.selectedSource);
const formName = await this.getFormNameByFormId(formId);
console.info('[WidgetFormAbility] Form name: ' + formName);
const content = await this.buildFormData(formName, settings);
const bindingData = formBindingData.createFormBindingData(content);
await formProvider.updateForm(formId, bindingData);
console.info('[WidgetFormAbility] refreshForm success');
} catch (error) {
console.error('[WidgetFormAbility] refreshForm error: ' + JSON.stringify(error));
}
}
private async getFormNameByFormId(formId: string): Promise<string> {
try {
const formInfos = await formProvider.getPublishedRunningFormInfos();
for (const info of formInfos) {
if (info.formId === formId) {
return info.formName;
}
}
} catch (error) {
console.error('[WidgetFormAbility] getFormNameByFormId error: ' + JSON.stringify(error));
}
return 'WidgetCardSmall';
}
onCastToNormal(formId: string): void {
console.info('[WidgetFormAbility] onCastToNormal, formId: ' + formId);
}
onVisibilityChange(newStatus: Record<string, number>): void {
console.info('[WidgetFormAbility] onVisibilityChange, newStatus: ' + JSON.stringify(newStatus));
}
onDestroy(formId: string): void {
console.info('[WidgetFormAbility] onDestroy, formId: ' + formId);
}
onAcquireFormState?(want: Want): number {
console.info('[WidgetFormAbility] onAcquireFormState');
return 0;
}
private async updateForm(formId: string, formName: string = 'WidgetCardSmall'): Promise<void> {
try {
const settings = await this.loadSettings();
const content = await this.buildFormData(formName, settings);
const bindingData = formBindingData.createFormBindingData(content);
formProvider.updateForm(formId, bindingData).then(() => {
console.info('[WidgetFormAbility] updateForm success');
}).catch((error: BusinessError) => {
console.error('[WidgetFormAbility] updateForm error: ' + JSON.stringify(error));
});
} catch (error) {
console.error('[WidgetFormAbility] updateForm exception: ' + JSON.stringify(error));
}
}
private getChineseTimePeriod(): string {
const hour = new Date().getHours();
if (hour >= 23 || hour < 1) {
return '子时';
} else if (hour >= 1 && hour < 3) {
return '丑时';
} else if (hour >= 3 && hour < 5) {
return '寅时';
} else if (hour >= 5 && hour < 7) {
return '卯时';
} else if (hour >= 7 && hour < 9) {
return '辰时';
} else if (hour >= 9 && hour < 11) {
return '巳时';
} else if (hour >= 11 && hour < 13) {
return '午时';
} else if (hour >= 13 && hour < 15) {
return '未时';
} else if (hour >= 15 && hour < 17) {
return '申时';
} else if (hour >= 17 && hour < 19) {
return '酉时';
} else if (hour >= 19 && hour < 21) {
return '戌时';
} else {
return '亥时';
}
}
private getCurrentTime(): string {
return this.getChineseTimePeriod();
}
}

View File

@@ -0,0 +1,24 @@
import common from '@ohos.app.ability.common';
import { FlutterPage } from '@ohos/flutter_ohos'
let storage = LocalStorage.getShared()
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'
@Entry(storage)
@Component
struct Index {
private context = getContext(this) as common.UIAbilityContext
@LocalStorageLink('viewId') viewId: string = "";
build() {
Column() {
FlutterPage({ viewId: this.viewId })
}
}
onBackPress(): boolean {
this.context.eventHub.emit(EVENT_BACK_PRESS)
return true
}
}

View File

@@ -0,0 +1,532 @@
@Entry
@Component
struct WidgetCard {
@LocalStorageProp('formName') formName: string = 'WidgetCardSmall';
@LocalStorageProp('style') style: string = 'classic';
@LocalStorageProp('showTime') showTime: boolean = true;
@LocalStorageProp('showWeather') showWeather: boolean = true;
@LocalStorageProp('showQuote') showQuote: boolean = true;
@LocalStorageProp('quote') quote: string = '床前明月光,疑是地上霜。';
@LocalStorageProp('author') author: string = '李白《静夜思》';
@LocalStorageProp('dynasty') dynasty: string = '唐代';
@LocalStorageProp('time') time: string = '12:00';
@LocalStorageProp('weather') weather: string = '晴 16°C';
@LocalStorageProp('city') city: string = '昆明';
private getTimePeriod(): string {
const hour = new Date().getHours();
if (hour >= 0 && hour < 6) {
return '凌晨';
} else if (hour >= 6 && hour < 9) {
return '早上';
} else if (hour >= 9 && hour < 12) {
return '上午';
} else if (hour >= 12 && hour < 14) {
return '中午';
} else if (hour >= 14 && hour < 18) {
return '下午';
} else if (hour >= 18 && hour < 22) {
return '晚上';
} else {
return '深夜';
}
}
private getChineseTimePeriod(): string {
const hour = new Date().getHours();
if (hour >= 23 || hour < 1) {
return '子时';
} else if (hour >= 1 && hour < 3) {
return '丑时';
} else if (hour >= 3 && hour < 5) {
return '寅时';
} else if (hour >= 5 && hour < 7) {
return '卯时';
} else if (hour >= 7 && hour < 9) {
return '辰时';
} else if (hour >= 9 && hour < 11) {
return '巳时';
} else if (hour >= 11 && hour < 13) {
return '午时';
} else if (hour >= 13 && hour < 15) {
return '未时';
} else if (hour >= 15 && hour < 17) {
return '申时';
} else if (hour >= 17 && hour < 19) {
return '酉时';
} else if (hour >= 19 && hour < 21) {
return '戌时';
} else {
return '亥时';
}
}
private getWeekDay(): string {
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
return weekDays[new Date().getDay()];
}
private getGreeting(): string {
const hour = new Date().getHours();
if (hour >= 5 && hour < 7) {
return '清晨了,呼吸新鲜空气';
} else if (hour >= 7 && hour < 9) {
return '早上好,元气满满';
} else if (hour >= 9 && hour < 12) {
return '上午好,努力工作';
} else if (hour >= 12 && hour < 14) {
return '中午了,吃顿好的';
} else if (hour >= 14 && hour < 17) {
return '下午了,喝杯咖啡';
} else if (hour >= 17 && hour < 19) {
return '傍晚了,休息一下';
} else if (hour >= 19 && hour < 22) {
return '晚上好,放松身心';
} else {
return '深夜了,注意身体';
}
}
private getRecommendation(): string {
const hour = new Date().getHours();
if (hour >= 5 && hour < 7) {
return '推荐:山水田园诗';
} else if (hour >= 7 && hour < 9) {
return '推荐:励志诗词';
} else if (hour >= 9 && hour < 12) {
return '推荐:送别诗';
} else if (hour >= 12 && hour < 14) {
return '推荐:思乡诗';
} else if (hour >= 14 && hour < 17) {
return '推荐:咏史诗';
} else if (hour >= 17 && hour < 19) {
return '推荐:边塞诗';
} else if (hour >= 19 && hour < 22) {
return '推荐:爱情诗';
} else {
return '推荐:婉约词';
}
}
private isSmallCard(): boolean {
return this.formName === 'WidgetCardSmall';
}
private isDarkText(): boolean {
return this.style === 'transparent' || this.style === 'liquid' || this.style === 'immersive';
}
private getTextColor(): string {
return this.isDarkText() ? '#1A1A1A' : '#FFFFFF';
}
private getTextOpacity(): number {
return this.isDarkText() ? 1 : 0.9;
}
private getPoet(): string {
const author = this.author;
const titleMatch = author.match(/《.*?》/);
if (titleMatch) {
return author.replace(/《.*?》/, '').trim();
}
return author;
}
private getTitle(): string {
const author = this.author;
const titleMatch = author.match(/《(.*?)》/);
if (titleMatch) {
return titleMatch[1];
}
return '';
}
build() {
if (this.style === 'frosted') {
this.buildCardFrosted()
} else if (this.style === 'liquid') {
this.buildCardLiquid()
} else if (this.style === 'immersive') {
this.buildCardImmersive()
} else if (this.style === 'transparent') {
this.buildCardTransparent()
} else {
this.buildCardGradient()
}
}
@Builder
buildCardFrosted() {
Column() {
if (this.isSmallCard()) {
this.buildSmallCard();
} else {
this.buildNormalCard();
}
}
.width('100%')
.height('100%')
.padding(16)
.borderRadius(20)
.backgroundColor('rgba(0,0,0,0.3)')
.backdropBlur(20)
.border({
width: 1,
color: 'rgba(255,255,255,0.2)'
})
.shadow({
radius: 10,
color: 'rgba(0, 0, 0, 0.15)',
offsetX: 0,
offsetY: 4
})
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'pages/WidgetQuoteSettings',
formName: this.formName
}
});
})
}
@Builder
buildCardLiquid() {
Column() {
if (this.isSmallCard()) {
this.buildSmallCard();
} else {
this.buildNormalCard();
}
}
.width('100%')
.height('100%')
.padding(16)
.borderRadius(20)
.linearGradient({
angle: 135,
colors: [['rgba(100,181,246,0.6)', 0.0], ['rgba(255,255,255,0.3)', 0.5], ['rgba(255,138,128,0.4)', 1.0]]
})
.backdropBlur(15)
.shadow({
radius: 10,
color: 'rgba(0, 0, 0, 0.15)',
offsetX: 0,
offsetY: 4
})
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'pages/WidgetQuoteSettings',
formName: this.formName
}
});
})
}
@Builder
buildCardImmersive() {
Column() {
if (this.isSmallCard()) {
this.buildSmallCard();
} else {
this.buildNormalCard();
}
}
.width('100%')
.height('100%')
.padding(16)
.borderRadius(20)
.linearGradient({
angle: 180,
colors: [['rgba(0,0,0,0.4)', 0.0], ['rgba(0,0,0,0.1)', 0.5], ['rgba(0,0,0,0.3)', 1.0]]
})
.backdropBlur(10)
.shadow({
radius: 10,
color: 'rgba(0, 0, 0, 0.15)',
offsetX: 0,
offsetY: 4
})
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'pages/WidgetQuoteSettings',
formName: this.formName
}
});
})
}
@Builder
buildCardTransparent() {
Column() {
if (this.isSmallCard()) {
this.buildSmallCard();
} else {
this.buildNormalCard();
}
}
.width('100%')
.height('100%')
.padding(16)
.borderRadius(20)
.backgroundColor('transparent')
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'pages/WidgetQuoteSettings',
formName: this.formName
}
});
})
}
@Builder
buildCardGradient() {
Column() {
if (this.isSmallCard()) {
this.buildSmallCard();
} else {
this.buildNormalCard();
}
}
.width('100%')
.height('100%')
.padding(16)
.borderRadius(20)
.linearGradient({
angle: 135,
colors: this.getGradientColors()
})
.shadow({
radius: 10,
color: 'rgba(0, 0, 0, 0.15)',
offsetX: 0,
offsetY: 4
})
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'pages/WidgetQuoteSettings',
formName: this.formName
}
});
})
}
private getGradientColors(): Array<[ResourceColor, number]> {
switch (this.style) {
case 'classic':
return [['#7B1FA2', 0.0], ['#9C27B0', 1.0]];
case 'modern':
return [['#2E7D32', 0.0], ['#4CAF50', 1.0]];
case 'minimal':
return [['#8E8E93', 0.0], ['#C7C7CC', 1.0]];
case 'elegant':
return [['#FF9500', 0.0], ['#FFCC00', 1.0]];
default:
return [['#7B1FA2', 0.0], ['#9C27B0', 1.0]];
}
}
@Builder
buildSmallCard() {
Column() {
Row() {
if (this.showTime) {
Row({ space: 3 }) {
Text(this.getWeekDay())
.fontSize(12)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.opacity(this.isDarkText() ? 0.8 : 0.85)
Text(this.time)
.fontSize(12)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Bold)
}
}
Button(this.city)
.fontSize(12)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.backgroundColor(this.isDarkText() ? 'rgba(0,122,255,0.15)' : 'rgba(255, 255, 255, 0.25)')
.borderRadius(14)
.height(28)
.padding({ left: 10, right: 10 })
.margin({ left: 6 })
.onClick(() => {
postCardAction(this, {
action: 'message',
params: {
message: 'refreshWeather'
}
});
})
Blank()
}
.width('100%')
.margin({ bottom: 12 })
Blank()
Column({ space: 8 }) {
Text(this.getGreeting())
.fontSize(16)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
Text(this.getRecommendation())
.fontSize(13)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.opacity(this.isDarkText() ? 0.7 : 0.85)
.textAlign(TextAlign.Center)
}
Blank()
}
.width('100%')
.height('100%')
}
@Builder
buildNormalCard() {
Column() {
Row() {
if (this.showTime) {
Row({ space: 4 }) {
Text(this.getWeekDay())
.fontSize(13)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.opacity(this.isDarkText() ? 0.7 : 0.85)
Text(this.getTimePeriod())
.fontSize(13)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.opacity(this.isDarkText() ? 0.7 : 0.85)
Text(this.time)
.fontSize(13)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Bold)
}
}
if (this.showWeather) {
Button(this.city + ' ' + this.weather)
.fontSize(13)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.backgroundColor(this.isDarkText() ? 'rgba(0,122,255,0.15)' : 'rgba(255, 255, 255, 0.25)')
.borderRadius(16)
.height(30)
.padding({ left: 10, right: 10 })
.margin({ left: 8 })
.onClick(() => {
postCardAction(this, {
action: 'message',
params: {
message: 'refreshWeather'
}
});
})
}
Blank()
Button('🔄 加载')
.fontSize(13)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.backgroundColor(this.isDarkText() ? 'rgba(0,122,255,0.15)' : 'rgba(255, 255, 255, 0.25)')
.borderRadius(16)
.height(32)
.padding({ left: 12, right: 12 })
.onClick(() => {
postCardAction(this, {
action: 'message',
params: {
message: 'refreshQuote'
}
});
})
}
.width('100%')
// .margin({ bottom: 16 })
if (this.showQuote) {
Text(this.quote)
.fontSize(20)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Bold)
.lineHeight(32)
.textAlign(TextAlign.Center)
.maxLines(3)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
Blank()
Column() {
if (this.showQuote) {
if (this.getTitle()) {
Text('《' + this.getTitle() + '》')
.fontSize(14)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.opacity(this.isDarkText() ? 0.8 : 0.9)
.margin({ bottom: 8 })
.width('100%')
.textAlign(TextAlign.Start)
}
Row() {
if (this.dynasty) {
Text(this.dynasty)
.fontSize(14)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.opacity(this.isDarkText() ? 0.7 : 0.85)
}
if (this.getPoet()) {
Text('·' + this.getPoet())
.fontSize(14)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Medium)
.opacity(this.isDarkText() ? 0.6 : 0.8)
.fontStyle(FontStyle.Italic)
}
Blank()
Text('✨ 精选诗句')
.fontSize(14)
.fontColor(this.getTextColor())
.fontWeight(FontWeight.Bold)
.opacity(this.getTextOpacity())
}
.width('100%')
}
}
.width('100%')
}
.width('100%')
.height('100%')
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,462 @@
import { router } from '@kit.ArkUI';
import { preferences } from '@kit.ArkData';
//todo 重要 后续 刷新按钮要求固定显示
@Entry
@Component
struct WidgetSettings {
@State selectedStyle: string = 'classic';
@State showTime: boolean = true;
@State showWeather: boolean = true;
@State showQuote: boolean = true;
@State quoteText: string = '床前明月光,疑是地上霜。';
@State authorText: string = '李白《静夜思》';
private preference: preferences.Preferences | null = null;
async aboutToAppear() {
await this.loadSettings();
}
async loadSettings() {
try {
const context = getContext(this);
this.preference = await preferences.getPreferences(context, 'widget_settings');
this.selectedStyle = await this.preference.get('style', 'classic') as string;
const showTimeVal = await this.preference.get('showTime', true);
this.showTime = Boolean(showTimeVal);
console.info('[WidgetSettings] showTime raw value: ' + showTimeVal + ', type: ' + typeof showTimeVal + ', parsed: ' + this.showTime);
const showWeatherVal = await this.preference.get('showWeather', true);
this.showWeather = Boolean(showWeatherVal);
console.info('[WidgetSettings] showWeather raw value: ' + showWeatherVal + ', type: ' + typeof showWeatherVal + ', parsed: ' + this.showWeather);
const showQuoteVal = await this.preference.get('showQuote', true);
this.showQuote = Boolean(showQuoteVal);
this.quoteText = await this.preference.get('quote', '床前明月光,疑是地上霜。') as string;
this.authorText = await this.preference.get('author', '李白《静夜思》') as string;
console.info('[WidgetSettings] Settings loaded - showTime: ' + this.showTime + ', showWeather: ' + this.showWeather);
} catch (error) {
console.error('[WidgetSettings] loadSettings error: ' + JSON.stringify(error));
}
}
async saveSettings() {
try {
if (!this.preference) {
const context = getContext(this);
this.preference = await preferences.getPreferences(context, 'widget_settings');
}
console.info('[WidgetSettings] Saving settings - showTime: ' + this.showTime + ', showWeather: ' + this.showWeather);
await this.preference.put('style', this.selectedStyle);
await this.preference.put('showTime', Boolean(this.showTime));
await this.preference.put('showWeather', Boolean(this.showWeather));
await this.preference.put('showQuote', Boolean(this.showQuote));
await this.preference.put('quote', this.quoteText);
await this.preference.put('author', this.authorText);
await this.preference.flush();
console.info('[WidgetSettings] Settings saved successfully');
} catch (error) {
console.error('[WidgetSettings] saveSettings error: ' + JSON.stringify(error));
}
}
private isDarkText(): boolean {
return this.selectedStyle === 'transparent' || this.selectedStyle === 'frosted' || this.selectedStyle === 'liquid' || this.selectedStyle === 'immersive';
}
build() {
Navigation() {
Scroll() {
Column({ space: 16 }) {
Text('卡片设置')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#1A1A1A')
.margin({ top: 16, bottom: 8 })
this.buildPreview()
this.buildStyleSelector()
this.buildDisplayOptions()
this.buildContentSettings()
Button('保存设置')
.width('100%')
.height(48)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.backgroundColor('#007AFF')
.borderRadius(12)
.margin({ top: 24, bottom: 32 })
.onClick(() => {
this.saveSettings();
})
}
.width('100%')
.padding(16)
}
.width('100%')
.height('100%')
}
.title('卡片配置')
.titleMode(NavigationTitleMode.Mini)
.mode(NavigationMode.Stack)
}
@Builder
buildPreview() {
Column() {
Text('预览')
.fontSize(14)
.fontColor('#666666')
.margin({ bottom: 8 })
.alignSelf(ItemAlign.Start)
if (this.selectedStyle === 'frosted') {
this.buildPreviewCardFrosted()
} else if (this.selectedStyle === 'liquid') {
this.buildPreviewCardLiquid()
} else if (this.selectedStyle === 'immersive') {
this.buildPreviewCardImmersive()
} else if (this.selectedStyle === 'transparent') {
this.buildPreviewCardTransparent()
} else {
this.buildPreviewCardGradient()
}
}
.width('100%')
.padding(16)
.backgroundColor('#F5F5F5')
.borderRadius(12)
}
@Builder
buildPreviewCardFrosted() {
Column() {
this.buildPreviewCardContent()
}
.width('100%')
.height(160)
.padding(16)
.borderRadius(16)
.backgroundColor('rgba(255,255,255,0.25)')
.backdropBlur(20)
.border({
width: 1,
color: 'rgba(255,255,255,0.4)'
})
}
@Builder
buildPreviewCardLiquid() {
Column() {
this.buildPreviewCardContent()
}
.width('100%')
.height(160)
.padding(16)
.borderRadius(16)
.linearGradient({
angle: 135,
colors: [['rgba(100,181,246,0.6)', 0.0], ['rgba(255,255,255,0.3)', 0.5], ['rgba(255,138,128,0.4)', 1.0]]
})
.backdropBlur(15)
}
@Builder
buildPreviewCardImmersive() {
Column() {
this.buildPreviewCardContent()
}
.width('100%')
.height(160)
.padding(16)
.borderRadius(16)
.linearGradient({
angle: 180,
colors: [['rgba(0,0,0,0.4)', 0.0], ['rgba(0,0,0,0.1)', 0.5], ['rgba(0,0,0,0.3)', 1.0]]
})
.backdropBlur(10)
}
@Builder
buildPreviewCardTransparent() {
Column() {
this.buildPreviewCardContent()
}
.width('100%')
.height(160)
.padding(16)
.borderRadius(16)
.backgroundColor('transparent')
.border({
width: 1,
color: '#E0E0E0',
style: BorderStyle.Dashed
})
}
@Builder
buildPreviewCardGradient() {
Column() {
this.buildPreviewCardContent()
}
.width('100%')
.height(160)
.padding(16)
.borderRadius(16)
.linearGradient({
angle: 135,
colors: this.getGradientColors()
})
}
@Builder
buildPreviewCardContent() {
if (this.showTime) {
Row() {
Text('12:00')
.fontSize(15)
.fontColor(this.isDarkText() ? '#1A1A1A' : '#FFFFFF')
.fontWeight(FontWeight.Bold)
}
.width('100%')
.justifyContent(FlexAlign.End)
}
if (this.showQuote) {
Column() {
Text(this.quoteText)
.fontSize(18)
.fontColor(this.isDarkText() ? '#1A1A1A' : '#FFFFFF')
.fontWeight(FontWeight.Bold)
.margin({ bottom: 4 })
Text(this.authorText)
.fontSize(14)
.fontColor(this.isDarkText() ? '#666666' : '#FFFFFFCC')
.fontWeight(FontWeight.Medium)
}
.alignItems(HorizontalAlign.Start)
.margin({ top: 12, bottom: 12 })
}
if (this.showWeather) {
Row() {
Text('☀️')
.fontSize(18)
Text('25°C')
.fontSize(15)
.fontColor(this.isDarkText() ? '#1A1A1A' : '#FFFFFF')
.fontWeight(FontWeight.Medium)
.margin({ left: 4 })
}
.width('100%')
.justifyContent(FlexAlign.End)
}
}
@Builder
buildStyleSelector() {
Column({ space: 12 }) {
Text('卡片样式')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#1A1A1A')
.alignSelf(ItemAlign.Start)
Column({ space: 8 }) {
Row({ space: 8 }) {
this.buildStyleOption('classic', '经典', ['#7B1FA2', '#9C27B0'], 'gradient')
this.buildStyleOption('modern', '护眼', ['#2E7D32', '#4CAF50'], 'gradient')
this.buildStyleOption('minimal', '极简', ['#424242', '#757575'], 'gradient')
this.buildStyleOption('elegant', '优雅', ['#FF6B6B', '#FF8E53'], 'gradient')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Row({ space: 8 }) {
this.buildStyleOption('frosted', '毛玻璃', ['rgba(255,255,255,0.25)', 'rgba(255,255,255,0.1)'], 'frosted')
this.buildStyleOption('liquid', '柔光', ['rgba(100,181,246,0.4)', 'rgba(255,255,255,0.2)'], 'liquid')
this.buildStyleOption('immersive', '沉浸式', ['rgba(0,0,0,0.3)', 'rgba(0,0,0,0.1)'], 'immersive')
this.buildStyleOption('transparent', '纯白', ['transparent', 'transparent'], 'transparent')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
}
.width('100%')
.padding(16)
.backgroundColor('#F5F5F5')
.borderRadius(12)
}
@Builder
buildStyleOption(style: string, label: string, colors: string[], type: string) {
Column() {
if (type === 'frosted') {
Column()
.width(40)
.height(40)
.borderRadius(20)
.backgroundColor('rgba(0,0,0,0.3)')
.backdropBlur(20)
.border({
width: 1,
color: 'rgba(255,255,255,0.2)'
})
} else if (type === 'liquid') {
Column()
.width(40)
.height(40)
.borderRadius(20)
.linearGradient({
angle: 135,
colors: [['rgba(100,181,246,0.6)', 0.0], ['rgba(255,255,255,0.3)', 0.5], ['rgba(255,138,128,0.4)', 1.0]]
})
.backdropBlur(15)
} else if (type === 'immersive') {
Column()
.width(40)
.height(40)
.borderRadius(20)
.linearGradient({
angle: 180,
colors: [['rgba(0,0,0,0.4)', 0.0], ['rgba(0,0,0,0.1)', 0.5], ['rgba(0,0,0,0.3)', 1.0]]
})
.backdropBlur(10)
} else if (type === 'transparent') {
Column()
.width(40)
.height(40)
.borderRadius(20)
.border({
width: 2,
color: '#E0E0E0',
style: BorderStyle.Dashed
})
} else {
Column()
.width(40)
.height(40)
.borderRadius(20)
.linearGradient({
angle: 135,
colors: [[colors[0], 0.0], [colors[1], 1.0]]
})
}
Text(label)
.fontSize(12)
.fontColor(this.selectedStyle === style ? '#007AFF' : '#666666')
.margin({ top: 4 })
}
.padding(8)
.borderRadius(12)
.border({
width: this.selectedStyle === style ? 2 : 0,
color: '#007AFF'
})
.backgroundColor(this.selectedStyle === style ? '#E3F2FD' : 'transparent')
.onClick(() => {
this.selectedStyle = style;
})
}
@Builder
buildDisplayOptions() {
Column({ space: 12 }) {
Text('显示选项(内测功能)')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#1A1A1A')
.alignSelf(ItemAlign.Start)
this.buildToggle('显示时间', this.showTime, (value) => { this.showTime = value; })
this.buildToggle('显示天气', this.showWeather, (value) => { this.showWeather = value; })
this.buildToggle('显示诗词', this.showQuote, (value) => { this.showQuote = value; })
}
.width('100%')
.padding(16)
.backgroundColor('#F5F5F5')
.borderRadius(12)
}
@Builder
buildToggle(label: string, value: boolean, onChange: (value: boolean) => void) {
Row() {
Text(label)
.fontSize(14)
.fontColor('#1A1A1A')
Toggle({ type: ToggleType.Switch, isOn: value })
.selectedColor('#007AFF')
.onChange(onChange)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
@Builder
buildContentSettings() {
Column({ space: 12 }) {
Text('内容设置')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#1A1A1A')
.alignSelf(ItemAlign.Start)
Column({ space: 8 }) {
Text('诗词内容')
.fontSize(14)
.fontColor('#666666')
.alignSelf(ItemAlign.Start)
TextArea({ text: $$this.quoteText })
.width('100%')
.height(80)
.fontSize(14)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.padding(12)
}
Column({ space: 8 }) {
Text('作者信息')
.fontSize(14)
.fontColor('#666666')
.alignSelf(ItemAlign.Start)
TextInput({ text: $$this.authorText })
.width('100%')
.height(44)
.fontSize(14)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.padding({ left: 12, right: 12 })
}
}
.width('100%')
.padding(16)
.backgroundColor('#F5F5F5')
.borderRadius(12)
}
getGradientColors(): Array<[ResourceColor, number]> {
switch (this.selectedStyle) {
case 'classic':
return [['#7B1FA2', 0.0], ['#9C27B0', 1.0]];
case 'modern':
return [['#2E7D32', 0.0], ['#4CAF50', 1.0]];
case 'minimal':
return [['#424242', 0.0], ['#757575', 1.0]];
case 'elegant':
return [['#FF6B6B', 0.0], ['#FF8E53', 1.0]];
default:
return [['#7B1FA2', 0.0], ['#9C27B0', 1.0]];
}
}
}

View File

@@ -0,0 +1,17 @@
import { FlutterPlugin, FlutterPluginBinding, Log } from '@ohos/flutter_ohos';
const TAG = "ShortcutPlugin";
export default class ShortcutPlugin implements FlutterPlugin {
getUniqueClassName(): string {
return TAG;
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
Log.i(TAG, "ShortcutPlugin attached to engine");
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
Log.i(TAG, "ShortcutPlugin detached from engine");
}
}

View File

@@ -0,0 +1,82 @@
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
],
"metadata": [
{
"name": "ohos.ability.shortcuts",
"resource": "$profile:shortcuts_config"
}
]
},
{
"name": "WidgetMenuAbility",
"srcEntry": "./ets/ability/WidgetMenuAbility.ets",
"description": "$string:WidgetMenuAbility_desc",
"icon": "$media:icon",
"label": "$string:WidgetMenuAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
},
],
"extensionAbilities": [
{
"name": "WidgetFormAbility",
"srcEntry": "./ets/formability/WidgetFormAbility.ets",
"description": "$string:WidgetFormAbility_desc",
"icon": "$media:icon",
"label": "$string:WidgetFormAbility_label",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
],
"requestPermissions": [
{"name" : "ohos.permission.INTERNET"},
{"name" : "ohos.permission.VIBRATE"}
]
}
}

View File

@@ -0,0 +1,8 @@
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}

View File

@@ -0,0 +1,100 @@
{
"string": [
{
"name": "module_desc",
"value": "📜 诗词卡片"
},
{
"name": "EntryAbility_desc",
"value": "📜 诗词卡片主程序"
},
{
"name": "EntryAbility_label",
"value": "📜 诗词卡片"
},
{
"name": "WidgetFormAbility_desc",
"value": "每日诗词桌面卡片"
},
{
"name": "WidgetFormAbility_label",
"value": "📜 每日诗词"
},
{
"name": "WidgetMenuAbility_desc",
"value": "卡片样式与快捷操作"
},
{
"name": "WidgetMenuAbility_label",
"value": "⚙️ 卡片选项"
},
{
"name": "shortcut_add_widget",
"value": " 笔记"
},
{
"name": "shortcut_widget_settings",
"value": "🔧 卡片信息"
},
{
"name": "shortcut_open_app",
"value": "📱 打开应用"
},
{
"name": "menu_style_classic",
"value": "🎨 经典风格"
},
{
"name": "menu_style_classic_desc",
"value": "传统诗词卡片样式"
},
{
"name": "menu_style_modern",
"value": "✨ 现代风格"
},
{
"name": "menu_style_modern_desc",
"value": "简洁现代的设计风格"
},
{
"name": "menu_style_minimal",
"value": "◽ 极简风格"
},
{
"name": "menu_style_minimal_desc",
"value": "最小化设计,突出内容"
},
{
"name": "menu_style_elegant",
"value": "💎 优雅风格"
},
{
"name": "menu_style_elegant_desc",
"value": "精致优雅的视觉效果"
},
{
"name": "widget_display_name_small",
"value": "🎋 诗词短句"
},
{
"name": "widget_display_name_medium",
"value": "📖 经典诗词"
},
{
"name": "widget_display_name_large",
"value": "🎨 诗词意境"
},
{
"name": "widget_desc_small",
"value": "📱 小尺寸2x2卡片展示精选诗词短句"
},
{
"name": "widget_desc_medium",
"value": "📱 中等尺寸2x4卡片展示经典诗词作品"
},
{
"name": "widget_desc_large",
"value": "📱 大尺寸4x4卡片完整展示诗词意境"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,58 @@
{
"forms": [
{
"name": "WidgetCardSmall",
"displayName": "$string:widget_display_name_small",
"description": "$string:widget_desc_small",
"src": "./ets/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "00:00",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
},
{
"name": "WidgetCardMedium",
"displayName": "$string:widget_display_name_medium",
"description": "$string:widget_desc_medium",
"src": "./ets/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": false,
"updateEnabled": true,
"scheduledUpdateTime": "00:00",
"updateDuration": 1,
"defaultDimension": "2*4",
"supportDimensions": ["2*4"]
},
{
"name": "WidgetCardLarge",
"displayName": "$string:widget_display_name_large",
"description": "$string:widget_desc_large",
"src": "./ets/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": false,
"updateEnabled": true,
"scheduledUpdateTime": "00:00",
"updateDuration": 1,
"defaultDimension": "4*4",
"supportDimensions": ["4*4"]
}
]
}

View File

@@ -0,0 +1,7 @@
{
"src": [
"pages/Index",
"pages/WidgetSettings",
"pages/WidgetQuoteSettings"
]
}

View File

@@ -0,0 +1,16 @@
{
"shortcuts": [
{
"shortcutId": "open_app",
"label": "$string:shortcut_open_app",
"icon": "$media:icon",
"wants": [
{
"bundleName": "app.whushu.poes",
"moduleName": "entry",
"abilityName": "EntryAbility"
}
]
}
]
}

View File

@@ -0,0 +1,56 @@
{
"string": [
{
"name": "module_desc",
"value": "Poetry Widget"
},
{
"name": "EntryAbility_desc",
"value": "Main App"
},
{
"name": "EntryAbility_label",
"value": "📜 Poetry Card"
},
{
"name": "WidgetFormAbility_desc",
"value": "Daily Poetry Widget"
},
{
"name": "WidgetFormAbility_label",
"value": "📜 Daily Poetry"
},
{
"name": "WidgetMenuAbility_desc",
"value": "Widget Style & Shortcuts"
},
{
"name": "WidgetMenuAbility_label",
"value": "⚙️ Widget Options"
},
{
"name": "widget_display_name_small",
"value": "🎋 Poetry Phrase"
},
{
"name": "widget_display_name_medium",
"value": "📖 Classic Poetry"
},
{
"name": "widget_display_name_large",
"value": "🎨 Poetry Mood"
},
{
"name": "widget_desc_small",
"value": "📱 Small 2x2 card, showing selected poetry phrases"
},
{
"name": "widget_desc_medium",
"value": "📱 Medium 2x4 card, showing classic poetry works"
},
{
"name": "widget_desc_large",
"value": "📱 Large 4x4 card, showing complete poetry mood"
}
]
}

View File

@@ -0,0 +1,8 @@
{
"string": [
{
"name": "enable_impeller",
"value": "true"
}
]
}

View File

@@ -0,0 +1,37 @@
{
"SWITCH": 1,
"TRANSLATE": [
{
"serial_number": 1,
"min": 800,
"max": -1,
"preferred_fps": 90
},
{
"serial_number": 2,
"min": 77,
"max": 800,
"preferred_fps": 120
},
{
"serial_number": 3,
"min": 46,
"max": 77,
"preferred_fps": 90
},
{
"serial_number": 4,
"min": 10,
"max": 46,
"preferred_fps": 72
},
{
"serial_number": 5,
"min": 0,
"max": 10,
"preferred_fps": 60
}
],
"SCALE": [],
"ROTATION": []
}

View File

@@ -0,0 +1,56 @@
{
"string": [
{
"name": "module_desc",
"value": "诗词桌面卡片"
},
{
"name": "EntryAbility_desc",
"value": "主应用"
},
{
"name": "EntryAbility_label",
"value": "📜 诗词卡片"
},
{
"name": "WidgetFormAbility_desc",
"value": "每日诗词桌面卡片"
},
{
"name": "WidgetFormAbility_label",
"value": "📜 每日诗词"
},
{
"name": "WidgetMenuAbility_desc",
"value": "卡片样式与快捷操作"
},
{
"name": "WidgetMenuAbility_label",
"value": "⚙️ 卡片选项"
},
{
"name": "widget_display_name_small",
"value": "🎋 诗词短句"
},
{
"name": "widget_display_name_medium",
"value": "📖 经典诗词"
},
{
"name": "widget_display_name_large",
"value": "🎨 诗词意境"
},
{
"name": "widget_desc_small",
"value": "📱 小尺寸2x2卡片展示精选诗词短句"
},
{
"name": "widget_desc_medium",
"value": "📱 中等尺寸2x4卡片展示经典诗词作品"
},
{
"name": "widget_desc_large",
"value": "📱 大尺寸4x4卡片完整展示诗词意境"
}
]
}

View File

@@ -0,0 +1,35 @@
import hilog from '@ohos.hilog';
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
export default function abilityTest() {
describe('ActsAbilityTest', function () {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
beforeAll(function () {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
})
beforeEach(function () {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
})
afterEach(function () {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
})
afterAll(function () {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
})
it('assertContain',0, function () {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
let a = 'abc'
let b = 'b'
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
expect(a).assertContain(b)
expect(a).assertEqual(a)
})
})
}

View File

@@ -0,0 +1,5 @@
import abilityTest from './Ability.test'
export default function testsuite() {
abilityTest()
}

View File

@@ -0,0 +1,48 @@
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
import hilog from '@ohos.hilog';
import { Hypium } from '@ohos/hypium';
import testsuite from '../test/List.test';
import window from '@ohos.window';
export default class TestAbility extends UIAbility {
onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');
var abilityDelegator: any
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
var abilityDelegatorArguments: any
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)
}
onDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage) {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
windowStage.loadContent('testability/pages/Index', (err, data) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',
JSON.stringify(data) ?? '');
});
}
onWindowStageDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
}
onForeground() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
}
onBackground() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
}
}

View File

@@ -0,0 +1,36 @@
import hilog from '@ohos.hilog';
@Entry
@Component
struct Index {
aboutToAppear() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');
}
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button() {
Text('next page')
.fontSize(20)
.fontWeight(FontWeight.Bold)
}.type(ButtonType.Capsule)
.margin({
top: 20
})
.backgroundColor('#0D9FFB')
.width('35%')
.height('5%')
.onClick(()=>{
})
}
.width('100%')
}
.height('100%')
}
}

View File

@@ -0,0 +1,50 @@
import hilog from '@ohos.hilog';
import TestRunner from '@ohos.application.testRunner';
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
var abilityDelegator = undefined
var abilityDelegatorArguments = undefined
async function onAbilityCreateCallback() {
hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
}
async function addAbilityMonitorCallback(err: any) {
hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
}
export default class OpenHarmonyTestRunner implements TestRunner {
constructor() {
}
onPrepare() {
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
}
async onRun() {
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility'
let lMonitor = {
abilityName: testAbilityName,
onAbilityCreate: onAbilityCreateCallback,
};
abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName
var debug = abilityDelegatorArguments.parameters['-D']
if (debug == 'true')
{
cmd += ' -D'
}
hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd);
abilityDelegator.executeShellCommand(cmd,
(err: any, d: any) => {
hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? '');
hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? '');
hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? '');
})
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
}
}

View File

@@ -0,0 +1,37 @@
{
"module": {
"name": "entry_test",
"type": "feature",
"description": "$string:module_test_desc",
"mainElement": "TestAbility",
"deviceTypes": [
"phone"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:test_pages",
"abilities": [
{
"name": "TestAbility",
"srcEntry": "./ets/testability/TestAbility.ets",
"description": "$string:TestAbility_desc",
"icon": "$media:icon",
"label": "$string:TestAbility_label",
"exported": true,
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"skills": [
{
"actions": [
"action.system.home"
],
"entities": [
"entity.system.home"
]
}
]
}
]
}
}

View File

@@ -0,0 +1,8 @@
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}

View File

@@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_test_desc",
"value": "test ability description"
},
{
"name": "TestAbility_desc",
"value": "the test ability"
},
{
"name": "TestAbility_label",
"value": "test label"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,5 @@
{
"src": [
"testability/pages/Index"
]
}