Administrator
Administrator
发布于 2025-08-18 / 15 阅读
0
0

afasf

<template>
    <div class="edit-popup">
        <popup ref="popupRef" :title="popupTitle" :async="true" width="80%" @confirm="handleSubmit"
            @close="handleClose">
            <el-form ref="formRef" :model="formData" label-width="15%" :rules="formRules">
                <el-form-item label="标题" prop="title">
                    <el-input v-model="formData.title" clearable placeholder="请输入标题" />
                </el-form-item>
                <el-form-item label="图片" prop="images">
                    <el-input v-model="formData.images" clearable placeholder="请输入图片" />
                </el-form-item>
                <el-form-item label="库存(展示使用)" prop="inventory">
                    <el-input v-model="formData.inventory" clearable placeholder="请输入库存(展示使用)" />
                </el-form-item>
                <el-form-item label="价格" prop="price">
                    <el-input v-model="formData.price" clearable placeholder="请输入价格" />
                </el-form-item>
                <el-form-item label="详情" prop="details">
                    <el-card header="" shadow="never" class="!border-none">
                        <editor v-model="formData.details" height="500px" />
                    </el-card>
                </el-form-item>
                <el-form-item label="商品所属分类(逗号拼接)" prop="goods_classify_ids">
                    <el-input v-model="formData.goods_classify_ids" clearable placeholder="请输入商品所属分类(逗号拼接)" />
                </el-form-item>
                <el-form-item label="是否热门:0=否,1=是" prop="status_hot">
                    <el-input v-model="formData.status_hot" clearable placeholder="请输入是否热门:0=否,1=是" />
                </el-form-item>
                <el-form-item label="是否推荐:0=否,1=是" prop="status_recommend">
                    <el-input v-model="formData.status_recommend" clearable placeholder="请输入是否推荐:0=否,1=是" />
                </el-form-item>
                <el-form-item label="属性及其对应规格" prop="sku_data_log">
                    <el-input v-model="formData.sku_data_log" type="textarea" autosize clearable
                        placeholder="请输入是否推荐:0=否,1=是" />
                </el-form-item>
                <el-form-item label="属性及其对应规格" prop="sku_data_log">
                    <el-row><el-col><el-button type="primary" @click="addAttr()">新 增 属 性</el-button></el-col></el-row>
                    <el-row v-for="(attr_item, attr_index) in formData.sku_data_list" :key="attr_index"
                        class="attr-container">
                        <el-col :span="6">
                            <el-select v-model="attr_item.attr_id" placeholder="请选择属性"
                                :disabled="attr_item.attr_select">
                                <el-option v-for="(item1, index1) in attr_array_from_sql" :key="index1"
                                    :label="item1.title" :value="item1.id">
                                </el-option>
                            </el-select>
                        </el-col>
                        <el-col :span="2"></el-col>
                        <el-col :span="16">
                            <!-- 绑定在attr_item.child.attr_spec_id里 -->
                            <el-select multiple v-model="attr_item.child_attr_spec_ids" placeholder="请选择规格"
                                @focus="handleFocus(attr_item.attr_id, attr_index)"
                                @change="(selectedIds) => handleChange(selectedIds, attr_item, attr_index)">
                                <!-- 这个选择器里的内容是靠属性id(attr_id)去数据库里筛选出来的属性规格列表 -->
                                <el-option v-for="(item2, index2) in attr_item.type_arr_temp" :key="index2"
                                    :label="item2.name" :value="item2.id">
                                </el-option>
                            </el-select>
                        </el-col>
                    </el-row>
                </el-form-item>

                <el-form-item label="sku_child_string" prop="sku_child_string">
                    <el-input v-model="formData.sku_child_string" type="textarea" autosize clearable
                        placeholder="sku_child_string" />
                </el-form-item>
                <el-form-item label="sku_child_string" prop="sku_child_string">
                    <el-row class="sku-container">
                        <el-col :span="8" class="sku—item-container">sku名称</el-col>
                        <el-col :span="4" class="sku—item-container">图片</el-col>
                        <el-col :span="4" class="sku—item-container">价格</el-col>
                        <el-col :span="4" class="sku—item-container">vip价格</el-col>
                        <el-col :span="4" class="sku—item-container">库存</el-col>
                    </el-row>
                    <el-row v-for="(item, index) in formData.sku_child" class="sku-container" :key="index">
                        <el-col :span="8" class="sku—item-container"><el-input disabled v-model="item.sku_name"
                                class="sku-input"></el-input></el-col>
                        <el-col :span="4" class="sku—item-container"><material-picker v-model="item.image" /></el-col>
                        <el-col :span="4" class="sku—item-container"><el-input clearable v-model="item.price"
                                class="sku-input"></el-input></el-col>
                        <el-col :span="4" class="sku—item-container"><el-input clearable v-model="item.price_vip"
                                class="sku-input"></el-input></el-col>
                        <el-col :span="4" class="sku—item-container"><el-input clearable v-model="item.inventory"
                                class="sku-input"></el-input></el-col>
                    </el-row>
                </el-form-item>
            </el-form>
        </popup>
    </div>
