forked from gxwebsoft/mp-10550
feat(shop): 添加文章详情页面并优化购物车样式
- 新增文章详情页面组件,用于显示文章内容 - 优化购物车页面样式,增加空购物车状态的透明背景 - 添加多个购物相关 API 接口,包括优惠券、订单等 - 更新环境配置,修改 API 基础 URL - 调整发现页面布局,增加文章详情入口
This commit is contained in:
101
src/api/shop/shopArticle/index.ts
Normal file
101
src/api/shop/shopArticle/index.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
import type { ApiResult, PageResult } from '@/api/index';
|
||||||
|
import type { ShopArticle, ShopArticleParam } from './model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询商品文章
|
||||||
|
*/
|
||||||
|
export async function pageShopArticle(params: ShopArticleParam) {
|
||||||
|
const res = await request.get<ApiResult<PageResult<ShopArticle>>>(
|
||||||
|
'/shop/shop-article/page',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询商品文章列表
|
||||||
|
*/
|
||||||
|
export async function listShopArticle(params?: ShopArticleParam) {
|
||||||
|
const res = await request.get<ApiResult<ShopArticle[]>>(
|
||||||
|
'/shop/shop-article',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加商品文章
|
||||||
|
*/
|
||||||
|
export async function addShopArticle(data: ShopArticle) {
|
||||||
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
|
'/shop/shop-article',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改商品文章
|
||||||
|
*/
|
||||||
|
export async function updateShopArticle(data: ShopArticle) {
|
||||||
|
const res = await request.put<ApiResult<unknown>>(
|
||||||
|
'/shop/shop-article',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除商品文章
|
||||||
|
*/
|
||||||
|
export async function removeShopArticle(id?: number) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/shop/shop-article/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除商品文章
|
||||||
|
*/
|
||||||
|
export async function removeBatchShopArticle(data: (number | undefined)[]) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/shop/shop-article/batch',
|
||||||
|
{
|
||||||
|
data
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询商品文章
|
||||||
|
*/
|
||||||
|
export async function getShopArticle(id: number) {
|
||||||
|
const res = await request.get<ApiResult<ShopArticle>>(
|
||||||
|
'/shop/shop-article/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
125
src/api/shop/shopArticle/model/index.ts
Normal file
125
src/api/shop/shopArticle/model/index.ts
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import type { PageParam } from '@/api/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品文章
|
||||||
|
*/
|
||||||
|
export interface ShopArticle {
|
||||||
|
// 文章ID
|
||||||
|
articleId?: number;
|
||||||
|
// 文章标题
|
||||||
|
title?: string;
|
||||||
|
// 文章类型 0常规 1视频
|
||||||
|
type?: number;
|
||||||
|
// 模型
|
||||||
|
model?: string;
|
||||||
|
// 详情页模板
|
||||||
|
detail?: string;
|
||||||
|
// 文章分类ID
|
||||||
|
categoryId?: number;
|
||||||
|
// 上级id, 0是顶级
|
||||||
|
parentId?: number;
|
||||||
|
// 话题
|
||||||
|
topic?: string;
|
||||||
|
// 标签
|
||||||
|
tags?: string;
|
||||||
|
// 封面图
|
||||||
|
image?: string;
|
||||||
|
// 封面图宽
|
||||||
|
imageWidth?: number;
|
||||||
|
// 封面图高
|
||||||
|
imageHeight?: number;
|
||||||
|
// 付费金额
|
||||||
|
price?: string;
|
||||||
|
// 开始时间
|
||||||
|
startTime?: string;
|
||||||
|
// 结束时间
|
||||||
|
endTime?: string;
|
||||||
|
// 来源
|
||||||
|
source?: string;
|
||||||
|
// 产品概述
|
||||||
|
overview?: string;
|
||||||
|
// 虚拟阅读量(仅用作展示)
|
||||||
|
virtualViews?: number;
|
||||||
|
// 实际阅读量
|
||||||
|
actualViews?: number;
|
||||||
|
// 评分
|
||||||
|
rate?: string;
|
||||||
|
// 列表显示方式(10小图展示 20大图展示)
|
||||||
|
showType?: number;
|
||||||
|
// 访问密码
|
||||||
|
password?: string;
|
||||||
|
// 可见类型 0所有人 1登录可见 2密码可见
|
||||||
|
permission?: number;
|
||||||
|
// 发布来源客户端 (APP、H5、小程序等)
|
||||||
|
platform?: string;
|
||||||
|
// 文章附件
|
||||||
|
files?: string;
|
||||||
|
// 视频地址
|
||||||
|
video?: string;
|
||||||
|
// 接受的文件类型
|
||||||
|
accept?: string;
|
||||||
|
// 经度
|
||||||
|
longitude?: string;
|
||||||
|
// 纬度
|
||||||
|
latitude?: string;
|
||||||
|
// 所在省份
|
||||||
|
province?: string;
|
||||||
|
// 所在城市
|
||||||
|
city?: string;
|
||||||
|
// 所在辖区
|
||||||
|
region?: string;
|
||||||
|
// 街道地址
|
||||||
|
address?: string;
|
||||||
|
// 点赞数
|
||||||
|
likes?: number;
|
||||||
|
// 评论数
|
||||||
|
commentNumbers?: number;
|
||||||
|
// 提醒谁看
|
||||||
|
toUsers?: string;
|
||||||
|
// 作者
|
||||||
|
author?: string;
|
||||||
|
// 推荐
|
||||||
|
recommend?: number;
|
||||||
|
// 报名人数
|
||||||
|
bmUsers?: number;
|
||||||
|
// 用户ID
|
||||||
|
userId?: number;
|
||||||
|
// 商户ID
|
||||||
|
merchantId?: number;
|
||||||
|
// 项目ID
|
||||||
|
projectId?: number;
|
||||||
|
// 语言
|
||||||
|
lang?: string;
|
||||||
|
// 关联默认语言的文章ID
|
||||||
|
langArticleId?: number;
|
||||||
|
// 是否自动翻译
|
||||||
|
translation?: string;
|
||||||
|
// 编辑器类型 0 Markdown编辑器 1 富文本编辑器
|
||||||
|
editor?: string;
|
||||||
|
// pdf文件地址
|
||||||
|
pdfUrl?: string;
|
||||||
|
// 版本号
|
||||||
|
version?: number;
|
||||||
|
// 排序(数字越小越靠前)
|
||||||
|
sortNumber?: number;
|
||||||
|
// 备注
|
||||||
|
comments?: string;
|
||||||
|
// 状态, 0已发布, 1待审核 2已驳回 3违规内容
|
||||||
|
status?: number;
|
||||||
|
// 是否删除, 0否, 1是
|
||||||
|
deleted?: number;
|
||||||
|
// 租户id
|
||||||
|
tenantId?: number;
|
||||||
|
// 创建时间
|
||||||
|
createTime?: string;
|
||||||
|
// 修改时间
|
||||||
|
updateTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品文章搜索条件
|
||||||
|
*/
|
||||||
|
export interface ShopArticleParam extends PageParam {
|
||||||
|
articleId?: number;
|
||||||
|
keywords?: string;
|
||||||
|
}
|
||||||
4
src/shop/shopArticle/index.config.ts
Normal file
4
src/shop/shopArticle/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '商品文章管理',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
151
src/shop/shopArticle/index.tsx
Normal file
151
src/shop/shopArticle/index.tsx
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import {useState} from "react";
|
||||||
|
import Taro, {useDidShow} from '@tarojs/taro'
|
||||||
|
import {Button, Cell, CellGroup, Space, Empty, ConfigProvider, Divider} from '@nutui/nutui-react-taro'
|
||||||
|
import {Dongdong, ArrowRight, CheckNormal, Checked} from '@nutui/icons-react-taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
|
||||||
|
import {listShopUserAddress, removeShopUserAddress, updateShopUserAddress} from "@/api/shop/shopUserAddress";
|
||||||
|
|
||||||
|
const Address = () => {
|
||||||
|
const [list, setList] = useState<ShopUserAddress[]>([])
|
||||||
|
const [address, setAddress] = useState<ShopUserAddress>()
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
listShopUserAddress({
|
||||||
|
userId: Taro.getStorageSync('UserId')
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
setList(data || [])
|
||||||
|
// 默认地址
|
||||||
|
setAddress(data.find(item => item.isDefault))
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '获取地址失败',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDefault = async (item: ShopUserAddress) => {
|
||||||
|
if (address) {
|
||||||
|
await updateShopUserAddress({
|
||||||
|
...address,
|
||||||
|
isDefault: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
await updateShopUserAddress({
|
||||||
|
id: item.id,
|
||||||
|
isDefault: true
|
||||||
|
})
|
||||||
|
Taro.showToast({
|
||||||
|
title: '设置成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDel = async (id?: number) => {
|
||||||
|
await removeShopUserAddress(id)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectAddress = async (item: ShopUserAddress) => {
|
||||||
|
if (address) {
|
||||||
|
await updateShopUserAddress({
|
||||||
|
...address,
|
||||||
|
isDefault: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
await updateShopUserAddress({
|
||||||
|
id: item.id,
|
||||||
|
isDefault: true
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
Taro.navigateBack()
|
||||||
|
},500)
|
||||||
|
}
|
||||||
|
|
||||||
|
useDidShow(() => {
|
||||||
|
reload()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (list.length == 0) {
|
||||||
|
return (
|
||||||
|
<ConfigProvider>
|
||||||
|
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
||||||
|
height: 'calc(100vh - 300px)',
|
||||||
|
}}>
|
||||||
|
<Empty
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
description="您还没有地址哦"
|
||||||
|
/>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={() => Taro.navigateTo({url: '/user/address/add'})}>新增地址</Button>
|
||||||
|
<Button type="success" fill="dashed"
|
||||||
|
onClick={() => Taro.navigateTo({url: '/user/address/wxAddress'})}>获取微信地址</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</ConfigProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CellGroup>
|
||||||
|
<Cell
|
||||||
|
onClick={() => Taro.navigateTo({url: '/user/address/wxAddress'})}
|
||||||
|
>
|
||||||
|
<div className={'flex justify-between items-center w-full'}>
|
||||||
|
<div className={'flex items-center gap-3'}>
|
||||||
|
<Dongdong className={'text-green-600'}/>
|
||||||
|
<div>获取微信地址</div>
|
||||||
|
</div>
|
||||||
|
<ArrowRight className={'text-gray-400'}/>
|
||||||
|
</div>
|
||||||
|
</Cell>
|
||||||
|
</CellGroup>
|
||||||
|
{list.map((item, _) => (
|
||||||
|
<Cell.Group>
|
||||||
|
<Cell className={'flex flex-col gap-1'} onClick={() => selectAddress(item)}>
|
||||||
|
<View>
|
||||||
|
<View className={'font-medium text-sm'}>{item.name} {item.phone}</View>
|
||||||
|
</View>
|
||||||
|
<View className={'text-xs'}>
|
||||||
|
{item.province} {item.city} {item.region} {item.address}
|
||||||
|
</View>
|
||||||
|
</Cell>
|
||||||
|
<Cell
|
||||||
|
align="center"
|
||||||
|
title={
|
||||||
|
<View className={'flex items-center gap-1'} onClick={() => onDefault(item)}>
|
||||||
|
{item.isDefault ? <Checked className={'text-green-600'} size={16}/> : <CheckNormal size={16}/>}
|
||||||
|
<View className={'text-gray-400'}>默认地址</View>
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
extra={
|
||||||
|
<>
|
||||||
|
<View className={'text-gray-400'} onClick={() => onDel(item.id)}>
|
||||||
|
删除
|
||||||
|
</View>
|
||||||
|
<Divider direction={'vertical'}/>
|
||||||
|
<View className={'text-gray-400'}
|
||||||
|
onClick={() => Taro.navigateTo({url: '/user/address/add?id=' + item.id})}>
|
||||||
|
修改
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Cell.Group>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Address;
|
||||||
Reference in New Issue
Block a user