product:优化商品列表的样式、实现代码
This commit is contained in:
parent
654094b4fd
commit
3a9668d632
@ -331,9 +331,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/product',
|
path: '/mall/product', // 商品中心
|
||||||
component: Layout,
|
component: Layout,
|
||||||
name: 'Product',
|
|
||||||
meta: {
|
meta: {
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
@ -348,11 +347,11 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
canTo: true,
|
canTo: true,
|
||||||
icon: 'ep:edit',
|
icon: 'ep:edit',
|
||||||
title: '添加商品',
|
title: '添加商品',
|
||||||
activeMenu: '/product/product-spu'
|
activeMenu: '/mall/product/spu'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'spu/edit/:spuId(\\d+)',
|
path: 'spu/edit/:id(\\d+)',
|
||||||
component: () => import('@/views/mall/product/spu/form/index.vue'),
|
component: () => import('@/views/mall/product/spu/form/index.vue'),
|
||||||
name: 'ProductSpuEdit',
|
name: 'ProductSpuEdit',
|
||||||
meta: {
|
meta: {
|
||||||
@ -361,11 +360,11 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
canTo: true,
|
canTo: true,
|
||||||
icon: 'ep:edit',
|
icon: 'ep:edit',
|
||||||
title: '编辑商品',
|
title: '编辑商品',
|
||||||
activeMenu: '/product/product-spu'
|
activeMenu: '/mall/product/spu'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'spu/detail/:spuId(\\d+)',
|
path: 'spu/detail/:id(\\d+)',
|
||||||
component: () => import('@/views/mall/product/spu/form/index.vue'),
|
component: () => import('@/views/mall/product/spu/form/index.vue'),
|
||||||
name: 'ProductSpuDetail',
|
name: 'ProductSpuDetail',
|
||||||
meta: {
|
meta: {
|
||||||
@ -374,7 +373,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
canTo: true,
|
canTo: true,
|
||||||
icon: 'ep:view',
|
icon: 'ep:view',
|
||||||
title: '商品详情',
|
title: '商品详情',
|
||||||
activeMenu: '/product/product-spu'
|
activeMenu: '/mall/product/spu'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -393,9 +392,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/trade',
|
path: '/trade', // 交易中心
|
||||||
component: Layout,
|
component: Layout,
|
||||||
name: 'Order',
|
|
||||||
meta: {
|
meta: {
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
@ -230,6 +230,7 @@ export const yuanToFen = (amount: string | number): number => {
|
|||||||
/**
|
/**
|
||||||
* 分转元
|
* 分转元
|
||||||
*/
|
*/
|
||||||
export const fenToYuan = (amount: string | number): number => {
|
export const fenToYuan = (price: string | number): number => {
|
||||||
return Number((Number(amount) / 100).toFixed(2))
|
price = Number(price)
|
||||||
|
return (price / 100.0).toFixed(2)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@ export const defaultProps = {
|
|||||||
children: 'children',
|
children: 'children',
|
||||||
label: 'name',
|
label: 'name',
|
||||||
value: 'id',
|
value: 'id',
|
||||||
isLeaf: 'leaf'
|
isLeaf: 'leaf',
|
||||||
|
emitPath: false // 用于 cascader 组件:在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值
|
||||||
}
|
}
|
||||||
|
|
||||||
const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
|
const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
|
||||||
|
@ -102,7 +102,7 @@ const getDetail = async () => {
|
|||||||
if ('ProductSpuDetail' === name) {
|
if ('ProductSpuDetail' === name) {
|
||||||
isDetail.value = true
|
isDetail.value = true
|
||||||
}
|
}
|
||||||
const id = params.spuId as unknown as number
|
const id = params.id as unknown as number
|
||||||
if (id) {
|
if (id) {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
|
@ -18,15 +18,14 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="商品分类" prop="categoryId">
|
<el-form-item label="商品分类" prop="categoryId">
|
||||||
<el-tree-select
|
<el-cascader
|
||||||
v-model="queryParams.categoryId"
|
v-model="queryParams.categoryId"
|
||||||
:data="categoryList"
|
:options="categoryList"
|
||||||
:props="defaultProps"
|
:props="defaultProps"
|
||||||
check-strictly
|
|
||||||
class="w-1/1"
|
class="w-1/1"
|
||||||
node-key="id"
|
clearable
|
||||||
placeholder="请选择商品分类"
|
placeholder="请选择商品分类"
|
||||||
@change="nodeClick"
|
filterable
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="创建时间" prop="createTime">
|
<el-form-item label="创建时间" prop="createTime">
|
||||||
@ -78,7 +77,7 @@
|
|||||||
/>
|
/>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-table v-loading="loading" :data="list">
|
||||||
<el-table-column type="expand" width="30">
|
<el-table-column type="expand">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-form class="spu-table-expand" label-position="left">
|
<el-form class="spu-table-expand" label-position="left">
|
||||||
<el-row>
|
<el-row>
|
||||||
@ -86,17 +85,17 @@
|
|||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="商品分类:">
|
<el-form-item label="商品分类:">
|
||||||
<span>{{ categoryString(row.categoryId) }}</span>
|
<span>{{ formatCategoryName(row.categoryId) }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="市场价:">
|
<el-form-item label="市场价:">
|
||||||
<span>{{ floatToFixed2(row.marketPrice) }}元</span>
|
<span>{{ fenToYuan(row.marketPrice) }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="成本价:">
|
<el-form-item label="成本价:">
|
||||||
<span>{{ floatToFixed2(row.costPrice) }}元</span>
|
<span>{{ fenToYuan(row.costPrice) }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -106,9 +105,8 @@
|
|||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="收藏:">
|
<el-form-item label="浏览量:">
|
||||||
<!-- TODO 没有这个属性,暂时写死 5 个 -->
|
<span>{{ row.browseCount }}</span>
|
||||||
<span>5</span>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@ -122,7 +120,7 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</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">
|
<el-table-column label="商品图" min-width="80">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" />
|
<el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" />
|
||||||
@ -130,7 +128,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
|
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
|
||||||
<el-table-column align="center" label="商品售价" min-width="90" prop="price">
|
<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>
|
||||||
<el-table-column align="center" label="销量" min-width="90" prop="salesCount" />
|
<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="90" prop="stock" />
|
||||||
@ -152,7 +150,7 @@
|
|||||||
active-text="上架"
|
active-text="上架"
|
||||||
inactive-text="下架"
|
inactive-text="下架"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
@change="changeStatus(row)"
|
@change="handleStatusChange(row)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@ -191,7 +189,7 @@
|
|||||||
v-hasPermi="['product:spu:update']"
|
v-hasPermi="['product:spu:update']"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="changeStatus(row, ProductSpuStatusEnum.DISABLE.status)"
|
@click="handleStatus02Change(row, ProductSpuStatusEnum.DISABLE.status)"
|
||||||
>
|
>
|
||||||
恢复到仓库
|
恢复到仓库
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -201,7 +199,7 @@
|
|||||||
v-hasPermi="['product:spu:update']"
|
v-hasPermi="['product:spu:update']"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="changeStatus(row, ProductSpuStatusEnum.RECYCLE.status)"
|
@click="handleStatus02Change(row, ProductSpuStatusEnum.RECYCLE.status)"
|
||||||
>
|
>
|
||||||
加入回收站
|
加入回收站
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -220,12 +218,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TabsPaneContext } from 'element-plus'
|
import { TabsPaneContext } from 'element-plus'
|
||||||
import { cloneDeep } from 'lodash-es'
|
|
||||||
import { createImageViewer } from '@/components/ImageViewer'
|
import { createImageViewer } from '@/components/ImageViewer'
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
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 { ProductSpuStatusEnum } from '@/utils/constants'
|
||||||
import { floatToFixed2 } from '@/utils'
|
import { fenToYuan } from '@/utils'
|
||||||
import download from '@/utils/download'
|
import download from '@/utils/download'
|
||||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
import * as ProductCategoryApi from '@/api/mall/product/category'
|
import * as ProductCategoryApi from '@/api/mall/product/category'
|
||||||
@ -254,7 +251,7 @@ const tabsData = ref([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
count: 0,
|
count: 0,
|
||||||
name: '已经售空商品',
|
name: '已售罄商品',
|
||||||
type: 2
|
type: 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -303,43 +300,37 @@ const getList = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 添加到仓库 / 回收站的状态 */
|
||||||
* 更改 SPU 状态
|
const handleStatus02Change = async (row, newStatus: number) => {
|
||||||
*
|
|
||||||
* @param row
|
|
||||||
* @param status 更改前的值
|
|
||||||
*/
|
|
||||||
const changeStatus = async (row, status?: number) => {
|
|
||||||
const deepCopyValue = cloneDeep(unref(row))
|
|
||||||
if (typeof status !== 'undefined') deepCopyValue.status = status
|
|
||||||
try {
|
try {
|
||||||
let text = ''
|
// 二次确认
|
||||||
switch (deepCopyValue.status) {
|
const text = newStatus === ProductSpuStatusEnum.RECYCLE.status ? '加入到回收站' : '恢复到仓库'
|
||||||
case ProductSpuStatusEnum.DISABLE.status:
|
await message.confirm(`确认要"${row.name}"${text}吗?`)
|
||||||
text = ProductSpuStatusEnum.DISABLE.name
|
// 发起修改
|
||||||
break
|
await ProductSpuApi.updateStatus({ id: row.id, status: newStatus })
|
||||||
case ProductSpuStatusEnum.ENABLE.status:
|
message.success(text + '成功')
|
||||||
text = ProductSpuStatusEnum.ENABLE.name
|
// 刷新 tabs 数据
|
||||||
break
|
await getTabsCount()
|
||||||
case ProductSpuStatusEnum.RECYCLE.status:
|
// 刷新列表
|
||||||
text = `加入${ProductSpuStatusEnum.RECYCLE.name}`
|
await getList()
|
||||||
break
|
} catch {}
|
||||||
}
|
}
|
||||||
await message.confirm(
|
|
||||||
deepCopyValue.status === -1
|
/** 更新上架/下架状态 */
|
||||||
? `确认要将[${row.name}]${text}吗?`
|
const handleStatusChange = async (row) => {
|
||||||
: row.status === -1 // 再判断一次原对象是否等于-1,例: 把回收站中的商品恢复到仓库中,事件触发时原对象status为-1 深拷贝对象status被赋值为0
|
try {
|
||||||
? `确认要将[${row.name}]恢复到仓库吗?`
|
// 二次确认
|
||||||
: `确认要${text}[${row.name}]吗?`
|
const text = row.status ? '上架' : '下架'
|
||||||
)
|
await message.confirm(`确认要${text}"${row.name}"吗?`)
|
||||||
await ProductSpuApi.updateStatus({ id: deepCopyValue.id, status: deepCopyValue.status })
|
// 发起修改
|
||||||
message.success('更新状态成功')
|
await ProductSpuApi.updateStatus({ id: row.id, status: row.status })
|
||||||
|
message.success(text + '成功')
|
||||||
// 刷新 tabs 数据
|
// 刷新 tabs 数据
|
||||||
await getTabsCount()
|
await getTabsCount()
|
||||||
// 刷新列表
|
// 刷新列表
|
||||||
await getList()
|
await getList()
|
||||||
} catch {
|
} catch {
|
||||||
// 取消更改状态时回显数据
|
// 异常时,需要重置回之前的值
|
||||||
row.status =
|
row.status =
|
||||||
row.status === ProductSpuStatusEnum.DISABLE.status
|
row.status === ProductSpuStatusEnum.DISABLE.status
|
||||||
? ProductSpuStatusEnum.ENABLE.status
|
? ProductSpuStatusEnum.ENABLE.status
|
||||||
@ -380,26 +371,20 @@ const resetQuery = () => {
|
|||||||
handleQuery()
|
handleQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 新增或修改 */
|
||||||
* 新增或修改
|
|
||||||
*
|
|
||||||
* @param id 商品 SPU 编号
|
|
||||||
*/
|
|
||||||
const openForm = (id?: number) => {
|
const openForm = (id?: number) => {
|
||||||
// 修改
|
// 修改
|
||||||
if (typeof id === 'number') {
|
if (typeof id === 'number') {
|
||||||
push({ name: 'ProductSpuEdit', params: { spuId: id } })
|
push({ name: 'ProductSpuEdit', params: { id } })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 新增
|
// 新增
|
||||||
push({ name: 'ProductSpuAdd' })
|
push({ name: 'ProductSpuAdd' })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 查看商品详情 */
|
||||||
* 查看商品详情
|
|
||||||
*/
|
|
||||||
const openDetail = (id: number) => {
|
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(
|
watch(
|
||||||
() => currentRoute.value,
|
() => 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 () => {
|
onMounted(async () => {
|
||||||
await getTabsCount()
|
await getTabsCount()
|
||||||
|
Loading…
Reference in New Issue
Block a user