</template>

<script lang="ts" setup name="goodsEdit">
import type { FormInstance } from 'element-plus'
import Popup from '@/components/popup/index.vue'
import { apiGoodsAdd, apiGoodsEdit, apiGoodsDetail } from '@/api/goods'
import { timeFormat } from '@/utils/util'
import type { PropType } from 'vue'
import { apiGoodsAttrLists } from '@/api/goods_attr'
import { apiGoodsAttrSpecLists } from '@/api/goods_attr_spec'
import { forEach } from 'lodash'
import { tr } from 'element-plus/es/locales.mjs'
import { number } from 'echarts'
defineProps({
    dictData: {
        type: Object as PropType<Record<string, any[]>>,
        default: () => ({})
    }
})
const emit = defineEmits(['success', 'close'])
const formRef = shallowRef<FormInstance>()
const formData_string = ref('')
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
let attr_array_from_sql: { id: number; title: string }[] = []
const attr_array_type_from_sql: { id: number; goods_attr_id: number; name: string }[] = []

// 弹窗标题
const popupTitle = computed(() => {
    return mode.value == 'edit' ? '编辑商品表' : '新增商品表'
})

// 表单数据
const formData = reactive({
    id: '',
    title: '',
    images: '',
    inventory: '',
    price: '',
    details: '',
    goods_classify_ids: '',
    status_hot: '',
    status_recommend: '',
    sku_data_log: '',
    sku_child: [] as Array<{ sku_name: string, sku_ids: string, goods_id: number, id: number, sku_name_string: string, image: string, price: string, price_vip: string, inventory: number }>,
    sku_child_string: '',
    status: 1,
    sku_data_list: [] as Array<{ attr_select: boolean, attr_id: number | undefined, attr_name: string, child: { attr_spec_id: number | null, attr_spec_name: string }[], child_attr_spec_ids: number[], type_arr_temp: { goods_attr_id: number, id: number, name: string }[] }>
})

// 表单验证
const formRules = reactive<any>({
    title: [{
        required: true,
        message: '请输入标题',
        trigger: ['blur']
    }],
    images: [{
        required: true,
        message: '请输入图片',
        trigger: ['blur']
    }]
})

// 规格下拉框开始下拉时
const handleFocus = async (attr_id: number | undefined, attr_index: number) => {
    const res = await apiGoodsAttrSpecLists({ goods_attr_id: attr_id })
    console.log('当前的attr_index', attr_index, '规格列表,靠attr_id', attr_id, '去筛选,返回的数据是', res)
    formData.sku_data_list[attr_index].type_arr_temp = res.lists
}

// 规格下拉框选择完成时
const handleChange = (
    selectedIds: number[],         // 用户选中的规格 id 数组,比如 [1, 3]
    attr_item: any,                // 当前这一行的数据对象,比如 { attr_id: 5, child: [1, 3] }
    attr_index: number                  // 当前是第几个 select(可选)
) => {
    if (selectedIds.length > 0) {
        formData.sku_data_list[attr_index].attr_select = true
    } else {
        formData.sku_data_list[attr_index].attr_select = false
    }
    console.log('选中的规格 ids:', selectedIds, '当前 attr_item:', attr_item, '当前索引:', attr_index)
}

