feat(shop): 添加商店列表API和审批中心页面

- 在shop模块中添加getShopListApi方法用于获取商店列表
- 在智能柜页面中增加商店选择功能
- 新增审批中心页面,包含申请类型展示和导航功能
This commit is contained in:
dzq 2025-06-23 17:43:54 +08:00
parent 07a27fa13d
commit 52f8cbff4a
3 changed files with 331 additions and 5 deletions

View File

@ -98,6 +98,13 @@ export const getShopList = (params?: ShopQuery) => {
});
};
/** 获取商店列表 */
export const getShopListApi = (params?: ShopQuery) => {
return http.request<ResponseData<ShopDTO[]>>('get', '/shop/shops/list', {
params
});
};
/** 获取商店详情 */
export const getShopById = (id: number) => {
return http.request<ResponseData<ShopDTO>>('get', `/shop/shops/${id}`);

View File

@ -6,6 +6,7 @@ import { type PaginationProps } from "@pureadmin/table";
import { CommonUtils } from "@/utils/common";
import { useRouter } from "vue-router";
import { CabinetImgMap } from "@/utils/cabinetImgMap";
import { getShopListApi, type ShopDTO } from "@/api/shop/shop";
import Search from "@iconify-icons/ep/search";
import Refresh from "@iconify-icons/ep/refresh";
@ -27,11 +28,13 @@ const formRef = ref();
const modalVisible = ref(false);
const searchFormParams = ref({
cabinetName: "",
cabinetType: null
cabinetType: null,
shopId: null
});
const pageLoading = ref(false);
const dataList = ref<SmartCabinetDTO[]>([]);
const shopList = ref<ShopDTO[]>([]);
const pagination = ref<PaginationProps>({
total: 0,
pageSize: 12,
@ -59,6 +62,15 @@ async function getList() {
}
}
async function getShopList() {
try {
const { data } = await getShopListApi();
shopList.value = data;
} catch (error) {
console.error("获取店铺列表失败", error);
}
}
const resetForm = formEl => {
if (!formEl) return;
formEl.resetFields();
@ -86,6 +98,7 @@ const handleViewDetail = (row: SmartCabinetDTO) => {
onMounted(() => {
getList();
getShopList();
});
</script>
@ -98,10 +111,9 @@ onMounted(() => {
<el-input @keydown.enter.prevent="onSearch" v-model="searchFormParams.cabinetName" placeholder="请输入柜体名称"
clearable class="!w-[200px]" />
</el-form-item>
<el-form-item label="" prop="cabinetType">
<el-select v-model="searchFormParams.cabinetType" placeholder="请选择柜体类型" clearable class="!w-[180px]">
<el-option label="主柜" :value="0" />
<el-option label="副柜" :value="1" />
<el-form-item label="" prop="shopId">
<el-select v-model="searchFormParams.shopId" placeholder="请选择机柜地址" clearable class="!w-[180px]">
<el-option v-for="shop in shopList" :key="shop.shopId" :label="shop.shopName" :value="shop.shopId" />
</el-select>
</el-form-item>
<el-form-item>

View File

@ -0,0 +1,307 @@
<script setup lang="ts">
import { ref, computed, markRaw } from 'vue';
import {
Calendar,
Briefcase,
Clock,
User,
Box,
Download,
Key,
UserFilled,
Delete,
RefreshLeft,
Check,
House,
Medal,
Document,
Money,
ArrowRight,
RefreshRight,
Wallet,
CreditCard,
} from '@element-plus/icons-vue';
defineOptions({
name: "approvalCenter"
});
//
const activeMenu = ref('apply');
//
const activeTab = ref('all');
//
const searchKey = ref('');
//
const applicationData = ref({
//
all: [],
//
shop: [
{ icon: markRaw(Calendar), name: '借用', color: '#f9a825' },
{ icon: markRaw(Box), name: '耗材领用', color: '#ff9800' },
{ icon: markRaw(Download), name: '商品归还', color: '#00bcd4' },
],
//
other: [
{ icon: markRaw(CreditCard), name: '借呗', color: '#00bcd4' },
{ icon: markRaw(Wallet), name: '退款', color: '#795548' }
],
});
//
applicationData.value.all = [
...applicationData.value.shop,
...applicationData.value.other,
];
//
const filteredApplications = computed(() => {
const data = applicationData.value[activeTab.value] || [];
if (!searchKey.value) return data;
return data.filter(item => item.name.includes(searchKey.value));
});
//
const handleMenuSelect = (index: string) => {
activeMenu.value = index;
};
//
const handleTabChange = (tab: any) => {
activeTab.value = tab.name;
};
//
const handleSearch = () => {
console.log('搜索关键词:', searchKey.value);
};
//
const handleCardClick = (item: any) => {
console.log('点击申请类型:', item.name);
};
</script>
<template>
<div class="approval-container">
<!-- 左侧导航栏 -->
<div class="sidebar">
<div class="custom-menu">
<!-- 发起申请无子类 -->
<div class="menu-item" :class="{ 'active': activeMenu === 'apply' }" @click="handleMenuSelect('apply')">
发起申请
</div>
<!-- 我收到的有子类 -->
<div class="sub-menu">
<div class="sub-menu-title"><span>我收到的</span></div>
<div class="sub-menu-items">
<div class="menu-item child" :class="{ 'active': activeMenu === 'received-pending' }"
@click="handleMenuSelect('received-pending')">
待处理
</div>
<div class="menu-item child" :class="{ 'active': activeMenu === 'received-processed' }"
@click="handleMenuSelect('received-processed')">
已处理
</div>
<div class="menu-item child" :class="{ 'active': activeMenu === 'cc' }"
@click="handleMenuSelect('cc')">
已处理
</div>
</div>
</div>
<!-- 我提交的有子类 -->
<div class="sub-menu">
<div class="sub-menu-title"><span>我提交的</span></div>
<div class="sub-menu-items">
<div class="menu-item child" :class="{ 'active': activeMenu === 'submitted-sent' }"
@click="handleMenuSelect('submitted-sent')">
已提交
</div>
</div>
</div>
</div>
</div>
<!-- 右侧内容区域 -->
<div class="content">
<!-- 顶部导航标签栏+搜索 -->
<div class="content-header">
<el-tabs v-model="activeTab" type="card" @tab-click="handleTabChange">
<el-tab-pane label="全部" name="all"></el-tab-pane>
<el-tab-pane label="借还审批" name="shop"></el-tab-pane>
<el-tab-pane label="其他" name="other"></el-tab-pane>
</el-tabs>
<el-input v-model="searchKey" placeholder="搜索" prefix-icon="ep:search" class="search-input"
@keydown.enter.prevent="handleSearch" />
</div>
<!-- 申请类型列表根据标签栏切换 -->
<div class="application-list">
<el-row :gutter="16">
<el-col v-for="(item, index) in filteredApplications" :key="index" :xs="24" :sm="12" :md="8" :lg="6" :xl="4">
<div class="application-card" @click="handleCardClick(item)">
<el-icon class="card-icon" :style="{ color: item.color }">
<component :is="item.icon" />
</el-icon>
<div class="card-name">{{ item.name }}</div>
</div>
</el-col>
</el-row>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.approval-container {
display: flex;
height: 100vh;
overflow: hidden;
}
/* 左侧导航栏 */
.sidebar {
width: 200px;
background-color: #f8f8f8;
border-right: 1px solid #eee;
padding-top: 20px;
.custom-menu {
border-right: none;
//
.menu-item {
height: 50px;
line-height: 50px;
padding-left: 20px;
margin-bottom: 10px;
cursor: pointer;
&.active {
background-color: #f0f0f0;
color: #333333;
}
&:hover {
background-color: #f0f0f0;
color: #333333;
}
&.child {
padding-left: 40px;
margin-bottom: 5px;
}
}
//
.sub-menu {
margin-bottom: 10px;
.sub-menu-title {
height: 50px;
line-height: 50px;
padding-left: 20px;
transition: background-color 0.3s;
cursor: default;
}
.sub-menu-items {
margin-left: 10px;
}
}
}
}
/* 右侧内容区域 */
.content {
flex: 1;
padding: 20px;
overflow-y: auto;
background-color: #FFFFFF;
// +
.content-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
.el-tabs {
flex: 1;
.el-tabs__nav {
border-bottom: none;
.el-tabs__item {
padding: 0 20px;
height: 40px;
line-height: 40px;
border-radius: 8px 8px 0 0;
margin-right: 10px;
transition: background-color 0.3s;
&.is-active {
background-color: #fff;
border: 1px solid #eee;
border-bottom: none;
font-weight: bold;
}
&:hover {
background-color: #f8f8f8;
}
}
}
}
.search-input {
width: 200px;
}
}
//
.application-list {
margin-top: 20px;
}
//
.application-card {
width: 100%;
height: 120px;
margin-bottom: 12px;
border: 1px solid #eee;
border-radius: 8px;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-3px);
}
.card-icon {
font-size: 40px;
margin-bottom: 10px;
}
.card-name {
font-size: 14px;
color: #333;
}
}
}
</style>