Compare commits

...

5 Commits

14 changed files with 189 additions and 48 deletions

View File

@ -242,5 +242,8 @@ export enum DICT_TYPE {
AI_WRITE_LENGTH = 'ai_write_length', // AI 写作长度 AI_WRITE_LENGTH = 'ai_write_length', // AI 写作长度
AI_WRITE_FORMAT = 'ai_write_format', // AI 写作格式 AI_WRITE_FORMAT = 'ai_write_format', // AI 写作格式
AI_WRITE_TONE = 'ai_write_tone', // AI 写作语气 AI_WRITE_TONE = 'ai_write_tone', // AI 写作语气
AI_WRITE_LANGUAGE = 'ai_write_language' // AI 写作语言 AI_WRITE_LANGUAGE = 'ai_write_language', // AI 写作语言
// ========== 通用模块 ==========
ZERO_OR_ONE = 'zero_or_one'
} }

View File

@ -0,0 +1,126 @@
<!-- 商品发布 - 库存价格 -->
<template>
<el-form ref="formRef" :disabled="isDetail" :model="formData" :rules="rules" label-width="120px">
<el-form-item label="付费会员专属" >
<el-switch
v-model="formData.onlyPaidMemberView"
:active-value="1"
:inactive-value="0" />
&nbsp;&nbsp;&nbsp;<el-text class="mx-1" size="small">开启后仅付费会员可以看见并购买此商品</el-text>
</el-form-item>
<el-form-item label="商品推荐">
<el-checkbox
v-model="formData.recommendHot"
:true-value="1"
:false-value="0"
name="productRecommend">
热卖单品
</el-checkbox>
<el-checkbox
v-model="formData.recommendBenefit"
:true-value="1"
:false-value="0"
name="productRecommend">
促销单品
</el-checkbox>
<el-checkbox
v-model="formData.recommendBest"
:true-value="1"
:false-value="0"
name="productRecommend">
精品推荐
</el-checkbox>
<el-checkbox
v-model="formData.recommendNew"
:true-value="1"
:false-value="0"
name="productRecommend">
首发新品
</el-checkbox>
<el-checkbox
v-model="formData.recommendGood"
:true-value="1"
:false-value="0"
name="productRecommend">
优品推荐
</el-checkbox>
</el-form-item>
</el-form>
<!-- 商品属性添加 Form 表单 -->
<ProductPropertyAddForm ref="attributesAddFormRef" :propertyList="propertyList" />
</template>
<script lang="ts" setup>
import { PropType } from 'vue'
import { copyValueToTarget } from '@/utils'
import { propTypes } from '@/utils/propTypes'
import {
getPropertyList,
PropertyAndValues,
RuleConfig,
SkuList
} from '@/views/mall/product/spu/components/index'
import ProductAttributes from './ProductAttributes.vue'
import ProductPropertyAddForm from './ProductPropertyAddForm.vue'
import type { Spu } from '@/api/mall/product/spu'
defineOptions({ name: 'ProductSpuSkuForm' })
const message = useMessage() //
const props = defineProps({
propFormData: {
type: Object as PropType<Spu>,
default: () => {}
},
isDetail: propTypes.bool.def(false) //
})
const attributesAddFormRef = ref() //
const formRef = ref() // Ref
const propertyList = ref<PropertyAndValues[]>([]) //
const skuListRef = ref() // Ref
const formData = reactive<Spu>({
recommendHot: 0, //
recommendBenefit: 0, //
recommendBest: 0, //
recommendNew: 0, //
recommendGood: 0, //
onlyPaidMemberView: 0 //
})
const rules = reactive({
// specType: [required],
// subCommissionType: [required]
})
/** 将传进来的值赋值给 formData */
watch(
() => props.propFormData,
(data) => {
if (!data) {
return
}
copyValueToTarget(formData, data)
// SKU PropertyAndValues
propertyList.value = getPropertyList(data)
},
{
immediate: true
}
)
/** 表单校验 */
const emit = defineEmits(['update:activeName'])
const validate = async () => {
if (!formRef) return
try {
//
Object.assign(props.propFormData, formData)
} catch (e) {
throw e //
}
}
defineExpose({ validate })
</script>

