feat(establish): 添加标签创建功能及模态框组件
- 在establish组件中新增标签创建功能 - 添加标签创建模态框及相关样式 - 实现分类选择下拉框和标签名称输入 - 完善模态框的显示/隐藏逻辑 - 调整部分样式以优化用户体验
This commit is contained in:
@@ -65,6 +65,7 @@
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="功能" name="second">
|
||||
<div>还在开发中.....</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -519,6 +520,9 @@ onUnmounted(() => {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
/* 确保在任何情况下都能居中显示 */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.category-modal-content {
|
||||
@@ -529,6 +533,7 @@ onUnmounted(() => {
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
/* 确保内容块在父容器中完美居中 */
|
||||
}
|
||||
|
||||
.category-modal-header {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<div class="search-box-container" :class="{ 'open': isSearchBoxOpen }">
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索文章..."
|
||||
placeholder="回车搜索文章..."
|
||||
class="search-input"
|
||||
@keyup.enter="performSearch"
|
||||
@blur="closeSearchBoxWithDelay"
|
||||
@@ -402,7 +402,9 @@ onUnmounted(() => {
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.el-input__wrapper{
|
||||
width: 80px;
|
||||
}
|
||||
.search-input {
|
||||
border: none;
|
||||
outline: none;
|
||||
@@ -430,7 +432,7 @@ onUnmounted(() => {
|
||||
/* 防止搜索框在小屏幕上重叠 */
|
||||
@media screen and (max-width: 1200px) {
|
||||
.search-box-container.open {
|
||||
width: 250px;
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,12 +2,8 @@
|
||||
<div class="establish-container">
|
||||
<!-- 弹出的按钮容器 -->
|
||||
<!-- <div class="expanded-buttons" :class="{ 'show': isExpanded }"> -->
|
||||
<button
|
||||
v-for="(btn, index) in isbuttonsave()"
|
||||
:class="['action-button', { 'show': isExpanded }]"
|
||||
:style="getButtonStyle(index)"
|
||||
:key="btn.id"
|
||||
@click="handleButtonClick(btn)">
|
||||
<button v-for="(btn, index) in isbuttonsave()" :class="['action-button', { 'show': isExpanded }]"
|
||||
:style="getButtonStyle(index)" :key="btn.id" @click="handleButtonClick(btn)">
|
||||
<!-- <i :class="btn.icon"></i> -->
|
||||
<span>{{ btn.label }}</span>
|
||||
</button>
|
||||
@@ -18,6 +14,28 @@
|
||||
<i class="icon">+</i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 标签蒙板组件 -->
|
||||
<Transition name="modal">
|
||||
<div v-if="showAttributeModal" class="category-modal" @click.self="closeAttributeModal">
|
||||
<div class="category-modal-content">
|
||||
<div class="category-modal-header">
|
||||
<h3>新建标签</h3>
|
||||
<button class="category-modal-close" @click="closeAttributeModal">×</button>
|
||||
</div>
|
||||
<div class="category-modal-body">
|
||||
|
||||
<el-select v-model="CategoryAttributeDto.categoryid" placeholder="请选择">
|
||||
<el-option v-for="item in categories" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-input v-model="CategoryAttributeDto.name" placeholder="输入新标签" />
|
||||
<el-button type="primary"
|
||||
@click="saveAttribute(CategoryAttributeDto)">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -30,13 +48,29 @@ import { useGlobalStore } from '@/store/globalStore'
|
||||
const globalStore = useGlobalStore()
|
||||
// 路由参数
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
// 分类列表
|
||||
const categories = ref([])
|
||||
//蒙版
|
||||
const showAttributeModal = ref(false)
|
||||
// 选中的分类和新标签
|
||||
const CategoryAttributeDto = ref({
|
||||
categoryid: '',
|
||||
name: ''
|
||||
})
|
||||
// 定义响应式状态
|
||||
const isExpanded = ref(false)
|
||||
// 疯言疯语模态框状态
|
||||
const isNonsenseModalVisible = ref(false)
|
||||
const nonsenseContent = ref('')
|
||||
// 显示标签蒙板
|
||||
const showAttributes = () => {
|
||||
showAttributeModal.value = true
|
||||
}
|
||||
const closeAttributeModal = () => {
|
||||
showAttributeModal.value = false
|
||||
}
|
||||
|
||||
|
||||
// 基础按钮配置
|
||||
const baseButtons = [
|
||||
{ id: 'logout', label: '登出', icon: 'icon-logout' },
|
||||
@@ -52,7 +86,7 @@ const pageButtons = {
|
||||
{ id: 'delete-article', label: '删除', icon: 'icon-create-category' },
|
||||
...baseButtons
|
||||
],
|
||||
|
||||
|
||||
// 首页按钮
|
||||
home: [
|
||||
{ id: 'create-article', label: '新建', icon: 'icon-add-article' },
|
||||
@@ -61,31 +95,32 @@ const pageButtons = {
|
||||
{ id: 'published-articles', label: '已发表', icon: 'icon-new-tag' },
|
||||
...baseButtons
|
||||
],
|
||||
|
||||
|
||||
// 疯言疯语页面按钮
|
||||
nonsense: [
|
||||
{ id: 'create-nonsense', label: '说说', icon: 'icon-upload-file' },
|
||||
...baseButtons
|
||||
],
|
||||
|
||||
|
||||
// 分类页面按钮
|
||||
articlelist: [
|
||||
{ id: 'create-category', label: '新建', icon: 'icon-create-category' },
|
||||
{ id: 'create-category', label: '分类', icon: 'icon-create-category' },
|
||||
{ id: 'create-tag', label: '标签', icon: 'icon-create-tag' },
|
||||
...baseButtons
|
||||
],
|
||||
|
||||
|
||||
// 标签页面按钮
|
||||
tag: [
|
||||
{ id: 'create-tag', label: '新建', icon: 'icon-new-tag' },
|
||||
...baseButtons
|
||||
],
|
||||
|
||||
|
||||
// 文章保存页面按钮
|
||||
articlesave: [
|
||||
{ id: 'view-articles', label: '查看文章列表', icon: 'icon-new-tag' },
|
||||
...baseButtons
|
||||
],
|
||||
|
||||
|
||||
// 默认按钮
|
||||
default: baseButtons
|
||||
}
|
||||
@@ -129,7 +164,7 @@ const toggleExpand = (event) => {
|
||||
const showNonsenseModal = () => {
|
||||
nonsenseContent.value = '' // 清空输入框
|
||||
isNonsenseModalVisible.value = true
|
||||
|
||||
|
||||
ElMessageBox.prompt('请输入您的疯言疯语:', '发表疯言疯语', {
|
||||
confirmButtonText: '保存',
|
||||
cancelButtonText: '取消',
|
||||
@@ -151,7 +186,7 @@ const saveNonsense = (content) => {
|
||||
ElMessage.warning('内容不能为空')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 调用服务保存疯言疯语
|
||||
nonsenseService.saveNonsense({
|
||||
content: content.trim(),
|
||||
@@ -169,7 +204,7 @@ const handleErrorResponse = (error, defaultMessage = '操作失败') => {
|
||||
console.error('操作失败:', error)
|
||||
ElMessage.error(error.message || defaultMessage)
|
||||
}
|
||||
// articlelist新建
|
||||
// 新建分类
|
||||
const createCategory = () => {
|
||||
ElMessageBox.prompt('请输入分类名称:', '新建分类', {
|
||||
confirmButtonText: '保存',
|
||||
@@ -210,6 +245,44 @@ const saveCategory = (typename) => {
|
||||
}
|
||||
}).catch(err => handleErrorResponse(err, '创建分类失败'));
|
||||
};
|
||||
// createAttribute新建标签
|
||||
const createAttribute = async () => {
|
||||
showAttributes()
|
||||
try {
|
||||
const response = await categoryService.getAllCategories();
|
||||
if (response.code === 200) {
|
||||
categories.value = response.data.map(item => ({
|
||||
value: item.typeid,
|
||||
label: item.typename
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
handleErrorResponse(error, '获取分类失败');
|
||||
} finally {
|
||||
console.log(categories.value)
|
||||
}
|
||||
};
|
||||
|
||||
const saveAttribute = (CategoryAttributeDto) => {
|
||||
if (!CategoryAttributeDto.categoryid || !CategoryAttributeDto.name) {
|
||||
ElMessage.warning('请选择分类和输入标签名称')
|
||||
return
|
||||
}
|
||||
categoryAttributeService.createAttribute({
|
||||
categoryid: Number(CategoryAttributeDto.categoryid),
|
||||
attributename: CategoryAttributeDto.name
|
||||
}).then(response => {
|
||||
if (response.code === 200) {
|
||||
closeAttributeModal()
|
||||
ElMessage.success('标签创建成功');
|
||||
// 刷新页面以显示新标签
|
||||
router.push({ path: `/home/aericlelist`});
|
||||
} else {
|
||||
ElMessage.error(response.message || '创建标签失败');
|
||||
}
|
||||
}).catch(err => handleErrorResponse(err, '创建标签失败'));
|
||||
};
|
||||
|
||||
// 删除文章方法
|
||||
const deleteArticle = () => {
|
||||
const articleId = globalStore.getValue('articleInfo')?.id
|
||||
@@ -252,8 +325,8 @@ const updateArticle = () => {
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
globalStore.setValue('updatearticle', articleId)
|
||||
router.push({ path: '/articlesave' })
|
||||
globalStore.setValue('updatearticle', articleId)
|
||||
router.push({ path: '/articlesave' })
|
||||
}).catch(() => {
|
||||
// 取消修改,静默处理
|
||||
})
|
||||
@@ -286,7 +359,7 @@ const reloadPage = () => {
|
||||
// 处理按钮点击事件
|
||||
const handleButtonClick = (button) => {
|
||||
console.log('点击了按钮:', button.id, button.label)
|
||||
|
||||
|
||||
// 使用按钮ID进行处理,更可靠且易于维护
|
||||
switch (button.id) {
|
||||
// 新增操作
|
||||
@@ -294,62 +367,62 @@ const handleButtonClick = (button) => {
|
||||
// 清除更新文章状态
|
||||
router.push({ path: '/articlesave' })
|
||||
break
|
||||
|
||||
|
||||
case 'create-category':
|
||||
createCategory()
|
||||
break
|
||||
|
||||
|
||||
case 'create-tag':
|
||||
// router.push({ path: '/tagsave' })
|
||||
createAttribute()
|
||||
break
|
||||
|
||||
|
||||
case 'create-nonsense':
|
||||
// 显示疯言疯语模态框
|
||||
showNonsenseModal()
|
||||
break
|
||||
|
||||
|
||||
// 修改操作
|
||||
case 'edit-article':
|
||||
updateArticle()
|
||||
break
|
||||
|
||||
|
||||
// 删除操作
|
||||
case 'delete-article':
|
||||
deleteArticle();
|
||||
break
|
||||
|
||||
|
||||
// 查看操作
|
||||
case 'del-articles':
|
||||
getButtonsByStatus(2)
|
||||
break
|
||||
|
||||
|
||||
case 'unpublished-articles':
|
||||
getButtonsByStatus(0)
|
||||
break
|
||||
|
||||
|
||||
case 'published-articles':
|
||||
getButtonsByStatus(1)
|
||||
break
|
||||
|
||||
|
||||
case 'view-articles':
|
||||
router.push({ path: '/home' })
|
||||
break
|
||||
|
||||
|
||||
// 登出操作
|
||||
case 'logout':
|
||||
logout();
|
||||
break
|
||||
|
||||
|
||||
case 'reload':
|
||||
reloadPage()
|
||||
break
|
||||
|
||||
|
||||
default:
|
||||
console.warn('未处理的按钮类型:', button.id, button.label)
|
||||
ElMessage.info(`功能 ${button.label} 暂未实现`)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 点击后收起按钮菜单
|
||||
isExpanded.value = false
|
||||
}
|
||||
@@ -361,7 +434,7 @@ const getButtonStyle = (index) => {
|
||||
// 隐藏时间:0.3s + index * 0.2s (递增)
|
||||
const showDelay = Math.max(0.1, 0.2 + index * 0.2);
|
||||
const hideDelay = 0.3 + index * 0.2;
|
||||
|
||||
|
||||
// 根据是否展开返回不同的过渡样式
|
||||
if (isExpanded.value) {
|
||||
return {
|
||||
@@ -494,6 +567,78 @@ onBeforeUnmount(() => {
|
||||
margin-left: 150px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 分类蒙板样式 */
|
||||
.category-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
/* 确保在任何情况下都能居中显示 */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.category-modal-content {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
/* 确保内容块在父容器中完美居中 */
|
||||
}
|
||||
|
||||
.category-modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.category-modal-header h3 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.category-modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
color: #999;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.category-modal-close:hover {
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.category-modal-body {
|
||||
padding: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 12px;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.establish-container {
|
||||
|
||||
@@ -24,11 +24,12 @@
|
||||
<!-- 计算该分类组中实际有文章的属性数量 -->
|
||||
<span class="badge badge-primary">共 {{ categoryGroup.attributes.reduce((total, cat) => total + (cat.articles && cat.articles.length ? cat.articles.length : 0), 0) }} 篇</span>
|
||||
<ul class="category-item-list">
|
||||
<li v-for="category in categoryGroup.attributes" :key="category.attributeid" >
|
||||
<a class="category-link" @click="handleCategoryClick(category)"><kbd>{{ category.attributename
|
||||
}}</kbd></a>
|
||||
— —({{ category.articles.length }})
|
||||
</li>
|
||||
<div v-for="category in categoryGroup.attributes" :key="category.attributeid">
|
||||
<li v-if="category.articles && category.articles.length > 0">
|
||||
<a class="category-link" @click="handleCategoryClick(category)"><kbd>{{ category.attributename}}</kbd></a>
|
||||
— —({{ category.articles.length }})
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -39,6 +40,7 @@
|
||||
<div v-else class="empty-state-container">
|
||||
<el-empty description="暂无分类" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user