优化模板界面内容
This commit is contained in:
parent
76e0db644d
commit
f0200ac849
@ -56,169 +56,191 @@
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import provinces from "./province.js";
|
||||
import citys from "./city.js";
|
||||
import areas from "./area.js";
|
||||
/**
|
||||
* city-select 省市区级联选择器
|
||||
* @property {String Number} z-index 弹出时的z-index值(默认1075)
|
||||
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker(默认true)
|
||||
* @property {String} default-region 默认选中的地区,中文形式
|
||||
* @property {String} default-code 默认选中的地区,编号形式
|
||||
*/
|
||||
export default {
|
||||
name: 'u-city-select',
|
||||
props: {
|
||||
// 通过双向绑定控制组件的弹出与收起
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 默认显示的地区,可传类似["河北省", "秦皇岛市", "北戴河区"]
|
||||
defaultRegion: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 默认显示地区的编码,defaultRegion和areaCode同时存在,areaCode优先,可传类似["13", "1303", "130304"]
|
||||
areaCode: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 是否允许通过点击遮罩关闭Picker
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 弹出的z-index值
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, PropType } from 'vue';
|
||||
import provincesSource from "./province.js";
|
||||
import citysSource from "./city.js";
|
||||
import areasSource from "./area.js";
|
||||
|
||||
// 定义接口
|
||||
interface Region {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface CitySelectResult {
|
||||
province: Region;
|
||||
city: Region;
|
||||
area: Region;
|
||||
}
|
||||
|
||||
interface TabItem {
|
||||
name: string;
|
||||
}
|
||||
|
||||
// Props 定义
|
||||
const props = defineProps({
|
||||
// 通过双向绑定控制组件的弹出与收起
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cityValue: "",
|
||||
isChooseP: false, //是否已经选择了省
|
||||
province: 0, //省级下标
|
||||
provinces: provinces,
|
||||
isChooseC: false, //是否已经选择了市
|
||||
city: 0, //市级下标
|
||||
citys: citys[0],
|
||||
isChooseA: false, //是否已经选择了区
|
||||
area: 0, //区级下标
|
||||
areas: areas[0][0],
|
||||
tabsIndex: 0,
|
||||
}
|
||||
// 默认显示的地区,可传类似["河北省", "秦皇岛市", "北戴河区"]
|
||||
defaultRegion: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
// 默认显示地区的编码,defaultRegion和areaCode同时存在,areaCode优先,可传类似["13", "1303", "130304"]
|
||||
areaCode: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
computed: {
|
||||
isChange() {
|
||||
return this.tabsIndex > 1;
|
||||
},
|
||||
genTabsList() {
|
||||
let tabsList = [{
|
||||
name: "请选择"
|
||||
}];
|
||||
if (this.isChooseP) {
|
||||
tabsList[0]['name'] = this.provinces[this.province]['label'];
|
||||
tabsList[1] = {
|
||||
name: "请选择"
|
||||
};
|
||||
}
|
||||
if (this.isChooseC) {
|
||||
tabsList[1]['name'] = this.citys[this.city]['label'];
|
||||
tabsList[2] = {
|
||||
name: "请选择"
|
||||
};
|
||||
}
|
||||
if (this.isChooseA) {
|
||||
tabsList[2]['name'] = this.areas[this.area]['label'];
|
||||
}
|
||||
return tabsList;
|
||||
},
|
||||
uZIndex() {
|
||||
// 如果用户有传递z-index值,优先使用
|
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
|
||||
}
|
||||
// 是否允许通过点击遮罩关闭Picker
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
emits: ['city-change'],
|
||||
methods: {
|
||||
init() {
|
||||
if (this.areaCode.length == 3) {
|
||||
this.setProvince("", this.areaCode[0]);
|
||||
this.setCity("", this.areaCode[1]);
|
||||
this.setArea("", this.areaCode[2]);
|
||||
} else if (this.defaultRegion.length == 3) {
|
||||
this.setProvince(this.defaultRegion[0], "");
|
||||
this.setCity(this.defaultRegion[1], "");
|
||||
this.setArea(this.defaultRegion[2], "");
|
||||
};
|
||||
},
|
||||
setProvince(label = "", value = "") {
|
||||
this.provinces.map((v, k) => {
|
||||
if (value ? v.value == value : v.label == label) {
|
||||
this.provinceChange(k);
|
||||
}
|
||||
})
|
||||
},
|
||||
setCity(label = "", value = "") {
|
||||
this.citys.map((v, k) => {
|
||||
if (value ? v.value == value : v.label == label) {
|
||||
this.cityChange(k);
|
||||
}
|
||||
})
|
||||
},
|
||||
setArea(label = "", value = "") {
|
||||
this.areas.map((v, k) => {
|
||||
if (value ? v.value == value : v.label == label) {
|
||||
this.isChooseA = true;
|
||||
this.area = k;
|
||||
}
|
||||
})
|
||||
},
|
||||
close() {
|
||||
this.$emit('update:modelValue', false);
|
||||
this.$emit('close');
|
||||
},
|
||||
tabsChange(value) {
|
||||
this.tabsIndex = value.index;
|
||||
},
|
||||
provinceChange(index) {
|
||||
this.isChooseP = true;
|
||||
this.isChooseC = false;
|
||||
this.isChooseA = false;
|
||||
this.province = index;
|
||||
this.citys = citys[index];
|
||||
this.tabsIndex = 1;
|
||||
},
|
||||
cityChange(index) {
|
||||
this.isChooseC = true;
|
||||
this.isChooseA = false;
|
||||
this.city = index;
|
||||
this.areas = areas[this.province][index];
|
||||
this.tabsIndex = 2;
|
||||
},
|
||||
areaChange(index) {
|
||||
this.isChooseA = true;
|
||||
this.area = index;
|
||||
let result = {};
|
||||
result.province = this.provinces[this.province];
|
||||
result.city = this.citys[this.city];
|
||||
result.area = this.areas[this.area];
|
||||
this.$emit('city-change', result);
|
||||
this.close();
|
||||
}
|
||||
// 弹出的z-index值
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
|
||||
// 事件定义
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: boolean): void;
|
||||
(e: 'close'): void;
|
||||
(e: 'city-change', result: CitySelectResult): void;
|
||||
}>();
|
||||
|
||||
const cityValue = ref("");
|
||||
const isChooseP = ref(false); // 是否已经选择了省
|
||||
const province = ref(0); // 省级下标
|
||||
const provinces = ref<Region[]>(provincesSource);
|
||||
const isChooseC = ref(false); // 是否已经选择了市
|
||||
const city = ref(0); // 市级下标
|
||||
const citys = ref<Region[]>(citysSource[0]);
|
||||
const isChooseA = ref(false); // 是否已经选择了区
|
||||
const area = ref(0); // 区级下标
|
||||
const areas = ref<Region[]>(areasSource[0][0]);
|
||||
const tabsIndex = ref(0);
|
||||
const tabs = ref();
|
||||
|
||||
// 计算属性
|
||||
const isChange = computed(() => {
|
||||
return tabsIndex.value > 1;
|
||||
});
|
||||
|
||||
const genTabsList = computed((): TabItem[] => {
|
||||
let tabsList: TabItem[] = [{
|
||||
name: "请选择"
|
||||
}];
|
||||
|
||||
if (isChooseP.value) {
|
||||
tabsList[0].name = provinces.value[province.value].label;
|
||||
tabsList[1] = {
|
||||
name: "请选择"
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
if (isChooseC.value) {
|
||||
tabsList[1].name = citys.value[city.value].label;
|
||||
tabsList[2] = {
|
||||
name: "请选择"
|
||||
};
|
||||
}
|
||||
|
||||
if (isChooseA.value) {
|
||||
tabsList[2].name = areas.value[area.value].label;
|
||||
}
|
||||
|
||||
return tabsList;
|
||||
});
|
||||
|
||||
const uZIndex = computed(() => {
|
||||
// 如果用户有传递z-index值,优先使用
|
||||
return props.zIndex ? props.zIndex : 1075; // 假设$u.zIndex.popup为1075
|
||||
});
|
||||
|
||||
// 方法
|
||||
const setProvince = (label = "", value = "") => {
|
||||
provinces.value.map((v, k) => {
|
||||
if (value ? v.value == value : v.label == label) {
|
||||
provinceChange(k);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const setCity = (label = "", value = "") => {
|
||||
citys.value.map((v, k) => {
|
||||
if (value ? v.value == value : v.label == label) {
|
||||
cityChange(k);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const setArea = (label = "", value = "") => {
|
||||
areas.value.map((v, k) => {
|
||||
if (value ? v.value == value : v.label == label) {
|
||||
isChooseA.value = true;
|
||||
area.value = k;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
emit('update:modelValue', false);
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const tabsChange = (value: { index: number }) => {
|
||||
tabsIndex.value = value.index;
|
||||
};
|
||||
|
||||
const provinceChange = (index: number) => {
|
||||
isChooseP.value = true;
|
||||
isChooseC.value = false;
|
||||
isChooseA.value = false;
|
||||
province.value = index;
|
||||
citys.value = citysSource[index];
|
||||
tabsIndex.value = 1;
|
||||
};
|
||||
|
||||
const cityChange = (index: number) => {
|
||||
isChooseC.value = true;
|
||||
isChooseA.value = false;
|
||||
city.value = index;
|
||||
areas.value = areasSource[province.value][index];
|
||||
tabsIndex.value = 2;
|
||||
};
|
||||
|
||||
const areaChange = (index: number) => {
|
||||
isChooseA.value = true;
|
||||
area.value = index;
|
||||
const result: CitySelectResult = {
|
||||
province: provinces.value[province.value],
|
||||
city: citys.value[city.value],
|
||||
area: areas.value[area.value]
|
||||
};
|
||||
emit('city-change', result);
|
||||
close();
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
if (props.areaCode.length == 3) {
|
||||
setProvince("", props.areaCode[0]);
|
||||
setCity("", props.areaCode[1]);
|
||||
setArea("", props.areaCode[2]);
|
||||
} else if (props.defaultRegion.length == 3) {
|
||||
setProvince(props.defaultRegion[0], "");
|
||||
setCity(props.defaultRegion[1], "");
|
||||
setArea(props.defaultRegion[2], "");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.area-box {
|
||||
width: 100%;
|
||||
|
||||
@ -2,22 +2,24 @@
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import tab from '@/plugins/tab';
|
||||
import citySelect from '@/components/u-city-select/u-city-select.vue';
|
||||
import { useAddressEditPage } from './index';
|
||||
|
||||
// 页面状态变量
|
||||
const show = ref(false);
|
||||
const defaultAddress = ref(false);
|
||||
const selectedTag = ref('家');
|
||||
const isEdit = ref(false); // 是否为编辑模式
|
||||
const editId = ref(''); // 编辑地址的ID
|
||||
// 使用编辑页面Hook
|
||||
const {
|
||||
isEdit,
|
||||
form,
|
||||
defaultAddress,
|
||||
selectedTag,
|
||||
addressTags,
|
||||
initEditPage,
|
||||
saveAddress,
|
||||
deleteAddress
|
||||
} = useAddressEditPage();
|
||||
|
||||
// 表单数据和验证状态
|
||||
const form = reactive({
|
||||
name: '',
|
||||
phone: '',
|
||||
region: '',
|
||||
address: ''
|
||||
});
|
||||
// 地区选择器显示状态
|
||||
const showRegionPicker = ref(false);
|
||||
|
||||
// 表单错误状态 - 移到Vue组件中
|
||||
const formErrors = reactive({
|
||||
name: false,
|
||||
phone: false,
|
||||
@ -25,101 +27,69 @@ const formErrors = reactive({
|
||||
address: false
|
||||
});
|
||||
|
||||
// 重置表单错误
|
||||
function resetFormErrors() {
|
||||
formErrors.name = false;
|
||||
formErrors.phone = false;
|
||||
formErrors.region = false;
|
||||
formErrors.address = false;
|
||||
}
|
||||
|
||||
// 初始化页面数据
|
||||
onMounted(() => {
|
||||
// 获取传递过来的地址数据(如果是编辑模式)
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const currentPage: any = pages[pages.length - 1];
|
||||
const options = currentPage.$page?.options;
|
||||
|
||||
if (options && options.id) {
|
||||
isEdit.value = true;
|
||||
editId.value = options.id;
|
||||
loadAddressData(options.id);
|
||||
}
|
||||
|
||||
// 调用hook的初始化方法
|
||||
initEditPage(options?.id);
|
||||
// 重置表单错误状态
|
||||
resetFormErrors();
|
||||
});
|
||||
|
||||
// 加载要编辑的地址数据
|
||||
const loadAddressData = (id: string) => {
|
||||
try {
|
||||
const addressList = uni.getStorageSync('addressList') || [];
|
||||
const address = addressList.find((item: any) => item.id === id);
|
||||
|
||||
if (address) {
|
||||
form.name = address.name;
|
||||
form.phone = address.phoneOriginal || address.phone; // 使用原始手机号,而不是隐藏处理后的号码
|
||||
form.region = address.region;
|
||||
form.address = address.address;
|
||||
selectedTag.value = address.tag || '家';
|
||||
defaultAddress.value = address.isDefault;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载地址数据失败', e);
|
||||
}
|
||||
};
|
||||
|
||||
// 设置默认地址
|
||||
const setDefault = (e: any) => {
|
||||
function handleSetDefault(e: any) {
|
||||
defaultAddress.value = e.detail.value;
|
||||
};
|
||||
}
|
||||
|
||||
// 显示地区选择器
|
||||
const showRegionPicker = () => {
|
||||
show.value = true;
|
||||
};
|
||||
function handleShowRegionPicker() {
|
||||
showRegionPicker.value = true;
|
||||
}
|
||||
|
||||
// 确认选择地区
|
||||
const cityChange = (e) => {
|
||||
function handleCityChange(e: any) {
|
||||
form.region = e.province.label + e.city.label + e.area.label;
|
||||
formErrors.region = false;
|
||||
};
|
||||
}
|
||||
|
||||
// 选择标签
|
||||
const selectTag = (tag: string) => {
|
||||
function handleSelectTag(tag: string) {
|
||||
selectedTag.value = tag;
|
||||
};
|
||||
}
|
||||
|
||||
// 表单验证
|
||||
const validateForm = () => {
|
||||
let isValid = true;
|
||||
|
||||
function validateForm(): boolean {
|
||||
// 验证姓名
|
||||
if (!form.name.trim()) {
|
||||
formErrors.name = true;
|
||||
isValid = false;
|
||||
} else {
|
||||
formErrors.name = false;
|
||||
}
|
||||
|
||||
formErrors.name = !form.name.trim();
|
||||
|
||||
// 验证手机号
|
||||
const phoneReg = /^1[3-9]\d{9}$/;
|
||||
if (!phoneReg.test(form.phone)) {
|
||||
formErrors.phone = true;
|
||||
isValid = false;
|
||||
} else {
|
||||
formErrors.phone = false;
|
||||
}
|
||||
|
||||
formErrors.phone = !phoneReg.test(form.phone);
|
||||
|
||||
// 验证地区
|
||||
if (!form.region) {
|
||||
formErrors.region = true;
|
||||
isValid = false;
|
||||
} else {
|
||||
formErrors.region = false;
|
||||
}
|
||||
|
||||
formErrors.region = !form.region;
|
||||
|
||||
// 验证详细地址
|
||||
if (!form.address.trim()) {
|
||||
formErrors.address = true;
|
||||
isValid = false;
|
||||
} else {
|
||||
formErrors.address = false;
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
||||
formErrors.address = !form.address.trim();
|
||||
|
||||
// 如果有任何错误,返回false
|
||||
return !(formErrors.name || formErrors.phone || formErrors.region || formErrors.address);
|
||||
}
|
||||
|
||||
// 保存地址
|
||||
const saveAddress = () => {
|
||||
function handleSaveAddress() {
|
||||
// 使用本地验证方法
|
||||
if (!validateForm()) {
|
||||
uni.showToast({
|
||||
title: '请填写完整信息',
|
||||
@ -127,53 +97,26 @@ const saveAddress = () => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// 获取现有地址列表
|
||||
let addressList = uni.getStorageSync('addressList') || [];
|
||||
|
||||
// 创建新地址对象
|
||||
const addressData = {
|
||||
id: isEdit.value ? editId.value : Date.now().toString(),
|
||||
name: form.name,
|
||||
phone: form.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'), // 隐藏中间4位
|
||||
phoneOriginal: form.phone, // 保存原始手机号码用于编辑
|
||||
region: form.region,
|
||||
address: form.address,
|
||||
tag: selectedTag.value,
|
||||
isDefault: defaultAddress.value
|
||||
};
|
||||
|
||||
if (defaultAddress.value) {
|
||||
// 如果设为默认,将其他地址设为非默认
|
||||
addressList = addressList.map((item: any) => {
|
||||
return { ...item, isDefault: false };
|
||||
const success = saveAddress();
|
||||
|
||||
if (success) {
|
||||
uni.showToast({
|
||||
title: isEdit.value ? '修改成功' : '添加成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 延迟返回,让用户看到提示
|
||||
setTimeout(() => {
|
||||
tab.navigateBack();
|
||||
}, 1000);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
|
||||
if (isEdit.value) {
|
||||
// 更新现有地址
|
||||
const index = addressList.findIndex((item: any) => item.id === editId.value);
|
||||
if (index !== -1) {
|
||||
addressList[index] = addressData;
|
||||
}
|
||||
} else {
|
||||
// 添加新地址
|
||||
addressList.push(addressData);
|
||||
}
|
||||
|
||||
// 保存到本地存储
|
||||
uni.setStorageSync('addressList', addressList);
|
||||
|
||||
uni.showToast({
|
||||
title: isEdit.value ? '修改成功' : '添加成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 延迟返回,让用户看到提示
|
||||
setTimeout(() => {
|
||||
tab.navigateBack();
|
||||
}, 1000);
|
||||
} catch (e) {
|
||||
console.error('保存地址失败', e);
|
||||
uni.showToast({
|
||||
@ -181,30 +124,35 @@ const saveAddress = () => {
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 删除地址
|
||||
const deleteAddress = () => {
|
||||
function handleDeleteAddress() {
|
||||
if (!isEdit.value) return;
|
||||
|
||||
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除此地址吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
let addressList = uni.getStorageSync('addressList') || [];
|
||||
addressList = addressList.filter((item: any) => item.id !== editId.value);
|
||||
uni.setStorageSync('addressList', addressList);
|
||||
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
tab.navigateBack();
|
||||
}, 1000);
|
||||
const success = deleteAddress();
|
||||
|
||||
if (success) {
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
tab.navigateBack();
|
||||
}, 1000);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '删除失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('删除地址失败', e);
|
||||
uni.showToast({
|
||||
@ -215,8 +163,9 @@ const deleteAddress = () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="wrap">
|
||||
<view class="container">
|
||||
@ -225,71 +174,50 @@ const deleteAddress = () => {
|
||||
<view class="left">
|
||||
<text class="required">*</text>收货人
|
||||
</view>
|
||||
<input
|
||||
type="text"
|
||||
v-model="form.name"
|
||||
placeholder-class="line"
|
||||
placeholder="请填写收货人姓名"
|
||||
:class="{ 'error-input': formErrors.name }"
|
||||
/>
|
||||
<input type="text" v-model="form.name" placeholder-class="line" placeholder="请填写收货人姓名"
|
||||
:class="{ 'error-input': formErrors.name }" />
|
||||
<u-icon name="account" size="36rpx" color="#999"></u-icon>
|
||||
</view>
|
||||
<view class="error-msg" v-if="formErrors.name">请输入收货人姓名</view>
|
||||
|
||||
|
||||
<view class="item">
|
||||
<view class="left">
|
||||
<text class="required">*</text>手机号码
|
||||
</view>
|
||||
<input
|
||||
type="number"
|
||||
v-model="form.phone"
|
||||
placeholder-class="line"
|
||||
placeholder="请填写收货人手机号"
|
||||
maxlength="11"
|
||||
:class="{ 'error-input': formErrors.phone }"
|
||||
/>
|
||||
<input type="number" v-model="form.phone" placeholder-class="line" placeholder="请填写收货人手机号" maxlength="11"
|
||||
:class="{ 'error-input': formErrors.phone }" />
|
||||
<u-icon name="phone" size="36rpx" color="#999"></u-icon>
|
||||
</view>
|
||||
<view class="error-msg" v-if="formErrors.phone">请输入正确的手机号码</view>
|
||||
|
||||
<view class="item" @tap="showRegionPicker">
|
||||
|
||||
<view class="item" @tap="handleShowRegionPicker">
|
||||
<view class="left">
|
||||
<text class="required">*</text>所在地区
|
||||
</view>
|
||||
<input
|
||||
disabled
|
||||
v-model="form.region"
|
||||
type="text"
|
||||
placeholder-class="line"
|
||||
placeholder="省市区县、乡镇等"
|
||||
:class="{ 'error-input': formErrors.region }"
|
||||
/>
|
||||
<input disabled v-model="form.region" type="text" placeholder-class="line" placeholder="省市区县、乡镇等"
|
||||
:class="{ 'error-input': formErrors.region }" />
|
||||
<u-icon name="arrow-right" size="36rpx" color="#999"></u-icon>
|
||||
</view>
|
||||
<view class="error-msg" v-if="formErrors.region">请选择所在地区</view>
|
||||
|
||||
|
||||
<view class="item address">
|
||||
<view class="left">
|
||||
<text class="required">*</text>详细地址
|
||||
</view>
|
||||
<textarea
|
||||
v-model="form.address"
|
||||
type="text"
|
||||
placeholder-class="line"
|
||||
placeholder="街道、楼牌等"
|
||||
:class="{ 'error-textarea': formErrors.address }"
|
||||
/>
|
||||
<textarea v-model="form.address" type="text" placeholder-class="line" placeholder="街道、楼牌等"
|
||||
:class="{ 'error-textarea': formErrors.address }" />
|
||||
</view>
|
||||
<view class="error-msg" v-if="formErrors.address">请输入详细地址</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="bottom">
|
||||
<view class="tag">
|
||||
<view class="left">标签</view>
|
||||
<view class="right">
|
||||
<text class="tags" :class="{'active': selectedTag === '家'}" @tap="selectTag('家')">家</text>
|
||||
<text class="tags" :class="{'active': selectedTag === '公司'}" @tap="selectTag('公司')">公司</text>
|
||||
<text class="tags" :class="{'active': selectedTag === '学校'}" @tap="selectTag('学校')">学校</text>
|
||||
<text v-for="tag in addressTags" :key="tag" class="tags" :class="{ 'active': selectedTag === tag }"
|
||||
@tap="handleSelectTag(tag)">
|
||||
{{ tag }}
|
||||
</text>
|
||||
<view class="tags plus"><u-icon size="22" name="plus" color="#999"></u-icon></view>
|
||||
</view>
|
||||
</view>
|
||||
@ -299,22 +227,22 @@ const deleteAddress = () => {
|
||||
<view class="tips">提醒:每次下单会默认推荐该地址</view>
|
||||
</view>
|
||||
<view class="right">
|
||||
<switch color="#fa3534" :checked="defaultAddress" @change="setDefault" />
|
||||
<switch color="#fa3534" :checked="defaultAddress" @change="handleSetDefault" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="button-group">
|
||||
<view class="save-btn" @tap="saveAddress">
|
||||
<view class="save-btn" @tap="handleSaveAddress">
|
||||
{{ isEdit ? '保存修改' : '保存地址' }}
|
||||
</view>
|
||||
<view v-if="isEdit" class="delete-btn" @tap="deleteAddress">
|
||||
<view v-if="isEdit" class="delete-btn" @tap="handleDeleteAddress">
|
||||
删除地址
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<city-select v-model="show" @city-change="cityChange"></city-select>
|
||||
|
||||
<city-select v-model="showRegionPicker" @city-change="handleCityChange"></city-select>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -352,7 +280,7 @@ const deleteAddress = () => {
|
||||
width: 180rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
|
||||
|
||||
.required {
|
||||
color: #fa3534;
|
||||
margin-right: 4rpx;
|
||||
@ -364,17 +292,17 @@ const deleteAddress = () => {
|
||||
flex: 1;
|
||||
height: 100rpx;
|
||||
font-size: 30rpx;
|
||||
|
||||
|
||||
&.error-input {
|
||||
border-bottom: 1px solid #fa3534;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
u-icon {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.error-msg {
|
||||
color: #fa3534;
|
||||
font-size: 24rpx;
|
||||
@ -400,7 +328,7 @@ const deleteAddress = () => {
|
||||
padding: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 30rpx;
|
||||
|
||||
|
||||
&.error-textarea {
|
||||
border: 1px solid #fa3534;
|
||||
}
|
||||
@ -444,7 +372,7 @@ const deleteAddress = () => {
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
transition: all 0.3s;
|
||||
|
||||
|
||||
&.active {
|
||||
background-color: #ffebec;
|
||||
color: #fa3534;
|
||||
@ -471,7 +399,7 @@ const deleteAddress = () => {
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
|
||||
.tips {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
@ -480,12 +408,12 @@ const deleteAddress = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 60rpx;
|
||||
|
||||
|
||||
.save-btn {
|
||||
background: linear-gradient(90deg, #ff4034, #fa3534);
|
||||
color: #fff;
|
||||
@ -498,7 +426,7 @@ const deleteAddress = () => {
|
||||
box-shadow: 0 10rpx 20rpx rgba(250, 53, 52, 0.2);
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
|
||||
.delete-btn {
|
||||
margin-top: 30rpx;
|
||||
background: #ffffff;
|
||||
|
||||
256
src/pages_template/pages/address/index.ts
Normal file
256
src/pages_template/pages/address/index.ts
Normal file
@ -0,0 +1,256 @@
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
|
||||
/**
|
||||
* 地址信息接口
|
||||
*/
|
||||
export interface AddressInfo {
|
||||
id: string; // 地址ID
|
||||
name: string; // 收货人姓名
|
||||
phone: string; // 手机号码(已脱敏)
|
||||
region: string; // 地区(如: 广东省深圳市南山区)
|
||||
address: string; // 详细地址
|
||||
tag: string; // 地址标签(如: 家、公司、学校)
|
||||
isDefault: boolean; // 是否为默认地址
|
||||
}
|
||||
|
||||
/**
|
||||
* 示例地址数据
|
||||
*/
|
||||
const sampleAddresses: AddressInfo[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: '张三',
|
||||
phone: '13712348888',
|
||||
region: '广东省深圳市南山区',
|
||||
address: '科技园南路888号创新大厦A座10楼',
|
||||
tag: '公司',
|
||||
isDefault: true
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '李四',
|
||||
phone: '13912345678',
|
||||
region: '广东省深圳市福田区',
|
||||
address: '福中路1000号海城大厦B座20楼2001室',
|
||||
tag: '家',
|
||||
isDefault: false
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '王五',
|
||||
phone: '15812342233',
|
||||
region: '广东省广州市天河区',
|
||||
address: '天河路100号天河城购物中心附近小区A栋3单元701室',
|
||||
tag: '学校',
|
||||
isDefault: false
|
||||
}
|
||||
];
|
||||
|
||||
// 共享的地址数据
|
||||
const addressStore = {
|
||||
list: ref<AddressInfo[]>([]),
|
||||
tags: ref(['家', '公司', '学校'])
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化手机号,中间4位用星号代替
|
||||
*/
|
||||
export function formatPhoneNumber(phone: string): string {
|
||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||
}
|
||||
|
||||
/**
|
||||
* 地址列表页面Hook
|
||||
* @description 提供地址列表页面所需的状态和方法
|
||||
*/
|
||||
export function useAddressListPage() {
|
||||
// 从共享store获取响应式数据
|
||||
const addressList = addressStore.list;
|
||||
|
||||
// 使用计算属性计算是否为空状态
|
||||
const emptyStatus = computed(() => addressList.value.length === 0);
|
||||
|
||||
// 更新地址列表
|
||||
function refreshAddressList() {
|
||||
addressList.value = sampleAddresses
|
||||
// 实际项目中,这里应该调用API获取最新的地址列表
|
||||
// const response = await api.getAddressList();
|
||||
// addressList.value = response.data;
|
||||
}
|
||||
|
||||
// 设置默认地址
|
||||
function setDefaultAddress(id: string): boolean {
|
||||
const index = addressList.value.findIndex(item => item.id === id);
|
||||
|
||||
if (index === -1) return false;
|
||||
|
||||
// 更新所有地址的默认状态
|
||||
addressList.value = addressList.value.map(item => ({
|
||||
...item,
|
||||
isDefault: item.id === id
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 删除地址
|
||||
function deleteAddress(id: string): boolean {
|
||||
const initialLength = addressList.value.length;
|
||||
addressList.value = addressList.value.filter(item => item.id !== id);
|
||||
return addressList.value.length !== initialLength;
|
||||
}
|
||||
|
||||
// 页面显示时刷新数据
|
||||
onShow(() => {
|
||||
refreshAddressList();
|
||||
});
|
||||
|
||||
return {
|
||||
// 响应式状态
|
||||
addressList,
|
||||
emptyStatus,
|
||||
// 方法
|
||||
setDefaultAddress,
|
||||
deleteAddress,
|
||||
refreshAddressList
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 地址编辑页面Hook
|
||||
* @description 提供地址编辑页面所需的状态和方法
|
||||
*/
|
||||
export function useAddressEditPage() {
|
||||
// 从共享store获取响应式数据
|
||||
const addressList = addressStore.list;
|
||||
const addressTags = addressStore.tags;
|
||||
|
||||
// 页面状态
|
||||
const isEdit = ref(false);
|
||||
const editId = ref('');
|
||||
const defaultAddress = ref(false);
|
||||
const selectedTag = ref('家');
|
||||
|
||||
// 表单数据
|
||||
const form = reactive({
|
||||
name: '',
|
||||
phone: '',
|
||||
region: '',
|
||||
address: ''
|
||||
});
|
||||
|
||||
// 加载编辑数据
|
||||
function loadAddressData(id: string): boolean {
|
||||
const address = addressList.value.find(item => item.id === id);
|
||||
|
||||
if (!address) return false;
|
||||
|
||||
// 填充表单数据
|
||||
form.name = address.name;
|
||||
form.phone = address.phone;
|
||||
form.region = address.region;
|
||||
form.address = address.address;
|
||||
selectedTag.value = address.tag;
|
||||
defaultAddress.value = address.isDefault;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 初始化页面
|
||||
function initEditPage(id?: string) {
|
||||
// 重置状态
|
||||
isEdit.value = !!id;
|
||||
editId.value = id || '';
|
||||
defaultAddress.value = false;
|
||||
selectedTag.value = '家';
|
||||
|
||||
form.name = '';
|
||||
form.phone = '';
|
||||
form.region = '';
|
||||
form.address = '';
|
||||
|
||||
// 如果是编辑模式,加载地址数据
|
||||
if (id) {
|
||||
loadAddressData(id);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存地址
|
||||
function saveAddress(): boolean {
|
||||
if (isEdit.value) {
|
||||
// 编辑现有地址
|
||||
const index = addressList.value.findIndex(item => item.id === editId.value);
|
||||
if (index === -1) return false;
|
||||
|
||||
// 如果设为默认,更新其他地址
|
||||
if (defaultAddress.value) {
|
||||
addressList.value = addressList.value.map(item => {
|
||||
if (item.id !== editId.value) {
|
||||
return { ...item, isDefault: false };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
// 更新当前地址
|
||||
addressList.value[index] = {
|
||||
...addressList.value[index],
|
||||
name: form.name,
|
||||
phone: form.phone,
|
||||
region: form.region,
|
||||
address: form.address,
|
||||
tag: selectedTag.value,
|
||||
isDefault: defaultAddress.value
|
||||
};
|
||||
} else {
|
||||
// 添加新地址
|
||||
const newId = Date.now().toString();
|
||||
|
||||
// 如果设为默认,更新其他地址
|
||||
if (defaultAddress.value) {
|
||||
addressList.value = addressList.value.map(item => ({
|
||||
...item,
|
||||
isDefault: false
|
||||
}));
|
||||
}
|
||||
|
||||
// 添加新地址
|
||||
addressList.value.push({
|
||||
id: newId,
|
||||
name: form.name,
|
||||
phone: form.phone,
|
||||
region: form.region,
|
||||
address: form.address,
|
||||
tag: selectedTag.value,
|
||||
isDefault: defaultAddress.value
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 删除地址
|
||||
function deleteAddress(): boolean {
|
||||
if (!isEdit.value) return false;
|
||||
|
||||
const initialLength = addressList.value.length;
|
||||
addressList.value = addressList.value.filter(item => item.id !== editId.value);
|
||||
|
||||
return addressList.value.length !== initialLength;
|
||||
}
|
||||
|
||||
return {
|
||||
// 响应式状态
|
||||
isEdit,
|
||||
editId,
|
||||
form,
|
||||
defaultAddress,
|
||||
selectedTag,
|
||||
addressTags,
|
||||
// 方法
|
||||
initEditPage,
|
||||
saveAddress,
|
||||
deleteAddress
|
||||
};
|
||||
}
|
||||
@ -1,30 +1,14 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
<script setup lang="ts">
|
||||
import tab from '@/plugins/tab';
|
||||
import { AddressInfo, useAddressListPage } from './index';
|
||||
|
||||
const siteList = ref([]);
|
||||
const emptyStatus = ref(false);
|
||||
|
||||
// 使用onShow钩子确保每次页面显示时都刷新地址列表
|
||||
onShow(() => {
|
||||
getData();
|
||||
});
|
||||
|
||||
// 从本地存储获取地址数据
|
||||
function getData() {
|
||||
try {
|
||||
const addressList = uni.getStorageSync('addressList') || [];
|
||||
siteList.value = addressList;
|
||||
emptyStatus.value = addressList.length === 0;
|
||||
} catch (e) {
|
||||
console.error('获取地址列表失败', e);
|
||||
uni.showToast({
|
||||
title: '获取地址列表失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
// 使用列表页面Hook
|
||||
const {
|
||||
addressList,
|
||||
emptyStatus,
|
||||
setDefaultAddress,
|
||||
deleteAddress
|
||||
} = useAddressListPage();
|
||||
|
||||
// 跳转到添加地址页面
|
||||
function toAddSite() {
|
||||
@ -32,25 +16,26 @@ function toAddSite() {
|
||||
}
|
||||
|
||||
// 跳转到编辑地址页面
|
||||
function toEditSite(id) {
|
||||
function toEditSite(id: string) {
|
||||
tab.navigateTo(`/pages_template/pages/address/addSite?id=${id}`);
|
||||
}
|
||||
|
||||
// 设置为默认地址
|
||||
function setAsDefault(id) {
|
||||
function handleSetDefault(id: string) {
|
||||
try {
|
||||
let addressList = uni.getStorageSync('addressList') || [];
|
||||
addressList = addressList.map(item => {
|
||||
return { ...item, isDefault: item.id === id };
|
||||
});
|
||||
const success = setDefaultAddress(id);
|
||||
|
||||
uni.setStorageSync('addressList', addressList);
|
||||
getData(); // 刷新列表
|
||||
|
||||
uni.showToast({
|
||||
title: '设置成功',
|
||||
icon: 'success'
|
||||
});
|
||||
if (success) {
|
||||
uni.showToast({
|
||||
title: '设置成功',
|
||||
icon: 'success'
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '设置失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('设置默认地址失败', e);
|
||||
uni.showToast({
|
||||
@ -61,22 +46,26 @@ function setAsDefault(id) {
|
||||
}
|
||||
|
||||
// 删除地址
|
||||
function deleteAddress(id) {
|
||||
function handleDeleteAddress(id: string) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除此地址吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
let addressList = uni.getStorageSync('addressList') || [];
|
||||
addressList = addressList.filter(item => item.id !== id);
|
||||
uni.setStorageSync('addressList', addressList);
|
||||
getData(); // 刷新列表
|
||||
const success = deleteAddress(id);
|
||||
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
if (success) {
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '删除失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('删除地址失败', e);
|
||||
uni.showToast({
|
||||
@ -90,9 +79,9 @@ function deleteAddress(id) {
|
||||
}
|
||||
|
||||
// 选择并返回地址(用于从订单页面选择地址的场景)
|
||||
function selectAddress(address) {
|
||||
function selectAddress(address: AddressInfo) {
|
||||
const pages = getCurrentPages();
|
||||
const prevPage = pages[pages.length - 2];
|
||||
const prevPage: any = pages[pages.length - 2];
|
||||
|
||||
// 检查页面是否从订单页面跳转而来
|
||||
if (prevPage && prevPage.$page?.options?.from === 'order') {
|
||||
@ -102,6 +91,7 @@ function selectAddress(address) {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="address-container">
|
||||
<!-- 空状态 -->
|
||||
@ -112,7 +102,7 @@ function selectAddress(address) {
|
||||
|
||||
<!-- 地址列表 -->
|
||||
<view v-else>
|
||||
<view class="item" v-for="(address, index) in siteList" :key="address.id">
|
||||
<view class="item" v-for="(address, index) in addressList" :key="address.id">
|
||||
<view class="top" @tap="selectAddress(address)">
|
||||
<view class="name">{{ address.name }}</view>
|
||||
<view class="phone">{{ address.phone }}</view>
|
||||
@ -125,7 +115,7 @@ function selectAddress(address) {
|
||||
{{ address.region }} {{ address.address }}
|
||||
</view>
|
||||
<view class="actions">
|
||||
<view class="action-btn" @tap="setAsDefault(address.id)" v-if="!address.isDefault">
|
||||
<view class="action-btn" @tap="handleSetDefault(address.id)" v-if="!address.isDefault">
|
||||
<u-icon name="checkmark-circle" color="#999" size="40rpx"></u-icon>
|
||||
<text>设为默认</text>
|
||||
</view>
|
||||
@ -133,7 +123,7 @@ function selectAddress(address) {
|
||||
<u-icon name="edit-pen" color="#999" size="40rpx"></u-icon>
|
||||
<text>编辑</text>
|
||||
</view>
|
||||
<view class="action-btn" @tap="deleteAddress(address.id)">
|
||||
<view class="action-btn" @tap="handleDeleteAddress(address.id)">
|
||||
<u-icon name="trash" color="#999" size="40rpx"></u-icon>
|
||||
<text>删除</text>
|
||||
</view>
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import citySelect from '@/components/u-city-select/u-city-select.vue';
|
||||
|
||||
const height = ref(30);
|
||||
const bgColor = ref(uni.$u.color.bgColor);
|
||||
const marginTop = ref(30);
|
||||
const marginBottom = ref(30);
|
||||
const value = ref(false);
|
||||
const input = ref('');
|
||||
|
||||
|
||||
@ -126,29 +126,43 @@ const getComment = () => {
|
||||
.comment {
|
||||
display: flex;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.left {
|
||||
image {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #f2f2f2;
|
||||
border: 2rpx solid #eaeaea;
|
||||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 1;
|
||||
padding-left: 20rpx;
|
||||
font-size: 30rpx;
|
||||
padding-left: 24rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.6;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.name {
|
||||
color: #5677fc;
|
||||
font-weight: 500;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.like {
|
||||
@ -156,9 +170,16 @@ const getComment = () => {
|
||||
align-items: center;
|
||||
color: #9a9a9a;
|
||||
font-size: 26rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 30rpx;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
background-color: rgba(86, 119, 252, 0.1);
|
||||
}
|
||||
|
||||
.num {
|
||||
margin-right: 4rpx;
|
||||
margin-right: 8rpx;
|
||||
color: #9a9a9a;
|
||||
}
|
||||
}
|
||||
@ -173,20 +194,32 @@ const getComment = () => {
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-bottom: 10rpx;
|
||||
margin-bottom: 16rpx;
|
||||
color: #333333;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.reply-box {
|
||||
background-color: rgb(242, 242, 242);
|
||||
background-color: #f7f7f7;
|
||||
border-radius: 12rpx;
|
||||
margin-top: 12rpx;
|
||||
margin-bottom: 8rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.item {
|
||||
padding: 20rpx;
|
||||
border-bottom: solid 2rpx $u-border-color;
|
||||
border-bottom: solid 1rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.username {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
font-size: 26rpx;
|
||||
color: #5677fc;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,6 +228,12 @@ const getComment = () => {
|
||||
display: flex;
|
||||
color: #5677fc;
|
||||
align-items: center;
|
||||
font-size: 26rpx;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
background-color: rgba(86, 119, 252, 0.1);
|
||||
}
|
||||
|
||||
.more {
|
||||
margin-left: 6rpx;
|
||||
@ -207,10 +246,18 @@ const getComment = () => {
|
||||
display: flex;
|
||||
font-size: 24rpx;
|
||||
color: #9a9a9a;
|
||||
align-items: center;
|
||||
|
||||
.reply {
|
||||
color: #5677fc;
|
||||
margin-left: 10rpx;
|
||||
margin-left: 16rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 30rpx;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
background-color: rgba(86, 119, 252, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
197
src/pages_template/pages/order/OrderItem.vue
Normal file
197
src/pages_template/pages/order/OrderItem.vue
Normal file
@ -0,0 +1,197 @@
|
||||
<script setup>
|
||||
import { defineProps } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
order: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
// 价格小数
|
||||
const priceDecimal = (val) => {
|
||||
if (val !== parseInt(val)) return val.slice(-2);
|
||||
else return '00';
|
||||
};
|
||||
|
||||
// 价格整数
|
||||
const priceInt = (val) => {
|
||||
if (val !== parseInt(val)) return val.split('.')[0];
|
||||
else return val;
|
||||
};
|
||||
|
||||
// 总价
|
||||
const totalPrice = (item) => {
|
||||
let price = 0;
|
||||
item.forEach(val => {
|
||||
price += parseFloat(val.price);
|
||||
});
|
||||
return price.toFixed(2);
|
||||
};
|
||||
|
||||
// 总件数
|
||||
const totalNum = (item) => {
|
||||
let num = 0;
|
||||
item.forEach(val => {
|
||||
num += val.number;
|
||||
});
|
||||
return num;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="order">
|
||||
<view class="top">
|
||||
<view class="left">
|
||||
<u-icon name="home" :size="30" color="rgb(94,94,94)"></u-icon>
|
||||
<view class="store">{{ order.store }}</view>
|
||||
<u-icon name="arrow-right" color="rgb(203,203,203)" :size="26"></u-icon>
|
||||
</view>
|
||||
<view class="right">{{ order.deal }}</view>
|
||||
</view>
|
||||
<view class="item" v-for="(item, index) in order.goodsList" :key="index">
|
||||
<view class="left">
|
||||
<image :src="item.goodsUrl" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="title u-line-2">{{ item.title }}</view>
|
||||
<view class="type">{{ item.type }}</view>
|
||||
<view class="delivery-time">发货时间 {{ item.deliveryTime }}</view>
|
||||
</view>
|
||||
<view class="right">
|
||||
<view class="price">
|
||||
¥{{ priceInt(item.price) }}
|
||||
<text class="decimal">.{{ priceDecimal(item.price) }}</text>
|
||||
</view>
|
||||
<view class="number">x{{ item.number }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="total">
|
||||
共{{ totalNum(order.goodsList) }}件商品 合计:
|
||||
<text class="total-price">
|
||||
¥{{ priceInt(totalPrice(order.goodsList)) }}.
|
||||
<text class="decimal">{{ priceDecimal(totalPrice(order.goodsList)) }}</text>
|
||||
</text>
|
||||
</view>
|
||||
<view class="bottom">
|
||||
<view class="more"><u-icon name="more-dot-fill" color="rgb(203,203,203)"></u-icon></view>
|
||||
<view class="logistics btn">查看物流</view>
|
||||
<view class="exchange btn">卖了换钱</view>
|
||||
<view class="evaluate btn">评价</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order {
|
||||
width: 710rpx;
|
||||
background-color: #ffffff;
|
||||
margin: 20rpx auto;
|
||||
border-radius: 20rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx;
|
||||
font-size: 28rpx;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.store {
|
||||
margin: 0 10rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
color: $u-warning-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
margin: 20rpx 0 0;
|
||||
|
||||
.left {
|
||||
margin-right: 20rpx;
|
||||
|
||||
image {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
line-height: 50rpx;
|
||||
}
|
||||
|
||||
.type {
|
||||
margin: 10rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: $u-tips-color;
|
||||
}
|
||||
|
||||
.delivery-time {
|
||||
color: #e5d001;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-left: 10rpx;
|
||||
padding-top: 20rpx;
|
||||
text-align: right;
|
||||
|
||||
.decimal {
|
||||
font-size: 24rpx;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.number {
|
||||
color: $u-tips-color;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.total {
|
||||
margin-top: 20rpx;
|
||||
text-align: right;
|
||||
font-size: 24rpx;
|
||||
|
||||
.total-price {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
display: flex;
|
||||
margin-top: 40rpx;
|
||||
padding: 0 10rpx;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.btn {
|
||||
line-height: 52rpx;
|
||||
width: 160rpx;
|
||||
border-radius: 26rpx;
|
||||
border: 2rpx solid $u-border-color;
|
||||
font-size: 26rpx;
|
||||
text-align: center;
|
||||
color: $u-info-dark;
|
||||
}
|
||||
|
||||
.evaluate {
|
||||
color: $u-warning-dark;
|
||||
border-color: $u-warning-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,5 +1,6 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import OrderItem from './OrderItem.vue';
|
||||
|
||||
const orderList = ref([[], [], [], []]);
|
||||
const dataList = reactive([
|
||||
@ -119,18 +120,6 @@ onMounted(() => {
|
||||
getOrderList(3);
|
||||
});
|
||||
|
||||
// 价格小数
|
||||
const priceDecimal = (val) => {
|
||||
if (val !== parseInt(val)) return val.slice(-2);
|
||||
else return '00';
|
||||
};
|
||||
|
||||
// 价格整数
|
||||
const priceInt = (val) => {
|
||||
if (val !== parseInt(val)) return val.split('.')[0];
|
||||
else return val;
|
||||
};
|
||||
|
||||
// 触底懒加载
|
||||
const reachBottom = () => {
|
||||
loadStatus.value.splice(current.value, 1, "loading");
|
||||
@ -151,34 +140,21 @@ const getOrderList = (idx) => {
|
||||
loadStatus.value.splice(current.value, 1, "loadmore");
|
||||
};
|
||||
|
||||
// 总价
|
||||
const totalPrice = (item) => {
|
||||
let price = 0;
|
||||
item.forEach(val => {
|
||||
price += parseFloat(val.price);
|
||||
});
|
||||
return price.toFixed(2);
|
||||
};
|
||||
|
||||
// 总件数
|
||||
const totalNum = (item) => {
|
||||
let num = 0;
|
||||
item.forEach(val => {
|
||||
num += val.number;
|
||||
});
|
||||
return num;
|
||||
};
|
||||
|
||||
// tab栏切换
|
||||
const change = ({ index }) => {
|
||||
current.value = index; // 更新current变量
|
||||
swiperCurrent.value = index;
|
||||
getOrderList(index);
|
||||
};
|
||||
|
||||
const animationfinish = ({ detail: { current } }) => {
|
||||
swiperCurrent.value = current;
|
||||
current.value = current;
|
||||
const animationfinish = (e) => {
|
||||
const currentIndex = e.detail.current;
|
||||
swiperCurrent.value = currentIndex;
|
||||
current.value = currentIndex; // 将current的值正确更新为swiper的current
|
||||
};
|
||||
|
||||
// 获取tabs组件的ref
|
||||
const tabs = ref(null);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -193,62 +169,20 @@ const animationfinish = ({ detail: { current } }) => {
|
||||
<scroll-view scroll-y style="height: 100%;width: 100%;" @scrolltolower="reachBottom"
|
||||
v-if="orderlist.length !== 0">
|
||||
<view class="page-box">
|
||||
<view class="order" v-for="(res, index) in orderlist" :key="res.id">
|
||||
<view class="top">
|
||||
<view class="left">
|
||||
<u-icon name="home" :size="30" color="rgb(94,94,94)"></u-icon>
|
||||
<view class="store">{{ res.store }}</view>
|
||||
<u-icon name="arrow-right" color="rgb(203,203,203)" :size="26"></u-icon>
|
||||
</view>
|
||||
<view class="right">{{ res.deal }}</view>
|
||||
</view>
|
||||
<view class="item" v-for="(item, index) in res.goodsList" :key="index">
|
||||
<view class="left">
|
||||
<image :src="item.goodsUrl" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="title u-line-2">{{ item.title }}</view>
|
||||
<view class="type">{{ item.type }}</view>
|
||||
<view class="delivery-time">发货时间 {{ item.deliveryTime }}</view>
|
||||
</view>
|
||||
<view class="right">
|
||||
<view class="price">
|
||||
¥{{ priceInt(item.price) }}
|
||||
<text class="decimal">.{{ priceDecimal(item.price) }}</text>
|
||||
</view>
|
||||
<view class="number">x{{ item.number }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="total">
|
||||
共{{ totalNum(res.goodsList) }}件商品 合计:
|
||||
<text class="total-price">
|
||||
¥{{ priceInt(totalPrice(res.goodsList)) }}.
|
||||
<text class="decimal">{{ priceDecimal(totalPrice(res.goodsList)) }}</text>
|
||||
</text>
|
||||
</view>
|
||||
<view class="bottom">
|
||||
<view class="more"><u-icon name="more-dot-fill" color="rgb(203,203,203)"></u-icon>
|
||||
</view>
|
||||
<view class="logistics btn">查看物流</view>
|
||||
<view class="exchange btn">卖了换钱</view>
|
||||
<view class="evaluate btn">评价</view>
|
||||
</view>
|
||||
</view>
|
||||
<OrderItem v-for="res in orderlist" :key="res.id" :order="res" />
|
||||
<u-loadmore :status="loadStatus[0]" bgColor="#f2f2f2"></u-loadmore>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view scroll-y style="height: 100%;width: 100%;" v-else>
|
||||
<view class="page-box">
|
||||
<view>
|
||||
<view class="centre">
|
||||
<image src="https://cdn.uviewui.com/uview/template/taobao-order.png" mode="">
|
||||
</image>
|
||||
<view class="explain">
|
||||
您还没有相关的订单
|
||||
<view class="tips">可以去看看有那些想买的</view>
|
||||
</view>
|
||||
<view class="btn">随便逛逛</view>
|
||||
<view class="centre">
|
||||
<image src="https://cdn.uviewui.com/uview/template/taobao-order.png" mode="aspectFit" class="empty-image">
|
||||
</image>
|
||||
<view class="explain">
|
||||
您还没有相关的订单
|
||||
<view class="tips">可以去看看有那些想买的</view>
|
||||
</view>
|
||||
<view class="btn">随便逛逛</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
@ -268,128 +202,18 @@ page {
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order {
|
||||
width: 710rpx;
|
||||
background-color: #ffffff;
|
||||
margin: 20rpx auto;
|
||||
border-radius: 20rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx;
|
||||
font-size: 28rpx;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.store {
|
||||
margin: 0 10rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
color: $u-warning-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
margin: 20rpx 0 0;
|
||||
|
||||
.left {
|
||||
margin-right: 20rpx;
|
||||
|
||||
image {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
line-height: 50rpx;
|
||||
}
|
||||
|
||||
.type {
|
||||
margin: 10rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: $u-tips-color;
|
||||
}
|
||||
|
||||
.delivery-time {
|
||||
color: #e5d001;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-left: 10rpx;
|
||||
padding-top: 20rpx;
|
||||
text-align: right;
|
||||
|
||||
.decimal {
|
||||
font-size: 24rpx;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.number {
|
||||
color: $u-tips-color;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.total {
|
||||
margin-top: 20rpx;
|
||||
text-align: right;
|
||||
font-size: 24rpx;
|
||||
|
||||
.total-price {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
display: flex;
|
||||
margin-top: 40rpx;
|
||||
padding: 0 10rpx;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.btn {
|
||||
line-height: 52rpx;
|
||||
width: 160rpx;
|
||||
border-radius: 26rpx;
|
||||
border: 2rpx solid $u-border-color;
|
||||
font-size: 26rpx;
|
||||
text-align: center;
|
||||
color: $u-info-dark;
|
||||
}
|
||||
|
||||
.evaluate {
|
||||
color: $u-warning-dark;
|
||||
border-color: $u-warning-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.centre {
|
||||
text-align: center;
|
||||
margin: 200rpx auto;
|
||||
font-size: 32rpx;
|
||||
width: 100%;
|
||||
|
||||
image {
|
||||
.empty-image {
|
||||
width: 164rpx;
|
||||
height: 164rpx;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 20rpx;
|
||||
margin: 0 auto 20rpx;
|
||||
display: block; /* 确保图片作为块级元素 */
|
||||
}
|
||||
|
||||
.tips {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user