|
|
@@ -0,0 +1,198 @@
|
|
|
+<template>
|
|
|
+ <cl-page>
|
|
|
+ <view class="p-3">
|
|
|
+ <view class="p-4 bg-white rounded-2xl dark:!bg-surface-800 mb-3">
|
|
|
+ <cl-form ref="formRef" v-model="formData" :rules="rules" :disabled="saving">
|
|
|
+ <cl-form-item :label="t('收货人')" prop="contact" required>
|
|
|
+ <cl-input
|
|
|
+ v-model="formData.contact"
|
|
|
+ :placeholder="t('请输入收货人姓名')"
|
|
|
+ ></cl-input>
|
|
|
+ </cl-form-item>
|
|
|
+
|
|
|
+ <cl-form-item :label="t('手机号')" prop="phone" required>
|
|
|
+ <cl-input
|
|
|
+ v-model="formData.phone"
|
|
|
+ :placeholder="t('请输入手机号')"
|
|
|
+ :maxlength="11"
|
|
|
+ ></cl-input>
|
|
|
+ </cl-form-item>
|
|
|
+
|
|
|
+ <cl-form-item :label="t('地区')" prop="province" required>
|
|
|
+ <cl-cascader
|
|
|
+ v-model="regions"
|
|
|
+ :placeholder="t('选择省市区')"
|
|
|
+ :options="pcaOptions"
|
|
|
+ @change="onRegionsChange"
|
|
|
+ ></cl-cascader>
|
|
|
+ </cl-form-item>
|
|
|
+
|
|
|
+ <cl-form-item :label="t('详细地址')" prop="address" required>
|
|
|
+ <cl-input
|
|
|
+ v-model="formData.address"
|
|
|
+ :placeholder="t('小区楼栋、门牌号、村等')"
|
|
|
+ ></cl-input>
|
|
|
+ </cl-form-item>
|
|
|
+ </cl-form>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <cl-list>
|
|
|
+ <cl-list-item :label="t('默认地址')">
|
|
|
+ <cl-switch v-model="formData.isDefault"></cl-switch>
|
|
|
+ </cl-list-item>
|
|
|
+ </cl-list>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <cl-footer>
|
|
|
+ <cl-button @tap="save()">{{ t("保存") }}</cl-button>
|
|
|
+ </cl-footer>
|
|
|
+ </cl-page>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { router, isEmpty, type Response, request, parse } from "@/cool";
|
|
|
+import { t } from "@/locale";
|
|
|
+import { useCascader, useForm, useUi, type ClFormRule } from "@/uni_modules/cool-ui";
|
|
|
+import { type Ref, ref } from "vue";
|
|
|
+import pca from "@/data/pca.json";
|
|
|
+import type { UserAddress } from "../types";
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ id: {
|
|
|
+ type: String,
|
|
|
+ default: ""
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const ui = useUi();
|
|
|
+
|
|
|
+const { formRef, validate } = useForm();
|
|
|
+
|
|
|
+// 省市区级联选项数据
|
|
|
+const pcaOptions = useCascader(pca);
|
|
|
+
|
|
|
+// 地区选择的值,格式为 [省, 市, 区]
|
|
|
+const regions = ref<string[]>([]);
|
|
|
+
|
|
|
+// 表单数据,包含收货人、手机号、地区、详细地址、是否默认等字段
|
|
|
+const formData = ref<UserAddress>({
|
|
|
+ contact: "",
|
|
|
+ phone: "",
|
|
|
+ province: "",
|
|
|
+ city: "",
|
|
|
+ district: "",
|
|
|
+ address: "",
|
|
|
+ isDefault: false
|
|
|
+}) as Ref<UserAddress>;
|
|
|
+
|
|
|
+// 表单验证规则,校验收货人、手机号、详细地址、地区等必填项
|
|
|
+const rules = new Map<string, ClFormRule[]>([
|
|
|
+ ["contact", [{ required: true, message: t("收货人不能为空") }]],
|
|
|
+ [
|
|
|
+ "phone",
|
|
|
+ [
|
|
|
+ { required: true, message: t("手机号不能为空") },
|
|
|
+ { pattern: /^1[3-9]\d{9}$/, message: t("手机号格式不正确") }
|
|
|
+ ]
|
|
|
+ ],
|
|
|
+ ["address", [{ required: true, message: t("详细地址不能为空") }]],
|
|
|
+ ["province", [{ required: true, message: t("所在地区不能为空") }]]
|
|
|
+]);
|
|
|
+
|
|
|
+// 保存按钮loading状态
|
|
|
+const saving = ref(false);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 保存地址信息
|
|
|
+ * 1. 校验表单
|
|
|
+ * 2. 组装数据
|
|
|
+ * 3. 请求后端接口,新增或更新地址
|
|
|
+ */
|
|
|
+function save() {
|
|
|
+ validate(async (valid, errors) => {
|
|
|
+ if (valid) {
|
|
|
+ ui.showLoading(t("保存中"));
|
|
|
+
|
|
|
+ // 解构地区信息
|
|
|
+ const [province, city, district] = regions.value;
|
|
|
+
|
|
|
+ saving.value = true;
|
|
|
+
|
|
|
+ // 合并表单数据和地区信息
|
|
|
+ const data = {
|
|
|
+ ...formData.value,
|
|
|
+ province,
|
|
|
+ city,
|
|
|
+ district
|
|
|
+ };
|
|
|
+
|
|
|
+ // 根据是否有id判断是新增还是编辑
|
|
|
+ request({
|
|
|
+ url: `/app/user/address/${props.id != "" ? "update" : "add"}`,
|
|
|
+ method: "POST",
|
|
|
+ data
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ // 保存成功返回上一页
|
|
|
+ router.back();
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ // 保存失败提示错误信息
|
|
|
+ ui.showToast({ message: (err as Response).message! });
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ ui.hideLoading();
|
|
|
+ saving.value = false;
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 校验失败提示第一个错误
|
|
|
+ ui.showToast({ message: errors[0].message });
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取地址详情(编辑时调用)
|
|
|
+ * 1. 请求后端接口获取地址详情
|
|
|
+ * 2. 回填表单和地区选择
|
|
|
+ */
|
|
|
+function getInfo() {
|
|
|
+ request({
|
|
|
+ url: "/app/user/address/info",
|
|
|
+ data: { id: props.id }
|
|
|
+ })
|
|
|
+ .then((res) => {
|
|
|
+ if (res != null) {
|
|
|
+ // 解析并赋值表单数据
|
|
|
+ formData.value = parse<UserAddress>(res)!;
|
|
|
+ // 回填地区选择
|
|
|
+ regions.value = [
|
|
|
+ formData.value.province,
|
|
|
+ formData.value.city,
|
|
|
+ formData.value.district
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ ui.showToast({ message: (err as Response).message! });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 地区选择变化时触发
|
|
|
+ * @param value 选中的地区数组 [省, 市, 区]
|
|
|
+ */
|
|
|
+function onRegionsChange(value: string[]) {
|
|
|
+ const [province, city, district] = isEmpty(value) ? ["", "", ""] : value;
|
|
|
+
|
|
|
+ formData.value.province = province;
|
|
|
+ formData.value.city = city;
|
|
|
+ formData.value.district = district;
|
|
|
+}
|
|
|
+
|
|
|
+onReady(() => {
|
|
|
+ if (props.id != "") {
|
|
|
+ getInfo();
|
|
|
+ }
|
|
|
+});
|
|
|
+</script>
|