// 监听 formData 对象的变化,自动转为 JSON 字符串并同步到 formData_string
watch(
    () => formData, // 返回 reactive 对象本身
    (newVal) => {
        console.log('formData变量发生改变,改变后的结果为', newVal)
    },
    { deep: true } // 深度监听,因为 formData 内部属性可能会变
)

// 监听 sku_child 的变化,自动同步到 sku_child_string(JSON 字符串格式)
watch(
    () => formData.sku_child,
    (newVal) => {
        try {
            // 将 sku_child 数组转为 JSON 字符串(无格式化,紧凑格式,适合存储)
            formData.sku_child_string = JSON.stringify(newVal, null, 0)
            console.log('sku_child 已变化,同步到 sku_child_string:', newVal)
        } catch (err) {
            console.error('sku_child 转为 JSON 失败:', err)
            formData.sku_child_string = '' // 出错时清空,避免页面报错
        }
    },
    { deep: true } // 深度监听,因为 sku_child 很可能是一个数组或对象,内部可能变化
)

// // 监听 sku_data_list 变化,自动同步到 sku_data_log,并优化 child 数据结构
// watch(
//     () => formData.sku_data_list,
//     (newVal: any[]) => {
//         try {
//             // 遍历每一个属性对象 item
//             const updatedList = newVal.map((item: any) => {
//                 const selectedSpecIds = item.child_attr_spec_ids || [] // 当前选中的规格ID数组,如 [1, 2]
//                 const typeArrTemp = item.type_arr_temp || [] // 所有规格选项,含 id 和 name

//                 // 构建一个 id -> name 的映射,方便快速查找
//                 const specIdToNameMap = typeArrTemp.reduce((map: Record<number, string>, spec: any) => {
//                     if (spec.id !== undefined && spec.name !== undefined) {
//                         map[spec.id] = spec.name
//                     }
//                     return map
//                 }, {})

//                 // 构造新的 child 数组:只保留选中的规格,且每个对象只包含 attr_spec_id 和 attr_spec_name
//                 const newChild = selectedSpecIds.map((specId: number) => {
//                     const name = specIdToNameMap[specId] // 从 type_arr_temp 中根据 id 找到对应的 name
//                     return {
//                         attr_spec_id: specId,
//                         attr_spec_name: name || '' // 如果找不到 name,默认空字符串
//                     }
//                 })

//                 // 返回一个新对象,只包含你需要的三个字段:attr_id、attr_name、child
//                 return {
//                     attr_id: item.attr_id,
//                     attr_name: item.attr_name,
//                     child: newChild,

//                     // 为了调试的时候放便查看字段详情,故开发这三个字段
//                     // child_attr_spec_ids: item.child_attr_spec_ids,
//                     // type_arr_temp: item.type_arr_temp,
//                     // attr_select: item.attr_select
//                     // 为了调试的时候放便查看字段详情,故开发这三个字段
//                 }
//             })

//             // 将处理后的结构转为 JSON(无格式化,紧凑格式)
//             formData.sku_data_log = JSON.stringify(updatedList, null, 0)
//             console.log('sku_data_list', formData.sku_data_list, ' 已优化并同步到 sku_data_log:', updatedList)

//             // 通过选择的属性和规格去组合sku
//             console.log('当前formData.sku_data_list值为',formData.sku_data_list)
//             generateSkus()

//         } catch (err) {
//             console.error('处理 sku_data_list 数据时出错:', err)
//             formData.sku_data_log = ''
//         }
//     },
//     { deep: true } // 深度监听
// )

