已添加7个文件
已修改32个文件
已删除1个文件
1883 ■■■■ 文件已修改
src/api/publicApi/commonFile.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system/message.js 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Dialog/FileListDialog.vue 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Dialog/FormDialog.vue 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Dialog/ImportDialog.vue 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Navbar.vue 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/NotificationCenter/index.vue 331 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/index.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFile/index.vue 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/components/BlacklistTab.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/components/HomeTab.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue 140 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/fileList.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index.vue 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/meetingManagement/index.vue 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/summary/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/purchaseApproval/index.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/afterSalesHandling/components/formDia.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/expiryAfterSales/components/formDia.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/feedbackRegistration/components/formDia.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/energyPower/components/formDia.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/waterManagement/components/formDia.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/waterManagement/components/waterBillForm.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/index.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/issueManagement/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/index.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/index.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentEntry/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/deliveryLedger/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceLedger/index.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/fileList.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/publicApi/commonFile.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
// å…¬å…±æ–‡ä»¶ç®¡ç†æŽ¥å£
import request from '@/utils/request'
// åˆ é™¤å…¬å…±æ–‡ä»¶
export function delCommonFile(ids) {
  return request({
    url: '/commonFile/delCommonFile',
    method: 'delete',
    data: ids
  })
}
// å¼€ç¥¨å°è´¦æ–‡ä»¶åˆ é™¤
export function delCommonFileInvoiceLedger(ids) {
  return request({
    url: '/invoiceLedger/delFile',
    method: 'delete',
    data: ids
  })
}
src/api/system/message.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
import request from "@/utils/request";
// æŸ¥è¯¢æ¶ˆæ¯é€šçŸ¥åˆ—表
export function listMessage(query) {
  return request({
    url: "/system/message/list",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢æœªè¯»æ¶ˆæ¯æ•°é‡
export function getUnreadCount() {
  return request({
    url: "/system/message/unreadCount",
    method: "get",
  });
}
// æ ‡è®°æ¶ˆæ¯ä¸ºå·²è¯»
export function markAsRead(messageId) {
  return request({
    url: "/system/message/markAsRead",
    method: "post",
    data: { messageId },
  });
}
// ä¸€é”®æ ‡è®°æ‰€æœ‰æ¶ˆæ¯ä¸ºå·²è¯»
export function markAllAsRead() {
  return request({
    url: "/system/message/markAllAsRead",
    method: "post",
  });
}
// ç¡®è®¤æ¶ˆæ¯
export function confirmMessage(messageId) {
  return request({
    url: "/system/message/confirm",
    method: "post",
    data: { messageId },
  });
}
src/components/Dialog/FileListDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,164 @@
<template>
  <el-dialog
    v-model="dialogVisible"
    :title="title"
    :width="width"
    :before-close="handleClose"
  >
    <el-table :data="tableData" border :height="tableHeight">
      <el-table-column
        :label="nameColumnLabel"
        :prop="nameColumnProp"
        :min-width="nameColumnMinWidth"
        show-overflow-tooltip
      />
      <el-table-column
        v-if="showActions"
        fixed="right"
        label="操作"
        :width="actionColumnWidth"
        align="center"
      >
        <template #default="scope">
          <el-button
            v-if="showDownload"
            link
            type="primary"
            size="small"
            @click="handleDownload(scope.row)"
          >
            ä¸‹è½½
          </el-button>
          <el-button
            v-if="showPreview"
            link
            type="primary"
            size="small"
            @click="handlePreview(scope.row)"
          >
            é¢„览
          </el-button>
          <slot name="actions" :row="scope.row"></slot>
        </template>
      </el-table-column>
      <slot name="columns"></slot>
    </el-table>
  </el-dialog>
  <filePreview v-if="showPreview" ref="filePreviewRef" />
</template>
<script setup>
import { ref, computed, getCurrentInstance } from 'vue'
import filePreview from '@/components/filePreview/index.vue'
const props = defineProps({
  modelValue: {
    type: Boolean,
    default: false
  },
  title: {
    type: String,
    default: '附件'
  },
  width: {
    type: String,
    default: '40%'
  },
  tableHeight: {
    type: String,
    default: '40vh'
  },
  nameColumnLabel: {
    type: String,
    default: '附件名称'
  },
  nameColumnProp: {
    type: String,
    default: 'name'
  },
  nameColumnMinWidth: {
    type: [String, Number],
    default: 400
  },
  actionColumnWidth: {
    type: [String, Number],
    default: 100
  },
  showActions: {
    type: Boolean,
    default: true
  },
  showDownload: {
    type: Boolean,
    default: true
  },
  showPreview: {
    type: Boolean,
    default: true
  },
  urlField: {
    type: String,
    default: 'url'
  },
  downloadMethod: {
    type: Function,
    default: null
  },
  previewMethod: {
    type: Function,
    default: null
  }
})
const emit = defineEmits(['update:modelValue', 'close', 'download', 'preview'])
const { proxy } = getCurrentInstance()
const filePreviewRef = ref(null)
const dialogVisible = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val)
})
const tableData = ref([])
const handleClose = () => {
  emit('close')
  dialogVisible.value = false
}
const handleDownload = (row) => {
  if (props.downloadMethod) {
    props.downloadMethod(row)
  } else {
    // é»˜è®¤ä¸‹è½½æ–¹æ³•
    proxy.$download.name(row[props.urlField])
  }
  emit('download', row)
}
const handlePreview = (row) => {
  if (props.previewMethod) {
    props.previewMethod(row)
  } else {
    // é»˜è®¤é¢„览方法
    if (filePreviewRef.value) {
      filePreviewRef.value.open(row[props.urlField])
    }
  }
  emit('preview', row)
}
const open = (list) => {
  dialogVisible.value = true
  tableData.value = list || []
}
defineExpose({
  open
})
</script>
<style scoped>
</style>
src/components/Dialog/FormDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
<template>
  <el-dialog
    v-model="dialogVisible"
    :title="computedTitle"
    :width="width"
    @close="handleClose"
  >
    <slot></slot>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">确认</el-button>
        <el-button @click="handleCancel">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
  modelValue: {
    type: Boolean,
    default: false
  },
  title: {
    type: [String, Function],
    default: ''
  },
  operationType: {
    type: String,
    default: ''
  },
  width: {
    type: String,
    default: '70%'
  }
})
const emit = defineEmits(['update:modelValue', 'close', 'confirm', 'cancel'])
const dialogVisible = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val)
})
const computedTitle = computed(() => {
  if (typeof props.title === 'function') {
    return props.title(props.operationType)
  }
  return props.title
})
const handleClose = () => {
  emit('close')
}
const handleConfirm = () => {
  emit('confirm')
}
const handleCancel = () => {
  emit('cancel')
  dialogVisible.value = false
}
</script>
<style scoped>
.dialog-footer {
  text-align: center;
}
</style>
src/components/Dialog/ImportDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,172 @@
<template>
  <el-dialog
    :title="title"
    v-model="dialogVisible"
    :width="width"
    :append-to-body="appendToBody"
    @close="handleClose"
  >
    <el-upload
      ref="uploadRef"
      :limit="limit"
      :accept="accept"
      :headers="headers"
      :action="action"
      :disabled="disabled"
      :before-upload="beforeUpload"
      :on-progress="onProgress"
      :on-success="onSuccess"
      :on-error="onError"
      :on-change="onChange"
      :auto-upload="autoUpload"
      drag
    >
      <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
      <template #tip>
        <div class="el-upload__tip text-center">
          <span>{{ tipText }}</span>
          <el-link
            v-if="showDownloadTemplate"
            type="primary"
            :underline="false"
            style="font-size: 12px; vertical-align: baseline; margin-left: 5px;"
            @click="handleDownloadTemplate"
            >下载模板</el-link
          >
        </div>
      </template>
    </el-upload>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">ç¡® å®š</el-button>
        <el-button @click="handleCancel">取 æ¶ˆ</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { computed, ref } from 'vue'
