初始化
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, toRefs } from 'vue';
|
||||
|
||||
import { $t } from '@easyflow/locales';
|
||||
import { preferences } from '@easyflow/preferences';
|
||||
|
||||
import { Delete, Edit, MoreFilled, Plus } from '@element-plus/icons-vue';
|
||||
import {
|
||||
ElButton,
|
||||
ElDialog,
|
||||
ElDropdown,
|
||||
ElDropdownItem,
|
||||
ElDropdownMenu,
|
||||
ElEmpty,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElIcon,
|
||||
ElInput,
|
||||
ElMessage,
|
||||
ElMessageBox,
|
||||
} from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '数据管理',
|
||||
},
|
||||
categoryData: {
|
||||
type: Array<Record<string, any>>,
|
||||
default: () => [],
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
},
|
||||
titleKey: {
|
||||
type: String,
|
||||
default: 'title',
|
||||
},
|
||||
panelWidth: {
|
||||
type: Number,
|
||||
default: 200,
|
||||
},
|
||||
valueKey: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
defaultSelectedCategory: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
defaultFormData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
// 暴露给父组件的事件
|
||||
const emit = defineEmits([
|
||||
'add', // 新增提交时触发
|
||||
'edit', // 编辑提交时触发
|
||||
'delete', // 删除时触发
|
||||
'click', // 点击时触发
|
||||
]);
|
||||
|
||||
const finalValueKey = computed(() => {
|
||||
// 父组件传递了 valueKey 就用 valueKey,否则用 titleKey
|
||||
return props.valueKey ?? props.titleKey;
|
||||
});
|
||||
|
||||
const { defaultFormData } = toRefs(props);
|
||||
|
||||
// 状态管理
|
||||
const dialogVisible = ref(false); // 弹窗显隐
|
||||
const isEdit = ref(false); // 是否为编辑模式
|
||||
const formData = ref({
|
||||
...defaultFormData,
|
||||
}); // 表单数据
|
||||
|
||||
/**
|
||||
* 从 Proxy 对象提取原始数据
|
||||
*/
|
||||
const extractFromProxy = (proxyObj: any) => {
|
||||
try {
|
||||
if (proxyObj && typeof proxyObj === 'object') {
|
||||
const raw = (proxyObj as any).__v_raw || proxyObj;
|
||||
|
||||
if (raw && typeof raw === 'object') {
|
||||
const result: any = Array.isArray(raw) ? [] : {};
|
||||
for (const key in raw) {
|
||||
if (
|
||||
key !== '__v_skip' &&
|
||||
Object.prototype.hasOwnProperty.call(raw, key)
|
||||
) {
|
||||
result[key] = extractFromProxy(raw[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
return proxyObj;
|
||||
} catch (error) {
|
||||
console.warn('提取 Proxy 对象失败,使用浅拷贝:', error);
|
||||
return { ...proxyObj };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增按钮点击事件
|
||||
*/
|
||||
const handleAdd = () => {
|
||||
isEdit.value = false;
|
||||
formData.value = {
|
||||
...defaultFormData,
|
||||
};
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 编辑按钮点击事件
|
||||
* @param {object} row - 表格当前行数据
|
||||
*/
|
||||
const handleEdit = (row: any) => {
|
||||
isEdit.value = true;
|
||||
// 使用提取原始数据的方法
|
||||
formData.value = extractFromProxy(row);
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除按钮点击事件
|
||||
* @param {object} row - 表格当前行数据
|
||||
*/
|
||||
const handleDelete = (row: any) => {
|
||||
// 先提取原始数据
|
||||
const rawData = extractFromProxy(row);
|
||||
|
||||
ElMessageBox.confirm(`此操作将永久删除该${props.title}, 是否继续?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
// 触发父组件删除事件,传递原始数据
|
||||
emit('delete', rawData);
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({ type: 'info', message: '已取消删除' });
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单提交事件
|
||||
*/
|
||||
const handleSubmit = () => {
|
||||
// 触发对应事件,传递表单数据
|
||||
if (isEdit.value) {
|
||||
emit('edit', formData.value);
|
||||
} else {
|
||||
emit('add', formData.value);
|
||||
}
|
||||
// 提交后关闭弹窗
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
const selectedCategory = ref();
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化时,检查是否有默认选中的分类
|
||||
if (props.defaultSelectedCategory) {
|
||||
selectedCategory.value = props.defaultSelectedCategory;
|
||||
}
|
||||
});
|
||||
|
||||
const handleCategoryClick = (category: any) => {
|
||||
// 选中值:用 finalValueKey(父组件指定的字段)
|
||||
selectedCategory.value = category[finalValueKey.value];
|
||||
emit('click', {
|
||||
item: extractFromProxy(category),
|
||||
value: category[finalValueKey.value], // 父组件指定字段的值
|
||||
label: category[props.titleKey], // 分类名称
|
||||
});
|
||||
};
|
||||
|
||||
const handleEditClick = (event: any, item: any) => {
|
||||
event.stopPropagation();
|
||||
handleEdit(item);
|
||||
};
|
||||
|
||||
const handleDeleteClick = (event: any, item: any) => {
|
||||
event.stopPropagation();
|
||||
handleDelete(item);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex h-full flex-col rounded-lg border border-[var(--el-border-color)] bg-[var(--el-bg-color)] p-2"
|
||||
:style="{ width: `${panelWidth}px` }"
|
||||
>
|
||||
<div class="flex flex-1 flex-col gap-5">
|
||||
<!-- <h3 class="text-base font-medium">{{ title }}</h3> -->
|
||||
|
||||
<div class="flex-1 overflow-scroll">
|
||||
<div v-for="(item, index) in categoryData" :key="index">
|
||||
<div
|
||||
:class="{ selected: selectedCategory === item[finalValueKey] }"
|
||||
class="crud-category-item"
|
||||
@click="handleCategoryClick(item)"
|
||||
>
|
||||
<div>{{ item[titleKey] }}</div>
|
||||
<div v-if="item.id !== '0'">
|
||||
<ElDropdown @click.stop>
|
||||
<span class="dropdown-trigger">
|
||||
<ElIcon><MoreFilled /></ElIcon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<ElDropdownMenu>
|
||||
<ElDropdownItem @click="handleEditClick($event, item)">
|
||||
<ElButton :icon="Edit" link>
|
||||
{{ $t('button.edit') }}
|
||||
</ElButton>
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem @click="handleDeleteClick($event, item)">
|
||||
<ElButton type="danger" :icon="Delete" link>
|
||||
{{ $t('button.delete') }}
|
||||
</ElButton>
|
||||
</ElDropdownItem>
|
||||
</ElDropdownMenu>
|
||||
</template>
|
||||
</ElDropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ElButton @click="handleAdd" :icon="Plus" :disabled="disabled" plain>
|
||||
{{ $t('button.add') }}
|
||||
</ElButton>
|
||||
|
||||
<!-- 无数据提示 -->
|
||||
<div v-if="categoryData.length === 0 && !loading" class="no-data">
|
||||
<ElEmpty
|
||||
:image="`/empty${preferences.theme.mode === 'dark' ? '-dark' : ''}.png`"
|
||||
:description="$t('common.noDataAvailable')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<ElDialog
|
||||
:title="
|
||||
isEdit ? `${$t('button.edit')}${title}` : `${$t('button.add')}${title}`
|
||||
"
|
||||
v-model="dialogVisible"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<!-- 表单内容(父组件通过插槽配置) -->
|
||||
<slot name="form" :form-data="formData" :is-edit="isEdit"></slot>
|
||||
<ElForm :model="formData" status-icon>
|
||||
<ElFormItem :prop="titleKey">
|
||||
<ElInput v-model.trim="formData[titleKey]" />
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
|
||||
<template #footer>
|
||||
<ElButton @click="dialogVisible = false">
|
||||
{{ $t('button.cancel') }}
|
||||
</ElButton>
|
||||
<ElButton type="primary" @click="handleSubmit">
|
||||
{{ $t('button.confirm') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.crud-category-item:hover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.crud-category-item.selected {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.no-data {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.crud-category-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.crud-category-item :deep(.el-icon) {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.dropdown-trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.dropdown-trigger:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,370 @@
|
||||
<script setup>
|
||||
import { computed, isVNode, onMounted, ref } from 'vue';
|
||||
|
||||
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue';
|
||||
import { ElIcon } from 'element-plus';
|
||||
|
||||
// 定义组件属性
|
||||
const props = defineProps({
|
||||
// 分类数据,格式示例:[{ name: '分类1', icon: SomeIcon }, { name: '分类2' }]
|
||||
categories: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
valueKey: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
titleKey: {
|
||||
type: String,
|
||||
default: 'name',
|
||||
},
|
||||
needHideCollapse: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
iconKey: {
|
||||
type: String,
|
||||
default: 'icon',
|
||||
},
|
||||
// 自定义展开状态宽度(默认300px)
|
||||
expandWidth: {
|
||||
type: Number,
|
||||
default: 120,
|
||||
},
|
||||
// 自定义收缩状态宽度(默认48px)
|
||||
collapseWidth: {
|
||||
type: Number,
|
||||
default: 48,
|
||||
},
|
||||
// 默认选中的分类(用于初始化) 指定key
|
||||
defaultSelectedCategory: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
iconSize: { type: [Number, String], default: 18 },
|
||||
iconColor: { type: String, default: 'var(--el-text-color-primary)' },
|
||||
// 新增:是否用 img 标签渲染 SVG 字符串(默认 false)
|
||||
useImgForSvg: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits([
|
||||
'click', // 分类项点击事件
|
||||
'panelToggle', // 面板收缩状态改变事件
|
||||
]);
|
||||
|
||||
const finalValueKey = computed(() => {
|
||||
// 父组件传递了 valueKey 就用 valueKey,否则用 titleKey
|
||||
return props.valueKey ?? props.titleKey;
|
||||
});
|
||||
|
||||
// -------------------------- 核心工具函数 --------------------------
|
||||
/**
|
||||
* SVG 字符串转 Data URL(供 img 标签使用)
|
||||
* @param {string} svgString - 清理后的 SVG 字符串
|
||||
* @returns {string} Data URL
|
||||
*/
|
||||
const svgToDataUrl = (svgString) => {
|
||||
// 1. 去除 SVG 中的换行和多余空格(优化编码后体积)
|
||||
const cleanedSvg = svgString
|
||||
.replaceAll('\n', '')
|
||||
.replaceAll(/\s+/g, ' ')
|
||||
.trim();
|
||||
// 2. URL 编码 + 拼接 Data URL 格式
|
||||
return `data:image/svg+xml;utf8,${encodeURIComponent(cleanedSvg)}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断是否为组件(Element Plus 图标 / 自定义 SVG 组件)
|
||||
*/
|
||||
const isComponent = (icon) => {
|
||||
return (
|
||||
typeof icon === 'object' && (typeof icon === 'object' || isVNode(icon))
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断是否为 SVG 字符串
|
||||
*/
|
||||
const isSvgString = (icon) => {
|
||||
return typeof icon === 'string' && icon.trim().startsWith('<svg');
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断是否为图片 URL
|
||||
*/
|
||||
const isImageUrl = (icon) => {
|
||||
return (
|
||||
typeof icon === 'string' &&
|
||||
(icon.endsWith('.svg') ||
|
||||
icon.endsWith('.png') ||
|
||||
icon.endsWith('.jpg') ||
|
||||
icon.startsWith('http://') ||
|
||||
icon.startsWith('https://'))
|
||||
);
|
||||
};
|
||||
|
||||
// 面板收缩状态
|
||||
const isCollapsed = ref(false);
|
||||
|
||||
// 检查是否有分类包含图标
|
||||
const hasIcons = computed(() => {
|
||||
return props.categories.some((item) => item[props.iconKey]);
|
||||
});
|
||||
|
||||
// 动态计算面板宽度
|
||||
const panelWidth = computed(() => {
|
||||
if (isCollapsed.value) {
|
||||
// 收缩状态:有图标用自定义收缩宽度,无图标保持最小适配宽度
|
||||
return hasIcons.value ? props.collapseWidth : 120;
|
||||
} else {
|
||||
// 展开状态:使用自定义展开宽度
|
||||
return props.expandWidth;
|
||||
}
|
||||
});
|
||||
|
||||
// 切换面板收缩状态
|
||||
const togglePanel = () => {
|
||||
isCollapsed.value = !isCollapsed.value;
|
||||
emit('panelToggle', {
|
||||
collapsed: isCollapsed.value,
|
||||
currentWidth: panelWidth.value,
|
||||
});
|
||||
};
|
||||
const selectedCategory = ref(null);
|
||||
// 处理分类项点击
|
||||
const handleCategoryClick = (category) => {
|
||||
// 选中值:用 finalValueKey(父组件指定的字段)
|
||||
selectedCategory.value = category[finalValueKey.value];
|
||||
emit('click', {
|
||||
item: category,
|
||||
value: category[finalValueKey.value], // 父组件指定字段的值
|
||||
label: category[props.titleKey], // 分类名称
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化时,检查是否有默认选中的分类
|
||||
if (props.defaultSelectedCategory) {
|
||||
selectedCategory.value = props.defaultSelectedCategory;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="category-panel" :style="{ width: `${panelWidth}px` }">
|
||||
<!-- 右上角收缩/展开按钮 -->
|
||||
<div class="toggle-panel-btn" @click="togglePanel" v-if="!needHideCollapse">
|
||||
<ElIcon>
|
||||
<ArrowLeft v-if="!isCollapsed" />
|
||||
<ArrowRight v-else />
|
||||
</ElIcon>
|
||||
</div>
|
||||
<div style="margin-bottom: 48px" v-if="!needHideCollapse"></div>
|
||||
<!-- 分类列表容器 -->
|
||||
<div class="category-list" :class="{ collapsed: isCollapsed }">
|
||||
<!-- 遍历一级分类数据 -->
|
||||
<div
|
||||
v-for="(category, index) in categories"
|
||||
:key="index"
|
||||
class="category-item"
|
||||
>
|
||||
<div
|
||||
class="category-item-content"
|
||||
:class="{ selected: selectedCategory === category[finalValueKey] }"
|
||||
@click="handleCategoryClick(category)"
|
||||
>
|
||||
<!-- 图标 -->
|
||||
<div v-if="category[iconKey]" class="category-icon">
|
||||
<!-- 1. 组件类型图标(Element Plus / 自定义 SVG 组件) -->
|
||||
<ElIcon v-if="isComponent(category[iconKey])">
|
||||
<component :is="category[iconKey]" />
|
||||
</ElIcon>
|
||||
<!-- 2. SVG 字符串:支持 v-html 或 img 两种渲染方式 -->
|
||||
<template v-else-if="isSvgString(category[iconKey])">
|
||||
<div
|
||||
v-if="!useImgForSvg"
|
||||
v-html="category[iconKey]"
|
||||
class="custom-svg"
|
||||
></div>
|
||||
<img
|
||||
v-else
|
||||
:src="svgToDataUrl(category[iconKey])"
|
||||
:alt="category[titleKey]"
|
||||
class="svg-image"
|
||||
/>
|
||||
</template>
|
||||
<!-- 3. 图片 URL(本地/网络 SVG/PNG) -->
|
||||
<img
|
||||
v-else-if="isImageUrl(category[iconKey])"
|
||||
:src="category[iconKey]"
|
||||
:alt="category[titleKey]"
|
||||
class="svg-image"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 分类名称(收缩状态且有图标时隐藏文字) -->
|
||||
<span
|
||||
class="category-name"
|
||||
:class="{ hidden: isCollapsed && category[iconKey] }"
|
||||
>
|
||||
{{ category[titleKey] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.category-panel {
|
||||
position: relative; /* 相对定位,用于按钮绝对定位 */
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* 平滑宽度过渡 */
|
||||
box-sizing: border-box;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #f0f0f0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 右上角收缩/展开按钮 */
|
||||
.toggle-panel-btn {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
z-index: 10; /* 确保按钮在最上层 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--el-color-white);
|
||||
border: 1px solid #e5e7eb;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
/* 按钮不随面板收缩移动 */
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.toggle-panel-btn:hover {
|
||||
background-color: #f3f4f6;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.toggle-panel-btn .el-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 分类列表容器 */
|
||||
.category-list {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
}
|
||||
|
||||
.category-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.category-item-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
gap: 12px;
|
||||
padding: 6px 0 6px 14px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.category-item-content:hover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: v-bind(iconSize);
|
||||
height: v-bind(iconSize);
|
||||
color: v-bind(iconColor);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.category-icon .el-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.category-item-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
transition:
|
||||
opacity 0.2s,
|
||||
transform 0.2s;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* 收缩状态样式 */
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapsed .category-item-content {
|
||||
justify-content: center;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
/* 收缩状态下文字强制隐藏(避免无图标时文字溢出) */
|
||||
.collapsed .category-name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 新增:选中态样式 */
|
||||
.category-item-content.selected {
|
||||
font-weight: 600;
|
||||
background: rgba(0, 102, 255, 0.06);
|
||||
color: #0066ff;
|
||||
}
|
||||
|
||||
.category-item-content.selected:hover {
|
||||
background: rgba(0, 102, 255, 0.06);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user