精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

HarmonyOS開發實例—蜜蜂AI助手

系統 OpenHarmony
HarmonyOS是華為公司開發的操作系統,它的設計理念是面向未來的全場景智慧體驗,可在各種設備上運行,包括手機、平板電腦、智能手表、智能音箱等。

想了解更多關于開源的內容,請訪問:

51CTO 鴻蒙開發者社區

https://ost.51cto.com

一、前言

自華為宣布HarmonyOS NEXT全面啟動,近期新浪、B站、小紅書、支付寶等各領域頭部企業紛紛啟動鴻蒙原生應用開發。據媒體統計,如今Top20的應用里,已經有近一半開始了鴻蒙原生應用開發。雖然目前HarmonyOS NEXT還未面向個人開發者開放,但我們可以體驗并使用最新的API9和開發工具,嘗試開發元服務,這個鴻蒙新的應用形態。體驗未來在HarmonyOS NEXT上實現的應用開發。但需要注意的是, 基于API9開發的應用或元服務是不可以適配HarmonyOS NEXT版本的,大家也可以期待一下明年推出的適配HarmonyOS NEXT新版本。

本文主要是基于蜜蜂AI元服務的開發案例:主要的功能有

元服務內部功能:

  • 提供兩個Tabs,首頁和我的。
  • 用戶只有登錄之后才可以去使用蜜蜂AI的功能。
  • 目前現有的知識庫包括知識百科小助手,節日小助手,文本翻譯小助手,產品名稱小助手,以及道歉信小助手等。
  • 用戶使用小助手之后,我們可以保存對話到列表內,下次快速的進行訪問。

元服務卡片:

  • 提供2-4的卡片,卡片界面展示每日妙語,點擊即可刷新。
  • 提供1-2的卡片,實現快速訪問首頁。
  • 提供2-2卡片,可以快速使用包括知識百科小助手,節日小助手,文本翻譯小助手,產品名稱小助手。
  • 提供4-4卡片,可以快速到達登陸頁面,訪問小助手等。

1、HarmonyOS

HarmonyOS是華為公司開發的操作系統,它的設計理念是面向未來的全場景智慧體驗,可在各種設備上運行,包括手機、平板電腦、智能手表、智能音箱等。HarmonyOS采用分布式技術,可以將不同設備之間的計算資源連接起來,實現設備間的協同工作,提高系統的性能和穩定性。此外,HarmonyOS還擁有高度自適應的界面、多屏協同等特性,使用戶能夠在不同設備上實現無縫的體驗。

2、元服務

在萬物互聯時代,人均持有設備量不斷攀升,設備和場景的多樣性,使應用開發變得更加復雜、應用入口更加多樣。在此背景下,應用提供方和用戶迫切需要一種新的服務提供方式,使應用開發更簡單、服務(如聽音樂、打車等)的獲取和使用更便捷。為此,HarmonyOS除支持傳統方式的需要安裝的應用(以下簡稱傳統應用)外,還支持更加方便快捷的免安裝的應用(即元服務)。

3、介紹AppGallery Connect(AGC)

AppGallery Connect(簡稱AGC)致力于為應用的創意、開發、分發、運營、經營各環節提供一站式服務,構建全場景智慧化的應用生態體驗。

4、蜜蜂AI元服務助手背景

目前AI正火,而我自己也想將鴻蒙和AI做一結合,于是有了蜜蜂這個作品。

元服務與傳統應用對比

項目

元服務

傳統應用

軟件包形態

App Pack(.app)

App Pack(.app)

分發平臺

由應用市場(AppGallery)管理和分發

由應用市場(AppGallery)管理和分發

安裝后有無桌面icon

無桌面icon,但可手動添加到桌面,顯示形式為服務卡片

有桌面icon

HAP免安裝要求

所有HAP(包括Entry HAP和Feature HAP)均需滿足免安裝要求

所有HAP(包括Entry HAP和Feature HAP)均為非免安裝的

新建元服務應用。

開通:

AI平臺https://fulitimes.com/登陸賬號17752170152

https://ai.fulitimes.com/model?modelId=

如何運行

二、準備工作

1、HarmonyOS應用開發環境

工欲善其事,必先利其器,我們首先要做的就是搭建開發環境。

這里面我們分為三步走。

(1)環境安裝

首先在這邊安裝最新的IDE:

下載鏈接:https://developer.harmonyos.com/cn/develop/deveco-studio/#download。

我的是M1,所以我們下載這一個就可以。

(2)環境配置

下載完成之后,我們就開始配置開發環境。下載SDK及工具鏈,首次使用DevEco Studio,工具的配置向導會引導您下載SDK及工具鏈。配置向導默認下載 API Version 9的SDK及工具鏈,我們選擇默認就好。

下載nodejs和ohpm,記得最好HarmonyOS SDK路徑中不能包含中文字符。

下載完成之后,我們下載HarmonyOS SDK。

在彈出的SDK下載信息頁面,單擊Next,并在彈出的License Agreement窗口,閱讀License協議,需同意License協議后,單擊Next。

目前最新的應該是3.2.13.5。

確認設置項的信息,點擊Next開始安裝。

等待Node.js、ohpm和SDK下載完成后,單擊Finish,界面會進入到DevEco Studio歡迎頁。

(3)創建HelloWord

在DevEco Studio的歡迎頁,選擇Create Project開始創建一個新工程。

根據工程創建向導,在HarmonyOS頁簽,選擇“Empty Ability”模板,單擊Next。

單擊Next,各個參數保持默認值即可,單擊Finish。

(4)運行Helloword

將搭載HarmonyOS手機與電腦連接。

單擊File>Project Structure >Project > SigningConfigs界面勾選“支持HarmonyOS,以及Automatically generate signature”,等待自動簽名完成即可,單擊OK。如右所示:。

在編輯窗口右上角的工具欄,單擊運行,等待編譯完成即可便運行在設備上。

這個時候真機就可以看到HelloWord。接下來我們就創建蜜蜂AI元服務。

2、創建蜜蜂AI元服務

這里我們的模版就不再選空模板了,而是直接選擇最后一個端云一體化模版

然后其他的就按照上面的配置就可以。完成項目的配置。

這里有個區別就是我們需要關聯云資源。所以我們創建的應用包名要牢記,這個要在后面我們云端配置的時候使用。

為工程關聯云開發所需的資源,即在DevEco Studio中選擇您的華為開發者賬號加入的開發者團隊,將該團隊在AGC的同包名應用關聯到當前工程,具體操作如下:

  • 若尚未登錄DevEco Studio,單擊“Sign in”,拉起瀏覽器在彈出的賬號登錄頁面,使用已實名認證的華為開發者賬號完成登錄。

單擊“Team”下拉框,選擇開發團隊。選中團隊后,系統根據工程包名自動查詢團隊下的同包名應用。若為首次創建且團隊下未創建同包名的應用,則提示需要在AGC平臺創建應用。

