diff --git a/.env.dev b/.env.dev
index a52eec30..9249ced8 100644
--- a/.env.dev
+++ b/.env.dev
@@ -19,13 +19,13 @@ VITE_API_URL=/admin-api
VITE_BASE_PATH=/
# 是否删除debugger
-VITE_DROP_DEBUGGER=false
+VITE_DROP_DEBUGGER=true
# 是否删除console.log
VITE_DROP_CONSOLE=false
# 是否sourcemap
-VITE_SOURCEMAP=true
+VITE_SOURCEMAP=false
# 输出路径
VITE_OUT_DIR=dist-dev
diff --git a/package.json b/package.json
index 06ce76df..d5a7ad01 100644
--- a/package.json
+++ b/package.json
@@ -65,6 +65,7 @@
"url": "^0.11.0",
"video.js": "^8.3.0",
"vue": "3.3.4",
+ "vue-dompurify-html": "^4.1.4",
"vue-i18n": "9.2.2",
"vue-router": "^4.2.1",
"vue-types": "^5.0.3",
diff --git a/src/api/mall/product/spu.ts b/src/api/mall/product/spu.ts
index fd55e126..b6bec97e 100644
--- a/src/api/mall/product/spu.ts
+++ b/src/api/mall/product/spu.ts
@@ -7,8 +7,7 @@ export interface Property {
valueName?: string // 属性值名称
}
-// TODO puhui999:是不是直接叫 Sku 更简洁一点哈。type 待后面,总感觉有个类型?
-export interface SkuType {
+export interface Sku {
id?: number // 商品 SKU 编号
spuId?: number // SPU 编号
properties?: Property[] // 属性数组
@@ -25,8 +24,7 @@ export interface SkuType {
salesCount?: number // 商品销量
}
-// TODO puhui999:是不是直接叫 Spu 更简洁一点哈。type 待后面,总感觉有个类型?
-export interface SpuType {
+export interface Spu {
id?: number
name?: string // 商品名称
categoryId?: number | null // 商品分类
@@ -39,9 +37,9 @@ export interface SpuType {
brandId?: number | null // 商品品牌编号
specType?: boolean // 商品规格
subCommissionType?: boolean // 分销类型
- skus: SkuType[] // sku数组
+ skus?: Sku[] // sku数组
description?: string // 商品详情
- sort?: string // 商品排序
+ sort?: number // 商品排序
giveIntegral?: number // 赠送积分
virtualSalesCount?: number // 虚拟销量
recommendHot?: boolean // 是否热卖
@@ -62,12 +60,12 @@ export const getTabsCount = () => {
}
// 创建商品 Spu
-export const createSpu = (data: SpuType) => {
+export const createSpu = (data: Spu) => {
return request.post({ url: '/product/spu/create', data })
}
// 更新商品 Spu
-export const updateSpu = (data: SpuType) => {
+export const updateSpu = (data: Spu) => {
return request.put({ url: '/product/spu/update', data })
}
@@ -90,3 +88,8 @@ export const deleteSpu = (id: number) => {
export const exportSpu = async (params) => {
return await request.download({ url: '/product/spu/export', params })
}
+
+// 获得商品 SPU 精简列表
+export const getSpuSimpleList = async () => {
+ return request.get({ url: '/product/spu/get-simple-list' })
+}
diff --git a/src/api/mall/promotion/coupon.ts b/src/api/mall/promotion/coupon.ts
new file mode 100755
index 00000000..565b86f7
--- /dev/null
+++ b/src/api/mall/promotion/coupon.ts
@@ -0,0 +1,18 @@
+import request from '@/config/axios'
+
+// TODO @dhb52:vo 缺少
+
+// 删除优惠劵
+export const deleteCoupon = async (id: number) => {
+ return request.delete({
+ url: `/promotion/coupon/delete?id=${id}`
+ })
+}
+
+// 获得优惠劵分页
+export const getCouponPage = async (params: PageParam) => {
+ return request.get({
+ url: '/promotion/coupon/page',
+ params: params
+ })
+}
diff --git a/src/api/mall/promotion/couponTemplate.ts b/src/api/mall/promotion/couponTemplate.ts
new file mode 100755
index 00000000..6a58876e
--- /dev/null
+++ b/src/api/mall/promotion/couponTemplate.ts
@@ -0,0 +1,83 @@
+import request from '@/config/axios'
+
+export interface CouponTemplateVO {
+ id: number
+ name: string
+ status: number
+ totalCount: number
+ takeLimitCount: number
+ takeType: number
+ usePrice: number
+ productScope: number
+ productSpuIds: string
+ validityType: number
+ validStartTime: Date
+ validEndTime: Date
+ fixedStartTerm: number
+ fixedEndTerm: number
+ discountType: number
+ discountPercent: number
+ discountPrice: number
+ discountLimitPrice: number
+ takeCount: number
+ useCount: number
+}
+
+// 创建优惠劵模板
+export function createCouponTemplate(data: CouponTemplateVO) {
+ return request.post({
+ url: '/promotion/coupon-template/create',
+ data: data
+ })
+}
+
+// 更新优惠劵模板
+export function updateCouponTemplate(data: CouponTemplateVO) {
+ return request.put({
+ url: '/promotion/coupon-template/update',
+ data: data
+ })
+}
+
+// 更新优惠劵模板的状态
+export function updateCouponTemplateStatus(id: number, status: [0, 1]) {
+ const data = {
+ id,
+ status
+ }
+ return request.put({
+ url: '/promotion/coupon-template/update-status',
+ data: data
+ })
+}
+
+// 删除优惠劵模板
+export function deleteCouponTemplate(id: number) {
+ return request.delete({
+ url: '/promotion/coupon-template/delete?id=' + id
+ })
+}
+
+// 获得优惠劵模板
+export function getCouponTemplate(id: number) {
+ return request.get({
+ url: '/promotion/coupon-template/get?id=' + id
+ })
+}
+
+// 获得优惠劵模板分页
+export function getCouponTemplatePage(params: PageParam) {
+ return request.get({
+ url: '/promotion/coupon-template/page',
+ params: params
+ })
+}
+
+// 导出优惠劵模板 Excel
+export function exportCouponTemplateExcel(params: PageParam) {
+ return request.get({
+ url: '/promotion/coupon-template/export-excel',
+ params: params,
+ responseType: 'blob'
+ })
+}
diff --git a/src/api/mall/trade/delivery/expressTemplate/index.ts b/src/api/mall/trade/delivery/expressTemplate/index.ts
index 9414c847..9ed23bc1 100644
--- a/src/api/mall/trade/delivery/expressTemplate/index.ts
+++ b/src/api/mall/trade/delivery/expressTemplate/index.ts
@@ -33,6 +33,11 @@ export const getDeliveryExpressTemplate = async (id: number) => {
return await request.get({ url: '/trade/delivery/express-template/get?id=' + id })
}
+// 查询快递运费模板详情
+export const getSimpleTemplateList = async () => {
+ return await request.get({ url: '/trade/delivery/express-template/list-all-simple' })
+}
+
// 新增快递运费模板
export const createDeliveryExpressTemplate = async (data: DeliveryExpressTemplateVO) => {
return await request.post({ url: '/trade/delivery/express-template/create', data })
@@ -47,8 +52,3 @@ export const updateDeliveryExpressTemplate = async (data: DeliveryExpressTemplat
export const deleteDeliveryExpressTemplate = async (id: number) => {
return await request.delete({ url: '/trade/delivery/express-template/delete?id=' + id })
}
-
-// 导出快递运费模板 Excel
-export const exportDeliveryExpressTemplateApi = async (params) => {
- return await request.download({ url: '/trade/delivery/express-template/export-excel', params })
-}
diff --git a/src/api/mall/trade/delivery/pickUpStore/index.ts b/src/api/mall/trade/delivery/pickUpStore/index.ts
new file mode 100644
index 00000000..90fb3d01
--- /dev/null
+++ b/src/api/mall/trade/delivery/pickUpStore/index.ts
@@ -0,0 +1,46 @@
+import request from '@/config/axios'
+
+export interface DeliveryPickUpStoreVO {
+ id: number
+ name: string
+ introduction: string
+ phone: string
+ areaId: number
+ detailAddress: string
+ logo: string
+ openingTime: string
+ closingTime: string
+ latitude: number
+ longitude: number
+ status: number
+}
+
+// 查询自提门店列表
+export const getDeliveryPickUpStorePage = async (params: DeliveryPickUpStorePageReqVO) => {
+ return await request.get({ url: '/trade/delivery/pick-up-store/page', params })
+}
+
+// 查询自提门店详情
+export const getDeliveryPickUpStore = async (id: number) => {
+ return await request.get({ url: '/trade/delivery/pick-up-store/get?id=' + id })
+}
+
+// 新增自提门店
+export const createDeliveryPickUpStore = async (data: DeliveryPickUpStoreVO) => {
+ return await request.post({ url: '/trade/delivery/pick-up-store/create', data })
+}
+
+// 修改自提门店
+export const updateDeliveryPickUpStore = async (data: DeliveryPickUpStoreVO) => {
+ return await request.put({ url: '/trade/delivery/pick-up-store/update', data })
+}
+
+// 删除自提门店
+export const deleteDeliveryPickUpStore = async (id: number) => {
+ return await request.delete({ url: '/trade/delivery/pick-up-store/delete?id=' + id })
+}
+
+// 导出自提门店 Excel
+export const exportDeliveryPickUpStoreApi = async (params) => {
+ return await request.download({ url: '/trade/delivery/pick-up-store/export-excel', params })
+}
diff --git a/src/components/Form/src/Form.vue b/src/components/Form/src/Form.vue
index c1121641..3dca6c94 100644
--- a/src/components/Form/src/Form.vue
+++ b/src/components/Form/src/Form.vue
@@ -1,16 +1,16 @@
diff --git a/src/views/mall/product/spu/components/DescriptionForm.vue b/src/views/mall/product/spu/components/DescriptionForm.vue
index fbae9a86..c272b239 100644
--- a/src/views/mall/product/spu/components/DescriptionForm.vue
+++ b/src/views/mall/product/spu/components/DescriptionForm.vue
@@ -1,28 +1,51 @@
-
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/mall/product/spu/components/spu.data.ts b/src/views/mall/product/spu/components/spu.data.ts
new file mode 100644
index 00000000..befb6e8a
--- /dev/null
+++ b/src/views/mall/product/spu/components/spu.data.ts
@@ -0,0 +1,106 @@
+import { CrudSchema } from '@/hooks/web/useCrudSchemas'
+
+// TODO @puhui999:如果只要 detail,可以不用 CrudSchema,只要描述的 Schema
+export const basicInfoSchema = reactive([
+ {
+ label: '商品名称',
+ field: 'name'
+ },
+ {
+ label: '关键字',
+ field: 'keyword'
+ },
+ {
+ label: '商品简介',
+ field: 'introduction'
+ },
+ {
+ label: '商品分类',
+ field: 'categoryId'
+ },
+ {
+ label: '商品品牌',
+ field: 'brandId'
+ },
+ {
+ label: '商品封面图',
+ field: 'picUrl'
+ },
+ {
+ label: '商品轮播图',
+ field: 'sliderPicUrls'
+ },
+ {
+ label: '商品视频',
+ field: 'videoUrl'
+ },
+ {
+ label: '单位',
+ field: 'unit',
+ dictType: DICT_TYPE.PRODUCT_UNIT
+ },
+ {
+ label: '规格类型',
+ field: 'specType'
+ },
+ {
+ label: '分销类型',
+ field: 'subCommissionType'
+ },
+ {
+ label: '物流模版',
+ field: 'deliveryTemplateId'
+ },
+ {
+ label: '商品属性列表',
+ field: 'skus'
+ }
+])
+export const descriptionSchema = reactive([
+ {
+ label: '商品详情',
+ field: 'description'
+ }
+])
+export const otherSettingsSchema = reactive([
+ {
+ label: '商品排序',
+ field: 'sort'
+ },
+ {
+ label: '赠送积分',
+ field: 'giveIntegral'
+ },
+ {
+ label: '虚拟销量',
+ field: 'virtualSalesCount'
+ },
+ {
+ label: '是否热卖推荐',
+ field: 'recommendHot'
+ },
+ {
+ label: '是否优惠推荐',
+ field: 'recommendBenefit'
+ },
+ {
+ label: '是否精品推荐',
+ field: 'recommendBest'
+ },
+ {
+ label: '是否新品推荐',
+ field: 'recommendNew'
+ },
+ {
+ label: '是否优品推荐',
+ field: 'recommendGood'
+ },
+ {
+ label: '赠送的优惠劵',
+ field: 'giveCouponTemplateIds'
+ },
+ {
+ label: '活动显示排序',
+ field: 'activityOrders'
+ }
+])
diff --git a/src/views/mall/product/spu/index.vue b/src/views/mall/product/spu/index.vue
index 539171b0..5cd90bf4 100644
--- a/src/views/mall/product/spu/index.vue
+++ b/src/views/mall/product/spu/index.vue
@@ -8,18 +8,16 @@
class="-mb-15px"
label-width="68px"
>
-
-
+
-
-
+
@@ -80,31 +79,60 @@
/>
-
+虚拟销量:999 -->
-
-
- {{ formatToFraction(row.marketPrice) }}
-
-
- {{ formatToFraction(row.costPrice) }}
-
-
- {{ row.virtualSalesCount }}
-
+
+
+
+
+
+
+ {{ categoryString(row.categoryId) }}
+
+
+
+
+ {{ formatToFraction(row.marketPrice) }}
+
+
+
+
+ {{ formatToFraction(row.costPrice) }}
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+ {{ row.virtualSalesCount }}
+
+
+
+
+
-
+
@@ -143,8 +171,12 @@
-
-
+
详情
@@ -202,7 +234,7 @@ import { TabsPaneContext } from 'element-plus'
import { cloneDeep } from 'lodash-es'
import { createImageViewer } from '@/components/ImageViewer'
import { dateFormatter } from '@/utils/formatTime'
-import { defaultProps, handleTree } from '@/utils/tree'
+import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree'
import { ProductSpuStatusEnum } from '@/utils/constants'
import { formatToFraction } from '@/utils'
import download from '@/utils/download'
@@ -256,12 +288,14 @@ const getTabsCount = async () => {
const queryParams = ref({
pageNo: 1,
pageSize: 10,
- tabType: 0
+ tabType: 0,
+ name: '',
+ categoryId: null
}) // 查询参数
const queryFormRef = ref() // 搜索的表单Ref
const handleTabClick = (tab: TabsPaneContext) => {
- queryParams.value.tabType = tab.paneName
+ queryParams.value.tabType = tab.paneName as number
getList()
}
@@ -372,8 +406,8 @@ const openForm = (id?: number) => {
/**
* 查看商品详情
*/
-const openDetail = () => {
- message.alert('查看详情未完善!!!')
+const openDetail = (id?: number) => {
+ push('/product/productSpuDetail/' + id)
}
/** 导出按钮操作 */
@@ -391,7 +425,7 @@ const handleExport = async () => {
}
}
-// 监听路由变化更新列表 TODO @puhui999:这个是必须加的么?fix: 因为编辑表单是以路由的方式打开,保存表单后列表不会刷新
+// 监听路由变化更新列表,解决商品保存后,列表不刷新的问题。
watch(
() => currentRoute.value,
() => {
@@ -400,6 +434,22 @@ watch(
)
const categoryList = ref() // 分类树
+/**
+ * 获取分类的节点的完整结构
+ * @param categoryId 分类id
+ */
+const categoryString = (categoryId) => {
+ return treeToString(categoryList.value, categoryId)
+}
+/**
+ * 校验所选是否为二级及以下节点
+ */
+const nodeClick = () => {
+ if (!checkSelectedNode(categoryList.value, queryParams.value.categoryId)) {
+ queryParams.value.categoryId = null
+ message.warning('必须选择二级及以下节点!!')
+ }
+}
/** 初始化 **/
onMounted(async () => {
await getTabsCount()
diff --git a/src/views/mall/promotion/coupon/index.vue b/src/views/mall/promotion/coupon/index.vue
new file mode 100755
index 00000000..bf296e9a
--- /dev/null
+++ b/src/views/mall/promotion/coupon/index.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 回收
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/mall/promotion/couponTemplate/index.vue b/src/views/mall/promotion/couponTemplate/index.vue
new file mode 100755
index 00000000..b63e93e1
--- /dev/null
+++ b/src/views/mall/promotion/couponTemplate/index.vue
@@ -0,0 +1,614 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+ 重置
+
+
+
+
+
+
+
+ 新增
+
+
+ 会员优惠劵
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 修改
+
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dict.label }}
+
+
+
+
+ 元
+
+
+
+ 折
+
+
+
+ 元
+
+
+
+ 元
+
+
+
+ 直接领取
+ 指定发放
+
+
+
+
+ 张
+
+
+
+ 张
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+
+ 第
+
+ 至
+
+ 天有效
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+ {{ item.name }}
+ ¥{{ (item.minPrice / 100.0).toFixed(2) }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/mall/trade/delivery/expressTemplate/ExpressTemplateForm.vue b/src/views/mall/trade/delivery/expressTemplate/ExpressTemplateForm.vue
index 5b25422d..6c9ff296 100644
--- a/src/views/mall/trade/delivery/expressTemplate/ExpressTemplateForm.vue
+++ b/src/views/mall/trade/delivery/expressTemplate/ExpressTemplateForm.vue
@@ -89,7 +89,7 @@
-
+
{
dialogVisible.value = true
@@ -204,9 +207,9 @@ const open = async (type: string, id?: number) => {
}
item.freePrice = fenToYuan(item.freePrice)
})
- //已选的区域节点
+ // 已选的区域节点
const areaIds = chargeAreaIds.concat(freeAreaIds)
- //区域节点,懒加载方式。 已选节点需要缓存展示
+ // 区域节点,懒加载方式。已选节点需要缓存展示
areaCache.value = await getAreaListByIds(areaIds.join(','))
}
} finally {
@@ -226,8 +229,9 @@ const submitForm = async () => {
formLoading.value = true
try {
const data = formData.value as DeliveryExpressTemplateApi.DeliveryExpressTemplateVO
+ // 前端价格以元展示,提交到后端。用分计算
+ // TODO @jason:不能直接这样改,要复制出来改。不然后端操作失败,数据已经被改了
data.templateCharge.forEach((item) => {
- //前端价格以元展示,提交到后端。用分计算
item.startPrice = yuanToFen(item.startPrice)
item.extraPrice = yuanToFen(item.extraPrice)
})
@@ -248,6 +252,7 @@ const submitForm = async () => {
formLoading.value = false
}
}
+
/** 重置表单 */
const resetForm = () => {
formData.value = {
@@ -269,6 +274,7 @@ const resetForm = () => {
columnTitle.value = columnTitleMap.get(1)
formRef.value?.resetFields()
}
+
/** 配送计费方法改变 */
const changeChargeMode = (chargeMode: number) => {
columnTitle.value = columnTitleMap.get(chargeMode)
@@ -276,6 +282,24 @@ const changeChargeMode = (chargeMode: number) => {
const defaultArea = [{ id: 1, name: '全国', disabled: false }]
/** 初始化数据 */
+// TODO @jason:是不是不用写这样一个初始化方法,columnTitleMap 直接就可以了呀
+// const columnTitleMap = {
+// '1': {
+// startCountTitle: '首件',
+// extraCountTitle: '续件',
+// freeCountTitle: '包邮件数'
+// },
+// '2': {
+// startCountTitle: '首件重量(kg)',
+// extraCountTitle: '续件重量(kg)',
+// freeCountTitle: '包邮重量(kg)'
+// },
+// '3': {
+// startCountTitle: '首件体积(m³)',
+// extraCountTitle: '续件体积(m³)',
+// freeCountTitle: '包邮体积(m³)'
+// }
+// }
const initData = async () => {
// TODO 从服务端全量加载数据, 后面看懒加载是不是可以从前端获取数据。 目前从后端获取数据
// formLoading.value = true
@@ -286,7 +310,7 @@ const initData = async () => {
// } finally {
// formLoading.value = false
// }
- //表头标题和计费方式的映射
+ // 表头标题和计费方式的映射
columnTitleMap.set(1, {
startCountTitle: '首件',
extraCountTitle: '续件',
@@ -320,6 +344,7 @@ const loadChargeArea = async (node, resolve) => {
const item = data[0]
if (areaIds.includes(item.id)) {
// TODO 禁止选中的区域有些问题, 导致修改时候不能重新选择 不知道如何处理。 暂时注释掉 @芋艿 有空瞅瞅
+ // TODO @jason:先不做这个功能哈。
//item.disabled = true
}
resolve(data)
@@ -357,10 +382,11 @@ const loadFreeArea = async (node, resolve) => {
} else {
const id = node.data.id
const data = await getChildrenArea(id)
- //已选区域需要禁止再次选择
+ // 已选区域需要禁止再次选择
data.forEach((item) => {
if (areaIds.includes(item.id)) {
// TODO 禁止选中的区域有些问题, 导致修改时候不能重新选择 不知道如何处理。 暂时注释掉 @芋艿 有空瞅瞅
+ // TODO @jason:先不做这个功能哈。
//item.disabled = true
}
})
@@ -378,11 +404,13 @@ const addChargeArea = () => {
extraPrice: 1
})
}
+
/** 删除计费区域 */
const deleteChargeArea = (index) => {
const data = formData.value
data.templateCharge.splice(index, 1)
}
+
/** 添加包邮区域 */
const addFreeArea = () => {
const data = formData.value
@@ -392,6 +420,7 @@ const addFreeArea = () => {
freePrice: 1
})
}
+
/** 删除包邮区域 */
const deleteFreeArea = (index) => {
const data = formData.value
diff --git a/src/views/mall/trade/delivery/expressTemplate/index.vue b/src/views/mall/trade/delivery/expressTemplate/index.vue
index e9f670f5..9acb5db9 100644
--- a/src/views/mall/trade/delivery/expressTemplate/index.vue
+++ b/src/views/mall/trade/delivery/expressTemplate/index.vue
@@ -110,6 +110,7 @@ const queryParams = reactive({
chargeMode: undefined
})
const queryFormRef = ref() // 搜索的表单
+
/** 查询列表 */
const getList = async () => {
loading.value = true
diff --git a/src/views/mall/trade/delivery/pickUpStore/PickUpStoreForm.vue b/src/views/mall/trade/delivery/pickUpStore/PickUpStoreForm.vue
new file mode 100644
index 00000000..cb9358ad
--- /dev/null
+++ b/src/views/mall/trade/delivery/pickUpStore/PickUpStoreForm.vue
@@ -0,0 +1,287 @@
+
+
+
+
+
diff --git a/src/views/mall/trade/delivery/pickUpStore/index.vue b/src/views/mall/trade/delivery/pickUpStore/index.vue
new file mode 100644
index 00000000..c8963442
--- /dev/null
+++ b/src/views/mall/trade/delivery/pickUpStore/index.vue
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
diff --git a/src/views/system/mail/log/MailLogDetail.vue b/src/views/system/mail/log/MailLogDetail.vue
index 26c56bc1..13f7956a 100644
--- a/src/views/system/mail/log/MailLogDetail.vue
+++ b/src/views/system/mail/log/MailLogDetail.vue
@@ -3,7 +3,7 @@
-
+