商品评论、优惠券模板复用组件【商品橱窗】
							parent
							
								
									3198688eb5
								
							
						
					
					
						commit
						e0a731dd86
					
				| 
						 | 
				
			
			@ -8,14 +8,7 @@
 | 
			
		|||
      v-loading="formLoading"
 | 
			
		||||
    >
 | 
			
		||||
      <el-form-item label="商品" prop="spuId">
 | 
			
		||||
        <div @click="handleSelectSpu" class="h-60px w-60px">
 | 
			
		||||
          <div v-if="spuData && spuData.picUrl">
 | 
			
		||||
            <el-image :src="spuData.picUrl" />
 | 
			
		||||
          </div>
 | 
			
		||||
          <div v-else class="select-box">
 | 
			
		||||
            <Icon icon="ep:plus" />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <SpuShowcase v-model="formData.spuId" :limit="1" />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="商品规格" prop="skuId" v-if="formData.spuId">
 | 
			
		||||
        <div @click="handleSelectSku" class="h-60px w-60px">
 | 
			
		||||
| 
						 | 
				
			
			@ -51,12 +44,11 @@
 | 
			
		|||
      <el-button @click="dialogVisible = false">取 消</el-button>
 | 
			
		||||
    </template>
 | 
			
		||||
  </Dialog>
 | 
			
		||||
  <SpuTableSelect ref="spuTableSelectRef" @change="handleSpuChange" />
 | 
			
		||||
  <SkuTableSelect ref="skuTableSelectRef" @change="handleSkuChange" :spu-id="spuData.id" />
 | 
			
		||||
  <SkuTableSelect ref="skuTableSelectRef" @change="handleSkuChange" :spu-id="formData.spuId" />
 | 
			
		||||
</template>
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import * as CommentApi from '@/api/mall/product/comment'
 | 
			
		||||
import SpuTableSelect from '@/views/mall/product/spu/components/SpuTableSelect.vue'
 | 
			
		||||
import SpuShowcase from "@/views/mall/product/spu/components/SpuShowcase.vue";
 | 
			
		||||
import * as ProductSpuApi from '@/api/mall/product/spu'
 | 
			
		||||
