Skip to content
On this page

地址管理

账号管理-效果

image-20221130084201005

账号管理-静态结构

src\pages\my\settings.vue

vue
<template>
  <view class="viewport">
    <!-- 🔔 已登录的用户才能修改收货地址 -->
    <template v-if="profile">
      <!-- 操作列表 -->
      <view class="list">
        <navigator url="./address/index" hover-class="none" class="item arrow"
          >我的收货地址</navigator
        >
      </view>
      <view class="list">
        <navigator url="./account" hover-class="none" class="item arrow"
          >账户与安全</navigator
        >
      </view>
    </template>
    <view class="list">
      <button hover-class="none" class="item arrow" open-type="feedback">
        问题反馈
      </button>
      <button hover-class="none" class="item arrow" open-type="contact">
        联系我们
      </button>
      <navigator hover-class="none" url=" " class="item arrow"
        >关于小兔鲜儿</navigator
      >
    </view>
  </view>
</template>

<script>
// 记得把 vuex中的用户信息引过来
export default {};
</script>

<style lang="scss">
page {
  background-color: #f4f4f4;
}
button {
  text-align: left;
  border-radius: 0;
  background-color: #fff;
  &::after {
    width: auto;
    height: auto;
    left: auto;
    border: none;
  }
}
.viewport {
  padding: 20rpx;
}
.arrow {
  &::after {
    position: absolute;
    top: 50%;
    content: "\e6c2";
    color: #ccc;
    font-family: "erabbit" !important;
    font-size: 32rpx;
    transform: translateY(-50%);
  }
}
.list {
  padding: 0 20rpx;
  background-color: #fff;
  margin-bottom: 20rpx;
  border-radius: 10rpx;
  .item {
    line-height: 90rpx;
    padding-left: 10rpx;
    font-size: 30rpx;
    color: #333;
    border-top: 1rpx solid #ddd;
    position: relative;
    &:first-child {
      border: none;
    }
  }
}
.item {
  &::after {
    right: 5rpx;
  }
}
</style>

地址管理-效果

image-20221128213802429

地址管理-静态结构

src\pages\my\address\index.vue

vue
<template>
  <view class="viewport">
    <!-- 地址列表 -->
    <scroll-view scroll-y>
      <view class="address">
        <!-- 按组使用 -->
        <uni-swipe-action>
          <uni-swipe-action-item class="swipe-cell">
            <view class="item" v-for="item in 3" :key="item">
              <view class="user">
                收件人
                <text>联系电话</text>
                <text class="badge"> 默认 </text>
              </view>
              <view class="locate"> 河北省唐山市 路北区小巷子 </view>
              <!-- 🐛 添加阻止冒泡 -->
              <navigator
                :url="`./form?id=${item.id}`"
                class="edit"
                hover-class="none"
              >
                修改
              </navigator>
            </view>
            <template v-slot:right>
              <view class="swipe-cell-action">
                <button class="delete-button">删除</button>
              </view>
            </template>
          </uni-swipe-action-item>
        </uni-swipe-action>
      </view>
    </scroll-view>
    <!-- 添加按钮 -->
    <view class="add-btn">
      <navigator hover-class="none" url="./form">新建地址</navigator>
    </view>
  </view>
</template>
<script>
export default {};
</script>
<style lang="scss">
page {
  height: 100%;
  overflow: hidden;
}
.viewport {
  display: flex;
  flex-direction: column;
  height: 100%;
  background-color: #f4f4f4;
  scroll-view {
    padding-top: 20rpx;
  }
}
.address {
  padding: 0 20rpx;
  margin: 0 20rpx;
  border-radius: 10rpx;
  background-color: #fff;
  .item {
    line-height: 1;
    padding: 40rpx 10rpx 38rpx;
    border-bottom: 1rpx solid #ddd;
    position: relative;
    .user {
      font-size: 28rpx;
      margin-bottom: 20rpx;
      color: #333;
      text {
        color: #666;
      }
      .badge {
        display: inline-block;
        padding: 4rpx 10rpx 2rpx 14rpx;
        margin: 2rpx 0 0 10rpx;
        font-size: 26rpx;
        color: #27ba9b;
        border-radius: 6rpx;
        border: 1rpx solid #27ba9b;
      }
    }
    .locate {
      line-height: 1.6;
      font-size: 26rpx;
      color: #333;
    }
    .edit {
      position: absolute;
      top: 36rpx;
      right: 30rpx;
      padding: 2rpx 0 2rpx 20rpx;
      border-left: 1rpx solid #666;
      font-size: 26rpx;
      color: #666;
      line-height: 1;
    }
  }
  .swipe-cell {
    &:last-child {
      .item {
        border: none;
      }
    }
  }
}
.swipe-cell-action {
  height: 100%;
  .delete-button {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 50px;
    height: 100%;
    font-size: 28rpx;
    color: #fff;
    border-radius: 0;
    padding: 0;
    background-color: #cf4444 !important;
  }
}
.add-btn {
  height: 80rpx;
  text-align: center;
  line-height: 80rpx;
  margin: 30rpx 20rpx;
  color: #fff;
  border-radius: 80rpx;
  font-size: 30rpx;
  background-color: #27ba9b;
}
.blank {
  margin-top: 300rpx;
  text-align: center;
  font-size: 32rpx;
  color: #888;
}
</style>

