0
点赞
收藏
分享

微信扫一扫

官网前端项目总结

三分梦_0bc3 2022-03-17 阅读 61

官网前端学习总结

结构目录

在这里插入图片描述

其中
dist: 项目打包后的产物
mock: 模拟数据
node_modules: 项目依赖
src: 源代码都在这里
assets: 静态资源
components: 公用组件
layouts: 通用布局
pages: 页面都放在这里
utils: 工具
global.tsx/global.less: 约定全局js和全局样式

项目配置

路由部分

在这里插入图片描述
在这里插入图片描述

路由是用全局layout,所以通过props.children渲染子组件
要看一个东西是组件还是页面,就看他的路由,路由配置了,那他就是页面,如下图,就是一个页面

在这里插入图片描述

项目改为多页面形式

  ssr: {} // 预渲染, 解决没有服务端情况下,页面的 SEO 和首屏渲染提速。
  exportStatic: { dynamicRoot: true, htmlSuffix: true },  // 配置 html 的输出形式

技术方案

大体是react+umi, 移动端方案是flexible.js+媒体查询

样式文件书写顺序

位置属性:position display float left top right bottom overflow clear z-index

尺寸(自身)属性:width height padding border margin

文字属性:font-family font-size font-style font-weight font-varient color text-align vertical-align
word-spacing white-space text-overflow

背景:background、border等

css3中新增属性:content box-shadow border-radius transform等

布局

第一个是layouts下的Header部分了

  <div className={styles.wrapper}> 
    <div className={styles.container}>
  </div>
  </div> 
.wrapper {
  position: fixed;
  top: 0;
  z-index: 999;
  width: 100%;
  line-height: 100px;
  background-color: #ffffff;
} 
.container {
  /* flex布局,让里面的模块自动分配空间 */
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 1220px; 
  margin: 0 auto; 
  /* 这个是导航区了,给一个定宽 margin: 0 auto 就可以居中了 */
}

wrapper是最外层,给一个通栏宽度就行了,用到了粘性定位,因为他要一直在顶部,不能划进去。

导航选中样式实现思路是:

  1. 获取地址栏地址
  2. 判断地址栏地址是否包含当前路由地址,若包含则给当前导航加上样式,这样即使页面刷新,当前样式也不会丢失
    代码如下
  const [selected, setSelected] = useState('');
  const handleUrl = (params = '/solution/') => { //处理地址栏是否包含路由地址
    return selected.includes(params);
  };
  useEffect(() => {
    const urlParams = new URL(window.location.href); // 获取地址栏地址
    const pathName = urlParams?.pathname;
    setSelected(pathName);
  }, []);

  <a
    href={item.href}
    className={handleUrl(item.href) ? styles.active : ''} // 若包含则给当前导航加上样式
    {...item?.otherProps}
  >{item.name}</a>

第二个是Footer

在这里插入图片描述

主要就是footerContent设置居中,再给个padding把内容挤下去。

图片如果需要独立设置宽高,要加上display: block;变成块级元素, 不然会有问题

banner区域

如果你想放一张图片做背景,然后还需要在上面放文字之类的,如果你设置backgroundImage不给高度是没办法撑开div的,
但是给了高度页面宽度变化他高度不会变,所以我们可以用下面的方法

在这里插入图片描述

我们在banner里面放了一个img,宽度100%,高度自适应,这样图片就不会失真了,然后再放一个bannerContainer,定位在banner上层,就像加了一张遮罩层,然后把内容放在bannerContainer里就行了,样式代码如下

.banner {
  position: relative;
  width: 100%;
  overflow: hidden;
  .bannerContainer {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 10;
    width: 100%;
    height: 100%;
  }
}

如果你发现缩小屏幕后,图片宽度不一样,就像下面这样

在这里插入图片描述

这可能是因为和下面的图片不一样宽,下面比较宽,但是他已经最大了,所以就不会撑开了
这种情况我在global.less里加了下面,就可以解决了,给他一个最小宽度

  body {
    min-width: 1220px;
  }

很多地方宽高不需要定死,这个需要自己看情况

抽离为公共组件

把多个具有相同样式或类似得抽离为公共组件,传入参数即可使用,比如下面,虽然内容一个靠左一个靠右,但是我们只需要传入一个type就可以

在这里插入图片描述

  {list.map((item: any) => {
        return item?.type === 'left' ? (
          <div className={styles.content} key={item.id}>
          </div>
        ) : (
          <div className={styles.content} key={item.id}>
          </div>
        );
      })}

我们组件公用组件在接受参数时,也可以设置默认值,如下,如果bgImg并没有传,那我们就使用默认值

   const {
    title, // banner标题
    subTitle = [], // banner副标题
    des = '', // 描述
    titleColor = '#000', // 字体颜色
    bgImg = '/images/software/banner.png',
    // 背景图片
  } = props;

硬件产品模块

滑动选择栏

在这里插入图片描述

实现这个是用了swiper插件,

