| src/api/managementMeetings/meeting.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/managementMeetings/meetingSettings.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/cooperativeOffice/collaborativeApproval/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/managementMeetings/meetApplication/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/managementMeetings/meetingList/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/managementMeetings/meetingSettings/detail.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/managementMeetings/meetingSettings/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/managementMeetings/meetingSettings/view.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/managementMeetings/meeting.js
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,26 @@ import request from '@/utils/request'; // è·åä¼è®®ä½¿ç¨å表 export function getMeetingUseList(data){ return request({ url: "/meeting/meetingUseList", method: "post", data: data, }); } // ä¿åä¼è®®ç³è¯· export function saveMeetingApplication(data){ return request({ url: "/meeting/saveMeetingApplication", method: "post", data: data, }); } export function getRoomEnum() { return request({ url: "/meeting/roomEnum", method: "get", }); } src/api/managementMeetings/meetingSettings.js
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,24 @@ // ä¼è®®ç®¡ç import request from "@/utils/request"; export function getMeetingRoomList(data) { return request({ url: "/meeting/roomList", method: "post", data: data, }); } export function saveRoom(data) { return request({ url: "/meeting/saveRoom", method: "post", data: data, }); } export function delRoom(id) { return request({ url: "/meeting/delRoom/"+id, method: "delete", }); } src/pages.json
@@ -296,9 +296,30 @@ } }, { "path": "pages/cooperativeOffice/collaborativeApproval/index", "path": "pages/managementMeetings/meetingSettings/index", "style": { "navigationBarTitleText": "审æ¹ç®¡ç", "navigationBarTitleText": "ä¼è®®è®¾ç½®", "navigationStyle": "custom" } }, { "path": "pages/managementMeetings/meetingSettings/detail", "style": { "navigationBarTitleText": "ä¼è®®å®¤è¯¦æ ", "navigationStyle": "custom" } }, { "path": "pages/managementMeetings/meetingList/index", "style": { "navigationBarTitleText": "ä¼è®®å表", "navigationStyle": "custom" } }, { "path": "pages/managementMeetings/meetApplication/index", "style": { "navigationBarTitleText": "ä¼è®®ç³è¯·", "navigationStyle": "custom" } }, src/pages/cooperativeOffice/collaborativeApproval/index.vue
@@ -1,351 +1,367 @@ // 审æ¹ç®¡çä¸»é¡µé¢ <template> <view class="sales-account"> <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> <PageHeader title="审æ¹ç®¡ç" @back="goBack" /> <!-- æç´¢åçéåºå --> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <up-input class="search-text" placeholder="请è¾å ¥æµç¨ç¼å·" v-model="searchForm.approveId" clearable /> </view> <view class="search-button" @click="getList"> <up-icon name="search" size="24" color="#999"></up-icon> </view> </view> </view> <!-- 审æ¹å表 --> <view class="ledger-list" v-if="ledgerList.length > 0"> <view v-for="(item, index) in ledgerList" :key="index"> <view class="ledger-item"> <view class="item-header"> <view class="item-left"> <view class="document-icon"> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> </view> <text class="item-id">{{ item.approveId }}</text> </view> <view class="item-tag"> <u-tag :type="getTagClass(item.approveStatus)">{{ formatReceiptType(item.approveStatus) }}</u-tag> </view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">ç³è¯·äºº</text> <text class="detail-value">{{ item.approveUserName }}</text> </view> <view class="detail-row"> <text class="detail-label">ç³è¯·é¨é¨</text> <text class="detail-value">{{ item.approveDeptName }}</text> </view> <view class="detail-row-approveReason"> <text class="detail-label">审æ¹äºç±</text> <text class="detail-value highlightBlue">{{ item.approveReason }}</text> </view> <view class="detail-row"> <text class="detail-label">ç³è¯·æ¥æ</text> <text class="detail-value">{{ item.approveTime }}</text> </view> <!-- approveType=2 请åç¸å ³å段 --> <template v-if="item.approveType === 2"> <view class="detail-row"> <text class="detail-label">请åå¼å§æ¶é´</text> <text class="detail-value">{{ item.startDate || '-' }}</text> </view> <view class="detail-row"> <text class="detail-label">请åç»ææ¶é´</text> <text class="detail-value">{{ item.endDate || '-' }}</text> </view> </template> <!-- approveType=3 åºå·®ç¸å ³å段 --> <view v-if="item.approveType === 3" class="detail-row"> <text class="detail-label">åºå·®å°ç¹</text> <text class="detail-value">{{ item.location || '-' }}</text> </view> <!-- approveType=4 æ¥éç¸å ³å段 --> <view v-if="item.approveType === 4" class="detail-row"> <text class="detail-label">æ¥ééé¢</text> <text class="detail-value highlightYellow">{{ item.price ? `Â¥${item.price}` : '-' }}</text> </view> <view class="detail-row"> <text class="detail-label">ç»ææ¥æ</text> <text class="detail-value">{{ item.approveOverTime }}</text> </view> <up-divider></up-divider> <view class="detail-info"> <view class="detail-row-user"> <text class="detail-label">å½å审æ¹äºº</text> <view class="detail-value approver-value"> <view class="approver-chip"> <text class="approver-name">{{ item.approveUserCurrentName || 'æªåé ' }}</text> </view> </view> </view> <view class="detail-row"> <view class="actions"> <u-button type="primary" size="small" class="action-btn edit" :disabled="item.approveStatus == 2 || item.approveStatus == 1 || item.approveStatus == 4" @click="handleItemClick(item)" > ç¼è¾ </u-button> <u-button type="success" size="small" class="action-btn approve" :disabled="item.approveUserCurrentId == null || item.approveStatus == 2 || item.approveStatus == 3 || item.approveStatus == 4 || item.approveUserCurrentId !== userStore.id" @click="approve(item)" > å®¡æ ¸ </u-button> </view> </view> </view> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ å®¡æ¹æ°æ®</text> </view> <!-- æµ®å¨æä½æé® --> <view class="fab-button" @click="handleAdd"> <up-icon name="plus" size="24" color="#ffffff"></up-icon> </view> </view> <view class="sales-account"> <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> <PageHeader :title="pageTitle" @back="goBack" /> <!-- æç´¢åçéåºå --> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <up-input class="search-text" placeholder="请è¾å ¥æµç¨ç¼å·" v-model="searchForm.approveId" clearable /> </view> <view class="search-button" @click="getList"> <up-icon name="search" size="24" color="#999"></up-icon> </view> </view> </view> <!-- 审æ¹å表 --> <view class="ledger-list" v-if="ledgerList.length > 0"> <view v-for="(item, index) in ledgerList" :key="index"> <view class="ledger-item"> <view class="item-header"> <view class="item-left"> <view class="document-icon"> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> </view> <text class="item-id">{{ item.approveId }}</text> </view> <view class="item-tag"> <u-tag :type="getTagClass(item.approveStatus)">{{ formatReceiptType(item.approveStatus) }}</u-tag> </view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">ç³è¯·äºº</text> <text class="detail-value">{{ item.approveUserName }}</text> </view> <view class="detail-row"> <text class="detail-label">ç³è¯·é¨é¨</text> <text class="detail-value">{{ item.approveDeptName }}</text> </view> <view class="detail-row-approveReason"> <text class="detail-label">审æ¹äºç±</text> <text class="detail-value highlightBlue">{{ item.approveReason }}</text> </view> <view class="detail-row"> <text class="detail-label">ç³è¯·æ¥æ</text> <text class="detail-value">{{ item.approveTime }}</text> </view> <!-- approveType=2 请åç¸å ³å段 --> <template v-if="item.approveType === 2"> <view class="detail-row"> <text class="detail-label">请åå¼å§æ¶é´</text> <text class="detail-value">{{ item.startDate || '-' }}</text> </view> <view class="detail-row"> <text class="detail-label">请åç»ææ¶é´</text> <text class="detail-value">{{ item.endDate || '-' }}</text> </view> </template> <!-- approveType=3 åºå·®ç¸å ³å段 --> <view v-if="item.approveType === 3" class="detail-row"> <text class="detail-label">åºå·®å°ç¹</text> <text class="detail-value">{{ item.location || '-' }}</text> </view> <!-- approveType=4 æ¥éç¸å ³å段 --> <view v-if="item.approveType === 4" class="detail-row"> <text class="detail-label">æ¥ééé¢</text> <text class="detail-value highlightYellow">{{ item.price ? `Â¥${item.price}` : '-' }}</text> </view> <view class="detail-row"> <text class="detail-label">ç»ææ¥æ</text> <text class="detail-value">{{ item.approveOverTime }}</text> </view> <up-divider></up-divider> <view class="detail-info"> <view class="detail-row-user"> <text class="detail-label">å½å审æ¹äºº</text> <view class="detail-value approver-value"> <view class="approver-chip"> <text class="approver-name">{{ item.approveUserCurrentName || 'æªåé ' }}</text> </view> </view> </view> <view class="detail-row"> <view class="actions"> <u-button type="primary" size="small" class="action-btn edit" :disabled="item.approveStatus == 2 || item.approveStatus == 1 || item.approveStatus == 4" @click="handleItemClick(item)"> ç¼è¾ </u-button> <u-button type="success" size="small" class="action-btn approve" :disabled="item.approveUserCurrentId == null || item.approveStatus == 2 || item.approveStatus == 3 || item.approveStatus == 4 || item.approveUserCurrentId !== userStore.id" @click="approve(item)"> å®¡æ ¸ </u-button> </view> </view> </view> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ å®¡æ¹æ°æ®</text> </view> <!-- æµ®å¨æä½æé® --> <view class="fab-button" @click="handleAdd"> <up-icon name="plus" size="24" color="#ffffff"></up-icon> </view> </view> </template> <script setup> import { ref, toRefs, reactive } from "vue"; import PageHeader from "@/components/PageHeader.vue"; import {approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess"; import {onShow} from "@dcloudio/uni-app"; import useUserStore from "@/store/modules/user"; // æ¥æ¶ç¶ç»ä»¶ä¼ éç approveType åæ° const props = defineProps({ approveType: { type: Number, default: 0 } }); const userStore = useUserStore() // æ°æ® const ledgerList = ref([]); const data = reactive({ searchForm: { approveId: "", }, }); const { searchForm } = toRefs(data); import { ref, toRefs, reactive } from "vue"; import PageHeader from "@/components/PageHeader.vue"; import { approveProcessListPage } from "@/api/collaborativeApproval/approvalProcess"; import { onShow } from "@dcloudio/uni-app"; import useUserStore from "@/store/modules/user"; // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; // æ¥è¯¢å表 const getList = () => { showLoadingToast('å è½½ä¸...') const page = { current: -1, size: -1, }; approveProcessListPage({ ...page,approveType: props.approveType,...searchForm.value }) .then((res) => { ledgerList.value = res.data.records; closeToast() }) .catch(() => { closeToast() }); }; // æ¾ç¤ºå è½½æç¤º const showLoadingToast = (message) => { uni.showLoading({ title: message, mask: true }); }; // æ¥æ¶ç¶ç»ä»¶ä¼ éç approveType åæ° const props = defineProps({ approveType: { type: Number, default: 0, }, }); // å ³éæç¤º const closeToast = () => { uni.hideLoading(); }; // æ å° approveType å°å¯¹åºç页颿 é¢ const getPageTitle = type => { const titleMap = { 1: "å ¬åºç®¡ç", 2: "请å管ç", 3: "åºå·®ç®¡ç", 4: "æ¥é管ç", 5: "éè´ç®¡ç", 6: "æ¥ä»·ç®¡ç", 7: "åºåºç®¡ç", }; return titleMap[type] || "审æ¹ç®¡ç"; }; // æ¾ç¤ºçéé项 const showFilterOptions = () => { uni.showActionSheet({ itemList: ["ææ¥æçé", "æç¶æçé", "æéé¢çé"], success: (res) => { console.log("éæ©äºçéé项:", res.tapIndex); }, }); }; // æ ¼å¼å忬¾æ¹å¼ const formatReceiptType = (params) => { if (params == 0) { return "å¾ å®¡æ ¸"; } else if (params == 1) { return "å®¡æ ¸ä¸"; } else if (params == 2) { return "å®¡æ ¸å®æ"; } else if (params == 4) { return "已鿰æäº¤"; } else { return 'ä¸éè¿'; } }; // è·åæ ç¾æ ·å¼ç±» const getTagClass = (type) => { if (type == 0) { return "warning"; } else if (type == 1) { return "primary"; } else if (type == 2) { return "success"; } else if (type == 4) { return "primary"; } else { return "error"; } }; const pageTitle = getPageTitle(props.approveType); // ç¹å»å表项 const handleItemClick = (item) => { // ä½¿ç¨æ¬å°åå¨ä¼ éæ°æ® uni.setStorageSync('invoiceLedgerEditRow', JSON.stringify(item)); uni.setStorageSync('operationType', 'edit'); uni.setStorageSync('approveId', item.approveId); uni.setStorageSync('approveType', props.approveType); uni.navigateTo({ url: "/pages/cooperativeOffice/collaborativeApproval/detail", }); }; const userStore = useUserStore(); // æ°æ® const ledgerList = ref([]); const data = reactive({ searchForm: { approveId: "", }, }); const { searchForm } = toRefs(data); // æ·»å æ°è®°å½ const handleAdd = () => { uni.setStorageSync('operationType', 'add'); uni.setStorageSync('approveType', props.approveType); uni.navigateTo({ url: `/pages/cooperativeOffice/collaborativeApproval/detail?approveType=${props.approveType}`, }); }; // ç¹å»å®¡æ ¸ const approve = (item) => { uni.setStorageSync('approveId', item.approveId); uni.setStorageSync('approveType', props.approveType); uni.navigateTo({ url: "/pages/cooperativeOffice/collaborativeApproval/approve?approveType=" + props.approveType }) } // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; // æ¥è¯¢å表 const getList = () => { showLoadingToast("å è½½ä¸..."); const page = { current: -1, size: -1, }; approveProcessListPage({ ...page, approveType: props.approveType, ...searchForm.value, }) .then(res => { ledgerList.value = res.data.records; closeToast(); }) .catch(() => { closeToast(); }); }; // æ¾ç¤ºå è½½æç¤º const showLoadingToast = message => { uni.showLoading({ title: message, mask: true, }); }; onShow(() => { // 页é¢å è½½å®æåçåå§åé»è¾ getList(); }); // å ³éæç¤º const closeToast = () => { uni.hideLoading(); }; // æ¾ç¤ºçéé项 const showFilterOptions = () => { uni.showActionSheet({ itemList: ["ææ¥æçé", "æç¶æçé", "æéé¢çé"], success: res => { console.log("éæ©äºçéé项:", res.tapIndex); }, }); }; // æ ¼å¼å忬¾æ¹å¼ const formatReceiptType = params => { if (params == 0) { return "å¾ å®¡æ ¸"; } else if (params == 1) { return "å®¡æ ¸ä¸"; } else if (params == 2) { return "å®¡æ ¸å®æ"; } else if (params == 4) { return "已鿰æäº¤"; } else { return "ä¸éè¿"; } }; // è·åæ ç¾æ ·å¼ç±» const getTagClass = type => { if (type == 0) { return "warning"; } else if (type == 1) { return "primary"; } else if (type == 2) { return "success"; } else if (type == 4) { return "primary"; } else { return "error"; } }; // ç¹å»å表项 const handleItemClick = item => { // ä½¿ç¨æ¬å°åå¨ä¼ éæ°æ® uni.setStorageSync("invoiceLedgerEditRow", JSON.stringify(item)); uni.setStorageSync("operationType", "edit"); uni.setStorageSync("approveId", item.approveId); uni.setStorageSync("approveType", props.approveType); uni.navigateTo({ url: "/pages/cooperativeOffice/collaborativeApproval/detail", }); }; // æ·»å æ°è®°å½ const handleAdd = () => { uni.setStorageSync("operationType", "add"); uni.setStorageSync("approveType", props.approveType); uni.navigateTo({ url: `/pages/cooperativeOffice/collaborativeApproval/detail?approveType=${props.approveType}`, }); }; // ç¹å»å®¡æ ¸ const approve = item => { uni.setStorageSync("approveId", item.approveId); uni.setStorageSync("approveType", props.approveType); uni.navigateTo({ url: "/pages/cooperativeOffice/collaborativeApproval/approve?approveType=" + props.approveType, }); }; onShow(() => { // 页é¢å è½½å®æåçåå§åé»è¾ getList(); }); </script> <style scoped lang="scss"> @import "../../../styles/sales-common.scss"; @import "../../../styles/sales-common.scss"; .u-divider { margin: 0 !important; } .u-divider { margin: 0 !important; } // ææ¡£å¾æ æ ·å¼ - è¦çå ¬å ±æ ·å¼ä¸çèæ¯è² .document-icon { background: #ed8d05; } // ææ¡£å¾æ æ ·å¼ - è¦çå ¬å ±æ ·å¼ä¸çèæ¯è² .document-icon { background: #ed8d05; } // æµ®å¨æé®æ ·å¼ - è¦çå ¬å ±æ ·å¼ä¸çèæ¯è² .fab-button { background: #ed8d05; } // æµ®å¨æé®æ ·å¼ - è¦çå ¬å ±æ ·å¼ä¸çèæ¯è² .fab-button { background: #ed8d05; } // ç¹ææ ·å¼ .detail-row-user { display: flex; align-items: center; justify-content: space-between; } .detail-row-approveReason { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } // ç¹ææ ·å¼ .detail-row-user { display: flex; align-items: center; justify-content: space-between; } .detail-value.highlightBlue { color: #2979ff; font-weight: 500; } .detail-row-approveReason { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } .detail-value.highlightYellow { color: #ed8d05; font-weight: 500; } .detail-value.highlightBlue { color: #2979ff; font-weight: 500; } .approver-value { display: flex; justify-content: flex-end; } .approver-chip { display: inline-flex; align-items: center; gap: 6px; background: #f0f6ff; color: #2b7cff; border: 1px solid #e0efff; border-radius: 999px; padding: 4px 10px; max-width: 100%; } .approver-name { font-size: 12px; color: #2b7cff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .detail-value.highlightYellow { color: #ed8d05; font-weight: 500; } .actions { display: flex; gap: 10px; align-items: center; justify-content: flex-end; } .approver-value { display: flex; justify-content: flex-end; } .action-btn { border-radius: 16px; height: 28px; line-height: 28px; padding: 0 12px; } .approver-chip { display: inline-flex; align-items: center; gap: 6px; background: #f0f6ff; color: #2b7cff; border: 1px solid #e0efff; border-radius: 999px; padding: 4px 10px; max-width: 100%; } .approver-name { font-size: 12px; color: #2b7cff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .actions { display: flex; gap: 10px; align-items: center; justify-content: flex-end; } .action-btn { border-radius: 16px; height: 28px; line-height: 28px; padding: 0 12px; } </style> src/pages/index.vue
@@ -303,6 +303,18 @@ label: "åºåºç®¡ç", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "ä¼è®®è®¾ç½®", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "ä¼è®®å表", }, { icon: "/static/images/icon/qingjiaguanli@2x.png", label: "ä¼è®®ç³è¯·", }, { icon: "/static/images/icon/xietongshenpi@2x.png", label: "åå审æ¹", }, @@ -495,6 +507,21 @@ url: "/pages/cooperativeOffice/collaborativeApproval/index7", }); break; case "ä¼è®®è®¾ç½®": uni.navigateTo({ url: "/pages/managementMeetings/meetingSettings/index", }); break; case "ä¼è®®å表": uni.navigateTo({ url: "/pages/managementMeetings/meetingList/index", }); break; case "ä¼è®®ç³è¯·": uni.navigateTo({ url: "/pages/managementMeetings/meetApplication/index", }); break; case "åå审æ¹": uni.navigateTo({ url: "/pages/cooperativeOffice/collaborativeApproval/index", src/pages/managementMeetings/meetApplication/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,497 @@ <template> <view style="background-color: #fff;" class="client-visit-detail"> <PageHeader title="ä¼è®®ç³è¯·" @back="goBack" /> <view> <view v-for="item in applicationTypes" :key="item.value" class="application-type-item" :class="{ active: meetingForm.applicationType === item.value }" @click="selectApplicationType(item)"> <view class="application-type-info"> <view class="application-type-name">{{ item.name }}</view> <view class="application-type-desc">{{ item.desc }}</view> </view> </view> </view> <u-form ref="formRef" label-width="90"> <!-- 客æ·ä¿¡æ¯ --> <u-cell-group title="ä¼è®®ç³è¯·"> <u-form-item label="ä¼è®®ä¸»é¢" prop="title" required border-bottom> <u-input v-model="meetingForm.title" placeholder="请è¾å ¥ä¼è®®ä¸»é¢" /> </u-form-item> <u-form-item label="ä¼è®®å®¤" prop="roomId" required border-bottom> <u-input v-model="meetingForm.roomName" placeholder="è¯·éæ©ä¼è®®å®¤" readonly @click="showRoomPicker = true" /> <template #right> <up-icon name="arrow-right" @click="showRoomPicker = true"></up-icon> </template> </u-form-item> <u-form-item label="主æäºº" prop="host" required border-bottom> <u-input v-model="meetingForm.host" placeholder="请è¾å ¥ä¸»æäººå§å" /> </u-form-item> <u-form-item label="ä¼è®®æ¥æ" prop="meetingDate" required border-bottom> <u-input v-model="meetingForm.meetingDate" placeholder="è¯·éæ©ä¼è®®æ¥æ" readonly @click="showDatePicker = true" /> <template #right> <up-icon name="calendar" @click="showDatePicker = true"></up-icon> </template> </u-form-item> <u-form-item label="å¼å§æ¶é´" prop="startTime" required border-bottom> <u-input v-model="meetingForm.startTime" placeholder="è¯·éæ©å¼å§æ¶é´" readonly @click="showStartTimePicker = true" /> <template #right> <up-icon name="clock" @click="showStartTimePicker = true"></up-icon> </template> </u-form-item> <u-form-item label="ç»ææ¶é´" prop="endTime" required border-bottom> <u-input v-model="meetingForm.endTime" placeholder="è¯·éæ©ç»ææ¶é´" readonly @click="showEndTimePicker = true" /> <template #right> <up-icon name="clock" @click="showEndTimePicker = true"></up-icon> </template> </u-form-item> <u-form-item label="åä¼äººå" prop="participants" required border-bottom> <u-input v-model="meetingForm.participantsNames" placeholder="è¯·éæ©åä¼äººå" readonly @click="showEquipmentSheet = true" /> <template #right> <up-icon name="arrow-right" @click="openParticipantPicker"></up-icon> </template> </u-form-item> <u-form-item label="ä¼è®®è¯´æ" prop="description" border-bottom> <u-input v-model="meetingForm.description" placeholder="请è¾å ¥ä¼è®®è¯´æ" /> </u-form-item> </u-cell-group> <!-- æäº¤æé® --> </u-form> <view class="footer-btns"> <u-button class="cancel-btn" @click="resetForm">éç½®</u-button> <u-button class="save-btn" @click="handleSubmit">ä¿å</u-button> </view> <!-- æ¥æéæ©å¨ --> <up-datetime-picker v-model="meetingForm.meetingDate" mode="date" :show="showDatePicker" @confirm="onDateSelect" @cancel="showDatePicker = false" format="YYYY-MM-DD" value-format="YYYY-MM-DD" :min-date="minDate" /> <!-- å¼å§æ¶é´éæ©å¨ --> <up-action-sheet :show="showStartTimePicker" :actions="timeOptions" @select="onStartTimeSelect" @close="showStartTimePicker = false" /> <!-- ç»ææ¶é´éæ©å¨ --> <up-action-sheet :show="showEndTimePicker" :actions="timeOptions" @select="onEndTimeSelect" @close="showEndTimePicker = false" /> <!-- ä¼è®®å®¤éæ©å¨ --> <up-action-sheet :show="showRoomPicker" :actions="meetingRooms" @select="onRoomSelect" @close="showRoomPicker = false" /> <u-popup :show="showEquipmentSheet" mode="bottom" @close="showEquipmentSheet = false" height="200px"> <view class="popup-content"> <view class="popup-body"> <u-checkbox-group v-model="meetingForm.participants" @change="handleParticipantChange" icon-placement="right" placement="row"> <view style="width:100%;padding:10px;margin-top:20px;"> <u-checkbox v-for="option in employees" :key="option.id" :name="option.id" :label="`${option.staffName} (${option.postJob})`" class="checkbox-item"></u-checkbox> </view> </u-checkbox-group> </view> </view> </u-popup> </view> </template> <script setup> // æ¿æ¢ toast æ¹æ³ defineOptions({ name: "meeting-settings-detail" }); const showToast = message => { uni.showToast({ title: message, icon: "none", }); }; import { ref, reactive, onMounted, computed } from "vue"; import PageHeader from "@/components/PageHeader.vue"; import useUserStore from "@/store/modules/user"; import { saveMeetingApplication, getRoomEnum, } from "@/api/managementMeetings/meeting"; import { getStaffOnJob } from "@/api/personnelManagement/onboarding"; import dayjs from "dayjs"; const userStore = useUserStore(); // è¡¨åæ°æ® const meetingForm = reactive({ title: "", type: "", roomId: "", roomName: "", host: "", meetingDate: "", startTime: "", endTime: "", participants: [], description: "", participantsNames: [], applicationType: "department", }); // ç³è¯·ç±»åé项 const applicationTypes = ref([ { value: "approval", name: "å®¡æ¹æµç¨ä¼è®®", desc: "éè¦ç»è¿å¤çº§å®¡æ¹çä¼è®®ç³è¯·", // icon: Document, }, { value: "department", name: "é¨é¨çº§ä¼è®®", desc: "é¨é¨å é¨ä¼è®®ç³è¯·æµç¨", // icon: Promotion, }, { value: "notification", name: "ä¼è®®éç¥", desc: "æ é审æ¹ç´æ¥åå¸çä¼è®®éç¥", // icon: Bell, }, ]); // 页é¢ç¶æ const loading = ref(false); const formRef = ref(null); const showDatePicker = ref(false); const showStartTimePicker = ref(false); const showEndTimePicker = ref(false); const showRoomPicker = ref(false); // æå°æ¥æï¼ä»å¤©ï¼ const minDate = computed(() => { return dayjs().format("YYYY-MM-DD"); }); // éæ©ç³è¯·ç±»å const selectApplicationType = item => { meetingForm.applicationType = item.value; }; // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; const showEquipmentSheet = ref(false); const openParticipantPicker = () => { showEquipmentSheet.value = true; }; // é置表å const resetForm = () => { meetingForm.title = ""; meetingForm.type = ""; meetingForm.roomId = ""; meetingForm.roomName = ""; meetingForm.host = ""; meetingForm.meetingDate = ""; meetingForm.startTime = ""; meetingForm.endTime = ""; meetingForm.participants = []; meetingForm.participantsNames = []; meetingForm.description = ""; meetingForm.applicationType = "department"; }; const handleParticipantChange = val => { console.log("val", val); meetingForm.participants = val; meetingForm.participantsNames = employees.value .filter(employee => val.includes(employee.id)) .map(employee => employee.staffName); }; // æäº¤è¡¨å const handleSubmit = async () => { console.log("meetingForm", meetingForm); if (!meetingForm.title) { showToast("请è¾å ¥ä¼è®®ä¸»é¢"); return; } if (!meetingForm.roomId) { showToast("è¯·éæ©ä¼è®®å®¤"); return; } if (!meetingForm.host) { showToast("请è¾å ¥ä¸»æäºº"); return; } if (!meetingForm.meetingDate) { showToast("è¯·éæ©ä¼è®®æ¥æ"); return; } if (!meetingForm.startTime) { showToast("è¯·éæ©å¼å§æ¶é´"); return; } if (!meetingForm.endTime) { showToast("è¯·éæ©ç»ææ¶é´"); return; } if (!meetingForm.participants) { showToast("è¯·éæ©åä¼äººå"); return; } // éªè¯å¼å§æ¶é´å¿ é¡»å°äºç»ææ¶é´ if (meetingForm.startTime >= meetingForm.endTime) { showToast("å¼å§æ¶é´å¿ é¡»å°äºç»ææ¶é´"); return; } try { loading.value = true; let formData = { ...meetingForm }; formData.startTime = `${meetingForm.meetingDate} ${meetingForm.startTime}:00`; formData.endTime = `${meetingForm.meetingDate} ${meetingForm.endTime}:00`; formData.participants = JSON.stringify(meetingForm.participants); console.log(formData); // è°ç¨å®é çAPI const res = await saveMeetingApplication(formData); if (res.code === 200) { showToast("ä¿åæå"); setTimeout(() => { goBack(); }, 500); } else { showToast(res.message || "ä¿å失败ï¼è¯·éè¯"); } } catch (e) { console.error("ä¿å失败:", e); showToast("ä¿å失败ï¼è¯·éè¯"); } finally { loading.value = false; } }; // æ¥æéæ©å¨ç¡®è®¤åè° const onDateSelect = action => { console.log(action.value); if (action.value) { meetingForm.meetingDate = dayjs(action.value).format("YYYY-MM-DD"); } else { meetingForm.meetingDate = dayjs().format("YYYY-MM-DD"); } showDatePicker.value = false; }; const onStartTimeSelect = action => { meetingForm.startTime = action.value; showStartTimePicker.value = false; }; const onEndTimeSelect = action => { meetingForm.endTime = action.value; showEndTimePicker.value = false; }; // ä¼è®®å®¤éæ©åè° const onRoomSelect = action => { meetingForm.roomId = action.id; meetingForm.roomName = action.name; showRoomPicker.value = false; }; // æ¶é´é项ï¼ä»¥åå°æ¶ä¸ºé´éï¼ const timeOptions = ref([]); // åå§åæ¶é´é项 const initTimeOptions = () => { const options = []; for (let hour = 8; hour <= 18; hour++) { // æ¯ä¸ªå°æ¶æ·»å 两个éé¡¹ï¼æ´ç¹ååç¹ options.push({ value: `${hour.toString().padStart(2, "0")}:00`, name: `${hour.toString().padStart(2, "0")}:00`, }); if (hour < 18) { // 18:00ä¹å没æåç¹é项 options.push({ value: `${hour.toString().padStart(2, "0")}:30`, name: `${hour.toString().padStart(2, "0")}:30`, }); } } timeOptions.value = options; }; // ä¼è®®å®¤å表 const employees = ref([]); const meetingRooms = ref([]); const getMeetingRooms = async () => { try { const res = await getRoomEnum(); if (res.code === 200) { meetingRooms.value = res.data || []; } else { showToast(res.message || "è·åä¼è®®å®¤å表失败"); } } catch (e) { console.error("è·åä¼è®®å®¤å表失败:", e); showToast("è·åä¼è®®å®¤å表失败ï¼è¯·éè¯"); } }; onMounted(() => { initPageData(); initTimeOptions(); getMeetingRooms(); getStaffOnJob().then(res => { employees.value = res.data.sort((a, b) => a.postJob.localeCompare(b.postJob) ); }); }); const initPageData = () => { // åå§åæ°æ® }; </script> <style scoped lang="scss"> @import "@/static/scss/form-common.scss"; .footer-btns { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; display: flex; justify-content: space-around; align-items: center; padding: 0.75rem 0; box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05); z-index: 1000; } .cancel-btn { font-weight: 400; font-size: 1rem; color: #ffffff; width: 6.375rem; background: #c7c9cc; box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2); border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; } .save-btn { font-weight: 400; font-size: 1rem; color: #ffffff; width: 14rem; background: linear-gradient(140deg, #00baff 0%, #006cfb 100%); box-shadow: 0 0.25rem 0.625rem 0 rgba(3, 88, 185, 0.2); border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; } .application-type-item { width: 94%; margin-left: 3%; // height: 120rpx; background-color: #f1f1f1; border-radius: 10rpx; margin-top: 20rpx; padding: 20rpx; box-shadow: 0 2rpx 12rpx 0 rgba(246, 244, 244, 0.1); transition: box-shadow 0.3s ease; } .application-type-item.active { box-shadow: 0 4rpx 16rpx 0 rgba(0, 0, 0, 0.15); background-color: #fff; border: 1rpx solid #0078a3; } .application-type-name { font-weight: 400; font-size: 1rem; color: #000000; } .application-type-item.active .application-type-name { color: #0078a3; } .application-type-desc { font-weight: 400; font-size: 0.875rem; margin-top: 0.5rem; color: #757575; } .application-type-item.active .application-type-desc { color: #0078a3; } </style> src/pages/managementMeetings/meetingList/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,529 @@ <template> <view class="meeting-list"> <PageHeader title="ä¼è®®å表" @back="goBack" /> <!-- æ¥è¯¢è¡¨å --> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <up-input class="search-text" placeholder="æ¥è¯¢æ¥æ" @click.stop="showDatePicker" v-model="queryForm.meetingDate" clearable /> </view> <view class="filter-button" @click="clearDate"> <u-icon name="close-circle-fill" size="24" color="#999"></u-icon> </view> </view> </view> <!-- æ¥æéæ©å¨ --> <up-datetime-picker v-model="datePickerValue" mode="date" :show="showDatePickerDialog" @confirm="handleDateConfirm" @cancel="showDatePickerDialog = false" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /> <!-- ä¼è®®å®¤ä½¿ç¨æ åµ --> <view class="table-container"> <scroll-view scroll-x="true" style="width: 100%;"> <view class="time-table"> <!-- 表头 --> <view class="table-header"> <view class="header-cell room-header">ä¼è®®å®¤</view> <view v-for="timeSlot in timeSlots" :key="timeSlot.value" class="header-cell time-header"> {{ timeSlot.label }} </view> </view> <!-- è¡¨æ ¼å 容 --> <view class="table-body"> <view v-for="room in roomUsage" :key="room.id" class="table-row"> <view class="cell room-cell">{{ room.name }}</view> <view class="cells-container"> <template v-for="(cell, index) in generateMeetingCells(room)" :key="index"> <view class="cell content-cell" :class="[cell.type, `status-${cell.meeting?.status || '0'}`]" :style="{ width: `${cell.span * 120}rpx` }" @click="viewMeetingDetails(cell)"> <view v-if="cell.type === 'meeting'" class="meeting-content"> <view class="meeting-title">{{ cell.meeting.title }}</view> <view class="meeting-time">{{ cell.startTime }}-{{ cell.endTime }}</view> </view> <view v-else class="free-content"> ç©ºé² </view> </view> </template> </view> </view> </view> </view> </scroll-view> </view> <!-- ä¼è®®è¯¦æ å¯¹è¯æ¡ --> <u-popup :show="detailDialogVisible" mode="center" customStyle="width: 80%;" :round="10"> <view class="dialog-content"> <view class="dialog-header"> <text class="dialog-title">ä¼è®®è¯¦æ </text> <up-icon name="close" @click="detailDialogVisible = false" class="close-icon"></up-icon> </view> <view v-if="currentMeeting" class="dialog-body"> <view class="detail-item"> <text class="detail-label">ä¼è®®ä¸»é¢</text> <text class="detail-value">{{ currentMeeting.title }}</text> </view> <view class="detail-item"> <text class="detail-label">ä¼è®®å®¤</text> <text class="detail-value">{{ currentMeeting.room }}</text> </view> <view class="detail-item"> <text class="detail-label">ä¼è®®æ¶é´</text> <text class="detail-value">{{ currentMeeting.time }}</text> </view> <view class="detail-item"> <text class="detail-label">主æäºº</text> <text class="detail-value">{{ currentMeeting.host }}</text> </view> <view class="detail-item"> <text class="detail-label">åä¼äººæ°</text> <text class="detail-value">{{ currentMeeting.participants }}人</text> </view> <view class="detail-item"> <text class="detail-label">ä¼è®®è¯´æ</text> <text class="detail-value">{{ currentMeeting.description }}</text> </view> </view> <view class="dialog-footer"> <u-button @click="detailDialogVisible = false">å ³é</u-button> </view> </view> </u-popup> </view> </template> <script setup> import { ref, reactive, onMounted } from "vue"; import PageHeader from "@/components/PageHeader.vue"; import { getMeetingUseList } from "@/api/managementMeetings/meeting.js"; import dayjs from "dayjs"; // æ¥è¯¢è¡¨å const queryForm = reactive({ meetingDate: dayjs().format("YYYY-MM-DD"), }); const loading = ref(false); const timeSlots = ref([]); const roomUsage = ref([]); const currentMeeting = ref(null); const detailDialogVisible = ref(false); const showDatePickerDialog = ref(false); const datePickerValue = ref(Date.now()); // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; // æ¸ ç©ºæ¥æ const clearDate = () => { datePickerValue.value = Date.now(); queryForm.meetingDate = dayjs().format("YYYY-MM-DD"); getList(); }; // æ¾ç¤ºæ¥æéæ©å¨ const showDatePicker = () => { if (queryForm.meetingDate) { datePickerValue.value = new Date(queryForm.meetingDate).getTime(); } else { datePickerValue.value = Date.now(); } console.log(datePickerValue.value, "datePickerValue.value"); showDatePickerDialog.value = true; }; // å¤çæ¥æéæ©ç¡®è®¤ const handleDateConfirm = value => { // dayjs().format("YYYY-MM-DD") console.log(value, "value"); queryForm.meetingDate = dayjs(value.value).format("YYYY-MM-DD"); showDatePickerDialog.value = false; getList(); }; // è·ååè¡¨æ°æ® const getList = () => { handleSearch(); }; // åå§åæ¶é´æ§½ï¼ä»¥åå°æ¶ä¸ºé´éï¼ä»8:00å°18:00ï¼ const initTimeSlots = () => { const slots = []; for (let hour = 8; hour < 18; hour++) { // æ¯ä¸ªå°æ¶æ·»å 两个æ¶é´æ®µï¼æ´ç¹ååç¹ slots.push({ label: `${hour.toString().padStart(2, "0")}:00`, value: `${hour.toString().padStart(2, "0")}:00`, }); if (hour < 17) { // å°17:30ä¸ºæ¢ slots.push({ label: `${hour.toString().padStart(2, "0")}:30`, value: `${hour.toString().padStart(2, "0")}:30`, }); } } timeSlots.value = slots; console.log(timeSlots.value, "timeSlots.value"); }; // çæä¼è®®å®¤çæ¶é´åå æ ¼ const generateMeetingCells = room => { const cells = []; const meetings = room.meetings || []; const occupiedSlots = new Set(); // å¤çæ¯ä¸ªä¼è®® for (const meeting of meetings) { const startIdx = timeSlots.value.findIndex( slot => slot.value === meeting.startTime ); let endIdx = timeSlots.value.findIndex( slot => slot.value === meeting.endTime ); if (endIdx === -1) { endIdx = timeSlots.value.length; } if (startIdx !== -1) { // æ 记被å ç¨çæ¶é´æ®µ for (let i = startIdx; i < endIdx; i++) { if (timeSlots.value[i]) { occupiedSlots.add(timeSlots.value[i].value); } } // å建ä¼è®®åå æ ¼ cells.push({ type: "meeting", meeting: meeting, span: endIdx - startIdx, startTime: meeting.startTime, endTime: meeting.endTime, }); } } // å¤çç©ºé²æ¶é´æ®µ for (let i = 0; i < timeSlots.value.length; i++) { const slot = timeSlots.value[i]; if (!occupiedSlots.has(slot.value)) { // æ¥æ¾è¿ç»çç©ºé²æ¶é´æ®µ let span = 1; while ( i + span < timeSlots.value.length && !occupiedSlots.has(timeSlots.value[i + span].value) ) { occupiedSlots.add(timeSlots.value[i + span].value); span++; } cells.push({ type: "free", span: span, time: slot.value, }); } } // ææ¶é´æåº cells.sort((a, b) => { const timeA = a.startTime || a.time; const timeB = b.startTime || b.time; return ( timeSlots.value.findIndex(s => s.value === timeA) - timeSlots.value.findIndex(s => s.value === timeB) ); }); return cells; }; // æ¥çä¼è®®è¯¦æ const viewMeetingDetails = cell => { console.log(cell, "cell"); if (cell && cell.type === "meeting") { currentMeeting.value = cell.meeting; detailDialogVisible.value = true; } else { uni.showToast({ title: "该æ¶é´æ®µä¼è®®å®¤ç©ºé²", icon: "info", }); } }; // æ¥è¯¢æé®æä½ const handleSearch = async () => { loading.value = true; try { const resp = await getMeetingUseList({ ...queryForm }); roomUsage.value = resp.data; } catch (error) { uni.showToast({ title: "è·åæ°æ®å¤±è´¥", icon: "error", }); } finally { loading.value = false; } }; // éç½®æç´¢è¡¨å const resetSearch = () => { queryForm.meetingDate = dayjs().format("YYYY-MM-DD"); }; // 页é¢å è½½æ¶è·åæ°æ® onMounted(() => { // åå§åæ¶é´æ§½ initTimeSlots(); // é»è®¤æ¥è¯¢ä»å¤©çæ°æ® handleSearch(); }); </script> <style scoped lang="scss"> @import "../../../styles/sales-common.scss"; .meeting-list { min-height: 100vh; background: #f8f9fa; padding: 16rpx; } .search-section { background: #fff; padding: 16rpx; border-radius: 8rpx; margin-bottom: 16rpx; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); } .search-bar { display: flex; align-items: center; gap: 12rpx; } .search-input { flex: 1; } .filter-button { padding: 12rpx 16rpx; background: #f5f7fa; border-radius: 4rpx; } .form-buttons { display: flex; gap: 12rpx; margin-top: 16rpx; justify-content: center; } .table-container { margin-top: 16rpx; overflow-x: auto; } .time-table { width: 100%; } .table-header { display: flex; border: 1rpx solid #e4e7ed; background: #f5f7fa; } .header-cell { padding: 12rpx 8rpx; text-align: center; font-weight: bold; border-right: 1rpx solid #e4e7ed; } .room-header { width: 120rpx; flex-shrink: 0; } .time-header { width: 120rpx; flex-shrink: 0; } .table-body { border: 1rpx solid #e4e7ed; border-top: none; } .table-row { display: flex; border-top: 1rpx solid #e4e7ed; } .table-row:first-child { border-top: none; } .cell { padding: 16rpx 8rpx; text-align: center; border-right: 1rpx solid #e4e7ed; display: flex; align-items: center; justify-content: center; word-break: break-word; line-height: 1.2; } .room-cell { width: 120rpx; font-weight: bold; flex-shrink: 0; background: #f9fafc; } .cells-container { display: flex; } .content-cell { min-height: 120rpx; cursor: pointer; transition: all 0.3s; flex-direction: column; justify-content: center; } .content-cell:active { opacity: 0.8; } .free { color: #f56c6c; } .meeting { display: flex; flex-direction: column; justify-content: center; } .status-1 { background-color: #fef0f0; color: #d14646; } .status-0 { background-color: #ecf5ff; color: #409eff; } .meeting-content { width: 100%; } .meeting-title { font-weight: bold; margin-bottom: 8rpx; font-size: 24rpx; } .meeting-time { font-size: 20rpx; opacity: 0.8; } .free-content { color: #909399; } /* å¯¹è¯æ¡æ ·å¼ */ .dialog-content { padding: 24rpx; } .dialog-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24rpx; padding-bottom: 16rpx; border-bottom: 1rpx solid #e4e7ed; } .dialog-title { font-size: 32rpx; font-weight: bold; color: #303133; } .close-icon { font-size: 32rpx; color: #909399; } .dialog-body { margin-bottom: 24rpx; } .detail-item { display: flex; margin-bottom: 16rpx; padding: 8rpx 0; border-bottom: 1rpx solid #f0f0f0; } .detail-label { width: 140rpx; font-weight: bold; color: #606266; } .detail-value { flex: 1; color: #303133; } .dialog-footer { display: flex; justify-content: center; margin-top: 16rpx; } </style> src/pages/managementMeetings/meetingSettings/detail.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,344 @@ <template> <view class="client-visit-detail"> <PageHeader title="ä¼è®®å®¤è¯¦æ " @back="goBack" /> <u-form ref="formRef" label-width="90"> <!-- 客æ·ä¿¡æ¯ --> <u-cell-group title="ä¼è®®å®¤ä¿¡æ¯"> <u-form-item label="ä¼è®®å®¤åç§°" prop="name" required border-bottom> <u-input v-model="form.name" placeholder="请è¾å ¥ä¼è®®å®¤åç§°" /> </u-form-item> <u-form-item label="ä½ç½®" required prop="location" border-bottom> <u-input v-model="form.location" placeholder="请è¾å ¥ä½ç½®" /> </u-form-item> <u-form-item label="容纳人æ°" required prop="capacity" border-bottom> <u-input v-model="form.capacity" placeholder="请è¾å ¥å®¹çº³äººæ°" /> </u-form-item> <u-form-item label="设å¤é ç½®" prop="equipment" border-bottom> <u-input v-model="form.equipment" readonly placeholder="è¯·éæ©è®¾å¤é ç½®" @click="showEquipmentSheet = true" /> <template #right> <up-icon name="arrow-right" @click="openEquipmentSheet"></up-icon> </template> </u-form-item> <u-form-item label="ç¶æ" prop="status" border-bottom> <u-input v-model="statusname" readonly placeholder="è¯·éæ©ç¶æ" @click="showStatusSheet = true" /> <template #right> <up-icon name="arrow-right" @click="showStatusSheet = true"></up-icon> </template> </u-form-item> <u-form-item label="夿³¨" prop="remark" border-bottom> <u-input v-model="form.remark" placeholder="请è¾å ¥å¤æ³¨" /> </u-form-item> </u-cell-group> <!-- æäº¤æé® --> <view class="footer-btns"> <u-button class="cancel-btn" @click="goBack">åæ¶</u-button> <u-button class="sign-btn" type="primary" @click="handleSubmit" :loading="loading">ä¿å</u-button> </view> </u-form> <!-- 设å¤é ç½®éæ©å¨ --> <u-popup :show="showEquipmentSheet" mode="bottom" @close="showEquipmentSheet = false" height="200px"> <view class="popup-content"> <view class="popup-body"> <u-checkbox-group v-model="form.equipment" @change="handleEquipmentChange" icon-placement="right" placement="row"> <view style="width:100%;padding:10px;margin-top:20px;"> <u-checkbox v-for="option in equipmentOptions" :key="option.value" :name="option.value" :label="option.name" class="checkbox-item"></u-checkbox> </view> </u-checkbox-group> </view> </view> </u-popup> <!-- ç¶æéæ©å¨ --> <up-action-sheet :show="showStatusSheet" :actions="statusOptions" @select="onStatusSelect" @close="showStatusSheet = false" /> </view> </template> <script setup> // æ¿æ¢ toast æ¹æ³ defineOptions({ name: "meeting-settings-detail" }); const showToast = message => { uni.showToast({ title: message, icon: "none", }); }; import { ref, onMounted } from "vue"; import PageHeader from "@/components/PageHeader.vue"; import useUserStore from "@/store/modules/user"; import { saveRoom } from "@/api/managementMeetings/meetingSettings"; const userStore = useUserStore(); // è¡¨åæ°æ® const form = ref({ name: "", location: "", capacity: "", equipment: [], status: "", remark: "", }); const equipmentOptions = ref([ { value: "æå½±ä»ª", name: "æå½±ä»ª" }, { value: "çµè§", name: "çµè§" }, { value: "é³å", name: "é³å" }, { value: "çµè¯", name: "çµè¯" }, { value: "è§é¢ä¼è®®ç³»ç»", name: "è§é¢ä¼è®®ç³»ç»" }, { value: "ç½æ¿", name: "ç½æ¿" }, { value: "ååæ¿", name: "ååæ¿" }, { value: "æ 线ç½ç»", name: "æ 线ç½ç»" }, ]); const statusOptions = ref([ { value: "1", name: "å¯ç¨" }, { value: "0", name: "ç¦ç¨" }, ]); //// 页é¢ç¶æ const loading = ref(false); const formRef = ref(null); const showEquipmentSheet = ref(false); const showStatusSheet = ref(false); const openEquipmentSheet = () => { showEquipmentSheet.value = true; }; // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; const statusname = ref(""); // ç¶æéæ© const onStatusSelect = action => { form.value.status = action.value; statusname.value = action.name; showStatusSheet.value = false; }; // 设å¤é ç½®éæ© const handleEquipmentChange = val => { form.value.equipment = val; console.log("form.value.equipment", form.value.equipment); }; // æäº¤è¡¨å const handleSubmit = async () => { if (!form.value.name) { showToast("请è¾å ¥ä¼è®®å®¤åç§°"); return; } if (!form.value.location) { showToast("请è¾å ¥ä½ç½®"); return; } if (!form.value.capacity) { showToast("请è¾å ¥å®¹çº³äººæ°"); return; } try { loading.value = true; form.value.equipment = form.value.equipment.join(","); form.value.status = Number(form.value.status); form.value.capacity = Number(form.value.capacity); // 使ç¨å®å ¨æµ æ·è´ï¼é¿å 对象å±å¼å¨æäºè¿è¡æ¶æé console.log("form.value", form.value); saveRoom(form.value).then(res => { if (res.code !== 200) { showToast("ä¿å失败ï¼è¯·éè¯"); return; } loading.value = false; showToast("ä¿åæå"); setTimeout(() => { goBack(); }, 500); }); } catch (e) { loading.value = false; console.error("ä¿å失败:", e); showToast("ä¿å失败ï¼è¯·éè¯"); } }; // åå§å页颿°æ® const initPageData = () => { // 仿¬å°åå¨ä¸è·åä¼è®® room æ°æ® const meetingRoom = uni.getStorageSync("meetingRoom"); if (meetingRoom) { form.value = JSON.parse(JSON.stringify(meetingRoom)); if (meetingRoom.equipment) { if (Array.isArray(meetingRoom.equipment)) { form.value.equipment = meetingRoom.equipment; } else { form.value.equipment = meetingRoom.equipment.split(","); } } statusname.value = meetingRoom.status === 1 ? "å¯ç¨" : "ç¦ç¨"; // æ¸ é¤æ¬å°åå¨ä¸çæ°æ®ï¼é¿å 䏿¬¡æå¼æ¶ä»ç¶æ¾ç¤º uni.removeStorageSync("meetingRoom"); } }; onMounted(() => { initPageData(); }); </script> <style scoped lang="scss"> @import "@/static/scss/form-common.scss"; .client-visit { min-height: 100vh; background: #f8f9fa; padding-bottom: 5rem; } .footer-btns { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; display: flex; justify-content: space-around; align-items: center; padding: 0.75rem 0; box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05); z-index: 1000; } .cancel-btn { font-weight: 400; font-size: 1rem; color: #666; background: #f5f5f5; border: 1px solid #ddd; width: 45%; height: 2.5rem; border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; } .sign-btn { font-weight: 500; font-size: 1rem; color: #fff; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; width: 45%; height: 2.5rem; border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; } .location-icon { color: #1989fa; font-size: 1.2rem; } .selector-container { display: flex; align-items: center; justify-content: space-between; width: 100%; height: 100%; } .selector-text { font-size: 14px; color: #333; } .popup-content { padding: 20rpx; } .popup-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; } .popup-title { font-size: 16px; font-weight: bold; color: #333; } .close-icon { font-size: 20px; color: #999; } .popup-body { max-height: 60vh; overflow-y: auto; margin-bottom: 20rpx; } .checkbox-item { margin-bottom: 15rpx; font-size: 14px; } .popup-footer { display: flex; justify-content: space-between; gap: 15rpx; } .cancel-btn-popup { flex: 1; border-radius: 8rpx; } .confirm-btn-popup { flex: 1; border-radius: 8rpx; } .checkbox-item { margin-top: 40rpx; } </style> src/pages/managementMeetings/meetingSettings/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,244 @@ <template> <view class="sales-accoun"> <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> <PageHeader title="ä¼è®®è®¾ç½®" @back="goBack" /> <!-- æç´¢åçéåºå --> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <up-input class="search-text" placeholder="请è¾å ¥å®¢æ·åç§°" v-model="name" @blur="getList" clearable /> </view> <view class="filter-button" @click="getList"> <u-icon name="search" size="24" color="#999"></u-icon> </view> </view> </view> <!-- æè®¿è®°å½å表 --> <view class="ledger-list" v-if="visitList.length > 0"> <view v-for="(item, index) in visitList" :key="index"> <view class="ledger-item"> <view class="item-header"> <view class="item-left"> <view class="document-icon"> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> </view> <text class="item-id">ä¼è®®å®¤åç§°ï¼{{ item.name || '-' }}</text> </view> </view> <up-divider></up-divider> <view class="item-details"> <!-- <view class="detail-row"> <text class="detail-label">ä¼è®®å®¤åç§°</text> <text class="detail-value">{{ item.name || '-' }}</text> </view> --> <view class="detail-row"> <text class="detail-label">ä½ç½®</text> <text class="detail-value">{{ item.location || '-' }}</text> </view> <view class="detail-row"> <text class="detail-label">容纳人æ°</text> <text class="detail-value">{{ item.capacity || '-' }}</text> </view> <view class="detail-row"> <text class="detail-label">设å¤é ç½®</text> <text class="detail-value">{{ item.equipment }}</text> </view> <view class="detail-row"> <text class="detail-label">ç¶æ</text> <text class="detail-value" :class="{'status-enabled': item.status == 1, 'status-disabled': item.status == 0}">{{ item.status == 1 ? 'å¯ç¨' : 'ç¦ç¨' }}</text> </view> </view> <!-- æé®åºå --> <view class="action-buttons"> <u-button type="primary" size="small" class="action-btn" @click="viewDetail(item)"> ç¼è¾ </u-button> <u-button type="error" size="small" class="action-btn" @click="confirmDelete(item)"> å é¤ </u-button> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ ä¼è®®å®¤è®°å½</text> </view> <!-- æµ®å¨æ°å¢æé® --> <view class="fab-button" @click="addVisit"> <up-icon name="plus" size="24" color="#ffffff"></up-icon> </view> </view> </template> <script setup> import { ref, onMounted } from "vue"; import { onShow } from "@dcloudio/uni-app"; import PageHeader from "@/components/PageHeader.vue"; import { getMeetingRoomList, delRoom, } from "@/api/managementMeetings/meetingSettings"; import useUserStore from "@/store/modules/user"; // æ¿æ¢ toast æ¹æ³ defineOptions({ name: "client-visit-index" }); const showToast = message => { uni.showToast({ title: message, icon: "none", }); }; import dayjs from "dayjs"; const userStore = useUserStore(); // æç´¢å ³é®è¯ const name = ref(""); // æè®¿è®°å½æ°æ® const visitList = ref([]); // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; const confirmDelete = item => { uni.showModal({ title: "确认å é¤", content: `æ¯å¦ç¡®è®¤å é¤ä¼è®®å®¤ ${item.name}ï¼`, success: res => { if (res.confirm) { delRoom(item.id) .then(() => { showToast("å 餿å"); getList(); }) .catch(() => { showToast("å é¤å¤±è´¥"); }); } }, }); }; // æ¥è¯¢å表 const getList = () => { showLoadingToast("å è½½ä¸..."); const params = { current: -1, size: -1, name: name.value, }; getMeetingRoomList(params) .then(res => { visitList.value = res.data.records; closeToast(); }) .catch(() => { closeToast(); showToast("è·åæ°æ®å¤±è´¥"); }); }; // æ¾ç¤ºå è½½æç¤º const showLoadingToast = message => { uni.showLoading({ title: message, mask: true, }); }; // å ³éæç¤º const closeToast = () => { uni.hideLoading(); }; // æ°å¢æè®¿ - 跳转å°ç»è®°é¡µé¢ const addVisit = () => { uni.navigateTo({ url: "/pages/managementMeetings/meetingSettings/detail", }); }; // ç¼è¾ const viewDetail = item => { uni.setStorageSync("meetingRoom", item); uni.navigateTo({ url: "/pages/managementMeetings/meetingSettings/detail", }); }; onMounted(() => { getList(); }); onShow(() => { getList(); }); </script> <style scoped lang="scss"> @import "../../../styles/sales-common.scss"; // 页é¢ç¹å®çæ ·å¼è¦ç .sales-accoun { min-height: 100vh; background: #f8f9fa; position: relative; padding-bottom: 80px; } // ç¹å®ç徿 æ ·å¼ .document-icon { background: #667eea; // ä¿æé¡µé¢ç¹æçèæ¯è² } // ç¹ææ ·å¼ .visit-status { display: flex; align-items: center; } .detail-value { word-break: break-all; // ä¿ç页é¢ç¹æçææ¬æ¢è¡æ ·å¼ color: #333; // ä¿æé¡µé¢ç¹æçææ¬é¢è² } // ç¶ææ ·å¼ .status-enabled { color: #28a745; // ä¿æé¡µé¢ç¹æçæåé¢è² } .status-disabled { color: #dc3545; // ä¿æé¡µé¢ç¹æçé误é¢è² } // ç¹å®çæµ®å¨æé®æ ·å¼ .fab-button { background: #667eea; // ä¿æé¡µé¢ç¹æçèæ¯è² box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3); // ä¿æé¡µé¢ç¹æçé´å½±ææ } </style> src/pages/managementMeetings/meetingSettings/view.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,178 @@ <template> <view class="client-visit-detail"> <PageHeader title="å®¢æ·æè®¿è¯¦æ " @back="goBack" /> <!-- å å®¹å®¹å¨ --> <view class="content-container"> <!-- 客æ·ä¿¡æ¯ --> <view class="section"> <view class="section-title">客æ·ä¿¡æ¯</view> <view class="info-item"> <text class="info-label">客æ·åç§°</text> <text class="info-value">{{ form.customerName || '-' }}</text> </view> <view class="info-item"> <text class="info-label">è系人</text> <text class="info-value">{{ form.contact || '-' }}</text> </view> <view class="info-item"> <text class="info-label">èç³»çµè¯</text> <text class="info-value">{{ form.contactPhone || '-' }}</text> </view> </view> <!-- æè®¿ä¿¡æ¯ --> <view class="section"> <view class="section-title">æè®¿ä¿¡æ¯</view> <view class="info-item"> <text class="info-label">æè®¿ç®ç</text> <text class="info-value">{{ form.purposeVisit || '-' }}</text> </view> <view class="info-item"> <text class="info-label">æè®¿æ¶é´</text> <text class="info-value">{{ form.purposeDate || '-' }}</text> </view> <view class="info-item"> <text class="info-label">æè®¿å°ç¹</text> <text class="info-value multi-line">{{ form.visitAddress || '-' }}</text> </view> <view class="info-item"> <text class="info-label">æè®¿äºº</text> <text class="info-value">{{ form.visitingPeople || '-' }}</text> </view> <view class="info-item" v-if="form.latitude && form.longitude"> <text class="info-label">ç»çº¬åº¦</text> <text class="info-value">{{ form.latitude }}, {{ form.longitude }}</text> </view> </view> <!-- 夿³¨ä¿¡æ¯ --> <view class="section"> <view class="section-title">夿³¨ä¿¡æ¯</view> <view class="info-item remark-item"> <text class="info-label">夿³¨</text> <text class="info-value multi-line">{{ form.remark }}</text> </view> </view> </view> </view> </template> <script setup> // æ¿æ¢ toast æ¹æ³ const showToast = (message) => { uni.showToast({ title: message, icon: 'none' }) } import { ref, onMounted } from 'vue' import PageHeader from '@/components/PageHeader.vue' import useUserStore from "@/store/modules/user" const userStore = useUserStore() // è¡¨åæ°æ® const form = ref({ customerName: '', contact: '', contactPhone: '', visitingPeople: '', purposeVisit: '', purposeDate: '', visitAddress: '', latitude: '', longitude: '', locationAddress: '', remark: '' }) // è¿åä¸ä¸é¡µ const goBack = () => { // è¿åæ¶æ¸ 餿¬å°åå¨çID uni.removeStorageSync('clientVisit') uni.navigateBack() } // åå§å页颿°æ® const initPageData = () => { // 仿¬å°åå¨è·åæè®¿è®°å½è¯¦æ const row = uni.getStorageSync('clientVisit') if (row) { form.value = { ...row } } else { showToast('ææ æè®¿è®°å½æ°æ®') } } onMounted(() => { initPageData() }) </script> <style scoped lang="scss"> @import '@/static/scss/form-common.scss'; .client-visit-detail { min-height: 100vh; background-color: #f8f9fa; } .content-container { padding: 16px; } .section { background-color: #ffffff; border-radius: 12px; margin-bottom: 16px; overflow: hidden; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); } .section-title { font-size: 16px; font-weight: 600; color: #333333; padding: 16px 16px 12px; border-bottom: 1px solid #f0f0f0; } .info-item { display: flex; padding: 14px 16px; border-bottom: 1px solid #f8f8f8; align-items: flex-start; } .info-item:last-child { border-bottom: none; } .info-label { font-size: 14px; color: #666666; min-width: 80px; flex-shrink: 0; line-height: 22px; } .info-value { font-size: 14px; color: #333333; flex: 1; line-height: 22px; text-align: right; } .multi-line { text-align: left; word-break: break-all; line-height: 1.6; } .remark-item { padding-bottom: 16px; } </style>