地址管理-请求数据、渲染界面

步骤:

1、封装API

2、封装请求函数,调用API

3、加载后,触发请求函数

4、声明变量,保存数据

代码:

src\api\category.js

js
export const getAddressAPI = () => {
  return request({
    url: "/member/address",
  });
};

src/pages/my/address/index.vue

vue
<script>
import { getAddressAPI } from "@/api/address";
export default {
  data() {
    return {
      list: [],
    };
  },
  methods: {
    async loadData() {
      const { result } = await getAddressAPI();
      this.list = result;
    },
  },
  onLoad() {
    this.loadData();
  },
};
</script>
html
<view class="item" v-for="item in list" :key="item.id">
  <view class="user">
    收件人
    <text>联系电话</text>
    <text class="badge"> 默认 </text>
    {{ item.receiver }}
    <text>{{ item.contact }}</text>
    <text class="badge" v-if="item.isDefault"> 默认 </text>
  </view>
  <view class="locate"> 河北省唐山市 路北区小巷子 </view>
  <view class="locate">
    {{ item.fullLocation }} {{ item.address }}
  </view>    
</view>

新增地址-效果

image-20221128225528977

新增地址-静态结构

src\pages\my\address\form.vue

vue
<template>
  <view class="viewport">
    <!-- 地址信息 -->
    <view class="form">
      <view class="form-item">
        <text class="label">姓名</text>
        <input
          v-model="form.receiver"
          placeholder-style="color: #888"
          placeholder="请填写收货人姓名"
        />
      </view>
      <view class="form-item">
        <text class="label">手机号码</text>
        <input
          v-model="form.contact"
          placeholder-style="color: #888"
          placeholder="请填写收货人手机号码"
        />
      </view>
      <view class="form-item">
        <text class="label">省/市/县 (区)</text>
        <picker @change="regionChange" mode="region">
          <view v-if="form.fullLocation" class="region">
            {{ form.fullLocation }}
          </view>
          <view v-else class="placeholder"> 请填写收货人所在城市 </view>
        </picker>
      </view>
      <view class="form-item">
        <text class="label">详细地址</text>
        <input
          v-model="form.address"
          placeholder-style="color: #888"
          placeholder="街道、楼牌号信息"
        />
      </view>
      <view class="form-item">
        <text class="label">设置默认地址</text>
        <switch
          @change="onChangeDefault"
          :checked="form.isDefault === 1"
          color="#27ba9b"
        />
      </view>
    </view>
    <!-- 提交按钮 -->
    <view class="button" @tap="submitForm"> 保 存 </view>
  </view>
</template>
<script>
export default {
   data() {
    return {
      form: {
        /** * 详细地址 */
        address: "",
        /** * 联系方式 */
        contact: "",
        /** * 是否为默认,1为是,0为否 */
        isDefault: 0,
        /** * 收货人姓名 */
        receiver: "",
        /** 省市县 */
        fullLocation: "",
        /** * 所在区/县编码 */
        countyCode: "",
        /** * 所在城市编码 */
        cityCode: "",
        /** * 所在省份编码 */
        provinceCode: "",
      },
    };
  },
};
</script>
<style lang="scss">
page {
  background-color: #f4f4f4;
}
.form {
  margin: 20rpx 20rpx 0;
  padding: 0 20rpx;
  border-radius: 10rpx;
  background-color: #fff;
  .form-item {
    display: flex;
    min-height: 96rpx;
    line-height: 46rpx;
    padding: 25rpx 10rpx;
    background-color: #fff;
    font-size: 28rpx;
    border-bottom: 1rpx solid #ddd;
    position: relative;
    &:last-child {
      border: none;
    }
    .label {
      width: 200rpx;
      color: #333;
    }
    input {
      flex: 1;
      display: block;
      height: 46rpx;
    }
    switch {
      position: absolute;
      right: -10rpx;
      transform: scale(0.7) translateY(-8px);
    }
    picker {
      flex: 1;
    }
    .region {
      color: #333;
    }
    .placeholder {
      color: #888;
    }
  }
}
.button {
  height: 80rpx;
  text-align: center;
  line-height: 80rpx;
  margin: 30rpx 20rpx;
  color: #fff;
  border-radius: 80rpx;
  font-size: 30rpx;
  background-color: #27ba9b;
}
</style>

