Commit 6ed7b3a6 by 芋道源码 Committed by Gitee

!270 合并商城最新代码

Merge pull request !270 from 芋道源码/dev
parents 04a94ad9 532b2377
...@@ -20,8 +20,8 @@ export interface Sku { ...@@ -20,8 +20,8 @@ export interface Sku {
stock?: number // 库存 stock?: number // 库存
weight?: number // 商品重量,单位:kg 千克 weight?: number // 商品重量,单位:kg 千克
volume?: number // 商品体积,单位:m^3 平米 volume?: number // 商品体积,单位:m^3 平米
firstBrokerageRecord?: number | string // 一级分销的佣金 firstBrokeragePrice?: number | string // 一级分销的佣金
secondBrokerageRecord?: number | string // 二级分销的佣金 secondBrokeragePrice?: number | string // 二级分销的佣金
salesCount?: number // 商品销量 salesCount?: number // 商品销量
} }
......
...@@ -7,17 +7,16 @@ export interface BargainActivityVO { ...@@ -7,17 +7,16 @@ export interface BargainActivityVO {
startTime?: Date startTime?: Date
endTime?: Date endTime?: Date
status?: number status?: number
userSize?: number // 达到该人数,才能砍到低价 helpMaxCount?: number // 达到该人数,才能砍到低价
bargainCount?: number // 最大帮砍次数 bargainCount?: number // 最大帮砍次数
totalLimitCount?: number // 最大购买次数 totalLimitCount?: number // 最大购买次数
spuId: number spuId: number
skuId: number skuId: number
bargainFirstPrice: number // 砍价起始价格,单位分 bargainFirstPrice: number // 砍价起始价格,单位分
bargainPrice: number // 砍价底价 bargainMinPrice: number // 砍价底价
stock: number // 活动库存 stock: number // 活动库存
randomMinPrice?: number // 用户每次砍价的最小金额,单位:分 randomMinPrice?: number // 用户每次砍价的最小金额,单位:分
randomMaxPrice?: number // 用户每次砍价的最大金额,单位:分 randomMaxPrice?: number // 用户每次砍价的最大金额,单位:分
successCount?: number // 砍价成功数量
} }
// 砍价活动所需属性。选择的商品和属性的时候使用方便使用活动的通用封装 // 砍价活动所需属性。选择的商品和属性的时候使用方便使用活动的通用封装
...@@ -25,7 +24,7 @@ export interface BargainProductVO { ...@@ -25,7 +24,7 @@ export interface BargainProductVO {
spuId: number spuId: number
skuId: number skuId: number
bargainFirstPrice: number // 砍价起始价格,单位分 bargainFirstPrice: number // 砍价起始价格,单位分
bargainPrice: number // 砍价底价 bargainMinPrice: number // 砍价底价
stock: number // 活动库存 stock: number // 活动库存
} }
...@@ -58,6 +57,11 @@ export const updateBargainActivity = async (data: BargainActivityVO) => { ...@@ -58,6 +57,11 @@ export const updateBargainActivity = async (data: BargainActivityVO) => {
return await request.put({ url: '/promotion/bargain-activity/update', data }) return await request.put({ url: '/promotion/bargain-activity/update', data })
} }
// 关闭砍价活动
export const closeBargainActivity = async (id: number) => {
return await request.put({ url: '/promotion/bargain-activity/close?id=' + id })
}
// 删除砍价活动 // 删除砍价活动
export const deleteBargainActivity = async (id: number) => { export const deleteBargainActivity = async (id: number) => {
return await request.delete({ url: '/promotion/bargain-activity/delete?id=' + id }) return await request.delete({ url: '/promotion/bargain-activity/delete?id=' + id })
......
import request from '@/config/axios'
export interface BargainHelpVO {
id: number
record: number
userId: number
reducePrice: number
endTime: Date
}
// 查询砍价记录列表
export const getBargainHelpPage = async (params) => {
return await request.get({ url: `/promotion/bargain-help/page`, params })
}
import request from '@/config/axios'
export interface BargainRecordVO {
id: number
activityId: number
userId: number
spuId: number
skuId: number
bargainFirstPrice: number
bargainPrice: number
status: number
orderId: number
endTime: Date
}
// 查询砍价记录列表
export const getBargainRecordPage = async (params) => {
return await request.get({ url: `/promotion/bargain-record/page`, params })
}
...@@ -55,6 +55,11 @@ export const updateCombinationActivity = async (data: CombinationActivityVO) => ...@@ -55,6 +55,11 @@ export const updateCombinationActivity = async (data: CombinationActivityVO) =>
return await request.put({ url: '/promotion/combination-activity/update', data }) return await request.put({ url: '/promotion/combination-activity/update', data })
} }
// 关闭拼团活动
export const closeCombinationActivity = async (id: number) => {
return await request.put({ url: '/promotion/bargain-combination/close?id=' + id })
}
// 删除拼团活动 // 删除拼团活动
export const deleteCombinationActivity = async (id: number) => { export const deleteCombinationActivity = async (id: number) => {
return await request.delete({ url: '/promotion/combination-activity/delete?id=' + id }) return await request.delete({ url: '/promotion/combination-activity/delete?id=' + id })
......
import request from '@/config/axios'
export interface CombinationRecordVO {
id: number // 拼团记录编号
activityId: number // 拼团活动编号
nickname: string // 用户昵称
avatar: string // 用户头像
headId: number // 团长编号
expireTime: string // 过期时间
userSize: number // 可参团人数
userCount: number // 已参团人数
status: number // 拼团状态
spuName: string // 商品名字
picUrl: string // 商品图片
virtualGroup: boolean // 是否虚拟成团
startTime: string // 开始时间 (订单付款后开始的时间)
endTime: string // 结束时间(成团时间/失败时间)
}
// 查询拼团记录列表
export const getCombinationRecordPage = async (params) => {
return await request.get({ url: '/promotion/combination-record/page', params })
}
// 查询一个拼团的完整拼团记录
export const getCombinationRecordPageByHeadId = async (params) => {
return await request.get({ url: '/promotion/combination-record/page-by-headId', params })
}
// 获得拼团记录的概要信息
export const getCombinationRecordSummary = async () => {
return await request.get({ url: '/promotion/combination-record/get-summary' })
}
...@@ -57,6 +57,11 @@ export const updateSeckillActivity = async (data: SeckillActivityVO) => { ...@@ -57,6 +57,11 @@ export const updateSeckillActivity = async (data: SeckillActivityVO) => {
return await request.put({ url: '/promotion/seckill-activity/update', data }) return await request.put({ url: '/promotion/seckill-activity/update', data })
} }
// 关闭秒杀活动
export const closeSeckillActivity = async (id: number) => {
return await request.put({ url: '/promotion/seckill-activity/close?id=' + id })
}
// 删除秒杀活动 // 删除秒杀活动
export const deleteSeckillActivity = async (id: number) => { export const deleteSeckillActivity = async (id: number) => {
return await request.delete({ url: '/promotion/seckill-activity/delete?id=' + id }) return await request.delete({ url: '/promotion/seckill-activity/delete?id=' + id })
......
import request from '@/config/axios'
import dayjs from 'dayjs'
import { TradeStatisticsComparisonRespVO } from '@/api/mall/statistics/trade'
import { formatDate } from '@/utils/formatTime'
/** 会员分析 Request VO */
export interface MemberAnalyseReqVO {
times: [dayjs.ConfigType, dayjs.ConfigType]
}
/** 会员分析 Response VO */
export interface MemberAnalyseRespVO {
visitorCount: number
orderUserCount: number
payUserCount: number
atv: number
comparison: TradeStatisticsComparisonRespVO<MemberAnalyseComparisonRespVO>
}
/** 会员分析对照数据 Response VO */
export interface MemberAnalyseComparisonRespVO {
userCount: number
activeUserCount: number
rechargeUserCount: number
}
/** 会员地区统计 Response VO */
export interface MemberAreaStatisticsRespVO {
areaId: number
areaName: string
userCount: number
orderCreateCount: number
orderPayCount: number
orderPayPrice: number
}
/** 会员性别统计 Response VO */
export interface MemberSexStatisticsRespVO {
sex: number
userCount: number
}
/** 会员统计 Response VO */
export interface MemberSummaryRespVO {
userCount: number
rechargeUserCount: number
rechargePrice: number
expensePrice: number
}
/** 会员终端统计 Response VO */
export interface MemberTerminalStatisticsRespVO {
terminal: number
userCount: number
}
// 查询会员统计
export const getMemberSummary = () => {
return request.get<MemberSummaryRespVO>({
url: '/statistics/member/summary'
})
}
// 查询会员分析数据
export const getMemberAnalyse = (params: MemberAnalyseReqVO) => {
return request.get<MemberAnalyseRespVO>({
url: '/statistics/member/analyse',
params: { times: [formatDate(params.times[0]), formatDate(params.times[1])] }
})
}
// 按照省份,查询会员统计列表
export const getMemberAreaStatisticsList = () => {
return request.get<MemberAreaStatisticsRespVO[]>({
url: '/statistics/member/get-area-statistics-list'
})
}
// 按照性别,查询会员统计列表
export const getMemberSexStatisticsList = () => {
return request.get<MemberSexStatisticsRespVO[]>({
url: '/statistics/member/get-sex-statistics-list'
})
}
// 按照终端,查询会员统计列表
export const getMemberTerminalStatisticsList = () => {
return request.get<MemberTerminalStatisticsRespVO[]>({
url: '/statistics/member/get-terminal-statistics-list'
})
}
import request from '@/config/axios'
import dayjs from 'dayjs'
import { formatDate } from '@/utils/formatTime'
/** 交易统计对照 Response VO */
export interface TradeStatisticsComparisonRespVO<T> {
value: T
reference: T
}
/** 交易统计 Response VO */
export interface TradeSummaryRespVO {
yesterdayOrderCount: number
monthOrderCount: number
yesterdayPayPrice: number
monthPayPrice: number
}
/** 交易状况 Request VO */
export interface TradeTrendReqVO {
times: [dayjs.ConfigType, dayjs.ConfigType]
}
/** 交易状况统计 Response VO */
export interface TradeTrendSummaryRespVO {
time: string
turnover: number
orderPayPrice: number
rechargePrice: number
expensePrice: number
balancePrice: number
brokerageSettlementPrice: number
orderRefundPrice: number
}
// 查询交易统计
export const getTradeStatisticsSummary = () => {
return request.get<TradeStatisticsComparisonRespVO<TradeSummaryRespVO>>({
url: '/statistics/trade/summary'
})
}
// 获得交易状况统计
export const getTradeTrendSummary = (params: TradeTrendReqVO) => {
return request.get<TradeStatisticsComparisonRespVO<TradeTrendSummaryRespVO>>({
url: '/statistics/trade/trend/summary',
params: formatDateParam(params)
})
}
// 获得交易状况明细
export const getTradeTrendList = (params: TradeTrendReqVO) => {
return request.get<TradeTrendSummaryRespVO[]>({
url: '/statistics/trade/trend/list',
params: formatDateParam(params)
})
}
// 导出交易状况明细
export const exportTradeTrend = (params: TradeTrendReqVO) => {
return request.download({
url: '/statistics/trade/trend/export-excel',
params: formatDateParam(params)
})
}
/** 时间参数需要格式化, 确保接口能识别 */
const formatDateParam = (params: TradeTrendReqVO) => {
return { times: [formatDate(params.times[0]), formatDate(params.times[1])] } as TradeTrendReqVO
}
...@@ -4,13 +4,13 @@ export interface ConfigVO { ...@@ -4,13 +4,13 @@ export interface ConfigVO {
brokerageEnabled: boolean brokerageEnabled: boolean
brokerageEnabledCondition: number brokerageEnabledCondition: number
brokerageBindMode: number brokerageBindMode: number
brokeragePostUrls: string brokeragePosterUrls: string
brokerageFirstPercent: number brokerageFirstPercent: number
brokerageSecondPercent: number brokerageSecondPercent: number
brokerageWithdrawMinPrice: number brokerageWithdrawMinPrice: number
brokerageBankNames: string brokerageBankNames: string
brokerageFrozenDays: number brokerageFrozenDays: number
brokerageWithdrawType: string brokerageWithdrawTypes: string
} }
// 查询交易中心配置详情 // 查询交易中心配置详情
......
...@@ -41,15 +41,22 @@ export interface OrderVO { ...@@ -41,15 +41,22 @@ export interface OrderVO {
refundPrice?: number | null // 退款金额 refundPrice?: number | null // 退款金额
couponId?: number | null // 优惠劵编号 couponId?: number | null // 优惠劵编号
couponPrice?: number | null // 优惠劵减免金额 couponPrice?: number | null // 优惠劵减免金额
vipPrice?: number | null // VIP 减免金额
pointPrice?: number | null // 积分抵扣的金额 pointPrice?: number | null // 积分抵扣的金额
receiverAreaName?: string //收件人地区名字 receiverAreaName?: string //收件人地区名字
items?: OrderItemRespVO[] // 订单项列表 items?: OrderItemRespVO[] // 订单项列表
// 用户信息 // 下单用户信息
user?: { user?: {
id?: number | null id?: number | null
nickname?: string nickname?: string
avatar?: string avatar?: string
} }
// 推广用户信息
brokerageUser?: {
id?: number | null
nickname?: string
avatar?: string
}
// 订单操作日志 // 订单操作日志
logs?: OrderLogRespVO[] logs?: OrderLogRespVO[]
} }
...@@ -114,21 +121,26 @@ export interface DeliveryVO { ...@@ -114,21 +121,26 @@ export interface DeliveryVO {
} }
// 订单发货 // 订单发货
export const delivery = async (data: DeliveryVO) => { export const deliveryOrder = async (data: DeliveryVO) => {
return await request.put({ url: `/trade/order/delivery`, data }) return await request.put({ url: `/trade/order/delivery`, data })
} }
// 订单备注 // 订单备注
export const updateRemark = async (data: any) => { export const updateOrderRemark = async (data: any) => {
return await request.put({ url: `/trade/order/update-remark`, data }) return await request.put({ url: `/trade/order/update-remark`, data })
} }
// 订单调价 // 订单调价
export const updatePrice = async (data: any) => { export const updateOrderPrice = async (data: any) => {
return await request.put({ url: `/trade/order/update-price`, data }) return await request.put({ url: `/trade/order/update-price`, data })
} }
// 修改订单地址 // 修改订单地址
export const updateAddress = async (data: any) => { export const updateOrderAddress = async (data: any) => {
return await request.put({ url: `/trade/order/update-address`, data }) return await request.put({ url: `/trade/order/update-address`, data })
} }
// 订单核销
export const pickUpOrder = async (id: number) => {
return await request.put({ url: `/trade/order/pick-up?id=${id}` })
}
...@@ -2,18 +2,18 @@ import request from '@/config/axios' ...@@ -2,18 +2,18 @@ import request from '@/config/axios'
export interface ConfigVO { export interface ConfigVO {
id: number id: number
tradeDeductEnable: number pointTradeDeductEnable: number
tradeDeductUnitPrice: number pointTradeDeductUnitPrice: number
tradeDeductMaxPrice: number pointTradeDeductMaxPrice: number
tradeGivePoint: number pointTradeGivePoint: number
} }
// 查询积分设置详情 // 查询积分设置详情
export const getConfig = async () => { export const getConfig = async () => {
return await request.get({ url: `/member/point/config/get` }) return await request.get({ url: `/member/config/get` })
} }
// 新增修改积分设置 // 新增修改积分设置
export const saveConfig = async (data: ConfigVO) => { export const saveConfig = async (data: ConfigVO) => {
return await request.put({ url: `/member/point/config/save`, data }) return await request.put({ url: `/member/config/save`, data })
} }
import request from '@/config/axios' import request from '@/config/axios'
export interface SignInConfigVO { export interface SignInConfigVO {
id: number id?: number
day: number | null day?: number
point: number | null point?: number
enable: boolean | null experience?: number
status?: number
} }
// 查询积分签到规则列表 // 查询积分签到规则列表
......
...@@ -41,3 +41,13 @@ export const updateUser = async (data: UserVO) => { ...@@ -41,3 +41,13 @@ export const updateUser = async (data: UserVO) => {
export const updateUserLevel = async (data: any) => { export const updateUserLevel = async (data: any) => {
return await request.put({ url: `/member/user/update-level`, data }) return await request.put({ url: `/member/user/update-level`, data })
} }
// 修改会员用户积分
export const updateUserPoint = async (data: any) => {
return await request.put({ url: `/member/user/update-point`, data })
}
// 修改会员用户余额
export const updateUserBalance = async (data: any) => {
return await request.put({ url: `/member/user/update-balance`, data })
}
import request from '@/config/axios'
/** 用户钱包查询参数 */
export interface PayWalletUserReqVO {
userId: number
userType: number
}
/** 钱包 VO */
export interface WalletVO {
id: number
userId: number
userType: number
balance: number
totalExpense: number
totalRecharge: number
freezePrice: number
}
/** 查询用户钱包详情 */
export const getWallet = async (params: PayWalletUserReqVO) => {
return await request.get<WalletVO>({ url: `/pay/wallet/get`, params })
}
...@@ -5,14 +5,6 @@ export const getAreaTree = async () => { ...@@ -5,14 +5,6 @@ export const getAreaTree = async () => {
return await request.get({ url: '/system/area/tree' }) return await request.get({ url: '/system/area/tree' })
} }
export const getChildrenArea = async (id: number) => {
return await request.get({ url: '/system/area/get-children?id=' + id })
}
export const getAreaListByIds = async (ids) => {
return await request.get({ url: '/system/area/get-by-ids?ids=' + ids })
}
// 获得 IP 对应的地区名 // 获得 IP 对应的地区名
export const getAreaByIp = async (ip: string) => { export const getAreaByIp = async (ip: string) => {
return await request.get({ url: '/system/area/get-by-ip?ip=' + ip }) return await request.get({ url: '/system/area/get-by-ip?ip=' + ip })
......
...@@ -11,21 +11,21 @@ const { getPrefixCls } = useDesign() ...@@ -11,21 +11,21 @@ const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('count-to') const prefixCls = getPrefixCls('count-to')
const props = defineProps({ const props = defineProps({
startVal: propTypes.number.def(0), startVal: propTypes.number.def(0), // 开始播放值
endVal: propTypes.number.def(2021), endVal: propTypes.number.def(2021), // 最终值
duration: propTypes.number.def(3000), duration: propTypes.number.def(3000), // 动画时长
autoplay: propTypes.bool.def(true), autoplay: propTypes.bool.def(true), // 是否自动播放动画, 默认播放
decimals: propTypes.number.validate((value: number) => value >= 0).def(0), decimals: propTypes.number.validate((value: number) => value >= 0).def(0), // 显示的小数位数, 默认不显示小数
decimal: propTypes.string.def('.'), decimal: propTypes.string.def('.'), // 小数分隔符号, 默认为点
separator: propTypes.string.def(','), separator: propTypes.string.def(','), // 数字每三位的分隔符, 默认为逗号
prefix: propTypes.string.def(''), prefix: propTypes.string.def(''), // 前缀, 数值前面显示的内容
suffix: propTypes.string.def(''), suffix: propTypes.string.def(''), // 后缀, 数值后面显示的内容
useEasing: propTypes.bool.def(true), useEasing: propTypes.bool.def(true), // 是否使用缓动效果, 默认启用
easingFn: { easingFn: {
type: Function as PropType<(t: number, b: number, c: number, d: number) => number>, type: Function as PropType<(t: number, b: number, c: number, d: number) => number>,
default(t: number, b: number, c: number, d: number) { default(t: number, b: number, c: number, d: number) {
return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b
} } // 缓动函数
} }
}) })
......
...@@ -32,6 +32,7 @@ const getIconifyStyle = computed(() => { ...@@ -32,6 +32,7 @@ const getIconifyStyle = computed(() => {
const { color, size } = props const { color, size } = props
return { return {
fontSize: `${size}px`, fontSize: `${size}px`,
height: '1em',
color color
} }
}) })
......
...@@ -52,10 +52,10 @@ export default defineComponent({ ...@@ -52,10 +52,10 @@ export default defineComponent({
return ( return (
<ElBreadcrumbItem to={{ path: disabled ? '' : v.path }} key={v.name}> <ElBreadcrumbItem to={{ path: disabled ? '' : v.path }} key={v.name}>
{meta?.icon && breadcrumbIcon.value ? ( {meta?.icon && breadcrumbIcon.value ? (
<> <div class="flex items-center">
<Icon icon={meta.icon} class="mr-[2px]" svgClass="inline-block"></Icon> <Icon icon={meta.icon} class="mr-[2px]" svgClass="inline-block"></Icon>
{t(v?.meta?.title)} {t(v?.meta?.title)}
</> </div>
) : ( ) : (
t(v?.meta?.title) t(v?.meta?.title)
)} )}
...@@ -114,9 +114,10 @@ $prefix-cls: #{$elNamespace}-breadcrumb; ...@@ -114,9 +114,10 @@ $prefix-cls: #{$elNamespace}-breadcrumb;
} }
} }
} }
:deep(&__item):last-child { :deep(&__item):last-child {
.#{$prefix-cls}__inner { .#{$prefix-cls}__inner {
display: flex;
align-items: center;
color: var(--el-text-color-placeholder); color: var(--el-text-color-placeholder);
&:hover { &:hover {
......
...@@ -107,7 +107,7 @@ export const useRenderLayout = () => { ...@@ -107,7 +107,7 @@ export const useRenderLayout = () => {
></ToolHeader> ></ToolHeader>
{tagsView.value ? ( {tagsView.value ? (
<TagsView class="layout-border__bottom layout-border__top"></TagsView> <TagsView class="layout-border__top layout-border__bottom"></TagsView>
) : undefined} ) : undefined}
</div> </div>
...@@ -121,13 +121,13 @@ export const useRenderLayout = () => { ...@@ -121,13 +121,13 @@ export const useRenderLayout = () => {
const renderTopLeft = () => { const renderTopLeft = () => {
return ( return (
<> <>
<div class="flex items-center bg-[var(--top-header-bg-color)] relative layout-border__bottom dark:bg-[var(--el-bg-color)]"> <div class="relative flex items-center bg-[var(--top-header-bg-color)] layout-border__bottom dark:bg-[var(--el-bg-color)]">
{logo.value ? <Logo class="custom-hover"></Logo> : undefined} {logo.value ? <Logo class="custom-hover"></Logo> : undefined}
<ToolHeader class="flex-1"></ToolHeader> <ToolHeader class="flex-1"></ToolHeader>
</div> </div>
<div class="absolute top-[var(--logo-height)+1px] left-0 w-full h-[calc(100%-1px-var(--logo-height))] flex"> <div class="absolute left-0 top-[var(--logo-height)+1px] h-[calc(100%-1px-var(--logo-height))] w-full flex">
<Menu class="!h-full relative layout-border__right"></Menu> <Menu class="relative layout-border__right !h-full"></Menu>
<div <div
class={[ class={[
`${prefixCls}-content`, `${prefixCls}-content`,
...@@ -187,7 +187,7 @@ export const useRenderLayout = () => { ...@@ -187,7 +187,7 @@ export const useRenderLayout = () => {
]} ]}
> >
{logo.value ? <Logo class="custom-hover"></Logo> : undefined} {logo.value ? <Logo class="custom-hover"></Logo> : undefined}
<Menu class="flex-1 px-10px h-[var(--top-tool-height)]"></Menu> <Menu class="h-[var(--top-tool-height)] flex-1 px-10px"></Menu>
<ToolHeader></ToolHeader> <ToolHeader></ToolHeader>
</div> </div>
<div <div
...@@ -233,12 +233,12 @@ export const useRenderLayout = () => { ...@@ -233,12 +233,12 @@ export const useRenderLayout = () => {
const renderCutMenu = () => { const renderCutMenu = () => {
return ( return (
<> <>
<div class="flex items-center bg-[var(--top-header-bg-color)] relative layout-border__bottom"> <div class="relative flex items-center bg-[var(--top-header-bg-color)] layout-border__bottom">
{logo.value ? <Logo class="custom-hover !pr-15px"></Logo> : undefined} {logo.value ? <Logo class="custom-hover !pr-15px"></Logo> : undefined}
<ToolHeader class="flex-1"></ToolHeader> <ToolHeader class="flex-1"></ToolHeader>
</div> </div>
<div class="absolute top-[var(--logo-height)] left-0 w-[calc(100%-2px)] h-[calc(100%-var(--logo-height))] flex"> <div class="absolute left-0 top-[var(--logo-height)] h-[calc(100%-var(--logo-height))] w-[calc(100%-2px)] flex">
<TabMenu></TabMenu> <TabMenu></TabMenu>
<div <div
class={[ class={[
......
...@@ -18,7 +18,8 @@ import { ...@@ -18,7 +18,8 @@ import {
AriaComponent, AriaComponent,
ParallelComponent, ParallelComponent,
LegendComponent, LegendComponent,
ToolboxComponent ToolboxComponent,
VisualMapComponent
} from 'echarts/components' } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers' import { CanvasRenderer } from 'echarts/renderers'
...@@ -32,6 +33,7 @@ echarts.use([ ...@@ -32,6 +33,7 @@ echarts.use([
PolarComponent, PolarComponent,
AriaComponent, AriaComponent,
ParallelComponent, ParallelComponent,
VisualMapComponent,
BarChart, BarChart,
LineChart, LineChart,
PieChart, PieChart,
......
...@@ -331,9 +331,8 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -331,9 +331,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
] ]
}, },
{ {
path: '/product', path: '/mall/product', // 商品中心
component: Layout, component: Layout,
name: 'Product',
meta: { meta: {
hidden: true hidden: true
}, },
...@@ -347,12 +346,12 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -347,12 +346,12 @@ const remainingRouter: AppRouteRecordRaw[] = [
hidden: true, hidden: true,
canTo: true, canTo: true,
icon: 'ep:edit', icon: 'ep:edit',
title: '添加商品', title: '商品添加',
activeMenu: '/product/product-spu' activeMenu: '/mall/product/spu'
} }
}, },
{ {
path: 'spu/edit/:spuId(\\d+)', path: 'spu/edit/:id(\\d+)',
component: () => import('@/views/mall/product/spu/form/index.vue'), component: () => import('@/views/mall/product/spu/form/index.vue'),
name: 'ProductSpuEdit', name: 'ProductSpuEdit',
meta: { meta: {
...@@ -360,12 +359,12 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -360,12 +359,12 @@ const remainingRouter: AppRouteRecordRaw[] = [
hidden: true, hidden: true,
canTo: true, canTo: true,
icon: 'ep:edit', icon: 'ep:edit',
title: '编辑商品', title: '商品编辑',
activeMenu: '/product/product-spu' activeMenu: '/mall/product/spu'
} }
}, },
{ {
path: 'spu/detail/:spuId(\\d+)', path: 'spu/detail/:id(\\d+)',
component: () => import('@/views/mall/product/spu/form/index.vue'), component: () => import('@/views/mall/product/spu/form/index.vue'),
name: 'ProductSpuDetail', name: 'ProductSpuDetail',
meta: { meta: {
...@@ -374,7 +373,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -374,7 +373,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
canTo: true, canTo: true,
icon: 'ep:view', icon: 'ep:view',
title: '商品详情', title: '商品详情',
activeMenu: '/product/product-spu' activeMenu: '/mall/product/spu'
} }
}, },
{ {
...@@ -393,24 +392,23 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -393,24 +392,23 @@ const remainingRouter: AppRouteRecordRaw[] = [
] ]
}, },
{ {
path: '/trade', path: '/mall/trade', // 交易中心
component: Layout, component: Layout,
name: 'Order',
meta: { meta: {
hidden: true hidden: true
}, },
children: [ children: [
{ {
path: 'order/detail/:orderId(\\d+)', path: 'order/detail/:id(\\d+)',
component: () => import('@/views/mall/trade/order/detail/index.vue'), component: () => import('@/views/mall/trade/order/detail/index.vue'),
name: 'TradeOrderDetail', name: 'TradeOrderDetail',
meta: { title: '订单详情', icon: '', activeMenu: '/trade/trade/order' } meta: { title: '订单详情', icon: 'ep:view', activeMenu: '/mall/trade/order' }
}, },
{ {
path: 'after-sale/detail/:orderId(\\d+)', path: 'after-sale/detail/:id(\\d+)',
component: () => import('@/views/mall/trade/afterSale/detail/index.vue'), component: () => import('@/views/mall/trade/afterSale/detail/index.vue'),
name: 'TradeAfterSaleDetail', name: 'TradeAfterSaleDetail',
meta: { title: '退款详情', icon: '', activeMenu: '/trade/trade/after-sale' } meta: { title: '退款详情', icon: 'ep:view', activeMenu: '/mall/trade/after-sale' }
} }
] ]
}, },
......
...@@ -4,12 +4,20 @@ ...@@ -4,12 +4,20 @@
* 枚举类 * 枚举类
*/ */
// ========== COMMON 模块 ==========
// 全局通用状态枚举 // 全局通用状态枚举
export const CommonStatusEnum = { export const CommonStatusEnum = {
ENABLE: 0, // 开启 ENABLE: 0, // 开启
DISABLE: 1 // 禁用 DISABLE: 1 // 禁用
} }
// 全局用户类型枚举
export const UserTypeEnum = {
MEMBER: 1, // 会员
ADMIN: 2 // 管理员
}
// ========== SYSTEM 模块 ==========
/** /**
* 菜单的类型枚举 * 菜单的类型枚举
*/ */
...@@ -39,6 +47,25 @@ export const SystemDataScopeEnum = { ...@@ -39,6 +47,25 @@ export const SystemDataScopeEnum = {
} }
/** /**
* 用户的社交平台的类型枚举
*/
export const SystemUserSocialTypeEnum = {
DINGTALK: {
title: '钉钉',
type: 20,
source: 'dingtalk',
img: 'https://s1.ax1x.com/2022/05/22/OzMDRs.png'
},
WECHAT_ENTERPRISE: {
title: '企业微信',
type: 30,
source: 'wechat_enterprise',
img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png'
}
}
// ========== INFRA 模块 ==========
/**
* 代码生成模板类型 * 代码生成模板类型
*/ */
export const InfraCodegenTemplateTypeEnum = { export const InfraCodegenTemplateTypeEnum = {
...@@ -65,24 +92,7 @@ export const InfraApiErrorLogProcessStatusEnum = { ...@@ -65,24 +92,7 @@ export const InfraApiErrorLogProcessStatusEnum = {
IGNORE: 2 // 已忽略 IGNORE: 2 // 已忽略
} }
/** // ========== PAY 模块 ==========
* 用户的社交平台的类型枚举
*/
export const SystemUserSocialTypeEnum = {
DINGTALK: {
title: '钉钉',
type: 20,
source: 'dingtalk',
img: 'https://s1.ax1x.com/2022/05/22/OzMDRs.png'
},
WECHAT_ENTERPRISE: {
title: '企业微信',
type: 30,
source: 'wechat_enterprise',
img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png'
}
}
/** /**
* 支付渠道枚举 * 支付渠道枚举
*/ */
...@@ -177,6 +187,7 @@ export const PayOrderStatusEnum = { ...@@ -177,6 +187,7 @@ export const PayOrderStatusEnum = {
} }
} }
// ========== MALL - 商品模块 ==========
/** /**
* 商品 SPU 状态 * 商品 SPU 状态
*/ */
...@@ -195,6 +206,7 @@ export const ProductSpuStatusEnum = { ...@@ -195,6 +206,7 @@ export const ProductSpuStatusEnum = {
} }
} }
// ========== MALL - 营销模块 ==========
/** /**
* 优惠劵模板的有限期类型的枚举 * 优惠劵模板的有限期类型的枚举
*/ */
...@@ -273,17 +285,22 @@ export const PromotionDiscountTypeEnum = { ...@@ -273,17 +285,22 @@ export const PromotionDiscountTypeEnum = {
} }
} }
// ========== MALL - 交易模块 ==========
/** /**
* 分销关系绑定模式枚举 * 分销关系绑定模式枚举
*/ */
export const BrokerageBindModeEnum = { export const BrokerageBindModeEnum = {
ANYTIME: { ANYTIME: {
mode: 0, mode: 1,
name: '没有推广人' name: '首次绑定'
}, },
REGISTER: { REGISTER: {
mode: 1, mode: 2,
name: '新用户' name: '注册绑定'
},
OVERRIDE: {
mode: 3,
name: '覆盖绑定'
} }
} }
/** /**
...@@ -291,11 +308,11 @@ export const BrokerageBindModeEnum = { ...@@ -291,11 +308,11 @@ export const BrokerageBindModeEnum = {
*/ */
export const BrokerageEnabledConditionEnum = { export const BrokerageEnabledConditionEnum = {
ALL: { ALL: {
condition: 0, condition: 1,
name: '人人分销' name: '人人分销'
}, },
ADMIN: { ADMIN: {
condition: 1, condition: 2,
name: '指定分销' name: '指定分销'
} }
} }
...@@ -358,3 +375,42 @@ export const BrokerageWithdrawTypeEnum = { ...@@ -358,3 +375,42 @@ export const BrokerageWithdrawTypeEnum = {
name: '支付宝' name: '支付宝'
} }
} }
/**
* 配送方式枚举
*/
export const DeliveryTypeEnum = {
EXPRESS: {
type: 1,
name: '快递发货'
},
PICK_UP: {
type: 2,
name: '到店自提'
}
}
/**
* 交易订单 - 状态
*/
export const TradeOrderStatusEnum = {
UNPAID: {
status: 0,
name: '待支付'
},
UNDELIVERED: {
status: 10,
name: '待发货'
},
DELIVERED: {
status: 20,
name: '已发货'
},
COMPLETED: {
status: 30,
name: '已完成'
},
CANCELED: {
status: 40,
name: '已取消'
}
}
...@@ -24,7 +24,7 @@ export const getDictOptions = (dictType: string) => { ...@@ -24,7 +24,7 @@ export const getDictOptions = (dictType: string) => {
return dictStore.getDictByType(dictType) || [] return dictStore.getDictByType(dictType) || []
} }
export const getIntDictOptions = (dictType: string) => { export const getIntDictOptions = (dictType: string): DictDataType[] => {
const dictOption: DictDataType[] = [] const dictOption: DictDataType[] = []
const dictOptions: DictDataType[] = getDictOptions(dictType) const dictOptions: DictDataType[] = getDictOptions(dictType)
dictOptions.forEach((dict: DictDataType) => { dictOptions.forEach((dict: DictDataType) => {
...@@ -180,5 +180,7 @@ export enum DICT_TYPE { ...@@ -180,5 +180,7 @@ export enum DICT_TYPE {
PROMOTION_COUPON_STATUS = 'promotion_coupon_status', // 优惠劵的状态 PROMOTION_COUPON_STATUS = 'promotion_coupon_status', // 优惠劵的状态
PROMOTION_COUPON_TAKE_TYPE = 'promotion_coupon_take_type', // 优惠劵的领取方式 PROMOTION_COUPON_TAKE_TYPE = 'promotion_coupon_take_type', // 优惠劵的领取方式
PROMOTION_ACTIVITY_STATUS = 'promotion_activity_status', // 优惠活动的状态 PROMOTION_ACTIVITY_STATUS = 'promotion_activity_status', // 优惠活动的状态
PROMOTION_CONDITION_TYPE = 'promotion_condition_type' // 营销的条件类型枚举 PROMOTION_CONDITION_TYPE = 'promotion_condition_type', // 营销的条件类型枚举
PROMOTION_BARGAIN_RECORD_STATUS = 'promotion_bargain_record_status', // 砍价记录的状态
PROMOTION_COMBINATION_RECORD_STATUS = 'promotion_combination_record_status' // 拼团记录的状态
} }
import dayjs from 'dayjs' import dayjs from 'dayjs'
/** /**
* 日期快捷选项适用于 el-date-picker
*/
export const defaultShortcuts = [
{
text: '今天',
value: () => {
return new Date()
}
},
{
text: '昨天',
value: () => {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24)
return [date, date]
}
},
{
text: '最近七天',
value: () => {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
return [date, new Date()]
}
},
{
text: '最近 30 天',
value: () => {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24 * 30)
return [date, new Date()]
}
},
{
text: '本月',
value: () => {
const date = new Date()
date.setDate(1) // 设置为当前月的第一天
return [date, new Date()]
}
},
{
text: '今年',
value: () => {
const date = new Date()
return [new Date(`${date.getFullYear()}-01-01`), date]
}
}
]
/**
* 时间日期转换 * 时间日期转换
* @param date 当前时间,new Date() 格式 * @param date 当前时间,new Date() 格式
* @param format 需要转换的时间格式字符串 * @param format 需要转换的时间格式字符串
...@@ -11,7 +62,7 @@ import dayjs from 'dayjs' ...@@ -11,7 +62,7 @@ import dayjs from 'dayjs'
* @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ" * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
* @returns 返回拼接后的时间字符串 * @returns 返回拼接后的时间字符串
*/ */
export function formatDate(date: Date | number, format?: string): string { export function formatDate(date: dayjs.ConfigType, format?: string): string {
// 日期不存在,则返回空 // 日期不存在,则返回空
if (!date) { if (!date) {
return '' return ''
...@@ -221,3 +272,68 @@ export function convertDate(param: Date | string) { ...@@ -221,3 +272,68 @@ export function convertDate(param: Date | string) {
} }
return param return param
} }
/**
* 指定的两个日期, 是否为同一天
* @param a 日期 A
* @param b 日期 B
*/
export function isSameDay(a: dayjs.ConfigType, b: dayjs.ConfigType): boolean {
if (!a || !b) return false
const aa = dayjs(a)
const bb = dayjs(b)
return aa.year() == bb.year() && aa.month() == bb.month() && aa.day() == bb.day()
}
/**
* 获取一天的开始时间、截止时间
* @param date 日期
* @param days 天数
*/
export function getDayRange(
date: dayjs.ConfigType,
days: number
): [dayjs.ConfigType, dayjs.ConfigType] {
const day = dayjs(date).add(days, 'd')
return getDateRange(day, day)
}
/**
* 获取最近7天的开始时间、截止时间
*/
export function getLast7Days(): [dayjs.ConfigType, dayjs.ConfigType] {
const lastWeekDay = dayjs().subtract(7, 'd')
const yesterday = dayjs().subtract(1, 'd')
return getDateRange(lastWeekDay, yesterday)
}
/**
* 获取最近30天的开始时间、截止时间
*/
export function getLast30Days(): [dayjs.ConfigType, dayjs.ConfigType] {
const lastMonthDay = dayjs().subtract(30, 'd')
const yesterday = dayjs().subtract(1, 'd')
return getDateRange(lastMonthDay, yesterday)
}
/**
* 获取最近1年的开始时间、截止时间
*/
export function getLast1Year(): [dayjs.ConfigType, dayjs.ConfigType] {
const lastYearDay = dayjs().subtract(1, 'y')
const yesterday = dayjs().subtract(1, 'd')
return getDateRange(lastYearDay, yesterday)
}
/**
* 获取指定日期的开始时间、截止时间
* @param beginDate 开始日期
* @param endDate 截止日期
*/
export function getDateRange(
beginDate: dayjs.ConfigType,
endDate: dayjs.ConfigType
): [dayjs.ConfigType, dayjs.ConfigType] {
return [dayjs(beginDate).startOf('d'), dayjs(endDate).endOf('d')]
}
import { fenToYuan } from '@/utils' import { floatToFixed2 } from '@/utils'
import { TableColumnCtx } from 'element-plus'
// 格式化金额【分转元】 // 格式化金额【分转元】
export const fenToYuanFormat = ( // @ts-ignore
row: any, export const fenToYuanFormat = (_, __, cellValue: any, ___) => {
column: TableColumnCtx<any>, return `¥${floatToFixed2(cellValue)}`
cellValue: any,
index: number
) => {
return `¥${fenToYuan(cellValue)}`
} }
...@@ -224,12 +224,12 @@ export const convertToInteger = (num: number | string | undefined): number => { ...@@ -224,12 +224,12 @@ export const convertToInteger = (num: number | string | undefined): number => {
* 元转分 * 元转分
*/ */
export const yuanToFen = (amount: string | number): number => { export const yuanToFen = (amount: string | number): number => {
return Math.round(Number(amount) * 100) return convertToInteger(amount)
} }
/** /**
* 分转元 * 分转元
*/ */
export const fenToYuan = (amount: string | number): number => { export const fenToYuan = (price: string | number): number => {
return Number((Number(amount) / 100).toFixed(2)) return formatToFraction(price)
} }
...@@ -13,7 +13,8 @@ export const defaultProps = { ...@@ -13,7 +13,8 @@ export const defaultProps = {
children: 'children', children: 'children',
label: 'name', label: 'name',
value: 'id', value: 'id',
isLeaf: 'leaf' isLeaf: 'leaf',
emitPath: false // 用于 cascader 组件:在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值
} }
const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config) const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
...@@ -377,10 +378,10 @@ export const treeToString = (tree: any[], nodeId) => { ...@@ -377,10 +378,10 @@ export const treeToString = (tree: any[], nodeId) => {
function performAThoroughValidation(arr) { function performAThoroughValidation(arr) {
for (const item of arr) { for (const item of arr) {
if (item.id === nodeId) { if (item.id === nodeId) {
str += `/${item.name}` str += ` / ${item.name}`
return true return true
} else if (typeof item.children !== 'undefined' && item.children.length !== 0) { } else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
str += `/${item.name}` str += ` / ${item.name}`
if (performAThoroughValidation(item.children)) { if (performAThoroughValidation(item.children)) {
return true return true
} }
......
...@@ -35,14 +35,14 @@ ...@@ -35,14 +35,14 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list" row-key="id" default-expand-all> <el-table v-loading="loading" :data="list" row-key="id" default-expand-all>
<el-table-column label="分类名称" prop="name" sortable /> <el-table-column label="名称" min-width="240" prop="name" sortable />
<el-table-column label="移动端分类图" align="center" prop="picUrl"> <el-table-column label="分类图标" align="center" min-width="80" prop="picUrl">
<template #default="scope"> <template #default="scope">
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="移动端分类图" class="h-30px" /> <img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="移动端分类图" class="h-36px" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="分类排序" align="center" prop="sort" /> <el-table-column label="排序" align="center" min-width="150" prop="sort" />
<el-table-column label="开启状态" align="center" prop="status"> <el-table-column label="状态" align="center" min-width="150" prop="status">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template> </template>
......
...@@ -59,9 +59,8 @@ ...@@ -59,9 +59,8 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="false"> <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="id" min-width="50" />
<el-table-column label="用户名称" align="center" prop="userNickname" width="80" /> <el-table-column label="商品信息" align="center" min-width="400">
<el-table-column label="商品信息" align="center" min-width="300">
<template #default="scope"> <template #default="scope">
<div class="row flex items-center gap-x-4px"> <div class="row flex items-center gap-x-4px">
<el-image <el-image
...@@ -82,10 +81,10 @@ ...@@ -82,10 +81,10 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="评分星级" align="center" prop="scores" width="80" /> <el-table-column label="用户名称" align="center" prop="userNickname" width="100" />
<el-table-column label="描述星级" align="center" prop="descriptionScores" width="80" /> <el-table-column label="商品评分" align="center" prop="descriptionScores" width="90" />
<el-table-column label="服务星级" align="center" prop="benefitScores" width="80" /> <el-table-column label="服务评分" align="center" prop="benefitScores" width="90" />
<el-table-column label="评论内容" align="center" prop="content" min-width="80"> <el-table-column label="评论内容" align="center" prop="content" min-width="210">
<template #default="scope"> <template #default="scope">
<p>{{ scope.row.content }}</p> <p>{{ scope.row.content }}</p>
<div class="flex justify-center gap-x-4px"> <div class="flex justify-center gap-x-4px">
...@@ -105,7 +104,7 @@ ...@@ -105,7 +104,7 @@
label="回复内容" label="回复内容"
align="center" align="center"
prop="replyContent" prop="replyContent"
min-width="100" min-width="250"
show-overflow-tooltip show-overflow-tooltip
/> />
<el-table-column <el-table-column
...@@ -113,7 +112,7 @@ ...@@ -113,7 +112,7 @@
align="center" align="center"
prop="createTime" prop="createTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="170" width="180"
/> />
<el-table-column label="是否展示" align="center" width="80px"> <el-table-column label="是否展示" align="center" width="80px">
<template #default="scope"> <template #default="scope">
......
...@@ -53,8 +53,8 @@ ...@@ -53,8 +53,8 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column align="center" label="编号" prop="id" /> <el-table-column align="center" label="编号" min-width="60" prop="id" />
<el-table-column align="center" label="名称" prop="name" /> <el-table-column align="center" label="属性名称" prop="name" min-width="150" />
<el-table-column :show-overflow-tooltip="true" align="center" label="备注" prop="remark" /> <el-table-column :show-overflow-tooltip="true" align="center" label="备注" prop="remark" />
<el-table-column <el-table-column
:formatter="dateFormatter" :formatter="dateFormatter"
...@@ -165,7 +165,7 @@ const handleDelete = async (id: number) => { ...@@ -165,7 +165,7 @@ const handleDelete = async (id: number) => {
/** 跳转商品属性列表 */ /** 跳转商品属性列表 */
const goValueList = (id: number) => { const goValueList = (id: number) => {
push({ path: '/product/property/value/' + id }) push({ name: 'ProductPropertyValue', params: { propertyId: id } })
} }
/** 初始化 **/ /** 初始化 **/
......
...@@ -45,8 +45,8 @@ ...@@ -45,8 +45,8 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" /> <el-table-column label="编号" align="center" min-width="60" prop="id" />
<el-table-column label="名称" align="center" prop="name" :show-overflow-tooltip="true" /> <el-table-column label="属性值名称" align="center" min-width="150" prop="name" />
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" /> <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column <el-table-column
label="创建时间" label="创建时间"
......
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
<el-table-column align="center" label="一级返佣(元)" min-width="168"> <el-table-column align="center" label="一级返佣(元)" min-width="168">
<template #default="{ row }"> <template #default="{ row }">
<el-input-number <el-input-number
v-model="row.firstBrokerageRecord" v-model="row.firstBrokeragePrice"
:min="0" :min="0"
:precision="2" :precision="2"
:step="0.1" :step="0.1"
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
<el-table-column align="center" label="二级返佣(元)" min-width="168"> <el-table-column align="center" label="二级返佣(元)" min-width="168">
<template #default="{ row }"> <template #default="{ row }">
<el-input-number <el-input-number
v-model="row.secondBrokerageRecord" v-model="row.secondBrokeragePrice"
:min="0" :min="0"
:precision="2" :precision="2"
:step="0.1" :step="0.1"
...@@ -181,12 +181,12 @@ ...@@ -181,12 +181,12 @@
<template v-if="formData!.subCommissionType"> <template v-if="formData!.subCommissionType">
<el-table-column align="center" label="一级返佣(元)" min-width="80"> <el-table-column align="center" label="一级返佣(元)" min-width="80">
<template #default="{ row }"> <template #default="{ row }">
{{ row.firstBrokerageRecord }} {{ row.firstBrokeragePrice }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="二级返佣(元)" min-width="80"> <el-table-column align="center" label="二级返佣(元)" min-width="80">
<template #default="{ row }"> <template #default="{ row }">
{{ row.secondBrokerageRecord }} {{ row.secondBrokeragePrice }}
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
...@@ -295,8 +295,8 @@ const skuList = ref<Sku[]>([ ...@@ -295,8 +295,8 @@ const skuList = ref<Sku[]>([
stock: 0, // 库存 stock: 0, // 库存
weight: 0, // 商品重量 weight: 0, // 商品重量
volume: 0, // 商品体积 volume: 0, // 商品体积
firstBrokerageRecord: 0, // 一级分销的佣金 firstBrokeragePrice: 0, // 一级分销的佣金
secondBrokerageRecord: 0 // 二级分销的佣金 secondBrokeragePrice: 0 // 二级分销的佣金
} }
]) // 批量添加时的临时数据 ]) // 批量添加时的临时数据
...@@ -415,8 +415,8 @@ const generateTableData = (propertyList: any[]) => { ...@@ -415,8 +415,8 @@ const generateTableData = (propertyList: any[]) => {
stock: 0, stock: 0,
weight: 0, weight: 0,
volume: 0, volume: 0,
firstBrokerageRecord: 0, firstBrokeragePrice: 0,
secondBrokerageRecord: 0 secondBrokeragePrice: 0
} }
// 如果存在属性相同的 sku 则不做处理 // 如果存在属性相同的 sku 则不做处理
const index = formData.value!.skus!.findIndex( const index = formData.value!.skus!.findIndex(
...@@ -491,8 +491,8 @@ watch( ...@@ -491,8 +491,8 @@ watch(
stock: 0, stock: 0,
weight: 0, weight: 0,
volume: 0, volume: 0,
firstBrokerageRecord: 0, firstBrokeragePrice: 0,
secondBrokerageRecord: 0 secondBrokeragePrice: 0
} }
] ]
} }
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
</el-table-column> </el-table-column>
<el-table-column align="center" label="销售价(元)" min-width="80"> <el-table-column align="center" label="销售价(元)" min-width="80">
<template #default="{ row }"> <template #default="{ row }">
{{ row.price }} {{ fenToYuan(row.price) }}
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
import { ElTable } from 'element-plus' import { ElTable } from 'element-plus'
import * as ProductSpuApi from '@/api/mall/product/spu' import * as ProductSpuApi from '@/api/mall/product/spu'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { fenToYuan } from '@/utils'
defineOptions({ name: 'SkuTableSelect' }) defineOptions({ name: 'SkuTableSelect' })
......
...@@ -15,15 +15,14 @@ ...@@ -15,15 +15,14 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="商品分类" prop="categoryId"> <el-form-item label="商品分类" prop="categoryId">
<el-tree-select <el-cascader
v-model="formData.categoryId" v-model="formData.categoryId"
:data="categoryList" :options="categoryList"
:props="defaultProps" :props="defaultProps"
check-strictly
class="w-1/1" class="w-1/1"
node-key="id" clearable
placeholder="请选择商品分类" placeholder="请选择商品分类"
@change="categoryNodeClick" filterable
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
...@@ -74,8 +73,6 @@ ...@@ -74,8 +73,6 @@
:value="item.id" :value="item.id"
/> />
</el-select> </el-select>
<!-- TODO 可能情况:善品录入后选择运费发现下拉选择中没有对应的模版 这里需不需要做添加运费模版后选择的功能 -->
<!-- <el-button class="ml-20px">运费模板</el-button>-->
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
...@@ -102,7 +99,7 @@ ...@@ -102,7 +99,7 @@
<el-form-item label="分销类型" props="subCommissionType"> <el-form-item label="分销类型" props="subCommissionType">
<el-radio-group v-model="formData.subCommissionType" @change="changeSubCommissionType"> <el-radio-group v-model="formData.subCommissionType" @change="changeSubCommissionType">
<el-radio :label="false">默认设置</el-radio> <el-radio :label="false">默认设置</el-radio>
<el-radio :label="true" class="radio">自行设置</el-radio> <el-radio :label="true" class="radio">单独设置</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
...@@ -117,7 +114,7 @@ ...@@ -117,7 +114,7 @@
/> />
</el-form-item> </el-form-item>
<el-form-item v-if="formData.specType" label="商品属性"> <el-form-item v-if="formData.specType" label="商品属性">
<el-button class="mb-10px mr-15px" @click="attributesAddFormRef.open">添加规格</el-button> <el-button class="mb-10px mr-15px" @click="attributesAddFormRef.open">添加属性</el-button>
<ProductAttributes :propertyList="propertyList" @success="generateSkus" /> <ProductAttributes :propertyList="propertyList" @success="generateSkus" />
</el-form-item> </el-form-item>
<template v-if="formData.specType && propertyList.length > 0"> <template v-if="formData.specType && propertyList.length > 0">
...@@ -139,7 +136,7 @@ ...@@ -139,7 +136,7 @@
<!-- 情况二:详情 --> <!-- 情况二:详情 -->
<Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema"> <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
<template #categoryId="{ row }"> {{ categoryString(row.categoryId) }}</template> <template #categoryId="{ row }"> {{ formatCategoryName(row.categoryId) }}</template>
<template #brandId="{ row }"> <template #brandId="{ row }">
{{ brandList.find((item) => item.id === row.brandId)?.name }} {{ brandList.find((item) => item.id === row.brandId)?.name }}
</template> </template>
...@@ -150,7 +147,7 @@ ...@@ -150,7 +147,7 @@
{{ row.specType ? '多规格' : '单规格' }} {{ row.specType ? '多规格' : '单规格' }}
</template> </template>
<template #subCommissionType="{ row }"> <template #subCommissionType="{ row }">
{{ row.subCommissionType ? '自行设置' : '默认设置' }} {{ row.subCommissionType ? '单独设置' : '默认设置' }}
</template> </template>
<template #picUrl="{ row }"> <template #picUrl="{ row }">
<el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" /> <el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" />
...@@ -206,17 +203,17 @@ const ruleConfig: RuleConfig[] = [ ...@@ -206,17 +203,17 @@ const ruleConfig: RuleConfig[] = [
{ {
name: 'price', name: 'price',
rule: (arg) => arg >= 0.01, rule: (arg) => arg >= 0.01,
message: '商品销售价格必须大于等于 0.01 !!!' message: '商品销售价格必须大于等于 0.01 !!!'
}, },
{ {
name: 'marketPrice', name: 'marketPrice',
rule: (arg) => arg >= 0.01, rule: (arg) => arg >= 0.01,
message: '商品市场价格必须大于等于 0.01 !!!' message: '商品市场价格必须大于等于 0.01 !!!'
}, },
{ {
name: 'costPrice', name: 'costPrice',
rule: (arg) => arg >= 0.01, rule: (arg) => arg >= 0.01,
message: '商品成本价格必须大于等于 0.01 !!!' message: '商品成本价格必须大于等于 0.01 !!!'
} }
] ]
...@@ -332,8 +329,8 @@ defineExpose({ validate }) ...@@ -332,8 +329,8 @@ defineExpose({ validate })
const changeSubCommissionType = () => { const changeSubCommissionType = () => {
// 默认为零,类型切换后也要重置为零 // 默认为零,类型切换后也要重置为零
for (const item of formData.skus) { for (const item of formData.skus) {
item.firstBrokerageRecord = 0 item.firstBrokeragePrice = 0
item.secondBrokerageRecord = 0 item.secondBrokeragePrice = 0
} }
} }
...@@ -352,30 +349,18 @@ const onChangeSpec = () => { ...@@ -352,30 +349,18 @@ const onChangeSpec = () => {
stock: 0, stock: 0,
weight: 0, weight: 0,
volume: 0, volume: 0,
firstBrokerageRecord: 0, firstBrokeragePrice: 0,
secondBrokerageRecord: 0 secondBrokeragePrice: 0
} }
] ]
} }
const categoryList = ref([]) // 分类树 const categoryList = ref([]) // 分类树
/** /** 获取分类的节点的完整结构 */
* 选择分类时触发校验 const formatCategoryName = (categoryId) => {
*/
const categoryNodeClick = () => {
if (!checkSelectedNode(categoryList.value, formData.categoryId)) {
formData.categoryId = null
message.warning('必须选择二级及以下节点!!')
}
}
/**
* 获取分类的节点的完整结构
*
* @param categoryId 分类id
*/
const categoryString = (categoryId) => {
return treeToString(categoryList.value, categoryId) return treeToString(categoryList.value, categoryId)
} }
const brandList = ref([]) // 精简商品品牌列表 const brandList = ref([]) // 精简商品品牌列表
const deliveryTemplateList = ref([]) // 运费模版 const deliveryTemplateList = ref([]) // 运费模版
onMounted(async () => { onMounted(async () => {
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<!-- TODO tag展示暂时不考虑排序 --> <!-- TODO @puhui999:tag展示暂时不考虑排序;支持拖动排序 -->
<el-form-item label="活动优先级"> <el-form-item label="活动优先级">
<el-tag>默认</el-tag> <el-tag>默认</el-tag>
<el-tag class="ml-2" type="success">秒杀</el-tag> <el-tag class="ml-2" type="success">秒杀</el-tag>
......
...@@ -82,8 +82,8 @@ const formData = ref<ProductSpuApi.Spu>({ ...@@ -82,8 +82,8 @@ const formData = ref<ProductSpuApi.Spu>({
stock: 0, // 库存 stock: 0, // 库存
weight: 0, // 商品重量 weight: 0, // 商品重量
volume: 0, // 商品体积 volume: 0, // 商品体积
firstBrokerageRecord: 0, // 一级分销的佣金 firstBrokeragePrice: 0, // 一级分销的佣金
secondBrokerageRecord: 0 // 二级分销的佣金 secondBrokeragePrice: 0 // 二级分销的佣金
} }
], ],
description: '', // 商品详情 description: '', // 商品详情
...@@ -102,7 +102,7 @@ const getDetail = async () => { ...@@ -102,7 +102,7 @@ const getDetail = async () => {
if ('ProductSpuDetail' === name) { if ('ProductSpuDetail' === name) {
isDetail.value = true isDetail.value = true
} }
const id = params.spuId as unknown as number const id = params.id as unknown as number
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
...@@ -112,15 +112,15 @@ const getDetail = async () => { ...@@ -112,15 +112,15 @@ const getDetail = async () => {
item.price = floatToFixed2(item.price) item.price = floatToFixed2(item.price)
item.marketPrice = floatToFixed2(item.marketPrice) item.marketPrice = floatToFixed2(item.marketPrice)
item.costPrice = floatToFixed2(item.costPrice) item.costPrice = floatToFixed2(item.costPrice)
item.firstBrokerageRecord = floatToFixed2(item.firstBrokerageRecord) item.firstBrokeragePrice = floatToFixed2(item.firstBrokeragePrice)
item.secondBrokerageRecord = floatToFixed2(item.secondBrokerageRecord) item.secondBrokeragePrice = floatToFixed2(item.secondBrokeragePrice)
} else { } else {
// 回显价格分转元 // 回显价格分转元
item.price = formatToFraction(item.price) item.price = formatToFraction(item.price)
item.marketPrice = formatToFraction(item.marketPrice) item.marketPrice = formatToFraction(item.marketPrice)
item.costPrice = formatToFraction(item.costPrice) item.costPrice = formatToFraction(item.costPrice)
item.firstBrokerageRecord = formatToFraction(item.firstBrokerageRecord) item.firstBrokeragePrice = formatToFraction(item.firstBrokeragePrice)
item.secondBrokerageRecord = formatToFraction(item.secondBrokerageRecord) item.secondBrokeragePrice = formatToFraction(item.secondBrokeragePrice)
} }
}) })
formData.value = res formData.value = res
...@@ -149,8 +149,8 @@ const submitForm = async () => { ...@@ -149,8 +149,8 @@ const submitForm = async () => {
item.price = convertToInteger(item.price) item.price = convertToInteger(item.price)
item.marketPrice = convertToInteger(item.marketPrice) item.marketPrice = convertToInteger(item.marketPrice)
item.costPrice = convertToInteger(item.costPrice) item.costPrice = convertToInteger(item.costPrice)
item.firstBrokerageRecord = convertToInteger(item.firstBrokerageRecord) item.firstBrokeragePrice = convertToInteger(item.firstBrokeragePrice)
item.secondBrokerageRecord = convertToInteger(item.secondBrokerageRecord) item.secondBrokeragePrice = convertToInteger(item.secondBrokeragePrice)
}) })
// 处理轮播图列表 // 处理轮播图列表
const newSliderPicUrls: any[] = [] const newSliderPicUrls: any[] = []
...@@ -161,7 +161,7 @@ const submitForm = async () => { ...@@ -161,7 +161,7 @@ const submitForm = async () => {
deepCopyFormData.sliderPicUrls = newSliderPicUrls deepCopyFormData.sliderPicUrls = newSliderPicUrls
// 校验都通过后提交表单 // 校验都通过后提交表单
const data = deepCopyFormData as ProductSpuApi.Spu const data = deepCopyFormData as ProductSpuApi.Spu
const id = params.spuId as unknown as number const id = params.id as unknown as number
if (!id) { if (!id) {
await ProductSpuApi.createSpu(data) await ProductSpuApi.createSpu(data)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
......
...@@ -18,15 +18,14 @@ ...@@ -18,15 +18,14 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="商品分类" prop="categoryId"> <el-form-item label="商品分类" prop="categoryId">
<el-tree-select <el-cascader
v-model="queryParams.categoryId" v-model="queryParams.categoryId"
:data="categoryList" :options="categoryList"
:props="defaultProps" :props="defaultProps"
check-strictly
class="w-1/1" class="w-1/1"
node-key="id" clearable
placeholder="请选择商品分类" placeholder="请选择商品分类"
@change="nodeClick" filterable
/> />
</el-form-item> </el-form-item>
<el-form-item label="创建时间" prop="createTime"> <el-form-item label="创建时间" prop="createTime">
...@@ -78,7 +77,7 @@ ...@@ -78,7 +77,7 @@
/> />
</el-tabs> </el-tabs>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column type="expand" width="30"> <el-table-column type="expand">
<template #default="{ row }"> <template #default="{ row }">
<el-form class="spu-table-expand" label-position="left"> <el-form class="spu-table-expand" label-position="left">
<el-row> <el-row>
...@@ -86,17 +85,17 @@ ...@@ -86,17 +85,17 @@
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="商品分类:"> <el-form-item label="商品分类:">
<span>{{ categoryString(row.categoryId) }}</span> <span>{{ formatCategoryName(row.categoryId) }}</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="市场价:"> <el-form-item label="市场价:">
<span>{{ floatToFixed2(row.marketPrice) }}</span> <span>{{ fenToYuan(row.marketPrice) }}</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="成本价:"> <el-form-item label="成本价:">
<span>{{ floatToFixed2(row.costPrice) }}</span> <span>{{ fenToYuan(row.costPrice) }}</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
...@@ -106,9 +105,8 @@ ...@@ -106,9 +105,8 @@
<el-col :span="24"> <el-col :span="24">
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="收藏:"> <el-form-item label="浏览量:">
<!-- TODO 没有这个属性,暂时写死 5 个 --> <span>{{ row.browseCount }}</span>
<span>5</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
...@@ -122,7 +120,7 @@ ...@@ -122,7 +120,7 @@
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column key="id" align="center" label="商品编号" prop="id" /> <el-table-column align="center" label="商品编号" min-width="60" prop="id" />
<el-table-column label="商品图" min-width="80"> <el-table-column label="商品图" min-width="80">
<template #default="{ row }"> <template #default="{ row }">
<el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" /> <el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" />
...@@ -130,7 +128,7 @@ ...@@ -130,7 +128,7 @@
</el-table-column> </el-table-column>
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" /> <el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
<el-table-column align="center" label="商品售价" min-width="90" prop="price"> <el-table-column align="center" label="商品售价" min-width="90" prop="price">
<template #default="{ row }"> {{ floatToFixed2(row.price) }}</template> <template #default="{ row }"> {{ fenToYuan(row.price) }}</template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="销量" min-width="90" prop="salesCount" /> <el-table-column align="center" label="销量" min-width="90" prop="salesCount" />
<el-table-column align="center" label="库存" min-width="90" prop="stock" /> <el-table-column align="center" label="库存" min-width="90" prop="stock" />
...@@ -152,7 +150,7 @@ ...@@ -152,7 +150,7 @@
active-text="上架" active-text="上架"
inactive-text="下架" inactive-text="下架"
inline-prompt inline-prompt
@change="changeStatus(row)" @change="handleStatusChange(row)"
/> />
</template> </template>
<template v-else> <template v-else>
...@@ -191,7 +189,7 @@ ...@@ -191,7 +189,7 @@
v-hasPermi="['product:spu:update']" v-hasPermi="['product:spu:update']"
link link
type="primary" type="primary"
@click="changeStatus(row, ProductSpuStatusEnum.DISABLE.status)" @click="handleStatus02Change(row, ProductSpuStatusEnum.DISABLE.status)"
> >
恢复到仓库 恢复到仓库
</el-button> </el-button>
...@@ -201,7 +199,7 @@ ...@@ -201,7 +199,7 @@
v-hasPermi="['product:spu:update']" v-hasPermi="['product:spu:update']"
link link
type="primary" type="primary"
@click="changeStatus(row, ProductSpuStatusEnum.RECYCLE.status)" @click="handleStatus02Change(row, ProductSpuStatusEnum.RECYCLE.status)"
> >
加入回收站 加入回收站
</el-button> </el-button>
...@@ -220,12 +218,11 @@ ...@@ -220,12 +218,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { TabsPaneContext } from 'element-plus' import { TabsPaneContext } from 'element-plus'
import { cloneDeep } from 'lodash-es'
import { createImageViewer } from '@/components/ImageViewer' import { createImageViewer } from '@/components/ImageViewer'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree' import { defaultProps, handleTree, treeToString } from '@/utils/tree'
import { ProductSpuStatusEnum } from '@/utils/constants' import { ProductSpuStatusEnum } from '@/utils/constants'
import { floatToFixed2 } from '@/utils' import { fenToYuan } from '@/utils'
import download from '@/utils/download' import download from '@/utils/download'
import * as ProductSpuApi from '@/api/mall/product/spu' import * as ProductSpuApi from '@/api/mall/product/spu'
import * as ProductCategoryApi from '@/api/mall/product/category' import * as ProductCategoryApi from '@/api/mall/product/category'
...@@ -254,7 +251,7 @@ const tabsData = ref([ ...@@ -254,7 +251,7 @@ const tabsData = ref([
}, },
{ {
count: 0, count: 0,
name: '已经售空商品', name: '已售罄商品',
type: 2 type: 2
}, },
{ {
...@@ -303,43 +300,37 @@ const getList = async () => { ...@@ -303,43 +300,37 @@ const getList = async () => {
} }
} }
/** /** 添加到仓库 / 回收站的状态 */
* 更改 SPU 状态 const handleStatus02Change = async (row, newStatus: number) => {
* try {
* @param row // 二次确认
* @param status 更改前的值 const text = newStatus === ProductSpuStatusEnum.RECYCLE.status ? '加入到回收站' : '恢复到仓库'
*/ await message.confirm(`确认要"${row.name}"${text}吗?`)
const changeStatus = async (row, status?: number) => { // 发起修改
const deepCopyValue = cloneDeep(unref(row)) await ProductSpuApi.updateStatus({ id: row.id, status: newStatus })
if (typeof status !== 'undefined') deepCopyValue.status = status message.success(text + '成功')
// 刷新 tabs 数据
await getTabsCount()
// 刷新列表
await getList()
} catch {}
}
/** 更新上架/下架状态 */
const handleStatusChange = async (row) => {
try { try {
let text = '' // 二次确认
switch (deepCopyValue.status) { const text = row.status ? '上架' : '下架'
case ProductSpuStatusEnum.DISABLE.status: await message.confirm(`确认要${text}"${row.name}"吗?`)
text = ProductSpuStatusEnum.DISABLE.name // 发起修改
break await ProductSpuApi.updateStatus({ id: row.id, status: row.status })
case ProductSpuStatusEnum.ENABLE.status: message.success(text + '成功')
text = ProductSpuStatusEnum.ENABLE.name
break
case ProductSpuStatusEnum.RECYCLE.status:
text = `加入${ProductSpuStatusEnum.RECYCLE.name}`
break
}
await message.confirm(
deepCopyValue.status === -1
? `确认要将[${row.name}]${text}吗?`
: row.status === -1 // 再判断一次原对象是否等于-1,例: 把回收站中的商品恢复到仓库中,事件触发时原对象status为-1 深拷贝对象status被赋值为0
? `确认要将[${row.name}]恢复到仓库吗?`
: `确认要${text}[${row.name}]吗?`
)
await ProductSpuApi.updateStatus({ id: deepCopyValue.id, status: deepCopyValue.status })
message.success('更新状态成功')
// 刷新 tabs 数据 // 刷新 tabs 数据
await getTabsCount() await getTabsCount()
// 刷新列表 // 刷新列表
await getList() await getList()
} catch { } catch {
// 取消更改状态时回显数据 // 异常时,需要重置回之前的值
row.status = row.status =
row.status === ProductSpuStatusEnum.DISABLE.status row.status === ProductSpuStatusEnum.DISABLE.status
? ProductSpuStatusEnum.ENABLE.status ? ProductSpuStatusEnum.ENABLE.status
...@@ -380,26 +371,20 @@ const resetQuery = () => { ...@@ -380,26 +371,20 @@ const resetQuery = () => {
handleQuery() handleQuery()
} }
/** /** 新增或修改 */
* 新增或修改
*
* @param id 商品 SPU 编号
*/
const openForm = (id?: number) => { const openForm = (id?: number) => {
// 修改 // 修改
if (typeof id === 'number') { if (typeof id === 'number') {
push({ name: 'ProductSpuEdit', params: { spuId: id } }) push({ name: 'ProductSpuEdit', params: { id } })
return return
} }
// 新增 // 新增
push({ name: 'ProductSpuAdd' }) push({ name: 'ProductSpuAdd' })
} }
/** /** 查看商品详情 */
* 查看商品详情
*/
const openDetail = (id: number) => { const openDetail = (id: number) => {
push({ name: 'ProductSpuDetail', params: { spuId: id } }) push({ name: 'ProductSpuDetail', params: { id } })
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
...@@ -417,6 +402,12 @@ const handleExport = async () => { ...@@ -417,6 +402,12 @@ const handleExport = async () => {
} }
} }
const categoryList = ref() // 分类树
/** 获取分类的节点的完整结构 */
const formatCategoryName = (categoryId) => {
return treeToString(categoryList.value, categoryId)
}
// 监听路由变化更新列表,解决商品保存后,列表不刷新的问题。 // 监听路由变化更新列表,解决商品保存后,列表不刷新的问题。
watch( watch(
() => currentRoute.value, () => currentRoute.value,
...@@ -425,25 +416,6 @@ watch( ...@@ -425,25 +416,6 @@ watch(
} }
) )
const categoryList = ref() // 分类树
/**
* 获取分类的节点的完整结构
* @param categoryId 分类id
*/
const categoryString = (categoryId) => {
return treeToString(categoryList.value, categoryId)
}
/**
* 校验所选是否为二级及以下节点
*/
const nodeClick = () => {
if (!checkSelectedNode(categoryList.value, queryParams.value.categoryId)) {
queryParams.value.categoryId = null
message.warning('必须选择二级及以下节点!!')
}
}
/** 初始化 **/ /** 初始化 **/
onMounted(async () => { onMounted(async () => {
await getTabsCount() await getTabsCount()
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
<el-table-column align="center" label="砍价底价(元)" min-width="168"> <el-table-column align="center" label="砍价底价(元)" min-width="168">
<template #default="{ row: sku }"> <template #default="{ row: sku }">
<el-input-number <el-input-number
v-model="sku.productConfig.bargainPrice" v-model="sku.productConfig.bargainMinPrice"
:min="0" :min="0"
:precision="2" :precision="2"
:step="0.1" :step="0.1"
...@@ -61,6 +61,7 @@ import { SpuAndSkuList, SpuProperty, SpuSelect } from '@/views/mall/promotion/co ...@@ -61,6 +61,7 @@ import { SpuAndSkuList, SpuProperty, SpuSelect } from '@/views/mall/promotion/co
import { getPropertyList, RuleConfig } from '@/views/mall/product/spu/components' import { getPropertyList, RuleConfig } from '@/views/mall/product/spu/components'
import * as ProductSpuApi from '@/api/mall/product/spu' import * as ProductSpuApi from '@/api/mall/product/spu'
import { convertToInteger, formatToFraction } from '@/utils' import { convertToInteger, formatToFraction } from '@/utils'
import { cloneDeep } from 'lodash-es'
defineOptions({ name: 'PromotionBargainActivityForm' }) defineOptions({ name: 'PromotionBargainActivityForm' })
...@@ -86,7 +87,7 @@ const ruleConfig: RuleConfig[] = [ ...@@ -86,7 +87,7 @@ const ruleConfig: RuleConfig[] = [
message: '商品砍价起始价格不能小于 0 !!!' message: '商品砍价起始价格不能小于 0 !!!'
}, },
{ {
name: 'productConfig.bargainPrice', name: 'productConfig.bargainMinPrice',
rule: (arg) => arg >= 0, rule: (arg) => arg >= 0,
message: '商品砍价底价不能小于 0 !!!' message: '商品砍价底价不能小于 0 !!!'
}, },
...@@ -123,14 +124,14 @@ const getSpuDetails = async ( ...@@ -123,14 +124,14 @@ const getSpuDetails = async (
spuId: spu.id!, spuId: spu.id!,
skuId: sku.id!, skuId: sku.id!,
bargainFirstPrice: 1, bargainFirstPrice: 1,
bargainPrice: 1, bargainMinPrice: 1,
stock: 1 stock: 1
} }
if (typeof products !== 'undefined') { if (typeof products !== 'undefined') {
const product = products.find((item) => item.skuId === sku.id) const product = products.find((item) => item.skuId === sku.id)
if (product) { if (product) {
product.bargainFirstPrice = formatToFraction(product.bargainFirstPrice) product.bargainFirstPrice = formatToFraction(product.bargainFirstPrice)
product.bargainPrice = formatToFraction(product.bargainPrice) product.bargainMinPrice = formatToFraction(product.bargainMinPrice)
} }
config = product || config config = product || config
} }
...@@ -173,7 +174,7 @@ const open = async (type: string, id?: number) => { ...@@ -173,7 +174,7 @@ const open = async (type: string, id?: number) => {
spuId: data.spuId!, spuId: data.spuId!,
skuId: data.skuId, skuId: data.skuId,
bargainFirstPrice: data.bargainFirstPrice, // 砍价起始价格,单位分 bargainFirstPrice: data.bargainFirstPrice, // 砍价起始价格,单位分
bargainPrice: data.bargainPrice, // 砍价底价 bargainMinPrice: data.bargainMinPrice, // 砍价底价
stock: data.stock // 活动库存 stock: data.stock // 活动库存
} }
] ]
...@@ -204,12 +205,12 @@ const submitForm = async () => { ...@@ -204,12 +205,12 @@ const submitForm = async () => {
// 提交请求 // 提交请求
formLoading.value = true formLoading.value = true
try { try {
const data = formRef.value.formModel as BargainActivityApi.BargainActivityVO const data = cloneDeep(formRef.value.formModel) as BargainActivityApi.BargainActivityVO
const products = spuAndSkuListRef.value.getSkuConfigs('productConfig') const products = spuAndSkuListRef.value.getSkuConfigs('productConfig')
products.forEach((item: BargainProductVO) => { products.forEach((item: BargainProductVO) => {
// 砍价价格元转分 // 砍价价格元转分
item.bargainFirstPrice = convertToInteger(item.bargainFirstPrice) item.bargainFirstPrice = convertToInteger(item.bargainFirstPrice)
item.bargainPrice = convertToInteger(item.bargainPrice) item.bargainMinPrice = convertToInteger(item.bargainMinPrice)
}) })
// 用户每次砍价金额分转元, 元转分 // 用户每次砍价金额分转元, 元转分
data.randomMinPrice = convertToInteger(data.randomMinPrice) data.randomMinPrice = convertToInteger(data.randomMinPrice)
......
...@@ -6,7 +6,7 @@ export const rules = reactive({ ...@@ -6,7 +6,7 @@ export const rules = reactive({
name: [required], name: [required],
startTime: [required], startTime: [required],
endTime: [required], endTime: [required],
userSize: [required], helpMaxCount: [required],
bargainCount: [required], bargainCount: [required],
singleLimitCount: [required] singleLimitCount: [required]
}) })
...@@ -72,7 +72,7 @@ const crudSchemas = reactive<CrudSchema[]>([ ...@@ -72,7 +72,7 @@ const crudSchemas = reactive<CrudSchema[]>([
}, },
{ {
label: '砍价人数', label: '砍价人数',
field: 'userSize', field: 'helpMaxCount',
isSearch: false, isSearch: false,
form: { form: {
component: 'InputNumber', component: 'InputNumber',
...@@ -133,20 +133,6 @@ const crudSchemas = reactive<CrudSchema[]>([ ...@@ -133,20 +133,6 @@ const crudSchemas = reactive<CrudSchema[]>([
} }
}, },
{ {
label: '砍价成功数量',
field: 'successCount',
isSearch: false,
isForm: false
},
{
label: '活动状态',
field: 'status',
dictType: DICT_TYPE.COMMON_STATUS,
dictClass: 'number',
isSearch: true,
isForm: false
},
{
label: '拼团商品', label: '拼团商品',
field: 'spuId', field: 'spuId',
isSearch: false, isSearch: false,
...@@ -155,11 +141,6 @@ const crudSchemas = reactive<CrudSchema[]>([ ...@@ -155,11 +141,6 @@ const crudSchemas = reactive<CrudSchema[]>([
span: 24 span: 24
} }
} }
},
{
label: '操作',
field: 'action',
isForm: false
} }
]) ])
export const { allSchemas } = useCrudSchemas(crudSchemas) export const { allSchemas } = useCrudSchemas(crudSchemas)
<template> <template>
<!-- 搜索工作栏 -->
<ContentWrap> <ContentWrap>
<Search :schema="allSchemas.searchSchema" @reset="setSearchParams" @search="setSearchParams"> <!-- 搜索工作栏 -->
<!-- 新增等操作按钮 --> <el-form
<template #actionMore> 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 <el-button
v-hasPermi="['promotion:bargain-activity:create']"
plain
type="primary" type="primary"
plain
@click="openForm('create')" @click="openForm('create')"
v-hasPermi="['promotion:bargain-activity:create']"
> >
<Icon class="mr-5px" icon="ep:plus" /> <Icon icon="ep:plus" class="mr-5px" /> 新增
新增
</el-button> </el-button>
</template> </el-form-item>
</Search> </el-form>
</ContentWrap> </ContentWrap>
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<Table <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
v-model:currentPage="tableObject.currentPage" <el-table-column label="活动编号" prop="id" min-width="80" />
v-model:pageSize="tableObject.pageSize" <el-table-column label="活动名称" prop="name" min-width="140" />
:columns="allSchemas.tableColumns" <el-table-column label="活动时间" min-width="210">
:data="tableObject.tableList" <template #default="scope">
:loading="tableObject.loading" {{ formatDate(scope.row.startTime, 'YYYY-MM-DD') }}
:pagination="{ ~ {{ formatDate(scope.row.endTime, 'YYYY-MM-DD') }}
total: tableObject.total </template>
}" </el-table-column>
> <el-table-column label="商品图片" prop="spuName" min-width="80">
<template #spuId="{ row }"> <template #default="scope">
<el-image <el-image
:src="row.picUrl" :src="scope.row.picUrl"
class="mr-5px h-30px w-30px align-middle" class="h-40px w-40px"
@click="imagePreview(row.picUrl)" :preview-src-list="[scope.row.picUrl]"
/> preview-teleported
<span class="align-middle">{{ row.spuName }}</span> />
</template> </template>
<template #action="{ row }"> </el-table-column>
<el-button <el-table-column label="商品标题" prop="spuName" min-width="300" />
v-hasPermi="['promotion:bargain-activity:update']" <el-table-column
link label="起始价格"
type="primary" prop="bargainFirstPrice"
@click="openForm('update', row.id)" min-width="100"
> :formatter="fenToYuanFormat"
编辑 />
</el-button> <el-table-column
<el-button label="砍价底价"
v-hasPermi="['promotion:bargain-activity:delete']" prop="bargainMinPrice"
link min-width="100"
type="danger" :formatter="fenToYuanFormat"
@click="handleDelete(row.id)" />
> <el-table-column label="总砍价人数" prop="recordUserCount" min-width="100" />
删除 <el-table-column label="成功砍价人数" prop="recordSuccessUserCount" min-width="110" />
</el-button> <el-table-column label="助力人数" prop="helpUserCount" min-width="100" />
</template> <el-table-column label="活动状态" align="center" prop="status" min-width="100">
</Table> <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="stock" min-width="80" />
<el-table-column label="总库存" align="center" prop="totalStock" min-width="80" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" width="150px" fixed="right">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['promotion:bargain-activity:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleClose(scope.row.id)"
v-if="scope.row.status === 0"
v-hasPermi="['promotion:bargain-activity:close']"
>
关闭
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-else
v-hasPermi="['promotion:bargain-activity: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> </ContentWrap>
<!-- 表单弹窗:添加/修改 --> <!-- 表单弹窗:添加/修改 -->
<BargainActivityForm ref="formRef" @success="getList" /> <BargainActivityForm ref="formRef" @success="getList" />
</template> </template>
<script lang="ts" setup>
import { allSchemas } from './bargainActivity.data' <script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import * as BargainActivityApi from '@/api/mall/promotion/bargain/bargainActivity' import * as BargainActivityApi from '@/api/mall/promotion/bargain/bargainActivity'
import BargainActivityForm from './BargainActivityForm.vue' import BargainActivityForm from './BargainActivityForm.vue'
import { createImageViewer } from '@/components/ImageViewer' import { formatDate } from '@/utils/formatTime'
import { sortTableColumns } from '@/hooks/web/useCrudSchemas' import { fenToYuanFormat } from '@/utils/formatter'
defineOptions({ name: 'PromotionBargainActivity' }) defineOptions({ name: 'PromotionBargainActivity' })
// tableObject:表格的属性对象,可获得分页大小、条数等属性 const message = useMessage() // 消息弹窗
// tableMethods:表格的操作对象,可进行获得分页、删除记录等操作 const { t } = useI18n() // 国际化
// 详细可见:https://doc.iocoder.cn/vue3/crud-schema/
const { tableObject, tableMethods } = useTable({ const loading = ref(true) // 列表的加载中
getListApi: BargainActivityApi.getBargainActivityPage, // 分页接口 const total = ref(0) // 列表的总页数
delListApi: BargainActivityApi.deleteBargainActivity // 删除接口 const list = ref([]) // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: null,
status: null
}) })
// 获得表格的各种操作 const queryFormRef = ref() // 搜索的表单
const { getList, setSearchParams } = tableMethods const exportLoading = ref(false) // 导出的加载中
/** 商品图预览 */ /** 查询列表 */
const imagePreview = (imgUrl: string) => { const getList = async () => {
createImageViewer({ loading.value = true
urlList: [imgUrl] try {
}) const data = await BargainActivityApi.getBargainActivityPage(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()
} }
/** 添加/修改操作 */ /** 添加/修改操作 */
...@@ -93,15 +197,35 @@ const openForm = (type: string, id?: number) => { ...@@ -93,15 +197,35 @@ const openForm = (type: string, id?: number) => {
formRef.value.open(type, id) formRef.value.open(type, id)
} }
// TODO 芋艿:这里要改下
/** 关闭按钮操作 */
const handleClose = async (id: number) => {
try {
// 关闭的二次确认
await message.confirm('确认关闭该砍价活动吗?')
// 发起关闭
await BargainActivityApi.closeSeckillActivity(id)
message.success('关闭成功')
// 刷新列表
await getList()
} catch {}
}
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = (id: number) => { const handleDelete = async (id: number) => {
tableMethods.delList(id, false) try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await BargainActivityApi.closeBargainActivity(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
} }
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(async () => {
// 获得活动列表 await getList()
sortTableColumns(allSchemas.tableColumns, 'spuId')
getList()
}) })
</script> </script>
<template>
<Dialog v-model="dialogVisible" title="助力列表">
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="用户编号" prop="userId" min-width="80px" />
<el-table-column label="用户头像" prop="avatar" min-width="80px">
<template #default="scope">
<el-avatar :src="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column label="用户昵称" prop="nickname" min-width="100px" />
<el-table-column
label="砍价金额"
prop="reducePrice"
min-width="100px"
:formatter="fenToYuanFormat"
/>
<el-table-column
label="助力时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
</Dialog>
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import * as BargainHelpApi from '@/api/mall/promotion/bargain/bargainHelp'
import { fenToYuanFormat } from '@/utils/formatter'
/** 助力列表 */
defineOptions({ name: 'BargainRecordListDialog' })
const message = useMessage() // 消息弹窗
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
recordId: undefined
})
const queryFormRef = ref() // 搜索的表单
/** 打开弹窗 */
const dialogVisible = ref(false) // 弹窗的是否展示
const open = async (recordId: any) => {
dialogVisible.value = true
queryParams.recordId = recordId
resetQuery()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await BargainHelpApi.getBargainHelpPage(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()
}
</script>
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<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.PROMOTION_BARGAIN_RECORD_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"
plain
@click="openForm('create')"
v-hasPermi="['promotion:bargain-record:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['promotion:bargain-record:export']"
>
<Icon icon="ep:download" 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="编号" min-width="50" prop="id" />
<el-table-column label="发起用户" min-width="120">
<template #default="scope">
<el-image
:src="scope.row.avatar"
class="h-20px w-20px"
:preview-src-list="[scope.row.avatar]"
preview-teleported
/>
{{ scope.row.nickname }}
</template>
</el-table-column>
<el-table-column
label="发起时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="砍价活动" min-width="150" prop="activity.name" />
<el-table-column
label="最低价"
min-width="100"
prop="activity.bargainMinPrice"
:formatter="fenToYuanFormat"
/>
<el-table-column
label="当前价"
min-width="100"
prop="bargainPrice"
:formatter="fenToYuanFormat"
/>
<el-table-column label="总砍价次数" min-width="100" prop="activity.helpMaxCount" />
<el-table-column label="剩余砍价次数" min-width="100" prop="helpCount" />
<el-table-column label="砍价状态" align="center" prop="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_BARGAIN_RECORD_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
label="结束时间"
align="center"
prop="endTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="订单编号" align="center" prop="orderId" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openRecordListDialog(scope.row.id)"
v-hasPermi="['promotion:bargain-help:query']"
>
助力
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗 -->
<BargainRecordListDialog ref="recordListDialogRef" />
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import * as BargainRecordApi from '@/api/mall/promotion/bargain/bargainRecord'
import { fenToYuanFormat } from '@/utils/formatter'
import BargainRecordListDialog from './BargainRecordListDialog.vue'
defineOptions({ name: 'PromotionBargainRecord' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
status: null,
createTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await BargainRecordApi.getBargainRecordPage(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 recordListDialogRef = ref()
const openRecordListDialog = (id?: number) => {
recordListDialogRef.value.open(id)
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
...@@ -167,7 +167,7 @@ const submitForm = async () => { ...@@ -167,7 +167,7 @@ const submitForm = async () => {
products.forEach((item: CombinationActivityApi.CombinationProductVO) => { products.forEach((item: CombinationActivityApi.CombinationProductVO) => {
item.combinationPrice = convertToInteger(item.combinationPrice) item.combinationPrice = convertToInteger(item.combinationPrice)
}) })
const data = formRef.value.formModel as CombinationActivityApi.CombinationActivityVO const data = cloneDeep(formRef.value.formModel) as CombinationActivityApi.CombinationActivityVO
data.products = products data.products = products
// 真正提交 // 真正提交
if (formType.value === 'create') { if (formType.value === 'create') {
......
...@@ -9,7 +9,8 @@ export const rules = reactive({ ...@@ -9,7 +9,8 @@ export const rules = reactive({
startTime: [required], startTime: [required],
endTime: [required], endTime: [required],
userSize: [required], userSize: [required],
limitDuration: [required] limitDuration: [required],
virtualGroup: [required]
}) })
// CrudSchema https://doc.iocoder.cn/vue3/crud-schema/ // CrudSchema https://doc.iocoder.cn/vue3/crud-schema/
...@@ -115,30 +116,15 @@ const crudSchemas = reactive<CrudSchema[]>([ ...@@ -115,30 +116,15 @@ const crudSchemas = reactive<CrudSchema[]>([
} }
}, },
{ {
label: '购买人数', label: '虚拟成团',
field: 'userSize', field: 'virtualGroup',
isSearch: false, dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
isForm: false dictClass: 'boolean',
},
{
label: '开团组数',
field: 'totalCount',
isSearch: false,
isForm: false
},
{
label: '成团组数',
field: 'successCount',
isSearch: false,
isForm: false
},
{
label: '活动状态',
field: 'status',
dictType: DICT_TYPE.COMMON_STATUS,
dictClass: 'number',
isSearch: true, isSearch: true,
isForm: false form: {
component: 'Radio',
value: false
}
}, },
{ {
label: '拼团商品', label: '拼团商品',
...@@ -149,11 +135,6 @@ const crudSchemas = reactive<CrudSchema[]>([ ...@@ -149,11 +135,6 @@ const crudSchemas = reactive<CrudSchema[]>([
span: 24 span: 24
} }
} }
},
{
label: '操作',
field: 'action',
isForm: false
} }
]) ])
export const { allSchemas } = useCrudSchemas(crudSchemas) export const { allSchemas } = useCrudSchemas(crudSchemas)
<template> <template>
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<ContentWrap> <ContentWrap>
<Search :schema="allSchemas.searchSchema" @reset="setSearchParams" @search="setSearchParams"> <!-- 搜索工作栏 -->
<!-- 新增等操作按钮 --> <el-form
<template #actionMore> 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 <el-button
v-hasPermi="['promotion:combination-activity:create']"
plain
type="primary" type="primary"
plain
@click="openForm('create')" @click="openForm('create')"
v-hasPermi="['promotion:combination-activity:create']"
> >
<Icon class="mr-5px" icon="ep:plus" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> </el-button>
</template> </el-form-item>
</Search> </el-form>
</ContentWrap> </ContentWrap>
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<Table <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
v-model:currentPage="tableObject.currentPage" <el-table-column label="活动编号" prop="id" min-width="80" />
v-model:pageSize="tableObject.pageSize" <el-table-column label="活动名称" prop="name" min-width="140" />
:columns="allSchemas.tableColumns" <el-table-column label="活动时间" min-width="210">
:data="tableObject.tableList" <template #default="scope">
:loading="tableObject.loading" {{ formatDate(scope.row.startTime, 'YYYY-MM-DD') }}
:pagination="{ ~ {{ formatDate(scope.row.endTime, 'YYYY-MM-DD') }}
total: tableObject.total </template>
}" </el-table-column>
> <el-table-column label="商品图片" prop="spuName" min-width="80">
<template #spuId="{ row }"> <template #default="scope">
<el-image <el-image
:src="row.picUrl" :src="scope.row.picUrl"
class="mr-5px h-30px w-30px align-middle" class="h-40px w-40px"
@click="imagePreview(row.picUrl)" :preview-src-list="[scope.row.picUrl]"
/> preview-teleported
<span class="align-middle">{{ row.spuName }}</span> />
</template> </template>
<template #action="{ row }"> </el-table-column>
<el-button <el-table-column label="商品标题" prop="spuName" min-width="300" />
v-hasPermi="['promotion:combination-activity:update']" <el-table-column
link label="原价"
type="primary" prop="marketPrice"
@click="openForm('update', row.id)" min-width="100"
> :formatter="fenToYuanFormat"
编辑 />
</el-button> <el-table-column label="拼团价" prop="seckillPrice" min-width="100">
<el-button <template #default="scope">
v-hasPermi="['promotion:combination-activity:delete']" {{ formatCombinationPrice(scope.row.products) }}
link </template>
type="danger" </el-table-column>
@click="handleDelete(row.id)" <el-table-column label="开团组数" prop="groupCount" min-width="100" />
> <el-table-column label="成团组数" prop="groupSuccessCount" min-width="100" />
删除 <el-table-column label="购买次数" prop="recordCount" min-width="100" />
</el-button> <el-table-column label="活动状态" align="center" prop="status" min-width="100">
</template> <template #default="scope">
</Table> <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"
width="180px"
/>
<el-table-column label="操作" align="center" width="150px" fixed="right">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['promotion:combination-activity:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleClose(scope.row.id)"
v-if="scope.row.status === 0"
v-hasPermi="['promotion:combination-activity:close']"
>
关闭
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-else
v-hasPermi="['promotion:combination-activity: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> </ContentWrap>
<!-- 表单弹窗:添加/修改 --> <!-- 表单弹窗:添加/修改 -->
<CombinationActivityForm ref="formRef" @success="getList" /> <CombinationActivityForm ref="formRef" @success="getList" />
</template> </template>
<script lang="ts" setup>
import { allSchemas } from './combinationActivity.data' <script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity' import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
import CombinationActivityForm from './CombinationActivityForm.vue' import CombinationActivityForm from './CombinationActivityForm.vue'
import { sortTableColumns } from '@/hooks/web/useCrudSchemas' import { formatDate } from '@/utils/formatTime'
import { createImageViewer } from '@/components/ImageViewer' import { fenToYuanFormat } from '@/utils/formatter'
import { fenToYuan } from '@/utils'
defineOptions({ name: 'PromotionBargainActivity' })
defineOptions({ name: 'PromotionCombinationActivity' }) const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
// tableObject:表格的属性对象,可获得分页大小、条数等属性 const loading = ref(true) // 列表的加载中
// tableMethods:表格的操作对象,可进行获得分页、删除记录等操作 const total = ref(0) // 列表的总页数
// 详细可见:https://doc.iocoder.cn/vue3/crud-schema/ const list = ref([]) // 列表的数据
const { tableObject, tableMethods } = useTable({ const queryParams = reactive({
getListApi: CombinationActivityApi.getCombinationActivityPage, // 分页接口 pageNo: 1,
delListApi: CombinationActivityApi.deleteCombinationActivity // 删除接口 pageSize: 10,
name: null,
status: null
}) })
// 获得表格的各种操作 const queryFormRef = ref() // 搜索的表单
const { getList, setSearchParams } = tableMethods const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await CombinationActivityApi.getCombinationActivityPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 商品图预览 */ /** 重置按钮操作 */
const imagePreview = (imgUrl: string) => { const resetQuery = () => {
createImageViewer({ queryFormRef.value.resetFields()
urlList: [imgUrl] handleQuery()
})
} }
/** 添加/修改操作 */ /** 添加/修改操作 */
...@@ -94,15 +195,40 @@ const openForm = (type: string, id?: number) => { ...@@ -94,15 +195,40 @@ const openForm = (type: string, id?: number) => {
formRef.value.open(type, id) formRef.value.open(type, id)
} }
// TODO 芋艿:这里要改下
/** 关闭按钮操作 */
const handleClose = async (id: number) => {
try {
// 关闭的二次确认
await message.confirm('确认关闭该秒杀活动吗?')
// 发起关闭
await CombinationActivityApi.closeCombinationActivity(id)
message.success('关闭成功')
// 刷新列表
await getList()
} catch {}
}
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = (id: number) => { const handleDelete = async (id: number) => {
tableMethods.delList(id, false) try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await CombinationActivityApi.deleteCombinationActivity(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
const formatCombinationPrice = (products) => {
const combinationPrice = Math.min(...products.map((item) => item.combinationPrice))
return `¥${fenToYuan(combinationPrice)}`
} }
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(async () => {
// 获得活动列表 await getList()
sortTableColumns(allSchemas.tableColumns, 'spuId')
getList()
}) })
</script> </script>
<template>
<Dialog v-model="dialogVisible" title="拼团列表" width="950">
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column align="center" label="编号" prop="id" min-width="50" />
<el-table-column align="center" label="头像" prop="avatar" min-width="80">
<template #default="scope">
<el-avatar :src="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column align="center" label="昵称" prop="nickname" min-width="100" />
<el-table-column align="center" label="开团团长" prop="headId" min-width="100">
<template #default="{ row }: { row: CombinationRecordApi.CombinationRecordVO }">
<el-tag> {{ row.headId === 0 ? '团长' : '团员' }} </el-tag>
</template>
</el-table-column>
<el-table-column
:formatter="dateFormatter"
align="center"
label="参团时间"
prop="createTime"
width="180"
/>
<el-table-column
:formatter="dateFormatter"
align="center"
label="结束时间"
prop="endTime"
width="180"
/>
<el-table-column align="center" label="拼团状态" prop="status" min-width="150">
<template #default="scope">
<dict-tag
:type="DICT_TYPE.PROMOTION_COMBINATION_RECORD_STATUS"
:value="scope.row.status"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
</ContentWrap>
</Dialog>
</template>
<script lang="ts" setup>
import { dateFormatter } from '@/utils/formatTime'
import * as CombinationRecordApi from '@/api/mall/promotion/combination/combinationRecord'
import { DICT_TYPE } from '@/utils/dict'
import { createImageViewer } from '@/components/ImageViewer'
/** 助力列表 */
defineOptions({ name: 'CombinationRecordListDialog' })
const message = useMessage() // 消息弹窗
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
headId: undefined
})
/** 打开弹窗 */
const dialogVisible = ref(false) // 弹窗的是否展示
const open = async (headId: any) => {
dialogVisible.value = true
queryParams.headId = headId
await getList()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await CombinationRecordApi.getCombinationRecordPageByHeadId(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 商品图预览 */
const imagePreview = (imgUrl: string) => {
createImageViewer({
urlList: [imgUrl]
})
}
</script>
<template> <template>
<div></div> <!-- 统计信息展示 -->
<el-row :gutter="12">
<el-col :span="6">
<ContentWrap class="h-[110px] pb-0!">
<div class="flex items-center">
<div
class="h-[50px] w-[50px] flex items-center justify-center"
style="color: rgb(24, 144, 255); background-color: rgba(24, 144, 255, 0.1)"
>
<Icon :size="23" icon="fa:user-times" />
</div>
<div class="ml-[20px]">
<div class="mb-8px text-14px text-gray-400">参与人数(个)</div>
<CountTo
:duration="2600"
:end-val="recordSummary.userCount"
:start-val="0"
class="text-20px"
/>
</div>
</div>
</ContentWrap>
</el-col>
<el-col :span="6">
<ContentWrap class="h-[110px]">
<div class="flex items-center">
<div
class="h-[50px] w-[50px] flex items-center justify-center"
style="color: rgb(162, 119, 255); background-color: rgba(162, 119, 255, 0.1)"
>
<Icon :size="23" icon="fa:user-plus" />
</div>
<div class="ml-[20px]">
<div class="mb-8px text-14px text-gray-400">成团数量(个)</div>
<CountTo
:duration="2600"
:end-val="recordSummary.successCount"
:start-val="0"
class="text-20px"
/>
</div>
</div>
</ContentWrap>
</el-col>
<el-col :span="6">
<ContentWrap class="h-[110px]">
<div class="flex items-center">
<div
class="h-[50px] w-[50px] flex items-center justify-center"
style="color: rgb(162, 119, 255); background-color: rgba(162, 119, 255, 0.1)"
>
<Icon :size="23" icon="fa:user-plus" />
</div>
<div class="ml-[20px]">
<div class="mb-8px text-14px text-gray-400">虚拟成团(个)</div>
<CountTo
:duration="2600"
:end-val="recordSummary.virtualGroupCount"
:start-val="0"
class="text-20px"
/>
</div>
</div>
</ContentWrap>
</el-col>
</el-row>
<!-- 搜索工作栏 -->
<ContentWrap>
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px"
>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
:shortcuts="defaultShortcuts"
class="!w-240px"
end-placeholder="结束日期"
start-placeholder="开始日期"
type="daterange"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="拼团状态" prop="status">
<el-select v-model="queryParams.status" class="!w-240px" clearable placeholder="全部">
<el-option
v-for="(dict, index) in getIntDictOptions(
DICT_TYPE.PROMOTION_COMBINATION_RECORD_STATUS
)"
:key="index"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<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-form-item>
</el-form>
</ContentWrap>
<!-- 分页列表数据展示 -->
<ContentWrap>
<el-table v-loading="loading" :data="pageList">
<el-table-column align="center" label="编号" prop="id" min-width="50" />
<el-table-column align="center" label="头像" prop="avatar" min-width="80">
<template #default="scope">
<el-avatar :src="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column align="center" label="昵称" prop="nickname" min-width="100" />
<el-table-column align="center" label="开团团长" prop="headId" min-width="100">
<template #default="{ row }: { row: CombinationRecordApi.CombinationRecordVO }">
{{
row.headId ? pageList.find((item) => item.id === row.headId)?.nickname : row.nickname
}}
</template>
</el-table-column>
<el-table-column
:formatter="dateFormatter"
align="center"
label="开团时间"
prop="startTime"
width="180"
/>
<el-table-column
align="center"
label="拼团商品"
prop="type"
show-overflow-tooltip
min-width="300"
>
<template #defaul="{ row }">
<el-image
:src="row.picUrl"
class="mr-5px h-30px w-30px align-middle"
@click="imagePreview(row.picUrl)"
/>
<span class="align-middle">{{ row.spuName }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="几人团" prop="userSize" min-width="100" />
<el-table-column align="center" label="参与人数" prop="userCount" min-width="100" />
<el-table-column
:formatter="dateFormatter"
align="center"
label="参团时间"
prop="createTime"
width="180"
/>
<el-table-column
:formatter="dateFormatter"
align="center"
label="结束时间"
prop="endTime"
width="180"
/>
<el-table-column align="center" label="拼团状态" prop="status" min-width="150">
<template #default="scope">
<dict-tag
:type="DICT_TYPE.PROMOTION_COMBINATION_RECORD_STATUS"
:value="scope.row.status"
/>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作">
<template #default="scope">
<el-button
v-hasPermi="['promotion:combination-record:query']"
link
type="primary"
@click="openRecordListDialog(scope.row)"
>
查看拼团
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗 -->
<CombinationRecordListDialog ref="combinationRecordListRef" />
</template> </template>
<script lang="ts" setup>
import CombinationRecordListDialog from './CombinationRecordListDialog.vue'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter, defaultShortcuts } from '@/utils/formatTime'
import { createImageViewer } from '@/components/ImageViewer'
import * as CombinationRecordApi from '@/api/mall/promotion/combination/combinationRecord'
defineOptions({ name: 'PromotionCombinationRecord' })
const queryParams = ref({
status: undefined, // 拼团状态
createTime: undefined, // 创建时间
pageSize: 10,
pageNo: 1
})
const queryFormRef = ref() // 搜索的表单
const combinationRecordListRef = ref() // 查询表单 Ref
const loading = ref(true) // 列表的加载中
const total = ref(0) // 总记录数
const pageList = ref<CombinationRecordApi.CombinationRecordVO[]>([]) // 分页数据
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await CombinationRecordApi.getCombinationRecordPage(queryParams.value)
pageList.value = data.list as CombinationRecordApi.CombinationRecordVO[]
total.value = data.total
} finally {
loading.value = false
}
}
// 拼团统计数据
const recordSummary = ref({
successCount: 0,
userCount: 0,
virtualGroupCount: 0
})
/** 获得拼团记录统计信息 */
const getSummary = async () => {
recordSummary.value = await CombinationRecordApi.getCombinationRecordSummary()
}
const openRecordListDialog = (row: CombinationRecordApi.CombinationRecordVO) => {
combinationRecordListRef.value?.open(row.headId)
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 商品图预览 */
const imagePreview = (imgUrl: string) => {
createImageViewer({
urlList: [imgUrl]
})
}
<script lang="ts" name="CombinationRecord" setup></script> /** 初始化 **/
onMounted(async () => {
await getSummary()
await getList()
})
</script>
...@@ -9,7 +9,7 @@ export const discountFormat = (row: CouponTemplateVO) => { ...@@ -9,7 +9,7 @@ export const discountFormat = (row: CouponTemplateVO) => {
return `¥${floatToFixed2(row.discountPrice)}` return `¥${floatToFixed2(row.discountPrice)}`
} }
if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) { if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
return `${row.discountPrice}%` return `${row.discountPercent}%`
} }
return '未知【' + row.discountType + '】' return '未知【' + row.discountType + '】'
} }
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
@keyup="handleQuery" @keyup="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="创建时间" prop="createTime"> <el-form-item label="领取时间" prop="createTime">
<el-date-picker <el-date-picker
v-model="queryParams.createTime" v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
...@@ -50,12 +50,17 @@ ...@@ -50,12 +50,17 @@
<!-- 列表 --> <!-- 列表 -->
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column label="会员信息" align="center" prop="nickname" /> <el-table-column label="会员昵称" align="center" min-width="100" prop="nickname" />
<!-- TODO 芋艿:以后支持头像,支持跳转 --> <el-table-column label="优惠券名称" align="center" min-width="140" prop="name" />
<el-table-column label="优惠劵" align="center" prop="name" /> <el-table-column label="类型" align="center" prop="discountType">
<el-table-column label="优惠券类型" align="center" prop="discountType"> <template #default="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_PRODUCT_SCOPE" :value="scope.row.productScope" />
</template>
</el-table-column>
<el-table-column label="优惠" min-width="100" prop="discount">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" /> <dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
{{ discountFormat(scope.row) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="领取方式" align="center" prop="takeType"> <el-table-column label="领取方式" align="center" prop="takeType">
...@@ -109,6 +114,7 @@ ...@@ -109,6 +114,7 @@
import { deleteCoupon, getCouponPage } from '@/api/mall/promotion/coupon/coupon' import { deleteCoupon, getCouponPage } from '@/api/mall/promotion/coupon/coupon'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import { discountFormat } from '@/views/mall/promotion/coupon/formatter'
defineOptions({ name: 'PromotionCoupon' }) defineOptions({ name: 'PromotionCoupon' })
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
@keyup="handleQuery" @keyup="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="优惠类型" prop="discountType"> <el-form-item label="优惠类型" prop="discountType">
<el-select <el-select
v-model="queryParams.discountType" v-model="queryParams.discountType"
class="!w-240px" class="!w-240px"
...@@ -71,14 +71,6 @@ ...@@ -71,14 +71,6 @@
> >
<Icon class="mr-5px" icon="ep:plus" /> 新增 <Icon class="mr-5px" icon="ep:plus" /> 新增
</el-button> </el-button>
<el-button
plain
type="success"
@click="$router.push('/promotion/coupon')"
v-hasPermi="['promotion:coupon:query']"
>
<Icon icon="ep:operation" class="mr-5px" />会员优惠劵
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
...@@ -86,17 +78,29 @@ ...@@ -86,17 +78,29 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column label="优惠券名称" align="center" prop="name" /> <el-table-column label="优惠券名称" min-width="140" prop="name" />
<el-table-column label="优惠券类型" align="center" prop="discountType"> <el-table-column label="类型" min-width="80" prop="productScope">
<template #default="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_PRODUCT_SCOPE" :value="scope.row.productScope" />
</template>
</el-table-column>
<el-table-column label="优惠" min-width="100" prop="discount">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" /> <dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
{{ discountFormat(scope.row) }}
</template>
</el-table-column>
<el-table-column label="领取方式" min-width="100" prop="takeType">
<template #default="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE" :value="scope.row.takeType" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="优惠金额 / 折扣" label="使用时间"
align="center" align="center"
prop="discount" prop="validityType"
:formatter="discountFormat" width="185"
:formatter="validityTypeFormat"
/> />
<el-table-column label="发放数量" align="center" prop="totalCount" /> <el-table-column label="发放数量" align="center" prop="totalCount" />
<el-table-column <el-table-column
...@@ -111,13 +115,6 @@ ...@@ -111,13 +115,6 @@
prop="takeLimitCount" prop="takeLimitCount"
:formatter="takeLimitCountFormat" :formatter="takeLimitCountFormat"
/> />
<el-table-column
label="有效期限"
align="center"
prop="validityType"
width="190"
:formatter="validityTypeFormat"
/>
<el-table-column label="状态" align="center" prop="status"> <el-table-column label="状态" align="center" prop="status">
<template #default="scope"> <template #default="scope">
<el-switch <el-switch
......
...@@ -95,42 +95,6 @@ const crudSchemas = reactive<CrudSchema[]>([ ...@@ -95,42 +95,6 @@ const crudSchemas = reactive<CrudSchema[]>([
} }
}, },
{ {
label: '新增订单数',
field: 'orderCount',
isForm: false,
form: {
component: 'InputNumber',
value: 0
},
table: {
width: 120
}
},
{
label: '付款人数',
field: 'userCount',
isForm: false,
form: {
component: 'InputNumber',
value: 0
},
table: {
width: 120
}
},
{
label: '订单实付金额',
field: 'totalPrice',
isForm: false,
form: {
component: 'InputNumber',
value: 0
},
table: {
width: 120
}
},
{
label: '总限购数量', label: '总限购数量',
field: 'totalLimitCount', field: 'totalLimitCount',
form: { form: {
...@@ -164,26 +128,6 @@ const crudSchemas = reactive<CrudSchema[]>([ ...@@ -164,26 +128,6 @@ const crudSchemas = reactive<CrudSchema[]>([
} }
}, },
{ {
label: '秒杀库存',
field: 'stock',
isForm: false,
form: {
component: 'InputNumber',
value: 0
},
table: {
width: 120
}
},
{
label: '秒杀总库存',
field: 'totalStock',
isForm: false,
table: {
width: 120
}
},
{
label: '秒杀活动商品', label: '秒杀活动商品',
field: 'spuId', field: 'spuId',
isTable: true, isTable: true,
...@@ -198,37 +142,6 @@ const crudSchemas = reactive<CrudSchema[]>([ ...@@ -198,37 +142,6 @@ const crudSchemas = reactive<CrudSchema[]>([
} }
}, },
{ {
label: '创建时间',
field: 'createTime',
formatter: dateFormatter,
search: {
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
type: 'daterange',
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
}
},
isForm: false,
table: {
width: 120
}
},
{
label: '状态',
field: 'status',
dictType: DICT_TYPE.COMMON_STATUS,
dictClass: 'number',
isForm: false,
isSearch: true,
form: {
component: 'Radio'
},
table: {
width: 80
}
},
{
label: '备注', label: '备注',
field: 'remark', field: 'remark',
isSearch: false, isSearch: false,
...@@ -245,15 +158,6 @@ const crudSchemas = reactive<CrudSchema[]>([ ...@@ -245,15 +158,6 @@ const crudSchemas = reactive<CrudSchema[]>([
table: { table: {
width: 300 width: 300
} }
},
{
label: '操作',
field: 'action',
isForm: false,
table: {
width: 120,
fixed: 'right'
}
} }
]) ])
export const { allSchemas } = useCrudSchemas(crudSchemas) export const { allSchemas } = useCrudSchemas(crudSchemas)
<template>
<div class="flex flex-col gap-2 bg-[var(--el-bg-color-overlay)] p-6">
<div class="flex items-center justify-between text-gray-500">
<span>{{ title }}</span>
<el-tooltip :content="tooltip" placement="top-start" v-if="tooltip">
<Icon icon="ep:warning" />
</el-tooltip>
</div>
<div class="mb-4 text-3xl">
<CountTo :prefix="prefix" :end-val="value" :decimals="decimals" />
</div>
<div class="flex flex-row gap-1 text-sm">
<span class="text-gray-500">环比</span>
<span :class="toNumber(percent) > 0 ? 'text-red-500' : 'text-green-500'">
{{ Math.abs(toNumber(percent)) }}%
<Icon :icon="toNumber(percent) > 0 ? 'ep:caret-top' : 'ep:caret-bottom'" class="!text-sm" />
</span>
</div>
</div>
</template>
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { toNumber } from 'lodash-es'
/** 交易统计值组件 */
defineOptions({ name: 'TradeStatisticValue' })
defineProps({
tooltip: propTypes.string.def(''),
title: propTypes.string.def(''),
prefix: propTypes.string.def(''),
value: propTypes.number.def(0),
decimals: propTypes.number.def(0),
percent: propTypes.oneOfType([Number, String]).def(0)
})
</script>
<template>
<div class="flex flex-row items-center gap-3 rounded bg-[var(--el-bg-color-overlay)] p-4">
<div
class="h-12 w-12 flex flex-shrink-0 items-center justify-center rounded-1"
:class="`${iconColor} ${iconBgColor}`"
>
<Icon :icon="icon" class="!text-6" />
</div>
<div class="flex flex-col gap-1">
<div class="flex items-center gap-1 text-gray-500">
<span class="text-3.5">{{ title }}</span>
<el-tooltip :content="tooltip" placement="top-start" v-if="tooltip">
<Icon icon="ep:warning" class="item-center flex !text-3" />
</el-tooltip>
</div>
<div class="flex flex-row items-baseline gap-2">
<div class="text-7">
<CountTo :prefix="prefix" :end-val="value" :decimals="decimals" />
</div>
<span
v-if="percent != undefined"
:class="toNumber(percent) > 0 ? 'text-red-500' : 'text-green-500'"
>
<span class="text-sm">{{ Math.abs(toNumber(percent)) }}%</span>
<Icon
:icon="toNumber(percent) > 0 ? 'ep:caret-top' : 'ep:caret-bottom'"
class="ml-0.5 !text-3"
/>
</span>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { toNumber } from 'lodash-es'
/** 交易状况统计值组件 */
defineOptions({ name: 'TradeTrendValue' })
defineProps({
title: propTypes.string.def(''),
tooltip: propTypes.string.def(''),
icon: propTypes.string.def(''),
iconColor: propTypes.string.def(''),
iconBgColor: propTypes.string.def(''),
prefix: propTypes.string.def(''),
value: propTypes.number.def(0),
decimals: propTypes.number.def(0),
percent: propTypes.oneOfType([Number, String]).def(undefined)
})
</script>
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
<el-descriptions-item label="配送方式: "> <el-descriptions-item label="配送方式: ">
<dict-tag :type="DICT_TYPE.TRADE_DELIVERY_TYPE" :value="formData.order.deliveryType" /> <dict-tag :type="DICT_TYPE.TRADE_DELIVERY_TYPE" :value="formData.order.deliveryType" />
</el-descriptions-item> </el-descriptions-item>
<!-- TODO 营销活动待实现 -->
<el-descriptions-item label="订单类型: "> <el-descriptions-item label="订单类型: ">
<dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="formData.order.type" /> <dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="formData.order.type" />
</el-descriptions-item> </el-descriptions-item>
...@@ -29,8 +28,7 @@ ...@@ -29,8 +28,7 @@
<el-descriptions-item label="付款方式: "> <el-descriptions-item label="付款方式: ">
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="formData.order.payChannelCode" /> <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="formData.order.payChannelCode" />
</el-descriptions-item> </el-descriptions-item>
<!-- TODO 芋艿:待实现:跳转会员 --> <el-descriptions-item label="买家: ">{{ formData?.user?.nickname }}</el-descriptions-item>
<!-- <el-descriptions-item label="买家: ">{{ formData.user.nickname }}</el-descriptions-item> -->
</el-descriptions> </el-descriptions>
<!-- 售后信息 --> <!-- 售后信息 -->
...@@ -46,7 +44,7 @@ ...@@ -46,7 +44,7 @@
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="formData.way" /> <dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="formData.way" />
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="退款金额: "> <el-descriptions-item label="退款金额: ">
{{ floatToFixed2(formData.refundPrice) }} {{ fenToYuan(formData.refundPrice) }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="退款原因: ">{{ formData.applyReason }}</el-descriptions-item> <el-descriptions-item label="退款原因: ">{{ formData.applyReason }}</el-descriptions-item>
<el-descriptions-item label="补充描述: "> <el-descriptions-item label="补充描述: ">
...@@ -92,7 +90,7 @@ ...@@ -92,7 +90,7 @@
<el-descriptions-item labelClassName="no-colon"> <el-descriptions-item labelClassName="no-colon">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="15"> <el-col :span="15">
<el-table :data="formData.items" border> <el-table :data="[formData.orderItem]" border>
<el-table-column label="商品" prop="spuName" width="auto"> <el-table-column label="商品" prop="spuName" width="auto">
<template #default="{ row }"> <template #default="{ row }">
{{ row.spuName }} {{ row.spuName }}
...@@ -102,19 +100,11 @@ ...@@ -102,19 +100,11 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="商品原价" prop="price" width="150"> <el-table-column label="商品原价" prop="price" width="150">
<template #default="{ row }">{{ floatToFixed2(row.price) }}</template> <template #default="{ row }">{{ fenToYuan(row.price) }} </template>
</el-table-column> </el-table-column>
<el-table-column label="数量" prop="count" width="100" /> <el-table-column label="数量" prop="count" width="100" />
<el-table-column label="合计" prop="payPrice" width="150"> <el-table-column label="合计" prop="payPrice" width="150">
<template #default="{ row }">{{ floatToFixed2(row.payPrice) }}</template> <template #default="{ row }">{{ fenToYuan(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-column>
</el-table> </el-table>
</el-col> </el-col>
...@@ -122,6 +112,8 @@ ...@@ -122,6 +112,8 @@
</el-row> </el-row>
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
<!-- 操作日志 -->
<el-descriptions title="售后日志"> <el-descriptions title="售后日志">
<el-descriptions-item labelClassName="no-colon"> <el-descriptions-item labelClassName="no-colon">
<el-timeline> <el-timeline>
...@@ -153,7 +145,7 @@ ...@@ -153,7 +145,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as AfterSaleApi from '@/api/mall/trade/afterSale/index' import * as AfterSaleApi from '@/api/mall/trade/afterSale/index'
import { floatToFixed2 } from '@/utils' import { fenToYuan } from '@/utils'
import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict' import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
import UpdateAuditReasonForm from '@/views/mall/trade/afterSale/form/AfterSaleDisagreeForm.vue' import UpdateAuditReasonForm from '@/views/mall/trade/afterSale/form/AfterSaleDisagreeForm.vue'
...@@ -191,7 +183,7 @@ const getUserTypeColor = (type: number) => { ...@@ -191,7 +183,7 @@ const getUserTypeColor = (type: number) => {
/** 获得详情 */ /** 获得详情 */
const getDetail = async () => { const getDetail = async () => {
const id = params.orderId as unknown as number const id = params.id as unknown as number
if (id) { if (id) {
const res = await AfterSaleApi.getAfterSale(id) const res = await AfterSaleApi.getAfterSale(id)
// 没有表单信息则关闭页面返回 // 没有表单信息则关闭页面返回
...@@ -204,44 +196,56 @@ const getDetail = async () => { ...@@ -204,44 +196,56 @@ const getDetail = async () => {
} }
/** 同意售后 */ /** 同意售后 */
const agree = () => { const agree = async () => {
message.confirm('是否同意售后?').then(() => { try {
AfterSaleApi.agree(formData.value.id) // 二次确认
await message.confirm('是否同意售后?')
await AfterSaleApi.agree(formData.value.id)
// 提示成功
message.success(t('common.success')) message.success(t('common.success'))
getDetail() await getDetail()
}) } catch {}
} }
/** 拒绝售后 */ /** 拒绝售后 */
const disagree = () => { const disagree = async () => {
updateAuditReasonFormRef.value?.open(formData.value) updateAuditReasonFormRef.value?.open(formData.value)
} }
/** 确认收货 */ /** 确认收货 */
const receive = () => { const receive = async () => {
message.confirm('是否确认收货?').then(() => { try {
AfterSaleApi.receive(formData.value.id) // 二次确认
await message.confirm('是否确认收货?')
await AfterSaleApi.receive(formData.value.id)
// 提示成功
message.success(t('common.success')) message.success(t('common.success'))
getDetail() await getDetail()
}) } catch {}
} }
/** 拒绝收货 */ /** 拒绝收货 */
const refuse = () => { const refuse = async () => {
message.confirm('是否拒绝收货?').then(() => { try {
AfterSaleApi.refuse(formData.value.id) // 二次确认
await message.confirm('是否拒绝收货?')
await AfterSaleApi.refuse(formData.value.id)
// 提示成功
message.success(t('common.success')) message.success(t('common.success'))
getDetail() await getDetail()
}) } catch {}
} }
/** 确认退款 */ /** 确认退款 */
const refund = () => { const refund = async () => {
message.confirm('是否确认退款?').then(() => { try {
AfterSaleApi.refund(formData.value.id) // 二次确认
await message.confirm('是否确认退款?')
await AfterSaleApi.refund(formData.value.id)
// 提示成功
message.success(t('common.success')) message.success(t('common.success'))
getDetail() await getDetail()
}) } catch {}
} }
/** 图片预览 */ /** 图片预览 */
......
...@@ -135,17 +135,16 @@ ...@@ -135,17 +135,16 @@
</el-table-column> </el-table-column>
<el-table-column align="center" label="订单金额" prop="refundPrice"> <el-table-column align="center" label="订单金额" prop="refundPrice">
<template #default="scope"> <template #default="scope">
<span>{{ floatToFixed2(scope.row.refundPrice) }}</span> <span>{{ fenToYuan(scope.row.refundPrice) }} </span>
</template> </template>
</el-table-column> </el-table-column>
<!-- TODO 芋艿:未来要加个会员链接 -->
<el-table-column align="center" label="买家" prop="user.nickname" /> <el-table-column align="center" label="买家" prop="user.nickname" />
<el-table-column align="center" label="申请时间" prop="createTime" width="180"> <el-table-column align="center" label="申请时间" prop="createTime" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ formatDate(scope.row.createTime) }}</span> <span>{{ formatDate(scope.row.createTime) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="售后状态"> <el-table-column align="center" label="售后状态" width="100">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :value="scope.row.status" />
</template> </template>
...@@ -177,7 +176,7 @@ import { formatDate } from '@/utils/formatTime' ...@@ -177,7 +176,7 @@ import { formatDate } from '@/utils/formatTime'
import { createImageViewer } from '@/components/ImageViewer' import { createImageViewer } from '@/components/ImageViewer'
import { TabsPaneContext } from 'element-plus' import { TabsPaneContext } from 'element-plus'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { floatToFixed2 } from '@/utils' import { fenToYuan } from '@/utils'
defineOptions({ name: 'TradeAfterSale' }) defineOptions({ name: 'TradeAfterSale' })
...@@ -240,7 +239,7 @@ const tabClick = async (tab: TabsPaneContext) => { ...@@ -240,7 +239,7 @@ const tabClick = async (tab: TabsPaneContext) => {
/** 处理退款 */ /** 处理退款 */
const openAfterSaleDetail = (id: number) => { const openAfterSaleDetail = (id: number) => {
push({ name: 'TradeAfterSaleDetail', params: { orderId: id } }) push({ name: 'TradeAfterSaleDetail', params: { id } })
} }
/** 查看订单详情 */ /** 查看订单详情 */
......
...@@ -96,14 +96,14 @@ ...@@ -96,14 +96,14 @@
align="center" align="center"
prop="unfreezeTime" prop="unfreezeTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="170px" width="180px"
/> />
<el-table-column <el-table-column
label="创建时间" label="创建时间"
align="center" align="center"
prop="createTime" prop="createTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="170px" width="180px"
/> />
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
......
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
align="center" align="center"
prop="createTime" prop="createTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="170px" width="180px"
/> />
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
......
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
align="center" align="center"
prop="bindUserTime" prop="bindUserTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="170px" width="180px"
/> />
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 展示上级推广人的信息 -->
<el-descriptions v-if="bindUser" :column="1" border> <el-descriptions v-if="bindUser" :column="1" border>
<el-descriptions-item label="头像"> <el-descriptions-item label="头像">
<el-avatar :src="bindUser.avatar" /> <el-avatar :src="bindUser.avatar" />
...@@ -79,7 +80,7 @@ const submitForm = async () => { ...@@ -79,7 +80,7 @@ const submitForm = async () => {
if (!formRef) return if (!formRef) return
const valid = await formRef.value.validate() const valid = await formRef.value.validate()
if (!valid) return if (!valid) return
// 未查找到合适的上级
if (!bindUser.value) { if (!bindUser.value) {
message.error('请先查询并确认推广人') message.error('请先查询并确认推广人')
return return
...@@ -116,7 +117,6 @@ const handleGetUser = async () => { ...@@ -116,7 +117,6 @@ const handleGetUser = async () => {
message.error('不能绑定自己为推广人') message.error('不能绑定自己为推广人')
return return
} }
formLoading.value = true formLoading.value = true
bindUser.value = await BrokerageUserApi.getBrokerageUser(formData.value.bindUserId) bindUser.value = await BrokerageUserApi.getBrokerageUser(formData.value.bindUserId)
if (!bindUser.value) { if (!bindUser.value) {
......
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
align="center" align="center"
prop="brokerageTime" prop="brokerageTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="170px" width="180px"
/> />
<el-table-column label="上级推广员编号" align="center" prop="bindUserId" width="150px" /> <el-table-column label="上级推广员编号" align="center" prop="bindUserId" width="150px" />
<el-table-column <el-table-column
...@@ -117,7 +117,7 @@ ...@@ -117,7 +117,7 @@
align="center" align="center"
prop="bindUserTime" prop="bindUserTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="170px" width="180px"
/> />
<el-table-column label="操作" align="center" width="150px" fixed="right"> <el-table-column label="操作" align="center" width="150px" fixed="right">
<template #default="scope"> <template #default="scope">
...@@ -204,7 +204,7 @@ const queryParams = reactive({ ...@@ -204,7 +204,7 @@ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
bindUserId: null, bindUserId: null,
brokerageEnabled: null, brokerageEnabled: true,
createTime: [] createTime: []
}) })
const queryFormRef = ref() // 搜索的表单 const queryFormRef = ref() // 搜索的表单
...@@ -281,7 +281,7 @@ const handleClearBindUser = async (row: BrokerageUserApi.BrokerageUserVO) => { ...@@ -281,7 +281,7 @@ const handleClearBindUser = async (row: BrokerageUserApi.BrokerageUserVO) => {
} catch {} } catch {}
} }
/** 推广资格 开通/关闭 */ /** 推广资格开通/关闭 */
const handleBrokerageEnabledChange = async (row: BrokerageUserApi.BrokerageUserVO) => { const handleBrokerageEnabledChange = async (row: BrokerageUserApi.BrokerageUserVO) => {
try { try {
// 二次确认 // 二次确认
......
...@@ -104,8 +104,8 @@ ...@@ -104,8 +104,8 @@
<template #default="scope"> <template #default="scope">
<div v-if="scope.row.type === BrokerageWithdrawTypeEnum.WALLET.type"> 余额 </div> <div v-if="scope.row.type === BrokerageWithdrawTypeEnum.WALLET.type"> 余额 </div>
<div v-else> <div v-else>
{{ getDictLabel(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE, scope.row.type) }}账号: {{ getDictLabel(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE, scope.row.type) }}
{{ scope.row.accountNo }} <span v-if="scope.row.accountNo">账号:{{ scope.row.accountNo }}</span>
</div> </div>
<template v-if="scope.row.type === BrokerageWithdrawTypeEnum.BANK.type"> <template v-if="scope.row.type === BrokerageWithdrawTypeEnum.BANK.type">
<div>真实姓名:{{ scope.row.name }}</div> <div>真实姓名:{{ scope.row.name }}</div>
...@@ -117,14 +117,16 @@ ...@@ -117,14 +117,16 @@
</template> </template>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="收款码" align="left" prop="accountQrCodeUrl" width="70px"> <el-table-column label="收款码" align="left" prop="accountQrCodeUrl" min-width="70px">
<template #default="scope"> <template #default="scope">
<el-image <el-image
v-if="scope.row.accountQrCodeUrl"
:src="scope.row.accountQrCodeUrl" :src="scope.row.accountQrCodeUrl"
class="w-40px h-40px" class="w-40px h-40px"
:preview-src-list="[scope.row.accountQrCodeUrl]" :preview-src-list="[scope.row.accountQrCodeUrl]"
preview-teleported preview-teleported
/> />
<span v-else></span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
...@@ -132,7 +134,7 @@ ...@@ -132,7 +134,7 @@
align="left" align="left"
prop="createTime" prop="createTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="170px" width="180px"
/> />
<el-table-column label="备注" align="left" prop="remark" /> <el-table-column label="备注" align="left" prop="remark" />
<el-table-column label="状态" align="left" prop="status" min-width="120px"> <el-table-column label="状态" align="left" prop="status" min-width="120px">
......
...@@ -10,8 +10,43 @@ ...@@ -10,8 +10,43 @@
<el-form-item label="hideId" v-show="false"> <el-form-item label="hideId" v-show="false">
<el-input v-model="formData.id" /> <el-input v-model="formData.id" />
</el-form-item> </el-form-item>
<el-tabs> <el-tabs>
<!-- 售后 -->
<el-tab-pane label="售后">
<el-form-item label="退款理由" prop="afterSaleRefundReasons">
<el-select
v-model="formData.afterSaleRefundReasons"
allow-create
filterable
multiple
placeholder="请直接输入退款理由"
>
<el-option
v-for="reason in formData.afterSaleRefundReasons"
:key="reason"
:label="reason"
:value="reason"
/>
</el-select>
</el-form-item>
<el-form-item label="退货理由" prop="afterSaleReturnReasons">
<el-select
v-model="formData.afterSaleReturnReasons"
allow-create
filterable
multiple
placeholder="请直接输入退货理由"
>
<el-option
v-for="reason in formData.afterSaleReturnReasons"
:key="reason"
:label="reason"
:value="reason"
/>
</el-select>
</el-form-item>
</el-tab-pane>
<!-- 配送 -->
<el-tab-pane label="配送"> <el-tab-pane label="配送">
<el-form-item label="启用包邮" prop="deliveryExpressFreeEnabled"> <el-form-item label="启用包邮" prop="deliveryExpressFreeEnabled">
<el-switch v-model="formData.deliveryExpressFreeEnabled" style="user-select: none" /> <el-switch v-model="formData.deliveryExpressFreeEnabled" style="user-select: none" />
...@@ -22,10 +57,18 @@ ...@@ -22,10 +57,18 @@
v-model="formData.deliveryExpressFreePrice" v-model="formData.deliveryExpressFreePrice"
placeholder="请输入满额包邮" placeholder="请输入满额包邮"
class="!w-xs" class="!w-xs"
:precision="2"
:min="0"
/> />
<el-text class="w-full" size="small" type="info"> 商城商品满多少金额即可包邮 </el-text> <el-text class="w-full" size="small" type="info">
商城商品满多少金额即可包邮,单位:元
</el-text>
</el-form-item>
<el-form-item label="启用门店自提" prop="deliveryPickUpEnabled">
<el-switch v-model="formData.deliveryPickUpEnabled" style="user-select: none" />
</el-form-item> </el-form-item>
</el-tab-pane> </el-tab-pane>
<!-- 分销 -->
<el-tab-pane label="分销"> <el-tab-pane label="分销">
<el-form-item label="分佣启用" prop="brokerageEnabled"> <el-form-item label="分佣启用" prop="brokerageEnabled">
<el-switch v-model="formData.brokerageEnabled" style="user-select: none" /> <el-switch v-model="formData.brokerageEnabled" style="user-select: none" />
...@@ -59,16 +102,16 @@ ...@@ -59,16 +102,16 @@
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
没有推广人:只要用户没有推广人,随时都可以绑定推广关系 首次绑定:只要用户没有推广人,随时都可以绑定推广关系
</el-text> </el-text>
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
新用户:只有新用户注册时或首次进入系统时才可以绑定推广关系 注册绑定:只有新用户注册时或首次进入系统时才可以绑定推广关系
</el-text> </el-text>
</el-form-item> </el-form-item>
<el-form-item label="分销海报图"> <el-form-item label="分销海报图">
<UploadImgs v-model="formData.brokeragePostUrls" width="75px" height="125px" /> <UploadImgs v-model="formData.brokeragePosterUrls" width="75px" height="125px" />
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
个人中心分销海报图片,建议尺寸600x1000 个人中心分销海报图片,建议尺寸 600x1000
</el-text> </el-text>
</el-form-item> </el-form-item>
<el-form-item label="一级返佣比例" prop="brokerageFirstPercent"> <el-form-item label="一级返佣比例" prop="brokerageFirstPercent">
...@@ -76,6 +119,8 @@ ...@@ -76,6 +119,8 @@
v-model="formData.brokerageFirstPercent" v-model="formData.brokerageFirstPercent"
placeholder="请输入一级返佣比例" placeholder="请输入一级返佣比例"
class="!w-xs" class="!w-xs"
:min="0"
:max="100"
/> />
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
订单交易成功后给推广人返佣的百分比 订单交易成功后给推广人返佣的百分比
...@@ -86,6 +131,8 @@ ...@@ -86,6 +131,8 @@
v-model="formData.brokerageSecondPercent" v-model="formData.brokerageSecondPercent"
placeholder="请输入二级返佣比例" placeholder="请输入二级返佣比例"
class="!w-xs" class="!w-xs"
:min="0"
:max="100"
/> />
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
订单交易成功后给推广人的推荐人返佣的百分比 订单交易成功后给推广人的推荐人返佣的百分比
...@@ -96,6 +143,7 @@ ...@@ -96,6 +143,7 @@
v-model="formData.brokerageFrozenDays" v-model="formData.brokerageFrozenDays"
placeholder="请输入佣金冻结天数" placeholder="请输入佣金冻结天数"
class="!w-xs" class="!w-xs"
:min="0"
/> />
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
防止用户退款,佣金被提现了,所以需要设置佣金冻结时间,单位:天 防止用户退款,佣金被提现了,所以需要设置佣金冻结时间,单位:天
...@@ -106,6 +154,8 @@ ...@@ -106,6 +154,8 @@
v-model="formData.brokerageWithdrawMinPrice" v-model="formData.brokerageWithdrawMinPrice"
placeholder="请输入提现最低金额" placeholder="请输入提现最低金额"
class="!w-xs" class="!w-xs"
:precision="2"
:min="0"
/> />
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
用户提现最低金额限制,单位:元 用户提现最低金额限制,单位:元
...@@ -116,13 +166,16 @@ ...@@ -116,13 +166,16 @@
v-model="formData.brokerageWithdrawFeePercent" v-model="formData.brokerageWithdrawFeePercent"
placeholder="请输入提现手续费" placeholder="请输入提现手续费"
class="!w-xs" class="!w-xs"
:min="0"
:max="100"
/> />
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
提现手续费百分比,范围0-100,0为无提现手续费,例:设置10,即收取10%手续费,提现100元,到账90元,10元手续费 提现手续费百分比,范围 0-100,0 为无提现手续费。例:设置 10,即收取 10% 手续费,提现
10 元,到账 9 元,1 元手续费
</el-text> </el-text>
</el-form-item> </el-form-item>
<el-form-item label="提现方式" prop="brokerageWithdrawType"> <el-form-item label="提现方式" prop="brokerageWithdrawTypes">
<el-checkbox-group v-model="formData.brokerageWithdrawType"> <el-checkbox-group v-model="formData.brokerageWithdrawTypes">
<el-checkbox <el-checkbox
v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE)" v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE)"
:key="dict.value" :key="dict.value"
...@@ -146,7 +199,7 @@ ...@@ -146,7 +199,7 @@
</el-form-item> </el-form-item>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<!-- 保存 -->
<el-form-item> <el-form-item>
<el-button type="primary" @click="submitForm" :loading="formLoading"> 保存 </el-button> <el-button type="primary" @click="submitForm" :loading="formLoading"> 保存 </el-button>
</el-form-item> </el-form-item>
...@@ -156,7 +209,6 @@ ...@@ -156,7 +209,6 @@
<script setup lang="ts"> <script setup lang="ts">
import * as ConfigApi from '@/api/mall/trade/config' import * as ConfigApi from '@/api/mall/trade/config'
import { BrokerageBindModeEnum, BrokerageEnabledConditionEnum } from '@/utils/constants'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
defineOptions({ name: 'TradeConfig' }) defineOptions({ name: 'TradeConfig' })
...@@ -167,19 +219,22 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加 ...@@ -167,19 +219,22 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
const formRef = ref() const formRef = ref()
const formData = ref({ const formData = ref({
id: null, id: null,
deliveryExpressFreeEnabled: true, afterSaleRefundReasons: [],
afterSaleReturnReasons: [],
deliveryExpressFreeEnabled: false,
deliveryExpressFreePrice: 0, deliveryExpressFreePrice: 0,
brokerageEnabled: true, deliveryPickUpEnabled: false,
brokerageEnabledCondition: BrokerageEnabledConditionEnum.ALL.condition, brokerageEnabled: false,
brokerageBindMode: BrokerageBindModeEnum.ANYTIME.mode, brokerageEnabledCondition: undefined,
brokeragePostUrls: [], brokerageBindMode: undefined,
brokeragePosterUrls: [],
brokerageFirstPercent: 0, brokerageFirstPercent: 0,
brokerageSecondPercent: 0, brokerageSecondPercent: 0,
brokerageWithdrawMinPrice: 0, brokerageWithdrawMinPrice: 0,
brokerageWithdrawFeePercent: 0, brokerageWithdrawFeePercent: 0,
brokerageBankNames: [], brokerageBankNames: [],
brokerageFrozenDays: 0, brokerageFrozenDays: 0,
brokerageWithdrawType: [] brokerageWithdrawTypes: []
}) })
const formRules = reactive({ const formRules = reactive({
deliveryExpressFreePrice: [{ required: true, message: '满额包邮不能为空', trigger: 'blur' }], deliveryExpressFreePrice: [{ required: true, message: '满额包邮不能为空', trigger: 'blur' }],
...@@ -193,7 +248,7 @@ const formRules = reactive({ ...@@ -193,7 +248,7 @@ const formRules = reactive({
brokerageWithdrawFeePercent: [{ required: true, message: '提现手续费不能为空', trigger: 'blur' }], brokerageWithdrawFeePercent: [{ required: true, message: '提现手续费不能为空', trigger: 'blur' }],
brokerageBankNames: [{ required: true, message: '提现银行不能为空', trigger: 'blur' }], brokerageBankNames: [{ required: true, message: '提现银行不能为空', trigger: 'blur' }],
brokerageFrozenDays: [{ required: true, message: '佣金冻结时间不能为空', trigger: 'blur' }], brokerageFrozenDays: [{ required: true, message: '佣金冻结时间不能为空', trigger: 'blur' }],
brokerageWithdrawType: [ brokerageWithdrawTypes: [
{ {
required: true, required: true,
message: '提现方式不能为空', message: '提现方式不能为空',
...@@ -211,10 +266,15 @@ const submitForm = async () => { ...@@ -211,10 +266,15 @@ const submitForm = async () => {
// 提交请求 // 提交请求
formLoading.value = true formLoading.value = true
try { try {
const data = formData.value as unknown as ConfigApi.ConfigVO const data = {
data.brokeragePostUrls = formData.value.brokeragePostUrls.map((item: any) => { ...formData.value
} as unknown as ConfigApi.ConfigVO
data.brokeragePosterUrls = formData.value.brokeragePosterUrls.map((item: any) => {
return item?.url ? item.url : item return item?.url ? item.url : item
}) })
// 金额放大
data.deliveryExpressFreePrice = data.deliveryExpressFreePrice * 100
data.brokerageWithdrawMinPrice = data.brokerageWithdrawMinPrice * 100
await ConfigApi.saveTradeConfig(data) await ConfigApi.saveTradeConfig(data)
message.success('保存成功') message.success('保存成功')
} finally { } finally {
...@@ -228,8 +288,11 @@ const getConfig = async () => { ...@@ -228,8 +288,11 @@ const getConfig = async () => {
try { try {
const data = await ConfigApi.getTradeConfig() const data = await ConfigApi.getTradeConfig()
if (data != null) { if (data != null) {
data.brokeragePostUrls = data.brokeragePostUrls.map((url) => ({ url })) data.brokeragePosterUrls = data.brokeragePosterUrls.map((url) => ({ url }))
formData.value = data formData.value = data
// 金额缩小
formData.value.deliveryExpressFreePrice = data.deliveryExpressFreePrice / 100
formData.value.brokerageWithdrawMinPrice = data.brokerageWithdrawMinPrice / 100
} }
} finally { } finally {
formLoading.value = false formLoading.value = false
......
...@@ -7,17 +7,17 @@ ...@@ -7,17 +7,17 @@
label-width="120px" label-width="120px"
v-loading="formLoading" v-loading="formLoading"
> >
<el-form-item label="快递公司编码" prop="code"> <el-form-item label="公司编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入快递编码" /> <el-input v-model="formData.code" placeholder="请输入快递编码" />
</el-form-item> </el-form-item>
<el-form-item label="快递公司名称" prop="name"> <el-form-item label="公司名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入快递名称" /> <el-input v-model="formData.name" placeholder="请输入快递名称" />
</el-form-item> </el-form-item>
<el-form-item label="快递公司 logo" prop="logo"> <el-form-item label="公司 logo" prop="logo">
<UploadImg v-model="formData.logo" :limit="1" :is-show-tip="false" /> <UploadImg v-model="formData.logo" :limit="1" :is-show-tip="false" />
<div style="font-size: 10px" class="pl-10px">推荐 180x180 图片分辨率</div> <div style="font-size: 10px" class="pl-10px">推荐 180x180 图片分辨率</div>
</el-form-item> </el-form-item>
<el-form-item label="分类排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input-number v-model="formData.sort" controls-position="right" :min="0" /> <el-input-number v-model="formData.sort" controls-position="right" :min="0" />
</el-form-item> </el-form-item>
<el-form-item label="开启状态" prop="status"> <el-form-item label="开启状态" prop="status">
......
...@@ -53,11 +53,11 @@ ...@@ -53,11 +53,11 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column label="快递公司编号" prop="code" /> <el-table-column label="公司编码" prop="code" />
<el-table-column label="快递公司名称" prop="name" /> <el-table-column label="公司名称" prop="name" />
<el-table-column label="快递公司 logo " prop="logo"> <el-table-column label="公司 logo " prop="logo">
<template #default="scope"> <template #default="scope">
<img v-if="scope.row.logo" :src="scope.row.logo" alt="快递公司logo" class="h-100px" /> <img v-if="scope.row.logo" :src="scope.row.logo" alt="公司logo" class="h-40px" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="排序" align="center" prop="sort" /> <el-table-column label="排序" align="center" prop="sort" />
......
...@@ -51,14 +51,14 @@ ...@@ -51,14 +51,14 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column label="编号" prop="id" /> <el-table-column label="编号" min-width="60" prop="id" />
<el-table-column label="模板名称" prop="name" /> <el-table-column label="模板名称" min-width="100" prop="name" />
<el-table-column label="计费方式" prop="chargeMode" align="center"> <el-table-column label="计费方式" prop="chargeMode" min-width="100" align="center">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.EXPRESS_CHARGE_MODE" :value="scope.row.chargeMode" /> <dict-tag :type="DICT_TYPE.EXPRESS_CHARGE_MODE" :value="scope.row.chargeMode" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="排序" prop="sort" /> <el-table-column label="排序" min-width="100" prop="sort" />
<el-table-column <el-table-column
label="创建时间" label="创建时间"
align="center" align="center"
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="门店所在地区" prop="areaId"> <el-form-item label="门店所在地区" prop="areaId">
<el-cascader v-model="formData.areaId" :options="areaList" :props="areaTreeProps" /> <el-cascader v-model="formData.areaId" :options="areaList" :props="defaultProps" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
...@@ -99,7 +99,7 @@ ...@@ -99,7 +99,7 @@
</el-col> </el-col>
</el-row> </el-row>
<el-form-item label="获取经纬度"> <el-form-item label="获取经纬度">
<el-button type="primary" @click="mapDialogVisible.value = true">获取</el-button> <el-button type="primary" @click="mapDialogVisible = true">获取</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
...@@ -121,8 +121,9 @@ ...@@ -121,8 +121,9 @@
import * as DeliveryPickUpStoreApi from '@/api/mall/trade/delivery/pickUpStore' import * as DeliveryPickUpStoreApi from '@/api/mall/trade/delivery/pickUpStore'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { CommonStatusEnum } from '@/utils/constants' import { CommonStatusEnum } from '@/utils/constants'
import { defaultProps } from '@/utils/tree'
import { getAreaTree } from '@/api/system/area' import { getAreaTree } from '@/api/system/area'
import * as ConfigApi from '@/api/infra/config' import * as ConfigApi from '@/api/mall/trade/config'
const { t } = useI18n() // 国际化 const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗 const message = useMessage() // 消息弹窗
...@@ -161,12 +162,6 @@ const formRules = reactive({ ...@@ -161,12 +162,6 @@ const formRules = reactive({
status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }] status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }]
}) })
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
const areaTreeProps = {
children: 'children',
label: 'name',
value: 'id',
emitPath: false
}
const areaList = ref() // 区域树 const areaList = ref() // 区域树
const tencentLbsUrl = ref('') // 腾讯位置服务 url const tencentLbsUrl = ref('') // 腾讯位置服务 url
...@@ -244,16 +239,8 @@ const selectAddress = function (loc: any): void { ...@@ -244,16 +239,8 @@ const selectAddress = function (loc: any): void {
mapDialogVisible.value = false mapDialogVisible.value = false
} }
/** 初始化数据 */ /** 初始化腾讯地图 */
const initData = async () => { const initTencentLbsMap = async () => {
formLoading.value = true
try {
const data = await getAreaTree()
areaList.value = data
} finally {
formLoading.value = false
}
// TODO @jason:要不创建一个 initTencentLbsMap
window.selectAddress = selectAddress window.selectAddress = selectAddress
window.addEventListener( window.addEventListener(
'message', 'message',
...@@ -267,17 +254,16 @@ const initData = async () => { ...@@ -267,17 +254,16 @@ const initData = async () => {
}, },
false false
) )
const data = await ConfigApi.getConfigKey('tencent.lbs.key') const data = await ConfigApi.getTradeConfig()
let key = '' const key = data.tencentLbsKey
if (data && data.length > 0) {
key = data
}
tencentLbsUrl.value = `https://apis.map.qq.com/tools/locpicker?type=1&key=${key}&referer=myapp` tencentLbsUrl.value = `https://apis.map.qq.com/tools/locpicker?type=1&key=${key}&referer=myapp`
} }
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(async () => {
initData() areaList.value = await getAreaTree()
// 加载地图
await initTencentLbsMap()
}) })
</script> </script>
<style lang="scss"> <style lang="scss">
......
...@@ -65,16 +65,21 @@ ...@@ -65,16 +65,21 @@
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<el-table-column label="编号" prop="id" /> <el-table-column label="编号" min-width="80" prop="id" />
<el-table-column label="门店 logo" prop="logo"> <el-table-column label="门店 logo" min-width="100" prop="logo">
<template #default="scope"> <template #default="scope">
<img v-if="scope.row.logo" :src="scope.row.logo" alt="门店 logo" class="h-100px" /> <img v-if="scope.row.logo" :src="scope.row.logo" alt="门店 logo" class="h-50px" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="门店名称" prop="name" /> <el-table-column label="门店名称" min-width="150" prop="name" />
<el-table-column label="门店手机" prop="phone" /> <el-table-column label="门店手机" min-width="100" prop="phone" />
<el-table-column align="center" label="门店详细地址" prop="detailAddress" /> <el-table-column label="地址" min-width="100" prop="detailAddress" />
<el-table-column align="center" label="开启状态" prop="status"> <el-table-column label="营业时间" min-width="180">
<template #default="scope">
{{ scope.row.openingTime }} ~ {{ scope.row.closingTime }}
</template>
</el-table-column>
<el-table-column align="center" label="开启状态" min-width="100" prop="status">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template> </template>
......
...@@ -54,7 +54,7 @@ const open = async (row: TradeOrderApi.OrderVO) => { ...@@ -54,7 +54,7 @@ const open = async (row: TradeOrderApi.OrderVO) => {
resetForm() resetForm()
// 设置数据 // 设置数据
copyValueToTarget(formData.value, row) copyValueToTarget(formData.value, row)
if (row.logisticsId === null || row.logisticsId === 0) { if (row.logisticsId === 0) {
expressType.value = 'none' expressType.value = 'none'
} }
dialogVisible.value = true dialogVisible.value = true
...@@ -73,7 +73,7 @@ const submitForm = async () => { ...@@ -73,7 +73,7 @@ const submitForm = async () => {
data.logisticsId = 0 data.logisticsId = 0
data.logisticsNo = '' data.logisticsNo = ''
} }
await TradeOrderApi.delivery(data) await TradeOrderApi.deliveryOrder(data)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
dialogVisible.value = false dialogVisible.value = false
// 发送操作成功的事件 // 发送操作成功的事件
......
...@@ -69,7 +69,7 @@ const submitForm = async () => { ...@@ -69,7 +69,7 @@ const submitForm = async () => {
formLoading.value = true formLoading.value = true
try { try {
const data = unref(formData) const data = unref(formData)
await TradeOrderApi.updateAddress(data) await TradeOrderApi.updateOrderAddress(data)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
dialogVisible.value = false dialogVisible.value = false
// 发送操作成功的事件 // 发送操作成功的事件
......
...@@ -69,7 +69,7 @@ const submitForm = async () => { ...@@ -69,7 +69,7 @@ const submitForm = async () => {
data.adjustPrice = convertToInteger(data.adjustPrice) data.adjustPrice = convertToInteger(data.adjustPrice)
delete data.payPrice delete data.payPrice
delete data.newPayPrice delete data.newPayPrice
await TradeOrderApi.updatePrice(data) await TradeOrderApi.updateOrderPrice(data)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
dialogVisible.value = false dialogVisible.value = false
// 发送操作成功的事件 // 发送操作成功的事件
......
...@@ -49,7 +49,7 @@ const submitForm = async () => { ...@@ -49,7 +49,7 @@ const submitForm = async () => {
formLoading.value = true formLoading.value = true
try { try {
const data = unref(formData) const data = unref(formData)
await TradeOrderApi.updateRemark(data) await TradeOrderApi.updateOrderRemark(data)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
dialogVisible.value = false dialogVisible.value = false
// 发送操作成功的事件 // 发送操作成功的事件
......
...@@ -74,7 +74,11 @@ ...@@ -74,7 +74,11 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="queryParams.deliveryType === 1" label="快递公司"> <el-form-item
v-if="queryParams.deliveryType === DeliveryTypeEnum.EXPRESS.type"
label="快递公司"
prop="logisticsId"
>
<el-select v-model="queryParams.logisticsId" class="!w-280px" clearable placeholder="全部"> <el-select v-model="queryParams.logisticsId" class="!w-280px" clearable placeholder="全部">
<el-option <el-option
v-for="item in deliveryExpressList" v-for="item in deliveryExpressList"
...@@ -84,7 +88,11 @@ ...@@ -84,7 +88,11 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="queryParams.deliveryType === 2" label="自提门店"> <el-form-item
v-if="queryParams.deliveryType === DeliveryTypeEnum.PICK_UP.type"
label="自提门店"
prop="pickUpStoreId"
>
<el-select <el-select
v-model="queryParams.pickUpStoreId" v-model="queryParams.pickUpStoreId"
class="!w-280px" class="!w-280px"
...@@ -100,26 +108,37 @@ ...@@ -100,26 +108,37 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- TODO puhui 聚合搜索等售后结束后实现--> <el-form-item
<!-- TODO puhui999:尽量不要用 .k 这样的参数,完整拼写,有完整的业务含义 --> v-if="queryParams.deliveryType === DeliveryTypeEnum.PICK_UP.type"
label="核销码"
prop="pickUpVerifyCode"
>
<el-input
v-model="queryParams.pickUpVerifyCode"
class="!w-280px"
clearable
placeholder="请输入自提核销码"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="聚合搜索"> <el-form-item label="聚合搜索">
<el-input <el-input
v-show="true" v-show="true"
v-model="queryParams[queryType.k]" v-model="queryParams[queryType.queryParam]"
class="!w-280px" class="!w-280px"
clearable clearable
placeholder="请输入" placeholder="请输入"
> >
<template #prepend> <template #prepend>
<el-select <el-select
v-model="queryType.k" v-model="queryType.queryParam"
class="!w-110px" class="!w-110px"
clearable clearable
placeholder="全部" placeholder="全部"
@change="inputChangeSelect" @change="inputChangeSelect"
> >
<el-option <el-option
v-for="dict in searchList" v-for="dict in dynamicSearchList"
:key="dict.value" :key="dict.value"
:label="dict.label" :label="dict.label"
:value="dict.value" :value="dict.value"
...@@ -234,7 +253,10 @@ ...@@ -234,7 +253,10 @@
<el-table-column label="买家/收货人" min-width="160"> <el-table-column label="买家/收货人" min-width="160">
<template #default> <template #default>
<!-- 快递发货 --> <!-- 快递发货 -->
<div v-if="scope.row.deliveryType === 1" class="flex flex-col"> <div
v-if="scope.row.deliveryType === DeliveryTypeEnum.EXPRESS.type"
class="flex flex-col"
>
<span>买家:{{ scope.row.user.nickname }}</span> <span>买家:{{ scope.row.user.nickname }}</span>
<span> <span>
收货人:{{ scope.row.receiverName }} {{ scope.row.receiverMobile }} 收货人:{{ scope.row.receiverName }} {{ scope.row.receiverMobile }}
...@@ -242,7 +264,10 @@ ...@@ -242,7 +264,10 @@
</span> </span>
</div> </div>
<!-- 自提 --> <!-- 自提 -->
<div v-if="scope.row.deliveryType === 2" class="flex flex-col"> <div
v-if="scope.row.deliveryType === DeliveryTypeEnum.PICK_UP.type"
class="flex flex-col"
>
<span> <span>
门店名称: 门店名称:
{{ pickUpStoreList.find((p) => p.id === scope.row.pickUpStoreId)?.name }} {{ pickUpStoreList.find((p) => p.id === scope.row.pickUpStoreId)?.name }}
...@@ -273,7 +298,7 @@ ...@@ -273,7 +298,7 @@
<el-table-column align="center" fixed="right" label="操作" width="160"> <el-table-column align="center" fixed="right" label="操作" width="160">
<template #default> <template #default>
<!-- TODO 权限后续补齐 --> <!-- TODO 权限后续补齐 -->
<div class="flex justify-center items-center"> <div class="flex items-center justify-center">
<el-button link type="primary" @click="openDetail(scope.row.id)"> <el-button link type="primary" @click="openDetail(scope.row.id)">
<Icon icon="ep:notification" /> <Icon icon="ep:notification" />
详情 详情
...@@ -287,7 +312,10 @@ ...@@ -287,7 +312,10 @@
<el-dropdown-menu> <el-dropdown-menu>
<!-- 如果是【快递】,并且【未发货】,则展示【发货】按钮 --> <!-- 如果是【快递】,并且【未发货】,则展示【发货】按钮 -->
<el-dropdown-item <el-dropdown-item
v-if="scope.row.deliveryType === 1 && scope.row.status === 10" v-if="
scope.row.deliveryType === DeliveryTypeEnum.EXPRESS.type &&
scope.row.status === TradeOrderStatusEnum.UNDELIVERED.status
"
command="delivery" command="delivery"
> >
<Icon icon="ep:takeaway-box" /> <Icon icon="ep:takeaway-box" />
...@@ -332,6 +360,7 @@ import { formatDate } from '@/utils/formatTime' ...@@ -332,6 +360,7 @@ import { formatDate } from '@/utils/formatTime'
import { floatToFixed2 } from '@/utils' import { floatToFixed2 } from '@/utils'
import { createImageViewer } from '@/components/ImageViewer' import { createImageViewer } from '@/components/ImageViewer'
import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express' import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express'
import { DeliveryTypeEnum, TradeOrderStatusEnum } from '@/utils/constants'
defineOptions({ name: 'TradeOrder' }) defineOptions({ name: 'TradeOrder' })
...@@ -352,13 +381,13 @@ const queryParams = ref({ ...@@ -352,13 +381,13 @@ const queryParams = ref({
type: null, // 订单类型 type: null, // 订单类型
deliveryType: null, // 配送方式 deliveryType: null, // 配送方式
logisticsId: null, // 快递公司 logisticsId: null, // 快递公司
pickUpStoreId: null // 自提门店 pickUpStoreId: null, // 自提门店
pickUpVerifyCode: null // 自提核销码
}) })
const queryType = reactive({ k: '' }) // 订单搜索类型 k const queryType = reactive({ queryParam: '' }) // 订单搜索类型 queryParam
// 订单聚合搜索 select 类型配置 // 订单聚合搜索 select 类型配置(动态搜索)
// TODO @puhui999:dynamicSearchList,动态搜索;其它相关的变量和方法,都可以朝着这个变量靠哈;这样更容易理解; const dynamicSearchList = ref([
const searchList = ref([
{ value: 'no', label: '订单号' }, { value: 'no', label: '订单号' },
{ value: 'userId', label: '用户UID' }, { value: 'userId', label: '用户UID' },
{ value: 'userNickname', label: '用户昵称' }, { value: 'userNickname', label: '用户昵称' },
...@@ -369,7 +398,7 @@ const searchList = ref([ ...@@ -369,7 +398,7 @@ const searchList = ref([
* @param val * @param val
*/ */
const inputChangeSelect = (val: string) => { const inputChangeSelect = (val: string) => {
searchList.value dynamicSearchList.value
.filter((item) => item.value !== val) .filter((item) => item.value !== val)
?.forEach((item1) => { ?.forEach((item1) => {
// 清除集合搜索无用属性 // 清除集合搜索无用属性
...@@ -443,6 +472,7 @@ const handleQuery = async () => { ...@@ -443,6 +472,7 @@ const handleQuery = async () => {
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields() queryFormRef.value?.resetFields()
queryParams.value = { queryParams.value = {
pickUpVerifyCode: null, // 自提核销码
pageNo: 1, // 页数 pageNo: 1, // 页数
pageSize: 10, // 每页显示数量 pageSize: 10, // 每页显示数量
status: null, // 订单状态 status: null, // 订单状态
...@@ -466,7 +496,7 @@ const imagePreview = (imgUrl: string) => { ...@@ -466,7 +496,7 @@ const imagePreview = (imgUrl: string) => {
/** 查看订单详情 */ /** 查看订单详情 */
const openDetail = (id: number) => { const openDetail = (id: number) => {
push({ name: 'TradeOrderDetail', params: { orderId: id } }) push({ name: 'TradeOrderDetail', params: { id } })
} }
/** 操作分发 */ /** 操作分发 */
......
...@@ -13,13 +13,13 @@ ...@@ -13,13 +13,13 @@
<el-tabs> <el-tabs>
<el-tab-pane label="积分"> <el-tab-pane label="积分">
<el-form-item label="积分抵扣" prop="tradeDeductEnable"> <el-form-item label="积分抵扣" prop="pointTradeDeductEnable">
<el-switch v-model="formData.tradeDeductEnable" style="user-select: none" /> <el-switch v-model="formData.pointTradeDeductEnable" style="user-select: none" />
<el-text class="w-full" size="small" type="info">下单积分是否抵用订单金额</el-text> <el-text class="w-full" size="small" type="info">下单积分是否抵用订单金额</el-text>
</el-form-item> </el-form-item>
<el-form-item label="积分抵扣" prop="tradeDeductUnitPrice"> <el-form-item label="积分抵扣" prop="pointTradeDeductUnitPrice">
<el-input-number <el-input-number
v-model="computedTradeDeductUnitPrice" v-model="computedPointTradeDeductUnitPrice"
placeholder="请输入积分抵扣金额" placeholder="请输入积分抵扣金额"
:precision="2" :precision="2"
/> />
...@@ -27,18 +27,18 @@ ...@@ -27,18 +27,18 @@
积分抵用比例(1 积分抵多少金额),单位:元 积分抵用比例(1 积分抵多少金额),单位:元
</el-text> </el-text>
</el-form-item> </el-form-item>
<el-form-item label="积分抵扣最大值" prop="tradeDeductMaxPrice"> <el-form-item label="积分抵扣最大值" prop="pointTradeDeductMaxPrice">
<el-input-number <el-input-number
v-model="formData.tradeDeductMaxPrice" v-model="formData.pointTradeDeductMaxPrice"
placeholder="请输入积分抵扣最大值" placeholder="请输入积分抵扣最大值"
/> />
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
单次下单积分使用上限,0 不限制 单次下单积分使用上限,0 不限制
</el-text> </el-text>
</el-form-item> </el-form-item>
<el-form-item label="1 元赠送多少分" prop="tradeGivePoint"> <el-form-item label="1 元赠送多少分" prop="pointTradeGivePoint">
<el-input-number <el-input-number
v-model="formData.tradeGivePoint" v-model="formData.pointTradeGivePoint"
placeholder="请输入 1 元赠送多少积分" placeholder="请输入 1 元赠送多少积分"
/> />
<el-text class="w-full" size="small" type="info"> <el-text class="w-full" size="small" type="info">
...@@ -55,9 +55,9 @@ ...@@ -55,9 +55,9 @@
</ContentWrap> </ContentWrap>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as ConfigApi from '@/api/member/point/config' import * as ConfigApi from '@/api/member/config'
defineOptions({ name: 'MemberPointConfig' }) defineOptions({ name: 'MemberConfig' })
const { t } = useI18n() // 国际化 const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗 const message = useMessage() // 消息弹窗
...@@ -66,17 +66,17 @@ const dialogVisible = ref(false) // 弹窗的是否展示 ...@@ -66,17 +66,17 @@ const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formData = ref({ const formData = ref({
id: undefined, id: undefined,
tradeDeductEnable: true, pointTradeDeductEnable: true,
tradeDeductUnitPrice: 0, pointTradeDeductUnitPrice: 0,
tradeDeductMaxPrice: 0, pointTradeDeductMaxPrice: 0,
tradeGivePoint: 0 pointTradeGivePoint: 0
}) })
// 创建一个计算属性,用于将 tradeDeductUnitPrice 显示为带两位小数的形式 // 创建一个计算属性,用于将 pointTradeDeductUnitPrice 显示为带两位小数的形式
const computedTradeDeductUnitPrice = computed({ const computedPointTradeDeductUnitPrice = computed({
get: () => (formData.value.tradeDeductUnitPrice / 100).toFixed(2), get: () => (formData.value.pointTradeDeductUnitPrice / 100).toFixed(2),
set: (newValue) => { set: (newValue: number) => {
formData.value.tradeDeductUnitPrice = Math.round(newValue * 100) formData.value.pointTradeDeductUnitPrice = Math.round(newValue * 100)
} }
}) })
......
...@@ -13,8 +13,11 @@ ...@@ -13,8 +13,11 @@
只允许设置 1-7,默认签到 7 天为一个周期 只允许设置 1-7,默认签到 7 天为一个周期
</el-text> </el-text>
</el-form-item> </el-form-item>
<el-form-item label="签到分数" prop="point"> <el-form-item label="奖励积分" prop="point">
<el-input-number v-model="formData.point" :precision="0" /> <el-input-number v-model="formData.point" :min="0" :precision="0" />
</el-form-item>
<el-form-item label="奖励经验" prop="experience">
<el-input-number v-model="formData.experience" :min="0" :precision="0" />
</el-form-item> </el-form-item>
<el-form-item label="开启状态" prop="status"> <el-form-item label="开启状态" prop="status">
<el-radio-group v-model="formData.status"> <el-radio-group v-model="formData.status">
...@@ -46,12 +49,30 @@ const dialogVisible = ref(false) // 弹窗的是否展示 ...@@ -46,12 +49,30 @@ const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题 const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({ const formData = ref<SignInConfigApi.SignInConfigVO>({} as SignInConfigApi.SignInConfigVO)
id: undefined, // 奖励校验规则
day: undefined, const awardValidator = (rule: any, _value: any, callback: any) => {
point: undefined if (!formData.value.point && !formData.value.experience) {
callback(new Error('奖励积分与奖励经验至少配置一个'))
return
}
// 清除另一个字段的错误提示
const otherAwardField = rule?.field === 'point' ? 'experience' : 'point'
formRef.value.validateField(otherAwardField, () => null)
callback()
}
const formRules = reactive({
day: [{ required: true, message: '签到天数不能空', trigger: 'blur' }],
point: [
{ required: true, message: '奖励积分不能空', trigger: 'blur' },
{ validator: awardValidator, trigger: 'blur' }
],
experience: [
{ required: true, message: '奖励经验不能空', trigger: 'blur' },
{ validator: awardValidator, trigger: 'blur' }
]
}) })
const formRules = reactive({})
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
/** 打开弹窗 */ /** 打开弹窗 */
...@@ -82,14 +103,11 @@ const submitForm = async () => { ...@@ -82,14 +103,11 @@ const submitForm = async () => {
// 提交请求 // 提交请求
formLoading.value = true formLoading.value = true
try { try {
const data = formData.value as unknown as SignInConfigApi.SignInConfigVO
if (formType.value === 'create') { if (formType.value === 'create') {
//默认新创建的自动启动 await SignInConfigApi.createSignInConfig(formData.value)
data.enable = true
await SignInConfigApi.createSignInConfig(data)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
} else { } else {
await SignInConfigApi.updateSignInConfig(data) await SignInConfigApi.updateSignInConfig(formData.value)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
} }
dialogVisible.value = false dialogVisible.value = false
...@@ -105,7 +123,8 @@ const resetForm = () => { ...@@ -105,7 +123,8 @@ const resetForm = () => {
formData.value = { formData.value = {
id: undefined, id: undefined,
day: undefined, day: undefined,
point: undefined, point: 0,
experience: 0,
status: CommonStatusEnum.ENABLE status: CommonStatusEnum.ENABLE
} }
formRef.value?.resetFields() formRef.value?.resetFields()
......
...@@ -20,7 +20,8 @@ ...@@ -20,7 +20,8 @@
prop="day" prop="day"
:formatter="(_, __, cellValue) => ['第', cellValue, '天'].join(' ')" :formatter="(_, __, cellValue) => ['第', cellValue, '天'].join(' ')"
/> />
<el-table-column label="获得积分" align="center" prop="point" /> <el-table-column label="奖励积分" align="center" prop="point" />
<el-table-column label="奖励经验" align="center" prop="experience" />
<el-table-column label="状态" align="center" prop="status"> <el-table-column label="状态" align="center" prop="status">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
......
<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" class="!w-240px" disabled />
</el-form-item>
<el-form-item label="用户昵称" prop="nickname">
<el-input v-model="formData.nickname" class="!w-240px" disabled />
</el-form-item>
<el-form-item label="变动前积分" prop="point">
<el-input-number v-model="formData.point" class="!w-240px" disabled />
</el-form-item>
<el-form-item label="变动类型" prop="changeType">
<el-radio-group v-model="formData.changeType">
<el-radio :label="1">增加</el-radio>
<el-radio :label="-1">减少</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="变动积分" prop="changePoint">
<el-input-number v-model="formData.changePoint" class="!w-240px" :min="0" :precision="0" />
</el-form-item>
<el-form-item label="变动后积分">
<el-input-number v-model="pointResult" class="!w-240px" disabled />
</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'
/** 修改用户积分表单 */
defineOptions({ name: 'UpdatePointForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formData = ref({
id: undefined,
nickname: undefined,
point: 0,
changePoint: 0,
changeType: 1
})
const formRules = reactive({
changePoint: [{ 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)
formData.value.changeType = 1 // 默认增加积分
formData.value.changePoint = 0 // 变动积分默认0
} 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
if (formData.value.changePoint < 1) {
message.error('变动积分不能小于 1')
return
}
if (pointResult.value < 0) {
message.error('变动后的积分不能小于 0')
return
}
// 提交请求
formLoading.value = true
try {
await UserApi.updateUserPoint({
id: formData.value.id,
point: formData.value.changePoint * formData.value.changeType
})
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()
}
/** 变动后的积分 */
const pointResult = computed(
() => formData.value.point + formData.value.changePoint * formData.value.changeType
)
</script>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'GrowthList'
})
</script>
<!-- TODO @梦:可以读取 member_experience_log 表 -->
<template>
<div>成长值列表</div>
</template>
<style scoped lang="scss"></style>
...@@ -24,31 +24,57 @@ ...@@ -24,31 +24,57 @@
</template> </template>
{{ user.totalPoint || 0 }} {{ user.totalPoint || 0 }}
</el-descriptions-item> </el-descriptions-item>
<!-- TODO @疯狂:从 wallet 读取下对应字段 -->
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<descriptions-item-label label=" 当前余额 " icon="svg-icon:member_balance" /> <descriptions-item-label label=" 当前余额 " icon="svg-icon:member_balance" />
</template> </template>
{{ 0 }} {{ fenToYuan(wallet.balance || 0) }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<descriptions-item-label label=" 支出金额 " icon="svg-icon:member_expenditure_balance" /> <descriptions-item-label label=" 支出金额 " icon="svg-icon:member_expenditure_balance" />
</template> </template>
{{ 0 }} {{ fenToYuan(wallet.totalExpense || 0) }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<descriptions-item-label label=" 充值金额 " icon="svg-icon:member_recharge_balance" /> <descriptions-item-label label=" 充值金额 " icon="svg-icon:member_recharge_balance" />
</template> </template>
{{ 0 }} {{ fenToYuan(wallet.totalRecharge || 0) }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { DescriptionsItemLabel } from '@/components/Descriptions' import { DescriptionsItemLabel } from '@/components/Descriptions'
import * as UserApi from '@/api/member/user' import * as UserApi from '@/api/member/user'
const { user } = defineProps<{ user: UserApi.UserVO }>() import * as WalletApi from '@/api/pay/wallet'
import { UserTypeEnum } from '@/utils/constants'
import { fenToYuan } from '@/utils'
const props = defineProps<{ user: UserApi.UserVO }>() // 用户信息
const WALLET_INIT_DATA = {
balance: 0,
totalExpense: 0,
totalRecharge: 0
} as WalletApi.WalletVO // 钱包初始化数据
const wallet = ref<WalletApi.WalletVO>(WALLET_INIT_DATA) // 钱包信息
/** 查询用户钱包信息 */
const getUserWallet = async () => {
if (!props.user.id) {
wallet.value = WALLET_INIT_DATA
return
}
const params = { userId: props.user.id, userType: UserTypeEnum.MEMBER }
wallet.value = (await WalletApi.getWallet(params)) || WALLET_INIT_DATA
}
/** 监听用户编号变化 */
watch(
() => props.user.id,
() => getUserWallet(),
{ immediate: true }
)
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.cell-item { .cell-item {
......
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="85px"
>
<el-form-item label="用户类型" prop="level">
<el-radio-group v-model="queryParams.level" @change="handleQuery">
<el-radio-button checked>全部</el-radio-button>
<el-radio-button label="1">一级推广人</el-radio-button>
<el-radio-button label="2">二级推广人</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="绑定时间" prop="bindUserTime">
<el-date-picker
v-model="queryParams.bindUserTime"
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-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="80px" />
<el-table-column label="头像" align="center" prop="avatar" width="70px">
<template #default="scope">
<el-avatar :src="scope.row.avatar" />
</template>
</el-table-column>
<el-table-column label="昵称" align="center" prop="nickname" min-width="80px" />
<el-table-column label="等级" align="center" prop="level" min-width="80px">
<template #default="scope">
<el-tag v-if="scope.row.bindUserId === bindUserId">一级</el-tag>
<el-tag v-else>二级</el-tag>
</template>
</el-table-column>
<el-table-column
label="绑定时间"
align="center"
prop="bindUserTime"
:formatter="dateFormatter"
width="170px"
/>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import * as BrokerageUserApi from '@/api/mall/trade/brokerage/user'
/** 推广人列表 */
defineOptions({ name: 'UserBrokerageList' })
const { bindUserId }: { bindUserId: number } = defineProps({
bindUserId: {
type: Number,
required: true
}
}) //用户编号
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
bindUserId: null,
level: '',
bindUserTime: []
})
const queryFormRef = ref() // 搜索的表单
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
queryParams.bindUserId = bindUserId
const data = await BrokerageUserApi.getBrokerageUserPage(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()
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment