多体系建设信息化条统-前端
祖安之光
2025-11-12 393233bf1f5f8949e249eec2c6113f6011ea6771
修改新增
4 files modified
2 files added
1772 ■■■■■ changed files
package.json 1 ●●●● patch | view | raw | blame | history
src/api/system/notice.js 24 ●●●●● patch | view | raw | blame | history
src/views/build/conpanyFunctionConsult/infoPlatform/components/editDialog.vue 97 ●●●●● patch | view | raw | blame | history
src/views/menuPage.vue 1154 ●●●●● patch | view | raw | blame | history
src/views/work/noticeMng/components/editDialog.vue 271 ●●●●● patch | view | raw | blame | history
src/views/work/noticeMng/index.vue 225 ●●●●● patch | view | raw | blame | history
package.json
@@ -43,6 +43,7 @@
    "jsencrypt": "3.3.1",
    "jspdf": "^3.0.1",
    "jszip-utils": "^0.1.0",
    "lunar-calendar": "^0.1.4",
    "moment": "^2.30.1",
    "nprogress": "0.2.0",
    "pinia": "2.0.22",
src/api/system/notice.js
@@ -42,3 +42,27 @@
    method: 'delete'
  })
}
export function getIndexTitle(query) {
  return request({
    url: '/system/memo/getIndexTitle',
    method: 'get',
    params: query
  })
}
export function getMemoList(query) {
  return request({
    url: '/system/memo/getMemo',
    method: 'get',
    params: query
  })
}
export function updateMemo(data) {
  return request({
    url: '/system/memo/saveMemo',
    method: 'post',
    data: data
  })
}
src/views/build/conpanyFunctionConsult/infoPlatform/components/editDialog.vue
@@ -31,6 +31,17 @@
              placeholder="请选择创建时间"
          />
        </el-form-item>
        <el-form-item label="平台链接:" prop="platformAddress">
          <el-input v-model.trim="state.form.platformAddress" :disabled="state.title =='查看'" placeholder="平台链接"></el-input>
        </el-form-item>
        <el-form-item label="系统图标" prop="platformPic">
          <el-upload accept="image/*" :action="state.uploadUrl" :disabled="state.title =='查看'" list-type="picture-card" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.fileLimit' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)" >
            <el-icon><Plus /></el-icon>
            <template #tip>
              <div class="el-upload__tip">支持上传图片格式,尺寸小于5M,最多可上传1份</div>
            </template>
          </el-upload>
        </el-form-item>
      </el-form>
      <template #footer v-if="state.title !='查看'">
        <span class="dialog-footer">
@@ -48,25 +59,44 @@
import {Base64} from "js-base64"
import {getCompany} from "@/api/onlineEducation/company";
import {updateInfoPlatforms} from "@/api/staffManage/staff";
import {delPic} from "@/api/onlineEducation/banner";
import {getToken} from "@/utils/auth";
const emit = defineEmits(["getList"]);
const dialogVisible = ref(false)
const superRef = ref()
const checkFiles = (rule, value, callback) => {
  if (state.fileList.length == 0) {
    callback(new Error('请上传系统图标'))
  } else {
    callback()
  }
}
const state = reactive({
  title: '',
  form: {
    id: null,
    platformName: '',
    buildDate: '',
    companyId: null
    companyId: null,
    platformAddress: '',
    platformPic: ''
  },
  formRules:{
    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
    platformName: [{ required: true, message: '请输入信息平台名称', trigger: 'blur' }],
    buildDate: [{ required: true, message: '请选择平台创建时间', trigger: 'blur' }]
    buildDate: [{ required: true, message: '请选择平台创建时间', trigger: 'blur' }],
    platformAddress: [{ required: true, message: '请填写平台链接', trigger: 'blur' }],
    platformPic: [{ required: true, validator: checkFiles, trigger: 'blur' }]
  },
  isAdmin: false,
  companyList: []
  companyList: [],
  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
  header: {
    Authorization: getToken()
  },
  fileLimit: 1,
  fileList: []
})
onMounted(() => {
@@ -85,10 +115,63 @@
        state.form[key] = value[key]
      }
    })
    if(value.platformPic) {
      const obj = {
        url: import.meta.env.VITE_APP_BASE_API + '/' + value.platformPic,
        name: value.platformName
      }
      state.fileList = [obj]
    }
  }
  dialogVisible.value = true
}
const handleAvatarSuccess = (res, uploadFile) => {
  if(res.code == 200){
    state.form.platformPic = res.data.path
  }else{
    state.fileList = []
    ElMessage({
      type: 'warning',
      message: '文件上传失败'
    })
  }
}
const showTip =()=>{
  ElMessage({
    type: 'warning',
    message: '超出文件上传数量'
  });
}
const picSize = async (rawFile) => {
  if(rawFile.size / 1024 / 1024 > 5){
    ElMessage({
      type: 'warning',
      message: '文件大小不能超过5M'
    });
    return false
  }
};
const handleRemove = async (file, uploadFiles) => {
  let path = state.form.platformPic
  await delPic({path: path}).then(res => {
    if(res.code == 200){
      // ElMessage({
      //   type: 'success',
      //   message: '文件已删除'
      // })
      state.form.platformPic = ''
    }else{
      ElMessage({
        type: 'warning',
        message: res.message
      })
    }
  }).catch(() => {
    state.form.platformPic = ''
  });
}
const onSubmit = async () => {
  const valid = await superRef.value.validate();
@@ -98,7 +181,9 @@
      data = {
        platformName: state.form.platformName,
        buildDate: state.form.buildDate,
        companyId: state.form.companyId
        companyId: state.form.companyId,
        platformAddress: state.form.platformAddress,
        platformPic: state.form.platformPic
      }
    }else{
      data = state.form
@@ -121,7 +206,9 @@
    id: null,
    platformName: '',
    buildDate: '',
    companyId: null
    companyId: null,
    platformAddress: '',
    platformPic: ''
  }
  superRef.value.clearValidate();
  superRef.value.resetFields()
src/views/menuPage.vue
@@ -1,6 +1,5 @@
<template>
  <div class="system-select-container">
    <!-- 顶部用户信息栏 -->
    <div class="user-info-bar">
      <div class="user-left"></div>
      <h3 class="user-details">欢迎访问多体系建设信息化系统</h3>
