serv\web-侧边栏 标签统一
This commit is contained in:
@@ -7,55 +7,283 @@
|
||||
placeholder="搜索标签..."
|
||||
style="width: 300px"
|
||||
clearable
|
||||
@change="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-table :data="tags" style="width: 100%">
|
||||
<el-table :data="filteredTags" style="width: 100%" v-loading="loading">
|
||||
<el-table-column prop="name" label="标签名称" min-width="150" />
|
||||
<el-table-column prop="category" label="标签分类" width="120" />
|
||||
<el-table-column prop="color" label="颜色" width="100">
|
||||
<el-table-column prop="tagType" label="标签类型" width="120">
|
||||
<template #default="{ row }">
|
||||
<div class="color-preview" :style="{ background: row.color }"></div>
|
||||
<el-tag :type="getTagTypeColor(row.tagType)">
|
||||
{{ getTagTypeName(row.tagType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="color" label="颜色" width="120">
|
||||
<template #default="{ row }">
|
||||
<div class="color-display">
|
||||
<div class="color-preview" :style="{ background: row.color }"></div>
|
||||
<span>{{ row.color }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'info'">
|
||||
{{ row.status === 1 ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="orderNum" label="排序" width="80" />
|
||||
<el-table-column prop="createTime" label="创建时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ formatTime(row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="usageCount" label="使用次数" width="100" />
|
||||
<el-table-column prop="createDate" label="创建时间" width="150" />
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="editTag(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteTag(row)">删除</el-button>
|
||||
<el-button link type="primary" size="small" @click="editTag(row)">编辑</el-button>
|
||||
<el-button link type="danger" size="small" @click="deleteTag(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 创建/编辑标签对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="isEdit ? '编辑标签' : '新增标签'"
|
||||
width="500px"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<el-form :model="currentTag" :rules="rules" ref="formRef" label-width="100px">
|
||||
<el-form-item label="标签名称" prop="name">
|
||||
<el-input v-model="currentTag.name" placeholder="请输入标签名称" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="标签类型" prop="tagType">
|
||||
<el-select v-model="currentTag.tagType" placeholder="请选择标签类型" style="width: 100%">
|
||||
<el-option label="文章分类标签" :value="1" />
|
||||
<el-option label="课程分类标签" :value="2" />
|
||||
<el-option label="学习任务分类标签" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="标签颜色" prop="color">
|
||||
<div class="color-picker-wrapper">
|
||||
<el-color-picker v-model="currentTag.color" />
|
||||
<el-input v-model="currentTag.color" placeholder="#000000" style="width: 150px; margin-left: 10px" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="标签描述">
|
||||
<el-input
|
||||
v-model="currentTag.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入标签描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="排序号" prop="orderNum">
|
||||
<el-input-number v-model="currentTag.orderNum" :min="0" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="currentTag.status">
|
||||
<el-radio :label="1">启用</el-radio>
|
||||
<el-radio :label="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitting">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ElButton, ElInput, ElTable, ElTableColumn, ElMessage } from 'element-plus';
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
|
||||
import { resourceTagApi } from '@/apis/resource';
|
||||
import type { Tag } from '@/types/resource';
|
||||
|
||||
const searchKeyword = ref('');
|
||||
const tags = ref<any[]>([]);
|
||||
const tags = ref<Tag[]>([]);
|
||||
const loading = ref(false);
|
||||
const dialogVisible = ref(false);
|
||||
const isEdit = ref(false);
|
||||
const submitting = ref(false);
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const currentTag = ref<Partial<Tag>>({
|
||||
name: '',
|
||||
tagType: 1,
|
||||
color: '#409EFF',
|
||||
description: '',
|
||||
orderNum: 0,
|
||||
status: 1
|
||||
});
|
||||
|
||||
const rules: FormRules = {
|
||||
name: [
|
||||
{ required: true, message: '请输入标签名称', trigger: 'blur' }
|
||||
],
|
||||
tagType: [
|
||||
{ required: true, message: '请选择标签类型', trigger: 'change' }
|
||||
],
|
||||
color: [
|
||||
{ required: true, message: '请选择标签颜色', trigger: 'change' }
|
||||
]
|
||||
};
|
||||
|
||||
// 过滤后的标签列表
|
||||
const filteredTags = computed(() => {
|
||||
if (!searchKeyword.value) return tags.value;
|
||||
const keyword = searchKeyword.value.toLowerCase();
|
||||
return tags.value.filter(tag =>
|
||||
tag.name?.toLowerCase().includes(keyword) ||
|
||||
tag.description?.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadTags();
|
||||
});
|
||||
|
||||
function loadTags() {
|
||||
// TODO: 加载标签数据
|
||||
// 加载标签列表
|
||||
async function loadTags() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const result = await resourceTagApi.getTagList();
|
||||
if (result.success) {
|
||||
tags.value = result.dataList || [];
|
||||
} else {
|
||||
ElMessage.error(result.message || '加载标签列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载标签列表失败:', error);
|
||||
ElMessage.error('加载标签列表失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
function handleSearch() {
|
||||
// 搜索由computed自动处理
|
||||
}
|
||||
|
||||
// 显示创建对话框
|
||||
function showCreateDialog() {
|
||||
// TODO: 显示创建标签对话框
|
||||
isEdit.value = false;
|
||||
currentTag.value = {
|
||||
name: '',
|
||||
tagType: 1,
|
||||
color: '#409EFF',
|
||||
description: '',
|
||||
orderNum: 0,
|
||||
status: 1
|
||||
};
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function editTag(row: any) {
|
||||
// TODO: 编辑标签
|
||||
// 编辑标签
|
||||
function editTag(row: Tag) {
|
||||
isEdit.value = true;
|
||||
currentTag.value = { ...row };
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function deleteTag(row: any) {
|
||||
// TODO: 删除标签
|
||||
ElMessage.success('删除成功');
|
||||
// 删除标签
|
||||
async function deleteTag(row: Tag) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除标签"${row.name}"吗?删除后不可恢复。`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
);
|
||||
|
||||
const result = await resourceTagApi.deleteTag(row.tagID!);
|
||||
if (result.success) {
|
||||
ElMessage.success('删除成功');
|
||||
loadTags();
|
||||
} else {
|
||||
ElMessage.error(result.message || '删除失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除标签失败:', error);
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
async function handleSubmit() {
|
||||
if (!formRef.value) return;
|
||||
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
submitting.value = true;
|
||||
|
||||
let result;
|
||||
if (isEdit.value) {
|
||||
result = await resourceTagApi.updateTag(currentTag.value as Tag);
|
||||
} else {
|
||||
result = await resourceTagApi.createTag(currentTag.value as Tag);
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
ElMessage.success(isEdit.value ? '更新成功' : '创建成功');
|
||||
dialogVisible.value = false;
|
||||
loadTags();
|
||||
} else {
|
||||
ElMessage.error(result.message || (isEdit.value ? '更新失败' : '创建失败'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 对话框关闭处理
|
||||
function handleDialogClose() {
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
|
||||
// 获取标签类型名称
|
||||
function getTagTypeName(type?: number): string {
|
||||
const map: Record<number, string> = {
|
||||
1: '文章分类',
|
||||
2: '课程分类',
|
||||
3: '任务分类'
|
||||
};
|
||||
return map[type || 1] || '未知';
|
||||
}
|
||||
|
||||
// 获取标签类型颜色
|
||||
function getTagTypeColor(type?: number): string {
|
||||
const map: Record<number, string> = {
|
||||
1: 'primary',
|
||||
2: 'success',
|
||||
3: 'warning'
|
||||
};
|
||||
return map[type || 1] || '';
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
function formatTime(time?: string): string {
|
||||
if (!time) return '-';
|
||||
return new Date(time).toLocaleString('zh-CN');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -71,11 +299,21 @@ function deleteTag(row: any) {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.color-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
width: 40px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
</style>
|
||||
|
||||
.color-picker-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -68,8 +68,8 @@
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ElButton, ElInput, ElTable, ElTableColumn, ElTag, ElPagination, ElMessage } from 'element-plus';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { resourceApi, resourceCategoryApi } from '@/apis/resource'
|
||||
import type { PageParam, ResourceSearchParams, Resource, ResourceCategory } from '@/types';
|
||||
import { resourceApi, resourceTagApi } from '@/apis/resource'
|
||||
import type { PageParam, ResourceSearchParams, Resource, Tag } from '@/types';
|
||||
import { ArticleShowView } from '@/views/article';
|
||||
import { ArticleStatus } from '@/types/enums';
|
||||
|
||||
@@ -86,7 +86,7 @@ const total = ref<number>(0);
|
||||
const articles = ref<Resource[]>([]);
|
||||
const showViewDialog = ref(false);
|
||||
const currentArticle = ref<any>(null);
|
||||
const categoryList = ref<ResourceCategory[]>([]);
|
||||
const categoryList = ref<Tag[]>([]); // 改为使用Tag类型(tagType=1表示文章分类)
|
||||
|
||||
onMounted(() => {
|
||||
loadArticles();
|
||||
@@ -95,7 +95,8 @@ onMounted(() => {
|
||||
|
||||
async function loadCategories() {
|
||||
try {
|
||||
const res = await resourceCategoryApi.getCategoryList();
|
||||
// 使用新的标签API获取文章分类标签(tagType=1)
|
||||
const res = await resourceTagApi.getTagsByType(1); // 1 = 文章分类标签
|
||||
if (res.success && res.dataList) {
|
||||
categoryList.value = res.dataList;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user