import { UploadFilled } from '@element-plus/icons-vue'
const props = defineProps({
  modelValue: {
    type: Boolean,
    default: false
  },
  title: {
    type: String,
    default: '导入'
  },
  width: {
    type: String,
    default: '400px'
  },
  appendToBody: {
    type: Boolean,
    default: true
  },
  limit: {
    type: Number,
    default: 1
  },
  accept: {
    type: String,
    default: '.xlsx, .xls'
  },
  headers: {
    type: Object,
    default: () => ({})
  },
  action: {
    type: String,
    required: true
  },
  disabled: {
    type: Boolean,
    default: false
  },
  autoUpload: {
    type: Boolean,
    default: false
  },
  tipText: {
    type: String,
    default: '仅允许导入xls、xlsx格式文件。'
  },
  showDownloadTemplate: {
    type: Boolean,
    default: true
  },
  beforeUpload: {
    type: Function,
    default: null
  },
  onProgress: {
    type: Function,
    default: null
  },
  onSuccess: {
    type: Function,
    default: null
  },
  onError: {
    type: Function,
    default: null
  },
  onChange: {
    type: Function,
    default: null
  }
})
const emit = defineEmits(['update:modelValue', 'close', 'confirm', 'cancel', 'download-template'])
const dialogVisible = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val)
})
const uploadRef = ref(null)
const handleClose = () => {
  emit('close')
}
const handleConfirm = () => {
  emit('confirm')
}
const submit = () => {
  if (uploadRef.value) {
    uploadRef.value.submit()
  }
}
const handleCancel = () => {
  emit('cancel')
  dialogVisible.value = false
}
const handleDownloadTemplate = () => {
  emit('download-template')
}
defineExpose({
  uploadRef,
  submit,
  clearFiles: () => {
    if (uploadRef.value) {
      uploadRef.value.clearFiles()
    }
  }
})
</script>
<style scoped>
.dialog-footer {
  text-align: center;
}
</style>
src/layout/components/Navbar.vue
@@ -24,6 +24,29 @@
      </el-dropdown>
    </div>
    <div class="right-menu">
      <!-- æ¶ˆæ¯é€šçŸ¥ -->
      <el-popover
        v-model:visible="notificationVisible"
        :width="500"
        placement="bottom-end"
        trigger="click"
        :popper-options="{ modifiers: [{ name: 'offset', options: { offset: [0, 10] } }] }"
        popper-class="notification-popover"
      >
        <template #reference>
          <div class="notification-container right-menu-item hover-effect">
            <el-badge :value="unreadCount" :hidden="unreadCount === 0" class="notification-badge">
              <el-icon :size="20" style="cursor: pointer;">
                <Bell />
              </el-icon>
            </el-badge>
          </div>
        </template>
        <NotificationCenter
          @unreadCountChange="handleUnreadCountChange"
          ref="notificationCenterRef"
        />
      </el-popover>
      <div class="avatar-container">
        <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
          <div class="avatar-wrapper">
@@ -51,6 +74,7 @@
<script setup>
import { ElMessageBox } from 'element-plus'
import { Bell } from '@element-plus/icons-vue'
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger'
@@ -59,6 +83,7 @@
import HeaderSearch from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
import NotificationCenter from './NotificationCenter/index.vue'
import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'
@@ -70,6 +95,9 @@
const userStore = useUserStore()
const settingsStore = useSettingsStore()
const factoryList = ref([])
const notificationVisible = ref(false)
const notificationCenterRef = ref(null)
const unreadCount = ref(0)
function toggleSideBar() {
  appStore.toggleSideBar()
}
@@ -147,6 +175,43 @@
}
getUserLoginFacotryList();
// æ¶ˆæ¯é€šçŸ¥ç›¸å…³
function handleUnreadCountChange(count) {
  unreadCount.value = count
}
// ç»„件挂载时加载未读数量和定时刷新
let unreadCountTimer = null
onMounted(() => {
  // å»¶è¿ŸåŠ è½½ï¼Œç¡®ä¿ç»„ä»¶å·²æ¸²æŸ“
  nextTick(() => {
    if (notificationCenterRef.value) {
      notificationCenterRef.value.loadUnreadCount()
    }
  })
  // å®šæ—¶åˆ·æ–°æœªè¯»æ•°é‡ï¼ˆæ¯30秒)
  unreadCountTimer = setInterval(() => {
    if (notificationCenterRef.value) {
      notificationCenterRef.value.loadUnreadCount()
    }
  }, 30000)
})
// ç›‘听 popover æ˜¾ç¤ºçŠ¶æ€ï¼Œæ‰“å¼€æ—¶åŠ è½½æ¶ˆæ¯åˆ—è¡¨
watch(notificationVisible, (val) => {
  if (val && notificationCenterRef.value) {
    nextTick(() => {
      notificationCenterRef.value.loadMessages()
    })
  }
})
onUnmounted(() => {
  if (unreadCountTimer) {
    clearInterval(unreadCountTimer)
  }
})
</script>
<style lang='scss' scoped>
@@ -241,6 +306,19 @@
      }
    }
    .notification-container {
      margin-right: 20px;
      display: flex;
      align-items: center;
      cursor: pointer;
      .notification-badge {
        :deep(.el-badge__content) {
          border: none;
        }
      }
    }
    .avatar-container {
      margin-right: 40px;
@@ -267,3 +345,17 @@
  }
}
</style>
<style lang="scss">
.notification-popover {
  padding: 0 !important;
  .el-popover__title {
    display: none;
  }
  .el-popover__body {
    padding: 0 !important;
  }
}
</style>
src/layout/components/NotificationCenter/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,331 @@
<template>
  <div class="notification-popover-content">
    <div class="popover-header">
      <span class="popover-title">消息通知</span>
      <el-button type="primary" size="small" @click="handleMarkAllAsRead" :disabled="unreadCount === 0">
        ä¸€é”®å·²è¯»
      </el-button>
    </div>
    <div class="notification-content">
      <el-tabs v-model="activeTab" @tab-change="handleTabChange">
        <el-tab-pane :label="`未读(${unreadCount})`" name="unread">
          <div v-if="unreadList.length === 0" class="empty-state">
            <el-empty description="暂无未读消息" />
          </div>
          <div v-else class="notification-list">
            <div
              v-for="item in unreadList"
              :key="item.id"
              class="notification-item"
            >
              <div class="notification-icon">
                <el-icon :size="24" color="#67C23A">
                  <Bell />
                </el-icon>
              </div>
              <div class="notification-content-wrapper">
                <div class="notification-title">{{ item.title }}</div>
                <div class="notification-detail">{{ item.content }}</div>
                <div class="notification-time">{{ item.createTime }}</div>
              </div>
              <div class="notification-action">
                <el-button type="primary" size="small" @click="handleConfirm(item.id)">
                  ç¡®è®¤
                </el-button>
              </div>
            </div>
          </div>
        </el-tab-pane>
        <el-tab-pane label="已读" name="read">
          <div v-if="readList.length === 0" class="empty-state">
            <el-empty description="暂无已读消息" />
          </div>
          <div v-else class="notification-list">
            <div
              v-for="item in readList"
              :key="item.id"
              class="notification-item read"
            >
              <div class="notification-icon">
                <el-icon :size="24" color="#909399">
                  <Bell />
                </el-icon>
              </div>
              <div class="notification-content-wrapper">
                <div class="notification-title">{{ item.title }}</div>
                <div class="notification-detail">{{ item.content }}</div>
                <div class="notification-time">{{ item.createTime }}</div>
              </div>
            </div>
          </div>
        </el-tab-pane>
      </el-tabs>
      <!-- åˆ†é¡µ -->
      <div class="pagination-wrapper" v-if="total > 0">
        <el-pagination
          v-model:current-page="pageNum"
          v-model:page-size="pageSize"
          :page-sizes="[10, 20, 50, 100]"
          :total="total"
          layout="prev, pager, next, sizes"
          @size-change="handleSizeChange"
          @current-change="handlePageChange"
        />
      </div>
    </div>
  </div>
