├── .gitignore ├── AppScope ├── app.json5 └── resources │ └── base │ ├── element │ └── string.json │ └── media │ └── app_icon.png ├── README.md ├── entry ├── .gitignore ├── build-profile.json5 ├── hvigorfile.ts ├── oh-package.json5 ├── patch.json └── src │ ├── main │ ├── ets │ │ ├── api │ │ │ └── Api.ets │ │ ├── components │ │ │ ├── AreaPicker.ets │ │ │ ├── Cart.ets │ │ │ ├── Category.ets │ │ │ ├── Home.ets │ │ │ ├── My.ets │ │ │ ├── NumberInput.ets │ │ │ ├── OrderList.ets │ │ │ └── ProductListItem.ets │ │ ├── configurations │ │ │ └── AppConst.ets │ │ ├── datamodels │ │ │ ├── AddAddrRequestModel.ets │ │ │ ├── AddCartReqModel.ets │ │ │ ├── AreaDataModel.ets │ │ │ ├── CartDataModel.ets │ │ │ ├── CategoryDataModel.ets │ │ │ ├── ConfirmOrderDataModel.ets │ │ │ ├── ConfirmOrderReqModel.ets │ │ │ ├── LoginDataModel.ets │ │ │ ├── LoginReqModel.ets │ │ │ ├── MyOrderListDataModel.ets │ │ │ ├── MyOrderListItemDataModel.ets │ │ │ ├── MyOrderListItemDtoDataModel.ets │ │ │ ├── NoticeDataModel.ets │ │ │ ├── OrderCountDataModel.ets │ │ │ ├── OrderTabViewModel.ets │ │ │ ├── ProductDataModel.ets │ │ │ ├── ResData.ets │ │ │ ├── ShopCartItemDataModel.ets │ │ │ ├── ShopCartItemDiscountDataModel.ets │ │ │ ├── ShopCartOrderDataModel.ets │ │ │ ├── SkuDataModel.ets │ │ │ ├── SubmitOrderDataModel.ets │ │ │ ├── SubmitOrderItemDataModel.ets │ │ │ ├── SwiperDataModel.ets │ │ │ ├── TotalPayDataModel.ets │ │ │ ├── TransfeeDataModel.ets │ │ │ ├── TransportDataModel.ets │ │ │ └── UserAddrDataModel.ets │ │ ├── entryability │ │ │ └── EntryAbility.ts │ │ ├── pages │ │ │ ├── Addr.ets │ │ │ ├── ConfirmOrder.ets │ │ │ ├── EditAddr.ets │ │ │ ├── Index.ets │ │ │ ├── Login.ets │ │ │ ├── Order.ets │ │ │ ├── PaySuccess.ets │ │ │ ├── ProductDetail.ets │ │ │ ├── ProductList.ets │ │ │ └── SearchProduct.ets │ │ ├── utils │ │ │ ├── CryptoJSUtil.ets │ │ │ └── PreferencesUtil.ts │ │ └── viewmodels │ │ │ ├── Brand.ets │ │ │ └── TabBarViewModel.ets │ ├── module.json5 │ └── resources │ │ ├── base │ │ ├── element │ │ │ ├── color.json │ │ │ └── string.json │ │ ├── media │ │ │ ├── addr.png │ │ │ ├── basket.png │ │ │ ├── basket_sel.png │ │ │ ├── bg1.png │ │ │ ├── car.png │ │ │ ├── car_new.png │ │ │ ├── category.png │ │ │ ├── category_sel.png │ │ │ ├── clear_his.png │ │ │ ├── coupon_ot.png │ │ │ ├── coupon_used.png │ │ │ ├── delive_dot.png │ │ │ ├── delivery_car.png │ │ │ ├── dot.png │ │ │ ├── empty_cash.png │ │ │ ├── everydaySale.png │ │ │ ├── getCoupon.png │ │ │ ├── head04.png │ │ │ ├── homepage.png │ │ │ ├── homepage_sel.png │ │ │ ├── horn.png │ │ │ ├── icon.png │ │ │ ├── logo.png │ │ │ ├── menu_01.png │ │ │ ├── menu_02.png │ │ │ ├── menu_03.png │ │ │ ├── menu_04.png │ │ │ ├── more.png │ │ │ ├── myAddr.png │ │ │ ├── myCoupon.png │ │ │ ├── newProd.png │ │ │ ├── neweveryday.png │ │ │ ├── newprods.png │ │ │ ├── plus_sign.png │ │ │ ├── prod_col.png │ │ │ ├── prod_col_red.png │ │ │ ├── promotion.png │ │ │ ├── revise.png │ │ │ ├── search.png │ │ │ ├── search_01.png │ │ │ ├── search_col.png │ │ │ ├── search_col2.png │ │ │ ├── star_empty.png │ │ │ ├── star_red.png │ │ │ ├── timePrice.png │ │ │ ├── toComment.png │ │ │ ├── toDelivery.png │ │ │ ├── toPay.png │ │ │ ├── toTake.png │ │ │ ├── tuiguang01.png │ │ │ ├── tuiguang02.png │ │ │ ├── tuiguang03.png │ │ │ ├── user.png │ │ │ └── user_sel.png │ │ └── profile │ │ │ └── main_pages.json │ │ ├── en_US │ │ └── element │ │ │ └── string.json │ │ └── zh_CN │ │ └── element │ │ └── string.json │ └── ohosTest │ ├── ets │ ├── test │ │ ├── Ability.test.ets │ │ └── List.test.ets │ ├── testability │ │ ├── TestAbility.ets │ │ └── pages │ │ │ └── Index.ets │ └── testrunner │ │ └── OpenHarmonyTestRunner.ts │ ├── module.json5 │ └── resources │ └── base │ ├── element │ ├── color.json │ └── string.json │ ├── media │ └── icon.png │ └── profile │ └── test_pages.json ├── hvigor └── hvigor-config.json5 ├── hvigorfile.ts ├── hvigorw ├── hvigorw.bat ├── local.properties ├── oh-package-lock.json5 ├── oh-package.json5 └── preview ├── 01.png ├── 02.png ├── 03.png ├── 04.png ├── 05.png ├── 06.png ├── 07.png └── 08png.png /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /oh_modules 3 | /local.properties 4 | /.idea 5 | **/build 6 | /.hvigor 7 | .cxx 8 | /.clangd 9 | /.clang-format 10 | /.clang-tidy 11 | **/.test -------------------------------------------------------------------------------- /AppScope/app.json5: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "bundleName": "com.example.mall4j", 4 | "vendor": "example", 5 | "versionCode": 1000000, 6 | "versionName": "1.0.0", 7 | "icon": "$media:app_icon", 8 | "label": "$string:app_name" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /AppScope/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "app_name", 5 | "value": "Mall4j" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AppScope/resources/base/media/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/AppScope/resources/base/media/app_icon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HarmonyOS4鸿蒙开发商城小应用 2 | 3 | > [https://gitee.com/gz-yami/mall4j](https://gitee.com/gz-yami/mall4j)的鸿蒙实现 4 | 5 | ## 说明 6 | 1. 仅已实现部分核心功能为目的,代码质量不高。 7 | 8 | ## 截图 9 | 10 | ![](./preview/01.png) 11 | 12 | ![](./preview/02.png) 13 | 14 | ![](./preview/03.png) 15 | 16 | ![](./preview/04.png) 17 | 18 | ![](./preview/05.png) 19 | 20 | ![](./preview/06.png) 21 | 22 | ![](./preview/07.png) 23 | 24 | ![](./preview/08.png) 25 | 26 | -------------------------------------------------------------------------------- /entry/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /oh_modules 3 | /.preview 4 | /build 5 | /.cxx 6 | /.test -------------------------------------------------------------------------------- /entry/build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | "apiType": 'stageMode', 3 | "buildOption": { 4 | }, 5 | "targets": [ 6 | { 7 | "name": "default", 8 | "runtimeOS": "HarmonyOS" 9 | }, 10 | { 11 | "name": "ohosTest", 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /entry/hvigorfile.ts: -------------------------------------------------------------------------------- 1 | // Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. 2 | export { hapTasks } from '@ohos/hvigor-ohos-plugin'; 3 | -------------------------------------------------------------------------------- /entry/oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | "license": "", 3 | "devDependencies": {}, 4 | "author": "", 5 | "name": "entry", 6 | "description": "Please describe the basic information.", 7 | "main": "", 8 | "version": "1.0.0", 9 | "dependencies": {} 10 | } 11 | -------------------------------------------------------------------------------- /entry/patch.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "bundleName": "com.example.mall4j", 4 | "patchVersionCode": 2000187, 5 | "versionCode": 1000000 6 | }, 7 | "module": { 8 | "name": "entry", 9 | "type": "hotreload" 10 | } 11 | } -------------------------------------------------------------------------------- /entry/src/main/ets/api/Api.ets: -------------------------------------------------------------------------------- 1 | import axios from '@ohos/axios' 2 | import Prompt from '@system.prompt' 3 | import AppConst from '../configurations/AppConst' 4 | import AddCartReqModel from '../datamodels/AddCartReqModel' 5 | import CartDataModel from '../datamodels/CartDataModel' 6 | import CategoryDataModel from '../datamodels/CategoryDataModel' 7 | import LoginDataModel from '../datamodels/LoginDataModel' 8 | import LoginReqModel from '../datamodels/LoginReqModel' 9 | import NoticeDataModel from '../datamodels/NoticeDataModel' 10 | import OrderCountDataModel from '../datamodels/OrderCountDataModel' 11 | import ProductDataModel from '../datamodels/ProductDataModel' 12 | import ResData from '../datamodels/ResData' 13 | import SwiperDataModel from '../datamodels/SwiperDataModel' 14 | import TotalPayDataModel from '../datamodels/TotalPayDataModel' 15 | import PreferencesUtil from '../utils/PreferencesUtil' 16 | import ConfirmOrderReqModel from '../datamodels/ConfirmOrderReqModel' 17 | import ConfirmOrderDataModel from '../datamodels/ConfirmOrderDataModel' 18 | import UserAddrDataModel from '../datamodels/UserAddrDataModel' 19 | import AreaDataModel from '../datamodels/AreaDataModel' 20 | import AddAddrRequestModel from '../datamodels/AddAddrRequestModel' 21 | import SubmitOrderDataModel from '../datamodels/SubmitOrderDataModel' 22 | import MyOrderListDataModel from '../datamodels/MyOrderListDataModel' 23 | 24 | class Api { 25 | 26 | // 统一网络请求 27 | protected async requestNetwork(options) { 28 | const accessToken = await PreferencesUtil.getPreference(AppConst.PREFERENCES_LOGIN_CODE) 29 | console.log('api获取到的accessToken:', accessToken) 30 | if (accessToken) { 31 | options.headers = { 32 | 'Authorization': accessToken, 33 | 'Content-Type': 'application/json' 34 | } 35 | } 36 | try { 37 | const axiosInstance = axios.create({ 38 | baseURL: "http://1.14.108.253:8086" 39 | }); 40 | axiosInstance.interceptors.request.use( 41 | (config) => { 42 | const { method, url, data, headers } = config; 43 | const logMessage = `Sending ${method} request to ${url}`; 44 | console.log('axios:', logMessage); 45 | 46 | // 可以选择性打印请求体和头部信息 47 | if (data) { 48 | console.debug('axios body Request body:', JSON.stringify(data)); 49 | } 50 | if (headers) { 51 | console.debug('axios headers:', JSON.stringify(headers)); 52 | } 53 | 54 | return config; 55 | }, 56 | (error) => { 57 | console.error('axios: Request error:', JSON.stringify(error)); 58 | return Promise.reject(error); 59 | }, 60 | ); 61 | const res = await axiosInstance.request(options) 62 | console.log('axios: res data,', JSON.stringify(res)) 63 | return Promise.resolve(res["data"] as ResData) 64 | } catch (err) { 65 | console.log('axios 网络请求失败', `网络请求失败: ${err.code} ${err.message}`) 66 | Prompt.showToast({ 67 | message: `网络请求失败: ${err.code} ${err.message}`, 68 | duration: 2000 69 | }) 70 | return Promise.reject(err) 71 | } 72 | } 73 | 74 | // get请求 75 | protected async get(url, params = {}) { 76 | return this.requestNetwork({ 77 | method: 'get', 78 | url: url, 79 | params 80 | }) 81 | } 82 | 83 | // post请求.如果服务器用的是delete接口,需要改成post接口. 84 | // 不知为何,axios发送delete请求会报404(只在请求参数是数组的情况下验证过) 85 | protected async post(url, data) { 86 | return this.requestNetwork({ 87 | method: 'post', 88 | url: url, 89 | data 90 | }) 91 | } 92 | 93 | // put请求 94 | protected async put(url, data) { 95 | return this.requestNetwork({ 96 | method: 'put', 97 | url: url, 98 | data 99 | }) 100 | } 101 | } 102 | 103 | // 首页的接口 104 | class HomeApi extends Api { 105 | // 获取轮播图 106 | async getSwiperData(): Promise { 107 | return (await this.get('/indexImgs')).data 108 | } 109 | 110 | // 获取通知消息 111 | async getNoticeData(): Promise { 112 | return (await this.get('/shop/notice/topNoticeList')).data 113 | } 114 | 115 | // 每日上新 116 | async getLastedProductData(): Promise { 117 | return (await this.get('/prod/lastedProdPage?current=1&size=6')).data 118 | } 119 | 120 | // 更多商品 121 | async getMoreProductData(): Promise { 122 | return (await this.get('/prod/moreBuyProdList?current=1&size=10')).data 123 | } 124 | } 125 | 126 | // 用户相关的接口 127 | class MyApi extends Api { 128 | 129 | // 登录 130 | async login(loginReqModel: LoginReqModel): Promise { 131 | return (await this.post('/login', loginReqModel)).data 132 | } 133 | 134 | // 各状态的订单数量 135 | async orders(): Promise { 136 | return (await this.get('/p/myOrder/orderCount')).data 137 | } 138 | } 139 | 140 | // 分类相关的接口 141 | class CategoryApi extends Api { 142 | async categories(parentId: number = 0): Promise { 143 | return (await this.get(`/category/categoryInfo?parentId=${parentId}`)).data 144 | } 145 | } 146 | 147 | // 商品相关的接口 148 | class ProductApi extends Api { 149 | async getCategoryProductData(categoryId: number): Promise { 150 | return (await this.get(`/prod/pageProd?categoryId=${categoryId}¤t=1&size=10&sort=0&isAllProdType=true`)).data 151 | } 152 | 153 | async getProductData(prodId: number): Promise { 154 | return (await this.get(`/prod/prodInfo?prodId=${prodId}`)).data 155 | } 156 | 157 | // 搜索商品 158 | async search(current: number, prodName: string, size: number): Promise { 159 | return (await this.get(`/search/searchProdPage?current=${current}&prodName=${prodName}&size=${size}&sort=0`)).data 160 | } 161 | 162 | } 163 | 164 | // 购物车 165 | class CartApi extends Api { 166 | 167 | // 添加购物车 168 | async add(addCarReqModel: AddCartReqModel): Promise { 169 | return (await this.post('/p/shopCart/changeItem', addCarReqModel)).data 170 | } 171 | 172 | // 购物车列表 173 | async list(): Promise { 174 | return (await this.post(`/p/shopCart/info`, {})).data 175 | } 176 | 177 | // 获取购物车商品数量 178 | async prodCount(): Promise { 179 | return (await this.get('/p/shopCart/prodCount')).data 180 | } 181 | 182 | 183 | // 计算购物车商品的价格 184 | async totalPay(basketIds: number[]): Promise { 185 | return (await this.post(`/p/shopCart/totalPay`, basketIds)).data 186 | } 187 | 188 | // 删除购物车中的商品 189 | async deleteCartProds(basketIds: number[]): Promise { 190 | return (await this.post(`/p/shopCart/deleteItem`, basketIds)).data 191 | } 192 | } 193 | 194 | // 确认订单接口 195 | class ConfirmOrderApi extends Api { 196 | 197 | // 确认订单 198 | async confirm(confirmOrderReqModel: ConfirmOrderReqModel): Promise { 199 | return (await this.post(`/p/order/confirm`, confirmOrderReqModel)).data 200 | } 201 | 202 | // 提交订单 203 | async submit(submitOrderDataModel: SubmitOrderDataModel): Promise> { 204 | return (await this.post(`/p/order/submit`, submitOrderDataModel)) 205 | } 206 | } 207 | 208 | // 我的订单相关接口 209 | class MyOrderApi extends Api { 210 | async list(status: number, page: number, current: number): Promise { 211 | return (await this.get(`/p/myOrder/myOrder?current=${current}&size=${page}&status=${status}`)).data 212 | } 213 | 214 | 215 | // 取消订单 216 | async cancel(orderNumber: string): Promise { 217 | return (await this.put(`/p/myOrder/cancel/${orderNumber}`, {})).data 218 | } 219 | 220 | // 模拟支付订单 221 | async pay(orderNumber: string): Promise { 222 | return (await this.post(`/p/order/normalPay`, {'orderNumbers': orderNumber})).data 223 | } 224 | 225 | 226 | } 227 | 228 | // 地址管理相关接口 229 | class AddrApi extends Api { 230 | async list(): Promise { 231 | return (await this.get('/p/address/list')).data 232 | } 233 | 234 | // 获取地区接口 235 | async areas(parentId: number): Promise { 236 | return (await this.get('/p/area/listByPid?pid=' + parentId)).data 237 | } 238 | 239 | // 增加收货地址 240 | async addAddr(addAddrRequestModel: AddAddrRequestModel): Promise> { 241 | return (await this.post>('/p/address/addAddr', addAddrRequestModel)).data 242 | } 243 | 244 | // 设为默认收货地址 245 | async setDefault(addrId: number): Promise> { 246 | return (await this.put>('/p/address/defaultAddr/' + addrId, {})).data 247 | } 248 | } 249 | 250 | 251 | const homeApi = new HomeApi() 252 | const myApi = new MyApi() 253 | const categoryApi = new CategoryApi() 254 | const productApi = new ProductApi() 255 | const cartApi = new CartApi() 256 | const confirmOrderApi = new ConfirmOrderApi() 257 | const addrApi = new AddrApi() 258 | const myOrderApi = new MyOrderApi() 259 | 260 | export { homeApi, myApi, categoryApi, productApi, cartApi, confirmOrderApi, addrApi, myOrderApi } -------------------------------------------------------------------------------- /entry/src/main/ets/components/AreaPicker.ets: -------------------------------------------------------------------------------- 1 | import { addrApi } from '../api/Api' 2 | import AreaDataModel from '../datamodels/AreaDataModel' 3 | 4 | @CustomDialog 5 | export struct AreaPicker { 6 | @Link selectProvinceId: number 7 | @Link selectProvinceName: string 8 | @State provinces: AreaDataModel[] = [] 9 | @State provincesStr: string[] = [] 10 | @Link selectCityId: number 11 | @Link selectCityName: string 12 | @State cities: AreaDataModel[] = [] 13 | @State citiesStr: string[] = [] 14 | @Link selectZoneId: number 15 | @Link selectZoneName: string 16 | @State zones: AreaDataModel[] = [] 17 | @State zonesStr: string[] = [] 18 | customDialogController: CustomDialogController 19 | 20 | async aboutToAppear() { 21 | // 加在所有的省份 22 | this.provinces = await addrApi.areas(0) 23 | // 处理所有省份的名称 24 | this.provincesStr = this.provinces.map(province => province.areaName) 25 | 26 | // 默认加载北京的城市 27 | this.cities = await addrApi.areas(this.selectCityId) 28 | this.citiesStr = this.cities.map(city => city.areaName) 29 | // 默认加载北京市辖区的区县 30 | this.zones = await addrApi.areas(this.selectZoneId) 31 | this.zonesStr = this.zones.map(zone => zone.areaName) 32 | } 33 | 34 | build() { 35 | Column() { 36 | Row() { 37 | Text('确定') 38 | .fontSize(14) 39 | .margin({top: 10, right: 10, bottom: 10}) 40 | .onClick(() => { 41 | this.customDialogController.close() 42 | }) 43 | }.width('100%') 44 | .justifyContent(FlexAlign.End) 45 | 46 | Row() { 47 | TextPicker({ 48 | range: this.provincesStr, selected: 0 49 | }) 50 | .onChange(async (provinceName: string, index: number) => { 51 | this.selectProvinceId = this.provinces.filter(province => province.areaName == provinceName)[0].areaId 52 | this.selectProvinceName = provinceName 53 | 54 | this.cities = await addrApi.areas(this.selectProvinceId) 55 | this.citiesStr = this.cities.map(city => city.areaName) 56 | 57 | }) 58 | TextPicker({ 59 | range: this.citiesStr, selected: 0 60 | }).onChange(async (cityName: string, index: number) => { 61 | this.selectCityId = this.cities.filter(city => city.areaName == cityName)[0].areaId 62 | this.selectCityName = cityName 63 | 64 | this.zones = await addrApi.areas(this.selectCityId) 65 | this.zonesStr = this.zones.map(zone => zone.areaName) 66 | }) 67 | 68 | TextPicker({ 69 | range: this.zonesStr, selected: 0 70 | }).onChange((zoneName: string, index: number) => { 71 | this.selectZoneId = this.zones.filter(zone => zone.areaName == zoneName)[0].areaId 72 | this.selectZoneName = zoneName 73 | }) 74 | } 75 | }.width('100%') 76 | } 77 | } -------------------------------------------------------------------------------- /entry/src/main/ets/components/Cart.ets: -------------------------------------------------------------------------------- 1 | import promptAction from '@ohos.promptAction' 2 | import router from '@ohos.router' 3 | import { cartApi } from '../api/Api' 4 | import CartDataModel from '../datamodels/CartDataModel' 5 | import ShopCartItemDataModel from '../datamodels/ShopCartItemDataModel' 6 | import ShopCartItemDiscount from '../datamodels/ShopCartItemDiscountDataModel' 7 | import TotalPayDataModel from '../datamodels/TotalPayDataModel' 8 | import NumberInput from './NumberInput' 9 | 10 | @Entry 11 | @Component 12 | export default struct Cart { 13 | @State cartDataModel: CartDataModel[] = [] 14 | @State checkboxGroupName: string = 'group' 15 | @State @Watch('onCheckedBasketIdsChanged') checkedBasketIds: number[] = [] 16 | @State totalPayDataModel: TotalPayDataModel = new TotalPayDataModel() 17 | 18 | async onCheckedBasketIdsChanged() { 19 | // 请求购物车内所有商品的总费用 20 | this.totalPayDataModel = await cartApi.totalPay(this.checkedBasketIds) 21 | } 22 | 23 | async aboutToAppear() { 24 | this.cartDataModel = await cartApi.list() 25 | } 26 | 27 | @Builder 28 | buildItemHeader(title: string) { 29 | Row() { 30 | Text(title) 31 | .fontSize(14) 32 | .fontWeight(600) 33 | }.width('100%') 34 | } 35 | 36 | build() { 37 | Stack({ alignContent: Alignment.Bottom }) { 38 | // 购物车商品列表 39 | Column() { 40 | Row() { 41 | Text('购物车') 42 | .fontSize(20) 43 | .fontWeight(600) 44 | }.width('100%') 45 | .justifyContent(FlexAlign.Center) 46 | .padding({ 47 | top: 10, 48 | bottom: 10 49 | }) 50 | .backgroundColor(Color.White) 51 | 52 | // 购物车列表数据 53 | Scroll() { 54 | List() { 55 | ForEach(this.cartDataModel, (cartDataModel) => { 56 | ListItemGroup({ header: this.buildItemHeader(cartDataModel.shopName) }) 57 | .padding({ bottom: 15 }) 58 | .backgroundColor(Color.White) 59 | 60 | ForEach(cartDataModel.shopCartItemDiscounts, (shopCartItemDiscount: ShopCartItemDiscount) => { 61 | ForEach(shopCartItemDiscount.shopCartItems, (shopCartItem: ShopCartItemDataModel, index: number) => { 62 | ListItem() { 63 | CartItem({ 64 | checkboxGroupName: this.checkboxGroupName, 65 | shopCartItemDataModel: shopCartItem, 66 | checkedBasketIds: $checkedBasketIds 67 | }) 68 | } 69 | .padding({ bottom: 20 }) 70 | .backgroundColor(Color.White) 71 | }) 72 | }) 73 | 74 | }) 75 | }.width('92%') 76 | .divider({ 77 | strokeWidth: 1, 78 | color: '#ffdddcdc', 79 | startMargin: 40 80 | }) 81 | }.width('100%') 82 | .backgroundColor(Color.White) 83 | 84 | }.width('100%') 85 | .height('100%') 86 | .justifyContent(FlexAlign.Start) 87 | 88 | // 操作栏 89 | Row({ space: 10 }) { 90 | Row() { 91 | CheckboxGroup({ group: this.checkboxGroupName }) 92 | .width(20) 93 | .margin({ left: 20 }) 94 | Text('全选') 95 | .fontSize(12) 96 | .margin({ left: 5 }) 97 | }.width(80) 98 | 99 | Text('删除') 100 | .fontSize(12) 101 | .fontColor(Color.Red) 102 | .onClick(() => { 103 | if (this.checkedBasketIds.length > 0) { 104 | promptAction.showDialog({ 105 | title: '确认', 106 | message: '确认要删除选中的商品吗', 107 | buttons: [ 108 | { 109 | text: '取消', 110 | color: '#666666', 111 | }, 112 | { 113 | text: '确定', 114 | color: '#ffef3131', 115 | }, 116 | ] 117 | }, async (err, data) => { 118 | if (err) { 119 | return 120 | } 121 | 122 | await cartApi.deleteCartProds(this.checkedBasketIds) 123 | // 清空保存的数组 124 | this.checkedBasketIds = [] 125 | // 刷新 126 | this.aboutToAppear() 127 | 128 | }) 129 | 130 | } else { 131 | promptAction.showToast({ 132 | message: "请先选择商品" 133 | }) 134 | } 135 | }) 136 | Column() { 137 | Text('合计:') 138 | .fontSize(14) 139 | Text(`¥ ${this.totalPayDataModel?.totalMoney || 0}`) 140 | .fontColor(Color.Red) 141 | } 142 | .layoutWeight(1) 143 | 144 | Button('结算') 145 | .type(ButtonType.Normal) 146 | .backgroundColor(Color.Red) 147 | .borderRadius(0) 148 | .fontColor(Color.White) 149 | .fontSize(14) 150 | .width(100) 151 | .height(50) 152 | .onClick(() => { 153 | if (this.checkedBasketIds.length > 0) { 154 | router.pushUrl({ 155 | url: "pages/ConfirmOrder", 156 | params: { 157 | "basketIds": this.checkedBasketIds 158 | } 159 | }) 160 | 161 | } else { 162 | promptAction.showToast({ 163 | message: "请先选择商品" 164 | }) 165 | } 166 | }) 167 | 168 | }.height(50) 169 | .backgroundColor(Color.White) 170 | .zIndex(2) 171 | } 172 | .width('100%') 173 | .height('100%') 174 | .backgroundColor('#f4f4f4') 175 | } 176 | } 177 | 178 | @Component 179 | struct CartItem { 180 | @Prop checkboxGroupName: string 181 | shopCartItemDataModel: ShopCartItemDataModel 182 | @State prodCount: number = 0 183 | @Link checkedBasketIds: number[] 184 | 185 | aboutToAppear() { 186 | this.prodCount = this.shopCartItemDataModel?.prodCount 187 | } 188 | 189 | build() { 190 | Row() { 191 | Checkbox({ group: this.checkboxGroupName, name: this.shopCartItemDataModel.basketId + '' }) 192 | .width(20) 193 | .onChange((value: boolean) => { 194 | if (value) { 195 | this.checkedBasketIds.push(this.shopCartItemDataModel.basketId) 196 | } else { 197 | const removeIndex = this.checkedBasketIds.indexOf(this.shopCartItemDataModel.basketId) 198 | if (removeIndex != -1) { 199 | this.checkedBasketIds.splice(removeIndex, 1) 200 | } 201 | } 202 | }) 203 | 204 | 205 | Image(this.shopCartItemDataModel.pic) 206 | .width(80) 207 | .margin({ left: 10 }) 208 | Column() { 209 | Row() { 210 | Text(this.shopCartItemDataModel.prodName) 211 | .fontSize(14) 212 | .textOverflow({ 213 | overflow: TextOverflow.Ellipsis 214 | }) 215 | .maxLines(2) 216 | } 217 | .width('100%') 218 | 219 | Row() { 220 | Text(this.shopCartItemDataModel.skuName) 221 | .fontSize(12) 222 | .fontColor('#999999') 223 | .backgroundColor('#f9f9f9') 224 | .borderRadius(5) 225 | }.width('100%') 226 | 227 | Row() { 228 | Text(`¥ ${this.shopCartItemDataModel.price}`) 229 | .fontSize(12) 230 | .fontColor(Color.Red) 231 | NumberInput({ value: $prodCount, min: 1, max: 9999 }) 232 | }.width('100%') 233 | .justifyContent(FlexAlign.SpaceBetween) 234 | }.layoutWeight(1) 235 | .margin({ left: 15 }) 236 | }.width('100%') 237 | .margin({ top: 10 }) 238 | } 239 | } -------------------------------------------------------------------------------- /entry/src/main/ets/components/Category.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import { categoryApi } from '../api/Api' 3 | import CategoryDataModel from '../datamodels/CategoryDataModel' 4 | 5 | @Entry 6 | @Component 7 | export default struct Category { 8 | @State categoryDataModels: CategoryDataModel[] = [] 9 | @State selectedIndex: number = 0 10 | 11 | async aboutToAppear() { 12 | this.categoryDataModels = await categoryApi.categories() 13 | } 14 | 15 | @Builder 16 | buildTabbar(categoryDataModel: CategoryDataModel, index: number) { 17 | Row() { 18 | Text('') 19 | .width(4) 20 | .height(16) 21 | .backgroundColor(this.selectedIndex == index ? Color.Red : '#f5f6f7') 22 | 23 | Text(categoryDataModel.categoryName).fontSize(16) 24 | .margin({left: 6, right: 10}) 25 | .fontColor(this.selectedIndex == index ? Color.Red : Color.Grey) 26 | 27 | }.backgroundColor(this.selectedIndex == index ? Color.White : '#f5f6f7') 28 | .height(50) 29 | } 30 | 31 | build() { 32 | Column() { 33 | Row() { 34 | Text('商品分类') 35 | .fontSize(20) 36 | .fontWeight(600) 37 | }.width('100%') 38 | .justifyContent(FlexAlign.Center) 39 | .margin({ 40 | top: 10, 41 | bottom: 10 42 | }) 43 | Divider() 44 | .width('100%') 45 | Tabs() { 46 | ForEach(this.categoryDataModels, (categoryDataModel: CategoryDataModel, index: number) => { 47 | TabContent() { 48 | MallTabContent({ categoryDataModel: categoryDataModel }) 49 | }.tabBar(this.buildTabbar(categoryDataModel, index)) 50 | .align(Alignment.Top) 51 | }) 52 | 53 | }.barPosition(BarPosition.Start) 54 | .vertical(true) 55 | .barWidth(80) 56 | .barMode(BarMode.Scrollable) 57 | .width('100%') 58 | .height('100%') 59 | .barHeight('100%') 60 | .backgroundColor('#f5f6f7') 61 | .onChange((index: number) => { 62 | this.selectedIndex = index 63 | }) 64 | } 65 | .width('100%') 66 | } 67 | } 68 | 69 | @Component 70 | struct MallTabContent { 71 | categoryDataModel: CategoryDataModel 72 | @State categories: CategoryDataModel[] = [] 73 | 74 | async aboutToAppear() { 75 | this.categories = await categoryApi.categories(this.categoryDataModel.categoryId) 76 | } 77 | 78 | @Builder 79 | buildCategory(category: CategoryDataModel) { 80 | Column() { 81 | Image(category.pic) 82 | .width('80%') 83 | .margin({top: 6}) 84 | Text(category.categoryName) 85 | .fontSize(14) 86 | }.width('100%') 87 | } 88 | 89 | build() { 90 | Scroll() { 91 | Column() { 92 | // banner图片 93 | Row() { 94 | Image(this.categoryDataModel.pic) 95 | .width('92%') 96 | }.width('100%') 97 | .justifyContent(FlexAlign.Center) 98 | .margin({top: 5}) 99 | 100 | 101 | if (this.categories.length == 0) { 102 | Row(){ 103 | Text('该分类下暂无子分类') 104 | .fontSize(14) 105 | .fontColor(Color.Grey) 106 | .margin({top: 4}) 107 | }.width('100%') 108 | .justifyContent(FlexAlign.Center) 109 | } else { 110 | Grid() { 111 | ForEach(this.categories, (category: CategoryDataModel, index: number) => { 112 | GridItem() { 113 | this.buildCategory(category) 114 | }.onClick(() => { 115 | router.pushUrl({ 116 | url: 'pages/ProductList', 117 | params: { 118 | category: this.categoryDataModel, 119 | index: index 120 | } 121 | }) 122 | }) 123 | }) 124 | }.rowsTemplate('1f 1fr 1fr') 125 | .columnsTemplate('1fr 1fr 1fr') 126 | .width('100%') 127 | .layoutWeight(1) 128 | .columnsGap(10) 129 | .rowsGap(10) 130 | } 131 | 132 | }.width('100%') 133 | }.width('100%') 134 | .height('100%') 135 | .backgroundColor(Color.White) 136 | .align(Alignment.Top) 137 | 138 | } 139 | } -------------------------------------------------------------------------------- /entry/src/main/ets/components/Home.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import { homeApi } from '../api/Api' 3 | import NoticeDataModel from '../datamodels/NoticeDataModel' 4 | import ProductDataModel from '../datamodels/ProductDataModel' 5 | import SwiperDataModel from '../datamodels/SwiperDataModel' 6 | import Brand from '../viewmodels/Brand' 7 | import ProductListItem from './ProductListItem' 8 | 9 | @Preview 10 | @Component 11 | export default struct Home { 12 | @State swiperDataModels: SwiperDataModel[] = [] 13 | @State noticeDataModels: NoticeDataModel[] = [] 14 | @State lastedProductData: ProductDataModel[] = [] 15 | @State moreProductData: ProductDataModel[] = [] 16 | private brands: Brand[] = [ 17 | new Brand($r('app.media.newProd'), '新品推荐'), 18 | new Brand($r('app.media.timePrice'), '新品推荐'), 19 | new Brand($r('app.media.neweveryday'), '每日疯抢'), 20 | new Brand($r('app.media.newprods'), '领券优惠'), 21 | ] 22 | 23 | @Builder 24 | buildBrand(brand: Brand) { 25 | Column() { 26 | Image(brand.image) 27 | .width(30) 28 | Text(brand.title) 29 | .fontSize(13) 30 | .margin({ top: 6 }) 31 | 32 | } 33 | } 34 | 35 | @Builder 36 | buildLastedProductItem(productDataModel: ProductDataModel) { 37 | Column() { 38 | Image(productDataModel.pic) 39 | .width(100) 40 | .height(140) 41 | .objectFit(ImageFit.Fill) 42 | .borderRadius(5) 43 | Text(productDataModel.prodName) 44 | .fontSize(12) 45 | .textOverflow({ overflow: TextOverflow.Ellipsis }) 46 | .maxLines(2) 47 | .height(24) 48 | .width(120) 49 | .padding({ 50 | right: 4 51 | }) 52 | .margin({top: 4}) 53 | Text(`¥ ${productDataModel.price}`) 54 | .fontSize(12) 55 | .fontWeight(500) 56 | .height(20) 57 | .width(120) 58 | }.width('100%') 59 | .height(220) 60 | .alignItems(HorizontalAlign.Start) 61 | .onClick(() => { 62 | // 跳转商品详情页 63 | router.pushUrl({ 64 | url: 'pages/ProductDetail', 65 | params: { 66 | prodId: productDataModel.prodId 67 | } 68 | }) 69 | }) 70 | } 71 | 72 | async aboutToAppear() { 73 | const swiperData = await homeApi.getSwiperData(); 74 | console.log("swiperData: ", swiperData) 75 | for (let item of swiperData) { 76 | if (!item.imgUrl.endsWith('.dpg')) { 77 | this.swiperDataModels.push(item) 78 | } 79 | } 80 | 81 | this.noticeDataModels = await homeApi.getNoticeData() 82 | console.log('aaa0' + JSON.stringify(this.noticeDataModels)) 83 | 84 | this.lastedProductData = (await homeApi.getLastedProductData())['records'] 85 | console.log("aaa1", JSON.stringify(this.lastedProductData)) 86 | 87 | this.moreProductData = (await homeApi.getMoreProductData())['records'] 88 | console.log('aaa2' + JSON.stringify(this.moreProductData)) 89 | } 90 | 91 | build() { 92 | Scroll() { 93 | Column() { 94 | // 搜索 95 | Row() { 96 | Row() { 97 | Image($r('app.media.search')) 98 | .width(20) 99 | Text('搜索').margin({ left: 5 }) 100 | .fontColor(Color.Grey) 101 | }.width('100%') 102 | .justifyContent(FlexAlign.Center) 103 | .onClick(() => { 104 | router.pushUrl({ 105 | url: 'pages/SearchProduct' 106 | }) 107 | }) 108 | } 109 | .backgroundColor('#f7f7f7') 110 | .width('96%') 111 | .height(40) 112 | .margin({ top: 5 }) 113 | 114 | // 轮播 115 | Row() { 116 | Swiper() { 117 | ForEach(this.swiperDataModels, (swiperDataModel: SwiperDataModel) => { 118 | Image(swiperDataModel.imgUrl) 119 | .width('100%') 120 | }) 121 | } 122 | .margin({ top: 5 }) 123 | .autoPlay(true) 124 | }.width('100%') 125 | 126 | 127 | // 品牌推荐 128 | Row() { 129 | ForEach(this.brands, (brand: Brand) => { 130 | this.buildBrand(brand) 131 | }) 132 | }.width('100%') 133 | .justifyContent(FlexAlign.SpaceAround) 134 | .margin({ top: 6 }) 135 | 136 | 137 | // 公告 138 | Row() { 139 | Image($r('app.media.horn')) 140 | .width(30) 141 | Swiper() { 142 | ForEach(this.noticeDataModels, (noticeDataModel: NoticeDataModel) => { 143 | Row() { 144 | Text(noticeDataModel.title) 145 | .fontSize(14) 146 | .fontWeight(600) 147 | .fontColor(Color.Black) 148 | .maxLines(1) 149 | .textOverflow({ overflow: TextOverflow.Ellipsis }) 150 | .margin({ right: 10 }) 151 | .layoutWeight(1) 152 | 153 | Image($r('app.media.more')) 154 | .width(16) 155 | } 156 | }) 157 | 158 | } 159 | .vertical(true) 160 | .autoPlay(true) 161 | .indicator(false) 162 | .height(30) 163 | .itemSpace(0) 164 | .layoutWeight(1) 165 | .margin(6) 166 | 167 | }.width('100%') 168 | .padding({ left: 10, right: 10 }) 169 | 170 | 171 | // 每日上新 172 | Row() { 173 | Text('每日上新') 174 | .fontSize(14) 175 | .fontColor(Color.White) 176 | .margin({ left: 10, top: 20 }) 177 | Button('查看更多') 178 | .fontSize(12) 179 | .fontColor(Color.White) 180 | .margin({ right: 10, top: 20 }) 181 | .backgroundColor('#65addf') 182 | .height(24) 183 | } 184 | .width('100%') 185 | .height(120) 186 | .backgroundColor('#3e99d7') 187 | .justifyContent(FlexAlign.SpaceBetween) 188 | .alignItems(VerticalAlign.Top) 189 | 190 | // 每日上新商品列表 191 | Column() { 192 | Grid() { 193 | ForEach(this.lastedProductData, (productDatModel: ProductDataModel) => { 194 | GridItem() { 195 | this.buildLastedProductItem(productDatModel) 196 | } 197 | }) 198 | } 199 | .rowsTemplate('1fr 1fr 1fr') 200 | .columnsTemplate('1fr 1fr 1fr') 201 | .columnsGap(0) 202 | .rowsGap(100) 203 | .width('94%') 204 | // .height(520) 205 | .offset({ y: -60 }) 206 | }.width('100%') 207 | .height(520) 208 | 209 | 210 | // 更多宝贝 211 | Column() { 212 | Text('更多宝贝') 213 | .fontSize(14) 214 | .margin({ left: 10, top: 20, bottom: 10 }) 215 | Divider() 216 | ForEach(this.moreProductData, (productDataModel: ProductDataModel) => { 217 | ProductListItem({productDataModel: productDataModel}) 218 | }) 219 | 220 | }.width('94%') 221 | .offset({ y: -150 }) 222 | .alignItems(HorizontalAlign.Start) 223 | 224 | } 225 | .width('100%') 226 | } 227 | .width('100%').height('100%').align(Alignment.TopStart) 228 | } 229 | } -------------------------------------------------------------------------------- /entry/src/main/ets/components/My.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import { myApi } from '../api/Api' 3 | import AppConst from '../configurations/AppConst' 4 | import OrderCountDataModel from '../datamodels/OrderCountDataModel' 5 | import PreferencesUtil from '../utils/PreferencesUtil' 6 | 7 | @Preview 8 | @Component 9 | export default struct My { 10 | @State isLogin: boolean = false 11 | @State orderCount: OrderCountDataModel = new OrderCountDataModel() 12 | 13 | async aboutToAppear() { 14 | // 从首选项中获取用户的登录状态 15 | const loginCode = await PreferencesUtil.getPreference(AppConst.PREFERENCES_LOGIN_CODE) 16 | console.log("loginCode", loginCode) 17 | if (loginCode) { 18 | // 已登录 19 | this.isLogin = true 20 | // 获取订单信息 21 | this.orderCount = await myApi.orders() 22 | console.log('获取订单信息数据: ', JSON.stringify(this.orderCount)) 23 | } 24 | } 25 | 26 | @Builder 27 | buildTag(title: string, content: string) { 28 | Column() { 29 | Text(content) 30 | .fontSize(14) 31 | .margin({ left: 10, top: 10 }) 32 | .fontColor(Color.Blue) 33 | Text(title) 34 | .fontSize(14) 35 | .margin({ left: 5, top: 5 }) 36 | }.width(100) 37 | } 38 | 39 | @Builder 40 | buildListItem(image: Resource, title: string) { 41 | Row() { 42 | Row() { 43 | Image(image) 44 | .width(30) 45 | Text(title) 46 | .fontSize(14) 47 | }.margin({ left: 20 }) 48 | 49 | Image($r('app.media.more')) 50 | .width(14).margin({ right: 20 }) 51 | .onClick(() => { 52 | if (title == '收货地址') { 53 | router.pushUrl({ 54 | url: 'pages/Addr' 55 | }) 56 | } 57 | }) 58 | }.width('100%') 59 | .margin({ top: 5, bottom: 5 }) 60 | .justifyContent(FlexAlign.SpaceBetween) 61 | } 62 | 63 | build() { 64 | Column() { 65 | Row() { 66 | Text('个人中心') 67 | .fontSize(20) 68 | .fontWeight(500) 69 | .margin({ left: 10, top: 10 }) 70 | }.justifyContent(FlexAlign.Center) 71 | .width('100%') 72 | .backgroundColor(Color.White) 73 | 74 | if (this.isLogin) { 75 | // 已登录状态 76 | Column() { 77 | Image($r('app.media.head04')) 78 | .width(100) 79 | .margin({ left: 20 }) 80 | Text('用户昵称') 81 | .fontSize(14) 82 | .margin({ left: 10, top: 10 }) 83 | }.width('100%') 84 | .width('100%') 85 | .padding({ top: 10, bottom: 20 }) 86 | .backgroundColor(Color.White) 87 | } else { 88 | // 未登录状态 89 | Row() { 90 | Image($r('app.media.head04')) 91 | .width(100) 92 | .margin({ left: 20 }) 93 | Column() { 94 | Text('未登录') 95 | .fontSize(14) 96 | .margin({ left: 10, top: 10 }) 97 | Text('点击登录账号') 98 | .margin({ left: 10, top: 10 }) 99 | .onClick(() => { 100 | router.pushUrl({ 101 | url: 'pages/Login' 102 | }) 103 | }) 104 | } 105 | .margin({ left: 10 }) 106 | .alignItems(HorizontalAlign.Start) 107 | }.justifyContent(FlexAlign.Start) 108 | .width('100%') 109 | .padding({ top: 10, bottom: 20 }) 110 | .backgroundColor(Color.White) 111 | } 112 | 113 | Blank() 114 | .width('100%') 115 | .height(10) 116 | .backgroundColor('#fff8f6f6') 117 | // 我的订单 118 | Column() { 119 | Row() { 120 | Text('我的订单') 121 | .fontSize(16) 122 | .fontWeight(500) 123 | .margin({ left: 20 }) 124 | 125 | Row() { 126 | Text('查看全部') 127 | .fontSize(14) 128 | .fontColor(Color.Grey) 129 | .onClick(() => { 130 | router.pushUrl({ 131 | url: 'pages/Order', 132 | params: { 133 | status: 0 134 | } 135 | }) 136 | }) 137 | Image($r('app.media.more')) 138 | .width(14) 139 | }.margin({ right: 20 }) 140 | } 141 | .width('100%') 142 | .justifyContent(FlexAlign.SpaceBetween) 143 | 144 | Row() { 145 | OrderItem({ 146 | isLogin: this.isLogin, 147 | title: '待支付', 148 | image: $r('app.media.toPay'), 149 | count: this.orderCount.unPay 150 | }).onClick(() => { 151 | router.pushUrl({ 152 | url: 'pages/Order', 153 | params: { 154 | status: 1 155 | } 156 | }) 157 | }) 158 | OrderItem({ 159 | isLogin: this.isLogin, 160 | title: '待发货', 161 | image: $r('app.media.toDelivery'), 162 | count: this.orderCount.payed 163 | }).onClick(() => { 164 | router.pushUrl({ 165 | url: 'pages/Order', 166 | params: { 167 | status: 2 168 | } 169 | }) 170 | }) 171 | OrderItem({ 172 | isLogin: this.isLogin, 173 | title: '待收货', 174 | image: $r('app.media.toTake'), 175 | count: this.orderCount.consignment 176 | }).onClick(() => { 177 | router.pushUrl({ 178 | url: 'pages/Order', 179 | params: { 180 | status: 3 181 | } 182 | }) 183 | }) 184 | OrderItem({ 185 | isLogin: this.isLogin, 186 | title: '已完成', 187 | image: $r('app.media.toComment'), 188 | count: this.orderCount.success 189 | }).onClick(() => { 190 | router.pushUrl({ 191 | url: 'pages/Order', 192 | params: { 193 | status: 5 194 | } 195 | }) 196 | }) 197 | 198 | } 199 | .width('100%') 200 | .justifyContent(FlexAlign.SpaceAround) 201 | 202 | }.justifyContent(FlexAlign.Start) 203 | .width('100%') 204 | .padding({ top: 20, bottom: 20 }) 205 | .backgroundColor(Color.White) 206 | 207 | Blank() 208 | .width('100%') 209 | .height(10) 210 | .backgroundColor('#fff8f6f6') 211 | Row() { 212 | this.buildTag('我的收藏', '--') 213 | this.buildTag('我的消息', '--') 214 | this.buildTag('我的足迹', '--') 215 | }.width('100%') 216 | .backgroundColor(Color.White) 217 | .padding({ bottom: 10 }) 218 | .justifyContent(FlexAlign.SpaceAround) 219 | 220 | Blank() 221 | .width('100%') 222 | .height(10) 223 | .backgroundColor('#fff8f6f6') 224 | // 列表 225 | List() { 226 | ListItem() { 227 | this.buildListItem($r('app.media.promotion'), '分销中心') 228 | } 229 | 230 | ListItem() { 231 | this.buildListItem($r('app.media.getCoupon'), '领券中心') 232 | } 233 | 234 | ListItem() { 235 | this.buildListItem($r('app.media.myCoupon'), '我的优惠券') 236 | } 237 | 238 | ListItem() { 239 | this.buildListItem($r('app.media.myAddr'), '收货地址') 240 | } 241 | }.width('100%') 242 | .backgroundColor(Color.White) 243 | .divider({ 244 | strokeWidth: 1, 245 | color: '#fff5f5f5' 246 | }) 247 | .height(160) 248 | 249 | if (this.isLogin) { 250 | // 登录的状态显示 退出登录 按钮 251 | Row() { 252 | Button('退出登录') 253 | .type(ButtonType.Normal) 254 | .width('60%') 255 | .backgroundColor(Color.Red) 256 | .fontColor(Color.White) 257 | .fontSize(14) 258 | .borderRadius(5) 259 | .onClick(async () => { 260 | // 从首选项中删除登录信息 261 | await PreferencesUtil.deletePreference(AppConst.PREFERENCES_LOGIN_CODE) 262 | // 跳回到首页 263 | router.replaceUrl({ 264 | url: 'pages/Index' 265 | }) 266 | }) 267 | }.width('100%') 268 | .justifyContent(FlexAlign.Center) 269 | .margin({ top: 10, bottom: 20 }) 270 | } 271 | 272 | }.width('100%') 273 | .backgroundColor('#f7f7f7') 274 | 275 | } 276 | } 277 | 278 | @Component 279 | struct OrderItem { 280 | @Prop isLogin: boolean 281 | @Prop title: string 282 | image: Resource 283 | @Prop count: number 284 | 285 | build() { 286 | Stack() { 287 | Image(this.image) 288 | .width(40) 289 | .height(40) 290 | .margin({ top: 20 }) 291 | if (this.isLogin) { 292 | Badge({ 293 | count: this.count, 294 | position: BadgePosition.RightTop, 295 | style: { badgeSize: 16, badgeColor: '#FA2A2D' } 296 | }) { 297 | Text(' ') 298 | }.width(30) 299 | .height(30) 300 | .offset({x: 13}) 301 | 302 | } 303 | Text(this.title) 304 | .fontSize(14) 305 | .margin({ top: 70 }) 306 | 307 | }.height(90) 308 | } 309 | } -------------------------------------------------------------------------------- /entry/src/main/ets/components/NumberInput.ets: -------------------------------------------------------------------------------- 1 | import formInfo from '@ohos.app.form.formInfo' 2 | 3 | @Preview 4 | @Component 5 | export default struct NumberInput { 6 | @Prop min: number 7 | @Prop max: number 8 | defaultWidth: number 9 | defaultHeight: number 10 | @Link value: number 11 | 12 | aboutToAppear() { 13 | if (!this.value) { 14 | this.value = 0 15 | } 16 | if (!this.min) { 17 | this.min = 0 18 | } 19 | 20 | if (!this.max) { 21 | this.max = 9999 22 | } 23 | if (!this.defaultWidth) { 24 | this.defaultWidth = 80 25 | } 26 | if (!this.defaultHeight) { 27 | this.defaultHeight = 30 28 | } 29 | 30 | } 31 | 32 | build() { 33 | Row() { 34 | Text('-') 35 | .width(30) 36 | .textAlign(TextAlign.Center) 37 | .fontColor(Color.Grey) 38 | .onClick(() => { 39 | this.value-- 40 | if (this.value < this.min) { 41 | this.value = this.min 42 | } 43 | }) 44 | Text(this.value + '') 45 | .width(20) 46 | .textAlign(TextAlign.Center) 47 | Text('+') 48 | .width(30) 49 | .textAlign(TextAlign.Center) 50 | .fontColor(Color.Grey) 51 | .onClick(() => { 52 | this.value++ 53 | if (this.value > this.max) { 54 | this.value = this.max 55 | } 56 | }) 57 | }.width(this.defaultWidth) 58 | .height(this.defaultHeight) 59 | .backgroundColor('#f7f7f7') 60 | } 61 | } -------------------------------------------------------------------------------- /entry/src/main/ets/components/OrderList.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import Prompt from '@system.prompt' 3 | import { myOrderApi } from '../api/Api' 4 | import MyOrderListItemDataModel from '../datamodels/MyOrderListItemDataModel' 5 | import MyOrderListItemDtoDataModel from '../datamodels/MyOrderListItemDtoDataModel' 6 | 7 | @Component 8 | export default struct OrderList { 9 | // 订单状态 10 | @Prop status: number 11 | // 分页: 页码 12 | @State current: number = 1 13 | // 分页: 每页数量 14 | @State page: number = 10 15 | @State data: MyOrderListItemDataModel[] = [] 16 | private statusTitle: string[] = [ 17 | '', '待支付', '待发货', '待收货', '', '已完成', '已取消' 18 | ] 19 | 20 | // 选中操作的订单编号 21 | @State selectedOrderNumber: string = '' 22 | @State @Watch('onShouldLoadData') shouldLoadData: boolean = false 23 | 24 | async onShouldLoadData() { 25 | if (this.shouldLoadData) { 26 | await this.requestData() 27 | this.shouldLoadData = false 28 | } 29 | } 30 | 31 | cancelOrderDialogController: CustomDialogController = new CustomDialogController({ 32 | builder: ConfirmCancel({ 33 | cancel: this.onCancel, 34 | confirm: this.onCancelOrderAccept, 35 | orderNumber: this.selectedOrderNumber, 36 | shouldLoadData: $shouldLoadData 37 | }), 38 | alignment: DialogAlignment.Center 39 | }) 40 | 41 | onCancel() { 42 | } 43 | 44 | async onCancelOrderAccept(orderNumber: string) { 45 | // 发起网络请求,刷新页面数据 46 | // 注意: 这里是无法访问到this.selecteedOrderNumber的,英文这个方法是在子组件中执行的,因为参数需要传递进来 47 | console.log('onCancelOrderAccept:', orderNumber) 48 | await myOrderApi.cancel(orderNumber) 49 | } 50 | 51 | async requestData() { 52 | // 发起网络请求 53 | const data = await myOrderApi.list(this.status, this.page, this.current) 54 | this.data = data.records 55 | console.log('订单数据:', JSON.stringify(this.data)) 56 | } 57 | 58 | async aboutToAppear() { 59 | this.requestData() 60 | } 61 | 62 | @Builder 63 | buildOrderListItem(myOrderListItemDataModel: MyOrderListItemDataModel, index: number) { 64 | Column({ space: 10 }) { 65 | Row() { 66 | Text(`订单编号: ${myOrderListItemDataModel.orderNumber}`) 67 | .fontColor(14) 68 | Text(`${this.statusTitle[myOrderListItemDataModel.status]}`) 69 | .fontSize(14) 70 | .fontColor(myOrderListItemDataModel.status < 2 ? Color.Red : Color.Black) 71 | }.width('100%') 72 | .justifyContent(FlexAlign.SpaceBetween) 73 | 74 | Row() { 75 | ForEach(myOrderListItemDataModel.orderItemDtos, (item: MyOrderListItemDtoDataModel, index: number) => { 76 | Image(item.pic) 77 | .width(80) 78 | }) 79 | } 80 | .width('100%') 81 | .backgroundColor('#fafafa') 82 | 83 | Row() { 84 | Text(`共${myOrderListItemDataModel.orderItemDtos.length}件商品 合计: ¥ ${myOrderListItemDataModel.actualTotal}`) 85 | .fontSize(14) 86 | }.width('100%') 87 | .justifyContent(FlexAlign.End) 88 | 89 | Divider() 90 | .opacity(0.3) 91 | .margin({ top: 6, bottom: 6 }) 92 | if (myOrderListItemDataModel.status == 1) { 93 | Row() { 94 | Button('取消订单') 95 | .type(ButtonType.Capsule) 96 | .backgroundColor(Color.White) 97 | .fontColor(Color.Black) 98 | .fontSize(12) 99 | .border({ 100 | color: Color.Gray, 101 | width: 1 102 | }) 103 | .padding({top: 0, bottom: 0, left: 8, right: 8}) 104 | .onClick(() => { 105 | this.selectedOrderNumber = myOrderListItemDataModel.orderNumber 106 | console.log('selectedOrderNumber', this.selectedOrderNumber) 107 | this.cancelOrderDialogController.open() 108 | }) 109 | 110 | Button('付款') 111 | .type(ButtonType.Capsule) 112 | .backgroundColor(Color.White) 113 | .fontColor(Color.Red) 114 | .fontSize(12) 115 | .border({ 116 | color: Color.Red, 117 | width: 1 118 | }) 119 | .padding({top: 0, bottom: 0, left: 8, right: 8}) 120 | .margin({ left: 5, right: 5 }) 121 | .onClick(async () => { 122 | Prompt.showToast({ 123 | message: '模拟支付' 124 | }) 125 | await myOrderApi.pay(myOrderListItemDataModel.orderNumber) 126 | router.replaceUrl({ 127 | url: 'pages/PaySuccess' 128 | }) 129 | }) 130 | }.width('100%') 131 | .justifyContent(FlexAlign.End) 132 | } 133 | 134 | }.width('100%') 135 | .padding(10) 136 | } 137 | 138 | build() { 139 | Column() { 140 | Scroll() { 141 | if (this.data.length > 0) { 142 | // 选单订单列表 143 | List() { 144 | ForEach(this.data, (myOrderListItemDataModel: MyOrderListItemDataModel, index: number) => { 145 | ListItem() { 146 | this.buildOrderListItem(myOrderListItemDataModel, index) 147 | }.width('100%') 148 | }) 149 | }.width('100%') 150 | 151 | } else { 152 | Row() { 153 | Text('还没有任何相关订单') 154 | .fontSize(16) 155 | .fontColor(Color.Black) 156 | 157 | }.width('100%') 158 | .height(100) 159 | .justifyContent(FlexAlign.Center) 160 | .alignItems(VerticalAlign.Center) 161 | 162 | } 163 | }.width('100%') 164 | 165 | 166 | }.width('100%') 167 | .height('100%') 168 | .justifyContent(FlexAlign.Start) 169 | 170 | } 171 | } 172 | 173 | // 确定取消订单对话框 174 | @CustomDialog 175 | struct ConfirmCancel { 176 | controller: CustomDialogController 177 | cancel: () => void 178 | confirm: (orderNumber: string) => void 179 | @Prop orderNumber: string 180 | @Link shouldLoadData: boolean 181 | 182 | build() { 183 | Column() { 184 | Text('确定取消订单?').fontSize(20).margin({ top: 10, bottom: 10 }) 185 | Flex({ justifyContent: FlexAlign.SpaceAround }) { 186 | Button('取消') 187 | .onClick(() => { 188 | this.controller.close() 189 | this.cancel() 190 | }).backgroundColor(0xffffff).fontColor(Color.Black) 191 | Button('确定') 192 | .onClick(() => { 193 | this.controller.close() 194 | this.confirm(this.orderNumber) 195 | this.shouldLoadData = true 196 | }).backgroundColor(0xffffff).fontColor(Color.Red) 197 | }.margin({ bottom: 10 }) 198 | } 199 | } 200 | } -------------------------------------------------------------------------------- /entry/src/main/ets/components/ProductListItem.ets: -------------------------------------------------------------------------------- 1 | 2 | import router from '@ohos.router' 3 | import ProductDataModel from '../datamodels/ProductDataModel' 4 | @Component 5 | export default struct ProductListItem { 6 | productDataModel: ProductDataModel 7 | build() { 8 | Row() { 9 | Image(this.productDataModel.pic) 10 | .width(80) 11 | .height(100) 12 | .objectFit(ImageFit.Fill) 13 | .borderRadius(5) 14 | 15 | Column() { 16 | Text(this.productDataModel.prodName) 17 | .fontWeight(500) 18 | .textOverflow({ overflow: TextOverflow.Ellipsis }) 19 | .maxLines(2) 20 | Text(this.productDataModel.brief) 21 | .fontSize(10) 22 | .fontColor(Color.Grey) 23 | Text(`¥ ${this.productDataModel.price}`) 24 | Row() { 25 | Image($r('app.media.basket_sel')) 26 | .width(16) 27 | .margin({ right: 10 }) 28 | }.width('100%') 29 | .justifyContent(FlexAlign.End) 30 | 31 | } 32 | .layoutWeight(1) 33 | .height(80) 34 | .margin({ left: 6 }) 35 | .alignItems(HorizontalAlign.Start) 36 | .justifyContent(FlexAlign.SpaceBetween) 37 | 38 | }.width('100%') 39 | .padding(10) 40 | .justifyContent(FlexAlign.Center) 41 | .height(120) 42 | .onClick(() => { 43 | router.pushUrl({ 44 | url: 'pages/ProductDetail', 45 | params: { 46 | prodId: this.productDataModel.prodId 47 | } 48 | }) 49 | }) 50 | } 51 | } -------------------------------------------------------------------------------- /entry/src/main/ets/configurations/AppConst.ets: -------------------------------------------------------------------------------- 1 | export default class AppConst { 2 | private constructor() { 3 | } 4 | 5 | // 登录状态 6 | static readonly PREFERENCES_LOGIN_CODE = "LOGIN_CODE" 7 | 8 | // 搜索历史 9 | static readonly PREFERENCES_SEARCH_HIS = "SEARCH_HIS" 10 | 11 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/AddAddrRequestModel.ets: -------------------------------------------------------------------------------- 1 | export default class AddAddrRequestModel { 2 | receiver: string 3 | mobile: string 4 | addr: string 5 | province: string 6 | provinceId: number 7 | city: string 8 | cityId: number 9 | areaId: number 10 | area: string 11 | userType: number 12 | addrId: number 13 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/AddCartReqModel.ets: -------------------------------------------------------------------------------- 1 | export default class AddCartReqModel { 2 | basketId: number 3 | count: number 4 | prodId: number 5 | shopId: number 6 | skuId: number 7 | 8 | constructor(basketId: number,count: number,prodId: number,shopId: number,skuId: number) { 9 | this.basketId = basketId 10 | this.count = count 11 | this.prodId = prodId 12 | this.shopId = shopId 13 | this.skuId = skuId 14 | } 15 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/AreaDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class AreaDataModel { 2 | areaId: number 3 | areaName: string 4 | parentId: number 5 | level: number 6 | areas: AreaDataModel[] 7 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/CartDataModel.ets: -------------------------------------------------------------------------------- 1 | import ShopCartItemDiscount from './ShopCartItemDiscountDataModel' 2 | 3 | export default class CartDataModel { 4 | shopId: number 5 | shopName: string 6 | shopCartItemDiscounts: ShopCartItemDiscount[] 7 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/CategoryDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class CategoryDataModel { 2 | categoryId: number 3 | parentId: number 4 | categoryName: string 5 | pic: string 6 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/ConfirmOrderDataModel.ets: -------------------------------------------------------------------------------- 1 | import ShopCartOrderDataModel from './ShopCartOrderDataModel' 2 | import UserAddrDataModel from './UserAddrDataModel' 3 | @Observed 4 | export default class ConfirmOrderDataModel { 5 | actualTotal: number 6 | total: number 7 | totalCount: number 8 | orderReduce: number 9 | userAddr: UserAddrDataModel 10 | shopCartOrders: ShopCartOrderDataModel[] 11 | coupons: number 12 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/ConfirmOrderReqModel.ets: -------------------------------------------------------------------------------- 1 | export default class ConfirmOrderReqModel { 2 | addrId: number 3 | basketIds: number[] 4 | couponIds: number[] 5 | userChangeCoupon: number 6 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/LoginDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class LoginDataModel { 2 | accessToken: string 3 | refreshToken: string 4 | expiresIn: number 5 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/LoginReqModel.ets: -------------------------------------------------------------------------------- 1 | export default class LoginReqModel { 2 | userName: string 3 | passWord: string 4 | 5 | constructor(userName: string, passWord: string) { 6 | this.userName = userName 7 | this.passWord = passWord 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/MyOrderListDataModel.ets: -------------------------------------------------------------------------------- 1 | import MyOrderListItemDataModel from './MyOrderListItemDataModel' 2 | export default class MyOrderListDataModel { 3 | records: MyOrderListItemDataModel[] 4 | total: number 5 | size: number 6 | current: number 7 | pages: number 8 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/MyOrderListItemDataModel.ets: -------------------------------------------------------------------------------- 1 | import MyOrderListItemDtoDataModel from './MyOrderListItemDtoDataModel' 2 | export default class MyOrderListItemDataModel { 3 | orderItemDtos: MyOrderListItemDtoDataModel[] 4 | orderNumber: string 5 | actualTotal: number 6 | status: number 7 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/MyOrderListItemDtoDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class MyOrderListItemDtoDataModel { 2 | pic: string 3 | prodName: string 4 | prodCount: number 5 | price: number 6 | skuName: string 7 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/NoticeDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class NoticeDataModel { 2 | id: number 3 | shopId: number 4 | title: string 5 | content: string 6 | publishTime: string 7 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/OrderCountDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class OrderCountDataModel { 2 | allCount: number = 0 3 | unPay: number = 0 4 | payed: number = 0 5 | consignment: number = 0 6 | confirm: number = 0 7 | success: number = 0 8 | close: number = 0 9 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/OrderTabViewModel.ets: -------------------------------------------------------------------------------- 1 | export default class OrderTabViewModel { 2 | title: string 3 | status: number 4 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/ProductDataModel.ets: -------------------------------------------------------------------------------- 1 | import SkuDataModel from './SkuDataModel' 2 | import TransfeeDataModel from './TransfeeDataModel' 3 | import TransportDataModel from './TransportDataModel' 4 | export default class ProductDataModel { 5 | shopId: number 6 | shopName: string 7 | prodId: number 8 | prodName: string 9 | price: number 10 | content: string 11 | oriPrice: number 12 | totalStocks: number 13 | brief: string 14 | pic: string 15 | imgs: string 16 | categoryId: number 17 | skuList: SkuDataModel[] 18 | transport: TransportDataModel[] 19 | transfeeFrees: string[] 20 | transfees: TransfeeDataModel[] 21 | } 22 | -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/ResData.ets: -------------------------------------------------------------------------------- 1 | class ResData { 2 | code: string 3 | msg: string 4 | data: T 5 | timestamp: string 6 | version: string 7 | sign:string 8 | success: boolean 9 | fail: boolean 10 | } 11 | export default ResData -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/ShopCartItemDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class ShopCartItemDataModel { 2 | prodName: string 3 | prodCount: number 4 | pic: string 5 | price: number 6 | productTotalAmount: number 7 | prodId: number 8 | skuId: number 9 | skuName: string 10 | basketId: number 11 | actualTotal: number 12 | discountId: number 13 | shareReduce: number 14 | discounts: number 15 | shopId: number 16 | shopName: string 17 | oriPrice: number 18 | distributionCardNo: number 19 | basketDate: string 20 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/ShopCartItemDiscountDataModel.ets: -------------------------------------------------------------------------------- 1 | import ShopCartItemDataModel from './ShopCartItemDataModel' 2 | 3 | export default class ShopCartItemDiscount { 4 | chooseDiscountItemDto: string 5 | shopCartItems: ShopCartItemDataModel[] 6 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/ShopCartOrderDataModel.ets: -------------------------------------------------------------------------------- 1 | import ShopCartItemDiscount from './ShopCartItemDiscountDataModel' 2 | export default class ShopCartOrderDataModel { 3 | shopId: number 4 | shopName: string 5 | actualTotal: number 6 | total: number 7 | totalCount: number 8 | transfee: number 9 | discountReduce: number 10 | couponReduce: number 11 | shopReduce: number 12 | remarks: string 13 | shopCartItemDiscounts: ShopCartItemDiscount[] 14 | coupons: number 15 | orderNumber: number 16 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/SkuDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class SkuDataModel { 2 | skuId:number 3 | price: number 4 | oriPrice:number 5 | stocksnumber 6 | skuName: string 7 | pic: string 8 | properties: string 9 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/SubmitOrderDataModel.ets: -------------------------------------------------------------------------------- 1 | import SubmitOrderItemDataModel from './SubmitOrderItemDataModel' 2 | export default class SubmitOrderDataModel { 3 | orderShopParam: SubmitOrderItemDataModel[] 4 | } 5 | 6 | -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/SubmitOrderItemDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class SubmitOrderItemDataModel { 2 | remarks: string 3 | shopId: number 4 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/SwiperDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class SwiperDataModel { 2 | imgUrl: string 3 | seq: number 4 | uploadTime: string 5 | type: number 6 | relation: number 7 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/TotalPayDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class TotalPayDataModel { 2 | totalMoney:number 3 | finalMoney:number 4 | subtractMoney:number 5 | count:number 6 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/TransfeeDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class TransfeeDataModel { 2 | transfeeId: number 3 | transportId:number 4 | continuousPiece: number 5 | firstPiece: number 6 | continuousFee: number 7 | firstFee: number 8 | cityList: string[] 9 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/TransportDataModel.ets: -------------------------------------------------------------------------------- 1 | import TransfeeDataModel from './TransfeeDataModel' 2 | export default class TransportDataModel { 3 | transportId: number 4 | transName: string 5 | createTime: string 6 | shopId: number 7 | chargeType: number 8 | isFreeFee: number 9 | hasFreeCondition: number 10 | transfeeFrees: [] 11 | transfees: TransfeeDataModel[] 12 | } -------------------------------------------------------------------------------- /entry/src/main/ets/datamodels/UserAddrDataModel.ets: -------------------------------------------------------------------------------- 1 | export default class UserAddrDataModel { 2 | addrId: number 3 | receiver: string 4 | province: string 5 | city: string 6 | area: string 7 | addr:string 8 | mobile: string 9 | commonAddr: number 10 | provinceId: number 11 | cityId: number 12 | areaId: number 13 | } -------------------------------------------------------------------------------- /entry/src/main/ets/entryability/EntryAbility.ts: -------------------------------------------------------------------------------- 1 | import UIAbility from '@ohos.app.ability.UIAbility'; 2 | import hilog from '@ohos.hilog'; 3 | import window from '@ohos.window'; 4 | import PreferencesUtil from '../utils/PreferencesUtil' 5 | 6 | export default class EntryAbility extends UIAbility { 7 | onCreate(want, launchParam) { 8 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 9 | } 10 | 11 | onDestroy() { 12 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); 13 | } 14 | 15 | async onWindowStageCreate(windowStage: window.WindowStage) { 16 | // Main window is created, set main page for this ability 17 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 18 | 19 | // 设置preferences 20 | console.log('mall4j entry: ', '开始设置首选项') 21 | await PreferencesUtil.setInstanceContext(this.context) 22 | 23 | windowStage.loadContent('pages/Index', (err, data) => { 24 | if (err.code) { 25 | hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 26 | return; 27 | } 28 | hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); 29 | }); 30 | } 31 | 32 | onWindowStageDestroy() { 33 | // Main window is destroyed, release UI related resources 34 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); 35 | } 36 | 37 | onForeground() { 38 | // Ability has brought to foreground 39 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); 40 | } 41 | 42 | onBackground() { 43 | // Ability has back to background 44 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /entry/src/main/ets/pages/Addr.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import { addrApi } from '../api/Api' 3 | import UserAddrDataModel from '../datamodels/UserAddrDataModel' 4 | 5 | @Entry 6 | @Component 7 | struct Addr { 8 | @State addrs: UserAddrDataModel[] = [] 9 | 10 | private backUrl: string = '' 11 | 12 | private selectedAddrId: number = 0 13 | private selectedAddr: UserAddrDataModel 14 | 15 | async aboutToAppear() { 16 | this.backUrl = router.getParams()['backUrl'] 17 | } 18 | 19 | async onPageShow() { 20 | this.addrs = await addrApi.list() 21 | for (let index = 0; index < this.addrs.length; index++) { 22 | const element = this.addrs[index]; 23 | if (element.commonAddr == 1) { 24 | this.selectedAddrId = element.addrId 25 | this.selectedAddr = element 26 | } 27 | } 28 | console.log('addr:', JSON.stringify(this.addrs)) 29 | } 30 | 31 | @Builder 32 | buildAddrItem(addr: UserAddrDataModel, index: number) { 33 | Column() { 34 | Row() { 35 | Column() { 36 | Text(addr.receiver + ' ' + addr.mobile) 37 | .fontSize(14) 38 | Text(addr.addr) 39 | .fontSize(14) 40 | .fontColor('#c3c3c3') 41 | .margin({ top: 4 }) 42 | }.layoutWeight(1) 43 | .alignItems(HorizontalAlign.Start) 44 | .padding({ left: 10, top: 10, bottom: 10 }) 45 | 46 | Image($r('app.media.revise')) 47 | .width(20) 48 | .margin({ right: 10 }) 49 | 50 | }.width('100%') 51 | .justifyContent(FlexAlign.SpaceBetween) 52 | 53 | Divider() 54 | .margin({ top: 4, bottom: 4 }) 55 | .opacity(0.6) 56 | 57 | Row() { 58 | Radio({ 59 | group: 'group', 60 | value: addr.addrId + '' 61 | }) 62 | .colorBlend(Color.Red) 63 | .checked(addr.commonAddr == 1) 64 | .onChange(async (isChecked: boolean) => { 65 | if (isChecked) { 66 | await addrApi.setDefault(addr.addrId) 67 | this.selectedAddrId = addr.addrId 68 | this.selectedAddr = addr 69 | } 70 | }) 71 | 72 | Text(' 设为默认地址') 73 | .fontSize(14) 74 | 75 | }.width('100%') 76 | 77 | }.width('100%') 78 | } 79 | 80 | build() { 81 | Stack({alignContent: Alignment.Bottom}) { 82 | Scroll() { 83 | Column() { 84 | Row() { 85 | Image($r('app.media.more')) 86 | .width(16) 87 | .rotate({ 88 | angle: 180 89 | }) 90 | .margin({ left: 10 }) 91 | .onClick(() => { 92 | router.back({ 93 | url: this.backUrl, 94 | params: { 95 | selectedAddrId: this.selectedAddrId, 96 | selectedAddr: this.selectedAddr 97 | } 98 | }) 99 | }) 100 | 101 | Row() { 102 | Text('收货地址') 103 | .fontSize(16) 104 | .fontWeight(600) 105 | }.layoutWeight(1) 106 | .justifyContent(FlexAlign.Center) 107 | .offset({ 108 | x: -10 109 | }) 110 | 111 | Blank() 112 | .width(20) 113 | }.width('100%') 114 | .height(50) 115 | .justifyContent(FlexAlign.SpaceAround) 116 | .backgroundColor(Color.White) 117 | 118 | 119 | Column() { 120 | List() { 121 | ForEach(this.addrs, (addr: UserAddrDataModel, index: number) => { 122 | ListItem() { 123 | this.buildAddrItem(addr, index) 124 | }.width('100%') 125 | }) 126 | }.width('98%') 127 | 128 | }.width('100%') 129 | .backgroundColor(Color.White) 130 | .margin({ top: 10 }) 131 | 132 | }.width('100%') 133 | .height('100%') 134 | .backgroundColor('#f4f4f4') 135 | 136 | }.width('100%') 137 | 138 | Row() { 139 | Button('新增收货地址') 140 | .type(ButtonType.Normal) 141 | .borderRadius(0) 142 | .width('100%') 143 | .fontSize(16) 144 | .fontColor(Color.Red) 145 | .backgroundColor(Color.White) 146 | .onClick(() => { 147 | router.pushUrl({ 148 | url: 'pages/EditAddr' 149 | }) 150 | }) 151 | }.width('100%') 152 | .height(50) 153 | }.width('100%') 154 | .height('100%') 155 | } 156 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/ConfirmOrder.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import Prompt from '@system.prompt' 3 | import { confirmOrderApi, homeApi } from '../api/Api' 4 | import ConfirmOrderDataModel from '../datamodels/ConfirmOrderDataModel' 5 | import ConfirmOrderReqModel from '../datamodels/ConfirmOrderReqModel' 6 | import ResData from '../datamodels/ResData' 7 | import ShopCartItemDataModel from '../datamodels/ShopCartItemDataModel' 8 | import ShopCartItemDiscount from '../datamodels/ShopCartItemDiscountDataModel' 9 | import ShopCartOrderDataModel from '../datamodels/ShopCartOrderDataModel' 10 | import SubmitOrderDataModel from '../datamodels/SubmitOrderDataModel' 11 | import SubmitOrderItemDataModel from '../datamodels/SubmitOrderItemDataModel' 12 | import UserAddrDataModel from '../datamodels/UserAddrDataModel' 13 | 14 | @Entry 15 | @Component 16 | export default struct ConfirmOrder { 17 | @State basketIds: number[] = [] 18 | @State confirmOrderDataModel: ConfirmOrderDataModel = new ConfirmOrderDataModel() 19 | @State totalTransfee: number = 0 20 | @State remark: string = '' 21 | private selectedAddrId: number = 0 22 | private selectedAddr: UserAddrDataModel 23 | 24 | async aboutToAppear() { 25 | this.basketIds = router.getParams()["basketIds"] as number[] 26 | if (this.basketIds && this.basketIds.length > 0) { 27 | const confirmOrderReqModel = new ConfirmOrderReqModel() 28 | confirmOrderReqModel.basketIds = this.basketIds 29 | confirmOrderReqModel.addrId = 0 30 | confirmOrderReqModel.couponIds = [] 31 | confirmOrderReqModel.userChangeCoupon = 1 32 | try { 33 | this.confirmOrderDataModel = await confirmOrderApi.confirm(confirmOrderReqModel) 34 | console.log('confirmOrderDataModel:', JSON.stringify(this.confirmOrderDataModel)) 35 | // 计算总运费 36 | this.confirmOrderDataModel.shopCartOrders.forEach(shopCartOrder => { 37 | this.totalTransfee += shopCartOrder.transfee 38 | }) 39 | } catch (e) { 40 | console.log("confirmOrderDataModel err:", JSON.stringify(e)) 41 | } 42 | } else { 43 | Prompt.showToast({ 44 | message: '没有获取到购物车商品' 45 | }) 46 | } 47 | } 48 | 49 | onPageShow() { 50 | this.selectedAddrId = router.getParams()['selectedAddrId'] 51 | this.selectedAddr = router.getParams()['selectedAddr'] 52 | this.confirmOrderDataModel.userAddr = this.selectedAddr 53 | } 54 | 55 | @Builder 56 | buildItemHeader(title: string) { 57 | Row() { 58 | Text(title) 59 | .fontSize(16) 60 | .fontWeight(500) 61 | .margin({ left: 10 }) 62 | } 63 | .width('100%') 64 | .padding(10) 65 | .backgroundColor(Color.White) 66 | } 67 | 68 | build() { 69 | 70 | Stack({ alignContent: Alignment.Bottom }) { 71 | Scroll() { 72 | Column() { 73 | // 导航栏 74 | Row() { 75 | Image($r('app.media.more')) 76 | .width(16) 77 | .margin({ left: 10 }) 78 | .rotate({ 79 | angle: 180 80 | }).onClick(() => { 81 | router.back() 82 | }) 83 | Row() { 84 | Text('提交订单') 85 | .fontSize(20) 86 | .fontWeight(600) 87 | }.layoutWeight(1) 88 | .justifyContent(FlexAlign.Center) 89 | 90 | Blank() 91 | .width(20) 92 | }.width('100%') 93 | .backgroundColor(Color.White) 94 | .justifyContent(FlexAlign.SpaceAround) 95 | .padding(10) 96 | 97 | // 地址栏 98 | Row() { 99 | Row() { 100 | Column() { 101 | Row() { 102 | Image($r('app.media.addr')) 103 | .width(12) 104 | Text(this.confirmOrderDataModel?.userAddr?.receiver) 105 | .margin({ left: 10 }) 106 | Text(this.confirmOrderDataModel?.userAddr?.mobile) 107 | .margin({ left: 10 }) 108 | } 109 | // 详细地址 110 | Text(`${this.confirmOrderDataModel?.userAddr?.province + this.confirmOrderDataModel?.userAddr?.city + this.confirmOrderDataModel?.userAddr?.area + this.confirmOrderDataModel?.userAddr?.addr}`) 111 | .fontSize(16) 112 | .fontColor(Color.Grey) 113 | .margin({ top: 10, left: 10 }) 114 | } 115 | } 116 | .layoutWeight(1) 117 | 118 | Row() { 119 | Image($r('app.media.more')) 120 | .width(12) 121 | .onClick(() => { 122 | router.pushUrl({ 123 | url: 'pages/Addr' 124 | }) 125 | }) 126 | }.width(30) 127 | 128 | } 129 | .width('100%') 130 | .backgroundColor(Color.White) 131 | .justifyContent(FlexAlign.SpaceBetween) 132 | .padding({ top: 10, left: 10, bottom: 10 }) 133 | 134 | // 商品列表 135 | List() { 136 | ForEach(this.confirmOrderDataModel?.shopCartOrders, (shopCartOrder: ShopCartOrderDataModel) => { 137 | ListItemGroup({ header: this.buildItemHeader(shopCartOrder.shopName), space: 10 }) 138 | 139 | ForEach(shopCartOrder?.shopCartItemDiscounts, (shopCartItemDiscount: ShopCartItemDiscount) => { 140 | ForEach(shopCartItemDiscount?.shopCartItems, (shopCartItem: ShopCartItemDataModel) => { 141 | ListItem() { 142 | Row() { 143 | Image(shopCartItem.pic) 144 | .width(80) 145 | Column() { 146 | Row() { 147 | // 商品标题 148 | Text(shopCartItem.prodName) 149 | .fontSize(14) 150 | .maxLines(2) 151 | .textOverflow({ 152 | overflow: TextOverflow.Ellipsis 153 | }) 154 | }.width('100%') 155 | 156 | // 商品SKU 157 | Row() { 158 | Text(shopCartItem.skuName) 159 | .fontSize(12) 160 | .fontColor('#c3c3c3') 161 | }.width('100%') 162 | .margin({ top: 4 }) 163 | 164 | // 商品价格和数量 165 | Row() { 166 | Text(`¥ ${shopCartItem.price}`) 167 | .fontSize(12) 168 | Text(`✕ ${shopCartItem.prodCount}`) 169 | .fontColor('#c3c3c3') 170 | .fontSize(12) 171 | }.justifyContent(FlexAlign.SpaceBetween) 172 | .width('100%') 173 | .margin({ top: 4 }) 174 | 175 | }.layoutWeight(1) 176 | } 177 | .width('100%') 178 | }.width('92%') 179 | .padding(10) 180 | }) 181 | }) 182 | }) 183 | }.width('100%') 184 | .backgroundColor(Color.White) 185 | .divider({ 186 | strokeWidth: 1, 187 | startMargin: 10, 188 | color: '#c3c3c3' 189 | }) 190 | 191 | Divider() 192 | .strokeWidth(1) 193 | .color('#c3c3c3') 194 | .width('100%') 195 | .padding(4) 196 | .opacity(0.7) 197 | .backgroundColor(Color.White) 198 | 199 | // 商品总计 200 | Row() { 201 | Text(`共${this.confirmOrderDataModel?.totalCount}件商品 合计: ¥ ${this.confirmOrderDataModel?.total}`) 202 | .margin({ right: 10 }) 203 | } 204 | .width('100%') 205 | .backgroundColor(Color.White) 206 | .justifyContent(FlexAlign.End) 207 | .padding(10) 208 | 209 | // 优惠券 210 | Row() { 211 | Text(`优惠券: 暂无可用`) 212 | .margin({ left: 10 }) 213 | Row() { 214 | Text('0张') 215 | .fontSize(12) 216 | .fontColor(Color.Grey) 217 | Image($r('app.media.more')) 218 | .width(12) 219 | .margin({ left: 10, right: 10 }) 220 | } 221 | } 222 | .padding({ top: 10, bottom: 10 }) 223 | .width('100%') 224 | .justifyContent(FlexAlign.SpaceBetween) 225 | .backgroundColor(Color.White) 226 | .margin({ top: 4 }) 227 | 228 | Divider() 229 | .strokeWidth(1) 230 | .color('#c3c3c3') 231 | .margin({ left: 10, right: 10 }) 232 | .width('100%') 233 | .padding(4) 234 | .opacity(0.7) 235 | .backgroundColor(Color.White) 236 | 237 | // 买家留言 238 | // 优惠券 239 | Row() { 240 | Text(`买家留言: `) 241 | .fontSize(14) 242 | .margin({ left: 10 }) 243 | TextInput({ text: this.remark, placeholder: '给卖家留言' }) 244 | .fontSize(14) 245 | .fontColor(Color.Grey) 246 | }.width('100%') 247 | .padding({ top: 10, bottom: 10 }) 248 | .backgroundColor(Color.White) 249 | 250 | // 订单总金额 251 | Column() { 252 | Row() { 253 | Text('订单总额:') 254 | .margin({ left: 10 }) 255 | Text(`¥ ${this.confirmOrderDataModel?.total}`) 256 | .margin({ right: 10 }) 257 | }.width('100%') 258 | .justifyContent(FlexAlign.SpaceBetween) 259 | 260 | Row() { 261 | Text('运费:') 262 | .margin({ left: 10 }) 263 | Text(`¥ ${this.totalTransfee}`) 264 | .margin({ right: 10 }) 265 | }.width('100%') 266 | .justifyContent(FlexAlign.SpaceBetween) 267 | .margin({ top: 10 }) 268 | 269 | Row() { 270 | Text('优惠金额:') 271 | .margin({ left: 10 }) 272 | Text(`¥ ${this.confirmOrderDataModel?.orderReduce}`) 273 | .margin({ right: 10 }) 274 | }.width('100%') 275 | .justifyContent(FlexAlign.SpaceBetween) 276 | .margin({ top: 10 }) 277 | 278 | Divider() 279 | .strokeWidth(1) 280 | .color('#c3c3c3') 281 | .opacity(0.7) 282 | .margin({ left: 10, right: 10 }) 283 | .width('100%') 284 | .padding(4) 285 | 286 | // 小计 287 | Row() { 288 | Text(`小计: ¥ ${this.confirmOrderDataModel?.actualTotal}`) 289 | .margin({ right: 10 }) 290 | .fontColor(Color.Red) 291 | } 292 | .width('100%') 293 | .justifyContent(FlexAlign.End) 294 | .padding(10) 295 | 296 | }.width('100%') 297 | .backgroundColor(Color.White) 298 | .padding({ top: 8 }) 299 | .margin({ top: 12 }) 300 | 301 | } 302 | .width('100%') 303 | .height('100%') 304 | .backgroundColor('#f4f4f4') 305 | }.width('100%') 306 | .height('100%') 307 | 308 | // 底部悬浮菜单栏 309 | Row() { 310 | Row() { 311 | Text('合计:') 312 | .fontSize(14) 313 | Text(`¥ ${this.confirmOrderDataModel?.total}`) 314 | .fontSize(14) 315 | .fontColor(Color.Red) 316 | } 317 | .margin({ left: 10 }) 318 | 319 | Button('提交订单') 320 | .type(ButtonType.Normal) 321 | .width(120) 322 | .height('100%') 323 | .borderRadius(3) 324 | .backgroundColor(Color.Red) 325 | .onClick(async () => { 326 | const reqData: SubmitOrderDataModel = new SubmitOrderDataModel() 327 | reqData.orderShopParam = [] 328 | this.confirmOrderDataModel.shopCartOrders.forEach((shopCartOrder: ShopCartOrderDataModel) => { 329 | const reqDataItem: SubmitOrderItemDataModel = new SubmitOrderItemDataModel() 330 | reqDataItem.remarks = this.remark 331 | reqDataItem.shopId = shopCartOrder.shopId 332 | 333 | reqData.orderShopParam.push(reqDataItem) 334 | }) 335 | 336 | // 发起网络请求 337 | const resData = await confirmOrderApi.submit(reqData) 338 | console.log('resData, ', JSON.stringify(resData)) 339 | if (!resData.success) { 340 | Prompt.showToast({ 341 | message: resData.msg 342 | }) 343 | } 344 | router.pushUrl({ 345 | url: 'pages/Index' 346 | }) 347 | }) 348 | 349 | } 350 | .width('100%') 351 | .height(50) 352 | .justifyContent(FlexAlign.SpaceBetween) 353 | 354 | }.width('100%') 355 | 356 | } 357 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/EditAddr.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import Prompt from '@system.prompt' 3 | import { addrApi } from '../api/Api' 4 | import { AreaPicker } from '../components/AreaPicker' 5 | import AddAddrRequestModel from '../datamodels/AddAddrRequestModel' 6 | @Entry 7 | @Component 8 | struct EditAddr { 9 | @State receiver: string = '' 10 | @State mobile: string = '' 11 | @State addr: string = '' 12 | 13 | @State selectProvinceId: number = 0 14 | @State selectProvinceName: string = '' 15 | @State selectCityId: number = 110000000000 16 | @State selectCityName: string = '' 17 | 18 | @State selectZoneId: number = 120100000000 19 | @State selectZoneName: string = '' 20 | 21 | customDialogController: CustomDialogController = new CustomDialogController({ 22 | builder: AreaPicker({ 23 | selectProvinceId: $selectProvinceId, 24 | selectProvinceName: $selectProvinceName, 25 | selectCityId: $selectCityId, 26 | selectCityName: $selectCityName, 27 | selectZoneId: $selectZoneId, 28 | selectZoneName: $selectZoneName 29 | }), 30 | autoCancel: true, 31 | alignment: DialogAlignment.Bottom, 32 | offset: { dx: 0, dy: -20 }, 33 | gridCount: 4, 34 | customStyle: false 35 | }) 36 | 37 | 38 | build() { 39 | Column() { 40 | Row() { 41 | Image($r('app.media.more')) 42 | .width(16) 43 | .rotate({ 44 | angle: 180 45 | }) 46 | .margin({ left: 10 }) 47 | .onClick(() => { 48 | router.back() 49 | }) 50 | 51 | Row() { 52 | Text('编辑收货地址') 53 | .fontSize(16) 54 | .fontWeight(600) 55 | }.layoutWeight(1) 56 | .justifyContent(FlexAlign.Center) 57 | .offset({ 58 | x: -10 59 | }) 60 | 61 | Blank() 62 | .width(20) 63 | }.width('100%') 64 | .height(50) 65 | .justifyContent(FlexAlign.SpaceAround) 66 | .backgroundColor(Color.White) 67 | 68 | Row() { 69 | Text('收 货 人') 70 | .fontSize(14) 71 | .width(70) 72 | TextInput({placeholder: '姓名', text: this.receiver}) 73 | .fontSize(14) 74 | .margin({left: 10}) 75 | .borderRadius(0) 76 | .backgroundColor(Color.White) 77 | .onChange(value => { 78 | this.receiver = value 79 | }) 80 | }.width('100%') 81 | .padding(10) 82 | Divider() 83 | .opacity(0.6) 84 | .margin({left: 10, right: 10}) 85 | 86 | Row() { 87 | Text('手机号码') 88 | .fontSize(14) 89 | .width(70) 90 | TextInput({placeholder: '11位手机号码', text: this.mobile}) 91 | .type(InputType.Number) 92 | .fontSize(14) 93 | .margin({left: 10}) 94 | .borderRadius(0) 95 | .backgroundColor(Color.White) 96 | .onChange((value: string) => { 97 | this.mobile = value 98 | }) 99 | }.width('100%') 100 | .padding(10) 101 | Divider() 102 | .opacity(0.6) 103 | .margin({left: 10, right: 10}) 104 | 105 | Row() { 106 | Text('所在地区') 107 | .fontSize(14) 108 | .width(70) 109 | Row() { 110 | Text(`${this.selectProvinceName} ${this.selectCityName} ${this.selectZoneName}`) 111 | .fontSize(14) 112 | Image($r('app.media.more')) 113 | .width(16) 114 | .onClick(() => { 115 | // 打开地区选择器 116 | this.customDialogController.open() 117 | }) 118 | }.layoutWeight(1) 119 | .justifyContent(FlexAlign.SpaceBetween) 120 | 121 | }.width('100%') 122 | .padding(10) 123 | .justifyContent(FlexAlign.SpaceBetween) 124 | 125 | .padding(10) 126 | Divider() 127 | .opacity(0.6) 128 | .margin({left: 10, right: 10}) 129 | 130 | Row() { 131 | Text('详细地址') 132 | .fontSize(14) 133 | .width(70) 134 | TextInput({placeholder: '如楼号/单元/门牌号', text: this.addr}) 135 | .fontSize(14) 136 | .margin({left: 10}) 137 | .borderRadius(0) 138 | .backgroundColor(Color.White) 139 | .onChange(value => { 140 | this.addr = value 141 | }) 142 | }.width('100%') 143 | .padding(10) 144 | Divider() 145 | .opacity(0.6) 146 | .margin({left: 10, right: 10}) 147 | 148 | Button('保存收货地址') 149 | .backgroundColor(Color.Red) 150 | .width(200) 151 | .margin({top: 20}) 152 | .onClick(async () => { 153 | console.log('receiver:', this.receiver) 154 | if (this.receiver == '') { 155 | Prompt.showToast({ 156 | message: '请填写收货人' 157 | }) 158 | return 159 | } 160 | if (this.mobile == '') { 161 | Prompt.showToast({ 162 | message: '请填写手机号码' 163 | }) 164 | return 165 | } 166 | if (this.selectProvinceName == '') { 167 | Prompt.showToast({ 168 | message: '请选择省份' 169 | }) 170 | return 171 | } 172 | 173 | if (this.selectCityName == '') { 174 | Prompt.showToast({ 175 | message: '请选择城市' 176 | }) 177 | return 178 | } 179 | 180 | if (this.selectZoneName == '') { 181 | Prompt.showToast({ 182 | message: '请选择区县' 183 | }) 184 | return 185 | } 186 | 187 | if (this.addr == '') { 188 | Prompt.showToast({ 189 | message: '请填写详细地址' 190 | }) 191 | return 192 | } 193 | 194 | const addAddrRequestModel: AddAddrRequestModel = new AddAddrRequestModel() 195 | addAddrRequestModel.receiver = this.receiver 196 | addAddrRequestModel.mobile = this.mobile 197 | addAddrRequestModel.provinceId = this.selectProvinceId 198 | addAddrRequestModel.province = this.selectProvinceName 199 | addAddrRequestModel.cityId = this.selectCityId 200 | addAddrRequestModel.city = this.selectCityName 201 | addAddrRequestModel.areaId = this.selectZoneId 202 | addAddrRequestModel.area = this.selectZoneName 203 | addAddrRequestModel.addr = this.addr 204 | addAddrRequestModel.userType = 0 205 | 206 | await addrApi.addAddr(addAddrRequestModel) 207 | router.back() 208 | 209 | }) 210 | } 211 | .width('100%') 212 | } 213 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/Index.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import { cartApi } from '../api/Api' 3 | import Cart from '../components/Cart' 4 | import Category from '../components/Category' 5 | import Home from '../components/Home' 6 | import My from '../components/My' 7 | import TabBarViewModel from '../viewmodels/TabBarViewModel' 8 | 9 | @Entry 10 | @Component 11 | struct Index { 12 | private tabBarViewModels: TabBarViewModel[] = [ 13 | new TabBarViewModel('首页', $r('app.media.homepage'), $r('app.media.homepage_sel'), 0), 14 | new TabBarViewModel('分类', $r('app.media.category'), $r('app.media.category_sel'), 1), 15 | new TabBarViewModel('购物车', $r('app.media.basket'), $r('app.media.basket_sel'), 2), 16 | new TabBarViewModel('我的', $r('app.media.user'), $r('app.media.user_sel'), 3) 17 | ] 18 | @State selectedIndex: number = 0 19 | @State prodCount: number = 0 20 | private tabsController: TabsController = new TabsController() 21 | 22 | async aboutToAppear() { 23 | // 获取从其它页面跳转过来的参数 24 | const selectedIndex = router.getParams()?.["selectedIndex"] 25 | this.selectedIndex = selectedIndex 26 | // 获取购物车数量 27 | this.prodCount = await cartApi.prodCount() 28 | } 29 | 30 | onPageShow() { 31 | if (this.selectedIndex) { 32 | this.tabsController.changeIndex(this.selectedIndex) 33 | } 34 | } 35 | 36 | @Builder 37 | buildTabBar(tabBarViewModel: TabBarViewModel) { 38 | 39 | Badge({ 40 | count: tabBarViewModel.title == '购物车' ? this.prodCount : 0, 41 | position: BadgePosition.RightTop, 42 | style: { badgeSize: 13, badgeColor: '#FA2A2D' } 43 | }) { 44 | Column() { 45 | Image(this.selectedIndex == tabBarViewModel.index ? tabBarViewModel.selectedImage : tabBarViewModel.image) 46 | .width(20) 47 | .height(20) 48 | .margin({ top: 3 }) 49 | Text(tabBarViewModel.title) 50 | .fontSize(12) 51 | .fontWeight(500) 52 | .margin({ top: 5 }) 53 | .fontColor(this.selectedIndex == tabBarViewModel.index ? '#00aaff' : Color.Black) 54 | 55 | }.height(50) 56 | } 57 | 58 | } 59 | 60 | build() { 61 | Tabs({controller: this.tabsController}) { 62 | TabContent() { 63 | Home() 64 | } 65 | .tabBar(this.buildTabBar(this.tabBarViewModels[0])) 66 | 67 | TabContent() { 68 | Category() 69 | } 70 | .tabBar(this.buildTabBar(this.tabBarViewModels[1])) 71 | 72 | TabContent() { 73 | if (this.selectedIndex == 2) { 74 | Cart() 75 | } 76 | } 77 | .tabBar(this.buildTabBar(this.tabBarViewModels[2])) 78 | 79 | TabContent() { 80 | My() 81 | } 82 | .tabBar(this.buildTabBar(this.tabBarViewModels[3])) 83 | 84 | }.barPosition(BarPosition.End) 85 | .width('100%') 86 | .onChange((index: number) => { 87 | this.selectedIndex = index 88 | }) 89 | } 90 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/Login.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import Prompt from '@system.prompt' 3 | import { myApi } from '../api/Api' 4 | import LoginReqModel from '../datamodels/LoginReqModel' 5 | import CryptoJSUtil from '../utils/CryptoJSUtil' 6 | import PreferencesUtil from '../utils/PreferencesUtil' 7 | import AppConst from '../configurations/AppConst' 8 | 9 | @Entry 10 | @Component 11 | export default struct Login { 12 | @State userName: string = 'a' 13 | @State passWord: string = 'a' 14 | 15 | build() { 16 | Column() { 17 | // 标题栏 18 | Row() { 19 | Image($r('app.media.more')) 20 | .width(20) 21 | .margin({ left: 10 }) 22 | .rotate({ 23 | angle: 180 24 | }).onClick(() => { 25 | router.back() 26 | }) 27 | 28 | Row() { 29 | Text('用户登录') 30 | .fontSize(20) 31 | .fontWeight(600) 32 | }.layoutWeight(1) 33 | .justifyContent(FlexAlign.Center) 34 | 35 | Blank() 36 | .width(20) 37 | 38 | } 39 | .width('100%') 40 | .height(40) 41 | .justifyContent(FlexAlign.SpaceBetween) 42 | 43 | // Logo 44 | Row() { 45 | Image($r('app.media.logo')) 46 | .width(80) 47 | }.width('100%') 48 | .margin({ top: 40 }) 49 | .justifyContent(FlexAlign.Center) 50 | 51 | // 登录框 52 | Column() { 53 | Row() { 54 | Text('账号') 55 | .fontSize(14) 56 | TextInput({ placeholder: '请输入用户名', text: this.userName }) 57 | .borderRadius(5) 58 | .backgroundColor('#f5f5f5') 59 | .layoutWeight(1) 60 | .onChange(value => { 61 | this.userName = value 62 | }) 63 | }.width('90%') 64 | .padding(3) 65 | .borderRadius(6) 66 | .backgroundColor('#f5f5f5') 67 | 68 | Row() { 69 | Text('密码') 70 | .fontSize(14) 71 | TextInput({ placeholder: '请输入密码', text: this.passWord }) 72 | .type(InputType.Password) 73 | .borderRadius(5) 74 | .backgroundColor('#f5f5f5') 75 | .layoutWeight(1) 76 | .onChange(value => { 77 | this.passWord = value 78 | }) 79 | } 80 | .width('90%') 81 | .padding(3) 82 | .borderRadius(6) 83 | .backgroundColor('#f5f5f5') 84 | .margin({ top: 10 }) 85 | 86 | Row() { 87 | Text('还没有账号? 去注册>') 88 | .fontSize(14) 89 | .fontColor('#00aaff') 90 | }.width('90%') 91 | .margin({ top: 10 }) 92 | 93 | Row() { 94 | Button('登录') 95 | .commonButton() 96 | .fontColor(Color.White) 97 | .backgroundColor('#0ab906') 98 | .onClick(async () => { 99 | if (this.userName.trim() == '') { 100 | Prompt.showToast({ 101 | message: '请输入用户名' 102 | }) 103 | return false 104 | } 105 | 106 | if (this.passWord.trim() == '') { 107 | Prompt.showToast({ 108 | message: '请输入密码' 109 | }) 110 | return false 111 | } 112 | console.log('username', CryptoJSUtil.encrypt(this.passWord)) 113 | const loginDataModel = await myApi.login(new LoginReqModel(this.userName, CryptoJSUtil.encrypt(this.passWord))) 114 | console.log('loginDataModel', JSON.stringify(loginDataModel)) 115 | // 保存用户的登录信息到preferences 116 | if (loginDataModel.accessToken) { 117 | try { 118 | // 这里必须要用try,否则router那里无法执行,不知道为什么 119 | console.log("开始保存用户登录信息到首选项") 120 | await PreferencesUtil.setPreference(AppConst.PREFERENCES_LOGIN_CODE, loginDataModel.accessToken + '') 121 | console.log("保存用户登录信息到首选项成功") 122 | } catch (e) { 123 | console.error("保存用户登录信息到首选项失败:", JSON.stringify(e)) 124 | } 125 | // 登录成功,返回到首页 126 | router.replaceUrl({ 127 | url: 'pages/Index' 128 | }) 129 | } 130 | }) 131 | } 132 | .width('90%') 133 | .margin({ top: 30 }) 134 | 135 | Row() { 136 | Button('回到首页') 137 | .commonButton() 138 | .fontColor(Color.Black) 139 | .backgroundColor('#c9c9c9') 140 | .onClick(() => { 141 | router.replaceUrl({ 142 | url: 'pages/Index' 143 | }) 144 | }) 145 | 146 | } 147 | .width('90%') 148 | .margin({ top: 10 }) 149 | }.width('100%') 150 | .margin({ top: 20 }) 151 | }.width('100%') 152 | } 153 | } 154 | 155 | @Extend(Button) function commonButton() { 156 | .width('100%') 157 | .height(35) 158 | .fontSize(14) 159 | .type(ButtonType.Normal) 160 | .borderRadius(3) 161 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/Order.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import OrderList from '../components/OrderList' 3 | import OrderCountDataModel from '../datamodels/OrderCountDataModel' 4 | import OrderTabViewModel from '../datamodels/OrderTabViewModel' 5 | 6 | @Entry 7 | @Component 8 | struct Order { 9 | 10 | private orderTabs: OrderTabViewModel[] = [ 11 | {title: '全部', status: 0}, 12 | {title: '待支付', status: 1}, 13 | {title: '待发货', status: 2}, 14 | {title: '待收货', status: 3}, 15 | {title: '已完成', status: 5} 16 | ] 17 | 18 | @State selectedIndex: number = 0 19 | @State status: number = 0 20 | 21 | private tabsController = new TabsController() 22 | 23 | aboutToAppear() { 24 | const status = router.getParams()["status"] 25 | if (status >= 0) { 26 | this.status = status 27 | } 28 | } 29 | 30 | onPageShow() { 31 | // 跳转到对应的标签 32 | if (this.status != 0) { 33 | this.selectedIndex = this.status 34 | this.tabsController.changeIndex(this.selectedIndex) 35 | } 36 | } 37 | 38 | @Builder 39 | buildOrderTab(orderTabViewModel: OrderTabViewModel, index: number) { 40 | Column() { 41 | Text(orderTabViewModel.title) 42 | .fontColor(this.selectedIndex == index ? Color.Red : Color.Black) 43 | .fontSize(16) 44 | 45 | Divider() 46 | .color(this.selectedIndex == index ? Color.Red : Color.Gray) 47 | .opacity(this.selectedIndex == index ? 1 : 0) 48 | .margin({top: 10}) 49 | .height(3) 50 | .width('100%') 51 | 52 | } 53 | } 54 | 55 | build() { 56 | 57 | Column() { 58 | Row() { 59 | Image($r('app.media.more')) 60 | .width(16) 61 | .margin({ left: 10 }) 62 | .rotate({ 63 | angle: 180 64 | }).onClick(() => { 65 | router.back() 66 | }) 67 | Row() { 68 | Text('订单') 69 | .fontSize(20) 70 | .fontWeight(600) 71 | }.layoutWeight(1) 72 | .justifyContent(FlexAlign.Center) 73 | 74 | Blank() 75 | .width(20) 76 | }.width('100%') 77 | .backgroundColor(Color.White) 78 | .justifyContent(FlexAlign.SpaceAround) 79 | .padding(10) 80 | 81 | Tabs({controller: this.tabsController}){ 82 | ForEach(this.orderTabs, (orderTab: OrderTabViewModel, index: number) => { 83 | TabContent() { 84 | OrderList({status: orderTab.status as number}) 85 | }.tabBar(this.buildOrderTab(orderTab, index)) 86 | .tabIndex(index) 87 | .align(Alignment.Top) 88 | }) 89 | 90 | }.barPosition(BarPosition.Start) 91 | .onChange((index: number) => { 92 | this.selectedIndex = index 93 | }) 94 | 95 | }.width('100%') 96 | 97 | } 98 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/PaySuccess.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | 3 | @Entry 4 | @Component 5 | struct PaySuccess { 6 | 7 | private greenColor: string = '#19be6b' 8 | 9 | build() { 10 | Column() { 11 | Row() { 12 | Blank() 13 | Row() { 14 | Text('支付结果') 15 | .fontSize(20) 16 | .fontWeight(600) 17 | }.layoutWeight(1) 18 | .justifyContent(FlexAlign.Center) 19 | 20 | Blank() 21 | .width(20) 22 | }.width('100%') 23 | .backgroundColor(Color.White) 24 | .justifyContent(FlexAlign.SpaceAround) 25 | .padding(10) 26 | 27 | Row() { 28 | Text('支付成功') 29 | .fontSize(22) 30 | .fontColor(this.greenColor) 31 | } 32 | .width('100%') 33 | .justifyContent(FlexAlign.Center) 34 | .margin({top: 30}) 35 | 36 | Row() { 37 | Text('感谢您的购买') 38 | .fontSize(18) 39 | .fontColor(Color.Gray) 40 | }.width('100%') 41 | .justifyContent(FlexAlign.Center) 42 | .margin({top: 10}) 43 | 44 | Row({space: 10}) { 45 | Button('查看订单') 46 | .type(ButtonType.Normal) 47 | .backgroundColor(this.greenColor) 48 | .fontColor(Color.White) 49 | .fontSize(16) 50 | .borderRadius(6) 51 | .onClick(() => { 52 | router.replaceUrl({ 53 | url: 'pages/Order', 54 | params: { 55 | status: 0 56 | } 57 | }) 58 | }) 59 | 60 | Button('继续购物') 61 | .type(ButtonType.Normal) 62 | .backgroundColor(Color.White) 63 | .fontColor(this.greenColor) 64 | .border({ 65 | width: 1, 66 | color: this.greenColor 67 | }) 68 | .fontSize(16) 69 | .borderRadius(6) 70 | .onClick(() => { 71 | router.replaceUrl({ 72 | url: 'pages/Index' 73 | }) 74 | }) 75 | } 76 | .width('100%') 77 | .justifyContent(FlexAlign.Center) 78 | .margin({top: 20}) 79 | } 80 | .width('100%') 81 | .height('100%') 82 | } 83 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/ProductDetail.ets: -------------------------------------------------------------------------------- 1 | // 商品详详情页 2 | import router from '@ohos.router' 3 | import Prompt from '@system.prompt' 4 | import { cartApi, productApi } from '../api/Api' 5 | import NumberInput from '../components/NumberInput' 6 | import AddCartReqModel from '../datamodels/AddCartReqModel' 7 | import ProductDataModel from '../datamodels/ProductDataModel' 8 | import SkuDataModel from '../datamodels/SkuDataModel' 9 | 10 | @Entry 11 | @Component 12 | struct ProductDetail { 13 | @State prodId: number = 0 14 | @State imgs: string[] = [] 15 | @State title: string = '' 16 | @State bref: string = '' 17 | @State price: number = 0 18 | @State content: string = '' 19 | @State curVersionIndex: number = 0 20 | @State curColorIndex: number = 0 21 | @State curMemIndex: number = 0 22 | @State versions: string[] = [] 23 | @State colors: string[] = [] 24 | @State mems: string[] = [] 25 | @State dialogHeight: number = 300 26 | @State selectedSku: SkuDataModel = new SkuDataModel() 27 | @State selectedCount: number = 1 28 | private productDataModel: ProductDataModel = new ProductDataModel() 29 | 30 | @State isEmptyProperties: Boolean = true 31 | 32 | skuDialogController: CustomDialogController = new CustomDialogController({ 33 | builder: SkuDialog({ 34 | productDataModel: this.productDataModel, 35 | skuList: this.productDataModel.skuList, 36 | selectedSku: $selectedSku, 37 | versions: this.versions, 38 | colors: this.colors, 39 | mems: this.mems, 40 | selectedCount: $selectedCount 41 | }), 42 | alignment: DialogAlignment.Bottom 43 | }) 44 | 45 | async aboutToAppear() { 46 | // 从页面跳转的路由获取商品ID 47 | this.prodId = router.getParams()["prodId"] as number 48 | if (!this.prodId) { 49 | this.prodId = 18 50 | } 51 | // 请求商品详情信息 52 | this.productDataModel = await productApi.getProductData(this.prodId) 53 | this.title = this.productDataModel.prodName 54 | this.bref = this.productDataModel.brief 55 | this.imgs = this.productDataModel.imgs.split(",") 56 | this.price = this.productDataModel.price 57 | this.content = this.productDataModel.content 58 | 59 | // SKU处理,版本,颜色,内存 60 | // 遍历所有的sku的properties切分 61 | for (const sku of this.productDataModel.skuList) { 62 | if (sku.properties == "") { 63 | this.isEmptyProperties = true 64 | break; 65 | } 66 | // 版本:公开版;颜色:深空灰色;内存:256GB 67 | const split = sku.properties.split(";") 68 | for (const pair of split) { 69 | const property = pair.split(":") 70 | if ("版本" == property[0]) { 71 | this.versions.push(property[1]) 72 | } 73 | if ("颜色" == property[0]) { 74 | this.colors.push(property[1]) 75 | } 76 | if ("内存" == property[0]) { 77 | this.mems.push(property[1]) 78 | } 79 | } 80 | } 81 | 82 | this.versions = this.versions.filter((value, index, self) => 83 | self.indexOf(value) === index 84 | ); 85 | this.colors = this.colors.filter((value, index, self) => 86 | self.indexOf(value) === index 87 | ); 88 | this.mems = this.mems.filter((value, index, self) => 89 | self.indexOf(value) === index 90 | ); 91 | 92 | } 93 | 94 | @Builder 95 | buildTag(label: string, num: number) { 96 | Text(label + '(' + num + ')') 97 | .fontSize(12) 98 | .fontColor(Color.Grey) 99 | .backgroundColor('#fdf0f0') 100 | .padding({ left: 10, right: 10, top: 3, bottom: 3 }) 101 | .margin({ left: 6, right: 6 }) 102 | .borderRadius(5) 103 | } 104 | 105 | build() { 106 | Stack({ alignContent: Alignment.Bottom }) { 107 | Scroll() { 108 | Column() { 109 | // 导航栏 110 | Row() { 111 | Image($r('app.media.more')) 112 | .width(18) 113 | .rotate({ 114 | angle: 180 115 | }) 116 | .margin({ left: 10 }) 117 | .onClick(() => { 118 | router.back() 119 | }) 120 | Row() { 121 | Text('商品详情') 122 | .fontSize(16) 123 | .fontWeight(800) 124 | .margin({ left: 10 }) 125 | }.layoutWeight(1) 126 | .justifyContent(FlexAlign.Center) 127 | 128 | Blank() 129 | .width(20) 130 | 131 | }.width('100%') 132 | .height(50) 133 | 134 | // 商品图片swiper 135 | Row() { 136 | Swiper() { 137 | ForEach(this.imgs, (img: string) => { 138 | Image(img.trim()) 139 | .width('92%') 140 | 141 | }) 142 | }.width('96%') 143 | }.width('100%') 144 | .justifyContent(FlexAlign.Center) 145 | .width('100%') 146 | 147 | Divider() 148 | 149 | // 标题栏,收藏 150 | Row() { 151 | Text(this.title) 152 | .fontSize(16) 153 | .maxLines(2) 154 | .textOverflow({ 155 | overflow: TextOverflow.Ellipsis 156 | }) 157 | .layoutWeight(1) 158 | 159 | Row() { 160 | Blank() 161 | .width(1) 162 | .backgroundColor(Color.Grey) 163 | 164 | Column() { 165 | Image($r('app.media.prod_col')) 166 | .width(16) 167 | 168 | Text('收藏') 169 | .fontColor(Color.Grey) 170 | }.margin({ left: 10 }) 171 | } 172 | }.width('96%') 173 | .margin({ top: 10 }) 174 | .justifyContent(FlexAlign.SpaceBetween) 175 | 176 | Row() { 177 | Text(this.bref) 178 | .fontSize(12) 179 | .fontColor(Color.Grey) 180 | }.width('96%') 181 | .margin({ top: 5 }) 182 | 183 | // 价格行 184 | Row() { 185 | Text(`¥ ${this.price}`) 186 | .fontSize(16) 187 | .fontColor(Color.Red) 188 | }.width("96%") 189 | .margin({ top: 6 }) 190 | .padding({ bottom: 10 }) 191 | 192 | Blank() 193 | .width('100%') 194 | .height(10) 195 | .backgroundColor('#f4f4f4') 196 | 197 | // 选择SKU行 198 | Row() { 199 | Row() { 200 | Text('已选 ') 201 | .fontSize(12) 202 | .fontColor(Color.Grey) 203 | 204 | Text(`1件`) 205 | .fontSize(14) 206 | .fontWeight(600) 207 | }.layoutWeight(1) 208 | 209 | Text('···') 210 | .fontSize(12) 211 | .fontColor(Color.Grey) 212 | .width(20) 213 | .onClick(() => { 214 | // 显示sku选择弹窗 215 | this.skuDialogController.open() 216 | 217 | }) 218 | }.width('96%') 219 | .height(30) 220 | 221 | Blank() 222 | .width('100%') 223 | .height(10) 224 | .backgroundColor('#f4f4f4') 225 | 226 | // 评价 227 | Row() { 228 | Row() { 229 | Text('评价') 230 | .fontSize(16) 231 | Text(`好评0%`) 232 | .fontSize(14) 233 | .fontColor(Color.Red) 234 | } 235 | 236 | Row() { 237 | Text(`共0条`) 238 | .fontColor(Color.Grey) 239 | Image($r('app.media.more')) 240 | .width(16) 241 | } 242 | 243 | }.width('96%') 244 | .height(40) 245 | .justifyContent(FlexAlign.SpaceBetween) 246 | 247 | Divider() 248 | 249 | // 标签 250 | Row() { 251 | // 全部 252 | this.buildTag('全部', 0) 253 | // 好评 254 | this.buildTag('好评', 0) 255 | // 差评 256 | this.buildTag('差评', 0) 257 | // 有图 258 | this.buildTag('有图', 0) 259 | 260 | }.width('100%') 261 | .height(40) 262 | 263 | Blank() 264 | .width('100%') 265 | .height(10) 266 | .backgroundColor('#f4f4f4') 267 | 268 | // 详情 269 | RichText(this.content) 270 | .height(2000) 271 | .padding(0) 272 | .width('130%') 273 | 274 | }.width('100%') 275 | } 276 | .height('100%') 277 | 278 | // 悬浮菜单 279 | Row() { 280 | Row() { 281 | Column() { 282 | Image($r('app.media.homepage')) 283 | .width(24) 284 | Text('首页') 285 | .fontSize(14) 286 | } 287 | .width('20%') 288 | .onClick(() => { 289 | router.replaceUrl({ 290 | url: 'pages/Index', 291 | params: { 292 | selectedIndex: 0 293 | } 294 | }) 295 | }) 296 | 297 | Blank() 298 | .width(1) 299 | .height('100%') 300 | .backgroundColor('#c3c3c3') 301 | 302 | Column() { 303 | Image($r('app.media.basket')) 304 | .width(24) 305 | Text('购物车') 306 | .fontSize(14) 307 | } 308 | .width('20%') 309 | .onClick(() => { 310 | router.replaceUrl({ 311 | url: 'pages/Index', 312 | params: { 313 | selectedIndex: 2 314 | } 315 | }) 316 | }) 317 | 318 | Row() { 319 | Text('加入购物车') 320 | .fontColor(Color.White) 321 | .onClick(() => { 322 | this.skuDialogController.open() 323 | }) 324 | } 325 | .backgroundColor('#584e61') 326 | .width('30%') 327 | .height('100%') 328 | .justifyContent(FlexAlign.Center) 329 | .alignItems(VerticalAlign.Center) 330 | 331 | Row() { 332 | Text('立即购买') 333 | .fontColor(Color.White) 334 | } 335 | .backgroundColor(Color.Red) 336 | .width('30%') 337 | .height('100%') 338 | .justifyContent(FlexAlign.Center) 339 | .alignItems(VerticalAlign.Center) 340 | } 341 | }.width('100%') 342 | .height(56) 343 | .backgroundColor(Color.White) 344 | .zIndex(2) 345 | 346 | } 347 | } 348 | } 349 | 350 | @CustomDialog 351 | struct SkuDialog { 352 | controller: CustomDialogController 353 | productDataModel: ProductDataModel 354 | skuList: SkuDataModel[] 355 | versions: string[] 356 | colors: string[] 357 | mems: string[] 358 | @Link selectedCount: number 359 | @State @Watch('onSkuChanged') curVersionIndex: number = 0 360 | @State @Watch('onSkuChanged') curColorIndex: number = 0 361 | @State @Watch('onSkuChanged') curMemIndex: number = 0 362 | @Link selectedSku: SkuDataModel 363 | 364 | @State isEmptyProperties: Boolean = true 365 | 366 | onSkuChanged() { 367 | // 获取版本,颜色,内存的具体值并组装,反向查询是哪一个商品 368 | const version = this.versions[this.curVersionIndex] 369 | const color = this.colors[this.curColorIndex] 370 | const mem = this.mems[this.curMemIndex] 371 | const propertiesStr = `版本:${version};颜色:${color};内存:${mem}` 372 | 373 | const skuDataModels: SkuDataModel[] = this.skuList.filter(it => it.properties == propertiesStr) 374 | if (skuDataModels.length > 0) { 375 | this.selectedSku = skuDataModels[0] 376 | } 377 | 378 | } 379 | 380 | aboutToAppear() { 381 | // 让UI产生变化 382 | this.onSkuChanged() 383 | } 384 | 385 | build() { 386 | // sku选择 387 | Stack({ alignContent: Alignment.Bottom }) { 388 | Column() { 389 | // 图片行 390 | Row() { 391 | Row() { 392 | Image(this.selectedSku.pic) 393 | .width(80) 394 | Column() { 395 | Text(`¥ ${this.selectedSku.price}`) 396 | .fontColor(Color.Red) 397 | .fontSize(12) 398 | Row() { 399 | Text('已选 ') 400 | .fontSize(12) 401 | .fontColor(Color.Grey) 402 | Text(`${this.selectedCount} 件`) 403 | .fontSize(12) 404 | } 405 | } 406 | } 407 | 408 | Text(" × ") 409 | .fontSize(25) 410 | .margin({ right: 12 }) 411 | .fontColor(Color.Grey) 412 | .offset({y: -20}) 413 | .onClick(() => { 414 | this.controller.close() 415 | }) 416 | }.width('100%') 417 | .justifyContent(FlexAlign.SpaceBetween) 418 | 419 | // 版本 420 | Column() { 421 | Row() { 422 | Text('版本') 423 | .fontColor(Color.Grey) 424 | .fontSize(12) 425 | }.width('100%') 426 | 427 | Row() { 428 | ForEach(Array.from(this.versions), (version: string, index: number) => { 429 | Button(version) 430 | .type(ButtonType.Normal) 431 | .fontColor(Color.White) 432 | .padding({ left: 4, right: 4 }) 433 | .fontSize(12) 434 | .borderRadius(3) 435 | .margin({ right: 10 }) 436 | .backgroundColor(this.curVersionIndex == index ? Color.Red : '#c3c3c3') 437 | .onClick(() => { 438 | this.curVersionIndex = index 439 | }) 440 | }) 441 | }.width('100%') 442 | .margin({ top: 10 }) 443 | }.width('100%') 444 | .margin({top: 10}) 445 | 446 | // 颜色 447 | Column() { 448 | Row() { 449 | Text('颜色') 450 | .fontColor(Color.Grey) 451 | .fontSize(12) 452 | }.width('100%') 453 | .margin({ top: 10 }) 454 | 455 | Row() { 456 | ForEach(Array.from(this.colors), (color: string, index: number) => { 457 | Button(color) 458 | .type(ButtonType.Normal) 459 | .fontColor(Color.White) 460 | .padding({ left: 4, right: 4 }) 461 | .fontSize(12) 462 | .borderRadius(3) 463 | .margin({ right: 10 }) 464 | .backgroundColor(this.curColorIndex == index ? Color.Red : '#c3c3c3') 465 | .onClick(() => { 466 | this.curColorIndex = index 467 | }) 468 | }) 469 | }.width('100%') 470 | .margin({ top: 10 }) 471 | }.width('100%') 472 | 473 | // 内存 474 | Column() { 475 | Row() { 476 | Text('内存') 477 | .fontColor(Color.Grey) 478 | .fontSize(12) 479 | }.width('100%') 480 | .margin({ top: 10 }) 481 | 482 | Row() { 483 | ForEach(Array.from(this.mems), (mem: string, index: number) => { 484 | Button(mem) 485 | .type(ButtonType.Normal) 486 | .fontColor(Color.White) 487 | .padding({ left: 4, right: 4 }) 488 | .fontSize(12) 489 | .borderRadius(3) 490 | .margin({ right: 10 }) 491 | .backgroundColor(this.curMemIndex == index ? Color.Red : '#c3c3c3') 492 | .onClick(() => { 493 | this.curMemIndex = index 494 | }) 495 | }) 496 | }.width('100%') 497 | .margin({ top: 10 }) 498 | }.width('100%') 499 | 500 | // 数量 501 | Row() { 502 | Text('数量') 503 | .fontColor(Color.Grey) 504 | 505 | NumberInput({ value: $selectedCount, min: 1, max: 100 }) 506 | 507 | }.width('100%') 508 | .justifyContent(FlexAlign.SpaceBetween) 509 | .margin({ top: 10 }) 510 | 511 | }.width('100%') 512 | .padding({ bottom: 60, left: 10 }) 513 | 514 | // 固定底部的按钮 515 | Row() { 516 | Button('加入购物车') 517 | .type(ButtonType.Normal) 518 | .width('50%') 519 | .backgroundColor('#584e61') 520 | .onClick(() => { 521 | const addCartReqModel = new AddCartReqModel(0, this.selectedCount, this.productDataModel.prodId, this.productDataModel.shopId, this.selectedSku.skuId) 522 | cartApi.add(addCartReqModel) 523 | this.controller.close() 524 | Prompt.showToast({ 525 | message: '加入购物车成功' 526 | }) 527 | }) 528 | Button('立即购买') 529 | .type(ButtonType.Normal) 530 | .layoutWeight(1) 531 | .backgroundColor('#eb2444') 532 | }.width('100%') 533 | }.width('100%') 534 | .backgroundColor(Color.White) 535 | .zIndex(3) 536 | } 537 | } 538 | -------------------------------------------------------------------------------- /entry/src/main/ets/pages/ProductList.ets: -------------------------------------------------------------------------------- 1 | // 商品列表组件 2 | import router from '@ohos.router' 3 | import { categoryApi, productApi } from '../api/Api' 4 | import CategoryDataModel from '../datamodels/CategoryDataModel' 5 | import ProductDataModel from '../datamodels/ProductDataModel' 6 | 7 | @Entry 8 | @Component 9 | struct ProductList { 10 | parentCategoryDataModel: CategoryDataModel 11 | @State categories: CategoryDataModel[] = [] 12 | @State selectedIndex: number = 0 13 | 14 | async aboutToAppear() { 15 | this.parentCategoryDataModel = router.getParams()["category"] as CategoryDataModel 16 | // 这样在第一次进入页面后tabbar可以切换到正确的索引,但是tabcontent不会切换,只能手工切换 17 | this.selectedIndex = router.getParams()["index"] as number 18 | this.categories = await categoryApi.categories(this.parentCategoryDataModel.categoryId) 19 | } 20 | 21 | @Builder 22 | buildTabbar(categoryDataModel: CategoryDataModel, index: number) { 23 | Column() { 24 | Text(categoryDataModel.categoryName) 25 | .fontColor(this.selectedIndex == index ? Color.Red : Color.Black) 26 | Divider() 27 | .width('100%') 28 | .height(this.selectedIndex == index ? 4 : 0) 29 | .backgroundColor(this.selectedIndex == index ? Color.Red : '#f5f6f7') 30 | .margin({ top: 6 }) 31 | } 32 | .height(50) 33 | .backgroundColor(Color.White) 34 | } 35 | 36 | build() { 37 | Column() { 38 | Row() { 39 | Image($r('app.media.more')) 40 | .width(16) 41 | .rotate({ 42 | angle: 180 43 | }) 44 | 45 | Text('返回') 46 | .margin({ left: 5 }) 47 | .fontSize(14) 48 | } 49 | .width('100%') 50 | .margin({ top: 10, left: 10 }) 51 | .backgroundColor(Color.White) 52 | .onClick(() => { 53 | router.back() 54 | }) 55 | 56 | Tabs() { 57 | ForEach(this.categories, (category: CategoryDataModel, index: number) => { 58 | TabContent() { 59 | ProductCell({ categoryId: category.categoryId }) 60 | .backgroundColor('#f5f6f7') 61 | } 62 | .tabBar(this.buildTabbar(category, index)) 63 | }) 64 | 65 | } 66 | .scrollable(true) 67 | .onChange((index: number) => { 68 | this.selectedIndex = index 69 | }) 70 | .backgroundColor(Color.White) 71 | 72 | }.width('100%') 73 | } 74 | } 75 | 76 | @Component 77 | struct ProductCell { 78 | categoryId: number 79 | @State products: ProductDataModel[] = [] 80 | 81 | async aboutToAppear() { 82 | this.products = (await productApi.getCategoryProductData(this.categoryId))['records'] 83 | } 84 | 85 | build() { 86 | Column() { 87 | Grid() { 88 | ForEach(this.products, (product: ProductDataModel) => { 89 | GridItem() { 90 | Column() { 91 | Image(product.pic) 92 | .width(150) 93 | Text(product.prodName) 94 | .fontSize(14) 95 | .maxLines(1) 96 | .textOverflow({ 97 | overflow: TextOverflow.Ellipsis 98 | }) 99 | Text(product.brief) 100 | .fontSize(12) 101 | .fontColor(Color.Grey) 102 | Text(`¥ ` + product.price) 103 | .fontSize(12) 104 | } 105 | .width('100%') 106 | .alignItems(HorizontalAlign.Start) 107 | .padding({ left: 6 }) 108 | .onClick(() => { 109 | router.pushUrl({ 110 | url: 'pages/ProductDetail', 111 | params: { 112 | prodId: product.prodId 113 | } 114 | }) 115 | }) 116 | }.width('92%') 117 | .backgroundColor(Color.White) 118 | }) 119 | } 120 | .width('100%') 121 | .rowsTemplate("1fr 1fr") 122 | .columnsTemplate("1fr 1fr") 123 | .rowsGap(10) 124 | .columnsGap(10) 125 | .margin({ top: 10 }) 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/SearchProduct.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import { productApi } from '../api/Api' 3 | import ProductListItem from '../components/ProductListItem' 4 | import AppConst from '../configurations/AppConst' 5 | import ProductDataModel from '../datamodels/ProductDataModel' 6 | import PreferencesUtil from '../utils/PreferencesUtil' 7 | 8 | @Entry 9 | @Component 10 | struct SearchProduct { 11 | @State searchText: string = '' 12 | 13 | @State searchHisArr: string[] = [] 14 | 15 | @State data: ProductDataModel[] = [] 16 | 17 | private pageSize: number = 10 18 | @State current: number = 1 19 | 20 | async aboutToAppear() { 21 | const searchHis = await PreferencesUtil.getPreference(AppConst.PREFERENCES_SEARCH_HIS) 22 | console.log("searchHis", JSON.stringify(searchHis)) 23 | if (searchHis) { 24 | this.searchHisArr = searchHis.split(',') 25 | } 26 | } 27 | 28 | // 搜索商品 29 | async searchProds() { 30 | this.data = (await productApi.search(this.current, this.searchText, this.pageSize))['records'] 31 | console.log("searchProds:", JSON.stringify(this.data)) 32 | this.searchHisArr.push(this.searchText) 33 | await PreferencesUtil.setPreference(AppConst.PREFERENCES_SEARCH_HIS, this.searchHisArr.join(",")) 34 | } 35 | 36 | @Builder 37 | buildHisTag(his: string) { 38 | Text(his) 39 | .backgroundColor('#f2f2f2') 40 | .fontSize(12) 41 | .padding({ top: 4, bottom: 4, left: 8, right: 8 }) 42 | .borderRadius(6) 43 | .margin({ right: 6 }) 44 | .onClick(() => { 45 | this.searchText = his 46 | }) 47 | } 48 | 49 | build() { 50 | Column() { 51 | Row() { 52 | Image($r('app.media.more')) 53 | .width(16) 54 | .margin({ left: 10 }) 55 | .rotate({ 56 | angle: 180 57 | }).onClick(() => { 58 | router.back() 59 | }) 60 | Row() { 61 | Text('商品搜索') 62 | .fontSize(20) 63 | .fontWeight(600) 64 | }.layoutWeight(1) 65 | .justifyContent(FlexAlign.Center) 66 | 67 | Blank() 68 | .width(20) 69 | }.width('100%') 70 | .backgroundColor(Color.White) 71 | .justifyContent(FlexAlign.SpaceAround) 72 | .padding(10) 73 | 74 | // 搜索框 75 | Row() { 76 | Row() { 77 | Image($r('app.media.search')) 78 | .width(16) 79 | TextInput({ text: this.searchText, placeholder: '输入关键字搜索' }) 80 | .backgroundColor(Color.Transparent) 81 | .stateStyles({ 82 | normal: { 83 | .backgroundColor(Color.Transparent) 84 | }, 85 | pressed: { 86 | .backgroundColor(Color.Transparent) 87 | }, 88 | focused: { 89 | .backgroundColor(Color.Transparent) 90 | }, 91 | clicked: { 92 | .backgroundColor(Color.Transparent) 93 | }, 94 | disabled: { 95 | .backgroundColor(Color.Transparent) 96 | } 97 | }) 98 | .onChange((value) => { 99 | this.searchText = value 100 | }) 101 | 102 | }.backgroundColor('#f7f7f7') 103 | .borderRadius(10) 104 | .padding(4) 105 | .layoutWeight(1) 106 | 107 | Text('搜索') 108 | .fontSize(14) 109 | .fontColor(Color.Red) 110 | .width(30) 111 | .margin({ left: 10, right: 10 }) 112 | .onClick(async () => { 113 | if (this.searchText == '') { 114 | return 115 | } 116 | // 搜索商品 117 | await this.searchProds() 118 | }) 119 | }.width('100%') 120 | .padding(8) 121 | .justifyContent(FlexAlign.SpaceBetween) 122 | 123 | Divider() 124 | .color(Color.Gray) 125 | .width('100%') 126 | .opacity(0.3) 127 | Column() { 128 | if (this.searchText == '') { 129 | Row() { 130 | Text('热门搜索') 131 | .fontSize(14) 132 | } 133 | .width('90%') 134 | 135 | Row() { 136 | Text('暂无数据') 137 | .fontSize(12) 138 | }.width('90%') 139 | .justifyContent(FlexAlign.Center) 140 | 141 | Divider() 142 | .width('90%') 143 | .opacity(0.3) 144 | .margin({ top: 6, bottom: 10 }) 145 | 146 | Row() { 147 | Text('搜索历史') 148 | .fontSize(14) 149 | Image($r('app.media.clear_his')) 150 | .width(20) 151 | }.width('90%') 152 | .justifyContent(FlexAlign.SpaceBetween) 153 | // 历史列表 154 | Flex({ wrap: FlexWrap.Wrap }) { 155 | ForEach(this.searchHisArr, (his: string, index: number) => { 156 | this.buildHisTag(his) 157 | }) 158 | }.width('90%') 159 | } else { 160 | List() { 161 | ForEach(this.data, (productDataModel: ProductDataModel) => { 162 | ListItem() { 163 | ProductListItem({productDataModel: productDataModel}) 164 | } 165 | }) 166 | }.width('100%') 167 | } 168 | }.width('100%') 169 | .margin({ top: 10 }) 170 | .justifyContent(FlexAlign.Start) 171 | 172 | }.width('100%') 173 | } 174 | } -------------------------------------------------------------------------------- /entry/src/main/ets/utils/CryptoJSUtil.ets: -------------------------------------------------------------------------------- 1 | import CryptoJS from '@ohos/crypto-js' 2 | 3 | class CryptoJSUtil { 4 | // 加密 5 | private keyStr = '-mall4j-password' // 解密用的key 6 | encrypt(word: string) { 7 | const time = Date.now() 8 | 9 | const key = CryptoJS.enc.Utf8.parse(this.keyStr) 10 | const srcs = CryptoJS.enc.Utf8.parse(time + word) // 加密方式: 时间戳 + 密文 11 | const encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }) 12 | return encrypted.toString() 13 | } 14 | } 15 | 16 | export default new CryptoJSUtil() -------------------------------------------------------------------------------- /entry/src/main/ets/utils/PreferencesUtil.ts: -------------------------------------------------------------------------------- 1 | import dataPreferences from '@ohos.data.preferences' 2 | 3 | class PreferencesUtil { 4 | private static preferenceName: string = 'mall4j' 5 | private preferencesInstance = null 6 | 7 | private getPreferencesInstance() { 8 | return this.preferencesInstance 9 | } 10 | 11 | // 设置首选项要用到的上下文 12 | async setInstanceContext(context) { 13 | try { 14 | this.preferencesInstance = await dataPreferences.getPreferences(context, PreferencesUtil.preferenceName); 15 | console.log('初始化首选项成功') 16 | } catch (e) { 17 | console.error('初始化首选项失败:', e.code, e.message) 18 | } 19 | } 20 | 21 | // 获取首选项 22 | async getPreference(key: string, defaultVal = '') { 23 | try { 24 | return await this.getPreferencesInstance().get(key, defaultVal) 25 | } catch (e) { 26 | console.error('获取用户首选项失败:', e.code, e.message) 27 | return defaultVal 28 | } 29 | } 30 | 31 | // 设置首选项 32 | async setPreference(key: string, value: any) { 33 | try { 34 | console.log('开始保存首选项') 35 | if (await this.getPreferencesInstance().has(key)) { 36 | await this.deletePreference(key) 37 | } 38 | await this.getPreferencesInstance().put(key, value) 39 | // 持久化 40 | await this.getPreferencesInstance().flush() 41 | console.log(`保存到首选项的${key}的值:`, await this.getPreference(key)) 42 | } 43 | catch (e) { 44 | console.error('保存首选项失败:', e.code, e.message) 45 | } 46 | } 47 | 48 | 49 | // 删除首选项 50 | async deletePreference(key: string) { 51 | await this.getPreferencesInstance().delete(key) 52 | // 持久化 53 | await this.getPreferencesInstance().flush() 54 | 55 | } 56 | } 57 | 58 | export default new PreferencesUtil() -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodels/Brand.ets: -------------------------------------------------------------------------------- 1 | export default class Brand { 2 | image: Resource 3 | title: string 4 | 5 | constructor(image: Resource, title: string) { 6 | this.image = image 7 | this.title = title 8 | } 9 | } -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodels/TabBarViewModel.ets: -------------------------------------------------------------------------------- 1 | export default class TabBarViewModel { 2 | title: string 3 | image: Resource 4 | selectedImage: Resource 5 | index: number 6 | 7 | constructor(title: string, image: Resource, selectedImage: Resource, index: number) { 8 | this.title = title 9 | this.image = image 10 | this.selectedImage = selectedImage 11 | this.index = index 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /entry/src/main/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "entry", 4 | "type": "entry", 5 | "description": "$string:module_desc", 6 | "mainElement": "EntryAbility", 7 | "deviceTypes": [ 8 | "phone", 9 | "tablet" 10 | ], 11 | "deliveryWithInstall": true, 12 | "installationFree": false, 13 | "pages": "$profile:main_pages", 14 | "requestPermissions": [ 15 | { 16 | "name":"ohos.permission.INTERNET" 17 | } 18 | ], 19 | "abilities": [ 20 | { 21 | "name": "EntryAbility", 22 | "srcEntry": "./ets/entryability/EntryAbility.ts", 23 | "description": "$string:EntryAbility_desc", 24 | "icon": "$media:icon", 25 | "label": "$string:EntryAbility_label", 26 | "startWindowIcon": "$media:icon", 27 | "startWindowBackground": "$color:start_window_background", 28 | "exported": true, 29 | "skills": [ 30 | { 31 | "entities": [ 32 | "entity.system.home" 33 | ], 34 | "actions": [ 35 | "action.system.home" 36 | ] 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/element/color.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": [ 3 | { 4 | "name": "start_window_background", 5 | "value": "#FFFFFF" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "module description" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/addr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/addr.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/basket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/basket.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/basket_sel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/basket_sel.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/bg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/bg1.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/car.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/car_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/car_new.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/category.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/category.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/category_sel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/category_sel.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/clear_his.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/clear_his.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/coupon_ot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/coupon_ot.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/coupon_used.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/coupon_used.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/delive_dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/delive_dot.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/delivery_car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/delivery_car.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/dot.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/empty_cash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/empty_cash.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/everydaySale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/everydaySale.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/getCoupon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/getCoupon.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/head04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/head04.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/homepage.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/homepage_sel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/homepage_sel.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/horn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/horn.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/icon.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/logo.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/menu_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/menu_01.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/menu_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/menu_02.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/menu_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/menu_03.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/menu_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/menu_04.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/more.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/myAddr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/myAddr.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/myCoupon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/myCoupon.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/newProd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/newProd.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/neweveryday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/neweveryday.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/newprods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/newprods.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/plus_sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/plus_sign.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/prod_col.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/prod_col.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/prod_col_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/prod_col_red.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/promotion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/promotion.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/revise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/revise.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/search.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/search_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/search_01.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/search_col.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/search_col.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/search_col2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/search_col2.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/star_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/star_empty.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/star_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/star_red.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/timePrice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/timePrice.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/toComment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/toComment.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/toDelivery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/toDelivery.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/toPay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/toPay.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/toTake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/toTake.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/tuiguang01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/tuiguang01.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/tuiguang02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/tuiguang02.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/tuiguang03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/tuiguang03.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/user.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/user_sel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/main/resources/base/media/user_sel.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/profile/main_pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": [ 3 | "pages/Index", 4 | "pages/Login", 5 | "pages/ProductList", 6 | "pages/ProductDetail", 7 | "pages/ConfirmOrder", 8 | "pages/Addr", 9 | "pages/EditAddr", 10 | "pages/Order", 11 | "pages/PaySuccess", 12 | "pages/SearchProduct" 13 | ] 14 | } -------------------------------------------------------------------------------- /entry/src/main/resources/en_US/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "module description" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /entry/src/main/resources/zh_CN/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "模块描述" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/test/Ability.test.ets: -------------------------------------------------------------------------------- 1 | import hilog from '@ohos.hilog'; 2 | import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' 3 | 4 | export default function abilityTest() { 5 | describe('ActsAbilityTest', function () { 6 | // Defines a test suite. Two parameters are supported: test suite name and test suite function. 7 | beforeAll(function () { 8 | // Presets an action, which is performed only once before all test cases of the test suite start. 9 | // This API supports only one parameter: preset action function. 10 | }) 11 | beforeEach(function () { 12 | // Presets an action, which is performed before each unit test case starts. 13 | // The number of execution times is the same as the number of test cases defined by **it**. 14 | // This API supports only one parameter: preset action function. 15 | }) 16 | afterEach(function () { 17 | // Presets a clear action, which is performed after each unit test case ends. 18 | // The number of execution times is the same as the number of test cases defined by **it**. 19 | // This API supports only one parameter: clear action function. 20 | }) 21 | afterAll(function () { 22 | // Presets a clear action, which is performed after all test cases of the test suite end. 23 | // This API supports only one parameter: clear action function. 24 | }) 25 | it('assertContain',0, function () { 26 | // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. 27 | hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); 28 | let a = 'abc' 29 | let b = 'b' 30 | // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 31 | expect(a).assertContain(b) 32 | expect(a).assertEqual(a) 33 | }) 34 | }) 35 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/test/List.test.ets: -------------------------------------------------------------------------------- 1 | import abilityTest from './Ability.test' 2 | 3 | export default function testsuite() { 4 | abilityTest() 5 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/testability/TestAbility.ets: -------------------------------------------------------------------------------- 1 | import UIAbility from '@ohos.app.ability.UIAbility'; 2 | import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; 3 | import hilog from '@ohos.hilog'; 4 | import { Hypium } from '@ohos/hypium'; 5 | import testsuite from '../test/List.test'; 6 | import window from '@ohos.window'; 7 | 8 | export default class TestAbility extends UIAbility { 9 | onCreate(want, launchParam) { 10 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); 11 | hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); 12 | hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? ''); 13 | var abilityDelegator: any 14 | abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() 15 | var abilityDelegatorArguments: any 16 | abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() 17 | hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); 18 | Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) 19 | } 20 | 21 | onDestroy() { 22 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); 23 | } 24 | 25 | onWindowStageCreate(windowStage: window.WindowStage) { 26 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); 27 | windowStage.loadContent('testability/pages/Index', (err, data) => { 28 | if (err.code) { 29 | hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 30 | return; 31 | } 32 | hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', 33 | JSON.stringify(data) ?? ''); 34 | }); 35 | } 36 | 37 | onWindowStageDestroy() { 38 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); 39 | } 40 | 41 | onForeground() { 42 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); 43 | } 44 | 45 | onBackground() { 46 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); 47 | } 48 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/testability/pages/Index.ets: -------------------------------------------------------------------------------- 1 | import hilog from '@ohos.hilog'; 2 | 3 | @Entry 4 | @Component 5 | struct Index { 6 | aboutToAppear() { 7 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); 8 | } 9 | @State message: string = 'Hello World' 10 | build() { 11 | Row() { 12 | Column() { 13 | Text(this.message) 14 | .fontSize(50) 15 | .fontWeight(FontWeight.Bold) 16 | Button() { 17 | Text('next page') 18 | .fontSize(20) 19 | .fontWeight(FontWeight.Bold) 20 | }.type(ButtonType.Capsule) 21 | .margin({ 22 | top: 20 23 | }) 24 | .backgroundColor('#0D9FFB') 25 | .width('35%') 26 | .height('5%') 27 | .onClick(()=>{ 28 | }) 29 | } 30 | .width('100%') 31 | } 32 | .height('100%') 33 | } 34 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts: -------------------------------------------------------------------------------- 1 | import hilog from '@ohos.hilog'; 2 | import TestRunner from '@ohos.application.testRunner'; 3 | import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; 4 | 5 | var abilityDelegator = undefined 6 | var abilityDelegatorArguments = undefined 7 | 8 | async function onAbilityCreateCallback() { 9 | hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); 10 | } 11 | 12 | async function addAbilityMonitorCallback(err: any) { 13 | hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); 14 | } 15 | 16 | export default class OpenHarmonyTestRunner implements TestRunner { 17 | constructor() { 18 | } 19 | 20 | onPrepare() { 21 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); 22 | } 23 | 24 | async onRun() { 25 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); 26 | abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() 27 | abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() 28 | var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' 29 | let lMonitor = { 30 | abilityName: testAbilityName, 31 | onAbilityCreate: onAbilityCreateCallback, 32 | }; 33 | abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) 34 | var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName 35 | var debug = abilityDelegatorArguments.parameters['-D'] 36 | if (debug == 'true') 37 | { 38 | cmd += ' -D' 39 | } 40 | hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); 41 | abilityDelegator.executeShellCommand(cmd, 42 | (err: any, d: any) => { 43 | hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); 44 | hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); 45 | hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); 46 | }) 47 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); 48 | } 49 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "entry_test", 4 | "type": "feature", 5 | "description": "$string:module_test_desc", 6 | "mainElement": "TestAbility", 7 | "deviceTypes": [ 8 | "phone", 9 | "tablet" 10 | ], 11 | "deliveryWithInstall": true, 12 | "installationFree": false, 13 | "pages": "$profile:test_pages", 14 | "abilities": [ 15 | { 16 | "name": "TestAbility", 17 | "srcEntry": "./ets/testability/TestAbility.ets", 18 | "description": "$string:TestAbility_desc", 19 | "icon": "$media:icon", 20 | "label": "$string:TestAbility_label", 21 | "exported": true, 22 | "startWindowIcon": "$media:icon", 23 | "startWindowBackground": "$color:start_window_background", 24 | "skills": [ 25 | { 26 | "actions": [ 27 | "action.system.home" 28 | ], 29 | "entities": [ 30 | "entity.system.home" 31 | ] 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /entry/src/ohosTest/resources/base/element/color.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": [ 3 | { 4 | "name": "start_window_background", 5 | "value": "#FFFFFF" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_test_desc", 5 | "value": "test ability description" 6 | }, 7 | { 8 | "name": "TestAbility_desc", 9 | "value": "the test ability" 10 | }, 11 | { 12 | "name": "TestAbility_label", 13 | "value": "test label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/resources/base/media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/entry/src/ohosTest/resources/base/media/icon.png -------------------------------------------------------------------------------- /entry/src/ohosTest/resources/base/profile/test_pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": [ 3 | "testability/pages/Index" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /hvigor/hvigor-config.json5: -------------------------------------------------------------------------------- 1 | { 2 | "hvigorVersion": "2.4.2", 3 | "dependencies": { 4 | "@ohos/hvigor-ohos-plugin": "2.4.2" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /hvigorfile.ts: -------------------------------------------------------------------------------- 1 | // Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. 2 | export { appTasks } from '@ohos/hvigor-ohos-plugin'; -------------------------------------------------------------------------------- /hvigorw: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ---------------------------------------------------------------------------- 4 | # Hvigor startup script, version 1.0.0 5 | # 6 | # Required ENV vars: 7 | # ------------------ 8 | # NODE_HOME - location of a Node home dir 9 | # or 10 | # Add /usr/local/nodejs/bin to the PATH environment variable 11 | # ---------------------------------------------------------------------------- 12 | 13 | HVIGOR_APP_HOME=$(dirname $(readlink -f $0)) 14 | HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js 15 | warn() { 16 | echo "" 17 | echo -e "\033[1;33m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" 18 | } 19 | 20 | error() { 21 | echo "" 22 | echo -e "\033[1;31m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" 23 | } 24 | 25 | fail() { 26 | error "$@" 27 | exit 1 28 | } 29 | 30 | # Determine node to start hvigor wrapper script 31 | if [ -n "${NODE_HOME}" ];then 32 | EXECUTABLE_NODE="${NODE_HOME}/bin/node" 33 | if [ ! -x "$EXECUTABLE_NODE" ];then 34 | fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed" 35 | fi 36 | else 37 | EXECUTABLE_NODE="node" 38 | which ${EXECUTABLE_NODE} > /dev/null 2>&1 || fail "ERROR: NODE_HOME is not set and not 'node' command found in your path" 39 | fi 40 | 41 | # Check hvigor wrapper script 42 | if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ];then 43 | fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}" 44 | fi 45 | 46 | # start hvigor-wrapper script 47 | exec "${EXECUTABLE_NODE}" \ 48 | "${HVIGOR_WRAPPER_SCRIPT}" "$@" 49 | -------------------------------------------------------------------------------- /hvigorw.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Hvigor startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 17 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 18 | 19 | set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js 20 | set NODE_EXE=node.exe 21 | 22 | goto start 23 | 24 | :start 25 | @rem Find node.exe 26 | if defined NODE_HOME goto findNodeFromNodeHome 27 | 28 | %NODE_EXE% --version >NUL 2>&1 29 | if "%ERRORLEVEL%" == "0" goto execute 30 | 31 | echo. 32 | echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. 33 | echo. 34 | echo Please set the NODE_HOME variable in your environment to match the 35 | echo location of your NodeJs installation. 36 | 37 | goto fail 38 | 39 | :findNodeFromNodeHome 40 | set NODE_HOME=%NODE_HOME:"=% 41 | set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE% 42 | 43 | if exist "%NODE_EXE_PATH%" goto execute 44 | echo. 45 | echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. 46 | echo. 47 | echo Please set the NODE_HOME variable in your environment to match the 48 | echo location of your NodeJs installation. 49 | 50 | goto fail 51 | 52 | :execute 53 | @rem Execute hvigor 54 | "%NODE_EXE%" %WRAPPER_MODULE_PATH% %* 55 | 56 | if "%ERRORLEVEL%" == "0" goto hvigorwEnd 57 | 58 | :fail 59 | exit /b 1 60 | 61 | :hvigorwEnd 62 | if "%OS%" == "Windows_NT" endlocal 63 | 64 | :end 65 | -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by DevEco Studio. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file should *NOT* be checked into Version Control Systems, 5 | # as it contains information specific to your local configuration. 6 | # 7 | # For customization when using a Version Control System, please read the header note. 8 | hwsdk.dir=D:/Dev/huawei/HarmonyOSSDK 9 | nodejs.dir=D:/Dev/huawei/nodejs 10 | -------------------------------------------------------------------------------- /oh-package-lock.json5: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", 4 | "specifiers": { 5 | "@ohos/hypium@1.0.6": "@ohos/hypium@1.0.6", 6 | "@ohos/crypto-js@^2.0.3": "@ohos/crypto-js@2.0.3", 7 | "@ohos/axios@^2.2.0": "@ohos/axios@2.2.0" 8 | }, 9 | "packages": { 10 | "@ohos/hypium@1.0.6": { 11 | "resolved": "https://repo.harmonyos.com/ohpm/@ohos/hypium/-/hypium-1.0.6.tgz", 12 | "integrity": "sha512-bb3DWeWhYrFqj9mPFV3yZQpkm36kbcK+YYaeY9g292QKSjOdmhEIQR2ULPvyMsgSR4usOBf5nnYrDmaCCXirgQ==" 13 | }, 14 | "@ohos/crypto-js@2.0.3": { 15 | "resolved": "https://repo.harmonyos.com/ohpm/@ohos/crypto-js/-/crypto-js-2.0.3.har", 16 | "integrity": "sha512-LuHaR1kD5PxnOXnuR1fWvPwGtbed9Q/QGzk6JOh8y5Wdzvi8brPesODZiaWf9scOVRHsbTPOtZw91vWB35p1vQ==" 17 | }, 18 | "@ohos/axios@2.2.0": { 19 | "resolved": "https://repo.harmonyos.com/ohpm/@ohos/axios/-/axios-2.2.0.har", 20 | "integrity": "sha512-v1QBWk6DfcN8wUW3D0ieFbHTR1taSI5cOgxp5l6B5cegXuNYhSc8ggKlAIXe6h/14LsfM+NW0ZGfSXcNEIM5yA==" 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | "license": "", 3 | "devDependencies": { 4 | "@ohos/hypium": "1.0.6" 5 | }, 6 | "author": "", 7 | "name": "mall4j", 8 | "description": "Please describe the basic information.", 9 | "main": "", 10 | "version": "1.0.0", 11 | "dependencies": { 12 | "@ohos/crypto-js": "^2.0.3", 13 | "@ohos/axios": "^2.2.0" 14 | } 15 | } -------------------------------------------------------------------------------- /preview/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/preview/01.png -------------------------------------------------------------------------------- /preview/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/preview/02.png -------------------------------------------------------------------------------- /preview/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/preview/03.png -------------------------------------------------------------------------------- /preview/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/preview/04.png -------------------------------------------------------------------------------- /preview/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/preview/05.png -------------------------------------------------------------------------------- /preview/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/preview/06.png -------------------------------------------------------------------------------- /preview/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/preview/07.png -------------------------------------------------------------------------------- /preview/08png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiuxuanzhi/HarmonyOS-mall/7e49c993066676da90ff6968bc91a83669a523f7/preview/08png.png --------------------------------------------------------------------------------