commit
ce84b14d15
49
src/api/mall/product/comment.ts
Normal file
49
src/api/mall/product/comment.ts
Normal file
@ -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
|
||||
}
|
15
src/api/member/address/index.ts
Normal file
15
src/api/member/address/index.ts
Normal file
@ -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 })
|
||||
}
|
38
src/api/member/group/index.ts
Normal file
38
src/api/member/group/index.ts
Normal file
@ -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 })
|
||||
}
|
42
src/api/member/level/index.ts
Normal file
42
src/api/member/level/index.ts
Normal file
@ -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 })
|
||||
}
|
||||
|
1
src/assets/svgs/member_balance.svg
Normal file
1
src/assets/svgs/member_balance.svg
Normal file
@ -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 |
1
src/assets/svgs/member_expenditure_balance.svg
Normal file
1
src/assets/svgs/member_expenditure_balance.svg
Normal file
@ -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 |
1
src/assets/svgs/member_level.svg
Normal file
1
src/assets/svgs/member_level.svg
Normal file
@ -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 |
1
src/assets/svgs/member_point.svg
Normal file
1
src/assets/svgs/member_point.svg
Normal file
@ -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 |
1
src/assets/svgs/member_recharge_balance.svg
Normal file
1
src/assets/svgs/member_recharge_balance.svg
Normal file
@ -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 |
3
src/components/Card/index.ts
Normal file
3
src/components/Card/index.ts
Normal file
@ -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 }
|
||||
|
28
src/components/Descriptions/src/DescriptionsItemLabel.vue
Normal file
28
src/components/Descriptions/src/DescriptionsItemLabel.vue
Normal file
@ -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>
|
||||
|
192
src/views/mall/product/comment/CommentForm.vue
Normal file
192
src/views/mall/product/comment/CommentForm.vue
Normal file
@ -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>
|
76
src/views/mall/product/comment/ReplyForm.vue
Normal file
76
src/views/mall/product/comment/ReplyForm.vue
Normal file
@ -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>
|
243
src/views/mall/product/comment/index.vue
Normal file
243
src/views/mall/product/comment/index.vue
Normal file
@ -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>
|
||||
|
94
src/views/mall/product/spu/components/SkuTableSelect.vue
Normal file
94
src/views/mall/product/spu/components/SkuTableSelect.vue
Normal file
@ -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>
|
167
src/views/mall/product/spu/components/SpuTableSelect.vue
Normal file
167
src/views/mall/product/spu/components/SpuTableSelect.vue
Normal file
@ -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()
|
||||
|
100
src/views/mall/trade/order/components/OrderDeliveryForm.vue
Normal file
100
src/views/mall/trade/order/components/OrderDeliveryForm.vue
Normal file
@ -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
112
src/views/member/group/GroupForm.vue
Normal file
112
src/views/member/group/GroupForm.vue
Normal file
@ -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>
|
45
src/views/member/group/components/MemberGroupSelect.vue
Normal file
45
src/views/member/group/components/MemberGroupSelect.vue
Normal file
@ -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>
|
174
src/views/member/group/index.vue
Normal file
174
src/views/member/group/index.vue
Normal file
@ -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>
|
175
src/views/member/level/LevelForm.vue
Normal file
175
src/views/member/level/LevelForm.vue
Normal file
@ -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>
|
45
src/views/member/level/components/MemberLevelSelect.vue
Normal file
45
src/views/member/level/components/MemberLevelSelect.vue
Normal file
@ -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>
|
169
src/views/member/level/index.vue
Normal file
169
src/views/member/level/index.vue
Normal file
@ -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()
|
||||
}
|
||||
|
||||
/** 添加用户标签表单弹框 */
|
||||
|
101
src/views/member/user/UpdateLevelForm.vue
Normal file
101
src/views/member/user/UpdateLevelForm.vue
Normal file
@ -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>
|
||||
|
60
src/views/member/user/detail/UserAccountInfo.vue
Normal file
60
src/views/member/user/detail/UserAccountInfo.vue
Normal file
@ -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>
|
54
src/views/member/user/detail/UserAddressList.vue
Normal file
54
src/views/member/user/detail/UserAddressList.vue
Normal file
@ -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>
|
85
src/views/member/user/detail/UserBasicInfo.vue
Normal file
85
src/views/member/user/detail/UserBasicInfo.vue
Normal file
@ -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
Block a user