910 lines
29 KiB
Vue
910 lines
29 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<!-- 一级分类管理区域 -->
|
||
<el-card shadow="hover" style="margin-bottom: 20px;">
|
||
<div slot="header" class="clearfix">
|
||
<span style="font-weight: bold; color: #409EFF;">一级分类管理</span>
|
||
<div style="float: right;">
|
||
<el-input
|
||
v-model="firstLevelSearch"
|
||
placeholder="请输入一级分类名称"
|
||
clearable
|
||
size="small"
|
||
prefix-icon="el-icon-search"
|
||
style="width: 200px; margin-right: 10px;"
|
||
/>
|
||
<el-button type="primary" size="small" @click="handleAddFirstLevel">
|
||
<i class="el-icon-plus"></i> 添加一级分类
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 一级分类网格布局 -->
|
||
<div class="category-grid">
|
||
<div
|
||
v-for="category in displayedFirstLevelList"
|
||
:key="category.id"
|
||
class="category-card"
|
||
:class="{ active: selectedFirstLevel && selectedFirstLevel.id === category.id }"
|
||
@click="selectFirstLevel(category)"
|
||
>
|
||
<!-- 卡片主体内容 -->
|
||
<div class="card-content">
|
||
<!-- 图标区域 -->
|
||
<div class="card-icon">
|
||
<image-preview
|
||
v-if="category.icon"
|
||
:src="category.icon"
|
||
:width="32"
|
||
:height="32"
|
||
style="border-radius: 4px;"
|
||
/>
|
||
<div v-else class="default-icon-small">
|
||
<i class="el-icon-folder" style="font-size: 20px; color: #409EFF;"></i>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 信息区域 -->
|
||
<div class="card-info">
|
||
<div class="card-title">{{ category.title }}</div>
|
||
<div class="card-meta">
|
||
<dict-tag :options="dict.type.service_sate_type" :value="category.type" size="mini"/>
|
||
<el-tag
|
||
size="mini"
|
||
:type="category.status === 1 ? 'success' : 'danger'"
|
||
effect="light"
|
||
>
|
||
{{ category.status === 1 ? '启用' : '禁用' }}
|
||
</el-tag>
|
||
</div>
|
||
<div class="card-stats">
|
||
<span class="stat-item">
|
||
<i class="el-icon-view"></i> {{ category.browse || 0 }}
|
||
</span>
|
||
<span class="stat-item" v-if="getSubCategoryCount(category.id) > 0">
|
||
<i class="el-icon-document"></i> {{ getSubCategoryCount(category.id) }}
|
||
</span>
|
||
<span class="stat-item">
|
||
<i class="el-icon-sort"></i> {{ category.sort }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作区域 -->
|
||
<div class="card-actions">
|
||
<div class="action-buttons">
|
||
<el-tooltip content="编辑分类" placement="top">
|
||
<el-button
|
||
size="mini"
|
||
type="primary"
|
||
plain
|
||
@click.stop="handleEditFirstLevel(category)"
|
||
class="action-btn edit-btn"
|
||
>
|
||
<i class="el-icon-edit"></i>
|
||
</el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="删除分类" placement="top">
|
||
<el-button
|
||
size="mini"
|
||
type="danger"
|
||
plain
|
||
@click.stop="handleDeleteFirstLevel(category)"
|
||
class="action-btn delete-btn"
|
||
>
|
||
<i class="el-icon-delete"></i>
|
||
</el-button>
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 显示更多按钮和统计信息 -->
|
||
<div class="grid-footer">
|
||
<div class="grid-stats">
|
||
<span class="stats-text">
|
||
显示 {{ displayedFirstLevelList.length }} / {{ filteredFirstLevelList.length }} 个分类
|
||
</span>
|
||
</div>
|
||
<div class="grid-actions">
|
||
<el-button
|
||
v-if="shouldShowMoreButton"
|
||
type="text"
|
||
size="small"
|
||
@click="showAllFirstLevel = true"
|
||
>
|
||
<i class="el-icon-arrow-down"></i> 显示更多 ({{ filteredFirstLevelList.length - defaultShowCount }})
|
||
</el-button>
|
||
<el-button
|
||
v-if="showAllFirstLevel && !firstLevelSearch"
|
||
type="text"
|
||
size="small"
|
||
@click="showAllFirstLevel = false"
|
||
>
|
||
<i class="el-icon-arrow-up"></i> 收起
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 空状态 -->
|
||
<div v-if="firstLevelList.length === 0" class="empty-state">
|
||
<i class="el-icon-folder-add" style="font-size: 48px; color: #ddd;"></i>
|
||
<p style="color: #999; margin-top: 20px;">暂无一级分类</p>
|
||
</div>
|
||
</el-card>
|
||
|
||
<el-card shadow="hover">
|
||
<div slot="header" class="clearfix">
|
||
<span style="font-weight: bold; color: #67C23A;">
|
||
二级分类管理
|
||
<span v-if="selectedFirstLevel" style="color: #909399; font-size: 12px;">
|
||
({{ selectedFirstLevel.title }} · 共{{ filteredSecondLevelList.length }}个)
|
||
</span>
|
||
</span>
|
||
<div style="float: right;">
|
||
<el-input
|
||
v-if="selectedFirstLevel"
|
||
v-model="secondLevelSearch"
|
||
placeholder="请输入二级分类名称"
|
||
clearable
|
||
size="small"
|
||
prefix-icon="el-icon-search"
|
||
style="width: 200px; margin-right: 10px;"
|
||
/>
|
||
<el-button
|
||
v-if="selectedFirstLevel"
|
||
type="success"
|
||
size="small"
|
||
@click="handleAddSecondLevel"
|
||
>
|
||
<i class="el-icon-plus"></i> 添加二级分类
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 未选择一级分类时的提示 -->
|
||
<div v-if="!selectedFirstLevel" class="empty-state">
|
||
<i class="el-icon-info" style="font-size: 48px; color: #ddd;"></i>
|
||
<p style="color: #999; margin-top: 20px;">请先选择上方的一级分类</p>
|
||
</div>
|
||
|
||
<!-- 二级分类表格 -->
|
||
<div v-else>
|
||
<el-table
|
||
:data="filteredSecondLevelList"
|
||
border
|
||
style="width: 100%"
|
||
:empty-text="'暂无二级分类'"
|
||
>
|
||
<el-table-column label="父级分类" width="100" align="center">
|
||
<template>
|
||
<el-tag size="mini" type="primary" effect="light">
|
||
{{ selectedFirstLevel.title }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column prop="title" label="分类名称" width="150">
|
||
<template slot-scope="scope">
|
||
<i class="el-icon-document" style="margin-right: 8px; color: #67C23A;"></i>
|
||
{{ scope.row.title }}
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column prop="sort" label="排序" width="60" align="center">
|
||
<template slot-scope="scope">
|
||
<el-tag size="mini" effect="plain">{{ scope.row.sort }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="状态" width="60" align="center">
|
||
<template slot-scope="scope">
|
||
<el-switch
|
||
v-model="scope.row.status"
|
||
:active-value="1"
|
||
:inactive-value="0"
|
||
@change="handleStatusChange(scope.row)"
|
||
></el-switch>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column prop="browse" label="浏览量" width="70" align="center">
|
||
<template slot-scope="scope">
|
||
<span>{{ scope.row.browse || 0 }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="类型" width="80" align="center">
|
||
<template slot-scope="scope">
|
||
<dict-tag :options="dict.type.service_sate_type" :value="scope.row.type"/>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column prop="icon" label="图标" width="60" align="center">
|
||
<template slot-scope="scope">
|
||
<image-preview v-if="scope.row.icon" :src="scope.row.icon" :width="30" :height="30"/>
|
||
<span v-else style="color: #ccc;">无</span>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="操作" width="100" align="center">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" type="text" @click="handleEditSecondLevel(scope.row)">
|
||
编辑
|
||
</el-button>
|
||
<el-button size="mini" type="text" @click="handleDeleteSecondLevel(scope.row)">
|
||
删除
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 分页组件 -->
|
||
<pagination
|
||
v-show="secondLevelTotal > 0"
|
||
:total="secondLevelTotal"
|
||
:page.sync="secondLevelPageNum"
|
||
:limit.sync="secondLevelPageSize"
|
||
@pagination="getSecondLevelList(selectedFirstLevel ? selectedFirstLevel.id : 0)"
|
||
style="margin-top: 20px;"
|
||
/>
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 添加/编辑一级分类对话框 -->
|
||
<el-dialog :title="firstLevelTitle" :visible.sync="firstLevelDialogVisible" width="500px">
|
||
<el-form ref="firstLevelForm" :model="firstLevelForm" :rules="firstLevelRules" label-width="100px">
|
||
<el-form-item label="分类名称" prop="title">
|
||
<el-input v-model="firstLevelForm.title" placeholder="请输入一级分类名称" />
|
||
</el-form-item>
|
||
<el-form-item label="排序" prop="sort">
|
||
<el-input-number v-model="firstLevelForm.sort" :min="0" :max="9999" style="width: 100%;" />
|
||
</el-form-item>
|
||
<el-form-item label="状态" prop="status">
|
||
<el-radio-group v-model="firstLevelForm.status">
|
||
<el-radio :label="1">启用</el-radio>
|
||
<el-radio :label="0">禁用</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="浏览量" prop="browse">
|
||
<el-input-number v-model="firstLevelForm.browse" :min="0" style="width: 100%;" />
|
||
</el-form-item>
|
||
<el-form-item label="类型" prop="type">
|
||
<el-radio-group v-model="firstLevelForm.type">
|
||
<el-radio
|
||
v-for="dict in dict.type.service_sate_type"
|
||
:key="dict.value"
|
||
:label="parseInt(dict.value)"
|
||
>{{dict.label}}</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="图标" prop="icon">
|
||
<image-upload v-model="firstLevelForm.icon"/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer">
|
||
<el-button @click="firstLevelDialogVisible = false">取 消</el-button>
|
||
<el-button type="primary" @click="submitFirstLevel">确 定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 添加/编辑二级分类对话框 -->
|
||
<el-dialog :title="secondLevelTitle" :visible.sync="secondLevelDialogVisible" width="500px">
|
||
<el-form ref="secondLevelForm" :model="secondLevelForm" :rules="secondLevelRules" label-width="100px">
|
||
<el-form-item label="父级分类">
|
||
<el-input
|
||
:value="selectedFirstLevel ? selectedFirstLevel.title : ''"
|
||
disabled
|
||
>
|
||
<template slot="prepend">
|
||
<i class="el-icon-folder" style="color: #409EFF;"></i>
|
||
</template>
|
||
<template slot="append">
|
||
<dict-tag
|
||
v-if="selectedFirstLevel && selectedFirstLevel.type !== null"
|
||
:options="dict.type.service_sate_type"
|
||
:value="selectedFirstLevel.type"
|
||
size="mini"
|
||
/>
|
||
</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
<el-form-item label="分类名称" prop="title">
|
||
<el-input v-model="secondLevelForm.title" placeholder="请输入二级分类名称" />
|
||
</el-form-item>
|
||
<el-form-item label="排序" prop="sort">
|
||
<el-input-number v-model="secondLevelForm.sort" :min="0" :max="9999" style="width: 100%;" />
|
||
</el-form-item>
|
||
<el-form-item label="状态" prop="status">
|
||
<el-radio-group v-model="secondLevelForm.status">
|
||
<el-radio :label="1">启用</el-radio>
|
||
<el-radio :label="0">禁用</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="浏览量" prop="browse">
|
||
<el-input-number v-model="secondLevelForm.browse" :min="0" style="width: 100%;" />
|
||
</el-form-item>
|
||
<el-form-item label="类型" prop="type">
|
||
<el-input
|
||
:value="getTypeLabel(secondLevelForm.type)"
|
||
disabled
|
||
placeholder="跟随父级分类类型"
|
||
/>
|
||
<div style="font-size: 12px; color: #909399; margin-top: 5px;">
|
||
<i class="el-icon-info"></i> 二级分类的类型自动跟随父级分类
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="图标" prop="icon">
|
||
<image-upload v-model="secondLevelForm.icon"/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer">
|
||
<el-button @click="secondLevelDialogVisible = false">取 消</el-button>
|
||
<el-button type="primary" @click="submitSecondLevel">确 定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listServiceCate, getServiceCate, delServiceCate, addServiceCate, updateServiceCate, changefenleiStatus } from "@/api/system/ServiceCate"
|
||
import { parseTime } from "@/utils/index"
|
||
|
||
export default {
|
||
name: "ServiceCate",
|
||
dicts: ['service_sate_status', 'service_sate_type'],
|
||
data() {
|
||
return {
|
||
// 一级分类相关
|
||
firstLevelList: [],
|
||
selectedFirstLevel: null,
|
||
firstLevelSearch: "",
|
||
firstLevelDialogVisible: false,
|
||
firstLevelTitle: "",
|
||
firstLevelForm: {},
|
||
firstLevelRules: {
|
||
title: [{ required: true, message: "分类名称不能为空", trigger: "blur" }],
|
||
sort: [{ required: true, message: "排序不能为空", trigger: "blur" }],
|
||
status: [{ required: true, message: "状态不能为空", trigger: "change" }],
|
||
type: [{ required: true, message: "类型不能为空", trigger: "change" }],
|
||
},
|
||
|
||
// 二级分类相关
|
||
secondLevelList: [],
|
||
secondLevelSearch: "",
|
||
secondLevelDialogVisible: false,
|
||
secondLevelTitle: "",
|
||
secondLevelForm: {},
|
||
secondLevelRules: {
|
||
title: [{ required: true, message: "分类名称不能为空", trigger: "blur" }],
|
||
sort: [{ required: true, message: "排序不能为空", trigger: "blur" }],
|
||
status: [{ required: true, message: "状态不能为空", trigger: "change" }],
|
||
type: [{ required: true, message: "类型不能为空", trigger: "change" }],
|
||
},
|
||
|
||
// 二级分类分页相关
|
||
secondLevelTotal: 0,
|
||
secondLevelPageNum: 1,
|
||
secondLevelPageSize: 10,
|
||
|
||
// 所有分类数据(用于统计子分类数量)
|
||
allCategoryList: [],
|
||
|
||
// 显示更多控制
|
||
showAllFirstLevel: false,
|
||
defaultShowCount: 20
|
||
}
|
||
},
|
||
|
||
computed: {
|
||
// 过滤后的一级分类
|
||
filteredFirstLevelList() {
|
||
let filtered = this.firstLevelList
|
||
if (this.firstLevelSearch) {
|
||
filtered = this.firstLevelList.filter(item =>
|
||
item.title.toLowerCase().includes(this.firstLevelSearch.toLowerCase())
|
||
)
|
||
}
|
||
return filtered
|
||
},
|
||
|
||
// 显示的一级分类(考虑显示更多逻辑)
|
||
displayedFirstLevelList() {
|
||
if (this.showAllFirstLevel || this.firstLevelSearch) {
|
||
return this.filteredFirstLevelList
|
||
}
|
||
return this.filteredFirstLevelList.slice(0, this.defaultShowCount)
|
||
},
|
||
|
||
// 是否显示"显示更多"按钮
|
||
shouldShowMoreButton() {
|
||
return !this.showAllFirstLevel &&
|
||
!this.firstLevelSearch &&
|
||
this.filteredFirstLevelList.length > this.defaultShowCount
|
||
},
|
||
|
||
// 过滤后的二级分类
|
||
filteredSecondLevelList() {
|
||
if (!this.secondLevelSearch) return this.secondLevelList
|
||
return this.secondLevelList.filter(item =>
|
||
item.title.toLowerCase().includes(this.secondLevelSearch.toLowerCase())
|
||
)
|
||
}
|
||
},
|
||
|
||
created() {
|
||
this.getFirstLevelList()
|
||
},
|
||
|
||
methods: {
|
||
// 时间格式化方法
|
||
parseTime,
|
||
|
||
/** 获取一级分类列表 */
|
||
getFirstLevelList() {
|
||
// 获取一级分类(不分页)
|
||
const params = {
|
||
parentId: 0,
|
||
pageNum: 1,
|
||
pageSize: 1000 // 设置一个很大的数值,确保获取所有一级分类
|
||
}
|
||
listServiceCate(params).then(response => {
|
||
this.firstLevelList = response.rows || []
|
||
})
|
||
|
||
// 获取所有分类数据用于统计(不分页)
|
||
listServiceCate({
|
||
pageNum: 1,
|
||
pageSize: 10000 // 获取所有分类数据
|
||
}).then(response => {
|
||
this.allCategoryList = response.rows || []
|
||
})
|
||
},
|
||
|
||
/** 获取二级分类列表 */
|
||
getSecondLevelList(parentId) {
|
||
const params = {
|
||
parentId: parentId,
|
||
pageNum: this.secondLevelPageNum,
|
||
pageSize: this.secondLevelPageSize
|
||
}
|
||
listServiceCate(params).then(response => {
|
||
this.secondLevelList = response.rows || []
|
||
this.secondLevelTotal = response.total || 0
|
||
})
|
||
},
|
||
|
||
/** 选择一级分类 */
|
||
selectFirstLevel(category) {
|
||
this.selectedFirstLevel = category
|
||
// 重置分页参数
|
||
this.secondLevelPageNum = 1
|
||
this.secondLevelPageSize = 10
|
||
this.secondLevelSearch = ""
|
||
this.getSecondLevelList(category.id)
|
||
},
|
||
|
||
/** 获取子分类数量 */
|
||
getSubCategoryCount(parentId) {
|
||
return this.allCategoryList.filter(item => item.parentId === parentId).length
|
||
},
|
||
|
||
/** 状态修改 */
|
||
handleStatusChange(row) {
|
||
let text = row.status === 0 ? "启用" : "停用"
|
||
this.$modal.confirm('确认要"' + text + '""' + row.title + '"状态吗?').then(function() {
|
||
return changefenleiStatus(row.id, row.status)
|
||
}).then(() => {
|
||
this.$modal.msgSuccess(text + "成功")
|
||
}).catch(function() {
|
||
row.status = row.status === 0 ? 1 : 0
|
||
})
|
||
},
|
||
|
||
// ========== 一级分类操作 ==========
|
||
|
||
/** 添加一级分类 */
|
||
handleAddFirstLevel() {
|
||
this.resetFirstLevelForm()
|
||
this.firstLevelDialogVisible = true
|
||
this.firstLevelTitle = "添加一级分类"
|
||
},
|
||
|
||
/** 编辑一级分类 */
|
||
handleEditFirstLevel(row) {
|
||
this.resetFirstLevelForm()
|
||
getServiceCate(row.id).then(response => {
|
||
this.firstLevelForm = response.data
|
||
this.firstLevelDialogVisible = true
|
||
this.firstLevelTitle = "编辑一级分类"
|
||
})
|
||
},
|
||
|
||
/** 删除一级分类 */
|
||
handleDeleteFirstLevel(row) {
|
||
// 检查是否有二级分类
|
||
const params = { parentId: row.id }
|
||
listServiceCate(params).then(response => {
|
||
if (response.rows && response.rows.length > 0) {
|
||
this.$modal.msgError("该分类下还有二级分类,不能删除")
|
||
return
|
||
}
|
||
|
||
this.$modal.confirm('是否确认删除分类"' + row.title + '"?').then(function() {
|
||
return delServiceCate(row.id)
|
||
}).then(() => {
|
||
this.getFirstLevelList()
|
||
this.$modal.msgSuccess("删除成功")
|
||
// 如果删除的是当前选中的分类,清空选中状态
|
||
if (this.selectedFirstLevel && this.selectedFirstLevel.id === row.id) {
|
||
this.selectedFirstLevel = null
|
||
this.secondLevelList = []
|
||
this.secondLevelTotal = 0
|
||
}
|
||
// 刷新统计数据
|
||
listServiceCate({
|
||
pageNum: 1,
|
||
pageSize: 10000
|
||
}).then(response => {
|
||
this.allCategoryList = response.rows || []
|
||
})
|
||
}).catch(() => {})
|
||
})
|
||
},
|
||
|
||
/** 提交一级分类 */
|
||
submitFirstLevel() {
|
||
this.$refs["firstLevelForm"].validate(valid => {
|
||
if (valid) {
|
||
this.firstLevelForm.parentId = 0 // 设置为一级分类
|
||
|
||
if (this.firstLevelForm.id != null) {
|
||
updateServiceCate(this.firstLevelForm).then(response => {
|
||
this.$modal.msgSuccess("修改成功")
|
||
this.firstLevelDialogVisible = false
|
||
this.getFirstLevelList()
|
||
})
|
||
} else {
|
||
addServiceCate(this.firstLevelForm).then(response => {
|
||
this.$modal.msgSuccess("新增成功")
|
||
this.firstLevelDialogVisible = false
|
||
this.getFirstLevelList()
|
||
})
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
/** 重置一级分类表单 */
|
||
resetFirstLevelForm() {
|
||
this.firstLevelForm = {
|
||
id: null,
|
||
title: null,
|
||
icon: null,
|
||
sort: 0,
|
||
status: 1,
|
||
browse: 0,
|
||
type: 1,
|
||
parentId: 0
|
||
}
|
||
this.resetForm("firstLevelForm")
|
||
},
|
||
|
||
// ========== 二级分类操作 ==========
|
||
|
||
/** 添加二级分类 */
|
||
handleAddSecondLevel() {
|
||
this.resetSecondLevelForm()
|
||
// 自动设置父级分类的类型
|
||
this.secondLevelForm.type = this.selectedFirstLevel ? this.selectedFirstLevel.type : 1
|
||
this.secondLevelDialogVisible = true
|
||
this.secondLevelTitle = "添加二级分类"
|
||
},
|
||
|
||
/** 编辑二级分类 */
|
||
handleEditSecondLevel(row) {
|
||
this.resetSecondLevelForm()
|
||
getServiceCate(row.id).then(response => {
|
||
this.secondLevelForm = response.data
|
||
// 确保类型跟随一级分类
|
||
this.secondLevelForm.type = this.selectedFirstLevel ? this.selectedFirstLevel.type : this.secondLevelForm.type
|
||
this.secondLevelDialogVisible = true
|
||
this.secondLevelTitle = "编辑二级分类"
|
||
})
|
||
},
|
||
|
||
/** 删除二级分类 */
|
||
handleDeleteSecondLevel(row) {
|
||
this.$modal.confirm('是否确认删除分类"' + row.title + '"?').then(function() {
|
||
return delServiceCate(row.id)
|
||
}).then(() => {
|
||
this.getSecondLevelList(this.selectedFirstLevel.id)
|
||
this.$modal.msgSuccess("删除成功")
|
||
// 更新所有分类数据以刷新计数
|
||
listServiceCate({}).then(response => {
|
||
this.allCategoryList = response.rows || []
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
|
||
/** 提交二级分类 */
|
||
submitSecondLevel() {
|
||
this.$refs["secondLevelForm"].validate(valid => {
|
||
if (valid) {
|
||
this.secondLevelForm.parentId = this.selectedFirstLevel ? this.selectedFirstLevel.id : 0 // 设置父级ID
|
||
this.secondLevelForm.type = this.selectedFirstLevel ? this.selectedFirstLevel.type : this.secondLevelForm.type // 确保类型跟随一级分类
|
||
|
||
if (this.secondLevelForm.id != null) {
|
||
updateServiceCate(this.secondLevelForm).then(response => {
|
||
this.$modal.msgSuccess("修改成功")
|
||
this.secondLevelDialogVisible = false
|
||
this.getSecondLevelList(this.selectedFirstLevel.id)
|
||
// 更新所有分类数据以刷新计数
|
||
listServiceCate({}).then(response => {
|
||
this.allCategoryList = response.rows || []
|
||
})
|
||
})
|
||
} else {
|
||
addServiceCate(this.secondLevelForm).then(response => {
|
||
this.$modal.msgSuccess("新增成功")
|
||
this.secondLevelDialogVisible = false
|
||
this.getSecondLevelList(this.selectedFirstLevel.id)
|
||
// 更新所有分类数据以刷新计数
|
||
listServiceCate({}).then(response => {
|
||
this.allCategoryList = response.rows || []
|
||
})
|
||
})
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
/** 重置二级分类表单 */
|
||
resetSecondLevelForm() {
|
||
this.secondLevelForm = {
|
||
id: null,
|
||
title: null,
|
||
icon: null,
|
||
sort: 0,
|
||
status: 1,
|
||
browse: 0,
|
||
type: null, // 不预设类型,在添加时设置
|
||
parentId: null
|
||
}
|
||
this.resetForm("secondLevelForm")
|
||
},
|
||
|
||
/** 获取类型标签 */
|
||
getTypeLabel(value) {
|
||
const dict = this.dict.type.service_sate_type
|
||
const found = dict.find(item => parseInt(item.value) === value)
|
||
return found ? found.label : value
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.app-container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.category-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(10, 1fr);
|
||
gap: 12px;
|
||
padding: 15px 0;
|
||
}
|
||
|
||
.category-card {
|
||
border: 1px solid #e8e8e8;
|
||
border-radius: 12px;
|
||
cursor: pointer;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
background: #fff;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 90px;
|
||
max-width: 120px;
|
||
margin: 0 auto;
|
||
overflow: hidden;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
|
||
}
|
||
|
||
.category-card:hover {
|
||
border-color: #409EFF;
|
||
background-color: #fafbfc;
|
||
box-shadow: 0 8px 25px rgba(64, 158, 255, 0.12);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.category-card.active {
|
||
border-color: #409EFF;
|
||
background: linear-gradient(135deg, #ecf5ff 0%, #f0f9ff 100%);
|
||
box-shadow: 0 8px 25px rgba(64, 158, 255, 0.15);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.card-content {
|
||
padding: 12px 8px 8px 8px;
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.card-icon {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.default-icon-small {
|
||
width: 32px;
|
||
height: 32px;
|
||
background: linear-gradient(135deg, #f5f7fa 0%, #e8f4f8 100%);
|
||
border-radius: 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.card-info {
|
||
flex: 1;
|
||
text-align: center;
|
||
}
|
||
|
||
.card-title {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #1a1a1a;
|
||
margin-bottom: 4px;
|
||
line-height: 1.2;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.card-meta {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 3px;
|
||
margin-bottom: 4px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.card-stats {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-size: 10px;
|
||
color: #909399;
|
||
}
|
||
|
||
.stat-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
}
|
||
|
||
.card-actions {
|
||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||
padding: 8px 6px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
opacity: 0;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
transform: translateY(10px);
|
||
}
|
||
|
||
.category-card:hover .card-actions {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.category-card.active .card-actions {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%);
|
||
border-top-color: rgba(64, 158, 255, 0.1);
|
||
}
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 6px;
|
||
align-items: center;
|
||
}
|
||
|
||
.action-btn {
|
||
width: 28px !important;
|
||
height: 28px !important;
|
||
padding: 0 !important;
|
||
font-size: 12px !important;
|
||
border-radius: 6px !important;
|
||
border-width: 1px !important;
|
||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08) !important;
|
||
}
|
||
|
||
.edit-btn {
|
||
background: linear-gradient(135deg, #e3f2fd 0%, #f0f9ff 100%) !important;
|
||
border-color: #90caf9 !important;
|
||
color: #1976d2 !important;
|
||
}
|
||
|
||
.edit-btn:hover {
|
||
background: linear-gradient(135deg, #1976d2 0%, #1565c0 100%) !important;
|
||
border-color: #1565c0 !important;
|
||
color: #fff !important;
|
||
transform: translateY(-1px) scale(1.05) !important;
|
||
box-shadow: 0 4px 12px rgba(25, 118, 210, 0.3) !important;
|
||
}
|
||
|
||
.delete-btn {
|
||
background: linear-gradient(135deg, #ffebee 0%, #fce4ec 100%) !important;
|
||
border-color: #f8bbd9 !important;
|
||
color: #c62828 !important;
|
||
}
|
||
|
||
.delete-btn:hover {
|
||
background: linear-gradient(135deg, #c62828 0%, #b71c1c 100%) !important;
|
||
border-color: #b71c1c !important;
|
||
color: #fff !important;
|
||
transform: translateY(-1px) scale(1.05) !important;
|
||
box-shadow: 0 4px 12px rgba(198, 40, 40, 0.3) !important;
|
||
}
|
||
|
||
.grid-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20px 0 10px 0;
|
||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||
margin-top: 15px;
|
||
background: linear-gradient(135deg, #fafbfc 0%, #f8f9fa 100%);
|
||
border-radius: 8px;
|
||
margin-left: -10px;
|
||
margin-right: -10px;
|
||
padding-left: 20px;
|
||
padding-right: 20px;
|
||
}
|
||
|
||
.grid-stats {
|
||
flex: 1;
|
||
}
|
||
|
||
.stats-text {
|
||
font-size: 12px;
|
||
color: #6b7280;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.grid-actions {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 60px 0;
|
||
color: #909399;
|
||
}
|
||
|
||
.clearfix:before,
|
||
.clearfix:after {
|
||
display: table;
|
||
content: "";
|
||
}
|
||
|
||
.clearfix:after {
|
||
clear: both;
|
||
}
|
||
</style>
|