9.18王德鹏/1
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"java.compile.nullAnalysis.mode": "automatic"
|
||||
}
|
||||
@@ -71,3 +71,6 @@ public class AttachmentController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -30,3 +30,6 @@ public class AttachmentPlaceholderProperties {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,3 +19,6 @@ public class AppDefaultsProperties {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.example.demo.order;
|
||||
|
||||
import com.example.demo.common.AppDefaultsProperties;
|
||||
import com.example.demo.order.dto.OrderDtos;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class OrderController {
|
||||
|
||||
private final OrderService orderService;
|
||||
private final AppDefaultsProperties defaults;
|
||||
|
||||
public OrderController(OrderService orderService, AppDefaultsProperties defaults) {
|
||||
this.orderService = orderService;
|
||||
this.defaults = defaults;
|
||||
}
|
||||
|
||||
@PostMapping("/orders")
|
||||
public ResponseEntity<?> createOrder(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||
@RequestBody OrderDtos.CreateOrderRequest req) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||
return ResponseEntity.ok(orderService.create(sid, uid, req));
|
||||
}
|
||||
|
||||
@PostMapping("/payments/{biz}")
|
||||
public ResponseEntity<?> createPayments(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||
@PathVariable("biz") String biz,
|
||||
@RequestBody java.util.List<OrderDtos.PaymentItem> req) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||
return ResponseEntity.ok(orderService.createPayments(sid, uid, req, biz));
|
||||
}
|
||||
|
||||
@PostMapping("/orders/{id}/void")
|
||||
public ResponseEntity<?> voidOrder(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||
@PathVariable("id") Long id,
|
||||
@RequestParam("type") String type) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||
orderService.voidOrder(sid, uid, id, type);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@GetMapping("/orders")
|
||||
public ResponseEntity<?> list(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "biz", required = false) String biz,
|
||||
@RequestParam(name = "type", required = false) String type,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
return ResponseEntity.ok(orderService.list(sid, biz, type, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
|
||||
// 兼容前端直接调用 /api/purchase-orders
|
||||
@GetMapping("/purchase-orders")
|
||||
public ResponseEntity<?> purchaseOrders(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "status", required = false) String status,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
String type = ("returned".equalsIgnoreCase(status) ? "purchase.return" : "purchase.in");
|
||||
return ResponseEntity.ok(orderService.list(sid, "purchase", type, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
|
||||
@GetMapping("/payments")
|
||||
public ResponseEntity<?> listPayments(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "direction", required = false) String direction,
|
||||
@RequestParam(name = "bizType", required = false) String bizType,
|
||||
@RequestParam(name = "accountId", required = false) Long accountId,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
return ResponseEntity.ok(orderService.listPayments(sid, direction, bizType, accountId, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
|
||||
@GetMapping("/other-transactions")
|
||||
public ResponseEntity<?> listOtherTransactions(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "type", required = false) String type,
|
||||
@RequestParam(name = "accountId", required = false) Long accountId,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
return ResponseEntity.ok(orderService.listOtherTransactions(sid, type, accountId, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
|
||||
@GetMapping("/inventories/logs")
|
||||
public ResponseEntity<?> listInventoryLogs(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "productId", required = false) Long productId,
|
||||
@RequestParam(name = "reason", required = false) String reason,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
return ResponseEntity.ok(orderService.listInventoryLogs(sid, productId, reason, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.example.demo.order;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 简单的进程内单号生成器:前缀 + yyyyMMdd + 4位流水
|
||||
* 说明:演示用,生产建议使用数据库序列或 Redis 自增确保多实例唯一。
|
||||
*/
|
||||
public class OrderNumberGenerator {
|
||||
private static final ConcurrentHashMap<String, AtomicInteger> dateCounters = new ConcurrentHashMap<>();
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
|
||||
public static String next(String prefix) {
|
||||
String day = LocalDateTime.now().format(DATE);
|
||||
String key = prefix + day;
|
||||
int seq = dateCounters.computeIfAbsent(key, k -> new AtomicInteger(0)).incrementAndGet();
|
||||
return prefix + day + String.format("%04d", seq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
353
backend/src/main/java/com/example/demo/order/OrderService.java
Normal file
353
backend/src/main/java/com/example/demo/order/OrderService.java
Normal file
@@ -0,0 +1,353 @@
|
||||
package com.example.demo.order;
|
||||
|
||||
import com.example.demo.order.dto.OrderDtos;
|
||||
import com.example.demo.product.entity.Inventory;
|
||||
import com.example.demo.product.repo.InventoryRepository;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||
import org.springframework.jdbc.support.KeyHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class OrderService {
|
||||
|
||||
private final InventoryRepository inventoryRepository;
|
||||
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public OrderService(InventoryRepository inventoryRepository,
|
||||
JdbcTemplate jdbcTemplate) {
|
||||
this.inventoryRepository = inventoryRepository;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Object create(Long shopId, Long userId, OrderDtos.CreateOrderRequest req) {
|
||||
String type = req.type == null ? "" : req.type;
|
||||
boolean isSaleOut = "sale.out".equals(type) || "out".equals(type) || "sale".equals(type);
|
||||
boolean isPurchaseIn = "purchase.in".equals(type) || "in".equals(type);
|
||||
boolean isSaleReturn = "sale.return".equals(type);
|
||||
boolean isPurchaseReturn = "purchase.return".equals(type);
|
||||
boolean isSaleCollect = "sale.collect".equals(type);
|
||||
boolean isPurchasePay = "purchase.pay".equals(type);
|
||||
|
||||
if (isSaleCollect || isPurchasePay) {
|
||||
java.util.List<OrderDtos.PaymentItem> payments = req.payments == null ? java.util.List.of() : req.payments;
|
||||
return createPayments(shopId, userId, payments, isSaleCollect ? "sale" : "purchase");
|
||||
}
|
||||
|
||||
if (!(isSaleOut || isPurchaseIn || isSaleReturn || isPurchaseReturn)) throw new IllegalArgumentException("不支持的type");
|
||||
if (req.items == null || req.items.isEmpty()) throw new IllegalArgumentException("明细为空");
|
||||
|
||||
// 后端重算金额
|
||||
final BigDecimal[] totalRef = new BigDecimal[]{BigDecimal.ZERO};
|
||||
for (OrderDtos.Item it : req.items) {
|
||||
BigDecimal qty = n(it.quantity);
|
||||
BigDecimal price = n(it.unitPrice);
|
||||
BigDecimal dr = n(it.discountRate);
|
||||
BigDecimal line = qty.multiply(price).multiply(BigDecimal.ONE.subtract(dr.divide(new BigDecimal("100"))));
|
||||
totalRef[0] = totalRef[0].add(scale2(line));
|
||||
}
|
||||
|
||||
// 库存变动(保存即 approved)
|
||||
LocalDateTime now = nowUtc();
|
||||
for (OrderDtos.Item it : req.items) {
|
||||
Long pid = it.productId;
|
||||
Inventory inv = inventoryRepository.findById(pid).orElseGet(Inventory::new);
|
||||
inv.setProductId(pid);
|
||||
inv.setShopId(shopId);
|
||||
inv.setUserId(userId);
|
||||
BigDecimal cur = n(inv.getQuantity());
|
||||
BigDecimal delta = BigDecimal.ZERO;
|
||||
if (isSaleOut) delta = n(it.quantity).negate();
|
||||
if (isPurchaseIn) delta = n(it.quantity);
|
||||
if (isSaleReturn) delta = n(it.quantity); // 退货入库
|
||||
if (isPurchaseReturn) delta = n(it.quantity).negate(); // 退货出库
|
||||
BigDecimal next = cur.add(delta);
|
||||
if (isSaleOut && next.compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new IllegalStateException("库存不足");
|
||||
}
|
||||
inv.setQuantity(next);
|
||||
inv.setUpdatedAt(now);
|
||||
inventoryRepository.save(inv);
|
||||
|
||||
// 写入库存流水(可选金额)
|
||||
String imSql = "INSERT INTO inventory_movements (shop_id,user_id,product_id,source_type,source_id,qty_delta,amount_delta,reason,tx_time,remark,created_at) VALUES (?,?,?,?,?,?,?,?,?,NULL,NOW())";
|
||||
String sourceType = isSaleOut ? "sale" : (isPurchaseIn ? "purchase" : (isSaleReturn ? "sale_return" : "purchase_return"));
|
||||
jdbcTemplate.update(imSql, shopId, userId, pid, sourceType, null, delta, null, null, java.sql.Timestamp.from(now.atZone(java.time.ZoneOffset.UTC).toInstant()));
|
||||
}
|
||||
|
||||
String prefix = isSaleOut || isSaleReturn ? (isSaleReturn ? "SR" : "SO") : (isPurchaseReturn ? "PR" : "PO");
|
||||
String orderNo = OrderNumberGenerator.next(prefix);
|
||||
|
||||
// 持久化订单头与明细(简化使用 JDBC)
|
||||
String headTable = isSaleOut ? "sales_orders" : (isPurchaseIn ? "purchase_orders" : (isSaleReturn ? "sales_return_orders" : "purchase_return_orders"));
|
||||
String itemTable = isSaleOut ? "sales_order_items" : (isPurchaseIn ? "purchase_order_items" : (isSaleReturn ? "sales_return_order_items" : "purchase_return_order_items"));
|
||||
|
||||
// insert head
|
||||
KeyHolder kh = new GeneratedKeyHolder();
|
||||
String headSql = "INSERT INTO " + headTable + " (shop_id,user_id,customer_id,supplier_id,order_no,order_time,status,amount,paid_amount,remark,created_at,updated_at) " +
|
||||
"VALUES (?,?,?,?,?,?, 'approved', ?, 0, ?, NOW(), NOW())";
|
||||
Long customerId = req.customerId;
|
||||
Long supplierId = req.supplierId;
|
||||
jdbcTemplate.update(con -> {
|
||||
java.sql.PreparedStatement ps = con.prepareStatement(headSql, new String[]{"id"});
|
||||
ps.setLong(1, shopId);
|
||||
ps.setLong(2, userId);
|
||||
if (headTable.startsWith("sales")) {
|
||||
ps.setObject(3, customerId, java.sql.Types.BIGINT);
|
||||
ps.setObject(4, null);
|
||||
} else if (headTable.startsWith("purchase")) {
|
||||
ps.setObject(3, null);
|
||||
ps.setObject(4, supplierId, java.sql.Types.BIGINT);
|
||||
} else {
|
||||
ps.setObject(3, null);
|
||||
ps.setObject(4, null);
|
||||
}
|
||||
ps.setString(5, orderNo);
|
||||
ps.setTimestamp(6, java.sql.Timestamp.from(now.atZone(java.time.ZoneOffset.UTC).toInstant()));
|
||||
ps.setBigDecimal(7, scale2(totalRef[0]));
|
||||
ps.setString(8, req.remark);
|
||||
return ps;
|
||||
}, kh);
|
||||
Number orderKey = kh.getKey();
|
||||
Long orderId = (orderKey == null ? null : orderKey.longValue());
|
||||
|
||||
// insert items
|
||||
String itemSql = "INSERT INTO " + itemTable + " (order_id,product_id,quantity,unit_price,discount_rate,amount) VALUES (?,?,?,?,?,?)";
|
||||
for (OrderDtos.Item it : req.items) {
|
||||
BigDecimal qty = n(it.quantity);
|
||||
BigDecimal price = n(it.unitPrice);
|
||||
BigDecimal dr = n(it.discountRate);
|
||||
BigDecimal line = scale2(qty.multiply(price).multiply(BigDecimal.ONE.subtract(dr.divide(new BigDecimal("100")))));
|
||||
jdbcTemplate.update(itemSql, orderId, it.productId, qty, price, dr, line);
|
||||
}
|
||||
|
||||
return new OrderDtos.CreateOrderResponse(orderId, orderNo);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OrderDtos.CreatePaymentsResponse createPayments(Long shopId, Long userId, java.util.List<OrderDtos.PaymentItem> req, String bizType) {
|
||||
ensureDefaultAccounts(shopId, userId);
|
||||
List<Long> ids = new ArrayList<>();
|
||||
if (req == null) return new OrderDtos.CreatePaymentsResponse(ids);
|
||||
String direction = "sale".equals(bizType) ? "in" : "out";
|
||||
for (OrderDtos.PaymentItem p : req) {
|
||||
Long accountId = resolveAccountId(shopId, userId, p.method);
|
||||
KeyHolder kh = new GeneratedKeyHolder();
|
||||
String sql = "INSERT INTO payments (shop_id,user_id,biz_type,biz_id,account_id,direction,amount,pay_time,remark,created_at) " +
|
||||
"VALUES (?,?,?,?,?,?,?,NOW(),NULL,NOW())";
|
||||
jdbcTemplate.update(con -> {
|
||||
java.sql.PreparedStatement ps = con.prepareStatement(sql, new String[]{"id"});
|
||||
ps.setLong(1, shopId);
|
||||
ps.setLong(2, userId);
|
||||
ps.setString(3, bizType);
|
||||
if (p.orderId == null) ps.setNull(4, java.sql.Types.BIGINT); else ps.setLong(4, p.orderId);
|
||||
ps.setLong(5, accountId);
|
||||
ps.setString(6, direction);
|
||||
ps.setBigDecimal(7, n(p.amount));
|
||||
return ps;
|
||||
}, kh);
|
||||
Number payKey = kh.getKey();
|
||||
Long pid = (payKey == null ? null : payKey.longValue());
|
||||
if (pid != null) ids.add(pid);
|
||||
|
||||
// 若挂单,累加已付
|
||||
if (p.orderId != null) {
|
||||
String table = "sale".equals(bizType) ? "sales_orders" : "purchase_orders";
|
||||
jdbcTemplate.update("UPDATE " + table + " SET paid_amount = paid_amount + ? WHERE id = ?", n(p.amount), p.orderId);
|
||||
}
|
||||
}
|
||||
return new OrderDtos.CreatePaymentsResponse(ids);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void voidOrder(Long shopId, Long userId, Long id, String type) {
|
||||
// type: sale.out / purchase.in / sale.return / purchase.return
|
||||
String headTable;
|
||||
String itemTable;
|
||||
boolean revertIncrease; // true 表示作废时库存应减少(原先增加),false 表示应增加(原先减少)
|
||||
if ("sale.out".equals(type)) { headTable = "sales_orders"; itemTable = "sales_order_items"; revertIncrease = false; }
|
||||
else if ("purchase.in".equals(type)) { headTable = "purchase_orders"; itemTable = "purchase_order_items"; revertIncrease = true; }
|
||||
else if ("sale.return".equals(type)) { headTable = "sales_return_orders"; itemTable = "sales_return_order_items"; revertIncrease = true; }
|
||||
else if ("purchase.return".equals(type)) { headTable = "purchase_return_orders"; itemTable = "purchase_return_order_items"; revertIncrease = false; }
|
||||
else throw new IllegalArgumentException("不支持的type");
|
||||
|
||||
// 查询明细
|
||||
List<java.util.Map<String,Object>> rows = jdbcTemplate.queryForList("SELECT product_id, quantity FROM " + itemTable + " WHERE order_id = ?", id);
|
||||
// 回滚库存
|
||||
LocalDateTime now = nowUtc();
|
||||
for (java.util.Map<String,Object> r : rows) {
|
||||
Long pid = ((Number)r.get("product_id")).longValue();
|
||||
java.math.BigDecimal qty = new java.math.BigDecimal(r.get("quantity").toString());
|
||||
Inventory inv = inventoryRepository.findById(pid).orElseGet(Inventory::new);
|
||||
inv.setProductId(pid);
|
||||
inv.setShopId(shopId);
|
||||
inv.setUserId(userId);
|
||||
java.math.BigDecimal delta = revertIncrease ? qty.negate() : qty; // 与创建时相反
|
||||
inv.setQuantity(n(inv.getQuantity()).add(delta));
|
||||
inv.setUpdatedAt(now);
|
||||
inventoryRepository.save(inv);
|
||||
}
|
||||
// 更新状态
|
||||
jdbcTemplate.update("UPDATE " + headTable + " SET status='void' WHERE id = ?", id);
|
||||
}
|
||||
|
||||
public java.util.Map<String,Object> list(Long shopId, String biz, String type, String kw, int page, int size, String startDate, String endDate) {
|
||||
String headTable;
|
||||
if ("sale".equals(biz)) {
|
||||
headTable = ("sale.return".equals(type) ? "sales_return_orders" : "sales_orders");
|
||||
} else if ("purchase".equals(biz)) {
|
||||
headTable = ("purchase.return".equals(type) ? "purchase_orders" : "purchase_orders");
|
||||
} else {
|
||||
// 若未传,默认销售出货
|
||||
headTable = "sales_orders";
|
||||
}
|
||||
StringBuilder sql = new StringBuilder("SELECT id, order_no, order_time, amount FROM " + headTable + " WHERE shop_id=?");
|
||||
java.util.List<Object> ps = new java.util.ArrayList<>();
|
||||
ps.add(shopId);
|
||||
if ("purchase".equals(biz) && "purchase.return".equals(type)) {
|
||||
sql.append(" AND status='returned'");
|
||||
}
|
||||
if (kw != null && !kw.isBlank()) {
|
||||
sql.append(" AND (order_no LIKE ?)");
|
||||
ps.add('%' + kw + '%');
|
||||
}
|
||||
if (startDate != null && !startDate.isBlank()) { sql.append(" AND order_time>=?"); ps.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sql.append(" AND order_time<=?"); ps.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
sql.append(" ORDER BY order_time DESC LIMIT ? OFFSET ?");
|
||||
ps.add(size);
|
||||
ps.add(page * size);
|
||||
java.util.List<java.util.Map<String,Object>> list = jdbcTemplate.queryForList(sql.toString(), ps.toArray());
|
||||
// 汇总
|
||||
StringBuilder sumSql = new StringBuilder("SELECT COALESCE(SUM(amount),0) FROM " + headTable + " WHERE shop_id=?");
|
||||
java.util.List<Object> sumPs = new java.util.ArrayList<>();
|
||||
sumPs.add(shopId);
|
||||
if ("purchase".equals(biz) && "purchase.return".equals(type)) { sumSql.append(" AND status='returned'"); }
|
||||
if (kw != null && !kw.isBlank()) { sumSql.append(" AND (order_no LIKE ?)"); sumPs.add('%' + kw + '%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sumSql.append(" AND order_time>=?"); sumPs.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sumSql.append(" AND order_time<=?"); sumPs.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
java.math.BigDecimal total = jdbcTemplate.queryForObject(sumSql.toString(), java.math.BigDecimal.class, sumPs.toArray());
|
||||
java.util.Map<String,Object> resp = new java.util.HashMap<>();
|
||||
resp.put("list", list);
|
||||
resp.put("totalAmount", total == null ? java.math.BigDecimal.ZERO : total);
|
||||
return resp;
|
||||
}
|
||||
|
||||
public java.util.Map<String,Object> listPayments(Long shopId, String direction, String bizType, Long accountId, String kw, int page, int size, String startDate, String endDate) {
|
||||
StringBuilder sql = new StringBuilder("SELECT id, biz_type, account_id, direction, amount, pay_time FROM payments WHERE shop_id=?");
|
||||
java.util.List<Object> ps = new java.util.ArrayList<>();
|
||||
ps.add(shopId);
|
||||
if (direction != null && !direction.isBlank()) { sql.append(" AND direction=?"); ps.add(direction); }
|
||||
if (bizType != null && !bizType.isBlank()) { sql.append(" AND biz_type=?"); ps.add(bizType); }
|
||||
if (accountId != null) { sql.append(" AND account_id=?"); ps.add(accountId); }
|
||||
if (kw != null && !kw.isBlank()) { sql.append(" AND (CAST(id AS CHAR) LIKE ?)"); ps.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sql.append(" AND pay_time>=?"); ps.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sql.append(" AND pay_time<=?"); ps.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
sql.append(" ORDER BY pay_time DESC LIMIT ? OFFSET ?");
|
||||
ps.add(size); ps.add(page * size);
|
||||
java.util.List<java.util.Map<String,Object>> list = jdbcTemplate.queryForList(sql.toString(), ps.toArray());
|
||||
StringBuilder sumSql = new StringBuilder("SELECT COALESCE(SUM(amount),0) FROM payments WHERE shop_id=?");
|
||||
java.util.List<Object> sumPs = new java.util.ArrayList<>(); sumPs.add(shopId);
|
||||
if (direction != null && !direction.isBlank()) { sumSql.append(" AND direction=?"); sumPs.add(direction); }
|
||||
if (bizType != null && !bizType.isBlank()) { sumSql.append(" AND biz_type=?"); sumPs.add(bizType); }
|
||||
if (accountId != null) { sumSql.append(" AND account_id=?"); sumPs.add(accountId); }
|
||||
if (kw != null && !kw.isBlank()) { sumSql.append(" AND (CAST(id AS CHAR) LIKE ?)"); sumPs.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sumSql.append(" AND pay_time>=?"); sumPs.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sumSql.append(" AND pay_time<=?"); sumPs.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
java.math.BigDecimal total = jdbcTemplate.queryForObject(sumSql.toString(), java.math.BigDecimal.class, sumPs.toArray());
|
||||
java.util.Map<String,Object> resp = new java.util.HashMap<>(); resp.put("list", list); resp.put("totalAmount", total == null ? java.math.BigDecimal.ZERO : total); return resp;
|
||||
}
|
||||
|
||||
public java.util.Map<String,Object> listOtherTransactions(Long shopId, String type, Long accountId, String kw, int page, int size, String startDate, String endDate) {
|
||||
StringBuilder sql = new StringBuilder("SELECT id, `type`, account_id, amount, tx_time, remark FROM other_transactions WHERE shop_id=?");
|
||||
java.util.List<Object> ps = new java.util.ArrayList<>(); ps.add(shopId);
|
||||
if (type != null && !type.isBlank()) { sql.append(" AND `type`=?"); ps.add(type); }
|
||||
if (accountId != null) { sql.append(" AND account_id=?"); ps.add(accountId); }
|
||||
if (kw != null && !kw.isBlank()) { sql.append(" AND (remark LIKE ? OR category LIKE ?)"); ps.add('%'+kw+'%'); ps.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sql.append(" AND tx_time>=?"); ps.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sql.append(" AND tx_time<=?"); ps.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
sql.append(" ORDER BY tx_time DESC LIMIT ? OFFSET ?"); ps.add(size); ps.add(page * size);
|
||||
java.util.List<java.util.Map<String,Object>> list = jdbcTemplate.queryForList(sql.toString(), ps.toArray());
|
||||
StringBuilder sumSql = new StringBuilder("SELECT COALESCE(SUM(amount),0) FROM other_transactions WHERE shop_id=?");
|
||||
java.util.List<Object> sumPs = new java.util.ArrayList<>(); sumPs.add(shopId);
|
||||
if (type != null && !type.isBlank()) { sumSql.append(" AND `type`=?"); sumPs.add(type); }
|
||||
if (accountId != null) { sumSql.append(" AND account_id=?"); sumPs.add(accountId); }
|
||||
if (kw != null && !kw.isBlank()) { sumSql.append(" AND (remark LIKE ? OR category LIKE ?)"); sumPs.add('%'+kw+'%'); sumPs.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sumSql.append(" AND tx_time>=?"); sumPs.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sumSql.append(" AND tx_time<=?"); sumPs.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
java.math.BigDecimal total = jdbcTemplate.queryForObject(sumSql.toString(), java.math.BigDecimal.class, sumPs.toArray());
|
||||
java.util.Map<String,Object> resp = new java.util.HashMap<>(); resp.put("list", list); resp.put("totalAmount", total == null ? java.math.BigDecimal.ZERO : total); return resp;
|
||||
}
|
||||
|
||||
public java.util.Map<String,Object> listInventoryLogs(Long shopId, Long productId, String reason, String kw, int page, int size, String startDate, String endDate) {
|
||||
StringBuilder sql = new StringBuilder("SELECT id, product_id, qty_delta, amount_delta, reason, tx_time FROM inventory_movements WHERE shop_id=?");
|
||||
java.util.List<Object> ps = new java.util.ArrayList<>(); ps.add(shopId);
|
||||
if (productId != null) { sql.append(" AND product_id=?"); ps.add(productId); }
|
||||
if (reason != null && !reason.isBlank()) { sql.append(" AND reason=?"); ps.add(reason); }
|
||||
if (kw != null && !kw.isBlank()) { sql.append(" AND (remark LIKE ? OR source_type LIKE ?)"); ps.add('%'+kw+'%'); ps.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sql.append(" AND tx_time>=?"); ps.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sql.append(" AND tx_time<=?"); ps.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
sql.append(" ORDER BY tx_time DESC LIMIT ? OFFSET ?"); ps.add(size); ps.add(page * size);
|
||||
java.util.List<java.util.Map<String,Object>> list = jdbcTemplate.queryForList(sql.toString(), ps.toArray());
|
||||
StringBuilder sumSql = new StringBuilder("SELECT COALESCE(SUM(COALESCE(amount_delta,0)),0) FROM inventory_movements WHERE shop_id=?");
|
||||
java.util.List<Object> sumPs = new java.util.ArrayList<>(); sumPs.add(shopId);
|
||||
if (productId != null) { sumSql.append(" AND product_id=?"); sumPs.add(productId); }
|
||||
if (reason != null && !reason.isBlank()) { sumSql.append(" AND reason=?"); sumPs.add(reason); }
|
||||
if (kw != null && !kw.isBlank()) { sumSql.append(" AND (remark LIKE ? OR source_type LIKE ?)"); sumPs.add('%'+kw+'%'); sumPs.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sumSql.append(" AND tx_time>=?"); sumPs.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sumSql.append(" AND tx_time<=?"); sumPs.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
java.math.BigDecimal total = jdbcTemplate.queryForObject(sumSql.toString(), java.math.BigDecimal.class, sumPs.toArray());
|
||||
java.util.Map<String,Object> resp = new java.util.HashMap<>(); resp.put("list", list); resp.put("totalAmount", total == null ? java.math.BigDecimal.ZERO : total); return resp;
|
||||
}
|
||||
|
||||
private static BigDecimal n(BigDecimal v) { return v == null ? BigDecimal.ZERO : v; }
|
||||
private static BigDecimal scale2(BigDecimal v) { return v.setScale(2, java.math.RoundingMode.HALF_UP); }
|
||||
private static LocalDateTime nowUtc() { return LocalDateTime.now(java.time.Clock.systemUTC()); }
|
||||
|
||||
private void ensureDefaultAccounts(Long shopId, Long userId) {
|
||||
// 为 cash/bank/wechat 分别确保存在一条账户记录;按 type→name 顺序检查,避免同名唯一冲突
|
||||
ensureAccount(shopId, userId, "cash", "现金");
|
||||
ensureAccount(shopId, userId, "bank", "银行存款");
|
||||
ensureAccount(shopId, userId, "wechat", "微信");
|
||||
}
|
||||
|
||||
private void ensureAccount(Long shopId, Long userId, String type, String name) {
|
||||
List<Long> byType = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND type=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, type);
|
||||
if (!byType.isEmpty()) return;
|
||||
List<Long> byName = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND name=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, name);
|
||||
if (!byName.isEmpty()) return; // 已有同名则直接复用,无需再插
|
||||
jdbcTemplate.update("INSERT INTO accounts (shop_id,user_id,name,type,balance,status,created_at,updated_at) VALUES (?,?,?,?,0,1,NOW(),NOW())",
|
||||
shopId, userId, name, type);
|
||||
}
|
||||
|
||||
private Long resolveAccountId(Long shopId, Long userId, String method) {
|
||||
String type = "cash";
|
||||
if ("bank".equalsIgnoreCase(method)) type = "bank";
|
||||
if ("wechat".equalsIgnoreCase(method)) type = "wechat";
|
||||
String name = "现金";
|
||||
if ("bank".equals(type)) name = "银行存款"; else if ("wechat".equals(type)) name = "微信";
|
||||
// 先按 type 查
|
||||
List<Long> byType = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND type=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, type);
|
||||
if (!byType.isEmpty()) return byType.get(0);
|
||||
// 再按 name 查,避免同名唯一冲突
|
||||
List<Long> byName = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND name=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, name);
|
||||
if (!byName.isEmpty()) return byName.get(0);
|
||||
// 都没有再插入
|
||||
jdbcTemplate.update("INSERT INTO accounts (shop_id,user_id,name,type,balance,status,created_at,updated_at) VALUES (?,?,?,?,0,1,NOW(),NOW())",
|
||||
shopId, userId, name, type);
|
||||
// 插入后按 type 读取
|
||||
List<Long> recheck = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND type=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, type);
|
||||
if (!recheck.isEmpty()) return recheck.get(0);
|
||||
throw new IllegalStateException("账户映射失败: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.example.demo.order.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public class OrderDtos {
|
||||
|
||||
public static class CreateOrderRequest {
|
||||
public String type; // sale.out / sale.return / sale.collect / purchase.in / purchase.return / purchase.pay
|
||||
public String orderTime; // ISO8601 或 yyyy-MM-dd
|
||||
public Long customerId; // 可空
|
||||
public Long supplierId; // 可空
|
||||
public List<Item> items; // 出入库时必填
|
||||
public List<PaymentItem> payments; // 收款/付款时必填
|
||||
public BigDecimal amount; // 前端提供,后端将重算覆盖
|
||||
public String remark;
|
||||
}
|
||||
|
||||
public static class Item {
|
||||
public Long productId;
|
||||
public BigDecimal quantity;
|
||||
public BigDecimal unitPrice;
|
||||
public BigDecimal discountRate; // 可空,缺省 0
|
||||
}
|
||||
|
||||
public static class PaymentItem {
|
||||
public String method; // cash/bank/wechat
|
||||
public BigDecimal amount;
|
||||
public Long orderId; // 可选:若挂单则带上
|
||||
}
|
||||
|
||||
public static class CreateOrderResponse {
|
||||
public Long id;
|
||||
public String orderNo;
|
||||
public CreateOrderResponse(Long id, String orderNo) { this.id = id; this.orderNo = orderNo; }
|
||||
}
|
||||
|
||||
public static class CreatePaymentsResponse {
|
||||
public java.util.List<Long> paymentIds;
|
||||
public CreatePaymentsResponse(java.util.List<Long> ids) { this.paymentIds = ids; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ public class ProductDtos {
|
||||
public BigDecimal stock;
|
||||
public BigDecimal purchasePrice;
|
||||
public BigDecimal retailPrice;
|
||||
public BigDecimal distributionPrice;
|
||||
public BigDecimal wholesalePrice;
|
||||
public BigDecimal bigClientPrice;
|
||||
public List<Image> images;
|
||||
@@ -61,7 +60,6 @@ public class ProductDtos {
|
||||
public static class Prices {
|
||||
public BigDecimal purchasePrice;
|
||||
public BigDecimal retailPrice;
|
||||
public BigDecimal distributionPrice;
|
||||
public BigDecimal wholesalePrice;
|
||||
public BigDecimal bigClientPrice;
|
||||
}
|
||||
@@ -70,3 +68,6 @@ public class ProductDtos {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -39,3 +39,6 @@ public class Inventory {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -98,3 +98,6 @@ public class Product {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -56,3 +56,6 @@ public class ProductCategory {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -41,3 +41,6 @@ public class ProductImage {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -59,3 +59,6 @@ public class ProductPrice {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -46,3 +46,6 @@ public class ProductUnit {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,3 +15,6 @@ public interface CategoryRepository extends JpaRepository<ProductCategory, Long>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,3 +9,6 @@ public interface InventoryRepository extends JpaRepository<Inventory, Long> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,3 +13,6 @@ public interface ProductImageRepository extends JpaRepository<ProductImage, Long
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,3 +9,6 @@ public interface ProductPriceRepository extends JpaRepository<ProductPrice, Long
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,3 +23,6 @@ public interface ProductRepository extends JpaRepository<Product, Long> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,3 +14,6 @@ public interface UnitRepository extends JpaRepository<com.example.demo.product.e
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@ import com.example.demo.product.repo.ProductImageRepository;
|
||||
import com.example.demo.product.repo.ProductPriceRepository;
|
||||
import com.example.demo.product.repo.ProductRepository;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -27,35 +29,66 @@ public class ProductService {
|
||||
private final ProductPriceRepository priceRepository;
|
||||
private final InventoryRepository inventoryRepository;
|
||||
private final ProductImageRepository imageRepository;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public ProductService(ProductRepository productRepository,
|
||||
ProductPriceRepository priceRepository,
|
||||
InventoryRepository inventoryRepository,
|
||||
ProductImageRepository imageRepository) {
|
||||
ProductImageRepository imageRepository,
|
||||
JdbcTemplate jdbcTemplate) {
|
||||
this.productRepository = productRepository;
|
||||
this.priceRepository = priceRepository;
|
||||
this.inventoryRepository = inventoryRepository;
|
||||
this.imageRepository = imageRepository;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
public Page<ProductDtos.ProductListItem> search(Long shopId, String kw, Long categoryId, int page, int size) {
|
||||
Page<Product> p = productRepository.search(shopId, kw, categoryId, PageRequest.of(page, size));
|
||||
return p.map(prod -> {
|
||||
ProductDtos.ProductListItem it = new ProductDtos.ProductListItem();
|
||||
it.id = prod.getId();
|
||||
it.name = prod.getName();
|
||||
it.brand = prod.getBrand();
|
||||
it.model = prod.getModel();
|
||||
it.spec = prod.getSpec();
|
||||
// stock
|
||||
inventoryRepository.findById(prod.getId()).ifPresent(inv -> it.stock = inv.getQuantity());
|
||||
// price
|
||||
priceRepository.findById(prod.getId()).ifPresent(pr -> it.retailPrice = pr.getRetailPrice());
|
||||
// cover
|
||||
List<ProductImage> imgs = imageRepository.findByProductIdOrderBySortOrderAscIdAsc(prod.getId());
|
||||
it.cover = imgs.isEmpty() ? null : imgs.get(0).getUrl();
|
||||
return it;
|
||||
});
|
||||
try {
|
||||
Page<Product> p = productRepository.search(shopId, kw, categoryId, PageRequest.of(page, size));
|
||||
return p.map(prod -> {
|
||||
ProductDtos.ProductListItem it = new ProductDtos.ProductListItem();
|
||||
it.id = prod.getId();
|
||||
it.name = prod.getName();
|
||||
it.brand = prod.getBrand();
|
||||
it.model = prod.getModel();
|
||||
it.spec = prod.getSpec();
|
||||
inventoryRepository.findById(prod.getId()).ifPresent(inv -> it.stock = inv.getQuantity());
|
||||
priceRepository.findById(prod.getId()).ifPresent(pr -> it.retailPrice = pr.getRetailPrice());
|
||||
List<ProductImage> imgs = imageRepository.findByProductIdOrderBySortOrderAscIdAsc(prod.getId());
|
||||
it.cover = imgs.isEmpty() ? null : imgs.get(0).getUrl();
|
||||
return it;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
// 安全回退为 JDBC 查询,保障功能可用
|
||||
StringBuilder sql = new StringBuilder("SELECT p.id,p.name,p.brand,p.model,p.spec,\n" +
|
||||
"(SELECT i.quantity FROM inventories i WHERE i.product_id=p.id) AS stock,\n" +
|
||||
"(SELECT pr.retail_price FROM product_prices pr WHERE pr.product_id=p.id) AS retail_price,\n" +
|
||||
"(SELECT img.url FROM product_images img WHERE img.product_id=p.id ORDER BY img.sort_order, img.id LIMIT 1) AS cover\n" +
|
||||
"FROM products p WHERE p.shop_id=? AND p.deleted_at IS NULL");
|
||||
List<Object> ps = new ArrayList<>();
|
||||
ps.add(shopId);
|
||||
if (kw != null && !kw.isBlank()) { sql.append(" AND (p.name LIKE ? OR p.brand LIKE ? OR p.model LIKE ? OR p.spec LIKE ? OR p.barcode LIKE ?)");
|
||||
String like = "%" + kw + "%"; ps.add(like); ps.add(like); ps.add(like); ps.add(like); ps.add(like); }
|
||||
if (categoryId != null) { sql.append(" AND p.category_id=?"); ps.add(categoryId); }
|
||||
sql.append(" ORDER BY p.id DESC LIMIT ? OFFSET ?");
|
||||
ps.add(size); ps.add(page * size);
|
||||
List<ProductDtos.ProductListItem> list = jdbcTemplate.query(sql.toString(), (rs,rn) -> {
|
||||
ProductDtos.ProductListItem it = new ProductDtos.ProductListItem();
|
||||
it.id = rs.getLong("id");
|
||||
it.name = rs.getString("name");
|
||||
it.brand = rs.getString("brand");
|
||||
it.model = rs.getString("model");
|
||||
it.spec = rs.getString("spec");
|
||||
java.math.BigDecimal st = (java.math.BigDecimal) rs.getObject("stock");
|
||||
it.stock = st;
|
||||
java.math.BigDecimal rp = (java.math.BigDecimal) rs.getObject("retail_price");
|
||||
it.retailPrice = rp;
|
||||
it.cover = rs.getString("cover");
|
||||
return it;
|
||||
}, ps.toArray());
|
||||
return new PageImpl<>(list, PageRequest.of(page, size), list.size());
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<ProductDtos.ProductDetail> findDetail(Long id) {
|
||||
@@ -78,7 +111,6 @@ public class ProductService {
|
||||
priceRepository.findById(p.getId()).ifPresent(pr -> {
|
||||
d.purchasePrice = pr.getPurchasePrice();
|
||||
d.retailPrice = pr.getRetailPrice();
|
||||
d.distributionPrice = pr.getDistributionPrice();
|
||||
d.wholesalePrice = pr.getWholesalePrice();
|
||||
d.bigClientPrice = pr.getBigClientPrice();
|
||||
});
|
||||
@@ -171,12 +203,6 @@ public class ProductService {
|
||||
pr.setUserId(userId);
|
||||
pr.setPurchasePrice(nvl(prices.purchasePrice, BigDecimal.ZERO));
|
||||
pr.setRetailPrice(nvl(prices.retailPrice, BigDecimal.ZERO));
|
||||
// 前端不再传分销价:仅当入参提供时更新;新建记录若未提供则置 0
|
||||
if (prices.distributionPrice != null) {
|
||||
pr.setDistributionPrice(prices.distributionPrice);
|
||||
} else if (existed.isEmpty()) {
|
||||
pr.setDistributionPrice(BigDecimal.ZERO);
|
||||
}
|
||||
pr.setWholesalePrice(nvl(prices.wholesalePrice, BigDecimal.ZERO));
|
||||
pr.setBigClientPrice(nvl(prices.bigClientPrice, BigDecimal.ZERO));
|
||||
pr.setUpdatedAt(now);
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
spring.application.name=demo
|
||||
|
||||
# 数据源配置(通过环境变量注入,避免硬编码)
|
||||
spring.datasource.url=${SPRING_DATASOURCE_URL}
|
||||
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
|
||||
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
|
||||
# 正确的配置
|
||||
# 格式为: jdbc:mysql://<主机名>:<端口号>/<数据库名>?参数
|
||||
# 默认附带 MySQL 8 推荐参数,避免握手/时区/编码问题
|
||||
spring.datasource.url=${DB_URL:jdbc:mysql://mysql.tonaspace.com:3306/partsinquiry?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8}
|
||||
|
||||
# 用户名和密码直接写值
|
||||
spring.datasource.username=${DB_USER:root}
|
||||
spring.datasource.password=${DB_PASSWORD:TONA1234}
|
||||
|
||||
# JPA 基本配置
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
|
||||
@@ -192,7 +192,6 @@
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| purchase_price | DECIMAL(18,2) | NOT NULL | 0.00 | |
|
||||
| retail_price | DECIMAL(18,2) | NOT NULL | 0.00 | |
|
||||
| distribution_price | DECIMAL(18,2) | NOT NULL | 0.00 | |
|
||||
| wholesale_price | DECIMAL(18,2) | NOT NULL | 0.00 | |
|
||||
| big_client_price | DECIMAL(18,2) | NOT NULL | 0.00 | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
@@ -308,7 +307,7 @@
|
||||
| supplier_id | BIGINT UNSIGNED | YES | | |
|
||||
| order_no | VARCHAR(32) | NOT NULL | | |
|
||||
| order_time | DATETIME | NOT NULL | | |
|
||||
| status | ENUM('draft','approved','void') | NOT NULL | draft | |
|
||||
| status | ENUM('draft','approved','void','returned') | NOT NULL | draft | |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | 0.00 | 应付合计 |
|
||||
| paid_amount | DECIMAL(18,2) | NOT NULL | 0.00 | 已付合计 |
|
||||
| remark | VARCHAR(255) | YES | | |
|
||||
@@ -399,6 +398,25 @@
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_notices_shop` (`shop_id`,`status`,`is_pinned`,`created_at`) - KEY: `idx_notices_time` (`starts_at`,`ends_at`)
|
||||
**Foreign Keys**: - `fk_notices_shop`: `shop_id` → `shops(id)` ON UPDATE NO ACTION ON DELETE NO ACTION - `fk_notices_user`: `user_id` → `users(id)` ON UPDATE NO ACTION ON DELETE NO ACTION
|
||||
|
||||
### inventory_movements
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| product_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| source_type | VARCHAR(32) | NOT NULL | | sale/purchase/sale_return/purchase_return/adjust |
|
||||
| source_id | BIGINT UNSIGNED | YES | | 关联单据ID |
|
||||
| qty_delta | DECIMAL(18,3) | NOT NULL | | 数量增减(正加负减) |
|
||||
| amount_delta | DECIMAL(18,2) | YES | | 金额变动(可空) |
|
||||
| reason | VARCHAR(64) | YES | | |
|
||||
| tx_time | DATETIME | NOT NULL | | |
|
||||
| remark | VARCHAR(255) | YES | | |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_im_shop_time` (`shop_id`,`tx_time`) - KEY: `idx_im_product` (`product_id`)
|
||||
**Foreign Keys**: - `fk_im_shop`: `shop_id` → `shops(id)` - `fk_im_user`: `user_id` → `users(id)` - `fk_im_product`: `product_id` → `products(id)`
|
||||
|
||||
### 附:演示种子数据(非完整,仅用于联调验证)
|
||||
- 演示店铺:演示店A(用户 3,全部店长 owner)
|
||||
- 商品域:基础单位3条、类别2条、全局SKU2条、商品2条(含别名/价格/库存/图片)
|
||||
@@ -406,3 +424,70 @@
|
||||
- 单据:销售单1(含明细2)与进货单1(含明细2)、收付款各1、其他收支2
|
||||
- 审核与公告:part_submissions 1、attachments 1、notices 2、新增 wechat 身份与会话各1
|
||||
|
||||
### sales_return_orders
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| customer_id | BIGINT UNSIGNED | YES | | |
|
||||
| order_no | VARCHAR(32) | NOT NULL | | |
|
||||
| order_time | DATETIME | NOT NULL | | |
|
||||
| status | ENUM('approved','void') | NOT NULL | approved | |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | 0.00 | |
|
||||
| paid_amount | DECIMAL(18,2) | NOT NULL | 0.00 | |
|
||||
| remark | VARCHAR(255) | YES | | |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| deleted_at | DATETIME | YES | | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - UNIQUE: `ux_sr_order_no` (`shop_id`,`order_no`) - KEY: `idx_sr_shop_time` (`shop_id`,`order_time`)
|
||||
**Foreign Keys**: - `fk_sr_shop`: `shop_id` → `shops(id)` - `fk_sr_user`: `user_id` → `users(id)` - `fk_sr_customer`: `customer_id` → `customers(id)`
|
||||
|
||||
### sales_return_order_items
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| order_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| product_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| quantity | DECIMAL(18,3) | NOT NULL | | |
|
||||
| unit_price | DECIMAL(18,2) | NOT NULL | | |
|
||||
| discount_rate | DECIMAL(5,2) | NOT NULL | 0.00 | |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_sroi_order` (`order_id`) - KEY: `idx_sroi_product` (`product_id`)
|
||||
**Foreign Keys**: - `fk_sroi_order`: `order_id` → `sales_return_orders(id)` ON DELETE CASCADE - `fk_sroi_product`: `product_id` → `products(id)`
|
||||
|
||||
### purchase_return_orders
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| user_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| supplier_id | BIGINT UNSIGNED | YES | | |
|
||||
| order_no | VARCHAR(32) | NOT NULL | | |
|
||||
| order_time | DATETIME | NOT NULL | | |
|
||||
| status | ENUM('approved','void') | NOT NULL | approved | |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | 0.00 | |
|
||||
| paid_amount | DECIMAL(18,2) | NOT NULL | 0.00 | |
|
||||
| remark | VARCHAR(255) | YES | | |
|
||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||
| deleted_at | DATETIME | YES | | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - UNIQUE: `ux_pr_order_no` (`shop_id`,`order_no`) - KEY: `idx_pr_shop_time` (`shop_id`,`order_time`)
|
||||
**Foreign Keys**: - `fk_pr_shop`: `shop_id` → `shops(id)` - `fk_pr_user`: `user_id` → `users(id)` - `fk_pr_supplier`: `supplier_id` → `suppliers(id)`
|
||||
|
||||
### purchase_return_order_items
|
||||
| Column Name | Data Type | Nullable | Default | Comment |
|
||||
| ----------- | --------- | -------- | ------- | ------- |
|
||||
| id | BIGINT UNSIGNED | NOT NULL | AUTO_INCREMENT | |
|
||||
| order_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| product_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||
| quantity | DECIMAL(18,3) | NOT NULL | | |
|
||||
| unit_price | DECIMAL(18,2) | NOT NULL | | |
|
||||
| amount | DECIMAL(18,2) | NOT NULL | | |
|
||||
|
||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_proi_order` (`order_id`) - KEY: `idx_proi_product` (`product_id`)
|
||||
**Foreign Keys**: - `fk_proi_order`: `order_id` → `purchase_return_orders(id)` ON DELETE CASCADE - `fk_proi_product`: `product_id` → `products(id)`
|
||||
|
||||
|
||||
242
doc/openapi.yaml
242
doc/openapi.yaml
@@ -370,8 +370,8 @@ paths:
|
||||
$ref: '#/components/schemas/Customer'
|
||||
/api/orders:
|
||||
post:
|
||||
summary: 新建单据(❌ Partially Implemented)
|
||||
description: 前端开单页已提交 payload,后端待实现。
|
||||
summary: 新建单据(✅ Fully Implemented)
|
||||
description: 销售/进货出入库与退货:保存即 approved;后端重算金额并联动库存。
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@@ -391,6 +391,224 @@ paths:
|
||||
format: int64
|
||||
orderNo:
|
||||
type: string
|
||||
/api/orders:
|
||||
get:
|
||||
summary: 单据列表查询(❌ Partially Implemented)
|
||||
description: 支持按时间范围与关键字筛选;参数 biz=sale|purchase,type=out|in|return;返回 {list:[]}。
|
||||
parameters:
|
||||
- in: query
|
||||
name: biz
|
||||
schema: { type: string }
|
||||
- in: query
|
||||
name: type
|
||||
schema: { type: string }
|
||||
- in: query
|
||||
name: kw
|
||||
schema: { type: string }
|
||||
- in: query
|
||||
name: page
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: size
|
||||
schema: { type: integer, default: 20 }
|
||||
- in: query
|
||||
name: startDate
|
||||
schema: { type: string, format: date }
|
||||
- in: query
|
||||
name: endDate
|
||||
schema: { type: string, format: date }
|
||||
/api/payments/{biz}:
|
||||
post:
|
||||
summary: 创建收款/付款(✅ Fully Implemented)
|
||||
description: biz=sale|purchase;根据 payments 写入多条记录,可选挂单并累加订单已付金额。
|
||||
parameters:
|
||||
- in: path
|
||||
name: biz
|
||||
required: true
|
||||
schema: { type: string }
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PaymentItem'
|
||||
responses:
|
||||
'200':
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
paymentIds:
|
||||
type: array
|
||||
items: { type: integer, format: int64 }
|
||||
description: 支持按时间范围与关键字筛选;返回 {list:[]}。前端已接入,后端待实现。
|
||||
parameters:
|
||||
- in: query
|
||||
name: kw
|
||||
schema: { type: string }
|
||||
- in: query
|
||||
name: page
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: size
|
||||
schema: { type: integer, default: 20 }
|
||||
- in: query
|
||||
name: startDate
|
||||
schema: { type: string, format: date }
|
||||
- in: query
|
||||
name: endDate
|
||||
schema: { type: string, format: date }
|
||||
responses:
|
||||
'200':
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
list:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: integer, format: int64 }
|
||||
orderNo: { type: string }
|
||||
orderTime: { type: string, format: date-time }
|
||||
amount: { type: number }
|
||||
customerName: { type: string }
|
||||
|
||||
/api/purchase-orders:
|
||||
get:
|
||||
summary: 进货单列表查询(❌ Partially Implemented)
|
||||
parameters:
|
||||
- in: query
|
||||
name: kw
|
||||
schema: { type: string }
|
||||
- in: query
|
||||
name: page
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: size
|
||||
schema: { type: integer, default: 20 }
|
||||
- in: query
|
||||
name: startDate
|
||||
schema: { type: string, format: date }
|
||||
- in: query
|
||||
name: endDate
|
||||
schema: { type: string, format: date }
|
||||
- in: query
|
||||
name: status
|
||||
schema:
|
||||
type: string
|
||||
enum: [draft, approved, void, returned]
|
||||
responses:
|
||||
'200':
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
list:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: integer, format: int64 }
|
||||
orderNo: { type: string }
|
||||
orderTime: { type: string, format: date-time }
|
||||
amount: { type: number }
|
||||
supplierName: { type: string }
|
||||
|
||||
/api/payments:
|
||||
get:
|
||||
summary: 收付款流水列表(❌ Partially Implemented)
|
||||
parameters:
|
||||
- in: query
|
||||
name: kw
|
||||
schema: { type: string }
|
||||
- in: query
|
||||
name: page
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: size
|
||||
schema: { type: integer, default: 20 }
|
||||
- in: query
|
||||
name: startDate
|
||||
schema: { type: string, format: date }
|
||||
- in: query
|
||||
name: endDate
|
||||
schema: { type: string, format: date }
|
||||
responses:
|
||||
'200':
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
list:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: integer, format: int64 }
|
||||
bizType: { type: string }
|
||||
direction: { type: string }
|
||||
payTime: { type: string, format: date-time }
|
||||
amount: { type: number }
|
||||
accountName: { type: string }
|
||||
|
||||
/api/inventories/logs:
|
||||
get:
|
||||
summary: 库存/盘点流水列表(❌ Partially Implemented)
|
||||
parameters:
|
||||
- in: query
|
||||
name: kw
|
||||
schema: { type: string }
|
||||
- in: query
|
||||
name: page
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: size
|
||||
schema: { type: integer, default: 20 }
|
||||
- in: query
|
||||
name: startDate
|
||||
schema: { type: string, format: date }
|
||||
- in: query
|
||||
name: endDate
|
||||
schema: { type: string, format: date }
|
||||
- in: query
|
||||
name: productId
|
||||
schema: { type: integer, format: int64 }
|
||||
- in: query
|
||||
name: reason
|
||||
schema: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
list:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: integer, format: int64 }
|
||||
bizType: { type: string }
|
||||
txTime: { type: string, format: date-time }
|
||||
amount: { type: number }
|
||||
remark: { type: string }
|
||||
productId: { type: integer, format: int64 }
|
||||
qtyDelta: { type: number }
|
||||
amountDelta: { type: number, nullable: true }
|
||||
/api/attachments:
|
||||
post:
|
||||
summary: 上传附件(✅ Fully Implemented,占位图方案)
|
||||
@@ -633,14 +851,18 @@ components:
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: 'sale.out/sale.return/sale.collect/purchase/income/expense 等'
|
||||
description: 'sale.out/sale.return/sale.collect/purchase.in/purchase.return/purchase.pay'
|
||||
orderTime:
|
||||
type: string
|
||||
format: date
|
||||
format: date-time
|
||||
customerId:
|
||||
type: integer
|
||||
format: int64
|
||||
nullable: true
|
||||
supplierId:
|
||||
type: integer
|
||||
format: int64
|
||||
nullable: true
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
@@ -653,6 +875,18 @@ components:
|
||||
type: number
|
||||
unitPrice:
|
||||
type: number
|
||||
discountRate:
|
||||
type: number
|
||||
amount:
|
||||
type: number
|
||||
payments:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PaymentItem'
|
||||
PaymentItem:
|
||||
type: object
|
||||
properties:
|
||||
method: { type: string, enum: [cash, bank, wechat] }
|
||||
amount: { type: number }
|
||||
orderId: { type: integer, format: int64, nullable: true }
|
||||
|
||||
|
||||
@@ -162,3 +162,6 @@ export default {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,4 +19,29 @@ export function createApp() {
|
||||
app
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 规范化 WebSocket 关闭码(仅微信小程序)
|
||||
// #ifdef MP-WEIXIN
|
||||
if (typeof uni !== 'undefined' && typeof uni.connectSocket === 'function') {
|
||||
const _connectSocket = uni.connectSocket
|
||||
uni.connectSocket = function(options) {
|
||||
const task = _connectSocket.call(this, options)
|
||||
if (task && typeof task.close === 'function') {
|
||||
const _close = task.close
|
||||
task.close = function(params = {}) {
|
||||
if (params && typeof params === 'object') {
|
||||
const codeNum = Number(params.code)
|
||||
const isValid = codeNum === 1000 || (codeNum >= 3000 && codeNum <= 4999)
|
||||
if (!isValid) {
|
||||
params.code = 1000
|
||||
if (!params.reason) params.reason = 'normalized from invalid close code'
|
||||
}
|
||||
}
|
||||
return _close.call(this, params)
|
||||
}
|
||||
}
|
||||
return task
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
@@ -65,6 +65,12 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择账户"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/detail/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "明细"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
162
frontend/pages/detail/index.vue
Normal file
162
frontend/pages/detail/index.vue
Normal file
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- 顶部时间维度筛选 -->
|
||||
<view class="seg">
|
||||
<view :class="['seg-item', range==='custom' && 'active']" @click="switchRange('custom')">自定义</view>
|
||||
<view :class="['seg-item', range==='week' && 'active']" @click="switchRange('week')">本周</view>
|
||||
<view :class="['seg-item', range==='today' && 'active']" @click="switchRange('today')">今日</view>
|
||||
<view :class="['seg-item', range==='month' && 'active']" @click="switchRange('month')">本月</view>
|
||||
<view :class="['seg-item', range==='year' && 'active']" @click="switchRange('year')">本年</view>
|
||||
</view>
|
||||
|
||||
<!-- 业务类型侧边切换:销售/进货/收款/资金/盘点 -->
|
||||
<view class="content">
|
||||
<view class="biz-tabs">
|
||||
<view v-for="b in bizList" :key="b.key" :class="['biz', biz===b.key && 'active']" @click="switchBiz(b.key)">{{ b.name }}</view>
|
||||
</view>
|
||||
|
||||
<view class="panel">
|
||||
<!-- 搜索框与期间显示、总额 -->
|
||||
<view class="toolbar">
|
||||
<view class="search">
|
||||
<input class="search-input" v-model.trim="query.kw" :placeholder="placeholder" @confirm="reload" />
|
||||
</view>
|
||||
<view class="period">{{ periodLabel }}</view>
|
||||
<button size="mini" @click="reload">查询</button>
|
||||
</view>
|
||||
|
||||
<view class="total">合计:¥{{ totalAmount.toFixed(2) }}</view>
|
||||
|
||||
<!-- 列表 -->
|
||||
<scroll-view scroll-y class="list" @scrolltolower="loadMore">
|
||||
<block v-if="items.length">
|
||||
<view class="item" v-for="it in items" :key="it.id" @click="openDetail(it)">
|
||||
<view class="item-left">
|
||||
<view class="date">{{ formatDate(it.orderTime || it.txTime || it.createdAt) }}</view>
|
||||
<view class="name">{{ it.customerName || it.supplierName || it.accountName || it.remark || '-' }}</view>
|
||||
<view class="no">{{ it.orderNo || it.code || it.id }}</view>
|
||||
</view>
|
||||
<view class="amount">¥ {{ (it.amount || 0).toFixed(2) }}</view>
|
||||
<view class="arrow">›</view>
|
||||
</view>
|
||||
</block>
|
||||
<view v-else class="empty">暂无数据</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 右下角新增按钮:根据业务类型跳转对应开单页或创建页 -->
|
||||
<view class="fab" @click="onCreate">+</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { get } from '../../common/http.js'
|
||||
|
||||
const API_OF = {
|
||||
sale: '/api/orders',
|
||||
purchase: '/api/purchase-orders',
|
||||
collect: '/api/payments',
|
||||
fund: '/api/other-transactions',
|
||||
stock: '/api/inventories/logs'
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
biz: 'sale',
|
||||
bizList: [
|
||||
{ key: 'sale', name: '出货' },
|
||||
{ key: 'purchase', name: '进货' },
|
||||
{ key: 'collect', name: '收款' },
|
||||
{ key: 'fund', name: '资金' },
|
||||
{ key: 'stock', name: '盘点' }
|
||||
],
|
||||
range: 'month',
|
||||
query: { kw: '' },
|
||||
items: [],
|
||||
page: 1,
|
||||
size: 20,
|
||||
finished: false,
|
||||
loading: false,
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
}
|
||||
},
|
||||
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() {
|
||||
try { console.log('[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 = 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() },
|
||||
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?.list) ? res.list : (Array.isArray(res) ? res : [])
|
||||
this.items = this.items.concat(list)
|
||||
if (list.length < this.size) this.finished = true
|
||||
this.page += 1
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
} finally { this.loading = false }
|
||||
},
|
||||
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.navigateTo({ url: '/pages/order/create' }); return } uni.showToast({ title: '该类型创建页待实现', icon: 'none' }) },
|
||||
openDetail(it) { uni.showToast({ title: '详情开发中', icon: 'none' }) }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.page { display:flex; flex-direction: column; height: 100vh; }
|
||||
.seg { display:flex; background:#fff; }
|
||||
.seg-item { flex:1; padding: 22rpx 0; text-align:center; color:#666; }
|
||||
.seg-item.active { color:#18b566; font-weight: 600; }
|
||||
|
||||
.content { display:flex; flex:1; min-height: 0; }
|
||||
.biz-tabs { width: 120rpx; background:#eef6ff; display:flex; flex-direction: column; }
|
||||
.biz { flex:0 0 120rpx; display:flex; align-items:center; justify-content:center; color:#4aa3d6; }
|
||||
.biz.active { background:#3ac1c9; color:#fff; border-radius: 0 16rpx 16rpx 0; }
|
||||
|
||||
.panel { flex:1; display:flex; flex-direction: column; background:#fff; margin: 16rpx; border-radius: 16rpx; padding: 12rpx; }
|
||||
.toolbar { display:flex; align-items: center; gap: 12rpx; padding: 8rpx 6rpx; }
|
||||
.search { flex:1; }
|
||||
.search-input { width:100%; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }
|
||||
.period { color:#999; font-size: 24rpx; padding: 0 6rpx; }
|
||||
.total { color:#18b566; font-weight: 700; padding: 6rpx 6rpx 12rpx; }
|
||||
.list { flex:1; }
|
||||
.item { display:flex; align-items:center; padding: 20rpx 10rpx; border-bottom: 1rpx solid #f1f1f1; }
|
||||
.item-left { flex:1; }
|
||||
.date { color:#999; font-size: 24rpx; }
|
||||
.name { color:#333; margin: 4rpx 0; font-weight: 600; }
|
||||
.no { color:#bbb; font-size: 22rpx; }
|
||||
.amount { color:#333; font-weight: 700; }
|
||||
.arrow { color:#ccc; font-size: 40rpx; margin-left: 8rpx; }
|
||||
.empty { height: 50vh; display:flex; align-items:center; justify-content:center; color:#999; }
|
||||
.fab { position: fixed; right: 30rpx; bottom: 120rpx; width: 100rpx; height: 100rpx; background:#18b566; color:#fff; border-radius: 50rpx; text-align:center; line-height: 100rpx; font-size: 48rpx; box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.15); }
|
||||
</style>
|
||||
@@ -75,7 +75,7 @@
|
||||
<view class="tab primary" @click="onCreateOrder">
|
||||
<text>开单</text>
|
||||
</view>
|
||||
<view class="tab" :class="{ active: activeTab==='detail' }" @click="activeTab='detail'">
|
||||
<view class="tab" :class="{ active: activeTab==='detail' }" @click="goDetail">
|
||||
<text>明细</text>
|
||||
</view>
|
||||
<view class="tab" :class="{ active: activeTab==='report' }" @click="activeTab='report'">
|
||||
@@ -161,6 +161,11 @@
|
||||
onCreateOrder() {
|
||||
uni.navigateTo({ url: '/pages/order/create' })
|
||||
},
|
||||
goDetail() {
|
||||
this.activeTab = 'detail'
|
||||
try { console.log('[index] goDetail → /pages/detail/index') } catch(e){}
|
||||
uni.navigateTo({ url: '/pages/detail/index' })
|
||||
},
|
||||
onNoticeTap(n) {
|
||||
uni.showModal({
|
||||
title: '广告',
|
||||
@@ -325,6 +330,7 @@
|
||||
background: rgba(255,255,255,0.85);
|
||||
box-shadow: 0 -6rpx 18rpx rgba(0,0,0,0.08);
|
||||
backdrop-filter: blur(10rpx);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.tab { flex: 1; text-align: center; color: #8a7535; font-size: 26rpx; }
|
||||
|
||||
@@ -36,8 +36,48 @@
|
||||
<text class="value">{{ supplierLabel }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 已选商品与合计(销售/进货) -->
|
||||
<view v-if="biz==='sale' || biz==='purchase'">
|
||||
<!-- 销售/进货:收款/付款 专用页面 -->
|
||||
<view v-if="(biz==='sale' && saleType==='collect') || (biz==='purchase' && purchaseType==='pay')">
|
||||
<!-- 客户 / 供应商 -->
|
||||
<view class="field" v-if="biz==='sale'" @click="chooseCustomer">
|
||||
<text class="label">客户</text>
|
||||
<text class="value">{{ customerLabel }}</text>
|
||||
</view>
|
||||
<view class="field" v-else @click="chooseSupplier">
|
||||
<text class="label">供应商</text>
|
||||
<text class="value">{{ supplierLabel }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 三种收付款方式 -->
|
||||
<view class="field pay-row">
|
||||
<text class="label">现金</text>
|
||||
<input class="pay-input" type="digit" v-model.number="payments.cash" placeholder="0.00" @input="recalcPay()"/>
|
||||
</view>
|
||||
<view class="field pay-row">
|
||||
<text class="label">银行存款</text>
|
||||
<input class="pay-input" type="digit" v-model.number="payments.bank" placeholder="0.00" @input="recalcPay()"/>
|
||||
</view>
|
||||
<view class="field pay-row">
|
||||
<text class="label">微信</text>
|
||||
<input class="pay-input" type="digit" v-model.number="payments.wechat" placeholder="0.00" @input="recalcPay()"/>
|
||||
</view>
|
||||
|
||||
<view class="collapse-trigger" @click="showMore = !showMore">{{ showMore ? '收起' : '' }}</view>
|
||||
|
||||
<!-- 备注与日期 -->
|
||||
<view class="textarea">
|
||||
<view class="amount-badge">总金额:{{ payTotal.toFixed(2) }}</view>
|
||||
<textarea v-model="order.remark" maxlength="200" placeholder="备注(最多输入200个字)"></textarea>
|
||||
<view class="date-mini">
|
||||
<picker mode="date" :value="order.orderTime" @change="onDateChange">
|
||||
<text>{{ order.orderTime }}</text>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 已选商品与合计(销售/进货 出入库) -->
|
||||
<view v-else-if="biz==='sale' || biz==='purchase'">
|
||||
<view class="summary">
|
||||
<text>选中货品({{ totalQuantity }})</text>
|
||||
<text>合计金额:¥ {{ totalAmount.toFixed(2) }}</text>
|
||||
@@ -127,7 +167,10 @@
|
||||
activeCategory: 'sale_income',
|
||||
trxAmount: 0,
|
||||
selectedAccountId: null,
|
||||
selectedAccountName: ''
|
||||
selectedAccountName: '',
|
||||
// 收款/付款输入
|
||||
payments: { cash: 0, bank: 0, wechat: 0 },
|
||||
showMore: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -141,11 +184,16 @@
|
||||
supplierLabel() { return this.supplierName || '零散供应商' },
|
||||
incomeCategories() { return INCOME_CATEGORIES },
|
||||
expenseCategories() { return EXPENSE_CATEGORIES },
|
||||
accountLabel() { return this.selectedAccountName || '现金' },
|
||||
counterpartyLabel() { return this.customerName || this.supplierName || '—' }
|
||||
accountLabel() { return this.selectedAccountName || '现金' },
|
||||
counterpartyLabel() { return 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)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
switchBiz(type) { this.biz = type },
|
||||
switchBiz(type) { this.biz = type },
|
||||
onDateChange(e) { this.order.orderTime = e.detail.value },
|
||||
chooseCustomer() {
|
||||
uni.navigateTo({ url: '/pages/customer/select' })
|
||||
@@ -160,17 +208,24 @@
|
||||
uni.navigateTo({ url: '/pages/customer/select' })
|
||||
}
|
||||
},
|
||||
recalc() { this.$forceUpdate() },
|
||||
recalc() { this.$forceUpdate() },
|
||||
recalcPay() { this.$forceUpdate() },
|
||||
async submit() {
|
||||
const isSaleOrPurchase = (this.biz==='sale' || this.biz==='purchase')
|
||||
const payload = isSaleOrPurchase ? {
|
||||
type: this.biz === 'sale' ? (this.saleType) : ('purchase.' + this.purchaseType),
|
||||
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
|
||||
} : {
|
||||
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)
|
||||
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,
|
||||
counterpartyId: this.order.customerId || null,
|
||||
@@ -179,8 +234,8 @@
|
||||
txTime: this.order.orderTime,
|
||||
remark: this.order.remark
|
||||
}
|
||||
try {
|
||||
const url = isSaleOrPurchase ? '/api/orders' : '/api/other-transactions'
|
||||
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)
|
||||
@@ -188,10 +243,11 @@
|
||||
uni.showToast({ title: e && e.message || '保存失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
saveAndReset() {
|
||||
saveAndReset() {
|
||||
this.items = []
|
||||
this.trxAmount = 0
|
||||
this.order.remark = ''
|
||||
this.payments = { cash: 0, bank: 0, wechat: 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,6 +275,11 @@
|
||||
.col.amount { text-align:right; padding-right: 12rpx; color:#333; }
|
||||
.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06); }
|
||||
.primary { width: 100%; background: linear-gradient(135deg, #FFE69A 0%, #F4CF62 45%, #D7A72E 100%); color:#493c1b; border-radius: 999rpx; padding: 20rpx 0; font-weight:800; }
|
||||
/* 收款/付款页样式 */
|
||||
.pay-row .pay-input { text-align: right; color:#333; }
|
||||
.textarea { position: relative; padding: 16rpx 24rpx; background:#fff; border-top: 1rpx solid #eee; }
|
||||
.amount-badge { position: absolute; right: 24rpx; top: -36rpx; background: #d1f0ff; color:#107e9b; padding: 8rpx 16rpx; border-radius: 12rpx; font-size: 24rpx; }
|
||||
.date-mini { position: absolute; right: 24rpx; bottom: 20rpx; color:#666; font-size: 24rpx; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -65,3 +65,6 @@ export default {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ export default {
|
||||
this.fetchCategories()
|
||||
this.reload()
|
||||
},
|
||||
onShow() {
|
||||
// 从创建/编辑页返回时,确保刷新最新列表
|
||||
this.reload()
|
||||
},
|
||||
computed: {
|
||||
categoryNames() { return this.categories.map(c => c.name) },
|
||||
categoryLabel() {
|
||||
@@ -131,3 +135,6 @@ export default {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<scroll-view scroll-y class="list">
|
||||
<view class="item" v-for="p in products" :key="p.id" @click="select(p)">
|
||||
<view class="name">{{ p.name }}</view>
|
||||
<view class="meta">{{ p.code }} · 库存:{{ p.stock || 0 }}</view>
|
||||
<view class="meta">{{ (p.brand||'') + ' ' + (p.model||'') + ' ' + (p.spec||'') }} · 库存:{{ p.stock ?? 0 }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
@@ -43,3 +43,6 @@ export default {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -65,3 +65,6 @@ export default {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
0
frontend/static/icons/detail.png
Normal file
0
frontend/static/icons/detail.png
Normal file
11
frontend/unpackage/dist/dev/.nvue/app.css.js
vendored
11
frontend/unpackage/dist/dev/.nvue/app.css.js
vendored
@@ -1,11 +0,0 @@
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __commonJS = (cb, mod) => function __require() {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
};
|
||||
var require_app_css = __commonJS({
|
||||
"app.css.js"(exports) {
|
||||
const _style_0 = {};
|
||||
exports.styles = [_style_0];
|
||||
}
|
||||
});
|
||||
export default require_app_css();
|
||||
2
frontend/unpackage/dist/dev/.nvue/app.js
vendored
2
frontend/unpackage/dist/dev/.nvue/app.js
vendored
@@ -1,2 +0,0 @@
|
||||
Promise.resolve("./app.css.js").then(() => {
|
||||
});
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"app.js","sources":["App.vue","main.js"],"sourcesContent":["<script>\r\n\texport default {\r\n\t\tonLaunch: function() {\r\n\t\t\tconsole.log('App Launch')\r\n\t\t},\r\n\t\tonShow: function() {\r\n\t\t\tconsole.log('App Show')\r\n\t\t},\r\n\t\tonHide: function() {\r\n\t\t\tconsole.log('App Hide')\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t/*每个页面公共css */\r\n</style>\r\n","import App from './App'\r\n\r\n// #ifndef VUE3\r\nimport Vue from 'vue'\r\nimport './uni.promisify.adaptor'\r\nVue.config.productionTip = false\r\nApp.mpType = 'app'\r\nconst app = new Vue({\r\n ...App\r\n})\r\napp.$mount()\r\n// #endif\r\n\r\n// #ifdef VUE3\r\nimport { createSSRApp } from 'vue'\r\nexport function createApp() {\r\n const app = createSSRApp(App)\r\n return {\r\n app\r\n }\r\n}\r\n// #endif"],"names":["uni","createSSRApp","App"],"mappings":";;;;;;;;;;;;;;;;AACC,MAAK,YAAU;AAAA,EACd,UAAU,WAAW;AACpBA,kBAAAA,MAAA,MAAA,OAAA,gBAAY,YAAY;AAAA,EACxB;AAAA,EACD,QAAQ,WAAW;AAClBA,kBAAAA,MAAY,MAAA,OAAA,gBAAA,UAAU;AAAA,EACtB;AAAA,EACD,QAAQ,WAAW;AAClBA,kBAAAA,MAAY,MAAA,OAAA,iBAAA,UAAU;AAAA,EACvB;AACD;ACIM,SAAS,YAAY;AAC1B,QAAM,MAAMC,cAAY,aAACC,SAAG;AAC5B,SAAO;AAAA,IACL;AAAA,EACD;AACH;;;"}
|
||||
{"version":3,"file":"app.js","sources":["App.vue","main.js"],"sourcesContent":["<script>\r\n\texport default {\r\n\t\tonLaunch: function() {\r\n\t\t\tconsole.log('App Launch')\r\n\t\t},\r\n\t\tonShow: function() {\r\n\t\t\tconsole.log('App Show')\r\n\t\t},\r\n\t\tonHide: function() {\r\n\t\t\tconsole.log('App Hide')\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t/*每个页面公共css */\r\n</style>\r\n","import App from './App'\r\n\r\n// #ifndef VUE3\r\nimport Vue from 'vue'\r\nimport './uni.promisify.adaptor'\r\nVue.config.productionTip = false\r\nApp.mpType = 'app'\r\nconst app = new Vue({\r\n ...App\r\n})\r\napp.$mount()\r\n// #endif\r\n\r\n// #ifdef VUE3\r\nimport { createSSRApp } from 'vue'\r\nexport function createApp() {\r\n const app = createSSRApp(App)\r\n return {\r\n app\r\n }\r\n}\r\n// #endif\r\n\r\n// 规范化 WebSocket 关闭码(仅微信小程序)\r\n// #ifdef MP-WEIXIN\r\nif (typeof uni !== 'undefined' && typeof uni.connectSocket === 'function') {\r\n const _connectSocket = uni.connectSocket\r\n uni.connectSocket = function(options) {\r\n const task = _connectSocket.call(this, options)\r\n if (task && typeof task.close === 'function') {\r\n const _close = task.close\r\n task.close = function(params = {}) {\r\n if (params && typeof params === 'object') {\r\n const codeNum = Number(params.code)\r\n const isValid = codeNum === 1000 || (codeNum >= 3000 && codeNum <= 4999)\r\n if (!isValid) {\r\n params.code = 1000\r\n if (!params.reason) params.reason = 'normalized from invalid close code'\r\n }\r\n }\r\n return _close.call(this, params)\r\n }\r\n }\r\n return task\r\n }\r\n}\r\n// #endif"],"names":["uni","createSSRApp","App"],"mappings":";;;;;;;;;;;;;;;;;AACC,MAAK,YAAU;AAAA,EACd,UAAU,WAAW;AACpBA,kBAAAA,MAAA,MAAA,OAAA,gBAAY,YAAY;AAAA,EACxB;AAAA,EACD,QAAQ,WAAW;AAClBA,kBAAAA,MAAY,MAAA,OAAA,gBAAA,UAAU;AAAA,EACtB;AAAA,EACD,QAAQ,WAAW;AAClBA,kBAAAA,MAAY,MAAA,OAAA,iBAAA,UAAU;AAAA,EACvB;AACD;ACIM,SAAS,YAAY;AAC1B,QAAM,MAAMC,cAAY,aAACC,SAAG;AAC5B,SAAO;AAAA,IACL;AAAA,EACD;AACH;AAKA,IAAI,OAAOF,cAAG,UAAK,eAAe,OAAOA,cAAAA,MAAI,kBAAkB,YAAY;AACzE,QAAM,iBAAiBA,cAAAA,MAAI;AAC3BA,sBAAI,gBAAgB,SAAS,SAAS;AACpC,UAAM,OAAO,eAAe,KAAK,MAAM,OAAO;AAC9C,QAAI,QAAQ,OAAO,KAAK,UAAU,YAAY;AAC5C,YAAM,SAAS,KAAK;AACpB,WAAK,QAAQ,SAAS,SAAS,IAAI;AACjC,YAAI,UAAU,OAAO,WAAW,UAAU;AACxC,gBAAM,UAAU,OAAO,OAAO,IAAI;AAClC,gBAAM,UAAU,YAAY,OAAS,WAAW,OAAQ,WAAW;AACnE,cAAI,CAAC,SAAS;AACZ,mBAAO,OAAO;AACd,gBAAI,CAAC,OAAO;AAAQ,qBAAO,SAAS;AAAA,UACrC;AAAA,QACF;AACD,eAAO,OAAO,KAAK,MAAM,MAAM;AAAA,MAChC;AAAA,IACF;AACD,WAAO;AAAA,EACR;AACH;;;"}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":3,"file":"categories.js","sources":["pages/product/categories.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC9jYXRlZ29yaWVzLnZ1ZQ"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"toolbar\">\r\n\t\t\t<input v-model.trim=\"name\" placeholder=\"新类别名称\" />\r\n\t\t\t<button size=\"mini\" @click=\"create\">新增</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"c in list\" :key=\"c.id\">\r\n\t\t\t\t<input v-model.trim=\"c.name\" />\r\n\t\t\t\t<view class=\"ops\">\r\n\t\t\t\t\t<button size=\"mini\" @click=\"update(c)\">保存</button>\r\n\t\t\t\t\t<button size=\"mini\" type=\"warn\" @click=\"remove(c)\">删除</button>\r\n\t\t\t\t</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { get, post, put, del } from '../../common/http.js'\r\n\r\nexport default {\r\n\tdata() {\r\n\t\treturn { name: '', list: [] }\r\n\t},\r\n\tonLoad() { this.reload() },\r\n\tmethods: {\r\n\t\tasync reload() {\r\n\t\t\ttry {\r\n\t\t\t\tconst res = await get('/api/product-categories')\r\n\t\t\t\tthis.list = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t} catch (_) {}\r\n\t\t},\r\n\t\tasync create() {\r\n\t\t\tif (!this.name) return\r\n\t\t\tawait post('/api/product-categories', { name: this.name })\r\n\t\t\tthis.name = ''\r\n\t\t\tthis.reload()\r\n\t\t},\r\n\t\tasync update(c) {\r\n\t\t\tawait put('/api/product-categories/' + c.id, { name: c.name })\r\n\t\t\tuni.showToast({ title: '已保存', icon: 'success' })\r\n\t\t},\r\n\t\tasync remove(c) {\r\n\t\t\tuni.showModal({ content: '确定删除该类别?', success: async (r) => {\r\n\t\t\t\tif (!r.confirm) return\r\n\t\t\t\tawait del('/api/product-categories/' + c.id)\r\n\t\t\t\tthis.reload()\r\n\t\t\t}})\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { display:flex; flex-direction: column; height: 100vh; }\r\n.toolbar { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n.toolbar input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n.list { flex:1; }\r\n.item { display:flex; gap: 12rpx; align-items:center; padding: 16rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n.item input { flex:1; background:#f7f7f7; border-radius: 10rpx; padding: 12rpx; }\r\n.ops { display:flex; gap: 10rpx; }\r\n</style>\r\n\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/categories.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","post","put","uni","del"],"mappings":";;;AAqBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO,EAAE,MAAM,IAAI,MAAM,CAAA,EAAG;AAAA,EAC5B;AAAA,EACD,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,yBAAyB;AAC/C,aAAK,OAAO,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eACtE,GAAG;AAAA,MAAC;AAAA,IACb;AAAA,IACD,MAAM,SAAS;AACd,UAAI,CAAC,KAAK;AAAM;AAChB,YAAMC,YAAAA,KAAK,2BAA2B,EAAE,MAAM,KAAK,KAAG,CAAG;AACzD,WAAK,OAAO;AACZ,WAAK,OAAO;AAAA,IACZ;AAAA,IACD,MAAM,OAAO,GAAG;AACf,YAAMC,YAAG,IAAC,6BAA6B,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM;AAC7DC,oBAAG,MAAC,UAAU,EAAE,OAAO,OAAO,MAAM,WAAW;AAAA,IAC/C;AAAA,IACD,MAAM,OAAO,GAAG;AACfA,oBAAG,MAAC,UAAU,EAAE,SAAS,YAAY,SAAS,OAAO,MAAM;AAC1D,YAAI,CAAC,EAAE;AAAS;AAChB,cAAMC,gBAAI,6BAA6B,EAAE,EAAE;AAC3C,aAAK,OAAO;AAAA,MACb,EAAC,CAAC;AAAA,IACH;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;AClDA,GAAG,WAAW,eAAe;"}
|
||||
{"version":3,"file":"categories.js","sources":["pages/product/categories.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC9jYXRlZ29yaWVzLnZ1ZQ"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"toolbar\">\r\n\t\t\t<input v-model.trim=\"name\" placeholder=\"新类别名称\" />\r\n\t\t\t<button size=\"mini\" @click=\"create\">新增</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"c in list\" :key=\"c.id\">\r\n\t\t\t\t<input v-model.trim=\"c.name\" />\r\n\t\t\t\t<view class=\"ops\">\r\n\t\t\t\t\t<button size=\"mini\" @click=\"update(c)\">保存</button>\r\n\t\t\t\t\t<button size=\"mini\" type=\"warn\" @click=\"remove(c)\">删除</button>\r\n\t\t\t\t</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { get, post, put, del } from '../../common/http.js'\r\n\r\nexport default {\r\n\tdata() {\r\n\t\treturn { name: '', list: [] }\r\n\t},\r\n\tonLoad() { this.reload() },\r\n\tmethods: {\r\n\t\tasync reload() {\r\n\t\t\ttry {\r\n\t\t\t\tconst res = await get('/api/product-categories')\r\n\t\t\t\tthis.list = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t} catch (_) {}\r\n\t\t},\r\n\t\tasync create() {\r\n\t\t\tif (!this.name) return\r\n\t\t\tawait post('/api/product-categories', { name: this.name })\r\n\t\t\tthis.name = ''\r\n\t\t\tthis.reload()\r\n\t\t},\r\n\t\tasync update(c) {\r\n\t\t\tawait put('/api/product-categories/' + c.id, { name: c.name })\r\n\t\t\tuni.showToast({ title: '已保存', icon: 'success' })\r\n\t\t},\r\n\t\tasync remove(c) {\r\n\t\t\tuni.showModal({ content: '确定删除该类别?', success: async (r) => {\r\n\t\t\t\tif (!r.confirm) return\r\n\t\t\t\tawait del('/api/product-categories/' + c.id)\r\n\t\t\t\tthis.reload()\r\n\t\t\t}})\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { display:flex; flex-direction: column; height: 100vh; }\r\n.toolbar { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n.toolbar input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n.list { flex:1; }\r\n.item { display:flex; gap: 12rpx; align-items:center; padding: 16rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n.item input { flex:1; background:#f7f7f7; border-radius: 10rpx; padding: 12rpx; }\r\n.ops { display:flex; gap: 10rpx; }\r\n</style>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/categories.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","post","put","uni","del"],"mappings":";;;AAqBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO,EAAE,MAAM,IAAI,MAAM,CAAA,EAAG;AAAA,EAC5B;AAAA,EACD,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,yBAAyB;AAC/C,aAAK,OAAO,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eACtE,GAAG;AAAA,MAAC;AAAA,IACb;AAAA,IACD,MAAM,SAAS;AACd,UAAI,CAAC,KAAK;AAAM;AAChB,YAAMC,YAAAA,KAAK,2BAA2B,EAAE,MAAM,KAAK,KAAG,CAAG;AACzD,WAAK,OAAO;AACZ,WAAK,OAAO;AAAA,IACZ;AAAA,IACD,MAAM,OAAO,GAAG;AACf,YAAMC,YAAG,IAAC,6BAA6B,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM;AAC7DC,oBAAG,MAAC,UAAU,EAAE,OAAO,OAAO,MAAM,WAAW;AAAA,IAC/C;AAAA,IACD,MAAM,OAAO,GAAG;AACfA,oBAAG,MAAC,UAAU,EAAE,SAAS,YAAY,SAAS,OAAO,MAAM;AAC1D,YAAI,CAAC,EAAE;AAAS;AAChB,cAAMC,gBAAI,6BAA6B,EAAE,EAAE;AAC3C,aAAK,OAAO;AAAA,MACb,EAAC,CAAC;AAAA,IACH;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;AClDA,GAAG,WAAW,eAAe;"}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":3,"file":"select.js","sources":["pages/product/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC9zZWxlY3QudnVl"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"search\">\r\n\t\t\t<input v-model=\"kw\" placeholder=\"搜索商品名称/编码\" @confirm=\"search\" />\r\n\t\t\t<button size=\"mini\" @click=\"search\">搜索</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"p in products\" :key=\"p.id\" @click=\"select(p)\">\r\n\t\t\t\t<view class=\"name\">{{ p.name }}</view>\r\n\t\t\t\t<view class=\"meta\">{{ p.code }} · 库存:{{ p.stock || 0 }}</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\n\timport { get } from '../../common/http.js'\r\n\texport default {\r\n\t\tdata() { return { kw: '', products: [] } },\r\n\t\tonLoad() { this.search() },\r\n\t\tmethods: {\r\n\t\t\tasync search() {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst res = await get('/api/products', { kw: this.kw, page: 1, size: 50 })\r\n\t\t\t\t\tthis.products = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t\t} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }\r\n\t\t\t},\r\n\t\t\tselect(p) {\r\n\t\t\t\tconst opener = getCurrentPages()[getCurrentPages().length-2]\r\n\t\t\t\tif (opener && opener.$vm && opener.$vm.items) {\r\n\t\t\t\t\topener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: Number(p.price || 0) })\r\n\t\t\t\t}\r\n\t\t\t\tuni.navigateBack()\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t.page { display:flex; flex-direction: column; height: 100vh; }\r\n\t.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n\t.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n\t.list { flex:1; }\r\n\t.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n\t.name { color:#333; margin-bottom: 6rpx; }\r\n\t.meta { color:#888; font-size: 24rpx; }\r\n</style>\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAiBC,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,UAAU,CAAA;EAAM;AAAA,EAC1C,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,gBAAI,iBAAiB,EAAE,IAAI,KAAK,IAAI,MAAM,GAAG,MAAM,GAAC,CAAG;AACzE,aAAK,WAAW,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC3E,GAAG;AAAEC,sBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,MAAE;AAAA,IAC5D;AAAA,IACD,OAAO,GAAG;AACT,YAAM,SAAS,gBAAiB,EAAC,gBAAe,EAAG,SAAO,CAAC;AAC3D,UAAI,UAAU,OAAO,OAAO,OAAO,IAAI,OAAO;AAC7C,eAAO,IAAI,MAAM,KAAK,EAAE,WAAW,EAAE,IAAI,aAAa,EAAE,MAAM,UAAU,GAAG,WAAW,OAAO,EAAE,SAAS,CAAC,GAAG;AAAA,MAC7G;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;AClCD,GAAG,WAAW,eAAe;"}
|
||||
{"version":3,"file":"select.js","sources":["pages/product/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC9zZWxlY3QudnVl"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"search\">\r\n\t\t\t<input v-model=\"kw\" placeholder=\"搜索商品名称/编码\" @confirm=\"search\" />\r\n\t\t\t<button size=\"mini\" @click=\"search\">搜索</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"p in products\" :key=\"p.id\" @click=\"select(p)\">\r\n\t\t\t\t<view class=\"name\">{{ p.name }}</view>\r\n\t\t\t\t<view class=\"meta\">{{ (p.brand||'') + ' ' + (p.model||'') + ' ' + (p.spec||'') }} · 库存:{{ p.stock ?? 0 }}</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\n\timport { get } from '../../common/http.js'\r\n\texport default {\r\n\t\tdata() { return { kw: '', products: [] } },\r\n\t\tonLoad() { this.search() },\r\n\t\tmethods: {\r\n\t\t\tasync search() {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst res = await get('/api/products', { kw: this.kw, page: 1, size: 50 })\r\n\t\t\t\t\tthis.products = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t\t} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }\r\n\t\t\t},\r\n\t\t\tselect(p) {\r\n\t\t\t\tconst opener = getCurrentPages()[getCurrentPages().length-2]\r\n\t\t\t\tif (opener && opener.$vm && opener.$vm.items) {\r\n\t\t\t\t\topener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: Number(p.price || 0) })\r\n\t\t\t\t}\r\n\t\t\t\tuni.navigateBack()\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t.page { display:flex; flex-direction: column; height: 100vh; }\r\n\t.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n\t.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n\t.list { flex:1; }\r\n\t.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n\t.name { color:#333; margin-bottom: 6rpx; }\r\n\t.meta { color:#888; font-size: 24rpx; }\r\n</style>\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAiBC,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,UAAU,CAAA;EAAM;AAAA,EAC1C,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,gBAAI,iBAAiB,EAAE,IAAI,KAAK,IAAI,MAAM,GAAG,MAAM,GAAC,CAAG;AACzE,aAAK,WAAW,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC3E,GAAG;AAAEC,sBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,MAAE;AAAA,IAC5D;AAAA,IACD,OAAO,GAAG;AACT,YAAM,SAAS,gBAAiB,EAAC,gBAAe,EAAG,SAAO,CAAC;AAC3D,UAAI,UAAU,OAAO,OAAO,OAAO,IAAI,OAAO;AAC7C,eAAO,IAAI,MAAM,KAAK,EAAE,WAAW,EAAE,IAAI,aAAa,EAAE,MAAM,UAAU,GAAG,WAAW,OAAO,EAAE,SAAS,CAAC,GAAG;AAAA,MAC7G;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;AClCD,GAAG,WAAW,eAAe;"}
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.js","sources":["pages/product/settings.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC9zZXR0aW5ncy52dWU"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"item\">\r\n\t\t\t<text>隐藏零库存商品</text>\r\n\t\t\t<switch :checked=\"settings.hideZeroStock\" @change=\"(e)=>update('hideZeroStock', e.detail.value)\" />\r\n\t\t</view>\r\n\t\t<view class=\"item\">\r\n\t\t\t<text>隐藏进货价</text>\r\n\t\t\t<switch :checked=\"settings.hidePurchasePrice\" @change=\"(e)=>update('hidePurchasePrice', e.detail.value)\" />\r\n\t\t</view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { get, put } from '../../common/http.js'\r\n\r\nexport default {\r\n\tdata() {\r\n\t\treturn { settings: { hideZeroStock: false, hidePurchasePrice: false } }\r\n\t},\r\n\tonLoad() { this.load() },\r\n\tmethods: {\r\n\t\tasync load() {\r\n\t\t\ttry {\r\n\t\t\t\tconst res = await get('/api/product-settings')\r\n\t\t\t\tthis.settings = { hideZeroStock: !!res?.hideZeroStock, hidePurchasePrice: !!res?.hidePurchasePrice }\r\n\t\t\t} catch (_) {}\r\n\t\t},\r\n\t\tasync update(key, val) {\r\n\t\t\tconst next = { ...this.settings, [key]: val }\r\n\t\t\tthis.settings = next\r\n\t\t\ttry { await put('/api/product-settings', next) } catch (_) {}\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { background:#fff; }\r\n.item { display:flex; justify-content: space-between; align-items:center; padding: 20rpx; border-bottom: 1rpx solid #f1f1f1; }\r\n</style>\r\n\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/settings.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","put"],"mappings":";;;AAgBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO,EAAE,UAAU,EAAE,eAAe,OAAO,mBAAmB,QAAQ;AAAA,EACtE;AAAA,EACD,SAAS;AAAE,SAAK;EAAQ;AAAA,EACxB,SAAS;AAAA,IACR,MAAM,OAAO;AACZ,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,uBAAuB;AAC7C,aAAK,WAAW,EAAE,eAAe,CAAC,EAAC,2BAAK,gBAAe,mBAAmB,CAAC,EAAC,2BAAK,mBAAkB;AAAA,eAC3F,GAAG;AAAA,MAAC;AAAA,IACb;AAAA,IACD,MAAM,OAAO,KAAK,KAAK;AACtB,YAAM,OAAO,EAAE,GAAG,KAAK,UAAU,CAAC,GAAG,GAAG,IAAI;AAC5C,WAAK,WAAW;AAChB,UAAI;AAAE,cAAMC,YAAAA,IAAI,yBAAyB,IAAI;AAAA,MAAI,SAAO,GAAG;AAAA,MAAC;AAAA,IAC7D;AAAA,EACD;AACD;;;;;;;;;;ACjCA,GAAG,WAAW,eAAe;"}
|
||||
{"version":3,"file":"settings.js","sources":["pages/product/settings.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC9zZXR0aW5ncy52dWU"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"item\">\r\n\t\t\t<text>隐藏零库存商品</text>\r\n\t\t\t<switch :checked=\"settings.hideZeroStock\" @change=\"(e)=>update('hideZeroStock', e.detail.value)\" />\r\n\t\t</view>\r\n\t\t<view class=\"item\">\r\n\t\t\t<text>隐藏进货价</text>\r\n\t\t\t<switch :checked=\"settings.hidePurchasePrice\" @change=\"(e)=>update('hidePurchasePrice', e.detail.value)\" />\r\n\t\t</view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { get, put } from '../../common/http.js'\r\n\r\nexport default {\r\n\tdata() {\r\n\t\treturn { settings: { hideZeroStock: false, hidePurchasePrice: false } }\r\n\t},\r\n\tonLoad() { this.load() },\r\n\tmethods: {\r\n\t\tasync load() {\r\n\t\t\ttry {\r\n\t\t\t\tconst res = await get('/api/product-settings')\r\n\t\t\t\tthis.settings = { hideZeroStock: !!res?.hideZeroStock, hidePurchasePrice: !!res?.hidePurchasePrice }\r\n\t\t\t} catch (_) {}\r\n\t\t},\r\n\t\tasync update(key, val) {\r\n\t\t\tconst next = { ...this.settings, [key]: val }\r\n\t\t\tthis.settings = next\r\n\t\t\ttry { await put('/api/product-settings', next) } catch (_) {}\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { background:#fff; }\r\n.item { display:flex; justify-content: space-between; align-items:center; padding: 20rpx; border-bottom: 1rpx solid #f1f1f1; }\r\n</style>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/settings.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","put"],"mappings":";;;AAgBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO,EAAE,UAAU,EAAE,eAAe,OAAO,mBAAmB,QAAQ;AAAA,EACtE;AAAA,EACD,SAAS;AAAE,SAAK;EAAQ;AAAA,EACxB,SAAS;AAAA,IACR,MAAM,OAAO;AACZ,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,uBAAuB;AAC7C,aAAK,WAAW,EAAE,eAAe,CAAC,EAAC,2BAAK,gBAAe,mBAAmB,CAAC,EAAC,2BAAK,mBAAkB;AAAA,eAC3F,GAAG;AAAA,MAAC;AAAA,IACb;AAAA,IACD,MAAM,OAAO,KAAK,KAAK;AACtB,YAAM,OAAO,EAAE,GAAG,KAAK,UAAU,CAAC,GAAG,GAAG,IAAI;AAC5C,WAAK,WAAW;AAChB,UAAI;AAAE,cAAMC,YAAAA,IAAI,yBAAyB,IAAI;AAAA,MAAI,SAAO,GAAG;AAAA,MAAC;AAAA,IAC7D;AAAA,EACD;AACD;;;;;;;;;;ACjCA,GAAG,WAAW,eAAe;"}
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"units.js","sources":["pages/product/units.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC91bml0cy52dWU"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"toolbar\">\r\n\t\t\t<input v-model.trim=\"name\" placeholder=\"新单位名称\" />\r\n\t\t\t<button size=\"mini\" @click=\"create\">新增</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"u in list\" :key=\"u.id\">\r\n\t\t\t\t<input v-model.trim=\"u.name\" />\r\n\t\t\t\t<view class=\"ops\">\r\n\t\t\t\t\t<button size=\"mini\" @click=\"update(u)\">保存</button>\r\n\t\t\t\t\t<button size=\"mini\" type=\"warn\" @click=\"remove(u)\">删除</button>\r\n\t\t\t\t</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { get, post, put, del } from '../../common/http.js'\r\n\r\nexport default {\r\n\tdata() {\r\n\t\treturn { name: '', list: [] }\r\n\t},\r\n\tonLoad() { this.reload() },\r\n\tmethods: {\r\n\t\tasync reload() {\r\n\t\t\ttry {\r\n\t\t\t\tconst res = await get('/api/product-units')\r\n\t\t\t\tthis.list = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t} catch (_) {}\r\n\t\t},\r\n\t\tasync create() {\r\n\t\t\tif (!this.name) return\r\n\t\t\tawait post('/api/product-units', { name: this.name })\r\n\t\t\tthis.name = ''\r\n\t\t\tthis.reload()\r\n\t\t},\r\n\t\tasync update(u) {\r\n\t\t\tawait put('/api/product-units/' + u.id, { name: u.name })\r\n\t\t\tuni.showToast({ title: '已保存', icon: 'success' })\r\n\t\t},\r\n\t\tasync remove(u) {\r\n\t\t\tuni.showModal({ content: '确定删除该单位?', success: async (r) => {\r\n\t\t\t\tif (!r.confirm) return\r\n\t\t\t\tawait del('/api/product-units/' + u.id)\r\n\t\t\t\tthis.reload()\r\n\t\t\t}})\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { display:flex; flex-direction: column; height: 100vh; }\r\n.toolbar { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n.toolbar input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n.list { flex:1; }\r\n.item { display:flex; gap: 12rpx; align-items:center; padding: 16rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n.item input { flex:1; background:#f7f7f7; border-radius: 10rpx; padding: 12rpx; }\r\n.ops { display:flex; gap: 10rpx; }\r\n</style>\r\n\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/units.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","post","put","uni","del"],"mappings":";;;AAqBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO,EAAE,MAAM,IAAI,MAAM,CAAA,EAAG;AAAA,EAC5B;AAAA,EACD,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,oBAAoB;AAC1C,aAAK,OAAO,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eACtE,GAAG;AAAA,MAAC;AAAA,IACb;AAAA,IACD,MAAM,SAAS;AACd,UAAI,CAAC,KAAK;AAAM;AAChB,YAAMC,YAAAA,KAAK,sBAAsB,EAAE,MAAM,KAAK,KAAG,CAAG;AACpD,WAAK,OAAO;AACZ,WAAK,OAAO;AAAA,IACZ;AAAA,IACD,MAAM,OAAO,GAAG;AACf,YAAMC,YAAG,IAAC,wBAAwB,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM;AACxDC,oBAAG,MAAC,UAAU,EAAE,OAAO,OAAO,MAAM,WAAW;AAAA,IAC/C;AAAA,IACD,MAAM,OAAO,GAAG;AACfA,oBAAG,MAAC,UAAU,EAAE,SAAS,YAAY,SAAS,OAAO,MAAM;AAC1D,YAAI,CAAC,EAAE;AAAS;AAChB,cAAMC,gBAAI,wBAAwB,EAAE,EAAE;AACtC,aAAK,OAAO;AAAA,MACb,EAAC,CAAC;AAAA,IACH;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;AClDA,GAAG,WAAW,eAAe;"}
|
||||
{"version":3,"file":"units.js","sources":["pages/product/units.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvcHJvZHVjdC91bml0cy52dWU"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"toolbar\">\r\n\t\t\t<input v-model.trim=\"name\" placeholder=\"新单位名称\" />\r\n\t\t\t<button size=\"mini\" @click=\"create\">新增</button>\r\n\t\t</view>\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"u in list\" :key=\"u.id\">\r\n\t\t\t\t<input v-model.trim=\"u.name\" />\r\n\t\t\t\t<view class=\"ops\">\r\n\t\t\t\t\t<button size=\"mini\" @click=\"update(u)\">保存</button>\r\n\t\t\t\t\t<button size=\"mini\" type=\"warn\" @click=\"remove(u)\">删除</button>\r\n\t\t\t\t</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { get, post, put, del } from '../../common/http.js'\r\n\r\nexport default {\r\n\tdata() {\r\n\t\treturn { name: '', list: [] }\r\n\t},\r\n\tonLoad() { this.reload() },\r\n\tmethods: {\r\n\t\tasync reload() {\r\n\t\t\ttry {\r\n\t\t\t\tconst res = await get('/api/product-units')\r\n\t\t\t\tthis.list = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])\r\n\t\t\t} catch (_) {}\r\n\t\t},\r\n\t\tasync create() {\r\n\t\t\tif (!this.name) return\r\n\t\t\tawait post('/api/product-units', { name: this.name })\r\n\t\t\tthis.name = ''\r\n\t\t\tthis.reload()\r\n\t\t},\r\n\t\tasync update(u) {\r\n\t\t\tawait put('/api/product-units/' + u.id, { name: u.name })\r\n\t\t\tuni.showToast({ title: '已保存', icon: 'success' })\r\n\t\t},\r\n\t\tasync remove(u) {\r\n\t\t\tuni.showModal({ content: '确定删除该单位?', success: async (r) => {\r\n\t\t\t\tif (!r.confirm) return\r\n\t\t\t\tawait del('/api/product-units/' + u.id)\r\n\t\t\t\tthis.reload()\r\n\t\t\t}})\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { display:flex; flex-direction: column; height: 100vh; }\r\n.toolbar { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }\r\n.toolbar input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }\r\n.list { flex:1; }\r\n.item { display:flex; gap: 12rpx; align-items:center; padding: 16rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }\r\n.item input { flex:1; background:#f7f7f7; border-radius: 10rpx; padding: 12rpx; }\r\n.ops { display:flex; gap: 10rpx; }\r\n</style>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/product/units.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","post","put","uni","del"],"mappings":";;;AAqBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO,EAAE,MAAM,IAAI,MAAM,CAAA,EAAG;AAAA,EAC5B;AAAA,EACD,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,oBAAoB;AAC1C,aAAK,OAAO,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eACtE,GAAG;AAAA,MAAC;AAAA,IACb;AAAA,IACD,MAAM,SAAS;AACd,UAAI,CAAC,KAAK;AAAM;AAChB,YAAMC,YAAAA,KAAK,sBAAsB,EAAE,MAAM,KAAK,KAAG,CAAG;AACpD,WAAK,OAAO;AACZ,WAAK,OAAO;AAAA,IACZ;AAAA,IACD,MAAM,OAAO,GAAG;AACf,YAAMC,YAAG,IAAC,wBAAwB,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM;AACxDC,oBAAG,MAAC,UAAU,EAAE,OAAO,OAAO,MAAM,WAAW;AAAA,IAC/C;AAAA,IACD,MAAM,OAAO,GAAG;AACfA,oBAAG,MAAC,UAAU,EAAE,SAAS,YAAY,SAAS,OAAO,MAAM;AAC1D,YAAI,CAAC,EAAE;AAAS;AAChB,cAAMC,gBAAI,wBAAwB,EAAE,EAAE;AACtC,aAAK,OAAO;AAAA,MACb,EAAC,CAAC;AAAA,IACH;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;AClDA,GAAG,WAAW,eAAe;"}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,24 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>View</title>
|
||||
<link rel="icon" href="data:,">
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<script>var __uniConfig = {"globalStyle":{},"darkmode":false}</script>
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="uni-app-view.umd.js"></script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
;(function(){
|
||||
let u=void 0,isReady=false,onReadyCallbacks=[],isServiceReady=false,onServiceReadyCallbacks=[];
|
||||
const __uniConfig = {"pages":[],"globalStyle":{"backgroundColor":"#F8F8F8","navigationBar":{"backgroundColor":"#F8F8F8","titleText":"五金配件管家","type":"default","titleColor":"#000000"},"isNVue":false},"nvue":{"compiler":"uni-app","styleCompiler":"uni-app","flex-direction":"column"},"renderer":"auto","appname":"林林林","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":true},"compilerVersion":"4.76","entryPagePath":"pages/index/index","entryPageQuery":"","realEntryPagePath":"","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000},"locales":{},"darkmode":false,"themeConfig":{}};
|
||||
const __uniRoutes = [{"path":"pages/index/index","meta":{"isQuit":true,"isEntry":true,"navigationBar":{"titleText":"五金配件管家","type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute));
|
||||
__uniConfig.styles=[];//styles
|
||||
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
|
||||
__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
|
||||
service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:16})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,global:u,window:u,document:u,frames:u,self:u,location:u,navigator:u,localStorage:u,history:u,Caches:u,screen:u,alert:u,confirm:u,prompt:u,fetch:u,XMLHttpRequest:u,WebSocket:u,webkit:u,print:u}}}});
|
||||
})();
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
(function(){})();
|
||||
320
frontend/unpackage/dist/dev/app-plus/app-service.js
vendored
320
frontend/unpackage/dist/dev/app-plus/app-service.js
vendored
@@ -1,320 +0,0 @@
|
||||
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";
|
||||
const _imports_0 = "/static/metal-bg.jpg";
|
||||
const _export_sfc = (sfc, props) => {
|
||||
const target = sfc.__vccOpts || sfc;
|
||||
for (const [key, val] of props) {
|
||||
target[key] = val;
|
||||
}
|
||||
return target;
|
||||
};
|
||||
const _sfc_main$1 = {
|
||||
data() {
|
||||
return {
|
||||
todayAmount: "0.00",
|
||||
monthProfit: "0.00",
|
||||
stockQty: "0.00",
|
||||
activeTab: "home",
|
||||
notices: [
|
||||
{ text: "选材精工:不锈钢、合金钢,耐腐蚀更耐用", tag: "品质" },
|
||||
{ text: "表面工艺:电镀锌/镀镍/发黑处理,性能均衡", tag: "工艺" },
|
||||
{ text: "库存齐全:螺丝、螺母、垫圈、膨胀螺栓等现货", tag: "库存" },
|
||||
{ text: "企业采购支持:批量优惠,次日发货", tag: "服务" }
|
||||
],
|
||||
features: [
|
||||
{ key: "customer", title: "客户", img: "/static/icons/customer.png", emoji: "👥" },
|
||||
{ key: "sale", title: "销售", img: "/static/icons/sale.png", emoji: "💰" },
|
||||
{ key: "account", title: "账户", img: "/static/icons/account.png", emoji: "💳" },
|
||||
{ key: "supplier", title: "供应商", img: "/static/icons/supplier.png", emoji: "🚚" },
|
||||
{ key: "purchase", title: "进货", img: "/static/icons/purchase.png", emoji: "🛒" },
|
||||
{ key: "otherPay", title: "其他支出", img: "/static/icons/other-pay.png", emoji: "💸" },
|
||||
{ key: "vip", title: "VIP会员", img: "/static/icons/vip.png", emoji: "👑" },
|
||||
{ key: "report", title: "报表", img: "/static/icons/report.png", emoji: "📊" },
|
||||
{ key: "more", title: "更多", img: "/static/icons/more.png", emoji: "⋯" }
|
||||
]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onFeatureTap(item) {
|
||||
uni.showToast({ title: item.title + "(开发中)", icon: "none" });
|
||||
},
|
||||
onCreateOrder() {
|
||||
uni.showToast({ title: "开单(开发中)", icon: "none" });
|
||||
},
|
||||
onNoticeTap(n) {
|
||||
uni.showModal({
|
||||
title: "公告",
|
||||
content: n.text,
|
||||
showCancel: false
|
||||
});
|
||||
},
|
||||
onNoticeList() {
|
||||
uni.showToast({ title: "公告列表(开发中)", icon: "none" });
|
||||
},
|
||||
onIconError(item) {
|
||||
item.img = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
return vue.openBlock(), vue.createElementBlock("view", { class: "home" }, [
|
||||
vue.createElementVNode("image", {
|
||||
class: "home-bg",
|
||||
src: _imports_0,
|
||||
mode: "aspectFill"
|
||||
}),
|
||||
vue.createCommentVNode(" 顶部统计卡片 "),
|
||||
vue.createElementVNode("view", { class: "hero" }, [
|
||||
vue.createElementVNode("view", { class: "hero-top" }, [
|
||||
vue.createElementVNode("text", { class: "brand" }, "五金配件管家"),
|
||||
vue.createElementVNode("view", { class: "cta" }, [
|
||||
vue.createElementVNode("text", { class: "cta-text" }, "咨询")
|
||||
])
|
||||
]),
|
||||
vue.createElementVNode("view", { class: "kpi" }, [
|
||||
vue.createElementVNode("view", { class: "kpi-item" }, [
|
||||
vue.createElementVNode("text", { class: "kpi-label" }, "今日销售额"),
|
||||
vue.createElementVNode(
|
||||
"text",
|
||||
{ class: "kpi-value" },
|
||||
vue.toDisplayString($data.todayAmount),
|
||||
1
|
||||
/* TEXT */
|
||||
)
|
||||
]),
|
||||
vue.createElementVNode("view", { class: "kpi-item" }, [
|
||||
vue.createElementVNode("text", { class: "kpi-label" }, "本月利润"),
|
||||
vue.createElementVNode(
|
||||
"text",
|
||||
{ class: "kpi-value" },
|
||||
vue.toDisplayString($data.monthProfit),
|
||||
1
|
||||
/* TEXT */
|
||||
)
|
||||
]),
|
||||
vue.createElementVNode("view", { class: "kpi-item" }, [
|
||||
vue.createElementVNode("text", { class: "kpi-label" }, "库存数量"),
|
||||
vue.createElementVNode(
|
||||
"text",
|
||||
{ class: "kpi-value" },
|
||||
vue.toDisplayString($data.stockQty),
|
||||
1
|
||||
/* TEXT */
|
||||
)
|
||||
])
|
||||
])
|
||||
]),
|
||||
vue.createCommentVNode(" 公告栏:自动轮播,可点击查看详情 "),
|
||||
vue.createElementVNode("view", { class: "notice" }, [
|
||||
vue.createElementVNode("view", { class: "notice-left" }, "公告"),
|
||||
vue.createElementVNode("swiper", {
|
||||
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.createElementVNode("view", {
|
||||
class: "notice-right",
|
||||
onClick: _cache[0] || (_cache[0] = (...args) => $options.onNoticeList && $options.onNoticeList(...args))
|
||||
}, "更多")
|
||||
]),
|
||||
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: "grid" }, [
|
||||
(vue.openBlock(true), vue.createElementBlock(
|
||||
vue.Fragment,
|
||||
null,
|
||||
vue.renderList($data.features, (item) => {
|
||||
return vue.openBlock(), vue.createElementBlock("view", {
|
||||
class: "grid-item",
|
||||
key: item.key,
|
||||
onClick: ($event) => $options.onFeatureTap(item)
|
||||
}, [
|
||||
vue.createElementVNode("view", { class: "icon icon-squircle" }, [
|
||||
item.img ? (vue.openBlock(), vue.createElementBlock("image", {
|
||||
key: 0,
|
||||
src: item.img,
|
||||
class: "icon-img",
|
||||
mode: "aspectFit",
|
||||
onError: ($event) => $options.onIconError(item)
|
||||
}, null, 40, ["src", "onError"])) : item.emoji ? (vue.openBlock(), vue.createElementBlock(
|
||||
"text",
|
||||
{
|
||||
key: 1,
|
||||
class: "icon-emoji"
|
||||
},
|
||||
vue.toDisplayString(item.emoji),
|
||||
1
|
||||
/* TEXT */
|
||||
)) : (vue.openBlock(), vue.createElementBlock("view", {
|
||||
key: 2,
|
||||
class: "icon-placeholder"
|
||||
}))
|
||||
]),
|
||||
vue.createElementVNode(
|
||||
"text",
|
||||
{ class: "grid-chip" },
|
||||
vue.toDisplayString(item.title),
|
||||
1
|
||||
/* TEXT */
|
||||
)
|
||||
], 8, ["onClick"]);
|
||||
}),
|
||||
128
|
||||
/* KEYED_FRAGMENT */
|
||||
))
|
||||
])
|
||||
]),
|
||||
vue.createCommentVNode(" 底部操作条 "),
|
||||
vue.createElementVNode("view", { class: "bottom-bar" }, [
|
||||
vue.createElementVNode(
|
||||
"view",
|
||||
{
|
||||
class: vue.normalizeClass(["tab", { active: $data.activeTab === "home" }]),
|
||||
onClick: _cache[1] || (_cache[1] = ($event) => $data.activeTab = "home")
|
||||
},
|
||||
[
|
||||
vue.createElementVNode("text", null, "首页")
|
||||
],
|
||||
2
|
||||
/* CLASS */
|
||||
),
|
||||
vue.createElementVNode("view", {
|
||||
class: "tab primary",
|
||||
onClick: _cache[2] || (_cache[2] = (...args) => $options.onCreateOrder && $options.onCreateOrder(...args))
|
||||
}, [
|
||||
vue.createElementVNode("text", null, "开单")
|
||||
]),
|
||||
vue.createElementVNode(
|
||||
"view",
|
||||
{
|
||||
class: vue.normalizeClass(["tab", { active: $data.activeTab === "detail" }]),
|
||||
onClick: _cache[3] || (_cache[3] = ($event) => $data.activeTab = "detail")
|
||||
},
|
||||
[
|
||||
vue.createElementVNode("text", null, "明细")
|
||||
],
|
||||
2
|
||||
/* CLASS */
|
||||
),
|
||||
vue.createElementVNode(
|
||||
"view",
|
||||
{
|
||||
class: vue.normalizeClass(["tab", { active: $data.activeTab === "me" }]),
|
||||
onClick: _cache[4] || (_cache[4] = ($event) => $data.activeTab = "me")
|
||||
},
|
||||
[
|
||||
vue.createElementVNode("text", null, "我的")
|
||||
],
|
||||
2
|
||||
/* CLASS */
|
||||
)
|
||||
])
|
||||
]);
|
||||
}
|
||||
const PagesIndexIndex = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__file", "D:/wx/PartsInquiry/frontend/pages/index/index.vue"]]);
|
||||
__definePage("pages/index/index", PagesIndexIndex);
|
||||
function formatAppLog(type, filename, ...args) {
|
||||
if (uni.__log__) {
|
||||
uni.__log__(type, filename, ...args);
|
||||
} else {
|
||||
console[type].apply(console, [...args, filename]);
|
||||
}
|
||||
}
|
||||
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", "D:/wx/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);
|
||||
4
frontend/unpackage/dist/dev/app-plus/app.css
vendored
4
frontend/unpackage/dist/dev/app-plus/app.css
vendored
File diff suppressed because one or more lines are too long
107
frontend/unpackage/dist/dev/app-plus/manifest.json
vendored
107
frontend/unpackage/dist/dev/app-plus/manifest.json
vendored
@@ -1,107 +0,0 @@
|
||||
{
|
||||
"@platforms": [
|
||||
"android",
|
||||
"iPhone",
|
||||
"iPad"
|
||||
],
|
||||
"id": "",
|
||||
"name": "林林林",
|
||||
"version": {
|
||||
"name": "1.0.0",
|
||||
"code": "100"
|
||||
},
|
||||
"description": "",
|
||||
"developer": {
|
||||
"name": "",
|
||||
"email": "",
|
||||
"url": ""
|
||||
},
|
||||
"permissions": {
|
||||
"UniNView": {
|
||||
"description": "UniNView原生渲染"
|
||||
}
|
||||
},
|
||||
"plus": {
|
||||
"useragent": {
|
||||
"value": "uni-app",
|
||||
"concatenate": true
|
||||
},
|
||||
"splashscreen": {
|
||||
"target": "id:1",
|
||||
"autoclose": true,
|
||||
"waiting": true,
|
||||
"delay": 0
|
||||
},
|
||||
"popGesture": "close",
|
||||
"launchwebview": {
|
||||
"render": "always",
|
||||
"id": "1",
|
||||
"kernel": "WKWebview"
|
||||
},
|
||||
"usingComponents": true,
|
||||
"nvueStyleCompiler": "uni-app",
|
||||
"compilerVersion": 3,
|
||||
"distribute": {
|
||||
"google": {
|
||||
"permissions": [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
"apple": {},
|
||||
"plugins": {
|
||||
"audio": {
|
||||
"mp3": {
|
||||
"description": "Android平台录音支持MP3格式文件"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"statusbar": {
|
||||
"immersed": "supportedDevice",
|
||||
"style": "dark",
|
||||
"background": "#F8F8F8"
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
},
|
||||
"allowsInlineMediaPlayback": true,
|
||||
"uni-app": {
|
||||
"control": "uni-v3",
|
||||
"vueVersion": "3",
|
||||
"compilerVersion": "4.76",
|
||||
"nvueCompiler": "uni-app",
|
||||
"renderer": "auto",
|
||||
"nvue": {
|
||||
"flex-direction": "column"
|
||||
},
|
||||
"nvueLaunchMode": "normal",
|
||||
"webView": {
|
||||
"minUserAgentVersion": "49.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"app-harmony": {
|
||||
"useragent": {
|
||||
"value": "uni-app",
|
||||
"concatenate": true
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
}
|
||||
},
|
||||
"launch_path": "__uniappview.html"
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
|
||||
.home {
|
||||
padding-bottom: 4.375rem;
|
||||
position: relative;
|
||||
/* 明亮奢华背景:金属拉丝纹理覆盖层 + 柔和浅色渐变 */
|
||||
background:
|
||||
repeating-linear-gradient(0deg, rgba(180,180,180,0.12) 0, rgba(180,180,180,0.12) 0.0625rem, rgba(255,255,255,0.0) 0.0625rem, rgba(255,255,255,0.0) 0.3125rem),
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.75) 0%, rgba(255, 255, 255, 0.55) 40%, rgba(255, 255, 255, 0.35) 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
.home-bg {
|
||||
position: fixed;
|
||||
left: 0; top: 0; right: 0; bottom: 0;
|
||||
width: 100%; height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* 公告栏 */
|
||||
.notice {
|
||||
margin: 0 0.75rem 0.75rem;
|
||||
padding: 0.625rem 0.6875rem;
|
||||
border-radius: 0.625rem;
|
||||
background: rgba(255,255,255,0.78);
|
||||
-webkit-backdrop-filter: blur(0.375rem);
|
||||
backdrop-filter: blur(0.375rem);
|
||||
border: 0.0625rem solid rgba(203, 166, 61, 0.28);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.notice-left {
|
||||
flex: 0 0 auto;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
min-width: 3rem; height: 1.375rem;
|
||||
padding: 0 0.5rem;
|
||||
border-radius: 31.21875rem;
|
||||
background: linear-gradient(135deg, #FFE69A, #F4CF62);
|
||||
color: #3f320f;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
.notice-swiper { height: 2.25rem; flex: 1;
|
||||
}
|
||||
.notice-item { display: flex; align-items: center; gap: 0.375rem; min-height: 2.25rem;
|
||||
}
|
||||
.notice-text { color: #4b3e19; font-size: 0.875rem; line-height: 1.125rem; font-weight: 600; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
||||
}
|
||||
.notice-tag { color: #B4880F; font-size: 0.6875rem; padding: 0.125rem 0.3125rem; border-radius: 31.21875rem; background: rgba(215,167,46,0.18);
|
||||
}
|
||||
.notice-right { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; min-width: 2.25rem; height: 1.375rem; color: #B4880F; font-size: 0.8125rem; padding-left: 0.25rem;
|
||||
}
|
||||
|
||||
/* 分割标题 */
|
||||
.section-title { display: flex; align-items: center; gap: 0.5rem; padding: 0.3125rem 0.875rem 0;
|
||||
}
|
||||
.section-title::before { content: ''; display: block; width: 0.25rem; height: 0.875rem; border-radius: 0.25rem; background: linear-gradient(180deg, #FFE69A, #D7A72E);
|
||||
}
|
||||
.section-text { color: #6b5a2a; font-size: 0.875rem; font-weight: 700; letter-spacing: 0.03125rem;
|
||||
}
|
||||
|
||||
/* 顶部英雄区:浅色玻璃卡片,带金色描边与柔和阴影 */
|
||||
.hero {
|
||||
margin: 0.75rem;
|
||||
padding: 1rem;
|
||||
border-radius: 0.875rem;
|
||||
background: rgba(255, 255, 255, 0.65);
|
||||
-webkit-backdrop-filter: blur(0.4375rem);
|
||||
backdrop-filter: blur(0.4375rem);
|
||||
border: 0.0625rem solid rgba(203, 166, 61, 0.35);
|
||||
box-shadow: 0 0.375rem 0.875rem rgba(0, 0, 0, 0.10), 0 0 0 0.0625rem rgba(255,255,255,0.60) inset;
|
||||
color: #473c22;
|
||||
}
|
||||
.hero-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.625rem;
|
||||
}
|
||||
.brand {
|
||||
font-size: 1.3125rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.0625rem;
|
||||
color: #B4880F; /* 金色标题 */
|
||||
}
|
||||
.cta {
|
||||
padding: 0.3125rem 0.6875rem;
|
||||
border-radius: 31.21875rem;
|
||||
background: linear-gradient(135deg, rgba(255, 220, 128, 0.65), rgba(255, 240, 190, 0.65));
|
||||
border: 0.0625rem solid rgba(203, 166, 61, 0.45);
|
||||
box-shadow: 0 0.1875rem 0.4375rem rgba(203, 166, 61, 0.25);
|
||||
}
|
||||
.cta-text { color: #5a4712; font-size: 0.8125rem;
|
||||
}
|
||||
.kpi { display: flex;
|
||||
}
|
||||
.kpi-item { flex: 1;
|
||||
}
|
||||
.kpi-label { opacity: 0.9; font-size: 0.75rem; color: #6b5a2a;
|
||||
}
|
||||
.kpi-value { display: block; margin-top: 0.375rem; font-size: 1.4375rem; font-weight: 800; color: #B4880F;
|
||||
}
|
||||
|
||||
/* 功能容器:整体玻璃面板,增强融入感 */
|
||||
.grid-wrap {
|
||||
margin: 0 0.625rem 1rem;
|
||||
padding: 0.875rem 0.625rem 0.375rem;
|
||||
border-radius: 0.75rem;
|
||||
background: rgba(255,255,255,0.55);
|
||||
-webkit-backdrop-filter: blur(0.3125rem);
|
||||
backdrop-filter: blur(0.3125rem);
|
||||
border: 0.0625rem solid rgba(203,166,61,0.22);
|
||||
box-shadow: 0 0.25rem 0.5625rem rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
/* 功能九宫格 */
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-row-gap: 2rem;
|
||||
grid-column-gap: 1.125rem;
|
||||
padding: 1.25rem 0.875rem 0.875rem;
|
||||
}
|
||||
.grid-item { display: flex; flex-direction: column; align-items: center; text-align: center;
|
||||
}
|
||||
.icon { display: flex; align-items: center; justify-content: center; color: #6b5a2a; position: relative;
|
||||
}
|
||||
.icon-squircle {
|
||||
width: 4.125rem; height: 4.125rem;
|
||||
border-radius: 0.875rem;
|
||||
background: linear-gradient(145deg, rgba(255,255,255,0.92), rgba(255,255,255,0.70));
|
||||
-webkit-backdrop-filter: blur(0.375rem);
|
||||
backdrop-filter: blur(0.375rem);
|
||||
border: 0.0625rem solid rgba(203,166,61,0.22);
|
||||
box-shadow: 0 0.3125rem 0.75rem rgba(0,0,0,0.10), 0 0 0 0.0625rem rgba(255,255,255,0.65) inset;
|
||||
overflow: hidden;
|
||||
}
|
||||
.icon-squircle::before { content: ''; position: absolute; left: -30%; top: -40%; width: 160%; height: 70%; background: linear-gradient( to bottom, rgba(255,255,255,0.9), rgba(255,255,255,0.0) ); transform: rotate(12deg);
|
||||
}
|
||||
.icon-img { width: 3rem; height: 3rem;
|
||||
}
|
||||
.icon-emoji { font-size: 1.875rem; line-height: 1;
|
||||
}
|
||||
.icon-placeholder { width: 2.625rem; height: 2.625rem; border-radius: 0.5625rem; background:
|
||||
linear-gradient(135deg, rgba(212,175,55,0.18), rgba(255,255,255,0.0)),
|
||||
repeating-linear-gradient(90deg, rgba(180,150,60,0.35) 0, rgba(180,150,60,0.35) 0.25rem, transparent 0.25rem, transparent 0.5rem),
|
||||
repeating-linear-gradient(0deg, rgba(180,150,60,0.20) 0, rgba(180,150,60,0.20) 0.25rem, transparent 0.25rem, transparent 0.5rem);
|
||||
box-shadow: inset 0 0 0 0.0625rem rgba(203,166,61,0.28);
|
||||
}
|
||||
.icon-text { font-size: 1.4375rem; font-weight: 700;
|
||||
}
|
||||
.grid-title { display: none;
|
||||
}
|
||||
.grid-chip { margin-top: 0.4375rem; padding: 0.1875rem 0.4375rem; border-radius: 31.21875rem; background: rgba(215,167,46,0.16); color: #5a4a1f; font-size: 0.6875rem;
|
||||
}
|
||||
|
||||
/* 底部操作条:浅色半透明 + 金色主按钮 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
left: 0; right: 0; bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
padding: 0.4375rem 0.5625rem calc(env(safe-area-inset-bottom) + 0.4375rem);
|
||||
background: rgba(255,255,255,0.85);
|
||||
box-shadow: 0 -0.1875rem 0.5625rem rgba(0,0,0,0.08);
|
||||
-webkit-backdrop-filter: blur(0.3125rem);
|
||||
backdrop-filter: blur(0.3125rem);
|
||||
}
|
||||
.tab { flex: 1; text-align: center; color: #8a7535; font-size: 0.8125rem;
|
||||
}
|
||||
.tab.active { color: #B4880F;
|
||||
}
|
||||
.tab.primary {
|
||||
flex: 0 0 auto;
|
||||
min-width: 5.625rem;
|
||||
margin: 0 0.5625rem;
|
||||
padding: 0.5625rem 1rem;
|
||||
background: linear-gradient(135deg, #FFE69A 0%, #F4CF62 45%, #D7A72E 100%);
|
||||
color: #493c1b;
|
||||
border-radius: 31.21875rem;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 800;
|
||||
box-shadow: 0 0.3125rem 0.6875rem rgba(215,167,46,0.25), 0 0 0 0.0625rem rgba(255,255,255,0.70) inset;
|
||||
}
|
||||
BIN
frontend/unpackage/dist/dev/app-plus/static/logo.png
vendored
BIN
frontend/unpackage/dist/dev/app-plus/static/logo.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
23
frontend/unpackage/dist/dev/mp-weixin/app.js
vendored
23
frontend/unpackage/dist/dev/mp-weixin/app.js
vendored
@@ -13,6 +13,7 @@ if (!Math) {
|
||||
"./pages/customer/select.js";
|
||||
"./pages/supplier/select.js";
|
||||
"./pages/account/select.js";
|
||||
"./pages/detail/index.js";
|
||||
}
|
||||
const _sfc_main = {
|
||||
onLaunch: function() {
|
||||
@@ -31,6 +32,28 @@ function createApp() {
|
||||
app
|
||||
};
|
||||
}
|
||||
if (typeof common_vendor.index !== "undefined" && typeof common_vendor.index.connectSocket === "function") {
|
||||
const _connectSocket = common_vendor.index.connectSocket;
|
||||
common_vendor.index.connectSocket = function(options) {
|
||||
const task = _connectSocket.call(this, options);
|
||||
if (task && typeof task.close === "function") {
|
||||
const _close = task.close;
|
||||
task.close = function(params = {}) {
|
||||
if (params && typeof params === "object") {
|
||||
const codeNum = Number(params.code);
|
||||
const isValid = codeNum === 1e3 || codeNum >= 3e3 && codeNum <= 4999;
|
||||
if (!isValid) {
|
||||
params.code = 1e3;
|
||||
if (!params.reason)
|
||||
params.reason = "normalized from invalid close code";
|
||||
}
|
||||
}
|
||||
return _close.call(this, params);
|
||||
};
|
||||
}
|
||||
return task;
|
||||
};
|
||||
}
|
||||
createApp().app.mount("#app");
|
||||
exports.createApp = createApp;
|
||||
//# sourceMappingURL=../.sourcemap/mp-weixin/app.js.map
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
"pages/product/settings",
|
||||
"pages/customer/select",
|
||||
"pages/supplier/select",
|
||||
"pages/account/select"
|
||||
"pages/account/select",
|
||||
"pages/detail/index"
|
||||
],
|
||||
"window": {
|
||||
"navigationBarTextStyle": "black",
|
||||
|
||||
@@ -85,12 +85,12 @@ const def = (obj, key, value) => {
|
||||
});
|
||||
};
|
||||
const looseToNumber = (val) => {
|
||||
const n = parseFloat(val);
|
||||
return isNaN(n) ? val : n;
|
||||
const n2 = parseFloat(val);
|
||||
return isNaN(n2) ? val : n2;
|
||||
};
|
||||
const toNumber = (val) => {
|
||||
const n = isString(val) ? Number(val) : NaN;
|
||||
return isNaN(n) ? val : n;
|
||||
const n2 = isString(val) ? Number(val) : NaN;
|
||||
return isNaN(n2) ? val : n2;
|
||||
};
|
||||
function normalizeStyle(value) {
|
||||
if (isArray(value)) {
|
||||
@@ -122,6 +122,26 @@ function parseStringStyle(cssText) {
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
function normalizeClass(value) {
|
||||
let res = "";
|
||||
if (isString(value)) {
|
||||
res = value;
|
||||
} else if (isArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const normalized = normalizeClass(value[i]);
|
||||
if (normalized) {
|
||||
res += normalized + " ";
|
||||
}
|
||||
}
|
||||
} else if (isObject(value)) {
|
||||
for (const name in value) {
|
||||
if (value[name]) {
|
||||
res += name + " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.trim();
|
||||
}
|
||||
const toDisplayString = (val) => {
|
||||
return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val);
|
||||
};
|
||||
@@ -5224,6 +5244,7 @@ const o = (value, key) => vOn(value, key);
|
||||
const f = (source, renderItem) => vFor(source, renderItem);
|
||||
const s = (value) => stringifyStyle(value);
|
||||
const e = (target, ...sources) => extend(target, ...sources);
|
||||
const n = (value) => normalizeClass(value);
|
||||
const t = (val) => toDisplayString(val);
|
||||
const p = (props) => renderProps(props);
|
||||
const m = (fn, modifiers, isComponent = false) => withModelModifiers(fn, modifiers, isComponent);
|
||||
@@ -5817,8 +5838,8 @@ const $once = defineSyncApi(API_ONCE, (name, callback) => {
|
||||
const $off = defineSyncApi(API_OFF, (name, callback) => {
|
||||
if (!isArray(name))
|
||||
name = name ? [name] : [];
|
||||
name.forEach((n) => {
|
||||
eventBus.off(n, callback);
|
||||
name.forEach((n2) => {
|
||||
eventBus.off(n2, callback);
|
||||
});
|
||||
}, OffProtocol);
|
||||
const $emit = defineSyncApi(API_EMIT, (name, ...args) => {
|
||||
@@ -7046,9 +7067,9 @@ function isConsoleWritable() {
|
||||
return isWritable;
|
||||
}
|
||||
function initRuntimeSocketService() {
|
||||
const hosts = "198.18.0.1,192.168.31.193,127.0.0.1";
|
||||
const hosts = "198.18.0.1,192.168.31.192,127.0.0.1";
|
||||
const port = "8090";
|
||||
const id = "mp-weixin_UjOtWQ";
|
||||
const id = "mp-weixin_eSBEHk";
|
||||
const lazy = typeof swan !== "undefined";
|
||||
let restoreError = lazy ? () => {
|
||||
} : initOnError();
|
||||
@@ -8000,6 +8021,7 @@ exports.e = e;
|
||||
exports.f = f;
|
||||
exports.index = index;
|
||||
exports.m = m;
|
||||
exports.n = n;
|
||||
exports.o = o;
|
||||
exports.p = p;
|
||||
exports.resolveComponent = resolveComponent;
|
||||
|
||||
@@ -1,133 +1,184 @@
|
||||
"use strict";
|
||||
const common_vendor = require("../../common/vendor.js");
|
||||
const common_http = require("../../common/http.js");
|
||||
function toDateString(d) {
|
||||
const m = (d.getMonth() + 1).toString().padStart(2, "0");
|
||||
const day = d.getDate().toString().padStart(2, "0");
|
||||
return `${d.getFullYear()}-${m}-${day}`;
|
||||
}
|
||||
const API_OF = {
|
||||
sale: "/api/orders",
|
||||
purchase: "/api/purchase-orders",
|
||||
collect: "/api/payments",
|
||||
fund: "/api/other-transactions",
|
||||
stock: "/api/inventories/logs"
|
||||
};
|
||||
const _sfc_main = {
|
||||
data() {
|
||||
const today = /* @__PURE__ */ new Date();
|
||||
const first = new Date(today.getFullYear(), today.getMonth(), 1);
|
||||
return {
|
||||
range: "month",
|
||||
biz: "sale",
|
||||
sub: "out",
|
||||
kw: "",
|
||||
begin: toDateString(first),
|
||||
end: toDateString(today),
|
||||
list: [],
|
||||
loading: false
|
||||
bizList: [
|
||||
{ key: "sale", name: "出货" },
|
||||
{ key: "purchase", name: "进货" },
|
||||
{ key: "collect", name: "收款" },
|
||||
{ key: "fund", name: "资金" },
|
||||
{ key: "stock", name: "盘点" }
|
||||
],
|
||||
range: "month",
|
||||
query: { kw: "" },
|
||||
items: [],
|
||||
page: 1,
|
||||
size: 20,
|
||||
finished: false,
|
||||
loading: false,
|
||||
startDate: "",
|
||||
endDate: ""
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
placeholder() {
|
||||
return "单据号/客户名称/品名规格/备注";
|
||||
},
|
||||
periodLabel() {
|
||||
return this.startDate && this.endDate ? `${this.startDate}~${this.endDate}` : "";
|
||||
},
|
||||
totalAmount() {
|
||||
return this.list.reduce((s, o) => s + Number(o.amount || 0), 0);
|
||||
return this.items.reduce((s, it) => s + Number(it.amount || 0), 0);
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
try {
|
||||
common_vendor.index.__f__("log", "at pages/detail/index.vue:92", "[detail] onLoad route = pages/detail/index");
|
||||
} catch (e) {
|
||||
}
|
||||
this.computeRange();
|
||||
this.reload();
|
||||
},
|
||||
methods: {
|
||||
setRange(r) {
|
||||
this.range = r;
|
||||
const now = /* @__PURE__ */ new Date();
|
||||
if (r === "today") {
|
||||
this.begin = this.end = toDateString(now);
|
||||
} else if (r === "week") {
|
||||
const day = now.getDay() || 7;
|
||||
const start = new Date(now);
|
||||
start.setDate(now.getDate() - day + 1);
|
||||
this.begin = toDateString(start);
|
||||
this.end = toDateString(now);
|
||||
} else if (r === "month") {
|
||||
const first = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
this.begin = toDateString(first);
|
||||
this.end = toDateString(now);
|
||||
} else if (r === "year") {
|
||||
const first = new Date(now.getFullYear(), 0, 1);
|
||||
this.begin = toDateString(first);
|
||||
this.end = toDateString(now);
|
||||
}
|
||||
switchBiz(k) {
|
||||
if (this.biz === k)
|
||||
return;
|
||||
this.biz = k;
|
||||
this.reload();
|
||||
},
|
||||
async 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();
|
||||
},
|
||||
async loadMore() {
|
||||
if (this.loading || this.finished)
|
||||
return;
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await common_http.get("/api/sales/orders", { begin: this.begin, end: this.end, kw: this.kw, sub: this.sub });
|
||||
this.list = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : [];
|
||||
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 common_http.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;
|
||||
} catch (e) {
|
||||
this.list = [];
|
||||
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
goCreate() {
|
||||
common_vendor.index.navigateTo({ url: "/pages/order/create" });
|
||||
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);
|
||||
}
|
||||
},
|
||||
open(o) {
|
||||
onCreate() {
|
||||
if (this.biz === "sale") {
|
||||
common_vendor.index.navigateTo({ url: "/pages/order/create" });
|
||||
return;
|
||||
}
|
||||
common_vendor.index.showToast({ title: "该类型创建页待实现", icon: "none" });
|
||||
},
|
||||
openDetail(it) {
|
||||
common_vendor.index.showToast({ title: "详情开发中", icon: "none" });
|
||||
}
|
||||
}
|
||||
};
|
||||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
return common_vendor.e({
|
||||
a: $data.range === "custom" ? 1 : "",
|
||||
b: common_vendor.o(($event) => $options.setRange("custom")),
|
||||
c: $data.range === "week" ? 1 : "",
|
||||
d: common_vendor.o(($event) => $options.setRange("week")),
|
||||
e: $data.range === "today" ? 1 : "",
|
||||
f: common_vendor.o(($event) => $options.setRange("today")),
|
||||
g: $data.range === "month" ? 1 : "",
|
||||
h: common_vendor.o(($event) => $options.setRange("month")),
|
||||
i: $data.range === "year" ? 1 : "",
|
||||
j: common_vendor.o(($event) => $options.setRange("year")),
|
||||
k: $data.biz === "sale" ? 1 : "",
|
||||
l: common_vendor.o(($event) => $data.biz = "sale"),
|
||||
m: $data.biz === "purchase" ? 1 : "",
|
||||
n: common_vendor.o(($event) => $data.biz = "purchase"),
|
||||
o: $data.biz === "collection" ? 1 : "",
|
||||
p: common_vendor.o(($event) => $data.biz = "collection"),
|
||||
q: $data.biz === "capital" ? 1 : "",
|
||||
r: common_vendor.o(($event) => $data.biz = "capital"),
|
||||
s: $data.biz === "inventory" ? 1 : "",
|
||||
t: common_vendor.o(($event) => $data.biz = "inventory"),
|
||||
v: $data.sub === "out" ? 1 : "",
|
||||
w: common_vendor.o(($event) => $data.sub = "out"),
|
||||
x: $data.sub === "return" ? 1 : "",
|
||||
y: common_vendor.o(($event) => $data.sub = "return"),
|
||||
z: $data.sub === "receive" ? 1 : "",
|
||||
A: common_vendor.o(($event) => $data.sub = "receive"),
|
||||
B: common_vendor.o((...args) => $options.goCreate && $options.goCreate(...args)),
|
||||
C: common_vendor.o((...args) => $options.reload && $options.reload(...args)),
|
||||
D: $data.kw,
|
||||
E: common_vendor.o(($event) => $data.kw = $event.detail.value),
|
||||
F: $data.range === "custom"
|
||||
}, $data.range === "custom" ? {
|
||||
G: common_vendor.t($data.begin),
|
||||
H: $data.begin,
|
||||
I: common_vendor.o((e) => {
|
||||
$data.begin = e.detail.value;
|
||||
$options.reload();
|
||||
}),
|
||||
J: common_vendor.t($data.end),
|
||||
K: $data.end,
|
||||
L: common_vendor.o((e) => {
|
||||
$data.end = e.detail.value;
|
||||
$options.reload();
|
||||
})
|
||||
} : {}, {
|
||||
M: common_vendor.t($options.totalAmount.toFixed(2)),
|
||||
N: common_vendor.f($data.list, (o, k0, i0) => {
|
||||
a: common_vendor.n($data.range === "custom" && "active"),
|
||||
b: common_vendor.o(($event) => $options.switchRange("custom")),
|
||||
c: common_vendor.n($data.range === "week" && "active"),
|
||||
d: common_vendor.o(($event) => $options.switchRange("week")),
|
||||
e: common_vendor.n($data.range === "today" && "active"),
|
||||
f: common_vendor.o(($event) => $options.switchRange("today")),
|
||||
g: common_vendor.n($data.range === "month" && "active"),
|
||||
h: common_vendor.o(($event) => $options.switchRange("month")),
|
||||
i: common_vendor.n($data.range === "year" && "active"),
|
||||
j: common_vendor.o(($event) => $options.switchRange("year")),
|
||||
k: common_vendor.f($data.bizList, (b, k0, i0) => {
|
||||
return {
|
||||
a: common_vendor.t(o.orderDate),
|
||||
b: common_vendor.t(o.customerName),
|
||||
c: common_vendor.t(o.orderNo),
|
||||
d: common_vendor.t(Number(o.amount).toFixed(2)),
|
||||
e: o.id,
|
||||
f: common_vendor.o(($event) => $options.open(o), o.id)
|
||||
a: common_vendor.t(b.name),
|
||||
b: b.key,
|
||||
c: common_vendor.n($data.biz === b.key && "active"),
|
||||
d: common_vendor.o(($event) => $options.switchBiz(b.key), b.key)
|
||||
};
|
||||
}),
|
||||
l: $options.placeholder,
|
||||
m: common_vendor.o((...args) => $options.reload && $options.reload(...args)),
|
||||
n: $data.query.kw,
|
||||
o: common_vendor.o(common_vendor.m(($event) => $data.query.kw = $event.detail.value, {
|
||||
trim: true
|
||||
})),
|
||||
p: common_vendor.t($options.periodLabel),
|
||||
q: common_vendor.o((...args) => $options.reload && $options.reload(...args)),
|
||||
r: common_vendor.t($options.totalAmount.toFixed(2)),
|
||||
s: $data.items.length
|
||||
}, $data.items.length ? {
|
||||
t: common_vendor.f($data.items, (it, k0, i0) => {
|
||||
return {
|
||||
a: common_vendor.t($options.formatDate(it.orderTime || it.txTime || it.createdAt)),
|
||||
b: common_vendor.t(it.customerName || it.supplierName || it.accountName || it.remark || "-"),
|
||||
c: common_vendor.t(it.orderNo || it.code || it.id),
|
||||
d: common_vendor.t((it.amount || 0).toFixed(2)),
|
||||
e: it.id,
|
||||
f: common_vendor.o(($event) => $options.openDetail(it), it.id)
|
||||
};
|
||||
})
|
||||
} : {}, {
|
||||
v: common_vendor.o((...args) => $options.loadMore && $options.loadMore(...args)),
|
||||
w: common_vendor.o((...args) => $options.onCreate && $options.onCreate(...args))
|
||||
});
|
||||
}
|
||||
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||
|
||||
@@ -1 +1 @@
|
||||
<view class="detail"><view class="filter-tabs"><view class="{{['tab', a && 'active']}}" bindtap="{{b}}">自定义</view><view class="{{['tab', c && 'active']}}" bindtap="{{d}}">本周</view><view class="{{['tab', e && 'active']}}" bindtap="{{f}}">今日</view><view class="{{['tab', g && 'active']}}" bindtap="{{h}}">本月</view><view class="{{['tab', i && 'active']}}" bindtap="{{j}}">本年</view></view><view class="biz-tabs"><view class="{{['biz', k && 'active']}}" bindtap="{{l}}">销售</view><view class="{{['biz', m && 'active']}}" bindtap="{{n}}">进货</view><view class="{{['biz', o && 'active']}}" bindtap="{{p}}">收款</view><view class="{{['biz', q && 'active']}}" bindtap="{{r}}">资金</view><view class="{{['biz', s && 'active']}}" bindtap="{{t}}">盘点</view></view><view class="card"><view class="subtabs"><view class="{{['sub', v && 'active']}}" bindtap="{{w}}">出货</view><view class="{{['sub', x && 'active']}}" bindtap="{{y}}">退货</view><view class="{{['sub', z && 'active']}}" bindtap="{{A}}">收款</view><view class="plus" bindtap="{{B}}">+</view></view><view class="search"><input placeholder="单据号/客户/名称/规格/备注" bindconfirm="{{C}}" value="{{D}}" bindinput="{{E}}"/></view><view wx:if="{{F}}" class="daterange"><picker mode="date" value="{{H}}" bindchange="{{I}}"><text>{{G}}</text></picker><text class="sep">—</text><picker mode="date" value="{{K}}" bindchange="{{L}}"><text>{{J}}</text></picker></view><view class="total">合计:¥ {{M}}</view><scroll-view scroll-y class="list"><view wx:for="{{N}}" wx:for-item="o" wx:key="e" class="row" bindtap="{{o.f}}"><view class="left"><view class="date">{{o.a}}</view><view class="name">{{o.b}}</view><view class="no">{{o.c}}</view></view><view class="right">¥ {{o.d}}</view></view></scroll-view></view></view>
|
||||
<view class="page"><view class="seg"><view class="{{['seg-item', a]}}" bindtap="{{b}}">自定义</view><view class="{{['seg-item', c]}}" bindtap="{{d}}">本周</view><view class="{{['seg-item', e]}}" bindtap="{{f}}">今日</view><view class="{{['seg-item', g]}}" bindtap="{{h}}">本月</view><view class="{{['seg-item', i]}}" bindtap="{{j}}">本年</view></view><view class="content"><view class="biz-tabs"><view wx:for="{{k}}" wx:for-item="b" wx:key="b" class="{{['biz', b.c]}}" bindtap="{{b.d}}">{{b.a}}</view></view><view class="panel"><view class="toolbar"><view class="search"><input class="search-input" placeholder="{{l}}" bindconfirm="{{m}}" value="{{n}}" bindinput="{{o}}"/></view><view class="period">{{p}}</view><button size="mini" bindtap="{{q}}">查询</button></view><view class="total">合计:¥{{r}}</view><scroll-view scroll-y class="list" bindscrolltolower="{{v}}"><block wx:if="{{s}}"><view wx:for="{{t}}" wx:for-item="it" wx:key="e" class="item" bindtap="{{it.f}}"><view class="item-left"><view class="date">{{it.a}}</view><view class="name">{{it.b}}</view><view class="no">{{it.c}}</view></view><view class="amount">¥ {{it.d}}</view><view class="arrow">›</view></view></block><view wx:else class="empty">暂无数据</view></scroll-view><view class="fab" bindtap="{{w}}">+</view></view></view></view>
|
||||
@@ -1,43 +1,49 @@
|
||||
|
||||
.detail { display:flex;
|
||||
.page { display:flex; flex-direction: column; height: 100vh;
|
||||
}
|
||||
.filter-tabs { display:flex; gap: 24rpx; padding: 18rpx 24rpx; color:#666;
|
||||
.seg { display:flex; background:#fff;
|
||||
}
|
||||
.filter-tabs .tab.active { color:#2aa7b6; font-weight: 700;
|
||||
.seg-item { flex:1; padding: 22rpx 0; text-align:center; color:#666;
|
||||
}
|
||||
.biz-tabs { position: fixed; left:0; top: 160rpx; bottom: 120rpx; width: 120rpx; display:flex; flex-direction: column; gap: 24rpx; padding: 12rpx;
|
||||
.seg-item.active { color:#18b566; font-weight: 600;
|
||||
}
|
||||
.biz { background:#6aa9ff; color:#fff; border-radius: 16rpx; padding: 20rpx 0; text-align:center; opacity: .85;
|
||||
.content { display:flex; flex:1; min-height: 0;
|
||||
}
|
||||
.biz.active { opacity: 1;
|
||||
.biz-tabs { width: 120rpx; background:#eef6ff; display:flex; flex-direction: column;
|
||||
}
|
||||
.card { margin-left: 140rpx; background:#fff; border-radius: 24rpx; padding: 16rpx;
|
||||
.biz { flex:0 0 120rpx; display:flex; align-items:center; justify-content:center; color:#4aa3d6;
|
||||
}
|
||||
.subtabs { display:flex; align-items:center; gap: 24rpx; padding: 8rpx 6rpx 12rpx;
|
||||
.biz.active { background:#3ac1c9; color:#fff; border-radius: 0 16rpx 16rpx 0;
|
||||
}
|
||||
.sub { color:#57c2cf; padding: 8rpx 12rpx;
|
||||
.panel { flex:1; display:flex; flex-direction: column; background:#fff; margin: 16rpx; border-radius: 16rpx; padding: 12rpx;
|
||||
}
|
||||
.sub.active { border-bottom: 4rpx solid #57c2cf; font-weight:700;
|
||||
.toolbar { display:flex; align-items: center; gap: 12rpx; padding: 8rpx 6rpx;
|
||||
}
|
||||
.plus { margin-left:auto; width: 60rpx; height: 60rpx; border-radius: 30rpx; background:#2ec0d0; color:#fff; font-size: 40rpx; display:flex; align-items:center; justify-content:center;
|
||||
.search { flex:1;
|
||||
}
|
||||
.search { background:#f6f7fb; border-radius: 999rpx; padding: 14rpx 20rpx; margin: 8rpx 0 12rpx;
|
||||
.search-input { width:100%; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx;
|
||||
}
|
||||
.daterange { display:flex; align-items:center; gap: 12rpx; color:#888; padding-bottom: 8rpx;
|
||||
.period { color:#999; font-size: 24rpx; padding: 0 6rpx;
|
||||
}
|
||||
.daterange .sep { color:#ccc;
|
||||
.total { color:#18b566; font-weight: 700; padding: 6rpx 6rpx 12rpx;
|
||||
}
|
||||
.total { color:#2ec0d0; font-weight: 800; padding: 12rpx 0; border-top: 2rpx solid #eaeaea;
|
||||
.list { flex:1;
|
||||
}
|
||||
.list { height: calc(100vh - 420rpx);
|
||||
.item { display:flex; align-items:center; padding: 20rpx 10rpx; border-bottom: 1rpx solid #f1f1f1;
|
||||
}
|
||||
.row { display:flex; justify-content: space-between; align-items:center; padding: 22rpx 10rpx; border-bottom: 1rpx solid #f0f0f0;
|
||||
.item-left { flex:1;
|
||||
}
|
||||
.left .date { color:#999; margin-bottom: 6rpx;
|
||||
.date { color:#999; font-size: 24rpx;
|
||||
}
|
||||
.left .name { color:#333; margin-bottom: 6rpx;
|
||||
.name { color:#333; margin: 4rpx 0; font-weight: 600;
|
||||
}
|
||||
.left .no { color:#bbb;
|
||||
.no { color:#bbb; font-size: 22rpx;
|
||||
}
|
||||
.right { color:#555;
|
||||
.amount { color:#333; font-weight: 700;
|
||||
}
|
||||
.arrow { color:#ccc; font-size: 40rpx; margin-left: 8rpx;
|
||||
}
|
||||
.empty { height: 50vh; display:flex; align-items:center; justify-content:center; color:#999;
|
||||
}
|
||||
.fab { position: fixed; right: 30rpx; bottom: 120rpx; width: 100rpx; height: 100rpx; background:#18b566; color:#fff; border-radius: 50rpx; text-align:center; line-height: 100rpx; font-size: 48rpx; box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,14 @@ const _sfc_main = {
|
||||
onCreateOrder() {
|
||||
common_vendor.index.navigateTo({ url: "/pages/order/create" });
|
||||
},
|
||||
goDetail() {
|
||||
this.activeTab = "detail";
|
||||
try {
|
||||
common_vendor.index.__f__("log", "at pages/index/index.vue:166", "[index] goDetail → /pages/detail/index");
|
||||
} catch (e) {
|
||||
}
|
||||
common_vendor.index.navigateTo({ url: "/pages/detail/index" });
|
||||
},
|
||||
onNoticeTap(n) {
|
||||
common_vendor.index.showModal({
|
||||
title: "广告",
|
||||
@@ -130,7 +138,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
o: common_vendor.o((...args) => $options.goProduct && $options.goProduct(...args)),
|
||||
p: common_vendor.o((...args) => $options.onCreateOrder && $options.onCreateOrder(...args)),
|
||||
q: $data.activeTab === "detail" ? 1 : "",
|
||||
r: common_vendor.o(($event) => $data.activeTab = "detail"),
|
||||
r: common_vendor.o((...args) => $options.goDetail && $options.goDetail(...args)),
|
||||
s: $data.activeTab === "report" ? 1 : "",
|
||||
t: common_vendor.o(($event) => $data.activeTab = "report"),
|
||||
v: $data.activeTab === "me" ? 1 : "",
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
box-shadow: 0 -6rpx 18rpx rgba(0,0,0,0.08);
|
||||
-webkit-backdrop-filter: blur(10rpx);
|
||||
backdrop-filter: blur(10rpx);
|
||||
z-index: 9999;
|
||||
}
|
||||
.tab { flex: 1; text-align: center; color: #8a7535; font-size: 26rpx;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,10 @@ const _sfc_main = {
|
||||
activeCategory: "sale_income",
|
||||
trxAmount: 0,
|
||||
selectedAccountId: null,
|
||||
selectedAccountName: ""
|
||||
selectedAccountName: "",
|
||||
// 收款/付款输入
|
||||
payments: { cash: 0, bank: 0, wechat: 0 },
|
||||
showMore: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -54,6 +57,11 @@ const _sfc_main = {
|
||||
},
|
||||
counterpartyLabel() {
|
||||
return 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);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -83,10 +91,19 @@ const _sfc_main = {
|
||||
recalc() {
|
||||
this.$forceUpdate();
|
||||
},
|
||||
recalcPay() {
|
||||
this.$forceUpdate();
|
||||
},
|
||||
async submit() {
|
||||
const isSaleOrPurchase = this.biz === "sale" || this.biz === "purchase";
|
||||
const payload = isSaleOrPurchase ? {
|
||||
type: this.biz === "sale" ? this.saleType : "purchase." + this.purchaseType,
|
||||
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;
|
||||
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,
|
||||
@@ -102,7 +119,7 @@ const _sfc_main = {
|
||||
remark: this.order.remark
|
||||
};
|
||||
try {
|
||||
const url = isSaleOrPurchase ? "/api/orders" : "/api/other-transactions";
|
||||
const url = isSaleOrPurchase ? isCollectOrPay ? `/api/payments/${this.biz}` : "/api/orders" : "/api/other-transactions";
|
||||
await common_http.post(url, payload);
|
||||
common_vendor.index.showToast({ title: "已保存", icon: "success" });
|
||||
setTimeout(() => {
|
||||
@@ -116,6 +133,7 @@ const _sfc_main = {
|
||||
this.items = [];
|
||||
this.trxAmount = 0;
|
||||
this.order.remark = "";
|
||||
this.payments = { cash: 0, bank: 0, wechat: 0 };
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -158,13 +176,42 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
F: common_vendor.o((...args) => $options.chooseSupplier && $options.chooseSupplier(...args))
|
||||
} : {}, {
|
||||
D: $data.biz === "purchase",
|
||||
G: $data.biz === "sale" || $data.biz === "purchase"
|
||||
}, $data.biz === "sale" || $data.biz === "purchase" ? {
|
||||
H: common_vendor.t($options.totalQuantity),
|
||||
I: common_vendor.t($options.totalAmount.toFixed(2)),
|
||||
J: common_vendor.o((...args) => $options.chooseProduct && $options.chooseProduct(...args))
|
||||
G: $data.biz === "sale" && $data.saleType === "collect" || $data.biz === "purchase" && $data.purchaseType === "pay"
|
||||
}, $data.biz === "sale" && $data.saleType === "collect" || $data.biz === "purchase" && $data.purchaseType === "pay" ? common_vendor.e({
|
||||
H: $data.biz === "sale"
|
||||
}, $data.biz === "sale" ? {
|
||||
I: common_vendor.t($options.customerLabel),
|
||||
J: common_vendor.o((...args) => $options.chooseCustomer && $options.chooseCustomer(...args))
|
||||
} : {
|
||||
K: common_vendor.f($data.biz === "income" ? $options.incomeCategories : $options.expenseCategories, (c, k0, i0) => {
|
||||
K: common_vendor.t($options.supplierLabel),
|
||||
L: common_vendor.o((...args) => $options.chooseSupplier && $options.chooseSupplier(...args))
|
||||
}, {
|
||||
M: common_vendor.o([common_vendor.m(($event) => $data.payments.cash = $event.detail.value, {
|
||||
number: true
|
||||
}), ($event) => $options.recalcPay()]),
|
||||
N: $data.payments.cash,
|
||||
O: common_vendor.o([common_vendor.m(($event) => $data.payments.bank = $event.detail.value, {
|
||||
number: true
|
||||
}), ($event) => $options.recalcPay()]),
|
||||
P: $data.payments.bank,
|
||||
Q: common_vendor.o([common_vendor.m(($event) => $data.payments.wechat = $event.detail.value, {
|
||||
number: true
|
||||
}), ($event) => $options.recalcPay()]),
|
||||
R: $data.payments.wechat,
|
||||
S: common_vendor.t($data.showMore ? "收起" : ""),
|
||||
T: common_vendor.o(($event) => $data.showMore = !$data.showMore),
|
||||
U: common_vendor.t($options.payTotal.toFixed(2)),
|
||||
V: $data.order.remark,
|
||||
W: common_vendor.o(($event) => $data.order.remark = $event.detail.value),
|
||||
X: common_vendor.t($data.order.orderTime),
|
||||
Y: $data.order.orderTime,
|
||||
Z: common_vendor.o((...args) => $options.onDateChange && $options.onDateChange(...args))
|
||||
}) : $data.biz === "sale" || $data.biz === "purchase" ? {
|
||||
ab: common_vendor.t($options.totalQuantity),
|
||||
ac: common_vendor.t($options.totalAmount.toFixed(2)),
|
||||
ad: common_vendor.o((...args) => $options.chooseProduct && $options.chooseProduct(...args))
|
||||
} : {
|
||||
ae: common_vendor.f($data.biz === "income" ? $options.incomeCategories : $options.expenseCategories, (c, k0, i0) => {
|
||||
return {
|
||||
a: common_vendor.t(c.label),
|
||||
b: c.key,
|
||||
@@ -172,22 +219,23 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
d: common_vendor.o(($event) => $data.activeCategory = c.key, c.key)
|
||||
};
|
||||
}),
|
||||
L: common_vendor.t($options.counterpartyLabel),
|
||||
M: common_vendor.o((...args) => $options.chooseCounterparty && $options.chooseCounterparty(...args)),
|
||||
N: common_vendor.t($options.accountLabel),
|
||||
O: common_vendor.o((...args) => $options.chooseAccount && $options.chooseAccount(...args)),
|
||||
P: $data.trxAmount,
|
||||
Q: common_vendor.o(common_vendor.m(($event) => $data.trxAmount = $event.detail.value, {
|
||||
af: common_vendor.t($options.counterpartyLabel),
|
||||
ag: common_vendor.o((...args) => $options.chooseCounterparty && $options.chooseCounterparty(...args)),
|
||||
ah: common_vendor.t($options.accountLabel),
|
||||
ai: common_vendor.o((...args) => $options.chooseAccount && $options.chooseAccount(...args)),
|
||||
aj: $data.trxAmount,
|
||||
ak: common_vendor.o(common_vendor.m(($event) => $data.trxAmount = $event.detail.value, {
|
||||
number: true
|
||||
})),
|
||||
R: $data.order.remark,
|
||||
S: common_vendor.o(($event) => $data.order.remark = $event.detail.value)
|
||||
al: $data.order.remark,
|
||||
am: common_vendor.o(($event) => $data.order.remark = $event.detail.value)
|
||||
}, {
|
||||
T: !$data.items.length
|
||||
aa: $data.biz === "sale" || $data.biz === "purchase",
|
||||
an: !$data.items.length
|
||||
}, !$data.items.length ? {
|
||||
U: common_assets._imports_0$1
|
||||
ao: common_assets._imports_0$1
|
||||
} : {
|
||||
V: common_vendor.f($data.items, (it, idx, i0) => {
|
||||
ap: common_vendor.f($data.items, (it, idx, i0) => {
|
||||
return {
|
||||
a: common_vendor.t(it.productName),
|
||||
b: common_vendor.o([common_vendor.m(($event) => it.quantity = $event.detail.value, {
|
||||
@@ -203,8 +251,8 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
};
|
||||
})
|
||||
}, {
|
||||
W: common_vendor.o((...args) => $options.saveAndReset && $options.saveAndReset(...args)),
|
||||
X: common_vendor.o((...args) => $options.submit && $options.submit(...args))
|
||||
aq: common_vendor.o((...args) => $options.saveAndReset && $options.saveAndReset(...args)),
|
||||
ar: common_vendor.o((...args) => $options.submit && $options.submit(...args))
|
||||
});
|
||||
}
|
||||
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||
|
||||
@@ -1 +1 @@
|
||||
<view class="order"><view class="tabs"><text class="{{[a && 'active']}}" bindtap="{{b}}">销售</text><text class="{{[c && 'active']}}" bindtap="{{d}}">进货</text><text class="{{[e && 'active']}}" bindtap="{{f}}">其他收入</text><text class="{{[g && 'active']}}" bindtap="{{h}}">其他支出</text></view><view wx:if="{{i}}" class="subtabs"><button class="{{['subbtn', j && 'active']}}" bindtap="{{k}}">出货</button><button class="{{['subbtn', l && 'active']}}" bindtap="{{m}}">退货</button><button class="{{['subbtn', n && 'active']}}" bindtap="{{o}}">收款</button></view><view wx:elif="{{p}}" class="subtabs"><button class="{{['subbtn', q && 'active']}}" bindtap="{{r}}">进货</button><button class="{{['subbtn', s && 'active']}}" bindtap="{{t}}">退货</button><button class="{{['subbtn', v && 'active']}}" bindtap="{{w}}">付款</button></view><picker mode="date" value="{{y}}" bindchange="{{z}}"><view class="field"><text class="label">时间</text><text class="value">{{x}}</text></view></picker><view wx:if="{{A}}" class="field" bindtap="{{C}}"><text class="label">客户</text><text class="value">{{B}}</text></view><view wx:elif="{{D}}" class="field" bindtap="{{F}}"><text class="label">供应商</text><text class="value">{{E}}</text></view><view wx:if="{{G}}"><view class="summary"><text>选中货品({{H}})</text><text>合计金额:¥ {{I}}</text></view><view class="add" bindtap="{{J}}">+</view></view><view wx:else><view class="chips"><view wx:for="{{K}}" wx:for-item="c" wx:key="b" class="{{['chip', c.c && 'active']}}" bindtap="{{c.d}}">{{c.a}}</view></view><view class="field" bindtap="{{M}}"><text class="label">往来单位</text><text class="value">{{L}}</text></view><view class="field" bindtap="{{O}}"><text class="label">结算账户</text><text class="value">{{N}}</text></view><view class="field"><text class="label">金额</text><input class="value" type="digit" placeholder="0.00" value="{{P}}" bindinput="{{Q}}"/></view><view class="textarea"><block wx:if="{{r0}}"><textarea maxlength="200" placeholder="备注(最多输入200个字)" value="{{R}}" bindinput="{{S}}"></textarea></block></view></view><view wx:if="{{T}}" class="empty"><image src="{{U}}" mode="widthFix" class="empty-img"></image><text class="empty-text">购物车里空空如也</text><text class="empty-sub">扫描或点击 “+” 选择商品吧</text></view><view wx:else class="list"><view wx:for="{{V}}" wx:for-item="it" wx:key="g" class="row"><view class="col name">{{it.a}}</view><view class="col qty"><input type="number" bindinput="{{it.b}}" value="{{it.c}}"/></view><view class="col price"><input type="number" bindinput="{{it.d}}" value="{{it.e}}"/></view><view class="col amount">¥ {{it.f}}</view></view></view><view class="bottom"><button class="ghost" bindtap="{{W}}">再记一笔</button><button class="primary" bindtap="{{X}}">保存</button></view></view>
|
||||
<view class="order"><view class="tabs"><text class="{{[a && 'active']}}" bindtap="{{b}}">销售</text><text class="{{[c && 'active']}}" bindtap="{{d}}">进货</text><text class="{{[e && 'active']}}" bindtap="{{f}}">其他收入</text><text class="{{[g && 'active']}}" bindtap="{{h}}">其他支出</text></view><view wx:if="{{i}}" class="subtabs"><button class="{{['subbtn', j && 'active']}}" bindtap="{{k}}">出货</button><button class="{{['subbtn', l && 'active']}}" bindtap="{{m}}">退货</button><button class="{{['subbtn', n && 'active']}}" bindtap="{{o}}">收款</button></view><view wx:elif="{{p}}" class="subtabs"><button class="{{['subbtn', q && 'active']}}" bindtap="{{r}}">进货</button><button class="{{['subbtn', s && 'active']}}" bindtap="{{t}}">退货</button><button class="{{['subbtn', v && 'active']}}" bindtap="{{w}}">付款</button></view><picker mode="date" value="{{y}}" bindchange="{{z}}"><view class="field"><text class="label">时间</text><text class="value">{{x}}</text></view></picker><view wx:if="{{A}}" class="field" bindtap="{{C}}"><text class="label">客户</text><text class="value">{{B}}</text></view><view wx:elif="{{D}}" class="field" bindtap="{{F}}"><text class="label">供应商</text><text class="value">{{E}}</text></view><view wx:if="{{G}}"><view wx:if="{{H}}" class="field" bindtap="{{J}}"><text class="label">客户</text><text class="value">{{I}}</text></view><view wx:else class="field" bindtap="{{L}}"><text class="label">供应商</text><text class="value">{{K}}</text></view><view class="field pay-row"><text class="label">现金</text><input class="pay-input" type="digit" placeholder="0.00" bindinput="{{M}}" value="{{N}}"/></view><view class="field pay-row"><text class="label">银行存款</text><input class="pay-input" type="digit" placeholder="0.00" bindinput="{{O}}" value="{{P}}"/></view><view class="field pay-row"><text class="label">微信</text><input class="pay-input" type="digit" placeholder="0.00" bindinput="{{Q}}" value="{{R}}"/></view><view class="collapse-trigger" bindtap="{{T}}">{{S}}</view><view class="textarea"><view class="amount-badge">总金额:{{U}}</view><block wx:if="{{r0}}"><textarea maxlength="200" placeholder="备注(最多输入200个字)" value="{{V}}" bindinput="{{W}}"></textarea></block><view class="date-mini"><picker mode="date" value="{{Y}}" bindchange="{{Z}}"><text>{{X}}</text></picker></view></view></view><view wx:elif="{{aa}}"><view class="summary"><text>选中货品({{ab}})</text><text>合计金额:¥ {{ac}}</text></view><view class="add" bindtap="{{ad}}">+</view></view><view wx:else><view class="chips"><view wx:for="{{ae}}" wx:for-item="c" wx:key="b" class="{{['chip', c.c && 'active']}}" bindtap="{{c.d}}">{{c.a}}</view></view><view class="field" bindtap="{{ag}}"><text class="label">往来单位</text><text class="value">{{af}}</text></view><view class="field" bindtap="{{ai}}"><text class="label">结算账户</text><text class="value">{{ah}}</text></view><view class="field"><text class="label">金额</text><input class="value" type="digit" placeholder="0.00" value="{{aj}}" bindinput="{{ak}}"/></view><view class="textarea"><block wx:if="{{r0}}"><textarea maxlength="200" placeholder="备注(最多输入200个字)" value="{{al}}" bindinput="{{am}}"></textarea></block></view></view><view wx:if="{{an}}" class="empty"><image src="{{ao}}" mode="widthFix" class="empty-img"></image><text class="empty-text">购物车里空空如也</text><text class="empty-sub">扫描或点击 “+” 选择商品吧</text></view><view wx:else class="list"><view wx:for="{{ap}}" wx:for-item="it" wx:key="g" class="row"><view class="col name">{{it.a}}</view><view class="col qty"><input type="number" bindinput="{{it.b}}" value="{{it.c}}"/></view><view class="col price"><input type="number" bindinput="{{it.d}}" value="{{it.e}}"/></view><view class="col amount">¥ {{it.f}}</view></view></view><view class="bottom"><button class="ghost" bindtap="{{aq}}">再记一笔</button><button class="primary" bindtap="{{ar}}">保存</button></view></view>
|
||||
@@ -40,4 +40,13 @@
|
||||
.bottom { position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06);
|
||||
}
|
||||
.primary { width: 100%; background: linear-gradient(135deg, #FFE69A 0%, #F4CF62 45%, #D7A72E 100%); color:#493c1b; border-radius: 999rpx; padding: 20rpx 0; font-weight:800;
|
||||
}
|
||||
/* 收款/付款页样式 */
|
||||
.pay-row .pay-input { text-align: right; color:#333;
|
||||
}
|
||||
.textarea { position: relative; padding: 16rpx 24rpx; background:#fff; border-top: 1rpx solid #eee;
|
||||
}
|
||||
.amount-badge { position: absolute; right: 24rpx; top: -36rpx; background: #d1f0ff; color:#107e9b; padding: 8rpx 16rpx; border-radius: 12rpx; font-size: 24rpx;
|
||||
}
|
||||
.date-mini { position: absolute; right: 24rpx; bottom: 20rpx; color:#666; font-size: 24rpx;
|
||||
}
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
"use strict";
|
||||
const common_vendor = require("../../common/vendor.js");
|
||||
const common_http = require("../../common/http.js");
|
||||
function parseSafeStock(text) {
|
||||
const m = String(text || "").match(/\s*(\d+(?:\.\d+)?)\s*(?:-|~|—|到|,)?\s*(\d+(?:\.\d+)?)?\s*/);
|
||||
if (!m)
|
||||
return { min: null, max: null };
|
||||
return { min: m[1] ? Number(m[1]) : null, max: m[2] ? Number(m[2]) : null };
|
||||
}
|
||||
const _sfc_main = {
|
||||
data() {
|
||||
return {
|
||||
id: null,
|
||||
units: [],
|
||||
categories: [],
|
||||
form: {
|
||||
name: "",
|
||||
categoryId: null,
|
||||
unitId: null,
|
||||
barcode: "",
|
||||
brand: "",
|
||||
model: "",
|
||||
spec: "",
|
||||
purchasePrice: 0,
|
||||
wholesalePrice: 0,
|
||||
bigClientPrice: 0,
|
||||
retailPrice: 0,
|
||||
stock: 0,
|
||||
safeMin: null,
|
||||
safeMax: null,
|
||||
images: []
|
||||
}
|
||||
};
|
||||
},
|
||||
onLoad(query) {
|
||||
this.id = query && query.id ? Number(query.id) : null;
|
||||
this.bootstrap();
|
||||
},
|
||||
computed: {
|
||||
categoryLabel() {
|
||||
const c = this.categories.find((x) => x.id === this.form.categoryId);
|
||||
return c ? c.name : "未选择";
|
||||
},
|
||||
unitLabel() {
|
||||
const u = this.units.find((x) => x.id === this.form.unitId);
|
||||
return u ? u.name : "未选择";
|
||||
},
|
||||
safeStockText: {
|
||||
get() {
|
||||
const a = this.form.safeMin != null ? String(this.form.safeMin) : "";
|
||||
const b = this.form.safeMax != null ? String(this.form.safeMax) : "";
|
||||
if (!a && !b)
|
||||
return "";
|
||||
if (a && b)
|
||||
return a + "-" + b;
|
||||
return a || b;
|
||||
},
|
||||
set(v) {
|
||||
const { min, max } = parseSafeStock(v);
|
||||
this.form.safeMin = min;
|
||||
this.form.safeMax = max;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async bootstrap() {
|
||||
try {
|
||||
const [units, cats] = await Promise.all([
|
||||
common_http.get("/api/product/units"),
|
||||
common_http.get("/api/product/categories")
|
||||
]);
|
||||
this.units = Array.isArray(units == null ? void 0 : units.list) ? units.list : Array.isArray(units) ? units : [];
|
||||
this.categories = Array.isArray(cats == null ? void 0 : cats.list) ? cats.list : Array.isArray(cats) ? cats : [];
|
||||
} catch (_) {
|
||||
}
|
||||
if (this.id) {
|
||||
try {
|
||||
const d = await common_http.get("/api/product/detail", { id: this.id });
|
||||
Object.assign(this.form, {
|
||||
name: d.name || "",
|
||||
categoryId: d.categoryId || null,
|
||||
unitId: d.unitId || null,
|
||||
barcode: d.barcode || "",
|
||||
brand: d.brand || "",
|
||||
model: d.model || "",
|
||||
spec: d.spec || "",
|
||||
purchasePrice: Number(d.purchasePrice || 0),
|
||||
wholesalePrice: Number(d.wholesalePrice || 0),
|
||||
bigClientPrice: Number(d.bigClientPrice || 0),
|
||||
retailPrice: Number(d.retailPrice || 0),
|
||||
stock: Number(d.stock || 0),
|
||||
safeMin: d.safeMin ?? null,
|
||||
safeMax: d.safeMax ?? null,
|
||||
images: Array.isArray(d.images) ? d.images : []
|
||||
});
|
||||
} catch (_) {
|
||||
}
|
||||
}
|
||||
},
|
||||
chooseCategory() {
|
||||
if (!this.categories.length)
|
||||
return;
|
||||
common_vendor.index.showActionSheet({ itemList: this.categories.map((c) => c.name), success: ({ tapIndex }) => {
|
||||
this.form.categoryId = this.categories[tapIndex].id;
|
||||
} });
|
||||
},
|
||||
chooseUnit() {
|
||||
if (!this.units.length)
|
||||
return;
|
||||
common_vendor.index.showActionSheet({ itemList: this.units.map((u) => u.name), success: ({ tapIndex }) => {
|
||||
this.form.unitId = this.units[tapIndex].id;
|
||||
} });
|
||||
},
|
||||
scanBarcode() {
|
||||
common_vendor.index.scanCode({ onlyFromCamera: false, success: (res) => {
|
||||
this.form.barcode = res.result;
|
||||
}, fail: () => {
|
||||
common_vendor.index.showToast({ title: "扫码失败,请手动录入", icon: "none" });
|
||||
} });
|
||||
},
|
||||
async chooseImages() {
|
||||
common_vendor.index.chooseImage({ count: 6, sizeType: ["compressed"], success: async (res) => {
|
||||
try {
|
||||
const uploaded = [];
|
||||
for (const path of res.tempFilePaths) {
|
||||
const ret = await common_http.upload("/api/upload", path, { biz: "product" });
|
||||
uploaded.push(ret.url || ret.path || path);
|
||||
}
|
||||
this.form.images = (this.form.images || []).concat(uploaded).slice(0, 9);
|
||||
} catch (e) {
|
||||
common_vendor.index.showToast({ title: "上传失败", icon: "none" });
|
||||
}
|
||||
} });
|
||||
},
|
||||
moveLeft(i) {
|
||||
if (i <= 0)
|
||||
return;
|
||||
const a = this.form.images;
|
||||
[a[i - 1], a[i]] = [a[i], a[i - 1]];
|
||||
},
|
||||
moveRight(i) {
|
||||
const a = this.form.images;
|
||||
if (i >= a.length - 1)
|
||||
return;
|
||||
[a[i + 1], a[i]] = [a[i], a[i + 1]];
|
||||
},
|
||||
removeImg(i) {
|
||||
this.form.images.splice(i, 1);
|
||||
},
|
||||
async submit() {
|
||||
if (!this.form.name) {
|
||||
common_vendor.index.showToast({ title: "请填写名称", icon: "none" });
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
name: this.form.name,
|
||||
categoryId: this.form.categoryId,
|
||||
unitId: this.form.unitId,
|
||||
barcode: this.form.barcode,
|
||||
brand: this.form.brand,
|
||||
model: this.form.model,
|
||||
spec: this.form.spec,
|
||||
purchasePrice: Number(this.form.purchasePrice || 0),
|
||||
wholesalePrice: Number(this.form.wholesalePrice || 0),
|
||||
bigClientPrice: Number(this.form.bigClientPrice || 0),
|
||||
retailPrice: Number(this.form.retailPrice || 0),
|
||||
stock: Number(this.form.stock || 0),
|
||||
safeMin: this.form.safeMin,
|
||||
safeMax: this.form.safeMax,
|
||||
images: this.form.images
|
||||
};
|
||||
try {
|
||||
if (this.id)
|
||||
await common_http.put("/api/product", { id: this.id, ...payload });
|
||||
else
|
||||
await common_http.post("/api/product", payload);
|
||||
common_vendor.index.showToast({ title: "已保存", icon: "success" });
|
||||
setTimeout(() => {
|
||||
common_vendor.index.navigateBack();
|
||||
}, 600);
|
||||
} catch (e) {
|
||||
common_vendor.index.showToast({ title: e && e.message || "保存失败", icon: "none" });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
return {
|
||||
a: $data.form.name,
|
||||
b: common_vendor.o(common_vendor.m(($event) => $data.form.name = $event.detail.value, {
|
||||
trim: true
|
||||
})),
|
||||
c: common_vendor.t($options.categoryLabel),
|
||||
d: common_vendor.o((...args) => $options.chooseCategory && $options.chooseCategory(...args)),
|
||||
e: common_vendor.t($options.unitLabel),
|
||||
f: common_vendor.o((...args) => $options.chooseUnit && $options.chooseUnit(...args)),
|
||||
g: $data.form.barcode,
|
||||
h: common_vendor.o(common_vendor.m(($event) => $data.form.barcode = $event.detail.value, {
|
||||
trim: true
|
||||
})),
|
||||
i: common_vendor.o((...args) => $options.scanBarcode && $options.scanBarcode(...args)),
|
||||
j: $data.form.brand,
|
||||
k: common_vendor.o(common_vendor.m(($event) => $data.form.brand = $event.detail.value, {
|
||||
trim: true
|
||||
})),
|
||||
l: $data.form.model,
|
||||
m: common_vendor.o(common_vendor.m(($event) => $data.form.model = $event.detail.value, {
|
||||
trim: true
|
||||
})),
|
||||
n: $data.form.spec,
|
||||
o: common_vendor.o(common_vendor.m(($event) => $data.form.spec = $event.detail.value, {
|
||||
trim: true
|
||||
})),
|
||||
p: $data.form.purchasePrice,
|
||||
q: common_vendor.o(common_vendor.m(($event) => $data.form.purchasePrice = $event.detail.value, {
|
||||
number: true
|
||||
})),
|
||||
r: $data.form.wholesalePrice,
|
||||
s: common_vendor.o(common_vendor.m(($event) => $data.form.wholesalePrice = $event.detail.value, {
|
||||
number: true
|
||||
})),
|
||||
t: $data.form.bigClientPrice,
|
||||
v: common_vendor.o(common_vendor.m(($event) => $data.form.bigClientPrice = $event.detail.value, {
|
||||
number: true
|
||||
})),
|
||||
w: $data.form.retailPrice,
|
||||
x: common_vendor.o(common_vendor.m(($event) => $data.form.retailPrice = $event.detail.value, {
|
||||
number: true
|
||||
})),
|
||||
y: $data.form.stock,
|
||||
z: common_vendor.o(common_vendor.m(($event) => $data.form.stock = $event.detail.value, {
|
||||
number: true
|
||||
})),
|
||||
A: $options.safeStockText,
|
||||
B: common_vendor.o(($event) => $options.safeStockText = $event.detail.value),
|
||||
C: common_vendor.o((...args) => $options.chooseImages && $options.chooseImages(...args)),
|
||||
D: common_vendor.f($data.form.images, (url, idx, i0) => {
|
||||
return {
|
||||
a: url,
|
||||
b: common_vendor.o(($event) => $options.moveLeft(idx), idx),
|
||||
c: common_vendor.o(($event) => $options.moveRight(idx), idx),
|
||||
d: common_vendor.o(($event) => $options.removeImg(idx), idx),
|
||||
e: idx
|
||||
};
|
||||
}),
|
||||
E: common_vendor.o((...args) => $options.submit && $options.submit(...args))
|
||||
};
|
||||
}
|
||||
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||
wx.createPage(MiniProgramPage);
|
||||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/product/edit.js.map
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"navigationBarTitleText": "编辑货品",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<view class="page"><view class="form"><view class="field"><text class="label">名称</text><input class="value" placeholder="必填" value="{{a}}" bindinput="{{b}}"/></view><view class="field" bindtap="{{d}}"><text class="label">类别</text><text class="value">{{c}}</text></view><view class="field" bindtap="{{f}}"><text class="label">主单位</text><text class="value">{{e}}</text></view><view class="field"><text class="label">条形码</text><input class="value" placeholder="手动录入或扫码" value="{{g}}" bindinput="{{h}}"/><button size="mini" class="scan" bindtap="{{i}}">扫码</button></view><view class="field"><text class="label">品牌</text><input class="value" placeholder="可选" value="{{j}}" bindinput="{{k}}"/></view><view class="field"><text class="label">型号</text><input class="value" placeholder="可选" value="{{l}}" bindinput="{{m}}"/></view><view class="field"><text class="label">规格</text><input class="value" placeholder="可选" value="{{n}}" bindinput="{{o}}"/></view><view class="grid2"><view class="field"><text class="label">进货价</text><input class="value" type="digit" value="{{p}}" bindinput="{{q}}"/></view><view class="field"><text class="label">批发价</text><input class="value" type="digit" value="{{r}}" bindinput="{{s}}"/></view><view class="field"><text class="label">大单报价</text><input class="value" type="digit" value="{{t}}" bindinput="{{v}}"/></view><view class="field"><text class="label">零售价</text><input class="value" type="digit" value="{{w}}" bindinput="{{x}}"/></view></view><view class="grid2"><view class="field"><text class="label">当前库存</text><input class="value" type="digit" value="{{y}}" bindinput="{{z}}"/></view><view class="field"><text class="label">安全库存</text><input class="value" type="text" placeholder="如 10-100" value="{{A}}" bindinput="{{B}}"/></view></view><view class="photos"><view class="photos-header"><text>商品图片</text><button size="mini" bindtap="{{C}}">选择图片</button></view><view class="photo-list"><view wx:for="{{D}}" wx:for-item="url" wx:key="e" class="photo"><image src="{{url.a}}" mode="aspectFill"/><view class="photo-actions"><text class="btn" bindtap="{{url.b}}">←</text><text class="btn" bindtap="{{url.c}}">→</text><text class="btn danger" bindtap="{{url.d}}">删</text></view></view></view></view></view><view class="bottom"><button class="primary" bindtap="{{E}}">保存</button></view></view>
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
.page{ padding-bottom: 140rpx;
|
||||
}
|
||||
.form{ background:#fff;
|
||||
}
|
||||
.field{ display:flex; align-items:center; gap: 12rpx; padding: 22rpx 20rpx; border-bottom: 1rpx solid #f2f2f2;
|
||||
}
|
||||
.label{ color:#666; width: 160rpx;
|
||||
}
|
||||
.value{ flex:1; color:#333;
|
||||
}
|
||||
.scan{ flex: 0 0 auto;
|
||||
}
|
||||
.grid2{ display:grid; grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.photos{ padding: 16rpx 20rpx;
|
||||
}
|
||||
.photos-header{ display:flex; justify-content: space-between; align-items:center; margin-bottom: 12rpx;
|
||||
}
|
||||
.photo-list{ display:grid; grid-template-columns: repeat(3, 1fr); gap: 12rpx;
|
||||
}
|
||||
.photo{ position: relative;
|
||||
}
|
||||
.photo image{ width: 100%; height: 200rpx; border-radius: 12rpx; background:#f6f6f6; display:block;
|
||||
}
|
||||
.photo-actions{ position:absolute; right:6rpx; bottom:6rpx; display:flex; gap: 6rpx;
|
||||
}
|
||||
.btn{ font-size: 22rpx; padding: 4rpx 8rpx; background: rgba(0,0,0,0.45); color:#fff; border-radius: 8rpx;
|
||||
}
|
||||
.btn.danger{ background: rgba(221,82,77,0.85);
|
||||
}
|
||||
.bottom{ position: fixed; left:0; right:0; bottom:0; background:#fff; padding: 16rpx 24rpx calc(env(safe-area-inset-bottom) + 16rpx); box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.06);
|
||||
}
|
||||
.primary{ width: 100%; background: linear-gradient(135deg, #FFE69A 0%, #F4CF62 45%, #D7A72E 100%); color:#493c1b; border-radius: 999rpx; padding: 20rpx 0; font-weight:800;
|
||||
}
|
||||
@@ -16,6 +16,9 @@ const _sfc_main = {
|
||||
this.fetchCategories();
|
||||
this.reload();
|
||||
},
|
||||
onShow() {
|
||||
this.reload();
|
||||
},
|
||||
computed: {
|
||||
categoryNames() {
|
||||
return this.categories.map((c) => c.name);
|
||||
|
||||
@@ -35,8 +35,8 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
e: common_vendor.f($data.products, (p, k0, i0) => {
|
||||
return {
|
||||
a: common_vendor.t(p.name),
|
||||
b: common_vendor.t(p.code),
|
||||
c: common_vendor.t(p.stock || 0),
|
||||
b: common_vendor.t((p.brand || "") + " " + (p.model || "") + " " + (p.spec || "")),
|
||||
c: common_vendor.t(p.stock ?? 0),
|
||||
d: p.id,
|
||||
e: common_vendor.o(($event) => $options.select(p), p.id)
|
||||
};
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"setting": {
|
||||
"urlCheck": false
|
||||
}
|
||||
}
|
||||
0
frontend/unpackage/dist/dev/mp-weixin/static/icons/detail.png
vendored
Normal file
0
frontend/unpackage/dist/dev/mp-weixin/static/icons/detail.png
vendored
Normal file
Reference in New Issue
Block a user