这是要展示的组件,下面的产品点击跳转到该产品的详情页面, 同时选择栏也跳转到相应的slider,并且该slider居中

// 这是一个产品详情
const classifyRoom = {
  type: 'large',
  name: '智能垃圾分类箱房',
  imgUrl: '/images/hardware/AIroomDetail.png'}
___________________________________________________________________
const arr: any[] = [  // 这是没更新前的产品详情数组,里面不包含产品详情的组件,我们在下面更新进去
  {
    type: 'all',
    title: '全部产品',
    url: '/hardware/hardwareProduct',
  },
  {
    title: '智能垃圾分类箱房',
    detail: classifyRoom,  // 这里映射上面的对象
    url: '/hardware/classifyRoom',
  },
];

  // 处理点击时跳转
  const handleClick = (id: number) => {
    console.log('________', id);
    swiperRef && swiperRef.slideTo(id, 1);
  };

  // 更新数组
  const updateArr = arr.map((item: any) => {
    return item?.type === 'all'  // 如果类型为全部产品,则更新全部产品组件进去
      ? {
          ...item,
          component: (
            <HardwareProduct
              onSelect={(item: any, id: any) => {
                select(item);
                handleClick(id); // 把方法传进全部产品组件, 点击了哪个就跳转到哪个
              }}
            />
          ),
        }
      : {
          ...item, // <ProductDetail /> 为产品详情公用组件
          component: <ProductDetail product={item.detail} />, // 把产品详情更新进去
        };
  });
    <Swiper
        spaceBetween={40} // 两个slider之间的距离
        slidesPerView="auto" // slider自适应可以看见几个
        className={styles.swiper}
        slideToClickedSlide  // 滑动到点击到的slider
        centeredSlides  // 当前选择的slider居中
        centeredSlidesBounds // 在选中的slider居中时,第一个和最后一个slider贴合边缘
        onSwiper={(swiper) => {
          swiperRef = swiper; //获取swiper实例
        }}
      >
      // 这里控制显示的组件
      {updateArr.map((item: any) => {
        return item.url === selected ? item.component : null;
      })}

tabBar切换组件

思路都差不多,点击了把他的id存进去,如果他的id等于状态的id,则切换并加上样式

   {tabSet.map((item: any) => {
          return item.type === 'all' ? ( // 这里不能直接等于切换按钮的名字,应该等于id或者类型
            <div>
              {item.title}
            </div>
          ) : (
            <h2>
              {item.title}
            </h2>
          );
        })}
      </div>
      {tabSet.map((item: any) =>
        item.id === selected ? item.component : null,
      )}

申请试用

在这里插入图片描述

选择产品类型,这里用的字典接口实现

  const getDictData = async () => {
    const res = await getDict(); // 获取数据
    const data = res?.data || [];
    data.map((item: any, index: number) => {   // 给每一项添加一个键,用于判断产品类型是否被选中
      item.details.map((childItem: any, childIndex: number) => {
        data[index]['details'][childIndex].checked = false;
        setProductList(data);
      });
    });
  };

  // 处理产品类型选中与取消
   const handleCheckbox = async (itemIndex: number, childItemIndex: number) => {
     // 拿到当前的checked
    const checked = productList[itemIndex]['details'][childItemIndex]?.checked;
    // 取反
    productList[itemIndex]['details'][childItemIndex].checked = !checked;
    // 更新
    setProductList(productList);
  };

  <div className={styles.interestTitle}>请选择您感兴趣的产品类型</div>
        {productList.map((item: any, itemIndex: number) => {
          return (
                  (childItem: any, childItemIndex: number) => (
                    <div
                      key={childItem.dictId}
                      // 为true添加样式,反之不加
                      className={childItem.checked ? styles.productChecked : ''}
                      onClick={() => {
                        handleCheckbox(itemIndex, childItemIndex);
                        // 点击时把productList的index和details的index传进去
                      }}
                    >
                      {childItem.dictValue}
                    </div>
                  ),
                )}
          );
        })}

正则表达式判断手机号码或12位座机号

const reg = /^(1[3|4|5|6|7|8|9])\d{9}$|^400-[016789]\d{2}-\d{4}$/;
reg.test(value)
      ? setValues({ ...values, [e.target.name]: value }) // 正则通过则存起来
      : setValues({ ...values, [e.target.name]: '' }) // 不通过则置该值为空

提交

  const handleSubmit = async (e: any) => {
    let applyProTypeList: any = []; // 先声明一个数组
    productList.map((item: any) => {
      item.details.map((listItem: any) => { // 先把产品类型选中的存起来
        listItem.checked ? applyProTypeList.push(listItem.dictKey) : '';
      });
    });
    if (applyProTypeList.length === 0) {
      message.error('请选择您感兴趣的产品类型');
      return;
    }
    if (values.applyUserPhone === '') {
      message.error('请输入正确的手机号码');
      return;
    }
    submit({
      ...values,
      applyProType: applyProTypeList?.join(','), // 把刚存起来的选中的分割存进要传递的参数中
    });
  };
举报

相关推荐

0 条评论