Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
owen
2024-01-11 11:35:08 +08:00
24 changed files with 634 additions and 414 deletions

View File

@@ -1,3 +1,4 @@
<!-- 商品中心 - 商品列表 -->
<template>
<!-- 搜索工作栏 -->
<ContentWrap>
@@ -125,27 +126,33 @@
</el-form>
</template>
</el-table-column>
<el-table-column align="center" label="商品编号" min-width="60" prop="id" />
<el-table-column label="商品" min-width="80">
<el-table-column label="商品编号" min-width="140" prop="id" />
<el-table-column label="商品信息" min-width="300">
<template #default="{ row }">
<el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" />
<div class="flex">
<el-image
fit="cover"
:src="row.picUrl"
class="flex-none w-50px h-50px"
@click="imagePreview(row.picUrl)"
/>
<div class="ml-4 overflow-hidden">
<el-tooltip effect="dark" :content="row.name" placement="top">
<div>
{{ row.name }}
</div>
</el-tooltip>
</div>
</div>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
<el-table-column align="center" label="商品售价" min-width="90" prop="price">
<template #default="{ row }"> {{ fenToYuan(row.price) }}</template>
<el-table-column align="center" label="价格" min-width="160" prop="price">
<template #default="{ row }"> ¥ {{ fenToYuan(row.price) }}</template>
</el-table-column>
<el-table-column align="center" label="销量" min-width="90" prop="salesCount" />
<el-table-column align="center" label="库存" min-width="90" prop="stock" />
<el-table-column align="center" label="排序" min-width="70" prop="sort" />
<el-table-column
:formatter="dateFormatter"
align="center"
label="创建时间"
prop="createTime"
width="180"
/>
<el-table-column align="center" label="状态" min-width="80">
<el-table-column align="center" label="销售状态" min-width="80">
<template #default="{ row }">
<template v-if="row.status >= 0">
<el-switch
@@ -163,16 +170,16 @@
</template>
</template>
</el-table-column>
<el-table-column
:formatter="dateFormatter"
align="center"
label="创建时间"
prop="createTime"
width="180"
/>
<el-table-column align="center" fixed="right" label="操作" min-width="200">
<template #default="{ row }">
<el-button
v-hasPermi="['product:spu:update']"
link
type="primary"
@click="openDetail(row.id)"
>
详情
</el-button>
<el-button link type="primary" @click="openDetail(row.id)"> 详情 </el-button>
<el-button
v-hasPermi="['product:spu:update']"
link
@@ -196,17 +203,17 @@
type="primary"
@click="handleStatus02Change(row, ProductSpuStatusEnum.DISABLE.status)"
>
恢复到仓库
恢复
</el-button>
</template>
<template v-else>
<el-button
v-hasPermi="['product:spu:update']"
link
type="primary"
type="danger"
@click="handleStatus02Change(row, ProductSpuStatusEnum.RECYCLE.status)"
>
加入回收
回收
</el-button>
</template>
</template>
@@ -236,48 +243,41 @@ defineOptions({ name: 'ProductSpu' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const { currentRoute, push } = useRouter() // 路由跳转
const { push } = useRouter() // 路由跳转
const loading = ref(false) // 列表的加载中
const exportLoading = ref(false) // 导出的加载中
const total = ref(0) // 列表的总页数
const list = ref<any[]>([]) // 列表的数据
const list = ref<ProductSpuApi.Spu[]>([]) // 列表的数据
// tabs 数据
const tabsData = ref([
{
count: 0,
name: '出售中商品',
type: 0
name: '出售中',
type: 0,
count: 0
},
{
count: 0,
name: '仓库中商品',
type: 1
name: '仓库中',
type: 1,
count: 0
},
{
count: 0,
name: '已售罄商品',
type: 2
name: '已售罄',
type: 2,
count: 0
},
{
count: 0,
name: '警戒库存',
type: 3
type: 3,
count: 0
},
{
count: 0,
name: '商品回收站',
type: 4
name: '回收站',
type: 4,
count: 0
}
])
/** 获得每个 Tab 的数量 */
const getTabsCount = async () => {
const res = await ProductSpuApi.getTabsCount()
for (let objName in res) {
tabsData.value[Number(objName)].count = res[objName]
}
}
const queryParams = ref({
pageNo: 1,
pageSize: 10,
@@ -288,11 +288,6 @@ const queryParams = ref({
}) // 查询参数
const queryFormRef = ref() // 搜索的表单Ref
const handleTabClick = (tab: TabsPaneContext) => {
queryParams.value.tabType = tab.paneName as number
getList()
}
/** 查询列表 */
const getList = async () => {
loading.value = true
@@ -305,8 +300,22 @@ const getList = async () => {
}
}
/** 切换 Tab */
const handleTabClick = (tab: TabsPaneContext) => {
queryParams.value.tabType = tab.paneName as number
getList()
}
/** 获得每个 Tab 的数量 */
const getTabsCount = async () => {
const res = await ProductSpuApi.getTabsCount()
for (let objName in res) {
tabsData.value[Number(objName)].count = res[objName]
}
}
/** 添加到仓库 / 回收站的状态 */
const handleStatus02Change = async (row, newStatus: number) => {
const handleStatus02Change = async (row: any, newStatus: number) => {
try {
// 二次确认
const text = newStatus === ProductSpuStatusEnum.RECYCLE.status ? '加入到回收站' : '恢复到仓库'
@@ -322,7 +331,7 @@ const handleStatus02Change = async (row, newStatus: number) => {
}
/** 更新上架/下架状态 */
const handleStatusChange = async (row) => {
const handleStatusChange = async (row: any) => {
try {
// 二次确认
const text = row.status ? '上架' : '下架'
@@ -407,19 +416,16 @@ const handleExport = async () => {
}
}
const categoryList = ref() // 分类树
/** 获取分类的节点的完整结构 */
const formatCategoryName = (categoryId) => {
const categoryList = ref() // 分类树
const formatCategoryName = (categoryId: number) => {
return treeToString(categoryList.value, categoryId)
}
// 监听路由变化更新列表,解决商品保存后,列表不刷新的问题。
watch(
() => currentRoute.value,
() => {
getList()
}
)
/** 激活时 */
onActivated(() => {
getList()
})
/** 初始化 **/
onMounted(async () => {

View File

@@ -24,22 +24,96 @@
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_CONDITION_TYPE)"
:key="dict.value"
:label="parseInt(dict.value)"
>{{ dict.label }}</el-radio
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="优惠设置">
<!-- TODO 待实现这个实现下哈 -->
<template v-for="(item, index) in formData.rules" :key="index">
<el-row type="flex">
<el-col :span="24" style="font-weight: bold; display: flex">
活动层级{{ index + 1 }}
<el-button
link
type="danger"
style="margin-left: auto"
v-if="index != 0"
@click="deleteActivityRule(index)"
>
删除
</el-button>
</el-col>
<e-form :ref="'formRef' + index" :model="item">
<el-form-item
label="优惠门槛:"
prop="limit"
label-width="100px"
style="padding-left: 50px"
>
<el-input
style="width: 150px; padding: 0 10px"
v-model="item.limit"
type="number"
placeholder=""
/>
</el-form-item>
<el-form-item label="优惠内容:" label-width="100px" style="padding-left: 50px">
<el-checkbox-group v-model="activityRules[index]" style="width: 100%">
<el-col :span="24">
<el-checkbox label="订单金额优惠" name="type" />
<el-form-item v-if="activityRules[index].includes('订单金额优惠')">
<el-input
style="width: 150px; padding: 0 20px"
v-model="item.discountPrice"
type="number"
placeholder=""
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-checkbox v-model="item.freeDelivery" label="包邮" name="type" />
</el-col>
<el-col :span="24">
<el-checkbox label="送积分" name="type" />
<el-form-item v-if="activityRules[index].includes('送积分')">
<el-input
style="width: 150px; padding: 0 20px"
v-model="item.point"
type="number"
placeholder=""
/>
积分
</el-form-item>
</el-col>
<!-- 优惠券待处理 也可以参考优惠劵的SpuShowcase-->
<!-- TODO 待实现-->
<el-col :span="24">
<el-checkbox label="送优惠券" name="type" />
</el-col>
</el-checkbox-group>
</el-form-item>
</e-form>
</el-row>
</template>
<!-- TODO 实现建议改成放在每一个活动层级的下面有点类似主子表 -->
<el-button type="primary" @click="addActivityStratum">添加活动层级</el-button>
</el-form-item>
<el-form-item label="活动商品" prop="productScope">
<el-radio-group v-model="formData.productScope">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
:key="dict.value"
:label="parseInt(dict.value)"
>{{ dict.label }}</el-radio
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- TODO活动商品的开发可以参考优惠劵的已经搞好啦 -->
@@ -58,9 +132,9 @@
>
<el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name }}</span>
<span style="float: right; font-size: 13px; color: #8492a6"
>{{ (item.price / 100.0).toFixed(2) }}</span
>
<span style="float: right; font-size: 13px; color: #8492a6">
{{ (item.price / 100.0).toFixed(2) }}
</span>
</el-option>
</el-select>
</el-form-item>
@@ -77,15 +151,8 @@
<script lang="ts" setup>
import { getSpuSimpleList } from '@/api/mall/product/spu'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { CommonStatusEnum } from '@/utils/constants'
import * as ProductBrandApi from '@/api/mall/product/brand'
import {
PromotionConditionTypeEnum,
PromotionProductScopeEnum,
PromotionActivityStatusEnum
} from '@/utils/constants'
// 商品数据
const productSpus = ref<any[]>([])
import * as RewardActivityApi from '@/api/mall/promotion/reward/rewardActivity'
import { PromotionConditionTypeEnum, PromotionProductScopeEnum } from '@/utils/constants'
/** 初始化 **/
onMounted(() => {
@@ -98,6 +165,7 @@ defineOptions({ name: 'ProductBrandForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const productSpus = ref<any[]>([]) // 商品数据
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
@@ -112,8 +180,18 @@ const formData = ref({
remark: undefined,
productScope: PromotionProductScopeEnum.ALL.scope,
productSpuIds: undefined,
rules: undefined
rules: [
{
limit: undefined,
discountPrice: undefined,
freeDelivery: undefined,
point: undefined,
couponIds: [],
couponCounts: []
}
]
})
const activityRules = reactive([]) // 优惠设置。每个元素都是一个 [],放“包邮”、“送积分”、“订单金额优惠”
const formRules = reactive({
name: [{ required: true, message: '活动名称不能为空', trigger: 'blur' }],
startAndEndTime: [{ required: true, message: '活动时间不能为空', trigger: 'blur' }],
@@ -121,7 +199,7 @@ const formRules = reactive({
productScope: [{ required: true, message: '商品范围不能为空', trigger: 'blur' }],
productSpuIds: [{ required: true, message: '商品范围不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
const formRef = ref([]) // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
@@ -133,19 +211,24 @@ const open = async (type: string, id?: number) => {
if (id) {
formLoading.value = true
try {
// formData.value = await ProductBrandApi.getBrand(id)
formData.value = {
conditionType: 10,
description: '',
id: undefined,
name: '测试活动',
picUrl: '',
productScope: 2,
productSpuIds: [634],
remark: '测试备注',
startAndEndTime: [new Date(), new Date('2023-12-31')],
status: 0
}
let data = await RewardActivityApi.getReward(id)
data.startAndEndTime = [new Date(data.startTime), new Date(data.endTime)]
activityRules.splice(0, activityRules.length)
data.rules.forEach((item) => {
// TODO 是不是不用 reactive直接 [] 就可以了?
let array: string[] = reactive([])
if (item.freeDelivery) {
array.push('包邮')
}
if (item.point) {
array.push('送积分')
}
if (item.discountPrice) {
array.push('订单金额优惠')
}
activityRules.push(array)
})
formData.value = data
} finally {
formLoading.value = false
}
@@ -160,18 +243,28 @@ const submitForm = async () => {
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
console.log(formData.value)
message.success('已在控制台打印数据')
return
// 处理下数据兼容接口
formData.value.startTime = +new Date(formData.value.startAndEndTime[0])
formData.value.endTime = +new Date(formData.value.startAndEndTime[1])
activityRules.forEach((item, index) => {
formData.value.rules[index].freeDelivery = !!item.includes('包邮')
if (!item.includes('送积分')) {
formData.value.rules[index].point = undefined
}
if (!item.includes('订单金额优惠')) {
formData.value.rules[index].discountPrice = undefined
}
})
// 提交请求
formLoading.value = true
try {
const data = formData.value as ProductBrandApi.BrandVO
const data = formData.value as RewardActivityApi.DiscountActivityVO
if (formType.value === 'create') {
await ProductBrandApi.createBrand(data)
await RewardActivityApi.createRewardActivity(data)
message.success(t('common.createSuccess'))
} else {
await ProductBrandApi.updateBrand(data)
await RewardActivityApi.updateRewardActivity(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
@@ -182,15 +275,51 @@ const submitForm = async () => {
}
}
const addActivityStratum = () => {
formData.value.rules.push({
limit: undefined,
discountPrice: undefined,
freeDelivery: undefined,
point: undefined,
couponIds: [],
couponCounts: []
})
activityRules.push([])
}
const deleteActivityRule = (index) => {
formData.value.rules.splice(index, 1)
activityRules.splice(index, 1)
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
name: '',
picUrl: '',
status: CommonStatusEnum.ENABLE,
description: ''
name: undefined,
startAndEndTime: undefined,
startTime: undefined,
endTime: undefined,
conditionType: PromotionConditionTypeEnum.PRICE.type,
remark: undefined,
productScope: PromotionProductScopeEnum.ALL.scope,
productSpuIds: undefined,
rules: [
{
limit: undefined,
discountPrice: undefined,
freeDelivery: undefined,
point: undefined,
couponIds: [],
couponCounts: []
}
]
}
formRef.value?.resetFields()
activityRules.splice(0, activityRules.length)
activityRules.push(reactive([]))
// 解决下有时刷新页面第一次点编辑报错
nextTick(() => {
formRef.value?.resetFields()
})
}
</script>

View File

@@ -65,13 +65,13 @@
<el-table-column
label="活动开始时间"
align="center"
prop="sort[0]"
prop="startTime"
:formatter="dateFormatter"
/>
<el-table-column
label="活动结束时间"
align="center"
prop="sort[1]"
prop="endTime"
:formatter="dateFormatter"
/>
<el-table-column label="状态" align="center" prop="status">
@@ -122,7 +122,7 @@
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import * as ProductBrandApi from '@/api/mall/product/brand'
import * as RewardActivityApi from '@/api/mall/promotion/reward/rewardActivity'
import RewardForm from './RewardForm.vue'
defineOptions({ name: 'PromotionRewardActivity' })
@@ -146,22 +146,7 @@ const queryFormRef = ref() // 搜索的表单
const getList = async () => {
loading.value = true
try {
// const data = await ProductBrandApi.getBrandParam(queryParams)
const data = {
list: [
{
createTime: 1693463998000,
description: '',
id: 3,
name: '索尼',
picUrl:
'http://127.0.0.1:48080/admin-api/infra/file/4/get/f5b7a536306cd1180a42a2211a8212dc23de6b949d30c30d036caa063042f928.png',
sort: [+new Date(), +new Date('2023-12-31')],
status: 10
}
],
total: 1
}
const data = await RewardActivityApi.getRewardActivityPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
@@ -171,16 +156,11 @@ const getList = async () => {
/** 搜索按钮操作 */
const handleQuery = () => {
console.log(queryParams)
message.success('已打印搜索参数')
return
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
message.success('重置查询表单获取数据')
return
queryFormRef.value.resetFields()
handleQuery()
}
@@ -196,10 +176,8 @@ const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
message.success('您以确认删除')
return
// 发起删除
await ProductBrandApi.deleteBrand(id)
await RewardActivityApi.deleteRewardActivity(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()

View File

@@ -77,12 +77,9 @@ const queryParams = reactive({
times: [],
sortingFields: {}
})
// 列表的加载中
const loading = ref(false)
// 列表的总页
const total = ref(0)
// 列表的数据
const list = ref<ProductStatisticsVO[]>([])
const loading = ref(false) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref<ProductStatisticsVO[]>([]) // 列表的数
/** 查询商品列表 */
const getSpuList = async () => {

View File

@@ -186,17 +186,6 @@
</el-checkbox-group>
<el-text class="w-full" size="small" type="info"> 商城开通提现的付款方式 </el-text>
</el-form-item>
<el-form-item label="提现银行" prop="brokerageBankNames">
<el-select v-model="formData.brokerageBankNames" placeholder="请选择提现银行" multiple>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BROKERAGE_BANK_NAME)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-text class="w-full" size="small" type="info"> 商城开通提现的银行列表 </el-text>
</el-form-item>
</el-tab-pane>
</el-tabs>
<!-- 保存 -->
@@ -232,7 +221,6 @@ const formData = ref({
brokerageSecondPercent: 0,
brokerageWithdrawMinPrice: 0,
brokerageWithdrawFeePercent: 0,
brokerageBankNames: [],
brokerageFrozenDays: 0,
brokerageWithdrawTypes: []
})
@@ -246,7 +234,6 @@ const formRules = reactive({
{ required: true, message: '用户提现最低金额不能为空', trigger: 'blur' }
],
brokerageWithdrawFeePercent: [{ required: true, message: '提现手续费不能为空', trigger: 'blur' }],
brokerageBankNames: [{ required: true, message: '提现银行不能为空', trigger: 'blur' }],
brokerageFrozenDays: [{ required: true, message: '佣金冻结时间不能为空', trigger: 'blur' }],
brokerageWithdrawTypes: [
{