Browse Source

添加了部分组件与某些优化改动

wangcy 5 months ago
parent
commit
ad7b9577ee
35 changed files with 722 additions and 183 deletions
  1. 1 1
      commons/basic/Index.ets
  2. 2 2
      commons/basic/src/main/ets/ads/RewardAd.ets
  3. 63 0
      commons/basic/src/main/ets/components/AgreePrivacy.ets
  4. 141 0
      commons/basic/src/main/ets/components/ChatList.ets
  5. 3 3
      commons/basic/src/main/ets/components/ReviseImgHeaderBuilder.ets
  6. 7 2
      commons/basic/src/main/ets/components/YTHeader.ets
  7. 65 0
      commons/basic/src/main/ets/components/YTSearch.ets
  8. 3 1
      commons/basic/src/main/ets/components/YtButton.ets
  9. 49 0
      commons/basic/src/main/ets/components/YtDoubleConfirm.ets
  10. 47 0
      commons/basic/src/main/ets/components/YtTiltleBar.ets
  11. 16 2
      commons/basic/src/main/ets/models/index.ets
  12. 8 17
      commons/basic/src/main/ets/utils/CopyText.ets
  13. 12 10
      commons/basic/src/main/ets/utils/YTRequest.ets
  14. 18 2
      commons/basic/src/main/ets/utils/YTRouter.ets
  15. 16 20
      commons/basic/src/main/ets/utils/YTToast.ets
  16. BIN
      commons/basic/src/main/resources/base/media/search.png
  17. 1 0
      features/sketch/src/main/module.json5
  18. 5 0
      features/sketch/src/main/resources/base/profile/router_map.json
  19. 3 3
      features/user/src/main/ets/components/LoginInput.ets
  20. 4 4
      features/user/src/main/ets/components/OtherLoginMethods.ets
  21. 2 2
      features/user/src/main/ets/components/Terms.ets
  22. 91 0
      features/user/src/main/ets/pages/AboutUS.ets
  23. 0 41
      features/user/src/main/ets/pages/AgreementPage.ets
  24. 5 4
      features/user/src/main/ets/pages/LoginPage.ets
  25. 33 0
      features/user/src/main/ets/pages/Privacy.ets
  26. 9 10
      features/user/src/main/ets/pages/SettingPage.ets
  27. 1 2
      features/user/src/main/ets/pages/SuggestionPage.ets
  28. 29 0
      features/user/src/main/ets/pages/UserAgreement.ets
  29. 30 50
      features/user/src/main/ets/views/Mine.ets
  30. 1 0
      features/user/src/main/module.json5
  31. BIN
      features/user/src/main/resources/base/media/MRight.png
  32. 38 0
      features/user/src/main/resources/base/profile/router_map.json
  33. 9 3
      products/entry/src/main/ets/entryability/EntryAbility.ets
  34. 4 4
      products/entry/src/main/ets/pages/Index.ets
  35. 6 0
      products/entry/src/main/module.json5

+ 1 - 1
commons/basic/Index.ets

@@ -44,7 +44,7 @@ export { yTRouter } from "./src/main/ets/utils/YTRouter"
 
 export * from "./src/main/ets/utils/YTBreakPoint"
 
-export { BarType } from './src/main/ets/models/index'
+export { BasicType } from './src/main/ets/models/index'
 
 export * from './src/main/ets/models'
 

+ 2 - 2
commons/basic/src/main/ets/ads/RewardAd.ets

@@ -51,7 +51,7 @@ export class RewardAd {
         setTimeout(() => {
           IBestToast.hide()
           setTimeout(() => {
-            IBestToast.show({ message: '加载失败', type: 'fail' })
+            IBestToast.show({ message: '加载失败' })
           }, 100)
         }, 1000)
       },
@@ -142,7 +142,7 @@ export class RewardAd {
 
             default:
               IBestToast.hide()
-              IBestToast.show({ message: '加载失败', type: 'fail' })
+              IBestToast.show({ message: '加载失败' })
               break;
           }
         }

+ 63 - 0
commons/basic/src/main/ets/components/AgreePrivacy.ets

@@ -0,0 +1,63 @@
+import { IBestToast } from '../../../../Index';
+import { BasicType, reqString } from '../models';
+import { yTRequest } from '../utils/YTRequest';
+import { yTToast } from '../utils/YTToast';
+import { YtButton } from './YtButton';
+
+@Builder
+export function AgreePrivacy(item: BasicType<undefined>) {
+  Column() {
+    Text('温馨提示')
+      .fontSize(14)
+      .fontColor('#FF000000')
+      .margin({ top: 14, bottom: 8 })
+    Text('已阅读并同意《用户协议》和《隐私政策》')
+      .fontSize(13)
+      .fontColor($r('sys.color.mask_secondary'))
+      .margin({ bottom: 19 })
+
+
+    Column() {
+      YtButton({
+        btContent: '同意并登入',
+        btHeight: 37,
+        click: () => {
+          yTToast.hide()
+          const loginData: reqString = AppStorage.get<reqString>('captchaLogin')!
+
+          if (item.text == '验证码登录') {
+            if (!loginData.phonenumber || !loginData.captcha) {
+              IBestToast.show({
+                message: "请输入手机号或验证码"
+              })
+              return
+            }
+
+            yTRequest.phonenumberLogin(AppStorage.get<reqString>('captchaLogin')!)
+          }
+          if (item.text == '华为登录') {
+            yTRequest.huaweiLogin()
+          }
+
+
+        }
+      })
+      YtButton({
+        btContent: '不同意',
+        btHeight: 37,
+        bgc: Color.White,
+        btFontColor: '#80000000',
+        click: () => {
+          yTToast.hide()
+        }
+      })
+    }
+    .padding({ left: 16, right: 16 })
+
+  }
+  .width(280)
+  .height(160)
+  .backgroundColor(Color.White)
+  .borderRadius(8)
+  .margin({ bottom: 28 })
+}

+ 141 - 0
commons/basic/src/main/ets/components/ChatList.ets

