从零开始纯血鸿蒙天气预报-MVVM

139 阅读1分钟

易得天气

项目采用MVVM模式,关于MVVM模式网上有许多讲解,可以自行去搜索

网络请求封装:

export type NetParamType = string | number | boolean

export default class NetUtils {
  private axiosInstance: AxiosInstance = axios.create({
    // 设置超时时间
    timeout: 15000,
    readTimeout: 15000,
    connectTimeout: 15000,
    headers: {
      'Content-Type': 'application/json;charset=utf-8',
      'Content-Language': 'zh_CN'
    },
  });

  constructor() {
    this.axiosInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
      return config
    })
    this.axiosInstance.interceptors.response.use((response: AxiosResponse) => {
      return response
    })
  }

  private axiosGet<T>(url: string, axiosConfig: AxiosRequestConfig): Promise<T> {
    return this.axiosInstance.get<T, AxiosResponse<T>, null>(url, axiosConfig).then((response: AxiosResponse<T>) => {
      const data = response.data
      if (!data) {
        Promise.reject(new Error('response data is null'))
      }
      return data
    }).catch((error: Error) => {
      return Promise.reject(error)
    })
  }

  netGetT<T>(url: string, params?: Map<string, NetParamType>,
    headers?: Map<string, string>): Promise<T> {
    // 请求参数
    const axiosParams = new Map<string, NetParamType>()
    if (params && params.size > 0) {
      params.forEach((value, key, _) => {
        axiosParams[key] = value
      })
    }
    // 请求头
    const axiosHeaders = new AxiosHeaders()
    if (headers && headers.size > 0) {
      headers.forEach((value, key, _) => {
        axiosHeaders.set(key, value)
      })
    }
    const axiosRequestConfig: AxiosRequestConfig = {
      headers: axiosHeaders,
      params: axiosParams
    }
    return this.axiosGet<T>(url, axiosRequestConfig)
  }

  netGet<T>(url: string, params?: Map<string, NetParamType>,
    headers?: Map<string, string>): Promise<ResultData<T>> {
    return this.netGetT<ResultData<T>>(url, params, headers)
  }
}

export const netUtils = new NetUtils()

页面状态:

export class ViewState {
  // 加载中
  static readonly VIEW_STATE_LOADING = 'view_state_loading'
  // 成功
  static readonly VIEW_STATE_SUCCESS = 'view_state_success'
  // 错误
  static readonly VIEW_STATE_ERROR = 'view_state_error'
}

ViewModel基类:

@ObservedV2
export default class BaseViewModel {
  private _isDestroy = false
  @Trace viewState = ViewState.VIEW_STATE_LOADING
  @Trace errorMessage = ''

  constructor() {
    this._isDestroy = false
  }

  setViewState(viewState: string) {
    if (this.viewState != viewState) {
      this.viewState = viewState
    }
  }

  setErrorMessage(message: string) {
    this.errorMessage = message
  }

  isSuccess(): boolean {
    return ViewState.VIEW_STATE_SUCCESS == this.viewState
  }

  isLoading(): boolean {
    return ViewState.VIEW_STATE_LOADING == this.viewState
  }

  isError(): boolean {
    return ViewState.VIEW_STATE_ERROR == this.viewState
  }

  get isDestroy(): boolean {
    return this._isDestroy;
  }

  destroy(): void {
    this._isDestroy = true;
  }
}

首页面的ViewModel:

@ObservedV2
export default class WeatherMainViewModel extends BaseViewModel {
  @Trace weatherData = ''
  private model = new WeatherMainModel()

  async obtainWeatherData(): Promise<void> {
    this.setViewState(ViewState.VIEW_STATE_LOADING)
    this.model.obtainWeatherData()
      .then((weatherData: WeatherData) => {
        this.weatherData = JSON.stringify(weatherData)
        this.setViewState(ViewState.VIEW_STATE_SUCCESS)
      })
      .catch((e:  Error) => {
        this.setErrorMessage(e.message)
        this.setViewState(ViewState.VIEW_STATE_ERROR)
      })
  }
}

首页面Model层:

export default class WeatherMainModel {
  async obtainWeatherData(): Promise<WeatherData> {
    return netUtils.netGetT<WeatherData>(Api.WEATHER_API, new Map([
      ['citykey', 101210113],
    ]))
  }
}

首页写法:

@Route({ name: RouterConstants.WEATHER_MAIN_PAGE })
@ComponentV2
export struct WeatherMainPage {
  @Local weatherMainVM: WeatherMainViewModel = new WeatherMainViewModel()

  async aboutToAppear() {
    console.log('WeatherMainPage aboutToAppear')
    setTimeout(() => {
      this.weatherMainVM.obtainWeatherData()
    }, 2000)
  }

  build() {
    NavDestination() {
      Column() {
        Text(this.getText())
          .fontColor(Color.Black)
          .fontSize(14)
          .height('100%')
      }
      .width('100%')
      .height('100%')
    }
    .hideTitleBar(true)
    .height('100%')
    .width('100%')
    .onBackPressed(() => {
      console.log('WeatherMainPage onBackPressed');
      AppUtil.exit()
      return true
    })
  }

  getText(): string {
    if (this.weatherMainVM.isLoading()) {
      return '天气数据加载中...'
    } else if (this.weatherMainVM.isError()) {
      return this.weatherMainVM.errorMessage
    } else {
      return this.weatherMainVM.weatherData
    }
  }
}

效果图:

2025-02-12 17.23.23.gif