|
|
@@ -1,22 +1,16 @@
|
|
|
<!-- eslint-disable vue/no-v-model-argument -->
|
|
|
<template>
|
|
|
- <el-dialog
|
|
|
- v-model="visible"
|
|
|
- :title="isEdit ? '编辑产品' : '新增产品'"
|
|
|
- width="960px"
|
|
|
- class="dialog-form-layout"
|
|
|
- align-center
|
|
|
- append-to-body
|
|
|
- destroy-on-close
|
|
|
- modal-class="order-module-dialog-mask"
|
|
|
- style="backdrop-filter: none"
|
|
|
- :close-on-click-modal="false"
|
|
|
+ <component
|
|
|
+ :is="isPageVariant ? 'div' : ElDialog"
|
|
|
+ v-bind="formShellBind"
|
|
|
@open="handleOpen"
|
|
|
- @close="handleClose"
|
|
|
+ @close="handleDialogClose"
|
|
|
+ @update:model-value="onFormShellVisible"
|
|
|
>
|
|
|
<el-form
|
|
|
+ v-show="!(isPageVariant && dialogLoading)"
|
|
|
ref="formRef"
|
|
|
- v-loading="dialogLoading"
|
|
|
+ v-loading="!isPageVariant && dialogLoading"
|
|
|
:model="formData"
|
|
|
:rules="formRules"
|
|
|
label-position="top"
|
|
|
@@ -24,7 +18,7 @@
|
|
|
scroll-to-error
|
|
|
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
|
|
>
|
|
|
- <div class="form-grid-3">
|
|
|
+ <div :class="isPageVariant ? 'form-grid-1' : 'form-grid-3'">
|
|
|
<el-form-item label="产品编号">
|
|
|
<el-input v-model="formData.productNo" placeholder="留空自动生成(如 SXWL000001)" />
|
|
|
</el-form-item>
|
|
|
@@ -45,7 +39,19 @@
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="产品分类">
|
|
|
+ <el-form-item>
|
|
|
+ <template #label>
|
|
|
+ 产品分类
|
|
|
+ <el-button
|
|
|
+ v-if="isPageVariant"
|
|
|
+ class="mp-category-maintain-link"
|
|
|
+ link
|
|
|
+ type="primary"
|
|
|
+ @click="goMaintainCategories"
|
|
|
+ >
|
|
|
+ 维护分类
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
<el-tree-select
|
|
|
v-model="formData.categoryId"
|
|
|
:data="categoryOptions"
|
|
|
@@ -178,21 +184,32 @@
|
|
|
<div v-else class="sub-card-empty">暂无别名,点击上方新增</div>
|
|
|
</div>
|
|
|
</el-form>
|
|
|
- <template #footer>
|
|
|
- <el-button class="btn-ghost" @click="visible = false">
|
|
|
+
|
|
|
+ <template v-if="!isPageVariant" #footer>
|
|
|
+ <el-button class="btn-ghost" @click="handleCancel">
|
|
|
<el-icon><Close /></el-icon>取消
|
|
|
</el-button>
|
|
|
<el-button class="btn-main" type="primary" :loading="submitLoading" @click="submitForm">
|
|
|
<el-icon><Check /></el-icon>确定
|
|
|
</el-button>
|
|
|
</template>
|
|
|
- </el-dialog>
|
|
|
+
|
|
|
+ <div v-if="isPageVariant && !dialogLoading" class="mp-plan-form-footer">
|
|
|
+ <el-button class="btn-ghost" @click="handleCancel">
|
|
|
+ <el-icon><Close /></el-icon>取消
|
|
|
+ </el-button>
|
|
|
+ <el-button class="btn-main" type="primary" :loading="submitLoading" @click="submitForm">
|
|
|
+ <el-icon><Check /></el-icon>确定
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </component>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { Check, Close, Delete, Plus, Refresh } from '@element-plus/icons-vue'
|
|
|
import type { FormInstance } from 'element-plus'
|
|
|
- import { ElMessage } from 'element-plus'
|
|
|
+ import { ElDialog, ElMessage } from 'element-plus'
|
|
|
+ import { useRouter } from 'vue-router'
|
|
|
import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
|
|
|
import {
|
|
|
fetchCategoryTree,
|
|
|
@@ -206,32 +223,73 @@
|
|
|
|
|
|
defineOptions({ name: 'ProductFormDialog' })
|
|
|
|
|
|
- const props = defineProps<{
|
|
|
- modelValue: boolean
|
|
|
- /** 有值则为编辑,无值则为新增 */
|
|
|
- productId?: string | null
|
|
|
- }>()
|
|
|
+ const props = withDefaults(
|
|
|
+ defineProps<{
|
|
|
+ modelValue: boolean
|
|
|
+ /** 有值则为编辑,无值则为新增 */
|
|
|
+ productId?: string | null
|
|
|
+ /** 小程序 WebView 全屏页:不使用桌面弹窗遮罩 */
|
|
|
+ variant?: 'dialog' | 'page'
|
|
|
+ }>(),
|
|
|
+ { variant: 'dialog' }
|
|
|
+ )
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
'update:modelValue': [value: boolean]
|
|
|
success: [product: Record<string, any>]
|
|
|
+ close: []
|
|
|
+ 'loading-change': [loading: boolean]
|
|
|
}>()
|
|
|
|
|
|
+ const router = useRouter()
|
|
|
const dictStore = useDictStore()
|
|
|
|
|
|
+ const isPageVariant = computed(() => props.variant === 'page')
|
|
|
+
|
|
|
const visible = computed({
|
|
|
get: () => props.modelValue,
|
|
|
set: (value: boolean) => emit('update:modelValue', value)
|
|
|
})
|
|
|
|
|
|
+ function onFormShellVisible(value: boolean) {
|
|
|
+ visible.value = value
|
|
|
+ }
|
|
|
+
|
|
|
+ const formShellBind = computed(() => {
|
|
|
+ if (isPageVariant.value) {
|
|
|
+ return { class: 'mp-product-form-page' }
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ modelValue: visible.value,
|
|
|
+ title: isEdit.value ? '编辑产品' : '新增产品',
|
|
|
+ width: '960px',
|
|
|
+ alignCenter: true,
|
|
|
+ appendToBody: true,
|
|
|
+ destroyOnClose: true,
|
|
|
+ class: 'dialog-form-layout',
|
|
|
+ modalClass: 'order-module-dialog-mask',
|
|
|
+ closeOnClickModal: false,
|
|
|
+ style: 'backdrop-filter: none'
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
const isEdit = computed(() => !!props.productId)
|
|
|
|
|
|
const formRef = ref<FormInstance>()
|
|
|
const productImagesUploadRef = ref<InstanceType<typeof AttachmentUploadField>>()
|
|
|
- const dialogLoading = ref(false)
|
|
|
+ /** page 模式默认 true,避免挂载到拉数完成前出现空白表单 */
|
|
|
+ const dialogLoading = ref(props.variant === 'page')
|
|
|
const submitLoading = ref(false)
|
|
|
const categoryOptions = ref<any[]>([])
|
|
|
|
|
|
+ watch(
|
|
|
+ dialogLoading,
|
|
|
+ (loading) => {
|
|
|
+ emit('loading-change', loading)
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+ )
|
|
|
+
|
|
|
const formData = reactive({
|
|
|
id: '',
|
|
|
productName: '',
|
|
|
@@ -299,6 +357,10 @@
|
|
|
ElMessage.info('已清空,保存时将按产品名称重新生成拼音首字母')
|
|
|
}
|
|
|
|
|
|
+ function goMaintainCategories() {
|
|
|
+ void router.push('/m/crm/product/categories')
|
|
|
+ }
|
|
|
+
|
|
|
async function loadCategoryOptions() {
|
|
|
try {
|
|
|
const res = await fetchCategoryTree()
|
|
|
@@ -309,13 +371,13 @@
|
|
|
}
|
|
|
|
|
|
async function handleOpen() {
|
|
|
- resetFormData()
|
|
|
- await loadCategoryOptions()
|
|
|
- if (!props.productId) {
|
|
|
- return
|
|
|
- }
|
|
|
dialogLoading.value = true
|
|
|
+ resetFormData()
|
|
|
try {
|
|
|
+ await loadCategoryOptions()
|
|
|
+ if (!props.productId) {
|
|
|
+ return
|
|
|
+ }
|
|
|
const res = await fetchProductDetail({ id: String(props.productId) })
|
|
|
const data = (res as any)?.data || res || {}
|
|
|
Object.assign(formData, {
|
|
|
@@ -349,17 +411,29 @@
|
|
|
})
|
|
|
} catch {
|
|
|
ElMessage.error('加载产品详情失败')
|
|
|
- visible.value = false
|
|
|
+ if (isPageVariant.value) {
|
|
|
+ emit('close')
|
|
|
+ } else {
|
|
|
+ visible.value = false
|
|
|
+ }
|
|
|
} finally {
|
|
|
dialogLoading.value = false
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- function handleClose() {
|
|
|
+ function handleDialogClose() {
|
|
|
resetFormData()
|
|
|
formRef.value?.resetFields()
|
|
|
}
|
|
|
|
|
|
+ function handleCancel() {
|
|
|
+ if (isPageVariant.value) {
|
|
|
+ emit('close')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ visible.value = false
|
|
|
+ }
|
|
|
+
|
|
|
function handleAddBrand() {
|
|
|
formData.brands.push({ brandName: '', status: 'on' })
|
|
|
}
|
|
|
@@ -434,9 +508,80 @@
|
|
|
submitLoading.value = false
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ if (isPageVariant.value) void handleOpen()
|
|
|
+ })
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
+ .mp-product-form-page {
|
|
|
+ width: 100%;
|
|
|
+ max-width: 100%;
|
|
|
+ overflow-x: hidden;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 12px 12px calc(56px + env(safe-area-inset-bottom, 0px));
|
|
|
+
|
|
|
+ :deep(.form-grid-1),
|
|
|
+ :deep(.form-grid-3) {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr !important;
|
|
|
+ gap: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.form-col-span-2),
|
|
|
+ :deep(.el-form-item[style*='grid-column']) {
|
|
|
+ grid-column: 1 / -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sub-card-grid {
|
|
|
+ grid-template-columns: minmax(0, 1fr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .mp-plan-form-footer {
|
|
|
+ position: fixed;
|
|
|
+ left: 50%;
|
|
|
+ bottom: 0;
|
|
|
+ z-index: 200;
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ width: 100%;
|
|
|
+ max-width: min(480px, 100%);
|
|
|
+ box-sizing: border-box;
|
|
|
+ margin: 0;
|
|
|
+ padding: 8px 10px calc(8px + env(safe-area-inset-bottom, 0px));
|
|
|
+ background: #fff;
|
|
|
+ border-top: 1px solid #dcebe4;
|
|
|
+ box-shadow: 0 -4px 16px rgba(15, 30, 24, 0.08);
|
|
|
+ transform: translateX(-50%);
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ :deep(.el-button) {
|
|
|
+ flex: 1 1 0;
|
|
|
+ min-width: 0;
|
|
|
+ height: 34px;
|
|
|
+ margin: 0;
|
|
|
+ padding: 0 6px;
|
|
|
+ font-size: 12px;
|
|
|
+
|
|
|
+ span {
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .mp-category-maintain-link {
|
|
|
+ margin-left: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 400;
|
|
|
+ vertical-align: baseline;
|
|
|
+ }
|
|
|
+
|
|
|
.pinyin-abbr-row {
|
|
|
display: flex;
|
|
|
gap: 8px;
|