單擊“AppGallery Connect”打開AGC應用創建向導,填寫應用信息,單擊“確認”按鈕創建應用。

完成以上操作后,DevEco Studio即可獲取到同包名應用對應的項目信息。

3、AGC配置

我們登陸云側,創建元服務。

然后我們開通手機登陸和郵箱登錄服務。

三、實現登錄

當前AGC認證服務為HarmonyOS應用/服務提供的登錄認證方式有手機、郵箱兩種方式。本工程使用“手機號碼+驗證碼”的方式作為應用的登錄入口。而且我們在前面已經開通。

在登陸這一塊,用戶首次登陸的時候,我們會首先利用首選項檢查他的登陸狀態。

首選項工具類

/**
 * 首選項操作類
 */
import { PreferenceDBUtil } from '../utils/PreferencesDBUtil';


const preDbService = new PreferenceDBUtil();
preDbService.getPreStorage();

export const getDBPre = async (key: string) => {
  const value = await preDbService.getPreVal(key);
  return value;
}

export const putDBPre = async (key: string, value: string) => {
  await preDbService.putPreData(key, value);
}

然后跳用調用AGConnectAuth.requestEmailVerifyCode申請驗證碼,在entry/src/main/ets/services/Auth.ts認證工具類中添加郵箱驗證碼獲取方法。

import { MainPage } from "@hw-agconnect/auth-component-ohos"
import router from '@ohos.router'
import { LogUtil } from '../common/utils/LogUtil';
import { Constants } from '../common/Constants';
import { putPre } from '../common/service/PreService';
import { UserInfo } from '../common/UserInfo';

@Entry
@Component
struct Index {
  @State icon: Resource = router.getParams()['icon'];
  @State isAgreement:boolean = router.getParams()['isAgreement'];
  @State agreementContent:string = router.getParams()['agreementContent'];
  @State onSuccess: Function = router.getParams()['onSuccess'];
  @State onError: Function = router.getParams()['onError'];

  build() {
    Column() {
      MainPage({
        icon: this.icon,
        agreement: {
          isAgreement: this.isAgreement,
          agreementContent: this.agreementContent,
        },
        onSuccess: async (user) => {
          LogUtil.info(`登錄用戶信息:${JSON.stringify(user)}`);
          const loginUser = user['user'];
          const userInfo: UserInfo = {
            uid: loginUser['uid'],
            email: loginUser['email'],
            phone: loginUser['phone'] === undefined ? "" : loginUser['phone'].split('-')[1],
            displayName: loginUser['displayName'] === undefined ? "" : loginUser['displayName'],
            photoUrl: loginUser['photoUrl'] === undefined ? "/common/imgs/ic_user.svg" : loginUser['photoUrl']
          }
          await putPre(Constants.LOGIN_USER_KEY, JSON.stringify(userInfo));
          router.back();
        },
        onError: (err) => {
          LogUtil.error(`登錄用戶信息:${JSON.stringify(err)}`);
        }
      })
    }
  }

  aboutToAppear() {
  }
}

未登錄彈窗

/**
 * 未登錄彈窗
 */
import common from '@ohos.app.ability.common';
import router from '@ohos.router';
import { GlobalConstant } from '../common/constants/GlobalConstant';
@CustomDialog
export struct LoginTipDialogView {
  loginTipCtrl: CustomDialogController;
  build() {
    Column({ space: GlobalConstant.SIZE_8 }) {
      Row({ space: GlobalConstant.SIZE_4 }) {
        Image($r('app.media.ic_tip'))
          .width(GlobalConstant.SIZE_32)
          .height(GlobalConstant.SIZE_32)
        Text('溫馨提示')
          .fontSize($r('app.float.font_size_24'))
          .fontColor($r('app.color.tip_color'))
          .fontWeight(FontWeight.Bolder)
      }
      .width(GlobalConstant.PAGE_FULL)
      .height(GlobalConstant.SIZE_64)
      .padding({ left: GlobalConstant.SIZE_16 })
      Text('您還未登錄,請登錄后體驗功能!')
        .height(GlobalConstant.SIZE_48)
        .fontSize(Color.Black)
        .fontSize($r('app.float.font_size_18'))
        .fontWeight(FontWeight.Normal)
      Row({ space: GlobalConstant.SIZE_8 }) {
        Button('退出', { type: ButtonType.Normal })
          .borderRadius(GlobalConstant.SIZE_4)
          .backgroundColor($r('app.color.embellishment_color'))
          .fontColor($r('app.color.text_color_9'))
          .onClick(() => {
            const ctx = getContext(this) as common.UIAbilityContext;
            ctx.terminateSelf();
          })
        Button('去登錄', { type: ButtonType.Normal })
          .borderRadius(GlobalConstant.SIZE_4)
          .backgroundColor($r('app.color.embellishment_color'))
          .fontColor($r('app.color.auxiliary_color'))
          .onClick(() => {
            this.loginTipCtrl.close();
            router.pushUrl({
              params:{
                isAgreement: true,
                agreementContent: "",
                icon: "",
                type: ["HWID_VERIFY_CODE","PHONE"]
              },
              url: '@bundle:com.jianguo.ai/common/ets/LoginComponent/LoginPage',
            })
          })
      }
      .width(GlobalConstant.PAGE_FULL)
      .justifyContent(FlexAlign.Center)
    }
    .width(GlobalConstant.PAGE_96)
    .padding({ bottom: GlobalConstant.SIZE_20 })
    .borderRadius(GlobalConstant.SIZE_16)
    .backgroundColor(Color.White)
  }
}

四、實現蜜蜂AI助手頁面

我們這個應用主要的一個功能就是AI助手,所以這一塊我們分為三塊。

1、蜜蜂AI列表頁

關于列表頁,我們使用一個列表就可以。

/**
 * 首頁
 */
import { ConfigConstant } from '../common/constants/ConfigConstant'
import { GlobalConstant } from '../common/constants/GlobalConstant'
import { AiAppConfig } from '../common/dto/AiAppConfig';
import router from '@ohos.router'
import { getDBPre } from '../common/api/PreDbService';
@Component
export struct HomeView {

  @State aiAppList: Array<AiAppConfig> = ConfigConstant.DEFAULT_AI_APP_LIST;

  }

  build() {
    Column() {
      List() {
        ForEach(this.aiAppList, (item: AiAppConfig) => {
          ListItem() {
            Row({ space: GlobalConstant.SIZE_8 }) {
              Row() {
                Image(item.avatar)
                  .width(GlobalConstant.SIZE_64)
                  .height(GlobalConstant.SIZE_64)
                  .borderRadius(GlobalConstant.SIZE_32)
              }
              .height(GlobalConstant.PAGE_FULL)
              .layoutWeight(1)
              Column({ space: GlobalConstant.SIZE_16 }) {
                Text(item.name)
                  .fontSize($r('app.float.font_size_18'))
                Text(item.intro)
                  .fontSize($r('app.float.font_size_14'))
                  .fontColor($r('app.color.text_color_9'))
              }
              .height(GlobalConstant.PAGE_FULL)
              .layoutWeight(3)
              .justifyContent(FlexAlign.Center)
              .alignItems(HorizontalAlign.Start)
            }
            .width(GlobalConstant.PAGE_96)
            .height(GlobalConstant.SIZE_100)
            .paddingStyle()
            .borderRadius(GlobalConstant.SIZE_16)
            .shadow({
              radius: GlobalConstant.SIZE_16,
              color: $r('app.color.main_color')
            })
            .onClick(() => {
              router.pushUrl({
                url: "pages/detail/index",
                params: {
                  "AiAppConfig": item
                }
              })
            })
          }
          .width(GlobalConstant.PAGE_FULL)
          .paddingStyle()
          .borderRadius(GlobalConstant.SIZE_16)
        })
      }
      .listDirection(Axis.Vertical)
      
    }
    .width(GlobalConstant.PAGE_FULL)
    .height(GlobalConstant.PAGE_FULL)
    .padding(GlobalConstant.SIZE_8)
  }


}

效果圖

2、對話頁

關鍵代碼

build() {
    Column({ space: GlobalConstant.SIZE_8 }) {
      Stack({ alignContent: Alignment.Bottom }) {
        Column() {
          Column({ space: GlobalConstant.SIZE_4 }) {
            Text("蜜蜂AI助手")
              .fontSize($r('app.float.font_size_16'))
              .fontColor(Color.Black)
              .fontWeight(FontWeight.Bolder)
            Text("介紹")
              .fontSize($r('app.float.font_size_12'))
              .fontColor($r('app.color.text_color_9'))
              .fontWeight(FontWeight.Lighter)
          }
          .width(GlobalConstant.PAGE_FULL)
          .justifyContent(FlexAlign.Center)
          .padding({
            top: GlobalConstant.SIZE_4,
            bottom: GlobalConstant.SIZE_8
          })

          Scroll() {
            Column({ space: GlobalConstant.SIZE_8 }) {
              ForEach(this.chatContentArr, (chat: ChatInfo) => {
                if (chat.role === "assistant") {
                  Row() {
                    Row({ space: GlobalConstant.SIZE_8 }) {
                      Image(chat.avatar)
                        .width(GlobalConstant.SIZE_24)
                        .height(GlobalConstant.SIZE_24)
                      Row() {
                        Text(chat.content)
                          .fontSize($r('app.float.font_size_14'))
                          .fontColor(Color.Black)
                      }
                      .width(chat.content.length > 15 ? GlobalConstant.PAGE_76 : 'auto')
                      .backgroundColor($r('app.color.embellishment_color'))
                      .padding({
                        left: GlobalConstant.SIZE_16,
                        right: GlobalConstant.SIZE_16,
                        top: GlobalConstant.SIZE_8,
                        bottom: GlobalConstant.SIZE_8
                      })
                      .borderRadius({
                        topRight: GlobalConstant.SIZE_4,
                        bottomLeft: GlobalConstant.SIZE_8,
                        bottomRight: GlobalConstant.SIZE_4
                      })
                    }
                    .justifyContent(FlexAlign.Start)
                    .alignItems(VerticalAlign.Top)
                  }
                  .width(GlobalConstant.PAGE_FULL)
                  .justifyContent(FlexAlign.Start)
                }
                if (chat.role === "user") {
                  Row() {
                    Row({ space: GlobalConstant.SIZE_8 }) {
                      Row() {
                        Text(chat.content)
                          .fontSize($r('app.float.font_size_14'))
                          .fontColor(Color.Black)
                      }
                      .width(chat.content.length > 15 ? GlobalConstant.PAGE_76 : 'auto')
                      .backgroundColor($r('app.color.tab_default_color'))
                      .padding({
                        left: GlobalConstant.SIZE_16,
                        right: GlobalConstant.SIZE_16,
                        top: GlobalConstant.SIZE_8,
                        bottom: GlobalConstant.SIZE_8
                      })
                      .borderRadius({
                        topLeft: GlobalConstant.SIZE_4,
                        bottomLeft: GlobalConstant.SIZE_4,
                        bottomRight: GlobalConstant.SIZE_8
                      })
                      Image(chat.avatar)
                        .width(GlobalConstant.SIZE_24)
                        .height(GlobalConstant.SIZE_24)
                    }
                    .justifyContent(FlexAlign.End)
                    .alignItems(VerticalAlign.Top)
                  }
                  .width(GlobalConstant.PAGE_FULL)
                  .justifyContent(FlexAlign.End)
                }
              })
            }.width(GlobalConstant.PAGE_FULL)
          }
          .width(GlobalConstant.PAGE_96)
          .scrollable(ScrollDirection.Vertical)
          .flexShrink(1)
        }
        .width(GlobalConstant.PAGE_FULL)
        .height(GlobalConstant.PAGE_FULL)
        .padding({ bottom: GlobalConstant.SIZE_50 })

        Row({ space: GlobalConstant.SIZE_8 }) {
          TextInput({ placeholder: "請輸入提示詞...", text: this.inputValue })
            .height(GlobalConstant.SIZE_48)
            .fontSize($r('app.float.font_size_16'))
            .placeholderFont({ size: $r('app.float.font_size_16') })
            .placeholderColor($r('app.color.text_color_9'))
            .borderRadius($r('app.float.size_8'))
            .backgroundColor($r('app.color.card_bg_color'))
            .flexShrink(1)
            .onChange((value: string) => {
              this.inputValue = value;
            })
          Image($r('app.media.ic_send'))
            .width(GlobalConstant.SIZE_32)
            .height(GlobalConstant.SIZE_32)
            .onClick(async () => {
              this.loadingCtrl.open();
              if (this.inputValue === "") {
                promptAction.showToast({
                  message: "發送內容不能為空!"
                })
                return;
              }
              await this.getAiResult();
            })
        }
        .width(GlobalConstant.PAGE_FULL)
        .padding({
          left: GlobalConstant.SIZE_8,
          right: GlobalConstant.SIZE_8
        })
        .backgroundColor($r('app.color.card_bg_color'))
      }
      .width(GlobalConstant.PAGE_FULL)
      .height(GlobalConstant.PAGE_FULL)
    }
    .width(GlobalConstant.PAGE_FULL)
    .height(GlobalConstant.PAGE_FULL)
  }

效果圖

加載中:

問答后:

五、服務卡片

1、服務卡片

服務卡片(以下簡稱“卡片”)是一種界面展示形式,可以將應用的重要信息或操作前置到卡片,以達到服務直達、減少體驗層級的目的。卡片常用于嵌入到其他應用(當前卡片使用方只支持系統應用,如桌面)中作為其界面顯示的一部分,并支持拉起頁面、發送消息等基礎的交互功能。

服務卡片架構

