wwf
2025-10-01 5f97e4b263b76b25f22592c02fbd482977e043bc
登录、注册
1个文件已删除
7个文件已修改
32个文件已添加
1501 ■■■■■ 已修改文件
AppScope/resources/base/media/foreground.png 补丁 | 查看 | 原始文档 | blame | 历史
code-linter.json5 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/data/HttpResponse.ets 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/data/UserInfoBase.ets 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/entryability/EntryAbility.ets 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/pages/MainPage.ets 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/pages/StartPage.ets 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/pages/account/AccountPage.ets 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/pages/home/Home.ets 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/pages/login/LoginPage.ets 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/pages/login/SignInPage.ets 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/pages/message/MessageDetailsPage.ets 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/pages/message/MessagePage.ets 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/pages/search/SearchPage.ets 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/utils/PreferencesUtils.ets 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/ets/utils/PromptActionClass.ets 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/module.json5 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/element/string.json 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/active_list_img.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/classification_AI.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/date_icon.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/icon_password.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/icon_username.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/image1.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/login_page_bg.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/signIn_page_check.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/startIcon.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/start_page_bg.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/tabs_account_icon.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/tabs_account_icon_active.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/tabs_home_icon.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/tabs_home_icon_active.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/tabs_message_icon.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/tabs_message_icon_active.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/tabs_search_platform_icon.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/tabs_search_platform_icon_active.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/media/title_decoration_icon.png 补丁 | 查看 | 原始文档 | blame | 历史
entry/src/main/resources/base/profile/main_pages.json 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
oh-package-lock.json5 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
AppScope/resources/base/media/foreground.png

code-linter.json5
File was deleted
entry/src/main/ets/data/HttpResponse.ets
New file
@@ -0,0 +1,5 @@
export class HttpResponseResult<T> {
  code: number = 0
  msg: string = ''
  data: T | null = null
}
entry/src/main/ets/data/UserInfoBase.ets
New file
@@ -0,0 +1,7 @@
/**
  * @Description : ps存储的对象类型
 */
