feat(商品管理): 添加商品管理模块及相关功能
- 新增商品管理页面及API接口 - 扩展用户余额信息展示,增加已用借呗和总额显示 - 添加商品管理路由入口和权限控制 - 移除商品列表页未使用的滚动定位代码 - 新增通用类型定义文件 - 添加余额刷新功能 商品管理模块包含商品列表展示、添加、编辑和删除功能,同时完善了用户余额信息的展示,包括剩余借呗、已用借呗和总额。移除了商品列表页中未使用的滚动定位相关代码以简化逻辑。
This commit is contained in:
parent
5bf5fd6b0e
commit
8cfa252d9a
|
@ -0,0 +1,96 @@
|
||||||
|
import { request } from "@/http/axios"
|
||||||
|
import { PageDTO, ResponseData, BasePageQuery } from "../type"
|
||||||
|
|
||||||
|
export interface ShopGoodsDTO {
|
||||||
|
goodsId?: number
|
||||||
|
goodsName?: string
|
||||||
|
categoryId?: number
|
||||||
|
categoryName?: string
|
||||||
|
price?: number
|
||||||
|
stock?: number
|
||||||
|
status?: number
|
||||||
|
autoApproval?: number
|
||||||
|
coverImg?: string
|
||||||
|
creatorId?: number
|
||||||
|
creatorName?: string
|
||||||
|
createTime?: string
|
||||||
|
remark?: string
|
||||||
|
cabinetName?: string
|
||||||
|
cellNo?: number
|
||||||
|
cellNoStr?: string
|
||||||
|
totalStock?: number
|
||||||
|
usageInstruction?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchShopGoodsQuery extends BasePageQuery {
|
||||||
|
goodsName?: string
|
||||||
|
categoryId?: number
|
||||||
|
status?: number
|
||||||
|
autoApproval?: number
|
||||||
|
minPrice?: number
|
||||||
|
maxPrice?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取商品列表 */
|
||||||
|
export function getGoodsList(query: SearchShopGoodsQuery) {
|
||||||
|
return request<ResponseData<PageDTO<ShopGoodsDTO>>>({
|
||||||
|
url: "manage/goods/list",
|
||||||
|
method: "get",
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增商品 */
|
||||||
|
export function addGoods(data: {
|
||||||
|
goodsName: string
|
||||||
|
categoryId: number
|
||||||
|
price: number
|
||||||
|
stock: number
|
||||||
|
status: number
|
||||||
|
autoApproval: number
|
||||||
|
coverImg: string
|
||||||
|
goodsDetail?: string
|
||||||
|
usageInstruction?: string
|
||||||
|
}) {
|
||||||
|
return request<ResponseData<void>>({
|
||||||
|
url: "manage/goods",
|
||||||
|
method: "post",
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除商品 */
|
||||||
|
export function deleteGoods(goodsIds: number[]) {
|
||||||
|
return request<ResponseData<void>>({
|
||||||
|
url: `manage/goods/${goodsIds.join(',')}`,
|
||||||
|
method: "delete"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改商品 */
|
||||||
|
export function updateGoods(goodsId: number, data: {
|
||||||
|
goodsName?: string
|
||||||
|
categoryId?: number
|
||||||
|
price?: number
|
||||||
|
stock?: number
|
||||||
|
status?: number
|
||||||
|
autoApproval?: number
|
||||||
|
coverImg?: string
|
||||||
|
goodsDetail?: string
|
||||||
|
usageInstruction?: string
|
||||||
|
}) {
|
||||||
|
return request<ResponseData<void>>({
|
||||||
|
url: `manage/goods/${goodsId}`,
|
||||||
|
method: "put",
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取单个商品信息 */
|
||||||
|
export function getGoodsInfo(goodsId: number) {
|
||||||
|
return request<ResponseData<ShopGoodsDTO>>({
|
||||||
|
url: "manage/goods/getGoodsInfo",
|
||||||
|
method: "get",
|
||||||
|
params: { goodsId }
|
||||||
|
})
|
||||||
|
}
|
|
@ -101,7 +101,12 @@ export interface GetOrdersByOpenIdDTO {
|
||||||
export interface GetBalanceResponse {
|
export interface GetBalanceResponse {
|
||||||
userid: string
|
userid: string
|
||||||
corpid: string
|
corpid: string
|
||||||
|
/** 剩余借呗 */
|
||||||
balance: number
|
balance: number
|
||||||
|
/** 已用借呗 */
|
||||||
|
useBalance: number
|
||||||
|
/** 借呗总额 */
|
||||||
|
balanceLimit: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QyLoginDTO {
|
export interface QyLoginDTO {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
export type ResponseData<T> = {
|
||||||
|
code: number;
|
||||||
|
msg: string;
|
||||||
|
data: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PageDTO<T> = {
|
||||||
|
total: number;
|
||||||
|
rows: Array<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface BasePageQuery extends BaseQuery {
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BaseQuery {
|
||||||
|
beginTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
orderColumn?: string;
|
||||||
|
orderDirection?: string;
|
||||||
|
timeRangeColumn?: string;
|
||||||
|
}
|
|
@ -0,0 +1,261 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
import { showConfirmDialog, showDialog, showSuccessToast } from 'vant';
|
||||||
|
import {
|
||||||
|
getGoodsList,
|
||||||
|
addGoods,
|
||||||
|
deleteGoods,
|
||||||
|
updateGoods,
|
||||||
|
type ShopGoodsDTO,
|
||||||
|
type SearchShopGoodsQuery
|
||||||
|
} from '@/common/apis/manage/goods';
|
||||||
|
|
||||||
|
// 商品列表数据
|
||||||
|
const goodsList = ref<ShopGoodsDTO[]>([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
const finished = ref(false);
|
||||||
|
|
||||||
|
// 搜索参数
|
||||||
|
const searchParams = reactive<SearchShopGoodsQuery>({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
goodsName: '',
|
||||||
|
status: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
// 弹窗控制
|
||||||
|
const showEditDialog = ref(false);
|
||||||
|
const currentGoods = ref<Partial<ShopGoodsDTO>>({});
|
||||||
|
const isEditMode = ref(false);
|
||||||
|
|
||||||
|
// 获取商品列表
|
||||||
|
const fetchGoodsList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getGoodsList(searchParams);
|
||||||
|
goodsList.value = res.data.rows;
|
||||||
|
} catch (e) {
|
||||||
|
showDialog({ message: '加载失败,请重试' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const onLoad = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getGoodsList(searchParams);
|
||||||
|
goodsList.value.push(...res.data.rows);
|
||||||
|
loading.value = false;
|
||||||
|
|
||||||
|
if (goodsList.value.length >= res.data.total) {
|
||||||
|
finished.value = true;
|
||||||
|
} else {
|
||||||
|
if (searchParams.pageNum) {
|
||||||
|
searchParams.pageNum++;
|
||||||
|
} else {
|
||||||
|
searchParams.pageNum = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取商品列表失败', error);
|
||||||
|
finished.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除商品
|
||||||
|
const handleDelete = async (ids: number[]) => {
|
||||||
|
await showConfirmDialog({ message: '确认删除选中商品?' });
|
||||||
|
await deleteGoods(ids);
|
||||||
|
showSuccessToast('删除成功');
|
||||||
|
await fetchGoodsList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const submitForm = async () => {
|
||||||
|
try {
|
||||||
|
if (isEditMode.value) {
|
||||||
|
await updateGoods(currentGoods.value.goodsId!, currentGoods.value);
|
||||||
|
} else {
|
||||||
|
await addGoods({
|
||||||
|
goodsName: currentGoods.value.goodsName || '',
|
||||||
|
categoryId: currentGoods.value.categoryId || 0,
|
||||||
|
price: currentGoods.value.price || 0,
|
||||||
|
stock: currentGoods.value.stock || 0,
|
||||||
|
status: currentGoods.value.status || 1,
|
||||||
|
autoApproval: currentGoods.value.autoApproval || 0,
|
||||||
|
coverImg: currentGoods.value.coverImg || '',
|
||||||
|
usageInstruction: currentGoods.value.usageInstruction
|
||||||
|
});
|
||||||
|
}
|
||||||
|
showSuccessToast('操作成功');
|
||||||
|
showEditDialog.value = false;
|
||||||
|
await fetchGoodsList();
|
||||||
|
} catch (e) {
|
||||||
|
showDialog({ message: '操作失败' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开编辑弹窗
|
||||||
|
const openEdit = (goods?: ShopGoodsDTO) => {
|
||||||
|
currentGoods.value = goods ? { ...goods } : { status: 1 };
|
||||||
|
isEditMode.value = !!goods;
|
||||||
|
showEditDialog.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(fetchGoodsList);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="goods-manage">
|
||||||
|
<!-- 搜索栏和操作按钮 -->
|
||||||
|
<div class="search-action-bar">
|
||||||
|
<van-search v-model="searchParams.goodsName" placeholder="输入商品名称搜索" @search="fetchGoodsList" />
|
||||||
|
<van-button type="primary" @click="openEdit">添加商品</van-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 商品表格 -->
|
||||||
|
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
|
||||||
|
<div class="goods-grid">
|
||||||
|
<van-cell v-for="item in goodsList" :key="item.goodsId" class="goods-card">
|
||||||
|
<template #icon>
|
||||||
|
<van-image :src="item.coverImg" width="80" height="80" class="goods-image">
|
||||||
|
<div v-if="item.stock === 0" class="sold-out-overlay">
|
||||||
|
<span class="sold-out-text">已售罄</span>
|
||||||
|
</div>
|
||||||
|
<template #error>
|
||||||
|
<div class="custom-error">
|
||||||
|
图片加载失败
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</van-image>
|
||||||
|
</template>
|
||||||
|
<div class="goods-info">
|
||||||
|
<div class="goods-name van-ellipsis">{{ item.goodsName }}</div>
|
||||||
|
<div class="goods-price">¥{{ item.price?.toFixed(2) }}</div>
|
||||||
|
<div class="action-row">
|
||||||
|
<span v-if="item.stock! > 0" class="stock-count">库存: {{ item.stock }}</span>
|
||||||
|
<div class="goods-actions">
|
||||||
|
<van-button size="mini" @click="openEdit(item)">编辑</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-cell>
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
|
||||||
|
<!-- 编辑弹窗 -->
|
||||||
|
<van-dialog v-model:show="showEditDialog" :title="isEditMode ? '编辑商品' : '新增商品'">
|
||||||
|
<van-form @submit="submitForm">
|
||||||
|
<van-cell-group inset>
|
||||||
|
<van-field v-model="currentGoods.goodsName" label="商品名称" placeholder="请输入"
|
||||||
|
:rules="[{ required: true }]" />
|
||||||
|
<van-field v-model="currentGoods.price" label="价格" type="number" placeholder="请输入" />
|
||||||
|
<van-field v-model="currentGoods.stock" label="库存" type="number" placeholder="请输入" />
|
||||||
|
</van-cell-group>
|
||||||
|
<div style="padding: 16px">
|
||||||
|
<van-button block type="primary" native-type="submit">提交</van-button>
|
||||||
|
</div>
|
||||||
|
</van-form>
|
||||||
|
</van-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.goods-manage {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-action-bar {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-action-bar .van-search {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 4px;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: min(2.667vw, 20px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-image {
|
||||||
|
margin-right: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 80px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-price {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #e95d5d;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-count {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #bbbbbb;
|
||||||
|
margin-right: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sold-out-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sold-out-text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
transform: rotate(-15deg);
|
||||||
|
border: 1px solid #eee;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-error {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,16 +1,17 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { useWxStore } from '@/pinia/stores/wx'
|
import { useWxStore } from '@/pinia/stores/wx'
|
||||||
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
|
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { publicPath } from "@/common/utils/path"
|
import { publicPath } from "@/common/utils/path"
|
||||||
import { showConfirmDialog } from 'vant';
|
import { showConfirmDialog } from 'vant';
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
const wxStore = useWxStore()
|
const route = useRoute();
|
||||||
const ab98UserStore = useAb98UserStore()
|
const wxStore = useWxStore();
|
||||||
|
const ab98UserStore = useAb98UserStore();
|
||||||
|
|
||||||
const { balance, name: qyName } = storeToRefs(wxStore);
|
const { balance, useBalance, balanceLimit, name: qyName } = storeToRefs(wxStore);
|
||||||
const { name: userName, sex: userSex, face_img } = storeToRefs(ab98UserStore);
|
const { name: userName, sex: userSex, face_img } = storeToRefs(ab98UserStore);
|
||||||
|
|
||||||
const name = computed(() => {
|
const name = computed(() => {
|
||||||
|
@ -29,7 +30,9 @@ const handleLogout = () => {
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// 取消操作
|
// 取消操作
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
wxStore.refreshBalance();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -61,9 +64,17 @@ const handleLogout = () => {
|
||||||
|
|
||||||
<!-- 余额区域 -->
|
<!-- 余额区域 -->
|
||||||
<div class="balance-card flex flex-wrap justify-between items-center p-4 bg-white rounded-lg shadow-sm un-mt-16px">
|
<div class="balance-card flex flex-wrap justify-between items-center p-4 bg-white rounded-lg shadow-sm un-mt-16px">
|
||||||
<van-icon name="gold-coin" size="28px" class="un-mr-8px un-color-#ffb300!" />
|
<van-icon name="gold-coin" size="28px" class="un-mr-8px un-color-#ffb300! mr-2" />
|
||||||
<div class="flex-1 ml-2">
|
<div class="flex-1 ml-1">
|
||||||
<div class="text-sm text-gray-700">余额</div>
|
<div class="text-sm text-gray-700">借呗总额</div>
|
||||||
|
<div class="text-lg font-bold text-primary">{{ balanceLimit }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 ml-1">
|
||||||
|
<div class="text-sm text-gray-700">未还借呗</div>
|
||||||
|
<div class="text-lg font-bold text-primary">{{ useBalance }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 ml-1">
|
||||||
|
<div class="text-sm text-gray-700">剩余借呗</div>
|
||||||
<div class="text-lg font-bold text-primary">{{ balance }}</div>
|
<div class="text-lg font-bold text-primary">{{ balance }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -73,19 +84,25 @@ const handleLogout = () => {
|
||||||
<van-col span="24">
|
<van-col span="24">
|
||||||
<div class="section-title text-sm font-bold pb-2">个人中心</div>
|
<div class="section-title text-sm font-bold pb-2">个人中心</div>
|
||||||
</van-col>
|
</van-col>
|
||||||
<van-col span="8">
|
<van-col span="6">
|
||||||
<div class="custom-btn" @click="router.push('/order-list')">
|
<div class="custom-btn" @click="router.push('/order-list')">
|
||||||
<van-icon name="orders-o" size="20px" />
|
<van-icon name="orders-o" size="20px" />
|
||||||
<span>订单列表</span>
|
<span>订单列表</span>
|
||||||
</div>
|
</div>
|
||||||
</van-col>
|
</van-col>
|
||||||
<van-col span="8">
|
<van-col span="6">
|
||||||
|
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/manage/goods')">
|
||||||
|
<van-icon name="comment-o" size="20px" />
|
||||||
|
<span>商品管理</span>
|
||||||
|
</div>
|
||||||
|
</van-col>
|
||||||
|
<van-col span="6">
|
||||||
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/cabinet')">
|
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/cabinet')">
|
||||||
<van-icon name="manager-o" size="20px" />
|
<van-icon name="manager-o" size="20px" />
|
||||||
<span>柜机管理</span>
|
<span>柜机管理</span>
|
||||||
</div>
|
</div>
|
||||||
</van-col>
|
</van-col>
|
||||||
<van-col span="8">
|
<van-col span="6">
|
||||||
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/approval/list')">
|
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/approval/list')">
|
||||||
<van-icon name="comment-o" size="20px" />
|
<van-icon name="comment-o" size="20px" />
|
||||||
<span>审批中心</span>
|
<span>审批中心</span>
|
||||||
|
|
|
@ -21,8 +21,6 @@ const { labels, categories } = storeToRefs(productStore)
|
||||||
|
|
||||||
// 当前选中的分类索引
|
// 当前选中的分类索引
|
||||||
const activeCategory = ref(0)
|
const activeCategory = ref(0)
|
||||||
// 存储每个分类标题的 DOM 元素引用
|
|
||||||
const categoryRefs = ref<HTMLElement[]>([])
|
|
||||||
// 滚动容器引用
|
// 滚动容器引用
|
||||||
const scrollContainer = ref<HTMLElement>()
|
const scrollContainer = ref<HTMLElement>()
|
||||||
// 顶部头图高度(随滚动变化)
|
// 顶部头图高度(随滚动变化)
|
||||||
|
@ -47,14 +45,10 @@ const searchQuery = ref('')
|
||||||
// 点击分类导航
|
// 点击分类导航
|
||||||
function handleCategoryClick(index: number) {
|
function handleCategoryClick(index: number) {
|
||||||
activeCategory.value = index
|
activeCategory.value = index
|
||||||
categoryRefs.value[index].scrollIntoView({
|
|
||||||
behavior: "smooth",
|
|
||||||
block: "start"
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 节流的滚动位置计算(用于切换左侧导航激活状态)
|
// 节流的滚动位置计算(用于切换左侧导航激活状态)
|
||||||
const throttledUpdate = throttle(() => {
|
/* const throttledUpdate = throttle(() => {
|
||||||
if (!scrollContainer.value || !categoryRefs.value.length) return
|
if (!scrollContainer.value || !categoryRefs.value.length) return
|
||||||
|
|
||||||
// 添加数组元素存在性检查
|
// 添加数组元素存在性检查
|
||||||
|
@ -73,7 +67,7 @@ const throttledUpdate = throttle(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// activeCategory.value = activeIndex
|
// activeCategory.value = activeIndex
|
||||||
}, 100)
|
}, 100) */
|
||||||
|
|
||||||
// 节流的头部高度计算(实现顶部图片缩放效果)
|
// 节流的头部高度计算(实现顶部图片缩放效果)
|
||||||
const throttledScroll = throttle(() => {
|
const throttledScroll = throttle(() => {
|
||||||
|
@ -117,7 +111,7 @@ const currentProducts = computed(() => {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
productStore.getGoods();
|
productStore.getGoods();
|
||||||
scrollListener.push(scrollContainer.value?.addEventListener("scroll", throttledScroll))
|
scrollListener.push(scrollContainer.value?.addEventListener("scroll", throttledScroll))
|
||||||
scrollListener.push(scrollContainer.value?.addEventListener("scroll", throttledUpdate))
|
// scrollListener.push(scrollContainer.value?.addEventListener("scroll", throttledUpdate))
|
||||||
})
|
})
|
||||||
|
|
||||||
// 组件卸载前清理事件监听
|
// 组件卸载前清理事件监听
|
||||||
|
@ -159,7 +153,7 @@ watch(() => route.path, (newPath) => {
|
||||||
shape="round"
|
shape="round"
|
||||||
class="search-box"
|
class="search-box"
|
||||||
/>
|
/>
|
||||||
<div :ref="el => categoryRefs[0] = el as HTMLElement" class="category-section">
|
<div class="category-section">
|
||||||
<van-cell v-for="product in currentProducts" :key="product.id" class="product-item">
|
<van-cell v-for="product in currentProducts" :key="product.id" class="product-item">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<van-image :src="product.image" width="80" height="80" @click.stop="showProductDetail(product.id)"
|
<van-image :src="product.image" width="80" height="80" @click.stop="showProductDetail(product.id)"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { pinia } from "@/pinia"
|
import { pinia } from "@/pinia"
|
||||||
import { getOpenIdApi, getBalanceApi, qyLogin, getBalanceByQyUserid } from "@/common/apis/shop"
|
import { getOpenIdApi, getBalanceApi, qyLogin, getBalanceByQyUserid } from "@/common/apis/shop"
|
||||||
|
import { GetBalanceResponse } from "@/common/apis/shop/type"
|
||||||
|
|
||||||
|
|
||||||
export const useWxStore = defineStore("wx", () => {
|
export const useWxStore = defineStore("wx", () => {
|
||||||
|
@ -11,8 +12,12 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
const openid = ref<string>("")
|
const openid = ref<string>("")
|
||||||
// 用户 userid
|
// 用户 userid
|
||||||
const userid = ref<string>("");
|
const userid = ref<string>("");
|
||||||
// 用户余额
|
// 剩余借呗
|
||||||
const balance = ref<number>(0);
|
const balance = ref<number>(0);
|
||||||
|
// 已用借呗
|
||||||
|
const useBalance = ref<number>(0);
|
||||||
|
// 借呗总额
|
||||||
|
const balanceLimit = ref<number>(0);
|
||||||
// 企业id
|
// 企业id
|
||||||
const corpid = ref<string>("");
|
const corpid = ref<string>("");
|
||||||
// 是否企业微信登录
|
// 是否企业微信登录
|
||||||
|
@ -34,6 +39,17 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
const setIsCabinetAdmin = (isAdmin: boolean) => {
|
const setIsCabinetAdmin = (isAdmin: boolean) => {
|
||||||
isCabinetAdmin.value = isAdmin;
|
isCabinetAdmin.value = isAdmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const refreshBalance = async () => {
|
||||||
|
if (corpid.value && userid.value) {
|
||||||
|
const res = await getBalanceByQyUserid(corpid.value, userid.value);
|
||||||
|
if (res && res.code == 0) {
|
||||||
|
balance.value = res.data.balance;
|
||||||
|
useBalance.value = res.data.useBalance;
|
||||||
|
balanceLimit.value = res.data.balanceLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleWxCallback = async (params: { corpid?: string; code?: string; state?: string; }) => {
|
const handleWxCallback = async (params: { corpid?: string; code?: string; state?: string; }) => {
|
||||||
console.log('handleWxCallback:', params)
|
console.log('handleWxCallback:', params)
|
||||||
|
@ -65,7 +81,7 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
|
|
||||||
if (openid.value) {
|
if (openid.value) {
|
||||||
// 获取用户余额
|
// 获取用户余额
|
||||||
let balanceRes = null;
|
let balanceRes: ApiResponseData<GetBalanceResponse> | null = null;
|
||||||
|
|
||||||
if(corpid.value) {
|
if(corpid.value) {
|
||||||
balanceRes = await getBalanceByQyUserid(corpid.value, userid.value);
|
balanceRes = await getBalanceByQyUserid(corpid.value, userid.value);
|
||||||
|
@ -74,7 +90,9 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
}
|
}
|
||||||
console.log('获取余额成功:', balanceRes)
|
console.log('获取余额成功:', balanceRes)
|
||||||
if (balanceRes && balanceRes.code == 0) {
|
if (balanceRes && balanceRes.code == 0) {
|
||||||
balance.value = balanceRes.data.balance
|
balance.value = balanceRes.data.balance;
|
||||||
|
useBalance.value = balanceRes.data.useBalance;
|
||||||
|
balanceLimit.value = balanceRes.data.balanceLimit;
|
||||||
if (!userid.value) {
|
if (!userid.value) {
|
||||||
userid.value = balanceRes.data.userid;
|
userid.value = balanceRes.data.userid;
|
||||||
}
|
}
|
||||||
|
@ -90,8 +108,8 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { code, state, openid, corpid, userid, balance, isCabinetAdmin, corpidLogin, name,
|
return { code, state, openid, corpid, userid, balance, useBalance, balanceLimit, isCabinetAdmin, corpidLogin, name,
|
||||||
setOpenid, setBalance, handleWxCallback, setIsCabinetAdmin }
|
setOpenid, setBalance, handleWxCallback, setIsCabinetAdmin, refreshBalance }
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,6 +25,10 @@ export function registerNavigationGuard(router: Router) {
|
||||||
if (corpid) {
|
if (corpid) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
/* const isAdmin = urlParams.get('isAdmin') || undefined;
|
||||||
|
if (isAdmin) {
|
||||||
|
return true;
|
||||||
|
} */
|
||||||
|
|
||||||
// useAb98UserStore位置不能放在外面,否则会导致路由守卫无法正常工作
|
// useAb98UserStore位置不能放在外面,否则会导致路由守卫无法正常工作
|
||||||
const ab98UserStore = useAb98UserStore();
|
const ab98UserStore = useAb98UserStore();
|
||||||
|
|
|
@ -111,6 +111,25 @@ export const routes: RouteRecordRaw[] = [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/* {
|
||||||
|
path: '/manage/goods',
|
||||||
|
component: () => import('@/pages/manage/goods/goodsList.vue'),
|
||||||
|
name: "ManageGoods",
|
||||||
|
meta: {
|
||||||
|
title: '商品管理',
|
||||||
|
keepAlive: false,
|
||||||
|
layout: {
|
||||||
|
navBar: {
|
||||||
|
showNavBar: true,
|
||||||
|
showLeftArrow: true
|
||||||
|
},
|
||||||
|
tabbar: {
|
||||||
|
showTabbar: false,
|
||||||
|
icon: "home-o"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, */
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
component: () => import("@/pages/product/ProductList.vue"),
|
component: () => import("@/pages/product/ProductList.vue"),
|
||||||
|
|
Loading…
Reference in New Issue