新增地址-双向绑定

diff
<template>
  
  <view class="viewport">
    <!-- 地址信息 -->
    <view class="form">
      <view class="form-item">
        <text class="label">姓名</text>
        <input
+         v-model="form.receiver"
          placeholder-style="color: #888"
          placeholder="请填写收货人姓名"
        />
      </view>
      <view class="form-item">
        <text class="label">手机号码</text>
        <input
+         v-model="form.contact"
          placeholder-style="color: #888"
          placeholder="请填写收货人手机号码"
        />
      </view>
      <view class="form-item">
        <text class="label">省/市/县 (区)</text>
        <picker 
+         @change="onChangeRegion" 
          mode="region"
        >
          <view v-if="form.fullLocation" class="region">
            {{ form.fullLocation }}
          </view>
          <view v-else class="placeholder"> 请填写收货人所在城市 </view>
        </picker>
      </view>
      <view class="form-item">
        <text class="label">详细地址</text>
        <input
+         v-model="form.address"
          placeholder-style="color: #888"
          placeholder="街道、楼牌号信息"
        />
      </view>
      <view class="form-item">
        <text class="label">设置默认地址</text>
        <switch
+         @change="onChangeDefault"
          :checked="form.isDefault === 1"
          color="#27ba9b"
        />
      </view>
    </view>
    <!-- 提交按钮 -->
    <view class="button"> 保 存 </view>
  </view>
</template>

<script>
export default {
  
+  methods: {
+    onChangeDefault(e) {
+      this.form.isDefault = e.detail.value ? 1 : 0;
+    },
+    onChangeRegion(e) {
+      const { code, value, postcode } = e.detail;
+      this.form.fullLocation = value.join("");
+      this.form.provinceCode = code[0];
+      this.form.cityCode = code[1];
+      this.form.countyCode = code[2];
+    },
+  },
};
</script>

新增地址-实现新增

src/api/address.js

js
/**
 * 添加收货地址
 */
export const addAddressAPI = (data) => {
  return request({
    url: "/member/address",
    method: "post",
    data,
  });
};

src/pages/my/address/form.vue

js
async onSubmit() {
  await addAddressAPI(this.form);
  uni.showToast({
    title: "新增成功",
    icon: "success",
  });
  setTimeout(() => {
    uni.navigateBack();
  }, 1000);
},

编辑地址

编制地址页面可以和新建地址页面共用一个页面

src/api/address.js

js
/**
 * 更新收货地址
 */
export const updateAddressAPI = (id, data) => {
  return request({
    url: `/member/address/${id}`,
    method: "put",
    data,
  });
};

src/pages/my/address/form.vue

js
// 2. 获取id,判断是否为编辑
  async onLoad({ id }) {
    if (id) {
      // 3. 查询表单数据
      const { result } = await getAddressDetailAPI(id);
      // 4. 回填表单信息
      this.form = { ...result };
      // 5. 修改标题
      uni.setNavigationBarTitle({ title: "编辑地址" });
    }
  },
diff
async onSubmit() {
-  await addAddressAPI(this.form);
  
  // 3. 更新表单
+  if (this.form.id) {
+    const obj = { ...this.form };
+    delete obj.id;

+    await updateAddressAPI(this.form.id, obj);
+  } else {
+    await addAddressAPI(this.form);
+  }

  uni.showToast({
-   title: "新增成功",
    // 4. 替换文字
+   title: this.form.id ? "编辑成功" : "新增成功",
    icon: "success",
  });
  setTimeout(() => {
    uni.navigateBack();
  }, 1000);
},

删除地址

image-20221128215210639

  1. 在显示地址时,我们使用了 uni-ui 提供的 uni-swipe-action 滑动组件,实现滑动删除地址
  2. 当用户点击 删除 时,先弹出一个对话模态框,当用户确定删除时,再执行删除功能。

src/api/address.js

js
/**
 * 删除收货地址
 */
export const delAddressAPI = (id) => {
  return request({
    url: `/member/address/${id}`,
    method: "delete",
  });
};

src/pages/my/address/form.vue

diff
<template v-slot:right>
-    <view class="swipe-cell-action">
+    <view class="swipe-cell-action" @click="onDelById(item.id)">
          <button class="delete-button">删除</button>
      </view>
  </template>
js
async onDelById(id) {
  // 💥💥 注意增加二次确认
  const [err, { confirm }] = await uni.showModal({
    title: "提示",
    content: "确认删除吗?",
  });
  if (!err && confirm) {
    await delAddressAPI(id);
    uni.showToast({
      title: "删除成功",
      icon: "success",
    });
    this.list = this.list.filter((item) => {
      return item.id !== id;
    });
  }
},

Released under the MIT License.