refactor(admin): split GoodsView into dialog components; fix(GoodsView): raw member names, no primary badges, member price grid; feat(tag): 光板 maps to 不打印
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { CategoryTree, Country } from '@/types'
|
||||
import { goodsApi } from '@/api/goods'
|
||||
import { productFamiliesApi } from '@/api/product-families'
|
||||
import { findCategoryPath, buildCascader } from '@/utils/category-tree'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
/** 拖拽/配置的链接节点(含 rawId/goodName/goodImage/goodPrice/sdsGoodId/familyId) */
|
||||
og: any | null
|
||||
/** 同分类下的可合并兄弟链接 */
|
||||
siblings: any[]
|
||||
/** 默认勾选的兄弟 rawId */
|
||||
defaultCheckedIds: string[]
|
||||
/** 左树拖放目标(国家/分类节点),为空时表单手动选 */
|
||||
dropTarget: any | null
|
||||
mode: 'category' | 'country' | 'global'
|
||||
countries: Country[]
|
||||
categories: CategoryTree[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', v: boolean): void
|
||||
(e: 'configured'): void
|
||||
}>()
|
||||
|
||||
const loading = ref(false)
|
||||
const form = ref({
|
||||
countryId: '', cascaderCategory: [] as string[], categoryId: '', goodImage: '',
|
||||
})
|
||||
|
||||
watch(() => props.visible, (v) => {
|
||||
if (!v) return
|
||||
const og = props.og
|
||||
form.value = { countryId: '', cascaderCategory: [], categoryId: '', goodImage: og?.goodImage || '' }
|
||||
if (props.dropTarget) {
|
||||
if (props.mode === 'category') {
|
||||
form.value.categoryId = props.dropTarget.id
|
||||
form.value.cascaderCategory = findCategoryPath(props.categories, props.dropTarget.id)
|
||||
} else {
|
||||
form.value.countryId = props.dropTarget.id
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const categoryCascader = computed(() => buildCascader(props.categories))
|
||||
|
||||
const checkedIds = ref<string[]>([])
|
||||
watch(() => props.visible, (v) => {
|
||||
if (v) checkedIds.value = [...props.defaultCheckedIds]
|
||||
})
|
||||
|
||||
function onCascaderChange(val: any) {
|
||||
form.value.categoryId = val.length ? val[val.length - 1] : ''
|
||||
}
|
||||
|
||||
/** 勾选的兄弟链接与当前链接静默归入同一族(无族则自动建族);失败不阻断原配置流程 */
|
||||
async function ensureFamilyMembership() {
|
||||
const og = props.og
|
||||
const primaryId = String(og.rawId)
|
||||
const checked = checkedIds.value.filter((id) => id !== primaryId)
|
||||
if (!checked.length) return
|
||||
try {
|
||||
if (og.familyId) {
|
||||
await productFamiliesApi.updateMembers(og.familyId, {
|
||||
addOriginGoodIds: [...new Set([primaryId, ...checked])],
|
||||
})
|
||||
} else {
|
||||
await productFamiliesApi.create({
|
||||
familyName: og.goodName ?? '',
|
||||
originGoodIds: [primaryId, ...checked],
|
||||
primaryOriginGoodId: primaryId,
|
||||
})
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage.warning(e?.response?.data?.message || '挂族失败,商品仍按原方式配置')
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
const og = props.og
|
||||
if (!form.value.countryId) { ElMessage.warning('请选择国家'); return }
|
||||
if (!form.value.categoryId) { ElMessage.warning('请选择分类'); return }
|
||||
loading.value = true
|
||||
try {
|
||||
await ensureFamilyMembership()
|
||||
await goodsApi.createGood({
|
||||
goodName: og.goodName,
|
||||
goodImage: form.value.goodImage || undefined,
|
||||
originGoodId: Number(og.rawId),
|
||||
countryId: Number(form.value.countryId),
|
||||
categoryId: Number(form.value.categoryId),
|
||||
positionId: undefined,
|
||||
} as any)
|
||||
ElMessage.success('配置成功')
|
||||
emit('update:visible', false)
|
||||
emit('configured')
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.response?.data?.message || '配置失败')
|
||||
} finally { loading.value = false }
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog :model-value="visible" title="配置原产品" width="520px" destroy-on-close @update:model-value="emit('update:visible', $event)">
|
||||
<div v-if="og" class="config-og-info">
|
||||
<div>
|
||||
<div class="config-og-name">{{ og.goodName }}</div>
|
||||
<div class="config-og-meta">SDS ID: {{ og.sdsGoodId }}<template v-if="og.goodPrice"> · 价格: ¥{{ og.goodPrice }}</template></div>
|
||||
</div>
|
||||
</div>
|
||||
<el-form label-width="80px" style="margin-top: 16px">
|
||||
<el-form-item label="国家">
|
||||
<div v-if="mode === 'category' || !dropTarget" class="select-inline">
|
||||
<el-select v-model="form.countryId" placeholder="请选择国家" filterable>
|
||||
<el-option v-for="c in countries" :key="c.id" :label="c.countryName" :value="c.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-tag v-else>{{ countries.find(c => c.id === form.countryId)?.countryName }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类">
|
||||
<el-cascader v-if="mode === 'country' || !dropTarget" v-model="form.cascaderCategory" :options="categoryCascader as any" :props="{ checkStrictly: true }" placeholder="请选择分类" @change="onCascaderChange" style="width:100%" />
|
||||
<el-tag v-else>{{ dropTarget?.label }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="siblings.length" label="合并同名">
|
||||
<div class="config-merge-box">
|
||||
<div class="config-merge-tip">勾选同分类下同名(不同工厂/仓库)原产品,与当前链接归入同一族:尺码/包装并集 + 价格矩阵。</div>
|
||||
<el-checkbox-group v-model="checkedIds">
|
||||
<el-checkbox v-for="s in siblings" :key="s.rawId" :value="String(s.rawId)">
|
||||
{{ s.goodName }}<template v-if="s.goodPrice"> · ¥{{ s.goodPrice }}</template>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片">
|
||||
<ImageUpload v-model="form.goodImage" label="上传图片" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="emit('update:visible', false)">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="handleSubmit">确认配置</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.config-og-info { padding: 12px; background: #f5f7fa; border-radius: 8px; }
|
||||
.config-og-name { font-weight: 600; font-size: 14px; }
|
||||
.config-og-meta { color: #909399; font-size: 12px; margin-top: 2px; }
|
||||
.config-merge-box { width: 100%; }
|
||||
.config-merge-tip { color: #909399; font-size: 12px; margin-bottom: 8px; line-height: 1.5; }
|
||||
.config-merge-box .el-checkbox-group { display: flex; flex-direction: column; align-items: flex-start; max-height: 160px; overflow-y: auto; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user