commit
						ce84b14d15
					
				|  | @ -0,0 +1,49 @@ | |||
| import request from '@/config/axios' | ||||
| 
 | ||||
| export interface CommentVO { | ||||
|   id: number | ||||
|   userId: number | ||||
|   userNickname: string | ||||
|   userAvatar: string | ||||
|   anonymous: boolean | ||||
|   orderId: number | ||||
|   orderItemId: number | ||||
|   spuId: number | ||||
|   spuName: string | ||||
|   skuId: number | ||||
|   visible: boolean | ||||
|   scores: number | ||||
|   descriptionScores: number | ||||
|   benefitScores: number | ||||
|   content: string | ||||
|   picUrls: string | ||||
|   replyStatus: boolean | ||||
|   replyUserId: number | ||||
|   replyContent: string | ||||
|   replyTime: Date | ||||
| } | ||||
| 
 | ||||
| // 查询商品评论列表
 | ||||
| export const getCommentPage = async (params) => { | ||||
|   return await request.get({ url: `/product/comment/page`, params }) | ||||
| } | ||||
| 
 | ||||
| // 查询商品评论详情
 | ||||
| export const getComment = async (id: number) => { | ||||
|   return await request.get({ url: `/product/comment/get?id=` + id }) | ||||
| } | ||||
| 
 | ||||
| // 添加自评
 | ||||
| export const createComment = async (data: CommentVO) => { | ||||
|   return await request.post({ url: `/product/comment/create`, data }) | ||||
| } | ||||
| 
 | ||||
| // 显示 / 隐藏评论
 | ||||
| export const updateCommentVisible = async (data: any) => { | ||||
|   return await request.put({ url: `/product/comment/update-visible`, data }) | ||||
| } | ||||
| 
 | ||||
| // 商家回复
 | ||||
