Compare commits
15 Commits
8cfa252d9a
...
9ff0984fe1
Author | SHA1 | Date |
---|---|---|
|
9ff0984fe1 | |
|
2132e0e1e5 | |
|
87574aebf6 | |
|
3130c26298 | |
|
032384cbe8 | |
|
79cc074bc2 | |
|
723ebd012d | |
|
195749ad6e | |
|
e23719854e | |
|
4bf21aa7f6 | |
|
089f78c836 | |
|
cbe79d1d6d | |
|
33ef5825b0 | |
|
2c2ca7745c | |
|
bd0cce7aab |
2
.env
2
.env
|
@ -1,7 +1,7 @@
|
||||||
# 所有环境的环境变量(命名必须以 VITE_ 开头)
|
# 所有环境的环境变量(命名必须以 VITE_ 开头)
|
||||||
|
|
||||||
## 项目标题
|
## 项目标题
|
||||||
VITE_APP_TITLE = 借还柜
|
VITE_APP_TITLE = 智柜通
|
||||||
|
|
||||||
## 路由模式 hash 或 html5
|
## 路由模式 hash 或 html5
|
||||||
VITE_ROUTER_HISTORY = hash
|
VITE_ROUTER_HISTORY = hash
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img alt="logo" width="120" height="120" src="./public/favicon.png">
|
<img alt="logo" width="120" height="120" src="./public/favicon.ico">
|
||||||
<h1>Mobile + Vue = MobVue</h1>
|
<h1>Mobile + Vue = MobVue</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img alt="logo" width="120" height="120" src="./public/favicon.png">
|
<img alt="logo" width="120" height="120" src="./public/favicon.ico">
|
||||||
<h1>Mobile + Vue = MobVue</h1>
|
<h1>Mobile + Vue = MobVue</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
|
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
|
||||||
/>
|
/>
|
||||||
<link rel="icon" href="/favicon.png" type="image/png" />
|
<link rel="icon" href="/favicon.ico" type="image/png" />
|
||||||
<link rel="stylesheet" href="/app-loading.css" />
|
<link rel="stylesheet" href="/app-loading.css" />
|
||||||
<title>%VITE_APP_TITLE%</title>
|
<title>%VITE_APP_TITLE%</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
BIN
public/c63.jpg
BIN
public/c63.jpg
Binary file not shown.
Before Width: | Height: | Size: 367 KiB |
BIN
public/c63.png
BIN
public/c63.png
Binary file not shown.
Before Width: | Height: | Size: 1.6 MiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 32 KiB |
|
@ -1,5 +1,6 @@
|
||||||
import { request } from '@/http/axios'
|
import { request } from '@/http/axios'
|
||||||
import {
|
import {
|
||||||
|
BindQyUserCommand,
|
||||||
GetTokenParams,
|
GetTokenParams,
|
||||||
LoginData,
|
LoginData,
|
||||||
LogoutResponse,
|
LogoutResponse,
|
||||||
|
@ -8,6 +9,7 @@ import {
|
||||||
VerifySmsParams,
|
VerifySmsParams,
|
||||||
WechatQrCodeParams
|
WechatQrCodeParams
|
||||||
} from './type'
|
} from './type'
|
||||||
|
import { ab98UserDTO } from '../shop/type'
|
||||||
|
|
||||||
/** 获取临时令牌 */
|
/** 获取临时令牌 */
|
||||||
export function getTokenApi(appName: string) {
|
export function getTokenApi(appName: string) {
|
||||||
|
@ -62,3 +64,11 @@ export function tokenLogin(token: string, userid: string, openid: string) {
|
||||||
params: { token, userid, openid }
|
params: { token, userid, openid }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function bindQyUserApi(data: BindQyUserCommand) {
|
||||||
|
return request<ApiResponseData<ab98UserDTO>>({
|
||||||
|
url: '/wx/login/bindQyUser',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
|
@ -59,3 +59,9 @@ export type VerifySmsParams = {
|
||||||
userid: string
|
userid: string
|
||||||
openid: string
|
openid: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BindQyUserCommand {
|
||||||
|
qyUserId: number;
|
||||||
|
name: string;
|
||||||
|
idNum: string;
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import { request } from '@/http/axios'
|
import { request } from '@/http/axios'
|
||||||
import { SubmitApprovalRequestData, SubmitApprovalResponseData, SearchApiReturnApprovalQuery, ApiResponsePageData, ReturnApprovalEntity, HandleApprovalRequestData } from './type'
|
import { SubmitApprovalRequestData, SubmitApprovalResponseData, SearchApiReturnApprovalQuery, ApiResponsePageData, ReturnApprovalEntity, HandleApprovalRequestData } from './type'
|
||||||
|
import { ShopOrderGoodsEntity } from '../shop/type'
|
||||||
|
|
||||||
export const getApprovalListApi = (params: SearchApiReturnApprovalQuery) => {
|
export const getApprovalListApi = (params: SearchApiReturnApprovalQuery) => {
|
||||||
return request<ApiResponsePageData<ReturnApprovalEntity>>({
|
return request<ApiResponsePageData<ReturnApprovalEntity>>({
|
||||||
|
@ -26,3 +27,11 @@ export const handleApprovalApi = (data: HandleApprovalRequestData) => {
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getApprovalOrderGoodsApi = (approvalId: number) => {
|
||||||
|
return request<ApiResponseMsgData<ShopOrderGoodsEntity[]>>({
|
||||||
|
url: 'approval/getApprovalOrderGoods',
|
||||||
|
method: 'get',
|
||||||
|
params: { approvalId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ export interface SubmitApprovalRequestData {
|
||||||
returnQuantity: number
|
returnQuantity: number
|
||||||
returnImages: string
|
returnImages: string
|
||||||
returnRemark: string
|
returnRemark: string
|
||||||
|
corpid: string
|
||||||
|
applyUserid: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HandleApprovalRequestData {
|
export interface HandleApprovalRequestData {
|
||||||
|
@ -14,6 +16,8 @@ export interface HandleApprovalRequestData {
|
||||||
auditImages: string
|
auditImages: string
|
||||||
auditRemark: string
|
auditRemark: string
|
||||||
userid: string
|
userid: string
|
||||||
|
corpid: string
|
||||||
|
auditUserid: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchApiReturnApprovalQuery {
|
export interface SearchApiReturnApprovalQuery {
|
||||||
|
@ -25,6 +29,8 @@ export interface SearchApiReturnApprovalQuery {
|
||||||
status?: number
|
status?: number
|
||||||
startTime?: string
|
startTime?: string
|
||||||
endTime?: string
|
endTime?: string
|
||||||
|
approvalType?: number
|
||||||
|
corpid?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiResponsePageData<T> {
|
export interface ApiResponsePageData<T> {
|
||||||
|
@ -37,16 +43,33 @@ export interface ApiResponsePageData<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReturnApprovalEntity {
|
export interface ReturnApprovalEntity {
|
||||||
|
/** 审批编号 */
|
||||||
approvalId: number
|
approvalId: number
|
||||||
|
/** 关联订单ID */
|
||||||
orderId: number
|
orderId: number
|
||||||
|
/** 关联商品ID */
|
||||||
goodsId: number
|
goodsId: number
|
||||||
/** 关联订单商品ID */
|
/** 关联订单商品ID */
|
||||||
orderGoodsId: number
|
orderGoodsId: number
|
||||||
|
/** 外部归属类型的商品ID */
|
||||||
|
externalGoodsId?: number
|
||||||
|
/** 外部归属类型的审批ID */
|
||||||
|
externalApprovalId?: number
|
||||||
|
/** 企业微信id */
|
||||||
|
corpid?: string
|
||||||
|
applyUserid?: string
|
||||||
|
auditUserid?: string
|
||||||
|
/** 申请数量 */
|
||||||
|
applyQuantity?: number
|
||||||
|
/** 审批类型(0为借还柜 1为固资通) */
|
||||||
|
approvalType?: number
|
||||||
|
/** 申请说明 */
|
||||||
|
applyRemark?: string
|
||||||
/** 归还数量 */
|
/** 归还数量 */
|
||||||
returnQuantity: number
|
returnQuantity: number
|
||||||
/** 商品单价 */
|
/** 商品单价 */
|
||||||
goodsPrice: number
|
goodsPrice: number
|
||||||
status: number
|
/** 退还金额 */
|
||||||
returnAmount: number
|
returnAmount: number
|
||||||
/** 归还图片路径数组 */
|
/** 归还图片路径数组 */
|
||||||
returnImages: string
|
returnImages: string
|
||||||
|
@ -56,6 +79,10 @@ export interface ReturnApprovalEntity {
|
||||||
returnRemark: string
|
returnRemark: string
|
||||||
/** 审核说明 */
|
/** 审核说明 */
|
||||||
auditRemark: string
|
auditRemark: string
|
||||||
|
/** 审批状态(1待审核 2已通过 3已驳回) */
|
||||||
|
status: number
|
||||||
|
/** 审批时间 */
|
||||||
|
approvalTime?: string
|
||||||
createTime: string
|
createTime: string
|
||||||
updateTime: string
|
updateTime: string
|
||||||
/** 商品名称 */
|
/** 商品名称 */
|
||||||
|
@ -72,6 +99,8 @@ export interface ReturnApprovalEntity {
|
||||||
isInternal: number
|
isInternal: number
|
||||||
/** 审核人姓名 */
|
/** 审核人姓名 */
|
||||||
auditName: string
|
auditName: string
|
||||||
|
/** 支付方式 */
|
||||||
|
paymentMethod?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SubmitApprovalResponseData = ApiResponseMsgData<{
|
export type SubmitApprovalResponseData = ApiResponseMsgData<{
|
||||||
|
|
|
@ -3,10 +3,13 @@ import type { CabinetDetailResponse } from './type'
|
||||||
import { OpenCabinetApiData } from '../shop/type'
|
import { OpenCabinetApiData } from '../shop/type'
|
||||||
|
|
||||||
/** 获取智能柜详情接口 */
|
/** 获取智能柜详情接口 */
|
||||||
export function getCabinetDetailApi() {
|
export function getCabinetDetailApi(shopId: number) {
|
||||||
return request<CabinetDetailResponse>({
|
return request<CabinetDetailResponse>({
|
||||||
url: 'cabinet/detail',
|
url: 'cabinet/detail',
|
||||||
method: 'get'
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
shopId
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,3 +20,25 @@ export function openCabinet(cabinetId: number, pinNo: number, data: OpenCabinetA
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const configureGoodsCellsStock = (cellId: number, goodsId: number, stock: number) => {
|
||||||
|
return request<ApiResponseData<void>>({
|
||||||
|
url: `/cabinet/configureGoodsCellsStock/${cellId}/${goodsId}/${stock}`,
|
||||||
|
method: 'put'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const changeGoodsCellsStock = (cellId: number, stock: number) => {
|
||||||
|
return request<ApiResponseData<void>>({
|
||||||
|
url: `/cabinet/changeGoodsCellsStock/${cellId}/${stock}`,
|
||||||
|
method: 'put'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearGoodsCells = (cellId: number) => {
|
||||||
|
return request<ApiResponseData<void>>({
|
||||||
|
url: `/cabinet/clearGoodsCells/${cellId}`,
|
||||||
|
method: 'put'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
import { request } from "@/http/axios"
|
import { request } from "@/http/axios"
|
||||||
import { GetBalanceResponse, GetOrdersByOpenIdDTO, OpenCabinetApiData, QyLoginDTO, QyLoginRequestParams, ShopGoodsResponseData, SubmitOrderRequestData, SubmitOrderResponseData } from './type'
|
import { GetBalanceResponse, GetOrdersByOpenIdDTO, OpenCabinetApiData, QyLoginDTO, QyLoginRequestParams, SearchGoodsDO, ShopEntity, ShopGoodsEntity, ShopGoodsResponseData, SubmitOrderRequestData, SubmitOrderResponseData } from './type'
|
||||||
import { GetOpenIdRequestParams } from './type'
|
import { GetOpenIdRequestParams } from './type'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** 获取商品列表 */
|
/** 获取商品列表 */
|
||||||
|
export function getShopGoodsListApi(corpid: string, belongType: number) {
|
||||||
|
return request<ApiResponseData<SearchGoodsDO[]>>({
|
||||||
|
url: "shop/goods/list",
|
||||||
|
method: "get",
|
||||||
|
params: { corpid, belongType }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getShopGoodsApi(shopId: number|null) {
|
export function getShopGoodsApi(shopId: number|null) {
|
||||||
return request<ShopGoodsResponseData>({
|
return request<ShopGoodsResponseData>({
|
||||||
url: "shop/goods",
|
url: "shop/goods",
|
||||||
|
@ -59,11 +67,11 @@ export function openCabinetApi(orderId: number, orderGoodsId: number, data: Open
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取用户余额接口 */
|
/** 获取用户余额接口 */
|
||||||
export function getBalanceApi(openid: string) {
|
export function getBalanceApi(corpid: string, openid: string) {
|
||||||
return request<ApiResponseData<GetBalanceResponse>>({
|
return request<ApiResponseData<GetBalanceResponse>>({
|
||||||
url: "payment/getBalance",
|
url: "payment/getBalance",
|
||||||
method: "get",
|
method: "get",
|
||||||
params: { openid }
|
params: { corpid, openid }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export function getBalanceByQyUserid(corpid: string, userid: string) {
|
export function getBalanceByQyUserid(corpid: string, userid: string) {
|
||||||
|
@ -73,3 +81,17 @@ export function getBalanceByQyUserid(corpid: string, userid: string) {
|
||||||
params: { corpid, userid }
|
params: { corpid, userid }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getShopListApi(corpid: string, mode?: number) {
|
||||||
|
const params: any = {
|
||||||
|
corpid
|
||||||
|
};
|
||||||
|
if (typeof mode !== 'undefined') {
|
||||||
|
params.mode = mode;
|
||||||
|
}
|
||||||
|
return request<ApiResponseData<ShopEntity[]>>({
|
||||||
|
url: "shop/list",
|
||||||
|
method: "get",
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
|
@ -8,7 +8,41 @@ export type Goods = {
|
||||||
coverImg: string,
|
coverImg: string,
|
||||||
goodsDetail: string,
|
goodsDetail: string,
|
||||||
usageInstruction: string,
|
usageInstruction: string,
|
||||||
cellId: number
|
cellId: number,
|
||||||
|
belongType: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShopGoodsEntity {
|
||||||
|
/** 商品唯一ID */
|
||||||
|
goodsId: number;
|
||||||
|
/** 商品名称 */
|
||||||
|
goodsName: string;
|
||||||
|
/** 商品分类ID */
|
||||||
|
categoryId: number;
|
||||||
|
/** 外部归属类型的商品ID */
|
||||||
|
externalGoodsId?: number;
|
||||||
|
/** 企业微信id */
|
||||||
|
corpid?: string;
|
||||||
|
/** 每人每月限购数量 */
|
||||||
|
monthlyPurchaseLimit?: number;
|
||||||
|
/** 销售价格 */
|
||||||
|
price: number;
|
||||||
|
/** 库存数量 */
|
||||||
|
stock: number;
|
||||||
|
/** 商品状态(1上架 2下架) */
|
||||||
|
status: number;
|
||||||
|
/** 免审批(0否 1是) */
|
||||||
|
autoApproval?: number;
|
||||||
|
/** 封面图URL */
|
||||||
|
coverImg?: string;
|
||||||
|
/** 商品详情(支持2000汉字+10个图片链接) */
|
||||||
|
goodsDetail?: string;
|
||||||
|
/** 备注 */
|
||||||
|
remark?: string;
|
||||||
|
/** 商品使用说明 */
|
||||||
|
usageInstruction?: string;
|
||||||
|
/** 归属类型(0-借还柜 1-固资通) */
|
||||||
|
belongType?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type category = {
|
export type category = {
|
||||||
|
@ -25,7 +59,7 @@ export interface SubmitOrderRequestData {
|
||||||
/** 企业ID */
|
/** 企业ID */
|
||||||
corpid: string;
|
corpid: string;
|
||||||
/** 支付类型 wechat:微信 balance:余额 */
|
/** 支付类型 wechat:微信 balance:余额 */
|
||||||
paymentType: 'wechat' | 'balance';
|
paymentType: 'wechat' | 'balance' | "approval";
|
||||||
/** 联系电话 */
|
/** 联系电话 */
|
||||||
mobile: string;
|
mobile: string;
|
||||||
/** 用户姓名 */
|
/** 用户姓名 */
|
||||||
|
@ -34,6 +68,7 @@ export interface SubmitOrderRequestData {
|
||||||
qyUserid: string;
|
qyUserid: string;
|
||||||
/** 是否内部订单 0否 1汇邦云用户 2企业微信用户 */
|
/** 是否内部订单 0否 1汇邦云用户 2企业微信用户 */
|
||||||
isInternal: number;
|
isInternal: number;
|
||||||
|
applyRemark: string;
|
||||||
/** 订单商品明细列表 */
|
/** 订单商品明细列表 */
|
||||||
goodsList: Array<{
|
goodsList: Array<{
|
||||||
goodsId: number
|
goodsId: number
|
||||||
|
@ -83,13 +118,30 @@ export interface ShopOrderEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShopOrderGoodsEntity {
|
export interface ShopOrderGoodsEntity {
|
||||||
|
/** 订单商品唯一ID */
|
||||||
orderGoodsId: number
|
orderGoodsId: number
|
||||||
|
/** 关联订单ID */
|
||||||
orderId: number
|
orderId: number
|
||||||
|
/** 审批ID */
|
||||||
|
approvalId: number
|
||||||
|
/** 关联商品ID */
|
||||||
goodsId: number
|
goodsId: number
|
||||||
|
/** 关联格口ID */
|
||||||
|
cellId: number
|
||||||
|
/** 购买数量 */
|
||||||
quantity: number
|
quantity: number
|
||||||
|
/** 购买时单价 */
|
||||||
price: number
|
price: number
|
||||||
|
/** 商品总金额 */
|
||||||
totalAmount: number
|
totalAmount: number
|
||||||
|
/** 商品名称 */
|
||||||
|
goodsName: string
|
||||||
|
/** 封面图URL */
|
||||||
|
coverImg: string
|
||||||
|
/** 商品状态(1正常 2已退货 3已换货 4已完成 5审核中 6退货未通过) */
|
||||||
status: number
|
status: number
|
||||||
|
/** 企业微信id */
|
||||||
|
corpid: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetOrdersByOpenIdDTO {
|
export interface GetOrdersByOpenIdDTO {
|
||||||
|
@ -110,10 +162,39 @@ export interface GetBalanceResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QyLoginDTO {
|
export interface QyLoginDTO {
|
||||||
userid: string
|
userid: string;
|
||||||
openid: string
|
openid: string;
|
||||||
isCabinetAdmin: number
|
isCabinetAdmin: number;
|
||||||
name: string
|
qyUserId: number;
|
||||||
|
name: string;
|
||||||
|
ab98User: ab98UserDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ab98UserDTO {
|
||||||
|
/** 主键ID */
|
||||||
|
ab98UserId?: number;
|
||||||
|
/** openid */
|
||||||
|
openid?: string;
|
||||||
|
/** 汇邦云用户唯一ID */
|
||||||
|
userid?: string;
|
||||||
|
/** 真实姓名 */
|
||||||
|
name?: string;
|
||||||
|
/** 手机号码 */
|
||||||
|
tel?: string;
|
||||||
|
/** 身份证号码 */
|
||||||
|
idnum?: string;
|
||||||
|
/** 性别(男 女) */
|
||||||
|
sex?: string;
|
||||||
|
/** 人脸照片地址 */
|
||||||
|
faceImg?: string;
|
||||||
|
/** 身份证正面地址 */
|
||||||
|
idcardFront?: string;
|
||||||
|
/** 身份证背面地址 */
|
||||||
|
idcardBack?: string;
|
||||||
|
/** 身份证登记地址 */
|
||||||
|
address?: string;
|
||||||
|
/** 是否已注册(0未注册 1已注册) */
|
||||||
|
registered?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OpenCabinetApiData {
|
export interface OpenCabinetApiData {
|
||||||
|
@ -130,3 +211,35 @@ export interface OpenCabinetApiData {
|
||||||
// 操作类型(1用户 2管理员)
|
// 操作类型(1用户 2管理员)
|
||||||
operationType: number
|
operationType: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ShopEntity {
|
||||||
|
/** 主键ID */
|
||||||
|
shopId: number;
|
||||||
|
/** 商店名称 */
|
||||||
|
shopName: string;
|
||||||
|
/** 企业微信id */
|
||||||
|
corpid: string;
|
||||||
|
/** 运行模式(0-支付模式 1-审批模式 2-借还模式 3-会员模式 4-耗材模式) */
|
||||||
|
mode?: number;
|
||||||
|
/** 借呗支付(1-正常使用 0-禁止使用) */
|
||||||
|
balanceEnable?: number;
|
||||||
|
/** 封面图URL */
|
||||||
|
coverImg?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchGoodsDO extends ShopGoodsEntity {
|
||||||
|
/** 分类名称 */
|
||||||
|
categoryName?: string;
|
||||||
|
/** 柜子ID */
|
||||||
|
cabinetId?: number;
|
||||||
|
/** 柜子名称 */
|
||||||
|
cabinetName?: string;
|
||||||
|
/** 商店名称字符串 */
|
||||||
|
shopNameStr?: string;
|
||||||
|
/** 格口编号 */
|
||||||
|
cellNo?: number;
|
||||||
|
/** 格口编号字符串 */
|
||||||
|
cellNoStr?: string;
|
||||||
|
/** 已分配库存 */
|
||||||
|
totalStock?: number;
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 un-flex-y-center>
|
<h1 un-flex-y-center>
|
||||||
<img src="/favicon.png" un-w-38px un-h-38px>
|
<img src="/favicon.ico" un-w-38px un-h-38px>
|
||||||
<span un-ml-16px un-text-32px un-fw400>MobVue</span>
|
<span un-ml-16px un-text-32px un-fw400>MobVue</span>
|
||||||
</h1>
|
</h1>
|
||||||
<h2 un-color-hex-969799 un-text-14px un-fw400>
|
<h2 un-color-hex-969799 un-text-14px un-fw400>
|
||||||
|
|
|
@ -26,7 +26,9 @@ const formData = ref<HandleApprovalRequestData>({
|
||||||
returnAmount: approvalStore.currentApproval?.goodsPrice || 0,
|
returnAmount: approvalStore.currentApproval?.goodsPrice || 0,
|
||||||
auditImages: '',
|
auditImages: '',
|
||||||
auditRemark: '',
|
auditRemark: '',
|
||||||
userid: wxStore.userid
|
userid: wxStore.userid,
|
||||||
|
corpid: wxStore.corpid,
|
||||||
|
auditUserid: wxStore.userid
|
||||||
})
|
})
|
||||||
|
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
|
@ -167,6 +169,8 @@ onMounted(() => {
|
||||||
// 填充历史审批数据
|
// 填充历史审批数据
|
||||||
formData.value = {
|
formData.value = {
|
||||||
...formData.value,
|
...formData.value,
|
||||||
|
corpid: wxStore.corpid,
|
||||||
|
auditUserid: wxStore.userid,
|
||||||
status: approvalStore.currentApproval.status,
|
status: approvalStore.currentApproval.status,
|
||||||
returnAmount: approvalStore.currentApproval.returnAmount,
|
returnAmount: approvalStore.currentApproval.returnAmount,
|
||||||
auditRemark: approvalStore.currentApproval.auditRemark,
|
auditRemark: approvalStore.currentApproval.auditRemark,
|
||||||
|
@ -223,6 +227,7 @@ const handleSubmit = async () => {
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
formData.value.userid = wxStore.userid;
|
formData.value.userid = wxStore.userid;
|
||||||
|
formData.value.corpid = wxStore.corpid;
|
||||||
const { code, msg } = await handleApprovalApi(formData.value)
|
const { code, msg } = await handleApprovalApi(formData.value)
|
||||||
|
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
|
|
|
@ -0,0 +1,440 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { showConfirmDialog, showSuccessToast, showFailToast, showToast, UploaderFileListItem, Popup, Picker } from 'vant'
|
||||||
|
import axios from "axios"
|
||||||
|
import { getApprovalOrderGoodsApi, handleApprovalApi } from '@/common/apis/approval'
|
||||||
|
import { openCabinetApi } from '@/common/apis/shop'
|
||||||
|
import type { HandleApprovalRequestData } from '@/common/apis/approval/type'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { useApprovalStore } from '@/pinia/stores/approval'
|
||||||
|
import { useWxStore } from '@/pinia/stores/wx';
|
||||||
|
import Compressor from 'compressorjs';
|
||||||
|
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
|
||||||
|
import { ShopOrderGoodsEntity } from '@/common/apis/shop/type'
|
||||||
|
|
||||||
|
const { VITE_APP_BASE_API } = import.meta.env;
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
const approvalStore = useApprovalStore();
|
||||||
|
const wxStore = useWxStore();
|
||||||
|
const { openid, balance, corpidLogin, userid: qyUserid, name: qyName } = storeToRefs(wxStore);
|
||||||
|
const ab98UserStore = useAb98UserStore();
|
||||||
|
const { tel, userid: ab98Userid, name } = storeToRefs(ab98UserStore);
|
||||||
|
|
||||||
|
const formData = ref<HandleApprovalRequestData>({
|
||||||
|
approvalId: approvalStore.currentApproval?.approvalId || 0,
|
||||||
|
status: 2,
|
||||||
|
returnAmount: approvalStore.currentApproval?.goodsPrice || 0,
|
||||||
|
auditImages: '',
|
||||||
|
auditRemark: '',
|
||||||
|
userid: wxStore.userid,
|
||||||
|
corpid: wxStore.corpid,
|
||||||
|
auditUserid: wxStore.userid
|
||||||
|
})
|
||||||
|
|
||||||
|
const orderGoods = ref<ShopOrderGoodsEntity[]>([]);
|
||||||
|
const submitting = ref(false)
|
||||||
|
const fileList = ref<UploaderFileListItem[]>([])
|
||||||
|
const uploading = ref(false)
|
||||||
|
const isButtonDisabled = ref(false)
|
||||||
|
|
||||||
|
const showStatusPicker = ref(false)
|
||||||
|
const showPreview = ref(false)
|
||||||
|
const currentPreviewImage = ref('')
|
||||||
|
const statusMap: { [key: number]: string } = {
|
||||||
|
1: '待审核',
|
||||||
|
2: '已通过',
|
||||||
|
3: '已驳回'
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusOptions = [
|
||||||
|
{ text: '待审核', value: 1 },
|
||||||
|
{ text: '通过', value: 2 },
|
||||||
|
{ text: '驳回', value: 3 }
|
||||||
|
]
|
||||||
|
const validateForm = () => {
|
||||||
|
if (!formData.value.approvalId || isNaN(formData.value.approvalId)) {
|
||||||
|
showConfirmDialog({ title: '错误', message: '审批单ID参数错误' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (formData.value.status === 3 && !formData.value.auditRemark) {
|
||||||
|
showConfirmDialog({ title: '提示', message: '驳回时必须填写审核说明' })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFileUpload = async (items: UploaderFileListItem | UploaderFileListItem[]) => {
|
||||||
|
const files = Array.isArray(items) ? items : [items]
|
||||||
|
uploading.value = true
|
||||||
|
try {
|
||||||
|
const uploadPromises = files.map(async (item) => {
|
||||||
|
item.status = 'uploading'
|
||||||
|
item.message = '上传中...'
|
||||||
|
const file = item.file as File
|
||||||
|
let compressedFile = file;
|
||||||
|
try {
|
||||||
|
compressedFile = await new Promise<File>((resolve, reject) => {
|
||||||
|
new Compressor(file, {
|
||||||
|
quality: 0.8,
|
||||||
|
maxWidth: 1280,
|
||||||
|
maxHeight: 1280,
|
||||||
|
success(result) {
|
||||||
|
resolve(new File([result], file.name, {
|
||||||
|
type: 'image/jpeg',
|
||||||
|
lastModified: Date.now()
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
error(err) {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('压缩失败:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', compressedFile)
|
||||||
|
|
||||||
|
const { data } = await axios.post<{
|
||||||
|
code: number
|
||||||
|
data: { url: string }
|
||||||
|
message?: string
|
||||||
|
}>(VITE_APP_BASE_API + '/file/upload', formData, {
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (data.code !== 0) {
|
||||||
|
throw new Error(data.message || '文件上传失败')
|
||||||
|
}
|
||||||
|
return { url: data.data.url }
|
||||||
|
})
|
||||||
|
|
||||||
|
const urls = await Promise.all(uploadPromises)
|
||||||
|
files.forEach((item, index) => {
|
||||||
|
item.status = 'done'
|
||||||
|
item.message = '上传成功'
|
||||||
|
item.url = urls[index].url
|
||||||
|
})
|
||||||
|
formData.value.auditImages = fileList.value.map(item => item.url).join(',')
|
||||||
|
} catch (error) {
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '上传失败',
|
||||||
|
message: error instanceof Error ? error.message : '未知错误'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
uploading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onStatusConfirm = ({ selectedOptions }: { selectedOptions: { value: number }[] }) => {
|
||||||
|
submitting.value = true
|
||||||
|
try {
|
||||||
|
if (!selectedOptions?.[0]?.value) {
|
||||||
|
throw new Error('请选择有效的审批状态')
|
||||||
|
}
|
||||||
|
formData.value.status = selectedOptions[0].value
|
||||||
|
showStatusPicker.value = false
|
||||||
|
} catch (error) {
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '操作失败',
|
||||||
|
message: error instanceof Error ? error.message : '状态选择异常'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const previewImage = (url: string) => {
|
||||||
|
currentPreviewImage.value = url
|
||||||
|
showPreview.value = true
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
if (!approvalStore.currentApproval) {
|
||||||
|
router.push('/approval/list');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getApprovalOrderGoodsApi(approvalStore.currentApproval.approvalId).then(({ data }) => {
|
||||||
|
orderGoods.value = data;
|
||||||
|
})
|
||||||
|
|
||||||
|
if (approvalStore.currentApproval.status !== 1) {
|
||||||
|
// 填充历史审批数据
|
||||||
|
formData.value = {
|
||||||
|
...formData.value,
|
||||||
|
corpid: wxStore.corpid,
|
||||||
|
status: approvalStore.currentApproval.status,
|
||||||
|
returnAmount: approvalStore.currentApproval.returnAmount,
|
||||||
|
auditRemark: approvalStore.currentApproval.auditRemark,
|
||||||
|
auditImages: approvalStore.currentApproval.auditImages
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文件回显
|
||||||
|
if (approvalStore.currentApproval.auditImages) {
|
||||||
|
fileList.value = approvalStore.currentApproval.auditImages.split(',').map(url => ({
|
||||||
|
url,
|
||||||
|
status: 'done',
|
||||||
|
message: '已上传'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleOpenCabinet = async () => {
|
||||||
|
if (!approvalStore.currentApproval?.orderId || !approvalStore.currentApproval?.orderGoodsId) {
|
||||||
|
showFailToast('缺少订单信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isButtonDisabled.value = true
|
||||||
|
try {
|
||||||
|
const isInternal = corpidLogin.value ? 2 : ab98Userid.value ? 1 : 0;
|
||||||
|
const result = await openCabinetApi(
|
||||||
|
approvalStore.currentApproval.orderId,
|
||||||
|
approvalStore.currentApproval.orderGoodsId, {
|
||||||
|
userid: isInternal === 2 ? qyUserid.value : ab98Userid.value,
|
||||||
|
isInternal: isInternal,
|
||||||
|
name: isInternal === 2 ? qyName.value : name.value,
|
||||||
|
mobile: tel.value,
|
||||||
|
operationType: 2
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (result.code !== 0) {
|
||||||
|
showFailToast(result.msg || '开启失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
showSuccessToast('柜口已开启')
|
||||||
|
} catch (error) {
|
||||||
|
showFailToast('请求失败')
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => {
|
||||||
|
isButtonDisabled.value = false
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!validateForm()) return
|
||||||
|
|
||||||
|
submitting.value = true
|
||||||
|
try {
|
||||||
|
formData.value.userid = wxStore.userid;
|
||||||
|
formData.value.corpid = wxStore.corpid;
|
||||||
|
formData.value.auditUserid = wxStore.userid;
|
||||||
|
const { code, msg } = await handleApprovalApi(formData.value)
|
||||||
|
|
||||||
|
if (code === 0) {
|
||||||
|
showSuccessToast('操作成功')
|
||||||
|
await showConfirmDialog({
|
||||||
|
title: "操作成功",
|
||||||
|
message: `审批处理已完成`
|
||||||
|
})
|
||||||
|
router.push('/approval/list')
|
||||||
|
} else {
|
||||||
|
console.error('操作失败code:', code, 'msg:', msg)
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '操作失败',
|
||||||
|
message: msg || '操作失败'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('提交失败:', error)
|
||||||
|
showConfirmDialog({
|
||||||
|
title: '提交失败',
|
||||||
|
message: error instanceof Error ? error.message : '网络请求异常'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleComfirm = () => {
|
||||||
|
formData.value.status = 2;
|
||||||
|
handleSubmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReject = () => {
|
||||||
|
formData.value.status = 3;
|
||||||
|
handleSubmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="approval-container">
|
||||||
|
<van-nav-bar title="审批处理" left-text="返回" left-arrow fixed @click-left="() => router.go(-1)" />
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<van-cell-group >
|
||||||
|
<van-cell title="申请人" :value="approvalStore.currentApproval?.name" />
|
||||||
|
<van-cell title="手机号" :value="approvalStore.currentApproval?.mobile" />
|
||||||
|
<van-cell title="当前状态" :value="statusMap[approvalStore.currentApproval?.status || 1]" />
|
||||||
|
<!-- <van-cell title="领用说明" :value="approvalStore.currentApproval?.applyRemark" /> -->
|
||||||
|
</van-cell-group>
|
||||||
|
|
||||||
|
<van-cell-group class="product-list">
|
||||||
|
<template v-for="item in orderGoods" :key="item.goodsId">
|
||||||
|
<van-cell class="product-item">
|
||||||
|
<template #icon>
|
||||||
|
<van-image :src="item.coverImg" width="60" height="60" class="product-image" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="product-info">
|
||||||
|
<div class="product-name van-ellipsis">
|
||||||
|
{{ item.goodsName }}
|
||||||
|
</div>
|
||||||
|
<div class="price-row">
|
||||||
|
<span class="product-price">
|
||||||
|
¥{{ item.price.toFixed(2) }}
|
||||||
|
</span>
|
||||||
|
<span class="quantity">
|
||||||
|
×{{ item.quantity }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-cell>
|
||||||
|
</template>
|
||||||
|
</van-cell-group>
|
||||||
|
|
||||||
|
<van-cell-group>
|
||||||
|
<!-- 原有表单字段保持不变 -->
|
||||||
|
<van-button type="primary" size="small"
|
||||||
|
@click="handleOpenCabinet" :disabled="isButtonDisabled" style="margin-bottom: 12px">
|
||||||
|
打开柜子
|
||||||
|
</van-button>
|
||||||
|
<van-field v-model="formData.auditRemark" label="审核说明" type="textarea" rows="2" autosize
|
||||||
|
class="audit-remark-field" />
|
||||||
|
</van-cell-group>
|
||||||
|
|
||||||
|
<van-popup v-model:show="showPreview" position="center" :style="{ width: '90%', height: '80%' }" round>
|
||||||
|
<div class="preview-wrapper">
|
||||||
|
<van-icon name="cross" class="close-icon" @click="showPreview = false" />
|
||||||
|
<van-image :src="currentPreviewImage" fit="contain" class="preview-image" />
|
||||||
|
</div>
|
||||||
|
</van-popup>
|
||||||
|
|
||||||
|
<div class="submit-bar" v-if="approvalStore.currentApproval?.status !== 2">
|
||||||
|
<van-button type="danger" block :loading="submitting" loading-text="提交中..." @click="handleReject">
|
||||||
|
拒绝
|
||||||
|
</van-button>
|
||||||
|
<van-button type="primary" block :loading="submitting" loading-text="提交中..." @click="handleComfirm">
|
||||||
|
同意
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.approval-container {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
padding-top: 46px;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-list {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-item {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-image {
|
||||||
|
margin-right: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-price {
|
||||||
|
color: #e95d5d;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity {
|
||||||
|
color: #666;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-section {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-wrapper {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.close-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 1;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-bar {
|
||||||
|
position: sticky;
|
||||||
|
bottom: 20px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 0 16px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
padding-bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-remark-field::v-deep .van-field__control {
|
||||||
|
background-color: #fffbe6;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable-status-field:not(:disabled)::v-deep .van-field__control {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background-color: #f7f8fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable-status-field:not(:disabled):hover::v-deep .van-field__control {
|
||||||
|
background-color: #f2f3f5;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable-status-field:not(:disabled):active::v-deep .van-field__control {
|
||||||
|
background-color: #ebedf0;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -59,11 +59,15 @@ import { getApprovalListApi } from '@/common/apis/approval'
|
||||||
import type { SearchApiReturnApprovalQuery, ReturnApprovalEntity } from '@/common/apis/approval/type'
|
import type { SearchApiReturnApprovalQuery, ReturnApprovalEntity } from '@/common/apis/approval/type'
|
||||||
import type { PickerConfirmEventParams } from 'vant/es';
|
import type { PickerConfirmEventParams } from 'vant/es';
|
||||||
import { useApprovalStore } from '@/pinia/stores/approval';
|
import { useApprovalStore } from '@/pinia/stores/approval';
|
||||||
|
import { useWxStore } from '@/pinia/stores/wx';
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
|
const wxStore = useWxStore();
|
||||||
|
|
||||||
// 搜索参数
|
// 搜索参数
|
||||||
const searchParams = reactive<SearchApiReturnApprovalQuery>({
|
const searchParams = reactive<SearchApiReturnApprovalQuery>({
|
||||||
|
corpid: wxStore.corpid,
|
||||||
|
approvalType: 0,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
})
|
})
|
||||||
|
@ -98,9 +102,9 @@ const handleCellClick = (approvalId: number) => {
|
||||||
const approvalStore = useApprovalStore()
|
const approvalStore = useApprovalStore()
|
||||||
const currentItem = list.value.find(item => item.approvalId === approvalId)
|
const currentItem = list.value.find(item => item.approvalId === approvalId)
|
||||||
if (currentItem) {
|
if (currentItem) {
|
||||||
approvalStore.setCurrentApproval(currentItem)
|
approvalStore.setCurrentApproval(currentItem);
|
||||||
}
|
}
|
||||||
router.push(`/approval/handle/${approvalId}`)
|
router.push(`/approval/handle/${approvalId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 状态标签类型
|
// 状态标签类型
|
||||||
|
@ -158,6 +162,7 @@ const handleReset = () => {
|
||||||
// 加载数据
|
// 加载数据
|
||||||
const onLoad = async () => {
|
const onLoad = async () => {
|
||||||
try {
|
try {
|
||||||
|
searchParams.corpid = wxStore.corpid;
|
||||||
const { data } = await getApprovalListApi(searchParams)
|
const { data } = await getApprovalListApi(searchParams)
|
||||||
list.value.push(...data.rows)
|
list.value.push(...data.rows)
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
@ -174,7 +179,7 @@ const onLoad = async () => {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="scss" scoped>
|
||||||
.approval-list-container {
|
.approval-list-container {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,9 @@ const formData = ref<SubmitApprovalRequestData>({
|
||||||
orderGoodsId: Number(route.query.orderGoodsId),
|
orderGoodsId: Number(route.query.orderGoodsId),
|
||||||
returnQuantity: 1,
|
returnQuantity: 1,
|
||||||
returnImages: '',
|
returnImages: '',
|
||||||
returnRemark: ''
|
returnRemark: '',
|
||||||
|
corpid: wxStore.corpid,
|
||||||
|
applyUserid: wxStore.userid,
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => route.query.orderGoodsId, (newVal) => {
|
watch(() => route.query.orderGoodsId, (newVal) => {
|
||||||
|
@ -117,7 +119,9 @@ const handleSubmit = async () => {
|
||||||
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
formData.value.returnImages = fileList.value.map(item => item.url).join(',')
|
formData.value.returnImages = fileList.value.map(item => item.url).join(',');
|
||||||
|
formData.value.corpid = wxStore.corpid;
|
||||||
|
formData.value.applyUserid = wxStore.userid;
|
||||||
|
|
||||||
const { code, data } = await submitApprovalApi(formData.value)
|
const { code, data } = await submitApprovalApi(formData.value)
|
||||||
|
|
||||||
|
@ -135,7 +139,7 @@ const handleSubmit = async () => {
|
||||||
console.error('提交失败:', error)
|
console.error('提交失败:', error)
|
||||||
showConfirmDialog({
|
showConfirmDialog({
|
||||||
title: '提交失败',
|
title: '提交失败',
|
||||||
message: error instanceof Error ? error.message : '网络请求异常'
|
message: error instanceof Error ? error.name + error.message + error.cause + error.stack : '网络请求异常'
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
submitting.value = false
|
submitting.value = false
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
<template>
|
||||||
|
<div class="approval-list-container">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<van-form @submit="handleSearch">
|
||||||
|
<van-field v-model="searchParams.approvalId" label="审批单号" type="number" placeholder="请输入审批单号" />
|
||||||
|
|
||||||
|
<van-field v-model="searchParams.orderId" label="订单编号" type="number" placeholder="请输入订单编号" />
|
||||||
|
|
||||||
|
<van-field v-model="searchParams.goodsId" label="商品ID" type="number" placeholder="请输入商品ID" />
|
||||||
|
|
||||||
|
<van-field name="status" label="审批状态" readonly clickable v-model="statusText" placeholder="请选择状态"
|
||||||
|
@click="showStatusPicker = true" />
|
||||||
|
<van-popup v-model:show="showStatusPicker" position="bottom">
|
||||||
|
<van-picker :columns="statusOptions" @confirm="onStatusConfirm" @cancel="showStatusPicker = false" />
|
||||||
|
</van-popup>
|
||||||
|
|
||||||
|
<!-- <van-field
|
||||||
|
readonly
|
||||||
|
clickable
|
||||||
|
name="date"
|
||||||
|
:value="dateRangeText"
|
||||||
|
label="申请时间"
|
||||||
|
placeholder="选择时间范围"
|
||||||
|
@click="showDatePicker = true"
|
||||||
|
/>
|
||||||
|
<van-popup v-model:show="showDatePicker" position="bottom">
|
||||||
|
<van-datetime-picker
|
||||||
|
type="daterange"
|
||||||
|
@confirm="onDateConfirm"
|
||||||
|
@cancel="showDatePicker = false"
|
||||||
|
/>
|
||||||
|
</van-popup> -->
|
||||||
|
|
||||||
|
<div style="margin: 16px;">
|
||||||
|
<van-button block type="primary" native-type="submit">搜索</van-button>
|
||||||
|
<van-button block plain type="primary" style="margin-top: 10px;" @click="handleReset">重置</van-button>
|
||||||
|
</div>
|
||||||
|
</van-form>
|
||||||
|
|
||||||
|
<!-- 审批列表 -->
|
||||||
|
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
|
||||||
|
<van-cell v-for="item in list" :key="item.approvalId" :title="`审批单号:${item.approvalId}`" clickable
|
||||||
|
@click="handleCellClick(item.approvalId)">
|
||||||
|
<template #label>
|
||||||
|
<div>商品名称:{{ item.goodsName }}</div>
|
||||||
|
<div>申请时间:{{ item.createTime }}</div>
|
||||||
|
<van-tag :type="statusTagType(item.status)">
|
||||||
|
{{ statusMap[item.status] }}
|
||||||
|
</van-tag>
|
||||||
|
</template>
|
||||||
|
</van-cell>
|
||||||
|
</van-list>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { getApprovalListApi } from '@/common/apis/approval'
|
||||||
|
import type { SearchApiReturnApprovalQuery, ReturnApprovalEntity } from '@/common/apis/approval/type'
|
||||||
|
import type { PickerConfirmEventParams } from 'vant/es';
|
||||||
|
import { useApprovalStore } from '@/pinia/stores/approval';
|
||||||
|
import { useWxStore } from '@/pinia/stores/wx';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const wxStore = useWxStore();
|
||||||
|
|
||||||
|
// 搜索参数
|
||||||
|
const searchParams = reactive<SearchApiReturnApprovalQuery>({
|
||||||
|
corpid: wxStore.corpid,
|
||||||
|
approvalType: 1,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 状态选择相关
|
||||||
|
const showStatusPicker = ref(false)
|
||||||
|
const statusOptions = [
|
||||||
|
{ text: '全部', value: undefined },
|
||||||
|
{ text: '待审核', value: 1 },
|
||||||
|
{ text: '已通过', value: 2 },
|
||||||
|
{ text: '已驳回', value: 3 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const statusMap: { [key: number]: string } = {
|
||||||
|
1: '待审核',
|
||||||
|
2: '已通过',
|
||||||
|
3: '已驳回'
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = ref('')
|
||||||
|
|
||||||
|
// 时间选择相关
|
||||||
|
const showDatePicker = ref(false)
|
||||||
|
const dateRangeText = ref('')
|
||||||
|
|
||||||
|
// 列表相关
|
||||||
|
const list = ref<ReturnApprovalEntity[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const finished = ref(false)
|
||||||
|
|
||||||
|
const handleCellClick = (approvalId: number) => {
|
||||||
|
const approvalStore = useApprovalStore()
|
||||||
|
const currentItem = list.value.find(item => item.approvalId === approvalId)
|
||||||
|
if (currentItem) {
|
||||||
|
approvalStore.setCurrentApproval(currentItem);
|
||||||
|
}
|
||||||
|
router.push(`/approval/handleApply/${approvalId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态标签类型
|
||||||
|
const statusTagType = (status: number) => {
|
||||||
|
switch (status) {
|
||||||
|
case 1: return 'warning'
|
||||||
|
case 2: return 'success'
|
||||||
|
case 3: return 'danger'
|
||||||
|
default: return 'default'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理状态选择
|
||||||
|
const onStatusConfirm = (e: PickerConfirmEventParams) => {
|
||||||
|
const { selectedOptions } = e;
|
||||||
|
searchParams.status = selectedOptions[0]?.value ? Number(selectedOptions[0]?.value) : undefined;
|
||||||
|
showStatusPicker.value = false;
|
||||||
|
// 确保赋值给 statusText 的是字符串类型
|
||||||
|
statusText.value = String(selectedOptions[0]?.text || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理时间选择
|
||||||
|
const onDateConfirm = (values: Date[]) => {
|
||||||
|
const [start, end] = values
|
||||||
|
searchParams.startTime = start.toISOString().split('T')[0]
|
||||||
|
searchParams.endTime = end.toISOString().split('T')[0]
|
||||||
|
dateRangeText.value = `${searchParams.startTime} 至 ${searchParams.endTime}`
|
||||||
|
showDatePicker.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索处理
|
||||||
|
const handleSearch = () => {
|
||||||
|
list.value = []
|
||||||
|
searchParams.pageNum = 1
|
||||||
|
onLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const handleReset = () => {
|
||||||
|
Object.assign(searchParams, {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
approvalId: undefined,
|
||||||
|
orderId: undefined,
|
||||||
|
goodsId: undefined,
|
||||||
|
status: undefined,
|
||||||
|
startTime: undefined,
|
||||||
|
endTime: undefined
|
||||||
|
})
|
||||||
|
statusText.value = ''
|
||||||
|
dateRangeText.value = ''
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const onLoad = async () => {
|
||||||
|
try {
|
||||||
|
searchParams.corpid = wxStore.corpid;
|
||||||
|
const { data } = await getApprovalListApi(searchParams)
|
||||||
|
list.value.push(...data.rows)
|
||||||
|
loading.value = false
|
||||||
|
|
||||||
|
if (list.value.length >= data.total) {
|
||||||
|
finished.value = true
|
||||||
|
} else {
|
||||||
|
searchParams.pageNum++
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取审批列表失败', error)
|
||||||
|
finished.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.approval-list-container {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.van-cell__title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.van-cell__label {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,341 @@
|
||||||
|
<template>
|
||||||
|
<div class="bind-goods-container">
|
||||||
|
<div class="header">
|
||||||
|
<van-nav-bar title="选择商品" left-text="取消" @click-left="onCancel" />
|
||||||
|
</div>
|
||||||
|
<div class="search-container">
|
||||||
|
<div v-if="currentGoodsName" class="current-goods-info">
|
||||||
|
<div class="goods-name">当前绑定: {{ currentGoodsName }}</div>
|
||||||
|
<div class="stock-controls">
|
||||||
|
<van-field
|
||||||
|
v-model="newStock"
|
||||||
|
type="number"
|
||||||
|
label="库存"
|
||||||
|
placeholder="输入库存数量"
|
||||||
|
:min="0"
|
||||||
|
/>
|
||||||
|
<van-button size="small" type="primary" @click="handleUpdateStock">修改库存</van-button>
|
||||||
|
<van-button size="small" type="danger" @click="handleUnbind">解绑</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<van-search v-model="searchQuery" placeholder="搜索商品名称" shape="round" />
|
||||||
|
</div>
|
||||||
|
<div class="goods-list" ref="scrollContainer">
|
||||||
|
<van-cell v-for="goods in filteredGoods" :key="goods.goodsId" class="goods-item">
|
||||||
|
<template #icon>
|
||||||
|
<van-image
|
||||||
|
:src="goods.coverImg || `${publicPath}img/product-image.svg`"
|
||||||
|
width="80"
|
||||||
|
height="80"
|
||||||
|
fit="cover"
|
||||||
|
radius="4"
|
||||||
|
class="goods-image"
|
||||||
|
:style="{ filter: goods.stock === 0 ? 'grayscale(100%)' : 'none' }"
|
||||||
|
>
|
||||||
|
</van-image>
|
||||||
|
</template>
|
||||||
|
<div class="goods-info">
|
||||||
|
<div class="name-price-container">
|
||||||
|
<div class="goods-name van-ellipsis">{{ goods.goodsName }}</div>
|
||||||
|
<div class="goods-price">¥{{ (goods.price || 0).toFixed(2) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="stock-info">
|
||||||
|
<span>总库存: {{ goods.stock || 0 }}</span>
|
||||||
|
<span>已分配: {{ goods.totalStock || 0 }}</span>
|
||||||
|
<span>剩余: {{ (goods.stock || 0) - (goods.totalStock || 0) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="action-row">
|
||||||
|
<van-stepper
|
||||||
|
v-model="allocatedStocks[goods.goodsId]"
|
||||||
|
:min="1"
|
||||||
|
:max="(goods.stock || 0) - (goods.totalStock || 0)"
|
||||||
|
:disabled="goods.stock === 0 || loading"
|
||||||
|
size="small"
|
||||||
|
class="stock-stepper"
|
||||||
|
/>
|
||||||
|
<van-button
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
class="bind-btn"
|
||||||
|
:disabled="(goods.stock || 0) - (goods.totalStock || 0) <= 0 || loading || !allocatedStocks[goods.goodsId]"
|
||||||
|
:loading="loading && currentBindGoodsId === goods.goodsId"
|
||||||
|
@click="handleBindGoods(goods)"
|
||||||
|
>
|
||||||
|
绑定
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-cell>
|
||||||
|
<div v-if="goodsList.length === 0 && !loading" class="empty-state">
|
||||||
|
<van-empty description="暂无商品数据" />
|
||||||
|
</div>
|
||||||
|
<div v-if="loading" class="loading-state">
|
||||||
|
<van-loading type="spinner" color="#e95d5d" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, onMounted, defineProps, defineEmits } from 'vue';
|
||||||
|
import { getShopGoodsApi, getShopGoodsListApi } from '@/common/apis/shop';
|
||||||
|
import { ShopGoodsEntity, SearchGoodsDO } from '@/common/apis/shop/type';
|
||||||
|
import { publicPath } from '@/common/utils/path';
|
||||||
|
import { useWxStore } from '@/pinia/stores/wx';
|
||||||
|
import { showToast } from 'vant';
|
||||||
|
import { configureGoodsCellsStock, changeGoodsCellsStock, clearGoodsCells } from '@/common/apis/cabinet';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
shopId: number;
|
||||||
|
lockerId: number;
|
||||||
|
visible: boolean;
|
||||||
|
currentStock?: number;
|
||||||
|
currentGoodsName?: string;
|
||||||
|
mode?: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(e: 'cancel'): void;
|
||||||
|
(e: 'success', goods: ShopGoodsEntity): void;
|
||||||
|
(e: 'unbind'): void;
|
||||||
|
(e: 'update:currentStock', stock: number): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const wxStore = useWxStore();
|
||||||
|
const searchQuery = ref('');
|
||||||
|
const newStock = ref<number>(props.currentStock || 0);
|
||||||
|
const goodsList = ref<SearchGoodsDO[]>([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
const currentBindGoodsId = ref<number | null>(null);
|
||||||
|
const scrollContainer = ref<HTMLElement>();
|
||||||
|
const allocatedStocks = ref<Record<number, number>>({});
|
||||||
|
|
||||||
|
// 过滤商品列表
|
||||||
|
const filteredGoods = computed(() => {
|
||||||
|
if (!searchQuery.value) return goodsList.value;
|
||||||
|
return goodsList.value.filter(goods =>
|
||||||
|
goods.goodsName.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取商品列表
|
||||||
|
const loadGoodsList = async () => {
|
||||||
|
if (!props.shopId) return;
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const belongType = props.mode === 4 ? 1 : 0;
|
||||||
|
const response = await getShopGoodsListApi(wxStore.corpid, belongType); // 根据mode动态设置belongType参数
|
||||||
|
if (response?.code === 0 && response.data) {
|
||||||
|
goodsList.value = response.data;
|
||||||
|
// 初始化分配库存
|
||||||
|
goodsList.value.forEach(goods => {
|
||||||
|
allocatedStocks.value[goods.goodsId] = 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取商品列表失败:', error);
|
||||||
|
showToast('获取商品列表失败,请重试');
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 绑定商品
|
||||||
|
const handleBindGoods = async (goods: SearchGoodsDO) => {
|
||||||
|
const remaining = (goods.stock || 0) - (goods.totalStock || 0);
|
||||||
|
if (goods.stock === 0 || remaining <= 0) {
|
||||||
|
showToast('商品剩余库存不足,无法绑定');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (allocatedStocks.value[goods.goodsId] > remaining) {
|
||||||
|
showToast(`入库数量不能超过剩余数量${remaining}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentBindGoodsId.value = goods.goodsId;
|
||||||
|
try {
|
||||||
|
await configureGoodsCellsStock(props.lockerId, goods.goodsId, allocatedStocks.value[goods.goodsId]);
|
||||||
|
emits('success', goods);
|
||||||
|
showToast('商品绑定成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('绑定商品失败:', error);
|
||||||
|
showToast('绑定商品失败,请重试');
|
||||||
|
} finally {
|
||||||
|
currentBindGoodsId.value = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdateStock = async () => {
|
||||||
|
if (newStock.value < 0) {
|
||||||
|
showToast('库存不能为负数');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await changeGoodsCellsStock(props.lockerId, newStock.value);
|
||||||
|
showToast('库存修改成功');
|
||||||
|
emits('update:currentStock', newStock.value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('修改库存失败:', error);
|
||||||
|
showToast('修改库存失败,请重试');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUnbind = async () => {
|
||||||
|
try {
|
||||||
|
await clearGoodsCells(props.lockerId);
|
||||||
|
showToast('商品解绑成功');
|
||||||
|
emits('unbind');
|
||||||
|
emits('cancel');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解绑商品失败:', error);
|
||||||
|
showToast('解绑商品失败,请重试');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 取消绑定
|
||||||
|
const onCancel = () => {
|
||||||
|
emits('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听visible变化,当弹窗显示时加载商品列表
|
||||||
|
watch(() => props.visible, (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
newStock.value = props.currentStock || 0;
|
||||||
|
loadGoodsList();
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始加载商品列表
|
||||||
|
if (props.visible) {
|
||||||
|
loadGoodsList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.bind-goods-container {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #f7f8fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-goods-info {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-controls .van-field {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-controls .van-button {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-bottom: 1px solid #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-image {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 90px; // 调整高度以适应新布局
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-price-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-price {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #e95d5d;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-stepper {
|
||||||
|
margin-right: 10px;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bind-btn {
|
||||||
|
background-color: #e95d5d;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info span {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
padding: 40px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-state {
|
||||||
|
padding: 40px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,29 +1,41 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="cabinet-container van-safe-area-bottom">
|
<div v-if="showShopList" class="shop-list">
|
||||||
<van-sidebar v-model="activeCabinet" class="cabinet-sidebar" @change="onCabinetChange">
|
<div class="shop-prompt">
|
||||||
<van-sidebar-item v-for="cabinet in cabinetList" :key="cabinet.cabinetId" :title="cabinet.cabinetName" />
|
<van-cell title="请选择机柜地址:" center />
|
||||||
</van-sidebar>
|
</div>
|
||||||
|
<van-row :gutter="[10, 10]" class="shop-row" justify="start">
|
||||||
|
<van-col v-for="shop in shopList" :key="shop.shopId" span="12" class="shop-col">
|
||||||
|
<div class="shop-item" @click="handleShopSelect(shop.shopId)">
|
||||||
|
<div class="shop-info">
|
||||||
|
<van-icon name="shop-o" size="20" class="shop-icon" />
|
||||||
|
<div class="shop-name van-ellipsis">{{ shop.shopName }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-col>
|
||||||
|
<van-col v-if="shopList.length % 2 === 0" span="12" class="shop-col"></van-col>
|
||||||
|
</van-row>
|
||||||
|
</div>
|
||||||
|
<div v-else class="cabinet-container van-safe-area-bottom">
|
||||||
|
<div class="left-container">
|
||||||
|
<van-button icon="revoke" type="default" class="showShopListBtn" @click="handleBackToShopList">重选地址</van-button>
|
||||||
|
<van-sidebar v-model="activeCabinet" class="cabinet-sidebar" @change="onCabinetChange">
|
||||||
|
<van-sidebar-item v-for="cabinet in cabinetList" :key="cabinet.cabinetId" :title="cabinet.cabinetName" />
|
||||||
|
</van-sidebar>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="product-list">
|
<div class="product-list">
|
||||||
<van-cell v-for="locker in lockerList" :key="locker.lockerId" class="product-item">
|
<van-cell v-for="locker in lockerList" :key="locker.lockerId" class="product-item">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<div class="image-container">
|
<div class="image-container">
|
||||||
<van-image width="80" height="80"
|
<van-image width="80" height="80"
|
||||||
:src="locker.coverImg ? locker.coverImg : `${publicPath}` + 'img/product-image.svg'" fit="cover"
|
:src="locker.coverImg ? locker.coverImg : `${publicPath}` + 'img/product-image.svg'"
|
||||||
radius="4" class="product-image" :style="{ filter: locker.stock === 0 ? 'grayscale(100%)' : 'none' }">
|
fit="cover" radius="4" class="product-image"
|
||||||
|
:style="{ filter: locker.stock === 0 ? 'grayscale(100%)' : 'none' }">
|
||||||
</van-image>
|
</van-image>
|
||||||
<div v-if="locker.stock >= 0" class="stock-overlay">
|
<div v-if="locker.stock >= 0" class="stock-overlay">
|
||||||
库存: {{ locker.stock }}
|
库存: {{ locker.stock }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div v-else class="status-overlay">
|
|
||||||
<svg width="80" height="80" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect x="5" y="5" width="90" height="90" rx="10" fill="#E8F5E9" stroke="#81C784"
|
|
||||||
stroke-width="2" />
|
|
||||||
<text x="50" y="60" font-family="Arial, sans-serif" font-size="24" font-weight="bold"
|
|
||||||
fill="#2E7D32" text-anchor="middle">空闲</text>
|
|
||||||
</svg>
|
|
||||||
</div> -->
|
|
||||||
</template>
|
</template>
|
||||||
<div class="product-info">
|
<div class="product-info">
|
||||||
<div class="goods-info">
|
<div class="goods-info">
|
||||||
|
@ -43,9 +55,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<van-button size="small" type="primary" class="detail-btn" @click="showLockerDetail(locker)">
|
<van-button size="small" type="primary" class="detail-btn" @click="showBindGoods(locker)">
|
||||||
详情
|
绑定商品
|
||||||
</van-button>
|
</van-button>
|
||||||
|
|
||||||
<van-button size="small" plain hairline :loading="openingLockerId === locker.lockerId"
|
<van-button size="small" plain hairline :loading="openingLockerId === locker.lockerId"
|
||||||
@click="handleOpenLocker(locker)">
|
@click="handleOpenLocker(locker)">
|
||||||
立即开启
|
立即开启
|
||||||
|
@ -54,15 +67,37 @@
|
||||||
</div>
|
</div>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<VanPopup v-model:show="showBindGoodsPopup" position="bottom" :style="{ height: '95%' }" round>
|
||||||
|
<BindGoods
|
||||||
|
v-if="currentLocker"
|
||||||
|
:shop-id="shopId"
|
||||||
|
:locker-id="currentLocker.lockerId"
|
||||||
|
:visible="showBindGoodsPopup"
|
||||||
|
:current-stock="currentLocker.stock"
|
||||||
|
:current-goods-name="currentLocker.goodsName"
|
||||||
|
:mode="selectedShop?.mode"
|
||||||
|
@cancel="showBindGoodsPopup = false"
|
||||||
|
@success="handleBindSuccess"
|
||||||
|
@unbind="handleBindSuccess"
|
||||||
|
@update:currentStock="handleStockUpdate"
|
||||||
|
/>
|
||||||
|
</VanPopup>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { throttle } from 'lodash-es';
|
||||||
import { getCabinetDetailApi, openCabinet } from '@/common/apis/cabinet'
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
|
||||||
import type { CabinetDetailDTO } from '@/common/apis/cabinet/type'
|
import { getShopListApi } from '@/common/apis/shop';
|
||||||
import { useWxStore, useWxStoreOutside } from '@/pinia/stores/wx'
|
import { ShopEntity } from '@/common/apis/shop/type';
|
||||||
import { publicPath } from "@/common/utils/path"
|
import { getCabinetDetailApi, openCabinet, changeGoodsCellsStock, clearGoodsCells } from '@/common/apis/cabinet';
|
||||||
|
import type { CabinetDetailDTO } from '@/common/apis/cabinet/type';
|
||||||
|
import { useWxStore, useWxStoreOutside } from '@/pinia/stores/wx';
|
||||||
|
import { publicPath } from "@/common/utils/path";
|
||||||
|
import BindGoods from './components/BindGoods.vue';
|
||||||
|
import VanPopup from 'vant/es/popup';
|
||||||
|
import { showDialog, showToast } from 'vant';
|
||||||
|
|
||||||
const wxStore = useWxStore();
|
const wxStore = useWxStore();
|
||||||
const { userid: qyUserid, name: qyName } = storeToRefs(wxStore);
|
const { userid: qyUserid, name: qyName } = storeToRefs(wxStore);
|
||||||
|
@ -71,7 +106,17 @@ const activeCabinet = ref(0)
|
||||||
const cabinetList = ref<CabinetItem[]>([])
|
const cabinetList = ref<CabinetItem[]>([])
|
||||||
const lockerList = ref<LockerItem[]>([])
|
const lockerList = ref<LockerItem[]>([])
|
||||||
const openingLockerId = ref<number | null>(null)
|
const openingLockerId = ref<number | null>(null)
|
||||||
|
const showBindGoodsPopup = ref(false)
|
||||||
|
const currentLocker = ref<LockerItem | null>(null)
|
||||||
const cabinetData = ref<CabinetDetailDTO[]>([]);
|
const cabinetData = ref<CabinetDetailDTO[]>([]);
|
||||||
|
const scrollContainer = ref<HTMLElement>();
|
||||||
|
// 机柜地址选择相关状态
|
||||||
|
const showShopList = ref(true);
|
||||||
|
const shopList = ref<ShopEntity[]>([]);
|
||||||
|
const shopId = ref<number>(0);
|
||||||
|
const selectedShop = ref<ShopEntity | null>(null);
|
||||||
|
const headerHeight = ref(150);
|
||||||
|
let scrollListener: any[] = [];
|
||||||
|
|
||||||
interface CabinetItem {
|
interface CabinetItem {
|
||||||
cabinetId: number
|
cabinetId: number
|
||||||
|
@ -91,10 +136,12 @@ interface LockerItem {
|
||||||
coverImg?: string
|
coverImg?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取柜机列表
|
// 获取机柜列表
|
||||||
const loadCabinetDetail = async () => {
|
const loadCabinetDetail = async (selectedShopId?: number) => {
|
||||||
|
const targetShopId = selectedShopId || shopId.value;
|
||||||
|
if (!targetShopId) return;
|
||||||
try {
|
try {
|
||||||
const { data } = await getCabinetDetailApi();
|
const { data } = await getCabinetDetailApi(targetShopId);
|
||||||
cabinetData.value = data;
|
cabinetData.value = data;
|
||||||
cabinetList.value = data.map(cabinet => ({
|
cabinetList.value = data.map(cabinet => ({
|
||||||
cabinetId: cabinet.cabinetId,
|
cabinetId: cabinet.cabinetId,
|
||||||
|
@ -134,10 +181,16 @@ const onCabinetChange = (index: number) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showLockerDetail = (locker: LockerItem) => {
|
const showBindGoods = (locker: LockerItem) => {
|
||||||
// 实现详情弹窗逻辑
|
currentLocker.value = locker;
|
||||||
|
showBindGoodsPopup.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleBindSuccess = () => {
|
||||||
|
showBindGoodsPopup.value = false;
|
||||||
|
loadCabinetDetail();
|
||||||
|
};
|
||||||
|
|
||||||
const handleOpenLocker = async (locker: LockerItem) => {
|
const handleOpenLocker = async (locker: LockerItem) => {
|
||||||
openingLockerId.value = locker.lockerId
|
openingLockerId.value = locker.lockerId
|
||||||
try {
|
try {
|
||||||
|
@ -157,7 +210,68 @@ const handleOpenLocker = async (locker: LockerItem) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadCabinetDetail()
|
// 初始化方法
|
||||||
|
const init = async () => {
|
||||||
|
if (showShopList.value) {
|
||||||
|
await getShopList();
|
||||||
|
} else if (shopId.value) {
|
||||||
|
await loadCabinetDetail();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取商店列表
|
||||||
|
const getShopList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getShopListApi(wxStore.corpid, -1);
|
||||||
|
if (res?.code === 0 && res?.data?.length > 0) {
|
||||||
|
shopList.value = res.data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取商店列表失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择商店
|
||||||
|
const handleShopSelect = (selectedShopId: number) => {
|
||||||
|
const shop = shopList.value.find(s => s.shopId === selectedShopId);
|
||||||
|
if (shop) {
|
||||||
|
selectedShop.value = shop;
|
||||||
|
shopId.value = selectedShopId;
|
||||||
|
}
|
||||||
|
showShopList.value = false;
|
||||||
|
loadCabinetDetail(selectedShopId);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 返回商店列表
|
||||||
|
const handleBackToShopList = () => {
|
||||||
|
showShopList.value = true;
|
||||||
|
shopId.value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听滚动事件调整头部高度
|
||||||
|
const throttledScroll = throttle(() => {
|
||||||
|
if (!scrollContainer.value) return;
|
||||||
|
const scrollTop = scrollContainer.value.scrollTop;
|
||||||
|
headerHeight.value = Math.max(150 - scrollTop * 0.5, 60);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleStockUpdate = (newStock: number) => {
|
||||||
|
if (currentLocker.value) {
|
||||||
|
currentLocker.value.stock = newStock;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
init();
|
||||||
|
scrollListener.push(window.addEventListener('scroll', throttledScroll));
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
scrollListener.forEach(listener => window.removeEventListener('scroll', listener));
|
||||||
|
scrollListener = [];
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -184,8 +298,87 @@ loadCabinetDetail()
|
||||||
height: calc(100vh - var(--van-tabbar-height));
|
height: calc(100vh - var(--van-tabbar-height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.left-container {
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 机柜选择相关样式 */
|
||||||
|
.shop {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-header {
|
||||||
|
overflow: hidden;
|
||||||
|
transition: height 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-cover {
|
||||||
|
width: 100%;
|
||||||
|
transform: translateY(calc((150px - var(--header-height)) / 2));
|
||||||
|
scale: calc(1 + (150 - var(--header-height)) / 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-list {
|
||||||
|
.shop-prompt {
|
||||||
|
margin: 8px;
|
||||||
|
height: 44px !important;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-row {
|
||||||
|
margin: 8px 0;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-col {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-item {
|
||||||
|
height: 60px !important;
|
||||||
|
padding: 12px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
|
||||||
|
.shop-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: start;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-name {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 2px 0 4px 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.showShopListBtn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.cabinet-sidebar {
|
.cabinet-sidebar {
|
||||||
width: 120px;
|
width: 90px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
@ -346,4 +539,14 @@ loadCabinetDetail()
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.adjust-btn {
|
||||||
|
background-color: #4e80ee;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unbind-btn {
|
||||||
|
color: #999;
|
||||||
|
border-color: #999;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -84,25 +84,25 @@ wxStore.refreshBalance();
|
||||||
<van-col span="24">
|
<van-col span="24">
|
||||||
<div class="section-title text-sm font-bold pb-2">个人中心</div>
|
<div class="section-title text-sm font-bold pb-2">个人中心</div>
|
||||||
</van-col>
|
</van-col>
|
||||||
<van-col span="6">
|
<van-col span="8">
|
||||||
<div class="custom-btn" @click="router.push('/order-list')">
|
<div class="custom-btn" @click="router.push('/order-list')">
|
||||||
<van-icon name="orders-o" size="20px" />
|
<van-icon name="orders-o" size="20px" />
|
||||||
<span>订单列表</span>
|
<span>订单列表</span>
|
||||||
</div>
|
</div>
|
||||||
</van-col>
|
</van-col>
|
||||||
<van-col span="6">
|
<!-- <van-col span="6">
|
||||||
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/manage/goods')">
|
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/manage/goods')">
|
||||||
<van-icon name="comment-o" size="20px" />
|
<van-icon name="comment-o" size="20px" />
|
||||||
<span>商品管理</span>
|
<span>商品管理</span>
|
||||||
</div>
|
</div>
|
||||||
</van-col>
|
</van-col> -->
|
||||||
<van-col span="6">
|
<van-col span="8">
|
||||||
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/cabinet')">
|
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/cabinet')">
|
||||||
<van-icon name="manager-o" size="20px" />
|
<van-icon name="manager-o" size="20px" />
|
||||||
<span>柜机管理</span>
|
<span>柜机管理</span>
|
||||||
</div>
|
</div>
|
||||||
</van-col>
|
</van-col>
|
||||||
<van-col span="6">
|
<van-col span="8">
|
||||||
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/approval/list')">
|
<div v-if="wxStore.isCabinetAdmin" class="custom-btn" @click="router.push('/approval/list')">
|
||||||
<van-icon name="comment-o" size="20px" />
|
<van-icon name="comment-o" size="20px" />
|
||||||
<span>审批中心</span>
|
<span>审批中心</span>
|
||||||
|
|
|
@ -9,15 +9,21 @@ import { computed, onBeforeUnmount, onMounted, ref } from "vue"
|
||||||
import Cart from "./components/cart.vue"
|
import Cart from "./components/cart.vue"
|
||||||
import Detail from "./components/detail.vue"
|
import Detail from "./components/detail.vue"
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useWxStore } from "@/pinia/stores/wx"
|
||||||
|
import { bindQyUserApi } from "@/common/apis/ab98"
|
||||||
|
import { getShopListApi } from "@/common/apis/shop"
|
||||||
|
import { ShopEntity } from "@/common/apis/shop/type"
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
// 状态管理
|
// 状态管理
|
||||||
const productStore = useProductStore()
|
const productStore = useProductStore();
|
||||||
const cartStore = useCartStore()
|
const cartStore = useCartStore();
|
||||||
const { cartItems, totalPrice, totalQuantity } = storeToRefs(cartStore) // 新增购物车状态
|
const wxStore = useWxStore();
|
||||||
|
const { cartItems, totalPrice, totalQuantity } = storeToRefs(cartStore); // 新增购物车状态
|
||||||
// 从 store 解构分类标签和商品数据
|
// 从 store 解构分类标签和商品数据
|
||||||
const { labels, categories } = storeToRefs(productStore)
|
const { labels, categories } = storeToRefs(productStore);
|
||||||
|
const { openid, corpidLogin, ab98User, qyUserId } = storeToRefs(wxStore);
|
||||||
|
|
||||||
// 当前选中的分类索引
|
// 当前选中的分类索引
|
||||||
const activeCategory = ref(0)
|
const activeCategory = ref(0)
|
||||||
|
@ -29,6 +35,11 @@ const headerHeight = ref(150)
|
||||||
// 滚动事件监听器存储数组
|
// 滚动事件监听器存储数组
|
||||||
let scrollListener: any[] = []
|
let scrollListener: any[] = []
|
||||||
|
|
||||||
|
// 显示选择商店列表
|
||||||
|
const showShopList = ref(true);
|
||||||
|
const shopList = ref<ShopEntity[]>([]);
|
||||||
|
const shopId = ref<number>(0);
|
||||||
|
|
||||||
// 商品详情弹窗控制
|
// 商品详情弹窗控制
|
||||||
const showDetailPopup = ref(false)
|
const showDetailPopup = ref(false)
|
||||||
// 当前查看的商品ID
|
// 当前查看的商品ID
|
||||||
|
@ -40,9 +51,26 @@ const currentProduct = computed(() =>
|
||||||
// 购物车弹窗控制
|
// 购物车弹窗控制
|
||||||
const showCartPopup = ref(false)
|
const showCartPopup = ref(false)
|
||||||
|
|
||||||
const searchQuery = ref('')
|
const searchQuery = ref('');
|
||||||
|
|
||||||
|
const name = ref('');
|
||||||
|
const idNum = ref('');
|
||||||
|
const showAb98BindPopup = ref(false);
|
||||||
|
|
||||||
// 点击分类导航
|
// 点击分类导航
|
||||||
|
function handleShopSelect(selectedShopId: number) {
|
||||||
|
shopId.value = selectedShopId;
|
||||||
|
showShopList.value = false;
|
||||||
|
productStore.getGoods(selectedShopId);
|
||||||
|
cartStore.clearCart();
|
||||||
|
activeCategory.value = 0;
|
||||||
|
}
|
||||||
|
function handleBackToShopList() {
|
||||||
|
showShopList.value = true;
|
||||||
|
shopId.value = 0;
|
||||||
|
cartStore.clearCart();
|
||||||
|
}
|
||||||
|
|
||||||
function handleCategoryClick(index: number) {
|
function handleCategoryClick(index: number) {
|
||||||
activeCategory.value = index
|
activeCategory.value = index
|
||||||
}
|
}
|
||||||
|
@ -109,7 +137,15 @@ const currentProducts = computed(() => {
|
||||||
|
|
||||||
// 组件挂载时添加滚动监听
|
// 组件挂载时添加滚动监听
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
productStore.getGoods();
|
if (showShopList.value) {
|
||||||
|
getShopListApi(wxStore.corpid).then((res) => {
|
||||||
|
if (res?.code === 0 && res?.data?.length > 0) {
|
||||||
|
shopList.value = res.data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
productStore.getGoods(shopId.value);
|
||||||
|
}
|
||||||
scrollListener.push(scrollContainer.value?.addEventListener("scroll", throttledScroll))
|
scrollListener.push(scrollContainer.value?.addEventListener("scroll", throttledScroll))
|
||||||
// scrollListener.push(scrollContainer.value?.addEventListener("scroll", throttledUpdate))
|
// scrollListener.push(scrollContainer.value?.addEventListener("scroll", throttledUpdate))
|
||||||
})
|
})
|
||||||
|
@ -129,30 +165,86 @@ function handleCheckout() {
|
||||||
// 路由更新时刷新数据
|
// 路由更新时刷新数据
|
||||||
watch(() => route.path, (newPath) => {
|
watch(() => route.path, (newPath) => {
|
||||||
if (newPath === '/') {
|
if (newPath === '/') {
|
||||||
productStore.getGoods()
|
if (showShopList.value) {
|
||||||
|
getShopListApi(wxStore.corpid).then((res) => {
|
||||||
|
if (res?.code === 0 && res?.data?.length > 0) {
|
||||||
|
shopList.value = res.data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
productStore.getGoods(shopId.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
}, { immediate: true });
|
||||||
|
|
||||||
|
let showAb98BindPopupOnce = false;
|
||||||
|
watch(openid, async () => {
|
||||||
|
if (openid.value && corpidLogin.value && !ab98User.value
|
||||||
|
&& !showAb98BindPopup.value && !showAb98BindPopupOnce) {
|
||||||
|
// 显示绑定弹窗
|
||||||
|
showAb98BindPopup.value = true;
|
||||||
|
showAb98BindPopupOnce = true;
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
async function handleAb98Bind() {
|
||||||
|
try {
|
||||||
|
const ab98UserData = await bindQyUserApi({
|
||||||
|
qyUserId: qyUserId.value,
|
||||||
|
name: name.value,
|
||||||
|
idNum: idNum.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ab98UserData?.code === 0) {
|
||||||
|
wxStore.setAb98User(ab98UserData.data);
|
||||||
|
showAb98BindPopup.value = false;
|
||||||
|
name.value = '';
|
||||||
|
idNum.value = '';
|
||||||
|
} else {
|
||||||
|
console.error('绑定失败:', ab98UserData?.msg);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('绑定失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="shop">
|
<div v-if="showShopList" class="shop-list">
|
||||||
|
<div class="shop-header" :style="{ height: `${headerHeight}px` }">
|
||||||
|
<van-image :src="`${publicPath}cover.png`" class="shop-cover" fit="cover" />
|
||||||
|
</div>
|
||||||
|
<div class="shop-prompt">
|
||||||
|
<van-cell title="请选择机柜地址:" center />
|
||||||
|
</div>
|
||||||
|
<van-row :gutter="[10, 10]" class="shop-row" justify="start">
|
||||||
|
<van-col v-for="shop in shopList" :key="shop.shopId" span="12" class="shop-col">
|
||||||
|
<div class="shop-item" @click="handleShopSelect(shop.shopId)">
|
||||||
|
<div class="shop-info">
|
||||||
|
<van-icon name="shop-o" size="20" class="shop-icon" />
|
||||||
|
<div class="shop-name van-ellipsis">{{ shop.shopName }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-col>
|
||||||
|
<van-col v-if="shopList.length % 2 === 0" span="12" class="shop-col"></van-col>
|
||||||
|
</van-row>
|
||||||
|
</div>
|
||||||
|
<div v-if="!showShopList" class="shop">
|
||||||
<div class="shop-header" :style="{ height: `${headerHeight}px` }">
|
<div class="shop-header" :style="{ height: `${headerHeight}px` }">
|
||||||
<van-image :src="`${publicPath}cover.png`" class="shop-cover" fit="cover" />
|
<van-image :src="`${publicPath}cover.png`" class="shop-cover" fit="cover" />
|
||||||
</div>
|
</div>
|
||||||
<div class="product-container">
|
<div class="product-container">
|
||||||
<!-- 左侧分类导航 -->
|
<!-- 左侧分类导航 -->
|
||||||
<van-sidebar v-model="activeCategory" class="category-nav" @change="handleCategoryClick">
|
<div>
|
||||||
<van-sidebar-item v-for="label in labels" :key="label.id" :title="label.name" />
|
<van-button icon="revoke" type="default" class="showShopListBtn" @click="showShopList = true">重选地址</van-button>
|
||||||
</van-sidebar>
|
<van-sidebar v-model="activeCategory" class="category-nav" @change="handleCategoryClick">
|
||||||
|
<van-sidebar-item v-for="label in labels" :key="label.id" :title="label.name" />
|
||||||
|
</van-sidebar>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 右侧商品列表 -->
|
<!-- 右侧商品列表 -->
|
||||||
<div ref="scrollContainer" class="product-list">
|
<div ref="scrollContainer" class="product-list">
|
||||||
<van-search
|
<van-search v-model="searchQuery" placeholder="搜索商品名称" shape="round" class="search-box" />
|
||||||
v-model="searchQuery"
|
|
||||||
placeholder="搜索商品名称"
|
|
||||||
shape="round"
|
|
||||||
class="search-box"
|
|
||||||
/>
|
|
||||||
<div class="category-section">
|
<div class="category-section">
|
||||||
<van-cell v-for="product in currentProducts" :key="product.id" class="product-item">
|
<van-cell v-for="product in currentProducts" :key="product.id" class="product-item">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
@ -183,7 +275,8 @@ watch(() => route.path, (newPath) => {
|
||||||
<div class="cart-actions">
|
<div class="cart-actions">
|
||||||
<van-button v-if="getCartItemCount(product.cellId)" size="mini" icon="minus"
|
<van-button v-if="getCartItemCount(product.cellId)" size="mini" icon="minus"
|
||||||
@click.stop="handleRemoveFromCart(product)" />
|
@click.stop="handleRemoveFromCart(product)" />
|
||||||
<span v-if="getCartItemCount(product.cellId)" class="cart-count">{{ getCartItemCount(product.cellId) }}</span>
|
<span v-if="getCartItemCount(product.cellId)" class="cart-count">{{ getCartItemCount(product.cellId)
|
||||||
|
}}</span>
|
||||||
<van-button size="mini" type="primary" class="add-cart-btn" icon="plus"
|
<van-button size="mini" type="primary" class="add-cart-btn" icon="plus"
|
||||||
:disabled="product.stock === 0" @click.stop="handleAddToCart(product)" :style="{
|
:disabled="product.stock === 0" @click.stop="handleAddToCart(product)" :style="{
|
||||||
opacity: product.stock === 0 ? 0.6 : 1,
|
opacity: product.stock === 0 ? 0.6 : 1,
|
||||||
|
@ -217,9 +310,24 @@ watch(() => route.path, (newPath) => {
|
||||||
<VanPopup v-model:show="showCartPopup" position="bottom" :style="{ height: '80%' }" round>
|
<VanPopup v-model:show="showCartPopup" position="bottom" :style="{ height: '80%' }" round>
|
||||||
<Cart class="detail-container" @cart-close="showCartPopup = false" />
|
<Cart class="detail-container" @cart-close="showCartPopup = false" />
|
||||||
</VanPopup>
|
</VanPopup>
|
||||||
|
<VanPopup v-model:show="showAb98BindPopup" position="center" :style="{ padding: '12px' }" round>
|
||||||
|
<div style="text-align: center; font-size: 16px; font-weight: bold; margin-bottom: 16px;">请绑定汇邦云账号</div>
|
||||||
|
<van-form @submit="handleAb98Bind">
|
||||||
|
<van-cell-group inset>
|
||||||
|
<van-field v-model="name" label="姓名" placeholder="请输入真实姓名" :rules="[{ required: true, message: '请填写姓名' }]" />
|
||||||
|
<van-field v-model="idNum" label="身份证号" placeholder="请输入身份证号码"
|
||||||
|
:rules="[{ required: true, message: '请填写身份证号' }]" />
|
||||||
|
</van-cell-group>
|
||||||
|
<div style="margin: 16px;">
|
||||||
|
<van-button block type="primary" native-type="submit">
|
||||||
|
立即绑定
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</van-form>
|
||||||
|
</VanPopup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang="scss">
|
||||||
.product-container {
|
.product-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
@ -242,6 +350,12 @@ watch(() => route.path, (newPath) => {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.showShopListBtn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.product-list {
|
.product-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
@ -442,4 +556,56 @@ watch(() => route.path, (newPath) => {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shop-list {
|
||||||
|
.shop-prompt {
|
||||||
|
margin: 8px;
|
||||||
|
height: 44px !important;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-row {
|
||||||
|
margin: 8px 0;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-col {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-item {
|
||||||
|
height: 60px !important;
|
||||||
|
padding: 12px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
|
||||||
|
.shop-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: start;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-name {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 2px 0 4px 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -19,18 +19,37 @@ const { openid, balance, corpidLogin, userid: qyUserid, name: qyName } = storeTo
|
||||||
const ab98UserStore = useAb98UserStore()
|
const ab98UserStore = useAb98UserStore()
|
||||||
const { tel, userid: ab98Userid, name } = storeToRefs(ab98UserStore)
|
const { tel, userid: ab98Userid, name } = storeToRefs(ab98UserStore)
|
||||||
|
|
||||||
const selectedPayment = ref<'wechat' | 'balance'>('wechat')
|
const selectedPayment = ref<'wechat' | 'balance' | 'approval'>('wechat');
|
||||||
const contact = ref("")
|
const contact = ref("");
|
||||||
const remark = ref("")
|
const applyRemark = ref("");
|
||||||
const submitting = ref(false);
|
const submitting = ref(false);
|
||||||
|
|
||||||
|
const isApproval = computed(() => {
|
||||||
|
return cartItems.value.some(item => item.product.belongType == 1);
|
||||||
|
});
|
||||||
|
|
||||||
watch(corpidLogin, (newValue) => {
|
watch(corpidLogin, (newValue) => {
|
||||||
if (newValue) {
|
if (isApproval.value) {
|
||||||
selectedPayment.value = 'balance';
|
selectedPayment.value = 'approval';
|
||||||
} else {
|
} else {
|
||||||
selectedPayment.value = 'wechat';
|
if (newValue) {
|
||||||
|
selectedPayment.value = 'balance';
|
||||||
|
} else {
|
||||||
|
selectedPayment.value = 'wechat';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
watch(isApproval, (newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
selectedPayment.value = 'approval';
|
||||||
|
} else {
|
||||||
|
if (corpidLogin.value) {
|
||||||
|
selectedPayment.value = 'balance';
|
||||||
|
} else {
|
||||||
|
selectedPayment.value = 'wechat';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function callWxJsApi(paymentInfo: WxJsApiPreCreateResponse) {
|
function callWxJsApi(paymentInfo: WxJsApiPreCreateResponse) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@ -111,11 +130,12 @@ async function handleSubmit() {
|
||||||
paymentType: selectedPayment.value,
|
paymentType: selectedPayment.value,
|
||||||
mobile: tel.value,
|
mobile: tel.value,
|
||||||
name: isInternal === 2 ? qyName.value : name.value,
|
name: isInternal === 2 ? qyName.value : name.value,
|
||||||
qyUserid: isInternal === 2 ? qyUserid.value : ab98Userid.value,
|
applyRemark: applyRemark.value,
|
||||||
|
qyUserid: wxStore.userid,
|
||||||
isInternal: isInternal
|
isInternal: isInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
const { code, data } = await submitOrderApi(requestData)
|
const { code, data } = await submitOrderApi(requestData);
|
||||||
|
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
throw new Error("订单提交失败")
|
throw new Error("订单提交失败")
|
||||||
|
@ -125,7 +145,7 @@ async function handleSubmit() {
|
||||||
if (data.paymentInfo) {
|
if (data.paymentInfo) {
|
||||||
await callWxJsApi(data.paymentInfo);
|
await callWxJsApi(data.paymentInfo);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (selectedPayment.value === 'balance') {
|
||||||
// 余额支付成功处理
|
// 余额支付成功处理
|
||||||
wxStore.setBalance(data.newBalance || 0);
|
wxStore.setBalance(data.newBalance || 0);
|
||||||
try {
|
try {
|
||||||
|
@ -135,6 +155,14 @@ async function handleSubmit() {
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
}
|
}
|
||||||
|
} else if (selectedPayment.value === 'approval') {
|
||||||
|
// 审批支付成功处理
|
||||||
|
try {
|
||||||
|
await showConfirmDialog({
|
||||||
|
title: "提交领用申请成功",
|
||||||
|
message: `请等待管理员审批`
|
||||||
|
})
|
||||||
|
} catch (error) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
router.push({
|
router.push({
|
||||||
|
@ -193,7 +221,11 @@ async function handleSubmit() {
|
||||||
</van-cell>
|
</van-cell>
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
|
|
||||||
<van-cell-group class="contact-form">
|
<!-- <van-cell-group v-if="isApproval">
|
||||||
|
<van-field v-model="applyRemark" label="领用说明" type="textarea" rows="2" autosize />
|
||||||
|
</van-cell-group> -->
|
||||||
|
|
||||||
|
<van-cell-group v-if="!isApproval" class="contact-form">
|
||||||
<van-cell v-if="!corpidLogin" :class="['payment-option', { selected: selectedPayment === 'wechat' }]"
|
<van-cell v-if="!corpidLogin" :class="['payment-option', { selected: selectedPayment === 'wechat' }]"
|
||||||
@click="selectedPayment = 'wechat'">
|
@click="selectedPayment = 'wechat'">
|
||||||
<van-icon name="wechat" class="method-icon" />
|
<van-icon name="wechat" class="method-icon" />
|
||||||
|
@ -202,12 +234,12 @@ async function handleSubmit() {
|
||||||
<van-icon v-if="selectedPayment === 'wechat'" name="success" class="check-icon" />
|
<van-icon v-if="selectedPayment === 'wechat'" name="success" class="check-icon" />
|
||||||
</van-cell>
|
</van-cell>
|
||||||
|
|
||||||
<van-cell v-if="corpidLogin"
|
<van-cell v-if="balance && balance > 0"
|
||||||
:class="['payment-option', { selected: selectedPayment === 'balance', disabled: balance < totalPrice }]"
|
:class="['payment-option', { selected: selectedPayment === 'balance', disabled: balance < totalPrice }]"
|
||||||
@click="selectedPayment = 'balance'">
|
@click="selectedPayment = 'balance'">
|
||||||
<van-icon name="balance-o" class="method-icon" />
|
<van-icon name="balance-o" class="method-icon" />
|
||||||
<span class="method-label">
|
<span class="method-label">
|
||||||
余额支付
|
借呗支付
|
||||||
<span class="balance-amount">(当前:¥{{ balance.toFixed(2) }})</span>
|
<span class="balance-amount">(当前:¥{{ balance.toFixed(2) }})</span>
|
||||||
</span>
|
</span>
|
||||||
<div class="empty"></div>
|
<div class="empty"></div>
|
||||||
|
|
|
@ -11,24 +11,25 @@ export interface Product {
|
||||||
label: number // 商品标签
|
label: number // 商品标签
|
||||||
cellId: number // 商品所在的格子ID
|
cellId: number // 商品所在的格子ID
|
||||||
usageInstruction: string // 商品使用说明
|
usageInstruction: string // 商品使用说明
|
||||||
|
belongType: number // 商品所属类型 0: 智借还 1: 固资通
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useProductStore = defineStore("product", () => {
|
export const useProductStore = defineStore("product", () => {
|
||||||
// 商品数据
|
// 商品数据
|
||||||
const labels = ref<Array<{ id: number, name: string }>>([]);
|
const labels = ref<{ id: number, name: string }[]>([]);
|
||||||
const categories = ref<Product[]>([]);
|
const categories = ref<Product[]>([]);
|
||||||
const shopId = ref<number|null>(null);
|
const shopId = ref<number|null>(null);
|
||||||
|
|
||||||
const setShopId = (id: number|string) => {
|
const getGoods = async (shopId_?: number) => {
|
||||||
shopId.value = Number(id);
|
if (shopId_ && shopId_ > 0) {
|
||||||
getGoods();
|
shopId.value = shopId_;
|
||||||
}
|
} else {
|
||||||
|
shopId.value = null;
|
||||||
const getGoods = async () => {
|
/* const urlParams = new URLSearchParams(window.location.search);
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const shopIdParams = urlParams.get('shopId') || undefined;
|
||||||
const shopIdParams = urlParams.get('shopId') || undefined;
|
if (shopIdParams) {
|
||||||
if (shopIdParams) {
|
shopId.value = Number(shopIdParams);
|
||||||
shopId.value = Number(shopIdParams);
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -50,13 +51,14 @@ export const useProductStore = defineStore("product", () => {
|
||||||
image: g.coverImg,
|
image: g.coverImg,
|
||||||
label: g.categoryId,
|
label: g.categoryId,
|
||||||
cellId: g.cellId,
|
cellId: g.cellId,
|
||||||
usageInstruction: g.usageInstruction || ""
|
usageInstruction: g.usageInstruction || "",
|
||||||
|
belongType: g.belongType
|
||||||
}))
|
}))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取商品数据失败:", error)
|
console.error("获取商品数据失败:", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { labels, categories, shopId, getGoods, setShopId }
|
return { labels, categories, shopId, getGoods }
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { pinia } from "@/pinia"
|
import { pinia } from "@/pinia"
|
||||||
import { getOpenIdApi, getBalanceApi, qyLogin, getBalanceByQyUserid } from "@/common/apis/shop"
|
import { getOpenIdApi, getBalanceApi, qyLogin, getBalanceByQyUserid } from "@/common/apis/shop"
|
||||||
import { GetBalanceResponse } from "@/common/apis/shop/type"
|
import { ab98UserDTO, GetBalanceResponse } from "@/common/apis/shop/type"
|
||||||
|
import { useAb98UserStore } from "./ab98-user"
|
||||||
|
|
||||||
|
|
||||||
export const useWxStore = defineStore("wx", () => {
|
export const useWxStore = defineStore("wx", () => {
|
||||||
|
@ -26,6 +27,10 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
const isCabinetAdmin = ref<boolean>(false);
|
const isCabinetAdmin = ref<boolean>(false);
|
||||||
// 企业微信用户姓名
|
// 企业微信用户姓名
|
||||||
const name = ref<string>("");
|
const name = ref<string>("");
|
||||||
|
// 企业微信用户id
|
||||||
|
const qyUserId = ref<number>(0);
|
||||||
|
// 汇邦云用户信息
|
||||||
|
const ab98User = ref<ab98UserDTO | null>(null);
|
||||||
|
|
||||||
// 设置 openid
|
// 设置 openid
|
||||||
const setOpenid = (id: string) => {
|
const setOpenid = (id: string) => {
|
||||||
|
@ -40,6 +45,20 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
isCabinetAdmin.value = isAdmin;
|
isCabinetAdmin.value = isAdmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setAb98User = (user: ab98UserDTO) => {
|
||||||
|
ab98User.value = user;
|
||||||
|
const ab98UserStore = useAb98UserStore();
|
||||||
|
ab98UserStore.setUserInfo({
|
||||||
|
face_img: ab98User.value.faceImg || "",
|
||||||
|
success: true,
|
||||||
|
sex: ab98User.value.sex || "",
|
||||||
|
name: ab98User.value.name || "",
|
||||||
|
userid: ab98User.value.userid || "",
|
||||||
|
registered: true,
|
||||||
|
tel: ab98User.value.tel || "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const refreshBalance = async () => {
|
const refreshBalance = async () => {
|
||||||
if (corpid.value && userid.value) {
|
if (corpid.value && userid.value) {
|
||||||
const res = await getBalanceByQyUserid(corpid.value, userid.value);
|
const res = await getBalanceByQyUserid(corpid.value, userid.value);
|
||||||
|
@ -76,6 +95,8 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
openid.value = res.data.openid;
|
openid.value = res.data.openid;
|
||||||
isCabinetAdmin.value = res.data.isCabinetAdmin === 1;
|
isCabinetAdmin.value = res.data.isCabinetAdmin === 1;
|
||||||
name.value = res.data.name;
|
name.value = res.data.name;
|
||||||
|
qyUserId.value = res.data.qyUserId;
|
||||||
|
setAb98User(res.data.ab98User);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +107,8 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
if(corpid.value) {
|
if(corpid.value) {
|
||||||
balanceRes = await getBalanceByQyUserid(corpid.value, userid.value);
|
balanceRes = await getBalanceByQyUserid(corpid.value, userid.value);
|
||||||
} else {
|
} else {
|
||||||
balanceRes = await getBalanceApi(openid.value)
|
corpid.value = "wpZ1ZrEgAA2QTxIRcB4cMtY7hQbTcPAw";
|
||||||
|
balanceRes = await getBalanceApi(corpid.value, openid.value)
|
||||||
}
|
}
|
||||||
console.log('获取余额成功:', balanceRes)
|
console.log('获取余额成功:', balanceRes)
|
||||||
if (balanceRes && balanceRes.code == 0) {
|
if (balanceRes && balanceRes.code == 0) {
|
||||||
|
@ -96,9 +118,9 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
if (!userid.value) {
|
if (!userid.value) {
|
||||||
userid.value = balanceRes.data.userid;
|
userid.value = balanceRes.data.userid;
|
||||||
}
|
}
|
||||||
if (!corpid.value) {
|
/* if (!corpid.value) {
|
||||||
corpid.value = balanceRes.data.corpid;
|
corpid.value = balanceRes.data.corpid;
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -108,8 +130,9 @@ export const useWxStore = defineStore("wx", () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { code, state, openid, corpid, userid, balance, useBalance, balanceLimit, isCabinetAdmin, corpidLogin, name,
|
return { code, state, openid, corpid, userid, balance, useBalance,
|
||||||
setOpenid, setBalance, handleWxCallback, setIsCabinetAdmin, refreshBalance }
|
balanceLimit, isCabinetAdmin, corpidLogin, name, ab98User, qyUserId,
|
||||||
|
setOpenid, setBalance, handleWxCallback, setIsCabinetAdmin, refreshBalance, setAb98User }
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,10 +25,10 @@ export function registerNavigationGuard(router: Router) {
|
||||||
if (corpid) {
|
if (corpid) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/* const isAdmin = urlParams.get('isAdmin') || undefined;
|
const isAdmin = urlParams.get('isAdmin') || undefined;
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
return true;
|
return true;
|
||||||
} */
|
}
|
||||||
|
|
||||||
// useAb98UserStore位置不能放在外面,否则会导致路由守卫无法正常工作
|
// useAb98UserStore位置不能放在外面,否则会导致路由守卫无法正常工作
|
||||||
const ab98UserStore = useAb98UserStore();
|
const ab98UserStore = useAb98UserStore();
|
||||||
|
|
|
@ -39,6 +39,11 @@ export const routes: RouteRecordRaw[] = [
|
||||||
component: () => import('@/pages/approval/handle.vue'),
|
component: () => import('@/pages/approval/handle.vue'),
|
||||||
meta: { requiresAuth: true }
|
meta: { requiresAuth: true }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/approval/handleApply/:approvalId',
|
||||||
|
component: () => import('@/pages/approval/handleApply.vue'),
|
||||||
|
meta: { requiresAuth: true }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/order-success',
|
path: '/order-success',
|
||||||
name: 'OrderSuccess',
|
name: 'OrderSuccess',
|
||||||
|
|
|
@ -17,6 +17,7 @@ declare module 'vue' {
|
||||||
VanCol: typeof import('vant/es')['Col']
|
VanCol: typeof import('vant/es')['Col']
|
||||||
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
|
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
|
||||||
VanDivider: typeof import('vant/es')['Divider']
|
VanDivider: typeof import('vant/es')['Divider']
|
||||||
|
VanEmpty: typeof import('vant/es')['Empty']
|
||||||
VanField: typeof import('vant/es')['Field']
|
VanField: typeof import('vant/es')['Field']
|
||||||
VanForm: typeof import('vant/es')['Form']
|
VanForm: typeof import('vant/es')['Form']
|
||||||
VanGrid: typeof import('vant/es')['Grid']
|
VanGrid: typeof import('vant/es')['Grid']
|
||||||
|
@ -32,6 +33,7 @@ declare module 'vue' {
|
||||||
VanSearch: typeof import('vant/es')['Search']
|
VanSearch: typeof import('vant/es')['Search']
|
||||||
VanSidebar: typeof import('vant/es')['Sidebar']
|
VanSidebar: typeof import('vant/es')['Sidebar']
|
||||||
VanSidebarItem: typeof import('vant/es')['SidebarItem']
|
VanSidebarItem: typeof import('vant/es')['SidebarItem']
|
||||||
|
VanStepper: typeof import('vant/es')['Stepper']
|
||||||
VanTabbar: typeof import('vant/es')['Tabbar']
|
VanTabbar: typeof import('vant/es')['Tabbar']
|
||||||
VanTabbarItem: typeof import('vant/es')['TabbarItem']
|
VanTabbarItem: typeof import('vant/es')['TabbarItem']
|
||||||
VanTag: typeof import('vant/es')['Tag']
|
VanTag: typeof import('vant/es')['Tag']
|
||||||
|
|
Loading…
Reference in New Issue