9.16/1
This commit is contained in:
@@ -13,6 +13,20 @@
|
|||||||
- User: `root`
|
- User: `root`
|
||||||
- 说明:所有结构变更均通过 MysqlMCP 执行并已落地到线上库。
|
- 说明:所有结构变更均通过 MysqlMCP 执行并已落地到线上库。
|
||||||
|
|
||||||
|
### 角色与模拟数据策略(统一为店长)
|
||||||
|
- 当前不进行角色划分,系统仅保留“店长”角色。
|
||||||
|
- 已将所有用户记录统一为:`role='owner'`、`is_owner=1`。
|
||||||
|
- 前端/后端权限逻辑暂未启用,后续若引入权限体系,再行扩展角色与边界。
|
||||||
|
|
||||||
|
### 小程序默认用户(可开关,默认关闭)
|
||||||
|
- 目的:开发/演示阶段,便于免登录联调。
|
||||||
|
- 机制:前端在请求头附加 `X-User-Id`(值为张老板 id=2),仅当开关开启时。
|
||||||
|
- 开关:
|
||||||
|
- 环境变量:`VITE_APP_ENABLE_DEFAULT_USER=true` 与 `VITE_APP_DEFAULT_USER_ID=2`
|
||||||
|
- 或本地存储:`ENABLE_DEFAULT_USER=true` 与 `DEFAULT_USER_ID=2`
|
||||||
|
- 关闭:不设置/置为 `false` 即可停用(生产环境默认关闭)。
|
||||||
|
- 完全移除:删除 `frontend/common/config.js` 中默认用户配置与 `frontend/common/http.js` 中注入逻辑。
|
||||||
|
|
||||||
### 后端(Spring Boot)数据库状态
|
### 后端(Spring Boot)数据库状态
|
||||||
- 依赖:`pom.xml` 未包含 `spring-boot-starter-web`、`spring-boot-starter-data-jpa`、`mysql-connector-j` 等数据库相关依赖。
|
- 依赖:`pom.xml` 未包含 `spring-boot-starter-web`、`spring-boot-starter-data-jpa`、`mysql-connector-j` 等数据库相关依赖。
|
||||||
- 配置:`src/main/resources/application.properties` 仅有 `spring.application.name=demo`;未配置 `spring.datasource.*`、`spring.jpa.*`。
|
- 配置:`src/main/resources/application.properties` 仅有 `spring.application.name=demo`;未配置 `spring.datasource.*`、`spring.jpa.*`。
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.example.demo.dashboard;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/dashboard")
|
||||||
|
public class DashboardController {
|
||||||
|
|
||||||
|
private final DashboardService dashboardService;
|
||||||
|
|
||||||
|
public DashboardController(DashboardService dashboardService) {
|
||||||
|
this.dashboardService = dashboardService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/overview")
|
||||||
|
public ResponseEntity<DashboardOverviewResponse> overview(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId) {
|
||||||
|
long sid = (shopId == null ? 1L : shopId);
|
||||||
|
return ResponseEntity.ok(dashboardService.getOverview(sid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.example.demo.dashboard;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public class DashboardOverviewResponse {
|
||||||
|
private BigDecimal todaySalesAmount;
|
||||||
|
private BigDecimal monthGrossProfit;
|
||||||
|
private BigDecimal stockTotalQuantity;
|
||||||
|
|
||||||
|
public DashboardOverviewResponse(BigDecimal todaySalesAmount, BigDecimal monthGrossProfit, BigDecimal stockTotalQuantity) {
|
||||||
|
this.todaySalesAmount = todaySalesAmount;
|
||||||
|
this.monthGrossProfit = monthGrossProfit;
|
||||||
|
this.stockTotalQuantity = stockTotalQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTodaySalesAmount() {
|
||||||
|
return todaySalesAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getMonthGrossProfit() {
|
||||||
|
return monthGrossProfit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getStockTotalQuantity() {
|
||||||
|
return stockTotalQuantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.example.demo.dashboard;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
|
import jakarta.persistence.PersistenceContext;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public class DashboardRepository {
|
||||||
|
|
||||||
|
@PersistenceContext
|
||||||
|
private EntityManager entityManager;
|
||||||
|
|
||||||
|
public BigDecimal sumTodaySalesOrders(Long shopId) {
|
||||||
|
Object result = entityManager.createNativeQuery(
|
||||||
|
"SELECT COALESCE(SUM(amount), 0) FROM sales_orders " +
|
||||||
|
"WHERE shop_id = :shopId AND status = 'approved' AND DATE(order_time) = CURRENT_DATE()"
|
||||||
|
).setParameter("shopId", shopId).getSingleResult();
|
||||||
|
return toBigDecimal(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal sumMonthGrossProfitApprox(Long shopId) {
|
||||||
|
Object result = entityManager.createNativeQuery(
|
||||||
|
"SELECT COALESCE(SUM(soi.amount - soi.quantity * COALESCE(pp.purchase_price, 0)), 0) AS gp " +
|
||||||
|
"FROM sales_orders so " +
|
||||||
|
"JOIN sales_order_items soi ON soi.order_id = so.id " +
|
||||||
|
"LEFT JOIN product_prices pp ON pp.product_id = soi.product_id AND pp.shop_id = so.shop_id " +
|
||||||
|
"WHERE so.shop_id = :shopId AND so.status = 'approved' " +
|
||||||
|
"AND so.order_time >= DATE_FORMAT(CURRENT_DATE(), '%Y-%m-01') " +
|
||||||
|
"AND so.order_time < DATE_ADD(DATE_FORMAT(CURRENT_DATE(), '%Y-%m-01'), INTERVAL 1 MONTH)"
|
||||||
|
).setParameter("shopId", shopId).getSingleResult();
|
||||||
|
return toBigDecimal(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal sumTotalInventoryQty(Long shopId) {
|
||||||
|
Object result = entityManager.createNativeQuery(
|
||||||
|
"SELECT COALESCE(SUM(quantity), 0) FROM inventories WHERE shop_id = :shopId"
|
||||||
|
).setParameter("shopId", shopId).getSingleResult();
|
||||||
|
return toBigDecimal(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal toBigDecimal(Object value) {
|
||||||
|
if (value == null) return BigDecimal.ZERO;
|
||||||
|
if (value instanceof BigDecimal) return (BigDecimal) value;
|
||||||
|
if (value instanceof Number) return BigDecimal.valueOf(((Number) value).doubleValue());
|
||||||
|
try {
|
||||||
|
return new BigDecimal(value.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.example.demo.dashboard;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DashboardService {
|
||||||
|
private final DashboardRepository dashboardRepository;
|
||||||
|
|
||||||
|
public DashboardService(DashboardRepository dashboardRepository) {
|
||||||
|
this.dashboardRepository = dashboardRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DashboardOverviewResponse getOverview(long shopId) {
|
||||||
|
BigDecimal todaySales = dashboardRepository.sumTodaySalesOrders(shopId);
|
||||||
|
BigDecimal monthGrossProfit = dashboardRepository.sumMonthGrossProfitApprox(shopId);
|
||||||
|
BigDecimal stockTotalQty = dashboardRepository.sumTotalInventoryQty(shopId);
|
||||||
|
return new DashboardOverviewResponse(todaySales, monthGrossProfit, stockTotalQty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
## partsinquiry 数据库文档
|
## partsinquiry 数据库文档
|
||||||
|
|
||||||
更新日期:2025-09-16
|
更新日期:2025-09-16(已插入演示数据)
|
||||||
|
|
||||||
说明:本文件根据远程库 mysql.tonaspace.com 中 `partsinquiry` 的实际结构生成,字段/索引/外键信息以线上为准。
|
说明:本文件根据远程库 mysql.tonaspace.com 中 `partsinquiry` 的实际结构生成,字段/索引/外键信息以线上为准。
|
||||||
|
|
||||||
@@ -371,8 +371,8 @@
|
|||||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_ot_shop_time` (`shop_id`,`tx_time`) - KEY: `idx_ot_account` (`account_id`)
|
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_ot_shop_time` (`shop_id`,`tx_time`) - KEY: `idx_ot_account` (`account_id`)
|
||||||
**Foreign Keys**: - `fk_ot_shop`: `shop_id` → `shops(id)` - `fk_ot_user`: `user_id` → `users(id)` - `fk_ot_account`: `account_id` → `accounts(id)`
|
**Foreign Keys**: - `fk_ot_shop`: `shop_id` → `shops(id)` - `fk_ot_user`: `user_id` → `users(id)` - `fk_ot_account`: `account_id` → `accounts(id)`
|
||||||
|
|
||||||
### product_images 触发器
|
### 触发器
|
||||||
- `trg_products_ai`: AFTER INSERT ON `products` → 更新 `products.search_text`
|
- `trg_products_bi`: BEFORE INSERT ON `products` → 设置 `products.search_text`
|
||||||
- `trg_products_au`: BEFORE UPDATE ON `products` → 维护 `products.search_text`
|
- `trg_products_au`: BEFORE UPDATE ON `products` → 维护 `products.search_text`
|
||||||
- `trg_palias_ai`: AFTER INSERT ON `product_aliases` → 重建 `products.search_text`
|
- `trg_palias_ai`: AFTER INSERT ON `product_aliases` → 重建 `products.search_text`
|
||||||
- `trg_palias_au`: AFTER UPDATE ON `product_aliases` → 重建 `products.search_text`
|
- `trg_palias_au`: AFTER UPDATE ON `product_aliases` → 重建 `products.search_text`
|
||||||
@@ -399,3 +399,10 @@
|
|||||||
**Indexes**: - PRIMARY KEY: `id` - KEY: `idx_notices_shop` (`shop_id`,`status`,`is_pinned`,`created_at`) - KEY: `idx_notices_time` (`starts_at`,`ends_at`)
|
**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
|
**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
|
||||||
|
|
||||||
|
### 附:演示种子数据(非完整,仅用于联调验证)
|
||||||
|
- 演示店铺:演示店A(用户 3,全部店长 owner)
|
||||||
|
- 商品域:基础单位3条、类别2条、全局SKU2条、商品2条(含别名/价格/库存/图片)
|
||||||
|
- 往来与账户:客户2、供应商2、账户3
|
||||||
|
- 单据:销售单1(含明细2)与进货单1(含明细2)、收付款各1、其他收支2
|
||||||
|
- 审核与公告:part_submissions 1、attachments 1、notices 2、新增 wechat 身份与会话各1
|
||||||
|
|
||||||
|
|||||||
279
doc/openapi.yaml
279
doc/openapi.yaml
@@ -27,6 +27,174 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Notice'
|
$ref: '#/components/schemas/Notice'
|
||||||
|
/api/metrics/overview:
|
||||||
|
get:
|
||||||
|
summary: 概览统计(❌ Partially Implemented)
|
||||||
|
description: 返回今日/本月销售额、本月利润与库存商品数量。前端已接入,后端待实现。
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/MetricsOverview'
|
||||||
|
/api/accounts:
|
||||||
|
get:
|
||||||
|
summary: 账户列表(❌ Partially Implemented)
|
||||||
|
description: 前端账户选择页已接入,后端返回数组或 {list:[]} 皆可。
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
oneOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Account'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
list:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Account'
|
||||||
|
/api/suppliers:
|
||||||
|
get:
|
||||||
|
summary: 供应商搜索(❌ Partially Implemented)
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: kw
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
oneOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Supplier'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
list:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Supplier'
|
||||||
|
/api/other-transactions:
|
||||||
|
post:
|
||||||
|
summary: 新建其他收入/支出(❌ Partially Implemented)
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateOtherTransactionRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
/api/products:
|
||||||
|
get:
|
||||||
|
summary: 商品搜索(❌ Partially Implemented)
|
||||||
|
description: 前端已接入查询参数 kw/page/size,后端待实现或对齐。
|
||||||
|
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: 50
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
oneOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Product'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
list:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Product'
|
||||||
|
/api/customers:
|
||||||
|
get:
|
||||||
|
summary: 客户搜索(❌ Partially Implemented)
|
||||||
|
description: 前端已接入查询参数 kw/page/size,后端待实现或对齐。
|
||||||
|
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: 50
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
oneOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Customer'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
list:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Customer'
|
||||||
|
/api/orders:
|
||||||
|
post:
|
||||||
|
summary: 新建单据(❌ Partially Implemented)
|
||||||
|
description: 前端开单页已提交 payload,后端待实现。
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateOrderRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
orderNo:
|
||||||
|
type: string
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
Notice:
|
Notice:
|
||||||
@@ -52,6 +220,66 @@ components:
|
|||||||
startsAt:
|
startsAt:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
MetricsOverview:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
todaySales:
|
||||||
|
type: string
|
||||||
|
example: '1234.56'
|
||||||
|
monthSales:
|
||||||
|
type: string
|
||||||
|
example: '23456.78'
|
||||||
|
monthProfit:
|
||||||
|
type: string
|
||||||
|
example: '3456.78'
|
||||||
|
stockCount:
|
||||||
|
type: string
|
||||||
|
example: '1200'
|
||||||
|
Account:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
enum: [cash, bank, alipay, wechat, other]
|
||||||
|
balance:
|
||||||
|
type: number
|
||||||
|
Supplier:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
mobile:
|
||||||
|
type: string
|
||||||
|
CreateOtherTransactionRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
enum: [income, expense]
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
|
counterpartyId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
nullable: true
|
||||||
|
accountId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
amount:
|
||||||
|
type: number
|
||||||
|
txTime:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
remark:
|
||||||
|
type: string
|
||||||
endsAt:
|
endsAt:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
@@ -64,4 +292,55 @@ components:
|
|||||||
updatedAt:
|
updatedAt:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
Product:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
code:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
price:
|
||||||
|
type: number
|
||||||
|
stock:
|
||||||
|
type: number
|
||||||
|
Customer:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
mobile:
|
||||||
|
type: string
|
||||||
|
CreateOrderRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
description: 'sale.out/sale.return/sale.collect/purchase/income/expense 等'
|
||||||
|
orderTime:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
customerId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
nullable: true
|
||||||
|
items:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
productId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
quantity:
|
||||||
|
type: number
|
||||||
|
unitPrice:
|
||||||
|
type: number
|
||||||
|
amount:
|
||||||
|
type: number
|
||||||
|
|
||||||
|
|||||||
167
doc/requirements.md
Normal file
167
doc/requirements.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
* ### **配件查询App需求规格说明书**
|
||||||
|
|
||||||
|
#### 1.0 项目概述
|
||||||
|
本项目旨在开发一款面向小微商户的移动端进销存管理应用,命名为“配件查询”。该应用核心功能是帮助用户高效管理商品、库存、销售、采购、客户、供应商及财务收支,并通过数据报表提供经营状况分析,助力商户实现数字化经营。
|
||||||
|
参考的小程序“智慧记进销存”,但是多了一个配件查询功能,以下所罗列的内容大多也参考至该小程序,如有歧义可优先参照这个小程序,拿不准优先问。
|
||||||
|
|
||||||
|
#### 2.0 功能模块需求
|
||||||
|
|
||||||
|
**2.1 首页 (Dashboard)**
|
||||||
|
|
||||||
|
* **2.1.1 核心数据概览:** 首页需直观展示当日、当月的核心经营数据。
|
||||||
|
|
||||||
|
* 今日销售额
|
||||||
|
* 本月销售额
|
||||||
|
* 本月利润
|
||||||
|
* 库存商品数量
|
||||||
|
|
||||||
|
**2.1.2 广告位:** 在首页区域提供一个展示广告的区域。
|
||||||
|
|
||||||
|
* **2.1.3 快捷功能入口:** 提供一个快捷功能区域,方便用户快速访问常用功能。
|
||||||
|
|
||||||
|
* 默认应包含:客户管理、销售开单、账户管理、供应商管理、进货开单、其他支出、VIP会员、报表统计等。
|
||||||
|
|
||||||
|
* **2.1.4 在线客服:** 提供一个悬浮的“咨询”或“在线客服”入口,方便用户随时获取帮助。
|
||||||
|
|
||||||
|
**2.2 货品管理模块**
|
||||||
|
|
||||||
|
* **2.2.1 货品列表与库存:**
|
||||||
|
* 展示所有货品的列表,包含名称、库存等基本信息。
|
||||||
|
* 支持按“全部类别”或指定类别筛选货品。
|
||||||
|
* 提供搜索功能,支持通过货品名称或条形码进行模糊查找。
|
||||||
|
* 列表底部显示总货品种类数量。
|
||||||
|
* 当库存为空时,应有明显的空状态提示,并引导用户“点击右上角‘+’录入货品信息”。
|
||||||
|
* **2.2.2 新增/编辑货品:**
|
||||||
|
* 支持添加商品图片。
|
||||||
|
* App端支持录入或扫描商品条形码;小程序端仅支持手动录入,不支持扫码。
|
||||||
|
* **货品名称**为必填项。
|
||||||
|
* 可为货品选择**类别**和**主单位**。
|
||||||
|
* 支持自定义扩展货品属性(如品牌、型号、产地、保质期等)。
|
||||||
|
* 货品图片支持多图上传,支持拖拽排序,支持图片放大预览。
|
||||||
|
* 可录入**当前库存**、**安全库存**(一个数值区间,用于库存预警)。
|
||||||
|
* 需分别录入四种价格,**进货价**、**批发价**、**大单报价**和**零售价**。
|
||||||
|
* 提供**备注**字段,用于记录额外信息。
|
||||||
|
* 保存后,可选择“继续新增”或返回列表。
|
||||||
|
* **2.2.3 货品设置:**
|
||||||
|
* 支持自定义**货品类别**管理。
|
||||||
|
* 支持自定义**货品单位**管理。
|
||||||
|
* 提供开关选项,允许用户选择是否“隐藏零库存商品”和“隐藏进货价”。
|
||||||
|
|
||||||
|
**2.3 开单(交易)模块**
|
||||||
|
* **2.3.1 核心功能:** 该模块是应用的核心操作区,整合了销售、进货和财务记账功能。
|
||||||
|
* **2.3.2 销售开单:**
|
||||||
|
* **出货单:**
|
||||||
|
* 自动记录开单**时间**,并支持手动修改。
|
||||||
|
* 可选择**客户**,默认为“零售客户”。
|
||||||
|
* 通过“+”号从货品列表中选择商品,自动计算**合计金额**。
|
||||||
|
* 支持在订单中对单个商品进行操作(如修改数量、价格等)。
|
||||||
|
* **退货单:** 用于处理客户退货业务。
|
||||||
|
* **收款单:** 用于处理销售单的后续收款或直接创建收款记录。
|
||||||
|
* **2.3.3 进货开单:** 用于记录从供应商处采购商品的业务流程。
|
||||||
|
* **2.3.4 其他收入/支出:**
|
||||||
|
* **其他收入:**
|
||||||
|
* 支持对收入进行分类,如“销售收入”、“经营所得”、“利息收入”等。
|
||||||
|
* 可选择**往来单位**和**结算账户**(如现金、银行等)。
|
||||||
|
* 可添加备注并选择日期。
|
||||||
|
* **其他支出:**
|
||||||
|
* 支持对支出进行分类,如“经营支出”、“办公用品”、“房租”等。
|
||||||
|
* 同样支持选择**往来单位**和**结算账户**。
|
||||||
|
|
||||||
|
**2.4 明细查询模块**
|
||||||
|
* **2.4.1 维度筛选:**
|
||||||
|
* 提供按时间维度(自定义、本周、今日、本月、本年)快速筛选单据。
|
||||||
|
* 提供按业务类型(销售、进货、收银、资金、盘点)进行分类查看。
|
||||||
|
* **2.4.2 单据列表:**
|
||||||
|
* 在选定维度下,以列表形式展示所有相关单据。
|
||||||
|
* 提供搜索功能,支持通过单据号、客户/供应商名、品名、备注等关键字查询。
|
||||||
|
* 显示当前筛选条件下的总金额。
|
||||||
|
* 当无数据时,提供清晰的空状态提示。
|
||||||
|
* 提供“+”号,支持在当前分类下快速新建单据。
|
||||||
|
|
||||||
|
**2.5 报表统计模块**
|
||||||
|
* **2.5.1 资金报表:**
|
||||||
|
* **利润统计:** 分析指定时间范围内的收入、支出和利润。
|
||||||
|
* **营业员统计:** 按销售人员维度统计销售业绩。
|
||||||
|
* **经营业绩:** 提供综合性的经营状况分析。
|
||||||
|
* **导入导出模块:** 提供导入导出功能方便用户切换手机或账号后仍能将旧数据导入。
|
||||||
|
* **2.5.2 进销存报表:**
|
||||||
|
* **销售统计:** 按商品、客户、时间等维度分析销售数据。
|
||||||
|
* **进货统计:** 按商品、供应商、时间等维度分析采购数据。
|
||||||
|
* **库存统计:** 提供当前库存成本、数量及分布情况的报告。
|
||||||
|
* **应收/应付对账单:** 生成与客户和供应商的对账单据。
|
||||||
|
|
||||||
|
**2.6 “我的”(用户中心)模块**
|
||||||
|
* **2.6.1 用户信息:** 显示用户头像、店铺名称、注册手机号及“老板”身份标识。
|
||||||
|
* **2.6.2 会员与订单:**
|
||||||
|
* 提供**VIP会员**入口,展示会员特权。
|
||||||
|
* 提供**我的订单**入口,可能用于查看应用内服务订单。
|
||||||
|
* **2.6.3 基础管理:**
|
||||||
|
* **供应商管理**
|
||||||
|
* **客户管理**
|
||||||
|
* **客户报价**
|
||||||
|
* **店铺管理**
|
||||||
|
* **2.6.4 设置中心:**
|
||||||
|
* **账号与安全:**
|
||||||
|
* 修改个人信息(头像、姓名)。
|
||||||
|
* 修改登录密码。
|
||||||
|
* **商品设置:**
|
||||||
|
* **系统参数:**
|
||||||
|
* 提供多种业务逻辑开关,如:“销售价低于进货价时提示”、“销售单默认全部收款”、“启用单行折扣”、“启用客户/供应商双身份”。
|
||||||
|
* **关于与协议:** 包含“关于我们”、“隐私协议”、“个人信息安全投诉”等静态页面。
|
||||||
|
* **账号操作:** 提供“账号注销”和“退出登录”功能。
|
||||||
|
|
||||||
|
#### 3.0 全局性需求
|
||||||
|
|
||||||
|
* **3.1 导航:** 采用底部Tab栏导航,包含“首页”、“货品”、“开单”、“明细”、“我的”五个主要模块。
|
||||||
|
* **3.2 统一的UI/UX:** 应用整体风格简洁、清晰,操作流程符合移动端使用习惯。
|
||||||
|
* **3.3 空状态页面:** 在列表、报表等数据为空的页面,需提供友好的空状态提示图和引导性文字。
|
||||||
|
* **3.4 数据同步:** 应用数据应在云端同步,保证用户更换设备或多设备使用时数据一致性。
|
||||||
|
* **3.5 多租户数据隔离:** 所有业务数据按店铺(租户)隔离,用户不可访问他人数据。
|
||||||
|
* 所有业务表需包含`user_id`并在读取/写入中强制按`user_id`过滤。
|
||||||
|
* 支持租户内角色与权限控制;导出仅限本租户数据。
|
||||||
|
* **3.6 公共SKU(全局商品库)众包与审核:** 全体用户共同补充、纠错SKU,经审核发布为全局可选SKU。
|
||||||
|
* 用户可提交“新增SKU/编辑建议”,进入审核流(草稿/待审/驳回/发布/下架)。
|
||||||
|
* 全局SKU字段:名称、品牌、规格、条码、主单位、图片、别名、分类标签等。
|
||||||
|
* 各用户通过“本地商品”引用全局SKU,并保留本地私有字段(价格、库存、分类、单位换算、条码别名等)。
|
||||||
|
* **3.7 商品模糊查询(增强):** 在货品列表、开单选品、对账等场景支持多字段模糊匹配。
|
||||||
|
* 支持名称/条码/别名/拼音/品牌/规格模糊匹配,并高亮命中片段。
|
||||||
|
* 支持全局SKU与本地商品联合检索,优先展示本地商品;结果可分页。
|
||||||
|
* 需满足大规模SKU下的性能目标;可通过系统参数配置匹配策略。
|
||||||
|
|
||||||
|
* **3.8 客户端平台:** 提供移动App与小程序;小程序不支持商品条形码扫描功能。
|
||||||
|
|
||||||
|
* **3.9 多列销售价格:** 销售价格分四列,即同一种商品有四个销售价格
|
||||||
|
|
||||||
|
### 配件查询
|
||||||
|
|
||||||
|
1. **数据查询功能**
|
||||||
|
- 多参数组合查询(分类、尺寸、型号等)
|
||||||
|
- 模糊匹配关键字
|
||||||
|
- 分页展示查询结果
|
||||||
|
- 一键导出Excel数据
|
||||||
|
2. **数据提交系统**
|
||||||
|
- 用户提交新配件数据
|
||||||
|
- 型号为唯一必填项
|
||||||
|
- 支持图片上传
|
||||||
|
- 提交后等待管理员审核
|
||||||
|
3. **审核管理系统**
|
||||||
|
- 管理员查看待审核列表
|
||||||
|
- 可编辑所有字段
|
||||||
|
- 支持图片更新和删除
|
||||||
|
- 一键批准或拒绝提交
|
||||||
|
4. **图片管理系统**
|
||||||
|
- 每条数据可关联多张图片
|
||||||
|
- 点击图片可放大查看
|
||||||
|
- 管理员可管理所有图片
|
||||||
|
- 自动处理文件名冲突
|
||||||
|
|
||||||
|
## 全局说明(必看)
|
||||||
|
|
||||||
|
由于这个文档写的还不是很完善,目前有存疑的部分先行参考小程序小程序“智慧记进销存”(功能和按钮可以参考,界面样式除外),管理端文档目前待定。
|
||||||
|
|
||||||
|
客户要求的是做双端应用(app端+小程序端),需要考虑兼容性相关问题。
|
||||||
|
|
||||||
|
本程序和“智慧记进销存”大多一致,主要的区别在于客户有配件查询要求,即在产品页面中要额外加一个配件查询按钮或入口,且要求一个产品要有四个销售价格(先按零售价 分销价 批发价 大客户价),且要求能自定义添加各种规格(尺寸,孔径等)。
|
||||||
|
|
||||||
|
有疑惑的部分一定要及时沟通(如未提及的页面和功能需要确认的时候)
|
||||||
|
|
||||||
@@ -12,3 +12,16 @@ const storageShopId = typeof uni !== 'undefined' ? (uni.getStorageSync('SHOP_ID'
|
|||||||
export const SHOP_ID = Number(envShopId || storageShopId || 1);
|
export const SHOP_ID = Number(envShopId || storageShopId || 1);
|
||||||
|
|
||||||
|
|
||||||
|
// 默认用户(可移除):
|
||||||
|
// - 用途:开发/演示环境,自动将用户固定为“张老板”(id=2)
|
||||||
|
// - 开关优先级:环境变量 > 本地存储 > 默认值
|
||||||
|
// - 生产默认关闭(false);开发可通过本地存储或环境变量开启
|
||||||
|
const envEnableDefaultUser = (typeof process !== 'undefined' && process.env && (process.env.VITE_APP_ENABLE_DEFAULT_USER || process.env.ENABLE_DEFAULT_USER)) || '';
|
||||||
|
const storageEnableDefaultUser = typeof uni !== 'undefined' ? (uni.getStorageSync('ENABLE_DEFAULT_USER') || '') : '';
|
||||||
|
export const ENABLE_DEFAULT_USER = String(envEnableDefaultUser || storageEnableDefaultUser || 'false').toLowerCase() === 'true';
|
||||||
|
|
||||||
|
const envDefaultUserId = (typeof process !== 'undefined' && process.env && (process.env.VITE_APP_DEFAULT_USER_ID || process.env.DEFAULT_USER_ID)) || '';
|
||||||
|
const storageDefaultUserId = typeof uni !== 'undefined' ? (uni.getStorageSync('DEFAULT_USER_ID') || '') : '';
|
||||||
|
export const DEFAULT_USER_ID = Number(envDefaultUserId || storageDefaultUserId || 2);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
18
frontend/common/constants.js
Normal file
18
frontend/common/constants.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// 统一常量配置:其他收入/支出分类,禁止在业务中硬编码
|
||||||
|
export const INCOME_CATEGORIES = [
|
||||||
|
{ key: 'sale_income', label: '销售收入' },
|
||||||
|
{ key: 'operation_income', label: '经营所得' },
|
||||||
|
{ key: 'interest_income', label: '利息收入' },
|
||||||
|
{ key: 'investment_income', label: '投资收入' },
|
||||||
|
{ key: 'other_income', label: '其它收入' }
|
||||||
|
]
|
||||||
|
|
||||||
|
export const EXPENSE_CATEGORIES = [
|
||||||
|
{ key: 'operation_expense', label: '经营支出' },
|
||||||
|
{ key: 'office_supplies', label: '办公用品' },
|
||||||
|
{ key: 'rent', label: '房租' },
|
||||||
|
{ key: 'interest_expense', label: '利息支出' },
|
||||||
|
{ key: 'other_expense', label: '其它支出' }
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { API_BASE_URL, SHOP_ID } from './config.js'
|
import { API_BASE_URL, SHOP_ID, ENABLE_DEFAULT_USER, DEFAULT_USER_ID } from './config.js'
|
||||||
|
|
||||||
function buildUrl(path) {
|
function buildUrl(path) {
|
||||||
if (!path) return API_BASE_URL
|
if (!path) return API_BASE_URL
|
||||||
@@ -8,11 +8,33 @@ function buildUrl(path) {
|
|||||||
|
|
||||||
export function get(path, params = {}) {
|
export function get(path, params = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const headers = { 'X-Shop-Id': SHOP_ID }
|
||||||
|
if (ENABLE_DEFAULT_USER && DEFAULT_USER_ID) headers['X-User-Id'] = DEFAULT_USER_ID
|
||||||
uni.request({
|
uni.request({
|
||||||
url: buildUrl(path),
|
url: buildUrl(path),
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
data: params,
|
data: params,
|
||||||
header: { 'X-Shop-Id': SHOP_ID },
|
header: headers,
|
||||||
|
success: (res) => {
|
||||||
|
const { statusCode, data } = res
|
||||||
|
if (statusCode >= 200 && statusCode < 300) return resolve(data)
|
||||||
|
reject(new Error('HTTP ' + statusCode))
|
||||||
|
},
|
||||||
|
fail: (err) => reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function post(path, body = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const headers = { 'Content-Type': 'application/json', 'X-Shop-Id': SHOP_ID }
|
||||||
|
if (ENABLE_DEFAULT_USER && DEFAULT_USER_ID) headers['X-User-Id'] = DEFAULT_USER_ID
|
||||||
|
uni.request({
|
||||||
|
url: buildUrl(path),
|
||||||
|
method: 'POST',
|
||||||
|
data: body,
|
||||||
|
header: headers,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
const { statusCode, data } = res
|
const { statusCode, data } = res
|
||||||
if (statusCode >= 200 && statusCode < 300) return resolve(data)
|
if (statusCode >= 200 && statusCode < 300) return resolve(data)
|
||||||
|
|||||||
@@ -5,6 +5,36 @@
|
|||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "五金配件管家"
|
"navigationBarTitleText": "五金配件管家"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/order/create",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "开单"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/product/select",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "选择商品"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/customer/select",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "选择客户"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/supplier/select",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "选择供应商"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/account/select",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "选择账户"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
|
|||||||
45
frontend/pages/account/select.vue
Normal file
45
frontend/pages/account/select.vue
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<scroll-view scroll-y class="list">
|
||||||
|
<view class="item" v-for="a in accounts" :key="a.id" @click="select(a)">
|
||||||
|
<view class="name">{{ a.name }}</view>
|
||||||
|
<view class="meta">{{ typeLabel(a.type) }} · 余额:{{ a.balance?.toFixed ? a.balance.toFixed(2) : a.balance }}</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { get } from '../../common/http.js'
|
||||||
|
const TYPE_MAP = { cash: '现金', bank: '银行', alipay: '支付宝', wechat: '微信', other: '其他' }
|
||||||
|
export default {
|
||||||
|
data() { return { accounts: [] } },
|
||||||
|
async onLoad() {
|
||||||
|
try {
|
||||||
|
const res = await get('/api/accounts')
|
||||||
|
this.accounts = Array.isArray(res) ? res : (res?.list || [])
|
||||||
|
} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
select(a) {
|
||||||
|
const opener = getCurrentPages()[getCurrentPages().length-2]
|
||||||
|
if (opener && opener.$vm) {
|
||||||
|
opener.$vm.selectedAccountId = a.id
|
||||||
|
opener.$vm.selectedAccountName = a.name
|
||||||
|
}
|
||||||
|
uni.navigateBack()
|
||||||
|
},
|
||||||
|
typeLabel(t) { return TYPE_MAP[t] || t }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.page { display:flex; flex-direction: column; height: 100vh; }
|
||||||
|
.list { flex:1; }
|
||||||
|
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }
|
||||||
|
.name { color:#333; margin-bottom: 6rpx; }
|
||||||
|
.meta { color:#888; font-size: 24rpx; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
50
frontend/pages/customer/select.vue
Normal file
50
frontend/pages/customer/select.vue
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<view class="search">
|
||||||
|
<input v-model="kw" placeholder="搜索客户名称/电话" @confirm="search" />
|
||||||
|
<button size="mini" @click="search">搜索</button>
|
||||||
|
</view>
|
||||||
|
<scroll-view scroll-y class="list">
|
||||||
|
<view class="item" v-for="c in customers" :key="c.id" @click="select(c)">
|
||||||
|
<view class="name">{{ c.name }}</view>
|
||||||
|
<view class="meta">{{ c.mobile || '—' }}</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { get } from '../../common/http.js'
|
||||||
|
export default {
|
||||||
|
data() { return { kw: '', customers: [] } },
|
||||||
|
onLoad() { this.search() },
|
||||||
|
methods: {
|
||||||
|
async search() {
|
||||||
|
try {
|
||||||
|
const res = await get('/api/customers', { kw: this.kw, page: 1, size: 50 })
|
||||||
|
this.customers = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
|
||||||
|
} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }
|
||||||
|
},
|
||||||
|
select(c) {
|
||||||
|
const opener = getCurrentPages()[getCurrentPages().length-2]
|
||||||
|
if (opener && opener.$vm) {
|
||||||
|
opener.$vm.order.customerId = c.id
|
||||||
|
opener.$vm.customerName = c.name
|
||||||
|
}
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.page { display:flex; flex-direction: column; height: 100vh; }
|
||||||
|
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }
|
||||||
|
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }
|
||||||
|
.list { flex:1; }
|
||||||
|
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }
|
||||||
|
.name { color:#333; margin-bottom: 6rpx; }
|
||||||
|
.meta { color:#888; font-size: 24rpx; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -12,22 +12,26 @@
|
|||||||
<view class="kpi">
|
<view class="kpi">
|
||||||
<view class="kpi-item">
|
<view class="kpi-item">
|
||||||
<text class="kpi-label">今日销售额</text>
|
<text class="kpi-label">今日销售额</text>
|
||||||
<text class="kpi-value">{{ todayAmount }}</text>
|
<text class="kpi-value">{{ kpi.todaySales }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="kpi-item">
|
||||||
|
<text class="kpi-label">本月销售额</text>
|
||||||
|
<text class="kpi-value">{{ kpi.monthSales }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="kpi-item">
|
<view class="kpi-item">
|
||||||
<text class="kpi-label">本月利润</text>
|
<text class="kpi-label">本月利润</text>
|
||||||
<text class="kpi-value">{{ monthProfit }}</text>
|
<text class="kpi-value">{{ kpi.monthProfit }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="kpi-item">
|
<view class="kpi-item">
|
||||||
<text class="kpi-label">库存数量</text>
|
<text class="kpi-label">库存商品数量</text>
|
||||||
<text class="kpi-value">{{ stockQty }}</text>
|
<text class="kpi-value">{{ kpi.stockCount }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 公告栏:自动轮播,可点击查看详情 -->
|
<!-- 广告栏:自动轮播,可点击查看详情 -->
|
||||||
<view class="notice">
|
<view class="notice">
|
||||||
<view class="notice-left">公告</view>
|
<view class="notice-left">广告</view>
|
||||||
<view v-if="loadingNotices" class="notice-swiper" style="display:flex;align-items:center;color:#6b5a2a;">加载中...</view>
|
<view v-if="loadingNotices" class="notice-swiper" style="display:flex;align-items:center;color:#6b5a2a;">加载中...</view>
|
||||||
<view v-else-if="noticeError" class="notice-swiper" style="display:flex;align-items:center;color:#dd524d;">{{ noticeError }}</view>
|
<view v-else-if="noticeError" class="notice-swiper" style="display:flex;align-items:center;color:#dd524d;">{{ noticeError }}</view>
|
||||||
<view v-else-if="!notices.length" class="notice-swiper" style="display:flex;align-items:center;color:#6b5a2a;">暂无公告</view>
|
<view v-else-if="!notices.length" class="notice-swiper" style="display:flex;align-items:center;color:#6b5a2a;">暂无公告</view>
|
||||||
@@ -39,7 +43,6 @@
|
|||||||
</view>
|
</view>
|
||||||
</swiper-item>
|
</swiper-item>
|
||||||
</swiper>
|
</swiper>
|
||||||
<view class="notice-right" @click="onNoticeList">更多</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 分割标题:产品与功能 -->
|
<!-- 分割标题:产品与功能 -->
|
||||||
@@ -66,12 +69,18 @@
|
|||||||
<view class="tab" :class="{ active: activeTab==='home' }" @click="activeTab='home'">
|
<view class="tab" :class="{ active: activeTab==='home' }" @click="activeTab='home'">
|
||||||
<text>首页</text>
|
<text>首页</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="tab" :class="{ active: activeTab==='product' }" @click="activeTab='product'">
|
||||||
|
<text>货品</text>
|
||||||
|
</view>
|
||||||
<view class="tab primary" @click="onCreateOrder">
|
<view class="tab primary" @click="onCreateOrder">
|
||||||
<text>开单</text>
|
<text>开单</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="tab" :class="{ active: activeTab==='detail' }" @click="activeTab='detail'">
|
<view class="tab" :class="{ active: activeTab==='detail' }" @click="activeTab='detail'">
|
||||||
<text>明细</text>
|
<text>明细</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="tab" :class="{ active: activeTab==='report' }" @click="activeTab='report'">
|
||||||
|
<text>报表</text>
|
||||||
|
</view>
|
||||||
<view class="tab" :class="{ active: activeTab==='me' }" @click="activeTab='me'">
|
<view class="tab" :class="{ active: activeTab==='me' }" @click="activeTab='me'">
|
||||||
<text>我的</text>
|
<text>我的</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -84,9 +93,7 @@
|
|||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
todayAmount: '0.00',
|
kpi: { todaySales: '0.00', monthSales: '0.00', monthProfit: '0.00', stockCount: '0' },
|
||||||
monthProfit: '0.00',
|
|
||||||
stockQty: '0.00',
|
|
||||||
activeTab: 'home',
|
activeTab: 'home',
|
||||||
notices: [],
|
notices: [],
|
||||||
loadingNotices: false,
|
loadingNotices: false,
|
||||||
@@ -105,9 +112,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
this.fetchMetrics()
|
||||||
this.fetchNotices()
|
this.fetchNotices()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async fetchMetrics() {
|
||||||
|
try {
|
||||||
|
const d = await get('/api/metrics/overview')
|
||||||
|
this.kpi = {
|
||||||
|
todaySales: (d && d.todaySales) || '0.00',
|
||||||
|
monthSales: (d && d.monthSales) || '0.00',
|
||||||
|
monthProfit: (d && d.monthProfit) || '0.00',
|
||||||
|
stockCount: (d && d.stockCount) || '0'
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 忽略错误,保留默认值
|
||||||
|
}
|
||||||
|
},
|
||||||
async fetchNotices() {
|
async fetchNotices() {
|
||||||
this.loadingNotices = true
|
this.loadingNotices = true
|
||||||
this.noticeError = ''
|
this.noticeError = ''
|
||||||
@@ -127,18 +148,16 @@
|
|||||||
uni.showToast({ title: item.title + '(开发中)', icon: 'none' })
|
uni.showToast({ title: item.title + '(开发中)', icon: 'none' })
|
||||||
},
|
},
|
||||||
onCreateOrder() {
|
onCreateOrder() {
|
||||||
uni.showToast({ title: '开单(开发中)', icon: 'none' })
|
uni.navigateTo({ url: '/pages/order/create' })
|
||||||
},
|
},
|
||||||
onNoticeTap(n) {
|
onNoticeTap(n) {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '公告',
|
title: '广告',
|
||||||
content: n && (n.text || n.title || n.content) || '',
|
content: n && (n.text || n.title || n.content) || '',
|
||||||
showCancel: false
|
showCancel: false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onNoticeList() {
|
|
||||||
uni.showToast({ title: '公告列表(开发中)', icon: 'none' })
|
|
||||||
},
|
|
||||||
onIconError(item) {
|
onIconError(item) {
|
||||||
item.img = ''
|
item.img = ''
|
||||||
}
|
}
|
||||||
@@ -192,7 +211,7 @@
|
|||||||
.notice-item { display: flex; align-items: center; gap: 12rpx; min-height: 72rpx; }
|
.notice-item { display: flex; align-items: center; gap: 12rpx; min-height: 72rpx; }
|
||||||
.notice-text { color: #4b3e19; font-size: 28rpx; line-height: 36rpx; font-weight: 600; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
.notice-text { color: #4b3e19; font-size: 28rpx; line-height: 36rpx; font-weight: 600; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
||||||
.notice-tag { color: #B4880F; font-size: 22rpx; padding: 4rpx 10rpx; border-radius: 999rpx; background: rgba(215,167,46,0.18); }
|
.notice-tag { color: #B4880F; font-size: 22rpx; padding: 4rpx 10rpx; border-radius: 999rpx; background: rgba(215,167,46,0.18); }
|
||||||
.notice-right { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; min-width: 72rpx; height: 44rpx; color: #B4880F; font-size: 26rpx; padding-left: 8rpx; }
|
|
||||||
|
|
||||||
/* 分割标题 */
|
/* 分割标题 */
|
||||||
.section-title { display: flex; align-items: center; gap: 16rpx; padding: 10rpx 28rpx 0; }
|
.section-title { display: flex; align-items: center; gap: 16rpx; padding: 10rpx 28rpx 0; }
|
||||||
|
|||||||
224
frontend/pages/order/create.vue
Normal file
224
frontend/pages/order/create.vue
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
<template>
|
||||||
|
<view class="order">
|
||||||
|
<!-- 顶部 Tab -->
|
||||||
|
<view class="tabs">
|
||||||
|
<text :class="{ active: biz==='sale' }" @click="switchBiz('sale')">销售</text>
|
||||||
|
<text :class="{ active: biz==='purchase' }" @click="switchBiz('purchase')">进货</text>
|
||||||
|
<text :class="{ active: biz==='income' }" @click="switchBiz('income')">其他收入</text>
|
||||||
|
<text :class="{ active: biz==='expense' }" @click="switchBiz('expense')">其他支出</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 子类目按钮 -->
|
||||||
|
<view class="subtabs" v-if="biz==='sale'">
|
||||||
|
<button class="subbtn" :class="{ active: saleType==='out' }" @click="saleType='out'">出货</button>
|
||||||
|
<button class="subbtn" :class="{ active: saleType==='return' }" @click="saleType='return'">退货</button>
|
||||||
|
<button class="subbtn" :class="{ active: saleType==='collect' }" @click="saleType='collect'">收款</button>
|
||||||
|
</view>
|
||||||
|
<view class="subtabs" v-else-if="biz==='purchase'">
|
||||||
|
<button class="subbtn" :class="{ active: purchaseType==='in' }" @click="purchaseType='in'">进货</button>
|
||||||
|
<button class="subbtn" :class="{ active: purchaseType==='return' }" @click="purchaseType='return'">退货</button>
|
||||||
|
<button class="subbtn" :class="{ active: purchaseType==='pay' }" @click="purchaseType='pay'">付款</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 日期与客户 -->
|
||||||
|
<picker mode="date" :value="order.orderTime" @change="onDateChange">
|
||||||
|
<view class="field">
|
||||||
|
<text class="label">时间</text>
|
||||||
|
<text class="value">{{ order.orderTime }}</text>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
<view class="field" v-if="biz==='sale'" @click="chooseCustomer">
|
||||||
|
<text class="label">客户</text>
|
||||||
|
<text class="value">{{ customerLabel }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="field" v-else-if="biz==='purchase'" @click="chooseSupplier">
|
||||||
|
<text class="label">供应商</text>
|
||||||
|
<text class="value">{{ supplierLabel }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 已选商品与合计(销售/进货) -->
|
||||||
|
<view v-if="biz==='sale' || biz==='purchase'">
|
||||||
|
<view class="summary">
|
||||||
|
<text>选中货品({{ totalQuantity }})</text>
|
||||||
|
<text>合计金额:¥ {{ totalAmount.toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加号添加商品 -->
|
||||||
|
<view class="add" @click="chooseProduct">+</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 其它收入/支出 表单 -->
|
||||||
|
<view v-else>
|
||||||
|
<view class="chips">
|
||||||
|
<view v-for="c in (biz==='income' ? incomeCategories : expenseCategories)" :key="c.key" class="chip" :class="{ active: activeCategory===c.key }" @click="activeCategory=c.key">{{ c.label }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="field" @click="chooseCounterparty">
|
||||||
|
<text class="label">往来单位</text>
|
||||||
|
<text class="value">{{ counterpartyLabel }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="field" @click="chooseAccount">
|
||||||
|
<text class="label">结算账户</text>
|
||||||
|
<text class="value">{{ accountLabel }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="field">
|
||||||
|
<text class="label">金额</text>
|
||||||
|
<input class="value" type="digit" v-model.number="trxAmount" placeholder="0.00" />
|
||||||
|
</view>
|
||||||
|
<view class="textarea">
|
||||||
|
<textarea v-model="order.remark" maxlength="200" placeholder="备注(最多输入200个字)"></textarea>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 购物车空态 -->
|
||||||
|
<view class="empty" v-if="!items.length">
|
||||||
|
<image src="/static/logo.png" mode="widthFix" class="empty-img"></image>
|
||||||
|
<text class="empty-text">购物车里空空如也</text>
|
||||||
|
<text class="empty-sub">扫描或点击 “+” 选择商品吧</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 商品列表 -->
|
||||||
|
<view v-else class="list">
|
||||||
|
<view class="row" v-for="(it, idx) in items" :key="idx">
|
||||||
|
<view class="col name">{{ it.productName }}</view>
|
||||||
|
<view class="col qty">
|
||||||
|
<input type="number" v-model.number="it.quantity" @input="recalc()" />
|
||||||
|
</view>
|
||||||
|
<view class="col price">
|
||||||
|
<input type="number" v-model.number="it.unitPrice" @input="recalc()" />
|
||||||
|
</view>
|
||||||
|
<view class="col amount">¥ {{ (Number(it.quantity)*Number(it.unitPrice)).toFixed(2) }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部提交栏 -->
|
||||||
|
<view class="bottom">
|
||||||
|
<button class="ghost" @click="saveAndReset">再记一笔</button>
|
||||||
|
<button class="primary" @click="submit">保存</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { post } from '../../common/http.js'
|
||||||
|
import { INCOME_CATEGORIES, EXPENSE_CATEGORIES } from '../../common/constants.js'
|
||||||
|
|
||||||
|
function todayString() {
|
||||||
|
const d = new Date()
|
||||||
|
const m = (d.getMonth()+1).toString().padStart(2,'0')
|
||||||
|
const day = d.getDate().toString().padStart(2,'0')
|
||||||
|
return `${d.getFullYear()}-${m}-${day}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
biz: 'sale',
|
||||||
|
saleType: 'out',
|
||||||
|
purchaseType: 'in',
|
||||||
|
order: {
|
||||||
|
orderTime: todayString(),
|
||||||
|
customerId: null,
|
||||||
|
supplierId: null,
|
||||||
|
remark: ''
|
||||||
|
},
|
||||||
|
customerName: '',
|
||||||
|
supplierName: '',
|
||||||
|
items: [],
|
||||||
|
activeCategory: 'sale_income',
|
||||||
|
trxAmount: 0,
|
||||||
|
selectedAccountId: null,
|
||||||
|
selectedAccountName: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
totalQuantity() {
|
||||||
|
return this.items.reduce((s, it) => s + Number(it.quantity || 0), 0)
|
||||||
|
},
|
||||||
|
totalAmount() {
|
||||||
|
return this.items.reduce((s, it) => s + Number(it.quantity || 0) * Number(it.unitPrice || 0), 0)
|
||||||
|
},
|
||||||
|
customerLabel() { return this.customerName || '零售客户' },
|
||||||
|
supplierLabel() { return this.supplierName || '零散供应商' },
|
||||||
|
incomeCategories() { return INCOME_CATEGORIES },
|
||||||
|
expenseCategories() { return EXPENSE_CATEGORIES },
|
||||||
|
accountLabel() { return this.selectedAccountName || '现金' },
|
||||||
|
counterpartyLabel() { return this.customerName || this.supplierName || '—' }
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
switchBiz(type) { this.biz = type },
|
||||||
|
onDateChange(e) { this.order.orderTime = e.detail.value },
|
||||||
|
chooseCustomer() {
|
||||||
|
uni.navigateTo({ url: '/pages/customer/select' })
|
||||||
|
},
|
||||||
|
chooseSupplier() { uni.navigateTo({ url: '/pages/supplier/select' }) },
|
||||||
|
chooseProduct() {
|
||||||
|
uni.navigateTo({ url: '/pages/product/select' })
|
||||||
|
},
|
||||||
|
chooseAccount() { uni.navigateTo({ url: '/pages/account/select' }) },
|
||||||
|
chooseCounterparty() {
|
||||||
|
if (this.biz==='income' || this.biz==='expense') {
|
||||||
|
uni.navigateTo({ url: '/pages/customer/select' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
recalc() { 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
|
||||||
|
} : {
|
||||||
|
type: this.biz,
|
||||||
|
category: this.activeCategory,
|
||||||
|
counterpartyId: this.order.customerId || null,
|
||||||
|
accountId: this.selectedAccountId || null,
|
||||||
|
amount: Number(this.trxAmount||0),
|
||||||
|
txTime: this.order.orderTime,
|
||||||
|
remark: this.order.remark
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const url = isSaleOrPurchase ? '/api/orders' : '/api/other-transactions'
|
||||||
|
await post(url, payload)
|
||||||
|
uni.showToast({ title: '已保存', icon: 'success' })
|
||||||
|
setTimeout(() => { uni.navigateBack() }, 600)
|
||||||
|
} catch (e) {
|
||||||
|
uni.showToast({ title: e && e.message || '保存失败', icon: 'none' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveAndReset() {
|
||||||
|
this.items = []
|
||||||
|
this.trxAmount = 0
|
||||||
|
this.order.remark = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.order { padding-bottom: 140rpx; }
|
||||||
|
.tabs { display: flex; justify-content: space-around; padding: 16rpx 24rpx; }
|
||||||
|
.tabs text { color: #666; }
|
||||||
|
.tabs text.active { color: #333; font-weight: 700; }
|
||||||
|
.subtabs { display: flex; gap: 16rpx; padding: 0 24rpx 16rpx; }
|
||||||
|
.subbtn { padding: 10rpx 20rpx; border-radius: 999rpx; background: #f4f4f4; color: #666; }
|
||||||
|
.subbtn.active { background: #ffe69a; color: #3f320f; }
|
||||||
|
.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background: #fff; border-bottom: 1rpx solid #eee; }
|
||||||
|
.label { color:#666; }
|
||||||
|
.value { color:#333; }
|
||||||
|
.summary { display:flex; justify-content: space-between; padding: 22rpx 24rpx; color:#333; }
|
||||||
|
.add { margin: 24rpx auto; width: 120rpx; height: 120rpx; border-radius: 20rpx; background: #c7eef7; color:#16a1c4; font-size: 72rpx; display:flex; align-items:center; justify-content:center; }
|
||||||
|
.empty { display:flex; flex-direction: column; align-items:center; padding: 60rpx 0; color:#888; }
|
||||||
|
.empty-img { width: 220rpx; margin-bottom: 20rpx; }
|
||||||
|
.empty-text { margin-bottom: 8rpx; }
|
||||||
|
.list { background:#fff; }
|
||||||
|
.row { display:grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 12rpx; padding: 16rpx 12rpx; align-items:center; border-bottom: 1rpx solid #f3f3f3; }
|
||||||
|
.col.name { padding-left: 12rpx; }
|
||||||
|
.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; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
49
frontend/pages/product/select.vue
Normal file
49
frontend/pages/product/select.vue
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<view class="search">
|
||||||
|
<input v-model="kw" placeholder="搜索商品名称/编码" @confirm="search" />
|
||||||
|
<button size="mini" @click="search">搜索</button>
|
||||||
|
</view>
|
||||||
|
<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>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { get } from '../../common/http.js'
|
||||||
|
export default {
|
||||||
|
data() { return { kw: '', products: [] } },
|
||||||
|
onLoad() { this.search() },
|
||||||
|
methods: {
|
||||||
|
async search() {
|
||||||
|
try {
|
||||||
|
const res = await get('/api/products', { kw: this.kw, page: 1, size: 50 })
|
||||||
|
this.products = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
|
||||||
|
} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }
|
||||||
|
},
|
||||||
|
select(p) {
|
||||||
|
const opener = getCurrentPages()[getCurrentPages().length-2]
|
||||||
|
if (opener && opener.$vm && opener.$vm.items) {
|
||||||
|
opener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: Number(p.price || 0) })
|
||||||
|
}
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.page { display:flex; flex-direction: column; height: 100vh; }
|
||||||
|
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }
|
||||||
|
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }
|
||||||
|
.list { flex:1; }
|
||||||
|
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }
|
||||||
|
.name { color:#333; margin-bottom: 6rpx; }
|
||||||
|
.meta { color:#888; font-size: 24rpx; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
50
frontend/pages/supplier/select.vue
Normal file
50
frontend/pages/supplier/select.vue
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<view class="search">
|
||||||
|
<input v-model="kw" placeholder="搜索供应商名称/电话" @confirm="search" />
|
||||||
|
<button size="mini" @click="search">搜索</button>
|
||||||
|
</view>
|
||||||
|
<scroll-view scroll-y class="list">
|
||||||
|
<view class="item" v-for="s in suppliers" :key="s.id" @click="select(s)">
|
||||||
|
<view class="name">{{ s.name }}</view>
|
||||||
|
<view class="meta">{{ s.mobile || '—' }}</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { get } from '../../common/http.js'
|
||||||
|
export default {
|
||||||
|
data() { return { kw: '', suppliers: [] } },
|
||||||
|
onLoad() { this.search() },
|
||||||
|
methods: {
|
||||||
|
async search() {
|
||||||
|
try {
|
||||||
|
const res = await get('/api/suppliers', { kw: this.kw, page: 1, size: 50 })
|
||||||
|
this.suppliers = Array.isArray(res?.list) ? res.list : (Array.isArray(res) ? res : [])
|
||||||
|
} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }
|
||||||
|
},
|
||||||
|
select(s) {
|
||||||
|
const opener = getCurrentPages()[getCurrentPages().length-2]
|
||||||
|
if (opener && opener.$vm) {
|
||||||
|
opener.$vm.order.supplierId = s.id
|
||||||
|
opener.$vm.supplierName = s.name
|
||||||
|
}
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.page { display:flex; flex-direction: column; height: 100vh; }
|
||||||
|
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff; }
|
||||||
|
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx; }
|
||||||
|
.list { flex:1; }
|
||||||
|
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1; }
|
||||||
|
.name { color:#333; margin-bottom: 6rpx; }
|
||||||
|
.meta { color:#888; font-size: 24rpx; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -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"],"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;;;"}
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"assets.js","sources":["../../../../../../static/metal-bg.jpg"],"sourcesContent":["export default \"/static/metal-bg.jpg\""],"names":[],"mappings":";AAAA,MAAe,aAAA;;"}
|
{"version":3,"file":"assets.js","sources":["../../../../../../static/metal-bg.jpg","static/logo.png"],"sourcesContent":["export default \"/static/metal-bg.jpg\"","export default \"__VITE_ASSET__46719607__\""],"names":[],"mappings":";AAAA,MAAe,eAAA;ACAf,MAAe,aAAA;;;"}
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"config.js","sources":["common/config.js"],"sourcesContent":["// 统一配置:禁止在业务代码中硬编码\r\n// 优先级:环境变量(Vite/HBuilderX 构建注入) > 本地存储 > 默认值\r\n\r\nconst envBaseUrl = (typeof process !== 'undefined' && process.env && (process.env.VITE_APP_API_BASE_URL || process.env.API_BASE_URL)) || '';\r\nconst storageBaseUrl = typeof uni !== 'undefined' ? (uni.getStorageSync('API_BASE_URL') || '') : '';\r\nconst fallbackBaseUrl = 'http://localhost:8080';\r\n\r\nexport const API_BASE_URL = (envBaseUrl || storageBaseUrl || fallbackBaseUrl).replace(/\\/$/, '');\r\n\r\nconst envShopId = (typeof process !== 'undefined' && process.env && (process.env.VITE_APP_SHOP_ID || process.env.SHOP_ID)) || '';\r\nconst storageShopId = typeof uni !== 'undefined' ? (uni.getStorageSync('SHOP_ID') || '') : '';\r\nexport const SHOP_ID = Number(envShopId || storageShopId || 1);\r\n\r\n\r\n"],"names":["uni"],"mappings":";;AAGA,MAAM,aAAc,OAAO,YAAY,eAAe,QAAQ,QAAQ,QAAQ,IAAI,yBAAyB,QAAQ,IAAI,iBAAkB;AACzI,MAAM,iBAAiB,OAAOA,cAAG,UAAK,cAAeA,cAAAA,MAAI,eAAe,cAAc,KAAK,KAAM;AACjG,MAAM,kBAAkB;AAEZ,MAAC,gBAAgB,cAAc,kBAAkB,iBAAiB,QAAQ,OAAO,EAAE;AAE/F,MAAM,YAAa,OAAO,YAAY,eAAe,QAAQ,QAAQ,QAAQ,IAAI,oBAAoB,QAAQ,IAAI,YAAa;AAC9H,MAAM,gBAAgB,OAAOA,cAAG,UAAK,cAAeA,cAAAA,MAAI,eAAe,SAAS,KAAK,KAAM;AAC/E,MAAC,UAAU,OAAO,aAAa,iBAAiB,CAAC;;;"}
|
{"version":3,"file":"config.js","sources":["common/config.js"],"sourcesContent":["// 统一配置:禁止在业务代码中硬编码\n// 优先级:环境变量(Vite/HBuilderX 构建注入) > 本地存储 > 默认值\n\nconst envBaseUrl = (typeof process !== 'undefined' && process.env && (process.env.VITE_APP_API_BASE_URL || process.env.API_BASE_URL)) || '';\nconst storageBaseUrl = typeof uni !== 'undefined' ? (uni.getStorageSync('API_BASE_URL') || '') : '';\nconst fallbackBaseUrl = 'http://localhost:8080';\n\nexport const API_BASE_URL = (envBaseUrl || storageBaseUrl || fallbackBaseUrl).replace(/\\/$/, '');\n\nconst envShopId = (typeof process !== 'undefined' && process.env && (process.env.VITE_APP_SHOP_ID || process.env.SHOP_ID)) || '';\nconst storageShopId = typeof uni !== 'undefined' ? (uni.getStorageSync('SHOP_ID') || '') : '';\nexport const SHOP_ID = Number(envShopId || storageShopId || 1);\n\n\n// 默认用户(可移除):\n// - 用途:开发/演示环境,自动将用户固定为“张老板”(id=2)\n// - 开关优先级:环境变量 > 本地存储 > 默认值\n// - 生产默认关闭(false);开发可通过本地存储或环境变量开启\nconst envEnableDefaultUser = (typeof process !== 'undefined' && process.env && (process.env.VITE_APP_ENABLE_DEFAULT_USER || process.env.ENABLE_DEFAULT_USER)) || '';\nconst storageEnableDefaultUser = typeof uni !== 'undefined' ? (uni.getStorageSync('ENABLE_DEFAULT_USER') || '') : '';\nexport const ENABLE_DEFAULT_USER = String(envEnableDefaultUser || storageEnableDefaultUser || 'false').toLowerCase() === 'true';\n\nconst envDefaultUserId = (typeof process !== 'undefined' && process.env && (process.env.VITE_APP_DEFAULT_USER_ID || process.env.DEFAULT_USER_ID)) || '';\nconst storageDefaultUserId = typeof uni !== 'undefined' ? (uni.getStorageSync('DEFAULT_USER_ID') || '') : '';\nexport const DEFAULT_USER_ID = Number(envDefaultUserId || storageDefaultUserId || 2);\n\n\n"],"names":["uni"],"mappings":";;AAGA,MAAM,aAAc,OAAO,YAAY,eAAe,QAAQ,QAAQ,QAAQ,IAAI,yBAAyB,QAAQ,IAAI,iBAAkB;AACzI,MAAM,iBAAiB,OAAOA,cAAG,UAAK,cAAeA,cAAAA,MAAI,eAAe,cAAc,KAAK,KAAM;AACjG,MAAM,kBAAkB;AAEZ,MAAC,gBAAgB,cAAc,kBAAkB,iBAAiB,QAAQ,OAAO,EAAE;AAE/F,MAAM,YAAa,OAAO,YAAY,eAAe,QAAQ,QAAQ,QAAQ,IAAI,oBAAoB,QAAQ,IAAI,YAAa;AAC9H,MAAM,gBAAgB,OAAOA,cAAG,UAAK,cAAeA,cAAAA,MAAI,eAAe,SAAS,KAAK,KAAM;AAC/E,MAAC,UAAU,OAAO,aAAa,iBAAiB,CAAC;AAO7D,MAAM,uBAAwB,OAAO,YAAY,eAAe,QAAQ,QAAQ,QAAQ,IAAI,gCAAgC,QAAQ,IAAI,wBAAyB;AACjK,MAAM,2BAA2B,OAAOA,cAAG,UAAK,cAAeA,cAAAA,MAAI,eAAe,qBAAqB,KAAK,KAAM;AACtG,MAAC,sBAAsB,OAAO,wBAAwB,4BAA4B,OAAO,EAAE,YAAW,MAAO;AAEzH,MAAM,mBAAoB,OAAO,YAAY,eAAe,QAAQ,QAAQ,QAAQ,IAAI,4BAA4B,QAAQ,IAAI,oBAAqB;AACrJ,MAAM,uBAAuB,OAAOA,cAAG,UAAK,cAAeA,cAAAA,MAAI,eAAe,iBAAiB,KAAK,KAAM;AAC9F,MAAC,kBAAkB,OAAO,oBAAoB,wBAAwB,CAAC;;;;;"}
|
||||||
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/common/constants.js.map
vendored
Normal file
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/common/constants.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"constants.js","sources":["common/constants.js"],"sourcesContent":["// 统一常量配置:其他收入/支出分类,禁止在业务中硬编码\r\nexport const INCOME_CATEGORIES = [\r\n\t{ key: 'sale_income', label: '销售收入' },\r\n\t{ key: 'operation_income', label: '经营所得' },\r\n\t{ key: 'interest_income', label: '利息收入' },\r\n\t{ key: 'investment_income', label: '投资收入' },\r\n\t{ key: 'other_income', label: '其它收入' }\r\n]\r\n\r\nexport const EXPENSE_CATEGORIES = [\r\n\t{ key: 'operation_expense', label: '经营支出' },\r\n\t{ key: 'office_supplies', label: '办公用品' },\r\n\t{ key: 'rent', label: '房租' },\r\n\t{ key: 'interest_expense', label: '利息支出' },\r\n\t{ key: 'other_expense', label: '其它支出' }\r\n]\r\n\r\n\r\n"],"names":[],"mappings":";AACY,MAAC,oBAAoB;AAAA,EAChC,EAAE,KAAK,eAAe,OAAO,OAAQ;AAAA,EACrC,EAAE,KAAK,oBAAoB,OAAO,OAAQ;AAAA,EAC1C,EAAE,KAAK,mBAAmB,OAAO,OAAQ;AAAA,EACzC,EAAE,KAAK,qBAAqB,OAAO,OAAQ;AAAA,EAC3C,EAAE,KAAK,gBAAgB,OAAO,OAAQ;AACvC;AAEY,MAAC,qBAAqB;AAAA,EACjC,EAAE,KAAK,qBAAqB,OAAO,OAAQ;AAAA,EAC3C,EAAE,KAAK,mBAAmB,OAAO,OAAQ;AAAA,EACzC,EAAE,KAAK,QAAQ,OAAO,KAAM;AAAA,EAC5B,EAAE,KAAK,oBAAoB,OAAO,OAAQ;AAAA,EAC1C,EAAE,KAAK,iBAAiB,OAAO,OAAQ;AACxC;;;"}
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"http.js","sources":["common/http.js"],"sourcesContent":["import { API_BASE_URL, SHOP_ID } from './config.js'\r\n\r\nfunction buildUrl(path) {\r\n if (!path) return API_BASE_URL\r\n if (path.startsWith('http')) return path\r\n return API_BASE_URL + (path.startsWith('/') ? path : '/' + path)\r\n}\r\n\r\nexport function get(path, params = {}) {\r\n return new Promise((resolve, reject) => {\r\n uni.request({\r\n url: buildUrl(path),\r\n method: 'GET',\r\n data: params,\r\n header: { 'X-Shop-Id': SHOP_ID },\r\n success: (res) => {\r\n const { statusCode, data } = res\r\n if (statusCode >= 200 && statusCode < 300) return resolve(data)\r\n reject(new Error('HTTP ' + statusCode))\r\n },\r\n fail: (err) => reject(err)\r\n })\r\n })\r\n}\r\n\r\n\r\n"],"names":["API_BASE_URL","uni","SHOP_ID"],"mappings":";;;AAEA,SAAS,SAAS,MAAM;AACtB,MAAI,CAAC;AAAM,WAAOA,cAAY;AAC9B,MAAI,KAAK,WAAW,MAAM;AAAG,WAAO;AACpC,SAAOA,cAAAA,gBAAgB,KAAK,WAAW,GAAG,IAAI,OAAO,MAAM;AAC7D;AAEO,SAAS,IAAI,MAAM,SAAS,IAAI;AACrC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtCC,kBAAAA,MAAI,QAAQ;AAAA,MACV,KAAK,SAAS,IAAI;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,QAAQ,EAAE,aAAaC,sBAAS;AAAA,MAChC,SAAS,CAAC,QAAQ;AAChB,cAAM,EAAE,YAAY,KAAI,IAAK;AAC7B,YAAI,cAAc,OAAO,aAAa;AAAK,iBAAO,QAAQ,IAAI;AAC9D,eAAO,IAAI,MAAM,UAAU,UAAU,CAAC;AAAA,MACvC;AAAA,MACD,MAAM,CAAC,QAAQ,OAAO,GAAG;AAAA,IAC/B,CAAK;AAAA,EACL,CAAG;AACH;;"}
|
{"version":3,"file":"http.js","sources":["common/http.js"],"sourcesContent":["import { API_BASE_URL, SHOP_ID, ENABLE_DEFAULT_USER, DEFAULT_USER_ID } from './config.js'\n\nfunction buildUrl(path) {\n if (!path) return API_BASE_URL\n if (path.startsWith('http')) return path\n return API_BASE_URL + (path.startsWith('/') ? path : '/' + path)\n}\n\nexport function get(path, params = {}) {\n return new Promise((resolve, reject) => {\n const headers = { 'X-Shop-Id': SHOP_ID }\n if (ENABLE_DEFAULT_USER && DEFAULT_USER_ID) headers['X-User-Id'] = DEFAULT_USER_ID\n uni.request({\n url: buildUrl(path),\n method: 'GET',\n data: params,\n header: headers,\n success: (res) => {\n const { statusCode, data } = res\n if (statusCode >= 200 && statusCode < 300) return resolve(data)\n reject(new Error('HTTP ' + statusCode))\n },\n fail: (err) => reject(err)\n })\n })\n}\n\n\nexport function post(path, body = {}) {\n return new Promise((resolve, reject) => {\n const headers = { 'Content-Type': 'application/json', 'X-Shop-Id': SHOP_ID }\n if (ENABLE_DEFAULT_USER && DEFAULT_USER_ID) headers['X-User-Id'] = DEFAULT_USER_ID\n uni.request({\n url: buildUrl(path),\n method: 'POST',\n data: body,\n header: headers,\n success: (res) => {\n const { statusCode, data } = res\n if (statusCode >= 200 && statusCode < 300) return resolve(data)\n reject(new Error('HTTP ' + statusCode))\n },\n fail: (err) => reject(err)\n })\n })\n}\n\n\n"],"names":["API_BASE_URL","SHOP_ID","ENABLE_DEFAULT_USER","DEFAULT_USER_ID","uni"],"mappings":";;;AAEA,SAAS,SAAS,MAAM;AACtB,MAAI,CAAC;AAAM,WAAOA,cAAY;AAC9B,MAAI,KAAK,WAAW,MAAM;AAAG,WAAO;AACpC,SAAOA,cAAAA,gBAAgB,KAAK,WAAW,GAAG,IAAI,OAAO,MAAM;AAC7D;AAEO,SAAS,IAAI,MAAM,SAAS,IAAI;AACrC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,UAAU,EAAE,aAAaC,sBAAS;AACxC,QAAIC,cAAmB,uBAAIC,cAAe;AAAE,cAAQ,WAAW,IAAIA,cAAe;AAClFC,kBAAAA,MAAI,QAAQ;AAAA,MACV,KAAK,SAAS,IAAI;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS,CAAC,QAAQ;AAChB,cAAM,EAAE,YAAY,KAAI,IAAK;AAC7B,YAAI,cAAc,OAAO,aAAa;AAAK,iBAAO,QAAQ,IAAI;AAC9D,eAAO,IAAI,MAAM,UAAU,UAAU,CAAC;AAAA,MACvC;AAAA,MACD,MAAM,CAAC,QAAQ,OAAO,GAAG;AAAA,IAC/B,CAAK;AAAA,EACL,CAAG;AACH;AAGO,SAAS,KAAK,MAAM,OAAO,IAAI;AACpC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,UAAU,EAAE,gBAAgB,oBAAoB,aAAaH,cAAAA,QAAS;AAC5E,QAAIC,cAAmB,uBAAIC,cAAe;AAAE,cAAQ,WAAW,IAAIA,cAAe;AAClFC,kBAAAA,MAAI,QAAQ;AAAA,MACV,KAAK,SAAS,IAAI;AAAA,MAClB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS,CAAC,QAAQ;AAChB,cAAM,EAAE,YAAY,KAAI,IAAK;AAC7B,YAAI,cAAc,OAAO,aAAa;AAAK,iBAAO,QAAQ,IAAI;AAC9D,eAAO,IAAI,MAAM,UAAU,UAAU,CAAC;AAAA,MACvC;AAAA,MACD,MAAM,CAAC,QAAQ,OAAO,GAAG;AAAA,IAC/B,CAAK;AAAA,EACL,CAAG;AACH;;;"}
|
||||||
File diff suppressed because one or more lines are too long
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/account/select.js.map
vendored
Normal file
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/account/select.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"select.js","sources":["pages/account/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvYWNjb3VudC9zZWxlY3QudnVl"],"sourcesContent":["<template>\r\n\t<view class=\"page\">\r\n\t\t<scroll-view scroll-y class=\"list\">\r\n\t\t\t<view class=\"item\" v-for=\"a in accounts\" :key=\"a.id\" @click=\"select(a)\">\r\n\t\t\t\t<view class=\"name\">{{ a.name }}</view>\r\n\t\t\t\t<view class=\"meta\">{{ typeLabel(a.type) }} · 余额:{{ a.balance?.toFixed ? a.balance.toFixed(2) : a.balance }}</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\tconst TYPE_MAP = { cash: '现金', bank: '银行', alipay: '支付宝', wechat: '微信', other: '其他' }\r\n\texport default {\r\n\t\tdata() { return { accounts: [] } },\r\n\t\tasync onLoad() {\r\n\t\t\ttry {\r\n\t\t\t\tconst res = await get('/api/accounts')\r\n\t\t\t\tthis.accounts = Array.isArray(res) ? res : (res?.list || [])\r\n\t\t\t} catch(e) { uni.showToast({ title: '加载失败', icon: 'none' }) }\r\n\t\t},\r\n\t\tmethods: {\r\n\t\t\tselect(a) {\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.selectedAccountId = a.id\r\n\t\t\t\t\topener.$vm.selectedAccountName = a.name\r\n\t\t\t\t}\r\n\t\t\t\tuni.navigateBack()\r\n\t\t\t},\r\n\t\t\ttypeLabel(t) { return TYPE_MAP[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.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","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/account/select.vue'\nwx.createPage(MiniProgramPage)"],"names":["get","uni"],"mappings":";;;AAaC,MAAM,WAAW,EAAE,MAAM,MAAM,MAAM,MAAM,QAAQ,OAAO,QAAQ,MAAM,OAAO,KAAK;AACpF,MAAK,YAAU;AAAA,EACd,OAAO;AAAE,WAAO,EAAE,UAAU,CAAG,EAAA;AAAA,EAAG;AAAA,EAClC,MAAM,SAAS;AACd,QAAI;AACH,YAAM,MAAM,MAAMA,YAAG,IAAC,eAAe;AACrC,WAAK,WAAW,MAAM,QAAQ,GAAG,IAAI,OAAO,2BAAK,SAAQ;aAClD,GAAG;AAAEC,oBAAAA,MAAI,UAAU,EAAE,OAAO,QAAQ,MAAM,OAAO,CAAC;AAAA,IAAE;AAAA,EAC5D;AAAA,EACD,SAAS;AAAA,IACR,OAAO,GAAG;AACT,YAAM,SAAS,gBAAiB,EAAC,gBAAe,EAAG,SAAO,CAAC;AAC3D,UAAI,UAAU,OAAO,KAAK;AACzB,eAAO,IAAI,oBAAoB,EAAE;AACjC,eAAO,IAAI,sBAAsB,EAAE;AAAA,MACpC;AACAA,oBAAAA,MAAI,aAAa;AAAA,IACjB;AAAA,IACD,UAAU,GAAG;AAAE,aAAO,SAAS,CAAC,KAAK;AAAA,IAAE;AAAA,EACxC;AACD;;;;;;;;;;;;;;;;AChCD,GAAG,WAAW,eAAe;"}
|
||||||
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/customer/select.js.map
vendored
Normal file
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/customer/select.js.map
vendored
Normal file
@@ -0,0 +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","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;"}
|
||||||
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/detail/index.js.map
vendored
Normal file
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/detail/index.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/order/create.js.map
vendored
Normal file
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/order/create.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/product/select.js.map
vendored
Normal file
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/product/select.js.map
vendored
Normal file
@@ -0,0 +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","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
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/supplier/select.js.map
vendored
Normal file
1
frontend/unpackage/dist/dev/.sourcemap/mp-weixin/pages/supplier/select.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"select.js","sources":["pages/supplier/select.vue","../../../../Downloads/HBuilderX.4.76.2025082103/HBuilderX/plugins/uniapp-cli-vite/uniPage:/cGFnZXMvc3VwcGxpZXIvc2VsZWN0LnZ1ZQ"],"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=\"s in suppliers\" :key=\"s.id\" @click=\"select(s)\">\r\n\t\t\t\t<view class=\"name\">{{ s.name }}</view>\r\n\t\t\t\t<view class=\"meta\">{{ s.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: '', suppliers: [] } },\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/suppliers', { kw: this.kw, page: 1, size: 50 })\r\n\t\t\t\t\tthis.suppliers = 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(s) {\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.supplierId = s.id\r\n\t\t\t\t\topener.$vm.supplierName = s.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","import MiniProgramPage from 'C:/Users/21826/Desktop/Wj/PartsInquiry/frontend/pages/supplier/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;"}
|
||||||
5
frontend/unpackage/dist/dev/mp-weixin/app.js
vendored
5
frontend/unpackage/dist/dev/mp-weixin/app.js
vendored
@@ -3,6 +3,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|||||||
const common_vendor = require("./common/vendor.js");
|
const common_vendor = require("./common/vendor.js");
|
||||||
if (!Math) {
|
if (!Math) {
|
||||||
"./pages/index/index.js";
|
"./pages/index/index.js";
|
||||||
|
"./pages/order/create.js";
|
||||||
|
"./pages/product/select.js";
|
||||||
|
"./pages/customer/select.js";
|
||||||
|
"./pages/supplier/select.js";
|
||||||
|
"./pages/account/select.js";
|
||||||
}
|
}
|
||||||
const _sfc_main = {
|
const _sfc_main = {
|
||||||
onLaunch: function() {
|
onLaunch: function() {
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
{
|
{
|
||||||
"pages": [
|
"pages": [
|
||||||
"pages/index/index"
|
"pages/index/index",
|
||||||
|
"pages/order/create",
|
||||||
|
"pages/product/select",
|
||||||
|
"pages/customer/select",
|
||||||
|
"pages/supplier/select",
|
||||||
|
"pages/account/select"
|
||||||
],
|
],
|
||||||
"window": {
|
"window": {
|
||||||
"navigationBarTextStyle": "black",
|
"navigationBarTextStyle": "black",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
const _imports_0 = "/static/metal-bg.jpg";
|
const _imports_0$1 = "/static/metal-bg.jpg";
|
||||||
exports._imports_0 = _imports_0;
|
const _imports_0 = "/static/logo.png";
|
||||||
|
exports._imports_0 = _imports_0$1;
|
||||||
|
exports._imports_0$1 = _imports_0;
|
||||||
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/assets.js.map
|
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/assets.js.map
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ const API_BASE_URL = (envBaseUrl || storageBaseUrl || fallbackBaseUrl).replace(/
|
|||||||
const envShopId = typeof process !== "undefined" && process.env && (process.env.VITE_APP_SHOP_ID || process.env.SHOP_ID) || "";
|
const envShopId = typeof process !== "undefined" && process.env && (process.env.VITE_APP_SHOP_ID || process.env.SHOP_ID) || "";
|
||||||
const storageShopId = typeof common_vendor.index !== "undefined" ? common_vendor.index.getStorageSync("SHOP_ID") || "" : "";
|
const storageShopId = typeof common_vendor.index !== "undefined" ? common_vendor.index.getStorageSync("SHOP_ID") || "" : "";
|
||||||
const SHOP_ID = Number(envShopId || storageShopId || 1);
|
const SHOP_ID = Number(envShopId || storageShopId || 1);
|
||||||
|
const envEnableDefaultUser = typeof process !== "undefined" && process.env && (process.env.VITE_APP_ENABLE_DEFAULT_USER || process.env.ENABLE_DEFAULT_USER) || "";
|
||||||
|
const storageEnableDefaultUser = typeof common_vendor.index !== "undefined" ? common_vendor.index.getStorageSync("ENABLE_DEFAULT_USER") || "" : "";
|
||||||
|
const ENABLE_DEFAULT_USER = String(envEnableDefaultUser || storageEnableDefaultUser || "false").toLowerCase() === "true";
|
||||||
|
const envDefaultUserId = typeof process !== "undefined" && process.env && (process.env.VITE_APP_DEFAULT_USER_ID || process.env.DEFAULT_USER_ID) || "";
|
||||||
|
const storageDefaultUserId = typeof common_vendor.index !== "undefined" ? common_vendor.index.getStorageSync("DEFAULT_USER_ID") || "" : "";
|
||||||
|
const DEFAULT_USER_ID = Number(envDefaultUserId || storageDefaultUserId || 2);
|
||||||
exports.API_BASE_URL = API_BASE_URL;
|
exports.API_BASE_URL = API_BASE_URL;
|
||||||
|
exports.DEFAULT_USER_ID = DEFAULT_USER_ID;
|
||||||
|
exports.ENABLE_DEFAULT_USER = ENABLE_DEFAULT_USER;
|
||||||
exports.SHOP_ID = SHOP_ID;
|
exports.SHOP_ID = SHOP_ID;
|
||||||
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/config.js.map
|
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/config.js.map
|
||||||
|
|||||||
18
frontend/unpackage/dist/dev/mp-weixin/common/constants.js
vendored
Normal file
18
frontend/unpackage/dist/dev/mp-weixin/common/constants.js
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"use strict";
|
||||||
|
const INCOME_CATEGORIES = [
|
||||||
|
{ key: "sale_income", label: "销售收入" },
|
||||||
|
{ key: "operation_income", label: "经营所得" },
|
||||||
|
{ key: "interest_income", label: "利息收入" },
|
||||||
|
{ key: "investment_income", label: "投资收入" },
|
||||||
|
{ key: "other_income", label: "其它收入" }
|
||||||
|
];
|
||||||
|
const EXPENSE_CATEGORIES = [
|
||||||
|
{ key: "operation_expense", label: "经营支出" },
|
||||||
|
{ key: "office_supplies", label: "办公用品" },
|
||||||
|
{ key: "rent", label: "房租" },
|
||||||
|
{ key: "interest_expense", label: "利息支出" },
|
||||||
|
{ key: "other_expense", label: "其它支出" }
|
||||||
|
];
|
||||||
|
exports.EXPENSE_CATEGORIES = EXPENSE_CATEGORIES;
|
||||||
|
exports.INCOME_CATEGORIES = INCOME_CATEGORIES;
|
||||||
|
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/constants.js.map
|
||||||
@@ -10,11 +10,34 @@ function buildUrl(path) {
|
|||||||
}
|
}
|
||||||
function get(path, params = {}) {
|
function get(path, params = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const headers = { "X-Shop-Id": common_config.SHOP_ID };
|
||||||
|
if (common_config.ENABLE_DEFAULT_USER && common_config.DEFAULT_USER_ID)
|
||||||
|
headers["X-User-Id"] = common_config.DEFAULT_USER_ID;
|
||||||
common_vendor.index.request({
|
common_vendor.index.request({
|
||||||
url: buildUrl(path),
|
url: buildUrl(path),
|
||||||
method: "GET",
|
method: "GET",
|
||||||
data: params,
|
data: params,
|
||||||
header: { "X-Shop-Id": common_config.SHOP_ID },
|
header: headers,
|
||||||
|
success: (res) => {
|
||||||
|
const { statusCode, data } = res;
|
||||||
|
if (statusCode >= 200 && statusCode < 300)
|
||||||
|
return resolve(data);
|
||||||
|
reject(new Error("HTTP " + statusCode));
|
||||||
|
},
|
||||||
|
fail: (err) => reject(err)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function post(path, body = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const headers = { "Content-Type": "application/json", "X-Shop-Id": common_config.SHOP_ID };
|
||||||
|
if (common_config.ENABLE_DEFAULT_USER && common_config.DEFAULT_USER_ID)
|
||||||
|
headers["X-User-Id"] = common_config.DEFAULT_USER_ID;
|
||||||
|
common_vendor.index.request({
|
||||||
|
url: buildUrl(path),
|
||||||
|
method: "POST",
|
||||||
|
data: body,
|
||||||
|
header: headers,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
const { statusCode, data } = res;
|
const { statusCode, data } = res;
|
||||||
if (statusCode >= 200 && statusCode < 300)
|
if (statusCode >= 200 && statusCode < 300)
|
||||||
@@ -26,4 +49,5 @@ function get(path, params = {}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.get = get;
|
exports.get = get;
|
||||||
|
exports.post = post;
|
||||||
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/http.js.map
|
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/http.js.map
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ const looseToNumber = (val) => {
|
|||||||
const n = parseFloat(val);
|
const n = parseFloat(val);
|
||||||
return isNaN(n) ? val : n;
|
return isNaN(n) ? val : n;
|
||||||
};
|
};
|
||||||
|
const toNumber = (val) => {
|
||||||
|
const n = isString(val) ? Number(val) : NaN;
|
||||||
|
return isNaN(n) ? val : n;
|
||||||
|
};
|
||||||
const toDisplayString = (val) => {
|
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);
|
return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val);
|
||||||
};
|
};
|
||||||
@@ -5094,10 +5098,32 @@ function vFor(source, renderItem) {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
function withModelModifiers(fn, { number, trim }, isComponent = false) {
|
||||||
|
if (isComponent) {
|
||||||
|
return (...args) => {
|
||||||
|
if (trim) {
|
||||||
|
args = args.map((a) => a.trim());
|
||||||
|
} else if (number) {
|
||||||
|
args = args.map(toNumber);
|
||||||
|
}
|
||||||
|
return fn(...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return (event) => {
|
||||||
|
const value = event.detail.value;
|
||||||
|
if (trim) {
|
||||||
|
event.detail.value = value.trim();
|
||||||
|
} else if (number) {
|
||||||
|
event.detail.value = toNumber(value);
|
||||||
|
}
|
||||||
|
return fn(event);
|
||||||
|
};
|
||||||
|
}
|
||||||
const o = (value, key) => vOn(value, key);
|
const o = (value, key) => vOn(value, key);
|
||||||
const f = (source, renderItem) => vFor(source, renderItem);
|
const f = (source, renderItem) => vFor(source, renderItem);
|
||||||
const e = (target, ...sources) => extend(target, ...sources);
|
const e = (target, ...sources) => extend(target, ...sources);
|
||||||
const t = (val) => toDisplayString(val);
|
const t = (val) => toDisplayString(val);
|
||||||
|
const m = (fn, modifiers, isComponent = false) => withModelModifiers(fn, modifiers, isComponent);
|
||||||
function createApp$1(rootComponent, rootProps = null) {
|
function createApp$1(rootComponent, rootProps = null) {
|
||||||
rootComponent && (rootComponent.mpType = "app");
|
rootComponent && (rootComponent.mpType = "app");
|
||||||
return createVueApp(rootComponent, rootProps).use(plugin);
|
return createVueApp(rootComponent, rootProps).use(plugin);
|
||||||
@@ -7870,6 +7896,7 @@ exports.createSSRApp = createSSRApp;
|
|||||||
exports.e = e;
|
exports.e = e;
|
||||||
exports.f = f;
|
exports.f = f;
|
||||||
exports.index = index;
|
exports.index = index;
|
||||||
|
exports.m = m;
|
||||||
exports.o = o;
|
exports.o = o;
|
||||||
exports.t = t;
|
exports.t = t;
|
||||||
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/vendor.js.map
|
//# sourceMappingURL=../../.sourcemap/mp-weixin/common/vendor.js.map
|
||||||
|
|||||||
47
frontend/unpackage/dist/dev/mp-weixin/pages/account/select.js
vendored
Normal file
47
frontend/unpackage/dist/dev/mp-weixin/pages/account/select.js
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
"use strict";
|
||||||
|
const common_vendor = require("../../common/vendor.js");
|
||||||
|
const common_http = require("../../common/http.js");
|
||||||
|
const TYPE_MAP = { cash: "现金", bank: "银行", alipay: "支付宝", wechat: "微信", other: "其他" };
|
||||||
|
const _sfc_main = {
|
||||||
|
data() {
|
||||||
|
return { accounts: [] };
|
||||||
|
},
|
||||||
|
async onLoad() {
|
||||||
|
try {
|
||||||
|
const res = await common_http.get("/api/accounts");
|
||||||
|
this.accounts = Array.isArray(res) ? res : (res == null ? void 0 : res.list) || [];
|
||||||
|
} catch (e) {
|
||||||
|
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
select(a) {
|
||||||
|
const opener = getCurrentPages()[getCurrentPages().length - 2];
|
||||||
|
if (opener && opener.$vm) {
|
||||||
|
opener.$vm.selectedAccountId = a.id;
|
||||||
|
opener.$vm.selectedAccountName = a.name;
|
||||||
|
}
|
||||||
|
common_vendor.index.navigateBack();
|
||||||
|
},
|
||||||
|
typeLabel(t) {
|
||||||
|
return TYPE_MAP[t] || t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||||
|
return {
|
||||||
|
a: common_vendor.f($data.accounts, (a, k0, i0) => {
|
||||||
|
var _a;
|
||||||
|
return {
|
||||||
|
a: common_vendor.t(a.name),
|
||||||
|
b: common_vendor.t($options.typeLabel(a.type)),
|
||||||
|
c: common_vendor.t(((_a = a.balance) == null ? void 0 : _a.toFixed) ? a.balance.toFixed(2) : a.balance),
|
||||||
|
d: a.id,
|
||||||
|
e: common_vendor.o(($event) => $options.select(a), a.id)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||||
|
wx.createPage(MiniProgramPage);
|
||||||
|
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/account/select.js.map
|
||||||
4
frontend/unpackage/dist/dev/mp-weixin/pages/account/select.json
vendored
Normal file
4
frontend/unpackage/dist/dev/mp-weixin/pages/account/select.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "选择账户",
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
1
frontend/unpackage/dist/dev/mp-weixin/pages/account/select.wxml
vendored
Normal file
1
frontend/unpackage/dist/dev/mp-weixin/pages/account/select.wxml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<view class="page"><scroll-view scroll-y class="list"><view wx:for="{{a}}" wx:for-item="a" wx:key="d" class="item" bindtap="{{a.e}}"><view class="name">{{a.a}}</view><view class="meta">{{a.b}} · 余额:{{a.c}}</view></view></scroll-view></view>
|
||||||
11
frontend/unpackage/dist/dev/mp-weixin/pages/account/select.wxss
vendored
Normal file
11
frontend/unpackage/dist/dev/mp-weixin/pages/account/select.wxss
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
.page { display:flex; flex-direction: column; height: 100vh;
|
||||||
|
}
|
||||||
|
.list { flex:1;
|
||||||
|
}
|
||||||
|
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1;
|
||||||
|
}
|
||||||
|
.name { color:#333; margin-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
.meta { color:#888; font-size: 24rpx;
|
||||||
|
}
|
||||||
48
frontend/unpackage/dist/dev/mp-weixin/pages/customer/select.js
vendored
Normal file
48
frontend/unpackage/dist/dev/mp-weixin/pages/customer/select.js
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
"use strict";
|
||||||
|
const common_vendor = require("../../common/vendor.js");
|
||||||
|
const common_http = require("../../common/http.js");
|
||||||
|
const _sfc_main = {
|
||||||
|
data() {
|
||||||
|
return { kw: "", customers: [] };
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
this.search();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async search() {
|
||||||
|
try {
|
||||||
|
const res = await common_http.get("/api/customers", { kw: this.kw, page: 1, size: 50 });
|
||||||
|
this.customers = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : [];
|
||||||
|
} catch (e) {
|
||||||
|
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select(c) {
|
||||||
|
const opener = getCurrentPages()[getCurrentPages().length - 2];
|
||||||
|
if (opener && opener.$vm) {
|
||||||
|
opener.$vm.order.customerId = c.id;
|
||||||
|
opener.$vm.customerName = c.name;
|
||||||
|
}
|
||||||
|
common_vendor.index.navigateBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||||
|
return {
|
||||||
|
a: common_vendor.o((...args) => $options.search && $options.search(...args)),
|
||||||
|
b: $data.kw,
|
||||||
|
c: common_vendor.o(($event) => $data.kw = $event.detail.value),
|
||||||
|
d: common_vendor.o((...args) => $options.search && $options.search(...args)),
|
||||||
|
e: common_vendor.f($data.customers, (c, k0, i0) => {
|
||||||
|
return {
|
||||||
|
a: common_vendor.t(c.name),
|
||||||
|
b: common_vendor.t(c.mobile || "—"),
|
||||||
|
c: c.id,
|
||||||
|
d: common_vendor.o(($event) => $options.select(c), c.id)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||||
|
wx.createPage(MiniProgramPage);
|
||||||
|
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/customer/select.js.map
|
||||||
4
frontend/unpackage/dist/dev/mp-weixin/pages/customer/select.json
vendored
Normal file
4
frontend/unpackage/dist/dev/mp-weixin/pages/customer/select.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "选择客户",
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
1
frontend/unpackage/dist/dev/mp-weixin/pages/customer/select.wxml
vendored
Normal file
1
frontend/unpackage/dist/dev/mp-weixin/pages/customer/select.wxml
vendored
Normal file
@@ -0,0 +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>
|
||||||
15
frontend/unpackage/dist/dev/mp-weixin/pages/customer/select.wxss
vendored
Normal file
15
frontend/unpackage/dist/dev/mp-weixin/pages/customer/select.wxss
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
.page { display:flex; flex-direction: column; height: 100vh;
|
||||||
|
}
|
||||||
|
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff;
|
||||||
|
}
|
||||||
|
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx;
|
||||||
|
}
|
||||||
|
.list { flex:1;
|
||||||
|
}
|
||||||
|
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1;
|
||||||
|
}
|
||||||
|
.name { color:#333; margin-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
.meta { color:#888; font-size: 24rpx;
|
||||||
|
}
|
||||||
135
frontend/unpackage/dist/dev/mp-weixin/pages/detail/index.js
vendored
Normal file
135
frontend/unpackage/dist/dev/mp-weixin/pages/detail/index.js
vendored
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
"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 _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
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
totalAmount() {
|
||||||
|
return this.list.reduce((s, o) => s + Number(o.amount || 0), 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
this.reload();
|
||||||
|
},
|
||||||
|
async reload() {
|
||||||
|
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 : [];
|
||||||
|
} catch (e) {
|
||||||
|
this.list = [];
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goCreate() {
|
||||||
|
common_vendor.index.navigateTo({ url: "/pages/order/create" });
|
||||||
|
},
|
||||||
|
open(o) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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) => {
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||||
|
wx.createPage(MiniProgramPage);
|
||||||
|
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/detail/index.js.map
|
||||||
4
frontend/unpackage/dist/dev/mp-weixin/pages/detail/index.json
vendored
Normal file
4
frontend/unpackage/dist/dev/mp-weixin/pages/detail/index.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "明细",
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
1
frontend/unpackage/dist/dev/mp-weixin/pages/detail/index.wxml
vendored
Normal file
1
frontend/unpackage/dist/dev/mp-weixin/pages/detail/index.wxml
vendored
Normal file
@@ -0,0 +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>
|
||||||
43
frontend/unpackage/dist/dev/mp-weixin/pages/detail/index.wxss
vendored
Normal file
43
frontend/unpackage/dist/dev/mp-weixin/pages/detail/index.wxss
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
.detail { display:flex;
|
||||||
|
}
|
||||||
|
.filter-tabs { display:flex; gap: 24rpx; padding: 18rpx 24rpx; color:#666;
|
||||||
|
}
|
||||||
|
.filter-tabs .tab.active { color:#2aa7b6; font-weight: 700;
|
||||||
|
}
|
||||||
|
.biz-tabs { position: fixed; left:0; top: 160rpx; bottom: 120rpx; width: 120rpx; display:flex; flex-direction: column; gap: 24rpx; padding: 12rpx;
|
||||||
|
}
|
||||||
|
.biz { background:#6aa9ff; color:#fff; border-radius: 16rpx; padding: 20rpx 0; text-align:center; opacity: .85;
|
||||||
|
}
|
||||||
|
.biz.active { opacity: 1;
|
||||||
|
}
|
||||||
|
.card { margin-left: 140rpx; background:#fff; border-radius: 24rpx; padding: 16rpx;
|
||||||
|
}
|
||||||
|
.subtabs { display:flex; align-items:center; gap: 24rpx; padding: 8rpx 6rpx 12rpx;
|
||||||
|
}
|
||||||
|
.sub { color:#57c2cf; padding: 8rpx 12rpx;
|
||||||
|
}
|
||||||
|
.sub.active { border-bottom: 4rpx solid #57c2cf; font-weight:700;
|
||||||
|
}
|
||||||
|
.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 { background:#f6f7fb; border-radius: 999rpx; padding: 14rpx 20rpx; margin: 8rpx 0 12rpx;
|
||||||
|
}
|
||||||
|
.daterange { display:flex; align-items:center; gap: 12rpx; color:#888; padding-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
.daterange .sep { color:#ccc;
|
||||||
|
}
|
||||||
|
.total { color:#2ec0d0; font-weight: 800; padding: 12rpx 0; border-top: 2rpx solid #eaeaea;
|
||||||
|
}
|
||||||
|
.list { height: calc(100vh - 420rpx);
|
||||||
|
}
|
||||||
|
.row { display:flex; justify-content: space-between; align-items:center; padding: 22rpx 10rpx; border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
.left .date { color:#999; margin-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
.left .name { color:#333; margin-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
.left .no { color:#bbb;
|
||||||
|
}
|
||||||
|
.right { color:#555;
|
||||||
|
}
|
||||||
@@ -5,9 +5,7 @@ const common_assets = require("../../common/assets.js");
|
|||||||
const _sfc_main = {
|
const _sfc_main = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
todayAmount: "0.00",
|
kpi: { todaySales: "0.00", monthSales: "0.00", monthProfit: "0.00", stockCount: "0" },
|
||||||
monthProfit: "0.00",
|
|
||||||
stockQty: "0.00",
|
|
||||||
activeTab: "home",
|
activeTab: "home",
|
||||||
notices: [],
|
notices: [],
|
||||||
loadingNotices: false,
|
loadingNotices: false,
|
||||||
@@ -26,9 +24,22 @@ const _sfc_main = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
this.fetchMetrics();
|
||||||
this.fetchNotices();
|
this.fetchNotices();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async fetchMetrics() {
|
||||||
|
try {
|
||||||
|
const d = await common_http.get("/api/metrics/overview");
|
||||||
|
this.kpi = {
|
||||||
|
todaySales: d && d.todaySales || "0.00",
|
||||||
|
monthSales: d && d.monthSales || "0.00",
|
||||||
|
monthProfit: d && d.monthProfit || "0.00",
|
||||||
|
stockCount: d && d.stockCount || "0"
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
},
|
||||||
async fetchNotices() {
|
async fetchNotices() {
|
||||||
this.loadingNotices = true;
|
this.loadingNotices = true;
|
||||||
this.noticeError = "";
|
this.noticeError = "";
|
||||||
@@ -48,18 +59,15 @@ const _sfc_main = {
|
|||||||
common_vendor.index.showToast({ title: item.title + "(开发中)", icon: "none" });
|
common_vendor.index.showToast({ title: item.title + "(开发中)", icon: "none" });
|
||||||
},
|
},
|
||||||
onCreateOrder() {
|
onCreateOrder() {
|
||||||
common_vendor.index.showToast({ title: "开单(开发中)", icon: "none" });
|
common_vendor.index.navigateTo({ url: "/pages/order/create" });
|
||||||
},
|
},
|
||||||
onNoticeTap(n) {
|
onNoticeTap(n) {
|
||||||
common_vendor.index.showModal({
|
common_vendor.index.showModal({
|
||||||
title: "公告",
|
title: "广告",
|
||||||
content: n && (n.text || n.title || n.content) || "",
|
content: n && (n.text || n.title || n.content) || "",
|
||||||
showCancel: false
|
showCancel: false
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onNoticeList() {
|
|
||||||
common_vendor.index.showToast({ title: "公告列表(开发中)", icon: "none" });
|
|
||||||
},
|
|
||||||
onIconError(item) {
|
onIconError(item) {
|
||||||
item.img = "";
|
item.img = "";
|
||||||
}
|
}
|
||||||
@@ -68,14 +76,15 @@ const _sfc_main = {
|
|||||||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||||
return common_vendor.e({
|
return common_vendor.e({
|
||||||
a: common_assets._imports_0,
|
a: common_assets._imports_0,
|
||||||
b: common_vendor.t($data.todayAmount),
|
b: common_vendor.t($data.kpi.todaySales),
|
||||||
c: common_vendor.t($data.monthProfit),
|
c: common_vendor.t($data.kpi.monthSales),
|
||||||
d: common_vendor.t($data.stockQty),
|
d: common_vendor.t($data.kpi.monthProfit),
|
||||||
e: $data.loadingNotices
|
e: common_vendor.t($data.kpi.stockCount),
|
||||||
|
f: $data.loadingNotices
|
||||||
}, $data.loadingNotices ? {} : $data.noticeError ? {
|
}, $data.loadingNotices ? {} : $data.noticeError ? {
|
||||||
g: common_vendor.t($data.noticeError)
|
h: common_vendor.t($data.noticeError)
|
||||||
} : !$data.notices.length ? {} : {
|
} : !$data.notices.length ? {} : {
|
||||||
i: common_vendor.f($data.notices, (n, idx, i0) => {
|
j: common_vendor.f($data.notices, (n, idx, i0) => {
|
||||||
return common_vendor.e({
|
return common_vendor.e({
|
||||||
a: common_vendor.t(n.text),
|
a: common_vendor.t(n.text),
|
||||||
b: n.tag
|
b: n.tag
|
||||||
@@ -87,9 +96,8 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
}, {
|
}, {
|
||||||
f: $data.noticeError,
|
g: $data.noticeError,
|
||||||
h: !$data.notices.length,
|
i: !$data.notices.length,
|
||||||
j: common_vendor.o((...args) => $options.onNoticeList && $options.onNoticeList(...args)),
|
|
||||||
k: common_vendor.f($data.features, (item, k0, i0) => {
|
k: common_vendor.f($data.features, (item, k0, i0) => {
|
||||||
return common_vendor.e({
|
return common_vendor.e({
|
||||||
a: item.img
|
a: item.img
|
||||||
@@ -107,11 +115,15 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|||||||
}),
|
}),
|
||||||
l: $data.activeTab === "home" ? 1 : "",
|
l: $data.activeTab === "home" ? 1 : "",
|
||||||
m: common_vendor.o(($event) => $data.activeTab = "home"),
|
m: common_vendor.o(($event) => $data.activeTab = "home"),
|
||||||
n: common_vendor.o((...args) => $options.onCreateOrder && $options.onCreateOrder(...args)),
|
n: $data.activeTab === "product" ? 1 : "",
|
||||||
o: $data.activeTab === "detail" ? 1 : "",
|
o: common_vendor.o(($event) => $data.activeTab = "product"),
|
||||||
p: common_vendor.o(($event) => $data.activeTab = "detail"),
|
p: common_vendor.o((...args) => $options.onCreateOrder && $options.onCreateOrder(...args)),
|
||||||
q: $data.activeTab === "me" ? 1 : "",
|
q: $data.activeTab === "detail" ? 1 : "",
|
||||||
r: common_vendor.o(($event) => $data.activeTab = "me")
|
r: common_vendor.o(($event) => $data.activeTab = "detail"),
|
||||||
|
s: $data.activeTab === "report" ? 1 : "",
|
||||||
|
t: common_vendor.o(($event) => $data.activeTab = "report"),
|
||||||
|
v: $data.activeTab === "me" ? 1 : "",
|
||||||
|
w: common_vendor.o(($event) => $data.activeTab = "me")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
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="home"><image class="home-bg" src="{{a}}" mode="aspectFill"></image><view class="hero"><view class="hero-top"><text class="brand">五金配件管家</text><view class="cta"><text class="cta-text">咨询</text></view></view><view class="kpi"><view class="kpi-item"><text class="kpi-label">今日销售额</text><text class="kpi-value">{{b}}</text></view><view class="kpi-item"><text class="kpi-label">本月利润</text><text class="kpi-value">{{c}}</text></view><view class="kpi-item"><text class="kpi-label">库存数量</text><text class="kpi-value">{{d}}</text></view></view></view><view class="notice"><view class="notice-left">公告</view><view wx:if="{{e}}" class="notice-swiper" style="display:flex;align-items:center;color:#6b5a2a">加载中...</view><view wx:elif="{{f}}" class="notice-swiper" style="display:flex;align-items:center;color:#dd524d">{{g}}</view><view wx:elif="{{h}}" class="notice-swiper" style="display:flex;align-items:center;color:#6b5a2a">暂无公告</view><swiper wx:else class="notice-swiper" circular autoplay interval="4000" duration="400" vertical><swiper-item wx:for="{{i}}" wx:for-item="n" wx:key="e"><view class="notice-item" bindtap="{{n.d}}"><text class="notice-text">{{n.a}}</text><text wx:if="{{n.b}}" class="notice-tag">{{n.c}}</text></view></swiper-item></swiper><view class="notice-right" bindtap="{{j}}">更多</view></view><view class="section-title"><text class="section-text">常用功能</text></view><view class="grid-wrap"><view class="grid"><view wx:for="{{k}}" wx:for-item="item" wx:key="g" class="grid-item" bindtap="{{item.h}}"><view class="icon icon-squircle"><image wx:if="{{item.a}}" src="{{item.b}}" class="icon-img" mode="aspectFit" binderror="{{item.c}}"></image><text wx:elif="{{item.d}}" class="icon-emoji">{{item.e}}</text><view wx:else class="icon-placeholder"></view></view><text class="grid-chip">{{item.f}}</text></view></view></view><view class="bottom-bar"><view class="{{['tab', l && 'active']}}" bindtap="{{m}}"><text>首页</text></view><view class="tab primary" bindtap="{{n}}"><text>开单</text></view><view class="{{['tab', o && 'active']}}" bindtap="{{p}}"><text>明细</text></view><view class="{{['tab', q && 'active']}}" bindtap="{{r}}"><text>我的</text></view></view></view>
|
<view class="home"><image class="home-bg" src="{{a}}" mode="aspectFill"></image><view class="hero"><view class="hero-top"><text class="brand">五金配件管家</text><view class="cta"><text class="cta-text">咨询</text></view></view><view class="kpi"><view class="kpi-item"><text class="kpi-label">今日销售额</text><text class="kpi-value">{{b}}</text></view><view class="kpi-item"><text class="kpi-label">本月销售额</text><text class="kpi-value">{{c}}</text></view><view class="kpi-item"><text class="kpi-label">本月利润</text><text class="kpi-value">{{d}}</text></view><view class="kpi-item"><text class="kpi-label">库存商品数量</text><text class="kpi-value">{{e}}</text></view></view></view><view class="notice"><view class="notice-left">广告</view><view wx:if="{{f}}" class="notice-swiper" style="display:flex;align-items:center;color:#6b5a2a">加载中...</view><view wx:elif="{{g}}" class="notice-swiper" style="display:flex;align-items:center;color:#dd524d">{{h}}</view><view wx:elif="{{i}}" class="notice-swiper" style="display:flex;align-items:center;color:#6b5a2a">暂无公告</view><swiper wx:else class="notice-swiper" circular autoplay interval="4000" duration="400" vertical><swiper-item wx:for="{{j}}" wx:for-item="n" wx:key="e"><view class="notice-item" bindtap="{{n.d}}"><text class="notice-text">{{n.a}}</text><text wx:if="{{n.b}}" class="notice-tag">{{n.c}}</text></view></swiper-item></swiper></view><view class="section-title"><text class="section-text">常用功能</text></view><view class="grid-wrap"><view class="grid"><view wx:for="{{k}}" wx:for-item="item" wx:key="g" class="grid-item" bindtap="{{item.h}}"><view class="icon icon-squircle"><image wx:if="{{item.a}}" src="{{item.b}}" class="icon-img" mode="aspectFit" binderror="{{item.c}}"></image><text wx:elif="{{item.d}}" class="icon-emoji">{{item.e}}</text><view wx:else class="icon-placeholder"></view></view><text class="grid-chip">{{item.f}}</text></view></view></view><view class="bottom-bar"><view class="{{['tab', l && 'active']}}" bindtap="{{m}}"><text>首页</text></view><view class="{{['tab', n && 'active']}}" bindtap="{{o}}"><text>货品</text></view><view class="tab primary" bindtap="{{p}}"><text>开单</text></view><view class="{{['tab', q && 'active']}}" bindtap="{{r}}"><text>明细</text></view><view class="{{['tab', s && 'active']}}" bindtap="{{t}}"><text>报表</text></view><view class="{{['tab', v && 'active']}}" bindtap="{{w}}"><text>我的</text></view></view></view>
|
||||||
@@ -48,8 +48,7 @@
|
|||||||
}
|
}
|
||||||
.notice-tag { color: #B4880F; font-size: 22rpx; padding: 4rpx 10rpx; border-radius: 999rpx; background: rgba(215,167,46,0.18);
|
.notice-tag { color: #B4880F; font-size: 22rpx; padding: 4rpx 10rpx; border-radius: 999rpx; background: rgba(215,167,46,0.18);
|
||||||
}
|
}
|
||||||
.notice-right { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; min-width: 72rpx; height: 44rpx; color: #B4880F; font-size: 26rpx; padding-left: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 分割标题 */
|
/* 分割标题 */
|
||||||
.section-title { display: flex; align-items: center; gap: 16rpx; padding: 10rpx 28rpx 0;
|
.section-title { display: flex; align-items: center; gap: 16rpx; padding: 10rpx 28rpx 0;
|
||||||
|
|||||||
212
frontend/unpackage/dist/dev/mp-weixin/pages/order/create.js
vendored
Normal file
212
frontend/unpackage/dist/dev/mp-weixin/pages/order/create.js
vendored
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
"use strict";
|
||||||
|
const common_vendor = require("../../common/vendor.js");
|
||||||
|
const common_http = require("../../common/http.js");
|
||||||
|
const common_constants = require("../../common/constants.js");
|
||||||
|
const common_assets = require("../../common/assets.js");
|
||||||
|
function todayString() {
|
||||||
|
const d = /* @__PURE__ */ new Date();
|
||||||
|
const m = (d.getMonth() + 1).toString().padStart(2, "0");
|
||||||
|
const day = d.getDate().toString().padStart(2, "0");
|
||||||
|
return `${d.getFullYear()}-${m}-${day}`;
|
||||||
|
}
|
||||||
|
const _sfc_main = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
biz: "sale",
|
||||||
|
saleType: "out",
|
||||||
|
purchaseType: "in",
|
||||||
|
order: {
|
||||||
|
orderTime: todayString(),
|
||||||
|
customerId: null,
|
||||||
|
supplierId: null,
|
||||||
|
remark: ""
|
||||||
|
},
|
||||||
|
customerName: "",
|
||||||
|
supplierName: "",
|
||||||
|
items: [],
|
||||||
|
activeCategory: "sale_income",
|
||||||
|
trxAmount: 0,
|
||||||
|
selectedAccountId: null,
|
||||||
|
selectedAccountName: ""
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
totalQuantity() {
|
||||||
|
return this.items.reduce((s, it) => s + Number(it.quantity || 0), 0);
|
||||||
|
},
|
||||||
|
totalAmount() {
|
||||||
|
return this.items.reduce((s, it) => s + Number(it.quantity || 0) * Number(it.unitPrice || 0), 0);
|
||||||
|
},
|
||||||
|
customerLabel() {
|
||||||
|
return this.customerName || "零售客户";
|
||||||
|
},
|
||||||
|
supplierLabel() {
|
||||||
|
return this.supplierName || "零散供应商";
|
||||||
|
},
|
||||||
|
incomeCategories() {
|
||||||
|
return common_constants.INCOME_CATEGORIES;
|
||||||
|
},
|
||||||
|
expenseCategories() {
|
||||||
|
return common_constants.EXPENSE_CATEGORIES;
|
||||||
|
},
|
||||||
|
accountLabel() {
|
||||||
|
return this.selectedAccountName || "现金";
|
||||||
|
},
|
||||||
|
counterpartyLabel() {
|
||||||
|
return this.customerName || this.supplierName || "—";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
switchBiz(type) {
|
||||||
|
this.biz = type;
|
||||||
|
},
|
||||||
|
onDateChange(e) {
|
||||||
|
this.order.orderTime = e.detail.value;
|
||||||
|
},
|
||||||
|
chooseCustomer() {
|
||||||
|
common_vendor.index.navigateTo({ url: "/pages/customer/select" });
|
||||||
|
},
|
||||||
|
chooseSupplier() {
|
||||||
|
common_vendor.index.navigateTo({ url: "/pages/supplier/select" });
|
||||||
|
},
|
||||||
|
chooseProduct() {
|
||||||
|
common_vendor.index.navigateTo({ url: "/pages/product/select" });
|
||||||
|
},
|
||||||
|
chooseAccount() {
|
||||||
|
common_vendor.index.navigateTo({ url: "/pages/account/select" });
|
||||||
|
},
|
||||||
|
chooseCounterparty() {
|
||||||
|
if (this.biz === "income" || this.biz === "expense") {
|
||||||
|
common_vendor.index.navigateTo({ url: "/pages/customer/select" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
recalc() {
|
||||||
|
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
|
||||||
|
} : {
|
||||||
|
type: this.biz,
|
||||||
|
category: this.activeCategory,
|
||||||
|
counterpartyId: this.order.customerId || null,
|
||||||
|
accountId: this.selectedAccountId || null,
|
||||||
|
amount: Number(this.trxAmount || 0),
|
||||||
|
txTime: this.order.orderTime,
|
||||||
|
remark: this.order.remark
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const url = isSaleOrPurchase ? "/api/orders" : "/api/other-transactions";
|
||||||
|
await common_http.post(url, 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" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveAndReset() {
|
||||||
|
this.items = [];
|
||||||
|
this.trxAmount = 0;
|
||||||
|
this.order.remark = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||||
|
return common_vendor.e({
|
||||||
|
a: $data.biz === "sale" ? 1 : "",
|
||||||
|
b: common_vendor.o(($event) => $options.switchBiz("sale")),
|
||||||
|
c: $data.biz === "purchase" ? 1 : "",
|
||||||
|
d: common_vendor.o(($event) => $options.switchBiz("purchase")),
|
||||||
|
e: $data.biz === "income" ? 1 : "",
|
||||||
|
f: common_vendor.o(($event) => $options.switchBiz("income")),
|
||||||
|
g: $data.biz === "expense" ? 1 : "",
|
||||||
|
h: common_vendor.o(($event) => $options.switchBiz("expense")),
|
||||||
|
i: $data.biz === "sale"
|
||||||
|
}, $data.biz === "sale" ? {
|
||||||
|
j: $data.saleType === "out" ? 1 : "",
|
||||||
|
k: common_vendor.o(($event) => $data.saleType = "out"),
|
||||||
|
l: $data.saleType === "return" ? 1 : "",
|
||||||
|
m: common_vendor.o(($event) => $data.saleType = "return"),
|
||||||
|
n: $data.saleType === "collect" ? 1 : "",
|
||||||
|
o: common_vendor.o(($event) => $data.saleType = "collect")
|
||||||
|
} : $data.biz === "purchase" ? {
|
||||||
|
q: $data.purchaseType === "in" ? 1 : "",
|
||||||
|
r: common_vendor.o(($event) => $data.purchaseType = "in"),
|
||||||
|
s: $data.purchaseType === "return" ? 1 : "",
|
||||||
|
t: common_vendor.o(($event) => $data.purchaseType = "return"),
|
||||||
|
v: $data.purchaseType === "pay" ? 1 : "",
|
||||||
|
w: common_vendor.o(($event) => $data.purchaseType = "pay")
|
||||||
|
} : {}, {
|
||||||
|
p: $data.biz === "purchase",
|
||||||
|
x: common_vendor.t($data.order.orderTime),
|
||||||
|
y: $data.order.orderTime,
|
||||||
|
z: common_vendor.o((...args) => $options.onDateChange && $options.onDateChange(...args)),
|
||||||
|
A: $data.biz === "sale"
|
||||||
|
}, $data.biz === "sale" ? {
|
||||||
|
B: common_vendor.t($options.customerLabel),
|
||||||
|
C: common_vendor.o((...args) => $options.chooseCustomer && $options.chooseCustomer(...args))
|
||||||
|
} : $data.biz === "purchase" ? {
|
||||||
|
E: common_vendor.t($options.supplierLabel),
|
||||||
|
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))
|
||||||
|
} : {
|
||||||
|
K: common_vendor.f($data.biz === "income" ? $options.incomeCategories : $options.expenseCategories, (c, k0, i0) => {
|
||||||
|
return {
|
||||||
|
a: common_vendor.t(c.label),
|
||||||
|
b: c.key,
|
||||||
|
c: $data.activeCategory === c.key ? 1 : "",
|
||||||
|
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, {
|
||||||
|
number: true
|
||||||
|
})),
|
||||||
|
R: $data.order.remark,
|
||||||
|
S: common_vendor.o(($event) => $data.order.remark = $event.detail.value)
|
||||||
|
}, {
|
||||||
|
T: !$data.items.length
|
||||||
|
}, !$data.items.length ? {
|
||||||
|
U: common_assets._imports_0$1
|
||||||
|
} : {
|
||||||
|
V: 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, {
|
||||||
|
number: true
|
||||||
|
}), ($event) => $options.recalc()], idx),
|
||||||
|
c: it.quantity,
|
||||||
|
d: common_vendor.o([common_vendor.m(($event) => it.unitPrice = $event.detail.value, {
|
||||||
|
number: true
|
||||||
|
}), ($event) => $options.recalc()], idx),
|
||||||
|
e: it.unitPrice,
|
||||||
|
f: common_vendor.t((Number(it.quantity) * Number(it.unitPrice)).toFixed(2)),
|
||||||
|
g: idx
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
W: common_vendor.o((...args) => $options.saveAndReset && $options.saveAndReset(...args)),
|
||||||
|
X: 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/order/create.js.map
|
||||||
4
frontend/unpackage/dist/dev/mp-weixin/pages/order/create.json
vendored
Normal file
4
frontend/unpackage/dist/dev/mp-weixin/pages/order/create.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "开单",
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
1
frontend/unpackage/dist/dev/mp-weixin/pages/order/create.wxml
vendored
Normal file
1
frontend/unpackage/dist/dev/mp-weixin/pages/order/create.wxml
vendored
Normal file
@@ -0,0 +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>
|
||||||
43
frontend/unpackage/dist/dev/mp-weixin/pages/order/create.wxss
vendored
Normal file
43
frontend/unpackage/dist/dev/mp-weixin/pages/order/create.wxss
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
.order { padding-bottom: 140rpx;
|
||||||
|
}
|
||||||
|
.tabs { display: flex; justify-content: space-around; padding: 16rpx 24rpx;
|
||||||
|
}
|
||||||
|
.tabs text { color: #666;
|
||||||
|
}
|
||||||
|
.tabs text.active { color: #333; font-weight: 700;
|
||||||
|
}
|
||||||
|
.subtabs { display: flex; gap: 16rpx; padding: 0 24rpx 16rpx;
|
||||||
|
}
|
||||||
|
.subbtn { padding: 10rpx 20rpx; border-radius: 999rpx; background: #f4f4f4; color: #666;
|
||||||
|
}
|
||||||
|
.subbtn.active { background: #ffe69a; color: #3f320f;
|
||||||
|
}
|
||||||
|
.field { display:flex; justify-content: space-between; padding: 22rpx 24rpx; background: #fff; border-bottom: 1rpx solid #eee;
|
||||||
|
}
|
||||||
|
.label { color:#666;
|
||||||
|
}
|
||||||
|
.value { color:#333;
|
||||||
|
}
|
||||||
|
.summary { display:flex; justify-content: space-between; padding: 22rpx 24rpx; color:#333;
|
||||||
|
}
|
||||||
|
.add { margin: 24rpx auto; width: 120rpx; height: 120rpx; border-radius: 20rpx; background: #c7eef7; color:#16a1c4; font-size: 72rpx; display:flex; align-items:center; justify-content:center;
|
||||||
|
}
|
||||||
|
.empty { display:flex; flex-direction: column; align-items:center; padding: 60rpx 0; color:#888;
|
||||||
|
}
|
||||||
|
.empty-img { width: 220rpx; margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
.empty-text { margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
.list { background:#fff;
|
||||||
|
}
|
||||||
|
.row { display:grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 12rpx; padding: 16rpx 12rpx; align-items:center; border-bottom: 1rpx solid #f3f3f3;
|
||||||
|
}
|
||||||
|
.col.name { padding-left: 12rpx;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
48
frontend/unpackage/dist/dev/mp-weixin/pages/product/select.js
vendored
Normal file
48
frontend/unpackage/dist/dev/mp-weixin/pages/product/select.js
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
"use strict";
|
||||||
|
const common_vendor = require("../../common/vendor.js");
|
||||||
|
const common_http = require("../../common/http.js");
|
||||||
|
const _sfc_main = {
|
||||||
|
data() {
|
||||||
|
return { kw: "", products: [] };
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
this.search();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async search() {
|
||||||
|
try {
|
||||||
|
const res = await common_http.get("/api/products", { kw: this.kw, page: 1, size: 50 });
|
||||||
|
this.products = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : [];
|
||||||
|
} catch (e) {
|
||||||
|
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select(p) {
|
||||||
|
const opener = getCurrentPages()[getCurrentPages().length - 2];
|
||||||
|
if (opener && opener.$vm && opener.$vm.items) {
|
||||||
|
opener.$vm.items.push({ productId: p.id, productName: p.name, quantity: 1, unitPrice: Number(p.price || 0) });
|
||||||
|
}
|
||||||
|
common_vendor.index.navigateBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||||
|
return {
|
||||||
|
a: common_vendor.o((...args) => $options.search && $options.search(...args)),
|
||||||
|
b: $data.kw,
|
||||||
|
c: common_vendor.o(($event) => $data.kw = $event.detail.value),
|
||||||
|
d: common_vendor.o((...args) => $options.search && $options.search(...args)),
|
||||||
|
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),
|
||||||
|
d: p.id,
|
||||||
|
e: common_vendor.o(($event) => $options.select(p), p.id)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||||
|
wx.createPage(MiniProgramPage);
|
||||||
|
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/product/select.js.map
|
||||||
4
frontend/unpackage/dist/dev/mp-weixin/pages/product/select.json
vendored
Normal file
4
frontend/unpackage/dist/dev/mp-weixin/pages/product/select.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "选择商品",
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
1
frontend/unpackage/dist/dev/mp-weixin/pages/product/select.wxml
vendored
Normal file
1
frontend/unpackage/dist/dev/mp-weixin/pages/product/select.wxml
vendored
Normal file
@@ -0,0 +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="p" wx:key="d" class="item" bindtap="{{p.e}}"><view class="name">{{p.a}}</view><view class="meta">{{p.b}} · 库存:{{p.c}}</view></view></scroll-view></view>
|
||||||
15
frontend/unpackage/dist/dev/mp-weixin/pages/product/select.wxss
vendored
Normal file
15
frontend/unpackage/dist/dev/mp-weixin/pages/product/select.wxss
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
.page { display:flex; flex-direction: column; height: 100vh;
|
||||||
|
}
|
||||||
|
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff;
|
||||||
|
}
|
||||||
|
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx;
|
||||||
|
}
|
||||||
|
.list { flex:1;
|
||||||
|
}
|
||||||
|
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1;
|
||||||
|
}
|
||||||
|
.name { color:#333; margin-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
.meta { color:#888; font-size: 24rpx;
|
||||||
|
}
|
||||||
48
frontend/unpackage/dist/dev/mp-weixin/pages/supplier/select.js
vendored
Normal file
48
frontend/unpackage/dist/dev/mp-weixin/pages/supplier/select.js
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
"use strict";
|
||||||
|
const common_vendor = require("../../common/vendor.js");
|
||||||
|
const common_http = require("../../common/http.js");
|
||||||
|
const _sfc_main = {
|
||||||
|
data() {
|
||||||
|
return { kw: "", suppliers: [] };
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
this.search();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async search() {
|
||||||
|
try {
|
||||||
|
const res = await common_http.get("/api/suppliers", { kw: this.kw, page: 1, size: 50 });
|
||||||
|
this.suppliers = Array.isArray(res == null ? void 0 : res.list) ? res.list : Array.isArray(res) ? res : [];
|
||||||
|
} catch (e) {
|
||||||
|
common_vendor.index.showToast({ title: "加载失败", icon: "none" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select(s) {
|
||||||
|
const opener = getCurrentPages()[getCurrentPages().length - 2];
|
||||||
|
if (opener && opener.$vm) {
|
||||||
|
opener.$vm.order.supplierId = s.id;
|
||||||
|
opener.$vm.supplierName = s.name;
|
||||||
|
}
|
||||||
|
common_vendor.index.navigateBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||||
|
return {
|
||||||
|
a: common_vendor.o((...args) => $options.search && $options.search(...args)),
|
||||||
|
b: $data.kw,
|
||||||
|
c: common_vendor.o(($event) => $data.kw = $event.detail.value),
|
||||||
|
d: common_vendor.o((...args) => $options.search && $options.search(...args)),
|
||||||
|
e: common_vendor.f($data.suppliers, (s, k0, i0) => {
|
||||||
|
return {
|
||||||
|
a: common_vendor.t(s.name),
|
||||||
|
b: common_vendor.t(s.mobile || "—"),
|
||||||
|
c: s.id,
|
||||||
|
d: common_vendor.o(($event) => $options.select(s), s.id)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||||||
|
wx.createPage(MiniProgramPage);
|
||||||
|
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/supplier/select.js.map
|
||||||
4
frontend/unpackage/dist/dev/mp-weixin/pages/supplier/select.json
vendored
Normal file
4
frontend/unpackage/dist/dev/mp-weixin/pages/supplier/select.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "选择供应商",
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
1
frontend/unpackage/dist/dev/mp-weixin/pages/supplier/select.wxml
vendored
Normal file
1
frontend/unpackage/dist/dev/mp-weixin/pages/supplier/select.wxml
vendored
Normal file
@@ -0,0 +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="s" wx:key="c" class="item" bindtap="{{s.d}}"><view class="name">{{s.a}}</view><view class="meta">{{s.b}}</view></view></scroll-view></view>
|
||||||
15
frontend/unpackage/dist/dev/mp-weixin/pages/supplier/select.wxss
vendored
Normal file
15
frontend/unpackage/dist/dev/mp-weixin/pages/supplier/select.wxss
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
.page { display:flex; flex-direction: column; height: 100vh;
|
||||||
|
}
|
||||||
|
.search { display:flex; gap: 12rpx; padding: 16rpx; background:#fff;
|
||||||
|
}
|
||||||
|
.search input { flex:1; background:#f6f6f6; border-radius: 12rpx; padding: 12rpx;
|
||||||
|
}
|
||||||
|
.list { flex:1;
|
||||||
|
}
|
||||||
|
.item { padding: 20rpx 24rpx; background:#fff; border-bottom: 1rpx solid #f1f1f1;
|
||||||
|
}
|
||||||
|
.name { color:#333; margin-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
.meta { color:#888; font-size: 24rpx;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user