Browse Source

feat: 实现协议页面功能并优化个人信息和分类页面

- 在agreement.vue中实现协议内容展示功能,支持隐私协议和服务协议
- 优化personalInformation.vue页面,添加用户信息获取和展示逻辑
- 完善classification.vue商品分类功能,添加商品列表、购物车和滚动加载
- 统一使用uv-ui组件库替换原生元素
- 修复多个页面样式问题和事件绑定错误
master
wei 1 week ago
parent
commit
eb188986fc
  1. 45
      pages/agreement/agreement.vue
  2. 358
      pages/classification/classification.vue
  3. 132
      pages/personalInformation/personalInformation.vue

45
pages/agreement/agreement.vue

@ -1,26 +1,41 @@
<script setup>
import {
getCurrentInstance,
onMounted,
} from "vue";
// import { onLoad } from "@dcloudio/uni-app";
import { onHide, onLoad } from "@dcloudio/uni-app";
import { getCurrentInstance, ref } from "vue";
import { gotoBack } from "@/libs/utils";
// onLoad((options) => {
// getOpenerEventChannel
// });
const content = ref("");
onMounted(() => {
const html = ref("");
const title = ref("");
onLoad(() => {
const instance = getCurrentInstance().proxy;
const eventChannel = instance.getOpenerEventChannel();
eventChannel.emit("getContent", (data) => {
console.log(data, 333);
// content
eventChannel.on("privacyAgreement", (data) => {
html.value = data.html;
title.value = data.title;
});
eventChannel.on("serviceAgreement", (data) => {
html.value = data.html;
title.value = data.title;
});
});
onHide(() => {
html.value = "";
title.value = "";
});
</script>
<template>
<view>
agreement
<view class="container">
<uv-status-bar />
<uv-navbar :title="title" @left-click="gotoBack" />
<uv-parse :content="html" />
</view>
</template>
<style lang="scss" scoped>
.container {
padding-top: 44px;
margin: 30rpx;
}
</style>

358
pages/classification/classification.vue

@ -3,8 +3,15 @@ import { onShow } from "@dcloudio/uni-app";
import { nextTick, ref } from "vue";
import customTabBar from "@/components/custom-tab-bar/my-tab-bar.vue";
import navigation from "@/components/navigation/navigation.vue";
import { getProductTypesApi } from "@/libs/api";
import { getCartInfoApi, getProductsApi, getProductTypesApi } from "@/libs/api";
import { getHeight, useHeight } from "@/libs/utils";
import useStore from "@/store";
// const app = getApp();
const store = useStore();
// const plugin = requirePlugin("WechatSI")
// const manager = plugin.getRecordRecognitionManager()
/**
* 搜索关键字
@ -34,7 +41,14 @@ const currentTab = ref(0);
* 当前子类标签
*/
const currentType = ref(0);
/**
* 右侧商品列表的高度
*/
const bottomHeight = ref(0);
/**
* 右侧商品列表的滚动高度
*/
const scrollTop = ref(0);
/**
* 顶部展开栏左右偏移量
*/
@ -51,6 +65,56 @@ const isShowAll = ref(false);
* 子类型数据
*/
const typeData = ref([]);
/**
* 商品数据
*/
const products = ref([]);
/**
* 判断商品列表滚动到底部是否还有数据
*/
const unLoading = ref(false);
/**
* 判断商品列表有无数据
*/
const showList = ref(true);
/**
* 当前页码
*/
const pageIndex = ref(1);
/**
* 每页显示数量
*/
const pageSize = ref(20);
/**
* 总页数
*/
const pageCount = ref(0);
/**
* 判断是否滚动到了底部
*/
const isBottom = ref(false);
/**
* 下拉刷新状态
*/
const triggered = ref(false);
/**
* 触摸开始时的X坐标
*/
const touchDotX = ref(0);
/**
* 触摸开始时的Y坐标
*/
const touchDotY = ref(0);
/**
* 是否触摸开始
*/
const touchStar = ref(false);
/**
* 右侧商品列表的高度
*/
const rightHeight = ref(0);
/**
* 滚动加载数据时当前商品类型id
*/
@ -81,21 +145,19 @@ function onShowAllTypes() {
*/
function onSwitchNav(item, index) {
navScrollLeft.value = (index - 2) * 50;
if (currentTab.value === index) {
return false;
}
else {
currentTab.value = index;
currentType.value = 0;
// products.value = [];
// unLoading.value = false;
// showList.value = true;
// pageIndex.value = 1;
isShowAll.value = false;
keyword.value = "";
isSearching.value = false;
getLeftTypes(item.id);
}
if (currentTab.value === index)
return;
currentTab.value = index;
currentType.value = 0;
products.value = [];
unLoading.value = false;
showList.value = true;
pageIndex.value = 1;
isShowAll.value = false;
keyword.value = "";
isSearching.value = false;
getLeftTypes(item.id);
}
function onOpenRecord() {}
/**
@ -146,15 +208,8 @@ async function getLeftTypes(parentId, isSearch) {
typeData.value = resp.data;
if (resp.total > 0) {
console.log(currentType.value, typeData.value, 333);
scrollId.value = typeData.value[currentType.value].id;
if (isSearch) {
getProducts("");
}
else {
getProducts(scrollId.value);
}
getProducts(isSearch ? "" : scrollId.value);
}
}
}
@ -162,32 +217,189 @@ async function getLeftTypes(parentId, isSearch) {
/**
* 获取商品信息
*/
function getProducts(parentId, tips) {
if (hasFetchedCart.value) {
loadProductList(parentId, tips);
async function getProducts(parentId, tips) {
if (!hasFetchedCart.value) {
const params = {
warehouseId: uni.getStorageSync("warehousId"),
addrId: uni.getStorageSync("addressId"),
};
const resp = await getCartInfoApi(params);
store.changeCartList(resp.code === "0" ? resp.data : []);
// app.globalData.cartList = resp.code === "0" ? resp.data : [];
hasFetchedCart.value = resp.code === "0";
}
loadProductList(parentId, tips);
}
/**
* 菜品列表 全局只加载一次
*/
async function loadProductList(parentId, tips) {
showList.value = true;
// const params = {
// warehouseId: wx.getStorageSync("warehousId"),
// addrId: wx.getStorageSync("addressId"),
// };
const cartList = store.cartList;
const total = cartList.reduce((cur, acc) => acc + cur.quantity, 0);
store.increment(total);
const params = {
typeId: parentId,
promotion: false,
orderByField: "name",
ase: true,
search: keyword.value,
pageNum: pageIndex.value,
pageSize: pageSize.value,
warehouseid: uni.getStorageSync("warehousId"),
};
const resp = await getProductsApi(params);
if (resp.code !== "0")
return;
pageCount.value = resp.pageCount;
if (!(resp.total > 0)) {
showList.value = false;
return;
}
resp.data.forEach((item) => {
// specId_price -> quantity
const cartMap = cartList.reduce((acc, cur) => {
return { ...acc, [`${cur.specId}_${cur.price}`]: cur.quantity };
}, {});
item.specs.forEach((specsItem) => {
//
const specKey = `${specsItem.id}_${specsItem.price}`;
// 0
specsItem.sum = cartMap[specKey] || 0;
});
//
item.showChoose = item.specs.length > 1 ? 1 : 0;
item.showChild = item.specs.length > 1 ? false : item.showChild;
});
products.value = pageIndex.value === 1
? resp.data
: [...products.value, ...(resp.data || [])];
rightHeight.value = (await getHeight("#scroll-page")) + 1;
if (tips === 1) {
currentType.value -= 1;
}
else if (tips === 2) {
currentType.value += 1;
scrollTop.value = 0;
bottomHeight.value = 0;
}
uni.hideNavigationBarLoading(); //
uni.stopPullDownRefresh();
}
/**
* 滚动事件
* @param {Event} e - 滚动事件参数
*/
function onScroll(e) {
isBottom.value = (e.detail.scrollHeight - e.detail.scrollTop) <= rightHeight.value;
}
/**
* 翻页数据
*/
function onLoadList() {
const pageIdx = pageIndex.value + 1;
if (pageIdx <= pageCount.value) {
pageIndex.value = pageIdx;
unLoading.value = false;
getProducts(scrollId.value);
}
else {
unLoading.value = true;
}
}
/**
* 下拉刷新
*/
function onRefresherrefresh() {
triggered.value = false;
if (currentType.value !== 0) {
getProducts(typeData[currentType.value - 1].id, 1);
}
}
function loadProductList(parentId, tips) {
// showList.value = true;
// uni.showLoading({
// title: "",
// mask: true,
// });
// uni.hideLoading();
// let cartList = app.globalData.cartList;
// @touchstart="onTouchStart"
// @touchmove="onTouchMove"
// @touchend="onTouchend"
function onTouchStart(e) {
touchDotX.value = e.touches[0].pageX;
touchDotY.value = e.touches[0].pageY;
touchStar.value = false;
}
function onTouchMove(e) {
const pageX = e.touches[0].pageX;
const pageY = e.touches[0].pageY;
if (!touchStar.value) {
return;
}
const moveY = Math.abs(pageY - touchDotY.value);
/**
* 左滑手势横向滑动距离 40 且纵向偏移 < 10且当前不是最后一个一级分类时切换到下一个一级分类
*/
const isLeft = pageX - touchDotX.value <= -40
&& moveY < 10
&& (currentTab.value < navData.value.length - 1);
/**
* 右滑手势横向滑动距离 40 且纵向偏移 < 10且当前不是第一个一级分类时切换到上一个一级分类
*/
const isRight = pageX - touchDotX.value >= 40
&& moveY < 10
&& (currentTab.value !== 0);
//
if (isLeft || isRight) {
currentType.value = 0;
products.value = [];
unLoading.value = false;
showList.value = true;
pageIndex.value = 1;
touchStar.value = false;
currentTab.value = isLeft ? currentTab.value + 1 : currentTab.value - 1;
const navId = navData.value[currentTab.value]?.id;
navScrollLeft.value = (currentTab.value - 2) * 50;
getLeftTypes(navId);
}
bottomHeight.value = touchDotY.value > pageY
? bottomHeight.value + 0.2 //
: bottomHeight.value - 0.2; //
}
function onTouchEnd() {
bottomHeight.value = 0;
}
onShow(() => {
getProductTypes();
safeHeight.value = useHeight();
nextTick(async () => {
const boxHeight = await getHeight(".first-box");
const { screen, status, menu, bottom } = safeHeight.value;
pageContainerHeight.value = screen - status - menu - bottom - boxHeight - 60;
// searchHeight.value = await getHeight(".header-box");
});
});
</script>
@ -196,9 +408,14 @@ onShow(() => {
<view class="w-full h-full">
<!-- 微信的胶囊以及搜索框 -->
<navigation background="#fff" class="header-box">
<!-- <image class="icon-left" src="../../images/all/search.png" mode="" /> -->
<div class="box" :style="{ height: `${safeHeight.menu || 45}px` }">
<text class="iconfont icon-search" />
<div
class="box"
:style="{
height: `${safeHeight.menu || 45}px`,
width: safeHeight.menu ? '70%' : '100%',
}"
>
<text class="iconfont icon-search1" />
<input
class="input"
type="text"
@ -238,6 +455,8 @@ onShow(() => {
</view>
</scroll-view>
</view>
<!-- 展开 -->
<view class="right" @click="onShowAllTypes">
<text class="text">
@ -256,7 +475,9 @@ onShow(() => {
<view
class="first-big-type"
:style="{
top: isShowAll ? `${safeHeight.status + (safeHeight.menu || 45) + 5}px` : '-1600rpx',
top: isShowAll
? `${safeHeight.status + (safeHeight.menu || 45) + 5}px`
: '-1600rpx',
}"
>
<view class="title">
@ -314,6 +535,39 @@ onShow(() => {
</text>
</view>
</view>
<scroll-view
id="scroll-page"
class="right"
:scroll-top="scrollTop"
scroll-y
:throttle="false"
:style="{
width: isSearch ? '100%' : '80%',
height: '100%',
marginTop: `${-(bottomHeight)}%`,
transition: '.01s all',
}"
refresher-enabled
refresher-threshold="20"
enhanced
enable-passive
:refresher-triggered="triggered"
refresher-default-style="white"
scroll-with-animation
show-scrollbar
@scrolltolower="onLoadList"
@scroll="onScroll"
@refresherrefresh="onRefresherrefresh"
>
<view
v-if="showList"
class="content-right"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
/>
</scroll-view>
<!-- @refresherrestore="onTriggered" -->
</view>
<customTabBar tab-index="1" />
@ -325,7 +579,8 @@ onShow(() => {
width: 100%;
.box {
margin-left: 14rpx;
width: 70%;
margin-right: 14rpx;
// width: 70%;
display: flex;
align-items: center;
border-radius: 50rpx;
@ -337,7 +592,7 @@ onShow(() => {
color: #333;
flex: 1;
}
.icon-search {
.icon-search1 {
margin-left: 38rpx;
}
.icon-yuyin {
@ -725,7 +980,7 @@ page {
}
.name {
font-size: 24rpx;
font-size: $text-base;
margin-top: 12rpx;
text-align: center;
padding: 10rpx 8rpx;
@ -747,7 +1002,8 @@ page {
align-items: center;
justify-content: center;
color: #666666;
font-size: 24rpx;
// font-size: 24rpx;
font-size: $text-base;
margin-top: 30rpx;
.left {
@ -1039,7 +1295,7 @@ page {
transform: translateY(-50%);
color: #3aa24b;
font-weight: bold;
font-size: 44rpx;
font-size: 80rpx;
}
.title {
@ -1097,7 +1353,8 @@ page {
.name {
font-weight: 400;
font-size: 25rpx;
font-size: $text-sm;
// font-size: 25rpx;
color: #333333;
margin-top: 8rpx;
transition: .3s all;
@ -1166,7 +1423,8 @@ page {
border-radius: 0 0 20rpx 20rpx;
.title {
font-size: 28rpx;
// font-size: 28rpx;
font-size: $text-lg;
font-weight: bold;
}
@ -1189,7 +1447,8 @@ page {
.name {
font-weight: 400;
font-size: 25rpx;
font-size: $text-base;
// font-size: 25rpx;
color: #333333;
margin-top: 8rpx;
@ -1222,7 +1481,8 @@ page {
height: 100%;
overflow-y: scroll;
font-weight: 400;
font-size: 26rpx;
font-size: $text-base;
// font-size: 26rpx;
color: #333333;
background: #F8FAF7;
padding-top: 14rpx;

132
pages/personalInformation/personalInformation.vue

@ -3,6 +3,7 @@ import { onLoad } from "@dcloudio/uni-app";
import { ref } from "vue";
import customTabBar from "@/components/custom-tab-bar/my-tab-bar.vue";
import navigation from "@/components/navigation/navigation.vue";
import { getUserInfoApi, yesterdayOrderApi } from "@/libs/api";
/**
* 判断登录状态
@ -15,11 +16,15 @@ const userInfo = ref({});
/**
* 公司信息
*/
const company = ref({});
const company = ref(undefined);
/**
* 位置信息
* 订单位置信息
*/
const position = ref("");
const position = ref(false);
/**
* 昨日下单订单号
*/
const orderId = ref("");
/**
* 常用订单
*/
@ -288,10 +293,61 @@ function onGotoOrder() {}
function onGetPosition() {}
/**
* 获取用户信息
*/
async function getUserInfo() {
const resp = await getUserInfoApi();
if (resp.code === "0") {
isLogin.value = true;
resp.data.walletMoney = (resp.data.walletMoney || 0).toFixed(2);
company.value = resp.data.company ? resp.data.company : undefined;
userInfo.value = resp.data;
}
else if (resp.code === "4") {
//
isLogin.value = false;
userInfo.value = "";
company.value = "";
}
}
/**
* 获取热线
*/
function getHotLines() { }
/**
* 获取地址
*/
function getMyArea() { }
/**
* 获取仓库列表
*/
function getWarehouse() { }
async function getYesterdayOrder() {
const resp = await yesterdayOrderApi();
if (resp.code === "0" && resp.data) {
position.value = true;
orderId.value = resp.data;
}
}
function openSwitchTab() { }
function handleOther() {}
onLoad(() => {
// #ifdef APP
uni.hideTabBar();
// #endif
getUserInfo(); //
getHotLines();
getMyArea();
getYesterdayOrder();
});
</script>
@ -306,6 +362,7 @@ onLoad(() => {
:title-style="{ color: 'white' }"
:icon-style="{ display: 'none' }"
/>
<!-- <uv-navbar title="个人中心" /> -->
<view class="userinfo">
<view class="left">
<image
@ -313,6 +370,8 @@ onLoad(() => {
:src="userInfo.image ? userInfo.image : '/static/personalInformation/touxiang1.png'"
mode="aspectFill"
/>
<!-- telephone true -->
<view v-if="userInfo.telephone" class="information">
<view class="name">
{{ userInfo.username }}
@ -321,18 +380,24 @@ onLoad(() => {
{{ company.name }}
</view>
<view v-else class="add-company" @click="onJoinEnterprise">
加入企业 >
<text>加入企业</text>
<uv-icon color="white" name="arrow-right" size="28rpx" />
</view>
</view>
<!-- telephone false -->
<view v-else class="btns">
<text v-if="isLogin" class="add-company" @click="onJoinEnterprise">
加入企业
</text>
<text v-else class="login-btn" @click="onGotoLogin">
登录 >
</text>
<view v-if="isLogin" class="add-company" @click="onJoinEnterprise">
<text>加入企业</text>
<uv-icon color="white" name="arrow-right" size="28rpx" />
</view>
<view v-else class="login-btn" @click="onGotoLogin">
<text> 登录 </text>
<uv-icon color="white" name="arrow-right" size="28rpx" />
</view>
</view>
</view>
<view v-if="userInfo.telephone" class="right">
<image
class="icon-one"
@ -403,7 +468,7 @@ onLoad(() => {
<button
v-if="item.type === 'tap'"
class="tool-list"
bindtap="exportDeliveryNote"
@click="exportDeliveryNote"
>
<image class="icon" :src="item.url" mode="aspectFit" />
<text class="name">
@ -414,7 +479,7 @@ onLoad(() => {
v-if="item.type === 'other'"
class="tool-list"
data-item="{{item}}"
bindtap="handleOther"
@click="handleOther"
>
<image class="icon" :src="item.url" mode="aspectFit" />
<text class="name">
@ -443,8 +508,7 @@ onLoad(() => {
<button
v-if="item.type === 'switchTab'"
class="tool-list"
data-path="{{item.path}}"
bindtap="openSwitchTab"
@click="openSwitchTab"
>
<image class="icon" :src="item.url" mode="aspectFit" />
<text class="name">
@ -936,29 +1000,35 @@ onLoad(() => {
.name,
.company,
.add-company {
display: flex;
align-items: center;
font-weight: 400;
font-size: 26rpx;
font-size: $text-base;
// font-size: 26rpx;
color: #FFFFFF;
}
.company {
margin-top: 20rpx;
}
.add-company {
background: rgba(255, 255, 255, 0.1);
border-radius: 60rpx;
border: 1px solid #FFFFFF;
padding: 14rpx 24rpx;
margin-top: 17rpx;
line-height: 1;
}
}
.btns {
.add-company {
color: #FFFFFF;
margin-left: 24rpx;
display: flex;
align-items: center;
font-weight: bold;
font-size: $text-xl;
}
color: #FFFFFF;
.login-btn {
display: flex;
align-items: center;
font-weight: bold;
font-size: 33rpx;
font-size: $text-xl;
// font-size: 33rpx;
color: #FFFFFF;
margin-left: 24rpx;
}
@ -993,7 +1063,8 @@ onLoad(() => {
.title {
font-weight: bold;
font-size: 29rpx;
font-size: $text-xl;
// font-size: 29rpx;
color: #333333;
}
@ -1015,7 +1086,8 @@ onLoad(() => {
.name {
font-weight: 400;
font-size: 26rpx;
font-size: $text-base;
// font-size: 26rpx;
color: #666666;
margin-top: 22rpx;
}
@ -1056,7 +1128,8 @@ onLoad(() => {
.title {
font-weight: bold;
font-size: 29rpx;
font-size: $text-xl;
// font-size: 29rpx;
color: #333333;
}
@ -1090,7 +1163,8 @@ onLoad(() => {
.name {
font-weight: 400;
font-size: 26rpx;
font-size: $text-base;
// font-size: 26rpx;
color: #666666;
margin-top: 22rpx;
}

Loading…
Cancel
Save