From 1f211daff3cf6831b3e2f8c78ef0769d1b6dc95e Mon Sep 17 00:00:00 2001 From: ck_yeun9 Date: Fri, 20 Feb 2026 15:50:21 +0800 Subject: [PATCH 01/20] feat: add role grants management page and update role permissions handling - Added new API route for reading role grants. - Introduced RoleGrantPermissionView component for managing role grants. - Updated routing to include role grants management. - Enhanced page title handling for role grants. - Refactored role management view to navigate to the new grants page. - Implemented logic for loading and saving role grants, including menu and permission management. - Improved date handling in RoomMapView and added utility functions for default empty date checks. - Cleaned up unused modal for role permissions in RoleManagementView. --- components.d.ts | 2 + src/api/roleapi.js | 123 +++ src/config/apiRoutes.js | 1 + src/router/index.js | 11 + src/utils/pageTitle.js | 4 + src/views/roominformation/RoomMapView.vue | 34 +- .../RoleGrantPermissionView.vue | 719 ++++++++++++++++++ .../systemmanagement/RoleManagementView.vue | 411 ++-------- .../UnifiedAccountPermissionView.vue | 25 +- 9 files changed, 977 insertions(+), 353 deletions(-) create mode 100644 src/views/systemmanagement/RoleGrantPermissionView.vue diff --git a/components.d.ts b/components.d.ts index 5135cc6..21897ce 100644 --- a/components.d.ts +++ b/components.d.ts @@ -48,6 +48,7 @@ declare module 'vue' { ARangePicker: typeof import('ant-design-vue/es')['RangePicker'] AResult: typeof import('ant-design-vue/es')['Result'] ARow: typeof import('ant-design-vue/es')['Row'] + ArrowLeftOutlined: typeof import('@ant-design/icons-vue')['ArrowLeftOutlined'] ASegmented: typeof import('ant-design-vue/es')['Segmented'] ASelect: typeof import('ant-design-vue/es')['Select'] ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] @@ -64,6 +65,7 @@ declare module 'vue' { ATimeline: typeof import('ant-design-vue/es')['Timeline'] ATimelineItem: typeof import('ant-design-vue/es')['TimelineItem'] ATooltip: typeof import('ant-design-vue/es')['Tooltip'] + ATree: typeof import('ant-design-vue/es')['Tree'] ATypographyParagraph: typeof import('ant-design-vue/es')['TypographyParagraph'] AUpload: typeof import('ant-design-vue/es')['Upload'] CaretRightOutlined: typeof import('@ant-design/icons-vue')['CaretRightOutlined'] diff --git a/src/api/roleapi.js b/src/api/roleapi.js index f1821a5..21539c9 100644 --- a/src/api/roleapi.js +++ b/src/api/roleapi.js @@ -16,6 +16,107 @@ const buildRoleNumberPayload = (roleNumber) => { return { RoleNumber: roleNumber }; }; +const toUniqueStringArray = (list) => + Array.from( + new Set( + (Array.isArray(list) ? list : []) + .map((item) => String(item || "").trim()) + .filter(Boolean), + ), + ); + +const toUniqueNumberArray = (list) => + Array.from( + new Set( + (Array.isArray(list) ? list : []) + .map((item) => Number(item)) + .filter((item) => Number.isFinite(item)), + ), + ); + +const parseLegacyRolePermissionItems = (items) => { + const normalizedItems = Array.isArray(items) ? items : []; + const permissionNumbers = []; + const menuIds = []; + + normalizedItems.forEach((item) => { + if (typeof item === "string") { + permissionNumbers.push(item); + return; + } + + if (!item || typeof item !== "object") { + return; + } + + permissionNumbers.push(item.PermissionNumber || item.PermissionCode || ""); + if (item.MenuId !== null && item.MenuId !== undefined) { + menuIds.push(item.MenuId); + } + }); + + return { + PermissionNumbers: toUniqueStringArray(permissionNumbers), + MenuIds: toUniqueNumberArray(menuIds), + }; +}; + +const normalizeRoleGrants = (rawData, fallbackRoleNumber = "") => { + if (Array.isArray(rawData)) { + const legacy = parseLegacyRolePermissionItems(rawData); + return { + RoleNumber: String(fallbackRoleNumber || ""), + PermissionNumbers: legacy.PermissionNumbers, + MenuIds: legacy.MenuIds, + }; + } + + if (!rawData || typeof rawData !== "object") { + return { + RoleNumber: String(fallbackRoleNumber || ""), + PermissionNumbers: [], + MenuIds: [], + }; + } + + const permissionNumbers = toUniqueStringArray(rawData.PermissionNumbers); + const menuIds = toUniqueNumberArray(rawData.MenuIds); + + if ( + permissionNumbers.length > 0 || + menuIds.length > 0 || + rawData.RoleNumber !== undefined + ) { + return { + RoleNumber: String(rawData.RoleNumber || fallbackRoleNumber || ""), + PermissionNumbers: permissionNumbers, + MenuIds: menuIds, + }; + } + + const legacy = parseLegacyRolePermissionItems(rawData.Items); + return { + RoleNumber: String(rawData.RoleNumber || fallbackRoleNumber || ""), + PermissionNumbers: legacy.PermissionNumbers, + MenuIds: legacy.MenuIds, + }; +}; + +const shouldFallbackToLegacyRolePermissions = (error) => { + const status = Number(error?.response?.status || 0); + if (status === 404) { + return true; + } + + const message = String( + error?.response?.data?.Message || error?.message || "", + ).toLowerCase(); + return ( + message.includes("readrolegrants") && + (message.includes("not found") || message.includes("不存在")) + ); +}; + export const fetchRoles = async (params) => { try { const response = await api.get(API_ENDPOINTS.routes.ROLE_SELECT_ROLE_LIST, { @@ -79,6 +180,28 @@ export const grantRolePermissions = async (data) => { } }; +export const readRoleGrants = async (roleNumber) => { + const payload = buildRoleNumberPayload(roleNumber); + + try { + const response = await api.post( + API_ENDPOINTS.routes.ROLE_READ_ROLE_GRANTS, + payload, + ); + return normalizeRoleGrants(response.data?.Data, payload.RoleNumber); + } catch (error) { + if (!shouldFallbackToLegacyRolePermissions(error)) { + throw error; + } + + const response = await api.post( + API_ENDPOINTS.routes.ROLE_READ_ROLE_PERMISSIONS, + payload, + ); + return normalizeRoleGrants(response.data?.Data, payload.RoleNumber); + } +}; + // 读取指定角色已授予的权限(返回后端 Data,页面逻辑做进一步兼容处理) export const readRolePermissions = async (roleNumber) => { try { diff --git a/src/config/apiRoutes.js b/src/config/apiRoutes.js index 15dbe5c..19bb4fb 100644 --- a/src/config/apiRoutes.js +++ b/src/config/apiRoutes.js @@ -127,6 +127,7 @@ export const FLAT_API_ROUTES = Object.freeze({ ROLE_DELETE_ROLE: "/Role/DeleteRole", ROLE_GRANT_ROLE_PERMISSIONS: "/Role/GrantRolePermissions", ROLE_INSERT_ROLE: "/Role/InsertRole", + ROLE_READ_ROLE_GRANTS: "/Role/ReadRoleGrants", ROLE_READ_ROLE_PERMISSIONS: "/Role/ReadRolePermissions", ROLE_READ_ROLE_USERS: "/Role/ReadRoleUsers", ROLE_SELECT_ROLE_LIST: "/Role/SelectRoleList", diff --git a/src/router/index.js b/src/router/index.js index eba4c0d..3141a7f 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -99,6 +99,8 @@ const AdminManagement = () => import("../views/systemmanagement/AdministratorManagementView.vue"); const RoleManagement = () => import("../views/systemmanagement/RoleManagementView.vue"); +const RoleGrantPermission = () => + import("../views/systemmanagement/RoleGrantPermissionView.vue"); const AdminTypeManagement = () => import("../views/systemmanagement/AdminTypeManagementView.vue"); const MenuManagement = () => @@ -274,6 +276,15 @@ const routes = [ component: RoleManagement, meta: { requiresAuth: true }, }, + { + path: "/rolemanagement/grants/:roleNumber", + name: "rolemanagement-grants", + component: RoleGrantPermission, + meta: { + requiresAuth: true, + requiredPerm: "system:role:grp", + }, + }, { path: "/admintypemanagement", name: "admintypemanagement", diff --git a/src/utils/pageTitle.js b/src/utils/pageTitle.js index 298abe7..90a6f2d 100644 --- a/src/utils/pageTitle.js +++ b/src/utils/pageTitle.js @@ -1,6 +1,10 @@ export function getPageTitle(routePath) { // 规范化路径:去除结尾斜杠并全部小写,以兼容 /goodsManagement, /operationLog 等不一致命名 const p = (routePath || "").replace(/\/+$/, "").toLowerCase() || "/"; + if (p.startsWith("/rolemanagement/grants/")) { + return "message.assignPermissions"; + } + switch (p) { case "/home": return "message.home"; diff --git a/src/views/roominformation/RoomMapView.vue b/src/views/roominformation/RoomMapView.vue index 600ea33..2005aed 100644 --- a/src/views/roominformation/RoomMapView.vue +++ b/src/views/roominformation/RoomMapView.vue @@ -19,11 +19,11 @@

{{ $t("message.checkInTime") }}: - {{ formatDate(room[RoomFields.CHECK_IN_TIME]) }} + {{ formatRoomDate(room[RoomFields.CHECK_IN_TIME]) }}

{{ $t("message.checkOutTime") }}: - {{ formatDate(room[RoomFields.CHECK_OUT_TIME]) }} + {{ formatRoomDate(room[RoomFields.CHECK_OUT_TIME]) }}

{{ $t("message.roomRent") }}: @@ -142,6 +142,34 @@ const pageTitleKey = computed(() => getPageTitle(route.path)); const translatedPageTitle = computed(() => t(pageTitleKey.value)); const groupedRooms = computed(() => groupRoomsByPosition()); +const isDefaultEmptyDateTime = (value) => { + if (!value) return true; + const raw = String(value).trim(); + if (!raw) return true; + + if ( + raw.startsWith("1900-01-01T00:00:00") || + raw.startsWith("1900-01-01 00:00:00") + ) { + return true; + } + + const parsed = dayjs(raw); + if (!parsed.isValid()) return false; + + return ( + parsed.year() === 1900 && + parsed.month() === 0 && + parsed.date() === 1 && + parsed.hour() === 0 && + parsed.minute() === 0 && + parsed.second() === 0 + ); +}; + +const formatRoomDate = (value) => + isDefaultEmptyDateTime(value) ? "" : formatDate(value); + const fetchRoomData = async () => { loading.value = true; try { @@ -183,7 +211,7 @@ const groupRoomsByPosition = () => { }; const calculateDaysLived = (checkInTime) => { - if (!checkInTime) return ""; + if (isDefaultEmptyDateTime(checkInTime)) return ""; return dayjs().diff(dayjs(checkInTime), "day") + t("message.dayUnit"); }; diff --git a/src/views/systemmanagement/RoleGrantPermissionView.vue b/src/views/systemmanagement/RoleGrantPermissionView.vue new file mode 100644 index 0000000..f90d1f1 --- /dev/null +++ b/src/views/systemmanagement/RoleGrantPermissionView.vue @@ -0,0 +1,719 @@ + + + + + diff --git a/src/views/systemmanagement/RoleManagementView.vue b/src/views/systemmanagement/RoleManagementView.vue index d97d3a9..b5341db 100644 --- a/src/views/systemmanagement/RoleManagementView.vue +++ b/src/views/systemmanagement/RoleManagementView.vue @@ -89,7 +89,7 @@ type="link" size="small" :title="$t('message.assignPermissions')" - @click="openRolePermModal(record)" + @click="openRoleGrantPage(record)" > - - -

-
- {{ $t("message.roleNumber") }}: - {{ currentRoleNumber }} -
- - {{ $t("message.choseAll") }} - -
- - - -
-
-
-
{{ grp.group }}
- - {{ $t("message.choseAll") }} - -
-
- - {{ - opt.label - }} - -
-
-
-
-
- - import { ref, onMounted, computed, reactive, onBeforeUnmount } from "vue"; -import { useRoute } from "vue-router"; +import { useRoute, useRouter } from "vue-router"; import { getPageTitle } from "@/utils/pageTitle"; import { showErrorNotification, showSuccessNotification } from "@/utils/index"; import { @@ -289,13 +218,10 @@ import { addRole, updateRole, deleteRole, - grantRolePermissions, - readRolePermissions, readRoleUsers, assignRoleUsers, } from "@/api/roleapi"; import { fetchAdmins } from "@/api/administratorapi"; -import { selectPermissionList } from "@/api/permissionapi"; import { fetchCustomers } from "@/api/customerapi"; import { fetchEmployees } from "@/api/employeeapi"; import { AdministratorFields } from "@/entities/administrator.entity"; @@ -317,6 +243,7 @@ import { emitter } from "@/utils/eventBus"; const { t } = useI18n(); const route = useRoute(); +const router = useRouter(); const pageTitleKey = computed(() => getPageTitle(route.path)); const translatedPageTitle = computed(() => t(pageTitleKey.value)); const loading = ref(false); @@ -332,7 +259,28 @@ const selectedRecord = ref(null); const selectedRowKeys = ref([]); const selectedRows = ref([]); -const refreshCurrentSessionPermissions = () => { +const getCurrentLoginType = () => { + try { + return String(localStorage.getItem("loginType") || "admin") + .trim() + .toLowerCase(); + } catch { + return "admin"; + } +}; + +const shouldRefreshCurrentSessionPermissions = ( + targetUserGroupType = "administrator", +) => { + const currentLoginType = getCurrentLoginType(); + if (currentLoginType !== "admin") return true; + return String(targetUserGroupType || "").toLowerCase() === "administrator"; +}; + +const refreshCurrentSessionPermissions = ( + targetUserGroupType = "administrator", +) => { + if (!shouldRefreshCurrentSessionPermissions(targetUserGroupType)) return; emitter.emit("refresh-menu"); }; @@ -598,7 +546,13 @@ onMounted(() => { fetchRoleData(); }); +const resetRoleModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetRoleModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertRole"); form[RoleFields.NUMBER] = generateSnowflakeId({ @@ -616,6 +570,7 @@ const refreshData = () => { }; const editRole = (record) => { + resetRoleModalForm(); modalVisible.value = true; modalTitle.value = t("message.updateRole"); form[RoleFields.ID] = record[RoleFields.ID]; @@ -644,6 +599,7 @@ const handleModalOk = async () => { } showSuccessNotification(t("message.addSuccess")); } + resetRoleModalForm(); modalVisible.value = false; clearSelection(); fetchRoleData(); @@ -655,6 +611,7 @@ const handleModalOk = async () => { }; const handleModalCancel = () => { + resetRoleModalForm(); modalVisible.value = false; }; @@ -685,200 +642,36 @@ const handleSorterChange = (pagination, filters, sorter) => { }; /** - * 角色权限分配 - 状态与计算属性 + * 角色权限分配 - 跳转到独立页面 */ -const permissionCatalog = ref([]); -const loadingPermissionCatalog = ref(false); -const checkedRolePerms = ref([]); -const rolePermModalVisible = ref(false); -const rolePermModalTitle = ref("角色权限分配"); -const rolePermModalLoading = ref(false); -const currentRoleNumber = ref(""); -const loadingRolePerms = ref(false); - -// 构建分组后的权限选项(复合值:PermissionNumber::Group) -const roleGroupedPermissions = computed(() => { - const groups = {}; - for (const p of permissionCatalog.value) { - const group = p.MenuKey || p.Module || "其他"; - if (!groups[group]) groups[group] = []; - const composite = `${p.PermissionNumber}::${group}`; - groups[group].push({ - label: `${p.PermissionName} (${p.PermissionNumber})`, - value: composite, - raw: p.PermissionNumber, - }); - } - return Object.entries(groups) - .map(([group, options]) => ({ - group, - options: options.sort((a, b) => a.label.localeCompare(b.label)), - })) - .sort((a, b) => a.group.localeCompare(b.group)); -}); - -// 权限编码 -> 复合值列表映射 -const rolePermNumberToComposite = computed(() => { - const map = new Map(); - for (const p of permissionCatalog.value) { - const group = p.MenuKey || p.Module || "其他"; - const composite = `${p.PermissionNumber}::${group}`; - const arr = map.get(p.PermissionNumber) || []; - arr.push(composite); - map.set(p.PermissionNumber, arr); - } - return map; -}); - -// 全选/半选计算 -const allRoleCompositeValues = computed(() => { - const vals = []; - for (const grp of roleGroupedPermissions.value) { - for (const opt of grp.options) { - vals.push(opt.value); - } - } - return vals; -}); -const allRoleChecked = computed({ - get() { - const all = allRoleCompositeValues.value; - if (all.length === 0) return false; - return all.every((v) => checkedRolePerms.value.includes(v)); - }, - set(val) { - if (val) { - checkedRolePerms.value = Array.from( - new Set(allRoleCompositeValues.value), - ); - } else { - checkedRolePerms.value = []; - } - }, -}); -const allRoleIndeterminate = computed(() => { - const all = allRoleCompositeValues.value; - if (all.length === 0) return false; - const selected = checkedRolePerms.value.length; - return selected > 0 && selected < all.length; -}); - -const getRoleGroupValues = (group) => { - if (!group || !Array.isArray(group.options)) return []; - return group.options.map((opt) => opt.value); -}; - -const isRoleGroupAllChecked = (group) => { - const values = getRoleGroupValues(group); - if (values.length === 0) return false; - const selected = new Set(checkedRolePerms.value); - return values.every((v) => selected.has(v)); -}; - -const isRoleGroupIndeterminate = (group) => { - const values = getRoleGroupValues(group); - if (values.length === 0) return false; - const selected = new Set(checkedRolePerms.value); - const checkedCount = values.filter((v) => selected.has(v)).length; - return checkedCount > 0 && checkedCount < values.length; -}; - -const setRoleGroupChecked = (group, checked) => { - const values = getRoleGroupValues(group); - const selected = new Set(checkedRolePerms.value); - if (checked) { - values.forEach((v) => selected.add(v)); - } else { - values.forEach((v) => selected.delete(v)); - } - checkedRolePerms.value = Array.from(selected); -}; - -// 拉取权限目录(不分页) -const loadPermissionCatalog = async () => { - loadingPermissionCatalog.value = true; - try { - const res = await selectPermissionList({ IgnorePaging: true }); - permissionCatalog.value = Array.isArray(res?.Items) ? res.Items : []; - } catch (e) { - showErrorNotification(e.message || t("message.pleaseTryAgainLater")); - } finally { - loadingPermissionCatalog.value = false; - } -}; - -// 读取角色已有权限,并映射为复合值集合 -const loadRoleAssignedPermissions = async (roleNumber) => { - if (!roleNumber) return; - loadingRolePerms.value = true; - try { - const items = await readRolePermissions(roleNumber); - const list = Array.isArray(items) ? items : []; - const permNums = Array.from( - new Set( - list - .map((it) => - typeof it === "string" - ? it - : (it && (it.PermissionNumber || it.PermissionCode)) || "", - ) - .filter(Boolean), - ), - ); - const set = new Set(); - (permNums || []).forEach((num) => { - const comps = rolePermNumberToComposite.value.get(num) || []; - comps.forEach((c) => set.add(c)); - }); - checkedRolePerms.value = Array.from(set); - } catch (e) { - showErrorNotification(e.message || t("message.pleaseTryAgainLater")); - } finally { - loadingRolePerms.value = false; - } -}; - -// 打开权限分配弹窗 -const openRolePermModal = async (record) => { - const roleNo = record?.[RoleFields.NUMBER]; +const openRoleGrantPage = (record) => { + const roleNo = String(record?.[RoleFields.NUMBER] || "").trim(); if (!roleNo) return; - currentRoleNumber.value = roleNo; - rolePermModalTitle.value = t("message.assignPermissions") || "角色权限分配"; - rolePermModalVisible.value = true; - await loadPermissionCatalog(); - await loadRoleAssignedPermissions(roleNo); -}; - -// 保存分配(全量覆盖) -const handleRolePermSave = async () => { - if (!currentRoleNumber.value) return; - rolePermModalLoading.value = true; - try { - const payload = Array.from( - new Set( - (checkedRolePerms.value || []).map((v) => String(v).split("::")[0]), - ), - ); - const res = await grantRolePermissions({ - RoleNumber: currentRoleNumber.value, - PermissionNumbers: payload, - }); - if (res && res.Success) { - showSuccessNotification(t("message.updateSuccess")); - rolePermModalVisible.value = false; - refreshCurrentSessionPermissions(); - } else { - showErrorNotification(res?.Message || t("message.operationFailed")); - } - } catch (e) { - showErrorNotification(e.message || t("message.operationFailed")); - } finally { - rolePermModalLoading.value = false; - } -}; - -const handleRolePermCancel = () => { - rolePermModalVisible.value = false; + const rawRoleUserGroupTypeCandidates = [ + record?.UserGroupType, + record?.RoleUserType, + record?.AccountType, + record?.RoleType, + record?.BelongType, + record?.TargetUserType, + record?.ApplicableUserType, + record?.Type, + record?.TypeName, + record?.[RoleFields.NAME], + record?.[RoleFields.NUMBER], + ]; + const roleUserGroupType = rawRoleUserGroupTypeCandidates + .map((item) => String(item || "").trim()) + .find(Boolean); + + router.push({ + name: "rolemanagement-grants", + params: { roleNumber: roleNo }, + query: { + roleName: String(record?.[RoleFields.NAME] || ""), + userGroupType: roleUserGroupType || "", + }, + }); }; /** @@ -1238,7 +1031,7 @@ const handleRoleUserSave = async () => { if (res && res.Success) { showSuccessNotification(t("message.updateSuccess")); roleUserModalVisible.value = false; - refreshCurrentSessionPermissions(); + refreshCurrentSessionPermissions(currentUserGroupType.value); } else { showErrorNotification(res?.Message || t("message.operationFailed")); } @@ -1291,81 +1084,3 @@ onBeforeUnmount(() => { doSearchEmployeeOptions.cancel(); }); - - diff --git a/src/views/systemmanagement/UnifiedAccountPermissionView.vue b/src/views/systemmanagement/UnifiedAccountPermissionView.vue index 8ce86e5..b239295 100644 --- a/src/views/systemmanagement/UnifiedAccountPermissionView.vue +++ b/src/views/systemmanagement/UnifiedAccountPermissionView.vue @@ -124,7 +124,9 @@ {{ opt.label }} @@ -387,10 +389,12 @@ const rolePermColumns = [ dataIndex: "PermissionName", key: "PermissionName", }, + { title: t("message.menuId"), dataIndex: "MenuId", key: "MenuId" }, { title: t("message.menuKey"), dataIndex: "MenuKey", key: "MenuKey" }, + { title: t("message.menuTitle"), dataIndex: "MenuName", key: "MenuName" }, ]; const rpRowKey = (rec) => - `${rec.RoleNumber || ""}-${rec.PermissionNumber || ""}`; + `${rec.RoleNumber || ""}-${rec.PermissionNumber || ""}-${rec.MenuId || ""}`; // 配置映射 const adminColumns = [ @@ -572,7 +576,24 @@ const listTitleText = computed(() => safeT(listTitleKey.value)); const waitSelectText = computed(() => safeT(waitSelectKey.value)); const selectedLabelText = computed(() => safeT(selectedLabelKey.value)); +const getCurrentLoginType = () => { + try { + return String(localStorage.getItem("loginType") || "admin") + .trim() + .toLowerCase(); + } catch { + return "admin"; + } +}; + +const shouldRefreshCurrentSessionPermissions = () => { + const currentLoginType = getCurrentLoginType(); + if (currentLoginType !== "admin") return true; + return inferredType.value === "admin"; +}; + const refreshCurrentSessionPermissions = () => { + if (!shouldRefreshCurrentSessionPermissions()) return; emitter.emit("refresh-menu"); }; -- Gitee From 16be64b7d4e8871d1f8582b7dc7d38a23ab1a212 Mon Sep 17 00:00:00 2001 From: ck_yeun9 Date: Fri, 20 Feb 2026 16:00:02 +0800 Subject: [PATCH 02/20] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20=E4=B8=8D=E4=BC=9A?= =?UTF-8?q?=E9=87=8D=E7=BD=AE=E5=BF=85=E5=A1=AB=E9=AA=8C=E8=AF=81=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/base/DepartmentView.vue | 6 ++++++ src/views/base/NationView.vue | 6 ++++++ src/views/base/NoticeTypeView.vue | 6 ++++++ src/views/base/PassportView.vue | 6 ++++++ src/views/base/PositionView.vue | 6 ++++++ src/views/base/PromotionContentView.vue | 6 ++++++ src/views/base/QualificationView.vue | 6 ++++++ src/views/customermanagement/CustomerTypeView.vue | 6 ++++++ src/views/customermanagement/CustomerView.vue | 6 ++++++ src/views/customermanagement/VipLevelView.vue | 6 ++++++ src/views/finance/InternalFinanceView.vue | 6 ++++++ src/views/humanresourcemanagement/StaffManagementView.vue | 6 ++++++ src/views/materialmanagement/GoodsmanagementView.vue | 6 ++++++ src/views/roominformation/ReserManagementView.vue | 6 ++++++ src/views/roominformation/RoomConfigView.vue | 6 ++++++ src/views/roominformation/RoomManagementView.vue | 6 ++++++ src/views/supervision/SupervisionView.vue | 6 ++++++ src/views/systemmanagement/AdminTypeManagementView.vue | 6 ++++++ src/views/systemmanagement/AdministratorManagementView.vue | 6 ++++++ src/views/systemmanagement/MenuManagementView.vue | 6 ++++++ src/views/systemmanagement/RoleManagementView.vue | 4 ++-- 21 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/views/base/DepartmentView.vue b/src/views/base/DepartmentView.vue index ec09791..87d67bb 100644 --- a/src/views/base/DepartmentView.vue +++ b/src/views/base/DepartmentView.vue @@ -546,7 +546,13 @@ onMounted(() => { fetchSelectLeaders(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertDepartment"); form[DepartmentFields.ID] = 0; diff --git a/src/views/base/NationView.vue b/src/views/base/NationView.vue index db7009e..ebbfd16 100644 --- a/src/views/base/NationView.vue +++ b/src/views/base/NationView.vue @@ -393,7 +393,13 @@ onMounted(() => { fetchNationData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertNation"); form[NationFields.NUMBER] = generateSnowflakeId({ diff --git a/src/views/base/NoticeTypeView.vue b/src/views/base/NoticeTypeView.vue index 28bef0a..bc4fe70 100644 --- a/src/views/base/NoticeTypeView.vue +++ b/src/views/base/NoticeTypeView.vue @@ -396,7 +396,13 @@ onMounted(() => { fetchNoticeTypeData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertNoticeType"); form[NoticeTypeFields.NUMBER] = generateSnowflakeId({ diff --git a/src/views/base/PassportView.vue b/src/views/base/PassportView.vue index 3807574..30c43f8 100644 --- a/src/views/base/PassportView.vue +++ b/src/views/base/PassportView.vue @@ -390,7 +390,13 @@ onMounted(() => { fetchPassportData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertPassport"); form[PassportFields.NUMBER] = null; diff --git a/src/views/base/PositionView.vue b/src/views/base/PositionView.vue index 59c6a6b..ab2ddd6 100644 --- a/src/views/base/PositionView.vue +++ b/src/views/base/PositionView.vue @@ -387,7 +387,13 @@ onMounted(() => { fetchPositionData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertPosition"); form[PositionFields.ID] = 0; diff --git a/src/views/base/PromotionContentView.vue b/src/views/base/PromotionContentView.vue index 605c74b..67ab55f 100644 --- a/src/views/base/PromotionContentView.vue +++ b/src/views/base/PromotionContentView.vue @@ -416,7 +416,13 @@ onMounted(() => { fetchPromotionContentData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertPromotionContent"); form[PromotionContentFields.ID] = 0; diff --git a/src/views/base/QualificationView.vue b/src/views/base/QualificationView.vue index 9333ca4..d3c0a2c 100644 --- a/src/views/base/QualificationView.vue +++ b/src/views/base/QualificationView.vue @@ -397,7 +397,13 @@ onMounted(() => { fetchQualificationData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertQualification"); form[EducationFields.ID] = 0; diff --git a/src/views/customermanagement/CustomerTypeView.vue b/src/views/customermanagement/CustomerTypeView.vue index 42ad87a..e106fb3 100644 --- a/src/views/customermanagement/CustomerTypeView.vue +++ b/src/views/customermanagement/CustomerTypeView.vue @@ -446,7 +446,13 @@ onMounted(() => { fetchCustomerTypeData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertCustomerType"); form[CustomerTypeFields.NUMBER] = null; diff --git a/src/views/customermanagement/CustomerView.vue b/src/views/customermanagement/CustomerView.vue index 12fd099..f4b38da 100644 --- a/src/views/customermanagement/CustomerView.vue +++ b/src/views/customermanagement/CustomerView.vue @@ -646,7 +646,13 @@ onMounted(() => { fetchSelectCustoTypes(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertCustomer"); form[CustomerFields.NUMBER] = generateSnowflakeId({ diff --git a/src/views/customermanagement/VipLevelView.vue b/src/views/customermanagement/VipLevelView.vue index 8289ae5..4d2523c 100644 --- a/src/views/customermanagement/VipLevelView.vue +++ b/src/views/customermanagement/VipLevelView.vue @@ -463,7 +463,13 @@ onMounted(() => { fetchSelectCustomerTypes(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertVipRule"); form[VipRuleFields.NUMBER] = generateSnowflakeId({ diff --git a/src/views/finance/InternalFinanceView.vue b/src/views/finance/InternalFinanceView.vue index c3eab5d..add347e 100644 --- a/src/views/finance/InternalFinanceView.vue +++ b/src/views/finance/InternalFinanceView.vue @@ -593,7 +593,13 @@ const handleFilterReset = (vals) => { fetchInternalfinanceData(); }; +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertInternalFinance"); form[InternalFinanceFields.NUMBER] = generateSnowflakeId({ diff --git a/src/views/humanresourcemanagement/StaffManagementView.vue b/src/views/humanresourcemanagement/StaffManagementView.vue index 1359b78..4b81ccd 100644 --- a/src/views/humanresourcemanagement/StaffManagementView.vue +++ b/src/views/humanresourcemanagement/StaffManagementView.vue @@ -896,7 +896,13 @@ onMounted(() => { queryAddress.cancel(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertStaff"); form[EmployeeFields.NUMBER] = generateSnowflakeId({ diff --git a/src/views/materialmanagement/GoodsmanagementView.vue b/src/views/materialmanagement/GoodsmanagementView.vue index 43cc47e..4a39c10 100644 --- a/src/views/materialmanagement/GoodsmanagementView.vue +++ b/src/views/materialmanagement/GoodsmanagementView.vue @@ -430,7 +430,13 @@ onMounted(() => { fetchGoodsData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertGoods"); form[GoodsFields.NAME] = ""; diff --git a/src/views/roominformation/ReserManagementView.vue b/src/views/roominformation/ReserManagementView.vue index 0cae28c..cee3b04 100644 --- a/src/views/roominformation/ReserManagementView.vue +++ b/src/views/roominformation/ReserManagementView.vue @@ -568,7 +568,13 @@ onMounted(() => { fetchReserTypeAll(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertReser"); form[ReserFields.NUMBER] = generateSnowflakeId({ diff --git a/src/views/roominformation/RoomConfigView.vue b/src/views/roominformation/RoomConfigView.vue index 984633f..61cd78c 100644 --- a/src/views/roominformation/RoomConfigView.vue +++ b/src/views/roominformation/RoomConfigView.vue @@ -428,7 +428,13 @@ onMounted(() => { fetchRoomConfigData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertRoomConfig"); form[RoomConfigFields.NO] = null; diff --git a/src/views/roominformation/RoomManagementView.vue b/src/views/roominformation/RoomManagementView.vue index b06c0e2..a46f233 100644 --- a/src/views/roominformation/RoomManagementView.vue +++ b/src/views/roominformation/RoomManagementView.vue @@ -485,7 +485,13 @@ onMounted(() => { fetchSelectRoomStates(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertRoom"); form[RoomFields.NO] = null; diff --git a/src/views/supervision/SupervisionView.vue b/src/views/supervision/SupervisionView.vue index 202f166..424ecd3 100644 --- a/src/views/supervision/SupervisionView.vue +++ b/src/views/supervision/SupervisionView.vue @@ -539,7 +539,13 @@ onMounted(() => { fetchSelectDepartments(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertSupervisionInfo"); Object.assign(form, initialFormValues); diff --git a/src/views/systemmanagement/AdminTypeManagementView.vue b/src/views/systemmanagement/AdminTypeManagementView.vue index e5948ba..5931397 100644 --- a/src/views/systemmanagement/AdminTypeManagementView.vue +++ b/src/views/systemmanagement/AdminTypeManagementView.vue @@ -453,7 +453,13 @@ onMounted(() => { fetchAdminTypeData(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertAdminType"); form[AdministratorTypeFields.NUMBER] = generateSnowflakeId({ diff --git a/src/views/systemmanagement/AdministratorManagementView.vue b/src/views/systemmanagement/AdministratorManagementView.vue index ef2be54..4c16aca 100644 --- a/src/views/systemmanagement/AdministratorManagementView.vue +++ b/src/views/systemmanagement/AdministratorManagementView.vue @@ -551,7 +551,13 @@ onMounted(() => { Promise.all([fetchAdministratorData(), fetchSelectAdminTypes()]); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertAdministrator"); form[AdministratorFields.NUMBER] = generateSnowflakeId({ diff --git a/src/views/systemmanagement/MenuManagementView.vue b/src/views/systemmanagement/MenuManagementView.vue index 09b769e..d0f4778 100644 --- a/src/views/systemmanagement/MenuManagementView.vue +++ b/src/views/systemmanagement/MenuManagementView.vue @@ -486,7 +486,13 @@ onMounted(() => { fetchSelectMenus(); }); +const resetModalForm = () => { + formRef.value?.resetFields(); + Object.assign(form, { ...initialFormValues }); +}; + const showModal = () => { + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertMenu"); form[MenuFields.KEY] = ""; diff --git a/src/views/systemmanagement/RoleManagementView.vue b/src/views/systemmanagement/RoleManagementView.vue index b5341db..d7eb7cb 100644 --- a/src/views/systemmanagement/RoleManagementView.vue +++ b/src/views/systemmanagement/RoleManagementView.vue @@ -546,13 +546,13 @@ onMounted(() => { fetchRoleData(); }); -const resetRoleModalForm = () => { +const resetModalForm = () => { formRef.value?.resetFields(); Object.assign(form, { ...initialFormValues }); }; const showModal = () => { - resetRoleModalForm(); + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertRole"); form[RoleFields.NUMBER] = generateSnowflakeId({ -- Gitee From 502c7eaceea98b937cf675422bb93812df770833 Mon Sep 17 00:00:00 2001 From: ck_yeun9 Date: Fri, 20 Feb 2026 16:51:01 +0800 Subject: [PATCH 03/20] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=87=8D=E7=BD=AE?= =?UTF-8?q?=E5=BF=85=E5=A1=AB=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/roominformation/RoomConfigView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/roominformation/RoomConfigView.vue b/src/views/roominformation/RoomConfigView.vue index 61cd78c..7eb2521 100644 --- a/src/views/roominformation/RoomConfigView.vue +++ b/src/views/roominformation/RoomConfigView.vue @@ -209,7 +209,7 @@ const modalVisible = ref(false); const viewModalVisible = ref(false); const modalTitle = ref(""); const confirmLoading = ref(false); -const formRef = ref([]); +const formRef = ref(null); const sortedInfo = ref({ order: null, columnKey: null }); const filterValues = ref({}); const selectedRecord = ref(null); -- Gitee From b307d0ec593524a08aefec874f95e72439c2737d Mon Sep 17 00:00:00 2001 From: ck_yeun9 Date: Sat, 21 Feb 2026 14:09:51 +0800 Subject: [PATCH 04/20] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=88=BF?= =?UTF-8?q?=E9=97=B4=E7=8A=B6=E6=80=81=E5=80=BC=E5=92=8C=E9=80=9A=E7=94=A8?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=A1=86=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=88=BF=E9=97=B4=E4=BF=A1=E6=81=AF=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E5=92=8C=E9=A2=84=E7=BA=A6=E8=A1=A8=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components.d.ts | 3 + src/components/common/GenericDialog.vue | 117 +++++++ src/entities/room.entity.js | 8 + src/i18n.js | 10 +- src/utils/icons.js | 2 + src/views/roominformation/ReservationForm.vue | 165 ++++++++++ src/views/roominformation/RoomMapView.vue | 289 ++++++++++++++---- 7 files changed, 528 insertions(+), 66 deletions(-) create mode 100644 src/components/common/GenericDialog.vue create mode 100644 src/views/roominformation/ReservationForm.vue diff --git a/components.d.ts b/components.d.ts index 21897ce..2d573bc 100644 --- a/components.d.ts +++ b/components.d.ts @@ -17,6 +17,7 @@ declare module 'vue' { ACol: typeof import('ant-design-vue/es')['Col'] ACollapse: typeof import('ant-design-vue/es')['Collapse'] ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel'] + ActionFormModal: typeof import('./src/components/common/ActionFormModal.vue')['default'] ADatePicker: typeof import('ant-design-vue/es')['DatePicker'] ADescriptions: typeof import('ant-design-vue/es')['Descriptions'] ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem'] @@ -72,7 +73,9 @@ declare module 'vue' { CarryOutOutlined: typeof import('@ant-design/icons-vue')['CarryOutOutlined'] DeleteOutlined: typeof import('@ant-design/icons-vue')['DeleteOutlined'] EditOutlined: typeof import('@ant-design/icons-vue')['EditOutlined'] + ExclamationCircleOutlined: typeof import('@ant-design/icons-vue')['ExclamationCircleOutlined'] EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined'] + GenericDialog: typeof import('./src/components/common/GenericDialog.vue')['default'] GlobalNotification: typeof import('./src/components/GlobalNotification.vue')['default'] GlobalOutlined: typeof import('@ant-design/icons-vue')['GlobalOutlined'] HomeOutlined: typeof import('@ant-design/icons-vue')['HomeOutlined'] diff --git a/src/components/common/GenericDialog.vue b/src/components/common/GenericDialog.vue new file mode 100644 index 0000000..115530d --- /dev/null +++ b/src/components/common/GenericDialog.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/src/entities/room.entity.js b/src/entities/room.entity.js index 34054c3..84b4220 100644 --- a/src/entities/room.entity.js +++ b/src/entities/room.entity.js @@ -27,6 +27,14 @@ export const RoomStateMap = { reserved: "预约", }; +export const RoomStateValues = { + VACANT: "空房", + OCCUPIED: "已住", + MAINTENANCE: "维修", + DIRTY: "脏房", + RESERVED: "预约", +}; + export const RoomStateColors = { 空房: "#48a54b", 已住: "#1f8de5", diff --git a/src/i18n.js b/src/i18n.js index bba2efc..b8aef1f 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -691,7 +691,7 @@ const messages = { normal: "INFO", error: "CRITICAL", warning: "WARNING", - reserve: "Reserve", + reserve: "Reserve Room", checkIn: "Check In", transferRoom: "Transfer Room", checkOut: "Check Out", @@ -1407,10 +1407,10 @@ const messages = { normal: "常规操作", error: "严重操作", warning: "敏感操作", - reserve: "预约", - checkIn: "入住", - transferRoom: "转房", - checkOut: "退房", + reserve: "预约房间", + checkIn: "办理入住", + transferRoom: "转换房间", + checkOut: "结算退房", changeRoomState: "更改房间状态", viewCustomerInfo: "查看客户信息", diff --git a/src/utils/icons.js b/src/utils/icons.js index 1db31c1..4e2bed3 100644 --- a/src/utils/icons.js +++ b/src/utils/icons.js @@ -53,6 +53,7 @@ import { ImportOutlined, SwapOutlined, InteractionOutlined, + ExclamationCircleOutlined, } from "@ant-design/icons-vue"; export function registerAntdIcons(app) { @@ -109,4 +110,5 @@ export function registerAntdIcons(app) { app.component("ImportOutlined", ImportOutlined); app.component("SwapOutlined", SwapOutlined); app.component("InteractionOutlined", InteractionOutlined); + app.component("ExclamationCircleOutlined", ExclamationCircleOutlined); } diff --git a/src/views/roominformation/ReservationForm.vue b/src/views/roominformation/ReservationForm.vue new file mode 100644 index 0000000..5c0236e --- /dev/null +++ b/src/views/roominformation/ReservationForm.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/src/views/roominformation/RoomMapView.vue b/src/views/roominformation/RoomMapView.vue index 2005aed..5e642a7 100644 --- a/src/views/roominformation/RoomMapView.vue +++ b/src/views/roominformation/RoomMapView.vue @@ -14,54 +14,57 @@ :key="room[RoomFields.NO]" class="room-col" > - - - - +

+ {{ $t("message.roomNo") }}: {{ room[RoomFields.NO] }} +

+

+ {{ $t("message.custoName") }}: + {{ room[RoomFields.CUSTOMER_NAME] || "" }} +

+

+ {{ $t("message.daysofStay") }}: + {{ calculateDaysLived(room[RoomFields.CHECK_IN_TIME]) }} +

+
@@ -86,38 +89,76 @@ > + + + + + + + diff --git a/src/views/roominformation/RoomMapView.vue b/src/views/roominformation/RoomMapView.vue index 5e642a7..55f66c8 100644 --- a/src/views/roominformation/RoomMapView.vue +++ b/src/views/roominformation/RoomMapView.vue @@ -171,6 +171,7 @@ import { useI18n } from "vue-i18n"; import dayjs from "dayjs"; import GenericDialog from "@/components/common/GenericDialog.vue"; import ReservationForm from "@/views/roominformation/ReservationForm.vue"; +import CheckInForm from "@/views/roominformation/CheckInForm.vue"; const { t } = useI18n(); const route = useRoute(); @@ -323,8 +324,8 @@ const handleMenuClick = ({ key }) => { case "checkin": openDialog( t("message.checkIn"), - 700, - null, + 600, + CheckInForm, { room }, { showOk: true, showCancel: true }, ); @@ -332,7 +333,7 @@ const handleMenuClick = ({ key }) => { case "transfer": openDialog( t("message.transferRoom"), - 650, + 600, null, { room }, { showOk: true, showCancel: true }, @@ -350,7 +351,7 @@ const handleMenuClick = ({ key }) => { case "changeState": openDialog( t("message.changeRoomState"), - 500, + 600, null, { room }, { showOk: true, showCancel: true, showFooter: true }, -- Gitee From 44de52631e4d6380b77aabff83cd0fb05094c703 Mon Sep 17 00:00:00 2001 From: ck_yeun9 Date: Sun, 22 Feb 2026 18:31:35 +0800 Subject: [PATCH 06/20] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=A9=BA=E6=88=BF?= =?UTF-8?q?=E5=85=A5=E4=BD=8F=E3=80=81=E9=A2=84=E7=BA=A6=E6=88=BF=E5=85=A5?= =?UTF-8?q?=E4=BD=8F=E3=80=81=E9=A2=84=E7=BA=A6=E3=80=81=E6=9F=A5=E7=9C=8B?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E4=BF=A1=E6=81=AF=E6=93=8D=E4=BD=9C=E7=9A=84?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91=E4=BB=A3=E7=A0=81=E3=80=82?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=E9=83=A8=E5=88=86=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=8F=B0=E8=AD=A6=E5=91=8A=E3=80=82=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=88=BF=E6=80=81=E5=9B=BE=E6=93=8D=E4=BD=9C=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/baseapi.js | 5 +- src/api/customerapi.js | 15 + src/api/reserapi.js | 13 + src/api/roomapi.js | 39 ++ src/config/apiRoutes.js | 5 + src/entities/reser.entity.js | 8 + src/entities/room.entity.js | 11 + src/i18n.js | 9 + src/views/customermanagement/CustomerView.vue | 125 +++++- src/views/roominformation/CheckInForm.vue | 56 ++- src/views/roominformation/CheckoutForm.vue | 123 ++++++ src/views/roominformation/ReservationForm.vue | 2 +- src/views/roominformation/RoomMapView.vue | 392 ++++++++++++++++-- .../roominformation/TransferRoomForm.vue | 120 ++++++ 14 files changed, 855 insertions(+), 68 deletions(-) create mode 100644 src/views/roominformation/CheckoutForm.vue create mode 100644 src/views/roominformation/TransferRoomForm.vue diff --git a/src/api/baseapi.js b/src/api/baseapi.js index 8744511..566ff17 100644 --- a/src/api/baseapi.js +++ b/src/api/baseapi.js @@ -4,7 +4,6 @@ import { handleApiError, handleHttpError } from "@/common/errorHandler"; import { ERROR_CODES } from "@/common/errorCodes"; import { getStoredToken } from "@/utils/tokenStorage"; import i18n from "@/i18n"; - const ACTION_PREFIX_REGEX = /^(Select|GetAll|Get|Build|Read|Insert|Add|Create|Update|Upd|Delete|Del)/i; const SUFFIX_CLEANUP_REGEX = /(AllCanUse|CanUseAll|All|List)$/i; @@ -785,8 +784,8 @@ const toBusinessErrorPayload = (error) => { api.interceptors.request.use( async (config) => { - ROW_VERSION_CONFLICT_MESSAGE = i18n.global.t("concurrencyConflict"); - ROW_VERSION_CONFLICT_HINT = i18n.global.t("concurrencyConflict"); + ROW_VERSION_CONFLICT_MESSAGE = i18n.global.t("message.concurrencyConflict"); + ROW_VERSION_CONFLICT_HINT = ROW_VERSION_CONFLICT_MESSAGE; const token = getStoredToken(); if (!token && rowVersionCache.size > 0) { clearRowVersionCache(); diff --git a/src/api/customerapi.js b/src/api/customerapi.js index 5cf821d..6b0c84d 100644 --- a/src/api/customerapi.js +++ b/src/api/customerapi.js @@ -54,3 +54,18 @@ export const deleteCustomer = async (data) => { throw error; } }; + +// 查询指定客户信息 +export const fetchCustomerByInfo = async (params) => { + try { + const response = await api.get( + API_ENDPOINTS.routes.CUSTOMER_SELECT_CUSTOMER_BY_INFO, + { + params, + }, + ); + return response.data.Data; + } catch (error) { + throw error; + } +}; diff --git a/src/api/reserapi.js b/src/api/reserapi.js index 945322a..dd22c3f 100644 --- a/src/api/reserapi.js +++ b/src/api/reserapi.js @@ -67,3 +67,16 @@ export const fetchReserTypes = async (params) => { throw error; } }; + +// 根据房间号获取预约信息 +export const fetchReserByRoom = async (params) => { + try { + const response = await api.get( + API_ENDPOINTS.routes.RESER_SELECT_RESER_INFO_BY_ROOM_NO, + { params }, + ); + return response.data.Data; + } catch (error) { + throw error; + } +}; diff --git a/src/api/roomapi.js b/src/api/roomapi.js index 4818ea6..7ede404 100644 --- a/src/api/roomapi.js +++ b/src/api/roomapi.js @@ -66,3 +66,42 @@ export const deleteRoom = async (data) => { throw error; } }; + +// 退房 +export const checkoutRoom = async (data) => { + try { + const response = await api.post( + API_ENDPOINTS.routes.ROOM_CHECKOUT_ROOM, + data, + ); + return response.data; + } catch (error) { + throw error; + } +}; + +// 转房 +export const transferRoom = async (data) => { + try { + const response = await api.post( + API_ENDPOINTS.routes.ROOM_TRANSFER_ROOM, + data, + ); + return response.data; + } catch (error) { + throw error; + } +}; + +// 根据预订信息办理入住 +export const checkinRoomByReservation = async (data) => { + try { + const response = await api.post( + API_ENDPOINTS.routes.ROOM_CHECKIN_ROOM_BY_RESERVATION, + data, + ); + return response.data; + } catch (error) { + throw error; + } +}; diff --git a/src/config/apiRoutes.js b/src/config/apiRoutes.js index 19bb4fb..b712f2a 100644 --- a/src/config/apiRoutes.js +++ b/src/config/apiRoutes.js @@ -56,6 +56,7 @@ export const FLAT_API_ROUTES = Object.freeze({ BASE_UPDATE_PASS_PORT_TYPE: "/Base/UpdatePassPortType", CUSTOMER_DEL_CUSTOMER_INFO: "/Customer/DelCustomerInfo", CUSTOMER_INSERT_CUSTOMER_INFO: "/Customer/InsertCustomerInfo", + CUSTOMER_SELECT_CUSTOMER_BY_INFO: "/Customer/SelectCustoByInfo", CUSTOMER_PERMISSION_ASSIGN_USER_PERMISSIONS: "/CustomerPermission/AssignUserPermissions", CUSTOMER_PERMISSION_ASSIGN_USER_ROLES: "/CustomerPermission/AssignUserRoles", @@ -120,6 +121,7 @@ export const FLAT_API_ROUTES = Object.freeze({ RESER_INSER_RESER_INFO: "/Reser/InserReserInfo", RESER_SELECT_RESER_ALL: "/Reser/SelectReserAll", RESER_SELECT_RESER_TYPE_ALL: "/Reser/SelectReserTypeAll", + RESER_SELECT_RESER_INFO_BY_ROOM_NO: "/Reser/SelectReserInfoByRoomNo", RESER_UPDATE_RESER_INFO: "/Reser/UpdateReserInfo", REWARD_PUNISHMENT_SELECT_ALL_REWARD_PUNISHMENT_BY_EMPLOYEE_ID: "/RewardPunishment/SelectAllRewardPunishmentByEmployeeId", @@ -134,6 +136,9 @@ export const FLAT_API_ROUTES = Object.freeze({ ROLE_UPDATE_ROLE: "/Role/UpdateRole", ROOM_DELETE_ROOM: "/Room/DeleteRoom", ROOM_INSERT_ROOM: "/Room/InsertRoom", + ROOM_CHECKOUT_ROOM: "/Room/CheckoutRoom", + ROOM_TRANSFER_ROOM: "/Room/TransferRoom", + ROOM_CHECKIN_ROOM_BY_RESERVATION: "/Room/CheckinRoomByReservation", ROOM_SELECT_CAN_USE_ROOM_ALL: "/Room/SelectCanUseRoomAll", ROOM_SELECT_ROOM_ALL: "/Room/SelectRoomAll", ROOM_TYPE_DELETE_ROOM_TYPE: "/RoomType/DeleteRoomType", diff --git a/src/entities/reser.entity.js b/src/entities/reser.entity.js index 2f78aa5..f961887 100644 --- a/src/entities/reser.entity.js +++ b/src/entities/reser.entity.js @@ -16,6 +16,14 @@ export const ReserFields = { ...BaseFields, }; +export const ReserChannelValues = { + OFFLINE: "Offline", + APP: "App", + APPLET: "Applet", + WEBSITE: "Website", + OTHER: "Other", +}; + export const initialFormValues = { ...BaseInitialValues, [ReserFields.NUMBER]: null, diff --git a/src/entities/room.entity.js b/src/entities/room.entity.js index 84b4220..dcee564 100644 --- a/src/entities/room.entity.js +++ b/src/entities/room.entity.js @@ -19,6 +19,17 @@ export const RoomFields = { ...BaseFields, }; +export const RoomOperationSpecificFields = { + ORIGINAL_ROOM_NUMBER: "OriginalRoomNumber", + TARGET_ROOM_NUMBER: "TargetRoomNumber", + WATER_USAGE: "WaterUsage", + ELECTRICITY_USAGE: "ElectricityUsage", +}; + +export const RoomOperationFields = { + ...RoomOperationSpecificFields, +}; + export const RoomStateMap = { vacant: "空房", occupied: "已住", diff --git a/src/i18n.js b/src/i18n.js index 0d02ab5..2661e72 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -30,6 +30,11 @@ const messages = { undefined: "Undefined", check: "Check", customerNotFound: "Customer information not found", + customerFound: "Customer information found", + checkinNeedCustomerRegistration: + "Please complete customer registration before check-in. Redirecting to Customer Management.", + reservationNotFoundForRoom: + "No available reservation was found for the selected room.", my: "My", passwordEditTip: "Do not change this field if you do not want to change your password.", @@ -787,6 +792,10 @@ const messages = { id: "编号", check: "检查", customerNotFound: "未找到客户信息", + customerFound: "客户信息已找到", + checkinNeedCustomerRegistration: + "请先完成客户信息注册再进行入住,系统将跳转至客户管理页面。", + reservationNotFoundForRoom: "未找到对应房间的有效预约信息。", my: "我的", passwordEditTip: "留空表示不修改密码", required: "请输入 {field}", diff --git a/src/views/customermanagement/CustomerView.vue b/src/views/customermanagement/CustomerView.vue index f4b38da..3241c4e 100644 --- a/src/views/customermanagement/CustomerView.vue +++ b/src/views/customermanagement/CustomerView.vue @@ -219,10 +219,10 @@ {{ formatDate(selectedRecord?.[CustomerFields.BIRTH_DATE]) }} - {{ selectedRecord?.[CustomerFields.TYPE] }} + {{ selectedRecord?.[CustomerFields.TYPE_NAME] }} - {{ selectedRecord?.[CustomerFields.PASSPORTTYPE] }} + {{ selectedRecord?.[CustomerFields.PASSPORTNAME] }} {{ selectedRecord?.[CustomerFields.ID_NUMBER] }} @@ -255,6 +255,8 @@ import { updateCustomer, deleteCustomer, } from "@/api/customerapi"; +import { checkinRoomByReservation } from "@/api/roomapi"; +import { fetchReserByRoom } from "@/api/reserapi"; import { fetchCardCode } from "@/api/utilityapi"; import { debounce } from "lodash-es"; import { @@ -264,6 +266,8 @@ import { getColumns, getFormRules, } from "@/entities/customer.entity"; +import { RoomFields } from "@/entities/room.entity"; +import { ReserFields } from "@/entities/reser.entity"; import { CustomerTypeFields } from "@/entities/customertype.entity"; import { PassportFields } from "@/entities/passport.entity"; import { fetchCanUsePassports } from "@/api/passportapi"; @@ -317,6 +321,26 @@ const customerTelLabel = computed(() => t("message.customerTel")); const customerBirthdayLabel = computed(() => t("message.customerBirth")); const customerAddressLabel = computed(() => t("message.customerAddress")); +const getQueryString = (value) => { + if (Array.isArray(value)) return String(value[0] || "").trim(); + return String(value || "").trim(); +}; + +const reservationCheckinContext = computed(() => { + if (getQueryString(route.query.flow) !== "reservation-checkin") { + return null; + } + return { + roomNumber: getQueryString(route.query.roomNumber), + reservationId: getQueryString(route.query.reservationId), + reservationCustomerName: getQueryString( + route.query.reservationCustomerName, + ), + reservationPhoneNumber: getQueryString(route.query.reservationPhoneNumber), + returnPath: getQueryString(route.query.returnPath) || "/roommap", + }; +}); + const customerFilterConfig = computed(() => getCustomerFilterConfig(t, customerTypeOptions.value), ); @@ -640,10 +664,39 @@ const fetchSelectCustoTypes = async () => { } }; +const extractReservationFromResponse = (response) => { + if (!response) return null; + if (Array.isArray(response.Items)) return response.Items[0] || null; + if (Array.isArray(response)) return response[0] || null; + return response; +}; + +const resolveReservationIdByRoom = async (roomNumber) => { + if (!roomNumber) return ""; + try { + const reservationResponse = await fetchReserByRoom({ + [ReserFields.ROOMNUMBER]: roomNumber, + [ReserFields.IS_DELETED]: 0, + }); + const reservation = extractReservationFromResponse(reservationResponse); + return reservation?.[ReserFields.NUMBER] || ""; + } catch (error) { + showErrorNotification(error.message || t("message.pleaseTryAgainLater")); + return ""; + } +}; + onMounted(() => { fetchCustomerData(); fetchSelectPassports(); fetchSelectCustoTypes(); + if (reservationCheckinContext.value?.roomNumber) { + showErrorNotification(t("message.checkinNeedCustomerRegistration")); + showModal({ + customerName: reservationCheckinContext.value.reservationCustomerName, + phoneNumber: reservationCheckinContext.value.reservationPhoneNumber, + }); + } }); const resetModalForm = () => { @@ -651,7 +704,10 @@ const resetModalForm = () => { Object.assign(form, { ...initialFormValues }); }; -const showModal = () => { +const showModal = (prefill = null) => { + const prefillCustomerName = String(prefill?.customerName || "").trim(); + const prefillPhoneNumber = String(prefill?.phoneNumber || "").trim(); + resetModalForm(); modalVisible.value = true; modalTitle.value = t("message.insertCustomer"); @@ -659,13 +715,13 @@ const showModal = () => { prefix: "TS-", separator: null, }); - form[CustomerFields.NAME] = ""; + form[CustomerFields.NAME] = prefillCustomerName; form[CustomerFields.GENDER] = null; form[CustomerFields.BIRTH_DATE] = null; form[CustomerFields.TYPE] = null; form[CustomerFields.PASSPORTTYPE] = null; form[CustomerFields.ID_NUMBER] = ""; - form[CustomerFields.PHONE] = ""; + form[CustomerFields.PHONE] = prefillPhoneNumber; form[CustomerFields.ADDRESS] = ""; form[CustomerFields.MODIFYSTATUS] = "insert"; @@ -706,12 +762,57 @@ const handleModalOk = async () => { try { await formRef.value.validate(); confirmLoading.value = true; + const birthDate = form[CustomerFields.BIRTH_DATE] + ? form[CustomerFields.BIRTH_DATE].format("YYYY-MM-DD") + : null; + + const isReservationCheckinInsertFlow = + form[CustomerFields.MODIFYSTATUS] !== "update" && + !!reservationCheckinContext.value?.roomNumber; + + if (isReservationCheckinInsertFlow) { + const { roomNumber, returnPath } = reservationCheckinContext.value; + const reservationId = + reservationCheckinContext.value.reservationId || + (await resolveReservationIdByRoom(roomNumber)); + + if (!reservationId) { + showErrorNotification(t("message.reservationNotFoundForRoom")); + return; + } + + const response = await checkinRoomByReservation({ + [CustomerFields.NUMBER]: (form[CustomerFields.NUMBER] || "").trim(), + [CustomerFields.NAME]: form[CustomerFields.NAME], + [CustomerFields.GENDER]: form[CustomerFields.GENDER], + [CustomerFields.PASSPORTTYPE]: form[CustomerFields.PASSPORTTYPE], + [CustomerFields.PHONE]: form[CustomerFields.PHONE], + [CustomerFields.BIRTH_DATE]: birthDate, + [CustomerFields.ID_NUMBER]: form[CustomerFields.ID_NUMBER], + [CustomerFields.ADDRESS]: form[CustomerFields.ADDRESS], + [CustomerFields.TYPE]: form[CustomerFields.TYPE], + [RoomFields.NO]: roomNumber, + [ReserFields.NUMBER]: reservationId, + }); + + if (response && response.Success) { + showSuccessNotification(t("message.operationSuccess")); + modalVisible.value = false; + clearSelection(); + fetchCustomerData(); + await router.replace(returnPath || "/roommap"); + } else { + showErrorNotification( + response?.Message || t("message.pleaseTryAgainLater"), + ); + } + return; + } + if (form[CustomerFields.MODIFYSTATUS] === "update") { - var response = await updateCustomer({ + const response = await updateCustomer({ ...form, - [CustomerFields.BIRTH_DATE]: form[CustomerFields.BIRTH_DATE] - ? form[CustomerFields.BIRTH_DATE].format("YYYY-MM-DD") - : null, + [CustomerFields.BIRTH_DATE]: birthDate, }); if (response && response.Success) { showSuccessNotification(t("message.updateSuccess")); @@ -719,11 +820,9 @@ const handleModalOk = async () => { showErrorNotification(t("message.pleaseTryAgainLater")); } } else { - var response = await addCustomer({ + const response = await addCustomer({ ...form, - [CustomerFields.BIRTH_DATE]: form[CustomerFields.BIRTH_DATE] - ? form[CustomerFields.BIRTH_DATE].format("YYYY-MM-DD") - : null, + [CustomerFields.BIRTH_DATE]: birthDate, }); if (response && response.Success) { showSuccessNotification(t("message.addSuccess")); diff --git a/src/views/roominformation/CheckInForm.vue b/src/views/roominformation/CheckInForm.vue index 932ac80..a99fe82 100644 --- a/src/views/roominformation/CheckInForm.vue +++ b/src/views/roominformation/CheckInForm.vue @@ -52,6 +52,14 @@ :disabled="true" /> + + + + @@ -60,9 +68,9 @@ import { ref, reactive, watch } from "vue"; import { useI18n } from "vue-i18n"; import dayjs from "dayjs"; import { RoomFields } from "@/entities/room.entity"; -import { fetchCustomers } from "@/api/customerapi"; import { CustomerFields } from "@/entities/customer.entity"; import { showErrorNotification, showSuccessNotification } from "@/utils/index"; +import { fetchCustomerByInfo } from "../../api/customerapi"; const props = defineProps({ room: { type: Object, default: () => ({}) }, @@ -76,6 +84,7 @@ const form = reactive({ [RoomFields.CHECK_IN_TIME]: dayjs(), [RoomFields.RENT]: props.room?.[RoomFields.RENT] || 0, [RoomFields.DEPOSIT]: props.room?.[RoomFields.DEPOSIT] || 0, + [RoomFields.POSITION]: props.room?.[RoomFields.POSITION] || "", }); const rules = { @@ -95,6 +104,7 @@ watch( (r) => { form[RoomFields.RENT] = r?.[RoomFields.RENT] || 0; form[RoomFields.DEPOSIT] = r?.[RoomFields.DEPOSIT] || 0; + form[RoomFields.POSITION] = r?.[RoomFields.POSITION] || ""; // 清除验证错误并重置可编辑字段 form[CustomerFields.NUMBER] = ""; form[RoomFields.CHECK_IN_TIME] = dayjs(); @@ -105,6 +115,14 @@ watch( { immediate: true, deep: true }, ); +watch( + () => form[CustomerFields.NUMBER], + () => { + customerChecked.value = null; + foundCustomer.value = null; + }, +); + const checkCustomer = async () => { const code = (form[CustomerFields.NUMBER] || "").toString().trim(); if (!code) { @@ -116,15 +134,13 @@ const checkCustomer = async () => { customerChecked.value = null; foundCustomer.value = null; try { - const res = await fetchCustomers({ - [CustomerFields.PAGE]: 1, - [CustomerFields.PAGE_SIZE]: 1, + const res = await fetchCustomerByInfo({ [CustomerFields.NUMBER]: code, [CustomerFields.IS_DELETED]: 0, }); - if (res?.Items && res.Items.length > 0) { - foundCustomer.value = res.Items[0]; + if (res) { + foundCustomer.value = res; customerChecked.value = true; showSuccessNotification(t("message.customerFound")); } else { @@ -140,6 +156,10 @@ const checkCustomer = async () => { const validate = async () => { try { await formRef.value.validate(); + if (!foundCustomer.value) { + showErrorNotification(t("message.customerNotFound")); + return false; + } return true; } catch { return false; @@ -148,14 +168,22 @@ const validate = async () => { const getFormData = () => { return { - roomId: props.room?.[RoomFields.ID], - customerNumber: form[CustomerFields.NUMBER], - customerId: foundCustomer.value?.[CustomerFields.ID] || null, - checkInTime: form[RoomFields.CHECK_IN_TIME] - ? form[RoomFields.CHECK_IN_TIME].toISOString() - : null, - rent: form[RoomFields.RENT], - deposit: form[RoomFields.DEPOSIT], + [CustomerFields.NUMBER]: (form[CustomerFields.NUMBER] || "").trim(), + [CustomerFields.NAME]: foundCustomer.value?.[CustomerFields.NAME] || "", + [CustomerFields.GENDER]: + foundCustomer.value?.[CustomerFields.GENDER] ?? null, + [CustomerFields.PASSPORTTYPE]: + foundCustomer.value?.[CustomerFields.PASSPORTTYPE] ?? null, + [CustomerFields.PHONE]: foundCustomer.value?.[CustomerFields.PHONE] || "", + [CustomerFields.BIRTH_DATE]: + foundCustomer.value?.[CustomerFields.BIRTH_DATE] || null, + [CustomerFields.ID_NUMBER]: + foundCustomer.value?.[CustomerFields.ID_NUMBER] || "", + [CustomerFields.ADDRESS]: + foundCustomer.value?.[CustomerFields.ADDRESS] || "", + [CustomerFields.TYPE]: foundCustomer.value?.[CustomerFields.TYPE] ?? null, + [RoomFields.NO]: props.room?.[RoomFields.NO] || "", + [RoomFields.CHECK_IN_TIME]: form[RoomFields.CHECK_IN_TIME] || null, }; }; diff --git a/src/views/roominformation/CheckoutForm.vue b/src/views/roominformation/CheckoutForm.vue new file mode 100644 index 0000000..c6a1264 --- /dev/null +++ b/src/views/roominformation/CheckoutForm.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/src/views/roominformation/ReservationForm.vue b/src/views/roominformation/ReservationForm.vue index 5c0236e..ffb2df2 100644 --- a/src/views/roominformation/ReservationForm.vue +++ b/src/views/roominformation/ReservationForm.vue @@ -117,7 +117,7 @@ watch( form[ReserFields.PHONENUMBER] = ""; form[ReserFields.ENDDATE] = null; // 自动填充字段 - form[ReserFields.ROOMNUMBER] = newRoom[RoomFields.NO] || newRoom.No || ""; + form[ReserFields.ROOMNUMBER] = newRoom[RoomFields.NO] || ""; form[ReserFields.NUMBER] = generateReservationNumber(); form[ReserFields.STARTDATE] = dayjs().format("YYYY-MM-DD"); // 清除验证错误 diff --git a/src/views/roominformation/RoomMapView.vue b/src/views/roominformation/RoomMapView.vue index 55f66c8..781bde4 100644 --- a/src/views/roominformation/RoomMapView.vue +++ b/src/views/roominformation/RoomMapView.vue @@ -1,4 +1,4 @@ - @@ -45,7 +45,8 @@ const formRef = ref(null); const form = reactive({ [RoomOperationFields.ORIGINAL_ROOM_NUMBER]: props.room?.[RoomFields.NO] || "", [RoomOperationFields.TARGET_ROOM_NUMBER]: "", - [CustomerFields.NUMBER]: "", + [RoomOperationFields.CURRENT_CUSTOMER_NUMBER]: + props.room?.[CustomerFields.NUMBER] || "", }); const targetRoomOptions = computed(() => @@ -56,10 +57,14 @@ const targetRoomOptions = computed(() => form[RoomOperationFields.ORIGINAL_ROOM_NUMBER] && item?.[RoomFields.STATE] === RoomStateValues.VACANT, ) - .map((item) => ({ - label: item?.[RoomFields.NO], - value: item?.[RoomFields.NO], - })), + .map((item) => { + const roomNo = item?.[RoomFields.NO] || ""; + const roomType = item?.[RoomFields.NAME]; + return { + label: roomType ? `${roomNo}(${roomType})` : roomNo, + value: roomNo, + }; + }), ); const rules = { @@ -85,7 +90,7 @@ watch( form[RoomOperationFields.ORIGINAL_ROOM_NUMBER] = room?.[RoomFields.NO] || ""; form[RoomOperationFields.TARGET_ROOM_NUMBER] = ""; - form[CustomerFields.NUMBER] = ""; + form[CustomerFields.NUMBER] = room?.[CustomerFields.NUMBER] || ""; formRef.value?.clearValidate?.(); }, { immediate: true, deep: true }, -- Gitee From eb8087901de2f20b24583d369efed9c0d2359d7c Mon Sep 17 00:00:00 2001 From: ck_yeun9 Date: Sat, 28 Feb 2026 15:15:26 +0800 Subject: [PATCH 08/20] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E4=B8=8E=E5=90=8E=E7=AB=AF=E7=9A=84=E5=94=AF=E4=B8=80=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E7=94=9F=E6=88=90=E6=96=B9=E6=B3=95=E3=80=82=20?= =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E8=AF=A6=E6=83=85=E9=A1=B5=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=AE=A1=E8=AE=A1=E5=AD=97=E6=AE=B5=E6=9F=A5=E7=9C=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 7 - package.json | 1 - src/api/customertypeapi.js | 15 + src/config/apiRoutes.js | 1 + src/config/dateFields.js | 7 +- src/entities/common.entity.js | 5 + src/i18n.js | 30 ++ src/utils/snowflake.js | 110 ++++- src/views/base/DepartmentView.vue | 24 + src/views/base/NationView.vue | 30 +- src/views/base/NoticeTypeView.vue | 34 +- src/views/base/PassportView.vue | 30 +- src/views/base/PositionView.vue | 30 +- src/views/base/PromotionContentView.vue | 42 +- src/views/base/QualificationView.vue | 34 +- src/views/roominformation/CheckoutForm.vue | 506 +++++++++++++++++++-- src/views/roominformation/RoomMapView.vue | 18 +- 17 files changed, 853 insertions(+), 71 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35efb47..79e2686 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "name": "topsky-hotel-management-system-vue3", "version": "1.0.0", "dependencies": { - "@akashrajpurohit/snowflake-id": "^2.0.0", "@vueuse/core": "^13.1.0", "ant-design-vue": "^4.2.6", "axios": "^1.13.2", @@ -37,12 +36,6 @@ "vite-plugin-commonjs": "^0.10.4" } }, - "node_modules/@akashrajpurohit/snowflake-id": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/@akashrajpurohit/snowflake-id/-/snowflake-id-2.0.0.tgz", - "integrity": "sha512-9En2OKHBOO39vztHUxHUh/Xh6wTI1lEQ9c0ivr7QX3ozaKgs770TRJrgtBbQBeLLbMoLGG3fBk3otAlSY440pw==", - "license": "MIT" - }, "node_modules/@ant-design/colors": { "version": "6.0.0", "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-6.0.0.tgz", diff --git a/package.json b/package.json index 1af3b10..7ca8311 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "format": "prettier --write ." }, "dependencies": { - "@akashrajpurohit/snowflake-id": "^2.0.0", "@vueuse/core": "^13.1.0", "ant-design-vue": "^4.2.6", "axios": "^1.13.2", diff --git a/src/api/customertypeapi.js b/src/api/customertypeapi.js index 9e184d1..83fd682 100644 --- a/src/api/customertypeapi.js +++ b/src/api/customertypeapi.js @@ -54,3 +54,18 @@ export const deleteCustomerType = async (data) => { throw error; } }; + +// 查询客户类型详情 +export const fetchCustomerTypeDetails = async (params) => { + try { + const response = await api.get( + API_ENDPOINTS.routes.BASE_SELECT_CUSTO_TYPE_BY_TYPE_ID, + { + params, + }, + ); + return response.data.Data; + } catch (error) { + throw error; + } +}; diff --git a/src/config/apiRoutes.js b/src/config/apiRoutes.js index b712f2a..34c8c6f 100644 --- a/src/config/apiRoutes.js +++ b/src/config/apiRoutes.js @@ -32,6 +32,7 @@ export const FLAT_API_ROUTES = Object.freeze({ BASE_DEL_POSITION: "/Base/DelPosition", BASE_DELETE_APPOINTMENT_NOTICE_TYPE: "/Base/DeleteAppointmentNoticeType", BASE_DELETE_CUSTO_TYPE: "/Base/DeleteCustoType", + BASE_SELECT_CUSTO_TYPE_BY_TYPE_ID: "/Base/SelectCustoTypeByTypeId", BASE_DELETE_PASS_PORT_TYPE: "/Base/DeletePassPortType", BASE_INSERT_CUSTO_TYPE: "/Base/InsertCustoType", BASE_INSERT_PASS_PORT_TYPE: "/Base/InsertPassPortType", diff --git a/src/config/dateFields.js b/src/config/dateFields.js index 682a28e..63c6b5a 100644 --- a/src/config/dateFields.js +++ b/src/config/dateFields.js @@ -1,9 +1,14 @@ import { EmployeeFields } from "@/entities/employee.entity"; import { LogFields } from "@/entities/log.entity"; import { SpendInfoFields } from "@/entities/spendinfo.entity"; +import { RoomFields } from "@/entities/room.entity"; export const dateFieldConfig = { WITH_TIME: [LogFields.TIME, SpendInfoFields.TIME], - WITHOUT_TIME: [EmployeeFields.DATEOFBITRH, EmployeeFields.HIREDATE], + WITHOUT_TIME: [ + EmployeeFields.DATEOFBITRH, + EmployeeFields.HIREDATE, + RoomFields.CHECK_IN_TIME, + ], }; diff --git a/src/entities/common.entity.js b/src/entities/common.entity.js index c0fc0e1..e99409a 100644 --- a/src/entities/common.entity.js +++ b/src/entities/common.entity.js @@ -8,6 +8,11 @@ export const BaseFields = { PAGE_SIZE: "PageSize", ROWVERSION: "RowVersion", SELECTED: "selected", + + DATA_INS_USER: "DataInsUsr", + DATA_INS_DATE: "DataInsDate", + DATA_CHG_USER: "DataChgUsr", + DATA_CHG_DATE: "DataChgDate", }; export const BaseInitialValues = { diff --git a/src/i18n.js b/src/i18n.js index 2661e72..c6ea04d 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -759,9 +759,24 @@ const messages = { late: "Late", absent: "Absent", consumptionTime: "Consumption Time", + consumptionTotal: "Consumption Total", + payableAmount: "Payable Amount", + vipDiscount: "VIP Discount", + changeAmount: "Change Amount", + actualReceived: "Actual Received", + settle: "Settle", + discount: "Discount", + discountTextWithType: "{type} {offPercent}% off", + discountText: "{offPercent}% off", + discountTextNoDiscount: "No discount", pleaseInputPath: "Please input path", pleaseInputUserName: "Please input user name", + + createdBy: "Created By", + createdDate: "Created Date", + lastUpdatedBy: "Last Update By", + lastUpdatedDate: "Last Update Date", }, }, "zh-CN": { @@ -1481,6 +1496,16 @@ const messages = { late: "迟到", absent: "旷工", consumptionTime: "消费时间", + consumptionTotal: "消费总额", + payableAmount: "应付金额", + vipDiscount: "会员折扣", + changeAmount: "找零金额", + actualReceived: "实收金额", + settle: "结 算", + discount: "折", + discountTextWithType: "{type} {fold}折 (优惠{offPercent}%)", + discountText: "{fold}折 (优惠{offPercent}%)", + discountTextNoDiscount: "无折扣(10折)", pleaseInputPath: "请输入请求路径", pleaseInputUserName: "请输入用户名", @@ -1549,6 +1574,11 @@ const messages = { homeAdminDetailNotFound: "管理员详情接口未返回当前账号数据。", homeAdminProfileLoadFailed: "管理员信息加载失败", homeEnvironmentLoadFailed: "系统环境配置加载失败", + + createdBy: "创建人", + createdDate: "创建日期", + lastUpdatedBy: "更新人", + lastUpdatedDate: "更新日期", }, }, }; diff --git a/src/utils/snowflake.js b/src/utils/snowflake.js index cbfd1bc..69170bb 100644 --- a/src/utils/snowflake.js +++ b/src/utils/snowflake.js @@ -1,18 +1,106 @@ -import { SnowflakeId } from "@akashrajpurohit/snowflake-id"; +const dic = new Map(); +const MAX_BUCKET_COUNT = 10; +const MAX_SEQUENCE = 9999; -const getLocalTime = () => new Date().getTime(); -const START_STAMP = getLocalTime(); +const pad2 = (value) => String(value).padStart(2, "0"); -const snowflake = SnowflakeId({ - workerId: 1, - epoch: START_STAMP, -}); +const getTimeKey = () => { + const now = new Date(); + const year = now.getFullYear(); + const month = pad2(now.getMonth() + 1); + const day = pad2(now.getDate()); + const hour = pad2(now.getHours()); + const minute = pad2(now.getMinutes()); + return `${year}${month}${day}${hour}${minute}`; +}; -function generateSnowflakeId(options = {}) { - const { prefix = "", separator = "" } = options; - const id = snowflake.generate(); +const clearBucketsIfNeeded = () => { + if (dic.size > MAX_BUCKET_COUNT) { + dic.clear(); + } +}; + +const tryGetNextSequence = (timeKey) => { + if (!dic.has(timeKey)) { + clearBucketsIfNeeded(); + dic.set(timeKey, 1); + return 1; + } + + const current = dic.get(timeKey); + if (current >= MAX_SEQUENCE) { + return null; + } + + const next = current + 1; + dic.set(timeKey, next); + return next; +}; + +const waitUntilNextMinute = (timeKey) => { + // Keep the same behavior as backend Thread.Sleep(0) spin wait. + while (getTimeKey() === timeKey) { + const noop = Date.now(); + if (noop < 0) { + break; + } + } +}; + +const normalizePrefix = (preCode) => { + if (preCode === null || preCode === undefined) { + return ""; + } + return String(preCode); +}; + +export const getNewId = (preCode = "") => { + const prefix = normalizePrefix(preCode); + + while (true) { + const timeKey = getTimeKey(); + const sequence = tryGetNextSequence(timeKey); + + if (sequence === null) { + waitUntilNextMinute(timeKey); + continue; + } - return prefix + (separator ? separator : "") + id; + return `${prefix}${timeKey}${String(sequence).padStart(4, "0")}`; + } +}; + +export const getListNewId = (preCodeOrCount = "", pCount = 1) => { + let prefix = ""; + let count = pCount; + + if (typeof preCodeOrCount === "number") { + count = preCodeOrCount; + } else { + prefix = normalizePrefix(preCodeOrCount); + } + + const total = Number.isInteger(count) && count > 0 ? count : 1; + const list = []; + + while (list.length < total) { + const timeKey = getTimeKey(); + const sequence = tryGetNextSequence(timeKey); + + if (sequence === null) { + waitUntilNextMinute(timeKey); + continue; + } + + list.push(`${prefix}${timeKey}${String(sequence).padStart(4, "0")}`); + } + + return list; +}; + +function generateSnowflakeId(options = {}) { + const { prefix = "", separator = "" } = options || {}; + return getNewId(`${normalizePrefix(prefix)}${separator ? separator : ""}`); } export default generateSnowflakeId; diff --git a/src/views/base/DepartmentView.vue b/src/views/base/DepartmentView.vue index 87d67bb..ab341db 100644 --- a/src/views/base/DepartmentView.vue +++ b/src/views/base/DepartmentView.vue @@ -188,6 +188,22 @@ {{ formatDate(selectedRecord?.[DepartmentFields.CREATIONDATE]) }} + + @@ -465,6 +481,10 @@ const departmentDescLabel = computed(() => t("message.departmentDesc")); const departmentLeaderLabel = computed(() => t("message.departmentLeader")); const departmentParentLabel = computed(() => t("message.departmentParent")); const departmentDateLabel = computed(() => t("message.departmentDate")); +const createdByLabel = computed(() => t("message.createdBy")); +const createdDateLabel = computed(() => t("message.createdDate")); +const lastUpdatedByLabel = computed(() => t("message.lastUpdatedBy")); +const lastUpdatedDateLabel = computed(() => t("message.lastUpdatedDate")); const pagination = reactive({ current: 1, @@ -497,6 +517,10 @@ const fetchDepartmentData = async () => { [DepartmentFields.PARENTNAME]: item[DepartmentFields.PARENTNAME], [DepartmentFields.CREATIONDATE]: item[DepartmentFields.CREATIONDATE], [DepartmentFields.IS_DELETED]: item[DepartmentFields.IS_DELETED] ?? 0, + [DepartmentFields.DATA_INS_USER]: item[DepartmentFields.DATA_INS_USER], + [DepartmentFields.DATA_INS_DATE]: item[DepartmentFields.DATA_INS_DATE], + [DepartmentFields.DATA_CHG_USER]: item[DepartmentFields.DATA_CHG_USER], + [DepartmentFields.DATA_CHG_DATE]: item[DepartmentFields.DATA_CHG_DATE], [DepartmentFields.SELECTED]: false, })); pagination.total = result.TotalCount; diff --git a/src/views/base/NationView.vue b/src/views/base/NationView.vue index ebbfd16..1488834 100644 --- a/src/views/base/NationView.vue +++ b/src/views/base/NationView.vue @@ -139,6 +139,22 @@ {{ selectedRecord?.[NationFields.NAME] }} + + @@ -148,7 +164,11 @@ import { ref, onMounted, computed, reactive } from "vue"; import { useRoute } from "vue-router"; import { getPageTitle } from "@/utils/pageTitle"; -import { showErrorNotification, showSuccessNotification } from "@/utils/index"; +import { + showErrorNotification, + showSuccessNotification, + formatDateTime, +} from "@/utils/index"; import { fetchNations, addNation, @@ -350,6 +370,10 @@ const rules = getFormRules(t); const nationNoLabel = computed(() => t("message.nationNo")); const nationNameLabel = computed(() => t("message.nationName")); +const createdByLabel = computed(() => t("message.createdBy")); +const createdDateLabel = computed(() => t("message.createdDate")); +const lastUpdatedByLabel = computed(() => t("message.lastUpdatedBy")); +const lastUpdatedDateLabel = computed(() => t("message.lastUpdatedDate")); const pagination = reactive({ current: 1, @@ -376,6 +400,10 @@ const fetchNationData = async () => { [NationFields.NUMBER]: item[NationFields.NUMBER], [NationFields.NAME]: item[NationFields.NAME], [NationFields.IS_DELETED]: item[NationFields.IS_DELETED], + [NationFields.DATA_INS_USER]: item[NationFields.DATA_INS_USER], + [NationFields.DATA_INS_DATE]: item[NationFields.DATA_INS_DATE], + [NationFields.DATA_CHG_USER]: item[NationFields.DATA_CHG_USER], + [NationFields.DATA_CHG_DATE]: item[NationFields.DATA_CHG_DATE], [NationFields.SELECTED]: false, })); pagination.total = result.TotlalCount; diff --git a/src/views/base/NoticeTypeView.vue b/src/views/base/NoticeTypeView.vue index bc4fe70..7c41167 100644 --- a/src/views/base/NoticeTypeView.vue +++ b/src/views/base/NoticeTypeView.vue @@ -141,6 +141,26 @@ {{ selectedRecord?.[NoticeTypeFields.NAME] }} + + @@ -150,7 +170,11 @@ import { ref, onMounted, computed, reactive } from "vue"; import { useRoute } from "vue-router"; import { getPageTitle } from "@/utils/pageTitle"; -import { showErrorNotification, showSuccessNotification } from "@/utils/index"; +import { + showErrorNotification, + showSuccessNotification, + formatDateTime, +} from "@/utils/index"; import { fetchNoticeTypes, addNoticeType, @@ -354,6 +378,10 @@ const rules = getFormRules(t); const noticeTypeNoLabel = computed(() => t("message.noticeTypeNumber")); const noticeTypeNameLabel = computed(() => t("message.noticeTypeName")); +const createdByLabel = computed(() => t("message.createdBy")); +const createdDateLabel = computed(() => t("message.createdDate")); +const lastUpdatedByLabel = computed(() => t("message.lastUpdatedBy")); +const lastUpdatedDateLabel = computed(() => t("message.lastUpdatedDate")); const pagination = reactive({ current: 1, @@ -380,6 +408,10 @@ const fetchNoticeTypeData = async () => { [NoticeTypeFields.NUMBER]: item[NoticeTypeFields.NUMBER], [NoticeTypeFields.NAME]: item[NoticeTypeFields.NAME], [NoticeTypeFields.IS_DELETED]: item[NoticeTypeFields.IS_DELETED], + [NoticeTypeFields.DATA_INS_USER]: item[NoticeTypeFields.DATA_INS_USER], + [NoticeTypeFields.DATA_INS_DATE]: item[NoticeTypeFields.DATA_INS_DATE], + [NoticeTypeFields.DATA_CHG_USER]: item[NoticeTypeFields.DATA_CHG_USER], + [NoticeTypeFields.DATA_CHG_DATE]: item[NoticeTypeFields.DATA_CHG_DATE], [NoticeTypeFields.SELECTED]: false, })); pagination.total = result.TotalCount; diff --git a/src/views/base/PassportView.vue b/src/views/base/PassportView.vue index 30c43f8..f7c4ff2 100644 --- a/src/views/base/PassportView.vue +++ b/src/views/base/PassportView.vue @@ -140,6 +140,22 @@ {{ selectedRecord?.[PassportFields.NAME] }} + + @@ -149,7 +165,11 @@ import { ref, onMounted, computed, reactive } from "vue"; import { useRoute } from "vue-router"; import { getPageTitle } from "@/utils/pageTitle"; -import { showErrorNotification, showSuccessNotification } from "@/utils/index"; +import { + showErrorNotification, + showSuccessNotification, + formatDateTime, +} from "@/utils/index"; import { fetchPassports, addPassport, @@ -348,6 +368,10 @@ const rules = getFormRules(t); const passportNoLabel = computed(() => t("message.passportNo")); const passportNameLabel = computed(() => t("message.passportName")); +const createdByLabel = computed(() => t("message.createdBy")); +const createdDateLabel = computed(() => t("message.createdDate")); +const lastUpdatedByLabel = computed(() => t("message.lastUpdatedBy")); +const lastUpdatedDateLabel = computed(() => t("message.lastUpdatedDate")); const pagination = reactive({ current: 1, @@ -374,6 +398,10 @@ const fetchPassportData = async () => { [PassportFields.NUMBER]: item[PassportFields.NUMBER], [PassportFields.NAME]: item[PassportFields.NAME], [PassportFields.IS_DELETED]: item[PassportFields.IS_DELETED], + [PassportFields.DATA_INS_USER]: item[PassportFields.DATA_INS_USER], + [PassportFields.DATA_INS_DATE]: item[PassportFields.DATA_INS_DATE], + [PassportFields.DATA_CHG_USER]: item[PassportFields.DATA_CHG_USER], + [PassportFields.DATA_CHG_DATE]: item[PassportFields.DATA_CHG_DATE], [PassportFields.SELECTED]: false, })); pagination.total = result.TotalCount; diff --git a/src/views/base/PositionView.vue b/src/views/base/PositionView.vue index ab2ddd6..c2ab41d 100644 --- a/src/views/base/PositionView.vue +++ b/src/views/base/PositionView.vue @@ -138,6 +138,22 @@ {{ selectedRecord?.[PositionFields.NAME] }} + + @@ -147,7 +163,11 @@ import { ref, onMounted, computed, reactive } from "vue"; import { useRoute } from "vue-router"; import { getPageTitle } from "@/utils/pageTitle"; -import { showErrorNotification, showSuccessNotification } from "@/utils/index"; +import { + showErrorNotification, + showSuccessNotification, + formatDateTime, +} from "@/utils/index"; import { fetchPositions, addPosition, @@ -346,6 +366,10 @@ const rules = getFormRules(t); const positionNoLabel = computed(() => t("message.positionNo")); const positionNameLabel = computed(() => t("message.positionName")); +const createdByLabel = computed(() => t("message.createdBy")); +const createdDateLabel = computed(() => t("message.createdDate")); +const lastUpdatedByLabel = computed(() => t("message.lastUpdatedBy")); +const lastUpdatedDateLabel = computed(() => t("message.lastUpdatedDate")); const pagination = reactive({ current: 1, @@ -372,6 +396,10 @@ const fetchPositionData = async () => { [PositionFields.NUMBER]: item[PositionFields.NUMBER], [PositionFields.NAME]: item[PositionFields.NAME], [PositionFields.IS_DELETED]: item[PositionFields.IS_DELETED], + [PositionFields.DATA_INS_USER]: item[PositionFields.DATA_INS_USER], + [PositionFields.DATA_INS_DATE]: item[PositionFields.DATA_INS_DATE], + [PositionFields.DATA_CHG_USER]: item[PositionFields.DATA_CHG_USER], + [PositionFields.DATA_CHG_DATE]: item[PositionFields.DATA_CHG_DATE], [PositionFields.SELECTED]: false, })); pagination.total = result.TotalCount; diff --git a/src/views/base/PromotionContentView.vue b/src/views/base/PromotionContentView.vue index 67ab55f..9d64edb 100644 --- a/src/views/base/PromotionContentView.vue +++ b/src/views/base/PromotionContentView.vue @@ -151,6 +151,30 @@ {{ selectedRecord?.[PromotionContentFields.MESSAGE] }} + + @@ -160,7 +184,11 @@ import { ref, onMounted, computed, reactive } from "vue"; import { useRoute } from "vue-router"; import { getPageTitle } from "@/utils/pageTitle"; -import { showErrorNotification, showSuccessNotification } from "@/utils/index"; +import { + showErrorNotification, + showSuccessNotification, + formatDateTime, +} from "@/utils/index"; import { fetchPromotionContents, addPromotionContent, @@ -372,6 +400,10 @@ const promotionContentNoLabel = computed(() => const promotionContentMessageLabel = computed(() => t("message.promotionContentMessage"), ); +const createdByLabel = computed(() => t("message.createdBy")); +const createdDateLabel = computed(() => t("message.createdDate")); +const lastUpdatedByLabel = computed(() => t("message.lastUpdatedBy")); +const lastUpdatedDateLabel = computed(() => t("message.lastUpdatedDate")); const pagination = reactive({ current: 1, @@ -400,6 +432,14 @@ const fetchPromotionContentData = async () => { [PromotionContentFields.MESSAGE]: item[PromotionContentFields.MESSAGE], [PromotionContentFields.IS_DELETED]: item[PromotionContentFields.IS_DELETED], + [PromotionContentFields.DATA_INS_USER]: + item[PromotionContentFields.DATA_INS_USER], + [PromotionContentFields.DATA_INS_DATE]: + item[PromotionContentFields.DATA_INS_DATE], + [PromotionContentFields.DATA_CHG_USER]: + item[PromotionContentFields.DATA_CHG_USER], + [PromotionContentFields.DATA_CHG_DATE]: + item[PromotionContentFields.DATA_CHG_DATE], [PromotionContentFields.SELECTED]: false, })); pagination.total = result.TotalCount; diff --git a/src/views/base/QualificationView.vue b/src/views/base/QualificationView.vue index d3c0a2c..4310fc4 100644 --- a/src/views/base/QualificationView.vue +++ b/src/views/base/QualificationView.vue @@ -144,6 +144,26 @@ {{ selectedRecord?.[EducationFields.NAME] }} + + @@ -153,7 +173,11 @@ import { ref, onMounted, computed, reactive } from "vue"; import { useRoute } from "vue-router"; import { getPageTitle } from "@/utils/pageTitle"; -import { showErrorNotification, showSuccessNotification } from "@/utils/index"; +import { + showErrorNotification, + showSuccessNotification, + formatDateTime, +} from "@/utils/index"; import { fetchQualifications, addQualification, @@ -356,6 +380,10 @@ const sortedInfo = ref({ order: null, columnKey: null }); const qualificationNoLabel = computed(() => t("message.qualificationNo")); const qualificationNameLabel = computed(() => t("message.qualificationName")); +const createdByLabel = computed(() => t("message.createdBy")); +const createdDateLabel = computed(() => t("message.createdDate")); +const lastUpdatedByLabel = computed(() => t("message.lastUpdatedBy")); +const lastUpdatedDateLabel = computed(() => t("message.lastUpdatedDate")); const pagination = reactive({ current: 1, @@ -382,6 +410,10 @@ const fetchQualificationData = async () => { [EducationFields.NUMBER]: item[EducationFields.NUMBER], [EducationFields.NAME]: item[EducationFields.NAME], [EducationFields.IS_DELETED]: item[EducationFields.IS_DELETED], + [EducationFields.DATA_INS_USER]: item[EducationFields.DATA_INS_USER], + [EducationFields.DATA_INS_DATE]: item[EducationFields.DATA_INS_DATE], + [EducationFields.DATA_CHG_USER]: item[EducationFields.DATA_CHG_USER], + [EducationFields.DATA_CHG_DATE]: item[EducationFields.DATA_CHG_DATE], [EducationFields.SELECTED]: false, })); pagination.total = result.TotalCount; diff --git a/src/views/roominformation/CheckoutForm.vue b/src/views/roominformation/CheckoutForm.vue index c6a1264..4eeabb1 100644 --- a/src/views/roominformation/CheckoutForm.vue +++ b/src/views/roominformation/CheckoutForm.vue @@ -1,57 +1,169 @@ - + diff --git a/src/views/roominformation/RoomMapView.vue b/src/views/roominformation/RoomMapView.vue index 781bde4..15eb83b 100644 --- a/src/views/roominformation/RoomMapView.vue +++ b/src/views/roominformation/RoomMapView.vue @@ -147,6 +147,7 @@ v-if="dialogComponent" ref="dialogComponentRef" v-bind="dialogProps" + @submit="handleDialogOk" />