@@ -0,0 +1,141 @@
+import { BasicType, MessageType, Role } from '../models'
+import { BreakPointType, yTBreakPoint } from '../utils/YTBreakPoint'
+
+@Component
+export struct ChatList {
+  @Prop chatList: MessageType[] = [
+    {
+      header: $r('app.media.ibest_icon_star'),
+      text: '喜欢不是一件浪漫的事',
+      role: Role.USER,
+    },
+    {
+      header: $r('app.media.ibest_icon_star_o'),
+      text: '那是什么?',
+      role: Role.OTHER,
+    },
+    {
+      header: $r('app.media.ibest_icon_star'),
+      text: '喜欢你才是',
+      role: Role.USER,
+    },
+
+  ]
+  @Prop haveMoreHistory: boolean = true
+  @Prop promptArr: BasicType<undefined>[] = [
+    {
+      text: '每日读书打卡'
+    },
+    {
+      text: '励志正能量'
+    },
+    {
+      text: '公司团建'
+    }
+  ]
+  @StorageProp(yTBreakPoint.KEY) bp: string = 'sm'
+  @State isLoading: boolean = false
+
+  build() {
+    List({ space: 24 }) {
+      ForEach(this.chatList, (item: MessageType, index) => {
+        ListItem() {
+          if (item.role == Role.USER) {
+            Row({ space: 16 }) {
+              Text(item.text)
+                .padding(12)
+                .backgroundColor('#FFF0FBFF')
+                .constraintSize({ maxWidth: '70%' })
+                .borderRadius(8)
+              Image(item.header)
+                .width(40)
+                .aspectRatio(1)
+            }
+            .width('100%')
+            .margin({ top: index == 0 ? 20 : 0, bottom: index == this.chatList.length - 1 ? 20 : 0 })
+            .alignItems(VerticalAlign.Bottom)
+            .justifyContent(FlexAlign.End)
+
+          } else if (item.role == Role.OTHER) {
+            Row({ space: 16 }) {
+              Image(item.header)
+                .width(40)
+                .aspectRatio(1)
+
+              Text(item.text)
+                .padding(12)
+                .constraintSize({ maxWidth: '70%' })
+                .borderRadius(8)
+                .backgroundColor('#FFF0FBFF')
+            }
+            .width('100%')
+            .margin({ top: index == 0 ? 20 : 0, bottom: index == this.chatList.length - 1 ? 20 : 0 })
+            .alignItems(VerticalAlign.Bottom)
+            .justifyContent(FlexAlign.Start)
+          } else {
+
+          }
+
+        }
+      })
+    }
+    .width('100%')
+    .height('100%')
+    .scrollBar(BarState.Off)
+
+  }
+
+  @Builder
+  card() {
+    ListItem() {
+      Column() {
+        if (this.haveMoreHistory) {
+          Row() {
+            Text('下拉查看历史消息')
+              .fontWeight(500)
+              .fontSize(10)
+              .fontColor('#80595959')
+          }
+          .height(38)
+          .width('100%')
+          .justifyContent(FlexAlign.Center)
+        }
+
+        Column() {
+          Text('输入你想生成的文案主题,一键生成所需文案,轻松高效不费脑,例如:')
+            .fontColor('#E6595959')
+            .fontWeight(500)
+            .fontSize(14)
+            .margin({ bottom: 10 })
+          ForEach(this.promptArr, (item: BasicType<undefined>, index) => {
+            Text(item.text)
+              .fontColor('#E6595959')
+              .fontSize(10)
+              .fontWeight(600)
+              .backgroundColor('#FFF7F9FA')
+              .height(30)
+              .borderRadius(4)
+              .width('100%')
+              .padding({ left: 8 })
+              .textAlign(new BreakPointType<TextAlign>({
+                sm: TextAlign.Start,
+                md: TextAlign.Center,
+
+              }
+              ).getValue(this.bp))
+              .margin({ bottom: this.promptArr.length - 1 == index ? 0 : 8 })
+              .onClick(() => {
+
+              })
+          })
+        }
+        .width('100%')
+        .backgroundColor(Color.White)
+        .borderRadius(8)
+        .padding(12)
+        .alignItems(HorizontalAlign.Start)
+        .margin({ bottom: 10 })
+      }
+    }
+  }
+}

+ 3 - 3
commons/basic/src/main/ets/components/ReviseImgHeaderBuilder.ets

@@ -1,9 +1,9 @@
-import { BarType } from '../models'
+import { BasicType } from '../models'
 
 @Builder