下圖為服務卡片架構。

另外了解卡片概念有助于我們更好的使用服務卡片。

卡片的基本概念:

  • 卡片使用方:如上圖中的桌面,顯示卡片內容的宿主應用,控制卡片在宿主中展示的位置。
  • 應用圖標:應用入口圖標,點擊后可拉起應用進程,圖標內容不支持交互。
  • 卡片:具備不同規格大小的界面展示,卡片的內容可以進行交互,如實現按鈕進行界面的刷新、應用的跳轉等。
  • 卡片提供方:包含卡片的應用,提供卡片的顯示內容、控件布局以及控件點擊處理邏輯。
  • FormExtensionAbility:卡片業務邏輯模塊,提供卡片創建、銷毀、刷新等生命周期回調。
  • 卡片頁面:卡片UI模塊,包含頁面控件、布局、事件等顯示和交互信息。

動態卡片事件能力說明

針對動態卡片,ArkTS卡片中提供了postCardAction()接口用于卡片內部和提供方應用間的交互,當前支持router、message和call三種類型的事件,僅在卡片中可以調用。后面我們也會用到這一塊的內容。

2、服務卡片創建方式

創建工程時,選擇Atomic Service,默認自帶卡片,也可以在創建工程后右鍵新建卡片。

另外就是我們可能不止一個卡片,所以,后續我們可以這樣創建服務卡片。

卡片相關的配置文件主要包含FormExtensionAbility的配置和卡片的配置兩部分。

卡片需要在module.json5配置文件中的extensionAbilities標簽下,配置FormExtensionAbility相關信息。FormExtensionAbility需要填寫metadata元信息標簽,其中鍵名稱為固定字符串“ohos.extension.form”,資源為卡片的具體配置信息的索引。

{
  "module": {
    ...
    "extensionAbilities": [
      {
        "name": "EntryFormAbility",
        "srcEntry": "./ets/entryformability/EntryFormAbility.ets",
        "label": "$string:EntryFormAbility_label",
        "description": "$string:EntryFormAbility_desc",
        "type": "form",
        "metadata": [
          {
            "name": "ohos.extension.form",
            "resource": "$profile:form_config"
          }
        ]
      }
    ]
  }
}

卡片的具體配置信息。在上述FormExtensionAbility的元信息(“metadata”配置項)中,可以指定卡片具體配置信息的資源索引。例如當resource指定為$profile:form_config時,會使用開發視圖的resources/base/profile/目錄下的form_config.json作為卡片profile配置文件。內部字段結構說明如下表所示。

卡片form_config.json配置文件

屬性名稱

含義

數據類型

是否可缺省

name

表示卡片的名稱,字符串最大長度為127字節。

字符串


description

表示卡片的描述。取值可以是描述性內容,也可以是對描述性內容的資源索引,以支持多語言。字符串最大長度為255字節。

字符串

可缺省,缺省為空。

src

表示卡片對應的UI代碼的完整路徑。當為ArkTS卡片時,完整路徑需要包含卡片文件的后綴,如:“./ets/widget/pages/WidgetCard.ets”。當為JS卡片時,完整路徑無需包含卡片文件的后綴,如:“./js/widget/pages/WidgetCard”

字符串


uiSyntax

表示該卡片的類型,當前支持如下兩種類型:- arkts:當前卡片為ArkTS卡片。- hml:當前卡片為JS卡片。

字符串

可缺省,缺省值為hml

window

用于定義與顯示窗口相關的配置。

對象

可缺省,缺省值見表2。

isDefault

表示該卡片是否為默認卡片,每個UIAbility有且只有一個默認卡片。- true:默認卡片。- false:非默認卡片。

布爾值


colorMode

表示卡片的主題樣式,取值范圍如下:- auto:跟隨系統的顏色模式值選取主題。- dark:深色主題。- light:淺色主題。

字符串

可缺省,缺省值為“auto”。

supportDimensions

表示卡片支持的外觀規格,取值范圍:- 1 * 2:表示1行2列的二宮格。- 2 * 2:表示2行2列的四宮格。- 2 * 4:表示2行4列的八宮格。- 4 * 4:表示4行4列的十六宮格。

字符串數組


defaultDimension

表示卡片的默認外觀規格,取值必須在該卡片supportDimensions配置的列表中。

字符串


updateEnabled

表示卡片是否支持周期性刷新(包含定時刷新和定點刷新),取值范圍:- true:表示支持周期性刷新,可以在定時刷新(updateDuration)和定點刷新(scheduledUpdateTime)兩種方式任選其一,當兩者同時配置時,定時刷新優先生效。- false:表示不支持周期性刷新。

布爾類型


scheduledUpdateTime

表示卡片的定點刷新的時刻,采用24小時制,精確到分鐘。> 說明:> updateDuration參數優先級高于scheduledUpdateTime,兩者同時配置時,以updateDuration配置的刷新時間為準。

字符串

可缺省,缺省時不進行定點刷新。

updateDuration

表示卡片定時刷新的更新周期,單位為30分鐘,取值為自然數。當取值為0時,表示該參數不生效。當取值為正整數N時,表示刷新周期為30*N分鐘。> 說明:> updateDuration參數優先級高于scheduledUpdateTime,兩者同時配置時,以updateDuration配置的刷新時間為準。

數值

可缺省,缺省值為“0”。

formConfigAbility

表示卡片的配置跳轉鏈接,采用URI格式。

字符串

可缺省,缺省值為空。

metadata

表示卡片的自定義信息,參考Metadata數組標簽。

對象

可缺省,缺省值為空。

dataProxyEnabled

表示卡片是否支持卡片代理刷新,取值范圍:- true:表示支持代理刷新。- false:表示不支持代理刷新。設置為true時,定時刷新和下次刷新不生效,但不影響定點刷新。

布爾類型

可缺省,缺省值為false。

isDynamic

表示此卡片是否為動態卡片(僅針對ArkTS卡片生效)。- true:為動態卡片 。- false:為靜態卡片。

布爾類型

可缺省,缺省值為true。

transparencyEnabled

表示是否支持卡片使用方設置此卡片的背景透明度(僅對系統應用的ArkTS卡片生效。)。- true:支持設置背景透明度 。- false:不支持設置背景透明度。

布爾類型

可缺省,缺省值為false。

