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 {
|
interface Props {
|
||||||
categoryOptions: Array<{ label: string; value: string }>;
|
categoryOptions: Array<{ label: string; value: string }>;
|
||||||
|
embedded?: boolean;
|
||||||
exportCategoryIds: string[];
|
exportCategoryIds: string[];
|
||||||
exportScopeType: 'all' | 'category';
|
exportScopeType: 'all' | 'category';
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -21,7 +22,9 @@ interface Emits {
|
|||||||
(event: 'update:exportScopeType', value: 'all' | 'category'): void;
|
(event: 'update:exportScopeType', value: 'all' | 'category'): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
embedded: false,
|
||||||
|
});
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||||
@@ -40,8 +43,12 @@ function setExportCategoryIds(value: unknown) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section v-if="props.open" class="pbt-panel">
|
<section
|
||||||
<header class="pbt-panel-hd">
|
v-if="props.open"
|
||||||
|
class="pbt-panel"
|
||||||
|
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||||
|
>
|
||||||
|
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||||
<h3>导入导出</h3>
|
<h3>导入导出</h3>
|
||||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Button, Select } from 'ant-design-vue';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
categoryOptions: Array<{ label: string; value: string }>;
|
categoryOptions: Array<{ label: string; value: string }>;
|
||||||
|
embedded?: boolean;
|
||||||
estimatedCount: number;
|
estimatedCount: number;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
sourceCategoryId: string;
|
sourceCategoryId: string;
|
||||||
@@ -17,7 +18,9 @@ interface Emits {
|
|||||||
(event: 'update:targetCategoryId', value: string): void;
|
(event: 'update:targetCategoryId', value: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
embedded: false,
|
||||||
|
});
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
function setSourceCategory(value: unknown) {
|
function setSourceCategory(value: unknown) {
|
||||||
@@ -30,8 +33,12 @@ function setTargetCategory(value: unknown) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section v-if="props.open" class="pbt-panel">
|
<section
|
||||||
<header class="pbt-panel-hd">
|
v-if="props.open"
|
||||||
|
class="pbt-panel"
|
||||||
|
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||||
|
>
|
||||||
|
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||||
<h3>批量移动分类</h3>
|
<h3>批量移动分类</h3>
|
||||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ interface Props {
|
|||||||
amountType: 'fixed' | 'percent';
|
amountType: 'fixed' | 'percent';
|
||||||
categoryOptions: Array<{ label: string; value: string }>;
|
categoryOptions: Array<{ label: string; value: string }>;
|
||||||
direction: 'down' | 'up';
|
direction: 'down' | 'up';
|
||||||
|
embedded?: boolean;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
previewItems: BatchPricePreviewRow[];
|
previewItems: BatchPricePreviewRow[];
|
||||||
previewLoading: boolean;
|
previewLoading: boolean;
|
||||||
@@ -32,7 +33,9 @@ interface Emits {
|
|||||||
(event: 'update:scopeType', value: BatchScopeType): void;
|
(event: 'update:scopeType', value: BatchScopeType): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
embedded: false,
|
||||||
|
});
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const scopePills = [
|
const scopePills = [
|
||||||
@@ -93,8 +96,12 @@ function setAmount(value: null | number | string) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section v-if="props.open" class="pbt-panel">
|
<section
|
||||||
<header class="pbt-panel-hd">
|
v-if="props.open"
|
||||||
|
class="pbt-panel"
|
||||||
|
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||||
|
>
|
||||||
|
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||||
<h3>批量调价</h3>
|
<h3>批量调价</h3>
|
||||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Button, Select } from 'ant-design-vue';
|
|||||||
interface Props {
|
interface Props {
|
||||||
action: 'off' | 'on';
|
action: 'off' | 'on';
|
||||||
categoryOptions: Array<{ label: string; value: string }>;
|
categoryOptions: Array<{ label: string; value: string }>;
|
||||||
|
embedded?: boolean;
|
||||||
estimatedCount: number;
|
estimatedCount: number;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
productOptions: Array<{ label: string; value: string }>;
|
productOptions: Array<{ label: string; value: string }>;
|
||||||
@@ -23,7 +24,9 @@ interface Emits {
|
|||||||
(event: 'update:scopeType', value: BatchScopeType): void;
|
(event: 'update:scopeType', value: BatchScopeType): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
embedded: false,
|
||||||
|
});
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const scopePills = [
|
const scopePills = [
|
||||||
@@ -54,8 +57,12 @@ function setScopeProductIds(value: unknown) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section v-if="props.open" class="pbt-panel">
|
<section
|
||||||
<header class="pbt-panel-hd">
|
v-if="props.open"
|
||||||
|
class="pbt-panel"
|
||||||
|
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||||
|
>
|
||||||
|
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||||
<h3>批量上下架</h3>
|
<h3>批量上下架</h3>
|
||||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { Button, Checkbox, Input, Select, Space } from 'ant-design-vue';
|
import { Button, Checkbox, Input, Select, Space } from 'ant-design-vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
embedded?: boolean;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
productIds: string[];
|
productIds: string[];
|
||||||
productOptions: Array<{ label: string; value: string }>;
|
productOptions: Array<{ label: string; value: string }>;
|
||||||
@@ -24,7 +25,9 @@ interface Emits {
|
|||||||
(event: 'update:targetStoreIds', value: string[]): void;
|
(event: 'update:targetStoreIds', value: string[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
embedded: false,
|
||||||
|
});
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
function normalizeArray(value: unknown) {
|
function normalizeArray(value: unknown) {
|
||||||
@@ -56,8 +59,12 @@ function setSyncStatus(value: boolean | string | undefined) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section v-if="props.open" class="pbt-panel">
|
<section
|
||||||
<header class="pbt-panel-hd">
|
v-if="props.open"
|
||||||
|
class="pbt-panel"
|
||||||
|
:class="{ 'pbt-panel-embedded': props.embedded }"
|
||||||
|
>
|
||||||
|
<header v-if="!props.embedded" class="pbt-panel-hd">
|
||||||
<h3>批量同步门店</h3>
|
<h3>批量同步门店</h3>
|
||||||
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
<button type="button" class="pbt-close" @click="emit('close')">×</button>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Page } from '@vben/common-ui';
|
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 BatchImportExportPanel from './components/BatchImportExportPanel.vue';
|
||||||
import BatchMoveCategoryPanel from './components/BatchMoveCategoryPanel.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 StoreScopeToolbar from '../../shared/components/StoreScopeToolbar.vue';
|
||||||
import BatchToolCards from './components/BatchToolCards.vue';
|
import BatchToolCards from './components/BatchToolCards.vue';
|
||||||
import { useProductBatchPage } from './composables/useProductBatchPage';
|
import { useProductBatchPage } from './composables/useProductBatchPage';
|
||||||
|
import type { BatchToolKey } from './types';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
activeTool,
|
activeTool,
|
||||||
@@ -119,6 +121,21 @@ function updateExportScopeType(value: 'all' | 'category') {
|
|||||||
function updateExportCategoryIds(value: string[]) {
|
function updateExportCategoryIds(value: string[]) {
|
||||||
exportCategoryIds.value = value;
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -151,9 +168,28 @@ function updateExportCategoryIds(value: string[]) {
|
|||||||
:active-tool="activeTool"
|
:active-tool="activeTool"
|
||||||
@toggle="toggleTool"
|
@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
|
<BatchPriceAdjustPanel
|
||||||
:open="activeTool === 'price'"
|
v-if="activeTool === 'price'"
|
||||||
|
embedded
|
||||||
|
:open="true"
|
||||||
:scope-type="scopeType"
|
:scope-type="scopeType"
|
||||||
:scope-category-ids="scopeCategoryIds"
|
:scope-category-ids="scopeCategoryIds"
|
||||||
:scope-product-ids="scopeProductIds"
|
:scope-product-ids="scopeProductIds"
|
||||||
@@ -178,7 +214,9 @@ function updateExportCategoryIds(value: string[]) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<BatchSaleSwitchPanel
|
<BatchSaleSwitchPanel
|
||||||
:open="activeTool === 'sale'"
|
v-if="activeTool === 'sale'"
|
||||||
|
embedded
|
||||||
|
:open="true"
|
||||||
:scope-type="scopeType"
|
:scope-type="scopeType"
|
||||||
:scope-category-ids="scopeCategoryIds"
|
:scope-category-ids="scopeCategoryIds"
|
||||||
:scope-product-ids="scopeProductIds"
|
:scope-product-ids="scopeProductIds"
|
||||||
@@ -196,7 +234,9 @@ function updateExportCategoryIds(value: string[]) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<BatchMoveCategoryPanel
|
<BatchMoveCategoryPanel
|
||||||
:open="activeTool === 'category'"
|
v-if="activeTool === 'category'"
|
||||||
|
embedded
|
||||||
|
:open="true"
|
||||||
:category-options="categoryOptions"
|
:category-options="categoryOptions"
|
||||||
:source-category-id="moveSourceCategoryId"
|
:source-category-id="moveSourceCategoryId"
|
||||||
:target-category-id="moveTargetCategoryId"
|
:target-category-id="moveTargetCategoryId"
|
||||||
@@ -209,7 +249,9 @@ function updateExportCategoryIds(value: string[]) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<BatchStoreSyncPanel
|
<BatchStoreSyncPanel
|
||||||
:open="activeTool === 'sync'"
|
v-if="activeTool === 'sync'"
|
||||||
|
embedded
|
||||||
|
:open="true"
|
||||||
:source-store-name="selectedStoreLabel"
|
:source-store-name="selectedStoreLabel"
|
||||||
:target-store-ids="syncTargetStoreIds"
|
:target-store-ids="syncTargetStoreIds"
|
||||||
:target-store-options="targetStoreOptions"
|
:target-store-options="targetStoreOptions"
|
||||||
@@ -229,7 +271,9 @@ function updateExportCategoryIds(value: string[]) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<BatchImportExportPanel
|
<BatchImportExportPanel
|
||||||
:open="activeTool === 'excel'"
|
v-if="activeTool === 'excel'"
|
||||||
|
embedded
|
||||||
|
:open="true"
|
||||||
:selected-file-name="importFile?.name || ''"
|
:selected-file-name="importFile?.name || ''"
|
||||||
:export-scope-type="exportScopeType"
|
:export-scope-type="exportScopeType"
|
||||||
:export-category-ids="exportCategoryIds"
|
:export-category-ids="exportCategoryIds"
|
||||||
@@ -243,16 +287,7 @@ function updateExportCategoryIds(value: string[]) {
|
|||||||
@submit-export="submitExport"
|
@submit-export="submitExport"
|
||||||
@close="closeTool"
|
@close="closeTool"
|
||||||
/>
|
/>
|
||||||
</template>
|
</Drawer>
|
||||||
|
|
||||||
<Alert
|
|
||||||
v-if="latestResultText"
|
|
||||||
class="pbt-result"
|
|
||||||
type="success"
|
|
||||||
show-icon
|
|
||||||
:message="latestResultText"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</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 {
|
.pbt-panel {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -6,6 +24,13 @@
|
|||||||
box-shadow: var(--pbt-shadow-sm);
|
box-shadow: var(--pbt-shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pbt-panel.pbt-panel-embedded {
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
.pbt-panel-hd {
|
.pbt-panel-hd {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
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