</template>
<script setup>
import { Bell } from '@element-plus/icons-vue'
import { listMessage, markAsRead, markAllAsRead, confirmMessage, getUnreadCount } from '@/api/system/message'
import { ElMessage } from 'element-plus'
const emit = defineEmits(['unreadCountChange'])
const activeTab = ref('unread')
const unreadList = ref([])
const readList = ref([])
const unreadCount = ref(0)
const total = ref(0)
const pageNum = ref(1)
const pageSize = ref(10)
// åŠ è½½æ¶ˆæ¯åˆ—è¡¨
const loadMessages = async () => {
  try {
    const params = {
      pageNum: pageNum.value,
      pageSize: pageSize.value,
      isRead: activeTab.value === 'read' ? 1 : 0
    }
    const res = await listMessage(params)
    if (res.code === 200) {
      if (activeTab.value === 'unread') {
        unreadList.value = res.rows || []
      } else {
        readList.value = res.rows || []
      }
      total.value = res.total || 0
    }
  } catch (error) {
    console.error('加载消息列表失败:', error)
  }
}
// åŠ è½½æœªè¯»æ•°é‡
const loadUnreadCount = async () => {
  try {
    const res = await getUnreadCount()
    if (res.code === 200) {
      unreadCount.value = res.data || 0
      emit('unreadCountChange', unreadCount.value)
    }
  } catch (error) {
    console.error('加载未读数量失败:', error)
  }
}
// æ ‡ç­¾é¡µåˆ‡æ¢
const handleTabChange = (tab) => {
  pageNum.value = 1
  loadMessages()
}
// ç¡®è®¤æ¶ˆæ¯
const handleConfirm = async (messageId) => {
  try {
    const res = await confirmMessage(messageId)
    if (res.code === 200) {
      ElMessage.success('确认成功')
      // æ ‡è®°ä¸ºå·²è¯»
      await markAsRead(messageId)
      // é‡æ–°åŠ è½½æ•°æ®
      loadMessages()
      loadUnreadCount()
    }
  } catch (error) {
    console.error('确认消息失败:', error)
    ElMessage.error('确认失败')
  }
}
// ä¸€é”®å·²è¯»
const handleMarkAllAsRead = async () => {
  try {
    const res = await markAllAsRead()
    if (res.code === 200) {
      ElMessage.success('已全部标记为已读')
      loadMessages()
      loadUnreadCount()
    }
  } catch (error) {
    console.error('一键已读失败:', error)
    ElMessage.error('操作失败')
  }
}
// åˆ†é¡µå¤§å°æ”¹å˜
const handleSizeChange = (size) => {
  pageSize.value = size
  pageNum.value = 1
  loadMessages()
}
// é¡µç æ”¹å˜
const handlePageChange = (page) => {
  pageNum.value = page
  loadMessages()
}
// ç»„件挂载时加载未读数量
onMounted(() => {
  loadUnreadCount()
})
// ç›‘听父组件传递的 visible çŠ¶æ€ï¼ˆé€šè¿‡ watch åœ¨ Navbar ä¸­å¤„理)
// è¿™é‡Œåªè´Ÿè´£æ•°æ®åŠ è½½ï¼Œä¸æŽ§åˆ¶æ˜¾ç¤º
// æš´éœ²æ–¹æ³•供外部调用
defineExpose({
  loadUnreadCount,
  loadMessages
})
</script>
<style lang="scss" scoped>
.notification-popover-content {
  display: flex;
  flex-direction: column;
  width: 500px;
  padding: 16px;
}
.popover-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid #f0f0f0;
  .popover-title {
    font-size: 18px;
    font-weight: 500;
    color: #303133;
  }
}
.notification-content {
  max-height: 60vh;
  display: flex;
  flex-direction: column;
  :deep(.el-tabs) {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    .el-tabs__header {
      margin-bottom: 0;
      flex-shrink: 0;
      padding: 0;
    }
    .el-tabs__content {
      flex: 1;
      overflow-y: auto;
      min-height: 0;
      padding-top: 16px;
    }
    .el-tab-pane {
      height: 100%;
    }
  }
}
.empty-state {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 300px;
  padding: 40px 0;
}
.notification-list {
  .notification-item {
    display: flex;
    padding: 12px 0;
    border-bottom: 1px solid #f0f0f0;
    transition: background-color 0.3s;
    &:hover {
      background-color: #f5f7fa;
    }
    &.read {
      opacity: 0.7;
    }
    .notification-icon {
      flex-shrink: 0;
      width: 40px;
      height: 40px;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: #f0f9ff;
      border-radius: 50%;
      margin-right: 12px;
    }
    .notification-content-wrapper {
      flex: 1;
      min-width: 0;
      .notification-title {
        font-size: 14px;
        font-weight: 500;
        color: #303133;
        margin-bottom: 8px;
      }
      .notification-detail {
        font-size: 13px;
        color: #606266;
        line-height: 1.5;
        margin-bottom: 8px;
        word-break: break-all;
      }
      .notification-time {
        font-size: 12px;
        color: #909399;
      }
    }
    .notification-action {
      flex-shrink: 0;
      margin-left: 12px;
      display: flex;
      align-items: center;
    }
  }
}
.pagination-wrapper {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid #f0f0f0;
  display: flex;
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
}
</style>
src/layout/components/index.js
@@ -2,3 +2,4 @@
export { default as Navbar } from './Navbar'
export { default as Settings } from './Settings'
export { default as TagsView } from './TagsView/index.vue'
export { default as NotificationCenter } from './NotificationCenter/index.vue'
src/views/basicData/customerFile/index.vue
@@ -47,11 +47,14 @@
        @pagination="pagination"
      ></PIMTable>
    </div>
    <el-dialog
    <FormDialog
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增客户信息' : '编辑客户信息'"
      :title="(type) => type === 'add' ? '新增客户信息' : '编辑客户信息'"
      :operation-type="operationType"
      width="70%"
      @close="closeDia"
      @confirm="submitForm"
      @cancel="closeDia"
    >
      <el-form
        :model="form"
@@ -193,63 +196,31 @@
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    </FormDialog>
    <!-- ç”¨æˆ·å¯¼å…¥å¯¹è¯æ¡† -->
    <el-dialog
      :title="upload.title"
    <ImportDialog
      ref="importDialogRef"
      v-model="upload.open"
      :title="upload.title"
      width="400px"
      append-to-body
    >
      <el-upload
        ref="uploadRef"
        :limit="1"
        accept=".xlsx, .xls"
        :headers="upload.headers"
        :action="upload.url + '?updateSupport=' + upload.updateSupport"
        :disabled="upload.isUploading"
        :before-upload="upload.beforeUpload"
        :on-progress="upload.onProgress"
        :on-success="upload.onSuccess"
        :on-error="upload.onError"
        :on-change="upload.onChange"
        :auto-upload="false"
        drag
      >
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <template #tip>
          <div class="el-upload__tip text-center">
            <span>仅允许导入xls、xlsx格式文件。</span>
            <el-link
              type="primary"
              :underline="false"
              style="font-size: 12px; vertical-align: baseline"
              @click="importTemplate"
              >下载模板</el-link
            >
          </div>
        </template>
      </el-upload>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitFileForm">ç¡® å®š</el-button>
          <el-button @click="upload.open = false">取 æ¶ˆ</el-button>
        </div>
      </template>
    </el-dialog>
      :action="upload.url"
      :headers="upload.headers"
      :disabled="upload.isUploading"
      :before-upload="upload.beforeUpload"
      :on-progress="upload.onProgress"
      :on-success="upload.onSuccess"
      :on-error="upload.onError"
      :on-change="upload.onChange"
      @confirm="submitFileForm"
      @cancel="handleImportCancel"
      @download-template="importTemplate"
    />
  </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import { Search } from "@element-plus/icons-vue";
import { Search, Close } from "@element-plus/icons-vue";
import {
  addCustomer,
  delCustomer,
@@ -261,6 +232,9 @@
import { userListNoPage } from "@/api/system/user.js";
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth.js";
import { getCurrentDate } from "@/utils/index.js";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import ImportDialog from "@/components/Dialog/ImportDialog.vue";
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
@@ -338,6 +312,7 @@
const selectedRows = ref([]);
const userList = ref([]);
const tableLoading = ref(false);
const importDialogRef = ref(null);
const page = reactive({
  current: 1,
  size: 100,
@@ -426,7 +401,9 @@
    if(response.code === 200){
      proxy.$modal.msgSuccess("文件上传成功");
      upload.open = false;
      proxy.$refs["uploadRef"].clearFiles();
      if (importDialogRef.value) {
        importDialogRef.value.clearFiles();
      }
      getList();
    }else if(response.code === 500){
      proxy.$modal.msgError(response.msg);
@@ -484,7 +461,13 @@
/** æäº¤ä¸Šä¼ æ–‡ä»¶ */
function submitFileForm() {
  upload.isUploading = true;
  proxy.$refs["uploadRef"].submit();
  if (importDialogRef.value) {
    importDialogRef.value.submit();
  }
}
/** å–消导入 */
function handleImportCancel() {
  upload.open = false;
}
/** å¯¼å…¥æŒ‰é’®æ“ä½œ */
function handleImport() {
@@ -613,15 +596,6 @@
      proxy.$modal.msg("已取消");
    });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
onMounted(() => {
    getList();
src/views/basicData/supplierManage/components/BlacklistTab.vue
@@ -240,6 +240,7 @@
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth.js";
import FilesDia from "../filesDia.vue";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
@@ -541,14 +542,6 @@
      });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// æ‰“开附件弹框
const openFilesFormDia = (row) => {
  nextTick(() => {
src/views/basicData/supplierManage/components/HomeTab.vue
@@ -246,6 +246,7 @@
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth.js";
import FilesDia from "../filesDia.vue";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
@@ -547,14 +548,6 @@
      });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// æ‰“开附件弹框
const openFilesFormDia = (row) => {
  nextTick(() => {
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -121,33 +121,16 @@
      <template #footer v-if="operationType === 'approval'">
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm(2)">不通过</el-button>
          <el-button type="primary" @click="openSignatureDialog(1)">通过</el-button>
          <el-button type="primary" @click="submitForm(1)">通过</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- ç”µå­ç­¾åå¼¹çª—(vue3-signature-pad) -->
    <el-dialog v-model="signatureDialogVisible" title="电子签名" width="600px" append-to-body>
            <vueEsign
                ref="esign"
                class="mySign"
                :width="800"
                :height="300"
                :isCrop="isCrop"
                :lineWidth="lineWidth"
                :lineColor="lineColor"
            />
      <div style="margin-top:10px;">
        <el-button @click="clearSignature">清除</el-button>
        <el-button type="primary" @click="confirmSignature">确定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script setup>
import { getCurrentInstance, reactive, ref, toRefs } from "vue";
import vueEsign from "vue-esign";
import {
    approveProcessDetails,
    getDept,
@@ -156,7 +139,6 @@
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
import { getToken } from "@/utils/auth";
const emit = defineEmits(['close'])
const { proxy } = getCurrentInstance()
@@ -178,21 +160,6 @@
    },
});
const { form } = toRefs(data);
const signatureDialogVisible = ref(false);
const signatureImg = ref('');
let submitStatus = null; // ä¸´æ—¶å­˜å‚¨é€šè¿‡/不通过状态
const isCrop = ref("");
const esign = ref(null);
const lineWidth = ref(0);
const lineColor = ref("#000000");
// ä¸Šä¼ é…ç½®
const upload = reactive({
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
// èŠ‚ç‚¹æ ‡é¢˜
const getNodeTitle = (index, len) => {
@@ -248,77 +215,10 @@
        productOptions.value = res.data;
    });
};
// æ‰“开签名弹窗
const openSignatureDialog = (status) => {
  submitStatus = status;
  signatureDialogVisible.value = true;
};
// æ¸…除签名
const clearSignature = () => {
    esign.value.reset();
};
// ç¡®è®¤ç­¾å
const confirmSignature = () => {
    esign.value.generate().then((res) => {
        console.log(res);
        // å°†base64转换为二进制
        const base64Data = res.split(',')[1]; // ç§»é™¤data:image/png;base64,前缀
        const binaryString = atob(base64Data);
        const bytes = new Uint8Array(binaryString.length);
        for (let i = 0; i < binaryString.length; i++) {
            bytes[i] = binaryString.charCodeAt(i);
        }
        signatureImg.value = bytes;
        // åˆ›å»ºæ–‡ä»¶å¯¹è±¡ç”¨äºŽä¸Šä¼ 
        const blob = new Blob([bytes], { type: 'image/png' });
        const file = new File([blob], 'signature.png', { type: 'image/png' });
        // åˆ›å»ºFormData
        const formData = new FormData();
        formData.append('file', file);
        // ä¸Šä¼ ç­¾åå›¾ç‰‡
        fetch(upload.url, {
            method: 'POST',
            headers: upload.headers,
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            if (data.code === 200) {
                console.log('data---', data)
                let tempFileIds = [];
                tempFileIds.push(data.data.tempId);
                signatureDialogVisible.value = false;
                clearSignature();
                // åªæœ‰é€šè¿‡æ—¶æ‰ä¼ é€’签名文件ID
                if (submitStatus === 1) {
                    submitForm(submitStatus, tempFileIds);
                } else {
                    submitForm(submitStatus);
                }
            } else {
                proxy.$modal.msgError("签名图片上传失败:" + data.msg);
            }
        })
        .catch(error => {
            console.error('上传失败:', error);
            proxy.$modal.msgError("签名图片上传失败");
        });
    }).catch((err) => {
        console.log(err);
        proxy.$modal.msgWarning("请先签名!");
    })
};
// æäº¤å®¡æ‰¹
const submitForm = (status, tempFileIds) => {
const submitForm = (status) => {
  const filteredActivities = activities.value.filter(activity => activity.isShen);
  filteredActivities[0].approveNodeStatus = status;
  // åªæœ‰é€šè¿‡æ—¶æ‰éœ€è¦ç­¾å
  if (status === 1 && tempFileIds) {
    filteredActivities[0].tempFileIds = tempFileIds;
  }
  // åˆ¤æ–­æ˜¯å¦ä¸ºæœ€åŽä¸€æ­¥
  const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1;
  updateApproveNode({ ...filteredActivities[0], isLast }).then(() => {
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -16,19 +16,20 @@
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="申请部门:" prop="approveDeptId">
                            <el-select
                                disabled
                                v-model="form.approveDeptId"
                                placeholder="选择部门"
                            >
                                <el-option
                                    v-for="user in productOptions"
                                    :key="user.deptId"
                                    :label="user.deptName"
                                    :value="user.deptId"
                                />
                            </el-select>
            <el-form-item label="申请部门:" prop="approveDeptName">
              <el-input v-model="form.approveDeptName" placeholder="请输入" clearable/>
<!--                            <el-select-->
<!--                                disabled-->
<!--                                v-model="form.approveDeptId"-->
<!--                                placeholder="选择部门"-->
<!--                            >-->
<!--                                <el-option-->
<!--                                    v-for="user in productOptions"-->
<!--                                    :key="user.deptId"-->
<!--                                    :label="user.deptName"-->
<!--                                    :value="user.deptId"-->
<!--                                />-->
<!--                            </el-select>-->
            </el-form-item>
          </el-col>
        </el-row>
@@ -36,6 +37,63 @@
          <el-col :span="24">
            <el-form-item :label="props.approveType == 5 ? '采购说明:' : '审批事由:'" prop="approveReason">
              <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- è¯·å‡æ—¶é—´ï¼ˆä»…当 approveType ä¸º 2 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row :gutter="30" v-if="props.approveType == 2">
          <el-col :span="12">
            <el-form-item label="请假开始时间:" prop="startDate">
              <el-date-picker
                  v-model="form.startDate"
                  type="date"
                  placeholder="请选择开始日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="请假结束时间:" prop="endDate">
              <el-date-picker
                  v-model="form.endDate"
                  type="date"
                  placeholder="请选择结束日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- æŠ¥é”€é‡‘额(仅当 approveType ä¸º 4 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row v-if="props.approveType == 4">
          <el-col :span="24">
            <el-form-item label="报销金额:" prop="price">
              <el-input-number
                  v-model="form.price"
                  placeholder="请输入报销金额"
                  :min="0"
                  :precision="2"
                  :step="0.01"
                  style="width: 100%"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- å‡ºå·®åœ°ç‚¹ï¼ˆä»…当 approveType ä¸º 3 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row v-if="props.approveType == 3">
          <el-col :span="24">
            <el-form-item label="出差地点:" prop="location">
              <el-input
                  v-model="form.location"
                  placeholder="请输入出差地点"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
@@ -88,6 +146,9 @@
                            <el-select
                                v-model="form.approveUser"
                                placeholder="选择人员"
                filterable
                default-first-option
                :reserve-keyword="false"
                            >
                                <el-option
                                    v-for="user in userList"
@@ -155,6 +216,7 @@
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
import useUserStore from "@/store/modules/user";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore();
const dialogFormVisible = ref(false);
@@ -172,18 +234,27 @@
    approveId: "",
    approveUser: "",
        approveDeptId: "",
    approveDeptName: "",
    approveReason: "",
    checkResult: "",
    tempFileIds: [],
    approverList: [] // æ–°å¢žå­—段,存储所有节点的审批人id
    approverList: [], // æ–°å¢žå­—段,存储所有节点的审批人id
    startDate: "", // è¯·å‡å¼€å§‹æ—¶é—´
    endDate: "", // è¯·å‡ç»“束时间
    price: null, // æŠ¥é”€é‡‘额
    location: "" // å‡ºå·®åœ°ç‚¹
  },
  rules: {
    approveTime: [{ required: false, message: "请输入", trigger: "change" },],
    approveId: [{ required: false, message: "请输入", trigger: "blur" }],
    approveUser: [{ required: false, message: "请输入", trigger: "blur" }],
        approveDeptId: [{ required: true, message: "请输入", trigger: "blur" }],
    approveDeptName: [{ required: true, message: "请输入", trigger: "blur" }],
    approveReason: [{ required: true, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    startDate: [{ required: true, message: "请选择请假开始时间", trigger: "change" }],
    endDate: [{ required: true, message: "请选择请假结束时间", trigger: "change" }],
    price: [{ required: true, message: "请输入报销金额", trigger: "blur" }],
    location: [{ required: true, message: "请输入出差地点", trigger: "blur" }],
  },
});
const { form, rules } = toRefs(data);
@@ -211,7 +282,6 @@
// æ‰“开弹框
const openDialog = (type, row) => {
  console.log('openDialog', type, row)
  operationType.value = type;
  dialogFormVisible.value = true;
    userListNoPageByTenantId().then((res) => {
@@ -278,6 +348,36 @@
    proxy.$modal.msgError("请为所有审批节点选择审批人!")
    return
  }
  // å½“ approveType ä¸º 2 æ—¶ï¼Œæ ¡éªŒè¯·å‡æ—¶é—´
  if (props.approveType == 2) {
    if (!form.value.startDate) {
      proxy.$modal.msgError("请选择请假开始时间!")
      return
    }
    if (!form.value.endDate) {
      proxy.$modal.msgError("请选择请假结束时间!")
      return
    }
    // æ ¡éªŒç»“束时间不能早于开始时间
    if (new Date(form.value.endDate) < new Date(form.value.startDate)) {
      proxy.$modal.msgError("请假结束时间不能早于开始时间!")
      return
    }
  }
  // å½“ approveType ä¸º 3 æ—¶ï¼Œæ ¡éªŒå‡ºå·®åœ°ç‚¹
  if (props.approveType == 3) {
    if (!form.value.location || form.value.location.trim() === '') {
      proxy.$modal.msgError("请输入出差地点!")
      return
    }
  }
  // å½“ approveType ä¸º 4 æ—¶ï¼Œæ ¡éªŒæŠ¥é”€é‡‘额
  if (props.approveType == 4) {
    if (!form.value.price || form.value.price <= 0) {
      proxy.$modal.msgError("请输入有效的报销金额!")
      return
    }
  }
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      if (operationType.value === "add" || currentApproveStatus.value == 3) {
@@ -301,14 +401,6 @@
  dialogFormVisible.value = false;
  emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
src/views/collaborativeApproval/approvalProcess/fileList.vue
@@ -1,11 +1,12 @@
<template>
  <el-dialog v-model="dialogVisible" title="附件" width="40%" :before-close="handleClose">
  <el-dialog v-model="dialogVisible" title="附件" width="40%" :before-close="handleClose" draggable>
    <el-table :data="tableData" border height="40vh">
      <el-table-column label="附件名称" prop="name" min-width="400" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" width="100" align="center">
      <el-table-column fixed="right" label="操作" width="150" align="center">
        <template #default="scope">
          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">下载</el-button>
          <el-button link type="primary" size="small" @click="lookFile(scope.row)">预览</el-button>
          <el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
@@ -16,6 +17,8 @@
<script setup>
import { ref } from 'vue'
import filePreview from '@/components/filePreview/index.vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import { delCommonFile } from '@/api/publicApi/commonFile.js'
const dialogVisible = ref(false)
const tableData = ref([])
@@ -35,6 +38,27 @@
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
// åˆ é™¤é™„ä»¶
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除附件"${row.name}"吗?`, '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    delCommonFile([row.id]).then(() => {
      ElMessage.success('删除成功')
      // ä»Žåˆ—表中移除已删除的附件
      const index = tableData.value.findIndex(item => item.id === row.id)
      if (index !== -1) {
        tableData.value.splice(index, 1)
      }
    }).catch(() => {
      ElMessage.error('删除失败')
    })
  }).catch(() => {
    ElMessage.info('已取消删除')
  })
}
defineExpose({
  open
})
src/views/collaborativeApproval/approvalProcess/index.vue
@@ -1,5 +1,14 @@
<template>
  <div class="app-container">
    <!-- æ ‡ç­¾é¡µåˆ‡æ¢ä¸åŒçš„审批类型 -->
    <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="approval-tabs">
      <el-tab-pane label="公出管理" name="1"></el-tab-pane>
      <el-tab-pane label="请假管理" name="2"></el-tab-pane>
      <el-tab-pane label="出差管理" name="3"></el-tab-pane>
      <el-tab-pane label="报销管理" name="4"></el-tab-pane>
      <el-tab-pane label="采购审批" name="5"></el-tab-pane>
    </el-tabs>
    <div class="search_form">
      <div>
        <span class="search_title">流程编号:</span>
@@ -32,7 +41,7 @@
    <div class="table_list">
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :column="tableColumnCopy"
          :tableData="tableData"
          :page="page"
          :isSelection="true"
@@ -42,7 +51,7 @@
          :total="page.total"
      ></PIMTable>
    </div>
    <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="approveType"></info-form-dia>
    <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="currentApproveType"></info-form-dia>
    <approval-dia ref="approvalDia" @close="handleQuery"></approval-dia>
    <FileList ref="fileListRef" />
  </div>
@@ -51,22 +60,31 @@
<script setup>
import FileList from "./fileList.vue";
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import {onMounted, ref, computed, reactive, toRefs, nextTick, getCurrentInstance} from "vue";
import {ElMessageBox} from "element-plus";
import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue";
import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue";
import {approveProcessDelete, approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess.js";
import useUserStore from "@/store/modules/user";
// å®šä¹‰ç»„件接收的props
const props = defineProps({
  approveType: {
    type: [Number, String],
    default: 0
  }
const userStore = useUserStore();
// å½“前选中的标签页,默认为公出管理
const activeTab = ref('1');
// å½“前审批类型,根据选中的标签页计算
const currentApproveType = computed(() => {
  return Number(activeTab.value);
});
const userStore = useUserStore();
// æ ‡ç­¾é¡µåˆ‡æ¢å¤„理
const handleTabChange = (tabName) => {
  // åˆ‡æ¢æ ‡ç­¾é¡µæ—¶é‡ç½®æœç´¢æ¡ä»¶å’Œåˆ†é¡µï¼Œå¹¶é‡æ–°åŠ è½½æ•°æ®
  searchForm.value.approveId = '';
  searchForm.value.approveStatus = '';
  page.current = 1;
  getList();
};
const data = reactive({
@@ -76,75 +94,100 @@
  },
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
  {
    label: "审批状态",
    prop: "approveStatus",
    dataType: "tag",
        width: 100,
    formatData: (params) => {
      if (params == 0) {
        return "待审核";
      } else if (params == 1) {
        return "审核中";
      } else if (params == 2) {
        return "审核完成";
      } else if (params == 4) {
        return "已重新提交";
      } else {
        return '不通过';
      }
// åŠ¨æ€è¡¨æ ¼åˆ—é…ç½®ï¼Œæ ¹æ®å®¡æ‰¹ç±»åž‹ç”Ÿæˆåˆ—
const tableColumnCopy = computed(() => {
  const isLeaveType = currentApproveType.value === 2; // è¯·å‡ç®¡ç†
  const isReimburseType = currentApproveType.value === 4; // æŠ¥é”€ç®¡ç†
  // åŸºç¡€åˆ—配置
  const baseColumns = [
    {
      label: "审批状态",
      prop: "approveStatus",
      dataType: "tag",
      width: 100,
      formatData: (params) => {
        if (params == 0) {
          return "待审核";
        } else if (params == 1) {
          return "审核中";
        } else if (params == 2) {
          return "审核完成";
        } else if (params == 4) {
          return "已重新提交";
        } else {
          return '不通过';
        }
      },
      formatType: (params) => {
        if (params == 0) {
          return "warning";
        } else if (params == 1) {
          return "primary";
        } else if (params == 2) {
          return "success";
        } else if (params == 4) {
          return "info";
        } else {
          return 'danger';
        }
      },
    },
    formatType: (params) => {
      if (params == 0) {
        return "warning";
      } else if (params == 1) {
        return "primary";
      } else if (params == 2) {
        return "success";
      } else if (params == 4) {
        return "";
      } else {
        return 'danger';
      }
    {
      label: "流程编号",
      prop: "approveId",
      width: 170
    },
  },
  {
    label: "流程编号",
    prop: "approveId",
    width: 170
  },
  {
    label: "申请部门",
    prop: "approveDeptName",
        width: 220
  },
  {
    label: "审批事由",
    prop: "approveReason",
        width: 200
  },
  {
    label: "申请人",
    prop: "approveUserName",
    width: 120
  },
  {
    label: "申请日期",
    prop: "approveTime",
        width: 200
  },
  {
    label: "结束日期",
    prop: "approveOverTime",
    width: 120
  },
  {
    {
      label: "申请部门",
      prop: "approveDeptName",
      width: 220
    },
    {
      label: "审批事由",
      prop: "approveReason",
      width: 200
    },
    {
      label: "申请人",
      prop: "approveUserName",
      width: 120
    }
  ];
  // é‡‘额列(仅报销管理显示)
  if (isReimburseType) {
    baseColumns.push({
      label: "金额(元)",
      prop: "price",
      width: 120
    });
  }
  // æ—¥æœŸåˆ—(根据类型动态配置)
  baseColumns.push(
    {
      label: isLeaveType ? "开始日期" : "申请日期",
      prop: isLeaveType ? "startDate" : "approveTime",
      width: 200
    },
    {
      label: "结束日期",
      prop: isLeaveType ? "endDate" : "approveOverTime",
      width: 120
    }
  );
  // å½“前审批人列
  baseColumns.push({
    label: "当前审批人",
    prop: "approveUserCurrentName",
    width: 120
  },
  {
  });
  // æ“ä½œåˆ—
  baseColumns.push({
    dataType: "action",
    label: "操作",
    align: "center",
@@ -157,7 +200,7 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
                disabled: (row) => row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
        disabled: (row) => row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
      },
      {
        name: "审核",
@@ -165,7 +208,7 @@
        clickFun: (row) => {
          openApprovalDia("approval", row);
        },
                disabled: (row) => row.approveUserCurrentId == null || row.approveStatus == 2 || row.approveStatus == 3 || row.approveStatus == 4 || row.approveUserCurrentId !== userStore.id
        disabled: (row) => row.approveUserCurrentId == null || row.approveStatus == 2 || row.approveStatus == 3 || row.approveStatus == 4 || row.approveUserCurrentId !== userStore.id
      },
      {
        name: "详情",
@@ -182,8 +225,10 @@
        },
      },
    ],
  },
]);
  });
  return baseColumns;
});
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
@@ -214,7 +259,7 @@
};
const getList = () => {
  tableLoading.value = true;
  approveProcessListPage({...page, ...searchForm.value,approveType:props.approveType}).then(res => {
  approveProcessListPage({...page, ...searchForm.value, approveType: currentApproveType.value}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
@@ -224,7 +269,7 @@
};
// å¯¼å‡º
const handleOut = () => {
  const type = Number(props.approveType || 0)
  const type = currentApproveType.value
  const urlMap = {
    0: "/approveProcess/exportZero",
    1: "/approveProcess/exportOne",
@@ -292,4 +337,8 @@
});
</script>
<style scoped></style>
<style scoped>
.approval-tabs {
  margin-bottom: 10px;
}
</style>
src/views/collaborativeApproval/meetingManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
<template>
  <div class="app-container">
    <div class="tabs-wrapper">
      <el-tabs
        v-model="activeTab"
        class="meeting-tabs"
        @tab-change="handleTabChange"
      >
        <el-tab-pane label="会议设置" name="setting" />
        <el-tab-pane label="会议列表" name="index" />
        <el-tab-pane label="会议申请" name="application" />
        <el-tab-pane label="会议审批" name="examine" />
        <el-tab-pane label="会议发布" name="publish" />
        <el-tab-pane label="会议总结" name="summary" />
      </el-tabs>
    </div>
    <div class="tab-content">
      <keep-alive>
        <component :is="currentComponent" />
      </keep-alive>
    </div>
  </div>
</template>
<script setup>
import { ref, computed } from 'vue'
import MeetSetting from '../notificationManagement/meetSetting/index.vue'
import MeetIndex from '../notificationManagement/meetIndex/index.vue'
import MeetApplication from '../notificationManagement/meetApplication/index.vue'
import MeetExamine from '../notificationManagement/meetExamine/index.vue'
import MeetPublish from '../notificationManagement/meetPublish/index.vue'
import MeetSummary from '../notificationManagement/summary/index.vue'
const activeTab = ref('setting')
const tabComponentMap = {
  setting: MeetSetting,
  index: MeetIndex,
  application: MeetApplication,
  examine: MeetExamine,
  publish: MeetPublish,
  summary: MeetSummary
}
const currentComponent = computed(() => tabComponentMap[activeTab.value] || MeetSetting)
function handleTabChange(name) {
  activeTab.value = name
}
</script>
<style scoped lang="scss">
.tabs-wrapper {
  margin-bottom: 10px;
}
.tab-content {
  min-height: 400px;
}
</style>
src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
@@ -1,9 +1,5 @@
<template>
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜ -->
    <div class="page-header">
      <h2>会议申请</h2>
    </div>
  <div>
    <!-- ç”³è¯·ç±»åž‹é€‰æ‹© -->
    <el-card class="type-card">
src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue
@@ -1,5 +1,5 @@
<template>
  <div class="app-container">
  <div>
    <!-- é¡µé¢æ ‡é¢˜ -->
    <div class="page-header">
      <h2>会议草稿</h2>
src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
@@ -1,13 +1,7 @@
<template>
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜ -->
    <div class="page-header">
      <h2>会议审批</h2>
    </div>
  <div>
    <!-- æœç´¢åŒºåŸŸ -->
    <el-card class="search-card">
      <el-form :model="searchForm" inline>
    <el-form :model="searchForm" inline>
        <el-form-item label="会议主题">
          <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable/>
        </el-form-item>
@@ -27,11 +21,10 @@
          <el-button @click="resetSearch">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <!-- ä¼šè®®å®¡æ‰¹åˆ—表 -->
    <el-card>
      <el-table v-loading="loading" :data="approvalList" border>
      <el-table v-loading="loading" :data="approvalList" border :height="tableHeight">
        <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip/>
        <el-table-column prop="applicant" label="申请人" align="center" width="120"/>
        <el-table-column prop="host" label="主理人" align="center" width="120"/>
@@ -203,6 +196,9 @@
// æ€»æ¡æ•°
const total = ref(0)
// è¡¨æ ¼é«˜åº¦ï¼ˆæ ¹æ®çª—口高度自适应)
const tableHeight = ref(window.innerHeight - 380)
const roomEnum = ref([])
const staffList = ref([])
// å®¡æ‰¹åˆ—表数据
src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue
@@ -1,13 +1,6 @@
<template>
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜ -->
    <div class="page-header">
      <h2>会议室使用查询</h2>
    </div>
    <!-- æŸ¥è¯¢æ¡ä»¶ -->
    <el-card class="search-card">
      <el-form :model="queryForm" label-width="80px" inline>
  <div>
    <el-form :model="queryForm" label-width="80px" inline>
        <el-form-item label="查询日期">
          <el-date-picker
              v-model="queryForm.meetingDate"
@@ -23,7 +16,6 @@
          <el-button @click="resetSearch">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <!-- ä¼šè®®å®¤ä½¿ç”¨æƒ…况 -->
    <el-card class="table-container" :loading="loading">
src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
@@ -1,13 +1,7 @@
<template>
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜ -->
    <div class="page-header">
      <h2>会议发布</h2>
    </div>
  <div>
    <!-- æœç´¢åŒºåŸŸ -->
    <el-card class="search-card">
      <el-form :model="searchForm" inline>
    <el-form :model="searchForm" inline>
        <el-form-item label="会议主题">
          <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable/>
        </el-form-item>
@@ -25,11 +19,10 @@
          <el-button @click="resetSearch">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <!-- ä¼šè®®å‘布列表 -->
    <el-card>
      <el-table v-loading="loading" :data="approvalList" border>
      <el-table v-loading="loading" :data="approvalList" border :height="tableHeight">
        <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip/>
        <el-table-column prop="applicant" label="申请人" align="center" width="120"/>
        <el-table-column prop="host" label="主理人" align="center" width="120"/>
@@ -201,6 +194,9 @@
// æ€»æ¡æ•°
const total = ref(0)
// è¡¨æ ¼é«˜åº¦ï¼ˆæ ¹æ®çª—口高度自适应)
const tableHeight = ref(window.innerHeight - 380)
const roomEnum = ref([])
const staffList = ref([])
// å‘布列表数据
src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue
@@ -1,36 +1,29 @@
<template>
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜ -->
    <div class="page-header">
      <h2>会议室设置</h2>
      <div>
        <el-button @click="handleExport" style="margin-right: 10px">导出</el-button>
        <el-button type="primary" @click="handleAdd">
        <el-icon><Plus /></el-icon>
        æ–°å¢žä¼šè®®å®¤
        </el-button>
      </div>
    </div>
  <div>
    <!-- æœç´¢åŒºåŸŸ -->
    <el-card class="search-card">
      <el-form :model="searchForm" label-width="100px" inline>
        <el-form-item label="会议室名称">
          <el-input v-model="searchForm.name" placeholder="请输入会议室名称" clearable />
        </el-form-item>
        <el-form-item label="位置">
          <el-input v-model="searchForm.location" placeholder="请输入位置" clearable />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleSearch">搜索</el-button>
          <el-button @click="resetSearch">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <el-form :model="searchForm" label-width="100px" class="search-form">
      <el-form-item label="会议室名称">
        <el-input v-model="searchForm.name" placeholder="请输入会议室名称" clearable />
      </el-form-item>
      <el-form-item label="位置">
        <el-input v-model="searchForm.location" placeholder="请输入位置" clearable />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleSearch">搜索</el-button>
        <el-button @click="resetSearch">重置</el-button>
      </el-form-item>
      <el-form-item class="search-actions">
        <el-button @click="handleExport">导出</el-button>
        <el-button type="primary" @click="handleAdd">
          <el-icon><Plus /></el-icon>
          æ–°å¢žä¼šè®®å®¤
        </el-button>
      </el-form-item>
    </el-form>
    <!-- ä¼šè®®å®¤åˆ—表 -->
    <el-card>
      <el-table v-loading="loading" :data="meetingRoomList" border>
      <el-table v-loading="loading" :data="meetingRoomList" border :height="tableHeight">
        <el-table-column prop="name" label="会议室名称" align="center" />
        <el-table-column prop="location" label="位置" align="center" />
        <el-table-column prop="capacity" label="容纳人数" align="center" />
@@ -120,6 +113,9 @@
// æ€»æ¡æ•°
const total = ref(0)
// è¡¨æ ¼é«˜åº¦ï¼ˆæ ¹æ®çª—口高度自适应)
const tableHeight = ref(window.innerHeight - 380)
// ä¼šè®®å®¤åˆ—表数据
const meetingRoomList = ref([])
@@ -291,6 +287,15 @@
  padding: 20px;
}
.search-form {
  display: flex;
  /* align-items: center; */
}
.search-actions {
  margin-left: auto;
}
.page-header {
  display: flex;
  justify-content: space-between;
src/views/collaborativeApproval/notificationManagement/summary/index.vue
@@ -1,13 +1,7 @@
<template>
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜ -->
    <div class="page-header">
      <h2>会议纪要</h2>
    </div>
  <div>
    <!-- æœç´¢åŒºåŸŸ -->
    <el-card class="search-card">
      <el-form :model="searchForm" inline>
    <el-form :model="searchForm" inline>
        <el-form-item label="会议主题">
          <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable />
        </el-form-item>
@@ -19,11 +13,10 @@
          <el-button @click="resetSearch">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <!-- ä¼šè®®åˆ—表 -->
    <el-card>
      <el-table v-loading="loading" :data="meetingList" border>
      <el-table v-loading="loading" :data="meetingList" border :height="tableHeight">
        <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip />
        <el-table-column prop="applicant" label="申请人" align="center" width="120" />
        <el-table-column prop="host" label="主持人" align="center" width="120" />
@@ -175,6 +168,9 @@
// æ€»æ¡æ•°
const total = ref(0)
// è¡¨æ ¼é«˜åº¦ï¼ˆæ ¹æ®çª—口高度自适应)
const tableHeight = ref(window.innerHeight - 380)
const roomEnum = ref([])
const staffList = ref([])
src/views/collaborativeApproval/purchaseApproval/index.vue
@@ -244,6 +244,7 @@
import useUserStore from "@/store/modules/user";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore();
@@ -776,14 +777,6 @@
      proxy.$modal.msg("已取消");
    });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
const mathNum = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
src/views/customerService/afterSalesHandling/components/formDia.vue
@@ -131,6 +131,7 @@
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import {afterSalesServiceAdd, afterSalesServiceDispose, afterSalesServiceUpdate} from "@/api/customerService/index.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -194,14 +195,6 @@
  dialogFormVisible.value = false;
  emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
  openDialog,
});
src/views/customerService/expiryAfterSales/components/formDia.vue
@@ -160,6 +160,7 @@
<script setup>
import {ref, computed} from "vue";
import useUserStore from "@/store/modules/user.js";
import { getCurrentDate } from "@/utils/index.js";
// import {userListNoPageByTenantId} from "@/api/system/user.js"; // æš‚时注释掉,使用假数据
// import {expiryAfterSalesAdd, expiryAfterSalesUpdate} from "@/api/customerService/index.js"; // æš‚时注释掉,使用假数据
const { proxy } = getCurrentInstance()
@@ -271,15 +272,6 @@
  dialogFormVisible.value = false;
  emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0");
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
  openDialog,
src/views/customerService/feedbackRegistration/components/formDia.vue
@@ -81,6 +81,7 @@
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import {afterSalesServiceAdd, afterSalesServiceUpdate} from "@/api/customerService/index.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -149,14 +150,6 @@
  dialogFormVisible.value = false;
  emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
  openDialog,
});
src/views/energyManagement/energyPower/components/formDia.vue
@@ -122,6 +122,7 @@
import {ref} from "vue";
import useUserStore from "@/store/modules/user.js";
import {deviceList, equipmentEnergyAdd, equipmentEnergyUpdate, areaListTree} from "@/api/energyManagement/index.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -217,14 +218,6 @@
  dialogFormVisible.value = false;
  emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
  openDialog,
});
src/views/energyManagement/waterManagement/components/formDia.vue
@@ -128,6 +128,7 @@
import {ref, reactive, nextTick} from "vue";
import useUserStore from "@/store/modules/user.js";
import {waterDeviceList, waterEquipmentAdd, waterEquipmentUpdate} from "@/api/energyManagement/waterManagement.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -203,14 +204,6 @@
  dialogFormVisible.value = false;
  emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
  openDialog,
});
src/views/energyManagement/waterManagement/components/waterBillForm.vue
@@ -109,6 +109,7 @@
import {ref, reactive, nextTick, watch} from "vue";
import useUserStore from "@/store/modules/user.js";
import {waterDeviceList, waterBillAdd, waterBillUpdate} from "@/api/energyManagement/waterManagement.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -192,14 +193,6 @@
  dialogFormVisible.value = false;
  emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
  openDialog,
});
src/views/inventoryManagement/dispatchLog/index.vue
@@ -261,6 +261,7 @@
import { ref } from "vue";
import { ElMessageBox } from "element-plus";
import useUserStore from "@/store/modules/user";
import { getCurrentDate } from "@/utils/index.js";
import {
    getStockOutPage,
    delStockOut,
@@ -682,14 +683,6 @@
    const seconds = String(date.getSeconds()).padStart(2, "0");
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
onMounted(() => {
    getList();
});
src/views/inventoryManagement/issueManagement/index.vue
@@ -277,14 +277,6 @@
    proxy.$modal.msg("已取消")
  })
}
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, '0'); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
onMounted(() => {
  getList()
})
src/views/inventoryManagement/receiptManagement/index.vue
@@ -143,6 +143,7 @@
  selectProductRecordListByPuechaserId
} from "@/api/inventoryManagement/stockIn.js";
import { purchaseListPage } from "@/api/procurementManagement/procurementLedger.js";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
@@ -541,14 +542,6 @@
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// ä¿æŒåŽŸæœ‰ getCurrentDate çš„兼容性
function getCurrentDate() {
  return formatDateTime(new Date(), false);
}
  onMounted(() => {
    getList()
src/views/inventoryManagement/stockManagement/index.vue
@@ -156,6 +156,7 @@
import useUserStore from '@/store/modules/user'
import { userListNoPageByTenantId } from "@/api/system/user.js";
import { productTreeList,modelList } from "@/api/basicData/product.js"
import { getCurrentDate } from "@/utils/index.js";
import {
  getStockManagePage,
  delStockManage,
@@ -392,14 +393,6 @@
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, '0'); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
onMounted(() => {
  getList()
src/views/procurementManagement/paymentEntry/index.vue
@@ -284,6 +284,7 @@
    updatePaymentRegistration
} from "@/api/procurementManagement/procurementInvoiceLedger.js";
import useFormData from "@/hooks/useFormData";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance();
const tableColumn = ref([
@@ -574,15 +575,6 @@
      proxy.$modal.msg("已取消");
    });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// å¯¼å‡º
const handleExport = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
src/views/procurementManagement/procurementLedger/index.vue
@@ -889,6 +889,7 @@
import useUserStore from "@/store/modules/user";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
@@ -1672,10 +1673,6 @@
    }
  }
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  return dayjs().format("YYYY-MM-DD");
}
const mathNum = () => {
  if (!productForm.value.taxRate) {
    proxy.$modal.msgWarning("请先选择税率");
src/views/salesManagement/deliveryLedger/index.vue
@@ -95,6 +95,7 @@
import pagination from "@/components/PIMTable/Pagination.vue";
import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
import { ElMessageBox } from "element-plus";
import { getCurrentDate } from "@/utils/index.js";
import {
  deliveryLedgerListPage,
  addOrUpdateDeliveryLedger,
@@ -283,15 +284,6 @@
      proxy.$modal.msg("已取消");
    });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0");
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
onMounted(() => {
  getList();
src/views/salesManagement/invoiceLedger/index.vue
@@ -168,6 +168,7 @@
import useUserStore from "@/store/modules/user.js";
import useFormData from "@/hooks/useFormData";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
@@ -404,14 +405,6 @@
    });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
const changeDateRange = (date) => {
  if (date) {
    searchForm.invoiceDateStart = date[0];
src/views/salesManagement/salesLedger/fileList.vue
ÎļþÒÑɾ³ý
src/views/salesManagement/salesLedger/index.vue
@@ -450,7 +450,7 @@
                </div>
            </template>
        </el-dialog>
    <FileList ref="fileListRef" />
    <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" />
  </div>
</template>
@@ -462,7 +462,7 @@
import {ElMessage, ElMessageBox} from "element-plus";
import useUserStore from "@/store/modules/user";
import { userListNoPage } from "@/api/system/user.js";
import FileList from "./fileList.vue";
import FileListDialog from '@/components/Dialog/FileListDialog.vue';
import {
  ledgerListPage,
  productList,
@@ -477,6 +477,7 @@
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
@@ -1295,15 +1296,6 @@
    const seconds = String(date.getSeconds()).padStart(2, "0");
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// è®¡ç®—产品总数量
const getTotalQuantity = (products) => {
  if (!products || products.length === 0) return '0';
@@ -1516,9 +1508,13 @@
 * @param row ä¸‹è½½æ–‡ä»¶çš„相关信息对象
 */
const fileListRef = ref(null)
const fileListDialogVisible = ref(false)
const downLoadFile = (row) => {
  getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
    fileListRef.value.open(res.salesLedgerFiles)
    if (fileListRef.value) {
      fileListRef.value.open(res.salesLedgerFiles)
      fileListDialogVisible.value = true
    }
  });
}