feat(establish): 添加标签创建功能及模态框组件
- 在establish组件中新增标签创建功能 - 添加标签创建模态框及相关样式 - 实现分类选择下拉框和标签名称输入 - 完善模态框的显示/隐藏逻辑 - 调整部分样式以优化用户体验
This commit is contained in:
@@ -65,6 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="功能" name="second">
|
<el-tab-pane label="功能" name="second">
|
||||||
|
<div>还在开发中.....</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
@@ -519,6 +520,9 @@ onUnmounted(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
|
/* 确保在任何情况下都能居中显示 */
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-modal-content {
|
.category-modal-content {
|
||||||
@@ -529,6 +533,7 @@ onUnmounted(() => {
|
|||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
/* 确保内容块在父容器中完美居中 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-modal-header {
|
.category-modal-header {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="search-box-container" :class="{ 'open': isSearchBoxOpen }">
|
<div class="search-box-container" :class="{ 'open': isSearchBoxOpen }">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchKeyword"
|
v-model="searchKeyword"
|
||||||
placeholder="搜索文章..."
|
placeholder="回车搜索文章..."
|
||||||
class="search-input"
|
class="search-input"
|
||||||
@keyup.enter="performSearch"
|
@keyup.enter="performSearch"
|
||||||
@blur="closeSearchBoxWithDelay"
|
@blur="closeSearchBoxWithDelay"
|
||||||
@@ -402,7 +402,9 @@ onUnmounted(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
.el-input__wrapper{
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
.search-input {
|
.search-input {
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -430,7 +432,7 @@ onUnmounted(() => {
|
|||||||
/* 防止搜索框在小屏幕上重叠 */
|
/* 防止搜索框在小屏幕上重叠 */
|
||||||
@media screen and (max-width: 1200px) {
|
@media screen and (max-width: 1200px) {
|
||||||
.search-box-container.open {
|
.search-box-container.open {
|
||||||
width: 250px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -2,12 +2,8 @@
|
|||||||
<div class="establish-container">
|
<div class="establish-container">
|
||||||
<!-- 弹出的按钮容器 -->
|
<!-- 弹出的按钮容器 -->
|
||||||
<!-- <div class="expanded-buttons" :class="{ 'show': isExpanded }"> -->
|
<!-- <div class="expanded-buttons" :class="{ 'show': isExpanded }"> -->
|
||||||
<button
|
<button v-for="(btn, index) in isbuttonsave()" :class="['action-button', { 'show': isExpanded }]"
|
||||||
v-for="(btn, index) in isbuttonsave()"
|
:style="getButtonStyle(index)" :key="btn.id" @click="handleButtonClick(btn)">
|
||||||
:class="['action-button', { 'show': isExpanded }]"
|
|
||||||
:style="getButtonStyle(index)"
|
|
||||||
:key="btn.id"
|
|
||||||
@click="handleButtonClick(btn)">
|
|
||||||
<!-- <i :class="btn.icon"></i> -->
|
<!-- <i :class="btn.icon"></i> -->
|
||||||
<span>{{ btn.label }}</span>
|
<span>{{ btn.label }}</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -18,6 +14,28 @@
|
|||||||
<i class="icon">+</i>
|
<i class="icon">+</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -30,13 +48,29 @@ import { useGlobalStore } from '@/store/globalStore'
|
|||||||
const globalStore = useGlobalStore()
|
const globalStore = useGlobalStore()
|
||||||
// 路由参数
|
// 路由参数
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
// 分类列表
|
||||||
|
const categories = ref([])
|
||||||
|
//蒙版
|
||||||
|
const showAttributeModal = ref(false)
|
||||||
|
// 选中的分类和新标签
|
||||||
|
const CategoryAttributeDto = ref({
|
||||||
|
categoryid: '',
|
||||||
|
name: ''
|
||||||
|
})
|
||||||
// 定义响应式状态
|
// 定义响应式状态
|
||||||
const isExpanded = ref(false)
|
const isExpanded = ref(false)
|
||||||
// 疯言疯语模态框状态
|
// 疯言疯语模态框状态
|
||||||
const isNonsenseModalVisible = ref(false)
|
const isNonsenseModalVisible = ref(false)
|
||||||
const nonsenseContent = ref('')
|
const nonsenseContent = ref('')
|
||||||
|
// 显示标签蒙板
|
||||||
|
const showAttributes = () => {
|
||||||
|
showAttributeModal.value = true
|
||||||
|
}
|
||||||
|
const closeAttributeModal = () => {
|
||||||
|
showAttributeModal.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 基础按钮配置
|
// 基础按钮配置
|
||||||
const baseButtons = [
|
const baseButtons = [
|
||||||
{ id: 'logout', label: '登出', icon: 'icon-logout' },
|
{ id: 'logout', label: '登出', icon: 'icon-logout' },
|
||||||
@@ -70,7 +104,8 @@ const pageButtons = {
|
|||||||
|
|
||||||
// 分类页面按钮
|
// 分类页面按钮
|
||||||
articlelist: [
|
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
|
...baseButtons
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -169,7 +204,7 @@ const handleErrorResponse = (error, defaultMessage = '操作失败') => {
|
|||||||
console.error('操作失败:', error)
|
console.error('操作失败:', error)
|
||||||
ElMessage.error(error.message || defaultMessage)
|
ElMessage.error(error.message || defaultMessage)
|
||||||
}
|
}
|
||||||
// articlelist新建
|
// 新建分类
|
||||||
const createCategory = () => {
|
const createCategory = () => {
|
||||||
ElMessageBox.prompt('请输入分类名称:', '新建分类', {
|
ElMessageBox.prompt('请输入分类名称:', '新建分类', {
|
||||||
confirmButtonText: '保存',
|
confirmButtonText: '保存',
|
||||||
@@ -210,6 +245,44 @@ const saveCategory = (typename) => {
|
|||||||
}
|
}
|
||||||
}).catch(err => handleErrorResponse(err, '创建分类失败'));
|
}).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 deleteArticle = () => {
|
||||||
const articleId = globalStore.getValue('articleInfo')?.id
|
const articleId = globalStore.getValue('articleInfo')?.id
|
||||||
@@ -252,8 +325,8 @@ const updateArticle = () => {
|
|||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
globalStore.setValue('updatearticle', articleId)
|
globalStore.setValue('updatearticle', articleId)
|
||||||
router.push({ path: '/articlesave' })
|
router.push({ path: '/articlesave' })
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// 取消修改,静默处理
|
// 取消修改,静默处理
|
||||||
})
|
})
|
||||||
@@ -300,7 +373,7 @@ const handleButtonClick = (button) => {
|
|||||||
break
|
break
|
||||||
|
|
||||||
case 'create-tag':
|
case 'create-tag':
|
||||||
// router.push({ path: '/tagsave' })
|
createAttribute()
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'create-nonsense':
|
case 'create-nonsense':
|
||||||
@@ -494,6 +567,78 @@ onBeforeUnmount(() => {
|
|||||||
margin-left: 150px;
|
margin-left: 150px;
|
||||||
opacity: 1;
|
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) {
|
@media (max-width: 768px) {
|
||||||
.establish-container {
|
.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>
|
<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">
|
<ul class="category-item-list">
|
||||||
<li v-for="category in categoryGroup.attributes" :key="category.attributeid" >
|
<div v-for="category in categoryGroup.attributes" :key="category.attributeid">
|
||||||
<a class="category-link" @click="handleCategoryClick(category)"><kbd>{{ category.attributename
|
<li v-if="category.articles && category.articles.length > 0">
|
||||||
}}</kbd></a>
|
<a class="category-link" @click="handleCategoryClick(category)"><kbd>{{ category.attributename}}</kbd></a>
|
||||||
— —({{ category.articles.length }})
|
— —({{ category.articles.length }})
|
||||||
</li>
|
</li>
|
||||||
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
<div v-else class="empty-state-container">
|
<div v-else class="empty-state-container">
|
||||||
<el-empty description="暂无分类" />
|
<el-empty description="暂无分类" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user