跳到主要内容

3 篇博文 含有标签「UI库」

查看所有标签

duxapp框架是如何实现对鸿蒙的兼容,开发过程中遇到了哪些问题

· 阅读需 13 分钟
duxapp
duxapp框架作者

Taro 4.0 版本已经搭建好了兼容鸿蒙的基础框架,duxapp 是在这个基础上进一步开发,降低了开发者上手门槛,提供了统一的 UI 库。

duxapp 是一个模块化的多端开发框架,其中提供了多端 UI 库,能真正实现一套代码开发小程序、React Native、H5、鸿蒙

兼容鸿蒙之前

在开始兼容鸿蒙之前,我们来讲一下,duxapp 是一个什么状况

1、样式兼容性

现在 duxapp 已经兼容 小程序、React Native以及H5了,因为 React Native 端在这些端中兼容的样式是最少的,我们是按照React Native的标准去做,我们针对 H5 端和 小程序端的基础样式做了调整

需要将 View 组件调整为默认 Flex 竖向布局、默认的定位方式、模型盒、以及边框等,像下面这样:

/* H5 端 */
taro-view-core {
display: flex;
flex-direction: column;
position: relative;
border-style: solid;
border-width: 0;
}
input,
textarea,
taro-view-core {
box-sizing: border-box;
}
/* 小程序端 */
view {
display: flex;
flex-direction: column;
position: relative;
border-style: solid;
border-width: 0;
}
input,
textarea,
view {
box-sizing: border-box;
}

通过这样的调整,我们在项目中使用 View 组件的时候将默认使用 Flex 竖向布局,这样编写的 css 就能做到同时兼容多个端了

2、现有模块

duxapp 框架中现在已提供了很多的公开模块了,这里介绍三个相关的基础模块,这三个模块在我们兼容鸿蒙的过程中都有很大的关联

  • duxapp

这个和框架名称相同的模块,是所有模块的根模块,所有的模块都需要依赖于这个模块

这个模块内提供了一些非常基础的组件、函数、配置等,且一些东西是必须使用的

用来包裹页面的容器组件 TopView
路由跳转函数 route
导出的用户配置

  • duxui

这个是提供的UI组件库,模块内提供了大量兼容多端的组件库,使用这些组件,能快速开发出兼容多端的应用

  • duxappReactNative

React Native端支持,要兼容 React Native,必须使用这个模块,模块内有 React Native 端的三方依赖、用户协议、版本更新、权限管理等

Taro 和鸿蒙的现状

Taro 4.0 目前有两套实现鸿蒙的方案

  • @tarojs/plugin-platform-harmony-hybrid

webview方案,将 H5 运行在 webview 上,和小程序类似

  • @tarojs/plugin-platform-harmony-ets

duxapp 中为了更好的性能和体验,选择的的是这个方案

底层通过 arkts 以及 arkui 实现,将 Taro 的组件转化为对应的 arkui 里面对应的组件,并实现了 React 的运行时

例如 View 组件会通过 arkui 的 FlexRowColumn 这三个组件来实现,会根据编写的不同样式选择不同的原生组件去渲染,从这里也大概能看出,鸿蒙的 arkui 里面也是 Flex 布局

兼容过程

1、创建一个鸿蒙模块

就像上面介绍的 React Native 端那样,鸿蒙端也需要创建一个模块,用来处理鸿蒙端的基础内容,例如用到的三方依赖、配置插件等

这个模块的名称为 duxappHarmony

2、添加需要的三方依赖

目前鸿蒙端仅支持使用 vite 编译,因此鸿蒙端需要添加编译的相关的依赖,在模块内创建 package.json,内容如下

{
"scripts": {
"build:harmony": "duxapp runtime enterFile && duxapp harmony create && taro build --type harmony",
"dev:harmony": "npm run build:harmony -- --watch"
},
"dependencies": {
"@tarojs/plugin-platform-harmony-ets": "4.0.7",
"vite": "^5.4.10",
"terser": "^5.36.0",
"@tarojs/vite-runner": "4.0.5",
"@vitejs/plugin-react": "^4.3.3",
"vite-plugin-commonjs": "^0.10.3"
}
}

这个完整的文件中还包含了编译鸿蒙的两个命令

