if (typeof Promise !== "undefined" && !Promise.prototype.finally) { Promise.prototype.finally = function(callback) { const promise = this.constructor; return this.then( (value) => promise.resolve(callback()).then(() => value), (reason) => promise.resolve(callback()).then(() => { throw reason; }) ); }; } ; if (typeof uni !== "undefined" && uni && uni.requireGlobal) { const global = uni.requireGlobal(); ArrayBuffer = global.ArrayBuffer; Int8Array = global.Int8Array; Uint8Array = global.Uint8Array; Uint8ClampedArray = global.Uint8ClampedArray; Int16Array = global.Int16Array; Uint16Array = global.Uint16Array; Int32Array = global.Int32Array; Uint32Array = global.Uint32Array; Float32Array = global.Float32Array; Float64Array = global.Float64Array; BigInt64Array = global.BigInt64Array; BigUint64Array = global.BigUint64Array; } ; if (uni.restoreGlobal) { uni.restoreGlobal(Vue, weex, plus, setTimeout, clearTimeout, setInterval, clearInterval); } (function(vue) { "use strict"; function formatAppLog(type, filename, ...args) { if (uni.__log__) { uni.__log__(type, filename, ...args); } else { console[type].apply(console, [...args, filename]); } } var define_process_env_default = {}; const envBaseUrl = typeof process !== "undefined" && define_process_env_default && (define_process_env_default.VITE_APP_API_BASE_URL || define_process_env_default.API_BASE_URL) || ""; const storageBaseUrl = typeof uni !== "undefined" ? uni.getStorageSync("API_BASE_URL") || "" : ""; const fallbackBaseUrl = "http://127.0.0.1:8080"; const API_BASE_URL = (envBaseUrl || storageBaseUrl || fallbackBaseUrl).replace(/\/$/, ""); const candidateBases = [envBaseUrl, storageBaseUrl, fallbackBaseUrl, "http://127.0.0.1:8080", "http://localhost:8080"]; const API_BASE_URL_CANDIDATES = Array.from(new Set(candidateBases.filter(Boolean))).map((u) => String(u).replace(/\/$/, "")); const envShopId = typeof process !== "undefined" && define_process_env_default && (define_process_env_default.VITE_APP_SHOP_ID || define_process_env_default.SHOP_ID) || ""; const storageShopId = typeof uni !== "undefined" ? uni.getStorageSync("SHOP_ID") || "" : ""; const SHOP_ID = Number(envShopId || storageShopId || 1); const envEnableDefaultUser = typeof process !== "undefined" && define_process_env_default && (define_process_env_default.VITE_APP_ENABLE_DEFAULT_USER || define_process_env_default.ENABLE_DEFAULT_USER) || ""; const storageEnableDefaultUser = typeof uni !== "undefined" ? uni.getStorageSync("ENABLE_DEFAULT_USER") || "" : ""; const ENABLE_DEFAULT_USER = String(envEnableDefaultUser || storageEnableDefaultUser || "false").toLowerCase() === "true"; const envDefaultUserId = typeof process !== "undefined" && define_process_env_default && (define_process_env_default.VITE_APP_DEFAULT_USER_ID || define_process_env_default.DEFAULT_USER_ID) || ""; const storageDefaultUserId = typeof uni !== "undefined" ? uni.getStorageSync("DEFAULT_USER_ID") || "" : ""; const DEFAULT_USER_ID = Number(envDefaultUserId || storageDefaultUserId || 0); typeof process !== "undefined" && define_process_env_default && (define_process_env_default.VITE_APP_VIP_PRICE || define_process_env_default.VIP_PRICE) || ""; typeof uni !== "undefined" ? uni.getStorageSync("VIP_PRICE") || "" : ""; typeof process !== "undefined" && define_process_env_default && (define_process_env_default.VITE_APP_HOME_BANNER_IMG || define_process_env_default.HOME_BANNER_IMG) || ""; typeof uni !== "undefined" ? uni.getStorageSync("HOME_BANNER_IMG") || "" : ""; const KPI_ICONS = { todaySales: "/static/icons/webwxgetmsgimg.jpg", monthSales: "/static/icons/webwxgetmsgimg.jpg", monthProfit: "/static/icons/icons8-profit-50.png", stockCount: "/static/icons/product.png" }; const envAuthLoginImg = typeof process !== "undefined" && define_process_env_default && (define_process_env_default.VITE_APP_AUTH_LOGIN_TOP_IMAGE || define_process_env_default.AUTH_LOGIN_TOP_IMAGE) || ""; const storageAuthLoginImg = typeof uni !== "undefined" ? uni.getStorageSync("AUTH_LOGIN_TOP_IMAGE") || "" : ""; const AUTH_LOGIN_TOP_IMAGE = String(envAuthLoginImg || storageAuthLoginImg || "/static/icons/undraw_visual-data_1eya.png"); function buildUrl(path) { if (!path) return API_BASE_URL; if (path.startsWith("http")) return path; return API_BASE_URL + (path.startsWith("/") ? path : "/" + path); } function parseJwtClaims(token) { try { const parts = String(token || "").split("."); if (parts.length < 2) return {}; const payload = JSON.parse(decodeURIComponent(escape(atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"))))); return payload || {}; } catch (_) { return {}; } } function buildAuthHeaders(base = {}) { const headers = { ...base }; try { const token = typeof uni !== "undefined" ? uni.getStorageSync("TOKEN") || "" : ""; if (token) { headers["Authorization"] = `Bearer ${token}`; const claims = parseJwtClaims(token); if (claims && claims.shopId) headers["X-Shop-Id"] = claims.shopId; if (claims && claims.userId) headers["X-User-Id"] = claims.userId; } else if (ENABLE_DEFAULT_USER && DEFAULT_USER_ID) { if (headers["Authorization"]) delete headers["Authorization"]; headers["X-User-Id"] = headers["X-User-Id"] || DEFAULT_USER_ID; if (SHOP_ID) headers["X-Shop-Id"] = headers["X-Shop-Id"] || SHOP_ID; } } catch (_) { if (ENABLE_DEFAULT_USER && DEFAULT_USER_ID) { headers["X-User-Id"] = headers["X-User-Id"] || DEFAULT_USER_ID; if (SHOP_ID) headers["X-Shop-Id"] = headers["X-Shop-Id"] || SHOP_ID; } } return headers; } function requestWithFallback(options, candidates, idx, resolve, reject) { const base = candidates[idx] || API_BASE_URL; let url = options.url; if (!/^https?:\/\//.test(url)) { url = base + (url.startsWith("/") ? "" : "/") + url; } else { url = options.url.replace(/^https?:\/\/[^/]+/, base); } uni.request({ ...options, url, dataType: "json", success: (res) => { const { statusCode, data } = res; if (statusCode >= 200 && statusCode < 300) return resolve(data); const msg = data && (data.message || data.error || data.msg) || "HTTP " + statusCode; uni.showToast({ title: msg, icon: "none" }); if (!options.__noRetry && statusCode >= 500 && idx + 1 < candidates.length) { return requestWithFallback(options, candidates, idx + 1, resolve, reject); } reject(new Error(msg)); }, fail: (err) => { if (!options.__noRetry && idx + 1 < candidates.length) return requestWithFallback(options, candidates, idx + 1, resolve, reject); reject(err); } }); } function get(path, params = {}) { return new Promise((resolve, reject) => { const headers = buildAuthHeaders({}); const options = { url: buildUrl(path), method: "GET", data: params, header: headers }; requestWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject); }); } function post(path, body = {}) { return new Promise((resolve, reject) => { const headers = buildAuthHeaders({ "Content-Type": "application/json" }); const options = { url: buildUrl(path), method: "POST", data: body, header: headers }; const p = String(path || ""); if (p.includes("/api/auth/wxmp/login") || p.includes("/api/auth/sms/login") || p.includes("/api/auth/sms/send") || p.includes("/api/auth/email/login") || p.includes("/api/auth/email/send") || p.includes("/api/auth/password/login") || p.includes("/api/auth/register")) options.__noRetry = true; requestWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject); }); } function put(path, body = {}) { return new Promise((resolve, reject) => { const headers = buildAuthHeaders({ "Content-Type": "application/json" }); const options = { url: buildUrl(path), method: "PUT", data: body, header: headers }; requestWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject); }); } function del(path, body = {}) { return new Promise((resolve, reject) => { const headers = buildAuthHeaders({ "Content-Type": "application/json" }); const options = { url: buildUrl(path), method: "DELETE", data: body, header: headers }; requestWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject); }); } function uploadWithFallback(options, candidates, idx, resolve, reject) { const base = candidates[idx] || API_BASE_URL; const url = options.url.replace(/^https?:\/\/[^/]+/, base); const uploadOptions = { ...options, url }; uni.uploadFile({ ...uploadOptions, success: (res) => { const statusCode = res.statusCode || 0; if (statusCode >= 200 && statusCode < 300) { try { const data = typeof res.data === "string" ? JSON.parse(res.data) : res.data; return resolve(data); } catch (e) { return resolve(res.data); } } if (statusCode >= 400 && statusCode < 500) { try { const data = typeof res.data === "string" ? JSON.parse(res.data) : res.data; return resolve(data); } catch (e) { return resolve({ success: false, message: "HTTP " + statusCode }); } } if (idx + 1 < candidates.length) return uploadWithFallback(options, candidates, idx + 1, resolve, reject); try { const data = typeof res.data === "string" ? JSON.parse(res.data) : res.data; return resolve(data); } catch (e) { return resolve({ success: false, message: "HTTP " + statusCode }); } }, fail: (err) => { if (idx + 1 < candidates.length) return uploadWithFallback(options, candidates, idx + 1, resolve, reject); reject(err); } }); } function upload(path, filePath, formData = {}, name = "file") { return new Promise((resolve, reject) => { const header = buildAuthHeaders({}); const options = { url: buildUrl(path), filePath, name, formData, header }; uploadWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject); }); } const INCOME_CATEGORIES = [ { key: "sale_income", label: "销售收入" }, { key: "operation_income", label: "经营所得" }, { key: "interest_income", label: "利息收入" }, { key: "investment_income", label: "投资收入" }, { key: "other_income", label: "其它收入" } ]; const EXPENSE_CATEGORIES = [ { key: "operation_expense", label: "经营支出" }, { key: "office_supplies", label: "办公用品" }, { key: "rent", label: "房租" }, { key: "interest_expense", label: "利息支出" }, { key: "other_expense", label: "其它支出" } ]; const ROUTES = { home: "/pages/index/index", productList: "/pages/product/list", productForm: "/pages/product/form", productSelect: "/pages/product/select", productSettings: "/pages/product/settings", orderCreate: "/pages/order/create", detail: "/pages/detail/index", my: "/pages/my/index", myAbout: "/pages/my/about", myVip: "/pages/my/vip", report: "/pages/report/index", customerSelect: "/pages/customer/select", supplierSelect: "/pages/supplier/select", accountSelect: "/pages/account/select" }; const KPI_LABELS = { todaySales: "今日销售额", monthSales: "本月销售额", monthProfit: "本月利润", stockCount: "库存量" }; const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const _sfc_main$u = { data() { return { KPI_ICONS, KPI_LABELS, kpi: { todaySales: "0.00", monthSales: "0.00", monthProfit: "0.00", stockCount: "0" }, activeTab: "home", notices: [], loadingNotices: false, noticeError: "", consultLabel: "咨询", consultDialogVisible: false, consultMessage: "", features: [ { key: "product", title: "货品", img: "/static/icons/product.png", emoji: "📦" }, { key: "customer", title: "客户", img: "/static/icons/webwxgetmsgimg.png", emoji: "👥" }, { key: "sale", title: "销售", img: "/static/icons/webwxgetmsgimg.jpg", emoji: "💰" }, { key: "account", title: "账户", img: "/static/icons/icons8-profile-50.png", emoji: "💳" }, { key: "supplier", title: "供应商", img: "/static/icons/icons8-supplier-50.png", emoji: "🚚" }, { key: "purchase", title: "进货", img: "/static/icons/icons8-dollar-ethereum-exchange-50.png", emoji: "🛒" }, { key: "otherPay", title: "其他支出", img: "/static/icons/icons8-expenditure-64.png", emoji: "💸" }, { key: "vip", title: "VIP会员", img: "/static/icons/icons8-vip-48.png", emoji: "👑" }, { key: "report", title: "报表", img: "/static/icons/icons8-graph-report-50.png", emoji: "📊" } ] }; }, onLoad() { const hasToken = (() => { try { return !!uni.getStorageSync("TOKEN"); } catch (e) { return false; } })(); if (!hasToken) { this.kpi = { todaySales: "0.00", monthSales: "0.00", monthProfit: "0.00", stockCount: "0" }; this.notices = []; uni.showToast({ title: "请登录使用该功能", icon: "none" }); return; } this.fetchMetrics(); this.fetchNotices(); this.fetchLatestConsult(); }, methods: { async fetchMetrics() { try { const d = await get("/api/dashboard/overview"); const toNum = (v) => typeof v === "number" ? v : Number(v || 0); this.kpi = { ...this.kpi, todaySales: toNum(d && d.todaySalesAmount).toFixed(2), monthSales: toNum(d && d.monthSalesAmount).toFixed(2), monthProfit: toNum(d && d.monthGrossProfit).toFixed(2), stockCount: String((d && d.stockTotalQuantity) != null ? d.stockTotalQuantity : 0) }; } catch (e) { } }, async fetchLatestConsult() { try { const d = await get("/api/consults"); if (d && d.replied) this.consultLabel = "已回复"; else this.consultLabel = "咨询"; this._latestConsult = d; } catch (e) { this.consultLabel = "咨询"; } }, onConsultTap() { if (this.consultLabel === "已回复" && this._latestConsult && this._latestConsult.id) { const msg = this._latestConsult.latestReply ? this._latestConsult.latestReply : this._latestConsult.message || ""; uni.showModal({ title: "咨询回复", content: msg || "暂无内容", showCancel: false, success: async (res) => { if (!res || res.confirm !== true) return; try { const r = await put(`/api/consults/${this._latestConsult.id}/ack`, {}); this.consultLabel = "咨询"; this._latestConsult = null; setTimeout(() => this.fetchLatestConsult(), 200); } catch (e) { try { uni.showToast({ title: e && e.message || "已读同步失败", icon: "none" }); } catch (_) { } } } }); return; } this.consultMessage = ""; this.consultDialogVisible = true; }, closeConsultDialog() { this.consultDialogVisible = false; }, async submitConsult() { const text = String(this.consultMessage || "").trim(); if (!text) { uni.showToast({ title: "请输入咨询内容", icon: "none" }); return; } try { await post("/api/consults", { message: text }); this.consultDialogVisible = false; uni.showToast({ title: "已提交", icon: "success" }); setTimeout(() => this.fetchLatestConsult(), 300); } catch (e) { uni.showToast({ title: e && e.message || "提交失败", icon: "none" }); } }, async fetchNotices() { this.loadingNotices = true; this.noticeError = ""; try { const list = await get("/api/notices"); this.notices = Array.isArray(list) ? list.map((n) => ({ text: n.content || n.title || "", tag: n.tag || "" })) : []; } catch (e) { this.noticeError = e && e.message || "公告加载失败"; } finally { this.loadingNotices = false; } }, onFeatureTap(item) { if (item.key === "product") { uni.switchTab({ url: "/pages/product/list" }); return; } if (item.key === "sale") { try { uni.setStorageSync("ORDER_DEFAULT_PARAMS", { biz: "sale", type: "out" }); } catch (e) { } uni.switchTab({ url: "/pages/order/create" }); return; } if (item.key === "customer") { uni.navigateTo({ url: "/pages/customer/select" }); return; } if (item.key === "account") { uni.navigateTo({ url: "/pages/account/select" }); return; } if (item.key === "supplier") { uni.navigateTo({ url: "/pages/supplier/select" }); return; } if (item.key === "purchase") { try { uni.setStorageSync("ORDER_DEFAULT_PARAMS", { biz: "purchase", type: "in" }); } catch (e) { } uni.switchTab({ url: "/pages/order/create" }); return; } if (item.key === "report") { uni.navigateTo({ url: ROUTES.report }); return; } if (item.key === "vip") { uni.navigateTo({ url: "/pages/my/vip" }); return; } if (item.key === "otherPay") { try { uni.setStorageSync("ORDER_DEFAULT_PARAMS", { biz: "expense" }); } catch (e) { } uni.switchTab({ url: "/pages/order/create" }); return; } uni.showToast({ title: item.title + "(开发中)", icon: "none" }); }, goProduct() { uni.switchTab({ url: "/pages/product/list" }); }, onCreateOrder() { uni.switchTab({ url: "/pages/order/create" }); }, goDetail() { try { formatAppLog("log", "at pages/index/index.vue:259", "[index] goDetail → /pages/detail/index"); } catch (e) { } uni.switchTab({ url: "/pages/detail/index" }); }, goMe() { uni.switchTab({ url: "/pages/my/index" }); }, onNoticeTap(n) { uni.showModal({ title: "广告", content: n && (n.text || n.title || n.content) || "", showCancel: false }); }, onIconError(item) { item.img = ""; } } }; function _sfc_render$t(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "home" }, [ vue.createCommentVNode(" 顶部统计卡片 "), vue.createElementVNode("view", { class: "hero" }, [ vue.createElementVNode("view", { class: "hero-top" }, [ vue.createElementVNode("text", { class: "brand" }, "五金配件管家"), vue.createElementVNode("view", { class: "cta", onClick: _cache[0] || (_cache[0] = (...args) => $options.onConsultTap && $options.onConsultTap(...args)), "hover-class": "cta-active", "hover-stay-time": "80" }, [ vue.createElementVNode( "text", { class: "cta-text" }, vue.toDisplayString($data.consultLabel), 1 /* TEXT */ ) ]) ]), vue.createElementVNode("view", { class: "kpi kpi-grid" }, [ vue.createElementVNode("view", { class: "kpi-item kpi-card" }, [ vue.createElementVNode("image", { src: $data.KPI_ICONS.todaySales, class: "kpi-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("view", { class: "kpi-content" }, [ vue.createElementVNode( "text", { class: "kpi-label" }, vue.toDisplayString($data.KPI_LABELS.todaySales), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "kpi-value" }, vue.toDisplayString($data.kpi.todaySales), 1 /* TEXT */ ) ]) ]), vue.createElementVNode("view", { class: "kpi-item kpi-card" }, [ vue.createElementVNode("image", { src: $data.KPI_ICONS.monthSales, class: "kpi-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("view", { class: "kpi-content" }, [ vue.createElementVNode( "text", { class: "kpi-label" }, vue.toDisplayString($data.KPI_LABELS.monthSales), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "kpi-value" }, vue.toDisplayString($data.kpi.monthSales), 1 /* TEXT */ ) ]) ]), vue.createElementVNode("view", { class: "kpi-item kpi-card" }, [ vue.createElementVNode("image", { src: $data.KPI_ICONS.monthProfit, class: "kpi-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("view", { class: "kpi-content" }, [ vue.createElementVNode( "text", { class: "kpi-label" }, vue.toDisplayString($data.KPI_LABELS.monthProfit), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "kpi-value" }, vue.toDisplayString($data.kpi.monthProfit), 1 /* TEXT */ ) ]) ]), vue.createElementVNode("view", { class: "kpi-item kpi-card" }, [ vue.createElementVNode("image", { src: $data.KPI_ICONS.stockCount, class: "kpi-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("view", { class: "kpi-content" }, [ vue.createElementVNode( "text", { class: "kpi-label" }, vue.toDisplayString($data.KPI_LABELS.stockCount), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "kpi-value" }, vue.toDisplayString($data.kpi.stockCount), 1 /* TEXT */ ) ]) ]) ]) ]), vue.createCommentVNode(" 咨询输入弹层 "), $data.consultDialogVisible ? (vue.openBlock(), vue.createElementBlock( "view", { key: 0, class: "dialog-mask", onTouchmove: _cache[4] || (_cache[4] = vue.withModifiers(() => { }, ["stop", "prevent"])), onClick: _cache[5] || (_cache[5] = vue.withModifiers(() => { }, ["stop"])) }, [ vue.createElementVNode("view", { class: "dialog" }, [ vue.createElementVNode("view", { class: "dialog-title" }, "咨询"), vue.withDirectives(vue.createElementVNode( "textarea", { class: "dialog-textarea", "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.consultMessage = $event), placeholder: "请输入咨询内容...", maxlength: "500" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.consultMessage] ]), vue.createElementVNode("view", { class: "dialog-actions" }, [ vue.createElementVNode("view", { class: "btn", onClick: _cache[2] || (_cache[2] = (...args) => $options.closeConsultDialog && $options.closeConsultDialog(...args)) }, "取消"), vue.createElementVNode("view", { class: "btn primary", onClick: _cache[3] || (_cache[3] = (...args) => $options.submitConsult && $options.submitConsult(...args)) }, "提交") ]) ]) ], 32 /* NEED_HYDRATION */ )) : vue.createCommentVNode("v-if", true), vue.createCommentVNode(" 公告栏:放在常用功能上方、KPI 下方 "), vue.createElementVNode("view", { class: "notice" }, [ vue.createElementVNode("view", { class: "notice-left" }, "公告"), $data.loadingNotices ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "notice-swiper", style: { "display": "flex", "align-items": "center", "color": "#6b5a2a" } }, "加载中...")) : $data.noticeError ? (vue.openBlock(), vue.createElementBlock( "view", { key: 1, class: "notice-swiper", style: { "display": "flex", "align-items": "center", "color": "#dd524d" } }, vue.toDisplayString($data.noticeError), 1 /* TEXT */ )) : !$data.notices.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 2, class: "notice-swiper", style: { "display": "flex", "align-items": "center", "color": "#6b5a2a" } }, "暂无公告")) : (vue.openBlock(), vue.createElementBlock("swiper", { key: 3, class: "notice-swiper", circular: "", autoplay: "", interval: "4000", duration: "400", vertical: "" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.notices, (n, idx) => { return vue.openBlock(), vue.createElementBlock("swiper-item", { key: idx }, [ vue.createElementVNode("view", { class: "notice-item", onClick: ($event) => $options.onNoticeTap(n) }, [ vue.createElementVNode( "text", { class: "notice-text" }, vue.toDisplayString(n.text), 1 /* TEXT */ ), n.tag ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "notice-tag" }, vue.toDisplayString(n.tag), 1 /* TEXT */ )) : vue.createCommentVNode("v-if", true) ], 8, ["onClick"]) ]); }), 128 /* KEYED_FRAGMENT */ )) ])) ]), vue.createCommentVNode(" 分割标题:产品与功能 "), vue.createElementVNode("view", { class: "section-title" }, [ vue.createElementVNode("text", { class: "section-text" }, "常用功能") ]), vue.createCommentVNode(" 功能九宫格(玻璃容器 + 圆角方形图标) "), vue.createElementVNode("view", { class: "grid-wrap" }, [ vue.createElementVNode("view", { class: "feature-grid" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.features, (item) => { return vue.openBlock(), vue.createElementBlock("view", { class: "feature-card", key: item.key, onClick: ($event) => $options.onFeatureTap(item) }, [ vue.createElementVNode("view", { class: "fc-icon" }, [ item.img ? (vue.openBlock(), vue.createElementBlock("image", { key: 0, src: item.img, class: "fc-img", mode: "aspectFit", onError: ($event) => $options.onIconError(item) }, null, 40, ["src", "onError"])) : item.emoji ? (vue.openBlock(), vue.createElementBlock( "text", { key: 1, class: "fc-emoji" }, vue.toDisplayString(item.emoji), 1 /* TEXT */ )) : (vue.openBlock(), vue.createElementBlock("view", { key: 2, class: "fc-placeholder" })) ]), vue.createElementVNode( "view", { class: "fc-title" }, vue.toDisplayString(item.title), 1 /* TEXT */ ) ], 8, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ]) ]), vue.createCommentVNode(" 底部操作条改为原生 tabBar,移除自定义栏 ") ]); } const PagesIndexIndex = /* @__PURE__ */ _export_sfc(_sfc_main$u, [["render", _sfc_render$t], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/index/index.vue"]]); const _imports_0$4 = "/static/icons/icons8-shopping-cart-100.png"; function todayString() { const d = /* @__PURE__ */ new Date(); const m = (d.getMonth() + 1).toString().padStart(2, "0"); const day = d.getDate().toString().padStart(2, "0"); return `${d.getFullYear()}-${m}-${day}`; } const _sfc_main$t = { data() { return { biz: "sale", saleType: "out", purchaseType: "in", order: { orderTime: todayString(), customerId: null, supplierId: null, remark: "" }, customerName: "", customerPriceLevel: "零售价", _lastCustomerId: null, _priceCache: {}, supplierName: "", items: [], activeCategory: "sale_income", counterpartyType: "customer", trxAmount: 0, selectedAccountId: null, selectedAccountName: "", // 收款/付款输入 payments: { cash: 0, bank: 0, wechat: 0 }, showMore: false, SEG_ICONS: { sale: { out: "/static/icons/icons8-shopping-cart-100.png", return: "/static/icons/icons8-return-purchase-50.png", collect: "/static/icons/icons8-profit-50.png" }, purchase: { in: "/static/icons/icons8-purchase-order-100.png", return: "/static/icons/icons8-return-purchase-50.png", pay: "/static/icons/icons8-dollar-ethereum-exchange-50.png" } } }; }, computed: { totalQuantity() { return this.items.reduce((s, it) => s + Number(it.quantity || 0), 0); }, totalAmount() { return this.items.reduce((s, it) => s + Number(it.quantity || 0) * Number(it.unitPrice || 0), 0); }, customerLabel() { return this.customerName || "零售客户"; }, supplierLabel() { return this.supplierName || "零散供应商"; }, incomeCategories() { return this._incomeCategories || INCOME_CATEGORIES; }, expenseCategories() { return this._expenseCategories || EXPENSE_CATEGORIES; }, accountLabel() { return this.selectedAccountName || "现金"; }, counterpartyLabel() { return this.counterpartyType === "customer" ? this.customerName || "—" : this.supplierName || "—"; }, // 收款/付款合计 payTotal() { const p = this.payments || { cash: 0, bank: 0, wechat: 0 }; return Number(p.cash || 0) + Number(p.bank || 0) + Number(p.wechat || 0); } }, onLoad(query) { try { const preset = uni.getStorageSync("ORDER_DEFAULT_PARAMS") || {}; const biz = query && query.biz || preset.biz; const type = query && query.type || preset.type; if (biz === "sale" || biz === "purchase" || biz === "income" || biz === "expense") { this.biz = biz; } if (this.biz === "sale" && (type === "out" || type === "return" || type === "collect")) { this.saleType = type; } if (this.biz === "purchase" && (type === "in" || type === "return" || type === "pay")) { this.purchaseType = type; } try { uni.removeStorageSync("ORDER_DEFAULT_PARAMS"); } catch (_) { } } catch (e) { } this.fetchCategories(); }, onShow() { if (this.biz === "sale") { if (this.order.customerId && this.order.customerId !== this._lastCustomerId) { this.loadCustomerLevel(this.order.customerId).then(() => { this._lastCustomerId = this.order.customerId; for (const it of this.items) { if (it && (it._autoPrice || !it.unitPrice)) this.autoPriceItem(it); } }); } for (const it of this.items) { if (it && !it.unitPrice) this.autoPriceItem(it); } } }, methods: { fixMojibake(s) { try { if (!s) return s; const bad = /[ÂÃæåé¼½¢]/.test(s); if (!bad) return s; return decodeURIComponent(escape(s)); } catch (_) { return s; } }, normalizeCats(list) { if (!Array.isArray(list)) return []; return list.map((it) => ({ key: it && it.key || "", label: this.fixMojibake(it && it.label || "") })).filter((it) => it.key && it.label); }, async fetchCategories() { try { const res = await get("/api/finance/categories"); if (res && Array.isArray(res.incomeCategories)) this._incomeCategories = this.normalizeCats(res.incomeCategories); if (res && Array.isArray(res.expenseCategories)) this._expenseCategories = this.normalizeCats(res.expenseCategories); this.ensureActiveCategory(); } catch (_) { this.ensureActiveCategory(); } }, ensureActiveCategory() { const list = this.biz === "income" ? this.incomeCategories || [] : this.expenseCategories || []; if (!list.length) return; const exists = list.some((it) => it && it.key === this.activeCategory); if (!exists) this.activeCategory = list[0].key; }, async loadCustomerLevel(customerId) { try { const d = await get(`/api/customers/${customerId}`); this.customerPriceLevel = d && d.priceLevel ? d.priceLevel : "零售价"; } catch (e) { this.customerPriceLevel = "零售价"; } }, priceFieldForLevel() { const lvl = this.customerPriceLevel || "零售价"; if (lvl === "批发价") return "wholesalePrice"; if (lvl === "大单报价") return "bigClientPrice"; return "retailPrice"; }, async autoPriceItem(it) { if (this.biz !== "sale") return; if (!it || !it.productId) return; const pid = it.productId; let detail = this._priceCache[pid]; if (!detail) { try { detail = await get(`/api/products/${pid}`); this._priceCache[pid] = detail; } catch (e) { return; } } const field = this.priceFieldForLevel(); let price = Number(detail && detail[field] != null ? detail[field] : 0); if (!price && field !== "retailPrice") { price = Number(detail && detail.retailPrice != null ? detail.retailPrice : 0); } it.unitPrice = price; it._autoPrice = true; this.recalc(); }, onPriceInput(it) { if (it) { it._autoPrice = false; this.recalc(); } }, switchBiz(type) { this.biz = type; this.ensureActiveCategory(); }, onDateChange(e) { this.order.orderTime = e.detail.value; }, chooseCustomer() { uni.navigateTo({ url: "/pages/customer/select" }); }, chooseSupplier() { uni.navigateTo({ url: "/pages/supplier/select" }); }, chooseProduct() { uni.navigateTo({ url: "/pages/product/select" }); }, chooseAccount() { uni.navigateTo({ url: "/pages/account/select?mode=pick" }); }, chooseCounterparty() { if (!(this.biz === "income" || this.biz === "expense")) return; if (this.counterpartyType === "customer") { uni.navigateTo({ url: "/pages/customer/select" }); } else { uni.navigateTo({ url: "/pages/supplier/select" }); } }, setCounterparty(t) { this.counterpartyType = t; this.ensureActiveCategory(); }, recalc() { this.$forceUpdate(); }, recalcPay() { this.$forceUpdate(); }, async submit() { const isSaleOrPurchase = this.biz === "sale" || this.biz === "purchase"; const isCollectOrPay = this.biz === "sale" && this.saleType === "collect" || this.biz === "purchase" && this.purchaseType === "pay"; const saleTypeValue = this.biz === "sale" ? "sale." + this.saleType : "purchase." + this.purchaseType; if (isSaleOrPurchase && !isCollectOrPay) { if (!this.items.length) { uni.showToast({ title: "请先选择商品", icon: "none" }); return; } const invalid = this.items.find((it) => !it.productId || Number(it.quantity || 0) <= 0); if (invalid) { uni.showToast({ title: "数量需大于0", icon: "none" }); return; } } const payload = isSaleOrPurchase ? isCollectOrPay ? [ { method: "cash", amount: Number(this.payments.cash || 0) }, { method: "bank", amount: Number(this.payments.bank || 0) }, { method: "wechat", amount: Number(this.payments.wechat || 0) } ].filter((p) => p.amount > 0) : { type: saleTypeValue, orderTime: this.order.orderTime, customerId: this.order.customerId, supplierId: this.order.supplierId, items: this.items.map((it) => ({ productId: it.productId, quantity: Number(it.quantity || 0), unitPrice: Number(it.unitPrice || 0) })), amount: this.totalAmount } : { type: this.biz, category: this.activeCategory, counterpartyType: this.counterpartyType, counterpartyId: this.counterpartyType === "customer" ? this.order.customerId || null : this.order.supplierId || null, accountId: this.selectedAccountId || null, amount: Number(this.trxAmount || 0), txTime: this.order.orderTime, remark: this.order.remark }; try { const url = isSaleOrPurchase ? isCollectOrPay ? `/api/payments/${this.biz}` : "/api/orders" : "/api/other-transactions"; await post(url, payload); uni.showToast({ title: "已保存", icon: "success" }); setTimeout(() => { uni.navigateBack(); }, 600); } catch (e) { uni.showToast({ title: e && e.message || "保存失败", icon: "none" }); } }, saveAndReset() { this.items = []; this.trxAmount = 0; this.order.remark = ""; this.payments = { cash: 0, bank: 0, wechat: 0 }; } } }; function _sfc_render$s(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "order" }, [ vue.createCommentVNode(" 顶部 Tab "), vue.createElementVNode("view", { class: "tabs" }, [ vue.createElementVNode( "text", { class: vue.normalizeClass({ active: $data.biz === "sale" }), onClick: _cache[0] || (_cache[0] = ($event) => $options.switchBiz("sale")) }, "销售", 2 /* CLASS */ ), vue.createElementVNode( "text", { class: vue.normalizeClass({ active: $data.biz === "purchase" }), onClick: _cache[1] || (_cache[1] = ($event) => $options.switchBiz("purchase")) }, "进货", 2 /* CLASS */ ), vue.createElementVNode( "text", { class: vue.normalizeClass({ active: $data.biz === "income" }), onClick: _cache[2] || (_cache[2] = ($event) => $options.switchBiz("income")) }, "其他收入", 2 /* CLASS */ ), vue.createElementVNode( "text", { class: vue.normalizeClass({ active: $data.biz === "expense" }), onClick: _cache[3] || (_cache[3] = ($event) => $options.switchBiz("expense")) }, "其他支出", 2 /* CLASS */ ) ]), vue.createCommentVNode(" 子类目:改为分段式圆角胶囊,与需求截图一致 "), $data.biz === "sale" ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "seg3" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["seg3-item", $data.saleType === "out" && "active"]), onClick: _cache[4] || (_cache[4] = ($event) => $data.saleType = "out") }, [ vue.createElementVNode("image", { src: $data.SEG_ICONS.sale.out, class: "seg3-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("text", null, "出货") ], 2 /* CLASS */ ), vue.createElementVNode( "view", { class: vue.normalizeClass(["seg3-item", $data.saleType === "return" && "active"]), onClick: _cache[5] || (_cache[5] = ($event) => $data.saleType = "return") }, [ vue.createElementVNode("image", { src: $data.SEG_ICONS.sale.return, class: "seg3-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("text", null, "退货") ], 2 /* CLASS */ ), vue.createElementVNode( "view", { class: vue.normalizeClass(["seg3-item", $data.saleType === "collect" && "active"]), onClick: _cache[6] || (_cache[6] = ($event) => $data.saleType = "collect") }, [ vue.createElementVNode("image", { src: $data.SEG_ICONS.sale.collect, class: "seg3-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("text", null, "收款") ], 2 /* CLASS */ ) ])) : $data.biz === "purchase" ? (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "seg3" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["seg3-item", $data.purchaseType === "in" && "active"]), onClick: _cache[7] || (_cache[7] = ($event) => $data.purchaseType = "in") }, [ vue.createElementVNode("image", { src: $data.SEG_ICONS.purchase.in, class: "seg3-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("text", null, "进货") ], 2 /* CLASS */ ), vue.createElementVNode( "view", { class: vue.normalizeClass(["seg3-item", $data.purchaseType === "return" && "active"]), onClick: _cache[8] || (_cache[8] = ($event) => $data.purchaseType = "return") }, [ vue.createElementVNode("image", { src: $data.SEG_ICONS.purchase.return, class: "seg3-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("text", null, "退货") ], 2 /* CLASS */ ), vue.createElementVNode( "view", { class: vue.normalizeClass(["seg3-item", $data.purchaseType === "pay" && "active"]), onClick: _cache[9] || (_cache[9] = ($event) => $data.purchaseType = "pay") }, [ vue.createElementVNode("image", { src: $data.SEG_ICONS.purchase.pay, class: "seg3-icon", mode: "aspectFit" }, null, 8, ["src"]), vue.createElementVNode("text", null, "付款") ], 2 /* CLASS */ ) ])) : vue.createCommentVNode("v-if", true), vue.createCommentVNode(" 日期与客户 "), vue.createElementVNode("picker", { mode: "date", value: $data.order.orderTime, onChange: _cache[10] || (_cache[10] = (...args) => $options.onDateChange && $options.onDateChange(...args)) }, [ vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "时间"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.order.orderTime), 1 /* TEXT */ ) ]) ], 40, ["value"]), $data.biz === "sale" ? (vue.openBlock(), vue.createElementBlock("view", { key: 2, class: "field", onClick: _cache[11] || (_cache[11] = (...args) => $options.chooseCustomer && $options.chooseCustomer(...args)) }, [ vue.createElementVNode("text", { class: "label" }, "客户"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.customerLabel), 1 /* TEXT */ ) ])) : $data.biz === "purchase" ? (vue.openBlock(), vue.createElementBlock("view", { key: 3, class: "field", onClick: _cache[12] || (_cache[12] = (...args) => $options.chooseSupplier && $options.chooseSupplier(...args)) }, [ vue.createElementVNode("text", { class: "label" }, "供应商"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.supplierLabel), 1 /* TEXT */ ) ])) : vue.createCommentVNode("v-if", true), vue.createCommentVNode(" 销售/进货:收款/付款 专用页面 "), $data.biz === "sale" && $data.saleType === "collect" || $data.biz === "purchase" && $data.purchaseType === "pay" ? (vue.openBlock(), vue.createElementBlock("view", { key: 4 }, [ vue.createCommentVNode(" 客户 / 供应商 "), $data.biz === "sale" ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "field", onClick: _cache[13] || (_cache[13] = (...args) => $options.chooseCustomer && $options.chooseCustomer(...args)) }, [ vue.createElementVNode("text", { class: "label" }, "客户"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.customerLabel), 1 /* TEXT */ ) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "field", onClick: _cache[14] || (_cache[14] = (...args) => $options.chooseSupplier && $options.chooseSupplier(...args)) }, [ vue.createElementVNode("text", { class: "label" }, "供应商"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.supplierLabel), 1 /* TEXT */ ) ])), vue.createCommentVNode(" 三种收付款方式 "), vue.createElementVNode("view", { class: "field pay-row" }, [ vue.createElementVNode("text", { class: "label" }, "现金"), vue.withDirectives(vue.createElementVNode( "input", { class: "pay-input", type: "digit", "onUpdate:modelValue": _cache[15] || (_cache[15] = ($event) => $data.payments.cash = $event), placeholder: "0.00", onInput: _cache[16] || (_cache[16] = ($event) => $options.recalcPay()) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [ vue.vModelText, $data.payments.cash, void 0, { number: true } ] ]) ]), vue.createElementVNode("view", { class: "field pay-row" }, [ vue.createElementVNode("text", { class: "label" }, "银行存款"), vue.withDirectives(vue.createElementVNode( "input", { class: "pay-input", type: "digit", "onUpdate:modelValue": _cache[17] || (_cache[17] = ($event) => $data.payments.bank = $event), placeholder: "0.00", onInput: _cache[18] || (_cache[18] = ($event) => $options.recalcPay()) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [ vue.vModelText, $data.payments.bank, void 0, { number: true } ] ]) ]), vue.createElementVNode("view", { class: "field pay-row" }, [ vue.createElementVNode("text", { class: "label" }, "微信"), vue.withDirectives(vue.createElementVNode( "input", { class: "pay-input", type: "digit", "onUpdate:modelValue": _cache[19] || (_cache[19] = ($event) => $data.payments.wechat = $event), placeholder: "0.00", onInput: _cache[20] || (_cache[20] = ($event) => $options.recalcPay()) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [ vue.vModelText, $data.payments.wechat, void 0, { number: true } ] ]) ]), vue.createElementVNode( "view", { class: "collapse-trigger", onClick: _cache[21] || (_cache[21] = ($event) => $data.showMore = !$data.showMore) }, vue.toDisplayString($data.showMore ? "收起" : ""), 1 /* TEXT */ ), vue.createCommentVNode(" 备注与日期 "), vue.createElementVNode("view", { class: "textarea" }, [ vue.createElementVNode( "view", { class: "amount-badge" }, "总金额:" + vue.toDisplayString($options.payTotal.toFixed(2)), 1 /* TEXT */ ), vue.withDirectives(vue.createElementVNode( "textarea", { "onUpdate:modelValue": _cache[22] || (_cache[22] = ($event) => $data.order.remark = $event), maxlength: "200", placeholder: "备注(最多输入200个字)" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.order.remark] ]), vue.createElementVNode("view", { class: "date-mini" }, [ vue.createElementVNode("picker", { mode: "date", value: $data.order.orderTime, onChange: _cache[23] || (_cache[23] = (...args) => $options.onDateChange && $options.onDateChange(...args)) }, [ vue.createElementVNode( "text", null, vue.toDisplayString($data.order.orderTime), 1 /* TEXT */ ) ], 40, ["value"]) ]) ]) ])) : $data.biz === "sale" || $data.biz === "purchase" ? (vue.openBlock(), vue.createElementBlock( vue.Fragment, { key: 5 }, [ vue.createCommentVNode(" 已选商品与合计(销售/进货 出入库) "), vue.createElementVNode("view", null, [ vue.createCommentVNode(" 快捷操作:加商品/选择客户(或供应商)/选择日期 → 不改功能,只换排版 "), vue.createElementVNode("view", { class: "info-card" }, [ vue.createElementVNode("view", { class: "info-field party-field", onClick: _cache[24] || (_cache[24] = ($event) => $data.biz === "sale" ? $options.chooseCustomer() : $options.chooseSupplier()) }, [ vue.createElementVNode( "text", { class: "info-label" }, vue.toDisplayString($data.biz === "sale" ? "客户" : "供应商"), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "info-value" }, vue.toDisplayString($data.biz === "sale" ? $options.customerLabel : $options.supplierLabel), 1 /* TEXT */ ) ]), vue.createElementVNode("picker", { class: "info-field time-field", mode: "date", value: $data.order.orderTime, onChange: _cache[25] || (_cache[25] = (...args) => $options.onDateChange && $options.onDateChange(...args)) }, [ vue.createElementVNode("view", null, [ vue.createElementVNode("text", { class: "info-label" }, "时间"), vue.createElementVNode( "text", { class: "info-value" }, vue.toDisplayString($data.order.orderTime), 1 /* TEXT */ ) ]) ], 40, ["value"]), vue.createElementVNode("button", { class: "info-action", onClick: _cache[26] || (_cache[26] = (...args) => $options.chooseProduct && $options.chooseProduct(...args)) }, [ vue.createElementVNode("image", { src: _imports_0$4, class: "info-icon", mode: "aspectFit" }), vue.createElementVNode("text", null, "加商品") ]) ]), vue.createElementVNode("view", { class: "summary" }, [ vue.createElementVNode( "text", { class: "sel" }, "选中货品(" + vue.toDisplayString($options.totalQuantity) + ")", 1 /* TEXT */ ), vue.createElementVNode( "view", { class: "total-pill" }, "合计: ¥ " + vue.toDisplayString($options.totalAmount.toFixed(2)), 1 /* TEXT */ ) ]), vue.createCommentVNode(" 加号添加商品 "), vue.createElementVNode("view", { class: "add", onClick: _cache[27] || (_cache[27] = (...args) => $options.chooseProduct && $options.chooseProduct(...args)) }, "+") ]) ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */ )) : (vue.openBlock(), vue.createElementBlock( vue.Fragment, { key: 6 }, [ vue.createCommentVNode(" 其它收入/支出 表单 "), vue.createElementVNode("view", null, [ vue.createCommentVNode(" 往来单位类型切换 "), vue.createElementVNode("view", { class: "subtabs" }, [ vue.createElementVNode( "button", { class: vue.normalizeClass(["subbtn", { active: $data.counterpartyType === "customer" }]), onClick: _cache[28] || (_cache[28] = ($event) => $options.setCounterparty("customer")) }, "客户", 2 /* CLASS */ ), vue.createElementVNode( "button", { class: vue.normalizeClass(["subbtn", { active: $data.counterpartyType === "supplier" }]), onClick: _cache[29] || (_cache[29] = ($event) => $options.setCounterparty("supplier")) }, "供应商", 2 /* CLASS */ ) ]), vue.createElementVNode("view", { class: "chips" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.biz === "income" ? $options.incomeCategories : $options.expenseCategories, (c) => { return vue.openBlock(), vue.createElementBlock("view", { key: c.key, class: vue.normalizeClass(["chip", { active: $data.activeCategory === c.key }]), onClick: ($event) => $data.activeCategory = c.key }, vue.toDisplayString(c.label), 11, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ]), vue.createElementVNode("view", { class: "field", onClick: _cache[30] || (_cache[30] = (...args) => $options.chooseCounterparty && $options.chooseCounterparty(...args)) }, [ vue.createElementVNode("text", { class: "label" }, "往来单位"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.counterpartyLabel), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "field", onClick: _cache[31] || (_cache[31] = (...args) => $options.chooseAccount && $options.chooseAccount(...args)) }, [ vue.createElementVNode("text", { class: "label" }, "结算账户"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.accountLabel), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "金额"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", type: "digit", "onUpdate:modelValue": _cache[32] || (_cache[32] = ($event) => $data.trxAmount = $event), placeholder: "0.00" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.trxAmount, void 0, { number: true } ] ]) ]), vue.createElementVNode("view", { class: "textarea" }, [ vue.withDirectives(vue.createElementVNode( "textarea", { "onUpdate:modelValue": _cache[33] || (_cache[33] = ($event) => $data.order.remark = $event), maxlength: "200", placeholder: "备注(最多输入200个字)" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.order.remark] ]) ]) ]) ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */ )), vue.createCommentVNode(" 购物车空态 "), !$data.items.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 7, class: "empty" }, [ vue.createElementVNode("text", { class: "empty-text" }, "购物车里空空如也"), vue.createElementVNode("text", { class: "empty-sub" }, "扫描或点击 “+” 选择商品吧") ])) : (vue.openBlock(), vue.createElementBlock( vue.Fragment, { key: 8 }, [ vue.createCommentVNode(" 商品列表 "), vue.createElementVNode("view", { class: "list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.items, (it, idx) => { return vue.openBlock(), vue.createElementBlock("view", { class: "row", key: idx }, [ vue.createElementVNode( "view", { class: "col name" }, vue.toDisplayString(it.productName), 1 /* TEXT */ ), vue.createElementVNode("view", { class: "col qty" }, [ vue.withDirectives(vue.createElementVNode("input", { type: "number", "onUpdate:modelValue": ($event) => it.quantity = $event, onInput: _cache[34] || (_cache[34] = ($event) => $options.recalc()) }, null, 40, ["onUpdate:modelValue"]), [ [ vue.vModelText, it.quantity, void 0, { number: true } ] ]) ]), vue.createElementVNode("view", { class: "col price" }, [ vue.withDirectives(vue.createElementVNode("input", { type: "number", "onUpdate:modelValue": ($event) => it.unitPrice = $event, onInput: ($event) => $options.onPriceInput(it) }, null, 40, ["onUpdate:modelValue", "onInput"]), [ [ vue.vModelText, it.unitPrice, void 0, { number: true } ] ]) ]), vue.createElementVNode( "view", { class: "col amount" }, "¥ " + vue.toDisplayString((Number(it.quantity) * Number(it.unitPrice)).toFixed(2)), 1 /* TEXT */ ) ]); }), 128 /* KEYED_FRAGMENT */ )) ]) ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */ )), vue.createCommentVNode(" 底部提交栏 "), vue.createElementVNode("view", { class: "bottom" }, [ vue.createElementVNode("button", { class: "ghost", onClick: _cache[35] || (_cache[35] = (...args) => $options.saveAndReset && $options.saveAndReset(...args)) }, "再记一笔"), vue.createElementVNode("button", { class: "primary", onClick: _cache[36] || (_cache[36] = (...args) => $options.submit && $options.submit(...args)) }, "保存") ]) ]); } const PagesOrderCreate = /* @__PURE__ */ _export_sfc(_sfc_main$t, [["render", _sfc_render$s], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/order/create.vue"]]); const _sfc_main$s = { data() { return { kw: "", products: [] }; }, onLoad() { this.search(); }, methods: { async search() { try { const res = await get("/api/products", { kw: this.kw, page: 1, size: 50 }); this.products = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; } catch (e) { uni.showToast({ title: "加载失败", icon: "none" }); } }, select(p) { const opener = getCurrentPages()[getCurrentPages().length - 2]; if (opener && opener.$vm && opener.$vm.items) { const initPrice = Number(p.retailPrice != null ? p.retailPrice : p.price || 0); opener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: initPrice, _autoPrice: true }); } uni.navigateBack(); } } }; function _sfc_render$r(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "search" }, [ vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.kw = $event), placeholder: "搜索商品名称/编码", onConfirm: _cache[1] || (_cache[1] = (...args) => $options.search && $options.search(...args)) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [vue.vModelText, $data.kw] ]), vue.createElementVNode("button", { size: "mini", onClick: _cache[2] || (_cache[2] = (...args) => $options.search && $options.search(...args)) }, "搜索") ]), vue.createElementVNode("scroll-view", { "scroll-y": "", class: "list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.products, (p) => { return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: p.id, onClick: ($event) => $options.select(p) }, [ vue.createElementVNode( "view", { class: "name" }, vue.toDisplayString(p.name), 1 /* TEXT */ ), vue.createElementVNode( "view", { class: "meta" }, vue.toDisplayString((p.brand || "") + " " + (p.model || "") + " " + (p.spec || "")) + " · 库存:" + vue.toDisplayString(p.stock ?? 0), 1 /* TEXT */ ) ], 8, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ]) ]); } const PagesProductSelect = /* @__PURE__ */ _export_sfc(_sfc_main$s, [["render", _sfc_render$r], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/select.vue"]]); const _sfc_main$r = { data() { return { items: [], query: { kw: "", page: 1, size: 20, categoryId: "", mode: "direct", templateId: "", params: {} }, finished: false, loading: false, tab: "all", categories: [], templates: [], paramValues: {} }; }, onLoad() { const hasToken = (() => { try { return !!uni.getStorageSync("TOKEN"); } catch (e) { return false; } })(); if (!hasToken) { this.items = []; this.categories = []; uni.showToast({ title: "请登录使用该功能", icon: "none" }); return; } this.fetchCategories(); this.reload(); }, onShow() { const hasToken = (() => { try { return !!uni.getStorageSync("TOKEN"); } catch (e) { return false; } })(); if (!hasToken) return; this.reload(); }, computed: { categoryNames() { return this.categories.map((c) => c.name); }, categoryLabel() { const c = this.categories.find((x) => String(x.id) === String(this.query.categoryId)); return c ? "类别:" + c.name : "选择类别"; }, modeLabel() { const map = { direct: "直接查询", nameLike: "名称模糊查询", template: "按模板参数查询" }; return map[this.query.mode] || "直接查询"; }, templateNames() { return this.templates.map((t) => t.name); }, templateLabel() { const t = this.templates.find((x) => String(x.id) === String(this.query.templateId)); return t ? "模板:" + t.name : "选择模板"; }, selectedTemplate() { return this.templates.find((t) => String(t.id) === String(this.query.templateId)) || null; }, selectedTemplateParams() { return this.selectedTemplate && Array.isArray(this.selectedTemplate.params) ? this.selectedTemplate.params : []; } }, methods: { switchTab(t) { this.tab = t; this.query.categoryId = ""; this.query.templateId = ""; this.paramValues = {}; this.reload(); }, onPickCategory(e) { const idx = Number(e.detail.value); const c = this.categories[idx]; this.query.categoryId = c ? c.id : ""; this.fetchTemplates(); }, onPickTemplate(e) { const idx = Number(e.detail.value); const t = this.templates[idx]; this.query.templateId = t ? t.id : ""; this.paramValues = {}; }, onPickParamEnumWrapper(p, e) { const idx = Number(e.detail.value); const arr = p.enumOptions || []; this.paramValues[p.fieldKey] = arr[idx]; }, onParamBoolChange(p, e) { var _a; this.paramValues[p.fieldKey] = ((_a = e == null ? void 0 : e.detail) == null ? void 0 : _a.value) ? true : false; }, onParamDateChange(p, e) { var _a; this.paramValues[p.fieldKey] = ((_a = e == null ? void 0 : e.detail) == null ? void 0 : _a.value) || ""; }, async fetchCategories() { try { const res = await get("/api/product-categories", {}); this.categories = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; } catch (_) { } }, async fetchTemplates() { try { const res = await get("/api/product-templates", this.query.categoryId ? { categoryId: this.query.categoryId } : {}); const list = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; this.templates = list; } catch (_) { this.templates = []; } }, reload() { this.items = []; this.query.page = 1; this.finished = false; this.loadMore(); }, async loadMore() { if (this.loading || this.finished) return; this.loading = true; try { const params = { kw: this.query.kw, page: this.query.page, size: this.query.size }; if (this.tab === "search") { if (this.query.categoryId) params.categoryId = this.query.categoryId; if (this.query.templateId) params.templateId = this.query.templateId; if (this.paramValues && Object.keys(this.paramValues).length) { for (const k of Object.keys(this.paramValues)) { const v = this.paramValues[k]; if (v !== void 0 && v !== null && v !== "") params["param_" + k] = v; } } } const res = await get("/api/products", params); const list = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; this.items = this.items.concat(list); if (list.length < this.query.size) this.finished = true; this.query.page += 1; } catch (e) { uni.showToast({ title: "加载失败", icon: "none" }); } finally { this.loading = false; } }, openDetail(id) { uni.navigateTo({ url: "/pages/product/product-detail?id=" + id }); }, goMySubmissions() { uni.navigateTo({ url: "/pages/product/submissions" }); }, async remove(it) { try { const r = await new Promise((resolve) => { uni.showModal({ content: "确认删除该货品?删除后可在后台恢复", success: resolve }); }); if (!r || !r.confirm) return; const { del: del2 } = require("../../common/http.js"); await del2("/api/products/" + it.id); uni.showToast({ title: "已删除", icon: "success" }); this.reload(); } catch (e) { uni.showToast({ title: "删除失败", icon: "none" }); } } } }; function _sfc_render$q(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "tabs" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["tab", { active: $data.tab === "all" }]), onClick: _cache[0] || (_cache[0] = ($event) => $options.switchTab("all")) }, "全部", 2 /* CLASS */ ), vue.createElementVNode( "view", { class: vue.normalizeClass(["tab", { active: $data.tab === "search" }]), onClick: _cache[1] || (_cache[1] = ($event) => $options.switchTab("search")) }, "查询", 2 /* CLASS */ ), vue.createElementVNode("view", { class: "tab extra", onClick: _cache[2] || (_cache[2] = (...args) => $options.goMySubmissions && $options.goMySubmissions(...args)) }, "我的提交") ]), $data.tab === "search" ? (vue.openBlock(), vue.createElementBlock( "view", { key: 0, class: vue.normalizeClass(["search", { "template-mode": $data.query.mode === "template" }]) }, [ vue.createElementVNode("view", { class: "mode" }, [ vue.createElementVNode( "picker", { mode: "selector", range: ["直接查询", "名称模糊查询", "按模板参数查询"], onChange: _cache[3] || (_cache[3] = (e) => $data.query.mode = ["direct", "nameLike", "template"][Number(e.detail.value)] || "direct") }, [ vue.createElementVNode( "view", { class: "picker" }, vue.toDisplayString($options.modeLabel), 1 /* TEXT */ ) ], 32 /* NEED_HYDRATION */ ) ]), $data.query.mode === "direct" || $data.query.mode === "nameLike" ? vue.withDirectives((vue.openBlock(), vue.createElementBlock( "input", { key: 0, "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => $data.query.kw = $event), placeholder: "输入名称/条码/规格查询", onConfirm: _cache[5] || (_cache[5] = (...args) => $options.reload && $options.reload(...args)) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ )), [ [ vue.vModelText, $data.query.kw, void 0, { trim: true } ] ]) : vue.createCommentVNode("v-if", true), $data.query.mode === "template" ? (vue.openBlock(), vue.createElementBlock( vue.Fragment, { key: 1 }, [ vue.createElementVNode("view", { class: "picker-row" }, [ vue.createElementVNode("picker", { mode: "selector", range: $options.categoryNames, onChange: _cache[6] || (_cache[6] = (...args) => $options.onPickCategory && $options.onPickCategory(...args)) }, [ vue.createElementVNode( "view", { class: "picker" }, vue.toDisplayString($options.categoryLabel), 1 /* TEXT */ ) ], 40, ["range"]), vue.createElementVNode("picker", { mode: "selector", range: $options.templateNames, onChange: _cache[7] || (_cache[7] = (...args) => $options.onPickTemplate && $options.onPickTemplate(...args)) }, [ vue.createElementVNode( "view", { class: "picker" }, vue.toDisplayString($options.templateLabel), 1 /* TEXT */ ) ], 40, ["range"]) ]), vue.createElementVNode("view", { class: "params-wrap" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($options.selectedTemplateParams, (p, idx) => { return vue.openBlock(), vue.createElementBlock("view", { class: "param-row", key: p.fieldKey }, [ p.type === "string" ? vue.withDirectives((vue.openBlock(), vue.createElementBlock("input", { key: 0, "onUpdate:modelValue": ($event) => $data.paramValues[p.fieldKey] = $event, placeholder: "输入" + p.fieldLabel }, null, 8, ["onUpdate:modelValue", "placeholder"])), [ [ vue.vModelText, $data.paramValues[p.fieldKey], void 0, { trim: true } ] ]) : p.type === "number" ? vue.withDirectives((vue.openBlock(), vue.createElementBlock("input", { key: 1, type: "number", "onUpdate:modelValue": ($event) => $data.paramValues[p.fieldKey] = $event, placeholder: "输入" + p.fieldLabel }, null, 8, ["onUpdate:modelValue", "placeholder"])), [ [ vue.vModelText, $data.paramValues[p.fieldKey], void 0, { number: true } ] ]) : p.type === "boolean" ? (vue.openBlock(), vue.createElementBlock("switch", { key: 2, checked: !!$data.paramValues[p.fieldKey], onChange: ($event) => $options.onParamBoolChange(p, $event) }, null, 40, ["checked", "onChange"])) : p.type === "enum" ? (vue.openBlock(), vue.createElementBlock("picker", { key: 3, mode: "selector", range: p.enumOptions || [], onChange: ($event) => $options.onPickParamEnumWrapper(p, $event) }, [ vue.createElementVNode( "view", { class: "picker" }, vue.toDisplayString(_ctx.displayParamEnum(p)), 1 /* TEXT */ ) ], 40, ["range", "onChange"])) : p.type === "date" ? (vue.openBlock(), vue.createElementBlock("picker", { key: 4, mode: "date", onChange: ($event) => $options.onParamDateChange(p, $event) }, [ vue.createElementVNode( "view", { class: "picker" }, vue.toDisplayString($data.paramValues[p.fieldKey] || "选择" + p.fieldLabel), 1 /* TEXT */ ) ], 40, ["onChange"])) : vue.withDirectives((vue.openBlock(), vue.createElementBlock("input", { key: 5, "onUpdate:modelValue": ($event) => $data.paramValues[p.fieldKey] = $event, placeholder: "输入" + p.fieldLabel }, null, 8, ["onUpdate:modelValue", "placeholder"])), [ [ vue.vModelText, $data.paramValues[p.fieldKey], void 0, { trim: true } ] ]) ]); }), 128 /* KEYED_FRAGMENT */ )) ]) ], 64 /* STABLE_FRAGMENT */ )) : vue.createCommentVNode("v-if", true), vue.createElementVNode("button", { size: "mini", onClick: _cache[8] || (_cache[8] = (...args) => $options.reload && $options.reload(...args)) }, "查询") ], 2 /* CLASS */ )) : vue.createCommentVNode("v-if", true), vue.createElementVNode( "scroll-view", { "scroll-y": "", class: "list", onScrolltolower: _cache[9] || (_cache[9] = (...args) => $options.loadMore && $options.loadMore(...args)) }, [ $data.items.length ? (vue.openBlock(true), vue.createElementBlock( vue.Fragment, { key: 0 }, vue.renderList($data.items, (it) => { return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: it.id, onClick: ($event) => $options.openDetail(it.id) }, [ it.cover ? (vue.openBlock(), vue.createElementBlock("image", { key: 0, src: it.cover, class: "thumb", mode: "aspectFill" }, null, 8, ["src"])) : vue.createCommentVNode("v-if", true), vue.createElementVNode("view", { class: "content" }, [ vue.createElementVNode("view", { class: "name" }, [ vue.createElementVNode( "text", null, vue.toDisplayString(it.name), 1 /* TEXT */ ), it.deleted ? (vue.openBlock(), vue.createElementBlock("text", { key: 0, class: "tag-deleted" }, "已删除")) : vue.createCommentVNode("v-if", true), it.platformStatus === "platform" ? (vue.openBlock(), vue.createElementBlock("text", { key: 1, class: "tag-platform" }, "平台推荐")) : it.sourceSubmissionId ? (vue.openBlock(), vue.createElementBlock("text", { key: 2, class: "tag-custom" }, "我的提交")) : vue.createCommentVNode("v-if", true) ]), vue.createElementVNode( "view", { class: "meta" }, vue.toDisplayString(it.brand || "-") + " " + vue.toDisplayString(it.model || "") + " " + vue.toDisplayString(it.spec || ""), 1 /* TEXT */ ), it.cardParams && Object.keys(it.cardParams).length ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "card-params" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList(it.cardParams, (v, k) => { return vue.openBlock(), vue.createElementBlock( "view", { class: "param", key: k }, vue.toDisplayString(k) + ":" + vue.toDisplayString(v), 1 /* TEXT */ ); }), 128 /* KEYED_FRAGMENT */ )) ])) : vue.createCommentVNode("v-if", true) ]) ], 8, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "empty" }, [ vue.createElementVNode("text", null, "暂无数据,点击右上角“+”新增") ])) ], 32 /* NEED_HYDRATION */ ), vue.createCommentVNode(" 保留“我的提交”页的+,此处不显示 ") ]); } const PagesProductList = /* @__PURE__ */ _export_sfc(_sfc_main$r, [["render", _sfc_render$q], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/list.vue"]]); const _imports_0$3 = "/static/icons/icons8-close-48.png"; const ITEM_SIZE = 210; const GAP = 18; const COLS = 3; function px(rpx) { return rpx; } const _sfc_main$q = { name: "ImageUploader", props: { modelValue: { type: Array, default: () => [] }, max: { type: Number, default: 9 }, uploadPath: { type: String, default: "/api/attachments" }, uploadFieldName: { type: String, default: "file" }, formData: { type: Object, default: () => ({ ownerType: "product" }) } }, data() { return { innerList: [] }; }, computed: { areaHeight() { const rows = Math.ceil((this.innerList.length + 1) / COLS) || 1; return rows * ITEM_SIZE + (rows - 1) * GAP; }, adderStyle() { const index = this.innerList.length; const row = Math.floor(index / COLS); const col = index % COLS; const x = px(col * (ITEM_SIZE + GAP)); const y = px(row * (ITEM_SIZE + GAP)); return { left: x + "rpx", top: y + "rpx" }; } }, watch: { modelValue: { immediate: true, handler(list) { const mapped = (list || []).map((u, i) => ({ uid: String(i) + "_" + (u.id || u.url || Math.random().toString(36).slice(2)), url: this.ensureAbsoluteUrl(typeof u === "string" ? u : u.url || ""), x: this.posOf(i).x, y: this.posOf(i).y })); this.innerList = mapped; } } }, methods: { ensureAbsoluteUrl(u) { if (!u) return ""; const s = String(u); if (s.startsWith("http://") || s.startsWith("https://")) return s; return API_BASE_URL + (s.startsWith("/") ? s : "/" + s); }, posOf(index) { const row = Math.floor(index / COLS); const col = index % COLS; return { x: px(col * (ITEM_SIZE + GAP)), y: px(row * (ITEM_SIZE + GAP)) }; }, cellStyle(index) { return { width: ITEM_SIZE + "rpx", height: ITEM_SIZE + "rpx" }; }, preview(index) { uni.previewImage({ urls: this.innerList.map((i) => i.url), current: index }); }, remove(index) { this.innerList.splice(index, 1); this.reflow(); this.emit(); }, choose() { const remain = this.max - this.innerList.length; if (remain <= 0) return; uni.chooseImage({ count: remain, success: async (res) => { for (const path of res.tempFilePaths) { await this.doUpload(path); } } }); }, async doUpload(filePath) { var _a; try { const resp = await upload(this.uploadPath, filePath, this.formData, this.uploadFieldName); const url = this.ensureAbsoluteUrl((resp == null ? void 0 : resp.url) || ((_a = resp == null ? void 0 : resp.data) == null ? void 0 : _a.url) || (resp == null ? void 0 : resp.path) || ""); if (!url) throw new Error("上传响应无 url"); this.innerList.push({ uid: Math.random().toString(36).slice(2), url, ...this.posOf(this.innerList.length) }); this.reflow(); this.emit(); } catch (e) { uni.showToast({ title: "上传失败", icon: "none" }); } }, onMoving(index, e) { const { x, y } = e.detail; this.innerList[index].x = x; this.innerList[index].y = y; }, onMoveEnd(index) { const mv = this.innerList[index]; const col = Math.round(mv.x / (ITEM_SIZE + GAP)); const row = Math.round(mv.y / (ITEM_SIZE + GAP)); let newIndex = row * COLS + col; newIndex = Math.max(0, Math.min(newIndex, this.innerList.length - 1)); if (newIndex !== index) { const moved = this.innerList.splice(index, 1)[0]; this.innerList.splice(newIndex, 0, moved); } this.reflow(); this.emit(); }, reflow() { this.innerList.forEach((it, i) => { const p = this.posOf(i); it.x = p.x; it.y = p.y; }); }, emit() { this.$emit("update:modelValue", this.innerList.map((i) => i.url)); this.$emit("change", this.innerList.map((i) => i.url)); } } }; function _sfc_render$p(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "uploader" }, [ vue.createElementVNode( "view", { class: "grid", style: vue.normalizeStyle({ height: $options.areaHeight + "rpx" }) }, [ vue.createElementVNode( "movable-area", { class: "area", style: vue.normalizeStyle({ height: $options.areaHeight + "rpx" }) }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.innerList, (img, index) => { return vue.openBlock(), vue.createElementBlock("movable-view", { key: img.uid, class: "cell", style: vue.normalizeStyle($options.cellStyle(index)), direction: "all", damping: 40, friction: 2, x: img.x, y: img.y, onChange: ($event) => $options.onMoving(index, $event), onTouchend: ($event) => $options.onMoveEnd(index) }, [ vue.createElementVNode("image", { src: img.url, mode: "aspectFill", class: "thumb", onClick: ($event) => $options.preview(index) }, null, 8, ["src", "onClick"]), vue.createElementVNode("image", { class: "remove", src: _imports_0$3, mode: "aspectFit", onClick: vue.withModifiers(($event) => $options.remove(index), ["stop"]) }, null, 8, ["onClick"]) ], 44, ["x", "y", "onChange", "onTouchend"]); }), 128 /* KEYED_FRAGMENT */ )), $data.innerList.length < $props.max ? (vue.openBlock(), vue.createElementBlock( "view", { key: 0, class: "adder", style: vue.normalizeStyle($options.adderStyle), onClick: _cache[0] || (_cache[0] = (...args) => $options.choose && $options.choose(...args)) }, [ vue.createElementVNode("text", null, "+") ], 4 /* STYLE */ )) : vue.createCommentVNode("v-if", true) ], 4 /* STYLE */ ) ], 4 /* STYLE */ ) ]); } const ImageUploader = /* @__PURE__ */ _export_sfc(_sfc_main$q, [["render", _sfc_render$p], ["__scopeId", "data-v-7bd1ddd2"], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/components/ImageUploader.vue"]]); const _sfc_main$p = { components: { ImageUploader }, data() { return { form: { model: "", brand: "", barcode: "", categoryId: "", templateId: "", externalCode: "", parameters: {}, images: [], remark: "", safeMin: null, safeMax: null }, templates: [], paramValues: {}, checking: false, parameterText: "", categories: [], submitting: false, paramPlaceholder: '可输入 JSON,如 {"颜色":"黑","材质":"钢"}' }; }, computed: { categoryNames() { return this.categories.map((c) => c.name); }, templateNames() { return this.templates.map((t) => t.name); }, categoryLabel() { const c = this.categories.find((x) => String(x.id) === String(this.form.categoryId)); return c ? c.name : "选择类别"; }, selectedTemplate() { return this.templates.find((t) => String(t.id) === String(this.form.templateId)); }, templateLabel() { const t = this.selectedTemplate; return t ? `${t.name}` : "选择模板"; } }, onLoad(options) { this.bootstrap(); if (options && options.prefill) { try { const data = JSON.parse(decodeURIComponent(options.prefill)); Object.assign(this.form, { model: data.model || "", brand: data.brand || "", barcode: data.barcode || "", categoryId: data.categoryId || "", remark: data.remark || "" }); if (data.parameters && typeof data.parameters === "object") { this.parameterText = JSON.stringify(data.parameters, null, 2); } } catch (_) { } } }, methods: { async bootstrap() { await Promise.all([this.fetchCategories()]); await this.fetchTemplates(); }, async fetchCategories() { try { const res = await get("/api/product-categories"); this.categories = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; } catch (_) { this.categories = []; } }, async fetchTemplates() { try { const res = await get("/api/product-templates", this.form.categoryId ? { categoryId: this.form.categoryId } : {}); const list = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; this.templates = list; } catch (_) { this.templates = []; } }, onPickCategory(e) { const idx = Number(e.detail.value); const c = this.categories[idx]; this.form.categoryId = c ? c.id : ""; this.fetchTemplates(); }, onPickTemplate(e) { const idx = Number(e.detail.value); const t = this.templates[idx]; this.form.templateId = t ? t.id : ""; this.paramValues = {}; }, onPickDate(p, e) { var _a; this.paramValues[p.fieldKey] = ((_a = e == null ? void 0 : e.detail) == null ? void 0 : _a.value) || ""; }, onPickEnum(p, e) { const idx = Number(e.detail.value); const arr = p.enumOptions || []; this.paramValues[p.fieldKey] = arr[idx]; }, displayEnum(p) { const v = this.paramValues[p.fieldKey]; return v === void 0 || v === null || v === "" ? "选择" + p.fieldLabel : String(v); }, async scanBarcode() { var _a; try { const chooseRes = await uni.chooseImage({ count: 1, sourceType: ["camera", "album"], sizeType: ["compressed"] }); let filePath = chooseRes.tempFilePaths[0]; try { const comp = await uni.compressImage({ src: filePath, quality: 80 }); filePath = comp.tempFilePath || filePath; } catch (_) { } const data = await upload("/api/barcode/scan", filePath, {}, "file"); const barcode = (data == null ? void 0 : data.barcode) || ((_a = data == null ? void 0 : data.data) == null ? void 0 : _a.barcode); if (barcode) { this.form.barcode = barcode; uni.showToast({ title: "识别成功", icon: "success" }); } else { uni.showToast({ title: "未识别到条码", icon: "none" }); } } catch (e) { const msg = (e == null ? void 0 : e.message) || "识码失败"; uni.showToast({ title: msg, icon: "none" }); } }, async checkModel() { if (!this.form.model) return uni.showToast({ title: "请填写型号", icon: "none" }); try { this.checking = true; const res = await post("/api/products/submissions/check-model", { templateId: this.form.templateId, model: this.form.model }); if (res && res.available) { uni.showToast({ title: "可用,无重复", icon: "success" }); } else { uni.showToast({ title: "已存在相同型号提交", icon: "none" }); } } catch (e) { const msg = (e == null ? void 0 : e.message) || "校验失败"; uni.showToast({ title: msg, icon: "none" }); } finally { this.checking = false; } }, async submit() { if (this.submitting) return; if (!this.form.model) { return uni.showToast({ title: "请填写型号", icon: "none" }); } let paramsObj = null; if (this.parameterText) { try { paramsObj = JSON.parse(this.parameterText); } catch (e) { return uni.showToast({ title: "参数 JSON 不合法", icon: "none" }); } } if (this.form.safeMin != null && this.form.safeMax != null && Number(this.form.safeMin) > Number(this.form.safeMax)) { return uni.showToast({ title: "安全库存区间不合法", icon: "none" }); } let paramsForSubmit = paramsObj; if (this.selectedTemplate) { for (const p of this.selectedTemplate.params || []) { if (p.required && (this.paramValues[p.fieldKey] === void 0 || this.paramValues[p.fieldKey] === null || this.paramValues[p.fieldKey] === "")) { return uni.showToast({ title: `请填写 ${p.fieldLabel}`, icon: "none" }); } } const shaped = {}; for (const p of this.selectedTemplate.params || []) { let v = this.paramValues[p.fieldKey]; if (p.type === "number" && v !== void 0 && v !== null && v !== "") v = Number(v); if (p.type === "boolean") v = !!v; shaped[p.fieldKey] = v; } paramsForSubmit = shaped; } const payload = { model: this.form.model, brand: this.form.brand, barcode: this.form.barcode, externalCode: this.form.externalCode || null, categoryId: this.form.categoryId || null, templateId: this.form.templateId || null, parameters: paramsForSubmit, images: this.form.images, remark: this.form.remark, safeMin: this.form.safeMin, safeMax: this.form.safeMax }; this.submitting = true; try { await post("/api/products/submissions", payload); uni.showToast({ title: "提交成功", icon: "success" }); setTimeout(() => { uni.redirectTo({ url: "/pages/product/submissions" }); }, 400); } catch (e) { const msg = (e == null ? void 0 : e.message) || "提交失败"; uni.showToast({ title: msg, icon: "none" }); } finally { this.submitting = false; } } } }; function _sfc_render$o(_ctx, _cache, $props, $setup, $data, $options) { const _component_ImageUploader = vue.resolveComponent("ImageUploader"); return vue.openBlock(), vue.createElementBlock("scroll-view", { "scroll-y": "", class: "page" }, [ vue.createElementVNode("view", { class: "hero" }, [ vue.createElementVNode("text", { class: "title" }, "提交配件"), vue.createElementVNode("text", { class: "desc" }, "填写型号、名称、参数与图片,提交后进入待审核状态") ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row required" }, [ vue.createElementVNode("text", { class: "label" }, "型号"), vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.form.model = $event), placeholder: "请输入型号(必填)" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.model, void 0, { trim: true } ] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "品牌"), vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.form.brand = $event), placeholder: "品牌/厂商" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.brand, void 0, { trim: true } ] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "条码"), vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $data.form.barcode = $event), placeholder: "可选,建议扫码录入" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.barcode, void 0, { trim: true } ] ]), vue.createElementVNode("button", { size: "mini", class: "picker-btn", onClick: _cache[3] || (_cache[3] = (...args) => $options.scanBarcode && $options.scanBarcode(...args)) }, "识码") ]) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "类别"), vue.createElementVNode("picker", { mode: "selector", range: $options.categoryNames, onChange: _cache[4] || (_cache[4] = (...args) => $options.onPickCategory && $options.onPickCategory(...args)) }, [ vue.createElementVNode( "view", { class: "picker" }, vue.toDisplayString($options.categoryLabel), 1 /* TEXT */ ) ], 40, ["range"]) ]) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "编号"), vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => $data.form.externalCode = $event), placeholder: "内部/外部编号(可选)" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.externalCode, void 0, { trim: true } ] ]) ]) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "模板") ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("picker", { mode: "selector", range: $options.templateNames, onChange: _cache[6] || (_cache[6] = (...args) => $options.onPickTemplate && $options.onPickTemplate(...args)) }, [ vue.createElementVNode( "view", { class: "picker" }, vue.toDisplayString($options.templateLabel), 1 /* TEXT */ ) ], 40, ["range"]) ]) ]), vue.createCommentVNode(" 动态参数:根据模板渲染必填/可选项 "), $options.selectedTemplate ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "参数") ]), vue.createElementVNode("view", { class: "param-list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($options.selectedTemplate.params || [], (p, idx) => { return vue.openBlock(), vue.createElementBlock("view", { class: "row", key: p.fieldKey }, [ vue.createElementVNode("text", { class: "label" }, [ vue.createTextVNode( vue.toDisplayString(p.fieldLabel), 1 /* TEXT */ ), p.unit ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0 }, "(" + vue.toDisplayString(p.unit) + ")", 1 /* TEXT */ )) : vue.createCommentVNode("v-if", true), p.required ? (vue.openBlock(), vue.createElementBlock("text", { key: 1, style: { "color": "#ff5b5b" } }, "*")) : vue.createCommentVNode("v-if", true) ]), p.type === "string" ? vue.withDirectives((vue.openBlock(), vue.createElementBlock("input", { key: 0, "onUpdate:modelValue": ($event) => $data.paramValues[p.fieldKey] = $event, placeholder: "请输入" + p.fieldLabel }, null, 8, ["onUpdate:modelValue", "placeholder"])), [ [ vue.vModelText, $data.paramValues[p.fieldKey], void 0, { trim: true } ] ]) : p.type === "number" ? vue.withDirectives((vue.openBlock(), vue.createElementBlock("input", { key: 1, type: "number", "onUpdate:modelValue": ($event) => $data.paramValues[p.fieldKey] = $event, placeholder: "请输入" + p.fieldLabel }, null, 8, ["onUpdate:modelValue", "placeholder"])), [ [ vue.vModelText, $data.paramValues[p.fieldKey], void 0, { number: true } ] ]) : p.type === "boolean" ? (vue.openBlock(), vue.createElementBlock("switch", { key: 2, checked: !!$data.paramValues[p.fieldKey], onChange: (e) => $data.paramValues[p.fieldKey] = e.detail.value }, null, 40, ["checked", "onChange"])) : p.type === "enum" ? (vue.openBlock(), vue.createElementBlock("picker", { key: 3, mode: "selector", range: p.enumOptions || [], onChange: ($event) => $options.onPickEnum(p, $event) }, [ vue.createElementVNode( "view", { class: "picker" }, vue.toDisplayString($options.displayEnum(p)), 1 /* TEXT */ ) ], 40, ["range", "onChange"])) : p.type === "date" ? (vue.openBlock(), vue.createElementBlock("picker", { key: 4, mode: "date", onChange: ($event) => $options.onPickDate(p, $event) }, [ vue.createElementVNode( "view", { class: "picker" }, vue.toDisplayString($data.paramValues[p.fieldKey] || "选择" + p.fieldLabel), 1 /* TEXT */ ) ], 40, ["onChange"])) : vue.withDirectives((vue.openBlock(), vue.createElementBlock("input", { key: 5, "onUpdate:modelValue": ($event) => $data.paramValues[p.fieldKey] = $event, placeholder: "请输入" + p.fieldLabel }, null, 8, ["onUpdate:modelValue", "placeholder"])), [ [ vue.vModelText, $data.paramValues[p.fieldKey], void 0, { trim: true } ] ]) ]); }), 128 /* KEYED_FRAGMENT */ )) ]) ])) : vue.createCommentVNode("v-if", true), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "图片") ]), vue.createVNode(_component_ImageUploader, { modelValue: $data.form.images, "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => $data.form.images = $event), max: 9, formData: { ownerType: "submission" } }, null, 8, ["modelValue"]) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "备注") ]), vue.withDirectives(vue.createElementVNode( "textarea", { class: "textarea", "onUpdate:modelValue": _cache[8] || (_cache[8] = ($event) => $data.form.remark = $event), placeholder: "选填:补充说明" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.remark, void 0, { trim: true } ] ]) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "安全库存") ]), vue.createElementVNode("view", { class: "row triple" }, [ vue.withDirectives(vue.createElementVNode( "input", { type: "number", "onUpdate:modelValue": _cache[9] || (_cache[9] = ($event) => $data.form.safeMin = $event), placeholder: "下限" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.safeMin, void 0, { number: true } ] ]), vue.withDirectives(vue.createElementVNode( "input", { type: "number", "onUpdate:modelValue": _cache[10] || (_cache[10] = ($event) => $data.form.safeMax = $event), placeholder: "上限" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.safeMax, void 0, { number: true } ] ]) ]) ]), vue.createElementVNode("view", { class: "fixed" }, [ vue.createElementVNode("button", { class: "primary", loading: $data.submitting, onClick: _cache[11] || (_cache[11] = (...args) => $options.submit && $options.submit(...args)) }, "提交审核", 8, ["loading"]), vue.createElementVNode("button", { class: "primary", style: { "margin-top": "16rpx", "background": "#7aa9ff" }, loading: $data.checking, onClick: _cache[12] || (_cache[12] = (...args) => $options.checkModel && $options.checkModel(...args)) }, "查重", 8, ["loading"]) ]) ]); } const PagesProductSubmit = /* @__PURE__ */ _export_sfc(_sfc_main$p, [["render", _sfc_render$o], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/submit.vue"]]); const _sfc_main$o = { data() { return { status: "", items: [], page: 1, size: 20, total: 0, loading: false, finished: false, cacheUnitsLoaded: false, cacheCategoriesLoaded: false }; }, onShow() { this.preloadDictionaries(); this.reload(); }, methods: { async preloadDictionaries() { try { const [units, categories, templates] = await Promise.all([ this.cacheUnitsLoaded ? Promise.resolve(null) : get("/api/product-units"), this.cacheCategoriesLoaded ? Promise.resolve(null) : get("/api/product-categories"), get("/api/product-templates") ]); if (units) { const list = Array.isArray(units == null ? void 0 : units.list) ? units.list : Array.isArray(units) ? units : []; uni.setStorageSync("CACHE_UNITS", list); this.cacheUnitsLoaded = true; } if (categories) { const list = Array.isArray(categories == null ? void 0 : categories.list) ? categories.list : Array.isArray(categories) ? categories : []; uni.setStorageSync("CACHE_CATEGORIES", list); this.cacheCategoriesLoaded = true; } if (templates) { const list = Array.isArray(templates == null ? void 0 : templates.list) ? templates.list : Array.isArray(templates) ? templates : []; uni.setStorageSync("CACHE_TEMPLATES", list); } } catch (_) { } }, switchStatus(s) { if (this.status === s) return; this.status = s; this.reload(); }, async reload() { this.page = 1; this.items = []; this.finished = false; await this.loadMore(); }, async loadMore() { if (this.loading || this.finished) return; this.loading = true; try { const params = { page: this.page, size: this.size }; if (this.status) params.status = this.status; const res = await get("/api/products/submissions", params); const list = Array.isArray(res == null ? void 0 : res.list) ? res.list : []; this.items = this.items.concat(list); this.total = Number((res == null ? void 0 : res.total) || this.items.length); if (list.length < this.size) this.finished = true; this.page += 1; } catch (e) { formatAppLog("warn", "at pages/product/submissions.vue:118", "加载提交记录失败", e); const msg = (e == null ? void 0 : e.message) || "加载失败"; uni.showToast({ title: msg, icon: "none" }); } finally { this.loading = false; } }, statusLabel(s) { if (s === "approved") return "已通过"; if (s === "rejected") return "已驳回"; return "待审核"; }, statusClass(s) { if (s === "approved") return "approved"; if (s === "rejected") return "rejected"; return "pending"; }, formatTime(value) { if (!value) return "-"; try { const d = new Date(value); if (!Number.isFinite(d.getTime())) return value; const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); const hh = String(d.getHours()).padStart(2, "0"); const mm = String(d.getMinutes()).padStart(2, "0"); return `${y}-${m}-${day} ${hh}:${mm}`; } catch (_) { return value; } }, viewDetail(id) { uni.navigateTo({ url: `/pages/product/submission-detail?id=${id}` }); }, notifyPending() { uni.showToast({ title: "审核中,请耐心等待", icon: "none" }); }, resubmit(item) { const payload = { model: item.model, name: item.name, brand: item.brand, spec: item.spec, origin: item.origin, unitId: item.unitId, categoryId: item.categoryId, remark: item.remark }; const query = encodeURIComponent(JSON.stringify(payload)); uni.navigateTo({ url: `/pages/product/submit?prefill=${query}` }); }, goSubmit() { uni.navigateTo({ url: "/pages/product/submit" }); } } }; function _sfc_render$n(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "hero" }, [ vue.createElementVNode("text", { class: "title" }, "我的配件提交"), vue.createElementVNode("text", { class: "desc" }, "查看待审核、已通过、已驳回的记录") ]), vue.createElementVNode("view", { class: "tabs" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["tab", { active: $data.status === "" }]), onClick: _cache[0] || (_cache[0] = ($event) => $options.switchStatus("")) }, "全部", 2 /* CLASS */ ), vue.createElementVNode( "view", { class: vue.normalizeClass(["tab", { active: $data.status === "pending" }]), onClick: _cache[1] || (_cache[1] = ($event) => $options.switchStatus("pending")) }, "待审核", 2 /* CLASS */ ), vue.createElementVNode( "view", { class: vue.normalizeClass(["tab", { active: $data.status === "approved" }]), onClick: _cache[2] || (_cache[2] = ($event) => $options.switchStatus("approved")) }, "已通过", 2 /* CLASS */ ), vue.createElementVNode( "view", { class: vue.normalizeClass(["tab", { active: $data.status === "rejected" }]), onClick: _cache[3] || (_cache[3] = ($event) => $options.switchStatus("rejected")) }, "已驳回", 2 /* CLASS */ ) ]), vue.createElementVNode( "scroll-view", { "scroll-y": "", class: "list", onScrolltolower: _cache[6] || (_cache[6] = (...args) => $options.loadMore && $options.loadMore(...args)) }, [ $data.items.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "cards" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.items, (item) => { return vue.openBlock(), vue.createElementBlock("view", { class: "card", key: item.id }, [ vue.createElementVNode("view", { class: "card-header" }, [ vue.createElementVNode( "text", { class: "model" }, vue.toDisplayString(item.model || "-"), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: vue.normalizeClass(["status", $options.statusClass(item.status)]) }, vue.toDisplayString($options.statusLabel(item.status)), 3 /* TEXT, CLASS */ ) ]), vue.createElementVNode("view", { class: "card-body" }, [ vue.createElementVNode( "text", { class: "name" }, vue.toDisplayString(item.name || "未填写名称"), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "brand" }, "品牌:" + vue.toDisplayString(item.brand || "-"), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "time" }, "提交:" + vue.toDisplayString($options.formatTime(item.createdAt)), 1 /* TEXT */ ), item.reviewedAt ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "time" }, "审核:" + vue.toDisplayString($options.formatTime(item.reviewedAt)), 1 /* TEXT */ )) : vue.createCommentVNode("v-if", true) ]), vue.createElementVNode("view", { class: "card-footer" }, [ vue.createElementVNode("button", { size: "mini", onClick: ($event) => $options.viewDetail(item.id) }, "详情", 8, ["onClick"]), item.status === "pending" ? (vue.openBlock(), vue.createElementBlock("button", { key: 0, size: "mini", type: "primary", onClick: _cache[4] || (_cache[4] = (...args) => $options.notifyPending && $options.notifyPending(...args)) }, "等待审核")) : item.status === "rejected" ? (vue.openBlock(), vue.createElementBlock("button", { key: 1, size: "mini", type: "warn", onClick: ($event) => $options.resubmit(item) }, "重新提交", 8, ["onClick"])) : vue.createCommentVNode("v-if", true) ]) ]); }), 128 /* KEYED_FRAGMENT */ )) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "empty" }, [ vue.createElementVNode("text", null, "暂无提交记录,快去提交新的配件吧"), vue.createElementVNode("button", { size: "mini", class: "primary", onClick: _cache[5] || (_cache[5] = (...args) => $options.goSubmit && $options.goSubmit(...args)) }, "立即提交") ])), $data.loading ? (vue.openBlock(), vue.createElementBlock("view", { key: 2, class: "loading" }, "加载中...")) : $data.finished && $data.items.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 3, class: "finished" }, "没有更多了")) : vue.createCommentVNode("v-if", true) ], 32 /* NEED_HYDRATION */ ), vue.createElementVNode("view", { class: "fab", onClick: _cache[7] || (_cache[7] = (...args) => $options.goSubmit && $options.goSubmit(...args)) }, "+") ]); } const PagesProductSubmissions = /* @__PURE__ */ _export_sfc(_sfc_main$o, [["render", _sfc_render$n], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/submissions.vue"]]); const _sfc_main$n = { data() { return { id: "", detail: null, unitName: "-", categoryName: "-", templateName: "-" }; }, async onLoad(query) { this.id = (query == null ? void 0 : query.id) || ""; if (!this.id) { uni.showToast({ title: "参数缺失", icon: "none" }); return; } await this.loadDetail(); }, methods: { async loadDetail() { try { const data = await get(`/api/products/submissions/${this.id}`); this.detail = data; this.categoryName = this.categoryLookup(data.categoryId); this.templateName = this.templateLookup(data.templateId); } catch (e) { const msg = (e == null ? void 0 : e.message) || "加载失败"; uni.showToast({ title: msg, icon: "none" }); } }, statusLabel(s) { if (s === "approved") return "已通过"; if (s === "rejected") return "已驳回"; return "待审核"; }, statusClass(s) { if (s === "approved") return "approved"; if (s === "rejected") return "rejected"; return "pending"; }, preview(idx) { var _a; if (!((_a = this.detail) == null ? void 0 : _a.images) || !this.detail.images.length) return; uni.previewImage({ urls: this.detail.images, current: idx }); }, formatTime(value) { if (!value) return "-"; try { const d = new Date(value); if (!Number.isFinite(d.getTime())) return value; const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); const hh = String(d.getHours()).padStart(2, "0"); const mm = String(d.getMinutes()).padStart(2, "0"); return `${y}-${m}-${day} ${hh}:${mm}`; } catch (_) { return value; } }, unitLookup(id) { try { const list = []; const found = list.find((x) => String(x.id) === String(id)); return found ? found.name : "-"; } catch (_) { return "-"; } }, categoryLookup(id) { try { const list = uni.getStorageSync("CACHE_CATEGORIES") || []; const found = list.find((x) => String(x.id) === String(id)); return found ? found.name : "-"; } catch (_) { return "-"; } }, templateLookup(id) { try { const list = uni.getStorageSync("CACHE_TEMPLATES") || []; const found = list.find((x) => String(x.id) === String(id)); return found ? found.name : "-"; } catch (_) { return "-"; } }, back() { uni.navigateBack({ delta: 1 }); }, resubmit() { const payload = { model: this.detail.model, name: this.detail.name, brand: this.detail.brand, spec: this.detail.spec, origin: this.detail.origin, unitId: this.detail.unitId, categoryId: this.detail.categoryId, remark: this.detail.remark, barcode: this.detail.barcode, parameters: this.detail.parameters }; const query = encodeURIComponent(JSON.stringify(payload)); uni.navigateTo({ url: `/pages/product/submit?prefill=${query}` }); } }, computed: { stockRange() { var _a, _b; const min = (_a = this.detail) == null ? void 0 : _a.safeMin; const max = (_b = this.detail) == null ? void 0 : _b.safeMax; if (min == null && max == null) return "-"; if (min != null && max != null) return `${min} ~ ${max}`; if (min != null) return `≥ ${min}`; return `≤ ${max}`; }, labeledPairs() { var _a; const params = (_a = this.detail) == null ? void 0 : _a.parameters; if (!params || typeof params !== "object") return []; let labelMap = {}; try { const templates = uni.getStorageSync("CACHE_TEMPLATES") || []; const tpl = templates.find((t) => { var _a2; return String(t.id) === String((_a2 = this.detail) == null ? void 0 : _a2.templateId); }); if (tpl && Array.isArray(tpl.params)) { for (const p of tpl.params) labelMap[p.fieldKey] = p.fieldLabel; } } catch (_) { } return Object.keys(params).map((k) => ({ key: k, label: labelMap[k] || k, value: params[k] })); } } }; function _sfc_render$m(_ctx, _cache, $props, $setup, $data, $options) { return $data.detail ? (vue.openBlock(), vue.createElementBlock("scroll-view", { key: 0, "scroll-y": "", class: "page" }, [ vue.createElementVNode("view", { class: "header" }, [ vue.createElementVNode( "text", { class: "model" }, vue.toDisplayString($data.detail.model), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: vue.normalizeClass(["status", $options.statusClass($data.detail.status)]) }, vue.toDisplayString($options.statusLabel($data.detail.status)), 3 /* TEXT, CLASS */ ) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "名称"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.name || "-"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "品牌"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.brand || "-"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "规格"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.spec || "-"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "条码"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.barcode || "-"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "类别"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.categoryName), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "模板"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.templateName), 1 /* TEXT */ ) ]), vue.createCommentVNode(" 隐藏产地/单位/安全库存显示 ") ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "block-title" }, "参数"), $options.labeledPairs.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "params" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($options.labeledPairs, (item) => { return vue.openBlock(), vue.createElementBlock("view", { class: "param", key: item.key }, [ vue.createElementVNode( "text", { class: "param-key" }, vue.toDisplayString(item.label), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "param-val" }, vue.toDisplayString(item.value), 1 /* TEXT */ ) ]); }), 128 /* KEYED_FRAGMENT */ )) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "placeholder" }, "未填写参数")) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "block-title" }, "图片"), $data.detail.images && $data.detail.images.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "images" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.detail.images, (img, idx) => { return vue.openBlock(), vue.createElementBlock("image", { key: idx, src: img, class: "image", mode: "aspectFill", onClick: ($event) => $options.preview(idx) }, null, 8, ["src", "onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "placeholder" }, "未上传图片")) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "block-title" }, "备注"), vue.createElementVNode( "view", { class: "placeholder" }, vue.toDisplayString($data.detail.remark || "无"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "提交时间"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.formatTime($data.detail.createdAt)), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "审核时间"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.formatTime($data.detail.reviewedAt)), 1 /* TEXT */ ) ]), $data.detail.reviewRemark ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "审核说明"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.reviewRemark), 1 /* TEXT */ ) ])) : vue.createCommentVNode("v-if", true) ]), vue.createElementVNode("view", { class: "footer" }, [ vue.createElementVNode("button", { size: "mini", onClick: _cache[0] || (_cache[0] = (...args) => $options.back && $options.back(...args)) }, "返回"), $data.detail.status === "rejected" ? (vue.openBlock(), vue.createElementBlock("button", { key: 0, size: "mini", type: "warn", onClick: _cache[1] || (_cache[1] = (...args) => $options.resubmit && $options.resubmit(...args)) }, "重新提交")) : vue.createCommentVNode("v-if", true) ]) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "loading" }, "加载中...")); } const PagesProductSubmissionDetail = /* @__PURE__ */ _export_sfc(_sfc_main$n, [["render", _sfc_render$m], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/submission-detail.vue"]]); const _sfc_main$m = { components: { ImageUploader }, data() { return { id: "", form: { name: "", barcode: "", brand: "", model: "", spec: "", categoryId: "", images: [], remark: "", platformStatus: "", sourceSubmissionId: "" }, categories: [], keyboardHeight: 0 }; }, onLoad(query) { this.id = (query == null ? void 0 : query.id) || ""; this.bootstrap(); this.initKeyboardListener(); }, onUnload() { this.disposeKeyboardListener(); }, computed: { categoryNames() { return this.categories.map((c) => c.name); }, categoryLabel() { const c = this.categories.find((x) => String(x.id) === String(this.form.categoryId)); return c ? c.name : "选择类别"; } }, methods: { async bootstrap() { await Promise.all([this.fetchCategories()]); if (this.id) this.loadDetail(); }, initKeyboardListener() { try { this.__keyboardListener = (e) => { const h = e && (e.height || e.targetHeight || 0) || 0; this.keyboardHeight = h; }; uni.onKeyboardHeightChange && uni.onKeyboardHeightChange(this.__keyboardListener); } catch (_) { } }, disposeKeyboardListener() { try { if (this.__keyboardListener && uni.offKeyboardHeightChange) { uni.offKeyboardHeightChange(this.__keyboardListener); } } catch (_) { } }, async fetchCategories() { try { const res = await get("/api/product-categories"); this.categories = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; } catch (_) { } }, onPickCategory(e) { const idx = Number(e.detail.value); const c = this.categories[idx]; this.form.categoryId = c ? c.id : ""; }, async chooseAndScanBarcode() { try { const chooseRes = await uni.chooseImage({ count: 1, sourceType: ["camera", "album"], sizeType: ["compressed"] }); let filePath = chooseRes.tempFilePaths[0]; try { const comp = await uni.compressImage({ src: filePath, quality: 80 }); filePath = comp.tempFilePath || filePath; } catch (e) { } const data = await upload("/api/barcode/scan", filePath, {}, "file"); if (data && data.success && data.barcode) { this.form.barcode = data.barcode; uni.showToast({ title: "识别成功", icon: "success", mask: false }); return; } const msg = data && (data.message || data.error || data.msg) || "未识别"; uni.showToast({ title: msg, icon: "none", mask: false }); } catch (e) { const msg = e && e.message ? String(e.message) : "网络异常或服务不可用"; uni.showToast({ title: msg, icon: "none", mask: false }); } }, async loadDetail() { try { const data = await get("/api/products/" + this.id); Object.assign(this.form, { name: data.name, barcode: data.barcode, brand: data.brand, model: data.model, spec: data.spec, categoryId: data.categoryId, images: (data.images || []).map((i) => i.url || i), remark: data.remark || "", platformStatus: data.platformStatus || "", sourceSubmissionId: data.sourceSubmissionId || "" }); } catch (_) { } }, validate() { if (!this.form.name) { uni.showToast({ title: "请填写名称", icon: "none" }); return false; } return true; }, buildPayload() { const f = this.form; return { name: f.name, barcode: f.barcode, brand: f.brand, model: f.model, spec: f.spec, categoryId: f.categoryId || null, images: f.images, remark: f.remark }; }, async save(goOn) { try { uni.hideKeyboard && uni.hideKeyboard(); } catch (_) { } if (!this.validate()) return; const payload = this.buildPayload(); try { if (this.id) await put("/api/products/" + this.id, payload); else await post("/api/products", payload); uni.showToast({ title: "保存成功", icon: "success", mask: false }); if (goOn && !this.id) { this.form = { name: "", barcode: "", brand: "", model: "", spec: "", categoryId: "", images: [], remark: "", platformStatus: "", sourceSubmissionId: "" }; } else { setTimeout(() => uni.navigateBack(), 400); } } catch (e) { uni.showToast({ title: "保存失败", icon: "none", mask: false }); } } } }; function _sfc_render$l(_ctx, _cache, $props, $setup, $data, $options) { const _component_ImageUploader = vue.resolveComponent("ImageUploader"); return vue.openBlock(), vue.createElementBlock("scroll-view", { "scroll-y": "", class: "page" }, [ vue.createCommentVNode(" 顶部标题与操作提示 "), vue.createElementVNode("view", { class: "hero small" }, [ vue.createElementVNode("text", { class: "title" }, "编辑货品"), vue.createElementVNode("text", { class: "sub" }, "完善基础信息与价格") ]), $data.form.platformStatus === "platform" ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "tip platform" }, "平台推荐货品,建议谨慎修改核心字段")) : $data.form.sourceSubmissionId ? (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "tip custom" }, "此货品源于我的提交,审核通过后已入库")) : vue.createCommentVNode("v-if", true), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "商品名称"), vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.form.name = $event), placeholder: "必填" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.name, void 0, { trim: true } ] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "条形码"), vue.withDirectives(vue.createElementVNode( "input", { class: "input-long", "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.form.barcode = $event), placeholder: "可扫码或输入" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.barcode, void 0, { trim: true } ] ]), vue.createElementVNode("button", { size: "mini", class: "picker-btn", onClick: _cache[2] || (_cache[2] = (...args) => $options.chooseAndScanBarcode && $options.chooseAndScanBarcode(...args)) }, "图片识码") ]), vue.createElementVNode("view", { class: "row" }, [ vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.form.brand = $event), placeholder: "品牌" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.brand, void 0, { trim: true } ] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => $data.form.model = $event), placeholder: "型号" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.model, void 0, { trim: true } ] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => $data.form.spec = $event), placeholder: "规格" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.spec, void 0, { trim: true } ] ]) ]), vue.createCommentVNode(" 隐藏产地输入 "), vue.createCommentVNode(" 隐藏主单位选择 "), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("picker", { mode: "selector", range: $options.categoryNames, onChange: _cache[6] || (_cache[6] = (...args) => $options.onPickCategory && $options.onPickCategory(...args)) }, [ vue.createElementVNode( "view", { class: "picker" }, "类别:" + vue.toDisplayString($options.categoryLabel), 1 /* TEXT */ ) ], 40, ["range"]) ]) ]), vue.createCommentVNode(" 隐藏库存与安全库存输入 "), vue.createCommentVNode(" 隐藏价格相关输入 "), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("text", { class: "label" }, "图片"), vue.createVNode(_component_ImageUploader, { modelValue: $data.form.images, "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => $data.form.images = $event), formData: { ownerType: "product" } }, null, 8, ["modelValue"]) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("text", { class: "label" }, "备注"), vue.withDirectives(vue.createElementVNode( "textarea", { "onUpdate:modelValue": _cache[8] || (_cache[8] = ($event) => $data.form.remark = $event), placeholder: "可选", "auto-height": "" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.remark, void 0, { trim: true } ] ]) ]), vue.createElementVNode( "view", { class: "fixed", style: vue.normalizeStyle({ bottom: ($data.keyboardHeight || 0) + "px" }) }, [ vue.createElementVNode("button", { class: "ghost", onClick: _cache[9] || (_cache[9] = ($event) => $options.save(false)) }, "保存"), vue.createElementVNode("button", { class: "primary", onClick: _cache[10] || (_cache[10] = ($event) => $options.save(true)) }, "保存并继续") ], 4 /* STYLE */ ) ]); } const PagesProductForm = /* @__PURE__ */ _export_sfc(_sfc_main$m, [["render", _sfc_render$l], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/form.vue"]]); const _sfc_main$l = { data() { return { id: "", detail: null, categoryName: "-", templateName: "-" }; }, async onLoad(query) { this.id = (query == null ? void 0 : query.id) || ""; if (!this.id) { uni.showToast({ title: "参数缺失", icon: "none" }); return; } await this.preloadDictionaries(); await this.loadDetail(); }, methods: { async preloadDictionaries() { try { const needCats = !Array.isArray(uni.getStorageSync("CACHE_CATEGORIES")); const needTpls = !Array.isArray(uni.getStorageSync("CACHE_TEMPLATES")); if (!needCats && !needTpls) return; const reqs = []; if (needCats) reqs.push(get("/api/product-categories")); if (needTpls) reqs.push(get("/api/product-templates")); const res = await Promise.all(reqs); let idx = 0; if (needCats) { const r = res[idx++]; const list = Array.isArray(r == null ? void 0 : r.list) ? r.list : Array.isArray(r) ? r : []; uni.setStorageSync("CACHE_CATEGORIES", list); } if (needTpls) { const r = res[idx++]; const list = Array.isArray(r == null ? void 0 : r.list) ? r.list : Array.isArray(r) ? r : []; uni.setStorageSync("CACHE_TEMPLATES", list); } } catch (_) { } }, async loadDetail() { try { const data = await get("/api/products/" + this.id); this.detail = data; this.categoryName = this.categoryLookup(data.categoryId); this.templateName = this.templateLookup(data.templateId); } catch (e) { uni.showToast({ title: (e == null ? void 0 : e.message) || "加载失败", icon: "none" }); } }, preview(idx) { var _a; try { const list = (((_a = this.detail) == null ? void 0 : _a.images) || []).map((i) => i.url || i); uni.previewImage({ urls: list, current: idx }); } catch (_) { } }, categoryLookup(id) { try { const list = uni.getStorageSync("CACHE_CATEGORIES") || []; const f = list.find((x) => String(x.id) === String(id)); return f ? f.name : "-"; } catch (_) { return "-"; } }, templateLookup(id) { try { const list = uni.getStorageSync("CACHE_TEMPLATES") || []; const f = list.find((x) => String(x.id) === String(id)); return f ? f.name : "-"; } catch (_) { return "-"; } }, async remove() { try { const r = await new Promise((resolve) => { uni.showModal({ content: "确认删除该货品?删除后可在后台恢复", success: resolve }); }); if (!r || !r.confirm) return; await del("/api/products/" + this.id); uni.showToast({ title: "已删除", icon: "success" }); setTimeout(() => uni.navigateBack(), 400); } catch (e) { uni.showToast({ title: "删除失败", icon: "none" }); } }, back() { uni.navigateBack({ delta: 1 }); } }, computed: { labeledPairs() { var _a; const params = (_a = this.detail) == null ? void 0 : _a.parameters; if (!params || typeof params !== "object") return []; let labelMap = {}, unitMap = {}; try { const templates = uni.getStorageSync("CACHE_TEMPLATES") || []; const tpl = templates.find((t) => { var _a2; return String(t.id) === String((_a2 = this.detail) == null ? void 0 : _a2.templateId); }); if (tpl && Array.isArray(tpl.params)) for (const p of tpl.params) { labelMap[p.fieldKey] = p.fieldLabel; unitMap[p.fieldKey] = p.unit; } } catch (_) { } return Object.keys(params).map((k) => ({ key: k, label: labelMap[k] || k, unit: unitMap[k] || "", value: params[k] })); } } }; function _sfc_render$k(_ctx, _cache, $props, $setup, $data, $options) { return $data.detail ? (vue.openBlock(), vue.createElementBlock("scroll-view", { key: 0, "scroll-y": "", class: "page" }, [ vue.createElementVNode("view", { class: "header" }, [ vue.createElementVNode( "text", { class: "model" }, vue.toDisplayString($data.detail.model), 1 /* TEXT */ ), $data.detail.deleted ? (vue.openBlock(), vue.createElementBlock("text", { key: 0, class: "status deleted" }, "已删除")) : vue.createCommentVNode("v-if", true) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "名称"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.name || "-"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "品牌"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.brand || "-"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "型号"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.model || "-"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "条码"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.barcode || "-"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "类别"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.categoryName), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "模板"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.templateName), 1 /* TEXT */ ) ]), $data.detail.externalCode ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "编号"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.detail.externalCode), 1 /* TEXT */ ) ])) : vue.createCommentVNode("v-if", true) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "block-title" }, "参数"), $options.labeledPairs.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "params" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($options.labeledPairs, (item) => { return vue.openBlock(), vue.createElementBlock("view", { class: "param", key: item.key }, [ vue.createElementVNode("text", { class: "param-key" }, [ vue.createTextVNode( vue.toDisplayString(item.label), 1 /* TEXT */ ), item.unit ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0 }, "(" + vue.toDisplayString(item.unit) + ")", 1 /* TEXT */ )) : vue.createCommentVNode("v-if", true) ]), vue.createElementVNode( "text", { class: "param-val" }, vue.toDisplayString(item.value), 1 /* TEXT */ ) ]); }), 128 /* KEYED_FRAGMENT */ )) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "placeholder" }, "未填写参数")) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "block-title" }, "图片"), $data.detail.images && $data.detail.images.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "images" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.detail.images, (img, idx) => { return vue.openBlock(), vue.createElementBlock("image", { key: idx, src: img.url || img, class: "image", mode: "aspectFill", onClick: ($event) => $options.preview(idx) }, null, 8, ["src", "onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "placeholder" }, "未上传图片")) ]), vue.createElementVNode("view", { class: "section" }, [ vue.createElementVNode("view", { class: "block-title" }, "备注"), vue.createElementVNode( "view", { class: "placeholder" }, vue.toDisplayString($data.detail.remark || "无"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "footer" }, [ vue.createElementVNode("button", { size: "mini", onClick: _cache[0] || (_cache[0] = (...args) => $options.back && $options.back(...args)) }, "返回"), vue.createElementVNode("button", { size: "mini", type: "warn", onClick: _cache[1] || (_cache[1] = (...args) => $options.remove && $options.remove(...args)) }, "删除") ]) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "loading" }, "加载中...")); } const PagesProductProductDetail = /* @__PURE__ */ _export_sfc(_sfc_main$l, [["render", _sfc_render$k], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/product-detail.vue"]]); const _sfc_main$k = { data() { return { name: "", list: [] }; }, onLoad() { this.reload(); }, methods: { async reload() { try { const res = await get("/api/product-categories"); this.list = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; } catch (_) { } }, async create() { if (!this.name) return; await post("/api/product-categories", { name: this.name }); this.name = ""; this.reload(); }, async update(c) { await put("/api/product-categories/" + c.id, { name: c.name }); uni.showToast({ title: "已保存", icon: "success" }); }, async remove(c) { uni.showModal({ content: "确定删除该类别?", success: async (r) => { if (!r.confirm) return; await del("/api/product-categories/" + c.id); this.reload(); } }); } } }; function _sfc_render$j(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "toolbar" }, [ vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.name = $event), placeholder: "新类别名称" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.name, void 0, { trim: true } ] ]), vue.createElementVNode("button", { size: "mini", onClick: _cache[1] || (_cache[1] = (...args) => $options.create && $options.create(...args)) }, "新增") ]), vue.createElementVNode("scroll-view", { "scroll-y": "", class: "list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.list, (c) => { return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: c.id }, [ vue.withDirectives(vue.createElementVNode("input", { "onUpdate:modelValue": ($event) => c.name = $event }, null, 8, ["onUpdate:modelValue"]), [ [ vue.vModelText, c.name, void 0, { trim: true } ] ]), vue.createElementVNode("view", { class: "ops" }, [ vue.createElementVNode("button", { size: "mini", onClick: ($event) => $options.update(c) }, "保存", 8, ["onClick"]), vue.createElementVNode("button", { size: "mini", type: "warn", onClick: ($event) => $options.remove(c) }, "删除", 8, ["onClick"]) ]) ]); }), 128 /* KEYED_FRAGMENT */ )) ]) ]); } const PagesProductCategories = /* @__PURE__ */ _export_sfc(_sfc_main$k, [["render", _sfc_render$j], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/categories.vue"]]); const _sfc_main$j = { data() { return { name: "", list: [] }; }, onLoad() { this.reload(); }, methods: { async reload() { try { const res = await get("/api/product-units"); this.list = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; } catch (_) { } }, async create() { if (!this.name) return; await post("/api/product-units", { name: this.name }); this.name = ""; this.reload(); }, async update(u) { await put("/api/product-units/" + u.id, { name: u.name }); uni.showToast({ title: "已保存", icon: "success" }); }, async remove(u) { uni.showModal({ content: "确定删除该单位?", success: async (r) => { if (!r.confirm) return; await del("/api/product-units/" + u.id); this.reload(); } }); } } }; function _sfc_render$i(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "toolbar" }, [ vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.name = $event), placeholder: "新单位名称" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.name, void 0, { trim: true } ] ]), vue.createElementVNode("button", { size: "mini", onClick: _cache[1] || (_cache[1] = (...args) => $options.create && $options.create(...args)) }, "新增") ]), vue.createElementVNode("scroll-view", { "scroll-y": "", class: "list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.list, (u) => { return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: u.id }, [ vue.withDirectives(vue.createElementVNode("input", { "onUpdate:modelValue": ($event) => u.name = $event }, null, 8, ["onUpdate:modelValue"]), [ [ vue.vModelText, u.name, void 0, { trim: true } ] ]), vue.createElementVNode("view", { class: "ops" }, [ vue.createElementVNode("button", { size: "mini", onClick: ($event) => $options.update(u) }, "保存", 8, ["onClick"]), vue.createElementVNode("button", { size: "mini", type: "warn", onClick: ($event) => $options.remove(u) }, "删除", 8, ["onClick"]) ]) ]); }), 128 /* KEYED_FRAGMENT */ )) ]) ]); } const PagesProductUnits = /* @__PURE__ */ _export_sfc(_sfc_main$j, [["render", _sfc_render$i], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/units.vue"]]); const _sfc_main$i = { data() { return { settings: { hideZeroStock: false, hidePurchasePrice: false } }; }, onLoad() { this.load(); }, methods: { async load() { try { const res = await get("/api/product-settings"); this.settings = { hideZeroStock: !!(res == null ? void 0 : res.hideZeroStock), hidePurchasePrice: !!(res == null ? void 0 : res.hidePurchasePrice) }; } catch (_) { } }, async update(key, val) { const next = { ...this.settings, [key]: val }; this.settings = next; try { await put("/api/product-settings", next); } catch (_) { } } } }; function _sfc_render$h(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "item" }, [ vue.createElementVNode("text", null, "隐藏零库存商品"), vue.createElementVNode("switch", { checked: $data.settings.hideZeroStock, onChange: _cache[0] || (_cache[0] = (e) => $options.update("hideZeroStock", e.detail.value)) }, null, 40, ["checked"]) ]), vue.createElementVNode("view", { class: "item" }, [ vue.createElementVNode("text", null, "隐藏进货价"), vue.createElementVNode("switch", { checked: $data.settings.hidePurchasePrice, onChange: _cache[1] || (_cache[1] = (e) => $options.update("hidePurchasePrice", e.detail.value)) }, null, 40, ["checked"]) ]) ]); } const PagesProductSettings = /* @__PURE__ */ _export_sfc(_sfc_main$i, [["render", _sfc_render$h], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/settings.vue"]]); const _sfc_main$h = { data() { return { kw: "", debtOnly: false, customers: [] }; }, onLoad() { this.search(); }, onShow() { this.search(); }, methods: { toggleDebtOnly() { this.debtOnly = !this.debtOnly; this.search(); }, async search() { try { const res = await get("/api/customers", { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 }); this.customers = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; } catch (e) { uni.showToast({ title: "加载失败", icon: "none" }); } }, createCustomer() { uni.navigateTo({ url: "/pages/customer/form" }); }, select(c) { const pages = getCurrentPages(); const prev = pages.length >= 2 ? pages[pages.length - 2] : null; const vm = prev && prev.$vm ? prev.$vm : null; if (vm && vm.order) { vm.order.customerId = c.id; vm.customerName = c.name; } uni.navigateBack(); }, openDetail(c) { uni.navigateTo({ url: "/pages/customer/detail?id=" + c.id }); } } }; function _sfc_render$g(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "search" }, [ vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.kw = $event), placeholder: "搜索客户名称/电话", onConfirm: _cache[1] || (_cache[1] = (...args) => $options.search && $options.search(...args)) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [vue.vModelText, $data.kw] ]), vue.createElementVNode("button", { size: "mini", onClick: _cache[2] || (_cache[2] = (...args) => $options.search && $options.search(...args)) }, "搜索"), vue.createElementVNode("button", { size: "mini", type: $data.debtOnly ? "primary" : "default", onClick: _cache[3] || (_cache[3] = (...args) => $options.toggleDebtOnly && $options.toggleDebtOnly(...args)) }, "只看欠款", 8, ["type"]) ]), vue.createElementVNode("scroll-view", { "scroll-y": "", class: "list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.customers, (c) => { return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: c.id, onClick: ($event) => $options.openDetail(c) }, [ vue.createElementVNode( "view", { class: "name" }, vue.toDisplayString(c.name), 1 /* TEXT */ ), vue.createElementVNode("view", { class: "meta" }, [ vue.createTextVNode( vue.toDisplayString(c.mobile || "—") + " ", 1 /* TEXT */ ), typeof c.receivable === "number" ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0 }, "|应收:¥ " + vue.toDisplayString(Number(c.receivable).toFixed(2)), 1 /* TEXT */ )) : vue.createCommentVNode("v-if", true) ]) ], 8, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ]), vue.createElementVNode("view", { class: "bottom" }, [ vue.createElementVNode("button", { class: "primary", onClick: _cache[4] || (_cache[4] = (...args) => $options.createCustomer && $options.createCustomer(...args)) }, "新增客户") ]) ]); } const PagesCustomerSelect = /* @__PURE__ */ _export_sfc(_sfc_main$h, [["render", _sfc_render$g], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/customer/select.vue"]]); const _sfc_main$g = { data() { return { id: null, form: { name: "", priceLevel: "retail", contactName: "", mobile: "", phone: "", address: "", arOpening: 0, remark: "" }, priceLevels: ["零售价", "批发价", "大单报价"], priceLabels: ["零售价", "批发价", "大单报价"], priceIdx: 0 }; }, onLoad(query) { if (query && query.id) { this.id = Number(query.id); this.load(); } }, methods: { onPriceChange(e) { this.priceIdx = Number(e.detail.value); this.form.priceLevel = this.priceLevels[this.priceIdx]; }, async load() { if (!this.id) return; try { const d = await get(`/api/customers/${this.id}`); this.form = { name: (d == null ? void 0 : d.name) || "", priceLevel: (d == null ? void 0 : d.priceLevel) || "零售价", contactName: (d == null ? void 0 : d.contactName) || "", mobile: (d == null ? void 0 : d.mobile) || "", phone: (d == null ? void 0 : d.phone) || "", address: (d == null ? void 0 : d.address) || "", arOpening: Number((d == null ? void 0 : d.arOpening) || 0), remark: (d == null ? void 0 : d.remark) || "" }; const idx = this.priceLevels.indexOf(this.form.priceLevel || "零售价"); this.priceIdx = idx >= 0 ? idx : 0; } catch (e) { uni.showToast({ title: (e == null ? void 0 : e.message) || "加载失败", icon: "none" }); } }, async save() { if (!this.form.name) return uni.showToast({ title: "请填写客户名称", icon: "none" }); try { if (this.id) await put(`/api/customers/${this.id}`, this.form); else await post("/api/customers", this.form); uni.showToast({ title: "保存成功", icon: "success" }); setTimeout(() => uni.navigateBack(), 500); } catch (e) { uni.showToast({ title: (e == null ? void 0 : e.message) || "保存失败", icon: "none" }); } } } }; function _sfc_render$f(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "客户名称"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.form.name = $event), placeholder: "必填" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.name] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "售价档位"), vue.createElementVNode("picker", { range: $data.priceLabels, value: $data.priceIdx, onChange: _cache[1] || (_cache[1] = (...args) => $options.onPriceChange && $options.onPriceChange(...args)) }, [ vue.createElementVNode( "view", { class: "value" }, vue.toDisplayString($data.priceLabels[$data.priceIdx]), 1 /* TEXT */ ) ], 40, ["range", "value"]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "联系人"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $data.form.contactName = $event), placeholder: "可选" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.contactName] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "手机"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.form.mobile = $event), placeholder: "可选" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.mobile] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "电话"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => $data.form.phone = $event), placeholder: "可选(座机)" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.phone] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "送货地址"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => $data.form.address = $event), placeholder: "可选" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.address] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "初始应收"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", type: "digit", "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => $data.form.arOpening = $event), placeholder: "默认 0.00" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.arOpening, void 0, { number: true } ] ]) ]), vue.createElementVNode("view", { class: "textarea" }, [ vue.withDirectives(vue.createElementVNode( "textarea", { "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => $data.form.remark = $event), maxlength: "200", placeholder: "备注(最多200字)" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.remark] ]) ]), vue.createElementVNode("view", { class: "bottom" }, [ vue.createElementVNode("button", { class: "primary", onClick: _cache[8] || (_cache[8] = (...args) => $options.save && $options.save(...args)) }, "保存") ]) ]); } const PagesCustomerForm = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["render", _sfc_render$f], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/customer/form.vue"]]); const _sfc_main$f = { data() { return { id: null, d: {}, editing: false, form: { name: "", contactName: "", mobile: "", phone: "", address: "", priceLevel: "零售价", arOpening: 0, remark: "" }, priceLevels: ["零售价", "批发价", "大单报价"], priceLabels: ["零售价", "批发价", "大单报价"], priceIdx: 0 }; }, onLoad(q) { if (q && q.id) { this.id = Number(q.id); this.fetch(); } }, methods: { async fetch() { try { this.d = await get(`/api/customers/${this.id}`); this.form = { name: this.d.name || "", contactName: this.d.contactName || "", mobile: this.d.mobile || "", phone: this.d.phone || "", address: this.d.address || "", priceLevel: this.d.priceLevel || "retail", arOpening: Number(this.d.arOpening || 0), remark: this.d.remark || "" }; const idx = this.priceLevels.indexOf(this.form.priceLevel); this.priceIdx = idx >= 0 ? idx : 0; } catch (e) { uni.showToast({ title: "加载失败", icon: "none" }); } }, toggleEdit() { this.editing = !this.editing; }, onPriceChange(e) { this.priceIdx = Number(e.detail.value); this.form.priceLevel = this.priceLevels[this.priceIdx]; }, choose() { const pages = getCurrentPages(); let targetIdx = -1; for (let i = pages.length - 2; i >= 0; i--) { const vm = pages[i] && pages[i].$vm ? pages[i].$vm : null; if (vm && vm.order) { vm.order.customerId = this.d.id; vm.customerName = this.d.name; targetIdx = i; break; } } if (targetIdx >= 0) { const delta = pages.length - 1 - targetIdx; uni.navigateBack({ delta }); } else { uni.navigateBack(); } }, async save() { if (!this.form.name) return uni.showToast({ title: "请填写客户名称", icon: "none" }); try { await put(`/api/customers/${this.id}`, this.form); uni.showToast({ title: "已保存", icon: "success" }); this.editing = false; await this.fetch(); } catch (e) { uni.showToast({ title: (e == null ? void 0 : e.message) || "保存失败", icon: "none" }); } } } }; function _sfc_render$e(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "card" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "名称"), !$data.editing ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "value" }, vue.toDisplayString($data.d.name), 1 /* TEXT */ )) : vue.withDirectives((vue.openBlock(), vue.createElementBlock( "input", { key: 1, class: "value-input", "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.form.name = $event), placeholder: "必填" }, null, 512 /* NEED_PATCH */ )), [ [vue.vModelText, $data.form.name] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "联系人"), !$data.editing ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "value" }, vue.toDisplayString($data.d.contactName || "—"), 1 /* TEXT */ )) : vue.withDirectives((vue.openBlock(), vue.createElementBlock( "input", { key: 1, class: "value-input", "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.form.contactName = $event), placeholder: "可选" }, null, 512 /* NEED_PATCH */ )), [ [vue.vModelText, $data.form.contactName] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "手机"), !$data.editing ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "value" }, vue.toDisplayString($data.d.mobile || "—"), 1 /* TEXT */ )) : vue.withDirectives((vue.openBlock(), vue.createElementBlock( "input", { key: 1, class: "value-input", "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $data.form.mobile = $event), placeholder: "可选" }, null, 512 /* NEED_PATCH */ )), [ [vue.vModelText, $data.form.mobile] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "电话"), !$data.editing ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "value" }, vue.toDisplayString($data.d.phone || "—"), 1 /* TEXT */ )) : vue.withDirectives((vue.openBlock(), vue.createElementBlock( "input", { key: 1, class: "value-input", "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.form.phone = $event), placeholder: "可选(座机)" }, null, 512 /* NEED_PATCH */ )), [ [vue.vModelText, $data.form.phone] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "地址"), !$data.editing ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "value" }, vue.toDisplayString($data.d.address || "—"), 1 /* TEXT */ )) : vue.withDirectives((vue.openBlock(), vue.createElementBlock( "input", { key: 1, class: "value-input", "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => $data.form.address = $event), placeholder: "可选" }, null, 512 /* NEED_PATCH */ )), [ [vue.vModelText, $data.form.address] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "售价档位"), !$data.editing ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "value" }, vue.toDisplayString($data.d.priceLevel), 1 /* TEXT */ )) : (vue.openBlock(), vue.createElementBlock("picker", { key: 1, range: $data.priceLabels, value: $data.priceIdx, onChange: _cache[5] || (_cache[5] = (...args) => $options.onPriceChange && $options.onPriceChange(...args)) }, [ vue.createElementVNode( "view", { class: "value" }, vue.toDisplayString($data.priceLabels[$data.priceIdx]), 1 /* TEXT */ ) ], 40, ["range", "value"])) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "初始应收"), !$data.editing ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "value" }, "¥ " + vue.toDisplayString(Number($data.d.arOpening || 0).toFixed(2)), 1 /* TEXT */ )) : vue.withDirectives((vue.openBlock(), vue.createElementBlock( "input", { key: 1, class: "value-input", type: "digit", "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => $data.form.arOpening = $event), placeholder: "0.00" }, null, 512 /* NEED_PATCH */ )), [ [ vue.vModelText, $data.form.arOpening, void 0, { number: true } ] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "当前应收"), vue.createElementVNode( "text", { class: "value emp" }, "¥ " + vue.toDisplayString(Number($data.d.receivable || 0).toFixed(2)), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "备注"), !$data.editing ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0, class: "value" }, vue.toDisplayString($data.d.remark || "—"), 1 /* TEXT */ )) : vue.withDirectives((vue.openBlock(), vue.createElementBlock( "input", { key: 1, class: "value-input", "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => $data.form.remark = $event), placeholder: "—" }, null, 512 /* NEED_PATCH */ )), [ [vue.vModelText, $data.form.remark] ]) ]) ]), vue.createElementVNode("view", { class: "bottom" }, [ vue.createElementVNode( "button", { class: "ghost", onClick: _cache[8] || (_cache[8] = (...args) => $options.toggleEdit && $options.toggleEdit(...args)) }, vue.toDisplayString($data.editing ? "取消" : "编辑"), 1 /* TEXT */ ), $data.editing ? (vue.openBlock(), vue.createElementBlock("button", { key: 0, class: "primary", onClick: _cache[9] || (_cache[9] = (...args) => $options.save && $options.save(...args)) }, "保存")) : (vue.openBlock(), vue.createElementBlock("button", { key: 1, class: "primary", onClick: _cache[10] || (_cache[10] = (...args) => $options.choose && $options.choose(...args)) }, "选择此客户")) ]) ]); } const PagesCustomerDetail = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["render", _sfc_render$e], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/customer/detail.vue"]]); const _sfc_main$e = { data() { return { kw: "", debtOnly: false, suppliers: [] }; }, onLoad() { this.search(); }, methods: { toggleDebtOnly() { this.debtOnly = !this.debtOnly; this.search(); }, async search() { try { const res = await get("/api/suppliers", { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 }); this.suppliers = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; } catch (e) { uni.showToast({ title: "加载失败", icon: "none" }); } }, createSupplier() { uni.navigateTo({ url: "/pages/supplier/form" }); }, select(s) { try { const pages = getCurrentPages(); const opener = pages && pages.length >= 2 ? pages[pages.length - 2] : null; const vm = opener && opener.$vm ? opener.$vm : null; const canPick = !!(vm && vm.order); if (canPick) { vm.order.supplierId = s.id; if (Object.prototype.hasOwnProperty.call(vm, "supplierName")) vm.supplierName = s.name; uni.navigateBack(); } else { uni.navigateTo({ url: `/pages/supplier/form?id=${s.id}` }); } } catch (_) { uni.navigateTo({ url: `/pages/supplier/form?id=${s.id}` }); } } } }; function _sfc_render$d(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "search" }, [ vue.withDirectives(vue.createElementVNode( "input", { "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.kw = $event), placeholder: "搜索供应商名称/电话", onConfirm: _cache[1] || (_cache[1] = (...args) => $options.search && $options.search(...args)) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [vue.vModelText, $data.kw] ]), vue.createElementVNode("button", { size: "mini", onClick: _cache[2] || (_cache[2] = (...args) => $options.search && $options.search(...args)) }, "搜索"), vue.createElementVNode("button", { size: "mini", type: $data.debtOnly ? "primary" : "default", onClick: _cache[3] || (_cache[3] = (...args) => $options.toggleDebtOnly && $options.toggleDebtOnly(...args)) }, "只看欠款", 8, ["type"]) ]), vue.createElementVNode("scroll-view", { "scroll-y": "", class: "list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.suppliers, (s) => { return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: s.id, onClick: ($event) => $options.select(s) }, [ vue.createElementVNode( "view", { class: "name" }, vue.toDisplayString(s.name), 1 /* TEXT */ ), vue.createElementVNode("view", { class: "meta" }, [ vue.createTextVNode( vue.toDisplayString(s.mobile || "—") + " ", 1 /* TEXT */ ), typeof s.apPayable === "number" ? (vue.openBlock(), vue.createElementBlock( "text", { key: 0 }, "|应付:¥ " + vue.toDisplayString(Number(s.apPayable).toFixed(2)), 1 /* TEXT */ )) : vue.createCommentVNode("v-if", true) ]) ], 8, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ]), vue.createElementVNode("view", { class: "bottom" }, [ vue.createElementVNode("button", { class: "primary", onClick: _cache[4] || (_cache[4] = (...args) => $options.createSupplier && $options.createSupplier(...args)) }, "新增供应商") ]) ]); } const PagesSupplierSelect = /* @__PURE__ */ _export_sfc(_sfc_main$e, [["render", _sfc_render$d], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/supplier/select.vue"]]); const _sfc_main$d = { data() { return { id: null, form: { name: "", contactName: "", mobile: "", phone: "", address: "", apOpening: 0, apPayable: 0, remark: "" } }; }, onLoad(query) { if (query && query.id) { this.id = Number(query.id); this.load(); } }, methods: { async load() { if (!this.id) return; try { const d = await get(`/api/suppliers/${this.id}`); this.form = { name: (d == null ? void 0 : d.name) || "", contactName: (d == null ? void 0 : d.contactName) || "", mobile: (d == null ? void 0 : d.mobile) || "", phone: (d == null ? void 0 : d.phone) || "", address: (d == null ? void 0 : d.address) || "", apOpening: Number((d == null ? void 0 : d.apOpening) || 0), apPayable: Number((d == null ? void 0 : d.apPayable) || 0), remark: (d == null ? void 0 : d.remark) || "" }; } catch (e) { uni.showToast({ title: (e == null ? void 0 : e.message) || "加载失败", icon: "none" }); } }, async save() { if (!this.form.name) return uni.showToast({ title: "请填写供应商名称", icon: "none" }); try { if (this.id) await put(`/api/suppliers/${this.id}`, this.form); else await post("/api/suppliers", this.form); uni.showToast({ title: "保存成功", icon: "success" }); setTimeout(() => uni.navigateBack(), 500); } catch (e) { uni.showToast({ title: (e == null ? void 0 : e.message) || "保存失败", icon: "none" }); } } } }; function _sfc_render$c(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "供应商名称"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.form.name = $event), placeholder: "必填" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.name] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "联系人"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.form.contactName = $event), placeholder: "可选" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.contactName] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "手机"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $data.form.mobile = $event), placeholder: "可选" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.mobile] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "电话"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.form.phone = $event), placeholder: "可选(座机)" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.phone] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "经营地址"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => $data.form.address = $event), placeholder: "可选" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.address] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "初始应付款"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", type: "digit", "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => $data.form.apOpening = $event), placeholder: "默认 0.00" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.apOpening, void 0, { number: true } ] ]) ]), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "应付款"), vue.withDirectives(vue.createElementVNode( "input", { class: "value", type: "digit", "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => $data.form.apPayable = $event), placeholder: "默认 0.00" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.apPayable, void 0, { number: true } ] ]) ]), vue.createElementVNode("view", { class: "textarea" }, [ vue.withDirectives(vue.createElementVNode( "textarea", { "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => $data.form.remark = $event), maxlength: "200", placeholder: "备注(最多200字)" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.remark] ]) ]), vue.createElementVNode("view", { class: "bottom" }, [ vue.createElementVNode("button", { class: "primary", onClick: _cache[8] || (_cache[8] = (...args) => $options.save && $options.save(...args)) }, "保存") ]) ]); } const PagesSupplierForm = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["render", _sfc_render$c], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/supplier/form.vue"]]); const TYPE_MAP = { cash: "现金", bank: "银行", alipay: "支付宝", wechat: "微信", other: "其他" }; const _sfc_main$c = { data() { return { accounts: [], mode: "view" }; }, async onLoad(q) { this.mode = q && q.mode || "view"; try { const res = await get("/api/accounts"); this.accounts = Array.isArray(res) ? res : (res == null ? void 0 : res.list) || []; } catch (e) { uni.showToast({ title: "加载失败", icon: "none" }); } }, methods: { select(a) { if (this.mode === "pick") { const opener = getCurrentPages()[getCurrentPages().length - 2]; if (opener && opener.$vm) { opener.$vm.selectedAccountId = a.id; opener.$vm.selectedAccountName = a.name; } uni.navigateBack(); } else { uni.navigateTo({ url: `/pages/account/ledger?id=${a.id}` }); } }, create() { uni.navigateTo({ url: "/pages/account/form" }); }, typeLabel(t) { return TYPE_MAP[t] || t; } } }; function _sfc_render$b(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("scroll-view", { "scroll-y": "", class: "list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.accounts, (a) => { var _a; return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: a.id, onClick: ($event) => $options.select(a) }, [ vue.createElementVNode( "view", { class: "name" }, vue.toDisplayString(a.name), 1 /* TEXT */ ), vue.createElementVNode( "view", { class: "meta" }, vue.toDisplayString($options.typeLabel(a.type)) + " · 余额:" + vue.toDisplayString(((_a = a.balance) == null ? void 0 : _a.toFixed) ? a.balance.toFixed(2) : a.balance), 1 /* TEXT */ ) ], 8, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ]), vue.createElementVNode("view", { class: "fab", onClick: _cache[0] || (_cache[0] = (...args) => $options.create && $options.create(...args)) }, "+") ]); } const PagesAccountSelect = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["render", _sfc_render$b], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/account/select.vue"]]); const _sfc_main$b = { data() { return { accountId: null, startDate: "", endDate: "", list: [], opening: 0, income: 0, expense: 0, ending: 0 }; }, onLoad(query) { this.accountId = Number(query && query.id); this.quickInit(); this.load(); }, methods: { quickInit() { const now = /* @__PURE__ */ new Date(); const y = now.getFullYear(), m = now.getMonth() + 1; this.startDate = `${y}-${String(m).padStart(2, "0")}-01`; const lastDay = new Date(y, m, 0).getDate(); this.endDate = `${y}-${String(m).padStart(2, "0")}-${String(lastDay).padStart(2, "0")}`; }, async load(page = 1, size = 50) { try { const res = await get(`/api/accounts/${this.accountId}/ledger`, { startDate: this.startDate, endDate: this.endDate, page, size }); this.list = res && res.list || []; this.opening = Number(res && res.opening || 0); this.income = Number(res && res.income || 0); this.expense = Number(res && res.expense || 0); this.ending = Number(res && res.ending || 0); } catch (e) { uni.showToast({ title: "加载失败", icon: "none" }); } }, fmt(v) { return (typeof v === "number" ? v : Number(v || 0)).toFixed(2); }, formatDate(s) { if (!s) return "-"; try { const d = new Date(s); const pad = (n) => String(n).padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`; } catch (e) { return s; } } } }; function _sfc_render$a(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "filters" }, [ vue.createElementVNode("picker", { mode: "date", value: $data.startDate, onChange: _cache[0] || (_cache[0] = (e) => { $data.startDate = e.detail.value; $options.load(); }) }, [ vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "开始"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.startDate || "—"), 1 /* TEXT */ ) ]) ], 40, ["value"]), vue.createElementVNode("picker", { mode: "date", value: $data.endDate, onChange: _cache[1] || (_cache[1] = (e) => { $data.endDate = e.detail.value; $options.load(); }) }, [ vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "结束"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($data.endDate || "—"), 1 /* TEXT */ ) ]) ], 40, ["value"]) ]), vue.createElementVNode("view", { class: "summary" }, [ vue.createElementVNode("view", { class: "sum-item" }, [ vue.createElementVNode("text", { class: "k" }, "收入"), vue.createElementVNode( "text", { class: "v" }, vue.toDisplayString($options.fmt($data.income)), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "sum-item" }, [ vue.createElementVNode("text", { class: "k" }, "支出"), vue.createElementVNode( "text", { class: "v" }, vue.toDisplayString($options.fmt($data.expense)), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "sum-item" }, [ vue.createElementVNode("text", { class: "k" }, "期初"), vue.createElementVNode( "text", { class: "v" }, vue.toDisplayString($options.fmt($data.opening)), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "sum-item" }, [ vue.createElementVNode("text", { class: "k" }, "期末"), vue.createElementVNode( "text", { class: "v" }, vue.toDisplayString($options.fmt($data.ending)), 1 /* TEXT */ ) ]) ]), vue.createElementVNode("scroll-view", { "scroll-y": "", class: "list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.list, (it) => { return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: it.id }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode( "text", { class: "title" }, vue.toDisplayString(it.src === "other" ? it.category || "其他" : it.remark || "收付款"), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: vue.normalizeClass(["amount", { in: it.direction === "in", out: it.direction === "out" }]) }, vue.toDisplayString(it.direction === "in" ? "+" : "-") + vue.toDisplayString($options.fmt(it.amount)), 3 /* TEXT, CLASS */ ) ]), vue.createElementVNode( "view", { class: "meta" }, vue.toDisplayString($options.formatDate(it.tx_time || it.txTime)) + " · " + vue.toDisplayString(it.remark || "-"), 1 /* TEXT */ ) ]); }), 128 /* KEYED_FRAGMENT */ )) ]) ]); } const PagesAccountLedger = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["render", _sfc_render$a], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/account/ledger.vue"]]); const _sfc_main$a = { data() { return { id: null, form: { name: "", type: "cash", bankName: "", bankAccount: "", openingBalance: "" }, showType: false, types: [ { key: "cash", name: "现金" }, { key: "bank", name: "银行存款" }, { key: "wechat", name: "微信" }, { key: "alipay", name: "支付宝" }, { key: "other", name: "其他" } ] }; }, onLoad(q) { this.id = q && q.id ? Number(q.id) : null; if (this.id) this.load(); }, methods: { typeLabel(t) { const m = { cash: "现金", bank: "银行存款", wechat: "微信", alipay: "支付宝", other: "其他" }; return m[t] || t; }, async load() { try { const list = await get("/api/accounts"); const a = (Array.isArray(list) ? list : (list == null ? void 0 : list.list) || []).find((x) => x.id == this.id); if (a) { this.form = { name: a.name, type: a.type, bankName: a.bank_name || a.bankName || "", bankAccount: a.bank_account || a.bankAccount || "", openingBalance: "" }; } } catch (e) { } }, async save() { if (!this.form.name) { uni.showToast({ title: "请输入名称", icon: "none" }); return; } try { const body = { ...this.form, openingBalance: Number(this.form.openingBalance || 0) }; if (this.id) await put(`/api/accounts/${this.id}`, body); else await post("/api/accounts", { ...body, status: 1 }); uni.showToast({ title: "已保存", icon: "success" }); setTimeout(() => uni.navigateBack(), 300); } catch (e) { uni.showToast({ title: "保存失败", icon: "none" }); } } } }; function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) { const _component_uni_popup = vue.resolveComponent("uni-popup"); return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createElementVNode("view", { class: "form" }, [ vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "账户名称"), vue.withDirectives(vue.createElementVNode( "input", { class: "input", "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.form.name = $event), placeholder: "必填" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.name] ]) ]), vue.createElementVNode("view", { class: "field", onClick: _cache[1] || (_cache[1] = ($event) => $data.showType = true) }, [ vue.createElementVNode("text", { class: "label" }, "账户类型"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.typeLabel($data.form.type)), 1 /* TEXT */ ) ]), $data.form.type === "bank" ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "银行名称"), vue.withDirectives(vue.createElementVNode( "input", { class: "input", "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $data.form.bankName = $event), placeholder: "选填" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.bankName] ]) ])) : vue.createCommentVNode("v-if", true), $data.form.type === "bank" ? (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "银行账号"), vue.withDirectives(vue.createElementVNode( "input", { class: "input", "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.form.bankAccount = $event), placeholder: "选填" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.bankAccount] ]) ])) : vue.createCommentVNode("v-if", true), vue.createElementVNode("view", { class: "field" }, [ vue.createElementVNode("text", { class: "label" }, "当前余额"), vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "number", "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => $data.form.openingBalance = $event), placeholder: "0.00" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.form.openingBalance] ]) ]) ]), vue.createElementVNode("view", { class: "actions" }, [ vue.createElementVNode("button", { class: "primary", onClick: _cache[5] || (_cache[5] = (...args) => $options.save && $options.save(...args)) }, "保存") ]), vue.createVNode(_component_uni_popup, { ref: "popup", type: "bottom", modelValue: $data.showType, "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => $data.showType = $event) }, { default: vue.withCtx(() => [ vue.createElementVNode("view", { class: "sheet" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.types, (t) => { return vue.openBlock(), vue.createElementBlock("view", { class: "sheet-item", key: t.key, onClick: ($event) => { $data.form.type = t.key; $data.showType = false; } }, vue.toDisplayString(t.name), 9, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )), vue.createElementVNode("view", { class: "sheet-cancel", onClick: _cache[6] || (_cache[6] = ($event) => $data.showType = false) }, "取消") ]) ]), _: 1 /* STABLE */ }, 8, ["modelValue"]) ]); } const PagesAccountForm = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["render", _sfc_render$9], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/account/form.vue"]]); const API_OF = { sale: "/api/orders", purchase: "/api/purchase-orders", collect: "/api/payments", fund: "/api/other-transactions" }; const _sfc_main$9 = { data() { return { biz: "sale", bizList: [ { key: "sale", name: "出货" }, { key: "purchase", name: "进货" }, { key: "collect", name: "收款" }, { key: "fund", name: "资金" } ], range: "month", query: { kw: "" }, items: [], page: 1, size: 15, finished: false, loading: false, startDate: "", endDate: "", nonVipRetentionDays: 0, isVip: false }; }, computed: { placeholder() { return "单据号/客户名称/品名规格/备注"; }, periodLabel() { return this.startDate && this.endDate ? `${this.startDate}~${this.endDate}` : ""; }, totalAmount() { return this.items.reduce((s, it) => s + Number(it.amount || 0), 0); } }, onLoad() { const hasToken = (() => { try { return !!uni.getStorageSync("TOKEN"); } catch (e) { return false; } })(); if (!hasToken) { this.items = []; this.total = 0; uni.showToast({ title: "请登录使用该功能", icon: "none" }); return; } try { formatAppLog("log", "at pages/detail/index.vue:104", "[detail] onLoad route = pages/detail/index"); } catch (e) { } this.computeRange(); this.reload(); }, methods: { switchBiz(k) { if (this.biz === k) return; this.biz = k; this.reload(); }, switchRange(r) { this.range = r; this.computeRange(); this.reload(); }, computeRange() { const now = /* @__PURE__ */ new Date(); const pad = (n) => String(n).padStart(2, "0"); const fmt = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; let start = now, end = now; if (this.range === "today") { start = end = now; } else if (this.range === "week") { const day = now.getDay() || 7; start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - day + 1); end = now; } else if (this.range === "month") { start = new Date(now.getFullYear(), now.getMonth(), 1); end = new Date(now.getFullYear(), now.getMonth() + 1, 0); } else if (this.range === "year") { start = new Date(now.getFullYear(), 0, 1); end = new Date(now.getFullYear(), 11, 31); } else { start = new Date(now.getFullYear(), now.getMonth(), 1); end = new Date(now.getFullYear(), now.getMonth() + 1, 0); } this.startDate = fmt(start); this.endDate = fmt(end); }, reload() { this.items = []; this.page = 1; this.finished = false; this.loadMore(); }, onStartChange(e) { var _a; this.startDate = ((_a = e == null ? void 0 : e.detail) == null ? void 0 : _a.value) || this.startDate; if (this.endDate && this.startDate > this.endDate) this.endDate = this.startDate; this.reload(); }, onEndChange(e) { var _a; this.endDate = ((_a = e == null ? void 0 : e.detail) == null ? void 0 : _a.value) || this.endDate; if (this.startDate && this.endDate < this.startDate) this.startDate = this.endDate; this.reload(); }, async loadMore() { if (this.loading || this.finished) return; this.loading = true; try { const path = API_OF[this.biz] || "/api/orders"; const params = { kw: this.query.kw, page: this.page, size: this.size, startDate: this.startDate, endDate: this.endDate, biz: this.biz }; if (this.biz === "sale") params.type = "out"; const res = await get(path, params); const list = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : []; this.items = this.items.concat(list); if (list.length < this.size) this.finished = true; this.page += 1; await this.hintIfNonVipOutOfWindow(); } catch (e) { uni.showToast({ title: "加载失败", icon: "none" }); } finally { this.loading = false; } }, async hintIfNonVipOutOfWindow() { try { if (this.isVip && this.isVip === true) return; if (!this.nonVipRetentionDays) { const v = await get("/api/vip/status"); this.isVip = !!(v == null ? void 0 : v.isVip); this.nonVipRetentionDays = Number((v == null ? void 0 : v.nonVipRetentionDays) || 60); if (this.isVip) return; } if (!this.startDate) return; const start = new Date(this.startDate).getTime(); const threshold = Date.now() - this.nonVipRetentionDays * 24 * 3600 * 1e3; if (start < threshold) { uni.showModal({ title: "提示", content: `普通用户仅显示近${this.nonVipRetentionDays}天数据,开通VIP可查看全部历史。`, confirmText: "去开通VIP", cancelText: "我知道了", success: (r) => { if (r.confirm) uni.navigateTo({ url: "/pages/my/vip" }); } }); } } catch (e) { } }, formatDate(s) { if (!s) return ""; try { const d = new Date(s); const pad = (n) => String(n).padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; } catch (_) { return String(s).slice(0, 10); } }, onCreate() { if (this.biz === "sale") { uni.switchTab({ url: "/pages/order/create" }); return; } uni.showToast({ title: "该类型创建页待实现", icon: "none" }); }, openDetail(it) { uni.showToast({ title: "详情开发中", icon: "none" }); } } }; function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "page" }, [ vue.createCommentVNode(" 业务类型侧边切换:销售/进货/收款/资金 "), vue.createElementVNode("view", { class: "content" }, [ vue.createElementVNode("view", { class: "biz-tabs" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.bizList, (b) => { return vue.openBlock(), vue.createElementBlock("view", { key: b.key, class: vue.normalizeClass(["biz", $data.biz === b.key && "active"]), onClick: ($event) => $options.switchBiz(b.key) }, vue.toDisplayString(b.name), 11, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ]), vue.createElementVNode("view", { class: "panel" }, [ vue.createCommentVNode(" 期间选择 + 搜索框 + 查询按钮 "), vue.createElementVNode("view", { class: "toolbar" }, [ vue.createElementVNode("view", { class: "period-group" }, [ vue.createElementVNode("text", { class: "period-label" }, "期间"), vue.createElementVNode("picker", { mode: "date", value: $data.startDate, onChange: _cache[0] || (_cache[0] = (...args) => $options.onStartChange && $options.onStartChange(...args)) }, [ vue.createElementVNode( "view", { class: "date-chip" }, vue.toDisplayString($data.startDate), 1 /* TEXT */ ) ], 40, ["value"]), vue.createElementVNode("text", { class: "sep" }, "~"), vue.createElementVNode("picker", { mode: "date", value: $data.endDate, onChange: _cache[1] || (_cache[1] = (...args) => $options.onEndChange && $options.onEndChange(...args)) }, [ vue.createElementVNode( "view", { class: "date-chip" }, vue.toDisplayString($data.endDate), 1 /* TEXT */ ) ], 40, ["value"]) ]), vue.createElementVNode("view", { class: "search-row" }, [ vue.createElementVNode("view", { class: "search" }, [ vue.withDirectives(vue.createElementVNode("input", { class: "search-input", "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $data.query.kw = $event), placeholder: $options.placeholder, onConfirm: _cache[3] || (_cache[3] = (...args) => $options.reload && $options.reload(...args)) }, null, 40, ["placeholder"]), [ [ vue.vModelText, $data.query.kw, void 0, { trim: true } ] ]) ]), vue.createElementVNode("button", { class: "btn", size: "mini", onClick: _cache[4] || (_cache[4] = (...args) => $options.reload && $options.reload(...args)) }, "查询") ]) ]), vue.createElementVNode( "view", { class: "total" }, "合计:¥" + vue.toDisplayString($options.totalAmount.toFixed(2)), 1 /* TEXT */ ), vue.createCommentVNode(" 列表 "), vue.createElementVNode( "scroll-view", { "scroll-y": "", class: "list", onScrolltolower: _cache[5] || (_cache[5] = (...args) => $options.loadMore && $options.loadMore(...args)) }, [ $data.items.length ? (vue.openBlock(true), vue.createElementBlock( vue.Fragment, { key: 0 }, vue.renderList($data.items, (it) => { return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: it.id, onClick: ($event) => $options.openDetail(it) }, [ vue.createElementVNode("view", { class: "item-left" }, [ vue.createElementVNode( "view", { class: "date" }, vue.toDisplayString($options.formatDate(it.orderTime || it.txTime || it.createdAt)), 1 /* TEXT */ ), vue.createElementVNode( "view", { class: "name" }, vue.toDisplayString(it.customerName || it.supplierName || it.accountName || it.remark || "-"), 1 /* TEXT */ ), vue.createElementVNode( "view", { class: "no" }, vue.toDisplayString(it.orderNo || it.code || it.id), 1 /* TEXT */ ) ]), vue.createElementVNode( "view", { class: vue.normalizeClass(["amount", { in: Number(it.amount || 0) >= 0, out: Number(it.amount || 0) < 0 }]) }, "¥ " + vue.toDisplayString((it.amount || 0).toFixed(2)), 3 /* TEXT, CLASS */ ), vue.createElementVNode("view", { class: "arrow" }, "›") ], 8, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "empty" }, "暂无数据")), $data.items.length && !$data.finished ? vue.withDirectives((vue.openBlock(), vue.createElementBlock( "view", { key: 2, class: "loading" }, "加载中...", 512 /* NEED_PATCH */ )), [ [vue.vShow, $data.loading] ]) : vue.createCommentVNode("v-if", true), $data.finished && $data.items.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 3, class: "finished" }, "没有更多了")) : vue.createCommentVNode("v-if", true) ], 32 /* NEED_HYDRATION */ ), vue.createCommentVNode(" 右下角新增按钮:根据业务类型跳转对应开单页或创建页 "), vue.createElementVNode("view", { class: "fab", onClick: _cache[6] || (_cache[6] = (...args) => $options.onCreate && $options.onCreate(...args)) }, "+") ]) ]) ]); } const PagesDetailIndex = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["render", _sfc_render$8], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/detail/index.vue"]]); const _sfc_main$8 = { data() { return { loading: false, tab: "login", authLoginTopImage: AUTH_LOGIN_TOP_IMAGE, loginForm: { email: "", password: "" }, regForm: { name: "", email: "", code: "", password: "", password2: "" }, resetForm: { email: "", code: "", password: "", password2: "" }, regCountdown: 0, resetCountdown: 0, _timers: [] }; }, beforeUnmount() { this._timers.forEach((t) => clearInterval(t)); }, methods: { gotoRegister() { this.tab = "register"; }, gotoReset() { this.tab = "reset"; }, toast(msg) { try { uni.showToast({ title: String(msg || "操作失败"), icon: "none" }); } catch (_) { } }, validateEmail(v) { return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(String(v || "").trim()); }, startCountdown(key) { if (this[key] > 0) return; this[key] = 60; const timer = setInterval(() => { this[key] = Math.max(0, this[key] - 1); if (this[key] === 0) clearInterval(timer); }, 1e3); this._timers.push(timer); }, async onLogin() { const { email, password } = this.loginForm; if (!this.validateEmail(email)) return this.toast("请输入正确邮箱"); if (!password || password.length < 6) return this.toast("请输入至少6位密码"); this.loading = true; try { const data = await post("/api/auth/password/login", { email, password }); this.afterLogin(data); } catch (e) { this.toast(e.message); } finally { this.loading = false; } }, afterLogin(data) { try { if (data && data.token) { uni.setStorageSync("TOKEN", data.token); if (data.user && data.user.shopId) uni.setStorageSync("SHOP_ID", data.user.shopId); uni.setStorageSync("ENABLE_DEFAULT_USER", "false"); uni.removeStorageSync("DEFAULT_USER_ID"); this.toast("登录成功"); setTimeout(() => { uni.reLaunch({ url: "/pages/index/index" }); }, 300); } else { this.toast("登录失败"); } } catch (_) { this.toast("登录失败"); } }, async sendRegCode() { if (!this.validateEmail(this.regForm.email)) return this.toast("请输入正确邮箱"); this.loading = true; try { const r = await post("/api/auth/email/send", { email: this.regForm.email, scene: "register" }); if (r && r.ok) this.startCountdown("regCountdown"); this.toast(r && r.ok ? "验证码已发送" : "发送过于频繁"); } catch (e) { this.toast(e.message); } finally { this.loading = false; } }, async onRegister() { const f = this.regForm; if (!f.name || f.name.trim().length < 1) return this.toast("请输入用户名"); if (!this.validateEmail(f.email)) return this.toast("请输入正确邮箱"); if (!f.code) return this.toast("请输入验证码"); if (!f.password || f.password.length < 6) return this.toast("密码至少6位"); if (f.password !== f.password2) return this.toast("两次密码不一致"); this.loading = true; try { const data = await post("/api/auth/email/register", { name: f.name.trim(), email: f.email.trim(), code: f.code.trim(), password: f.password }); this.afterLogin(data); } catch (e) { this.toast(e.message); } finally { this.loading = false; } }, async sendResetCode() { if (!this.validateEmail(this.resetForm.email)) return this.toast("请输入正确邮箱"); this.loading = true; try { const r = await post("/api/auth/email/send", { email: this.resetForm.email, scene: "reset" }); if (r && r.ok) this.startCountdown("resetCountdown"); this.toast(r && r.ok ? "验证码已发送" : "发送过于频繁"); } catch (e) { this.toast(e.message); } finally { this.loading = false; } }, async onReset() { const f = this.resetForm; if (!this.validateEmail(f.email)) return this.toast("请输入正确邮箱"); if (!f.code) return this.toast("请输入验证码"); if (!f.password || f.password.length < 6) return this.toast("新密码至少6位"); if (f.password !== f.password2) return this.toast("两次密码不一致"); this.loading = true; try { const r = await post("/api/auth/email/reset-password", { email: f.email.trim(), code: f.code.trim(), newPassword: f.password, confirmPassword: f.password2 }); if (r && r.ok) { this.toast("已重置,请使用新密码登录"); this.tab = "login"; this.loginForm.email = f.email; } else this.toast("重置失败"); } catch (e) { this.toast(e.message); } finally { this.loading = false; } } } }; function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "auth-page" }, [ vue.createElementVNode("view", { class: "login-hero" }, [ vue.createElementVNode("image", { class: "login-hero-img", src: $data.authLoginTopImage, mode: "widthFix" }, null, 8, ["src"]) ]), vue.createElementVNode("view", { class: "header" }, [ vue.createElementVNode("text", { class: "title" }, "邮箱密码登录") ]), $data.tab === "login" ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "panel" }, [ vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "text", "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.loginForm.email = $event), placeholder: "输入邮箱" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.loginForm.email, void 0, { trim: true } ] ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "password", "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.loginForm.password = $event), placeholder: "输入密码" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.loginForm.password] ]), vue.createElementVNode("button", { class: "btn primary", disabled: $data.loading, onClick: _cache[2] || (_cache[2] = (...args) => $options.onLogin && $options.onLogin(...args)) }, "登录", 8, ["disabled"]), vue.createElementVNode("view", { class: "quick-inline" }, [ vue.createElementVNode("button", { class: "quick-link", onClick: _cache[3] || (_cache[3] = (...args) => $options.gotoRegister && $options.gotoRegister(...args)) }, "注册"), vue.createElementVNode("button", { class: "quick-link", onClick: _cache[4] || (_cache[4] = (...args) => $options.gotoReset && $options.gotoReset(...args)) }, "忘记密码") ]) ])) : vue.createCommentVNode("v-if", true), $data.tab === "register" ? (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "panel minor" }, [ vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "text", "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => $data.regForm.name = $event), placeholder: "输入用户名" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.regForm.name, void 0, { trim: true } ] ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "text", "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => $data.regForm.email = $event), placeholder: "输入邮箱" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.regForm.email, void 0, { trim: true } ] ]), vue.createElementVNode("view", { class: "row" }, [ vue.withDirectives(vue.createElementVNode( "input", { class: "input flex1", type: "text", "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => $data.regForm.code = $event), placeholder: "邮箱验证码" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.regForm.code, void 0, { trim: true } ] ]), vue.createElementVNode("button", { class: "btn ghost", disabled: $data.regCountdown > 0 || $data.loading, onClick: _cache[8] || (_cache[8] = (...args) => $options.sendRegCode && $options.sendRegCode(...args)) }, vue.toDisplayString($data.regCountdown > 0 ? $data.regCountdown + "s" : "获取验证码"), 9, ["disabled"]) ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "password", "onUpdate:modelValue": _cache[9] || (_cache[9] = ($event) => $data.regForm.password = $event), placeholder: "输入密码(≥6位)" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.regForm.password] ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "password", "onUpdate:modelValue": _cache[10] || (_cache[10] = ($event) => $data.regForm.password2 = $event), placeholder: "再次输入密码" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.regForm.password2] ]), vue.createElementVNode("button", { class: "btn primary", disabled: $data.loading, onClick: _cache[11] || (_cache[11] = (...args) => $options.onRegister && $options.onRegister(...args)) }, "注册新用户", 8, ["disabled"]) ])) : vue.createCommentVNode("v-if", true), $data.tab === "reset" ? (vue.openBlock(), vue.createElementBlock("view", { key: 2, class: "panel minor" }, [ vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "text", "onUpdate:modelValue": _cache[12] || (_cache[12] = ($event) => $data.resetForm.email = $event), placeholder: "输入邮箱" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.resetForm.email, void 0, { trim: true } ] ]), vue.createElementVNode("view", { class: "row" }, [ vue.withDirectives(vue.createElementVNode( "input", { class: "input flex1", type: "text", "onUpdate:modelValue": _cache[13] || (_cache[13] = ($event) => $data.resetForm.code = $event), placeholder: "邮箱验证码" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.resetForm.code, void 0, { trim: true } ] ]), vue.createElementVNode("button", { class: "btn ghost", disabled: $data.resetCountdown > 0 || $data.loading, onClick: _cache[14] || (_cache[14] = (...args) => $options.sendResetCode && $options.sendResetCode(...args)) }, vue.toDisplayString($data.resetCountdown > 0 ? $data.resetCountdown + "s" : "获取验证码"), 9, ["disabled"]) ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "password", "onUpdate:modelValue": _cache[15] || (_cache[15] = ($event) => $data.resetForm.password = $event), placeholder: "新密码(≥6位)" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.resetForm.password] ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "password", "onUpdate:modelValue": _cache[16] || (_cache[16] = ($event) => $data.resetForm.password2 = $event), placeholder: "再次输入新密码" }, null, 512 /* NEED_PATCH */ ), [ [vue.vModelText, $data.resetForm.password2] ]), vue.createElementVNode("button", { class: "btn primary", disabled: $data.loading, onClick: _cache[17] || (_cache[17] = (...args) => $options.onReset && $options.onReset(...args)) }, "重置密码", 8, ["disabled"]) ])) : vue.createCommentVNode("v-if", true) ]); } const PagesAuthLogin = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["render", _sfc_render$7], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/auth/login.vue"]]); const _sfc_main$7 = { data() { return { form: { shopName: "", name: "", email: "", code: "", password: "" }, shopNameFocused: false, nameFocused: false, emailFocused: false, codeFocused: false, pwdFocused: false, countdown: 0, timer: null, sending: false }; }, computed: { btnText() { if (this.countdown > 0) return `${this.countdown}s`; if (this.sending) return "发送中..."; return "获取验证码"; } }, methods: { validate() { const email = String(this.form.email || "").trim(); const ok = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(email); if (!ok) { uni.showToast({ title: "请输入正确的邮箱地址", icon: "none" }); return false; } if (!/^\d{6}$/.test(String(this.form.code || "").trim())) { uni.showToast({ title: "验证码格式不正确", icon: "none" }); return false; } if (String(this.form.password || "").length < 6) { uni.showToast({ title: "密码至少6位", icon: "none" }); return false; } return true; }, startCountdown(sec) { this.countdown = sec; if (this.timer) clearInterval(this.timer); this.timer = setInterval(() => { if (this.countdown <= 1) { clearInterval(this.timer); this.timer = null; this.countdown = 0; return; } this.countdown--; }, 1e3); }, async sendCode() { if (this.sending || this.countdown > 0) return; const e = String(this.form.email || "").trim(); const ok = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(e); if (!ok) return uni.showToast({ title: "请输入正确的邮箱地址", icon: "none" }); this.sending = true; try { const res = await post("/api/auth/email/send", { email: e, scene: "login" }); const cd = Number(res && res.cooldownSec || 60); this.startCountdown(cd); uni.showToast({ title: "验证码已发送", icon: "none" }); } catch (e2) { const msg = e2 && e2.message || "发送失败"; uni.showToast({ title: msg, icon: "none" }); } finally { this.sending = false; } }, async onRegister() { if (!this.validate()) return; const email = String(this.form.email || "").trim(); const name = String(this.form.name || "").trim(); try { const data = await post("/api/auth/email/register", { email, code: String(this.form.code || "").trim(), name, password: String(this.form.password || "") }); if (data && data.token) { uni.setStorageSync("TOKEN", data.token); if (data.user && data.user.email) uni.setStorageSync("USER_EMAIL", data.user.email); if (name) try { uni.setStorageSync("USER_NAME", name); } catch (_) { } uni.showToast({ title: "注册成功", icon: "none" }); setTimeout(() => { uni.reLaunch({ url: "/pages/index/index" }); }, 300); } } catch (e) { const msg = e && e.message || "注册失败"; uni.showToast({ title: msg, icon: "none" }); } }, onGoLogin() { uni.navigateTo({ url: "/pages/auth/login" }); } } }; function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "register-container" }, [ vue.createCommentVNode(" 背景装饰 "), vue.createElementVNode("view", { class: "background-decoration" }, [ vue.createElementVNode("view", { class: "circle circle-1" }), vue.createElementVNode("view", { class: "circle circle-2" }), vue.createElementVNode("view", { class: "circle circle-3" }) ]), vue.createCommentVNode(" 主要内容卡片 "), vue.createElementVNode("view", { class: "register-card" }, [ vue.createCommentVNode(" 顶部Logo区域 "), vue.createElementVNode("view", { class: "header-section" }, [ vue.createElementVNode("view", { class: "logo-container" }, [ vue.createElementVNode("view", { class: "logo-icon" }, [ (vue.openBlock(), vue.createElementBlock("svg", { viewBox: "0 0 24 24", class: "icon" }, [ vue.createElementVNode("path", { d: "M12 2C13.1 2 14 2.9 14 4C14 5.1 13.1 6 12 6C10.9 6 10 5.1 10 4C10 2.9 10.9 2 12 2ZM21 9V7L15 4V6C15 7.66 13.66 9 12 9S9 7.66 9 6V4L3 7V9C3 10.1 3.9 11 5 11V17C5 18.1 5.9 19 7 19H9C9 20.1 9.9 21 11 21H13C14.1 21 15 20.1 15 19H17C18.1 19 19 18.1 19 17V11C20.1 11 21 10.1 21 9Z" }) ])) ]), vue.createElementVNode("text", { class: "app-name" }, "配件询价") ]), vue.createElementVNode("text", { class: "welcome-text" }, "创建账户"), vue.createElementVNode("text", { class: "subtitle" }, "请填写以下信息完成注册") ]), vue.createCommentVNode(" 表单区域 "), vue.createElementVNode("view", { class: "form-section" }, [ vue.createCommentVNode(" 店铺名称 "), vue.createElementVNode("view", { class: "input-group" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["input-container", { focused: $data.shopNameFocused, filled: $data.form.shopName }]) }, [ vue.createElementVNode("view", { class: "input-icon" }, [ (vue.openBlock(), vue.createElementBlock("svg", { viewBox: "0 0 24 24", class: "icon" }, [ vue.createElementVNode("path", { d: "M12,3L2,12H5V20H19V12H22L12,3M12,8.75A2.25,2.25 0 0,1 14.25,11A2.25,2.25 0 0,1 12,13.25A2.25,2.25 0 0,1 9.75,11A2.25,2.25 0 0,1 12,8.75Z" }) ])) ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input-field", "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.form.shopName = $event), type: "text", placeholder: "请输入店铺名称", onFocus: _cache[1] || (_cache[1] = ($event) => $data.shopNameFocused = true), onBlur: _cache[2] || (_cache[2] = ($event) => $data.shopNameFocused = false) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [ vue.vModelText, $data.form.shopName, void 0, { trim: true } ] ]) ], 2 /* CLASS */ ) ]), vue.createCommentVNode(" 姓名 "), vue.createElementVNode("view", { class: "input-group" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["input-container", { focused: $data.nameFocused, filled: $data.form.name }]) }, [ vue.createElementVNode("view", { class: "input-icon" }, [ (vue.openBlock(), vue.createElementBlock("svg", { viewBox: "0 0 24 24", class: "icon" }, [ vue.createElementVNode("path", { d: "M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" }) ])) ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input-field", "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.form.name = $event), type: "text", placeholder: "请输入您的姓名", onFocus: _cache[4] || (_cache[4] = ($event) => $data.nameFocused = true), onBlur: _cache[5] || (_cache[5] = ($event) => $data.nameFocused = false) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [ vue.vModelText, $data.form.name, void 0, { trim: true } ] ]) ], 2 /* CLASS */ ) ]), vue.createCommentVNode(" 邮箱 "), vue.createElementVNode("view", { class: "input-group" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["input-container", { focused: $data.emailFocused, filled: $data.form.email }]) }, [ vue.createElementVNode("view", { class: "input-icon" }, [ (vue.openBlock(), vue.createElementBlock("svg", { viewBox: "0 0 24 24", class: "icon" }, [ vue.createElementVNode("path", { d: "M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-1 4l-7 4-7-4V6l7 4 7-4v2z" }) ])) ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input-field", "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => $data.form.email = $event), type: "text", placeholder: "请输入邮箱地址", onFocus: _cache[7] || (_cache[7] = ($event) => $data.emailFocused = true), onBlur: _cache[8] || (_cache[8] = ($event) => $data.emailFocused = false) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [ vue.vModelText, $data.form.email, void 0, { trim: true } ] ]) ], 2 /* CLASS */ ) ]), vue.createCommentVNode(" 验证码 "), vue.createElementVNode("view", { class: "input-group" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["input-container", { focused: $data.codeFocused, filled: $data.form.code }]) }, [ vue.createElementVNode("view", { class: "input-icon" }, [ (vue.openBlock(), vue.createElementBlock("svg", { viewBox: "0 0 24 24", class: "icon" }, [ vue.createElementVNode("path", { d: "M3 10h18v2H3v-2zm0 6h12v2H3v-2zM3 6h18v2H3V6z" }) ])) ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input-field", "onUpdate:modelValue": _cache[9] || (_cache[9] = ($event) => $data.form.code = $event), type: "number", maxlength: "6", placeholder: "请输入6位验证码", onFocus: _cache[10] || (_cache[10] = ($event) => $data.codeFocused = true), onBlur: _cache[11] || (_cache[11] = ($event) => $data.codeFocused = false) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [ vue.vModelText, $data.form.code, void 0, { trim: true } ] ]) ], 2 /* CLASS */ ) ]), vue.createCommentVNode(" 密码 "), vue.createElementVNode("view", { class: "input-group" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["input-container", { focused: $data.pwdFocused, filled: $data.form.password }]) }, [ vue.createElementVNode("view", { class: "input-icon" }, [ (vue.openBlock(), vue.createElementBlock("svg", { viewBox: "0 0 24 24", class: "icon" }, [ vue.createElementVNode("path", { d: "M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z" }) ])) ]), vue.withDirectives(vue.createElementVNode( "input", { class: "input-field", "onUpdate:modelValue": _cache[12] || (_cache[12] = ($event) => $data.form.password = $event), password: "", placeholder: "请设置登录密码(至少6位)", onFocus: _cache[13] || (_cache[13] = ($event) => $data.pwdFocused = true), onBlur: _cache[14] || (_cache[14] = ($event) => $data.pwdFocused = false) }, null, 544 /* NEED_HYDRATION, NEED_PATCH */ ), [ [ vue.vModelText, $data.form.password, void 0, { trim: true } ] ]) ], 2 /* CLASS */ ) ]), vue.createCommentVNode(" 发送验证码按钮 "), vue.createElementVNode("view", { class: "input-group" }, [ vue.createElementVNode("button", { class: "login-button", disabled: $data.countdown > 0 || $data.sending, onClick: _cache[15] || (_cache[15] = (...args) => $options.sendCode && $options.sendCode(...args)) }, vue.toDisplayString($options.btnText), 9, ["disabled"]) ]) ]), vue.createCommentVNode(" 按钮区域 "), vue.createElementVNode("view", { class: "actions-section" }, [ vue.createElementVNode("button", { class: "register-button", onClick: _cache[16] || (_cache[16] = (...args) => $options.onRegister && $options.onRegister(...args)) }, [ vue.createElementVNode("text", { class: "button-text" }, "立即注册") ]), vue.createElementVNode("button", { class: "login-button", onClick: _cache[17] || (_cache[17] = (...args) => $options.onGoLogin && $options.onGoLogin(...args)) }, [ vue.createElementVNode("text", { class: "button-text" }, "已有账户?去登录") ]) ]), vue.createCommentVNode(" 提示信息 "), vue.createElementVNode("view", { class: "footer-section" }, [ vue.createElementVNode("text", { class: "hint-text" }, "注册即表示您同意我们的服务条款和隐私政策") ]) ]) ]); } const PagesAuthRegister = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["render", _sfc_render$6], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/auth/register.vue"]]); const _imports_0$2 = "/static/icons/icons8-login-50.png"; function normalizeAvatar(url) { if (!url) return "/static/icons/icons8-mitt-24.png"; const s = String(url); if (/^https?:\/\//i.test(s)) return s; if (!API_BASE_URL) return s; if (s.startsWith("/")) return `${API_BASE_URL}${s}`; return `${API_BASE_URL}/${s}`; } const _sfc_main$6 = { data() { return { avatarUrl: "/static/icons/icons8-mitt-24.png", shopName: "未登录", mobile: "", pendingJsCode: "", logging: false, vipIsVip: false, vipStart: "", vipEnd: "" }; }, onShow() { this.fetchProfile(); this.loadVip(); try { if (uni.getStorageSync("TOKEN")) { this.$forceUpdate && this.$forceUpdate(); } } catch (e) { } }, computed: { isLoggedIn() { try { return !!uni.getStorageSync("TOKEN"); } catch (e) { return false; } }, avatarDisplay() { return normalizeAvatar(this.avatarUrl); }, emailDisplay() { if (!this.isLoggedIn) return ""; const e = String(uni.getStorageSync("USER_EMAIL") || ""); if (!e) return "未绑定邮箱"; const at = e.indexOf("@"); if (at > 1) { const name = e.slice(0, at); const domain = e.slice(at); return (name.length <= 2 ? name[0] + "*" : name.slice(0, 2) + "***") + domain; } return e; }, vipStartDisplay() { return this.formatDisplay(this.vipStart); }, vipEndDisplay() { return this.formatDisplay(this.vipEnd); } }, methods: { // 登录相关方法已移除 async fetchProfile() { const hasToken = (() => { try { return !!uni.getStorageSync("TOKEN"); } catch (e) { return false; } })(); if (!hasToken) { this.shopName = "未登录"; this.avatarUrl = "/static/icons/icons8-mitt-24.png"; this.mobile = ""; return; } try { const profile = await get("/api/user/me"); const latestAvatar = (profile == null ? void 0 : profile.avatarUrl) || ""; if (latestAvatar) { const bust = `${latestAvatar}${latestAvatar.includes("?") ? "&" : "?"}t=${Date.now()}`; this.avatarUrl = bust; try { uni.setStorageSync("USER_AVATAR_RAW", latestAvatar); uni.setStorageSync("USER_AVATAR", latestAvatar); } catch (_) { } } else { const cached = uni.getStorageSync("USER_AVATAR") || ""; this.avatarUrl = cached || "/static/icons/icons8-mitt-24.png"; } const storeName = (profile == null ? void 0 : profile.name) || uni.getStorageSync("SHOP_NAME") || "未命名店铺"; this.shopName = storeName; const phone = (profile == null ? void 0 : profile.phone) || uni.getStorageSync("USER_MOBILE") || ""; this.mobile = phone; } catch (e) { try { const storeName = uni.getStorageSync("SHOP_NAME") || ""; const avatar = uni.getStorageSync("USER_AVATAR") || ""; const phone = uni.getStorageSync("USER_MOBILE") || ""; if (storeName) this.shopName = storeName; if (avatar) this.avatarUrl = avatar; this.mobile = phone; } catch (_) { } } }, async loadVip() { try { const hasToken = (() => { try { return !!uni.getStorageSync("TOKEN"); } catch (e) { return false; } })(); if (!hasToken) { this.vipIsVip = false; this.vipStart = ""; this.vipEnd = ""; return; } const data = await get("/api/vip/status"); const active = !!(data == null ? void 0 : data.isVip); this.vipIsVip = active; this.vipEnd = (data == null ? void 0 : data.expireAt) || ""; let computedStart = ""; const exp = this.vipEnd; if (exp) { const m = String(exp).match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?/); if (m) { const y = Number(m[1]); const mo = Number(m[2]) - 1; const da = Number(m[3]); const hh = Number(m[4] || "0"); const mm = Number(m[5] || "0"); const ss = Number(m[6] || "0"); const startDate = new Date(y, mo - 1, da, hh, mm, ss); const y2 = startDate.getFullYear(); const m2 = (startDate.getMonth() + 1).toString().padStart(2, "0"); const d2 = startDate.getDate().toString().padStart(2, "0"); const h2 = startDate.getHours().toString().padStart(2, "0"); const i2 = startDate.getMinutes().toString().padStart(2, "0"); computedStart = `${y2}-${m2}-${d2} ${h2}:${i2}`; } } this.vipStart = computedStart; try { uni.setStorageSync("USER_VIP_IS_VIP", String(active)); uni.setStorageSync("USER_VIP_END", this.vipEnd); if (this.vipStart) uni.setStorageSync("USER_VIP_START", this.vipStart); else uni.removeStorageSync("USER_VIP_START"); } catch (_) { } } catch (e) { try { const isVip = String(uni.getStorageSync("USER_VIP_IS_VIP") || "false").toLowerCase() === "true"; this.vipIsVip = isVip; this.vipStart = uni.getStorageSync("USER_VIP_START") || ""; this.vipEnd = uni.getStorageSync("USER_VIP_END") || ""; } catch (_) { } } }, formatDisplay(value) { if (!value) return "-"; const s = String(value); const m = s.match(/^(\d{4}-\d{2}-\d{2})/); if (m) return m[1]; const d = new Date(s); if (!isNaN(d.getTime())) { const y = d.getFullYear(); const mo = String(d.getMonth() + 1).padStart(2, "0"); const da = String(d.getDate()).padStart(2, "0"); return `${y}-${mo}-${da}`; } return s; }, startLogin() { if (this.logging) return; this.logging = true; const tryOnce = async () => ({}); uni.login({ provider: "weixin", success: async (res) => { this.pendingJsCode = res.code || ""; if (!this.pendingJsCode) { this.logging = false; return uni.showToast({ title: "获取登录code失败", icon: "none" }); } try { await tryOnce(); } catch (e) { const msg = e && e.message || ""; if (msg.includes("40163") || msg.toLowerCase().includes("been used")) { uni.login({ provider: "weixin", success: async (r2) => { const fresh = r2.code || ""; if (!fresh) { this.logging = false; return; } try { await tryOnce(); } finally { this.logging = false; } } }); return; } } finally { this.logging = false; } }, fail: () => { this.logging = false; uni.showToast({ title: "微信登录失败", icon: "none" }); } }); }, goLogin() { uni.navigateTo({ url: "/pages/auth/login" }); }, onGetPhoneNumber(e) { if (this.logging) return; this.logging = true; uni.login({ provider: "weixin", success: (res) => { const jsCode = res.code || ""; if (!jsCode) { this.logging = false; return uni.showToast({ title: "获取登录code失败", icon: "none" }); } Promise.resolve().finally(() => { this.logging = false; }); }, fail: () => { this.logging = false; uni.showToast({ title: "微信登录失败", icon: "none" }); } }); }, goSmsLogin() { uni.navigateTo({ url: "/pages/my/sms-login" }); }, onAvatarError() { this.avatarUrl = "/static/icons/icons8-mitt-24.png"; }, goVip() { uni.navigateTo({ url: "/pages/my/vip" }); }, goMyOrders() { uni.navigateTo({ url: "/pages/my/orders" }); }, editProfile() { uni.navigateTo({ url: "/pages/my/security" }); }, goAbout() { uni.navigateTo({ url: "/pages/my/about" }); }, logout() { try { uni.removeStorageSync("TOKEN"); uni.removeStorageSync("LOGINED"); uni.removeStorageSync("LOGIN_PHONE"); uni.removeStorageSync("DEFAULT_USER_ID"); uni.setStorageSync("ENABLE_DEFAULT_USER", "false"); uni.removeStorageSync("USER_AVATAR"); uni.removeStorageSync("USER_AVATAR_RAW"); uni.removeStorageSync("USER_NAME"); uni.removeStorageSync("USER_MOBILE"); uni.removeStorageSync("USER_EMAIL"); uni.removeStorageSync("SHOP_NAME"); uni.removeStorageSync("USER_VIP_IS_VIP"); uni.removeStorageSync("USER_VIP_START"); uni.removeStorageSync("USER_VIP_END"); uni.showToast({ title: "已清理本地信息", icon: "none" }); setTimeout(() => { uni.reLaunch({ url: "/pages/index/index" }); }, 300); } catch (e) { uni.reLaunch({ url: "/pages/index/index" }); } } } }; function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "me" }, [ $options.isLoggedIn ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "card user" }, [ vue.createElementVNode("image", { class: "avatar", src: $options.avatarDisplay, mode: "aspectFill", onError: _cache[0] || (_cache[0] = (...args) => $options.onAvatarError && $options.onAvatarError(...args)) }, null, 40, ["src"]), vue.createElementVNode("view", { class: "meta" }, [ vue.createElementVNode( "text", { class: "name" }, vue.toDisplayString($data.shopName), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "phone" }, vue.toDisplayString($options.emailDisplay), 1 /* TEXT */ ), vue.createElementVNode("text", { class: "role" }, "老板") ]) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "card user guest" }, [ vue.createElementVNode("image", { class: "avatar", src: _imports_0$2, mode: "aspectFill" }), vue.createElementVNode("view", { class: "meta" }, [ vue.createElementVNode("text", { class: "name" }, "未登录"), vue.createElementVNode("text", { class: "phone" }, "登录后同步数据"), vue.createElementVNode("text", { class: "role" }, "访客") ]), vue.createElementVNode("button", { class: "login-entry", onClick: _cache[1] || (_cache[1] = (...args) => $options.goLogin && $options.goLogin(...args)) }, "登录") ])), vue.createCommentVNode(" VIP 卡片(置于“会员与订单”分组上方) "), $options.isLoggedIn ? (vue.openBlock(), vue.createElementBlock( "view", { key: 2, class: vue.normalizeClass(["card vip", { active: $data.vipIsVip }]) }, [ vue.createElementVNode("view", { class: "vip-row" }, [ vue.createElementVNode( "text", { class: "vip-badge" }, vue.toDisplayString($data.vipIsVip ? "VIP" : "非VIP"), 1 /* TEXT */ ), vue.createElementVNode("text", { class: "vip-title" }, "会员状态") ]), vue.createElementVNode("view", { class: "vip-meta" }, [ vue.createElementVNode("view", { class: "item" }, [ vue.createElementVNode("text", { class: "label" }, "开始"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.vipStartDisplay), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "item" }, [ vue.createElementVNode("text", { class: "label" }, "结束"), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString($options.vipEndDisplay), 1 /* TEXT */ ) ]) ]) ], 2 /* CLASS */ )) : vue.createCommentVNode("v-if", true), vue.createElementVNode("view", { class: "group" }, [ vue.createElementVNode("view", { class: "group-title" }, "会员与订单"), vue.createElementVNode("view", { class: "cell", onClick: _cache[2] || (_cache[2] = (...args) => $options.goVip && $options.goVip(...args)) }, [ vue.createElementVNode("view", { class: "cell-left" }, [ vue.createElementVNode("text", null, "VIP会员"), $data.vipIsVip ? (vue.openBlock(), vue.createElementBlock("text", { key: 0, class: "vip-tag" }, "已开通")) : (vue.openBlock(), vue.createElementBlock("text", { key: 1, class: "vip-tag pending" }, "待开通")) ]), vue.createElementVNode("text", { class: "arrow" }, "›") ]), vue.createElementVNode("view", { class: "cell", onClick: _cache[3] || (_cache[3] = (...args) => $options.goMyOrders && $options.goMyOrders(...args)) }, [ vue.createElementVNode("text", null, "我的订单"), vue.createElementVNode("text", { class: "arrow" }, "›") ]) ]), vue.createElementVNode("view", { class: "group" }, [ vue.createElementVNode("view", { class: "group-title" }, "设置中心"), vue.createElementVNode("view", { class: "cell", onClick: _cache[4] || (_cache[4] = (...args) => $options.editProfile && $options.editProfile(...args)) }, [ vue.createElementVNode("text", null, "账号与安全"), vue.createElementVNode("text", { class: "desc" }, "修改头像、姓名、密码、电话"), vue.createElementVNode("text", { class: "arrow" }, "›") ]), vue.createElementVNode("view", { class: "cell", onClick: _cache[5] || (_cache[5] = (...args) => $options.goAbout && $options.goAbout(...args)) }, [ vue.createElementVNode("text", null, "关于与协议"), vue.createElementVNode("text", { class: "arrow" }, "›") ]), $options.isLoggedIn ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "cell danger", onClick: _cache[6] || (_cache[6] = (...args) => $options.logout && $options.logout(...args)) }, [ vue.createElementVNode("text", null, "退出登录") ])) : vue.createCommentVNode("v-if", true) ]) ]); } const PagesMyIndex = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["render", _sfc_render$5], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/my/index.vue"]]); const _imports_0$1 = "/static/logo.png"; const _sfc_main$5 = { methods: { openPolicy() { uni.showModal({ title: "隐私协议", content: "隐私协议(静态占位)", showCancel: false }); }, openTerms() { uni.showModal({ title: "用户协议", content: "用户协议(静态占位)", showCancel: false }); }, openComplaint() { uni.showToast({ title: "暂未开通", icon: "none" }); } } }; function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "about" }, [ vue.createElementVNode("view", { class: "hero" }, [ vue.createElementVNode("image", { class: "logo", src: _imports_0$1, mode: "aspectFit" }), vue.createElementVNode("text", { class: "title" }, "五金配件管家"), vue.createElementVNode("text", { class: "subtitle" }, "专注小微门店的极简进销存") ]), vue.createElementVNode("view", { class: "card" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "版本"), vue.createElementVNode("text", { class: "value" }, "1.0.0") ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "隐私协议"), vue.createElementVNode("text", { class: "link", onClick: _cache[0] || (_cache[0] = (...args) => $options.openPolicy && $options.openPolicy(...args)) }, "查看") ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "用户协议"), vue.createElementVNode("text", { class: "link", onClick: _cache[1] || (_cache[1] = (...args) => $options.openTerms && $options.openTerms(...args)) }, "查看") ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "个人信息安全投诉"), vue.createElementVNode("text", { class: "link", onClick: _cache[2] || (_cache[2] = (...args) => $options.openComplaint && $options.openComplaint(...args)) }, "提交") ]) ]) ]); } const PagesMyAbout = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["render", _sfc_render$4], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/my/about.vue"]]); const _sfc_main$4 = { data() { return { form: { name: "", avatarUrl: "" }, pwd: { oldPassword: "", newPassword: "" }, phone: { phone: "" }, savingProfile: false, savingPwd: false, savingPhone: false, sendingCode: false, originalAvatarUrl: "" }; }, onShow() { this.loadProfile(); }, computed: { avatarPreview() { return this.normalizeAvatar(this.form.avatarUrl); }, canSendPhone() { const p = String(this.phone.phone || "").trim(); return /^1\d{10}$/.test(p); } }, methods: { async loadProfile() { try { const data = await get("/api/user/me"); const rawAvatar = (data == null ? void 0 : data.avatarUrl) || (uni.getStorageSync("USER_AVATAR_RAW") || ""); this.originalAvatarUrl = rawAvatar; this.form.name = (data == null ? void 0 : data.name) || (uni.getStorageSync("USER_NAME") || ""); this.form.avatarUrl = rawAvatar; } catch (e) { } }, normalizeAvatar(url) { if (!url) return "/static/icons/icons8-mitt-24.png"; const s = String(url); if (/^https?:\/\//i.test(s)) return s; const base = API_BASE_URL || ""; if (!base) return s; if (s.startsWith("/")) return `${base}${s}`; return `${base}/${s}`; }, openAvatarDialog() { uni.showActionSheet({ itemList: ["粘贴图片URL", "从相册选择并上传"], success: (res) => { if (res.tapIndex === 0) { uni.showModal({ title: "头像URL", editable: true, placeholderText: "https://...", success: async (m) => { if (m.confirm && m.content) { this.form.avatarUrl = m.content.trim(); await this.saveProfile({ auto: true }); } } }); } else if (res.tapIndex === 1) { uni.chooseImage({ count: 1, sizeType: ["compressed"], success: (ci) => { const filePath = ci.tempFilePaths && ci.tempFilePaths[0] || ""; if (!filePath) return; uni.showLoading({ title: "上传中..." }); upload("/api/attachments", filePath, { ownerType: "user_avatar", ownerId: 0 }).then(async (data) => { const url = data && (data.url || data.path); if (url) { this.form.avatarUrl = url; await this.saveProfile({ auto: true }); } uni.showToast({ title: "已上传", icon: "success" }); }).catch((e) => { uni.showToast({ title: e && e.message || "上传失败", icon: "none" }); }).finally(() => { uni.hideLoading(); }); } }); } } }); }, async saveProfile(opts = {}) { const auto = opts && opts.auto; const payload = {}; if (this.form.name && this.form.name !== uni.getStorageSync("USER_NAME")) payload.name = this.form.name; if (this.form.avatarUrl && this.form.avatarUrl !== this.originalAvatarUrl) payload.avatarUrl = this.form.avatarUrl; if (Object.keys(payload).length === 0) { if (!auto) uni.showToast({ title: "无需修改", icon: "none" }); return; } if (this.savingProfile) return; this.savingProfile = true; try { await put("/api/user/me", payload); try { if (payload.name) uni.setStorageSync("USER_NAME", payload.name); if (payload.avatarUrl) { const rawUrl = payload.avatarUrl; const displayUrl = `${rawUrl}${rawUrl.includes("?") ? "&" : "?"}t=${Date.now()}`; uni.setStorageSync("USER_AVATAR_RAW", rawUrl); uni.setStorageSync("USER_AVATAR", rawUrl); this.originalAvatarUrl = rawUrl; this.form.avatarUrl = rawUrl; } } catch (_) { } if (!payload.avatarUrl && this.form.avatarUrl) { uni.setStorageSync("USER_AVATAR_RAW", this.form.avatarUrl); uni.setStorageSync("USER_AVATAR", this.form.avatarUrl); } uni.showToast({ title: auto ? "头像已更新" : "已保存", icon: "success" }); } catch (e) { const msg = e && e.message || "保存失败"; uni.showToast({ title: msg, icon: "none" }); } finally { this.savingProfile = false; } }, async changePassword() { if (!this.pwd.newPassword || this.pwd.newPassword.length < 6) return uni.showToast({ title: "新密码至少6位", icon: "none" }); this.savingPwd = true; try { await put("/api/user/me/password", { oldPassword: this.pwd.oldPassword || void 0, newPassword: this.pwd.newPassword }); this.pwd.oldPassword = ""; this.pwd.newPassword = ""; uni.showToast({ title: "密码已修改", icon: "success" }); } catch (e) { uni.showToast({ title: e && e.message || "修改失败", icon: "none" }); } finally { this.savingPwd = false; } }, async changePhoneDirect() { if (!this.canSendPhone) return uni.showToast({ title: "请输入正确手机号", icon: "none" }); this.savingPhone = true; try { await put("/api/user/me/phone", { phone: this.phone.phone }); uni.setStorageSync("USER_MOBILE", this.phone.phone); uni.showToast({ title: "手机号已保存", icon: "success" }); } catch (e) { uni.showToast({ title: e && e.message || "保存失败", icon: "none" }); } finally { this.savingPhone = false; } } } }; function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "security" }, [ vue.createElementVNode("view", { class: "card" }, [ vue.createElementVNode("view", { class: "cell", onClick: _cache[0] || (_cache[0] = (...args) => $options.openAvatarDialog && $options.openAvatarDialog(...args)) }, [ vue.createElementVNode("text", { class: "cell-label" }, "头像"), vue.createElementVNode("image", { class: "avatar-preview", src: $options.avatarPreview, mode: "aspectFill" }, null, 8, ["src"]), vue.createElementVNode("text", { class: "arrow" }, "›") ]), vue.createElementVNode("view", { class: "cell" }, [ vue.createElementVNode("text", { class: "cell-label" }, "姓名"), vue.withDirectives(vue.createElementVNode( "input", { class: "cell-input", type: "text", "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.form.name = $event), placeholder: "请输入姓名" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.form.name, void 0, { trim: true } ] ]) ]), vue.createElementVNode("button", { class: "btn", type: "primary", loading: $data.savingProfile, onClick: _cache[2] || (_cache[2] = (...args) => $options.saveProfile && $options.saveProfile(...args)) }, "保存资料", 8, ["loading"]) ]), vue.createElementVNode("view", { class: "card" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "旧密码"), vue.withDirectives(vue.createElementVNode( "input", { class: "input", password: "", "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.pwd.oldPassword = $event), placeholder: "如从未设置可留空" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.pwd.oldPassword, void 0, { trim: true } ] ]) ]), vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "新密码"), vue.withDirectives(vue.createElementVNode( "input", { class: "input", password: "", "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => $data.pwd.newPassword = $event), placeholder: "至少6位" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.pwd.newPassword, void 0, { trim: true } ] ]) ]), vue.createElementVNode("button", { class: "btn", loading: $data.savingPwd, onClick: _cache[5] || (_cache[5] = (...args) => $options.changePassword && $options.changePassword(...args)) }, "修改密码", 8, ["loading"]) ]), vue.createElementVNode("view", { class: "card" }, [ vue.createElementVNode("view", { class: "row" }, [ vue.createElementVNode("text", { class: "label" }, "手机号"), vue.withDirectives(vue.createElementVNode( "input", { class: "input", type: "text", "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => $data.phone.phone = $event), placeholder: "11位手机号" }, null, 512 /* NEED_PATCH */ ), [ [ vue.vModelText, $data.phone.phone, void 0, { trim: true } ] ]) ]), vue.createElementVNode("button", { class: "btn", loading: $data.savingPhone, onClick: _cache[7] || (_cache[7] = (...args) => $options.changePhoneDirect && $options.changePhoneDirect(...args)) }, "保存手机号", 8, ["loading"]) ]) ]); } const PagesMySecurity = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$3], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/my/security.vue"]]); const _imports_0 = "/static/icons/icons8-vip-48 (1).png"; const _sfc_main$3 = { data() { return { isVip: false, expire: "", price: 0, benefits: [], normalAdmin: { isNormalAdmin: false, applicationStatus: "none" } }; }, onShow() { this.loadVip(); this.loadNormalAdminStatus(); this.composeBenefits(); }, computed: { expireDisplay() { const v = this.expire; if (v === null || v === void 0) return ""; if (typeof v === "number") { const d = new Date(v); if (!isNaN(d.getTime())) { const y = d.getFullYear(); const m2 = String(d.getMonth() + 1).padStart(2, "0"); const dd = String(d.getDate()).padStart(2, "0"); return `${y}-${m2}-${dd}`; } return ""; } const s = String(v); const m = s.match(/^(\d{4}-\d{2}-\d{2})/); if (m) return m[1]; const idx = s.search(/[ T]/); if (idx > 0) { const head = s.slice(0, idx); if (head) return head; } const d2 = new Date(s); if (!isNaN(d2.getTime())) { const y = d2.getFullYear(); const m2 = String(d2.getMonth() + 1).padStart(2, "0"); const dd2 = String(d2.getDate()).padStart(2, "0"); return `${y}-${m2}-${dd2}`; } return s; }, priceDisplay() { const n = Number(this.price); return Number.isFinite(n) && n > 0 ? n.toFixed(2) : "0.00"; }, applyDisabled() { var _a, _b; const s = String(((_a = this.normalAdmin) == null ? void 0 : _a.applicationStatus) || "none"); return !!(((_b = this.normalAdmin) == null ? void 0 : _b.isNormalAdmin) || s === "approved" || s === "pending"); }, applyBtnText() { var _a, _b, _c; if (((_a = this.normalAdmin) == null ? void 0 : _a.isNormalAdmin) || ((_b = this.normalAdmin) == null ? void 0 : _b.applicationStatus) === "approved") return "已通过"; if (((_c = this.normalAdmin) == null ? void 0 : _c.applicationStatus) === "pending") return "审核中"; if (!this.isVip) return "仅限VIP"; return "提交申请"; } }, methods: { composeBenefits() { this.benefits = [ { key: "history", title: "完整历史留存", desc: "无限期保留交易、库存与客户数据", icon: "/static/icons/icons8-graph-report-50.png" }, { key: "analysis", title: "高级统计面板", desc: "秒级汇总销售毛利,掌握生意节奏", icon: "/static/icons/icons8-profit-50.png" }, { key: "priority", title: "优先客服支持", desc: "遇到问题优先处理,响应更迅速", icon: "/static/icons/icons8-account-male-100.png" } ]; }, async loadVip() { try { const data = await get("/api/vip/status"); this.isVip = !!(data == null ? void 0 : data.isVip); this.expire = (data == null ? void 0 : data.expireAt) || ""; if (typeof (data == null ? void 0 : data.price) === "number") this.price = data.price; } catch (e) { this.isVip = false; } }, async loadNormalAdminStatus() { try { const data = await get("/api/normal-admin/application/status"); this.normalAdmin = { isNormalAdmin: !!(data == null ? void 0 : data.isNormalAdmin), applicationStatus: String((data == null ? void 0 : data.applicationStatus) || "none") }; } catch (e) { this.normalAdmin = { isNormalAdmin: false, applicationStatus: "none" }; } }, async onPay() { try { await post("/api/vip/pay", {}); uni.showToast({ title: "已开通VIP", icon: "success" }); await this.loadVip(); } catch (e) { uni.showToast({ title: String(e.message || "开通失败"), icon: "none" }); } }, async onApplyNormalAdmin() { var _a, _b, _c; if (this.applyDisabled) { const msg = ((_a = this.normalAdmin) == null ? void 0 : _a.isNormalAdmin) || ((_b = this.normalAdmin) == null ? void 0 : _b.applicationStatus) === "approved" ? "已通过,无需重复申请" : ((_c = this.normalAdmin) == null ? void 0 : _c.applicationStatus) === "pending" ? "审核中,请耐心等待" : "不可申请"; return uni.showToast({ title: msg, icon: "none" }); } try { await post("/api/normal-admin/apply", { remark: "从我的-会员发起申请" }); uni.showToast({ title: "申请已提交", icon: "success" }); await this.loadNormalAdminStatus(); } catch (e) { uni.showToast({ title: String(e.message || "申请失败"), icon: "none" }); } } } }; function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "vip-page" }, [ vue.createElementVNode("view", { class: "vip-hero" }, [ vue.createElementVNode("image", { class: "hero-icon", src: _imports_0, mode: "aspectFit" }), vue.createElementVNode("view", { class: "hero-text" }, [ vue.createElementVNode( "text", { class: "hero-title" }, vue.toDisplayString($data.isVip ? "VIP会员" : "升级 VIP 会员"), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "hero-subtitle" }, vue.toDisplayString($data.isVip ? "尊享完整数据与高效体验" : "开通后可查看全部历史数据并解锁高级功能"), 1 /* TEXT */ ) ]), vue.createElementVNode( "view", { class: vue.normalizeClass(["status-pill", { active: $data.isVip }]) }, [ vue.createElementVNode( "text", null, vue.toDisplayString($data.isVip ? "已开通" : "普通用户"), 1 /* TEXT */ ) ], 2 /* CLASS */ ) ]), $data.isVip ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "vip-summary" }, [ vue.createElementVNode("view", { class: "summary-item" }, [ vue.createElementVNode("text", { class: "summary-label" }, "会员状态"), vue.createElementVNode("text", { class: "summary-value success" }, "已激活") ]), vue.createElementVNode("view", { class: "summary-item" }, [ vue.createElementVNode("text", { class: "summary-label" }, "有效期至"), vue.createElementVNode( "text", { class: "summary-value" }, vue.toDisplayString($options.expireDisplay), 1 /* TEXT */ ) ]) ])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "vip-summary" }, [ vue.createElementVNode("view", { class: "summary-item" }, [ vue.createElementVNode("text", { class: "summary-label" }, "当前身份"), vue.createElementVNode("text", { class: "summary-value" }, "普通用户") ]), vue.createElementVNode("view", { class: "summary-item" }, [ vue.createElementVNode("text", { class: "summary-label" }, "会员价格"), vue.createElementVNode( "text", { class: "summary-value highlight" }, "¥" + vue.toDisplayString($options.priceDisplay) + "/月", 1 /* TEXT */ ) ]) ])), vue.createElementVNode("view", { class: "benefit-section" }, [ vue.createElementVNode("view", { class: "section-header" }, [ vue.createElementVNode("text", { class: "section-title" }, "会员特权"), vue.createElementVNode("text", { class: "section-subtitle" }, "聚焦数据留存与专业形象,让经营更有底气") ]), vue.createElementVNode("view", { class: "benefit-grid" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.benefits, (item) => { return vue.openBlock(), vue.createElementBlock("view", { key: item.key, class: "benefit-card" }, [ item.icon ? (vue.openBlock(), vue.createElementBlock("image", { key: 0, src: item.icon, class: "benefit-icon", mode: "aspectFit" }, null, 8, ["src"])) : vue.createCommentVNode("v-if", true), vue.createElementVNode( "text", { class: "benefit-title" }, vue.toDisplayString(item.title), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "benefit-desc" }, vue.toDisplayString(item.desc), 1 /* TEXT */ ) ]); }), 128 /* KEYED_FRAGMENT */ )) ]) ]), vue.createCommentVNode(" 已是VIP:展示申请普通管理员入口 "), $data.isVip ? (vue.openBlock(), vue.createElementBlock("view", { key: 2, class: "apply-card" }, [ vue.createElementVNode("view", { class: "apply-text" }, [ vue.createElementVNode("text", { class: "apply-title" }, "申请成为普通管理员"), vue.createElementVNode("text", { class: "apply-desc" }, "在普通管理端参与配件审核") ]), vue.createElementVNode( "view", { role: "button", class: vue.normalizeClass(["apply-btn", { disabled: $options.applyDisabled }]), onClick: _cache[0] || (_cache[0] = (...args) => $options.onApplyNormalAdmin && $options.onApplyNormalAdmin(...args)) }, [ vue.createElementVNode( "text", null, vue.toDisplayString($options.applyBtnText), 1 /* TEXT */ ) ], 2 /* CLASS */ ) ])) : vue.createCommentVNode("v-if", true), !$data.isVip ? (vue.openBlock(), vue.createElementBlock("view", { key: 3, class: "purchase-card" }, [ vue.createElementVNode("view", { class: "purchase-text" }, [ vue.createElementVNode("text", { class: "purchase-title" }, "立即升级 VIP"), vue.createElementVNode("text", { class: "purchase-desc" }, "不限历史数据、专属标识,助您高效管账") ]), vue.createElementVNode("button", { class: "purchase-btn", onClick: _cache[1] || (_cache[1] = (...args) => $options.onPay && $options.onPay(...args)) }, [ vue.createElementVNode("text", null, "立即开通") ]) ])) : vue.createCommentVNode("v-if", true) ]); } const PagesMyVip = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$2], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/my/vip.vue"]]); const _sfc_main$2 = { data() { return { list: [], page: 1, size: 20, loading: false }; }, onShow() { this.fetch(true); }, computed: { isLoggedIn() { try { return !!uni.getStorageSync("TOKEN"); } catch (e) { return false; } } }, methods: { async fetch(reset = false) { if (!this.isLoggedIn) return; if (this.loading) return; this.loading = true; try { const p = reset ? 1 : this.page; const data = await get("/api/vip/recharges", { page: p, size: this.size }); const arr = Array.isArray(data == null ? void 0 : data.list) ? data.list : []; this.list = reset ? arr : (this.list || []).concat(arr); this.page = p + 1; } finally { this.loading = false; } }, fmt(v) { if (!v) return ""; const s = String(v); const m = s.match(/^(\d{4}-\d{2}-\d{2})([ T](\d{2}:\d{2}))/); return m ? `${m[1]} ${m[3]}` : s; }, toMoney(v) { try { return Number(v).toFixed(2); } catch (_) { return v; } } } }; function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "orders" }, [ !$options.isLoggedIn ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "hint" }, "请先登录后查看VIP支付记录")) : (vue.openBlock(), vue.createElementBlock("view", { key: 1 }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.list, (it) => { return vue.openBlock(), vue.createElementBlock("view", { class: "item", key: it.id }, [ vue.createElementVNode("view", { class: "row1" }, [ vue.createElementVNode( "text", { class: "price" }, "¥ " + vue.toDisplayString($options.toMoney(it.price)), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "channel" }, vue.toDisplayString(it.channel || "支付"), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "row2" }, [ vue.createElementVNode( "text", { class: "date" }, vue.toDisplayString($options.fmt(it.createdAt)), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "duration" }, vue.toDisplayString(it.durationDays) + " 天", 1 /* TEXT */ ) ]), it.expireTo ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "row3" }, [ vue.createElementVNode( "text", { class: "expire" }, "有效期至 " + vue.toDisplayString($options.fmt(it.expireTo)), 1 /* TEXT */ ) ])) : vue.createCommentVNode("v-if", true) ]); }), 128 /* KEYED_FRAGMENT */ )), $data.list.length === 0 ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "empty" }, "暂无支付记录")) : vue.createCommentVNode("v-if", true) ])) ]); } const PagesMyOrders = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/my/orders.vue"]]); function formatDate(d) { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); return `${y}-${m}-${day}`; } const _sfc_main$1 = { data() { const now = /* @__PURE__ */ new Date(); const start = new Date(now.getFullYear(), now.getMonth(), 1); return { startDate: formatDate(start), endDate: formatDate(now), dim: "customer", rows: [], summary: { salesAmount: 0, costAmount: 0, profit: 0, profitRate: 0, itemCount: 0 }, loading: false, error: "" }; }, onLoad(query) { try { const d = query && query.dim; if (d === "product" || d === "customer") this.dim = d; } catch (e) { } this.refresh(); }, computed: { profitRateText() { var _a; const rate = Number(((_a = this.summary) == null ? void 0 : _a.profitRate) || 0); return rate.toFixed(2) + "%"; }, summaryItems() { if (!this.rows.length) return []; return [ { label: "销售额", value: `¥ ${this.fmt(this.summary.salesAmount)}` }, { label: "成本", value: `¥ ${this.fmt(this.summary.costAmount)}` }, { label: "利润", value: `¥ ${this.fmt(this.summary.profit)}` }, { label: "利润率", value: this.profitRateText } ]; } }, methods: { fmt(n) { return Number(n || 0).toFixed(2); }, showProductSpec(row) { return this.dim === "product" && row && row.spec; }, rowMetrics(row) { if (!row) return []; return [ { label: "销售额", value: `¥ ${this.fmt(row.salesAmount)}` }, { label: "成本", value: `¥ ${this.fmt(row.costAmount)}` }, { label: "利润", value: `¥ ${this.fmt(row.profit)}` }, { label: "利润率", value: `${Number(row.profitRate || 0).toFixed(2)}%` } ]; }, setDimension(d) { if (d !== "customer" && d !== "product") return; if (this.dim === d) return; this.dim = d; this.refresh(); }, onStartChange(e) { this.startDate = e.detail.value; this.refresh(); }, onEndChange(e) { this.endDate = e.detail.value; this.refresh(); }, async refresh() { var _a, _b, _c, _d, _e; this.loading = true; this.error = ""; try { const resp = await get("/api/report/sales", { dimension: this.dim, startDate: this.startDate, endDate: this.endDate }); const items = Array.isArray(resp == null ? void 0 : resp.items) ? resp.items : []; this.rows = items.map((it) => ({ name: (it == null ? void 0 : it.name) || (this.dim === "product" ? "未命名商品" : "未指定客户"), spec: (it == null ? void 0 : it.spec) || "", salesAmount: Number((it == null ? void 0 : it.salesAmount) || 0), costAmount: Number((it == null ? void 0 : it.costAmount) || 0), profit: Number((it == null ? void 0 : it.profit) || 0), profitRate: Number((it == null ? void 0 : it.profitRate) || 0) })); this.summary = { salesAmount: Number(((_a = resp == null ? void 0 : resp.summary) == null ? void 0 : _a.salesAmount) || 0), costAmount: Number(((_b = resp == null ? void 0 : resp.summary) == null ? void 0 : _b.costAmount) || 0), profit: Number(((_c = resp == null ? void 0 : resp.summary) == null ? void 0 : _c.profit) || 0), profitRate: Number(((_d = resp == null ? void 0 : resp.summary) == null ? void 0 : _d.profitRate) || 0), itemCount: Number(((_e = resp == null ? void 0 : resp.summary) == null ? void 0 : _e.itemCount) || this.rows.length) }; } catch (e) { this.error = e && e.message || "报表加载失败"; } finally { this.loading = false; } } } }; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "report" }, [ vue.createElementVNode("view", { class: "header" }, "销售报表"), vue.createElementVNode("view", { class: "toolbar" }, [ vue.createElementVNode("picker", { mode: "date", value: $data.startDate, onChange: _cache[0] || (_cache[0] = (...args) => $options.onStartChange && $options.onStartChange(...args)) }, [ vue.createElementVNode( "view", { class: "date" }, vue.toDisplayString($data.startDate), 1 /* TEXT */ ) ], 40, ["value"]), vue.createElementVNode("text", { style: { "margin": "0 8rpx" } }, "—"), vue.createElementVNode("picker", { mode: "date", value: $data.endDate, onChange: _cache[1] || (_cache[1] = (...args) => $options.onEndChange && $options.onEndChange(...args)) }, [ vue.createElementVNode( "view", { class: "date" }, vue.toDisplayString($data.endDate), 1 /* TEXT */ ) ], 40, ["value"]) ]), vue.createElementVNode("view", { class: "tabs" }, [ vue.createElementVNode( "view", { class: vue.normalizeClass(["tab", { active: $data.dim === "customer" }]), onClick: _cache[2] || (_cache[2] = ($event) => $options.setDimension("customer")) }, "按客户", 2 /* CLASS */ ), vue.createElementVNode( "view", { class: vue.normalizeClass(["tab", { active: $data.dim === "product" }]), onClick: _cache[3] || (_cache[3] = ($event) => $options.setDimension("product")) }, "按货品", 2 /* CLASS */ ) ]), $options.summaryItems.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "summary" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($options.summaryItems, (item, ix) => { return vue.openBlock(), vue.createElementBlock("view", { class: "summary-item", key: ix }, [ vue.createElementVNode( "text", { class: "label" }, vue.toDisplayString(item.label), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "value" }, vue.toDisplayString(item.value), 1 /* TEXT */ ) ]); }), 128 /* KEYED_FRAGMENT */ )) ])) : vue.createCommentVNode("v-if", true), $data.loading ? (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "loading" }, "加载中...")) : $data.error ? (vue.openBlock(), vue.createElementBlock( "view", { key: 2, class: "empty" }, vue.toDisplayString($data.error), 1 /* TEXT */ )) : !$data.rows.length ? (vue.openBlock(), vue.createElementBlock("view", { key: 3, class: "empty" }, "暂无统计数据")) : (vue.openBlock(), vue.createElementBlock("view", { key: 4 }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.rows, (row, idx) => { return vue.openBlock(), vue.createElementBlock("view", { key: idx, class: "card" }, [ vue.createElementVNode("view", { class: "row-head" }, [ vue.createElementVNode("view", { class: "row-title" }, [ vue.createElementVNode( "view", { class: "title" }, vue.toDisplayString(row.name), 1 /* TEXT */ ), $options.showProductSpec(row) ? (vue.openBlock(), vue.createElementBlock( "view", { key: 0, class: "subtitle" }, vue.toDisplayString(row.spec), 1 /* TEXT */ )) : vue.createCommentVNode("v-if", true) ]) ]), vue.createElementVNode("view", { class: "row-body" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($options.rowMetrics(row), (metric, mIdx) => { return vue.openBlock(), vue.createElementBlock("view", { class: "metric", key: mIdx }, [ vue.createElementVNode( "text", { class: "metric-label" }, vue.toDisplayString(metric.label), 1 /* TEXT */ ), vue.createElementVNode( "text", { class: "metric-value" }, vue.toDisplayString(metric.value), 1 /* TEXT */ ) ]); }), 128 /* KEYED_FRAGMENT */ )) ]) ]); }), 128 /* KEYED_FRAGMENT */ )) ])) ]); } const PagesReportIndex = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/report/index.vue"]]); __definePage("pages/index/index", PagesIndexIndex); __definePage("pages/order/create", PagesOrderCreate); __definePage("pages/product/select", PagesProductSelect); __definePage("pages/product/list", PagesProductList); __definePage("pages/product/submit", PagesProductSubmit); __definePage("pages/product/submissions", PagesProductSubmissions); __definePage("pages/product/submission-detail", PagesProductSubmissionDetail); __definePage("pages/product/form", PagesProductForm); __definePage("pages/product/product-detail", PagesProductProductDetail); __definePage("pages/product/categories", PagesProductCategories); __definePage("pages/product/units", PagesProductUnits); __definePage("pages/product/settings", PagesProductSettings); __definePage("pages/customer/select", PagesCustomerSelect); __definePage("pages/customer/form", PagesCustomerForm); __definePage("pages/customer/detail", PagesCustomerDetail); __definePage("pages/supplier/select", PagesSupplierSelect); __definePage("pages/supplier/form", PagesSupplierForm); __definePage("pages/account/select", PagesAccountSelect); __definePage("pages/account/ledger", PagesAccountLedger); __definePage("pages/account/form", PagesAccountForm); __definePage("pages/detail/index", PagesDetailIndex); __definePage("pages/auth/login", PagesAuthLogin); __definePage("pages/auth/register", PagesAuthRegister); __definePage("pages/my/index", PagesMyIndex); __definePage("pages/my/about", PagesMyAbout); __definePage("pages/my/security", PagesMySecurity); __definePage("pages/my/vip", PagesMyVip); __definePage("pages/my/orders", PagesMyOrders); __definePage("pages/report/index", PagesReportIndex); const _sfc_main = { onLaunch: function() { formatAppLog("log", "at App.vue:4", "App Launch"); }, onShow: function() { formatAppLog("log", "at App.vue:7", "App Show"); }, onHide: function() { formatAppLog("log", "at App.vue:10", "App Hide"); } }; const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/App.vue"]]); function createApp() { const app = vue.createVueApp(App); return { app }; } const { app: __app__, Vuex: __Vuex__, Pinia: __Pinia__ } = createApp(); uni.Vuex = __Vuex__; uni.Pinia = __Pinia__; __app__.provide("__globalStyles", __uniConfig.styles); __app__._component.mpType = "app"; __app__._component.render = () => { }; __app__.mount("#app"); })(Vue);