{
  "forms": [
    {
      "uiSyntax": "arkts",
      "isDefault": true,
      "defaultDimension": "1*2",
      "scheduledUpdateTime": "00:00",
      "src": "./ets/jianguoaizhushoutuijian/jianguoaizhushoutuijian.ets",
      "name": "jianguoaizhushoutuijian",
      "description": "蜜蜂AI助手推薦",
      "window": {
        "designWidth": 720,
        "autoDesignWidth": true
      },
      "supportDimensions": [
        "1*2"
      ],
      "updateEnabled": true,
      "updateDuration": 0
    },
    {
      "uiSyntax": "arkts",
      "isDefault": false,
      "defaultDimension": "2*2",
      "src": "./ets/jianguoaizhushou/jianguoaizhushou.ets",
      "name": "jianguoaizhushou",
      "description": "蜜蜂AI助手,幫你所幫",
      "window": {
        "designWidth": 720,
        "autoDesignWidth": true
      },
      "supportDimensions": [
        "2*2"
      ],
      "updateEnabled": false,
      "updateDuration": 0
    },
    {
      "name": "poetry",
      "description": "蜂蜜AI助手助你學妙語.",
      "src": "./ets/poetry/pages/PoetryCard.ets",
      "uiSyntax": "arkts",
      "window": {
        "designWidth": 720,
        "autoDesignWidth": true
      },
      "colorMode": "auto",
      "isDefault": false,
      "updateEnabled": false,
      "scheduledUpdateTime": "10:30",
      "updateDuration": 1,
      "defaultDimension": "2*4",
      "supportDimensions": [
        "2*4"
      ]
    },
    {
      "name": "history",
      "description": "蜂蜜AI助手歷史記錄",
      "src": "./ets/history/pages/HistoryCard.ets",
      "uiSyntax": "arkts",
      "window": {
        "designWidth": 720,
        "autoDesignWidth": true
      },
      "colorMode": "auto",
      "isDefault": false,
      "updateEnabled": false,
      "scheduledUpdateTime": "10:30",
      "updateDuration": 1,
      "defaultDimension": "4*4",
      "supportDimensions": [
        "4*4"
      ]
    }
  ]
}

3、實現2*2/2*4/4*4服務卡片

1-2卡片

首先我們來看1-2卡片的實現。

@Entry
@Component
struct Jianguoaizhushoutuijian {
  private readonly PAGE_FULL: string = "100%";
  private readonly SIZE_4: number = 4;
  build() {
    Row({ space: this.SIZE_4 }) {
      Image('/common/imgs/ic_user.svg')
        .width($r('app.float.size_32'))
        .height($r('app.float.size_32'))

      Column() {
        Text('蜜蜂AI助手')
          .fontSize($r('app.float.font_size_14'))
          .fontColor($r('app.color.main_color'))
          .fontWeight(FontWeight.Bolder)

        Text('知識百科/文本翻譯/...')
          .fontSize($r('app.float.font_size_12'))
          .fontColor($r('app.color.text_color_9'))
      }
      .height(this.PAGE_FULL)
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Start)
    }    
    .width(this.PAGE_FULL)
    .height(this.PAGE_FULL)
    .padding({
      left: $r('app.float.size_8'),
      right: $r('app.float.size_8')
    })
    .onClick(() => {
      postCardAction(this, {
        "action": "router",
        "abilityName": "EntryAbility",
        "params": {}
      });
    })
  }
}
效果

實現效果如圖所示:

原理

我可以用router來進行跳轉,默認不傳遞任何參數,就會跳轉到首頁。

.onClick(() => {
      postCardAction(this, {
        "action": "router",
        "abilityName": "EntryAbility",
        "params": {}
      });
    })

2-4的卡片

我們來看妙語集這一個2-4卡片的實現。

完整代碼
const storage = new LocalStorage();
@Entry(storage)
@Component
struct PoetryCard {
  readonly PAGE_FULL: string = "100%";
  readonly PRE_96: string = "96%";
  readonly SIZE_40: number = 40;
  readonly SIZE_30: number = 30;
  readonly SIZE_20: number = 20;
  readonly SIZE_16: number = 16;
  readonly SIZE_8: number = 8;
  readonly SIZE_4: number = 4;

  @LocalStorageProp("poetry") poetry: any = {
    content: "秀樾橫塘十里香,水花晚色靜年芳。",
    author: "蔡松年",
    origin: "鷓鴣天·賞荷",
    category: "古詩文-四季-夏天"
  };

  build() {
    Column() {
      Row({ space: this.SIZE_8 }) {
        Image("/common/imgs/ic_ai_home.svg")
          .width(this.SIZE_20)
          .height(this.SIZE_20)
          .fillColor($r('app.color.text_font_color'))
        Text('妙語集')
          .fontSize($r('app.float.font_size_14'))
          .fontColor($r('app.color.text_font_color'))
      }
      .width(this.PAGE_FULL)
      .height(this.SIZE_40)
      .linearGradient({
        angle: 45,
        colors: [[$r('app.color.main_color'), 0.1], [$r('app.color.auxiliary_color'), 1.0]]
      })
      .padding({
        left: this.SIZE_16,
        right: this.SIZE_16
      })

      Column() {
        Stack({ alignContent: Alignment.TopEnd }) {
          Column({ space: this.SIZE_8 }) {
            Text(this.poetry['origin'])
              .fontSize($r('app.float.font_size_18'))
              .fontWeight(FontWeight.Bolder)
              .fontColor($r('app.color.text_color_title'))
            Text(this.poetry['author'])
              .fontSize($r('app.float.font_size_14'))
              .fontWeight(FontWeight.Medium)
              .fontColor($r('app.color.text_color_9'))

            Text(this.poetry['content'])
              .fontSize($r('app.float.font_size_16'))
              .fontColor($r('app.color.text_color_title'))
          }
          .width(this.PRE_96)
          .height(this.PRE_96)
          .justifyContent(FlexAlign.Center)
          Button({ type: ButtonType.Capsule }) {
            Image($r('app.media.ic_refreshing'))
              .width(this.SIZE_20)
              .height(this.SIZE_20)
              .fillColor(Color.White)
          }
          .width(this.SIZE_30).height(this.SIZE_30)
          .backgroundColor($r('app.color.tip_color'))
          .onClick(() => {
            postCardAction(this, {
              'action': 'message',
              'params': {
                'function': 'refreshing'
              }
            })
          })
        }
      }
      .width(this.PAGE_FULL)
      .flexShrink(1)
      .padding({top: this.SIZE_4, bottom: this.SIZE_8})
    }
    .width(this.PAGE_FULL)
    .height(this.PAGE_FULL)
  }
}
效果

原理

我們是如何實現數據刷新的呢?

我們首先判斷返回的functionName,如果是refreshing,那么我們就去請求網絡接口,并完成數據的顯示和刷新。具體的關鍵代碼如下所示。

if (functionName === "refreshing") {
      fetchGetPoetry().then((ret) => {
        let formData = {
          poetry: {}
        }
        LogUtil.info(`widget refreshing: ${ret}`);
        const result = JSON.parse(ret as string);
        if (result.code === 200) {
          const poetry: PoetryDto = result['data'];
          formData.poetry = poetry;
        }
        let formBD = formBindingData.createFormBindingData(formData);
        formProvider.updateForm(formId, formBD);
      })
    }

4-4的卡片

完整代碼:

@Entry
@Component
struct HistoryCard {