3、配置依赖项

添加了依赖这些依赖并不会自动起作用,需要配置 Taro 编译配置,我们在这个模块内创建一个 Taro 的配置文件 taro.config.js,内容如下

// eslint-disable-next-line import/no-commonjs
const commonjs = require('vite-plugin-commonjs').default

module.exports = {
// 鸿蒙编译插件
plugins: [
'@tarojs/plugin-platform-harmony-ets'
],
harmony: {
compiler: {
// 指定 vite 编译
type: 'vite',
vitePlugins: [
commonjs()
]
},
projectPath: './dist/harmony'
}
}

这里面配置了平台插件、指定使用 vite 编译、设置了项目目录

这其中还加入了一个 vite 插件,因为 vite 默认不支持 commonjs,但是 duxapp 项目中使用了commonjs,所以加入了这个插件

到此基础环境配置好了,但是我们还需要创建鸿蒙工程文件,Taro 没有提供这个内容

4、在cli内处理鸿蒙工程文件

在第 2 步的命令中,duxapp harmony create 有这个命令,我们就是通过这个命令来创建的鸿蒙工程文件,这样就不需要再通过鸿蒙的 IDE 再去手动操作创建了,这也是一个相对麻烦的过程

这个过程和 React Native 端是类似的,React Native端也是自动创建安卓和 ios 的工程文件,鸿蒙的工程文件创建会放在 dist/harmony 目录中

创建工程文件的过程中,可以通过配置或脚本对工程文件进行处理,例如鸿蒙端需要包名、版本号、app 名称等信息,都是通过配置文件来生成的

这里就不详细展开了,可以查看文档了解 鸿蒙入门文档

5、组件库兼容

上面的步骤处理完之后,框架的东西就处理完了,但是仍然是不可用的,因为要编写一个兼容多端的项目,现有的组件,还需要针对鸿蒙端做相应的兼容,要兼容的组件包括基础模块中的组件和 duxui 模块中的组件

鸿蒙端兼容是一个很费事的过程,对鸿蒙端的兼容性 React Native还难,难就难在,鸿蒙有一个非标准的 Flex

在上面介绍了 View 组件会被在不同的情况编译的 Flex Row Column,具体来说,

  • align-items 的属性值为 stretchdaseline 或者 flex-wrap 的属性值为 wrap会使用 Flex 组件
  • 否则当 flex-directionrow 或者 row-reverse 使用 Row 组件
  • 其他的情况使用 Column 组件

因为鸿蒙的 RowColumn 组件的align-items不支持 stretchdaseline,而 Row组件又不支持 wrap 换行

在使用 Row 或者 Column 的情况下,这和标准 Flex 布局基本没多大差别,问题就出在使用 Flex 组件这,这个组件有个奇怪的特性

Flex组件主轴默认不设置时撑满父容器,Column、Row组件主轴不设置时默认是跟随子节点大小。

这是官方文档的描述,这是一个很奇怪的特性,且没有办法通过设置取消,这导致很多奇怪的布局行为,这个效果类似于给元素永久设置了个 flex: 1,且比这个好厉害,就算有多层父元素,他也能把外层父元素撑开,感觉它才是爹!

在做兼容的时候,我总结了关于这个特性的兼容方式:

当 flex 的 alitn-items 为 stretch 或者 daseline 时,需要给当前元素指定尺寸,在任何能不用 alitn-items 的这两个属性的时候,都不要用这两个属性。如果要使用stretch,建议父元素使用 alitn-items: flex-start,子元素使用 align-self: stretch,这样的形式

当然,除了 Flex 布局这个最大的问题之外,还有很多与标准不太一样的地方,我整理总结了放在 兼容性章节,在开发的时候需要特别注意

可以说我在兼容鸿蒙的过程中,大部分时间都是在做组件库的兼容,和这些样式斗智斗勇

开始使用

通过上面这一系列的操作,duxapp 框架已经基本实现了兼容鸿蒙,且提供了大量可用的 UI 组件库

你可以快速初始化一个 UI 库的示例项目,你可以通过这个示例项目快速的查看到 UI 库在鸿蒙端以及其他端的效果

npx duxapp-cli create projectExample duxuiExample

在运行这个命令之前,你需要安装好 nodejs 20+ yarn git

创建项目后,进入项目运行以下命令

yarn dev:harmony --app=duxuiExample

编译完成后,使用 DevEco Studio 打开 dist/harmony 目录,这个目录就是一个原生鸿蒙项目

打开项目后,右上角工具栏区域,选择已经开启的虚拟机,并且点击右侧的运行按钮(三角符号),开始编译,编译后,会自动在虚拟机上启动这个 app

如果想继续开发项目,请查看鸿蒙入门文档

当前版本在 windows 系统上,仍然存在问题,能编译成功,但是启动 app 会报错,你可以先尝试使用其他系统进行编译,例如 Mac,这个问题将会在不久后修复

未来展望

目前对鸿蒙的兼容取得了初步的成效,但是也还有少数一些组件和功能还未实现兼容,像下面这些,将逐步完善这些组件或功能

  • PullView Modal 等组件动画效果
  • Picker 组件暂不支持
  • Sign 签名组件待完善
  • 录音组件录音功能待完善
  • 微信模块相关功能暂不支持
  • 地图暂不支持
  • List、Detail 组件待完善

Taro 目前开发中的版本,将放弃使用鸿蒙的 Flex 布局方式,转而使用更底层的实现方式,使用了 React Native 的布局引擎 Yoga,用过 Yoga 引擎来来布局,将引擎的计算结果再赋值到具体的组件上,这样就能极大改善鸿蒙布局兼容性问题,等待 Taro 的这套方案开源后,duxapp 也将跟进

如果你有兴趣,请继续查看文档了解详情吧

开发文档
GitHub

Taro首个支持鸿蒙的 UI 库,同时还兼容 React Native、小程序、H5

· 阅读需 16 分钟
duxapp
duxapp框架作者

Taro 4.0 已经推出一段时间了,4.0 版本主要是支持了鸿蒙端的开发以及 Vite 编译工具的支持。duxapp 在这段时间也跟随 Taro 的脚步,实现的对鸿蒙端的支持,并且也将之前的 duxui 这个多端的 UI 库,对鸿蒙端实现了兼容。

duxui 组件库提供了 60+ 的组件支持,能快速帮助你完成业务。

现在使用这个 UI 库,不仅能开发鸿蒙,还能实现同时开发 React Native、小程序和 H5,也是目前唯一一个能兼容这么多端的 UI 库。

组件展示

效果图

下面我将介绍这一切是如何做到的,这其中遇到了哪些问题

兼容鸿蒙之前

在开始兼容鸿蒙之前,我们来讲一下,duxapp 是一个什么状况

1、样式兼容性

现在 duxapp 已经兼容 小程序、React Native以及H5了,因为 React Native 端在这些端中兼容的样式是最少的,我们是按照React Native的标准去做,我们针对 H5 端和 小程序端的基础样式做了调整

需要将 View 组件调整为默认 Flex 竖向布局、默认的定位方式、模型盒、以及边框等,像下面这样:

/* H5 端 */
taro-view-core {
display: flex;
flex-direction: column;
position: relative;
border-style: solid;
border-width: 0;
}
input,
textarea,
taro-view-core {
box-sizing: border-box;
}
/* 小程序端 */
view {
display: flex;
flex-direction: column;
position: relative;
border-style: solid;
border-width: 0;
}
input,
textarea,
view {
box-sizing: border-box;
}

通过这样的调整,我们在项目中使用 View 组件的时候将默认使用 Flex 竖向布局,这样编写的 css 就能做到同时兼容多个端了

2、现有模块

duxapp 框架中现在已提供了很多的公开模块了,这里介绍三个相关的基础模块,这三个模块在我们兼容鸿蒙的过程中都有很大的关联

  • duxapp

这个和框架名称相同的模块,是所有模块的根模块,所有的模块都需要依赖于这个模块

这个模块内提供了一些非常基础的组件、函数、配置等,且一些东西是必须使用的

用来包裹页面的容器组件 TopView
路由跳转函数 route
导出的用户配置

  • duxui

这个是提供的UI组件库,模块内提供了大量兼容多端的组件库,使用这些组件,能快速开发出兼容多端的应用

  • duxappReactNative

