优化资产分类管理功能,移除类型字段并新增自定义属性配置功能
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="assets-edit-category-container">
|
||||
<el-dialog :title="isEdit ? '修改分类' : '添加分类'" v-model="isShowDialog" width="500px">
|
||||
<el-dialog :title="isEdit ? '修改分类' : '添加分类'" v-model="isShowDialog" width="700px">
|
||||
<el-form ref="formRef" :model="ruleForm" :rules="rules" size="default" label-width="90px">
|
||||
<el-form-item label="上级分类">
|
||||
<el-cascader
|
||||
@@ -9,22 +9,17 @@
|
||||
:props="{ checkStrictly: true, emitPath: false, value: 'id', label: 'name', children: 'children' }"
|
||||
placeholder="请选择上级分类"
|
||||
clearable
|
||||
:disabled="isEdit || isAddChild"
|
||||
class="w100"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类名称" prop="name">
|
||||
<el-input v-model="ruleForm.name" placeholder="请输入分类名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select v-model="ruleForm.type" placeholder="请选择类型" class="w100">
|
||||
<el-option label="商品" value="product" />
|
||||
<el-option label="服务" value="service" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-form-item label="状态" v-if="!isEdit">
|
||||
<el-switch
|
||||
v-model="ruleForm.status"
|
||||
active-value="1"
|
||||
@@ -34,6 +29,64 @@
|
||||
inactive-text="隐藏"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 自定义属性 -->
|
||||
<el-form-item label="自定义属性">
|
||||
<div class="custom-attrs-container">
|
||||
<div v-for="(attr, index) in ruleForm.attrs" :key="index" class="custom-attr-item">
|
||||
<el-input
|
||||
v-if="attr.type !== 'select' && attr.type !== 'multi_select'"
|
||||
v-model="attr.name"
|
||||
placeholder="属性名称"
|
||||
style="width: 120px"
|
||||
/>
|
||||
<el-select v-model="attr.type" placeholder="属性类型" style="width: 120px" @change="onAttrTypeChange(attr)">
|
||||
<el-option label="文本" value="text" />
|
||||
<el-option label="数字" value="number" />
|
||||
<el-option label="日期" value="date" />
|
||||
<el-option label="单选" value="select" />
|
||||
<el-option label="多选" value="multi_select" />
|
||||
<el-option label="布尔" value="boolean" />
|
||||
<el-option label="图片" value="image" />
|
||||
</el-select>
|
||||
<!-- 单选/多选时显示字典类型选择 -->
|
||||
<el-select
|
||||
v-if="attr.type === 'select' || attr.type === 'multi_select'"
|
||||
v-model="attr.dictKey"
|
||||
placeholder="选择字典类型"
|
||||
style="width: 120px"
|
||||
:loading="dictLoading"
|
||||
@change="onDictKeyChange(attr)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, idx) in dictTypeOptions"
|
||||
:key="idx"
|
||||
:label="item.name || ''"
|
||||
:value="item.name || ''"
|
||||
/>
|
||||
</el-select>
|
||||
<!-- 选择字典类型后显示字典值选择 -->
|
||||
<el-select
|
||||
v-if="attr.dictKey && (attr.type === 'select' || attr.type === 'multi_select')"
|
||||
v-model="attr.dictValues"
|
||||
multiple
|
||||
placeholder="选择字典值"
|
||||
style="width: 150px"
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
:max-collapse-tags="2"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, idx) in getDictValuesByType(attr.dictKey)"
|
||||
:key="idx"
|
||||
:label="item.key"
|
||||
:value="item.key"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="danger" :icon="Delete" circle @click="removeAttr(index)" />
|
||||
</div>
|
||||
<el-button type="primary" :icon="Plus" @click="addAttr">添加属性</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
@@ -54,7 +107,9 @@ export default {
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { Plus, Delete } from '@element-plus/icons-vue';
|
||||
import { getCategoryTree, getCategory, addCategory, updateCategory } from '/@/api/assets/category';
|
||||
import { getDicts } from '/@/api/system/dict/data';
|
||||
|
||||
interface CategoryRow {
|
||||
id: string;
|
||||
@@ -66,13 +121,37 @@ interface CategoryRow {
|
||||
children?: CategoryRow[] | null;
|
||||
}
|
||||
|
||||
interface CustomAttr {
|
||||
name: string;
|
||||
type: string;
|
||||
required?: boolean;
|
||||
multiple?: boolean;
|
||||
options?: string;
|
||||
description?: string;
|
||||
sort?: number;
|
||||
dictKey?: string;
|
||||
dictValues?: string[];
|
||||
}
|
||||
|
||||
interface DictInfo {
|
||||
name: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
interface DictValue {
|
||||
key: string;
|
||||
value: string;
|
||||
isDefault: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
interface RuleForm {
|
||||
id: string;
|
||||
parentId: string;
|
||||
name: string;
|
||||
type: string;
|
||||
sort: number;
|
||||
status: string;
|
||||
attrs: CustomAttr[];
|
||||
}
|
||||
|
||||
const emit = defineEmits(['getCategoryList']);
|
||||
@@ -80,21 +159,96 @@ const emit = defineEmits(['getCategoryList']);
|
||||
const formRef = ref();
|
||||
const isShowDialog = ref(false);
|
||||
const isEdit = ref(false);
|
||||
const isAddChild = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const categoryData = ref<CategoryRow[]>([]);
|
||||
const dictTypeOptions = ref<DictInfo[]>([]);
|
||||
const dictValueOptions = ref<DictValue[]>([]);
|
||||
const dictLoading = ref(false);
|
||||
|
||||
const ruleForm = reactive<RuleForm>({
|
||||
id: '',
|
||||
parentId: '',
|
||||
name: '',
|
||||
type: 'product',
|
||||
sort: 0,
|
||||
status: 'enabled',
|
||||
attrs: [],
|
||||
});
|
||||
|
||||
const rules = {
|
||||
name: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }],
|
||||
type: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
||||
};
|
||||
|
||||
// 获取字典类型数据
|
||||
const fetchDictTypeOptions = () => {
|
||||
dictLoading.value = true;
|
||||
getDicts('assets')
|
||||
.then((res: any) => {
|
||||
console.log('字典接口返回数据:', res);
|
||||
const info = res.data?.info;
|
||||
let infoList: DictInfo[] = [];
|
||||
// 处理info可能是对象或数组的情况
|
||||
if (Array.isArray(info)) {
|
||||
infoList = info;
|
||||
} else if (info && typeof info === 'object') {
|
||||
infoList = [info];
|
||||
}
|
||||
// 过滤掉name为空的项
|
||||
dictTypeOptions.value = infoList.filter((item: DictInfo) => item && item.name);
|
||||
// 保存字典值列表
|
||||
dictValueOptions.value = res.data?.values ?? [];
|
||||
console.log('字典类型选项:', dictTypeOptions.value);
|
||||
console.log('字典值选项:', dictValueOptions.value);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.error('获取字典数据失败:', err);
|
||||
dictTypeOptions.value = [];
|
||||
dictValueOptions.value = [];
|
||||
})
|
||||
.finally(() => {
|
||||
dictLoading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 根据字典类型获取对应的字典值
|
||||
const getDictValuesByType = (dictKey: string) => {
|
||||
if (!dictKey) return [];
|
||||
// 暂时返回所有字典值,后续可根据实际数据结构调整过滤逻辑
|
||||
// 如果需要按类型过滤,可以根据后端返回的数据结构调整
|
||||
return dictValueOptions.value;
|
||||
};
|
||||
|
||||
// 字典类型选择变化时
|
||||
const onDictKeyChange = (attr: CustomAttr) => {
|
||||
// 清空已选的字典值
|
||||
attr.dictValues = [];
|
||||
};
|
||||
|
||||
// 添加自定义属性
|
||||
const addAttr = () => {
|
||||
ruleForm.attrs.push({
|
||||
name: '',
|
||||
type: 'text',
|
||||
dictKey: '',
|
||||
});
|
||||
};
|
||||
|
||||
// 删除自定义属性
|
||||
const removeAttr = (index: number) => {
|
||||
ruleForm.attrs.splice(index, 1);
|
||||
};
|
||||
|
||||
// 属性类型变化时
|
||||
const onAttrTypeChange = (attr: CustomAttr) => {
|
||||
if (attr.type === 'select' || attr.type === 'multi_select') {
|
||||
// 如果字典类型数据还没加载,则加载
|
||||
if (dictTypeOptions.value.length === 0) {
|
||||
fetchDictTypeOptions();
|
||||
}
|
||||
attr.dictKey = '';
|
||||
} else {
|
||||
attr.dictKey = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
@@ -102,15 +256,16 @@ const resetForm = () => {
|
||||
ruleForm.id = '';
|
||||
ruleForm.parentId = '';
|
||||
ruleForm.name = '';
|
||||
ruleForm.type = 'product';
|
||||
ruleForm.sort = 0;
|
||||
ruleForm.status = 'enabled';
|
||||
ruleForm.attrs = [];
|
||||
};
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (row?: CategoryRow | string, edit?: boolean) => {
|
||||
resetForm();
|
||||
isEdit.value = edit || false;
|
||||
isAddChild.value = false;
|
||||
|
||||
// 获取分类树数据
|
||||
getCategoryTree().then((res: any) => {
|
||||
@@ -124,13 +279,18 @@ const openDialog = (row?: CategoryRow | string, edit?: boolean) => {
|
||||
ruleForm.id = data.id || '';
|
||||
ruleForm.parentId = data.parentId || '';
|
||||
ruleForm.name = data.name || '';
|
||||
ruleForm.type = data.type || 'product';
|
||||
ruleForm.sort = data.sort || 0;
|
||||
ruleForm.status = data.status || 'enabled';
|
||||
ruleForm.attrs = data.attrs || [];
|
||||
// 如果有单选/多选属性,预加载字典类型数据
|
||||
if (ruleForm.attrs.some((attr: CustomAttr) => attr.type === 'select' || attr.type === 'multi_select')) {
|
||||
fetchDictTypeOptions();
|
||||
}
|
||||
});
|
||||
} else if (row && typeof row === 'string') {
|
||||
// 新增子分类模式:设置父级ID
|
||||
ruleForm.parentId = row;
|
||||
isAddChild.value = true;
|
||||
}
|
||||
|
||||
isShowDialog.value = true;
|
||||
@@ -190,4 +350,21 @@ defineExpose({
|
||||
.w100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-attrs-container {
|
||||
width: 100%;
|
||||
|
||||
.custom-attr-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.el-button.is-circle {
|
||||
:deep(.el-icon) {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
<el-form-item label="名称">
|
||||
<el-input size="default" v-model="tableData.param.name" placeholder="请输入分类名称" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类">
|
||||
<el-input size="default" v-model="tableData.param.type" placeholder="请输入分类" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button size="default" type="primary" @click="getCategoryList">
|
||||
<el-icon><ele-Search /></el-icon>
|
||||
@@ -30,13 +27,7 @@
|
||||
v-loading="tableData.loading"
|
||||
>
|
||||
<!-- <el-table-column type="index" label="序号" width="80" /> -->
|
||||
<el-table-column prop="name" label="分类名称" show-overflow-tooltip min-width="180" />
|
||||
<el-table-column prop="type" label="类型" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.type === 'product'" type="primary">商品</el-tag>
|
||||
<el-tag v-else-if="scope.row.type === 'service'" type="success">服务</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="分类名称" show-overflow-tooltip min-width="100" />
|
||||
<el-table-column prop="sort" label="排序" width="80" />
|
||||
<el-table-column prop="createdAt" label="创建时间" width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="updatedAt" label="修改时间" width="180" show-overflow-tooltip />
|
||||
|
||||
Reference in New Issue
Block a user