View File

@ -17,43 +17,6 @@
<el-radio :label="true">多规格</el-radio> <el-radio :label="true">多规格</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="商品推荐">
<el-checkbox
v-model="formData.recommendHot"
:true-value="1"
:false-value="0"
name="productRecommend">
热卖单品
</el-checkbox>
<el-checkbox
v-model="formData.recommendBenefit"
:true-value="1"
:false-value="0"
name="productRecommend">
促销单品
</el-checkbox>
<el-checkbox
v-model="formData.recommendBest"
:true-value="1"
:false-value="0"
name="productRecommend">
精品推荐
</el-checkbox>
<el-checkbox
v-model="formData.recommendNew"
:true-value="1"
:false-value="0"
name="productRecommend">
新品单品
</el-checkbox>
<el-checkbox
v-model="formData.recommendGood"
:true-value="1"
:false-value="0"
name="productRecommend">
优品推荐
</el-checkbox>
</el-form-item>
<!-- 多规格添加--> <!-- 多规格添加-->
<el-form-item v-if="!formData.specType"> <el-form-item v-if="!formData.specType">
<SkuList <SkuList
@ -146,11 +109,6 @@ const skuListRef = ref() // 商品属性列表 Ref
const formData = reactive<Spu>({ const formData = reactive<Spu>({
specType: false, // specType: false, //
subCommissionType: false, // subCommissionType: false, //
recommendHot: 0, //
recommendBenefit: 0, //
recommendBest: 0, //
recommendNew: 0, //
recommendGood: 0, //
skus: [] skus: []
}) })
const rules = reactive({ const rules = reactive({

View File

@ -33,6 +33,14 @@
:propFormData="formData" :propFormData="formData"
/> />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="营销设置" name="marketing">
<MarketingForm
ref="marketingRef"
v-model:activeName="activeName"
:is-detail="isDetail"
:propFormData="formData"
/>
</el-tab-pane>
<el-tab-pane label="其它设置" name="other"> <el-tab-pane label="其它设置" name="other">
<OtherForm <OtherForm
ref="otherRef" ref="otherRef"
@ -41,6 +49,7 @@
:propFormData="formData" :propFormData="formData"
/> />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<el-form> <el-form>
<el-form-item style="float: right"> <el-form-item style="float: right">
@ -61,6 +70,7 @@ import DescriptionForm from './DescriptionForm.vue'
import OtherForm from './OtherForm.vue' import OtherForm from './OtherForm.vue'
import SkuForm from './SkuForm.vue' import SkuForm from './SkuForm.vue'
import DeliveryForm from './DeliveryForm.vue' import DeliveryForm from './DeliveryForm.vue'
import MarketingForm from './MarketingForm.vue'
import { convertToInteger, floatToFixed2, formatToFraction } from '@/utils' import { convertToInteger, floatToFixed2, formatToFraction } from '@/utils'
defineOptions({ name: 'ProductSpuForm' }) defineOptions({ name: 'ProductSpuForm' })
@ -79,6 +89,7 @@ const skuRef = ref() // 商品规格 Ref
const deliveryRef = ref() // Ref const deliveryRef = ref() // Ref
const descriptionRef = ref() // Ref const descriptionRef = ref() // Ref
const otherRef = ref() // Ref const otherRef = ref() // Ref
const marketingRef = ref() // Ref
// SPU // SPU
const formData = ref<ProductSpuApi.Spu>({ const formData = ref<ProductSpuApi.Spu>({
name: '', // name: '', //
@ -97,6 +108,7 @@ const formData = ref<ProductSpuApi.Spu>({
recommendBest: 0, // recommendBest: 0, //
recommendNew: 0, // recommendNew: 0, //
recommendGood: 0, // recommendGood: 0, //
onlyPaidMemberView: 0, //
skus: [ skus: [
{ {
price: 0, // price: 0, //
@ -164,6 +176,7 @@ const submitForm = async () => {
await unref(deliveryRef)?.validate() await unref(deliveryRef)?.validate()
await unref(descriptionRef)?.validate() await unref(descriptionRef)?.validate()
await unref(otherRef)?.validate() await unref(otherRef)?.validate()
await unref(marketingRef)?.validate()
// , server // , server
const deepCopyFormData = cloneDeep(unref(formData.value)) as ProductSpuApi.Spu const deepCopyFormData = cloneDeep(unref(formData.value)) as ProductSpuApi.Spu
deepCopyFormData.skus!.forEach((item) => { deepCopyFormData.skus!.forEach((item) => {

View File

@ -1,6 +1,6 @@
<!-- 商品中心 - 商品列表 --> <!-- 商品中心 - 商品列表 -->
<template> <template>
<doc-alert title="【商品】商品 SPU 与 SKU" url="https://doc.iocoder.cn/mall/product-spu-sku/" /> <!-- <doc-alert title="【商品】商品 SPU 与 SKU" url="https://doc.iocoder.cn/mall/product-spu-sku/" /> -->
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<ContentWrap> <ContentWrap>
@ -9,7 +9,7 @@
:inline="true" :inline="true"
:model="queryParams" :model="queryParams"
class="-mb-15px" class="-mb-15px"
label-width="68px" label-width="auto"
> >
<el-form-item label="商品名称" prop="name"> <el-form-item label="商品名称" prop="name">
<el-input <el-input
@ -31,6 +31,16 @@
placeholder="请选择商品分类" placeholder="请选择商品分类"
/> />
</el-form-item> </el-form-item>
<el-form-item label="仅付费会员可见" prop="isPaidMember">
<el-select v-model="queryParams.isPaidMember" style="width: 100px" placeholder="请选择" clearable>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.ZERO_OR_ONE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime"> <el-form-item label="创建时间" prop="createTime">
<el-date-picker <el-date-picker
v-model="queryParams.createTime" v-model="queryParams.createTime"
@ -234,6 +244,7 @@
import { TabsPaneContext } from 'element-plus' import { TabsPaneContext } from 'element-plus'
import { createImageViewer } from '@/components/ImageViewer' import { createImageViewer } from '@/components/ImageViewer'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { defaultProps, handleTree, treeToString } from '@/utils/tree' import { defaultProps, handleTree, treeToString } from '@/utils/tree'
import { ProductSpuStatusEnum } from '@/utils/constants' import { ProductSpuStatusEnum } from '@/utils/constants'
import { fenToYuan } from '@/utils' import { fenToYuan } from '@/utils'
@ -286,7 +297,8 @@ const queryParams = ref({
tabType: 0, tabType: 0,
name: '', name: '',
categoryId: undefined, categoryId: undefined,
createTime: undefined createTime: undefined,
isPaidMember: undefined
}) // }) //
const queryFormRef = ref() // Ref const queryFormRef = ref() // Ref

View File

@ -173,7 +173,7 @@
v-if="checkPermi(['member:user:update-balance'])" v-if="checkPermi(['member:user:update-balance'])"
command="handleUpdateBlance" command="handleUpdateBlance"
> >
修改余额(WIP) 修改余额
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>

View File

@ -51,8 +51,12 @@ public class ProductSpuPageReqVO extends PageParam {
@Schema(description = "商品分类编号", example = "1") @Schema(description = "商品分类编号", example = "1")
private Long categoryId; private Long categoryId;
@Schema(description = "是否是付费会员", example = "1")
private Integer isPaidMember;
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") @Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime; private LocalDateTime[] createTime;
} }

View File

@ -85,6 +85,10 @@ public class ProductSpuRespVO {
@Schema(description = "是否优品推荐") @Schema(description = "是否优品推荐")
@ExcelProperty("是否优品推荐") @ExcelProperty("是否优品推荐")
private Integer recommendGood; private Integer recommendGood;
@Schema(description = "是否仅付费会员可见")
@ExcelProperty("是否仅付费会员可见")
private Integer onlyPaidMemberView;
// ========== SKU 相关字段 ========= // ========== SKU 相关字段 =========
@Schema(description = "规格类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") @Schema(description = "规格类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")

View File

@ -118,4 +118,8 @@ public class ProductSpuSaveReqVO {
@Schema(description = "是否优品推荐") @Schema(description = "是否优品推荐")
@Valid @Valid
private Integer recommendGood; private Integer recommendGood;
@Schema(description = "是否仅付费会员可见")
@Valid
private Integer onlyPaidMemberView;
} }

View File

@ -39,6 +39,9 @@ public class AppProductSpuPageReqVO extends PageParam {
@Schema(description = "排序方式", example = "true") @Schema(description = "排序方式", example = "true")
private Boolean sortAsc; private Boolean sortAsc;
@Schema(description = "是否是付费会员", example = "1")
private Integer isPaidMember;
@AssertTrue(message = "排序字段不合法") @AssertTrue(message = "排序字段不合法")
@JsonIgnore @JsonIgnore
public boolean isSortFieldValid() { public boolean isSortFieldValid() {

View File

@ -23,4 +23,6 @@ public class AppProductSpuRecommendPageReqVo extends PageParam {
private Integer recommendNew; private Integer recommendNew;
@Schema(description = "优品推荐", example = "1") @Schema(description = "优品推荐", example = "1")
private Integer recommendGood; private Integer recommendGood;
@Schema(description = "是否是付费会员", example = "1")
private Integer isPaidMember;
} }

View File

@ -15,6 +15,7 @@ import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import javax.validation.Valid;
import java.util.List; import java.util.List;
/** /**
@ -115,6 +116,11 @@ public class ProductSpuDO extends BaseDO {
*/ */
private Integer recommendGood; private Integer recommendGood;
/**
* 是否仅付费会员可见
*/
private Integer onlyPaidMemberView;
// ========== SKU 相关字段 ========= // ========== SKU 相关字段 =========
/** /**

View File

@ -31,6 +31,7 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
.eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId()) .eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId())
.betweenIfPresent(ProductSpuDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(ProductSpuDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ProductSpuDO::getSort) .orderByDesc(ProductSpuDO::getSort)
.eqIfPresent(ProductSpuDO::getOnlyPaidMemberView, reqVO.getIsPaidMember())
.orderByDesc(ProductSpuDO::getId); .orderByDesc(ProductSpuDO::getId);
appendTabQuery(tabType, queryWrapper); appendTabQuery(tabType, queryWrapper);
return selectPage(reqVO, queryWrapper); return selectPage(reqVO, queryWrapper);
@ -61,7 +62,9 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
.inIfPresent(ProductSpuDO::getCategoryId, categoryIds); .inIfPresent(ProductSpuDO::getCategoryId, categoryIds);
// 上架状态 且有库存 // 上架状态 且有库存
query.eq(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()); query.eq(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus());
if (pageReqVO.getIsPaidMember() == null || pageReqVO.getIsPaidMember() == 0) {
query.eq(ProductSpuDO::getOnlyPaidMemberView, 0);
}
// 排序逻辑 // 排序逻辑
if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) { if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) {
query.last(String.format(" ORDER BY (sales_count + virtual_sales_count) %s, sort DESC, id DESC", query.last(String.format(" ORDER BY (sales_count + virtual_sales_count) %s, sort DESC, id DESC",

View File

@ -186,6 +186,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
if (recommendGood != null && recommendGood != 0){ if (recommendGood != null && recommendGood != 0){
wrapperX.eq(ProductSpuDO::getRecommendGood, recommendGood); wrapperX.eq(ProductSpuDO::getRecommendGood, recommendGood);
} }
if (pageVo.getIsPaidMember() == null || pageVo.getIsPaidMember() == 0){
wrapperX.eq(ProductSpuDO::getOnlyPaidMemberView, 0);
}
wrapperX.eq(ProductSpuDO::getStatus, 1); wrapperX.eq(ProductSpuDO::getStatus, 1);
wrapperX.last(" ORDER BY (sales_count + virtual_sales_count) DESC, sort DESC, id DESC"); wrapperX.last(" ORDER BY (sales_count + virtual_sales_count) DESC, sort DESC, id DESC");
return productSpuMapper.selectList(wrapperX); return productSpuMapper.selectList(wrapperX);