React Native端支持,要兼容 React Native,必须使用这个模块,模块内有 React Native 端的三方依赖、用户协议、版本更新、权限管理等

Taro 和鸿蒙的现状

Taro 4.0 目前有两套实现鸿蒙的方案

  • @tarojs/plugin-platform-harmony-hybrid

webview方案,将 H5 运行在 webview 上,和小程序类似

  • @tarojs/plugin-platform-harmony-ets

duxapp 中为了更好的性能和体验,选择的的是这个方案

底层通过 arkts 以及 arkui 实现,将 Taro 的组件转化为对应的 arkui 里面对应的组件,并实现了 React 的运行时

例如 View 组件会通过 arkui 的 FlexRowColumn 这三个组件来实现,会根据编写的不同样式选择不同的原生组件去渲染,从这里也大概能看出,鸿蒙的 arkui 里面也是 Flex 布局

兼容过程

1、创建一个鸿蒙模块

就像上面介绍的 React Native 端那样,鸿蒙端也需要创建一个模块,用来处理鸿蒙端的基础内容,例如用到的三方依赖、配置插件等

这个模块的名称为 duxappHarmony

2、添加需要的三方依赖

目前鸿蒙端仅支持使用 vite 编译,因此鸿蒙端需要添加编译的相关的依赖,在模块内创建 package.json,内容如下

{
"scripts": {
"build:harmony": "duxapp runtime enterFile && duxapp harmony create && taro build --type harmony",
"dev:harmony": "npm run build:harmony -- --watch"
},
"dependencies": {
"@tarojs/plugin-platform-harmony-ets": "4.0.7",
"vite": "^5.4.10",
"terser": "^5.36.0",
"@tarojs/vite-runner": "4.0.5",
"@vitejs/plugin-react": "^4.3.3",
"vite-plugin-commonjs": "^0.10.3"
}
}

这个完整的文件中还包含了编译鸿蒙的两个命令

3、配置依赖项

添加了依赖这些依赖并不会自动起作用,需要配置 Taro 编译配置,我们在这个模块内创建一个 Taro 的配置文件 taro.config.js,内容如下

// eslint-disable-next-line import/no-commonjs
const commonjs = require('vite-plugin-commonjs').default

module.exports = {
// 鸿蒙编译插件
plugins: [
'@tarojs/plugin-platform-harmony-ets'
],
harmony: {
compiler: {
// 指定 vite 编译
type: 'vite',
vitePlugins: [
commonjs()
]
},
projectPath: './dist/harmony'
}
}

这里面配置了平台插件、指定使用 vite 编译、设置了项目目录

这其中还加入了一个 vite 插件,因为 vite 默认不支持 commonjs,但是 duxapp 项目中使用了commonjs,所以加入了这个插件

到此基础环境配置好了,但是我们还需要创建鸿蒙工程文件,Taro 没有提供这个内容

4、在cli内处理鸿蒙工程文件

在第 2 步的命令中,duxapp harmony create 有这个命令,我们就是通过这个命令来创建的鸿蒙工程文件,这样就不需要再通过鸿蒙的 IDE 再去手动操作创建了,这也是一个相对麻烦的过程

这个过程和 React Native 端是类似的,React Native端也是自动创建安卓和 ios 的工程文件,鸿蒙的工程文件创建会放在 dist/harmony 目录中

创建工程文件的过程中,可以通过配置或脚本对工程文件进行处理,例如鸿蒙端需要包名、版本号、app 名称等信息,都是通过配置文件来生成的

这里就不详细展开了,可以查看文档了解 鸿蒙入门文档

5、组件库兼容

上面的步骤处理完之后,框架的东西就处理完了,但是仍然是不可用的,因为要编写一个兼容多端的项目,现有的组件,还需要针对鸿蒙端做相应的兼容,要兼容的组件包括基础模块中的组件和 duxui 模块中的组件

鸿蒙端兼容是一个很费事的过程,对鸿蒙端的兼容性 React Native还难,难就难在,鸿蒙有一个非标准的 Flex