  readonly PAGE_FULL: string = "100%";
  readonly PRE_96: string = "96%";
  readonly SIZE_81: number = 81;
  readonly SIZE_64: number = 64;
  readonly SIZE_48: number = 48;
  readonly SIZE_32: number = 32;
  readonly SIZE_24: number = 24;
  readonly SIZE_16: number = 16;
  readonly SIZE_8: number = 8;
  readonly SIZE_4: number = 4;
  readonly DEFAULT_AI_APP_LIST: Array<AiAppConfig> = [
    {
      appId: "6548c7fdeb28cf9c75531f66",
      chatId: "",
      name: "知識百科小助手",
      avatar: "/common/imgs/ic_wiki.svg",
      intro: "知識百科小助手。"
    },
    {
      appId: "65488134eb28cf9c75530e48",
      chatId: "",
      name: "節日小助手",
      avatar: "/common/imgs/ic_festival.svg",
      intro: "節日小助手。"
    },
    {
      appId: "65487d64eb28cf9c75530cd2",
      chatId: "",
      name: "文本翻譯助手",
      avatar: "/common/imgs/ic_document.svg",
      intro: "文本翻譯助手。"
    },
    {
      appId: "654ed429ab7249585cd2cab7",
      chatId: "",
      name: "產品名稱助手",
      avatar: "/common/imgs/ic_product.svg",
      intro: "產品名稱助手。"
    },
    {
      appId: "654ed4c3ab7249585cd2caf4",
      chatId: "",
      name: "道歉信助手",
      avatar: "/common/imgs/ic_sorry.svg",
      intro: "道歉信助手。"
    }
  ];

  build() {
    Column({ space: this.SIZE_8 }) {
      Row({ space: this.SIZE_4 }) {
        Image($r('app.media.ic_history'))
          .width(this.SIZE_24)
          .height(this.SIZE_24)
          .fillColor($r('app.color.main_color'))
        Text('查看歷史數據')
          .fontSize($r('app.float.font_size_16'))
          .fontColor($r('app.color.main_color'))
          .fontWeight(FontWeight.Bolder)
      }
      .width(this.PAGE_FULL)
      .height(this.SIZE_48)
      .padding({ left: this.SIZE_16 })

      Column() {
        GridRow({
          columns: 3,
          gutter: { x: this.SIZE_4, y: this.SIZE_4 }
        }) {
          ForEach(this.DEFAULT_AI_APP_LIST, (item: AiAppConfig) => {
            GridCol() {
              Column({ space: this.SIZE_8 }) {
                Image(item.avatar)
                  .width(this.SIZE_32)
                  .height(this.SIZE_32)
                  .fillColor($r('app.color.main_color'))
                Text(item.name)
                  .fontSize($r('app.float.font_size_12'))
                  .fontColor($r('app.color.auxiliary_color'))
                  .fontWeight(FontWeight.Bold)
              }
              .width(this.PAGE_FULL)
              .height(this.SIZE_81)
              .justifyContent(FlexAlign.Center)
              .onClick(() => {
                postCardAction(this, {
                  'action': 'router',
                  'abilityName': 'HistoryAbility',
                  'params': {
                    'targetPage': 'history',
                    'aiApp': item
                  }
                })
              })
            }
            .borderRadius(this.SIZE_8)
            .padding({
              left: this.SIZE_4,
              right: this.SIZE_4,
              top: this.SIZE_8,
              bottom: this.SIZE_4
            })
            .shadow({
              radius: this.SIZE_8,
              color: $r('app.color.tab_default_color')
            })
          })
        }
      }
      .width(this.PRE_96)
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Center)
      .flexShrink(1)
    }
    .width(this.PAGE_FULL)
    .height(this.PAGE_FULL)
  }
}

/**
 * AI應用配置
 */
interface AiAppConfig {
  appId: string;  // AI應用AppId
  chatId: string; // 會話窗口ID
  name: string; // AI應用名稱
  avatar: string; // AI應用LOGO
  intro?: string;  // AI應用介紹
}

interface ChatHistory {
  chat: AiAppConfig;
  total: number;
}
效果

原理

在卡片中使用postCardAction接口的router能力,能夠快速拉起卡片提供方應用的指定UIAbility,因此UIAbility較多的應用往往會通過卡片提供不同的跳轉按鈕,實現一鍵直達的效果。

通常使用按鈕控件來實現頁面拉起。

@Entry
@Component
struct WidgetCard {
  build() {
    Column() {
      Button('跳轉')
        .onClick(() => {
          console.info('Jump to EntryAbility funA');
          postCardAction(this, {
            action: 'router',
            abilityName: 'EntryAbility', // 只能跳轉到當前應用下的UIAbility
            params: {
              targetPage: 'funA' // 在EntryAbility中處理這個信息
            }
          });
        })
    }
    .width('100%')
    .height('100%').justifyContent(FlexAlign.SpaceAround)
  }
}
  • 在UIAbility中接收router事件并獲取參數,根據傳遞的params不同,選擇拉起不同的頁面。
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import Want from '@ohos.app.ability.Want';
import Base from '@ohos.base';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';

let selectPage: string = "";
let currentWindowStage: window.WindowStage | null = null;

export default class EntryAbility extends UIAbility {
  // 如果UIAbility第一次啟動,在收到Router事件后會觸發onCreate生命周期回調
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // 獲取router事件中傳遞的targetPage參數
    console.info("onCreate want:" + JSON.stringify(want));
    if (want.parameters?.params !== undefined) {
      let params: Record<string, string> = JSON.parse(want.parameters?.params.toString());
      console.info("onCreate router targetPage:" + params.targetPage);
      selectPage = params.targetPage;
    }
  }
  // 如果UIAbility已在后臺運行,在收到Router事件后會觸發onNewWant生命周期回調
  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    console.info("onNewWant want:" + JSON.stringify(want));
    if (want.parameters?.params !== undefined) {
      let params: Record<string, string> = JSON.parse(want.parameters?.params.toString());
      console.info("onNewWant router targetPage:" + params.targetPage);
      selectPage = params.targetPage;
    }
    if (currentWindowStage != null) {
      this.onWindowStageCreate(currentWindowStage);
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    let targetPage: string;
    // 根據傳遞的targetPage不同,選擇拉起不同的頁面
    switch (selectPage) {
      case 'funA':
        targetPage = 'pages/FunA';
        break;
      case 'funB':
        targetPage = 'pages/FunB';
        break;
      default:
        targetPage = 'pages/Index';
    }
    if (currentWindowStage === null) {
      currentWindowStage = windowStage;
    }
    windowStage.loadContent(targetPage, (err: Base.BusinessError) => {
      if (err && err.code) {
        console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
    });
  }
};

六、總結

通過蜜蜂AI助手元服務的開發,我們體驗到了端云一體化帶來的便捷,尤其注冊登陸這一塊,有了云端的接入,我們可以很快的加入。另外在項目里我們還用到了低碼能力,不用一行代碼,就完成了手機號登陸的功能。

