diff --git a/manager/src/router/router.js b/manager/src/router/router.js index e861d650b..3c94850dd 100644 --- a/manager/src/router/router.js +++ b/manager/src/router/router.js @@ -481,6 +481,12 @@ export const otherRouter = { meta: { title: "等级变更记录" }, component: () => import("@/views/distribution/distributionGradeHistory.vue") }, + { + path: "procurement/purchase-order/list", + title: "采购单", + name: "manager-procurement-order-list", + component: () => import("@/views/procurement/purchase-order/list.vue") + }, { path: "procurement/purchase-order/detail", title: "采购单详情", diff --git a/manager/src/views/appointment/workOrderList.vue b/manager/src/views/appointment/workOrderList.vue index 5152b5fa8..36e04fcaa 100644 --- a/manager/src/views/appointment/workOrderList.vue +++ b/manager/src/views/appointment/workOrderList.vue @@ -8,6 +8,11 @@ + + + + + 搜索 @@ -22,7 +27,9 @@ - + + + import { getManagerWorkOrderPage } from "@/api/appointment"; +const STATUS_OPTIONS = [ + { label: "未完成", value: "OPEN" }, + { label: "待领取", value: "PENDING_CLAIM" }, + { label: "已领取", value: "CLAIMED" }, + { label: "待核销", value: "WAIT_VERIFY" }, + { label: "服务中", value: "IN_SERVICE" }, + { label: "服务结束", value: "SERVICE_ENDED" }, + { label: "已退款", value: "REFUNDED" }, + { label: "逾期未到店", value: "NO_SHOW" }, +]; + export default { name: "managerAppointmentWorkOrders", data() { @@ -46,18 +64,28 @@ export default { loading: false, data: [], total: 0, + statusOptions: STATUS_OPTIONS, searchForm: { pageNumber: 1, pageSize: 10, orderSn: "", workOrderSn: "", + serviceStatus: "", }, }; }, mounted() { + const status = this.$route.query.serviceStatus; + if (status) { + this.searchForm.serviceStatus = status; + } this.getData(); }, methods: { + statusText(value) { + const found = STATUS_OPTIONS.find((item) => item.value === value); + return found ? found.label : value || "—"; + }, handleSearch() { this.searchForm.pageNumber = 1; this.getData(); diff --git a/manager/src/views/home/home.scss b/manager/src/views/home/home.scss index 58fe821cc..9c084a6b1 100644 --- a/manager/src/views/home/home.scss +++ b/manager/src/views/home/home.scss @@ -29,7 +29,14 @@ h4 { margin-right: 20px; } +.todo-list { + flex-wrap: wrap; + gap: 12px; +} .todo-item { + flex: 1 1 140px; + min-width: 120px; + margin-right: 0; flex-direction: column; background: #ebebeb88; .counts { @@ -257,3 +264,34 @@ h4 { flex-wrap: wrap; gap: 16px; } +.range-card { + padding-bottom: 8px; +} +.range-bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + flex-wrap: wrap; + h4 { + margin: 16px 0; + } +} +.range-options { + display: flex; + align-items: center; + gap: 16px; + flex-wrap: wrap; + span { + cursor: pointer; + } + .active { + color: $theme_color; + font-weight: 600; + } +} +.range-tip { + margin: 0 0 12px; + color: #909399; + font-size: 13px; +} diff --git a/manager/src/views/home/home.vue b/manager/src/views/home/home.vue index c15f7a6f9..5f1876c45 100644 --- a/manager/src/views/home/home.vue +++ b/manager/src/views/home/home.vue @@ -73,6 +73,14 @@
{{ $store.state.notices.waitPayBill || 0 }}
待审核分账
+
+
{{ $store.state.notices.appointmentWorkOrder || 0 }}
+
未完成预约工单
+
+
+
{{ $store.state.notices.procurementAudit || 0 }}
+
待审核采购单
+
@@ -127,10 +135,7 @@
今日交易额
- {{ $filters.unitPrice(homeData.todayOrderPrice, '¥') }} - ¥0.00 + {{ $filters.unitPrice(homeData.todayOrderPrice || 0, "¥") }}
今日新增店铺
@@ -160,6 +165,34 @@
+
+
+

统计范围

+
+ {{ item.title }} + + + +
+
+

流量走势、交易趋势和热卖 TOP10 使用同一时间范围,当前为{{ rangeTitle }}。

+
@@ -174,7 +207,7 @@
-

热卖商品TOP10

+

热卖商品TOP10 · {{ rangeTitle }}

@@ -189,7 +222,7 @@
-

热卖店铺TOP10

+

热卖店铺TOP10 · {{ rangeTitle }}

@@ -223,19 +256,40 @@ export default { pvChart: "", // 流量统计 orderChart: "", // 订单统计 historyMemberChart: "", // 最近会员流量统计 - params: { - // 请求参数 - searchType: "LAST_SEVEN", - }, - // 订单传参 - orderParams: { - searchType: "LAST_SEVEN", // TODAY , YESTERDAY , LAST_SEVEN , LAST_THIRTY - year: "", - shopId: "", - memberId: "", - }, + rangeKey: "LAST_SEVEN", + rangeMonth: "", + rangeOptions: [ + { title: "今天", searchType: "TODAY" }, + { title: "昨天", searchType: "YESTERDAY" }, + { title: "过去7天", searchType: "LAST_SEVEN" }, + { title: "过去30天", searchType: "LAST_THIRTY" }, + ], }; }, + computed: { + recentMonths() { + const list = []; + const now = new Date(); + for (let i = 0; i < 12; i += 1) { + const date = new Date(now.getFullYear(), now.getMonth() - i, 1); + const year = date.getFullYear(); + const month = date.getMonth() + 1; + list.push({ + value: `${year}-${month}`, + label: `${year}年${month}月`, + }); + } + return list; + }, + rangeTitle() { + if (this.rangeMonth) { + const found = this.recentMonths.find((item) => item.value === this.rangeMonth); + return found ? found.label : this.rangeMonth; + } + const found = this.rangeOptions.find((item) => item.searchType === this.rangeKey); + return found ? found.title : "过去7天"; + }, + }, methods: { // 路由跳转 navigateTo(name) { @@ -250,16 +304,53 @@ export default { query: { tabStatus: 'APPLY' } }); }, + goAppointmentWorkOrders() { + this.$router.push({ + name: "manager-appointment-work-orders", + query: { serviceStatus: "OPEN" }, + }); + }, + goProcurementAudit() { + this.$router.push({ + name: "manager-procurement-order-list", + query: { status: "SUBMITTED" }, + }); + }, + rangeQuery() { + if (this.rangeMonth) { + const [year, month] = this.rangeMonth.split("-"); + return { year, month }; + } + return { searchType: this.rangeKey || "LAST_SEVEN" }; + }, + changeRange(item) { + this.rangeMonth = ""; + this.rangeKey = item.searchType; + this.reloadRange(); + }, + changeMonth(value) { + this.rangeMonth = value || ""; + if (!this.rangeMonth) { + this.rangeKey = "LAST_SEVEN"; + } + this.reloadRange(); + }, + reloadRange() { + this.toHotGoods(); + this.topHotShops(); + this.getPvChart(); + this.initOrderChartList(); + }, // top10热卖商品 async toHotGoods() { - let res = await hotGoods(this.params); - res.success ? (this.topHotGoodsData = res.result) : ""; + let res = await hotGoods(this.rangeQuery()); + this.topHotGoodsData = res.success ? res.result || [] : []; }, // top10热卖店铺 async topHotShops() { - let res = await hotShops(this.params); - res.success ? (this.topHotShopsData = res.result) : ""; + let res = await hotShops(this.rangeQuery()); + this.topHotShopsData = res.success ? res.result || [] : []; }, // 今日待办 async awaitTodo() { @@ -271,25 +362,21 @@ export default { async getHomeData() { let res = await homeStatistics(); if (res.success) { - if ( - res.result.todayOrderPrice && - res.result.todayOrderPrice != "null" - ) { - res.result.todayOrderPrice = parseInt(res.result.todayOrderPrice); - } else { - res.result.todayOrderPrice = 0; - } - + const price = res.result.todayOrderPrice; + const amount = price === null || price === undefined || price === "" || price === "null" + ? 0 + : Number(price); + res.result.todayOrderPrice = Number.isNaN(amount) ? 0 : amount; this.homeData = res.result; } }, // 实例化订单图表 - async initOrderChartList(name) { + async initOrderChartList() { this.orderChart ? this.orderChart.clear() : '' - const res = await API_Goods.getOrderChart(this.orderParams); + const res = await API_Goods.getOrderChart(this.rangeQuery()); if (res.success) { - this.chartList = res.result; + this.orderChartData = res.result || []; if (!this.orderChart) { this.orderChart = new Chart({ @@ -306,11 +393,11 @@ export default { // 订单表 initOrderChart() { - // 默认已经加载 legend-filter 交互 - let data = this.chartList; + let data = this.orderChartData || []; data.forEach((item) => { - item.createTime = item.createTime.split(" ")[0]; + const time = item.createTime ? String(item.createTime) : ""; + item.createTime = time.includes(" ") ? time.split(" ")[0] : time; item.title = "交易额"; }); this.orderChart.data(data); @@ -346,7 +433,8 @@ export default { initPvChart() { let uv = []; let pv = []; - this.data.forEach((item) => { + const rows = Array.isArray(this.data) ? this.data : []; + rows.forEach((item) => { uv.push({ date: item.date, uvNum: item.uvNum, @@ -364,7 +452,6 @@ export default { let data = [...uv, ...pv]; - console.log("pv", data); this.pvChart.data(data); this.pvChart.tooltip({ @@ -397,9 +484,9 @@ export default { // 浏览量 async getPvChart() { this.pvChart ? this.pvChart.clear() : '' - API_Member.getStatisticsList(this.params).then((res) => { + API_Member.getStatisticsList(this.rangeQuery()).then((res) => { if (res.result) { - this.data = res.result; + this.data = res.result || []; if (!this.pvChart) { this.pvChart = new Chart({ diff --git a/manager/src/views/main-parts/message-tip.vue b/manager/src/views/main-parts/message-tip.vue index 97a1c18ee..11c294ab0 100644 --- a/manager/src/views/main-parts/message-tip.vue +++ b/manager/src/views/main-parts/message-tip.vue @@ -28,6 +28,12 @@ 待与商家对账 + + 未完成预约工单 + + + 待审核采购单 + @@ -58,6 +64,14 @@ export default { }, methods: { navigateTo(name) { + if (name === "manager-appointment-work-orders") { + this.$router.push({ name, query: { serviceStatus: "OPEN" } }); + return; + } + if (name === "manager-procurement-order-list") { + this.$router.push({ name, query: { status: "SUBMITTED" } }); + return; + } this.$router.push({ name }); }, }, diff --git a/manager/src/views/order/order/orderList.vue b/manager/src/views/order/order/orderList.vue index 76da142a6..04320b6dc 100644 --- a/manager/src/views/order/order/orderList.vue +++ b/manager/src/views/order/order/orderList.vue @@ -4,51 +4,41 @@ - - - - + - + - + + + + - + + + + - - - - - - + + - - + + - - - - - + + @@ -59,12 +49,12 @@ clearable start-placeholder="开始时间" end-placeholder="结束时间" - style="width: 360px" @change="selectDateRange" /> - - 搜索 + + 搜索 + 重置 @@ -73,38 +63,61 @@
- 导出订单 + 导出订单
- - + + + + + - + + + + - + - + + + + - + @@ -113,7 +126,7 @@ - + @@ -124,7 +137,7 @@ import * as API_Order from "@/api/order"; -import { ElMessage } from "element-plus"; +import { ElMessage, ElMessageBox } from "element-plus"; import { unitPrice, customRouterPush } from "@/utils/filters"; +const QUERY_KEY = "manager-order-list-query"; +const PAGE_SIZES = [20, 50, 100]; + +const PROMOTION_OPTIONS = [ + { label: "普通订单", value: "NORMAL" }, + { label: "拼团订单", value: "PINTUAN" }, + { label: "赠品订单", value: "GIFT" }, + { label: "积分订单", value: "POINTS" }, + { label: "砍价订单", value: "KANJIA" }, +]; + +const ORDER_TYPE_OPTIONS = [ + { label: "普通订单", value: "NORMAL" }, + { label: "虚拟订单", value: "VIRTUAL" }, + { label: "电子卡券", value: "E_COUPON" }, + { label: "预约订单", value: "APPOINTMENT" }, +]; + +const PAYMENT_OPTIONS = [ + { label: "微信支付", value: "WECHAT" }, + { label: "支付宝", value: "ALIPAY" }, + { label: "余额支付", value: "WALLET" }, + { label: "线下转账", value: "BANK_TRANSFER" }, +]; + +function emptySearchForm(pageSize = 20) { + return { + pageNumber: 1, + pageSize, + sort: "createTime", + order: "desc", + startDate: "", + endDate: "", + orderType: "", + orderSn: "", + buyerName: "", + goodsName: "", + shipName: "", + consigneeMobile: "", + storeName: "", + orderStatus: "", + paymentMethod: "", + orderPromotionType: "", + }; +} + +function readSavedQuery() { + const raw = sessionStorage.getItem(QUERY_KEY); + if (!raw) return null; + try { + const saved = JSON.parse(raw); + const form = emptySearchForm(); + const src = saved && saved.searchForm ? saved.searchForm : {}; + Object.keys(form).forEach((key) => { + if (src[key] !== undefined && src[key] !== null) { + form[key] = src[key]; + } + }); + form.pageNumber = Number(form.pageNumber) || 1; + form.pageSize = PAGE_SIZES.includes(Number(form.pageSize)) ? Number(form.pageSize) : 20; + const currentStatus = saved.currentStatus || form.orderStatus || "ALL"; + if (!currentStatus || currentStatus === "ALL") { + form.orderStatus = ""; + } + return { + searchForm: form, + selectDate: Array.isArray(saved.selectDate) ? saved.selectDate : null, + currentStatus: currentStatus || "ALL", + }; + } catch (e) { + sessionStorage.removeItem(QUERY_KEY); + return null; + } +} + export default { name: "orderList", data() { return { - fields: { - 订单编号: "sn", - 下单时间: "createTime", - 客户名称: "memberName", - 支付方式: { - field: "clientType", - callback: (value) => { - if (value == "H5") return "移动端"; - if (value == "PC") return "PC端"; - if (value == "WECHAT_MP") return "小程序端"; - if (value == "APP") return "移动应用端"; - return value; - }, - }, - 商品数量: "groupNum", - 付款状态: { - field: "payStatus", - callback: (value) => - value == "UNPAID" ? "未付款" : value == "PAID" ? "已付款" : "", - }, - 店铺: "storeName", - }, loading: true, - searchForm: { - pageNumber: 1, - pageSize: 20, - sort: "createTime", - order: "desc", - startDate: "", - endDate: "", - orderType: "", - orderSn: "", - keywords: "", - buyerName: "", - goodsName: "", - shipName: "", - orderStatus: "", - paymentMethod: "", - orderPromotionType: "", - }, + exporting: false, + pageSizes: PAGE_SIZES, + promotionOptions: PROMOTION_OPTIONS, + orderTypeOptions: ORDER_TYPE_OPTIONS, + paymentOptions: PAYMENT_OPTIONS, + searchForm: emptySearchForm(), selectDate: null, data: [], total: 0, @@ -194,44 +249,18 @@ export default { }, computed: { orderStatusWithCount() { + const count = (value) => (value ? `(${value})` : ""); return [ { title: "全部", value: "ALL" }, - { - title: `未付款${this.orderNumData.waitPayNum ? "(" + this.orderNumData.waitPayNum + ")" : ""}`, - value: "UNPAID", - }, - { - title: `已付款${this.orderNumData.waitDeliveryNum ? "(" + this.orderNumData.waitDeliveryNum + ")" : ""}`, - value: "PAID", - }, - { - title: `待发货${this.orderNumData.waitShipNum ? "(" + this.orderNumData.waitShipNum + ")" : ""}`, - value: "UNDELIVERED", - }, - { - title: `部分发货${this.orderNumData.partsDeliveredNumNum ? "(" + this.orderNumData.partsDeliveredNumNum + ")" : ""}`, - value: "PARTS_DELIVERED", - }, - { - title: `待收货${this.orderNumData.deliveredNum ? "(" + this.orderNumData.deliveredNum + ")" : ""}`, - value: "DELIVERED", - }, - { - title: `待核验${this.orderNumData.waitCheckNum ? "(" + this.orderNumData.waitCheckNum + ")" : ""}`, - value: "TAKE", - }, - { - title: `待自提${this.orderNumData.waitSelfPickNum ? "(" + this.orderNumData.waitSelfPickNum + ")" : ""}`, - value: "STAY_PICKED_UP", - }, - { - title: `已完成${this.orderNumData.finishNum ? "(" + this.orderNumData.finishNum + ")" : ""}`, - value: "COMPLETED", - }, - { - title: `已关闭${this.orderNumData.closeNum ? "(" + this.orderNumData.closeNum + ")" : ""}`, - value: "CANCELLED", - }, + { title: `未付款${count(this.orderNumData.waitPayNum)}`, value: "UNPAID" }, + { title: `已付款${count(this.orderNumData.waitDeliveryNum)}`, value: "PAID" }, + { title: `待发货${count(this.orderNumData.waitShipNum)}`, value: "UNDELIVERED" }, + { title: `部分发货${count(this.orderNumData.partsDeliveredNumNum)}`, value: "PARTS_DELIVERED" }, + { title: `待收货${count(this.orderNumData.deliveredNum)}`, value: "DELIVERED" }, + { title: `待核验${count(this.orderNumData.waitCheckNum)}`, value: "TAKE" }, + { title: `待自提${count(this.orderNumData.waitSelfPickNum)}`, value: "STAY_PICKED_UP" }, + { title: `已完成${count(this.orderNumData.finishNum)}`, value: "COMPLETED" }, + { title: `已关闭${count(this.orderNumData.closeNum)}`, value: "CANCELLED" }, ]; }, }, @@ -240,19 +269,21 @@ export default { onStatusTabClick(tab) { this.orderStatusClick(tab.paneName); }, + orderGoods(row) { + const items = row && row.orderItems; + return items && items.length ? items : []; + }, clientTypeText(v) { const map = { H5: "移动端", PC: "PC端", WECHAT_MP: "小程序端", APP: "移动应用端" }; - return map[v] || v || "-"; + return map[v] || v || "—"; + }, + orderTypeText(v) { + const found = ORDER_TYPE_OPTIONS.find((item) => item.value === v); + return found ? found.label : v || "—"; }, orderPromotionText(v) { - const map = { - NORMAL: "普通订单", - PINTUAN: "拼团订单", - GIFT: "赠品订单", - POINTS: "积分订单", - KANJIA: "砍价订单", - }; - return map[v] || v || "-"; + const found = PROMOTION_OPTIONS.find((item) => item.value === v); + return found ? found.label : v || "—"; }, orderPromotionTagType(v) { const map = { @@ -264,15 +295,24 @@ export default { }; return map[v] || "info"; }, + deliveryMethodText(v) { + const map = { + SELF_PICK_UP: "自提", + LOCAL_TOWN_DELIVERY: "同城配送", + LOGISTICS: "物流", + VIRTUAL: "虚拟发货", + }; + return map[v] || v || "—"; + }, paymentMethodText(v) { const map = { - NOT_ACTUALLY_PAID: "-", + NOT_ACTUALLY_PAID: "—", WECHAT: "微信支付", ALIPAY: "支付宝", WALLET: "余额支付", BANK_TRANSFER: "线下转账", }; - return map[v] || v || "-"; + return map[v] || v || "—"; }, orderStatusText(v) { const map = { @@ -286,7 +326,7 @@ export default { TAKE: "待核验", CANCELLED: "已关闭", }; - return map[v] || v || "-"; + return map[v] || v || "—"; }, orderStatusTagType(v) { const map = { @@ -302,6 +342,23 @@ export default { }; return map[v] || "info"; }, + rememberQuery() { + sessionStorage.setItem( + QUERY_KEY, + JSON.stringify({ + searchForm: this.searchForm, + selectDate: this.selectDate, + currentStatus: this.currentStatus, + }) + ); + }, + restoreQuery() { + const saved = readSavedQuery(); + if (!saved) return; + this.searchForm = saved.searchForm; + this.selectDate = saved.selectDate; + this.currentStatus = saved.currentStatus; + }, init() { this.getDataList(); this.getOrderNumData(); @@ -317,10 +374,17 @@ export default { }, handleSearch() { this.searchForm.pageNumber = 1; - this.searchForm.pageSize = 20; this.getDataList(); this.getOrderNumData(); }, + handleReset() { + const pageSize = this.searchForm.pageSize; + this.searchForm = emptySearchForm(pageSize); + this.selectDate = null; + this.currentStatus = "ALL"; + sessionStorage.removeItem(QUERY_KEY); + this.init(); + }, selectDateRange(v) { if (v && v.length === 2) { this.searchForm.startDate = v[0]; @@ -330,17 +394,26 @@ export default { this.searchForm.endDate = ""; } }, + countParams() { + const params = { ...this.searchForm }; + delete params.orderStatus; + delete params.pageNumber; + delete params.pageSize; + delete params.sort; + delete params.order; + return params; + }, getDataList() { this.loading = true; + this.rememberQuery(); API_Order.getOrderList(this.searchForm) .then((res) => { - this.loading = false; if (res.success) { this.data = res.result.records; this.total = res.result.total; } }) - .catch(() => { + .finally(() => { this.loading = false; }); }, @@ -352,29 +425,36 @@ export default { }, async exportOrder() { if (!this.searchForm.startDate || !this.searchForm.endDate) { - ElMessage.error("必须选择时间范围,搜索后进行导出!"); - return; + try { + await ElMessageBox.confirm( + "未选择下单时间,将按当前筛选条件导出全部匹配订单。", + "导出订单", + { type: "warning", confirmButtonText: "继续导出", cancelButtonText: "取消" } + ); + } catch (e) { + return; + } } + this.exporting = true; API_Order.exportOrder(this.searchForm) .then((res) => { const blob = new Blob([res], { type: "application/vnd.ms-excel;charset=utf-8", }); - if ("download" in document.createElement("a")) { - const link = document.createElement("a"); - link.download = "订单列表.xlsx"; - link.style.display = "none"; - link.href = URL.createObjectURL(blob); - document.body.appendChild(link); - link.click(); - URL.revokeObjectURL(link.href); - document.body.removeChild(link); - } else { - navigator.msSaveBlob(blob, "订单列表.xlsx"); - } + const link = document.createElement("a"); + link.download = "订单列表.xlsx"; + link.style.display = "none"; + link.href = URL.createObjectURL(blob); + document.body.appendChild(link); + link.click(); + URL.revokeObjectURL(link.href); + document.body.removeChild(link); }) - .catch((err) => { - console.log(err); + .catch(() => { + ElMessage.error("导出失败"); + }) + .finally(() => { + this.exporting = false; }); }, orderStatusClick(name) { @@ -385,22 +465,24 @@ export default { this.searchForm.orderStatus = name; this.currentStatus = name; } + this.searchForm.pageNumber = 1; this.getDataList(); }, getOrderNumData() { - // orderNum 接口仅查 li_order,不含 order_item 关联;keywords/goodsName 会引用 oi 字段导致 SQL 报错 - const { orderStatus, keywords, goodsName, ...searchParams } = this.searchForm; - API_Order.getOrderNum(searchParams) + API_Order.getOrderNum(this.countParams()) .then((res) => { if (res.success) { this.orderNumData = res.result; } }) - .catch((err) => { - console.error("获取订单数量统计失败:", err); + .catch(() => { + this.orderNumData = {}; }); }, }, + created() { + this.restoreQuery(); + }, mounted() { this.init(); }, @@ -408,6 +490,37 @@ export default { diff --git a/manager/src/views/procurement/purchase-order/list.vue b/manager/src/views/procurement/purchase-order/list.vue index 7146f804e..b173beab9 100644 --- a/manager/src/views/procurement/purchase-order/list.vue +++ b/manager/src/views/procurement/purchase-order/list.vue @@ -69,6 +69,10 @@ export default { }; }, mounted() { + const status = this.$route.query.status; + if (status) { + this.searchForm.status = status; + } this.getDataList(); }, methods: {