在上面介绍了 View 组件会被在不同的情况编译的 Flex Row Column,具体来说,

  • align-items 的属性值为 stretchdaseline 或者 flex-wrap 的属性值为 wrap会使用 Flex 组件
  • 否则当 flex-directionrow 或者 row-reverse 使用 Row 组件
  • 其他的情况使用 Column 组件

因为鸿蒙的 RowColumn 组件的align-items不支持 stretchdaseline,而 Row组件又不支持 wrap 换行

在使用 Row 或者 Column 的情况下,这和标准 Flex 布局基本没多大差别,问题就出在使用 Flex 组件这,这个组件有个奇怪的特性

Flex组件主轴默认不设置时撑满父容器,Column、Row组件主轴不设置时默认是跟随子节点大小。

这是官方文档的描述,这是一个很奇怪的特性,且没有办法通过设置取消,这导致很多奇怪的布局行为,这个效果类似于给元素永久设置了个 flex: 1,且比这个好厉害,就算有多层父元素,他也能把外层父元素撑开,感觉它才是爹!

在做兼容的时候,我总结了关于这个特性的兼容方式:

当 flex 的 alitn-items 为 stretch 或者 daseline 时,需要给当前元素指定尺寸,在任何能不用 alitn-items 的这两个属性的时候,都不要用这两个属性。如果要使用stretch,建议父元素使用 alitn-items: flex-start,子元素使用 align-self: stretch,这样的形式

当然,除了 Flex 布局这个最大的问题之外,还有很多与标准不太一样的地方,我整理总结了放在 兼容性章节,在开发的时候需要特别注意

可以说我在兼容鸿蒙的过程中,大部分时间都是在做组件库的兼容,和这些样式斗智斗勇

开始使用

通过上面这一系列的操作,duxapp 框架已经基本实现了兼容鸿蒙,且提供了大量可用的 UI 组件库

你可以快速初始化一个 UI 库的示例项目,你可以通过这个示例项目快速的查看到 UI 库在鸿蒙端以及其他端的效果

npx duxapp-cli create projectExample duxuiExample

在运行这个命令之前,你需要安装好 nodejs 20+ yarn git

创建项目后,进入项目运行以下命令

yarn dev:harmony --app=duxuiExample

编译完成后,使用 DevEco Studio 打开 dist/harmony 目录,这个目录就是一个原生鸿蒙项目

打开项目后,右上角工具栏区域,选择已经开启的虚拟机,并且点击右侧的运行按钮(三角符号),开始编译,编译后,会自动在虚拟机上启动这个 app

如果想继续开发项目,请查看鸿蒙入门文档

当前版本在 windows 系统上,仍然存在问题,能编译成功,但是启动 app 会报错,你可以先尝试使用其他系统进行编译,例如 Mac,这个问题将会在不久后修复

未来展望

目前对鸿蒙的兼容取得了初步的成效,但是也还有少数一些组件和功能还未实现兼容,像下面这些,将逐步完善这些组件或功能

  • PullView Modal 等组件动画效果
  • Picker 组件暂不支持
  • Sign 签名组件待完善
  • 录音组件录音功能待完善
  • 微信模块相关功能暂不支持
  • 地图暂不支持
  • List、Detail 组件待完善

Taro 目前开发中的版本,将放弃使用鸿蒙的 Flex 布局方式,转而使用更底层的实现方式,使用了 React Native 的布局引擎 Yoga,用过 Yoga 引擎来来布局,将引擎的计算结果再赋值到具体的组件上,这样就能极大改善鸿蒙布局兼容性问题,等待 Taro 的这套方案开源后,duxapp 也将跟进

如果你有兴趣,请继续查看文档了解详情吧

开发文档
GitHub

用duxapp快速完成项目

· 阅读需 10 分钟
duxapp
duxapp框架作者

使用duxapp,我是如何实现快速完成项目开发的?

像下面这个例子,这个项目有140多个页面,但是真实的开发时间,在熟练使用duxapp的情况下,不会超过两周,并且可以将它兼容APP、小程序、H5

p1 p2 p3

这里仅展示了其中一部分页面,这个项目主要包含下这些功能

  • 购物订单流程
  • 售后退换
  • 文章发布
  • 门店功能
  • 送货人员功能
  • 经销商功能
  • 扫码抽奖
  • 等其他功能

下面我将详细介绍使用了哪些方法,来快速完成项目

