diff --git a/.env.dev b/.env.dev index 610924b8..3c41cc68 100644 --- a/.env.dev +++ b/.env.dev @@ -4,8 +4,8 @@ NODE_ENV=development VITE_DEV=true # 请求路径 -# VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn' -VITE_BASE_URL='http://dofast.demo.huizhizao.vip:20001' +VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn' +# VITE_BASE_URL='http://dofast.demo.huizhizao.vip:20001' # 上传路径 VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload' @@ -35,4 +35,4 @@ VITE_OUT_DIR=dist VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn' # 验证码的开关 -VITE_APP_CAPTCHA_ENABLE=true +VITE_APP_CAPTCHA_ENABLE=false diff --git a/package.json b/package.json index 2bf975ca..5cdca2af 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "pinia": "^2.1.7", "qrcode": "^1.5.3", "qs": "^6.11.2", - "sortablejs": "^1.15.0", "steady-xml": "^0.1.0", "url": "^0.11.3", "video.js": "^7.21.5", @@ -81,7 +80,6 @@ "@types/nprogress": "^0.2.3", "@types/qrcode": "^1.5.5", "@types/qs": "^6.9.10", - "@types/sortablejs": "^1.15.5", "@typescript-eslint/eslint-plugin": "^6.11.0", "@typescript-eslint/parser": "^6.11.0", "@unocss/transformer-variant-group": "^0.57.4", diff --git a/src/api/crm/product/index.ts b/src/api/crm/product/index.ts index c6d5dfdb..2d88cb09 100644 --- a/src/api/crm/product/index.ts +++ b/src/api/crm/product/index.ts @@ -41,3 +41,8 @@ export const deleteProduct = async (id: number) => { export const exportProduct = async (params) => { return await request.download({ url: `/crm/product/export-excel`, params }) } + +// 查询产品操作日志 +export const getOperateLogPage = async (params: any) => { + return await request.get({ url: '/crm/product/operate-log-page', params }) +} diff --git a/src/api/mall/product/category.ts b/src/api/mall/product/category.ts index 8158fc0f..7e80b76a 100644 --- a/src/api/mall/product/category.ts +++ b/src/api/mall/product/category.ts @@ -20,10 +20,6 @@ export interface CategoryVO { * 移动端分类图 */ picUrl: string - /** - * PC 端分类图 - */ - bigPicUrl?: string /** * 分类排序 */ diff --git a/src/api/mall/product/property.ts b/src/api/mall/product/property.ts index ac8bac59..44dc663f 100644 --- a/src/api/mall/product/property.ts +++ b/src/api/mall/product/property.ts @@ -65,16 +65,6 @@ export const getPropertyPage = (params: PageParam) => { return request.get({ url: '/product/property/page', params }) } -// 获得属性项列表 -export const getPropertyList = (params: any) => { - return request.get({ url: '/product/property/list', params }) -} - -// 获得属性项列表 -export const getPropertyListAndValue = (data: any) => { - return request.post({ url: '/product/property/get-value-list', data }) -} - // ------------------------ 属性值 ------------------- // 获得属性值分页 diff --git a/src/api/mall/product/spu.ts b/src/api/mall/product/spu.ts index 8ccd02a5..eee632d5 100644 --- a/src/api/mall/product/spu.ts +++ b/src/api/mall/product/spu.ts @@ -33,14 +33,15 @@ export interface GiveCouponTemplate { export interface Spu { id?: number name?: string // 商品名称 - categoryId?: number | undefined // 商品分类 + categoryId?: number // 商品分类 keyword?: string // 关键字 unit?: number | undefined // 单位 picUrl?: string // 商品封面图 sliderPicUrls?: string[] // 商品轮播图 introduction?: string // 商品简介 + deliveryTypes?: number[] // 配送方式 deliveryTemplateId?: number | undefined // 运费模版 - brandId?: number | undefined // 商品品牌编号 + brandId?: number // 商品品牌编号 specType?: boolean // 商品规格 subCommissionType?: boolean // 分销类型 skus?: Sku[] // sku数组 @@ -48,11 +49,6 @@ export interface Spu { sort?: number // 商品排序 giveIntegral?: number // 赠送积分 virtualSalesCount?: number // 虚拟销量 - recommendHot?: boolean // 是否热卖 - recommendBenefit?: boolean // 是否优惠 - recommendBest?: boolean // 是否精品 - recommendNew?: boolean // 是否新品 - recommendGood?: boolean // 是否优品 price?: number // 商品价格 salesCount?: number // 商品销量 marketPrice?: number // 市场价 @@ -60,7 +56,6 @@ export interface Spu { stock?: number // 商品库存 createTime?: Date // 商品创建时间 status?: number // 商品状态 - activityOrders: number[] // 活动排序 } // 获得 Spu 列表 diff --git a/src/api/mall/promotion/diy/page.ts b/src/api/mall/promotion/diy/page.ts index 1cd34282..a834b240 100644 --- a/src/api/mall/promotion/diy/page.ts +++ b/src/api/mall/promotion/diy/page.ts @@ -5,7 +5,7 @@ export interface DiyPageVO { templateId?: number name: string remark: string - previewImageUrls: string[] + previewPicUrls: string[] property: string } diff --git a/src/api/mall/promotion/diy/template.ts b/src/api/mall/promotion/diy/template.ts index f8d4bc80..87134c95 100644 --- a/src/api/mall/promotion/diy/template.ts +++ b/src/api/mall/promotion/diy/template.ts @@ -7,7 +7,7 @@ export interface DiyTemplateVO { used: boolean usedTime?: Date remark: string - previewImageUrls: string[] + previewPicUrls: string[] property: string } diff --git a/src/api/mall/promotion/reward/rewardActivity.ts b/src/api/mall/promotion/reward/rewardActivity.ts index 50c33564..691db476 100644 --- a/src/api/mall/promotion/reward/rewardActivity.ts +++ b/src/api/mall/promotion/reward/rewardActivity.ts @@ -1,17 +1,18 @@ import request from '@/config/axios' export interface DiscountActivityVO { - id?:number, + id?: number name?: string - startTime?:Date - endTime?:Date - remark?:string - conditionType?:number - productScope?:number - productSpuIds?:number[] - rules?:DiscountProductVO[] + startTime?: Date + endTime?: Date + remark?: string + conditionType?: number + productScope?: number + productSpuIds?: number[] + rules?: DiscountProductVO[] } -//优惠规则 + +// 优惠规则 export interface DiscountProductVO { limit: number discountPrice: number @@ -21,23 +22,26 @@ export interface DiscountProductVO { couponCounts: number[] } - // 新增满减送活动 export const createRewardActivity = async (data: DiscountActivityVO) => { return await request.post({ url: '/promotion/reward-activity/create', data }) } + // 更新满减送活动 export const updateRewardActivity = async (data: DiscountActivityVO) => { return await request.put({ url: '/promotion/reward-activity/update', data }) } + // 查询满减送活动列表 export const getRewardActivityPage = async (params) => { return await request.get({ url: '/promotion/reward-activity/page', params }) } + // 查询满减送活动详情 -export const getReward = async (id:number) => { - return await request.get({ url: '/promotion/reward-activity/get?id='+id, }) +export const getReward = async (id: number) => { + return await request.get({ url: '/promotion/reward-activity/get?id=' + id }) } + // 删除限时折扣活动 export const deleteRewardActivity = async (id: number) => { return await request.delete({ url: '/promotion/reward-activity/delete?id=' + id }) diff --git a/src/components/DiyEditor/components/ComponentContainerProperty.vue b/src/components/DiyEditor/components/ComponentContainerProperty.vue index 4f48e204..9d0750da 100644 --- a/src/components/DiyEditor/components/ComponentContainerProperty.vue +++ b/src/components/DiyEditor/components/ComponentContainerProperty.vue @@ -23,7 +23,7 @@ <template #tip>建议宽度 750px</template> </UploadImg> </el-form-item> - <el-tree :data="treeData" :expand-on-click-node="false"> + <el-tree :data="treeData" :expand-on-click-node="false" default-expand-all> <template #default="{ node, data }"> <el-form-item :label="data.label" @@ -43,7 +43,7 @@ </el-form-item> </template> </el-tree> - <slot name="style" :formData="formData"></slot> + <slot name="style" :style="formData"></slot> </el-form> </el-card> </el-tab-pane> diff --git a/src/components/DiyEditor/components/mobile/CouponCard/component.tsx b/src/components/DiyEditor/components/mobile/CouponCard/component.tsx index 1817428d..689690b0 100644 --- a/src/components/DiyEditor/components/mobile/CouponCard/component.tsx +++ b/src/components/DiyEditor/components/mobile/CouponCard/component.tsx @@ -2,15 +2,13 @@ import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate' import { CouponTemplateValidityTypeEnum, PromotionDiscountTypeEnum } from '@/utils/constants' import { floatToFixed2 } from '@/utils' import { formatDate } from '@/utils/formatTime' +import { object } from 'vue-types' // 优惠值 -// TODO @疯狂:idea 有告警 export const CouponDiscount = defineComponent({ name: 'CouponDiscount', props: { - coupon: { - type: CouponTemplateApi.CouponTemplateVO - } + coupon: object<CouponTemplateApi.CouponTemplateVO>() }, setup(props) { const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO @@ -35,9 +33,7 @@ export const CouponDiscount = defineComponent({ export const CouponDiscountDesc = defineComponent({ name: 'CouponDiscountDesc', props: { - coupon: { - type: CouponTemplateApi.CouponTemplateVO - } + coupon: object<CouponTemplateApi.CouponTemplateVO>() }, setup(props) { const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO @@ -61,9 +57,7 @@ export const CouponDiscountDesc = defineComponent({ export const CouponValidTerm = defineComponent({ name: 'CouponValidTerm', props: { - coupon: { - type: CouponTemplateApi.CouponTemplateVO - } + coupon: object<CouponTemplateApi.CouponTemplateVO>() }, setup(props) { const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO diff --git a/src/components/DiyEditor/components/mobile/CouponCard/config.ts b/src/components/DiyEditor/components/mobile/CouponCard/config.ts index 40c6bed3..304533d1 100644 --- a/src/components/DiyEditor/components/mobile/CouponCard/config.ts +++ b/src/components/DiyEditor/components/mobile/CouponCard/config.ts @@ -24,7 +24,6 @@ export interface CouponCardProperty { } // 定义组件 -// TODO @疯狂:idea 有告警 export const component = { id: 'CouponCard', name: '优惠券', diff --git a/src/components/DiyEditor/components/mobile/MagicCube/config.ts b/src/components/DiyEditor/components/mobile/MagicCube/config.ts index d2edfddf..5e10ab55 100644 --- a/src/components/DiyEditor/components/mobile/MagicCube/config.ts +++ b/src/components/DiyEditor/components/mobile/MagicCube/config.ts @@ -31,7 +31,6 @@ export interface MagicCubeItemProperty { } // 定义组件 -// TODO @疯狂:有 idea 爆红告警 export const component = { id: 'MagicCube', name: '广告魔方', diff --git a/src/components/DiyEditor/components/mobile/ProductCard/config.ts b/src/components/DiyEditor/components/mobile/ProductCard/config.ts index fd4b21fc..735b6ba0 100644 --- a/src/components/DiyEditor/components/mobile/ProductCard/config.ts +++ b/src/components/DiyEditor/components/mobile/ProductCard/config.ts @@ -59,7 +59,6 @@ export interface ProductCardFieldProperty { } // 定义组件 -// TODO @疯狂:idea 有告警 export const component = { id: 'ProductCard', name: '商品卡片', diff --git a/src/components/DiyEditor/components/mobile/ProductList/config.ts b/src/components/DiyEditor/components/mobile/ProductList/config.ts index 6522a314..1f168323 100644 --- a/src/components/DiyEditor/components/mobile/ProductList/config.ts +++ b/src/components/DiyEditor/components/mobile/ProductList/config.ts @@ -38,7 +38,6 @@ export interface ProductListFieldProperty { } // 定义组件 -// TODO @疯狂:idea 有告警 export const component = { id: 'ProductList', name: '商品栏', diff --git a/src/components/DiyEditor/components/mobile/PromotionArticle/index.vue b/src/components/DiyEditor/components/mobile/PromotionArticle/index.vue index 3bca5da9..e003b081 100644 --- a/src/components/DiyEditor/components/mobile/PromotionArticle/index.vue +++ b/src/components/DiyEditor/components/mobile/PromotionArticle/index.vue @@ -1,17 +1,16 @@ <template> - <div class="min-h-30px" v-html="article.content"></div> + <div class="min-h-30px" v-html="article?.content"></div> </template> <script setup lang="ts"> import { PromotionArticleProperty } from './config' import * as ArticleApi from '@/api/mall/promotion/article/index' /** 营销文章 */ -// TODO @疯狂:idea 有告警 defineOptions({ name: 'PromotionArticle' }) // 定义属性 const props = defineProps<{ property: PromotionArticleProperty }>() // 商品列表 -const article = ref<ArticleApi.ArticleVO[]>({}) +const article = ref<ArticleApi.ArticleVO>() watch( () => props.property.id, async () => { diff --git a/src/components/DiyEditor/components/mobile/PromotionCombination/config.ts b/src/components/DiyEditor/components/mobile/PromotionCombination/config.ts index 2b211a03..0c7e9ff7 100644 --- a/src/components/DiyEditor/components/mobile/PromotionCombination/config.ts +++ b/src/components/DiyEditor/components/mobile/PromotionCombination/config.ts @@ -39,13 +39,11 @@ export interface PromotionCombinationFieldProperty { } // 定义组件 -// TODO @疯狂:idea 有告警 export const component = { id: 'PromotionCombination', name: '拼团', icon: 'mdi:account-group', property: { - activityId: undefined, layoutType: 'oneCol', fields: { name: { show: true, color: '#000' }, diff --git a/src/components/DiyEditor/components/mobile/SearchBar/config.ts b/src/components/DiyEditor/components/mobile/SearchBar/config.ts index 877a2cca..ef47b27c 100644 --- a/src/components/DiyEditor/components/mobile/SearchBar/config.ts +++ b/src/components/DiyEditor/components/mobile/SearchBar/config.ts @@ -17,7 +17,6 @@ export interface SearchProperty { export type PlaceholderPosition = 'left' | 'center' // 定义组件 -// TODO @疯狂:idea 这里爆红,可以卡看咋优化下哇:is missing the following properties from type DiyComponent<SearchProperty>: uid, position export const component = { id: 'SearchBar', name: '搜索框', diff --git a/src/components/DiyEditor/components/mobile/VideoPlayer/config.ts b/src/components/DiyEditor/components/mobile/VideoPlayer/config.ts index 90cd6a7a..02f03747 100644 --- a/src/components/DiyEditor/components/mobile/VideoPlayer/config.ts +++ b/src/components/DiyEditor/components/mobile/VideoPlayer/config.ts @@ -19,7 +19,6 @@ export interface VideoPlayerStyle extends ComponentStyle { } // 定义组件 -// TODO @疯狂:idea 有告警 export const component = { id: 'VideoPlayer', name: '视频播放', @@ -33,6 +32,6 @@ export const component = { bgColor: '#fff', marginBottom: 8, height: 300 - } as ComponentStyle + } as VideoPlayerStyle } } as DiyComponent<VideoPlayerProperty> diff --git a/src/components/DiyEditor/components/mobile/VideoPlayer/property.vue b/src/components/DiyEditor/components/mobile/VideoPlayer/property.vue index 96f317d6..7598543b 100644 --- a/src/components/DiyEditor/components/mobile/VideoPlayer/property.vue +++ b/src/components/DiyEditor/components/mobile/VideoPlayer/property.vue @@ -1,9 +1,9 @@ <template> <ComponentContainerProperty v-model="formData.style"> - <template #style="{ formData }"> + <template #style> <el-form-item label="高度" prop="height"> <el-slider - v-model="formData.height" + v-model="formData.style.height" :max="500" :min="100" show-input diff --git a/src/components/DiyEditor/util.ts b/src/components/DiyEditor/util.ts index 6bec25f3..fac26e75 100644 --- a/src/components/DiyEditor/util.ts +++ b/src/components/DiyEditor/util.ts @@ -6,7 +6,7 @@ import { TabBarProperty } from '@/components/DiyEditor/components/mobile/TabBar/ // 页面装修组件 export interface DiyComponent<T> { // 用于区分同一种组件的不同实例 - uid: number + uid?: number // 组件唯一标识 id: string // 组件名称 @@ -21,7 +21,7 @@ export interface DiyComponent<T> { 空:同center fixed: 由组件自己决定位置,如弹窗位于手机中心、浮动按钮一般位于手机右下角 */ - position: 'top' | 'bottom' | 'center' | '' | 'fixed' + position?: 'top' | 'bottom' | 'center' | '' | 'fixed' // 组件属性 property: T } @@ -103,8 +103,7 @@ export function usePropertyForm<T>(modelValue: T, emit: Function): { formData: R } ) - // TODO @疯狂:这个 idea 爆红,看看怎么可以解决哈 - return { formData } + return { formData } as { formData: Ref<T> } } // 页面组件库 diff --git a/src/components/UploadFile/src/UploadImg.vue b/src/components/UploadFile/src/UploadImg.vue index 101801e6..a79c84b5 100644 --- a/src/components/UploadFile/src/UploadImg.vue +++ b/src/components/UploadFile/src/UploadImg.vue @@ -16,7 +16,7 @@ <template v-if="modelValue"> <img :src="modelValue" class="upload-image" /> <div class="upload-handle" @click.stop> - <div class="handle-icon" @click="editImg"> + <div class="handle-icon" @click="editImg" v-if="!disabled"> <Icon icon="ep:edit" /> <span v-if="showBtnText">{{ t('action.edit') }}</span> </div> @@ -24,7 +24,7 @@ <Icon icon="ep:zoom-in" /> <span v-if="showBtnText">{{ t('action.detail') }}</span> </div> - <div v-if="showDelete" class="handle-icon" @click="deleteImg"> + <div v-if="showDelete && !disabled" class="handle-icon" @click="deleteImg"> <Icon icon="ep:delete" /> <span v-if="showBtnText">{{ t('action.del') }}</span> </div> diff --git a/src/components/UploadFile/src/UploadImgs.vue b/src/components/UploadFile/src/UploadImgs.vue index 91bb5e31..041c8ca5 100644 --- a/src/components/UploadFile/src/UploadImgs.vue +++ b/src/components/UploadFile/src/UploadImgs.vue @@ -28,7 +28,7 @@ <Icon icon="ep:zoom-in" /> <span>查看</span> </div> - <div class="handle-icon" @click="handleRemove(file)"> + <div class="handle-icon" @click="handleRemove(file)" v-if="!disabled"> <Icon icon="ep:delete" /> <span>删除</span> </div> diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts index b2e5fac9..07fa7bb8 100644 --- a/src/router/modules/remaining.ts +++ b/src/router/modules/remaining.ts @@ -473,8 +473,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ title: '模板装修', noCache: true, hidden: true, - // TODO @疯狂:建议 menu 那的 /mall/promotion/diy-template/diy-template 改成 /mall/promotion/diy/template - activeMenu: '/mall/promotion/diy-template/diy-template' + activeMenu: '/mall/promotion/diy/template' }, component: () => import('@/views/mall/promotion/diy/template/decorate.vue') }, @@ -485,8 +484,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ title: '页面装修', noCache: true, hidden: true, - // TODO @疯狂:建议 menu 那的 /mall/promotion/diy-template/diy-page 改成 /mall/promotion/diy/page - activeMenu: '/mall/promotion/diy-template/diy-page' + activeMenu: '/mall/promotion/diy/page' }, component: () => import('@/views/mall/promotion/diy/page/decorate.vue') } @@ -519,6 +517,17 @@ const remainingRouter: AppRouteRecordRaw[] = [ activeMenu: '/crm/contact' }, component: () => import('@/views/crm/contact/detail/index.vue') + }, + { + path: 'product/detail/:id', + name: 'CrmProductDetail', + meta: { + title: '产品详情', + noCache: true, + hidden: true, + activeMenu: '/crm/product' + }, + component: () => import('@/views/crm/product/detail/index.vue') } ] } diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 9d72124e..747dc887 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -103,7 +103,6 @@ export const getDictLabel = (dictType: string, value: any): string => { export enum DICT_TYPE { USER_TYPE = 'user_type', COMMON_STATUS = 'common_status', - SYSTEM_TENANT_PACKAGE_ID = 'system_tenant_package_id', TERMINAL = 'terminal', // 终端 // ========== SYSTEM 模块 ========== diff --git a/src/utils/index.ts b/src/utils/index.ts index 771bb7a4..25c76f01 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -177,7 +177,7 @@ export const fileSizeFormatter = (row, column, cellValue) => { * @param target 目标对象 * @param source 源对象 */ -export const copyValueToTarget = (target, source) => { +export const copyValueToTarget = (target: any, source: any) => { const newObj = Object.assign({}, target, source) // 删除多余属性 Object.keys(newObj).forEach((key) => { @@ -194,10 +194,10 @@ export const copyValueToTarget = (target, source) => { * 将一个整数转换为分数保留两位小数 * @param num */ -export const formatToFraction = (num: number | string | undefined): number => { - if (typeof num === 'undefined') return 0 +export const formatToFraction = (num: number | string | undefined): string => { + if (typeof num === 'undefined') return '0.00' const parsedNumber = typeof num === 'string' ? parseFloat(num) : num - return parseFloat((parsedNumber / 100).toFixed(2)) + return (parsedNumber / 100.0).toFixed(2) } /** @@ -249,7 +249,7 @@ export const yuanToFen = (amount: string | number): number => { /** * 分转元 */ -export const fenToYuan = (price: string | number): number => { +export const fenToYuan = (price: string | number): string => { return formatToFraction(price) } diff --git a/src/views/crm/product/ProductDetail.vue b/src/views/crm/product/ProductDetail.vue deleted file mode 100644 index f2638df0..00000000 --- a/src/views/crm/product/ProductDetail.vue +++ /dev/null @@ -1,71 +0,0 @@ -<template> - <Dialog v-model="dialogVisible" :max-height="500" :scroll="true" title="产品详情"> - <el-descriptions :column="1" border> - <el-descriptions-item label="产品名称"> - {{ detailData.name }} - </el-descriptions-item> - <el-descriptions-item label="创建时间"> - {{ formatDate(detailData.createTime) }} - </el-descriptions-item> - <el-descriptions-item label="状态"> - <dict-tag :type="DICT_TYPE.CRM_PRODUCT_STATUS" :value="detailData.status" /> - </el-descriptions-item> - <el-descriptions-item label="产品分类"> - {{ productCategoryList?.find((c) => c.id === detailData.categoryId)?.name }} - </el-descriptions-item> - <el-descriptions-item label="产品编码"> - {{ detailData.no }} - </el-descriptions-item> - <el-descriptions-item label="产品描述"> - {{ detailData.description }} - </el-descriptions-item> - <el-descriptions-item label="负责人"> - {{ detailData.ownerUserId }} - </el-descriptions-item> - <el-descriptions-item label="单位"> - <dict-tag :type="DICT_TYPE.PRODUCT_UNIT" :value="detailData.unit" /> - </el-descriptions-item> - <el-descriptions-item label="价格"> - {{ fenToYuan(detailData.price) }}元 - </el-descriptions-item> - </el-descriptions> - </Dialog> -</template> -<script setup lang="ts"> -// TODO 芋艿:统一改成,独立 tab -import { DICT_TYPE } from '@/utils/dict' -import * as ProductCategoryApi from '@/api/crm/product/productCategory' -import * as ProductApi from '@/api/crm/product' -import { formatDate } from '@/utils/formatTime' -import { fenToYuan } from '@/utils' -import { getSimpleUserList, UserVO } from '@/api/system/user' - -defineOptions({ name: 'CrmProductDetail' }) - -const { t } = useI18n() // 国际化 - -const dialogVisible = ref(false) // 弹窗的是否展示 -const detailLoading = ref(false) // 表单的加载中 -const detailData = ref() // 详情数据 - -/** 打开弹窗 */ -const open = async (data: ProductApi.ProductVO) => { - dialogVisible.value = true - // 设置数据 - detailLoading.value = true - try { - detailData.value = data - } finally { - detailLoading.value = false - } -} -defineExpose({ open }) // 提供 open 方法,用于打开弹窗 - -const productCategoryList = ref([]) // 产品分类树 -const userList = ref<UserVO[]>([]) // 系统用户 - -onMounted(async () => { - productCategoryList.value = await ProductCategoryApi.getProductCategoryList({}) - userList.value = await getSimpleUserList() -}) -</script> diff --git a/src/views/crm/product/ProductForm.vue b/src/views/crm/product/ProductForm.vue index 0864c8d8..e12af4c8 100644 --- a/src/views/crm/product/ProductForm.vue +++ b/src/views/crm/product/ProductForm.vue @@ -62,13 +62,13 @@ </el-col> <el-col :span="12"> <el-form-item label="价格" prop="price"> - <el-input - type="number" + <el-input-number v-model="formData.price" placeholder="请输入价格" :min="0" :precision="2" :step="0.1" + class="w-full!" /> </el-form-item> </el-col> @@ -149,7 +149,7 @@ const open = async (type: string, id?: number) => { formLoading.value = true try { formData.value = await ProductApi.getProduct(id) - formData.value.price = fenToYuan(formData.value.price) + formData.value.price = Number(fenToYuan(formData.value.price)) } finally { formLoading.value = false } diff --git a/src/views/crm/product/detail/ProductDetailsHeader.vue b/src/views/crm/product/detail/ProductDetailsHeader.vue new file mode 100644 index 00000000..356f1dbc --- /dev/null +++ b/src/views/crm/product/detail/ProductDetailsHeader.vue @@ -0,0 +1,55 @@ +<template> + <div> + <div class="flex items-start justify-between"> + <div> + <el-col> + <el-row> + <span class="text-xl font-bold">{{ product.name }}</span> + </el-row> + </el-col> + </div> + <div> + <!-- 右上:按钮 --> + <el-button @click="openForm('update', product.id)" v-hasPermi="['crm:product:update']"> + 编辑 + </el-button> + </div> + </div> + </div> + <ContentWrap class="mt-10px"> + <el-descriptions :column="5" direction="vertical"> + <el-descriptions-item label="产品类别"> + {{ productCategoryList?.find((c) => c.id === product.categoryId)?.name }} + </el-descriptions-item> + <el-descriptions-item label="产品单位"> + <dict-tag :type="DICT_TYPE.PRODUCT_UNIT" :value="product.unit" /> + </el-descriptions-item> + <el-descriptions-item label="产品价格">{{ fenToYuan(product.price) }}元</el-descriptions-item> + <el-descriptions-item label="产品编码">{{ product.no }}</el-descriptions-item> + </el-descriptions> + </ContentWrap> + <!-- 表单弹窗:添加/修改 --> + <ProductForm ref="formRef" @success="emit('refresh')" /> +</template> +<script setup lang="ts"> +import ProductForm from '@/views/crm/product/ProductForm.vue' +import { DICT_TYPE } from '@/utils/dict' +import { fenToYuan } from '@/utils' +import * as ProductApi from '@/api/crm/product' +import * as ProductCategoryApi from '@/api/crm/product/productCategory' + +// 操作修改 +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} +const { product } = defineProps<{ product: ProductApi.ProductVO }>() +const emit = defineEmits(['refresh']) // 定义 success 事件,用于操作成功后的回调 + +/** 初始化 */ +const productCategoryList = ref([]) // 产品分类树 + +onMounted(async () => { + productCategoryList.value = await ProductCategoryApi.getProductCategoryList({}) +}) +</script> diff --git a/src/views/crm/product/detail/ProductDetailsInfo.vue b/src/views/crm/product/detail/ProductDetailsInfo.vue new file mode 100644 index 00000000..85046b85 --- /dev/null +++ b/src/views/crm/product/detail/ProductDetailsInfo.vue @@ -0,0 +1,45 @@ +<template> + <ContentWrap> + <el-collapse v-model="activeNames"> + <el-collapse-item name="basicInfo"> + <template #title> + <span class="text-base font-bold">基本信息</span> + </template> + <el-descriptions :column="4"> + <el-descriptions-item label="产品名称">{{ product.name }}</el-descriptions-item> + <el-descriptions-item label="产品编码">{{ product.no }}</el-descriptions-item> + <el-descriptions-item label="价格">{{ fenToYuan(product.price) }}元</el-descriptions-item> + <el-descriptions-item label="产品描述">{{ product.description }}</el-descriptions-item> + <el-descriptions-item label="产品类型"> + {{ productCategoryList?.find((c) => c.id === product.categoryId)?.name }} + </el-descriptions-item> + <el-descriptions-item label="是否上下架"> + <dict-tag :type="DICT_TYPE.CRM_PRODUCT_STATUS" :value="product.status"/> + </el-descriptions-item> + <el-descriptions-item label="单位"> + <dict-tag :type="DICT_TYPE.PRODUCT_UNIT" :value="product.unit"/> + </el-descriptions-item> + </el-descriptions> + </el-collapse-item> + </el-collapse> + </ContentWrap> +</template> +<script setup lang="ts"> +import {DICT_TYPE} from '@/utils/dict' +import * as ProductApi from '@/api/crm/product' +import {fenToYuan} from '@/utils' +import * as ProductCategoryApi from '@/api/crm/product/productCategory' + +const {product} = defineProps<{ + product: ProductApi.ProductVO +}>() + +// 展示的折叠面板 +const activeNames = ref(['basicInfo']) + +/** 初始化 */ +const productCategoryList = ref([]) // 产品分类树 +onMounted(async () => { + productCategoryList.value = await ProductCategoryApi.getProductCategoryList({}) +}) +</script> diff --git a/src/views/crm/product/detail/index.vue b/src/views/crm/product/detail/index.vue new file mode 100644 index 00000000..847e9445 --- /dev/null +++ b/src/views/crm/product/detail/index.vue @@ -0,0 +1,62 @@ +<template> + <ProductDetailsHeader :product="product" :loading="loading" @refresh="getProductData(id)" /> + <el-col> + <el-tabs> + <el-tab-pane label="详细资料"> + <ProductDetailsInfo :product="product" /> + </el-tab-pane> + <el-tab-pane label="操作日志"> + <OperateLogV2 :log-list="logList" /> + </el-tab-pane> + </el-tabs> + </el-col> +</template> +<script setup lang="ts"> +import { useTagsViewStore } from '@/store/modules/tagsView' +import { OperateLogV2VO } from '@/api/system/operatelog' +import * as ProductApi from '@/api/crm/product' +import ProductDetailsHeader from '@/views/crm/product/detail/ProductDetailsHeader.vue' +import ProductDetailsInfo from '@/views/crm/product/detail/ProductDetailsInfo.vue' + +defineOptions({ name: 'CrmProductDetail' }) + +const route = useRoute() +const id = Number(route.params.id) // 编号 +const loading = ref(true) // 加载中 +const product = ref<ProductApi.ProductVO>({} as ProductApi.ProductVO) // 详情 + +/** 获取详情 */ +const getProductData = async (id: number) => { + loading.value = true + try { + product.value = await ProductApi.getProduct(id) + await getOperateLog(id) + } finally { + loading.value = false + } +} + +/** 获取操作日志 */ +const logList = ref<OperateLogV2VO[]>([]) // 操作日志列表 +const getOperateLog = async (productId: number) => { + if (!productId) { + return + } + const data = await ProductApi.getOperateLogPage({ + bizId: productId + }) + logList.value = data.list +} + +/** 初始化 */ +const { delView } = useTagsViewStore() // 视图操作 +const { currentRoute } = useRouter() // 路由 +onMounted(async () => { + if (!id) { + ElMessage.warning('参数错误,产品不能为空!') + delView(unref(currentRoute)) + return + } + await getProductData(id) +}) +</script> diff --git a/src/views/crm/product/index.vue b/src/views/crm/product/index.vue index 3f01455c..5100d7f4 100644 --- a/src/views/crm/product/index.vue +++ b/src/views/crm/product/index.vue @@ -28,8 +28,8 @@ </el-select> </el-form-item> <el-form-item> - <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> - <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button @click="handleQuery"> <Icon icon="ep:search" class="mr-5px" /> 搜索 </el-button> + <el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" /> 重置 </el-button> <el-button type="primary" @click="openForm('create')" v-hasPermi="['crm:product:create']"> <Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button> @@ -40,7 +40,8 @@ :loading="exportLoading" v-hasPermi="['crm:product:export']" > - <Icon icon="ep:download" class="mr-5px" /> 导出 + <Icon icon="ep:download" class="mr-5px" /> + 导出 </el-button> </el-form-item> </el-form> @@ -49,8 +50,14 @@ <!-- 列表 --> <ContentWrap> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> - <el-table-column label="产品名称" align="center" prop="name" /> - <el-table-column label="产品类型" align="center" prop="categoryName" /> + <el-table-column label="产品名称" align="center" prop="name" width="160"> + <template #default="scope"> + <el-link :underline="false" type="primary" @click="openDetail(scope.row.id)"> + {{ scope.row.name }} + </el-link> + </template> + </el-table-column> + <el-table-column label="产品类型" align="center" prop="categoryName" width="160" /> <el-table-column label="产品单位" align="center" prop="unit"> <template #default="scope"> <dict-tag :type="DICT_TYPE.PRODUCT_UNIT" :value="scope.row.unit" /> @@ -62,14 +69,15 @@ align="center" prop="price" :formatter="fenToYuanFormat" + width="100" /> - <el-table-column label="产品描述" align="center" prop="description" /> - <el-table-column label="是否上下架" align="center" prop="status"> + <el-table-column label="产品描述" align="center" prop="description" width="150" /> + <el-table-column label="上架状态" align="center" prop="status" width="120"> <template #default="scope"> <dict-tag :type="DICT_TYPE.CRM_PRODUCT_STATUS" :value="scope.row.status" /> </template> </el-table-column> - <el-table-column label="负责人" align="center" prop="ownerUserName" /> + <el-table-column label="负责人" align="center" prop="ownerUserName" width="120" /> <el-table-column label="更新时间" align="center" @@ -77,7 +85,7 @@ :formatter="dateFormatter" width="180px" /> - <el-table-column label="创建" align="center" prop="creatorName" /> + <el-table-column label="创建人" align="center" prop="creatorName" width="120" /> <el-table-column label="创建时间" align="center" @@ -85,16 +93,8 @@ :formatter="dateFormatter" width="180px" /> - <el-table-column label="操作" align="center" width="160"> + <el-table-column label="操作" align="center" fixed="right" width="160"> <template #default="scope"> - <el-button - v-hasPermi="['crm:product:query']" - link - type="primary" - @click="openDetail(scope.row)" - > - 详情 - </el-button> <el-button link type="primary" @@ -125,8 +125,6 @@ <!-- 表单弹窗:添加/修改 --> <ProductForm ref="formRef" @success="getList" /> - <!-- 表单弹窗:详情 --> - <ProductDetail ref="detailRef" @success="getList" /> </template> <script setup lang="ts"> @@ -135,7 +133,6 @@ import { dateFormatter } from '@/utils/formatTime' import download from '@/utils/download' import * as ProductApi from '@/api/crm/product' import ProductForm from './ProductForm.vue' -import ProductDetail from './ProductDetail.vue' import { fenToYuanFormat } from '@/utils/formatter' defineOptions({ name: 'CrmProduct' }) @@ -184,10 +181,11 @@ const formRef = ref() const openForm = (type: string, id?: number) => { formRef.value.open(type, id) } -/** 详情操作 */ -const detailRef = ref() -const openDetail = (data: ProductApi.ProductVO) => { - detailRef.value.open(data) + +/** 打开详情 */ +const { currentRoute, push } = useRouter() +const openDetail = (id: number) => { + push({ name: 'CrmProductDetail', params: { id } }) } /** 删除按钮操作 */ @@ -218,8 +216,13 @@ const handleExport = async () => { } } +/** 激活时 */ +onActivated(() => { + getList() +}) + /** 初始化 **/ -onMounted(async () => { - await getList() +onMounted(() => { + getList() }) </script> diff --git a/src/views/mall/product/category/CategoryForm.vue b/src/views/mall/product/category/CategoryForm.vue index 5d738525..7f209275 100644 --- a/src/views/mall/product/category/CategoryForm.vue +++ b/src/views/mall/product/category/CategoryForm.vue @@ -25,10 +25,6 @@ <UploadImg v-model="formData.picUrl" :limit="1" :is-show-tip="false" /> <div style="font-size: 10px" class="pl-10px">推荐 180x180 图片分辨率</div> </el-form-item> - <el-form-item label="PC 端分类图" prop="bigPicUrl"> - <UploadImg v-model="formData.bigPicUrl" :limit="1" :is-show-tip="false" /> - <div style="font-size: 10px" class="pl-10px">推荐 468x340 图片分辨率</div> - </el-form-item> <el-form-item label="分类排序" prop="sort"> <el-input-number v-model="formData.sort" controls-position="right" :min="0" /> </el-form-item> @@ -68,7 +64,6 @@ const formData = ref({ id: undefined, name: '', picUrl: '', - bigPicUrl: '', status: CommonStatusEnum.ENABLE }) const formRules = reactive({ @@ -133,7 +128,6 @@ const resetForm = () => { id: undefined, name: '', picUrl: '', - bigPicUrl: '', status: CommonStatusEnum.ENABLE } formRef.value?.resetFields() diff --git a/src/views/mall/product/category/index.vue b/src/views/mall/product/category/index.vue index 9bfd1eff..5fa4ef50 100644 --- a/src/views/mall/product/category/index.vue +++ b/src/views/mall/product/category/index.vue @@ -38,7 +38,7 @@ <el-table-column label="名称" min-width="240" prop="name" sortable /> <el-table-column label="分类图标" align="center" min-width="80" prop="picUrl"> <template #default="scope"> - <img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="移动端分类图" class="h-36px" /> + <img :src="scope.row.picUrl" alt="移动端分类图" class="h-36px" /> </template> </el-table-column> <el-table-column label="排序" align="center" min-width="150" prop="sort" /> diff --git a/src/views/mall/product/property/value/index.vue b/src/views/mall/product/property/value/index.vue index 700afb5a..d708172c 100644 --- a/src/views/mall/product/property/value/index.vue +++ b/src/views/mall/product/property/value/index.vue @@ -9,7 +9,7 @@ label-width="68px" > <el-form-item label="属性项" prop="propertyId"> - <el-select v-model="queryParams.propertyId" class="!w-240px"> + <el-select v-model="queryParams.propertyId" class="!w-240px" disabled> <el-option v-for="item in propertyOptions" :key="item.id" @@ -158,6 +158,6 @@ const handleDelete = async (id: number) => { onMounted(async () => { await getList() // 属性项下拉框数据 - propertyOptions.value = await PropertyApi.getPropertyList({}) + propertyOptions.value.push(await PropertyApi.getProperty(queryParams.propertyId)) }) </script> diff --git a/src/views/mall/product/spu/components/SkuList.vue b/src/views/mall/product/spu/components/SkuList.vue index efbc1ae7..2befe640 100644 --- a/src/views/mall/product/spu/components/SkuList.vue +++ b/src/views/mall/product/spu/components/SkuList.vue @@ -8,9 +8,9 @@ max-height="500" size="small" > - <el-table-column align="center" fixed="left" label="图片" min-width="100"> + <el-table-column align="center" label="图片" min-width="65"> <template #default="{ row }"> - <UploadImg v-model="row.picUrl" height="80px" width="100%" /> + <UploadImg v-model="row.picUrl" height="50px" width="50px" /> </template> </el-table-column> <template v-if="formData!.specType && !isBatch"> @@ -34,12 +34,19 @@ <el-input v-model="row.barCode" class="w-100%" /> </template> </el-table-column> - <el-table-column align="center" label="销售价(元)" min-width="168"> + <el-table-column align="center" label="销售价" min-width="168"> <template #default="{ row }"> - <el-input-number v-model="row.price" :min="0" :precision="2" :step="0.1" class="w-100%" /> + <el-input-number + v-model="row.price" + :min="0" + :precision="2" + :step="0.1" + class="w-100%" + controls-position="right" + /> </template> </el-table-column> - <el-table-column align="center" label="市场价(元)" min-width="168"> + <el-table-column align="center" label="市场价" min-width="168"> <template #default="{ row }"> <el-input-number v-model="row.marketPrice" @@ -47,10 +54,11 @@ :precision="2" :step="0.1" class="w-100%" + controls-position="right" /> </template> </el-table-column> - <el-table-column align="center" label="成本价(元)" min-width="168"> + <el-table-column align="center" label="成本价" min-width="168"> <template #default="{ row }"> <el-input-number v-model="row.costPrice" @@ -58,22 +66,37 @@ :precision="2" :step="0.1" class="w-100%" + controls-position="right" /> </template> </el-table-column> <el-table-column align="center" label="库存" min-width="168"> <template #default="{ row }"> - <el-input-number v-model="row.stock" :min="0" class="w-100%" /> + <el-input-number v-model="row.stock" :min="0" class="w-100%" controls-position="right" /> </template> </el-table-column> <el-table-column align="center" label="重量(kg)" min-width="168"> <template #default="{ row }"> - <el-input-number v-model="row.weight" :min="0" :precision="2" :step="0.1" class="w-100%" /> + <el-input-number + v-model="row.weight" + :min="0" + :precision="2" + :step="0.1" + class="w-100%" + controls-position="right" + /> </template> </el-table-column> <el-table-column align="center" label="体积(m^3)" min-width="168"> <template #default="{ row }"> - <el-input-number v-model="row.volume" :min="0" :precision="2" :step="0.1" class="w-100%" /> + <el-input-number + v-model="row.volume" + :min="0" + :precision="2" + :step="0.1" + class="w-100%" + controls-position="right" + /> </template> </el-table-column> <template v-if="formData!.subCommissionType"> @@ -85,6 +108,7 @@ :precision="2" :step="0.1" class="w-100%" + controls-position="right" /> </template> </el-table-column> @@ -96,6 +120,7 @@ :precision="2" :step="0.1" class="w-100%" + controls-position="right" /> </template> </el-table-column> @@ -124,7 +149,12 @@ <el-table-column v-if="isComponent" type="selection" width="45" /> <el-table-column align="center" label="图片" min-width="80"> <template #default="{ row }"> - <el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" /> + <el-image + v-if="row.picUrl" + :src="row.picUrl" + class="h-50px w-50px" + @click="imagePreview(row.picUrl)" + /> </template> </el-table-column> <template v-if="formData!.specType && !isBatch"> diff --git a/src/views/mall/product/spu/form/ActivityOrdersSort.vue b/src/views/mall/product/spu/form/ActivityOrdersSort.vue deleted file mode 100644 index 3a41b3c5..00000000 --- a/src/views/mall/product/spu/form/ActivityOrdersSort.vue +++ /dev/null @@ -1,66 +0,0 @@ -<template> - <div ref="elTagWrappingRef"> - <template v-if="activityOrders && activityOrders.length > 0"> - <el-tag - v-for="activityType in activityOrders" - :key="activityType" - :type="promotionTypes.find((item) => item.value === activityType)?.colorType" - class="mr-[10px]" - > - {{ promotionTypes.find((item) => item.value === activityType)?.label }} - </el-tag> - </template> - <template v-else> - <el-tag - v-for="type in promotionTypes" - :key="type.value as number" - :type="type.colorType" - class="mr-[10px]" - > - {{ type.label }} - </el-tag> - </template> - </div> -</template> -<script lang="ts" setup> -import Sortable from 'sortablejs' -import type { DictDataType } from '@/utils/dict' - -defineOptions({ name: 'ActivityOrdersSort' }) -const props = defineProps<{ - promotionTypes: DictDataType[] - activityOrders: number[] -}>() -const emit = defineEmits<{ - (e: 'update:activityOrders', v: number[]) -}>() -const elTagWrappingRef = ref() // elTag 容器 Ref - -const initSortable = () => { - new Sortable(elTagWrappingRef.value, { - swapThreshold: 1, - animation: 150, - onEnd: (el) => { - const innerText = el.to.innerText - // 将字符串按换行符分割成数组 - const activityOrder = innerText.split('\n') - // 根据字符串中的顺序重新排序数组 - const sortedActivityOrder = activityOrder.map((activityName) => { - return props.promotionTypes.find((item) => item.label === activityName)?.value - }) - emit('update:activityOrders', sortedActivityOrder as number[]) - } - }) -} -onMounted(async () => { - await nextTick() - // 如果活动排序为空也就是新增的时候加入活动 - if (props.activityOrders && props.activityOrders.length === 0) { - emit( - 'update:activityOrders', - props.promotionTypes.map((item) => item.value as number) - ) - } - initSortable() -}) -</script> diff --git a/src/views/mall/product/spu/form/BasicInfoForm.vue b/src/views/mall/product/spu/form/BasicInfoForm.vue deleted file mode 100644 index f7da536d..00000000 --- a/src/views/mall/product/spu/form/BasicInfoForm.vue +++ /dev/null @@ -1,375 +0,0 @@ -<template> - <!-- 情况一:添加/修改 --> - <el-form - v-if="!isDetail" - ref="productSpuBasicInfoRef" - :model="formData" - :rules="rules" - label-width="120px" - > - <el-row> - <el-col :span="12"> - <el-form-item label="商品名称" prop="name"> - <el-input v-model="formData.name" placeholder="请输入商品名称" /> - </el-form-item> - </el-col> - <el-col :span="12"> - <el-form-item label="商品分类" prop="categoryId"> - <el-cascader - v-model="formData.categoryId" - :options="categoryList" - :props="defaultProps" - class="w-1/1" - clearable - placeholder="请选择商品分类" - filterable - /> - </el-form-item> - </el-col> - <el-col :span="12"> - <el-form-item label="商品关键字" prop="keyword"> - <el-input v-model="formData.keyword" placeholder="请输入商品关键字" /> - </el-form-item> - </el-col> - <el-col :span="12"> - <el-form-item label="单位" prop="unit"> - <el-select v-model="formData.unit" class="w-1/1" placeholder="请选择单位"> - <el-option - v-for="dict in getIntDictOptions(DICT_TYPE.PRODUCT_UNIT)" - :key="dict.value" - :label="dict.label" - :value="dict.value" - /> - </el-select> - </el-form-item> - </el-col> - <el-col :span="12"> - <el-form-item label="商品简介" prop="introduction"> - <el-input - v-model="formData.introduction" - :rows="3" - placeholder="请输入商品简介" - type="textarea" - /> - </el-form-item> - </el-col> - <el-col :span="12"> - <el-form-item label="商品封面图" prop="picUrl"> - <UploadImg v-model="formData.picUrl" height="80px" /> - </el-form-item> - </el-col> - <el-col :span="24"> - <el-form-item label="商品轮播图" prop="sliderPicUrls"> - <UploadImgs v-model:modelValue="formData.sliderPicUrls" /> - </el-form-item> - </el-col> - <el-col :span="12"> - <el-form-item label="运费模板" prop="deliveryTemplateId"> - <el-select v-model="formData.deliveryTemplateId" placeholder="请选择"> - <el-option - v-for="item in deliveryTemplateList" - :key="item.id" - :label="item.name" - :value="item.id" - /> - </el-select> - </el-form-item> - </el-col> - <el-col :span="12"> - <el-form-item label="品牌" prop="brandId"> - <el-select v-model="formData.brandId" placeholder="请选择"> - <el-option - v-for="item in brandList" - :key="item.id" - :label="item.name" - :value="item.id" - /> - </el-select> - </el-form-item> - </el-col> - <el-col :span="12"> - <el-form-item label="商品规格" props="specType"> - <el-radio-group v-model="formData.specType" @change="onChangeSpec"> - <el-radio :label="false" class="radio">单规格</el-radio> - <el-radio :label="true">多规格</el-radio> - </el-radio-group> - </el-form-item> - </el-col> - <el-col :span="12"> - <el-form-item label="分销类型" props="subCommissionType"> - <el-radio-group v-model="formData.subCommissionType" @change="changeSubCommissionType"> - <el-radio :label="false">默认设置</el-radio> - <el-radio :label="true" class="radio">单独设置</el-radio> - </el-radio-group> - </el-form-item> - </el-col> - <!-- 多规格添加--> - <el-col :span="24"> - <el-form-item v-if="!formData.specType"> - <SkuList - ref="skuListRef" - :prop-form-data="formData" - :propertyList="propertyList" - :rule-config="ruleConfig" - /> - </el-form-item> - <el-form-item v-if="formData.specType" label="商品属性"> - <el-button class="mb-10px mr-15px" @click="attributesAddFormRef.open">添加属性</el-button> - <ProductAttributes :propertyList="propertyList" @success="generateSkus" /> - </el-form-item> - <template v-if="formData.specType && propertyList.length > 0"> - <el-form-item label="批量设置"> - <SkuList :is-batch="true" :prop-form-data="formData" :propertyList="propertyList" /> - </el-form-item> - <el-form-item label="属性列表"> - <SkuList - ref="skuListRef" - :prop-form-data="formData" - :propertyList="propertyList" - :rule-config="ruleConfig" - /> - </el-form-item> - </template> - </el-col> - </el-row> - </el-form> - - <!-- 情况二:详情 --> - <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema"> - <template #categoryId="{ row }"> {{ formatCategoryName(row.categoryId) }}</template> - <template #brandId="{ row }"> - {{ brandList.find((item) => item.id === row.brandId)?.name }} - </template> - <template #deliveryTemplateId="{ row }"> - {{ deliveryTemplateList.find((item) => item.id === row.deliveryTemplateId)?.name }} - </template> - <template #specType="{ row }"> - {{ row.specType ? '多规格' : '单规格' }} - </template> - <template #subCommissionType="{ row }"> - {{ row.subCommissionType ? '单独设置' : '默认设置' }} - </template> - <template #picUrl="{ row }"> - <el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" /> - </template> - <template #sliderPicUrls="{ row }"> - <el-image - v-for="(item, index) in row.sliderPicUrls" - :key="index" - :src="item.url" - class="mr-10px h-60px w-60px" - @click="imagePreview(row.sliderPicUrls)" - /> - </template> - <template #skus> - <SkuList - ref="skuDetailListRef" - :is-detail="isDetail" - :prop-form-data="formData" - :propertyList="propertyList" - /> - </template> - </Descriptions> - - <!-- 商品属性添加 Form 表单 --> - <ProductPropertyAddForm ref="attributesAddFormRef" :propertyList="propertyList" /> -</template> -<script lang="ts" setup> -import { PropType } from 'vue' -import { isArray } from '@/utils/is' -import { copyValueToTarget } from '@/utils' -import { propTypes } from '@/utils/propTypes' -import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree' -import { createImageViewer } from '@/components/ImageViewer' -import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' -import { getPropertyList, RuleConfig, SkuList } from '@/views/mall/product/spu/components/index.ts' -import ProductAttributes from './ProductAttributes.vue' -import ProductPropertyAddForm from './ProductPropertyAddForm.vue' -import { basicInfoSchema } from './spu.data' -import type { Spu } from '@/api/mall/product/spu' -import * as ProductCategoryApi from '@/api/mall/product/category' -import * as ProductBrandApi from '@/api/mall/product/brand' -import * as ExpressTemplateApi from '@/api/mall/trade/delivery/expressTemplate' - -defineOptions({ name: 'ProductSpuBasicInfoForm' }) - -// sku 相关属性校验规则 -const ruleConfig: RuleConfig[] = [ - { - name: 'stock', - rule: (arg) => arg >= 0, - message: '商品库存必须大于等于 1 !!!' - }, - { - name: 'price', - rule: (arg) => arg >= 0.01, - message: '商品销售价格必须大于等于 0.01 元!!!' - }, - { - name: 'marketPrice', - rule: (arg) => arg >= 0.01, - message: '商品市场价格必须大于等于 0.01 元!!!' - }, - { - name: 'costPrice', - rule: (arg) => arg >= 0.01, - message: '商品成本价格必须大于等于 0.00 元!!!' - } -] - -// ====== 商品详情相关操作 ====== -const { allSchemas } = useCrudSchemas(basicInfoSchema) -/** 商品图预览 */ -const imagePreview = (args) => { - const urlList = [] - if (isArray(args)) { - args.forEach((item) => { - urlList.push(item.url) - }) - } else { - urlList.push(args) - } - createImageViewer({ - urlList - }) -} - -// ====== end ====== - -const message = useMessage() // 消息弹窗 - -const props = defineProps({ - propFormData: { - type: Object as PropType<Spu>, - default: () => {} - }, - activeName: propTypes.string.def(''), - isDetail: propTypes.bool.def(false) // 是否作为详情组件 -}) -const attributesAddFormRef = ref() // 添加商品属性表单 -const productSpuBasicInfoRef = ref() // 表单 Ref -const propertyList = ref([]) // 商品属性列表 -const skuListRef = ref() // 商品属性列表Ref -/** 调用 SkuList generateTableData 方法*/ -const generateSkus = (propertyList) => { - skuListRef.value.generateTableData(propertyList) -} -const formData = reactive<Spu>({ - name: '', // 商品名称 - categoryId: null, // 商品分类 - keyword: '', // 关键字 - unit: null, // 单位 - picUrl: '', // 商品封面图 - sliderPicUrls: [], // 商品轮播图 - introduction: '', // 商品简介 - deliveryTemplateId: null, // 运费模版 - brandId: null, // 商品品牌 - specType: false, // 商品规格 - subCommissionType: false, // 分销类型 - skus: [] -}) -const rules = reactive({ - name: [required], - categoryId: [required], - keyword: [required], - unit: [required], - introduction: [required], - picUrl: [required], - sliderPicUrls: [required], - deliveryTemplateId: [required], - brandId: [required], - specType: [required], - subCommissionType: [required] -}) - -/** - * 将传进来的值赋值给 formData - */ -watch( - () => props.propFormData, - (data) => { - if (!data) { - return - } - copyValueToTarget(formData, data) - formData.sliderPicUrls = data['sliderPicUrls']?.map((item) => ({ - url: item - })) - propertyList.value = getPropertyList(data) - }, - { - immediate: true - } -) - -/** - * 表单校验 - */ -const emit = defineEmits(['update:activeName']) -const validate = async () => { - // 校验 sku - skuListRef.value.validateSku() - // 校验表单 - if (!productSpuBasicInfoRef) return - return await unref(productSpuBasicInfoRef).validate((valid) => { - if (!valid) { - message.warning('商品信息未完善!!') - emit('update:activeName', 'basicInfo') - // 目的截断之后的校验 - throw new Error('商品信息未完善!!') - } else { - // 校验通过更新数据 - Object.assign(props.propFormData, formData) - } - }) -} -defineExpose({ validate }) - -/** 分销类型 */ -const changeSubCommissionType = () => { - // 默认为零,类型切换后也要重置为零 - for (const item of formData.skus) { - item.firstBrokeragePrice = 0 - item.secondBrokeragePrice = 0 - } -} - -/** 选择规格 */ -const onChangeSpec = () => { - // 重置商品属性列表 - propertyList.value = [] - // 重置sku列表 - formData.skus = [ - { - price: 0, - marketPrice: 0, - costPrice: 0, - barCode: '', - picUrl: '', - stock: 0, - weight: 0, - volume: 0, - firstBrokeragePrice: 0, - secondBrokeragePrice: 0 - } - ] -} - -const categoryList = ref([]) // 分类树 -/** 获取分类的节点的完整结构 */ -const formatCategoryName = (categoryId) => { - return treeToString(categoryList.value, categoryId) -} - -const brandList = ref([]) // 精简商品品牌列表 -const deliveryTemplateList = ref([]) // 运费模版 -onMounted(async () => { - // 获得分类树 - const data = await ProductCategoryApi.getCategoryList({}) - categoryList.value = handleTree(data, 'id', 'parentId') - // 获取商品品牌列表 - brandList.value = await ProductBrandApi.getSimpleBrandList() - // 获取运费模版 - deliveryTemplateList.value = await ExpressTemplateApi.getSimpleTemplateList() -}) -</script> diff --git a/src/views/mall/product/spu/form/DeliveryForm.vue b/src/views/mall/product/spu/form/DeliveryForm.vue new file mode 100644 index 00000000..1503122d --- /dev/null +++ b/src/views/mall/product/spu/form/DeliveryForm.vue @@ -0,0 +1,96 @@ +<!-- 商品发布 - 物流设置 --> +<template> + <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" :disabled="isDetail"> + <el-form-item label="配送方式" prop="deliveryTypes"> + <el-checkbox-group v-model="formData.deliveryTypes" class="w-80"> + <el-checkbox + v-for="dict in getIntDictOptions(DICT_TYPE.TRADE_DELIVERY_TYPE)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-checkbox> + </el-checkbox-group> + </el-form-item> + <el-form-item + label="运费模板" + prop="deliveryTemplateId" + v-if="formData.deliveryTypes?.includes(DeliveryTypeEnum.EXPRESS.type)" + > + <el-select placeholder="请选择运费模板" v-model="formData.deliveryTemplateId" class="w-80"> + <el-option + v-for="item in deliveryTemplateList" + :key="item.id" + :label="item.name" + :value="item.id" + /> + </el-select> + </el-form-item> + </el-form> +</template> +<script lang="ts" setup> +import { PropType } from 'vue' +import { copyValueToTarget } from '@/utils' +import { propTypes } from '@/utils/propTypes' +import type { Spu } from '@/api/mall/product/spu' +import * as ExpressTemplateApi from '@/api/mall/trade/delivery/expressTemplate' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { DeliveryTypeEnum } from '@/utils/constants' + +defineOptions({ name: 'ProductDeliveryForm' }) + +const message = useMessage() // 消息弹窗 + +const props = defineProps({ + propFormData: { + type: Object as PropType<Spu>, + default: () => {} + }, + isDetail: propTypes.bool.def(false) // 是否作为详情组件 +}) +const formRef = ref() // 表单 Ref +const formData = reactive<Spu>({ + deliveryTypes: [], // 配送方式 + deliveryTemplateId: undefined // 运费模版 +}) +const rules = reactive({ + deliveryTypes: [required], + deliveryTemplateId: [required] +}) + +/** 将传进来的值赋值给 formData */ +watch( + () => props.propFormData, + (data) => { + if (!data) { + return + } + copyValueToTarget(formData, data) + }, + { + immediate: true + } +) + +/** 表单校验 */ +const emit = defineEmits(['update:activeName']) +const validate = async () => { + if (!formRef) return + try { + await unref(formRef)?.validate() + // 校验通过更新数据 + Object.assign(props.propFormData, formData) + } catch (e) { + message.error('【物流设置】不完善,请填写相关信息') + emit('update:activeName', 'delivery') + throw e // 目的截断之后的校验 + } +} +defineExpose({ validate }) + +/** 初始化 */ +const deliveryTemplateList = ref([]) // 运费模版 +onMounted(async () => { + deliveryTemplateList.value = await ExpressTemplateApi.getSimpleTemplateList() +}) +</script> diff --git a/src/views/mall/product/spu/form/DescriptionForm.vue b/src/views/mall/product/spu/form/DescriptionForm.vue index d23106d0..2980aa46 100644 --- a/src/views/mall/product/spu/form/DescriptionForm.vue +++ b/src/views/mall/product/spu/form/DescriptionForm.vue @@ -1,30 +1,11 @@ +<!-- 商品发布 - 商品详情 --> <template> - <!-- 情况一:添加/修改 --> - <el-form - v-if="!isDetail" - ref="descriptionFormRef" - :model="formData" - :rules="rules" - label-width="120px" - > + <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" :disabled="isDetail"> <!--富文本编辑器组件--> <el-form-item label="商品详情" prop="description"> <Editor v-model:modelValue="formData.description" /> </el-form-item> </el-form> - - <!-- 情况二:详情 --> - <Descriptions - v-if="isDetail" - :data="formData" - :schema="allSchemas.detailSchema" - class="descriptionFormDescriptions" - > - <!-- 展示 HTML 内容 --> - <template #description="{ row }"> - <div v-dompurify-html="row.description" style="width: 600px"></div> - </template> - </Descriptions> </template> <script lang="ts" setup> import type { Spu } from '@/api/mall/product/spu' @@ -32,13 +13,11 @@ import { Editor } from '@/components/Editor' import { PropType } from 'vue' import { propTypes } from '@/utils/propTypes' import { copyValueToTarget } from '@/utils' -import { descriptionSchema } from './spu.data' -defineOptions({ name: 'DescriptionForm' }) +defineOptions({ name: 'ProductDescriptionForm' }) const message = useMessage() // 消息弹窗 -const { allSchemas } = useCrudSchemas(descriptionSchema) const props = defineProps({ propFormData: { type: Object as PropType<Spu>, @@ -47,7 +26,7 @@ const props = defineProps({ activeName: propTypes.string.def(''), isDetail: propTypes.bool.def(false) // 是否作为详情组件 }) -const descriptionFormRef = ref() // 表单Ref +const formRef = ref() // 表单Ref const formData = ref<Spu>({ description: '' // 商品详情 }) @@ -55,9 +34,8 @@ const formData = ref<Spu>({ const rules = reactive({ description: [required] }) -/** - * 富文本编辑器如果输入过再清空会有残留,需再重置一次 - */ + +/** 富文本编辑器如果输入过再清空会有残留,需再重置一次 */ watch( () => formData.value.description, (newValue) => { @@ -70,9 +48,8 @@ watch( immediate: true } ) -/** - * 将传进来的值赋值给formData - */ + +/** 将传进来的值赋值给 formData */ watch( () => props.propFormData, (data) => { @@ -86,24 +63,19 @@ watch( } ) -/** - * 表单校验 - */ +/** 表单校验 */ const emit = defineEmits(['update:activeName']) const validate = async () => { - // 校验表单 - if (!descriptionFormRef) return - return await unref(descriptionFormRef).validate((valid) => { - if (!valid) { - message.warning('商品详情为完善!!') - emit('update:activeName', 'description') - // 目的截断之后的校验 - throw new Error('商品详情为完善!!') - } else { - // 校验通过更新数据 - Object.assign(props.propFormData, formData.value) - } - }) + if (!formRef) return + try { + await unref(formRef)?.validate() + // 校验通过更新数据 + Object.assign(props.propFormData, formData.value) + } catch (e) { + message.error('【商品详情】不完善,请填写相关信息') + emit('update:activeName', 'description') + throw e // 目的截断之后的校验 + } } defineExpose({ validate }) </script> diff --git a/src/views/mall/product/spu/form/InfoForm.vue b/src/views/mall/product/spu/form/InfoForm.vue new file mode 100644 index 00000000..151e04d4 --- /dev/null +++ b/src/views/mall/product/spu/form/InfoForm.vue @@ -0,0 +1,146 @@ +<!-- 商品发布 - 基础设置 --> +<template> + <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" :disabled="isDetail"> + <el-form-item label="商品名称" prop="name"> + <el-input + v-model="formData.name" + placeholder="请输入商品名称" + type="textarea" + :autosize="{ minRows: 2, maxRows: 2 }" + maxlength="64" + :show-word-limit="true" + :clearable="true" + class="w-80!" + /> + </el-form-item> + <el-form-item label="商品分类" prop="categoryId"> + <el-cascader + v-model="formData.categoryId" + :options="categoryList" + :props="defaultProps" + class="w-80" + clearable + placeholder="请选择商品分类" + filterable + /> + </el-form-item> + <el-form-item label="商品品牌" prop="brandId"> + <el-select v-model="formData.brandId" placeholder="请选择商品品牌" class="w-80"> + <el-option + v-for="item in brandList" + :key="item.id" + :label="item.name" + :value="item.id as number" + /> + </el-select> + </el-form-item> + <el-form-item label="商品关键字" prop="keyword"> + <el-input v-model="formData.keyword" placeholder="请输入商品关键字" class="w-80!" /> + </el-form-item> + <el-form-item label="商品简介" prop="introduction"> + <el-input + v-model="formData.introduction" + placeholder="请输入商品名称" + type="textarea" + :autosize="{ minRows: 2, maxRows: 2 }" + maxlength="128" + :show-word-limit="true" + :clearable="true" + class="w-80!" + /> + </el-form-item> + <el-form-item label="商品封面图" prop="picUrl"> + <UploadImg v-model="formData.picUrl" height="80px" :disabled="isDetail" /> + </el-form-item> + <el-form-item label="商品轮播图" prop="sliderPicUrls"> + <UploadImgs v-model:modelValue="formData.sliderPicUrls" :disabled="isDetail" /> + </el-form-item> + </el-form> +</template> +<script lang="ts" setup> +import { PropType } from 'vue' +import { copyValueToTarget } from '@/utils' +import { propTypes } from '@/utils/propTypes' +import { defaultProps, handleTree } from '@/utils/tree' +import type { Spu } from '@/api/mall/product/spu' +import * as ProductCategoryApi from '@/api/mall/product/category' +import * as ProductBrandApi from '@/api/mall/product/brand' +import { BrandVO } from '@/api/mall/product/brand' +import { CategoryVO } from '@/api/mall/product/category' + +defineOptions({ name: 'ProductSpuInfoForm' }) +const props = defineProps({ + propFormData: { + type: Object as PropType<Spu>, + default: () => {} + }, + isDetail: propTypes.bool.def(false) // 是否作为详情组件 +}) + +const message = useMessage() // 消息弹窗 + +const formRef = ref() // 表单 Ref +const formData = reactive<Spu>({ + name: '', // 商品名称 + categoryId: undefined, // 商品分类 + keyword: '', // 关键字 + picUrl: '', // 商品封面图 + sliderPicUrls: [], // 商品轮播图 + introduction: '', // 商品简介 + brandId: undefined // 商品品牌 +}) +const rules = reactive({ + name: [required], + categoryId: [required], + keyword: [required], + introduction: [required], + picUrl: [required], + sliderPicUrls: [required], + brandId: [required] +}) + +/** 将传进来的值赋值给 formData */ +watch( + () => props.propFormData, + (data) => { + if (!data) { + return + } + copyValueToTarget(formData, data) + // TODO @puhui999:优化多文件上传,看看有没可能搞成返回 v-model 图片列表这种 + formData.sliderPicUrls = data['sliderPicUrls']?.map((item) => ({ + url: item + })) + }, + { + immediate: true + } +) + +/** 表单校验 */ +const emit = defineEmits(['update:activeName']) +const validate = async () => { + if (!formRef) return + try { + await unref(formRef)?.validate() + // 校验通过更新数据 + Object.assign(props.propFormData, formData) + } catch (e) { + message.error('【基础设置】不完善,请填写相关信息') + emit('update:activeName', 'info') + throw e // 目的截断之后的校验 + } +} +defineExpose({ validate }) + +/** 初始化 */ +const brandList = ref<BrandVO[]>([]) // 商品品牌列表 +const categoryList = ref<CategoryVO[]>([]) // 商品分类树 +onMounted(async () => { + // 获得分类树 + const data = await ProductCategoryApi.getCategoryList({}) + categoryList.value = handleTree(data, 'id') + // 获取商品品牌列表 + brandList.value = await ProductBrandApi.getSimpleBrandList() +}) +</script> diff --git a/src/views/mall/product/spu/form/OtherForm.vue b/src/views/mall/product/spu/form/OtherForm.vue new file mode 100644 index 00000000..e7e63580 --- /dev/null +++ b/src/views/mall/product/spu/form/OtherForm.vue @@ -0,0 +1,91 @@ +<!-- 商品发布 - 其它设置 --> +<template> + <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" :disabled="isDetail"> + <el-form-item label="商品排序" prop="sort"> + <el-input-number + v-model="formData.sort" + :min="0" + placeholder="请输入商品排序" + class="w-80!" + /> + </el-form-item> + <el-form-item label="赠送积分" prop="giveIntegral"> + <el-input-number + v-model="formData.giveIntegral" + :min="0" + placeholder="请输入赠送积分" + class="w-80!" + /> + </el-form-item> + <el-form-item label="虚拟销量" prop="virtualSalesCount"> + <el-input-number + v-model="formData.virtualSalesCount" + :min="0" + placeholder="请输入虚拟销量" + class="w-80!" + /> + </el-form-item> + </el-form> +</template> +<script lang="ts" setup> +import type { Spu } from '@/api/mall/product/spu' +import { PropType } from 'vue' +import { propTypes } from '@/utils/propTypes' +import { copyValueToTarget } from '@/utils' + +defineOptions({ name: 'ProductOtherForm' }) + +const message = useMessage() // 消息弹窗 + +const props = defineProps({ + propFormData: { + type: Object as PropType<Spu>, + default: () => {} + }, + isDetail: propTypes.bool.def(false) // 是否作为详情组件 +}) + +const formRef = ref() // 表单Ref +// 表单数据 +const formData = ref<Spu>({ + sort: 0, // 商品排序 + giveIntegral: 0, // 赠送积分 + virtualSalesCount: 0 // 虚拟销量 +}) +// 表单规则 +const rules = reactive({ + sort: [required], + giveIntegral: [required], + virtualSalesCount: [required] +}) + +/** 将传进来的值赋值给 formData */ +watch( + () => props.propFormData, + (data) => { + if (!data) { + return + } + copyValueToTarget(formData.value, data) + }, + { + immediate: true + } +) + +/** 表单校验 */ +const emit = defineEmits(['update:activeName']) +const validate = async () => { + if (!formRef) return + try { + await unref(formRef)?.validate() + // 校验通过更新数据 + Object.assign(props.propFormData, formData.value) + } catch (e) { + message.error('【其它设置】不完善,请填写相关信息') + emit('update:activeName', 'other') + throw e // 目的截断之后的校验 + } +} +defineExpose({ validate }) +</script> diff --git a/src/views/mall/product/spu/form/OtherSettingsForm.vue b/src/views/mall/product/spu/form/OtherSettingsForm.vue deleted file mode 100644 index d3bdb6a1..00000000 --- a/src/views/mall/product/spu/form/OtherSettingsForm.vue +++ /dev/null @@ -1,209 +0,0 @@ -<template> - <!-- 情况一:添加/修改 --> - <el-form - v-if="!isDetail" - ref="otherSettingsFormRef" - :model="formData" - :rules="rules" - label-width="120px" - > - <el-row> - <el-col :span="24"> - <el-row :gutter="20"> - <el-col :span="8"> - <el-form-item label="商品排序" prop="sort"> - <el-input-number v-model="formData.sort" :min="0" /> - </el-form-item> - </el-col> - <el-col :span="8"> - <el-form-item label="赠送积分" prop="giveIntegral"> - <el-input-number v-model="formData.giveIntegral" :min="0" /> - </el-form-item> - </el-col> - <el-col :span="8"> - <el-form-item label="虚拟销量" prop="virtualSalesCount"> - <el-input-number - v-model="formData.virtualSalesCount" - :min="0" - placeholder="请输入虚拟销量" - /> - </el-form-item> - </el-col> - </el-row> - </el-col> - <el-col :span="24"> - <el-form-item label="商品推荐"> - <el-checkbox-group v-model="checkboxGroup" @change="onChangeGroup"> - <el-checkbox v-for="(item, index) in recommendOptions" :key="index" :label="item.value"> - {{ item.name }} - </el-checkbox> - </el-checkbox-group> - </el-form-item> - </el-col> - <el-col :span="24"> - <el-form-item label="活动优先级"> - <ActivityOrdersSort - v-model:activity-orders="formData.activityOrders" - :promotion-types="promotionTypes" - /> - </el-form-item> - </el-col> - </el-row> - </el-form> - - <!-- 情况二:详情 --> - <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema"> - <template #recommendHot="{ row }"> - <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.recommendHot" /> - </template> - <template #recommendBenefit="{ row }"> - <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.recommendBenefit" /> - </template> - <template #recommendBest="{ row }"> - <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.recommendBest" /> - </template> - <template #recommendNew="{ row }"> - <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.recommendNew" /> - </template> - <template #recommendGood="{ row }"> - <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.recommendGood" /> - </template> - <template #activityOrders="{ row }"> - <el-tag - v-for="activityType in row.activityOrders" - :key="activityType" - :type="promotionTypes.find((item) => item.value === activityType)?.colorType" - class="mr-[10px]" - > - {{ promotionTypes.find((item) => item.value === activityType)?.label }} - </el-tag> - </template> - </Descriptions> -</template> -<script lang="ts" setup> -import type { Spu } from '@/api/mall/product/spu' -import { PropType } from 'vue' -import { propTypes } from '@/utils/propTypes' -import { copyValueToTarget } from '@/utils' -import { otherSettingsSchema } from './spu.data' -import { DICT_TYPE, DictDataType } from '@/utils/dict' -import ActivityOrdersSort from './ActivityOrdersSort.vue' - -defineOptions({ name: 'OtherSettingsForm' }) - -const message = useMessage() // 消息弹窗 - -const { allSchemas } = useCrudSchemas(otherSettingsSchema) - -const props = defineProps({ - propFormData: { - type: Object as PropType<Spu>, - default: () => {} - }, - activeName: propTypes.string.def(''), - isDetail: propTypes.bool.def(false) // 是否作为详情组件 -}) - -// TODO @puhui999:这个目前先写死;主要是,这个优惠类型不好用 promotion_type_enum;因为优惠劵、会员折扣都算 -// 活动优先级处理 -const promotionTypes = ref<DictDataType[]>([ - { - dictType: 'promotionTypes', - label: '秒杀', - value: 1, - colorType: 'warning', - cssClass: '' - }, - { - dictType: 'promotionTypes', - label: '砍价', - value: 2, - colorType: 'warning', - cssClass: '' - }, - { - dictType: 'promotionTypes', - label: '拼团', - value: 3, - colorType: 'warning', - cssClass: '' - } -]) - -const otherSettingsFormRef = ref() // 表单Ref -// 表单数据 -const formData = ref<Spu>({ - sort: 1, // 商品排序 - giveIntegral: 1, // 赠送积分 - virtualSalesCount: 1, // 虚拟销量 - recommendHot: false, // 是否热卖 - recommendBenefit: false, // 是否优惠 - recommendBest: false, // 是否精品 - recommendNew: false, // 是否新品 - recommendGood: false, // 是否优品 - activityOrders: [] // 活动排序 -}) -// 表单规则 -const rules = reactive({ - sort: [required], - giveIntegral: [required], - virtualSalesCount: [required] -}) -const recommendOptions = [ - { name: '是否热卖', value: 'recommendHot' }, - { name: '是否优惠', value: 'recommendBenefit' }, - { name: '是否精品', value: 'recommendBest' }, - { name: '是否新品', value: 'recommendNew' }, - { name: '是否优品', value: 'recommendGood' } -] // 商品推荐选项 -const checkboxGroup = ref<string[]>([]) // 选中的推荐选项 - -/** 选择商品后赋值 */ -const onChangeGroup = () => { - recommendOptions.forEach(({ value }) => { - formData.value[value] = checkboxGroup.value.includes(value) - }) -} - -/** - * 将传进来的值赋值给formData - */ -watch( - () => props.propFormData, - (data) => { - if (!data) { - return - } - copyValueToTarget(formData.value, data) - recommendOptions.forEach(({ value }) => { - if (formData.value[value] && !checkboxGroup.value.includes(value)) { - checkboxGroup.value.push(value) - } - }) - }, - { - immediate: true - } -) - -/** - * 表单校验 - */ -const emit = defineEmits(['update:activeName']) -const validate = async () => { - // 校验表单 - if (!otherSettingsFormRef) return - return await unref(otherSettingsFormRef).validate((valid) => { - if (!valid) { - message.warning('商品其他设置未完善!!') - emit('update:activeName', 'otherSettings') - // 目的截断之后的校验 - throw new Error('商品其他设置未完善!!') - } else { - // 校验通过更新数据 - Object.assign(props.propFormData, formData.value) - } - }) -} -defineExpose({ validate }) -</script> diff --git a/src/views/mall/product/spu/form/ProductAttributes.vue b/src/views/mall/product/spu/form/ProductAttributes.vue index 7239ab85..28962f47 100644 --- a/src/views/mall/product/spu/form/ProductAttributes.vue +++ b/src/views/mall/product/spu/form/ProductAttributes.vue @@ -1,9 +1,10 @@ +<!-- 商品发布 - 库存价格 - 属性列表 --> <template> <el-col v-for="(item, index) in attributeList" :key="index"> <div> <el-text class="mx-1">属性名:</el-text> - <el-tag class="mx-1" closable type="success" @close="handleCloseProperty(index)" - >{{ item.name }} + <el-tag class="mx-1" :closable="!isDetail" type="success" @close="handleCloseProperty(index)"> + {{ item.name }} </el-tag> </div> <div> @@ -12,7 +13,7 @@ v-for="(value, valueIndex) in item.values" :key="value.id" class="mx-1" - closable + :closable="!isDetail" @close="handleCloseValue(index, valueIndex)" > {{ value.name }} @@ -43,6 +44,9 @@ <script lang="ts" setup> import { ElInput } from 'element-plus' import * as PropertyApi from '@/api/mall/product/property' +import { PropertyVO } from '@/api/mall/product/property' +import { PropertyAndValues } from '@/views/mall/product/spu/components' +import { propTypes } from '@/utils/propTypes' defineOptions({ name: 'ProductAttributes' }) @@ -51,7 +55,7 @@ const message = useMessage() // 消息弹窗 const inputValue = ref('') // 输入框值 const attributeIndex = ref<number | null>(null) // 获取焦点时记录当前属性项的index // 输入框显隐控制 -const inputVisible = computed(() => (index) => { +const inputVisible = computed(() => (index: number) => { if (attributeIndex.value === null) return false if (attributeIndex.value === index) return true }) @@ -59,17 +63,18 @@ const inputRef = ref([]) //标签输入框Ref /** 解决 ref 在 v-for 中的获取问题*/ const setInputRef = (el) => { if (el === null || typeof el === 'undefined') return - // 如果不存在id相同的元素才添加 + // 如果不存在 id 相同的元素才添加 if (!inputRef.value.some((item) => item.input?.attributes.id === el.input?.attributes.id)) { inputRef.value.push(el) } } -const attributeList = ref([]) // 商品属性列表 +const attributeList = ref<PropertyAndValues[]>([]) // 商品属性列表 const props = defineProps({ propertyList: { type: Array, default: () => {} - } + }, + isDetail: propTypes.bool.def(false) // 是否作为详情组件 }) watch( @@ -85,23 +90,24 @@ watch( ) /** 删除属性值*/ -const handleCloseValue = (index, valueIndex) => { +const handleCloseValue = (index: number, valueIndex: number) => { attributeList.value[index].values?.splice(valueIndex, 1) } + /** 删除属性*/ -const handleCloseProperty = (index) => { +const handleCloseProperty = (index: number) => { attributeList.value?.splice(index, 1) } + /** 显示输入框并获取焦点 */ const showInput = async (index) => { attributeIndex.value = index inputRef.value[index].focus() } -const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 - /** 输入框失去焦点或点击回车时触发 */ -const handleInputConfirm = async (index, propertyId) => { +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const handleInputConfirm = async (index: number, propertyId: number) => { if (inputValue.value) { // 保存属性值 try { @@ -110,7 +116,7 @@ const handleInputConfirm = async (index, propertyId) => { message.success(t('common.createSuccess')) emit('success', attributeList.value) } catch { - message.error('添加失败,请重试') // TODO 缺少国际化 + message.error('添加失败,请重试') } } attributeIndex.value = null diff --git a/src/views/mall/product/spu/form/ProductPropertyAddForm.vue b/src/views/mall/product/spu/form/ProductPropertyAddForm.vue index a3e09fef..9a8eee00 100644 --- a/src/views/mall/product/spu/form/ProductPropertyAddForm.vue +++ b/src/views/mall/product/spu/form/ProductPropertyAddForm.vue @@ -1,5 +1,6 @@ +<!-- 商品发布 - 库存价格 - 添加属性 --> <template> - <Dialog v-model="dialogVisible" :title="dialogTitle"> + <Dialog v-model="dialogVisible" title="添加商品属性"> <el-form ref="formRef" v-loading="formLoading" @@ -26,8 +27,7 @@ const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 const dialogVisible = ref(false) // 弹窗的是否展示 -const dialogTitle = ref('添加商品属性') // 弹窗的标题 -const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formLoading = ref(false) // 表单的加载中 const formData = ref({ name: '' }) @@ -44,7 +44,7 @@ const props = defineProps({ }) watch( - () => props.propertyList, + () => props.propertyList, // 解决 props 无法直接修改父组件的问题 (data) => { if (!data) return attributeList.value = data @@ -54,6 +54,7 @@ watch( immediate: true } ) + /** 打开弹窗 */ const open = async () => { dialogVisible.value = true @@ -71,19 +72,13 @@ const submitForm = async () => { formLoading.value = true try { const data = formData.value as PropertyApi.PropertyVO - // 检查属性是否已存在,如果有则返回属性和其下属性值 - const res = await PropertyApi.getPropertyListAndValue({ name: data.name }) - if (res.length === 0) { - const propertyId = await PropertyApi.createProperty(data) - attributeList.value.push({ id: propertyId, ...formData.value, values: [] }) - } else { - if (res[0].values === null) { - res[0].values = [] - } - // 不需要属性值 - res[0].values = [] - attributeList.value.push(res[0]) // 因为只用一个 - } + const propertyId = await PropertyApi.createProperty(data) + // 添加到属性列表 + attributeList.value.push({ + id: propertyId, + ...formData.value, + values: [] + }) message.success(t('common.createSuccess')) dialogVisible.value = false } finally { diff --git a/src/views/mall/product/spu/form/SkuForm.vue b/src/views/mall/product/spu/form/SkuForm.vue new file mode 100644 index 00000000..0bd79723 --- /dev/null +++ b/src/views/mall/product/spu/form/SkuForm.vue @@ -0,0 +1,187 @@ +<!-- 商品发布 - 库存价格 --> +<template> + <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" :disabled="isDetail"> + <el-form-item label="分销类型" props="subCommissionType"> + <el-radio-group + v-model="formData.subCommissionType" + @change="changeSubCommissionType" + class="w-80" + > + <el-radio :label="false">默认设置</el-radio> + <el-radio :label="true" class="radio">单独设置</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="商品规格" props="specType"> + <el-radio-group v-model="formData.specType" @change="onChangeSpec" class="w-80"> + <el-radio :label="false" class="radio">单规格</el-radio> + <el-radio :label="true">多规格</el-radio> + </el-radio-group> + </el-form-item> + <!-- 多规格添加--> + <el-form-item v-if="!formData.specType"> + <SkuList + ref="skuListRef" + :prop-form-data="formData" + :property-list="propertyList" + :rule-config="ruleConfig" + /> + </el-form-item> + <el-form-item v-if="formData.specType" label="商品属性"> + <el-button class="mb-10px mr-15px" @click="attributesAddFormRef.open">添加属性</el-button> + <ProductAttributes + :property-list="propertyList" + @success="generateSkus" + :is-detail="isDetail" + /> + </el-form-item> + <template v-if="formData.specType && propertyList.length > 0"> + <el-form-item label="批量设置" v-if="!isDetail"> + <SkuList :is-batch="true" :prop-form-data="formData" :property-list="propertyList" /> + </el-form-item> + <el-form-item label="规格列表"> + <SkuList + ref="skuListRef" + :prop-form-data="formData" + :property-list="propertyList" + :rule-config="ruleConfig" + :is-detail="isDetail" + /> + </el-form-item> + </template> + </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' }) + +// sku 相关属性校验规则 +const ruleConfig: RuleConfig[] = [ + { + name: 'stock', + rule: (arg) => arg >= 0, + message: '商品库存必须大于等于 1 !!!' + }, + { + name: 'price', + rule: (arg) => arg >= 0.01, + message: '商品销售价格必须大于等于 0.01 元!!!' + }, + { + name: 'marketPrice', + rule: (arg) => arg >= 0.01, + message: '商品市场价格必须大于等于 0.01 元!!!' + }, + { + name: 'costPrice', + rule: (arg) => arg >= 0.01, + message: '商品成本价格必须大于等于 0.00 元!!!' + } +] + +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>({ + specType: false, // 商品规格 + subCommissionType: false, // 分销类型 + skus: [] +}) +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 { + // 校验 sku + skuListRef.value.validateSku() + await unref(formRef).validate() + // 校验通过更新数据 + Object.assign(props.propFormData, formData) + } catch (e) { + message.error('【库存价格】不完善,请填写相关信息') + emit('update:activeName', 'sku') + throw e // 目的截断之后的校验 + } +} +defineExpose({ validate }) + +/** 分销类型 */ +const changeSubCommissionType = () => { + // 默认为零,类型切换后也要重置为零 + for (const item of formData.skus!) { + item.firstBrokeragePrice = 0 + item.secondBrokeragePrice = 0 + } +} + +/** 选择规格 */ +const onChangeSpec = () => { + // 重置商品属性列表 + propertyList.value = [] + // 重置sku列表 + formData.skus = [ + { + price: 0, + marketPrice: 0, + costPrice: 0, + barCode: '', + picUrl: '', + stock: 0, + weight: 0, + volume: 0, + firstBrokeragePrice: 0, + secondBrokeragePrice: 0 + } + ] +} + +/** 调用 SkuList generateTableData 方法*/ +const generateSkus = (propertyList) => { + skuListRef.value.generateTableData(propertyList) +} +</script> diff --git a/src/views/mall/product/spu/form/index.vue b/src/views/mall/product/spu/form/index.vue index 9d69923a..de874524 100644 --- a/src/views/mall/product/spu/form/index.vue +++ b/src/views/mall/product/spu/form/index.vue @@ -1,9 +1,25 @@ <template> <ContentWrap v-loading="formLoading"> <el-tabs v-model="activeName"> - <el-tab-pane label="商品信息" name="basicInfo"> - <BasicInfoForm - ref="basicInfoRef" + <el-tab-pane label="基础设置" name="info"> + <InfoForm + ref="infoRef" + v-model:activeName="activeName" + :is-detail="isDetail" + :propFormData="formData" + /> + </el-tab-pane> + <el-tab-pane label="价格库存" name="sku"> + <SkuForm + ref="skuRef" + v-model:activeName="activeName" + :is-detail="isDetail" + :propFormData="formData" + /> + </el-tab-pane> + <el-tab-pane label="物流设置" name="delivery"> + <DeliveryForm + ref="deliveryRef" v-model:activeName="activeName" :is-detail="isDetail" :propFormData="formData" @@ -17,9 +33,9 @@ :propFormData="formData" /> </el-tab-pane> - <el-tab-pane label="其他设置" name="otherSettings"> - <OtherSettingsForm - ref="otherSettingsRef" + <el-tab-pane label="其它设置" name="other"> + <OtherForm + ref="otherRef" v-model:activeName="activeName" :is-detail="isDetail" :propFormData="formData" @@ -40,9 +56,11 @@ import { cloneDeep } from 'lodash-es' import { useTagsViewStore } from '@/store/modules/tagsView' import * as ProductSpuApi from '@/api/mall/product/spu' -import BasicInfoForm from './BasicInfoForm.vue' +import InfoForm from './InfoForm.vue' import DescriptionForm from './DescriptionForm.vue' -import OtherSettingsForm from './OtherSettingsForm.vue' +import OtherForm from './OtherForm.vue' +import SkuForm from './SkuForm.vue' +import DeliveryForm from './DeliveryForm.vue' import { convertToInteger, floatToFixed2, formatToFraction } from '@/utils' defineOptions({ name: 'ProductSpuForm' }) @@ -54,20 +72,22 @@ const { params, name } = useRoute() // 查询参数 const { delView } = useTagsViewStore() // 视图操作 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 -const activeName = ref('basicInfo') // Tag 激活的窗口 +const activeName = ref('info') // Tag 激活的窗口 const isDetail = ref(false) // 是否查看详情 -const basicInfoRef = ref() // 商品信息Ref -const descriptionRef = ref() // 商品详情Ref -const otherSettingsRef = ref() // 其他设置Ref -// spu 表单数据 +const infoRef = ref() // 商品信息 Ref +const skuRef = ref() // 商品规格 Ref +const deliveryRef = ref() // 物流设置 Ref +const descriptionRef = ref() // 商品详情 Ref +const otherRef = ref() // 其他设置 Ref +// SPU 表单数据 const formData = ref<ProductSpuApi.Spu>({ name: '', // 商品名称 categoryId: undefined, // 商品分类 keyword: '', // 关键字 - unit: undefined, // 单位 picUrl: '', // 商品封面图 sliderPicUrls: [], // 商品轮播图 introduction: '', // 商品简介 + deliveryTypes: [], // 配送方式数组 deliveryTemplateId: undefined, // 运费模版 brandId: undefined, // 商品品牌 specType: false, // 商品规格 @@ -89,13 +109,7 @@ const formData = ref<ProductSpuApi.Spu>({ description: '', // 商品详情 sort: 0, // 商品排序 giveIntegral: 0, // 赠送积分 - virtualSalesCount: 0, // 虚拟销量 - recommendHot: false, // 是否热卖 - recommendBenefit: false, // 是否优惠 - recommendBest: false, // 是否精品 - recommendNew: false, // 是否新品 - recommendGood: false, // 是否优品 - activityOrders: [] // 活动排序 + virtualSalesCount: 0 // 虚拟销量 }) /** 获得详情 */ @@ -135,13 +149,14 @@ const getDetail = async () => { const submitForm = async () => { // 提交请求 formLoading.value = true - // 三个表单逐一校验,如果有一个表单校验不通过则切换到对应表单,如果有两个及以上的情况则切换到最前面的一个并弹出提示消息 - // 校验各表单 try { - await unref(basicInfoRef)?.validate() + // 校验各表单 + await unref(infoRef)?.validate() + await unref(skuRef)?.validate() + await unref(deliveryRef)?.validate() await unref(descriptionRef)?.validate() - await unref(otherSettingsRef)?.validate() - // 深拷贝一份, 这样最终 server 端不满足,不需要恢复, + await unref(otherRef)?.validate() + // 深拷贝一份, 这样最终 server 端不满足,不需要影响原始数据 const deepCopyFormData = cloneDeep(unref(formData.value)) as ProductSpuApi.Spu deepCopyFormData.skus!.forEach((item) => { // 给sku name赋值 @@ -181,6 +196,7 @@ const close = () => { delView(unref(currentRoute)) push({ name: 'ProductSpu' }) } + /** 初始化 */ onMounted(async () => { await getDetail() diff --git a/src/views/mall/product/spu/form/spu.data.ts b/src/views/mall/product/spu/form/spu.data.ts deleted file mode 100644 index 9d88f433..00000000 --- a/src/views/mall/product/spu/form/spu.data.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { CrudSchema } from '@/hooks/web/useCrudSchemas' - -export const basicInfoSchema = reactive<CrudSchema[]>([ - { - label: '商品名称', - field: 'name' - }, - { - label: '关键字', - field: 'keyword' - }, - { - label: '商品简介', - field: 'introduction' - }, - { - label: '商品分类', - field: 'categoryId' - }, - { - label: '商品品牌', - field: 'brandId' - }, - { - label: '商品封面图', - field: 'picUrl' - }, - { - label: '商品轮播图', - field: 'sliderPicUrls' - }, - { - label: '商品视频', - field: 'videoUrl' - }, - { - label: '单位', - field: 'unit', - dictType: DICT_TYPE.PRODUCT_UNIT - }, - { - label: '规格类型', - field: 'specType' - }, - { - label: '分销类型', - field: 'subCommissionType' - }, - { - label: '物流模版', - field: 'deliveryTemplateId' - }, - { - label: '商品属性列表', - field: 'skus' - } -]) -export const descriptionSchema = reactive<CrudSchema[]>([ - { - label: '商品详情', - field: 'description' - } -]) -export const otherSettingsSchema = reactive<CrudSchema[]>([ - { - label: '商品排序', - field: 'sort' - }, - { - label: '赠送积分', - field: 'giveIntegral' - }, - { - label: '虚拟销量', - field: 'virtualSalesCount' - }, - { - label: '是否热卖推荐', - field: 'recommendHot' - }, - { - label: '是否优惠推荐', - field: 'recommendBenefit' - }, - { - label: '是否精品推荐', - field: 'recommendBest' - }, - { - label: '是否新品推荐', - field: 'recommendNew' - }, - { - label: '是否优品推荐', - field: 'recommendGood' - }, - { - label: '活动显示排序', - field: 'activityOrders' - } -]) diff --git a/src/views/mall/product/spu/index.vue b/src/views/mall/product/spu/index.vue index 27a4bd2d..6817323b 100644 --- a/src/views/mall/product/spu/index.vue +++ b/src/views/mall/product/spu/index.vue @@ -1,3 +1,4 @@ +<!-- 商品中心 - 商品列表 --> <template> <!-- 搜索工作栏 --> <ContentWrap> @@ -125,27 +126,33 @@ </el-form> </template> </el-table-column> - <el-table-column align="center" label="商品编号" min-width="60" prop="id" /> - <el-table-column label="商品图" min-width="80"> + <el-table-column label="商品编号" min-width="140" prop="id" /> + <el-table-column label="商品信息" min-width="300"> <template #default="{ row }"> - <el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" /> + <div class="flex"> + <el-image + fit="cover" + :src="row.picUrl" + class="flex-none w-50px h-50px" + @click="imagePreview(row.picUrl)" + /> + <div class="ml-4 overflow-hidden"> + <el-tooltip effect="dark" :content="row.name" placement="top"> + <div> + {{ row.name }} + </div> + </el-tooltip> + </div> + </div> </template> </el-table-column> - <el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" /> - <el-table-column align="center" label="商品售价" min-width="90" prop="price"> - <template #default="{ row }"> {{ fenToYuan(row.price) }}元</template> + <el-table-column align="center" label="价格" min-width="160" prop="price"> + <template #default="{ row }"> ¥ {{ fenToYuan(row.price) }}</template> </el-table-column> <el-table-column align="center" label="销量" min-width="90" prop="salesCount" /> <el-table-column align="center" label="库存" min-width="90" prop="stock" /> <el-table-column align="center" label="排序" min-width="70" prop="sort" /> - <el-table-column - :formatter="dateFormatter" - align="center" - label="创建时间" - prop="createTime" - width="180" - /> - <el-table-column align="center" label="状态" min-width="80"> + <el-table-column align="center" label="销售状态" min-width="80"> <template #default="{ row }"> <template v-if="row.status >= 0"> <el-switch @@ -163,16 +170,16 @@ </template> </template> </el-table-column> + <el-table-column + :formatter="dateFormatter" + align="center" + label="创建时间" + prop="createTime" + width="180" + /> <el-table-column align="center" fixed="right" label="操作" min-width="200"> <template #default="{ row }"> - <el-button - v-hasPermi="['product:spu:update']" - link - type="primary" - @click="openDetail(row.id)" - > - 详情 - </el-button> + <el-button link type="primary" @click="openDetail(row.id)"> 详情 </el-button> <el-button v-hasPermi="['product:spu:update']" link @@ -196,17 +203,17 @@ type="primary" @click="handleStatus02Change(row, ProductSpuStatusEnum.DISABLE.status)" > - 恢复到仓库 + 恢复 </el-button> </template> <template v-else> <el-button v-hasPermi="['product:spu:update']" link - type="primary" + type="danger" @click="handleStatus02Change(row, ProductSpuStatusEnum.RECYCLE.status)" > - 加入回收站 + 回收 </el-button> </template> </template> @@ -236,48 +243,41 @@ defineOptions({ name: 'ProductSpu' }) const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 -const { currentRoute, push } = useRouter() // 路由跳转 +const { push } = useRouter() // 路由跳转 const loading = ref(false) // 列表的加载中 const exportLoading = ref(false) // 导出的加载中 const total = ref(0) // 列表的总页数 -const list = ref<any[]>([]) // 列表的数据 +const list = ref<ProductSpuApi.Spu[]>([]) // 列表的数据 // tabs 数据 const tabsData = ref([ { - count: 0, - name: '出售中商品', - type: 0 + name: '出售中', + type: 0, + count: 0 }, { - count: 0, - name: '仓库中商品', - type: 1 + name: '仓库中', + type: 1, + count: 0 }, { - count: 0, - name: '已售罄商品', - type: 2 + name: '已售罄', + type: 2, + count: 0 }, { - count: 0, name: '警戒库存', - type: 3 + type: 3, + count: 0 }, { - count: 0, - name: '商品回收站', - type: 4 + name: '回收站', + type: 4, + count: 0 } ]) -/** 获得每个 Tab 的数量 */ -const getTabsCount = async () => { - const res = await ProductSpuApi.getTabsCount() - for (let objName in res) { - tabsData.value[Number(objName)].count = res[objName] - } -} const queryParams = ref({ pageNo: 1, pageSize: 10, @@ -288,11 +288,6 @@ const queryParams = ref({ }) // 查询参数 const queryFormRef = ref() // 搜索的表单Ref -const handleTabClick = (tab: TabsPaneContext) => { - queryParams.value.tabType = tab.paneName as number - getList() -} - /** 查询列表 */ const getList = async () => { loading.value = true @@ -305,8 +300,22 @@ const getList = async () => { } } +/** 切换 Tab */ +const handleTabClick = (tab: TabsPaneContext) => { + queryParams.value.tabType = tab.paneName as number + getList() +} + +/** 获得每个 Tab 的数量 */ +const getTabsCount = async () => { + const res = await ProductSpuApi.getTabsCount() + for (let objName in res) { + tabsData.value[Number(objName)].count = res[objName] + } +} + /** 添加到仓库 / 回收站的状态 */ -const handleStatus02Change = async (row, newStatus: number) => { +const handleStatus02Change = async (row: any, newStatus: number) => { try { // 二次确认 const text = newStatus === ProductSpuStatusEnum.RECYCLE.status ? '加入到回收站' : '恢复到仓库' @@ -322,7 +331,7 @@ const handleStatus02Change = async (row, newStatus: number) => { } /** 更新上架/下架状态 */ -const handleStatusChange = async (row) => { +const handleStatusChange = async (row: any) => { try { // 二次确认 const text = row.status ? '上架' : '下架' @@ -407,19 +416,16 @@ const handleExport = async () => { } } -const categoryList = ref() // 分类树 /** 获取分类的节点的完整结构 */ -const formatCategoryName = (categoryId) => { +const categoryList = ref() // 分类树 +const formatCategoryName = (categoryId: number) => { return treeToString(categoryList.value, categoryId) } -// 监听路由变化更新列表,解决商品保存后,列表不刷新的问题。 -watch( - () => currentRoute.value, - () => { - getList() - } -) +/** 激活时 */ +onActivated(() => { + getList() +}) /** 初始化 **/ onMounted(async () => { diff --git a/src/views/mall/promotion/diy/page/DiyPageForm.vue b/src/views/mall/promotion/diy/page/DiyPageForm.vue index e0cb18b6..4e3c84fc 100644 --- a/src/views/mall/promotion/diy/page/DiyPageForm.vue +++ b/src/views/mall/promotion/diy/page/DiyPageForm.vue @@ -13,8 +13,8 @@ <el-form-item label="备注" prop="remark"> <el-input v-model="formData.remark" placeholder="请输入备注" /> </el-form-item> - <el-form-item label="预览图" prop="previewImageUrls"> - <UploadImgs v-model="formData.previewImageUrls" /> + <el-form-item label="预览图" prop="previewPicUrls"> + <UploadImgs v-model="formData.previewPicUrls" /> </el-form-item> </el-form> <template #footer> @@ -40,7 +40,7 @@ const formData = ref({ id: undefined, name: undefined, remark: undefined, - previewImageUrls: [] + previewPicUrls: [] }) const formRules = reactive({ name: [{ required: true, message: '页面名称不能为空', trigger: 'blur' }] @@ -58,8 +58,8 @@ const open = async (type: string, id?: number) => { formLoading.value = true try { const diyPage = await DiyPageApi.getDiyPage(id) // 处理预览图 - if (diyPage?.previewImageUrls?.length > 0) { - diyPage.previewImageUrls = diyPage.previewImageUrls.map((url: string) => { + if (diyPage?.previewPicUrls?.length > 0) { + diyPage.previewPicUrls = diyPage.previewPicUrls.map((url: string) => { return { url } }) } @@ -82,10 +82,10 @@ const submitForm = async () => { formLoading.value = true try { // 处理预览图 - const previewImageUrls = formData.value.previewImageUrls.map((item) => { + const previewPicUrls = formData.value.previewPicUrls.map((item) => { return item['url'] ? item['url'] : item }) - const data = { ...formData.value, previewImageUrls } as unknown as DiyPageApi.DiyPageVO + const data = { ...formData.value, previewPicUrls } as unknown as DiyPageApi.DiyPageVO if (formType.value === 'create') { await DiyPageApi.createDiyPage(data) message.success(t('common.createSuccess')) @@ -107,7 +107,7 @@ const resetForm = () => { id: undefined, name: undefined, remark: undefined, - previewImageUrls: [] + previewPicUrls: [] } formRef.value?.resetFields() } diff --git a/src/views/mall/promotion/diy/page/decorate.vue b/src/views/mall/promotion/diy/page/decorate.vue index c77ccfbf..fa20c3eb 100644 --- a/src/views/mall/promotion/diy/page/decorate.vue +++ b/src/views/mall/promotion/diy/page/decorate.vue @@ -52,7 +52,7 @@ const resetForm = () => { templateId: undefined, name: '', remark: '', - previewImageUrls: [], + previewPicUrls: [], property: '' } as DiyPageApi.DiyPageVO formRef.value?.resetFields() diff --git a/src/views/mall/promotion/diy/page/index.vue b/src/views/mall/promotion/diy/page/index.vue index 6436c2f7..89ae0003 100644 --- a/src/views/mall/promotion/diy/page/index.vue +++ b/src/views/mall/promotion/diy/page/index.vue @@ -47,14 +47,14 @@ <ContentWrap> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> <el-table-column label="编号" align="center" prop="id" /> - <el-table-column label="预览图" align="center" prop="previewImageUrls"> + <el-table-column label="预览图" align="center" prop="previewPicUrls"> <template #default="scope"> <el-image class="h-40px max-w-40px" - v-for="(url, index) in scope.row.previewImageUrls" + v-for="(url, index) in scope.row.previewPicUrls" :key="index" :src="url" - :preview-src-list="scope.row.previewImageUrls" + :preview-src-list="scope.row.previewPicUrls" :initial-index="index" preview-teleported /> diff --git a/src/views/mall/promotion/diy/template/DiyTemplateForm.vue b/src/views/mall/promotion/diy/template/DiyTemplateForm.vue index e4289f65..16e1fc06 100644 --- a/src/views/mall/promotion/diy/template/DiyTemplateForm.vue +++ b/src/views/mall/promotion/diy/template/DiyTemplateForm.vue @@ -13,8 +13,8 @@ <el-form-item label="备注" prop="remark"> <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" /> </el-form-item> - <el-form-item label="预览图" prop="previewImageUrls"> - <UploadImgs v-model="formData.previewImageUrls" /> + <el-form-item label="预览图" prop="previewPicUrls"> + <UploadImgs v-model="formData.previewPicUrls" /> </el-form-item> </el-form> <template #footer> @@ -40,7 +40,7 @@ const formData = ref({ id: undefined, name: undefined, remark: undefined, - previewImageUrls: [] + previewPicUrls: [] }) const formRules = reactive({ name: [{ required: true, message: '模板名称不能为空', trigger: 'blur' }] @@ -59,8 +59,8 @@ const open = async (type: string, id?: number) => { try { const diyTemplate = await DiyTemplateApi.getDiyTemplate(id) // 处理预览图 - if (diyTemplate?.previewImageUrls?.length > 0) { - diyTemplate.previewImageUrls = diyTemplate.previewImageUrls.map((url: string) => { + if (diyTemplate?.previewPicUrls?.length > 0) { + diyTemplate.previewPicUrls = diyTemplate.previewPicUrls.map((url: string) => { return { url } }) } @@ -83,10 +83,10 @@ const submitForm = async () => { formLoading.value = true try { // 处理预览图 - const previewImageUrls = formData.value.previewImageUrls.map((item) => { + const previewPicUrls = formData.value.previewPicUrls.map((item) => { return item['url'] ? item['url'] : item }) - const data = { ...formData.value, previewImageUrls } as unknown as DiyTemplateApi.DiyTemplateVO + const data = { ...formData.value, previewPicUrls } as unknown as DiyTemplateApi.DiyTemplateVO if (formType.value === 'create') { await DiyTemplateApi.createDiyTemplate(data) message.success(t('common.createSuccess')) @@ -108,7 +108,7 @@ const resetForm = () => { id: undefined, name: undefined, remark: undefined, - previewImageUrls: [] + previewPicUrls: [] } formRef.value?.resetFields() } diff --git a/src/views/mall/promotion/diy/template/decorate.vue b/src/views/mall/promotion/diy/template/decorate.vue index 6f4899a2..e7838f29 100644 --- a/src/views/mall/promotion/diy/template/decorate.vue +++ b/src/views/mall/promotion/diy/template/decorate.vue @@ -118,7 +118,7 @@ const resetForm = () => { used: false, usedTime: undefined, remark: '', - previewImageUrls: [], + previewPicUrls: [], property: '', pages: [] } as DiyTemplateApi.DiyTemplatePropertyVO diff --git a/src/views/mall/promotion/diy/template/index.vue b/src/views/mall/promotion/diy/template/index.vue index 97f8bdef..6f703573 100644 --- a/src/views/mall/promotion/diy/template/index.vue +++ b/src/views/mall/promotion/diy/template/index.vue @@ -47,14 +47,14 @@ <ContentWrap> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> <el-table-column label="编号" align="center" prop="id" /> - <el-table-column label="预览图" align="center" prop="previewImageUrls"> + <el-table-column label="预览图" align="center" prop="previewPicUrls"> <template #default="scope"> <el-image class="h-40px max-w-40px" - v-for="(url, index) in scope.row.previewImageUrls" + v-for="(url, index) in scope.row.previewPicUrls" :key="index" :src="url" - :preview-src-list="scope.row.previewImageUrls" + :preview-src-list="scope.row.previewPicUrls" :initial-index="index" preview-teleported /> diff --git a/src/views/mall/promotion/rewardActivity/RewardForm.vue b/src/views/mall/promotion/rewardActivity/RewardForm.vue index 716f4e26..9fb69a56 100644 --- a/src/views/mall/promotion/rewardActivity/RewardForm.vue +++ b/src/views/mall/promotion/rewardActivity/RewardForm.vue @@ -24,51 +24,96 @@ <el-radio v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_CONDITION_TYPE)" :key="dict.value" - :label="parseInt(dict.value)" - >{{ dict.label }}</el-radio + :label="dict.value" > + {{ dict.label }} + </el-radio> </el-radio-group> </el-form-item> <el-form-item label="优惠设置"> - <template v-for="(item,index) in formData.rules" :key="index"> - <el-row type="flex"> - <el-col :span="24" style="font-weight: bold;display: flex;">活动层级{{ index+1 }}<el-button link type="danger" style="margin-left: auto;" v-if="index!=0" @click="deleteStratum(index)">删除</el-button></el-col> - <e-form :ref="'formRef'+index" :model="item" > - <el-form-item label="优惠门槛:" prop="limit" label-width="100px" style="padding-left: 50px;">满<el-input style="width: 150px;padding:0 10px;" v-model="item.limit" type='number' placeholder="" /> 元 - </el-form-item> - <el-form-item label="优惠内容:" label-width="100px" style="padding-left: 50px;"> - <el-checkbox-group v-model="rules[index]" style="width:100%"> - <el-col :span="24"> - <el-checkbox label="订单金额优惠" name="type" /> - <el-form-item v-if="rules[index].includes('订单金额优惠')"> - 减<el-input style="width: 150px;padding:0 20px;" v-model="item.discountPrice" type='number' placeholder="" />元 - </el-form-item> - </el-col> - <el-col :span="24"><el-checkbox v-model="item.freeDelivery" label="包邮" name="type" /></el-col> - <el-col :span="24"> - <el-checkbox label="送积分" name="type" /> - <el-form-item v-if="rules[index].includes('送积分')"> - 送<el-input style="width: 150px;padding:0 20px;" v-model="item.point" type='number' placeholder="" />积分 - </el-form-item> - </el-col> - <!-- 优惠券待处理 也可以参考优惠劵的SpuShowcase--> - <!-- TODO 待实现!--> - <el-col :span="24"><el-checkbox label="送优惠券" name="type" /></el-col> + <template v-for="(item, index) in formData.rules" :key="index"> + <el-row type="flex"> + <el-col :span="24" style="font-weight: bold; display: flex"> + 活动层级{{ index + 1 }} + <el-button + link + type="danger" + style="margin-left: auto" + v-if="index != 0" + @click="deleteActivityRule(index)" + > + 删除 + </el-button> + </el-col> + <e-form :ref="'formRef' + index" :model="item"> + <el-form-item + label="优惠门槛:" + prop="limit" + label-width="100px" + style="padding-left: 50px" + > + 满 + <el-input + style="width: 150px; padding: 0 10px" + v-model="item.limit" + type="number" + placeholder="" + /> + 元 + </el-form-item> + <el-form-item label="优惠内容:" label-width="100px" style="padding-left: 50px"> + <el-checkbox-group v-model="activityRules[index]" style="width: 100%"> + <el-col :span="24"> + <el-checkbox label="订单金额优惠" name="type" /> + <el-form-item v-if="activityRules[index].includes('订单金额优惠')"> + 减 + <el-input + style="width: 150px; padding: 0 20px" + v-model="item.discountPrice" + type="number" + placeholder="" + /> + 元 + </el-form-item> + </el-col> + <el-col :span="24"> + <el-checkbox v-model="item.freeDelivery" label="包邮" name="type" /> + </el-col> + <el-col :span="24"> + <el-checkbox label="送积分" name="type" /> + <el-form-item v-if="activityRules[index].includes('送积分')"> + 送 + <el-input + style="width: 150px; padding: 0 20px" + v-model="item.point" + type="number" + placeholder="" + /> + 积分 + </el-form-item> + </el-col> + <!-- 优惠券待处理 也可以参考优惠劵的SpuShowcase--> + <!-- TODO 待实现!--> + <el-col :span="24"> + <el-checkbox label="送优惠券" name="type" /> + </el-col> </el-checkbox-group> </el-form-item> </e-form> - </el-row> - </template> - <el-button type="primary" @click="addStratum">添加活动层级</el-button> + </el-row> + </template> + <!-- TODO 实现:建议改成放在每一个【活动层级】的下面,有点类似主子表 --> + <el-button type="primary" @click="addActivityStratum">添加活动层级</el-button> </el-form-item> <el-form-item label="活动商品" prop="productScope"> <el-radio-group v-model="formData.productScope"> <el-radio v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)" :key="dict.value" - :label="parseInt(dict.value)" - >{{ dict.label }}</el-radio + :label="dict.value" > + {{ dict.label }} + </el-radio> </el-radio-group> </el-form-item> <!-- TODO:活动商品的开发,可以参考优惠劵的,已经搞好啦; --> @@ -87,9 +132,9 @@ > <el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id"> <span style="float: left">{{ item.name }}</span> - <span style="float: right; font-size: 13px; color: #8492a6" - >¥{{ (item.price / 100.0).toFixed(2) }}</span - > + <span style="float: right; font-size: 13px; color: #8492a6"> + ¥{{ (item.price / 100.0).toFixed(2) }} + </span> </el-option> </el-select> </el-form-item> @@ -106,15 +151,8 @@ <script lang="ts" setup> import { getSpuSimpleList } from '@/api/mall/product/spu' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' -import { CommonStatusEnum } from '@/utils/constants' -import * as RewardActivityApi from '@/api/mall/promotion/reward/rewardActivity' -import { - PromotionConditionTypeEnum, - PromotionProductScopeEnum, - PromotionActivityStatusEnum -} from '@/utils/constants' -// 商品数据 -const productSpus = ref<any[]>([]) +import * as RewardActivityApi from '@/api/mall/promotion/reward/rewardActivity' +import { PromotionConditionTypeEnum, PromotionProductScopeEnum } from '@/utils/constants' /** 初始化 **/ onMounted(() => { @@ -127,6 +165,7 @@ defineOptions({ name: 'ProductBrandForm' }) const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 +const productSpus = ref<any[]>([]) // 商品数据 const dialogVisible = ref(false) // 弹窗的是否展示 const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 @@ -141,19 +180,18 @@ const formData = ref({ remark: undefined, productScope: PromotionProductScopeEnum.ALL.scope, productSpuIds: undefined, - rules: [{ - limit: undefined, - discountPrice: undefined, - freeDelivery: undefined, - point: undefined, - couponIds: [], - couponCounts: [] - }], + rules: [ + { + limit: undefined, + discountPrice: undefined, + freeDelivery: undefined, + point: undefined, + couponIds: [], + couponCounts: [] + } + ] }) -// 优惠设置 -let rules=reactive([]); -// 优惠券列表 - +const activityRules = reactive([]) // 优惠设置。每个元素都是一个 [],放“包邮”、“送积分”、“订单金额优惠” const formRules = reactive({ name: [{ required: true, message: '活动名称不能为空', trigger: 'blur' }], startAndEndTime: [{ required: true, message: '活动时间不能为空', trigger: 'blur' }], @@ -173,23 +211,24 @@ const open = async (type: string, id?: number) => { if (id) { formLoading.value = true try { - let data= await RewardActivityApi.getReward(id); - data.startAndEndTime=[new Date(data.startTime), new Date(data.endTime)]; - rules.splice(0,rules.length); - data.rules.forEach((item)=>{ - let ars:string[]=reactive([]); - if(item.freeDelivery){ - ars.push('包邮') - } - if(item.point){ - ars.push('送积分') - } - if(item.discountPrice){ - ars.push('订单金额优惠') - } - rules.push(ars) - }) - formData.value=data + let data = await RewardActivityApi.getReward(id) + data.startAndEndTime = [new Date(data.startTime), new Date(data.endTime)] + activityRules.splice(0, activityRules.length) + data.rules.forEach((item) => { + // TODO 是不是不用 reactive,直接 [] 就可以了? + let array: string[] = reactive([]) + if (item.freeDelivery) { + array.push('包邮') + } + if (item.point) { + array.push('送积分') + } + if (item.discountPrice) { + array.push('订单金额优惠') + } + activityRules.push(array) + }) + formData.value = data } finally { formLoading.value = false } @@ -205,21 +244,16 @@ const submitForm = async () => { const valid = await formRef.value.validate() if (!valid) return // 处理下数据兼容接口 - formData.value.startTime= +new Date(formData.value.startAndEndTime[0]) - formData.value.endTime=+new Date(formData.value.startAndEndTime[1]) - console.log(rules) - rules.forEach((item,index)=>{ - if(item.includes('包邮')){ - formData.value.rules[index].freeDelivery=true; - }else{ - formData.value.rules[index].freeDelivery=false; - } - if(!item.includes('送积分')){ - formData.value.rules[index].point=undefined; - } - if(!item.includes('订单金额优惠')){ - formData.value.rules[index].discountPrice=undefined; - } + formData.value.startTime = +new Date(formData.value.startAndEndTime[0]) + formData.value.endTime = +new Date(formData.value.startAndEndTime[1]) + activityRules.forEach((item, index) => { + formData.value.rules[index].freeDelivery = !!item.includes('包邮') + if (!item.includes('送积分')) { + formData.value.rules[index].point = undefined + } + if (!item.includes('订单金额优惠')) { + formData.value.rules[index].discountPrice = undefined + } }) // 提交请求 @@ -241,22 +275,21 @@ const submitForm = async () => { } } -const addStratum =()=>{ +const addActivityStratum = () => { formData.value.rules.push({ - limit: undefined, - discountPrice: undefined, - freeDelivery: undefined, - point: undefined, - couponIds: [], - couponCounts: [] - }) - rules.push([]); - console.log(rules) + limit: undefined, + discountPrice: undefined, + freeDelivery: undefined, + point: undefined, + couponIds: [], + couponCounts: [] + }) + activityRules.push([]) } -const deleteStratum=(index)=>{ - formData.value.rules.splice(index,1) - rules.splice(index,1) +const deleteActivityRule = (index) => { + formData.value.rules.splice(index, 1) + activityRules.splice(index, 1) } /** 重置表单 */ @@ -271,20 +304,22 @@ const resetForm = () => { remark: undefined, productScope: PromotionProductScopeEnum.ALL.scope, productSpuIds: undefined, - rules: [{ - limit: undefined, - discountPrice: undefined, - freeDelivery: undefined, - point: undefined, - couponIds: [], - couponCounts: [] - }], + rules: [ + { + limit: undefined, + discountPrice: undefined, + freeDelivery: undefined, + point: undefined, + couponIds: [], + couponCounts: [] + } + ] } - rules.splice(0,rules.length); - rules.push(reactive([])); + activityRules.splice(0, activityRules.length) + activityRules.push(reactive([])) // 解决下有时刷新页面第一次点编辑报错 - nextTick(()=>{ - formRef.value?.resetFields() - }) + nextTick(() => { + formRef.value?.resetFields() + }) } </script> diff --git a/src/views/mall/promotion/rewardActivity/index.vue b/src/views/mall/promotion/rewardActivity/index.vue index 86f91fea..c1d352be 100644 --- a/src/views/mall/promotion/rewardActivity/index.vue +++ b/src/views/mall/promotion/rewardActivity/index.vue @@ -122,8 +122,7 @@ <script lang="ts" setup> import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { dateFormatter } from '@/utils/formatTime' -import * as ProductBrandApi from '@/api/mall/product/brand' -import * as RewardActivityApi from '@/api/mall/promotion/reward/rewardActivity' +import * as RewardActivityApi from '@/api/mall/promotion/reward/rewardActivity' import RewardForm from './RewardForm.vue' defineOptions({ name: 'PromotionRewardActivity' }) @@ -157,16 +156,11 @@ const getList = async () => { /** 搜索按钮操作 */ const handleQuery = () => { - // console.log(queryParams) - // message.success('已打印搜索参数') - // return getList() } /** 重置按钮操作 */ const resetQuery = () => { - // message.success('重置查询表单获取数据') - // return queryFormRef.value.resetFields() handleQuery() } @@ -182,8 +176,6 @@ const handleDelete = async (id: number) => { try { // 删除的二次确认 await message.delConfirm() - // message.success('您以确认删除') - // return // 发起删除 await RewardActivityApi.deleteRewardActivity(id) message.success(t('common.delSuccess')) diff --git a/src/views/mall/trade/delivery/express/ExpressForm.vue b/src/views/mall/trade/delivery/express/ExpressForm.vue index f7d5dac7..232fb793 100644 --- a/src/views/mall/trade/delivery/express/ExpressForm.vue +++ b/src/views/mall/trade/delivery/express/ExpressForm.vue @@ -119,7 +119,6 @@ const resetForm = () => { id: undefined, name: '', picUrl: '', - bigPicUrl: '', status: CommonStatusEnum.ENABLE } formRef.value?.resetFields()