@@ -13,9 +12,6 @@
          </div>
          <template #dropdown>
            <el-dropdown-menu>
<!--              <el-dropdown-item command="info">-->
<!--                <span>基本信息</span>-->
<!--              </el-dropdown-item>-->
              <el-dropdown-item command="password">
                <span>修改密码</span>
              </el-dropdown-item>
@@ -28,47 +24,198 @@
      </div>
    </div>
    <!-- 系统选择区域 -->
    <!-- 重新布局的系统选择区域 -->
    <div class="systems-container">
      <div class="layout-container">
        <!-- 左侧列 -->
        <div class="left-column">
          <!-- 通知公告 -->
          <div class="module-card notice-module">
            <div class="module-header">
              <h3>通知公告</h3>
              <span class="more-link" @click="toNoticeMng">更多 ></span>
            </div>
            <div class="notice-list">
              <div class="notice-item" v-for="item in noticeList" :key="item">
                <span class="notice-title" @click="openNoticeFile(item.filePath)">{{item.content}}</span>
                <span class="notice-date">{{item.publishDate}}</span>
              </div>
            </div>
          </div>
          <!-- 流程中心 -->
          <div class="module-card process-module">
            <div class="module-header">
              <h3>流程中心</h3>
<!--              <span class="more-link">更多 ></span>-->
            </div>
            <div class="process-list" v-if="flowList && flowList.length>0">
              <div class="process-item" v-for="process in flowList" :key="process.id">
                <div class="process-info">
                  <span class="process-name">{{process.title}}</span>
                  <span class="process-status" :class="{processing: process.type == 1,pending: process.type == 2,success: process.type == 3,normal: process.type == 4}">
                    {{process.type == 1? '内审实施计划':process.type == 2? '培训计划':process.type == 3? '项目评审':'年度检定计划'}}
                  </span>
                </div>
              </div>
            </div>
            <div class="process-list" v-else>
              <span style="color: #999;font-size: 16px">暂无流程</span>
            </div>
          </div>
          <!-- 备忘录 -->
          <div class="module-card memo-module">
            <div class="module-header">
              <h3>备忘录</h3>
              <el-button @click="addMemo" type="primary">保存</el-button>
            </div>
            <div class="memo-content">
              <div class="memo-input">
                <el-input
                    v-model="memo.content"
                    placeholder="添加新的备忘录..."
                    type="textarea"
                    :autosize="{ minRows: 6, maxRows: 10}"
                >
                </el-input>
                <div class="memo-time" v-if="memo.updateTime">{{memo.updateTime}}</div>
              </div>
            </div>
          </div>
        </div>
        <!-- 右侧列 -->
        <div class="right-column">
          <!-- 快捷入口 -->
          <div class="module-card quick-access-module">
            <div class="module-header">
              <h3>快捷入口</h3>
              <div class="pagination-controls" v-if="platformList.length > 9">
                <el-button
                    :icon="ArrowLeft"
                    @click="prevPage"
                    size="small"
                    :disabled="currentPage === 0"
                    circle
                />
                <span class="page-info">{{ currentPage + 1 }}/{{ totalPages }}</span>
                <el-button
                    :icon="ArrowRight"
                    @click="nextPage"
                    size="small"
                    :disabled="currentPage === totalPages - 1"
                    circle
                />
              </div>
            </div>
            <div class="systems-grid-container">
      <div class="systems-grid">
        <div
            v-for="system in systems"
                    v-for="(system, index) in displayedSystems"
            :key="system.id"
            class="system-card"
            @mouseenter="handleCardEnter($event, system.id)"
            @mousemove="handleCardMove($event, system.id)"
            @mouseleave="handleCardLeave(system.id)"
            @click="enterSystem(system.id)"
                    @click="enterSystem(system.platformAddress, getActualIndex(index))"
            :style="getCardStyle(system.id)"
        >
          <div class="card-content">
          <div class="system-icon">
            <img :src="system.icon"/>
                      <el-image v-if="getActualIndex(index) == 0" :src="system.platformPic"/>
                      <el-image v-else :src="picUrl + system.platformPic"/>
          </div>
          <h3>{{ system.name }}</h3>
          <p>{{ system.description }}</p>
                    <h3>{{ system.platformName }}</h3>
                  </div>
                </div>
                <!-- 填充空白格子 -->
                <div
                    v-if="showEmptyCard && displayedSystems.length < 9"
                    class="system-card empty-card"
                    @click="openAdd('add',{})"
                >
                  <div class="card-content">
                    <div class="system-icon">
                      <el-icon><Plus /></el-icon>
                    </div>
                    <h3>新增平台</h3>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <!-- 日历 -->
          <div class="module-card calendar-module">
            <div class="module-header">
              <h3>日历</h3>
            </div>
            <div class="calendar-header">
              <div class="lunar-info">
                <span class="lunar-date-full">{{ currentLunarDate }}</span>
                <span class="lunar-year">{{ currentLunarYear }}</span>
              </div>
              <div class="current-date-info">
                <div class="solar-date-large">{{ currentSolarDate }}</div>
                <div class="week-day">{{ currentWeekDay }}</div>
              </div>
              <div class="calendar-actions">
                <el-button-group>
                  <el-button :icon="ArrowLeft" @click="prevMonth" size="small" />
                  <el-button @click="goToday" size="small">今天</el-button>
                  <el-button :icon="ArrowRight" @click="nextMonth" size="small" />
                </el-button-group>
              </div>
            </div>
            <div class="calendar-content">
              <el-calendar v-model="currentDate" ref="calendarRef">
                <template #header>
                  <!-- 隐藏默认header -->
                  <div style="display: none;"></div>
                </template>
                <template #date-cell="{ data }">
                  <div class="calendar-date" :class="{ 'is-today': isToday(data.day), 'is-current-month': isCurrentMonth(data.day) }">
                    <div class="solar-date">{{ getSolarDate(data.day) }}</div>
                    <div class="lunar-date">{{ getLunarDate(data.day) }}</div>
                    <div v-if="hasEvent(data.day)" class="calendar-event-dot"></div>
                  </div>
                </template>
              </el-calendar>
            </div>
          </div>
        </div>
      </div>
    </div>
    <user-dialog ref="reviewRef"></user-dialog>
    <edit-dialog ref="dialogRef" @getList=getPlatformList></edit-dialog>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import {ref, onMounted, computed, reactive, toRefs, watch} from 'vue'
