Parcourir la source

初版开发完成

李富豪 il y a 9 mois
Parent
commit
4c0088fb5d

+ 1 - 0
src/App.vue

@@ -17,6 +17,7 @@ onPageNotFound(() => {
 
 <style lang="scss">
 page {
+    background: $background-color;
     font-family: PingFang SC;
     font-size: $font-size-base;
     color: $text-color;

+ 7 - 1
src/pages.json

@@ -18,6 +18,12 @@
 				"navigationBarTitleText": "设备设施"
 			}
 		},
+		{
+			"path": "pages/facility/houseDetail/index",
+			"style": {
+				"navigationBarTitleText": "房型详情"
+			}
+		},
 		{
 			"path": "pages/mine/index/index",
 			"style": {
@@ -29,7 +35,7 @@
 		"navigationBarBackgroundColor": "#651FFF",
 		"navigationBarTitleText": "适老设施",
 		"navigationBarTextStyle": "white",
-		"backgroundColor": "#F0F2F5"
+		"backgroundColor": "#FBFBFB"
 	},
 	"tabBar": {
 		"color": "#333333",

+ 158 - 0
src/pages/facility/houseDetail/index.vue

@@ -0,0 +1,158 @@
+<template>
+  <view class="facility-houseDetail">
+    <wd-swiper :autoplay="false" :list="state.photoList" :indicator="{ type: 'fraction' }"
+      indicatorPosition="bottom-right" v-model:current="state.swiperCurrentIndex" @click="onClickSwiper" />
+    <view class="facility-houseDetail-content">
+      <view class="facility-houseDetail-content-title">
+        三亚太保养老族库
+      </view>
+      <view class="facility-houseDetail-content-chunk">
+        装修实例 丨 90㎡ 丨 两居
+      </view>
+      <view v-for="(item, index) in state.areaList" :key="index">
+        <view class="facility-houseDetail-content-title">
+          {{ item.title }}
+        </view>
+        <view style="margin-bottom: 10rpx;">
+          <wd-img width="100%" mode="widthFix" :src="item.url" @click="onClickLookImage(item.url)" />
+        </view>
+        <view class="facility-houseDetail-content-text">
+          {{ item.content }}
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script lang="ts" setup>
+import { reactive, computed } from 'vue';
+import { onLoad } from '@dcloudio/uni-app';
+
+interface State {
+  swiperCurrentIndex: number,
+  photoList: string[],
+  areaList: {
+    url: string,
+    title: string,
+    content: string,
+  }[];
+};
+
+const state: State = reactive({
+  swiperCurrentIndex: 0,
+  photoList: [],
+  areaList: [],
+});
+
+const coverUrl = computed(() => {
+  return state.photoList[0];
+})
+
+const api = {
+  // 获取列表
+  fetchList: async () => {
+    uni.showLoading({
+      title: '加载中',
+    });
+    try {
+      const photoList = [
+        'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+        'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+      ];
+      state.photoList = photoList;
+      const list = [
+        {
+          url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+          title: '户型图',
+          content: '99㎡两室一厅的户型,整屋以白色和原木色为主基调,用鲜艳的颜色点缀空间,清新淡雅,不杂乱,简单纯粹。',
+        },
+        {
+          url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+          title: '客厅',
+          content: '沙发墙特意选择了跟电视墙同色的卡其色护墙板铺贴,看上去就不会觉得单调。背景墙两侧都有卧室门,特意选择跟背景墙相近色的木门,达到隐形门的效果。 整屋以实用精致的搭配为主,吊顶也没有过多设计,在平面吊顶的基础上,安装吸顶灯和灯带做主要照明,不压层高。',
+        },
+        {
+          url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+          title: '卧室',
+          content: '主卧把飘窗拆除后也做了落地窗封窗,充足的采光融入,让空间看上去更显宽敞大气。原飘窗的位置也没有浪费,而是安装了梳妆台,有效利用空间。 卧室整体墙面留白,也没有做过多的处理,避免因为太复杂的设计让空间显得更局促。将床靠墙摆放,尽可能节省空间,留出充足的活动空间来。',
+        },
+        {
+          url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+          title: '厨房',
+          content: '厨房的面积较小,将生活阳台打通后做了双一字型的布局,将空间都有效利用起来。拥有充足的收纳空间,满足一家人对收纳的高需求。 空间虽然小,但是操作区布局也都做到了合理规划。而且还做了高低台的设计,符合人体工学,做饭洗菜时不易累腰。',
+        },
+        {
+          url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+          title: '卫生间',
+          content: '卫生间的介绍',
+        },
+      ];
+      state.areaList = list;
+    } catch (error: any) {
+      console.error(error);
+    } finally {
+      uni.hideLoading();
+    }
+  },
+}
+
+const init = async () => {
+  uni.showLoading({
+    title: '加载中',
+  });
+  // 获取列表
+  await api.fetchList();
+  uni.hideLoading();
+};
+
+onLoad(() => {
+  init();
+});
+
+// 点击轮播
+const onClickSwiper = () => {
+  uni.previewImage({
+    urls: state.photoList,
+  });
+}
+
+// 点击查看图片
+const onClickLookImage = (url: string) => {
+  if (!url) {
+    return;
+  }
+  uni.previewImage({
+    urls: [url],
+  });
+}
+</script>
+
+<style lang="scss" scoped>
+.facility-houseDetail {
+  :deep(.wd-swiper__track) {
+    border-radius: 0;
+  }
+
+  &-content {
+    padding: 0 20rpx;
+    background: #FFFFFF;
+
+    &-title {
+      font-weight: bold;
+      margin: 20rpx 0;
+    }
+
+    &-chunk {
+      width: 100%;
+      padding: 20rpx;
+      background: #F5F5F5;
+      border-radius: $border-radius-base;
+      color: #717376;
+    }
+
+    &-text {
+      color: #101010;
+    }
+  }
+}
+</style>

+ 272 - 7
src/pages/facility/index/index.vue

@@ -1,28 +1,293 @@
 <template>
-  <view class="category">
-    分类
+  <view class="facility">
+    <wd-sticky :z-index="10">
+      <view class="facility-top">
+        <wd-search cancel-txt="搜索" />
+        <wd-tabs slidable="always" v-model="state.tab">
+          <wd-tab v-for="(item, index) in tabList " :key="index" :title="item.name" />
+        </wd-tabs>
+        <wd-drop-menu>
+          <wd-drop-menu-item v-model="state.dropMenu" :options="state.dropMenuList" />
+          <wd-drop-menu-item title="筛选">
+            <view>
+              <wd-cell title="标题" value="筛选内容1" />
+              <wd-cell title="标题" value="筛选内容2" />
+            </view>
+          </wd-drop-menu-item>
+        </wd-drop-menu>
+      </view>
+    </wd-sticky>
+    <view class="facility-list">
+      <view class="facility-list-item" v-for="(item, index) in state.list" :key="index"
+        @click="onClickNavigate(item.id)">
+        <view class="facility-list-item-sign">
+          {{ item.sign }}
+        </view>
+        <wd-img height="300rpx" mode="heightFix" :src="item.url" />
+        <view style="padding: 0 10rpx;">
+          <view class="facility-list-item-title">
+            {{ item.title }}
+          </view>
+          <view class="facility-list-item-text">
+            {{ item.content }}
+          </view>
+        </view>
+      </view>
+    </view>
   </view>
 </template>
 
 <script lang="ts" setup>
 import { reactive } from 'vue';
-import { onShow } from '@dcloudio/uni-app';
+import { onShow, onReachBottom } from '@dcloudio/uni-app';
 
 interface State {
-
+  tab: number,
+  dropMenu: string,
+  dropMenuList: {
+    label: string,
+    value: string,
+  }[],
+  list: {
+    id: string,
+    url: string,
+    sign: string,
+    title: string,
+    content: string,
+  }[],
+  page: {
+    pageNumber: number,
+    pageSize: number,
+    total: number,
+  },
 };
 
 const state: State = reactive({
-
+  tab: 0,
+  dropMenu: '1',
+  dropMenuList: [
+    {
+      label: '效果图',
+      value: '1',
+    },
+    {
+      label: '物料实物',
+      value: '2',
+    }
+  ],
+  list: [],
+  page: {
+    pageNumber: 1,
+    pageSize: 10,
+    total: 0,
+  },
 });
 
+const tabList = [
+  {
+    name: '户型',
+  },
+  {
+    name: '全屋',
+  },
+  {
+    name: '客厅',
+  },
+  {
+    name: '卧室',
+  },
+  {
+    name: '卫生间',
+  },
+  {
+    name: '厨房',
+  },
+  {
+    name: '餐厅',
+  },
+  {
+    name: '阳台',
+  }
+];
+
+const api = {
+  // 获取列表
+  fetchList: async (concat?: boolean) => {
+    uni.showLoading({
+      title: '加载中',
+    });
+    try {
+      const data = {
+        page_number: state.page.pageNumber,
+        page_size: state.page.pageSize,
+      };
+      const res = {
+        data: {
+          list: [
+            {
+              id: '1',
+              url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+              sign: 'ST-02 | 深色大理石深色大理石',
+              title: '门槛石/D户型客厅地面收 边、卫生间地面边、卫生间地面边、卫生间地面边、卫生间地面边、卫生间地面',
+              content: '参数要求:时静摩擦系数Aw Ad级 (COF≥0.7, BPN≥80)纹理清 晰,色差小,无裂痕,需做结晶防护处理,放射性指标的测试方法应符合现行国家标准 GB6566',
+            },
+            {
+              id: '2',
+              url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+              sign: 'ST-02 | 深色大理石',
+              title: '门槛石/D户型客厅地面收 边、卫生间地面',
+              content: '参数要求:时静摩擦系数Aw Ad级 (COF≥0.7, BPN≥80)纹理清 晰,色差小,无裂痕,需做结晶防护处理,放射性指标的测试方法应符合现行国家标准 GB6566',
+            },
+            {
+              id: '3',
+              url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+              sign: 'ST-02 | 深色大理石',
+              title: '门槛石/D户型客厅地面收 边、卫生间地面',
+              content: '参数要求:时静摩擦系数Aw Ad级 (COF≥0.7, BPN≥80)纹理清 晰,色差小,无裂痕,需做结晶防护处理,放射性指标的测试方法应符合现行国家标准 GB6566',
+            },
+            {
+              id: '4',
+              url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+              sign: 'ST-02 | 深色大理石',
+              title: '门槛石/D户型客厅地面收 边、卫生间地面',
+              content: '参数要求:时静摩擦系数Aw Ad级 (COF≥0.7, BPN≥80)纹理清 晰,色差小,无裂痕,需做结晶防护处理,放射性指标的测试方法应符合现行国家标准 GB6566',
+            },
+            {
+              id: '5',
+              url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+              sign: 'ST-02 | 深色大理石',
+              title: '门槛石/D户型客厅地面收 边、卫生间地面',
+              content: '参数要求:时静摩擦系数Aw Ad级 (COF≥0.7, BPN≥80)纹理清 晰,色差小,无裂痕,需做结晶防护处理,放射性指标的测试方法应符合现行国家标准 GB6566',
+            },
+            {
+              id: '6',
+              url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+              sign: 'ST-02 | 深色大理石',
+              title: '门槛石/D户型客厅地面收 边、卫生间地面',
+              content: '参数要求:时静摩擦系数Aw Ad级 (COF≥0.7, BPN≥80)纹理清 晰,色差小,无裂痕,需做结晶防护处理,放射性指标的测试方法应符合现行国家标准 GB6566',
+            }
+          ],
+          total: 10,
+        }
+      };
+      const list = res.data.list;
+      state.list = concat ? state.list.concat(list) : list;
+      state.page = {
+        ...state.page,
+        total: res.data.total,
+      };
+    } catch (error: any) {
+      console.error(error);
+    } finally {
+      uni.hideLoading();
+    }
+  },
+}
+
+const init = async () => {
+  uni.showLoading({
+    title: '加载中',
+  });
+  // 获取列表
+  await api.fetchList();
+  uni.hideLoading();
+};
+
+const onChangePagination = async () => {
+  const { pageNumber, pageSize, total } = state.page;
+  if (total > pageNumber * pageSize) {
+    state.page = {
+      ...state.page,
+      pageNumber: pageNumber + 1,
+    }
+    // 获取列表
+    await api.fetchList(true);
+  }
+}
+
 onShow(() => {
+  state.page = {
+    ...state.page,
+    pageNumber: 1,
+  };
+  init();
+});
 
+onReachBottom(() => {
+  onChangePagination();
 });
+
+// 点击跳转
+const onClickNavigate = (id: string) => {
+  uni.navigateTo({
+    url: `/pages/facility/houseDetail/index?id=${id}`,
+  });
+}
 </script>
 
 <style lang="scss" scoped>
-.category {
-  padding: 0 20rpx;
+.facility {
+  &-top {
+    width: 100vw;
+    border-bottom: 2rpx solid $border-color;
+  }
+
+  &-list {
+    padding: 0 20rpx;
+    display: flex;
+    flex-wrap: wrap;
+    margin-top: 20rpx;
+
+    &-item {
+      width: calc(50% - 10rpx);
+      background: #F4F4F4;
+      border-radius: $border-radius-base;
+      margin-bottom: 20rpx;
+      position: relative;
+      overflow: hidden;
+
+      &-sign {
+        width: 85%;
+        height: 50rpx;
+        padding: 0 10rpx;
+        background: rgba(0, 0, 0, 0.4);
+        border-top-right-radius: $border-radius-base;
+        color: #FFFFFF;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        position: absolute;
+        top: 250rpx;
+        left: 0;
+        z-index: 2;
+      }
+
+      &-title {
+        font-weight: bold;
+        display: -webkit-box;
+        -webkit-box-orient: vertical;
+        -webkit-line-clamp: 2;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        margin-bottom: 10rpx;
+      }
+
+      &-text {
+        width: 100%;
+        height: 300rpx;
+        color: $gray-color;
+        overflow: hidden;
+        margin-bottom: 10rpx;
+      }
+    }
+
+    &-item:nth-child(odd) {
+      margin-right: 10rpx;
+    }
+
+    &-item:nth-child(even) {
+      margin-left: 10rpx;
+    }
+  }
 }
 </style>

+ 228 - 3
src/pages/home/index/index.vue

@@ -1,6 +1,49 @@
 <template>
   <view class="home">
-    首页
+    <view>
+      <wd-drop-menu>
+        <wd-drop-menu-item :options="state.addressList" v-model="state.address" />
+      </wd-drop-menu>
+    </view>
+    <view>
+      <wd-swiper :autoplay="true" :list="state.swiperList" @click="onClickSwiper" />
+    </view>
+    <view class="home-menu">
+      <view class="home-menu-item" v-for="(item, index) in menuList" :key="index">
+        <wd-img width="60rpx" height="60rpx" :src="item.icon" />
+        <view class="home-menu-item-right">
+          <view style="font-weight: 400;">
+            {{ item.title }}
+          </view>
+          <view style="font-size: 24rpx;margin-top: 10rpx;">
+            {{ item.text }}
+          </view>
+        </view>
+      </view>
+    </view>
+    <view class="home-house">
+      <view class="home-house-title">
+        <view>
+          最新发布
+        </view>
+        <wd-icon name="arrow-right" />
+      </view>
+      <view class="home-house-item" v-for="(item, index) in state.houseList" :key="index"
+        @click="onClickNavigate(item.id)">
+        <wd-img width="220rpx" height="180rpx" radius="8rpx" mode="aspectFill" :src="item.url" />
+        <view class="home-house-item-right">
+          <view class="home-house-item-right-title">
+            {{ item.title }}
+          </view>
+          <view class="home-house-item-right-text">
+            {{ item.content }}
+          </view>
+          <view class="home-house-item-right-date">
+            {{ item.date }}
+          </view>
+        </view>
+      </view>
+    </view>
   </view>
 </template>
 
@@ -9,20 +52,202 @@ import { reactive } from 'vue';
 import { onShow } from '@dcloudio/uni-app';
 
 interface State {
-
+  address: string,
+  addressList: {
+    label: string,
+    value: string,
+  }[],
+  swiperList: string[],
+  houseList: {
+    id: string,
+    url: string,
+    title: string,
+    content: string,
+    date: string,
+  }[],
 };
 
 const state: State = reactive({
-
+  address: '1',
+  addressList: [
+    {
+      label: '太保家园·郑州国际颐养社区',
+      value: '1',
+    },
+    {
+      label: '太保家园·郑州国际颐养社区2',
+      value: '2',
+    },
+    {
+      label: '太保家园·郑州国际颐养社区3',
+      value: '3',
+    }
+  ],
+  swiperList: [
+    'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+    'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+  ],
+  houseList: [
+    {
+      id: '1',
+      url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+      title: '户型1户型1户型1户型1户型1户型1户型1户型1户型1户型1户型1',
+      content: '老年人照料设施建筑基地应选择在工程地质条件稳定老年人照料设施建筑基地应选择在工程地质条件稳定老年人照料设施建筑基地应选择在工程地质条件稳定',
+      date: '2025-02-15',
+    },
+    {
+      id: '2',
+      url: 'https://registry.npmmirror.com/wot-design-uni-assets/*/files/moon.jpg',
+      title: '户型2',
+      content: '老年人照料设施建筑基地应选择在工程地质条件稳定',
+      date: '2025-02-16',
+    }
+  ],
 });
 
+const menuList = [
+  {
+    icon: '/static/home/icon_towel.svg',
+    title: '生活用房',
+    text: '能力完好老年人',
+  },
+  {
+    icon: '/static/home/icon_badminton.svg',
+    title: '文娱与健身用房',
+    text: '',
+  },
+  {
+    icon: '/static/home/icon_file.svg',
+    title: '康复与医疗用房',
+    text: '',
+  },
+  {
+    icon: '/static/home/icon_medical.svg',
+    title: '服务管理用房',
+    text: '',
+  },
+  {
+    icon: '/static/home/icon_sign.svg',
+    title: '交通空间',
+    text: '',
+  },
+  {
+    icon: '/static/home/icon_city.svg',
+    title: '建筑细节',
+    text: '',
+  }
+];
+
 onShow(() => {
 
 });
+
+// 点击轮播
+const onClickSwiper = () => {
+  uni.navigateTo({
+    url: `/pages/facility/houseDetail/index`,
+  });
+}
+
+// 点击跳转
+const onClickNavigate = (id: string) => {
+  uni.navigateTo({
+    url: `/pages/facility/houseDetail/index?id=${id}`,
+  });
+}
 </script>
 
 <style lang="scss" scoped>
 .home {
   padding: 0 20rpx;
+
+  &-menu {
+    display: flex;
+    flex-wrap: wrap;
+    margin-top: 20rpx;
+
+    &-item {
+      width: calc(50% - 10rpx);
+      padding: 20rpx;
+      border-radius: $border-radius-base;
+      display: flex;
+      align-items: center;
+      margin-bottom: 20rpx;
+
+      &-right {
+        margin-left: 20rpx;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+      }
+    }
+
+    &-item:nth-child(odd) {
+      background: #F1EFF8;
+      margin-right: 10rpx;
+    }
+
+    &-item:nth-child(even) {
+      background: #FAF3F0;
+      margin-left: 10rpx;
+    }
+  }
+
+  &-house {
+    padding: 20rpx;
+    background: #FFFFFF;
+    border: 2rpx solid $border-color;
+    border-radius: $border-radius-base;
+    margin-bottom: 20rpx;
+
+    &-title {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      font-weight: bold;
+      margin-bottom: 20rpx;
+    }
+
+    &-item {
+      display: flex;
+      padding-bottom: 20rpx;
+      border-bottom: 2rpx solid $border-color;
+      margin-bottom: 20rpx;
+
+      &-right {
+        flex: 1;
+        margin-left: 20rpx;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        overflow: hidden;
+
+        &-title {
+          font-weight: bold;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+
+        &-text {
+          display: -webkit-box;
+          -webkit-box-orient: vertical;
+          -webkit-line-clamp: 2;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+
+        &-date {
+          font-size: $font-size-mini;
+          color: $gray-color;
+          text-align: right;
+        }
+      }
+    }
+
+    &-item:last-child {
+      border: none;
+    }
+  }
 }
 </style>

+ 98 - 37
src/pages/login/index/index.vue

@@ -1,44 +1,96 @@
 <template>
   <view class="login">
     <view class="login-logo">
-      <wd-img width="180rpx" height="180rpx" :src="logoSrc" />
+      <image :src="logoSrc" />
+      <view class="login-logo-text">
+        适老设备设施
+      </view>
     </view>
-    <view class="login-info">
-      <wd-input :no-border="true" placeholder="请输入账号" v-model="state.account" />
-      <view class="login-info-line"></view>
-      <wd-input type="password" :no-border="true" placeholder="请输入密码" v-model="state.password" />
+    <view class="login-input">
+      <wd-input :noBorder="true" placeholder="请输入账号" v-model="state.account" />
+    </view>
+    <view class="login-input">
+      <wd-input :showPassword="true" :noBorder="true" placeholder="请输入密码" v-model="state.password" />
     </view>
     <view class="login-operation">
       <wd-checkbox shape="square" v-model="state.rememberChecked">
         记住密码
       </wd-checkbox>
     </view>
-    <wd-button :block="true" :disabled="!state.account || !state.password" @click="onClickLogin">登录</wd-button>
+    <wd-button :round="false" :block="true" :disabled="!state.account || !state.password" :loading="state.buttonLoading"
+      @click="onClickLogin">
+      登录
+    </wd-button>
   </view>
 </template>
 
 <script lang="ts" setup>
-import { reactive } from 'vue';
 import { onLoad } from '@dcloudio/uni-app';
+import { reactive } from 'vue';
 import logoSrc from '@/static/public/logo@2x.png';
 import LocalStorage from '@/LocalStorage';
-import { apis } from '@/apis';
+import { regex } from '@/utils';
+import { apis, LoginApiParams } from '@/apis';
 
 interface State {
   account: string,
   password: string,
   rememberChecked: boolean,
+  buttonLoading: boolean,
 };
 
 const state: State = reactive({
   account: '',
   password: '',
   rememberChecked: false,
+  buttonLoading: false,
 });
 
-onLoad(() => {
+const api = {
+  // 登录
+  login: async (data: LoginApiParams) => {
+    state.buttonLoading = true;
+    try {
+      // const res = await apis.login(data);
+      // const token = res.data;
+      const token = 'token';
+      LocalStorage.setToken(token);
+      if (state.rememberChecked) {// 记住密码
+        LocalStorage.setAccountPassword({
+          account: data.account,
+          password: data.password,
+        });
+      } else {// 不记住密码
+        LocalStorage.setAccountPassword(undefined);
+      }
+      uni.switchTab({
+        url: '/pages/home/index/index',
+        success: () => {
+          uni.showToast({
+            icon: 'success',
+            mask: true,
+            duration: 2000,
+            title: '登录成功',
+          });
+        }
+      });
+    } catch (error: any) {
+      LocalStorage.clear();
+      uni.showToast({
+        icon: 'none',
+        mask: true,
+        duration: 2000,
+        title: error.msg,
+      });
+    } finally {
+      state.buttonLoading = false;
+    }
+  },
+}
+
+const init = () => {
   const token = LocalStorage.getToken();
-  if (token) {// 已登陆直接进入主页
+  if (token) {// 已登陆直接进入
     uni.switchTab({
       url: '/pages/home/index/index',
     });
@@ -50,6 +102,10 @@ onLoad(() => {
       state.rememberChecked = true;
     }
   }
+};
+
+onLoad(() => {
+  init();
 });
 
 // 点击登录
@@ -58,48 +114,53 @@ const onClickLogin = async () => {
     account: state.account,
     password: state.password,
   }
-  // await apis.login(data);
-  LocalStorage.setToken('token');
-  uni.switchTab({
-    url: '/pages/home/index/index',
-    success: () => {
-      uni.showToast({
-        icon: 'success',
-        mask: true,
-        duration: 2000,
-        title: '登录成功',
-      });
-    }
-  });
+  const passwordRegex = new RegExp(regex.password);
+  if (!passwordRegex.test(data.password)) {
+    return uni.showToast({
+      icon: 'none',
+      mask: true,
+      duration: 2000,
+      title: '密码格式不正确',
+    });
+  }
+  // 登录
+  await api.login(data);
 }
 </script>
 
 <style lang="scss" scoped>
 .login {
   padding: 0 20rpx;
+  background: #FFFFFF;
 
   &-logo {
     display: flex;
-    justify-content: center;
-    margin: 100rpx 0;
-  }
+    flex-direction: column;
+    align-items: center;
+    padding: 80rpx 0;
 
-  &-info {
-    padding: 20rpx;
-    background: #FFFFFF;
-    border-radius: $border-radius-base;
+    image {
+      width: 180rpx;
+      height: 180rpx
+    }
 
-    &-line {
-      width: 100%;
-      height: 1rpx;
-      background: $border-color;
-      margin: 20rpx 0;
+    &-text {
+      font-size: $font-size-large;
+      font-weight: bold;
+      color: $primary-color;
+      letter-spacing: 10rpx;
+      margin-top: 20rpx;
     }
   }
 
+  &-input {
+    padding-bottom: 30rpx;
+    border-bottom: 2rpx solid $border-color;
+    margin-bottom: 30rpx;
+  }
+
   &-operation {
-    display: flex;
-    margin: 40rpx 0;
+    margin: 60rpx 0;
   }
 }
 </style>

+ 125 - 22
src/pages/mine/index/index.vue

@@ -1,7 +1,26 @@
 <template>
-  <view class="personalCenter">
-    <view class="personalCenter-button">
-      <wd-button :block="true" @click="onClickLogout">
+  <view class="mine">
+    <view class="mine-header">
+      <view class="mine-header-info">
+        <image :src="avatarSrc" />
+        <view>
+          {{ state.info.userName }}
+        </view>
+      </view>
+    </view>
+    <view class="mine-content">
+      <view class="mine-content-cell">
+        <view class="mine-content-cell-left">
+          <image :src="phoneSrc" />
+          手机号码
+        </view>
+        <view>
+          {{ state.info.phoneNumber }}
+        </view>
+      </view>
+    </view>
+    <view class="mine-button">
+      <wd-button :round="false" :block="true" :loading="state.buttonLoading" @click="onClickLogout">
         退出登录
       </wd-button>
     </view>
@@ -9,46 +28,130 @@
 </template>
 
 <script lang="ts" setup>
-import { reactive } from 'vue';
 import { onShow } from '@dcloudio/uni-app';
+import { reactive } from 'vue';
+import avatarSrc from '@/static/public/avatar@2x.png';
+import phoneSrc from '@/static/mine/phone@2x.png';
 import LocalStorage from '@/LocalStorage';
+import { apis } from '@/apis';
 
 interface State {
-
+  info: {
+    userName: string,
+    phoneNumber: string,
+  },
+  buttonLoading: boolean,
 };
 
 const state: State = reactive({
-
+  info: {
+    userName: '测试',
+    phoneNumber: '18888888888',
+  },
+  buttonLoading: false,
 });
 
+const api = {
+  // 退出登录
+  logout: async () => {
+    state.buttonLoading = true;
+    try {
+      // await apis.logout();
+    } catch (error: any) {
+      console.error(error);
+    } finally {
+      LocalStorage.clear();
+      uni.reLaunch({
+        url: '/pages/login/index/index',
+        success: () => {
+          uni.showToast({
+            icon: 'success',
+            mask: true,
+            duration: 2000,
+            title: '退出成功',
+          });
+        }
+      });
+      state.buttonLoading = false;
+    }
+  },
+}
+
 onShow(() => {
 
 });
 
 // 点击退出登录
-const onClickLogout = () => {
-  LocalStorage.clear();
-  uni.reLaunch({
-    url: '/pages/login/index/index',
-    success: () => {
-      uni.showToast({
-        icon: 'success',
-        mask: true,
-        duration: 2000,
-        title: '退出成功',
-      });
-    }
-  });
+const onClickLogout = async () => {
+  // 退出登录
+  await api.logout();
 }
 </script>
 
 <style lang="scss" scoped>
-.personalCenter {
-  padding: 0 20rpx;
+.mine {
+  &-header {
+    height: 180rpx;
+    padding: 20rpx;
+    border-bottom: 2rpx solid $background-color;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+
+    &-info {
+      display: flex;
+      align-items: center;
+
+      image {
+        width: 100rpx;
+        height: 100rpx;
+        background: $background-color;
+        border: 2rpx solid $background-color;
+        border-radius: 50%;
+        margin-right: 20rpx;
+      }
+    }
+  }
+
+  &-content {
+    padding: 0 20rpx;
+
+    &-cell {
+      width: 100%;
+      height: 120rpx;
+      border-bottom: 2rpx solid $border-color;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+
+      &-left {
+        display: flex;
+        align-items: center;
+
+        image {
+          width: 40rpx;
+          height: 40rpx;
+          margin-right: 20rpx;
+        }
+      }
+    }
+  }
 
   &-button {
     width: 100%;
-    margin-top: 100rpx;
+    height: 150rpx;
+    padding: 0 20rpx;
+    position: fixed;
+
+    /* #ifdef H5 */
+    bottom: var(--tab-bar-height);
+    left: 0;
+    /* #endif */
+
+    /* #ifndef H5 */
+    bottom: 0;
+    left: 0;
+    /* #endif */
   }
 }
 </style>

+ 6 - 0
src/static/home/icon_badminton.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" width="27" height="28" style="" filter="none">
+    
+    <g>
+    <path d="M19.5 44C21.99 44 24 41.99 24 39.5V19H20L15 39.5C15 41.99 17.01 44 19.5 44Z" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-miterlimit="2" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M20 19H16L6.67996 37.9C5.56996 40.51 7.15996 43.64 9.97996 43.97C10.15 43.99 10.32 44 10.5 44C12.99 44 15 41.99 15 39.5" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-miterlimit="2" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M28.5 44C26.01 44 24 41.99 24 39.5V19H28L33 39.5C33 41.99 30.99 44 28.5 44Z" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-miterlimit="2" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M28 19H32L41.32 37.9C42.43 40.51 40.84 43.64 38.02 43.97C37.85 43.99 37.68 44 37.5 44C35.01 44 33 41.99 33 39.5" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-miterlimit="2" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M16.06 13C16.02 12.67 16 12.34 16 12C16 7.58 19.58 4 24 4C28.42 4 32 7.58 32 12C32 12.34 31.98 12.67 31.94 13H16.06Z" fill="none" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-miterlimit="2" stroke-linecap="round" stroke-linejoin="round"></path>
+    </g>
+  </svg>

+ 6 - 0
src/static/home/icon_city.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" width="27" height="28" style="" filter="none">
+    
+    <g>
+    <rect width="48" height="48" fill="rgba(246,97,5,1)" fill-opacity="0.01" stroke="none"></rect><path d="M4 42H44" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><rect x="8" y="22" width="12" height="20" rx="2" fill="none" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-linejoin="round"></rect><rect x="20" y="4" width="20" height="38" rx="2" fill="none" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-linejoin="round"></rect><path d="M28 32.0083H32" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M12 32.0083H16" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M28 23.0083H32" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M28 14.0083H32" stroke="rgba(246,97,5,1)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"></path>
+    </g>
+  </svg>

+ 6 - 0
src/static/home/icon_file.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" width="27" height="28" style="" filter="none">
+    
+    <g>
+    <rect width="48" height="48" fill="rgba(67,11,206,1)" fill-opacity="0.01" stroke="none"></rect><path d="M23 42H19H15H9C7.89543 42 7 41.1046 7 40V8C7 6.89543 7.89543 6 9 6H37C38.1046 6 39 6.89543 39 8V15V19.5" stroke="rgba(67,11,206,1)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M36.6364 27C39.0463 27 41 28.8804 41 31.2C41 34.2196 38.0909 36.8 36.6364 38.2C35.6667 39.1333 34.4545 40.0667 33 41C31.5455 40.0667 30.3333 39.1333 29.3636 38.2C27.9091 36.8 25 34.2196 25 31.2C25 28.8804 26.9537 27 29.3636 27C30.8814 27 32.2182 27.7459 33 28.8775C33.7818 27.7459 35.1186 27 36.6364 27Z" fill="none" stroke="rgba(67,11,206,1)" stroke-width="4" stroke-linejoin="round"></path><path d="M15 14H31" stroke="rgba(67,11,206,1)" stroke-width="4" stroke-linecap="round" fill="none"></path>
+    </g>
+  </svg>

+ 6 - 0
src/static/home/icon_medical.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" width="27" height="28" style="" filter="none">
+    
+    <g>
+    <path d="M4.215 5.959c1.568-1.569 3.735-2.539 6.129-2.539 2.167 0 4.149 0.796 5.669 2.111l-0.011-0.009c1.508-1.305 3.489-2.099 5.655-2.099 4.786 0 8.667 3.88 8.667 8.667 0 2.158-0.789 4.132-2.094 5.65l0.010-0.011-10.353 10.387c-0.483 0.483-1.149 0.781-1.886 0.781-0.666 0-1.275-0.244-1.743-0.648l0.003 0.003-0.147-0.135-10.352-10.388c-1.296-1.506-2.086-3.48-2.086-5.639 0-2.393 0.97-4.56 2.539-6.129l0-0zM6.1 7.844c-1.085 1.086-1.757 2.586-1.757 4.242 0 1.557 0.593 2.976 1.566 4.043l-0.004-0.005 0.195 0.205 9.9 9.9 7.071-7.072-4.713-4.713-1.413 1.413c-0.724 0.724-1.724 1.172-2.829 1.172-2.209 0-4-1.791-4-4 0-1.104 0.447-2.104 1.171-2.828l2.803-2.804c-1.016-0.817-2.322-1.311-3.744-1.311-1.558 0-2.977 0.594-4.044 1.567l0.005-0.004-0.205 0.195zM17.415 11.615c0.241-0.241 0.575-0.39 0.943-0.39s0.701 0.149 0.943 0.39l5.656 5.656 0.944-0.941c1.087-1.086 1.759-2.587 1.759-4.244 0-3.314-2.686-6-6-6-1.558 0-2.978 0.594-4.045 1.568l0.005-0.004-0.204 0.195-4.243 4.243c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.313 0.108 0.601 0.289 0.829l-0.002-0.003 0.104 0.116c0.241 0.241 0.575 0.391 0.943 0.391 0.313 0 0.601-0.108 0.829-0.289l-0.003 0.002 0.116-0.104 2.357-2.357z" fill="rgba(246,97,5,1)"></path>
+    </g>
+  </svg>

+ 6 - 0
src/static/home/icon_sign.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" width="27" height="28" style="" filter="none">
+    
+    <g>
+    <rect width="48" height="48" fill="rgba(67,11,206,1)" fill-opacity="0.01" stroke="none"></rect><path d="M6 10V22H38L44 16L38 10L6 10Z" fill="none" stroke="rgba(67,11,206,1)" stroke-width="4" stroke-linejoin="round"></path><path d="M23 22V44" stroke="rgba(67,11,206,1)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M23 4V10" stroke="rgba(67,11,206,1)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"></path><path d="M18 44H28" stroke="rgba(67,11,206,1)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"></path>
+    </g>
+  </svg>

+ 6 - 0
src/static/home/icon_towel.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" width="27" height="28" style="" filter="none">
+    
+    <g>
+    <path d="M36 18H4V26H36V18Z" fill="none" stroke="rgba(67,11,206,1)" stroke-width="4" stroke-miterlimit="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M36 12V32C36 34.2091 34.2091 36 32 36H12M12 36H8C5.79086 36 4 34.2091 4 32V8C4 5.79086 5.79086 4 8 4H40C42.2091 4 44 5.79086 44 8V40C44 42.21 42.21 44 40 44H16C13.79 44 12 42.21 12 40V36Z" stroke="rgba(67,11,206,1)" stroke-width="4" stroke-miterlimit="2" stroke-linecap="round" stroke-linejoin="round" fill="none"></path>
+    </g>
+  </svg>

BIN
src/static/mine/phone@2x.png


BIN
src/static/public/avatar@2x.png


+ 10 - 0
src/typings/components.d.ts

@@ -7,9 +7,19 @@ export {}
 
 declare module 'vue' {
   export interface GlobalComponents {
+    WdBadge: typeof import('wot-design-uni/components/wd-badge/wd-badge.vue')['default']
     WdButton: typeof import('wot-design-uni/components/wd-button/wd-button.vue')['default']
+    WdCell: typeof import('wot-design-uni/components/wd-cell/wd-cell.vue')['default']
     WdCheckbox: typeof import('wot-design-uni/components/wd-checkbox/wd-checkbox.vue')['default']
+    WdDropMenu: typeof import('wot-design-uni/components/wd-drop-menu/wd-drop-menu.vue')['default']
+    WdDropMenuItem: typeof import('wot-design-uni/components/wd-drop-menu-item/wd-drop-menu-item.vue')['default']
+    WdIcon: typeof import('wot-design-uni/components/wd-icon/wd-icon.vue')['default']
     WdImg: typeof import('wot-design-uni/components/wd-img/wd-img.vue')['default']
     WdInput: typeof import('wot-design-uni/components/wd-input/wd-input.vue')['default']
+    WdSearch: typeof import('wot-design-uni/components/wd-search/wd-search.vue')['default']
+    WdSticky: typeof import('wot-design-uni/components/wd-sticky/wd-sticky.vue')['default']
+    WdSwiper: typeof import('wot-design-uni/components/wd-swiper/wd-swiper.vue')['default']
+    WdTab: typeof import('wot-design-uni/components/wd-tab/wd-tab.vue')['default']
+    WdTabs: typeof import('wot-design-uni/components/wd-tabs/wd-tabs.vue')['default']
   }
 }

+ 1 - 1
src/uni.scss

@@ -5,7 +5,7 @@ $error-color: #FF4D4F;
 $gray-color: #B2B8C0;
 $text-color: #333333;
 $border-color: #F3F3F3;
-$background-color: #F0F2F5;
+$background-color: #FBFBFB;
 $font-size-mini: 26rpx;
 $font-size-small: 28rpx;
 $font-size-base: 30rpx;

+ 0 - 2
src/utils/index.ts

@@ -1,6 +1,4 @@
 // 正则表达式
 export const regex = {
-    phoneNumber: /^1[13456789]\d{9}$/,// 手机号码
     password: /^[a-zA-Z0-9]{6,16}$/,// 密码
-    email: /^([a-zA-Z0-9_\.\-])+@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,// 电子邮箱
 }