Vue3.2: 仿飞书App选址组件封装(TDesign)

3,703 阅读5分钟

一、简介

有一件事不得不承认,飞书在应用桌面端组件开发方面,还是做得比较优秀的,特别是在用户体验方面适用不同人群。如果做得很一般,那么也就不会有这么多产品设计者,争先相仿飞书的产品了,哈哈哈😄

那么有JYM好奇,会问,飞书的地址选择器又是咋样的呢?继续往下看:

二、飞书APP地址选址组件

飞书的地址选择组件,包括了三部分:(国家、省市区、详细地址) 而我们产品的设计,则是将国家省市区嵌套在一起,进行组件联动,这个待会后面再说。

我们先继续看飞书APP,

国家的弹层选择是怎样子的:

其中,分为推荐(即热门)字母分类搜索 两大部分,对全球国家进行分组和定位。当选择国家后,则会依据选择的国家重新请求省市区中的数据。大致就这么一个逻辑

省市区的弹层是怎样子

先选省份,然后拉城市的数据,选完城市,再拉区县的数据,如果所得到的下级数据数组为空则不展示该分层及后面的后面的分层。当然,这里还提供了暂不选择的方式,就是不一定需要选择到最后一层才能结束地址选择并关闭弹层。也可以直接点击 暂不选择按钮 关闭弹层,并以上一层的选择目标为结果集,回显到输入框中。

在这里,之所以先通过体验飞书选址组件的,也是为了后面的逻辑分析和模仿。

三、仿飞书选址组件

设计思路

这个下拉框可真是个“智能小助手”啊!它知道现在大家都爱“热门”的东西,所以一上来就给咱们推荐了中国、中国澳门、中国香港、中国台湾、越南、新加坡、美国这些“网红”地点。当然啦,如果你觉得自己比它更懂潮流,也可以把它当成一个“搜索引擎”,直接输入你想找的国家或地区名字,它就能立马帮你找出来,简直比百度还厉害!

不过啊,这个“智能小助手”也有个“小脾气”,就是你得按照它的规矩来。它喜欢“一步一步来”,你得先选了上一层的,它才会告诉你下一层有什么。这可不能怪它,毕竟它得确保你找到自己真正想去的地方嘛!

最有趣的是,这个下拉框还有个“暂不选择”的按钮,就像是它给你的一个“后悔药”。但在这个“组织地址”的场景下,它可不会随便给你这个“后悔药”,毕竟这是件严肃的事情,不能让你随便反悔。

总的来说,这个下拉框就像是个既聪明又有点“小傲娇”的助手,虽然有时候得按照它的规矩来,但也能给你带来不少乐趣和便利

接口定义

从设计图中的信息,我们无法需要这些信息:字段名称、唯一值代号、以及所依附的父级代号,形成一个完整的国家省市区的关系链树状结构。但是,因考虑到性能的问题,我们不可能要求后端接口一次性返回所有国家省市区所组合而成的的庞大数状结构数据,那么我们或许可以将其拆分成一个单元结构即树叶,再通过按需,将整个单元结构串联起来形成躯干。

如此做法,一来可以按照用户所需加载数据,二来提高性能和响应速度。也方便后续地址数据的维护。

所以定义了如下单元结构:

export interface Region {
    /**
     * 区域简称
     */
    area?: string;
    /**
     * 区域代码,身份证头两位
     */
    code?: string;
    /**
     * 导入时间
     */
    create_at?: number;
    /**
     * 名称
     */
    name?: string;
    /**
     * 所属区域代码
     */
    parent: string;
}

然后通过行政区域代号去请求,得到区域列表

请求参数:body

export interface Request {
    /**
     * 所属区域代码,不知道就传空
     */
    code: string;
}

请求结果:response

export interface Response {
    /**
     * 行政区域列表
     */
    regions: Region[];
}

示例结果:

开发组件(基于TDesign框架)

我们先对需求进行分析,理清开发思路即可

1.设置四个分类常量

const topBar: Array<TabItem> = [
  {
    value: "country",
    label: "国家",
  },
  {
    value: "province",
    label: "省份/地区",
  },
  {
    value: "city",
    label: "城市",
  },
  {
    value: "area",
    label: "区/县",
  },
];

2.入参定义

const props = defineProps({
  // data: {
  // 	type: Object,
  // 	default: () => {},
  // },
  value: {
    type: Object,
    default: () => undefined,
  },
  disabled: {
    type: Boolean,
    default: false,
  },

  placeholder: {
    type: String,
    default: "请选择地址",
  },
  isShowControl: {
    // 是否显示‘暂时不选得控件’
    type: Boolean,
    default: true,
  },
});

其中isShowControl,用于控制暂不选择的功能节点

3.数据回显

这是关键的一步,需要通过向数据库保存选择的代号,以数组(按分类的顺序)保存, 等获取详情后再通过相应的code去请求默认数据


const onSetDefaultAddress = async (cur) => {
  // address: [],  // 以这个的数量为参考
  // 默认选中的项目
  // select: [], // 这里的数量只会比address少于或等于

  if (cur) {
    const arr = [];
    cur.address.forEach((item, itemIndex) => {
      if (itemIndex === 0) {
        // countryDatas.value = item;
        arr.push(onGetRegion(undefined, "country"));
      } else if (itemIndex === 1) {
        // provinceDatas.value = item;
        arr.push(onGetRegion(cur.address[itemIndex - 1], "province"));
      } else if (itemIndex === 2) {
        // cityDatas.value = item;
        arr.push(onGetRegion(cur.address[itemIndex - 1], "city"));
      } else if (itemIndex === 3) {
        // areaDatas.value = item;
        arr.push(onGetRegion(cur.address[itemIndex - 1], "area"));
      }
    });
    await Promise.all(arr);

    cur.select.forEach((item, itemIndex) => {
      if (itemIndex === 0) {
        countryActiveItem.value = item;
      } else if (itemIndex === 1) {
        provinceActiveItem.value = item;
      } else if (itemIndex === 2) {
        cityActiveItem.value = item;
      } else if (itemIndex === 3) {
        areaActiveItem.value = item;
      }
    });
    currentTopActive.value = "country"; // 默认跳回国家
    autoAddressText(); // 显示文本信息
  } else {
    initData();
    onGetRegion(undefined, "country");
  }
};

代码中的onGetRegion就是接口定义中的接口调用方法

autoAddressText用于自动拼凑回显

回传结构如下:

{
    "select": [
        {
            "area": "CN",
            "code": "822",
            "name": "中国澳门",
            "create_at": 16853367473
        },
        {
            "area": "CN",
            "code": "82031",
            "name": "澳门xx",
            "parent": "82",
            "create_at": 1684923594
        },
        {
            "area": "CN",
            "code": "8240102",
            "name": "xx区",
            "parent": "8201",
     
        }
    ],
    "address": [
        "82",
        "8201",
        "820102"
    ]
}

大致的思路就如上面所述,供大家参考!也对自己开发过程的一次总结