¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢å
¬åå表 |
| | | export function listNotice(query) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/list', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢å
¬åè¯¦ç» |
| | | export function getNotice(noticeId) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/' + noticeId, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢å
Œ |
| | | export function addNotice(data) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹å
Œ |
| | | export function updateNotice(data) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice', |
| | | method: 'put', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // å é¤å
Œ |
| | | export function delNotice(noticeId) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/' + noticeId, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | | |
| | | // æ¹éå é¤å
Œ |
| | | export function delNoticeBatch(noticeIds) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/batch', |
| | | method: 'delete', |
| | | data: noticeIds |
| | | }) |
| | | } |
| | | |
| | | // åå¸å
Œ |
| | | export function publishNotice(noticeId) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/publish/' + noticeId, |
| | | method: 'put' |
| | | }) |
| | | } |
| | | |
| | | // ä¸çº¿å
Œ |
| | | export function offlineNotice(noticeId) { |
| | | return request({ |
| | | url: '/collaborativeApproval/notice/offline/' + noticeId, |
| | | method: 'put' |
| | | }) |
| | | } |
| | |
| | | }
|
| | | ]
|
| | | },
|
| | | // {
|
| | | // path: '/equipment',
|
| | | // component: Layout,
|
| | | // redirect: '/equipment/iot-monitor',
|
| | | // children: [
|
| | | // {
|
| | | // path: 'iot-monitor',
|
| | | // component: () => import('@/views/equipmentManagement/iotMonitor/index.vue'),
|
| | | // name: 'IoTMonitor',
|
| | | // meta: { title: 'IoTçæ§', icon: 'monitor', noCache: true }
|
| | | // }
|
| | | // ]
|
| | | // },
|
| | | // {
|
| | | // path: '/main/MobileChat',
|
| | | // component: Layout,
|
| | | // redirect: '',
|
| | | // hidden: true,
|
| | | // children: [
|
| | | // {
|
| | | // path: '',
|
| | | // component: () => import('@/views/chatHome/chatHomeIndex/MobileChat'),
|
| | | // name: 'MobileChat',
|
| | | // meta: { title: 'AI对è¯', icon: 'dashboard', affix: true}
|
| | | // }
|
| | | // ]
|
| | | // },
|
| | | {
|
| | | path: '/user',
|
| | | component: Layout,
|
| | |
| | | ]
|
| | | },
|
| | | {
|
| | | path: '/main/MobileChat', |
| | | component: Layout, |
| | | redirect: '', |
| | | hidden: true, |
| | | permissions: ['MobileChat:edit'], |
| | | children: [ |
| | | { |
| | | path: '', |
| | | component: () => import('@/views/chatHome/chatHomeIndex/MobileChat'), |
| | | name: 'MobileChat', |
| | | meta: { title: 'AI对è¯', activeMenu: '/chatHome/chatHomeIndex'} |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | path: '/system/role-auth',
|
| | | component: Layout,
|
| | | hidden: true,
|
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>ä¼è®®çæ¿</h2> |
| | | <!-- <el-button type="primary" @click="createMeeting">å建ä¼è®®</el-button>--> |
| | | </div> |
| | | |
| | | <!-- ä¼è®®ç»è®¡å¡ç --> |
| | | <div class="stats-cards"> |
| | | <el-card class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-number">{{ stats.total }}</div> |
| | | <div class="stat-label">æ»ä¼è®®æ°</div> |
| | | </div> |
| | | </el-card> |
| | | <el-card class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-number">{{ stats.ongoing }}</div> |
| | | <div class="stat-label">è¿è¡ä¸</div> |
| | | </div> |
| | | </el-card> |
| | | <el-card class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-number">{{ stats.completed }}</div> |
| | | <div class="stat-label">已宿</div> |
| | | </div> |
| | | </el-card> |
| | | <el-card class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-number">{{ stats.upcoming }}</div> |
| | | <div class="stat-label">å³å°å¼å§</div> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- ä¼è®®å表 --> |
| | | <div class="meeting-list"> |
| | | <el-card v-for="meeting in meetings" :key="meeting.id" class="meeting-card"> |
| | | <div class="meeting-header"> |
| | | <div class="meeting-title"> |
| | | <h3>{{ meeting.title }}</h3> |
| | | <el-tag :type="getStatusType(meeting.status)" size="small"> |
| | | {{ getStatusText(meeting.status) }} |
| | | </el-tag> |
| | | </div> |
| | | <div class="meeting-time"> |
| | | <el-icon><Clock /></el-icon> |
| | | {{ formatTime(meeting.startTime) }} - {{ formatTime(meeting.endTime) }} |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="meeting-info"> |
| | | <div class="info-item"> |
| | | <el-icon><Location /></el-icon> |
| | | <span>{{ meeting.location }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <el-icon><User /></el-icon> |
| | | <span>主æäºº: {{ meeting.host }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <el-icon><UserFilled /></el-icon> |
| | | <span>åä¼äººæ°: {{ meeting.participants.length }}人</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="meeting-agenda"> |
| | | <h4>è®®ç¨å®æ</h4> |
| | | <div class="agenda-list"> |
| | | <div |
| | | v-for="(agenda, index) in meeting.agenda" |
| | | :key="index" |
| | | class="agenda-item" |
| | | :class="{ 'active': agenda.status === 'active', 'completed': agenda.status === 'completed' }" |
| | | > |
| | | <span class="agenda-time">{{ agenda.time }}</span> |
| | | <span class="agenda-content">{{ agenda.content }}</span> |
| | | <el-tag |
| | | :type="getAgendaStatusType(agenda.status)" |
| | | size="small" |
| | | > |
| | | {{ getAgendaStatusText(agenda.status) }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- <div class="meeting-actions">--> |
| | | <!-- <el-button type="primary" size="small" @click="joinMeeting(meeting)">--> |
| | | <!-- å å
¥ä¼è®®--> |
| | | <!-- </el-button>--> |
| | | <!-- <el-button type="info" size="small" @click="viewDetails(meeting)">--> |
| | | <!-- æ¥ç详æ
--> |
| | | <!-- </el-button>--> |
| | | <!-- <el-button type="warning" size="small" @click="editMeeting(meeting)">--> |
| | | <!-- ç¼è¾--> |
| | | <!-- </el-button>--> |
| | | <!-- </div>--> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- å建ä¼è®®å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="dialogVisible" title="å建ä¼è®®" width="600px"> |
| | | <el-form :model="meetingForm" label-width="100px"> |
| | | <el-form-item label="ä¼è®®æ é¢"> |
| | | <el-input v-model="meetingForm.title" placeholder="请è¾å
¥ä¼è®®æ é¢" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼è®®æ¶é´"> |
| | | <el-date-picker |
| | | v-model="meetingForm.timeRange" |
| | | type="datetimerange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¶é´" |
| | | end-placeholder="ç»ææ¶é´" |
| | | format="YYYY-MM-DD HH:mm" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼è®®å°ç¹"> |
| | | <el-input v-model="meetingForm.location" placeholder="请è¾å
¥ä¼è®®å°ç¹" /> |
| | | </el-form-item> |
| | | <el-form-item label="主æäºº"> |
| | | <el-input v-model="meetingForm.host" placeholder="请è¾å
¥ä¸»æäººå§å" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼è®®æè¿°"> |
| | | <el-input |
| | | v-model="meetingForm.description" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥ä¼è®®æè¿°" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitMeeting">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { Clock, Location, User, UserFilled } from '@element-plus/icons-vue' |
| | | |
| | | // ç»è®¡æ°æ® |
| | | const stats = reactive({ |
| | | total: 12, |
| | | ongoing: 3, |
| | | completed: 7, |
| | | upcoming: 2 |
| | | }) |
| | | |
| | | // ä¼è®®æ°æ® |
| | | const meetings = ref([ |
| | | { |
| | | id: 1, |
| | | title: '产åå¼åå¨ä¼', |
| | | status: 'ongoing', |
| | | startTime: '2024-01-15 09:00:00', |
| | | endTime: '2024-01-15 10:30:00', |
| | | location: 'ä¼è®®å®¤A', |
| | | host: 'å¼ ç»ç', |
| | | participants: ['å¼ ç»ç', 'æå·¥ç¨å¸', 'ç设计å¸', 'èµµæµè¯å'], |
| | | agenda: [ |
| | | { time: '09:00-09:15', content: 'ä¸å¨å·¥ä½æ»ç»', status: 'completed' }, |
| | | { time: '09:15-09:45', content: 'æ¬å¨å¼å计å', status: 'active' }, |
| | | { time: '09:45-10:00', content: 'ææ¯é¾ç¹è®¨è®º', status: 'pending' }, |
| | | { time: '10:00-10:30', content: 'é®é¢åé¦ä¸è§£å³', status: 'pending' } |
| | | ] |
| | | }, |
| | | { |
| | | id: 2, |
| | | title: '客æ·éæ±è¯å®¡ä¼', |
| | | status: 'upcoming', |
| | | startTime: '2024-01-15 14:00:00', |
| | | endTime: '2024-01-15 15:00:00', |
| | | location: '线ä¸ä¼è®®', |
| | | host: 'éæ»ç', |
| | | participants: ['éæ»ç', 'å产åç»ç', 'å客æ·ç»ç', '客æ·ä»£è¡¨'], |
| | | agenda: [ |
| | | { time: '14:00-14:20', content: 'éæ±èæ¯ä»ç»', status: 'pending' }, |
| | | { time: '14:20-14:40', content: 'åè½éæ±åæ', status: 'pending' }, |
| | | { time: '14:40-15:00', content: 'ææ¯å¯è¡æ§è¯ä¼°', status: 'pending' } |
| | | ] |
| | | }, |
| | | { |
| | | id: 3, |
| | | title: 'å¢é建设活å¨', |
| | | status: 'completed', |
| | | startTime: '2024-01-14 16:00:00', |
| | | endTime: '2024-01-14 18:00:00', |
| | | location: 'å
¬å¸å¤§å
', |
| | | host: '人äºé¨', |
| | | participants: ['å
¨ä½åå·¥'], |
| | | agenda: [ |
| | | { time: '16:00-16:30', content: 'å¢é游æ', status: 'completed' }, |
| | | { time: '16:30-17:00', content: 'ç»éªå享', status: 'completed' }, |
| | | { time: '17:00-18:00', content: 'èªç±äº¤æµ', status: 'completed' } |
| | | ] |
| | | } |
| | | ]) |
| | | |
| | | // å¯¹è¯æ¡ç¸å
³ |
| | | const dialogVisible = ref(false) |
| | | const meetingForm = reactive({ |
| | | title: '', |
| | | timeRange: [], |
| | | location: '', |
| | | host: '', |
| | | description: '' |
| | | }) |
| | | |
| | | // è·åç¶æç±»å |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | 'ongoing': 'success', |
| | | 'upcoming': 'warning', |
| | | 'completed': 'info' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | |
| | | // è·åç¶æææ¬ |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | 'ongoing': 'è¿è¡ä¸', |
| | | 'upcoming': 'å³å°å¼å§', |
| | | 'completed': '已宿' |
| | | } |
| | | return statusMap[status] || 'æªç¥' |
| | | } |
| | | |
| | | // è·åè®®ç¨ç¶æç±»å |
| | | const getAgendaStatusType = (status) => { |
| | | const statusMap = { |
| | | 'completed': 'success', |
| | | 'active': 'warning', |
| | | 'pending': 'info' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | |
| | | // è·åè®®ç¨ç¶æææ¬ |
| | | const getAgendaStatusText = (status) => { |
| | | const statusMap = { |
| | | 'completed': '已宿', |
| | | 'active': 'è¿è¡ä¸', |
| | | 'pending': 'å¾
å¼å§' |
| | | } |
| | | return statusMap[status] || 'æªç¥' |
| | | } |
| | | |
| | | // æ ¼å¼åæ¶é´ |
| | | const formatTime = (timeStr) => { |
| | | const date = new Date(timeStr) |
| | | return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }) |
| | | } |
| | | |
| | | // å建ä¼è®® |
| | | const createMeeting = () => { |
| | | dialogVisible.value = true |
| | | // é置表å |
| | | Object.assign(meetingForm, { |
| | | title: '', |
| | | timeRange: [], |
| | | location: '', |
| | | host: '', |
| | | description: '' |
| | | }) |
| | | } |
| | | |
| | | // æäº¤ä¼è®® |
| | | const submitMeeting = () => { |
| | | if (!meetingForm.title || !meetingForm.timeRange.length || !meetingForm.location || !meetingForm.host) { |
| | | ElMessage.warning('请填å宿´çä¼è®®ä¿¡æ¯') |
| | | return |
| | | } |
| | | |
| | | // å建æ°ä¼è®® |
| | | const newMeeting = { |
| | | id: Date.now(), |
| | | title: meetingForm.title, |
| | | status: 'upcoming', |
| | | startTime: meetingForm.timeRange[0], |
| | | endTime: meetingForm.timeRange[1], |
| | | location: meetingForm.location, |
| | | host: meetingForm.host, |
| | | participants: [meetingForm.host], |
| | | agenda: [ |
| | | { time: 'å¾
å®', content: 'è®®ç¨å¾
å®', status: 'pending' } |
| | | ] |
| | | } |
| | | |
| | | meetings.value.unshift(newMeeting) |
| | | stats.total++ |
| | | stats.upcoming++ |
| | | |
| | | ElMessage.success('ä¼è®®å建æå') |
| | | dialogVisible.value = false |
| | | } |
| | | |
| | | // å å
¥ä¼è®® |
| | | const joinMeeting = (meeting) => { |
| | | ElMessage.success(`å·²å å
¥ä¼è®®ï¼${meeting.title}`) |
| | | } |
| | | |
| | | // æ¥ç详æ
|
| | | const viewDetails = (meeting) => { |
| | | ElMessage.info(`æ¥çä¼è®®è¯¦æ
ï¼${meeting.title}`) |
| | | } |
| | | |
| | | // ç¼è¾ä¼è®® |
| | | const editMeeting = (meeting) => { |
| | | ElMessage.info(`ç¼è¾ä¼è®®ï¼${meeting.title}`) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | console.log('ä¼è®®çæ¿é¡µé¢å è½½å®æ') |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .page-header h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .stats-cards { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
| | | gap: 20px; |
| | | margin-bottom: 30px; |
| | | } |
| | | |
| | | .stat-card { |
| | | text-align: center; |
| | | } |
| | | |
| | | .stat-content { |
| | | padding: 10px; |
| | | } |
| | | |
| | | .stat-number { |
| | | font-size: 32px; |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .meeting-list { |
| | | display: grid; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .meeting-card { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .meeting-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .meeting-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .meeting-title h3 { |
| | | margin: 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .meeting-time { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 5px; |
| | | color: #606266; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .meeting-info { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 5px; |
| | | color: #606266; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .meeting-agenda { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .meeting-agenda h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .agenda-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .agenda-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 15px; |
| | | padding: 10px; |
| | | border-radius: 6px; |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | .agenda-item.active { |
| | | background-color: #fdf6ec; |
| | | border-left: 3px solid #e6a23c; |
| | | } |
| | | |
| | | .agenda-item.completed { |
| | | background-color: #f0f9ff; |
| | | border-left: 3px solid #409eff; |
| | | } |
| | | |
| | | .agenda-time { |
| | | font-weight: bold; |
| | | color: #606266; |
| | | min-width: 80px; |
| | | } |
| | | |
| | | .agenda-content { |
| | | flex: 1; |
| | | color: #303133; |
| | | } |
| | | |
| | | .meeting-actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .stats-cards { |
| | | grid-template-columns: repeat(2, 1fr); |
| | | } |
| | | |
| | | .meeting-header { |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .meeting-info { |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .meeting-actions { |
| | | flex-direction: column; |
| | | } |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- æç´¢è¡¨å --> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">å
¬åæ é¢ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.noticeTitle" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥å
¬åæ é¢æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <span class="search_title ml10">å
¬åç±»åï¼</span> |
| | | <el-select v-model="searchForm.noticeType" clearable @change="handleQuery" style="width: 240px"> |
| | | <el-option label="æ¾åéç¥" value="1" /> |
| | | <el-option label="设å¤ç»´ä¿®éç¥" value="2" /> |
| | | </el-select> |
| | | <span class="search_title ml10">ç¶æï¼</span> |
| | | <el-select v-model="searchForm.status" clearable @change="handleQuery" style="width: 240px"> |
| | | <el-option label="è稿" value="0" /> |
| | | <el-option label="å·²åå¸" value="1" /> |
| | | <el-option label="å·²ä¸çº¿" value="2" /> |
| | | </el-select> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">æç´¢</el-button> |
| | | <el-button @click="resetQuery" style="margin-left: 10px">éç½®</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">æ°å¢å
Œ</el-button> |
| | | <el-button type="danger" plain @click="handleDelete" :disabled="!selectedIds.length">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- éç¥å
¬åæ¿ --> |
| | | <div class="notice-board"> |
| | | <!-- æ¾åéç¥åºå --> |
| | | <div class="notice-section" v-if="holidayNotices.length > 0"> |
| | | <div class="section-header"> |
| | | <h3>ð
æ¾åéç¥</h3> |
| | | <span class="section-count">{{ holidayNotices.length }}æ¡</span> |
| | | </div> |
| | | <div class="notice-cards"> |
| | | <div |
| | | v-for="notice in holidayNotices" |
| | | :key="notice.id" |
| | | class="notice-card holiday-card" |
| | | :class="{ 'urgent': notice.priority === '3' }" |
| | | > |
| | | <div class="card-header"> |
| | | <div class="card-title"> |
| | | <el-icon class="holiday-icon"><Calendar /></el-icon> |
| | | {{ notice.noticeTitle }} |
| | | </div> |
| | | <div class="card-actions"> |
| | | <el-button link type="primary" @click="handleEdit(notice)">ç¼è¾</el-button> |
| | | <el-button link type="danger" @click="handleDelete(notice.id)">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="card-content"> |
| | | <p>{{ notice.noticeContent }}</p> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <div class="card-meta"> |
| | | <span class="priority" :class="'priority-' + notice.priority"> |
| | | {{ getPriorityText(notice.priority) }} |
| | | </span> |
| | | <span class="status" :class="'status-' + notice.status"> |
| | | {{ getStatusText(notice.status) }} |
| | | </span> |
| | | </div> |
| | | <div class="card-info"> |
| | | <span class="creator">{{ notice.createBy }}</span> |
| | | <span class="time">{{ notice.createTime }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="card-remark" v-if="notice.remark"> |
| | | <el-icon><InfoFilled /></el-icon> |
| | | <span>{{ notice.remark }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 设å¤ç»´ä¿®éç¥åºå --> |
| | | <div class="notice-section" v-if="maintenanceNotices.length > 0"> |
| | | <div class="section-header"> |
| | | <h3>ð§ 设å¤ç»´ä¿®éç¥</h3> |
| | | <span class="section-count">{{ maintenanceNotices.length }}æ¡</span> |
| | | </div> |
| | | <div class="notice-cards"> |
| | | <div |
| | | v-for="notice in maintenanceNotices" |
| | | :key="notice.id" |
| | | class="notice-card maintenance-card" |
| | | :class="{ 'urgent': notice.priority === '3' }" |
| | | > |
| | | <div class="card-header"> |
| | | <div class="card-title"> |
| | | <el-icon class="maintenance-icon"><Tools /></el-icon> |
| | | {{ notice.noticeTitle }} |
| | | </div> |
| | | <div class="card-actions"> |
| | | <el-button link type="primary" @click="handleEdit(notice)">ç¼è¾</el-button> |
| | | <el-button link type="danger" @click="handleDelete(notice.id)">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="card-content"> |
| | | <p>{{ notice.noticeContent }}</p> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <div class="card-meta"> |
| | | <span class="priority" :class="'priority-' + notice.priority"> |
| | | {{ getPriorityText(notice.priority) }} |
| | | </span> |
| | | <span class="status" :class="'status-' + notice.status"> |
| | | {{ getStatusText(notice.status) }} |
| | | </span> |
| | | </div> |
| | | <div class="card-info"> |
| | | <span class="creator">{{ notice.createBy }}</span> |
| | | <span class="time">{{ notice.createTime }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="card-remark" v-if="notice.remark"> |
| | | <el-icon><InfoFilled /></el-icon> |
| | | <span>{{ notice.remark }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ç©ºç¶æ --> |
| | | <div class="empty-state" v-if="filteredNotices.length === 0"> |
| | | <el-empty description="ææ éç¥å
Œ" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æ°å¢/ç¼è¾å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | :title="dialogTitle" |
| | | v-model="dialogVisible" |
| | | width="800px" |
| | | append-to-body |
| | | @close="resetForm" |
| | | > |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="100px"> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬åæ é¢" prop="noticeTitle"> |
| | | <el-input v-model="form.noticeTitle" placeholder="请è¾å
¥å
¬åæ é¢" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬åç±»å" prop="noticeType"> |
| | | <el-select v-model="form.noticeType" placeholder="è¯·éæ©å
¬åç±»å" style="width: 100%"> |
| | | <el-option label="æ¾åéç¥" value="1" /> |
| | | <el-option label="设å¤ç»´ä¿®éç¥" value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¶æ"> |
| | | <el-radio-group v-model="form.status"> |
| | | <el-radio value="0">è稿</el-radio> |
| | | <el-radio value="1">å·²åå¸</el-radio> |
| | | <el-radio value="2">å·²ä¸çº¿</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¼å
级"> |
| | | <el-select v-model="form.priority" 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-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="å
¬åå
容" prop="noticeContent"> |
| | | <el-input |
| | | v-model="form.noticeContent" |
| | | type="textarea" |
| | | :rows="6" |
| | | placeholder="请è¾å
¥å
¬åå
容" |
| | | maxlength="500" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="夿³¨"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥å¤æ³¨ä¿¡æ¯" |
| | | maxlength="200" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">ç¡® å®</el-button> |
| | | <el-button @click="dialogVisible = false">å æ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search, Calendar, Tools, InfoFilled } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref, reactive, toRefs, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | // ååºå¼æ°æ® |
| | | const data = reactive({ |
| | | searchForm: { |
| | | noticeTitle: "", |
| | | noticeType: "", |
| | | status: "", |
| | | }, |
| | | form: { |
| | | id: undefined, |
| | | noticeTitle: "", |
| | | noticeType: "", |
| | | noticeContent: "", |
| | | status: "0", |
| | | priority: "1", |
| | | remark: "", |
| | | createBy: "", |
| | | createTime: "", |
| | | }, |
| | | rules: { |
| | | noticeTitle: [ |
| | | { required: true, message: "å
¬åæ é¢ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | noticeType: [ |
| | | { required: true, message: "è¯·éæ©å
¬åç±»å", trigger: "change" } |
| | | ], |
| | | noticeContent: [ |
| | | { required: true, message: "å
¬åå
容ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ] |
| | | } |
| | | }); |
| | | |
| | | const { searchForm, form, rules } = toRefs(data); |
| | | |
| | | // 页é¢ç¶æ |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const selectedIds = ref([]); |
| | | const formRef = ref(); |
| | | |
| | | // æ¨¡ææ°æ® - æ ¹æ®æ³å®èåæ¥è®¾è®¡ |
| | | const mockData = [ |
| | | { |
| | | id: 1, |
| | | noticeTitle: "2024å¹´æ¥èæ¾åéç¥", |
| | | noticeType: "1", |
| | | priority: "2", |
| | | status: "1", |
| | | noticeContent: "æ ¹æ®å½å¡é¢åå
Œ
éç¥ï¼2024å¹´æ¥èæ¾å宿å¦ä¸ï¼2æ10æ¥ï¼åä¸ï¼è³2æ17æ¥ï¼åå
«ï¼æ¾åè°ä¼ï¼å
±8天ã2æ4æ¥ï¼æææ¥ï¼ã2æ18æ¥ï¼æææ¥ï¼ä¸çã请åé¨é¨æåå好工ä½å®æã", |
| | | remark: "æ¾åæé´è¯·ä¿æææºç
éï¼å¦æç´§æ¥äºå¡åæ¶èç³»", |
| | | createBy: "人äºé¨", |
| | | createTime: "2024-01-15 10:30:00" |
| | | }, |
| | | { |
| | | id: 2, |
| | | noticeTitle: "2024å¹´æ¸
æèæ¾åéç¥", |
| | | noticeType: "1", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "æ ¹æ®å½å¡é¢åå
Œ
éç¥ï¼2024å¹´æ¸
æèæ¾å宿å¦ä¸ï¼4æ4æ¥ï¼ææåï¼è³4æ6æ¥ï¼ææå
ï¼æ¾åè°ä¼ï¼å
±3天ã4æ7æ¥ï¼æææ¥ï¼ä¸çã", |
| | | remark: "请åé¨é¨å好å¼ç宿ï¼ç¡®ä¿èæ¥æé´å项工使£å¸¸è¿è½¬", |
| | | createBy: "è¡æ¿é¨", |
| | | createTime: "2024-01-14 14:20:00" |
| | | }, |
| | | { |
| | | id: 3, |
| | | noticeTitle: "2024å¹´å³å¨èæ¾åéç¥", |
| | | noticeType: "1", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "æ ¹æ®å½å¡é¢åå
Œ
éç¥ï¼2024å¹´å³å¨èæ¾å宿å¦ä¸ï¼5æ1æ¥ï¼ææä¸ï¼è³5æ5æ¥ï¼æææ¥ï¼æ¾åè°ä¼ï¼å
±5天ã4æ28æ¥ï¼æææ¥ï¼ã5æ11æ¥ï¼ææå
ï¼ä¸çã", |
| | | remark: "æ¾åå请å
³éçµæºï¼é好é¨çªï¼æ³¨æå®å
¨", |
| | | createBy: "è¡æ¿é¨", |
| | | createTime: "2024-01-13 09:15:00" |
| | | }, |
| | | { |
| | | id: 4, |
| | | noticeTitle: "2024年端åèæ¾åéç¥", |
| | | noticeType: "1", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "æ ¹æ®å½å¡é¢åå
Œ
éç¥ï¼2024年端åèæ¾å宿å¦ä¸ï¼6æ8æ¥ï¼ææå
ï¼è³6æ10æ¥ï¼ææä¸ï¼æ¾åè°ä¼ï¼å
±3天ã6æ11æ¥ï¼ææäºï¼ä¸çã", |
| | | remark: "ç¥å¤§å®¶ç«¯åèå¿«ä¹ï¼é家幸ç¦ï¼", |
| | | createBy: "è¡æ¿é¨", |
| | | createTime: "2024-01-12 16:30:00" |
| | | }, |
| | | { |
| | | id: 5, |
| | | noticeTitle: "2024å¹´ä¸ç§èæ¾åéç¥", |
| | | noticeType: "1", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "æ ¹æ®å½å¡é¢åå
Œ
éç¥ï¼2024å¹´ä¸ç§èæ¾å宿å¦ä¸ï¼9æ15æ¥ï¼æææ¥ï¼è³9æ17æ¥ï¼ææäºï¼æ¾åè°ä¼ï¼å
±3天ã9æ14æ¥ï¼ææå
ï¼ä¸çã", |
| | | remark: "ä¸ç§ä½³èï¼ç¥å¤§å®¶å¢åç¾æ»¡ï¼å¹¸ç¦å®åº·ï¼", |
| | | createBy: "è¡æ¿é¨", |
| | | createTime: "2024-01-11 11:20:00" |
| | | }, |
| | | { |
| | | id: 6, |
| | | noticeTitle: "2024å¹´å½åºèæ¾åéç¥", |
| | | noticeType: "1", |
| | | priority: "2", |
| | | status: "1", |
| | | noticeContent: "æ ¹æ®å½å¡é¢åå
Œ
éç¥ï¼2024å¹´å½åºèæ¾å宿å¦ä¸ï¼10æ1æ¥ï¼ææäºï¼è³10æ7æ¥ï¼ææä¸ï¼æ¾åè°ä¼ï¼å
±7天ã9æ29æ¥ï¼æææ¥ï¼ã10æ12æ¥ï¼ææå
ï¼ä¸çã", |
| | | remark: "å½åºæé´è¯·åé¨é¨å好å¼ç宿ï¼ç¡®ä¿å®å
¨ç¨³å®", |
| | | createBy: "è¡æ¿é¨", |
| | | createTime: "2024-01-10 15:45:00" |
| | | }, |
| | | { |
| | | id: 7, |
| | | noticeTitle: "A车é´ç产线年度æ£ä¿®éç¥", |
| | | noticeType: "2", |
| | | priority: "2", |
| | | status: "1", |
| | | noticeContent: "A车é´ç产线å°äº2024å¹´1æ20æ¥ï¼å¨å
ï¼è¿è¡å¹´åº¦æ£ä¿®ç»´æ¤ï¼é¢è®¡åå·¥8å°æ¶ãæ£ä¿®å
容å
æ¬ï¼è®¾å¤æ¸
æ´ã润æ»ä¿å
»ãå®å
¨è£
ç½®æ£æ¥çã请ç产é¨é¨æåè°æ´ç产计åã", |
| | | remark: "ç»´ä¿®æé´è¯·ç¸å
³äººåé
åï¼ç¡®ä¿æ£ä¿®å·¥ä½å®å
¨é¡ºå©è¿è¡", |
| | | createBy: "设å¤é¨", |
| | | createTime: "2024-01-14 14:20:00" |
| | | }, |
| | | { |
| | | id: 8, |
| | | noticeTitle: "B车é´è®¾å¤é¢é²æ§ç»´æ¤éç¥", |
| | | noticeType: "2", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "B车é´å
³é®è®¾å¤å°äº2024å¹´1æ25æ¥è¿è¡é¢é²æ§ç»´æ¤ï¼é¢è®¡åå·¥4å°æ¶ãç»´æ¤å
容å
æ¬ï¼è®¾å¤æ£æ¥ãé¶ä»¶æ´æ¢ãæ§è½æµè¯çã请ç¸å
³é¨é¨é
åã", |
| | | remark: "ç»´æ¤å®æåå°è¿è¡è¯è¿è¡ï¼ç¡®ä¿è®¾å¤æ£å¸¸è¿è¡", |
| | | createBy: "设å¤é¨", |
| | | createTime: "2024-01-13 09:15:00" |
| | | } |
| | | ]; |
| | | |
| | | // 计ç®å±æ§ |
| | | const filteredNotices = computed(() => { |
| | | let filtered = [...mockData]; |
| | | |
| | | if (searchForm.value.noticeTitle) { |
| | | filtered = filtered.filter(item => |
| | | item.noticeTitle.includes(searchForm.value.noticeTitle) |
| | | ); |
| | | } |
| | | if (searchForm.value.noticeType) { |
| | | filtered = filtered.filter(item => |
| | | item.noticeType === searchForm.value.noticeType |
| | | ); |
| | | } |
| | | if (searchForm.value.status !== "") { |
| | | filtered = filtered.filter(item => |
| | | item.status === searchForm.value.status |
| | | ); |
| | | } |
| | | |
| | | return filtered; |
| | | }); |
| | | |
| | | const holidayNotices = computed(() => { |
| | | return filteredNotices.value.filter(notice => notice.noticeType === "1"); |
| | | }); |
| | | |
| | | const maintenanceNotices = computed(() => { |
| | | return filteredNotices.value.filter(notice => notice.noticeType === "2"); |
| | | }); |
| | | |
| | | // æ¹æ³å®ä¹ |
| | | const handleQuery = () => { |
| | | // æç´¢åè½ä¿æä¸åï¼ä½æ°æ®éè¿è®¡ç®å±æ§èªå¨è¿æ»¤ |
| | | }; |
| | | |
| | | const resetQuery = () => { |
| | | searchForm.value = { |
| | | noticeTitle: "", |
| | | noticeType: "", |
| | | status: "" |
| | | }; |
| | | }; |
| | | |
| | | const getPriorityText = (priority) => { |
| | | const priorityMap = { "1": "æ®é", "2": "éè¦", "3": "ç´§æ¥" }; |
| | | return priorityMap[priority] || "æ®é"; |
| | | }; |
| | | |
| | | const getStatusText = (status) => { |
| | | const statusMap = { "0": "è稿", "1": "å·²åå¸", "2": "å·²ä¸çº¿" }; |
| | | return statusMap[status] || "æªç¥"; |
| | | }; |
| | | |
| | | const openForm = (type) => { |
| | | if (type === 'add') { |
| | | dialogTitle.value = "æ°å¢å
Œ"; |
| | | form.value = { |
| | | id: undefined, |
| | | noticeTitle: "", |
| | | noticeType: "", |
| | | noticeContent: "", |
| | | status: "0", |
| | | priority: "1", |
| | | remark: "", |
| | | createBy: userStore.name || "å½åç¨æ·", |
| | | createTime: new Date().toLocaleString() |
| | | }; |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleEdit = (row) => { |
| | | dialogTitle.value = "ç¼è¾å
Œ"; |
| | | form.value = { ...row }; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | selectedIds.value = selection.map(item => item.id); |
| | | }; |
| | | |
| | | const handleDelete = (id) => { |
| | | ElMessageBox.confirm( |
| | | "确认å é¤è¿æ¡å
¬ååï¼", |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning" |
| | | } |
| | | ).then(() => { |
| | | const index = mockData.findIndex(item => item.id === id); |
| | | if (index > -1) { |
| | | mockData.splice(index, 1); |
| | | ElMessage.success("å 餿å"); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | if (form.value.id) { |
| | | // ç¼è¾æ¨¡å¼ |
| | | const index = mockData.findIndex(item => item.id === form.value.id); |
| | | if (index > -1) { |
| | | mockData[index] = { ...form.value }; |
| | | } |
| | | ElMessage.success("ä¿®æ¹æå"); |
| | | } else { |
| | | // æ°å¢æ¨¡å¼ |
| | | const newId = Math.max(...mockData.map(item => item.id)) + 1; |
| | | const newNotice = { |
| | | ...form.value, |
| | | id: newId, |
| | | createTime: new Date().toLocaleString() |
| | | }; |
| | | mockData.unshift(newNotice); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const resetForm = () => { |
| | | formRef.value?.resetFields(); |
| | | }; |
| | | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | // 页é¢å è½½å®æ |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .search_form { |
| | | background: #fff; |
| | | padding: 20px; |
| | | margin-bottom: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .search_title { |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .ml10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .notice-board { |
| | | background: #f5f7fa; |
| | | padding: 20px; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .notice-section { |
| | | margin-bottom: 30px; |
| | | } |
| | | |
| | | .section-header { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .section-header h3 { |
| | | margin: 0; |
| | | color: #303133; |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .section-count { |
| | | margin-left: 10px; |
| | | background: #409eff; |
| | | color: white; |
| | | padding: 2px 8px; |
| | | border-radius: 12px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .notice-cards { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); |
| | | gap: 20px; |
| | | } |
| | | |
| | | .notice-card { |
| | | background: white; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
| | | transition: all 0.3s ease; |
| | | border-left: 4px solid transparent; |
| | | } |
| | | |
| | | .notice-card:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .holiday-card { |
| | | border-left-color: #67c23a; |
| | | } |
| | | |
| | | .maintenance-card { |
| | | border-left-color: #e6a23c; |
| | | } |
| | | |
| | | .urgent { |
| | | border-left-color: #f56c6c; |
| | | background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .card-title { |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | flex: 1; |
| | | } |
| | | |
| | | .holiday-icon { |
| | | color: #67c23a; |
| | | margin-right: 8px; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .maintenance-icon { |
| | | color: #e6a23c; |
| | | margin-right: 8px; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .card-actions { |
| | | display: flex; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .card-content { |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .card-content p { |
| | | margin: 0; |
| | | color: #606266; |
| | | line-height: 1.6; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .card-footer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .card-meta { |
| | | display: flex; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .priority, .status { |
| | | padding: 2px 8px; |
| | | border-radius: 12px; |
| | | font-size: 12px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .priority-1 { background: #f0f9ff; color: #0369a1; } |
| | | .priority-2 { background: #fef3c7; color: #d97706; } |
| | | .priority-3 { background: #fef2f2; color: #dc2626; } |
| | | |
| | | .status-0 { background: #f3f4f6; color: #6b7280; } |
| | | .status-1 { background: #d1fae5; color: #059669; } |
| | | .status-2 { background: #fef3c7; color: #d97706; } |
| | | |
| | | .card-info { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: flex-end; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .creator { |
| | | font-weight: 500; |
| | | margin-bottom: 2px; |
| | | } |
| | | |
| | | .card-remark { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 6px; |
| | | padding: 8px 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | font-size: 12px; |
| | | color: #606266; |
| | | border-left: 3px solid #409eff; |
| | | } |
| | | |
| | | .empty-state { |
| | | text-align: center; |
| | | padding: 60px 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | |
| | | /* ååºå¼è®¾è®¡ */ |
| | | @media (max-width: 768px) { |
| | | .notice-cards { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | |
| | | .search_form { |
| | | flex-direction: column; |
| | | gap: 15px; |
| | | } |
| | | |
| | | .search_form > div { |
| | | width: 100%; |
| | | } |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="warning-system"> |
| | | <h2>é¢è¦è卿ºå¶</h2> |
| | | |
| | | <!-- ç»è®¡å¡ç --> |
| | | <div class="stats"> |
| | | <div class="stat-card red"> |
| | | <span class="number">2</span> |
| | | <span class="label">红è²é¢è¦</span> |
| | | </div> |
| | | <div class="stat-card orange"> |
| | | <span class="number">1</span> |
| | | <span class="label">æ©è²é¢è¦</span> |
| | | </div> |
| | | <div class="stat-card yellow"> |
| | | <span class="number">1</span> |
| | | <span class="label">é»è²é¢è¦</span> |
| | | </div> |
| | | <div class="stat-card green"> |
| | | <span class="number">1</span> |
| | | <span class="label">绿è²é¢è¦</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- é¢è¦å表 --> |
| | | <div class="warning-list"> |
| | | <h3>é¢è¦å表</h3> |
| | | <table> |
| | | <thead> |
| | | <tr> |
| | | <th>ç¼å·</th> |
| | | <th>æ é¢</th> |
| | | <th>ç±»å</th> |
| | | <th>ç级</th> |
| | | <th>ç¶æ</th> |
| | | <th>责任人</th> |
| | | <th>æä½</th> |
| | | </tr> |
| | | </thead> |
| | | <tbody> |
| | | <tr v-for="warning in warnings" :key="warning.id"> |
| | | <td>{{ warning.id }}</td> |
| | | <td>{{ warning.title }}</td> |
| | | <td>{{ warning.type }}</td> |
| | | <td> |
| | | <span :class="['level-tag', warning.level]"> |
| | | {{ warning.levelText }} |
| | | </span> |
| | | </td> |
| | | <td> |
| | | <span :class="['status-tag', warning.status]"> |
| | | {{ warning.statusText }} |
| | | </span> |
| | | </td> |
| | | <td>{{ warning.responsible }}</td> |
| | | <td> |
| | | <button @click="viewDetail(warning)">æ¥ç详æ
</button> |
| | | </td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | </div> |
| | | |
| | | <!-- 详æ
å¯¹è¯æ¡ --> |
| | | <div v-if="showDetail" class="modal"> |
| | | <div class="modal-content"> |
| | | <h3>é¢è¦è¯¦æ
</h3> |
| | | <div v-if="currentWarning"> |
| | | <p><strong>ç¼å·ï¼</strong>{{ currentWarning.id }}</p> |
| | | <p><strong>æ é¢ï¼</strong>{{ currentWarning.title }}</p> |
| | | <p><strong>ç±»åï¼</strong>{{ currentWarning.type }}</p> |
| | | <p><strong>ç级ï¼</strong>{{ currentWarning.levelText }}</p> |
| | | <p><strong>æè¿°ï¼</strong>{{ currentWarning.description }}</p> |
| | | <p><strong>å½±åï¼</strong>{{ currentWarning.impact }}</p> |
| | | <p><strong>建议ï¼</strong>{{ currentWarning.suggestions }}</p> |
| | | </div> |
| | | <button @click="showDetail = false">å
³é</button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'WarningSystem', |
| | | data() { |
| | | return { |
| | | showDetail: false, |
| | | currentWarning: null, |
| | | warnings: [ |
| | | { |
| | | id: 'W001', |
| | | title: '项ç®é¢ç®è¶
æ¯é¢è¦', |
| | | type: 'è´¢å¡é¢è¦', |
| | | level: 'red', |
| | | levelText: '红è²é¢è¦', |
| | | status: 'pending', |
| | | statusText: 'å¾
å¤ç', |
| | | responsible: 'å¼ ç»ç', |
| | | description: 'A项ç®é¢ç®æ§è¡ç已达95%ï¼é¢è®¡å°è¶
åºé¢ç®èå´ã', |
| | | impact: 'å½±åé¡¹ç®æ´ä½è´¢å¡ææ ï¼å¯è½å¯¼è´é¡¹ç®äºæ', |
| | | suggestions: 'æåéå¿
è¦æ¯åºï¼ä¼åèµæºé
ç½®ï¼ç³è¯·é¢ç®è°æ´' |
| | | }, |
| | | { |
| | | id: 'W002', |
| | | title: 'ååå°æé¢è¦', |
| | | type: 'åè§é¢è¦', |
| | | level: 'orange', |
| | | levelText: 'æ©è²é¢è¦', |
| | | status: 'processing', |
| | | statusText: 'å¤çä¸', |
| | | responsible: 'æä¸»ç®¡', |
| | | description: 'ä¸ä¾åºåBçååå°äº2024å¹´1æ25æ¥å°æã', |
| | | impact: 'å½±åä¾åºé¾ç¨³å®æ§ï¼å¯è½å¯¼è´æå¡ä¸æ', |
| | | suggestions: 'è¯ä¼°ä¾åºå表ç°ï¼åå¤ç»ç¾ææï¼å¶å®å¤éæ¹æ¡' |
| | | }, |
| | | { |
| | | id: 'W003', |
| | | title: '设å¤ç»´æ¤é¢è¦', |
| | | type: 'è¿è¥é¢è¦', |
| | | level: 'yellow', |
| | | levelText: 'é»è²é¢è¦', |
| | | status: 'pending', |
| | | statusText: 'å¾
å¤ç', |
| | | responsible: 'çå·¥ç¨å¸', |
| | | description: 'ç产线设å¤Cå·²è¿è¡8000å°æ¶ï¼æ¥è¿ç»´æ¤å¨æã', |
| | | impact: 'å¯è½å½±åç产æçå产åè´¨é', |
| | | suggestions: 'å®æç»´æ¤æ¶é´ï¼åå¤å¤ä»¶ï¼å¶å®ç»´æ¤è®¡å' |
| | | }, |
| | | { |
| | | id: 'W004', |
| | | title: '人åé
ç½®é¢è¦', |
| | | type: 'è¿è¥é¢è¦', |
| | | level: 'green', |
| | | levelText: '绿è²é¢è¦', |
| | | status: 'resolved', |
| | | statusText: '已解å³', |
| | | responsible: 'èµµHR', |
| | | description: 'ææ¯é¨é¨äººåé
ç½®å
è¶³ï¼é¡¹ç®è¿åº¦æ£å¸¸ã', |
| | | impact: 'æ è´é¢å½±å', |
| | | suggestions: 'ç»§ç»çæ§äººåé
ç½®æ
åµ' |
| | | }, |
| | | { |
| | | id: 'W005', |
| | | title: 'è´¨éäºæ
é¢è¦', |
| | | type: 'è¿è¥é¢è¦', |
| | | level: 'red', |
| | | levelText: '红è²é¢è¦', |
| | | status: 'pending', |
| | | statusText: 'å¾
å¤ç', |
| | | responsible: 'éæ»ç', |
| | | description: '产åDå¨å®¢æ·ç°åºåºç°è´¨éé®é¢ã', |
| | | impact: 'å½±åå®¢æ·æ»¡æåº¦ï¼å¯è½é æç»æµæå¤±', |
| | | suggestions: 'ç«å³å¬åé®é¢äº§åï¼åæåå ï¼å¶å®æ¹è¿æªæ½' |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | | methods: { |
| | | viewDetail(warning) { |
| | | this.currentWarning = warning |
| | | this.showDetail = true |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .warning-system { |
| | | padding: 20px; |
| | | max-width: 1200px; |
| | | margin: 0 auto; |
| | | } |
| | | |
| | | h2 { |
| | | color: #333; |
| | | margin-bottom: 30px; |
| | | } |
| | | |
| | | .stats { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
| | | gap: 20px; |
| | | margin-bottom: 30px; |
| | | } |
| | | |
| | | .stat-card { |
| | | padding: 20px; |
| | | border-radius: 8px; |
| | | color: white; |
| | | text-align: center; |
| | | box-shadow: 0 2px 8px rgba(0,0,0,0.1); |
| | | } |
| | | |
| | | .stat-card.red { background: linear-gradient(135deg, #ff6b6b, #ee5a52); } |
| | | .stat-card.orange { background: linear-gradient(135deg, #ffa726, #ff9800); } |
| | | .stat-card.yellow { background: linear-gradient(135deg, #ffd54f, #ffc107); } |
| | | .stat-card.green { background: linear-gradient(135deg, #66bb6a, #4caf50); } |
| | | |
| | | .stat-card .number { |
| | | display: block; |
| | | font-size: 32px; |
| | | font-weight: bold; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .stat-card .label { |
| | | font-size: 14px; |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | .warning-list h3 { |
| | | margin-bottom: 20px; |
| | | color: #333; |
| | | } |
| | | |
| | | table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | background: white; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0,0,0,0.1); |
| | | } |
| | | |
| | | th, td { |
| | | padding: 12px; |
| | | text-align: left; |
| | | border-bottom: 1px solid #eee; |
| | | } |
| | | |
| | | th { |
| | | background: #f8f9fa; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .level-tag, .status-tag { |
| | | padding: 4px 8px; |
| | | border-radius: 4px; |
| | | font-size: 12px; |
| | | color: white; |
| | | } |
| | | |
| | | .level-tag.red { background: #f56c6c; } |
| | | .level-tag.orange { background: #e6a23c; } |
| | | .level-tag.yellow { background: #e6a23c; } |
| | | .level-tag.green { background: #67c23a; } |
| | | |
| | | .status-tag.pending { background: #f56c6c; } |
| | | .status-tag.processing { background: #e6a23c; } |
| | | .status-tag.resolved { background: #67c23a; } |
| | | |
| | | button { |
| | | padding: 6px 12px; |
| | | margin: 0 4px; |
| | | border: none; |
| | | border-radius: 4px; |
| | | cursor: pointer; |
| | | font-size: 12px; |
| | | background: #409eff; |
| | | color: white; |
| | | } |
| | | |
| | | .modal { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | background: rgba(0,0,0,0.5); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .modal-content { |
| | | background: white; |
| | | padding: 30px; |
| | | border-radius: 8px; |
| | | max-width: 600px; |
| | | width: 90%; |
| | | max-height: 80vh; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .modal-content h3 { |
| | | margin-bottom: 20px; |
| | | color: #333; |
| | | } |
| | | |
| | | .modal-content p { |
| | | margin-bottom: 15px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .modal-content strong { |
| | | color: #333; |
| | | } |
| | | |
| | | .modal-content button { |
| | | background: #409eff; |
| | | color: white; |
| | | padding: 10px 20px; |
| | | font-size: 14px; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-card shadow="never"> |
| | | <div class="toolbar"> |
| | | <el-input |
| | | v-model="query.keyword" |
| | | placeholder="æç´¢åç§°/ç±»å«" |
| | | clearable |
| | | style="width: 240px" |
| | | @keyup.enter="handleSearch" |
| | | /> |
| | | <el-select |
| | | v-model="query.status" |
| | | placeholder="ç¶æ" |
| | | clearable |
| | | style="width: 140px; margin-left: 12px" |
| | | > |
| | | <el-option label="å¯ç¨" value="å¯ç¨" /> |
| | | <el-option label="åç¨" value="åç¨" /> |
| | | </el-select> |
| | | <el-button type="primary" style="margin-left: 12px" @click="handleSearch">æ¥è¯¢</el-button> |
| | | <el-button @click="resetQuery">éç½®</el-button> |
| | | <el-button type="success" plain style="float: right" @click="openCreate">æ°å¢</el-button> |
| | | </div> |
| | | |
| | | <el-table :data="pagedList" border style="width: 100%" height="480"> |
| | | <el-table-column prop="id" label="ç¼å·" width="90" sortable /> |
| | | <el-table-column prop="name" label="åç§°" min-width="140" /> |
| | | <el-table-column prop="category" label="ç±»å«" width="120" /> |
| | | <el-table-column prop="stock" label="åºå" width="100" sortable /> |
| | | <el-table-column prop="price" label="åä»·(Â¥)" width="120"> |
| | | <template #default="scope">{{ formatPrice(scope.row.price) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="ç¶æ" width="120"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'å¯ç¨' ? 'success' : 'info'">{{ scope.row.status }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="updatedAt" label="æ´æ°æ¶é´" min-width="160" /> |
| | | <el-table-column label="æä½" width="180" fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="openEdit(scope.row)">ç¼è¾</el-button> |
| | | <el-button link type="danger" @click="handleDelete(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div class="pagination"> |
| | | <el-pagination |
| | | background |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="filteredList.length" |
| | | :page-sizes="[5, 10, 20, 50]" |
| | | :page-size="pager.pageSize" |
| | | :current-page="pager.pageNum" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <el-dialog v-model="dialogVisible" :title="isEdit ? 'ç¼è¾' : 'æ°å¢'" width="520px"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="90px"> |
| | | <el-form-item label="åç§°" prop="name"> |
| | | <el-input v-model="form.name" placeholder="请è¾å
¥åç§°" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç±»å«" prop="category"> |
| | | <el-select v-model="form.category" placeholder="è¯·éæ©ç±»å«" style="width: 100%"> |
| | | <el-option label="åæ" value="åæ" /> |
| | | <el-option label="åæå" value="åæå" /> |
| | | <el-option label="æå" value="æå" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="åºå" prop="stock"> |
| | | <el-input v-model.number="form.stock" type="number" min="0" /> |
| | | </el-form-item> |
| | | <el-form-item label="åä»·(Â¥)" prop="price"> |
| | | <el-input v-model.number="form.price" type="number" min="0" step="0.01" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-radio-group v-model="form.status"> |
| | | <el-radio label="å¯ç¨">å¯ç¨</el-radio> |
| | | <el-radio label="åç¨">åç¨</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="dialogVisible = false">å æ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡® å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, nextTick } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | |
| | | defineOptions({ name: 'FakePage' }) |
| | | |
| | | const query = reactive({ |
| | | keyword: '', |
| | | status: '' |
| | | }) |
| | | |
| | | const pager = reactive({ |
| | | pageNum: 1, |
| | | pageSize: 10 |
| | | }) |
| | | |
| | | const allList = ref(generateMockData()) |
| | | |
| | | const filteredList = computed(() => { |
| | | const keyword = (query.keyword || '').trim() |
| | | const status = query.status |
| | | return allList.value.filter(item => { |
| | | const hitKeyword = !keyword || item.name.includes(keyword) || item.category.includes(keyword) |
| | | const hitStatus = !status || item.status === status |
| | | return hitKeyword && hitStatus |
| | | }) |
| | | }) |
| | | |
| | | const pagedList = computed(() => { |
| | | const start = (pager.pageNum - 1) * pager.pageSize |
| | | const end = start + pager.pageSize |
| | | return filteredList.value.slice(start, end) |
| | | }) |
| | | |
| | | function handleSearch() { |
| | | pager.pageNum = 1 |
| | | } |
| | | |
| | | function resetQuery() { |
| | | query.keyword = '' |
| | | query.status = '' |
| | | pager.pageNum = 1 |
| | | } |
| | | |
| | | function handleSizeChange(size) { |
| | | pager.pageSize = size |
| | | pager.pageNum = 1 |
| | | } |
| | | |
| | | function handleCurrentChange(page) { |
| | | pager.pageNum = page |
| | | } |
| | | |
| | | function formatPrice(val) { |
| | | return Number(val || 0).toFixed(2) |
| | | } |
| | | |
| | | // æ°å¢/ç¼è¾ |
| | | const dialogVisible = ref(false) |
| | | const isEdit = ref(false) |
| | | const formRef = ref() |
| | | const form = reactive({ id: null, name: '', category: '', stock: 0, price: 0, status: 'å¯ç¨' }) |
| | | |
| | | const rules = { |
| | | name: [{ required: true, message: '请è¾å
¥åç§°', trigger: 'blur' }], |
| | | category: [{ required: true, message: 'è¯·éæ©ç±»å«', trigger: 'change' }], |
| | | stock: [{ required: true, message: '请è¾å
¥åºå', trigger: 'blur' }], |
| | | price: [{ required: true, message: '请è¾å
¥åä»·', trigger: 'blur' }] |
| | | } |
| | | |
| | | function openCreate() { |
| | | isEdit.value = false |
| | | Object.assign(form, { id: null, name: '', category: '', stock: 0, price: 0, status: 'å¯ç¨' }) |
| | | dialogVisible.value = true |
| | | nextTick(() => formRef.value?.clearValidate?.()) |
| | | } |
| | | |
| | | function openEdit(row) { |
| | | isEdit.value = true |
| | | Object.assign(form, JSON.parse(JSON.stringify(row))) |
| | | dialogVisible.value = true |
| | | nextTick(() => formRef.value?.clearValidate?.()) |
| | | } |
| | | |
| | | function submitForm() { |
| | | formRef.value?.validate?.((valid) => { |
| | | if (!valid) return |
| | | if (isEdit.value) { |
| | | const index = allList.value.findIndex(x => x.id === form.id) |
| | | if (index > -1) { |
| | | allList.value[index] = { ...form, updatedAt: nowString() } |
| | | ElMessage.success('å·²ä¿å') |
| | | } |
| | | } else { |
| | | const newId = Date.now() |
| | | allList.value.unshift({ ...form, id: newId, updatedAt: nowString() }) |
| | | ElMessage.success('å·²æ°å¢') |
| | | } |
| | | dialogVisible.value = false |
| | | }) |
| | | } |
| | | |
| | | function handleDelete(row) { |
| | | ElMessageBox.confirm(`确认å é¤ã${row.name}ãåï¼`, 'æç¤º', { type: 'warning' }) |
| | | .then(() => { |
| | | allList.value = allList.value.filter(x => x.id !== row.id) |
| | | ElMessage.success('å·²å é¤') |
| | | }) |
| | | .catch(() => {}) |
| | | } |
| | | |
| | | function generateMockData() { |
| | | const categories = ['åæ', 'åæå', 'æå'] |
| | | const statusOptions = ['å¯ç¨', 'åç¨'] |
| | | const list = [] |
| | | for (let i = 1; i <= 36; i++) { |
| | | list.push({ |
| | | id: i, |
| | | name: `ç©æ-${i.toString().padStart(3, '0')}`, |
| | | category: categories[i % categories.length], |
| | | stock: Math.floor(Math.random() * 1000), |
| | | price: (Math.random() * 500 + 10).toFixed(2), |
| | | status: statusOptions[i % 2], |
| | | updatedAt: nowString() |
| | | }) |
| | | } |
| | | return list |
| | | } |
| | | |
| | | function nowString() { |
| | | const d = new Date() |
| | | const yyyy = d.getFullYear() |
| | | const MM = String(d.getMonth() + 1).padStart(2, '0') |
| | | const dd = String(d.getDate()).padStart(2, '0') |
| | | const hh = String(d.getHours()).padStart(2, '0') |
| | | const mm = String(d.getMinutes()).padStart(2, '0') |
| | | const ss = String(d.getSeconds()).padStart(2, '0') |
| | | return `${yyyy}-${MM}-${dd} ${hh}:${mm}:${ss}` |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .toolbar { |
| | | margin-bottom: 12px; |
| | | } |
| | | .pagination { |
| | | margin-top: 12px; |
| | | text-align: right; |
| | | } |
| | | </style> |
| | | |
| | | |
| | | |