import SkuTableSelect from '@/views/mall/product/spu/components/SkuTableSelect.vue'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -72,8 +64,7 @@ const formData = ref({
 | 
			
		|||
  userId: undefined,
 | 
			
		||||
  userNickname: undefined,
 | 
			
		||||
  userAvatar: undefined,
 | 
			
		||||
  spuId: undefined,
 | 
			
		||||
  spuName: undefined,
 | 
			
		||||
  spuId: 0,
 | 
			
		||||
  skuId: undefined,
 | 
			
		||||
  descriptionScores: 5,
 | 
			
		||||
  benefitScores: 5,
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +81,6 @@ const formRules = reactive({
 | 
			
		|||
  benefitScores: [{ required: true, message: '服务星级不能为空', trigger: 'blur' }]
 | 
			
		||||
})
 | 
			
		||||
const formRef = ref() // 表单 Ref
 | 
			
		||||
const spuData = ref<ProductSpuApi.Spu>({})
 | 
			
		||||
const skuData = ref({
 | 
			
		||||
  id: -1,
 | 
			
		||||
  name: '',
 | 
			
		||||
| 
						 | 
				
			
			@ -149,8 +139,7 @@ const resetForm = () => {
 | 
			
		|||
    userId: undefined,
 | 
			
		||||
    userNickname: undefined,
 | 
			
		||||
    userAvatar: undefined,
 | 
			
		||||
    spuId: undefined,
 | 
			
		||||
    spuName: undefined,
 | 
			
		||||
    spuId: 0,
 | 
			
		||||
    skuId: undefined,
 | 
			
		||||
    descriptionScores: 5,
 | 
			
		||||
    benefitScores: 5,
 | 
			
		||||
| 
						 | 
				
			
			@ -160,16 +149,6 @@ const resetForm = () => {
 | 
			
		|||
  formRef.value?.resetFields()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** SPU 表格选择 */
 | 
			
		||||
const spuTableSelectRef = ref()
 | 
			
		||||
const handleSelectSpu = () => {
 | 
			
		||||
  spuTableSelectRef.value.open()
 | 
			
		||||
}
 | 
			
		||||
const handleSpuChange = (spu: ProductSpuApi.Spu) => {
 | 
			
		||||
  spuData.value = spu
 | 
			
		||||
  formData.value.spuId = spu.id
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** SKU 表格选择 */
 | 
			
		||||
const skuTableSelectRef = ref()
 | 
			
		||||
const handleSelectSku = () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,7 +59,7 @@
 | 
			
		|||
  <!-- 列表 -->
 | 
			
		||||
  <ContentWrap>
 | 
			
		||||
    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="false">
 | 
			
		||||
      <el-table-column label="评论编号" align="center" prop="id" min-width="50" />
 | 
			
		||||
      <el-table-column label="评论编号" align="center" prop="id" min-width="80" />
 | 
			
		||||
      <el-table-column label="商品信息" align="center" min-width="400">
 | 
			
		||||
        <template #default="scope">
 | 
			
		||||
          <div class="row flex items-center gap-x-4px">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,13 +13,8 @@
 | 
			
		|||
        </div>
 | 
			
		||||
      </el-tooltip>
 | 
			
		||||
    </div>
 | 
			
		||||
    <el-tooltip content="选择商品">
 | 
			
		||||
      <div
 | 
			
		||||
        v-show="!disabled"
 | 
			
		||||
        v-if="!limit || limit <= productSpus.length"
 | 
			
		||||
        class="select-box"
 | 
			
		||||
        @click="openSpuTableSelect"
 | 
			
		||||
      >
 | 
			
		||||
    <el-tooltip content="选择商品" v-if="canAdd">
 | 
			
		||||
      <div class="select-box" @click="openSpuTableSelect">
 | 
			
		||||
        <Icon icon="ep:plus" />
 | 
			
		||||
      </div>
 | 
			
		||||
    </el-tooltip>
 | 
			
		||||
| 
						 | 
				
			
			@ -31,35 +26,52 @@
 | 
			
		|||
import * as ProductSpuApi from '@/api/mall/product/spu'
 | 
			
		||||
import SpuTableSelect from '@/views/mall/product/spu/components/SpuTableSelect.vue'
 | 
			
		||||
import { propTypes } from '@/utils/propTypes'
 | 
			
		||||
import { array } from 'vue-types'
 | 
			
		||||
import { oneOfType } from 'vue-types'
 | 
			
		||||
import { isArray } from "@/utils/is";
 | 
			
		||||
 | 
			
		||||
// 商品橱窗,一般用于与商品建立关系时使用
 | 
			
		||||
// 提供功能:展示商品列表、添加商品、移除商品
 | 
			
		||||
defineOptions({ name: 'SpuShowcase' })
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  modelValue: array<number>().def([]).isRequired,
 | 
			
		||||
  modelValue: oneOfType<number | Array<number>>([Number, Array]).isRequired,
 | 
			
		||||
  // 限制数量:默认不限制
 | 
			
		||||
  limit: propTypes.number.def(0),
 | 
			
		||||
  limit: propTypes.number.def(Number.MAX_VALUE),
 | 
			
		||||
  disabled: propTypes.bool.def(false)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 计算是否可以添加
 | 
			
		||||
const canAdd = computed(() => {
 | 
			
		||||
  // 情况一:禁用时不可以添加
 | 
			
		||||
  if(props.disabled) return false
 | 
			
		||||
  // 情况二:未指定限制数量时,可以添加
 | 
			
		||||
  if(!props.limit) return true
 | 
			
		||||
  // 情况三:检查已添加数量是否小于限制数量
 | 
			
		||||
  return productSpus.value.length < props.limit
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 商品列表
 | 
			
		||||
const productSpus = ref<ProductSpuApi.Spu[]>([])
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => props.modelValue,
 | 
			
		||||
  async () => {
 | 
			
		||||
    if (props.modelValue.length === 0) {
 | 
			
		||||
    const ids = isArray(props.modelValue)
 | 
			
		||||
        // 情况一:多选
 | 
			
		||||
        ? props.modelValue
 | 
			
		||||
        // 情况二:单选
 | 
			
		||||
        : props.modelValue ? [props.modelValue]: []
 | 
			
		||||
    // 不需要返显
 | 
			
		||||
    if(ids.length === 0) {
 | 
			
		||||
      productSpus.value = []
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    // 只有商品发生变化之后,才去查询商品
 | 
			
		||||
    if (
 | 
			
		||||
      productSpus.value.length === 0 ||
 | 
			
		||||
      productSpus.value.some((spu) => !props.modelValue.includes(spu.id))
 | 
			
		||||
      productSpus.value.some((spu) => !ids.includes(spu.id!))
 | 
			
		||||
    ) {
 | 
			
		||||
      productSpus.value = await ProductSpuApi.getSpuDetailList(props.modelValue)
 | 
			
		||||
      productSpus.value = await ProductSpuApi.getSpuDetailList(ids)
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  { immediate: true }
 | 
			
		||||
| 
						 | 
				
			
			@ -91,11 +103,14 @@ const handleRemoveSpu = (index: number) => {
 | 
			
		|||
}
 | 
			
		||||
const emit = defineEmits(['update:modelValue', 'change'])
 | 
			
		||||
const emitSpuChange = () => {
 | 
			
		||||
  emit(
 | 
			
		||||
    'update:modelValue',
 | 
			
		||||
    productSpus.value.map((spu) => spu.id)
 | 
			
		||||
  )
 | 
			
		||||
  emit('change', productSpus.value)
 | 
			
		||||
  if(props.limit === 1) {
 | 
			
		||||
    const spu = productSpus.value.length > 0 ? productSpus.value[0] : null
 | 
			
		||||
    emit('update:modelValue', spu?.id || 0)
 | 
			
		||||
    emit('change', spu)
 | 
			
		||||
  } else {
 | 
			
		||||
    emit('update:modelValue', productSpus.value.map((spu) => spu.id))
 | 
			
		||||
    emit('change', productSpus.value)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,15 +26,7 @@
 | 
			
		|||
        label="商品"
 | 
			
		||||
        prop="productSpuIds"
 | 
			
		||||
      >
 | 
			
		||||
        <div class="flex flex-wrap items-center gap-1">
 | 
			
		||||
          <div v-for="(spu, index) in productSpus" :key="spu.id" class="select-box spu-pic">
 | 
			
		||||
            <el-image :src="spu.picUrl" />
 | 
			
		||||
            <Icon class="del-icon" icon="ep:circle-close-filled" @click="handleRemoveSpu(index)" />
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="select-box" @click="openSpuTableSelect">
 | 
			
		||||
            <Icon icon="ep:plus" />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <SpuShowcase v-model="formData.productSpuIds" />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item
 | 
			
		||||
        v-if="formData.productScope === PromotionProductScopeEnum.CATEGORY.scope"
 | 
			
		||||
| 
						 | 
				
			
			@ -186,18 +178,16 @@
 | 
			
		|||
      <el-button @click="dialogVisible = false">取 消</el-button>
 | 
			
		||||
    </template>
 | 
			
		||||
  </Dialog>
 | 
			
		||||
  <SpuTableSelect ref="spuTableSelectRef" multiple @change="handleSpuSelected" />
 | 
			
		||||
</template>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 | 
			
		||||
import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
 | 
			
		||||
import * as ProductSpuApi from '@/api/mall/product/spu'
 | 
			
		||||
import {
 | 
			
		||||
  CouponTemplateValidityTypeEnum,
 | 
			
		||||
  PromotionDiscountTypeEnum,
 | 
			
		||||
  PromotionProductScopeEnum
 | 
			
		||||
} from '@/utils/constants'
 | 
			
		||||
import SpuTableSelect from '@/views/mall/product/spu/components/SpuTableSelect.vue'
 | 
			
		||||
import SpuShowcase from "@/views/mall/product/spu/components/SpuShowcase.vue";
 | 
			
		||||
import ProductCategorySelect from '@/views/mall/product/category/components/ProductCategorySelect.vue'
 | 
			
		||||
import { convertToInteger, formatToFraction } from '@/utils'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -251,7 +241,6 @@ const formRules = reactive({
 | 
			
		|||
  productCategoryIds: [{ required: true, message: '分类不能为空', trigger: 'blur' }]
 | 
			
		||||
})
 | 
			
		||||
const formRef = ref() // 表单 Ref
 | 
			
		||||
const productSpus = ref<ProductSpuApi.Spu[]>([]) // 商品列表
 | 
			
		||||
 | 
			
		||||
/** 打开弹窗 */
 | 
			
		||||
const open = async (type: string, id?: number) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -354,7 +343,6 @@ const resetForm = () => {
 | 
			
		|||
    productCategoryIds: []
 | 
			
		||||
  }
 | 
			
		||||
  formRef.value?.resetFields()
 | 
			
		||||
  productSpus.value = []
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 获得商品范围 */
 | 
			
		||||
| 
						 | 
				
			
			@ -363,8 +351,6 @@ const getProductScope = async () => {
 | 
			
		|||
    case PromotionProductScopeEnum.SPU.scope:
 | 
			
		||||
      // 设置商品编号
 | 
			
		||||
      formData.value.productSpuIds = formData.value.productScopeValues
 | 
			
		||||
      // 获得商品列表
 | 
			
		||||
      productSpus.value = await ProductSpuApi.getSpuDetailList(formData.value.productScopeValues)
 | 
			
		||||
      break
 | 
			
		||||
    case PromotionProductScopeEnum.CATEGORY.scope:
 | 
			
		||||
      await nextTick(() => {
 | 
			
		||||
| 
						 | 
				
			
			@ -397,47 +383,7 @@ function setProductScopeValues(data: CouponTemplateApi.CouponTemplateVO) {
 | 
			
		|||
      break
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 活动商品的按钮 */
 | 
			
		||||
const spuTableSelectRef = ref()
 | 
			
		||||
const openSpuTableSelect = () => {
 | 
			
		||||
  spuTableSelectRef.value.open(productSpus.value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 选择商品后触发 */
 | 
			
		||||
const handleSpuSelected = (spus: ProductSpuApi.Spu[]) => {
 | 
			
		||||
  productSpus.value = spus
 | 
			
		||||
  formData.value.productSpuIds = spus.map((spu) => spu.id) as []
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 选择商品后触发 */
 | 
			
		||||
const handleRemoveSpu = (index: number) => {
 | 
			
		||||
  productSpus.value.splice(index, 1)
 | 
			
		||||
  formData.value.productSpuIds.splice(index, 1)
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.select-box {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  width: 60px;
 | 
			
		||||
  height: 60px;
 | 
			
		||||
  border: 1px dashed var(--el-border-color-darker);
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.spu-pic {
 | 
			
		||||
  position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.del-icon {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: -10px;
 | 
			
		||||
  right: -10px;
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
  width: 20px !important;
 | 
			
		||||
  height: 20px !important;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue