diff --git a/src/App.vue b/src/App.vue index 63fc79f..9a1e42c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -9,7 +9,7 @@ + + diff --git a/src/components/FastMenuConfig/index.vue b/src/components/FastMenuConfig/index.vue new file mode 100644 index 0000000..dbec7fa --- /dev/null +++ b/src/components/FastMenuConfig/index.vue @@ -0,0 +1,183 @@ + + + + diff --git a/src/layout/components/FastMenu/index.vue b/src/layout/components/FastMenu/index.vue new file mode 100644 index 0000000..992a7a0 --- /dev/null +++ b/src/layout/components/FastMenu/index.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/layout/index.vue b/src/layout/index.vue index b75e210..51d4a0a 100644 --- a/src/layout/index.vue +++ b/src/layout/index.vue @@ -27,10 +27,11 @@ + - + @@ -42,11 +43,15 @@ + - + + + + @@ -105,6 +110,7 @@ function handleOutsideClick() { function toggleSidebar() { appStore.toggleSidebar(); } +function showFastMenu() {} const route = useRoute(); watch(route, () => { diff --git a/src/store/index.ts b/src/store/index.ts index a36e43d..a2020d9 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -14,5 +14,6 @@ export * from "./modules/settings"; export * from "./modules/tags-view"; export * from "./modules/user"; export * from "./modules/dict"; +export * from "./modules/quick"; export { store }; diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts index b90fc9f..3f22d9a 100644 --- a/src/store/modules/permission.ts +++ b/src/store/modules/permission.ts @@ -16,6 +16,7 @@ export const usePermissionStore = defineStore("permission", () => { // 路由是否加载完成 const isRoutesLoaded = ref(false); + const menus = ref([]) /** @@ -31,10 +32,9 @@ export const usePermissionStore = defineStore("permission", () => { } MenuAPI.getRoutes() .then((data) => { + menus.value = data if (!isTest) { const dynamicRoutes = parseDynamicRoutes(data.filter(v => v.type == 0)); - console.log('dynamicRoutes') - console.log(dynamicRoutes) dynamicRoutes.forEach((route) => { //过滤出可见子节点 let onlyOneChild = null @@ -95,8 +95,60 @@ export const usePermissionStore = defineStore("permission", () => { }); isRoutesLoaded.value = false; }; + function getMenuMap() { + // 初始化Map + const map = new Map(); + + // 定义递归函数处理菜单节点 + function processMenuNode(menuNode) { + // 将当前节点存入Map + map.set(menuNode.menuId, menuNode); + + // 如果有子节点且子节点数组不为空,则递归处理每个子节点 + if (menuNode.children && Array.isArray(menuNode.children) && menuNode.children.length > 0) { + menuNode.children.forEach((childNode) => { + processMenuNode(childNode); + }); + } + } + + // 遍历根级菜单,逐个处理 + if (menus.value && Array.isArray(menus.value)) { + menus.value.forEach((rootMenu) => { + processMenuNode(rootMenu); + }); + } + + return map; + } + + function returnMenuName(menuId: number | string) { + const menu = menusIdMap.value.get(`${menuId}`); + return menu ? menu.title : ''; + } + const menusIdMap = computed(() => { + const map = getMenuMap(); + return map; + }); + + function menuJump(menuId: number | string) { + const menu = menusIdMap.value.get(`${menuId}`); + console.log('menu', menu); + if (menu) { + if (menu.name) { + router.push({ name: menu.name as string }) + + } else { + router.push({ path: menu.path as string }) + + } + } + } return { + menuJump, + returnMenuName, + menus, routes, mixedLayoutLeftRoutes, isRoutesLoaded, diff --git a/src/store/modules/quick.ts b/src/store/modules/quick.ts new file mode 100644 index 0000000..e08f465 --- /dev/null +++ b/src/store/modules/quick.ts @@ -0,0 +1,37 @@ + +import { store } from "@/store"; +import quickApi, { type QuickMenu } from "@/api/account/quick"; +import { usePermissionStoreHook } from "@/store/modules/permission"; + + +export const useQuickStore = defineStore("quick", () => { + const quickMenus = ref([]); + + async function getQuickMenus() { + const res = await quickApi.getList({ isEdit: 0 }); + const obj: any = {} + let arr = [] + for (let menu of res) { + if (obj.hasOwnProperty(menu.menuId) || menu.status != 1 || !usePermissionStoreHook().returnMenuName(menu.menuId)) { + continue; + } else { + obj[menu.menuId] = true; + arr.push(menu) + } + } + quickMenus.value = arr; + } + getQuickMenus() + return { + quickMenus, getQuickMenus + }; +}); + +/** + * 用于在组件外部(如在Pinia Store 中)使用 Pinia 提供的 store 实例。 + * 官方文档解释了如何在组件外部使用 Pinia Store: + * https://pinia.vuejs.org/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component + */ +export function useQuickStoreHook() { + return useQuickStore(store); +} diff --git a/src/views/admin/system/commonlyUsedMenu/index.vue b/src/views/admin/system/commonlyUsedMenu/index.vue new file mode 100644 index 0000000..63a05ba --- /dev/null +++ b/src/views/admin/system/commonlyUsedMenu/index.vue @@ -0,0 +1,5 @@ + diff --git a/src/views/data/sales.vue b/src/views/data/sales.vue index d840b63..d894063 100644 --- a/src/views/data/sales.vue +++ b/src/views/data/sales.vue @@ -56,7 +56,7 @@
-
¥{{ payCount.totalAmount || 0 }}
+
¥{{ multiplyAndFormat(payCount.totalAmount || 0) }}
总金额
@@ -67,7 +67,7 @@
-
¥{{ payCount.refundAmount || 0 }}
+
¥{{ multiplyAndFormat(payCount.refundAmount || 0) }}
退款金额
@@ -100,7 +100,7 @@
-
{{ payCount.totalAmount - payCount.refundAmount || 0 }}
+
{{ multiplyAndFormat((payCount.totalAmount - payCount.refundAmount) || 0) }}
实际销售额总和
@@ -111,7 +111,7 @@
-
{{ payCount.saleCount - payCount.refundCount || 0 }}
+
{{ multiplyAndFormat((payCount.saleCount - payCount.refundCount) || 0) }}
实际销量总和
@@ -152,10 +152,10 @@ - + - + @@ -165,17 +165,17 @@ - + - + - + - + @@ -193,12 +193,13 @@ import saleSummaryApi from "@/api/order/sale-summary"; import categoryApi from "@/api/product/productclassification"; import ShopApi from "@/api/account/shop"; import dayjs from "dayjs"; -import { downloadFile } from "@/utils/index"; +import { downloadFile, multiplyAndFormat } from "@/utils/index"; import { formatDateRange } from './utils/index.js' export default { data() { return { + multiplyAndFormat, timeValue: "today", resetQuery: null, orderType: "2", @@ -314,7 +315,8 @@ export default { endDate: this.query.createdAt[1], categoryId: this.query.prodCategoryId, productName: this.query.productName, - shopId: this.shopId + shopId: this.shopId, + rangeType: this.timeValue, }); downloadFile(file, "数据", "xlsx"); } catch (error) { diff --git a/src/views/data/table.vue b/src/views/data/table.vue index 4af4868..634406c 100644 --- a/src/views/data/table.vue +++ b/src/views/data/table.vue @@ -187,7 +187,8 @@ export default { const file = await tableSummaryApi.export({ beginDate: this.query.createdAt[0], endDate: this.query.createdAt[1], - shopId: this.shopId + shopId: this.shopId, + rangeType: this.timeValue, }); downloadFile(file, "数据", "xlsx"); this.downloadLoading = false; diff --git a/src/views/inventory/consumables/components/add-haocai.vue b/src/views/inventory/consumables/components/add-haocai.vue index b94d078..2135ff1 100644 --- a/src/views/inventory/consumables/components/add-haocai.vue +++ b/src/views/inventory/consumables/components/add-haocai.vue @@ -12,7 +12,15 @@ - +
+ + {{ form.conUnit }} +
+ 第二单位价格: + + {{ form.conUnitTwo }} +
+
+ + @@ -65,6 +72,7 @@ + + \ No newline at end of file diff --git a/src/views/shop/config/components/shopInfo.vue b/src/views/shop/config/components/shopInfo.vue index 3268e20..5f0a243 100644 --- a/src/views/shop/config/components/shopInfo.vue +++ b/src/views/shop/config/components/shopInfo.vue @@ -3,25 +3,14 @@
- + - +
- +
- + - + - + - +
- +
开启后,用户只能在店铺附近xx公里内点餐
- + @@ -247,29 +175,24 @@
- +
起菜到上菜的时间间隔,开启后会有超时提示
- +
+ + + 营业中 @@ -285,24 +208,11 @@
- - + + 点击上传 @@ -19,6 +21,7 @@ import shopInfo from "./components/shopInfo.vue"; import safe from "./components/safe.vue"; import notifications from "./components/notifications.vue"; +import contactSetting from "./components/contactSetting.vue"; const state = reactive({ activeName: "1", diff --git a/src/views/tool/Instead/components/carts/list.vue b/src/views/tool/Instead/components/carts/list.vue index 84fdc76..2a61a85 100644 --- a/src/views/tool/Instead/components/carts/list.vue +++ b/src/views/tool/Instead/components/carts/list.vue @@ -4,22 +4,38 @@
以下是优惠菜品
- +
@@ -50,9 +74,14 @@
- + @@ -131,7 +187,7 @@ import { useUserStore } from "@/store/modules/user"; function isEmptyObject(obj) { // 步骤1:排除null和非对象类型 - if (obj === null || typeof obj !== 'object') { + if (obj === null || typeof obj !== "object") { return false; } // 步骤2:排除数组(数组也是对象,需单独判断) @@ -313,4 +369,4 @@ defineExpose({ margin-left: 10px; } } - \ No newline at end of file + diff --git a/src/views/tool/Instead/components/order.vue b/src/views/tool/Instead/components/order.vue index c786a31..8aca734 100644 --- a/src/views/tool/Instead/components/order.vue +++ b/src/views/tool/Instead/components/order.vue @@ -13,7 +13,7 @@

余额:{{ carts.vipUser.amount }} - 积分:{{ carts.vipUser.accountPoints }} + 积分:{{ carts.vipUser.pointBalance }}

@@ -46,7 +46,8 @@ + :max="pointsRes.maxUsablePoints" :disabled="!pointsRes.usable" + @change="pointsToMoney">

* @@ -398,8 +399,16 @@ watch( ); //002-获取订单可用积分及抵扣金额(支付页面使用) -const pointsRes = ref({ usable: true, maxUsablePoints: 0, minDeductionPoints: 0 }); -const usePointsNumber = ref(0); +const pointsRes = ref({ + usable: false, + equivalentPoints: 0, // 积分换算比例 eg: 20 积分 = 1 元 + maxDeductionRatio: 0, // 下单最高抵扣比例(小数) + minPaymentAmount: 0, // 下单实付抵扣门槛(元) + maxUsablePoints: 0, + minDeductionPoints: 0, + unusableReason: "", +}); +const usePointsNumber = ref(0); // 输入的积分数量(用户填写) //积分可抵扣最大金额 const scoreMaxMoney = computed(() => { return ( @@ -421,22 +430,129 @@ async function pointsInit() { if (!carts.vipUser.id) { return; } - const res = await PointsApi.calcOrderUsablePoints({ + const { pointsConfig, pointsUser } = await PointsApi.calcOrderUsablePoints({ shopUserId: carts.vipUser.id, - orderAmount: scoreMaxMoney.value, }); + + // 不修改 accountPoints(为余额),将积分保存到 pointBalance 字段 + carts.vipUser.pointBalance = pointsUser && pointsUser.id ? pointsUser.pointBalance : 0; + + // 保险取值 + const eq = pointsConfig?.equivalentPoints || 0; + const maxRatio = pointsConfig?.maxDeductionRatio || 0; + const minPay = pointsConfig?.minPaymentAmount || 0; + + // 计算当前订单可抵扣金额上限(元) + let finalPay = Number(carts.orderCostSummary.finalPayAmount) || 0; + + const res = { + usable: true, + equivalentPoints: eq, + maxDeductionRatio: maxRatio, + minPaymentAmount: minPay, + maxUsablePoints: 0, + minDeductionPoints: pointsConfig?.minDeductionPoints || eq, + unusableReason: "", + }; + + // 如果订单实付低于最小使用门槛,则不可用 + if (finalPay <= 0 || (minPay > 0 && finalPay < minPay)) { + res.usable = false; + res.unusableReason = `订单实付金额低于 ${minPay} 元,无法使用积分抵扣`; + } else if (eq <= 0) { + res.usable = false; + res.unusableReason = `积分换算比例配置错误,无法使用积分抵扣`; + } else { + // 计算基于比例限制的最大抵扣金额(元) + let maxByRatio = finalPay * maxRatio; + // 保证抵扣后剩余金额 >= minPaymentAmount + if (minPay > 0) { + const allowed = finalPay - minPay; + if (allowed <= 0) { + res.usable = false; + res.unusableReason = `抵扣后实付金额必须大于等于 ${minPay} 元,当前不可使用积分`; + } else { + maxByRatio = Math.min(maxByRatio, allowed); + } + } + + if (res.usable) { + // 可用积分上限(向下取整为 eq 的倍数) + const maxByMoney = Math.floor(maxByRatio * eq); + const userPoints = carts.vipUser.pointBalance || 0; + res.maxUsablePoints = Math.min(userPoints, maxByMoney); + // 最小抵扣积分为配置值或等于换算比 + res.minDeductionPoints = pointsConfig?.minDeductionPoints || eq; + if (res.maxUsablePoints < res.minDeductionPoints) { + res.usable = false; + res.unusableReason = `可用积分不足,至少需要 ${res.minDeductionPoints} 积分才可抵扣`; + } + } + } + pointsRes.value = res; carts.pointDeductionRule.pointsPerYuan = res.equivalentPoints; + if (score.sel == -1) { + // 未选择使用积分 + return res; + } + + // 如果可用则默认填充可用最大值,否则清零 + usePointsNumber.value = res.usable ? res.maxUsablePoints : 0; + if (!res.usable) score.sel = -1; + + return res; +} + +// 将输入的积分数转换为抵扣金额并写回 carts.orderCostSummary +function pointsToMoney(val) { + const cfg = pointsRes.value; + if (!cfg.usable || cfg.equivalentPoints <= 0) { + usePointsNumber.value = 0; + carts.orderCostSummary.pointUsed = 0; + carts.orderCostSummary.pointDeductionAmount = 0; return; } - usePointsNumber.value = res.usable ? res.maxUsablePoints : 0; - if (res.usable) { - } else { - score.sel = -1; + // 确保为整数积分 + let pts = parseInt(usePointsNumber.value || 0, 10); + if (isNaN(pts) || pts <= 0) { + pts = 0; } - return res; + + // 限制最大值 + if (cfg.maxUsablePoints && pts > cfg.maxUsablePoints) { + pts = cfg.maxUsablePoints; + } + + // 计算抵扣金额(元),向下保留两位 + const money = new BigNumber(pts).div(cfg.equivalentPoints).decimalPlaces(2, BigNumber.ROUND_DOWN).toNumber(); + + // 再次校验不超过允许的最大抵扣金额(基于比例或门槛) + let finalPay = Number(carts.orderCostSummary.finalPayAmount) || 0; + let maxByRatio = finalPay * cfg.maxDeductionRatio; + if (cfg.minPaymentAmount > 0) { + const allowed = finalPay - cfg.minPaymentAmount; + if (allowed <= 0) { + usePointsNumber.value = 0; + carts.orderCostSummary.pointUsed = 0; + carts.orderCostSummary.pointDeductionAmount = 0; + return; + } + maxByRatio = Math.min(maxByRatio, allowed); + } + const maxAllowedMoney = new BigNumber(maxByRatio).decimalPlaces(2, BigNumber.ROUND_DOWN).toNumber(); + if (money > maxAllowedMoney) { + // 调整积分到允许的最大金额对应的积分 + const allowedPts = Math.floor(maxAllowedMoney * cfg.equivalentPoints); + pts = Math.min(allowedPts, cfg.maxUsablePoints); + } + + usePointsNumber.value = pts; + const finalMoney = new BigNumber(pts).div(cfg.equivalentPoints).decimalPlaces(2, BigNumber.ROUND_DOWN).toNumber(); + carts.orderCostSummary.pointUsed = pts; + carts.orderCostSummary.pointDeductionAmount = finalMoney; } const emits = defineEmits(["chooseUser", "paysuccess"]); @@ -693,6 +809,10 @@ watch( onMounted(() => { carts.payParamsInit(); getPaytype(); + + if (carts.vipUser.id) { + pointsInit(); + } }); defineExpose({ nowPayClick,