本次鴻蒙和AI的結合,給了我新的體驗。大家也可以自行嘗試下HarmonyOS的開發,會給你帶來不一樣的體驗。

想了解更多關于開源的內容,請訪問:

51CTO 鴻蒙開發者社區

https://ost.51cto.com

責任編輯:jianghua 來源: 51CTO 鴻蒙開發者社區
相關推薦

2019-04-19 13:37:01

FacebookAI語音助手人工智能

2024-08-12 08:41:40

2024-09-14 12:42:32

2024-02-26 00:00:00

AI編程助手Copilot

2025-07-08 09:24:52

2025-11-05 07:47:49

2025-04-25 08:08:46

2018-09-27 14:14:36

Infor AI

2024-05-21 12:13:12

2023-08-07 12:53:05

開發服務

2023-08-11 14:00:42

鴻蒙元服務

2025-05-23 10:41:00

2023-11-09 11:31:43

GitHub開發

2024-07-11 15:26:23

2024-09-27 17:06:13

2023-05-16 09:53:18

ChatGPT人工智能

2024-12-12 09:00:33

2025-03-28 07:33:09

數據庫AI助手設計

2025-03-28 00:00:00

SOCAI安全

2025-07-02 00:00:00

點贊
收藏

51CTO技術棧公眾號

