product:优化商品列表的样式、实现代码

This commit is contained in:
YunaiV 2023-10-01 19:44:13 +08:00
parent 654094b4fd
commit 3a9668d632
5 changed files with 68 additions and 96 deletions

View File

@ -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
}, },

View File

@ -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)
} }

View File

@ -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)
@ -377,10 +378,10 @@ export const treeToString = (tree: any[], nodeId) => {
function performAThoroughValidation(arr) { function performAThoroughValidation(arr) {
for (const item of arr) { for (const item of arr) {
if (item.id === nodeId) { if (item.id === nodeId) {
str += `/${item.name}` str += ` / ${item.name}`
return true return true
} else if (typeof item.children !== 'undefined' && item.children.length !== 0) { } else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
str += `/${item.name}` str += ` / ${item.name}`
if (performAThoroughValidation(item.children)) { if (performAThoroughValidation(item.children)) {
return true return true
} }

View File

@ -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 {

View File

@ -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 status0 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()