| export const replyComment = async (data: any) => { | ||||
|   return await request.put({ url: `/product/comment/reply`, data }) | ||||
| } | ||||
|  | @ -9,18 +9,19 @@ export interface Property { | |||
| 
 | ||||
| export interface Sku { | ||||
|   id?: number // 商品 SKU 编号
 | ||||
|   name?: string // 商品 SKU 名称
 | ||||
|   spuId?: number // SPU 编号
 | ||||
|   properties?: Property[] // 属性数组
 | ||||
|   price?: number // 商品价格
 | ||||
|   marketPrice?: number // 市场价
 | ||||
|   costPrice?: number // 成本价
 | ||||
|   price?: number | string // 商品价格
 | ||||
|   marketPrice?: number | string // 市场价
 | ||||
|   costPrice?: number | string // 成本价
 | ||||
|   barCode?: string // 商品条码
 | ||||
|   picUrl?: string // 图片地址
 | ||||
|   stock?: number // 库存
 | ||||
|   weight?: number // 商品重量,单位:kg 千克
 | ||||
|   volume?: number // 商品体积,单位:m^3 平米
 | ||||
|   subCommissionFirstPrice?: number // 一级分销的佣金
 | ||||
|   subCommissionSecondPrice?: number // 二级分销的佣金
 | ||||
|   subCommissionFirstPrice?: number | string // 一级分销的佣金
 | ||||
|   subCommissionSecondPrice?: number | string // 二级分销的佣金
 | ||||
|   salesCount?: number // 商品销量
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,18 +7,20 @@ export interface BargainActivityVO { | |||
|   startTime?: Date | ||||
|   endTime?: Date | ||||
|   status?: number | ||||
|   spuId?: number | ||||
|   userSize?: number // 达到该人数,才能砍到低价
 | ||||
|   bargainCount?: number // 最大帮砍次数
 | ||||
|   totalLimitCount?: number // 最大购买次数
 | ||||
|   stock?: number // 活动总库存
 | ||||
|   spuId: number | ||||
|   skuId: number | ||||
|   bargainFirstPrice: number // 砍价起始价格,单位分
 | ||||
|   bargainPrice: number // 砍价底价
 | ||||
|   stock: number // 活动库存
 | ||||
|   randomMinPrice?: number // 用户每次砍价的最小金额,单位:分
 | ||||
|   randomMaxPrice?: number // 用户每次砍价的最大金额,单位:分
 | ||||
|   successCount?: number // 砍价成功数量
 | ||||
|   products?: BargainProductVO[] | ||||
| } | ||||
| 
 | ||||
| // 砍价活动所需属性
 | ||||
| // 砍价活动所需属性。选择的商品和属性的时候使用方便使用活动的通用封装
 | ||||
| export interface BargainProductVO { | ||||
|   spuId: number | ||||
|   skuId: number | ||||
|  |  | |||
|  | @ -19,6 +19,11 @@ export const getDeliveryExpress = async (id: number) => { | |||
|   return await request.get({ url: '/trade/delivery/express/get?id=' + id }) | ||||
| } | ||||
| 
 | ||||
| // 获得快递公司精简信息列表
 | ||||
| export const getSimpleDeliveryExpressList = () => { | ||||
|   return request.get({ url: '/trade/delivery/express/list-all-simple' }) | ||||
| } | ||||
| 
 | ||||
| // 新增快递公司
 | ||||
| export const createDeliveryExpress = async (data: DeliveryExpressVO) => { | ||||
|   return await request.post({ url: '/trade/delivery/express/create', data }) | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ export interface DeliveryPickUpStoreVO { | |||
| } | ||||
| 
 | ||||
| // 查询自提门店列表
 | ||||
| export const getDeliveryPickUpStorePage = async (params: DeliveryPickUpStorePageReqVO) => { | ||||
| export const getDeliveryPickUpStorePage = async (params) => { | ||||
|   return await request.get({ url: '/trade/delivery/pick-up-store/page', params }) | ||||
| } | ||||
| 
 | ||||
|  | @ -25,6 +25,11 @@ export const getDeliveryPickUpStore = async (id: number) => { | |||
|   return await request.get({ url: '/trade/delivery/pick-up-store/get?id=' + id }) | ||||
| } | ||||
| 
 | ||||
| // 查询自提门店精简列表
 | ||||
| export const getListAllSimple = async () => { | ||||
|   return await request.get({ url: '/trade/delivery/pick-up-store/list-all-simple' }) | ||||
| } | ||||
| 
 | ||||
| // 新增自提门店
 | ||||
| export const createDeliveryPickUpStore = async (data: DeliveryPickUpStoreVO) => { | ||||
|   return await request.post({ url: '/trade/delivery/pick-up-store/create', data }) | ||||
|  | @ -39,8 +44,3 @@ export const updateDeliveryPickUpStore = async (data: DeliveryPickUpStoreVO) => | |||
| export const deleteDeliveryPickUpStore = async (id: number) => { | ||||
|   return await request.delete({ url: '/trade/delivery/pick-up-store/delete?id=' + id }) | ||||
| } | ||||
| 
 | ||||
| // 导出自提门店 Excel
 | ||||
| export const exportDeliveryPickUpStoreApi = async (params) => { | ||||
|   return await request.download({ url: '/trade/delivery/pick-up-store/export-excel', params }) | ||||
| } | ||||
|  |  | |||
|  | @ -1,12 +1,121 @@ | |||
| import request from '@/config/axios' | ||||
| 
 | ||||
| // 获得交易订单分页
 | ||||
| // TODO @xiaobai:改成 getOrderPage
 | ||||
| export const getOrderList = (params: PageParam) => { | ||||
|   return request.get({ url: '/trade/order/page', params }) | ||||
| } | ||||
| 
 | ||||
| // 获得交易订单详情
 | ||||
| export const getOrderDetail = (id: number) => { | ||||
|   return request.get({ url: '/trade/order/get-detail?id=' + id }) | ||||
| } | ||||
| import request from '@/config/axios' | ||||
| 
 | ||||
| export interface OrderVO { | ||||
|   id?: number | null // 订单编号
 | ||||
|   no?: string // 订单流水号
 | ||||
|   createTime?: Date | null // 下单时间
 | ||||
|   type?: number | null // 订单类型
 | ||||
|   terminal?: number | null // 订单来源
 | ||||
|   userId?: number | null // 用户编号
 | ||||
|   userIp?: string // 用户 IP
 | ||||
|   userRemark?: string // 用户备注
 | ||||
|   status?: number | null // 订单状态
 | ||||
|   productCount?: number | null // 购买的商品数量
 | ||||
|   finishTime?: Date | null // 订单完成时间
 | ||||
|   cancelTime?: Date | null // 订单取消时间
 | ||||
|   cancelType?: number | null // 取消类型
 | ||||
|   remark?: string // 商家备注
 | ||||
|   payOrderId: number | null // 支付订单编号
 | ||||
|   payed?: boolean // 是否已支付
 | ||||
|   payTime?: Date | null // 付款时间
 | ||||
|   payChannelCode?: string // 支付渠道
 | ||||
|   originalPrice?: number | null // 商品原价(总)
 | ||||
|   orderPrice?: number | null // 订单原价(总)
 | ||||
|   discountPrice?: number | null // 订单优惠(总)
 | ||||
|   deliveryPrice?: number | null // 运费金额
 | ||||
|   adjustPrice?: number | null // 订单调价(总)
 | ||||
|   payPrice?: number | null // 应付金额(总)
 | ||||
|   deliveryType?: number | null // 发货方式
 | ||||
|   deliveryTemplateId?: number | null // 配送模板编号
 | ||||
|   logisticsId?: number | null | null // 发货物流公司编号
 | ||||
|   logisticsNo?: string // 发货物流单号
 | ||||
|   deliveryStatus?: number | null // 发货状态
 | ||||
|   deliveryTime?: Date | null // 发货时间
 | ||||
|   receiveTime?: Date | null // 收货时间
 | ||||
|   receiverName?: string // 收件人名称
 | ||||
|   receiverMobile?: string // 收件人手机
 | ||||
|   receiverAreaId?: number | null // 收件人地区编号
 | ||||
|   receiverPostCode?: number | null // 收件人邮编
 | ||||
|   receiverDetailAddress?: string // 收件人详细地址
 | ||||
|   afterSaleStatus?: number | null // 售后状态
 | ||||
|   refundPrice?: number | null // 退款金额
 | ||||
|   couponId?: number | null // 优惠劵编号
 | ||||
|   couponPrice?: number | null // 优惠劵减免金额
 | ||||
|   pointPrice?: number | null // 积分抵扣的金额
 | ||||
|   receiverAreaName?: string //收件人地区名字
 | ||||
|   items?: OrderItemRespVO[] // 订单项列表
 | ||||
|   //用户信息
 | ||||
|   user?: { | ||||
|     id?: number | null | ||||
|     nickname?: string | ||||
|     avatar?: string | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export interface OrderItemRespVO { | ||||
|   // ========== 订单项基本信息 ==========
 | ||||
|   id?: number | null // 编号
 | ||||
|   userId?: number | null // 用户编号
 | ||||
|   orderId?: number | null // 订单编号
 | ||||
|   // ========== 商品基本信息 ==========
 | ||||
|   spuId?: number | null // 商品 SPU 编号
 | ||||
|   spuName?: string //商品 SPU 名称
 | ||||
|   skuId?: number | null // 商品 SKU 编号
 | ||||
|   picUrl?: string //商品图片
 | ||||
|   count?: number | null //购买数量
 | ||||
|   // ========== 价格 + 支付基本信息 ==========
 | ||||
|   originalPrice?: number | null //商品原价(总)
 | ||||
|   originalUnitPrice?: number | null //商品原价(单)
 | ||||
|   discountPrice?: number | null //商品优惠(总)
 | ||||
|   payPrice?: number | null //商品实付金额(总)
 | ||||
|   orderPartPrice?: number | null //子订单分摊金额(总)
 | ||||
|   orderDividePrice?: number | null //分摊后子订单实付金额(总)
 | ||||
|   // ========== 营销基本信息 ==========
 | ||||
|   // TODO 芋艿:在捉摸一下
 | ||||
|   // ========== 售后基本信息 ==========
 | ||||
|   afterSaleStatus?: number | null // 售后状态
 | ||||
|   properties?: ProductPropertiesVO[] //属性数组
 | ||||
| } | ||||
| 
 | ||||
| export interface ProductPropertiesVO { | ||||
|   propertyId?: number | null // 属性的编号
 | ||||
|   propertyName?: string // 属性的名称
 | ||||
|   valueId?: number | null //属性值的编号
 | ||||
|   valueName?: string // 属性值的名称
 | ||||
| } | ||||
| 
 | ||||
| // 查询交易订单列表
 | ||||
| export const getOrderPage = async (params) => { | ||||
|   return await request.get({ url: `/trade/order/page`, params }) | ||||
| } | ||||
| 
 | ||||
| // 查询交易订单详情
 | ||||
| export const getOrder = async (id: number | null) => { | ||||
|   return await request.get({ url: `/trade/order/get-detail?id=` + id }) | ||||
| } | ||||
| 
 | ||||
| export interface DeliveryVO { | ||||
|   id: number // 订单编号
 | ||||
|   logisticsId: number | null // 物流公司编号
 | ||||
|   logisticsNo: string // 物流编号
 | ||||
| } | ||||
| 
 | ||||
| // 订单发货
 | ||||
| export const delivery = async (data: DeliveryVO) => { | ||||
|   return await request.put({ url: `/trade/order/delivery`, data }) | ||||
| } | ||||
| 
 | ||||
| // 订单备注
 | ||||
| export const updateRemark = async (data: any) => { | ||||
|   return await request.put({ url: `/trade/order/update-remark`, data }) | ||||
| } | ||||
| 
 | ||||
| // 订单调价
 | ||||
| export const updatePrice = async (data: any) => { | ||||
|   return await request.put({ url: `/trade/order/update-price`, data }) | ||||
| } | ||||
| 
 | ||||
| // 修改订单地址
 | ||||
| export const updateAddress = async (data: any) => { | ||||
|   return await request.put({ url: `/trade/order/update-address`, data }) | ||||
| } | ||||
|  |  | |||
|  | @ -1,228 +0,0 @@ | |||
| // TODO @xiaobai:这个放到 order/index.ts  里哈
 | ||||
| // TODO @xiaobai:注释放到变量后面,这样简洁一点
 | ||||
| // TODO @xiaobai:这个改成 TradeOrderRespVO
 | ||||
| export interface TradeOrderPageItemRespVO { | ||||
|   // 订单编号
 | ||||
|   id?: number | ||||
|   // 订单流水号
 | ||||
|   no?: string | ||||
|   // 下单时间
 | ||||
|   createTime?: Date | ||||
|   // 订单类型
 | ||||
|   type?: number | ||||
|   // 订单来源
 | ||||
|   terminal?: number | ||||
|   // 用户编号
 | ||||
|   userId?: number | ||||
|   // 用户 IP
 | ||||
|   userIp?: string | ||||
|   // 用户备注
 | ||||
|   userRemark?: string | ||||
|   // 订单状态
 | ||||
|   status?: number | ||||
|   // 购买的商品数量
 | ||||
|   productCount?: number | ||||
|   // 订单完成时间
 | ||||
|   finishTime?: Date | ||||
|   // 订单取消时间
 | ||||
|   cancelTime?: Date | ||||
|   // 取消类型
 | ||||
|   cancelType?: number | ||||
|   // 商家备注
 | ||||
|   remark?: string | ||||
|   // 支付订单编号
 | ||||
|   payOrderId: number | ||||
|   // 是否已支付
 | ||||
|   payed?: boolean | ||||
|   // 付款时间
 | ||||
|   payTime?: Date | ||||
|   // 支付渠道
 | ||||
|   payChannelCode?: string | ||||
|   // 商品原价(总)
 | ||||
|   originalPrice?: number | ||||
|   // 订单原价(总)
 | ||||
|   orderPrice?: number | ||||
|   // 订单优惠(总)
 | ||||
|   discountPrice?: number | ||||
|   // 运费金额
 | ||||
|   deliveryPrice?: number | ||||
|   // 订单调价(总)
 | ||||
|   adjustPrice?: number | ||||
|   // 应付金额(总)
 | ||||
|   payPrice?: number | ||||
|   // 配送模板编号
 | ||||
|   deliveryTemplateId?: number | ||||
|   // 发货物流公司编号
 | ||||
|   logisticsId?: number | ||||
|   // 发货物流单号
 | ||||
|   logisticsNo?: string | ||||
|   // 发货状态
 | ||||
|   deliveryStatus?: number | ||||
|   // 发货时间
 | ||||
|   deliveryTime?: Date | ||||
|   // 收货时间
 | ||||
|   receiveTime?: Date | ||||
|   // 收件人名称
 | ||||
|   receiverName?: string | ||||
|   // 收件人手机
 | ||||
|   receiverMobile?: string | ||||
|   // 收件人地区编号
 | ||||
|   receiverAreaId?: number | ||||
|   // 收件人邮编
 | ||||
|   receiverPostCode?: number | ||||
|   // 收件人详细地址
 | ||||
|   receiverDetailAddress?: string | ||||
|   // 售后状态
 | ||||
|   afterSaleStatus?: number | ||||
|   // 退款金额
 | ||||
|   refundPrice?: number | ||||
|   // 优惠劵编号
 | ||||
|   couponId?: number | ||||
|   // 优惠劵减免金额
 | ||||
|   couponPrice?: number | ||||
|   // 积分抵扣的金额
 | ||||
|   pointPrice?: number | ||||
|   //收件人地区名字
 | ||||
|   receiverAreaName?: string | ||||
|   // 订单项列表
 | ||||
|   items?: TradeOrderItemBaseVO[] | ||||
|   //用户信息
 | ||||
|   user?: MemberUserRespDTO | ||||
| } | ||||
| 
 | ||||
| // TODO @xiaobai:这个改成 TradeOrderItemRespVO
 | ||||
| /** | ||||
|  * 交易订单项 Base VO,提供给添加、修改、详细的子 VO 使用 | ||||
|  * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | ||||
|  */ | ||||
| export interface TradeOrderItemBaseVO { | ||||
|   // ========== 订单项基本信息 ==========
 | ||||
|   /** | ||||
|    * 编号 | ||||
|    */ | ||||
|   id?: number | ||||
|   /** | ||||
|    * 用户编号 | ||||
|    */ | ||||
|   userId?: number | ||||
|   /** | ||||
|    * 订单编号 | ||||
|    */ | ||||
|   orderId?: number | ||||
|   // ========== 商品基本信息 ==========
 | ||||
|   /** | ||||
|    * 商品 SPU 编号 | ||||
|    */ | ||||
|   spuId?: number | ||||
|   /** | ||||
|    * 商品 SPU 名称 | ||||
|    */ | ||||
|   spuName?: string | ||||
|   /** | ||||
|    * 商品 SKU 编号 | ||||
|    */ | ||||
|   skuId?: number | ||||
|   /** | ||||
|    * 商品图片 | ||||
|    */ | ||||
|   picUrl?: string | ||||
|   /** | ||||
|    * 购买数量 | ||||
|    */ | ||||
|   count?: number | ||||
|   // ========== 价格 + 支付基本信息 ==========
 | ||||
|   /** | ||||
|    * 商品原价(总) | ||||
|    */ | ||||
|   originalPrice?: number | ||||
|   /** | ||||
|    * 商品原价(单) | ||||
|    */ | ||||
|   originalUnitPrice?: number | ||||
|   /** | ||||
|    * 商品优惠(总) | ||||
|    */ | ||||
|   discountPrice?: number | ||||
|   /** | ||||
|    * 商品实付金额(总) | ||||
|    */ | ||||
|   payPrice?: number | ||||
|   /** | ||||
|    * 子订单分摊金额(总) | ||||
|    */ | ||||
|   orderPartPrice?: number | ||||
|   /** | ||||
|    * 分摊后子订单实付金额(总) | ||||
|    */ | ||||
|   orderDividePrice?: number | ||||
|   // ========== 营销基本信息 ==========
 | ||||
|   // TODO 芋艿:在捉摸一下
 | ||||
|   // ========== 售后基本信息 ==========
 | ||||
|   /** | ||||
|    * 售后状态 | ||||
|    */ | ||||
|   afterSaleStatus?: number | ||||
|   //属性数组
 | ||||
|   properties?: ProductPropertyValueDetailRespVO[] | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 管理后台 - 商品属性值的明细 Response VO | ||||
|  */ | ||||
| export interface ProductPropertyValueDetailRespVO { | ||||
|   /** | ||||
|    * 属性的编号 | ||||
|    */ | ||||
|   propertyId?: number | ||||
|   /** | ||||
|    * 属性的名称 | ||||
|    */ | ||||
|   propertyName?: string | ||||
|   /** | ||||
|    * 属性值的编号 | ||||
|    */ | ||||
|   valueId?: number | ||||
|   /** | ||||
|    * 属性值的名称 | ||||
|    */ | ||||
|   valueName?: string | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 订单详情查询 请求 | ||||
|  */ | ||||
| export interface TradeOrderPageReqVO { | ||||
|   pageNo: number | ||||
|   pageSize: number | ||||
|   no?: string | ||||
|   userId?: string | ||||
|   userNickname?: string | ||||
|   userMobile?: string | ||||
|   receiverName?: string | ||||
|   receiverMobile?: string | ||||
|   terminal?: string | ||||
|   type?: number | ||||
|   status?: number | ||||
|   payChannelCode?: string | ||||
|   createTime?: [Date, Date] | ||||
|   spuName?: string | ||||
|   itemCount?: string | ||||
|   all?: string | ||||
| } | ||||
| 
 | ||||
| //用户信息
 | ||||
| export interface MemberUserRespDTO { | ||||
|   id?: number | ||||
|   nickname?: string | ||||
|   status?: number | ||||
|   avatar?: string | ||||
|   mobile?: string | ||||
| } | ||||
| //订单详情选中type
 | ||||
| export interface SelectType { | ||||
|   queryParams: TradeOrderPageReqVO | ||||
|   selectTotal: number //选中的数量
 | ||||
|   selectAllFlag: boolean //全选标识
 | ||||
|   selectData: Map<number, Set<string>> //存放涉及选中得页面以及每页选中得数据订单号  全选时根据条件查询 排除取消的list订单
 | ||||
|   unSelectList: Set<string> //登记取消的list 全选标识为true 时登记单独取消的list,再次选中时排除, 全选标识为false 时清空list
 | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| import request from '@/config/axios' | ||||
| 
 | ||||
| export interface AddressVO { | ||||
|   id: number | ||||
|   name: string | ||||
|   mobile: string | ||||
|   areaId: number | ||||
|   detailAddress: string | ||||
|   defaultStatus: boolean | ||||
| } | ||||
| 
 | ||||
| // 查询用户收件地址列表
 | ||||
| export const getAddressList = async (params) => { | ||||
|   return await request.get({ url: `/member/address/list`, params }) | ||||
| } | ||||
|  | @ -0,0 +1,38 @@ | |||
| import request from '@/config/axios' | ||||
| 
 | ||||
| export interface GroupVO { | ||||
|   id: number | ||||
|   name: string | ||||
|   remark: string | ||||
|   status: number | ||||
| } | ||||
| 
 | ||||
| // 查询用户分组列表
 | ||||
| export const getGroupPage = async (params: any) => { | ||||
|   return await request.get({ url: `/member/group/page`, params }) | ||||
| } | ||||
| 
 | ||||
| // 查询用户分组详情
 | ||||
| export const getGroup = async (id: number) => { | ||||
|   return await request.get({ url: `/member/group/get?id=` + id }) | ||||
| } | ||||
| 
 | ||||
| // 新增用户分组
 | ||||
| export const createGroup = async (data: GroupVO) => { | ||||
|   return await request.post({ url: `/member/group/create`, data }) | ||||
| } | ||||
| 
 | ||||
| // 查询用户分组 - 精简信息列表
 | ||||
| export const getSimpleGroupList = async () => { | ||||
|   return await request.get({ url: `/member/group/list-all-simple` }) | ||||
| } | ||||
| 
 | ||||
| // 修改用户分组
 | ||||
| export const updateGroup = async (data: GroupVO) => { | ||||
|   return await request.put({ url: `/member/group/update`, data }) | ||||
| } | ||||
| 
 | ||||
| // 删除用户分组
 | ||||
| export const deleteGroup = async (id: number) => { | ||||
|   return await request.delete({ url: `/member/group/delete?id=` + id }) | ||||
| } | ||||
|  | @ -0,0 +1,42 @@ | |||
| import request from '@/config/axios' | ||||
| 
 | ||||
| export interface LevelVO { | ||||
|   id: number | ||||
|   name: string | ||||
|   experience: number | ||||
|   value: number | ||||
|   discountPercent: number | ||||
|   icon: string | ||||
|   bgUrl: string | ||||
|   status: number | ||||
| } | ||||
| 
 | ||||
| // 查询会员等级列表
 | ||||
| export const getLevelList = async (params) => { | ||||
|   return await request.get({ url: `/member/level/list`, params }) | ||||
| } | ||||
| 
 | ||||
| // 查询会员等级详情
 | ||||
| export const getLevel = async (id: number) => { | ||||
|   return await request.get({ url: `/member/level/get?id=` + id }) | ||||
| } | ||||
| 
 | ||||
| // 查询会员等级 - 精简信息列表
 | ||||
| export const getSimpleLevelList = async () => { | ||||
|   return await request.get({ url: `/member/level/list-all-simple` }) | ||||
| } | ||||
| 
 | ||||
| // 新增会员等级
 | ||||
| export const createLevel = async (data: LevelVO) => { | ||||
|   return await request.post({ url: `/member/level/create`, data }) | ||||
| } | ||||
| 
 | ||||
| // 修改会员等级
 | ||||
| export const updateLevel = async (data: LevelVO) => { | ||||
|   return await request.put({ url: `/member/level/update`, data }) | ||||
| } | ||||
| 
 | ||||
| // 删除会员等级
 | ||||
| export const deleteLevel = async (id: number) => { | ||||
|   return await request.delete({ url: `/member/level/delete?id=` + id }) | ||||
| } | ||||
|  | @ -8,20 +8,9 @@ export interface RecordVO { | |||
|   description: string | ||||
|   point: number | ||||
|   totalPoint: number | ||||
|   status: number | ||||
|   userId: number | ||||
|   freezingTime: Date | ||||
|   thawingTime: Date | ||||
|   createDate: Date | ||||
| } | ||||
| export interface RecordQueryVO { | ||||
|   bizType: string | null | undefined | ||||
|   title: string | null | undefined | ||||
|   pageNo: number | ||||
|   pageSize: number | ||||
|   userId: number | null | undefined | ||||
|   createDate: string[] | ||||
| } | ||||
| 
 | ||||
| // 查询用户积分记录列表
 | ||||
| export const getRecordPage = async (params) => { | ||||
|  |  | |||
|  | @ -9,25 +9,25 @@ export interface SignInConfigVO { | |||
| 
 | ||||
| // 查询积分签到规则列表
 | ||||
| export const getSignInConfigList = async () => { | ||||
|   return await request.get({ url: `/member/point/sign-in-config/list` }) | ||||
|   return await request.get({ url: `/member/sign-in/config/list` }) | ||||
| } | ||||
| 
 | ||||
| // 查询积分签到规则详情
 | ||||
| export const getSignInConfig = async (id: number) => { | ||||
|   return await request.get({ url: `/member/point/sign-in-config/get?id=` + id }) | ||||
|   return await request.get({ url: `/member/sign-in/config/get?id=` + id }) | ||||
| } | ||||
| 
 | ||||
| // 新增积分签到规则
 | ||||
| export const createSignInConfig = async (data: SignInConfigVO) => { | ||||
|   return await request.post({ url: `/member/point/sign-in-config/create`, data }) | ||||
|   return await request.post({ url: `/member/sign-in/config/create`, data }) | ||||
| } | ||||
| 
 | ||||
| // 修改积分签到规则
 | ||||
| export const updateSignInConfig = async (data: SignInConfigVO) => { | ||||
|   return await request.put({ url: `/member/point/sign-in-config/update`, data }) | ||||
|   return await request.put({ url: `/member/sign-in/config/update`, data }) | ||||
| } | ||||
| 
 | ||||
| // 删除积分签到规则
 | ||||
| export const deleteSignInConfig = async (id: number) => { | ||||
|   return await request.delete({ url: `/member/point/sign-in-config/delete?id=` + id }) | ||||
|   return await request.delete({ url: `/member/sign-in/config/delete?id=` + id }) | ||||
| } | ||||
|  |  | |||
|  | @ -6,17 +6,8 @@ export interface SignInRecordVO { | |||
|   day: number | ||||
|   point: number | ||||
| } | ||||
| export interface SignInRecordQueryVO { | ||||
|   pageNo: number | ||||
|   pageSize: number | ||||
|   userId?: number | ||||
|   nickname: number | undefined | null | ||||
|   day?: number | null | undefined | ||||
|   point?: number | null | undefined | ||||
|   createTime: string[] | null | undefined | ||||
| } | ||||
| 
 | ||||
| // 查询用户签到积分列表
 | ||||
| export const getSignInRecordPage = async (params) => { | ||||
|   return await request.get({ url: `/member/point/sign-in-record/page`, params }) | ||||
|   return await request.get({ url: `/member/sign-in/record/page`, params }) | ||||
| } | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ export interface TagVO { | |||
| } | ||||
| 
 | ||||
| // 查询会员标签列表
 | ||||
| export const getMemberTagPage = async (params) => { | ||||
| export const getMemberTagPage = async (params: any) => { | ||||
|   return await request.get({ url: `/member/tag/page`, params }) | ||||
| } | ||||
| 
 | ||||
|  | @ -15,6 +15,11 @@ export const getMemberTag = async (id: number) => { | |||
|   return await request.get({ url: `/member/tag/get?id=` + id }) | ||||
| } | ||||
| 
 | ||||
| // 查询会员标签 - 精简信息列表
 | ||||
| export const getSimpleTagList = async () => { | ||||
|   return await request.get({ url: `/member/tag/list-all-simple` }) | ||||
| } | ||||
| 
 | ||||
| // 新增会员标签
 | ||||
| export const createMemberTag = async (data: TagVO) => { | ||||
|   return await request.post({ url: `/member/tag/create`, data }) | ||||
|  |  | |||
|  | @ -2,38 +2,26 @@ import request from '@/config/axios' | |||
| 
 | ||||
| export interface UserVO { | ||||
|   id: number | ||||
|   mobile: string | ||||
|   password: string | ||||
|   status: number | ||||
|   registerIp: string | ||||
|   avatar: string | undefined | ||||
|   birthday: number | undefined | ||||
|   createTime: number | undefined | ||||
|   loginDate: number | undefined | ||||
|   loginIp: string | ||||
|   loginDate: Date | ||||
|   nickname: string | ||||
|   avatar: string | ||||
|   name: string | ||||
|   sex: number | ||||
|   areaId: number | ||||
|   birthday: Date | ||||
|   mark: string | ||||
|   createTime: Date | ||||
| } | ||||
| export interface UserBaseInfoVO { | ||||
|   id: number | undefined | null | ||||
|   mobile: string | ||||
|   password: string | null | undefined | ||||
|   status: number | ||||
|   registerIp: string | null | undefined | ||||
|   loginIp: string | null | undefined | ||||
|   loginDate: Date | null | undefined | ||||
|   nickname: string | null | undefined | ||||
|   avatar: string | null | undefined | ||||
|   name: string | null | undefined | ||||
|   name: string | undefined | ||||
|   nickname: string | undefined | ||||
|   registerIp: string | ||||
|   sex: number | ||||
|   areaId: number | null | undefined | ||||
|   birthday: Date | null | undefined | ||||
|   mark: string | null | undefined | ||||
|   createTime: Date | null | undefined | ||||
|   status: number | ||||
|   areaId: number | undefined | ||||
|   areaName: string | undefined | ||||
|   levelName: string | null | ||||
|   point: number | undefined | null | ||||
|   totalPoint: number | undefined | null | ||||
|   experience: number | null | undefined | ||||
| } | ||||
| 
 | ||||
| // 查询会员用户列表
 | ||||
| export const getUserPage = async (params) => { | ||||
|   return await request.get({ url: `/member/user/page`, params }) | ||||
|  | @ -48,3 +36,8 @@ export const getUser = async (id: number) => { | |||
| export const updateUser = async (data: UserVO) => { | ||||
|   return await request.put({ url: `/member/user/update`, data }) | ||||
| } | ||||
| 
 | ||||
| // 修改会员用户等级
 | ||||
| export const updateUserLevel = async (data: any) => { | ||||
|   return await request.put({ url: `/member/user/update-level`, data }) | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693028338187" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22985" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M983.8 312.7C958 251.7 921 197 874 150c-47-47-101.8-83.9-162.7-109.7C648.2 13.5 581.1 0 512 0S375.8 13.5 312.7 40.2C251.7 66 197 102.9 150 150c-47 47-83.9 101.8-109.7 162.7C13.5 375.8 0 442.9 0 512s13.5 136.2 40.2 199.3C66 772.3 102.9 827 150 874c47 47 101.8 83.9 162.7 109.7 63.1 26.7 130.2 40.2 199.3 40.2s136.2-13.5 199.3-40.2C772.3 958 827 921 874 874c47-47 83.9-101.8 109.7-162.7 26.7-63.1 40.2-130.2 40.2-199.3s-13.4-136.2-40.1-199.3z m-55.3 375.2c-22.8 53.8-55.4 102.2-96.9 143.7s-89.9 74.1-143.7 96.9C632.2 952.1 573 964 512 964s-120.2-11.9-175.9-35.5c-53.8-22.8-102.2-55.4-143.7-96.9s-74.1-89.9-96.9-143.7C71.9 632.2 60 573 60 512s11.9-120.2 35.5-175.9c22.8-53.8 55.4-102.2 96.9-143.7s89.9-74.1 143.7-96.9C391.8 71.9 451 60 512 60s120.2 11.9 175.9 35.5c53.8 22.8 102.2 55.4 143.7 96.9s74.1 89.9 96.9 143.7C952.1 391.8 964 451 964 512s-11.9 120.2-35.5 175.9z" fill="#000000" p-id="22986"></path><path d="M706 469.1H574.7l84.2-180.6c7-15 0.4-32.9-14.5-39.9-15-7-32.9-0.4-39.9 14.5L512 461.5l-92.5-198.3c-7-15-24.9-21.5-39.9-14.5s-21.5 24.9-14.5 39.9l84.2 180.6H318c-16.5 0-30 13.5-30 30s13.5 30 30 30h164v64h-92.5c-20.6 0-37.5 13.5-37.5 30s16.9 30 37.5 30H482v95c0 16.5 13.5 30 30 30s30-13.5 30-30v-95h92.5c20.6 0 37.5-13.5 37.5-30s-16.9-30-37.5-30H542v-64h164c16.5 0 30-13.5 30-30 0-16.6-13.5-30.1-30-30.1z" fill="#000000" p-id="22987"></path></svg> | ||||
| After Width: | Height: | Size: 1.7 KiB | 
|  | @ -0,0 +1 @@ | |||
| <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693028553383" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="28918" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M510.72 962.56C262.4 960 61.44 757.76 64 509.44 66.56 263.68 264.96 65.28 510.72 62.72c17.92 0 34.56 14.08 34.56 32s-14.08 34.56-32 34.56h-2.56C299.52 130.56 128 300.8 128 512s171.52 382.72 382.72 382.72S893.44 723.2 893.44 512c0-17.92 16.64-33.28 34.56-32 17.92 0 32 15.36 32 32 0 248.32-200.96 450.56-449.28 450.56z" fill="#000000" p-id="28919"></path><path d="M645.12 480H375.04c-17.92 0-34.56-14.08-34.56-32s14.08-34.56 32-34.56h272.64c17.92 0 33.28 16.64 32 34.56 0 17.92-14.08 32-32 32z m0 130.56H375.04c-17.92 0-33.28-16.64-32-34.56 0-17.92 15.36-32 32-32h270.08c17.92 0 33.28 16.64 32 34.56 0 16.64-14.08 32-32 32z" fill="#000000" p-id="28920"></path><path d="M510.72 746.24c-17.92 0-33.28-15.36-33.28-33.28V441.6c0-17.92 16.64-33.28 34.56-32 17.92 0 32 15.36 32 32v270.08c0 19.2-15.36 34.56-33.28 34.56z" fill="#000000" p-id="28921"></path><path d="M510.72 458.24c-8.96 0-17.92-3.84-24.32-10.24l-111.36-111.36c-14.08-12.8-15.36-33.28-2.56-47.36s33.28-15.36 47.36-2.56l2.56 2.56 111.36 111.36c12.8 12.8 12.8 34.56 0 47.36-6.4 6.4-15.36 10.24-23.04 10.24z" fill="#000000" p-id="28922"></path><path d="M510.72 458.24c-8.96 0-17.92-3.84-24.32-10.24-12.8-12.8-12.8-34.56 0-47.36l111.36-111.36c14.08-12.8 35.84-10.24 47.36 2.56 11.52 12.8 11.52 32 0 44.8L533.76 448c-6.4 6.4-15.36 10.24-23.04 10.24zM925.44 241.92c17.92 0 33.28-15.36 33.28-33.28 0-8.96-3.84-17.92-10.24-24.32l-111.36-111.36c-12.8-14.08-33.28-14.08-47.36-1.28s-14.08 33.28-1.28 47.36l1.28 1.28 111.36 111.36c7.68 6.4 15.36 10.24 24.32 10.24z" fill="#000000" p-id="28923"></path><path d="M815.36 353.28c8.96 0 17.92-3.84 24.32-10.24l111.36-111.36c12.8-14.08 10.24-35.84-2.56-47.36-12.8-11.52-32-11.52-44.8 0l-111.36 111.36c-12.8 12.8-12.8 34.56 0 47.36 5.12 6.4 14.08 10.24 23.04 10.24z" fill="#000000" p-id="28924"></path><path d="M920.32 241.92c17.92 0 34.56-14.08 34.56-32s-14.08-34.56-32-34.56H695.04c-17.92 0-33.28 16.64-32 34.56 0 17.92 15.36 32 32 32h225.28z" fill="#000000" p-id="28925"></path></svg> | ||||
| After Width: | Height: | Size: 2.3 KiB | 
|  | @ -0,0 +1 @@ | |||
| <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693027700643" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8876" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M936.96 385.877333l-203.434667-204.8-18.090667-7.68L308.565333 173.397333l-18.090667 7.68L87.04 385.877333c-9.728 9.898667-9.898667 25.941333-0.170667 35.84l406.869333 421.034667c4.778667 4.949333 11.434667 7.850667 18.432 7.850667 6.997333 0 13.653333-2.901333 18.432-7.850667l406.869333-421.034667C946.858667 411.648 946.688 395.776 936.96 385.877333zM868.522667 389.632l-141.994667 0-163.84-165.034667 141.994667 0L868.522667 389.632zM319.317333 224.768l143.018667 0-163.84 165.034667L155.477333 389.802667 319.317333 224.768zM176.469333 440.832l132.608 0 18.090667-7.509333 185.173333-186.538667 185.173333 186.538667 18.090667 7.509333 131.584 0L512 787.968 176.469333 440.832z" p-id="8877" fill="#000000"></path></svg> | ||||
| After Width: | Height: | Size: 1.0 KiB | 
|  | @ -0,0 +1 @@ | |||
| <svg t="1693027780777" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10083" width="128" height="128"><path d="M509.091764 501.653351c241.775532 0 424.086741-78.085426 424.086741-181.63992 0-103.543238-182.311209-181.628664-424.086741-181.628664S84.993766 216.471217 84.993766 320.014454C84.993766 423.568948 267.316232 501.653351 509.091764 501.653351zM509.091764 184.220698c222.908836 0 378.251833 71.561849 378.251833 135.793756S732.001623 455.818443 509.091764 455.818443c-222.920092 0-378.26309-71.573105-378.26309-135.803989S286.171672 184.220698 509.091764 184.220698z" fill="#000000" p-id="10084"></path><path d="M509.083577 694.061522c241.1155 0 422.937568-77.598332 422.937568-180.482561 0-27.169803-13.127995-52.453652-36.241412-75.131141-0.148379-0.153496-0.26606-0.320295-0.418532-0.468674-0.170892-0.166799-0.285502-0.345877-0.456395-0.51063l-0.11461 0.125867c-3.717671-3.40761-8.576329-5.608741-14.017248-5.608741-11.542894 0-20.898982 9.356089-20.898982 20.898982 0 6.110161 2.721994 11.481496 6.901177 15.302521l-0.082888 0.091074c13.948687 14.024411 21.809725 31.154557 21.809725 45.300742 0 64.785515-155.813718 136.966465-379.419426 136.966465-223.595474 0-379.410216-72.180949-379.410216-136.966465 0-16.139585 4.53734-29.952172 22.323425-45.670156 0.213871-0.204661 0.429789-0.381693 0.635473-0.594541 0.137123-0.118704 0.240477-0.233314 0.378623-0.354064l-0.084934-0.080841c3.416819-3.719718 5.623068-8.588609 5.623068-14.037714 0-11.542894-9.356089-20.898982-20.898982-20.898982-5.770424 0-10.993378 2.340301-14.773472 6.119371l-0.122797-0.118704c-23.408129 22.797215-36.594453 48.27754-36.594453 75.635631C86.158289 616.462167 267.979334 694.061522 509.083577 694.061522z" fill="#000000" p-id="10085"></path><path d="M895.577119 629.529787c-0.168846-0.164752-0.282433-0.342808-0.453325-0.50756l-0.11461 0.124843c-3.717671-3.40761-8.577353-5.608741-14.018272-5.608741-11.540847 0-20.897959 9.356089-20.897959 20.898982 0 6.110161 2.720971 11.482519 6.901177 15.302521l-0.083911 0.091074c13.94971 14.024411 21.810748 31.154557 21.810748 45.300742 0 64.787562-155.813718 136.966465-379.419426 136.966465-223.595474 0-379.410216-72.179926-379.410216-136.966465 0-16.139585 4.53734-29.952172 22.321378-45.670156 0.213871-0.202615 0.429789-0.381693 0.635473-0.594541 0.137123-0.118704 0.240477-0.233314 0.378623-0.354064l-0.084934-0.080841c3.416819-3.719718 5.623068-8.588609 5.623068-14.037714 0-11.542894-9.356089-20.898982-20.897959-20.898982-5.770424 0-10.993378 2.340301-14.773472 6.119371l-0.122797-0.118704c-23.410176 22.797215-36.594453 48.278563-36.594453 75.635631 0 102.884228 181.821045 180.482561 422.926312 180.482561 241.114476 0 422.935522-77.598332 422.935522-180.482561 0-27.166733-13.125949-52.452629-36.235272-75.127048C895.851365 629.847012 895.730615 629.681236 895.577119 629.529787z" fill="#000000" p-id="10086"></path></svg> | ||||
| After Width: | Height: | Size: 2.8 KiB | 
|  | @ -0,0 +1 @@ | |||
| <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693028440322" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="25843" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 750.509317c-19.080745 0-31.801242-12.720497-31.801242-31.801242L480.198758 432.496894c0-19.080745 12.720497-31.801242 31.801242-31.801242s31.801242 12.720497 31.801242 31.801242l0 286.21118C537.440994 737.78882 524.720497 750.509317 512 750.509317z" fill="#000000" p-id="25844"></path><path d="M651.925466 534.26087 365.714286 534.26087c-19.080745 0-31.801242-12.720497-31.801242-31.801242 0-19.080745 12.720497-31.801242 31.801242-31.801242l286.21118 0c19.080745 0 31.801242 12.720497 31.801242 31.801242C683.726708 521.540373 671.006211 534.26087 651.925466 534.26087z" fill="#000000" p-id="25845"></path><path d="M651.925466 648.745342 365.714286 648.745342c-19.080745 0-31.801242-12.720497-31.801242-31.801242 0-19.080745 12.720497-31.801242 31.801242-31.801242l286.21118 0c19.080745 0 31.801242 12.720497 31.801242 31.801242C683.726708 636.024845 671.006211 648.745342 651.925466 648.745342z" fill="#000000" p-id="25846"></path><path d="M512 464.298137c-6.360248 0-19.080745 0-25.440994-6.360248L352.993789 324.372671c-12.720497-12.720497-12.720497-31.801242 0-44.521739 12.720497-12.720497 31.801242-12.720497 44.521739 0l133.565217 133.565217c12.720497 12.720497 12.720497 31.801242 0 44.521739C524.720497 464.298137 518.360248 464.298137 512 464.298137z" fill="#000000" p-id="25847"></path><path d="M512 464.298137c-6.360248 0-19.080745 0-25.440994-6.360248-12.720497-12.720497-12.720497-31.801242 0-44.521739l133.565217-133.565217c12.720497-12.720497 31.801242-12.720497 44.521739 0 12.720497 12.720497 12.720497 31.801242 0 44.521739L531.080745 457.937888C524.720497 464.298137 518.360248 464.298137 512 464.298137z" fill="#000000" p-id="25848"></path><path d="M512 1017.639752c-279.850932 0-508.819876-228.968944-508.819876-508.819876s228.968944-508.819876 508.819876-508.819876 508.819876 228.968944 508.819876 508.819876c0 25.440994 0 50.881988-6.360248 82.68323 0 19.080745-19.080745 31.801242-38.161491 25.440994-19.080745 0-31.801242-19.080745-25.440994-38.161491 6.360248-25.440994 6.360248-44.521739 6.360248-69.962733 0-248.049689-197.167702-445.217391-445.217391-445.217391S66.782609 267.130435 66.782609 515.180124s197.167702 445.217391 445.217391 445.217391c25.440994 0 57.242236 0 82.68323-6.360248 19.080745-6.360248 31.801242 6.360248 38.161491 25.440994 6.360248 19.080745-6.360248 31.801242-25.440994 38.161491C575.602484 1017.639752 543.801242 1017.639752 512 1017.639752z" fill="#000000" p-id="25849"></path><path d="M989.018634 864.993789l-318.012422 0c-19.080745 0-31.801242-12.720497-31.801242-31.801242s12.720497-31.801242 31.801242-31.801242l318.012422 0c19.080745 0 31.801242 12.720497 31.801242 31.801242S1001.73913 864.993789 989.018634 864.993789z" fill="#000000" p-id="25850"></path><path d="M830.012422 1024c-19.080745 0-31.801242-12.720497-31.801242-31.801242l0-318.012422c0-19.080745 12.720497-31.801242 31.801242-31.801242s31.801242 12.720497 31.801242 31.801242l0 318.012422C861.813665 1004.919255 842.732919 1024 830.012422 1024z" fill="#000000" p-id="25851"></path></svg> | ||||
| After Width: | Height: | Size: 3.3 KiB | 
|  | @ -0,0 +1,3 @@ | |||
| import CardTitle from './src/CardTitle.vue' | ||||
| 
 | ||||
| export { CardTitle } | ||||
|  | @ -1,8 +1,8 @@ | |||
| <script lang="ts" setup> | ||||
| import { defineComponent } from 'vue' | ||||
| defineComponent({ | ||||
|   name: 'CardTitle' | ||||
| }) | ||||
| 
 | ||||
| const { title } = defineProps({ | ||||
|   title: { | ||||
|     type: String, | ||||
|  | @ -1,3 +1,4 @@ | |||
| import Descriptions from './src/Descriptions.vue' | ||||
| import DescriptionsItemLabel from './src/DescriptionsItemLabel.vue' | ||||
| 
 | ||||
| export { Descriptions } | ||||
| export { Descriptions, DescriptionsItemLabel } | ||||
|  |  | |||
|  | @ -0,0 +1,28 @@ | |||
| <script setup lang="ts"> | ||||
| const { label } = defineProps({ | ||||
|   label: { | ||||
|     type: String, | ||||
|     required: true | ||||
|   }, | ||||
|   icon: { | ||||
|     type: String, | ||||
|     required: false | ||||
|   } | ||||
| }) | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div class="cell-item"> | ||||
|     <Icon :icon="icon" v-if="icon" style="vertical-align: middle" :size="18" /> | ||||
|     {{ label }} | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped lang="scss"> | ||||
| .cell-item { | ||||
|   display: inline; | ||||
| } | ||||
| .cell-item::after { | ||||
|   content: ':'; | ||||
| } | ||||
| </style> | ||||
|  | @ -5,32 +5,32 @@ const { t } = useI18n() | |||
|  * redirect: noredirect        当设置 noredirect 的时候该路由在面包屑导航中不可被点击 | ||||
|  * name:'router-name'          设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题 | ||||
|  * meta : { | ||||
|     hidden: true              当设置 true 的时候该路由不会再侧边栏出现 如404,login等页面(默认 false) | ||||
|  hidden: true              当设置 true 的时候该路由不会再侧边栏出现 如404,login等页面(默认 false) | ||||
| 
 | ||||
|     alwaysShow: true          当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式, | ||||
|                               只有一个时,会将那个子路由当做根路由显示在侧边栏, | ||||
|                               若你想不管路由下面的 children 声明的个数都显示你的根路由, | ||||
|                               你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则, | ||||
|                               一直显示根路由(默认 false) | ||||
|  alwaysShow: true          当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式, | ||||
|  只有一个时,会将那个子路由当做根路由显示在侧边栏, | ||||
|  若你想不管路由下面的 children 声明的个数都显示你的根路由, | ||||
|  你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则, | ||||
|  一直显示根路由(默认 false) | ||||
| 
 | ||||
|     title: 'title'            设置该路由在侧边栏和面包屑中展示的名字 | ||||
|  title: 'title'            设置该路由在侧边栏和面包屑中展示的名字 | ||||
| 
 | ||||
|     icon: 'svg-name'          设置该路由的图标 | ||||
|  icon: 'svg-name'          设置该路由的图标 | ||||
| 
 | ||||
|     noCache: true             如果设置为true,则不会被 <keep-alive> 缓存(默认 false) | ||||
|  noCache: true             如果设置为true,则不会被 <keep-alive> 缓存(默认 false) | ||||
| 
 | ||||
|     breadcrumb: false         如果设置为false,则不会在breadcrumb面包屑中显示(默认 true) | ||||
|  breadcrumb: false         如果设置为false,则不会在breadcrumb面包屑中显示(默认 true) | ||||
| 
 | ||||
|     affix: true               如果设置为true,则会一直固定在tag项中(默认 false) | ||||
|  affix: true               如果设置为true,则会一直固定在tag项中(默认 false) | ||||
| 
 | ||||
|     noTagsView: true          如果设置为true,则不会出现在tag中(默认 false) | ||||
|  noTagsView: true          如果设置为true,则不会出现在tag中(默认 false) | ||||
| 
 | ||||
|     activeMenu: '/dashboard'  显示高亮的路由路径 | ||||
|  activeMenu: '/dashboard'  显示高亮的路由路径 | ||||
| 
 | ||||
|     followAuth: '/dashboard'  跟随哪个路由进行权限过滤 | ||||
|  followAuth: '/dashboard'  跟随哪个路由进行权限过滤 | ||||
| 
 | ||||
|     canTo: true               设置为true即使hidden为true,也依然可以进行路由跳转(默认 false) | ||||
|   } | ||||
|  canTo: true               设置为true即使hidden为true,也依然可以进行路由跳转(默认 false) | ||||
|  } | ||||
|  **/ | ||||
| const remainingRouter: AppRouteRecordRaw[] = [ | ||||
|   { | ||||
|  | @ -346,22 +346,6 @@ const remainingRouter: AppRouteRecordRaw[] = [ | |||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     path: '/property', | ||||
|     component: Layout, | ||||
|     name: 'property', | ||||
|     meta: { | ||||
|       hidden: true | ||||
|     }, | ||||
|     children: [ | ||||
|       { | ||||
|         path: 'value/:propertyId(\\d+)', | ||||
|         component: () => import('@/views/mall/product/property/value/index.vue'), | ||||
|         name: 'ProductPropertyValue', | ||||
|         meta: { title: '商品属性值', icon: '', activeMenu: '/product/property' } | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     path: '/product', | ||||
|     component: Layout, | ||||
|  | @ -408,6 +392,53 @@ const remainingRouter: AppRouteRecordRaw[] = [ | |||
|           title: '商品详情', | ||||
|           activeMenu: '/product/product-spu' | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         path: 'property/value/:propertyId(\\d+)', | ||||
|         component: () => import('@/views/mall/product/property/value/index.vue'), | ||||
|         name: 'ProductPropertyValue', | ||||
|         meta: { | ||||
|           noCache: true, | ||||
|           hidden: true, | ||||
|           canTo: true, | ||||
|           icon: 'ep:view', | ||||
|           title: '商品属性值', | ||||
|           activeMenu: '/product/property' | ||||
|         } | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     path: '/trade/order', | ||||
|     component: Layout, | ||||
|     name: 'Detail', | ||||
|     meta: { | ||||
|       hidden: true | ||||
|     }, | ||||
|     children: [ | ||||
|       { | ||||
|         path: 'detail/:orderId(\\d+)', | ||||
|         component: () => import('@/views/mall/trade/order/detail/index.vue'), | ||||
|         name: 'TradeOrderDetailForm', | ||||
|         meta: { title: '订单详情', icon: '', activeMenu: '/trade/trade/order' } | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     path: '/member', | ||||
|     component: Layout, | ||||
|     name: 'member', | ||||
|     meta: { hidden: true }, | ||||
|     children: [ | ||||
|       { | ||||
|         path: 'user/detail/:id', | ||||
|         name: 'MemberUserDetail', | ||||
|         meta: { | ||||
|           title: '会员详情', | ||||
|           noCache: true, | ||||
|           hidden: true | ||||
|         }, | ||||
|         component: () => import('@/views/member/user/detail/index.vue') | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|  |  | |||
|  | @ -90,6 +90,7 @@ export enum DICT_TYPE { | |||
|   USER_TYPE = 'user_type', | ||||
|   COMMON_STATUS = 'common_status', | ||||
|   SYSTEM_TENANT_PACKAGE_ID = 'system_tenant_package_id', | ||||
|   TERMINAL = 'terminal', // 终端
 | ||||
| 
 | ||||
|   // ========== SYSTEM 模块 ==========
 | ||||
|   SYSTEM_USER_SEX = 'system_user_sex', | ||||
|  | @ -130,7 +131,7 @@ export enum DICT_TYPE { | |||
|   BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type', | ||||
| 
 | ||||
|   // ========== PAY 模块 ==========
 | ||||
|   PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型
 | ||||
|   PAY_CHANNEL_CODE = 'pay_channel_code_type', // 支付渠道编码类型
 | ||||
|   PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态
 | ||||
|   PAY_REFUND_STATUS = 'pay_refund_status', // 退款订单状态
 | ||||
|   PAY_NOTIFY_STATUS = 'pay_notify_status', // 商户支付回调状态
 | ||||
|  | @ -155,7 +156,7 @@ export enum DICT_TYPE { | |||
|   TRADE_ORDER_TYPE = 'trade_order_type', // 订单 - 类型
 | ||||
|   TRADE_ORDER_STATUS = 'trade_order_status', // 订单 - 状态
 | ||||
|   TRADE_ORDER_ITEM_AFTER_SALE_STATUS = 'trade_order_item_after_sale_status', // 订单项 - 售后状态
 | ||||
|   TERMINAL = 'terminal', // 终端
 | ||||
|   TRADE_DELIVERY_TYPE = 'trade_delivery_type', // 配送方式
 | ||||
| 
 | ||||
|   // ========== MALL - 营销模块 ==========
 | ||||
|   PROMOTION_DISCOUNT_TYPE = 'promotion_discount_type', // 优惠类型
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ import dayjs from 'dayjs' | |||
|  * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ" | ||||
|  * @returns 返回拼接后的时间字符串 | ||||
|  */ | ||||
| export function formatDate(date: Date, format?: string): string { | ||||
| export function formatDate(date: Date | number, format?: string): string { | ||||
|   // 日期不存在,则返回空
 | ||||
|   if (!date) { | ||||
|     return '' | ||||
|  |  | |||
|  | @ -174,7 +174,6 @@ export const copyValueToTarget = (target, source) => { | |||
|   Object.assign(target, newObj) | ||||
| } | ||||
| 
 | ||||
| // TODO @puhui999:返回要带上 .00 哈.例如说 1.00
 | ||||
| /** | ||||
|  * 将一个整数转换为分数保留两位小数 | ||||
|  * @param num | ||||
|  | @ -185,6 +184,31 @@ export const formatToFraction = (num: number | string | undefined): number => { | |||
|   return parseFloat((parsedNumber / 100).toFixed(2)) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 将一个数转换为 1.00 这样 | ||||
|  * 数据呈现的时候使用 | ||||
|  * | ||||
|  * @param num 整数 | ||||
|  */ | ||||
| export const floatToFixed2 = (num: number | string | undefined): string => { | ||||
|   let str = '0.00' | ||||
|   if (typeof num === 'undefined') { | ||||
|     return str | ||||
|   } | ||||
|   const f = formatToFraction(num) | ||||
|   const decimalPart = f.toString().split('.')[1] | ||||
|   const len = decimalPart ? decimalPart.length : 0 | ||||
|   switch (len) { | ||||
|     case 0: | ||||
|       str = f.toString() + '.00' | ||||
|       break | ||||
|     case 1: | ||||
|       str = f.toString() + '0' | ||||
|       break | ||||
|   } | ||||
|   return str | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 将一个分数转换为整数 | ||||
|  * @param num | ||||
|  |  | |||
|  | @ -21,8 +21,8 @@ | |||
|             v-model="loginData.loginForm.tenantName" | ||||
|             :placeholder="t('login.tenantNamePlaceholder')" | ||||
|             :prefix-icon="iconHouse" | ||||
|             type="primary" | ||||
|             link | ||||
|             type="primary" | ||||
|           /> | ||||
|         </el-form-item> | ||||
|       </el-col> | ||||
|  |  | |||
|  | @ -45,9 +45,9 @@ | |||
|             <el-button class="float-right" text type="primary" @click="copy(item.code)"> | ||||
|               {{ t('common.copy') }} | ||||
|             </el-button> | ||||
|             <div> | ||||
|             <el-scrollbar height="600px"> | ||||
|               <pre><code v-dompurify-html="highlightedCode(item)" class="hljs"></code></pre> | ||||
|             </div> | ||||
|             </el-scrollbar> | ||||
|           </el-tab-pane> | ||||
|         </el-tabs> | ||||
|       </el-card> | ||||
|  |  | |||
|  | @ -0,0 +1,192 @@ | |||
| <template> | ||||
|   <Dialog title="添加虚拟评论" v-model="dialogVisible"> | ||||
|     <el-form | ||||
|       ref="formRef" | ||||
|       :model="formData" | ||||
|       :rules="formRules" | ||||
|       label-width="100px" | ||||
|       v-loading="formLoading" | ||||
|     > | ||||
|       <el-form-item label="商品" prop="spuId"> | ||||
|         <div @click="handleSelectSpu" class="w-60px h-60px"> | ||||
|           <div v-if="spuData && spuData.picUrl"> | ||||
|             <el-image :src="spuData.picUrl" /> | ||||
|           </div> | ||||
|           <div v-else class="select-box"> | ||||
|             <Icon icon="ep:plus" /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="商品规格" prop="skuId" v-if="formData.spuId"> | ||||
|         <div @click="handleSelectSku" class="w-60px h-60px"> | ||||
|           <div v-if="skuData && skuData.picUrl"> | ||||
|             <el-image :src="skuData.picUrl" /> | ||||
|           </div> | ||||
|           <div v-else class="select-box"> | ||||
|             <Icon icon="ep:plus" /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="用户头像" prop="userAvatar"> | ||||
|         <UploadImg v-model="formData.userAvatar" height="60px" width="60px" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="用户名称" prop="userNickname"> | ||||
|         <el-input v-model="formData.userNickname" placeholder="请输入用户名称" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="评论内容" prop="content"> | ||||
|         <el-input type="textarea" v-model="formData.content" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="描述星级" prop="descriptionScores"> | ||||
|         <el-rate v-model="formData.descriptionScores" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="服务星级" prop="benefitScores"> | ||||
|         <el-rate v-model="formData.benefitScores" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="评论图片" prop="picUrls"> | ||||
|         <UploadImgs v-model="formData.picUrls" :limit="9" height="60px" width="60px" /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|     <template #footer> | ||||
|       <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
|   <SpuTableSelect ref="spuTableSelectRef" @change="handleSpuChange" /> | ||||
|   <SkuTableSelect ref="skuTableSelectRef" @change="handleSkuChange" :spu-id="spuData.id" /> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import * as CommentApi from '@/api/mall/product/comment' | ||||
| import SpuTableSelect from '@/views/mall/product/spu/components/SpuTableSelect.vue' | ||||
| import * as ProductSpuApi from '@/api/mall/product/spu' | ||||
| import SkuTableSelect from '@/views/mall/product/spu/components/SkuTableSelect.vue' | ||||
| 
 | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const dialogTitle = ref('') // 弹窗的标题 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formType = ref('') // 表单的类型:create - 新增;update - 修改 | ||||
| const formData = ref({ | ||||
|   id: undefined, | ||||
|   userId: undefined, | ||||
|   userNickname: undefined, | ||||
|   userAvatar: undefined, | ||||
|   spuId: undefined, | ||||
|   spuName: undefined, | ||||
|   skuId: undefined, | ||||
|   descriptionScores: 5, | ||||
|   benefitScores: 5, | ||||
|   content: undefined, | ||||
|   picUrls: [] | ||||
| }) | ||||
| const formRules = reactive({ | ||||
|   spuId: [{ required: true, message: '商品不能为空', trigger: 'blur' }], | ||||
|   skuId: [{ required: true, message: '规格不能为空', trigger: 'blur' }], | ||||
|   userAvatar: [{ required: true, message: '用户头像不能为空', trigger: 'blur' }], | ||||
|   userNickname: [{ required: true, message: '用户名称不能为空', trigger: 'blur' }], | ||||
|   content: [{ required: true, message: '评论内容不能为空', trigger: 'blur' }], | ||||
|   descriptionScores: [{ required: true, message: '描述星级不能为空', trigger: 'blur' }], | ||||
|   benefitScores: [{ required: true, message: '服务星级不能为空', trigger: 'blur' }] | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| const spuData = ref<ProductSpuApi.Spu>({}) | ||||
| const skuData = ref({ | ||||
|   id: -1, | ||||
|   name: '', | ||||
|   picUrl: '' | ||||
| }) | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (type: string, id?: number) => { | ||||
|   dialogVisible.value = true | ||||
|   dialogTitle.value = t('action.' + type) | ||||
|   formType.value = type | ||||
|   resetForm() | ||||
|   // 修改时,设置数据 | ||||
|   if (id) { | ||||
|     formLoading.value = true | ||||
|     try { | ||||
|       formData.value = await CommentApi.getComment(id) | ||||
|     } finally { | ||||
|       formLoading.value = false | ||||
|     } | ||||
|   } | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 提交表单 */ | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitForm = async () => { | ||||
|   // 校验表单 | ||||
|   if (!formRef) return | ||||
|   const valid = await formRef.value.validate() | ||||
|   if (!valid) return | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     //处理评论图片 | ||||
|     const picUrls = formData.value.picUrls.map((item) => { | ||||
|       return item?.url ? item.url : item | ||||
|     }) | ||||
|     const data = { ...formData.value, picUrls } | ||||
|     if (formType.value === 'create') { | ||||
|       await CommentApi.createComment(data) | ||||
|       message.success(t('common.createSuccess')) | ||||
|     } | ||||
|     dialogVisible.value = false | ||||
|     // 发送操作成功的事件 | ||||
|     emit('success') | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
|     id: undefined, | ||||
|     userId: undefined, | ||||
|     userNickname: undefined, | ||||
|     userAvatar: undefined, | ||||
|     spuId: undefined, | ||||
|     skuId: undefined, | ||||
|     descriptionScores: 5, | ||||
|     benefitScores: 5, | ||||
|     content: undefined, | ||||
|     picUrls: [] | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
| 
 | ||||
| /** SPU 表格选择 */ | ||||
| const spuTableSelectRef = ref() | ||||
| const handleSelectSpu = () => { | ||||
|   spuTableSelectRef.value.open() | ||||
| } | ||||
| const handleSpuChange = (spu: ProductSpuApi.Spu) => { | ||||
|   spuData.value = spu | ||||
|   formData.value.spuId = spu.id | ||||
| } | ||||
| 
 | ||||
| /** SKU 表格选择 */ | ||||
| const skuTableSelectRef = ref() | ||||
| const handleSelectSku = () => { | ||||
|   skuTableSelectRef.value.open() | ||||
| } | ||||
| const handleSkuChange = (sku: ProductSpuApi.Sku) => { | ||||
|   skuData.value = sku | ||||
|   formData.value.skuId = sku.id | ||||
| } | ||||
| </script> | ||||
| <style> | ||||
| .select-box { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   border: 1px dashed var(--el-border-color-darker); | ||||
|   border-radius: 8px; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
| } | ||||
| </style> | ||||
|  | @ -0,0 +1,76 @@ | |||
| <template> | ||||
|   <Dialog title="回复" v-model="dialogVisible"> | ||||
|     <el-form | ||||
|       ref="formRef" | ||||
|       :model="formData" | ||||
|       :rules="formRules" | ||||
|       label-width="100px" | ||||
|       v-loading="formLoading" | ||||
|     > | ||||
|       <el-form-item label="回复内容" prop="replyContent"> | ||||
|         <el-input type="textarea" v-model="formData.replyContent" /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|     <template #footer> | ||||
|       <el-button @click="submitReplyForm" type="primary" :disabled="formLoading">确 定 </el-button> | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import * as CommentApi from '@/api/mall/product/comment' | ||||
| import { ElInput } from 'element-plus' | ||||
| 
 | ||||
| defineOptions({ name: 'ProductComment' }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { t } = useI18n() // 国际化 | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formData = ref({ | ||||
|   id: undefined, | ||||
|   replyContent: undefined | ||||
| }) | ||||
| const formRules = reactive({ | ||||
|   replyContent: [{ required: true, message: '回复内容不能为空', trigger: 'blur' }] | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (id?: number) => { | ||||
|   resetForm() | ||||
|   formData.value.id = id | ||||
|   dialogVisible.value = true | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 提交表单 */ | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitReplyForm = async () => { | ||||
|   // 校验表单 | ||||
|   const valid = await formRef?.value?.validate() | ||||
|   if (!valid) return | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     await CommentApi.replyComment(formData.value) | ||||
|     message.success(t('common.createSuccess')) | ||||
|     dialogVisible.value = false | ||||
|     // 发送操作成功的事件 | ||||
|     emit('success') | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
|     id: undefined, | ||||
|     replyContent: undefined | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
| </script> | ||||
|  | @ -0,0 +1,243 @@ | |||
| <template> | ||||
|   <ContentWrap> | ||||
|     <!-- 搜索工作栏 --> | ||||
|     <el-form | ||||
|       class="-mb-15px" | ||||
|       :model="queryParams" | ||||
|       ref="queryFormRef" | ||||
|       :inline="true" | ||||
|       label-width="68px" | ||||
|     > | ||||
|       <el-form-item label="回复状态" prop="replyStatus"> | ||||
|         <el-select v-model="queryParams.replyStatus"> | ||||
|           <el-option label="已回复" :value="true" /> | ||||
|           <el-option label="未回复" :value="false" /> | ||||
|         </el-select> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="商品名称" prop="spuName"> | ||||
|         <el-input v-model="queryParams.spuName" placeholder="请输入商品名称" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="用户名称" prop="userNickname"> | ||||
|         <el-input v-model="queryParams.userNickname" placeholder="请输入用户名称" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="订单编号" prop="orderId"> | ||||
|         <el-input v-model="queryParams.orderId" placeholder="请输入订单编号" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="评论时间" prop="createTime"> | ||||
|         <el-date-picker | ||||
|           v-model="queryParams.createTime" | ||||
|           value-format="YYYY-MM-DD HH:mm:ss" | ||||
|           type="daterange" | ||||
|           start-placeholder="开始日期" | ||||
|           end-placeholder="结束日期" | ||||
|           :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" | ||||
|           class="!w-240px" | ||||
|         /> | ||||
|       </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 | ||||
|           type="primary" | ||||
|           plain | ||||
|           @click="openForm('create')" | ||||
|           v-hasPermi="['product:comment:create']" | ||||
|         > | ||||
|           <Icon icon="ep:plus" class="mr-5px" /> | ||||
|           添加虚拟评论 | ||||
|         </el-button> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|   </ContentWrap> | ||||
| 
 | ||||
|   <!-- 列表 --> | ||||
|   <ContentWrap> | ||||
|     <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="false"> | ||||
|       <el-table-column label="评论编号" align="center" prop="id" min-width="60" /> | ||||
|       <el-table-column label="用户名称" align="center" prop="userNickname" width="80" /> | ||||
|       <el-table-column label="商品信息" align="center" min-width="300"> | ||||
|         <template #default="scope"> | ||||
|           <div class="flex row items-center gap-x-4px"> | ||||
|             <el-image | ||||
|               v-if="scope.row.skuPicUrl" | ||||
|               :src="scope.row.skuPicUrl" | ||||
|               :preview-src-list="[scope.row.skuPicUrl]" | ||||
|               class="w-40px h-40px shrink-0" | ||||
|               preview-teleported | ||||
|             /> | ||||
|             <div>{{ scope.row.spuName }}</div> | ||||
|             <el-tag | ||||
|               v-for="property in scope.row.skuProperties" | ||||
|               :key="property.propertyId" | ||||
|               class="mr-10px" | ||||
|             > | ||||
|               {{ property.propertyName }}: {{ property.valueName }} | ||||
|             </el-tag> | ||||
|           </div> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="评分星级" align="center" prop="scores" width="80" /> | ||||
|       <el-table-column label="描述星级" align="center" prop="descriptionScores" width="80" /> | ||||
|       <el-table-column label="服务星级" align="center" prop="benefitScores" width="80" /> | ||||
|       <el-table-column label="评论内容" align="center" prop="content" min-width="80"> | ||||
|         <template #default="scope"> | ||||
|           <p>{{ scope.row.content }}</p> | ||||
|           <div class="flex justify-center gap-x-4px"> | ||||
|             <el-image | ||||
|               v-for="(picUrl, index) in scope.row.picUrls" | ||||
|               :key="index" | ||||
|               :src="picUrl" | ||||
|               :preview-src-list="scope.row.picUrls" | ||||
|               :initial-index="index" | ||||
|               class="w-40px h-40px" | ||||
|               preview-teleported | ||||
|             /> | ||||
|           </div> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column | ||||
|         label="回复内容" | ||||
|         align="center" | ||||
|         prop="replyContent" | ||||
|         min-width="100" | ||||
|         show-overflow-tooltip | ||||
|       /> | ||||
|       <el-table-column | ||||
|         label="评论时间" | ||||
|         align="center" | ||||
|         prop="createTime" | ||||
|         :formatter="dateFormatter" | ||||
|         width="170" | ||||
|       /> | ||||
|       <el-table-column label="状态" align="center" width="65px"> | ||||
|         <template #default="scope"> | ||||
|           <el-switch | ||||
|             v-model="scope.row.visible" | ||||
|             :active-value="true" | ||||
|             :inactive-value="false" | ||||
|             v-hasPermi="['product:comment:update']" | ||||
|             @change="handleVisibleChange(scope.row)" | ||||
|           /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="操作" align="center" min-width="60px" fixed="right"> | ||||
|         <template #default="scope"> | ||||
|           <el-button | ||||
|             link | ||||
|             type="primary" | ||||
|             @click="handleReply(scope.row.id)" | ||||
|             v-hasPermi="['product:comment:update']" | ||||
|           > | ||||
|             回复 | ||||
|           </el-button> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|     <!-- 分页 --> | ||||
|     <Pagination | ||||
|       :total="total" | ||||
|       v-model:page="queryParams.pageNo" | ||||
|       v-model:limit="queryParams.pageSize" | ||||
|       @pagination="getList" | ||||
|     /> | ||||
|   </ContentWrap> | ||||
| 
 | ||||
|   <!-- 表单弹窗:添加/修改 --> | ||||
|   <CommentForm ref="formRef" @success="getList" /> | ||||
|   <!-- 回复表单弹窗 --> | ||||
|   <ReplyForm ref="replyFormRef" @success="getList" /> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import * as CommentApi from '@/api/mall/product/comment' | ||||
| import CommentForm from './CommentForm.vue' | ||||
| import ReplyForm from './ReplyForm.vue' | ||||
| 
 | ||||
| defineOptions({ name: 'ProductComment' }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { t } = useI18n() // 国际化 | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref([]) // 列表的数据 | ||||
| const queryParams = reactive({ | ||||
|   pageNo: 1, | ||||
|   pageSize: 10, | ||||
|   replyStatus: null, | ||||
|   spuName: null, | ||||
|   userNickname: null, | ||||
|   orderId: null, | ||||
|   createTime: [] | ||||
| }) | ||||
| const queryFormRef = ref() // 搜索的表单 | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     const data = await CommentApi.getCommentPage(queryParams) | ||||
|     // visible 如果为 null,会导致刷新的时候触发 e-switch 的 change 事件 | ||||
|     data.list.forEach((item) => { | ||||
|       if (!item.visible) { | ||||
|         item.visible = false | ||||
|       } | ||||
|     }) | ||||
|     list.value = data.list | ||||
|     total.value = data.total | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.pageNo = 1 | ||||
|   getList() | ||||
| } | ||||
| 
 | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value.resetFields() | ||||
|   handleQuery() | ||||
| } | ||||
| 
 | ||||
| /** 添加/修改操作 */ | ||||
| const formRef = ref() | ||||
| const openForm = (type: string, id?: number) => { | ||||
|   formRef.value.open(type, id) | ||||
| } | ||||
| 
 | ||||
| /** 回复按钮操作 **/ | ||||
| const replyFormRef = ref() | ||||
| const handleReply = (id: number) => { | ||||
|   replyFormRef.value.open(id) | ||||
| } | ||||
| 
 | ||||
| /** 显示/隐藏 **/ | ||||
| const handleVisibleChange = async (row: CommentApi.CommentVO) => { | ||||
|   if (loading.value) { | ||||
|     return | ||||
|   } | ||||
|   let changedValue = row.visible | ||||
|   try { | ||||
|     await message.confirm(changedValue ? '是否显示评论?' : '是否隐藏评论?') | ||||
|     await CommentApi.updateCommentVisible({ id: row.id, visible: changedValue }) | ||||
|     await getList() | ||||
|   } catch { | ||||
|     row.visible = !changedValue | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(() => { | ||||
|   getList() | ||||
| }) | ||||
| </script> | ||||
|  | @ -73,9 +73,7 @@ | |||
|           > | ||||
|             编辑 | ||||
|           </el-button> | ||||
|           <el-button link type="primary"> | ||||
|             <router-link :to="'/property/value/' + scope.row.id">属性值</router-link> | ||||
|           </el-button> | ||||
|           <el-button link type="primary" @click="goValueList(scope.row.id)">属性值</el-button> | ||||
|           <el-button | ||||
|             v-hasPermi="['product:property:delete']" | ||||
|             link | ||||
|  | @ -104,6 +102,8 @@ import { dateFormatter } from '@/utils/formatTime' | |||
| import * as PropertyApi from '@/api/mall/product/property' | ||||
| import PropertyForm from './PropertyForm.vue' | ||||
| 
 | ||||
| const { push } = useRouter() | ||||
| 
 | ||||
| defineOptions({ name: 'ProductProperty' }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
|  | @ -163,6 +163,11 @@ const handleDelete = async (id: number) => { | |||
|   } catch {} | ||||
| } | ||||
| 
 | ||||
| /** 跳转商品属性列表 */ | ||||
| const goValueList = (id: number) => { | ||||
|   push({ path: '/product/property/value/' + id }) | ||||
| } | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(() => { | ||||
|   getList() | ||||
|  |  | |||
|  | @ -42,7 +42,9 @@ import { useTagsViewStore } from '@/store/modules/tagsView' | |||
| import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components' | ||||
| // 业务api | ||||
| import * as ProductSpuApi from '@/api/mall/product/spu' | ||||
| import { convertToInteger, formatToFraction } from '@/utils' | ||||
| import { convertToInteger, floatToFixed2, formatToFraction } from '@/utils' | ||||
| 
 | ||||
| // TODO @芋艿:后续稍微调整下; | ||||
| 
 | ||||
| defineOptions({ name: 'ProductSpuForm' }) | ||||
| 
 | ||||
|  | @ -107,12 +109,20 @@ const getDetail = async () => { | |||
|     try { | ||||
|       const res = (await ProductSpuApi.getSpu(id)) as ProductSpuApi.Spu | ||||
|       res.skus?.forEach((item) => { | ||||
|         // 回显价格分转元 | ||||
|         item.price = formatToFraction(item.price) | ||||
|         item.marketPrice = formatToFraction(item.marketPrice) | ||||
|         item.costPrice = formatToFraction(item.costPrice) | ||||
|         item.subCommissionFirstPrice = formatToFraction(item.subCommissionFirstPrice) | ||||
|         item.subCommissionSecondPrice = formatToFraction(item.subCommissionSecondPrice) | ||||
|         if (isDetail.value === true) { | ||||
|           item.price = floatToFixed2(item.price) | ||||
|           item.marketPrice = floatToFixed2(item.marketPrice) | ||||
|           item.costPrice = floatToFixed2(item.costPrice) | ||||
|           item.subCommissionFirstPrice = floatToFixed2(item.subCommissionFirstPrice) | ||||
|           item.subCommissionSecondPrice = floatToFixed2(item.subCommissionSecondPrice) | ||||
|         } else { | ||||
|           // 回显价格分转元 | ||||
|           item.price = formatToFraction(item.price) | ||||
|           item.marketPrice = formatToFraction(item.marketPrice) | ||||
|           item.costPrice = formatToFraction(item.costPrice) | ||||
|           item.subCommissionFirstPrice = formatToFraction(item.subCommissionFirstPrice) | ||||
|           item.subCommissionSecondPrice = formatToFraction(item.subCommissionSecondPrice) | ||||
|         } | ||||
|       }) | ||||
|       formData.value = res | ||||
|     } finally { | ||||
|  | @ -132,19 +142,19 @@ const submitForm = async () => { | |||
|     await unref(descriptionRef)?.validate() | ||||
|     await unref(otherSettingsRef)?.validate() | ||||
|     // 深拷贝一份, 这样最终 server 端不满足,不需要恢复, | ||||
|     const deepCopyFormData = cloneDeep(unref(formData.value)) | ||||
|     const deepCopyFormData = cloneDeep(unref(formData.value)) as ProductSpuApi.Spu | ||||
|     // 兜底处理 sku 空数据 | ||||
|     formData.value.skus!.forEach((sku) => { | ||||
|       // 因为是空数据这里判断一下商品条码是否为空就行 | ||||
|       if (sku.barCode === '') { | ||||
|         const index = deepCopyFormData.skus.findIndex( | ||||
|         const index = deepCopyFormData.skus!.findIndex( | ||||
|           (item) => JSON.stringify(item.properties) === JSON.stringify(sku.properties) | ||||
|         ) | ||||
|         // 删除这条 sku | ||||
|         deepCopyFormData.skus.splice(index, 1) | ||||
|         deepCopyFormData.skus!.splice(index, 1) | ||||
|       } | ||||
|     }) | ||||
|     deepCopyFormData.skus.forEach((item) => { | ||||
|     deepCopyFormData.skus!.forEach((item) => { | ||||
|       // 给sku name赋值 | ||||
|       item.name = deepCopyFormData.name | ||||
|       // sku相关价格元转分 | ||||
|  | @ -156,7 +166,7 @@ const submitForm = async () => { | |||
|     }) | ||||
|     // 处理轮播图列表 | ||||
|     const newSliderPicUrls: any[] = [] | ||||
|     deepCopyFormData.sliderPicUrls.forEach((item: any) => { | ||||
|     deepCopyFormData.sliderPicUrls!.forEach((item: any) => { | ||||
|       // 如果是前端选的图 | ||||
|       typeof item === 'object' ? newSliderPicUrls.push(item.url) : newSliderPicUrls.push(item) | ||||
|     }) | ||||
|  |  | |||
|  | @ -334,7 +334,7 @@ const validateSku = () => { | |||
|   for (const sku of formData.value!.skus!) { | ||||
|     // 作为活动组件的校验 | ||||
|     if (props.isActivityComponent) { | ||||
|       for (const rule of props.ruleConfig) { | ||||
|       for (const rule of props?.ruleConfig) { | ||||
|         const arg = getValue(sku, rule.name) | ||||
|         if (!rule.rule(arg)) { | ||||
|           validate = false // 只要有一个不通过则直接不通过 | ||||
|  | @ -534,9 +534,10 @@ watch( | |||
|   } | ||||
| ) | ||||
| const activitySkuListRef = ref<InstanceType<typeof ElTable>>() | ||||
| const clearSelection = () => { | ||||
|   activitySkuListRef.value.clearSelection() | ||||
| 
 | ||||
| const getSkuTableRef = () => { | ||||
|   return activitySkuListRef.value | ||||
| } | ||||
| // 暴露出生成 sku 方法,给添加属性成功时调用 | ||||
| defineExpose({ generateTableData, validateSku, clearSelection }) | ||||
| defineExpose({ generateTableData, validateSku, getSkuTableRef }) | ||||
| </script> | ||||
|  |  | |||
|  | @ -0,0 +1,94 @@ | |||
| <template> | ||||
|   <Dialog v-model="dialogVisible" :appendToBody="true" title="选择规格" width="700"> | ||||
|     <el-table v-loading="loading" :data="list" show-overflow-tooltip> | ||||
|       <el-table-column label="#" width="55"> | ||||
|         <template #default="{ row }"> | ||||
|           <el-radio :label="row.id" v-model="selectedSkuId" @change="handleSelected(row)" | ||||
|             >  | ||||
|           </el-radio> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="图片" min-width="80"> | ||||
|         <template #default="{ row }"> | ||||
|           <el-image | ||||
|             :src="row.picUrl" | ||||
|             class="w-30px h-30px" | ||||
|             :preview-src-list="[row.picUrl]" | ||||
|             preview-teleported | ||||
|           /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="规格" align="center" min-width="80"> | ||||
|         <template #default="{ row }"> | ||||
|           {{ row.properties?.map((p) => p.valueName)?.join(' ') }} | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column align="center" label="销售价(元)" min-width="80"> | ||||
|         <template #default="{ row }"> | ||||
|           {{ row.price }} | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|   </Dialog> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { ElTable } from 'element-plus' | ||||
| import * as ProductSpuApi from '@/api/mall/product/spu' | ||||
| import { propTypes } from '@/utils/propTypes' | ||||
| 
 | ||||
| defineOptions({ name: 'SkuTableSelect' }) | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   spuId: propTypes.number.def(null) | ||||
| }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const list = ref<any[]>([]) // 列表的数据 | ||||
| const loading = ref(false) // 列表的加载中 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| 
 | ||||
| const selectedSkuId = ref() // 选中的商品 spuId | ||||
| 
 | ||||
| /** 选中时触发 */ | ||||
| const handleSelected = (row: ProductSpuApi.Sku) => { | ||||
|   emits('change', row) | ||||
|   // 关闭弹窗 | ||||
|   dialogVisible.value = false | ||||
|   selectedSkuId.value = undefined | ||||
| } | ||||
| 
 | ||||
| // 确认选择时的触发事件 | ||||
| const emits = defineEmits<{ | ||||
|   (e: 'change', spu: ProductSpuApi.Sku): void | ||||
| }>() | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = () => { | ||||
|   dialogVisible.value = true | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const getSpuDetail = async () => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     const spu = await ProductSpuApi.getSpu(props.spuId) | ||||
|     list.value = spu.skus | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(async () => {}) | ||||
| watch( | ||||
|   () => props.spuId, | ||||
|   () => { | ||||
|     if (!props.spuId) { | ||||
|       return | ||||
|     } | ||||
|     getSpuDetail() | ||||
|   } | ||||
| ) | ||||
| </script> | ||||
|  | @ -0,0 +1,167 @@ | |||
| <template> | ||||
|   <Dialog v-model="dialogVisible" :appendToBody="true" title="选择商品" width="70%"> | ||||
|     <el-row :gutter="20" class="mb-10px"> | ||||
|       <el-col :span="6"> | ||||
|         <el-input | ||||
|           v-model="queryParams.name" | ||||
|           class="!w-240px" | ||||
|           clearable | ||||
|           placeholder="请输入商品名称" | ||||
|           @keyup.enter="handleQuery" | ||||
|         /> | ||||
|       </el-col> | ||||
|       <el-col :span="6"> | ||||
|         <el-tree-select | ||||
|           v-model="queryParams.categoryId" | ||||
|           :data="categoryTreeList" | ||||
|           :props="defaultProps" | ||||
|           check-strictly | ||||
|           class="w-1/1" | ||||
|           node-key="id" | ||||
|           placeholder="请选择商品分类" | ||||
|         /> | ||||
|       </el-col> | ||||
|       <el-col :span="6"> | ||||
|         <el-date-picker | ||||
|           v-model="queryParams.createTime" | ||||
|           :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" | ||||
|           class="!w-240px" | ||||
|           end-placeholder="结束日期" | ||||
|           start-placeholder="开始日期" | ||||
|           type="daterange" | ||||
|           value-format="YYYY-MM-DD HH:mm:ss" | ||||
|         /> | ||||
|       </el-col> | ||||
|       <el-col :span="6"> | ||||
|         <el-button @click="handleQuery"> | ||||
|           <Icon class="mr-5px" icon="ep:search" /> | ||||
|           搜索 | ||||
|         </el-button> | ||||
|         <el-button @click="resetQuery"> | ||||
|           <Icon class="mr-5px" icon="ep:refresh" /> | ||||
|           重置 | ||||
|         </el-button> | ||||
|       </el-col> | ||||
|     </el-row> | ||||
|     <el-table ref="spuListRef" v-loading="loading" :data="list" show-overflow-tooltip> | ||||
|       <el-table-column label="#" width="55"> | ||||
|         <template #default="{ row }"> | ||||
|           <el-radio :label="row.id" v-model="selectedSpuId" @change="handleSelected(row)" | ||||
|             > </el-radio | ||||
|           > | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column key="id" align="center" label="商品编号" prop="id" min-width="60" /> | ||||
|       <el-table-column label="商品图" min-width="80"> | ||||
|         <template #default="{ row }"> | ||||
|           <el-image | ||||
|             :src="row.picUrl" | ||||
|             class="w-30px h-30px" | ||||
|             :preview-src-list="[row.picUrl]" | ||||
|             preview-teleported | ||||
|           /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="商品名称" min-width="200" prop="name" /> | ||||
|       <el-table-column label="商品分类" min-width="100" prop="categoryId"> | ||||
|         <template #default="{ row }"> | ||||
|           <span>{{ categoryList.find((c) => c.id === row.categoryId)?.name }}</span> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|     <!-- 分页 --> | ||||
|     <Pagination | ||||
|       v-model:limit="queryParams.pageSize" | ||||
|       v-model:page="queryParams.pageNo" | ||||
|       :total="total" | ||||
|       @pagination="getList" | ||||
|     /> | ||||
|   </Dialog> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { ElTable } from 'element-plus' | ||||
| import { defaultProps, handleTree } from '@/utils/tree' | ||||
| 
 | ||||
| import * as ProductCategoryApi from '@/api/mall/product/category' | ||||
| import * as ProductSpuApi from '@/api/mall/product/spu' | ||||
| 
 | ||||
| defineOptions({ name: 'SpuTableSelect' }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref<any[]>([]) // 列表的数据 | ||||
| const loading = ref(false) // 列表的加载中 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const queryParams = ref({ | ||||
|   pageNo: 1, | ||||
|   pageSize: 10, | ||||
|   tabType: 0, // 默认获取上架的商品 | ||||
|   name: '', | ||||
|   categoryId: null, | ||||
|   createTime: [] | ||||
| }) // 查询参数 | ||||
| const spuListRef = ref<InstanceType<typeof ElTable>>() | ||||
| 
 | ||||
| const selectedSpuId = ref() // 选中的商品 spuId | ||||
| 
 | ||||
| /** 选中时触发 */ | ||||
| const handleSelected = (row: ProductSpuApi.Spu) => { | ||||
|   emits('change', row) | ||||
|   // 关闭弹窗 | ||||
|   dialogVisible.value = false | ||||
|   selectedSpuId.value = undefined | ||||
| } | ||||
| 
 | ||||
| // 确认选择时的触发事件 | ||||
| const emits = defineEmits<{ | ||||
|   (e: 'change', spu: ProductSpuApi.Spu): void | ||||
| }>() | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = () => { | ||||
|   dialogVisible.value = true | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     const data = await ProductSpuApi.getSpuPage(queryParams.value) | ||||
|     list.value = data.list | ||||
|     total.value = data.total | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   getList() | ||||
| } | ||||
| 
 | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryParams.value = { | ||||
|     pageNo: 1, | ||||
|     pageSize: 10, | ||||
|     tabType: 0, // 默认获取上架的商品 | ||||
|     name: '', | ||||
|     categoryId: null, | ||||
|     createTime: [] | ||||
|   } | ||||
|   getList() | ||||
| } | ||||
| 
 | ||||
| const categoryList = ref() // 分类列表 | ||||
| const categoryTreeList = ref() // 分类树 | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(async () => { | ||||
|   await getList() | ||||
|   // 获得分类树 | ||||
|   categoryList.value = await ProductCategoryApi.getCategoryList({}) | ||||
|   categoryTreeList.value = handleTree(categoryList.value, 'id', 'parentId') | ||||
| }) | ||||
| </script> | ||||
|  | @ -7,7 +7,6 @@ import SkuList from './SkuList.vue' | |||
| 
 | ||||
| import { Spu } from '@/api/mall/product/spu' | ||||
| 
 | ||||
| // TODO @puhui999:Properties 改成 Property 更合适?Property 在 Spu 中已存在避免冲突 PropertyAndValues
 | ||||
| interface PropertyAndValues { | ||||
|   id: number | ||||
|   name: string | ||||
|  |  | |||
|  | @ -80,7 +80,7 @@ | |||
|     <el-table v-loading="loading" :data="list"> | ||||
|       <el-table-column type="expand" width="30"> | ||||
|         <template #default="{ row }"> | ||||
|           <el-form class="demo-table-expand" label-position="left"> | ||||
|           <el-form class="spu-table-expand" label-position="left"> | ||||
|             <el-row> | ||||
|               <el-col :span="24"> | ||||
|                 <el-row> | ||||
|  | @ -91,12 +91,12 @@ | |||
|                   </el-col> | ||||
|                   <el-col :span="8"> | ||||
|                     <el-form-item label="市场价:"> | ||||
|                       <span>{{ formatToFraction(row.marketPrice) }}</span> | ||||
|                       <span>{{ floatToFixed2(row.marketPrice) }}元</span> | ||||
|                     </el-form-item> | ||||
|                   </el-col> | ||||
|                   <el-col :span="8"> | ||||
|                     <el-form-item label="成本价:"> | ||||
|                       <span>{{ formatToFraction(row.costPrice) }}</span> | ||||
|                       <span>{{ floatToFixed2(row.costPrice) }}元</span> | ||||
|                     </el-form-item> | ||||
|                   </el-col> | ||||
|                 </el-row> | ||||
|  | @ -130,9 +130,7 @@ | |||
|       </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 }"> | ||||
|           {{ formatToFraction(row.price) }} | ||||
|         </template> | ||||
|         <template #default="{ row }"> {{ floatToFixed2(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" /> | ||||
|  | @ -229,7 +227,7 @@ import { createImageViewer } from '@/components/ImageViewer' | |||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree' | ||||
| import { ProductSpuStatusEnum } from '@/utils/constants' | ||||
| import { formatToFraction } from '@/utils' | ||||
| import { floatToFixed2 } from '@/utils' | ||||
| import download from '@/utils/download' | ||||
| import * as ProductSpuApi from '@/api/mall/product/spu' | ||||
| import * as ProductCategoryApi from '@/api/mall/product/category' | ||||
|  | @ -392,7 +390,7 @@ const resetQuery = () => { | |||
| const openForm = (id?: number) => { | ||||
|   // 修改 | ||||
|   if (typeof id === 'number') { | ||||
|     push('/product/spu/edit/' + id) | ||||
|     push({ name: 'ProductSpuEdit', params: { spuId: id } }) | ||||
|     return | ||||
|   } | ||||
|   // 新增 | ||||
|  | @ -402,8 +400,8 @@ const openForm = (id?: number) => { | |||
| /** | ||||
|  * 查看商品详情 | ||||
|  */ | ||||
| const openDetail = (id?: number) => { | ||||
|   push('/product/spu/detail/' + id) | ||||
| const openDetail = (id: number) => { | ||||
|   push({ name: 'ProductSpuDetail', params: { spuId: id } }) | ||||
| } | ||||
| 
 | ||||
| /** 导出按钮操作 */ | ||||
|  | @ -458,7 +456,7 @@ onMounted(async () => { | |||
| }) | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .demo-table-expand { | ||||
| .spu-table-expand { | ||||
|   padding-left: 42px; | ||||
| 
 | ||||
|   :deep(.el-form-item__label) { | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ | |||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
|   <SpuSelect ref="spuSelectRef" :isSelectSku="true" @confirm="selectSpu" /> | ||||
|   <SpuSelect ref="spuSelectRef" :isSelectSku="true" :radio="true" @confirm="selectSpu" /> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import * as BargainActivityApi from '@/api/mall/promotion/bargain/bargainActivity' | ||||
|  | @ -83,17 +83,17 @@ const ruleConfig: RuleConfig[] = [ | |||
|   { | ||||
|     name: 'productConfig.bargainFirstPrice', | ||||
|     rule: (arg) => arg > 0, | ||||
|     message: '商品砍价起始价格不能小于0 !!!' | ||||
|     message: '商品砍价起始价格不能小于 0 !!!' | ||||
|   }, | ||||
|   { | ||||
|     name: 'productConfig.bargainPrice', | ||||
|     rule: (arg) => arg > 0, | ||||
|     message: '商品砍价底价不能小于0 !!!' | ||||
|     rule: (arg) => arg >= 0, | ||||
|     message: '商品砍价底价不能小于 0 !!!' | ||||
|   }, | ||||
|   { | ||||
|     name: 'productConfig.stock', | ||||
|     rule: (arg) => arg > 1, | ||||
|     message: '商品活动库存不能小于1 !!!' | ||||
|     rule: (arg) => arg >= 1, | ||||
|     message: '商品活动库存不能小于 1 !!!' | ||||
|   } | ||||
| ] | ||||
| const selectSpu = (spuId: number, skuIds: number[]) => { | ||||
|  | @ -164,7 +164,20 @@ const open = async (type: string, id?: number) => { | |||
|       // 用户每次砍价金额分转元, 分转元 | ||||
|       data.randomMinPrice = formatToFraction(data.randomMinPrice) | ||||
|       data.randomMaxPrice = formatToFraction(data.randomMaxPrice) | ||||
|       await getSpuDetails(data.spuId!, data.products?.map((sku) => sku.skuId), data.products) | ||||
|       // 对齐活动商品处理结构 | ||||
|       await getSpuDetails( | ||||
|         data.spuId!, | ||||
|         [data.skuId], | ||||
|         [ | ||||
|           { | ||||
|             spuId: data.spuId!, | ||||
|             skuId: data.skuId, | ||||
|             bargainFirstPrice: data.bargainFirstPrice, // 砍价起始价格,单位分 | ||||
|             bargainPrice: data.bargainPrice, // 砍价底价 | ||||
|             stock: data.stock // 活动库存 | ||||
|           } | ||||
|         ] | ||||
|       ) | ||||
|       formRef.value.setValues(data) | ||||
|     } finally { | ||||
|       formLoading.value = false | ||||
|  | @ -201,12 +214,12 @@ const submitForm = async () => { | |||
|     // 用户每次砍价金额分转元, 元转分 | ||||
|     data.randomMinPrice = convertToInteger(data.randomMinPrice) | ||||
|     data.randomMaxPrice = convertToInteger(data.randomMaxPrice) | ||||
|     data.products = products | ||||
|     const formData = { ...data, ...products[0] } | ||||
|     if (formType.value === 'create') { | ||||
|       await BargainActivityApi.createBargainActivity(data) | ||||
|       await BargainActivityApi.createBargainActivity(formData) | ||||
|       message.success(t('common.createSuccess')) | ||||
|     } else { | ||||
|       await BargainActivityApi.updateBargainActivity(data) | ||||
|       await BargainActivityApi.updateBargainActivity(formData) | ||||
|       message.success(t('common.updateSuccess')) | ||||
|     } | ||||
|     dialogVisible.value = false | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| 
 | ||||
|   <div></div> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| </script> | ||||
| 
 | ||||
| <script lang="ts" name="CombinationRecord" setup></script> | ||||
|  |  | |||
|  | @ -127,7 +127,8 @@ defineOptions({ name: 'PromotionSpuSelect' }) | |||
| const props = defineProps({ | ||||
|   // 默认不需要(不需要的情况下只返回 spu,需要的情况下返回 选中的 spu 和 sku 列表) | ||||
|   // 其它活动需要选择商品和商品属性导入此组件即可,需添加组件属性 :isSelectSku='true' | ||||
|   isSelectSku: propTypes.bool.def(false) // 是否需要选择 sku 属性 | ||||
|   isSelectSku: propTypes.bool.def(false), // 是否需要选择 sku 属性 | ||||
|   radio: propTypes.bool.def(false) // 是否单选 sku | ||||
| }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
|  | @ -146,7 +147,7 @@ const queryParams = ref({ | |||
| }) // 查询参数 | ||||
| const propertyList = ref<PropertyAndValues[]>([]) // 商品属性列表 | ||||
| const spuListRef = ref<InstanceType<typeof ElTable>>() | ||||
| const skuListRef = ref() // 商品属性选择 Ref | ||||
| const skuListRef = ref<InstanceType<typeof SkuList>>() // 商品属性选择 Ref | ||||
| const spuData = ref<ProductSpuApi.Spu>() // 商品详情 | ||||
| const isExpand = ref(false) // 控制 SKU 列表显示 | ||||
| const expandRowKeys = ref<number[]>() // 控制展开行需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。 | ||||
|  | @ -155,12 +156,30 @@ const expandRowKeys = ref<number[]>() // 控制展开行需要设置 row-key 属 | |||
| const selectedSpuId = ref<number>(0) // 选中的商品 spuId | ||||
| const selectedSkuIds = ref<number[]>([]) // 选中的商品 skuIds | ||||
| const selectSku = (val: ProductSpuApi.Sku[]) => { | ||||
|   const skuTable = skuListRef.value?.getSkuTableRef() | ||||
|   if (selectedSpuId.value === 0) { | ||||
|     message.warning('请先选择商品再选择相应的规格!!!') | ||||
|     skuListRef.value.clearSelection() | ||||
|     skuTable?.clearSelection() | ||||
|     return | ||||
|   } | ||||
|   selectedSkuIds.value = val.map((sku) => sku.id!) | ||||
|   if (val.length === 0) { | ||||
|     selectedSkuIds.value = [] | ||||
|     return | ||||
|   } | ||||
|   if (props.radio) { | ||||
|     // 只选择一个 | ||||
|     selectedSkuIds.value = [val.map((sku) => sku.id!)[0]] | ||||
|     // 如果大于1个 | ||||
|     if (val.length > 1) { | ||||
|       // 清空选择 | ||||
|       skuTable?.clearSelection() | ||||
|       // 变更为最后一次选择的 | ||||
|       skuTable?.toggleRowSelection(val.pop(), true) | ||||
|       return | ||||
|     } | ||||
|   } else { | ||||
|     selectedSkuIds.value = val.map((sku) => sku.id!) | ||||
|   } | ||||
| } | ||||
| const selectSpu = (val: ProductSpuApi.Spu[]) => { | ||||
|   if (val.length === 0) { | ||||
|  | @ -176,9 +195,9 @@ const selectSpu = (val: ProductSpuApi.Spu[]) => { | |||
|   // 如果大于1个 | ||||
|   if (val.length > 1) { | ||||
|     // 清空选择 | ||||
|     spuListRef.value.clearSelection() | ||||
|     spuListRef.value?.clearSelection() | ||||
|     // 变更为最后一次选择的 | ||||
|     spuListRef.value.toggleRowSelection(val.pop(), true) | ||||
|     spuListRef.value?.toggleRowSelection(val.pop(), true) | ||||
|     return | ||||
|   } | ||||
|   expandChange(val[0], val) | ||||
|  | @ -194,7 +213,7 @@ const expandChange = async (row: ProductSpuApi.Spu, expandedRows?: ProductSpuApi | |||
|       expandRowKeys.value = [selectedSpuId.value] | ||||
|       return | ||||
|     } | ||||
|     // 如果以展开 skuList 则选择此对应的 spu 不需要重新获取渲染 skuList | ||||
|     // 如果已展开 skuList 则选择此对应的 spu 不需要重新获取渲染 skuList | ||||
|     if (isExpand.value && spuData.value?.id === row.id) { | ||||
|       return | ||||
|     } | ||||
|  |  | |||
|  | @ -193,16 +193,4 @@ const resetForm = async () => { | |||
|   await nextTick() | ||||
|   formRef.value.getElFormRef().resetFields() | ||||
| } | ||||
| // TODO @puhui999:下面的 css 名字,是不是可以改下;demo-table-expand | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .demo-table-expand { | ||||
|   padding-left: 42px; | ||||
| 
 | ||||
|   :deep(.el-form-item__label) { | ||||
|     width: 82px; | ||||
|     font-weight: bold; | ||||
|     color: #99a9bf; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -1,27 +1,27 @@ | |||
| <template> | ||||
|   <!-- 搜索工作栏 --> | ||||
|   <ContentWrap> | ||||
|     <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true"> | ||||
|     <el-form ref="queryFormRef" :inline="true" :model="queryParams" class="-mb-15px"> | ||||
|       <el-form-item label="门店手机" prop="phone"> | ||||
|         <el-input | ||||
|           v-model="queryParams.phone" | ||||
|           placeholder="请输门店手机" | ||||
|           clearable | ||||
|           @keyup.enter="handleQuery" | ||||
|           class="!w-240px" | ||||
|           clearable | ||||
|           placeholder="请输门店手机" | ||||
|           @keyup.enter="handleQuery" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="门店名称" prop="name"> | ||||
|         <el-input | ||||
|           v-model="queryParams.name" | ||||
|           placeholder="请输门店名称" | ||||
|           clearable | ||||
|           @keyup.enter="handleQuery" | ||||
|           class="!w-240px" | ||||
|           clearable | ||||
|           placeholder="请输门店名称" | ||||
|           @keyup.enter="handleQuery" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="门店状态" prop="status"> | ||||
|         <el-select v-model="queryParams.status" placeholder="门店状态" clearable class="!w-240px"> | ||||
|         <el-select v-model="queryParams.status" class="!w-240px" clearable placeholder="门店状态"> | ||||
|           <el-option | ||||
|             v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" | ||||
|             :key="dict.value" | ||||
|  | @ -33,32 +33,30 @@ | |||
|       <el-form-item label="创建时间" prop="createTime"> | ||||
|         <el-date-picker | ||||
|           v-model="queryParams.createTime" | ||||
|           value-format="YYYY-MM-DD HH:mm:ss" | ||||
|           type="datetimerange" | ||||
|           start-placeholder="开始日期" | ||||
|           end-placeholder="结束日期" | ||||
|           class="!w-240px" | ||||
|           end-placeholder="结束日期" | ||||
|           start-placeholder="开始日期" | ||||
|           type="datetimerange" | ||||
|           value-format="YYYY-MM-DD HH:mm:ss" | ||||
|         /> | ||||
|       </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 | ||||
|           type="primary" | ||||
|           plain | ||||
|           @click="openForm('create')" | ||||
|           v-hasPermi="['trade:delivery:pick-up-store:create']" | ||||
|         > | ||||
|           <Icon icon="ep:plus" class="mr-5px" /> 新增 | ||||
|         <el-button @click="handleQuery"> | ||||
|           <Icon class="mr-5px" icon="ep:search" /> | ||||
|           搜索 | ||||
|         </el-button> | ||||
|         <el-button @click="resetQuery"> | ||||
|           <Icon class="mr-5px" icon="ep:refresh" /> | ||||
|           重置 | ||||
|         </el-button> | ||||
|         <el-button | ||||
|           type="success" | ||||
|           v-hasPermi="['trade:delivery:pick-up-store:create']" | ||||
|           plain | ||||
|           @click="handleExport" | ||||
|           :loading="exportLoading" | ||||
|           v-hasPermi="['trade:delivery:pick-up-store:export']" | ||||
|           type="primary" | ||||
|           @click="openForm('create')" | ||||
|         > | ||||
|           <Icon icon="ep:download" class="mr-5px" /> 导出 | ||||
|           <Icon class="mr-5px" icon="ep:plus" /> | ||||
|           新增 | ||||
|         </el-button> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|  | @ -75,34 +73,34 @@ | |||
|       </el-table-column> | ||||
|       <el-table-column label="门店名称" prop="name" /> | ||||
|       <el-table-column label="门店手机" prop="phone" /> | ||||
|       <el-table-column label="门店详细地址" align="center" prop="detailAddress" /> | ||||
|       <el-table-column label="开启状态" align="center" prop="status"> | ||||
|       <el-table-column align="center" label="门店详细地址" prop="detailAddress" /> | ||||
|       <el-table-column align="center" label="开启状态" prop="status"> | ||||
|         <template #default="scope"> | ||||
|           <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column | ||||
|         label="创建时间" | ||||
|         :formatter="dateFormatter" | ||||
|         align="center" | ||||
|         label="创建时间" | ||||
|         prop="createTime" | ||||
|         width="180" | ||||
|         :formatter="dateFormatter" | ||||
|       /> | ||||
|       <el-table-column label="操作" align="center"> | ||||
|       <el-table-column align="center" label="操作"> | ||||
|         <template #default="scope"> | ||||
|           <el-button | ||||
|             v-hasPermi="['trade:delivery:pick-up-store:update']" | ||||
|             link | ||||
|             type="primary" | ||||
|             @click="openForm('update', scope.row.id)" | ||||
|             v-hasPermi="['trade:delivery:pick-up-store:update']" | ||||
|           > | ||||
|             编辑 | ||||
|           </el-button> | ||||
|           <el-button | ||||
|             v-hasPermi="['trade:delivery:pick-up-store:delete']" | ||||
|             link | ||||
|             type="danger" | ||||
|             @click="handleDelete(scope.row.id)" | ||||
|             v-hasPermi="['trade:delivery:pick-up-store:delete']" | ||||
|           > | ||||
|             删除 | ||||
|           </el-button> | ||||
|  | @ -113,18 +111,17 @@ | |||
|   <!-- 表单弹窗:添加/修改 --> | ||||
|   <DeliveryPickUpStoreForm ref="formRef" @success="getList" /> | ||||
| </template> | ||||
| <script setup lang="ts" name="DeliveryPickUpStore"> | ||||
| <script lang="ts" name="DeliveryPickUpStore" setup> | ||||
| import * as DeliveryPickUpStoreApi from '@/api/mall/trade/delivery/pickUpStore' | ||||
| import DeliveryPickUpStoreForm from './PickUpStoreForm.vue' | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import download from '@/utils/download' | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { t } = useI18n() // 国际化 | ||||
| 
 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const exportLoading = ref(false) // 导出的加载中 | ||||
| const list = ref<any[]>([]) // 列表的数据 | ||||
| const queryParams = reactive({ | ||||
|   pageNo: 1, | ||||
|  | @ -179,21 +176,6 @@ const resetQuery = () => { | |||
|   handleQuery() | ||||
| } | ||||
| 
 | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = async () => { | ||||
|   try { | ||||
|     // 导出的二次确认 | ||||
|     await message.exportConfirm() | ||||
|     // 发起导出 | ||||
|     exportLoading.value = true | ||||
|     const data = await DeliveryPickUpStoreApi.exportDeliveryPickUpStoreApi(queryParams) | ||||
|     download.excel(data, '自提门店.xls') | ||||
|   } catch { | ||||
|   } finally { | ||||
|     exportLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(() => { | ||||
|   getList() | ||||
|  |  | |||
|  | @ -0,0 +1,100 @@ | |||
| <template> | ||||
|   <Dialog v-model="dialogVisible" title="订单发货" width="25%"> | ||||
|     <el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px"> | ||||
|       <el-form-item label="发货方式"> | ||||
|         <el-radio-group v-model="expressType"> | ||||
|           <el-radio border label="express">快递物流</el-radio> | ||||
|           <el-radio border label="none">无需发货</el-radio> | ||||
|         </el-radio-group> | ||||
|       </el-form-item> | ||||
|       <template v-if="expressType === 'express'"> | ||||
|         <el-form-item label="物流公司"> | ||||
|           <el-select v-model="formData.logisticsId" placeholder="请选择" style="width: 100%"> | ||||
|             <el-option | ||||
|               v-for="item in deliveryExpressList" | ||||
|               :key="item.id" | ||||
|               :label="item.name" | ||||
|               :value="item.id" | ||||
|             /> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="物流单号"> | ||||
|           <el-input v-model="formData.logisticsNo" /> | ||||
|         </el-form-item> | ||||
|       </template> | ||||
|     </el-form> | ||||
|     <template #footer> | ||||
|       <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button> | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express' | ||||
| import * as TradeOrderApi from '@/api/mall/trade/order' | ||||
| import { copyValueToTarget } from '@/utils' | ||||
| 
 | ||||
| // TODO @puhui999:要不 Form 保持和别的模块一样,和 index.vue 放在一个目录下,不作为 components;components 更多要支持给多个模块使用哈。 | ||||
| defineOptions({ name: 'OrderDeliveryForm' }) | ||||
| 
 | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const expressType = ref('express') // 如果值是 express,则是快递;none 则是无;未来做同城配送; | ||||
| const formData = ref<TradeOrderApi.DeliveryVO>({ | ||||
|   id: 0, // 订单编号 | ||||
|   logisticsId: null, // 物流公司编号 | ||||
|   logisticsNo: '' // 物流编号 | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (row: TradeOrderApi.OrderVO) => { | ||||
|   resetForm() | ||||
|   // 设置数据 | ||||
|   copyValueToTarget(formData.value, row) | ||||
|   if (row.logisticsId === null || row.logisticsId === 0) { | ||||
|     expressType.value = 'none' | ||||
|   } | ||||
|   dialogVisible.value = true | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 提交表单 */ | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitForm = async () => { | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     const data = unref(formData) | ||||
|     if (expressType.value === 'none') { | ||||
|       // 无需发货的情况 | ||||
|       data.logisticsId = 0 | ||||
|       data.logisticsNo = '' | ||||
|     } | ||||
|     await TradeOrderApi.delivery(data) | ||||
|     message.success(t('common.updateSuccess')) | ||||
|     dialogVisible.value = false | ||||
|     // 发送操作成功的事件 | ||||
|     emit('success', true) | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
|     id: 0, // 订单编号 | ||||
|     logisticsId: null, // 物流公司编号 | ||||
|     logisticsNo: '' // 物流编号 | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
| const deliveryExpressList = ref([]) | ||||
| onMounted(async () => { | ||||
|   deliveryExpressList.value = await DeliveryExpressApi.getSimpleDeliveryExpressList() | ||||
| }) | ||||
| </script> | ||||
|  | @ -0,0 +1,98 @@ | |||
| <template> | ||||
|   <Dialog v-model="dialogVisible" title="修改订单收货地址" width="35%"> | ||||
|     <el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="120px"> | ||||
|       <el-form-item label="收件人"> | ||||
|         <el-input v-model="formData.receiverName" placeholder="请输入收件人名称" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="手机号"> | ||||
|         <el-input v-model="formData.receiverMobile" placeholder="请输入收件人手机号" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="所在地"> | ||||
|         <el-tree-select | ||||
|           v-model="formData.receiverAreaId" | ||||
|           :data="areaList" | ||||
|           :props="defaultProps" | ||||
|           :render-after-expand="true" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="详细地址"> | ||||
|         <el-input | ||||
|           v-model="formData.receiverDetailAddress" | ||||
|           :rows="3" | ||||
|           placeholder="请输入收件人详细地址" | ||||
|           type="textarea" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|     <template #footer> | ||||
|       <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button> | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import * as TradeOrderApi from '@/api/mall/trade/order' | ||||
| import { getAreaTree } from '@/api/system/area' | ||||
| import { copyValueToTarget } from '@/utils' | ||||
| import { defaultProps } from '@/utils/tree' | ||||
| 
 | ||||
| defineOptions({ name: 'OrderUpdateAddressForm' }) | ||||
| 
 | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formData = ref({ | ||||
|   id: 0, // 订单编号 | ||||
|   receiverName: '', // 收件人名称 | ||||
|   receiverMobile: '', // 收件人手机 | ||||
|   receiverAreaId: null, //收件人地区编号 | ||||
|   receiverDetailAddress: '' //收件人详细地址 | ||||
| }) | ||||
| const areaList = ref([]) // 地区列表 | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (row: TradeOrderApi.OrderVO) => { | ||||
|   resetForm() | ||||
|   // 设置数据 | ||||
|   copyValueToTarget(formData.value, row) | ||||
|   dialogVisible.value = true | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 提交表单 */ | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitForm = async () => { | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     const data = unref(formData) | ||||
|     await TradeOrderApi.updateAddress(data) | ||||
|     message.success(t('common.updateSuccess')) | ||||
|     dialogVisible.value = false | ||||
|     // 发送操作成功的事件 | ||||
|     emit('success', true) | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
|     id: 0, // 订单编号 | ||||
|     receiverName: '', // 收件人名称 | ||||
|     receiverMobile: '', // 收件人手机 | ||||
|     receiverAreaId: null, //收件人地区编号 | ||||
|     receiverDetailAddress: '' //收件人详细地址 | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
| 
 | ||||
| onMounted(async () => { | ||||
|   // 获得地区列表 | ||||
|   areaList.value = await getAreaTree() | ||||
| }) | ||||
| </script> | ||||
|  | @ -0,0 +1,92 @@ | |||
| <template> | ||||
|   <Dialog v-model="dialogVisible" title="订单调价" width="25%"> | ||||
|     <el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="100px"> | ||||
|       <el-form-item label="应付金额(总)"> | ||||
|         <el-input v-model="formData.payPrice" disabled /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="订单调价"> | ||||
|         <el-input-number v-model="formData.adjustPrice" :precision="2" :step="0.1" class="w-100%" /> | ||||
|         <el-tag class="mt-10px" type="warning">订单调价。 正数,加价;负数,减价</el-tag> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="调价后"> | ||||
|         <el-input v-model="formData.newPayPrice" disabled /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|     <template #footer> | ||||
|       <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button> | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import * as TradeOrderApi from '@/api/mall/trade/order' | ||||
| import { convertToInteger, floatToFixed2, formatToFraction } from '@/utils' | ||||
| import { cloneDeep } from 'lodash-es' | ||||
| 
 | ||||
| defineOptions({ name: 'OrderUpdatePriceForm' }) | ||||
| 
 | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formData = ref({ | ||||
|   id: 0, // 订单编号 | ||||
|   adjustPrice: 0, // 订单调价 | ||||
|   payPrice: '', // 应付金额(总) | ||||
|   newPayPrice: '' // 调价后应付金额(总) | ||||
| }) | ||||
| watch( | ||||
|   () => formData.value.adjustPrice, | ||||
|   (data: number) => { | ||||
|     const num = formData.value.payPrice!.replace('元', '') | ||||
|     // @ts-ignore | ||||
|     formData.value.newPayPrice = (num * 1 + data).toFixed(2) + '元' | ||||
|   } | ||||
| ) | ||||
| 
 | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (row: TradeOrderApi.OrderVO) => { | ||||
|   resetForm() | ||||
|   formData.value.id = row.id! | ||||
|   // 设置数据 | ||||
|   formData.value.adjustPrice = formatToFraction(row.adjustPrice!) | ||||
|   formData.value.payPrice = floatToFixed2(row.payPrice!) + '元' | ||||
|   formData.value.newPayPrice = formData.value.payPrice | ||||
|   dialogVisible.value = true | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 提交表单 */ | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitForm = async () => { | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     const data = cloneDeep(unref(formData)) | ||||
|     data.adjustPrice = convertToInteger(data.adjustPrice) | ||||
|     delete data.payPrice | ||||
|     delete data.newPayPrice | ||||
|     await TradeOrderApi.updatePrice(data) | ||||
|     message.success(t('common.updateSuccess')) | ||||
|     dialogVisible.value = false | ||||
|     // 发送操作成功的事件 | ||||
|     emit('success', true) | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
|     id: 0, // 订单编号 | ||||
|     adjustPrice: 0, // 订单调价 | ||||
|     payPrice: '', // 应付金额(总) | ||||
|     newPayPrice: '' // 调价后应付金额(总) | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
| </script> | ||||
|  | @ -0,0 +1,71 @@ | |||
| <template> | ||||
|   <Dialog v-model="dialogVisible" title="商家备注" width="45%"> | ||||
|     <el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px"> | ||||
|       <el-form-item label="备注"> | ||||
|         <el-input | ||||
|           v-model="formData.remark" | ||||
|           :rows="3" | ||||
|           placeholder="请输入订单备注" | ||||
|           type="textarea" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|     <template #footer> | ||||
|       <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button> | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import * as TradeOrderApi from '@/api/mall/trade/order' | ||||
| 
 | ||||
| defineOptions({ name: 'OrderUpdateRemarkForm' }) | ||||
| 
 | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formData = ref({ | ||||
|   id: 0, // 订单编号 | ||||
|   remark: '' // 订单备注 | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (row: TradeOrderApi.OrderVO) => { | ||||
|   resetForm() | ||||
|   // 设置数据 | ||||
|   formData.value.id = row.id | ||||
|   formData.value.remark = row.remark | ||||
|   dialogVisible.value = true | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 提交表单 */ | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitForm = async () => { | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     const data = unref(formData) | ||||
|     console.log(data) | ||||
|     await TradeOrderApi.updateRemark(data) | ||||
|     message.success(t('common.updateSuccess')) | ||||
|     dialogVisible.value = false | ||||
|     // 发送操作成功的事件 | ||||
|     emit('success', true) | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
|     id: 0, // 订单编号 | ||||
|     remark: '' // 订单备注 | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
| </script> | ||||
|  | @ -1,365 +1,358 @@ | |||
| <template> | ||||
|   <ContentWrap> | ||||
|     <!-- 订单信息 --> | ||||
|     <el-descriptions title="订单信息"> | ||||
|       <el-descriptions-item label="订单号: ">{{ order.no }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="配送方式: ">物流配送</el-descriptions-item> | ||||
|       <!-- TODO 芋艿:待实现 --> | ||||
|       <el-descriptions-item label="营销活动: ">物流配送</el-descriptions-item> | ||||
|       <!-- TODO 芋艿:待实现 --> | ||||
|       <el-descriptions-item label="订单类型: "> | ||||
|         <dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="order.type" /> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="收货人: ">{{ order.receiverName }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="买家留言: ">{{ order.userRemark }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="订单来源: "> | ||||
|         <dict-tag :type="DICT_TYPE.TERMINAL" :value="order.terminal" /> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="联系电话: ">{{ order.receiverMobile }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="商家备注: ">{{ order.remark }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="支付单号: ">{{ order.payOrderId }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="付款方式: "> | ||||
|         <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE_TYPE" :value="order.payChannelCode" /> | ||||
|       </el-descriptions-item> | ||||
|       <!-- <el-descriptions-item label="买家: ">{{ order.user.nickname }}</el-descriptions-item> --> | ||||
|       <!-- TODO 芋艿:待实现:跳转会员 --> | ||||
|       <el-descriptions-item label="收货地址: "> | ||||
|         {{ order.receiverAreaName }} {{ order.receiverDetailAddress }} | ||||
|         <el-link | ||||
|           v-clipboard:copy="order.receiverAreaName + ' ' + order.receiverDetailAddress" | ||||
|           v-clipboard:success="clipboardSuccess" | ||||
|           icon="ep:document-copy" | ||||
|           type="primary" | ||||
|         /> | ||||
|       </el-descriptions-item> | ||||
|     </el-descriptions> | ||||
| 
 | ||||
|     <!-- 订单状态 --> | ||||
|     <el-descriptions title="订单状态" :column="1"> | ||||
|       <el-descriptions-item label="订单状态: "> | ||||
|         <!-- TODO xiaobai:status 一定有值哈,不用判断 --> | ||||
|         <dict-tag | ||||
|           v-if="order.status !== ''" | ||||
|           :type="DICT_TYPE.TRADE_ORDER_STATUS" | ||||
|           :value="order.status" | ||||
|         /> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label-class-name="no-colon"> | ||||
|         <el-button type="primary" size="small">调整价格</el-button> | ||||
|         <!-- TODO 芋艿:待实现 --> | ||||
|         <el-button type="primary" size="small">备注</el-button> | ||||
|         <!-- TODO 芋艿:待实现 --> | ||||
|         <el-button type="primary" size="small">发货</el-button> | ||||
|         <!-- TODO 芋艿:待实现 --> | ||||
|         <el-button type="primary" size="small">关闭订单</el-button> | ||||
|         <!-- TODO 芋艿:待实现 --> | ||||
|         <el-button type="primary" size="small">修改地址</el-button> | ||||
|         <!-- TODO 芋艿:待实现 --> | ||||
|         <el-button type="primary" size="small">打印电子面单</el-button> | ||||
|         <!-- TODO 芋艿:待实现 --> | ||||
|         <el-button type="primary" size="small">打印发货单</el-button> | ||||
|         <!-- TODO 芋艿:待实现 --> | ||||
|         <el-button type="primary" size="small">确认收货</el-button> | ||||
|         <!-- TODO 芋艿:待实现 --> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item> | ||||
|         <template #label><span style="color: red">提醒: </span></template> | ||||
|         买家付款成功后,货款将直接进入您的商户号(微信、支付宝)<br /> | ||||
|         请及时关注你发出的包裹状态,确保可以配送至买家手中 <br /> | ||||
|         如果买家表示没收到货或货物有问题,请及时联系买家处理,友好协商 | ||||
|       </el-descriptions-item> | ||||
|     </el-descriptions> | ||||
| 
 | ||||
|     <!-- 物流信息 TODO --> | ||||
| 
 | ||||
|     <!-- 商品信息 --> | ||||
|     <el-descriptions title="商品信息"> | ||||
|       <el-descriptions-item labelClassName="no-colon"> | ||||
|         <el-row :gutter="20"> | ||||
|           <el-col :span="15"> | ||||
|             <el-table :data="order.items" border> | ||||
|               <el-table-column prop="spuName" label="商品" width="auto"> | ||||
|                 <template #default="{ row }"> | ||||
|                   {{ row.spuName }} | ||||
|                   <el-tag | ||||
|                     size="medium" | ||||
|                     v-for="property in row.properties" | ||||
|                     :key="property.propertyId" | ||||
|                   > | ||||
|                     {{ property.propertyName }}: {{ property.valueName }}</el-tag | ||||
|                   > | ||||
|                 </template> | ||||
|               </el-table-column> | ||||
|               <el-table-column prop="price" label="商品原价(元)" width="150"> | ||||
|                 <template #default="{ row }"> ¥{{ (row.price / 100.0).toFixed(2) }} </template> | ||||
|               </el-table-column> | ||||
|               <el-table-column prop="count" label="数量" width="100" /> | ||||
|               <el-table-column prop="payPrice" label="合计(元)" width="150"> | ||||
|                 <template #default="{ row }"> ¥{{ (row.payPrice / 100.0).toFixed(2) }} </template> | ||||
|               </el-table-column> | ||||
|               <el-table-column prop="afterSaleStatus" label="售后状态" width="auto"> | ||||
|                 <template #default="{ row }"> | ||||
|                   <dict-tag | ||||
|                     :type="DICT_TYPE.TRADE_ORDER_ITEM_AFTER_SALE_STATUS" | ||||
|                     :value="row.afterSaleStatus" | ||||
|                   /> | ||||
|                 </template> | ||||
|               </el-table-column> | ||||
|             </el-table> | ||||
|           </el-col> | ||||
|           <el-col :span="10" /> | ||||
|         </el-row> | ||||
|       </el-descriptions-item> | ||||
|       <!-- 占位 --> | ||||
|       <!-- <el-descriptions-item v-for="item in 5" label-class-name="no-colon" :key="item" /> --> | ||||
|     </el-descriptions> | ||||
|     <el-descriptions column="6"> | ||||
|       <el-descriptions-item label="商品总额: "> | ||||
|         <!-- TODO xiaobai: 是不是 (item.payPrice / 100.0).toFixed(2) --> | ||||
|         ¥{{ parseFloat((order.totalPrice / 100.0) as unknown as string).toFixed(2) }} | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="运费金额: "> | ||||
|         ¥{{ parseFloat((order.deliveryPrice / 100.0) as unknown as string).toFixed(2) }} | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="订单调价: "> | ||||
|         ¥{{ | ||||
|           parseFloat((order.adjustPrice / 100.0) as unknown as string).toFixed(2) | ||||
|         }}</el-descriptions-item | ||||
|       > | ||||
| 
 | ||||
|       <el-descriptions-item> | ||||
|         <template #label><span style="color: red">商品优惠: </span></template> | ||||
|         <!-- 没理解TODO  order.totalPrice - order.totalPrice --> | ||||
|         ¥{{ | ||||
|           parseFloat(((order.totalPrice - order.totalPrice) / 100.0) as unknown as string).toFixed( | ||||
|             2 | ||||
|           ) | ||||
|         }} | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item> | ||||
|         <template #label><span style="color: red">订单优惠: </span></template> | ||||
|         ¥{{ parseFloat((order.discountPrice / 100.0) as unknown as string).toFixed(2) }} | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item> | ||||
|         <template #label><span style="color: red">积分抵扣: </span></template> | ||||
|         ¥{{ parseFloat((order.pointPrice / 100.0) as unknown as string).toFixed(2) }} | ||||
|       </el-descriptions-item> | ||||
| 
 | ||||
|       <el-descriptions-item v-for="item in 5" label-class-name="no-colon" :key="item" /> | ||||
|       <!-- 占位 --> | ||||
|       <el-descriptions-item label="应付金额: "> | ||||
|         ¥{{ (order.payPrice / 100.0).toFixed(2) }} | ||||
|       </el-descriptions-item> | ||||
|     </el-descriptions> | ||||
| 
 | ||||
|     <!-- TODO 芋艿:需要改改 --> | ||||
|     <div v-for="group in detailGroups" :key="group.title"> | ||||
|       <el-descriptions v-bind="group.groupProps" :title="group.title"> | ||||
|         <!-- 订单操作日志 --> | ||||
|         <el-descriptions-item v-if="group.key === 'orderLog'" labelClassName="no-colon"> | ||||
|           <el-timeline> | ||||
|             <el-timeline-item | ||||
|               v-for="activity in detailInfo[group.key]" | ||||
|               :key="activity.timestamp" | ||||
|               :timestamp="activity.timestamp" | ||||
|             > | ||||
|               {{ activity.content }} | ||||
|             </el-timeline-item> | ||||
|           </el-timeline> | ||||
|         </el-descriptions-item> | ||||
| 
 | ||||
|         <!-- 物流信息 --> | ||||
|         <!-- TODO @xiaobai:改成一个包裹哈;目前只允许发货一次 --> | ||||
|         <el-descriptions-item v-if="group.key === 'expressInfo'" labelClassName="no-colon"> | ||||
|           <!-- 循环包裹物流信息 --> | ||||
|           <div v-show="(pkgInfo = detailInfo[group.key]) !== null" style="border: 1px dashed"> | ||||
|             <!-- 包裹详情 --> | ||||
|             <el-descriptions class="m-5"> | ||||
|               <el-descriptions-item | ||||
|                 v-for="(pkgChild, pkgCIdx) in group.children" | ||||
|                 v-bind="pkgChild.childProps" | ||||
|                 :key="`pkgChild_${pkgCIdx}`" | ||||
|                 :label="pkgChild.label" | ||||
|               > | ||||
|                 <!-- 包裹商品列表 --> | ||||
|                 <template v-if="pkgChild.valueKey === 'goodsList' && pkgInfo[pkgChild.valueKey]"> | ||||
|                   <div | ||||
|                     v-for="(goodInfo, goodInfoIdx) in pkgInfo[pkgChild.valueKey]" | ||||
|                     :key="`goodInfo_${goodInfoIdx}`" | ||||
|                     style="display: flex" | ||||
|                   > | ||||
|                     <el-image | ||||
|                       style="width: 100px; height: 100px; flex: none" | ||||
|                       :src="goodInfo.imgUrl" | ||||
|                     /> | ||||
|                     <el-descriptions :column="1"> | ||||
|                       <el-descriptions-item labelClassName="no-colon">{{ | ||||
|                         goodInfo.name | ||||
|                       }}</el-descriptions-item> | ||||
|                       <el-descriptions-item label="数量">{{ goodInfo.count }}</el-descriptions-item> | ||||
|                     </el-descriptions> | ||||
|                   </div> | ||||
|                 </template> | ||||
| 
 | ||||
|                 <!-- 包裹物流详情 --> | ||||
|                 <template v-else-if="pkgChild.valueKey === 'wlxq'"> | ||||
|                   <el-row :gutter="10"> | ||||
|                     <el-col :span="6" :offset="1"> | ||||
|                       <el-timeline> | ||||
|                         <el-timeline-item | ||||
|                           v-for="(activity, index) in pkgInfo[pkgChild.valueKey]" | ||||
|                           :key="index" | ||||
|                           :timestamp="activity.timestamp" | ||||
|                         > | ||||
|                           {{ activity.content }} | ||||
|                         </el-timeline-item> | ||||
|                       </el-timeline> | ||||
|                     </el-col> | ||||
|                   </el-row> | ||||
|                 </template> | ||||
|                 <template v-else> | ||||
|                   {{ pkgInfo[pkgChild.valueKey] }} | ||||
|                 </template> | ||||
|               </el-descriptions-item> | ||||
|             </el-descriptions> | ||||
|           </div> | ||||
|         </el-descriptions-item> | ||||
|       </el-descriptions> | ||||
|     </div> | ||||
|   </ContentWrap> | ||||
| </template> | ||||
| <script lang="ts" name="TradeOrderDetail" setup> | ||||
| // TODO @xiaobai:在 order 下创建一个 order/detail,然后改名为 index.vue | ||||
| import { DICT_TYPE } from '@/utils/dict' | ||||
| import * as TradeOrderApi from '@/api/mall/trade/order' | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
| const { query } = useRoute() | ||||
| const queryParams = reactive({ | ||||
|   id: query.id | ||||
| }) | ||||
| const dialogVisible = ref(false) | ||||
| const loading = ref(false) | ||||
| const order = ref<any>({ | ||||
|   items: [], | ||||
|   user: {} | ||||
| }) // 详情数据 | ||||
| 
 | ||||
| const detailGroups = ref([ | ||||
|   { | ||||
|     title: '物流信息', | ||||
|     key: 'expressInfo', | ||||
|     children: [ | ||||
|       { label: '发货时间: ', valueKey: 'fhsj' }, | ||||
|       { label: '物流公司: ', valueKey: 'wlgs' }, | ||||
|       { label: '运单号: ', valueKey: 'ydh' }, | ||||
|       { label: '物流状态: ', valueKey: 'wlzt', childProps: { span: 3 } }, | ||||
|       { label: '物流详情: ', valueKey: 'wlxq' } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     title: '订单操作日志', | ||||
|     key: 'orderLog' | ||||
|   } | ||||
| ]) | ||||
| 
 | ||||
| const detailInfo = ref({ | ||||
|   expressInfo: | ||||
|     // 物流信息 | ||||
|     { | ||||
|       label: '包裹1', | ||||
|       name: 'bg1', | ||||
|       fhsj: '2022-11-03 16:50:45', | ||||
|       wlgs: '极兔', | ||||
|       ydh: '2132123', | ||||
|       wlzt: '不支持此快递公司', | ||||
|       wlxq: [ | ||||
|         { | ||||
|           content: '正在派送途中,请您准备签收(派件人:王涛,电话:13854563814)', | ||||
|           timestamp: '2018-04-15 15:00:16' | ||||
|         }, | ||||
|         { | ||||
|           content: '快件到达 【烟台龙口东江村委营业点】', | ||||
|           timestamp: '2018-04-13 14:54:19' | ||||
|         }, | ||||
|         { | ||||
|           content: '快件已发车', | ||||
|           timestamp: '2018-04-11 12:55:52' | ||||
|         }, | ||||
|         { | ||||
|           content: '快件已发车', | ||||
|           timestamp: '2018-04-11 12:55:52' | ||||
|         }, | ||||
|         { | ||||
|           content: '快件已发车', | ||||
|           timestamp: '2018-04-11 12:55:52' | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|   orderLog: [ | ||||
|     // 订单操作日志 | ||||
|     { | ||||
|       content: '买家【乌鸦】关闭了订单', | ||||
|       timestamp: '2018-04-15 15:00:16' | ||||
|     }, | ||||
|     { | ||||
|       content: '买家【乌鸦】下单了', | ||||
|       timestamp: '2018-04-15 15:00:16' | ||||
|     } | ||||
|   ], | ||||
|   goodsInfo: [] // 商品详情tableData | ||||
| }) | ||||
| // 暂考虑一次性加载详情页面所有数据 TODO | ||||
| const getlist = async () => { | ||||
|   dialogVisible.value = true | ||||
|   loading.value = true | ||||
|   try { | ||||
|     const res = await TradeOrderApi.getOrderDetail(queryParams.id as unknown as number) | ||||
|     order.value = res | ||||
|     console.log(order) | ||||
|   } catch { | ||||
|     message.error('获取详情数据失败') | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
| onMounted(async () => { | ||||
|   await getlist() | ||||
| }) | ||||
| const clipboardSuccess = () => { | ||||
|   message.success('复制成功') | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| :deep(.el-descriptions) { | ||||
|   &:not(:nth-child(1)) { | ||||
|     margin-top: 20px; | ||||
|   } | ||||
| 
 | ||||
|   .el-descriptions__title { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
| 
 | ||||
|     &::before { | ||||
|       display: inline-block; | ||||
|       width: 3px; | ||||
|       height: 20px; | ||||
|       margin-right: 10px; | ||||
|       background-color: #409eff; | ||||
|       content: ''; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .el-descriptions-item__container { | ||||
|     margin: 0 10px; | ||||
| 
 | ||||
|     .no-colon { | ||||
|       margin: 0; | ||||
| 
 | ||||
|       &::after { | ||||
|         content: ''; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| <template> | ||||
|   <ContentWrap> | ||||
|     <!-- 订单信息 --> | ||||
|     <el-descriptions title="订单信息"> | ||||
|       <el-descriptions-item label="订单号: ">{{ orderInfo.no }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="配送方式: "> | ||||
|         <dict-tag :type="DICT_TYPE.TRADE_DELIVERY_TYPE" :value="orderInfo.deliveryType" /> | ||||
|       </el-descriptions-item> | ||||
|       <!-- TODO 营销活动待实现     --> | ||||
|       <el-descriptions-item label="营销活动: ">秒杀活动</el-descriptions-item> | ||||
|       <el-descriptions-item label="订单类型: "> | ||||
|         <dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="orderInfo.type" /> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="收货人: ">{{ orderInfo.receiverName }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="买家留言: ">{{ orderInfo.userRemark }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="订单来源: "> | ||||
|         <dict-tag :type="DICT_TYPE.TERMINAL" :value="orderInfo.terminal" /> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="联系电话: ">{{ orderInfo.receiverMobile }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="商家备注: ">{{ orderInfo.remark }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="支付单号: ">{{ orderInfo.payOrderId }}</el-descriptions-item> | ||||
|       <el-descriptions-item label="付款方式: "> | ||||
|         <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="orderInfo.payChannelCode" /> | ||||
|       </el-descriptions-item> | ||||
|       <!-- <el-descriptions-item label="买家: ">{{ orderInfo.user.nickname }}</el-descriptions-item> --> | ||||
|       <!-- TODO 芋艿:待实现:跳转会员 --> | ||||
|       <el-descriptions-item label="收货地址: "> | ||||
|         {{ orderInfo.receiverAreaName }} {{ orderInfo.receiverDetailAddress }} | ||||
|         <el-link | ||||
|           v-clipboard:copy="orderInfo.receiverAreaName + ' ' + orderInfo.receiverDetailAddress" | ||||
|           v-clipboard:success="clipboardSuccess" | ||||
|           icon="ep:document-copy" | ||||
|           type="primary" | ||||
|         /> | ||||
|       </el-descriptions-item> | ||||
|     </el-descriptions> | ||||
| 
 | ||||
|     <!-- 订单状态 --> | ||||
|     <el-descriptions :column="1" title="订单状态"> | ||||
|       <el-descriptions-item label="订单状态: "> | ||||
|         <dict-tag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="orderInfo.status" /> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label-class-name="no-colon"> | ||||
|         <el-button type="primary" @click="openForm('updatePrice')">调整价格</el-button> | ||||
|         <el-button type="primary" @click="openForm('remark')">备注</el-button> | ||||
|         <el-button type="primary" @click="openForm('delivery')">发货</el-button> | ||||
|         <el-button type="primary" @click="openForm('updateAddress')">修改地址</el-button> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item> | ||||
|         <template #label><span style="color: red">提醒: </span></template> | ||||
|         买家付款成功后,货款将直接进入您的商户号(微信、支付宝)<br /> | ||||
|         请及时关注你发出的包裹状态,确保可以配送至买家手中 <br /> | ||||
|         如果买家表示没收到货或货物有问题,请及时联系买家处理,友好协商 | ||||
|       </el-descriptions-item> | ||||
|     </el-descriptions> | ||||
| 
 | ||||
|     <!-- 商品信息 --> | ||||
|     <el-descriptions title="商品信息"> | ||||
|       <el-descriptions-item labelClassName="no-colon"> | ||||
|         <el-row :gutter="20"> | ||||
|           <el-col :span="15"> | ||||
|             <el-table :data="orderInfo.items" border> | ||||
|               <el-table-column label="商品" prop="spuName" width="auto"> | ||||
|                 <template #default="{ row }"> | ||||
|                   {{ row.spuName }} | ||||
|                   <el-tag v-for="property in row.properties" :key="property.propertyId"> | ||||
|                     {{ property.propertyName }}: {{ property.valueName }} | ||||
|                   </el-tag> | ||||
|                 </template> | ||||
|               </el-table-column> | ||||
|               <el-table-column label="商品原价" prop="price" width="150"> | ||||
|                 <template #default="{ row }">{{ floatToFixed2(row.price) }}元</template> | ||||
|               </el-table-column> | ||||
|               <el-table-column label="数量" prop="count" width="100" /> | ||||
|               <el-table-column label="合计" prop="payPrice" width="150"> | ||||
|                 <template #default="{ row }">{{ floatToFixed2(row.payPrice) }}元</template> | ||||
|               </el-table-column> | ||||
|               <el-table-column label="售后状态" prop="afterSaleStatus" width="120"> | ||||
|                 <template #default="{ row }"> | ||||
|                   <dict-tag | ||||
|                     :type="DICT_TYPE.TRADE_ORDER_ITEM_AFTER_SALE_STATUS" | ||||
|                     :value="row.afterSaleStatus" | ||||
|                   /> | ||||
|                 </template> | ||||
|               </el-table-column> | ||||
|             </el-table> | ||||
|           </el-col> | ||||
|           <el-col :span="10" /> | ||||
|         </el-row> | ||||
|       </el-descriptions-item> | ||||
|     </el-descriptions> | ||||
|     <el-descriptions :column="6"> | ||||
|       <el-descriptions-item label="商品总额: "> | ||||
|         {{ floatToFixed2(orderInfo.totalPrice) }}元 | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="运费金额: "> | ||||
|         {{ floatToFixed2(orderInfo.deliveryPrice) }}元 | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="订单调价: "> | ||||
|         {{ floatToFixed2(orderInfo.updatePrice) }}元 | ||||
|       </el-descriptions-item> | ||||
| 
 | ||||
|       <el-descriptions-item> | ||||
|         <template #label><span style="color: red">商品优惠: </span></template> | ||||
|         {{ floatToFixed2(orderInfo.couponPrice) }}元 | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item> | ||||
|         <template #label><span style="color: red">订单优惠: </span></template> | ||||
|         {{ floatToFixed2(orderInfo.discountPrice) }}元 | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item> | ||||
|         <template #label><span style="color: red">积分抵扣: </span></template> | ||||
|         {{ floatToFixed2(orderInfo.pointPrice) }}元 | ||||
|       </el-descriptions-item> | ||||
| 
 | ||||
|       <el-descriptions-item v-for="item in 5" :key="item" label-class-name="no-colon" /> | ||||
|       <!-- 占位 --> | ||||
|       <el-descriptions-item label="应付金额: "> | ||||
|         {{ floatToFixed2(orderInfo.payPrice) }}元 | ||||
|       </el-descriptions-item> | ||||
|     </el-descriptions> | ||||
| 
 | ||||
|     <!-- TODO 芋艿:需要改改 --> | ||||
|     <div v-for="group in detailGroups" :key="group.title"> | ||||
|       <el-descriptions :title="group.title" v-bind="group.groupProps"> | ||||
|         <!-- 订单操作日志 --> | ||||
|         <el-descriptions-item v-if="group.key === 'orderLog'" labelClassName="no-colon"> | ||||
|           <el-timeline> | ||||
|             <el-timeline-item | ||||
|               v-for="activity in detailInfo[group.key]" | ||||
|               :key="activity.timestamp" | ||||
|               :timestamp="activity.timestamp" | ||||
|             > | ||||
|               {{ activity.content }} | ||||
|             </el-timeline-item> | ||||
|           </el-timeline> | ||||
|         </el-descriptions-item> | ||||
| 
 | ||||
|         <!-- 物流信息 TODO 等物流接口搞定重构一下 --> | ||||
|         <!-- TODO @xiaobai:改成一个包裹哈;目前只允许发货一次 --> | ||||
|         <el-descriptions-item v-if="group.key === 'expressInfo'" labelClassName="no-colon"> | ||||
|           <!-- 循环包裹物流信息 --> | ||||
|           <div v-show="(pkgInfo = detailInfo[group.key]) !== null" style="border: 1px dashed"> | ||||
|             <!-- 包裹详情 --> | ||||
|             <el-descriptions class="m-5"> | ||||
|               <el-descriptions-item | ||||
|                 v-for="(pkgChild, pkgCIdx) in group.children" | ||||
|                 :key="`pkgChild_${pkgCIdx}`" | ||||
|                 :label="pkgChild.label" | ||||
|                 v-bind="pkgChild.childProps" | ||||
|               > | ||||
|                 <!-- 包裹商品列表 --> | ||||
|                 <template v-if="pkgChild.valueKey === 'goodsList' && pkgInfo[pkgChild.valueKey]"> | ||||
|                   <div | ||||
|                     v-for="(goodInfo, goodInfoIdx) in pkgInfo[pkgChild.valueKey]" | ||||
|                     :key="`goodInfo_${goodInfoIdx}`" | ||||
|                     style="display: flex" | ||||
|                   > | ||||
|                     <el-image | ||||
|                       :src="goodInfo.imgUrl" | ||||
|                       style="width: 100px; height: 100px; flex: none" | ||||
|                     /> | ||||
|                     <el-descriptions :column="1"> | ||||
|                       <el-descriptions-item labelClassName="no-colon" | ||||
|                         >{{ goodInfo.name }} | ||||
|                       </el-descriptions-item> | ||||
|                       <el-descriptions-item label="数量" | ||||
|                         >{{ goodInfo.count }} | ||||
|                       </el-descriptions-item> | ||||
|                     </el-descriptions> | ||||
|                   </div> | ||||
|                 </template> | ||||
| 
 | ||||
|                 <!-- 包裹物流详情 --> | ||||
|                 <template v-else-if="pkgChild.valueKey === 'wlxq'"> | ||||
|                   <el-row :gutter="10"> | ||||
|                     <el-col :offset="1" :span="6"> | ||||
|                       <el-timeline> | ||||
|                         <el-timeline-item | ||||
|                           v-for="(activity, index) in pkgInfo[pkgChild.valueKey]" | ||||
|                           :key="index" | ||||
|                           :timestamp="activity.timestamp" | ||||
|                         > | ||||
|                           {{ activity.content }} | ||||
|                         </el-timeline-item> | ||||
|                       </el-timeline> | ||||
|                     </el-col> | ||||
|                   </el-row> | ||||
|                 </template> | ||||
|                 <template v-else> | ||||
|                   {{ pkgInfo[pkgChild.valueKey] }} | ||||
|                 </template> | ||||
|               </el-descriptions-item> | ||||
|             </el-descriptions> | ||||
|           </div> | ||||
|         </el-descriptions-item> | ||||
|       </el-descriptions> | ||||
|     </div> | ||||
|   </ContentWrap> | ||||
| 
 | ||||
|   <!-- 各种操作的弹窗 --> | ||||
|   <OrderDeliveryForm ref="deliveryFormRef" @success="getDetail" /> | ||||
|   <OrderUpdateRemarkForm ref="updateRemarkForm" @success="getDetail" /> | ||||
|   <OrderUpdateAddressForm ref="updateAddressFormRef" @success="getDetail" /> | ||||
|   <OrderUpdatePriceForm ref="updatePriceFormRef" @success="getDetail" /> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import * as TradeOrderApi from '@/api/mall/trade/order' | ||||
| import { floatToFixed2 } from '@/utils' | ||||
| import { DICT_TYPE } from '@/utils/dict' | ||||
| import OrderUpdateRemarkForm from '@/views/mall/trade/order/components/OrderUpdateRemarkForm.vue' | ||||
| import OrderDeliveryForm from '@/views/mall/trade/order/components/OrderDeliveryForm.vue' | ||||
| import OrderUpdateAddressForm from '@/views/mall/trade/order/components/OrderUpdateAddressForm.vue' | ||||
| import OrderUpdatePriceForm from '@/views/mall/trade/order/components/OrderUpdatePriceForm.vue' | ||||
| 
 | ||||
| defineOptions({ name: 'TradeOrderDetailForm' }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { params } = useRoute() // 查询参数 | ||||
| const orderInfo = ref<TradeOrderApi.OrderVO>({}) | ||||
| 
 | ||||
| // TODO @puhui999:这个改成直接读属性,不用按照这种写法;后续再改 | ||||
| const detailGroups = ref([ | ||||
|   { | ||||
|     title: '物流信息', | ||||
|     key: 'expressInfo', | ||||
|     children: [ | ||||
|       { label: '发货时间: ', valueKey: 'fhsj' }, | ||||
|       { label: '物流公司: ', valueKey: 'wlgs' }, | ||||
|       { label: '运单号: ', valueKey: 'ydh' }, | ||||
|       { label: '物流状态: ', valueKey: 'wlzt', childProps: { span: 3 } }, | ||||
|       { label: '物流详情: ', valueKey: 'wlxq' } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     title: '订单操作日志', | ||||
|     key: 'orderLog' | ||||
|   } | ||||
| ]) | ||||
| 
 | ||||
| // TODO @puhui999:从后台读数据哈。后续再改 | ||||
| const detailInfo = ref({ | ||||
|   // 物流信息 | ||||
|   expressInfo: { | ||||
|     label: '包裹1', | ||||
|     name: 'bg1', | ||||
|     fhsj: '2022-11-03 16:50:45', | ||||
|     wlgs: '极兔', | ||||
|     ydh: '2132123', | ||||
|     wlzt: '不支持此快递公司', | ||||
|     wlxq: [ | ||||
|       { | ||||
|         content: '正在派送途中,请您准备签收(派件人:王涛,电话:13854563814)', | ||||
|         timestamp: '2018-04-15 15:00:16' | ||||
|       }, | ||||
|       { | ||||
|         content: '快件到达 【烟台龙口东江村委营业点】', | ||||
|         timestamp: '2018-04-13 14:54:19' | ||||
|       }, | ||||
|       { | ||||
|         content: '快件已发车', | ||||
|         timestamp: '2018-04-11 12:55:52' | ||||
|       }, | ||||
|       { | ||||
|         content: '快件已发车', | ||||
|         timestamp: '2018-04-11 12:55:52' | ||||
|       }, | ||||
|       { | ||||
|         content: '快件已发车', | ||||
|         timestamp: '2018-04-11 12:55:52' | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   orderLog: [ | ||||
|     // 订单操作日志 | ||||
|     { | ||||
|       content: '买家【乌鸦】关闭了订单', | ||||
|       timestamp: '2018-04-15 15:00:16' | ||||
|     }, | ||||
|     { | ||||
|       content: '买家【乌鸦】下单了', | ||||
|       timestamp: '2018-04-15 15:00:16' | ||||
|     } | ||||
|   ], | ||||
|   goodsInfo: [] // 商品详情tableData | ||||
| }) | ||||
| 
 | ||||
| const deliveryFormRef = ref() // 发货表单 Ref | ||||
| const updateRemarkForm = ref() // 订单备注表单 Ref | ||||
| const updateAddressFormRef = ref() // 收货地址表单 Ref | ||||
| const updatePriceFormRef = ref() // 订单调价表单 Ref | ||||
| const openForm = (type: string) => { | ||||
|   switch (type) { | ||||
|     case 'remark': | ||||
|       updateRemarkForm.value?.open(orderInfo.value) | ||||
|       break | ||||
|     case 'delivery': | ||||
|       deliveryFormRef.value?.open(orderInfo.value) | ||||
|       break | ||||
|     case 'updateAddress': | ||||
|       updateAddressFormRef.value?.open(orderInfo.value) | ||||
|       break | ||||
|     case 'updatePrice': | ||||
|       updatePriceFormRef.value?.open(orderInfo.value) | ||||
|       break | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 获得详情 */ | ||||
| const getDetail = async () => { | ||||
|   const id = params.orderId as unknown as number | ||||
|   if (id) { | ||||
|     const res = (await TradeOrderApi.getOrder(id)) as TradeOrderApi.OrderVO | ||||
|     orderInfo.value = res | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| onMounted(async () => { | ||||
|   await getDetail() | ||||
| }) | ||||
| 
 | ||||
| const clipboardSuccess = () => { | ||||
|   message.success('复制成功') | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| :deep(.el-descriptions) { | ||||
|   &:not(:nth-child(1)) { | ||||
|     margin-top: 20px; | ||||
|   } | ||||
| 
 | ||||
|   .el-descriptions__title { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
| 
 | ||||
|     &::before { | ||||
|       display: inline-block; | ||||
|       width: 3px; | ||||
|       height: 20px; | ||||
|       margin-right: 10px; | ||||
|       background-color: #409eff; | ||||
|       content: ''; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .el-descriptions-item__container { | ||||
|     margin: 0 10px; | ||||
| 
 | ||||
|     .no-colon { | ||||
|       margin: 0; | ||||
| 
 | ||||
|       &::after { | ||||
|         content: ''; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,112 @@ | |||
| <template> | ||||
|   <Dialog :title="dialogTitle" v-model="dialogVisible" width="600"> | ||||
|     <el-form | ||||
|       ref="formRef" | ||||
|       :model="formData" | ||||
|       :rules="formRules" | ||||
|       label-width="100px" | ||||
|       v-loading="formLoading" | ||||
|     > | ||||
|       <el-form-item label="名称" prop="name"> | ||||
|         <el-input v-model="formData.name" placeholder="请输入名称" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="状态" prop="status"> | ||||
|         <el-radio-group v-model="formData.status"> | ||||
|           <el-radio | ||||
|             v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" | ||||
|             :key="dict.value" | ||||
|             :label="dict.value" | ||||
|           > | ||||
|             {{ dict.label }} | ||||
|           </el-radio> | ||||
|         </el-radio-group> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="备注" prop="remark"> | ||||
|         <el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|     <template #footer> | ||||
|       <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| import * as GroupApi from '@/api/member/group' | ||||
| import { CommonStatusEnum } from '@/utils/constants' | ||||
| 
 | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const dialogTitle = ref('') // 弹窗的标题 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formType = ref('') // 表单的类型:create - 新增;update - 修改 | ||||
| const formData = ref({ | ||||
|   id: undefined, | ||||
|   name: undefined, | ||||
|   remark: undefined, | ||||
|   status: CommonStatusEnum.ENABLE | ||||
| }) | ||||
| const formRules = reactive({ | ||||
|   name: [{ required: true, message: '名称不能为空', trigger: 'blur' }], | ||||
|   status: [{ required: true, message: '状态不能为空', trigger: 'blur' }] | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (type: string, id?: number) => { | ||||
|   dialogVisible.value = true | ||||
|   dialogTitle.value = t('action.' + type) | ||||
|   formType.value = type | ||||
|   resetForm() | ||||
|   // 修改时,设置数据 | ||||
|   if (id) { | ||||
|     formLoading.value = true | ||||
|     try { | ||||
|       formData.value = await GroupApi.getGroup(id) | ||||
|     } finally { | ||||
|       formLoading.value = false | ||||
|     } | ||||
|   } | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 提交表单 */ | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitForm = async () => { | ||||
|   // 校验表单 | ||||
|   if (!formRef) return | ||||
|   const valid = await formRef.value.validate() | ||||
|   if (!valid) return | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     const data = formData.value as unknown as GroupApi.GroupVO | ||||
|     if (formType.value === 'create') { | ||||
|       await GroupApi.createGroup(data) | ||||
|       message.success(t('common.createSuccess')) | ||||
|     } else { | ||||
|       await GroupApi.updateGroup(data) | ||||
|       message.success(t('common.updateSuccess')) | ||||
|     } | ||||
|     dialogVisible.value = false | ||||
|     // 发送操作成功的事件 | ||||
|     emit('success') | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
|     id: undefined, | ||||
|     name: undefined, | ||||
|     remark: undefined, | ||||
|     status: CommonStatusEnum.ENABLE | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
| </script> | ||||
|  | @ -0,0 +1,45 @@ | |||
| <template> | ||||
|   <el-select v-model="groupId" placeholder="请选择用户分组" clearable class="!w-240px"> | ||||
|     <el-option | ||||
|       v-for="group in groupOptions" | ||||
|       :key="group.id" | ||||
|       :label="group.name" | ||||
|       :value="group.id" | ||||
|     /> | ||||
|   </el-select> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import * as GroupApi from '@/api/member/group' | ||||
| 
 | ||||
| /** 会员分组选择框 **/ | ||||
| defineOptions({ name: 'MemberGroupSelect' }) | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   /** 下拉框选中值 **/ | ||||
|   modelValue: { | ||||
|     type: Number, | ||||
|     default: undefined | ||||
|   } | ||||
| }) | ||||
| const emit = defineEmits(['update:modelValue']) | ||||
| 
 | ||||
| const groupId = computed({ | ||||
|   get() { | ||||
|     return props.modelValue | ||||
|   }, | ||||
|   set(value: any) { | ||||
|     emit('update:modelValue', value) | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| const groupOptions = ref<GroupApi.GroupVO[]>([]) | ||||
| 
 | ||||
| const getList = async () => { | ||||
|   groupOptions.value = await GroupApi.getSimpleGroupList() | ||||
| } | ||||
| 
 | ||||
| /** 初始化 */ | ||||
| onMounted(() => { | ||||
|   getList() | ||||
| }) | ||||
| </script> | ||||
|  | @ -0,0 +1,174 @@ | |||
| <template> | ||||
|   <ContentWrap> | ||||
|     <!-- 搜索工作栏 --> | ||||
|     <el-form | ||||
|       class="-mb-15px" | ||||
|       :model="queryParams" | ||||
|       ref="queryFormRef" | ||||
|       :inline="true" | ||||
|       label-width="68px" | ||||
|     > | ||||
|       <el-form-item label="分组名称" prop="name"> | ||||
|         <el-input | ||||
|           v-model="queryParams.name" | ||||
|           placeholder="请输入分组名称" | ||||
|           clearable | ||||
|           @keyup.enter="handleQuery" | ||||
|           class="!w-240px" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="状态" prop="status"> | ||||
|         <el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px"> | ||||
|           <el-option | ||||
|             v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" | ||||
|             :key="dict.value" | ||||
|             :label="dict.label" | ||||
|             :value="dict.value" | ||||
|           /> | ||||
|         </el-select> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="创建时间" prop="createTime"> | ||||
|         <el-date-picker | ||||
|           v-model="queryParams.createTime" | ||||
|           value-format="YYYY-MM-DD HH:mm:ss" | ||||
|           type="daterange" | ||||
|           start-placeholder="开始日期" | ||||
|           end-placeholder="结束日期" | ||||
|           :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" | ||||
|           class="!w-240px" | ||||
|         /> | ||||
|       </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 type="primary" @click="openForm('create')" v-hasPermi="['member:group:create']"> | ||||
|           <Icon icon="ep:plus" class="mr-5px" /> 新增 | ||||
|         </el-button> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|   </ContentWrap> | ||||
| 
 | ||||
|   <!-- 列表 --> | ||||
|   <ContentWrap> | ||||
|     <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> | ||||
|       <el-table-column label="编号" align="center" prop="id" min-width="60" /> | ||||
|       <el-table-column label="名称" align="center" prop="name" min-width="80" /> | ||||
|       <el-table-column label="备注" align="center" prop="remark" min-width="100" /> | ||||
|       <el-table-column label="状态" align="center" prop="status" min-width="70"> | ||||
|         <template #default="scope"> | ||||
|           <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column | ||||
|         label="创建时间" | ||||
|         align="center" | ||||
|         prop="createTime" | ||||
|         :formatter="dateFormatter" | ||||
|         min-width="170" | ||||
|       /> | ||||
|       <el-table-column label="操作" align="center" width="150px"> | ||||
|         <template #default="scope"> | ||||
|           <el-button | ||||
|             link | ||||
|             type="primary" | ||||
|             @click="openForm('update', scope.row.id)" | ||||
|             v-hasPermi="['member:group:update']" | ||||
|           > | ||||
|             编辑 | ||||
|           </el-button> | ||||
|           <el-button | ||||
|             link | ||||
|             type="danger" | ||||
|             @click="handleDelete(scope.row.id)" | ||||
|             v-hasPermi="['member:group:delete']" | ||||
|           > | ||||
|             删除 | ||||
|           </el-button> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|     <!-- 分页 --> | ||||
|     <Pagination | ||||
|       :total="total" | ||||
|       v-model:page="queryParams.pageNo" | ||||
|       v-model:limit="queryParams.pageSize" | ||||
|       @pagination="getList" | ||||
|     /> | ||||
|   </ContentWrap> | ||||
| 
 | ||||
|   <!-- 表单弹窗:添加/修改 --> | ||||
|   <GroupForm ref="formRef" @success="getList" /> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import * as GroupApi from '@/api/member/group' | ||||
| import GroupForm from './GroupForm.vue' | ||||
| 
 | ||||
| /** 用户分组管理 **/ | ||||
| defineOptions({ name: 'MemberGroup' }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { t } = useI18n() // 国际化 | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref([]) // 列表的数据 | ||||
| const queryParams = reactive({ | ||||
|   pageNo: 1, | ||||
|   pageSize: 10, | ||||
|   name: null, | ||||
|   status: null, | ||||
|   createTime: [] | ||||
| }) | ||||
| const queryFormRef = ref() // 搜索的表单 | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     const data = await GroupApi.getGroupPage(queryParams) | ||||
|     list.value = data.list | ||||
|     total.value = data.total | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.pageNo = 1 | ||||
|   getList() | ||||
| } | ||||
| 
 | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value.resetFields() | ||||
|   handleQuery() | ||||
| } | ||||
| 
 | ||||
| /** 添加/修改操作 */ | ||||
| const formRef = ref() | ||||
| const openForm = (type: string, id?: number) => { | ||||
|   formRef.value.open(type, id) | ||||
| } | ||||
| 
 | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (id: number) => { | ||||
|   try { | ||||
|     // 删除的二次确认 | ||||
|     await message.delConfirm() | ||||
|     // 发起删除 | ||||
|     await GroupApi.deleteGroup(id) | ||||
|     message.success(t('common.delSuccess')) | ||||
|     // 刷新列表 | ||||
|     await getList() | ||||
|   } catch {} | ||||
| } | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(() => { | ||||
|   getList() | ||||
| }) | ||||
| </script> | ||||
|  | @ -0,0 +1,175 @@ | |||
| <template> | ||||
|   <Dialog :title="dialogTitle" v-model="dialogVisible" width="800"> | ||||
|     <el-form | ||||
|       ref="formRef" | ||||
|       :model="formData" | ||||
|       :rules="formRules" | ||||
|       label-width="110px" | ||||
|       v-loading="formLoading" | ||||
|     > | ||||
|       <el-row> | ||||
|         <el-col :span="12"> | ||||
|           <el-form-item label="等级名称" prop="name"> | ||||
|             <el-input v-model="formData.name" placeholder="请输入等级名称" class="!w-240px" /> | ||||
|           </el-form-item> | ||||
|         </el-col> | ||||
|         <el-col :span="12"> | ||||
|           <el-form-item label="等级" prop="level"> | ||||
|             <el-input-number | ||||
|               v-model="formData.level" | ||||
|               :min="0" | ||||
|               :precision="0" | ||||
|               placeholder="请输入等级" | ||||
|               class="!w-240px" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|         </el-col> | ||||
|       </el-row> | ||||
|       <el-row> | ||||
|         <el-col :span="12"> | ||||
|           <el-form-item label="升级经验" prop="experience"> | ||||
|             <el-input-number | ||||
|               v-model="formData.experience" | ||||
|               :min="0" | ||||
|               :precision="0" | ||||
|               placeholder="请输入升级经验" | ||||
|               class="!w-240px" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|         </el-col> | ||||
|         <el-col :span="12"> | ||||
|           <el-form-item label="享受折扣(%)" prop="discountPercent"> | ||||
|             <el-input-number | ||||
|               v-model="formData.discountPercent" | ||||
|               :min="0" | ||||
|               :max="100" | ||||
|               :precision="0" | ||||
|               placeholder="请输入享受折扣" | ||||
|               class="!w-240px" | ||||
|             /> | ||||
|           </el-form-item> | ||||
|         </el-col> | ||||
|       </el-row> | ||||
|       <el-row> | ||||
|         <el-col :span="12"> | ||||
|           <el-form-item label="等级图标"> | ||||
|             <UploadImg v-model="formData.icon" /> | ||||
|           </el-form-item> | ||||
|         </el-col> | ||||
|         <el-col :span="12"> | ||||
|           <el-form-item label="背景图"> | ||||
|             <UploadImg v-model="formData.backgroundUrl" /> | ||||
|           </el-form-item> | ||||
|         </el-col> | ||||
|       </el-row> | ||||
|       <el-form-item label="状态" prop="status"> | ||||
|         <el-radio-group v-model="formData.status"> | ||||
|           <el-radio | ||||
|             v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" | ||||
|             :key="dict.value" | ||||
|             :label="dict.value" | ||||
|           > | ||||
|             {{ dict.label }} | ||||
|           </el-radio> | ||||
|         </el-radio-group> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|     <template #footer> | ||||
|       <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| import * as LevelApi from '@/api/member/level' | ||||
| import { CommonStatusEnum } from '@/utils/constants' | ||||
| 
 | ||||
| /** 会员等级表单 **/ | ||||
| defineOptions({ name: 'MemberLevelForm' }) | ||||
| 
 | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const dialogTitle = ref('') // 弹窗的标题 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formType = ref('') // 表单的类型:create - 新增;update - 修改 | ||||
| const formData = ref({ | ||||
|   id: undefined, | ||||
|   name: undefined, | ||||
|   experience: undefined, | ||||
|   level: undefined, | ||||
|   discountPercent: undefined, | ||||
|   icon: undefined, | ||||
|   backgroundUrl: undefined, | ||||
|   status: CommonStatusEnum.ENABLE | ||||
| }) | ||||
| const formRules = reactive({ | ||||
|   name: [{ required: true, message: '等级名称不能为空', trigger: 'blur' }], | ||||
|   experience: [{ required: true, message: '升级经验不能为空', trigger: 'blur' }], | ||||
|   level: [{ required: true, message: '等级不能为空', trigger: 'blur' }], | ||||
|   discountPercent: [{ required: true, message: '享受折扣不能为空', trigger: 'blur' }], | ||||
|   status: [{ required: true, message: '状态不能为空', trigger: 'change' }] | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (type: string, id?: number) => { | ||||
|   dialogVisible.value = true | ||||
|   dialogTitle.value = t('action.' + type) | ||||
|   formType.value = type | ||||
|   resetForm() | ||||
|   // 修改时,设置数据 | ||||
|   if (id) { | ||||
|     formLoading.value = true | ||||
|     try { | ||||
|       formData.value = await LevelApi.getLevel(id) | ||||
|     } finally { | ||||
|       formLoading.value = false | ||||
|     } | ||||
|   } | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 提交表单 */ | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitForm = async () => { | ||||
|   // 校验表单 | ||||
|   if (!formRef) return | ||||
|   const valid = await formRef.value.validate() | ||||
|   if (!valid) return | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     const data = formData.value as unknown as LevelApi.LevelVO | ||||
|     if (formType.value === 'create') { | ||||
|       await LevelApi.createLevel(data) | ||||
|       message.success(t('common.createSuccess')) | ||||
|     } else { | ||||
|       await LevelApi.updateLevel(data) | ||||
|       message.success(t('common.updateSuccess')) | ||||
|     } | ||||
|     dialogVisible.value = false | ||||
|     // 发送操作成功的事件 | ||||
|     emit('success') | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
|     id: undefined, | ||||
|     name: undefined, | ||||
|     experience: undefined, | ||||
|     level: undefined, | ||||
|     discountPercent: undefined, | ||||
|     icon: undefined, | ||||
|     backgroundUrl: undefined, | ||||
|     status: CommonStatusEnum.ENABLE | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
| </script> | ||||
|  | @ -0,0 +1,45 @@ | |||
| <template> | ||||
|   <el-select v-model="levelId" placeholder="请选择用户等级" clearable class="!w-240px"> | ||||
|     <el-option v-for="level in levelOptions" :key="level.id" :label="level.name" :value="level.id"> | ||||
|       <span class="flex items-center gap-x-8px"> | ||||
|         <el-avatar :src="level.icon" size="small" /> | ||||
|         {{ level.name }} | ||||
|       </span> | ||||
|     </el-option> | ||||
|   </el-select> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import * as LevelApi from '@/api/member/level' | ||||
| 
 | ||||
| /** 会员等级选择框 **/ | ||||
| defineOptions({ name: 'MemberLevelSelect' }) | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   /** 下拉框选中值 **/ | ||||
|   modelValue: { | ||||
|     type: Number, | ||||
|     default: undefined | ||||
|   } | ||||
| }) | ||||
| const emit = defineEmits(['update:modelValue']) | ||||
| 
 | ||||
| const levelId = computed({ | ||||
|   get() { | ||||
|     return props.modelValue | ||||
|   }, | ||||
|   set(value: any) { | ||||
|     emit('update:modelValue', value) | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| const levelOptions = ref<LevelApi.LevelVO[]>([]) | ||||
| 
 | ||||
| const getList = async () => { | ||||
|   levelOptions.value = await LevelApi.getSimpleLevelList() | ||||
| } | ||||
| 
 | ||||
| /** 初始化 */ | ||||
| onMounted(() => { | ||||
|   getList() | ||||
| }) | ||||
| </script> | ||||
|  | @ -0,0 +1,169 @@ | |||
| <template> | ||||
|   <ContentWrap> | ||||
|     <!-- 搜索工作栏 --> | ||||
|     <el-form | ||||
|       class="-mb-15px" | ||||
|       :model="queryParams" | ||||
|       ref="queryFormRef" | ||||
|       :inline="true" | ||||
|       label-width="68px" | ||||
|     > | ||||
|       <el-form-item label="等级名称" prop="name"> | ||||
|         <el-input | ||||
|           v-model="queryParams.name" | ||||
|           placeholder="请输入等级名称" | ||||
|           clearable | ||||
|           @keyup.enter="handleQuery" | ||||
|           class="!w-240px" | ||||
|         /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="状态" prop="status"> | ||||
|         <el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px"> | ||||
|           <el-option | ||||
|             v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" | ||||
|             :key="dict.value" | ||||
|             :label="dict.label" | ||||
|             :value="dict.value" | ||||
|           /> | ||||
|         </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 type="primary" @click="openForm('create')" v-hasPermi="['member:level:create']"> | ||||
|           <Icon icon="ep:plus" class="mr-5px" /> 新增 | ||||
|         </el-button> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|   </ContentWrap> | ||||
| 
 | ||||
|   <!-- 列表 --> | ||||
|   <ContentWrap> | ||||
|     <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> | ||||
|       <el-table-column label="编号" align="center" prop="id" min-width="60" /> | ||||
|       <el-table-column label="等级图标" align="center" prop="icon" min-width="80"> | ||||
|         <template #default="scope"> | ||||
|           <el-image | ||||
|             :src="scope.row.icon" | ||||
|             class="w-30px h-30px" | ||||
|             :preview-src-list="[scope.row.icon]" | ||||
|           /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="等级背景图" align="center" prop="backgroundUrl" min-width="100"> | ||||
|         <template #default="scope"> | ||||
|           <el-image | ||||
|             :src="scope.row.backgroundUrl" | ||||
|             class="w-30px h-30px" | ||||
|             :preview-src-list="[scope.row.backgroundUrl]" | ||||
|           /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="等级名称" align="center" prop="name" min-width="100" /> | ||||
|       <el-table-column label="等级" align="center" prop="level" min-width="60" /> | ||||
|       <el-table-column label="升级经验" align="center" prop="experience" min-width="80" /> | ||||
|       <el-table-column label="享受折扣(%)" align="center" prop="discountPercent" min-width="110" /> | ||||
|       <el-table-column label="状态" align="center" prop="status" min-width="70"> | ||||
|         <template #default="scope"> | ||||
|           <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column | ||||
|         label="创建时间" | ||||
|         align="center" | ||||
|         prop="createTime" | ||||
|         :formatter="dateFormatter" | ||||
|         min-width="170" | ||||
|       /> | ||||
|       <el-table-column label="操作" align="center" min-width="110px" fixed="right"> | ||||
|         <template #default="scope"> | ||||
|           <el-button | ||||
|             link | ||||
|             type="primary" | ||||
|             @click="openForm('update', scope.row.id)" | ||||
|             v-hasPermi="['member:level:update']" | ||||
|           > | ||||
|             编辑 | ||||
|           </el-button> | ||||
|           <el-button | ||||
|             link | ||||
|             type="danger" | ||||
|             @click="handleDelete(scope.row.id)" | ||||
|             v-hasPermi="['member:level:delete']" | ||||
|           > | ||||
|             删除 | ||||
|           </el-button> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|   </ContentWrap> | ||||
| 
 | ||||
|   <!-- 表单弹窗:添加/修改 --> | ||||
|   <LevelForm ref="formRef" @success="getList" /> | ||||
| </template> | ||||
| 
 | ||||
| <script setup lang="ts"> | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import * as LevelApi from '@/api/member/level' | ||||
| import LevelForm from './LevelForm.vue' | ||||
| 
 | ||||
| /** 会员等级管理 **/ | ||||
| defineOptions({ name: 'MemberLevel' }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { t } = useI18n() // 国际化 | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const list = ref([]) // 列表的数据 | ||||
| const queryParams = reactive({ | ||||
|   name: null, | ||||
|   status: null | ||||
| }) | ||||
| const queryFormRef = ref() // 搜索的表单 | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     list.value = await LevelApi.getLevelList(queryParams) | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   getList() | ||||
| } | ||||
| 
 | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value.resetFields() | ||||
|   handleQuery() | ||||
| } | ||||
| 
 | ||||
| /** 添加/修改操作 */ | ||||
| const formRef = ref() | ||||
| const openForm = (type: string, id?: number) => { | ||||
|   formRef.value.open(type, id) | ||||
| } | ||||
| 
 | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (id: number) => { | ||||
|   try { | ||||
|     // 删除的二次确认 | ||||
|     await message.delConfirm() | ||||
|     // 发起删除 | ||||
|     await LevelApi.deleteLevel(id) | ||||
|     message.success(t('common.delSuccess')) | ||||
|     // 刷新列表 | ||||
|     await getList() | ||||
|   } catch {} | ||||
| } | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(() => { | ||||
|   getList() | ||||
| }) | ||||
| </script> | ||||
|  | @ -52,8 +52,7 @@ const tagIds = computed({ | |||
| const tags = ref<TagApi.TagVO[]>([]) | ||||
| 
 | ||||
| const getList = async () => { | ||||
|   const data = await TagApi.getMemberTagPage({}) | ||||
|   tags.value = data.list | ||||
|   tags.value = await TagApi.getSimpleTagList() | ||||
| } | ||||
| 
 | ||||
| /** 添加用户标签表单弹框 */ | ||||
|  |  | |||
|  | @ -0,0 +1,101 @@ | |||
| <template> | ||||
|   <Dialog title="修改用户等级" v-model="dialogVisible" width="600"> | ||||
|     <el-form | ||||
|       ref="formRef" | ||||
|       :model="formData" | ||||
|       :rules="formRules" | ||||
|       label-width="100px" | ||||
|       v-loading="formLoading" | ||||
|     > | ||||
|       <el-form-item label="用户编号" prop="id"> | ||||
|         <el-input v-model="formData.id" placeholder="请输入用户昵称" class="!w-240px" disabled /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="用户昵称" prop="nickname"> | ||||
|         <el-input | ||||
|           v-model="formData.nickname" | ||||
|           placeholder="请输入用户昵称" | ||||
|           class="!w-240px" | ||||
|           disabled | ||||
|         /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="用户等级" prop="levelId"> | ||||
|         <MemberLevelSelect v-model="formData.levelId" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="修改原因" prop="reason"> | ||||
|         <el-input type="textarea" v-model="formData.reason" placeholder="请输入修改原因" /> | ||||
|       </el-form-item> | ||||
|     </el-form> | ||||
|     <template #footer> | ||||
|       <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import * as UserApi from '@/api/member/user' | ||||
| import MemberLevelSelect from '@/views/member/level/components/MemberLevelSelect.vue' | ||||
| 
 | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
| const dialogVisible = ref(false) // 弹窗的是否展示 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formData = ref({ | ||||
|   id: undefined, | ||||
|   nickname: undefined, | ||||
|   levelId: undefined, | ||||
|   reason: undefined | ||||
| }) | ||||
| const formRules = reactive({ | ||||
|   reason: [{ required: true, message: '修改原因不能为空', trigger: 'blur' }] | ||||
| }) | ||||
| const formRef = ref() // 表单 Ref | ||||
| 
 | ||||
| /** 打开弹窗 */ | ||||
| const open = async (id?: number) => { | ||||
|   dialogVisible.value = true | ||||
|   resetForm() | ||||
|   // 修改时,设置数据 | ||||
|   if (id) { | ||||
|     formLoading.value = true | ||||
|     try { | ||||
|       formData.value = await UserApi.getUser(id) | ||||
|     } finally { | ||||
|       formLoading.value = false | ||||
|     } | ||||
|   } | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
| 
 | ||||
| /** 提交表单 */ | ||||
| const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||
| const submitForm = async () => { | ||||
|   // 校验表单 | ||||
|   if (!formRef) return | ||||
|   const valid = await formRef.value.validate() | ||||
|   if (!valid) return | ||||
|   // 提交请求 | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     await UserApi.updateUserLevel(formData.value) | ||||
| 
 | ||||
|     message.success(t('common.updateSuccess')) | ||||
|     dialogVisible.value = false | ||||
|     // 发送操作成功的事件 | ||||
|     emit('success') | ||||
|   } finally { | ||||
|     formLoading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 重置表单 */ | ||||
| const resetForm = () => { | ||||
|   formData.value = { | ||||
|     id: undefined, | ||||
|     nickname: undefined, | ||||
|     levelId: undefined, | ||||
|     reason: undefined | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
| </script> | ||||
|  | @ -60,6 +60,9 @@ | |||
|       <el-form-item label="用户标签" prop="tagIds"> | ||||
|         <MemberTagSelect v-model="formData.tagIds" show-add /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="用户分组" prop="groupId"> | ||||
|         <MemberGroupSelect v-model="formData.groupId" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="会员备注" prop="mark"> | ||||
|         <el-input type="textarea" v-model="formData.mark" placeholder="请输入会员备注" /> | ||||
|       </el-form-item> | ||||
|  | @ -76,6 +79,7 @@ import * as UserApi from '@/api/member/user' | |||
| import * as AreaApi from '@/api/system/area' | ||||
| import { defaultProps } from '@/utils/tree' | ||||
| import MemberTagSelect from '@/views/member/tag/components/MemberTagSelect.vue' | ||||
| import MemberGroupSelect from '@/views/member/group/components/MemberGroupSelect.vue' | ||||
| 
 | ||||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
|  | @ -87,6 +91,7 @@ const formType = ref('') // 表单的类型:create - 新增;update - 修改 | |||
| const formData = ref({ | ||||
|   id: undefined, | ||||
|   mobile: undefined, | ||||
|   password: undefined, | ||||
|   status: undefined, | ||||
|   nickname: undefined, | ||||
|   avatar: undefined, | ||||
|  | @ -95,7 +100,8 @@ const formData = ref({ | |||
|   areaId: undefined, | ||||
|   birthday: undefined, | ||||
|   mark: undefined, | ||||
|   tagIds: [] | ||||
|   tagIds: [], | ||||
|   groupId: undefined | ||||
| }) | ||||
| const formRules = reactive({ | ||||
|   mobile: [{ required: true, message: '手机号不能为空', trigger: 'blur' }], | ||||
|  | @ -158,9 +164,6 @@ const resetForm = () => { | |||
|     mobile: undefined, | ||||
|     password: undefined, | ||||
|     status: undefined, | ||||
|     registerIp: undefined, | ||||
|     loginIp: undefined, | ||||
|     loginDate: undefined, | ||||
|     nickname: undefined, | ||||
|     avatar: undefined, | ||||
|     name: undefined, | ||||
|  | @ -168,8 +171,8 @@ const resetForm = () => { | |||
|     areaId: undefined, | ||||
|     birthday: undefined, | ||||
|     mark: undefined, | ||||
|     createTime: undefined, | ||||
|     tagIds: [] | ||||
|     tagIds: [], | ||||
|     groupId: undefined | ||||
|   } | ||||
|   formRef.value?.resetFields() | ||||
| } | ||||
|  |  | |||
|  | @ -1,43 +0,0 @@ | |||
| <template> | ||||
|   <el-descriptions :column="2"> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <div class="cell-item"> 储值余额 </div> | ||||
|       </template> | ||||
|       {{ 0 }} | ||||
|     </el-descriptions-item> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <div class="cell-item"> 现金余额 </div> | ||||
|       </template> | ||||
|       {{ 0 }} | ||||
|     </el-descriptions-item> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <div class="cell-item"> 积分 </div> | ||||
|       </template> | ||||
|       {{ 0 }} | ||||
|     </el-descriptions-item> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <div class="cell-item"> 成长值 </div> | ||||
|       </template> | ||||
|       {{ 0 }} | ||||
|     </el-descriptions-item> | ||||
|   </el-descriptions> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { defineComponent } from 'vue' | ||||
| 
 | ||||
| defineComponent({ | ||||
|   name: 'AccountInfo' | ||||
| }) | ||||
| </script> | ||||
| <style scoped lang="scss"> | ||||
| .cell-item { | ||||
|   display: inline; | ||||
| } | ||||
| .cell-item::after { | ||||
|   content: ':'; | ||||
| } | ||||
| </style> | ||||
|  | @ -1,13 +0,0 @@ | |||
| <script lang="ts"> | ||||
| import { defineComponent } from 'vue' | ||||
| 
 | ||||
| export default defineComponent({ | ||||
|   name: 'AddressList' | ||||
| }) | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div>收货地址列表</div> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped lang="scss"></style> | ||||
|  | @ -6,6 +6,7 @@ export default defineComponent({ | |||
| }) | ||||
| </script> | ||||
| 
 | ||||
| <!-- TODO @芋艿:未来实现,等周建的 --> | ||||
| <template> | ||||
|   <div>余额列表</div> | ||||
| </template> | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ export default defineComponent({ | |||
| }) | ||||
| </script> | ||||
| 
 | ||||
| <!-- TODO @梦:可以读取 member_experience_log 表 --> | ||||
| <template> | ||||
|   <div>成长值列表</div> | ||||
| </template> | ||||
|  |  | |||
|  | @ -0,0 +1,60 @@ | |||
| <template> | ||||
|   <el-descriptions :column="2"> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <descriptions-item-label label=" 等级 " icon="svg-icon:member_level" /> | ||||
|       </template> | ||||
|       {{ user.levelName || '无' }} | ||||
|     </el-descriptions-item> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <descriptions-item-label label=" 成长值 " icon="ep:suitcase" /> | ||||
|       </template> | ||||
|       {{ user.experience || 0 }} | ||||
|     </el-descriptions-item> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <descriptions-item-label label=" 当前积分 " icon="ep:coin" /> | ||||
|       </template> | ||||
|       {{ user.point || 0 }} | ||||
|     </el-descriptions-item> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <descriptions-item-label label=" 总积分 " icon="ep:coin" /> | ||||
|       </template> | ||||
|       {{ user.totalPoint || 0 }} | ||||
|     </el-descriptions-item> | ||||
|     <!-- TODO 芋艿:后续接入余额、支付金额 --> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <descriptions-item-label label=" 当前余额 " icon="svg-icon:member_balance" /> | ||||
|       </template> | ||||
|       {{ 0 }} | ||||
|     </el-descriptions-item> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <descriptions-item-label label=" 支出金额 " icon="svg-icon:member_expenditure_balance" /> | ||||
|       </template> | ||||
|       {{ 0 }} | ||||
|     </el-descriptions-item> | ||||
|     <el-descriptions-item> | ||||
|       <template #label> | ||||
|         <descriptions-item-label label=" 充值金额 " icon="svg-icon:member_recharge_balance" /> | ||||
|       </template> | ||||
|       {{ 0 }} | ||||
|     </el-descriptions-item> | ||||
|   </el-descriptions> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { DescriptionsItemLabel } from '@/components/Descriptions' | ||||
| import * as UserApi from '@/api/member/user' | ||||
| const { user } = defineProps<{ user: UserApi.UserVO }>() | ||||
| </script> | ||||
| <style scoped lang="scss"> | ||||
| .cell-item { | ||||
|   display: inline; | ||||
| } | ||||
| .cell-item::after { | ||||
|   content: ':'; | ||||
| } | ||||
| </style> | ||||
|  | @ -0,0 +1,54 @@ | |||
| <template> | ||||
|   <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> | ||||
|     <el-table-column label="地址编号" align="center" prop="id" width="150px" /> | ||||
|     <el-table-column label="收件人名称" align="center" prop="name" width="150px" /> | ||||
|     <el-table-column label="手机号" align="center" prop="mobile" width="150px" /> | ||||
|     <el-table-column label="地区编码" align="center" prop="areaId" width="150px" /> | ||||
|     <el-table-column label="收件详细地址" align="center" prop="detailAddress" /> | ||||
|     <el-table-column label="是否默认" align="center" prop="defaultStatus" width="150px"> | ||||
|       <template #default="scope"> | ||||
|         <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="Number(scope.row.defaultStatus)" /> | ||||
|       </template> | ||||
|     </el-table-column> | ||||
|     <el-table-column | ||||
|       label="创建时间" | ||||
|       align="center" | ||||
|       prop="createTime" | ||||
|       :formatter="dateFormatter" | ||||
|       width="180px" | ||||
|     /> | ||||
|   </el-table> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { DICT_TYPE } from '@/utils/dict' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import * as AddressApi from '@/api/member/address' | ||||
| 
 | ||||
| const { userId }: { userId: number } = defineProps({ | ||||
|   userId: { | ||||
|     type: Number, | ||||
|     required: true | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref([]) // 列表的数据 | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     list.value = await AddressApi.getAddressList({ userId }) | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(() => { | ||||
|   getList() | ||||
| }) | ||||
| </script> | ||||
| 
 | ||||
| <style scoped lang="scss"></style> | ||||
|  | @ -0,0 +1,85 @@ | |||
| <template> | ||||
|   <el-card shadow="never"> | ||||
|     <template #header> | ||||
|       <slot name="header"></slot> | ||||
|     </template> | ||||
|     <el-row> | ||||
|       <el-col :span="4"> | ||||
|         <ElAvatar shape="square" :size="140" :src="user.avatar || undefined" /> | ||||
|       </el-col> | ||||
|       <el-col :span="20"> | ||||
|         <el-descriptions :column="2"> | ||||
|           <el-descriptions-item> | ||||
|             <template #label> | ||||
|               <descriptions-item-label label="用户名" icon="ep:user" /> | ||||
|             </template> | ||||
|             {{ user.name || '空' }} | ||||
|           </el-descriptions-item> | ||||
|           <el-descriptions-item> | ||||
|             <template #label> | ||||
|               <descriptions-item-label label="昵称" icon="ep:user" /> | ||||
|             </template> | ||||
|             {{ user.nickname }} | ||||
|           </el-descriptions-item> | ||||
|           <el-descriptions-item label="手机号"> | ||||
|             <template #label> | ||||
|               <descriptions-item-label label="手机号" icon="ep:phone" /> | ||||
|             </template> | ||||
|             {{ user.mobile }} | ||||
|           </el-descriptions-item> | ||||
|           <el-descriptions-item> | ||||
|             <template #label> | ||||
|               <descriptions-item-label label="性别" icon="fa:mars-double" /> | ||||
|             </template> | ||||
|             <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="user.sex" /> | ||||
|           </el-descriptions-item> | ||||
|           <el-descriptions-item> | ||||
|             <template #label> | ||||
|               <descriptions-item-label label="所在地" icon="ep:location" /> | ||||
|             </template> | ||||
|             {{ user.areaName }} | ||||
|           </el-descriptions-item> | ||||
|           <el-descriptions-item> | ||||
|             <template #label> | ||||
|               <descriptions-item-label label="注册 IP" icon="ep:position" /> | ||||
|             </template> | ||||
|             {{ user.registerIp }} | ||||
|           </el-descriptions-item> | ||||
|           <el-descriptions-item> | ||||
|             <template #label> | ||||
|               <descriptions-item-label label="生日" icon="fa:birthday-cake" /> | ||||
|             </template> | ||||
|             {{ user.birthday ? formatDate(user.birthday) : '空' }} | ||||
|           </el-descriptions-item> | ||||
|           <el-descriptions-item> | ||||
|             <template #label> | ||||
|               <descriptions-item-label label="注册时间" icon="ep:calendar" /> | ||||
|             </template> | ||||
|             {{ user.createTime ? formatDate(user.createTime) : '空' }} | ||||
|           </el-descriptions-item> | ||||
|           <el-descriptions-item> | ||||
|             <template #label> | ||||
|               <descriptions-item-label label="最后登录时间" icon="ep:calendar" /> | ||||
|             </template> | ||||
|             {{ user.loginDate ? formatDate(user.loginDate) : '空' }} | ||||
|           </el-descriptions-item> | ||||
|         </el-descriptions> | ||||
|       </el-col> | ||||
|     </el-row> | ||||
|   </el-card> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { DICT_TYPE } from '@/utils/dict' | ||||
| import { formatDate } from '@/utils/formatTime' | ||||
| import * as UserApi from '@/api/member/user' | ||||
| import { DescriptionsItemLabel } from '@/components/Descriptions/index' | ||||
| 
 | ||||
| const { user } = defineProps<{ user: UserApi.UserVO }>() | ||||
| </script> | ||||
| <style scoped lang="scss"> | ||||
| .card-header { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
| } | ||||
| </style> | ||||
|  | @ -67,7 +67,6 @@ | |||
|         :formatter="dateFormatter" | ||||
|         width="180" | ||||
|       /> | ||||
|       <el-table-column label="用户" align="center" prop="nickname" width="200" /> | ||||
|       <el-table-column label="获得积分" align="center" prop="point" width="100"> | ||||
|         <template #default="scope"> | ||||
|           <el-tag v-if="scope.row.point > 0" class="ml-2" type="success" effect="dark"> | ||||
|  | @ -99,20 +98,18 @@ | |||
| <script lang="ts" setup> | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import * as RecordApi from '@/api/member/point/record' | ||||
| import { RecordQueryVO } from '@/api/member/point/record' | ||||
| import * as RecordApi from '@/api//member/point/record' | ||||
| 
 | ||||
| defineOptions({ name: 'PointList' }) | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref([]) // 列表的数据 | ||||
| const queryParams = reactive<RecordQueryVO>({ | ||||
| const queryParams = reactive({ | ||||
|   pageNo: 1, | ||||
|   pageSize: 10, | ||||
|   bizType: undefined, | ||||
|   title: null, | ||||
|   createDate: [], | ||||
|   userId: null | ||||
|   userId: NaN | ||||
| }) | ||||
| const queryFormRef = ref() // 搜索的表单 | ||||
| 
 | ||||
|  | @ -139,15 +136,17 @@ const resetQuery = () => { | |||
|   queryFormRef.value.resetFields() | ||||
|   handleQuery() | ||||
| } | ||||
| const { memberId } = defineProps({ | ||||
|   memberId: { | ||||
| 
 | ||||
| const { userId } = defineProps({ | ||||
|   userId: { | ||||
|     type: Number, | ||||
|     required: true | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(() => { | ||||
|   queryParams.userId = memberId | ||||
|   queryParams.userId = userId | ||||
|   getList() | ||||
| }) | ||||
| </script> | ||||
|  | @ -48,7 +48,6 @@ | |||
|   <ContentWrap> | ||||
|     <el-table v-loading="loading" :data="list"> | ||||
|       <el-table-column label="编号" align="center" prop="id" /> | ||||
|       <el-table-column label="签到用户" align="center" prop="nickname" /> | ||||
|       <el-table-column | ||||
|         label="签到天数" | ||||
|         align="center" | ||||
|  | @ -83,16 +82,14 @@ | |||
| <script lang="ts" setup> | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import * as SignInRecordApi from '@/api/member/signin/record' | ||||
| import { SignInRecordQueryVO } from '@/api/member/signin/record' | ||||
| 
 | ||||
| defineOptions({ name: 'SignList' }) | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref([]) // 列表的数据 | ||||
| const queryParams = reactive<SignInRecordQueryVO>({ | ||||
| const queryParams = reactive({ | ||||
|   pageNo: 1, | ||||
|   pageSize: 10, | ||||
|   userId: NaN, | ||||
|   nickname: null, | ||||
|   day: null, | ||||
|   createTime: [] | ||||
|  | @ -122,15 +119,17 @@ const resetQuery = () => { | |||
|   queryFormRef.value.resetFields() | ||||
|   handleQuery() | ||||
| } | ||||
| const { memberId } = defineProps({ | ||||
|   memberId: { | ||||
| 
 | ||||
| const { userId } = defineProps({ | ||||
|   userId: { | ||||
|     type: Number, | ||||
|     required: true | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
| onMounted(() => { | ||||
|   queryParams.userId = memberId | ||||
|   queryParams.userId = userId | ||||
|   getList() | ||||
| }) | ||||
| </script> | ||||
|  | @ -1,200 +1,105 @@ | |||
| <template> | ||||
|   <div v-loading="loading"> | ||||
|     <el-row :gutter="10" class="detail-info-warp"> | ||||
|     <el-row :gutter="10"> | ||||
|       <!-- 左上角:基本信息 --> | ||||
|       <el-col :span="14" class="detail-info-item"> | ||||
|         <el-card shadow="never"> | ||||
|         <UserBasicInfo :user="user"> | ||||
|           <template #header> | ||||
|             <div class="card-header"> | ||||
|               <CardTitle title="基本信息" /> | ||||
|               <el-button | ||||
|                 v-if="userInfo.id" | ||||
|                 type="primary" | ||||
|                 text | ||||
|                 @click="openForm('update', userInfo.id)" | ||||
|                 >编辑</el-button | ||||
|               > | ||||
|               <el-button type="primary" size="small" text @click="openForm('update')"> | ||||
|                 编辑 | ||||
|               </el-button> | ||||
|             </div> | ||||
|           </template> | ||||
|           <el-row> | ||||
|             <el-col :span="4"> | ||||
|               <ElAvatar shape="square" :size="140" :src="userInfo.avatar || undefined" /> | ||||
|             </el-col> | ||||
|             <el-col :span="20"> | ||||
|               <el-descriptions :column="2"> | ||||
|                 <el-descriptions-item> | ||||
|                   <template #label> | ||||
|                     <div class="cell-item"> | ||||
|                       <Icon icon="ep:user" /> | ||||
|                       用户名 | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   {{ userInfo.name || '空' }} | ||||
|                 </el-descriptions-item> | ||||
|                 <el-descriptions-item> | ||||
|                   <template #label> | ||||
|                     <div class="cell-item"> | ||||
|                       <Icon icon="ep:user" /> | ||||
|                       昵称 | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   {{ userInfo.nickname }}</el-descriptions-item | ||||
|                 > | ||||
|                 <el-descriptions-item label="手机号"> | ||||
|                   <template #label> | ||||
|                     <div class="cell-item"> | ||||
|                       <Icon icon="ep:phone" /> | ||||
|                       手机号 | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   {{ userInfo.mobile }}</el-descriptions-item | ||||
|                 > | ||||
|                 <el-descriptions-item> | ||||
|                   <template #label> | ||||
|                     <div class="cell-item"> | ||||
|                       <Icon icon="fa:mars-double" /> | ||||
|                       性别 | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="userInfo.sex" /> | ||||
|                 </el-descriptions-item> | ||||
|                 <el-descriptions-item> | ||||
|                   <template #label> | ||||
|                     <div class="cell-item"> | ||||
|                       <Icon icon="ep:location" /> | ||||
|                       所在地 | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   {{ userInfo.areaId }} | ||||
|                 </el-descriptions-item> | ||||
|                 <el-descriptions-item> | ||||
|                   <template #label> | ||||
|                     <div class="cell-item"> | ||||
|                       <Icon icon="ep:position" /> | ||||
|                       注册IP | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   {{ userInfo.registerIp }} | ||||
|                 </el-descriptions-item> | ||||
|                 <el-descriptions-item> | ||||
|                   <template #label> | ||||
|                     <div class="cell-item"> | ||||
|                       <Icon icon="fa:birthday-cake" /> | ||||
|                       生日 | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   {{ userInfo.birthday ? formatDate(userInfo.birthday) : '空' }} | ||||
|                 </el-descriptions-item> | ||||
|                 <el-descriptions-item> | ||||
|                   <template #label> | ||||
|                     <div class="cell-item"> | ||||
|                       <Icon icon="ep:calendar" /> | ||||
|                       注册时间 | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   {{ userInfo.createTime ? formatDate(userInfo.createTime) : '空' }} | ||||
|                 </el-descriptions-item> | ||||
|                 <el-descriptions-item> | ||||
|                   <template #label> | ||||
|                     <div class="cell-item"> | ||||
|                       <Icon icon="ep:calendar" /> | ||||
|                       最后登录时间 | ||||
|                     </div> | ||||
|                   </template> | ||||
|                   {{ userInfo.loginDate ? formatDate(userInfo.loginDate) : '空' }} | ||||
|                 </el-descriptions-item> | ||||
|               </el-descriptions> | ||||
|             </el-col> | ||||
|           </el-row> | ||||
|         </el-card> | ||||
|         </UserBasicInfo> | ||||
|       </el-col> | ||||
|       <!-- 右上角:账户信息 --> | ||||
|       <el-col :span="10" class="detail-info-item"> | ||||
|         <el-card shadow="never"> | ||||
|           <template #header> | ||||
|             <CardTitle title="账户信息(WIP)" /> | ||||
|             <CardTitle title="账户信息" /> | ||||
|           </template> | ||||
|           <AccountInfo /> | ||||
|           <UserAccountInfo :user="user" /> | ||||
|         </el-card> | ||||
|       </el-col> | ||||
|       <!-- 下边:账户明细 --> | ||||
|       <!-- TODO 芋艿:【订单管理】【售后管理】【收藏记录】【优惠劵】 --> | ||||
|       <el-card header="账户明细" style="width: 100%; margin-top: 20px" shadow="never"> | ||||
|         <template #header> | ||||
|           <CardTitle title="账户明细" /> | ||||
|         </template> | ||||
|         <el-tabs v-model="activeName" class="demo-tabs"> | ||||
|         <el-tabs v-model="activeName"> | ||||
|           <el-tab-pane label="积分" name="point"> | ||||
|             <PointList v-if="userInfo.id" :member-id="userInfo.id" /> | ||||
|             <UserPointList :user-id="id" /> | ||||
|           </el-tab-pane> | ||||
|           <el-tab-pane label="签到" name="sign"> | ||||
|             <SignList v-if="userInfo.id" :member-id="userInfo.id" /> | ||||
|           <el-tab-pane label="签到" name="sign" lazy> | ||||
|             <UserSignList :user-id="id" /> | ||||
|           </el-tab-pane> | ||||
|           <el-tab-pane label="成长值" name="third">成长值(WIP)</el-tab-pane> | ||||
|           <el-tab-pane label="余额" name="fourth">余额(WIP)</el-tab-pane> | ||||
|           <el-tab-pane label="收货地址" name="address" lazy> | ||||
|             <UserAddressList :user-id="id" /> | ||||
|           </el-tab-pane> | ||||
|           <el-tab-pane label="订单管理" name="fourth">订单管理(WIP)</el-tab-pane> | ||||
|           <el-tab-pane label="售后管理" name="fourth">售后管理(WIP)</el-tab-pane> | ||||
|           <el-tab-pane label="收藏记录" name="fourth">收藏记录(WIP)</el-tab-pane> | ||||
|           <el-tab-pane label="优惠劵" name="fourth">优惠劵(WIP)</el-tab-pane> | ||||
|         </el-tabs> | ||||
|       </el-card> | ||||
|     </el-row> | ||||
|   </div> | ||||
| 
 | ||||
|   <!-- 表单弹窗:添加/修改 --> | ||||
|   <UserForm ref="formRef" @success="getUserData(userInfo.id)" /> | ||||
|   <UserForm ref="formRef" @success="getUserData(id)" /> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { ref } from 'vue' | ||||
| import PointList from '@/views/member/user/components/point-list.vue' | ||||
| import SignList from '@/views/member/user/components/sign-list.vue' | ||||
| import CardTitle from '@/views/member/user/components/card-title.vue' | ||||
| import { ElMessage } from 'element-plus' | ||||
| import { getUser, UserBaseInfoVO } from '@/api/member/user' | ||||
| import { formatDate } from '@/utils/formatTime' | ||||
| import { DICT_TYPE } from '@/utils/dict' | ||||
| import * as UserApi from '@/api/member/user' | ||||
| import { useTagsViewStore } from '@/store/modules/tagsView' | ||||
| import UserBasicInfo from './UserBasicInfo.vue' | ||||
| import UserForm from '@/views/member/user/UserForm.vue' | ||||
| import AccountInfo from '@/views/member/user/components/account-info.vue' | ||||
| import UserAccountInfo from './UserAccountInfo.vue' | ||||
| import UserAddressList from './UserAddressList.vue' | ||||
| import UserPointList from './UserPointList.vue' | ||||
| import UserSignList from './UserSignList.vue' | ||||
| import { CardTitle } from '@/components/Card/index' | ||||
| 
 | ||||
| defineOptions({ name: 'MemberDetail' }) | ||||
| 
 | ||||
| const activeName = ref('point') | ||||
| const loading = ref(true) | ||||
| const activeName = ref('point') // 账户明细 选中的 tabs | ||||
| const loading = ref(true) // 加载中 | ||||
| let user = ref<UserApi.UserVO>({}) | ||||
| 
 | ||||
| /** 添加/修改操作 */ | ||||
| const formRef = ref() | ||||
| const openForm = (type: string, id?: number) => { | ||||
| const openForm = (type: string) => { | ||||
|   formRef.value.open(type, id) | ||||
| } | ||||
| let userInfo = ref<UserBaseInfoVO>({ | ||||
|   areaId: undefined, | ||||
|   avatar: undefined, | ||||
|   birthday: undefined, | ||||
|   createTime: undefined, | ||||
|   id: undefined, | ||||
|   loginDate: undefined, | ||||
|   loginIp: '', | ||||
|   mark: '', | ||||
|   mobile: '', | ||||
|   name: '', | ||||
|   nickname: '', | ||||
|   password: null, | ||||
|   registerIp: undefined, | ||||
|   sex: 0, | ||||
|   status: 0 | ||||
| }) | ||||
| 
 | ||||
| const getUserData = async (id: any) => { | ||||
| /** 获得用户 */ | ||||
| const getUserData = async (id: number) => { | ||||
|   loading.value = true | ||||
|   try { | ||||
|     // userInfo.value = Object.assign(userInfo, await getUser(parseInt(id as string))) | ||||
|     userInfo.value = await getUser(parseInt(id as string)) | ||||
|     user.value = await UserApi.getUser(id) | ||||
|   } finally { | ||||
|     loading.value = false | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 初始化 */ | ||||
| const { currentRoute } = useRouter() // 路由 | ||||
| const { delView } = useTagsViewStore() // 视图操作 | ||||
| const route = useRoute() | ||||
| let router = useRouter() | ||||
| const { member_id } = route.query | ||||
| const id = Number(route.params.id) | ||||
| onMounted(() => { | ||||
|   if (!member_id) { | ||||
|     ElMessage.warning('会员id 未携带!') | ||||
|     router.back() | ||||
|   if (!id) { | ||||
|     ElMessage.warning('参数错误,会员编号不能为空!') | ||||
|     delView(unref(currentRoute)) | ||||
|     return | ||||
|   } | ||||
|   getUserData(member_id) | ||||
|   getUserData(id) | ||||
| }) | ||||
| </script> | ||||
| 
 | ||||
| <style scoped lang="css"> | ||||
| .detail-info-item:first-child { | ||||
|   padding-left: 0 !important; | ||||
|  | @ -208,10 +113,4 @@ onMounted(() => { | |||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
| } | ||||
| .cell-item { | ||||
|   display: inline; | ||||
| } | ||||
| .cell-item::after { | ||||
|   content: ':'; | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -51,6 +51,12 @@ | |||
|       <el-form-item label="用户标签" prop="tagIds"> | ||||
|         <MemberTagSelect v-model="queryParams.tagIds" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="用户等级" prop="levelId"> | ||||
|         <MemberLevelSelect v-model="queryParams.levelId" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="用户分组" prop="groupId"> | ||||
|         <MemberGroupSelect v-model="queryParams.groupId" /> | ||||
|       </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> | ||||
|  | @ -69,9 +75,8 @@ | |||
|       </el-table-column> | ||||
|       <el-table-column label="手机号" align="center" prop="mobile" width="120px" /> | ||||
|       <el-table-column label="昵称" align="center" prop="nickname" width="80px" /> | ||||
|       <!-- TODO 芋艿:待接入 --> | ||||
|       <el-table-column label="等级" align="center" width="100px" /> | ||||
|       <el-table-column label="分组" align="center" width="100px" /> | ||||
|       <el-table-column label="等级" align="center" prop="levelName" width="100px" /> | ||||
|       <el-table-column label="分组" align="center" prop="groupName" width="100px" /> | ||||
|       <el-table-column | ||||
|         label="用户标签" | ||||
|         align="center" | ||||
|  | @ -79,10 +84,12 @@ | |||
|         :show-overflow-tooltip="false" | ||||
|       > | ||||
|         <template #default="scope"> | ||||
|           <el-tag v-for="(tagName, index) in scope.row.tagNames" :key="index">{{ tagName }}</el-tag> | ||||
|           <el-tag v-for="(tagName, index) in scope.row.tagNames" :key="index" class="mr-5px"> | ||||
|             {{ tagName }} | ||||
|           </el-tag> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="积分" align="center" width="100px" /> | ||||
|       <el-table-column label="积分" align="center" prop="point" width="100px" /> | ||||
|       <el-table-column label="状态" align="center" prop="status" width="100px"> | ||||
|         <template #default="scope"> | ||||
|           <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> | ||||
|  | @ -102,8 +109,9 @@ | |||
|         :formatter="dateFormatter" | ||||
|         width="180px" | ||||
|       /> | ||||
|       <el-table-column label="操作" align="center"> | ||||
|       <el-table-column label="操作" align="center" width="180px" fixed="right"> | ||||
|         <template #default="scope"> | ||||
|           <el-button link type="primary" @click="openDetail(scope.row.id)">详情</el-button> | ||||
|           <el-button | ||||
|             link | ||||
|             type="primary" | ||||
|  | @ -112,6 +120,15 @@ | |||
|           > | ||||
|             编辑 | ||||
|           </el-button> | ||||
|           <!-- todo 放到更多菜单中 --> | ||||
|           <el-button | ||||
|             link | ||||
|             type="primary" | ||||
|             @click="updateLevelFormRef.open(scope.row.id)" | ||||
|             v-hasPermi="['member:user:update-level']" | ||||
|           > | ||||
|             修改等级 | ||||
|           </el-button> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|  | @ -126,19 +143,20 @@ | |||
| 
 | ||||
|   <!-- 表单弹窗:添加/修改 --> | ||||
|   <UserForm ref="formRef" @success="getList" /> | ||||
|   <!-- 修改用户等级弹窗 --> | ||||
|   <UpdateLevelForm ref="updateLevelFormRef" @success="getList" /> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { dateFormatter } from '@/utils/formatTime' | ||||
| import * as UserApi from '@/api/member/user' | ||||
| import UserForm from './UserForm.vue' | ||||
| import { DICT_TYPE } from '@/utils/dict' | ||||
| import MemberTagSelect from '@/views/member/tag/components/MemberTagSelect.vue' | ||||
| import MemberLevelSelect from '@/views/member/level/components/MemberLevelSelect.vue' | ||||
| import MemberGroupSelect from '@/views/member/group/components/MemberGroupSelect.vue' | ||||
| import UpdateLevelForm from '@/views/member/user/UpdateLevelForm.vue' | ||||
| 
 | ||||
| defineOptions({ name: 'MemberUser' }) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { t } = useI18n() // 国际化 | ||||
| 
 | ||||
| const loading = ref(true) // 列表的加载中 | ||||
| const total = ref(0) // 列表的总页数 | ||||
| const list = ref([]) // 列表的数据 | ||||
|  | @ -149,10 +167,12 @@ const queryParams = reactive({ | |||
|   mobile: null, | ||||
|   loginDate: [], | ||||
|   createTime: [], | ||||
|   tagIds: [] | ||||
|   tagIds: [], | ||||
|   levelId: null, | ||||
|   groupId: null | ||||
| }) | ||||
| const queryFormRef = ref() // 搜索的表单 | ||||
| const exportLoading = ref(false) // 导出的加载中 | ||||
| const updateLevelFormRef = ref() // 修改会员等级表单 | ||||
| 
 | ||||
| /** 查询列表 */ | ||||
| const getList = async () => { | ||||
|  | @ -178,10 +198,10 @@ const resetQuery = () => { | |||
|   handleQuery() | ||||
| } | ||||
| 
 | ||||
| /** 添加/修改操作 */ | ||||
| const formRef = ref() | ||||
| const openForm = (type: string, id?: number) => { | ||||
|   formRef.value.open(type, id) | ||||
| /** 打开会员详情 */ | ||||
| const { push } = useRouter() | ||||
| const openDetail = (id: number) => { | ||||
|   push({ name: 'MemberUserDetail', params: { id } }) | ||||
| } | ||||
| 
 | ||||
| /** 初始化 **/ | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ | |||
|           class="!w-240px" | ||||
|         > | ||||
|           <el-option | ||||
|             v-for="dict in getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)" | ||||
|             v-for="dict in getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE)" | ||||
|             :key="dict.value" | ||||
|             :label="dict.label" | ||||
|             :value="dict.value" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 芋道源码
						芋道源码