Merge remote-tracking branch 'yudao/dev' into dev-to-dev

This commit is contained in:
puhui999
2023-10-05 21:50:33 +08:00
146 changed files with 3359 additions and 1302 deletions

View File

@@ -35,14 +35,14 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" row-key="id" default-expand-all>
<el-table-column label="分类名称" prop="name" sortable />
<el-table-column label="移动端分类图" align="center" prop="picUrl">
<el-table-column label="名称" min-width="240" prop="name" sortable />
<el-table-column label="分类图" align="center" min-width="80" prop="picUrl">
<template #default="scope">
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="移动端分类图" class="h-30px" />
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="移动端分类图" class="h-36px" />
</template>
</el-table-column>
<el-table-column label="分类排序" align="center" prop="sort" />
<el-table-column label="开启状态" align="center" prop="status">
<el-table-column label="排序" align="center" min-width="150" prop="sort" />
<el-table-column label="状态" align="center" min-width="150" prop="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>

View File

@@ -8,7 +8,7 @@
v-loading="formLoading"
>
<el-form-item label="商品" prop="spuId">
<div @click="handleSelectSpu" class="w-60px h-60px">
<div @click="handleSelectSpu" class="h-60px w-60px">
<div v-if="spuData && spuData.picUrl">
<el-image :src="spuData.picUrl" />
</div>
@@ -18,7 +18,7 @@
</div>
</el-form-item>
<el-form-item label="商品规格" prop="skuId" v-if="formData.spuId">
<div @click="handleSelectSku" class="w-60px h-60px">
<div @click="handleSelectSku" class="h-60px w-60px">
<div v-if="skuData && skuData.picUrl">
<el-image :src="skuData.picUrl" />
</div>
@@ -150,6 +150,7 @@ const resetForm = () => {
userNickname: undefined,
userAvatar: undefined,
spuId: undefined,
spuName: undefined,
skuId: undefined,
descriptionScores: 5,
benefitScores: 5,
@@ -182,11 +183,11 @@ const handleSkuChange = (sku: ProductSpuApi.Sku) => {
<style>
.select-box {
display: flex;
align-items: center;
justify-content: center;
border: 1px dashed var(--el-border-color-darker);
border-radius: 8px;
width: 100%;
height: 100%;
border: 1px dashed var(--el-border-color-darker);
border-radius: 8px;
align-items: center;
justify-content: center;
}
</style>

View File

@@ -59,16 +59,15 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="false">
<el-table-column label="评论编号" align="center" prop="id" min-width="60" />
<el-table-column label="用户名称" align="center" prop="userNickname" width="80" />
<el-table-column label="商品信息" align="center" min-width="300">
<el-table-column label="评论编号" align="center" prop="id" min-width="50" />
<el-table-column label="商品信息" align="center" min-width="400">
<template #default="scope">
<div class="flex row items-center gap-x-4px">
<div class="row flex items-center gap-x-4px">
<el-image
v-if="scope.row.skuPicUrl"
:src="scope.row.skuPicUrl"
:preview-src-list="[scope.row.skuPicUrl]"
class="w-40px h-40px shrink-0"
class="h-40px w-40px shrink-0"
preview-teleported
/>
<div>{{ scope.row.spuName }}</div>
@@ -82,10 +81,10 @@
</div>
</template>
</el-table-column>
<el-table-column label="评分星级" align="center" prop="scores" width="80" />
<el-table-column label="描述星级" align="center" prop="descriptionScores" width="80" />
<el-table-column label="服务星级" align="center" prop="benefitScores" width="80" />
<el-table-column label="评论内容" align="center" prop="content" min-width="80">
<el-table-column label="用户名称" align="center" prop="userNickname" width="100" />
<el-table-column label="商品评分" align="center" prop="descriptionScores" width="90" />
<el-table-column label="服务评分" align="center" prop="benefitScores" width="90" />
<el-table-column label="评论内容" align="center" prop="content" min-width="210">
<template #default="scope">
<p>{{ scope.row.content }}</p>
<div class="flex justify-center gap-x-4px">
@@ -95,7 +94,7 @@
:src="picUrl"
:preview-src-list="scope.row.picUrls"
:initial-index="index"
class="w-40px h-40px"
class="h-40px w-40px"
preview-teleported
/>
</div>
@@ -105,7 +104,7 @@
label="回复内容"
align="center"
prop="replyContent"
min-width="100"
min-width="250"
show-overflow-tooltip
/>
<el-table-column
@@ -113,7 +112,7 @@
align="center"
prop="createTime"
:formatter="dateFormatter"
width="170"
width="180"
/>
<el-table-column label="是否展示" align="center" width="80px">
<template #default="scope">

View File

@@ -53,8 +53,8 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column align="center" label="编号" prop="id" />
<el-table-column align="center" label="名称" prop="name" />
<el-table-column align="center" label="编号" min-width="60" prop="id" />
<el-table-column align="center" label="属性名称" prop="name" min-width="150" />
<el-table-column :show-overflow-tooltip="true" align="center" label="备注" prop="remark" />
<el-table-column
:formatter="dateFormatter"
@@ -165,7 +165,7 @@ const handleDelete = async (id: number) => {
/** 跳转商品属性列表 */
const goValueList = (id: number) => {
push({ path: '/product/property/value/' + id })
push({ name: 'ProductPropertyValue', params: { propertyId: id } })
}
/** 初始化 **/

View File

@@ -45,8 +45,8 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名称" align="center" prop="name" :show-overflow-tooltip="true" />
<el-table-column label="编号" align="center" min-width="60" prop="id" />
<el-table-column label="属性值名称" align="center" min-width="150" prop="name" />
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column
label="创建时间"

View File

@@ -124,7 +124,7 @@
<el-table-column v-if="isComponent" type="selection" width="45" />
<el-table-column align="center" label="图片" min-width="80">
<template #default="{ row }">
<el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
<el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" />
</template>
</el-table-column>
<template v-if="formData!.specType && !isBatch">
@@ -204,7 +204,7 @@
<el-table-column v-if="isComponent" type="selection" width="45" />
<el-table-column align="center" label="图片" min-width="80">
<template #default="{ row }">
<el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
<el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" />
</template>
</el-table-column>
<template v-if="formData!.specType">

View File

@@ -12,7 +12,7 @@
<template #default="{ row }">
<el-image
:src="row.picUrl"
class="w-30px h-30px"
class="h-30px w-30px"
:preview-src-list="[row.picUrl]"
preview-teleported
/>
@@ -25,7 +25,7 @@
</el-table-column>
<el-table-column align="center" label="销售价(元)" min-width="80">
<template #default="{ row }">
{{ row.price }}
{{ fenToYuan(row.price) }}
</template>
</el-table-column>
</el-table>
@@ -36,6 +36,7 @@
import { ElTable } from 'element-plus'
import * as ProductSpuApi from '@/api/mall/product/spu'
import { propTypes } from '@/utils/propTypes'
import { fenToYuan } from '@/utils'
defineOptions({ name: 'SkuTableSelect' })

View File

@@ -73,7 +73,7 @@
<template #default="{ row }">
<el-image
:src="row.picUrl"
class="w-30px h-30px"
class="h-30px w-30px"
:preview-src-list="[row.picUrl]"
preview-teleported
/>

View File

@@ -15,15 +15,14 @@
</el-col>
<el-col :span="12">
<el-form-item label="商品分类" prop="categoryId">
<el-tree-select
<el-cascader
v-model="formData.categoryId"
:data="categoryList"
:options="categoryList"
:props="defaultProps"
check-strictly
class="w-1/1"
node-key="id"
clearable
placeholder="请选择商品分类"
@change="categoryNodeClick"
filterable
/>
</el-form-item>
</el-col>
@@ -74,8 +73,6 @@
:value="item.id"
/>
</el-select>
<!-- TODO 可能情况善品录入后选择运费发现下拉选择中没有对应的模版 这里需不需要做添加运费模版后选择的功能 -->
<!-- <el-button class="ml-20px">运费模板</el-button>-->
</el-form-item>
</el-col>
<el-col :span="12">
@@ -102,7 +99,7 @@
<el-form-item label="分销类型" props="subCommissionType">
<el-radio-group v-model="formData.subCommissionType" @change="changeSubCommissionType">
<el-radio :label="false">默认设置</el-radio>
<el-radio :label="true" class="radio">自行设置</el-radio>
<el-radio :label="true" class="radio">单独设置</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@@ -117,7 +114,7 @@
/>
</el-form-item>
<el-form-item v-if="formData.specType" label="商品属性">
<el-button class="mr-15px mb-10px" @click="attributesAddFormRef.open">添加规格</el-button>
<el-button class="mb-10px mr-15px" @click="attributesAddFormRef.open">添加属性</el-button>
<ProductAttributes :propertyList="propertyList" @success="generateSkus" />
</el-form-item>
<template v-if="formData.specType && propertyList.length > 0">
@@ -139,7 +136,7 @@
<!-- 情况二详情 -->
<Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
<template #categoryId="{ row }"> {{ categoryString(row.categoryId) }}</template>
<template #categoryId="{ row }"> {{ formatCategoryName(row.categoryId) }}</template>
<template #brandId="{ row }">
{{ brandList.find((item) => item.id === row.brandId)?.name }}
</template>
@@ -150,17 +147,17 @@
{{ row.specType ? '多规格' : '单规格' }}
</template>
<template #subCommissionType="{ row }">
{{ row.subCommissionType ? '自行设置' : '默认设置' }}
{{ row.subCommissionType ? '单独设置' : '默认设置' }}
</template>
<template #picUrl="{ row }">
<el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
<el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" />
</template>
<template #sliderPicUrls="{ row }">
<el-image
v-for="(item, index) in row.sliderPicUrls"
:key="index"
:src="item.url"
class="w-60px h-60px mr-10px"
class="mr-10px h-60px w-60px"
@click="imagePreview(row.sliderPicUrls)"
/>
</template>
@@ -206,17 +203,17 @@ const ruleConfig: RuleConfig[] = [
{
name: 'price',
rule: (arg) => arg >= 0.01,
message: '商品销售价格必须大于等于 0.01 '
message: '商品销售价格必须大于等于 0.01 '
},
{
name: 'marketPrice',
rule: (arg) => arg >= 0.01,
message: '商品市场价格必须大于等于 0.01 '
message: '商品市场价格必须大于等于 0.01 '
},
{
name: 'costPrice',
rule: (arg) => arg >= 0.01,
message: '商品成本价格必须大于等于 0.01 '
message: '商品成本价格必须大于等于 0.01 '
}
]
@@ -359,23 +356,11 @@ const onChangeSpec = () => {
}
const categoryList = ref([]) // 分类树
/**
* 选择分类时触发校验
*/
const categoryNodeClick = () => {
if (!checkSelectedNode(categoryList.value, formData.categoryId)) {
formData.categoryId = null
message.warning('必须选择二级及以下节点!!')
}
}
/**
* 获取分类的节点的完整结构
*
* @param categoryId 分类id
*/
const categoryString = (categoryId) => {
/** 获取分类的节点的完整结构 */
const formatCategoryName = (categoryId) => {
return treeToString(categoryList.value, categoryId)
}
const brandList = ref([]) // 精简商品品牌列表
const deliveryTemplateList = ref([]) // 运费模版
onMounted(async () => {

View File

@@ -41,7 +41,7 @@
</el-form-item>
</el-col>
<el-col :span="24">
<!-- TODO tag展示暂时不考虑排序 -->
<!-- TODO @puhui999tag展示暂时不考虑排序支持拖动排序 -->
<el-form-item label="活动优先级">
<el-tag>默认</el-tag>
<el-tag class="ml-2" type="success">秒杀</el-tag>

View File

@@ -102,7 +102,7 @@ const getDetail = async () => {
if ('ProductSpuDetail' === name) {
isDetail.value = true
}
const id = params.spuId as unknown as number
const id = params.id as unknown as number
if (id) {
formLoading.value = true
try {
@@ -161,7 +161,7 @@ const submitForm = async () => {
deepCopyFormData.sliderPicUrls = newSliderPicUrls
// 校验都通过后提交表单
const data = deepCopyFormData as ProductSpuApi.Spu
const id = params.spuId as unknown as number
const id = params.id as unknown as number
if (!id) {
await ProductSpuApi.createSpu(data)
message.success(t('common.createSuccess'))

View File

@@ -18,15 +18,14 @@
/>
</el-form-item>
<el-form-item label="商品分类" prop="categoryId">
<el-tree-select
<el-cascader
v-model="queryParams.categoryId"
:data="categoryList"
:options="categoryList"
:props="defaultProps"
check-strictly
class="w-1/1"
node-key="id"
clearable
placeholder="请选择商品分类"
@change="nodeClick"
filterable
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
@@ -78,7 +77,7 @@
/>
</el-tabs>
<el-table v-loading="loading" :data="list">
<el-table-column type="expand" width="30">
<el-table-column type="expand">
<template #default="{ row }">
<el-form class="spu-table-expand" label-position="left">
<el-row>
@@ -86,17 +85,17 @@
<el-row>
<el-col :span="8">
<el-form-item label="商品分类:">
<span>{{ categoryString(row.categoryId) }}</span>
<span>{{ formatCategoryName(row.categoryId) }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="市场价:">
<span>{{ floatToFixed2(row.marketPrice) }}</span>
<span>{{ fenToYuan(row.marketPrice) }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="成本价:">
<span>{{ floatToFixed2(row.costPrice) }}</span>
<span>{{ fenToYuan(row.costPrice) }}</span>
</el-form-item>
</el-col>
</el-row>
@@ -106,9 +105,8 @@
<el-col :span="24">
<el-row>
<el-col :span="8">
<el-form-item label="收藏:">
<!-- TODO 没有这个属性暂时写死 5 -->
<span>5</span>
<el-form-item label="浏览量:">
<span>{{ row.browseCount }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
@@ -122,15 +120,15 @@
</el-form>
</template>
</el-table-column>
<el-table-column key="id" align="center" label="商品编号" prop="id" />
<el-table-column align="center" label="商品编号" min-width="60" prop="id" />
<el-table-column label="商品图" min-width="80">
<template #default="{ row }">
<el-image :src="row.picUrl" class="w-30px h-30px" @click="imagePreview(row.picUrl)" />
<el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" />
</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 }"> {{ floatToFixed2(row.price) }}</template>
<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" />
@@ -152,7 +150,7 @@
active-text="上架"
inactive-text="下架"
inline-prompt
@change="changeStatus(row)"
@change="handleStatusChange(row)"
/>
</template>
<template v-else>
@@ -191,7 +189,7 @@
v-hasPermi="['product:spu:update']"
link
type="primary"
@click="changeStatus(row, ProductSpuStatusEnum.DISABLE.status)"
@click="handleStatus02Change(row, ProductSpuStatusEnum.DISABLE.status)"
>
恢复到仓库
</el-button>
@@ -201,7 +199,7 @@
v-hasPermi="['product:spu:update']"
link
type="primary"
@click="changeStatus(row, ProductSpuStatusEnum.RECYCLE.status)"
@click="handleStatus02Change(row, ProductSpuStatusEnum.RECYCLE.status)"
>
加入回收站
</el-button>
@@ -220,12 +218,11 @@
</template>
<script lang="ts" setup>
import { TabsPaneContext } from 'element-plus'
import { cloneDeep } from 'lodash-es'
import { createImageViewer } from '@/components/ImageViewer'
import { dateFormatter } from '@/utils/formatTime'
import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree'
import { defaultProps, handleTree, treeToString } from '@/utils/tree'
import { ProductSpuStatusEnum } from '@/utils/constants'
import { floatToFixed2 } from '@/utils'
import { fenToYuan } from '@/utils'
import download from '@/utils/download'
import * as ProductSpuApi from '@/api/mall/product/spu'
import * as ProductCategoryApi from '@/api/mall/product/category'
@@ -254,7 +251,7 @@ const tabsData = ref([
},
{
count: 0,
name: '已经售空商品',
name: '已售罄商品',
type: 2
},
{
@@ -303,43 +300,37 @@ const getList = async () => {
}
}
/**
* 更改 SPU 状态
*
* @param row
* @param status 更改前的值
*/
const changeStatus = async (row, status?: number) => {
const deepCopyValue = cloneDeep(unref(row))
if (typeof status !== 'undefined') deepCopyValue.status = status
/** 添加到仓库 / 回收站的状态 */
const handleStatus02Change = async (row, newStatus: number) => {
try {
let text = ''
switch (deepCopyValue.status) {
case ProductSpuStatusEnum.DISABLE.status:
text = ProductSpuStatusEnum.DISABLE.name
break
case ProductSpuStatusEnum.ENABLE.status:
text = ProductSpuStatusEnum.ENABLE.name
break
case ProductSpuStatusEnum.RECYCLE.status:
text = `加入${ProductSpuStatusEnum.RECYCLE.name}`
break
}
await message.confirm(
deepCopyValue.status === -1
? `确认要将[${row.name}]${text}吗?`
: row.status === -1 // 再判断一次原对象是否等于-1例: 把回收站中的商品恢复到仓库中事件触发时原对象status为-1 深拷贝对象status被赋值为0
? `确认要将[${row.name}]恢复到仓库吗?`
: `确认要${text}[${row.name}]吗?`
)
await ProductSpuApi.updateStatus({ id: deepCopyValue.id, status: deepCopyValue.status })
message.success('更新状态成功')
// 二次确认
const text = newStatus === ProductSpuStatusEnum.RECYCLE.status ? '加入到回收站' : '恢复到仓库'
await message.confirm(`确认要"${row.name}"${text}吗?`)
// 发起修改
await ProductSpuApi.updateStatus({ id: row.id, status: newStatus })
message.success(text + '成功')
// 刷新 tabs 数据
await getTabsCount()
// 刷新列表
await getList()
} catch {}
}
/** 更新上架/下架状态 */
const handleStatusChange = async (row) => {
try {
// 二次确认
const text = row.status ? '上架' : '下架'
await message.confirm(`确认要${text}"${row.name}"吗?`)
// 发起修改
await ProductSpuApi.updateStatus({ id: row.id, status: row.status })
message.success(text + '成功')
// 刷新 tabs 数据
await getTabsCount()
// 刷新列表
await getList()
} catch {
// 取消更改状态时回显数据
// 异常时,需要重置回之前的值
row.status =
row.status === ProductSpuStatusEnum.DISABLE.status
? ProductSpuStatusEnum.ENABLE.status
@@ -380,26 +371,20 @@ const resetQuery = () => {
handleQuery()
}
/**
* 新增或修改
*
* @param id 商品 SPU 编号
*/
/** 新增或修改 */
const openForm = (id?: number) => {
// 修改
if (typeof id === 'number') {
push({ name: 'ProductSpuEdit', params: { spuId: id } })
push({ name: 'ProductSpuEdit', params: { id } })
return
}
// 新增
push({ name: 'ProductSpuAdd' })
}
/**
* 查看商品详情
*/
/** 查看商品详情 */
const openDetail = (id: number) => {
push({ name: 'ProductSpuDetail', params: { spuId: id } })
push({ name: 'ProductSpuDetail', params: { id } })
}
/** 导出按钮操作 */
@@ -417,6 +402,12 @@ const handleExport = async () => {
}
}
const categoryList = ref() // 分类树
/** 获取分类的节点的完整结构 */
const formatCategoryName = (categoryId) => {
return treeToString(categoryList.value, categoryId)
}
// 监听路由变化更新列表,解决商品保存后,列表不刷新的问题。
watch(
() => currentRoute.value,
@@ -425,25 +416,6 @@ watch(
}
)
const categoryList = ref() // 分类树
/**
* 获取分类的节点的完整结构
* @param categoryId 分类id
*/
const categoryString = (categoryId) => {
return treeToString(categoryList.value, categoryId)
}
/**
* 校验所选是否为二级及以下节点
*/
const nodeClick = () => {
if (!checkSelectedNode(categoryList.value, queryParams.value.categoryId)) {
queryParams.value.categoryId = null
message.warning('必须选择二级及以下节点!!')
}
}
/** 初始化 **/
onMounted(async () => {
await getTabsCount()