使用全局样式快速布局页面

以这个门店管理首页为例,你可以看到我并没有导入一个scss文件,但是我使用了很多的className,这些就是用的全局样式,这全局样式都是duxapp模块提供的

import { TopView, Header, Card, Divider, ScrollView, Tag, Row, Image, Column, Text, px, nav, ModalForm, DatePicker, dayjs } from '@/duxui'
import { ArecaIcon, CmsIcon, TipNav, useRequest } from '@/arecaShop'
import { useState } from 'react'

export default function StoreManage() {

const [date, setDate] = useState()

const [{ info = {}, sku = {}, distribution = {} }] = useRequest({
url: 'store/index',
toast: true,
data: {
date
}
}, { reloadForShow: true })

return (
<TopView>
<Header title='门店详情' />
<ScrollView>
<Card className='p-2 gap-2' margin disableMarginBottom>
<Row className='gap-2'>
<Image className='r-2'
src={info.image}
style={{ width: px(148) }}
square
/>
<Column justify='between' grow>
<Row justify='between' grow>
<Text bold size={4}>{info.name}</Text>
<ArecaIcon name='shezhi' className='text-c1 text-s7'
onClick={() => nav('arecaShop/storeManage/info')}
/>
</Row>
<Column>
<Text color={2} size={1}>联系人:{info.contact}</Text>
<Row grow justify='between' items='center'>
<Text color={2} size={1}></Text>
<Tag type='secondary' size='s' radiusType='round' onClick={() => nav(`tel:${info.distributor_tel}`)}>
<ArecaIcon className='text-white text-s2' name='shiwu-shouji' /> 一键报单进货
</Tag>
</Row>
</Column>
</Column>
</Row>
<Divider />
<Row>
<Text color={2} size={1} shrink>门店地址:</Text>
<Text bold size={1}>{info.area}{info.address}</Text>
</Row>
</Card>
<TipNav title='货品统计' url='arecaShop/storeManage/mallTotal'>
<Row className='items-center' grow>
<ArecaIcon className='text-secondary' name='promot_tips' />
<Text type='secondary' size={2}>{dayjs().format('MM/DD HH:mm')} 更新</Text>
</Row>
</TipNav>
<Card margin disableMarginBottom className='gap-2'>
<Text color={3} size={2}>历史进货数量(小包):{sku.in}</Text>

<Row justify='between' className='gap-2'>
<Column grow className='bg-page r-2 p-3 gap-1'>
<Text color={3} size={2}>历史销售数量(小包)</Text>
<Text bold size={40}>{sku.out}</Text>
</Column>
<Column grow className='bg-page r-2 p-3 gap-1'>
<Text color={3} size={2}>历史剩余数量(小包)</Text>
<Text bold size={40}>{sku.supply}</Text>
</Column>
</Row>
<Text size={22} type='secondary'>*销量及剩余仅供参考:记录消费者扫码数量,可能与实际结果有出入</Text>
</Card>
<TipNav title='配送记录'
right={<ModalForm
title='月份'
renderForm={<DatePicker
mode='month'
/>}
childPropsValueKey='value'
value={date}
onChange={setDate}
>
<DateChild />
</ModalForm>}
></TipNav>
<Card margin disableMarginBottom className='gap-2'>
<Text color={3} size={2}>门店配送(中包):{distribution.middle_num}</Text>

<Row justify='between' className='gap-2'>
<Column grow className='bg-page r-2 p-3 gap-1'
onClick={() => nav('arecaShop/storeManage/emptyRecovery', { type: 'store' })}
>
<Text color={3} size={2}>已结算空袋(小包)</Text>
<Row items='center' className='gap-1'>
<Text bold size={40}>{distribution.recycle_num}</Text>
<CmsIcon className='text-c3 text-s5' name='direction_right' />
</Row>
</Column>
<Column grow className='bg-page r-2 p-3 gap-1'
onClick={() => nav('arecaShop/storeManage/deliveryList')}
>
<Text color={3} size={2}>门店已退货数(小包)</Text>
<Row items='center' className='gap-1'>
<Text bold size={40}>{distribution.refund_num}</Text>
<CmsIcon className='text-c3 text-s5' name='direction_right' />
</Row>
</Column>
</Row>
</Card>
</ScrollView>
</TopView>

)
}