import { useRouter, useRoute } from 'vue-router'
import {getToken, removeToken} from "@/utils/auth";
import Cookies from "js-cookie";
import {ElMessage, ElMessageBox} from "element-plus";
import { Plus, ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
import  useUserStore from '@/store/modules/user'
import userDialog from '@/views/build/conpanyFunctionConsult/staffManage/staffRegister/components/staffDialog.vue'
import editDialog from '@/views/build/conpanyFunctionConsult/infoPlatform/components/editDialog.vue'
import menu1 from '@/assets/icons/menu1.png'
import menu2 from '@/assets/icons/menu2.png'
import menu3 from '@/assets/icons/menu3.png'
import menu4 from '@/assets/icons/menu4.png'
import menu5 from '@/assets/icons/menu5.png'
import menu6 from '@/assets/icons/menu6.png'
// 引入农历库
import * as lunarCalendar from 'lunar-calendar'
import {getIndexTitle, getMemoList, listNotice, updateMemo} from "@/api/system/notice";
import {renderAsync} from "docx-preview";
import {getInfoPlatforms} from "@/api/staffManage/staff";
import {getCompany} from "@/api/onlineEducation/company";
const router = useRouter()
const route = useRoute();
@@ -77,17 +224,52 @@
const userName = ref('')
const userTypeName = ref('')
const cardStates = ref({})
const currentDate = ref(new Date())
const newMemo = ref('')
const calendarRef = ref()
const dialogRef = ref()
const userStore = useUserStore()
const state = reactive({
  noticeParams: {
    pageNum: 1,
    pageSize: 6,
    companyId: null
  },
  platformParams: {
    pageNum: 1,
    pageSize: 99,
    companyId: null
  },
  noticeList: [],
  platformList: [],
  picUrl: import.meta.env.VITE_APP_BASE_API + '/',
  isAdmin: false,
  companyList: [],
  flowList: [],
  memo: {}
})
const { noticeParams,platformParams, noticeList,platformList,picUrl,isAdmin,companyList,flowList,memo } = toRefs(state)
// 组件挂载时获取用户信息和系统列表
onMounted(() => {
onMounted(async () => {
  if(getToken()){
    userInfo.value = JSON.parse(Cookies.get('userInfo'))
    userName.value = userInfo.value.username
    userTypeName.value = userInfo.value.userType == 0 ? '系统管理员' : (userInfo.value.userType == 1 ||  userInfo.value.userType == 2 || userInfo.value.userType == 3) ? '企业用户' :userInfo.value.userType == 6 ? '企业管理员' :userInfo.value.userType == 4 ? '其他' : '学员'
  }
  const userStore = useUserStore()
  userStore.roles = []
  systems.value.forEach(system => {
  if(userStore.roles.includes('admin')){
    state.noticeParams.companyId = null
    state.platformParams.companyId = null
    state.isAdmin = true
  }else{
    state.noticeParams.companyId = userStore.companyId
    state.platformParams.companyId = userStore.companyId
    state.isAdmin = false
  }
  await getNoticeList()
  await getPlatformList()
  await getFlowList()
  await getMemo()
  state.platformList.forEach(system => {
    cardStates.value[system.id] = {
      mouseX: 0,
      mouseY: 0,
@@ -98,8 +280,306 @@
  })
})
const toNoticeMng = ()=>{
  router.push({ path: "/work/noticeMng" });
}
// 鼠标进入卡片
function getNoticeList() {
  listNotice(state.noticeParams).then(res => {
    state.noticeList = res.data.list
  })
}
const getCompanyList = async ()=>{
  const queryParams = {
    pageNum: 1,
    pageSize: 999
  }
  const res = await getCompany(queryParams)
  if (res.code == 200) {
    state.companyList = res.data.list?res.data.list:[]
  } else {
    ElMessage.warning(res.message)
  }
}
const getPlatformList = async () => {
  const res = await getInfoPlatforms(state.platformParams)
  if(res.code == 200){
    const originPlatform = {
      id: 0,
      platformName: '国军标9001C质量管理体系',
      platformPic: menu1
    }
    state.platformList = [originPlatform, ...(Array.isArray(res.data) ? res.data : [])]
  }else{
    ElMessage.warning(res.message)
  }
}
const openAdd = async (type, value) => {
  await getCompanyList()
  dialogRef.value.openDialog(type, value, state.platformParams.companyId, state.isAdmin, state.companyList );
}
const getFlowList = async () => {
  const res = await getIndexTitle({pageNum: 1,pageSize: 99})
  if(res.code == 200){
    state.flowList = Array.isArray(res.data) ? res.data : []
  }else{
    ElMessage.warning(res.message)
  }
}
const getMemo = async () => {
  const res = await getMemoList()
  if(res.code == 200){
    state.memo = res.data ? res.data : {}
    console.log(state.memo,'memo')
  }else{
    ElMessage.warning(res.message)
  }
}
// 分页相关
const currentPage = ref(0)
const pageSize = 9 // 九宫格,每页9个
// 计算总页数
const totalPages = computed(() => {
  return Math.ceil(state.platformList.length / pageSize)
})
// 获取当前页显示的系统列表
const displayedSystems = computed(() => {
  const start = currentPage.value * pageSize
  const end = start + pageSize
  return state.platformList.slice(start, end)
})
// 是否显示敬请期待的卡片(只在最后一页且系统总数不是9的倍数时显示)
const showEmptyCard = computed(() => {
  // 如果是最后一页,并且系统总数不是9的倍数,且当前页显示的系统数量小于9
  const isLastPage = currentPage.value === totalPages.value - 1
  const totalCount = state.platformList.length
  const currentCount = displayedSystems.value.length
  return isLastPage && (totalCount % pageSize !== 0) && currentCount < 9
})
// 获取实际在原始数组中的索引
const getActualIndex = (displayIndex) => {
  return currentPage.value * pageSize + displayIndex
}
// 分页方法
const prevPage = () => {
  if (currentPage.value > 0) {
    currentPage.value--
  }
}
const nextPage = () => {
  if (currentPage.value < totalPages.value - 1) {
    currentPage.value++
  }
}
// 重置分页(当系统列表变化时)
watch(state.platformList, () => {
  currentPage.value = 0
})
const openNoticeFile = async(path)=>{
  const ext = path.split('.').pop().toLowerCase();
  if (ext === 'doc' || ext === 'xls' || ext === 'xlsx') {
    ElMessageBox.confirm('暂不支持线上预览文件,是否下载查看?', '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
    }).catch(() => {
      console.log('取消预览')
    });
    return
  }
  if(ext === 'pdf'){
    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank')
    return
  }
  try {
    // 1. 获取文件
    const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
    const arrayBuffer = await response.arrayBuffer();
    // 2. 创建新窗口
    const win = window.open('', '_blank')
    win.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>预览</title>
          <style>
            body { margin: 20px; font-family: Arial; }
            .docx-container { width: 100%; height: 100%; }
          </style>
        </head>
        <body>
          <div id="container" class="docx-container"></div>
        </body>
      </html>
    `);
    // 3. 渲染 DOCX
    await renderAsync(arrayBuffer, win.document.getElementById('container'));
  } catch (error) {
    console.error('预览失败:', error);
    alert(`预览失败: ${error.message}`);
  }
}
// 十二生肖数组
const zodiacAnimals = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']
// 获取生肖年份
const getZodiacYear = (lunarYear) => {
  // 农历年份计算生肖:年份减去4后除以12取余数
  // 因为鼠年对应4,牛年对应5,以此类推
  const index = (lunarYear - 4) % 12
  return index >= 0 ? zodiacAnimals[index] : zodiacAnimals[index + 12]
}
// 计算当前日期信息
const currentWeekDay = computed(() => {
  const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
  return days[currentDate.value.getDay()]
})
const currentSolarDate = computed(() => {
  const date = currentDate.value
  return `${date.getMonth() + 1}月${date.getDate()}日`
})
const currentLunarDate = computed(() => {
  try {
    const date = currentDate.value
    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()
    const lunar = lunarCalendar.solarToLunar(year, month, day)
    if (lunar && lunar.lunarMonthName && lunar.lunarDayName) {
      return `${lunar.lunarMonthName}${lunar.lunarDayName}`
    }
    return ''
  } catch (error) {
    console.error('获取农历日期错误:', error)
    return ''
  }
})
const currentLunarYear = computed(() => {
  try {
    const date = currentDate.value
    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()
    const lunar = lunarCalendar.solarToLunar(year, month, day)
    if (lunar && lunar.GanZhiYear) {
      const zodiac = getZodiacYear(year)
      return `${lunar.GanZhiYear}年【${zodiac}年】`
    }
    return ''
  } catch (error) {
    console.error('获取农历年份错误:', error)
    return ''
  }
})
// 月份导航方法
const prevMonth = () => {
  const date = new Date(currentDate.value)
  date.setMonth(date.getMonth() - 1)
  currentDate.value = date
}
const nextMonth = () => {
  const date = new Date(currentDate.value)
  date.setMonth(date.getMonth() + 1)
  currentDate.value = date
}
const goToday = () => {
  currentDate.value = new Date()
}
// 添加备忘录
const addMemo = async () => {
  if (state.memo.content) {
    const data = {
      id: state.memo.id || null,
      companyId: state.noticeParams.companyId,
      content: state.memo.content,
      createById: userStore.id
    }
    const res = await updateMemo(data)
    if(res.code == 200){
      ElMessage.success('保存成功')
      await getMemo()
    }else{
      ElMessage.warning(res.message)
    }
    newMemo.value = ''
  }
}
// 获取公历日期(去掉前导零)
const getSolarDate = (dateString) => {
  const date = new Date(dateString)
  return date.getDate()
}
// 获取农历日期
const getLunarDate = (dateString) => {
  try {
    const date = new Date(dateString)
    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()
    // 使用农历库获取农历信息
    const lunar = lunarCalendar.solarToLunar(year, month, day)
    if (lunar && lunar.lunarDayName) {
      // 如果是初一,显示月份,否则显示日期
      if (lunar.lunarDay === 1) {
        return lunar.lunarMonthName + '月'
      } else {
        // 简化显示,只显示数字日期
        const lunarDay = lunar.lunarDayName.replace('初', '').replace('十', '')
        return lunarDay
      }
    }
    return ''
  } catch (error) {
    console.error('获取农历日期错误:', error)
    return ''
  }
}
// 检查日期是否有事件
const hasEvent = (date) => {
  const eventDates = []
  return eventDates.includes(date)
}
// 检查是否是今天
const isToday = (dateString) => {
  const today = new Date().toISOString().split('T')[0]
  return dateString === today
}
// 检查是否是当前月份
const isCurrentMonth = (dateString) => {
  const date = new Date(dateString)
  const current = new Date(currentDate.value)
  return date.getMonth() === current.getMonth() && date.getFullYear() === current.getFullYear()
}
const handleCardEnter = (event, id) => {
  const card = event.currentTarget
  cardStates.value[id] = {
@@ -110,7 +590,6 @@
  }
}
// 鼠标移动
const handleCardMove = (event, id) => {
  if (!cardStates.value[id]?.hover) return
@@ -121,21 +600,18 @@
  cardStates.value[id].mouseY = event.clientY - rect.top - cardStates.value[id].height / 2
}
// 鼠标离开
const handleCardLeave = (id) => {
  cardStates.value[id].hover = false;
  // 立即开始归位动画,不使用setTimeout延迟
  cardStates.value[id].mouseX = 0;
  cardStates.value[id].mouseY = 0;
}
// 获取卡片样式
const getCardStyle = (id) => {
  const state = cardStates.value[id] || {}
  const mousePX = state.mouseX / (state.width || 1)
  const mousePY = state.mouseY / (state.height || 1)
  const rX = mousePX * 20 // 减小旋转角度,使效果更柔和
  const rX = mousePX * 20
  const rY = mousePY * -20
  const tX = mousePX * -20
@@ -147,46 +623,6 @@
  }
}
// 系统列表
const systems = ref([
  {
    id: 1,
    name: '国军标9001C质量管理体系',
    description: '确保产品和服务质量符合国际标准',
    icon: menu1
  },
  {
    id: 2,
    name: 'ISO 27001 信息安全体系',
    description: '保护企业信息资产安全与机密性',
    icon: menu2
  },
  {
    id: 3,
    name: 'ISO 45001 安全体系',
    description: '实现企业安全的持续改进',
    icon: menu3
  },
  {
    id: 4,
    name: '项目管理控制',
    description: '标准化项目管理流程与方法',
    icon: menu4
  },
  {
    id: 5,
    name: '承制评价体系',
    description: '供应商与承包商能力评估标准',
    icon: menu5
  },
  {
    id: 6,
    name: '新体系评价',
    description: '新体系评价',
    icon: menu6
  }
])
function handleCommand(command) {
  switch (command) {
    case "info":
@@ -202,21 +638,24 @@
      break;
  }
}
// 进入系统
const enterSystem = (systemId) => {
  if(systemId == 1){
    router.push({ path: "/"});
const enterSystem = (address,index) => {
  if(index == 0){
    router.push({ path: "/learn/standardSysTemp/sysStandardModule"})
  }else{
    ElMessage.warning('系统正在开发中...')
    window.open(address)
    // ElMessage.warning('系统正在开发中...')
  }
}
function getInfo() {
  reviewRef.value.openDialog('view',userInfo.value)
}
function editPsd() {
  reviewRef.value.openDialog('pwd',userInfo.value)
}
// 退出登录
function logout() {
  ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
    confirmButtonText: '确定',
@@ -225,11 +664,8 @@
  }).then(() => {
    removeToken()
    location.href = '/homePage';
  }).catch(() => { });
}
</script>
<style scoped lang="scss">
@@ -286,29 +722,242 @@
      }
    }
  }
}
.systems-container {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
  margin-top: 60px;
  width: 100%;
}
.layout-container {
  display: flex;
  gap: 20px;
  width: 100%;
  height: 100%;
  max-width: none; /* 移除最大宽度限制 */
  margin: 0; /* 移除居中margin */
}
// 左侧列 - 宽度占比2
.left-column {
  flex: 2;
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-width: 0; /* 防止内容溢出 */
}
// 右侧列 - 宽度占比1
.right-column {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-width: 0; /* 防止内容溢出 */
  min-width: 400px; /* 设置最小宽度避免过窄 */
}
// 通用模块卡片样式
.module-card {
  background: #ffffff;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  transition: box-shadow 0.3s ease;
  height: 100%;
  display: flex;
  flex-direction: column;
  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  }
}
.module-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid #f0f0f0;
  flex-shrink: 0; /* 防止header被压缩 */
  h3 {
    margin: 0;
    color: #333;
    font-size: 18px;
    font-weight: 600;
  }
  .more-link {
    color: #409eff;
    font-size: 14px;
    cursor: pointer;
    &:hover {
      color: #337ecc;
    }
  }
}
// 通知公告模块
.notice-module {
  .notice-list {
    flex: 1;
    overflow-y: auto;
    .notice-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 12px 0;
      border-bottom: 1px solid #f5f5f5;
      &:last-child {
        border-bottom: none;
      }
      .notice-title {
        color: #333;
        font-size: 16px;
        cursor: pointer;
        flex: 1;
        margin-right: 12px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        &:hover {
          color: #409eff;
        }
      }
      .notice-date {
        color: #999;
        font-size: 14px;
        flex-shrink: 0;
      }
    }
  }
}
// 流程中心模块
.process-module {
  .process-list {
    flex: 1;
    overflow-y: auto;
    .process-item {
      margin-bottom: 16px;
      .process-info {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 8px;
        .process-name {
          color: #333;
          font-size: 14px;
          flex: 1;
          margin-right: 12px;
        }
        .process-status {
          font-size: 12px;
          padding: 2px 8px;
          border-radius: 4px;
          flex-shrink: 0;
          &.processing {
            background: #e6f7ff;
            color: #1890ff;
          }
          &.pending {
            background: #fff7e6;
            color: #fa8c16;
          }
          &.success {
            background: #f6ffed;
            color: #52c41a;
          }
          &.normal {
            background: #ccc;
            color: #333;
          }
        }
      }
    }
  }
}
// 备忘录模块
.memo-module {
  .memo-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    .memo-input {
      flex: 1;
      .memo-time{
        color: #999;
        font-size: 12px;
        text-align: right;
        margin-top: 10px;
      }
    }
  }
}
// 快捷入口模块
.quick-access-module {
  .module-header {
    .pagination-controls {
      display: flex;
      align-items: center;
      gap: 8px;
      .page-info {
        font-size: 12px;
        color: #666;
        min-width: 40px;
        text-align: center;
      }
      .el-button {
        width: 28px;
        height: 28px;
        display: flex;
        align-items: center;
        justify-content: center;
      }
    }
  }
  .systems-grid-container {
    flex: 1;
    overflow: hidden;
}
.systems-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
  gap: 30px;
  max-width: 1500px;
  margin: 0 auto;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(3, 1fr);
    gap: 16px;
    height: 100%;
    min-height: 400px; // 确保有足够的高度显示九宫格
  perspective: 1000px;
}
.system-card {
  position: relative;
  height: 280px;
  background-color: #ffffff;
  border-radius: 4px;
  transition: transform 1s cubic-bezier(0.23, 1, 0.32, 1),
@@ -316,23 +965,50 @@
  transform-style: preserve-3d;
  cursor: pointer;
  overflow: hidden;
    min-height: 100px; // 确保卡片有最小高度
    // 空白卡片样式
    &.empty-card {
      opacity: 0.6;
      .system-icon {
        color: #ccc;
      }
      h3 {
        color: #999;
      }
  &:hover{
    border-radius: 16px;
        transform: none;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        &::after {
          border-color: transparent;
        }
        &::before {
          background: #fff;
        }
      }
    }
    &:hover{
      border-radius: 8px;
  }
  &::after {
    content: '';
    position: absolute;
    top: 8px;
    left: 8px;
    right: 8px;
    bottom: 8px;
    border-radius: 4px; /* 比卡片小1px */
      top: 4px;
      left: 4px;
      right: 4px;
      bottom: 4px;
      border-radius: 4px;
    border: 1px solid transparent;
    transition: border-color 0.3s ease;
    pointer-events: none; /* 确保不影响鼠标事件 */
    z-index: 3; /* 确保在内容之上 */
      pointer-events: none;
      z-index: 3;
  }
  &::before {
@@ -348,22 +1024,24 @@
    transition: transform 0.5s cubic-bezier(0.23, 1, 0.32, 1);
    z-index: 1;
  }
  &:hover::after {
    border-color: rgba(37,99,235,1); /* 使用蓝色描边 */
    border-radius: 12px;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5); /* 可选:添加内发光效果 */
      border-color: rgba(37,99,235,1);
      border-radius: 6px;
      box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
  }
  &:hover::before {
    border-radius: 16px;
      border-radius: 8px;
    background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(20,20,20,0.05) 100%);
  }
}
.system-card:hover {
  //transform: translateY(-5px);
  box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15),
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15),
  0 0 0 1px rgba(255, 255, 255, 0.5) inset;
}
.card-content {
  position: relative;
  height: 100%;
@@ -371,40 +1049,290 @@
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 25px;
    padding: 12px;
  z-index: 2;
  transform-style: preserve-3d;
}
.system-icon img {
  width: 80px;
  height: 80px;
  margin-bottom: 30px;
  .system-icon {
    margin-bottom: 8px;
    .el-image {
      width: 40px;
      height: 40px;
  transition: transform 0.5s;
}
    .el-icon {
      font-size: 40px;
      color: #ccc;
    }
  }
.system-card:hover .system-icon img {
  transform: scale(1.4);
    transform: scale(1.2);
}
.system-card h3 {
  margin: 0 0 12px;
  color: #333;
  font-size: 24px;
  text-align: center;
  transition: transform 0.3s;
}
.system-card p {
  margin: 0;
  color: #999;
  font-size: 15px;
    color: #333;
    font-size: 14px;
  text-align: center;
  transition: transform 0.3s;
    line-height: 1.2;
    font-weight: 500;
}
.system-card:hover h3,
.system-card:hover p {
  transform: translateZ(20px);
  .system-card:hover h3 {
    transform: translateZ(10px);
  }
}
// 日历模块
.calendar-module {
  .calendar-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-bottom: 16px;
    .lunar-info {
      width: 33.33%;
      display: flex;
      flex-direction: column;
      font-size: 16px;
      .lunar-date-full {
        color: #e6a23c;
        font-weight: 500;
      }
      .lunar-year {
        color: #999;
      }
    }
    .current-date-info {
      width: 33.33%;
      font-size: 16px;
      display: flex;
      flex-direction: column;
      align-items: center;
      .solar-date-large {
        color: #333;
        font-weight: 600;
      }
      .week-day {
        color: #666;
        margin-bottom: 4px;
        font-weight: 500;
      }
    }
    .calendar-actions {
      display: flex;
      justify-content: right;
      width: 33.33%;
      .el-button-group {
        display: flex;
        gap: 1px;
        .el-button {
          padding: 6px 12px;
          border-radius: 4px;
          &:first-child {
            border-top-right-radius: 0;
            border-bottom-right-radius: 0;
          }
          &:last-child {
            border-top-left-radius: 0;
            border-bottom-left-radius: 0;
          }
          &:not(:first-child):not(:last-child) {
            border-radius: 0;
          }
        }
      }
    }
  }
  .calendar-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    :deep(.el-calendar) {
      border: none;
      flex: 1;
      display: flex;
      flex-direction: column;
      .el-calendar__header {
        padding: 0;
        flex-shrink: 0;
      }
      .el-calendar__body {
        flex: 1;
      }
      .el-calendar-table {
        height: 100%;
        .el-calendar-day {
          height: 60px;
          padding: 2px;
          text-align: center;
        }
        td.is-selected {
          background-color: transparent;
        }
        .el-calendar-day:hover {
          background-color: #f5f7fa;
        }
      }
    }
    .calendar-date {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
      height: 100%;
      position: relative;
      padding-top: 4px;
      .solar-date {
        font-size: 14px;
        font-weight: 500;
        color: #333;
        margin-bottom: 2px;
      }
      .lunar-date {
        font-size: 10px;
        color: #999;
        margin-bottom: 4px;
      }
      .calendar-event-dot {
        width: 4px;
        height: 4px;
        background: #409eff;
        border-radius: 50%;
        margin-bottom: 2px;
      }
      .today-indicator {
        position: absolute;
        top: 2px;
        right: 2px;
        width: 16px;
        height: 16px;
        background: #409eff;
        color: white;
        border-radius: 50%;
        font-size: 10px;
        display: flex;
        align-items: center;
        justify-content: center;
        line-height: 1;
      }
    }
    // 今天日期的样式
    .calendar-date.is-today {
      background-color: #ecf5ff;
      .solar-date {
        color: #409eff;
        font-weight: 600;
      }
      &::before {
        content: '';
        position: absolute;
        top: 2px;
        left: 50%;
        transform: translateX(-50%);
        width: 24px;
        height: 24px;
        background: #409eff;
        border-radius: 50%;
        z-index: -1;
        opacity: 0.1;
      }
    }
    .calendar-legend {
      display: flex;
      justify-content: center;
      gap: 16px;
      margin-top: 12px;
      padding-top: 12px;
      border-top: 1px solid #f0f0f0;
      flex-shrink: 0;
      .legend-item {
        display: flex;
        align-items: center;
        gap: 6px;
        .legend-dot {
          width: 8px;
          height: 8px;
          border-radius: 50%;
        }
        .event-dot {
          background: #409eff;
        }
        .today-dot {
          background: #409eff;
        }
        span {
          font-size: 12px;
          color: #666;
        }
      }
    }
  }
}
// 响应式设计
@media (max-width: 1200px) {
  .layout-container {
    flex-direction: column;
  }
  .left-column,
  .right-column {
    flex: 1;
    min-width: auto;
  }
}
// 超大屏幕优化
@media (min-width: 1920px) {
  .systems-container {
    padding: 30px 40px;
  }
  .layout-container {
    gap: 30px;
  }
  .module-card {
    padding: 25px;
  }
  // 在超大屏幕上可以显示3列的系统卡片
  .quick-access-module .systems-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
</style>
src/views/work/noticeMng/components/editDialog.vue
New file
@@ -0,0 +1,271 @@
<template>
  <div class="notice">
    <el-dialog
        v-model="dialogVisible"
        :title="state.title"
        width="50%"
        :before-close="handleClose"
        :close-on-press-escape="false"
        :close-on-click-modal="false"
    >
      <el-form :model="state.form" size="default" ref="superRef" :rules="state.formRules" label-width="150px" >
        <el-form-item v-if="state.isAdmin" label="企业:" prop="companyId">
          <el-select v-model="state.form.companyId" placeholder="请选择" clearable @change="getDeptList" style="width: 100%">
            <el-option
                v-for="item in state.companyList"
                :key="item.id"
                :label="item.name"
                :value="item.id">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="通知内容:" prop="content">
          <el-input v-model.trim="state.form.content" :readonly="state.title =='查看'" placeholder="通知内容"></el-input>
        </el-form-item>
        <el-form-item label="发布部门:" prop="deptId">
          <el-select
              clearable
              v-model="state.form.deptId"
              :disabled="state.title =='查看'"
              filterable
              placeholder="选择部门"
              style="width: 100%"
          >
            <el-option
                v-for="item in state.deptList"
                :key="item.deptId"
                :label="item.deptName"
                :value="item.deptId"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="发布日期:" prop="publishDate">
            <el-date-picker
                :disabled="state.title =='查看'"
                v-model="state.form.publishDate"
                type="date"
                placeholder="请选择日期"
                value-format="YYYY-MM-DD"
            />
        </el-form-item>
        <el-form-item label="文件:" prop="filePath">
          <el-upload accept=".doc,.docx,.pdf" :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.fileLimit' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)" >
            <el-button type="primary">点击上传</el-button>
            <template #tip>
              <div class="el-upload__tip">支持上传.doc、.docx、.pdf格式文档,尺寸小于5M,最多可上传1张</div>
            </template>
          </el-upload>
        </el-form-item>
      </el-form>
      <template #footer v-if="state.title !='查看'">
        <span class="dialog-footer">
            <el-button @click="handleClose" size="default">取 消</el-button>
            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {reactive, ref, toRefs, defineEmits, nextTick, onMounted} from 'vue'
import {ElMessage} from "element-plus";
import {getToken} from "@/utils/auth";
import {delPic} from "@/api/onlineEducation/banner";
import {getDepart} from "@/api/orgStructure/depart";
import {addNotice, updateNotice} from "@/api/system/notice";
const emit = defineEmits(["getList"]);
const dialogVisible = ref(false)
const superRef = ref()
const checkFiles = (rule, value, callback) => {
  if (state.fileList.length == 0) {
    callback(new Error('请上传文件'))
  } else {
    callback()
  }
}
const state = reactive({
  title: '',
  form: {
    id: null,
    companyId: null,
    content: '',
    deptId: null,
    publishDate: '',
    filePath: '',
    fileName: ''
  },
  formRules:{
    companyId: [{ required: true, message: '请选择企业', trigger: 'blur' }],
    content: [{ required: true, message: '请输入内容', trigger: 'blur' }],
    deptId: [{ required: true, message: '请选择部门', trigger: 'blur' }],
    publishDate: [{ required: true, message: '请选择发布日期', trigger: 'blur' }],
    filePath: [{ required: true, validator: checkFiles, trigger: 'blur' }]
  },
  isAdmin: false,
  companyList: [],
  deptList: [],
  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
  header: {
    Authorization: getToken()
  },
  fileLimit: 1,
  fileList: []
})
onMounted(() => {
});
const openDialog = async (type, value,companyId, isAdmin, companyList) => {
  console.log(type, value,companyId, isAdmin, companyList,666)
  state.isAdmin = isAdmin
  if(isAdmin){
    state.companyList = companyList
  }
  await getDepartList(companyId)
  state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : '查看'
  state.form.companyId = companyId
  if(state.title == '编辑'||state.title == '查看'){
    Object.keys(state.form).forEach(key => {
      if (key in value) {
        state.form[key] = value[key]
      }
    })
    if(value.filePath) {
      const obj = {
        url: value.filePath,
        name: value.fileName
      }
      state.fileList = [obj]
    }
  }
  dialogVisible.value = true
}
const getDeptList = async ()=>{
  state.form.deptId = null
  await getDepartList(state.form.companyId)
}
const getDepartList = async (companyId)=> {
  const params = {
    companyId: companyId
  }
  const res = await getDepart(params)
  if(res.code == 200){
    state.deptList = res.data
  }else{
    ElMessage.warning(res.message)
  }
}
const onSubmit = async () => {
  const valid = await superRef.value.validate();
  if(valid){
    if(state.title == '新增'){
      const {id,...data} = state.form
      const res = await addNotice(data)
      if(res.code == 200){
        ElMessage.success(res.message)
        emit('getList')
        handleClose()
        dialogVisible.value = false;
      }else{
        ElMessage.warning(res.message)
      }
    }else{
      const res = await updateNotice(state.form)
      if(res.code == 200){
        ElMessage.success(res.message)
        emit('getList')
        handleClose()
        dialogVisible.value = false;
      }else{
        ElMessage.warning(res.message)
      }
    }
  }
}
const handleAvatarSuccess = (res, uploadFile) => {
  if(res.code == 200){
    state.form.fileName = res.data.originName
    state.form.filePath = res.data.path
  }else{
    state.fileList = []
    ElMessage({
      type: 'warning',
      message: '文件上传失败'
    })
  }
}
const showTip =()=>{
  ElMessage({
    type: 'warning',
    message: '超出文件上传数量'
  });
}
const picSize = async (rawFile) => {
  if(rawFile.size / 1024 / 1024 > 5){
    ElMessage({
      type: 'warning',
      message: '文件大小不能超过5M'
    });
    return false
  }
};
const handleRemove = async (file, uploadFiles) => {
  let path = state.form.filePath;
  await delPic({path: path}).then(res => {
    if(res.code == 200){
      // ElMessage({
      //   type: 'success',
      //   message: '文件已删除'
      // })
      state.form.filePath = ''
      state.form.fileName = ''
    }else{
      ElMessage({
        type: 'warning',
        message: res.message
      })
    }
  }).catch(() => {
    state.form.imgUrl = ''
  });
}
const handleClose = () => {
  state.form = {
    id: null,
    companyId: null,
    content: '',
    deptId: null,
    publishDate: '',
    filePath: '',
    fileName: ''
  }
  state.fileList = []
  superRef.value.clearValidate();
  superRef.value.resetFields()
  dialogVisible.value = false;
}
defineExpose({
  openDialog
});
</script>
<style scoped lang="scss">
.notice{
  :deep(.el-form .el-form-item__label) {
    font-size: 15px;
  }
  .file {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }
}
</style>
src/views/work/noticeMng/index.vue
New file
@@ -0,0 +1,225 @@
<template>
  <div class="app-container">
    <div style="display: flex;justify-content: space-between">
      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
        <el-form-item>
          <el-button
              type="primary"
              plain
              icon="Plus"
              @click="openDialog('add',{})"
              v-hasPermi="['noticeMng:add']"
          >新增</el-button>
        </el-form-item>
        <el-form-item v-if="isAdmin" label="企业:" >
          <el-select v-model="data.queryParams.companyId" placeholder="请选择" clearable>
            <el-option
                v-for="item in companyList"
                :key="item.id"
                :label="item.name"
                :value="item.id">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item >
          <el-button  type="primary" @click="getList">查询</el-button>
          <el-button  type="primary" plain @click="reset">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <!-- 表格数据 -->
    <el-table v-loading="loading" :data="dataList" :border="true">
      <el-table-column label="序号" type="index" align="center" width="80"/>
      <el-table-column label="通知内容" prop="content" align="center"/>
      <el-table-column label="发布部门" prop="deptName" align="center"/>
      <el-table-column label="分布日期" prop="publishDate" align="center"/>
      <el-table-column label="文件" prop="filePath" align="center">
        <template #default="scope">
          <el-link v-if="scope.row.filePath && scope.row.filePath !== ''"  style="" type="primary" @click="openFile(scope.row.filePath)">{{scope.row.fileName}}</el-link>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" >
        <template #default="scope">
          <el-button link type="primary" :disabled="!scope.row.filePath || scope.row.filePath == ''" @click="downloadFile(scope.row)" v-hasPermi="['noticeMng:edit']">下载</el-button>
          <el-button link type="primary" @click="openDialog('edit',scope.row)" v-hasPermi="['noticeMng:edit']">编辑</el-button>
          <el-button link type="danger" @click="handleDelete(scope.row)" v-hasPermi="['noticeMng:del']">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.pageNum"
        v-model:limit="queryParams.pageSize"
        @pagination="getList"
    />
    <edit-dialog ref="dialogRef" @getList=getList></edit-dialog>
  </div>
</template>
<script setup>
import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {delCompany, getCompany} from "@/api/onlineEducation/company";
import editDialog from './components/editDialog.vue'
import useUserStore from "@/store/modules/user";
import axios from "axios";
import {getToken} from "@/utils/auth";
import {renderAsync} from "docx-preview";
import {delNotice, listNotice} from "@/api/system/notice";
const userStore = useUserStore()
const { proxy } = getCurrentInstance();
const loading = ref(false);
const dialogRef = ref();
const data = reactive({
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    companyId: null
  },
  total: 0,
  dataList: [],
  companyList: [],
  isAdmin: false
})
const { queryParams, total, dataList,companyList,isAdmin } = toRefs(data);
const userInfo = ref()
onMounted(async ()=>{
  if(userStore.roles.includes('admin')){
    data.isAdmin = true
    data.queryParams.companyId = null
    await getCompanyList()
  }else{
    data.isAdmin = false
    data.queryParams.companyId = userStore.companyId
  }
  await getList()
})
onUnmounted(()=>{
})
const openFile = async(path)=>{
  const ext = path.split('.').pop().toLowerCase();
  if (ext === 'doc' || ext === 'xls' || ext === 'xlsx') {
    ElMessageBox.confirm('暂不支持线上预览文件,是否下载查看?', '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
    }).catch(() => {
      console.log('取消预览')
    });
    return
  }
  if(ext === 'pdf'){
    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank')
    return
  }
  try {
    // 1. 获取文件
    const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
    const arrayBuffer = await response.arrayBuffer();
    // 2. 创建新窗口
    const win = window.open('', '_blank')
    win.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>预览</title>
          <style>
            body { margin: 20px; font-family: Arial; }
            .docx-container { width: 100%; height: 100%; }
          </style>
        </head>
        <body>
          <div id="container" class="docx-container"></div>
        </body>
      </html>
    `);
    // 3. 渲染 DOCX
    await renderAsync(arrayBuffer, win.document.getElementById('container'));
  } catch (error) {
    console.error('预览失败:', error);
    alert(`预览失败: ${error.message}`);
  }
}
function getList() {
  loading.value = true
  listNotice(data.queryParams).then(res => {
    data.dataList = res.data.list
    data.total = res.data.total
    loading.value = false
  })
}
const getCompanyList = async ()=>{
  const queryParams = {
    pageNum: 1,
    pageSize: 999
  }
  const res = await getCompany(queryParams)
  if (res.code == 200) {
    data.companyList = res.data.list?res.data.list:[]
  } else {
    ElMessage.warning(res.message)
  }
}
const downloadFile = (e)=>{
  axios.get(import.meta.env.VITE_APP_BASE_API + '/' +e.filePath,{headers:{'Content-Type': 'application/json','Authorization': `${getToken()}`},responseType: 'blob'}).then(res=>{
    if (res) {
      const link = document.createElement('a')
      let blob = new Blob([res.data],{type: res.data.type})
      link.style.display = "none";
      link.href = URL.createObjectURL(blob); // 创建URL
      link.setAttribute("download", e.fileName);
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    } else {
      ElMessage({
        type: 'warning',
        message: '文件读取失败'
      });
    }
  })
}
const openDialog = (type, value) => {
  dialogRef.value.openDialog(type, value, data.queryParams.companyId, data.isAdmin, data.companyList);
}
/** 重置新增的表单以及其他数据  */
const reset= async()=> {
  data.queryParams = {
    ...data.queryParams,
    pageNum: 1,
    pageSize: 10
  }
  await getCompanyList()
  await getList()
}
const handleDelete = (val) => {
  ElMessageBox.confirm(
      '确定删除此条数据?',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
      .then( async() => {
        const res = await delNotice(val.id)
        if(res.code == 200){
          ElMessage.success('数据删除成功')
          await getList()
        }else{
          ElMessage.warning(res.message)
        }
      })
}
</script>