feat(category): 商品分类和规格属性升级Vue3完成
This commit is contained in:
30
src/api/pms/attribute.ts
Normal file
30
src/api/pms/attribute.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取商品属性列表
|
||||||
|
*
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function listAttributes(params:object) {
|
||||||
|
return request({
|
||||||
|
url: '/mall-pms/api/v1/attributes',
|
||||||
|
method: 'get',
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量修改商品属性
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export function saveAttributeBatch(data:object) {
|
||||||
|
return request({
|
||||||
|
url: '/mall-pms/api/v1/attributes/batch',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,13 +1,166 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div class="components-container">
|
||||||
|
<el-card class="box-card" shadow="always">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-tag type="success" v-if="category && category.name">{{ category.name }} {{ attributeTypeName }} </el-tag>
|
||||||
|
<el-tag v-else type="info"><i class="el-icon-info"></i> 请选择商品分类</el-tag>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" style="text-align: right">
|
||||||
|
<el-button type="primary" :icon="Check" size="mini" @click="submitForm">提交</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row style="margin-top: 10px">
|
||||||
|
<el-form
|
||||||
|
ref="form"
|
||||||
|
:model="formData"
|
||||||
|
:disabled="category?.childrenLen>0"
|
||||||
|
label-width="100"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
v-for="(item, index) in formData.attributes"
|
||||||
|
:label="attributeTypeName + (index+1)"
|
||||||
|
:prop="'attributes.' + index + '.name'"
|
||||||
|
:rules="rules.attribute.name"
|
||||||
|
>
|
||||||
|
<el-input v-model="item.name" style="width: 300px"/>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
v-if="index===0"
|
||||||
|
type="success"
|
||||||
|
:icon="Plus"
|
||||||
|
circle
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
@click.prevent="handleAdd(index)"
|
||||||
|
style="margin-left: 15px"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
:icon="Delete"
|
||||||
|
plain
|
||||||
|
circle
|
||||||
|
size="mini"
|
||||||
|
@click.prevent="handleDelete(index)"
|
||||||
|
style="margin-left: 15px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
export default {
|
import {listAttributes, saveAttributeBatch} from "@/api/pms/attribute";
|
||||||
name: "Attribute"
|
import {computed, reactive, toRefs, watch} from "vue";
|
||||||
|
import {Plus, Check, Delete} from '@element-plus/icons'
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
import {listRoleMenuIds} from "@api/system/role";
|
||||||
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
attributeType: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
type: Object,
|
||||||
|
default: {
|
||||||
|
id: undefined,
|
||||||
|
name: '',
|
||||||
|
childrenLen: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const attributeTypeName = computed(() => props.attributeType === 1 ? '规格' : '属性')
|
||||||
|
|
||||||
|
const attributeNameValidator = (rule: any, value: any, callback: any) => {
|
||||||
|
if (!value) {
|
||||||
|
return callback(new Error('请输入' + attributeTypeName.value + '名称'))
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
formData: {
|
||||||
|
categoryId: undefined,
|
||||||
|
type: 1,
|
||||||
|
attributes: [{
|
||||||
|
id: undefined,
|
||||||
|
name: ''
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
attribute: {
|
||||||
|
name: [
|
||||||
|
{required: true, validator: attributeNameValidator, trigger: 'blur'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const {formData, rules} = toRefs(state)
|
||||||
|
|
||||||
|
watch(() => props.category.id as any, (newVal, oldVal) => {
|
||||||
|
const categoryId = props.category.id
|
||||||
|
if (categoryId) {
|
||||||
|
listAttributes({categoryId: categoryId,type:props.attributeType}).then(response => {
|
||||||
|
|
||||||
|
const {data} = response
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
state.formData.attributes = response.data
|
||||||
|
} else {
|
||||||
|
state.formData.attributes = [{
|
||||||
|
id: undefined,
|
||||||
|
name: ''
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
state.formData.attributes = [{
|
||||||
|
id: undefined,
|
||||||
|
name: ''
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
state.formData.attributes.push({
|
||||||
|
id: undefined,
|
||||||
|
name: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(index: number) {
|
||||||
|
if (state.formData.attributes.length == 1) {
|
||||||
|
state.formData.attributes = [{
|
||||||
|
id: undefined,
|
||||||
|
name: ''
|
||||||
|
}]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.formData.attributes.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitForm() {
|
||||||
|
state.formData.categoryId = props.category.id
|
||||||
|
state.formData.type = props.attributeType
|
||||||
|
saveAttributeBatch(state.formData).then(() => {
|
||||||
|
ElMessage.success('提交成功')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.components-container{
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -13,19 +13,24 @@
|
|||||||
:accordion="true"
|
:accordion="true"
|
||||||
@node-click="handleNodeClick"
|
@node-click="handleNodeClick"
|
||||||
>
|
>
|
||||||
|
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="custom-tree-node">
|
<div class="custom-tree-node">
|
||||||
<span>
|
<span>
|
||||||
<el-image
|
<el-image
|
||||||
v-show="scope.data.level == 3"
|
v-show="scope.data.level == 3 "
|
||||||
:src="scope.data.iconUrl"
|
:src="scope.data.iconUrl"
|
||||||
style="width: 30px; height: 30px;
|
style="width: 20px; height:20px; vertical-align: middle;margin-top: -5px"
|
||||||
vertical-align: middle"
|
>
|
||||||
/>
|
<template #error>
|
||||||
{{ scope.data.name }}
|
<div class="image-slot">
|
||||||
</span>
|
<Picture style="width: 20px; height:20px;"/>
|
||||||
<span>
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
|
||||||
|
{{ scope.data.name }}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
<el-button
|
<el-button
|
||||||
v-show="scope.data.level != 3 "
|
v-show="scope.data.level != 3 "
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -49,7 +54,7 @@
|
|||||||
:icon="Delete"
|
:icon="Delete"
|
||||||
circle
|
circle
|
||||||
plain
|
plain
|
||||||
@click.stop="handleDelete(scope.node, scope.data)"/>
|
@click.stop="handleDelete(scope.data)"/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -65,6 +70,7 @@
|
|||||||
ref="dataFormRef"
|
ref="dataFormRef"
|
||||||
:model="formData"
|
:model="formData"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
|
label-width="100px"
|
||||||
>
|
>
|
||||||
<el-form-item label="上级分类" prop="parentId">
|
<el-form-item label="上级分类" prop="parentId">
|
||||||
<el-input v-model="parent.name" readonly/>
|
<el-input v-model="parent.name" readonly/>
|
||||||
@@ -109,13 +115,13 @@ import {ElForm, ElMessage, ElMessageBox, ElTree} from "element-plus";
|
|||||||
|
|
||||||
const emit = defineEmits(['categoryClick'])
|
const emit = defineEmits(['categoryClick'])
|
||||||
|
|
||||||
const categoryTreeRef=ref(ElTree)
|
const categoryTreeRef = ref(ElTree)
|
||||||
const dataFormRef=ref(ElForm)
|
const dataFormRef = ref(ElForm)
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
// 遮罩层
|
// 遮罩层
|
||||||
loading: true,
|
loading: true,
|
||||||
ids:[],
|
ids: [],
|
||||||
queryParam: {},
|
queryParam: {},
|
||||||
categoryOptions: [] as Array<any>,
|
categoryOptions: [] as Array<any>,
|
||||||
formData: {
|
formData: {
|
||||||
@@ -143,8 +149,7 @@ const state = reactive({
|
|||||||
current: {}
|
current: {}
|
||||||
})
|
})
|
||||||
|
|
||||||
const {loading, categoryOptions, formData, rules, dialog} = toRefs(state)
|
const {loading, categoryOptions, formData, rules, dialog, parent} = toRefs(state)
|
||||||
|
|
||||||
|
|
||||||
function handleQuery() {
|
function handleQuery() {
|
||||||
state.loading = true
|
state.loading = true
|
||||||
@@ -161,8 +166,8 @@ function handleQuery() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNodeClick(row:any) {
|
function handleNodeClick(row: any) {
|
||||||
const categoryTree=unref(categoryTreeRef)
|
const categoryTree = unref(categoryTreeRef)
|
||||||
const parentNode = categoryTree.getNode(row.parentId)
|
const parentNode = categoryTree.getNode(row.parentId)
|
||||||
|
|
||||||
state.parent = {
|
state.parent = {
|
||||||
@@ -197,7 +202,7 @@ function handleUpdate(row: any) {
|
|||||||
title: '修改商品分类',
|
title: '修改商品分类',
|
||||||
visible: true
|
visible: true
|
||||||
}
|
}
|
||||||
Object.assign( state.formData ,state.current)
|
Object.assign(state.formData, state.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitForm() {
|
function submitForm() {
|
||||||
@@ -211,7 +216,12 @@ function submitForm() {
|
|||||||
handleQuery()
|
handleQuery()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
addCategory(state.formData).then(response => {
|
const parentCategory= state.parent as any
|
||||||
|
console.log('parent',parentCategory)
|
||||||
|
state.formData.parentId = parentCategory.id
|
||||||
|
state.formData.level = parentCategory.level+1
|
||||||
|
|
||||||
|
addCategory(state.formData).then(() => {
|
||||||
ElMessage.success('新增成功')
|
ElMessage.success('新增成功')
|
||||||
state.dialog.visible = false
|
state.dialog.visible = false
|
||||||
handleQuery()
|
handleQuery()
|
||||||
@@ -232,10 +242,9 @@ function handleDelete(row: any) {
|
|||||||
ElMessage.success('删除成功')
|
ElMessage.success('删除成功')
|
||||||
handleQuery()
|
handleQuery()
|
||||||
})
|
})
|
||||||
}).catch(() =>
|
})
|
||||||
ElMessage.info('已取消删除')
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
state.formData = {
|
state.formData = {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
@@ -259,6 +268,10 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.component-container {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.custom-tree-node {
|
.custom-tree-node {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<el-col :span="14" :xs="24">
|
<el-col :span="14" :xs="24">
|
||||||
<el-card class="box-card" shadow="always">
|
<el-card class="box-card" shadow="always">
|
||||||
<template #header>
|
<template #header>
|
||||||
<svg-icon color="#333" icon-class="menu"/>
|
<svg-icon icon-class="menu"/>
|
||||||
商品分类
|
商品分类
|
||||||
</template>
|
</template>
|
||||||
<category ref="categoryRef" @categoryClick="handleCategoryClick"/>
|
<category ref="categoryRef" @categoryClick="handleCategoryClick"/>
|
||||||
@@ -14,13 +14,13 @@
|
|||||||
<el-col :span="10" :xs="24">
|
<el-col :span="10" :xs="24">
|
||||||
<el-card class="box-card" shadow="always">
|
<el-card class="box-card" shadow="always">
|
||||||
<template #header>
|
<template #header>
|
||||||
<svg-icon color="#333" icon-class="menu"/>
|
<svg-icon icon-class="menu"/>
|
||||||
商品属性
|
{{category.name}} 规格属性
|
||||||
</template>
|
</template>
|
||||||
<!-- 商品规格 -->
|
<!-- 商品规格 -->
|
||||||
<attribute ref="specificationRef" :attributeType="1"/>
|
<attribute ref="specificationRef" :attributeType="1" :category="category"/>
|
||||||
<!-- 商品属性 -->
|
<!-- 商品属性 -->
|
||||||
<attribute ref="attributeRef" :attributeType="2"/>
|
<attribute ref="attributeRef" :attributeType="2" :category="category"/>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -31,21 +31,33 @@
|
|||||||
|
|
||||||
import Category from './components/Category.vue'
|
import Category from './components/Category.vue'
|
||||||
import Attribute from './components/Attribute.vue'
|
import Attribute from './components/Attribute.vue'
|
||||||
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||||
|
|
||||||
import {reactive} from "vue";
|
import {reactive, toRefs} from "vue";
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
categoryId: undefined,
|
category:{
|
||||||
categoryName: ''
|
id: undefined,
|
||||||
|
name: '',
|
||||||
|
childrenLen: 0
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const {category}=toRefs(state)
|
||||||
|
|
||||||
function handleCategoryClick(categoryRow: any) {
|
function handleCategoryClick(categoryRow: any) {
|
||||||
if (categoryRow) {
|
if (categoryRow) {
|
||||||
state.categoryId = categoryRow.id
|
state.category={
|
||||||
state.categoryName = categoryRow.name
|
id: categoryRow.id,
|
||||||
|
name: categoryRow.name,
|
||||||
|
childrenLen: categoryRow.children.length
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
state.categoryId = undefined
|
state.category={
|
||||||
state.categoryName =''
|
id: undefined,
|
||||||
|
name: '',
|
||||||
|
childrenLen: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user