9.18/2
This commit is contained in:
@@ -0,0 +1,74 @@
|
|||||||
|
package com.example.demo.customer.controller;
|
||||||
|
|
||||||
|
import com.example.demo.common.AppDefaultsProperties;
|
||||||
|
import com.example.demo.customer.dto.CustomerDtos;
|
||||||
|
import com.example.demo.customer.entity.Customer;
|
||||||
|
import com.example.demo.customer.service.CustomerService;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/customers")
|
||||||
|
public class CustomerController {
|
||||||
|
|
||||||
|
private final CustomerService customerService;
|
||||||
|
private final AppDefaultsProperties defaults;
|
||||||
|
|
||||||
|
public CustomerController(CustomerService customerService, AppDefaultsProperties defaults) {
|
||||||
|
this.customerService = customerService;
|
||||||
|
this.defaults = defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<?> search(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||||
|
@RequestParam(name = "kw", required = false) String kw,
|
||||||
|
@RequestParam(name = "debtOnly", required = false, defaultValue = "false") boolean debtOnly,
|
||||||
|
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||||
|
@RequestParam(name = "size", defaultValue = "50") int size) {
|
||||||
|
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||||
|
return ResponseEntity.ok(customerService.search(sid, kw, debtOnly, Math.max(0, page-1), size));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseEntity<?> detail(@PathVariable("id") Long id,
|
||||||
|
@RequestHeader(name = "X-Shop-Id", required = false) Long shopId) {
|
||||||
|
java.util.Optional<Customer> oc = customerService.findById(id);
|
||||||
|
if (oc.isEmpty()) return ResponseEntity.notFound().build();
|
||||||
|
Customer c = oc.get();
|
||||||
|
java.util.Map<String,Object> body = new java.util.HashMap<>();
|
||||||
|
body.put("id", c.getId());
|
||||||
|
body.put("name", c.getName());
|
||||||
|
body.put("contactName", c.getContactName());
|
||||||
|
body.put("mobile", c.getMobile());
|
||||||
|
body.put("phone", c.getPhone());
|
||||||
|
body.put("level", c.getLevel());
|
||||||
|
body.put("priceLevel", c.getPriceLevel());
|
||||||
|
body.put("remark", c.getRemark());
|
||||||
|
body.put("address", c.getAddress());
|
||||||
|
body.put("receivable", new java.math.BigDecimal("0")); // 详情页如需可扩展计算
|
||||||
|
return ResponseEntity.ok(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<?> create(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||||
|
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||||
|
@RequestBody CustomerDtos.CreateOrUpdateCustomerRequest req) {
|
||||||
|
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||||
|
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||||
|
Long id = customerService.create(sid, uid, req);
|
||||||
|
java.util.Map<String,Object> body = new java.util.HashMap<>(); body.put("id", id); return ResponseEntity.ok(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ResponseEntity<?> update(@PathVariable("id") Long id,
|
||||||
|
@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||||
|
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||||
|
@RequestBody CustomerDtos.CreateOrUpdateCustomerRequest req) {
|
||||||
|
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||||
|
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||||
|
customerService.update(id, sid, uid, req);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.example.demo.customer.dto;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public class CustomerDtos {
|
||||||
|
|
||||||
|
public static class CustomerListItem {
|
||||||
|
public Long id;
|
||||||
|
public String name;
|
||||||
|
public String contactName;
|
||||||
|
public String mobile;
|
||||||
|
public String phone;
|
||||||
|
public String level;
|
||||||
|
public String priceLevel;
|
||||||
|
public String remark;
|
||||||
|
public BigDecimal receivable;
|
||||||
|
public CustomerListItem() {}
|
||||||
|
public CustomerListItem(Long id, String name, String contactName, String mobile, String phone, String level, String priceLevel, String remark, BigDecimal receivable) {
|
||||||
|
this.id = id; this.name = name; this.contactName = contactName; this.mobile = mobile; this.phone = phone; this.level = level; this.priceLevel = priceLevel; this.remark = remark; this.receivable = receivable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CreateOrUpdateCustomerRequest {
|
||||||
|
public String name;
|
||||||
|
public String level;
|
||||||
|
public String priceLevel;
|
||||||
|
public String contactName;
|
||||||
|
public String mobile;
|
||||||
|
public String phone;
|
||||||
|
public String address;
|
||||||
|
public java.math.BigDecimal arOpening;
|
||||||
|
public String remark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.example.demo.customer.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "customers")
|
||||||
|
public class Customer {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "shop_id", nullable = false)
|
||||||
|
private Long shopId;
|
||||||
|
|
||||||
|
@Column(name = "user_id", nullable = false)
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@Column(name = "name", nullable = false, length = 120)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "phone", length = 32)
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Column(name = "mobile", length = 32)
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@Column(name = "address", length = 255)
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Column(name = "level", length = 32)
|
||||||
|
private String level;
|
||||||
|
|
||||||
|
@Column(name = "contact_name", length = 64)
|
||||||
|
private String contactName;
|
||||||
|
|
||||||
|
@Column(name = "price_level", nullable = false, length = 32)
|
||||||
|
private String priceLevel;
|
||||||
|
|
||||||
|
@Column(name = "status", nullable = false)
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@Column(name = "ar_opening", precision = 18, scale = 2, nullable = false)
|
||||||
|
private BigDecimal arOpening;
|
||||||
|
|
||||||
|
@Column(name = "remark", length = 255)
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@Column(name = "created_at")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Column(name = "updated_at")
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@Column(name = "deleted_at")
|
||||||
|
private LocalDateTime deletedAt;
|
||||||
|
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public Long getShopId() { return shopId; }
|
||||||
|
public void setShopId(Long shopId) { this.shopId = shopId; }
|
||||||
|
public Long getUserId() { return userId; }
|
||||||
|
public void setUserId(Long userId) { this.userId = userId; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
public String getPhone() { return phone; }
|
||||||
|
public void setPhone(String phone) { this.phone = phone; }
|
||||||
|
public String getMobile() { return mobile; }
|
||||||
|
public void setMobile(String mobile) { this.mobile = mobile; }
|
||||||
|
public String getAddress() { return address; }
|
||||||
|
public void setAddress(String address) { this.address = address; }
|
||||||
|
public String getLevel() { return level; }
|
||||||
|
public void setLevel(String level) { this.level = level; }
|
||||||
|
public String getContactName() { return contactName; }
|
||||||
|
public void setContactName(String contactName) { this.contactName = contactName; }
|
||||||
|
public String getPriceLevel() { return priceLevel; }
|
||||||
|
public void setPriceLevel(String priceLevel) { this.priceLevel = priceLevel; }
|
||||||
|
public Integer getStatus() { return status; }
|
||||||
|
public void setStatus(Integer status) { this.status = status; }
|
||||||
|
public BigDecimal getArOpening() { return arOpening; }
|
||||||
|
public void setArOpening(BigDecimal arOpening) { this.arOpening = arOpening; }
|
||||||
|
public String getRemark() { return remark; }
|
||||||
|
public void setRemark(String remark) { this.remark = remark; }
|
||||||
|
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||||
|
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||||
|
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
||||||
|
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
|
||||||
|
public LocalDateTime getDeletedAt() { return deletedAt; }
|
||||||
|
public void setDeletedAt(LocalDateTime deletedAt) { this.deletedAt = deletedAt; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.example.demo.customer.repo;
|
||||||
|
|
||||||
|
import com.example.demo.customer.entity.Customer;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CustomerRepository extends JpaRepository<Customer, Long> {
|
||||||
|
|
||||||
|
@Query("SELECT c FROM Customer c WHERE c.shopId=:shopId AND c.deletedAt IS NULL AND ( :kw IS NULL OR c.name LIKE CONCAT('%',:kw,'%') OR c.mobile LIKE CONCAT('%',:kw,'%') OR c.phone LIKE CONCAT('%',:kw,'%')) ORDER BY c.id DESC")
|
||||||
|
List<Customer> search(@Param("shopId") Long shopId, @Param("kw") String kw, org.springframework.data.domain.Pageable pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.example.demo.customer.service;
|
||||||
|
|
||||||
|
import com.example.demo.customer.dto.CustomerDtos;
|
||||||
|
import com.example.demo.customer.entity.Customer;
|
||||||
|
import com.example.demo.customer.repo.CustomerRepository;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CustomerService {
|
||||||
|
|
||||||
|
private final CustomerRepository customerRepository;
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
public CustomerService(CustomerRepository customerRepository, JdbcTemplate jdbcTemplate) {
|
||||||
|
this.customerRepository = customerRepository;
|
||||||
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public java.util.Map<String, Object> search(Long shopId, String kw, boolean debtOnly, int page, int size) {
|
||||||
|
List<Customer> list = customerRepository.search(shopId, kw, PageRequest.of(page, size));
|
||||||
|
List<CustomerDtos.CustomerListItem> items = list.stream().map(c -> new CustomerDtos.CustomerListItem(
|
||||||
|
c.getId(), c.getName(), c.getContactName(), c.getMobile(), c.getPhone(), c.getLevel(), c.getPriceLevel(), c.getRemark(), calcReceivable(shopId, c.getId(), c.getArOpening())
|
||||||
|
)).collect(Collectors.toList());
|
||||||
|
if (debtOnly) {
|
||||||
|
items = items.stream().filter(it -> it.receivable != null && it.receivable.compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
java.util.Map<String, Object> resp = new java.util.HashMap<>();
|
||||||
|
resp.put("list", items);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Customer> findById(Long id) {
|
||||||
|
return customerRepository.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Long create(Long shopId, Long userId, CustomerDtos.CreateOrUpdateCustomerRequest req) {
|
||||||
|
Customer c = new Customer();
|
||||||
|
c.setShopId(shopId); c.setUserId(userId);
|
||||||
|
c.setName(req.name); c.setLevel(req.level); c.setPriceLevel(nullToDefaultPriceLevel(req.priceLevel));
|
||||||
|
c.setContactName(req.contactName); c.setMobile(req.mobile); c.setPhone(req.phone); c.setAddress(req.address);
|
||||||
|
c.setStatus(1);
|
||||||
|
c.setArOpening(req.arOpening == null ? BigDecimal.ZERO : req.arOpening);
|
||||||
|
c.setRemark(req.remark);
|
||||||
|
java.time.LocalDateTime now = java.time.LocalDateTime.now();
|
||||||
|
c.setCreatedAt(now);
|
||||||
|
c.setUpdatedAt(now);
|
||||||
|
return customerRepository.save(c).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void update(Long id, Long shopId, Long userId, CustomerDtos.CreateOrUpdateCustomerRequest req) {
|
||||||
|
Customer c = customerRepository.findById(id).orElseThrow();
|
||||||
|
if (!c.getShopId().equals(shopId)) throw new IllegalArgumentException("跨店铺修改");
|
||||||
|
c.setName(req.name); c.setLevel(req.level); c.setPriceLevel(nullToDefaultPriceLevel(req.priceLevel));
|
||||||
|
c.setContactName(req.contactName); c.setMobile(req.mobile); c.setPhone(req.phone); c.setAddress(req.address);
|
||||||
|
if (req.arOpening != null) c.setArOpening(req.arOpening);
|
||||||
|
c.setRemark(req.remark);
|
||||||
|
c.setUpdatedAt(java.time.LocalDateTime.now());
|
||||||
|
customerRepository.save(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String nullToDefaultPriceLevel(String v) { return (v == null || v.isBlank()) ? "retail" : v; }
|
||||||
|
|
||||||
|
private BigDecimal calcReceivable(Long shopId, Long customerId, BigDecimal opening) {
|
||||||
|
BigDecimal open = opening == null ? BigDecimal.ZERO : opening;
|
||||||
|
BigDecimal sale = n(jdbcTemplate.queryForObject("SELECT COALESCE(SUM(amount - paid_amount),0) FROM sales_orders WHERE shop_id=? AND customer_id=? AND status='approved'", BigDecimal.class, shopId, customerId));
|
||||||
|
BigDecimal saleRet = n(jdbcTemplate.queryForObject("SELECT COALESCE(SUM(amount - paid_amount),0) FROM sales_return_orders WHERE shop_id=? AND customer_id=? AND status='approved'", BigDecimal.class, shopId, customerId));
|
||||||
|
BigDecimal otherIn = n(jdbcTemplate.queryForObject("SELECT COALESCE(SUM(amount),0) FROM other_transactions WHERE shop_id=? AND counterparty_type='customer' AND counterparty_id=? AND `type`='income'", BigDecimal.class, shopId, customerId));
|
||||||
|
BigDecimal otherOut = n(jdbcTemplate.queryForObject("SELECT COALESCE(SUM(amount),0) FROM other_transactions WHERE shop_id=? AND counterparty_type='customer' AND counterparty_id=? AND `type`='expense'", BigDecimal.class, shopId, customerId));
|
||||||
|
return open.add(sale).subtract(saleRet).add(otherIn).subtract(otherOut).setScale(2, java.math.RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BigDecimal n(BigDecimal v) { return v == null ? BigDecimal.ZERO : v; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -218,16 +218,20 @@
|
|||||||
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
|
| shop_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||||
| user_id | BIGINT UNSIGNED | NOT NULL | | |
|
| user_id | BIGINT UNSIGNED | NOT NULL | | |
|
||||||
| name | VARCHAR(120) | NOT NULL | | |
|
| name | VARCHAR(120) | NOT NULL | | |
|
||||||
| phone | VARCHAR(32) | YES | | |
|
| phone | VARCHAR(32) | YES | | 座机 |
|
||||||
|
| address | VARCHAR(255) | YES | | 送货地址 |
|
||||||
|
| mobile | VARCHAR(32) | YES | | 手机 |
|
||||||
| level | VARCHAR(32) | YES | | 客户等级标签 |
|
| level | VARCHAR(32) | YES | | 客户等级标签 |
|
||||||
|
| contact_name | VARCHAR(64) | YES | | 联系人 |
|
||||||
| price_level | ENUM('retail','distribution','wholesale','big_client') | NOT NULL | retail | 默认售价列 |
|
| price_level | ENUM('retail','distribution','wholesale','big_client') | NOT NULL | retail | 默认售价列 |
|
||||||
| status | TINYINT UNSIGNED | NOT NULL | 1 | |
|
| status | TINYINT UNSIGNED | NOT NULL | 1 | |
|
||||||
|
| ar_opening | DECIMAL(18,2) | NOT NULL | 0.00 | 期初应收 |
|
||||||
| remark | VARCHAR(255) | YES | | |
|
| remark | VARCHAR(255) | YES | | |
|
||||||
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
| created_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||||
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
| updated_at | TIMESTAMP | NOT NULL | CURRENT_TIMESTAMP | |
|
||||||
| deleted_at | DATETIME | YES | | |
|
| deleted_at | DATETIME | YES | | |
|
||||||
|
|
||||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_customers_shop` (`shop_id`) - KEY: `idx_customers_phone` (`phone`)
|
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_customers_shop` (`shop_id`) - KEY: `idx_customers_phone` (`phone`) - KEY: `idx_customers_mobile` (`mobile`)
|
||||||
**Foreign Keys**: - `fk_customers_shop`: `shop_id` → `shops(id)` - `fk_customers_user`: `user_id` → `users(id)`
|
**Foreign Keys**: - `fk_customers_shop`: `shop_id` → `shops(id)` - `fk_customers_user`: `user_id` → `users(id)`
|
||||||
|
|
||||||
### suppliers
|
### suppliers
|
||||||
|
|||||||
@@ -335,13 +335,17 @@ paths:
|
|||||||
$ref: '#/components/schemas/Product'
|
$ref: '#/components/schemas/Product'
|
||||||
/api/customers:
|
/api/customers:
|
||||||
get:
|
get:
|
||||||
summary: 客户搜索(❌ Partially Implemented)
|
summary: 客户搜索(✅ Fully Implemented)
|
||||||
description: 前端已接入查询参数 kw/page/size,后端待实现或对齐。
|
description: 支持 kw/page/size/debtOnly;返回 {list:[]},含动态计算的 receivable 字段。
|
||||||
parameters:
|
parameters:
|
||||||
- in: query
|
- in: query
|
||||||
name: kw
|
name: kw
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: debtOnly
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
- in: query
|
- in: query
|
||||||
name: page
|
name: page
|
||||||
schema:
|
schema:
|
||||||
@@ -368,6 +372,17 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Customer'
|
$ref: '#/components/schemas/Customer'
|
||||||
|
post:
|
||||||
|
summary: 新建客户(✅ Fully Implemented)
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateCustomerRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
/api/orders:
|
/api/orders:
|
||||||
post:
|
post:
|
||||||
summary: 新建单据(✅ Fully Implemented)
|
summary: 新建单据(✅ Fully Implemented)
|
||||||
@@ -839,13 +854,27 @@ components:
|
|||||||
Customer:
|
Customer:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
id:
|
id: { type: integer, format: int64 }
|
||||||
type: integer
|
name: { type: string }
|
||||||
format: int64
|
contactName: { type: string }
|
||||||
name:
|
mobile: { type: string }
|
||||||
type: string
|
phone: { type: string }
|
||||||
mobile:
|
level: { type: string }
|
||||||
type: string
|
priceLevel: { type: string, enum: [retail, distribution, wholesale, big_client] }
|
||||||
|
remark: { type: string }
|
||||||
|
receivable: { type: number }
|
||||||
|
CreateCustomerRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name: { type: string }
|
||||||
|
level: { type: string, nullable: true }
|
||||||
|
priceLevel: { type: string, enum: [retail, distribution, wholesale, big_client] }
|
||||||
|
contactName: { type: string, nullable: true }
|
||||||
|
mobile: { type: string, nullable: true }
|
||||||
|
phone: { type: string, nullable: true }
|
||||||
|
address: { type: string, nullable: true }
|
||||||
|
arOpening: { type: number, nullable: true }
|
||||||
|
remark: { type: string, nullable: true }
|
||||||
CreateOrderRequest:
|
CreateOrderRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
@@ -54,6 +54,12 @@
|
|||||||
"navigationBarTitleText": "选择客户"
|
"navigationBarTitleText": "选择客户"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/customer/form",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "新增客户"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/supplier/select",
|
"path": "pages/supplier/select",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
59
frontend/pages/customer/form.vue
Normal file
59
frontend/pages/customer/form.vue
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<view class="field"><text class="label">客户名称</text><input class="value" v-model="form.name" placeholder="必填" /></view>
|
||||||
|
<view class="field"><text class="label">客户等级</text><input class="value" v-model="form.level" placeholder="可选,如 VIP/A/B" /></view>
|
||||||
|
<view class="field">
|
||||||
|
<text class="label">售价档位</text>
|
||||||
|
<picker :range="priceLevels" :value="priceIdx" @change="onPriceChange">
|
||||||
|
<view class="value">{{ priceLevels[priceIdx] }}</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
<view class="field"><text class="label">联系人</text><input class="value" v-model="form.contactName" placeholder="可选" /></view>
|
||||||
|
<view class="field"><text class="label">手机</text><input class="value" v-model="form.mobile" placeholder="可选" /></view>
|
||||||
|
<view class="field"><text class="label">电话</text><input class="value" v-model="form.phone" placeholder="可选(座机)" /></view>
|
||||||
|
<view class="field"><text class="label">送货地址</text><input class="value" v-model="form.address" placeholder="可选" /></view>
|
||||||
|
<view class="field"><text class="label">初始应收</text><input class="value" type="digit" v-model.number="form.arOpening" placeholder="默认 0.00" /></view>
|
||||||
|
<view class="textarea"><textarea v-model="form.remark" maxlength="200" placeholder="备注(最多200字)"></textarea></view>
|
||||||
|
|
||||||
|
<view class="bottom"><button class="primary" @click="save">保存</button></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { post, put } from '../../common/http.js'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: null,
|
||||||
|
form: { name:'', level:'', priceLevel:'retail', contactName:'', mobile:'', phone:'', address:'', arOpening:0, remark:'' },
|
||||||
|
priceLevels: ['retail','distribution','wholesale','big_client'],
|
||||||
|
priceIdx: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(query) { if (query && query.id) { this.id = Number(query.id) } },
|
||||||
|
methods: {
|
||||||
|
onPriceChange(e){ this.priceIdx = Number(e.detail.value); this.form.priceLevel = this.priceLevels[this.priceIdx] },
|
||||||
|
async save() {
|
||||||
|
if (!this.form.name) return uni.showToast({ title:'请填写客户名称', icon:'none' })
|
||||||
|
try {
|
||||||
|
if (this.id) await put(`/api/customers/${this.id}`, this.form)
|
||||||
|
else await post('/api/customers', this.form)
|
||||||
|
uni.showToast({ title:'保存成功', icon:'success' })
|
||||||
|
setTimeout(() => uni.navigateBack(), 500)
|
||||||
|
} catch(e) { uni.showToast({ title: e?.message || '保存失败', icon:'none' }) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.page { padding-bottom: 140rpx; }
|
||||||
|
.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background:#fff; border-bottom:1rpx solid #eee; }
|
||||||
|
.label { color:#666; }
|
||||||
|
.value { color:#333; text-align: right; flex: 1; }
|
||||||
|
.textarea { padding: 16rpx 24rpx; background:#fff; margin-top: 12rpx; }
|
||||||
|
.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, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -3,28 +3,37 @@
|
|||||||
<view class="search">
|
<view class="search">
|
||||||
<input v-model="kw" placeholder="搜索客户名称/电话" @confirm="search" />
|
<input v-model="kw" placeholder="搜索客户名称/电话" @confirm="search" />
|
||||||
<button size="mini" @click="search">搜索</button>
|
<button size="mini" @click="search">搜索</button>
|
||||||
|
<button size="mini" :type="debtOnly ? 'primary' : 'default'" @click="toggleDebtOnly">只看欠款</button>
|
||||||
</view>
|
</view>
|
||||||
<scroll-view scroll-y class="list">
|
<scroll-view scroll-y class="list">
|
||||||
<view class="item" v-for="c in customers" :key="c.id" @click="select(c)">
|
<view class="item" v-for="c in customers" :key="c.id" @click="select(c)">
|
||||||
<view class="name">{{ c.name }}</view>
|
<view class="name">{{ c.name }}</view>
|
||||||
<view class="meta">{{ c.mobile || '—' }}</view>
|
<view class="meta">
|
||||||
|
{{ c.mobile || '—' }}
|
||||||
|
<text v-if="typeof c.receivable === 'number'">|应收:¥ {{ Number(c.receivable).toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
<view class="bottom">
|
||||||
|
<button class="primary" @click="createCustomer">新增客户</button>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { get } from '../../common/http.js'
|
import { get } from '../../common/http.js'
|
||||||
export default {
|
export default {
|
||||||
data() { return { kw: '', customers: [] } },
|
data() { return { kw: '', debtOnly: false, customers: [] } },
|
||||||
onLoad() { this.search() },
|
onLoad() { this.search() },
|
||||||
methods: {
|
methods: {
|
||||||
|
toggleDebtOnly() { this.debtOnly = !this.debtOnly; this.search() },
|
||||||
async search() {
|
async search() {
|
||||||
try {
|
try {
|
||||||
const res = await get('/api/customers', { kw: this.kw, page: 1, size: 50 })
|
const res = await get('/api/customers', { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 })
|
||||||
this.customers = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
|
this.customers = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
|
||||||
} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }
|
} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }
|
||||||
},
|
},
|
||||||
|
createCustomer() { uni.navigateTo({ url: '/pages/customer/form' }) },
|
||||||
select(c) {
|
select(c) {
|
||||||
const opener = getCurrentPages()[getCurrentPages().length-2]
|
const opener = getCurrentPages()[getCurrentPages().length-2]
|
||||||
if (opener && opener.$vm) {
|
if (opener && opener.$vm) {
|
||||||
@@ -39,12 +48,14 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.page { display:flex; flex-direction: column; height: 100vh; }
|
.page { display:flex; flex-direction: column; height: 100vh; }
|
||||||
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }
|
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; align-items:center; }
|
||||||
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }
|
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }
|
||||||
.list { flex:1; }
|
.list { flex:1; }
|
||||||
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }
|
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }
|
||||||
.name { color:#333; margin-bottom: 6rpx; }
|
.name { color:#333; margin-bottom: 6rpx; }
|
||||||
.meta { color:#888; font-size: 24rpx; }
|
.meta { color:#888; font-size: 24rpx; }
|
||||||
|
.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, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -152,6 +152,10 @@
|
|||||||
uni.navigateTo({ url: '/pages/product/list' })
|
uni.navigateTo({ url: '/pages/product/list' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (item.key === 'customer') {
|
||||||
|
uni.navigateTo({ url: '/pages/customer/select' })
|
||||||
|
return
|
||||||
|
}
|
||||||
uni.showToast({ title: item.title + '(开发中)', icon: 'none' })
|
uni.showToast({ title: item.title + '(开发中)', icon: 'none' })
|
||||||
},
|
},
|
||||||
goProduct() {
|
goProduct() {
|
||||||
|
|||||||
@@ -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\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;;;"}
|
{"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;;;"}
|
||||||
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/customer/form.js.map
vendored
Normal file
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/customer/form.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"form.js","sources":["pages/customer/form.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvY3VzdG9tZXIvZm9ybS52dWU"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<view class=\"field\"><text class=\"label\">客户名称</text><input class=\"value\" v-model=\"form.name\" placeholder=\"必填\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">客户等级</text><input class=\"value\" v-model=\"form.level\" placeholder=\"可选,如 VIP/A/B\" /></view>\r\n\t\t<view class=\"field\">\r\n\t\t\t<text class=\"label\">售价档位</text>\r\n\t\t\t<picker :range=\"priceLevels\" :value=\"priceIdx\" @change=\"onPriceChange\">\r\n\t\t\t\t<view class=\"value\">{{ priceLevels[priceIdx] }}</view>\r\n\t\t\t</picker>\r\n\t\t</view>\r\n\t\t<view class=\"field\"><text class=\"label\">联系人</text><input class=\"value\" v-model=\"form.contactName\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">手机</text><input class=\"value\" v-model=\"form.mobile\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">电话</text><input class=\"value\" v-model=\"form.phone\" placeholder=\"可选(座机)\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">送货地址</text><input class=\"value\" v-model=\"form.address\" placeholder=\"可选\" /></view>\r\n\t\t<view class=\"field\"><text class=\"label\">初始应收</text><input class=\"value\" type=\"digit\" v-model.number=\"form.arOpening\" placeholder=\"默认 0.00\" /></view>\r\n\t\t<view class=\"textarea\"><textarea v-model=\"form.remark\" maxlength=\"200\" placeholder=\"备注(最多200字)\"></textarea></view>\r\n\r\n\t\t<view class=\"bottom\"><button class=\"primary\" @click=\"save\">保存</button></view>\r\n\t</view>\r\n</template>\r\n\r\n<script>\r\nimport { post, put } from '../../common/http.js'\r\nexport default {\r\n\tdata() {\r\n\t\treturn {\r\n\t\t\tid: null,\r\n\t\t\tform: { name:'', level:'', priceLevel:'retail', contactName:'', mobile:'', phone:'', address:'', arOpening:0, remark:'' },\r\n\t\t\tpriceLevels: ['retail','distribution','wholesale','big_client'],\r\n\t\t\tpriceIdx: 0\r\n\t\t}\r\n\t},\r\n\tonLoad(query) { if (query && query.id) { this.id = Number(query.id) } },\r\n\tmethods: {\r\n\t\tonPriceChange(e){ this.priceIdx = Number(e.detail.value); this.form.priceLevel = this.priceLevels[this.priceIdx] },\r\n\t\tasync save() {\r\n\t\t\tif (!this.form.name) return uni.showToast({ title:'请填写客户名称', icon:'none' })\r\n\t\t\ttry {\r\n\t\t\t\tif (this.id) await put(`/api/customers/${this.id}`, this.form)\r\n\t\t\t\telse await post('/api/customers', this.form)\r\n\t\t\t\tuni.showToast({ title:'保存成功', icon:'success' })\r\n\t\t\t\tsetTimeout(() => uni.navigateBack(), 500)\r\n\t\t\t} catch(e) { uni.showToast({ title: e?.message || '保存失败', icon:'none' }) }\r\n\t\t}\r\n\t}\r\n}\r\n</script>\r\n\r\n<style>\r\n.page { padding-bottom: 140rpx; }\r\n.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background:#fff; border-bottom:1rpx solid #eee; }\r\n.label { color:#666; }\r\n.value { color:#333; text-align: right; flex: 1; }\r\n.textarea { padding: 16rpx 24rpx; background:#fff; margin-top: 12rpx; }\r\n.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); }\r\n.primary { width: 100%; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }\r\n</style>\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/customer/form.vue'\nwx.createPage(MiniProgramPage)"],"names":["uni","put","post"],"mappings":";;;AAuBA,MAAK,YAAU;AAAA,EACd,OAAO;AACN,WAAO;AAAA,MACN,IAAI;AAAA,MACJ,MAAM,EAAE,MAAK,IAAI,OAAM,IAAI,YAAW,UAAU,aAAY,IAAI,QAAO,IAAI,OAAM,IAAI,SAAQ,IAAI,WAAU,GAAG,QAAO,GAAI;AAAA,MACzH,aAAa,CAAC,UAAS,gBAAe,aAAY,YAAY;AAAA,MAC9D,UAAU;AAAA,IACX;AAAA,EACA;AAAA,EACD,OAAO,OAAO;AAAE,QAAI,SAAS,MAAM,IAAI;AAAE,WAAK,KAAK,OAAO,MAAM,EAAE;AAAA,IAAE;AAAA,EAAG;AAAA,EACvE,SAAS;AAAA,IACR,cAAc,GAAE;AAAE,WAAK,WAAW,OAAO,EAAE,OAAO,KAAK;AAAG,WAAK,KAAK,aAAa,KAAK,YAAY,KAAK,QAAQ;AAAA,IAAG;AAAA,IAClH,MAAM,OAAO;AACZ,UAAI,CAAC,KAAK,KAAK;AAAM,eAAOA,cAAG,MAAC,UAAU,EAAE,OAAM,WAAW,MAAK,QAAQ;AAC1E,UAAI;AACH,YAAI,KAAK;AAAI,gBAAMC,YAAAA,IAAI,kBAAkB,KAAK,EAAE,IAAI,KAAK,IAAI;AAAA;AACxD,gBAAMC,iBAAK,kBAAkB,KAAK,IAAI;AAC3CF,sBAAG,MAAC,UAAU,EAAE,OAAM,QAAQ,MAAK,WAAW;AAC9C,mBAAW,MAAMA,cAAAA,MAAI,aAAY,GAAI,GAAG;AAAA,MACzC,SAAQ,GAAG;AAAEA,sBAAG,MAAC,UAAU,EAAE,QAAO,uBAAG,YAAW,QAAQ,MAAK,OAAQ,CAAA;AAAA,MAAE;AAAA,IAC1E;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5CA,GAAG,WAAW,eAAe;"}
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"select.js","sources":["pages/customer/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvY3VzdG9tZXIvc2VsZWN0LnZ1ZQ"],"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=\"c in customers\" :key=\"c.id\" @click=\"select(c)\">\r\n\t\t\t\t<view class=\"name\">{{ c.name }}</view>\r\n\t\t\t\t<view class=\"meta\">{{ c.mobile || '—' }}</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: '', customers: [] } },\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/customers', { kw: this.kw, page: 1, size: 50 })\r\n\t\t\t\t\tthis.customers = 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(c) {\r\n\t\t\t\tconst opener = getCurrentPages()[getCurrentPages().length-2]\r\n\t\t\t\tif (opener && opener.$vm) {\r\n\t\t\t\t\topener.$vm.order.customerId = c.id\r\n\t\t\t\t\topener.$vm.customerName = c.name\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/customer/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAiBC,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,WAAW,CAAA;EAAM;AAAA,EAC3C,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,gBAAI,kBAAkB,EAAE,IAAI,KAAK,IAAI,MAAM,GAAG,MAAM,GAAC,CAAG;AAC1E,aAAK,YAAY,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC5E,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,KAAK;AACzB,eAAO,IAAI,MAAM,aAAa,EAAE;AAChC,eAAO,IAAI,eAAe,EAAE;AAAA,MAC7B;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;ACnCD,GAAG,WAAW,eAAe;"}
|
{"version":3,"file":"select.js","sources":["pages/customer/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvY3VzdG9tZXIvc2VsZWN0LnZ1ZQ"],"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\t<button size=\"mini\" :type=\"debtOnly ? 'primary' : 'default'\" @click=\"toggleDebtOnly\">只看欠款</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 customers\" :key=\"c.id\" @click=\"select(c)\">\r\n\t\t\t\t<view class=\"name\">{{ c.name }}</view>\r\n\t\t\t\t<view class=\"meta\">\r\n\t\t\t\t\t{{ c.mobile || '—' }}\r\n\t\t\t\t\t<text v-if=\"typeof c.receivable === 'number'\">|应收:¥ {{ Number(c.receivable).toFixed(2) }}</text>\r\n\t\t\t\t</view>\r\n\t\t\t</view>\r\n\t\t</scroll-view>\r\n\t\t<view class=\"bottom\">\r\n\t\t\t<button class=\"primary\" @click=\"createCustomer\">新增客户</button>\r\n\t\t</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: '', debtOnly: false, customers: [] } },\r\n\t\tonLoad() { this.search() },\r\n\t\tmethods: {\r\n\t\t\ttoggleDebtOnly() { this.debtOnly = !this.debtOnly; this.search() },\r\n\t\t\tasync search() {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst res = await get('/api/customers', { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 })\r\n\t\t\t\t\tthis.customers = 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\tcreateCustomer() { uni.navigateTo({ url: '/pages/customer/form' }) },\r\n\t\t\tselect(c) {\r\n\t\t\t\tconst opener = getCurrentPages()[getCurrentPages().length-2]\r\n\t\t\t\tif (opener && opener.$vm) {\r\n\t\t\t\t\topener.$vm.order.customerId = c.id\r\n\t\t\t\t\topener.$vm.customerName = c.name\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; align-items:center; }\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\t.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); }\r\n\t.primary { width: 100%; background: linear-gradient(135deg, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0; }\r\n</style>\r\n\r\n\r\n\r\n","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/customer/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAwBC,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,IAAI,IAAI,UAAU,OAAO,WAAW,CAAA;EAAM;AAAA,EAC5D,SAAS;AAAE,SAAK;EAAU;AAAA,EAC1B,SAAS;AAAA,IACR,iBAAiB;AAAE,WAAK,WAAW,CAAC,KAAK;AAAU,WAAK;IAAU;AAAA,IAClE,MAAM,SAAS;AACd,UAAI;AACH,cAAM,MAAM,MAAMA,YAAG,IAAC,kBAAkB,EAAE,IAAI,KAAK,IAAI,UAAU,KAAK,UAAU,MAAM,GAAG,MAAM,IAAI;AACnG,aAAK,YAAY,MAAM,QAAQ,2BAAK,IAAI,IAAI,IAAI,OAAQ,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAA;AAAA,eAC5E,GAAG;AAAEC,sBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,MAAE;AAAA,IAC5D;AAAA,IACD,iBAAiB;AAAEA,oBAAAA,MAAI,WAAW,EAAE,KAAK,uBAAqB,CAAG;AAAA,IAAG;AAAA,IACpE,OAAO,GAAG;AACT,YAAM,SAAS,gBAAiB,EAAC,gBAAe,EAAG,SAAO,CAAC;AAC3D,UAAI,UAAU,OAAO,KAAK;AACzB,eAAO,IAAI,MAAM,aAAa,EAAE;AAChC,eAAO,IAAI,eAAe,EAAE;AAAA,MAC7B;AACAA,oBAAAA,MAAI,aAAa;AAAA,IAClB;AAAA,EACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;AC5CD,GAAG,WAAW,eAAe;"}
|
||||||
File diff suppressed because one or more lines are too long
1
frontend/unpackage/dist/dev/mp-weixin/app.js
vendored
1
frontend/unpackage/dist/dev/mp-weixin/app.js
vendored
@@ -11,6 +11,7 @@ if (!Math) {
|
|||||||
"./pages/product/units.js";
|
"./pages/product/units.js";
|
||||||
"./pages/product/settings.js";
|
"./pages/product/settings.js";
|
||||||
"./pages/customer/select.js";
|
"./pages/customer/select.js";
|
||||||
|
"./pages/customer/form.js";
|
||||||
"./pages/supplier/select.js";
|
"./pages/supplier/select.js";
|
||||||
"./pages/account/select.js";
|
"./pages/account/select.js";
|
||||||
"./pages/detail/index.js";
|
"./pages/detail/index.js";
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"pages/product/units",
|
"pages/product/units",
|
||||||
"pages/product/settings",
|
"pages/product/settings",
|
||||||
"pages/customer/select",
|
"pages/customer/select",
|
||||||
|
"pages/customer/form",
|
||||||
"pages/supplier/select",
|
"pages/supplier/select",
|
||||||
"pages/account/select",
|
"pages/account/select",
|
||||||
"pages/detail/index"
|
"pages/detail/index"
|
||||||
|
|||||||
68
frontend/unpackage/dist/dev/mp-weixin/pages/customer/form.js
vendored
Normal file
68
frontend/unpackage/dist/dev/mp-weixin/pages/customer/form.js
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
"use strict";
|
||||||
|
const common_vendor = require("../../common/vendor.js");
|
||||||
|
const common_http = require("../../common/http.js");
|
||||||
|
const _sfc_main = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: null,
|
||||||
|
form: { name: "", level: "", priceLevel: "retail", contactName: "", mobile: "", phone: "", address: "", arOpening: 0, remark: "" },
|
||||||
|
priceLevels: ["retail", "distribution", "wholesale", "big_client"],
|
||||||
|
priceIdx: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onLoad(query) {
|
||||||
|
if (query && query.id) {
|
||||||
|
this.id = Number(query.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onPriceChange(e) {
|
||||||
|
this.priceIdx = Number(e.detail.value);
|
||||||
|
this.form.priceLevel = this.priceLevels[this.priceIdx];
|
||||||
|
},
|
||||||
|
async save() {
|
||||||
|
if (!this.form.name)
|
||||||
|
return common_vendor.index.showToast({ title: "请填写客户名称", icon: "none" });
|
||||||
|
try {
|
||||||
|
if (this.id)
|
||||||
|
await common_http.put(`/api/customers/${this.id}`, this.form);
|
||||||
|
else
|
||||||
|
await common_http.post("/api/customers", this.form);
|
||||||
|
common_vendor.index.showToast({ title: "保存成功", icon: "success" });
|
||||||
|
setTimeout(() => common_vendor.index.navigateBack(), 500);
|
||||||
|
} catch (e) {
|
||||||
|
common_vendor.index.showToast({ title: (e == null ? void 0 : e.message) || "保存失败", icon: "none" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||||
|
return {
|
||||||
|
a: $data.form.name,
|
||||||
|
b: common_vendor.o(($event) => $data.form.name = $event.detail.value),
|
||||||
|
c: $data.form.level,
|
||||||
|
d: common_vendor.o(($event) => $data.form.level = $event.detail.value),
|
||||||
|
e: common_vendor.t($data.priceLevels[$data.priceIdx]),
|
||||||
|
f: $data.priceLevels,
|
||||||
|
g: $data.priceIdx,
|
||||||
|
h: common_vendor.o((...args) => $options.onPriceChange && $options.onPriceChange(...args)),
|
||||||
|
i: $data.form.contactName,
|
||||||
|
j: common_vendor.o(($event) => $data.form.contactName = $event.detail.value),
|
||||||
|
k: $data.form.mobile,
|
||||||
|
l: common_vendor.o(($event) => $data.form.mobile = $event.detail.value),
|
||||||
|
m: $data.form.phone,
|
||||||
|
n: common_vendor.o(($event) => $data.form.phone = $event.detail.value),
|
||||||
|
o: $data.form.address,
|
||||||
|
p: common_vendor.o(($event) => $data.form.address = $event.detail.value),
|
||||||
|
q: $data.form.arOpening,
|
||||||
|
r: common_vendor.o(common_vendor.m(($event) => $data.form.arOpening = $event.detail.value, {
|
||||||
|
number: true
|
||||||
|
})),
|
||||||
|
s: $data.form.remark,
|
||||||
|
t: common_vendor.o(($event) => $data.form.remark = $event.detail.value),
|
||||||
|
v: common_vendor.o((...args) => $options.save && $options.save(...args))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||||
|
wx.createPage(MiniProgramPage);
|
||||||
|
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/customer/form.js.map
|
||||||
4
frontend/unpackage/dist/dev/mp-weixin/pages/customer/form.json
vendored
Normal file
4
frontend/unpackage/dist/dev/mp-weixin/pages/customer/form.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "新增客户",
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
1
frontend/unpackage/dist/dev/mp-weixin/pages/customer/form.wxml
vendored
Normal file
1
frontend/unpackage/dist/dev/mp-weixin/pages/customer/form.wxml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<view class="page"><view class="field"><text class="label">客户名称</text><input class="value" placeholder="必填" value="{{a}}" bindinput="{{b}}"/></view><view class="field"><text class="label">客户等级</text><input class="value" placeholder="可选,如 VIP/A/B" value="{{c}}" bindinput="{{d}}"/></view><view class="field"><text class="label">售价档位</text><picker range="{{f}}" value="{{g}}" bindchange="{{h}}"><view class="value">{{e}}</view></picker></view><view class="field"><text class="label">联系人</text><input class="value" placeholder="可选" value="{{i}}" bindinput="{{j}}"/></view><view class="field"><text class="label">手机</text><input class="value" placeholder="可选" value="{{k}}" bindinput="{{l}}"/></view><view class="field"><text class="label">电话</text><input class="value" placeholder="可选(座机)" value="{{m}}" bindinput="{{n}}"/></view><view class="field"><text class="label">送货地址</text><input class="value" placeholder="可选" value="{{o}}" bindinput="{{p}}"/></view><view class="field"><text class="label">初始应收</text><input class="value" type="digit" placeholder="默认 0.00" value="{{q}}" bindinput="{{r}}"/></view><view class="textarea"><block wx:if="{{r0}}"><textarea maxlength="200" placeholder="备注(最多200字)" value="{{s}}" bindinput="{{t}}"></textarea></block></view><view class="bottom"><button class="primary" bindtap="{{v}}">保存</button></view></view>
|
||||||
15
frontend/unpackage/dist/dev/mp-weixin/pages/customer/form.wxss
vendored
Normal file
15
frontend/unpackage/dist/dev/mp-weixin/pages/customer/form.wxss
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
.page { padding-bottom: 140rpx;
|
||||||
|
}
|
||||||
|
.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background:#fff; border-bottom:1rpx solid #eee;
|
||||||
|
}
|
||||||
|
.label { color:#666;
|
||||||
|
}
|
||||||
|
.value { color:#333; text-align: right; flex: 1;
|
||||||
|
}
|
||||||
|
.textarea { padding: 16rpx 24rpx; background:#fff; margin-top: 12rpx;
|
||||||
|
}
|
||||||
|
.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, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0;
|
||||||
|
}
|
||||||
@@ -3,20 +3,27 @@ const common_vendor = require("../../common/vendor.js");
|
|||||||
const common_http = require("../../common/http.js");
|
const common_http = require("../../common/http.js");
|
||||||
const _sfc_main = {
|
const _sfc_main = {
|
||||||
data() {
|
data() {
|
||||||
return { kw: "", customers: [] };
|
return { kw: "", debtOnly: false, customers: [] };
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.search();
|
this.search();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
toggleDebtOnly() {
|
||||||
|
this.debtOnly = !this.debtOnly;
|
||||||
|
this.search();
|
||||||
|
},
|
||||||
async search() {
|
async search() {
|
||||||
try {
|
try {
|
||||||
const res = await common_http.get("/api/customers", { kw: this.kw, page: 1, size: 50 });
|
const res = await common_http.get("/api/customers", { kw: this.kw, debtOnly: this.debtOnly, page: 1, size: 50 });
|
||||||
this.customers = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : [];
|
this.customers = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
|
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
createCustomer() {
|
||||||
|
common_vendor.index.navigateTo({ url: "/pages/customer/form" });
|
||||||
|
},
|
||||||
select(c) {
|
select(c) {
|
||||||
const opener = getCurrentPages()[getCurrentPages().length - 2];
|
const opener = getCurrentPages()[getCurrentPages().length - 2];
|
||||||
if (opener && opener.$vm) {
|
if (opener && opener.$vm) {
|
||||||
@@ -33,14 +40,21 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|||||||
b: $data.kw,
|
b: $data.kw,
|
||||||
c: common_vendor.o(($event) => $data.kw = $event.detail.value),
|
c: common_vendor.o(($event) => $data.kw = $event.detail.value),
|
||||||
d: common_vendor.o((...args) => $options.search && $options.search(...args)),
|
d: common_vendor.o((...args) => $options.search && $options.search(...args)),
|
||||||
e: common_vendor.f($data.customers, (c, k0, i0) => {
|
e: $data.debtOnly ? "primary" : "default",
|
||||||
return {
|
f: common_vendor.o((...args) => $options.toggleDebtOnly && $options.toggleDebtOnly(...args)),
|
||||||
|
g: common_vendor.f($data.customers, (c, k0, i0) => {
|
||||||
|
return common_vendor.e({
|
||||||
a: common_vendor.t(c.name),
|
a: common_vendor.t(c.name),
|
||||||
b: common_vendor.t(c.mobile || "—"),
|
b: common_vendor.t(c.mobile || "—"),
|
||||||
c: c.id,
|
c: typeof c.receivable === "number"
|
||||||
d: common_vendor.o(($event) => $options.select(c), c.id)
|
}, typeof c.receivable === "number" ? {
|
||||||
};
|
d: common_vendor.t(Number(c.receivable).toFixed(2))
|
||||||
})
|
} : {}, {
|
||||||
|
e: c.id,
|
||||||
|
f: common_vendor.o(($event) => $options.select(c), c.id)
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
h: common_vendor.o((...args) => $options.createCustomer && $options.createCustomer(...args))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<view class="page"><view class="search"><input placeholder="搜索客户名称/电话" bindconfirm="{{a}}" value="{{b}}" bindinput="{{c}}"/><button size="mini" bindtap="{{d}}">搜索</button></view><scroll-view scroll-y class="list"><view wx:for="{{e}}" wx:for-item="c" wx:key="c" class="item" bindtap="{{c.d}}"><view class="name">{{c.a}}</view><view class="meta">{{c.b}}</view></view></scroll-view></view>
|
<view class="page"><view class="search"><input placeholder="搜索客户名称/电话" bindconfirm="{{a}}" value="{{b}}" bindinput="{{c}}"/><button size="mini" bindtap="{{d}}">搜索</button><button size="mini" type="{{e}}" bindtap="{{f}}">只看欠款</button></view><scroll-view scroll-y class="list"><view wx:for="{{g}}" wx:for-item="c" wx:key="e" class="item" bindtap="{{c.f}}"><view class="name">{{c.a}}</view><view class="meta">{{c.b}} <text wx:if="{{c.c}}">|应收:¥ {{c.d}}</text></view></view></scroll-view><view class="bottom"><button class="primary" bindtap="{{h}}">新增客户</button></view></view>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
.page { display:flex; flex-direction: column; height: 100vh;
|
.page { display:flex; flex-direction: column; height: 100vh;
|
||||||
}
|
}
|
||||||
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff;
|
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; align-items:center;
|
||||||
}
|
}
|
||||||
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx;
|
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx;
|
||||||
}
|
}
|
||||||
@@ -13,3 +13,7 @@
|
|||||||
}
|
}
|
||||||
.meta { color:#888; font-size: 24rpx;
|
.meta { color:#888; font-size: 24rpx;
|
||||||
}
|
}
|
||||||
|
.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, #A0E4FF 0%, #17A2C4 100%); color:#fff; border-radius: 999rpx; padding: 20rpx 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ const _sfc_main = {
|
|||||||
common_vendor.index.navigateTo({ url: "/pages/product/list" });
|
common_vendor.index.navigateTo({ url: "/pages/product/list" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (item.key === "customer") {
|
||||||
|
common_vendor.index.navigateTo({ url: "/pages/customer/select" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
common_vendor.index.showToast({ title: item.title + "(开发中)", icon: "none" });
|
common_vendor.index.showToast({ title: item.title + "(开发中)", icon: "none" });
|
||||||
},
|
},
|
||||||
goProduct() {
|
goProduct() {
|
||||||
@@ -75,7 +79,7 @@ const _sfc_main = {
|
|||||||
goDetail() {
|
goDetail() {
|
||||||
this.activeTab = "detail";
|
this.activeTab = "detail";
|
||||||
try {
|
try {
|
||||||
common_vendor.index.__f__("log", "at pages/index/index.vue:166", "[index] goDetail → /pages/detail/index");
|
common_vendor.index.__f__("log", "at pages/index/index.vue:170", "[index] goDetail → /pages/detail/index");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
common_vendor.index.navigateTo({ url: "/pages/detail/index" });
|
common_vendor.index.navigateTo({ url: "/pages/detail/index" });
|
||||||
|
|||||||
Reference in New Issue
Block a user