feat: 新增横屏暂存柜管理页面及相关功能
refactor(router): 添加横屏路由配置并优化路由守卫逻辑 feat(api): 新增企业corpid查询接口 feat(components): 实现地址选择面板组件 feat(pinia): 扩展wx store添加corpid设置方法 style(images): 更新柜格图标为更清晰的SVG版本 fix(postcss): 调整移动端适配配置
|
|
@ -3,7 +3,9 @@
|
|||
"allow": [
|
||||
"Bash(mkdir:*)",
|
||||
"Bash(tree -L 3 -I 'node_modules|dist')",
|
||||
"Bash(cat:*)"
|
||||
"Bash(cat:*)",
|
||||
"Bash(dir:*)",
|
||||
"Bash(find:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export default {
|
|||
// UI 设计稿宽度
|
||||
viewportWidth: (file: string) => file.includes("vant") ? 375 : 375,
|
||||
// 限制视图的最大宽度
|
||||
maxDisplayWidth: 750,
|
||||
// maxDisplayWidth: 750,
|
||||
// 页面最外层选择器
|
||||
appSelector: "#app",
|
||||
// 是否对「页面最外层选择器」对应的元素进行描边
|
||||
|
|
@ -18,7 +18,8 @@ export default {
|
|||
// 转换后的单位
|
||||
mobileUnit: "vw",
|
||||
// 需要转换的属性
|
||||
propList: ["*"],
|
||||
// propList: ["*"],
|
||||
propList: [],
|
||||
// 忽略的选择器
|
||||
selectorBlackList: [".ignore", "keep-px"],
|
||||
// 忽略的属性
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" rx="10" fill="#E91E63" opacity="0.8"/>
|
||||
<text x="50" y="55" font-family="Arial" font-size="16" font-weight="bold" fill="white" text-anchor="middle">超大格</text>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
|
||||
<path fill="currentColor"
|
||||
d="M200 24H56a12 12 0 0 0-12 12v184a12 12 0 0 0 12 12h144a12 12 0 0 0 12-12V36a12 12 0 0 0-12-12m4 196a4 4 0 0 1-4 4H56a4 4 0 0 1-4-4V36a4 4 0 0 1 4-4h144a4 4 0 0 1 4 4v184Zm-64-92a4 4 0 0 1-4 4h-16a4 4 0 0 1 0-8h16a4 4 0 0 1 4 4" />
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 367 B |
|
|
@ -1,4 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" rx="10" fill="#FF9800" opacity="0.8"/>
|
||||
<text x="50" y="55" font-family="Arial" font-size="16" font-weight="bold" fill="white" text-anchor="middle">大格</text>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
|
||||
<path fill="currentColor"
|
||||
d="M200 48H56a12 12 0 0 0-12 12v136a12 12 0 0 0 12 12h144a12 12 0 0 0 12-12V60a12 12 0 0 0-12-12m4 148a4 4 0 0 1-4 4H56a4 4 0 0 1-4-4V60a4 4 0 0 1 4-4h144a4 4 0 0 1 4 4v136Zm-64-68a4 4 0 0 1-4 4h-16a4 4 0 0 1 0-8h16a4 4 0 0 1 4 4" />
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 367 B |
|
|
@ -1,4 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" rx="10" fill="#2196F3" opacity="0.8"/>
|
||||
<text x="50" y="55" font-family="Arial" font-size="16" font-weight="bold" fill="white" text-anchor="middle">中格</text>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
|
||||
<path fill="currentColor"
|
||||
d="M200 72H56a12 12 0 0 0-12 12v88a12 12 0 0 0 12 12h144a12 12 0 0 0 12-12V84a12 12 0 0 0-12-12m4 100a4 4 0 0 1-4 4H56a4 4 0 0 1-4-4V84a4 4 0 0 1 4-4h144a4 4 0 0 1 4 4v88Zm-64-44a4 4 0 0 1-4 4h-16a4 4 0 0 1 0-8h16a4 4 0 0 1 4 4" />
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 365 B |
|
|
@ -1,4 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" rx="10" fill="#4CAF50" opacity="0.8"/>
|
||||
<text x="50" y="55" font-family="Arial" font-size="16" font-weight="bold" fill="white" text-anchor="middle">小格</text>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
|
||||
<path fill="currentColor"
|
||||
d="M200 96H56a12 12 0 0 0-12 12v40a12 12 0 0 0 12 12h144a12 12 0 0 0 12-12v-40a12 12 0 0 0-12-12m4 52a4 4 0 0 1-4 4H56a4 4 0 0 1-4-4v-40a4 4 0 0 1 4-4h144a4 4 0 0 1 4 4v40Zm-64-20a4 4 0 0 1-4 4h-16a4 4 0 0 1 0-8h16a4 4 0 0 1 4 4" />
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 366 B |
18
src/App.vue
|
|
@ -6,6 +6,7 @@ import { useWxStore } from "@/pinia/stores/wx"
|
|||
import { tokenLogin } from '@/common/apis/ab98'
|
||||
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
|
||||
import { useProductStore } from "./pinia/stores/product"
|
||||
import { getCorpidByIdApi } from "./common/apis/qy"
|
||||
|
||||
// const userStore = useUserStore()
|
||||
const wxStore = useWxStore();
|
||||
|
|
@ -30,14 +31,27 @@ const isLoading = false;
|
|||
// )
|
||||
|
||||
initDark()
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
console.log('urlParams', urlParams);
|
||||
const code = urlParams.get('code') || undefined;
|
||||
const state = urlParams.get('state') || undefined;
|
||||
const corpid = urlParams.get('corpid') || undefined;
|
||||
let corpid = urlParams.get('corpid') || undefined;
|
||||
const cid = urlParams.get('cid') || undefined;
|
||||
let isAdmin = urlParams.get('isAdmin') || undefined;
|
||||
|
||||
if (cid && Number(cid)) {
|
||||
try {
|
||||
const corpidRes = await getCorpidByIdApi(Number(cid));
|
||||
if (corpidRes?.code === 0 && corpidRes.data) {
|
||||
corpid = corpidRes.data;
|
||||
wxStore.setCorpid(corpid);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('getCorpidByIdApi error', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (state && state.indexOf('token') !== -1) {
|
||||
const token = state.split('token_')[1];
|
||||
if (token) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import { request } from "@/http/axios"
|
||||
|
||||
/** 根据企业ID查询corpid */
|
||||
export function getCorpidByIdApi(id: number) {
|
||||
return request<ApiResponseData<string>>({
|
||||
url: "qy/getCorpidById",
|
||||
method: "get",
|
||||
params: { id }
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
<template>
|
||||
<div v-if="showPanel" class="address-selection-panel">
|
||||
<!-- 提示区域 -->
|
||||
<div class="shop-prompt">
|
||||
<van-cell title="请选择机柜地址:" center />
|
||||
</div>
|
||||
|
||||
<!-- 商店网格 -->
|
||||
<div class="shop-grid">
|
||||
<div
|
||||
v-for="shop in shopList"
|
||||
:key="shop.shopId"
|
||||
class="shop-item"
|
||||
@click="handleShopSelect(shop.shopId)"
|
||||
>
|
||||
<van-image
|
||||
:src="shop.coverImg || `${publicPath}product-image.png`"
|
||||
class="shop-cover-img"
|
||||
fit="cover"
|
||||
/>
|
||||
<div class="shop-info">
|
||||
<van-icon name="shop-o" size="16" class="shop-icon" />
|
||||
<div class="shop-name van-ellipsis">{{ shop.shopName }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 占位列,确保每行4列 -->
|
||||
<div
|
||||
v-for="n in (4 - (shopList.length % 4))"
|
||||
:key="'placeholder-' + n"
|
||||
v-if="shopList.length % 4 !== 0"
|
||||
class="shop-item placeholder-col"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading-state">
|
||||
<van-loading type="spinner" />
|
||||
<span class="loading-text">正在加载地址列表...</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { publicPath } from "@/common/utils/path"
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
shopList: any[]
|
||||
loading?: boolean
|
||||
showPanel?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
shopList: () => [],
|
||||
loading: false,
|
||||
showPanel: true
|
||||
})
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
(e: 'select', shopId: number): void
|
||||
}>()
|
||||
|
||||
// 处理商店选择
|
||||
const handleShopSelect = (shopId: number) => {
|
||||
emit('select', shopId)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.address-selection-panel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
max-width: 100vw !important;
|
||||
max-height: 100vh !important;
|
||||
overflow: hidden;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.shop-prompt {
|
||||
flex-shrink: 0;
|
||||
margin: 8px 16px;
|
||||
height: 44px !important;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.placeholder-col {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.shop-grid {
|
||||
flex: 1;
|
||||
margin: 8px 16px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
row-gap: 8px;
|
||||
column-gap: 6px;
|
||||
overflow-y: auto;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.shop-item {
|
||||
min-height: 150px;
|
||||
padding: 0;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.shop-item:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.shop-cover-img {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
flex-shrink: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.shop-icon {
|
||||
margin-right: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 64px 0;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 24px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -80,20 +80,27 @@
|
|||
v-model:show="popupVisible"
|
||||
position="bottom"
|
||||
round
|
||||
:style="{ padding: '32px', background: '#fff', maxHeight: '80vh' }"
|
||||
:style="popupStyle"
|
||||
@close="handlePopupCancel">
|
||||
|
||||
<div class="popup-content">
|
||||
<div class="popup-content" :style="popupContentStyle">
|
||||
<!-- 标题 -->
|
||||
<div class="popup-title">{{ popupTitle }}</div>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<div class="popup-message">{{ popupMessage }}</div>
|
||||
<!-- 密码显示区域(仅在密码展示状态显示) -->
|
||||
<div v-if="currentState.type === 'password-show'" class="password-display-area">
|
||||
<div class="password-display-label">您的暂存密码</div>
|
||||
<div class="password-display-value">{{ currentState.password }}</div>
|
||||
<div class="password-display-hint">请务必牢记此密码</div>
|
||||
</div>
|
||||
|
||||
<!-- 消息内容(密码展示状态不显示) -->
|
||||
<div v-if="currentState.type !== 'password-show'" class="popup-message">{{ popupMessage }}</div>
|
||||
|
||||
<!-- 密码输入框 -->
|
||||
<van-password-input
|
||||
v-if="currentState.type === 'password-verify' || currentState.type === 'retrieve-input'"
|
||||
v-model="popupInputValue"
|
||||
:value="popupInputValue"
|
||||
:length="passwordLength"
|
||||
:gutter="8"
|
||||
:mask="true"
|
||||
|
|
@ -132,11 +139,12 @@
|
|||
<!-- 数字键盘 -->
|
||||
<van-number-keyboard
|
||||
v-model="popupInputValue"
|
||||
v-model:show="keyboardVisible"
|
||||
:show="keyboardVisible"
|
||||
:maxlength="passwordLength"
|
||||
theme="custom"
|
||||
close-button-text="完成"
|
||||
:safe-area-inset-bottom="true" />
|
||||
:safe-area-inset-bottom="true"
|
||||
@blur="handleKeyboardClose" />
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
|
|
@ -152,6 +160,7 @@ import {
|
|||
} from '@/common/apis/cabinet'
|
||||
import type { AvailableStorageCellDTO } from '@/common/apis/cabinet/type'
|
||||
import { usePopupState } from '@/common/composables/usePopupState'
|
||||
import { publicPath } from '@/common/utils/path'
|
||||
|
||||
// 格口类型映射
|
||||
const CELL_TYPE_MAP = {
|
||||
|
|
@ -214,6 +223,36 @@ const {
|
|||
handleKeyboardClose
|
||||
} = usePopupState()
|
||||
|
||||
// 弹窗样式计算属性
|
||||
const popupStyle = computed(() => {
|
||||
const baseStyle = {
|
||||
padding: '32px',
|
||||
background: '#fff'
|
||||
}
|
||||
|
||||
if (keyboardVisible.value) {
|
||||
return {
|
||||
...baseStyle,
|
||||
maxHeight: 'calc(100vh - 360px)',
|
||||
overflow: 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...baseStyle,
|
||||
maxHeight: '80vh'
|
||||
}
|
||||
})
|
||||
|
||||
const popupContentStyle = computed(() => {
|
||||
if (keyboardVisible.value) {
|
||||
return {
|
||||
paddingBottom: '160px'
|
||||
}
|
||||
}
|
||||
return {}
|
||||
})
|
||||
|
||||
// 统计计算属性
|
||||
const availableCells = computed(() =>
|
||||
cellsData.value.filter(cell => !cell.hasPassword)
|
||||
|
|
@ -387,7 +426,7 @@ const CELL_TYPE_ICON_MAP = {
|
|||
|
||||
function getCellImg(type: number): string {
|
||||
// 假设图片放在 public/images/cabinet/ 目录下
|
||||
const basePath = '/images/cabinet/'
|
||||
const basePath = publicPath + 'images/cabinet/'
|
||||
switch (type) {
|
||||
case 1: return `${basePath}small-cell.svg`
|
||||
case 2: return `${basePath}medium-cell.svg`
|
||||
|
|
@ -420,21 +459,21 @@ onMounted(() => {
|
|||
|
||||
<style scoped>
|
||||
.storage-cells-summary {
|
||||
padding: 32px; /* 原: 32rpx → 16px, 但考虑到适配,使用32px */
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
border-radius: 16px; /* 原: 16rpx → 8px */
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08); /* 原: 0 4rpx 24rpx */
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.summary-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 32px; /* 原: 32rpx → 16px */
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 32px; /* 原: 32rpx → 16px */
|
||||
font-size: 40px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
|
@ -462,21 +501,21 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.cell-type-selection {
|
||||
margin-bottom: 48px; /* 原: 48rpx → 24px */
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.cell-type-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px; /* 原: 16rpx → 8px */
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.cell-type-card {
|
||||
position: relative;
|
||||
padding: 32px 24px; /* 原: 32rpx 24rpx → 16px 12px */
|
||||
padding: 40px 32px;
|
||||
background: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 16px; /* 原: 16rpx → 8px */
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
|
@ -488,8 +527,9 @@ onMounted(() => {
|
|||
|
||||
.cell-type-card--selected {
|
||||
border-color: #2979ff;
|
||||
border-width: 2px;
|
||||
background: rgba(41, 121, 255, 0.04);
|
||||
box-shadow: 0 4px 16px rgba(41, 121, 255, 0.15); /* 原: 0 4rpx 16rpx */
|
||||
box-shadow: 0 8px 32px rgba(41, 121, 255, 0.25);
|
||||
}
|
||||
|
||||
.cell-type-card--selected .cell-type-name {
|
||||
|
|
@ -513,31 +553,37 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.cell-type-icon {
|
||||
margin-bottom: 16px; /* 原: 16rpx → 8px */
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.cell-type-name {
|
||||
display: block;
|
||||
font-size: 28px; /* 原: 28rpx → 14px */
|
||||
font-size: 32px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
margin-bottom: 8px; /* 原: 8rpx → 4px */
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.cell-type-count {
|
||||
display: block;
|
||||
font-size: 24px; /* 原: 24rpx → 12px */
|
||||
font-size: 28px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 80px; /* 原: 80rpx → 40px */
|
||||
height: 80px; /* 原: 80rpx → 40px */
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
margin-top: 32px; /* 原: 32rpx → 16px */
|
||||
margin-top: 12px;
|
||||
|
||||
.van-button {
|
||||
height: 52px;
|
||||
font-size: 20px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 状态样式 */
|
||||
|
|
@ -610,6 +656,39 @@ onMounted(() => {
|
|||
margin-top: 32px; /* 原: 32rpx → 16px */
|
||||
}
|
||||
|
||||
/* 密码显示区域样式 */
|
||||
.password-display-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 48px 32px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 24px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.password-display-label {
|
||||
font-size: 28px;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.password-display-value {
|
||||
font-size: 72px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
letter-spacing: 16px;
|
||||
line-height: 1.2;
|
||||
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.password-display-hint {
|
||||
font-size: 24px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* 注意:当前项目使用postcss-px-to-viewport插件,px单位会自动转换为vw */
|
||||
/* 因此我们保持px单位,让插件自动处理移动端适配 */
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
<template>
|
||||
<div class="storage-horizontal-page">
|
||||
<!-- 地址选择面板 -->
|
||||
<div v-if="showAddressPanel" class="panel address-panel horizontal-mode">
|
||||
<AddressSelectionPanel
|
||||
:shop-list="shopList"
|
||||
:loading="loading"
|
||||
@select="handleShopSelect"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 格口信息面板 -->
|
||||
<div v-else class="panel cells-panel horizontal-mode">
|
||||
<!-- <div class="panel-header">
|
||||
<van-button
|
||||
icon="arrow-left"
|
||||
type="default"
|
||||
@click="showAddressPanel = true"
|
||||
class="panel-switch-btn"
|
||||
>
|
||||
切换地址
|
||||
</van-button>
|
||||
|
||||
<div class="panel-title">
|
||||
<van-icon name="shop-o" />
|
||||
<span>{{ selectedShop?.shopName }}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<StorageCellsSummary
|
||||
:shop-id="shopId"
|
||||
:show-buttons="true"
|
||||
@backToAddressSelect="showAddressPanel = true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getShopListApi } from '@/common/apis/shop'
|
||||
import type { ShopEntity } from '@/common/apis/shop/type'
|
||||
import { useWxStore } from '@/pinia/stores/wx'
|
||||
import AddressSelectionPanel from './components/AddressSelectionPanel.vue'
|
||||
import StorageCellsSummary from './components/StorageCellsSummary.vue'
|
||||
|
||||
const wxStore = useWxStore()
|
||||
|
||||
const appElement = document.getElementById('app')
|
||||
console.log('appElement', appElement)
|
||||
if (appElement) {
|
||||
appElement.style.setProperty('max-width', '100%', 'important')
|
||||
}
|
||||
|
||||
// 核心状态
|
||||
const showAddressPanel = ref(true) // 控制地址选择面板显示
|
||||
const shopList = ref<ShopEntity[]>([])
|
||||
const shopId = ref<number>(0)
|
||||
const selectedShop = ref<ShopEntity | null>(null)
|
||||
const loading = ref(false)
|
||||
|
||||
|
||||
// 初始化
|
||||
const init = async () => {
|
||||
await loadShopList()
|
||||
}
|
||||
|
||||
// 加载店铺列表
|
||||
const loadShopList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getShopListApi(wxStore.corpid || 'wpZ1ZrEgAA2QTxIRcB4cMtY7hQbTcPAw', -1)
|
||||
if (res?.code === 0 && res?.data?.length > 0) {
|
||||
shopList.value = res.data.filter(shop => shop.mode === 5)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取商店列表失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 选择店铺
|
||||
const handleShopSelect = (selectedShopId: number) => {
|
||||
const shop = shopList.value.find(s => s.shopId === selectedShopId)
|
||||
if (shop) {
|
||||
selectedShop.value = shop
|
||||
shopId.value = selectedShopId
|
||||
showAddressPanel.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.storage-horizontal-page {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #f8f8f8;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.panel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: transform 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 面板切换按钮 */
|
||||
.panel-switch-btn {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
z-index: 10;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 20px;
|
||||
padding: 8px 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:active {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
/* 面板标题 */
|
||||
.panel-title {
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
|
||||
.van-icon {
|
||||
margin-right: 8px;
|
||||
color: #2979ff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 地址选择面板专用样式 */
|
||||
.address-panel {
|
||||
overflow-y: auto;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
}
|
||||
|
||||
/* 格口信息面板专用样式 */
|
||||
.cells-panel {
|
||||
overflow-y: auto;
|
||||
padding: 0px;
|
||||
background: #ffffff;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -45,6 +45,10 @@ export const useWxStore = defineStore("wx", () => {
|
|||
balance.value = amount;
|
||||
}
|
||||
|
||||
const setCorpid = (id: string) => {
|
||||
corpid.value = id;
|
||||
}
|
||||
|
||||
const setIsCabinetAdmin = (isAdmin: boolean) => {
|
||||
isCabinetAdmin.value = isAdmin;
|
||||
}
|
||||
|
|
@ -178,7 +182,7 @@ export const useWxStore = defineStore("wx", () => {
|
|||
|
||||
return { code, state, openid, corpid, userid, balance, useBalance,
|
||||
balanceLimit, isCabinetAdmin, corpidLogin, name, ab98User, qyUserId, isFakeQyLogin,
|
||||
isHandleWxCallbackComplete, setOpenid, setBalance, handleWxCallback, setIsCabinetAdmin,
|
||||
isHandleWxCallbackComplete, setOpenid, setBalance, setCorpid, handleWxCallback, setIsCabinetAdmin,
|
||||
refreshBalance, setAb98User, fakeQyLogin, waitForHandleWxCallbackComplete }
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ export function registerNavigationGuard(router: Router) {
|
|||
if (isAdmin) {
|
||||
return true;
|
||||
}
|
||||
const cid = urlParams.get('cid') || undefined;
|
||||
if (cid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// useAb98UserStore位置不能放在外面,否则会导致路由守卫无法正常工作
|
||||
const ab98UserStore = useAb98UserStore();
|
||||
|
|
|
|||
|
|
@ -143,6 +143,24 @@ export const routes: RouteRecordRaw[] = [
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/storage",
|
||||
component: () => import("@/pages/cabinet/storage-horizontal.vue"),
|
||||
name: "StorageHorizontal",
|
||||
meta: {
|
||||
title: "暂存柜管理(横屏)",
|
||||
keepAlive: false,
|
||||
layout: {
|
||||
navBar: {
|
||||
showNavBar: false,
|
||||
showLeftArrow: false
|
||||
},
|
||||
tabbar: {
|
||||
showTabbar: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/approval/list",
|
||||
component: () => import("@/pages/approval/list.vue"),
|
||||
|
|
|
|||