From 0d17d9e049b4750a03ca7c8eb7b435b823446def Mon Sep 17 00:00:00 2001
From: Liuyi <candymxq888@outlook.com>
Date: 星期四, 26 九月 2024 18:33:31 +0800
Subject: [PATCH] 添加地址管理,新增地址页,导入tree树形组件

---
 components/da-tree/readme.md     |  310 +++++++
 package-lock.json                |   17 
 pages.json                       |   24 
 static/images/address/delete.png |    0 
 static/images/address/edit.png   |    0 
 App.vue                          |    3 
 pages/address/index.vue          |  147 +++
 uni.scss                         |    1 
 components/da-tree/props.ts      |  197 ++++
 pages/addressAdd/index.vue       |  198 ++++
 pages/addressLocate/index.vue    |   13 
 components/da-tree/utils.ts      |  150 +++
 pages/addressEdit/index.vue      |   22 
 components/da-tree/changelog.md  |  196 ++++
 package.json                     |   33 
 uni_modules/uview-ui/LICENSE     |   21 
 main.js                          |    2 
 components/da-tree/index.vue     | 1151 +++++++++++++++++++++++++++
 18 files changed, 2,461 insertions(+), 24 deletions(-)

diff --git a/App.vue b/App.vue
index 1efba44..23ef92a 100644
--- a/App.vue
+++ b/App.vue
@@ -25,6 +25,5 @@
 	});
 </script>
 
-<style>
-	/*每个页面公共css */
+<style lang='scss'>
 </style>
