feat: 批量工具面板改为抽屉式交互
All checks were successful
Build and Deploy TenantUI / build-and-deploy (push) Successful in 51s
All checks were successful
Build and Deploy TenantUI / build-and-deploy (push) Successful in 51s
This commit is contained in:
@@ -4,6 +4,7 @@ import { Button, Select, Space, Upload } from 'ant-design-vue';
|
||||
|
||||
interface Props {
|
||||
categoryOptions: Array<{ label: string; value: string }>;
|
||||
embedded?: boolean;
|
||||
exportCategoryIds: string[];
|
||||
exportScopeType: 'all' | 'category';
|
||||
open: boolean;
|
||||
@@ -21,7 +22,9 @@ interface Emits {
|
||||
(event: 'update:exportScopeType', value: 'all' | 'category'): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
embedded: false,
|
||||
});
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
@@ -40,8 +43,12 @@ function setExportCategoryIds(value: unknown) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="props.open" class="pbt-panel">
|
||||
<header class="pbt-panel-hd">
|
||||
<section
|
||||
v-if="props.open"
|
||||
class="pbt-panel"
|
||||
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||
>
|
||||
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||
<h3>导入导出</h3>
|
||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||
</header>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button, Select } from 'ant-design-vue';
|
||||
|
||||
interface Props {
|
||||
categoryOptions: Array<{ label: string; value: string }>;
|
||||
embedded?: boolean;
|
||||
estimatedCount: number;
|
||||
open: boolean;
|
||||
sourceCategoryId: string;
|
||||
@@ -17,7 +18,9 @@ interface Emits {
|
||||
(event: 'update:targetCategoryId', value: string): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
embedded: false,
|
||||
});
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
function setSourceCategory(value: unknown) {
|
||||
@@ -30,8 +33,12 @@ function setTargetCategory(value: unknown) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="props.open" class="pbt-panel">
|
||||
<header class="pbt-panel-hd">
|
||||
<section
|
||||
v-if="props.open"
|
||||
class="pbt-panel"
|
||||
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||
>
|
||||
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||
<h3>批量移动分类</h3>
|
||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||
</header>
|
||||
|
||||
@@ -9,6 +9,7 @@ interface Props {
|
||||
amountType: 'fixed' | 'percent';
|
||||
categoryOptions: Array<{ label: string; value: string }>;
|
||||
direction: 'down' | 'up';
|
||||
embedded?: boolean;
|
||||
open: boolean;
|
||||
previewItems: BatchPricePreviewRow[];
|
||||
previewLoading: boolean;
|
||||
@@ -32,7 +33,9 @@ interface Emits {
|
||||
(event: 'update:scopeType', value: BatchScopeType): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
embedded: false,
|
||||
});
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const scopePills = [
|
||||
@@ -93,8 +96,12 @@ function setAmount(value: null | number | string) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="props.open" class="pbt-panel">
|
||||
<header class="pbt-panel-hd">
|
||||
<section
|
||||
v-if="props.open"
|
||||
class="pbt-panel"
|
||||
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||
>
|
||||
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||
<h3>批量调价</h3>
|
||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||
</header>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Button, Select } from 'ant-design-vue';
|
||||
interface Props {
|
||||
action: 'off' | 'on';
|
||||
categoryOptions: Array<{ label: string; value: string }>;
|
||||
embedded?: boolean;
|
||||
estimatedCount: number;
|
||||
open: boolean;
|
||||
productOptions: Array<{ label: string; value: string }>;
|
||||
@@ -23,7 +24,9 @@ interface Emits {
|
||||
(event: 'update:scopeType', value: BatchScopeType): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
embedded: false,
|
||||
});
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const scopePills = [
|
||||
@@ -54,8 +57,12 @@ function setScopeProductIds(value: unknown) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="props.open" class="pbt-panel">
|
||||
<header class="pbt-panel-hd">
|
||||
<section
|
||||
v-if="props.open"
|
||||
class="pbt-panel"
|
||||
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||
>
|
||||
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||
<h3>批量上下架</h3>
|
||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||
</header>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button, Checkbox, Input, Select, Space } from 'ant-design-vue';
|
||||
|
||||
interface Props {
|
||||
embedded?: boolean;
|
||||
open: boolean;
|
||||
productIds: string[];
|
||||
productOptions: Array<{ label: string; value: string }>;
|
||||
@@ -24,7 +25,9 @@ interface Emits {
|
||||
(event: 'update:targetStoreIds', value: string[]): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
embedded: false,
|
||||
});
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
function normalizeArray(value: unknown) {
|
||||
@@ -56,8 +59,12 @@ function setSyncStatus(value: boolean | string | undefined) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="props.open" class="pbt-panel">
|
||||
<header class="pbt-panel-hd">
|
||||
<section
|
||||
v-if="props.open"
|
||||
class="pbt-panel"
|
||||
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||
>
|
||||
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||
<h3>批量同步门店</h3>
|
||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||
</header>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { Alert, Empty, Spin } from 'ant-design-vue';
|
||||
import { Alert, Drawer, Empty, Spin } from 'ant-design-vue';
|
||||
|
||||
import BatchImportExportPanel from './components/BatchImportExportPanel.vue';
|
||||
import BatchMoveCategoryPanel from './components/BatchMoveCategoryPanel.vue';
|
||||
@@ -11,6 +12,7 @@ import BatchStoreSyncPanel from './components/BatchStoreSyncPanel.vue';
|
||||
import StoreScopeToolbar from '../../shared/components/StoreScopeToolbar.vue';
|
||||
import BatchToolCards from './components/BatchToolCards.vue';
|
||||
import { useProductBatchPage } from './composables/useProductBatchPage';
|
||||
import type { BatchToolKey } from './types';
|
||||
|
||||
const {
|
||||
activeTool,
|
||||
@@ -119,6 +121,21 @@ function updateExportScopeType(value: 'all' | 'category') {
|
||||
function updateExportCategoryIds(value: string[]) {
|
||||
exportCategoryIds.value = value;
|
||||
}
|
||||
|
||||
const toolTitleMap: Record<BatchToolKey, string> = {
|
||||
price: '批量调价',
|
||||
sale: '批量上下架',
|
||||
category: '批量移动分类',
|
||||
sync: '批量同步门店',
|
||||
excel: '导入导出',
|
||||
};
|
||||
|
||||
const activeToolTitle = computed(() => {
|
||||
if (!activeTool.value) {
|
||||
return '';
|
||||
}
|
||||
return toolTitleMap[activeTool.value];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -151,9 +168,28 @@ function updateExportCategoryIds(value: string[]) {
|
||||
:active-tool="activeTool"
|
||||
@toggle="toggleTool"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<Alert
|
||||
v-if="latestResultText"
|
||||
class="pbt-result"
|
||||
type="success"
|
||||
show-icon
|
||||
:message="latestResultText"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Drawer
|
||||
class="pbt-drawer"
|
||||
:title="activeToolTitle"
|
||||
:open="Boolean(activeTool)"
|
||||
:width="920"
|
||||
@close="closeTool"
|
||||
>
|
||||
<BatchPriceAdjustPanel
|
||||
:open="activeTool === 'price'"
|
||||
v-if="activeTool === 'price'"
|
||||
embedded
|
||||
:open="true"
|
||||
:scope-type="scopeType"
|
||||
:scope-category-ids="scopeCategoryIds"
|
||||
:scope-product-ids="scopeProductIds"
|
||||
@@ -178,7 +214,9 @@ function updateExportCategoryIds(value: string[]) {
|
||||
/>
|
||||
|
||||
<BatchSaleSwitchPanel
|
||||
:open="activeTool === 'sale'"
|
||||
v-if="activeTool === 'sale'"
|
||||
embedded
|
||||
:open="true"
|
||||
:scope-type="scopeType"
|
||||
:scope-category-ids="scopeCategoryIds"
|
||||
:scope-product-ids="scopeProductIds"
|
||||
@@ -196,7 +234,9 @@ function updateExportCategoryIds(value: string[]) {
|
||||
/>
|
||||
|
||||
<BatchMoveCategoryPanel
|
||||
:open="activeTool === 'category'"
|
||||
v-if="activeTool === 'category'"
|
||||
embedded
|
||||
:open="true"
|
||||
:category-options="categoryOptions"
|
||||
:source-category-id="moveSourceCategoryId"
|
||||
:target-category-id="moveTargetCategoryId"
|
||||
@@ -209,7 +249,9 @@ function updateExportCategoryIds(value: string[]) {
|
||||
/>
|
||||
|
||||
<BatchStoreSyncPanel
|
||||
:open="activeTool === 'sync'"
|
||||
v-if="activeTool === 'sync'"
|
||||
embedded
|
||||
:open="true"
|
||||
:source-store-name="selectedStoreLabel"
|
||||
:target-store-ids="syncTargetStoreIds"
|
||||
:target-store-options="targetStoreOptions"
|
||||
@@ -229,7 +271,9 @@ function updateExportCategoryIds(value: string[]) {
|
||||
/>
|
||||
|
||||
<BatchImportExportPanel
|
||||
:open="activeTool === 'excel'"
|
||||
v-if="activeTool === 'excel'"
|
||||
embedded
|
||||
:open="true"
|
||||
:selected-file-name="importFile?.name || ''"
|
||||
:export-scope-type="exportScopeType"
|
||||
:export-category-ids="exportCategoryIds"
|
||||
@@ -243,16 +287,7 @@ function updateExportCategoryIds(value: string[]) {
|
||||
@submit-export="submitExport"
|
||||
@close="closeTool"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<Alert
|
||||
v-if="latestResultText"
|
||||
class="pbt-result"
|
||||
type="success"
|
||||
show-icon
|
||||
:message="latestResultText"
|
||||
/>
|
||||
</div>
|
||||
</Drawer>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
.page-product-batch {
|
||||
.pbt-drawer .ant-drawer-header {
|
||||
padding: 14px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.pbt-drawer .ant-drawer-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
.pbt-drawer .ant-drawer-body {
|
||||
padding: 16px 20px 20px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.page-product-batch,
|
||||
.pbt-drawer {
|
||||
|
||||
.pbt-panel {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
@@ -6,6 +24,13 @@
|
||||
box-shadow: var(--pbt-shadow-sm);
|
||||
}
|
||||
|
||||
.pbt-panel.pbt-panel-embedded {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.pbt-panel-hd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -21,3 +21,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 1200px) {
|
||||
.pbt-drawer .ant-drawer-content-wrapper {
|
||||
width: min(920px, 100vw) !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user