const DateChild = ({ value, onClick }) => {
return <Row items='center' className='r-2 ph-2 bg-white gap-1' onClick={onClick}>
<Text color={value ? 1 : 3}>{value || '请选择'}</Text>
<ArecaIcon name='rili1' className='text-c3' />
</Row>
}

使用UI库

在上面的示例中已经展示了如何使用UI库的组件,可以看到使用了UI库的组件,在结合全局样式,你会发现,似乎不需要编写scss就能很好的完成页面布局了

这些组件都是duxui UI库提供的

使用List组件完成列表页面

以下面这个列表页面为例,List组件帮你完成了数据请求、自动分页、下拉刷新、自动刷新等功能,你只需要关注你列表中的每一项是如何编写的

import { TopView, Header, Card, Tab, Row, Image, Column, Text, px, Empty, DatePicker, SelectorPicker, ModalForm, Button } from '@/duxui'
import { ArecaIcon, List, usePageData, nav } from '@/arecaShop'
import { useState } from 'react'

export default function DeliveryList() {

const [users] = usePageData('store/salesman')

const [user, setUser] = useState()

const [type, setType] = useState(0)

return (
<TopView>
<Header title='配送记录' />
<Tab className='bg-white' value={type} onChange={setType}>
<Tab.Item title='门店配送' paneKey={0} />
<Tab.Item title='门店退货' paneKey={1} />
</Tab>
<Row className='ph-3 mt-3' items='center' justify='between'>

<ModalForm
title='业务员'
renderForm={<SelectorPicker range={users} nameKey='nickname' valueKey='id' />}
childPropsValueKey='value'
value={user}
onChange={setUser}
>
<SelectChild />
</ModalForm>

</Row>

<List
key={type}
renderItem={Item}
renderEmpty={<Empty title='暂无记录' />}
url={type ? 'store/refund' : 'store/distribution'}
data={{
salesman_id: user
}}
/>
</TopView>
)
}

const SelectChild = ({ value, ...props }) => {

return (
<Row items='center' {...props}>
<Text size={2}>{value || '全部业务员'}</Text>
<ArecaIcon name='arrow_down_fill' className='text-s4 text-c2' />
</Row >
)
}

const Item = ({ item }) => {
return <Card margin disableMarginBottom className='gap-3'>
<Row items='center'>
<ArecaIcon className='text-c1 text-s7' name='store' />
<Text size={2} style={{ marginLeft: px(4) }}>{item.store_name}</Text>
<ArecaIcon className='text-c3 text-s5' name='you2' />
</Row>
{
item.goods.map(good => <Row className='gap-3' key={good.id}>
<Image style={{ width: px(160) }} square className='r-2' src={good.image} />
<Column grow justify='between'>
<Text size={2} bold numberOfLines={2}>{good.title}</Text>
<Text size={2} color={3}>规格:{good.spec}</Text>
<Text size={2} color={3}>数量:{good.num}</Text>
</Column>
</Row>)
}

<Column className='r-2 bg-page p-3 gap-2'>
<Row>
<Row grow items='center'>
<ArecaIcon className='text-secondary text-s7' name='man_mine' />
<Text size={2}>{item.salesman_name || '-'}</Text>
</Row>
<Row grow items='center'>
<ArecaIcon className='text-secondary text-s7' name='shiwu-shouji' />
<Text size={2}>{item.salesman_tel || '-'}</Text>
</Row>
</Row>
<Row>
<Row grow items='center'>
<ArecaIcon className='text-secondary text-s7' name='map' />
<Text size={2}>{item.area}</Text>
</Row>
<Row grow items='center'>
<ArecaIcon className='text-secondary text-s7' name='shijian1' />
<Text size={2}>{item.created_at}</Text>
</Row>
</Row>
</Column>
</Card>
}

使用hook快速请求数据

这里是指的使用useRequest替代request快速获取数据,而不需要在编写具体请求逻辑

import { TopView, Header, Card, Text, ScrollView, Space, HtmlView, useRoute } from '@/duxui'
import { useRequest } from '@/arecaShop'

export default function HelpDetail() {

const { params } = useRoute()

const [detail] = useRequest(`tools/magic/help/${params.id}`)

return (
<TopView isSafe>
<Header title='帮助详情' />
<ScrollView >
<Card shadow={false} margin>
<Space size={32}>
<Text bold size={42} >{detail.title}</Text>
<HtmlView html={detail.content} />
</Space>
</Card>
</ScrollView>
</TopView>
)
}

使用现有模块

这个项目中的 购物订单流程、售后退换、文章发布等功能,在之前的模块中都已经是开发过的功能,并且他们都是成熟可用的,我们只需要直接使用他们就行了

{
"name": "arecaShop",
"description": "大工匠槟榔商城",
"version": "1.0.0",
"dependencies": [
"duxcmsMall",
"amap"
]
}

在我这个项目模块的配置文件中,将 duxcmsMall 添加到依赖中,这是商城模块他提供了商品、订单、售后、购物车、收货地址等功能

因为当前项目需求是积分兑换商品,但是默认的商城不支持积分功能,下面介绍是使用渲染钩子来修改为积分商品

使用渲染钩子对商城进行修改

使用RenderHook,将商城购物流程修改为积分兑换流程,这个文件需要在模块入口文件中导入

import { mallHook, orderHook, Price } from '@/duxcmsMall'
import { Card, contextState, Row, Text } from '@/duxui'

const ListPrice = ({ item, children }) => {
if (item.type === 'point') {
return <Text bold size={4} type='danger'>{item.sell_point}积分</Text>
}
return children
}

mallHook.add('goods.list.item.sellPrice', ListPrice)
mallHook.add('MallCateQuick.item.sellPrice', ListPrice)
mallHook.add('MallList.item.sellPrice', ListPrice)

mallHook.add('detail.info.price', ({ children }) => {

const [data] = contextState.useState()

if (data.type !== 'point') {
return children
}

return <Row className='gap-2' items='center'>
<Text size={40} bold color={4}>
{data.sell_point}积分
</Text>
<Price size={1} delete color={4} className='mt-2'>{data.market_price}</Price>
</Row>
})

mallHook.add('GoodsSpec.price', ({ children, data, item }) => {
if (data.type !== 'point') {
return children
}
return <Text size={48} bold type='danger'>
{item?.sell_point || data.sell_point}积分
</Text>
})

orderHook.add('order.create.data.total', ({ store }) => {

// const isPoint = store.items.some(v => v.type === 'point')

return <>
<Row items='center' justify='between'>
<Text bold>商品金额</Text>
<Price color={2}>{store.total.order_price}</Price>
</Row>
<Row items='center' justify='between'>
<Text bold>运费</Text>
<Price color={2}>{store.total.delivery_price}</Price>
</Row>
{
store.discount.map(item => <Row key={item.name} items='center' justify='between'>
<Text bold>{item.desc}</Text>
<Text type='danger'>{item.price}</Text>
</Row>)
}
{/* {+store.total.discount_price > 0 && <Row items='center' justify='between'>
<Text bold>{isPoint ? '积分抵扣' : '优惠'}</Text>
<Price type='danger'>{-store.total.discount_price}</Price>
</Row>} */}
</>
})

orderHook.add('order.detail.total', () => {
const [{ data }] = contextState.useState()

return <Card margin disableMarginBottom className='gap-3'>
<Row items='center' justify='between'>
<Text color={2}>订单总额</Text>
<Price bold color={1}>{data.order_price}</Price>
</Row>
<Row items='center' justify='between'>
<Text color={2}>运费</Text>
<Price bold color={1}>{data.delivery_price}</Price>
</Row>
{
data.discount_data?.map(item => <Row key={item.name} items='center' justify='between'>
<Text color={2}>{item.desc}</Text>
<Text bold type='danger'>{item.price}</Text>
</Row>)
}
<Row items='center' justify='between'>
<Text color={2}>实付款</Text>
<Price bold type='primary'>{data.pay_price}</Price>
</Row>
</Card>
})

总结

上面提到的就是用于快速开发的主要方法,当你熟练掌握这些方法后,你的开发速度将一骑绝尘

当然这些上面提到这些并不是全部,你可以阅读开发文档,从中获取更多的使用方法