// 监听 sku_data_list 变化
watch(
  () => formData.sku_data_list,
  (newVal: any[]) => {
    const updatedList = newVal.map((item: any) => {
      const selectedSpecIds = item.child_attr_spec_ids || [] // 👈 用户选中的规格ID
      const typeArrTemp = item.type_arr_temp || []
      const specIdToNameMap = typeArrTemp.reduce((map: any, spec: any) => {
        if (spec.id !== undefined && spec.name !== undefined) {
          map[spec.id] = spec.name
        }
        return map
      }, {})

      // 👇 这里才是根据用户选择,构造的新 child(只包含选中的!)
      const newChild = selectedSpecIds.map((specId: number) => {
        const name = specIdToNameMap[specId]
        return {
          attr_spec_id: specId,
          attr_spec_name: name || ''
        }
      })
      console.log('构造的新 child为变量newChild',newChild)

      return {
        attr_id: item.attr_id,
        attr_name: item.attr_name,
        child: newChild, // 👈 只有选中的规格!
        // 原始的 child 没有被修改!
      }
    })

    formData.sku_data_log = JSON.stringify(updatedList, null, 0)
    // console.log('sku_data_list',formData.sku_data_list)

    console.log('sku_data_list(updatedList) 已优化并同步到 sku_data_log:!!!!!!!!', updatedList)

    generateSkus(updatedList) // 👈 然后调用生成 SKU
  },
  { deep: true }
)


// 获取详情并设置表单数据
const setFormData = async (data: Record<any, any>) => {
    console.log('接收到的原始数据 data =', data)

    // 优先处理特殊字段:sku_data_log(假设它可能是 JSON 字符串)
    if (typeof data.sku_data_log === 'string') {
        try {
            // 尝试将字符串解析为对象数组
            formData.sku_data_list = JSON.parse(data.sku_data_log)
            console.log('解析后的 sku_data_list =', formData.sku_data_list)
        } catch (e) {
            console.error('解析 sku_data_log 失败,将设置为空数组', e)
            formData.sku_data_list = [] // 解析失败时设为空数组
        }
    } else if (Array.isArray(data.sku_data_log)) {
        // 如果后端直接返回的是数组(不是字符串),也直接使用
        formData.sku_data_list = data.sku_data_log
        console.log('直接使用数组格式的 sku_data_log =', formData.sku_data_list)
    } else {
        // 其它情况(null / undefined / 非数组 / 非字符串),设为空数组
        formData.sku_data_list = []
    }

    // 再处理其他普通字段(推荐明确赋值,而不是遍历 formData 的 key)
    formData.id = data.id ?? ''
    formData.title = data.title ?? ''
    formData.images = data.images ?? ''
    formData.inventory = data.inventory ?? ''
    formData.price = data.price ?? ''
    formData.details = data.details ?? ''
    formData.goods_classify_ids = data.goods_classify_ids ?? ''
    formData.status_hot = data.status_hot ?? ''
    formData.status_recommend = data.status_recommend ?? ''
    formData.sku_child = data.sku_child ?? ''
    formData.sku_child_string = JSON.stringify(data.sku_child, null, 0)


    // 如果你不想用 sku_data_log 字段,可以不赋值,或者赋值原始字符串
    formData.sku_data_log = data.sku_data_log ?? ''
    let attr_spec_ids: number[] = []
    formData.sku_data_list.forEach((item1: any, index: any) => {
        attr_spec_ids = []
        //自己改变数据结构,生成attr_spec_id数组,以供选择器使用
        item1.child.forEach((item2: any, index: any) => {
            console.log('item2.attr_spec_id', item2.attr_spec_id)
            attr_spec_ids.push(item2.attr_spec_id)
        })
        item1.child_attr_spec_ids = attr_spec_ids
        console.log('sku_data_list.item1', item1)

        // 构造一个新的数组,每个元素只包含 id 和 name,来源于 item1.child 中的 attr_spec_id 和 attr_spec_name
        item1.type_arr_temp = item1.child.map((childItem: any) => ({
            id: childItem.attr_spec_id,
            name: childItem.attr_spec_name
        }))

        // 当规格被选择时,属性下拉框不可选;当规格未被选择时,属性下拉框可选
        if (item1.child.length > 0) {
            item1.attr_select = true
            console.log('item1.child', item1.child, '的长度为', item1.child.length, '设置完后item1变量为', item1)
        } else {
            item1.attr_select = false
            console.log('item1.child', item1.child, 'item1.child的长度为', item1.child.length, '设置完后item1变量为', item1)
        }
    })



    // 打印最终表单数据,用于调试
    console.log('最终设置的表单数据 formData =', formData)
}

// 新增属性
const addAttr = () => {
    // 新增一条空的属性数据对象
    const newAttrItem = {
        attr_select: false,                     // 初始时属性可选(没有选规格时不禁用)
        attr_id: undefined,                            // 默认值,未选择属性
        attr_name: '',                         // 可选字段,可以不设置
        child: [],                             // 当前属性下还没有规格
        child_attr_spec_ids: [],               // 用户还未选择任何规格
        type_arr_temp: []                      // 规格下拉选项,初始为空
    }

    // 将新对象添加到 sku_data_list 数组中
    formData.sku_data_list.push(newAttrItem)

    console.log('已新增一条属性,当前 sku_data_list 长度为:', formData.sku_data_list.length)
}

const getDetail = async (row: Record<string, any>) => {
    const data = await apiGoodsDetail({
        id: row.id
    })
    setFormData(data)
}


// 提交按钮
const handleSubmit = async () => {
    await formRef.value?.validate()
    const data = { ...formData, }
    mode.value == 'edit'
        ? await apiGoodsEdit(data)
        : await apiGoodsAdd(data)
    popupRef.value?.close()
    emit('success')
}

//打开弹窗
const open = async (type = 'add') => {
    const res = await apiGoodsAttrLists()
    console.log('属性数组', res.lists)
    attr_array_from_sql = res.lists
    console.log('属性数组', attr_array_from_sql)
    mode.value = type
    popupRef.value?.open()
}

// 关闭回调
const handleClose = () => {
    emit('close')
}


defineExpose({
    open,
    setFormData,
    getDetail
})

// 工具函数:计算多个数组的笛卡尔积(所有可能的组合)
const cartesianProduct = (arrays: any[][]): any[][] => {
  if (arrays.length === 0) return [[]]
  if (arrays.length === 1) return arrays[0].map(item => [item])

  const [first, ...rest] = arrays
  const restProduct = cartesianProduct(rest)

  return first.flatMap(item =>
    restProduct.map(restItem => [item, ...restItem])
  )
}

// const generateSkus = (updatedList) => {

//     console.log('生成sku使用的sku_data_listshu!!!!!!!!!!',formData.sku_data_list)

//   // 1. 提取每个属性的 child(即规格列表),并过滤掉未选择规格的属性(child 为空的)
//   const validSpecGroups = formData.sku_data_list
//     .map(item => item.child)
//     .filter(childList => childList && childList.length > 0) // 只保留有选中的规格的属性

//   // 2. 检查是否还有任何属性被选中了规格
//   if (validSpecGroups.length === 0) {
//     console.warn('没有属性被选择任何规格,无法生成 SKU')
//     formData.sku_child = [] // 不生成任何 SKU
//     return
//   }

//   // 3. 计算所有 **被选中属性的规格** 的笛卡尔积(组合)
//   const combinations = cartesianProduct(validSpecGroups)

//   // 4. 遍历每一种组合,构造 SKU 对象
//   const skuList = combinations.map((combination, index) => {
//     // combination 是一个数组,如:
//     // [
//     //   { attr_spec_id: 1, attr_spec_name: '红色' },
//     //   { attr_spec_id: 6, attr_spec_name: '8G' },
//     //   { attr_spec_id: 12, attr_spec_name: '大' }
//     // ]

//     // 构造 sku_ids:每个 attr_spec_id 用逗号拼接,如 "1,6,12"
//     const sku_ids = combination.map(spec => spec.attr_spec_id).join(',')

//     // 构造 sku_name:每个规格名称用 " - " 拼接,如 "红色 - 8G - 大"
//     const sku_name = combination.map(spec => spec.attr_spec_name).join(' - ')

//     // 构造 sku_name_string(可加商品前缀,比如 "测试1 " + sku_name)
//     const sku_name_string = `测试1 ${sku_name}`

