Browse Source

普法服务,轮播图,公告管理列表和新增页面的编写

dev-V1.0.1
wangmingyong 5 months ago
parent
commit
449d59ab54
  1. 69
      zdxt-web-client/zdxt-admin-plus-ui/src/api/supervision/carouselBanner.ts
  2. 66
      zdxt-web-client/zdxt-admin-plus-ui/src/api/supervision/noticeInfo.ts
  3. 38
      zdxt-web-client/zdxt-admin-plus-ui/src/router/index.ts
  4. 262
      zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/carouselBanner/add.vue
  5. 394
      zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/carouselBanner/index.vue
  6. 287
      zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/educationServices/add.vue
  7. 347
      zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/educationServices/index.vue
  8. 161
      zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/noticeInfo/add.vue
  9. 348
      zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/noticeInfo/index.vue

69
zdxt-web-client/zdxt-admin-plus-ui/src/api/supervision/carouselBanner.ts

@ -0,0 +1,69 @@
import request from '@/utils/request';
export interface CarouselBannerQuery {
title?: string;
startTime?: string;
endTime?: string;
department?: string;
category?: string;
publishTo?: string;
pageNum?: number;
pageSize?: number;
}
export interface CarouselBannerItem {
id: number;
title: string;
publishTime: string;
category: string;
department: string;
publishTo: string;
viewCount: string;
}
export interface CarouselBannerResponse {
code: number;
msg: string;
data: {
total: number;
list: CarouselBannerItem[];
};
}
export function listCarouselBanner(query: CarouselBannerQuery) {
return request<CarouselBannerResponse>({
url: '/web/enforcementcode/educationServices/list',
method: 'get',
params: query
});
}
export function getCarouselBanner(id: number) {
return request({
url: `/web/supervision/carouselBanner/${id}`,
method: 'get'
});
}
export function addCarouselBanner(data: any) {
return request({
url: '/web/supervision/carouselBanner',
method: 'post',
data
});
}
export function updateCarouselBanner(data: any) {
return request({
url: '/web/supervision/carouselBanner',
method: 'put',
data
});
}
export function deleteCarouselBanner(id: number) {
return request({
url: `/web/supervision/carouselBanner/${id}`,
method: 'delete'
});
}

66
zdxt-web-client/zdxt-admin-plus-ui/src/api/supervision/noticeInfo.ts

@ -0,0 +1,66 @@
import request from '@/utils/request';
export interface NoticeInfoQuery {
content?: string;
startTime?: string;
endTime?: string;
publishTo?: string;
pageNum?: number;
pageSize?: number;
}
export interface NoticeInfoItem {
id: number;
content: string;
publishTime: string;
displayDays: string;
publishTo: string;
status: string;
}
export interface NoticeInfoResponse {
code: number;
msg: string;
data: {
total: number;
list: NoticeInfoItem[];
};
}
export function listNoticeInfo(query: NoticeInfoQuery) {
return request<NoticeInfoResponse>({
url: '/web/supervision/noticeInfo/list',
method: 'get',
params: query
});
}
export function getNoticeInfo(id: number) {
return request({
url: `/web/supervision/noticeInfo/${id}`,
method: 'get'
});
}
export function addNoticeInfo(data: any) {
return request({
url: '/web/supervision/noticeInfo',
method: 'post',
data
});
}
export function updateNoticeInfo(data: any) {
return request({
url: '/web/supervision/noticeInfo',
method: 'put',
data
});
}
export function deleteNoticeInfo(id: number) {
return request({
url: `/web/supervision/noticeInfo/${id}`,
method: 'delete'
});
}

38
zdxt-web-client/zdxt-admin-plus-ui/src/router/index.ts

@ -83,6 +83,44 @@ export const constantRoutes: RouteRecordRaw[] = [
meta: { title: '个人中心', icon: 'user' }
}
]
},
{
path: '/supervision/carouselBanner/add',
component: Layout,
hidden: true,
children: [
{
path: '',
component: () => import('@/views/supervision/carouselBanner/add.vue'),
name: 'CarouselBannerAdd',
meta: { title: '新增轮播图', icon: 'plus' }
}
]
},
{
path: '/supervision/noticeInfo',
component: Layout,
children: [
{
path: '',
component: () => import('@/views/supervision/noticeInfo/index.vue'),
name: 'NoticeInfo',
meta: { title: '公共管理', icon: 'info' }
}
]
},
{
path: '/supervision/noticeInfo/add',
component: Layout,
hidden: true,
children: [
{
path: '',
component: () => import('@/views/supervision/noticeInfo/add.vue'),
name: 'NoticeInfoAdd',
meta: { title: '新增公告', icon: 'plus' }
}
]
}
];

262
zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/carouselBanner/add.vue

