Files
PartsInquiry/frontend/unpackage/dist/dev/app-plus/app-service.js
2025-10-08 19:15:20 +08:00

8699 lines
318 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 _imports_0$5 = "/static/icons/product.png";
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:265", "[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
});
},
onPartsSearchTap() {
try {
uni.setStorageSync("PRODUCT_SEARCH_CONFIG", JSON.stringify({
openTab: "search",
mode: "template"
}));
} catch (e) {
formatAppLog("error", "at pages/index/index.vue:285", "[index] 设置存储标志失败:", e);
}
uni.switchTab({ url: "/pages/product/list" });
},
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: "parts-search-btn",
onClick: _cache[6] || (_cache[6] = (...args) => $options.onPartsSearchTap && $options.onPartsSearchTap(...args)),
"hover-class": "parts-search-btn-active",
"hover-stay-time": "80"
}, [
vue.createElementVNode("image", {
src: _imports_0$5,
class: "parts-search-icon",
mode: "aspectFit"
}),
vue.createElementVNode("text", { class: "parts-search-text" }, "配件查询")
]),
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;
try {
const configStr = uni.getStorageSync("PRODUCT_SEARCH_CONFIG");
if (configStr) {
const config = JSON.parse(configStr);
if (config.openTab) {
this.tab = config.openTab;
}
if (config.mode) {
this.query.mode = config.mode;
}
uni.removeStorageSync("PRODUCT_SEARCH_CONFIG");
}
} catch (e) {
formatAppLog("error", "at pages/product/list.vue:121", "[list] 处理查询配置失败:", e);
}
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" });
}
},
goSubmit() {
uni.navigateTo({ url: "/pages/product/submit" });
}
}
};
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(' 右下角的"+"按钮 '),
vue.createElementVNode("view", {
class: "fab",
onClick: _cache[10] || (_cache[10] = (...args) => $options.goSubmit && $options.goSubmit(...args))
}, "")
]);
}
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: "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;
const email = (profile == null ? void 0 : profile.email) || "";
try {
if (email) {
uni.setStorageSync("USER_EMAIL", email);
} else {
uni.removeStorageSync("USER_EMAIL");
}
} catch (_) {
}
} 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);