国产精品国产对白熟妇| 欧美做爰性生交视频| 亚洲视频在线不卡| 麻豆免费版在线观看| 久久久国产一区二区三区四区小说| 国产精品aaa| 三级影片在线看| 日韩动漫一区| 欧美精品一二三区| 久久精品视频16| 男人天堂久久久| 不卡欧美aaaaa| 国产有码一区二区| 成年人免费高清视频| 亚洲精彩视频| 亚洲天堂成人在线视频| 伊人久久久久久久久| 精品视频在线一区二区在线| 亚洲精品视频一区| 精品一区日韩成人| 国产精品毛片久久久久久久av| 国产精品一区毛片| 欧美多人爱爱视频网站| 正在播放国产对白害羞| 日韩极品少妇| 日韩欧美一区二区视频| 日本人视频jizz页码69| av在线中出| 亚洲日本在线天堂| 日韩精品极品视频在线观看免费| 亚洲第一页综合| 韩国v欧美v日本v亚洲v| 国产精品国产三级国产专播精品人 | 三区精品视频| 天天在线女人的天堂视频| 国产精品一区二区免费不卡| 国产精品久久久久久中文字| 欧美a∨亚洲欧美亚洲| 欧美大片一区| 久久影视免费观看| 青青青视频在线免费观看| 久操精品在线| 亚洲精品中文字| 久久人人爽人人人人片| 日本少妇精品亚洲第一区| 欧美日韩一区成人| 嫩草影院国产精品| 日本.亚洲电影| 欧美亚洲综合另类| 成年人在线观看视频免费| 妞干网免费在线视频| 亚洲国产wwwccc36天堂| 国产欧美精品aaaaaa片| 青青青草视频在线| 一区二区三区欧美| 国产一区二区三区乱码| 久久av色综合| 一区二区日韩电影| 国产精品久久久久久久乖乖| 蜜臀av国内免费精品久久久夜夜| 亚洲一区日韩精品中文字幕| 91黄色在线看| www.综合| 色欧美乱欧美15图片| 91香蕉视频污版| av亚洲一区| 正在播放亚洲一区| 国产九九九视频| 成人看片爽爽爽| 日韩av在线网页| 亚洲成人黄色av| 久久精品国产68国产精品亚洲| 亚洲视频国产视频| www中文在线| 91成人精品| 午夜精品免费视频| 蜜臀精品一区二区三区| 秋霞电影一区二区| 亚洲a∨日韩av高清在线观看| 99产精品成人啪免费网站| 国产91丝袜在线观看| 精品国产_亚洲人成在线| 欧美在线一卡| 国产精品国模大尺度视频| 在线观看17c| 依依综合在线| 91精品国产综合久久福利 | 蜜桃在线一区| 日韩大片免费观看视频播放| 国产激情av在线| 欧美日韩一区二区国产| 26uuu日韩精品一区二区| 中国一级片黄色一级片黄| 国内久久精品视频| 久久草视频在线看| 精品孕妇一区二区三区| 亚洲va国产天堂va久久en| 天天操天天爱天天爽| 欧美一区一区| 亚洲一区二区国产| 久久久久97国产| 美洲天堂一区二卡三卡四卡视频 | 欧美天堂社区| 久久九九亚洲综合| 日韩人妻精品中文字幕| 国产精品亚洲第一| 性欧美大战久久久久久久免费观看 | 伊人成色综合网| 成人噜噜噜噜| 亚洲色图av在线| 国产网站在线看| 理论片日本一区| 欧美18视频| 爱福利在线视频| 在线不卡免费欧美| 亚洲av成人无码久久精品| 亚洲无线视频| 亚洲一区制服诱惑| 91社区在线高清| 欧美日韩亚洲一区二区三区| 原创真实夫妻啪啪av| 欧美精选一区二区三区| 国模极品一区二区三区| 99久久亚洲精品日本无码| 国产欧美一区二区三区鸳鸯浴| 东北少妇不带套对白| 精品国产亚洲一区二区在线观看 | 毛片网站免费观看| 激情综合中文娱乐网| 亚洲在线第一页| 看黄网站在线观看| 欧美三级资源在线| 丰腴饱满的极品熟妇| 亚洲在线观看| 久久久久se| 在线天堂资源| 亚洲国产婷婷香蕉久久久久久| 久久免费看少妇高潮v片特黄| 免费高清视频精品| 亚洲国产一区二区三区在线| 激情开心成人网| 亚洲人在线观看| 中文字幕在线观看视频网站| eeuss鲁片一区二区三区在线观看| 四虎精品欧美一区二区免费| 精品国产麻豆| 欧美日韩国产成人在线观看| www.污视频| 亚洲精品乱码久久久久久日本蜜臀| 亚洲一级片av| 综合天天久久| 俄罗斯精品一区二区三区| 18videosex性欧美麻豆| 欧美一级一区二区| 妺妺窝人体色www婷婷| 欧美家庭影院| 国产日本精品| 久久久av水蜜桃| 日本午夜大片a在线观看| 亚洲精品久久久久久久久| 日韩精品――中文字幕| 久久夜色精品一区| 精品一卡二卡三卡| 精品av一区二区| 国产区精品视频| 色黄网站在线观看| 亚洲丁香婷深爱综合| 久久久久久久久久免费视频| 2020国产精品久久精品美国| 我看黄色一级片| 一区二区三区在线观看免费| 国产精品一区视频| 欧洲亚洲两性| 久久精品小视频| 六月婷婷综合网| 色综合色综合色综合 | 中文字幕成人在线观看| 第一区免费在线观看| 欧美激情aⅴ一区二区三区| 国产精品国产亚洲精品看不卡15| 日本免费不卡| 7777精品伊人久久久大香线蕉的| 精品一区二区三区四| 久久综合色8888| 亚洲免费999| 亚洲人妖在线| 亚洲自拍三区| 国产精品对白久久久久粗| 日本韩国欧美精品大片卡二| 日韩欧美小视频| 精品国产乱码久久久久久夜甘婷婷| 天堂а√在线中文在线新版| 中文字幕一区二区三区色视频| 黑森林av导航| 美国av一区二区| 日韩中文字幕在线免费| 欧洲grand老妇人| 国产精品久久久久久久天堂第1集| 性欧美gay| 久久久久久成人| 免费a级在线播放| 日韩av中文字幕在线| 国产一区二区小视频| 国产suv精品一区二区四区视频| 亚洲第一免费网站| 亚洲天堂手机在线| 黑人狂躁日本妞一区二区三区| 懂色av粉嫩av蜜臀av一区二区三区| 福利91精品一区二区三区| 最新天堂中文在线| 国产精品普通话对白| 狠狠噜天天噜日日噜| 日韩欧美精品一区| 欧美精品亚洲| 国产精品极品在线观看| 亚洲精品免费网站| 素人一区二区三区| 97成人精品区在线播放| 亚洲图区一区| xvideos亚洲| 高清美女视频一区| 日韩成人在线视频观看| 亚洲精品一区二区口爆| 91精品国模一区二区三区| 波多野结衣日韩| 欧美视频13p| 亚洲另类欧美日韩| 午夜久久久久久久久久一区二区| 久久中文免费视频| 成人欧美一区二区三区| 91禁男男在线观看| 国产精品视频看| 精品人妻中文无码av在线| 久久亚区不卡日本| av直播在线观看| 91原创在线视频| 日本japanese极品少妇| 99久久伊人网影院| yy6080午夜| av电影在线观看完整版一区二区| av天堂一区二区| 成人久久18免费网站麻豆 | 免费看毛片网站| 精品动漫一区二区三区| www.国产成人| 大桥未久av一区二区三区| 国产又爽又黄的视频| 精品久久久久久久久久久久| 天天插天天操天天干| 精品久久久久久中文字幕大豆网 | 国产精品人人妻人人爽| 精品国产一区二区三区久久久樱花| 国产成人精品久久二区二区| 亚洲欧洲日本韩国| 欧美综合在线第二页| 成人免费福利| 国产欧美精品xxxx另类| 国产精品高潮久久| 国产日韩在线观看av| 成人97精品毛片免费看| av一区二区三区免费| 在线综合色站| 欧美不卡在线一区二区三区| 成人短片线上看| 日韩第一页在线观看| 欧美婷婷在线| 天堂…中文在线最新版在线| 午夜影院日韩| 日韩中文字幕a| 国产成人在线视频网站| 香蕉视频污视频| 国产日产欧美精品一区二区三区| 欧美成人短视频| 亚洲精品福利视频网站| 国产成人亚洲精品自产在线| 欧美性猛交xxxx富婆| 中文字幕乱码视频| 欧美一区二区播放| 无套内谢的新婚少妇国语播放| 亚洲人成人99网站| 哥也色在线视频| 8050国产精品久久久久久| 久久天堂av| 成人免费看片网址| 国产欧美久久一区二区三区| 正在播放国产精品| 亚洲精品日本| 91pony九色| 91丨porny丨户外露出| 欧美日韩生活片| 亚洲激情第一区| 亚洲 欧美 中文字幕| 日韩一区二区三区电影在线观看| 亚洲精品97久久中文字幕| 亚洲欧美日韩中文在线| 国产成人午夜| 国产精品电影观看| 狠狠一区二区三区| 中文字幕不卡每日更新1区2区| 极品裸体白嫩激情啪啪国产精品| 啊啊啊国产视频| 99久久久久免费精品国产| 麻豆天美蜜桃91| 色94色欧美sute亚洲线路一久 | 精品欧美乱码久久久久久1区2区| 全色精品综合影院| 欧美老女人性生活| avav成人| 欧美精品123| 亚洲大胆在线| 日本一本在线视频| 欧美国产视频在线| 九一国产在线观看| 精品国产乱码久久久久久久 | 国内精品久久久久影院优| jvid一区二区三区| 欧美日本韩国一区二区三区| 狠狠色狠狠色综合日日tαg| 午夜免费福利视频在线观看| 久久久高清一区二区三区| 国产在线欧美在线| 91精品国产91久久久久久一区二区 | 亚洲人成电影网站色…| 999av小视频在线| **亚洲第一综合导航网站| 日韩一区亚洲二区| 国产精品亚洲二区在线观看| 97久久精品人人做人人爽| 久草视频在线资源| 欧美一区二区播放| 91香蕉在线观看| 成人黄色免费网站在线观看| 欧美日韩中文一区二区| 粉嫩虎白女毛片人体| 久久精品一区八戒影视| 日本午夜视频在线观看| 亚洲国产精品久久91精品| 欧美78videosex性欧美| 51国偷自产一区二区三区的来源 | 亚洲精品观看| 特色特色大片在线| 国产综合久久久久久久久久久久| 国产极品视频在线观看| 欧美在线一二三| 91精品专区| 国产欧美一区二区三区视频| 色777狠狠狠综合伊人| 最新天堂在线视频| 最新高清无码专区| 国产三级精品在线观看| 久久久精品在线观看| 日韩免费高清视频网站| 香蕉视频免费版| 成人性生交大片免费看中文网站| 久久久久久久九九九九| 亚洲国产精品99久久| 色资源二区在线视频| 裸体丰满少妇做受久久99精品| 亚洲女优在线| 在线看片中文字幕| 欧美男女性生活在线直播观看| 国产乱色在线观看| eeuss一区二区三区| 亚洲激情av| 亚洲理论片在线观看| 欧美日韩在线免费视频| 中国av在线播放| 国产一区高清视频| 久久综合影视| 国产67194| 亚洲韩国青草视频| 欧美日韩精品免费观看视完整| 亚洲乱码一区二区三区三上悠亚| 韩国成人福利片在线播放| 天天操天天射天天爽| 亚洲欧美国产另类| 国产精品天堂蜜av在线播放| 免费成人进口网站| 91偷拍与自偷拍精品| 中文字幕久久久久| 欧美激情在线观看| 色综合综合网| 69久久精品无码一区二区| 欧美日韩免费区域视频在线观看| 国产三区四区在线观看| 亚洲xxxxx性| 日一区二区三区| 精品99在线观看| 亚洲最大在线视频| 少妇精品在线| 日本激情视频在线| 亚洲成人av一区二区三区| 成人午夜电影在线观看| 福利视频久久| 日本亚洲欧美天堂免费| 久久精品免费在线| 日韩小视频在线观看| 亚州综合一区| 师生出轨h灌满了1v1| 欧美性xxxxxx少妇|