feat: 添加商品分类管理页面
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
import request from "@/utils/request";
|
import request from "@/utils/request";
|
||||||
|
|
||||||
export const list=(queryParams:object)=>{
|
/**
|
||||||
|
* 获取商品分类列表
|
||||||
|
*
|
||||||
|
* @param queryParams
|
||||||
|
*/
|
||||||
|
export function listCategories(queryParams:object){
|
||||||
return request({
|
return request({
|
||||||
url: '/mall-pms/api/v1/categories',
|
url: '/mall-pms/api/v1/categories',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -8,7 +13,12 @@ export const list=(queryParams:object)=>{
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cascadeList=(queryParams:object)=> {
|
/**
|
||||||
|
* 获取商品分类级联器树形列表
|
||||||
|
*
|
||||||
|
* @param queryParams
|
||||||
|
*/
|
||||||
|
export function listCascadeCategories(queryParams:object) {
|
||||||
return request({
|
return request({
|
||||||
url: '/mall-pms/api/v1/categories/cascade',
|
url: '/mall-pms/api/v1/categories/cascade',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -16,16 +26,24 @@ export const cascadeList=(queryParams:object)=> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取商品分类详情
|
||||||
export const detail=(id:number)=> {
|
*
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export function getCategoryDetail(id:number){
|
||||||
return request({
|
return request({
|
||||||
url: '/mall-pms/api/v1/categories/' + id,
|
url: '/mall-pms/api/v1/categories/' + id,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const add=(data:object)=> {
|
/**
|
||||||
|
* 添加商品分类
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export function addCategory(data:object){
|
||||||
return request({
|
return request({
|
||||||
url: '/mall-pms/api/v1/categories',
|
url: '/mall-pms/api/v1/categories',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
@@ -33,7 +51,13 @@ export const add=(data:object)=> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update(id:number, data:object) {
|
/**
|
||||||
|
* 修改商品分类
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export function updateCategory(id:number, data:object) {
|
||||||
return request({
|
return request({
|
||||||
url: '/mall-pms/api/v1/categories/' + id,
|
url: '/mall-pms/api/v1/categories/' + id,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
@@ -41,14 +65,25 @@ export function update(id:number, data:object) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function del(ids:string) {
|
/**
|
||||||
|
* 删除商品分类
|
||||||
|
*
|
||||||
|
* @param ids
|
||||||
|
*/
|
||||||
|
export function deleteCategories(ids:string) {
|
||||||
return request({
|
return request({
|
||||||
url: '/mall-pms/api/v1/categories/' + ids,
|
url: '/mall-pms/api/v1/categories/' + ids,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function patch(id:number, data:object) {
|
/**
|
||||||
|
* 选择性修改商品分类
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export function updateCategoryPart(id:number, data:object) {
|
||||||
return request({
|
return request({
|
||||||
url: '/mall-pms/api/v1/categories/' + id,
|
url: '/mall-pms/api/v1/categories/' + id,
|
||||||
method: 'patch',
|
method: 'patch',
|
||||||
|
|||||||
13
src/views/pms/category/components/Attribute.vue
Normal file
13
src/views/pms/category/components/Attribute.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Attribute"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
278
src/views/pms/category/components/Category.vue
Normal file
278
src/views/pms/category/components/Category.vue
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
<!-- 商品分类层级最多为三层,level字段标识 -->
|
||||||
|
<template>
|
||||||
|
<div class="component-container">
|
||||||
|
|
||||||
|
<el-tree
|
||||||
|
v-loading="loading"
|
||||||
|
ref="categoryTreeRef"
|
||||||
|
:data="categoryOptions"
|
||||||
|
:props="{ label: 'name', children: 'children' }"
|
||||||
|
node-key="id"
|
||||||
|
:expand-on-click-node="false"
|
||||||
|
default-expand-all
|
||||||
|
:accordion="true"
|
||||||
|
@node-click="handleNodeClick"
|
||||||
|
>
|
||||||
|
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="custom-tree-node">
|
||||||
|
<span>
|
||||||
|
<el-image
|
||||||
|
v-show="scope.data.level == 3"
|
||||||
|
:src="scope.data.iconUrl"
|
||||||
|
style="width: 30px; height: 30px;
|
||||||
|
vertical-align: middle"
|
||||||
|
/>
|
||||||
|
{{ scope.data.name }}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<el-button
|
||||||
|
v-show="scope.data.level != 3 "
|
||||||
|
type="primary"
|
||||||
|
size="mini"
|
||||||
|
icon="Plus"
|
||||||
|
circle
|
||||||
|
plain
|
||||||
|
@click.stop="handleAdd(scope.data)"/>
|
||||||
|
<el-button
|
||||||
|
v-show="scope.data.id !== 0"
|
||||||
|
type="warning"
|
||||||
|
:icon="Edit"
|
||||||
|
size="mini"
|
||||||
|
circle
|
||||||
|
plain
|
||||||
|
@click.stop="handleUpdate(scope.data)"/>
|
||||||
|
<el-button
|
||||||
|
v-show="scope.data.id && (!scope.data.children || scope.data.children.length <= 0)"
|
||||||
|
type="danger"
|
||||||
|
size="mini"
|
||||||
|
:icon="Delete"
|
||||||
|
circle
|
||||||
|
plain
|
||||||
|
@click.stop="handleDelete(scope.node, scope.data)"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-tree>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
:title="dialog.title"
|
||||||
|
v-model="dialog.visible"
|
||||||
|
width="750px"
|
||||||
|
>
|
||||||
|
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
>
|
||||||
|
<el-form-item label="上级分类" prop="parentId">
|
||||||
|
<el-input v-model="parent.name" readonly/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="分类名称" prop="name">
|
||||||
|
<el-input v-model="formData.name"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="分类图标" prop="iconUrl">
|
||||||
|
<single-upload v-model="formData.iconUrl"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="显示状态" prop="visible">
|
||||||
|
<el-radio-group v-model="formData.visible">
|
||||||
|
<el-radio :label="1">显示</el-radio>
|
||||||
|
<el-radio :label="0">隐藏</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="排序" prop="sort">
|
||||||
|
<el-input v-model="formData.sort"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {listCategories, addCategory, updateCategory, deleteCategories} from '@/api/pms/category'
|
||||||
|
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons'
|
||||||
|
import SingleUpload from '@/components/Upload/SingleUpload.vue'
|
||||||
|
import {getCurrentInstance, onMounted, reactive, ref, toRefs, unref} from "vue";
|
||||||
|
import {ElForm, ElMessage, ElMessageBox, ElTree} from "element-plus";
|
||||||
|
|
||||||
|
const emit = defineEmits(['categoryClick'])
|
||||||
|
|
||||||
|
const categoryTreeRef=ref(ElTree)
|
||||||
|
const dataFormRef=ref(ElForm)
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
// 遮罩层
|
||||||
|
loading: true,
|
||||||
|
ids:[],
|
||||||
|
queryParam: {},
|
||||||
|
categoryOptions: [] as Array<any>,
|
||||||
|
formData: {
|
||||||
|
id: undefined,
|
||||||
|
name: undefined,
|
||||||
|
parentId: 0,
|
||||||
|
level: undefined,
|
||||||
|
iconUrl: undefined,
|
||||||
|
visible: 1,
|
||||||
|
sort: 100
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
parentId: [{
|
||||||
|
required: true, message: '请选择上级分类', trigger: 'blur'
|
||||||
|
}],
|
||||||
|
name: [{
|
||||||
|
required: true, message: '请输入分类名称', trigger: 'blur'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
title: '',
|
||||||
|
visible: false
|
||||||
|
},
|
||||||
|
parent: {},
|
||||||
|
current: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const {loading, categoryOptions, formData, rules, dialog} = toRefs(state)
|
||||||
|
|
||||||
|
|
||||||
|
function handleQuery() {
|
||||||
|
state.loading = true
|
||||||
|
listCategories(state.queryParam).then(response => {
|
||||||
|
state.categoryOptions = [{
|
||||||
|
id: 0,
|
||||||
|
name: '全部分类',
|
||||||
|
parentId: 0,
|
||||||
|
level: 0,
|
||||||
|
children: response.data
|
||||||
|
}]
|
||||||
|
state.loading = false
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNodeClick(row:any) {
|
||||||
|
const categoryTree=unref(categoryTreeRef)
|
||||||
|
const parentNode = categoryTree.getNode(row.parentId)
|
||||||
|
|
||||||
|
state.parent = {
|
||||||
|
id: parentNode.key,
|
||||||
|
name: parentNode.label,
|
||||||
|
level: row.level
|
||||||
|
}
|
||||||
|
state.current = JSON.parse(JSON.stringify(row))
|
||||||
|
emit('categoryClick', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleAdd(row: any) {
|
||||||
|
resetForm()
|
||||||
|
state.dialog = {
|
||||||
|
title: '新增商品分类',
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
if (row.id != null) { // 行点击新增
|
||||||
|
state.parent = {
|
||||||
|
id: row.id,
|
||||||
|
name: row.name,
|
||||||
|
level: row.level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdate(row: any) {
|
||||||
|
resetForm()
|
||||||
|
handleNodeClick(row)
|
||||||
|
state.dialog = {
|
||||||
|
title: '修改商品分类',
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
Object.assign( state.formData ,state.current)
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitForm() {
|
||||||
|
const form = unref(dataFormRef)
|
||||||
|
form.validate((valid: any) => {
|
||||||
|
if (valid) {
|
||||||
|
if (state.formData.id) {
|
||||||
|
updateCategory(state.formData.id, state.formData).then(response => {
|
||||||
|
ElMessage.success('修改成功')
|
||||||
|
state.dialog.visible = false
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
addCategory(state.formData).then(response => {
|
||||||
|
ElMessage.success('新增成功')
|
||||||
|
state.dialog.visible = false
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(row: any) {
|
||||||
|
const ids = [row.id || state.ids].join(',')
|
||||||
|
ElMessageBox.confirm('确认删除已选中的数据项?', '警告', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteCategories(ids).then(() => {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
|
}).catch(() =>
|
||||||
|
ElMessage.info('已取消删除')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function resetForm() {
|
||||||
|
state.formData = {
|
||||||
|
id: undefined,
|
||||||
|
name: undefined,
|
||||||
|
parentId: 0,
|
||||||
|
level: undefined,
|
||||||
|
iconUrl: undefined,
|
||||||
|
visible: 1,
|
||||||
|
sort: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
resetForm()
|
||||||
|
state.dialog.visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.custom-tree-node {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 14px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tree-node__content {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-divider--horizontal {
|
||||||
|
margin: 30px 0 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
55
src/views/pms/category/index.vue
Normal file
55
src/views/pms/category/index.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="14" :xs="24">
|
||||||
|
<el-card class="box-card" shadow="always">
|
||||||
|
<template #header>
|
||||||
|
<svg-icon color="#333" icon-class="menu"/>
|
||||||
|
商品分类
|
||||||
|
</template>
|
||||||
|
<category ref="categoryRef" @categoryClick="handleCategoryClick"/>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="10" :xs="24">
|
||||||
|
<el-card class="box-card" shadow="always">
|
||||||
|
<template #header>
|
||||||
|
<svg-icon color="#333" icon-class="menu"/>
|
||||||
|
商品属性
|
||||||
|
</template>
|
||||||
|
<!-- 商品规格 -->
|
||||||
|
<attribute ref="specificationRef" :attributeType="1"/>
|
||||||
|
<!-- 商品属性 -->
|
||||||
|
<attribute ref="attributeRef" :attributeType="2"/>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import Category from './components/Category.vue'
|
||||||
|
import Attribute from './components/Attribute.vue'
|
||||||
|
|
||||||
|
import {reactive} from "vue";
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
categoryId: undefined,
|
||||||
|
categoryName: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleCategoryClick(categoryRow: any) {
|
||||||
|
if (categoryRow) {
|
||||||
|
state.categoryId = categoryRow.id
|
||||||
|
state.categoryName = categoryRow.name
|
||||||
|
} else {
|
||||||
|
state.categoryId = undefined
|
||||||
|
state.categoryName =''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {Search, Refresh} from '@element-plus/icons'
|
import {Search, Refresh} from '@element-plus/icons'
|
||||||
import {page, removeGoods} from '@/api/pms/goods'
|
import {page, removeGoods} from '@/api/pms/goods'
|
||||||
import {cascadeList} from '@/api/pms/category'
|
import {listCascadeCategories} from '@/api/pms/category'
|
||||||
import {reactive, ref,onMounted, toRefs} from 'vue'
|
import {reactive, ref,onMounted, toRefs} from 'vue'
|
||||||
import {ElMessage, ElMessageBox, ElTree} from 'element-plus'
|
import {ElMessage, ElMessageBox, ElTree} from 'element-plus'
|
||||||
import { getCurrentInstance } from 'vue'
|
import { getCurrentInstance } from 'vue'
|
||||||
@@ -152,7 +152,7 @@ function loadData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadCategoryOptions() {
|
function loadCategoryOptions() {
|
||||||
cascadeList({}).then(response => {
|
listCascadeCategories({}).then(response => {
|
||||||
state.categoryOptions = ref(response.data)
|
state.categoryOptions = ref(response.data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user