-export function reviseImgHeaderBuilder(items: BarType<undefined>[]) {
+export function reviseImgHeaderBuilder(items: BasicType<undefined>[]) {
   Column() {
-    ForEach(items, (item: BarType<undefined>, index) => {
+    ForEach(items, (item: BasicType<undefined>, index) => {
       Text(item.text)
         .width('100%')
         .height(43)

+ 7 - 2
commons/basic/src/main/ets/components/YTHeader.ets

@@ -1,3 +1,4 @@
+import { YtAvoid } from '../../../../Index'
 import { yTRouter } from '../utils/YTRouter'
 
 @Component
@@ -5,11 +6,13 @@ export struct YTHeader {
   @BuilderParam leftComp?: () => void
   @BuilderParam RightComp?: () => void
   @BuilderParam CenterComp?: () => void
+  @StorageProp(YtAvoid.safeTopKey) safeTop: number = 0
   backArrow: boolean = true
   title: string = ''
   click = () => {
     yTRouter.routerBack()
   }
+  bgc: ResourceColor = Color.White
 
   build() {
     Stack() {
@@ -40,13 +43,15 @@ export struct YTHeader {
           Text(this.title)
             .fontSize(18)
             .fontWeight(700)
+            .fontColor(Color.Black)
         }
       }
       .width('100%')
       .justifyContent(FlexAlign.Center)
       .hitTestBehavior(HitTestMode.None)
     }
-    .height(44)
-    .backgroundColor(Color.White)
+    .height(44 + this.safeTop)
+    .padding({ top: this.safeTop })
+    .backgroundColor(this.bgc)
   }
 }

+ 65 - 0
commons/basic/src/main/ets/components/YTSearch.ets

@@ -0,0 +1,65 @@
+@Component
+export struct YTSearch {
+  //----搜索栏属性
+  @State searchValue:string = ''
+  //是否可以输入内容
+  isEnable:boolean = true
+  searchHeight:number = 36
+  searchPadding:Padding|Length = {left:14,right:14}
+  placeHolder:string = '请输入搜索内容'
+  placeholderColor:ResourceColor = '#CC333333'
+  placeholderFont:Font = {size:12}
+  needLeftBuilder:boolean = true
+  //----默认搜索图片属性 在传入leftBuilder或needLeftBuilder=false后无效
+  src:ResourceStr = $r('app.media.search')
+  imgWidth:Length = 24
+  //----提交函数,回调以回传输入内容
+  submit=(_:string)=>{
+
+  }
+  //----整体点击的处理函数
+  click=()=>{
+
+  }
+  @BuilderParam leftBuilder:()=>void = this.defaultLeftBuilder
+  @BuilderParam rightBuilder?:()=>void
+  build() {
+    Row(){
+      if (this.needLeftBuilder){
+        this.leftBuilder()
+      }
+
+      TextInput({text:$$this.searchValue,placeholder:this.placeHolder})
+        .backgroundColor(Color.Transparent)
+        .height('100%')
+        .layoutWeight(1)
+        .fontSize(12)
+        .borderRadius(0)
+        .placeholderFont(this.placeholderFont)
+        .placeholderColor(this.placeholderColor)
+        .padding(0)
+        .caretColor('app.color.main_ac_color_dark')
+        .enabled(this.isEnable)
+        .onSubmit(()=>{
+          this.submit(this.searchValue)
+        })
+      if (this.rightBuilder){
+        this.rightBuilder()
+      }
+    }
+    .padding(this.searchPadding)
+    .borderRadius(this.searchHeight/2)
+    .height(this.searchHeight)
+    .width('100%')
+    .border({width:1,color:$r('app.color.main_ac_color_dark')})
+    .onClick(this.click)
+  }
+
+  @Builder
+  defaultLeftBuilder(){
+    Image(this.src)
+      .width(this.imgWidth)
+      .aspectRatio(1)
+      .margin({right:4})
+  }
+}

+ 3 - 1
commons/basic/src/main/ets/components/YtButton.ets

@@ -1,6 +1,6 @@
 @Component
 export struct YtButton {
-  btHeight: Length = 37
+  btHeight: number = 37
   btWidth: Length = '100%'
   btContent: string = ''
   click = () => {
@@ -12,6 +12,7 @@ export struct YtButton {
   btFontColor: ResourceColor = Color.White
   btBorder?: BorderOptions
   btState: boolean = true
+  btBorderRadius: Length | BorderRadiuses | LocalizedBorderRadiuses = this.btHeight / 2
 
   build() {
     Button(this.btContent)
@@ -25,5 +26,6 @@ export struct YtButton {
       .padding(this.btPadding ?? {})
       .border(this.btBorder)
       .enabled(this.btState)
+      .borderRadius(this.btBorderRadius)
   }
 }

+ 49 - 0
commons/basic/src/main/ets/components/YtDoubleConfirm.ets

@@ -0,0 +1,49 @@
+import { YtButton, yTToast } from '../../../../Index';
+import { BasicType } from '../models';
+
+@Builder
+export function yTDoubleConfirm(item: BasicType<undefined>) {
+  Column() {
+    Text(item.text)
+      .fontSize(14)
+      .fontColor('#FF000000')
+      .margin({ bottom: 18 })
+    Text(item.message)
+      .fontColor($r('sys.color.mask_secondary'))
+      .lineHeight(18)
+      .fontSize(13)
+      .margin({ bottom: 18 })
+    Row() {
+      YtButton(
+        {
+          btContent: '确定',
+          btWidth: 108,
+          btHeight: 37,
+          btFontSize: 12,
+          click: item.click
+        }
+      )
+      YtButton(
+        {
+          btContent: '取消',
+          btWidth: 108,
+          btHeight: 37,
+          btFontColor: '#80000000',
+          btBorder: { width: 1, color: '#33000000' },
+          btFontSize: 12,
+          bgc: Color.White,
+          click: () => {
+            yTToast.hide()
+          }
+        }
+      )
+    }
+    .justifyContent(FlexAlign.SpaceBetween)
+    .width('100%')
+  }
+  .height(160)
+  .width(280)
+  .padding({ top: 14, left: 24, right: 24 })
+  .backgroundColor(Color.White)
+  .borderRadius(8)
+}

+ 47 - 0
commons/basic/src/main/ets/components/YtTiltleBar.ets

@@ -0,0 +1,47 @@
+@Component
+export struct YtTitleBar {
+  @Prop headerTitleList: string[]
+  listScroller: ListScroller = new ListScroller()
+  @Prop @Watch('currentIndexChange') currentIndex: number
+  onCurrentIndexChange = (_: number) => {
+
+  }
+
+  currentIndexChange() {
+    this.listScroller.scrollToIndex(this.currentIndex, true, ScrollAlign.CENTER)
+  }
+
+  build() {
+    List({ space: 18, scroller: this.listScroller }) {
+      ForEach(this.headerTitleList, (item: string, index) => {
+        ListItem() {
+          Column() {
+            Text(item)
+              .height(24)
+              .fontColor(this.currentIndex == index ? '#FF171717' : '#B3333333')
+              .fontSize(18)
+              .fontWeight(this.currentIndex == index ? 700 : 500)
+              .onClick(() => {
+                this.currentIndex = index
+                this.onCurrentIndexChange(this.currentIndex)
+
+              })
+            if (this.currentIndex == index) {
+              Text()
+                .width(14)
+                .height(4)
+                .borderRadius(12)
+                .backgroundColor($r('app.color.main_ac_color_dark'))
+            }
+
+          }
+          .justifyContent(FlexAlign.SpaceBetween)
+        }
+      })
+    }
+    .height(30)
+    .width('100%')
+    .listDirection(Axis.Horizontal)
+    .scrollBar(BarState.Off)
+  }
+}

+ 16 - 2
commons/basic/src/main/ets/models/index.ets

@@ -1,11 +1,12 @@
-export interface BarType<T> {
+export interface BasicType<T> {
   src?: ResourceStr,
   acSrc?: ResourceStr,
   text?: string,
   message?: string,
+  color?: ResourceColor,
   click?: () => void,
   number?: number,
-  state?: T,
+  generics?: T,
   date?: string,
   index?: number,
   finally?: () => void
@@ -16,4 +17,17 @@ export type reqString = Record<string, string>
 export interface avoidType {
   top?: number,
   bottom?: number
+}
+
+export interface MessageType {
+  text: string,
+  header: ResourceStr,
+  role: Role,
+  id?: number
+}
+
+export enum Role {
+  USER,
+  OTHER,
+  CARD
 }

+ 8 - 17
commons/basic/src/main/ets/utils/CopyText.ets

@@ -1,24 +1,15 @@
-import { IBestToast } from '@ibestservices/ibest-ui';
 import { pasteboard } from '@kit.BasicServicesKit';
 
 
-export function copyText(text: string, showToast: boolean = true) {
+export function copyText(text: string, copyResult?: (data?: pasteboard.PasteData, err?: Error) => void) {
   const pasteboardData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text);
   const systemPasteboard = pasteboard.getSystemPasteboard();
   systemPasteboard.setData(pasteboardData); // 将数据放入剪切板
-  systemPasteboard.getData().then((data) => {
-    if (data) {
-      if (showToast) {
-        IBestToast.show({
-          type: "success",
-          message: "复制成功"
-        })
-      }
-    } else {
-      IBestToast.show({
-        type: "fail",
-        message: "复制失败"
-      })
-    }
-  })
+  systemPasteboard.getData()
+    .then((data) => {
+      copyResult?.(data)
+    })
+    .catch((e: Error) => {
+      copyResult?.(undefined, e)
+    })
 }

+ 12 - 10
commons/basic/src/main/ets/utils/YTRequest.ets

@@ -54,7 +54,7 @@ instance.interceptors.response.use((response: AxiosResponse) => {
   setTimeout(() => {
     IBestToast.hide()
     setTimeout(() => {
-      IBestToast.show({ message: '请求超时,请检查网络', type: 'fail' })
+      IBestToast.show({ message: '请求超时,请检查网络' })
     }, 100)
   }, 1000)
   return Promise.reject(error);
@@ -62,6 +62,8 @@ instance.interceptors.response.use((response: AxiosResponse) => {
 
 
 class YtRequest {
+  private productName: string = 'chat'
+
   get<T>(url: string, params?: Record<string, string | number | boolean>,
     headers?: Record<string, string>): Promise<T> {
     return instance.get<null, T, null>(url, { params, headers })
@@ -77,7 +79,7 @@ class YtRequest {
 
   //获取验证码
   getCaptcha(phonenumber: string, success: (res: string) => void, fail: (err: Error) => void) {
-    yTRequest.post<reqString, reqString>('/api/aiAccount/member/sendSmsCode',
+    yTRequest.post<reqString, reqString>(`/api/${this.productName}/member/sendSmsCode`,
       { 'phonenumber': phonenumber })
       .then(res => {
         success(res['uuid'])
@@ -91,7 +93,7 @@ class YtRequest {
   phonenumberLogin(param: reqString) {
     const uuid = AppStorage.get<string>('uuid')
     if (uuid !== undefined) {
-      yTRequest.post<reqString, reqString>('/api/aiAccount/member/phoneLogin', {
+      yTRequest.post<reqString, reqString>(`/api/${this.productName}/member/phoneLogin`, {
         'phonenumber': param['phonenumber'],
         'smsCode': param['captcha'],
         'uuid': uuid
@@ -99,7 +101,7 @@ class YtRequest {
         .then(res => {
           userInfo.setToken(res['token'])
           yTRequest.refreshUserInfo(() => {
-            IBestToast.show({ message: '登录成功', type: 'success' })
+            IBestToast.show({ message: '登录成功' })
             yTRouter.routerBack()
           })
         })
@@ -112,7 +114,7 @@ class YtRequest {
       IBestToast.showLoading()
       huaweiAuthPlugin.requestAuth()
         .then(res => {
-          yTRequest.post<reqString, reqString>('/api/aiAccount/member/hmLogin',
+          yTRequest.post<reqString, reqString>(`/api/${this.productName}/member/hmLogin`,
             { 'code': res } as reqString)
             .then(data => {
               const token = data['token']
@@ -121,7 +123,7 @@ class YtRequest {
                 // YTLog.info(userInfo)
                 IBestToast.hide()
                 setTimeout(() => {
-                  IBestToast.show({ message: '登录成功', type: 'success' })
+                  IBestToast.show({ message: '登录成功' })
                 }, 100)
 
                 yTRouter.routerBack()
@@ -148,7 +150,7 @@ class YtRequest {
 
   //刷新用户信息
   refreshUserInfo(success?: (res: UserInfo) => void) {
-    yTRequest.post<UserInfo, null>('/api/aiAccount/member/info')
+    yTRequest.post<UserInfo, null>(`/api/${this.productName}/member/info`)
       .then(res => {
         userInfo.setUserInfoAndLogin(res)
         YTLog.info(userInfo)
@@ -189,7 +191,7 @@ class YtRequest {
     })
       .then(res => {
         const url = res['url']
-        yTRequest.post<null, reqString>('/api/aiAccount/member/modifyMemberIcon', { 'memberIcon': url })
+        yTRequest.post<null, reqString>(`/api/${this.productName}/member/modifyMemberIcon`, { 'memberIcon': url })
           .then(() => {
             success()
           })
@@ -205,7 +207,7 @@ class YtRequest {
 
   // 修改用户昵称
   changeNickname(name: string, success: () => void) {
-    yTRequest.post<null, reqString>('/api/aiAccount/member/modifyMemberName', { 'memberName': name })
+    yTRequest.post<null, reqString>(`/api/${this.productName}/member/modifyMemberName`, { 'memberName': name })
       .then(() => {
         yTRequest.refreshUserInfo(() => {
           success()
@@ -216,7 +218,7 @@ class YtRequest {
   //问题反馈
   questionBack(des: string, createBy: string) {
 
-    yTRequest.post<null, reqString>('/api/aiAccount/question/saveQuestion', {
+    yTRequest.post<null, reqString>(`/api/${this.productName}/question/saveQuestion`, {
       'backQuestion': des,
       'createBy': createBy,
       'createTime': formatDate(new Date()),

+ 18 - 2
commons/basic/src/main/ets/utils/YTRouter.ets

@@ -1,6 +1,4 @@
 class YTRouter extends NavPathStack {
-
-
   router2SettingPage() {
     yTRouter.pushPathByName('SettingPage', '')
   }
@@ -13,6 +11,24 @@ class YTRouter extends NavPathStack {
     yTRouter.pushPathByName('AgreementPage', param)
   }
 
+  /**
+   * 跳转至用户协议
+   */
+  router2userAgreement() {
+    yTRouter.pushPathByName('UserAgreementPage', '')
+  }
+
+  /**
+   * 跳转至隐私政策
+   */
+  router2Privacy() {
+    yTRouter.pushPathByName('Privacy', '')
+  }
+
+  router2AboutUS() {
+    yTRouter.pushPathByName('AboutUS', '')
+  }
+
   getAgreementPageParam() {
     return yTRouter.getParamByName('AgreementPage').pop() as '关于我们' | '隐私政策' | '用户协议'
   }

+ 16 - 20
commons/basic/src/main/ets/utils/YTToast.ets

@@ -1,7 +1,9 @@
 import { BusinessError } from '@kit.BasicServicesKit';
 import { ComponentContent, promptAction } from '@kit.ArkUI';
 import { UIContext } from '@ohos.arkui.UIContext';
-import { BarType,  YTLog } from '../../../../Index';
+import { BasicType, YTLog } from '../../../../Index';
+import { yTDoubleConfirm } from '../components/YtDoubleConfirm';
+import { AgreePrivacy } from '../components/AgreePrivacy';
 
 
 interface InitOption {
@@ -9,7 +11,7 @@ interface InitOption {
   options: promptAction.BaseDialogOptions;
 }
 
-class Params<T> implements BarType<T> {
+class Params<T> implements BasicType<T> {
   src?: ResourceStr
   acSrc?: ResourceStr
   text?: string
@@ -18,7 +20,7 @@ class Params<T> implements BarType<T> {
   message?: string
   click?: () => void
 
-  constructor(item: BarType<undefined>) {
+  constructor(item: BasicType<undefined>) {
     this.src = item.src;
     this.text = item.text;
     this.index = item.index;
@@ -33,9 +35,11 @@ export class YTToast {
   private declare ctx: UIContext;
   private declare contentNode: ComponentContent<Object> | null;
   private declare options: promptAction.BaseDialogOptions;
+  private maskColor: string = '#80000000'
 
   init(initOption: InitOption) {
     this.ctx = initOption.context;
+    initOption.options.maskColor = this.maskColor
     this.options = initOption.options;
   }
 
@@ -52,8 +56,8 @@ export class YTToast {
   }
 
   openToast(builder: WrappedBuilder<[
-    BarType<undefined>
-  ]>, item: BarType<undefined>) {
+    BasicType<undefined>
+  ]>, item: BasicType<undefined>) {
     this.contentNode =
       new ComponentContent(this.ctx, builder, new Params<undefined>(item));
     this.ctx.getPromptAction()
@@ -68,21 +72,13 @@ export class YTToast {
       })
   }
 
-  // doubleConfirm(item: BarType<undefined>) {
-  //   this.openToast(wrapBuilder(yTDoubleConfirm), item)
-  // }
-  //
-  // getReward(item: BarType<undefined>) {
-  //   this.openToast(wrapBuilder(rewardTreasure), item)
-  // }
-  //
-  // agreePrivacy(item: BarType<undefined>) {
-  //   this.openToast(wrapBuilder(AgreePrivacy), item)
-  // }
-  //
-  // showBillImage(item: BarType<undefined>) {
-  //   this.openToast(wrapBuilder(showBillImage), item)
-  // }
+  doubleConfirm(item: BasicType<undefined>) {
+    this.openToast(wrapBuilder(yTDoubleConfirm), item)
+  }
+
+  agreePrivacy(item: BasicType<undefined>) {
+    this.openToast(wrapBuilder(AgreePrivacy), item)
+  }
 
 
   hide() {

BIN
commons/basic/src/main/resources/base/media/search.png


+ 1 - 0
features/sketch/src/main/module.json5

@@ -3,6 +3,7 @@
     "name": "sketch",
     "type": "shared",
     "description": "$string:shared_desc",
+    "routerMap": "$profile:router_map",
     "deviceTypes": [
       "phone",
       "tablet",

+ 5 - 0
features/sketch/src/main/resources/base/profile/router_map.json

@@ -0,0 +1,5 @@
+{
+  "routerMap": [
+
+  ]
+}

+ 3 - 3
features/user/src/main/ets/components/LoginInput.ets

@@ -56,13 +56,13 @@ export struct LoginInput {
               // TODO  此处放置验证码获取逻辑
               yTRequest.getCaptcha(this.phoneNumber, (res) => {
                 if (!res) {
-                  IBestToast.show({ type: 'warning', message: '请勿重复发送验证码' })
+                  IBestToast.show({ message: '请勿重复发送验证码' })
                   return
                 }
 
                 AppStorage.setOrCreate<string>('uuid', res)
               }, (err) => {
-                IBestToast.show({ type: 'fail', message: '验证码发送失败' })
+                IBestToast.show({ message: '验证码发送失败' })
                 this.time = 61
               })
               // yTRequest.post<CodeType, AskCodeNum>('/api/friendcase/member/sendSmsCode',
@@ -81,7 +81,7 @@ export struct LoginInput {
               //   })
             } else {
               if (!this.phoneNumber.match(rep)) {
-                IBestToast.show({ message: '请输入正确的手机号', type: 'warning' })
+                IBestToast.show({ message: '请输入正确的手机号' })
               }
             }
           })

+ 4 - 4
features/user/src/main/ets/components/OtherLoginMethods.ets

@@ -1,9 +1,9 @@
-import { BarType } from 'basic'
+import { BasicType } from 'basic'
 
 
 @Component
 export struct OtherLoginMethods {
-  loginMethodArr: BarType<undefined>[] = []
+  loginMethodArr: BasicType<undefined>[] = []
 
   build() {
     Column() {
@@ -11,7 +11,7 @@ export struct OtherLoginMethods {
         .fontSize($r('[basic].float.page_text_font_size_10'))
         .margin({ bottom: 24 })
       Column({ space: 16 }) {
-        ForEach(this.loginMethodArr, (item: BarType<undefined>) => {
+        ForEach(this.loginMethodArr, (item: BasicType<undefined>) => {
           LoginMethod(item)
         })
       }
@@ -23,7 +23,7 @@ export struct OtherLoginMethods {
 
 
 @Builder
-function LoginMethod(item: BarType<undefined>) {
+function LoginMethod(item: BasicType<undefined>) {
   Stack({ alignContent: Alignment.Start }) {
     Button(item.text)
       .border({ width: 1, color: $r('[basic].color.main_ac_color_dark') })

+ 2 - 2
features/user/src/main/ets/components/Terms.ets

@@ -25,13 +25,13 @@ export struct Terms {
           // Span('以及')
           Span('"用户协议"')
             .spanEx(() => {
-              yTRouter.router2AgreementPage('用户协议')
+              yTRouter.router2userAgreement()
 
             })
           Span('和')
           Span('"隐私政策"')
             .spanEx(() => {
-              yTRouter.router2AgreementPage('隐私政策')
+              yTRouter.router2Privacy()
             })
           Span('。')
         }

+ 91 - 0
features/user/src/main/ets/pages/AboutUS.ets

@@ -0,0 +1,91 @@
+import { YtAvoid, YTHeader, yTRouter } from 'basic';
+import { webview } from '@kit.ArkWeb';
+import { bundleManager } from '@kit.AbilityKit';
+
+
+@Builder
+function AboutUSBuilder() {
+  NavDestination() {
+    AboutUS()
+  }.title("关于我们")
+  .hideTitleBar(true)
+}
+
+@Component
+export struct AboutUS {
+  @State content: string = '';
+  private webviewController: WebviewController = new webview.WebviewController()
+  @StorageProp(YtAvoid.safeBottomKey) safeBottom: number = 0
+  ICPRecordNumber: string = '19019164号-5A'
+  listData: string[] = ['用户协议', '隐私政策']
+  @State versionName: string = ''
+
+  async aboutToAppear() {
+    const res = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT)
+    this.versionName = res.versionName
+  }
+
+  build() {
+    Column() {
+      YTHeader({ title: "关于我们" })
+
+      Column() {
+        Image($r('[basic].media.app_icon'))
+          .width(100)
+          .margin({ bottom: 16 })
+        Text('版本号:' + this.versionName)
+          .margin({ bottom: 8 })
+        Text('ICP备案号:' + this.ICPRecordNumber)
+          .margin({ bottom: 20 })
+          .fontSize(12)
+          .fontColor(Color.Gray)
+        List({ space: 25 }) {
+          ForEach(this.listData, (item: string, index) => {
+            if (index != 2) {
+              ListItem() {
+                Row() {
+                  Text(item).fontSize(13)
+                    .fontColor(Color.Gray)
+                  Blank()
+                  Image($r('app.media.MRight')).width('24').height('24').margin({ right: 10 })
+                }.width('100%')
+                .onClick(() => {
+                  switch (index) {
+                    case 0:
+                      yTRouter.router2userAgreement()
+                      break;
+                    case 1:
+                      yTRouter.router2Privacy()
+                      break;
+                  }
+
+                })
+              }
+            }
+          }, (item: string) => item)
+        }
+        .divider({
+          strokeWidth: 1,
+          color: '#DFDFDF',
+          startMargin: 20,
+          endMargin: 10
+        })
+        .width('100%')
+        .padding({ top: 10 })
+
+      }
+      .width('100%')
+      .margin({ top: 20 })
+      .padding({ left: 20, right: 20 })
+
+      // Web({
+      //   src: "https://hm-static.ytpm.net/friend/doc/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE.html",
+      //   controller: this.webviewController,
+      //   renderMode: RenderMode.ASYNC_RENDER // 设置渲染模式
+      // }).padding(40).width('100%').height('100%')
+
+    }
+    .padding({ bottom: this.safeBottom })
+
+  }
+}

+ 0 - 41
features/user/src/main/ets/pages/AgreementPage.ets

@@ -1,41 +0,0 @@
-import {YtAvoid, YTHeader, yTRouter } from 'basic'
-import { webview } from '@kit.ArkWeb'
-
-@Builder
-function agreementBuilder() {
-  NavDestination() {
-    AgreementPage()
-  }
-  .hideTitleBar(true)
-}
-
-@Component
-struct AgreementPage {
-  @State title: '隐私政策' | '关于我们' | '用户协议' = '隐私政策'
-  @StorageProp(YtAvoid.safeTopKey) safeTop: number = 0
-  @StorageProp(YtAvoid.safeBottomKey) safeBottom: number = 0
-  controller: webview.WebviewController = new webview.WebviewController()
-
-  aboutToAppear(): void {
-    this.title = yTRouter.getAgreementPageParam()
-    // IBestToast.showLoading({
-    //   message: '加载中...'
-    // })
-  }
-
-  build() {
-    Column({ space: 12 }) {
-      YTHeader({ title: this.title })
-      Column() {
-        Web({ src: 'https://www.baidu.com', controller: this.controller })
-          .width('100%')
-          .layoutWeight(1)
-          .onPageEnd(() => {
-            // IBestToast.hide()
-          })
-      }
-      .padding({ left: 16, right: 16 })
-    }
-    .padding({ top: this.safeTop, bottom: this.safeBottom })
-  }
-}

+ 5 - 4
features/user/src/main/ets/pages/LoginPage.ets

@@ -1,4 +1,4 @@
-import { BarType, YtAvoid, YtButton, yTRequest, yTRouter, yTToast } from 'basic'
+import { BasicType, YtAvoid, YtButton, yTRequest, yTRouter, yTToast } from 'basic'
 import { CodeInputType } from '../models'
 import { LoginInput } from '../components/LoginInput'
 import { Terms } from '../components/Terms'
@@ -30,7 +30,7 @@ struct LoginPage {
     txt: '验证码',
     placeHolder: '请输入验证码'
   }
-  loginMethodArr: BarType<undefined>[] = [
+  loginMethodArr: BasicType<undefined>[] = [
   // {
   //   src: $r('app.media.wechat'),
   //   txt: '微信登入',
@@ -44,7 +44,7 @@ struct LoginPage {
       text: '华为登录',
       click: () => {
         if (!this.isAgree) {
-          // yTToast.agreePrivacy({ text: '华为登录' })
+          yTToast.agreePrivacy({ text: '华为登录' })
           return
         }
         yTRequest.huaweiLogin()
@@ -84,7 +84,7 @@ struct LoginPage {
                 'phonenumber': this.phoneNumber,
                 'captcha': this.Captcha
               })
-            // yTToast.agreePrivacy({ text: '验证码登录' })
+            yTToast.agreePrivacy({ text: '验证码登录' })
             return
           }
           yTRequest.phonenumberLogin({
@@ -125,6 +125,7 @@ struct LoginPage {
 
       OtherLoginMethods({ loginMethodArr: this.loginMethodArr })
     }
+    .backgroundColor(Color.White)
     .padding({
       top: 10 + this.safeTop,
       left: 16,

+ 33 - 0
features/user/src/main/ets/pages/Privacy.ets

@@ -0,0 +1,33 @@
+import { YtAvoid, YTHeader } from 'basic';
+import { webview } from '@kit.ArkWeb';
+
+
+@Builder
+function PrivacyBuilder() {
+  NavDestination() {
+    Privacy()
+  }.title("隐私政策")
+  .hideTitleBar(true)
+}
+
+@Component
+export struct Privacy {
+  @State content: string = '';
+  private webviewController: WebviewController = new webview.WebviewController()
+  @StorageProp(YtAvoid.safeBottomKey) safeBottom: number = 0
+
+  build() {
+
+    Column() {
+      YTHeader({ title: "隐私政策" })
+      Web({
+        src: "https://hm-static.ytpm.net/friend/doc/%E9%9A%90%E7%A7%81%E6%94%BF%E7%AD%96.html",
+        controller: this.webviewController,
+        renderMode: RenderMode.ASYNC_RENDER // 设置渲染模式
+      }).padding(20)
+    }
+    .width('100%')
+    .height('100%')
+    .padding({ bottom: this.safeBottom })
+  }
+}

+ 9 - 10
features/user/src/main/ets/pages/SettingPage.ets

@@ -1,5 +1,5 @@
 import {
-  BarType,
+  BasicType,
   IBestToast,
   reviseImgHeaderBuilder,
   takePicture,
@@ -11,8 +11,7 @@ import {
   YTHeader,
   YTLog,
   yTRequest,
-  yTRouter,
-  yTToast
+  yTRouter
 } from 'basic'
 
 @Builder
@@ -31,7 +30,7 @@ struct SettingPage {
   @State showReviseName: boolean = false
   @State showHeaderImgRevise: boolean = false
   @State value: string = ''
-  reviseBuilderArr: Array<BarType<undefined>> = [
+  reviseBuilderArr: Array<BasicType<undefined>> = [
     {
       text: '头像修改',
       src: '',
@@ -46,7 +45,7 @@ struct SettingPage {
       }
     }
   ]
-  options: BarType<undefined>[] = [
+  options: BasicType<undefined>[] = [
     {
       text: '拍照',
       click: async () => {
@@ -69,7 +68,7 @@ struct SettingPage {
             yTRequest.refreshUserInfo(() => {
               IBestToast.hide()
               setTimeout(() => {
-                IBestToast.show({ message: '头像修改成功', type: 'success' })
+                IBestToast.show({ message: '头像修改成功' })
               }, 100)
             })
           })
@@ -88,7 +87,7 @@ struct SettingPage {
     Column() {
       YTHeader({ title: '用户设置' })
       Column() {
-        ForEach(this.reviseBuilderArr, (item: BarType<undefined>, index) => {
+        ForEach(this.reviseBuilderArr, (item: BasicType<undefined>, index) => {
           this.reviseBuilder(item)
           if (index < this.reviseBuilderArr.length - 1) {
             Column() {
@@ -109,7 +108,7 @@ struct SettingPage {
           btContent: '退出登录', click: () => {
             this.userInfo.logout()
             yTRouter.routerBack()
-            IBestToast.show({ message: '退出登录成功', type: 'success' })
+            IBestToast.show({ message: '退出登录成功' })
           }
         })
           .margin({ top: 426, bottom: 12 })
@@ -142,7 +141,7 @@ struct SettingPage {
   }
 
   @Builder
-  reviseBuilder(item: BarType<undefined>) {
+  reviseBuilder(item: BasicType<undefined>) {
     Row() {
       Text(item.text)
         .fontSize($r('[basic].float.page_text_font_size_12'))
@@ -230,7 +229,7 @@ struct SettingPage {
               return
             }
             yTRequest.changeNickname(this.value, () => {
-              IBestToast.show({ message: '名称修改成功', type: 'success' })
+              IBestToast.show({ message: '名称修改成功' })
             })
             //TODO 发送请求后关闭弹窗
             this.showReviseName = false

+ 1 - 2
features/user/src/main/ets/pages/SuggestionPage.ets

@@ -10,7 +10,6 @@ function suggestionBuilder() {
 
 @Component
 struct SuggestionPage {
-  @StorageProp(YtAvoid.safeTopKey) safeTop: number = 0
   @StorageProp(YtAvoid.safeBottomKey) safeBottom: number = 0
   @State description: string = ''
   @State contact: string = ''
@@ -132,7 +131,7 @@ struct SuggestionPage {
       .padding({ left: 18, right: 18 })
 
     }
-    .padding({ top: this.safeTop, bottom: this.safeBottom })
+    .padding({ bottom: this.safeBottom })
 
   }
 }

+ 29 - 0
features/user/src/main/ets/pages/UserAgreement.ets

@@ -0,0 +1,29 @@
+import { webview } from '@kit.ArkWeb'
+import { YTHeader } from 'basic'
+
+@Builder
+function userAgreementBuilder() {
+  NavDestination() {
+    UserAgreementPage()
+  }.title("用户协议")
+  .hideTitleBar(true)
+}
+
+@Component
+struct UserAgreementPage {
+  private webviewController: WebviewController = new webview.WebviewController()
+
+  build() {
+    Column() {
+      YTHeader({ title: '用户协议' })
+      Web({
+        src: "https://hm-static.ytpm.net/friend/doc/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE.html",
+        controller: this.webviewController,
+        renderMode: RenderMode.ASYNC_RENDER // 设置渲染模式
+      })
+        .padding(40)
+        .width('100%')
+        .layoutWeight(1)
+    }
+  }
+}

+ 30 - 50
features/user/src/main/ets/views/Mine.ets

@@ -1,57 +1,37 @@
-import { BarType, copyText, ShowBannerAd, UserInfo, userInfo, YtAvoid, yTRouter } from 'basic'
+import { BasicType, copyText, ShowBannerAd, UserInfo, userInfo, YtAvoid, yTRouter } from 'basic'
 
 @Component
 export struct Mine {
   @StorageProp(YtAvoid.safeTopKey) safeTop: number = 0
   @StorageProp(userInfo.KEY) userInfo: UserInfo = new UserInfo()
-  setArr: Array<BarType<undefined>> = [
-    // {
-    //   text: 'AI次数兑换',
-    //   click: () => {
-    //     yTRouter.router2AIExchangePage()
-    //     if (this.userInfo.checkLogin()) {
-    //
-    //     } else {
-    //       //TODO 去登录页
-    //     }
-    //
-    //   },
-    //   src: $r('app.media.right_arrow')
-    // },
-    // {
-    //   text: '意见反馈',
-    //   click: () => {
-    //     yTRouter.router2SuggestionPage()
-    //     if (this.userInfo.checkLogin()) {
-    //
-    //     } else {
-    //       //TODO 去登录页
-    //     }
-    //
-    //   },
-    //   src: $r('app.media.right_arrow')
-    // },
-    // {
-    //   text: '给个好评',
-    //   click: () => {
-    //
-    //   },
-    //   src: $r('app.media.right_arrow')
-    // },
-    // {
-    //   text: '关于我们',
-    //   click: () => {
-    //     yTRouter.router2AgreementPage('关于我们')
-    //   },
-    //   src: $r('app.media.right_arrow')
-    // },
-    // {
-    //   text: '隐私政策',
-    //   click: () => {
-    //     yTRouter.router2AgreementPage('隐私政策')
-    //   },
-    //   src: $r('app.media.right_arrow')
-    // }
+  setArr: Array<BasicType<undefined>> = [
+    {
+      text: '意见反馈',
+      click: () => {
+        yTRouter.router2SuggestionPage()
+        if (this.userInfo.checkLogin()) {
+
+        } else {
+          //TODO 去登录页
+        }
+
+      },
+      src: $r('app.media.right_arrow')
+    },
+    {
+      text: '给个好评',
+      click: () => {
+
+      },
+      src: $r('app.media.right_arrow')
+    },
+    {
+      text: '关于我们',
+      click: () => {
+        yTRouter.router2AboutUS()
+      },
+      src: $r('app.media.right_arrow')
+    }
   ]
 
   aboutToAppear(): void {
@@ -111,7 +91,7 @@ export struct Mine {
       ShowBannerAd()
 
       List() {
-        ForEach(this.setArr, (item: BarType<undefined>, index) => {
+        ForEach(this.setArr, (item: BasicType<undefined>, index) => {
           ListItem() {
             Row() {
               Text(item.text)

+ 1 - 0
features/user/src/main/module.json5

@@ -3,6 +3,7 @@
     "name": "user",
     "type": "shared",
     "description": "$string:shared_desc",
+    "routerMap": "$profile:router_map",
     "deviceTypes": [
       "phone",
       "tablet",

BIN
features/user/src/main/resources/base/media/MRight.png


+ 38 - 0
features/user/src/main/resources/base/profile/router_map.json

@@ -0,0 +1,38 @@
+{
+  "routerMap": [
+    {
+      "name": "SuggestionPage",
+      "pageSourceFile": "src/main/ets/pages/SuggestionPage.ets",
+      "buildFunction": "suggestionBuilder"
+    },
+    {
+      "name": "AboutUS",
+      "pageSourceFile": "src/main/ets/pages/AboutUS.ets",
+      "buildFunction": "AboutUSBuilder"
+    },
+    {
+      "name": "UserAgreementPage",
+      "pageSourceFile": "src/main/ets/pages/UserAgreement.ets",
+      "buildFunction": "userAgreementBuilder",
+      "data": {
+        "description": "this is userAgreementPage"
+      }
+    },
+    {
+      "name": "Privacy",
+      "pageSourceFile": "src/main/ets/pages/Privacy.ets",
+      "buildFunction": "PrivacyBuilder",
+      "data": {
+        "description": "this is PrivacyPage"
+      }
+    },
+    {
+      "name": "LoginPage",
+      "pageSourceFile": "src/main/ets/pages/LoginPage.ets",
+      "buildFunction": "LoginBuilder",
+      "data": {
+        "description": "this is loginPage"
+      }
+    }
+  ]
+}

+ 9 - 3
products/entry/src/main/ets/entryability/EntryAbility.ets

@@ -1,9 +1,7 @@
 import { AbilityConstant, bundleManager, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { window } from '@kit.ArkUI';
-import {  jhStartAd, permissionControl, YtAvoid,
-  yTBreakPoint,
-  YTLog, yTRequest } from 'basic';
+import { jhStartAd, permissionControl, YtAvoid, yTBreakPoint, YTLog, yTRequest } from 'basic';
 import { identifier } from '@kit.AdsKit';
 import { BusinessError } from '@kit.BasicServicesKit';
 import { preferences } from '@kit.ArkData';
@@ -27,6 +25,14 @@ export default class EntryAbility extends UIAbility {
         hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
         return;
       }
+      YTLog.init({
+        tag: "YTLog",
+        domain: 0x0000,
+        close: false,
+        isHilog: true,
+        showLogLocation: true,
+        logSize: 800
+      })
 
       PersistentStorage.persistProp<string>('token', '')
       if (AppStorage.get<string>('token')) {

+ 4 - 4
products/entry/src/main/ets/pages/Index.ets

@@ -1,4 +1,4 @@
-import { BarType, YtAvoid, yTRouter, yTToast } from 'basic';
+import { BasicType, YtAvoid, yTRouter, yTToast } from 'basic';
 import { Mine } from 'user';
 
 @Entry
@@ -10,7 +10,7 @@ struct Index {
   aboutToAppear(): void {
     yTToast.init({
       context: this.getUIContext(),
-      options: { alignment: DialogAlignment.Center, maskColor: $r('[basic].color.mask_color') }
+      options: { alignment: DialogAlignment.Center, maskColor: '#80000000' }
     })
   }
 
@@ -46,7 +46,7 @@ struct Index {
       }
       .barBackgroundColor($r('[basic].color.main_blank'))
       .barPosition(BarPosition.End)
-
+      .backgroundColor(Color.White)
       .onTabBarClick((index) => {
         this.currentIndex = index
       })
@@ -59,7 +59,7 @@ struct Index {
   }
 
   @Builder
-  barBuilder(item: BarType<undefined>) {
+  barBuilder(item: BasicType<undefined>) {
     Column() {
       Image(item.src)
         .width(24)

+ 6 - 0
products/entry/src/main/module.json5

@@ -49,6 +49,12 @@
         ]
       }
     ],
+//    "metadata": [
+//      {
+//        "name": "client_id",
+//        "value": "6917573740100956853"
+//      }
+//    ],
     "extensionAbilities": [
       {
         "name": "EntryBackupAbility",