create-m2m-bo-template-vue 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/src/components/DataGrid/DataGrid.vue +9 -1
- package/template/src/components/SearchField/SearchField.module.css +11 -2
- package/template/src/components/SearchField/SearchField.vue +5 -1
- package/template/src/components/shell/GlobalSearchBar.module.css +6 -0
- package/template/src/components/shell/GlobalSearchBar.vue +49 -6
- package/template/src/components/shell/Header.vue +8 -1
- package/template/src/components/shell/QuickSearchPanel.module.css +547 -0
- package/template/src/components/shell/QuickSearchPanel.vue +446 -0
- package/template/src/lib/globalSearch.ts +7 -0
- package/template/src/lib/recentSearches.ts +29 -0
- package/template/src/lib/relatedKeywords.ts +134 -0
- package/template/src/lib/searchHistory.ts +169 -0
- package/template/src/lib/searchIndex.ts +14 -0
- package/template/src/pages/admin/AdminRolesPage.vue +7 -7
- package/template/src/pages/auth/Auth.module.css +164 -0
- package/template/src/pages/auth/AuthLayout.module.css +85 -0
- package/template/src/pages/auth/AuthLayout.vue +29 -0
- package/template/src/pages/auth/LoginPage.vue +52 -0
- package/template/src/pages/auth/PasswordResetPage.vue +96 -0
- package/template/src/pages/auth/TwoFactorPage.vue +97 -0
- package/template/src/pages/b2c/products/B2cProductCreatePage.module.css +383 -14
- package/template/src/pages/b2c/products/B2cProductCreatePage.vue +315 -113
- package/template/src/pages/b2c/products/B2cProductDetailDrawer.module.css +227 -128
- package/template/src/pages/b2c/products/B2cProductDetailDrawer.vue +332 -193
- package/template/src/pages/b2c/products/B2cProductListPage.vue +5 -7
- package/template/src/pages/b2c/products/B2cProductManagementPage.vue +34 -5
- package/template/src/pages/b2c/products/B2cStandardInfoPage.vue +0 -8
- package/template/src/pages/b2c/products/b2cProductCreateModel.ts +38 -8
- package/template/src/pages/c2c/chat/ChatListPage.vue +1 -0
- package/template/src/pages/c2c/chat/MessagePolicyPage.vue +1 -0
- package/template/src/pages/c2c/products/UserProductModerationPage.vue +0 -1
- package/template/src/pages/c2c/purchases/DisputeHistoryPage.vue +1 -0
- package/template/src/pages/c2c/purchases/PurchaseActivityHistoryPage.vue +200 -0
- package/template/src/pages/c2c/purchases/PurchaseDisputesPage.vue +1 -0
- package/template/src/pages/c2c/reports/ReportProcessingPage.vue +1 -1
- package/template/src/pages/c2c/sales/SalesTradesPage.vue +1 -1
- package/template/src/pages/c2c/sales/SellerListPage.vue +1 -1
- package/template/src/pages/c2c/sales/SellerProductsPage.vue +1 -1
- package/template/src/pages/c2c/sanctions/SanctionHistoryPage.vue +1 -0
- package/template/src/pages/c2c/sanctions/SanctionPolicyPage.vue +1 -0
- package/template/src/pages/c2c/sanctions/SanctionProcessingPage.vue +1 -0
- package/template/src/pages/cartconversion/CartConversionPage.vue +5 -43
- package/template/src/pages/coupons/CouponPolicyPage.vue +30 -21
- package/template/src/pages/cs/ConsultationsPage.vue +3 -1
- package/template/src/pages/cs/CsHistoryDetailDrawer.vue +3 -1
- package/template/src/pages/cs/CsInquiriesPage.vue +3 -2
- package/template/src/pages/cs/ProductInquiryDetailDrawer.vue +3 -1
- package/template/src/pages/deliverypolicy/RegionalShippingFeePage.vue +4 -4
- package/template/src/pages/deliverypolicy/ShippingBaseFeePage.module.css +1 -1
- package/template/src/pages/deliverypolicy/ShippingBaseFeePage.vue +26 -28
- package/template/src/pages/logs/SystemLogPage.vue +2 -2
- package/template/src/pages/members/MembersPage.module.css +4 -0
- package/template/src/pages/members/MembersPage.vue +1 -1
- package/template/src/pages/orders/ordersShared.module.css +12 -0
- package/template/src/pages/points/PointPolicyPage.vue +103 -32
- package/template/src/pages/policy/CancelPolicyPage.vue +37 -32
- package/template/src/pages/policy/FeePolicyPage.vue +32 -28
- package/template/src/pages/policy/PaymentPolicyPage.vue +41 -34
- package/template/src/pages/products/MinOrderQtyPage.vue +1 -2
- package/template/src/pages/products/PartnerPricingPage.vue +1 -2
- package/template/src/pages/products/ProductDetailDrawer.vue +17 -11
- package/template/src/pages/products/ProductsListPage.vue +223 -89
- package/template/src/pages/products/SupplyPricePage.vue +1 -2
- package/template/src/pages/products/productsData.ts +21 -0
- package/template/src/pages/promotions/ExposureTooltipCell.vue +108 -0
- package/template/src/pages/promotions/PromotionDetailDrawer.module.css +553 -0
- package/template/src/pages/promotions/PromotionDetailDrawer.vue +438 -100
- package/template/src/pages/promotions/PromotionStackSettingPage.vue +8 -8
- package/template/src/pages/promotions/PromotionsListPage.module.css +209 -0
- package/template/src/pages/promotions/PromotionsListPage.vue +107 -17
- package/template/src/pages/promotions/applicationsData.ts +46 -15
- package/template/src/pages/promotions/promotionsData.ts +349 -28
- package/template/src/pages/stats/DeliveryClaimsStatsPage.vue +411 -0
- package/template/src/pages/stats/InventoryAnalysisPage.vue +853 -0
- package/template/src/pages/stats/PromotionAnalysisPage.vue +444 -0
- package/template/src/pages/stats/SellerActivityStatsPage.vue +535 -0
- package/template/src/pages/stats/StatisticsFilterToolbar.vue +3 -2
- package/template/src/pages/stats/TransactionConversionStatsPage.vue +470 -0
- package/template/src/pages/system/CommonCodesPage.vue +4 -2
- package/template/src/pages/system/DisplaySettingsPage.vue +3 -2
- package/template/src/pages/system/IntegrationManagementPage.vue +4 -2
- package/template/src/pages/system/JobManagementPage.vue +4 -2
- package/template/src/pages/system/ServiceSettingsPage.vue +4 -2
- package/template/src/router/index.ts +4 -2
- package/template/src/router/routes.auth.ts +12 -0
- package/template/src/router/routes.c2cPurchases.ts +2 -0
- package/template/src/router/routes.stats.ts +17 -5
- package/template/src/style.css +0 -19
package/package.json
CHANGED
|
@@ -172,6 +172,7 @@ function cellFgColor(cell: Cell): string | undefined {
|
|
|
172
172
|
|
|
173
173
|
const rootRef = ref<HTMLDivElement | null>(null);
|
|
174
174
|
const internalSelected = ref<Set<string | number>>(new Set());
|
|
175
|
+
const autoSelectable = ref(false);
|
|
175
176
|
const pageSize = ref<number | null>(null);
|
|
176
177
|
const currentPage = ref(1);
|
|
177
178
|
const sortColumnIndex = ref<number | null>(props.defaultSortColumn ?? null);
|
|
@@ -185,12 +186,19 @@ function handlePageSize(event: Event) {
|
|
|
185
186
|
currentPage.value = 1;
|
|
186
187
|
}
|
|
187
188
|
|
|
189
|
+
function updateAutoSelectable() {
|
|
190
|
+
if (props.selectable !== undefined) return;
|
|
191
|
+
autoSelectable.value = Boolean(rootRef.value?.ownerDocument.querySelector('[data-excel-download]'));
|
|
192
|
+
}
|
|
193
|
+
|
|
188
194
|
onMounted(() => {
|
|
189
195
|
rootRef.value?.addEventListener(DATA_GRID_PAGE_SIZE_EVENT, handlePageSize);
|
|
196
|
+
updateAutoSelectable();
|
|
190
197
|
});
|
|
191
198
|
onUnmounted(() => {
|
|
192
199
|
rootRef.value?.removeEventListener(DATA_GRID_PAGE_SIZE_EVENT, handlePageSize);
|
|
193
200
|
});
|
|
201
|
+
watch(() => props.selectable, updateAutoSelectable);
|
|
194
202
|
|
|
195
203
|
const totalPageCount = computed(() => (pageSize.value ? Math.max(1, Math.ceil(props.rows.length / pageSize.value)) : 1));
|
|
196
204
|
watch(totalPageCount, (count) => {
|
|
@@ -206,7 +214,7 @@ watch(
|
|
|
206
214
|
}
|
|
207
215
|
);
|
|
208
216
|
|
|
209
|
-
const effectiveSelectable = computed(() => props.selectable ??
|
|
217
|
+
const effectiveSelectable = computed(() => props.selectable ?? autoSelectable.value);
|
|
210
218
|
const pageStart = computed(() => (pageSize.value ? (currentPage.value - 1) * pageSize.value : 0));
|
|
211
219
|
const visibleSourceRows = computed(() => (pageSize.value ? props.rows.slice(pageStart.value, pageStart.value + pageSize.value) : props.rows));
|
|
212
220
|
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
min-width: 0;
|
|
37
37
|
max-width: none;
|
|
38
38
|
height: 100%;
|
|
39
|
-
|
|
39
|
+
/* m2m-uiux-vue의 .bsSearchField .bsSearchFieldInput 리셋 규칙과 클래스 특이도가
|
|
40
|
+
같아 로드 순서에 따라 우리 패딩이 밀릴 수 있다 — !important로 확정 고정 */
|
|
41
|
+
padding: 0 36px 0 12px !important;
|
|
40
42
|
border: 0;
|
|
41
43
|
outline: 0;
|
|
42
44
|
background: transparent;
|
|
@@ -46,7 +48,7 @@
|
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
.field:has(.shortcut) .control > input {
|
|
49
|
-
padding-right: 64px;
|
|
51
|
+
padding-right: 64px !important;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
.control > button:first-child {
|
|
@@ -71,6 +73,13 @@
|
|
|
71
73
|
font-size: 0.625rem;
|
|
72
74
|
pointer-events: none;
|
|
73
75
|
transform: translateY(-50%);
|
|
76
|
+
background: #fff;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* 입력 내용이 있거나 포커스 시 shortcut 자동 숨김 */
|
|
80
|
+
.field:has(input:not(:placeholder-shown)) .shortcut,
|
|
81
|
+
.field:has(input:focus) .shortcut {
|
|
82
|
+
display: none !important;
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
@media (max-width: 720px) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
2
3
|
import { SearchField as M2MSearchField } from 'm2m-uiux-vue/SearchField';
|
|
3
4
|
|
|
4
5
|
export interface SearchFieldProps {
|
|
@@ -26,6 +27,9 @@ function handleUpdate(value: string) {
|
|
|
26
27
|
props.onChange?.({ target: { value } } as unknown as Event);
|
|
27
28
|
props.onValueChange?.(value);
|
|
28
29
|
}
|
|
30
|
+
|
|
31
|
+
const currentVal = computed(() => String((props.value ?? props.defaultValue) ?? ''));
|
|
32
|
+
const hasValue = computed(() => currentVal.value.trim().length > 0);
|
|
29
33
|
</script>
|
|
30
34
|
|
|
31
35
|
<template>
|
|
@@ -39,7 +43,7 @@ function handleUpdate(value: string) {
|
|
|
39
43
|
:class-names="$style.control"
|
|
40
44
|
@update:model-value="handleUpdate"
|
|
41
45
|
/>
|
|
42
|
-
<span v-if="shortcutHint" :class="$style.shortcut" aria-hidden="true">{{ shortcutHint }}</span>
|
|
46
|
+
<span v-if="shortcutHint && !hasValue" :class="$style.shortcut" aria-hidden="true">{{ shortcutHint }}</span>
|
|
43
47
|
</span>
|
|
44
48
|
</template>
|
|
45
49
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { onMounted, onUnmounted, ref } from 'vue';
|
|
3
3
|
import { useRouter } from 'vue-router';
|
|
4
4
|
import SearchField from '../SearchField/SearchField.vue';
|
|
5
|
+
import QuickSearchPanel from './QuickSearchPanel.vue';
|
|
6
|
+
import { addRecentSearch } from '../../lib/recentSearches';
|
|
5
7
|
import styles from './GlobalSearchBar.module.css';
|
|
6
8
|
|
|
7
9
|
withDefaults(defineProps<{ placeholder?: string; className?: string }>(), {
|
|
@@ -12,12 +14,18 @@ withDefaults(defineProps<{ placeholder?: string; className?: string }>(), {
|
|
|
12
14
|
const router = useRouter();
|
|
13
15
|
const wrapRef = ref<HTMLElement | null>(null);
|
|
14
16
|
const keyword = ref('');
|
|
17
|
+
const panelOpen = ref(false);
|
|
18
|
+
|
|
19
|
+
function getInput() {
|
|
20
|
+
return wrapRef.value?.querySelector('input') ?? null;
|
|
21
|
+
}
|
|
15
22
|
|
|
16
23
|
function focusInput() {
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
getInput()?.focus();
|
|
25
|
+
getInput()?.select();
|
|
19
26
|
}
|
|
20
27
|
|
|
28
|
+
// Ctrl/⌘ + K 로 검색창 포커스
|
|
21
29
|
function handler(e: KeyboardEvent) {
|
|
22
30
|
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
|
|
23
31
|
e.preventDefault();
|
|
@@ -31,22 +39,46 @@ onUnmounted(() => window.removeEventListener('keydown', handler));
|
|
|
31
39
|
function runSearch(value: string) {
|
|
32
40
|
const query = value.trim();
|
|
33
41
|
if (!query) {
|
|
34
|
-
|
|
42
|
+
focusInput();
|
|
35
43
|
return;
|
|
36
44
|
}
|
|
45
|
+
addRecentSearch(query);
|
|
46
|
+
panelOpen.value = false;
|
|
37
47
|
router.push(`/search?q=${encodeURIComponent(query)}`);
|
|
38
48
|
}
|
|
39
49
|
|
|
50
|
+
function handleSelectKeyword(kw: string) {
|
|
51
|
+
keyword.value = kw;
|
|
52
|
+
runSearch(kw);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handleValueChange(value: string) {
|
|
56
|
+
keyword.value = value;
|
|
57
|
+
panelOpen.value = true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function handleFocus() {
|
|
61
|
+
panelOpen.value = true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function handleClose() {
|
|
65
|
+
panelOpen.value = false;
|
|
66
|
+
focusInput();
|
|
67
|
+
}
|
|
68
|
+
|
|
40
69
|
function handleKeyDown(e: KeyboardEvent) {
|
|
41
70
|
if (e.key === 'Enter') {
|
|
42
71
|
e.preventDefault();
|
|
43
72
|
runSearch(keyword.value);
|
|
44
73
|
}
|
|
74
|
+
if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && panelOpen.value) {
|
|
75
|
+
e.preventDefault();
|
|
76
|
+
}
|
|
45
77
|
}
|
|
46
78
|
</script>
|
|
47
79
|
|
|
48
80
|
<template>
|
|
49
|
-
<
|
|
81
|
+
<div ref="wrapRef" :class="`${styles.wrapper} ${className}`" style="position: relative">
|
|
50
82
|
<SearchField
|
|
51
83
|
size="sm"
|
|
52
84
|
clearable
|
|
@@ -55,10 +87,21 @@ function handleKeyDown(e: KeyboardEvent) {
|
|
|
55
87
|
:placeholder="placeholder"
|
|
56
88
|
shortcut-hint="Ctrl K"
|
|
57
89
|
:value="keyword"
|
|
58
|
-
:on-value-change="
|
|
90
|
+
:on-value-change="handleValueChange"
|
|
59
91
|
:on-search="runSearch"
|
|
60
92
|
@keydown="handleKeyDown"
|
|
93
|
+
@focusin="handleFocus"
|
|
61
94
|
aria-label="통합 검색"
|
|
95
|
+
:aria-expanded="panelOpen"
|
|
96
|
+
aria-haspopup="listbox"
|
|
97
|
+
/>
|
|
98
|
+
|
|
99
|
+
<QuickSearchPanel
|
|
100
|
+
v-if="panelOpen"
|
|
101
|
+
:query="keyword.trim()"
|
|
102
|
+
:anchor-el="wrapRef"
|
|
103
|
+
@close="handleClose"
|
|
104
|
+
@select-keyword="handleSelectKeyword"
|
|
62
105
|
/>
|
|
63
|
-
</
|
|
106
|
+
</div>
|
|
64
107
|
</template>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
|
3
|
-
import { Blocks, Settings, ChevronDown, Check } from 'lucide-vue-next';
|
|
3
|
+
import { Blocks, Settings, ChevronDown, Check, LogOut } from 'lucide-vue-next';
|
|
4
4
|
import { Header as M2MHeader } from 'm2m-uiux-vue/Header';
|
|
5
5
|
import { useRoute, useRouter } from 'vue-router';
|
|
6
6
|
import { activeKeyForPath, breadcrumbForKey } from '../../lib/nav';
|
|
@@ -75,6 +75,9 @@ function goSystemDisplay() {
|
|
|
75
75
|
modeDropdownOpen.value = false;
|
|
76
76
|
router.push('/system/display');
|
|
77
77
|
}
|
|
78
|
+
function logout() {
|
|
79
|
+
router.push('/login');
|
|
80
|
+
}
|
|
78
81
|
</script>
|
|
79
82
|
|
|
80
83
|
<template>
|
|
@@ -128,6 +131,10 @@ function goSystemDisplay() {
|
|
|
128
131
|
<Settings :size="15" :stroke-width="1.8" />
|
|
129
132
|
</button>
|
|
130
133
|
|
|
134
|
+
<button type="button" :class="styles.iconBtn" title="로그아웃" @click="logout">
|
|
135
|
+
<LogOut :size="15" :stroke-width="1.8" />
|
|
136
|
+
</button>
|
|
137
|
+
|
|
131
138
|
<div :class="styles.account">
|
|
132
139
|
<div :class="styles.accountAvatar">관</div>
|
|
133
140
|
<span :class="styles.accountName">관리자</span>
|