export class UserInfoBase {
  account: string = ''
  password: string = ''
}
entry/src/main/ets/entryability/EntryAbility.ets
@@ -1,6 +1,7 @@
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import PreferencesUtils from '../utils/PreferencesUtils';
const DOMAIN = 0x0000;
@@ -8,6 +9,8 @@
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
    PreferencesUtils.loadPreferences(this.context,'UserInfo')
  }
  onDestroy(): void {
@@ -18,7 +21,7 @@
    // Main window is created, set main page for this ability
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    windowStage.loadContent('pages/Index', (err) => {
    windowStage.loadContent('pages/StartPage', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
entry/src/main/ets/pages/MainPage.ets
New file
@@ -0,0 +1,76 @@
import { Home } from './home/Home'
/**
 * @Description : 底部导航,APP模块
 * Tabs 选项卡功能
 */
class TabBarBase {
  icon: ResourceStr  = ''
  title: string = ''
}
@Entry
@Component
struct MainPage {
  @State tabBarList: TabBarBase[] = [
    { icon: 'tabs_home_icon', title: '主页' },
    { icon: 'tabs_search_platform_icon', title: '发现' },
    { icon: 'tabs_message_icon', title: '消息' },
    { icon: 'tabs_account_icon', title: '我的' }
  ]
  @State selectIndex: number = 0
  private tabController: TabsController = new TabsController()
  @Builder tabItem(icon: ResourceStr,title: string,index: number){
    Column({space: 5}){
      Image($r('app.media.' + icon + (index===this.selectIndex ? '_active' : '' )))
        .width(22)
        .height(22)
      Text(title)
        .fontSize(14)
        .fontColor(index === this.selectIndex ? "#1756f4" : '#6a6a6a')
    }
    .onClick(() => {
      this.selectIndex = index
      this.tabController.changeIndex(this.selectIndex)
    })
  }
  build() {
    Stack({alignContent: Alignment.Bottom}){
      Tabs({barPosition: BarPosition.End,controller: this.tabController}){
        ForEach(this.tabBarList,(item: TabBarBase,index:number) => {
          TabContent(){
            if (index === 0) {
              Home()
            } else if (index === 1){
              // SearchPage()
            } else if (index === 2){
              // MessagePage()
            } else if (index === 3){
              // AccountPage()
            }
          }
          //调用自定义的样式
          // .tabBar(this.tabItem(item.icon,item.title,index))
        })
      }
      .barHeight(0)
      .onChange((index: number) => {
        this.selectIndex = index
      })
      Row() {
        ForEach(this.tabBarList, (item: TabBarBase, index) => {
          this.tabItem(item.icon, item.title, index)
        })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width('100%')
      .height(60)
      .padding(10)
      .border({ width: { top: 1 }, color: { top: '#e3e3e3' }, style: { top: BorderStyle.Solid }})
      .backgroundColor(Color.White)
    }
    .width('100%')
    .height('100%')
  }
}
entry/src/main/ets/pages/StartPage.ets
New file
@@ -0,0 +1,92 @@
/**
 * @Description : 启动页面
 */
@Entry
@Component
struct StartUpPage {
  @State logoY: number = -200
  @State columnY: number = 200
  @State isShow: boolean = false
  aboutToAppear(): void {
  }
  build() {
    Column(){
      Image($r('app.media.logo'))
        .width(100)
        .height(100)
        //设置logo图片的Y轴位置
        .translate({y: this.logoY})
        //属性动画
        .animation({
          //动画时长
          duration: 3000,
          //播放速度
          tempo: 1,
          //循环次数  -1无限循环
          iterations: 1,
          curve: Curve.EaseInOut,
          //播放模式:
          playMode: PlayMode.Normal,
          //延时播放
          delay: 500
        })
      Blank()
      Column({ space: 10 }) {
        Text('学聘同行').newExtend(30, 700)
        Text('以学习蓄力,以应聘通关').newExtend(20, 400)
          .textCase(TextCase.UpperCase)
        Blank()
      }
      .shadow({
        radius: 8,
        color: '#efefef',
        offsetX: 0,
        offsetY: -10
      })
      .StartColumn()
      .translate({ y: this.columnY })
      .animation({
        duration: 3000,
        delay: 500,
        curve: Curve.EaseInOut,
        iterations: 1
      })
      .onAppear(() => {
        //执行动画操作
        this.logoY = 200
        this.columnY = 0
        //设置倒计时
        let intervalId = setInterval(() => {
          //页面跳转
          this.getUIContext().getRouter().replaceUrl({
            url:'pages/login/LoginPage'
          })
          //销毁计时器
          clearInterval(intervalId)
        },4000)
      })
    }
    .width('100%')
    .height('100%')
    .backgroundImage($r('app.media.start_page_bg'))
    .backgroundImageSize({width: '100%',height:'100%'})
  }
}
@Extend(Column)
function StartColumn() {
  .width('100%')
  .height(180)
  .alignItems(HorizontalAlign.Start)
  .borderRadius({ topLeft: 20, topRight: 20 })
  .backgroundColor(Color.White)
  .padding(20)
}
@Extend(Text)
function newExtend(mSize: number,mWeight: FontWeight) {
  .fontSize(mSize)
  .fontWeight(mWeight)
}
entry/src/main/ets/pages/account/AccountPage.ets
New file
@@ -0,0 +1,23 @@
@Entry
@Component
export struct AccountPage {
  @State message: string = 'Hello World';
  build() {
    RelativeContainer() {
      Text(this.message)
        .id('AccountPageHelloWorld')
        .fontSize($r('app.float.page_text_font_size'))
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          this.message = 'Welcome';
        })
    }
    .height('100%')
    .width('100%')
  }
}
entry/src/main/ets/pages/home/Home.ets
New file
@@ -0,0 +1,233 @@
class TabBarBase {
  title: string = ''
}
class Classification {
  title: string = ''
  icon: ResourceStr = ''
}
class Activity {
  title: string = ''
  timeScope: string = ''
  signupCount: number = 0
  statusText: string = ''
}
@Entry
@Component
export struct Home {
  @State pageHeight: number = 0
  @State activeIndex:number = 0
  @State tabList: TabBarBase[] = [
    { title: '首页' },
    { title: '课程' }
  ]
  private tabController: TabsController = new TabsController()
  @State swiperList: ResourceStr[] = [
    $r('app.media.image1'),
    $r('app.media.image1'),
    $r('app.media.image1'),
  ]
  @State swiperIndex: number = 0
  //TabBar样式
  @State classificationList: Classification[] = [
    { title: '数通', icon: $r('app.media.classification_shutong') },
    { title: '安全', icon: $r('app.media.classification_safety') },
    { title: '云计算', icon: $r('app.media.classification_cloudcalc') },
    { title: '存储', icon: $r('app.media.classification_storage') },
    { title: '鲲鹏', icon: $r('app.media.classification_roc') },
    { title: 'AI', icon: $r('app.media.classification_AI') },
    { title: '连接', icon: $r('app.media.classification_link') },
    { title: '大数据', icon: $r('app.media.classification_bigdata') },
    { title: '云服务', icon: $r('app.media.classification_cloudservice') },
    { title: '更多', icon: $r('app.media.classification_more') },
  ]
  @State activityList: Activity[] = [
    { title: '北京人工智能IE', timeScope: '20250520-20250620', signupCount: 80, statusText: '报名中' },
    { title: '北京人工智能IE', timeScope: '20250520-20250620', signupCount: 80, statusText: '进行中' },
    { title: '北京人工智能IE', timeScope: '20250520-20250620', signupCount: 80, statusText: '已结束' },
    { title: '北京人工智能IE', timeScope: '20250520-20250620', signupCount: 80, statusText: '报名中' },
    { title: '北京人工智能IE', timeScope: '20250520-20250620', signupCount: 80, statusText: '已结束' },
  ]
  @Builder tabBarItem(name:string,index: number){
    Row(){
      Text(name)
        .fontSize(this.activeIndex == index ? 16 : 14)
        .fontWeight(700)
        .fontColor(this.activeIndex == index ? '#1756f4' : '#666666')
        .margin(index == 0 ? { left: '50%' } : { right: '50%' } )
    }
    .onClick(() => {
      this.activeIndex = index
      this.tabController.changeIndex(index)
    })
  }
  getTagFontColor(statusText: string) {
    switch (statusText) {
      case '报名中':
        return '#10920e'
      case '进行中':
        return '#ffa100'
      default :
        return '#666666'
    }
  }
  getTagBgColor(statusText: string) {
    switch (statusText) {
      case '报名中':
        return '#d5f2db'
      case '进行中':
        return '#fff0cc'
      default :
        return '#ebebeb'
    }
  }
  build() {
    Column() {
      Text('鸿蒙实训首页')
        .width('100%')
        .textAlign(TextAlign.Center)
        .fontWeight(800)
        .fontSize(18)
      Tabs({controller:this.tabController}){
        ForEach(this.tabList, (item: TabBarBase, index: number) => {
          // 首页 ------------
          if (item.title == '首页') {
            TabContent() {
              Column({ space: 10 }){
                Swiper() {
                  ForEach(this.swiperList, (item: Resource) => {
                    Image(item)
                      .width('100%')
                      .height(120)
                      .backgroundColor(0xAFEEEE)
                  })
                }
                .borderRadius(10)
                .interval(3000)
                .autoPlay(true)
                .indicator(
                  Indicator.dot()
                    .itemWidth(8)
                    .itemHeight(8)
                    .selectedItemWidth(16)
                    .selectedItemHeight(8)
                    .color(Color.White)
                )
                .onChange((index) => {
                  this.swiperIndex = index;
                })
                Grid() {
                  ForEach(this.classificationList, (item: Classification ) => {
                    GridItem() {
                      Column({ space: 6 }) {
                        Image(item.icon)
                          .width(26)
                          .height(26)
                        Text(item.title)
                          .fontSize(12)
                          .fontWeight(500)
                      }
                    }
                  })
                }
                .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
                .columnsGap(14)
                .rowsGap(14)
                .width('100%')
                .height(110)
                Row() {
                  Image($r('app.media.title_decoration_icon'))
                    .width(4)
                  Text('热门培训')
                    .margin({ left: 8 })
                    .fontSize(16)
                    .fontWeight(700)
                }
                .alignItems(VerticalAlign.Center)
                .width('100%')
                List({ space: 10, initialIndex: 0 }) {
                  ForEach(this.activityList, (item: Activity, index: number) => {
                    ListItem() {
                      Row() {
                        Column({ space: 6 }) {
                          Text(item.title)
                            .fontSize(12)
                            .fontWeight(500)
                          Row() {
                            Image($r('app.media.date_icon'))
                              .width(12)
                              .height(12)
                            Text(item.timeScope)
                              .fontSize(10)
                              .fontColor('#676767')
                              .margin({ left: 4 })
                          }
                          Text(`${item.signupCount || 0}人已报名`)
                            .fontSize(10)
                            .fontColor('#676767')
                          Text(item.statusText)
                            .fontSize(10)
                            .fontWeight(500)
                            .padding(6)
                            .borderRadius(10)
                            .fontColor(this.getTagFontColor(item.statusText))
                            .backgroundColor(this.getTagBgColor(item.statusText))
                        }
                        .height('100%')
                        .justifyContent(FlexAlign.Start)
                        .alignItems(HorizontalAlign.Start)
                        Image($r('app.media.active_list_img'))
                          .aspectRatio(16/9)
                          .height('100%')
                      }
                      .width('100%')
                      .height(100)
                      .padding(10)
                      .justifyContent(FlexAlign.SpaceBetween)
                    }
                  }, (index: number) => index.toString())
                }
                .listDirection(Axis.Vertical) // 排列方向
                .scrollBar(BarState.Off)
                .friction(0.6)
                .divider({ strokeWidth: 1, color: '#e8e8e8' }) // 每行之间的分界线
                .edgeEffect(EdgeEffect.Spring)
                .width('100%')
                .height(300)
              }
              .height('100%')
            }
            .tabBar(this.tabBarItem(item.title, index))
            .height('100%')
          }
          // 课程------------
          if (item.title == '课程') {
            TabContent() {
              Text('课程content')
            }.tabBar(this.tabBarItem(item.title, index))
          }
        })
      }
      .barHeight(40)
      .margin({ top: 6 })
      .width('100%')
      .height('100%')
      .padding({ left: 14, right: 14, bottom: 14 })
      .onChange((index: number) => {
        this.activeIndex = index
      })
    }
    .width('100%')
    .height('100%')
  }
}
entry/src/main/ets/pages/login/LoginPage.ets
New file
@@ -0,0 +1,286 @@
import { UserInfoBase } from '../../data/UserInfoBase'
import { promptAction } from '@kit.ArkUI'
import PreferencesUtils from '../../utils/PreferencesUtils'
import http from '@ohos.net.http';
import { HttpResponseResult } from '../../data/HttpResponse';
import { JSON } from '@kit.ArkTS';
/**
 * @Description : 登录页
 */
@Entry
@Component
struct LoginPage {
  @State account: string = ''
  @State password: string = ''
  @State rememberPassword: boolean = false
  @State agreement: boolean = false
  @State accountErrorFlag: boolean = false
  @State passwordErrorFlag: boolean = false
  //用来获取存储的密码和用户账号
  @State userInfoData: UserInfoBase  = new UserInfoBase()
  aboutToAppear(): void {
    PreferencesUtils.getPreferences('UserInfo','user','').then((value: string) => {
      if (!value) return
      this.userInfoData = JSON.parse(value) as UserInfoBase
      this.account = this.userInfoData.account
      this.password = this.userInfoData.password
      if (this.account && this.password) {
        this.rememberPassword = true
      }
    })
  }
  submitLogin() {
    if (!this.account) {
      this.accountErrorFlag = true
      return
    }
    if (!this.password  || this.password.length !== 8) {
      this.passwordErrorFlag = true
      return
    }
    if (!this.agreement) {
      promptAction.showToast({
        message: '请勾选协议和隐私政策',
        backgroundColor: '#242933',
        backgroundBlurStyle: BlurStyle.NONE,
        textColor: Color.White
      })
      return
    }
    this.userInfoData = {
      account: this.account,
      password: this.password
    }
    let httpRequest = http.createHttp();
    // 准备要提交的数据体
    interface PostData  {
      mobilePhone: string,
      password: string
    }
    let postData: PostData = {
      mobilePhone: this.account,
      password: this.password
    }
    httpRequest.request(
      "http://192.168.20.70:8080/quiz-community/public/v1.0/users/login",
      {
        method: http.RequestMethod.PUT,
        header: { 'Content-Type': 'application/json' },
        extraData: postData
      },
      (err, data) => {
        console.log('response', '/users/register')
        console.log(JSON.stringify(data.result))
        if (data.responseCode == 200) {
          interface Data {
            token: string
          }
          const resData = (typeof data.result == 'string' ? JSON.parse(data.result) : data.result) as HttpResponseResult<Data>
          if (resData.code == 200) {
            PreferencesUtils.putPreferences('UserInfo','user',JSON.stringify(this.userInfoData))
            AppStorage.SetOrCreate('x-jwt-token', resData.data?.token)
            promptAction.showToast({ message: '登录成功' })
          } else {
            promptAction.showToast({ message: resData.msg })
          }
        } else {
          promptAction.showToast({ message: err.message })
        }
      }
    )
  }
  build() {
    Column({space: 10}){
      Text('您好,')
        .width('100%')
        .fontSize(22)
        .fontWeight(800)
        .fontColor(Color.White)
      Text('欢迎使用学聘同行')
        .width('100%')
        .fontSize(22)
        .fontWeight(800)
        .fontColor(Color.White)
      Column({ space: 20 }) {
        Text('账号登录')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
          .fontWeight(800)
        Row() {
          Image($r('app.media.icon_username'))
            .width(16)
            .height(16)
          TextInput({ placeholder: '请输入账号', text: $$this.account })
            .type(InputType.USER_NAME)
            .backgroundColor('#f3f7fe')
            .onChange(() => {
              this.accountErrorFlag = false
            })
        }
        .width('100%')
        .backgroundColor('#f3f7fe')
        .border(this.accountErrorFlag ? { width: 1, color: Color.Red } : { width: 'none' })
        .borderRadius(10)
        .padding({ left: 10, right: 10 })
        if(this.accountErrorFlag) {
          Text('账号不能空或账号有误')
            .width('100%')
            .fontSize(12)
            .fontColor(Color.Red)
            .margin({ top: -16, bottom: -16, left: 20 })
        }
        Row() {
          Image($r('app.media.icon_password'))
            .width(16)
            .height(16)
          TextInput({ placeholder: '请输入密码', text: $$this.password })
            .type(InputType.Password)
            .backgroundColor('#f3f7fe')
            .onChange(() =>{
              this.passwordErrorFlag = false
            })
        }
        .width('100%')
        .backgroundColor('#f3f7fe')
        .border(this.passwordErrorFlag ? { width: 1, color: Color.Red } : { width: 'none' })
        .borderRadius(10)
        .padding({ left: 10, right: 10 })
        if(this.passwordErrorFlag) {
          Text('密码不能空或密码有误')
            .width('100%')
            .fontSize(12)
            .fontColor(Color.Red)
            .margin({ top: -16, bottom: -16, left: 20 })
        }
        Row(){
          Row() {
            Checkbox()
              .select($$this.rememberPassword)
              .selectedColor(0x39a2db)
              .shape(CheckBoxShape.ROUNDED_SQUARE)
              .width(14)
            Text('记住密码')
              .fontSize(14)
          }
          .onClick(() => this.rememberPassword = !this.rememberPassword)
          Text('忘记密码?')
            .fontSize(14)
            .fontColor('#1756f4')
            .decoration({ type: TextDecorationType.Underline, color: '#1756f4' })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        Button('登 录')
          .type(ButtonType.Normal)
          .width('100%')
          .borderRadius(10)
          .onClick(() => {
            this.submitLogin()
          })
        Row() {
          Checkbox()
            .select($$this.agreement)
            .selectedColor(0x39a2db)
            .shape(CheckBoxShape.ROUNDED_SQUARE)
            .width(14)
          Text(){
            Span('我已阅读并同意')
              .fontColor(Color.Black)
            Span('《用户协议》')
              .fontColor('#1756f4')
            Span('和')
              .fontColor(Color.Black)
            Span('《隐私协议》')
              .fontColor('#1756f4')
          }
            .fontSize(12)
        }
        .width('100%')
        .justifyContent(FlexAlign.Center)
        .onClick(() => { this.agreement = !this.agreement })
      }
      .width('100%')
      .margin({ top: 20 })
      .padding({ top: 30, bottom: 30, left: 20, right: 20 })
      .borderRadius(6)
      .backgroundColor(Color.White)
      Row() {
        Text() {
          Span('没有账号?')
          Span('立即注册>')
            .fontColor('#1756f4')
            .decoration({ type: TextDecorationType.Underline, color: '#1756f4' })
            .onClick(() => {
              this.getUIContext().getRouter().pushUrl({
                url: 'pages/login/SignInPage'
              })
            })
        }
        .fontSize(14)
        .fontWeight(500)
        .margin({ top: 10 })
      }
      // Row(){
      //   Text('注册')
      //     .fontColor(Color.White)
      //     .onClick(() => {
      //       this.getUIContext().getRouter().pushUrl({
      //         url: 'pages/login/SingInPage'
      //       })
      //     })
      // }.width('90%')
      // .offset({y: -15,x: -20})
      // .justifyContent(FlexAlign.End)
      // Blank()
      // Button('登  录')
      //   .width('90%')
      //   .height(70)
      //   .fontSize(25)
      //   .backgroundColor("#ff09b8aa")
      //   .onClick(() => {
      //     //点击获取输入框内容
      //     console.log(`用户名:${this.userInfoData.userName},密码: ${this.userInfoData.userPwd}`);
      //     if (this.userName === '' || this.userName === undefined || this.userName.trim().length <= 0 || this.userName != this.userInfoData.userName) {
      //       this.isShowName = false
      //       this.getUIContext().getPromptAction().showToast({message: '用户不能空或用户名有误'})
      //       return //结束当前逻辑,不在向下执行
      //     }
      //     if (this.userPwd === '' || this.userPwd === undefined || this.userPwd.trim().length <= 0 || this.userPwd != this.userInfoData.userPwd) {
      //       this.getUIContext().getPromptAction().showToast({message: '密码不能空或密码有误'})
      //       this.isShowPwd = false
      //       return //结束当前逻辑,不在向下执行
      //     }
      //     this.getUIContext().getPromptAction().showToast({message: '登录成功'})
      //     this.getUIContext().getRouter().replaceUrl({
      //       url:'pages/MainPage'
      //     })
      //   })
    }
    .width('100%')
    .height('100%')
    .padding({ top: 100, left: 20, right: 20 })
    .backgroundImage($r('app.media.login_page_bg'))
    .backgroundImageSize({width: '100%',height:'100%'})
  }
}
entry/src/main/ets/pages/login/SignInPage.ets
New file
@@ -0,0 +1,254 @@
import { promptAction } from '@kit.ArkUI'
import http from '@ohos.net.http';
import { HttpResponseResult } from '../../data/HttpResponse';
import { ComponentContent } from '@kit.ArkUI';
import { PromptActionClass } from '../../utils/PromptActionClass';
class DialogParams {
  text: string = "";
  constructor(text: string) {
    this.text = text;
  }
}
@Builder
function buildText(params: DialogParams) {
  Column({ space: 10 }) {
    Image($r('app.media.signIn_page_check'))
      .width(80)
      .height(80)
    Text(params.text)
      .fontSize(20)
      .fontWeight(FontWeight.Bold)
    Text()
      .fontSize(16)
      .fontColor('#666666')
    Button('返回登录页')
      .type(ButtonType.Normal)
      .borderRadius(8)
      .width('100%')
      .onClick(() => {
        PromptActionClass.closeDialog();
        PromptActionClass.ctx.getRouter().replaceUrl({
          url: 'pages/login/LoginPage'
        })
      })
  }
  .width(300)
  .padding({ top: 40, bottom: 40, left: 20, right: 20 })
  .borderRadius(14)
  .backgroundColor(Color.White)
}
/**
 * @Description : 注册页
 */
@Entry
@Component
struct SignInPage {
  @State account: string = ''
  @State password: string = ''
  @State confirmPassword: string = ''
  @State accountErrorFlag: boolean = false
  @State passwordErrorFlag: boolean = false
  @State confirmPasswordErrorFlag: boolean = false
  @State dialogMessage: string = '注册成功';
  private ctx: UIContext = this.getUIContext();
  private contentNode: ComponentContent<Object> =
    new ComponentContent(this.ctx, wrapBuilder(buildText), new DialogParams((this.dialogMessage)));
  params: DialogParams = new DialogParams('注册成功')
  aboutToAppear(): void {
    PromptActionClass.setContext(this.ctx);
    PromptActionClass.setContentNode(this.contentNode);
    PromptActionClass.setOptions({ alignment: DialogAlignment.Center, offset: { dx: 0, dy: 0 } });
  }
  submitRegister() {
    if (!this.account) {
      this.accountErrorFlag = true
      return
    }
    if (!this.password || this.password.length !== 8) {
      this.passwordErrorFlag = true
      return
    }
    if (this.confirmPassword !== this.password) {
      this.confirmPasswordErrorFlag = true
      return
    }
    let httpRequest = http.createHttp();
    // 准备要提交的数据体
    interface PostData  {
      mobilePhone: string,
      password: string
    }
    let postData: PostData = {
      mobilePhone: this.account,
      password: this.password
    }
    httpRequest.request(
      "http://192.168.20.70:8080/quiz-community/public/v1.0/users/register",
      {
        method: http.RequestMethod.POST,
        header: { 'Content-Type': 'application/json' },
        extraData: postData
      },
      (err, data) => {
        console.log('response', '/users/register')
        console.log(JSON.stringify(data.result))
        if (data.responseCode == 200) {
          const resData = (typeof data.result == 'string' ? JSON.parse(data.result) : data.result) as HttpResponseResult<null>
          if (resData.code == 200) {
            PromptActionClass.openDialog()
          } else {
            promptAction.showToast({ message: resData.msg })
          }
        } else {
          promptAction.showToast({ message: err.message })
        }
      }
    )
  }
  build() {
    Column({space: 10}){
      Text('您好,')
        .width('100%')
        .fontSize(22)
        .fontWeight(800)
        .fontColor(Color.White)
        .onClick(() => {
          PromptActionClass.openDialog()
        })
      Text('欢迎使用学聘同行')
        .width('100%')
        .fontSize(22)
        .fontWeight(800)
        .fontColor(Color.White)
      Column({ space: 20 }) {
        Text('账号注册')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
          .fontWeight(800)
        Row() {
          Image($r('app.media.icon_username'))
            .width(16)
            .height(16)
          TextInput({ placeholder: '请输入账号', text: $$this.account })
            .type(InputType.USER_NAME)
            .backgroundColor('#f3f7fe')
            .onChange(() => {
              this.accountErrorFlag = false
            })
        }
        .width('100%')
        .backgroundColor('#f3f7fe')
        .border(this.accountErrorFlag ? { width: 1, color: Color.Red } : { width: 'none' })
        .borderRadius(10)
        .padding({ left: 10, right: 10 })
        if(this.accountErrorFlag) {
          Text('账号不能空')
            .width('100%')
            .fontSize(12)
            .fontColor(Color.Red)
            .margin({ top: -16, bottom: -16, left: 20 })
        }
        Row() {
          Image($r('app.media.icon_password'))
            .width(16)
            .height(16)
          TextInput({ placeholder: '请输入8位密码', text: $$this.password })
            .type(InputType.Password)
            .backgroundColor('#f3f7fe')
            .onChange(() =>{
              this.passwordErrorFlag = false
            })
        }
        .width('100%')
        .backgroundColor('#f3f7fe')
        .border(this.passwordErrorFlag ? { width: 1, color: Color.Red } : { width: 'none' })
        .borderRadius(10)
        .padding({ left: 10, right: 10 })
        if(this.passwordErrorFlag) {
          Text('请输入8位数的密码')
            .width('100%')
            .fontSize(12)
            .fontColor(Color.Red)
            .margin({ top: -16, bottom: -16, left: 20 })
        }
        Row() {
          Image($r('app.media.icon_password'))
            .width(16)
            .height(16)
          TextInput({ placeholder: '请再次输入密码', text: $$this.confirmPassword })
            .type(InputType.Password)
            .backgroundColor('#f3f7fe')
            .onChange(() =>{
              this.confirmPasswordErrorFlag = false
            })
        }
        .width('100%')
        .backgroundColor('#f3f7fe')
        .border(this.confirmPasswordErrorFlag ? { width: 1, color: Color.Red } : { width: 'none' })
        .borderRadius(10)
        .padding({ left: 10, right: 10 })
        if(this.confirmPasswordErrorFlag) {
          Text('两次密码输入不一致')
            .width('100%')
            .fontSize(12)
            .fontColor(Color.Red)
            .margin({ top: -16, bottom: -16, left: 20 })
        }
        Button('注 册')
          .type(ButtonType.Normal)
          .width('100%')
          .borderRadius(10)
          .onClick(() => {
            this.submitRegister()
          })
      }
      .width('100%')
      .margin({ top: 20 })
      .padding({ top: 30, bottom: 30, left: 20, right: 20 })
      .borderRadius(6)
      .backgroundColor(Color.White)
      Row() {
        Text() {
          Span('已有账号?')
          Span('立即登录>')
            .fontColor('#1756f4')
            .decoration({ type: TextDecorationType.Underline, color: '#1756f4' })
            .onClick(() => {
              this.getUIContext().getRouter().pushUrl({
                url: 'pages/login/LoginPage'
              })
            })
        }
        .fontSize(14)
        .fontWeight(500)
        .margin({ top: 10 })
      }
    }
    .width('100%')
    .height('100%')
    .padding({ top: 100, left: 20, right: 20 })
    .backgroundImage($r('app.media.login_page_bg'))
    .backgroundImageSize({width: '100%',height:'100%'})
  }
}
entry/src/main/ets/pages/message/MessageDetailsPage.ets
New file
@@ -0,0 +1,131 @@
import { CommonConstantWX } from '../../common/CommonConstantWX'
import { WxMessageBase } from '../../data/WxMessageBase'
import { WxUserInfoBase } from '../../data/WxUserInfoBase'
import { TitleUtils } from '../../utils/TitleUtils'
/**
  * @ProjectName : HealthNS
   * @FileName : MessageDetailsPage
   * @UserName : 修梦
  * @Time : 2025/9/14 16:35
  * @Description : 文件描述
 */
@Entry
@Component
struct MessageDetailsPage {
  @State msgData: Array<WxMessageBase> = CommonConstantWX.mUserMessage
  @State mContent:string = ''
  private mScroller: Scroller = new Scroller()
  @State mUserInfo: WxUserInfoBase = new WxMessageBase()
  aboutToAppear(): void {
    let params = this.getUIContext().getRouter().getParams() as Record<string,WxUserInfoBase>
    this.mUserInfo = params.userInfo
  }
  build() {
    RelativeContainer(){
      TitleUtils({titleName:this.mUserInfo.name})
        .id('heard')
      List({space: 15,scroller:this.mScroller}){
        ForEach(this.msgData,(item: WxMessageBase) => {
         ListItem(){
           MessageDetailsItem({item: item,icon: this.mUserInfo.img})
         }
        })
      }
      .id('list')
      .alignRules({
        top:{anchor: 'heard',align: VerticalAlign.Bottom},
        bottom: {anchor: 'rowBottom',align: VerticalAlign.Top}
      })
      .padding(10)
      Row({space: 5}){
        Image($r('app.media.wx_voice_icon')).MessageImageSty()
        TextArea({text: this.mContent})
          .backgroundColor(Color.White)
          .borderRadius(5)
          .width('70%')
          .offset({y: 5})
          .onChange((value: string) => {
            this.mContent = value
          })
        Image($r('app.media.wx_smile_icon')).MessageImageSty()
        if (this.mContent.trim().length === 0 || !this.mContent){
          Image($r('app.media.wx_more_icon')).MessageImageSty()
        } else {
          Button(){
            Text('发送')
              .fontSize(15)
              .fontColor(Color.White)
          }
          .padding(5)
          //发送数据,添加到了聊天列表
          .onClick(() =>{
            this.msgData.push({
              id: 99,
              content: this.mContent,
              img: $r('app.media.avatar_icon1'),
              category: 1
            })
            this.mContent = ''
            //滚动到底部
            this.mScroller.scrollToIndex(this.msgData.length - 1)
          })
        }
      }
      .width('100%')
      .alignItems(VerticalAlign.Bottom)
      .padding(10)
      .backgroundColor("#ffe9e6e6")
      .id('rowBottom')
      .alignRules({
        bottom: {anchor: '__container__',align: VerticalAlign.Bottom}
      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#ccc')
  }
}
@Component
struct MessageDetailsItem {
  @Prop item: WxMessageBase
  @Prop icon: ResourceStr
  build() {
    if (this.item.category === 0){
      Row({space:5}){
        Image(this.icon)
          .width(40)
          .height(40)
        Text(this.item.content)
          .backgroundColor(Color.White)
          .padding(5)
      }
      .width('78%')
    } else {
      Row({space:5}){
       Row(){
         Text(this.item.content)
           .backgroundColor("#ff3cc12e")
           .padding(5)
       }
       .justifyContent(FlexAlign.End)
       .width('78%')
        Image($r('app.media.avatar_icon1'))
          .width(40)
          .height(40)
      }
      .width('100%')
      .justifyContent(FlexAlign.End)
    }
  }
}
@Extend(Image)
function MessageImageSty() {
  .width(25)
  .height(25)
  .objectFit(ImageFit.Fill)
}
entry/src/main/ets/pages/message/MessagePage.ets
New file
@@ -0,0 +1,94 @@
import { CommonConstantWX } from "../../common/CommonConstantWX"
import { WxUserInfoBase } from "../../data/WxUserInfoBase"
/*
 * Search({placeholder: '请输入您要搜索的内容',value: 'DeepSeek'})
        .width('90%')
        .backgroundColor(Color.White)
        .searchButton('搜索')
        .onChange((value: string) => {
          console.info(`输入的内容为:${value}`)
        })
        //提交方法
        .onSubmit((value:string) => {
          console.info(`提交的内容为:${value}`)
        })
 * */
@Entry
@Component
export struct MessagePage {
  @State userList:Array<WxUserInfoBase> = CommonConstantWX.mUser
  @State searchUser:Array<WxUserInfoBase> = []
  build() {
    Column({space: 10}) {
      Search({placeholder: '搜索'})
        .backgroundColor(Color.White)
        .width('90%')
        .borderRadius(5)
        .onChange((value: string) =>{
          //每次输入都要清空searchUser数组
          this.searchUser = []
          let searchCount = 0
          //进行遍历,在数组源头查找和value相关的数据
          for (let index = 0; index < this.userList.length; index++) {
            const element = this.userList[index];
            //判断每一条数据是否有和Value相关的内容
            if (element.name?.indexOf(value) != -1) {
              //找到相关的数据,添加到search数组当中,并且数据加一
              searchCount ++
              this.searchUser.push(element)
            }
          }
          //判断SearchCount是否为0
          if (searchCount === 0) {
            //表示查无此人
            this.searchUser.push({
              id: 999,
              name: '无此用户',
              img:$r('app.media.avatar_icon')
            })
          }
        })
      List({space: 20}){
        ForEach(this.searchUser.length === 0 ? this.userList: this.searchUser,(item: WxUserInfoBase) => {
          ListItem(){
            userInfoItem({item: item})
          }
        })
      }
      .height('90%')
      .padding({top:10})
      .margin({bottom: 30})
      .backgroundColor(Color.White)
      .divider({strokeWidth: 2,color: '#ccc'})
    }
    .height('100%')
    .width('100%')
    .backgroundColor('#ccc')
  }
}
@Component
struct userInfoItem {
  @Prop item: WxUserInfoBase
  build() {
    Row({space: 15}){
      Image(this.item.img)
        .width(40)
        .height(40)
        .objectFit(ImageFit.Cover)
      Text(this.item.name)
        .fontSize(20)
    }
    .onClick(() => {
      this.getUIContext().getRouter().pushUrl({
        url: 'pages/message/MessageDetailsPage',
        params:{
          userInfo: this.item
        }
      })
    })
    .padding(10)
  }
}
entry/src/main/ets/pages/search/SearchPage.ets
New file
@@ -0,0 +1,82 @@
import { AxiosResponse } from "@ohos/axios"
import { NewsBase, NewsBaseData } from "../../data/NewsBase"
import { TextBase } from "../../data/TextBase"
import { NewsApi, TextApi } from "../../http/AxiosAPI"
@Entry
@Component
export struct SearchPage {
  //变量承载新闻数据
  @State newsBase: NewsBase = new NewsBase()
  @State TextData: TextBase = new TextBase()
  async aboutToAppear() {
    let newsData:NewsBase = await NewsApi()
    this.newsBase =  newsData
    let textData:TextBase = await TextApi()
    this.TextData = textData
    console.info('textData ' + JSON.stringify(textData))
  }
  build() {
    Column() {
      Column(){
          Text(JSON.stringify(this.TextData.text))
            .fontWeight(700)
            .fontSize(20)
      }
      .width('95%')
      .height(50)
      .borderRadius(20)
      .backgroundColor(Color.White)
      .justifyContent(FlexAlign.Center)
      .onClick(() => {
        console.info('TextData: ' + JSON.stringify(this.TextData.text))
      })
      List({space:15}){
        ForEach(this.newsBase.data,(item:NewsBaseData) =>{
          ListItem(){
           newsItem({newsData: item})
          }
        })
      }
      .alignListItem(ListItemAlign.Center)
    }
    .height('100%')
    .width('100%')
    .backgroundColor("#ffececec")
  }
}
//条目样式,自定义组件
@Component
struct newsItem {
  @Prop newsData: NewsBaseData
  build() {
    Row({space: 10}){
      Image(this.newsData.pic)
        //占位图
        .alt($r('app.media.app_logo_image'))
        .width(100)
        .height(70)
        .objectFit(ImageFit.Fill)
      Column({space: 10}){
        Text(this.newsData.title).SearchTextSty(18,700)
        Text(this.newsData.desc).SearchTextSty(15,400,Color.Black,2)
        Text(`热度: ${this.newsData.hot}`).SearchTextSty(10,400,Color.Red)
          .textAlign(TextAlign.End)
      }
      .width('65%')
    }
    .width('95%')
    .padding(10)
    .borderRadius(10)
    .backgroundColor(Color.White)
  }
}
@Extend(Text) function SearchTextSty(mSize:number,mWeight?:FontWeight,mColor?: ResourceColor,mLines?: number){
  .fontSize(mSize)
  .fontWeight(mWeight? mWeight : 400)
  .fontColor(mColor? mColor : Color.Black)
  .maxLines(mLines? mLines : 1)
  .textOverflow({overflow: TextOverflow.Ellipsis})
  .width('100%')
}
entry/src/main/ets/utils/PreferencesUtils.ets
New file
@@ -0,0 +1,92 @@
import { preferences } from "@kit.ArkData";
/**
 * @ProjectName : HealthNS
 * @FileName : PreferencesUtils
 * @UserName : 修梦
 * @Time : 2025/9/14 09:25
 * @Description : 本地持久化存储
 */
class PreferencesUtils {
  //创建PS本地存储的集合数据,可以支持多个存储存在
  prefMap: Map<string,preferences.Preferences> = new Map();
  //初始化方法 同步(类似于排队,上个任务完成,才回到下一个任务,基本不能有耗时操作)
  //异步,开辟一个子线程,在子线程运行完毕,结果返回给主线程
  //提供异步线程语法糖 async/await
  //context: Context 当前那个页面要使用,name存储的名称
  async loadPreferences(context: Context,name:string){
    //为了保证正常进行初始化,推荐使用异常捕获,保证在异常情况下,APP不会崩溃
    try {
      //初始化无问题,执行正常逻辑
      //创建PS存储
      const pref = await preferences.getPreferences(context,name)
      //初始后的存储,存放近Map集合
      this.prefMap.set(name,pref)
      console.info(`初始化${name}存储完成`)
    } catch (e) {
      console.info(`初始化${name}失败,失败原因: ${e}}`)
    }
  }
  //添加数据 name: PS存储的名称,key:存储值的别名,value存储的具体值
  async putPreferences(name:string,key:string,value:preferences.ValueType){
    //先判断Map数据里有没有正常初始化(集合里存不存在当前PS存储)
    if (!this.prefMap.has(name)) {
      //! 非,表示改PS不存在
      console.info(`${name}存储不存在,请初始化`)
      return
    }
    try {
      //当PS存储存在,执行当下逻辑
      const pref = this.prefMap.get(name)
      //写入数据
      await pref?.put(key,value)
      //持久化,刷磁盘
      await pref?.flush()
      console.info(`${name}存储写入数据成功`)
    } catch (e){
      console.info(`${name}存储写入数据失败,失败原因: ${e}`)
    }
  }
  //读取数据
  async getPreferences(name: string,key: string,defaultValue: preferences.ValueType){
    //先判断Map数据里有没有正常初始化(集合里存不存在当前PS存储)
    if (!this.prefMap.has(name)) {
      //! 非,表示改PS不存在
      console.info(`读取${name}存储不存在,请初始化`)
      return
    }
    try {
      //获取Map里面存的存储
      const pref = this.prefMap.get(name)
      //开始读取数据
      const value = await pref?.get(key,defaultValue)
      console.info(`读取${name}存储成功,数据为:${JSON.stringify(value)}`)
      return value
    } catch (e){
      console.info(`读取${name}存储手失败,失败原因: ${e}}`)
      return e
    }
  }
  //删除数据
  async delPreferences(name:string,key:string){
    //先判断Map数据里有没有正常初始化(集合里存不存在当前PS存储)
    if (!this.prefMap.has(name)) {
      //! 非,表示改PS不存在
      console.info(`读取${name}存储不存在,无法删除`)
      return
    }
    try {
      //拿到存储
      const pref = this.prefMap.get(name)
      //开始删除数据
      await pref?.delete(key)
      //刷磁盘
      await pref?.flush()
      console.info(`删除${name}存储成功`)
    }catch (e) {
      console.info(`删除${name}存储失败,失败原因:${e}`)
    }
  }
}
//仿单利,整个项目只需要一个存储,减少线程消耗
export default new PreferencesUtils() as PreferencesUtils
entry/src/main/ets/utils/PromptActionClass.ets
New file
@@ -0,0 +1,63 @@
// PromptActionClass.ets
import { BusinessError } from '@kit.BasicServicesKit';
import { ComponentContent, promptAction, UIContext } from '@kit.ArkUI';
export class PromptActionClass {
  static ctx: UIContext;
  static contentNode: ComponentContent<Object>;
  static options: promptAction.BaseDialogOptions;
  static setContext(context: UIContext) {
    PromptActionClass.ctx = context;
  }
  static setContentNode(node: ComponentContent<Object>) {
    PromptActionClass.contentNode = node;
  }
  static setOptions(options: promptAction.BaseDialogOptions) {
    PromptActionClass.options = options;
  }
  static openDialog() {
    if (PromptActionClass.contentNode !== null) {
      PromptActionClass.ctx.getPromptAction().openCustomDialog(PromptActionClass.contentNode, PromptActionClass.options)
        .then(() => {
          console.info('OpenCustomDialog complete.');
        })
        .catch((error: BusinessError) => {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
        })
    }
  }
  static closeDialog() {
    if (PromptActionClass.contentNode !== null) {
      PromptActionClass.ctx.getPromptAction().closeCustomDialog(PromptActionClass.contentNode)
        .then(() => {
          console.info('CloseCustomDialog complete.');
        })
        .catch((error: BusinessError) => {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
        })
    }
  }
  static updateDialog(options: promptAction.BaseDialogOptions) {
    if (PromptActionClass.contentNode !== null) {
      PromptActionClass.ctx.getPromptAction().updateCustomDialog(PromptActionClass.contentNode, options)
        .then(() => {
          console.info('UpdateCustomDialog complete.');
        })
        .catch((error: BusinessError) => {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`UpdateCustomDialog args error code is ${code}, message is ${message}`);
        })
    }
  }
}
entry/src/main/module.json5
@@ -45,6 +45,11 @@
          }
        ],
      }
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}
entry/src/main/resources/base/element/string.json
@@ -2,15 +2,15 @@
  "string": [
    {
      "name": "module_desc",
      "value": "module description"
      "value": "学聘同行"
    },
    {
      "name": "EntryAbility_desc",
      "value": "description"
      "value": "学聘同行"
    },
    {
      "name": "EntryAbility_label",
      "value": "label"
      "value": "学聘同行"
    }
  ]
}
entry/src/main/resources/base/media/active_list_img.png
entry/src/main/resources/base/media/classification_AI.png
entry/src/main/resources/base/media/date_icon.png
entry/src/main/resources/base/media/icon_password.svg
New file
@@ -0,0 +1,3 @@
<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.625 19.5V15.7084C14.625 9.42558 19.7167 4.33337 26 4.33337C32.2833 4.33337 37.375 9.42287 37.375 15.7084V19.5H37.9172C41.2062 19.5 43.875 22.171 43.875 25.4584V41.7084C43.875 45.0001 41.2105 47.6667 37.9172 47.6667H14.0833C10.7938 47.6667 8.125 44.9958 8.125 41.7084V25.4584C8.125 22.1667 10.7895 19.5 14.0828 19.5H14.625ZM17.875 19.5H34.125V15.7084C34.125 11.218 30.4888 7.58337 26 7.58337C21.5118 7.58337 17.875 11.2207 17.875 15.7084V19.5ZM11.375 25.4584V41.7084C11.375 43.2012 12.5894 44.4167 14.0828 44.4167H37.9167C38.2725 44.4171 38.6248 44.3474 38.9536 44.2114C39.2824 44.0755 39.5811 43.876 39.8327 43.6244C40.0843 43.3728 40.2838 43.0741 40.4197 42.7453C40.5557 42.4165 40.6254 42.0642 40.625 41.7084V25.4584C40.625 23.9655 39.4106 22.75 37.9172 22.75H14.0833C13.7276 22.7496 13.3752 22.8194 13.0464 22.9553C12.7176 23.0913 12.4189 23.2908 12.1673 23.5423C11.9157 23.7939 11.7162 24.0926 11.5803 24.4214C11.4443 24.7502 11.3746 25.1026 11.375 25.4584Z" fill="#1756F4"/>
</svg>
entry/src/main/resources/base/media/icon_username.svg
New file
@@ -0,0 +1,3 @@
<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M30.7509 49.4094H21.4523C12.4091 49.4094 5.26379 49.4094 5.26379 44.4411V43.5382C5.26379 34.7738 12.5249 27.6427 21.4523 27.6427H30.7509C39.6712 27.6427 46.9323 34.7738 46.9323 43.5382V44.4434C46.93 49.4094 39.4325 49.4094 30.7509 49.4094ZM21.4523 31.1882C14.482 31.1882 8.80925 36.7285 8.80925 43.5382V44.4434C8.80925 45.8663 17.3467 45.8663 21.4523 45.8663H30.7509C33.8756 45.8663 43.3869 45.8663 43.3869 44.4434V43.5382C43.3869 36.7285 37.7189 31.1882 30.7509 31.1882H21.4523ZM25.6312 27.5434C18.5025 27.5434 12.7022 21.8423 12.7022 14.8389C12.7022 7.83543 18.5025 2.13434 25.6312 2.13434C32.76 2.13434 38.5603 7.83307 38.5603 14.8389C38.5603 21.8447 32.76 27.5434 25.6312 27.5434ZM16.25 14.8389C16.25 19.8876 20.4596 23.998 25.6336 23.998C30.8076 23.998 35.0172 19.89 35.0172 14.8389C35.0172 9.78779 30.8076 5.67979 25.6336 5.67979C20.4596 5.67979 16.25 9.79016 16.25 14.8389Z" fill="#1756F4"/>
</svg>
entry/src/main/resources/base/media/image1.png
entry/src/main/resources/base/media/login_page_bg.png
entry/src/main/resources/base/media/logo.png
entry/src/main/resources/base/media/signIn_page_check.png
entry/src/main/resources/base/media/startIcon.png

entry/src/main/resources/base/media/start_page_bg.png
entry/src/main/resources/base/media/tabs_account_icon.png
entry/src/main/resources/base/media/tabs_account_icon_active.png
entry/src/main/resources/base/media/tabs_home_icon.png
entry/src/main/resources/base/media/tabs_home_icon_active.png
entry/src/main/resources/base/media/tabs_message_icon.png
entry/src/main/resources/base/media/tabs_message_icon_active.png
entry/src/main/resources/base/media/tabs_search_platform_icon.png
entry/src/main/resources/base/media/tabs_search_platform_icon_active.png
entry/src/main/resources/base/media/title_decoration_icon.png
entry/src/main/resources/base/profile/main_pages.json
@@ -1,5 +1,9 @@
{
  "src": [
    "pages/Index"
    "pages/Index",
    "pages/StartPage",
    "pages/login/LoginPage",
    "pages/login/SignInPage",
    "pages/MainPage"
  ]
}
oh-package-lock.json5
@@ -1,6 +1,7 @@
{
  "meta": {
    "stableOrder": true
    "stableOrder": true,
    "enableUnifiedLockfile": false
  },
  "lockfileVersion": 3,
  "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",