1
175
src/pages/client/address/components/AddressItem.vue
Normal file
@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<uni-swipe-action :key="data.id" class="address-item-container">
|
||||
<uni-swipe-action-item
|
||||
class="address-item-content"
|
||||
:right-options="{}"
|
||||
:auto-close="false"
|
||||
@click="$emit('jumpToDetails', data)"
|
||||
>
|
||||
<view
|
||||
class="flex-row-start address-item"
|
||||
>
|
||||
<view class="address-item-clickable" @click="$emit('toDetails', data)">
|
||||
<view class="address-content">
|
||||
<view class="flex-row-start">
|
||||
<text
|
||||
class="fs-20 PingFangSC-Regular default-tip"
|
||||
v-if="data.is_default"
|
||||
>默认</text
|
||||
>
|
||||
<text class="fs-30 app-fc-main PingFangSC-Medium address-user">
|
||||
{{ data.name || "" }}
|
||||
</text>
|
||||
<text class="fs-30 app-fc-main PingFangSC-Medium">
|
||||
{{ data.phone || "" }}
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
class="app-fc-normal fs-26 PingFangSC-Regular app-text-ellipse address-name"
|
||||
>
|
||||
{{ data.address || "" }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="address-right-content">
|
||||
<view class="switch-container" @tap.stop="handleSwitchClick">
|
||||
<switch
|
||||
:checked="data.is_default"
|
||||
@change="onDefaultChange"
|
||||
color="#FF19A0"
|
||||
class="default-switch"
|
||||
/>
|
||||
</view>
|
||||
<image
|
||||
class="address-edit-icon"
|
||||
src="@/static/images/address_edit.png"
|
||||
@click.stop="$emit('toDetails', data)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view slot="right" class="flex-center address-right">
|
||||
<image
|
||||
class="address-delete"
|
||||
src="@/static/images/mine_delete.png"
|
||||
@click.stop="$emit('delete', data)"
|
||||
/>
|
||||
</view>
|
||||
</uni-swipe-action-item>
|
||||
</uni-swipe-action>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
handleSwitchClick(e) {
|
||||
console.log('handleSwitchClick 触发');
|
||||
// 手动触发开关切换
|
||||
const newValue = !this.data.is_default;
|
||||
this.emitSetDefault(newValue);
|
||||
},
|
||||
onDefaultChange(e) {
|
||||
console.log('onDefaultChange 触发:', e, e.detail, this.data);
|
||||
// 阻止事件冒泡到父元素,避免触发编辑
|
||||
if (e && e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
// 获取新的开关状态
|
||||
const newValue = e.detail?.value !== undefined ? e.detail.value : (e.detail?.checked !== undefined ? e.detail.checked : !this.data.is_default);
|
||||
console.log('新的开关状态:', newValue);
|
||||
this.emitSetDefault(newValue);
|
||||
},
|
||||
emitSetDefault(newValue) {
|
||||
// 发送设置默认地址事件
|
||||
const newData = {
|
||||
...this.data,
|
||||
is_default: newValue ? 1 : 0
|
||||
};
|
||||
console.log('发送 setDefault 事件:', newData);
|
||||
this.$emit('setDefault', newData);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.address-item-container {
|
||||
|
||||
.address-item {
|
||||
width: calc(100vw - 32rpx * 2);
|
||||
padding: 40rpx 30rpx;
|
||||
background: #fff;
|
||||
border-radius: 30rpx 30rpx 30rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20rpx;
|
||||
margin-left: 32rpx;
|
||||
margin-right: 32rpx;
|
||||
|
||||
.address-right-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.switch-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10rpx;
|
||||
margin: -10rpx;
|
||||
}
|
||||
|
||||
.default-switch {
|
||||
transform: scale(0.8);
|
||||
transform-origin: right center;
|
||||
}
|
||||
|
||||
.address-edit-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.address-item-clickable {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.address-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.default-tip {
|
||||
padding: 6rpx;
|
||||
color: #40ae36;
|
||||
background: rgba(64, 174, 54, 0.2);
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.address-name {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
||||
.address-user {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.address-right {
|
||||
padding: 0 32rpx 0 0;
|
||||
.address-delete {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
407
src/pages/client/address/edit.vue
Normal file
@ -0,0 +1,407 @@
|
||||
<template>
|
||||
<view class="address-edit">
|
||||
<view class="edit-form">
|
||||
<form-cell
|
||||
title="姓名"
|
||||
placeholderText="请输入姓名"
|
||||
:value="addressInfo.name"
|
||||
:showRightArrow="false"
|
||||
@onChange="(value) => onChange(value, 'name')"
|
||||
/>
|
||||
<form-cell
|
||||
title="手机号"
|
||||
placeholderText="请输入手机号"
|
||||
:value="addressInfo.phone"
|
||||
:showRightArrow="false"
|
||||
@onChange="(value) => onChange(value, 'phone')"
|
||||
/>
|
||||
<form-cell
|
||||
title="省市区"
|
||||
type="checkAddress"
|
||||
:value="regionText"
|
||||
:defaultRegion="[provinceIndex, cityIndex, areaIndex]"
|
||||
@onChange="onRegionChange"
|
||||
/>
|
||||
<form-cell
|
||||
title="详细地址"
|
||||
placeholderText="请输入详细地址"
|
||||
:value="addressInfo.address"
|
||||
:showRightArrow="false"
|
||||
:noBorder="true"
|
||||
@onChange="(value) => onChange(value, 'address')"
|
||||
/>
|
||||
</view>
|
||||
<view class="edit-form default">
|
||||
<form-cell
|
||||
title="设为默认地址"
|
||||
type="custom"
|
||||
class="default-wrapper"
|
||||
:showRightArrow="false"
|
||||
:noBorder="true"
|
||||
>
|
||||
<view class="flex-row-end default-cell" slot="right">
|
||||
<switch
|
||||
:checked="isDefault"
|
||||
@change="onSwitchChange"
|
||||
color="#FF19A0"
|
||||
class="default-switch"
|
||||
/>
|
||||
</view>
|
||||
</form-cell>
|
||||
</view>
|
||||
|
||||
<view class="flex-center edit-bottom">
|
||||
<view
|
||||
class="flex-center PingFangSC-Semibold fs-30 confirm-btn"
|
||||
@click="save"
|
||||
>
|
||||
保存
|
||||
</view>
|
||||
<view class="bottom-safe-area"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormCell from "@/components/FormCell.vue";
|
||||
import { createAddress, updateAddress, getAddressInfo } from "../../../api/address";
|
||||
import { pcaData } from "@/api/areas.js";
|
||||
import appConfig from "../../../constants/app.config";
|
||||
const QQMapWX = require("./static/qqmap-wx-jssdk.min.js");
|
||||
var qqmapsdk;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FormCell,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
addressInfo: {
|
||||
address_id: 0,
|
||||
name: "",
|
||||
phone: "",
|
||||
address: "",
|
||||
province: "",
|
||||
provinceId: "",
|
||||
city: "",
|
||||
cityId: "",
|
||||
area: "",
|
||||
areaId: "",
|
||||
},
|
||||
isDefault: false,
|
||||
provinceIndex: 0,
|
||||
cityIndex: 0,
|
||||
areaIndex: 0,
|
||||
regionList: pcaData,
|
||||
isAdd: true,
|
||||
id: 0,
|
||||
lastSaveTime: 0, // 上次保存时间戳
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
userInfo() {
|
||||
return this.$store.state?.user?.userInfo || {};
|
||||
},
|
||||
regionText() {
|
||||
const parts = [
|
||||
this.addressInfo.province,
|
||||
this.addressInfo.city,
|
||||
this.addressInfo.area
|
||||
].filter(item => item && item.trim());
|
||||
return parts.join(' ');
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"addressInfo.provinceId": {
|
||||
async handler(val) {
|
||||
this.provinceIndex = this.regionList.findIndex(
|
||||
(v) => v.area_id === val
|
||||
);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
"addressInfo.cityId": {
|
||||
handler(val) {
|
||||
const citytList = this.regionList?.[this.provinceIndex]?.children || [];
|
||||
this.cityIndex = citytList.findIndex((v) => v.area_id === val);
|
||||
},
|
||||
},
|
||||
"addressInfo.areaId": {
|
||||
handler(val) {
|
||||
const citytList = this.regionList?.[this.provinceIndex]?.children || [];
|
||||
const areaList = citytList?.[this.cityIndex]?.children || [];
|
||||
this.areaIndex = areaList.findIndex((v) => v.area_id === val);
|
||||
},
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
const { isAdd, id } = options;
|
||||
this.isAdd = !!isAdd;
|
||||
if (id) {
|
||||
this.id = id;
|
||||
this.addressInfo.address_id = id;
|
||||
this.getInfo(id);
|
||||
}
|
||||
qqmapsdk = new QQMapWX({
|
||||
key: appConfig.tencentMapKey,
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
onChange(value, key) {
|
||||
this.addressInfo[key] = value;
|
||||
},
|
||||
onSwitchChange(e) {
|
||||
this.isDefault = e.detail.value;
|
||||
},
|
||||
onRegionChange(data) {
|
||||
const [province, city, area] = data;
|
||||
this.addressInfo.province = province.label;
|
||||
this.addressInfo.provinceId = province.value;
|
||||
this.addressInfo.city = city.label;
|
||||
this.addressInfo.cityId = city.value;
|
||||
this.addressInfo.area = area.label;
|
||||
this.addressInfo.areaId = area.value;
|
||||
},
|
||||
getInfo(id) {
|
||||
uni.showLoading({ title: "加载中..." });
|
||||
getAddressInfo(id).then((res) => {
|
||||
uni.hideLoading();
|
||||
const info = res.info || res.data || res;
|
||||
|
||||
// 填充地址信息
|
||||
this.addressInfo.address_id = info.address_id || info.id || id;
|
||||
this.addressInfo.name = info.name || info.recipient_name || '';
|
||||
this.addressInfo.phone = info.phone || '';
|
||||
this.addressInfo.address = info.address || info.full_address || '';
|
||||
this.addressInfo.province = info.province || info.sheng || '';
|
||||
this.addressInfo.provinceId = info.province_id;
|
||||
this.addressInfo.city = info.city || info.shi || '';
|
||||
this.addressInfo.cityId = info.city_id;
|
||||
this.addressInfo.area = info.district || info.area || info.qu || '';
|
||||
this.addressInfo.areaId = info.district_id;
|
||||
|
||||
// 设置默认地址状态
|
||||
this.isDefault = !!info.is_default || !!info.isDefault;
|
||||
|
||||
// 设置省市区索引(用于地区选择器)
|
||||
if (this.addressInfo.provinceId) {
|
||||
this.provinceIndex = this.regionList.findIndex(
|
||||
(v) => v.area_id === this.addressInfo.provinceId
|
||||
);
|
||||
}
|
||||
if (this.addressInfo.cityId && this.provinceIndex >= 0) {
|
||||
const cityList = this.regionList[this.provinceIndex]?.children || [];
|
||||
this.cityIndex = cityList.findIndex(
|
||||
(v) => v.area_id === this.addressInfo.cityId
|
||||
);
|
||||
}
|
||||
if (this.addressInfo.areaId && this.cityIndex >= 0) {
|
||||
const cityList = this.regionList[this.provinceIndex]?.children || [];
|
||||
const areaList = cityList[this.cityIndex]?.children || [];
|
||||
this.areaIndex = areaList.findIndex(
|
||||
(v) => v.area_id === this.addressInfo.areaId
|
||||
);
|
||||
}
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
console.error('获取地址详情失败:', err);
|
||||
uni.showToast({
|
||||
title: '获取地址信息失败',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
},
|
||||
// 获取经纬度
|
||||
getLatLotData() {},
|
||||
save() {
|
||||
// 防抖处理:1秒内只能调用1次接口
|
||||
const now = Date.now();
|
||||
if (now - this.lastSaveTime < 1000) {
|
||||
uni.showToast({
|
||||
title: '操作过于频繁,请稍后再试',
|
||||
icon: 'none',
|
||||
duration: 1500
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新上次保存时间
|
||||
this.lastSaveTime = now;
|
||||
|
||||
const {
|
||||
province = "",
|
||||
city = "",
|
||||
area = "",
|
||||
address = "",
|
||||
} = this.addressInfo;
|
||||
// const addressText = province + city + area + address
|
||||
// uni.request({
|
||||
// url: `https://apis.map.qq.com/ws/geocoder/v1/?address=${addressText}&key=${appConfig.tencentMapKey}`,
|
||||
// method: 'GET',
|
||||
// success: (res) => {
|
||||
// console.log('地址转经纬度----->', res?.data)
|
||||
// }
|
||||
// })
|
||||
|
||||
qqmapsdk.geocoder({
|
||||
address: province + city + area + address,
|
||||
sig: appConfig.tencentSecret,
|
||||
success: (res) => {
|
||||
console.log("解析地址成功----->", res);
|
||||
const { lat, lng } = res?.result?.location || {};
|
||||
this.editAddress(lat, lng);
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error("解析地址失败----->", error);
|
||||
this.editAddress();
|
||||
},
|
||||
});
|
||||
return;
|
||||
},
|
||||
|
||||
editAddress(l) {
|
||||
const addressParam = {
|
||||
user_id: this.userInfo.userID,
|
||||
// address_id: this.addressInfo.address_id,
|
||||
recipient_name: this.addressInfo.name.trim(),
|
||||
phone: this.addressInfo.phone.trim(),
|
||||
is_default: this.isDefault,
|
||||
province: this.addressInfo.province,
|
||||
province_id: this.addressInfo.provinceId,
|
||||
city: this.addressInfo.city,
|
||||
city_id: this.addressInfo.cityId,
|
||||
district: this.addressInfo.area,
|
||||
district_id: this.addressInfo.areaId,
|
||||
full_address: this.addressInfo.address.trim(),
|
||||
region_id: 1
|
||||
};
|
||||
console.log("addressParam", addressParam);
|
||||
|
||||
const error = Object.keys(addressParam).some((v) => {
|
||||
if (!addressParam[v] && v !== "address_id" && v !== "is_default") {
|
||||
if (v === "sheng_id") {
|
||||
uni.showToast({ icon: "none", title: "请选择省市区" });
|
||||
return true;
|
||||
}
|
||||
if (v === "phone") {
|
||||
uni.showToast({ icon: "none", title: "请输入手机号" });
|
||||
return true;
|
||||
}
|
||||
if (v === "name") {
|
||||
uni.showToast({ icon: "none", title: "请输入姓名" });
|
||||
return true;
|
||||
}
|
||||
if (v === "address") {
|
||||
uni.showToast({ icon: "none", title: "请输入详细地址" });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (v === "phone" && !/^1[0-9]{10}$/.test(addressParam[v])) {
|
||||
uni.showToast({ icon: "none", title: "手机号有误" });
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是编辑模式,需要添加 address_id
|
||||
if (!this.isAdd && this.addressInfo.address_id) {
|
||||
addressParam.id = this.addressInfo.address_id;
|
||||
}
|
||||
|
||||
uni.showLoading({ title: "处理中..." });
|
||||
|
||||
// 根据 isAdd 标志决定调用创建还是更新接口
|
||||
const savePromise = this.isAdd
|
||||
? createAddress(addressParam)
|
||||
: updateAddress(addressParam);
|
||||
|
||||
savePromise.then((r) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ icon: "success", title: "保存成功" });
|
||||
// const eventChannel = this.getOpenerEventChannel();
|
||||
// eventChannel.emit("refreshAddress");
|
||||
uni.navigateBack();
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
console.error('保存地址失败:', err);
|
||||
uni.showToast({
|
||||
title: err?.msg || err?.message || '保存失败,请稍后重试',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.address-edit {
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #F7F8FA;
|
||||
padding: 20rpx 20rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.edit-form {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&.default {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.default-wrapper {
|
||||
::v-deep {
|
||||
.form-cell .form-label {
|
||||
color: #3D3D3D;
|
||||
}
|
||||
}
|
||||
|
||||
.default-cell {
|
||||
width: 100%;
|
||||
|
||||
.default-switch {
|
||||
transform: scale(0.8);
|
||||
transform-origin: right center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.edit-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 32rpx 32rpx 0 0;
|
||||
padding: 24rpx 24rpx 0;
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||
box-sizing: border-box;
|
||||
|
||||
.confirm-btn {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
margin-bottom: 24rpx;
|
||||
border-radius: 100rpx;
|
||||
background: #FF19A0;
|
||||
color: #fff;
|
||||
padding: 32rpx 0rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.bottom-safe-area {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
160
src/pages/client/address/index.vue
Normal file
@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<view class="address-container">
|
||||
<list-page-temp
|
||||
class="address-list-inner"
|
||||
:getDataPromise="getAddressList"
|
||||
:reloadFlag="reloadFlag"
|
||||
:requestData="requestData"
|
||||
>
|
||||
<template v-slot:item="{ data }">
|
||||
<address-item
|
||||
:data="data"
|
||||
@toDetails="toDetails(data)"
|
||||
@delete="deleteAction"
|
||||
@refresh="handleRefresh"
|
||||
/>
|
||||
</template>
|
||||
<view slot="bottom" class="place-view"></view>
|
||||
</list-page-temp>
|
||||
|
||||
<view class="flex-center address-list-bottom">
|
||||
<view class="flex-row-center address-add" @click="jumpToAdd">
|
||||
<text class="fs-60 app-fc-white PingFangSC-Semibold add-icon">+</text>
|
||||
<text class="fs-30 app-fc-white PingFangSC-Semibold add-icon">
|
||||
添加地址
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<pop-up-modal
|
||||
v-if="showModal"
|
||||
content="确定要删除该地址信息吗?"
|
||||
@confirm="deleteAddress(data)"
|
||||
@cancel="showModal = false"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AddressItem from "./components/AddressItem.vue";
|
||||
import PopUpModal from "../../../components/PopUpModal.vue";
|
||||
import { delAddress, getAddressList } from "../../../api/address";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AddressItem,
|
||||
PopUpModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
reloadFlag: 0,
|
||||
requestData: {},
|
||||
deleteData: {},
|
||||
showModal: false,
|
||||
isSelectAddress: false, // 从订单页跳转过来选择地址的标识
|
||||
selectAddressId: "", // 选中的地址id
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
userInfo() {
|
||||
return this.$store.state?.user?.userInfo || {};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
userInfo: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
if (newVal && newVal.userID) {
|
||||
this.requestData = {
|
||||
user_id: newVal.userID
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
onLoad(options) {
|
||||
const { typeSelect, addressId } = options;
|
||||
this.isSelectAddress = typeSelect;
|
||||
this.selectAddressId = addressId;
|
||||
},
|
||||
methods: {
|
||||
getAddressList,
|
||||
toDetails(data) {
|
||||
if (this.isSelectAddress) {
|
||||
this.selectAddressId = data?.id || "";
|
||||
uni.navigateBack();
|
||||
uni.$emit('selectAddress', data)
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/address/edit?id=${data?.id || ""}`,
|
||||
});
|
||||
},
|
||||
jumpToAdd() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/address/edit?isAdd=1`,
|
||||
});
|
||||
},
|
||||
deleteAction(data) {
|
||||
this.deleteData = data;
|
||||
this.showModal = true;
|
||||
},
|
||||
deleteAddress() {
|
||||
this.showModal = false;
|
||||
console.log("this.deleteData", this.deleteData);
|
||||
delAddress(this.deleteData.id).then(() => {
|
||||
this.reloadFlag = Math.random();
|
||||
});
|
||||
},
|
||||
handleRefresh() {
|
||||
// 刷新地址列表
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.address-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #fbf8fc;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
padding-top: 20rpx;
|
||||
|
||||
.address-list-inner {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.place-view {
|
||||
height: 90rpx;
|
||||
}
|
||||
|
||||
.address-list-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
bottom: constant(safe-area-inset-bottom);
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
.address-add {
|
||||
width: 630rpx;
|
||||
height: 90rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 45rpx 45rpx 45rpx 45rpx;
|
||||
|
||||
.add-icon {
|
||||
margin-right: 20rpx;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1
src/pages/client/address/static/qqmap-wx-jssdk.min.js
vendored
Normal file
395
src/pages/client/auth/index.vue
Normal file
@ -0,0 +1,395 @@
|
||||
<template>
|
||||
<view class="loginContainer">
|
||||
<view class="body">
|
||||
<image class="login-ground-img" :src="`${imgPrefix}loginGroundImg.png`" mode="widthFix" />
|
||||
<view class="btnConent">
|
||||
<button v-show="!checked" class="loginBtn" @click="unCheckAndGetPhoneNumber">
|
||||
手机快捷登录
|
||||
</button>
|
||||
<button v-show="checked" class="loginBtn" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
|
||||
手机快捷登录
|
||||
</button>
|
||||
<view class="notLoginBtn" @click="goBack">
|
||||
暂不登录
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer">
|
||||
<view class="radioWrapper">
|
||||
<view class="checkbox-wrapper" @click.stop="changeChecked">
|
||||
<view class="checkbox" :class="{ 'checkbox-checked': checked }">
|
||||
<image v-if="checked" class="check-icon" :src="require('@/static/images/y.png')" />
|
||||
</view>
|
||||
<!-- <view v-if="uncheckMessageDialog" class="tooltip">
|
||||
<view class="tooltip-content">请先勾选,同意后再进行登录</view>
|
||||
<view class="tooltip-arrow"></view>
|
||||
</view> -->
|
||||
</view>
|
||||
<text class="radioText">
|
||||
请阅读并同意
|
||||
<text class="color" @click.stop="ptfwxy">《帮宠到家平台服务协议》</text>
|
||||
<text class="color" @click.stop="ysxy">《隐私协议》</text>
|
||||
</text>
|
||||
</view>
|
||||
<view class="bottom-safe-area" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import appConfig from "@/constants/app.config";
|
||||
import { login, getPhone } from "@/api/login";
|
||||
import { imgPrefix } from "@/utils/common";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
appConfig,
|
||||
imgPrefix,
|
||||
checked: false,
|
||||
uncheckMessageDialog: false,
|
||||
};
|
||||
},
|
||||
mounted() { },
|
||||
methods: {
|
||||
ptfwxy() {
|
||||
this.jumpTo("/pages/richText/index?code=ptfwxy");
|
||||
},
|
||||
ysxy() {
|
||||
this.jumpTo("/pages/richText/index?code=ysxy");
|
||||
},
|
||||
goBack() {
|
||||
this.loginAction();
|
||||
},
|
||||
jumpTo(url) {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
});
|
||||
},
|
||||
changeChecked() {
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: ['QoTeQwj4xw2UQMK5jI67MzAVOo6og76oqZ7BDIJW7cE', 'GPWlTkaNbi7JqvxltLKuZZMtKedSZfEKlirV7yOUu-0'],// TEMPLATE_ID替换为选用的模版id
|
||||
success(res) {
|
||||
uni.hideLoading()
|
||||
if (res['QoTeQwj4xw2UQMK5jI67MzAVOo6og76oqZ7BDIJW7cE'] === 'accept') {
|
||||
// setSubscribeStatus(true, '已订阅')
|
||||
uni.showToast({
|
||||
title: '订阅成功',
|
||||
icon: 'success',
|
||||
})
|
||||
}
|
||||
if (res['GPWlTkaNbi7JqvxltLKuZZMtKedSZfEKlirV7yOUu-0'] === 'accept') {
|
||||
// setSubscribeStatus(true, '已订阅')
|
||||
uni.showToast({
|
||||
title: '订阅成功',
|
||||
icon: 'success',
|
||||
})
|
||||
}
|
||||
},
|
||||
fail() {
|
||||
uni.showToast({
|
||||
title: '订阅失败',
|
||||
icon: 'error',
|
||||
})
|
||||
},
|
||||
complete: () => {
|
||||
// isSubscribing = false
|
||||
},
|
||||
})
|
||||
this.checked = !this.checked;
|
||||
},
|
||||
unCheckAndGetPhoneNumber() {
|
||||
uni.showToast({
|
||||
title: '请先勾选,同意后再进行登录',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
},
|
||||
async getPhoneNumber(e) {
|
||||
// console.log(e,'?')
|
||||
// return
|
||||
// 检查是否有 code
|
||||
if (!e.detail.code) {
|
||||
uni.showToast({
|
||||
title: "获取手机号失败,请重试",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uni.showLoading({
|
||||
title: "获取手机号中...",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
});
|
||||
|
||||
try {
|
||||
// 登录前先记录是否有邀请码(referrerID),用于登录后导航判断
|
||||
const hasReferrer =
|
||||
this.$store.state?.user && this.$store.state.user.referrerID;
|
||||
// 拿着code获取手机号
|
||||
const codeRes = await getPhone(e.detail.code);
|
||||
const phone = codeRes?.data?.phoneNumber || "";
|
||||
|
||||
if (!phone) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "获取手机号失败,请重试",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uni.showLoading({
|
||||
title: "登录中...",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
});
|
||||
|
||||
// 手机号登录
|
||||
login(phone)
|
||||
.then((res) => {
|
||||
uni.hideLoading();
|
||||
this.$store.dispatch("user/setToken", res?.data?.token || "");
|
||||
this.$store.dispatch("user/setUserInfo", res?.data || {});
|
||||
|
||||
// 如果是带邀请码(referrerID)的登录,统一跳转到首页
|
||||
if (hasReferrer) {
|
||||
uni.reLaunch({
|
||||
url: "/pages/client/index/index",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var pages = getCurrentPages();
|
||||
if (pages.length === 1) {
|
||||
uni.reLaunch({
|
||||
url: "/pages/client/index/index",
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateBack();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: err || "登录失败,请重试",
|
||||
icon: "none",
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error("获取手机号失败:", error);
|
||||
// 检查是否是 access_token 相关错误
|
||||
const errorMsg = error?.message || error || "";
|
||||
if (errorMsg.includes("access_token") || errorMsg.includes("invalid credential")) {
|
||||
uni.showToast({
|
||||
title: "服务暂时不可用,请稍后重试",
|
||||
icon: "none",
|
||||
duration: 3000,
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: errorMsg || "获取手机号失败,请重试",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
loginAction() {
|
||||
if (!this.checked) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请先勾选,同意后再进行登录',
|
||||
confirmText: '确认',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 自动勾选协议
|
||||
this.checked = true;
|
||||
// 继续执行登录
|
||||
this.loginAction();
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 登录前先记录是否有邀请码(referrerID),用于登录后导航判断
|
||||
const hasReferrer =
|
||||
this.$store.state?.user && this.$store.state.user.referrerID;
|
||||
|
||||
uni.showLoading({
|
||||
title: "加载中...",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
});
|
||||
login()
|
||||
.then((res) => {
|
||||
this.$store.dispatch("user/setToken", res?.data?.token || "");
|
||||
this.$store.dispatch("user/setUserInfo", res?.data || {});
|
||||
|
||||
// 如果是带邀请码(referrerID)的登录,统一跳转到首页
|
||||
if (hasReferrer) {
|
||||
uni.reLaunch({
|
||||
url: "/pages/client/index/index",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var pages = getCurrentPages();
|
||||
if (pages.length === 1) {
|
||||
uni.reLaunch({
|
||||
url: "/pages/client/index/index",
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateBack();
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.loginContainer {
|
||||
height: 100vh;
|
||||
background-color: #f7f8fa;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-top: 20rpx;
|
||||
|
||||
.body {
|
||||
background-color: #fff;
|
||||
padding: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
margin: 0 20rpx;
|
||||
|
||||
.login-ground-img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btnConent {
|
||||
padding: 0rpx 20rpx;
|
||||
padding-bottom: 100rpx;
|
||||
margin-top: 30rpx;
|
||||
|
||||
.loginBtn {
|
||||
color: #fff;
|
||||
background-color: #ff19a0;
|
||||
border-radius: 300rpx;
|
||||
padding: 26rpx 0rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
border: none;
|
||||
width: 100%;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.notLoginBtn {
|
||||
background-color: #fff;
|
||||
color: #FF19A0;
|
||||
border: 1rpx solid #FF19A0;
|
||||
border-radius: 300rpx;
|
||||
padding: 26rpx 0rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
margin-top: 60rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
.radioWrapper {
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
.checkbox-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 8rpx;
|
||||
|
||||
.checkbox {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #999999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #FFFFFF;
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.checkbox-checked {
|
||||
border-color: #FF19A0;
|
||||
background: #FF19A0;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
margin-bottom: 12rpx;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
border-radius: 12rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
white-space: nowrap;
|
||||
z-index: 100;
|
||||
|
||||
.tooltip-content {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.tooltip-arrow {
|
||||
position: absolute;
|
||||
bottom: -12rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 12rpx solid transparent;
|
||||
border-right: 12rpx solid transparent;
|
||||
border-top: 12rpx solid rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radioText {
|
||||
font-size: 22rpx;
|
||||
color: #9b939a;
|
||||
|
||||
.color {
|
||||
color: #FF19A0;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-safe-area {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
224
src/pages/client/butler/edit.vue
Normal file
@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<view class="butler-edit-container">
|
||||
<view class="edit-content">
|
||||
<view class="edit-top">
|
||||
<image class="edit-bg" src="https://activity.wagoo.live/butler.png" />
|
||||
<view class="top-inner">
|
||||
<view class="fs-44 app-font-bold app-fc-mark"> 申请成为小哇</view>
|
||||
<view class="fs-28 top-text"> 期待您的加入 </view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="edit-inner">
|
||||
<view class="flex-row-start edit-cell">
|
||||
<text class="fs-32 app-fc-main cell-title">姓名</text>
|
||||
<input
|
||||
class="fs-32 app-fc-main cell-input"
|
||||
placeholder-class="fs-32 cell-placeholder"
|
||||
placeholder="请输入姓名"
|
||||
:value="butlerApplyInfo.name"
|
||||
@input="nameChange"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="flex-row-start edit-cell">
|
||||
<text class="fs-32 app-fc-main cell-title">手机号</text>
|
||||
<input
|
||||
class="fs-32 app-fc-main cell-input"
|
||||
placeholder-class="fs-32 cell-placeholder"
|
||||
placeholder="请输入手机号"
|
||||
type="number"
|
||||
:value="butlerApplyInfo.phone"
|
||||
@input="phoneChange"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="fs-30 flex-center app-fc-white request-btn"
|
||||
@click="submit"
|
||||
>
|
||||
立即申请
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<success-modal
|
||||
v-if="showModal"
|
||||
@ok="confirmModal"
|
||||
:title="
|
||||
butlerApplyInfo.status === 1
|
||||
? '申请处理中'
|
||||
: butlerApplyInfo.status === 2
|
||||
? '您已申请成功'
|
||||
: butlerApplyInfo.status === 3
|
||||
? '您已提交申请'
|
||||
: '提交成功'
|
||||
"
|
||||
:message="
|
||||
butlerApplyInfo.status === 1
|
||||
? '请耐心等待工作人员联系'
|
||||
: butlerApplyInfo.status === 2
|
||||
? ''
|
||||
: butlerApplyInfo.status === 3
|
||||
? '审核失败'
|
||||
: '请耐心等待工作人员联系'
|
||||
"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { applyKeeper, applyKeeperDetail } from "@/api/user";
|
||||
import SuccessModal from "@/components/SuccessModal.vue";
|
||||
|
||||
export default {
|
||||
components: { SuccessModal },
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
butlerApplyInfo: {
|
||||
name: "",
|
||||
phone: "",
|
||||
status: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
comments: {
|
||||
SuccessModal,
|
||||
},
|
||||
mounted() {
|
||||
this.getApplyKeeperDetails();
|
||||
},
|
||||
methods: {
|
||||
// 获取管家信息
|
||||
getApplyKeeperDetails() {
|
||||
applyKeeperDetail().then((res) => {
|
||||
this.butlerApplyInfo = res?.info || {};
|
||||
// 1.已提交 2.已处理
|
||||
if (
|
||||
this.butlerApplyInfo.status === 1 ||
|
||||
this.butlerApplyInfo.status === 2
|
||||
) {
|
||||
this.showModal = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
nameChange(e) {
|
||||
this.butlerApplyInfo.name = e.detail.value?.trim?.() || "";
|
||||
},
|
||||
phoneChange(e) {
|
||||
this.butlerApplyInfo.phone = e.detail.value?.trim?.() || "";
|
||||
},
|
||||
confirmModal() {
|
||||
this.showModal = false;
|
||||
uni.navigateBack();
|
||||
},
|
||||
submit() {
|
||||
if (!this.butlerApplyInfo.name) {
|
||||
uni.showToast({
|
||||
title: "请填写姓名",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!this.butlerApplyInfo.phone) {
|
||||
uni.showToast({
|
||||
title: "请填写手机号",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!/^1[0-9]{10}$/.test(this.butlerApplyInfo.phone)) {
|
||||
uni.showToast({
|
||||
title: "手机号有误",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.showLoading({
|
||||
title: "处理中",
|
||||
ico: "none",
|
||||
mask: true,
|
||||
});
|
||||
applyKeeper({
|
||||
name: this.butlerApplyInfo.name,
|
||||
phone: this.butlerApplyInfo.phone,
|
||||
}).then((res) => {
|
||||
uni.hideLoading();
|
||||
this.showModal = true;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.butler-edit-container {
|
||||
height: 100%;
|
||||
background: #fbf8fc;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.edit-top {
|
||||
width: 100%;
|
||||
height: 180rpx;
|
||||
position: relative;
|
||||
|
||||
.edit-bg {
|
||||
width: 100%;
|
||||
height: 180rpx;
|
||||
}
|
||||
|
||||
.top-inner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 36rpx 0 36rpx 100rpx;
|
||||
|
||||
.top-text {
|
||||
color: #3d3d3d;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.edit-content {
|
||||
background: #fff;
|
||||
border-radius: 30rpx;
|
||||
|
||||
.edit-inner {
|
||||
padding: 40rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.edit-cell {
|
||||
box-sizing: border-box;
|
||||
padding: 0 44rpx;
|
||||
height: 92rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 92rpx;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.cell-title {
|
||||
width: 140rpx;
|
||||
}
|
||||
.cell-input {
|
||||
flex: 1;
|
||||
}
|
||||
.cell-placeholder {
|
||||
color: #acacac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.request-btn {
|
||||
width: 360rpx;
|
||||
height: 90rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 90rpx;
|
||||
margin: 60rpx auto 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
300
src/pages/client/cart/components/CartItem.vue
Normal file
@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<view class="flex-row-start cart-item" @click.stop="jumpToDetails">
|
||||
<view class="flex-center cart-icon-wrapper">
|
||||
<image class="cart-icon" :src="
|
||||
(data.mode === 'pay' && data.is_select === 1) ||
|
||||
(data.mode === 'delete' && data.deleteSelect)
|
||||
? require('@/static/images/cart_checked.png')
|
||||
: require('@/static/images/unchecked.png')
|
||||
" @click.stop="selectAction" />
|
||||
</view>
|
||||
<view class="flex-row-start cart-inner">
|
||||
<image class="goods-img" :src="data.product_pic" mode="aspectFill" />
|
||||
<view class="goods-content">
|
||||
<view>
|
||||
<view class="text-multi-ellipse fs-24 app-fc-main app-font-bold goods-name">
|
||||
{{ data.product_name || "" }}
|
||||
</view>
|
||||
<view class="fs-22 app-fc-cancel cart-shuxing">
|
||||
{{ data.price_name || "" }}{{ data.price_name ? ";" : ""
|
||||
}}{{ data.shuxing_name || "" }}
|
||||
</view>
|
||||
<!-- <view class="flex-row-start">
|
||||
<text class="fs-20 sale-btn" v-for="(label, i) in labelList" :key="i"
|
||||
:class="[i === 0 ? 'sale-today app-fc-mark' : 'app-fc-white']">
|
||||
{{ label }}
|
||||
</text>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<text class="fs-24 app-fc-mark">
|
||||
¥
|
||||
<text class="fs-28 app-font-bold app-fc-mark">
|
||||
{{ Number(data.product_price) || 0 }}
|
||||
</text>
|
||||
<!-- <text class="fs-20" style="margin-left: 4rpx;color: #FF19A0;">到手价</text>
|
||||
<text class="fs-20 origin-price">
|
||||
¥{{ Number(data.item_price) || 0 }}
|
||||
</text> -->
|
||||
</text>
|
||||
<!-- <view class="fs-20 good-salenum">
|
||||
已售{{ data.xiaoliang || 0 }}
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<view class="flex-row-end nums-list">
|
||||
<view class="nums-btn nums-btn-decrease" @click.stop="decrease">
|
||||
<text class="nums-btn-text">-</text>
|
||||
</view>
|
||||
<view class="nums-display">
|
||||
<text class="fs-24 nums-text">{{ data.number }}</text>
|
||||
</view>
|
||||
<view class="nums-btn nums-btn-add" @click.stop="add">
|
||||
<text class="nums-btn-text">+</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
updateCartNum
|
||||
} from "@/api/shop";
|
||||
import {
|
||||
updateCartSelect
|
||||
} from "../../../../api/shop";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
mode: {
|
||||
type: String, // 'pay', 'delete'
|
||||
default: "pay",
|
||||
},
|
||||
cartData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
labelList() {
|
||||
return (this.data?.label || "").split(",").filter((v) => !!v);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.data = {
|
||||
...this.cartData
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
cartData: {
|
||||
handler(val) {
|
||||
this.data = {
|
||||
...val
|
||||
};
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
jumpToDetails() {
|
||||
// console.log(this.data,'--=')
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/shop/details?product_id=${this.data.product_id}`,
|
||||
});
|
||||
},
|
||||
decrease() {
|
||||
if (this.data.number === 1) {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: "宝贝数量不能再减少了"
|
||||
});
|
||||
return;
|
||||
}
|
||||
updateCartNum({
|
||||
cart_id: this.data.cart_id,
|
||||
number: this.data.number - 1,
|
||||
})
|
||||
.then(() => {
|
||||
this.data.number -= 1;
|
||||
this.$forceUpdate();
|
||||
this.$emit("changeList");
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: err
|
||||
});
|
||||
});
|
||||
},
|
||||
add() {
|
||||
if (this.data.number === this.data.kucun) {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: "宝贝库存不够了"
|
||||
});
|
||||
return;
|
||||
}
|
||||
updateCartNum({
|
||||
cart_id: this.data.cart_id,
|
||||
number: this.data.number + 1,
|
||||
})
|
||||
.then(() => {
|
||||
this.data.number += 1;
|
||||
this.$forceUpdate();
|
||||
this.$emit("changeList");
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: err
|
||||
});
|
||||
});
|
||||
},
|
||||
selectAction() {
|
||||
if (this.data.mode === "pay") {
|
||||
const value = this.data.is_select === 1 ? 2 : 1;
|
||||
updateCartSelect({
|
||||
cart_id: this.data.cart_id,
|
||||
is_select: value,
|
||||
})
|
||||
.then(() => {
|
||||
this.data.is_select = value;
|
||||
this.$emit("changeList");
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: err
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.data.deleteSelect = !this.data.deleteSelect;
|
||||
this.$forceUpdate();
|
||||
this.$emit("deleteSelectChange", this.data);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cart-item {
|
||||
width: calc(100vw - 40rpx);
|
||||
box-sizing: border-box;
|
||||
align-items: stretch;
|
||||
background: #fff;
|
||||
border-radius: 40rpx;
|
||||
margin: auto;
|
||||
padding: 20rpx;
|
||||
|
||||
.cart-icon-wrapper {
|
||||
width: 80rpx;
|
||||
height: 100%;
|
||||
align-self: center;
|
||||
|
||||
.cart-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-inner {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.goods-img {
|
||||
border-radius: 20rpx;
|
||||
width: 104rpx;
|
||||
height: 104rpx;
|
||||
}
|
||||
|
||||
.goods-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin-left: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
|
||||
.sale-btn {
|
||||
padding: 3rpx 4rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 4rpx;
|
||||
border: 1px solid $app_color_main;
|
||||
margin-right: 10rpx;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.sale-today {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.origin-price {
|
||||
color: #726E71;
|
||||
text-decoration: line-through;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.good-salenum {
|
||||
color: #9b939a;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.nums-list {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
|
||||
.nums-btn {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
background: #F5F5F5;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.nums-btn-text {
|
||||
font-size: 32rpx;
|
||||
color: #3D3D3D;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.nums-display {
|
||||
width: 60rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
.nums-text {
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
295
src/pages/client/cart/index.vue
Normal file
@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<view class="cart-list-container">
|
||||
<view class="flex-row-between cart-list-header">
|
||||
<text class="fs-24 app-fc-normal">
|
||||
{{ mode === "pay" ? "请勾选结算" : "请勾选删除" }}
|
||||
</text>
|
||||
<text class="fs-24 app-fc-main" @click="changeMode">
|
||||
{{ mode === "pay" ? "管理" : "取消" }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="cart-list-inner">
|
||||
<scroll-view class="list-template-wrapper" :scroll-y="true" :refresher-enabled="true"
|
||||
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh">
|
||||
<view class="list-content">
|
||||
<view v-for="(item, index) in cartList" :key="item.id" :class="{ left: index % 2 === 0 }"
|
||||
class="flex-column-start news-item">
|
||||
<cart-item :cartData="{...item, mode: mode}" @changeList="reloadList"
|
||||
@deleteSelectChange="deleteSelectChange" />
|
||||
</view>
|
||||
<uni-load-more v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||
:status="isLoading ? 'loading' : 'nomore'"></uni-load-more>
|
||||
</view>
|
||||
<view class="place-view"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="flex-row-between cart-list-bottom">
|
||||
<view class="flex-row-start" @click="payAll">
|
||||
<image class="select-icon" :src="
|
||||
isAllSelect
|
||||
? require('@/static/images/cart_checked.png')
|
||||
: require('@/static/images/unchecked.png')
|
||||
" />
|
||||
<text class="fs-24 app-fc-main">全选</text>
|
||||
</view>
|
||||
<view v-if="mode === 'pay'" class="flex-row-end">
|
||||
<text class="fs-24 app-fc-main">
|
||||
合计:
|
||||
<text class="fs-48 totalPrice">¥{{ payPrice }}</text>
|
||||
</text>
|
||||
<view class="flex-center fs-30 app-fc-white pay-btn" @click="goBuy">
|
||||
去结算
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="mode === 'delete'" class="flex-center fs-30 app-fc-mark pay-btn delete"
|
||||
@click="deleteCartAction">
|
||||
删除
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CartItem from "./components/CartItem.vue";
|
||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
||||
|
||||
import {
|
||||
getCartList,
|
||||
updateCartSelect,
|
||||
deleteCart
|
||||
} from "@/api/shop";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CartItem,
|
||||
ListPageTemp,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cartList: [],
|
||||
requestData: {},
|
||||
mode: "pay", //pay/delete
|
||||
payPrice: 0,
|
||||
total: 0,
|
||||
refreshTriggered: false,
|
||||
p: 1,
|
||||
num: 999,
|
||||
isLoading: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isAllSelect() {
|
||||
return (
|
||||
this.cartList.length > 0 &&
|
||||
!this.cartList.some(
|
||||
(item) =>
|
||||
(this.mode === "pay" && item.is_select !== 1) ||
|
||||
(this.mode === "delete" && !item.deleteSelect)
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
getCartList,
|
||||
onRefresh() {
|
||||
if (this.refreshTriggered) return;
|
||||
this.refreshTriggered = true;
|
||||
this.p = 1;
|
||||
this.total = 0;
|
||||
this.getList();
|
||||
},
|
||||
changeMode() {
|
||||
if (this.mode === "pay") {
|
||||
this.mode = "delete";
|
||||
} else {
|
||||
this.mode = "pay";
|
||||
}
|
||||
},
|
||||
getList() {
|
||||
this.isLoading = true
|
||||
getCartList({
|
||||
p: this.p,
|
||||
num: this.num
|
||||
})
|
||||
.then((res) => {
|
||||
this.payPrice = res.data.total_amount
|
||||
console.log( res.data,'???')
|
||||
this.cartList = (res?.data.list || []).map(v => {
|
||||
return {
|
||||
...v,
|
||||
deleteSelect: !!v.deleteSelect,
|
||||
}
|
||||
});
|
||||
this.total = res?.count || 0;
|
||||
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
this.refreshTriggered = false;
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
reloadList() {
|
||||
this.getList();
|
||||
},
|
||||
deleteSelectChange(data) {
|
||||
const index = this.cartList.findIndex(
|
||||
(item) => item.cart_id === data.cart_id
|
||||
);
|
||||
this.cartList[index]["deleteSelect"] = data.deleteSelect;
|
||||
this.$forceUpdate();
|
||||
},
|
||||
deleteCartAction() {
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "您确定要删除么?",
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
console.log(this.cartList,'this.cartList')
|
||||
const deletList = this.cartList
|
||||
.filter((item) => item.deleteSelect)
|
||||
.map((v) => v.cart_id);
|
||||
|
||||
if (!deletList.length) {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: "请选择要删除的商品"
|
||||
});
|
||||
return;
|
||||
}
|
||||
deleteCart({
|
||||
cart_id: this.isAllSelect ? 0 : deletList.join(","),
|
||||
})
|
||||
.then(() => {
|
||||
this.reloadList();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: err
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
payAll() {
|
||||
if (this.mode === "delete") {
|
||||
const val = !this.isAllSelect;
|
||||
this.cartList.forEach((item) => {
|
||||
item.deleteSelect = val;
|
||||
});
|
||||
} else if (this.mode === "pay") {
|
||||
updateCartSelect({
|
||||
cart_id: 0,
|
||||
is_select: this.isAllSelect ? 2 : 1,
|
||||
})
|
||||
.then(() => {
|
||||
this.reloadList();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: err
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
goBuy() {
|
||||
const selectList = this.cartList.filter((item) => item.is_select === 1);
|
||||
uni.navigateTo({
|
||||
url: "/pages/client/order/create?type=cart",
|
||||
success: (res) => {
|
||||
// 通过eventChannel向被打开页面传送数据
|
||||
res.eventChannel.emit("createOrder", {
|
||||
goodList: selectList,
|
||||
payPrice:this.payPrice
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cart-list-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #fbf8fc;
|
||||
padding-bottom: 0;
|
||||
padding-top: 20rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.list-template-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.list-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.news-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-list-header {
|
||||
margin: 0 20rpx 20rpx;
|
||||
}
|
||||
|
||||
.cart-list-inner {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.place-view {
|
||||
height: 164rpx;
|
||||
}
|
||||
|
||||
.cart-list-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
padding-left: 32rpx;
|
||||
padding-right: 32rpx;
|
||||
padding-top: 20rpx;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
|
||||
.select-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.pay-btn {
|
||||
width: 220rpx;
|
||||
height: 92rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 92rpx;
|
||||
margin-left: 20rpx;
|
||||
border: 3rpx solid transparent;
|
||||
|
||||
&.delete {
|
||||
background: transparent;
|
||||
border: 3rpx solid $app_color_main;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.totalPrice {
|
||||
color: #FF19A0;
|
||||
}
|
||||
</style>
|
||||
123
src/pages/client/category/components/CategoryModal.vue
Normal file
@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<view class="all-category-modal" @click.stop="$emit('close')">
|
||||
<view class="category-modal-content">
|
||||
<text class="fs-36 app-fc-main">全部分类</text>
|
||||
<view class="flex-row-start category-list">
|
||||
<view
|
||||
class="flex-center category-item"
|
||||
:class="{ active: selectCategoryId === item.id }"
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
@click.stop="$emit('change', item)"
|
||||
>
|
||||
<image
|
||||
class="category-item-icon"
|
||||
:src="item.type_pic"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="fs-20 app-text-ellipse category-item-name">
|
||||
{{ item.name }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="flex-row-center category-modal-close"
|
||||
@click.stop="$emit('close')"
|
||||
>
|
||||
<text class="fs-24 app-fc-cancel">点击收起</text>
|
||||
<image
|
||||
class="arrow-icon"
|
||||
src="@/static/images/arrow_up.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
selectCategoryId: {
|
||||
type: String | Number,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.all-category-modal {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
|
||||
.category-modal-content {
|
||||
background: #fff;
|
||||
padding: 30rpx;
|
||||
|
||||
.category-list {
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
padding-top: 30rpx;
|
||||
max-height: 35vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
.category-item {
|
||||
width: 20%;
|
||||
margin-bottom: 24rpx;
|
||||
.category-item-icon {
|
||||
width: 92rpx;
|
||||
height: 92rpx;
|
||||
background: #fee9f3;
|
||||
border-radius: 92rpx;
|
||||
border: 4rpx solid #fee9f3;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.category-item-name {
|
||||
padding: 0 22rpx;
|
||||
box-sizing: border-box;
|
||||
max-width: 140rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 36rpx;
|
||||
color: $app_fc_main;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.category-item-icon {
|
||||
border: 4rpx solid $app_color_main;
|
||||
}
|
||||
.category-item-name {
|
||||
background: $app_color_main;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.category-modal-close {
|
||||
background: #ffffff;
|
||||
border-radius: 0rpx 0rpx 40rpx 40rpx;
|
||||
height: 94rpx;
|
||||
|
||||
.arrow-icon {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
180
src/pages/client/category/components/GoodItem.vue
Normal file
@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<view class="flex-row-start goods-item" @click.stop="$emit('clickCard', data)">
|
||||
<image class="goods-img" :src="data.product_pic" mode="aspectFill" />
|
||||
<view class="goods-content">
|
||||
<view class="text-multi-ellipse fs-28 app-fc-main app-font-bold goods-name">
|
||||
{{ data.product_name || "" }}
|
||||
</view>
|
||||
<view class="flex-row-start label">
|
||||
<image class="hot-icon" :src="`${imgPrefix}mall-hot.png`"></image>
|
||||
<view class="fs-20 app-fc-main label-name">{{data.sales}}人买过</view>
|
||||
</view>
|
||||
<view class="price-row">
|
||||
<view class="flex-row-start">
|
||||
<text class="fs-28 price-text">
|
||||
¥
|
||||
<text class="fs-28">{{data.prices[0].original_price || 0 }}</text>
|
||||
</text>
|
||||
<text class="fs-20 price-label">到手价</text>
|
||||
</view>
|
||||
<text class="fs-24 origin-price" v-if="minPrice.price_shichang">
|
||||
¥{{ minPrice.price_shichang || 0 }}
|
||||
</text>
|
||||
</view>
|
||||
<image
|
||||
class="add-cart-icon"
|
||||
:class="{ 'add-cart-icon-animate': isAnimating }"
|
||||
:src="`${imgPrefix}mall-addCar.png`"
|
||||
@click.stop="$emit('addToCar', data)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { imgPrefix } from '@/utils/common';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imgPrefix,
|
||||
isAnimating: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// minPrice() {
|
||||
// let minPrice = {};
|
||||
// let minPriceValue = 0;
|
||||
// this.data.price_list.map((v) => {
|
||||
// if (!minPriceValue || minPriceValue > +v.price) {
|
||||
// minPriceValue = +v.price;
|
||||
// minPrice = { ...v };
|
||||
// }
|
||||
// });
|
||||
// return minPrice;
|
||||
// },
|
||||
labelList() {
|
||||
return this.data.label.split(",").filter((v) => !!v);
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
// 触发添加购物车动画
|
||||
triggerAddCartAnimation() {
|
||||
this.isAnimating = true;
|
||||
setTimeout(() => {
|
||||
this.isAnimating = false;
|
||||
}, 600);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-item {
|
||||
background: #fff;
|
||||
border-radius: 40rpx;
|
||||
width: 100%;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20rpx;
|
||||
position: relative;
|
||||
border-bottom: 1rpx solid #F5F5F5;
|
||||
|
||||
.goods-img {
|
||||
border-radius: 20rpx;
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.goods-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.goods-name {
|
||||
margin: 0 0 12rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: #ffecf3;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 4rpx;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.hot-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
|
||||
.label-name {
|
||||
padding: 4rpx 8rpx;
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
|
||||
.price-row {
|
||||
margin-top: 12rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.price-text {
|
||||
color: #3D3D3D;
|
||||
}
|
||||
|
||||
.price-label {
|
||||
color: #999;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.origin-price {
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.add-cart-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
position: absolute;
|
||||
bottom: 20rpx;
|
||||
right: 20rpx;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
&.add-cart-icon-animate {
|
||||
animation: addCartBounce 0.6s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes addCartBounce {
|
||||
0% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
}
|
||||
25% {
|
||||
transform: scale(1.2) rotate(-10deg);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.3) rotate(10deg);
|
||||
}
|
||||
75% {
|
||||
transform: scale(1.1) rotate(-5deg);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
652
src/pages/client/category/index.vue
Normal file
@ -0,0 +1,652 @@
|
||||
<template>
|
||||
<view class="flex-column-start category-container">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar">
|
||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<view class="navbar-content">
|
||||
<view class="back-btn" @click="handleBack">
|
||||
<view class="back-icon"></view>
|
||||
</view>
|
||||
<view class="search-input-wrapper" :style="{ width: searchInputWidth + 'rpx' }">
|
||||
<input class="search-input" type="text" placeholder="搜索商品" v-model="searchKeyword"
|
||||
@confirm="handleSearch" @focus="handleSearchFocus" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="flex-row-start category-list">
|
||||
<view
|
||||
class="flex-center category-item"
|
||||
:class="{ active: selectCategoryId === item.id }"
|
||||
v-for="item in categoryList.slice(0, 6)"
|
||||
:key="item.id"
|
||||
@click="changeCategory(item)"
|
||||
>
|
||||
<image
|
||||
class="category-item-icon"
|
||||
:src="selectCategoryId === item.id ? item.select_type_pic : item.type_pic"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="flex-center fs-20 app-text-ellipse category-item-name">{{
|
||||
item.name
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="flex-center open-cell" @click="showAllCategory = true">
|
||||
<text class="fs-24 app-fc-main app-font-bold">展</text>
|
||||
<text class="fs-24 app-fc-main app-font-bold">开</text>
|
||||
<image class="open-icon" src="@/static/images/open_icon.png" />
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<view class="flex-row-start category-content">
|
||||
<!-- 左侧:一级分类列表 -->
|
||||
<view v-if="categoryList.length" class="category-list-left">
|
||||
<view class="flex-center category-item-left" :class="{
|
||||
'category-item-left-active': item.id === selectCategoryId,
|
||||
}" v-for="(item, index) in categoryList" :key="index" @click="changeCateg(item)">
|
||||
<text class="fs-28 app-fc-main app-text-ellipse category-item-left-name">
|
||||
{{ item.type_name }}
|
||||
</text>
|
||||
<view v-if="selectCategoryId === item.id" class="border-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 右侧内容区域 -->
|
||||
<view class="category-right-wrapper">
|
||||
<!-- 右侧上方:二级分类标签 -->
|
||||
<!-- <view v-if="subCategoryList.length" class="sub-category-tabs">
|
||||
<scroll-view class="sub-category-tabs-scroll" scroll-x>
|
||||
<view class="sub-category-tab-item" :class="{
|
||||
'sub-category-tab-item-active': !selectSubCategoryId || selectSubCategoryId === '',
|
||||
}" @click="changeSubCategory({ id: '', name: '全部' })">
|
||||
全部
|
||||
</view>
|
||||
<view class="sub-category-tab-item" :class="{
|
||||
'sub-category-tab-item-active': item.id === selectSubCategoryId,
|
||||
}" v-for="(item, index) in subCategoryList" :key="index" @click="changeSubCategory(item)">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view> -->
|
||||
<!-- 右侧下方:商品列表 -->
|
||||
<scroll-view class="category-right" scroll-y :refresher-enabled="true"
|
||||
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
|
||||
<view class="goods-list">
|
||||
<good-item v-for="good in goodsList" :key="good.product_id" :ref="`goodItem_${good.product_id}`"
|
||||
:data="good" @addToCar="addToCar" @clickCard="jumpToDetail" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="add-car-view">
|
||||
<image class="add-car-icon" :src="`${imgPrefix}mall-shopCar.png`" @click="jumpToCart" />
|
||||
<view v-if="cartShowCount" class="fs-20 app-fc-white flex-center cart-count"
|
||||
:class="{ 'cart-count-bounce': cartCountBounce }">
|
||||
{{ cartShowCount }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<add-goods-modal v-if="showModal" :data="addGoodInfo" optText="加入购物车" @change="(val) => (showModal = val)"
|
||||
@optAction="optAction" />
|
||||
|
||||
<category-modal v-if="showAllCategory" :list="categoryList" :selectCategoryId="selectCategoryId"
|
||||
@close="showAllCategory = false" @change="changeCategory" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getGoodsClassify,
|
||||
addCart,
|
||||
getCartList
|
||||
} from "@/api/shop";
|
||||
import {
|
||||
imgPrefix
|
||||
} from "@/utils/common";
|
||||
|
||||
import GoodItem from "./components/GoodItem.vue";
|
||||
import AddGoodsModal from "@/components/goods/AddGoodsModal.vue";
|
||||
import CategoryModal from "./components/CategoryModal.vue";
|
||||
import {
|
||||
getGoodsListData
|
||||
} from "../../../api/shop";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GoodItem,
|
||||
AddGoodsModal,
|
||||
CategoryModal,
|
||||
},
|
||||
data() {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
// 计算搜索输入框宽度,避免被胶囊按钮遮挡
|
||||
let searchInputWidth = 500; // 默认宽度
|
||||
try {
|
||||
const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
if (menuButtonInfo && menuButtonInfo.left) {
|
||||
// 屏幕宽度转换为 rpx (750rpx 对应 375px)
|
||||
const screenWidthRpx = 750;
|
||||
const leftPadding = 32; // navbar-content 左侧 padding
|
||||
const backBtnWidth = 60; // 返回按钮宽度
|
||||
const backBtnMargin = 20; // 返回按钮右边距
|
||||
// 右侧需要留出的空间:屏幕宽度(px) - 胶囊按钮左边距(px) + 额外间距(px),然后转换为 rpx
|
||||
const rightSpacePx = systemInfo.windowWidth - menuButtonInfo.left + 20;
|
||||
const rightSpaceRpx = (rightSpacePx / systemInfo.windowWidth) * screenWidthRpx;
|
||||
searchInputWidth = screenWidthRpx - leftPadding - backBtnWidth - backBtnMargin - rightSpaceRpx;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('获取胶囊按钮信息失败', e);
|
||||
}
|
||||
return {
|
||||
statusBarHeight: systemInfo.statusBarHeight || 0,
|
||||
imgPrefix,
|
||||
searchInputWidth: searchInputWidth,
|
||||
searchKeyword: '',
|
||||
id: "",
|
||||
categoryList: [],
|
||||
selectCategoryId: "",
|
||||
selectSubCategoryId: "",
|
||||
showModal: false,
|
||||
addGoodInfo: null,
|
||||
showAllCategory: false,
|
||||
goodsList: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
size: 10,
|
||||
refreshTriggered: false,
|
||||
isLoading: false,
|
||||
petOrderId: '',
|
||||
petOrderAddressId: '',
|
||||
cartCount: 0,
|
||||
cartCountBounce: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
cartShowCount() {
|
||||
return this.cartCount > 9 ? "9+" : this.cartCount;
|
||||
},
|
||||
selectCategory() {
|
||||
return (
|
||||
this.categoryList.find((item) => item.id === this.selectCategoryId) || {}
|
||||
);
|
||||
},
|
||||
subCategoryList() {
|
||||
return this.selectCategory?.children || [];
|
||||
},
|
||||
selectSubCategory() {
|
||||
return (
|
||||
this.subCategoryList.find(
|
||||
(item) => item.id === this.selectSubCategoryId
|
||||
) || {}
|
||||
);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getCategoryList();
|
||||
},
|
||||
onLoad(options) {
|
||||
const {
|
||||
id,
|
||||
petOrderId = '',
|
||||
addressId = ''
|
||||
} = options;
|
||||
this.selectCategoryId = +id;
|
||||
this.petOrderId = petOrderId
|
||||
this.petOrderAddressId = addressId
|
||||
},
|
||||
onShow() {
|
||||
this.getCartListData();
|
||||
},
|
||||
watch: {
|
||||
selectCategoryId(val) {
|
||||
if (val && !this.selectSubCategoryId) {
|
||||
this.getShopList();
|
||||
}
|
||||
},
|
||||
subCategoryList(list) {
|
||||
// 当二级分类列表变化时,默认选择"全部"(空字符串)
|
||||
this.selectSubCategoryId = "";
|
||||
},
|
||||
selectSubCategoryId(id) {
|
||||
this.getShopList();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 分类列表
|
||||
getCategoryList(parent_id = -1) {
|
||||
getGoodsClassify({
|
||||
parent_id,
|
||||
p: 1,
|
||||
num: 999
|
||||
}).then((res) => {
|
||||
this.categoryList = res?.data || [];
|
||||
if (!this.selectCategoryId) {
|
||||
this.selectCategoryId = this.categoryList[0]?.id || "";
|
||||
}
|
||||
});
|
||||
},
|
||||
changeCateg(item) {
|
||||
this.changeId = item.id
|
||||
this.selectCategoryId = item.id;
|
||||
this.showAllCategory = false;
|
||||
// console.log(item,'--')
|
||||
|
||||
},
|
||||
// 商品列表
|
||||
getShopList() {
|
||||
getGoodsListData({
|
||||
type: this.changeId,
|
||||
p: this.page,
|
||||
num: this.size,
|
||||
keyword: "",
|
||||
is_tui: 0,
|
||||
})
|
||||
.then((res) => {
|
||||
const list = res?.data || [];
|
||||
this.goodsList =
|
||||
this.page === 1 ? list : [...this.goodsList, ...list];
|
||||
this.total = res?.count || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
this.refreshTriggered = false;
|
||||
});
|
||||
},
|
||||
changeCategory(data) {
|
||||
console.log(data, '--=')
|
||||
this.selectCategoryId = data.id;
|
||||
this.showAllCategory = false;
|
||||
},
|
||||
changeSubCategory(data) {
|
||||
this.selectSubCategoryId = data.id;
|
||||
},
|
||||
// 加入购物车
|
||||
addCartAction(data) {
|
||||
addCart(data)
|
||||
.then(() => {
|
||||
uni.showToast({
|
||||
title: "已加入购物车!",
|
||||
icon: "none"
|
||||
});
|
||||
this.getCartListData();
|
||||
// 触发购物车数量徽章动画
|
||||
this.triggerCartCountAnimation();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
title: err || "加入购物车失败!",
|
||||
icon: "none"
|
||||
});
|
||||
});
|
||||
},
|
||||
optAction(data) {
|
||||
console.log(data, '??')
|
||||
const {
|
||||
product_id,
|
||||
type,
|
||||
prices,
|
||||
product_name,
|
||||
product_pic,
|
||||
number
|
||||
} = data;
|
||||
|
||||
this.addCartAction({
|
||||
item_id: product_id,
|
||||
item_type: type,
|
||||
price_id: prices?.[0]?.price_id,
|
||||
price_desc: prices?.[0]?.price_desc,
|
||||
item_name: product_name,
|
||||
item_price: prices?.[0]?.actual_price,
|
||||
number,
|
||||
item_pic: product_pic,
|
||||
is_select: 1
|
||||
});
|
||||
this.showModal = false;
|
||||
// 触发对应商品项的动画
|
||||
this.triggerGoodItemAnimation(product_id);
|
||||
},
|
||||
// 触发商品项的添加购物车动画
|
||||
triggerGoodItemAnimation(goodsId) {
|
||||
this.$nextTick(() => {
|
||||
const ref = this.$refs[`goodItem_${goodsId}`];
|
||||
if (ref && ref[0]) {
|
||||
ref[0].triggerAddCartAnimation();
|
||||
}
|
||||
});
|
||||
},
|
||||
// 触发购物车数量徽章动画
|
||||
triggerCartCountAnimation() {
|
||||
this.cartCountBounce = true;
|
||||
setTimeout(() => {
|
||||
this.cartCountBounce = false;
|
||||
}, 600);
|
||||
},
|
||||
addToCar(good) {
|
||||
this.showModal = true;
|
||||
this.addGoodInfo = {
|
||||
...good
|
||||
};
|
||||
},
|
||||
// 购物车列表
|
||||
getCartListData() {
|
||||
getCartList({
|
||||
p: 1,
|
||||
num: 999
|
||||
}).then((res) => {
|
||||
this.cartCount = (res?.data.list || []).reduce(
|
||||
(total, prev) => total + prev.number,
|
||||
0
|
||||
);
|
||||
});
|
||||
},
|
||||
jumpToCart() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/cart/index`,
|
||||
});
|
||||
},
|
||||
jumpToDetail(details) {
|
||||
console.log(details, '??')
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/shop/details?product_id=${details.product_id}&petOrderId=${this.petOrderId}&petOrderAddressId=${this.petOrderAddressId}`,
|
||||
});
|
||||
},
|
||||
onRefresh() {
|
||||
this.refreshTriggered = true;
|
||||
this.page = 1;
|
||||
this.size = 10;
|
||||
this.total = 0;
|
||||
this.getShopList();
|
||||
},
|
||||
onLoadMore() {
|
||||
if (!this.isLoading && this.total > this.goodsList.length) {
|
||||
this.page++;
|
||||
this.getShopList();
|
||||
}
|
||||
},
|
||||
handleBack() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
handleSearch() {
|
||||
// 处理搜索逻辑
|
||||
this.page = 1;
|
||||
this.getShopList();
|
||||
},
|
||||
handleSearchFocus() {
|
||||
// 可以跳转到搜索页面
|
||||
// uni.navigateTo({
|
||||
// url: '/pages/client/search/index'
|
||||
// });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.category-container {
|
||||
height: 100%;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
padding-top: calc(var(--status-bar-height, 0px) + 88rpx + 20rpx);
|
||||
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
background: #ff19a0;
|
||||
border-radius: 0px 0px 16px 16px;
|
||||
|
||||
.status-bar {
|
||||
background: #ff19a0;
|
||||
}
|
||||
|
||||
.navbar-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32rpx;
|
||||
height: 88rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.back-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.back-icon {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-left: 3rpx solid #fff;
|
||||
border-bottom: 3rpx solid #fff;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
height: 64rpx;
|
||||
background: #fff;
|
||||
border-radius: 32rpx;
|
||||
padding: 0 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.category-list {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 20rpx 24rpx;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
min-height: 176rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.category-item {
|
||||
.category-item-icon {
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
background: #fee9f3;
|
||||
border-radius: 90rpx;
|
||||
border: 4rpx solid #fee9f3;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.category-item-name {
|
||||
padding: 0 22rpx;
|
||||
box-sizing: border-box;
|
||||
max-width: 140rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 36rpx;
|
||||
color: $app_fc_main;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.category-item-icon {
|
||||
border: 4rpx solid $app_color_main;
|
||||
}
|
||||
|
||||
.category-item-name {
|
||||
background: $app_color_main;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-cell {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 72rpx;
|
||||
background: #fff;
|
||||
|
||||
.open-icon {
|
||||
width: 27rpx;
|
||||
height: 23rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.category-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding-top: 50rpx;
|
||||
|
||||
// 左侧:一级分类列表
|
||||
.category-list-left {
|
||||
width: 180rpx;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background: #f7f8fa;
|
||||
|
||||
.category-item-left {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 110rpx;
|
||||
position: relative;
|
||||
background: #f7f8fa;
|
||||
|
||||
.category-item-left-name {
|
||||
color: #3D3D3D;
|
||||
}
|
||||
|
||||
&.category-item-left-active {
|
||||
background-color: #fff;
|
||||
|
||||
.category-item-left-name {
|
||||
color: $app_fc_mark;
|
||||
}
|
||||
|
||||
.border-line {
|
||||
position: absolute;
|
||||
top: 28rpx;
|
||||
bottom: 28rpx;
|
||||
left: 0;
|
||||
width: 10rpx;
|
||||
border-radius: 28rpx;
|
||||
background: $app_color_main;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 右侧内容区域
|
||||
.category-right-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
|
||||
// 二级分类标签
|
||||
.sub-category-tabs {
|
||||
flex-shrink: 0;
|
||||
background: #fff;
|
||||
padding: 28rpx 0;
|
||||
|
||||
.sub-category-tabs-scroll {
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32rpx;
|
||||
|
||||
.sub-category-tab-item {
|
||||
display: inline-block;
|
||||
padding: 12rpx 20rpx;
|
||||
margin-right: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #3D3D3D;
|
||||
white-space: nowrap;
|
||||
background: #F5F5F5;
|
||||
border-radius: 32rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.sub-category-tab-item-active {
|
||||
color: #FF19A0;
|
||||
background: #FFECF3;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 商品列表区域
|
||||
.category-right {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-car-view {
|
||||
position: fixed;
|
||||
bottom: 35vh;
|
||||
right: 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
padding: 20rpx;
|
||||
box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.add-car-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.cart-count {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
border-radius: 30rpx;
|
||||
background: #FF19A0;
|
||||
z-index: 10;
|
||||
border: 1px solid #FFFFFF;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
&.cart-count-bounce {
|
||||
animation: cartCountBounce 0.6s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cartCountBounce {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
110
src/pages/client/collect/list.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<scroll-view
|
||||
:scroll-y="true"
|
||||
class="collect-list-container"
|
||||
:refresher-enabled="true"
|
||||
:refresher-triggered="refreshTriggered"
|
||||
@refresherrefresh="onRefresh"
|
||||
@scrolltolower="onLoadMore"
|
||||
>
|
||||
<view class="collect-list-content">
|
||||
<view class="goods-list-item left">
|
||||
<good-item
|
||||
v-for="(good, i) in leftColumnGoods"
|
||||
:index="2 * i"
|
||||
:key="good.shoucang_id"
|
||||
:data="good"
|
||||
@addToCar="addToCar"
|
||||
/>
|
||||
</view>
|
||||
<view class="goods-list-item">
|
||||
<good-item
|
||||
v-for="(good, i) in rightColumnGoods"
|
||||
:index="2 * i + 1"
|
||||
:key="good.shoucang_id"
|
||||
:data="good"
|
||||
@addToCar="addToCar"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { collectList } from "@/api/shop";
|
||||
import GoodItem from "../shop/components/GoodItem.vue";
|
||||
|
||||
export default {
|
||||
components: { GoodItem },
|
||||
data() {
|
||||
return {
|
||||
goodsList: [],
|
||||
p: 1,
|
||||
num: 10,
|
||||
total: 0,
|
||||
refreshTriggered: false,
|
||||
isLoading: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
leftColumnGoods() {
|
||||
return this.goodsList.filter((v, i) => i % 2 === 0);
|
||||
},
|
||||
rightColumnGoods() {
|
||||
return this.goodsList.filter((v, i) => i % 2 === 1);
|
||||
},
|
||||
},
|
||||
onShow() {
|
||||
this.onRefresh();
|
||||
},
|
||||
methods: {
|
||||
// 获取商品列表
|
||||
getGoodsList() {
|
||||
if (this.isLoading) return;
|
||||
this.isLoading = true;
|
||||
collectList({ p: this.p, num: this.num })
|
||||
.then((res) => {
|
||||
const list = res?.info || [];
|
||||
this.goodsList = this.p === 1 ? list : [...this.goodsList, ...list];
|
||||
this.total = res?.count || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
this.refreshTriggered = false;
|
||||
});
|
||||
},
|
||||
onRefresh() {
|
||||
if (this.refreshTriggered) return;
|
||||
this.refreshTriggered = true;
|
||||
this.p = 1;
|
||||
this.num = 10;
|
||||
this.total = 0;
|
||||
this.getGoodsList();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.collect-list-container {
|
||||
height: 100%;
|
||||
background: #f7f8fa;
|
||||
|
||||
.collect-list-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
height: 100%;
|
||||
padding: 24rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.goods-list-item {
|
||||
flex: 1;
|
||||
|
||||
&.left {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
256
src/pages/client/community/components/CommunityItem.vue
Normal file
@ -0,0 +1,256 @@
|
||||
<template>
|
||||
<view class="community-item">
|
||||
<view class="flex-row-start item-title">
|
||||
<image class="item-avator" :src="data.head_pic" />
|
||||
<text class="fs-32 app-fc-main app-font-bold item-nickname">
|
||||
{{ data.nick_name || "" }}
|
||||
</text>
|
||||
<text class="fs-22 item-time">
|
||||
{{ time }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="wash-info" v-if="data.old_list.length">
|
||||
<text class="fs-28 app-fc-main app-font-bold">洗护前</text>
|
||||
<view class="wash-imgs-wrapper">
|
||||
<view class="flex-row-start">
|
||||
<image
|
||||
class="wash-img"
|
||||
:class="{'wash-img-right': index % 2 === 2}"
|
||||
:src="item"
|
||||
v-for="(item, index) in data.old_list"
|
||||
:key="index"
|
||||
@click="preview(index, data.old_list)"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="wash-info wash-info-new" v-if="data.new_list.length">
|
||||
<text class="fs-28 app-fc-main app-font-bold">洗护后</text>
|
||||
<view class="wash-imgs-wrapper">
|
||||
<view class="flex-row-start">
|
||||
<image
|
||||
class="wash-img"
|
||||
:class="{'wash-img-right': index % 2 === 2}"
|
||||
:src="item"
|
||||
v-for="(item, index) in data.new_list"
|
||||
:key="index"
|
||||
@click="preview(index, data.new_list)"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="showDeletBtn"
|
||||
class="flex-center fs-30 app-fc-white delet-btn"
|
||||
@click="$emit('delete', data)"
|
||||
>
|
||||
一键删除
|
||||
</view>
|
||||
|
||||
<view v-if="showOptBtns" class="flex-row-start opt-btns">
|
||||
<view class="flex-row-start opt-btn">
|
||||
<image class="opt-icon" :src="require('../static/see.png')" />
|
||||
<text class="fs-30 opt-text">{{ cellData.see || 0 }}</text>
|
||||
</view>
|
||||
|
||||
<view class="flex-row-start opt-btn" @click="zanComunity">
|
||||
<image
|
||||
class="opt-icon"
|
||||
:src="
|
||||
cellData.zan_id
|
||||
? require('../static/zan_active.png')
|
||||
: require('../static/zan.png')
|
||||
"
|
||||
/>
|
||||
<text class="fs-30 opt-text">{{ cellData.zan || 0 }}</text>
|
||||
</view>
|
||||
|
||||
<button class="share-wrapper" open-type="share" @click="share">
|
||||
<view class="flex-row-start opt-btn">
|
||||
<image class="opt-icon" :src="require('../static/share.png')" />
|
||||
<text class="fs-30 opt-text">{{ cellData.share || 0 }}</text>
|
||||
</view>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from "moment";
|
||||
import { zanCommunity, getCommunityDetail } from "@/api/community";
|
||||
import appConfig from "../../../../constants/app.config";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
showDeletBtn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showOptBtns: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cellData: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
time() {
|
||||
return moment(this.data.add_time * 1000).format("YYYY/MM/DD");
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
watch: {
|
||||
data: {
|
||||
handler(val) {
|
||||
this.cellData = { ...val };
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
preview(index, list = []) {
|
||||
uni.previewImage({
|
||||
urls: list,
|
||||
current: index,
|
||||
});
|
||||
this.getDetail();
|
||||
},
|
||||
// 圈子详情
|
||||
getDetail(is_see = 1, is_share = 0) {
|
||||
getCommunityDetail({
|
||||
chongquan_id: this.data.chongquan_id,
|
||||
is_see,
|
||||
is_share,
|
||||
}).then((res) => {
|
||||
this.cellData = { ...this.cellData, ...res?.info };
|
||||
this.$emit("update");
|
||||
this.$forceUpdate();
|
||||
});
|
||||
},
|
||||
// 点赞
|
||||
zanComunity() {
|
||||
zanCommunity({
|
||||
chongquan_id: this.data.chongquan_id,
|
||||
type: this.cellData.zan_id ? 2 : 1,
|
||||
}).then(() => {
|
||||
this.$emit("update");
|
||||
});
|
||||
},
|
||||
// 转发
|
||||
share() {
|
||||
this.$emit("share", {
|
||||
title: appConfig.appShareName,
|
||||
path: `/pages/client/index/index?communityId=${this.data.chongquan_id}`,
|
||||
communityId: this.cellData.chongquan_id,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.community-item {
|
||||
width: calc(100vw - 32rpx * 2);
|
||||
background: #fff;
|
||||
border-radius: 40rpx;
|
||||
padding: 32rpx 24rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.item-title {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.item-avator {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 60rpx;
|
||||
}
|
||||
|
||||
.item-nickname {
|
||||
flex: 1;
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
|
||||
.item-time {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.wash-info {
|
||||
padding: 24rpx;
|
||||
border-radius: 20rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.wash-info-new {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.wash-imgs-wrapper {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: scroll;
|
||||
|
||||
.wash-img {
|
||||
width: calc((100vw - 32rpx * 2 - 24rpx * 4 - 20rpx * 2) / 3);
|
||||
height: calc((100vw - 32rpx * 2 - 24rpx * 4 - 20rpx * 2) / 3);
|
||||
border-radius: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
margin-top: 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.wash-img-right {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.delet-btn {
|
||||
height: 92rpx;
|
||||
width: 100%;
|
||||
border-radius: 92rpx;
|
||||
background: $app_color_main;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.share-wrapper {
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.opt-btns {
|
||||
margin-top: 32rpx;
|
||||
|
||||
.opt-btn {
|
||||
margin-right: 48rpx;
|
||||
|
||||
.opt-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.opt-text {
|
||||
color: #726e71;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
140
src/pages/client/community/index.vue
Normal file
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<view class="flex-column-start community-list-container">
|
||||
<nav-bar title="Wagoo" />
|
||||
<scroll-view
|
||||
class="list-template-wrapper"
|
||||
:scroll-y="!disableScroll"
|
||||
:refresher-enabled="true"
|
||||
:refresher-triggered="refreshTriggered"
|
||||
@refresherrefresh="onRefresh"
|
||||
@scrolltolower="onLoadMore"
|
||||
>
|
||||
<view class="list-content">
|
||||
<view
|
||||
v-for="(item, index) in list"
|
||||
:key="item[idKey]"
|
||||
:class="{ left: index % 2 === 0 }"
|
||||
class="flex-column-start news-item"
|
||||
>
|
||||
<community-item
|
||||
:data="item"
|
||||
:showOptBtns="true"
|
||||
@share="(val) => $emit('share', val)"
|
||||
@update="getAllList"
|
||||
/>
|
||||
</view>
|
||||
<uni-load-more
|
||||
v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||
:status="isLoading ? 'loading' : 'nomore'"
|
||||
></uni-load-more>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCommunityList } from "@/api/community";
|
||||
|
||||
import CommunityItem from "./components/CommunityItem.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CommunityItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
reloadFlag: 0,
|
||||
list: [],
|
||||
isLoading: false,
|
||||
total: 0,
|
||||
list: [],
|
||||
refreshTriggered: false,
|
||||
p: 1,
|
||||
num: 10,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
reloadFlag: {
|
||||
handler(value) {
|
||||
if (value) {
|
||||
this.p = 1;
|
||||
this.num = 10;
|
||||
this.total = 0;
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getCommunityList,
|
||||
onShowFun() {
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
onRefresh() {
|
||||
if (this.refreshTriggered) return;
|
||||
this.refreshTriggered = true;
|
||||
this.p = 1;
|
||||
this.num = 10;
|
||||
this.total = 0;
|
||||
this.getList();
|
||||
},
|
||||
onLoadMore() {
|
||||
if (!this.isLoading && this.total > this.list.length) {
|
||||
this.p++;
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
getList() {
|
||||
if (this.isLoading) return;
|
||||
this.isLoading = true;
|
||||
|
||||
const data = Object.assign({}, { p: this.p, num: this.num });
|
||||
getCommunityList(data)
|
||||
.then((res) => {
|
||||
const list =
|
||||
this.p === 1
|
||||
? res?.info || []
|
||||
: [...this.list, ...(res?.info || [])];
|
||||
this.list = list;
|
||||
this.total = res?.count || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
this.refreshTriggered = false;
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
getAllList() {
|
||||
getCommunityList({
|
||||
p: 1,
|
||||
num: this.list.length,
|
||||
}).then((res) => {
|
||||
this.list = res?.info || [];
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.community-list-container {
|
||||
height: 100%;
|
||||
align-items: stretch;
|
||||
|
||||
.list-template-wrapper {
|
||||
padding: 20rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
background: #f7f8fa;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
.list-content {
|
||||
width: 100%;
|
||||
}
|
||||
.news-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
80
src/pages/client/community/my-list.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<view class="community-list-container">
|
||||
<list-page-temp
|
||||
:getDataPromise="getMyCommunityList"
|
||||
:reloadFlag="reloadFlag"
|
||||
:requestData="{}"
|
||||
>
|
||||
<template v-slot:item="{ data }">
|
||||
<community-item
|
||||
:data="data"
|
||||
:showDeletBtn="true"
|
||||
@delete="deleteAction"
|
||||
/>
|
||||
</template>
|
||||
</list-page-temp>
|
||||
|
||||
<pop-up-modal
|
||||
v-if="showDeleteModal"
|
||||
content="确定要删除该条圈子吗?"
|
||||
@confirm="deleteCommunity"
|
||||
@cancel="showDeleteModal = false"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMyCommunityList, deleteCommunity } from "@/api/community";
|
||||
|
||||
import CommunityItem from "./components/CommunityItem.vue";
|
||||
import PopUpModal from "@/components/PopUpModal.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CommunityItem,
|
||||
PopUpModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
deleteInfo: null,
|
||||
showDeleteModal: false,
|
||||
reloadFlag: 0,
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
methods: {
|
||||
getMyCommunityList,
|
||||
deleteAction(data) {
|
||||
this.showDeleteModal = true;
|
||||
this.deleteInfo = { ...data };
|
||||
},
|
||||
deleteCommunity() {
|
||||
uni.showLoading({
|
||||
title: "删除中",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
});
|
||||
deleteCommunity(this.deleteInfo.chongquan_id).then(() => {
|
||||
this.reloadFlag = Math.random();
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "删除成功",
|
||||
icon: "none",
|
||||
});
|
||||
this.showDeleteModal = false
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.community-list-container {
|
||||
height: 100%;
|
||||
padding: 20rpx 32rpx;
|
||||
background: #f7f8fa;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
BIN
src/pages/client/community/static/see.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/pages/client/community/static/share.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/pages/client/community/static/zan.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/pages/client/community/static/zan_active.png
Normal file
|
After Width: | Height: | Size: 784 B |
81
src/pages/client/coupon/get-list.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<view class="coupon-list-container">
|
||||
<list-page-temp
|
||||
:getDataPromise="getCouponData"
|
||||
:reloadFlag="reloadFlag"
|
||||
:requestData="{ is_lingqu: 1 }"
|
||||
>
|
||||
<template v-slot:item="{ data }">
|
||||
<coupon-item
|
||||
:data="data"
|
||||
optBtnText="领取"
|
||||
@useCoupon="receiveCoupon"
|
||||
/>
|
||||
</template>
|
||||
<view slot="bottom" class="place-view"></view>
|
||||
</list-page-temp>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListPageTemp from "@/components/ListPageTemp";
|
||||
import CouponItem from "@/components/coupon/CouponItem";
|
||||
import { getCouponData, receiveCoupon } from "@/api/coupon";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
reloadFlag: 0,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
ListPageTemp,
|
||||
CouponItem,
|
||||
},
|
||||
onShow() {
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
methods: {
|
||||
getCouponData,
|
||||
receiveCoupon(data) {
|
||||
uni.showLoading({
|
||||
title: "领取中",
|
||||
icon: 'none'
|
||||
});
|
||||
receiveCoupon(data.coupon_id)
|
||||
.then(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: "领取成功",
|
||||
});
|
||||
this.reloadFlag = Math.random();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: err || "领取失败",
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.coupon-list-container {
|
||||
height: 100%;
|
||||
padding: 20rpx 32rpx 0;
|
||||
background: #f7f8fa;
|
||||
|
||||
::v-deep {
|
||||
.coupon-price {
|
||||
color: $app_fc_alarm;
|
||||
}
|
||||
.coupon-item .item-bottom .circle {
|
||||
background: #f7f8fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
344
src/pages/client/coupon/list.vue
Normal file
@ -0,0 +1,344 @@
|
||||
<template>
|
||||
<view class="flex-column-start order-list-container">
|
||||
<tabs-list :list="tabsList" :isScroll="false" :currentIndex="curTabIndex" flexClass="flex-row-around"
|
||||
@change="onTabChange" />
|
||||
|
||||
<view class="order-list-content">
|
||||
<scroll-view
|
||||
class="coupon-scroll"
|
||||
scroll-y
|
||||
:refresher-enabled="true"
|
||||
:refresher-triggered="refreshTriggered"
|
||||
@refresherrefresh="onRefresh"
|
||||
@scrolltolower="onLoadMore">
|
||||
<view class="coupon-list" v-if="couponList.length > 0">
|
||||
<coupon-item
|
||||
v-for="(item, index) in couponList"
|
||||
:key="item.distribution_id || index"
|
||||
:data="item"
|
||||
:showOptBtn="false"
|
||||
:disabled="item.use_status === 2"
|
||||
:showCountDown="item.nearDisabled && item.daojishi"
|
||||
:past="curTabIndex === 2"
|
||||
@useCoupon="handleUseCoupon" />
|
||||
</view>
|
||||
<view v-else-if="!isLoading" class="empty-container">
|
||||
<image class="empty-image" mode="widthFix" src="https://activity.wagoo.live/empty.png" />
|
||||
<!-- <text class="empty-text">暂无优惠券</text> -->
|
||||
</view>
|
||||
<view v-if="isLoading && couponList.length === 0" class="loading-container">
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
<view v-if="hasMore && couponList.length > 0" class="load-more">
|
||||
<text class="load-more-text">{{ isLoading ? '加载中...' : '上拉加载更多' }}</text>
|
||||
</view>
|
||||
<view v-if="!hasMore && couponList.length > 0" class="no-more">
|
||||
<text class="no-more-text">没有更多了</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabsList from "@/components/TabsList.vue";
|
||||
import CouponItem from "@/components/coupon/CouponItem";
|
||||
|
||||
import {
|
||||
userCoupolistwo
|
||||
} from "../../../api/login";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TabsList,
|
||||
CouponItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
curTabIndex: 0,
|
||||
user_id: '',
|
||||
couponList: [],
|
||||
isLoading: false,
|
||||
refreshTriggered: false,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tabsList() {
|
||||
return [{
|
||||
name: "未使用",
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
name: "已使用",
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
name: "已过期",
|
||||
id: 3,
|
||||
},
|
||||
];
|
||||
},
|
||||
hasMore() {
|
||||
return this.total > this.couponList.length;
|
||||
},
|
||||
},
|
||||
options: {
|
||||
styleIsolation: "shared",
|
||||
},
|
||||
onShow() {
|
||||
this.refreshList();
|
||||
},
|
||||
onLoad() {
|
||||
const value = uni.getStorageSync("userInfo");
|
||||
this.user_id = value?.user_id || '';
|
||||
this.getCouponList();
|
||||
},
|
||||
methods: {
|
||||
onTabChange(item, index) {
|
||||
this.curTabIndex = index;
|
||||
this.refreshList();
|
||||
},
|
||||
refreshList() {
|
||||
this.page = 1;
|
||||
this.couponList = [];
|
||||
this.total = 0;
|
||||
this.getCouponList();
|
||||
},
|
||||
onRefresh() {
|
||||
if (this.refreshTriggered) return;
|
||||
this.refreshTriggered = true;
|
||||
this.refreshList();
|
||||
},
|
||||
onLoadMore() {
|
||||
if (this.isLoading || !this.hasMore) return;
|
||||
this.page++;
|
||||
this.getCouponList();
|
||||
},
|
||||
handleUseCoupon(couponData) {
|
||||
// 跳转到 index 页面并切换到预约页面
|
||||
uni.redirectTo({
|
||||
url: '/pages/client/index/index?activePageId=reservationPage',
|
||||
success: () => {
|
||||
// 如果 redirectTo 后页面已加载,也可以再触发一次确保切换
|
||||
setTimeout(() => {
|
||||
uni.$emit("changeTabBar", {
|
||||
pageId: 'reservationPage'
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
},
|
||||
getCouponList() {
|
||||
if (this.isLoading) return;
|
||||
this.isLoading = true;
|
||||
|
||||
const params = {
|
||||
p: this.page,
|
||||
num: this.pageSize,
|
||||
status: this.tabsList[this.curTabIndex].id,
|
||||
type: 0,
|
||||
user_id: this.user_id,
|
||||
};
|
||||
|
||||
userCoupolistwo(params)
|
||||
.then((res) => {
|
||||
// 处理接口返回的数据结构
|
||||
const list = Array.isArray(res?.data)
|
||||
? res.data
|
||||
: (res?.data?.list || []);
|
||||
|
||||
// 处理列表数据,添加额外字段
|
||||
const processedList = list.map(item => ({
|
||||
...item,
|
||||
nearDisabled: this.curTabIndex === 2,
|
||||
}));
|
||||
|
||||
if (this.page === 1) {
|
||||
this.couponList = processedList;
|
||||
} else {
|
||||
this.couponList = [...this.couponList, ...processedList];
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
this.total = res?.count ?? res?.data?.count ?? 0;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('获取优惠券列表失败', err);
|
||||
uni.showToast({
|
||||
title: err || '获取列表失败',
|
||||
icon: 'none',
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
this.refreshTriggered = false;
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-list-container {
|
||||
height: 100%;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
background: #ffecf3;
|
||||
|
||||
::v-deep {
|
||||
.list-wrapper {
|
||||
padding: 0 100rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-header {
|
||||
padding: 36rpx 40rpx;
|
||||
background: #fff;
|
||||
border-radius: 30rpx;
|
||||
box-sizing: border-box;
|
||||
width: 686rpx;
|
||||
margin-top: 24rpx;
|
||||
margin-left: 32rpx;
|
||||
|
||||
.coupon-header-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.header-btn {
|
||||
width: 132rpx;
|
||||
height: 52rpx;
|
||||
background: $app_fc_alarm;
|
||||
border-radius: 52rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.order-list-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding-left: 32rpx;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.coupon-scroll {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.coupon-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.empty-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 400rpx auto 0;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
.empty-image {
|
||||
width: 160px;
|
||||
height: 174px;
|
||||
display: block;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.load-more-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.no-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.no-more-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.coupon-price {
|
||||
color: $app_fc_alarm;
|
||||
}
|
||||
}
|
||||
|
||||
.use-icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.serviceVoucher {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
|
||||
.view {
|
||||
border: 1px solid #FF19A0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 90%;
|
||||
height: 48px;
|
||||
border-radius: 200rpx;
|
||||
|
||||
.c {
|
||||
color: #FF19A0;
|
||||
font-family: PingFang SC;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
123
src/pages/client/coupon/service-list.vue
Normal file
@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<view class="flex-column-start order-list-container">
|
||||
<tabs-list
|
||||
:list="tabsList"
|
||||
:isScroll="false"
|
||||
:currentIndex="curTabIndex"
|
||||
flexClass="flex-row-around"
|
||||
@change="onTabChange"
|
||||
/>
|
||||
<view class="order-list-content">
|
||||
<list-page-temp
|
||||
:requestData="{ status: tabsList[curTabIndex].id }"
|
||||
:getDataPromise="getMyServiceCouponList"
|
||||
:reloadFlag="reloadFlag"
|
||||
>
|
||||
<template v-slot:item="{ data }">
|
||||
<service-coupon-item
|
||||
:data="data"
|
||||
:showOptBtn="false"
|
||||
:disabled="data.status === 3 || data.status === 4"
|
||||
:showOrderBtn="data.status === 2"
|
||||
@jumpToDetails="jumpToDetails"
|
||||
>
|
||||
<image
|
||||
v-if="data.status === 3"
|
||||
slot="status"
|
||||
class="use-icon"
|
||||
src="./static/coupon_used.png"
|
||||
/>
|
||||
</service-coupon-item>
|
||||
</template>
|
||||
</list-page-temp>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabsList from "@/components/TabsList.vue";
|
||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
||||
import ServiceCouponItem from "@/components/coupon/ServiceCouponItem";
|
||||
|
||||
import { getMyServiceCouponList } from "@/api/coupon";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TabsList,
|
||||
ListPageTemp,
|
||||
ServiceCouponItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
curTabIndex: 0,
|
||||
reloadFlag: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tabsList() {
|
||||
return [
|
||||
{
|
||||
name: "待使用",
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
name: "已使用",
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
name: "已过期",
|
||||
id: 4,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
options: {
|
||||
styleIsolation: "shared",
|
||||
},
|
||||
onShow() {
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
methods: {
|
||||
getMyServiceCouponList,
|
||||
onTabChange(item, index) {
|
||||
this.curTabIndex = index;
|
||||
},
|
||||
jumpToDetails(data) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/service/details?id=${data.fuwuquan_id}`
|
||||
})
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-list-container {
|
||||
height: 100%;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
|
||||
::v-deep {
|
||||
.list-wrapper {
|
||||
padding: 0 100rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.order-list-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding-left: 32rpx;
|
||||
|
||||
::v-deep {
|
||||
.coupon-price {
|
||||
color: $app_fc_alarm;
|
||||
}
|
||||
}
|
||||
|
||||
.use-icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
src/pages/client/coupon/static/coupon_used.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
src/pages/client/coupon/static/get_coupon_icon.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
745
src/pages/client/home/index.vue
Normal file
@ -0,0 +1,745 @@
|
||||
<template>
|
||||
<view class="home-page">
|
||||
<scroll-view class="homeContainer" scroll-y :show-scrollbar="false" :enhanced="true">
|
||||
<view class="swiperWrapper">
|
||||
<swiper indicator-dots="true" autoplay="true" interval="3000" duration="500" circular="true"
|
||||
class="swiper">
|
||||
<swiper-item v-for="(item, index) in swiperDataList" :key="index">
|
||||
<image class="swiper-img" :src="item.image || `${imgPrefix}home-ground.png`" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<view class="userInfoWrapper">
|
||||
<view class="userInfo" @click="handleUserInfo">
|
||||
<image class="avatar-img"
|
||||
:src="userInfo.avatar ? userInfo.avatar : `${imgPrefix}defaultHeadImg.png`"
|
||||
mode="aspectFill" />
|
||||
<view class="userContent">
|
||||
<view class="userName">
|
||||
{{ userInfo.username ? userInfo.username : "嗨,你好呀" }}
|
||||
</view>
|
||||
<view class="userTips" v-if="!userInfo.userID">
|
||||
登陆享受更多精彩内容
|
||||
</view>
|
||||
<view v-else class="user-membership-row">
|
||||
<view class="vipWrapper">
|
||||
<image class="lableImg" :src="`${imgPrefix}home-vipLabel.png`" />
|
||||
v{{ userInfo.vipLevel ? userInfo.vipLevel : 0 }}会员
|
||||
</view>
|
||||
<view
|
||||
v-if="userInfo.membershipTier && userInfo.membershipTier > 0"
|
||||
class="membership-tier-badge"
|
||||
:class="{
|
||||
'membership-tier-1': userInfo.membershipTier === 1,
|
||||
'membership-tier-2': userInfo.membershipTier === 2,
|
||||
'membership-tier-3': userInfo.membershipTier === 3
|
||||
}"
|
||||
>
|
||||
<text class="membership-tier-text">{{ getMembershipTierText(userInfo.membershipTier) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="loginBtn" @click="toLogin" v-if="!userInfo.userID">
|
||||
注册/登陆
|
||||
</view>
|
||||
<view class="loginBtn flexClass" v-else @click="toCouponList">
|
||||
<view class="couponText">{{ userInfo.couponCount ? userInfo.couponCount : 0 }}张优惠券</view>
|
||||
<image :src="`${imgPrefix}home-rightWhite Arrow.png`" class="discountCoupon" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="shadowBackground" />
|
||||
</view>
|
||||
|
||||
<view class="menuBody">
|
||||
<view class="firstMenu">
|
||||
<view class="itemWrapper" @click="toReservation">
|
||||
<text class="titlWrapper" style="transform: translateY(-4rpx);">你们在哪我们去哪</text>
|
||||
<view class="content">预约洗护</view>
|
||||
<view class="tips">随时随地上车洗澡</view>
|
||||
<view class="itemImg">
|
||||
<image class="menu-img-lg" :src="`${imgPrefix}home-menuBath.png`" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="line" />
|
||||
<view class="itemWrapper" @click="toDogTraining">
|
||||
<text class="titlWrapper">狗狗训练</text>
|
||||
<view class="content">上门服务</view>
|
||||
<view class="tips">上门训犬、寄养、喂猫、遛狗</view>
|
||||
<view class="itemImg">
|
||||
<image class="menu-img-sm" :src="`${imgPrefix}home-dogTraining.png`" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="secondMenu">
|
||||
<scroll-view class="scrollWrapper" :scroll-x="secondMenuItemList.length > 4" enable-flex
|
||||
:scroll-with-animation="false"
|
||||
@scroll="onSecondMenuScroll">
|
||||
<view class="secondMenuInner" :class="{ 'no-scroll': secondMenuItemList.length <= 4 }">
|
||||
<view class="itemWrapper" v-for="(item, i) in secondMenuItemList" :key="i"
|
||||
@click="handleNav(item)">
|
||||
<view class="imgWrapper">
|
||||
<image class="second-icon" :src="item.img" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="itemTitle">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
<view class="itemTips">
|
||||
{{ item.tips }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="custom-indicator" v-if="secondMenuItemList.length > 4">
|
||||
<view class="itemLine" :class="{ active: secondMenuIndicatorIndex === 0 }"></view>
|
||||
<view class="itemLine" :class="{ active: secondMenuIndicatorIndex === 1 }"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="thirdMenu">
|
||||
<view class="itemWrapper" @click="toPublicBenefit">
|
||||
<view>
|
||||
<view class="title">公益助力</view>
|
||||
<view class="tips">帮我找个家</view>
|
||||
</view>
|
||||
<image class="third-icon" :src="`${imgPrefix}home-publicBenefit.png`" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="itemWrapper" @click="toJoin">
|
||||
<view>
|
||||
<view class="title">加盟咨询</view>
|
||||
<view class="tips">立即加盟咨询</view>
|
||||
</view>
|
||||
<image class="third-icon" :src="`${imgPrefix}home-joinIn.png`" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="serviceMenu" @click="jumpToWeChat">
|
||||
<view class="left">
|
||||
<image class="service-icon" :src="`${imgPrefix}supportStaff.png`" mode="aspectFill" />
|
||||
<view class="content">
|
||||
<view class="title">在线客服</view>
|
||||
<view class="tips">遇到什么问题您尽管说哦~</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<image :src="`${imgPrefix}right-arrow.png`" class="rightArrow" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
imgPrefix,
|
||||
jumpToWeChat,
|
||||
showLoginConfirmModal
|
||||
} from "@/utils/common";
|
||||
import {
|
||||
userWllet
|
||||
} from "../../../api/login";
|
||||
|
||||
export default {
|
||||
name: "HomePage",
|
||||
data() {
|
||||
return {
|
||||
imgPrefix,
|
||||
couponCount: 1,
|
||||
swiperDataList: [
|
||||
{
|
||||
image: `${imgPrefix}banner1.png`
|
||||
},
|
||||
{
|
||||
image: `${imgPrefix}bannerShare.png`
|
||||
}
|
||||
],
|
||||
secondMenuItemList: [{
|
||||
title: "邀请有礼",
|
||||
tips: "邀请好友得好礼",
|
||||
img: `${imgPrefix}home-invite .png`,
|
||||
naviUrl: "/pages/client/recharge/membership-code",
|
||||
params: {
|
||||
yaoqing_code: (userInfo) => userInfo?.userID || '',
|
||||
member_id: (userInfo) => userInfo?.userID || ''
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "领券中心",
|
||||
tips: "领取更多优惠券",
|
||||
img: `${imgPrefix}home-getCoupon.png`,
|
||||
naviUrl: "/pages/client/coupon/get-list",
|
||||
},
|
||||
{
|
||||
title: "充值有礼",
|
||||
tips: "最高返赠1650元",
|
||||
img: `${imgPrefix}home-recharge.png`,
|
||||
naviUrl: '/pages/client/recharge/index',
|
||||
params: {
|
||||
user_id: (userInfo) => userInfo?.userID || '',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "开通会员",
|
||||
tips: "服务享8折",
|
||||
img: `${imgPrefix}home-openVip.png`,
|
||||
naviUrl: "/pages/richText/member-interests"
|
||||
},
|
||||
],
|
||||
walletInfo: {}, // 钱包信息
|
||||
secondMenuIndicatorIndex: 0, // 第二排菜单指示器当前页 0=第一页 1=第二页
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
couponCountText() {
|
||||
return `${this.couponCount}张优惠券`;
|
||||
},
|
||||
userInfo() {
|
||||
return this.$store.state?.user?.userInfo || {};
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.buyService()
|
||||
|
||||
},
|
||||
methods: {
|
||||
toLogin() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/client/auth/index",
|
||||
});
|
||||
},
|
||||
// 统一的 token 检查方法,未登录时弹窗让用户自主选择
|
||||
async checkTokenAndExecute(callback) {
|
||||
const token = uni.getStorageSync('token');
|
||||
if (!token) {
|
||||
const willLogin = await showLoginConfirmModal();
|
||||
if (!willLogin) return;
|
||||
// 用户选择去登录,跳转后不执行 callback
|
||||
return;
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
||||
async handleUserInfo() {
|
||||
const token = uni.getStorageSync('token');
|
||||
if (token) {
|
||||
uni.navigateTo({
|
||||
url: "/pages/client/mine/userInfo",
|
||||
});
|
||||
} else {
|
||||
await showLoginConfirmModal();
|
||||
}
|
||||
},
|
||||
toReservation() {
|
||||
this.checkTokenAndExecute(() => {
|
||||
// 直接调用父组件的 handleTabChange 方法来切换 TabBar
|
||||
// 这是最可靠的方式,因为 home 组件是 index 组件的子组件
|
||||
if (this.$parent && typeof this.$parent.handleTabChange === 'function') {
|
||||
this.$parent.handleTabChange(["reservationPage"]);
|
||||
}
|
||||
});
|
||||
},
|
||||
toDogTraining() {
|
||||
uni.navigateTo({
|
||||
url: '/pageHome/service/index'
|
||||
});
|
||||
},
|
||||
// 我的钱包
|
||||
buyService() {
|
||||
const value = JSON.parse(uni.getStorageSync('vuex'));
|
||||
console.log(value, '??')
|
||||
this.userId = value.user.userInfo.userID
|
||||
this.nick_name = value.user.userInfo.username
|
||||
// this.yaoqing_code = value.user.userInfo.yaoqing_code
|
||||
// console.log(value,'--')
|
||||
userWllet(value.user.userInfo.userID).then((res) => {
|
||||
uni.setStorage({ //存入Storage
|
||||
key: 'userInfo',
|
||||
data: { //存的数据(可以是多条)
|
||||
'user_id': res.data.user_id,
|
||||
'wallet_id': res.data.id
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
onSecondMenuScroll(e) {
|
||||
const scrollLeft = e.detail.scrollLeft || 0;
|
||||
const sysInfo = uni.getSystemInfoSync();
|
||||
const windowWidth = sysInfo.windowWidth || 375;
|
||||
// 5项一屏4个,可滚动范围=1项宽≈windowWidth/4,过半即第二页
|
||||
const maxScroll = windowWidth / 4;
|
||||
this.secondMenuIndicatorIndex = scrollLeft >= maxScroll * 0.5 ? 1 : 0;
|
||||
},
|
||||
getMembershipTierText(tier) {
|
||||
|
||||
const tierMap = {
|
||||
1: '黄金会员',
|
||||
2: '白金会员',
|
||||
3: '黑金会员'
|
||||
};
|
||||
return tierMap[tier] || '';
|
||||
},
|
||||
handleNav(item) {
|
||||
this.checkTokenAndExecute(() => {
|
||||
// 健康顾问暂未开放
|
||||
if (item.title === '健康顾问') {
|
||||
uni.showToast({
|
||||
title: '暂未开放',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.naviUrl) {
|
||||
let url = item.naviUrl;
|
||||
|
||||
// 如果有 params 参数,进行拼接
|
||||
if (item.params && typeof item.params === 'object') {
|
||||
const params = [];
|
||||
for (const key in item.params) {
|
||||
const value = item.params[key];
|
||||
// 支持函数形式,动态获取值(可接收 userInfo 和 walletInfo)
|
||||
const paramValue = typeof value === 'function'
|
||||
? value(this.userInfo, this.walletInfo)
|
||||
: value;
|
||||
if (paramValue !== undefined && paramValue !== null) {
|
||||
params.push(`${key}=${encodeURIComponent(paramValue)}`);
|
||||
}
|
||||
}
|
||||
if (params.length > 0) {
|
||||
url += (url.includes('?') ? '&' : '?') + params.join('&');
|
||||
}
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: url,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
toCouponList() { // 前往优惠券
|
||||
uni.navigateTo({
|
||||
url: "/pages/client/coupon/list",
|
||||
});
|
||||
},
|
||||
toPublicBenefit() {
|
||||
this.checkTokenAndExecute(() => {
|
||||
uni.navigateTo({
|
||||
url: '/pageHome/welfare/index'
|
||||
});
|
||||
});
|
||||
},
|
||||
toJoin() {
|
||||
this.checkTokenAndExecute(() => {
|
||||
uni.navigateTo({
|
||||
url: '/pageHome/franchise/index'
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
jumpToWeChat() {
|
||||
this.checkTokenAndExecute(() => {
|
||||
jumpToWeChat();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home-page {
|
||||
height: 100vh;
|
||||
background-color: #ffecf3;
|
||||
}
|
||||
|
||||
.homeContainer {
|
||||
height: calc(100vh - #{$app_tabbar_height + 36});
|
||||
background-color: #ffecf3;
|
||||
overflow: scroll;
|
||||
|
||||
.avatar-img {
|
||||
width: 92rpx;
|
||||
height: 92rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.menu-img-lg {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.menu-img-sm {
|
||||
width: 120rpx;
|
||||
height: 164rpx;
|
||||
}
|
||||
|
||||
.second-icon,
|
||||
.third-icon,
|
||||
.service-icon {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
width: 66rpx;
|
||||
height: 66rpx;
|
||||
}
|
||||
|
||||
.swiperWrapper {
|
||||
position: relative;
|
||||
|
||||
.swiper {
|
||||
height: 552rpx;
|
||||
width: 100%;
|
||||
|
||||
.swiper-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.userInfoWrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
position: absolute;
|
||||
bottom: -5%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: calc(100% - 40rpx);
|
||||
padding: 32rpx 28rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 16rpx;
|
||||
z-index: 10;
|
||||
|
||||
.userInfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.userContent {
|
||||
margin-left: 16rpx;
|
||||
|
||||
.userName {
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.userTips {
|
||||
font-size: 20rpx;
|
||||
color: #808080;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.user-membership-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin-top: 16rpx;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.vipWrapper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background-color: #ff19a0;
|
||||
padding: 4rpx 8rpx;
|
||||
border-radius: 50px;
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
|
||||
.lableImg {
|
||||
width: 24rpx;
|
||||
height: 20rpx;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.membership-tier-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4rpx 8rpx;
|
||||
border-radius: 50px;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.membership-tier-1 {
|
||||
background-color: #EDCA69;
|
||||
}
|
||||
|
||||
.membership-tier-1 .membership-tier-text {
|
||||
color: #754410;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.membership-tier-2 {
|
||||
background-color: #CECECE;
|
||||
}
|
||||
|
||||
.membership-tier-2 .membership-tier-text {
|
||||
color: #3D3D3D;
|
||||
font-size: 20rpx;
|
||||
|
||||
}
|
||||
|
||||
.membership-tier-3 {
|
||||
background-color: #321500;
|
||||
}
|
||||
|
||||
.membership-tier-3 .membership-tier-text {
|
||||
color: #CBAD78;
|
||||
font-size: 20rpx;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loginBtn {
|
||||
background-color: #ff19a0;
|
||||
border-radius: 218px;
|
||||
color: #fff;
|
||||
font-size: 23rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.couponText {
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.discountCoupon {
|
||||
width: 11rpx;
|
||||
height: 18rpx;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shadowBackground {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -20rpx;
|
||||
z-index: 8;
|
||||
width: 100%;
|
||||
height: 72rpx;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #ffecf3 64%);
|
||||
}
|
||||
}
|
||||
|
||||
.menuBody {
|
||||
margin-top: 32rpx;
|
||||
padding: 20rpx;
|
||||
|
||||
.firstMenu {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.itemWrapper {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 2rpx;
|
||||
background-color: #e8e8e8;
|
||||
height: 280rpx;
|
||||
}
|
||||
|
||||
.titlWrapper {
|
||||
background-color: #ff19a0;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 0px 0px 12rpx 12rpx;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 20rpx;
|
||||
margin-top: 16rpx;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.itemImg {
|
||||
margin: auto;
|
||||
margin-top: 48rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.secondMenu {
|
||||
margin-top: 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.scrollWrapper {
|
||||
width: 100%;
|
||||
height: auto; /* 由内容撑开;小程序 scroll-view 横向滚动时默认可能不随内容计算高度 */
|
||||
min-height: 220rpx; /* 兜底:约等于 padding-top + 图标 + 标题 + 副标题 */
|
||||
display: flex;
|
||||
align-items: flex-start; /* 不拉伸子项,高度由内容决定 */
|
||||
white-space: nowrap;
|
||||
padding-top: 24rpx;
|
||||
padding-bottom: 24rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.secondMenuInner {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-shrink: 0;
|
||||
width: 937.5rpx; /* 5 * 187.5 一屏4个共5项 */
|
||||
|
||||
&.no-scroll {
|
||||
width: 100%; /* 四项时一屏排满,不滚动 */
|
||||
|
||||
.itemWrapper {
|
||||
flex: 1;
|
||||
width: 0; /* 均分宽度,避免被 menuBody padding 裁切 */
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.itemWrapper {
|
||||
width: 187.5rpx; /* 750/4 一屏固定4个 */
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
|
||||
.imgWrapper {
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.itemTitle {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.itemTips {
|
||||
font-size: 20rpx;
|
||||
color: #808080;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-indicator {
|
||||
display: flex;
|
||||
margin-top: 12rpx;
|
||||
justify-content: center;
|
||||
margin-bottom: 20rpx;
|
||||
gap: 12rpx;
|
||||
|
||||
.itemLine {
|
||||
background-color: #ededed;
|
||||
width: 20rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 170px;
|
||||
|
||||
&.active {
|
||||
background-color: #ff19a0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.thirdMenu {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.itemWrapper {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 24rpx;
|
||||
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 20rpx;
|
||||
color: #808080;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.serviceMenu {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
margin-top: 20rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.rightArrow {
|
||||
width: 11rpx;
|
||||
height: 18rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-left: 16rpx;
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 22rpx;
|
||||
margin-top: 8rpx;
|
||||
color: #808080;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flexClass {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
255
src/pages/client/index/index.vue
Normal file
@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<view class="index-container">
|
||||
<view class="tab-page-wrapper">
|
||||
<!-- 使用 v-if 替代 v-show,只渲染当前页面以提升性能 -->
|
||||
<home
|
||||
v-if="activePageId === 'homePage'"
|
||||
ref="homePage"
|
||||
/>
|
||||
<mine
|
||||
v-if="activePageId === 'minePage'"
|
||||
ref="minePage"
|
||||
/>
|
||||
<shop
|
||||
v-if="activePageId === 'shopPage'"
|
||||
ref="shopPage"
|
||||
/>
|
||||
<pet-profile
|
||||
v-if="activePageId === 'filesPage'"
|
||||
ref="filesPage"
|
||||
/>
|
||||
<reservation
|
||||
v-show="activePageId === 'reservationPage'"
|
||||
ref="reservationPage"
|
||||
/>
|
||||
</view>
|
||||
<tab-bar
|
||||
:activePageId="activePageId"
|
||||
@changeTab="handleTabChange"
|
||||
class="tab-bar-view"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabBar from "@/components/TabBar.vue";
|
||||
import Home from "../home/index.vue";
|
||||
import Mine from "../mine/index.vue";
|
||||
import Shop from "../shop/index.vue";
|
||||
import PetProfile from "../pet-profile/index.vue";
|
||||
import Reservation from "../../../page-reser/reservation/index.vue";
|
||||
import { getUserInfo } from "../../../api/user";
|
||||
import appConfig from "@/constants/app.config";
|
||||
import { getCommunityDetail } from "../../../api/community";
|
||||
|
||||
export default {
|
||||
name: "IndexPage",
|
||||
components: {
|
||||
TabBar,
|
||||
Home,
|
||||
Mine,
|
||||
Shop,
|
||||
PetProfile,
|
||||
Reservation,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activePageId: "homePage",
|
||||
shareInfo: {
|
||||
title: appConfig.appShareName,
|
||||
path: "/pages/client/index/index",
|
||||
},
|
||||
getUserInfoPromise: null, // 用于防止重复调用 getUserInfo
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 处理 Tab 切换
|
||||
* @param {Array} params - [pageId] 页面ID数组
|
||||
* @param {*} otherInfo - 其他信息(如订单状态等)
|
||||
*/
|
||||
async handleTabChange([pageId], otherInfo) {
|
||||
if (this.activePageId === pageId) {
|
||||
return; // 避免重复切换
|
||||
}
|
||||
|
||||
// 需要登录的页面列表(除了首页)
|
||||
const needLoginPages = ['filesPage'];
|
||||
|
||||
// 如果需要登录且没有 token,弹窗让用户自主选择
|
||||
if (needLoginPages.includes(pageId)) {
|
||||
const token = uni.getStorageSync('token');
|
||||
if (!token) {
|
||||
const { showLoginConfirmModal } = await import('@/utils/common');
|
||||
const willLogin = await showLoginConfirmModal();
|
||||
if (!willLogin) return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.activePageId = pageId;
|
||||
// 切换到 mine 页面时,调用 getUserInfo 获取最新用户信息
|
||||
if (pageId === 'minePage') {
|
||||
this.getUserInfo();
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.triggerPageShow(pageId, otherInfo);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 触发页面 onShow 方法
|
||||
* @param {String} pageId - 页面ID
|
||||
* @param {*} otherInfo - 其他信息
|
||||
*/
|
||||
triggerPageShow(pageId, otherInfo) {
|
||||
// Reservation 组件不需要触发更新
|
||||
const pageRef = this.$refs[pageId];
|
||||
if (pageRef && typeof pageRef.onShowFun === "function") {
|
||||
pageRef.onShowFun(otherInfo);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取用户信息(带防重复调用机制)
|
||||
*/
|
||||
getUserInfo() {
|
||||
// 如果正在调用中,直接返回之前的 Promise
|
||||
if (this.getUserInfoPromise) {
|
||||
return this.getUserInfoPromise;
|
||||
}
|
||||
// 创建新的 Promise 并保存
|
||||
this.getUserInfoPromise = getUserInfo()
|
||||
.then((res) => {
|
||||
this.$store.dispatch("user/setUserInfo", res?.data || {});
|
||||
this.getUserInfoPromise = null; // 调用完成后清空
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("获取用户信息失败:", err);
|
||||
this.getUserInfoPromise = null; // 调用失败后清空
|
||||
throw err;
|
||||
});
|
||||
return this.getUserInfoPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取圈子详情
|
||||
* @param {String|Number} id - 圈子ID
|
||||
*/
|
||||
getCommunityDetail(id) {
|
||||
if (!id) return;
|
||||
|
||||
getCommunityDetail({
|
||||
chongquan_id: id,
|
||||
is_see: 0,
|
||||
is_share: 1,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理分享成功事件
|
||||
* @param {String|Number} communityId - 圈子ID
|
||||
*/
|
||||
handleShareSuccess(communityId) {
|
||||
if (communityId) {
|
||||
this.getCommunityDetail(communityId);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
onLoad(option) {
|
||||
console.log("首页 onLoad", option);
|
||||
// 处理二维码扫描的 referrerID,写入 vuex(App.onLaunch 也会处理一次,这里兜底)
|
||||
if (option?.referrerID) {
|
||||
this.$store.dispatch('user/setReferrerID', Number(option.referrerID) || 0);
|
||||
}
|
||||
|
||||
// 处理页面跳转参数
|
||||
if (option?.activePageId) {
|
||||
const targetPageId = option.activePageId;
|
||||
// 如果目标页面与当前页面不同,触发切换
|
||||
if (this.activePageId !== targetPageId) {
|
||||
this.handleTabChange([targetPageId]);
|
||||
} else {
|
||||
this.activePageId = targetPageId;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理分享宠圈
|
||||
if (option?.communityId) {
|
||||
this.getCommunityDetail(option.communityId);
|
||||
}
|
||||
|
||||
// 监听外部切换 TabBar 事件
|
||||
uni.$on("changeTabBar", this.handleExternalTabChange);
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.getUserInfo();
|
||||
this.$nextTick(() => {
|
||||
this.triggerPageShow(this.activePageId);
|
||||
});
|
||||
},
|
||||
|
||||
onShareAppMessage(res) {
|
||||
// 来自页面内分享按钮
|
||||
if (res.from === "button") {
|
||||
// 延迟触发分享成功事件
|
||||
setTimeout(() => {
|
||||
console.log("分发分享成功事件");
|
||||
uni.$emit("shareSuccess", this.shareInfo?.communityId);
|
||||
}, 3000);
|
||||
|
||||
return {
|
||||
title: this.shareInfo.title,
|
||||
path: this.shareInfo.path,
|
||||
imageUrl: this.shareInfo.imageUrl,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...this.shareInfo,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理外部切换 TabBar 事件
|
||||
* @param {Object} pageInfo - 页面信息 {pageId, orderState}
|
||||
*/
|
||||
handleExternalTabChange(pageInfo) {
|
||||
const { pageId, orderState } = pageInfo || {};
|
||||
console.log(111)
|
||||
if (pageId) {
|
||||
this.handleTabChange([pageId], orderState);
|
||||
}
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
// 清理事件监听
|
||||
uni.$off("changeTabBar", this.handleExternalTabChange);
|
||||
uni.$off("shareSuccess", this.handleShareSuccess);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.index-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
|
||||
|
||||
.tab-page-wrapper {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
padding-bottom: calc(#{$app_tabbar_height});
|
||||
}
|
||||
|
||||
.tab-bar-view {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
64
src/pages/client/mine/aboutus.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<view class="about-us">
|
||||
<!-- <view class="flex-row-center">
|
||||
<image class="logo-icon" :src="logoImg" />
|
||||
</view> -->
|
||||
<view
|
||||
class="fs-32 app-fc-main SourceHanSansCN-Regular about-us-inner"
|
||||
v-html="content"
|
||||
></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getArticleDetail } from "../../../api/article";
|
||||
import { ARTICLE_TYPE_ABOUT_US } from "@/constants/app.business";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
logoImg: "",
|
||||
content: "",
|
||||
id: ARTICLE_TYPE_ABOUT_US
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getDetail(id) {
|
||||
getArticleDetail(id).then(
|
||||
res => {
|
||||
this.logoImg = res.data.article_thumb
|
||||
this.content = res.data.content
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
onLoad(option) {
|
||||
this.getDetail(this.id);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.about-us {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.logo-icon {
|
||||
width: 216rpx;
|
||||
height: 216rpx;
|
||||
border-radius: 42rpx;
|
||||
margin: 34rpx 0;
|
||||
}
|
||||
|
||||
.about-us-inner {
|
||||
line-height: 72rpx;
|
||||
padding: 0 32rpx 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
49
src/pages/client/mine/help.vue
Normal file
@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<view class="article-container">
|
||||
<view class="article-content" v-html="content"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getArticleDetail } from "../../../api/article";
|
||||
import { ARTICLE_TYPE_HELP } from "@/constants/app.business";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
id: ARTICLE_TYPE_HELP,
|
||||
content: "",
|
||||
};
|
||||
},
|
||||
mounted() {},
|
||||
onLoad(option) {
|
||||
this.getDetail(this.id);
|
||||
},
|
||||
methods: {
|
||||
getDetail(id) {
|
||||
getArticleDetail(id).then(
|
||||
res => {
|
||||
this.content = res.data.content
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.article-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
padding-bottom: 20rpx;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
padding-top: 20rpx;
|
||||
|
||||
.article-content {
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1011
src/pages/client/mine/index.vue
Normal file
332
src/pages/client/mine/userInfo.vue
Normal file
@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<view class="mine-user-edit">
|
||||
<view class="edit-content">
|
||||
<view class="flex-row-between edit-cell">
|
||||
<text class="title">头像</text>
|
||||
<button class="flex-row-end user-avator" open-type="chooseAvatar" @chooseavatar="chooseavatar">
|
||||
<image class="avator-icon" :src="userInfo.head_pic_url" />
|
||||
<!-- <image class="arrow-icon" :src="`${imgPrefix}right-arrow.png`" /> -->
|
||||
</button>
|
||||
</view>
|
||||
<view class="flex-row-between edit-cell">
|
||||
<text class="title">昵称</text>
|
||||
<input class="fs-24 app-fc-main SourceHanSansCN-Medium edit-input" type="nickname"
|
||||
placeholder-class="fs-24 app-fc-normal SourceHanSansCN-Regular app-text-right"
|
||||
:value="userInfo.nick_name" placeholder="点击输入昵称" @change="
|
||||
(e) =>
|
||||
onChange(e.detail.value && e.detail.value.trim(), 'nick_name')
|
||||
|
||||
" />
|
||||
</view>
|
||||
<form-cell title="出生年月" type="checkDate" placeholderText="选择出生日期" :value="userInfo.birthday"
|
||||
:defaultDate="userInfo.birthday" @onChange="(value) => onChange(value, 'birthday')" />
|
||||
<form-cell title="性别" type="custom" :showRightArrow="false" :noBorder="true">
|
||||
<view class="flex-row-end edit-sex" slot="right">
|
||||
<view class="flex-center fs-24 app-fc-main SourceHanSansCN-Medium sex-item"
|
||||
:class="[userInfo.sex === 1 ? 'active man' : '']" @click="onChange(1, 'sex')">
|
||||
|
||||
<image class="sex-item-img"
|
||||
:src="userInfo.sex === 1 ? `${imgPrefix}whiteMale.png` : `${imgPrefix}record-maleImg.png`">
|
||||
</image>
|
||||
|
||||
</view>
|
||||
<view class="flex-center fs-24 app-fc-main SourceHanSansCN-Medium sex-item"
|
||||
:class="[userInfo.sex === 2 ? 'active women' : '']" @click="onChange(2, 'sex')">
|
||||
<image class="sex-item-img"
|
||||
:src="userInfo.sex === 2 ? `${imgPrefix}whiteFemale.png` : `${imgPrefix}record-femaleImg.png`">
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
</form-cell>
|
||||
</view>
|
||||
|
||||
<view class="footer">
|
||||
<view class="footerBtn" @click="onsubmit()">
|
||||
保存
|
||||
</view>
|
||||
<view class="bottom-safe-area"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormCell from "../../../components/FormCell.vue";
|
||||
import {
|
||||
updateUserInfo,
|
||||
getUserInfo
|
||||
} from "../../../api/user";
|
||||
import appConfig from "../../../constants/app.config";
|
||||
|
||||
import {
|
||||
imgPrefix
|
||||
} from "@/utils/common";
|
||||
import {
|
||||
uploadImageToOSS_PUT
|
||||
} from "@/utils/oss";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FormCell,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
userInfo: {
|
||||
head_pic_url: "",
|
||||
head_pic: "",
|
||||
nick_name: "",
|
||||
birthday: "",
|
||||
sex: 0,
|
||||
},
|
||||
appConfig,
|
||||
imgPrefix
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
// 直接从 store(缓存)中获取用户信息并映射到表单字段
|
||||
const storeUserInfo = this.$store.state?.user?.userInfo || {};
|
||||
if (storeUserInfo && Object.keys(storeUserInfo).length > 0) {
|
||||
// 将 gender 转换为 sex: "male" -> 1, "female" -> 2, 其他 -> 0
|
||||
let sex = 0;
|
||||
if (storeUserInfo.gender === 'male') {
|
||||
sex = 1;
|
||||
} else if (storeUserInfo.gender === 'female') {
|
||||
sex = 2;
|
||||
} else if (storeUserInfo.sex) {
|
||||
// 兼容旧的 sex 字段
|
||||
sex = storeUserInfo.sex;
|
||||
}
|
||||
|
||||
// 格式化生日:将 ISO 8601 格式 (2006-01-02T00:00:00+08:00) 转换为 YYYY-MM-DD 格式
|
||||
let birthday = storeUserInfo.birthday || '';
|
||||
if (birthday) {
|
||||
// 如果是 ISO 8601 格式,提取日期部分
|
||||
if (birthday.includes('T')) {
|
||||
birthday = birthday.split('T')[0];
|
||||
}
|
||||
// 确保格式为 YYYY-MM-DD
|
||||
const dateMatch = birthday.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
||||
if (!dateMatch) {
|
||||
// 如果不是标准格式,尝试转换
|
||||
const date = new Date(birthday);
|
||||
if (!isNaN(date.getTime())) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
birthday = `${year}-${month}-${day}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.userInfo = {
|
||||
head_pic_url: storeUserInfo.avatar || storeUserInfo.head_pic_url || storeUserInfo.head_pic || '',
|
||||
head_pic: storeUserInfo.avatar || storeUserInfo.head_pic || storeUserInfo.head_pic_url || '',
|
||||
nick_name: storeUserInfo.nickname || storeUserInfo.nick_name || storeUserInfo.username || '',
|
||||
birthday: birthday,
|
||||
sex: sex,
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 保存
|
||||
onsubmit() {
|
||||
const {
|
||||
head_pic,
|
||||
nick_name,
|
||||
birthday,
|
||||
sex
|
||||
} = this.userInfo;
|
||||
if (!head_pic || !nick_name) {
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: "请将昵称和头像填写完整",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 将 sex 转换为 gender: 1 -> "male", 2 -> "female", 其他 -> "other"
|
||||
let gender = "other";
|
||||
if (sex === 1) {
|
||||
gender = "male";
|
||||
} else if (sex === 2) {
|
||||
gender = "female";
|
||||
}
|
||||
uni.showLoading({
|
||||
title: "处理中..."
|
||||
});
|
||||
|
||||
// 构建请求参数
|
||||
const params = {
|
||||
username: nick_name,
|
||||
gender: gender,
|
||||
birthday: birthday,
|
||||
};
|
||||
|
||||
// 如果头像地址不是 https 开头,才传给后端
|
||||
if (head_pic && !head_pic.startsWith('https://')) {
|
||||
params.avatar = head_pic;
|
||||
}
|
||||
|
||||
updateUserInfo(params).then(() => {
|
||||
uni.hideLoading();
|
||||
uni.navigateBack();
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
console.error('更新用户信息失败:', err);
|
||||
});
|
||||
},
|
||||
// 更新头像
|
||||
async chooseavatar(e) {
|
||||
const {
|
||||
avatarUrl
|
||||
} = e.detail;
|
||||
|
||||
uni.showLoading({
|
||||
title: "上传中..."
|
||||
});
|
||||
|
||||
try {
|
||||
const { url, objectKey } = await uploadImageToOSS_PUT(avatarUrl);
|
||||
console.log(url, objectKey, 'url, objectKey')
|
||||
this.userInfo.head_pic_url = url; // 同时更新 head_pic_url,因为模板使用的是这个字段
|
||||
this.userInfo.head_pic = objectKey;
|
||||
this.$forceUpdate();
|
||||
uni.hideLoading();
|
||||
} catch (error) {
|
||||
console.error('头像上传失败:', error);
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: error?.message || "头像上传失败",
|
||||
icon: "none"
|
||||
});
|
||||
}
|
||||
},
|
||||
onChange(value, key) {
|
||||
this.userInfo[key] = value;
|
||||
this.$forceUpdate();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.mine-user-edit {
|
||||
background-color: #ffecf3;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.edit-content {
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0rpx 24rpx;
|
||||
width: calc(100vw - 40rpx);
|
||||
margin: 20rpx auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
.edit-cell {
|
||||
border-bottom: 1rpx solid #ececec;
|
||||
height: 110rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.title {
|
||||
color: #3D3D3D;
|
||||
font-size: 24rpx;
|
||||
|
||||
.required {
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
|
||||
.avator-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 80rpx;
|
||||
background: gray;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
width: 11rpx;
|
||||
height: 18rpx;
|
||||
}
|
||||
|
||||
.edit-input {
|
||||
height: 100%;
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
margin-left: 30rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-sex {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sex-item {
|
||||
width: 120rpx;
|
||||
height: 64rpx;
|
||||
background: #f9f7f9;
|
||||
border-radius: 16rpx 16rpx 16rpx 16rpx;
|
||||
margin-left: 50rpx;
|
||||
|
||||
&-img {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #fff;
|
||||
|
||||
&.man {
|
||||
background: #FF19A0;
|
||||
}
|
||||
|
||||
&.women {
|
||||
background: #FF19A0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-avator {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
border-color: transparent;
|
||||
padding-left: 100rpx;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #fff;
|
||||
border-radius: 32rpx 32rpx 0px 0px;
|
||||
|
||||
.footerBtn {
|
||||
color: #fff;
|
||||
background-color: #FF19A0;
|
||||
width: calc(100% - 48rpx);
|
||||
margin: auto;
|
||||
margin-bottom: 24rpx;
|
||||
margin-top: 12rpx;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
padding: 32rpx 0rpx;
|
||||
}
|
||||
|
||||
.bottom-safe-area {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
/* 兼容 iOS < 11.2 */
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
/* 兼容 iOS >= 11.2 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
750
src/pages/client/mine/welfare.vue
Normal file
@ -0,0 +1,750 @@
|
||||
<template>
|
||||
<view class="welfare-page">
|
||||
<!-- Tab 导航 -->
|
||||
<view class="tab-bar">
|
||||
<view class="tab-item" :class="{ 'tab-active': activeTab === 'certificates' }" @click="switchTab('certificates')">
|
||||
<text class="tab-text">荣誉证书</text>
|
||||
<view class="tab-line" :class="{ 'tab-line-active': activeTab === 'certificates' }"></view>
|
||||
</view>
|
||||
<view class="tab-item" :class="{ 'tab-active': activeTab === 'adoptions' }" @click="switchTab('adoptions')">
|
||||
<text class="tab-text">领养记录</text>
|
||||
<view class="tab-line" :class="{ 'tab-line-active': activeTab === 'adoptions' }"></view>
|
||||
</view>
|
||||
<view class="tab-item" :class="{ 'tab-active': activeTab === 'applications' }" @click="switchTab('applications')">
|
||||
<text class="tab-text">申请记录</text>
|
||||
<view class="tab-line" :class="{ 'tab-line-active': activeTab === 'applications' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 荣誉证书内容 -->
|
||||
<view v-show="activeTab === 'certificates'" class="tab-content">
|
||||
<view class="certificate-grid" :class="{ 'empty-container': !certificateList || certificateList.length === 0 }">
|
||||
<template v-if="certificateList && certificateList.length > 0">
|
||||
<view v-for="(item, index) in certificateList" :key="item.id || index" class="certificate-card">
|
||||
<image :src="item.certificate_url" mode="aspectFit" class="certificate-image" @error="handleImageError" />
|
||||
</view>
|
||||
</template>
|
||||
<view v-else class="certificate-placeholder">
|
||||
<image :src="`${imgPrefix}certificatePlaceholder.png`" mode="aspectFit" class="placeholder-image" />
|
||||
<text class="placeholder-text">暂无内容</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 领养记录内容 -->
|
||||
<view v-show="activeTab === 'adoptions'" class="tab-content">
|
||||
<view class="record-list">
|
||||
<view v-for="(item, index) in adoptionList" :key="item.id || index" class="record-item"
|
||||
@click="() => viewAdoptionDetail(item)">
|
||||
<image class="record-pet-image" :src="item.pet_image_url || getDefaultPetImage()" mode="aspectFill"></image>
|
||||
<view class="record-info">
|
||||
<text class="record-pet-name">{{ item.pet_nickname || '未知' }}</text>
|
||||
<text class="record-time">{{ formatTime(item.created_at) }}</text>
|
||||
</view>
|
||||
<view class="record-status" :class="{
|
||||
'status-approved': item.status === 'approved' || item.status === 1,
|
||||
'status-rejected': item.status === 'rejected' || item.status === 0,
|
||||
'status-pending': !(item.status === 'approved' || item.status === 1 || item.status === 'rejected' || item.status === 0)
|
||||
}">
|
||||
<text class="status-text">{{ getStatusText(item.status) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="adoptionList.length === 0" class="empty-state">
|
||||
<text class="empty-text">暂无领养记录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申请记录内容 -->
|
||||
<view v-show="activeTab === 'applications'" class="tab-content">
|
||||
<view class="application-list-content">
|
||||
<view v-for="(item, index) in applicationList" :key="item.id || index" class="adopt-card"
|
||||
@click="() => viewApplicationDetail(item)">
|
||||
<view class="adopt-card-content">
|
||||
<!-- 左侧图片 -->
|
||||
<view class="pet-image-wrapper">
|
||||
<image class="pet-image" :src="getPetImage(item)" mode="aspectFill" />
|
||||
</view>
|
||||
|
||||
<!-- 右侧信息 -->
|
||||
<view class="pet-info">
|
||||
<!-- 名称、性别、年龄 -->
|
||||
<view class="pet-header">
|
||||
<text class="pet-name">{{ item.pet_nickname || '未知' }}</text>
|
||||
<image v-if="item.gender" class="pet-gender"
|
||||
:src="item.gender === 'male' ? imgPrefix + 'record-maleImg.png' : imgPrefix + 'record-femaleImg.png'"
|
||||
mode="aspectFit" />
|
||||
<text v-if="item.gender" class="pet-divider">|</text>
|
||||
<text v-if="item.pet_age" class="pet-age">{{ item.pet_age + '个月' }}</text>
|
||||
<view class="status-label" :class="{
|
||||
'status-pending': item.status === 1,
|
||||
'status-reviewing': item.status === 2,
|
||||
'status-completed': item.status === 3
|
||||
}">
|
||||
<text class="status-label-text">{{ getApplicationStatusText(item.status) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 描述文字 -->
|
||||
<view class="pet-description" v-if="item.description">
|
||||
<text class="description-text">{{ item.description }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 状态标签 -->
|
||||
<view class="pet-tags">
|
||||
<view class="tag" :class="item.is_immunized === 1 ? 'tag-completed' : 'tag-pending'">
|
||||
<text class="tag-text">{{ item.is_immunized === 1 ? '已免疫' : '未免疫' }}</text>
|
||||
</view>
|
||||
<view class="tag" :class="item.is_sterilized === 1 ? 'tag-completed' : 'tag-pending'">
|
||||
<text class="tag-text">{{ item.is_sterilized === 1 ? '已驱虫' : '未驱虫' }}</text>
|
||||
</view>
|
||||
<view class="tag" :class="item.is_dewormed === 1 ? 'tag-completed' : 'tag-pending'">
|
||||
<text class="tag-text">{{ item.is_dewormed === 1 ? '已绝育' : '未绝育' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 撤销申请按钮(待处理状态时显示) -->
|
||||
<view v-if="item.status === 1" class="cancel-application-btn" @click.stop="() => handleCancelApplication(item)">
|
||||
<text class="cancel-application-text">撤销申请</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="applicationList.length === 0" class="empty-state">
|
||||
<text class="empty-text">暂无申请记录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 撤销申请确认弹窗 -->
|
||||
<pop-up-modal v-if="isShowCancelApplicationModal" content="确定要撤销申请吗?" @confirm="confirmCancelApplication"
|
||||
@cancel="isShowCancelApplicationModal = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCertificates as getCertificatesApi } from '@/api/user';
|
||||
import { getMyApplications, cancelPetApply } from '@/api/common';
|
||||
import { imgPrefix } from '@/utils/common';
|
||||
import moment from 'moment';
|
||||
import PopUpModal from '@/components/PopUpModal.vue';
|
||||
|
||||
export default {
|
||||
name: 'MyWelfare',
|
||||
components: {
|
||||
PopUpModal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imgPrefix,
|
||||
activeTab: 'certificates', // 'certificates', 'adoptions', 'applications'
|
||||
certificateList: [],
|
||||
adoptionList: [],
|
||||
applicationList: [],
|
||||
applicationPage: 1,
|
||||
applicationPageSize: 10,
|
||||
isShowCancelApplicationModal: false,
|
||||
cancelApplicationInfo: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
userName() {
|
||||
const userInfo = this.$store.state?.user?.userInfo || {};
|
||||
return userInfo.username || userInfo.nickname || '用户';
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
const tab = (options && options.tab) || '';
|
||||
if (tab === 'applications') {
|
||||
this.activeTab = 'applications';
|
||||
this.loadApplications();
|
||||
} else {
|
||||
this.loadCertificates();
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// 每次页面显示时,如果当前是证书 tab,重新加载数据
|
||||
if (this.activeTab === 'certificates') {
|
||||
this.loadCertificates();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
switchTab(tab) {
|
||||
this.activeTab = tab;
|
||||
if (tab === 'certificates') {
|
||||
// 切换到证书 tab 时,重新加载数据
|
||||
this.loadCertificates();
|
||||
} else if (tab === 'adoptions' && this.adoptionList.length === 0) {
|
||||
this.loadAdoptions();
|
||||
} else if (tab === 'applications' && this.applicationList.length === 0) {
|
||||
this.loadApplications();
|
||||
}
|
||||
},
|
||||
loadCertificates() {
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
});
|
||||
getCertificatesApi().then((res) => {
|
||||
uni.hideLoading();
|
||||
// this.certificateList = res.data || [];
|
||||
console.log('证书列表数据:', this.certificateList);
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
console.error('获取证书列表失败:', err);
|
||||
this.certificateList = [];
|
||||
uni.showToast({
|
||||
title: err || '获取证书列表失败',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
},
|
||||
handleImageError(e) {
|
||||
console.error('图片加载失败:', e);
|
||||
},
|
||||
loadAdoptions() {
|
||||
// TODO: 调用领养记录接口
|
||||
this.adoptionList = [];
|
||||
},
|
||||
loadApplications() {
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
});
|
||||
getMyApplications({
|
||||
page: this.applicationPage,
|
||||
page_size: this.applicationPageSize
|
||||
}).then((res) => {
|
||||
uni.hideLoading();
|
||||
this.applicationList = res.data || [];
|
||||
console.log('申请记录列表数据:', this.applicationList);
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
console.error('获取申请记录失败:', err);
|
||||
this.applicationList = [];
|
||||
uni.showToast({
|
||||
title: err || '获取申请记录失败',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
},
|
||||
viewAdoptionDetail(item) {
|
||||
uni.navigateTo({
|
||||
url: `/pageHome/welfare/pet-detail?id=${item.pet_id || item.id}`
|
||||
});
|
||||
},
|
||||
viewApplicationDetail(item) {
|
||||
const adoptionId = item.adoption_id || item.id;
|
||||
if (!adoptionId) return;
|
||||
uni.navigateTo({
|
||||
url: `/pageHome/welfare/pet-detail?adoption_id=${adoptionId}&from=applications`,
|
||||
success: (res) => {
|
||||
res.eventChannel.on('refreshApplications', () => {
|
||||
this.loadApplications();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handleCancelApplication(item) {
|
||||
// 显示撤销申请确认弹窗
|
||||
if (!item) {
|
||||
console.error('撤销申请:item 未定义');
|
||||
uni.showToast({
|
||||
title: "数据错误",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
console.log('撤销申请信息:', item);
|
||||
this.cancelApplicationInfo = item;
|
||||
this.isShowCancelApplicationModal = true;
|
||||
},
|
||||
confirmCancelApplication() {
|
||||
// 确认撤销申请
|
||||
const cancelInfo = this.cancelApplicationInfo;
|
||||
if (!cancelInfo) {
|
||||
this.isShowCancelApplicationModal = false;
|
||||
uni.showToast({
|
||||
title: "数据错误",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const adoptionId = cancelInfo.adoption_id || cancelInfo.id;
|
||||
if (!adoptionId) {
|
||||
uni.showToast({
|
||||
title: "缺少必要参数",
|
||||
icon: "none",
|
||||
});
|
||||
this.isShowCancelApplicationModal = false;
|
||||
this.cancelApplicationInfo = null;
|
||||
return;
|
||||
}
|
||||
this.isShowCancelApplicationModal = false;
|
||||
uni.showLoading({
|
||||
title: "正在撤销申请",
|
||||
mask: true,
|
||||
});
|
||||
|
||||
cancelPetApply({
|
||||
adoption_id: adoptionId
|
||||
})
|
||||
.then(() => {
|
||||
uni.hideLoading();
|
||||
this.cancelApplicationInfo = null;
|
||||
uni.showToast({
|
||||
title: "撤销成功",
|
||||
icon: "success",
|
||||
});
|
||||
// 刷新申请记录列表
|
||||
this.loadApplications();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.hideLoading();
|
||||
this.cancelApplicationInfo = null;
|
||||
uni.showToast({
|
||||
title: err || "撤销申请失败",
|
||||
icon: "none",
|
||||
});
|
||||
});
|
||||
},
|
||||
getDefaultCertificateImage(index) {
|
||||
// 返回默认证书图片路径
|
||||
const defaultImages = [
|
||||
'welfare-cert-1.png',
|
||||
'welfare-cert-2.png',
|
||||
'welfare-cert-3.png'
|
||||
];
|
||||
return `${this.imgPrefix}${defaultImages[index % defaultImages.length]}`;
|
||||
},
|
||||
getCertificateTitle(index) {
|
||||
const titles = [
|
||||
'爱心猫粮证书',
|
||||
'爱心狗粮证书',
|
||||
'投喂达人证书',
|
||||
'为爱续航证书',
|
||||
'予爱同行证书',
|
||||
'以爱为名证书',
|
||||
'自在摇曳证书',
|
||||
'万物生长证书',
|
||||
'生生不息证书',
|
||||
'哇们爱宠官证书',
|
||||
'拆蛋专家证书',
|
||||
'哇们爱心使者证书'
|
||||
];
|
||||
return titles[index] || '荣誉证书';
|
||||
},
|
||||
getDefaultPetImage() {
|
||||
return `${this.imgPrefix}home-head.png`;
|
||||
},
|
||||
getPetImage(item) {
|
||||
// 如果有图片,使用图片
|
||||
if (item.pet_image_url) {
|
||||
return item.pet_image_url;
|
||||
}
|
||||
return 'https://qcloud.dpfile.com/pc/YqyJiv76-rg2R6TRfCBcanAGqub6NNziEZdnXFdG23T2y5o8aoN_P8m9D9Q8eeWh.jpg';
|
||||
},
|
||||
formatTime(time) {
|
||||
if (!time) return '';
|
||||
return moment(time).format('YYYY-MM-DD HH:mm');
|
||||
},
|
||||
getStatusClass(status) {
|
||||
// 根据状态返回样式类
|
||||
if (status === 'approved' || status === 1) {
|
||||
return 'status-approved';
|
||||
} else if (status === 'rejected' || status === 0) {
|
||||
return 'status-rejected';
|
||||
}
|
||||
return 'status-pending';
|
||||
},
|
||||
getStatusText(status) {
|
||||
// 根据状态返回文本
|
||||
if (status === 'approved' || status === 1) {
|
||||
return '已通过';
|
||||
} else if (status === 'rejected' || status === 0) {
|
||||
return '已拒绝';
|
||||
}
|
||||
return '审核中';
|
||||
},
|
||||
getApplicationStatusText(status) {
|
||||
// 申请记录状态文本
|
||||
if (status === 1) {
|
||||
return '待处理';
|
||||
} else if (status === 2) {
|
||||
return '审核中';
|
||||
} else if (status === 3) {
|
||||
return '已完成';
|
||||
}
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.welfare-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f7f8fa;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
background-color: #fff;
|
||||
padding: 0 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
position: relative;
|
||||
|
||||
.tab-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.tab-line {
|
||||
width: 24rpx;
|
||||
height: 10rpx;
|
||||
background-color: transparent;
|
||||
border-radius: 100px;
|
||||
margin-top: 16rpx;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
&.tab-active {
|
||||
.tab-text {
|
||||
color: #FF19A0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tab-line-active {
|
||||
background-color: #FF19A0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 20rpx;
|
||||
min-height: calc(100vh - 200rpx);
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
&[v-show="false"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// 证书网格样式
|
||||
.certificate-grid {
|
||||
background-color: #fff;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.empty-container {
|
||||
min-height: calc(100vh - 200rpx);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.certificate-card {
|
||||
flex: 0 0 calc(100% / 3);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.certificate-image {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.certificate-placeholder {
|
||||
width: 100%;
|
||||
min-height: 400rpx;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx 20rpx;
|
||||
}
|
||||
|
||||
.placeholder-image {
|
||||
width: 213.15px;
|
||||
height: 160px;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
// 申请记录列表样式(按照 adopt-list 的 adopt-card 样式)
|
||||
.application-list-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.adopt-card {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.adopt-card-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.pet-image-wrapper {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.pet-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pet-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pet-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16rpx;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.pet-name {
|
||||
font-size: 28rpx;
|
||||
color: #3D3D3D;
|
||||
}
|
||||
|
||||
.pet-gender {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
.pet-divider {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin: 0 8rpx;
|
||||
}
|
||||
|
||||
.pet-age {
|
||||
font-size: 24rpx;
|
||||
color: #3D3D3D;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
margin-left: auto;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.status-pending {
|
||||
background-color: #FEF6FF;
|
||||
|
||||
.status-label-text {
|
||||
color: #FF19A0;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&.status-reviewing {
|
||||
background-color: #FEF6FF;
|
||||
|
||||
.status-label-text {
|
||||
color: #FF19A0;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&.status-completed {
|
||||
background-color: #E8F5E9;
|
||||
|
||||
.status-label-text {
|
||||
color: #4CAF50;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.status-label-text {
|
||||
font-size: 20rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.pet-description {
|
||||
margin-bottom: 16rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.description-text {
|
||||
font-size: 20rpx;
|
||||
color: #9B939A;
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
line-clamp: 3;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.pet-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tag-pending {
|
||||
background-color: #E5E5E5;
|
||||
}
|
||||
|
||||
.tag-pending .tag-text {
|
||||
color: #9B939A;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.tag-completed {
|
||||
background-color: #FF19A0;
|
||||
}
|
||||
|
||||
.tag-completed .tag-text {
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.tag-text {
|
||||
font-size: 20rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.cancel-application-btn {
|
||||
margin-top: 0;
|
||||
padding: 12rpx 18rpx;
|
||||
border-radius: 100px;
|
||||
border: 1rpx solid #9B939A;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-self: flex-end;
|
||||
|
||||
|
||||
.cancel-application-text {
|
||||
font-size: 24rpx;
|
||||
color: #272427;
|
||||
}
|
||||
}
|
||||
|
||||
// 领养记录列表样式(保留原有样式)
|
||||
.record-list {
|
||||
.record-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.record-pet-image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.record-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.record-pet-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.record-status {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
|
||||
&.status-approved {
|
||||
background-color: #E8F5E9;
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
&.status-rejected {
|
||||
background-color: #FFEBEE;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
&.status-pending {
|
||||
background-color: #FFF3E0;
|
||||
color: #FF9800;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 100rpx 0;
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
154
src/pages/client/news/components/NewsItem.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<view class="news-item" @click="$emit('toDetails', data)">
|
||||
<view class="news-icon-wrapper">
|
||||
<view class="news-icon">
|
||||
<image class="bell-icon-img" :src="`${imgPrefix}notice-tips.png`" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="news-content-wrapper">
|
||||
<view class="news-top-row">
|
||||
<view class="news-title">{{ data.title || "" }}</view>
|
||||
<view class="news-time">{{ time }}</view>
|
||||
</view>
|
||||
<view class="news-desc-row">
|
||||
<view class="news-desc">{{ data.desc || "" }}</view>
|
||||
<view v-if="!isRead" class="news-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { imgPrefix } from '@/utils/common';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imgPrefix
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
time() {
|
||||
if (this.data.add_time) {
|
||||
const date = new Date(this.data.add_time * 1000);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
isRead() {
|
||||
// 假设数据中有 is_read 字段,如果没有则默认为未读
|
||||
return this.data.is_read === 1 || this.data.read_status === 1;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.news-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 24rpx 32rpx;
|
||||
background: #fff;
|
||||
|
||||
.news-icon-wrapper {
|
||||
flex-shrink: 0;
|
||||
margin-right: 24rpx;
|
||||
|
||||
.news-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background: #FF19A0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.bell-icon-img {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
.no-message-text {
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
margin-top: 4rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.news-content-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
|
||||
.news-top-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.news-title {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #3D3D3D;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.news-time {
|
||||
flex-shrink: 0;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.news-desc-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
|
||||
.news-desc {
|
||||
flex: 1;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
line-height: 36rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.news-dot {
|
||||
flex-shrink: 0;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: #FF19A0;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
77
src/pages/client/news/details.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<view class="news-details">
|
||||
<view class="fs-32 SourceHanSansCN-Bold app-fc-main details-title">
|
||||
{{ newsInfo.desc || "" }}
|
||||
</view>
|
||||
<view class="fs-24 PingFangSC-Light app-fc-normal details-time">
|
||||
{{ time }}
|
||||
</view>
|
||||
<view
|
||||
class="fs-32 SourceHanSansCN-Regular details-info"
|
||||
v-html="newsInfo.content"
|
||||
></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getNoticeDetails } from "../../../api/notice";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
id: "",
|
||||
newsInfo: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
time() {
|
||||
return this.newsInfo.add_time
|
||||
? new Date(this.newsInfo.add_time * 1000).format("yyyy-MM-dd hh:mm:ss")
|
||||
: "";
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
const { id } = options;
|
||||
this.id = id;
|
||||
this.getNoticeInfo();
|
||||
},
|
||||
methods: {
|
||||
getNoticeInfo() {
|
||||
getNoticeDetails(this.id).then((res) => {
|
||||
this.newsInfo = res?.info || {};
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.newsInfo.title || "",
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.news-details {
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
padding-top: 38rpx;
|
||||
|
||||
.details-title {
|
||||
padding:0 36rpx;
|
||||
}
|
||||
|
||||
.details-time {
|
||||
margin: 20rpx 0;
|
||||
padding: 0 36rpx;
|
||||
}
|
||||
|
||||
.details-info {
|
||||
color: #726e71;
|
||||
line-height: 72rpx;
|
||||
padding: 0 36rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
63
src/pages/client/news/index.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<view class="news-list-container">
|
||||
<list-page-temp class="news-list-inner" :getDataPromise="getNoticeList" :reloadFlag="reloadFlag"
|
||||
:requestData="requestData" :emptyImage="`${imgPrefix}certificatePlaceholder.png`" emptyText="暂无系统消息">
|
||||
<template v-slot:item="{ data }">
|
||||
<news-item class="news-item-wrapper" :data="data" @toDetails="toDetails(data)" />
|
||||
</template>
|
||||
</list-page-temp>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getNoticeList
|
||||
} from "../../../api/notice";
|
||||
import NewsItem from "./components/NewsItem.vue";
|
||||
import { imgPrefix } from "@/utils/common";
|
||||
export default {
|
||||
components: {
|
||||
NewsItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newsList: [],
|
||||
reloadFlag: 0,
|
||||
requestData: {},
|
||||
imgPrefix,
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
methods: {
|
||||
getNoticeList,
|
||||
toDetails(data) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/news/details?id=${data.notice_id}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.news-list-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
border-top: 1rpx solid #f4f4f4;
|
||||
|
||||
.news-list-inner {
|
||||
height: 100%;
|
||||
::v-deep {
|
||||
.news-item {
|
||||
width: 100vw;
|
||||
box-sizing: border-box;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
101
src/pages/client/order/after-sale.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<view class="flex-column-start after-sale-container">
|
||||
<tabs-list
|
||||
:list="tabsList"
|
||||
:isScroll="false"
|
||||
:currentIndex="curTabIndex"
|
||||
flexClass="flex-row-around"
|
||||
@change="onTabChange"
|
||||
/>
|
||||
|
||||
<view class="after-sale-content">
|
||||
<list-page-temp
|
||||
:getDataPromise="getShopOrderList"
|
||||
:requestData="{ tui_status: tabsList[curTabIndex].id }"
|
||||
:reloadFlag="reloadFlag"
|
||||
>
|
||||
<template v-slot:item="{ data }">
|
||||
<after-sale-order-item :data="data" />
|
||||
</template>
|
||||
</list-page-temp>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabsList from "@/components/TabsList.vue";
|
||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
||||
import AfterSaleOrderItem from "./components/AfterSaleOrderItem.vue";
|
||||
|
||||
import { getShopOrderList } from '@/api/shop'
|
||||
import {
|
||||
SHOP_ORDER_AFTERSALE,
|
||||
SHOP_ORDER_AFTERSALE_DONE,
|
||||
SHOP_ORDER_AFTERSALE_REJECT,
|
||||
} from "@/constants/app.business";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TabsList,
|
||||
ListPageTemp,
|
||||
AfterSaleOrderItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
curTabIndex: 0,
|
||||
reloadFlag: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tabsList() {
|
||||
return [
|
||||
{
|
||||
name: "售后中",
|
||||
id: SHOP_ORDER_AFTERSALE,
|
||||
},
|
||||
{
|
||||
name: "已退款",
|
||||
id: SHOP_ORDER_AFTERSALE_DONE,
|
||||
},
|
||||
{
|
||||
name: "已驳回",
|
||||
id: SHOP_ORDER_AFTERSALE_REJECT,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
options: {
|
||||
styleIsolation: "shared",
|
||||
},
|
||||
onLoad() {
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
methods: {
|
||||
getShopOrderList,
|
||||
onTabChange(item, index) {
|
||||
this.curTabIndex = index;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.after-sale-container {
|
||||
height: 100%;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
|
||||
::v-deep {
|
||||
.tabs-list {
|
||||
.list-wrapper {
|
||||
padding: 0 100rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.after-sale-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
113
src/pages/client/order/components/AfterSaleOrderItem.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<view class="order-item">
|
||||
<view class="flex-row-between order-title">
|
||||
<text class="fs-28 app-fc-normal">订单编号:{{ data.order_no || '-' }}</text>
|
||||
</view>
|
||||
<view class="order-content">
|
||||
<good-info
|
||||
:data="data.goods_list"
|
||||
@openGoodsModal="$emit('openGoodsModal')"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="flex-row-between order-status" @click="jumpToAfterSaleDetails">
|
||||
<text
|
||||
v-if="data.tui_status === SHOP_ORDER_AFTERSALE"
|
||||
class="fs-28 app-fc-main"
|
||||
>
|
||||
{{ "等待平台确认" }}
|
||||
</text>
|
||||
<text
|
||||
v-else-if="data.tui_status === SHOP_ORDER_AFTERSALE_DONE"
|
||||
class="fs-28 app-fc-main"
|
||||
>
|
||||
{{ "退款成功" }}
|
||||
</text>
|
||||
<text
|
||||
v-else-if="data.tui_status === SHOP_ORDER_AFTERSALE_REJECT"
|
||||
class="fs-28 app-fc-main"
|
||||
>
|
||||
{{ "退款驳回" }}
|
||||
</text>
|
||||
<image
|
||||
class="arrow-icon"
|
||||
src="@/static/images/arrow_right_black.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
SHOP_ORDER_AFTERSALE,
|
||||
SHOP_ORDER_AFTERSALE_DONE,
|
||||
SHOP_ORDER_AFTERSALE_REJECT,
|
||||
} from "@/constants/app.business";
|
||||
|
||||
import GoodInfo from "./GoodInfo.vue";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
GoodInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
SHOP_ORDER_AFTERSALE,
|
||||
SHOP_ORDER_AFTERSALE_DONE,
|
||||
SHOP_ORDER_AFTERSALE_REJECT,
|
||||
};
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
jumpToAfterSaleDetails() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/order/details?id=${this.data.order_id}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-item {
|
||||
width: calc(100vw - 32rpx * 2);
|
||||
background: #fff;
|
||||
margin: 28rpx 32rpx 32rpx;
|
||||
border-radius: 30rpx;
|
||||
padding: 0 32rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.order-title {
|
||||
padding: 32rpx 0;
|
||||
border-bottom: 1rpx solid #ececec;
|
||||
|
||||
.order-btn {
|
||||
width: 104rpx;
|
||||
height: 48rpx;
|
||||
background: #fef6ff;
|
||||
border-radius: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.order-status {
|
||||
width: 100%;
|
||||
height: 76rpx;
|
||||
background: #f5f5f5;
|
||||
padding: 0 32rpx 0 24rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 16rpx;
|
||||
|
||||
.arrow-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
175
src/pages/client/order/components/GoodInfo.vue
Normal file
@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<view class="flex-row-start info-cell good-info" :class="{'good-info-multi' : data.length > 1}">
|
||||
<template v-if="data.length === 1">
|
||||
<image class="good-icon" :src="data[0].product_pic" mode="aspectFill" @click="$emit('clickGoodImg', data[0])" />
|
||||
<view class="good-content" @click="$emit('clickGoodInfo', data[0])">
|
||||
<view class="goods-row-first">
|
||||
<view class="goods-name">{{ data[0].product_name || "" }}</view>
|
||||
<text class="goods-price">¥{{ data[0].goods_price || actual_price }}</text>
|
||||
</view>
|
||||
<view class="goods-row-second">
|
||||
<view class="goods-spec">
|
||||
{{ data[0].shuxing_name || "" }}{{ data[0].shuxing_name && data[0].price_name ? ";" : "" }}{{ data[0].price_name || "" }}
|
||||
</view>
|
||||
<text class="goods-count">共{{ data[0].number || 1 }}件</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<view class="flex-row-between good-info-more" @click="$emit('openGoodsModal')">
|
||||
<view class="flex-row-start">
|
||||
<image v-for="(img, i) in goodsImgs.slice(0, 3)" :key="i" class="good-img" :src="img" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="good-info-right">
|
||||
<text class="good-total-price">¥{{ actual_price }}</text>
|
||||
<text class="fs-24 app-fc-normal good-num">
|
||||
共{{ goodsImgs.length }}件
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
actual_price:String
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
|
||||
goodsImgs() {
|
||||
return this.data.map((item) => item.product_pic).filter((v) => !!v);
|
||||
},
|
||||
totalPrice() {
|
||||
return this.data.reduce((sum, item) => {
|
||||
const price = parseFloat(item.goods_price || 0);
|
||||
const number = parseInt(item.number || 1);
|
||||
return sum + price * number;
|
||||
}, 0).toFixed(2);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.data,'--')
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.info-cell {
|
||||
border-radius: 20rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.arrow-icon {
|
||||
width: 11rpx;
|
||||
height: 18rpx;
|
||||
}
|
||||
|
||||
&.good-info {
|
||||
padding-top: 20rpx;
|
||||
align-items: center;
|
||||
|
||||
.good-icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.good-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100rpx;
|
||||
|
||||
.goods-row-first {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.goods-name {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #3D3D3D;
|
||||
line-height: 40rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
flex-shrink: 0;
|
||||
font-size: 28rpx;
|
||||
color: #3D3D3D;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-row-second {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.goods-spec {
|
||||
flex: 1;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 20rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.goods-count {
|
||||
flex-shrink: 0;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.good-info-more {
|
||||
width: 100%;
|
||||
|
||||
.good-img {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
margin-right: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.good-info-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
|
||||
.good-total-price {
|
||||
font-size: 28rpx;
|
||||
color: #3D3D3D;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.good-num {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
114
src/pages/client/order/components/GoodInfoModal.vue
Normal file
@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<select-modal
|
||||
class="good-info-modal"
|
||||
title="商品信息"
|
||||
@close="$emit('close', false)"
|
||||
>
|
||||
<view class="good-info-content">
|
||||
<view
|
||||
class="flex-row-start info-cell good-info"
|
||||
v-for="data in goods"
|
||||
:key="data.goods_id"
|
||||
>
|
||||
<image
|
||||
class="good-icon"
|
||||
:src="data.goods_pic"
|
||||
@click="$emit('clickGoodImg', data)"
|
||||
/>
|
||||
<view class="good-content" @click="$emit('clickGoodInfo', data)">
|
||||
<view>
|
||||
<view class="fs-32 app-fc-main app-text-ellipse app-font-bold">
|
||||
{{ data.goods_name || "" }}
|
||||
</view>
|
||||
<view class="fs-24 app-fc-normal">
|
||||
{{ data.shuxing_name || "" }};{{ data.price_name || "" }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row-between">
|
||||
<text class="fs-28 app-fc-main">
|
||||
实付款
|
||||
<text class="fs-28 app-fc-alarm">
|
||||
¥{{ data.goods_price || 0 }}
|
||||
</text>
|
||||
</text>
|
||||
<text class="fs-24 app-fc-normal">x{{ data.number || 1 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</select-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SelectModal from "@/components/select-modal.vue";
|
||||
import GoodInfo from "./GoodInfo.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SelectModal,
|
||||
GoodInfo,
|
||||
},
|
||||
options: {
|
||||
styleIsolation: "shared",
|
||||
},
|
||||
props: {
|
||||
goods: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.good-info-modal {
|
||||
::v-deep {
|
||||
.selected-modal .model-container {
|
||||
background: #fff0f5;
|
||||
}
|
||||
}
|
||||
.good-info-content {
|
||||
padding: 48rpx 32rpx 0;
|
||||
max-height: 75vh;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
.info-cell {
|
||||
background: #fff;
|
||||
border-radius: 30rpx;
|
||||
padding: 40rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
.arrow-icon {
|
||||
width: 20rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
&.good-info {
|
||||
padding: 20rpx 48rpx 20rpx 20rpx;
|
||||
align-items: stretch;
|
||||
.good-icon {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
.good-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
392
src/pages/client/order/components/OrderItem.vue
Normal file
@ -0,0 +1,392 @@
|
||||
<template>
|
||||
<view class="order-item">
|
||||
<view class="flex-row-between order-title" @click="jumpToDetails">
|
||||
<text class="fs-24 app-fc-normal">
|
||||
订单编号:{{ data.order_no || "-" }}
|
||||
</text>
|
||||
<!-- 待支付状态:显示倒计时横幅 -->
|
||||
<view v-if="data.status === SHOP_ORDER_UNPAY && !data.tui_status && countDownTime > 0"
|
||||
class="order-status-banner">
|
||||
<view class="status-banner-left">
|
||||
<text class="status-text">等待付款</text>
|
||||
</view>
|
||||
<view class="status-banner-right">
|
||||
<text class="countdown-text">{{ formatCountdown(countDownTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 其他状态:显示原有样式 -->
|
||||
<view v-else-if="
|
||||
([
|
||||
SHOP_ORDER_CANCEL,
|
||||
SHOP_ORDER_UNPAY,
|
||||
SHOP_ORDER_UNSLIVER,
|
||||
SHOP_ORDER_UNRECEIVE,
|
||||
SHOP_ORDER_DONE,
|
||||
SHOP_ORDER_UNREMARK,
|
||||
].includes(data.status) &&
|
||||
!data.tui_status) ||
|
||||
[SHOP_ORDER_AFTERSALE_REJECT].includes(data.tui_status)
|
||||
" class="flex-center fs-24 order-btn" :class="[
|
||||
![SHOP_ORDER_DONE, SHOP_ORDER_CANCEL, SHOP_ORDER_UNREMARK].includes(
|
||||
data.status
|
||||
)
|
||||
? 'app-fc-mark confirm'
|
||||
: 'cancel',
|
||||
]">
|
||||
{{ orderStatus }}
|
||||
</view>
|
||||
<view v-if="
|
||||
[SHOP_ORDER_AFTERSALE, SHOP_ORDER_AFTERSALE_DONE].includes(
|
||||
data.tui_status
|
||||
)
|
||||
" class="flex-center fs-24 order-btn cancel">
|
||||
{{ refundOrderStatus }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-content" :class="{ 'split-border': showStatusBtn }" @click="jumpToDetails">
|
||||
<good-info :data="data.items" :actual_price="data.actual_price" />
|
||||
<view v-if="data.type && data.type !== 1" class="fs-24 app-fc-mark order-type">
|
||||
随车订单
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="showStatusBtn" class="order-btns">
|
||||
<!-- 待支付 -->
|
||||
<template v-if="[SHOP_ORDER_UNPAY].includes(data.status)">
|
||||
<view class="flex-center fs-24 app-fc-main cancel-order-btn" @click.stop="$emit('cancelOrder', data)">
|
||||
取消订单
|
||||
</view>
|
||||
<view class="order-btns-right">
|
||||
<!-- <view class="flex-center fs-24 app-fc-main status-btn" @click.stop="$emit('concactService', data)">
|
||||
联系客服
|
||||
</view> -->
|
||||
<view class="flex-center fs-24 app-fc-white status-btn confirm" @click.stop="$emit('pay', data)">
|
||||
立即支付
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 待发货 -->
|
||||
<template v-if="[SHOP_ORDER_UNSLIVER].includes(data.status)">
|
||||
<view class="flex-center fs-24 app-fc-main cancel-order-btn" @click.stop="$emit('cancelOrder', data)">
|
||||
取消订单
|
||||
</view>
|
||||
<view class="order-btns-right">
|
||||
<!-- <view class="flex-center fs-24 app-fc-main status-btn" @click.stop="$emit('concactService', data)">
|
||||
联系客服
|
||||
</view> -->
|
||||
<!-- <view
|
||||
class="flex-center fs-24 app-fc-main status-btn"
|
||||
@click.stop="$emit('afterSale', data)"
|
||||
>
|
||||
申请售后
|
||||
</view> -->
|
||||
<view class="flex-center fs-24 app-fc-white status-btn confirm"
|
||||
@click.stop="$emit('remindSilver', data)">
|
||||
提醒发货
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 待收货 -->
|
||||
<template v-if="[SHOP_ORDER_UNRECEIVE].includes(data.status)">
|
||||
<!-- <view class="flex-center fs-24 app-fc-main status-btn" @click.stop="$emit('concactService', data)">
|
||||
联系客服
|
||||
</view> -->
|
||||
<view class="flex-center fs-24 app-fc-main status-btn" @click.stop="$emit('afterSale', data)">
|
||||
申请售后
|
||||
</view>
|
||||
<!-- 随车订单不显示物流 -->
|
||||
<!-- <view v-if="data.pay_type !== pay_type_BYCAR" class="flex-center fs-24 app-fc-main status-btn"
|
||||
@click.stop="$emit('checkSliver', data)">
|
||||
查看物流
|
||||
</view> -->
|
||||
<view class="flex-center fs-24 app-fc-white status-btn confirm"
|
||||
@click.stop="$emit('confirmSliver', data)">
|
||||
确认收货
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 已签收未评价 -->
|
||||
<template v-if="[SHOP_ORDER_UNREMARK].includes(data.status)">
|
||||
<!-- <view class="flex-center fs-24 app-fc-main status-btn" @click.stop="$emit('concactService', data)">
|
||||
联系客服
|
||||
</view> -->
|
||||
<view class="flex-center fs-24 app-fc-white status-btn confirm" @click.stop="$emit('remark', data)">
|
||||
立即评价
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 已完成 -->
|
||||
<template v-if="[SHOP_ORDER_DONE].includes(data.status)">
|
||||
<!-- <view class="flex-center fs-24 app-fc-main status-btn" @click.stop="$emit('concactService', data)">
|
||||
联系客服
|
||||
</view> -->
|
||||
<view class="flex-center fs-24 app-fc-white status-btn confirm "
|
||||
style="visibility: hidden;"
|
||||
>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
<template v-if="[SHOP_ORDER_DONE].includes(data.status)">
|
||||
<!-- <view class="flex-center fs-24 app-fc-main status-btn" @click.stop="$emit('concactService', data)">
|
||||
联系客服
|
||||
</view> -->
|
||||
<view class="flex-center fs-24 app-fc-white status-btn confirm"
|
||||
@click.stop="$emit('remarkDetails', data)">
|
||||
查看评价
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PopUpModal from "@/components/PopUpModal.vue";
|
||||
import GoodInfo from "./GoodInfo.vue";
|
||||
import {
|
||||
SHOP_ORDER_STATUS,
|
||||
SHOP_ORDER_UNPAY,
|
||||
SHOP_ORDER_UNSLIVER,
|
||||
SHOP_ORDER_UNRECEIVE,
|
||||
SHOP_ORDER_DONE,
|
||||
SHOP_ORDER_CANCEL,
|
||||
SHOP_ORDER_AFTERSALE,
|
||||
SHOP_ORDER_AFTERSALE_DONE,
|
||||
SHOP_ORDER_AFTERSALE_REJECT,
|
||||
SHOP_ORDER_AFTERSALE_STATUS,
|
||||
SHOP_ORDER_UNREMARK,
|
||||
pay_type_ADDRESS,
|
||||
pay_type_BYCAR,
|
||||
pay_type_BYPET
|
||||
} from "@/constants/app.business";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pay_type_ADDRESS,
|
||||
pay_type_BYCAR,
|
||||
pay_type_BYPET,
|
||||
SHOP_ORDER_STATUS,
|
||||
SHOP_ORDER_UNPAY,
|
||||
SHOP_ORDER_UNSLIVER,
|
||||
SHOP_ORDER_UNRECEIVE,
|
||||
SHOP_ORDER_DONE,
|
||||
SHOP_ORDER_CANCEL,
|
||||
SHOP_ORDER_AFTERSALE,
|
||||
SHOP_ORDER_AFTERSALE_DONE,
|
||||
SHOP_ORDER_AFTERSALE_REJECT,
|
||||
SHOP_ORDER_UNREMARK,
|
||||
showCancelModal: false,
|
||||
countDownTime: 0,
|
||||
countDownTimer: null,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
GoodInfo,
|
||||
PopUpModal,
|
||||
},
|
||||
options: {
|
||||
styleIsolation: "shared",
|
||||
},
|
||||
computed: {
|
||||
orderStatus() {
|
||||
return SHOP_ORDER_STATUS[this.data.status] || "";
|
||||
},
|
||||
refundOrderStatus() {
|
||||
return SHOP_ORDER_AFTERSALE_STATUS[this.data.tui_status] || "";
|
||||
},
|
||||
showStatusBtn() {
|
||||
return (
|
||||
([
|
||||
SHOP_ORDER_UNPAY,
|
||||
SHOP_ORDER_UNSLIVER,
|
||||
SHOP_ORDER_UNRECEIVE,
|
||||
SHOP_ORDER_DONE,
|
||||
SHOP_ORDER_UNREMARK,
|
||||
].includes(this.data.status) &&
|
||||
!this.data.tui_status) || [SHOP_ORDER_AFTERSALE_REJECT].includes(this.data.tui_status)
|
||||
);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
showCancelModal(val) {
|
||||
this.$emit("disableScroll", val);
|
||||
},
|
||||
'data.daojishi'(newVal) {
|
||||
if (this.data.status === this.SHOP_ORDER_UNPAY && newVal) {
|
||||
this.countDownTime = newVal;
|
||||
this.startCountDown();
|
||||
}
|
||||
},
|
||||
'data.status'(newVal) {
|
||||
if (newVal === this.SHOP_ORDER_UNPAY && this.data.daojishi) {
|
||||
this.countDownTime = this.data.daojishi;
|
||||
this.startCountDown();
|
||||
} else {
|
||||
this.stopCountDown();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// console.log(this.data,'--=')
|
||||
if (this.data.status === SHOP_ORDER_UNPAY && this.data.daojishi) {
|
||||
this.countDownTime = this.data.daojishi;
|
||||
this.startCountDown();
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.stopCountDown();
|
||||
},
|
||||
methods: {
|
||||
startCountDown() {
|
||||
this.stopCountDown();
|
||||
if (this.countDownTime > 0) {
|
||||
this.countDownTimer = setInterval(() => {
|
||||
if (this.countDownTime > 0) {
|
||||
this.countDownTime--;
|
||||
} else {
|
||||
this.stopCountDown();
|
||||
// 倒计时结束,可以触发刷新
|
||||
this.$emit('countdownEnd', this.data);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
},
|
||||
stopCountDown() {
|
||||
if (this.countDownTimer) {
|
||||
clearInterval(this.countDownTimer);
|
||||
this.countDownTimer = null;
|
||||
}
|
||||
},
|
||||
formatCountdown(seconds) {
|
||||
const hour = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds - hour * 3600) / 60);
|
||||
return `${hour}小时${minutes}分钟`;
|
||||
},
|
||||
orderCancel() {
|
||||
this.showCancelModal = false;
|
||||
},
|
||||
cancel() {
|
||||
this.showCancelModal = false;
|
||||
},
|
||||
jumpToDetails() {
|
||||
this.$emit('jumpToDetails', this.data)
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-item {
|
||||
width: calc(100vw - 40rpx);
|
||||
background: #fff;
|
||||
margin: 20rpx;
|
||||
border-radius: 30rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 0;
|
||||
|
||||
.order-title {
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #ececec;
|
||||
|
||||
.order-btn {
|
||||
width: 104rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 48rpx;
|
||||
|
||||
&.confirm {
|
||||
background: #fef6ff;
|
||||
}
|
||||
|
||||
&.cancel {
|
||||
background: #f7f7f7;
|
||||
color: #afa5ae;
|
||||
}
|
||||
}
|
||||
|
||||
.order-status-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 48rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.status-banner-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
background: #FF19A0;
|
||||
padding: 8rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 10px 0px 10px 10px;
|
||||
|
||||
.status-text {
|
||||
font-size: 20rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.status-banner-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
background: #FFF0F8;
|
||||
padding: 8rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
|
||||
|
||||
.countdown-text {
|
||||
font-size: 20rpx;
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.order-btns {
|
||||
padding-top: 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.cancel-order-btn {
|
||||
font-size: 24rpx;
|
||||
color: #9B939A;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
.order-btns-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-btn {
|
||||
padding: 16rpx 20rpx;
|
||||
border-radius: 64rpx;
|
||||
border: 1px solid #9b939a;
|
||||
margin-left: 20rpx;
|
||||
|
||||
&.confirm {
|
||||
color: $app_color_main;
|
||||
border: 1px solid $app_color_main;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.good-info-multi {
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
261
src/pages/client/order/components/SliverInfo.vue
Normal file
@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<select-modal title="物流信息" @close="$emit('close')">
|
||||
<view class="sliver-info-container">
|
||||
<view class="sliver-info-content">
|
||||
<view v-if="isLoading" class="flex-center">
|
||||
<uni-load-more status="loading" />
|
||||
</view>
|
||||
<template v-else>
|
||||
<view class="flex-row-start sliver-info-title">
|
||||
<!-- <image class="sliver-icon" :src="sliverInfo.logo" /> -->
|
||||
<text class="fs-24 app-fc-main sliver-text">
|
||||
{{ sliverInfo.name }} {{ orderData.kuaidi_no }}
|
||||
</text>
|
||||
<text class="fs-24 app-fc-main" @click="copySliverId">复制</text>
|
||||
</view>
|
||||
<view
|
||||
class="flex-row-start sliver-item"
|
||||
:class="{
|
||||
first: index === sliverList.length - 1,
|
||||
last: index === 0,
|
||||
}"
|
||||
v-for="(item, index) in sliverList"
|
||||
:key="index"
|
||||
>
|
||||
<view class="flex-center icon-wrapper">
|
||||
<view class="icon-circle"></view>
|
||||
<view v-if="index === 0" class="icon-circle-bg"></view>
|
||||
<view
|
||||
v-if="index < sliverList.length - 1"
|
||||
class="flex-1 icon-line"
|
||||
></view>
|
||||
</view>
|
||||
<view class="flex-1 flex-column-start item-info">
|
||||
<text>
|
||||
<!-- index === 0 || index > 0 && item.status !== sliverList[index - 1].status -->
|
||||
<text class="fs-32 app-fc-main app-font-bold info-status">
|
||||
{{ item.status }}
|
||||
</text>
|
||||
<text class="fs-24 info-time ali-puhui-regular">{{ item.time }}</text>
|
||||
</text>
|
||||
<text class="fs-24 app-fc-main info-desc">{{
|
||||
item.context
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<view
|
||||
class="flex-center app-fc-white fs-30 confirm-btn"
|
||||
@click.stop="$emit('close')"
|
||||
>
|
||||
确定
|
||||
</view>
|
||||
</view>
|
||||
</select-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import selectModal from "@/components/select-modal.vue";
|
||||
import { getSliverInfo } from "@/api/shop";
|
||||
import moment from "moment";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
orderId: {
|
||||
type: String | Number,
|
||||
default: "",
|
||||
},
|
||||
orderInfo: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
components: { selectModal },
|
||||
data() {
|
||||
return {
|
||||
isLoading: true,
|
||||
sliverList: [],
|
||||
sliverInfo: {},
|
||||
orderData: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
defaultSliver() {
|
||||
return [
|
||||
{
|
||||
status: "已发货",
|
||||
time: moment(this.orderInfo.fahuo_time * 1000).format(
|
||||
"YYYY-MM-DD HH:mm:ss"
|
||||
),
|
||||
context: "包裹正在等待揽收",
|
||||
},
|
||||
{
|
||||
status: "已下单",
|
||||
time: moment(this.orderInfo.add_time * 1000).format(
|
||||
"YYYY-MM-DD HH:mm:ss"
|
||||
),
|
||||
context: "商品已经下单",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getSliverInfo();
|
||||
},
|
||||
methods: {
|
||||
formatStatus(status) {
|
||||
switch (status) {
|
||||
case "揽收":
|
||||
return "已揽件";
|
||||
case "在途":
|
||||
return "运输中";
|
||||
case "派件":
|
||||
return "派送中";
|
||||
case "签收":
|
||||
return "已签收";
|
||||
case "已发货":
|
||||
return "已发货";
|
||||
case "已下单":
|
||||
return "已下单";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
},
|
||||
getSliverInfo() {
|
||||
getSliverInfo(this.orderId)
|
||||
.then((res) => {
|
||||
this.sliverList = [
|
||||
...(res?.info?.wuliu_list || []),
|
||||
...this.defaultSliver,
|
||||
].map((v) => ({
|
||||
...v,
|
||||
status: this.formatStatus(v.status),
|
||||
}));
|
||||
this.orderData = res?.info?.order_info || {};
|
||||
this.sliverInfo = res?.info?.express_info || {};
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
copySliverId() {
|
||||
uni.setClipboardData({
|
||||
data: this.orderData.kuaidi_no,
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: "复制成功",
|
||||
icon: "success",
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sliver-info-container {
|
||||
padding: 30rpx 56rpx 0 48rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.sliver-info-content {
|
||||
height: 55vh;
|
||||
overflow-y: auto;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
|
||||
.sliver-info-title {
|
||||
margin-bottom: 50rpx;
|
||||
|
||||
.sliver-icon {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 56rpx;
|
||||
background: gray;
|
||||
}
|
||||
|
||||
.sliver-text {
|
||||
margin-right: 14rpx;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.sliver-item {
|
||||
align-items: stretch;
|
||||
padding-left: 8rpx;
|
||||
|
||||
&.last {
|
||||
.icon-wrapper .icon-circle {
|
||||
background: $app_color_main;
|
||||
}
|
||||
}
|
||||
|
||||
&.first {
|
||||
.item-info {
|
||||
min-height: unset;
|
||||
}
|
||||
.icon-wrapper {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
position: relative;
|
||||
|
||||
.icon-circle {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
background: #d4ced1;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.icon-line {
|
||||
width: 2rpx;
|
||||
background: #dddddd;
|
||||
}
|
||||
|
||||
.icon-circle-bg {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
left: -8rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 40rpx;
|
||||
background: rgba(254, 1, 155, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.item-info {
|
||||
min-height: 190rpx;
|
||||
padding: 0rpx 0 30rpx 24rpx;
|
||||
box-sizing: border-box;
|
||||
justify-content: flex-start;
|
||||
margin-top: -10rpx;
|
||||
|
||||
.info-time {
|
||||
color: #afa5ae;
|
||||
// font-weight: 400;
|
||||
}
|
||||
|
||||
.info-status {
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
.info-desc {
|
||||
margin-top: 12rpx;
|
||||
line-height: 1.6;
|
||||
margin-left: -4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 630rpx;
|
||||
height: 92rpx;
|
||||
background: #fe019b;
|
||||
border-radius: 92rpx;
|
||||
margin: 20rpx auto 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1275
src/pages/client/order/create.vue
Normal file
1276
src/pages/client/order/details.vue
Normal file
698
src/pages/client/order/list.vue
Normal file
@ -0,0 +1,698 @@
|
||||
<template>
|
||||
<view class="flex-column-start order-list-container">
|
||||
<tabs-list :list="tabsList" :isScroll="true" :currentIndex="curTabIndex" @change="onTabChange" />
|
||||
|
||||
<view class="order-list-content">
|
||||
<scroll-view class="list-template-wrapper" :scroll-y="!disableScroll" :refresher-enabled="true"
|
||||
:refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
|
||||
<view class="list-content">
|
||||
<view v-for="(data, index) in list" :key="data.order_id" :class="{ left: index % 2 === 0 }"
|
||||
class="flex-column-start news-item">
|
||||
<order-item :data="data" @disableScroll="disableScrollAction" @cancelOrder="cancelOrderAction"
|
||||
@concactService="jumpToWeChat" @confirmSliver="confirmSliver" @remindSilver="remindSliver" @pay="pay"
|
||||
@afterSale="afterSale" @checkSliver="checkSliver" @remark="remark" @remarkDetails="remarkDetails"
|
||||
@jumpToDetails="jumpToDetails" />
|
||||
</view>
|
||||
<uni-load-more v-if="isLoading || (!isLoading && total && total === list.length)"
|
||||
:status="isLoading ? 'loading' : 'nomore'"></uni-load-more>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<!-- <list-page-temp
|
||||
:getDataPromise="getShopOrderList"
|
||||
:requestData="{ status: tabsList[curTabIndex].id }"
|
||||
:reloadFlag="reloadFlag"
|
||||
:disableScroll="disableScroll"
|
||||
>
|
||||
<template v-slot:item="{ data }">
|
||||
<order-item
|
||||
:data="data"
|
||||
@disableScroll="disableScrollAction"
|
||||
@cancelOrder="cancelOrderAction"
|
||||
@concactService="showConcact = true"
|
||||
@confirmSliver="confirmSliver"
|
||||
@remindSilver="remindSliver"
|
||||
@pay="pay"
|
||||
@afterSale="afterSale"
|
||||
@checkSliver="checkSliver"
|
||||
@remark="remark"
|
||||
@remarkDetails="remarkDetails"
|
||||
/>
|
||||
</template>
|
||||
</list-page-temp> -->
|
||||
<view v-if="elasticLayer" class="elastic-layer" />
|
||||
</view>
|
||||
|
||||
<!-- 右侧浮动联系客服按钮 -->
|
||||
<view class="contact-float-btn" :style="{ right: contactBtnRight + 'rpx', bottom: contactBtnBottom + 'rpx' }"
|
||||
@touchstart="onContactBtnTouchStart" @touchmove="onContactBtnTouchMove" @touchend="onContactBtnTouchEnd"
|
||||
@click="handleContactBtnClick">
|
||||
<image class="contact-icon" :src="`${imgPrefix}supportStaff.png`" />
|
||||
<view class="contact-btn fs-20">
|
||||
联系客服
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="additional-bottom" v-if="additionalBom">
|
||||
<view class="recharge-method">
|
||||
<view class="method-header">
|
||||
<text class="payment-title">支付方式</text>
|
||||
<image src="@/static/images/close.png" mode="aspectFit" class="close-icon"
|
||||
@click="(additionalBom = false), (elasticLayer = false)" />
|
||||
</view>
|
||||
<view class="wechat" @click.stop="selectOption1('1')">
|
||||
<view class="select">
|
||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
||||
<text class="x">微信</text>
|
||||
</view>
|
||||
<image v-if="selected1" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
||||
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||
</view>
|
||||
<view class="wechat" @click.stop="selectOption2('2')">
|
||||
<view class="select">
|
||||
<image class="w" src="@/static/images/wallet.png" mode="widthFix" />
|
||||
<text class="x">钱包支付</text>
|
||||
</view>
|
||||
<image v-if="selected3" class="not-selected" src="@/static/images/w.png" mode="widthFix" />
|
||||
<image v-if="selected4" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="payment" @click="rechargeNow">
|
||||
<text class="Z"> 支付 </text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<pop-up-modal v-if="showCancelModal" content="确定要取消该订单吗?" @confirm="orderCancel"
|
||||
@cancel="showCancelModal = false" />
|
||||
|
||||
<contact-modal v-if="showConcact" @close="showConcact = false" />
|
||||
|
||||
<pop-up-modal v-if="showSliverModal" content="是否要确认收货?" @confirm="confirmReceiveOrder"
|
||||
@cancel="showSliverModal = false" />
|
||||
|
||||
<success-modal v-if="showRemindSliver" title="提醒成功" message="已通知商家,请耐心等待商家发货" @close="showRemindSliver = false"
|
||||
@ok="showRemindSliver = false" />
|
||||
|
||||
<sliver-info v-if="showSliverRouteModal" :orderId="orderInfo.order_id" :orderInfo="orderInfo"
|
||||
@close="showSliverRouteModal = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabsList from "@/components/TabsList.vue";
|
||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
||||
import OrderItem from "./components/OrderItem.vue";
|
||||
import PopUpModal from "@/components/PopUpModal.vue";
|
||||
import SuccessModal from "@/components/SuccessModal.vue";
|
||||
import ContactModal from "@/components/ContactModal.vue";
|
||||
import SliverInfo from "./components/SliverInfo.vue";
|
||||
import { walletTransaction, cancelPetOrderRefund, cancelPetOrderMall } from "../../../api/login";
|
||||
|
||||
|
||||
import {
|
||||
payOrder,
|
||||
remindOrder,
|
||||
getShopOrderList,
|
||||
cancelOrder,
|
||||
confirmOrder,
|
||||
} from "@/api/shop";
|
||||
import {
|
||||
SHOP_ORDER_UNPAY,
|
||||
SHOP_ORDER_UNSLIVER,
|
||||
SHOP_ORDER_UNRECEIVE,
|
||||
SHOP_ORDER_DONE,
|
||||
SHOP_ORDER_CANCEL,
|
||||
SHOP_ORDER_UNREMARK,
|
||||
} from "@/constants/app.business";
|
||||
import { jumpToWeChat, imgPrefix } from "@/utils/common";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TabsList,
|
||||
ListPageTemp,
|
||||
OrderItem,
|
||||
PopUpModal,
|
||||
ContactModal,
|
||||
SuccessModal,
|
||||
SliverInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selected1: true,
|
||||
selected2: false,
|
||||
selected3: true,
|
||||
selected4: false,
|
||||
recharge: true,
|
||||
record: false,
|
||||
WeChat: "",
|
||||
wallet: "",
|
||||
curTabIndex: 0,
|
||||
reloadFlag: 0,
|
||||
additionalBom: false,
|
||||
elasticLayer: false,
|
||||
disableScroll: false,
|
||||
showCancelModal: false,
|
||||
showConcact: false,
|
||||
showRemindSliver: false,
|
||||
showSliverModal: false,
|
||||
showSliverRouteModal: false,
|
||||
orderInfo: {},
|
||||
isLoading: false,
|
||||
total: 0,
|
||||
list: [],
|
||||
refreshTriggered: false,
|
||||
p: 1,
|
||||
num: 10,
|
||||
imgPrefix,
|
||||
// 联系客服按钮拖拽相关
|
||||
contactBtnRight: 0, // 默认右侧距离,始终为0
|
||||
contactBtnBottom: 0, // 默认底部距离,需要计算
|
||||
isDragging: false, // 是否正在拖拽
|
||||
touchStartX: 0, // 触摸开始X坐标
|
||||
touchStartY: 0, // 触摸开始Y坐标
|
||||
initialRight: 0, // 初始右侧距离
|
||||
initialBottom: 0, // 初始底部距离
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tabsList() {
|
||||
return [
|
||||
{
|
||||
name: "全部",
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
name: "待支付",
|
||||
id: SHOP_ORDER_UNPAY,
|
||||
},
|
||||
{
|
||||
name: "待发货",
|
||||
id: SHOP_ORDER_UNSLIVER,
|
||||
},
|
||||
{
|
||||
name: "待收货",
|
||||
id: SHOP_ORDER_UNRECEIVE,
|
||||
},
|
||||
{
|
||||
name: "待评价",
|
||||
id: SHOP_ORDER_UNREMARK,
|
||||
},
|
||||
{
|
||||
name: "已取消",
|
||||
id: SHOP_ORDER_CANCEL,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
options: {
|
||||
styleIsolation: "shared",
|
||||
},
|
||||
onLoad() {
|
||||
this.reloadData();
|
||||
|
||||
// 初始化联系客服按钮位置
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
const windowHeight = systemInfo.windowHeight;
|
||||
// 计算底部距离:calc(35vh - 130rpx - 48rpx) 转换为rpx
|
||||
// 35vh = windowHeight * 0.35,转换为rpx (750rpx = windowHeight px)
|
||||
const vh35InRpx = (windowHeight * 0.35 / windowHeight) * 750;
|
||||
this.contactBtnBottom = vh35InRpx - 130 - 48;
|
||||
},
|
||||
methods: {
|
||||
jumpToWeChat,
|
||||
getShopOrderList,
|
||||
getList() {
|
||||
this.isLoading = true;
|
||||
getShopOrderList({ status: this.tabsList[this.curTabIndex].id })
|
||||
.then((res) => {
|
||||
const list = res?.data || [];
|
||||
this.list = this.p === 1 ? list : [...this.list, data];
|
||||
this.total = res?.count || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
this.refreshTriggered = false;
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
reloadData() {
|
||||
this.page = 1;
|
||||
this.total = 0;
|
||||
this.getList();
|
||||
},
|
||||
onTabChange(item, index) {
|
||||
this.curTabIndex = index;
|
||||
this.reloadData();
|
||||
},
|
||||
disableScrollAction(val) {
|
||||
this.disableScroll = val;
|
||||
},
|
||||
onRefresh() {
|
||||
if (this.refreshTriggered) return;
|
||||
this.refreshTriggered = true;
|
||||
this.p = 1;
|
||||
this.num = 10;
|
||||
this.total = 0;
|
||||
this.getList();
|
||||
},
|
||||
onLoadMore() {
|
||||
if (!this.isLoading && this.total > this.list.length) {
|
||||
this.p++;
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
orderCancel() {
|
||||
uni.showLoading({
|
||||
icon: "none",
|
||||
title: "处理中",
|
||||
mask: true,
|
||||
});
|
||||
const data = {
|
||||
order_id: this.orderInfo.order_id,
|
||||
// business_type:1
|
||||
}
|
||||
cancelPetOrderMall(data).then((res) => {
|
||||
uni.hideLoading();
|
||||
this.showCancelModal = false;
|
||||
this.reloadData();
|
||||
});
|
||||
},
|
||||
// 确认收货
|
||||
confirmSliver(data) {
|
||||
this.showSliverModal = true;
|
||||
this.orderInfo = data;
|
||||
},
|
||||
// 确认收货
|
||||
confirmReceiveOrder() {
|
||||
uni.showLoading({
|
||||
icon: "none",
|
||||
title: "处理中",
|
||||
mask: true,
|
||||
});
|
||||
confirmOrder(this.orderInfo.order_id).then((res) => {
|
||||
uni.hideLoading();
|
||||
this.reloadData();
|
||||
this.showSliverModal = false;
|
||||
});
|
||||
},
|
||||
selectOption1(v) {
|
||||
this.WeChat = v;
|
||||
this.wallet = "";
|
||||
console.log(this.WeChat);
|
||||
this.selected1 = false;
|
||||
this.selected2 = true;
|
||||
this.selected3 = true;
|
||||
this.selected4 = false;
|
||||
},
|
||||
selectOption2(v) {
|
||||
this.WeChat = "";
|
||||
this.wallet = v;
|
||||
console.log(this.wallet);
|
||||
this.selected1 = true;
|
||||
this.selected2 = false;
|
||||
this.selected3 = false;
|
||||
this.selected4 = true;
|
||||
},
|
||||
// 支付
|
||||
pay(data) {
|
||||
|
||||
this.additionalBom = true;
|
||||
this.elasticLayer = true;
|
||||
this.dataList = data;
|
||||
},
|
||||
async rechargeNow() {
|
||||
// console.log(this.wallet, "???");
|
||||
if (!this.WeChat && !this.wallet) {
|
||||
uni.showToast({
|
||||
title: "请选择充值方式",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.WeChat == 1) {
|
||||
uni.showLoading({
|
||||
icon: "none",
|
||||
title: "支付中",
|
||||
mask: true,
|
||||
});
|
||||
payOrder({
|
||||
type: 4,
|
||||
total_fee: Number(this.dataList.actual_price),
|
||||
order_id: this.dataList.order_id,
|
||||
order_no: this.dataList.order_no
|
||||
}).then((res) => {
|
||||
const payData = res?.data || {};
|
||||
uni.requestPayment({
|
||||
provider: "wxpay",
|
||||
timeStamp: payData.timeStamp,
|
||||
nonceStr: payData.nonceStr,
|
||||
package: payData.package,
|
||||
signType: payData.signType,
|
||||
paySign: payData.paySign,
|
||||
success: (res) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "支付成功",
|
||||
icon: "none",
|
||||
});
|
||||
this.additionalBom = false,
|
||||
this.elasticLayer = false,
|
||||
this.reloadData();
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: err?.msg || "支付失败",
|
||||
icon: "none",
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
} else if (this.wallet == 2) {
|
||||
const value = (this.$store.state && this.$store.state.user && this.$store.state.user.userInfo) || {};
|
||||
// console.log(data, "???");
|
||||
// console.log(this.selectService,'---')
|
||||
const data = {
|
||||
wallet_id: value.wallet_id,
|
||||
total_fee: this.dataList.actual_price,
|
||||
type: 4,
|
||||
order_id: this.dataList.order_id,
|
||||
order_no: this.dataList.order_no
|
||||
// business_type: 2,
|
||||
// wallet_id: value.wallet_id,
|
||||
// user_id: value.user_id,
|
||||
// amount: Number(this.dataList.pay_price),
|
||||
// business_id: this.dataList.order_id,
|
||||
};
|
||||
walletTransaction(data).then((res) => {
|
||||
if (res.code == 0) {
|
||||
uni.showToast({
|
||||
title: "支付成功",
|
||||
icon: "none",
|
||||
});
|
||||
this.additionalBom = false,
|
||||
this.elasticLayer = false,
|
||||
uni.hideLoading();
|
||||
this.reloadData();
|
||||
} else if (res.code == 100) {
|
||||
uni.showToast({
|
||||
title: res.message,
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// if (!this.giftLabel) {
|
||||
// uni.showToast({
|
||||
// title: "请选择充值金额",
|
||||
// icon: "none",
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
},
|
||||
// 提醒发货
|
||||
remindSliver(data) {
|
||||
this.showRemindSliver = true;
|
||||
// remindOrder(data.order_id).then(() => {
|
||||
// this.showRemindSliver = true;
|
||||
// });
|
||||
},
|
||||
// 申请售后
|
||||
afterSale() {
|
||||
// 如果正在拖拽,不触发点击事件
|
||||
if (this.isDragging) {
|
||||
return;
|
||||
}
|
||||
this.jumpToWeChat();
|
||||
},
|
||||
// afterSale(data) {
|
||||
// uni.navigateTo({
|
||||
// url: `/pages/client/order/refund?orderId=${data.order_id}`,
|
||||
// events: {
|
||||
// refreshData: () => this.reloadData(),
|
||||
// },
|
||||
// });
|
||||
// },
|
||||
// 订单详情
|
||||
jumpToDetails(data) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/order/details?id=${data?.order_id}`,
|
||||
events: {
|
||||
refreshData: () => this.reloadData(),
|
||||
},
|
||||
});
|
||||
},
|
||||
// 查看物流
|
||||
checkSliver(data) {
|
||||
this.showSliverRouteModal = true;
|
||||
this.orderInfo = data;
|
||||
},
|
||||
// 立即评价
|
||||
remark(data) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/order/remark?orderId=${data.order_id}`,
|
||||
events: {
|
||||
refreshData: () => this.reloadData(),
|
||||
},
|
||||
});
|
||||
},
|
||||
// 查看评价
|
||||
remarkDetails(data) {
|
||||
if (!data.pinglun_id) {
|
||||
uni.showToast({
|
||||
title: "暂无评价",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/remark/details?remarkId=${data.pinglun_id}`,
|
||||
});
|
||||
},
|
||||
// 取消订单
|
||||
cancelOrderAction(data) {
|
||||
this.showCancelModal = true;
|
||||
this.orderInfo = data;
|
||||
},
|
||||
// 联系客服按钮触摸开始
|
||||
onContactBtnTouchStart(e) {
|
||||
this.isDragging = false;
|
||||
const touch = e.touches[0];
|
||||
this.touchStartX = touch.clientX;
|
||||
this.touchStartY = touch.clientY;
|
||||
this.initialRight = this.contactBtnRight;
|
||||
this.initialBottom = this.contactBtnBottom;
|
||||
},
|
||||
// 联系客服按钮触摸移动
|
||||
onContactBtnTouchMove(e) {
|
||||
const touch = e.touches[0];
|
||||
const deltaX = touch.clientX - this.touchStartX;
|
||||
const deltaY = touch.clientY - this.touchStartY;
|
||||
|
||||
// 如果移动距离超过10px,认为是拖拽
|
||||
if (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 10) {
|
||||
this.isDragging = true;
|
||||
}
|
||||
|
||||
if (this.isDragging) {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
const windowHeight = systemInfo.windowHeight;
|
||||
|
||||
// 只处理上下移动,将px转换为rpx
|
||||
const deltaYRpx = (deltaY / windowHeight) * 750;
|
||||
|
||||
// 计算新位置(只改变bottom,right始终保持为0)
|
||||
// 向上移动时deltaY为负,但bottom值应该减小
|
||||
let newBottom = this.initialBottom - deltaYRpx;
|
||||
|
||||
// 限制在屏幕范围内
|
||||
// 按钮高度约120rpx
|
||||
const btnHeight = 120;
|
||||
|
||||
newBottom = Math.max(0, Math.min(newBottom, windowHeight * 2 - btnHeight));
|
||||
|
||||
// right始终保持为0
|
||||
this.contactBtnRight = 0;
|
||||
this.contactBtnBottom = newBottom;
|
||||
}
|
||||
},
|
||||
// 联系客服按钮触摸结束
|
||||
onContactBtnTouchEnd(e) {
|
||||
// 松手后,right重置为0
|
||||
this.contactBtnRight = 0;
|
||||
this.isDragging = false;
|
||||
},
|
||||
// 联系客服按钮点击
|
||||
handleContactBtnClick() {
|
||||
// 如果正在拖拽,不触发点击事件
|
||||
if (this.isDragging) {
|
||||
return;
|
||||
}
|
||||
this.jumpToWeChat();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-list-container {
|
||||
height: 100%;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
|
||||
.elastic-layer {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.additional-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
background: #fff;
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.recharge-method {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
padding: 40rpx 0 30rpx;
|
||||
border-radius: 30rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.method-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 30rpx;
|
||||
position: relative;
|
||||
padding: 0 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.payment-title {
|
||||
font-family: PingFangSC;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #272427;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
position: absolute;
|
||||
right: 30rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.wechat {
|
||||
width: 100%;
|
||||
height: 116rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 60rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.w {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.x {
|
||||
font-size: 32rpx;
|
||||
color: #272427;
|
||||
}
|
||||
}
|
||||
|
||||
.not-selected {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.payment {
|
||||
position: relative;
|
||||
border-radius: 100rpx;
|
||||
margin: auto;
|
||||
width: calc(100% - 60rpx);
|
||||
height: 96rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2rpx solid #FF19A0;
|
||||
background: #FF19A0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.Z {
|
||||
font-family: PingFang SC;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.order-list-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
background: #f7f8fa;
|
||||
|
||||
|
||||
.list-template-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.list-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.news-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contact-float-btn {
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
z-index: 100;
|
||||
|
||||
.contact-icon {
|
||||
width: 66rpx;
|
||||
height: 66rpx;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.contact-btn {
|
||||
color: #FFFFFF;
|
||||
background-color: #FF19A0;
|
||||
border-radius: 257px;
|
||||
padding: 6rpx 8rpx;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
272
src/pages/client/order/refund.vue
Normal file
@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<view class="flex-column-start refund-edit-container">
|
||||
<view class="refund-edit-content">
|
||||
<view class="refund-cell">
|
||||
<form-cell
|
||||
:title="'售后事项'"
|
||||
type="checkPicker"
|
||||
placeholderText="请选择"
|
||||
:value="refundInfo.reason"
|
||||
:pickerData="reasonList"
|
||||
:noBorder="true"
|
||||
valueKey="name"
|
||||
labelKey="name"
|
||||
@onChange="(value) => onChange(value, 'reason')"
|
||||
/>
|
||||
</view>
|
||||
<view class="refund-content-cell">
|
||||
<text class="fs-32 app-fc-main">申请售后原因</text>
|
||||
<view class="flex-column-start remark-content">
|
||||
<textarea
|
||||
class="remark-input"
|
||||
:v-model="refundInfo.content"
|
||||
:maxlength="maxLen"
|
||||
placeholder="在此输入内容......"
|
||||
placeholder-style="color:#ACACAC; font-size: 28rpx;"
|
||||
:auto-height="true"
|
||||
@input="onRemarkChange"
|
||||
/>
|
||||
<view class="flex-row-end remark-count">
|
||||
{{ refundInfo.content.length }}/{{ maxLen }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="refund-content-cell">
|
||||
<text class="fs-32 app-fc-main">图片</text>
|
||||
<uni-file-picker
|
||||
limit="1"
|
||||
:title="' '"
|
||||
:imageStyles="{
|
||||
width: '220rpx',
|
||||
height: '220rpx',
|
||||
'border-radius': '20rpx',
|
||||
}"
|
||||
:del-icon="true"
|
||||
:auto-upload="false"
|
||||
@select="onImgChange"
|
||||
@delete="onImgDelete"
|
||||
></uni-file-picker>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-center refund-edit-footer">
|
||||
<view
|
||||
class="flex-center app-fc-white fs-30 app-fc-bold refund-edit-btn"
|
||||
@click="submit"
|
||||
>
|
||||
提交
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormCell from "@/components/FormCell";
|
||||
import appConfig from "@/constants/app.config";
|
||||
import { createAfterSale } from "../../../api/shop";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FormCell,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
orderId: "",
|
||||
refundInfo: {
|
||||
reason: "",
|
||||
content: "",
|
||||
},
|
||||
maxLen: 300,
|
||||
reasonList: [
|
||||
{
|
||||
value: "",
|
||||
name: "多拍/错拍/不想要了",
|
||||
},
|
||||
{
|
||||
value: "",
|
||||
name: "商家发错货",
|
||||
},
|
||||
{
|
||||
value: "",
|
||||
name: "未按约定时间发货",
|
||||
},
|
||||
{
|
||||
value: "",
|
||||
name: "填错地址/不方便收货",
|
||||
},
|
||||
],
|
||||
imgList: [],
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
this.orderId = options.orderId;
|
||||
},
|
||||
beforeDestroy() {
|
||||
const eventChannel = this.getOpenerEventChannel();
|
||||
eventChannel && eventChannel.emit('refreshData');
|
||||
},
|
||||
methods: {
|
||||
onChange(val, key) {
|
||||
this.refundInfo[key] = val;
|
||||
},
|
||||
onRemarkChange(e) {
|
||||
this.refundInfo.content = e.mp.detail.value.slice(0, this.maxLen);
|
||||
},
|
||||
onImgChange(e) {
|
||||
this.uploadFile(e?.tempFilePaths?.[0]);
|
||||
},
|
||||
onImgDelete(e) {
|
||||
this.imgList.splice(e.index, 1);
|
||||
},
|
||||
uploadFile(url) {
|
||||
uni.showLoading({
|
||||
title: "图片上传中...",
|
||||
icon: "none",
|
||||
mask: true
|
||||
})
|
||||
uni.uploadFile({
|
||||
url: appConfig.apiBaseUrl + "/app/upload/upload_file",
|
||||
filePath: url,
|
||||
name: "image",
|
||||
formData: {
|
||||
file_name: "app",
|
||||
pic_name: +new Date(),
|
||||
},
|
||||
header: { token: uni.getStorageSync("token") },
|
||||
success: (res) => {
|
||||
const data = JSON.parse(res.data);
|
||||
const { code, msg, info } = data;
|
||||
const errCode = String(code);
|
||||
uni.hideLoading()
|
||||
if (errCode === "0" || errCode === "200") {
|
||||
this.imgList.push({
|
||||
fullUrl: info?.[0]?.urlname,
|
||||
url: info?.[0]?.filename,
|
||||
});
|
||||
this.$forceUpdate();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: msg || "上传失败",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: "上传失败",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
submit() {
|
||||
if (!this.refundInfo.reason || !this.refundInfo.reason.trim()) {
|
||||
uni.showToast({
|
||||
title: "请选择售后事项",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!this.refundInfo.content || !this.refundInfo.content.trim()) {
|
||||
uni.showToast({
|
||||
title: "请选择售后原因",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.showLoading({
|
||||
title: "处理中",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
});
|
||||
createAfterSale({
|
||||
order_id: this.orderId,
|
||||
tui_yuanyin: this.refundInfo.reason,
|
||||
tui_desc: this.refundInfo.content,
|
||||
tui_pic: this.imgList.map((v) => v.url).join(","),
|
||||
}).then(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "提交成功",
|
||||
icon: "none",
|
||||
});
|
||||
uni.redirectTo({
|
||||
url: "/pages/client/order/after-sale",
|
||||
})
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.refund-edit-container {
|
||||
align-items: stretch;
|
||||
height: 100vh;
|
||||
|
||||
.refund-edit-content {
|
||||
flex: 1;
|
||||
padding: 20rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.refund-cell {
|
||||
width: 100%;
|
||||
height: 116rpx;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 0 40rpx;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
|
||||
.refund-content-cell {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 40rpx 28rpx;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
|
||||
.remark-content {
|
||||
padding: 30rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||||
margin: 28rpx 0;
|
||||
min-height: 260rpx;
|
||||
align-items: stretch;
|
||||
|
||||
.remark-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.remark-count {
|
||||
color: #acacac;
|
||||
margin-top: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.form-label {
|
||||
color: $app_fc_main;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.refund-edit-footer {
|
||||
padding: 20rpx 60rpx 10rpx;
|
||||
background: #fff;
|
||||
margin-bottom: 10rpx;
|
||||
margin-bottom: constant(safe-area-inset-bottom);
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.refund-edit-btn {
|
||||
width: 100%;
|
||||
height: 92rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 92rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
417
src/pages/client/order/remark.vue
Normal file
@ -0,0 +1,417 @@
|
||||
<template>
|
||||
<view class="remark-page">
|
||||
<scroll-view class="remark-scroll" scroll-y>
|
||||
<!-- 文字评价 -->
|
||||
<view class="section text-review-section">
|
||||
<view class="section-title">文字评价</view>
|
||||
<view class="textarea-wrap">
|
||||
<textarea class="remark-textarea" :value="remark" :maxlength="remarkMaxLen" placeholder="请输入评价"
|
||||
placeholder-class="textarea-placeholder" :auto-height="true" @input="onRemarkChange" />
|
||||
<view class="char-count">{{ remark.length }}/{{ remarkMaxLen }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 上传图片 -->
|
||||
<view class="section upload-section">
|
||||
<view class="section-title">上传图片 <text class="tips">(最多可上传3张)</text></view>
|
||||
<view class="image-upload-grid">
|
||||
<view v-for="(item, index) in imgList" :key="index" class="image-item">
|
||||
<image class="uploaded-image" :src="item.fullUrl || item.url" mode="aspectFill" @click="previewImage(item.fullUrl || item.url, imgList.map(i => i.fullUrl || i.url))" />
|
||||
<view class="delete-btn" @click="deleteImage(index)">
|
||||
<text class="delete-icon">×</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="imgList.length < 3" class="image-item upload-placeholder" @click="chooseImage">
|
||||
<text class="add-icon">+</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 星级评分 -->
|
||||
<view class="section rating-section">
|
||||
<view class="rating-row">
|
||||
<text class="rating-label">描述相符</text>
|
||||
<view class="star-row">
|
||||
<image v-for="v in 5" :key="v" class="star-icon"
|
||||
:src="starDesc >= v ? require('@/static/images/star.png') : require('@/static/images/star_dark.png')"
|
||||
@click="starDesc = v" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="rating-row" style="margin-top: 40rpx;">
|
||||
<text class="rating-label">服务态度</text>
|
||||
<view class="star-row">
|
||||
<image v-for="v in 5" :key="v" class="star-icon"
|
||||
:src="starService >= v ? require('@/static/images/star.png') : require('@/static/images/star_dark.png')"
|
||||
@click="starService = v" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 洗护对比图(有数据时展示) -->
|
||||
<!-- <view v-if="showWashImg" class="section wash-compare-section" @click="showModal = true">
|
||||
<view class="wash-compare-row">
|
||||
<text class="wash-compare-label">洗护对比图</text>
|
||||
<image class="arrow-icon" src="@/static/images/arrow_right_black.png" mode="aspectFit" />
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<view class="bottom-placeholder" />
|
||||
</scroll-view>
|
||||
|
||||
<view class="submit-footer">
|
||||
<view class="submit-btn" @click="submit">提交</view>
|
||||
</view>
|
||||
|
||||
<compare-modal v-if="showModal" :before-imgs="washBeforeImgs" :after-imgs="washAfterImgs" :select-imgs="selectImgs"
|
||||
:is-can-remark="true" @close="showModal = false" @add="selectImgsChange" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createRemark } from "@/api/shop";
|
||||
import CompareModal from "@/components/CompareModal.vue";
|
||||
import { ORDER_TYPE_GOODS } from "@/constants/app.business";
|
||||
import { uploadImageToOSS_PUT } from "@/utils/oss";
|
||||
|
||||
export default {
|
||||
components: { CompareModal },
|
||||
data() {
|
||||
return {
|
||||
orderId: "",
|
||||
remark: "",
|
||||
remarkMaxLen: 300,
|
||||
starDesc: 5,
|
||||
starService: 5,
|
||||
imgList: [],
|
||||
washBeforeImgs: [],
|
||||
washAfterImgs: [],
|
||||
selectImgs: [],
|
||||
showModal: false,
|
||||
orderType: ORDER_TYPE_GOODS,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showWashImg() {
|
||||
return [...this.washBeforeImgs, ...this.washAfterImgs].length > 0;
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
console.log(options, '--')
|
||||
this.orderId = options.orderId || "";
|
||||
if (options.orderType) this.orderType = options.orderType;
|
||||
const eventChannel = this.getOpenerEventChannel();
|
||||
if (eventChannel && typeof eventChannel.on === "function") {
|
||||
eventChannel.on("remarkInfo", (data) => {
|
||||
this.washAfterImgs = data.afterImages || [];
|
||||
this.washBeforeImgs = data.beforeImages || [];
|
||||
this.selectImgs = data.remarkImages || [];
|
||||
});
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
const ec = this.getOpenerEventChannel();
|
||||
if (ec && typeof ec.emit === "function") ec.emit("refreshData");
|
||||
},
|
||||
methods: {
|
||||
selectImgsChange(imgs) {
|
||||
this.selectImgs = imgs;
|
||||
this.showModal = false;
|
||||
},
|
||||
onRemarkChange(e) {
|
||||
const val = (e && e.detail && e.detail.value) || (e && e.mp && e.mp.detail && e.mp.detail.value) || "";
|
||||
this.remark = String(val).slice(0, this.remarkMaxLen);
|
||||
},
|
||||
chooseImage() {
|
||||
const remaining = 3 - this.imgList.length;
|
||||
if (remaining <= 0) {
|
||||
uni.showToast({ title: "最多只能上传3张图片", icon: "none" });
|
||||
return;
|
||||
}
|
||||
uni.chooseImage({
|
||||
count: remaining,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
const tempFilePaths = res.tempFilePaths || [];
|
||||
if (tempFilePaths.length === 0) return;
|
||||
this.uploadImages(tempFilePaths);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('选择图片失败', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
async uploadImages(filePaths) {
|
||||
uni.showLoading({ title: "图片上传中...", icon: "none", mask: true });
|
||||
try {
|
||||
const uploadTasks = filePaths.map(filePath => this.uploadImageToOSS(filePath));
|
||||
await Promise.all(uploadTasks);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: "上传成功", icon: "success" });
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: error?.message || "上传失败", icon: "none" });
|
||||
}
|
||||
},
|
||||
async uploadImageToOSS(filePath) {
|
||||
try {
|
||||
const { url, objectKey } = await uploadImageToOSS_PUT(filePath);
|
||||
this.imgList.push({
|
||||
url: objectKey,
|
||||
fullUrl: url,
|
||||
});
|
||||
this.$forceUpdate();
|
||||
} catch (error) {
|
||||
console.error('图片上传失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
deleteImage(index) {
|
||||
this.imgList.splice(index, 1);
|
||||
this.$forceUpdate();
|
||||
},
|
||||
previewImage(current, urls) {
|
||||
uni.previewImage({
|
||||
current: current,
|
||||
urls: urls || [current]
|
||||
});
|
||||
},
|
||||
submit() {
|
||||
if (!this.remark || !this.remark.trim()) {
|
||||
uni.showToast({ title: "请输入评价内容", icon: "none" });
|
||||
return;
|
||||
}
|
||||
const pics = [...this.selectImgs, ...this.imgList].map((v) => (v.url || v.objectKey || v.filename || v.fullUrl)).filter(Boolean);
|
||||
if (!pics.length) {
|
||||
uni.showToast({ title: "请上传图片", icon: "none" });
|
||||
return;
|
||||
}
|
||||
uni.showLoading({ title: "提交中...", icon: "none" });
|
||||
createRemark({
|
||||
order_id: +this.orderId,
|
||||
type: this.orderType,
|
||||
imgs: pics,
|
||||
content: this.remark.trim(),
|
||||
description: this.starDesc || 0,
|
||||
attitude: this.starService || 0,
|
||||
})
|
||||
.then(() => {
|
||||
uni.hideLoading();
|
||||
uni.$emit("createRemarkSuccess");
|
||||
uni.navigateBack();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: err?.message || err || "提交失败", icon: "none" });
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.remark-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.remark-scroll {
|
||||
height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #272427;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.tips {
|
||||
font-size: 20rpx;
|
||||
color: #ACACAC;
|
||||
}
|
||||
}
|
||||
|
||||
/* 文字评价 */
|
||||
.text-review-section {
|
||||
.textarea-wrap {
|
||||
position: relative;
|
||||
padding: 20rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 16rpx;
|
||||
min-height: 240rpx;
|
||||
}
|
||||
|
||||
.remark-textarea {
|
||||
width: 100%;
|
||||
min-height: 200rpx;
|
||||
font-size: 28rpx;
|
||||
color: #272427;
|
||||
line-height: 1.5;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.textarea-placeholder {
|
||||
color: #acacac;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.char-count {
|
||||
position: absolute;
|
||||
right: 32rpx;
|
||||
bottom: 24rpx;
|
||||
font-size: 24rpx;
|
||||
color: #9b939a;
|
||||
}
|
||||
}
|
||||
|
||||
/* 上传图片 */
|
||||
.upload-section {
|
||||
.image-upload-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.image-item {
|
||||
position: relative;
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.uploaded-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.upload-placeholder {
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1rpx dashed #d0d0d0;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
font-size: 64rpx;
|
||||
color: #999;
|
||||
font-weight: 300;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 星级 */
|
||||
.rating-section {
|
||||
margin-top: 24rpx;
|
||||
padding: 20rpx;
|
||||
|
||||
.rating-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.rating-label {
|
||||
font-size: 24rpx;
|
||||
color: #3D3D3D;
|
||||
}
|
||||
|
||||
.star-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.star-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 洗护对比图 */
|
||||
.wash-compare-section {
|
||||
.wash-compare-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx 32rpx;
|
||||
background: #f5f5f5;
|
||||
margin: 0 24rpx 24rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.wash-compare-label {
|
||||
font-size: 28rpx;
|
||||
color: #272427;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-placeholder {
|
||||
height: 180rpx;
|
||||
}
|
||||
|
||||
.submit-footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 24rpx 32rpx;
|
||||
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
background: #fe019b;
|
||||
border-radius: 44rpx;
|
||||
}
|
||||
</style>
|
||||
BIN
src/pages/client/order/static/kefu.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/pages/client/order/static/order_cancel.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/pages/client/order/static/order_done.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/pages/client/order/static/order_go.png
Normal file
|
After Width: | Height: | Size: 722 B |
BIN
src/pages/client/order/static/order_ungo.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/pages/client/order/static/order_unpay.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/pages/client/order/static/phone.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/pages/client/order/static/refund_undo.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/pages/client/order/static/sliver-icon.png
Normal file
|
After Width: | Height: | Size: 838 B |
BIN
src/pages/client/order/static/wx_pay.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
293
src/pages/client/order/wash-compare.vue
Normal file
@ -0,0 +1,293 @@
|
||||
<template>
|
||||
<view class="wash-compare-page">
|
||||
<!-- 小哇信息(取数组第 0 条) -->
|
||||
<view class="content-card wa-header-card" v-if="waHeader">
|
||||
<view class="wa-header-section">
|
||||
<image class="wa-avatar" :src="waHeader.avatar" mode="aspectFill" />
|
||||
<text class="wa-name">{{ waHeader.wa_name || '小哇名字' }}</text>
|
||||
<text class="order-date">{{ waHeader.updated_at || '' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="content-card" v-for="(row, rowIndex) in orderList" :key="row.order_id || rowIndex">
|
||||
<!-- 头部:宠物信息 -->
|
||||
<view class="header-section">
|
||||
<image class="pet-avatar" :src="row.pet_avatar || defaultAvatar" mode="aspectFill" />
|
||||
<view class="pet-info">
|
||||
<text class="pet-name">{{ row.pet_name || '--' }}</text>
|
||||
<text class="pet-desc">{{ formatPetDesc(row) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 洗护前 -->
|
||||
<view class="image-section">
|
||||
<text class="section-label">洗护前</text>
|
||||
<view class="image-grid">
|
||||
<view v-for="(item, index) in row.beforeImages" :key="index" class="image-item"
|
||||
@click="previewImage(item.fullUrl || item.url, row.beforeImages.map(img => img.fullUrl || img.url))">
|
||||
<image class="grid-image" :src="item.fullUrl || item.url" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 洗护后 -->
|
||||
<view class="image-section">
|
||||
<text class="section-label">洗护后</text>
|
||||
<view class="image-grid">
|
||||
<view v-for="(item, index) in row.afterImages" :key="index" class="image-item"
|
||||
@click="previewImage(item.fullUrl || item.url, row.afterImages.map(img => img.fullUrl || img.url))">
|
||||
<image class="grid-image" :src="item.fullUrl || item.url" mode="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getServiceImgs } from "@/api/shop";
|
||||
import { imgPrefix } from "@/utils/common";
|
||||
import moment from "moment";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
orderId: "",
|
||||
orderList: [],
|
||||
defaultAvatar: `${imgPrefix}record_avator.png`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
waHeader() {
|
||||
return this.orderList.length > 0 ? this.orderList[0] : null;
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
this.orderId = options.orderid || options.order_id || "";
|
||||
if (this.orderId) {
|
||||
this.getOrderData();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: "订单ID不存在",
|
||||
icon: "none",
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getOrderData() {
|
||||
uni.showLoading({
|
||||
title: "加载中...",
|
||||
icon: "none",
|
||||
});
|
||||
try {
|
||||
const res = await getServiceImgs(+this.orderId);
|
||||
const raw = res?.data || res?.info;
|
||||
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" ? [raw] : [];
|
||||
|
||||
this.orderList = list.map((data) => {
|
||||
let beforeList = [];
|
||||
if (typeof data.json_before_service === "string" && data.json_before_service.trim()) {
|
||||
const str = data.json_before_service.trim();
|
||||
beforeList = str.indexOf(",") > -1
|
||||
? str.split(",").map((s) => s.trim()).filter((s) => s)
|
||||
: [str];
|
||||
}
|
||||
if (!beforeList.length && (Array.isArray(data.before_imgs) || Array.isArray(data.before))) {
|
||||
beforeList = data.before_imgs || data.before || [];
|
||||
}
|
||||
const beforeImages = beforeList
|
||||
.map((item) => ({
|
||||
url: typeof item === "string" ? item : item?.url || item?.objectKey || "",
|
||||
fullUrl:
|
||||
typeof item === "string"
|
||||
? item
|
||||
: item?.fullUrl || item?.url || item?.objectKey || "",
|
||||
}))
|
||||
.filter((item) => item.url || item.fullUrl);
|
||||
|
||||
let afterList = [];
|
||||
if (typeof data.json_after_service === "string" && data.json_after_service.trim()) {
|
||||
const str = data.json_after_service.trim();
|
||||
afterList = str.indexOf(",") > -1
|
||||
? str.split(",").map((s) => s.trim()).filter((s) => s)
|
||||
: [str];
|
||||
}
|
||||
if (!afterList.length && (Array.isArray(data.after_imgs) || Array.isArray(data.after))) {
|
||||
afterList = data.after_imgs || data.after || [];
|
||||
}
|
||||
const afterImages = afterList
|
||||
.map((item) => ({
|
||||
url: typeof item === "string" ? item : item?.url || item?.objectKey || "",
|
||||
fullUrl:
|
||||
typeof item === "string"
|
||||
? item
|
||||
: item?.fullUrl || item?.url || item?.objectKey || "",
|
||||
}))
|
||||
.filter((item) => item.url || item.fullUrl);
|
||||
|
||||
return {
|
||||
...data,
|
||||
beforeImages,
|
||||
afterImages,
|
||||
};
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("获取服务图片失败:", error);
|
||||
uni.showToast({
|
||||
title: error?.message || "获取服务图片失败",
|
||||
icon: "none",
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
} finally {
|
||||
uni.hideLoading();
|
||||
}
|
||||
},
|
||||
formatPetDesc(item) {
|
||||
if (!item) return "";
|
||||
const parts = [];
|
||||
if (item.breed_name) parts.push(item.breed_name);
|
||||
const gender = (item.pet_gender || "").toLowerCase();
|
||||
parts.push(gender === "male" ? "男生" : gender === "female" ? "女生" : "");
|
||||
if (item.pet_age != null && item.pet_age !== "") {
|
||||
const age = Number(item.pet_age);
|
||||
parts.push(age >= 12 ? age / 12 + "岁" : age + "个月");
|
||||
}
|
||||
return parts.filter(Boolean).join("/");
|
||||
},
|
||||
previewImage(current, urls) {
|
||||
uni.previewImage({
|
||||
current: current,
|
||||
urls: urls || [current],
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wash-compare-page {
|
||||
min-height: 100vh;
|
||||
background: #f8f8f8;
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.content-card {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wa-header-card {
|
||||
.wa-header-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 0;
|
||||
|
||||
.wa-avatar {
|
||||
width: 92rpx;
|
||||
height: 92rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.wa-name {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #272427;
|
||||
}
|
||||
|
||||
.order-date {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.pet-avatar {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 8rpx;
|
||||
flex-shrink: 0;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.pet-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.pet-name {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.pet-desc {
|
||||
font-size: 20rpx;
|
||||
color: #666666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.image-section {
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #726E71;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.image-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 160rpx);
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.image-item {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.grid-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1034
src/pages/client/pet-profile/index.vue
Normal file
368
src/pages/client/petOrder/home-service-order-item.vue
Normal file
@ -0,0 +1,368 @@
|
||||
<template>
|
||||
<view class="home-service-order-item" @click="goToOrderDetail">
|
||||
<view class="order-header">
|
||||
<text class="order-number">订单编号: {{ orderInfo.order_no || orderInfo.order_id || '--' }}</text>
|
||||
<view class="status-btn" :class="statusBtnClass">
|
||||
<text class="status-text">{{ orderStatus }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-view"></view>
|
||||
<view class="order-content">
|
||||
<!-- 宠物信息 -->
|
||||
<view class="info-row">
|
||||
<text class="info-label">{{ petsCountLabel }}</text>
|
||||
<text class="info-value">{{ petsNames }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 服务时长 -->
|
||||
<view class="info-row" >
|
||||
<text class="info-label">{{ serviceDurationTypeLabel }}</text>
|
||||
<text class="info-value">{{ serviceDurationLabel }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 服务地址 -->
|
||||
<view class="info-row" >
|
||||
<text class="info-label">服务地址</text>
|
||||
<text class="info-value">{{ serviceAddress }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<view class="order-footer" v-if="showFooterButtons" @click.stop>
|
||||
<!-- 未支付状态:显示取消预约和立即支付 -->
|
||||
<template v-if="isUnpay">
|
||||
<view class="footer-btn cancel-btn" @click.stop="cancelOrder">
|
||||
<text class="footer-btn-text">取消预约</text>
|
||||
</view>
|
||||
<view class="footer-btn pay-btn" @click.stop="payNow">
|
||||
<text class="footer-btn-text">立即支付</text>
|
||||
</view>
|
||||
</template>
|
||||
<!-- 已预约状态:显示取消预约 -->
|
||||
<view v-else-if="isReserved" class="footer-btn cancel-btn" @click.stop="cancelOrder">
|
||||
<text class="footer-btn-text">取消预约</text>
|
||||
</view>
|
||||
<!-- 服务中状态:显示查看服务图片 -->
|
||||
<view v-else-if="isService" class="footer-btn service-btn" @click.stop="viewServiceImages">
|
||||
<text class="footer-btn-text">查看服务图片</text>
|
||||
</view>
|
||||
<!-- 已取消或已完成状态:显示再次预约 -->
|
||||
<view v-else-if="isCancelled || isCompleted" class="book-again-btn" @click.stop="bookAgain">
|
||||
<text class="book-again-text">再次预约</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { orderStatusList, ORDER_STATUS_CANCELED, ORDER_STATUS_COMPLETED, ORDER_STATUS_UNPAY, ORDER_STATUS_RESERVED, ORDER_STATUS_SERVICE } from "@/pageHome/constants/home";
|
||||
import { jumpToWeChat } from "@/utils/common";
|
||||
|
||||
export default {
|
||||
name: 'HomeServiceOrderItem',
|
||||
props: {
|
||||
orderInfo: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
orderStatus() {
|
||||
// 根据订单状态返回对应的文本
|
||||
const statusItem = orderStatusList.find(
|
||||
(item) => `${item.value}` === `${this.orderInfo?.status}`
|
||||
);
|
||||
return statusItem?.label || '--';
|
||||
},
|
||||
isUnpay() {
|
||||
return this.orderInfo?.status === ORDER_STATUS_UNPAY;
|
||||
},
|
||||
isReserved() {
|
||||
return this.orderInfo?.status === ORDER_STATUS_RESERVED;
|
||||
},
|
||||
isService() {
|
||||
return this.orderInfo?.status === ORDER_STATUS_SERVICE;
|
||||
},
|
||||
isCancelled() {
|
||||
return this.orderInfo?.status === ORDER_STATUS_CANCELED;
|
||||
},
|
||||
isCompleted() {
|
||||
return this.orderInfo?.status === ORDER_STATUS_COMPLETED || this.orderInfo?.status === 5;
|
||||
},
|
||||
showFooterButtons() {
|
||||
// 未支付、已预约、服务中、已完成或已取消状态显示按钮
|
||||
return this.isUnpay || this.isReserved || this.isService || this.isCancelled || this.isCompleted;
|
||||
},
|
||||
statusBtnClass() {
|
||||
// 根据订单状态返回不同的样式类
|
||||
if (this.orderInfo?.status === ORDER_STATUS_UNPAY) {
|
||||
return 'status-unpay';
|
||||
} else if (this.orderInfo?.status === ORDER_STATUS_CANCELED) {
|
||||
return 'status-cancelled';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
petsCountLabel() {
|
||||
// 显示多少只宠物
|
||||
if (this.orderInfo.home_service_pets && Array.isArray(this.orderInfo.home_service_pets) && this.orderInfo.home_service_pets.length > 0) {
|
||||
return `${this.orderInfo.home_service_pets.length}只宠物`;
|
||||
}
|
||||
return '宠物';
|
||||
},
|
||||
petsNames() {
|
||||
// 显示宠物名称
|
||||
if (this.orderInfo.home_service_pets && Array.isArray(this.orderInfo.home_service_pets) && this.orderInfo.home_service_pets.length > 0) {
|
||||
const petNames = this.orderInfo.home_service_pets.map(pet => pet.pet_name || '').filter(Boolean);
|
||||
if (petNames.length > 0) {
|
||||
return petNames.join('、');
|
||||
}
|
||||
}
|
||||
return '--';
|
||||
},
|
||||
serviceDurationLabel() {
|
||||
// 使用 home_service_slots 数组长度显示天数
|
||||
if (this.orderInfo.home_service_slots && Array.isArray(this.orderInfo.home_service_slots) && this.orderInfo.home_service_slots.length > 0) {
|
||||
return `共${this.orderInfo.home_service_slots.length}天`;
|
||||
}
|
||||
return '--';
|
||||
},
|
||||
serviceDurationTypeLabel() {
|
||||
// 根据 service_type 显示不同的标签
|
||||
const serviceType = this.orderInfo && this.orderInfo.home_service_order && this.orderInfo.home_service_order.service_type;
|
||||
return serviceType === 'WALK' ? '遛宠时长' : '喂宠时长';
|
||||
},
|
||||
serviceAddress() {
|
||||
// 获取服务地址
|
||||
return this.orderInfo.home_service_order.address;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToOrderDetail() {
|
||||
// 如果是未支付状态,不允许进入详情
|
||||
if (this.isUnpay) {
|
||||
return;
|
||||
}
|
||||
// 跳转到订单详情页面
|
||||
const orderId = this.orderInfo?.home_service_order?.id || this.orderInfo?.order_id;
|
||||
if (orderId) {
|
||||
uni.navigateTo({
|
||||
url: `/pageHome/service/order-detail?order_id=${orderId}&source=home_service`
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '订单信息错误',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
bookAgain() {
|
||||
// 再次预约的逻辑:根据 service_type 跳转到不同的服务页面
|
||||
const serviceType = this.orderInfo?.home_service_order?.service_type;
|
||||
let serviceTypeParam = '';
|
||||
|
||||
if (serviceType === 'FEED') {
|
||||
serviceTypeParam = 'feeding';
|
||||
} else if (serviceType === 'WALK') {
|
||||
serviceTypeParam = 'walking';
|
||||
}
|
||||
|
||||
if (serviceTypeParam) {
|
||||
uni.navigateTo({
|
||||
url: `/pageHome/service/feeding?serviceType=${serviceTypeParam}`
|
||||
});
|
||||
} else {
|
||||
// 如果 service_type 不匹配,使用默认值或提示
|
||||
uni.showToast({
|
||||
title: '服务类型错误',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
cancelOrder() {
|
||||
// 取消预约
|
||||
this.$emit('cancel-order', this.orderInfo);
|
||||
},
|
||||
payNow() {
|
||||
// 立即支付:跳转到订单详情页面
|
||||
const orderId = this.orderInfo?.home_service_order?.id || this.orderInfo?.order_id;
|
||||
if (orderId) {
|
||||
uni.navigateTo({
|
||||
url: `/pageHome/service/payment-confirm?orderId=${orderId}`
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '订单信息错误',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
viewServiceImages() {
|
||||
// 查看服务图片,跳转到客服
|
||||
jumpToWeChat();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home-service-order-item {
|
||||
width: calc(100% - 40rpx);
|
||||
margin: 20rpx auto;
|
||||
padding: 20rpx;
|
||||
border-radius: 30rpx;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.order-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.order-number {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.status-btn {
|
||||
width: 104rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fef6ff;
|
||||
|
||||
.status-text {
|
||||
font-size: 20rpx;
|
||||
color: $app_fc_mark;
|
||||
}
|
||||
|
||||
&.status-unpay {
|
||||
background-color: #FEF6FF;
|
||||
|
||||
.status-text {
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
|
||||
&.status-cancelled {
|
||||
background-color: #f7f7f7;
|
||||
|
||||
.status-text {
|
||||
color: #afa5ae;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line-view {
|
||||
width: 100%;
|
||||
height: 2rpx;
|
||||
background-color: #ececec;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.info-label {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.order-footer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.cancel-btn {
|
||||
padding: 12rpx 18rpx;
|
||||
border-radius: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #9B939A;
|
||||
margin-left: 24rpx;
|
||||
|
||||
.footer-btn-text {
|
||||
font-size: 24rpx;
|
||||
color: $app_fc_main;
|
||||
}
|
||||
}
|
||||
|
||||
.pay-btn {
|
||||
padding: 12rpx 18rpx;
|
||||
border-radius: 100px;
|
||||
border: 2rpx solid #ff19a0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 24rpx;
|
||||
|
||||
.footer-btn-text {
|
||||
font-size: 24rpx;
|
||||
color: #ff19a0;
|
||||
}
|
||||
}
|
||||
|
||||
.service-btn {
|
||||
padding: 12rpx 18rpx;
|
||||
border-radius: 100px;
|
||||
border: 2rpx solid #ff19a0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 24rpx;
|
||||
|
||||
.footer-btn-text {
|
||||
font-size: 24rpx;
|
||||
color: #ff19a0;
|
||||
}
|
||||
}
|
||||
|
||||
.book-again-btn {
|
||||
padding: 12rpx 18rpx;
|
||||
border-radius: 100px;
|
||||
border: 2rpx solid #ff19a0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 24rpx;
|
||||
|
||||
.book-again-text {
|
||||
font-size: 24rpx;
|
||||
color: #ff19a0;
|
||||
}
|
||||
}
|
||||
|
||||
// 最后一个按钮的特殊样式
|
||||
>view:last-child {
|
||||
border: 2rpx solid #FF19A0 !important;
|
||||
|
||||
text {
|
||||
color: #FF19A0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
436
src/pages/client/petOrder/home-training-order-item.vue
Normal file
@ -0,0 +1,436 @@
|
||||
<template>
|
||||
<view class="home-training-order-item">
|
||||
<!-- 订单头部 -->
|
||||
<view class="order-header">
|
||||
<text class="order-number">订单编号: {{ orderNumber }}</text>
|
||||
<view class="status-btn" :class="statusBtnClass">
|
||||
<text class="status-text">{{ orderStatus }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-view"></view>
|
||||
<!-- 订单内容 -->
|
||||
<view class="order-content">
|
||||
<!-- 宠物信息 -->
|
||||
<view class="info-row pet-row">
|
||||
<image class="pet-icon" :src="petIcon" mode="aspectFit" />
|
||||
<text class="pet-name">{{ petName }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 服务地址 -->
|
||||
<view class="info-row" v-if="serviceAddress">
|
||||
<text class="info-label">服务地址</text>
|
||||
<text class="info-value">{{ serviceAddress }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 服务券 -->
|
||||
<!-- <view class="info-row" v-if="serviceCoupon">
|
||||
<text class="info-label">服务券</text>
|
||||
<text class="info-value">{{ serviceCoupon }}</text>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<!-- 宠物报告横幅 -->
|
||||
<!-- <view class="report-banner" v-if="hasReport">
|
||||
<text class="report-text">该订单宠物报告已生成</text>
|
||||
<view class="view-report-btn" @click="viewReport">
|
||||
<text class="view-report-text">立即查看</text>
|
||||
<text class="arrow">></text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="order-footer" v-if="showFooterButtons">
|
||||
<!-- 未支付状态:显示立即支付 -->
|
||||
<view v-if="isUnpay && !isPendingPay" class="footer-btn pay-btn" @click="payNow">
|
||||
<text class="footer-btn-text">立即支付</text>
|
||||
</view>
|
||||
|
||||
<!-- 待支付状态:显示购买课程 -->
|
||||
<view v-if="isPendingPay" class="footer-btn buy-course-btn" @click="buyCourse">
|
||||
<text class="footer-btn-text">购买课程</text>
|
||||
</view>
|
||||
|
||||
<!-- 已预约状态:显示取消预约 -->
|
||||
<view v-if="isReserved" class="footer-btn cancel-btn" @click="cancelOrder">
|
||||
<text class="footer-btn-text">取消预约</text>
|
||||
</view>
|
||||
|
||||
<!-- 服务中状态:显示查看合同和去评价 -->
|
||||
<template v-if="isService">
|
||||
<view class="footer-btn view-contract-btn" @click="viewContract">
|
||||
<text class="footer-btn-text">查看合同</text>
|
||||
</view>
|
||||
<view class="footer-btn evaluate-btn" @click="gotoEvaluate">
|
||||
<text class="footer-btn-text">去评价</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 已完成状态:显示去评价 -->
|
||||
<view v-if="isCompleted" class="footer-btn evaluate-btn" @click="gotoEvaluate">
|
||||
<text class="footer-btn-text">去评价</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { orderStatusList, ORDER_STATUS_CANCELED, ORDER_STATUS_SERVICE, ORDER_STATUS_COMPLETED, ORDER_STATUS_RESERVED, ORDER_STATUS_SEND, ORDER_STATUS_UNPAY } from "@/pageHome/constants/home";
|
||||
import { PET_TYPE_CAT } from "@/constants/app.business";
|
||||
|
||||
export default {
|
||||
name: 'HomeTrainingOrderItem',
|
||||
props: {
|
||||
orderInfo: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
orderNumber() {
|
||||
return this.orderInfo?.order_no || this.orderInfo?.order_id || '--';
|
||||
},
|
||||
orderStatus() {
|
||||
const statusItem = orderStatusList.find(
|
||||
(item) => `${item.value}` === `${this.orderInfo?.status}`
|
||||
);
|
||||
return statusItem?.label || '--';
|
||||
},
|
||||
statusBtnClass() {
|
||||
// 根据状态返回不同的样式类
|
||||
if (this.orderInfo?.status === ORDER_STATUS_UNPAY) {
|
||||
return 'status-unpay';
|
||||
} else if (this.orderInfo?.status === ORDER_STATUS_SERVICE) {
|
||||
return 'status-service';
|
||||
} else if (this.orderInfo?.status === ORDER_STATUS_COMPLETED) {
|
||||
return 'status-completed';
|
||||
} else if (this.orderInfo?.status === ORDER_STATUS_CANCELED) {
|
||||
return 'status-cancelled';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
petName() {
|
||||
return this.orderInfo?.home_training_order.pet_name;
|
||||
},
|
||||
petIcon() {
|
||||
// 根据宠物类型返回图标(粉色图标)
|
||||
const petType = this.orderInfo?.type || this.orderInfo?.home_training?.type;
|
||||
// 使用粉色图标,可以根据实际图片路径调整
|
||||
return require("@/static/images/dog.png");
|
||||
},
|
||||
appointmentTime() {
|
||||
// 格式化预约时间 "2024-12-13 16:00-17:00"
|
||||
if (this.orderInfo?.slots && Array.isArray(this.orderInfo.slots) && this.orderInfo.slots.length > 0) {
|
||||
const slot = this.orderInfo.slots[0];
|
||||
const date = slot.date || '';
|
||||
const period = slot.period_name || '';
|
||||
if (date && period) {
|
||||
// 将 "16:00:00 ~ 17:00:00" 转换为 "16:00-17:00"
|
||||
const formattedPeriod = period.replace(/:00/g, '').replace(/\s*~\s*/g, '-');
|
||||
return `${date} ${formattedPeriod}`;
|
||||
}
|
||||
}
|
||||
// 如果有 order_date 和 period_name
|
||||
if (this.orderInfo?.order_date && this.orderInfo?.period_name) {
|
||||
const formattedPeriod = this.orderInfo.period_name.replace(/:00/g, '').replace(/\s*~\s*/g, '-');
|
||||
return `${this.orderInfo.order_date} ${formattedPeriod}`;
|
||||
}
|
||||
// 如果有 service_time 字段
|
||||
if (this.orderInfo?.service_time) {
|
||||
return this.orderInfo.service_time;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
serviceAddress() {
|
||||
return this.orderInfo?.home_training_order?.address_detail;
|
||||
},
|
||||
hasReport() {
|
||||
// 判断是否有宠物报告(可以根据实际字段判断)
|
||||
return this.orderInfo?.has_report || this.orderInfo?.report_id || false;
|
||||
},
|
||||
isUnpay() {
|
||||
return this.orderInfo?.status === ORDER_STATUS_UNPAY;
|
||||
},
|
||||
isPendingPay() {
|
||||
// 待支付状态,如果和未支付是同一个状态,可以根据其他字段判断
|
||||
// 例如:如果未支付且需要购买课程,则显示"购买课程"按钮
|
||||
// 这里暂时返回 false,如果待支付是独立状态,需要添加对应的状态判断
|
||||
// 如果需要区分,可以检查 orderInfo 中的其他字段,比如 order_type 或 is_course_order
|
||||
return this.orderInfo?.is_course_order || false;
|
||||
},
|
||||
isReserved() {
|
||||
return this.orderInfo?.status === ORDER_STATUS_RESERVED;
|
||||
},
|
||||
isService() {
|
||||
return this.orderInfo?.status === ORDER_STATUS_SERVICE;
|
||||
},
|
||||
isCompleted() {
|
||||
return this.orderInfo?.status === ORDER_STATUS_COMPLETED;
|
||||
},
|
||||
showFooterButtons() {
|
||||
// 根据订单状态决定是否显示底部按钮
|
||||
return this.isUnpay || this.isPendingPay || this.isReserved || this.isService || this.isCompleted;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
viewReport() {
|
||||
// 查看宠物报告
|
||||
this.$emit('view-report', this.orderInfo);
|
||||
},
|
||||
buyGoods() {
|
||||
// 随车购商品
|
||||
this.$emit('buy-goods', this.orderInfo);
|
||||
},
|
||||
addService() {
|
||||
// 添加附加项
|
||||
this.$emit('add-service', this.orderInfo);
|
||||
},
|
||||
payNow() {
|
||||
// 立即支付 - 跳转到支付确认页面
|
||||
const orderId = this.orderInfo?.home_training_order?.id || this.orderInfo?.id || this.orderInfo?.order_id;
|
||||
if (!orderId) {
|
||||
uni.showToast({
|
||||
title: '订单ID不存在',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pageHome/service/training-order-detail?order_id=${orderId}&source=home_training`
|
||||
});
|
||||
},
|
||||
buyCourse() {
|
||||
// 购买课程
|
||||
this.$emit('buy-course', this.orderInfo);
|
||||
},
|
||||
cancelOrder() {
|
||||
// 取消预约
|
||||
this.$emit('cancel-order', this.orderInfo);
|
||||
},
|
||||
viewContract() {
|
||||
// 查看合同
|
||||
this.$emit('view-contract', this.orderInfo);
|
||||
},
|
||||
gotoEvaluate() {
|
||||
// 去评价
|
||||
this.$emit('goto-evaluate', this.orderInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home-training-order-item {
|
||||
width: calc(100% - 40rpx);
|
||||
margin: 20rpx auto;
|
||||
padding: 20rpx;
|
||||
border-radius: 30rpx;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.order-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.order-number {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.status-btn {
|
||||
width: 104rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fef6ff;
|
||||
|
||||
.status-text {
|
||||
font-size: 20rpx;
|
||||
color: $app_fc_mark;
|
||||
}
|
||||
|
||||
&.status-unpay {
|
||||
background-color: #FEF6FF;
|
||||
|
||||
.status-text {
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
|
||||
&.status-service {
|
||||
background-color: #fef6ff;
|
||||
|
||||
.status-text {
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
|
||||
&.status-completed {
|
||||
background-color: #f7f7f7;
|
||||
|
||||
.status-text {
|
||||
color: #afa5ae;
|
||||
}
|
||||
}
|
||||
|
||||
&.status-cancelled {
|
||||
background-color: #f7f7f7;
|
||||
|
||||
.status-text {
|
||||
color: #afa5ae;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line-view {
|
||||
width: 100%;
|
||||
height: 2rpx;
|
||||
background-color: #ececec;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.info-label {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.pet-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.pet-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
.pet-name {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.report-banner {
|
||||
margin-top: 20rpx;
|
||||
padding: 20rpx;
|
||||
background-color: #fef6ff;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.report-text {
|
||||
font-size: 24rpx;
|
||||
color: #FF19A0;
|
||||
}
|
||||
|
||||
.view-report-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
|
||||
.view-report-text {
|
||||
font-size: 24rpx;
|
||||
color: #FF19A0;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
font-size: 24rpx;
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.order-footer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.footer-btn {
|
||||
padding: 12rpx 18rpx;
|
||||
border-radius: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 24rpx;
|
||||
|
||||
.footer-btn-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
&.pay-btn {
|
||||
border: 2rpx solid #ff19a0;
|
||||
|
||||
.footer-btn-text {
|
||||
color: #ff19a0;
|
||||
}
|
||||
}
|
||||
|
||||
&.buy-course-btn {
|
||||
border: 2rpx solid #ff19a0;
|
||||
|
||||
.footer-btn-text {
|
||||
color: #ff19a0;
|
||||
}
|
||||
}
|
||||
|
||||
&.cancel-btn {
|
||||
border: 1px solid #9B939A;
|
||||
|
||||
.footer-btn-text {
|
||||
color: $app_fc_main;
|
||||
}
|
||||
}
|
||||
|
||||
&.view-contract-btn {
|
||||
border: 2rpx solid #ff19a0;
|
||||
|
||||
.footer-btn-text {
|
||||
color: #ff19a0;
|
||||
}
|
||||
}
|
||||
|
||||
&.evaluate-btn {
|
||||
border: 2rpx solid #ff19a0;
|
||||
|
||||
.footer-btn-text {
|
||||
color: #ff19a0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 最后一个按钮的特殊样式
|
||||
>view:last-child {
|
||||
border: 2rpx solid #FF19A0 !important;
|
||||
|
||||
text {
|
||||
color: #FF19A0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
406
src/pages/client/petOrder/index.vue
Normal file
@ -0,0 +1,406 @@
|
||||
<template>
|
||||
<view class="order-page-container">
|
||||
<scroll-view class="tab-view" scroll-x="true" :show-scrollbar="false">
|
||||
<view class="tab-view-content">
|
||||
<view class="tab-item" v-for="item in tabList" :key="item.value" @click.stop="selectOrderStatus(item)">
|
||||
<text class="fs-28 app-fc-normal" :class="{ 'tab-text ali-puhui-bold': item.value === currentStatus }">
|
||||
{{ item.label }}
|
||||
</text>
|
||||
<view class="tab-line" :class="{ 'tab-selected-line': item.value === currentStatus }" />
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="order-content">
|
||||
<view class="status-view-container" v-if="isLoading">
|
||||
<uni-load-more status="loading" />
|
||||
</view>
|
||||
<view class="status-view-container" v-if="!isLoading && orderList.length === 0">
|
||||
<uni-load-more status="noMore" />
|
||||
</view>
|
||||
<scroll-view
|
||||
class="scroll-view"
|
||||
scroll-y
|
||||
v-if="!isLoading && orderList.length"
|
||||
refresher-enabled
|
||||
:refresher-triggered="isRefresh"
|
||||
@refresherrefresh="refresherAction"
|
||||
@scrolltolower="loadMoreAction"
|
||||
refresher-background="transparent"
|
||||
>
|
||||
<!-- 根据 source 字段动态渲染不同的组件 -->
|
||||
<block v-for="(item, index) in orderList" :key="item.order_id || index">
|
||||
<!-- source 为 'pet_order' 时使用 reservation 组件 -->
|
||||
<reservation
|
||||
v-if="item.source === 'pet_order'"
|
||||
:order-info="item"
|
||||
@refresh-list="refresherAction"
|
||||
@cancel-order="handleCancelOrder"
|
||||
/>
|
||||
<!-- source 为 'home_service' 时使用 home-service-order-item 组件 -->
|
||||
<home-service-order-item
|
||||
v-else-if="item.source === 'home_service'"
|
||||
:order-info="item"
|
||||
@refresh-list="refresherAction"
|
||||
@cancel-order="handleCancelOrder"
|
||||
/>
|
||||
<!-- source 为 'home_training' 时使用 home-training-order-item 组件 -->
|
||||
<home-training-order-item
|
||||
v-else-if="item.source === 'home_training'"
|
||||
:order-info="item"
|
||||
@refresh-list="refresherAction"
|
||||
@pay-now="handlePayNow"
|
||||
@buy-course="handleBuyCourse"
|
||||
@cancel-order="handleCancelOrder"
|
||||
@view-contract="handleViewContract"
|
||||
@goto-evaluate="handleGotoEvaluate"
|
||||
/>
|
||||
</block>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 取消预约确认弹窗 - 放在父组件中避免被 overflow: hidden 裁剪 -->
|
||||
<pop-up-modal
|
||||
v-if="isShowCancelModal"
|
||||
content="确定要取消预约吗?"
|
||||
@confirm="confirmCancelOrder"
|
||||
@cancel="isShowCancelModal = false"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Reservation from './reservation.vue';
|
||||
import HomeServiceOrderItem from './home-service-order-item.vue';
|
||||
import HomeTrainingOrderItem from './home-training-order-item.vue';
|
||||
import { showOrderStatus } from "@/pageHome/constants/home";
|
||||
import { getOrderList, cancelHomeOrder, cancelTrainingOrder } from "@/api/order";
|
||||
import { cancelPetOrderRefund } from "@/api/login";
|
||||
import PopUpModal from "@/components/PopUpModal.vue";
|
||||
|
||||
export default {
|
||||
name: 'index',
|
||||
components: {
|
||||
Reservation,
|
||||
HomeServiceOrderItem,
|
||||
HomeTrainingOrderItem,
|
||||
PopUpModal
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabList: showOrderStatus,
|
||||
currentStatus: 0,
|
||||
isLoading: true,
|
||||
orderList: [],
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
isRefresh: false,
|
||||
isLoadMore: false,
|
||||
isNoMore: false,
|
||||
isShowCancelModal: false,
|
||||
cancelOrderInfo: null,
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
// 如果从外部传入状态,设置当前状态
|
||||
if (options.status !== undefined) {
|
||||
this.currentStatus = parseInt(options.status) || 0;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
uni.$on("createRemarkSuccess", this.refresherAction);
|
||||
uni.$on("refreshOrderList", this.refresherAction);
|
||||
this.initData();
|
||||
},
|
||||
destroyed() {
|
||||
uni.$off("createRemarkSuccess", this.refresherAction);
|
||||
uni.$off("refreshOrderList", this.refresherAction);
|
||||
},
|
||||
watch: {
|
||||
currentStatus: {
|
||||
handler(newVal, oldVal) {
|
||||
if (newVal !== oldVal && oldVal !== undefined) {
|
||||
this.initData();
|
||||
}
|
||||
},
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectOrderStatus(status) {
|
||||
if (this.currentStatus === status.value) {
|
||||
return;
|
||||
}
|
||||
this.currentStatus = status.value;
|
||||
},
|
||||
initData() {
|
||||
this.isLoading = true;
|
||||
this.pageNumber = 1;
|
||||
this.isRefresh = false;
|
||||
this.isLoadMore = false;
|
||||
this.isNoMore = false;
|
||||
this.orderList = [];
|
||||
this.getData();
|
||||
},
|
||||
refresherAction() {
|
||||
if (this.isRefresh) {
|
||||
return;
|
||||
}
|
||||
this.pageNumber = 1;
|
||||
this.isRefresh = true;
|
||||
this.isLoadMore = false;
|
||||
this.isNoMore = false;
|
||||
this.getData();
|
||||
},
|
||||
loadMoreAction() {
|
||||
console.log(this.isNoMore)
|
||||
if (this.isNoMore) {
|
||||
return;
|
||||
}
|
||||
this.pageNumber++;
|
||||
this.getData();
|
||||
},
|
||||
getData() {
|
||||
const value = uni.getStorageSync("userInfo");
|
||||
const params = {
|
||||
user_id: value.user_id,
|
||||
status: this.currentStatus || 0,
|
||||
page: this.pageNumber,
|
||||
page_size: this.pageSize
|
||||
};
|
||||
getOrderList(params)
|
||||
.then((res) => {
|
||||
let list = res?.data || [];
|
||||
if (this.pageNumber === 1) {
|
||||
this.orderList = list;
|
||||
} else {
|
||||
this.orderList = [...this.orderList, ...list];
|
||||
}
|
||||
this.isRefresh = false;
|
||||
this.isLoading = false;
|
||||
this.isLoadMore = false;
|
||||
this.isNoMore = list.length < 9;
|
||||
})
|
||||
.catch(() => {
|
||||
if (this.pageNumber !== 1) {
|
||||
this.pageNumber--;
|
||||
}
|
||||
this.isLoading = false;
|
||||
this.isRefresh = false;
|
||||
this.isLoadMore = false;
|
||||
});
|
||||
},
|
||||
handleBookAgain(orderInfo) {
|
||||
// 处理再次预约的逻辑
|
||||
// 可以跳转到预约页面
|
||||
console.log('再次预约', orderInfo);
|
||||
// 例如:uni.navigateTo({ url: '/pageHome/service/feeding?orderId=' + orderInfo.order_id });
|
||||
},
|
||||
handleViewReport(orderInfo) {
|
||||
// 处理查看宠物报告的逻辑
|
||||
console.log('查看宠物报告', orderInfo);
|
||||
// 可以跳转到报告详情页面
|
||||
// uni.navigateTo({ url: '/pages/client/report/detail?orderId=' + orderInfo.order_id });
|
||||
},
|
||||
handleBuyGoods(orderInfo) {
|
||||
// 处理随车购商品的逻辑
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/category/index?petOrderId=${orderInfo.order_id}&addressId=${orderInfo.address_id}`,
|
||||
});
|
||||
},
|
||||
handleAddService(orderInfo) {
|
||||
// 处理添加附加项的逻辑
|
||||
console.log('添加附加项', orderInfo);
|
||||
// 可以跳转到添加附加项页面或显示弹窗
|
||||
},
|
||||
handleCancelOrder(orderInfo) {
|
||||
// 显示取消预约确认弹窗
|
||||
this.cancelOrderInfo = orderInfo;
|
||||
this.isShowCancelModal = true;
|
||||
},
|
||||
confirmCancelOrder() {
|
||||
// 确认取消订单
|
||||
if (!this.cancelOrderInfo) {
|
||||
this.isShowCancelModal = false;
|
||||
return;
|
||||
}
|
||||
const orderInfo = this.cancelOrderInfo;
|
||||
this.isShowCancelModal = false;
|
||||
uni.showLoading({
|
||||
title: "正在取消订单",
|
||||
mask: true,
|
||||
});
|
||||
|
||||
// 根据订单类型调用不同的取消接口
|
||||
let cancelPromise;
|
||||
if (orderInfo.source === 'home_service') {
|
||||
// 上门服务订单使用 cancelHomeOrder 接口
|
||||
const orderId = orderInfo.home_service_order?.id;
|
||||
cancelPromise = cancelHomeOrder(orderId);
|
||||
} else if (orderInfo.source === 'pet_order') {
|
||||
// pet_order 类型订单使用 cancelPetOrderRefund 接口
|
||||
const orderId = orderInfo.pet_order?.order_id || orderInfo.order_id;
|
||||
cancelPromise = cancelPetOrderRefund({ order_id: Number(orderId) });
|
||||
} else {
|
||||
// 上门训练订单使用 cancelTrainingOrder 接口
|
||||
const orderId = orderInfo.home_training_order?.id;
|
||||
cancelPromise = cancelTrainingOrder({ order_id: Number(orderId) });
|
||||
}
|
||||
|
||||
cancelPromise
|
||||
.then((res) => {
|
||||
uni.hideLoading();
|
||||
// 判断是否成功:result === 'success'
|
||||
if (res.result === 'success') {
|
||||
this.cancelOrderInfo = null;
|
||||
uni.showToast({
|
||||
title:"取消成功",
|
||||
icon: "success",
|
||||
});
|
||||
// 刷新订单列表
|
||||
this.refresherAction();
|
||||
} else {
|
||||
// 失败:弹出错误信息
|
||||
this.cancelOrderInfo = null;
|
||||
uni.showToast({
|
||||
title: res.msg || res.message || "取消订单失败",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.hideLoading();
|
||||
this.cancelOrderInfo = null;
|
||||
// 捕获异常时显示错误信息
|
||||
const errorMsg = err?.msg || err?.message || err || "取消订单失败,请稍后重试";
|
||||
uni.showToast({
|
||||
title: errorMsg,
|
||||
icon: "none",
|
||||
});
|
||||
});
|
||||
},
|
||||
handlePayNow(orderInfo) {
|
||||
// 处理立即支付
|
||||
console.log('立即支付', orderInfo);
|
||||
// TODO: 实现支付逻辑
|
||||
uni.showToast({
|
||||
title: '支付功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
handleBuyCourse(orderInfo) {
|
||||
// 处理购买课程
|
||||
console.log('购买课程', orderInfo);
|
||||
// TODO: 实现购买课程逻辑
|
||||
uni.showToast({
|
||||
title: '购买课程功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
handleViewContract(orderInfo) {
|
||||
// 处理查看合同
|
||||
console.log('查看合同', orderInfo);
|
||||
// TODO: 实现查看合同逻辑,可能需要跳转到合同详情页
|
||||
uni.showToast({
|
||||
title: '查看合同功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
handleGotoEvaluate(orderInfo) {
|
||||
// 处理去评价
|
||||
const orderId = orderInfo.order_id || orderInfo.home_training_order?.order_id;
|
||||
const pinglunId = orderInfo.pinglun_id;
|
||||
|
||||
if (pinglunId) {
|
||||
// 如果已有评价,跳转到评价详情
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/remark/details?remarkId=${pinglunId}`,
|
||||
});
|
||||
} else {
|
||||
// 跳转到评价页面
|
||||
// TODO: 确认订单类型常量
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/order/remark?orderId=${orderId}&orderType=3`, // 3 可能是 home_training 的订单类型
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-page-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f7f8fa;
|
||||
}
|
||||
|
||||
.tab-view {
|
||||
width: 100%;
|
||||
height: 100rpx;
|
||||
background-color: #ffffff;
|
||||
white-space: nowrap;
|
||||
border-top: 2rpx solid #f4f4f4;
|
||||
flex-shrink: 0;
|
||||
|
||||
.tab-view-content {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 20rpx;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
transition: 0.2ms all;
|
||||
|
||||
.tab-text {
|
||||
color: $app_fc_main;
|
||||
white-space: nowrap;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-line {
|
||||
width: 24rpx;
|
||||
height: 10rpx;
|
||||
background-color: transparent;
|
||||
margin-top: 18rpx;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.tab-selected-line {
|
||||
background-color: $app_color_main;
|
||||
}
|
||||
}
|
||||
|
||||
.order-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.status-view-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.scroll-view {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding-bottom: 32rpx;
|
||||
}
|
||||
</style>
|
||||
514
src/pages/client/petOrder/reservation.vue
Normal file
@ -0,0 +1,514 @@
|
||||
<template>
|
||||
<view class="order-container">
|
||||
<order-cell
|
||||
v-if="orderInfo"
|
||||
:orderInfo="orderInfo.pet_order"
|
||||
:isShowReport="orderInfo.has_precheck"
|
||||
:key="orderInfo.order_id"
|
||||
@addService="showAddService"
|
||||
@callPhone="showCall"
|
||||
@cancelOrder="clickCancel"
|
||||
@gotoDetail="gotoDetail"
|
||||
@gotoEvaluate="gotoEvaluate"
|
||||
@gotoComparisonChart="gotoComparisonChart"
|
||||
@showGoods="showGoods"
|
||||
@payNow="handlePayNow"
|
||||
/>
|
||||
<view v-if="elasticLayer" class="elastic-layer" />
|
||||
<view class="additional-bottom" v-if="additionalBom">
|
||||
<image src="@/static/images/close.png" mode="aspectFit" class="close-icon"
|
||||
@click="(additionalBom = false), (elasticLayer = false)" />
|
||||
<view class="recharge-method">
|
||||
<view class="wechat">
|
||||
<view class="select">
|
||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
||||
<text class="x">微信</text>
|
||||
</view>
|
||||
<image v-if="selected1" class="not-selected" @click.stop="selectOption1('1')" src="@/static/images/w.png"
|
||||
mode="widthFix" />
|
||||
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||
</view>
|
||||
<view class="wechat">
|
||||
<view class="select">
|
||||
<image class="w" src="@/static/images/wallet.png" mode="widthFix" />
|
||||
<text class="x">钱包支付</text>
|
||||
</view>
|
||||
<image v-if="selected3" class="not-selected" @click.stop="selectOption2('2')" src="@/static/images/w.png"
|
||||
mode="widthFix" />
|
||||
<image v-if="selected4" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="payment" @click="wxPayAction">
|
||||
<text class="Z"> 支付 </text>
|
||||
</view>
|
||||
</view>
|
||||
<add-service-pay-modal v-if="isShowPayModal" @close="hidePayModal" :order-info="showOrderInfo"
|
||||
:new-weight="otherWeight" @paymentConfirm="paymentConfirm" />
|
||||
<call-modal :phone-number="showOrderInfo.phone" v-if="isShowCallModal" @close="hideCallModal" />
|
||||
<pay-success-modal v-if="isShowPaySuccessModal" @close="isShowPaySuccessModal = false" @ok="okPayAction" />
|
||||
<!-- CompareModal 已移除,改为跳转到新页面 -->
|
||||
<success-modal v-if="showSuccessModal" title="转发成功" message="请前往宠圈查看" @ok="jumpToCommunity" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OrderCell from "@/components/petOrder/order-cell.vue";
|
||||
import PaySuccessModal from "@/components/petOrder/pay-success-modal.vue";
|
||||
import {
|
||||
PRICE_DIFF_TYPE_SERVICE,
|
||||
} from "@/pageHome/constants/home";
|
||||
import {
|
||||
addServicePay,
|
||||
cancelOrder,
|
||||
getOrderList,
|
||||
payOrder,
|
||||
} from "@/api/order";
|
||||
import CallModal from "@/components/petOrder/call-modal.vue";
|
||||
import AddServicePayModal from "@/components/petOrder/add-service-pay-modal.vue";
|
||||
import { ORDER_TYPE_PET_SERVICE } from "@/constants/app.business";
|
||||
import SuccessModal from "@/components/SuccessModal.vue";
|
||||
import { walletTransaction } from "../../../api/login";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AddServicePayModal,
|
||||
CallModal,
|
||||
OrderCell,
|
||||
PaySuccessModal,
|
||||
SuccessModal,
|
||||
},
|
||||
props: {
|
||||
orderInfo: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
additionalBom: false,
|
||||
elasticLayer: false,
|
||||
selected1: true,
|
||||
selected2: false,
|
||||
selected3: true,
|
||||
selected4: false,
|
||||
recharge: true,
|
||||
record: false,
|
||||
WeChat: "",
|
||||
wallet: "",
|
||||
isShowPayModal: false,
|
||||
isShowCallModal: false,
|
||||
showOrderInfo: null,
|
||||
otherWeight: {},
|
||||
isShowPaySuccessModal: false,
|
||||
showSuccessModal: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
jumpToCommunity() {
|
||||
this.showSuccessModal = false;
|
||||
uni.redirectTo({
|
||||
url: "/pages/client/index/index?activePageId=communityPage",
|
||||
});
|
||||
},
|
||||
selectOption1(v) {
|
||||
this.WeChat = v;
|
||||
this.wallet = "";
|
||||
console.log(this.WeChat);
|
||||
this.selected1 = false;
|
||||
this.selected2 = true;
|
||||
this.selected3 = true;
|
||||
this.selected4 = false;
|
||||
},
|
||||
selectOption2(v) {
|
||||
this.WeChat = "";
|
||||
this.wallet = v;
|
||||
console.log(this.wallet);
|
||||
this.selected1 = true;
|
||||
this.selected2 = false;
|
||||
this.selected3 = false;
|
||||
this.selected4 = true;
|
||||
},
|
||||
showAddService() {
|
||||
this.showOrderInfo = this.orderInfo;
|
||||
this.isShowPayModal = true;
|
||||
},
|
||||
hidePayModal() {
|
||||
this.isShowPayModal = false;
|
||||
this.showOrderInfo = null;
|
||||
this.otherWeight = {};
|
||||
},
|
||||
showCall() {
|
||||
this.showOrderInfo = this.orderInfo;
|
||||
this.isShowCallModal = true;
|
||||
},
|
||||
hideCallModal() {
|
||||
this.isShowCallModal = false;
|
||||
this.showOrderInfo = null;
|
||||
},
|
||||
okPayAction() {
|
||||
this.isShowPaySuccessModal = false;
|
||||
this.$emit('refresh-list');
|
||||
},
|
||||
// paymentConfirm(data) {
|
||||
// this.additionalBom = true
|
||||
// this.elasticLayer = true
|
||||
// },
|
||||
paymentConfirm(data) {
|
||||
console.log(data);
|
||||
this.dataList = data;
|
||||
addServicePay(this.dataList.order_id, this.otherWeight.weight_id)
|
||||
.then((res) => {
|
||||
const { code, msg } = res;
|
||||
if (`${code}` === "-121") {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: msg,
|
||||
icon: "none",
|
||||
duration: 4000,
|
||||
});
|
||||
this.additionalBom = false;
|
||||
this.elasticLayer = false;
|
||||
this.hidePayModal();
|
||||
this.$emit('refresh-list');
|
||||
} else {
|
||||
// console.log(111,data,'--')
|
||||
if (!data.needRefund) {
|
||||
console.log(111)
|
||||
this.additionalBom = true;
|
||||
this.elasticLayer = true;
|
||||
this.hidePayModal();
|
||||
} else if (data.needRefund) {
|
||||
// console.log(this,'--')
|
||||
// console.log(222)
|
||||
this.additionalBom = false;
|
||||
this.elasticLayer = false;
|
||||
this.isShowPayModal = false
|
||||
uni.showToast({
|
||||
title: "退差价成功",
|
||||
icon: "none",
|
||||
});
|
||||
this.$emit('refresh-list');
|
||||
}
|
||||
// this.wxPayAction(data.needRefund, PRICE_DIFF_TYPE_SERVICE)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
title: err || "创建订单失败",
|
||||
icon: "none",
|
||||
});
|
||||
uni.hideLoading();
|
||||
});
|
||||
},
|
||||
wxPayAction() {
|
||||
if (this.dataList.needRefund) {
|
||||
uni.hideLoading();
|
||||
this.hidePayModal();
|
||||
this.isShowPaySuccessModal = true;
|
||||
return;
|
||||
}
|
||||
if (this.WeChat == 1) {
|
||||
payOrder(this.dataList.order_id, PRICE_DIFF_TYPE_SERVICE)
|
||||
.then((res) => {
|
||||
const payData = res?.info?.pay_data || {};
|
||||
uni.requestPayment({
|
||||
provider: "wxpay",
|
||||
timeStamp: payData.timeStamp,
|
||||
nonceStr: payData.nonceStr,
|
||||
package: payData.package,
|
||||
signType: payData.signType,
|
||||
paySign: payData.sign,
|
||||
success: (res) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "支付成功",
|
||||
icon: "none",
|
||||
});
|
||||
this.additionalBom = false;
|
||||
this.elasticLayer = false;
|
||||
this.hidePayModal();
|
||||
this.$emit('refresh-list');
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showToast({
|
||||
title: err?.msg || "支付失败",
|
||||
icon: "none",
|
||||
});
|
||||
uni.hideLoading();
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
title: err || "支付失败",
|
||||
icon: "none",
|
||||
});
|
||||
});
|
||||
} else if (this.wallet == 2) {
|
||||
const value = (this.$store.state && this.$store.state.user && this.$store.state.user.userInfo) || {};
|
||||
// console.log(this.selectService,'---')
|
||||
const data = {
|
||||
business_type: 2,
|
||||
wallet_id: value.wallet_id,
|
||||
user_id: value.userID,
|
||||
amount: Number(this.dataList.diffPrice),
|
||||
business_id: this.dataList.order_id,
|
||||
difference_amount: Number(this.dataList.diffPrice),
|
||||
};
|
||||
walletTransaction(data).then((res) => {
|
||||
if (res.code == 200) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "支付成功",
|
||||
icon: "none",
|
||||
});
|
||||
this.additionalBom = false;
|
||||
this.elasticLayer = false;
|
||||
this.hidePayModal();
|
||||
this.$emit('refresh-list');
|
||||
} else if (res.code == 100) {
|
||||
uni.showToast({
|
||||
title: res.message,
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
clickCancel() {
|
||||
// 触发事件,让父组件处理弹窗显示
|
||||
this.$emit('cancel-order', this.orderInfo);
|
||||
},
|
||||
handlePayNow(orderInfo) {
|
||||
// 跳转到订单详情页面进行支付
|
||||
uni.navigateTo({
|
||||
url: `/page-reser/order/order-detail-page?source=${this.orderInfo.source}&order_id=${orderInfo.order_id}`,
|
||||
events: {
|
||||
refreshList: () => this.$emit('refresh-list'),
|
||||
},
|
||||
});
|
||||
},
|
||||
gotoDetail() {
|
||||
uni.navigateTo({
|
||||
url: `/pageHome/order/order-detail-page?source=${this.orderInfo.source}&order_id=${this.orderInfo.pet_order.order_id}`,
|
||||
events: {
|
||||
refreshList: () => this.$emit('refresh-list'),
|
||||
},
|
||||
});
|
||||
},
|
||||
aspectFillto() {
|
||||
uni.redirectTo({
|
||||
url: "/pages/client/index/index?activePageId=minePage",
|
||||
});
|
||||
},
|
||||
gotoEvaluate() {
|
||||
// if (this.orderInfo.pinglun_id) {
|
||||
// uni.navigateTo({
|
||||
// url: `/pages/client/remark/details?remarkId=${this.orderInfo.pinglun_id}`,
|
||||
// });
|
||||
// } else {
|
||||
|
||||
// }
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/order/remark?orderId=${this.orderInfo.pet_order.order_id}`,
|
||||
});
|
||||
},
|
||||
gotoComparisonChart() {
|
||||
const orderId = this.orderInfo?.order_id;
|
||||
if (!orderId) {
|
||||
uni.showToast({
|
||||
title: "订单ID不存在",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/order/wash-compare?orderid=${orderId}`,
|
||||
});
|
||||
},
|
||||
showGoods() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/category/index?petOrderId=${this.orderInfo.order_id}&addressId=${this.orderInfo.address_id}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-container {
|
||||
// display: flex;
|
||||
// flex: 1;
|
||||
// flex-direction: column;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// box-sizing: border-box;
|
||||
background-color: #f7f8fa;
|
||||
// padding-bottom: 32rpx;
|
||||
|
||||
.nav-container {
|
||||
background: #fff;
|
||||
height: 180rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
.reservation-order {
|
||||
margin-top: 82rpx;
|
||||
}
|
||||
|
||||
.ctFill {
|
||||
width: 148rpx;
|
||||
height: 57rpx;
|
||||
position: absolute;
|
||||
top: 102rpx;
|
||||
left: 15rpx;
|
||||
|
||||
// border: 1px solid #000;
|
||||
.home-cw {
|
||||
position: absolute;
|
||||
top: 17rpx;
|
||||
left: 20rpx;
|
||||
width: 8.5px;
|
||||
height: 14.21px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 14px;
|
||||
// font-weight: bold;
|
||||
color: #000;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.body-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
.status-view-container {
|
||||
width: 100%;
|
||||
height: 60%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.scroll-view {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.elastic-layer {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.additional-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
|
||||
.close-icon {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
position: absolute;
|
||||
right: 23rpx;
|
||||
top: 2rpx;
|
||||
}
|
||||
|
||||
.recharge-method {
|
||||
height: 541rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
margin-bottom: 30rpx;
|
||||
border-radius: 30rpx;
|
||||
width: 100%;
|
||||
|
||||
.wechat {
|
||||
width: 367px;
|
||||
height: 58px;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
// border: 1px solid #ececec;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.w {
|
||||
width: 68rpx;
|
||||
height: 68rpx;
|
||||
}
|
||||
|
||||
.x {
|
||||
font-family: PingFangSC;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
letter-spacing: normal;
|
||||
color: #3d3d3d;
|
||||
display: flex;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.not-selected {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.payment {
|
||||
position: absolute;
|
||||
border-radius: 100px;
|
||||
bottom: 50rpx;
|
||||
left: 30rpx;
|
||||
width: 343px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #FF19A0;
|
||||
z-index: 10000;
|
||||
|
||||
.Z {
|
||||
font-family: PingFang SC;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
560
src/pages/client/recharge/components/points-content.vue
Normal file
@ -0,0 +1,560 @@
|
||||
<template>
|
||||
<view style="background: #fff">
|
||||
<view class="scroll">
|
||||
<view class="card-roll">
|
||||
<image class="file-pet" :src="imgPrefix + 'integral-ground.png'" mode="widthFix" />
|
||||
<view class="points-content">
|
||||
<view class="points-top-row">
|
||||
<text class="points-label">积分数量 (个)</text>
|
||||
<view class="points-right" @click="goToPointsDetails">
|
||||
<text class="points-details-text">积分明细</text>
|
||||
<image class="points-arrow" :src="imgPrefix + 'recharge-whiteArrow.png'" mode="widthFix" />
|
||||
</view>
|
||||
</view>
|
||||
<text class="points-value">{{ totalPoints || 0 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="select-amount">
|
||||
<text class="select">请选择充值金额</text>
|
||||
<view class="select-recharge">
|
||||
<view @click.stop="giftOrderStatus(item)" v-for="item in giftList" :key="item.id">
|
||||
<view class="recharge" :class="{ recharge2: item.id === giftStatus }">
|
||||
<view class="first">
|
||||
<image class="integral-icon" :src="imgPrefix + 'integral-icon.png'" mode="widthFix" />
|
||||
<text class="f">{{ item.points }}</text>
|
||||
</view>
|
||||
<text class="s">¥{{ item.amount }} </text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="select">请选择充值方式</text>
|
||||
<view class="recharge-method">
|
||||
<view class="wechat" @click.stop="selectOption1">
|
||||
<view class="select">
|
||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
||||
<text class="x">微信</text>
|
||||
</view>
|
||||
<image v-if="selected1" class="not-selected"
|
||||
src="@/static/images/w.png" mode="widthFix" />
|
||||
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="remk-reservation">
|
||||
<view class="rese-bom" @click="rechargeNow()">
|
||||
<text class="l" style="color: #fff;">立即充值</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pointsRechargeList, walletWxpay } from '@/api/login';
|
||||
import { getUserInfo } from '@/api/user';
|
||||
import { imgPrefix } from '@/utils/common';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
user_id: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imgPrefix,
|
||||
giftList: [],
|
||||
giftStatus: null,
|
||||
giftLabel: null,
|
||||
selected1: true,
|
||||
selected2: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getGiftList();
|
||||
},
|
||||
computed: {
|
||||
totalPoints() {
|
||||
return this.$store.state?.user?.userInfo.totalPoints || 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getGiftList() {
|
||||
pointsRechargeList().then((res) => {
|
||||
this.giftList = res.data;
|
||||
});
|
||||
},
|
||||
giftOrderStatus(item) {
|
||||
// 如果点击的是已选中的项,不做处理
|
||||
if (this.giftStatus === item.id) {
|
||||
return;
|
||||
}
|
||||
// 更新选中状态
|
||||
this.giftStatus = item.id;
|
||||
this.giftLabel = item.amount;
|
||||
},
|
||||
goToPointsDetails() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/client/recharge/points-details'
|
||||
});
|
||||
},
|
||||
// 刷新用户信息
|
||||
refreshUserInfo() {
|
||||
getUserInfo().then((res) => {
|
||||
if (res && res.data) {
|
||||
this.$store.dispatch("user/setUserInfo", res.data);
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error('刷新用户信息失败:', err);
|
||||
});
|
||||
},
|
||||
selectOption1() {
|
||||
this.selected1 = false;
|
||||
this.selected2 = true;
|
||||
},
|
||||
rechargeNow() {
|
||||
// 验证是否选择了充值金额
|
||||
if (!this.giftStatus || !this.giftLabel) {
|
||||
uni.showToast({
|
||||
title: '请选择充值金额',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 验证是否选择了支付方式
|
||||
if (this.selected1) {
|
||||
uni.showToast({
|
||||
title: '请选择支付方式',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.showLoading({
|
||||
title: '处理中...',
|
||||
mask: true
|
||||
});
|
||||
const data = {
|
||||
type: 6, // 购买积分传6
|
||||
total_fee: +this.giftLabel
|
||||
};
|
||||
walletWxpay(data).then((res) => {
|
||||
uni.hideLoading();
|
||||
// 使用获取的支付参数进行支付
|
||||
uni.requestPayment({
|
||||
provider: 'wxpay',
|
||||
timeStamp: res.data.timeStamp,
|
||||
nonceStr: res.data.nonceStr,
|
||||
package: res.data.package,
|
||||
signType: res.data.signType,
|
||||
paySign: res.data.paySign,
|
||||
success: (payRes) => {
|
||||
uni.showToast({
|
||||
title: '支付成功',
|
||||
icon: 'success'
|
||||
});
|
||||
// 刷新用户信息,更新 totalPoints
|
||||
this.refreshUserInfo();
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showToast({
|
||||
title: '支付失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
console.error('获取支付参数失败:', err);
|
||||
uni.showToast({
|
||||
title: err.msg || '获取支付参数失败',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll {
|
||||
display: flex;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.card-roll {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
|
||||
.file-pet {
|
||||
width: 710rpx;
|
||||
height: 300rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
font-family: PingFangSC;
|
||||
font-size: 40rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.points-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 32rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.points-top-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.points-label {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.points-value {
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.points-right {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.points-details-text {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.points-arrow {
|
||||
width: 11rpx;
|
||||
height: 18rpx;
|
||||
}
|
||||
|
||||
.effectiveTime {
|
||||
display: flex;
|
||||
margin-top: 16rpx;
|
||||
|
||||
.surplus {
|
||||
font-family: PingFang SC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.first {
|
||||
font-family: PingFang SC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.recharge {
|
||||
position: absolute;
|
||||
left: 35rpx;
|
||||
top: 185rpx;
|
||||
width: 577rpx;
|
||||
height: 41rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16rpx 24rpx;
|
||||
gap: 20rpx;
|
||||
// border: 1px solid #000;
|
||||
border-radius: 179px;
|
||||
|
||||
|
||||
// background: linear-gradient(263deg, #ffea7b 0%, #ece5bf 137%);
|
||||
.immediately {
|
||||
font-family: PingFangSC;
|
||||
font-size: 28rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #3a3d55;
|
||||
}
|
||||
}
|
||||
|
||||
.discount {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 50rpx;
|
||||
height: 20rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 24rpx;
|
||||
gap: 4rpx;
|
||||
border-radius: 0 24rpx 0 24rpx;
|
||||
background: linear-gradient(270deg, #ff3c4a 0%, #f4c24e 107%);
|
||||
|
||||
.break {
|
||||
font-family: PingFangSC;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.use-record {
|
||||
position: absolute;
|
||||
right: 34rpx;
|
||||
top: 27rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// border: 1px solid #000;
|
||||
width: 142rpx;
|
||||
height: 46rpx;
|
||||
|
||||
.record {
|
||||
font-family: PingFang SC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.s {
|
||||
width: 11rpx;
|
||||
height: 18rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-amount {
|
||||
padding: 20rpx;
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
background: #ffffff;
|
||||
|
||||
.select {
|
||||
font-family: PingFangSC;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #272427;
|
||||
}
|
||||
|
||||
.select-recharge {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10rpx;
|
||||
justify-content: space-between;
|
||||
|
||||
.recharge {
|
||||
width: 125rpx;
|
||||
height: 76rpx;
|
||||
margin: 10rpx 0;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 32rpx 44rpx;
|
||||
gap: 16rpx;
|
||||
z-index: 0;
|
||||
border-radius: 16rpx;
|
||||
background: #F8F8F8;
|
||||
border: 1px solid #F8F8F8;
|
||||
|
||||
|
||||
.s {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9B939A;
|
||||
}
|
||||
|
||||
.first {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
|
||||
.integral-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
.f {
|
||||
font-family: PingFangSC;
|
||||
font-size: 40rpx;
|
||||
font-weight: 500;
|
||||
letter-spacing: normal;
|
||||
color: #272427;
|
||||
}
|
||||
|
||||
.t {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #272427;
|
||||
margin-left: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recharge2 {
|
||||
width: 125rpx;
|
||||
height: 76rpx;
|
||||
margin: 10rpx 0;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 32rpx 44rpx;
|
||||
gap: 16rpx;
|
||||
z-index: 0;
|
||||
border-radius: 16rpx;
|
||||
background: #fee9f3;
|
||||
border: 1px solid #FF0097;
|
||||
|
||||
.s {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #FF0097;
|
||||
}
|
||||
|
||||
.first {
|
||||
.f {
|
||||
font-family: PingFangSC;
|
||||
font-size: 40rpx;
|
||||
font-weight: 500;
|
||||
letter-spacing: normal;
|
||||
color: #FF0097;
|
||||
}
|
||||
|
||||
.t {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #FF0097;
|
||||
margin-left: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recharge-method {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.wechat {
|
||||
height: 58px;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ececec;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.w {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.x {
|
||||
font-family: PingFangSC;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
letter-spacing: normal;
|
||||
color: #3d3d3d;
|
||||
display: flex;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.not-selected {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.remk-reservation {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 150rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
padding-top: 12rpx;
|
||||
// align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0px 0px 22px 0px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 16px 16px 0px 0px;
|
||||
|
||||
.rese-bom {
|
||||
width: calc(100vw - 48rpx);
|
||||
height: 48px;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
// gap: 10px;
|
||||
z-index: 0;
|
||||
border-radius: 100px;
|
||||
box-sizing: border-box;
|
||||
/* 主色1 */
|
||||
border: 1px solid #FE019B;
|
||||
background-color: #FE019B;
|
||||
color: #fff;
|
||||
|
||||
.l {
|
||||
font-family: PingFang SC;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 主色1 */
|
||||
color: #FE019B;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
334
src/pages/client/recharge/favorable-comment.vue
Normal file
@ -0,0 +1,334 @@
|
||||
<template>
|
||||
<view class="home-share">
|
||||
<image
|
||||
class="home-comm"
|
||||
src="https://activity.wagoo.live/home-comment.png"
|
||||
/>
|
||||
<view class="tab-view">
|
||||
<view
|
||||
class="tab-item"
|
||||
v-for="item in tabList"
|
||||
:key="item.value"
|
||||
@click.stop="selectOrderStatus(item)"
|
||||
>
|
||||
<text
|
||||
class="app-fc-normal"
|
||||
:class="{ 'tab-text ali-puhui-bold': item.value === currentStatus }"
|
||||
>
|
||||
{{ item.label }}
|
||||
</text>
|
||||
<view
|
||||
class="tab-line"
|
||||
:class="{ 'tab-selected-line': item.value === currentStatus }"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="remark-layer">
|
||||
<view class="remark-item" v-for="(item,index) in remarkList"
|
||||
:key="index" >
|
||||
<view class="flex-row-start remark-item-info">
|
||||
|
||||
<image v-if="item.head_pic" class="info-avator" :src="item.head_pic" />
|
||||
<image v-else class="info-avator" src="https://activity.wagoo.live/home-head.png" />
|
||||
<view class="info-center">
|
||||
<view class="app-fc-main">{{ item.nick_name || "" }}</view>
|
||||
<view class="remark-comment">
|
||||
<view class="remak-solo">
|
||||
<text class="z">超赞</text>
|
||||
</view>
|
||||
<view class="flex-row-start star-list">
|
||||
<image
|
||||
v-for="item1 in [1, 2, 3, 4, 5]"
|
||||
:key="item1"
|
||||
class="star-item"
|
||||
:src="
|
||||
item1 <= item.star
|
||||
? require('@/static/images/star.png')
|
||||
: require('@/static/images/star_dark.png')
|
||||
"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="fs-24 app-fc-normal">
|
||||
{{ formatTime(item.add_time) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="remark-bottom">
|
||||
<text class="fs-26 app-fc-normal">
|
||||
{{ item.content || "" }}
|
||||
</text>
|
||||
<view class="flex-row-start remark-imgs">
|
||||
<image
|
||||
v-for="(item2, i) in item.pinglun_pic"
|
||||
:key="item2"
|
||||
class="remark-img"
|
||||
:class="{ 'remark-img-right': i % 3 === 2 }"
|
||||
:src="item2"
|
||||
@click="preview(item2,item.pinglun_pic)"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="remk-reservation">
|
||||
<view class="rese-bom" @click="
|
||||
jumpTo('/pages/client/index/index?activePageId=reservationPage')
|
||||
">
|
||||
<text class="l">立即预约</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from "moment";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
remarkList: [],
|
||||
remarkId: "4",
|
||||
currentStatus: "1",
|
||||
|
||||
tabList: [
|
||||
{
|
||||
value: "1",
|
||||
label: "好评分享",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
|
||||
const eventChannel = this.getOpenerEventChannel();
|
||||
eventChannel.on('favorableInfo', (data) => {
|
||||
this.getRemarkDetails(data);
|
||||
// console.log(data,'111')
|
||||
})
|
||||
|
||||
},
|
||||
methods: {
|
||||
jumpTo(url) {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
});
|
||||
},
|
||||
getRemarkDetails() {
|
||||
this.remarkList = [];
|
||||
},
|
||||
formatTime(time) {
|
||||
return moment(time * 1000).format("YYYY/MM/DD");
|
||||
},
|
||||
preview(item,v) {
|
||||
uni.previewImage({
|
||||
urls:v,
|
||||
current: item,
|
||||
});
|
||||
},
|
||||
|
||||
selectOrderStatus(status) {
|
||||
if (this.currentStatus === status.value) {
|
||||
return;
|
||||
}
|
||||
this.currentStatus = status.value;
|
||||
this.isLoading = true;
|
||||
this.pageNumber = 1;
|
||||
this.isRefresh = false;
|
||||
this.isLoadMore = false;
|
||||
this.isNoMore = false;
|
||||
this.orderList = [];
|
||||
// this.getData();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home-share {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.remk-reservation{
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height:150rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
padding-top: 24rpx;
|
||||
// align-items: center;
|
||||
justify-content: center;
|
||||
.rese-bom{
|
||||
width: 351px;
|
||||
height: 48px;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
// gap: 10px;
|
||||
z-index: 0;
|
||||
border-radius: 100px;
|
||||
box-sizing: border-box;
|
||||
/* 主色1 */
|
||||
border: 1px solid #FE019B;
|
||||
.l{
|
||||
font-family: PingFang SC;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 主色1 */
|
||||
color: #FE019B;
|
||||
}
|
||||
}
|
||||
}
|
||||
.remark-layer{
|
||||
padding-bottom: 160rpx;
|
||||
.remark-item {
|
||||
padding: 40rpx 36rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
// height: 100%;
|
||||
|
||||
.remark-item-info {
|
||||
margin-bottom: 20rpx;
|
||||
align-items: stretch;
|
||||
|
||||
.info-avator {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 100rpx;
|
||||
}
|
||||
.info-center {
|
||||
flex: 1;
|
||||
margin: 0 20rpx;
|
||||
overflow: hidden;
|
||||
.app-fc-main {
|
||||
font-family: PingFangSC;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #333333;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.remark-comment {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
.remak-solo {
|
||||
margin: 28rpx 10rpx 0 0 ;
|
||||
width: 48rpx;
|
||||
height: 28rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
padding: 4rpx;
|
||||
border-radius: 6rpx;
|
||||
background: #fee9f3;
|
||||
.z {
|
||||
font-family: PingFangSC;
|
||||
font-size: 20rpx;
|
||||
font-weight: normal;
|
||||
line-height: 20rpx;
|
||||
letter-spacing: normal;
|
||||
/* 主色1 */
|
||||
color: #fe019b;
|
||||
}
|
||||
}
|
||||
.star-item {
|
||||
width: 30rpx;
|
||||
height: 28rpx;
|
||||
margin-right: 6rpx;
|
||||
margin-top: 22rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.remark-bottom{
|
||||
// border: 1px solid #000;
|
||||
width: 560rpx;
|
||||
margin-left: 122rpx;
|
||||
.remark-imgs {
|
||||
width:560rpx;
|
||||
margin-top: 20rpx;
|
||||
flex-wrap: wrap;
|
||||
.remark-img {
|
||||
width: 85px;
|
||||
height: 85px;
|
||||
// width: calc((100vw - 32rpx * 2 - 24rpx * 2 - 14rpx * 2) / 3);
|
||||
// height: calc((100vw - 32rpx * 2 - 24rpx * 2 - 14rpx * 2) / 3);
|
||||
border-radius: 20rpx;
|
||||
margin-right: 14rpx;
|
||||
margin-bottom: 14rpx;
|
||||
|
||||
&.remark-img-right {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.home-comm {
|
||||
width: 95%;
|
||||
height: 267rpx;
|
||||
margin: 20rpx auto;
|
||||
display: block;
|
||||
}
|
||||
.tab-view {
|
||||
width: 95%;
|
||||
height:87rpx;
|
||||
display: flex;
|
||||
// align-items: center;
|
||||
// background-color: #ffffff;
|
||||
|
||||
.tab-item {
|
||||
display: flex;
|
||||
margin-left: 34rpx;
|
||||
// flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.app-fc-normal {
|
||||
color: #666666;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
// font-weight: bold;
|
||||
|
||||
// color: $app_fc_main;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-line {
|
||||
transform: translateX(-50%);
|
||||
width: 24rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 12rpx;
|
||||
// width: 30rpx;
|
||||
// height: 6rpx;
|
||||
background-color: transparent;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.tab-selected-line {
|
||||
background-color: $app_color_main;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
780
src/pages/client/recharge/index.vue
Normal file
@ -0,0 +1,780 @@
|
||||
<template>
|
||||
<view class="container" @touchmove.stop.prevent>
|
||||
<view class="tab-view">
|
||||
<view
|
||||
class="tab-item"
|
||||
v-for="item in mainTabList"
|
||||
:key="item.value"
|
||||
@click.stop="selectMainTab(item)"
|
||||
>
|
||||
<text
|
||||
class="fs-28 app-fc-normal"
|
||||
:class="{ 'tab-text ali-puhui-bold': item.value == currentMainTab }"
|
||||
>
|
||||
{{ item.label }}
|
||||
</text>
|
||||
<view
|
||||
class="tab-line"
|
||||
:class="{ 'tab-selected-line': item.value == currentMainTab }"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view style="background: #fff" v-show="recharge && currentMainTab === 'balance'">
|
||||
<view class="scroll">
|
||||
<view class="card-roll">
|
||||
<image class="file-pet" src="https://activity.wagoo.live/myRechargetwo.png" mode="widthFix" />
|
||||
<view class="amount">
|
||||
<text class="surplus">{{ walletList.balance }}</text>
|
||||
</view>
|
||||
<!-- <view class="effectiveTime">
|
||||
<text class="surplus">有效期:</text>
|
||||
<text class="surplus">2025/07/01至2026/07/01</text>
|
||||
</view> -->
|
||||
<view class="recharge" @click="jumpTo('/pages/client/recharge/index')">
|
||||
<!-- <text class="immediately">立即充值</text> -->
|
||||
</view>
|
||||
<view class="use-record" @click="
|
||||
jumpTo(
|
||||
`/pages/client/recharge/use-list?wallet_id=${walletList.id}&user_id=${user_id}`
|
||||
)
|
||||
">
|
||||
<!-- <text class="record">使用记录</text> -->
|
||||
<!-- <image class="s" src="@/static/images/jt.png" /> -->
|
||||
</view>
|
||||
<!-- <view class="discount">
|
||||
<text class="break">9.8折</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<view class="select-amount">
|
||||
<text class="select">请选择充值金额</text>
|
||||
<view class="select-recharge">
|
||||
<view @click.stop="giftOrderStatus(item)" v-for="item in giftList" :key="item.id">
|
||||
<view class="recharge" :class="{ recharge2: item.id === giftStatus }">
|
||||
<view class="first">
|
||||
<text class="f">{{ item.amount }}</text>
|
||||
<text class="t">元 </text>
|
||||
</view>
|
||||
<text class="s">送{{ item.bonus_ratio }}元 </text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="select">请选择充值方式</text>
|
||||
<view class="recharge-method">
|
||||
<view class="wechat" @click.stop="selectOption1">
|
||||
<view class="select">
|
||||
<image class="w" src="@/static/images/wx.png" mode="widthFix" />
|
||||
<text class="x">微信</text>
|
||||
</view>
|
||||
<image v-if="selected1" class="not-selected"
|
||||
src="@/static/images/w.png" mode="widthFix" />
|
||||
<image v-if="selected2" class="not-selected" src="@/static/images/y.png" mode="widthFix" />
|
||||
</view>
|
||||
<!-- <view class="wechat">
|
||||
<view class="select">
|
||||
<image class="w" src="@/static/images/zf.png" mode="widthFix" />
|
||||
<text class="x">支付宝</text>
|
||||
</view>
|
||||
<image
|
||||
v-if="selected3"
|
||||
class="not-selected"
|
||||
@click.stop="selectOption2"
|
||||
src="@/static/images/w.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<image
|
||||
v-if="selected4"
|
||||
class="not-selected"
|
||||
src="@/static/images/y.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
</view> -->
|
||||
</view>
|
||||
<view class="remk-reservation">
|
||||
<view class="rese-bom" @click="rechargeNow()">
|
||||
<text class="l" style="color: #fff;">立即充值</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view @click="rechargeNow()" class="recharge-now">
|
||||
<text class="r">立即充值</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- 积分组件 -->
|
||||
<points-content v-show="currentMainTab === 'points'" :user_id="user_id" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
rechargeStatus
|
||||
} from "@/pageHome/constants/home";
|
||||
import {
|
||||
walletWxpay,
|
||||
userWllet,
|
||||
walletRechagre
|
||||
} from "../../../api/login";
|
||||
import {
|
||||
serviceCouponCreateOrder,
|
||||
serviceCouponOrderPay,
|
||||
} from "../../../api/coupon";
|
||||
import PointsContent from "./components/points-content.vue";
|
||||
export default {
|
||||
components: {
|
||||
PointsContent,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabList: rechargeStatus,
|
||||
mainTabList: [
|
||||
{
|
||||
value: "balance",
|
||||
label: "余额",
|
||||
},
|
||||
{
|
||||
value: "points",
|
||||
label: "积分",
|
||||
},
|
||||
],
|
||||
currentMainTab: "balance",
|
||||
giftLabel: "",
|
||||
giftList: [],
|
||||
walletList: {},
|
||||
paymentList: {},
|
||||
currentStatus: "1",
|
||||
giftStatus: "",
|
||||
selectedOption: "",
|
||||
selected1: true,
|
||||
selected2: false,
|
||||
selected3: true,
|
||||
selected4: false,
|
||||
recharge: true,
|
||||
record: false,
|
||||
balance: "",
|
||||
user_id: ""
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
this.user_id = options.user_id;
|
||||
// 如果传递了 tab 参数,自动选中对应的标签
|
||||
if (options.tab === 'points') {
|
||||
this.currentMainTab = 'points';
|
||||
}
|
||||
this.buyService(options.user_id);
|
||||
this.walletRechagre()
|
||||
},
|
||||
methods: {
|
||||
async rechargeNow() {
|
||||
if (!this.giftLabel) {
|
||||
uni.showToast({
|
||||
title: "请选择充值金额",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.selected1) {
|
||||
uni.showToast({
|
||||
title: "请选择充值方式",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = {
|
||||
wallet_id: this.walletList.id,
|
||||
user_id: +this.user_id,
|
||||
total_fee: this.giftLabel,
|
||||
type: 1
|
||||
};
|
||||
walletWxpay(data).then((res) => {
|
||||
// 使用获取的支付参数进行支付
|
||||
uni.requestPayment({
|
||||
timeStamp: res.data.timeStamp, // 确保这些字段都正确
|
||||
nonceStr: res.data.nonceStr,
|
||||
package: res.data.package,
|
||||
signType: res.data.signType,
|
||||
paySign: res.data.paySign,
|
||||
success: (res) => {
|
||||
this.buyService(this.user_id);
|
||||
// console.log('支付成功:', res);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error("支付失败:", err);
|
||||
},
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("捕获到的错误:", error);
|
||||
}
|
||||
},
|
||||
buyService(user_id) {
|
||||
userWllet(user_id).then((res) => {
|
||||
this.walletList = res.data;
|
||||
});
|
||||
},
|
||||
|
||||
walletRechagre() {
|
||||
walletRechagre().then((res) => {
|
||||
this.giftList = res.data
|
||||
});
|
||||
},
|
||||
selectOption1() {
|
||||
this.selected1 = false;
|
||||
this.selected2 = true;
|
||||
this.selected3 = true;
|
||||
this.selected4 = false;
|
||||
},
|
||||
selectOption2() {
|
||||
this.selected1 = true;
|
||||
this.selected2 = false;
|
||||
this.selected3 = false;
|
||||
this.selected4 = true;
|
||||
},
|
||||
giftOrderStatus(status) {
|
||||
// console.log(status, "-=-");
|
||||
if (this.giftStatus === status.id) {
|
||||
return;
|
||||
}
|
||||
this.giftStatus = status.id;
|
||||
this.giftLabel = status.amount;
|
||||
},
|
||||
jumpTo(url) {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
});
|
||||
},
|
||||
selectMainTab(item) {
|
||||
if (this.currentMainTab === item.value) {
|
||||
return;
|
||||
}
|
||||
this.currentMainTab = item.value;
|
||||
},
|
||||
selectOrderStatus(status) {
|
||||
if (status.value == 1) {
|
||||
(this.recharge = true), (this.record = false);
|
||||
} else {
|
||||
(this.recharge = false), (this.record = true);
|
||||
}
|
||||
if (this.currentStatus === status.value) {
|
||||
return;
|
||||
}
|
||||
this.currentStatus = status.value;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
width: 746rpx;
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
|
||||
.tab-view {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
|
||||
.tab-item {
|
||||
display: flex;
|
||||
magin-bottom: 20rpx;
|
||||
padding: 20rpx 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.tab-text {
|
||||
color: $app_fc_main;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-line {
|
||||
width: 24rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: transparent;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.tab-selected-line {
|
||||
background-color: $app_color_main;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll {
|
||||
display: flex;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.card-roll {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
|
||||
.file-pet {
|
||||
width: 710rpx;
|
||||
height: 300rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
font-family: PingFangSC;
|
||||
font-size: 40rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.amount {
|
||||
position: absolute;
|
||||
left: 40rpx;
|
||||
top: 82rpx;
|
||||
|
||||
.surplus {
|
||||
font-family: PingFangSC;
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
line-height: 32px;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.effectiveTime {
|
||||
display: flex;
|
||||
margin-top: 16rpx;
|
||||
|
||||
.surplus {
|
||||
font-family: PingFang SC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.first {
|
||||
font-family: PingFang SC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.recharge {
|
||||
position: absolute;
|
||||
left: 35rpx;
|
||||
top: 185rpx;
|
||||
width: 577rpx;
|
||||
height: 41rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16rpx 24rpx;
|
||||
gap: 20rpx;
|
||||
// border: 1px solid #000;
|
||||
border-radius: 179px;
|
||||
|
||||
// background: linear-gradient(263deg, #ffea7b 0%, #ece5bf 137%);
|
||||
.immediately {
|
||||
font-family: PingFangSC;
|
||||
font-size: 28rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #3a3d55;
|
||||
}
|
||||
}
|
||||
|
||||
.discount {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 50rpx;
|
||||
height: 20rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 24rpx;
|
||||
gap: 4rpx;
|
||||
border-radius: 0 24rpx 0 24rpx;
|
||||
background: linear-gradient(270deg, #ff3c4a 0%, #f4c24e 107%);
|
||||
|
||||
.break {
|
||||
font-family: PingFangSC;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.use-record {
|
||||
position: absolute;
|
||||
right: 34rpx;
|
||||
top: 27rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// border: 1px solid #000;
|
||||
width: 142rpx;
|
||||
height: 46rpx;
|
||||
|
||||
.record {
|
||||
font-family: PingFang SC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.s {
|
||||
width: 11rpx;
|
||||
height: 18rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-amount {
|
||||
padding: 20rpx;
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
background: #ffffff;
|
||||
|
||||
.select {
|
||||
font-family: PingFangSC;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #272427;
|
||||
}
|
||||
|
||||
.select-recharge {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10rpx;
|
||||
justify-content: space-between;
|
||||
|
||||
.recharge {
|
||||
width: 125rpx;
|
||||
height: 76rpx;
|
||||
margin: 10rpx 0rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 32rpx 44rpx;
|
||||
gap: 16rpx;
|
||||
z-index: 0;
|
||||
border-radius: 16rpx;
|
||||
background: #F8F8F8;
|
||||
border: 1px solid #F8F8F8;
|
||||
|
||||
.s {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #272427;
|
||||
}
|
||||
|
||||
.first {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.f {
|
||||
font-family: PingFangSC;
|
||||
font-size: 40rpx;
|
||||
font-weight: 500;
|
||||
line-height: 36rpx;
|
||||
letter-spacing: normal;
|
||||
color: #272427;
|
||||
}
|
||||
|
||||
.t {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #272427;
|
||||
margin-left: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recharge2 {
|
||||
width: 125rpx;
|
||||
height: 76rpx;
|
||||
margin: 10rpx 0rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 32rpx 44rpx;
|
||||
gap: 16rpx;
|
||||
z-index: 0;
|
||||
border-radius: 16rpx;
|
||||
background: #fee9f3;
|
||||
border: 1px solid #FF0097;
|
||||
|
||||
.s {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #FF0097;
|
||||
}
|
||||
|
||||
.first {
|
||||
.f {
|
||||
font-family: PingFangSC;
|
||||
font-size: 40rpx;
|
||||
font-weight: 500;
|
||||
line-height: 36rpx;
|
||||
letter-spacing: normal;
|
||||
color: #FF0097;
|
||||
}
|
||||
|
||||
.t {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #FF0097;
|
||||
margin-left: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recharge-method {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.wechat {
|
||||
height: 58px;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ececec;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.w {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.x {
|
||||
font-family: PingFangSC;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
letter-spacing: normal;
|
||||
color: #3d3d3d;
|
||||
display: flex;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.not-selected {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.remk-reservation {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 150rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
padding-top: 12rpx;
|
||||
// align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0px 0px 22px 0px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 16px 16px 0px 0px;
|
||||
|
||||
.rese-bom {
|
||||
width: calc(100vw - 48rpx);
|
||||
height: 48px;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
// gap: 10px;
|
||||
z-index: 0;
|
||||
border-radius: 100px;
|
||||
box-sizing: border-box;
|
||||
/* 主色1 */
|
||||
border: 1px solid #FE019B;
|
||||
background-color: #FE019B;
|
||||
color: #fff;
|
||||
|
||||
.l {
|
||||
font-family: PingFang SC;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 主色1 */
|
||||
color: #FE019B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .recharge-now {
|
||||
// position: fixed;
|
||||
// bottom: 100rpx;
|
||||
// left: 20rpx;
|
||||
// // margin: 133rpx 0 0 7rpx;
|
||||
// width: 89%;
|
||||
// height: 48rpx;
|
||||
// /* 自动布局 */
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// padding: 24rpx;
|
||||
// gap: 20rpx;
|
||||
// align-self: stretch;
|
||||
// z-index: 0;
|
||||
// border-radius: 200rpx;
|
||||
// border: 1px solid #fe019b;
|
||||
// /* 主色1 */
|
||||
// // background: #fe019b;
|
||||
// .r {
|
||||
// font-family: PingFang SC;
|
||||
// font-size: 32rpx;
|
||||
// font-weight: normal;
|
||||
// line-height: 32rpx;
|
||||
// text-align: center;
|
||||
// letter-spacing: normal;
|
||||
// /* 主色1 */
|
||||
// color: #fe019b;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
.recharge-record {
|
||||
margin: 32rpx auto;
|
||||
width: 654rpx;
|
||||
height: 1127rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// align-items: center;
|
||||
padding: 0 28rpx 28rpx 28rpx;
|
||||
gap: 32rpx;
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
background: #ffffff;
|
||||
|
||||
.card-layer {
|
||||
height: 106rpx;
|
||||
border-bottom: 1px solid #ececec;
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 30rpx;
|
||||
|
||||
.k {
|
||||
font-family: PingFangSC;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 28rpx;
|
||||
letter-spacing: normal;
|
||||
color: #3d3d3d;
|
||||
}
|
||||
}
|
||||
|
||||
.time {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 30rpx;
|
||||
|
||||
.time-t {
|
||||
display: flex;
|
||||
|
||||
.t {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9b939a;
|
||||
}
|
||||
|
||||
.i {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9b939a;
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.t {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9b939a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.occupation-map {
|
||||
margin: 250rpx auto;
|
||||
width: 160px;
|
||||
height: 379.5rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
gap: 40rpx;
|
||||
|
||||
.service-head-img {
|
||||
width: 320rpx;
|
||||
height: 299.5rpx;
|
||||
}
|
||||
|
||||
.z {
|
||||
font-family: PingFang SC;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
line-height: 40rpx;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #726e71;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
240
src/pages/client/recharge/invite-friends.vue
Normal file
@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<view class="send-friends">
|
||||
<image class="diamond" src="https://activity.wagoo.live/poster.png" mode="widthFix" />
|
||||
<view class="my-friend">
|
||||
<text class="y">新人专属福利</text>
|
||||
<view class="my-reward">
|
||||
<text class="m">首单更</text> <text class="j">优惠</text>
|
||||
</view>
|
||||
<view class="my-login" @click="LoginNow">
|
||||
<text class="d">立即登录领取</text>
|
||||
</view>
|
||||
<view class="my-step">
|
||||
<view class="c" /> <text class="step">新人专享劵</text>
|
||||
<view class="c" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-list-content">
|
||||
<list-page-temp :requestData="{ use_status: tabsList[curTabIndex].id, type: 0 }"
|
||||
:getDataPromise="getOwnCouponData" :reloadFlag="reloadFlag"
|
||||
:listExtraFields="{ nearDisabled: curTabIndex === 2 }">
|
||||
<template v-slot:item="{ data }">
|
||||
<coupon-item :data="data" :showOptBtn="false" :disabled="data.use_status === 2"
|
||||
:showCountDown="data.nearDisabled && data.daojishi">
|
||||
<!-- <image
|
||||
v-if="data.use_status === 2"
|
||||
slot="status"
|
||||
class="use-icon"
|
||||
src="./static/coupon_used.png"
|
||||
/> -->
|
||||
</coupon-item>
|
||||
</template>
|
||||
</list-page-temp>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabsList from "@/components/TabsList.vue";
|
||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
||||
import CouponItem from "@/components/coupon/CouponItem";
|
||||
import { getOwnCouponData } from "@/api/coupon";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TabsList,
|
||||
ListPageTemp,
|
||||
CouponItem,
|
||||
},
|
||||
computed: {
|
||||
tabsList() {
|
||||
return [
|
||||
{
|
||||
name: "",
|
||||
id: 1,
|
||||
},
|
||||
|
||||
];
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options?.referrerID) {
|
||||
this.$store.dispatch('user/setReferrerID', Number(options.referrerID) || 0);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
curTabIndex: 0,
|
||||
reloadFlag: 0,
|
||||
userId: ''
|
||||
|
||||
}
|
||||
},
|
||||
options: {
|
||||
styleIsolation: "shared",
|
||||
},
|
||||
onShow() {
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
methods: {
|
||||
getOwnCouponData,
|
||||
onTabChange(item, index) {
|
||||
this.curTabIndex = index;
|
||||
},
|
||||
LoginNow() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/auth/index`
|
||||
});
|
||||
},
|
||||
jumpTo(url) {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.send-friends {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.diamond {
|
||||
width: 100%;
|
||||
height: 676rpx;
|
||||
}
|
||||
|
||||
.my-friend {
|
||||
margin-top: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 140rpx;
|
||||
gap: 32rpx;
|
||||
|
||||
.y {
|
||||
|
||||
font-family: Alibaba PuHuiTi 2.0;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #3D3D3D;
|
||||
|
||||
}
|
||||
|
||||
.my-reward {
|
||||
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.j {
|
||||
font-family: PingFang SC;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 首单更 */
|
||||
color: #FE019B
|
||||
}
|
||||
}
|
||||
|
||||
.m {
|
||||
font-family: PingFang SC;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 首单更 */
|
||||
color: #3D3D3D
|
||||
}
|
||||
|
||||
.my-login {
|
||||
width: 207px;
|
||||
height: 36px;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
gap: 4px;
|
||||
z-index: 0;
|
||||
border-radius: 100px;
|
||||
/* 主色1 */
|
||||
background: #FE019B;
|
||||
|
||||
.d {
|
||||
font-family: PingFangSC;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.my-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 26rpx;
|
||||
|
||||
.c {
|
||||
width: 120rpx;
|
||||
height: 0;
|
||||
z-index: 0;
|
||||
border-top: 1rpx solid #333333;
|
||||
}
|
||||
|
||||
.step {
|
||||
width: 138rpx;
|
||||
height: 28rpx;
|
||||
z-index: 1;
|
||||
font-family: PingFang SC;
|
||||
font-size: 28rpx;
|
||||
font-weight: normal;
|
||||
line-height: 28rpx;
|
||||
letter-spacing: normal;
|
||||
color: #333333;
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.order-list-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding-left: 32rpx;
|
||||
margin-top: 190rpx;
|
||||
|
||||
::v-deep {
|
||||
.coupon-price {
|
||||
color: $app_fc_alarm;
|
||||
}
|
||||
|
||||
.coupon-item {
|
||||
.circle {
|
||||
background: #f7f8fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.use-icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
384
src/pages/client/recharge/membership-code.vue
Normal file
@ -0,0 +1,384 @@
|
||||
<template>
|
||||
<view class="send-welfare">
|
||||
<image class="diamond" src="https://activity.wagoo.live/poster.png" mode="widthFix" />
|
||||
<view class="my-friend">
|
||||
<view class="my-reward">
|
||||
<text class="y">邀请好友获得</text> <text class="j">奖励</text>
|
||||
</view>
|
||||
<text class="m">每成功邀1个新用户达成任务,可重复获得奖励</text>
|
||||
</view>
|
||||
<view class="share-Pictures">
|
||||
<image
|
||||
class="container"
|
||||
src="https://activity.wagoo.live/container.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<button open-type="share" class="left" />
|
||||
<!-- <view @click="jumpTo('/pages/client/recharge/share-pictures')" class="right" /> -->
|
||||
</view>
|
||||
<view class="my-achievements">
|
||||
<view class="my-title">
|
||||
<view class="my-step">
|
||||
<view class="c" /> <text class="step">我的成就</text> <view class="c"
|
||||
/></view>
|
||||
<view class="my-invite">
|
||||
<view class="invite">
|
||||
<text class="y">已成功邀请</text>
|
||||
<text class="people">{{orderInfo.user_count}} 人</text>
|
||||
</view>
|
||||
<!-- <view class="invite">
|
||||
<text class="y">已获得积分</text>
|
||||
<text class="people">{{orderInfo.points}} 个</text>
|
||||
</view> -->
|
||||
<view class="invite">
|
||||
<text class="y">已获得优惠券</text>
|
||||
<text class="people">{{orderInfo.coupon_count}} 张</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="invitation-record">
|
||||
<view class="my-title">
|
||||
<view class="my-step">
|
||||
<view class="c" /> <text class="step">邀请记录</text> <view class="c"
|
||||
/></view>
|
||||
<!-- <view v-for="(item, index) in orderInfo.users"
|
||||
:key="index"> -->
|
||||
<view class="drop-down" v-if="orderInfo.users && orderInfo.users.length" >
|
||||
<view class="my-invite" v-for="(item, index) in orderInfo.users"
|
||||
:key="index">
|
||||
<view class="left">
|
||||
<image
|
||||
|
||||
:src="item.head_pic"
|
||||
mode="widthFix"
|
||||
class="head-img"
|
||||
/>
|
||||
<view class="my-name">
|
||||
<text class="y">{{item.nick_name}}</text>
|
||||
<!-- <text class="y">ID: 101067</text> -->
|
||||
</view>
|
||||
</view>
|
||||
<text class="right">{{item.add_time}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text v-else class="invite-ser">空空如也,赶快去邀请吧</text>
|
||||
<!-- </view> -->
|
||||
</view>
|
||||
</view>
|
||||
<view class="bottom" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userShare } from '../../../api/login';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
orderInfo: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
userInfo() {
|
||||
return this.$store.state?.user?.userInfo || {};
|
||||
},
|
||||
memberId() {
|
||||
const u = this.userInfo;
|
||||
return (u && (u.userID || u.user_id)) ? String(u.userID || u.user_id) : '';
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.getData(this.memberId);
|
||||
},
|
||||
onShareAppMessage(e) {
|
||||
// console.log(e,'-=-=-')
|
||||
if (e.from === 'button') {
|
||||
// let obj = e.target.dataset.obj // 获取 button 组件 自定义的data-obj值
|
||||
// console.log(obj, '查看分享的输出');
|
||||
return {
|
||||
title: 'wagoo', // 标题
|
||||
imageUrl:'https://activity.wagoo.live/poster.png', // 封面图
|
||||
path: `pages/client/recharge/invite-friends?referrerID=${this.memberId}` // 地址以及参数
|
||||
};
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getData(member_id) {
|
||||
userShare(member_id).then((res) => {
|
||||
this.orderInfo = res?.data;
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
jumpTo(url) {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.send-welfare {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.diamond {
|
||||
width: 100%;
|
||||
height: 676rpx;
|
||||
}
|
||||
.my-friend{
|
||||
margin-top:30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 140rpx;
|
||||
gap: 32rpx;
|
||||
.my-reward{
|
||||
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.y{
|
||||
font-family: Alibaba PuHuiTi 2.0;
|
||||
font-size: 48rpx;
|
||||
font-weight: 500;
|
||||
line-height: 48rpx;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 邀请好友得 */
|
||||
color: #3D3D3D
|
||||
|
||||
}
|
||||
.j{
|
||||
font-family: Alibaba PuHuiTi 2.0;
|
||||
font-size: 48rpx;
|
||||
font-weight: 500;
|
||||
line-height: 48rpx;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 邀请好友得 */
|
||||
color: #C13030
|
||||
|
||||
}
|
||||
}
|
||||
.m{
|
||||
font-family: Alibaba PuHuiTi 2.0;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #3D3D3D;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
.share-Pictures{
|
||||
position: relative;
|
||||
.container {
|
||||
width: 750rpx;
|
||||
height: 384px;
|
||||
}
|
||||
.left{
|
||||
width: 90%;
|
||||
height: 85rpx;
|
||||
border-radius: 100rpx;
|
||||
position: absolute;
|
||||
left: 32rpx;
|
||||
top: 7rpx;
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
}
|
||||
wx-button:after{
|
||||
border: none;
|
||||
}
|
||||
.right{
|
||||
width: 332rpx;
|
||||
height: 85rpx;
|
||||
border-radius: 100rpx;
|
||||
position: absolute;
|
||||
right: 18rpx;
|
||||
top: 7rpx;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.my-achievements {
|
||||
margin: 20rpx auto;
|
||||
width: 656rpx;
|
||||
height: 248rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 32rpx 24rpx;
|
||||
gap: 32rpx;
|
||||
z-index: 2;
|
||||
border-radius: 24rpx;
|
||||
background: #ffffff;
|
||||
.my-title {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.my-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 50rpx;
|
||||
.c {
|
||||
width: 120rpx;
|
||||
height: 0;
|
||||
z-index: 0;
|
||||
border-top: 1rpx solid #333333;
|
||||
}
|
||||
.step {
|
||||
width: 117rpx;
|
||||
height: 28rpx;
|
||||
z-index: 1;
|
||||
font-family: PingFang SC;
|
||||
font-size: 28rpx;
|
||||
font-weight: normal;
|
||||
line-height: 28rpx;
|
||||
letter-spacing: normal;
|
||||
color: #333333;
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
}
|
||||
.my-invite {
|
||||
margin-top: 30rrpx;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
.invite {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 40rpx;
|
||||
.y {
|
||||
font-family: PingFangSC;
|
||||
font-size: 28rpx;
|
||||
font-weight: normal;
|
||||
line-height:28rpx;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #3d3d3d;
|
||||
}
|
||||
.people {
|
||||
font-family: PingFangSC;
|
||||
font-size: 36rpx;
|
||||
font-weight: normal;
|
||||
line-height: 36rpx;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 错误色/错误色 */
|
||||
color: #ea0000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.invitation-record {
|
||||
margin: 20rpx auto;
|
||||
width:656rpx;
|
||||
height: 248rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 32rpx 24rpx;
|
||||
gap: 32rpx;
|
||||
z-index: 2;
|
||||
border-radius: 24rpx;
|
||||
background: #ffffff;
|
||||
.my-title {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.my-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10rpx;
|
||||
.c {
|
||||
width: 120rpx;
|
||||
height: 0;
|
||||
z-index: 0;
|
||||
border-top: 0.5px solid #333333;
|
||||
}
|
||||
.step {
|
||||
width: 112rpx;
|
||||
height: 28rpx;
|
||||
z-index: 1;
|
||||
font-family: PingFang SC;
|
||||
font-size: 28rpx;
|
||||
font-weight: normal;
|
||||
line-height: 28rpx;
|
||||
letter-spacing: normal;
|
||||
color: #333333;
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
}
|
||||
.drop-down {
|
||||
height: 280rpx;
|
||||
overflow: auto;
|
||||
.my-invite {
|
||||
margin-top: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.head-img {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
z-index: 0;
|
||||
box-sizing: border-box;
|
||||
border: 0 solid #ffffff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.my-name {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
.y {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9b939a;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9b939a;
|
||||
}
|
||||
}
|
||||
}
|
||||
.invite-ser{
|
||||
margin: 100rpx auto;
|
||||
display: block;
|
||||
color: #707070;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom {
|
||||
height: 50rpx;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
917
src/pages/client/recharge/my-card.vue
Normal file
@ -0,0 +1,917 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="tab-view">
|
||||
<view
|
||||
class="tab-item"
|
||||
v-for="item in tabList"
|
||||
:key="item.value"
|
||||
@click.stop="selectOrderStatus(item)"
|
||||
>
|
||||
<text
|
||||
class="fs-34 app-fc-normal"
|
||||
:class="{ 'tab-text ali-puhui-bold': item.value == currentStatus }"
|
||||
>
|
||||
{{ item.label }}
|
||||
</text>
|
||||
<view
|
||||
class="tab-line"
|
||||
:class="{ 'tab-selected-line': item.value == currentStatus }"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="scroll-white" v-if="scrollLeft">
|
||||
<view class="scroll" v-for="(item , index) in platinum"
|
||||
:key="index">
|
||||
<image
|
||||
src="https://activity.wagoo.live/member2.png"
|
||||
mode="widthFix"
|
||||
class="service-head-img"
|
||||
@click="cardDetails(item,1)"
|
||||
/>
|
||||
<!-- <image
|
||||
@click="jumpTo(`/pages/richText/member-interests`)"
|
||||
src="https://activity.wagoo.live/xf-1.png"
|
||||
mode="widthFix"
|
||||
class="head-img"
|
||||
/> -->
|
||||
<image v-if="item.bounding_pets[0].pet_avatar" :src="item.bounding_pets[0].pet_avatar" mode="widthFix" class="add-img" />
|
||||
<image
|
||||
v-else
|
||||
@click="clickElasticLayer(item)"
|
||||
src="https://activity.wagoo.live/bd-3.png"
|
||||
mode="widthFix"
|
||||
class="add-img"
|
||||
/>
|
||||
|
||||
<view class="number">
|
||||
<text class="n">编号</text>
|
||||
<text class="n">{{item.card_number.substring(0, 16)}}</text>
|
||||
</view>
|
||||
|
||||
<view class="immediately">
|
||||
<text class="n">有效期至:</text>
|
||||
<text class="n">{{item.expired_date.substring(0, 10)}}</text>
|
||||
</view>
|
||||
|
||||
<view class="startDate">
|
||||
<text class="n">购卡日期:</text>
|
||||
<text class="n">{{item.effective_date.substring(0, 10)}}</text>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<!-- <view class="card-roll">
|
||||
</view> -->
|
||||
<!-- <view class="recharge" @click="jumpTo('/pages/client/recharge/index')">
|
||||
<text class="immediately">立即充值</text>
|
||||
</view> -->
|
||||
</view>
|
||||
<view class="scroll" v-for="(item , index) in blackGold"
|
||||
:key="index">
|
||||
<image
|
||||
src="https://activity.wagoo.live/member1.png"
|
||||
mode="widthFix"
|
||||
class="service-head-img"
|
||||
@click="cardDetails(item,2)"
|
||||
/>
|
||||
<!-- <image
|
||||
@click="jumpTo(`/pages/richText/member-interests`)"
|
||||
src="https://activity.wagoo.live/xf-3.png"
|
||||
mode="widthFix"
|
||||
class="head-img"
|
||||
/> -->
|
||||
<view class="imgList" >
|
||||
<view class="img" v-for="(it, inx) in item.bounding_pets"
|
||||
:key="inx">
|
||||
<image
|
||||
:src="it.pet_avatar"
|
||||
mode="aspectFill"
|
||||
class="add-i"
|
||||
/>
|
||||
</view>
|
||||
<image
|
||||
v-if="item.bounding_pets.length <=5"
|
||||
src="https://activity.wagoo.live/bd-1.png"
|
||||
mode="widthFix"
|
||||
class="add-img2"
|
||||
@click="selectelaList(item)"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="number1">
|
||||
<text class="n">编号</text>
|
||||
<text class="n">{{item.card_number.substring(0, 16)}}</text>
|
||||
</view>
|
||||
|
||||
<view class="immediately1">
|
||||
<text class="n">有效期至:</text>
|
||||
<text class="n">{{item.expired_date.substring(0, 10)}}</text>
|
||||
</view>
|
||||
|
||||
<view class="startDate1">
|
||||
<text class="n">购卡日期:</text>
|
||||
<text class="n">{{item.effective_date.substring(0, 10)}}</text>
|
||||
</view>
|
||||
<!-- <text class="number1">{{item.card_number.substring(0, 8)}}</text>
|
||||
<text class="immediately1">{{item.effective_date.substring(0, 10)}}</text>
|
||||
<text class="startDate1">{{item.expired_date.substring(0, 10)}}</text> -->
|
||||
<!-- <view class="card-roll">
|
||||
</view> -->
|
||||
<!-- <view class="recharge" @click="jumpTo('/pages/client/recharge/index')">
|
||||
<text class="immediately">立即充值</text>
|
||||
</view> -->
|
||||
</view>
|
||||
<view class="scroll" v-for="(item, index) in gold"
|
||||
:key="index">
|
||||
<image
|
||||
src="https://activity.wagoo.live/member3.png"
|
||||
mode="widthFix"
|
||||
class="service-head-img"
|
||||
@click="cardDetails(item,3)"
|
||||
/>
|
||||
<!-- <image
|
||||
@click="jumpTo(`/pages/richText/member-interests`)"
|
||||
src="https://activity.wagoo.live/xf-2.png"
|
||||
mode="widthFix"
|
||||
class="head-img"
|
||||
/> -->
|
||||
<image
|
||||
v-if="item.bounding_pets[0].pet_avatar"
|
||||
:src="item.bounding_pets[0].pet_avatar"
|
||||
mode="widthFix"
|
||||
class="add-img"
|
||||
/>
|
||||
<image
|
||||
v-else
|
||||
src="https://activity.wagoo.live/bd-2.png"
|
||||
mode="widthFix"
|
||||
class="add-img"
|
||||
@click="clickElasticLayer2(item)"
|
||||
/>
|
||||
<view class="number">
|
||||
<text class="n">编号</text>
|
||||
<text class="n">{{item.card_number.substring(0, 16)}}</text>
|
||||
</view>
|
||||
|
||||
<view class="immediately">
|
||||
<text class="n">有效期至:</text>
|
||||
<text class="n">{{item.expired_date.substring(0, 10)}}</text>
|
||||
</view>
|
||||
|
||||
<view class="startDate">
|
||||
<text class="n">购卡日期:</text>
|
||||
<text class="n">{{item.effective_date.substring(0, 10)}}</text>
|
||||
</view>
|
||||
|
||||
<!-- <text class="number">{{item.card_number.substring(0, 8)}}</text>
|
||||
<text class="immediately">{{item.effective_date.substring(0, 10)}}</text>
|
||||
<text class="startDate">{{item.expired_date.substring(0, 10)}}</text> -->
|
||||
|
||||
<!-- <view class="card-roll">
|
||||
|
||||
</view> -->
|
||||
<!-- <view class="recharge" @click="jumpTo('/pages/client/recharge/index')">
|
||||
<text class="immediately">立即充值</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="occupation-bom"> -->
|
||||
<view
|
||||
class="occupation-map"
|
||||
v-if="false"
|
||||
@click="jumpTo('/pages/client/index/index?activePageId=shopPage')"
|
||||
>
|
||||
<image
|
||||
src="https://activity.wagoo.live/membership-card.png"
|
||||
mode="widthFix"
|
||||
class="service-head-img"
|
||||
/>
|
||||
<!-- <text class="z">暂无内容</text> -->
|
||||
<!-- <view class="card-purchase">
|
||||
<text class="l">立即购卡</text>
|
||||
</view> -->
|
||||
</view>
|
||||
<view
|
||||
@click="elasticLayer = false"
|
||||
v-if="elasticLayer"
|
||||
class="elastic-layer"
|
||||
/>
|
||||
<view v-if="elasticLayer" class="select-pet">
|
||||
<view class="select-pet-header">
|
||||
<text class="s">选择宠物</text>
|
||||
<image
|
||||
src="@/static/images/close.png"
|
||||
mode="aspectFit"
|
||||
class="close-icon"
|
||||
@click="elasticLayer = false"
|
||||
/>
|
||||
</view>
|
||||
<scroll-view class="select-pet-scroll" scroll-y>
|
||||
<view
|
||||
class="select-bom"
|
||||
@click="addPets(item,index)"
|
||||
v-for="(item, index) in dataList"
|
||||
:key="index"
|
||||
>
|
||||
<view
|
||||
:class="[
|
||||
item.id == selectPetid ? 'select-item2' : 'select-item',
|
||||
]"
|
||||
>
|
||||
<view class="select-left">
|
||||
<image
|
||||
class="diamond"
|
||||
:src="item.avatar"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="a">{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="add-animal">
|
||||
<view class="right" @click="switchimgLoad">
|
||||
<text class="a" style="color: #fff;">绑定</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- </view> -->
|
||||
<!-- 黑金多选 -->
|
||||
<view
|
||||
@click="elasticLayer2 = false"
|
||||
v-if="elasticLayer2"
|
||||
class="elastic-layer"
|
||||
/>
|
||||
<view v-if="elasticLayer2" class="select-pet">
|
||||
<view class="select-pet-header">
|
||||
<text class="s">选择宠物</text>
|
||||
<image
|
||||
src="@/static/images/close.png"
|
||||
mode="aspectFit"
|
||||
class="close-icon"
|
||||
@click="elasticLayer2 = false"
|
||||
/>
|
||||
</view>
|
||||
<scroll-view class="select-pet-scroll" scroll-y>
|
||||
<view
|
||||
class="select-bom"
|
||||
@click="selectAction(item, index)"
|
||||
v-for="(item, index) in dataList"
|
||||
:key="index"
|
||||
>
|
||||
<view :class="[item.delete == 2 ? 'select-item2' : 'select-item']">
|
||||
<view class="select-left">
|
||||
<image
|
||||
class="diamond"
|
||||
:src="item.avatar"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="a">{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="add-animal">
|
||||
<view class="right" @click="switchimgLoad2">
|
||||
<text class="a" style="color: #fff;">绑定</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
userHoldrlist,
|
||||
bindPets,
|
||||
petBinding
|
||||
} from "../../../api/login";
|
||||
import { getPetList } from "@/api/common";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
platinum:[],
|
||||
petList:[],
|
||||
gold:[],
|
||||
dataList:[],
|
||||
data: [],
|
||||
blackGold:[],
|
||||
selectId: [],
|
||||
selectList:[],
|
||||
currentStatus: "0",
|
||||
elasticLayer: false,
|
||||
elasticLayer2: false,
|
||||
scrollLeft:true,
|
||||
haerImg: "",
|
||||
haerImg2: "",
|
||||
selectPetid: "",
|
||||
additionalBom: false,
|
||||
tabList: [
|
||||
{
|
||||
value: "0",
|
||||
label: "生效中",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "已失效",
|
||||
},
|
||||
// ,
|
||||
// {
|
||||
// value: "2",
|
||||
// label: "服务记录",
|
||||
// },
|
||||
// {
|
||||
// value: '1',
|
||||
// label: '健康记录',
|
||||
// }
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
|
||||
onLoad(options) {
|
||||
this.user_id = options.user_id
|
||||
// this.getData();
|
||||
this.getuserHoldrlist()
|
||||
},
|
||||
methods: {
|
||||
jumpTo(url) {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
});
|
||||
},
|
||||
cardDetails(item,v){
|
||||
// @click="jumpTo(`/pages/client/recharge/recharge-details?catId=1`)"
|
||||
// console.log(item,v,'--')
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/recharge/recharge-details?catId=${v}&userList=${encodeURIComponent(JSON.stringify(item))}`,
|
||||
});
|
||||
},
|
||||
// 绑定接口
|
||||
geBindin() {
|
||||
console.log(this.bindPets1,'--?')
|
||||
const data = {
|
||||
// user_id:this.bindPets1.user_id,
|
||||
instance_id:this.bindPets1.id,
|
||||
user_nickname:this.bindPets1.user_nickname,
|
||||
card_type:this.bindPets1.card_type,
|
||||
card_number:this.bindPets1.card_number,
|
||||
pets:this.petList
|
||||
// pet_id:this.pet_id,
|
||||
// pet_name:this.pet_name,
|
||||
// pet_avatar:this.pet_avatar
|
||||
// Pet List
|
||||
}
|
||||
petBinding(data).then((res) => {
|
||||
if(res.code == 0){
|
||||
uni.showToast({
|
||||
title: "绑定成功",
|
||||
icon: "none",
|
||||
});
|
||||
this.selectId = []
|
||||
this.petList = []
|
||||
this.selectPetid = ''
|
||||
console.log(111222)
|
||||
// this.getData();
|
||||
this.getuserHoldrlist()
|
||||
}
|
||||
// console.log( this.gold,'--')
|
||||
// this.butlerApplyInfo = res?.info || {};
|
||||
});
|
||||
},
|
||||
// 卡包列表接口
|
||||
getuserHoldrlist() {
|
||||
const data = {
|
||||
user_id:this.user_id
|
||||
}
|
||||
userHoldrlist(data).then((res) => {
|
||||
this.platinum = res.data.active.白金
|
||||
this.gold = res.data.active.黄金
|
||||
this.blackGold = res.data.active.黑金
|
||||
|
||||
// console.log( this.gold,'--')
|
||||
// this.butlerApplyInfo = res?.info || {};
|
||||
});
|
||||
},
|
||||
clickElasticLayer(item) {
|
||||
|
||||
this.bindPets1 = item
|
||||
this.v = 1;
|
||||
this.elasticLayer = true;
|
||||
const data = {
|
||||
user_id:this.user_id,
|
||||
card_number:item.card_number
|
||||
}
|
||||
bindPets(data)
|
||||
.then((res) => {
|
||||
this.dataList = res.data
|
||||
})
|
||||
},
|
||||
clickElasticLayer2(item) {
|
||||
this.bindPets1 = item
|
||||
this.v = 2;
|
||||
this.elasticLayer = true;
|
||||
const data = {
|
||||
user_id:this.user_id,
|
||||
card_number:item.card_number
|
||||
}
|
||||
bindPets(data)
|
||||
.then((res) => {
|
||||
this.dataList = res.data
|
||||
})
|
||||
|
||||
},
|
||||
addPets(item,index) {
|
||||
console.log(item, "--");
|
||||
this.switchList = item;
|
||||
this.petList = item
|
||||
this.selectPetid = item.id;
|
||||
},
|
||||
switchimgLoad() {
|
||||
// console.log(this.v,'--')
|
||||
if (this.v == 1) {
|
||||
this.geBindin()
|
||||
console.log(this.bindPets1,'??')
|
||||
// this.haerImg = this.switchList.chongwu_pic;
|
||||
this.elasticLayer = false;
|
||||
|
||||
} else if (this.v == 2) {
|
||||
this.geBindin()
|
||||
// this.haerImg2 = this.switchList.chongwu_pic;
|
||||
// console.log(this.switchList.chongwu_pic,'--')
|
||||
this.elasticLayer = false;
|
||||
|
||||
}
|
||||
},
|
||||
switchimgLoad2(){
|
||||
this.geBindin()
|
||||
this.elasticLayer2 = false;
|
||||
// this.selectList = this.selectId
|
||||
// this.elasticLayer2 = false;
|
||||
// uni.showToast({
|
||||
// title: "绑定成功",
|
||||
// icon: "none",
|
||||
// });
|
||||
},
|
||||
selectelaList(item){
|
||||
this.elasticLayer2 = true;
|
||||
this.bindPets1 = item
|
||||
const data = {
|
||||
user_id:this.user_id,
|
||||
card_number:item.card_number
|
||||
}
|
||||
bindPets(data)
|
||||
.then((res) => {
|
||||
this.dataList = res.data
|
||||
})
|
||||
|
||||
},
|
||||
selectAction(item, index) {
|
||||
// console.log(item,index,'--=')
|
||||
if (item.delete == 2) {
|
||||
item.delete = 1;
|
||||
//取消选中时删除数组中的值
|
||||
for (var i = 0; i < this.selectId.length; i++) {
|
||||
if (this.selectId[i] === item) {
|
||||
this.selectId.splice(i, 1);
|
||||
}
|
||||
}
|
||||
console.log("选中的值1", this.selectId);
|
||||
// this.seleId = this.selectId[index].item_id
|
||||
} else {
|
||||
item.delete = 2;
|
||||
this.selectId.push(item);
|
||||
this.petList = this.selectId
|
||||
// this.seleIdx = item.services[index].idx;
|
||||
// console.log(this.seleIdx, "--");
|
||||
console.log("选中的值2", this.selectId);
|
||||
}
|
||||
},
|
||||
// getData() {
|
||||
|
||||
// .catch(() => {});
|
||||
// },
|
||||
selectOrderStatus(status) {
|
||||
// console.log(status, "--===-");
|
||||
if (this.currentStatus === status.value) {
|
||||
return;
|
||||
}
|
||||
this.currentStatus = status.value;
|
||||
if (status.value == 0) {
|
||||
this.scrollLeft = true
|
||||
|
||||
} else if (status.value == 1) {
|
||||
this.scrollLeft = false
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tab-view {
|
||||
width: 100%;
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
background-color: #ffffff;
|
||||
|
||||
.tab-item {
|
||||
display: flex;
|
||||
margin-left: 40rpx;
|
||||
// flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.tab-text {
|
||||
// font-weight: bold;
|
||||
color: $app_fc_main;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-line {
|
||||
// transform: translateX(-50%);
|
||||
width: 24rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 12rpx;
|
||||
// width: 30rpx;
|
||||
// height: 6rpx;
|
||||
background-color: transparent;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.tab-selected-line {
|
||||
background-color: $app_color_main;
|
||||
}
|
||||
}
|
||||
.scroll-white {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
padding-bottom: 30rpx;
|
||||
.scroll {
|
||||
display: flex;
|
||||
margin-top: 20rpx;
|
||||
justify-content: center;
|
||||
// justify-content: space-between;
|
||||
position: relative;
|
||||
.imgList {
|
||||
height: 92rpx;
|
||||
width: 500rpx;
|
||||
// border: 1px solid #fff;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
left:55rpx;
|
||||
bottom: 84rpx;
|
||||
.img{
|
||||
display: flex;
|
||||
margin-left: 20rpx;
|
||||
.add-i{
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.add-img2 {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
}
|
||||
.service-head-img {
|
||||
width:96%;
|
||||
height: 155px;
|
||||
}
|
||||
.head-img {
|
||||
position: absolute;
|
||||
right: 84rpx;
|
||||
top: 76rpx;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.number {
|
||||
position: absolute;
|
||||
// border: 1px solid #000;
|
||||
right:48rpx;
|
||||
top: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.n{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644d29;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
}
|
||||
.add-img {
|
||||
position: absolute;
|
||||
left:51rpx;
|
||||
top: 124rpx;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.immediately {
|
||||
position: absolute;
|
||||
right:42rpx;
|
||||
bottom: 33rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.n{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644d29;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
}
|
||||
.startDate {
|
||||
position: absolute;
|
||||
left:46rpx;
|
||||
bottom: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.n{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644d29;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.immediately1 {
|
||||
position: absolute;
|
||||
right:42rpx;
|
||||
bottom: 33rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.n{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #CBAD78;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
}
|
||||
.number1 {
|
||||
position: absolute;
|
||||
// border: 1px solid #000;
|
||||
right:48rpx;
|
||||
top: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.n{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #CBAD78;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
}
|
||||
.startDate1 {
|
||||
position: absolute;
|
||||
left:46rpx;
|
||||
bottom: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.n{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #CBAD78;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
}
|
||||
// .startDate1 {
|
||||
// position: absolute;
|
||||
// left: 179rpx;
|
||||
// bottom: 30rpx;
|
||||
// font-family: PingFangSC;
|
||||
// font-size: 12px;
|
||||
// color: #CBAD78;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
.occupation-map {
|
||||
height: 1004px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 30rpx;
|
||||
.service-head-img {
|
||||
width: 351px;
|
||||
height: 502px;
|
||||
}
|
||||
.z {
|
||||
font-family: PingFang SC;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
line-height: 40rpx;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #726e71;
|
||||
}
|
||||
.card-purchase {
|
||||
width: 96px;
|
||||
height: 37px;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
gap: 10px;
|
||||
z-index: 2;
|
||||
border-radius: 20px;
|
||||
/* 主色1 */
|
||||
background: #fe019b;
|
||||
.l {
|
||||
font-family: PingFang SC;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 21px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.elastic-layer {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
z-index: 999;
|
||||
}
|
||||
.select-pet {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
max-height: 70vh;
|
||||
background: #fff;
|
||||
border-radius: 20px 20px 0px 0px;
|
||||
z-index: 99999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.select-pet-header {
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
padding: 30rpx 0 50rpx;
|
||||
.s {
|
||||
font-family: PingFang SC;
|
||||
font-size: 18px;
|
||||
color: #3e4055;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
}
|
||||
.close-icon {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
position: absolute;
|
||||
right: 23rpx;
|
||||
top: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.select-pet-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.select-pet .select-bom {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
margin-top: 30rpx;
|
||||
.select-item {
|
||||
width: 351px;
|
||||
height: 56px;
|
||||
border-radius: 8px;
|
||||
opacity: 1;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0 8px 12px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
margin: auto;
|
||||
.select-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.diamond {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.a {
|
||||
margin-left: 20rpx;
|
||||
font-family: PingFang SC;
|
||||
font-size: 16px;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
.operate {
|
||||
display: flex;
|
||||
.operate-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 40rpx;
|
||||
.diamond {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
margin: 0 20rpx 4rpx 0;
|
||||
}
|
||||
.a {
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #808080;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.select-item2 {
|
||||
width: 351px;
|
||||
height: 56px;
|
||||
border-radius: 8px;
|
||||
opacity: 1;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0 8px 12px;
|
||||
margin: auto;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #FF19A0;
|
||||
.select-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.diamond {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.a {
|
||||
margin-left: 20rpx;
|
||||
font-family: PingFang SC;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.operate {
|
||||
display: flex;
|
||||
.operate-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 40rpx;
|
||||
.diamond {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
margin: 0 20rpx 4rpx 0;
|
||||
}
|
||||
.a {
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #808080;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-pet .add-animal {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 30rpx;
|
||||
padding-bottom: 60rpx;
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 343px;
|
||||
height: 48px;
|
||||
border-radius: 100px;
|
||||
margin-left: 30rpx;
|
||||
background-color: #FF19A0;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
231
src/pages/client/recharge/points-details.vue
Normal file
@ -0,0 +1,231 @@
|
||||
<template>
|
||||
<view class="points-details-container">
|
||||
<!-- 标签栏 -->
|
||||
<view class="tabs-container">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 0 }"
|
||||
@click="switchTab(0)"
|
||||
>
|
||||
<text class="tab-text">全部</text>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 1 }"
|
||||
@click="switchTab(1)"
|
||||
>
|
||||
<text class="tab-text">已获取</text>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 2 }"
|
||||
@click="switchTab(2)"
|
||||
>
|
||||
<text class="tab-text">已使用</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<scroll-view class="list-container" scroll-y>
|
||||
<view
|
||||
class="list-item"
|
||||
v-for="(item, index) in pointsList"
|
||||
:key="index"
|
||||
>
|
||||
<view class="item-left">
|
||||
<text class="item-type">{{ item.type || '未知' }}</text>
|
||||
<text class="item-time">{{ item.time || '' }}</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text
|
||||
class="item-points"
|
||||
:class="{ positive: item.points_change > 0, negative: item.points_change < 0 }"
|
||||
>
|
||||
{{ item.points_change > 0 ? '+' : '' }}{{ item.points_change }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-if="pointsList.length === 0" class="empty-state">
|
||||
<text class="empty-text">暂无积分明细</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPointsRecords } from '@/api/login';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
activeTab: 0, // 0: 全部, 1: 已获取, 2: 已使用
|
||||
pointsList: []
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
this.getPointsList();
|
||||
},
|
||||
methods: {
|
||||
switchTab(index) {
|
||||
if (this.activeTab === index) {
|
||||
return;
|
||||
}
|
||||
this.activeTab = index;
|
||||
this.getPointsList();
|
||||
},
|
||||
// 获取积分明细列表
|
||||
getPointsList() {
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
getPointsRecords({
|
||||
status: this.activeTab // 0: 全部, 1: 已获取, 2: 已使用
|
||||
}).then((res) => {
|
||||
uni.hideLoading();
|
||||
// 处理返回的数据
|
||||
this.pointsList = (res?.data || res?.info || []).map(item => {
|
||||
return {
|
||||
type: item.type || item.description || '未知',
|
||||
time: item.created_at || item.time || '',
|
||||
points_change: item.points_change || item.amount || 0
|
||||
};
|
||||
});
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: err.msg || '获取数据失败',
|
||||
icon: 'none'
|
||||
});
|
||||
this.pointsList = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.points-details-container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
// 标签栏
|
||||
.tabs-container {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
padding: 0 32rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx 0;
|
||||
position: relative;
|
||||
|
||||
.tab-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.tab-text {
|
||||
color: #FF19A0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 24rpx;
|
||||
height: 10rpx;
|
||||
background-color: #FF19A0;
|
||||
border-radius: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 列表容器
|
||||
.list-container {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-type {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #3D3D3D;
|
||||
}
|
||||
|
||||
.item-time {
|
||||
font-size: 24rpx;
|
||||
color: #9B939A;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-points {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
|
||||
&.positive {
|
||||
color: #FF19A0;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
color: #9B939A;
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态
|
||||
.empty-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
|
||||
411
src/pages/client/recharge/recharge-details.vue
Normal file
@ -0,0 +1,411 @@
|
||||
<template>
|
||||
<view class="rech-details">
|
||||
<view class="scroll-white" v-if="true">
|
||||
<view class="scroll" v-if="catId == 1">
|
||||
<image
|
||||
src="https://activity.wagoo.live/member2.png"
|
||||
mode="widthFix"
|
||||
class="service-head-img"
|
||||
/>
|
||||
<!-- <image
|
||||
@click="jumpTo(`/pages/richText/member-interests`)"
|
||||
src="https://activity.wagoo.live/xf-1.png"
|
||||
mode="widthFix"
|
||||
class="head-img"
|
||||
/> -->
|
||||
<!-- <text class="number">{{item.card_number.substring(0, 8)}}</text>
|
||||
<text class="immediately1">{{item.effective_date.substring(0, 10)}}</text>
|
||||
<text class="startDate1">{{item.expired_date.substring(0, 10)}}</text> -->
|
||||
<image
|
||||
:src="userList.bounding_pets[0].pet_avatar"
|
||||
mode="widthFix"
|
||||
class="add-img"
|
||||
/>
|
||||
<view class="number1">
|
||||
<text class="n">编号</text>
|
||||
<text class="n">{{ userList.card_number.substring(0, 8) }}</text>
|
||||
</view>
|
||||
<view class="immediately1">
|
||||
<text class="n">有效期至:</text>
|
||||
<text class="n">{{ userList.expired_date.substring(0, 10) }}</text>
|
||||
</view>
|
||||
<view class="startDate1">
|
||||
<text class="n">购卡日期:</text>
|
||||
<text class="n">{{ userList.effective_date.substring(0, 10) }}</text>
|
||||
</view>
|
||||
<!-- <text class="number">{{userList.card_number.substring(0, 8)}}</text>
|
||||
<text class="immediately">{{userList.effective_date.substring(0, 10)}}</text>
|
||||
<text class="startDate">{{userList.expired_date.substring(0, 10)}}</text> -->
|
||||
</view>
|
||||
<view class="scroll" v-if="catId == 2">
|
||||
<image
|
||||
src="https://activity.wagoo.live/member1.png"
|
||||
mode="widthFix"
|
||||
class="service-head-img"
|
||||
/>
|
||||
<!-- <image
|
||||
@click="jumpTo(`/pages/richText/member-interests`)"
|
||||
src="https://activity.wagoo.live/xf-3.png"
|
||||
mode="widthFix"
|
||||
class="head-img"
|
||||
/> -->
|
||||
<view class="imgList">
|
||||
<view
|
||||
class="img"
|
||||
v-for="(item, index) in userList.bounding_pets"
|
||||
:key="index"
|
||||
>
|
||||
<image :src="item.pet_avatar" mode="aspectFill" class="add-i" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="number">
|
||||
<text class="n">编号</text>
|
||||
<text class="n">{{ userList.card_number.substring(0, 8) }}</text>
|
||||
</view>
|
||||
<view class="immediately">
|
||||
<text class="n">有效期至:</text>
|
||||
<text class="n">{{ userList.effective_date.substring(0, 10) }}</text>
|
||||
</view>
|
||||
<view class="startDate">
|
||||
<text class="n">购卡日期:</text>
|
||||
<text class="n">{{ userList.effective_date.substring(0, 10) }}</text>
|
||||
</view>
|
||||
<!-- <text class="number1">{{ userList.card_number.substring(0, 8) }}</text>
|
||||
<text class="immediately1">{{
|
||||
userList.effective_date.substring(0, 10)
|
||||
}}</text>
|
||||
<text class="startDate1">{{
|
||||
userList.expired_date.substring(0, 10)
|
||||
}}</text> -->
|
||||
</view>
|
||||
<view class="scroll" v-if="catId == 3">
|
||||
<image
|
||||
src="https://activity.wagoo.live/member3.png"
|
||||
mode="widthFix"
|
||||
class="service-head-img"
|
||||
/>
|
||||
<!-- <image
|
||||
@click="jumpTo(`/pages/richText/member-interests`)"
|
||||
src="https://activity.wagoo.live/xf-2.png"
|
||||
mode="widthFix"
|
||||
class="head-img"
|
||||
/> -->
|
||||
<image
|
||||
:src="userList.bounding_pets[0].pet_avatar"
|
||||
mode="widthFix"
|
||||
class="add-img"
|
||||
/>
|
||||
<view class="number">
|
||||
<text class="n1">编号</text>
|
||||
<text class="n1">{{ userList.card_number.substring(0, 8) }}</text>
|
||||
</view>
|
||||
<view class="immediately">
|
||||
<text class="n1">有效期至:</text>
|
||||
<text class="n1">{{ userList.expired_date.substring(0, 10) }}</text>
|
||||
</view>
|
||||
<view class="startDate">
|
||||
<text class="n1">购卡日期:</text>
|
||||
<text class="n1">{{ userList.effective_date.substring(0, 10) }}</text>
|
||||
</view>
|
||||
<!-- <text class="number">{{ userList.card_number.substring(0, 8) }}</text>
|
||||
<text class="immediately">{{ userList.effective_date.substring(0, 10) }}</text>
|
||||
<text class="startDate">{{ userList.expired_date.substring(0, 10) }}</text> -->
|
||||
</view>
|
||||
</view>
|
||||
<view class="PetContent">
|
||||
<view class="PetItem">
|
||||
<text class="c1">已绑定宠物</text>
|
||||
<view class="pt-item">
|
||||
<view class="c2" v-for="(item, index) in userList.bounding_pets" :key="index">
|
||||
<text
|
||||
v-if="userList.bounding_pets && userList.bounding_pets.length >1 "
|
||||
>{{ item.pet_name }},
|
||||
</text>
|
||||
<text
|
||||
v-else
|
||||
>{{ item.pet_name }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">洗护折扣权益</text>
|
||||
<text class="c2">{{ userList.discount/10 }}折</text>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">入会礼-减免券</text>
|
||||
<text class="c2">已发放</text>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">定制浴液</text>
|
||||
<text class="c2">生效中</text>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">专属会员活动</text>
|
||||
<text class="c2">生效中</text>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">免调度费</text>
|
||||
<text class="c2">{{ userList.fee_exemption[0].fee_value }}次</text>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">公益勋章</text>
|
||||
<text class="c2">生效中</text>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">专属影集</text>
|
||||
<text class="c2">生效中</text>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">Al宠物档案</text>
|
||||
<text class="c2">生效中</text>
|
||||
</view>
|
||||
<!-- <view class="PetItem">
|
||||
<text class="c1">积分加速</text>
|
||||
<text class="c2">生效中</text>
|
||||
</view> -->
|
||||
<view class="PetItem">
|
||||
<text class="c1">专属丝带</text>
|
||||
<text class="c2">生效中</text>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">免夜间费</text>
|
||||
<text class="c2">{{ userList.fee_exemption[0].fee_value }}次</text>
|
||||
</view>
|
||||
<view class="PetItem">
|
||||
<text class="c1">专属客服</text>
|
||||
<text class="c2">生效中</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
catId: "",
|
||||
userList: "",
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
this.userList = JSON.parse(decodeURIComponent(options.userList));
|
||||
console.log(this.userList, "--");
|
||||
this.catId = options.catId;
|
||||
},
|
||||
|
||||
methods: {
|
||||
jumpTo(url) {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rech-details {
|
||||
margin: 28rpx auto;
|
||||
width: 96%;
|
||||
// height: 1234rpx;
|
||||
border-radius: 24rpx;
|
||||
// background: #ffffff;
|
||||
.scroll-white {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
.scroll {
|
||||
display: flex;
|
||||
// margin-top: 20rpx;
|
||||
justify-content: center;
|
||||
// justify-content: space-between;
|
||||
position: relative;
|
||||
.imgList {
|
||||
height: 92rpx;
|
||||
width: 500rpx;
|
||||
// border: 1px solid #fff;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
left: 37rpx;
|
||||
bottom: 84rpx;
|
||||
.img {
|
||||
display: flex;
|
||||
margin-left: 30rpx;
|
||||
.add-i {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.add-img2 {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
.service-head-img {
|
||||
width: 98%;
|
||||
height: 155px;
|
||||
}
|
||||
.head-img {
|
||||
position: absolute;
|
||||
right: 66rpx;
|
||||
top: 76rpx;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.number1 {
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #cbad78;
|
||||
position: absolute;
|
||||
right: 48rpx;
|
||||
top: 30rpx;
|
||||
.n {
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #3d3d3d;
|
||||
}
|
||||
}
|
||||
.add-img {
|
||||
position: absolute;
|
||||
left:51rpx;
|
||||
top: 124rpx;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.immediately1 {
|
||||
position: absolute;
|
||||
right: 60rpx;
|
||||
bottom: 30rpx;
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #cbad78;
|
||||
.n {
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #3d3d3d;
|
||||
}
|
||||
.n1{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644D29;
|
||||
}
|
||||
}
|
||||
.startDate1 {
|
||||
position: absolute;
|
||||
left: 49rpx;
|
||||
bottom: 30rpx;
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #cbad78;
|
||||
.n {
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #3d3d3d;
|
||||
}
|
||||
.n1{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644D29;
|
||||
}
|
||||
}
|
||||
.immediately {
|
||||
position: absolute;
|
||||
right: 60rpx;
|
||||
bottom: 30rpx;
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644d29;
|
||||
.n{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #CBAD78;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
.n1{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644D29;
|
||||
}
|
||||
}
|
||||
.number {
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644d29;
|
||||
position: absolute;
|
||||
right: 48rpx;
|
||||
top: 30rpx;
|
||||
.n {
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #CBAD78;
|
||||
}
|
||||
.n1{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644D29;
|
||||
}
|
||||
}
|
||||
.startDate {
|
||||
position: absolute;
|
||||
left:34rpx;
|
||||
bottom: 30rpx;
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644d29;
|
||||
.n{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #CBAD78;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
.n1{
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
color: #644D29;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.PetContent {
|
||||
background: #fff;
|
||||
// width: 351px;
|
||||
height: 100%;
|
||||
margin: 30rpx auto;
|
||||
border-radius: 12px;
|
||||
.PetItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
// margin-top: 30rpx;
|
||||
height: 120rpx;
|
||||
border-bottom: 1px solid #ececec;
|
||||
.c1 {
|
||||
font-family: PingFangSC;
|
||||
font-size: 14px;
|
||||
color: #686868;
|
||||
}
|
||||
.pt-item{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.c2 {
|
||||
font-family: PingFangSC;
|
||||
font-size: 14px;
|
||||
color: #040404;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
405
src/pages/client/recharge/share-pictures.vue
Normal file
@ -0,0 +1,405 @@
|
||||
<template>
|
||||
<view class="share-img">
|
||||
<image
|
||||
class="diamond"
|
||||
src="https://activity.wagoo.live/generate -images.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<view class="share-layer">
|
||||
<image
|
||||
class="share-cat"
|
||||
src="https://activity.wagoo.live/share-cat.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<view @click="shareLeft" class="left" />
|
||||
<view @click="shareRight" class="right" />
|
||||
<view @click="shareBottom" class="bottom" />
|
||||
</view>
|
||||
<view class="share-bg" v-if="shareDog">
|
||||
<image
|
||||
class="share-img"
|
||||
src="https://activity.wagoo.live/share-dog2.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<view class="share-bottom">
|
||||
<button class="share-friend" @click="FenXiang">
|
||||
<image
|
||||
class="diamond"
|
||||
src="https://activity.wagoo.live/share-friend.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<text class="f"> 发送给朋友 </text>
|
||||
</button>
|
||||
<view @click="shareToFriends" class="share-friend">
|
||||
<image
|
||||
class="diamond"
|
||||
src="https://activity.wagoo.live/share-moments.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<text class="f"> 分享到朋友圈 </text>
|
||||
</view>
|
||||
<view class="share-friend">
|
||||
<image
|
||||
class="diamond"
|
||||
src="https://activity.wagoo.live/share-collect.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<text class="f"> 收藏 </text>
|
||||
</view>
|
||||
<view class="share-friend">
|
||||
<image
|
||||
class="diamond"
|
||||
src="https://activity.wagoo.live/share-save.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<text class="f"> 保存图片 </text>
|
||||
</view>
|
||||
<image
|
||||
@click="shareDog = false"
|
||||
class="close"
|
||||
src="https://activity.wagoo.live/share.delete.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <canvas id="qrcode" canvas-id="qrcode" style="width: 200px;height: 200px;"></canvas> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import UQRCode from 'uqrcodejs';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
shareDog: false,
|
||||
str: "https://activity.wagoo.live/generate -images.png",
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
// console.log(1122);
|
||||
},
|
||||
methods: {
|
||||
initMenu() {
|
||||
uni.showShareMenu({
|
||||
withShareTicket: true,
|
||||
//设置下方的Menus菜单,才能够让发送给朋友与分享到朋友圈两个按钮可以点击
|
||||
menus: ["shareAppMessage", "shareTimeline"],
|
||||
});
|
||||
},
|
||||
FenXiang() {
|
||||
let _this = this;
|
||||
console.log("分享图片路径url", _this.str);
|
||||
wx.downloadFile({
|
||||
url: _this.str,
|
||||
success: (res) => {
|
||||
wx.showShareImageMenu({
|
||||
path: res.tempFilePath,
|
||||
});
|
||||
console.log(res.tempFilePath);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log("错误信息", err);
|
||||
},
|
||||
});
|
||||
},
|
||||
// 发送给朋友
|
||||
onShareAppMessage(res) {
|
||||
// 此处的distSource为分享者的部分信息,需要传递给其他人
|
||||
return {
|
||||
title: "#wagoo",
|
||||
// path:'/pages/home/home'
|
||||
};
|
||||
},
|
||||
|
||||
//分享到朋友圈
|
||||
onShareTimeline(res) {
|
||||
return {
|
||||
title: "#wagoo",
|
||||
// path:'/pages/home/home'
|
||||
};
|
||||
},
|
||||
|
||||
shareBklink() {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
});
|
||||
},
|
||||
shareLeft() {
|
||||
let _this = this;
|
||||
console.log("分享图片路径url", _this.str);
|
||||
wx.downloadFile({
|
||||
url: _this.str,
|
||||
success: (res) => {
|
||||
wx.showShareImageMenu({
|
||||
path: res.tempFilePath,
|
||||
});
|
||||
console.log(res.tempFilePath);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log("错误信息", err);
|
||||
},
|
||||
});
|
||||
// console.log(111);
|
||||
// this.shareDog = true;
|
||||
},
|
||||
shareRight() {
|
||||
uni.getSetting({
|
||||
success(res) {
|
||||
if (res.authSetting["scope.writePhotosAlbum"]) {
|
||||
// 已授权,直接保存图片
|
||||
uni.downloadFile({
|
||||
url: "https://activity.wagoo.live/generate -images.png",
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: "保存成功",
|
||||
duration: 2000,
|
||||
});
|
||||
},
|
||||
fail: function () {
|
||||
uni.showToast({
|
||||
title: "保存失败,请稍后重试",
|
||||
icon: "none",
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (res.authSetting["scope.writePhotosAlbum"] === false) {
|
||||
// 用户已拒绝授权,提示用户授权
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "您未授权保存图片到相册,是否前往设置页面进行授权?",
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
uni.openSetting({
|
||||
success: function (res) {
|
||||
if (res.authSetting["scope.writePhotosAlbum"]) {
|
||||
uni.downloadFile({
|
||||
url: "https://activity.wagoo.live/home_service_head.png",
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: "保存成功",
|
||||
duration: 2000,
|
||||
});
|
||||
},
|
||||
fail: function () {
|
||||
uni.showToast({
|
||||
title: "保存失败,请稍后重试",
|
||||
icon: "none",
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (res.cancel) {
|
||||
uni.showToast({
|
||||
title: "您取消了授权",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// 用户第一次调用,调用授权接口
|
||||
uni.authorize({
|
||||
scope: "scope.writePhotosAlbum",
|
||||
success() {
|
||||
uni.downloadFile({
|
||||
url: "https://activity.wagoo.live/home_service_head.png",
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: "保存成功",
|
||||
duration: 2000,
|
||||
});
|
||||
},
|
||||
fail: function () {
|
||||
uni.showToast({
|
||||
title: "保存失败,请稍后重试",
|
||||
icon: "none",
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
fail() {
|
||||
uni.showToast({
|
||||
title: "授权失败,请稍后重试",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
shareBottom() {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
});
|
||||
},
|
||||
jumpTo(url) {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.share-img {
|
||||
.nav-container {
|
||||
background: #fff;
|
||||
height: 180rpx;
|
||||
position: relative;
|
||||
.nav-title {
|
||||
font-size: 14px;
|
||||
padding-top: 42rpx;
|
||||
// font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #000;
|
||||
height: 100%;
|
||||
}
|
||||
.sk {
|
||||
position: absolute;
|
||||
left: 13rpx;
|
||||
top: 92rpx;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
// border: 1px solid #000;
|
||||
.right-arrow {
|
||||
position: absolute;
|
||||
left: 18rpx;
|
||||
top: 24rpx;
|
||||
width: 8.5px;
|
||||
height: 14.21px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.diamond {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.share-layer {
|
||||
position: relative;
|
||||
// height: 325rpx;
|
||||
.share-cat {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.left {
|
||||
width: 100rpx;
|
||||
height: 156rpx;
|
||||
border-radius: 50%;
|
||||
// border: 1px solid #000;
|
||||
position: absolute;
|
||||
left: 225rpx;
|
||||
bottom: 45rpx;
|
||||
}
|
||||
.right {
|
||||
width: 100rpx;
|
||||
height: 156rpx;
|
||||
border-radius: 50%;
|
||||
// border: 1px solid #000;
|
||||
position: absolute;
|
||||
right: 225rpx;
|
||||
bottom: 45rpx;
|
||||
}
|
||||
.bottom {
|
||||
width: 100%;
|
||||
height: 107rpx;
|
||||
// border: 1px solid #000;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -105rpx;
|
||||
}
|
||||
}
|
||||
.share-bg {
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
|
||||
.share-img {
|
||||
margin: 204rpx auto;
|
||||
display: block;
|
||||
|
||||
width: 261px;
|
||||
height: 464.23px;
|
||||
}
|
||||
.share-bottom {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 77rpx;
|
||||
width: 698rpx;
|
||||
height: 153px;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
// flex-direction: column;
|
||||
// align-items: center;
|
||||
padding: 0 26rpx 26rpx 26rpx;
|
||||
// gap: 33px;
|
||||
.share-friend {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200rpx;
|
||||
width: 160rpx;
|
||||
.diamond {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
.f {
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 12px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #ffffff;
|
||||
margin-top: 50rpx;
|
||||
}
|
||||
}
|
||||
.close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
left: 346rpx;
|
||||
bottom: 5rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
146
src/pages/client/recharge/use-details.vue
Normal file
@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<view class="rech-details">
|
||||
<view class="rech-layer">
|
||||
<view class="rech-amount">
|
||||
<text class="c" v-if="objNew.third_party_sn">充值金额</text>
|
||||
<text class="c" v-else-if="objNew.transaction_sn">消费金额</text>
|
||||
<view class="rech-add">
|
||||
<text v-if="objNew.third_party_sn" class="m">+</text>
|
||||
<text v-else-if="objNew.transaction_sn" class="m">-</text>
|
||||
<text class="m">{{objNew.amount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rech-bill">
|
||||
<view class="rech-row">
|
||||
<text class="c">交易描述</text>
|
||||
<text class="m">{{objNew.description}}</text>
|
||||
</view>
|
||||
<view v-if="!objNew.third_party_sn" class="rech-row">
|
||||
<text class="c">订单id</text>
|
||||
<text class="m">{{objNew.id}}</text>
|
||||
</view>
|
||||
<view v-if="!objNew.third_party_sn" class="rech-row">
|
||||
<text class="c">业务类型</text>
|
||||
<text class="m">{{objNew.related_business_type}}</text>
|
||||
</view>
|
||||
<view class="rech-row">
|
||||
<text class="c">交易流水号</text>
|
||||
<text class="m">{{objNew.business_sn}}</text>
|
||||
|
||||
</view>
|
||||
<view v-if="objNew.third_party_sn " class="rech-row">
|
||||
<text class="c">第三方流水号</text>
|
||||
<text class="m">{{objNew.third_party_sn}}</text>
|
||||
</view>
|
||||
<view v-if="objNew.third_party_sn " class="rech-row">
|
||||
<text class="c">充值方式</text>
|
||||
<text v-if="objNew.payment_method == 1" class="m">微信</text>
|
||||
<text v-else class="m">支付宝</text>
|
||||
</view>
|
||||
<view class="rech-row">
|
||||
<text class="c">交易时间</text>
|
||||
<text class="m">{{objNew.created_at}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
objNew:{}
|
||||
};
|
||||
},
|
||||
onLoad(options){
|
||||
this.objNew=JSON.parse(options.obj)
|
||||
|
||||
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rech-details {
|
||||
margin: 28rpx auto;
|
||||
width: 700rpx;
|
||||
height: 1234rpx;
|
||||
border-radius: 24rpx;
|
||||
background: #ffffff;
|
||||
.rech-layer {
|
||||
padding-top: 80rpx;
|
||||
.rech-amount {
|
||||
margin: 0 auto;
|
||||
width: 206rpx;
|
||||
height: 120rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
gap: 24rpx;
|
||||
z-index: 0;
|
||||
.c {
|
||||
font-family: PingFangSC;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
line-height: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
letter-spacing: normal;
|
||||
color: #333333;
|
||||
}
|
||||
.rech-add{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.m {
|
||||
font-family: PingFangSC;
|
||||
font-size: 48rpx;
|
||||
font-weight: 500;
|
||||
line-height: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
letter-spacing: normal;
|
||||
color: #272426;
|
||||
}
|
||||
}
|
||||
.rech-bill {
|
||||
height: 460rpx;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
padding: 60rpx 24rpx;
|
||||
gap: 40rpx;
|
||||
z-index: 1;
|
||||
.rech-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.c {
|
||||
display: block;
|
||||
width: 180rpx;
|
||||
font-family: PingFangSC;
|
||||
font-size: 30rpx;
|
||||
font-weight: normal;
|
||||
line-height: 30rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9b939a;
|
||||
}
|
||||
.m {
|
||||
font-family: PingFangSC;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
line-height: 30rpx;
|
||||
text-align: right;
|
||||
letter-spacing: normal;
|
||||
color: #272427;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
367
src/pages/client/recharge/use-list.vue
Normal file
@ -0,0 +1,367 @@
|
||||
<template>
|
||||
<view class="carDate">
|
||||
<view class="layer">
|
||||
<view class="filter-data">
|
||||
<view class="x1"
|
||||
>{{ dateStringData }}
|
||||
</view>
|
||||
<xp-picker :yearRange=[1950,null] :history="true" mode="ym" style="color:#8f9090" @confirm="confirm">选择时间</xp-picker>
|
||||
<!-- <xp-picker ref="picker"
|
||||
mode="ym"
|
||||
@confirm="confirm" /> -->
|
||||
<!-- <text v-else class="x" @tap="show">筛选日期 </text> -->
|
||||
<image v-if="!dateStringData" class="selected" src="@/static/images/sx.png" mode="widthFix" />
|
||||
</view>
|
||||
<view>
|
||||
<form-cell
|
||||
class="newData2"
|
||||
type="checkPicker"
|
||||
placeholderText="全部类型"
|
||||
:value="recordInfo.weight_id"
|
||||
:pickerData="weightListFilters"
|
||||
valueKey="weight_id"
|
||||
labelKey="weight_name"
|
||||
:noBorder="true"
|
||||
@onChange="(value) => onChange(value, 'weight_id')"
|
||||
/>
|
||||
<image class="selected2" src="@/static/images/sx.png" mode="widthFix" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="recharge-record" v-if="remarkList.length">
|
||||
<view
|
||||
v-for="(item, index) in remarkList"
|
||||
:key="index"
|
||||
class="card-layer"
|
||||
@click="jumpView(item)"
|
||||
>
|
||||
<view class="card">
|
||||
<text class="k">{{ item.description }}</text>
|
||||
<view class="rech-add">
|
||||
<text v-if="item.third_party_sn || item.description == '消费退款' " class="m">+</text>
|
||||
<text v-else-if="item.transaction_sn" class="m">-</text>
|
||||
<text class="k">{{ item.amount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="time">
|
||||
<view class="time-t">
|
||||
<text class="t">{{ item.created_at }}</text>
|
||||
</view>
|
||||
<text class="t">余额{{ item.current_balance }}元</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="occupation-map" v-else>
|
||||
<image
|
||||
src="https://activity.wagoo.live/no-information.png"
|
||||
mode="widthFix"
|
||||
class="service-head-img"
|
||||
/>
|
||||
<text class="z">暂无内容</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { rechargeWallet } from "../../../api/login";
|
||||
import FormCell from "@/components/FormCell.vue";
|
||||
import PopUpModal from "@/components/PopUpModal.vue";
|
||||
export default {
|
||||
components: {
|
||||
FormCell,
|
||||
PopUpModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// todayTimestamp: Number(new Date()),
|
||||
remarkList: [],
|
||||
weightListFilters: [
|
||||
{
|
||||
type: 0,
|
||||
weight_id: 0,
|
||||
weight_name: "全部类型",
|
||||
},
|
||||
{
|
||||
type: 1,
|
||||
weight_id: 1,
|
||||
weight_name: "充值",
|
||||
},
|
||||
{
|
||||
type: 2,
|
||||
weight_id: 2,
|
||||
weight_name: "消费",
|
||||
},
|
||||
],
|
||||
// value: Number(new Date()),
|
||||
|
||||
timestamp: "",
|
||||
dateStringData: "",
|
||||
dateStData:"",
|
||||
// maxDate: new Date(),
|
||||
recordInfo: {
|
||||
chongwu_id: null,
|
||||
name: "",
|
||||
weight_id: "",
|
||||
chongwu_pic_url: "",
|
||||
chongwu_pic: "",
|
||||
jianhuren_name: "",
|
||||
jianhuren_no: "",
|
||||
pinzhong_name: "",
|
||||
birthday: "",
|
||||
daojia_time: "",
|
||||
age: 1,
|
||||
pinzhong_id: "",
|
||||
maofa: "",
|
||||
user_id: "",
|
||||
wallet_id: "",
|
||||
data: "",
|
||||
typeL: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
this.user_id = options.user_id;
|
||||
this.wallet_id = options.wallet_id;
|
||||
this.myrechargeWallet();
|
||||
},
|
||||
methods: {
|
||||
// show() {
|
||||
// console.log(this.$refs.picker,'---')
|
||||
// // this.$refs.picker.show()
|
||||
// },
|
||||
confirm(e) {
|
||||
this.dateStringData = e.value
|
||||
// console.log(e,'--')
|
||||
// const date = new Date(e.value); // 时间戳通常是秒,需要乘以1000转换成毫秒
|
||||
// const year = date.getFullYear();
|
||||
// const month = date.getMonth() + 1; // 月份是从0开始的,所以需要+1
|
||||
// this.dateStData = `${year}-${month < 10 ? "0" + month : month}`;
|
||||
// console.log(this.dateStData,'---')
|
||||
this.myrechargeWallet();
|
||||
},
|
||||
myrechargeWallet() {
|
||||
const data = {
|
||||
wallet_id: +this.wallet_id,
|
||||
user_id: +this.user_id,
|
||||
type: this.type,
|
||||
date: this.dateStringData,
|
||||
};
|
||||
rechargeWallet(data).then((res) => {
|
||||
// console.log(res.data,'---')
|
||||
this.remarkList = res?.data;
|
||||
// console.log( this.remarkList,'---==')
|
||||
});
|
||||
},
|
||||
jumpView(item) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/recharge/use-details?obj=${JSON.stringify(item)}`,
|
||||
});
|
||||
console.log(item);
|
||||
},
|
||||
|
||||
jumpTo(url) {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
});
|
||||
},
|
||||
onChange(value, key) {
|
||||
|
||||
if (key == "birthday") {
|
||||
this.data = value;
|
||||
this.myrechargeWallet();
|
||||
} else if (key == "weight_id") {
|
||||
this.type = value;
|
||||
this.myrechargeWallet();
|
||||
}
|
||||
// console.log(key, "--");
|
||||
// console.log(value,'=-=-')
|
||||
if (key === "type" && this.recordInfo[key] !== value) {
|
||||
this.recordInfo.type = value;
|
||||
// this.getRecordTypeList();
|
||||
// this.getRecordWeightList();
|
||||
}
|
||||
if (key === "age" && this.recordInfo[key] !== value) {
|
||||
this.recordInfo.birthday = "";
|
||||
}
|
||||
// if (key === "birthday") {
|
||||
// this.recordInfo.age = Math.ceil(
|
||||
// moment(moment().format("YYYY-MM")).diff(
|
||||
// moment(moment(value).format("YYYY-MM")),
|
||||
// "years",
|
||||
// true
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
this.recordInfo[key] = value;
|
||||
this.$forceUpdate();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.carDate {
|
||||
|
||||
// ::v-deep .form-cell{
|
||||
// justify-content: flex-start;
|
||||
// }
|
||||
.layer {
|
||||
position: relative;
|
||||
height: 80rpx;
|
||||
background: #fff;
|
||||
margin-top: 6rpx;
|
||||
.filter-data {
|
||||
position: absolute;
|
||||
top: 22rpx;
|
||||
left: 50rpx;
|
||||
width: 300rpx;
|
||||
::v-deep .xp-h-full{
|
||||
color: #8f9090;
|
||||
}
|
||||
.x {
|
||||
color: #8f9090;
|
||||
}
|
||||
.x1{
|
||||
margin: 0 0 0 14rpx;
|
||||
color: #8f9090;
|
||||
position: absolute;
|
||||
left: 115rpx;
|
||||
top: 3rpx;
|
||||
}
|
||||
|
||||
.selected {
|
||||
position: absolute;
|
||||
top: 18rpx;
|
||||
left: 123rpx;
|
||||
width: 6px;
|
||||
height: 3.5px;
|
||||
}
|
||||
}
|
||||
|
||||
.selected2 {
|
||||
|
||||
position: absolute;
|
||||
top: 38rpx;
|
||||
right: 50rpx;
|
||||
width: 6px;
|
||||
height: 3.5px;
|
||||
}
|
||||
.newData {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
background: #fff;
|
||||
margin-top: 5rpx;
|
||||
::v-deep {
|
||||
.form-cell-wrapper .form-cell {
|
||||
width: 190rpx;
|
||||
border: none;
|
||||
.flex-center {
|
||||
flex: none;
|
||||
}
|
||||
.form-arrow {
|
||||
display: none;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.newData2 {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
// background: #fff;
|
||||
margin-top: 5rpx;
|
||||
::v-deep {
|
||||
.form-cell-wrapper .form-cell {
|
||||
width: 180rpx;
|
||||
border: none;
|
||||
position: absolute;
|
||||
right: 58rpx;
|
||||
top: -3rpx;
|
||||
.flex-center {
|
||||
flex: none;
|
||||
}
|
||||
.form-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recharge-record {
|
||||
margin: 20rpx auto;
|
||||
width: 654rpx;
|
||||
height: 100%;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// align-items: center;
|
||||
padding: 28rpx;
|
||||
gap: 32rpx;
|
||||
border-radius: 30rpx;
|
||||
background: #ffffff;
|
||||
.card-layer {
|
||||
height: 106rpx;
|
||||
border-bottom: 1px solid #ececec;
|
||||
// display: flex;
|
||||
// position: relative;
|
||||
z-index: 2;
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
.card {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
// margin-top: 30rpx;
|
||||
.k {
|
||||
font-family: PingFangSC;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 28rpx;
|
||||
letter-spacing: normal;
|
||||
color: #3d3d3d;
|
||||
}
|
||||
}
|
||||
.time {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 20rpx 0 20rpx;
|
||||
.time-t {
|
||||
display: flex;
|
||||
.t {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9b939a;
|
||||
}
|
||||
.i {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9b939a;
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
}
|
||||
.t {
|
||||
font-family: PingFangSC;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
line-height: 24rpx;
|
||||
letter-spacing: normal;
|
||||
color: #9b939a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.occupation-map {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
175
src/pages/client/recharge/view-score.vue
Normal file
@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<view class="score">
|
||||
<text class="health">健康记录分析</text>
|
||||
<view class="view-files">
|
||||
<image class="selected" :src="chongwu_pic_url" mode="widthFix" />
|
||||
<text class="name"> {{name}}综合评分: </text>
|
||||
<view class="fraction"> <text class="p">{{remarkList.total}}</text><text class="f">分</text></view>
|
||||
</view>
|
||||
<view class="charts-box">
|
||||
<qiun-data-charts
|
||||
type="radar"
|
||||
:opts="opts"
|
||||
:chartData="remarkList"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data(){
|
||||
return{
|
||||
chongwu_id:'',
|
||||
userId:'',
|
||||
chartData: {},
|
||||
remarkList:{},
|
||||
name:'',
|
||||
//您可以通过修改 config-ucharts.js 文件中下标为 ['radar'] 的节点来配置全局默认参数,如都是默认参数,此处可以不传 opts 。实际应用过程中 opts 只需传入与全局默认参数中不一致的【某一个属性】即可实现同类型的图表显示不同的样式,达到页面简洁的需求。
|
||||
opts: {
|
||||
color: ["#1890FF","#91CB74","#FAC858","#EE6666","#73C0DE","#3CA272","#FC8452","#9A60B4","#ea7ccc"],
|
||||
padding: [5,5,5,5],
|
||||
dataLabel: false,
|
||||
enableScroll: false,
|
||||
legend: {
|
||||
show: true,
|
||||
position: "bottom",
|
||||
lineHeight: 25
|
||||
},
|
||||
extra: {
|
||||
radar: {
|
||||
gridType: "circle",
|
||||
gridColor: "#CCCCCC",
|
||||
gridCount: 3,
|
||||
opacity: 0.2,
|
||||
max: 100,
|
||||
labelShow: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
onLoad(options){
|
||||
// console.log(options,'---')
|
||||
this.chongwu_id = options.chongwu_id
|
||||
this.userId = options.userId
|
||||
this.name = options.name
|
||||
this.mypetradarChart()
|
||||
},
|
||||
methods: {
|
||||
mypetradarChart() {
|
||||
this.remarkList = {};
|
||||
},
|
||||
// getServerData(){
|
||||
// //模拟从服务器获取数据时的延时
|
||||
// setTimeout(() => {
|
||||
// //模拟服务器返回数据,如果数据格式和标准格式不同,需自行按下面的格式拼接
|
||||
// let res = {
|
||||
// categories: ["洗护","标题","健康","日常","咨询","清洁"],
|
||||
// series: [
|
||||
// {
|
||||
// name: "日常记录",
|
||||
// data: [30,50,74,38,87,64]
|
||||
// },
|
||||
// // {
|
||||
// // name: "成交量2",
|
||||
// // data: [90,21,46,35,83,69]
|
||||
// // }
|
||||
// ]
|
||||
// };
|
||||
// this.chartData = JSON.parse(JSON.stringify(res));
|
||||
// }, 500);
|
||||
// }
|
||||
},
|
||||
// onReady() {
|
||||
// this.mypetradarChart();
|
||||
// },
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.score{
|
||||
width: 351px;
|
||||
height: 468.5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* 自动布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 24rpx 0px;
|
||||
gap: 24rpx;
|
||||
border-radius: 24rpx;
|
||||
background: #FFFFFF;
|
||||
margin: 20rpx auto;
|
||||
.charts-box {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
.health{
|
||||
font-family: PingFangSC;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 28rpx;
|
||||
letter-spacing: normal;
|
||||
color: #272427;
|
||||
}
|
||||
.view-files{
|
||||
width: 100%;
|
||||
padding-left: 50rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.selected{
|
||||
width:88rpx;
|
||||
height: 88rpx;
|
||||
z-index: 0;
|
||||
border-radius:50%;
|
||||
}
|
||||
.name{
|
||||
font-family: PingFang SC;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
line-height: 32rpx;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
color: #272427;
|
||||
margin-left:20rpx;
|
||||
}
|
||||
.fraction{
|
||||
display: flex;
|
||||
justify-content: flex-end; /* 如果需要,也可以将内容靠右对齐 */
|
||||
align-items: flex-end;
|
||||
.p{
|
||||
// margin-left:6rpx;
|
||||
font-family: PingFang SC;
|
||||
font-size: 48rpx;
|
||||
font-weight: normal;
|
||||
line-height: 48rpx;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 主色1 */
|
||||
color: #FE019B;
|
||||
}
|
||||
.f{
|
||||
margin-bottom:6rpx;
|
||||
font-family: PingFangSC;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
line-height: 12px;
|
||||
text-align: center;
|
||||
letter-spacing: normal;
|
||||
/* 主色1 */
|
||||
color: #FE019B;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
284
src/pages/client/record/components/BreedModal.vue
Normal file
@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<view class="breed-modal-container">
|
||||
<view class="modal-mask" @click="close"></view>
|
||||
<view class="model-content">
|
||||
<view class="flex-row-start modal-title">
|
||||
<view class="title-icon"></view>
|
||||
<view class="app-fc-main PingFangSC-Bold fs-36 title-name">
|
||||
宠物品种
|
||||
</view>
|
||||
<image
|
||||
class="title-icon"
|
||||
src="@/static/images/close_icon.png"
|
||||
@click="close"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="breed-list">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
:scroll-into-view="`item_${breedIndex}`"
|
||||
class="breed-list-wrapper"
|
||||
@scroll="onScroll"
|
||||
>
|
||||
<view
|
||||
class="flex-row-start breed-item"
|
||||
v-for="(breed, i) in data"
|
||||
:key="breed.id"
|
||||
:id="`item_${i}`"
|
||||
@click="save(breed)"
|
||||
>
|
||||
<image class="breed-icon" :src="breed.image_url" />
|
||||
<text
|
||||
class="fs-32 SourceHanSansCN-Regular app-text-ellipse breed-name"
|
||||
>
|
||||
{{ breed.name }}
|
||||
</text>
|
||||
<view class="split-line"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="breed-bottom"></view>
|
||||
|
||||
<view class="flex-column-start letters-list">
|
||||
<view
|
||||
class="flex-center letters-item"
|
||||
v-for="(letter, i) in lettersList"
|
||||
:key="i"
|
||||
@click="setPostion(letter)"
|
||||
>
|
||||
<view
|
||||
class="flex-center fs-20 letters-item-text"
|
||||
:class="letter === curTab ? 'active' : ''"
|
||||
>
|
||||
{{ letter }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import appConfig from "../../../../constants/app.config";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String | Number,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
appConfig,
|
||||
curTab: "",
|
||||
curBreedId: "",
|
||||
indicatorStyle: `height: 60px;border: 0;`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
lettersList() {
|
||||
const letters = [];
|
||||
for (let i = 65; i <= 90; i++) {
|
||||
letters.push(String.fromCharCode(i));
|
||||
}
|
||||
return letters;
|
||||
},
|
||||
breedIndex() {
|
||||
const index = this.data.findIndex(
|
||||
(v) => v.id === this.curBreedId
|
||||
);
|
||||
return index < 0 ? 0 : index;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(val) {
|
||||
const breed = this.data.find((v) => v.id === val);
|
||||
this.curBreedId = breed?.id || this.data?.[0]?.id;
|
||||
this.curTab = (breed?.alpha_code || this.data?.[0]?.alpha_code || "").toUpperCase();
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
data(list) {
|
||||
if (list.length) {
|
||||
this.curBreedId = list?.[0]?.id;
|
||||
this.curTab = list?.[0]?.alpha_code?.toUpperCase();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit("close");
|
||||
},
|
||||
save(breed) {
|
||||
this.curBreedId = breed.id;
|
||||
this.curTab = (breed.alpha_code || "").toUpperCase();
|
||||
this.$emit("confirm", this.curBreedId);
|
||||
},
|
||||
onScroll(e) {
|
||||
const top = e.detail.scrollTop;
|
||||
const itemHeight = 44 + 10 * 2;
|
||||
const index = Math.floor(top / itemHeight);
|
||||
const breed = this.data[index];
|
||||
this.curTab = (breed.alpha_code || "").toUpperCase();
|
||||
},
|
||||
setPostion(letter) {
|
||||
this.curTab = letter;
|
||||
const index = this.data.findIndex(
|
||||
(v) => (v?.alpha_code || "").toUpperCase() === letter
|
||||
);
|
||||
this.curBreedId = this.data?.[index]?.id;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.breed-modal-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
|
||||
.modal-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.model-content {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100vw;
|
||||
height: 80vh;
|
||||
overflow: hidden;
|
||||
padding-bottom: 20rpx;
|
||||
background: #fff;
|
||||
border-top-left-radius: 20rpx;
|
||||
border-top-right-radius: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
box-sizing: border-box;
|
||||
|
||||
.modal-title {
|
||||
padding: 50rpx 52rpx;
|
||||
.title-icon {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
}
|
||||
|
||||
.title-name {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.breed-list {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
height: calc(80vh - 180rpx); // 添加固定高度,减去标题和底部的高度
|
||||
|
||||
.breed-list-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.breed-item {
|
||||
padding: 10px 52rpx;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
.breed-icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 44px;
|
||||
}
|
||||
|
||||
.breed-name {
|
||||
flex: 1;
|
||||
margin: 0 20rpx;
|
||||
color: #726e71;
|
||||
}
|
||||
|
||||
.split-line {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 52rpx;
|
||||
right: 52rpx;
|
||||
height: 1px;
|
||||
background: #f3f4f7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breed-bottom {
|
||||
height: 60rpx;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 630rpx;
|
||||
height: 90rpx;
|
||||
border-radius: 90rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 45rpx 45rpx 45rpx 45rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.letters-list {
|
||||
position: absolute;
|
||||
top: 108rpx;
|
||||
right: 20rpx;
|
||||
height: 80vh;
|
||||
width: 60rpx;
|
||||
z-index: 2;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
.letters-item {
|
||||
height: 34rpx;
|
||||
|
||||
.letters-item-text {
|
||||
text-align: center;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
color: #3e4055;
|
||||
|
||||
&.active {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
border-radius: 24rpx;
|
||||
background: $app_color_main;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
98
src/pages/client/record/components/RecordItem.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<uni-swipe-action :key="data.id" class="record-item-container">
|
||||
<uni-swipe-action-item
|
||||
:right-options="{}"
|
||||
:auto-close="false"
|
||||
@change="change()"
|
||||
@click="toDetails()"
|
||||
>
|
||||
<view class="flex-row-start pet-item">
|
||||
<image
|
||||
class="pet-icon"
|
||||
:src="data.avatar"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="fs-32 app-fc-main text-multi-ellipse pet-name">
|
||||
{{ data.name }}
|
||||
</text>
|
||||
<text class="fs-28 SourceHanSansCN-Regular pet-edit" @click="toDetails">
|
||||
编辑
|
||||
</text>
|
||||
</view>
|
||||
<view slot="right" class="flex-center pet-right">
|
||||
<image
|
||||
class="pet-delete"
|
||||
src="@/static/images/mine_delete.png"
|
||||
@click.stop="deleteClick"
|
||||
/>
|
||||
</view>
|
||||
</uni-swipe-action-item>
|
||||
</uni-swipe-action>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import appConfig from "@/constants/app.config";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
appConfig,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
toDetails() {
|
||||
this.$emit("toDetails", this.data);
|
||||
},
|
||||
deleteClick() {
|
||||
this.$emit("deleteClick", this.data);
|
||||
},
|
||||
change() {
|
||||
this.$emit("change", this.data);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.record-item-container {
|
||||
.pet-item {
|
||||
padding: 44rpx 40rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 30rpx;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
.pet-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 80rpx;
|
||||
}
|
||||
|
||||
.pet-edit {
|
||||
color: $app_fc_mark;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pet-name {
|
||||
margin: 0 24rpx;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.pet-right {
|
||||
padding: 0 0 0 32rpx;
|
||||
.pet-delete {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
747
src/pages/client/record/edit.vue
Normal file
@ -0,0 +1,747 @@
|
||||
<template>
|
||||
<view class="record-edit">
|
||||
<view class="record-edit-content" :class="{ 'disable-scroll': showBreedModal }">
|
||||
<view class="tips">请完善您的宠物信息</view>
|
||||
<!-- <view class="flex-row-center avator-wrapper">
|
||||
<button
|
||||
class="avator-inner"
|
||||
open-type="chooseAvatar"
|
||||
@chooseavatar="changeAvator"
|
||||
>
|
||||
<image
|
||||
class="avator-ic"
|
||||
src="https://activity.wagoo.live/zxj.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
|
||||
<image
|
||||
v-if="recordInfo.chongwu_pic_url"
|
||||
class="avator-icon"
|
||||
:src="recordInfo.chongwu_pic_url"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<image
|
||||
v-else
|
||||
class="avator-icon"
|
||||
src="https://activity.wagoo.live/wagoodeft.png"
|
||||
/>
|
||||
</button>
|
||||
</view> -->
|
||||
|
||||
<view class="edit-form">
|
||||
<form-cell title="宠物头像" type="custom" :showRightArrow="false">
|
||||
<view class="uploadImgWrapper" slot="right">
|
||||
<button class="upload-btn" open-type="chooseAvatar" @chooseavatar="changeAvator">
|
||||
<image v-if="recordInfo.avatar" class="uploaded-img" :src="recordInfo.avatar"
|
||||
mode="aspectFill" />
|
||||
<image v-else :src="`${imgPrefix}record-cameraImg.png`" class="img" />
|
||||
</button>
|
||||
</view>
|
||||
</form-cell>
|
||||
<form-cell :isRequired='true' title="宠物姓名" placeholderText="点击输入姓名" :value="recordInfo.name"
|
||||
:showRightArrow="false" @onChange="(value) => onChange(value, 'name')" />
|
||||
<view>
|
||||
<form-cell :title="'宠物类别'" type="custom" :showRightArrow="false" :isRequired='true'>
|
||||
<view class="flex-row-end edit-sex" slot="right">
|
||||
<view class="flex-row-center opt-item opt-item-type"
|
||||
:class="[recordInfo.type === 1 ? 'active' : '', id ? 'disabled' : '']"
|
||||
@click="!id && onChange(1, 'type')">
|
||||
<image class="icon"
|
||||
:src="recordInfo.type === 1 ? `${imgPrefix}record-selectedDog.png` : `${imgPrefix}record-dogImg.png`" />
|
||||
<text class="text">狗</text>
|
||||
</view>
|
||||
<view class="flex-row-center opt-item opt-item-type"
|
||||
:class="[recordInfo.type === 2 ? 'active' : '', id ? 'disabled' : '']"
|
||||
@click="!id && onChange(2, 'type')">
|
||||
<image class="icon"
|
||||
:src="recordInfo.type === 2 ? `${imgPrefix}record-selectedCat.png` : `${imgPrefix}record-carImg.png`" />
|
||||
<text class="text">猫</text>
|
||||
</view>
|
||||
</view>
|
||||
</form-cell>
|
||||
|
||||
<form-cell type="custom" :title="'宠物品种'" placeholderText="选择宠物品种" :showRightArrow="!id"
|
||||
:isRequired='true' @clickAction="!id && (showBreedModal = true)">
|
||||
<view class="flex-row-end edit-sex" slot="right">
|
||||
<text class="fs-26" v-if="breedName">{{ breedName }}</text>
|
||||
<text class="fs-26 place-text" v-else>选择宠物品种</text>
|
||||
</view>
|
||||
</form-cell>
|
||||
|
||||
<form-cell :title="'宠物性别'" type="custom" :showRightArrow="false" :isRequired='true'>
|
||||
<view class="flex-row-end edit-sex" slot="right">
|
||||
<view class="opt-item"
|
||||
:class="[recordInfo.gender === 'male' ? 'active' : '', id ? 'disabled' : '']"
|
||||
@click="!id && (recordInfo.gender = 'male')">
|
||||
<image class="icon"
|
||||
:src="recordInfo.gender === 'male' ? `${imgPrefix}whiteMale.png` : `${imgPrefix}record-maleImg.png`" />
|
||||
</view>
|
||||
<view class="opt-item"
|
||||
:class="[recordInfo.gender === 'female' ? 'active' : '', id ? 'disabled' : '']"
|
||||
style="margin-left: 32rpx;" @click="!id && (recordInfo.gender = 'female')">
|
||||
<image class="icon"
|
||||
:src="recordInfo.gender === 'female' ? `${imgPrefix}whiteFemale.png` : `${imgPrefix}record-femaleImg.png`" />
|
||||
</view>
|
||||
</view>
|
||||
</form-cell>
|
||||
<form-cell :title="'是否绝育'" type="custom" :showRightArrow="false" :isRequired='true'>
|
||||
<view class="flex-row-end edit-sex" slot="right">
|
||||
<view class="flex-center fs-28 app-fc-main SourceHanSansCN-Medium opt-item"
|
||||
:class="[recordInfo.is_neutered == 1 ? 'active' : '', id ? 'disabled' : '']"
|
||||
@click="!id && (recordInfo.is_neutered = '1')">
|
||||
是
|
||||
</view>
|
||||
<view class="flex-center fs-28 app-fc-main SourceHanSansCN-Medium opt-item"
|
||||
:class="[recordInfo.is_neutered == 2 ? 'active' : '', id ? 'disabled' : '']"
|
||||
@click="!id && (recordInfo.is_neutered = '2')">
|
||||
否
|
||||
</view>
|
||||
</view>
|
||||
</form-cell>
|
||||
<form-cell :title="'出生年月'" type="checkDate" placeholderText="选择出生日期" :value="recordInfo.birthday"
|
||||
:defaultDate="recordInfo.birthday || ''" :disabled="!!id"
|
||||
@onChange="(value) => onChange(value, 'birthday')" :isRequired='true' />
|
||||
<form-cell :title="'宠物年龄'" type="checkPicker" placeholderText="选择宠物年龄" :value="recordInfo.age"
|
||||
:pickerData="ageList" valueKey="value" labelKey="name" :disabled="!!id"
|
||||
@onChange="(value) => onChange(value, 'age')" :isRequired='true' />
|
||||
|
||||
<form-cell v-if="recordInfo.type == 2" :title="'宠物毛发'" type="custom" :showRightArrow="false"
|
||||
:isRequired='true'>
|
||||
<view class="flex-row-end edit-sex" slot="right">
|
||||
<view class="flex-center fs-28 app-fc-main SourceHanSansCN-Medium opt-item"
|
||||
:class="[recordInfo.hair == 1 ? 'active' : '', id ? 'disabled' : '']"
|
||||
@click="!id && changeHair(1)">
|
||||
长毛
|
||||
</view>
|
||||
<view class="flex-center fs-28 app-fc-main SourceHanSansCN-Medium opt-item"
|
||||
:class="[recordInfo.hair == 2 ? 'active' : '', id ? 'disabled' : '']"
|
||||
@click="!id && changeHair(2)">
|
||||
短毛
|
||||
</view>
|
||||
</view>
|
||||
</form-cell>
|
||||
<form-cell :isRequired='true' :title="'宠物体重'" type="checkPicker" placeholderText="选择宠物体重"
|
||||
:value="recordInfo.weight_id" :pickerData="weightList" valueKey="id" labelKey="weight_name"
|
||||
:noBorder="true" :disabled="!!id" @onChange="(value) => onChange(value, 'weight_id')" />
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="place-view"></view>
|
||||
</view>
|
||||
|
||||
<view class="flex-row-center edit-bottom">
|
||||
<!-- <view
|
||||
v-if="id"
|
||||
class="flex-center PingFangSC-Semibold fs-30 confirm-btn delete"
|
||||
@click="showModal = true"
|
||||
>
|
||||
删除
|
||||
</view> -->
|
||||
<view class="flex-center PingFangSC-Semibold fs-30 confirm-btn" @click="save">
|
||||
保存
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<pop-up-modal v-if="showModal" content="确定要删除该档案信息吗?" @confirm="deleteRecord" @cancel="showModal = false" />
|
||||
|
||||
<!-- 品种modal -->
|
||||
<BreedModal v-if="showBreedModal" :data="breedList" :value="recordInfo.breed_id" @confirm="onBreedChange"
|
||||
@close="showBreedModal = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from "moment";
|
||||
import {
|
||||
delRecord,
|
||||
editRecord,
|
||||
updatePet,
|
||||
getRecordInfo,
|
||||
getRecordTypeList,
|
||||
getRecordWeightList,
|
||||
} from "../../../api/record";
|
||||
import appConfig from "../../../constants/app.config";
|
||||
import {
|
||||
PET_IS_STERILIZE_YES,
|
||||
PET_SEX_MALE,
|
||||
PET_TYPE_DOG,
|
||||
} from "@/constants/app.business";
|
||||
import {
|
||||
imgPrefix
|
||||
} from "@/utils/common.js";
|
||||
import {
|
||||
uploadImageToOSS_PUT
|
||||
} from "@/utils/oss";
|
||||
|
||||
import FormCell from "@/components/FormCell.vue";
|
||||
import PopUpModal from "@/components/PopUpModal.vue";
|
||||
import BreedModal from "./components/BreedModal.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FormCell,
|
||||
PopUpModal,
|
||||
BreedModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: "",
|
||||
recordInfo: {
|
||||
id: null,
|
||||
name: "",
|
||||
weight_id: "",
|
||||
avatar: "",
|
||||
chongwu_pic: "",
|
||||
// jianhuren_name: "",
|
||||
jianhuren_no: "",
|
||||
type: PET_TYPE_DOG, // 1狗 2猫
|
||||
gender: "male", // 性别 "male" 公 "female" 母
|
||||
birthday: "",
|
||||
age: 1,
|
||||
is_neutered: PET_IS_STERILIZE_YES, // 是否绝育 1.是 2.否
|
||||
breed_id: "",
|
||||
hair: 1,
|
||||
},
|
||||
breedList: [],
|
||||
weightList: [],
|
||||
showModal: false,
|
||||
showBreedModal: false,
|
||||
appConfig,
|
||||
imgPrefix,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
ageList() {
|
||||
return Array.from({
|
||||
length: 20
|
||||
}, (v, k) => ({
|
||||
value: k + 1,
|
||||
name: `${k + 1}岁`,
|
||||
}));
|
||||
},
|
||||
breedName() {
|
||||
return (
|
||||
this.breedList.find(
|
||||
(v) => v.id === this.recordInfo.breed_id
|
||||
)?.name || ""
|
||||
);
|
||||
},
|
||||
userInfo() {
|
||||
return this.$store.state?.user?.userInfo || {};
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
// console.log()
|
||||
console.log(options, '--?')
|
||||
|
||||
const {
|
||||
id,
|
||||
type
|
||||
} = options;
|
||||
// this.recordInfo.jianhuren_name = this.userInfo.nick_name;
|
||||
// if (type) {
|
||||
// this.recordInfo.type = parseInt(type);
|
||||
// }
|
||||
if (id) {
|
||||
this.id = id;
|
||||
this.recordInfo.id = id;
|
||||
this.getInfo(id);
|
||||
} else {
|
||||
this.getRecordTypeList();
|
||||
this.getRecordWeightList();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
moment,
|
||||
// 更新头像
|
||||
async changeAvator(e) {
|
||||
const {
|
||||
avatarUrl
|
||||
} = e.detail;
|
||||
|
||||
uni.showLoading({
|
||||
title: "上传中..."
|
||||
});
|
||||
|
||||
try {
|
||||
const { url, objectKey } = await uploadImageToOSS_PUT(avatarUrl);
|
||||
console.log(url, objectKey, 'url, objectKey');
|
||||
this.recordInfo.avatar = url; // 完整URL
|
||||
this.recordInfo.chongwu_pic = objectKey; // 对象键
|
||||
this.$forceUpdate();
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "上传成功",
|
||||
icon: "success",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('头像上传失败:', error);
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: error?.message || "头像上传失败",
|
||||
icon: "none"
|
||||
});
|
||||
}
|
||||
},
|
||||
onChange(value, key) {
|
||||
if (key === "type" && this.recordInfo[key] !== value) {
|
||||
this.recordInfo.type = value;
|
||||
this.getRecordTypeList();
|
||||
this.getRecordWeightList();
|
||||
this.recordInfo.breed_id = null;
|
||||
this.recordInfo.weight_id = null;
|
||||
}
|
||||
// 移除年龄和生日的自动关联逻辑,让用户独立选择
|
||||
this.recordInfo[key] = value;
|
||||
this.$forceUpdate();
|
||||
},
|
||||
changeHair(value) {
|
||||
this.recordInfo.hair = value;
|
||||
this.recordInfo.weight_id = "";
|
||||
this.getRecordWeightList();
|
||||
this.$forceUpdate();
|
||||
},
|
||||
onBreedChange(val) {
|
||||
this.recordInfo.breed_id = val;
|
||||
this.showBreedModal = false;
|
||||
},
|
||||
getRecordTypeList() {
|
||||
const param = {
|
||||
species: this.recordInfo.type
|
||||
};
|
||||
getRecordTypeList(param).then((res) => {
|
||||
this.breedList = res.data;
|
||||
});
|
||||
},
|
||||
getRecordWeightList() {
|
||||
const param = {
|
||||
species: this.recordInfo.type,
|
||||
hair: this.recordInfo.type === PET_TYPE_DOG ? 1 : this.recordInfo.hair
|
||||
};
|
||||
|
||||
getRecordWeightList(param).then((res) => {
|
||||
this.weightList = res.data;
|
||||
});
|
||||
},
|
||||
getInfo(id) {
|
||||
getRecordInfo(id, this.userInfo.userID).then((res) => {
|
||||
// 合并数据,确保保留所有字段
|
||||
const data = { ...res.data };
|
||||
// 如果后端返回的是 false 或 2,转换为 2(否)
|
||||
if (data.is_neutered === true || data.is_neutered === 1) {
|
||||
data.is_neutered = 1;
|
||||
} else if (data.is_neutered === false || data.is_neutered === 2) {
|
||||
data.is_neutered = 2;
|
||||
}
|
||||
|
||||
this.recordInfo = data;
|
||||
|
||||
// 使用 $nextTick 确保数据更新后再调用
|
||||
this.$nextTick(() => {
|
||||
this.getRecordTypeList();
|
||||
this.getRecordWeightList();
|
||||
});
|
||||
});
|
||||
},
|
||||
save() {
|
||||
// 必填字段校验
|
||||
if (!this.recordInfo.name || this.recordInfo.name.trim() === '') {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请输入宠物姓名'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.recordInfo.type) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择宠物类别'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.recordInfo.breed_id) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择宠物品种'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.recordInfo.gender) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择宠物性别'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.recordInfo.is_neutered) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择是否绝育'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.recordInfo.birthday) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择出生年月'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.recordInfo.age) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择宠物年龄'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.recordInfo.weight_id) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择宠物体重'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是猫,需要校验毛发
|
||||
if (this.recordInfo.type === 2 && !this.recordInfo.hair) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择宠物毛发'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
user_id: this.userInfo.userID,
|
||||
name: this.recordInfo.name,
|
||||
type: this.recordInfo.type, // 1狗 2猫
|
||||
breed_id: this.recordInfo.breed_id,
|
||||
gender: this.recordInfo.gender, // "male" 或 "female"
|
||||
weight_id: this.recordInfo.weight_id,
|
||||
hair: this.recordInfo.hair ? this.recordInfo.hair : 1,
|
||||
age: this.recordInfo.age || 1,
|
||||
birthday: this.recordInfo.birthday,
|
||||
is_neutered: this.recordInfo.is_neutered == 1 ? true : false,
|
||||
};
|
||||
// 如果有 id,说明是更新,添加 id 字段
|
||||
const isEdit = !!this.recordInfo.id;
|
||||
if (isEdit) {
|
||||
params.pet_id = this.recordInfo.id;
|
||||
}
|
||||
|
||||
// 处理头像字段
|
||||
if (isEdit) {
|
||||
// 编辑模式:如果 chongwu_pic 存在,使用它;否则使用 avatar
|
||||
if (this.recordInfo.chongwu_pic) {
|
||||
params.avatar = this.recordInfo.chongwu_pic;
|
||||
} else if (this.recordInfo.avatar) {
|
||||
params.avatar = this.recordInfo.avatar;
|
||||
}
|
||||
} else {
|
||||
// 添加模式:直接使用 chongwu_pic
|
||||
if (this.recordInfo.chongwu_pic) {
|
||||
params.avatar = this.recordInfo.chongwu_pic;
|
||||
}
|
||||
}
|
||||
uni.showLoading({
|
||||
title: "保存中..."
|
||||
});
|
||||
// 根据是否有 id 判断是新增还是更新
|
||||
const savePromise = this.recordInfo.id ? updatePet(params) : editRecord(params);
|
||||
savePromise
|
||||
.then((r) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: "保存成功"
|
||||
});
|
||||
// 通过 eventChannel 触发事件(如果页面通过 events 参数传递)
|
||||
const eventChannel = this.getOpenerEventChannel();
|
||||
if (eventChannel) {
|
||||
eventChannel.emit("addPetSuccess");
|
||||
}
|
||||
// 触发全局刷新事件
|
||||
uni.$emit('refreshPetList');
|
||||
uni.navigateBack();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: err || "保存失败",
|
||||
});
|
||||
});
|
||||
},
|
||||
deleteRecord() {
|
||||
this.showModal = false;
|
||||
uni.showLoading({
|
||||
title: "删除中..."
|
||||
});
|
||||
delRecord(this.recordInfo.id)
|
||||
.then(() => {
|
||||
uni.navigateBack();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: err || "删除失败",
|
||||
icon: "none",
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.record-edit {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #ffecf3;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
.record-edit-content {
|
||||
height: 100%;
|
||||
|
||||
&.disable-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tips {
|
||||
padding: 20rpx 0rpx;
|
||||
text-align: center;
|
||||
color: #3D3D3D;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.avator-wrapper {
|
||||
padding: 40rpx 0 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.avator-inner {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.avator-ic {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 121rpx;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
|
||||
.avator-icon {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: 180rpx;
|
||||
}
|
||||
|
||||
.avator-upload {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.edit-form {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 24rpx;
|
||||
width: calc(100vw - 40rpx);
|
||||
margin: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.default {
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.uploadImgWrapper {
|
||||
background-color: #a2a2a2;
|
||||
border-radius: 218px;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
margin-left: auto;
|
||||
|
||||
.upload-btn {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 32rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
|
||||
.uploaded-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opt-item {
|
||||
width: 104rpx;
|
||||
height: 56rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 8rpx;
|
||||
margin-left: 32rpx;
|
||||
color: #3D3D3D;
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #fff;
|
||||
background: #FF19A0;
|
||||
|
||||
&.man {
|
||||
background: #FF19A0;
|
||||
}
|
||||
|
||||
&.women {
|
||||
background: #FF19A0;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.opt-item-type {
|
||||
width: auto;
|
||||
padding: 0rpx 8rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 8rpx;
|
||||
margin-left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: 32rpx;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-left: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #3D3D3D;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: #FF19A0;
|
||||
color: #fff;
|
||||
|
||||
.text {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-sex {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.place-text {
|
||||
color: #8f9090;
|
||||
}
|
||||
|
||||
.place-view {
|
||||
width: 100%;
|
||||
height: 186rpx;
|
||||
}
|
||||
|
||||
.edit-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 32rpx 32rpx 0px 0px;
|
||||
padding-top: 12rpx;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
box-sizing: border-box;
|
||||
|
||||
.confirm-btn {
|
||||
width: calc(100% - 48rpx);
|
||||
margin: auto;
|
||||
margin-bottom: 24rpx;
|
||||
height: auto;
|
||||
border-radius: 100px;
|
||||
background: #FF19A0;
|
||||
color: #fff;
|
||||
padding: 32rpx 0rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
|
||||
&.delete {
|
||||
background: #e7e2e7;
|
||||
color: #9b939a;
|
||||
margin-right: 70rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
src/pages/client/record/static/pet_cat.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/pages/client/record/static/pet_dog.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/pages/client/record/static/record_avator_update.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
240
src/pages/client/remark/compare.vue
Normal file
@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<view class="remark-compare-container">
|
||||
<view class="remark-compare-content">
|
||||
<view class="fs-32 app-fc-main compare-title">
|
||||
洗护前
|
||||
<text class="fs-20 compare-tips">(最多可上传9张)</text>
|
||||
</view>
|
||||
<uni-file-picker
|
||||
limit="9"
|
||||
:imageStyles="{
|
||||
width: '196rpx',
|
||||
height: '196rpx',
|
||||
'border-radius': '20rpx',
|
||||
}"
|
||||
:del-icon="true"
|
||||
:auto-upload="false"
|
||||
@select="onImgChange"
|
||||
@delete="onImgDelete"
|
||||
>
|
||||
<view class="flex-center add-wrapper">
|
||||
<image class="add-icon" src="./static/add_icon.png" />
|
||||
<text class="fs-24 add-text">添加图片</text>
|
||||
</view>
|
||||
</uni-file-picker>
|
||||
|
||||
<view class="fs-32 app-fc-main compare-title compare-after">
|
||||
洗护后
|
||||
<text class="fs-20 compare-tips">(最多可上传9张)</text>
|
||||
</view>
|
||||
<uni-file-picker
|
||||
limit="9"
|
||||
:imageStyles="{
|
||||
width: '196rpx',
|
||||
height: '196rpx',
|
||||
'border-radius': '20rpx',
|
||||
}"
|
||||
:del-icon="true"
|
||||
:auto-upload="false"
|
||||
@select="onAfterImgChange"
|
||||
@delete="onAfterImgDelete"
|
||||
>
|
||||
<view class="flex-center add-wrapper">
|
||||
<image class="add-icon" src="./static/add_icon.png" />
|
||||
<text class="fs-24 add-text">添加图片</text>
|
||||
</view>
|
||||
</uni-file-picker>
|
||||
</view>
|
||||
<view class="place-view"></view>
|
||||
<view class="flex-center confirm-footer">
|
||||
<view class="flex-center fs-30 app-fc-white confirm-btn" @click="submit">
|
||||
上传
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import appConfig from "../../../constants/app.config";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
beforeImgList: [],
|
||||
afterImgList: [],
|
||||
orderId: "",
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
this.orderId = options?.orderId || "";
|
||||
},
|
||||
methods: {
|
||||
onImgChange(e) {
|
||||
uni.showLoading({
|
||||
title: "图片上传中...",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
});
|
||||
const promiseList = [];
|
||||
(e?.tempFilePaths || []).map((path, i) => {
|
||||
promiseList.push(this.uploadFile(path, "before", i));
|
||||
});
|
||||
Promise.all(promiseList).finally(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
},
|
||||
onImgDelete(e) {
|
||||
this.beforeImgList.splice(e.index, 1);
|
||||
},
|
||||
onAfterImgChange(e) {
|
||||
const promiseList = [];
|
||||
(e?.tempFilePaths || []).map((path, i) => {
|
||||
promiseList.push(this.uploadFile(path, "after", i));
|
||||
});
|
||||
Promise.all(promiseList).finally(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
},
|
||||
onAfterImgDelete(e) {
|
||||
this.afterImgList.splice(e.index, 1);
|
||||
},
|
||||
uploadFile(url, type, index) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: appConfig.apiBaseUrl + "/app/upload/upload_file",
|
||||
filePath: url,
|
||||
name: "image",
|
||||
formData: {
|
||||
file_name: "app",
|
||||
pic_name: +new Date() + '_' + index,
|
||||
},
|
||||
header: { token: uni.getStorageSync("token") },
|
||||
success: (res) => {
|
||||
const data = JSON.parse(res.data);
|
||||
const { code, msg, info } = data;
|
||||
const errCode = String(code);
|
||||
|
||||
if (errCode === "0" || errCode === "200") {
|
||||
if (type === "after") {
|
||||
this.afterImgList.push({
|
||||
fullUrl: info?.[0]?.urlname,
|
||||
url: info?.[0]?.filename,
|
||||
});
|
||||
} else {
|
||||
this.beforeImgList.push({
|
||||
fullUrl: info?.[0]?.urlname,
|
||||
url: info?.[0]?.filename,
|
||||
});
|
||||
}
|
||||
this.$forceUpdate();
|
||||
resolve();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: msg || "上传失败",
|
||||
icon: "none",
|
||||
});
|
||||
reject();
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: "上传失败",
|
||||
icon: "none",
|
||||
});
|
||||
reject();
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
submit() {
|
||||
if (!this.beforeImgList.length) {
|
||||
uni.showToast({
|
||||
title: "请上传洗护前图片",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!this.afterImgList.length) {
|
||||
uni.showToast({
|
||||
title: "请上传洗护后图片",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: "接口开发中",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.remark-compare-container {
|
||||
height: 100%;
|
||||
padding: 20rpx 32rpx 0;
|
||||
padding: 20rpx 32rpx constant(safe-area-inset-bottom);
|
||||
padding: 20rpx 32rpx env(safe-area-inset-bottom);
|
||||
background: #f7f8fa;
|
||||
|
||||
.remark-compare-content {
|
||||
background: #fff;
|
||||
border-radius: 30rpx;
|
||||
padding: 52rpx 28rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.compare-tips {
|
||||
color: #726e71;
|
||||
}
|
||||
|
||||
.compare-title {
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.add-wrapper {
|
||||
background: #f8f9fa;
|
||||
width: 196rpx;
|
||||
height: 196rpx;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.add-icon {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.add-text {
|
||||
color: #d2d4dd;
|
||||
}
|
||||
}
|
||||
|
||||
.compare-after {
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
background: #fff;
|
||||
padding: 20rpx 0 0;
|
||||
padding: 20rpx 0 constant(safe-area-inset-bottom);
|
||||
padding: 20rpx 0 env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.place-view {
|
||||
height: 166rpx;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 630rpx;
|
||||
height: 90rpx;
|
||||
border-radius: 90rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 45rpx 45rpx 45rpx 45rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
42
src/pages/client/remark/details.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<view class="remark-details">
|
||||
<remark-item :data="data" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RemarkItem from "@/components/goods/RemarkItem.vue";
|
||||
import { getRemarkDetails } from "../../../api/shop";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data: {},
|
||||
remarkId: "",
|
||||
};
|
||||
},
|
||||
components: {
|
||||
RemarkItem,
|
||||
},
|
||||
onLoad(options) {
|
||||
const { remarkId = "" } = options;
|
||||
this.remarkId = remarkId;
|
||||
this.getRemarkDetails();
|
||||
},
|
||||
methods: {
|
||||
getRemarkDetails() {
|
||||
getRemarkDetails(this.remarkId).then((res) => {
|
||||
this.data = res?.info;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.remark-details {
|
||||
padding: 20rpx 0;
|
||||
box-sizing: border-box;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
</style>
|
||||
82
src/pages/client/remark/list.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<view class="remark-list-container">
|
||||
<list-page-temp
|
||||
class="remark-list-inner"
|
||||
:getDataPromise="getRemarkList"
|
||||
:reloadFlag="reloadFlag"
|
||||
:requestData="requestData"
|
||||
>
|
||||
<template v-slot:item="{ data }">
|
||||
<remark-item :data="data" />
|
||||
</template>
|
||||
<view slot="bottom" class="place-view"></view>
|
||||
</list-page-temp>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RemarkItem from "@/components/goods/RemarkItem.vue";
|
||||
import ListPageTemp from "@/components/ListPageTemp.vue";
|
||||
import { getRemarkList } from "@/api/shop";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// 1.商品订单 2.服务券订单
|
||||
type: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
guanlian_id: {
|
||||
type: String | Number,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
components: {
|
||||
RemarkItem,
|
||||
ListPageTemp,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
reloadFlag: 0,
|
||||
remarkList: [],
|
||||
requestData: {
|
||||
type: this.type,
|
||||
guanlian_id: this.guanlian_id,
|
||||
},
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.reloadFlag = Math.random();
|
||||
},
|
||||
onLoad(options) {
|
||||
const { type, shopId } = options;
|
||||
if (type) {
|
||||
this.requestData.type = type;
|
||||
}
|
||||
if (shopId) {
|
||||
this.requestData.guanlian_id = shopId;
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
getRemarkList,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.remark-list-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #fbf8fc;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
padding-top: 20rpx;
|
||||
|
||||
.remark-list-inner {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
src/pages/client/remark/static/add_icon.png
Normal file
|
After Width: | Height: | Size: 489 B |
527
src/pages/client/screening/details.vue
Normal file
@ -0,0 +1,527 @@
|
||||
<template>
|
||||
<view class="screening-details">
|
||||
<uni-load-more v-if="isLoading" status="loading" :show-text="false" />
|
||||
<scroll-view v-if="!isLoading" class="scroll-view" scroll-y>
|
||||
<!-- 宠物信息 -->
|
||||
<view class="section pet-info-section">
|
||||
<view class="section-title pet-info-title">宠物信息</view>
|
||||
<view class="info-list">
|
||||
<view class="info-item">
|
||||
<text class="info-label">报告编号</text>
|
||||
<text class="info-value">{{ reportData.order_no || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">宠物昵称</text>
|
||||
<text class="info-value">{{ petInfo.name || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">种类</text>
|
||||
<text class="info-value">{{ getSpeciesName(petInfo.species) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">品种</text>
|
||||
<text class="info-value">{{ petInfo.breed_name || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">年龄</text>
|
||||
<text class="info-value">{{ `${petInfo.age}岁` || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">性别</text>
|
||||
<text class="info-value">{{ petInfo.gender || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">宠物体重</text>
|
||||
<text class="info-value">{{ petInfo.weight_name || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">报告时间</text>
|
||||
<text class="info-value">{{ reportData.report_time || '--' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 检测图片 -->
|
||||
<view class="section detection-images-section">
|
||||
<view class="section-title detection-title">预检结果</view>
|
||||
<view class="detection-list">
|
||||
<view v-for="(item, index) in reportData.precheck_json" :key="index" class="detection-item">
|
||||
<view class="detection-item-header">
|
||||
<text class="detection-item-title">
|
||||
{{ index + 1 }}. {{ getPartLabel(item.name) }}{{ item.radioSelected === 'abnormal' ? '不正常' : '正常' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="detection-item-content" v-if="getDetectionItemContent(item)">
|
||||
<text class="detection-text">{{ getDetectionItemContent(item) }}</text>
|
||||
</view>
|
||||
<view class="detection-images">
|
||||
<image v-for="(img, imgIndex) in (item.beforeImgList || [])" :key="img"
|
||||
class="detection-image" :src="img" mode="aspectFill"
|
||||
@click="previewImage(img, item.beforeImgList)" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 部位诊断 -->
|
||||
<!-- <view class="section part-diagnosis-section">
|
||||
<view class="section-title part-diagnosis-title">部位诊断</view>
|
||||
<view class="diagnosis-list">
|
||||
<view
|
||||
v-for="(item, index) in partDiagnosisList"
|
||||
:key="index"
|
||||
class="diagnosis-item"
|
||||
>
|
||||
<view class="diagnosis-item-title">{{ item.part_name }}</view>
|
||||
<view class="diagnosis-item-content">
|
||||
<text class="diagnosis-text">{{ item.content || '正文正文...' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 综合诊断 -->
|
||||
<!-- <view class="section comprehensive-diagnosis-section">
|
||||
<view class="section-title comprehensive-title">综合诊断</view>
|
||||
<view class="diagnosis-content">
|
||||
<text class="diagnosis-text">{{ comprehensiveDiagnosis || '正文正文...' }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 诊断依据 -->
|
||||
<!-- <view class="section diagnosis-basis-section">
|
||||
<view class="section-title diagnosis-basis-title">诊断依据</view>
|
||||
<view class="diagnosis-content">
|
||||
<text class="diagnosis-text">{{ diagnosisBasis || '正文正文...' }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 治疗建议 -->
|
||||
<!-- <view class="section treatment-section">
|
||||
<view class="section-title treatment-title">治疗建议</view>
|
||||
<view class="diagnosis-content">
|
||||
<text class="diagnosis-text">{{ treatmentSuggestions || '正文正文...' }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 护理建议 -->
|
||||
<!-- <view class="section care-section">
|
||||
<view class="section-title care-title">护理建议</view>
|
||||
<view class="diagnosis-content">
|
||||
<text class="diagnosis-text">{{ careSuggestions || '正文正文...' }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 康复建议 -->
|
||||
<!-- <view class="section recovery-section">
|
||||
<view class="section-title recovery-title">康复建议</view>
|
||||
<view class="diagnosis-content">
|
||||
<text class="diagnosis-text">{{ recoverySuggestions || '正文正文...' }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 健康管理 -->
|
||||
<!-- <view class="section health-management-section">
|
||||
<view class="section-title health-management-title">健康管理</view>
|
||||
<view class="diagnosis-content">
|
||||
<text class="diagnosis-text">{{ healthManagement || '正文正文...' }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 底部安全区域 -->
|
||||
<view class="bottom-safe-area"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPetPrecheckRecords } from "@/api/order";
|
||||
import moment from "moment/moment";
|
||||
|
||||
export default {
|
||||
name: 'ScreeningDetails',
|
||||
data() {
|
||||
return {
|
||||
orderId: '',
|
||||
petId: '',
|
||||
isLoading: true,
|
||||
reportData: {},
|
||||
detectionList: [],
|
||||
partDiagnosisList: [],
|
||||
comprehensiveDiagnosis: '',
|
||||
diagnosisBasis: '',
|
||||
treatmentSuggestions: '',
|
||||
careSuggestions: '',
|
||||
recoverySuggestions: '',
|
||||
healthManagement: '',
|
||||
petInfo: {},
|
||||
state: [
|
||||
{ value: 'eye', label: '眼睛' },
|
||||
{ value: 'ear', label: '耳朵' },
|
||||
{ value: 'nose', label: '鼻子' },
|
||||
{ value: 'cavity', label: '口腔' },
|
||||
{ value: 'skin', label: '皮肤' },
|
||||
{ value: 'joint', label: '关节' },
|
||||
{ value: 'anus', label: '肛门' },
|
||||
{ value: 'genitals', label: '生殖器' },
|
||||
{ value: 'nail', label: '指甲' },
|
||||
{ value: 'whole', label: '整体' }
|
||||
],
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options.orderId) {
|
||||
this.orderId = options.orderId;
|
||||
}
|
||||
if (options.petId) {
|
||||
this.petId = options.petId;
|
||||
}
|
||||
if (this.orderId && this.petId) {
|
||||
this.getReportDetails();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '参数错误',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getReportDetails() {
|
||||
this.isLoading = true;
|
||||
getPetPrecheckRecords({
|
||||
pet_id: this.petId.split(',').map(Number),
|
||||
order_id: parseInt(this.orderId)
|
||||
})
|
||||
.then((res) => {
|
||||
this.isLoading = false;
|
||||
if (res.code === 0 && res.data) {
|
||||
const raw = res.data;
|
||||
let data = raw;
|
||||
if (Array.isArray(raw) && raw.length > 0) {
|
||||
const petIdStr = String(this.petId);
|
||||
data = raw.find(function(item) {
|
||||
const pid = (item.list && item.list.pet_id) != null ? item.list.pet_id : (item.pet && item.pet.pet_id);
|
||||
return pid != null && String(pid) === petIdStr;
|
||||
});
|
||||
if (!data) data = raw[0];
|
||||
}
|
||||
if (data) this.processReportData(data);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res?.msg || '获取报告失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.isLoading = false;
|
||||
console.error('获取预检报告失败:', err);
|
||||
uni.showToast({
|
||||
title: err?.msg || err?.message || '获取报告失败',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
},
|
||||
processReportData(data) {
|
||||
const list = data.list;
|
||||
const reportItem = (Array.isArray(list) ? list[0] : list) || {};
|
||||
const pet = data.pet || {};
|
||||
|
||||
// 处理 submit_time,使用 moment 格式化
|
||||
let formattedTime = '--';
|
||||
if (reportItem.submit_time) {
|
||||
// 如果是时间戳(数字),使用 unix 转换
|
||||
if (typeof reportItem.submit_time === 'number') {
|
||||
formattedTime = moment.unix(reportItem.submit_time).format("YYYY-MM-DD HH:mm:ss");
|
||||
} else {
|
||||
// 如果是字符串,直接解析
|
||||
formattedTime = moment(reportItem.submit_time).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
}
|
||||
|
||||
this.reportData = {
|
||||
...reportItem,
|
||||
report_time: formattedTime
|
||||
};
|
||||
|
||||
this.petInfo = {
|
||||
...pet,
|
||||
gender: this.formatGender(pet.gender)
|
||||
};
|
||||
},
|
||||
formatGender(val) {
|
||||
if (!val) return '--';
|
||||
const v = String(val).toLowerCase();
|
||||
if (v === 'male') return '男生';
|
||||
if (v === 'female') return '女生';
|
||||
return val;
|
||||
},
|
||||
getSpeciesName(species) {
|
||||
if (species === 1) return '狗';
|
||||
if (species === 2) return '猫';
|
||||
return '--';
|
||||
},
|
||||
getPartLabel(name) {
|
||||
if (!name) return '--';
|
||||
const found = this.state.find(v => v.value === name);
|
||||
return found ? found.label : name;
|
||||
},
|
||||
// 拼接 selectList 的 name(以、隔开),最后拼接 inputContent
|
||||
getDetectionItemContent(item) {
|
||||
const names = (item.selectList || []).map(s => s && s.name).filter(Boolean).join('、');
|
||||
const input = (item.inputContent || '').trim();
|
||||
if (names && input) return names + '、' + input;
|
||||
if (names) return names;
|
||||
return input;
|
||||
},
|
||||
previewImage(current, urls) {
|
||||
uni.previewImage({
|
||||
current: current,
|
||||
urls: urls
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.screening-details {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
box-sizing: border-box;
|
||||
|
||||
.scroll-view {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.section {
|
||||
width: calc(100% - 40rpx);
|
||||
margin: 0 20rpx 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
|
||||
.section-title {
|
||||
width: 100%;
|
||||
padding: 16rpx 20rpx;
|
||||
font-size: 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.detection-title {
|
||||
background-color: #ffecd9;
|
||||
color: #FF7F00;
|
||||
}
|
||||
|
||||
.part-diagnosis-title {
|
||||
background-color: #e8f5e9;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.comprehensive-title {
|
||||
background-color: #e3f2fd;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.diagnosis-basis-title {
|
||||
background-color: #ffecf3;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.treatment-title {
|
||||
background-color: #e3f2fd;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.care-title {
|
||||
background-color: #ffecf3;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.recovery-title {
|
||||
background-color: #e3f2fd;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.health-management-title {
|
||||
background-color: #ffecf3;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
// 宠物信息部分
|
||||
.pet-info-section {
|
||||
margin: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
|
||||
.pet-info-title {
|
||||
background-color: #FFE7E8;
|
||||
color: #EC6D71;
|
||||
border-radius: 16rpx 16rpx 0 0;
|
||||
}
|
||||
|
||||
.info-list {
|
||||
padding: 0 32rpx 0;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10rpx 0;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 24rpx;
|
||||
color: #9B939A;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 24rpx;
|
||||
color: #272427;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检测图片部分
|
||||
.detection-images-section {
|
||||
.detection-list {
|
||||
padding: 0 32rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.detection-item {
|
||||
padding: 20rpx 0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.detection-item-header {
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.detection-item-title {
|
||||
font-size: 24rpx;
|
||||
color: #272427;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.detection-item-content {
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.detection-text {
|
||||
font-size: 24rpx;
|
||||
color: #272427;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
.detection-images {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.detection-image {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border-radius: 20rpx;
|
||||
background-color: #f5f5f5;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detection-image-placeholder {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border-radius: 8rpx;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
.placeholder-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 部位诊断部分
|
||||
.part-diagnosis-section {
|
||||
.diagnosis-list {
|
||||
padding: 0 32rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.diagnosis-item {
|
||||
padding: 32rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.diagnosis-item-title {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.diagnosis-item-content {
|
||||
.diagnosis-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 综合诊断和其他建议部分
|
||||
.comprehensive-diagnosis-section,
|
||||
.diagnosis-basis-section,
|
||||
.treatment-section,
|
||||
.care-section,
|
||||
.recovery-section,
|
||||
.health-management-section {
|
||||
.diagnosis-content {
|
||||
padding: 0 32rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.diagnosis-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-safe-area {
|
||||
width: 100%;
|
||||
height: constant(safe-area-inset-bottom);
|
||||
height: env(safe-area-inset-bottom);
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
223
src/pages/client/screening/pet-list.vue
Normal file
@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<view class="pet-list-page">
|
||||
<view class="record-list">
|
||||
<view
|
||||
class="record-item"
|
||||
v-for="(item, index) in petsList"
|
||||
:key="item.pet_id"
|
||||
@click="selectPet(item)"
|
||||
>
|
||||
<view class="record-content">
|
||||
<view class="pet-info">
|
||||
<text class="pet-name">{{ item.pet_name || item.name || '--' }}</text>
|
||||
<text class="pet-gender">{{ formatGender(item.sex || item.gender) }}</text>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<view class="order-info">
|
||||
<view class="info-row">
|
||||
<text class="label">订单编号</text>
|
||||
<text class="value">{{ item.order_no || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">车牌号</text>
|
||||
<text class="value">{{ item.license_plate || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">预约时间</text>
|
||||
<text class="value">{{ item.reservation_time || item.created_at || orderCreatedAt || '--' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="status-badge">
|
||||
<text class="status-text">预检</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ScreeningPetList',
|
||||
data() {
|
||||
return {
|
||||
orderId: '',
|
||||
orderCreatedAt: '',
|
||||
petsList: []
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
this.orderId = options.orderId || '';
|
||||
try {
|
||||
this.orderCreatedAt = options.created_at ? decodeURIComponent(options.created_at) : '';
|
||||
} catch (e) {
|
||||
this.orderCreatedAt = options.created_at || '';
|
||||
}
|
||||
try {
|
||||
let list = options.petsList ? JSON.parse(decodeURIComponent(options.petsList)) : [];
|
||||
console.log(list, 'list')
|
||||
if (!Array.isArray(list)) list = [];
|
||||
this.petsList = list.map(function(p) {
|
||||
if (typeof p === 'object' && p !== null) return p;
|
||||
return { pet_id: p, id: p, chongwu_id: p, pet_name: '宠物' };
|
||||
});
|
||||
} catch (e) {
|
||||
this.petsList = [];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatGender(val) {
|
||||
if (!val) return '--';
|
||||
const v = String(val).toLowerCase();
|
||||
if (v === 'male' || v === '男') return '男生';
|
||||
if (v === 'female' || v === '女') return '女生';
|
||||
return val;
|
||||
},
|
||||
getPetAvatar(pet) {
|
||||
const url = pet.chongwu_pic || pet.pet_avatar || pet.avatar || pet.pet_image_url;
|
||||
return url || 'https://activity.wagoo.live/record_avator.png';
|
||||
},
|
||||
selectPet(pet) {
|
||||
const petId = pet.pet_id || pet.id || pet.chongwu_id;
|
||||
if (!this.orderId || !petId) {
|
||||
uni.showToast({ title: '参数错误', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/screening/details?orderId=${this.orderId}&petId=${petId}`
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pet-list-page {
|
||||
min-height: 100vh;
|
||||
background: #f7f8fa;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.record-list {
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
padding: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 16rpx;
|
||||
|
||||
.record-item {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
overflow: hidden;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
position: relative;
|
||||
border: 2rpx solid #FF19A0;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
|
||||
.record-content {
|
||||
flex: 1;
|
||||
padding: 32rpx 88rpx 32rpx 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32rpx;
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
|
||||
.pet-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
flex-shrink: 0;
|
||||
width: 100rpx;
|
||||
|
||||
.pet-name {
|
||||
font-size: 24rpx;
|
||||
color: #272427;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.pet-gender {
|
||||
font-size: 24rpx;
|
||||
color: #9B939A;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 2rpx;
|
||||
height: 88rpx;
|
||||
background: #ececec;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.order-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
width: 100%;
|
||||
|
||||
.label {
|
||||
font-size: 24rpx;
|
||||
color: #9B939A;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 24rpx;
|
||||
color: #272427;
|
||||
text-align: right;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
width: 60rpx;
|
||||
background: #FF19A0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: upright;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
border-radius: 0 22rpx 22rpx 0;
|
||||
|
||||
.status-text {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
398
src/pages/client/service/details.vue
Normal file
@ -0,0 +1,398 @@
|
||||
<template>
|
||||
<view class="shop-details-container">
|
||||
<view class="banner-swiper" v-if="bannerList.length">
|
||||
<swiper
|
||||
class="swiper-wrapper"
|
||||
circular
|
||||
:indicator-dots="false"
|
||||
:autoplay="true"
|
||||
@change="bannerChange"
|
||||
>
|
||||
<swiper-item v-for="(banner, i) in bannerList" :key="i">
|
||||
<image class="swiper-img" :src="banner" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="flex-row-center dot-box">
|
||||
<view
|
||||
v-for="(banner, index) in bannerList"
|
||||
:key="index"
|
||||
class="dot-item"
|
||||
:class="{ active: bannerIndex === index }"
|
||||
></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="good-info">
|
||||
<view class="info-cell">
|
||||
<view class="flex-row-between">
|
||||
<view class="">
|
||||
<text class="app-fc-mark fs-24">¥</text>
|
||||
<text class="app-fc-mark fs-44">{{ details.price || 0 }}</text>
|
||||
<text class="fs-24 origin-price">
|
||||
¥{{ details.old_price || 0 }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="flex-row-end">
|
||||
<text class="fs-22 app-fc-cancel">
|
||||
已售 {{ details.xiaoliang || 0 }}件
|
||||
</text>
|
||||
<view class="fs-22 app-fc-cancel split-line">|</view>
|
||||
<text class="fs-22 app-fc-cancel">
|
||||
剩余{{ details.kucun || 0 }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="fs-36 app-font-bold app-fc-main good-name">
|
||||
{{ details.name || "" }}
|
||||
</view>
|
||||
<view class="flex-row-start">
|
||||
<text
|
||||
class="fs-20 sale-btn"
|
||||
v-for="(label, i) in labelList"
|
||||
:key="i"
|
||||
:class="[i > 0 ? 'sale-today app-fc-mark' : 'app-fc-white']"
|
||||
>
|
||||
{{ label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-cell">
|
||||
<view class="flex-row-start cell-inner">
|
||||
<text class="fs-26 postage-info">使用区间</text>
|
||||
<text class="fs-26 app-fc-main flex-1">
|
||||
{{ details.weight_name || "-" }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="flex-row-start cell-inner">
|
||||
<text class="fs-26 postage-info">有效时间</text>
|
||||
<text class="fs-26 app-fc-main flex-1">
|
||||
{{ details.youxiao_time || "-" }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="flex-row-start cell-inner" @click="showGuideModal = true">
|
||||
<text class="fs-26 postage-info">使用规则</text>
|
||||
<text class="fs-26 app-fc-main flex-1 app-text-ellipse">
|
||||
{{ details.desc || "-" }}
|
||||
</text>
|
||||
<image
|
||||
class="arrow-icon"
|
||||
:src="require('@/static/images/arrow_right_black.png')"
|
||||
mode="widthFix"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-cell" v-if="remarkList.length">
|
||||
<view class="width-fill flex-row-between">
|
||||
<text class="fs-28 postage-info">
|
||||
用户评价
|
||||
<text class="fs-24 app-fc-normal"> ({{ remarkCount }}) </text>
|
||||
</text>
|
||||
<view class="flex-row-end" @click="jumpToRemark">
|
||||
<text class="fs-26 app-fc-main">查看全部</text>
|
||||
<image
|
||||
class="arrow-icon"
|
||||
:src="require('@/static/images/arrow_right_black.png')"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="remark-list">
|
||||
<remark-item v-for="item in remarkList" :key="item.id" :data="item" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="fs-36 app-fc-normal rich-text-title">—— 服务详情 ——</view>
|
||||
<view class="rich-text-content" v-html="details.content"></view>
|
||||
<view class="place-view"></view>
|
||||
|
||||
<view class="flex-row-between details-bottom">
|
||||
<text class="fs-30 app-fc-main">
|
||||
¥<text class="fs-48 app-fc-main app-font-bold">{{ payPrice }}</text>
|
||||
</text>
|
||||
<view
|
||||
class="flex-center fs-30 app-fc-white app-font-bold opt-btn"
|
||||
@click="createOrder"
|
||||
>
|
||||
立即购买
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<use-guide-modal
|
||||
v-if="showGuideModal"
|
||||
title="使用规则"
|
||||
:content="details.desc"
|
||||
@ok="showGuideModal = false"
|
||||
/>
|
||||
|
||||
<success-modal
|
||||
v-if="showSuccessModal"
|
||||
title="购买成功"
|
||||
ok-text="立即预约"
|
||||
@ok="paySuccessAction"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getServiceCouponDetail } from "@/api/coupon";
|
||||
import { getRemarkList } from "../../../api/shop";
|
||||
import { ORDER_TYPE_PET_SERVICE } from '@/constants/app.business';
|
||||
import {
|
||||
serviceCouponCreateOrder,
|
||||
serviceCouponOrderPay,
|
||||
} from "../../../api/coupon";
|
||||
|
||||
import UseGuideModal from "@/components/coupon/UseGuideModal.vue";
|
||||
import SuccessModal from "@/components/SuccessModal";
|
||||
import RemarkItem from "@/components/goods/RemarkItem";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UseGuideModal,
|
||||
SuccessModal,
|
||||
RemarkItem
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
couponId: "",
|
||||
details: {},
|
||||
bannerList: [],
|
||||
bannerIndex: 0,
|
||||
remarkList: [],
|
||||
showGuideModal: false,
|
||||
showSuccessModal: false,
|
||||
remarkCount : 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
labelList() {
|
||||
return (this.details?.label || "").split(",").filter((v) => !!v);
|
||||
},
|
||||
payPrice() {
|
||||
return +this.details.price || 0;
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
this.couponId = options.id;
|
||||
this.getDetails();
|
||||
this.getRemarkListData();
|
||||
},
|
||||
methods: {
|
||||
// 获取服务券详情
|
||||
getDetails() {
|
||||
getServiceCouponDetail(this.couponId).then((res) => {
|
||||
this.details = res?.info || {};
|
||||
this.bannerList = (this.details.fuwuquan_pic || []).split(',').filter(v => !!v);
|
||||
});
|
||||
},
|
||||
// 获取评论列表
|
||||
getRemarkListData() {
|
||||
getRemarkList({ p: 1, num: 1, type: ORDER_TYPE_PET_SERVICE, guanlian_id: this.couponId }).then(
|
||||
(res) => {
|
||||
this.remarkList = res?.info || [];
|
||||
this.remarkCount = res?.count || 0;
|
||||
}
|
||||
);
|
||||
},
|
||||
// 轮播图
|
||||
bannerChange(e) {
|
||||
this.bannerIndex = e.detail.current;
|
||||
},
|
||||
stripTags(html = "") {
|
||||
return html.replace(/<[^>]+>/g, "");
|
||||
},
|
||||
jumpToRemark() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/remark/list?shopId=${this.couponId}&type=${ORDER_TYPE_PET_SERVICE}`,
|
||||
});
|
||||
},
|
||||
// 下单
|
||||
createOrder() {
|
||||
uni.showLoading({
|
||||
title: "处理中",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
});
|
||||
serviceCouponCreateOrder(this.couponId).then((res) => {
|
||||
this.pay(res?.info);
|
||||
});
|
||||
},
|
||||
// 支付
|
||||
pay(orderId) {
|
||||
serviceCouponOrderPay(orderId).then((res) => {
|
||||
const payData = res?.info?.pay_data || {};
|
||||
uni.requestPayment({
|
||||
provider: "wxpay",
|
||||
timeStamp: payData.timeStamp,
|
||||
nonceStr: payData.nonceStr,
|
||||
package: payData.package,
|
||||
signType: payData.signType,
|
||||
paySign: payData.sign,
|
||||
success: (res) => {
|
||||
uni.hideLoading();
|
||||
this.showSuccessModal = true;
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: err?.msg || "支付失败",
|
||||
icon: "none",
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
paySuccessAction(){
|
||||
this.showSuccessModal = false;
|
||||
uni.redirectTo({
|
||||
url: '/pageHome/reservation/index'
|
||||
})
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shop-details-container {
|
||||
height: 100%;
|
||||
background: #f7f8fa;
|
||||
|
||||
.banner-swiper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 750rpx;
|
||||
.swiper-wrapper {
|
||||
width: 100%;
|
||||
height: 750rpx;
|
||||
.swiper-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.dot-box {
|
||||
position: absolute;
|
||||
bottom: 30rpx;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
.dot-item {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 12rpx;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
margin-right: 12rpx;
|
||||
&.active {
|
||||
width: 28rpx;
|
||||
height: 12rpx;
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.good-info {
|
||||
margin: 24rpx 32rpx;
|
||||
|
||||
.info-cell {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 36rpx 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.split-line {
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cell-inner {
|
||||
padding: 12rpx 0;
|
||||
|
||||
.info-arrow-icon {
|
||||
width: 40rpx;
|
||||
height: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.origin-price {
|
||||
color: #726e71;
|
||||
text-decoration: line-through;
|
||||
margin-left: 18rpx;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
|
||||
.good-name {
|
||||
margin: 30rpx 0;
|
||||
}
|
||||
|
||||
.sale-btn {
|
||||
padding: 6rpx 12rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 4rpx;
|
||||
border: 1rpx solid $app_color_main;
|
||||
margin-right: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
&.sale-today {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.postage-info {
|
||||
color: #9b939a;
|
||||
margin-right: 32rpx;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
width: 30rpx;
|
||||
height: 18rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.remark-list {
|
||||
border-top: 2rpx solid #ebebeb;
|
||||
margin-top: 36rpx;
|
||||
::v-deep {
|
||||
.remark-item {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rich-text-title {
|
||||
margin: 32rpx 0 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rich-text-content {
|
||||
background: #fff;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.place-view {
|
||||
height: 190rpx;
|
||||
}
|
||||
|
||||
.details-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
padding-bottom: 20rpx;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
padding-top: 20rpx;
|
||||
padding-left: 40rpx;
|
||||
padding-right: 36rpx;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
|
||||
.opt-btn {
|
||||
width: 260rpx;
|
||||
height: 92rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 92rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
124
src/pages/client/shop/components/CouponModal.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<view class="flex-center coupon-modal">
|
||||
<image
|
||||
class="coupon-title-icon"
|
||||
src="'https://activity.wagoo.live/coupon_modal_title.png'"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<view class="coupon-modal-content">
|
||||
<view
|
||||
v-for="item in couponList"
|
||||
:key="item.coupon_id"
|
||||
class="flex-row-start coupon-item"
|
||||
>
|
||||
<view class="flex-row-center item-left">
|
||||
<text class="fs-60 app-font-bold app-fc-white">
|
||||
{{ item.card_money || 0 }}
|
||||
</text>
|
||||
<text class="fs-32 price-unit app-fc-white">元</text>
|
||||
</view>
|
||||
<view class="flex-column-start item-right">
|
||||
<text v-if="item.type === 1" class="fs-36 app-fc-main app-font-bold">
|
||||
{{ item.name }}
|
||||
</text>
|
||||
<text class="fs-24 app-fc-main app-font-bold coupon-text">
|
||||
{{ item.type === 1 ? "商品优惠券" : item.name }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<image
|
||||
class="get-icon"
|
||||
:src="require('../static/coupon_modal_get.png')"
|
||||
@click="$emit('getCoupon')"
|
||||
/>
|
||||
<image
|
||||
class="close-icon"
|
||||
:src="require('../static/coupon_modal_close.png')"
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
couponList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.coupon-modal {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99999;
|
||||
|
||||
.coupon-title-icon {
|
||||
width: 620rpx;
|
||||
height: 232rpx;
|
||||
padding-right: 30px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.coupon-modal-content {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
width: 596rpx;
|
||||
max-height: 520rpx;
|
||||
overflow-y: auto;
|
||||
padding: 82rpx 32rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
background: #FDC8DA;
|
||||
background-size: 100% 100%;
|
||||
border-radius: 40rpx;
|
||||
margin-top: -42rpx;
|
||||
|
||||
.coupon-item {
|
||||
background: #ffffff;
|
||||
border-radius: 30rpx 30rpx 30rpx 30rpx;
|
||||
height: 160rpx;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
.item-left {
|
||||
width: 208rpx;
|
||||
height: 100%;
|
||||
margin-right: 32rpx;
|
||||
background: #FD269C;
|
||||
border-radius: 30rpx 30rpx 30rpx 30rpx;
|
||||
}
|
||||
|
||||
.price-unit {
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
|
||||
.coupon-text {
|
||||
// color: #726e71;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.get-icon {
|
||||
width: 300rpx;
|
||||
height: 80rpx;
|
||||
margin: 52rpx 0;
|
||||
}
|
||||
.close-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
158
src/pages/client/shop/components/GoodItem.vue
Normal file
@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<view class="goods-item" @click.stop="jumpToDetails">
|
||||
<image class="goods-img" :src="data.product_pic" mode="aspectFill" />
|
||||
<view class=" fs-24 app-fc-main goods-name">
|
||||
{{ data.product_name || "" }}
|
||||
</view>
|
||||
<view class="flex-row-start label">
|
||||
<image class="hot-icon" :src="`${imgPrefix}mall-hot.png`"></image>
|
||||
<view class="fs-20 app-fc-main label-name">{{data.sales}}人买过</view>
|
||||
</view>
|
||||
<view class="flex-row-start" style="margin-top: 12rpx;">
|
||||
<text class="fs-28" style="color: #3D3D3D;">
|
||||
¥
|
||||
<text class="fs-28">{{
|
||||
data.prices[0].actual_price || 0
|
||||
}}</text>
|
||||
</text>
|
||||
<!-- <text class="fs-24 origin-price">
|
||||
¥{{ minPrice.price_shichang || 0 }}
|
||||
</text> -->
|
||||
</view>
|
||||
<!-- <text class="fs-20 good-salenum">已售{{ data.xiaoliang || 0 }}</text> -->
|
||||
<!-- <image class="add-cart-icon" src="@/static/images/cart-icon.png" @click.stop="$emit('addToCar', data)" /> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
imgPrefix
|
||||
} from '@/utils/common';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imgPrefix,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// minPrice() {
|
||||
// let minPrice = {};
|
||||
// let minPriceValue = 0;
|
||||
// this.data.price_list.map((v) => {
|
||||
// if (!minPriceValue || minPriceValue > +v.price) {
|
||||
// minPriceValue = +v.price;
|
||||
// minPrice = {
|
||||
// ...v
|
||||
// };
|
||||
// }
|
||||
// });
|
||||
// return minPrice;
|
||||
// },
|
||||
labelList() {
|
||||
return (this.data?.label || "").split(",").filter((v) => !!v);
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
jumpToDetails() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/shop/details?product_id=${this.data.product_id}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-item {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 22rpx;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
|
||||
.goods-img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: 320rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.goods-name {
|
||||
margin: 20rpx 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: #ffecf3;
|
||||
display: inline-flex;
|
||||
border-radius: 4rpx;
|
||||
|
||||
.hot-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
|
||||
.label-name {
|
||||
padding: 4rpx;
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
|
||||
.sale-btn {
|
||||
padding: 4rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 4rpx;
|
||||
border: 1rpx solid $app_color_main;
|
||||
margin-right: 10rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.sale-today {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.origin-price {
|
||||
color: #726e71;
|
||||
text-decoration: line-through;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.good-salenum {
|
||||
color: #9b939a;
|
||||
}
|
||||
|
||||
.add-cart-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
position: absolute;
|
||||
bottom: 26rpx;
|
||||
right: 20rpx;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
739
src/pages/client/shop/details.vue
Normal file
@ -0,0 +1,739 @@
|
||||
<template>
|
||||
<view class="shop-details-container">
|
||||
<view class="banner-swiper">
|
||||
<swiper class="swiper-wrapper" circular :indicator-dots="false" :autoplay="true" @change="bannerChange">
|
||||
<swiper-item v-for="(banner, i) in details.product_pic_json" :key="i">
|
||||
<image class="swiper-img" :src="banner.photos_image" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="flex-row-center dot-box">
|
||||
<view v-for="(banner, index) in bannerList" :key="index" class="dot-item"
|
||||
:class="{ active: bannerIndex === index }"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="good-info">
|
||||
<view class="info-cell">
|
||||
<view class="flex-row-start label">
|
||||
<image class="hot-icon" :src="`${imgPrefix}mall-hot.png`"></image>
|
||||
<view class="fs-20 app-fc-main label-name">{{details.sales}}人买过</view>
|
||||
</view>
|
||||
<view class="flex-row-between" style="margin-top: 20rpx">
|
||||
<view class="">
|
||||
<text class="app-fc-mark fs-28">¥</text>
|
||||
<text class="app-fc-mark fs-28">{{picList[0].actual_price || 0 }}</text>
|
||||
<!-- <text class="fs-24 origin-price">
|
||||
¥{{ minPrice.price_shichang || 0 }}
|
||||
</text> -->
|
||||
</view>
|
||||
<!-- <text class="fs-22 app-fc-cancel">
|
||||
已售 {{ details.xiaoliang || 0 }}件
|
||||
</text> -->
|
||||
</view>
|
||||
<view class="fs-29 good-name">
|
||||
{{ details.product_name || "" }}
|
||||
</view>
|
||||
<!-- <view class="flex-row-start">
|
||||
<text class="fs-20 sale-btn" v-for="(label, i) in labelList" :key="i"
|
||||
:class="[i > 0 ? 'sale-today app-fc-mark' : 'app-fc-white']">
|
||||
{{ label }}
|
||||
</text>
|
||||
</view> -->
|
||||
</view>
|
||||
<view class="info-cell shipping-info-cell">
|
||||
<view class="selected-row" @click="showSpecModal">
|
||||
<text class="selected-label">已选</text>
|
||||
<text class="selected-value">{{ selectedSpec || "请选择规格" }}</text>
|
||||
<image class="arrow-right-icon" :src="require('./static/arrow_right.png')" />
|
||||
</view>
|
||||
<view class="shipping-row">
|
||||
<text class="shipping-label">运费</text>
|
||||
<text class="shipping-value">{{ sliverFee ? `¥${sliverFee}` : "待下单时候确认" }}</text>
|
||||
</view>
|
||||
<view class="service-features">
|
||||
<view class="feature-item">
|
||||
<image :src="`${imgPrefix}mall-detailTips.png`" class="mallPng"></image>
|
||||
<!-- <view class="check-icon">✓</view> -->
|
||||
<text class="feature-text">快递发货</text>
|
||||
</view>
|
||||
<view class="feature-item" style="margin-left: 20rpx;">
|
||||
<!-- <view class="check-icon">✓</view> -->
|
||||
<image :src="`${imgPrefix}mall-detailTips.png`" class="mallPng"></image>
|
||||
<text class="feature-text">七天无理由退货</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-cell" v-if="remarkList.length">
|
||||
<view class="width-fill flex-row-between">
|
||||
<text class="fs-28 postage-info">
|
||||
用户评价
|
||||
<text class="fs-24 app-fc-normal">
|
||||
({{ details.pingjia_num }})
|
||||
</text>
|
||||
</text>
|
||||
<view class="flex-row-end" @click="jumpToRemark">
|
||||
<text class="fs-26 app-fc-main">查看全部</text>
|
||||
<image class="arrow-icon" :src="require('./static/arrow_right.png')" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="remark-list">
|
||||
<remark-item v-for="item in remarkList" :key="item.id" :data="item" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="fs-24 app-fc-normal rich-text-title">—— 商品详情 ——</view>
|
||||
<view class="rich-text-content" v-html="details.content"></view>
|
||||
<view class="place-view"></view>
|
||||
|
||||
<view class="flex-row-between details-bottom">
|
||||
<view class="flex-row-start">
|
||||
<view class="flex-center opt-item" @click="collectAction">
|
||||
<image class="opt-icon" :src="
|
||||
details.shoucang_id
|
||||
? `${imgPrefix}shopDetail-collect_done.png`
|
||||
: `${imgPrefix}shopDetail-collect.png`
|
||||
" mode="widthFix" />
|
||||
<text class="fs-20 app-fc-main">收藏</text>
|
||||
</view>
|
||||
<view class="flex-center opt-item service" @click="jumpToWeChat">
|
||||
<image class="opt-icon" :src="`${imgPrefix}shopDetail-contact.png`" mode="widthFix" />
|
||||
<text class="fs-20 app-fc-main">客服</text>
|
||||
</view>
|
||||
<view class="flex-center opt-item service cart-item-wrapper" @click="jumpToCart">
|
||||
<view class="cart-icon-wrapper">
|
||||
<image class="opt-icon" :src="`${imgPrefix}shopDetail-car.png`" mode="widthFix" />
|
||||
<view v-if="cartCount > 0" class="cart-badge">
|
||||
<text class="cart-badge-text">{{ cartCount > 99 ? '99+' : cartCount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="fs-20 app-fc-main">购物车</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row-start opt-btns">
|
||||
<view class="flex-center fs-30 app-fc-white opt-btn opt-btn-cart" @click="addToCart">
|
||||
加入购物车
|
||||
</view>
|
||||
<view class="flex-center fs-30 app-fc-white opt-btn" @click="goBuy">
|
||||
立即购买
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<add-goods-modal v-if="showModal" :optText="type === 'cart' ? '加入购物车' : '立即购买'" :data="details"
|
||||
@optAction="optAction" @change="(val) => (showModal = val)" />
|
||||
|
||||
<contact-modal v-if="showConcactModal" @close="showConcactModal = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
addCart,
|
||||
getGoodsDetail,
|
||||
collectShop,
|
||||
getCartList
|
||||
} from "@/api/shop";
|
||||
|
||||
import {
|
||||
imgPrefix
|
||||
} from '@/utils/common';
|
||||
|
||||
|
||||
import RemarkItem from "@/components/goods/RemarkItem.vue";
|
||||
import AddGoodsModal from "@/components/goods/AddGoodsModal.vue";
|
||||
import ContactModal from "@/components/ContactModal.vue";
|
||||
import {
|
||||
getRemarkList
|
||||
} from "../../../api/shop";
|
||||
import {
|
||||
jumpToWeChat,
|
||||
showLoginConfirmModal
|
||||
} from "@/utils/common";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
RemarkItem,
|
||||
AddGoodsModal,
|
||||
ContactModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imgPrefix,
|
||||
bannerIndex: 0,
|
||||
picList:[],
|
||||
bannerList: [],
|
||||
details: {},
|
||||
showModal: false,
|
||||
type: "", // cart/gobuy
|
||||
goodId: "",
|
||||
showConcactModal: false,
|
||||
remarkList: [],
|
||||
petOrderId: "",
|
||||
petOrderAddressId: "",
|
||||
selectedSpec: "", // 已选规格
|
||||
cartCount: 0, // 购物车商品数量
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
minPrice() {
|
||||
let minPrice = {};
|
||||
let minPriceValue = 0;
|
||||
(this.details?.price_list || []).map((v) => {
|
||||
if (!minPriceValue || minPriceValue > +v.price) {
|
||||
minPriceValue = +v.price;
|
||||
minPrice = {
|
||||
...v
|
||||
};
|
||||
}
|
||||
});
|
||||
return minPrice;
|
||||
},
|
||||
labelList() {
|
||||
return (this.details?.label || "").split(",").filter((v) => !!v);
|
||||
},
|
||||
sliverFee() {
|
||||
return +this.details?.yunfei || 0;
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
this.product_id = options.product_id
|
||||
// console.log(this.product_id ,'??')
|
||||
const {
|
||||
id,
|
||||
petOrderId = "",
|
||||
petOrderAddressId = ""
|
||||
} = options;
|
||||
this.goodId = id;
|
||||
this.petOrderId = petOrderId;
|
||||
this.petOrderAddressId = petOrderAddressId;
|
||||
this.getDetails(this.product_id);
|
||||
// this.getRemarkListData();
|
||||
this.getCartCount();
|
||||
},
|
||||
onShow() {
|
||||
// 页面显示时更新购物车数量
|
||||
this.getCartCount();
|
||||
},
|
||||
methods: {
|
||||
jumpToWeChat,
|
||||
// 跳转购物车
|
||||
jumpToCart() {
|
||||
uni.redirectTo({
|
||||
url: '/pages/client/cart/index'
|
||||
});
|
||||
},
|
||||
// 获取购物车数量
|
||||
getCartCount() {
|
||||
getCartList({
|
||||
p: 1,
|
||||
num: 999
|
||||
}).then((res) => {
|
||||
const cartList = res?.info || [];
|
||||
// 计算购物车商品总数量
|
||||
this.cartCount = cartList.reduce((total, item) => {
|
||||
return total + (item.number || 0);
|
||||
}, 0);
|
||||
}).catch(() => {
|
||||
this.cartCount = 0;
|
||||
});
|
||||
},
|
||||
// 获取商品详情
|
||||
getDetails(id) {
|
||||
getGoodsDetail({
|
||||
product_id: Number(id)
|
||||
}).then((res) => {
|
||||
this.details = res.data || {};
|
||||
// console.log(this.details,'--')
|
||||
this.picList = res.data?.prices
|
||||
// console.log(this.picList,'??')
|
||||
this.bannerList = this.details?.pic_list || [];
|
||||
// 默认选择第一个规格
|
||||
this.setDefaultSpec();
|
||||
});
|
||||
},
|
||||
// 设置默认规格
|
||||
setDefaultSpec() {
|
||||
const priceList = this.details?.price_list || [];
|
||||
const shuxingList = this.details?.shuxing_list || [];
|
||||
|
||||
if (priceList.length > 0) {
|
||||
const firstPrice = priceList[0];
|
||||
const firstShuxing = shuxingList.length > 0 ? shuxingList[0] : null;
|
||||
|
||||
// 使用 price_name 或 shuxing_name
|
||||
const specName = firstPrice.price_name || firstShuxing?.name || "";
|
||||
this.selectedSpec = specName ? `${specName}*1` : "";
|
||||
}
|
||||
},
|
||||
// 获取评论列表
|
||||
// getRemarkListData() {
|
||||
// getRemarkList({
|
||||
// p: 1,
|
||||
// num: 1,
|
||||
// type: 1,
|
||||
// guanlian_id: this.goodId
|
||||
// }).then(
|
||||
// (res) => {
|
||||
// this.remarkList = res?.info || [];
|
||||
// }
|
||||
// );
|
||||
// },
|
||||
// 跳转评论列表
|
||||
jumpToRemark() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/remark/list?shopId=${this.goodId}`,
|
||||
});
|
||||
},
|
||||
bannerChange(e) {
|
||||
this.bannerIndex = e.detail.current;
|
||||
},
|
||||
addToCart() {
|
||||
const {
|
||||
goods_id,
|
||||
price_list,
|
||||
shuxing_list
|
||||
} = this.details;
|
||||
// 只有一个规格时,直接加入购物车
|
||||
// if (price_list?.length === 1 && shuxing_list.length === 1) {
|
||||
// this.addCartAction({
|
||||
// goods_id,
|
||||
// price_id: price_list?.[0]?.price_id,
|
||||
// number: 1,
|
||||
// shuxing_name: shuxing_list?.[0]?.name,
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
this.showModal = true;
|
||||
this.type = "cart";
|
||||
},
|
||||
// 加入购物车
|
||||
addCartAction({number}) {
|
||||
const data = {
|
||||
item_id:this.details.id,
|
||||
item_type:this.details.type,
|
||||
price_id:this.details.prices[0].id,
|
||||
price_desc:this.details.prices[0].price_desc,
|
||||
item_name:this.details.product_name,
|
||||
item_price:this.details.prices[0].actual_price,
|
||||
number:number,
|
||||
item_pic:this.details.product_pic,
|
||||
is_select:1,
|
||||
property:this.details.property_name,
|
||||
}
|
||||
// console.log(this.details,'??')
|
||||
addCart(data).then(() => {
|
||||
uni.showToast({
|
||||
title: "已加入购物车!",
|
||||
icon: "none"
|
||||
});
|
||||
this.showModal = false;
|
||||
// 更新购物车数量
|
||||
this.getCartCount();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
title: err || "加入购物车失败!",
|
||||
icon: "none"
|
||||
});
|
||||
});
|
||||
},
|
||||
optAction({ goods_id, shuxing_name, price_id, number}) {
|
||||
|
||||
// if (kucun <= 0) {
|
||||
// uni.showToast({
|
||||
// title: "该商品已售罄",
|
||||
// icon: "none",
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// 更新已选规格显示
|
||||
const priceInfo = (this.details?.price_list || []).find(
|
||||
(v) => v.price_id === price_id
|
||||
);
|
||||
if (priceInfo) {
|
||||
this.selectedSpec = `${priceInfo.price_name || shuxing_name}*${number}`;
|
||||
}
|
||||
if (this.type === "cart") {
|
||||
// 加入购物车
|
||||
this.addCartAction({
|
||||
goods_id,
|
||||
price_id,
|
||||
number,
|
||||
shuxing_name,
|
||||
});
|
||||
} else {
|
||||
// 立即购买
|
||||
this.jumpToOrder({
|
||||
goods_id,
|
||||
shuxing_name,
|
||||
price_id,
|
||||
number
|
||||
});
|
||||
}
|
||||
},
|
||||
// 显示规格选择modal
|
||||
showSpecModal() {
|
||||
this.showModal = true;
|
||||
this.type = "cart";
|
||||
},
|
||||
goBuy() {
|
||||
const {
|
||||
goods_id,
|
||||
price_list,
|
||||
shuxing_list
|
||||
} = this.details;
|
||||
// 只有一个规格时,直接加入购物车
|
||||
// if (price_list?.length === 1 && shuxing_list.length === 1) {
|
||||
// this.jumpToOrder({ goods_id, shuxing_name, price_id, 1 })
|
||||
// return;
|
||||
// }
|
||||
this.showModal = true;
|
||||
this.type = "gobuy";
|
||||
},
|
||||
// 立即购买
|
||||
jumpToOrder({
|
||||
goods_id,
|
||||
shuxing_name,
|
||||
price_id,
|
||||
number
|
||||
}) {
|
||||
// console.log(this.details,'--?')
|
||||
// const petList = {
|
||||
// original_price:this.details.prices[0].actual_price,
|
||||
// actual_price:this.details.prices[0].original_price,
|
||||
// reduction_amount:0,
|
||||
// // pay_amount:, //数量乘实际单价
|
||||
// // note:''
|
||||
// }
|
||||
// console.log(petList,'=-=')
|
||||
console.log(number, 'number')
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/order/create`,
|
||||
success: (res) => {
|
||||
// 通过eventChannel向被打开页面传送数据
|
||||
const priceInfo = (this.details?.prices || []).find(
|
||||
(v) => price_id === v.id
|
||||
);
|
||||
res.eventChannel.emit("createOrder", {
|
||||
goodList: [{
|
||||
...this.details,
|
||||
goods_id,
|
||||
shuxing_name,
|
||||
price_id,
|
||||
number,
|
||||
goods_name: this.details.goods_name,
|
||||
price_name: priceInfo?.price_name,
|
||||
goods_price: +(+priceInfo?.actual_price * number).toFixed(2),
|
||||
}, ],
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
// 收藏
|
||||
async collectAction() {
|
||||
const token = uni.getStorageSync('token');
|
||||
if (!token) {
|
||||
const willLogin = await showLoginConfirmModal();
|
||||
if (!willLogin) return;
|
||||
return;
|
||||
}
|
||||
collectShop({
|
||||
goods_id: this.details.goods_id,
|
||||
type: this.details.shoucang_id ? 2 : 1,
|
||||
})
|
||||
.then(() => {
|
||||
this.getDetails();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
title: err || "收藏失败!",
|
||||
icon: "none"
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shop-details-container {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.banner-swiper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 750rpx;
|
||||
|
||||
.swiper-wrapper {
|
||||
width: 100%;
|
||||
height: 750rpx;
|
||||
|
||||
.swiper-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.dot-box {
|
||||
position: absolute;
|
||||
bottom: 30rpx;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
.dot-item {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 12rpx;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
margin-right: 12rpx;
|
||||
|
||||
&.active {
|
||||
width: 28rpx;
|
||||
height: 12rpx;
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.good-info {
|
||||
margin: 20rpx;
|
||||
|
||||
.info-cell {
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.origin-price {
|
||||
color: #726e71;
|
||||
text-decoration: line-through;
|
||||
margin-left: 18rpx;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
|
||||
.good-name {
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.sale-btn {
|
||||
padding: 6rpx 12rpx;
|
||||
background: $app_color_main;
|
||||
border-radius: 4rpx;
|
||||
border: 1rpx solid $app_color_main;
|
||||
margin-right: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
&.sale-today {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.postage-info {
|
||||
color: #9b939a;
|
||||
margin-right: 32rpx;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
width: 11rpx;
|
||||
height: 18rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.shipping-info-cell {
|
||||
.selected-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
cursor: pointer;
|
||||
|
||||
.selected-label {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.selected-value {
|
||||
flex: 1;
|
||||
font-size: 24rpx;
|
||||
color: #3D3D3D;
|
||||
}
|
||||
|
||||
.arrow-right-icon {
|
||||
width: 11rpx;
|
||||
height: 18rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.shipping-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.shipping-label {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.shipping-value {
|
||||
font-size: 24rpx;
|
||||
color: #3D3D3D;
|
||||
}
|
||||
}
|
||||
|
||||
.service-features {
|
||||
background: #F5F5F5;
|
||||
border-radius: 12rpx;
|
||||
padding: 12rpx 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 339px;
|
||||
|
||||
.feature-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.mallPng {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.feature-text {
|
||||
font-size: 20rpx;
|
||||
color: #3D3D3D;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remark-list {
|
||||
border-top: 2rpx solid #ebebeb;
|
||||
margin-top: 36rpx;
|
||||
|
||||
::v-deep {
|
||||
.remark-item {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rich-text-title {
|
||||
margin: 32rpx 0 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rich-text-content {
|
||||
background: #fff;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.place-view {
|
||||
height: 190rpx;
|
||||
}
|
||||
|
||||
.details-bottom {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
padding-bottom: 20rpx;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
padding-top: 20rpx;
|
||||
padding-left: 40rpx;
|
||||
padding-right: 36rpx;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
|
||||
.opt-item {
|
||||
&.service {
|
||||
margin-left: 36rpx;
|
||||
}
|
||||
|
||||
.opt-icon {
|
||||
width: 33rpx;
|
||||
height: 33rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
&.cart-item-wrapper {
|
||||
.cart-icon-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.cart-badge {
|
||||
position: absolute;
|
||||
top: -16rpx;
|
||||
right: -16rpx;
|
||||
min-width: 32rpx;
|
||||
height: 32rpx;
|
||||
background: #FF19A0;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 8rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.cart-badge-text {
|
||||
color: #FFFFFF;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opt-btns {
|
||||
width: 376rpx;
|
||||
height: 76rpx;
|
||||
border-radius: 92rpx;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
.opt-btn {
|
||||
width: 188rpx;
|
||||
height: 100%;
|
||||
background: $app_color_main;
|
||||
font-size: 28rpx;
|
||||
|
||||
&.opt-btn-cart {
|
||||
background: #F8C142;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: #ffecf3;
|
||||
display: inline-flex;
|
||||
border-radius: 4rpx;
|
||||
|
||||
|
||||
.hot-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
|
||||
.label-name {
|
||||
padding: 4rpx;
|
||||
color: #FF19A0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
749
src/pages/client/shop/index.vue
Normal file
@ -0,0 +1,749 @@
|
||||
<template>
|
||||
<view class="flex-column-start shop-container">
|
||||
<view class="shop-header">
|
||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<view class="header-content">
|
||||
<view class="search-bar" :style="{ width: searchBarWidth + 'rpx' }" @click="jumpToSearch">
|
||||
<text class="search-icon">🔍</text>
|
||||
<text class="search-placeholder">猫猫主粮</text>
|
||||
</view>
|
||||
<view class="category-tabs-wrapper">
|
||||
<!-- 左侧固定:综合 -->
|
||||
<view class="category-tab fixed-left" :class="{ active: activeCategoryIndex === 0 }"
|
||||
@click="switchCategory(0)">
|
||||
综合
|
||||
</view>
|
||||
<!-- 中间可滚动:动态分类数据 -->
|
||||
<scroll-view class="category-tabs-scroll" scroll-x>
|
||||
<view class="category-tab" v-for="(tab, index) in classifyList" :key="index"
|
||||
:class="{ active: activeCategoryIndex === tab.id }" @click="switchCategory(tab.id)">
|
||||
{{ tab.type_name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
<!-- 右侧固定:分类 -->
|
||||
<view class="category-tab fixed-right"
|
||||
:class="{ active: activeCategoryIndex === classifyList.length + 1 }" @click="jumpToSearch">
|
||||
<image class="category-icon" :src="`${imgPrefix}mall-category.png`" />
|
||||
分类
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view class="shop-content" :style="{ paddingTop: headerHeight + 'rpx' }" scroll-y
|
||||
:refresher-enabled="true" :refresher-triggered="refreshTriggered" @refresherrefresh="onRefresh"
|
||||
@scrolltolower="onLoadMore">
|
||||
<view>
|
||||
<image class="info-avator" src="https://activity.wagoo.live/bannerMall.png" />
|
||||
</view>
|
||||
<!-- <view class="banner-swiper">
|
||||
<swiper class="swiper-wrapper" circular :indicator-dots="false" :autoplay="true" @change="bannerChange">
|
||||
<swiper-item v-for="banner in bannerList" :key="banner.ad_id" @click="jumpTo(banner.ad_url)">
|
||||
<image class="swiper-img" :src="banner.thumb" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="flex-row-center dot-box">
|
||||
<view v-for="(banner, index) in bannerList" :key="banner.ad_id" class="dot-item"
|
||||
:class="{ active: bannerIndex === index }"></view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- <view class="flex-row-start shop-classify">
|
||||
<view
|
||||
class="flex-center classify-item"
|
||||
v-for="classify in classifyList"
|
||||
:key="classify.id"
|
||||
@click="jumpToCategory(classify)"
|
||||
>
|
||||
<image class="classify-img" :src="classify.select_type_pic" />
|
||||
<text class="app-fc-main fs-26 app-font-bold">{{
|
||||
classify.name
|
||||
}}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- <view
|
||||
v-if="couponList.length"
|
||||
class="flex-row-start coupon-view"
|
||||
@click="showCouponModal = true"
|
||||
>
|
||||
<image
|
||||
class="coupon-icon"
|
||||
src="https://activity.wagoo.live/new_account_coupon.png"
|
||||
/>
|
||||
<text class="flex-1 app-fc-mark fs-24 app-text-ellipse">
|
||||
您有{{
|
||||
couponList.length === 1
|
||||
? `一张${couponList[0].card_money || 0}元`
|
||||
: "多张"
|
||||
}}优惠券待领取
|
||||
</text>
|
||||
<image
|
||||
class="coupon-arrow"
|
||||
src="@/static/images/arrow_mark.png"
|
||||
mode="heightFix"
|
||||
/>
|
||||
</view> -->
|
||||
|
||||
<view class="recommand-goods-wrapper">
|
||||
<!-- <view class="fs-36 app-font-bold app-fc-main recommand-title">
|
||||
推荐商品
|
||||
</view> -->
|
||||
<view class="goods-list">
|
||||
<view class="goods-list-item left">
|
||||
<good-item v-for="(good, i) in leftColumnGoods" :index="2 * i" :key="2 * i" :data="good"
|
||||
@addToCar="addToCar" />
|
||||
</view>
|
||||
<view class="goods-list-item">
|
||||
<good-item v-for="(good, i) in rightColumnGoods" :index="2 * i + 1" :key="2 * i + 1"
|
||||
:data="good" @addToCar="addToCar" />
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="isLoadingGoods" class="loading-wrapper flex-center">
|
||||
<uni-load-more status="loading" :show-text="false" />
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="add-car-view">
|
||||
<image class="add-car-icon" :src="`${imgPrefix}mall-shopCar.png`" @click="jumpToCart" />
|
||||
</view>
|
||||
<view v-if="cartShowCount" class="fs-20 app-fc-white flex-center cart-count">
|
||||
{{ cartShowCount }}
|
||||
</view>
|
||||
|
||||
<view class="contact-icon-view">
|
||||
<image class="contact-icon" :src="`${imgPrefix}supportStaff.png`" @click="jumpToWeChat" />
|
||||
<view class="contact-btn fs-20">
|
||||
联系客服
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<add-goods-modal v-if="showModal" :data="addGoodInfo" optText="加入购物车" @change="(val) => (showModal = val)"
|
||||
@optAction="optAction" />
|
||||
<coupon-modal v-if="showCouponModal" :couponList="couponList" @close="onCouponModalClose"
|
||||
@getCoupon="getCoupon" />
|
||||
<contact-modal v-if="showContactModal" @close="showContactModal = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
imgPrefix
|
||||
} from '@/utils/common';
|
||||
|
||||
import GoodItem from "./components/GoodItem.vue";
|
||||
import AddGoodsModal from "@/components/goods/AddGoodsModal.vue";
|
||||
import CouponModal from "./components/CouponModal.vue";
|
||||
import ContactModal from "@/components/ContactModal.vue";
|
||||
|
||||
import {
|
||||
getGoodsClassify,
|
||||
getGoodsListData
|
||||
} from "@/api/shop";
|
||||
import {
|
||||
getCouponData
|
||||
} from "@/api/coupon";
|
||||
import {
|
||||
addCart,
|
||||
getCartList
|
||||
} from "../../../api/shop";
|
||||
import {
|
||||
getImageList
|
||||
} from "../../../api/common";
|
||||
import {
|
||||
jumpToWeChat
|
||||
} from "@/utils/common";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GoodItem,
|
||||
AddGoodsModal,
|
||||
CouponModal,
|
||||
ContactModal,
|
||||
},
|
||||
data() {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
// 获取胶囊按钮位置信息,计算搜索栏宽度
|
||||
// 默认宽度:750rpx (标准屏幕宽度) - 32rpx (左侧padding) - 右侧到胶囊的距离
|
||||
let searchBarWidth = 686;
|
||||
try {
|
||||
const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
if (menuButtonInfo && menuButtonInfo.left) {
|
||||
// 屏幕宽度转换为 rpx (750rpx 对应 375px)
|
||||
const screenWidthRpx = 750;
|
||||
const leftPadding = 32; // header-content 左侧 padding
|
||||
// 右侧需要留出的空间:屏幕宽度(px) - 胶囊按钮左边距(px) + 额外间距(px),然后转换为 rpx
|
||||
const rightSpacePx = systemInfo.windowWidth - menuButtonInfo.left + 20;
|
||||
const rightSpaceRpx = (rightSpacePx / systemInfo.windowWidth) * screenWidthRpx;
|
||||
searchBarWidth = screenWidthRpx - leftPadding - rightSpaceRpx;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('获取胶囊按钮信息失败', e);
|
||||
}
|
||||
// 计算 shop-header 的总高度
|
||||
// status-bar 高度(px转rpx) + header-content 高度
|
||||
// header-content: 20rpx(上padding) + 72rpx(搜索栏) + 24rpx(margin-bottom) + 约40rpx(分类标签) + 20rpx(下padding)
|
||||
const statusBarHeightRpx = (systemInfo.statusBarHeight || 0) * 2; // px转rpx
|
||||
const headerContentHeight = 20 + 72 + 10 + 40 + 20; // 约176rpx
|
||||
const headerHeight = statusBarHeightRpx + headerContentHeight;
|
||||
return {
|
||||
statusBarHeight: systemInfo.statusBarHeight || 0,
|
||||
headerHeight: headerHeight,
|
||||
searchBarWidth: searchBarWidth,
|
||||
activeCategoryIndex: 0,
|
||||
// categoryTabs: ['综合', '宠物食品', '宠物洗护', '宠物用品', '宠物健康'],
|
||||
refreshTriggered: false,
|
||||
bannerList: [{
|
||||
id: 1,
|
||||
image: 'https://activity.wagoo.live/mine_pet.png',
|
||||
}
|
||||
],
|
||||
classifyList: [],
|
||||
bannerIndex: 0,
|
||||
goodsList: [],
|
||||
isLoadingGoods: false,
|
||||
goodsTotal: 0,
|
||||
goodPage: 1,
|
||||
goodSize: 10,
|
||||
couponList: [],
|
||||
showModal: false,
|
||||
addGoodInfo: null,
|
||||
showCouponModal: false,
|
||||
unGetCouponList: [], // 待领取的优惠券列表
|
||||
showContactModal: false,
|
||||
cartCount: 0,
|
||||
imgPrefix
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
leftColumnGoods() {
|
||||
return this.goodsList.filter((v, i) => i % 2 === 0);
|
||||
},
|
||||
rightColumnGoods() {
|
||||
return this.goodsList.filter((v, i) => i % 2 === 1);
|
||||
},
|
||||
cartShowCount() {
|
||||
return this.cartCount > 9 ? "9+" : this.cartCount;
|
||||
},
|
||||
},
|
||||
// watch: {
|
||||
// showCouponModal(val) {
|
||||
// if (val) {
|
||||
// this.getCouponList();
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
mounted() {
|
||||
|
||||
// this.getCouponList();
|
||||
},
|
||||
methods: {
|
||||
jumpToWeChat,
|
||||
onShowFun() {
|
||||
this.initData();
|
||||
},
|
||||
initData() {
|
||||
this.getGoodsCategory();
|
||||
// this.getCouponList();
|
||||
this.goodPage = 1;
|
||||
this.getGoodsList();
|
||||
// this.getBannerList();
|
||||
this.getCartListData();
|
||||
},
|
||||
bannerChange(e) {
|
||||
this.bannerIndex = e.detail.current;
|
||||
},
|
||||
onRefresh() {
|
||||
if (this.refreshTriggered) return;
|
||||
this.refreshTriggered = true;
|
||||
// 请求商品列表/轮播图/商品分类/优惠券
|
||||
this.initData();
|
||||
},
|
||||
onLoadMore() {
|
||||
if (!this.isLoadingGoods && this.goodsTotal > this.goodsList.length) {
|
||||
this.goodPage++;
|
||||
this.getGoodsList();
|
||||
}
|
||||
},
|
||||
// 获取商品列表
|
||||
getGoodsList() {
|
||||
if (this.isLoadingGoods) return;
|
||||
this.isLoadingGoods = true;
|
||||
const params = {
|
||||
type: this.activeCategoryIndex
|
||||
}
|
||||
getGoodsListData(params)
|
||||
.then((res) => {
|
||||
const list = res?.data || [];
|
||||
this.goodsList =
|
||||
this.goodPage === 1 ? list : [...this.goodsList, ...list];
|
||||
this.goodsTotal = res?.count || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoadingGoods = false;
|
||||
this.refreshTriggered = false;
|
||||
});
|
||||
},
|
||||
// 获取商品分类
|
||||
getGoodsCategory() {
|
||||
getGoodsClassify()
|
||||
.then((res) => {
|
||||
this.classifyList = res?.data || [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.refreshTriggered = false;
|
||||
});
|
||||
},
|
||||
// 获取待领取的新人优惠券
|
||||
getCouponList() {
|
||||
getCouponData({
|
||||
p: 1,
|
||||
num: 9999,
|
||||
is_lingqu: 1,
|
||||
is_xinren: 1,
|
||||
type: 1,
|
||||
})
|
||||
.then((res) => {
|
||||
this.couponList = res?.info || [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.refreshTriggered = false;
|
||||
});
|
||||
},
|
||||
// 加入购物车
|
||||
addCartAction({
|
||||
goods_id,
|
||||
price_id,
|
||||
number,
|
||||
shuxing_name
|
||||
}) {
|
||||
addCart({
|
||||
goods_id,
|
||||
price_id,
|
||||
number,
|
||||
shuxing_name,
|
||||
})
|
||||
.then(() => {
|
||||
uni.showToast({
|
||||
title: "已加入购物车!",
|
||||
icon: "none"
|
||||
});
|
||||
this.getCartListData();
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.showToast({
|
||||
title: err || "加入购物车失败!",
|
||||
icon: "none"
|
||||
});
|
||||
});
|
||||
},
|
||||
// 轮播图片列表
|
||||
// getBannerList() {
|
||||
// getImageList(3).then((res) => {
|
||||
// this.bannerList = res?.info || [];
|
||||
// });
|
||||
// },
|
||||
// 购物车列表
|
||||
getCartListData() {
|
||||
getCartList({
|
||||
p: 1,
|
||||
num: 999
|
||||
}).then((res) => {
|
||||
this.cartCount = (res?.data.list || []).reduce(
|
||||
(total, prev) => total + prev.number,
|
||||
0
|
||||
);
|
||||
console.log(this.cartCount,'--=?')
|
||||
});
|
||||
},
|
||||
optAction(data) {
|
||||
const {
|
||||
goods_id,
|
||||
price_id,
|
||||
number,
|
||||
shuxing_name,
|
||||
kucun
|
||||
} = data;
|
||||
if (kucun <= 0) {
|
||||
uni.showToast({
|
||||
title: "该商品已售罄",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.addCartAction({
|
||||
goods_id,
|
||||
price_id,
|
||||
number,
|
||||
shuxing_name
|
||||
});
|
||||
this.showModal = false;
|
||||
},
|
||||
addToCar(good) {
|
||||
const {
|
||||
goods_id,
|
||||
price_list,
|
||||
shuxing_list
|
||||
} = good;
|
||||
if (price_list?.length === 1 && shuxing_list.length === 1) {
|
||||
if (price_list?.[0]?.kucun <= 0) {
|
||||
uni.showToast({
|
||||
title: "该商品已售罄",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.addCartAction({
|
||||
goods_id,
|
||||
price_id: price_list?.[0]?.price_id,
|
||||
number: 1,
|
||||
shuxing_name: shuxing_list?.[0]?.name,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.showModal = true;
|
||||
this.addGoodInfo = {
|
||||
...good
|
||||
};
|
||||
},
|
||||
jumpToCategory(category) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/category/index?id=${category.id}`,
|
||||
});
|
||||
},
|
||||
jumpToCart() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/client/cart/index`,
|
||||
});
|
||||
},
|
||||
jumpTo(url) {
|
||||
console.log(1111, url)
|
||||
uni.navigateTo({
|
||||
url
|
||||
})
|
||||
},
|
||||
onCouponModalClose() {
|
||||
this.showCouponModal = false;
|
||||
this.getCouponList();
|
||||
},
|
||||
getCoupon() {
|
||||
this.showCouponModal = false;
|
||||
uni.navigateTo({
|
||||
url: "/pages/client/coupon/get-list",
|
||||
});
|
||||
},
|
||||
jumpToSearch() {
|
||||
// 跳转到搜索页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/client/category/index'
|
||||
});
|
||||
},
|
||||
switchCategory(_id) {
|
||||
this.activeCategoryIndex = _id;
|
||||
// 可以根据分类切换商品列表
|
||||
this.goodPage = 1;
|
||||
this.getGoodsList();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shop-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: stretch;
|
||||
background: #f7f8fa;
|
||||
|
||||
.shop-header {
|
||||
background: #ff19a0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
border-radius: 0px 0px 16px 16px;
|
||||
|
||||
.status-bar {
|
||||
background: #ff19a0;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
padding: 20rpx 32rpx;
|
||||
background: #ff19a0;
|
||||
padding-top: 0;
|
||||
border-radius: 0px 0px 16px 16px;
|
||||
|
||||
.search-bar {
|
||||
background: #fff;
|
||||
border-radius: 100rpx;
|
||||
height: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
|
||||
.search-icon {
|
||||
font-size: 32rpx;
|
||||
color: #999;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.search-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
|
||||
.action-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
border: 1rpx solid #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.category-tabs-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.category-tab {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
white-space: nowrap;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.active {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.fixed-left {
|
||||
padding: 0 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.fixed-right {
|
||||
margin-left: 16rpx;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
color: #fff;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -16rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 1rpx;
|
||||
height: 24rpx;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 20rpx;
|
||||
height: 17rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.category-tabs-scroll {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
.category-tab {
|
||||
display: inline-block;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shop-content {
|
||||
background: #ffecf3;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
.info-avator{
|
||||
width: 100%;
|
||||
height: 260rpx;
|
||||
}
|
||||
|
||||
.banner-swiper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 260rpx;
|
||||
|
||||
.swiper-wrapper {
|
||||
width: 100%;
|
||||
height: 260rpx;
|
||||
|
||||
.swiper-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.dot-box {
|
||||
position: absolute;
|
||||
bottom: 12rpx;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
.dot-item {
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
margin-right: 8rpx;
|
||||
|
||||
&.active {
|
||||
width: 20rpx;
|
||||
height: 8rpx;
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shop-classify {
|
||||
margin: 52rpx 0 44rpx;
|
||||
|
||||
.classify-item {
|
||||
width: 25%;
|
||||
|
||||
.classify-img {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
margin-bottom: 22rpx;
|
||||
border-radius: 100rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-view {
|
||||
width: 100%;
|
||||
height: 56rpx;
|
||||
background: #fee9f3;
|
||||
border-radius: 16rpx;
|
||||
padding-right: 30rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.coupon-icon {
|
||||
height: 100%;
|
||||
width: 186rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.coupon-arrow {
|
||||
width: 10rpx;
|
||||
height: 14rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.recommand-goods-wrapper {
|
||||
.recommand-title {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.goods-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
margin-top: 16rpx;
|
||||
|
||||
.goods-list-item {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
&.left {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading-wrapper {
|
||||
padding: 10rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-car-view {
|
||||
position: fixed;
|
||||
bottom: 35vh;
|
||||
right: 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
padding: 20rpx;
|
||||
box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.add-car-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.contact-icon-view {
|
||||
position: fixed;
|
||||
bottom: calc(35vh - 130rpx - 48rpx);
|
||||
right: 20rpx;
|
||||
text-align: center;
|
||||
|
||||
.contact-icon {
|
||||
width: 66rpx;
|
||||
height: 66rpx;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.contact-btn {
|
||||
color: #FFFFFF;
|
||||
background-color: #FF19A0;
|
||||
border-radius: 257px;
|
||||
padding: 6rpx 8rpx;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.cart-count {
|
||||
position: fixed;
|
||||
bottom: calc(35vh + 88rpx - 20rpx);
|
||||
right: 20rpx;
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
border-radius: 30rpx;
|
||||
background: #FF19A0;
|
||||
z-index: 10;
|
||||
border: 1px solid #FFFFFF;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
BIN
src/pages/client/shop/static/arrow_right.png
Normal file
|
After Width: | Height: | Size: 360 B |
BIN
src/pages/client/shop/static/coupon_modal_close.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/pages/client/shop/static/coupon_modal_get.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |