| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // å
Œ
±æä»¶ç®¡çæ¥å£ |
| | | 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 |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 }, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| | | |
| | |
| | | </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">
|
| | |
| | |
|
| | | <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'
|
| | |
| | | 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'
|
| | |
| | | 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()
|
| | | }
|
| | |
| | | }
|
| | |
|
| | | 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>
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | .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;
|
| | |
|
| | |
| | | }
|
| | | }
|
| | | </style>
|
| | |
|
| | | <style lang="scss">
|
| | | .notification-popover {
|
| | | padding: 0 !important;
|
| | | |
| | | .el-popover__title {
|
| | | display: none;
|
| | | }
|
| | | |
| | | .el-popover__body {
|
| | | padding: 0 !important;
|
| | | }
|
| | | }
|
| | | </style>
|
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| | | |
| | |
| | | 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'
|
| | |
| | | @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" |
| | |
| | | </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, |
| | |
| | | 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(); |
| | | |
| | |
| | | const selectedRows = ref([]); |
| | | const userList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const importDialogRef = ref(null); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | |
| | | 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); |
| | |
| | | /** æäº¤ä¸ä¼ æä»¶ */ |
| | | function submitFileForm() { |
| | | upload.isUploading = true; |
| | | proxy.$refs["uploadRef"].submit(); |
| | | if (importDialogRef.value) { |
| | | importDialogRef.value.submit(); |
| | | } |
| | | } |
| | | /** 忶坼å
¥ */ |
| | | function handleImportCancel() { |
| | | upload.open = false; |
| | | } |
| | | /** 导å
¥æé®æä½ */ |
| | | function handleImport() { |
| | |
| | | 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(); |
| | |
| | | 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(); |
| | | |
| | |
| | | }); |
| | | }; |
| | | |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 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(() => { |
| | |
| | | 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(); |
| | | |
| | |
| | | }); |
| | | }; |
| | | |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 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(() => { |
| | |
| | | <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, |
| | |
| | | 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() |
| | | |
| | |
| | | }, |
| | | }); |
| | | 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) => { |
| | |
| | | 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(() => { |
| | |
| | | </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> |
| | |
| | | <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> |
| | |
| | | <el-select |
| | | v-model="form.approveUser" |
| | | placeholder="éæ©äººå" |
| | | filterable |
| | | default-first-option |
| | | :reserve-keyword="false" |
| | | > |
| | | <el-option |
| | | v-for="user in userList" |
| | |
| | | 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); |
| | |
| | | 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); |
| | |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | console.log('openDialog', type, row) |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | userListNoPageByTenantId().then((res) => { |
| | |
| | | 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) { |
| | |
| | | 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) { |
| | |
| | | <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> |
| | |
| | | <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([]) |
| | |
| | | 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 |
| | | }) |
| | |
| | | <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> |
| | |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :column="tableColumnCopy" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | |
| | | :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> |
| | |
| | | <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({ |
| | |
| | | }, |
| | | }); |
| | | 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", |
| | |
| | | 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: "å®¡æ ¸", |
| | |
| | | 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: "详æ
", |
| | |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | }); |
| | | |
| | | return baseColumns; |
| | | }); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | |
| | | }; |
| | | 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; |
| | |
| | | }; |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | const type = Number(props.approveType || 0) |
| | | const type = currentApproveType.value |
| | | const urlMap = { |
| | | 0: "/approveProcess/exportZero", |
| | | 1: "/approveProcess/exportOne", |
| | |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped></style> |
| | | <style scoped> |
| | | .approval-tabs { |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| | | |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>ä¼è®®ç³è¯·</h2> |
| | | </div> |
| | | <div> |
| | | |
| | | <!-- ç³è¯·ç±»åéæ© --> |
| | | <el-card class="type-card"> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>ä¼è®®è稿</h2> |
| | |
| | | <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> |
| | |
| | | <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"/> |
| | |
| | | |
| | | // æ»æ¡æ° |
| | | const total = ref(0) |
| | | |
| | | // è¡¨æ ¼é«åº¦ï¼æ ¹æ®çªå£é«åº¦èªéåºï¼ |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | // 审æ¹åè¡¨æ°æ® |
| | |
| | | <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" |
| | |
| | | <el-button @click="resetSearch">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®å®¤ä½¿ç¨æ
åµ --> |
| | | <el-card class="table-container" :loading="loading"> |
| | |
| | | <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> |
| | |
| | | <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"/> |
| | |
| | | |
| | | // æ»æ¡æ° |
| | | const total = ref(0) |
| | | |
| | | // è¡¨æ ¼é«åº¦ï¼æ ¹æ®çªå£é«åº¦èªéåºï¼ |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | // åå¸åè¡¨æ°æ® |
| | |
| | | <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" /> |
| | |
| | | |
| | | // æ»æ¡æ° |
| | | const total = ref(0) |
| | | |
| | | // è¡¨æ ¼é«åº¦ï¼æ ¹æ®çªå£é«åº¦èªéåºï¼ |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | |
| | | // ä¼è®®å®¤åè¡¨æ°æ® |
| | | const meetingRoomList = ref([]) |
| | |
| | | padding: 20px; |
| | | } |
| | | |
| | | .search-form { |
| | | display: flex; |
| | | /* align-items: center; */ |
| | | } |
| | | |
| | | .search-actions { |
| | | margin-left: auto; |
| | | } |
| | | |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | |
| | | <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> |
| | |
| | | <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" /> |
| | |
| | | |
| | | // æ»æ¡æ° |
| | | const total = ref(0) |
| | | |
| | | // è¡¨æ ¼é«åº¦ï¼æ ¹æ®çªå£é«åº¦èªéåºï¼ |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | |
| | |
| | | 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(); |
| | | |
| | |
| | | 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("请å
éæ©ç¨ç"); |
| | |
| | | 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); |
| | |
| | | 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, |
| | | }); |
| | |
| | | <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() |
| | |
| | | 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, |
| | |
| | | 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); |
| | |
| | | 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, |
| | | }); |
| | |
| | | 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); |
| | |
| | | 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, |
| | | }); |
| | |
| | | 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); |
| | |
| | | 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, |
| | | }); |
| | |
| | | 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); |
| | |
| | | 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, |
| | | }); |
| | |
| | | import { ref } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | import { |
| | | getStockOutPage, |
| | | delStockOut, |
| | |
| | | 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(); |
| | | }); |
| | |
| | | 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() |
| | | }) |
| | |
| | | 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() |
| | |
| | | |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | } |
| | | |
| | | // ä¿æåæ getCurrentDate çå
¼å®¹æ§ |
| | | function getCurrentDate() { |
| | | return formatDateTime(new Date(), false); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | |
| | | 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, |
| | |
| | | }).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() |
| | |
| | | updatePaymentRegistration |
| | | } from "@/api/procurementManagement/procurementInvoiceLedger.js"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableColumn = ref([ |
| | |
| | | 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("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | |
| | | 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([]); |
| | |
| | | } |
| | | } |
| | | }; |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD |
| | | function getCurrentDate() { |
| | | return dayjs().format("YYYY-MM-DD"); |
| | | } |
| | | const mathNum = () => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | |
| | | 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, |
| | |
| | | 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(); |
| | |
| | | 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([]); |
| | |
| | | }); |
| | | }; |
| | | |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 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]; |
| | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <FileList ref="fileListRef" /> |
| | | <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | 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, |
| | |
| | | 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(); |
| | |
| | | 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'; |
| | |
| | | * @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 |
| | | } |
| | | }); |
| | | } |
| | | |