//     // 构造一个标准的 SKU 对象
//     const skuItem = {
//       id: Date.now() + index, // 示例唯一ID,可以用实际商品SKU ID
//       goods_id: 7,            // 示例商品ID,请替换为真实值
//       sku_ids: sku_ids,
//       sku_name: sku_name,
//       sku_name_string: sku_name_string,
//       image: 'http://default.image.png', // 默认图片,可动态设置
//       price: '100.00',        // 默认价格
//       price_vip: '0.00',      // 默认VIP价格
//       inventory: 1000,        // 默认库存
//       create_time: new Date().toISOString().slice(0, 19).replace('T', ' '),
//       update_time: new Date().toISOString().slice(0, 19).replace('T', ' '),
//       delete_time: null
//     }

//     return skuItem
//   })

//   // 5. 将生成的 SKU 列表赋值给 formData.sku_child
//   formData.sku_child = skuList

//   // 6. (可选)同步更新 sku_child_string 为 JSON 格式字符串
//   formData.sku_child_string = JSON.stringify(skuList, null, 0)

//   console.log('生成的SKU列表:', skuList)
// }

const generateSkus = (updatedList: any[]) => {
  console.log('🎯 generateSkus 使用的是 updatedList(用户选中的规格):', updatedList)

  // 1. 提取每个属性的 child(已为用户选中的规格),不再使用 formData.sku_data_list
  const validSpecGroups = updatedList
    .map(item => item.child) // 每个 item 是 { attr_id, attr_name, child: [...] }
    .filter(childList => childList && childList.length > 0) // 只保留有选中规格的属性

  // 2. 如果没有属性被选中任何规格,直接返回,不生成 SKU
  if (validSpecGroups.length === 0) {
    console.warn('⚠️ 没有属性被选择任何规格,无法生成 SKU')
    formData.sku_child = [] // 不生成任何 SKU
    return
  }

  // 3. 计算所有被选中属性规格的笛卡尔积(组合)
  const combinations = cartesianProduct(validSpecGroups)

  // 4. 遍历每一种组合,构造 SKU 对象
  const skuList = combinations.map((combination, index) => {
    // combination 是一个数组,如:
    // [
    //   { attr_spec_id: 1, attr_spec_name: '红色' },
    //   { attr_spec_id: 10, attr_spec_name: 'S' }
    // ]

    // 构造 sku_ids:用逗号拼接所有选中的规格ID,如 "1,10"
    const sku_ids = combination.map(spec => spec.attr_spec_id).join(',')

    // 构造 sku_name:用 " - " 拼接所有选中的规格名称,如 "红色 - S"
    const sku_name = combination.map(spec => spec.attr_spec_name).join(' - ')

    // 构造 sku_name_string(可加商品前缀,如 "测试商品:" + sku_name)
    const sku_name_string = `测试商品:${sku_name}`

    // 5. 构造一个标准的 SKU 对象
    const skuItem = {
      id: Date.now() + index, // 示例唯一ID
      goods_id: 7,            // TODO: 替换为真实商品ID
      sku_ids: sku_ids,
      sku_name: sku_name,
      sku_name_string: sku_name_string,
      image: 'http://default.image.png', // 默认图片,可后续动态设置
      price: '100.00',        // 可改为从表单或规格中获取
      price_vip: '0.00',
      inventory: 1000,        // 默认库存,可调整
      create_time: new Date().toISOString().slice(0, 19).replace('T', ' '),
      update_time: new Date().toISOString().slice(0, 19).replace('T', ' '),
      delete_time: null
    }

    return skuItem
  })

  // 6. 将生成的 SKU 列表赋值给 formData.sku_child,用于页面展示
  formData.sku_child = skuList

  // 7. (可选)同步更新 sku_child_string 为 JSON 字符串
  formData.sku_child_string = JSON.stringify(skuList, null, 0)

  console.log('✅ 最终生成的SKU列表:', skuList)
}
</script>

<style>
.sku-input {
    height: 32px;
}

.attr-container {
    width: 100%;
    height: 20px;
    margin: 10px 0;
}

.sku-container {
    width: 100%;
}

.sku—item-container {
    display: flex;
    justify-content: center;
    padding: 0 10px;
    align-items: center;
}
</style>


评论