Merge branch 'master' of http://47.109.37.87:3000/by2025/admin-vben5
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
import {PageParam} from "#/api/types";
|
||||
import {PageResult} from "#/api/common";
|
||||
|
||||
export namespace MallBannerApi {
|
||||
/** Banner 信息 */
|
||||
export interface Banner {
|
||||
id: number;
|
||||
title: string;
|
||||
picUrl: string;
|
||||
status: number;
|
||||
url: string;
|
||||
position: number;
|
||||
sort: number;
|
||||
memo: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询Banner管理列表 */
|
||||
export function getBannerPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallBannerApi.Banner>>(
|
||||
'/promotion/banner/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询Banner管理详情 */
|
||||
export function getBanner(id: number) {
|
||||
return requestClient.get<MallBannerApi.Banner>(
|
||||
`/promotion/banner/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增Banner管理 */
|
||||
export function createBanner(data: MallBannerApi.Banner) {
|
||||
return requestClient.post('/promotion/banner/create', data);
|
||||
}
|
||||
|
||||
/** 修改Banner管理 */
|
||||
export function updateBanner(data: MallBannerApi.Banner) {
|
||||
return requestClient.put('/promotion/banner/update', data);
|
||||
}
|
||||
|
||||
/** 删除Banner管理 */
|
||||
export function deleteBanner(id: number) {
|
||||
return requestClient.delete(`/promotion/banner/delete?id=${id}`);
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallBrandApi {
|
||||
/** 商品品牌 */
|
||||
export interface Brand {
|
||||
/** 品牌编号 */
|
||||
id?: number;
|
||||
/** 品牌名称 */
|
||||
name: string;
|
||||
/** 品牌图片 */
|
||||
picUrl: string;
|
||||
/** 品牌排序 */
|
||||
sort?: number;
|
||||
/** 品牌描述 */
|
||||
description?: string;
|
||||
/** 开启状态 */
|
||||
status: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建商品品牌 */
|
||||
export function createBrand(data: MallBrandApi.Brand) {
|
||||
return requestClient.post('/product/brand/create', data);
|
||||
}
|
||||
|
||||
/** 更新商品品牌 */
|
||||
export function updateBrand(data: MallBrandApi.Brand) {
|
||||
return requestClient.put('/product/brand/update', data);
|
||||
}
|
||||
|
||||
/** 删除商品品牌 */
|
||||
export function deleteBrand(id: number) {
|
||||
return requestClient.delete(`/product/brand/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得商品品牌 */
|
||||
export function getBrand(id: number) {
|
||||
return requestClient.get<MallBrandApi.Brand>(`/product/brand/get?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得商品品牌列表 */
|
||||
export function getBrandPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallBrandApi.Brand>>(
|
||||
'/product/brand/page',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得商品品牌精简信息列表 */
|
||||
export function getSimpleBrandList() {
|
||||
return requestClient.get<MallBrandApi.Brand[]>(
|
||||
'/product/brand/list-all-simple',
|
||||
);
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallCategoryApi {
|
||||
/** 产品分类 */
|
||||
export interface Category {
|
||||
/** 分类编号 */
|
||||
id?: number;
|
||||
/** 父分类编号 */
|
||||
parentId?: number;
|
||||
/** 分类名称 */
|
||||
name: string;
|
||||
/** 移动端分类图 */
|
||||
picUrl: string;
|
||||
/** 分类排序 */
|
||||
sort: number;
|
||||
/** 开启状态 */
|
||||
status: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建商品分类 */
|
||||
export function createCategory(data: MallCategoryApi.Category) {
|
||||
return requestClient.post('/product/category/create', data);
|
||||
}
|
||||
|
||||
/** 更新商品分类 */
|
||||
export function updateCategory(data: MallCategoryApi.Category) {
|
||||
return requestClient.put('/product/category/update', data);
|
||||
}
|
||||
|
||||
/** 删除商品分类 */
|
||||
export function deleteCategory(id: number) {
|
||||
return requestClient.delete(`/product/category/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得商品分类 */
|
||||
export function getCategory(id: number) {
|
||||
return requestClient.get<MallCategoryApi.Category>(
|
||||
`/product/category/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得商品分类列表 */
|
||||
export function getCategoryList(params: any) {
|
||||
return requestClient.get<MallCategoryApi.Category[]>(
|
||||
'/product/category/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallCommentApi {
|
||||
export interface Property {
|
||||
propertyId: number;
|
||||
propertyName: string;
|
||||
valueId: number;
|
||||
valueName: string;
|
||||
}
|
||||
/** 商品评论 */
|
||||
export interface Comment {
|
||||
id: number;
|
||||
userId: number;
|
||||
userNickname: string;
|
||||
userAvatar: string;
|
||||
anonymous: boolean;
|
||||
orderId: number;
|
||||
orderItemId: number;
|
||||
spuId: number;
|
||||
spuName: string;
|
||||
skuId: number;
|
||||
visible: boolean;
|
||||
scores: number;
|
||||
descriptionScores: number;
|
||||
benefitScores: number;
|
||||
content: string;
|
||||
picUrls: string[];
|
||||
replyStatus: boolean;
|
||||
replyUserId: number;
|
||||
replyContent: string;
|
||||
replyTime: Date;
|
||||
createTime: Date;
|
||||
skuProperties: Property[];
|
||||
}
|
||||
|
||||
/** 评论可见性更新 */
|
||||
export interface CommentVisibleUpdate {
|
||||
id: number;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
/** 评论回复 */
|
||||
export interface CommentReply {
|
||||
id: number;
|
||||
replyContent: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询商品评论列表 */
|
||||
export function getCommentPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallCommentApi.Comment>>(
|
||||
'/product/comment/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询商品评论详情 */
|
||||
export function getComment(id: number) {
|
||||
return requestClient.get<MallCommentApi.Comment>(
|
||||
`/product/comment/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 添加自评 */
|
||||
export function createComment(data: MallCommentApi.Comment) {
|
||||
return requestClient.post('/product/comment/create', data);
|
||||
}
|
||||
|
||||
/** 显示 / 隐藏评论 */
|
||||
export function updateCommentVisible(
|
||||
data: MallCommentApi.CommentVisibleUpdate,
|
||||
) {
|
||||
return requestClient.put('/product/comment/update-visible', data);
|
||||
}
|
||||
|
||||
/** 商家回复 */
|
||||
export function replyComment(data: MallCommentApi.CommentReply) {
|
||||
return requestClient.put('/product/comment/reply', data);
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallFavoriteApi {
|
||||
/** 商品收藏 */
|
||||
export interface Favorite {
|
||||
/** 收藏编号 */
|
||||
id?: number;
|
||||
/** 用户编号 */
|
||||
userId?: string;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId?: null | number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获得商品收藏列表 */
|
||||
export function getFavoritePage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallFavoriteApi.Favorite>>(
|
||||
'/product/favorite/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallHistoryApi {
|
||||
/** 商品浏览记录 */
|
||||
export interface BrowseHistory {
|
||||
/** 记录编号 */
|
||||
id?: number;
|
||||
/** 用户编号 */
|
||||
userId?: number;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId?: number;
|
||||
/** 浏览时间 */
|
||||
createTime?: Date;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得商品浏览记录分页
|
||||
*
|
||||
* @param params 请求参数
|
||||
*/
|
||||
export function getBrowseHistoryPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallHistoryApi.BrowseHistory>>(
|
||||
'/product/browse-history/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
@@ -1,111 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallPropertyApi {
|
||||
/** 商品属性 */
|
||||
export interface Property {
|
||||
/** 属性编号 */
|
||||
id?: number;
|
||||
/** 名称 */
|
||||
name: string;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/** 属性值 */
|
||||
export interface PropertyValue {
|
||||
/** 属性值编号 */
|
||||
id?: number;
|
||||
/** 属性项的编号 */
|
||||
propertyId?: number;
|
||||
/** 名称 */
|
||||
name: string;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/** 属性值查询参数 */
|
||||
export interface PropertyValueQuery extends PageParam {
|
||||
propertyId?: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建属性项 */
|
||||
export function createProperty(data: MallPropertyApi.Property) {
|
||||
return requestClient.post('/product/property/create', data);
|
||||
}
|
||||
|
||||
/** 更新属性项 */
|
||||
export function updateProperty(data: MallPropertyApi.Property) {
|
||||
return requestClient.put('/product/property/update', data);
|
||||
}
|
||||
|
||||
/** 删除属性项 */
|
||||
export function deleteProperty(id: number) {
|
||||
return requestClient.delete(`/product/property/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得属性项 */
|
||||
export function getProperty(id: number) {
|
||||
return requestClient.get<MallPropertyApi.Property>(
|
||||
`/product/property/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得属性项分页 */
|
||||
export function getPropertyPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallPropertyApi.Property>>(
|
||||
'/product/property/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得属性项精简列表 */
|
||||
export function getPropertySimpleList() {
|
||||
return requestClient.get<MallPropertyApi.Property[]>(
|
||||
'/product/property/simple-list',
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得属性值分页 */
|
||||
export function getPropertyValuePage(
|
||||
params: MallPropertyApi.PropertyValueQuery,
|
||||
) {
|
||||
return requestClient.get<PageResult<MallPropertyApi.PropertyValue>>(
|
||||
'/product/property/value/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得属性值 */
|
||||
export function getPropertyValue(id: number) {
|
||||
return requestClient.get<MallPropertyApi.PropertyValue>(
|
||||
`/product/property/value/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 创建属性值 */
|
||||
export function createPropertyValue(data: MallPropertyApi.PropertyValue) {
|
||||
return requestClient.post('/product/property/value/create', data);
|
||||
}
|
||||
|
||||
/** 更新属性值 */
|
||||
export function updatePropertyValue(data: MallPropertyApi.PropertyValue) {
|
||||
return requestClient.put('/product/property/value/update', data);
|
||||
}
|
||||
|
||||
/** 删除属性值 */
|
||||
export function deletePropertyValue(id: number) {
|
||||
return requestClient.delete(`/product/property/value/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得属性值精简列表 */
|
||||
export function getPropertyValueSimpleList(propertyId: number) {
|
||||
return requestClient.get<MallPropertyApi.PropertyValue[]>(
|
||||
'/product/property/value/simple-list',
|
||||
{
|
||||
params: { propertyId },
|
||||
},
|
||||
);
|
||||
}
|
@@ -1,177 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallSpuApi {
|
||||
/** 商品属性 */
|
||||
export interface Property {
|
||||
/** 属性编号 */
|
||||
propertyId?: number;
|
||||
/** 属性名称 */
|
||||
propertyName?: string;
|
||||
/** 属性值编号 */
|
||||
valueId?: number;
|
||||
/** 属性值名称 */
|
||||
valueName?: string;
|
||||
}
|
||||
|
||||
/** 商品 SKU */
|
||||
export interface Sku {
|
||||
/** 商品 SKU 编号 */
|
||||
id?: number;
|
||||
/** 商品 SKU 名称 */
|
||||
name?: string;
|
||||
/** SPU 编号 */
|
||||
spuId?: number;
|
||||
/** 属性数组 */
|
||||
properties?: Property[];
|
||||
/** 商品价格 */
|
||||
price?: number | string;
|
||||
/** 市场价 */
|
||||
marketPrice?: number | string;
|
||||
/** 成本价 */
|
||||
costPrice?: number | string;
|
||||
/** 商品条码 */
|
||||
barCode?: string;
|
||||
/** 图片地址 */
|
||||
picUrl?: string;
|
||||
/** 库存 */
|
||||
stock?: number;
|
||||
/** 商品重量,单位:kg 千克 */
|
||||
weight?: number;
|
||||
/** 商品体积,单位:m^3 平米 */
|
||||
volume?: number;
|
||||
/** 一级分销的佣金 */
|
||||
firstBrokeragePrice?: number | string;
|
||||
/** 二级分销的佣金 */
|
||||
secondBrokeragePrice?: number | string;
|
||||
/** 商品销量 */
|
||||
salesCount?: number;
|
||||
}
|
||||
|
||||
/** 优惠券模板 */
|
||||
export interface GiveCouponTemplate {
|
||||
/** 优惠券编号 */
|
||||
id?: number;
|
||||
/** 优惠券名称 */
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/** 商品 SPU */
|
||||
export interface Spu {
|
||||
/** 商品编号 */
|
||||
id?: number;
|
||||
/** 商品名称 */
|
||||
name?: string;
|
||||
/** 商品分类 */
|
||||
categoryId?: number;
|
||||
/** 关键字 */
|
||||
keyword?: string;
|
||||
/** 单位 */
|
||||
unit?: number | undefined;
|
||||
/** 商品封面图 */
|
||||
picUrl?: string;
|
||||
/** 商品轮播图 */
|
||||
sliderPicUrls?: string[];
|
||||
/** 商品简介 */
|
||||
introduction?: string;
|
||||
/** 配送方式 */
|
||||
deliveryTypes?: number[];
|
||||
/** 运费模版 */
|
||||
deliveryTemplateId?: number | undefined;
|
||||
/** 商品品牌编号 */
|
||||
brandId?: number;
|
||||
/** 商品规格 */
|
||||
specType?: boolean;
|
||||
/** 分销类型 */
|
||||
subCommissionType?: boolean;
|
||||
/** sku数组 */
|
||||
skus?: Sku[];
|
||||
/** 商品详情 */
|
||||
description?: string;
|
||||
/** 商品排序 */
|
||||
sort?: number;
|
||||
/** 赠送积分 */
|
||||
giveIntegral?: number;
|
||||
/** 虚拟销量 */
|
||||
virtualSalesCount?: number;
|
||||
/** 商品价格 */
|
||||
price?: number;
|
||||
/** 商品拼团价格 */
|
||||
combinationPrice?: number;
|
||||
/** 商品秒杀价格 */
|
||||
seckillPrice?: number;
|
||||
/** 商品销量 */
|
||||
salesCount?: number;
|
||||
/** 市场价 */
|
||||
marketPrice?: number;
|
||||
/** 成本价 */
|
||||
costPrice?: number;
|
||||
/** 商品库存 */
|
||||
stock?: number;
|
||||
/** 商品创建时间 */
|
||||
createTime?: Date;
|
||||
/** 商品状态 */
|
||||
status?: number;
|
||||
}
|
||||
|
||||
/** 商品状态更新 */
|
||||
export interface StatusUpdate {
|
||||
/** 商品编号 */
|
||||
id: number;
|
||||
/** 商品状态 */
|
||||
status: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获得商品 SPU 列表 */
|
||||
export function getSpuPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallSpuApi.Spu>>('/product/spu/page', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/** 获得商品 SPU 列表 tabsCount */
|
||||
export function getTabsCount() {
|
||||
return requestClient.get<Record<string, number>>('/product/spu/get-count');
|
||||
}
|
||||
|
||||
/** 创建商品 SPU */
|
||||
export function createSpu(data: MallSpuApi.Spu) {
|
||||
return requestClient.post('/product/spu/create', data);
|
||||
}
|
||||
|
||||
/** 更新商品 SPU */
|
||||
export function updateSpu(data: MallSpuApi.Spu) {
|
||||
return requestClient.put('/product/spu/update', data);
|
||||
}
|
||||
|
||||
/** 更新商品 SPU 状态 */
|
||||
export function updateStatus(data: MallSpuApi.StatusUpdate) {
|
||||
return requestClient.put('/product/spu/update-status', data);
|
||||
}
|
||||
|
||||
/** 获得商品 SPU */
|
||||
export function getSpu(id: number) {
|
||||
return requestClient.get<MallSpuApi.Spu>(`/product/spu/get-detail?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得商品 SPU 详情列表 */
|
||||
export function getSpuDetailList(ids: number[]) {
|
||||
return requestClient.get<MallSpuApi.Spu[]>(`/product/spu/list?spuIds=${ids}`);
|
||||
}
|
||||
|
||||
/** 删除商品 SPU */
|
||||
export function deleteSpu(id: number) {
|
||||
return requestClient.delete(`/product/spu/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 导出商品 SPU Excel */
|
||||
export function exportSpu(params: PageParam) {
|
||||
return requestClient.download('/product/spu/export-excel', { params });
|
||||
}
|
||||
|
||||
/** 获得商品 SPU 精简列表 */
|
||||
export function getSpuSimpleList() {
|
||||
return requestClient.get<MallSpuApi.Spu[]>('/product/spu/list-all-simple');
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallArticleApi {
|
||||
/** 文章管理 */
|
||||
export interface Article {
|
||||
/** 文章编号 */
|
||||
id: number;
|
||||
/** 分类编号 */
|
||||
categoryId: number;
|
||||
/** 文章标题 */
|
||||
title: string;
|
||||
/** 作者 */
|
||||
author: string;
|
||||
/** 封面图 */
|
||||
picUrl: string;
|
||||
/** 文章简介 */
|
||||
introduction: string;
|
||||
/** 浏览数量 */
|
||||
browseCount: string;
|
||||
/** 排序 */
|
||||
sort: number;
|
||||
/** 状态 */
|
||||
status: number;
|
||||
/** 商品编号 */
|
||||
spuId: number;
|
||||
/** 是否热门 */
|
||||
recommendHot: boolean;
|
||||
/** 是否轮播图 */
|
||||
recommendBanner: boolean;
|
||||
/** 文章内容 */
|
||||
content: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询文章管理列表 */
|
||||
export function getArticlePage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallArticleApi.Article>>(
|
||||
'/promotion/article/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询文章管理详情 */
|
||||
export function getArticle(id: number) {
|
||||
return requestClient.get<MallArticleApi.Article>(
|
||||
`/promotion/article/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增文章管理 */
|
||||
export function createArticle(data: MallArticleApi.Article) {
|
||||
return requestClient.post('/promotion/article/create', data);
|
||||
}
|
||||
|
||||
/** 修改文章管理 */
|
||||
export function updateArticle(data: MallArticleApi.Article) {
|
||||
return requestClient.put('/promotion/article/update', data);
|
||||
}
|
||||
|
||||
/** 删除文章管理 */
|
||||
export function deleteArticle(id: number) {
|
||||
return requestClient.delete(`/promotion/article/delete?id=${id}`);
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallArticleCategoryApi {
|
||||
/** 文章分类 */
|
||||
export interface ArticleCategory {
|
||||
/** 分类编号 */
|
||||
id: number;
|
||||
/** 分类名称 */
|
||||
name: string;
|
||||
/** 分类图片 */
|
||||
picUrl: string;
|
||||
/** 状态 */
|
||||
status: number;
|
||||
/** 排序 */
|
||||
sort: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询文章分类列表 */
|
||||
export function getArticleCategoryPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallArticleCategoryApi.ArticleCategory>>(
|
||||
'/promotion/article-category/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询文章分类精简信息列表 */
|
||||
export function getSimpleArticleCategoryList() {
|
||||
return requestClient.get<MallArticleCategoryApi.ArticleCategory[]>(
|
||||
'/promotion/article-category/list-all-simple',
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询文章分类详情 */
|
||||
export function getArticleCategory(id: number) {
|
||||
return requestClient.get<MallArticleCategoryApi.ArticleCategory>(
|
||||
`/promotion/article-category/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增文章分类 */
|
||||
export function createArticleCategory(
|
||||
data: MallArticleCategoryApi.ArticleCategory,
|
||||
) {
|
||||
return requestClient.post('/promotion/article-category/create', data);
|
||||
}
|
||||
|
||||
/** 修改文章分类 */
|
||||
export function updateArticleCategory(
|
||||
data: MallArticleCategoryApi.ArticleCategory,
|
||||
) {
|
||||
return requestClient.put('/promotion/article-category/update', data);
|
||||
}
|
||||
|
||||
/** 删除文章分类 */
|
||||
export function deleteArticleCategory(id: number) {
|
||||
return requestClient.delete(`/promotion/article-category/delete?id=${id}`);
|
||||
}
|
@@ -1,106 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import type { MallSpuApi } from '#/api/mall/product/spu';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallBargainActivityApi {
|
||||
/** 砍价活动 */
|
||||
export interface BargainActivity {
|
||||
/** 活动编号 */
|
||||
id?: number;
|
||||
/** 活动名称 */
|
||||
name?: string;
|
||||
/** 开始时间 */
|
||||
startTime?: Date;
|
||||
/** 结束时间 */
|
||||
endTime?: Date;
|
||||
/** 状态 */
|
||||
status?: number;
|
||||
/** 达到该人数,才能砍到低价 */
|
||||
helpMaxCount?: number;
|
||||
/** 最大帮砍次数 */
|
||||
bargainCount?: number;
|
||||
/** 最大购买次数 */
|
||||
totalLimitCount?: number;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId: number;
|
||||
/** 商品 SKU 编号 */
|
||||
skuId: number;
|
||||
/** 砍价起始价格,单位分 */
|
||||
bargainFirstPrice: number;
|
||||
/** 砍价底价 */
|
||||
bargainMinPrice: number;
|
||||
/** 活动库存 */
|
||||
stock: number;
|
||||
/** 用户每次砍价的最小金额,单位:分 */
|
||||
randomMinPrice?: number;
|
||||
/** 用户每次砍价的最大金额,单位:分 */
|
||||
randomMaxPrice?: number;
|
||||
}
|
||||
|
||||
/** 砍价活动所需属性。选择的商品和属性的时候使用方便使用活动的通用封装 */
|
||||
export interface BargainProduct {
|
||||
/** 商品 SPU 编号 */
|
||||
spuId: number;
|
||||
/** 商品 SKU 编号 */
|
||||
skuId: number;
|
||||
/** 砍价起始价格,单位分 */
|
||||
bargainFirstPrice: number;
|
||||
/** 砍价底价 */
|
||||
bargainMinPrice: number;
|
||||
/** 活动库存 */
|
||||
stock: number;
|
||||
}
|
||||
|
||||
/** 扩展 SKU 配置 */
|
||||
export type SkuExtension = {
|
||||
/** 砍价活动配置 */
|
||||
productConfig: BargainProduct;
|
||||
} & MallSpuApi.Sku;
|
||||
|
||||
/** 扩展 SPU 配置 */
|
||||
export interface SpuExtension extends MallSpuApi.Spu {
|
||||
/** SKU 列表 */
|
||||
skus: SkuExtension[];
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询砍价活动列表 */
|
||||
export function getBargainActivityPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallBargainActivityApi.BargainActivity>>(
|
||||
'/promotion/bargain-activity/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询砍价活动详情 */
|
||||
export function getBargainActivity(id: number) {
|
||||
return requestClient.get<MallBargainActivityApi.BargainActivity>(
|
||||
`/promotion/bargain-activity/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增砍价活动 */
|
||||
export function createBargainActivity(
|
||||
data: MallBargainActivityApi.BargainActivity,
|
||||
) {
|
||||
return requestClient.post('/promotion/bargain-activity/create', data);
|
||||
}
|
||||
|
||||
/** 修改砍价活动 */
|
||||
export function updateBargainActivity(
|
||||
data: MallBargainActivityApi.BargainActivity,
|
||||
) {
|
||||
return requestClient.put('/promotion/bargain-activity/update', data);
|
||||
}
|
||||
|
||||
/** 关闭砍价活动 */
|
||||
export function closeBargainActivity(id: number) {
|
||||
return requestClient.put(`/promotion/bargain-activity/close?id=${id}`);
|
||||
}
|
||||
|
||||
/** 删除砍价活动 */
|
||||
export function deleteBargainActivity(id: number) {
|
||||
return requestClient.delete(`/promotion/bargain-activity/delete?id=${id}`);
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallBargainHelpApi {
|
||||
/** 砍价记录 */
|
||||
export interface BargainHelp {
|
||||
/** 记录编号 */
|
||||
id: number;
|
||||
/** 砍价记录编号 */
|
||||
record: number;
|
||||
/** 用户编号 */
|
||||
userId: number;
|
||||
/** 砍掉金额 */
|
||||
reducePrice: number;
|
||||
/** 结束时间 */
|
||||
endTime: Date;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询砍价记录列表 */
|
||||
export function getBargainHelpPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallBargainHelpApi.BargainHelp>>(
|
||||
'/promotion/bargain-help/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallBargainRecordApi {
|
||||
/** 砍价记录 */
|
||||
export interface BargainRecord {
|
||||
/** 记录编号 */
|
||||
id: number;
|
||||
/** 活动编号 */
|
||||
activityId: number;
|
||||
/** 用户编号 */
|
||||
userId: number;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId: number;
|
||||
/** 商品 SKU 编号 */
|
||||
skuId: number;
|
||||
/** 砍价起始价格 */
|
||||
bargainFirstPrice: number;
|
||||
/** 砍价价格 */
|
||||
bargainPrice: number;
|
||||
/** 状态 */
|
||||
status: number;
|
||||
/** 订单编号 */
|
||||
orderId: number;
|
||||
/** 结束时间 */
|
||||
endTime: Date;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询砍价记录列表 */
|
||||
export function getBargainRecordPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallBargainRecordApi.BargainRecord>>(
|
||||
'/promotion/bargain-record/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
@@ -1,111 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import type { MallSpuApi } from '#/api/mall/product/spu';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallCombinationActivityApi {
|
||||
/** 拼团活动所需属性 */
|
||||
export interface CombinationProduct {
|
||||
/** 商品 SPU 编号 */
|
||||
spuId: number;
|
||||
/** 商品 SKU 编号 */
|
||||
skuId: number;
|
||||
/** 拼团价格 */
|
||||
combinationPrice: number;
|
||||
}
|
||||
/** 拼团活动 */
|
||||
export interface CombinationActivity {
|
||||
/** 活动编号 */
|
||||
id?: number;
|
||||
/** 活动名称 */
|
||||
name?: string;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId?: number;
|
||||
/** 总限购数量 */
|
||||
totalLimitCount?: number;
|
||||
/** 单次限购数量 */
|
||||
singleLimitCount?: number;
|
||||
/** 开始时间 */
|
||||
startTime?: Date;
|
||||
/** 结束时间 */
|
||||
endTime?: Date;
|
||||
/** 用户数量 */
|
||||
userSize?: number;
|
||||
/** 总数量 */
|
||||
totalCount?: number;
|
||||
/** 成功数量 */
|
||||
successCount?: number;
|
||||
/** 订单用户数量 */
|
||||
orderUserCount?: number;
|
||||
/** 虚拟成团 */
|
||||
virtualGroup?: number;
|
||||
/** 状态 */
|
||||
status?: number;
|
||||
/** 限制时长 */
|
||||
limitDuration?: number;
|
||||
/** 拼团价格 */
|
||||
combinationPrice?: number;
|
||||
/** 商品列表 */
|
||||
products: CombinationProduct[];
|
||||
}
|
||||
|
||||
/** 扩展 SKU 配置 */
|
||||
export type SkuExtension = {
|
||||
/** 拼团活动配置 */
|
||||
productConfig: CombinationProduct;
|
||||
} & MallSpuApi.Sku;
|
||||
|
||||
/** 扩展 SPU 配置 */
|
||||
export interface SpuExtension extends MallSpuApi.Spu {
|
||||
/** SKU 列表 */
|
||||
skus: SkuExtension[];
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询拼团活动列表 */
|
||||
export function getCombinationActivityPage(params: PageParam) {
|
||||
return requestClient.get<
|
||||
PageResult<MallCombinationActivityApi.CombinationActivity>
|
||||
>('/promotion/combination-activity/page', { params });
|
||||
}
|
||||
|
||||
/** 查询拼团活动详情 */
|
||||
export function getCombinationActivity(id: number) {
|
||||
return requestClient.get<MallCombinationActivityApi.CombinationActivity>(
|
||||
`/promotion/combination-activity/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得拼团活动列表,基于活动编号数组 */
|
||||
export function getCombinationActivityListByIds(ids: number[]) {
|
||||
return requestClient.get<MallCombinationActivityApi.CombinationActivity[]>(
|
||||
`/promotion/combination-activity/list-by-ids?ids=${ids}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增拼团活动 */
|
||||
export function createCombinationActivity(
|
||||
data: MallCombinationActivityApi.CombinationActivity,
|
||||
) {
|
||||
return requestClient.post('/promotion/combination-activity/create', data);
|
||||
}
|
||||
|
||||
/** 修改拼团活动 */
|
||||
export function updateCombinationActivity(
|
||||
data: MallCombinationActivityApi.CombinationActivity,
|
||||
) {
|
||||
return requestClient.put('/promotion/combination-activity/update', data);
|
||||
}
|
||||
|
||||
/** 关闭拼团活动 */
|
||||
export function closeCombinationActivity(id: number) {
|
||||
return requestClient.put(`/promotion/combination-activity/close?id=${id}`);
|
||||
}
|
||||
|
||||
/** 删除拼团活动 */
|
||||
export function deleteCombinationActivity(id: number) {
|
||||
return requestClient.delete(
|
||||
`/promotion/combination-activity/delete?id=${id}`,
|
||||
);
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallCombinationRecordApi {
|
||||
/** 拼团记录 */
|
||||
export interface CombinationRecord {
|
||||
/** 拼团记录编号 */
|
||||
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 interface RecordSummary {
|
||||
/** 待成团数量 */
|
||||
pendingCount: number;
|
||||
/** 已成团数量 */
|
||||
successCount: number;
|
||||
/** 已失败数量 */
|
||||
failCount: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询拼团记录列表 */
|
||||
export function getCombinationRecordPage(params: PageParam) {
|
||||
return requestClient.get<
|
||||
PageResult<MallCombinationRecordApi.CombinationRecord>
|
||||
>('/promotion/combination-record/page', { params });
|
||||
}
|
||||
|
||||
/** 获得拼团记录的概要信息 */
|
||||
export function getCombinationRecordSummary() {
|
||||
return requestClient.get<MallCombinationRecordApi.RecordSummary>(
|
||||
'/promotion/combination-record/get-summary',
|
||||
);
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallCouponApi {
|
||||
/** 优惠券 */
|
||||
export interface Coupon {
|
||||
/** 优惠券编号 */
|
||||
id: number;
|
||||
/** 优惠券名称 */
|
||||
name: string;
|
||||
/** 优惠券状态 */
|
||||
status: number;
|
||||
/** 优惠券类型 */
|
||||
type: number;
|
||||
/** 优惠券金额 */
|
||||
price: number;
|
||||
/** 使用门槛 */
|
||||
usePrice: number;
|
||||
/** 商品范围 */
|
||||
productScope: number;
|
||||
/** 商品编号数组 */
|
||||
productSpuIds: number[];
|
||||
/** 有效期类型 */
|
||||
validityType: number;
|
||||
/** 固定日期-生效开始时间 */
|
||||
validStartTime: Date;
|
||||
/** 固定日期-生效结束时间 */
|
||||
validEndTime: Date;
|
||||
/** 领取日期-开始天数 */
|
||||
fixedStartTerm: number;
|
||||
/** 领取日期-结束天数 */
|
||||
fixedEndTerm: number;
|
||||
/** 每人限领个数 */
|
||||
takeLimitCount: number;
|
||||
/** 是否设置满多少金额可用 */
|
||||
usePriceEnabled: boolean;
|
||||
/** 商品分类编号数组 */
|
||||
productCategoryIds: number[];
|
||||
}
|
||||
|
||||
/** 发送优惠券 */
|
||||
export interface SendCoupon {
|
||||
/** 优惠券编号 */
|
||||
couponId: number;
|
||||
/** 用户编号数组 */
|
||||
userIds: number[];
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除优惠劵 */
|
||||
export function deleteCoupon(id: number) {
|
||||
return requestClient.delete(`/promotion/coupon/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得优惠劵分页 */
|
||||
export function getCouponPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallCouponApi.Coupon>>(
|
||||
'/promotion/coupon/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 发送优惠券 */
|
||||
export function sendCoupon(data: MallCouponApi.SendCoupon) {
|
||||
return requestClient.post('/promotion/coupon/send', data);
|
||||
}
|
@@ -1,112 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallCouponTemplateApi {
|
||||
/** 优惠券模板 */
|
||||
export interface CouponTemplate {
|
||||
/** 模板编号 */
|
||||
id: number;
|
||||
/** 模板名称 */
|
||||
name: string;
|
||||
/** 状态 */
|
||||
status: number;
|
||||
/** 发放数量 */
|
||||
totalCount: number;
|
||||
/** 每人限领个数 */
|
||||
takeLimitCount: number;
|
||||
/** 领取方式 */
|
||||
takeType: number;
|
||||
/** 使用门槛 */
|
||||
usePrice: number;
|
||||
/** 商品范围 */
|
||||
productScope: number;
|
||||
/** 商品范围值 */
|
||||
productScopeValues: number[];
|
||||
/** 有效期类型 */
|
||||
validityType: number;
|
||||
/** 固定日期-生效开始时间 */
|
||||
validStartTime: Date;
|
||||
/** 固定日期-生效结束时间 */
|
||||
validEndTime: Date;
|
||||
/** 领取日期-开始天数 */
|
||||
fixedStartTerm: number;
|
||||
/** 领取日期-结束天数 */
|
||||
fixedEndTerm: number;
|
||||
/** 优惠类型 */
|
||||
discountType: number;
|
||||
/** 折扣百分比 */
|
||||
discountPercent: number;
|
||||
/** 优惠金额 */
|
||||
discountPrice: number;
|
||||
/** 折扣上限 */
|
||||
discountLimitPrice: number;
|
||||
/** 已领取数量 */
|
||||
takeCount: number;
|
||||
/** 已使用数量 */
|
||||
useCount: number;
|
||||
}
|
||||
|
||||
/** 优惠券模板状态更新 */
|
||||
export interface StatusUpdate {
|
||||
/** 模板编号 */
|
||||
id: number;
|
||||
/** 状态 */
|
||||
status: 0 | 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建优惠劵模板 */
|
||||
export function createCouponTemplate(
|
||||
data: MallCouponTemplateApi.CouponTemplate,
|
||||
) {
|
||||
return requestClient.post('/promotion/coupon-template/create', data);
|
||||
}
|
||||
|
||||
/** 更新优惠劵模板 */
|
||||
export function updateCouponTemplate(
|
||||
data: MallCouponTemplateApi.CouponTemplate,
|
||||
) {
|
||||
return requestClient.put('/promotion/coupon-template/update', data);
|
||||
}
|
||||
|
||||
/** 更新优惠劵模板的状态 */
|
||||
export function updateCouponTemplateStatus(id: number, status: 0 | 1) {
|
||||
const data: MallCouponTemplateApi.StatusUpdate = { id, status };
|
||||
return requestClient.put('/promotion/coupon-template/update-status', data);
|
||||
}
|
||||
|
||||
/** 删除优惠劵模板 */
|
||||
export function deleteCouponTemplate(id: number) {
|
||||
return requestClient.delete(`/promotion/coupon-template/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得优惠劵模板 */
|
||||
export function getCouponTemplate(id: number) {
|
||||
return requestClient.get<MallCouponTemplateApi.CouponTemplate>(
|
||||
`/promotion/coupon-template/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得优惠劵模板分页 */
|
||||
export function getCouponTemplatePage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallCouponTemplateApi.CouponTemplate>>(
|
||||
'/promotion/coupon-template/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得优惠劵模板列表 */
|
||||
export function getCouponTemplateList(ids: number[]) {
|
||||
return requestClient.get<MallCouponTemplateApi.CouponTemplate[]>(
|
||||
`/promotion/coupon-template/list?ids=${ids}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 导出优惠劵模板 Excel */
|
||||
export function exportCouponTemplateExcel(params: PageParam) {
|
||||
return requestClient.get('/promotion/coupon-template/export-excel', {
|
||||
params,
|
||||
responseType: 'blob',
|
||||
});
|
||||
}
|
@@ -1,91 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import type { MallSpuApi } from '#/api/mall/product/spu';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallDiscountActivityApi {
|
||||
/** 限时折扣相关属性 */
|
||||
export interface DiscountProduct {
|
||||
/** 商品 SPU 编号 */
|
||||
spuId: number;
|
||||
/** 商品 SKU 编号 */
|
||||
skuId: number;
|
||||
/** 折扣类型 */
|
||||
discountType: number;
|
||||
/** 折扣百分比 */
|
||||
discountPercent: number;
|
||||
/** 折扣价格 */
|
||||
discountPrice: number;
|
||||
}
|
||||
|
||||
/** 限时折扣活动 */
|
||||
export interface DiscountActivity {
|
||||
/** 活动编号 */
|
||||
id?: number;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId?: number;
|
||||
/** 活动名称 */
|
||||
name?: string;
|
||||
/** 状态 */
|
||||
status?: number;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
/** 开始时间 */
|
||||
startTime?: Date;
|
||||
/** 结束时间 */
|
||||
endTime?: Date;
|
||||
/** 商品列表 */
|
||||
products?: DiscountProduct[];
|
||||
}
|
||||
|
||||
/** 扩展 SKU 配置 */
|
||||
export type SkuExtension = {
|
||||
/** 限时折扣配置 */
|
||||
productConfig: DiscountProduct;
|
||||
} & MallSpuApi.Sku;
|
||||
|
||||
/** 扩展 SPU 配置 */
|
||||
export interface SpuExtension extends MallSpuApi.Spu {
|
||||
/** SKU 列表 */
|
||||
skus: SkuExtension[];
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询限时折扣活动列表 */
|
||||
export function getDiscountActivityPage(params: PageParam) {
|
||||
return requestClient.get<
|
||||
PageResult<MallDiscountActivityApi.DiscountActivity>
|
||||
>('/promotion/discount-activity/page', { params });
|
||||
}
|
||||
|
||||
/** 查询限时折扣活动详情 */
|
||||
export function getDiscountActivity(id: number) {
|
||||
return requestClient.get<MallDiscountActivityApi.DiscountActivity>(
|
||||
`/promotion/discount-activity/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增限时折扣活动 */
|
||||
export function createDiscountActivity(
|
||||
data: MallDiscountActivityApi.DiscountActivity,
|
||||
) {
|
||||
return requestClient.post('/promotion/discount-activity/create', data);
|
||||
}
|
||||
|
||||
/** 修改限时折扣活动 */
|
||||
export function updateDiscountActivity(
|
||||
data: MallDiscountActivityApi.DiscountActivity,
|
||||
) {
|
||||
return requestClient.put('/promotion/discount-activity/update', data);
|
||||
}
|
||||
|
||||
/** 关闭限时折扣活动 */
|
||||
export function closeDiscountActivity(id: number) {
|
||||
return requestClient.put(`/promotion/discount-activity/close?id=${id}`);
|
||||
}
|
||||
|
||||
/** 删除限时折扣活动 */
|
||||
export function deleteDiscountActivity(id: number) {
|
||||
return requestClient.delete(`/promotion/discount-activity/delete?id=${id}`);
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallDiyPageApi {
|
||||
/** 装修页面 */
|
||||
export interface DiyPage {
|
||||
/** 页面编号 */
|
||||
id?: number;
|
||||
/** 模板编号 */
|
||||
templateId?: number;
|
||||
/** 页面名称 */
|
||||
name: string;
|
||||
/** 备注 */
|
||||
remark: string;
|
||||
/** 预览图片地址数组 */
|
||||
previewPicUrls: string[];
|
||||
/** 页面属性 */
|
||||
property: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询装修页面列表 */
|
||||
export function getDiyPagePage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallDiyPageApi.DiyPage>>(
|
||||
'/promotion/diy-page/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询装修页面详情 */
|
||||
export function getDiyPage(id: number) {
|
||||
return requestClient.get<MallDiyPageApi.DiyPage>(
|
||||
`/promotion/diy-page/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增装修页面 */
|
||||
export function createDiyPage(data: MallDiyPageApi.DiyPage) {
|
||||
return requestClient.post('/promotion/diy-page/create', data);
|
||||
}
|
||||
|
||||
/** 修改装修页面 */
|
||||
export function updateDiyPage(data: MallDiyPageApi.DiyPage) {
|
||||
return requestClient.put('/promotion/diy-page/update', data);
|
||||
}
|
||||
|
||||
/** 删除装修页面 */
|
||||
export function deleteDiyPage(id: number) {
|
||||
return requestClient.delete(`/promotion/diy-page/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得装修页面属性 */
|
||||
export function getDiyPageProperty(id: number) {
|
||||
return requestClient.get<string>(`/promotion/diy-page/get-property?id=${id}`);
|
||||
}
|
||||
|
||||
/** 更新装修页面属性 */
|
||||
export function updateDiyPageProperty(data: MallDiyPageApi.DiyPage) {
|
||||
return requestClient.put('/promotion/diy-page/update-property', data);
|
||||
}
|
@@ -1,80 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import type { MallDiyPageApi } from './page';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallDiyTemplateApi {
|
||||
/** 装修模板 */
|
||||
export interface DiyTemplate {
|
||||
/** 模板编号 */
|
||||
id?: number;
|
||||
/** 模板名称 */
|
||||
name: string;
|
||||
/** 是否使用 */
|
||||
used: boolean;
|
||||
/** 使用时间 */
|
||||
usedTime?: Date;
|
||||
/** 备注 */
|
||||
remark: string;
|
||||
/** 预览图片地址数组 */
|
||||
previewPicUrls: string[];
|
||||
/** 模板属性 */
|
||||
property: string;
|
||||
}
|
||||
|
||||
/** 装修模板属性(包含页面列表) */
|
||||
export interface DiyTemplateProperty extends DiyTemplate {
|
||||
/** 页面列表 */
|
||||
pages: MallDiyPageApi.DiyPage[];
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询装修模板列表 */
|
||||
export function getDiyTemplatePage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallDiyTemplateApi.DiyTemplate>>(
|
||||
'/promotion/diy-template/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询装修模板详情 */
|
||||
export function getDiyTemplate(id: number) {
|
||||
return requestClient.get<MallDiyTemplateApi.DiyTemplate>(
|
||||
`/promotion/diy-template/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增装修模板 */
|
||||
export function createDiyTemplate(data: MallDiyTemplateApi.DiyTemplate) {
|
||||
return requestClient.post('/promotion/diy-template/create', data);
|
||||
}
|
||||
|
||||
/** 修改装修模板 */
|
||||
export function updateDiyTemplate(data: MallDiyTemplateApi.DiyTemplate) {
|
||||
return requestClient.put('/promotion/diy-template/update', data);
|
||||
}
|
||||
|
||||
/** 删除装修模板 */
|
||||
export function deleteDiyTemplate(id: number) {
|
||||
return requestClient.delete(`/promotion/diy-template/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 使用装修模板 */
|
||||
export function useDiyTemplate(id: number) {
|
||||
return requestClient.put(`/promotion/diy-template/use?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获得装修模板属性 */
|
||||
export function getDiyTemplateProperty(id: number) {
|
||||
return requestClient.get<MallDiyTemplateApi.DiyTemplateProperty>(
|
||||
`/promotion/diy-template/get-property?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 更新装修模板属性 */
|
||||
export function updateDiyTemplateProperty(
|
||||
data: MallDiyTemplateApi.DiyTemplate,
|
||||
) {
|
||||
return requestClient.put('/promotion/diy-template/update-property', data);
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
import type { PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallKefuConversationApi {
|
||||
/** 客服会话 */
|
||||
export interface Conversation {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 会话所属用户 */
|
||||
userId: number;
|
||||
/** 会话所属用户头像 */
|
||||
userAvatar: string;
|
||||
/** 会话所属用户昵称 */
|
||||
userNickname: string;
|
||||
/** 最后聊天时间 */
|
||||
lastMessageTime: Date;
|
||||
/** 最后聊天内容 */
|
||||
lastMessageContent: string;
|
||||
/** 最后发送的消息类型 */
|
||||
lastMessageContentType: number;
|
||||
/** 管理端置顶 */
|
||||
adminPinned: boolean;
|
||||
/** 用户是否可见 */
|
||||
userDeleted: boolean;
|
||||
/** 管理员是否可见 */
|
||||
adminDeleted: boolean;
|
||||
/** 管理员未读消息数 */
|
||||
adminUnreadMessageCount: number;
|
||||
/** 创建时间 */
|
||||
createTime?: string;
|
||||
}
|
||||
|
||||
/** 会话置顶请求 */
|
||||
export interface ConversationPinnedUpdate {
|
||||
/** 会话编号 */
|
||||
id: number;
|
||||
/** 是否置顶 */
|
||||
pinned: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获得客服会话列表 */
|
||||
export function getConversationList() {
|
||||
return requestClient.get<PageResult<MallKefuConversationApi.Conversation>>(
|
||||
'/promotion/kefu-conversation/list',
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得客服会话 */
|
||||
export function getConversation(id: number) {
|
||||
return requestClient.get<MallKefuConversationApi.Conversation>(
|
||||
`/promotion/kefu-conversation/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 客服会话置顶 */
|
||||
export function updateConversationPinned(
|
||||
data: MallKefuConversationApi.ConversationPinnedUpdate,
|
||||
) {
|
||||
return requestClient.put(
|
||||
'/promotion/kefu-conversation/update-conversation-pinned',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/** 删除客服会话 */
|
||||
export function deleteConversation(id: number) {
|
||||
return requestClient.delete(`/promotion/kefu-conversation/delete?id=${id}`);
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallKefuMessageApi {
|
||||
/** 客服消息 */
|
||||
export interface Message {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 会话编号 */
|
||||
conversationId: number;
|
||||
/** 发送人编号 */
|
||||
senderId: number;
|
||||
/** 发送人头像 */
|
||||
senderAvatar: string;
|
||||
/** 发送人类型 */
|
||||
senderType: number;
|
||||
/** 接收人编号 */
|
||||
receiverId: number;
|
||||
/** 接收人类型 */
|
||||
receiverType: number;
|
||||
/** 消息类型 */
|
||||
contentType: number;
|
||||
/** 消息内容 */
|
||||
content: string;
|
||||
/** 是否已读 */
|
||||
readStatus: boolean;
|
||||
/** 创建时间 */
|
||||
createTime: Date;
|
||||
}
|
||||
|
||||
/** 发送消息请求 */
|
||||
export interface MessageSend {
|
||||
/** 会话编号 */
|
||||
conversationId: number;
|
||||
/** 消息类型 */
|
||||
contentType: number;
|
||||
/** 消息内容 */
|
||||
content: string;
|
||||
}
|
||||
|
||||
/** 消息列表查询参数 */
|
||||
export interface MessageQuery extends PageParam {
|
||||
/** 会话编号 */
|
||||
conversationId: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 发送客服消息 */
|
||||
export function sendKeFuMessage(data: MallKefuMessageApi.MessageSend) {
|
||||
return requestClient.post('/promotion/kefu-message/send', data);
|
||||
}
|
||||
|
||||
/** 更新客服消息已读状态 */
|
||||
export function updateKeFuMessageReadStatus(conversationId: number) {
|
||||
return requestClient.put(
|
||||
`/promotion/kefu-message/update-read-status?conversationId=${conversationId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得消息列表(流式加载) */
|
||||
export function getKeFuMessageList(params: MallKefuMessageApi.MessageQuery) {
|
||||
return requestClient.get<PageResult<MallKefuMessageApi.Message>>(
|
||||
'/promotion/kefu-message/list',
|
||||
{ params },
|
||||
);
|
||||
}
|
@@ -1,127 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import type { MallSpuApi } from '#/api/mall/product/spu';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallPointActivityApi {
|
||||
/** 积分商城商品 */
|
||||
export interface PointProduct {
|
||||
/** 积分商城商品编号 */
|
||||
id?: number;
|
||||
/** 积分商城活动 id */
|
||||
activityId?: number;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId?: number;
|
||||
/** 商品 SKU 编号 */
|
||||
skuId: number;
|
||||
/** 可兑换数量 */
|
||||
count: number;
|
||||
/** 兑换积分 */
|
||||
point: number;
|
||||
/** 兑换金额,单位:分 */
|
||||
price: number;
|
||||
/** 积分商城商品库存 */
|
||||
stock: number;
|
||||
/** 积分商城商品状态 */
|
||||
activityStatus?: number;
|
||||
}
|
||||
|
||||
/** 积分商城活动 */
|
||||
export interface PointActivity {
|
||||
/** 积分商城活动编号 */
|
||||
id: number;
|
||||
/** 积分商城活动商品 */
|
||||
spuId: number;
|
||||
/** 活动状态 */
|
||||
status: number;
|
||||
/** 积分商城活动库存 */
|
||||
stock: number;
|
||||
/** 积分商城活动总库存 */
|
||||
totalStock: number;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
/** 排序 */
|
||||
sort: number;
|
||||
/** 创建时间 */
|
||||
createTime: string;
|
||||
/** 积分商城商品 */
|
||||
products: PointProduct[];
|
||||
/** 商品名称 */
|
||||
spuName: string;
|
||||
/** 商品主图 */
|
||||
picUrl: string;
|
||||
/** 商品市场价,单位:分 */
|
||||
marketPrice: number;
|
||||
/** 兑换积分 */
|
||||
point: number;
|
||||
/** 兑换金额,单位:分 */
|
||||
price: number;
|
||||
}
|
||||
|
||||
/** 扩展 SKU 配置 */
|
||||
export type SkuExtension = {
|
||||
/** 积分商城商品配置 */
|
||||
productConfig: PointProduct;
|
||||
} & MallSpuApi.Sku;
|
||||
|
||||
/** 扩展 SPU 配置 */
|
||||
export interface SpuExtension extends MallSpuApi.Spu {
|
||||
/** SKU 列表 */
|
||||
skus: SkuExtension[];
|
||||
}
|
||||
|
||||
/** 扩展 SPU 配置(带积分信息) */
|
||||
export interface SpuExtensionWithPoint extends MallSpuApi.Spu {
|
||||
/** 积分商城活动库存 */
|
||||
pointStock: number;
|
||||
/** 积分商城活动总库存 */
|
||||
pointTotalStock: number;
|
||||
/** 兑换积分 */
|
||||
point: number;
|
||||
/** 兑换金额,单位:分 */
|
||||
pointPrice: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询积分商城活动分页 */
|
||||
export function getPointActivityPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallPointActivityApi.PointActivity>>(
|
||||
'/promotion/point-activity/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询积分商城活动详情 */
|
||||
export function getPointActivity(id: number) {
|
||||
return requestClient.get<MallPointActivityApi.PointActivity>(
|
||||
`/promotion/point-activity/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询积分商城活动列表,基于活动编号数组 */
|
||||
export function getPointActivityListByIds(ids: number[]) {
|
||||
return requestClient.get<MallPointActivityApi.PointActivity[]>(
|
||||
`/promotion/point-activity/list-by-ids?ids=${ids}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增积分商城活动 */
|
||||
export function createPointActivity(data: MallPointActivityApi.PointActivity) {
|
||||
return requestClient.post('/promotion/point-activity/create', data);
|
||||
}
|
||||
|
||||
/** 修改积分商城活动 */
|
||||
export function updatePointActivity(data: MallPointActivityApi.PointActivity) {
|
||||
return requestClient.put('/promotion/point-activity/update', data);
|
||||
}
|
||||
|
||||
/** 删除积分商城活动 */
|
||||
export function deletePointActivity(id: number) {
|
||||
return requestClient.delete(`/promotion/point-activity/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 关闭积分商城活动 */
|
||||
export function closePointActivity(id: number) {
|
||||
return requestClient.put(`/promotion/point-activity/close?id=${id}`);
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallRewardActivityApi {
|
||||
/** 优惠规则 */
|
||||
export interface RewardRule {
|
||||
/** 满足金额 */
|
||||
limit?: number;
|
||||
/** 优惠金额 */
|
||||
discountPrice?: number;
|
||||
/** 是否包邮 */
|
||||
freeDelivery?: boolean;
|
||||
/** 赠送积分 */
|
||||
point: number;
|
||||
/** 赠送优惠券数量 */
|
||||
giveCouponTemplateCounts?: {
|
||||
[key: number]: number;
|
||||
};
|
||||
}
|
||||
|
||||
/** 满减送活动 */
|
||||
export interface RewardActivity {
|
||||
/** 活动编号 */
|
||||
id?: number;
|
||||
/** 活动名称 */
|
||||
name?: string;
|
||||
/** 开始时间 */
|
||||
startTime?: Date;
|
||||
/** 结束时间 */
|
||||
endTime?: Date;
|
||||
/** 开始和结束时间(仅前端使用) */
|
||||
startAndEndTime?: Date[];
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
/** 条件类型 */
|
||||
conditionType?: number;
|
||||
/** 商品范围 */
|
||||
productScope?: number;
|
||||
/** 优惠规则列表 */
|
||||
rules: RewardRule[];
|
||||
/** 商品范围值(仅表单使用):值为品类编号列表、商品编号列表 */
|
||||
productScopeValues?: number[];
|
||||
/** 商品分类编号列表(仅表单使用) */
|
||||
productCategoryIds?: number[];
|
||||
/** 商品 SPU 编号列表(仅表单使用) */
|
||||
productSpuIds?: number[];
|
||||
}
|
||||
}
|
||||
|
||||
/** 新增满减送活动 */
|
||||
export function createRewardActivity(
|
||||
data: MallRewardActivityApi.RewardActivity,
|
||||
) {
|
||||
return requestClient.post('/promotion/reward-activity/create', data);
|
||||
}
|
||||
|
||||
/** 更新满减送活动 */
|
||||
export function updateRewardActivity(
|
||||
data: MallRewardActivityApi.RewardActivity,
|
||||
) {
|
||||
return requestClient.put('/promotion/reward-activity/update', data);
|
||||
}
|
||||
|
||||
/** 查询满减送活动列表 */
|
||||
export function getRewardActivityPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallRewardActivityApi.RewardActivity>>(
|
||||
'/promotion/reward-activity/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询满减送活动详情 */
|
||||
export function getReward(id: number) {
|
||||
return requestClient.get<MallRewardActivityApi.RewardActivity>(
|
||||
`/promotion/reward-activity/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 删除满减送活动 */
|
||||
export function deleteRewardActivity(id: number) {
|
||||
return requestClient.delete(`/promotion/reward-activity/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 关闭满减送活动 */
|
||||
export function closeRewardActivity(id: number) {
|
||||
return requestClient.put(`/promotion/reward-activity/close?id=${id}`);
|
||||
}
|
@@ -1,117 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import type { MallSpuApi } from '#/api/mall/product/spu';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallSeckillActivityApi {
|
||||
/** 秒杀商品 */
|
||||
export interface SeckillProduct {
|
||||
/** 商品 SKU 编号 */
|
||||
skuId: number;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId: number;
|
||||
/** 秒杀价格 */
|
||||
seckillPrice: number;
|
||||
/** 秒杀库存 */
|
||||
stock: number;
|
||||
}
|
||||
|
||||
/** 秒杀活动 */
|
||||
export interface SeckillActivity {
|
||||
/** 活动编号 */
|
||||
id?: number;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId?: number;
|
||||
/** 活动名称 */
|
||||
name?: string;
|
||||
/** 活动状态 */
|
||||
status?: number;
|
||||
/** 备注 */
|
||||
remark?: string;
|
||||
/** 开始时间 */
|
||||
startTime?: Date;
|
||||
/** 结束时间 */
|
||||
endTime?: Date;
|
||||
/** 排序 */
|
||||
sort?: number;
|
||||
/** 配置编号 */
|
||||
configIds?: string;
|
||||
/** 订单数量 */
|
||||
orderCount?: number;
|
||||
/** 用户数量 */
|
||||
userCount?: number;
|
||||
/** 总金额 */
|
||||
totalPrice?: number;
|
||||
/** 总限购数量 */
|
||||
totalLimitCount?: number;
|
||||
/** 单次限购数量 */
|
||||
singleLimitCount?: number;
|
||||
/** 秒杀库存 */
|
||||
stock?: number;
|
||||
/** 秒杀总库存 */
|
||||
totalStock?: number;
|
||||
/** 秒杀价格 */
|
||||
seckillPrice?: number;
|
||||
/** 秒杀商品列表 */
|
||||
products?: SeckillProduct[];
|
||||
}
|
||||
|
||||
/** 扩展 SKU 配置 */
|
||||
export type SkuExtension = {
|
||||
/** 秒杀商品配置 */
|
||||
productConfig: SeckillProduct;
|
||||
} & MallSpuApi.Sku;
|
||||
|
||||
/** 扩展 SPU 配置 */
|
||||
export interface SpuExtension extends MallSpuApi.Spu {
|
||||
/** SKU 列表 */
|
||||
skus: SkuExtension[];
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询秒杀活动列表 */
|
||||
export function getSeckillActivityPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallSeckillActivityApi.SeckillActivity>>(
|
||||
'/promotion/seckill-activity/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询秒杀活动列表,基于活动编号数组 */
|
||||
export function getSeckillActivityListByIds(ids: number[]) {
|
||||
return requestClient.get<MallSeckillActivityApi.SeckillActivity[]>(
|
||||
`/promotion/seckill-activity/list-by-ids?ids=${ids}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询秒杀活动详情 */
|
||||
export function getSeckillActivity(id: number) {
|
||||
return requestClient.get<MallSeckillActivityApi.SeckillActivity>(
|
||||
`/promotion/seckill-activity/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增秒杀活动 */
|
||||
export function createSeckillActivity(
|
||||
data: MallSeckillActivityApi.SeckillActivity,
|
||||
) {
|
||||
return requestClient.post('/promotion/seckill-activity/create', data);
|
||||
}
|
||||
|
||||
/** 修改秒杀活动 */
|
||||
export function updateSeckillActivity(
|
||||
data: MallSeckillActivityApi.SeckillActivity,
|
||||
) {
|
||||
return requestClient.put('/promotion/seckill-activity/update', data);
|
||||
}
|
||||
|
||||
/** 关闭秒杀活动 */
|
||||
export function closeSeckillActivity(id: number) {
|
||||
return requestClient.put(`/promotion/seckill-activity/close?id=${id}`);
|
||||
}
|
||||
|
||||
/** 删除秒杀活动 */
|
||||
export function deleteSeckillActivity(id: number) {
|
||||
return requestClient.delete(`/promotion/seckill-activity/delete?id=${id}`);
|
||||
}
|
@@ -1,74 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallSeckillConfigApi {
|
||||
/** 秒杀时段 */
|
||||
export interface SeckillConfig {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 秒杀时段名称 */
|
||||
name: string;
|
||||
/** 开始时间点 */
|
||||
startTime: string;
|
||||
/** 结束时间点 */
|
||||
endTime: string;
|
||||
/** 秒杀轮播图 */
|
||||
sliderPicUrls: string[];
|
||||
/** 活动状态 */
|
||||
status: number;
|
||||
}
|
||||
|
||||
/** 时段配置状态更新 */
|
||||
export interface StatusUpdate {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 状态 */
|
||||
status: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询秒杀时段分页 */
|
||||
export function getSeckillConfigPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallSeckillConfigApi.SeckillConfig>>(
|
||||
'/promotion/seckill-config/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询秒杀时段列表 */
|
||||
export function getSimpleSeckillConfigList() {
|
||||
return requestClient.get<MallSeckillConfigApi.SeckillConfig[]>(
|
||||
'/promotion/seckill-config/list',
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询秒杀时段详情 */
|
||||
export function getSeckillConfig(id: number) {
|
||||
return requestClient.get<MallSeckillConfigApi.SeckillConfig>(
|
||||
`/promotion/seckill-config/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增秒杀时段 */
|
||||
export function createSeckillConfig(data: MallSeckillConfigApi.SeckillConfig) {
|
||||
return requestClient.post('/promotion/seckill-config/create', data);
|
||||
}
|
||||
|
||||
/** 修改秒杀时段 */
|
||||
export function updateSeckillConfig(data: MallSeckillConfigApi.SeckillConfig) {
|
||||
return requestClient.put('/promotion/seckill-config/update', data);
|
||||
}
|
||||
|
||||
/** 删除秒杀时段 */
|
||||
export function deleteSeckillConfig(id: number) {
|
||||
return requestClient.delete(`/promotion/seckill-config/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 修改时段配置状态 */
|
||||
export function updateSeckillConfigStatus(id: number, status: number) {
|
||||
return requestClient.put('/promotion/seckill-config/update-status', {
|
||||
id,
|
||||
status,
|
||||
});
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
/** 数据对照 Response */
|
||||
export interface MallDataComparisonResp<T> {
|
||||
value: T;
|
||||
reference: T;
|
||||
}
|
@@ -1,131 +0,0 @@
|
||||
import type { MallDataComparisonResp } from './common';
|
||||
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallMemberStatisticsApi {
|
||||
/** 会员分析 Request */
|
||||
export interface AnalyseReq {
|
||||
times: Date[];
|
||||
}
|
||||
|
||||
/** 会员分析对照数据 Response */
|
||||
export interface AnalyseComparison {
|
||||
registerUserCount: number;
|
||||
visitUserCount: number;
|
||||
rechargeUserCount: number;
|
||||
}
|
||||
|
||||
/** 会员分析 Response */
|
||||
export interface Analyse {
|
||||
visitUserCount: number;
|
||||
orderUserCount: number;
|
||||
payUserCount: number;
|
||||
atv: number;
|
||||
comparison: MallDataComparisonResp<AnalyseComparison>;
|
||||
}
|
||||
|
||||
/** 会员地区统计 Response */
|
||||
export interface AreaStatistics {
|
||||
areaId: number;
|
||||
areaName: string;
|
||||
userCount: number;
|
||||
orderCreateUserCount: number;
|
||||
orderPayUserCount: number;
|
||||
orderPayPrice: number;
|
||||
}
|
||||
|
||||
/** 会员性别统计 Response */
|
||||
export interface SexStatistics {
|
||||
sex: number;
|
||||
userCount: number;
|
||||
}
|
||||
|
||||
/** 会员统计 Response */
|
||||
export interface Summary {
|
||||
userCount: number;
|
||||
rechargeUserCount: number;
|
||||
rechargePrice: number;
|
||||
expensePrice: number;
|
||||
}
|
||||
|
||||
/** 会员终端统计 Response */
|
||||
export interface TerminalStatistics {
|
||||
terminal: number;
|
||||
userCount: number;
|
||||
}
|
||||
|
||||
/** 会员数量统计 Response */
|
||||
export interface Count {
|
||||
/** 用户访问量 */
|
||||
visitUserCount: string;
|
||||
/** 注册用户数量 */
|
||||
registerUserCount: number;
|
||||
}
|
||||
|
||||
/** 会员注册数量 Response */
|
||||
export interface RegisterCount {
|
||||
date: string;
|
||||
count: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询会员统计 */
|
||||
export function getMemberSummary() {
|
||||
return requestClient.get<MallMemberStatisticsApi.Summary>(
|
||||
'/statistics/member/summary',
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询会员分析数据 */
|
||||
export function getMemberAnalyse(params: MallMemberStatisticsApi.AnalyseReq) {
|
||||
return requestClient.get<MallMemberStatisticsApi.Analyse>(
|
||||
'/statistics/member/analyse',
|
||||
{
|
||||
params: {
|
||||
times: [formatDate(params.times[0]), formatDate(params.times[1])],
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 按照省份,查询会员统计列表 */
|
||||
export function getMemberAreaStatisticsList() {
|
||||
return requestClient.get<MallMemberStatisticsApi.AreaStatistics[]>(
|
||||
'/statistics/member/area-statistics-list',
|
||||
);
|
||||
}
|
||||
|
||||
/** 按照性别,查询会员统计列表 */
|
||||
export function getMemberSexStatisticsList() {
|
||||
return requestClient.get<MallMemberStatisticsApi.SexStatistics[]>(
|
||||
'/statistics/member/sex-statistics-list',
|
||||
);
|
||||
}
|
||||
|
||||
/** 按照终端,查询会员统计列表 */
|
||||
export function getMemberTerminalStatisticsList() {
|
||||
return requestClient.get<MallMemberStatisticsApi.TerminalStatistics[]>(
|
||||
'/statistics/member/terminal-statistics-list',
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得用户数量量对照 */
|
||||
export function getUserCountComparison() {
|
||||
return requestClient.get<
|
||||
MallDataComparisonResp<MallMemberStatisticsApi.Count>
|
||||
>('/statistics/member/user-count-comparison');
|
||||
}
|
||||
|
||||
/** 获得会员注册数量列表 */
|
||||
export function getMemberRegisterCountList(beginTime: Date, endTime: Date) {
|
||||
return requestClient.get<MallMemberStatisticsApi.RegisterCount[]>(
|
||||
'/statistics/member/register-count-list',
|
||||
{
|
||||
params: {
|
||||
times: [formatDate(beginTime), formatDate(endTime)],
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallPayStatisticsApi {
|
||||
/** 支付统计 */
|
||||
export interface PaySummaryResp {
|
||||
/** 充值金额,单位分 */
|
||||
rechargePrice: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取钱包充值金额 */
|
||||
export function getWalletRechargePrice() {
|
||||
return requestClient.get<MallPayStatisticsApi.PaySummaryResp>(
|
||||
'/statistics/pay/summary',
|
||||
);
|
||||
}
|
@@ -1,68 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import type { MallDataComparisonResp } from './common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallProductStatisticsApi {
|
||||
/** 商品统计数据 */
|
||||
export interface ProductStatistics {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 统计日期 */
|
||||
day: string;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId: number;
|
||||
/** 商品 SPU 名称 */
|
||||
spuName: string;
|
||||
/** 商品 SPU 图片 */
|
||||
spuPicUrl: string;
|
||||
/** 浏览次数 */
|
||||
browseCount: number;
|
||||
/** 浏览人数 */
|
||||
browseUserCount: number;
|
||||
/** 收藏次数 */
|
||||
favoriteCount: number;
|
||||
/** 加购次数 */
|
||||
cartCount: number;
|
||||
/** 下单次数 */
|
||||
orderCount: number;
|
||||
/** 支付次数 */
|
||||
orderPayCount: number;
|
||||
/** 支付金额 */
|
||||
orderPayPrice: number;
|
||||
/** 售后次数 */
|
||||
afterSaleCount: number;
|
||||
/** 退款金额 */
|
||||
afterSaleRefundPrice: number;
|
||||
/** 浏览转化率 */
|
||||
browseConvertPercent: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获得商品统计分析 */
|
||||
export function getProductStatisticsAnalyse(params: PageParam) {
|
||||
return requestClient.get<
|
||||
MallDataComparisonResp<MallProductStatisticsApi.ProductStatistics>
|
||||
>('/statistics/product/analyse', { params });
|
||||
}
|
||||
|
||||
/** 获得商品状况明细 */
|
||||
export function getProductStatisticsList(params: PageParam) {
|
||||
return requestClient.get<MallProductStatisticsApi.ProductStatistics[]>(
|
||||
'/statistics/product/list',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 导出获得商品状况明细 Excel */
|
||||
export function exportProductStatisticsExcel(params: PageParam) {
|
||||
return requestClient.download('/statistics/product/export-excel', { params });
|
||||
}
|
||||
|
||||
/** 获得商品排行榜分页 */
|
||||
export function getProductStatisticsRankPage(params: PageParam) {
|
||||
return requestClient.get<
|
||||
PageResult<MallProductStatisticsApi.ProductStatistics>
|
||||
>('/statistics/product/rank-page', { params });
|
||||
}
|
@@ -1,135 +0,0 @@
|
||||
import type { MallDataComparisonResp } from './common';
|
||||
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallTradeStatisticsApi {
|
||||
/** 交易统计 Response */
|
||||
export interface TradeSummary {
|
||||
yesterdayOrderCount: number;
|
||||
monthOrderCount: number;
|
||||
yesterdayPayPrice: number;
|
||||
monthPayPrice: number;
|
||||
}
|
||||
|
||||
/** 交易状况 Request */
|
||||
export interface TradeTrendReq {
|
||||
times: [Date, Date];
|
||||
}
|
||||
|
||||
/** 交易状况统计 Response */
|
||||
export interface TradeTrendSummary {
|
||||
time: string;
|
||||
turnoverPrice: number;
|
||||
orderPayPrice: number;
|
||||
rechargePrice: number;
|
||||
expensePrice: number;
|
||||
walletPayPrice: number;
|
||||
brokerageSettlementPrice: number;
|
||||
afterSaleRefundPrice: number;
|
||||
}
|
||||
|
||||
/** 交易订单数量 Response */
|
||||
export interface TradeOrderCount {
|
||||
/** 待发货 */
|
||||
undelivered?: number;
|
||||
/** 待核销 */
|
||||
pickUp?: number;
|
||||
/** 退款中 */
|
||||
afterSaleApply?: number;
|
||||
/** 提现待审核 */
|
||||
auditingWithdraw?: number;
|
||||
}
|
||||
|
||||
/** 交易订单统计 Response */
|
||||
export interface TradeOrderSummary {
|
||||
/** 支付订单商品数 */
|
||||
orderPayCount?: number;
|
||||
/** 总支付金额,单位:分 */
|
||||
orderPayPrice?: number;
|
||||
}
|
||||
|
||||
/** 订单量趋势统计 Response */
|
||||
export interface TradeOrderTrend {
|
||||
/** 日期 */
|
||||
date: string;
|
||||
/** 订单数量 */
|
||||
orderPayCount: number;
|
||||
/** 订单支付金额 */
|
||||
orderPayPrice: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 时间参数需要格式化, 确保接口能识别 */
|
||||
const formatDateParam = (params: MallTradeStatisticsApi.TradeTrendReq) => {
|
||||
return {
|
||||
times: [formatDate(params.times[0]), formatDate(params.times[1])],
|
||||
} as MallTradeStatisticsApi.TradeTrendReq;
|
||||
};
|
||||
|
||||
/** 查询交易统计 */
|
||||
export function getTradeStatisticsSummary() {
|
||||
return requestClient.get<
|
||||
MallDataComparisonResp<MallTradeStatisticsApi.TradeSummary>
|
||||
>('/statistics/trade/summary');
|
||||
}
|
||||
|
||||
/** 获得交易状况统计 */
|
||||
export function getTradeStatisticsAnalyse(
|
||||
params: MallTradeStatisticsApi.TradeTrendReq,
|
||||
) {
|
||||
return requestClient.get<
|
||||
MallDataComparisonResp<MallTradeStatisticsApi.TradeTrendSummary>
|
||||
>('/statistics/trade/analyse', { params: formatDateParam(params) });
|
||||
}
|
||||
|
||||
/** 获得交易状况明细 */
|
||||
export function getTradeStatisticsList(
|
||||
params: MallTradeStatisticsApi.TradeTrendReq,
|
||||
) {
|
||||
return requestClient.get<MallTradeStatisticsApi.TradeTrendSummary[]>(
|
||||
'/statistics/trade/list',
|
||||
{ params: formatDateParam(params) },
|
||||
);
|
||||
}
|
||||
|
||||
/** 导出交易状况明细 */
|
||||
export function exportTradeStatisticsExcel(
|
||||
params: MallTradeStatisticsApi.TradeTrendReq,
|
||||
) {
|
||||
return requestClient.download('/statistics/trade/export-excel', {
|
||||
params: formatDateParam(params),
|
||||
});
|
||||
}
|
||||
|
||||
/** 获得交易订单数量 */
|
||||
export function getOrderCount() {
|
||||
return requestClient.get<MallTradeStatisticsApi.TradeOrderCount>(
|
||||
'/statistics/trade/order-count',
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得交易订单数量对照 */
|
||||
export function getOrderComparison() {
|
||||
return requestClient.get<
|
||||
MallDataComparisonResp<MallTradeStatisticsApi.TradeOrderSummary>
|
||||
>('/statistics/trade/order-comparison');
|
||||
}
|
||||
|
||||
/** 获得订单量趋势统计 */
|
||||
export function getOrderCountTrendComparison(
|
||||
type: number,
|
||||
beginTime: Date,
|
||||
endTime: Date,
|
||||
) {
|
||||
return requestClient.get<
|
||||
MallDataComparisonResp<MallTradeStatisticsApi.TradeOrderTrend>[]
|
||||
>('/statistics/trade/order-count-trend', {
|
||||
params: {
|
||||
type,
|
||||
beginTime: formatDate(beginTime),
|
||||
endTime: formatDate(endTime),
|
||||
},
|
||||
});
|
||||
}
|
@@ -1,127 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallAfterSaleApi {
|
||||
/** 商品属性 */
|
||||
export interface ProductProperty {
|
||||
/** 属性的编号 */
|
||||
propertyId?: null | number;
|
||||
/** 属性的名称 */
|
||||
propertyName?: string;
|
||||
/** 属性值的编号 */
|
||||
valueId?: null | number;
|
||||
/** 属性值的名称 */
|
||||
valueName?: string;
|
||||
}
|
||||
|
||||
/** 交易售后 */
|
||||
export interface AfterSale {
|
||||
/** 售后编号,主键自增 */
|
||||
id?: null | number;
|
||||
/** 售后单号 */
|
||||
no?: string;
|
||||
/** 退款状态 */
|
||||
status?: null | number;
|
||||
/** 售后方式 */
|
||||
way?: null | number;
|
||||
/** 售后类型 */
|
||||
type?: null | number;
|
||||
/** 用户编号 */
|
||||
userId?: null | number;
|
||||
/** 申请原因 */
|
||||
applyReason?: string;
|
||||
/** 补充描述 */
|
||||
applyDescription?: string;
|
||||
/** 补充凭证图片 */
|
||||
applyPicUrls?: string[];
|
||||
/** 交易订单编号 */
|
||||
orderId?: null | number;
|
||||
/** 订单流水号 */
|
||||
orderNo?: string;
|
||||
/** 交易订单项编号 */
|
||||
orderItemId?: null | number;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId?: null | number;
|
||||
/** 商品 SPU 名称 */
|
||||
spuName?: string;
|
||||
/** 商品 SKU 编号 */
|
||||
skuId?: null | number;
|
||||
/** 属性数组 */
|
||||
properties?: ProductProperty[];
|
||||
/** 商品图片 */
|
||||
picUrl?: string;
|
||||
/** 退货商品数量 */
|
||||
count?: null | number;
|
||||
/** 审批时间 */
|
||||
auditTime?: Date;
|
||||
/** 审批人 */
|
||||
auditUserId?: null | number;
|
||||
/** 审批备注 */
|
||||
auditReason?: string;
|
||||
/** 退款金额,单位:分 */
|
||||
refundPrice?: null | number;
|
||||
/** 支付退款编号 */
|
||||
payRefundId?: null | number;
|
||||
/** 退款时间 */
|
||||
refundTime?: Date;
|
||||
/** 退货物流公司编号 */
|
||||
logisticsId?: null | number;
|
||||
/** 退货物流单号 */
|
||||
logisticsNo?: string;
|
||||
/** 退货时间 */
|
||||
deliveryTime?: Date;
|
||||
/** 收货时间 */
|
||||
receiveTime?: Date;
|
||||
/** 收货备注 */
|
||||
receiveReason?: string;
|
||||
}
|
||||
|
||||
/** 拒绝售后请求 */
|
||||
export interface DisagreeRequest {
|
||||
/** 售后编号 */
|
||||
id: number;
|
||||
/** 拒绝原因 */
|
||||
reason: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获得交易售后分页 */
|
||||
export function getAfterSalePage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallAfterSaleApi.AfterSale>>(
|
||||
'/trade/after-sale/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得交易售后详情 */
|
||||
export function getAfterSale(id: number) {
|
||||
return requestClient.get<MallAfterSaleApi.AfterSale>(
|
||||
`/trade/after-sale/get-detail?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 同意售后 */
|
||||
export function agree(id: number) {
|
||||
return requestClient.put(`/trade/after-sale/agree?id=${id}`);
|
||||
}
|
||||
|
||||
/** 拒绝售后 */
|
||||
export function disagree(data: MallAfterSaleApi.DisagreeRequest) {
|
||||
return requestClient.put('/trade/after-sale/disagree', data);
|
||||
}
|
||||
|
||||
/** 确认收货 */
|
||||
export function receive(id: number) {
|
||||
return requestClient.put(`/trade/after-sale/receive?id=${id}`);
|
||||
}
|
||||
|
||||
/** 拒绝收货 */
|
||||
export function refuse(id: number) {
|
||||
return requestClient.put(`/trade/after-sale/refuse?id=${id}`);
|
||||
}
|
||||
|
||||
/** 确认退款 */
|
||||
export function refund(id: number) {
|
||||
return requestClient.put(`/trade/after-sale/refund?id=${id}`);
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallBrokerageRecordApi {
|
||||
/** 佣金记录 */
|
||||
export interface BrokerageRecord {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 用户编号 */
|
||||
userId: number;
|
||||
/** 用户昵称 */
|
||||
userNickname: string;
|
||||
/** 用户头像 */
|
||||
userAvatar: string;
|
||||
/** 佣金金额,单位:分 */
|
||||
price: number;
|
||||
/** 佣金类型 */
|
||||
type: number;
|
||||
/** 关联订单编号 */
|
||||
orderId: number;
|
||||
/** 关联订单号 */
|
||||
orderNo: string;
|
||||
/** 创建时间 */
|
||||
createTime: Date;
|
||||
/** 状态 */
|
||||
status: number;
|
||||
/** 结算时间 */
|
||||
settlementTime: Date;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询佣金记录列表 */
|
||||
export function getBrokerageRecordPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallBrokerageRecordApi.BrokerageRecord>>(
|
||||
'/trade/brokerage-record/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询佣金记录详情 */
|
||||
export function getBrokerageRecord(id: number) {
|
||||
return requestClient.get<MallBrokerageRecordApi.BrokerageRecord>(
|
||||
`/trade/brokerage-record/get?id=${id}`,
|
||||
);
|
||||
}
|
@@ -1,99 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallBrokerageUserApi {
|
||||
/** 分销用户 */
|
||||
export interface BrokerageUser {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 推广员编号 */
|
||||
bindUserId: number;
|
||||
/** 推广员绑定时间 */
|
||||
bindUserTime: Date;
|
||||
/** 是否启用分销 */
|
||||
brokerageEnabled: boolean;
|
||||
/** 分销资格时间 */
|
||||
brokerageTime: Date;
|
||||
/** 可提现金额,单位:分 */
|
||||
price: number;
|
||||
/** 冻结金额,单位:分 */
|
||||
frozenPrice: number;
|
||||
/** 用户昵称 */
|
||||
nickname: string;
|
||||
/** 用户头像 */
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
/** 创建分销用户请求 */
|
||||
export interface CreateRequest {
|
||||
/** 用户编号 */
|
||||
userId: number;
|
||||
/** 推广员编号 */
|
||||
bindUserId: number;
|
||||
}
|
||||
|
||||
/** 修改推广员请求 */
|
||||
export interface UpdateBindUserRequest {
|
||||
/** 用户编号 */
|
||||
id: number;
|
||||
/** 推广员编号 */
|
||||
bindUserId: number;
|
||||
}
|
||||
|
||||
/** 清除推广员请求 */
|
||||
export interface ClearBindUserRequest {
|
||||
/** 用户编号 */
|
||||
id: number;
|
||||
}
|
||||
|
||||
/** 修改推广资格请求 */
|
||||
export interface UpdateBrokerageEnabledRequest {
|
||||
/** 用户编号 */
|
||||
id: number;
|
||||
/** 是否启用分销 */
|
||||
enabled: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建分销用户 */
|
||||
export function createBrokerageUser(data: MallBrokerageUserApi.CreateRequest) {
|
||||
return requestClient.post('/trade/brokerage-user/create', data);
|
||||
}
|
||||
|
||||
/** 查询分销用户列表 */
|
||||
export function getBrokerageUserPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallBrokerageUserApi.BrokerageUser>>(
|
||||
'/trade/brokerage-user/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询分销用户详情 */
|
||||
export function getBrokerageUser(id: number) {
|
||||
return requestClient.get<MallBrokerageUserApi.BrokerageUser>(
|
||||
`/trade/brokerage-user/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 修改推广员 */
|
||||
export function updateBindUser(
|
||||
data: MallBrokerageUserApi.UpdateBindUserRequest,
|
||||
) {
|
||||
return requestClient.put('/trade/brokerage-user/update-bind-user', data);
|
||||
}
|
||||
|
||||
/** 清除推广员 */
|
||||
export function clearBindUser(data: MallBrokerageUserApi.ClearBindUserRequest) {
|
||||
return requestClient.put('/trade/brokerage-user/clear-bind-user', data);
|
||||
}
|
||||
|
||||
/** 修改推广资格 */
|
||||
export function updateBrokerageEnabled(
|
||||
data: MallBrokerageUserApi.UpdateBrokerageEnabledRequest,
|
||||
) {
|
||||
return requestClient.put(
|
||||
'/trade/brokerage-user/update-brokerage-enable',
|
||||
data,
|
||||
);
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallBrokerageWithdrawApi {
|
||||
/** 佣金提现 */
|
||||
export interface BrokerageWithdraw {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 用户编号 */
|
||||
userId: number;
|
||||
/** 提现金额,单位:分 */
|
||||
price: number;
|
||||
/** 手续费,单位:分 */
|
||||
feePrice: number;
|
||||
/** 总金额,单位:分 */
|
||||
totalPrice: number;
|
||||
/** 提现类型 */
|
||||
type: number;
|
||||
/** 用户名称 */
|
||||
userName: string;
|
||||
/** 用户账号 */
|
||||
userAccount: string;
|
||||
/** 银行名称 */
|
||||
bankName: string;
|
||||
/** 银行地址 */
|
||||
bankAddress: string;
|
||||
/** 收款码地址 */
|
||||
qrCodeUrl: string;
|
||||
/** 状态 */
|
||||
status: number;
|
||||
/** 审核备注 */
|
||||
auditReason: string;
|
||||
/** 审核时间 */
|
||||
auditTime: Date;
|
||||
/** 备注 */
|
||||
remark: string;
|
||||
/** 支付转账编号 */
|
||||
payTransferId?: number;
|
||||
/** 转账渠道编码 */
|
||||
transferChannelCode?: string;
|
||||
/** 转账时间 */
|
||||
transferTime?: Date;
|
||||
/** 转账错误信息 */
|
||||
transferErrorMsg?: string;
|
||||
}
|
||||
|
||||
/** 驳回申请请求 */
|
||||
export interface RejectRequest {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 驳回原因 */
|
||||
auditReason: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询佣金提现列表 */
|
||||
export function getBrokerageWithdrawPage(params: PageParam) {
|
||||
return requestClient.get<
|
||||
PageResult<MallBrokerageWithdrawApi.BrokerageWithdraw>
|
||||
>('/trade/brokerage-withdraw/page', { params });
|
||||
}
|
||||
|
||||
/** 查询佣金提现详情 */
|
||||
export function getBrokerageWithdraw(id: number) {
|
||||
return requestClient.get<MallBrokerageWithdrawApi.BrokerageWithdraw>(
|
||||
`/trade/brokerage-withdraw/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 佣金提现 - 通过申请 */
|
||||
export function approveBrokerageWithdraw(id: number) {
|
||||
return requestClient.put(`/trade/brokerage-withdraw/approve?id=${id}`);
|
||||
}
|
||||
|
||||
/** 审核佣金提现 - 驳回申请 */
|
||||
export function rejectBrokerageWithdraw(
|
||||
data: MallBrokerageWithdrawApi.RejectRequest,
|
||||
) {
|
||||
return requestClient.put('/trade/brokerage-withdraw/reject', data);
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallTradeConfigApi {
|
||||
/** 交易中心配置 */
|
||||
export interface Config {
|
||||
id?: number;
|
||||
afterSaleRefundReasons?: string[];
|
||||
afterSaleReturnReasons?: string[];
|
||||
deliveryExpressFreeEnabled?: boolean;
|
||||
deliveryExpressFreePrice?: number;
|
||||
deliveryPickUpEnabled?: boolean;
|
||||
brokerageEnabled?: boolean;
|
||||
brokerageEnabledCondition?: number;
|
||||
brokerageBindMode?: number;
|
||||
brokeragePosterUrls?: string;
|
||||
brokerageFirstPercent?: number;
|
||||
brokerageSecondPercent?: number;
|
||||
brokerageWithdrawMinPrice?: number;
|
||||
brokerageFrozenDays?: number;
|
||||
brokerageWithdrawTypes?: string;
|
||||
tencentLbsKey?: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询交易中心配置详情 */
|
||||
export function getTradeConfig() {
|
||||
return requestClient.get<MallTradeConfigApi.Config>('/trade/config/get');
|
||||
}
|
||||
|
||||
/** 保存交易中心配置 */
|
||||
export function saveTradeConfig(data: MallTradeConfigApi.Config) {
|
||||
return requestClient.put('/trade/config/save', data);
|
||||
}
|
@@ -1,79 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallDeliveryExpressApi {
|
||||
/** 快递公司 */
|
||||
export interface DeliveryExpress {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 快递公司编码 */
|
||||
code: string;
|
||||
/** 快递公司名称 */
|
||||
name: string;
|
||||
/** 快递公司 logo */
|
||||
logo: string;
|
||||
/** 排序 */
|
||||
sort: number;
|
||||
/** 状态 */
|
||||
status: number;
|
||||
}
|
||||
|
||||
/** 快递公司精简信息 */
|
||||
export interface SimpleDeliveryExpress {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 快递公司编码 */
|
||||
code: string;
|
||||
/** 快递公司名称 */
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询快递公司列表 */
|
||||
export function getDeliveryExpressPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallDeliveryExpressApi.DeliveryExpress>>(
|
||||
'/trade/delivery/express/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询快递公司详情 */
|
||||
export function getDeliveryExpress(id: number) {
|
||||
return requestClient.get<MallDeliveryExpressApi.DeliveryExpress>(
|
||||
`/trade/delivery/express/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 获得快递公司精简信息列表 */
|
||||
export function getSimpleDeliveryExpressList() {
|
||||
return requestClient.get<MallDeliveryExpressApi.SimpleDeliveryExpress[]>(
|
||||
'/trade/delivery/express/list-all-simple',
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增快递公司 */
|
||||
export function createDeliveryExpress(
|
||||
data: MallDeliveryExpressApi.DeliveryExpress,
|
||||
) {
|
||||
return requestClient.post('/trade/delivery/express/create', data);
|
||||
}
|
||||
|
||||
/** 修改快递公司 */
|
||||
export function updateDeliveryExpress(
|
||||
data: MallDeliveryExpressApi.DeliveryExpress,
|
||||
) {
|
||||
return requestClient.put('/trade/delivery/express/update', data);
|
||||
}
|
||||
|
||||
/** 删除快递公司 */
|
||||
export function deleteDeliveryExpress(id: number) {
|
||||
return requestClient.delete(`/trade/delivery/express/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 导出快递公司 Excel */
|
||||
export function exportDeliveryExpress(params: PageParam) {
|
||||
return requestClient.download('/trade/delivery/express/export-excel', {
|
||||
params,
|
||||
});
|
||||
}
|
@@ -1,95 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallDeliveryExpressTemplateApi {
|
||||
/** 运费模板计费 */
|
||||
export interface TemplateCharge {
|
||||
/** 区域编号列表 */
|
||||
areaIds: number[];
|
||||
/** 首件数量 */
|
||||
startCount: number;
|
||||
/** 首件价格,单位:分 */
|
||||
startPrice: number;
|
||||
/** 续件数量 */
|
||||
extraCount: number;
|
||||
/** 续件价格,单位:分 */
|
||||
extraPrice: number;
|
||||
}
|
||||
|
||||
/** 运费模板包邮 */
|
||||
export interface TemplateFree {
|
||||
/** 区域编号列表 */
|
||||
areaIds: number[];
|
||||
/** 包邮件数 */
|
||||
freeCount: number;
|
||||
/** 包邮金额,单位:分 */
|
||||
freePrice: number;
|
||||
}
|
||||
|
||||
/** 快递运费模板 */
|
||||
export interface ExpressTemplate {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 模板名称 */
|
||||
name: string;
|
||||
/** 计费方式 */
|
||||
chargeMode: number;
|
||||
/** 排序 */
|
||||
sort: number;
|
||||
/** 计费区域列表 */
|
||||
templateCharge: TemplateCharge[];
|
||||
/** 包邮区域列表 */
|
||||
templateFree: TemplateFree[];
|
||||
}
|
||||
|
||||
/** 运费模板精简信息 */
|
||||
export interface SimpleTemplate {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 模板名称 */
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询快递运费模板列表 */
|
||||
export function getDeliveryExpressTemplatePage(params: PageParam) {
|
||||
return requestClient.get<
|
||||
PageResult<MallDeliveryExpressTemplateApi.ExpressTemplate>
|
||||
>('/trade/delivery/express-template/page', { params });
|
||||
}
|
||||
|
||||
/** 查询快递运费模板详情 */
|
||||
export function getDeliveryExpressTemplate(id: number) {
|
||||
return requestClient.get<MallDeliveryExpressTemplateApi.ExpressTemplate>(
|
||||
`/trade/delivery/express-template/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询快递运费模板详情 */
|
||||
export function getSimpleTemplateList() {
|
||||
return requestClient.get<MallDeliveryExpressTemplateApi.SimpleTemplate[]>(
|
||||
'/trade/delivery/express-template/list-all-simple',
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增快递运费模板 */
|
||||
export function createDeliveryExpressTemplate(
|
||||
data: MallDeliveryExpressTemplateApi.ExpressTemplate,
|
||||
) {
|
||||
return requestClient.post('/trade/delivery/express-template/create', data);
|
||||
}
|
||||
|
||||
/** 修改快递运费模板 */
|
||||
export function updateDeliveryExpressTemplate(
|
||||
data: MallDeliveryExpressTemplateApi.ExpressTemplate,
|
||||
) {
|
||||
return requestClient.put('/trade/delivery/express-template/update', data);
|
||||
}
|
||||
|
||||
/** 删除快递运费模板 */
|
||||
export function deleteDeliveryExpressTemplate(id: number) {
|
||||
return requestClient.delete(
|
||||
`/trade/delivery/express-template/delete?id=${id}`,
|
||||
);
|
||||
}
|
@@ -1,94 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallDeliveryPickUpStoreApi {
|
||||
/** 自提门店 */
|
||||
export interface PickUpStore {
|
||||
/** 编号 */
|
||||
id: number;
|
||||
/** 门店名称 */
|
||||
name: string;
|
||||
/** 门店简介 */
|
||||
introduction: string;
|
||||
/** 联系电话 */
|
||||
phone: string;
|
||||
/** 区域编号 */
|
||||
areaId: number;
|
||||
/** 详细地址 */
|
||||
detailAddress: string;
|
||||
/** 门店 logo */
|
||||
logo: string;
|
||||
/** 营业开始时间 */
|
||||
openingTime: string;
|
||||
/** 营业结束时间 */
|
||||
closingTime: string;
|
||||
/** 纬度 */
|
||||
latitude: number;
|
||||
/** 经度 */
|
||||
longitude: number;
|
||||
/** 状态 */
|
||||
status: number;
|
||||
/** 绑定用户编号组数 */
|
||||
verifyUserIds: number[];
|
||||
}
|
||||
|
||||
/** 绑定自提店员请求 */
|
||||
export interface BindStaffRequest {
|
||||
id?: number;
|
||||
/** 门店名称 */
|
||||
name: string;
|
||||
/** 门店编号 */
|
||||
storeId: number;
|
||||
/** 用户编号列表 */
|
||||
userIds: number[];
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询自提门店列表 */
|
||||
export function getDeliveryPickUpStorePage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallDeliveryPickUpStoreApi.PickUpStore>>(
|
||||
'/trade/delivery/pick-up-store/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询自提门店详情 */
|
||||
export function getDeliveryPickUpStore(id: number) {
|
||||
return requestClient.get<MallDeliveryPickUpStoreApi.PickUpStore>(
|
||||
`/trade/delivery/pick-up-store/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询自提门店精简列表 */
|
||||
export function getSimpleDeliveryPickUpStoreList() {
|
||||
return requestClient.get<MallDeliveryPickUpStoreApi.PickUpStore[]>(
|
||||
'/trade/delivery/pick-up-store/simple-list',
|
||||
);
|
||||
}
|
||||
|
||||
/** 新增自提门店 */
|
||||
export function createDeliveryPickUpStore(
|
||||
data: MallDeliveryPickUpStoreApi.PickUpStore,
|
||||
) {
|
||||
return requestClient.post('/trade/delivery/pick-up-store/create', data);
|
||||
}
|
||||
|
||||
/** 修改自提门店 */
|
||||
export function updateDeliveryPickUpStore(
|
||||
data: MallDeliveryPickUpStoreApi.PickUpStore,
|
||||
) {
|
||||
return requestClient.put('/trade/delivery/pick-up-store/update', data);
|
||||
}
|
||||
|
||||
/** 删除自提门店 */
|
||||
export function deleteDeliveryPickUpStore(id: number) {
|
||||
return requestClient.delete(`/trade/delivery/pick-up-store/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 绑定自提店员 */
|
||||
export function bindStoreStaffId(
|
||||
data: MallDeliveryPickUpStoreApi.BindStaffRequest,
|
||||
) {
|
||||
return requestClient.post('/trade/delivery/pick-up-store/bind', data);
|
||||
}
|
@@ -1,298 +0,0 @@
|
||||
import type { PageParam, PageResult } from '../../../types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace MallOrderApi {
|
||||
/** 商品属性 */
|
||||
export interface ProductProperty {
|
||||
/** 属性的编号 */
|
||||
propertyId?: null | number;
|
||||
/** 属性的名称 */
|
||||
propertyName?: string;
|
||||
/** 属性值的编号 */
|
||||
valueId?: null | number;
|
||||
/** 属性值的名称 */
|
||||
valueName?: string;
|
||||
}
|
||||
|
||||
/** 订单项 */
|
||||
export interface OrderItem {
|
||||
/** 编号 */
|
||||
id?: null | number;
|
||||
/** 用户编号 */
|
||||
userId?: null | number;
|
||||
/** 订单编号 */
|
||||
orderId?: null | number;
|
||||
/** 商品 SPU 编号 */
|
||||
spuId?: null | number;
|
||||
/** 商品 SPU 名称 */
|
||||
spuName?: string;
|
||||
/** 商品 SKU 编号 */
|
||||
skuId?: null | number;
|
||||
/** 商品图片 */
|
||||
picUrl?: string;
|
||||
/** 购买数量 */
|
||||
count?: null | number;
|
||||
/** 商品原价(总) */
|
||||
originalPrice?: null | number;
|
||||
/** 商品原价(单) */
|
||||
originalUnitPrice?: null | number;
|
||||
/** 商品优惠(总) */
|
||||
discountPrice?: null | number;
|
||||
/** 商品实付金额(总) */
|
||||
payPrice?: null | number;
|
||||
/** 子订单分摊金额(总) */
|
||||
orderPartPrice?: null | number;
|
||||
/** 分摊后子订单实付金额(总) */
|
||||
orderDividePrice?: null | number;
|
||||
/** 售后状态 */
|
||||
afterSaleStatus?: null | number;
|
||||
/** 属性数组 */
|
||||
properties?: ProductProperty[];
|
||||
}
|
||||
|
||||
/** 订单日志 */
|
||||
export interface OrderLog {
|
||||
/** 日志内容 */
|
||||
content?: string;
|
||||
/** 创建时间 */
|
||||
createTime?: Date;
|
||||
/** 用户类型 */
|
||||
userType?: number;
|
||||
}
|
||||
|
||||
/** 订单 */
|
||||
export interface Order {
|
||||
/** 订单编号 */
|
||||
id?: null | number;
|
||||
/** 订单流水号 */
|
||||
no?: string;
|
||||
/** 下单时间 */
|
||||
createTime?: Date | null;
|
||||
/** 订单类型 */
|
||||
type?: null | number;
|
||||
/** 订单来源 */
|
||||
terminal?: null | number;
|
||||
/** 用户编号 */
|
||||
userId?: null | number;
|
||||
/** 用户 IP */
|
||||
userIp?: string;
|
||||
/** 用户备注 */
|
||||
userRemark?: string;
|
||||
/** 订单状态 */
|
||||
status?: null | number;
|
||||
/** 购买的商品数量 */
|
||||
productCount?: null | number;
|
||||
/** 订单完成时间 */
|
||||
finishTime?: Date | null;
|
||||
/** 订单取消时间 */
|
||||
cancelTime?: Date | null;
|
||||
/** 取消类型 */
|
||||
cancelType?: null | number;
|
||||
/** 商家备注 */
|
||||
remark?: string;
|
||||
/** 支付订单编号 */
|
||||
payOrderId?: null | number;
|
||||
/** 是否已支付 */
|
||||
payStatus?: boolean;
|
||||
/** 付款时间 */
|
||||
payTime?: Date | null;
|
||||
/** 支付渠道 */
|
||||
payChannelCode?: string;
|
||||
/** 商品原价(总) */
|
||||
totalPrice?: null | number;
|
||||
/** 订单优惠(总) */
|
||||
discountPrice?: null | number;
|
||||
/** 运费金额 */
|
||||
deliveryPrice?: null | number;
|
||||
/** 订单调价(总) */
|
||||
adjustPrice?: null | number;
|
||||
/** 应付金额(总) */
|
||||
payPrice?: null | number;
|
||||
/** 发货方式 */
|
||||
deliveryType?: null | number;
|
||||
/** 自提门店编号 */
|
||||
pickUpStoreId?: number;
|
||||
/** 自提核销码 */
|
||||
pickUpVerifyCode?: string;
|
||||
/** 配送模板编号 */
|
||||
deliveryTemplateId?: null | number;
|
||||
/** 发货物流公司编号 */
|
||||
logisticsId?: null | number;
|
||||
/** 发货物流单号 */
|
||||
logisticsNo?: string;
|
||||
/** 发货时间 */
|
||||
deliveryTime?: Date | null;
|
||||
/** 收货时间 */
|
||||
receiveTime?: Date | null;
|
||||
/** 收件人名称 */
|
||||
receiverName?: string;
|
||||
/** 收件人手机 */
|
||||
receiverMobile?: string;
|
||||
/** 收件人邮编 */
|
||||
receiverPostCode?: null | number;
|
||||
/** 收件人地区编号 */
|
||||
receiverAreaId?: null | number;
|
||||
/** 收件人地区名字 */
|
||||
receiverAreaName?: string;
|
||||
/** 收件人详细地址 */
|
||||
receiverDetailAddress?: string;
|
||||
/** 售后状态 */
|
||||
afterSaleStatus?: null | number;
|
||||
/** 退款金额 */
|
||||
refundPrice?: null | number;
|
||||
/** 优惠劵编号 */
|
||||
couponId?: null | number;
|
||||
/** 优惠劵减免金额 */
|
||||
couponPrice?: null | number;
|
||||
/** 积分抵扣的金额 */
|
||||
pointPrice?: null | number;
|
||||
/** VIP 减免金额 */
|
||||
vipPrice?: null | number;
|
||||
/** 订单项列表 */
|
||||
items?: OrderItem[];
|
||||
/** 下单用户信息 */
|
||||
user?: {
|
||||
/** 用户头像 */
|
||||
avatar?: string;
|
||||
/** 用户编号 */
|
||||
id?: null | number;
|
||||
/** 用户昵称 */
|
||||
nickname?: string;
|
||||
};
|
||||
/** 推广用户信息 */
|
||||
brokerageUser?: {
|
||||
/** 用户头像 */
|
||||
avatar?: string;
|
||||
/** 用户编号 */
|
||||
id?: null | number;
|
||||
/** 用户昵称 */
|
||||
nickname?: string;
|
||||
};
|
||||
/** 订单操作日志 */
|
||||
logs?: OrderLog[];
|
||||
}
|
||||
|
||||
/** 交易订单统计 */
|
||||
export interface OrderSummary {
|
||||
/** 订单数量 */
|
||||
orderCount: number;
|
||||
/** 订单金额 */
|
||||
orderPayPrice: number;
|
||||
/** 退款单数 */
|
||||
afterSaleCount: number;
|
||||
/** 退款金额 */
|
||||
afterSalePrice: number;
|
||||
}
|
||||
|
||||
/** 订单发货请求 */
|
||||
export interface DeliveryRequest {
|
||||
/** 订单编号 */
|
||||
id?: number;
|
||||
/** 发货方式 */
|
||||
expressType: string;
|
||||
/** 物流公司编号 */
|
||||
logisticsId: null | number;
|
||||
/** 物流编号 */
|
||||
logisticsNo: string;
|
||||
}
|
||||
|
||||
/** 订单备注请求 */
|
||||
export interface RemarkRequest {
|
||||
/** 订单编号 */
|
||||
id: number;
|
||||
/** 备注 */
|
||||
remark: string;
|
||||
}
|
||||
|
||||
/** 订单调价请求 */
|
||||
export interface PriceRequest {
|
||||
/** 订单编号 */
|
||||
id: number;
|
||||
/** 调整金额,单位:分 */
|
||||
adjustPrice: number;
|
||||
}
|
||||
|
||||
/** 订单地址请求 */
|
||||
export interface AddressRequest {
|
||||
/** 订单编号 */
|
||||
id: number;
|
||||
/** 收件人名称 */
|
||||
receiverName: string;
|
||||
/** 收件人手机 */
|
||||
receiverMobile: string;
|
||||
/** 收件人地区编号 */
|
||||
receiverAreaId: number;
|
||||
/** 收件人详细地址 */
|
||||
receiverDetailAddress: string;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询交易订单列表 */
|
||||
export function getOrderPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<MallOrderApi.Order>>(
|
||||
'/trade/order/page',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询交易订单统计 */
|
||||
export function getOrderSummary(params: PageParam) {
|
||||
return requestClient.get<MallOrderApi.OrderSummary>('/trade/order/summary', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/** 查询交易订单详情 */
|
||||
export function getOrder(id: number) {
|
||||
return requestClient.get<MallOrderApi.Order>(
|
||||
`/trade/order/get-detail?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 查询交易订单物流详情 */
|
||||
export function getExpressTrackList(id: number) {
|
||||
return requestClient.get(`/trade/order/get-express-track-list?id=${id}`);
|
||||
}
|
||||
|
||||
/** 订单发货 */
|
||||
export function deliveryOrder(data: MallOrderApi.DeliveryRequest) {
|
||||
return requestClient.put('/trade/order/delivery', data);
|
||||
}
|
||||
|
||||
/** 订单备注 */
|
||||
export function updateOrderRemark(data: MallOrderApi.RemarkRequest) {
|
||||
return requestClient.put('/trade/order/update-remark', data);
|
||||
}
|
||||
|
||||
/** 订单调价 */
|
||||
export function updateOrderPrice(data: MallOrderApi.PriceRequest) {
|
||||
return requestClient.put('/trade/order/update-price', data);
|
||||
}
|
||||
|
||||
/** 修改订单地址 */
|
||||
export function updateOrderAddress(data: MallOrderApi.AddressRequest) {
|
||||
return requestClient.put('/trade/order/update-address', data);
|
||||
}
|
||||
|
||||
/** 订单核销 */
|
||||
export function pickUpOrder(id: number) {
|
||||
return requestClient.put(`/trade/order/pick-up-by-id?id=${id}`);
|
||||
}
|
||||
|
||||
/** 订单核销 */
|
||||
export function pickUpOrderByVerifyCode(pickUpVerifyCode: string) {
|
||||
return requestClient.put('/trade/order/pick-up-by-verify-code', {
|
||||
params: { pickUpVerifyCode },
|
||||
});
|
||||
}
|
||||
|
||||
/** 查询核销码对应的订单 */
|
||||
export function getOrderByPickUpVerifyCode(pickUpVerifyCode: string) {
|
||||
return requestClient.get<MallOrderApi.Order>(
|
||||
'/trade/order/get-by-pick-up-verify-code',
|
||||
{ params: { pickUpVerifyCode } },
|
||||
);
|
||||
}
|
@@ -240,6 +240,10 @@ function createRequestClient(baseURL: string) {
|
||||
// 不再执行下面逻辑
|
||||
throw new Error(_msg);
|
||||
}
|
||||
case 403: {
|
||||
window.location.href = msg;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (msg) {
|
||||
timeoutMsg = msg;
|
||||
|
@@ -28,7 +28,7 @@ export interface AuthRecordForm extends BaseEntity {
|
||||
/**
|
||||
* 设备ids
|
||||
*/
|
||||
deviceIds?: string[] | number[];
|
||||
deviceIds?: any[];
|
||||
}
|
||||
|
||||
export interface AuthRecordQuery extends PageQuery {
|
||||
|
@@ -1,176 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type {
|
||||
AnalysisOverviewItem,
|
||||
WorkbenchProjectItem,
|
||||
WorkbenchQuickNavItem,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import {
|
||||
AnalysisOverview,
|
||||
Page,
|
||||
WorkbenchQuickNav,
|
||||
} from '@vben/common-ui';
|
||||
import {
|
||||
SvgBellIcon,
|
||||
SvgCakeIcon,
|
||||
SvgCardIcon,
|
||||
SvgDownloadIcon,
|
||||
} from '@vben/icons';
|
||||
import { isString, openWindow } from '@vben/utils';
|
||||
|
||||
import { getUserCountComparison } from '#/api/mall/statistics/member';
|
||||
import { getOrderComparison } from '#/api/mall/statistics/trade';
|
||||
|
||||
/** 商城首页 */
|
||||
defineOptions({ name: 'MallHome' });
|
||||
|
||||
const loading = ref(true); // 加载中
|
||||
const orderComparison = ref(); // 交易对照数据
|
||||
const userComparison = ref(); // 用户对照数据
|
||||
|
||||
/** 查询交易对照卡片数据 */
|
||||
const getOrder = async () => {
|
||||
orderComparison.value = await getOrderComparison();
|
||||
};
|
||||
|
||||
/** 查询会员用户数量对照卡片数据 */
|
||||
const getUserCount = async () => {
|
||||
userComparison.value = await getUserCountComparison();
|
||||
};
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
loading.value = true;
|
||||
await Promise.all([getOrder(), getUserCount()]);
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
const overviewItems: AnalysisOverviewItem[] = [
|
||||
{
|
||||
icon: SvgCardIcon,
|
||||
title: '今日销售额',
|
||||
totalTitle: '昨日数据',
|
||||
totalValue: orderComparison.value?.reference?.orderPayPrice || 0,
|
||||
value: orderComparison.value?.orderPayPrice || 0,
|
||||
},
|
||||
{
|
||||
icon: SvgCakeIcon,
|
||||
title: '今日用户访问量',
|
||||
totalTitle: '总访问量',
|
||||
totalValue: userComparison.value?.reference?.visitUserCount || 0,
|
||||
value: userComparison.value?.visitUserCount || 0,
|
||||
},
|
||||
{
|
||||
icon: SvgDownloadIcon,
|
||||
title: '今日订单量',
|
||||
totalTitle: '总订单量',
|
||||
totalValue: orderComparison.value?.orderPayCount || 0,
|
||||
value: orderComparison.value?.reference?.orderPayCount || 0,
|
||||
},
|
||||
{
|
||||
icon: SvgBellIcon,
|
||||
title: '今日会员注册量',
|
||||
totalTitle: '总会员注册量',
|
||||
totalValue: userComparison.value?.registerUserCount || 0,
|
||||
value: userComparison.value?.reference?.registerUserCount || 0,
|
||||
},
|
||||
];
|
||||
|
||||
// 同样,这里的 url 也可以使用以 http 开头的外部链接
|
||||
const quickNavItems: WorkbenchQuickNavItem[] = [
|
||||
{
|
||||
color: '#1fdaca',
|
||||
icon: 'ep:user-filled',
|
||||
title: '用户管理',
|
||||
url: 'MemberUser',
|
||||
},
|
||||
{
|
||||
color: '#ff6b6b',
|
||||
icon: 'fluent-mdl2:product',
|
||||
title: '商品管理',
|
||||
url: 'ProductSpu',
|
||||
},
|
||||
{
|
||||
color: '#7c3aed',
|
||||
icon: 'ep:list',
|
||||
title: '订单管理',
|
||||
url: 'TradeOrder',
|
||||
},
|
||||
{
|
||||
color: '#3fb27f',
|
||||
icon: 'ri:refund-2-line',
|
||||
title: '售后管理',
|
||||
url: 'TradeAfterSale',
|
||||
},
|
||||
{
|
||||
color: '#4daf1bc9',
|
||||
icon: 'fa-solid:project-diagram',
|
||||
title: '分销管理',
|
||||
url: 'TradeBrokerageUser',
|
||||
},
|
||||
{
|
||||
color: '#1a73e8',
|
||||
icon: 'ep:ticket',
|
||||
title: '优惠券',
|
||||
url: 'PromotionCoupon',
|
||||
},
|
||||
{
|
||||
color: '#4daf1bc9',
|
||||
icon: 'fa:group',
|
||||
title: '拼团活动',
|
||||
url: 'PromotionBargainActivity',
|
||||
},
|
||||
{
|
||||
color: '#1a73e8',
|
||||
icon: 'vaadin:money-withdraw',
|
||||
title: '佣金提现',
|
||||
url: 'TradeBrokerageWithdraw',
|
||||
},
|
||||
{
|
||||
color: '#1a73e8',
|
||||
icon: 'vaadin:money-withdraw',
|
||||
title: '数据统计',
|
||||
url: 'TradeBrokerageWithdraw',
|
||||
},
|
||||
];
|
||||
|
||||
const router = useRouter();
|
||||
function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
|
||||
if (nav.url?.startsWith('http')) {
|
||||
openWindow(nav.url);
|
||||
return;
|
||||
}
|
||||
if (nav.url?.startsWith('/')) {
|
||||
router.push(nav.url).catch((error) => {
|
||||
console.error('Navigation failed:', error);
|
||||
});
|
||||
} else if (isString(nav.url)) {
|
||||
router.push({ name: nav.url });
|
||||
} else {
|
||||
console.warn(`Unknown URL for navigation item: ${nav.title} -> ${nav.url}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<!-- <template #doc>-->
|
||||
<!-- <DocAlert-->
|
||||
<!-- title="商城手册(功能开启)"-->
|
||||
<!-- url="https://doc.iocoder.cn/mall/build/"-->
|
||||
<!-- />-->
|
||||
<!-- </template>-->
|
||||
<AnalysisOverview :items="overviewItems" />
|
||||
<div class="mt-5 w-full lg:w-2/5">
|
||||
<WorkbenchQuickNav
|
||||
:items="quickNavItems"
|
||||
class="mt-5 lg:mt-0"
|
||||
title="快捷导航"
|
||||
@click="navTo"
|
||||
/>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
@@ -1,132 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeGridPropTypes } from '#/adapter/vxe-table';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import {
|
||||
CommonStatusEnum,
|
||||
DICT_TYPE,
|
||||
getDictOptions,
|
||||
getRangePickerDefaultProps,
|
||||
} from '#/utils/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '品牌名称',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'picUrl',
|
||||
label: '品牌图片',
|
||||
component: 'ImageUpload',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '品牌排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入品牌排序',
|
||||
},
|
||||
rules: z.number().min(0).default(1),
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '品牌状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||
},
|
||||
{
|
||||
fieldName: 'description',
|
||||
label: '品牌描述',
|
||||
component: 'Textarea',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '品牌名称',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '品牌状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 表格列配置 */
|
||||
export function useGridColumns(): VxeGridPropTypes.Columns {
|
||||
return [
|
||||
{
|
||||
field: 'name',
|
||||
title: '分类名称',
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
field: 'picUrl',
|
||||
title: '品牌图片',
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'sort',
|
||||
title: '品牌排序',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '开启状态',
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,127 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallBrandApi } from '#/api/mall/product/brand';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteBrand, getBrandPage } from '#/api/mall/product/brand';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建品牌 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑品牌 */
|
||||
function handleEdit(row: MallBrandApi.Brand) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除品牌 */
|
||||
async function handleDelete(row: MallBrandApi.Brand) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteBrand(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getBrandPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallBrandApi.Brand>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="品牌列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['品牌']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['product:brand:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['product:brand:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['product:brand:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,83 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallBrandApi } from '#/api/mall/product/brand';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createBrand, getBrand, updateBrand } from '#/api/mall/product/brand';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallBrandApi.Brand>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['品牌'])
|
||||
: $t('ui.actionTitle.create', ['品牌']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallBrandApi.Brand;
|
||||
try {
|
||||
await (formData.value?.id ? updateBrand(data) : createBrand(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallBrandApi.Brand>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getBrand(data.id as number);
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,139 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallCategoryApi } from '#/api/mall/product/category';
|
||||
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import { getCategoryList } from '#/api/mall/product/category';
|
||||
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'parentId',
|
||||
label: '上级分类',
|
||||
component: 'ApiTreeSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: async () => {
|
||||
const data = await getCategoryList({ parentId: 0 });
|
||||
data.unshift({
|
||||
id: 0,
|
||||
name: '顶级分类',
|
||||
picUrl: '',
|
||||
sort: 0,
|
||||
status: 0,
|
||||
});
|
||||
return handleTree(data);
|
||||
},
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
childrenField: 'children',
|
||||
placeholder: '请选择上级分类',
|
||||
treeDefaultExpandAll: true,
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '分类名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入分类名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'picUrl',
|
||||
label: '移动端分类图',
|
||||
component: 'ImageUpload',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '分类排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入分类排序',
|
||||
},
|
||||
rules: z.number().min(0).default(1),
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '开启状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '分类名称',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<MallCategoryApi.Category>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'name',
|
||||
title: '分类名称',
|
||||
align: 'left',
|
||||
fixed: 'left',
|
||||
treeNode: true,
|
||||
},
|
||||
{
|
||||
field: 'picUrl',
|
||||
title: '移动端分类图',
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'sort',
|
||||
title: '分类排序',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '开启状态',
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 300,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,187 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallCategoryApi } from '#/api/mall/product/category';
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteCategory, getCategoryList } from '#/api/mall/product/category';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建分类 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
}
|
||||
|
||||
/** 添加下级分类 */
|
||||
function handleAppend(row: MallCategoryApi.Category) {
|
||||
formModalApi.setData({ parentId: row.id }).open();
|
||||
}
|
||||
|
||||
/** 编辑分类 */
|
||||
function handleEdit(row: MallCategoryApi.Category) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 查看商品操作 */
|
||||
const router = useRouter(); // 路由
|
||||
const handleViewSpu = (id: number) => {
|
||||
router.push({
|
||||
name: 'ProductSpu',
|
||||
query: { categoryId: id },
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除分类 */
|
||||
async function handleDelete(row: MallCategoryApi.Category) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteCategory(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 切换树形展开/收缩状态 */
|
||||
const isExpanded = ref(false);
|
||||
function toggleExpand() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
gridApi.grid.setAllTreeExpand(isExpanded.value);
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async (_, formValues) => {
|
||||
return await getCategoryList(formValues);
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
},
|
||||
treeConfig: {
|
||||
parentField: 'parentId',
|
||||
rowField: 'id',
|
||||
transform: true,
|
||||
reserve: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallCategoryApi.Category>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【产品】产品管理、产品分类"
|
||||
url="https://doc.iocoder.cn/crm/product/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid>
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['分类']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['product:category:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: isExpanded ? '收缩' : '展开',
|
||||
type: 'primary',
|
||||
onClick: toggleExpand,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #name="{ row }">
|
||||
<div class="flex w-full items-center gap-1">
|
||||
<span class="flex-auto">{{ row.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '新增下级',
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['product:category:create'],
|
||||
onClick: handleAppend.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['product:category:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '查看商品',
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['product:category:update'],
|
||||
ifShow: row.parentId > 0,
|
||||
onClick: handleViewSpu.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['product:category:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,89 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallCategoryApi } from '#/api/mall/product/category';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createCategory,
|
||||
getCategory,
|
||||
updateCategory,
|
||||
} from '#/api/mall/product/category';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<MallCategoryApi.Category>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['产品分类'])
|
||||
: $t('ui.actionTitle.create', ['产品分类']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallCategoryApi.Category;
|
||||
try {
|
||||
await (formData.value?.id ? updateCategory(data) : createCategory(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
let data = modalApi.getData<MallCategoryApi.Category>();
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
if (data.id) {
|
||||
data = await getCategory(data.id as number);
|
||||
}
|
||||
// 设置到 values
|
||||
formData.value = data;
|
||||
await formApi.setValues(data);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,202 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeGridPropTypes } from '#/adapter/vxe-table';
|
||||
import type { MallCommentApi } from '#/api/mall/product/comment';
|
||||
|
||||
import { getSpuSimpleList } from '#/api/mall/product/spu';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'spuId',
|
||||
label: '商品',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSpuSimpleList,
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'userAvatar',
|
||||
label: '用户头像',
|
||||
component: 'ImageUpload',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'userNickname',
|
||||
label: '用户名称',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'content',
|
||||
label: '评论内容',
|
||||
component: 'Textarea',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'descriptionScores',
|
||||
label: '描述星级',
|
||||
component: 'Rate',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'benefitScores',
|
||||
label: '服务星级',
|
||||
component: 'Rate',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'picUrls',
|
||||
label: '评论图片',
|
||||
component: 'ImageUpload',
|
||||
componentProps: {
|
||||
maxNumber: 9,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'replyStatus',
|
||||
label: '回复状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '已回复', value: true },
|
||||
{ label: '未回复', value: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'spuName',
|
||||
label: '商品名称',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'userNickname',
|
||||
label: '用户名称',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'orderId',
|
||||
label: '订单编号',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '评论时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 表格列配置 */
|
||||
export function useGridColumns<T = MallCommentApi.Comment>(
|
||||
onStatusChange?: (
|
||||
newStatus: boolean,
|
||||
row: T,
|
||||
) => PromiseLike<boolean | undefined>,
|
||||
): VxeGridPropTypes.Columns {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '评论编号',
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
field: 'skuPicUrl',
|
||||
title: '商品图片',
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'spuName',
|
||||
title: '商品名称',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'skuProperties',
|
||||
title: '商品属性',
|
||||
minWidth: 200,
|
||||
formatter: ({ cellValue }) => {
|
||||
return cellValue && cellValue.length > 0
|
||||
? cellValue
|
||||
.map((item: any) => `${item.propertyName} : ${item.valueName}`)
|
||||
.join('\n')
|
||||
: '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'userNickname',
|
||||
title: '用户名称',
|
||||
},
|
||||
{
|
||||
field: 'descriptionScores',
|
||||
title: '商品评分',
|
||||
},
|
||||
{
|
||||
field: 'benefitScores',
|
||||
title: '服务评分',
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
title: '评论内容',
|
||||
},
|
||||
{
|
||||
field: 'picUrls',
|
||||
title: '评论图片',
|
||||
cellRender: {
|
||||
name: 'CellImages',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'replyContent',
|
||||
title: '回复内容',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '评论时间',
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'visible',
|
||||
title: '是否展示',
|
||||
align: 'center',
|
||||
cellRender: {
|
||||
attrs: { beforeChange: onStatusChange },
|
||||
name: 'CellSwitch',
|
||||
props: {
|
||||
checkedValue: true,
|
||||
unCheckedValue: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 80,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,156 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallCommentApi } from '#/api/mall/product/comment';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { confirm, DocAlert, Page, prompt, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message, Textarea } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
getCommentPage,
|
||||
replyComment,
|
||||
updateCommentVisible,
|
||||
} from '#/api/mall/product/comment';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建评价 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 回复评价 */
|
||||
function handleReply(row: MallCommentApi.Comment) {
|
||||
prompt({
|
||||
component: () => {
|
||||
return h(Textarea, {});
|
||||
},
|
||||
content: row.content
|
||||
? `用户评论:${row.content}\n请输入回复内容:`
|
||||
: '请输入回复内容:',
|
||||
title: '回复评论',
|
||||
modelPropName: 'value',
|
||||
}).then(async (val) => {
|
||||
if (val) {
|
||||
await replyComment({
|
||||
id: row.id as number,
|
||||
replyContent: val,
|
||||
});
|
||||
onRefresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 更新状态 */
|
||||
async function handleStatusChange(
|
||||
newStatus: boolean,
|
||||
row: MallCommentApi.Comment,
|
||||
): Promise<boolean | undefined> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const text = newStatus ? '展示' : '隐藏';
|
||||
confirm({
|
||||
content: `确认要${text + row.id}评论吗?`,
|
||||
})
|
||||
.then(async () => {
|
||||
// 更新状态
|
||||
const res = await updateCommentVisible({
|
||||
id: row.id as number,
|
||||
visible: newStatus,
|
||||
});
|
||||
if (res) {
|
||||
// 提示并返回成功
|
||||
message.success(`${text}成功`);
|
||||
resolve(true);
|
||||
} else {
|
||||
reject(new Error('操作失败'));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
reject(new Error('取消操作'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(handleStatusChange),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getCommentPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallCommentApi.Comment>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【商品】商品评价"
|
||||
url="https://doc.iocoder.cn/mall/product-comment/"
|
||||
/>
|
||||
</template>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="评论列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['虚拟评论']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['product:comment:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '回复',
|
||||
type: 'link',
|
||||
auth: ['product:comment:update'],
|
||||
onClick: handleReply.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,83 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallCommentApi } from '#/api/mall/product/comment';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createComment, getComment } from '#/api/mall/product/comment';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallCommentApi.Comment>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['品牌'])
|
||||
: $t('ui.actionTitle.create', ['品牌']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallCommentApi.Comment;
|
||||
try {
|
||||
await createComment(data);
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallCommentApi.Comment>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getComment(data.id as number);
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,176 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { getPropertySimpleList } from '#/api/mall/product/property';
|
||||
|
||||
// ============================== 属性 ==============================
|
||||
|
||||
/** 类型新增/修改的表单 */
|
||||
export function usePropertyFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 类型列表的搜索表单 */
|
||||
export function usePropertyGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入名称',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 类型列表的字段 */
|
||||
export function usePropertyGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '编号',
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '名称',
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 160,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// ============================== 值数据 ==============================
|
||||
|
||||
/** 数据新增/修改的表单 */
|
||||
export function useValueFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'propertyId',
|
||||
label: '属性编号',
|
||||
component: 'ApiSelect',
|
||||
componentProps: (values) => {
|
||||
return {
|
||||
api: getPropertySimpleList,
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
disabled: !!values.id,
|
||||
};
|
||||
},
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 字典数据列表搜索表单 */
|
||||
export function useValueGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典数据表格列
|
||||
*/
|
||||
export function useValueGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '编号',
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '属性值名称',
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
field: 'createTime',
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 160,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { DocAlert, Page } from '@vben/common-ui';
|
||||
|
||||
import PropertyGrid from './modules/property-grid.vue';
|
||||
import ValueGrid from './modules/value-grid.vue';
|
||||
|
||||
const searchPropertyId = ref<number>(); // 搜索的属性ID
|
||||
|
||||
function handlePropertyIdSelect(propertyId: number) {
|
||||
searchPropertyId.value = propertyId;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【商品】商品属性"
|
||||
url="https://doc.iocoder.cn/mall/product-property/"
|
||||
/>
|
||||
</template>
|
||||
<div class="flex h-full">
|
||||
<!-- 左侧属性列表 -->
|
||||
<div class="w-1/2 pr-3">
|
||||
<PropertyGrid @select="handlePropertyIdSelect" />
|
||||
</div>
|
||||
<!-- 右侧属性数据列表 -->
|
||||
<div class="w-1/2">
|
||||
<ValueGrid :property-id="searchPropertyId" />
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
@@ -1,88 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallPropertyApi } from '#/api/mall/product/property';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createProperty,
|
||||
getProperty,
|
||||
updateProperty,
|
||||
} from '#/api/mall/product/property';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { usePropertyFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<MallPropertyApi.Property>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['属性'])
|
||||
: $t('ui.actionTitle.create', ['属性']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: usePropertyFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallPropertyApi.Property;
|
||||
try {
|
||||
await (formData.value?.id ? updateProperty(data) : createProperty(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallPropertyApi.Property>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getProperty(data.id as number);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,142 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type {
|
||||
VxeGridListeners,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { MallPropertyApi } from '#/api/mall/product/property';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteProperty, getPropertyPage } from '#/api/mall/product/property';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { usePropertyGridColumns, usePropertyGridFormSchema } from '../data';
|
||||
import PropertyForm from './property-form.vue';
|
||||
|
||||
const emit = defineEmits(['select']);
|
||||
|
||||
const [PropertyFormModal, propertyFormModalApi] = useVbenModal({
|
||||
connectedComponent: PropertyForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建属性 */
|
||||
function handleCreate() {
|
||||
propertyFormModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑属性 */
|
||||
function handleEdit(row: any) {
|
||||
propertyFormModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除属性 */
|
||||
async function handleDelete(row: MallPropertyApi.Property) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteProperty(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 表格事件 */
|
||||
const gridEvents: VxeGridListeners<MallPropertyApi.Property> = {
|
||||
cellClick: ({ row }) => {
|
||||
emit('select', row.id);
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: usePropertyGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: usePropertyGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getPropertyPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isCurrent: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallPropertyApi.Property>,
|
||||
gridEvents,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<PropertyFormModal @success="onRefresh" />
|
||||
|
||||
<Grid table-title="属性列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['属性']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['product:property:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['product:property:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['product:property:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</div>
|
||||
</template>
|
@@ -1,100 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallPropertyApi } from '#/api/mall/product/property';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createPropertyValue,
|
||||
getPropertyValue,
|
||||
updatePropertyValue,
|
||||
} from '#/api/mall/product/property';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useValueFormSchema } from '../data';
|
||||
|
||||
defineOptions({ name: 'MallPropertyValueForm' });
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<MallPropertyApi.PropertyValue>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['属性值'])
|
||||
: $t('ui.actionTitle.create', ['属性值']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useValueFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallPropertyApi.PropertyValue;
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updatePropertyValue(data)
|
||||
: createPropertyValue(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<
|
||||
MallPropertyApi.PropertyValue | { propertyId?: string }
|
||||
>();
|
||||
|
||||
// 如果有ID,表示是编辑
|
||||
if (data && 'id' in data && data.id) {
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getPropertyValue(data.id as number);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
} else if (data && 'propertyId' in data && data.propertyId) {
|
||||
// 新增时,如果传入了propertyId,则需要设置
|
||||
await formApi.setValues({
|
||||
propertyId: data.propertyId,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,151 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallPropertyApi } from '#/api/mall/product/property';
|
||||
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
deletePropertyValue,
|
||||
getPropertyValuePage,
|
||||
} from '#/api/mall/product/property';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useValueGridColumns, useValueGridFormSchema } from '../data';
|
||||
import ValueForm from './value-form.vue';
|
||||
|
||||
const props = defineProps({
|
||||
propertyId: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const [ValueFormModal, valueFormModalApi] = useVbenModal({
|
||||
connectedComponent: ValueForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建字典数据 */
|
||||
function handleCreate() {
|
||||
valueFormModalApi.setData({ propertyId: props.propertyId }).open();
|
||||
}
|
||||
|
||||
/** 编辑字典数据 */
|
||||
function handleEdit(row: MallPropertyApi.PropertyValue) {
|
||||
valueFormModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除字典数据 */
|
||||
async function handleDelete(row: MallPropertyApi.PropertyValue) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deletePropertyValue(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useValueGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useValueGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getPropertyValuePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
propertyId: props.propertyId,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallPropertyApi.PropertyValue>,
|
||||
});
|
||||
|
||||
/** 监听 dictType 变化,重新查询 */
|
||||
watch(
|
||||
() => props.propertyId,
|
||||
() => {
|
||||
if (props.propertyId) {
|
||||
onRefresh();
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full flex-col">
|
||||
<ValueFormModal @success="onRefresh" />
|
||||
|
||||
<Grid table-title="属性值列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['属性值']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['product:property:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['product:property:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['product:property:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</div>
|
||||
</template>
|
@@ -1,117 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallSpuApi } from '#/api/mall/product/spu';
|
||||
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { getCategoryList } from '#/api/mall/product/category';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '商品名称',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'categoryId',
|
||||
label: '商品分类',
|
||||
component: 'ApiTreeSelect',
|
||||
componentProps: {
|
||||
api: async () => {
|
||||
const res = await getCategoryList({});
|
||||
return handleTree(res, 'id', 'parentId', 'children');
|
||||
},
|
||||
fieldNames: { label: 'name', value: 'id', children: 'children' },
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = MallSpuApi.Spu>(
|
||||
onStatusChange?: (
|
||||
newStatus: number,
|
||||
row: T,
|
||||
) => PromiseLike<boolean | undefined>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
type: 'expand',
|
||||
width: 80,
|
||||
slots: { content: 'expand_content' },
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
field: 'id',
|
||||
title: '商品编号',
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '商品名称',
|
||||
fixed: 'left',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'picUrl',
|
||||
title: '商品图片',
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '价格',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'salesCount',
|
||||
title: '销量',
|
||||
},
|
||||
{
|
||||
field: 'stock',
|
||||
title: '库存',
|
||||
},
|
||||
{
|
||||
field: 'sort',
|
||||
title: '排序',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '销售状态',
|
||||
cellRender: {
|
||||
attrs: { beforeChange: onStatusChange },
|
||||
name: 'CellSwitch',
|
||||
props: {
|
||||
checkedValue: 1,
|
||||
checkedChildren: '上架',
|
||||
unCheckedValue: 0,
|
||||
unCheckedChildren: '下架',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 300,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,348 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallSpuApi } from '#/api/mall/product/spu';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { confirm, DocAlert, Page } from '@vben/common-ui';
|
||||
import {
|
||||
downloadFileFromBlobPart,
|
||||
fenToYuan,
|
||||
handleTree,
|
||||
treeToString,
|
||||
} from '@vben/utils';
|
||||
|
||||
import { Descriptions, message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getCategoryList } from '#/api/mall/product/category';
|
||||
import {
|
||||
deleteSpu,
|
||||
exportSpu,
|
||||
getSpuPage,
|
||||
getTabsCount,
|
||||
updateStatus,
|
||||
} from '#/api/mall/product/spu';
|
||||
import { $t } from '#/locales';
|
||||
import { ProductSpuStatusEnum } from '#/utils';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
|
||||
const { push } = useRouter();
|
||||
const tabType = ref(0);
|
||||
|
||||
const categoryList = ref();
|
||||
|
||||
// tabs 数据
|
||||
const tabsData = ref([
|
||||
{
|
||||
name: '出售中',
|
||||
type: 0,
|
||||
count: 0,
|
||||
},
|
||||
{
|
||||
name: '仓库中',
|
||||
type: 1,
|
||||
count: 0,
|
||||
},
|
||||
{
|
||||
name: '已售罄',
|
||||
type: 2,
|
||||
count: 0,
|
||||
},
|
||||
{
|
||||
name: '警戒库存',
|
||||
type: 3,
|
||||
count: 0,
|
||||
},
|
||||
{
|
||||
name: '回收站',
|
||||
type: 4,
|
||||
count: 0,
|
||||
},
|
||||
]);
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 获得每个 Tab 的数量 */
|
||||
async function getTabCount() {
|
||||
const res = await getTabsCount();
|
||||
for (const objName in res) {
|
||||
const index = Number(objName);
|
||||
if (tabsData.value[index]) {
|
||||
tabsData.value[index].count = res[objName] as number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建商品 */
|
||||
function handleCreate() {
|
||||
push({ name: 'ProductSpuAdd' });
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function handleExport() {
|
||||
const data = await exportSpu(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '商品.xls', source: data });
|
||||
}
|
||||
|
||||
/** 编辑商品 */
|
||||
function handleEdit(row: MallSpuApi.Spu) {
|
||||
push({ name: 'ProductSpuEdit', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 删除商品 */
|
||||
async function handleDelete(row: MallSpuApi.Spu) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteSpu(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 添加到仓库 / 回收站的状态 */
|
||||
async function handleStatus02Change(row: MallSpuApi.Spu, newStatus: number) {
|
||||
// 二次确认
|
||||
const text =
|
||||
newStatus === ProductSpuStatusEnum.RECYCLE.status
|
||||
? '加入到回收站'
|
||||
: '恢复到仓库';
|
||||
confirm(`确认要"${row.name}"${text}吗?`)
|
||||
.then(async () => {
|
||||
await updateStatus({ id: row.id as number, status: newStatus });
|
||||
message.success(`${text}成功`);
|
||||
onRefresh();
|
||||
})
|
||||
.catch(() => {
|
||||
message.error(`${text}失败`);
|
||||
});
|
||||
}
|
||||
|
||||
/** 更新状态 */
|
||||
async function handleStatusChange(
|
||||
newStatus: number,
|
||||
row: MallSpuApi.Spu,
|
||||
): Promise<boolean | undefined> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 二次确认
|
||||
const text = row.status ? '上架' : '下架';
|
||||
confirm({
|
||||
content: `确认要${text + row.name}吗?`,
|
||||
})
|
||||
.then(async () => {
|
||||
// 更新状态
|
||||
const res = await updateStatus({
|
||||
id: row.id as number,
|
||||
status: newStatus,
|
||||
});
|
||||
if (res) {
|
||||
// 提示并返回成功
|
||||
message.success(`${text}成功`);
|
||||
resolve(true);
|
||||
} else {
|
||||
reject(new Error('操作失败'));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
reject(new Error('取消操作'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 查看商品详情 */
|
||||
function handleDetail(row: MallSpuApi.Spu) {
|
||||
push({ name: 'ProductSpuDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(handleStatusChange),
|
||||
height: 'auto',
|
||||
cellConfig: {
|
||||
height: 80,
|
||||
},
|
||||
expandConfig: {
|
||||
height: 100,
|
||||
},
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getSpuPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
tabType: tabType.value,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
resizable: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallSpuApi.Spu>,
|
||||
});
|
||||
|
||||
function onChangeTab(key: any) {
|
||||
tabType.value = Number(key);
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getTabCount();
|
||||
getCategoryList({}).then((res) => {
|
||||
categoryList.value = handleTree(res, 'id', 'parentId', 'children');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【商品】商品 SPU 与 SKU"
|
||||
url="https://doc.iocoder.cn/mall/product-spu-sku/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<Grid>
|
||||
<template #top>
|
||||
<Tabs class="border-none" @change="onChangeTab">
|
||||
<Tabs.TabPane
|
||||
v-for="item in tabsData"
|
||||
:key="item.type"
|
||||
:tab="`${item.name} (${item.count})`"
|
||||
/>
|
||||
</Tabs>
|
||||
</template>
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['商品']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['product:spu:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['product:spu:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #expand_content="{ row }">
|
||||
<Descriptions
|
||||
:column="4"
|
||||
class="mt-4"
|
||||
:label-style="{
|
||||
width: '100px',
|
||||
fontWeight: 'bold',
|
||||
fontSize: '14px',
|
||||
}"
|
||||
:content-style="{ width: '100px', fontSize: '14px' }"
|
||||
>
|
||||
<Descriptions.Item label="商品分类">
|
||||
{{ treeToString(categoryList, row.categoryId) }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="商品名称">
|
||||
{{ row.name }}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="市场价">
|
||||
{{ fenToYuan(row.marketPrice) }} 元
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="成本价">
|
||||
{{ fenToYuan(row.costPrice) }} 元
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="浏览量">
|
||||
{{ row.browseCount }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="虚拟销量">
|
||||
{{ row.virtualSalesCount }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['product:spu:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.detail'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.VIEW,
|
||||
onClick: handleDetail.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['product:spu:delete'],
|
||||
ifShow: () => row.type === 4,
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '恢复',
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['product:spu:update'],
|
||||
ifShow: () => row.type === 4,
|
||||
onClick: handleStatus02Change.bind(
|
||||
null,
|
||||
row,
|
||||
ProductSpuStatusEnum.DISABLE.status,
|
||||
),
|
||||
},
|
||||
{
|
||||
label: '回收',
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['product:spu:update'],
|
||||
ifShow: () => row.type !== 4,
|
||||
onClick: handleStatus02Change.bind(
|
||||
null,
|
||||
row,
|
||||
ProductSpuStatusEnum.RECYCLE.status,
|
||||
),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,3 +0,0 @@
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>detail</template>
|
@@ -1,3 +0,0 @@
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>form</template>
|
@@ -1,135 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeGridPropTypes } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '分类名称',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'picUrl',
|
||||
label: '图标地址',
|
||||
component: 'ImageUpload',
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入排序',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '分类名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入分类名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择状态',
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 表格列配置 */
|
||||
export function useGridColumns(): VxeGridPropTypes.Columns {
|
||||
return [
|
||||
{
|
||||
title: '编号',
|
||||
field: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '分类名称',
|
||||
field: 'name',
|
||||
minWidth: 240,
|
||||
},
|
||||
{
|
||||
title: '分类图片',
|
||||
field: 'picUrl',
|
||||
width: 80,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
field: 'status',
|
||||
width: 150,
|
||||
cellRender: {
|
||||
name: 'CellDictTag',
|
||||
props: {
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
field: 'sort',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
field: 'createTime',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,130 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallArticleCategoryApi } from '#/api/mall/promotion/articleCategory';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
deleteArticleCategory,
|
||||
getArticleCategoryPage,
|
||||
} from '#/api/mall/promotion/articleCategory';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建分类 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑分类 */
|
||||
function handleEdit(row: MallArticleCategoryApi.ArticleCategory) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除分类 */
|
||||
async function handleDelete(row: MallArticleCategoryApi.ArticleCategory) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteArticleCategory(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getArticleCategoryPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallArticleCategoryApi.ArticleCategory>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="文章分类列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['文章分类']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['promotion:article-category:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:article-category:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:article-category:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,90 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallArticleCategoryApi } from '#/api/mall/promotion/articleCategory';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createArticleCategory,
|
||||
getArticleCategory,
|
||||
updateArticleCategory,
|
||||
} from '#/api/mall/promotion/articleCategory';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallArticleCategoryApi.ArticleCategory>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['文章分类'])
|
||||
: $t('ui.actionTitle.create', ['文章分类']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data =
|
||||
(await formApi.getValues()) as MallArticleCategoryApi.ArticleCategory;
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateArticleCategory(data)
|
||||
: createArticleCategory(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallArticleCategoryApi.ArticleCategory>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getArticleCategory(data.id as number);
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,210 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeGridPropTypes } from '#/adapter/vxe-table';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import { getSimpleArticleCategoryList } from '#/api/mall/promotion/articleCategory';
|
||||
import {
|
||||
CommonStatusEnum,
|
||||
DICT_TYPE,
|
||||
getDictOptions,
|
||||
getRangePickerDefaultProps,
|
||||
} from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'title',
|
||||
label: '文章标题',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'categoryId',
|
||||
label: '文章分类',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSimpleArticleCategoryList,
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'author',
|
||||
label: '文章作者',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'introduction',
|
||||
label: '文章简介',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'picUrl',
|
||||
label: '文章封面',
|
||||
component: 'ImageUpload',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'recommendHot',
|
||||
label: '是否热门',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'recommendBanner',
|
||||
label: '是否轮播图',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
},
|
||||
{
|
||||
// TODO: 商品关联
|
||||
fieldName: 'spuId',
|
||||
label: '商品关联',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入品牌排序',
|
||||
},
|
||||
rules: z.number().min(0).default(1),
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||
},
|
||||
{
|
||||
fieldName: 'description',
|
||||
label: '文章内容',
|
||||
component: 'RichTextarea',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '文章分类',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSimpleArticleCategoryList,
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'title',
|
||||
label: '文章标题',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 表格列配置 */
|
||||
export function useGridColumns(): VxeGridPropTypes.Columns {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '编号',
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
title: '标题',
|
||||
},
|
||||
{
|
||||
field: 'picUrl',
|
||||
title: '封面',
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'categoryId',
|
||||
title: '分类',
|
||||
},
|
||||
{
|
||||
field: 'browseCount',
|
||||
title: '浏览量',
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
title: '作者',
|
||||
},
|
||||
{
|
||||
field: 'introduction',
|
||||
title: '文章简介',
|
||||
},
|
||||
{
|
||||
field: 'sort',
|
||||
title: '排序',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,127 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallArticleApi } from '#/api/mall/promotion/article';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteArticle, getArticlePage } from '#/api/mall/promotion/article';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建品牌 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑品牌 */
|
||||
function handleEdit(row: MallArticleApi.Article) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除品牌 */
|
||||
async function handleDelete(row: MallArticleApi.Article) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.title]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteArticle(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.title]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getArticlePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallArticleApi.Article>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="文章列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['文章']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['promotion:article:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:article:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:article:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.title]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,87 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallArticleApi } from '#/api/mall/promotion/article';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createArticle,
|
||||
getArticle,
|
||||
updateArticle,
|
||||
} from '#/api/mall/promotion/article';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallArticleApi.Article>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['文章'])
|
||||
: $t('ui.actionTitle.create', ['文章']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallArticleApi.Article;
|
||||
try {
|
||||
await (formData.value?.id ? updateArticle(data) : createArticle(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallArticleApi.Article>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getArticle(data.id as number);
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,175 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeGridPropTypes } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'id',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'title',
|
||||
label: 'Banner标题',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'picUrl',
|
||||
label: '图片地址',
|
||||
component: 'ImageUpload',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'position',
|
||||
label: '定位',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.PROMOTION_BANNER_POSITION, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'url',
|
||||
label: '跳转地址',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入排序',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'memo',
|
||||
label: '描述',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
rows: 4,
|
||||
placeholder: '请输入描述',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'title',
|
||||
label: 'Banner标题',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入Banner标题',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择状态',
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 表格列配置 */
|
||||
export function useGridColumns(): VxeGridPropTypes.Columns {
|
||||
return [
|
||||
{
|
||||
title: 'Banner标题',
|
||||
field: 'title',
|
||||
},
|
||||
{
|
||||
title: '图片',
|
||||
field: 'picUrl',
|
||||
width: 80,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
field: 'status',
|
||||
width: 150,
|
||||
cellRender: {
|
||||
name: 'CellDictTag',
|
||||
props: {
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '定位',
|
||||
field: 'position',
|
||||
width: 150,
|
||||
cellRender: {
|
||||
name: 'CellDictTag',
|
||||
props: {
|
||||
dictType: DICT_TYPE.PROMOTION_BANNER_POSITION,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '跳转地址',
|
||||
field: 'url',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
field: 'createTime',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
field: 'sort',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
field: 'memo',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,127 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallBannerApi } from '#/api/mall/market/banner';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteBanner, getBannerPage } from '#/api/mall/market/banner';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建Banner */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑Banner */
|
||||
function handleEdit(row: MallBannerApi.Banner) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除Banner */
|
||||
async function handleDelete(row: MallBannerApi.Banner) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.title]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteBanner(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.title]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getBannerPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallBannerApi.Banner>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="Banner列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['Banner']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['promotion:banner:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:banner:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:banner:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.title]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,87 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallBannerApi } from '#/api/mall/market/banner';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createBanner,
|
||||
getBanner,
|
||||
updateBanner,
|
||||
} from '#/api/mall/market/banner';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallBannerApi.Banner>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['Banner'])
|
||||
: $t('ui.actionTitle.create', ['Banner']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 100,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallBannerApi.Banner;
|
||||
try {
|
||||
await (formData.value?.id ? updateBanner(data) : createBanner(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallBannerApi.Banner>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getBanner(data.id as number);
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,262 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '活动名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入活动名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'startTime',
|
||||
label: '开始时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择开始时间',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'endTime',
|
||||
label: '结束时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择结束时间',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'bargainFirstPrice',
|
||||
label: '砍价起始价格(元)',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
precision: 2,
|
||||
step: 0.01,
|
||||
placeholder: '请输入砍价起始价格',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'bargainMinPrice',
|
||||
label: '砍价底价(元)',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
precision: 2,
|
||||
step: 0.01,
|
||||
placeholder: '请输入砍价底价',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'stock',
|
||||
label: '活动库存',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 1,
|
||||
placeholder: '请输入活动库存',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'helpMaxCount',
|
||||
label: '助力人数',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 1,
|
||||
placeholder: '请输入助力人数',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'bargainCount',
|
||||
label: '砍价次数',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 1,
|
||||
placeholder: '请输入砍价次数',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'totalLimitCount',
|
||||
label: '购买限制',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 1,
|
||||
placeholder: '请输入购买限制',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'randomMinPrice',
|
||||
label: '最小砍价金额(元)',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
precision: 2,
|
||||
step: 0.01,
|
||||
placeholder: '请输入最小砍价金额',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'randomMaxPrice',
|
||||
label: '最大砍价金额(元)',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
precision: 2,
|
||||
step: 0.01,
|
||||
placeholder: '请输入最大砍价金额',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '活动名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入活动名称',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '活动状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择活动状态',
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '活动编号',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '活动名称',
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
field: 'activityTime',
|
||||
title: '活动时间',
|
||||
minWidth: 210,
|
||||
formatter: ({ row }) => {
|
||||
if (!row.startTime || !row.endTime) return '';
|
||||
return `${formatDate(row.startTime, 'YYYY-MM-DD')} ~ ${formatDate(row.endTime, 'YYYY-MM-DD')}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'picUrl',
|
||||
title: '商品图片',
|
||||
minWidth: 80,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
props: {
|
||||
height: 40,
|
||||
width: 40,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'spuName',
|
||||
title: '商品标题',
|
||||
minWidth: 300,
|
||||
},
|
||||
{
|
||||
field: 'bargainFirstPrice',
|
||||
title: '起始价格',
|
||||
minWidth: 100,
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'bargainMinPrice',
|
||||
title: '砍价底价',
|
||||
minWidth: 100,
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'recordUserCount',
|
||||
title: '总砍价人数',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'recordSuccessUserCount',
|
||||
title: '成功砍价人数',
|
||||
minWidth: 110,
|
||||
},
|
||||
{
|
||||
field: 'helpUserCount',
|
||||
title: '助力人数',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '活动状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'stock',
|
||||
title: '库存',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'totalStock',
|
||||
title: '总库存',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 150,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,178 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallBargainActivityApi } from '#/api/mall/promotion/bargain/bargainActivity';
|
||||
|
||||
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
closeBargainActivity,
|
||||
deleteBargainActivity,
|
||||
getBargainActivityPage,
|
||||
} from '#/api/mall/promotion/bargain/bargainActivity';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
defineOptions({ name: 'PromotionBargainActivity' });
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建砍价活动 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑砍价活动 */
|
||||
function handleEdit(row: MallBargainActivityApi.BargainActivity) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 关闭砍价活动 */
|
||||
async function handleClose(row: MallBargainActivityApi.BargainActivity) {
|
||||
try {
|
||||
await confirm({
|
||||
content: '确认关闭该砍价活动吗?',
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
const hideLoading = message.loading({
|
||||
content: '确认关闭该砍价活动吗?',
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await closeBargainActivity(row.id as number);
|
||||
message.success({
|
||||
content: '关闭成功',
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除砍价活动 */
|
||||
async function handleDelete(row: MallBargainActivityApi.BargainActivity) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteBargainActivity(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getBargainActivityPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallBargainActivityApi.BargainActivity>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【营销】砍价活动"
|
||||
url="https://doc.iocoder.cn/mall/promotion-bargain/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<Grid table-title="砍价活动列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['砍价活动']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['promotion:bargain-activity:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:bargain-activity:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '关闭',
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:bargain-activity:close'],
|
||||
ifShow: row.status === 0,
|
||||
onClick: handleClose.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:bargain-activity:delete'],
|
||||
ifShow: row.status !== 0,
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,92 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallBargainActivityApi } from '#/api/mall/promotion/bargain/bargainActivity';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createBargainActivity,
|
||||
getBargainActivity,
|
||||
updateBargainActivity,
|
||||
} from '#/api/mall/promotion/bargain/bargainActivity';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
defineOptions({ name: 'PromotionBargainActivityForm' });
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallBargainActivityApi.BargainActivity>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['砍价活动'])
|
||||
: $t('ui.actionTitle.create', ['砍价活动']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 120,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data =
|
||||
(await formApi.getValues()) as MallBargainActivityApi.BargainActivity;
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateBargainActivity(data)
|
||||
: createBargainActivity(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallBargainActivityApi.BargainActivity>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getBargainActivity(data.id as number);
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,161 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '砍价状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择砍价状态',
|
||||
clearable: true,
|
||||
options: getDictOptions(
|
||||
DICT_TYPE.PROMOTION_BARGAIN_RECORD_STATUS,
|
||||
'number',
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '编号',
|
||||
minWidth: 50,
|
||||
},
|
||||
{
|
||||
field: 'avatar',
|
||||
title: '用户头像',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
props: {
|
||||
height: 40,
|
||||
width: 40,
|
||||
shape: 'circle',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'nickname',
|
||||
title: '用户昵称',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '发起时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'activity.name',
|
||||
title: '砍价活动',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'activity.bargainMinPrice',
|
||||
title: '最低价',
|
||||
minWidth: 100,
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'bargainPrice',
|
||||
title: '当前价',
|
||||
minWidth: 100,
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'activity.helpMaxCount',
|
||||
title: '总砍价次数',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'helpCount',
|
||||
title: '剩余砍价次数',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '砍价状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PROMOTION_BARGAIN_RECORD_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
title: '结束时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'orderId',
|
||||
title: '订单编号',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 助力列表表格列配置 */
|
||||
export function useHelpGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'userId',
|
||||
title: '用户编号',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'avatar',
|
||||
title: '用户头像',
|
||||
minWidth: 80,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
props: {
|
||||
height: 40,
|
||||
width: 40,
|
||||
shape: 'circle',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'nickname',
|
||||
title: '用户昵称',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'reducePrice',
|
||||
title: '砍价金额',
|
||||
minWidth: 100,
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '助力时间',
|
||||
width: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,83 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallBargainRecordApi } from '#/api/mall/promotion/bargain/bargainRecord';
|
||||
|
||||
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getBargainRecordPage } from '#/api/mall/promotion/bargain/bargainRecord';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import HelpListModal from './modules/list.vue';
|
||||
|
||||
defineOptions({ name: 'PromotionBargainRecord' });
|
||||
|
||||
const [HelpListModalApi, helpListModalApi] = useVbenModal({
|
||||
connectedComponent: HelpListModal,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 查看助力详情 */
|
||||
function handleViewHelp(row: MallBargainRecordApi.BargainRecord) {
|
||||
helpListModalApi.setData({ recordId: row.id }).open();
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getBargainRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallBargainRecordApi.BargainRecord>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【营销】砍价活动"
|
||||
url="https://doc.iocoder.cn/mall/promotion-bargain/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<HelpListModalApi />
|
||||
|
||||
<Grid table-title="砍价记录列表">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '助力',
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.VIEW,
|
||||
auth: ['promotion:bargain-help:query'],
|
||||
onClick: handleViewHelp.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,67 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallBargainHelpApi } from '#/api/mall/promotion/bargain/bargainHelp';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getBargainHelpPage } from '#/api/mall/promotion/bargain/bargainHelp';
|
||||
|
||||
import { useHelpGridColumns } from '../data';
|
||||
|
||||
/** 助力列表 */
|
||||
defineOptions({ name: 'BargainRecordListDialog' });
|
||||
|
||||
const recordId = ref<number>();
|
||||
const getTitle = computed(() => {
|
||||
return `助力列表 - 记录${recordId.value || ''}`;
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
recordId.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 获取传入的记录ID
|
||||
const data = modalApi.getData<{ recordId: number }>();
|
||||
if (data?.recordId) {
|
||||
recordId.value = data.recordId;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useHelpGridColumns(),
|
||||
height: 600,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
return await getBargainHelpPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
recordId: recordId.value,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
},
|
||||
} as VxeTableGridOptions<MallBargainHelpApi.BargainHelp>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Grid class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,238 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
/** 表单配置 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '活动名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入活动名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '活动状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择活动状态',
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'startTime',
|
||||
label: '开始时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '请选择开始时间',
|
||||
showTime: false,
|
||||
valueFormat: 'x',
|
||||
format: 'YYYY-MM-DD',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'endTime',
|
||||
label: '结束时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '请选择结束时间',
|
||||
showTime: false,
|
||||
valueFormat: 'x',
|
||||
format: 'YYYY-MM-DD',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'userSize',
|
||||
label: '用户数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '请输入用户数量',
|
||||
min: 2,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'limitDuration',
|
||||
label: '限制时长',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '请输入限制时长(小时)',
|
||||
min: 0,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'totalLimitCount',
|
||||
label: '总限购数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '请输入总限购数量',
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'singleLimitCount',
|
||||
label: '单次限购数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '请输入单次限购数量',
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'virtualGroup',
|
||||
label: '虚拟成团',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
|
||||
},
|
||||
},
|
||||
{
|
||||
// TODO
|
||||
fieldName: 'spuId',
|
||||
label: '拼团商品',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '活动名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入活动名称',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '活动状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择活动状态',
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '活动编号',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '活动名称',
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
field: 'activityTime',
|
||||
title: '活动时间',
|
||||
minWidth: 210,
|
||||
formatter: ({ row }) => {
|
||||
if (!row.startTime || !row.endTime) return '';
|
||||
return `${formatDate(row.startTime, 'YYYY-MM-DD')} ~ ${formatDate(row.endTime, 'YYYY-MM-DD')}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'picUrl',
|
||||
title: '商品图片',
|
||||
minWidth: 80,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
props: {
|
||||
height: 40,
|
||||
width: 40,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'spuName',
|
||||
title: '商品标题',
|
||||
minWidth: 300,
|
||||
},
|
||||
{
|
||||
field: 'marketPrice',
|
||||
title: '原价',
|
||||
minWidth: 100,
|
||||
formatter: ({ cellValue }) => {
|
||||
return `¥${(cellValue / 100).toFixed(2)}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'combinationPrice',
|
||||
title: '拼团价',
|
||||
minWidth: 100,
|
||||
formatter: ({ row }) => {
|
||||
if (!row.products || row.products.length === 0) return '';
|
||||
const combinationPrice = Math.min(
|
||||
...row.products.map((item: any) => item.combinationPrice),
|
||||
);
|
||||
return `¥${(combinationPrice / 100).toFixed(2)}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'groupCount',
|
||||
title: '开团组数',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'groupSuccessCount',
|
||||
title: '成团组数',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'recordCount',
|
||||
title: '购买次数',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '活动状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,182 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallCombinationActivityApi } from '#/api/mall/promotion/combination/combinationActivity';
|
||||
|
||||
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
closeCombinationActivity,
|
||||
deleteCombinationActivity,
|
||||
getCombinationActivityPage,
|
||||
} from '#/api/mall/promotion/combination/combinationActivity';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import CombinationActivityForm from './modules/form.vue';
|
||||
|
||||
defineOptions({ name: 'PromotionCombinationActivity' });
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: CombinationActivityForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建拼团活动 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑拼团活动 */
|
||||
function handleEdit(row: MallCombinationActivityApi.CombinationActivity) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 关闭拼团活动 */
|
||||
async function handleClose(
|
||||
row: MallCombinationActivityApi.CombinationActivity,
|
||||
) {
|
||||
try {
|
||||
await confirm({
|
||||
content: '确认关闭该拼团活动吗?',
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
const hideLoading = message.loading({
|
||||
content: '关闭中...',
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await closeCombinationActivity(row.id as number);
|
||||
message.success({
|
||||
content: '关闭成功',
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除拼团活动 */
|
||||
async function handleDelete(
|
||||
row: MallCombinationActivityApi.CombinationActivity,
|
||||
) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteCombinationActivity(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getCombinationActivityPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallCombinationActivityApi.CombinationActivity>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【营销】拼团活动"
|
||||
url="https://doc.iocoder.cn/mall/promotion-combination/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<Grid table-title="拼团活动列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['拼团活动']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['promotion:combination-activity:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:combination-activity:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '关闭',
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:combination-activity:close'],
|
||||
ifShow: row.status === 0,
|
||||
onClick: handleClose.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:combination-activity:delete'],
|
||||
ifShow: row.status !== 0,
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,93 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallCombinationActivityApi } from '#/api/mall/promotion/combination/combinationActivity';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
createCombinationActivity,
|
||||
getCombinationActivity,
|
||||
updateCombinationActivity,
|
||||
} from '#/api/mall/promotion/combination/combinationActivity';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
defineOptions({ name: 'CombinationActivityForm' });
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<MallCombinationActivityApi.CombinationActivity>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['拼团活动'])
|
||||
: $t('ui.actionTitle.create', ['拼团活动']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
labelWidth: 100,
|
||||
},
|
||||
wrapperClass: 'grid-cols-2',
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data =
|
||||
(await formApi.getValues()) as MallCombinationActivityApi.CombinationActivity;
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateCombinationActivity(data)
|
||||
: createCombinationActivity(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data =
|
||||
modalApi.getData<MallCombinationActivityApi.CombinationActivity>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getCombinationActivity(data.id as number);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-3/5" :title="getTitle">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,177 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '拼团状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择拼团状态',
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
clearable: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '拼团编号',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'avatar',
|
||||
title: '头像',
|
||||
minWidth: 80,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
props: {
|
||||
height: 40,
|
||||
width: 40,
|
||||
shape: 'circle',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'nickname',
|
||||
title: '昵称',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'headId',
|
||||
title: '开团团长',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'picUrl',
|
||||
title: '拼团商品图',
|
||||
minWidth: 80,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'spuName',
|
||||
title: '拼团商品',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'activityName',
|
||||
title: '拼团活动',
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
field: 'userSize',
|
||||
title: '几人团',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'userCount',
|
||||
title: '参与人数',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '参团时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
title: '结束时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '拼团状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 用户列表表格列配置 */
|
||||
export function useUserGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '编号',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'avatar',
|
||||
title: '用户头像',
|
||||
minWidth: 80,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
props: {
|
||||
height: 40,
|
||||
width: 40,
|
||||
shape: 'circle',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'nickname',
|
||||
title: '用户昵称',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'headId',
|
||||
title: '开团团长',
|
||||
minWidth: 100,
|
||||
formatter: ({ cellValue }) => {
|
||||
return cellValue === 0 ? '团长' : '团员';
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '参团时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
title: '结束时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '拼团状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getCombinationRecordPage } from '#/api/mall/promotion/combination/combinationRecord';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import CombinationUserList from './modules/list.vue';
|
||||
|
||||
defineOptions({ name: 'PromotionCombinationRecord' });
|
||||
|
||||
const [UserListModal, userListModalApi] = useVbenModal({
|
||||
connectedComponent: CombinationUserList,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 查看拼团用户 */
|
||||
function handleViewUsers(row: any) {
|
||||
userListModalApi.setData({ recordId: row.id }).open();
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getCombinationRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【营销】拼团活动"
|
||||
url="https://doc.iocoder.cn/mall/promotion-combination/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<UserListModal />
|
||||
|
||||
<Grid table-title="拼团记录列表">
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '查看成员',
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.VIEW,
|
||||
onClick: handleViewUsers.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,63 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getCombinationRecordPage } from '#/api/mall/promotion/combination/combinationRecord';
|
||||
|
||||
import { useUserGridColumns } from '../data';
|
||||
|
||||
defineOptions({ name: 'CombinationUserList' });
|
||||
|
||||
const headId = ref<number>();
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
headId.value = undefined;
|
||||
return;
|
||||
}
|
||||
const data = modalApi.getData<{ headId: number }>();
|
||||
if (data?.headId) {
|
||||
headId.value = data.headId;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useUserGridColumns(),
|
||||
height: 600,
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
// 暂时返回空数据,待API实现后替换
|
||||
return await getCombinationRecordPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
headId: headId.value,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
} as VxeTableGridOptions,
|
||||
});
|
||||
|
||||
const getTitle = computed(() => {
|
||||
return `拼团成员列表 (拼团ID: ${headId.value || ''})`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Grid class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,129 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
import { discountFormat } from './formatter';
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'nickname',
|
||||
label: '会员昵称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入会员昵称',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '领取时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'nickname',
|
||||
title: '会员昵称',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '优惠券名称',
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
field: 'productScope',
|
||||
title: '类型',
|
||||
minWidth: 110,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'discountType',
|
||||
title: '优惠',
|
||||
minWidth: 110,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PROMOTION_DISCOUNT_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'discountPrice',
|
||||
title: '优惠力度',
|
||||
minWidth: 110,
|
||||
formatter: ({ row }) => {
|
||||
return discountFormat(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'takeType',
|
||||
title: '领取方式',
|
||||
minWidth: 110,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
minWidth: 110,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PROMOTION_COUPON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '领取时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'useTime',
|
||||
title: '使用时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 获取状态选项卡配置 */
|
||||
export function getStatusTabs() {
|
||||
const tabs = [
|
||||
{
|
||||
label: '全部',
|
||||
value: 'all',
|
||||
},
|
||||
];
|
||||
|
||||
// 添加字典状态选项
|
||||
const statusOptions = getDictOptions(DICT_TYPE.PROMOTION_COUPON_STATUS);
|
||||
for (const option of statusOptions) {
|
||||
tabs.push({
|
||||
label: option.label,
|
||||
value: String(option.value),
|
||||
});
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate';
|
||||
|
||||
import { floatToFixed2, formatDate } from '@vben/utils';
|
||||
|
||||
import {
|
||||
CouponTemplateValidityTypeEnum,
|
||||
PromotionDiscountTypeEnum,
|
||||
} from '#/utils';
|
||||
|
||||
// 格式化【优惠金额/折扣】
|
||||
export function discountFormat(row: MallCouponTemplateApi.CouponTemplate) {
|
||||
if (row.discountType === PromotionDiscountTypeEnum.PRICE.type) {
|
||||
return `¥${floatToFixed2(row.discountPrice)}`;
|
||||
}
|
||||
if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
|
||||
return `${row.discountPercent}%`;
|
||||
}
|
||||
return `未知【${row.discountType}】`;
|
||||
}
|
||||
|
||||
// 格式化【领取上限】
|
||||
export function takeLimitCountFormat(
|
||||
row: MallCouponTemplateApi.CouponTemplate,
|
||||
) {
|
||||
if (row.takeLimitCount) {
|
||||
if (row.takeLimitCount === -1) {
|
||||
return '无领取限制';
|
||||
}
|
||||
return `${row.takeLimitCount} 张/人`;
|
||||
} else {
|
||||
return ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化【有效期限】
|
||||
export function validityTypeFormat(row: MallCouponTemplateApi.CouponTemplate) {
|
||||
if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) {
|
||||
return `${formatDate(row.validStartTime)} 至 ${formatDate(row.validEndTime)}`;
|
||||
}
|
||||
if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) {
|
||||
return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`;
|
||||
}
|
||||
return `未知【${row.validityType}】`;
|
||||
}
|
||||
|
||||
// 格式化【totalCount】
|
||||
export function totalCountFormat(row: MallCouponTemplateApi.CouponTemplate) {
|
||||
if (row.totalCount === -1) {
|
||||
return '不限制';
|
||||
}
|
||||
return row.totalCount;
|
||||
}
|
||||
|
||||
// 格式化【剩余数量】
|
||||
export function remainedCountFormat(row: MallCouponTemplateApi.CouponTemplate) {
|
||||
if (row.totalCount === -1) {
|
||||
return '不限制';
|
||||
}
|
||||
return row.totalCount - row.takeCount;
|
||||
}
|
||||
|
||||
// 格式化【最低消费】
|
||||
export function usePriceFormat(row: MallCouponTemplateApi.CouponTemplate) {
|
||||
return `¥${floatToFixed2(row.usePrice)}`;
|
||||
}
|
@@ -1,132 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallCouponApi } from '#/api/mall/promotion/coupon/coupon';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { DocAlert, Page } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { message, TabPane, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
deleteCoupon,
|
||||
getCouponPage,
|
||||
} from '#/api/mall/promotion/coupon/coupon';
|
||||
|
||||
import { getStatusTabs, useGridColumns, useGridFormSchema } from './data';
|
||||
|
||||
defineOptions({ name: 'PromotionCoupon' });
|
||||
|
||||
const activeTab = ref('all');
|
||||
const statusTabs = ref(getStatusTabs());
|
||||
|
||||
/** 删除优惠券 */
|
||||
async function handleDelete(row: MallCouponApi.Coupon) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteCoupon(row.id as number);
|
||||
message.success({
|
||||
content: '回收成功',
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** Tab切换 */
|
||||
function onTabChange(tabName: string) {
|
||||
activeTab.value = tabName;
|
||||
// 设置状态查询参数
|
||||
const formValues = gridApi.formApi.getValues();
|
||||
const status = tabName === 'all' ? undefined : Number(tabName);
|
||||
gridApi.formApi.setValues({ ...formValues, status });
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
const params = {
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
// Tab状态过滤
|
||||
status:
|
||||
activeTab.value === 'all' ? undefined : Number(activeTab.value),
|
||||
};
|
||||
return await getCouponPage(params);
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallCouponApi.Coupon>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【营销】优惠劵"
|
||||
url="https://doc.iocoder.cn/mall/promotion-coupon/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<Grid table-title="优惠券列表">
|
||||
<template #top>
|
||||
<Tabs v-model:active-key="activeTab" type="card" @change="onTabChange">
|
||||
<TabPane
|
||||
v-for="tab in statusTabs"
|
||||
:key="tab.value"
|
||||
:tab="tab.label"
|
||||
/>
|
||||
</Tabs>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '回收',
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:coupon:delete'],
|
||||
popConfirm: {
|
||||
title:
|
||||
'回收将会收回会员领取的待使用的优惠券,已使用的将无法回收,确定要回收所选优惠券吗?',
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,252 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
// 格式化函数移到组件内部实现
|
||||
import { z } from '#/adapter/form';
|
||||
import {
|
||||
CommonStatusEnum,
|
||||
DICT_TYPE,
|
||||
getDictOptions,
|
||||
getRangePickerDefaultProps,
|
||||
} from '#/utils';
|
||||
|
||||
import {
|
||||
discountFormat,
|
||||
remainedCountFormat,
|
||||
takeLimitCountFormat,
|
||||
totalCountFormat,
|
||||
validityTypeFormat,
|
||||
} from '../formatter';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '优惠券名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入优惠券名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'description',
|
||||
label: '优惠券描述',
|
||||
component: 'Textarea',
|
||||
},
|
||||
// TODO
|
||||
{
|
||||
fieldName: 'productScope',
|
||||
label: '优惠类型',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE, 'number'),
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'takeType',
|
||||
label: '领取方式',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择领取方式',
|
||||
options: getDictOptions(DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE, 'number'),
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'validityType',
|
||||
label: '有效期类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择有效期类型',
|
||||
options: getDictOptions(
|
||||
DICT_TYPE.PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE,
|
||||
'number',
|
||||
),
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'totalCount',
|
||||
label: '发放数量',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
placeholder: '请输入发放数量',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'takeLimitCount',
|
||||
label: '领取上限',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
placeholder: '请输入领取上限',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '优惠券状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '优惠券名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入优惠券名称',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'discountType',
|
||||
label: '优惠类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择优惠类型',
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.PROMOTION_DISCOUNT_TYPE, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '优惠券状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择优惠券状态',
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{ type: 'checkbox', width: 40 },
|
||||
{
|
||||
field: 'name',
|
||||
title: '优惠券名称',
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
field: 'productScope',
|
||||
title: '类型',
|
||||
minWidth: 130,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'discountType',
|
||||
title: '优惠',
|
||||
minWidth: 110,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PROMOTION_DISCOUNT_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'discountPrice',
|
||||
title: '优惠力度',
|
||||
minWidth: 110,
|
||||
formatter: ({ row }) => {
|
||||
return discountFormat(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'takeType',
|
||||
title: '领取方式',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'validityType',
|
||||
title: '使用时间',
|
||||
minWidth: 180,
|
||||
formatter: ({ row }) => {
|
||||
return validityTypeFormat(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'totalCount',
|
||||
title: '发放数量',
|
||||
minWidth: 100,
|
||||
formatter: ({ row }) => {
|
||||
return totalCountFormat(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remainedCount',
|
||||
title: '剩余数量',
|
||||
minWidth: 100,
|
||||
formatter: ({ row }) => {
|
||||
return remainedCountFormat(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'takeLimitCount',
|
||||
title: '领取上限',
|
||||
minWidth: 100,
|
||||
formatter: ({ row }) => {
|
||||
return takeLimitCountFormat(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
minWidth: 100,
|
||||
slots: { default: 'status' },
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,190 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { message, Switch } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
deleteCouponTemplate,
|
||||
getCouponTemplatePage,
|
||||
updateCouponTemplateStatus,
|
||||
} from '#/api/mall/promotion/coupon/couponTemplate';
|
||||
import { CommonStatusEnum } from '#/utils';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
defineOptions({ name: 'PromotionCouponTemplate' });
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 编辑优惠券模板 */
|
||||
function handleEdit(row: MallCouponTemplateApi.CouponTemplate) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 创建优惠券模板 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 删除优惠券模板 */
|
||||
async function handleDelete(row: MallCouponTemplateApi.CouponTemplate) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteCouponTemplate(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const checkedIds = ref<number[]>([]);
|
||||
function handleRowCheckboxChange({
|
||||
records,
|
||||
}: {
|
||||
records: MallCouponTemplateApi.CouponTemplate[];
|
||||
}) {
|
||||
checkedIds.value = records.map((item) => item.id as number);
|
||||
}
|
||||
|
||||
/** 优惠券模板状态修改 */
|
||||
async function handleStatusChange(row: MallCouponTemplateApi.CouponTemplate) {
|
||||
const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用';
|
||||
const hideLoading = message.loading({
|
||||
content: `正在${text}优惠券模板...`,
|
||||
key: 'status_key_msg',
|
||||
});
|
||||
try {
|
||||
await updateCouponTemplateStatus(row.id as number, row.status as number);
|
||||
message.success({
|
||||
content: `${text}成功`,
|
||||
key: 'status_key_msg',
|
||||
});
|
||||
} catch {
|
||||
// 异常时,需要将 row.status 状态重置回之前的
|
||||
row.status =
|
||||
row.status === CommonStatusEnum.ENABLE
|
||||
? CommonStatusEnum.DISABLE
|
||||
: CommonStatusEnum.ENABLE;
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getCouponTemplatePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallCouponTemplateApi.CouponTemplate>,
|
||||
gridEvents: {
|
||||
checkboxAll: handleRowCheckboxChange,
|
||||
checkboxChange: handleRowCheckboxChange,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【营销】优惠劵"
|
||||
url="https://doc.iocoder.cn/mall/promotion-coupon/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="优惠券列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['优惠券模板']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['promotion:coupon-template:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<Switch
|
||||
v-model:checked="row.status"
|
||||
:checked-value="CommonStatusEnum.ENABLE"
|
||||
:un-checked-value="CommonStatusEnum.DISABLE"
|
||||
@change="handleStatusChange(row)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:coupon-template:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:coupon-template:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,89 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createCouponTemplate,
|
||||
getCouponTemplate,
|
||||
updateCouponTemplate,
|
||||
} from '#/api/mall/promotion/coupon/couponTemplate';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<MallCouponTemplateApi.CouponTemplate>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['优惠券模板'])
|
||||
: $t('ui.actionTitle.create', ['优惠券模板']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data =
|
||||
(await formApi.getValues()) as MallCouponTemplateApi.CouponTemplate;
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateCouponTemplate(data)
|
||||
: createCouponTemplate(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallCouponTemplateApi.CouponTemplate>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getCouponTemplate(data.id as number);
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,159 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { formatDate } from '@vben/utils';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
/** 表单配置 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '活动名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入活动名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '活动状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择活动状态',
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'startTime',
|
||||
label: '开始时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '请选择开始时间',
|
||||
showTime: false,
|
||||
valueFormat: 'x',
|
||||
format: 'YYYY-MM-DD',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'endTime',
|
||||
label: '结束时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '请选择结束时间',
|
||||
showTime: false,
|
||||
valueFormat: 'x',
|
||||
format: 'YYYY-MM-DD',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 4,
|
||||
},
|
||||
},
|
||||
// TODO
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '活动名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入活动名称',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '活动状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择活动状态',
|
||||
clearable: true,
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'activeTime',
|
||||
label: '活动时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
clearable: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '活动编号',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '活动名称',
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
field: 'activityTime',
|
||||
title: '活动时间',
|
||||
minWidth: 210,
|
||||
formatter: ({ row }) => {
|
||||
if (!row.startTime || !row.endTime) return '';
|
||||
return `${formatDate(row.startTime, 'YYYY-MM-DD')} ~ ${formatDate(row.endTime, 'YYYY-MM-DD')}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '活动状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 150,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,178 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallDiscountActivityApi } from '#/api/mall/promotion/discount/discountActivity';
|
||||
|
||||
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
closeDiscountActivity,
|
||||
deleteDiscountActivity,
|
||||
getDiscountActivityPage,
|
||||
} from '#/api/mall/promotion/discount/discountActivity';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import DiscountActivityForm from './modules/form.vue';
|
||||
|
||||
defineOptions({ name: 'PromotionDiscountActivity' });
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: DiscountActivityForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建满减活动 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑满减活动 */
|
||||
function handleEdit(row: MallDiscountActivityApi.DiscountActivity) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 关闭满减活动 */
|
||||
async function handleClose(row: MallDiscountActivityApi.DiscountActivity) {
|
||||
try {
|
||||
await confirm({
|
||||
content: '确认关闭该限时折扣活动吗?',
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
const hideLoading = message.loading({
|
||||
content: '正在关闭中',
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await closeDiscountActivity(row.id as number);
|
||||
message.success({
|
||||
content: '关闭成功',
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除满减活动 */
|
||||
async function handleDelete(row: MallDiscountActivityApi.DiscountActivity) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDiscountActivity(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_key_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getDiscountActivityPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallDiscountActivityApi.DiscountActivity>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【营销】限时折扣"
|
||||
url="https://doc.iocoder.cn/mall/promotion-discount/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<Grid table-title="限时折扣活动列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['限时折扣活动']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['promotion:discount-activity:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:discount-activity:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '关闭',
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:discount-activity:close'],
|
||||
ifShow: row.status === 0,
|
||||
onClick: handleClose.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:discount-activity:delete'],
|
||||
ifShow: row.status !== 0,
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,98 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallDiscountActivityApi } from '#/api/mall/promotion/discount/discountActivity';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
createDiscountActivity,
|
||||
getDiscountActivity,
|
||||
updateDiscountActivity,
|
||||
} from '#/api/mall/promotion/discount/discountActivity';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
defineOptions({ name: 'DiscountActivityForm' });
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<MallDiscountActivityApi.DiscountActivity>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['限时折扣活动'])
|
||||
: $t('ui.actionTitle.create', ['限时折扣活动']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
labelWidth: 100,
|
||||
},
|
||||
wrapperClass: 'grid-cols-2',
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data =
|
||||
(await formApi.getValues()) as MallDiscountActivityApi.DiscountActivity;
|
||||
|
||||
// 确保必要的默认值
|
||||
if (!data.products) {
|
||||
data.products = [];
|
||||
}
|
||||
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateDiscountActivity(data)
|
||||
: createDiscountActivity(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallDiscountActivityApi.DiscountActivity>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getDiscountActivity(data.id as number);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-3/5" :title="getTitle">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,109 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
/** 表单配置 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '页面名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入页面名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'previewPicUrls',
|
||||
component: 'ImageUpload',
|
||||
label: '预览图',
|
||||
componentProps: {
|
||||
maxNumber: 10,
|
||||
multiple: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '页面名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入页面名称',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
clearable: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '编号',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'previewPicUrls',
|
||||
title: '预览图',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellImages',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '页面名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,141 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallDiyPageApi } from '#/api/mall/promotion/diy/page';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deleteDiyPage, getDiyPagePage } from '#/api/mall/promotion/diy/page';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import DiyPageForm from './modules/form.vue';
|
||||
|
||||
defineOptions({ name: 'PromotionDiyPage' });
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: DiyPageForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建DIY页面 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑DIY页面 */
|
||||
function handleEdit(row: MallDiyPageApi.DiyPage) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 装修页面 */
|
||||
function handleDecorate(row: MallDiyPageApi.DiyPage) {
|
||||
// 跳转到装修页面
|
||||
push({ name: 'DiyPageDecorate', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 删除DIY页面 */
|
||||
async function handleDelete(row: MallDiyPageApi.DiyPage) {
|
||||
await deleteDiyPage(row.id as number);
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getDiyPagePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallDiyPageApi.DiyPage>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【营销】商城装修"
|
||||
url="https://doc.iocoder.cn/mall/diy/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<Grid table-title="装修页面列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['装修页面']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['promotion:diy-page:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '装修',
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:diy-page:update'],
|
||||
onClick: handleDecorate.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:diy-page:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:diy-page:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,92 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallDiyPageApi } from '#/api/mall/promotion/diy/page';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
createDiyPage,
|
||||
getDiyPage,
|
||||
updateDiyPage,
|
||||
} from '#/api/mall/promotion/diy/page';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<MallDiyPageApi.DiyPage>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['装修页面'])
|
||||
: $t('ui.actionTitle.create', ['装修页面']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
labelWidth: 100,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallDiyPageApi.DiyPage;
|
||||
|
||||
// 确保必要的默认值
|
||||
if (!data.previewPicUrls) {
|
||||
data.previewPicUrls = [];
|
||||
}
|
||||
|
||||
try {
|
||||
await (formData.value?.id ? updateDiyPage(data) : createDiyPage(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallDiyPageApi.DiyPage>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getDiyPage(data.id as number);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,120 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
/** 表单配置 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '模板名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入模板名称',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'previewPicUrls',
|
||||
component: 'ImageUpload',
|
||||
label: '预览图',
|
||||
componentProps: {
|
||||
maxNumber: 10,
|
||||
multiple: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '模板名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入模板名称',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
clearable: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '编号',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'previewPicUrls',
|
||||
title: '预览图',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellImages',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '模板名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'used',
|
||||
title: '是否使用',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 250,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
@@ -1,167 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { MallDiyTemplateApi } from '#/api/mall/promotion/diy/template';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
deleteDiyTemplate,
|
||||
getDiyTemplatePage,
|
||||
useDiyTemplate,
|
||||
} from '#/api/mall/promotion/diy/template';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import DiyTemplateForm from './modules/form.vue';
|
||||
|
||||
defineOptions({ name: 'PromotionDiyTemplate' });
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: DiyTemplateForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建DIY模板 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑DIY模板 */
|
||||
function handleEdit(row: MallDiyTemplateApi.DiyTemplate) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 装修模板 */
|
||||
function handleDecorate(row: MallDiyTemplateApi.DiyTemplate) {
|
||||
// 跳转到装修页面
|
||||
router.push({ name: 'DiyTemplateDecorate', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 使用模板 */
|
||||
async function handleUse(row: MallDiyTemplateApi.DiyTemplate) {
|
||||
confirm({
|
||||
content: `是否使用模板"${row.name}"?`,
|
||||
}).then(async () => {
|
||||
// 发起删除
|
||||
await useDiyTemplate(row.id as number);
|
||||
message.success('使用成功');
|
||||
onRefresh();
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除DIY模板 */
|
||||
async function handleDelete(row: MallDiyTemplateApi.DiyTemplate) {
|
||||
await deleteDiyTemplate(row.id as number);
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getDiyTemplatePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<MallDiyTemplateApi.DiyTemplate>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【营销】商城装修"
|
||||
url="https://doc.iocoder.cn/mall/diy/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<Grid table-title="装修模板列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['装修模板']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['promotion:diy-template:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '装修',
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:diy-template:update'],
|
||||
onClick: handleDecorate.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.edit'),
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['promotion:diy-template:update'],
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '使用',
|
||||
type: 'link' as const,
|
||||
auth: ['promotion:diy-template:use'],
|
||||
ifShow: !row.used,
|
||||
onClick: handleUse.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
type: 'link' as const,
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['promotion:diy-template:delete'],
|
||||
ifShow: !row.used,
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
@@ -1,99 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MallDiyTemplateApi } from '#/api/mall/promotion/diy/template';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
createDiyTemplate,
|
||||
getDiyTemplate,
|
||||
updateDiyTemplate,
|
||||
} from '#/api/mall/promotion/diy/template';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const formData = ref<MallDiyTemplateApi.DiyTemplate>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['装修模板'])
|
||||
: $t('ui.actionTitle.create', ['装修模板']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
labelWidth: 100,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as MallDiyTemplateApi.DiyTemplate;
|
||||
|
||||
// 确保必要的默认值
|
||||
if (!data.previewPicUrls) {
|
||||
data.previewPicUrls = [];
|
||||
}
|
||||
if (data.used === undefined) {
|
||||
data.used = false;
|
||||
}
|
||||
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateDiyTemplate(data)
|
||||
: createDiyTemplate(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<MallDiyTemplateApi.DiyTemplate>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getDiyTemplate(data.id as number);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-2/5" :title="getTitle">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
@@ -1,28 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mall/promotion/kefu/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mall/promotion/kefu/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
@@ -1,140 +0,0 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
/** 表单配置 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'spuId',
|
||||
label: '积分商城活动商品',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请选择商品',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'sort',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '请输入排序',
|
||||
min: 0,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
rows: 4,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '活动状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择活动状态',
|
||||
clearable: true,
|
||||
dictType: DICT_TYPE.COMMON_STATUS,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '活动编号',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'picUrl',
|
||||
title: '商品图片',
|
||||
minWidth: 80,
|
||||
cellRender: {
|
||||
name: 'CellImage',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'spuName',
|
||||
title: '商品标题',
|
||||
minWidth: 300,
|
||||
},
|
||||
{
|
||||
field: 'marketPrice',
|
||||
title: '原价',
|
||||
minWidth: 100,
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'point',
|
||||
title: '兑换积分',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '兑换金额',
|
||||
minWidth: 100,
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '活动状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'stock',
|
||||
title: '库存',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'totalStock',
|
||||
title: '总库存',
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'redeemedQuantity',
|
||||
title: '已兑换数量',
|
||||
minWidth: 100,
|
||||
slots: { default: 'redeemedQuantity' },
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 150,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user