diff --git a/components/da-tree/changelog.md b/components/da-tree/changelog.md
new file mode 100644
index 0000000..7117197
--- /dev/null
+++ b/components/da-tree/changelog.md
@@ -0,0 +1,196 @@
+# 1.4.2
+
+新增
+
+1. 新增`filterValue`属性,支持通过此关键词来搜索并筛选树结构的内容
+
+# 1.4.1
+
+修复
+
+1. 修复单选 onlyRadioLeaf 时末级节点无法选中的 bug
+
+# 1.4.0
+
+版本调整
+
+建议更新,但需要注意,异步数据的时候,后台需返回 leaf 字段来判断是否末项数据
+
+1. **调整数据项格式,新增 `leaf` 字段,来判断是否为末节点**
+2. **调整数据项格式,新增 `sort` 字段,来排序节点位置**
+3. **注意:异步加载数据,当为末项的时候,需要服务端数据返回 `leaf` 字段**
+4. 新增 `alwaysFirstLoad` ,即异步数据总会在第一次展开节点时,拉取一次后台数据,来比对是否一致
+5. 拆分 `field` 属性,**注意: 1.5.0 版本后将移除 `field` 属性**
+6. 新增 `labelField` 同 `field.label`,指定节点对象中某个属性为**标签**字段,默认`label`
+7. 新增 `valueField` 同 `field.key`,指定节点对象中某个属性为**值**字段,默认`value`
+8. 新增 `childrenField` 同 `field.children`,指定节点对象中某个属性为**子树节点**字段,默认`children`
+9. 新增 `disabledField` 同 `field.disabled`,指定节点对象中某个属性为**禁用**字段,默认`disabled`
+10. 新增 `appendField` 同 `field.append`,指定节点对象中某个属性为**副标签**字段,默认`append`
+11. 新增 `leafField` 同 `field.label`,指定节点对象中某个属性为**末级节点**字段,默认`leaf`
+12. 新增 `sortField` 同 `field.label`,指定节点对象中某个属性为**排序**字段,默认`sort`
+13. 新增 `isLeafFn` ,用来自定义控制数据项的末项
+14. 更多的项目示例
+15. 支持单选取消选中
+16. 修复节点展开时可能存在的 bug
+17. 修复节点选择可能存在的 bug
+18. 调整为子节点默认继承父节点禁用属性
+19. `setExpandedKeys` 添加参数一为 `all` 即可支持一键展开/收起全部节点
+20. 其它更多优化
+
+# 1.3.4
+
+优化
+
+1. 优化图标字体命名
+
+# 1.3.3
+
+优化
+
+1. 新增方法调用
+   > - 新增`getUncheckedKeys`,返回未选的 key
+   > - 新增`getUncheckedNodes`,返回未选的节点
+   > - 新增`getUnexpandedKeys`,返回未展开的 key
+   > - 新增`getUnexpandedNodes`,返回未展开的节点
+2. 优化示例项目
+
+# 1.3.2
+
+修复
+
+1. 修复在 APP 真机环境中的报错
+
+# 1.3.1
+
+修复
+
+1. 修复方法`setExpandedKeys`没联动展开上级父子节点
+
+# 1.3.0
+
+优化
+
+1. `field`新增字段 `append` 用于在标签后面显示小提示
+2. 新增支持点击标签也能选中节点
+3. 方法`setExpandedKeys`支持加载动态数据
+4. 修复父节点禁用,则不能展开及图标展开显示
+5. 修复动态加载数据时,末级节点的 `children` 为 `null` 时仍显示展开图标
+
+# 1.2.6
+
+新增
+
+1. 新增支持主题换色
+2. 支持单选的`onlyRadioLeaf`为`true`时可点父节点展开/收起
+3. 优化`expandChecked`调整为不展开无子节点的节点
+
+# 1.2.5
+
+新增
+
+1. 新增 `expandChecked`,控制选择时是否展开当前已选的所有下级节点
+
+# 1.2.4
+
+修复
+
+1. 修复动态数据展开状态异常问题
+
+# 1.2.3
+
+新增
+
+1. 新增 `checkedDisabled`,是否渲染禁用值
+2. 新增 `packDisabledkey`,是否返回已禁用并选中的 key
+3. 修复选择父级时,子级已禁用但仍被选中的问题
+
+# 1.2.2
+
+优化
+
+1. 调整动态数据载入处理方式
+2. 修复节点数据因动态数据引起的状态异常
+3. 修复初始节点数据默认选中
+
+# 1.2.1
+
+修复
+
+1. 修复切换`选中状态`被重复选中问题
+2. 修复动态数据引起的重复选择问题
+
+# 1.2.0
+
+新增
+
+1. 新增方法调用
+   > - 新增`setCheckedKeys`,方法设置指定 key 的节点选中状态
+   > - 新增`setExpandedKeys`,方法设置指定 key 的节点展开状态
+2. 修复小程序重复插槽一直刷报错问题
+3. 优化展开时,会展开子级所以下级节点
+
+# 1.1.1
+
+新增
+
+1. 新增`data`的`disabled`,支持节点禁用状态
+2. 新增`field`的`disabled`,可自定`disabled`字段值
+
+# 1.1.0
+
+新增
+
+1. 新增`loadMode`、`loadApi`,支持展开时加载异步数据
+2. 新增方法调用
+   > - 新增`getCheckedKeys`,方法返回已选的 key
+   > - 新增`getHalfCheckedKeys`,方法返回半选的 key
+   > - 新增`getExpandedKeys`,方法返回已展开的 key
+   > - 新增`getCheckedNodes`,方法返回已选的节点
+   > - 新增`getHalfCheckedNodes`,方法返回半选的节点
+   > - 新增`getExpandedNodes`,方法返回已展开的节点
+3. 对代码进行重构,更易于后期拓展
+4. 此次更新后,页面多个的 DaTee 组件间的数据不再关联
+
+# 1.0.6
+
+新增
+
+1. 新增`checkStrictly`,多选模式下选中时是否父子不关联
+
+# 1.0.5
+
+修复
+
+1. 修复多选时已选数据重复问题
+
+# 1.0.4
+
+修复
+
+1. 修复 `change` 事件回调数据的问题
+
+# 1.0.3
+
+优化
+
+1. 优化文档及示例说明
+
+# 1.0.2
+
+新增
+
+1. 新增 `onlyRadioLeaf` ,单选时只允许选中末级
+2. 优化默认展开及默认选择的展开问题
+
+# 1.0.1
+
+新增
+
+1. 支持展开/收起回调事件`@expand`
+
+# 1.0.0
+
+初始版本 1.0.0,基于 Vue3 进行开发,支持单选、多选,兼容各大平台
+
+1. 支持单选
+2. 支持多选
diff --git a/components/da-tree/index.vue b/components/da-tree/index.vue
new file mode 100644
index 0000000..ead519d
--- /dev/null
+++ b/components/da-tree/index.vue
@@ -0,0 +1,1151 @@
+<template>
+  <view class="da-tree" :style="{'--theme-color': themeColor}">
+    <scroll-view class="da-tree-scroll" :scroll-y="true" :scroll-x="false">
+      <view
+        class="da-tree-item"
+        :class="{'is-show': item.show}"
+        :style="{paddingLeft: item.level * indent + 'rpx'}"
+        v-for="item in datalist"
+        :key="item.key">
+        <view
+          v-if="item.showArrow && !filterValue"
+          class="da-tree-item__icon"
+          @click="handleExpandedChange(item)">
+          <view :class="['da-tree-item__icon--arr','is-loading']" v-if="loadLoading && item.loading"></view>
+          <view :class="['da-tree-item__icon--arr','is-expand', {'is-right':!item.expand}]" v-else></view>
+        </view>
+        <view v-else class="da-tree-item__icon"></view>
+        <view
+          class="da-tree-item__checkbox"
+          :class="[`da-tree-item__checkbox--${checkboxPlacement}`,{'is--disabled': item.disabled}]"
+          v-if="showCheckbox"
+          @click="handleCheckChange(item)">
+          <view class="da-tree-item__checkbox--icon da-tree-checkbox-checked" v-if="item.checkedStatus === isCheckedStatus"></view>
+          <view class="da-tree-item__checkbox--icon da-tree-checkbox-indeterminate" v-else-if="item.checkedStatus === halfCheckedStatus"></view>
+          <view class="da-tree-item__checkbox--icon da-tree-checkbox-outline" v-else></view>
+        </view>
+        <view
+          class="da-tree-item__checkbox"
+          :class="[`da-tree-item__checkbox--${checkboxPlacement}`,{'is--disabled': item.disabled}]"
+          v-if="!showCheckbox && showRadioIcon"
+          @click="handleRadioChange(item)">
+          <view class="da-tree-item__checkbox--icon da-tree-radio-checked" v-if="item.checkedStatus === isCheckedStatus"></view>
+          <view class="da-tree-item__checkbox--icon da-tree-radio-indeterminate" v-else-if="item.checkedStatus === halfCheckedStatus"></view>
+          <view class="da-tree-item__checkbox--icon da-tree-radio-outline" v-else></view>
+        </view>
+        <view class="da-tree-item__label" :class="'da-tree-item__label--'+item.checkedStatus" @click="handleLabelClick(item)">{{ item.label }} <text class="da-tree-item__label--append" v-if="item.append">{{ item.append }}</text></view>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+
+import { defineComponent, ref, unref, watch } from 'vue'
+
+import {
+  unCheckedStatus,
+  halfCheckedStatus,
+  isCheckedStatus,
+  deepClone,
+  getAllNodeKeys,
+  getAllNodes,
+  logError,
+  isArray,
+  isString,
+  isNumber,
+  isFunction,
+} from './utils'
+import basicProps from './props'
+
+export default defineComponent({
+  name: 'DaTree',
+  props: basicProps,
+  emits: ['change', 'expand'],
+  setup(props, { emit }) {
+    /** 原始的树数据 */
+    const dataRef = ref([])
+    /** 处理后的一维树项数据 */
+    const datalist = ref([])
+    /** 处理后的以key为键值的树项数据 */
+    const datamap = ref({})
+    /** 默认的展开数据 */
+    const expandedKeys = ref([])
+    /** 默认的已选数据 */
+    const checkedKeys = ref(null)
+    /** 加载状态 */
+    const loadLoading = ref(false)
+    let fieldMap = {
+      value: 'value',
+      label: 'label',
+      children: 'children',
+      disabled: 'disabled',
+      append: 'append',
+      leaf: 'leaf',
+      sort: 'sort',
+    }
+
+    /**
+     * 初始化数据结构
+     */
+    function initData() {
+      fieldMap = {
+        value: props.field?.key || props.field?.value || props.valueField || 'value',
+        label: props.field?.label || props.labelField || 'label',
+        children: props.field?.children || props.childrenField || 'children',
+        disabled: props.field?.disabled || props.disabledField || 'disabled',
+        append: props.field?.append || props.appendField || 'append',
+        leaf: props.field?.leaf || props.leafField || 'leaf',
+        sort: props.field?.sort || props.sortField || 'sort',
+      }
+
+      const data = deepClone(dataRef.value)
+      datalist.value = []
+      datamap.value = {}
+
+      // clean tree
+      handleTreeData(data)
+      // flat tree
+      datalist.value = checkInitData(datalist.value)
+      // console.log('init datalist', datalist.value)
+      // console.log('init datamap', datamap.value)
+    }
+
+    /**
+     * 转换为节点数据
+     * @param data
+     * @param parent
+     * @param level
+     */
+    function handleTreeData(data = [], parent = null, level = 0, insertIndex = -1) {
+      return data.reduce((prev, cur, index) => {
+        const key = cur[fieldMap.value]
+        const children = cur[fieldMap.children] || null
+        const newItem = createNewItem(cur, index, parent, level)
+        if (insertIndex > -1) {
+          // 插入子项尾部
+          const index = (parent.childrenKeys?.length || 0) + insertIndex + 1
+          if (!parent?.childrenKeys?.includes(key)) {
+            datamap.value[key] = newItem
+            datalist.value.splice(index, 0, newItem)
+            parent.children.push(newItem)
+            if (newItem.parentKeys?.length) {
+              newItem.parentKeys.forEach(k => {
+                datamap.value[k].childrenKeys = [...datamap.value[k].childrenKeys, newItem.key]
+              })
+            }
+          }
+        } else {
+          datamap.value[key] = newItem
+          datalist.value.push(newItem)
+        }
+
+        const hasChildren = children && children.length > 0
+        if (hasChildren) {
+          const childrenData = handleTreeData(children, newItem, level + 1)
+          // childrenData.sort((a, b) => a.sort - b.sort)
+          newItem.children = childrenData
+          const childrenKeys = childrenData.reduce((p, k) => {
+            const keys = k.childrenKeys
+            p.push(...keys, k.key)
+            return p
+          }, [])
+          newItem.childrenKeys = childrenKeys
+        }
+        prev.push(newItem)
+        return prev
+      }, [])
+    }
+
+    /**
+     * 创建节点
+     * @param item
+     * @param index
+     * @param parent
+     * @param level
+     */
+    function createNewItem(item, index, parent, level) {
+      const key = item[fieldMap.value]
+      const label = item[fieldMap.label]
+      const sort = item[fieldMap.sort] || 0
+      const children = item[fieldMap.children] || null
+      const append = item[fieldMap.append] || null
+      let disabled = item[fieldMap.disabled] || false
+      // 优先继承父级禁用属性
+      disabled = parent?.disabled || disabled
+      let isLeaf = isFunction(props.isLeafFn) ? props.isLeafFn(item) : (item[fieldMap.leaf] || false)
+      // const hasChildren = children && children.length > 0
+      const isEmptyChildren = children && children.length === 0
+      let showArrow = true
+      // let isLeaf = !hasChildren
+      let expand = props.defaultExpandAll || false
+      // 是否异步加载模式
+      const isLoadMode = props.loadMode && isFunction(props.loadApi)
+
+      if (!children) {
+        expand = false
+        if (isLoadMode) {
+          showArrow = true
+        } else {
+          isLeaf = true
+          showArrow = false
+        }
+      }
+
+      if (isEmptyChildren) {
+        expand = false
+        if (isLoadMode) {
+          showArrow = true
+        } else {
+          isLeaf = true
+          showArrow = false
+        }
+      }
+
+      if (isLeaf) {
+        showArrow = false
+        expand = false
+      } else {
+        showArrow = true
+      }
+
+      // onlyRadioLeaf 单选只能选择末级节点
+      if (!props.showCheckbox) {
+        if (props.onlyRadioLeaf) {
+          if (!isLeaf) {
+            disabled = true
+          } else {
+            // 仍旧继承父类原始禁用状态
+            disabled = parent?.originItem?.disabled || false
+          }
+        }
+      }
+
+      if (disabled) {
+        if (isLeaf || !children || isEmptyChildren) {
+          expand = false
+          showArrow = false
+        }
+      }
+
+      const parentKey = parent ? parent.key : null
+      const show = props.defaultExpandAll || level === 0
+
+      const newItem = {
+        key,
+        parentKey,
+        label,
+        append,
+        isLeaf,
+        showArrow,
+        level,
+        expand,
+        show,
+        sort,
+        disabled,
+        loaded: false,
+        loading: false,
+        indexs: [index],
+        checkedStatus: unCheckedStatus,
+        parentKeys: [],
+        childrenKeys: [],
+        children: [],
+        originItem: item,
+      }
+
+      if (parent) {
+        newItem.parentKeys = [parent.key, ...parent.parentKeys]
+        newItem.indexs = [...parent.indexs, index]
+      }
+
+      return newItem
+    }
+
+    /**
+     * 处理初始化内容
+     * @param list
+     */
+    function checkInitData(list) {
+      let checkedKeyList = null
+      let expandedKeyList = []
+      if (props.showCheckbox) {
+        checkedKeyList = [...new Set(checkedKeys.value || [])]
+        expandedKeyList = props.expandChecked ? ([...(checkedKeys.value || []), ...(expandedKeys.value || [])]) : expandedKeys.value
+      } else {
+        checkedKeyList = checkedKeys.value || null
+        expandedKeyList = props.expandChecked && checkedKeys.value ? ([checkedKeys.value, ...(expandedKeys.value || [])]) : expandedKeys.value
+      }
+
+      handleCheckState(list, checkedKeyList, true)
+
+      // 处理初始展开
+      expandedKeyList = [...new Set(expandedKeyList)]
+      if (!props.defaultExpandAll) {
+        handleExpandState(list, expandedKeyList, true)
+      }
+
+      list.sort((a, b) => {
+        if (a.sort === 0 && b.sort === 0) {
+          return 0
+        }
+
+        if (a.parentKey === b.parentKey) {
+          if (a.sort - b.sort > 0) {
+            return 1
+          } else {
+            return -1
+          }
+        }
+
+        return 0
+      })
+
+      return list
+    }
+
+    /**
+     * 处理选中
+     * @param list
+     * @param checkedKeyList
+     */
+    function handleCheckState(list, checkedKeyList, checked = true) {
+      // 多选
+      if (props.showCheckbox) {
+        if (checkedKeyList?.length) {
+          checkedKeyList.forEach(k => {
+            const item = datamap.value[k]
+            if (item) {
+              checkTheChecked(item, checked)
+            }
+          })
+        }
+
+        return
+      }
+
+      // 单选
+      for (let i = 0; i < list.length; i++) {
+        const item = list[i]
+        if (item.key === checkedKeyList) {
+          checkTheRadio(item, checked)
+          break
+        }
+      }
+    }
+
+    /**
+     * 校验多选节点
+     * @param item
+     * @param checked
+     */
+    function checkTheChecked(item, checked = true) {
+      const { childrenKeys, parentKeys, disabled = false } = item
+      if (!props.checkedDisabled && disabled) return
+
+      // 当前
+      item.checkedStatus = checked ? isCheckedStatus : unCheckedStatus
+
+      if (!props.checkStrictly) {
+        // 子类
+        childrenKeys.forEach(k => {
+          const childrenItem = unref(datamap)[k]
+          childrenItem.checkedStatus = (!props.checkedDisabled && childrenItem.disabled) ? childrenItem.checkedStatus : item.checkedStatus
+        })
+
+        // 父类
+        parentKeys.forEach(k => {
+          const parentItem = datamap.value[k]
+          parentItem.checkedStatus = getParentCheckedStatus(parentItem)
+        })
+      }
+    }
+
+    /**
+     * 校验单选节点
+     * @param item
+     */
+    function checkTheRadio(item, checked) {
+      const { parentKeys, isLeaf, disabled = false } = item
+      if (!props.checkedDisabled && disabled) return
+
+      // 限制末节点选中,但当前非末节点
+      if (props.onlyRadioLeaf && !isLeaf) {
+        logError(`限制了末节点选中,当前[${item.label}]非末节点`)
+        return
+      }
+
+      if (datalist.value?.length) {
+        datalist.value.forEach(k => {
+          k.checkedStatus = unCheckedStatus
+        })
+      }
+
+      parentKeys.forEach(k => {
+        const parentItem = datamap.value[k]
+        parentItem.checkedStatus = checked ? getParentCheckedStatus(parentItem) : unCheckedStatus
+      })
+
+      // 当前
+      item.checkedStatus = checked ? isCheckedStatus : unCheckedStatus
+    }
+
+    /**
+     * 处理父节点展开
+     * @param item
+     * @param expand
+     */
+    // function handleExpandParentNode(item, expand = true) {
+    //   if (!expand) return
+
+    //   if (item?.parentKeys?.length) {
+    //     item.parentKeys.forEach(pk => {
+    //       if (!datamap.value[pk].expand) {
+    //         datamap.value[pk].expand = true
+    //       }
+    //     })
+    //   }
+    // }
+
+    /**
+     * 处理节点展开
+     * @param list
+     * @param expandedKeyList
+     * @param expand
+     */
+    function handleExpandState(list, expandedKeyList, expand = true) {
+      // 收起
+      if (expand === false) {
+        for (let i = 0; i < list.length; i++) {
+          const item = list[i]
+          if (expandedKeyList?.includes(item.key)) {
+            item.expand = false
+            if (item.childrenKeys?.length) {
+              item.childrenKeys.forEach(ck => {
+                datamap.value[ck].expand = false
+                datamap.value[ck].show = false
+              })
+            }
+          }
+        }
+        return
+      }
+      // 展开
+      for (let i = 0; i < list.length; i++) {
+        const item = list[i]
+        // 处理展开
+        if (expandedKeyList?.includes(item.key)) {
+          // 父子
+          item.expand = true
+          if (item.children?.length) {
+            item.children.forEach(k => {
+              const kItem = unref(datamap)[k.key]
+              kItem.show = true
+            })
+          }
+
+          // 族系
+          if (item.parentKeys?.length) {
+            item.parentKeys.forEach(k => {
+              const kItem = unref(datamap)[k]
+              kItem.expand = true
+              if (kItem.children?.length) {
+                kItem.children.forEach(k => {
+                  const skItem = unref(datamap)[k.key]
+                  skItem.show = true
+                })
+              }
+            })
+          }
+        }
+      }
+    }
+
+    /**
+     * 点击选框
+     * @param item
+     */
+    function handleCheckChange(item) {
+      const { childrenKeys, parentKeys, checkedStatus, isLeaf, disabled = false } = item
+      if (!props.showCheckbox) return
+      if (disabled) return
+
+      // 当前
+      item.checkedStatus = checkedStatus === isCheckedStatus ? unCheckedStatus : isCheckedStatus
+
+      // 子类
+      if (!props.checkStrictly) {
+        if (props.expandChecked) {
+          item.show = true
+          item.expand = childrenKeys?.length > 0 || isLeaf
+        }
+
+        childrenKeys.forEach(k => {
+          const childrenItem = unref(datamap)[k]
+          childrenItem.checkedStatus = childrenItem.disabled ? childrenItem.checkedStatus : item.checkedStatus
+
+          if (props.expandChecked) {
+            childrenItem.show = true
+            childrenItem.expand = childrenItem?.childrenKeys?.length > 0 || childrenItem.isLeaf
+          }
+        })
+      } else {
+        if (props.expandChecked) {
+          logError(`多选时,当 checkStrictly 为 true 时,不支持选择自动展开子节点属性(expandChecked)`)
+        }
+      }
+
+      // 父类
+      if (!props.checkStrictly) {
+        parentKeys.forEach(k => {
+          const parentItem = datamap.value[k]
+          parentItem.checkedStatus = getParentCheckedStatus(parentItem)
+        })
+      }
+
+      const hasCheckedKeys = []
+      for (let i = 0; i < datalist.value.length; i++) {
+        const k = datalist.value[i]
+        if (k.checkedStatus === isCheckedStatus) {
+          if ((props.packDisabledkey && k.disabled) || !k.disabled) {
+            hasCheckedKeys.push(k.key)
+          }
+        }
+      }
+
+      checkedKeys.value = [...hasCheckedKeys]
+
+      emit('change', hasCheckedKeys, item)
+    }
+
+    /**
+     * 点击单选
+     * @param item
+     */
+    function handleRadioChange(item) {
+      const { parentKeys, checkedStatus, key, disabled = false, isLeaf } = item
+      if (props.showCheckbox) return
+      if (props.onlyRadioLeaf && !isLeaf) handleExpandedChange(item)
+
+      if (disabled) return
+
+      // 重置所有选择
+      if (datalist.value?.length) {
+        for (let i = 0; i < datalist.value.length; i++) {
+          const k = datalist.value[i]
+          k.checkedStatus = unCheckedStatus
+        }
+      }
+
+      parentKeys.forEach(k => {
+        const parentItem = datamap.value[k]
+        parentItem.checkedStatus = getParentCheckedStatus(parentItem)
+      })
+
+      // 当前
+      item.checkedStatus = checkedStatus === isCheckedStatus ? unCheckedStatus : isCheckedStatus
+
+      checkedKeys.value = key
+      emit('change', key, item)
+    }
+
+    /**
+     * 点击标签
+     */
+    function handleLabelClick(item) {
+      if (props.showCheckbox) {
+        handleCheckChange(item)
+      } else {
+        handleRadioChange(item)
+      }
+    }
+
+    /**
+     * 点击展开收起
+     * @param item
+     */
+    async function handleExpandedChange(item) {
+      if (props.filterValue) return
+
+      const { expand, loading = false, disabled } = item
+      if (loadLoading.value && loading) return
+
+      checkExpandedChange(item)
+
+      // 异步
+      item.expand = !expand
+
+      let currentItem = null
+      if (!disabled) {
+        if (!props.showCheckbox && props.onlyRadioLeaf && props.loadMode) {
+          logError(`单选时,当 onlyRadioLeaf 为 true 时不支持动态数据`)
+        } else {
+          currentItem = await loadExpandNode(item)
+        }
+      }
+
+      emit('expand', !expand, currentItem || item || null)
+    }
+
+    /**
+     * 检查展开状态
+     * @param item
+     */
+    function checkExpandedChange(item) {
+      const { expand, childrenKeys, children = null } = item
+
+      if (expand) {
+        if (childrenKeys?.length) {
+          childrenKeys.forEach(k => {
+            if (unref(datamap)[k]) {
+              unref(datamap)[k].show = false
+              unref(datamap)[k].expand = false
+            }
+          })
+        }
+      } else {
+        if (children?.length) {
+          const childrenKeys = children.map(k => k.key)
+          childrenKeys.forEach(k => {
+            if (unref(datamap)[k]) {
+              unref(datamap)[k].show = true
+            }
+          })
+        }
+      }
+    }
+
+    /**
+     * 加载异步数据
+     * @param item
+     */
+    async function loadExpandNode(item) {
+      const { expand, key, loaded, children } = item
+      if (children?.length && !props.alwaysFirstLoad) {
+        return item
+      }
+
+      if (expand && props.loadMode && !loaded) {
+        if (isFunction(props.loadApi)) {
+          expandedKeys.value.push(key)
+          loadLoading.value = true
+          item.loading = true
+
+          const currentNode = deepClone(item)
+          const apiRes = await props.loadApi(currentNode)
+
+          // 新增子项
+          let newChildren = [...(item.originItem?.children || []), ...(apiRes || [])]
+          const newChildrenObj = {}
+          newChildren = newChildren.reduce((total, next) => {
+            newChildrenObj[next[fieldMap.value]] ? '' : newChildrenObj[next[fieldMap.value]] = true && total.push(next)
+            return total
+          }, [])
+
+          item.originItem.children = newChildren || null
+          if (apiRes?.length) {
+            const insertIndex = datalist.value.findIndex(k => k.key === item.key)
+            handleTreeData(apiRes, item, item.level + 1, insertIndex)
+            datalist.value = checkInitData(datalist.value)
+          } else {
+            // 加载后无数据就移除展开图标
+            item.expand = false
+            item.isLeaf = true
+            item.showArrow = false
+          }
+
+          loadLoading.value = false
+          item.loading = false
+          item.loaded = true
+        }
+      } else {
+        const eki = expandedKeys.value.findIndex(k => k === key)
+        if (eki >= 0) {
+          expandedKeys.value.splice(eki, 1)
+        }
+      }
+
+      return item
+    }
+
+    /**
+     * 获取父类的选中状态
+     * @param item
+     */
+    function getParentCheckedStatus(item) {
+      if (!item) {
+        return unCheckedStatus
+      }
+
+      if (!props.checkedDisabled && item.disabled) {
+        return item.checkedStatus || unCheckedStatus
+      }
+
+      // 单选时,父类永远为半选
+      if (!props.showCheckbox) {
+        return halfCheckedStatus
+      }
+
+      const { children } = item
+      // 子类全选中
+      const childrenCheckedAll = children.every(k => k.checkedStatus === isCheckedStatus)
+      if (childrenCheckedAll) {
+        return isCheckedStatus
+      }
+
+      // 子类全不选中
+      const childrenUncheckedAll = children.every(k => k.checkedStatus === unCheckedStatus)
+      if (childrenUncheckedAll) {
+        return unCheckedStatus
+      }
+
+      return halfCheckedStatus
+    }
+
+    function filterData() {
+      if (props.filterValue === '') {
+        datalist.value.forEach(k => {
+          k.show = true
+        })
+        return
+      }
+
+      datalist.value.forEach(k => {
+        if (k.label.indexOf(props.filterValue) > -1) {
+          k.show = true
+          k.parentKeys.forEach(k => {
+            datamap.value[k].show = true
+          })
+        } else {
+          k.show = false
+        }
+      })
+
+      datalist.value.forEach(k => {
+        if (k.show) {
+          k.parentKeys.forEach(k => {
+            datamap.value[k].show = true
+          })
+        }
+      })
+    }
+
+    /**
+     * 返回已选的 key
+     */
+    const getCheckedKeys = () => getAllNodeKeys(datalist.value, 'checkedStatus', isCheckedStatus, props.packDisabledkey)
+    /**
+     * 根据key设置已选
+     * @param keys 单选时为数字或者字符串,多选时为数组
+     * @param checked 多选时为key的数组,单选时为key
+     */
+    function setCheckedKeys(keys, checked = true) {
+      // 多选
+      if (props.showCheckbox) {
+        if (!isArray(keys)) {
+          logError(`setCheckedKeys 第一个参数非数组,传入的是[${keys}]`)
+          return
+        }
+
+        const list = datalist.value
+
+        // 取消选择
+        if (checked === false) {
+          let newCheckedKeys = []
+          for (let i = 0; i < checkedKeys.value.length; i++) {
+            const ck = checkedKeys.value[i]
+            if (!keys.includes(ck)) {
+              newCheckedKeys.push(ck)
+            }
+          }
+          newCheckedKeys = [...new Set(newCheckedKeys)]
+          checkedKeys.value = newCheckedKeys
+          handleCheckState(list, keys, false)
+
+          return
+        }
+
+        // 选择
+        const newCheckedKeys = [...checkedKeys.value, ...keys]
+        checkedKeys.value = [...new Set(newCheckedKeys)]
+        handleCheckState(list, checkedKeys.value, true)
+
+        if (props.expandChecked && checked) {
+          expandedKeys.value = [...new Set([...(checkedKeys.value || []), ...(keys || [])])]
+          handleExpandState(list, keys, true)
+        }
+        return
+      }
+
+      // 单选
+      // 如果为数组则拿第一个
+      if (isArray(keys)) {
+        keys = keys[0]
+      }
+
+      if (!isString(keys) && !isNumber(keys)) {
+        logError('setCheckedKeys 第一个参数字符串或数字,传入的是==>', keys)
+        return
+      }
+
+      const list = datalist.value
+      checkedKeys.value = checked ? keys : null
+
+      if (props.expandChecked && checked) {
+        handleExpandState(list, [keys], true)
+      }
+
+      handleCheckState(list, keys, !!checked)
+    }
+    /**
+     * 返回半选的 key
+     */
+    const getHalfCheckedKeys = () => getAllNodeKeys(datalist.value, 'checkedStatus', halfCheckedStatus, props.packDisabledkey)
+    /**
+     * 返回未选的 key
+     */
+    const getUncheckedKeys = () => getAllNodeKeys(datalist.value, 'checkedStatus', unCheckedStatus, props.packDisabledkey)
+    /**
+     * 返回已展开的 key
+     */
+    const getExpandedKeys = () => getAllNodeKeys(datalist.value, 'expand', true)
+    /**
+     * 返回未展开的 key
+     */
+    const getUnexpandedKeys = () => getAllNodeKeys(datalist.value, 'expand', false)
+    /**
+     * 根据key展开/收起
+     *
+     * @param keys 数组,或字符串 all
+     * @param expand true为展开/false为收起
+     */
+    function setExpandedKeys(keys, expand = true) {
+      if (!Array.isArray(keys) && keys !== 'all') {
+        logError('setExpandedKeys 第一个参数非数组,传入的是===>', keys)
+        return
+      }
+
+      const list = datalist.value
+
+      // 展开/收起全部
+      if (keys === 'all') {
+        list.forEach(k => {
+          k.expand = expand
+          if (k.level > 0) {
+            k.show = expand
+          }
+        })
+        return
+      }
+
+      // 收起
+      if (expand === false) {
+        const newExpandedKeys = []
+        for (let i = 0; i < expandedKeys.value.length; i++) {
+          const ek = expandedKeys.value[i]
+          if (!keys.includes(ek)) {
+            newExpandedKeys.push(ek)
+          }
+        }
+        expandedKeys.value = [...new Set(newExpandedKeys)]
+        handleExpandState(list, keys, false)
+
+        return
+      }
+
+      // 展开
+      const newExpandedKeys = []
+      for (let i = 0; i < list.length; i++) {
+        if (keys.includes(list[i].key)) {
+          newExpandedKeys.push(list[i].key)
+        }
+      }
+      expandedKeys.value = [...new Set(newExpandedKeys)]
+      handleExpandState(list, newExpandedKeys, true)
+    }
+    /**
+     * 返回已选的节点
+     */
+    const getCheckedNodes = () => getAllNodes(datalist.value, 'checkedStatus', isCheckedStatus, props.packDisabledkey)
+    /**
+     * 返回半选的节点
+     */
+    const getHalfCheckedNodes = () => getAllNodes(datalist.value, 'checkedStatus', halfCheckedStatus, props.packDisabledkey)
+    /**
+     * 返回未选的节点
+     */
+    const getUncheckedNodes = () => getAllNodes(datalist.value, 'checkedStatus', unCheckedStatus, props.packDisabledkey)
+    /**
+     * 返回已展开的节点
+     */
+    const getExpandedNodes = () => getAllNodes(datalist.value, 'expand', true)
+    /**
+     * 返回未展开的节点
+     */
+    const getUnexpandedNodes = () => getAllNodes(datalist.value, 'expand', false)
+
+    watch(
+      () => props.defaultExpandedKeys,
+      (v) => {
+        if (v?.length) {
+          expandedKeys.value = v
+        } else {
+          expandedKeys.value = []
+        }
+
+        // if (v) checkInitData(datalist.value)
+      },
+      { immediate: true }
+    )
+
+    watch(
+      () => props.defaultCheckedKeys,
+      (v) => {
+        if (props.showCheckbox) {
+          if (v?.length) {
+            checkedKeys.value = v
+          } else {
+            checkedKeys.value = []
+          }
+        } else {
+          if (v || v === 0) {
+            checkedKeys.value = v
+          } else {
+            checkedKeys.value = null
+          }
+        }
+        // checkInitData(datalist.value)
+      },
+      { immediate: true }
+    )
+
+    watch(
+      () => props.data,
+      (v) => {
+        dataRef.value = deepClone(v)
+        setTimeout(() => {
+          initData()
+        }, 36)
+      },
+      { immediate: true, deep: true }
+    )
+
+    watch(
+      () => props.filterValue,
+      () => {
+        filterData()
+      },
+    )
+
+    return {
+      datalist,
+      unCheckedStatus,
+      halfCheckedStatus,
+      isCheckedStatus,
+      handleCheckChange,
+      handleRadioChange,
+      handleLabelClick,
+      handleExpandedChange,
+      loadLoading,
+
+      // updateChildrenByKey: () => {},
+      // insertBeforeByKey: () => {},
+      // insertAfterByKey: () => {},
+      getCheckedKeys,
+      setCheckedKeys,
+      getHalfCheckedKeys,
+      getUncheckedKeys,
+      getExpandedKeys,
+      getUnexpandedKeys,
+      setExpandedKeys,
+      getCheckedNodes,
+      getHalfCheckedNodes,
+      getUncheckedNodes,
+      getExpandedNodes,
+      getUnexpandedNodes,
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+@font-face {
+  font-family: 'da-tree-iconfont'; /* Project id  */
+  src: url('data:application/octet-stream;base64,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') format('truetype');
+}
+
+.da-tree {
+  width: 100%;
+  height: 100%;
+
+  &-scroll {
+    width: 100%;
+    height: 100%;
+  }
+
+  &-item {
+    display: flex;
+    align-items: center;
+    height: 0;
+    padding: 0;
+    overflow: hidden;
+    font-size: 28rpx;
+    line-height: 1;
+    visibility: hidden;
+    opacity: 0;
+    transition: opacity 0.2s linear;
+
+    &.is-show {
+      height: auto;
+      padding: 12rpx 24rpx;
+      visibility: visible;
+      opacity: 1;
+    }
+
+    &__icon {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 40rpx;
+      height: 40rpx;
+      overflow: hidden;
+
+      &--arr {
+        position: relative;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 32rpx;
+        height: 32rpx;
+
+        &::after {
+          position: relative;
+          z-index: 1;
+          overflow: hidden;
+          /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
+          font-family: 'da-tree-iconfont' !important;
+          font-size: 32rpx;
+          font-style: normal;
+          color: #999;
+          -webkit-font-smoothing: antialiased;
+          -moz-osx-font-smoothing: grayscale;
+        }
+
+        &.is-expand {
+          &::after {
+            content: '\e604';
+          }
+        }
+
+        &.is-right {
+          transform: rotate(-90deg);
+        }
+
+        &.is-loading {
+          animation: IconLoading 1s linear 0s infinite;
+
+          &::after {
+            content: '\e7f1';
+          }
+        }
+      }
+    }
+
+    &__checkbox {
+      width: 40rpx;
+      height: 40rpx;
+      overflow: hidden;
+
+      &--left {
+        order: 0;
+      }
+
+      &--right {
+        order: 1;
+      }
+
+      &--icon {
+        position: relative;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 40rpx;
+        height: 40rpx;
+
+        &::after {
+          position: relative;
+          top: 0;
+          left: 0;
+          z-index: 1;
+          overflow: hidden;
+          /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
+          font-family: 'da-tree-iconfont' !important;
+          font-size: 32rpx;
+          font-style: normal;
+          -webkit-font-smoothing: antialiased;
+          -moz-osx-font-smoothing: grayscale;
+        }
+
+        &.da-tree-checkbox-outline::after {
+          color: #bbb;
+          content: '\ead5';
+        }
+
+        &.da-tree-checkbox-checked::after {
+          color: var(--theme-color,#007aff);
+          content: '\ead4';
+        }
+
+        &.da-tree-checkbox-indeterminate::after {
+          color: var(--theme-color,#007aff);
+          content: '\ebce';
+        }
+
+        &.da-tree-radio-outline::after {
+          color: #bbb;
+          content: '\ecc5';
+        }
+
+        &.da-tree-radio-checked::after {
+          color: var(--theme-color,#007aff);
+          content: '\ecc4';
+        }
+
+        &.da-tree-radio-indeterminate::after {
+          color: var(--theme-color,#007aff);
+          content: '\ea4f';
+        }
+      }
+
+      &.is--disabled {
+        cursor: not-allowed;
+        opacity: 0.35;
+      }
+    }
+
+    &__label {
+      flex: 1;
+      margin-left: 4rpx;
+      color: #555;
+
+      &--2 {
+        color: var(--theme-color,#007aff);
+      }
+
+      &--append {
+        font-size: 60%;
+        opacity: 0.6;
+      }
+    }
+  }
+}
+
+@keyframes IconLoading {
+  0% {
+    transform: rotate(0deg);
+  }
+
+  100% {
+    transform: rotate(360deg);
+  }
+}
+</style>
diff --git a/components/da-tree/props.ts b/components/da-tree/props.ts
new file mode 100644
index 0000000..2b80e0d
--- /dev/null
+++ b/components/da-tree/props.ts
@@ -0,0 +1,197 @@
+export default {
+  /**
+   * 树的数据
+   */
+  data: {
+    type: Array,
+    default: () => [],
+  },
+  /**
+   * 主题色
+   */
+  themeColor: {
+    type: String,
+    default: '#007aff',
+  },
+  /**
+   * 是否开启多选,默认单选
+   */
+  showCheckbox: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 默认选中的节点,注意单选时为单个key,多选时为key的数组
+   */
+  defaultCheckedKeys: {
+    type: [Array, String, Number],
+    default: null,
+  },
+  /**
+   * 是否默认展开全部
+   */
+  defaultExpandAll: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 默认展开的节点
+   */
+  defaultExpandedKeys: {
+    type: Array,
+    default: null,
+  },
+  /**
+   * 筛选关键词
+   */
+  filterValue: {
+    type: String,
+    default: '',
+  },
+  /**
+   * 是否自动展开到选中的节点,默认不展开
+   */
+  expandChecked: {
+    type: Boolean,
+    default: false,
+  },
+
+  /**
+   * (旧)字段对应内容,默认为 {label: 'label',key: 'key', children: 'children', disabled: 'disabled', append: 'append'}
+   * 注意:1.5.0版本后不再兼容
+   */
+  field: {
+    type: Object,
+    default: null,
+  },
+  /**
+   * 标签字段(新,拆分了)
+   */
+  labelField: {
+    type: String,
+    default: 'label',
+  },
+  /**
+   * 值字段(新,拆分了)
+   */
+  valueField: {
+    type: String,
+    default: 'value',
+  },
+  /**
+   * 下级字段(新,拆分了)
+   */
+  childrenField: {
+    type: String,
+    default: 'children',
+  },
+  /**
+   * 禁用字段(新,拆分了)
+   */
+  disabledField: {
+    type: String,
+    default: 'disabled',
+  },
+  /**
+   * 末级节点字段(新,拆分了)
+   */
+  leafField: {
+    type: String,
+    default: 'leaf',
+  },
+  /**
+   * 副标签字段(新,拆分了)
+   */
+  appendField: {
+    type: String,
+    default: 'append',
+  },
+  /**
+   * 排序字段(新,拆分了)
+   */
+  sortField: {
+    type: String,
+    default: 'sort',
+  },
+  /**
+   * Api数据返回后的结果路径,支持嵌套如`data.list`
+   */
+  resultField: {
+    type: String,
+    default: '',
+  },
+  isLeafFn: {
+    type: Function,
+    default: null,
+  },
+  /**
+   * 是否显示单选图标,默认显示
+   */
+  showRadioIcon: {
+    type: Boolean,
+    default: true,
+  },
+  /**
+   * 单选时只允许选中末级,默认可随意选中
+   */
+  onlyRadioLeaf: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 多选时,是否执行父子不关联的任意勾选,默认父子关联
+   */
+  checkStrictly: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 为 true 时,空的 children 数组会显示展开图标
+   */
+  loadMode: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 异步加载接口
+   */
+  loadApi: {
+    type: Function,
+    default: null,
+  },
+  /**
+   * 是否总在首次的时候加载一下内容,来比对是否一致
+   */
+  alwaysFirstLoad: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 是否渲染(操作)禁用值
+   */
+  checkedDisabled: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 是否返回已禁用的但已选中的key
+   */
+  packDisabledkey: {
+    type: Boolean,
+    default: true,
+  },
+  /**
+   * 选择框的位置,可选 left/right
+   */
+  checkboxPlacement: {
+    type: String,
+    default: 'left',
+  },
+  /**
+   * 子项缩进距离,默认40,单位rpx
+   */
+  indent: {
+    type: Number,
+    default: 40,
+  },
+}
diff --git a/components/da-tree/readme.md b/components/da-tree/readme.md
new file mode 100644
index 0000000..9b06ead
--- /dev/null
+++ b/components/da-tree/readme.md
@@ -0,0 +1,310 @@
+# da-tree
+
+一个基于 Vue3 的 tree(树)组件,同时支持主题换色,可能是最适合你的 tree(树)组件
+
+组件一直在更新,遇到问题可在下方讨论。
+
+`同时更新 Vue2 版本,在此查看 ===>` **[Vue2 版](https://ext.dcloud.net.cn/plugin?id=12692)**
+
+### 关于使用
+
+可在右侧的`使用 HBuilderX 导入插件`或`下载示例项目ZIP`,方便快速上手。
+
+可通过下方的示例及文档说明,进一步了解使用组件相关细节参数。
+
+插件地址:https://ext.dcloud.net.cn/plugin?id=12384
+
+### 组件示例
+
+```jsx
+<template>
+  <view>多选</view>
+  <view><button @click="doCheckedTree(['2'],true)">全选</button></view>
+  <view><button @click="doCheckedTree(['2'],false)">取消全选</button></view>
+  <view><button @click="doCheckedTree(['211','222'],true)">选中指定节点</button></view>
+  <view><button @click="doCheckedTree(['211','222'],false)">取消选中指定节点</button></view>
+  <view><button @click="doExpandTree('all',true)">展开全部节点</button></view>
+  <view><button @click="doExpandTree('all',false)">收起全部节点</button></view>
+  <view><button @click="doExpandTree(['22','23'],true)">展开节点</button></view>
+  <view><button @click="doExpandTree(['22','23'],false)">收起节点</button></view>
+  <DaTree
+    ref="DaTreeRef"
+    :data="roomTreeData"
+    labelField="name"
+    valueField="id"
+    defaultExpandAll
+    showCheckbox
+    :defaultCheckedKeys="defaultCheckedKeysValue"
+    @change="handleTreeChange"
+    @expand="handleExpandChange"></DaTree>
+  <view>单选</view>
+  <DaTree
+    :data="roomTreeData"
+    labelField="name"
+    valueField="id"
+    defaultExpandAll
+    :defaultCheckedKeys="defaultCheckedKeysValue2"
+    @change="handleTreeChange"
+    @expand="handleExpandChange"></DaTree>
+  <view>默认展开指定节点</view>
+  <DaTree
+    :data="roomTreeData"
+    labelField="name"
+    valueField="id"
+    showCheckbox
+    :defaultExpandedKeys="defaultExpandKeysValue3"
+    @change="handleTreeChange"
+    @expand="handleExpandChange"></DaTree>
+  <view>异步加载数据</view>
+  <DaTree
+    :data="roomTreeData"
+    labelField="name"
+    valueField="id"
+    showCheckbox
+    loadMode
+    :loadApi="GetApiData"
+    defaultExpandAll
+    @change="handleTreeChange"
+    @expand="handleExpandChange"></DaTree>
+</template>
+```
+
+```js
+import { defineComponent, ref } from 'vue'
+
+/**
+ * 模拟创建一个接口数据
+ */
+function GetApiData(currentNode) {
+  const { key } = currentNode
+
+  return new Promise((resolve) => {
+    setTimeout(() => {
+      // 模拟返回空数据
+      if (key.indexOf('-') > -1) {
+        return resolve(null)
+        // return resolve([])
+      }
+
+      return resolve([
+        {
+          id: `${key}-1`,
+          name: `行政部X${key}-1`,
+        },
+        {
+          id: `${key}-2`,
+          name: `财务部X${key}-2`,
+          append: '定义了末项数据',
+          leaf: true,
+        },
+        {
+          id: `${key}-3`,
+          name: `资源部X${key}-3`,
+        },
+        {
+          id: `${key}-4`,
+          name: `资源部X${key}-3`,
+          append: '被禁用,无展开图标',
+          disabled: true,
+        },
+      ])
+    }, 2000)
+  })
+}
+
+import DaTree from '@/components/da-tree/index.vue'
+export default defineComponent({
+  components: { DaTree },
+  setup() {
+    const DaTreeRef = ref()
+    // key的类型必须对应树数据key的类型
+    const defaultCheckedKeysValue = ref(['211', '222'])
+    const defaultCheckedKeysValue2 = ref('222')
+    const defaultExpandKeysValue3 = ref(['212', '231'])
+    const roomTreeData = ref([
+      {
+        id: '2',
+        name: '行政中心',
+        children: [
+          {
+            id: '21',
+            name: '行政部',
+            children: [
+              {
+                id: '211',
+                name: '行政一部',
+                children: null,
+              },
+              {
+                id: '212',
+                name: '行政二部',
+                children: [],
+                disabled: true,
+              },
+            ],
+          },
+          {
+            id: '22',
+            name: '财务部',
+            children: [
+              {
+                id: '221',
+                name: '财务一部',
+                children: [],
+                disabled: true,
+              },
+              {
+                id: '222',
+                name: '财务二部',
+                children: [],
+              },
+            ],
+          },
+          {
+            id: '23',
+            name: '人力资源部',
+            children: [
+              {
+                id: '231',
+                name: '人力一部',
+                children: [],
+              },
+              {
+                id: '232',
+                name: '人力二部',
+                append: '更多示例,请下载示例项目查看',
+              },
+            ],
+          },
+        ],
+      },
+    ])
+    function doExpandTree(keys, expand) {
+      DaTreeRef.value?.setExpandedKeys(keys, expand)
+
+      const gek = DaTreeRef.value?.getExpandedKeys()
+      console.log('当前已展开的KEY ==>', gek)
+    }
+    function doCheckedTree(keys, checked) {
+      DaTreeRef.value?.setCheckedKeys(keys, checked)
+
+      const gek = DaTreeRef.value?.getCheckedKeys()
+      console.log('当前已选中的KEY ==>', gek)
+    }
+    function handleTreeChange(allSelectedKeys, currentItem) {
+      console.log('handleTreeChange ==>', allSelectedKeys, currentItem)
+    }
+    function handleExpandChange(expand, currentItem) {
+      console.log('handleExpandChange ==>', expand, currentItem)
+    }
+    return {
+      DaTreeRef,
+      roomTreeData,
+      defaultCheckedKeysValue,
+      defaultCheckedKeysValue2,
+      defaultExpandKeysValue3,
+      handleTreeChange,
+      handleExpandChange,
+      GetApiData,
+      doExpandTree,
+      doCheckedTree,
+    }
+  },
+})
+```
+
+** 更多示例请下载/导入示例项目 ZIP 查看 **
+
+### 组件参数
+
+| 属性                | 类型                            | 默认值     | 必填 | 说明                                                                         |
+| :------------------ | :------------------------------ | :--------- | :--- | :--------------------------------------------------------------------------- |
+| data                | `Array`                         | -          | 是   | 树的数据                                                                     |
+| themeColor          | `String`                        | `#007aff`  | 否   | 主题色,十六进制                                                             |
+| defaultCheckedKeys  | `Array` \| `Number` \| `String` | -          | 否   | 默认选中的节点,单选为单个 key,多选为 key 的数组                            |
+| showCheckbox        | `Boolean`                       | `false`    | 否   | 是否开启多选,默认单选                                                       |
+| checkStrictly       | `Boolean`                       | `false`    | 否   | 多选时,是否执行父子不关联的任意勾选,默认父子关联                           |
+| showRadioIcon       | `Boolean`                       | `true`     | 否   | 是否显示单选图标,默认显示                                                   |
+| onlyRadioLeaf       | `Boolean`                       | `true`     | 否   | 单选时只允许选中末级,默认可随意选中                                         |
+| defaultExpandAll    | `Boolean`                       | `false`    | 否   | 是否默认展开全部                                                             |
+| defaultExpandedKeys | `Array`                         | -          | 否   | 默认展开的节点                                                               |
+| indent              | `Number`                        | `40`       | 否   | 子项缩进距离,单位 rpx                                                       |
+| checkboxPlacement   | `String`                        | `left`     | 否   | 选择框的位置,可选 left/right                                                |
+| loadMode            | `Boolean`                       | `false`    | 否   | 为 true 时,空的 children 数组会显示展开图标                                 |
+| loadApi             | `Function`                      | -          | 否   | 选择框的位置,可选 left/right                                                |
+| checkedDisabled     | `Boolean`                       | `false`    | 否   | 是否渲染禁用值,默认不渲染                                                   |
+| packDisabledkey     | `Boolean`                       | `true`     | 否   | 是否返回已禁用的但已选中的 key,默认返回禁用已选值                           |
+| expandChecked       | `Boolean`                       | `false`    | 否   | 是否自动展开到选中的节点,默认不展开                                         |
+| alwaysFirstLoad     | `Boolean`                       | `false`    | 否   | 是否总在首次的时候加载一下内容,默认不加载,否则只有展开末级节点才会加载数据 |
+| isLeafFn            | `Function`                      | -          | 否   | 自定义函数返回来控制数据项的末项                                             |
+| field               | `Object`                        | -          | 否   | 字段对应内容,格式参考下方(1.5.0 后移除,请用单独的字段匹配)                 |
+| labelField          | `String`                        | `label`    | 否   | 指定节点对象中某个属性为标签字段,默认`label`                                |
+| valueField          | `String`                        | `value`    | 否   | 指定节点对象中某个属性为值字段,默认`value`                                  |
+| childrenField       | `String`                        | `children` | 否   | 指定节点对象中某个属性为子树节点字段,默认`children`                         |
+| disabledField       | `String`                        | `disabled` | 否   | 指定节点对象中某个属性为禁用字段,默认`disabled`                             |
+| appendField         | `String`                        | `append`   | 否   | 指定节点对象中某个属性为副标签字段,默认`append`                             |
+| leafField           | `String`                        | `leaf`     | 否   | 指定节点对象中某个属性为末级节点字段,默认`leaf`                             |
+| sortField           | `String`                        | `sort`     | 否   | 指定节点对象中某个属性为排序字段,默认`sort`                                 |
+| filterValue         | `String`                        | -          | 否   | 搜索筛选的关键词,通过输入关键词筛选内容                                     |
+
+**field 格式(1.5.0 后移除,请用单独的字段匹配)**
+
+```js
+{
+  label: 'label',
+  key: 'key',
+  children: 'children',
+  disabled: 'disabled',
+  append: 'append'
+}
+```
+
+### 组件事件
+
+| 事件名称 | 回调参数                                | 说明            |
+| :------- | :-------------------------------------- | :-------------- |
+| change   | `(allCheckedKeys, currentItem) => void` | 选中时回调      |
+| expand   | `(expandState, currentItem) => void`    | 展开/收起时回调 |
+
+### 组件方法
+
+| 方法名称            | 参数             | 说明                                                                                              |
+| :------------------ | :--------------- | :------------------------------------------------------------------------------------------------ |
+| setCheckedKeys      | `(keys,checked)` | 设置指定 key 的节点选中/取消选中的状态。注: keys 单选时为 key,多选时为 key 的数组                |
+| setExpandedKeys     | `(keys,expand)`  | 设置指定 key 的节点展开/收起的状态,当 keys 为 all 时即代表展开/收起全部。注:keys 为数组或 `all` |
+| getCheckedKeys      | -                | 返回已选的 key                                                                                    |
+| getHalfCheckedKeys  | -                | 返回半选的 key                                                                                    |
+| getUncheckedKeys    | -                | 返回未选的 key                                                                                    |
+| getCheckedNodes     | -                | 返回已选的节点                                                                                    |
+| getUncheckedNodes   | -                | 返回未选的节点                                                                                    |
+| getHalfCheckedNodes | -                | 返回半选的节点                                                                                    |
+| getExpandedKeys     | -                | 返回已展开的 key                                                                                  |
+| getUnexpandedKeys   | -                | 返回未展开的 key                                                                                  |
+| getExpandedNodes    | -                | 返回已展开的节点                                                                                  |
+| getUnexpandedNodes  | -                | 返回未展开的节点                                                                                  |
+
+### 组件版本
+
+v1.4.2
+
+### 差异化
+
+已通过测试
+
+> - H5 页面
+> - 微信小程序
+> - 支付宝、钉钉小程序
+> - 字节跳动、抖音、今日头条小程序
+> - 百度小程序
+> - 飞书小程序
+> - QQ 小程序
+> - 京东小程序
+
+未测试
+
+> - 快手小程序由于非企业用户暂无演示
+> - 快应用、360 小程序因 Vue3 支持的原因暂无演示
+
+### 开发组
+
+[@CRLANG](https://crlang.com)
diff --git a/components/da-tree/utils.ts b/components/da-tree/utils.ts
new file mode 100644
index 0000000..f11bbb3
--- /dev/null
+++ b/components/da-tree/utils.ts
@@ -0,0 +1,150 @@
+/** 未选 */
+export const unCheckedStatus = 0
+/** 半选 */
+export const halfCheckedStatus = 1
+/** 选中 */
+export const isCheckedStatus = 2
+
+/**
+ * 深拷贝内容
+ * @param originData 拷贝对象
+ * @author crlang(https://crlang.com)
+ */
+export function deepClone(originData) {
+  const type = Object.prototype.toString.call(originData)
+  let data
+  if (type === '[object Array]') {
+    data = []
+    for (let i = 0; i < originData.length; i++) {
+      data.push(deepClone(originData[i]))
+    }
+  } else if (type === '[object Object]') {
+    data = {}
+    for (const prop in originData) {
+      // eslint-disable-next-line no-prototype-builtins
+      if (originData.hasOwnProperty(prop)) { // 非继承属性
+        data[prop] = deepClone(originData[prop])
+      }
+    }
+  } else {
+    data = originData
+  }
+  return data
+}
+
+/**
+ * 获取所有指定的节点
+ * @param type
+ * @param value
+ * @author crlang(https://crlang.com)
+ */
+export function getAllNodes(list, type, value, packDisabledkey = true) {
+  if (!list || list.length === 0) {
+    return []
+  }
+
+  const res = []
+  for (let i = 0; i < list.length; i++) {
+    const item = list[i]
+    if (item[type] === value) {
+      if ((packDisabledkey && item.disabled) || !item.disabled) {
+        res.push(item)
+      }
+    }
+  }
+
+  return res
+}
+
+/**
+ * 获取所有指定的key值
+ * @param type
+ * @param value
+ * @author crlang(https://crlang.com)
+ */
+export function getAllNodeKeys(list, type, value, packDisabledkey = true) {
+  if (!list || list.length === 0) {
+    return null
+  }
+
+  const res = []
+  for (let i = 0; i < list.length; i++) {
+    const item = list[i]
+    if (item[type] === value) {
+      if ((packDisabledkey && item.disabled) || !item.disabled) {
+        res.push(item.key)
+      }
+    }
+  }
+
+  return res.length ? res : null
+}
+
+/**
+ * 错误输出
+ *
+ * @param msg
+ */
+export function logError(msg, ...args) {
+  console.error(`DaTree: ${msg}`, ...args)
+}
+
+const toString = Object.prototype.toString
+
+export function is(val, type) {
+  return toString.call(val) === `[object ${type}]`
+}
+
+/**
+ * 是否对象(Object)
+ * @param val
+
+ */
+export function isObject(val) {
+  return val !== null && is(val, 'Object')
+}
+
+/**
+ * 是否数字(Number)
+ * @param val
+
+ */
+export function isNumber(val) {
+  return is(val, 'Number')
+}
+
+/**
+ * 是否字符串(String)
+ * @param val
+
+ */
+export function isString(val) {
+  return is(val, 'String')
+}
+
+/**
+ * 是否函数方法(Function)
+ * @param val
+
+ */
+export function isFunction(val) {
+  return typeof val === 'function'
+}
+
+/**
+ * 是否布尔(Boolean)
+ * @param val
+
+ */
+export function isBoolean(val) {
+  return is(val, 'Boolean')
+}
+
+/**
+ * 是否数组(Array)
+ * @param val
+
+ */
+export function isArray(val) {
+  return val && Array.isArray(val)
+}
diff --git a/main.js b/main.js
index d6eab51..0ecbff0 100644
--- a/main.js
+++ b/main.js
@@ -1,7 +1,7 @@
 import App from './App'
 import navbar from './components/navbar/navbar.vue'
-
 import { createSSRApp } from 'vue'
+
 export function createApp() {
 	const app = createSSRApp(App)
 	app.component('navbar', navbar)
diff --git a/package-lock.json b/package-lock.json
index 6cd76f1..063845d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,21 @@
 {
   "name": "water-drinking-uniapp",
+  "version": "1.0.0",
   "lockfileVersion": 3,
   "requires": true,
-  "packages": {}
+  "packages": {
+    "": {
+      "name": "water-drinking-uniapp",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "@dcloudio/uni-ui": "^1.4.28"
+      }
+    },
+    "node_modules/@dcloudio/uni-ui": {
+      "version": "1.5.6",
+      "resolved": "https://registry.npmmirror.com/@dcloudio/uni-ui/-/uni-ui-1.5.6.tgz",
+      "integrity": "sha512-jmb98PasFvZkrIDXGh94GbdWg2/jyhgs1HUG+bU8eyL7Ltias/5XBz4q8w9RXyWUfqepJRqapPA2IIQpLCuTIg=="
+    }
+  }
 }
diff --git a/package.json b/package.json
index c5861ff..815f9dd 100644
--- a/package.json
+++ b/package.json
@@ -1,16 +1,19 @@
 {
-  "name": "water-drinking-uniapp",
-  "version": "1.0.0",
-  "description": "",
-  "main": "main.js",
-  "dependencies":{
-	  "@dcloudio/uni-ui": "^1.4.28"
-  },
-  "devDependencies": {},
-  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
-  },
-  "keywords": [],
-  "author": "",
-  "license": "ISC"
-}
+    "id": "da-tree",
+    "name": "da-tree 树组件(支持单选、多选、无限级、主题色,Vue3版) ",
+    "displayName": "da-tree 树组件(支持单选、多选、无限级、主题色,Vue3版) ",
+    "version": "1.4.2",
+    "description": "一个基于 Vue3 的tree(树)组件,支持主题换色,可能是最适合你的tree(树)组件",
+    "keywords": [
+        "tree",
+        "树",
+        "树组件",
+        "da系列"
+    ],
+    "dcloudext": {
+        "category": [
+            "前端组件",
+            "通用组件"
+        ]
+    }
+}
\ No newline at end of file
diff --git a/pages.json b/pages.json
index 1f90b6f..a407d75 100644
--- a/pages.json
+++ b/pages.json
@@ -1,5 +1,11 @@
 {
 	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		// {
+		// 	"path" : "pages/addressAdd/index",
+		// 	"style": {
+		// 		"navigationStyle":"custom"
+		// 	}
+		// },
 		{
 			"path": "pages/index/index",
 			"style": {
@@ -41,6 +47,24 @@
 			"style": {
 				"navigationStyle":"custom"
 			}
+		},
+		{
+			"path" : "pages/addressEdit/index",
+			"style": {
+				"navigationStyle":"custom"
+			}
+		},
+		{
+			"path" : "pages/addressAdd/index",
+			"style": {
+				"navigationStyle":"custom"
+			}
+		},
+		{
+			"path" : "pages/addressLocate/index",
+			"style": {
+				"navigationStyle":"custom"
+			}
 		}
 	],
 	"globalStyle": {
diff --git a/pages/address/index.vue b/pages/address/index.vue
index ce84ea2..cc4fbc5 100644
--- a/pages/address/index.vue
+++ b/pages/address/index.vue
@@ -1,11 +1,57 @@
 <script setup>
-	
+	import { ref ,onMounted } from 'vue'
+	const addressList = ref([
+		{name:'张大左',phone:'13512334002',address:'重庆 重庆市 北碚区 互联网产业生态园 2-5-1',checked:true,id:'12'},
+		{name:'张大左',phone:'13512334002',address:'重庆 重庆市 北碚区 互联网产业生态园 2-5-1',checked:true,id:'12'},
+		{name:'张大左',phone:'13512334002',address:'重庆 重庆市 北碚区 互联网产业生态园 2-5-1',checked:true,id:'12'},
+		{name:'张大左',phone:'13512334002',address:'重庆 重庆市 北碚区 互联网产业生态园 2-5-1',checked:true,id:'12'},
+		{name:'张大左',phone:'13512334002',address:'重庆 重庆市 北碚区 互联网产业生态园 2-5-1',checked:true,id:'12'},
+		{name:'张大左',phone:'13512334002',address:'重庆 重庆市 北碚区 互联网产业生态园 2-5-1',checked:true,id:'12'},
+		{name:'',phone:'',address:'',checked:false,id:''},
+		{name:'',phone:'',address:'',checked:false,id:''},
+		{name:'',phone:'',address:'',checked:false,id:''},
+	])
+	function navTo(){
+		let title = '新增地址'
+		let id = 0
+		uni.navigateTo({
+			url:'/pages/addressAdd/index'
+		})
+	}
 </script>
 <template>
 	<view class="container">
 		<navbar title = '地址管理'></navbar>
 		<view class="content">
-			<view class="main"></view>
+			<view class="main">
+				<block v-for="(item,index) in addressList">
+					<view class="address-item">
+						<view class="user-info">
+							<text>{{item.name}}</text>
+							<text>{{item.phone}}</text>
+						</view>
+						<view class="address-info">{{item.address}}</view>
+						<view class="address-divide"></view>
+						<view class="handel">
+							<view class="handel-left">
+								 <radio color = "#4996E3" :value="item.id" :checked="item.checked"></radio>
+								<text>设为默认</text>
+							</view>
+							<view class="handel-right">
+								<view>
+									<image src="../../static/images/address/edit.png" alt=""></image>
+									<text>编辑</text>
+								</view>
+								<view>
+									<image src="../../static/images/address/delete.png" alt=""></image>
+									<text>删除</text>
+								</view>
+							</view>
+						</view>
+					</view>
+				</block>
+			</view>
+			<view class="subBtn" @click="navTo()">新增收货地址</view>
 		</view>
 	</view>
 </template>
@@ -16,15 +62,106 @@
 		height: 100vh;
 		.content{
 			width: 100%;
-			height:calc(100vh - 176rpx);
+			height:calc(100vh - 176rpx - 20rpx);
 			background:linear-gradient(to top,#FFFFFF,#E8EFFF);
 		    padding-top:20rpx;
 			.main{
 				width: 686rpx;
-				height:1262rpx;
-				background: #ffaaff;
+				height:1162rpx;
 				margin:0 auto;
+				overflow-y: scroll;
+				// background-color: #a6ffd3;
+				.address-item{
+					width:100%;
+					height:254rpx;
+					padding:20rpx 0 26rpx;
+					box-sizing: border-box;
+					margin-bottom:20rpx;
+					background: #FFFFFF;
+					display: flex;
+					flex-direction: column;
+					justify-content: space-between;
+					align-items:flex-start;
+					.user-info{
+						width:100%;
+						padding: 0 65rpx;
+						box-sizing: border-box;
+						display: flex;
+						justify-content: space-between;
+						font-weight: 300;
+						font-size: 32rpx;
+						color: #000000;
+					}
+					.address-info{
+						padding: 0 65rpx;
+						box-sizing: border-box;
+						font-weight: 300;
+						font-size: 28rpx;
+						color: #646464;
+					}
+					.address-divide{
+						width: 100%;
+						height: 1rpx;
+						border-bottom:2rpx dashed #D5DDE0;
+					}
+					.handel{
+						width: 100%;
+						padding: 0 65rpx;
+						box-sizing: border-box;
+						display: flex;
+						justify-content: space-between;
+						align-items:flex-start;
+						.handel-left{
+							radio{
+								color: #000000;
+							}
+							text{
+								font-weight: 300;
+								font-size: 32rpx;
+								color: #0088FF;
+								line-height:28rpx;
+							}
+						}
+						.handel-right{
+							width:35%;
+							height:48rpx;
+							display: flex;
+							justify-content:space-between;
+							align-items:center;
+							view{
+								display: flex;
+								align-items:center;
+								image{
+									width:28rpx;
+									height:28rpx;
+									margin-right:10rpx;
+								}
+								text{
+									font-weight: 300;
+									font-size: 24rpx;
+									color: #373737;
+								}
+							}
+						}
+					}
+				}
 			}
+	   .subBtn{
+		   width:686rpx;
+		   height: 98rpx;
+		   // margin-top:100rpx;
+		   padding:0 26rpx;
+		   box-sizing:border-box;
+		   background-color:#5EA1FA;
+		   border-radius:50rpx;
+		   font-weight: 300;
+		   font-size: 36rpx;
+		   color: #FFFFFF;
+		   line-height:98rpx;
+		   text-align: center;
+		   letter-spacing: 2rpx;
+		   margin: 100rpx auto 0;
+	   }
 	   }
    }
 </style>
diff --git a/pages/addressAdd/index.vue b/pages/addressAdd/index.vue
new file mode 100644
index 0000000..e7105be
--- /dev/null
+++ b/pages/addressAdd/index.vue
@@ -0,0 +1,198 @@
+<script setup>
+	import{ref} from 'vue'
+	const isDefault =ref(false)
+	const form = ref({
+		name:'',
+		phone:'',
+		region:'',
+		address:'',
+	})
+	function submit(){
+		
+	}
+	const treeListData = ref([
+	  {
+	    id: '2',
+	    name: '行政中心',
+	    children: [
+	      {
+	        id: '21',
+	        name: '行政部',
+	        children: [
+	          {
+	            id: '211',
+	            name: '行政一部',
+	            children: null,
+	          },
+	          {
+	            id: '212',
+	            name: '行政二部',
+	            children: [],
+	            disabled: true,
+	          },
+	        ],
+	      },
+	      {
+	        id: '22',
+	        name: '财务部',
+	        children: [
+	          {
+	            id: '221',
+	            name: '财务一部',
+	            children: [],
+	            disabled: true,
+	          },
+	          {
+	            id: '222',
+	            name: '财务二部',
+	            children: [],
+	          },
+	        ],
+	      },
+	      {
+	        id: '23',
+	        name: '人力资源部',
+	        children: [
+	          {
+	            id: '231',
+	            name: '人力一部',
+	            children: [],
+	          },
+	          {
+	            id: '232',
+	            name: '人力二部',
+	            append: '更多示例,请下载示例项目查看',
+	          },
+	        ],
+	      },
+	    ],
+	  },
+	])
+	function handleTreeChange(e){
+		console.log(e)
+	}
+	function handleExpandChange(e){
+		console.log(e)
+	}
+	const DaTreeRef = ref()
+</script>
+<template>
+	<view class="container">
+		<navbar title ='新增地址'></navbar>
+		<view class="content">
+			<view class="main">
+				<view>
+					<text>联系人</text>
+					<input v-model="form.name" placeholder="请输入联系人" />
+				</view>
+				<view>
+					<text>联系电话</text>
+					<input v-model="form.phpne" placeholder="请输入联系电话" />
+				</view>
+				<view >
+					<text>送水区域</text>
+					<!-- <da-tree ref="DaTreeRef"
+						:data="treeListData"
+						labelField="name"
+						valueField="id"
+						defaultExpandAll
+						showCheckbox
+						@change="handleTreeChange"
+						@expand="handleExpandChange"></da-tree> -->
+						
+						<input v-model="form.phpne" placeholder="请选择区域" />
+				</view>
+				<view>
+					<text>送水地址</text>
+					<input v-model="form.address" placeholder="请输入详细地址" />
+				</view>
+			</view>
+			<view class="default">
+				<view>设为默认收货地址</view>
+				<switch :checked = 'isDefault' color="#1890FF"/>
+			</view>
+			<view class="subBtn" @click="submit()">提交信息</view>
+		</view>
+	</view>
+</template>
+
+<style lang="scss">
+	.container{
+		width: 100%;
+		height: 100vh;
+		.content{
+			width: 100%;
+			height:calc(100vh - 176rpx - 20rpx);
+			background:linear-gradient(to top,#FFFFFF,#E8EFFF);   
+			padding:94rpx 32rpx 0;
+			box-sizing: border-box;
+			.main{
+				width: 100%;
+				height:565rpx;
+				// background: #E8EFFF;
+				view{
+					width:100%;
+					height:93rpx;
+					display: flex;
+					justify-content: space-between;
+					align-items:center;
+					margin-bottom:20rpx;
+					text{
+						font-weight:light;
+						font-size: 32rpx;
+						color: #000000;
+					}
+					input{
+						padding-left:36rpx;
+						box-sizing: border-box;
+						background: #FFFFFF;
+						border-radius:22rpx;
+						height:100%;
+						width:534rpx;
+					}
+				}
+				.tree{
+					width:100%;
+					height:93rpx;
+					background: #FFFFFF;
+					border-radius:22rpx;
+					da-tree{
+						width:543rpx;
+						height:93rpx;
+						background: #FFFFFF;
+						border-radius:22rpx;
+					}
+				}
+			}
+			.default{
+				width:100%;
+				box-sizing: border-box;
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+				height: 100rpx;
+				background: #FFFFFF;
+				border-radius:16rpx;
+				view{
+					margin-left:24rpx;
+					font-weight: 300;
+					font-size: 32rpx;
+					color: #000000;
+				}
+			}
+		   .subBtn{
+			   width: 100%;
+			   height: 98rpx;
+			   background-color:#5EA1FA;
+			   border-radius: 50rpx;
+			   font-weight: 300;
+			   font-size: 36rpx;
+			   color: #FFFFFF;
+			   text-align: center;
+			   line-height:98rpx;
+			   margin-top:520rpx;
+			   letter-spacing:2px;
+		   }
+		}
+	}
+</style>
diff --git a/pages/addressEdit/index.vue b/pages/addressEdit/index.vue
new file mode 100644
index 0000000..c060725
--- /dev/null
+++ b/pages/addressEdit/index.vue
@@ -0,0 +1,22 @@
+<template>
+	<view>
+		<navbar title ='编辑地址'></navbar>
+	</view>
+</template>
+
+<script setup>
+	import {ref,onMounted} from 'vue'
+	import { onLoad } from "@dcloudio/uni-app"
+	
+	const title = ref()
+	const id = ref()
+	onLoad((option)=>{
+		title.value = option.title
+		id.value = option.id
+		console.log('123',title.value,id.value)
+	})
+</script>
+
+<style>
+	       
+</style>
diff --git a/pages/addressLocate/index.vue b/pages/addressLocate/index.vue
new file mode 100644
index 0000000..ddadfea
--- /dev/null
+++ b/pages/addressLocate/index.vue
@@ -0,0 +1,13 @@
+<template>
+	<view>
+		
+	</view>
+</template>
+
+<script setup>
+	
+</script>
+
+<style>
+	       
+</style>
diff --git a/static/images/address/delete.png b/static/images/address/delete.png
new file mode 100644
index 0000000..9e23a21
--- /dev/null
+++ b/static/images/address/delete.png
Binary files differ
diff --git a/static/images/address/edit.png b/static/images/address/edit.png
new file mode 100644
index 0000000..a137e40
--- /dev/null
+++ b/static/images/address/edit.png
Binary files differ
diff --git a/uni.scss b/uni.scss
index b9249e9..d129e4f 100644
--- a/uni.scss
+++ b/uni.scss
@@ -74,3 +74,4 @@
 $uni-font-size-subtitle:26px;
 $uni-color-paragraph: #3F536E; // 文章段落颜色
 $uni-font-size-paragraph:15px;
+
diff --git a/uni_modules/uview-ui/LICENSE b/uni_modules/uview-ui/LICENSE
new file mode 100644
index 0000000..4db40ef
--- /dev/null
+++ b/uni_modules/uview-ui/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 www.uviewui.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

--
Gitblit v1.9.3