@ -0,0 +1,262 @@
<template>
<div class="carousel-add-container">
<div class="page-header">
<h2>新增轮播图</h2>
</div>
<div class="form-wrapper">
<el-form :model="form" ref="formRef" :rules="rules" label-width="100px" class="form-container">
<el-form-item label="标题" prop="title">
<el-input v-model="form.title" placeholder="请输入标题" class="form-input" />
</el-form-item>
<el-form-item label="内容发布到" prop="publishTo">
<el-select v-model="form.publishTo" placeholder="请选择" class="form-select">
<el-option label="企业端" value="enterprise" />
<el-option label="监管端" value="supervision" />
<el-option label="企业端+监管端" value="both" />
</el-select>
</el-form-item>
<el-form-item label="橱窗选择" prop="windowSelect">
<el-select v-model="form.windowSelect" placeholder="请选择" class="form-select">
<el-option label="橱窗1" value="window1" />
<el-option label="橱窗2" value="window2" />
<el-option label="橱窗3" value="window3" />
<el-option label="橱窗4" value="window4" />
<el-option label="橱窗5" value="window5" />
</el-select>
</el-form-item>
<el-form-item label="显示天数" prop="displayDays">
<el-input v-model.number="form.displayDays" placeholder="请输入天数" class="form-input" />
</el-form-item>
<el-form-item label="链接" prop="link">
<el-input v-model="form.link" placeholder="请输入链接" class="form-input" />
</el-form-item>
<el-form-item label="橱窗图" prop="windowImage">
<div class="upload-area">
<div class="upload-box" @click="handleUpload">
<div class="upload-icon">
<span>+</span>
</div>
<span class="upload-text">上传</span>
</div>
<p class="upload-hint">建议比例 16:9,小于5M的JPG、PNG格式图片</p>
<div v-if="form.windowImage" class="preview-image">
<img :src="form.windowImage" alt="橱窗图预览" />
<span class="remove-btn" @click="removeImage">×</span>
</div>
</div>
</el-form-item>
</el-form>
<div class="form-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button @click="handleSaveDraft">保存草稿</el-button>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
const router = useRouter();
const formRef = ref();
const form = reactive({
title: '',
publishTo: '',
windowSelect: '',
displayDays: '',
link: '',
windowImage: ''
});
const rules = {
title: [
{ required: true, message: '请输入标题', trigger: 'blur' }
],
publishTo: [
{ required: true, message: '请选择内容发布到', trigger: 'change' }
],
windowSelect: [
{ required: true, message: '请选择橱窗', trigger: 'change' }
],
displayDays: [
{ required: true, message: '请输入显示天数', trigger: 'blur' },
{ type: 'number' as const, min: 1, message: '显示天数必须大于0', trigger: 'blur' }
],
windowImage: [
{ required: true, message: '请上传橱窗图', trigger: 'blur' }
]
};
const handleUpload = () => {
ElMessage.info('图片上传功能开发中');
form.windowImage = 'https://via.placeholder.com/900x100?text=橱窗图预览';
};
const removeImage = () => {
form.windowImage = '';
};
const handleCancel = () => {
router.push('/supervision/carouselBanner');
};
const handleSaveDraft = () => {
ElMessage.success('草稿已保存');
};
const handleSubmit = async () => {
if (!formRef.value) return;
try {
const isValid = await formRef.value.validate();
if (!isValid) {
return;
}
ElMessage.success('提交成功');
router.push('/supervision/carouselBanner');
} catch (error) {
console.error('表单验证失败:', error);
}
};
</script>
<style scoped>
.carousel-add-container {
padding: 20px;
background: #f5f7fa;
min-height: calc(100vh - 100px);
}
.page-header {
background: #fff;
padding: 16px 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.page-header h2 {
margin: 0;
font-size: 18px;
font-weight: bold;
color: #303133;
}
.form-wrapper {
background: #fff;
padding: 24px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.form-container {
max-width: 600px;
}
:deep(.el-form-item__label) {
font-weight: bold;
white-space: nowrap;
}
:deep(.el-form-item.is-required:not(.is-no-asterisk)>.el-form-item__label)::before {
content: '*';
color: #f56c6c;
margin-right: 4px;
}
.form-input,
.form-select {
width: 100%;
max-width: 400px;
}
.upload-area {
margin-top: 10px;
}
.upload-box {
width: 100%;
max-width: 400px;
border: 2px dashed #d9d9d9;
border-radius: 8px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.upload-box:hover {
border-color: #409eff;
background: #f0f5ff;
}
.upload-icon {
width: 48px;
height: 48px;
margin: 0 auto 12px;
border: 2px dashed #d9d9d9;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: #909399;
}
.upload-text {
color: #909399;
}
.upload-hint {
margin: 12px 0 0;
font-size: 12px;
color: #909399;
}
.preview-image {
margin-top: 12px;
position: relative;
max-width: 400px;
}
.preview-image img {
width: 100%;
border-radius: 4px;
}
.remove-btn {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
background: rgba(0, 0, 0, 0.5);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 16px;
}
.form-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 32px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
}
</style>

394
zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/carouselBanner/index.vue

@ -0,0 +1,394 @@
<template>
<div class="carousel-container">
<!-- 搜索区域 -->
<div class="search-panel">
<el-form :model="searchForm" inline>
<el-form-item label="标题:">
<el-input v-model="searchForm.title" placeholder="请输入关键字" class="search-input" clearable
@keyup.enter="handleSearch" />
</el-form-item>
<el-form-item label="发布时间:">
<el-date-picker v-model="searchForm.timeRange" type="datetimerange" range-separator="至"
start-placeholder="开始时间" end-placeholder="结束时间" class="date-picker" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
<el-form-item label="内容发布到:">
<el-select v-model="searchForm.publishTo" placeholder="请选择" class="search-select" clearable>
<el-option label="企业端" value="enterprise" />
<el-option label="执法端" value="law_enforcement" />
<el-option label="监管端" value="supervision" />
</el-select>
</el-form-item>
<el-form-item label="状态:">
<el-select v-model="searchForm.status" placeholder="请选择" class="search-select" clearable>
<el-option label="在线" value="online" />
<el-option label="下架" value="offline" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch" :loading="loading">搜索</el-button>
</el-form-item>
<el-form-item>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 操作栏 -->
<div class="action-bar">
<div class="action-left">
<el-button type="primary" @click="handleAdd">新增</el-button>
<el-button @click="handleBatchDelete" :disabled="selectedRows.length === 0">删除</el-button>
</div>
</div>
<!-- 数据表格 -->
<div class="table-container">
<el-table v-loading="loading" :data="tableData" border
:header-cell-style="{ backgroundColor: '#f5f7fa', fontWeight: 'bold' }"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="title" label="标题" min-width="200" show-overflow-tooltip />
<el-table-column prop="publishTime" label="发布时间" width="180" />
<el-table-column prop="displayDays" label="显示时长" width="120" />
<el-table-column prop="publishTo" label="内容发布到" width="150" />
<el-table-column prop="windowName" label="橱窗" width="120" />
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<div class="status-badge">
<span :class="scope.row.status === 'online' ? 'status-dot online' : 'status-dot offline'"></span>
{{ scope.row.status === 'online' ? '在线' : '下架' }}
</div>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right">
<template #default="scope">
<el-button size="small" type="text" @click="handleView(scope.row)">查看</el-button>
<el-button size="small" type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="text" :class="scope.row.status === 'online' ? 'offline-btn' : 'online-btn'"
@click="handleStatusChange(scope.row)">
{{ scope.row.status === 'online' ? '下架' : '上架' }}
</el-button>
<el-button size="small" type="text" class="delete-btn" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination-container">
<span class="total-text">共 {{ total }} 条数据</span>
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :total="total"
:page-sizes="[10, 20, 50, 100]" layout="prev, pager, next, jumper, ->, total, sizes"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useRouter } from 'vue-router';
import { listCarouselBanner, deleteCarouselBanner } from '@/api/supervision/carouselBanner';
const router = useRouter();
const loading = ref(false);
const tableData = ref<any[]>([]);
const total = ref(0);
const currentPage = ref(1);
const pageSize = ref(10);
const selectedRows = ref<any[]>([]);
const searchForm = reactive({
title: '',
timeRange: [] as string[],
publishTo: '',
status: ''
});
const getList = async () => {
loading.value = true;
try {
const queryParams = {
...searchForm,
startTime: searchForm.timeRange[0] || '',
endTime: searchForm.timeRange[1] || '',
pageNum: currentPage.value,
pageSize: pageSize.value
};
delete queryParams.timeRange;
const res: any = await listCarouselBanner(queryParams);
if (res.code === 200) {
tableData.value = res.rows || [];
total.value = res.total || 0;
} else {
ElMessage.error(res.msg || '获取数据失败');
}
} catch (error) {
console.error('获取列表数据失败:', error);
ElMessage.error('获取数据失败,请检查网络连接');
} finally {
loading.value = false;
}
};
const handleSearch = () => {
currentPage.value = 1;
getList();
};
const handleReset = () => {
searchForm.title = '';
searchForm.timeRange = [];
searchForm.publishTo = '';
searchForm.status = '';
currentPage.value = 1;
handleSearch();
ElMessage.info('已重置搜索条件');
};
const handleAdd = () => {
router.push('/supervision/carouselBanner/add');
};
const handleBatchDelete = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请选择要删除的数据');
return;
}
ElMessageBox.confirm(`确定要删除选中的 ${selectedRows.value.length} 条数据吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
ElMessage.success('批量删除成功');
getList();
selectedRows.value = [];
}).catch(() => {
ElMessage.info('已取消删除');
});
};
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection;
};
const handleTop = (row: any) => {
ElMessage.success(`"${row.title}" 已置顶`);
};
const handleStatusChange = (row: any) => {
const newStatus = row.status === 'online' ? 'offline' : 'online';
const actionText = newStatus === 'online' ? '上架' : '下架';
ElMessageBox.confirm(`确定要${actionText}轮播图"${row.title}"吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
row.status = newStatus;
ElMessage.success(`${actionText}成功`);
}).catch(() => {
ElMessage.info('已取消操作');
});
};
const handleEdit = (row: any) => {
console.log('编辑:', row);
ElMessage.info(`编辑标题: ${row.title}`);
};
const handleView = (row: any) => {
console.log('查看:', row);
ElMessage.info(`查看标题: ${row.title}`);
};
const handleDelete = async (row: any) => {
try {
await ElMessageBox.confirm('确定要删除这条数据吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
});
const res: any = await deleteCarouselBanner(row.id);
if (res.code === 200) {
ElMessage.success('删除成功');
getList();
} else {
ElMessage.error(res.msg || '删除失败');
}
} catch (error: any) {
if (error !== 'cancel') {
console.error('删除失败:', error);
ElMessage.error('删除失败,请重试');
}
}
};
const handleSizeChange = (val: number) => {
pageSize.value = val;
currentPage.value = 1;
getList();
};
const handleCurrentChange = (val: number) => {
currentPage.value = val;
getList();
};
onMounted(() => {
getList();
});
</script>
<style scoped>
.carousel-container {
padding: 20px;
background: #f5f7fa;
min-height: calc(100vh - 100px);
}
.search-panel {
background: #fff;
padding: 16px 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
:deep(.el-form-item__label) {
white-space: nowrap;
margin-right: 8px;
}
.search-input,
.date-picker,
.search-select {
width: 150px;
}
.action-bar {
display: flex;
justify-content: space-between;
align-items: flex-start;
background: #fff;
padding: 12px 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.action-left {
display: flex;
gap: 8px;
}
.action-right {
max-width: 600px;
}
.tips-label {
font-weight: bold;
color: #303133;
display: block;
margin-bottom: 8px;
}
.tips-list {
margin: 0;
padding-left: 20px;
font-size: 12px;
color: #606266;
line-height: 1.8;
}
.tips-list li {
margin-bottom: 4px;
}
.table-container {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-bottom: 16px;
}
.status-badge {
display: flex;
align-items: center;
gap: 6px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-dot.online {
background: #67c23a;
}
.status-dot.offline {
background: #909399;
}
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
padding: 12px 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.total-text {
color: #909399;
}
:deep(.el-button--text) {
padding: 4px 8px;
}
:deep(.online-btn) {
color: #67c23a;
}
:deep(.online-btn:hover) {
color: #67c23a;
background: rgba(103, 194, 58, 0.1);
}
:deep(.offline-btn) {
color: #e6a23c;
}
:deep(.offline-btn:hover) {
color: #e6a23c;
background: rgba(230, 162, 60, 0.1);
}
:deep(.delete-btn) {
color: #f56c6c;
}
:deep(.delete-btn:hover) {
color: #f56c6c;
background: rgba(245, 108, 108, 0.1);
}
</style>

287
zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/educationServices/add.vue

@ -0,0 +1,287 @@
<template>
<div class="add-carousel-container">
<div class="page-header">
<h1>新增普法服务</h1>
</div>
<el-form :model="form" ref="formRef" :rules="rules" label-width="80px" class="form-container">
<el-form-item label="标题:" prop="title">
<el-input v-model="form.title" placeholder="请输入标题" class="form-input" />
</el-form-item>
<el-form-item label="发布部门:" prop="department">
<el-input v-model="form.department" placeholder="请输入发布部门" class="form-input" />
</el-form-item>
<el-form-item label="发布部门:" prop="department2">
<el-input v-model="form.department2" placeholder="请选择发布部门" class="form-input" />
<span class="form-tip">注: 多部门用,分隔</span>
</el-form-item>
<el-form-item label="内容发布在:" prop="publishTo">
<el-select v-model="form.publishTo" placeholder="请选择" class="form-select">
<el-option label="企业端" value="enterprise" />
<el-option label="执法端" value="law_enforcement" />
<el-option label="公众端" value="public" />
</el-select>
</el-form-item>
<el-form-item label="内容分类:" prop="category">
<el-select v-model="form.category" placeholder="请选择" class="form-select">
<el-option label="法律法规解读" value="law_解读" />
<el-option label="典型案例警示" value="case_warning" />
<el-option label="政策文件解读" value="policy_interpretation" />
<el-option label="普法宣传" value="law_publicity" />
</el-select>
</el-form-item>
<el-form-item label="内容分类:" prop="category2">
<el-select v-model="form.category2" placeholder="请选择" class="form-select">
<el-option label="法律法规解读" value="law_解读" />
<el-option label="典型案例警示" value="case_warning" />
<el-option label="政策文件解读" value="policy_interpretation" />
<el-option label="普法宣传" value="law_publicity" />
</el-select>
</el-form-item>
<el-form-item label="服务信息:" prop="content">
<div class="editor-wrapper">
<QuillEditor ref="quillEditor" v-model:content="form.content" content-type="html" :toolbar="toolbarOptions"
theme="snow" class="quill-editor" placeholder="请输入服务信息" />
</div>
</el-form-item>
<el-form-item label="附件:">
<div class="upload-area">
<div class="upload-box" @click="handleUpload">
<div class="upload-icon">📁</div>
<p>点击或将文件拖拽到这里上传</p>
<p class="upload-tip">支持扩展名称:.pdf .word;文件大小:10MB以内</p>
</div>
</div>
</el-form-item>
<el-form-item class="form-actions">
<el-button type="primary" @click="handlePreview">预览</el-button>
<el-button @click="handleCancel">取消</el-button>
<el-button type="info" @click="handleSaveDraft">保存草稿箱</el-button>
<el-button type="success" @click="handleSubmit">发布</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { ElMessage, ElForm, FormRules } from 'element-plus';
import { QuillEditor } from '@vueup/vue-quill';
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { useRouter } from 'vue-router';
import { addCarouselBanner } from '@/api/supervision/carouselBanner';
const router = useRouter();
const formRef = ref<InstanceType<typeof ElForm>>();
const quillEditor = ref();
const rules: FormRules = {
title: [
{ required: true, message: '请输入标题', trigger: 'blur' }
],
department: [
{ required: true, message: '请输入发布部门', trigger: 'blur' }
],
department2: [
{ required: true, message: '请选择发布部门', trigger: 'blur' }
],
publishTo: [
{ required: true, message: '请选择内容发布到', trigger: 'change' }
],
category: [
{ required: true, message: '请选择内容分类', trigger: 'change' }
],
content: [
{ required: true, message: '请输入服务信息', trigger: 'blur' }
]
};
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'],
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'indent': '-1' }, { 'indent': '+1' }],
[{ 'direction': 'rtl' }],
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }],
[{ 'font': [] }],
[{ 'align': [] }],
['link', 'image', 'video'],
['clean']
];
const form = reactive({
title: '',
department: '',
department2: '',
publishTo: '',
category: '',
category2: '',
content: '',
attachment: ''
});
const handlePreview = () => {
ElMessage.info('预览功能开发中');
};
const handleCancel = () => {
router.push('/supervision/carouselBanner');
};
const handleSaveDraft = () => {
ElMessage.success('已保存到草稿箱');
};
const handleSubmit = async () => {
if (!formRef.value) return;
try {
const isValid = await formRef.value.validate();
if (!isValid) {
return;
}
const res: any = await addCarouselBanner(form);
if (res.code === 200) {
ElMessage.success('发布成功');
router.push('/supervision/carouselBanner');
} else {
ElMessage.error(res.msg || '发布失败');
}
} catch (error) {
console.error('发布失败:', error);
ElMessage.error('发布失败,请重试');
}
};
const handleUpload = () => {
ElMessage.info('文件上传功能开发中');
};
</script>
<style scoped>
.add-carousel-container {
padding: 20px;
background: #f5f7fa;
min-height: calc(100vh - 100px);
}
.page-header {
margin-bottom: 20px;
}
.page-header h1 {
font-size: 24px;
font-weight: bold;
color: #303133;
}
.form-container {
background: #fff;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.form-input {
width: 100%;
max-width: 500px;
}
.form-select {
width: 100%;
max-width: 500px;
}
.form-tip {
display: block;
color: #909399;
font-size: 12px;
margin-top: 4px;
}
.editor-wrapper {
width: 100%;
border: 1px solid #dcdfe6;
border-radius: 4px;
overflow: hidden;
}
.quill-editor {
height: 500px;
}
:deep(.ql-container) {
min-height: 500px;
height: 100%;
}
:deep(.ql-editor) {
min-height: 450px;
}
.upload-area {
width: 100%;
}
.upload-box {
border: 2px dashed #d9d9d9;
border-radius: 8px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.upload-box:hover {
border-color: #409eff;
background: #f0f5ff;
}
.upload-icon {
font-size: 48px;
color: #409eff;
margin-bottom: 16px;
}
.upload-box p {
color: #606266;
margin: 8px 0;
}
.upload-tip {
font-size: 12px;
color: #909399 !important;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
}
:deep(.el-form-item__label) {
font-weight: bold;
white-space: nowrap;
}
:deep(.el-form-item.is-required:not(.is-no-asterisk)>.el-form-item__label)::before {
content: '*';
color: #f56c6c;
margin-right: 4px;
}
</style>

347
zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/educationServices/index.vue

@ -0,0 +1,347 @@
<template>
<div class="law-service-container">
<!-- 搜索区域 -->
<div class="search-panel">
<el-form :model="searchForm" inline>
<el-form-item label="标题:">
<el-input v-model="searchForm.title" placeholder="请输入关键字" class="search-input" clearable
@keyup.enter="handleSearch" />
</el-form-item>
<el-form-item label="发布时间:">
<el-date-picker v-model="searchForm.timeRange" type="datetimerange" range-separator="至"
start-placeholder="开始时间" end-placeholder="结束时间" class="date-picker publish-time-picker" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
<el-form-item label="发布部门:">
<el-input v-model="searchForm.department" placeholder="请输入关键字" class="search-input" clearable
@keyup.enter="handleSearch" />
</el-form-item>
<el-form-item label="内容分类:">
<el-select v-model="searchForm.category" placeholder="请选择" class="search-select" clearable>
<el-option label="法律法规解读" value="law_解读" />
<el-option label="典型案例警示" value="case_warning" />
<el-option label="政策文件解读" value="policy_interpretation" />
<el-option label="普法宣传" value="law_publicity" />
</el-select>
</el-form-item>
<el-form-item label="内容发布到:">
<el-select v-model="searchForm.publishTo" placeholder="请选择" class="search-select" clearable>
<el-option label="企业端" value="enterprise" />
<el-option label="执法端" value="law_enforcement" />
<!-- <el-option label="公众端" value="public" /> -->
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleSearch" :loading="loading">
搜索
</el-button>
</el-form-item>
<el-form-item>
<el-button :icon="Refresh" @click="handleReset">
重置
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 统计信息 -->
<div class="stats-bar">
<div class="stats-left">
<span class="total-count">总浏览次数:{{ totalViewCount }}次</span>
</div>
<div class="stats-right">
<el-button size="small" type="primary" :icon="Plus" @click="handleAdd">新增</el-button>
<el-button size="small" type="success" :icon="Upload" @click="handleImport">导入</el-button>
<el-button size="small" type="warning" :icon="Download" @click="handleExport">导出</el-button>
<el-button size="small" type="danger" :icon="Delete" @click="handleBatchDelete" :disabled="selectedRows.length === 0">删除</el-button>
</div>
</div>
<!-- 数据表格 -->
<div class="table-container">
<el-table v-loading="loading" :data="tableData" border
:header-cell-style="{ backgroundColor: '#f5f7fa', fontWeight: 'bold' }"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="title" label="标题" min-width="200" show-overflow-tooltip />
<el-table-column prop="publishTime" label="发布&转载时间" width="180" />
<el-table-column prop="category" label="内容分类" width="120" />
<el-table-column prop="department" label="发布部门" width="150" />
<el-table-column prop="publishTo" label="内容发布到" width="120" />
<el-table-column prop="viewCount" label="浏览次数" width="100" />
<el-table-column label="操作" width="240" fixed="right">
<template #default="scope">
<el-button size="small" type="text" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="text" icon="el-icon-view" @click="handleView(scope.row)">查看</el-button>
<el-button size="small" type="text" icon="el-icon-delete" class="delete-btn"
@click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination-container">
<span class="total-text">共 {{ total }} 条数据</span>
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :total="total"
:page-sizes="[10, 20, 50, 100]" layout="prev, pager, next, jumper, ->, total, sizes"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Plus, Upload, Download, Delete, Search, Refresh } from '@element-plus/icons-vue';
import { useRouter } from 'vue-router';
import { listCarouselBanner, deleteCarouselBanner } from '@/api/supervision/carouselBanner';
const router = useRouter();
const loading = ref(false);
const tableData = ref<any[]>([]);
const total = ref(0);
const totalViewCount = ref(0);
const currentPage = ref(1);
const pageSize = ref(10);
const selectedRows = ref<any[]>([]);
const searchForm = reactive({
title: '',
timeRange: [] as string[],
department: '',
category: '',
publishTo: ''
});
const getList = async () => {
loading.value = true;
try {
const queryParams = {
...searchForm,
startTime: searchForm.timeRange[0] || '',
endTime: searchForm.timeRange[1] || '',
pageNum: currentPage.value,
pageSize: pageSize.value
};
delete queryParams.timeRange;
const res: any = await listCarouselBanner(queryParams);
if (res.code === 200) {
tableData.value = res.rows || [];
total.value = res.total || 0;
totalViewCount.value = res.totalViewCount || 0;
} else {
ElMessage.error(res.msg || '获取数据失败');
}
} catch (error) {
console.error('获取列表数据失败:', error);
ElMessage.error('获取数据失败,请检查网络连接');
} finally {
loading.value = false;
}
};
const handleSearch = () => {
currentPage.value = 1;
getList();
};
const handleReset = () => {
searchForm.title = '';
searchForm.timeRange = [];
searchForm.department = '';
searchForm.category = '';
searchForm.publishTo = '';
currentPage.value = 1;
handleSearch();
ElMessage.info('已重置搜索条件');
};
const handleAdd = () => {
router.push('/supervision/carouselBanner/add');
};
const handleImport = () => {
ElMessage.info('导入功能开发中');
};
const handleExport = () => {
ElMessage.info('导出功能开发中');
};
const handleBatchDelete = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请选择要删除的数据');
return;
}
ElMessageBox.confirm(`确定要删除选中的 ${selectedRows.value.length} 条数据吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(async () => {
ElMessage.success('批量删除成功');
}).catch(() => {
ElMessage.info('已取消删除');
});
};
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection;
};
const handleEdit = (row: any) => {
console.log('编辑:', row);
ElMessage.info(`编辑标题: ${row.title}`);
};
const handleView = (row: any) => {
console.log('查看:', row);
ElMessage.info(`查看标题: ${row.title}`);
};
const handleDelete = async (row: any) => {
try {
await ElMessageBox.confirm('确定要删除这条数据吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
});
const res: any = await deleteCarouselBanner(row.id);
if (res.code === 200) {
ElMessage.success('删除成功');
getList();
} else {
ElMessage.error(res.msg || '删除失败');
}
} catch (error: any) {
if (error !== 'cancel') {
console.error('删除失败:', error);
ElMessage.error('删除失败,请重试');
}
}
};
const handleSizeChange = (val: number) => {
pageSize.value = val;
currentPage.value = 1;
getList();
};
const handleCurrentChange = (val: number) => {
currentPage.value = val;
getList();
};
onMounted(() => {
getList();
});
</script>
<style scoped>
.law-service-container {
padding: 20px;
background: #f5f7fa;
min-height: calc(100vh - 100px);
}
.search-panel {
background: #fff;
padding: 16px 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
/* 让标签不换行 */
:deep(.el-form-item__label) {
white-space: nowrap;
margin-right: 8px;
}
/* 让所有输入框和选择框宽度统一 */
.search-input,
.date-picker,
.search-select {
width: 126px;
}
:deep(.publish-time-picker) {
width: 200px;
}
.stats-bar {
display: flex;
/* justify-content: space-between; */
align-items: center;
background: #fff;
padding: 12px 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.stats-left {
display: flex;
align-items: center;
}
.total-count {
font-weight: bold;
color: #303133;
}
.stats-right {
display: flex;
gap: 8px;
margin-left: 30px;
}
.table-container {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-bottom: 16px;
}
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
padding: 12px 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.total-text {
color: #909399;
}
:deep(.el-button--text) {
padding: 4px 8px;
}
:deep(.delete-btn) {
color: #f56c6c;
}
:deep(.delete-btn:hover) {
color: #f56c6c;
background: rgba(245, 108, 108, 0.1);
}
</style>

161
zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/noticeInfo/add.vue

@ -0,0 +1,161 @@
<template>
<div class="notice-add-container">
<div class="page-header">
<h2>新增公告</h2>
</div>
<div class="form-wrapper">
<el-form :model="form" ref="formRef" :rules="rules" label-width="100px" class="form-container">
<el-form-item label="公告内容" prop="content">
<el-input
v-model="form.content"
type="textarea"
:rows="2"
placeholder="请输入公告内容"
maxlength="100"
show-word-limit
class="form-textarea"
/>
</el-form-item>
<el-form-item label="内容发布到" prop="publishTo">
<el-select v-model="form.publishTo" placeholder="请选择" class="form-select">
<el-option label="企业端" value="enterprise" />
<el-option label="执法端" value="law_enforcement" />
<el-option label="企业端+执法端" value="both" />
</el-select>
</el-form-item>
<el-form-item label="显示时长" prop="displayDays">
<el-input v-model.number="form.displayDays" placeholder="请输入天数" class="form-input" />
</el-form-item>
</el-form>
<div class="form-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button @click="handleSaveDraft">保存草稿箱</el-button>
<el-button type="primary" @click="handleSubmit">发布</el-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { addNoticeInfo } from '@/api/supervision/noticeInfo';
const router = useRouter();
const formRef = ref();
const form = reactive({
content: '',
publishTo: '',
displayDays: ''
});
const rules = {
content: [
{ required: true, message: '请输入公告内容', trigger: 'blur' },
{ max: 100, message: '公告内容最多100个字', trigger: 'blur' }
],
publishTo: [
{ required: true, message: '请选择内容发布到', trigger: 'change' }
],
displayDays: [
{ required: true, message: '请输入显示时长', trigger: 'blur' },
{ type: 'number' as const, min: 1, message: '显示时长必须大于0', trigger: 'blur' }
]
};
const handleCancel = () => {
router.push('/general-manage/supervision/noticeInfo');
}
const handleSaveDraft = () => {
ElMessage.success('已保存到草稿箱');
};
const handleSubmit = async () => {
if (!formRef.value) return;
try {
const isValid = await formRef.value.validate();
if (!isValid) {
return;
}
const res: any = await addNoticeInfo(form);
if (res.code === 200) {
ElMessage.success('发布成功');
router.push('/supervision/noticeInfo');
} else {
ElMessage.error(res.msg || '发布失败');
}
} catch (error) {
console.error('表单验证失败:', error);
}
};
</script>
<style scoped>
.notice-add-container {
padding: 20px;
background: #f5f7fa;
min-height: calc(100vh - 100px);
}
.page-header {
background: #fff;
padding: 16px 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.page-header h2 {
margin: 0;
font-size: 18px;
font-weight: bold;
color: #303133;
}
.form-wrapper {
background: #fff;
padding: 24px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.form-container {
max-width: 800px;
}
:deep(.el-form-item__label) {
font-weight: bold;
white-space: nowrap;
}
:deep(.el-form-item.is-required:not(.is-no-asterisk)>.el-form-item__label)::before {
content: '*';
color: #f56c6c;
margin-right: 4px;
}
.form-textarea,
.form-select,
.form-input {
width: 100%;
max-width: 600px;
}
.form-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 32px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
}
</style>

348
zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/noticeInfo/index.vue

@ -0,0 +1,348 @@
<template>
<div class="notice-info-container">
<!-- 搜索区域 -->
<div class="search-panel">
<el-form :model="searchForm" inline>
<el-form-item label="公告内容:">
<el-input v-model="searchForm.content" placeholder="请输入关键字" class="search-input" clearable
@keyup.enter="handleSearch" />
</el-form-item>
<el-form-item label="发布时间:">
<el-date-picker v-model="searchForm.timeRange" type="datetimerange" range-separator="至"
start-placeholder="开始时间" end-placeholder="结束时间" class="date-picker" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
<el-form-item label="内容发布到:">
<el-select v-model="searchForm.publishTo" placeholder="请选择" class="search-select" clearable>
<el-option label="企业端" value="enterprise" />
<el-option label="执法端" value="law_enforcement" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch" :loading="loading">搜索</el-button>
</el-form-item>
<el-form-item>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 操作栏 -->
<div class="action-bar">
<div class="action-left">
<el-button type="primary" @click="handleAdd">新增</el-button>
<el-button @click="handleBatchDelete" :disabled="selectedRows.length === 0">删除</el-button>
</div>
</div>
<!-- 数据表格 -->
<div class="table-container">
<el-table v-loading="loading" :data="tableData" border
:header-cell-style="{ backgroundColor: '#f5f7fa', fontWeight: 'bold' }"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="content" label="公告内容" min-width="250" show-overflow-tooltip />
<el-table-column prop="publishTime" label="发布时间" width="180" />
<el-table-column prop="displayDays" label="显示时长" width="100" />
<el-table-column prop="publishTo" label="内容发布到" width="120" />
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<div class="status-badge">
<span :class="scope.row.status === 'online' ? 'status-dot online' : 'status-dot offline'"></span>
{{ scope.row.status === 'online' ? '在线' : '下架' }}
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="180" fixed="right">
<template #default="scope">
<el-button size="small" type="text" :class="scope.row.status === 'online' ? 'offline-btn' : 'online-btn'"
@click="handleStatusChange(scope.row)">
{{ scope.row.status === 'online' ? '下架' : '上架' }}
</el-button>
<el-button size="small" type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="text" class="delete-btn" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination-container">
<span class="total-text">共 {{ total }} 条数据</span>
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :total="total"
:page-sizes="[10, 20, 50, 100]" layout="prev, pager, next, jumper, ->, total, sizes"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useRouter } from 'vue-router';
import { listNoticeInfo, deleteNoticeInfo } from '@/api/supervision/noticeInfo';
const router = useRouter();
const loading = ref(false);
const tableData = ref<any[]>([]);
const total = ref(0);
const currentPage = ref(1);
const pageSize = ref(10);
const selectedRows = ref<any[]>([]);
const searchForm = reactive({
content: '',
timeRange: [] as string[],
publishTo: ''
});
const getList = async () => {
loading.value = true;
try {
const queryParams = {
...searchForm,
startTime: searchForm.timeRange[0] || '',
endTime: searchForm.timeRange[1] || '',
pageNum: currentPage.value,
pageSize: pageSize.value
};
delete queryParams.timeRange;
const res: any = await listNoticeInfo(queryParams);
if (res.code === 200) {
tableData.value = res.rows || [];
total.value = res.total || 0;
} else {
ElMessage.error(res.msg || '获取数据失败');
}
} catch (error) {
console.error('获取列表数据失败:', error);
ElMessage.error('获取数据失败,请检查网络连接');
} finally {
loading.value = false;
}
};
const handleSearch = () => {
currentPage.value = 1;
getList();
};
const handleReset = () => {
searchForm.content = '';
searchForm.timeRange = [];
searchForm.publishTo = '';
currentPage.value = 1;
handleSearch();
ElMessage.info('已重置搜索条件');
};
const handleAdd = () => {
router.push('/supervision/noticeInfo/add');
};
const handleBatchDelete = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请选择要删除的数据');
return;
}
ElMessageBox.confirm(`确定要删除选中的 ${selectedRows.value.length} 条数据吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
ElMessage.success('批量删除成功');
getList();
selectedRows.value = [];
}).catch(() => {
ElMessage.info('已取消删除');
});
};
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection;
};
const handleStatusChange = (row: any) => {
const newStatus = row.status === 'online' ? 'offline' : 'online';
const actionText = newStatus === 'online' ? '上架' : '下架';
ElMessageBox.confirm(`确定要${actionText}公告"${row.content}"吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
row.status = newStatus;
ElMessage.success(`${actionText}成功`);
}).catch(() => {
ElMessage.info('已取消操作');
});
};
const handleEdit = (row: any) => {
ElMessage.info(`编辑公告: ${row.content}`);
};
const handleDelete = async (row: any) => {
try {
await ElMessageBox.confirm('确定要删除这条数据吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
});
const res: any = await deleteNoticeInfo(row.id);
if (res.code === 200) {
ElMessage.success('删除成功');
getList();
} else {
ElMessage.error(res.msg || '删除失败');
}
} catch (error: any) {
if (error !== 'cancel') {
console.error('删除失败:', error);
ElMessage.error('删除失败,请重试');
}
}
};
const handleSizeChange = (val: number) => {
pageSize.value = val;
currentPage.value = 1;
getList();
};
const handleCurrentChange = (val: number) => {
currentPage.value = val;
getList();
};
onMounted(() => {
getList();
});
</script>
<style scoped>
.notice-info-container {
padding: 20px;
background: #f5f7fa;
min-height: calc(100vh - 100px);
}
.search-panel {
background: #fff;
padding: 16px 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
:deep(.el-form-item__label) {
white-space: nowrap;
margin-right: 8px;
}
.search-input,
.date-picker,
.search-select {
width: 150px;
}
.action-bar {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
padding: 12px 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.action-left {
display: flex;
gap: 8px;
}
.table-container {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-bottom: 16px;
}
.status-badge {
display: flex;
align-items: center;
gap: 6px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-dot.online {
background: #67c23a;
}
.status-dot.offline {
background: #909399;
}
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
padding: 12px 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.total-text {
color: #909399;
}
:deep(.el-button--text) {
padding: 4px 8px;
}
:deep(.online-btn) {
color: #67c23a;
}
:deep(.online-btn:hover) {
color: #67c23a;
background: rgba(103, 194, 58, 0.1);
}
:deep(.offline-btn) {
color: #e6a23c;
}
:deep(.offline-btn:hover) {
color: #e6a23c;
background: rgba(230, 162, 60, 0.1);
}
:deep(.delete-btn) {
color: #f56c6c;
}
:deep(.delete-btn:hover) {
color: #f56c6c;
background: rgba(245, 108, 108, 0.1);
}
</style>
Loading…
Cancel
Save