diff --git a/my-uno.config.ts b/my-uno.config.ts index 3bfbbee..97b764f 100644 --- a/my-uno.config.ts +++ b/my-uno.config.ts @@ -11,10 +11,11 @@ import { export default defineConfig({ shortcuts: [ - ['surface', 'bg-white dark:bg-[#1f1f1f]'], - ['surface-muted', 'bg-gray-50 dark:bg-[#262626]'], - ['border-base', 'border-gray-200 dark:border-[color:var(--admin-border)]'], - ['text-muted', 'text-gray-600 dark:text-gray-400'], + ['surface', 'bg-[color:var(--admin-bg-elevated)]'], + ['surface-muted', 'bg-[color:var(--admin-bg-muted)]'], + // رنگ + ضخامت — فقط color کافی نیست و در دارک/لایت کادر دیده نمی‌شود + ['border-base', 'border border-solid border-[color:var(--admin-border-strong)]'], + ['text-muted', 'text-[color:var(--admin-text-secondary)]'], ], theme: { colors: { diff --git a/src/assets/style.less b/src/assets/style.less index 6091538..92f72ba 100644 --- a/src/assets/style.less +++ b/src/assets/style.less @@ -9,6 +9,10 @@ .v-leave-to { opacity: 0; } + +.ant-tabs-rtl .ant-tabs-nav .ant-tabs-tab{ + margin: 3px !important; +} /*::-webkit-scrollbar-track { background-color: #f2f6ff !important; } @@ -29,9 +33,24 @@ } } -.ant-form-item { - display: block; - margin-bottom: 0px; +/* قبل‌تر display:block انعطاف ant-row را می‌شکست و لیبل/اینپوت افقی می‌شدند */ +.ant-form:not(.ant-form-inline) .ant-form-item.ant-row { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + margin-bottom: 0; +} + +.ant-form:not(.ant-form-inline) .ant-form-item > .ant-form-item-label, +.ant-form:not(.ant-form-inline) .ant-form-item > .ant-form-item-control { + flex: 0 0 auto !important; + max-width: 100% !important; + width: 100% !important; + padding-inline: 0 !important; +} + +.ant-form:not(.ant-form-inline) .ant-form-item > .ant-form-item-label { + padding-bottom: 8px !important; } .ant-select-selector{ diff --git a/src/components/ProductUploader/IndexImageUploader.vue b/src/components/ProductUploader/IndexImageUploader.vue index d33770f..d4ff0f6 100644 --- a/src/components/ProductUploader/IndexImageUploader.vue +++ b/src/components/ProductUploader/IndexImageUploader.vue @@ -1,33 +1,84 @@ @@ -45,9 +96,9 @@ import { computed, onMounted, ref } from 'vue' const { prefixCls } = usePrefix('index-image-uploader') const previewVisible = ref(false) const previewImage = ref('') -const previewTitle = ref('') +const previewTitle = ref('تصویر شاخص') -const loading = ref(false) +const loading = ref(false) const fileHeader = { Authorization: `Bearer ${getToken()}` } @@ -73,16 +124,30 @@ const fileList = computed({ } }) -const handlePreview = async (file: UploadProps['fileList'][number]) => { - previewImage.value = file.url || file.thumbUrl || file.src - previewVisible.value = true - previewTitle.value = - file.name || (file.url ? file.url.substring(file.url.lastIndexOf('/') + 1) : 'image') +const currentImageUrl = computed(() => { + const file = fileList.value?.[0] as { url?: string; thumbUrl?: string; src?: string } | undefined + return file?.url || file?.thumbUrl || file?.src || '' +}) + +function onFileListUpdate(value: UploadProps['fileList']) { + fileList.value = value } -const handleCancel = () => { +function openPreview() { + previewImage.value = currentImageUrl.value + previewVisible.value = true +} + +function handleCancel() { previewVisible.value = false - previewTitle.value = '' +} + +function removeImage() { + const uid = (fileList.value?.[0] as { uid?: string } | undefined)?.uid + fileList.value = [] + if (uid) { + emits('delete', uid) + } } const beforeUpload: UploadProps['beforeUpload'] = (file) => { @@ -150,28 +215,160 @@ onMounted(() => { @prefix: ~'@{prefixCls}-index-image-uploader'; .@{prefix} { - .ant-upload-rtl.ant-upload.ant-upload-select-picture-card { - margin: 0 !important; - border: none; - } + width: 100%; +} + +.index-image-uploader__card { + border: 1px solid var(--admin-border, var(--ant-color-border-secondary, rgba(0, 0, 0, 0.06))); + border-radius: 12px; + background: var(--admin-bg-muted, var(--ant-color-fill-alter, rgba(0, 0, 0, 0.02))); + padding: 1rem 1.25rem; +} + +.index-image-uploader__header { + margin-bottom: 1rem; +} + +.index-image-uploader__title { + font-size: 0.875rem; + font-weight: 700; + color: var(--admin-text, inherit); +} + +.index-image-uploader__desc { + margin: 0.35rem 0 0; + font-size: 0.75rem; + line-height: 1.6; + color: var(--admin-text-secondary, #6b7280); +} + +.index-image-uploader__zone { + display: block; + width: 100%; + .ant-upload { - width: 78px !important; - height: 78px !important; - background-color: #eff6ff; - border: dashed 1px #93c5fd; - border-radius: 4px; + display: block; + width: 100%; } - .ant-upload-list-picture-card-container { - margin: 0; - width: 78px !important; - height: 78px !important; + + .ant-upload-select { + display: block !important; + width: 100% !important; + margin: 0 !important; + border: none !important; + background: transparent !important; } - .ant-upload-list-picture-card .ant-upload-list-item { - padding: 0; - border: none; +} + +.index-image-uploader__empty { + display: flex; + width: 100%; + min-height: 12rem; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.4rem; + padding: 1.5rem 1.25rem; + border: 2px dashed color-mix(in srgb, var(--admin-primary, #f5b800) 45%, var(--admin-border, #d9d9d9)); + border-radius: 12px; + background: color-mix(in srgb, var(--admin-bg-muted, #f5f5f5) 85%, transparent); + color: var(--admin-text-secondary, #6b7280); + text-align: center; + cursor: pointer; + transition: border-color 0.15s ease, background 0.15s ease; + + &:hover { + border-color: var(--admin-primary, #f5b800); + background: color-mix(in srgb, var(--admin-primary, #f5b800) 8%, var(--admin-bg-muted, #f5f5f5)); } - .ant-upload-list-picture-card .ant-upload-list-item-info { - border-radius: 4px; +} + +.index-image-uploader__icon { + color: var(--admin-primary, #f5b800); + opacity: 0.9; + margin-bottom: 0.25rem; +} + +.index-image-uploader__cta { + font-size: 0.9375rem; + font-weight: 600; + color: var(--admin-text, inherit); +} + +.index-image-uploader__hint { + font-size: 0.75rem; + line-height: 1.5; +} + +.index-image-uploader__loading { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + font-size: 0.8125rem; +} + +.index-image-uploader__filled { + position: relative; + width: 100%; + max-width: 22rem; + aspect-ratio: 1; + overflow: hidden; + border-radius: 12px; + border: 1px solid var(--admin-border, #d9d9d9); + background: #000; +} + +.index-image-uploader__img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.index-image-uploader__bar { + position: absolute; + inset: auto 0 0 0; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.5rem; + padding: 0.75rem; + background: linear-gradient(to top, rgba(0, 0, 0, 0.78), transparent); + + .ant-upload { + display: inline-block; + } + + .ant-upload-select { + display: inline-block !important; + margin: 0 !important; + border: none !important; + background: transparent !important; + } +} + +.index-image-uploader__btn { + padding: 0.4rem 0.85rem; + border: 1px solid rgba(255, 255, 255, 0.35); + border-radius: 8px; + background: rgba(0, 0, 0, 0.45); + color: #fff; + font-size: 0.8125rem; + cursor: pointer; + transition: background 0.15s ease; + + &--primary:hover { + background: rgba(0, 0, 0, 0.65); + } + + &--danger { + border-color: rgba(255, 77, 79, 0.55); + color: #ffb3b3; + } + + &--danger:hover { + background: rgba(255, 77, 79, 0.35); } } diff --git a/src/components/mobile/MobileCard.vue b/src/components/mobile/MobileCard.vue index 39988d0..df7ebff 100644 --- a/src/components/mobile/MobileCard.vue +++ b/src/components/mobile/MobileCard.vue @@ -1,6 +1,6 @@