初版提交
This commit is contained in:
parent
6ebdfd33bc
commit
12ded6e413
48
.gitignore
vendored
Normal file
48
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# ---> Vue
|
||||
# gitignore template for Vue.js projects
|
||||
#
|
||||
# Recommended template: Node.gitignore
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
docs/_book
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
test/
|
||||
|
||||
# ---> Android
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Log/OS Files
|
||||
*.log
|
||||
|
||||
# Android Studio generated files and folders
|
||||
captures/
|
||||
.externalNativeBuild/
|
||||
.cxx/
|
||||
*.apk
|
||||
output.json
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/
|
||||
misc.xml
|
||||
deploymentTargetDropDown.xml
|
||||
render.experimental.xml
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
*.keystore
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
google-services.json
|
||||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
|
||||
unpackage/
|
||||
|
||||
25
AndroidManifest/network_security_config.xml
Normal file
25
AndroidManifest/network_security_config.xml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<!-- 针对特定域名的配置 -->
|
||||
<domain-config cleartextTrafficPermitted="false">
|
||||
<!-- 配置您的服务器域名 -->
|
||||
<domain includeSubdomains="true">wxperkinsasrs.ap.cat.com</domain>
|
||||
|
||||
<!-- 信任策略 -->
|
||||
<trust-anchors>
|
||||
<!-- 信任系统预装的 CA 证书 -->
|
||||
<certificates src="system" />
|
||||
|
||||
<!-- 信任用户手动安装的 CA 证书(最重要!) -->
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
|
||||
<!-- 其他所有域名使用默认安全策略 -->
|
||||
<domain-config cleartextTrafficPermitted="false">
|
||||
<domain includeSubdomains="true">.</domain>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
36
App.vue
Normal file
36
App.vue
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<script>
|
||||
export default {
|
||||
setup() {
|
||||
// 修复 uni-app 环境中 console.clear 不可用的问题
|
||||
try {
|
||||
if (typeof console !== 'undefined' && !console.clear) {
|
||||
Object.defineProperty(console, 'clear', {
|
||||
value: function() { /* no-op */ },
|
||||
writable: false,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// 某些平台 console 对象不可修改,静默失败
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="app">
|
||||
<!-- 应用主内容 -->
|
||||
<router-view />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import './common/uni.css';
|
||||
@import '/static/mui-icons.css';
|
||||
page { background-color: #F5F5F5; min-height: 100%; }
|
||||
|
||||
.app {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
14
README.md
14
README.md
|
|
@ -1,3 +1,13 @@
|
|||
# pda-uni-app-perkins
|
||||
## pda-uni-app-perkins
|
||||
|
||||
铂金斯pda
|
||||
> uni-app版手持pda
|
||||
|
||||
### HBuilderX配置
|
||||
|
||||
* MuMu模拟器端口号:16384
|
||||
|
||||
* adb路径:选择本地adb.exe文件
|
||||
|
||||
### 运行方式
|
||||
|
||||
> 运行 -> 运行到手机或模拟器 -> 运行到Android App基座
|
||||
230
common/env.js
Normal file
230
common/env.js
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
// 环境配置
|
||||
export const ENV = {
|
||||
// API 地址配置
|
||||
// 说明:
|
||||
// - HTTP 模式(端口 80):用于 PDA 应用,避免证书验证问题(企业内网使用)
|
||||
// - HTTPS 模式(端口 443):用于 Web 端或需要安全连接的客户端
|
||||
//
|
||||
// 现已配置 Android 网络安全策略信任自签名证书,可改回 HTTPS
|
||||
API_URL: 'https://wxperkinsasrs.ap.cat.com', // 改回 HTTPS 以使用安全连接
|
||||
|
||||
APP_NAME: 'PdaUniAppPerkins',
|
||||
VERSION: '1.0.0',
|
||||
|
||||
// 测试模式开关(true:使用 Mock 数据,false:使用真实 API)
|
||||
TEST_MODE: true,
|
||||
|
||||
// URL 调试模式(显示请求信息)
|
||||
DEBUG_URL: true,
|
||||
|
||||
// SSL 证书验证开关
|
||||
// 说明:
|
||||
// - Android 平台:由 src/main/res/xml/network_security_config.xml 控制
|
||||
// - iOS 平台:此参数可能无效,需通过 Info.plist 配置
|
||||
// - H5 平台:此参数无效,由浏览器控制
|
||||
// - 开发环境:已配置信任自签名证书(wxperkinsasrs.ap.cat.com)
|
||||
SSL_VERIFY: false,
|
||||
|
||||
// 协议选择(调试用)
|
||||
// 'http' - 使用 HTTP 协议(端口 80)
|
||||
// 'https' - 使用 HTTPS 协议(端口 443,推荐)
|
||||
// 注意:修改此值需要同时修改 API_URL
|
||||
PROTOCOL: 'https'
|
||||
};
|
||||
|
||||
// Mock 数据定义
|
||||
// 格式:{ [url]: { code: 200, data: {...}, message: '...' } }
|
||||
export const MOCK_DATA = {
|
||||
// 1. 发动机入库 - POST /wmsServer/wms/api/orderIn/loginInBad
|
||||
'/wmsServer/wms/api/orderIn/loginInBad': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 0,
|
||||
message: '发动机入库成功',
|
||||
orderId: 'ENG-IN-20251030001',
|
||||
standId: 'EF01',
|
||||
vehicleNo: 'V12345',
|
||||
goodsId: 'SN-ENG-ABC123456',
|
||||
model: 'C13-ACERT',
|
||||
goodsNum: 1,
|
||||
createTime: '2025-10-30T10:15:30.000Z',
|
||||
operatorName: 'PDA-USER-001'
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 2. 空载具入库 - POST /wmsServer/wms/api/orderIn/loginToByVehicle
|
||||
'/wmsServer/wms/api/orderIn/loginToByVehicle': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 0,
|
||||
message: '空载具入库成功',
|
||||
orderId: 'EMPTY-IN-20251030001',
|
||||
standId: 'EF05',
|
||||
vehicleNo: 'D99999999',
|
||||
createTime: '2025-10-30T10:20:15.000Z',
|
||||
operatorName: 'PDA-USER-001'
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 3. 绑定物料 - POST /wmsServer/wms/api/orderIn/bindingVehicl
|
||||
'/wmsServer/wms/api/orderIn/bindingVehicl': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 0,
|
||||
message: '物料绑定成功',
|
||||
bindId: 'BIND-20251030001',
|
||||
vehicleNo: 'V23456',
|
||||
goodsId: 'RAW-MAT-001',
|
||||
goodsNum: 10,
|
||||
userName: 'PDA',
|
||||
bindTime: '2025-10-30T10:25:45.000Z'
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 3-失败场景:物料已绑定(可选,用于测试错误处理)
|
||||
// 如果需要测试失败场景,可以临时替换上面的成功场景
|
||||
'/wmsServer/wms/api/orderIn/bindingVehicl_ERROR': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 1001,
|
||||
message: '该物料已经绑定到母托号 V23456,请勿重复绑定',
|
||||
vehicleNo: 'V23456',
|
||||
goodsId: 'RAW-MAT-001'
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 4. 查询物料列表 - GET /wmsServer/wms/api/orderIn/getOrderInWithVehicleNo
|
||||
'/wmsServer/wms/api/orderIn/getOrderInWithVehicleNo': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 0,
|
||||
message: '查询成功',
|
||||
vehicleNo: 'V23456',
|
||||
returnData: [{
|
||||
rowId: '10001',
|
||||
goodsId: 'RAW-MAT-001',
|
||||
goodsNum: 10,
|
||||
batchNo: 'BATCH-2025-10-001',
|
||||
productionDate: '2025-10-15T00:00:00.000Z',
|
||||
model: 'C13-RAW',
|
||||
itemId: 'ITM-001',
|
||||
segment1: 'SEG-001',
|
||||
weight: 25.5,
|
||||
productData: 'PD-2025-10-001',
|
||||
area: 'RF01'
|
||||
},
|
||||
{
|
||||
rowId: '10002',
|
||||
goodsId: 'RAW-MAT-002',
|
||||
goodsNum: 5,
|
||||
batchNo: 'BATCH-2025-10-002',
|
||||
productionDate: '2025-10-20T00:00:00.000Z',
|
||||
model: 'C15-RAW',
|
||||
itemId: 'ITM-002',
|
||||
segment1: 'SEG-002',
|
||||
weight: 15.3,
|
||||
productData: 'PD-2025-10-002',
|
||||
area: 'RF02'
|
||||
},
|
||||
{
|
||||
rowId: '10003',
|
||||
goodsId: 'RAW-MAT-003',
|
||||
goodsNum: 8,
|
||||
batchNo: 'BATCH-2025-10-003',
|
||||
productionDate: '2025-10-25T00:00:00.000Z',
|
||||
model: 'C18-RAW',
|
||||
itemId: 'ITM-003',
|
||||
segment1: 'SEG-003',
|
||||
weight: 32.1,
|
||||
productData: 'PD-2025-10-003',
|
||||
area: 'RF03'
|
||||
}
|
||||
],
|
||||
totalCount: 3
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 4-空列表场景(可选,用于测试空数据处理)
|
||||
'/wmsServer/wms/api/orderIn/getOrderInWithVehicleNo_EMPTY': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 0,
|
||||
message: '该母托号暂无绑定物料',
|
||||
vehicleNo: 'V99999',
|
||||
returnData: [],
|
||||
totalCount: 0
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 5. 原材料入库 - POST /wmsServer/wms/api/orderIn/addInByTask
|
||||
'/wmsServer/wms/api/orderIn/addInByTask': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 0,
|
||||
message: '原材料入库成功',
|
||||
orderId: 'RAW-IN-20251030001',
|
||||
vehicleNo: 'V23456',
|
||||
standId: 'RF01',
|
||||
totalItems: 3,
|
||||
totalWeight: 72.9,
|
||||
createTime: '2025-10-30T10:35:20.000Z',
|
||||
operatorName: 'PDA-USER-001',
|
||||
taskId: 'TASK-20251030001'
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 5-失败场景:未绑定物料(可选)
|
||||
'/wmsServer/wms/api/orderIn/addInByTask_ERROR': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 1002,
|
||||
message: '母托号 V23456 未绑定任何物料,请先绑定物料后再入库',
|
||||
vehicleNo: 'V23456'
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 6. 删除入库记录 - DELETE /wmsServer/wms/api/orderIn/deleteOrderIn/{rowId}
|
||||
// 注意:由于 http.js 的 httpDelete 函数会移除路径参数,key 使用基础路径
|
||||
'/wmsServer/wms/api/orderIn/deleteOrderIn': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 0,
|
||||
message: '物料删除成功',
|
||||
rowId: '10001',
|
||||
deleteTime: '2025-10-30T10:40:15.000Z'
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 6-失败场景:记录不存在(可选)
|
||||
'/wmsServer/wms/api/orderIn/deleteOrderIn_ERROR': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 1003,
|
||||
message: '物料记录不存在或已被删除',
|
||||
rowId: '99999'
|
||||
},
|
||||
message: 'success'
|
||||
},
|
||||
|
||||
// 7. 测试连通性 - GET /wmsServer/wms/api/orderIn/test
|
||||
'/wmsServer/wms/api/orderIn/test': {
|
||||
code: 200,
|
||||
data: {
|
||||
code: 0,
|
||||
message: 'API 连接正常',
|
||||
serverTime: '2025-10-30T10:45:00.000Z',
|
||||
version: '1.0.0',
|
||||
status: 'healthy'
|
||||
},
|
||||
message: 'success'
|
||||
}
|
||||
};
|
||||
239
common/http.js
Normal file
239
common/http.js
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
import {
|
||||
ENV,
|
||||
MOCK_DATA
|
||||
} from './env.js';
|
||||
import {
|
||||
DialogUtils
|
||||
} from '../utils/dialog.js';
|
||||
|
||||
/**
|
||||
* HTTP 拦截器 - 统一处理测试模式和生产模式的请求
|
||||
*
|
||||
* 核心功能:
|
||||
* 1. 测试模式:返回 MOCK_DATA 中定义的模拟数据
|
||||
* 2. 生产模式:发起真实的 HTTP 请求到 API 服务器
|
||||
* 3. 调试模式:显示请求信息和详细日志
|
||||
* 4. 统一的错误处理和响应格式
|
||||
*/
|
||||
function createHttpInterceptor() {
|
||||
return {
|
||||
/**
|
||||
* 请求前拦截器 - 统一处理 URL 拼接和调试信息
|
||||
* @param {string} method - HTTP 方法(GET/POST/DELETE)
|
||||
* @param {string} url - API 路径(不包含域名)
|
||||
* @param {object} params - 请求参数
|
||||
* @param {object} extra - 额外配置(timeout、mockDelay 等)
|
||||
* @returns {object} 处理后的请求信息
|
||||
*/
|
||||
beforeRequest(method, url, params, extra) {
|
||||
const fullUrl = ENV.API_URL + url;
|
||||
|
||||
// URL 调试功能 - 在测试和生产模式下都可用
|
||||
if (ENV.DEBUG_URL) {
|
||||
// 显示详细的 URL 调试信息
|
||||
console.log('HTTP Request Debug:', {
|
||||
method,
|
||||
fullUrl,
|
||||
path: url,
|
||||
params,
|
||||
isTestMode: ENV.TEST_MODE
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
fullUrl,
|
||||
params,
|
||||
extra
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 响应处理器 - 根据模式返回 Mock 数据或真实请求
|
||||
* @param {string} method - HTTP 方法
|
||||
* @param {string} url - API 路径
|
||||
* @param {object} params - 请求参数
|
||||
* @param {object} extra - 额外配置
|
||||
* @returns {Promise} 统一格式的响应 Promise
|
||||
*/
|
||||
processResponse(method, url, params, extra) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 统一调用前置拦截器(测试模式和生产模式都需要)
|
||||
const {
|
||||
fullUrl
|
||||
} = this.beforeRequest(method, url, params, extra);
|
||||
|
||||
// 测试模式 - 返回模拟数据
|
||||
if (ENV.TEST_MODE && MOCK_DATA[url]) {
|
||||
const mockResponse = JSON.parse(JSON.stringify(MOCK_DATA[url])); // 深拷贝避免数据污染
|
||||
|
||||
// 只在开发环境输出 Mock 日志
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.log('测试模式 - 返回模拟数据:', {
|
||||
method,
|
||||
url,
|
||||
response: mockResponse
|
||||
});
|
||||
}
|
||||
|
||||
// 模拟网络延迟(让 UI 更贴近真实场景)
|
||||
setTimeout(() => {
|
||||
resolve(mockResponse);
|
||||
}, extra.mockDelay || 500);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 生产模式 - 执行真实请求
|
||||
// 从本地存储获取认证 token
|
||||
const token = uni.getStorageSync('user_token') || '';
|
||||
|
||||
// 对于 GET 请求,将参数拼接到 URL 查询字符串中
|
||||
let requestUrl = fullUrl;
|
||||
let requestData;
|
||||
|
||||
if (method === 'GET') {
|
||||
// GET 请求:参数拼接到 URL,body 为空
|
||||
if (params && Object.keys(params).length > 0) {
|
||||
const queryString = Object.entries(params)
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
||||
.join('&');
|
||||
requestUrl = `${fullUrl}?${queryString}`;
|
||||
}
|
||||
requestData = undefined;
|
||||
} else if (method === 'DELETE') {
|
||||
// DELETE 请求:通常不需要 body(资源 ID 在 URL 中)
|
||||
requestData = undefined;
|
||||
} else {
|
||||
// POST/PUT 请求:参数在 body 中
|
||||
requestData = params;
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-App-Version': ENV.VERSION || '1.0.0'
|
||||
};
|
||||
|
||||
// 添加认证头(如果存在 token)
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
uni.request({
|
||||
url: requestUrl,
|
||||
method: method.toUpperCase(),
|
||||
data: requestData,
|
||||
header: headers,
|
||||
timeout: extra.timeout || 5000,
|
||||
sslVerify: ENV.SSL_VERIFY, // SSL 证书验证
|
||||
// Android: 由 res/xml/network_security_config.xml 控制
|
||||
// iOS: 由 Info.plist 控制
|
||||
success: (res) => {
|
||||
const response = {
|
||||
code: res.statusCode,
|
||||
data: res.data,
|
||||
message: res.errMsg
|
||||
};
|
||||
|
||||
// 只在开发环境和调试模式下输出响应日志
|
||||
if (ENV.DEBUG_URL && process.env.NODE_ENV !== 'production') {
|
||||
console.log('生产模式 - 真实请求响应:', {
|
||||
method,
|
||||
url,
|
||||
fullUrl,
|
||||
response
|
||||
});
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
},
|
||||
fail: (err) => {
|
||||
const errorResponse = {
|
||||
code: 500,
|
||||
data: null,
|
||||
message: err.errMsg || 'network error'
|
||||
};
|
||||
|
||||
// 错误日志在所有环境下都输出(帮助排查问题)
|
||||
console.error('请求失败:', {
|
||||
method,
|
||||
url,
|
||||
fullUrl,
|
||||
error: errorResponse
|
||||
});
|
||||
|
||||
reject(errorResponse);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const httpInterceptor = createHttpInterceptor();
|
||||
|
||||
/**
|
||||
* GET 请求 - 用于查询数据
|
||||
* @param {string} url - API 路径(例如:'/wmsServer/wms/api/orderIn/test')
|
||||
* @param {object} params - 查询参数(会自动拼接到 URL)
|
||||
* @param {object} extra - 额外配置 { timeout, mockDelay }
|
||||
* @returns {Promise<{code: number, data: any, message: string}>}
|
||||
*
|
||||
* @example
|
||||
* httpGet('/wmsServer/wms/api/orderIn/getOrderInWithVehicleNo', { vehicleNo: 'V12345' })
|
||||
*/
|
||||
export async function httpGet(url, params = {}, extra = {}) {
|
||||
return httpInterceptor.processResponse('GET', url, params, extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求 - 用于创建或提交数据
|
||||
* @param {string} url - API 路径
|
||||
* @param {object} data - 请求体数据
|
||||
* @param {object} extra - 额外配置 { timeout, mockDelay }
|
||||
* @returns {Promise<{code: number, data: any, message: string}>}
|
||||
*
|
||||
* @example
|
||||
* httpPost('/wmsServer/wms/api/orderIn/loginInBad', { standId: 'EF01', vehicleNo: 'V12345' })
|
||||
*/
|
||||
export async function httpPost(url, data = {}, extra = {}) {
|
||||
return httpInterceptor.processResponse('POST', url, data, extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE 请求 - 用于删除资源
|
||||
* 注意:DELETE 请求应该保持完整的 URL(包括资源 ID)
|
||||
* Mock 数据匹配时会自动处理路径参数(env.js 中使用基础路径作为 key)
|
||||
*
|
||||
* @param {string} url - 完整的 API 路径(例如:'/wmsServer/wms/api/orderIn/deleteOrderIn/12345')
|
||||
* @param {object} extra - 额外配置 { timeout, mockDelay }
|
||||
* @returns {Promise<{code: number, data: any, message: string}>}
|
||||
*
|
||||
* @example
|
||||
* httpDelete('/wmsServer/wms/api/orderIn/deleteOrderIn/12345')
|
||||
*
|
||||
* 对于测试模式:
|
||||
* - 会先尝试匹配完整 URL
|
||||
* - 如果找不到,会尝试匹配基础路径(去除末尾数字 ID)
|
||||
*/
|
||||
export async function httpDelete(url, extra = {}) {
|
||||
// 测试模式下,支持基础路径匹配(兼容 env.js 的 MOCK_DATA 定义)
|
||||
if (ENV.TEST_MODE) {
|
||||
// 先尝试完整 URL 匹配
|
||||
if (MOCK_DATA[url]) {
|
||||
return httpInterceptor.processResponse('DELETE', url, {}, extra);
|
||||
}
|
||||
|
||||
// 如果找不到,尝试基础路径匹配(移除末尾的数字 ID)
|
||||
const cleanUrl = url.split('?')[0]; // 移除查询参数
|
||||
const baseUrl = cleanUrl.replace(/\/\d+$/, ''); // 移除末尾数字 ID
|
||||
|
||||
if (MOCK_DATA[baseUrl]) {
|
||||
// 使用基础路径进行 Mock 数据匹配
|
||||
return httpInterceptor.processResponse('DELETE', baseUrl, {}, extra);
|
||||
}
|
||||
}
|
||||
|
||||
// 生产模式:保持完整的 URL(包括资源 ID)
|
||||
return httpInterceptor.processResponse('DELETE', url, {}, extra);
|
||||
}
|
||||
69
common/theme.js
Normal file
69
common/theme.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
export const theme = {
|
||||
colors: {
|
||||
aqua: '#05DCEF',
|
||||
background: '#ffffff',
|
||||
backgroundGray: '#F5F5F5',
|
||||
text: '#333333',
|
||||
textLight: '#666666',
|
||||
border: '#dddddd',
|
||||
error: '#ff3b30',
|
||||
cyan: '#00FFFF',
|
||||
lightGreen: '#7FFFAA',
|
||||
gradients: {
|
||||
primary: ['#05DCEF', '#7DE2F5', '#B8F2FF'],
|
||||
contrast: ['#00F5A0', '#00D9F5'],
|
||||
header: ['#05DCEF', '#7DE2F5'],
|
||||
button: ['#05DCEF', '#7DE2F5']
|
||||
},
|
||||
chart: {
|
||||
cyan: '#00FFFF',
|
||||
lightGreen: '#7FFFAA'
|
||||
}
|
||||
},
|
||||
fontSize: {
|
||||
small: 12,
|
||||
regular: 14,
|
||||
medium: 16,
|
||||
large: 18,
|
||||
xlarge: 20,
|
||||
xxlarge: 24,
|
||||
title: 32
|
||||
},
|
||||
spacing: {
|
||||
xs: 4,
|
||||
small: 8,
|
||||
medium: 16,
|
||||
large: 24,
|
||||
xl: 32
|
||||
},
|
||||
borderRadius: {
|
||||
small: 4,
|
||||
medium: 8,
|
||||
large: 12,
|
||||
xl: 16,
|
||||
circle: 999
|
||||
},
|
||||
shadow: {
|
||||
small: {
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
elevation: 2
|
||||
},
|
||||
medium: {
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 4.65,
|
||||
elevation: 4
|
||||
},
|
||||
large: {
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 6 },
|
||||
shadowOpacity: 0.37,
|
||||
shadowRadius: 7.49,
|
||||
elevation: 6
|
||||
}
|
||||
}
|
||||
};
|
||||
1458
common/uni.css
Normal file
1458
common/uni.css
Normal file
File diff suppressed because it is too large
Load Diff
87
common/wmsApi.js
Normal file
87
common/wmsApi.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import { httpGet, httpPost, httpDelete } from './http.js';
|
||||
import { ENV } from './env.js';
|
||||
|
||||
const BASE_PATH = '/wmsServer/wms/api/orderIn';
|
||||
|
||||
function buildUrl(endpoint) {
|
||||
return `${BASE_PATH}${endpoint}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的响应数据解包器
|
||||
* @param {object} response - HTTP 响应对象 { code, data, message }
|
||||
* @param {string} fallbackMessage - 失败时的默认错误消息
|
||||
* @returns {object} 解包后的业务数据(附带 httpStatus)
|
||||
* @throws {Error} 当 HTTP 状态码不是 200 或响应格式不正确时抛出错误
|
||||
*/
|
||||
function unwrapResponse(response, fallbackMessage) {
|
||||
const { code: statusCode, data, message } = response || {};
|
||||
if (statusCode !== 200) {
|
||||
throw new Error(message || fallbackMessage || '请求失败');
|
||||
}
|
||||
if (!data || typeof data !== 'object') {
|
||||
throw new Error(fallbackMessage || '响应格式不正确');
|
||||
}
|
||||
return Object.assign({ httpStatus: statusCode }, data);
|
||||
}
|
||||
|
||||
export const WmsApiClient = {
|
||||
async engineStockIn({ standId, vehicleNo, goodsId, model, goodsNum = 0, timeout = 10000 }) {
|
||||
const payload = {
|
||||
efSelect: standId,
|
||||
vehicleNo,
|
||||
goodsId,
|
||||
goodsNum,
|
||||
model,
|
||||
};
|
||||
const res = await httpPost(buildUrl('/loginInBad'), payload, { timeout });
|
||||
return unwrapResponse(res, '发动机入库失败');
|
||||
},
|
||||
|
||||
async emptyVehicleIn({ standId, vehicleNo = 'D99999999', timeout = 8000 }) {
|
||||
const payload = {
|
||||
efSelect: standId,
|
||||
vehicleNo,
|
||||
goodsId: '',
|
||||
goodsNum: 0,
|
||||
model: '',
|
||||
};
|
||||
const res = await httpPost(buildUrl('/loginToByVehicle'), payload, { timeout });
|
||||
return unwrapResponse(res, '空载具入库失败');
|
||||
},
|
||||
|
||||
async bindVehicle({ vehicleNo, goodsId, goodsNum = 0, userName = 'PDA', timeout = 8000 }) {
|
||||
const payload = {
|
||||
goodsId,
|
||||
goodsNum,
|
||||
vehicleNo,
|
||||
userName,
|
||||
};
|
||||
const res = await httpPost(buildUrl('/bindingVehicl'), payload, { timeout });
|
||||
return unwrapResponse(res, '绑定物料失败');
|
||||
},
|
||||
|
||||
async getOrderInWithVehicleNo({ vehicleNo, timeout = 8000 }) {
|
||||
const res = await httpGet(buildUrl('/getOrderInWithVehicleNo'), { vehicleNo }, { timeout });
|
||||
return unwrapResponse(res, '获取物料列表失败');
|
||||
},
|
||||
|
||||
async rawStockIn({ vehicleNo, standId, timeout = 10000 }) {
|
||||
const payload = {
|
||||
vehicleNo,
|
||||
selectByIn: standId,
|
||||
};
|
||||
const res = await httpPost(buildUrl('/addInByTask'), payload, { timeout });
|
||||
return unwrapResponse(res, '原材料入库失败');
|
||||
},
|
||||
|
||||
async deleteOrderIn({ rowId, timeout = 8000 }) {
|
||||
const res = await httpDelete(`${buildUrl('/deleteOrderIn')}/${rowId}`, { timeout });
|
||||
return unwrapResponse(res, '删除物料失败');
|
||||
},
|
||||
|
||||
async testLink({ vehicleNo = 'LSP00168', timeout = 8000 } = {}) {
|
||||
const res = await httpGet(buildUrl('/getOrderInWithVehicleNo'), { vehicleNo }, { timeout });
|
||||
return unwrapResponse(res, '接口不通');
|
||||
},
|
||||
};
|
||||
42
index.html
Normal file
42
index.html
Normal file
File diff suppressed because one or more lines are too long
11
jest.config.js
Normal file
11
jest.config.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
module.exports = {
|
||||
testTimeout: 20000,
|
||||
reporters: [
|
||||
'default'
|
||||
],
|
||||
watchPathIgnorePatterns: ['/node_modules/', '/dist/', '/.git/'],
|
||||
moduleFileExtensions: ['js', 'json'],
|
||||
rootDir: __dirname,
|
||||
testMatch: ["<rootDir>/pages/**/*test.[jt]s?(x)"],
|
||||
testPathIgnorePatterns: ['/node_modules/']
|
||||
}
|
||||
7
main.js
Normal file
7
main.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import App from './App'
|
||||
import { createSSRApp } from 'vue'
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
return { app }
|
||||
}
|
||||
192
manifest.json
Normal file
192
manifest.json
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
{
|
||||
"name" : "pda-perkins",
|
||||
"appid" : "__UNI__D3D7919",
|
||||
"description" : "应用描述",
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
"app-plus" : {
|
||||
"usingComponents" : true,
|
||||
"nvueCompiler" : "uni-app",
|
||||
"nvueStyleCompiler" : "uni-app",
|
||||
"compilerVersion" : 3,
|
||||
"nvueLaunchMode" : "fast",
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
"modules" : {
|
||||
"OAuth" : {},
|
||||
"Payment" : {},
|
||||
"Push" : {},
|
||||
"Share" : {},
|
||||
"Speech" : {},
|
||||
"VideoPlayer" : {}
|
||||
},
|
||||
"distribute" : {
|
||||
"android" : {
|
||||
"networkSecurityConfig": "AndroidManifest/network_security_config.xml",
|
||||
"permissions" : [
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_MOCK_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_TASKS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_SMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
|
||||
"<uses-permission android:name=\"android.permission.SEND_SMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECEIVE_USER_PRESENT\"/>"
|
||||
]
|
||||
},
|
||||
"ios" : {
|
||||
"UIBackgroundModes" : [ "audio" ],
|
||||
"urlschemewhitelist" : [ "baidumap", "iosamap" ]
|
||||
},
|
||||
"sdkConfigs" : {
|
||||
"speech" : {
|
||||
"ifly" : {}
|
||||
}
|
||||
},
|
||||
"orientation" : [ "portrait-primary" ]
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"quickapp" : {},
|
||||
"quickapp-native" : {
|
||||
"icon" : "/static/logo.png",
|
||||
"package" : "com.example.demo",
|
||||
"features" : [
|
||||
{
|
||||
"name" : "system.clipboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"quickapp-webview" : {
|
||||
"icon" : "/static/logo.png",
|
||||
"package" : "com.example.demo",
|
||||
"minPlatformVersion" : 1070,
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : 100
|
||||
},
|
||||
"mp-weixin" : {
|
||||
"appid" : "",
|
||||
"setting" : {
|
||||
"urlCheck" : false
|
||||
},
|
||||
"usingComponents" : true,
|
||||
"permission" : {
|
||||
"scope.userLocation" : {
|
||||
"desc" : "演示定位能力"
|
||||
}
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"mp-alipay" : {
|
||||
"usingComponents" : true,
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"mp-baidu" : {
|
||||
"usingComponents" : true,
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
},
|
||||
"dynamicLib" : {
|
||||
"editorLib" : {
|
||||
"provider" : "swan-editor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true,
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"mp-jd" : {
|
||||
"usingComponents" : true,
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"h5" : {
|
||||
"template" : "template.h5.html",
|
||||
"router" : {
|
||||
"mode" : "history",
|
||||
"base" : ""
|
||||
},
|
||||
"sdkConfigs" : {
|
||||
"maps" : {
|
||||
"qqmap" : {
|
||||
"key" : "TKUBZ-D24AF-GJ4JY-JDVM2-IBYKK-KEBCU"
|
||||
}
|
||||
}
|
||||
},
|
||||
"async" : {
|
||||
"timeout" : 20000
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"vueVersion" : "3",
|
||||
"mp-kuaishou" : {
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"mp-lark" : {
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"mp-qq" : {
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"quickapp-webview-huawei" : {
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"quickapp-webview-union" : {
|
||||
"uniStatistics" : {
|
||||
"enable" : true
|
||||
}
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"version" : "2",
|
||||
"enable" : true
|
||||
}
|
||||
}
|
||||
133
package.json
Normal file
133
package.json
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
{
|
||||
"id": "hello-uniapp",
|
||||
"name": "hello-uniapp",
|
||||
"displayName": "hello-uniapp 示例工程",
|
||||
"version": "3.4.9",
|
||||
"description": "uni-app 框架示例,一套代码,同时发行到iOS、Android、H5、小程序等多个平台,请使用手机扫码快速体验 uni-app 的强大功能",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": "https://github.com/dcloudio/hello-uniapp.git",
|
||||
"keywords": [
|
||||
"hello-uniapp",
|
||||
"uni-app",
|
||||
"uni-ui",
|
||||
"示例工程"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/dcloudio/hello-uniapp/issues"
|
||||
},
|
||||
"homepage": "https://github.com/dcloudio/hello-uniapp#readme",
|
||||
"dependencies": {},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"type": "uniapp-template-project",
|
||||
"darkmode": "x",
|
||||
"i18n": "x",
|
||||
"widescreen": "x"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "√",
|
||||
"aliyun": "√",
|
||||
"alipay": "x"
|
||||
},
|
||||
"client": {
|
||||
"uni-app": {
|
||||
"vue": {
|
||||
"vue2": "√",
|
||||
"vue3": "√"
|
||||
},
|
||||
"web": {
|
||||
"safari": "√",
|
||||
"chrome": "√"
|
||||
},
|
||||
"app": {
|
||||
"vue": "√",
|
||||
"nvue": "√",
|
||||
"android": "√",
|
||||
"ios": "√",
|
||||
"harmony": "√"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "√",
|
||||
"alipay": "√",
|
||||
"toutiao": "√",
|
||||
"baidu": "√",
|
||||
"kuaishou": "√",
|
||||
"jd": "√",
|
||||
"harmony": "√",
|
||||
"qq": "√",
|
||||
"lark": "√"
|
||||
},
|
||||
"quickapp": {
|
||||
"huawei": "-",
|
||||
"union": "-"
|
||||
}
|
||||
},
|
||||
"uni-app-x": {
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"android": "-",
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"uni-app": {
|
||||
"scripts": {
|
||||
"mp-dingtalk": {
|
||||
"title": "钉钉小程序",
|
||||
"env": {
|
||||
"UNI_PLATFORM": "mp-alipay"
|
||||
},
|
||||
"define": {
|
||||
"MP-DINGTALK": true
|
||||
}
|
||||
},
|
||||
"hello-uniapp-demo": {
|
||||
"title": "hello-uniapp 演示网站",
|
||||
"env": {
|
||||
"UNI_PLATFORM": "h5"
|
||||
},
|
||||
"define": {
|
||||
"H5-DEMO": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0",
|
||||
"uni-app": "^4.03",
|
||||
"uni-app-x": ""
|
||||
}
|
||||
}
|
||||
107
pages.json
Normal file
107
pages.json
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/login/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
},
|
||||
"h5": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/home/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
},
|
||||
"h5": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/stock-in/empty",
|
||||
"style": {
|
||||
"navigationBarTitleText": "空载具入库",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
},
|
||||
"h5": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/stock-in/engine",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发动机入库",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
},
|
||||
"h5": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/stock-in/raw",
|
||||
"style": {
|
||||
"navigationBarTitleText": "原材料入库",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
},
|
||||
"h5": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/stock-in/raw-return",
|
||||
"style": {
|
||||
"navigationBarTitleText": "通道三原材料回库",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
},
|
||||
"h5": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/stock-in/manual",
|
||||
"style": {
|
||||
"navigationBarTitleText": "手动码盘入库",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
},
|
||||
"h5": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/test/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "测试页面",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
},
|
||||
"h5": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"pageOrientation": "portrait",
|
||||
"navigationBarTitleText": "PDA",
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarBackgroundColor": "#05DCEF",
|
||||
"backgroundColor": "#F5F5F5"
|
||||
}
|
||||
}
|
||||
340
pages/home/index.vue
Normal file
340
pages/home/index.vue
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="header">
|
||||
<view class="header-bg">
|
||||
<view class="wave w1" />
|
||||
<view class="wave w2" />
|
||||
</view>
|
||||
<button class="icon-btn" @click="toggleDrawer">
|
||||
<i class="icon icon-menu" style="font-size:40rpx;color:#fff"></i>
|
||||
</button>
|
||||
<text class="header-title">WMS移动终端(模板)</text>
|
||||
<button class="icon-btn" @click="noop">
|
||||
<i class="icon icon-notifications" style="font-size:40rpx;color:#fff"></i>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- Drawer Overlay -->
|
||||
<view class="drawer-mask" :class="{ show: showDrawer }" @click="closeDrawer" />
|
||||
<view class="drawer" :class="{ open: showDrawer }">
|
||||
<view class="drawer-top">
|
||||
<text class="drawer-title">导航</text>
|
||||
</view>
|
||||
<view class="drawer-list">
|
||||
<button
|
||||
v-for="item in drawerItems"
|
||||
:key="item.path"
|
||||
class="drawer-link"
|
||||
@click="nav(item.path)"
|
||||
>
|
||||
<i class="icon" :class="item.icon"></i>
|
||||
<text class="drawer-text">{{ item.label }}</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="grid">
|
||||
<view
|
||||
v-for="(item, index) in quickLinks"
|
||||
:key="item.path"
|
||||
class="card"
|
||||
:class="{ single: quickLinks.length % 2 === 1 && index === quickLinks.length - 1 }"
|
||||
@click="go(item.path)"
|
||||
>
|
||||
<view class="action-icon">
|
||||
<i class="icon" :class="item.icon" style="font-size:48rpx;color:#fff"></i>
|
||||
</view>
|
||||
<text class="action-title">{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
chartSize: 260,
|
||||
showDrawer: false,
|
||||
quickLinks: [
|
||||
{ label: '测试页面', path: '/pages/test/index', icon: 'icon-menu' },
|
||||
{ label: '空载具入库', path: '/pages/stock-in/empty', icon: 'icon-local_shipping' },
|
||||
{ label: '发动机入库', path: '/pages/stock-in/engine', icon: 'icon-save' },
|
||||
{ label: '原材料入库', path: '/pages/stock-in/raw', icon: 'icon-inventory' },
|
||||
{ label: '通道三原材料回库', path: '/pages/stock-in/raw-return', icon: 'icon-task_alt' }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
drawerItems() {
|
||||
return this.quickLinks
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
noop() { },
|
||||
toggleDrawer() { this.showDrawer = !this.showDrawer },
|
||||
closeDrawer() { this.showDrawer = false },
|
||||
nav(url) { this.showDrawer = false; this.go(url) },
|
||||
go(url) {
|
||||
uni.navigateTo({
|
||||
url
|
||||
})
|
||||
},
|
||||
arcPath(startRatio, endRatio) {
|
||||
const start = this.polar(50, 50, 40, startRatio * 2 * Math.PI)
|
||||
const end = this.polar(50, 50, 40, endRatio * 2 * Math.PI)
|
||||
const large = endRatio - startRatio > 0.5 ? 1 : 0
|
||||
return `M 50 50 L ${start.x} ${start.y} A 40 40 0 ${large} 1 ${end.x} ${end.y} Z`
|
||||
},
|
||||
polar(cx, cy, r, ang) {
|
||||
return {
|
||||
x: cx + r * Math.cos(ang),
|
||||
y: cy + r * Math.sin(ang)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: var(--bg-gray);
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 200rpx;
|
||||
position: relative;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(180deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
}
|
||||
|
||||
.wave {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 120rpx;
|
||||
background: radial-gradient(ellipse at 50% 30%, rgba(255, 255, 255, .25), rgba(255, 255, 255, 0) 60%);
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.w1 {
|
||||
top: 10rpx
|
||||
}
|
||||
|
||||
.w2 {
|
||||
top: 80rpx
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
z-index: 1;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 32rpx;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 24rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 180rpx;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(90deg, var(--grad-contrast-start), var(--grad-contrast-end));
|
||||
box-shadow: 0 8rpx 22rpx rgba(5, 220, 239, .25);
|
||||
}
|
||||
|
||||
.card.single {
|
||||
grid-column: span 2;
|
||||
height: 180rpx;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, .25);
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.action-icon .icon {
|
||||
color: #fff;
|
||||
font-size: 48rpx
|
||||
}
|
||||
|
||||
.action-title {
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 24rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: var(--text-color);
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.chart-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.legend {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.legend-text {
|
||||
color: var(--text-light);
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.pie {
|
||||
width: 280rpx;
|
||||
height: 280rpx;
|
||||
border-radius: 50%;
|
||||
background: conic-gradient(#7FFFAA 0 60%, #00FFFF 60% 100%);
|
||||
box-shadow: inset 0 0 0 0 #fff;
|
||||
}
|
||||
|
||||
.drawer-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, .28);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity .22s ease;
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
.drawer-mask.show {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.drawer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 460rpx;
|
||||
background: linear-gradient(180deg, #05DCEF 0%, #7DE2F5 60%, #FFFFFF 100%);
|
||||
box-shadow: 16rpx 0 32rpx rgba(0, 0, 0, .15);
|
||||
transform: translateX(-104%);
|
||||
transition: transform .26s cubic-bezier(.2, .8, .2, 1);
|
||||
z-index: 999;
|
||||
border-top-right-radius: 24rpx;
|
||||
border-bottom-right-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.drawer.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.drawer-top {
|
||||
padding: calc(env(safe-area-inset-top, 0) + 24rpx) 16rpx 8rpx 24rpx;
|
||||
}
|
||||
|
||||
.drawer-title {
|
||||
color: #fff;
|
||||
font-size: 44rpx;
|
||||
font-weight: 800;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.drawer-list {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 8rpx 0 24rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.drawer-link {
|
||||
width: 100%;
|
||||
height: 110rpx;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.drawer-link:active {
|
||||
background: rgba(255, 255, 255, .12);
|
||||
}
|
||||
|
||||
.drawer-link .icon {
|
||||
color: #ffffff;
|
||||
font-size: 38rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.drawer-text {
|
||||
color: #fff;
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
159
pages/login/index.vue
Normal file
159
pages/login/index.vue
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="hero">
|
||||
<view class="hero-bg">
|
||||
<view class="wave w1" />
|
||||
<view class="wave w2" />
|
||||
</view>
|
||||
<text class="title">仓库模板</text>
|
||||
<text class="subtitle">智能仓储管理系统</text>
|
||||
|
||||
<view class="logo-wrap">
|
||||
<view class="logo"><text>BK</text></view>
|
||||
</view>
|
||||
|
||||
<button class="login-btn" @click="goHome">进入系统</button>
|
||||
</view>
|
||||
|
||||
<view class="footer"><text>© 2025 菲达宝开</text></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
goHome() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/home/index'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #9BEAF8 0%, #E6FBFF 60%, #FFFFFF 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hero {
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hero-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 68vh;
|
||||
background: linear-gradient(180deg, #05DCEF 0%, #7DE2F5 60%, #FFFFFF 100%);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.hero-bg::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 65%, rgba(255, 255, 255, .18) 80%, rgba(255, 255, 255, .32) 92%, rgba(255, 255, 255, .45) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.wave {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 140rpx;
|
||||
background: radial-gradient(ellipse at 50% 0%, rgba(255, 255, 255, .35), rgba(255, 255, 255, 0) 60%);
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.w1 {
|
||||
bottom: 200rpx;
|
||||
}
|
||||
|
||||
.w2 {
|
||||
bottom: 120rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 160rpx;
|
||||
color: #fff;
|
||||
font-size: 64rpx;
|
||||
font-weight: 700;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #fff;
|
||||
font-size: 30rpx;
|
||||
opacity: .95;
|
||||
margin-top: 12rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
.logo {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
background: #fff;
|
||||
border-radius: 90rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
box-shadow: 0 12rpx 28rpx rgba(0, 0, 0, .12);
|
||||
}
|
||||
|
||||
.logo text {
|
||||
color: var(--grad-primary-start);
|
||||
font-size: 56rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
position: absolute;
|
||||
top: 70vh;
|
||||
transform: translateY(-10%);
|
||||
width: 600rpx;
|
||||
height: 96rpx;
|
||||
line-height: 96rpx;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 48rpx;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
box-shadow: 0 10rpx 24rpx rgba(5, 220, 239, .35);
|
||||
font-size: 32rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 40rpx 0 60rpx;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer text {
|
||||
color: #8a8a8a;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.logo-wrap {
|
||||
position: absolute;
|
||||
top: 56vh;
|
||||
transform: translateY(-50%);
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
||||
240
pages/stock-in/empty.vue
Normal file
240
pages/stock-in/empty.vue
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="header">
|
||||
<button class="back" @click="back"><i class="icon icon-arrow_back" style="color:#05DCEF;font-size:36rpx"></i></button>
|
||||
<text class="title">空载具入库</text>
|
||||
</view>
|
||||
|
||||
<view class="content">
|
||||
<view class="tip">
|
||||
<view class="tip-icon"><i class="icon icon-info" style="color:#05DCEF"></i></view>
|
||||
<text class="tip-text">请扫描或输入入库口完成空载具入库操作</text>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<text class="label">入库口 <text class="required">*</text></text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入入库口" v-model="standId" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="static-field">
|
||||
<text class="static-label">母托号 <text class="required">*</text></text>
|
||||
<text class="static-value">{{ defaultVehicle }}</text>
|
||||
</view>
|
||||
|
||||
<button class="submit" :disabled="loading" @click="submit">
|
||||
<i class="icon icon-save" style="margin-right:12rpx"></i> 空载具入库
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { WmsApiClient } from '@/common/wmsApi.js';
|
||||
import { DialogUtils } from '@/utils/dialog.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
standId: '',
|
||||
defaultVehicle: 'D99999999',
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
resolveError(error, fallback = '请求失败') {
|
||||
if (!error) return fallback;
|
||||
if (typeof error === 'string') return error;
|
||||
if (typeof error.message === 'string') return error.message;
|
||||
if (typeof error.errMsg === 'string') return error.errMsg;
|
||||
if (typeof error.code === 'number') return `${fallback} (${error.code})`;
|
||||
return fallback;
|
||||
},
|
||||
async submit() {
|
||||
const stand = (this.standId || '').trim();
|
||||
if (!stand) {
|
||||
DialogUtils.showWarningMessage('提示', '请先输入入库口');
|
||||
return;
|
||||
}
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
uni.showLoading({ title: '正在请求', mask: true });
|
||||
try {
|
||||
const response = await WmsApiClient.emptyVehicleIn({ standId: stand });
|
||||
const code = Number(response.code);
|
||||
if (code === 0) {
|
||||
DialogUtils.showSuccessMessage('成功', '空载具入库成功');
|
||||
this.standId = '';
|
||||
} else {
|
||||
DialogUtils.showWarningMessage('操作未成功', `${response.message || '提交失败'} (${response.code})`);
|
||||
}
|
||||
} catch (error) {
|
||||
DialogUtils.showErrorMessage('错误', this.resolveError(error));
|
||||
} finally {
|
||||
this.loading = false;
|
||||
uni.hideLoading();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.header {
|
||||
position: relative;
|
||||
height: 120rpx;
|
||||
padding: 0;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.tip {
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f2fdff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
color: #333;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #e6f7ff;
|
||||
text-align: center;
|
||||
line-height: 40rpx;
|
||||
margin-right: 12rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
color: #333;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
height: 96rpx;
|
||||
border-radius: 12rpx;
|
||||
border: 1px solid #e6e6e6;
|
||||
background: #fff;
|
||||
padding: 0 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left-icon {
|
||||
color: #05DCEF;
|
||||
font-size: 36rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.static-field {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
border: 1px solid #e6e6e6;
|
||||
padding: 24rpx 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.static-label {
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.static-value {
|
||||
font-size: 32rpx;
|
||||
color: #111;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.submit {
|
||||
width: 100%;
|
||||
height: 92rpx;
|
||||
border: none;
|
||||
color: #fff;
|
||||
border-radius: 46rpx;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
font-size: 32rpx;
|
||||
box-shadow: 0 8rpx 22rpx rgba(5, 220, 239, .25);
|
||||
text-align: center;
|
||||
margin-top: 28rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.submit[disabled] {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.back {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 80rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
border: none;
|
||||
color: #05DCEF;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
265
pages/stock-in/engine.vue
Normal file
265
pages/stock-in/engine.vue
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="header">
|
||||
<button class="back" @click="back"><i class="icon icon-arrow_back" style="color:#05DCEF;font-size:36rpx"></i></button>
|
||||
<text class="title">发动机入库</text>
|
||||
</view>
|
||||
|
||||
<view class="content">
|
||||
<view class="tip">
|
||||
<view class="tip-icon"><i class="icon icon-info" style="color:#05DCEF"></i></view>
|
||||
<text class="tip-text">请依次扫描入库口、母托号与发动机信息后提交</text>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<text class="label">入库口 <text class="required">*</text></text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入入库口" v-model="standId" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<text class="label">母托号 <text class="required">*</text></text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入母托号" v-model="vehicleNo" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<text class="label">发动机序列号 <text class="required">*</text></text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入发动机序列号" v-model="goodsId" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<text class="label">发动机机型 <text class="required">*</text></text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入发动机机型" v-model="model" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="submit" :disabled="loading" @click="submit">
|
||||
<i class="icon icon-save" style="margin-right:12rpx"></i> 确认入库
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { WmsApiClient } from '@/common/wmsApi.js';
|
||||
import { DialogUtils } from '@/utils/dialog.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
standId: '',
|
||||
vehicleNo: '',
|
||||
goodsId: '',
|
||||
model: '',
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
resolveError(error, fallback = '请求失败') {
|
||||
if (!error) return fallback;
|
||||
if (typeof error === 'string') return error;
|
||||
if (typeof error.message === 'string') return error.message;
|
||||
if (typeof error.errMsg === 'string') return error.errMsg;
|
||||
if (typeof error.code === 'number') return `${fallback} (${error.code})`;
|
||||
return fallback;
|
||||
},
|
||||
resetForm() {
|
||||
this.standId = '';
|
||||
this.vehicleNo = '';
|
||||
this.goodsId = '';
|
||||
this.model = '';
|
||||
},
|
||||
async submit() {
|
||||
const stand = (this.standId || '').trim();
|
||||
const vehicle = (this.vehicleNo || '').trim();
|
||||
const goods = (this.goodsId || '').trim();
|
||||
const model = (this.model || '').trim();
|
||||
|
||||
if (!stand) {
|
||||
DialogUtils.showWarningMessage('提示', '请先输入入库口');
|
||||
return;
|
||||
}
|
||||
if (!vehicle) {
|
||||
DialogUtils.showWarningMessage('提示', '请输入母托号');
|
||||
return;
|
||||
}
|
||||
if (!goods) {
|
||||
DialogUtils.showWarningMessage('提示', '请输入发动机序列号');
|
||||
return;
|
||||
}
|
||||
if (!model) {
|
||||
DialogUtils.showWarningMessage('提示', '请输入发动机机型');
|
||||
return;
|
||||
}
|
||||
if (this.loading) return;
|
||||
|
||||
this.loading = true;
|
||||
uni.showLoading({ title: '正在请求', mask: true });
|
||||
try {
|
||||
const response = await WmsApiClient.engineStockIn({
|
||||
standId: stand,
|
||||
vehicleNo: vehicle,
|
||||
goodsId: goods,
|
||||
model,
|
||||
});
|
||||
const code = Number(response.code);
|
||||
if (code === 0) {
|
||||
DialogUtils.showSuccessMessage('成功', '入库成功');
|
||||
this.resetForm();
|
||||
} else {
|
||||
DialogUtils.showWarningMessage('操作未成功', `${response.message || '提交失败'} (${response.code})`);
|
||||
}
|
||||
} catch (error) {
|
||||
DialogUtils.showErrorMessage('错误', this.resolveError(error));
|
||||
} finally {
|
||||
this.loading = false;
|
||||
uni.hideLoading();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
position: relative;
|
||||
height: 120rpx;
|
||||
padding: 0;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.tip {
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f2fdff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
color: #333;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #e6f7ff;
|
||||
text-align: center;
|
||||
line-height: 40rpx;
|
||||
margin-right: 12rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
color: #333;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
height: 96rpx;
|
||||
border-radius: 12rpx;
|
||||
border: 1px solid #e6e6e6;
|
||||
background: #fff;
|
||||
padding: 0 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left-icon {
|
||||
color: #05DCEF;
|
||||
font-size: 36rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.submit {
|
||||
width: 100%;
|
||||
height: 92rpx;
|
||||
border: none;
|
||||
color: #fff;
|
||||
border-radius: 46rpx;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
font-size: 32rpx;
|
||||
box-shadow: 0 8rpx 22rpx rgba(5, 220, 239, .25);
|
||||
text-align: center;
|
||||
margin-top: 28rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.submit[disabled] {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.back {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 80rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
border: none;
|
||||
color: #05DCEF;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
392
pages/stock-in/manual.vue
Normal file
392
pages/stock-in/manual.vue
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="header">
|
||||
<button class="back" @click="back"><i class="icon icon-arrow_back"
|
||||
style="color:#05DCEF;font-size:36rpx"></i></button>
|
||||
<text class="title">手动码盘入库</text>
|
||||
</view>
|
||||
|
||||
<view class="content">
|
||||
<!-- 顶部提示 -->
|
||||
<view class="tip">
|
||||
<view class="tip-icon"><i class="icon icon-info" style="color:#05DCEF"></i></view>
|
||||
<text class="tip-text">请先扫描载具号,然后添加物料信息进行码盘入库</text>
|
||||
</view>
|
||||
|
||||
<!-- 载具条码 -->
|
||||
<view class="field">
|
||||
<text class="label">载具号</text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入载具号" v-model="vehicleCode" @blur="resolveVehicle" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 物料条码 -->
|
||||
<view class="field">
|
||||
<text class="label">物料条码</text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描物料二维码" v-model="goodsCode" @blur="resolveCode" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮组 -->
|
||||
<view class="button-row">
|
||||
<button class="btn" @click="addGoods"><i class="icon icon-add"
|
||||
style="margin-right:8rpx"></i>添加物料</button>
|
||||
<button class="btn" @click="finish"><i class="icon icon-task_alt"
|
||||
style="margin-right:8rpx"></i>码盘完成</button>
|
||||
</view>
|
||||
|
||||
<!-- 列表统计 -->
|
||||
<view class="list-header"><text>已添加物料 ({{ packageData.length }})</text></view>
|
||||
|
||||
<!-- 物料卡片列表:尽量还原 RN 的布局与字段 -->
|
||||
<view class="list">
|
||||
<view v-for="item in packageData" :key="item.id" class="card">
|
||||
<view class="card-header">
|
||||
<view class="card-header-left">
|
||||
<text class="card-header-text">批次:{{ item.batch }}</text>
|
||||
<text class="card-header-text">物料:{{ item.segment1 }}</text>
|
||||
</view>
|
||||
<view class="card-actions">
|
||||
<button class="card-action-button" @click="editItem(item)"><i
|
||||
class="icon icon-edit"></i></button>
|
||||
<button class="card-action-button" @click="deleteItem(item.id)"><i
|
||||
class="icon icon-delete"></i></button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-content">
|
||||
<view class="card-row">
|
||||
<view class="card-field">
|
||||
<text class="card-label">物料编码</text>
|
||||
<text class="card-value">{{ item.segment1 }}</text>
|
||||
</view>
|
||||
<view class="card-field">
|
||||
<text class="card-label">物料ID</text>
|
||||
<text class="card-value">{{ item.itemId }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-row">
|
||||
<view class="card-field">
|
||||
<text class="card-label">数量</text>
|
||||
<view class="editable-value">
|
||||
<input class="num-input" type="number" v-model="item.quantity" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-field">
|
||||
<text class="card-label">重量(kg)</text>
|
||||
<view class="editable-value">
|
||||
<input class="num-input" type="number" v-model="item.weight" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-row">
|
||||
<view class="card-field">
|
||||
<text class="card-label">产品数据</text>
|
||||
<text class="card-value">{{ item.productData }}</text>
|
||||
</view>
|
||||
<view class="card-field">
|
||||
<text class="card-label">区域/库位</text>
|
||||
<text class="card-value">{{ item.area || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DialogUtils } from '@/utils/dialog.js';
|
||||
let nextId = 1
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
vehicleCode: '',
|
||||
goodsCode: '',
|
||||
packageData: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
resolveVehicle() {
|
||||
const code = (this.vehicleCode || '').trim()
|
||||
if (!code) return
|
||||
if (code.length === 15) {
|
||||
DialogUtils.showSuccessMessage('扫描成功', `车辆码: ${code}`)
|
||||
} else {
|
||||
DialogUtils.showErrorMessage('校验失败', '无效的车辆码长度')
|
||||
}
|
||||
},
|
||||
resolveCode() {
|
||||
const code = (this.goodsCode || '').trim()
|
||||
if (!code) return
|
||||
const parts = code.split(',')
|
||||
if (![6, 7, 8].includes(parts.length)) {
|
||||
DialogUtils.showWarningMessage('提示', '物料条码格式不正确')
|
||||
return
|
||||
}
|
||||
const p = parts.slice(0, 6)
|
||||
const item = {
|
||||
id: String(nextId++),
|
||||
segment1: p[0],
|
||||
itemId: p[1],
|
||||
batch: p[2],
|
||||
quantity: p[3],
|
||||
weight: p[4],
|
||||
productData: p[5],
|
||||
area: ''
|
||||
}
|
||||
if (this.packageData.some(x => x.batch === item.batch)) {
|
||||
DialogUtils.showWarningMessage('重复', '该物料已在列表中')
|
||||
} else {
|
||||
this.packageData.push(item)
|
||||
}
|
||||
this.goodsCode = ''
|
||||
},
|
||||
addGoods() {
|
||||
this.resolveCode()
|
||||
},
|
||||
deleteItem(id) {
|
||||
this.packageData = this.packageData.filter(x => x.id !== id)
|
||||
},
|
||||
editItem(item) {
|
||||
DialogUtils.toast('可编辑数量与重量')
|
||||
},
|
||||
finish() {
|
||||
DialogUtils.toast('码盘完成')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
position: relative;
|
||||
height: 120rpx;
|
||||
padding: 0;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.tip {
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f2fdff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
color: #333;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #e6f7ff;
|
||||
text-align: center;
|
||||
line-height: 40rpx;
|
||||
margin-right: 12rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
color: #333;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
height: 96rpx;
|
||||
border-radius: 12rpx;
|
||||
border: 1px solid #e6e6e6;
|
||||
background: #fff;
|
||||
padding: 0 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left-icon {
|
||||
color: #05DCEF;
|
||||
font-size: 36rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
margin: 24rpx 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 92rpx;
|
||||
border: none;
|
||||
color: #fff;
|
||||
border-radius: 46rpx;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
font-size: 30rpx;
|
||||
box-shadow: 0 8rpx 22rpx rgba(5, 220, 239, .25);
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.list-header {
|
||||
margin-top: 8rpx;
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 0;
|
||||
margin-top: 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, .06)
|
||||
}
|
||||
|
||||
.card-header {
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .08);
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
}
|
||||
|
||||
.card-header-left {
|
||||
flex: 1
|
||||
}
|
||||
|
||||
.card-header-text {
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
margin-right: 16rpx
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
gap: 8rpx
|
||||
}
|
||||
|
||||
.card-action-button {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border: none;
|
||||
border-radius: 8rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 16rpx;
|
||||
background: #fff
|
||||
}
|
||||
|
||||
.card-row {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 12rpx
|
||||
}
|
||||
|
||||
.card-field {
|
||||
flex: 1
|
||||
}
|
||||
|
||||
.card-label {
|
||||
color: #888;
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 4rpx;
|
||||
display: block
|
||||
}
|
||||
|
||||
.card-value {
|
||||
color: #333;
|
||||
font-size: 28rpx
|
||||
}
|
||||
|
||||
.editable-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx
|
||||
}
|
||||
|
||||
.num-input {
|
||||
width: 220rpx;
|
||||
height: 64rpx;
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 8rpx;
|
||||
padding: 0 12rpx;
|
||||
font-size: 28rpx
|
||||
}
|
||||
|
||||
.back {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 80rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
border: none;
|
||||
color: #05DCEF;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
241
pages/stock-in/raw-return.vue
Normal file
241
pages/stock-in/raw-return.vue
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="header">
|
||||
<button class="back" @click="back"><i class="icon icon-arrow_back" style="color:#05DCEF;font-size:36rpx"></i></button>
|
||||
<text class="title">通道三原材料回库</text>
|
||||
</view>
|
||||
|
||||
<view class="content">
|
||||
<view class="tip">
|
||||
<view class="tip-icon"><i class="icon icon-info" style="color:#05DCEF"></i></view>
|
||||
<text class="tip-text">请输入母托号,系统会自动回库至固定入库口</text>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<text class="label">母托号 <text class="required">*</text></text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入母托号" v-model="vehicleNo" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="static-field">
|
||||
<text class="static-label">回库口</text>
|
||||
<text class="static-value">{{ fixedStand }}</text>
|
||||
</view>
|
||||
|
||||
<button class="submit" :disabled="loading" @click="submit">
|
||||
<i class="icon icon-task_alt" style="margin-right:12rpx"></i> 确认回库
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { WmsApiClient } from '@/common/wmsApi.js';
|
||||
import { DialogUtils } from '@/utils/dialog.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
vehicleNo: '',
|
||||
fixedStand: '104',
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
resolveError(error, fallback = '请求失败') {
|
||||
if (!error) return fallback;
|
||||
if (typeof error === 'string') return error;
|
||||
if (typeof error.message === 'string') return error.message;
|
||||
if (typeof error.errMsg === 'string') return error.errMsg;
|
||||
if (typeof error.code === 'number') return `${fallback} (${error.code})`;
|
||||
return fallback;
|
||||
},
|
||||
async submit() {
|
||||
const vehicle = (this.vehicleNo || '').trim();
|
||||
if (!vehicle) {
|
||||
DialogUtils.showWarningMessage('提示', '请输入母托号');
|
||||
return;
|
||||
}
|
||||
if (this.loading) return;
|
||||
|
||||
this.loading = true;
|
||||
uni.showLoading({ title: '正在请求', mask: true });
|
||||
try {
|
||||
const response = await WmsApiClient.rawStockIn({
|
||||
vehicleNo: vehicle,
|
||||
standId: this.fixedStand,
|
||||
});
|
||||
const code = Number(response.code);
|
||||
if (code === 0) {
|
||||
DialogUtils.showSuccessMessage('成功', '回库成功');
|
||||
this.vehicleNo = '';
|
||||
} else {
|
||||
DialogUtils.showWarningMessage('操作未成功', `${response.message || '提交失败'} (${response.code})`);
|
||||
}
|
||||
} catch (error) {
|
||||
DialogUtils.showErrorMessage('错误', this.resolveError(error));
|
||||
} finally {
|
||||
uni.hideLoading();
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
position: relative;
|
||||
height: 120rpx;
|
||||
padding: 0;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.tip {
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f2fdff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
color: #333;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #e6f7ff;
|
||||
text-align: center;
|
||||
line-height: 40rpx;
|
||||
margin-right: 12rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
color: #333;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
height: 96rpx;
|
||||
border-radius: 12rpx;
|
||||
border: 1px solid #e6e6e6;
|
||||
background: #fff;
|
||||
padding: 0 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left-icon {
|
||||
color: #05DCEF;
|
||||
font-size: 36rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.static-field {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
border: 1px solid #e6e6e6;
|
||||
padding: 24rpx 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.static-label {
|
||||
color: #666;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.static-value {
|
||||
font-size: 32rpx;
|
||||
color: #111;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.submit {
|
||||
width: 100%;
|
||||
height: 92rpx;
|
||||
border: none;
|
||||
color: #fff;
|
||||
border-radius: 46rpx;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
font-size: 32rpx;
|
||||
box-shadow: 0 8rpx 22rpx rgba(52, 199, 89, .25);
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.submit[disabled] {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.back {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 80rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
border: none;
|
||||
color: #05DCEF;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
511
pages/stock-in/raw.vue
Normal file
511
pages/stock-in/raw.vue
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="header">
|
||||
<button class="back" @click="back"><i class="icon icon-arrow_back" style="color:#05DCEF;font-size:36rpx"></i></button>
|
||||
<text class="title">原材料入库</text>
|
||||
</view>
|
||||
|
||||
<view class="content">
|
||||
<view class="tip">
|
||||
<view class="tip-icon"><i class="icon icon-info" style="color:#05DCEF"></i></view>
|
||||
<text class="tip-text">请输入母托号并逐个绑定原材料,最后确认入库</text>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<text class="label">母托号 <text class="required">*</text></text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入母托号" v-model="vehicleNo" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="field">
|
||||
<text class="label">原材料号</text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入原材料号" v-model="goodsId" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="action-button" :disabled="isBusy" @click="handleNextMaterial">
|
||||
<i class="icon icon-arrow_forward" style="margin-right:10rpx"></i> 下一颗物料
|
||||
</button>
|
||||
|
||||
<view class="field">
|
||||
<text class="label">入库口 <text class="required">*</text></text>
|
||||
<view class="input-wrap">
|
||||
<i class="left-icon icon icon-qr_code_2" style="font-size:36rpx"></i>
|
||||
<input class="input" placeholder="请扫描或输入入库口" v-model="standId" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="list-section">
|
||||
<view class="list-header">
|
||||
<text class="list-title">已绑定的原材料</text>
|
||||
<text class="list-count">({{ orderInList.length }})</text>
|
||||
</view>
|
||||
<view v-if="!orderInList.length" class="empty-placeholder">
|
||||
<i class="icon icon-info" style="margin-right:8rpx;color:#05DCEF"></i>
|
||||
<text>暂无数据</text>
|
||||
</view>
|
||||
<view v-else class="card-group">
|
||||
<view v-for="item in orderInList" :key="item.rowId" class="card">
|
||||
<view class="card-header">
|
||||
<view class="card-header-text">
|
||||
<text class="card-title">物料号:{{ item.goodsId }}</text>
|
||||
<text class="card-subtitle">数量:{{ formatNumber(item.goodsNum) }}</text>
|
||||
</view>
|
||||
<button class="card-action" :disabled="isBusy" @click="deleteItem(item)">
|
||||
<i class="icon icon-delete" style="color:#ff4d4f;font-size:32rpx"></i>
|
||||
</button>
|
||||
</view>
|
||||
<view class="card-row">
|
||||
<text class="card-label">批次:</text>
|
||||
<text class="card-value">{{ item.batchNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="card-row">
|
||||
<text class="card-label">生产日期:</text>
|
||||
<text class="card-value">{{ formatDate(item.productionDate) }}</text>
|
||||
</view>
|
||||
<view class="card-row">
|
||||
<text class="card-label">机型:</text>
|
||||
<text class="card-value">{{ item.model || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="submit" :disabled="isBusy" @click="submitRawIn">
|
||||
<i class="icon icon-task_alt" style="margin-right:10rpx"></i> 确认入库
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { WmsApiClient } from '@/common/wmsApi.js';
|
||||
import { DialogUtils } from '@/utils/dialog.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
vehicleNo: '',
|
||||
goodsId: '',
|
||||
standId: '',
|
||||
orderInList: [],
|
||||
loadingAction: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isBusy() {
|
||||
return this.loadingAction !== '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
resolveError(error, fallback = '请求失败') {
|
||||
if (!error) return fallback;
|
||||
if (typeof error === 'string') return error;
|
||||
if (typeof error.message === 'string') return error.message;
|
||||
if (typeof error.errMsg === 'string') return error.errMsg;
|
||||
if (typeof error.code === 'number') return `${fallback} (${error.code})`;
|
||||
return fallback;
|
||||
},
|
||||
formatNumber(value) {
|
||||
const num = Number(value);
|
||||
if (Number.isNaN(num)) return '0';
|
||||
return num % 1 === 0 ? String(num) : num.toFixed(2);
|
||||
},
|
||||
formatDate(value) {
|
||||
if (!value) return '-';
|
||||
const parsed = new Date(value);
|
||||
if (!Number.isNaN(parsed.getTime())) {
|
||||
const y = parsed.getFullYear();
|
||||
const m = String(parsed.getMonth() + 1).padStart(2, '0');
|
||||
const d = String(parsed.getDate()).padStart(2, '0');
|
||||
return `${y}-${m}-${d}`;
|
||||
}
|
||||
const normalized = value.replace('T', ' ').split(' ')[0];
|
||||
return normalized || '-';
|
||||
},
|
||||
async handleNextMaterial() {
|
||||
const vehicle = (this.vehicleNo || '').trim();
|
||||
const goods = (this.goodsId || '').trim();
|
||||
if (!vehicle) {
|
||||
DialogUtils.showWarningMessage('提示', '请输入母托号');
|
||||
return;
|
||||
}
|
||||
if (this.isBusy) return;
|
||||
|
||||
if (goods) {
|
||||
this.loadingAction = 'binding';
|
||||
uni.showLoading({ title: '正在绑定物料', mask: true });
|
||||
try {
|
||||
const bindRes = await WmsApiClient.bindVehicle({
|
||||
vehicleNo: vehicle,
|
||||
goodsId: goods,
|
||||
});
|
||||
const code = Number(bindRes.code);
|
||||
if (code === 0) {
|
||||
DialogUtils.showSuccessMessage('成功', '物料绑定成功');
|
||||
this.goodsId = '';
|
||||
} else {
|
||||
DialogUtils.showWarningMessage('绑定未成功', `${bindRes.message || '操作失败'} (${bindRes.code})`);
|
||||
}
|
||||
} catch (error) {
|
||||
DialogUtils.showErrorMessage('错误', this.resolveError(error));
|
||||
} finally {
|
||||
uni.hideLoading();
|
||||
this.loadingAction = '';
|
||||
}
|
||||
}
|
||||
|
||||
this.loadingAction = 'query';
|
||||
uni.showLoading({ title: '获取物料列表', mask: true });
|
||||
try {
|
||||
await this.loadOrderList(vehicle);
|
||||
} catch (error) {
|
||||
DialogUtils.showErrorMessage('错误', this.resolveError(error));
|
||||
} finally {
|
||||
uni.hideLoading();
|
||||
this.loadingAction = '';
|
||||
}
|
||||
},
|
||||
async loadOrderList(vehicle) {
|
||||
const listRes = await WmsApiClient.getOrderInWithVehicleNo({ vehicleNo: vehicle });
|
||||
const code = Number(listRes.code);
|
||||
if (code === 0) {
|
||||
const data = Array.isArray(listRes.returnData) ? listRes.returnData : [];
|
||||
this.orderInList = data.map((item) => ({
|
||||
...item,
|
||||
goodsNum: item.goodsNum,
|
||||
}));
|
||||
} else {
|
||||
DialogUtils.showWarningMessage('提示', `${listRes.message || '获取物料列表失败'} (${listRes.code})`);
|
||||
}
|
||||
},
|
||||
async deleteItem(item) {
|
||||
const vehicle = (this.vehicleNo || '').trim();
|
||||
if (!vehicle) {
|
||||
DialogUtils.showWarningMessage('提示', '母托号不能为空');
|
||||
return;
|
||||
}
|
||||
if (!item || !item.rowId) return;
|
||||
if (this.isBusy) return;
|
||||
|
||||
this.loadingAction = 'deleting';
|
||||
uni.showLoading({ title: '正在删除', mask: true });
|
||||
try {
|
||||
const res = await WmsApiClient.deleteOrderIn({ rowId: item.rowId });
|
||||
const code = Number(res.code);
|
||||
if (code === 0) {
|
||||
DialogUtils.showSuccessMessage('成功', '物料删除成功');
|
||||
await this.loadOrderList(vehicle);
|
||||
} else {
|
||||
DialogUtils.showWarningMessage('删除未成功', `${res.message || '删除失败'} (${res.code})`);
|
||||
}
|
||||
} catch (error) {
|
||||
DialogUtils.showErrorMessage('错误', this.resolveError(error));
|
||||
} finally {
|
||||
uni.hideLoading();
|
||||
this.loadingAction = '';
|
||||
}
|
||||
},
|
||||
async submitRawIn() {
|
||||
const vehicle = (this.vehicleNo || '').trim();
|
||||
const stand = (this.standId || '').trim();
|
||||
if (!vehicle) {
|
||||
DialogUtils.showWarningMessage('提示', '请输入母托号');
|
||||
return;
|
||||
}
|
||||
if (!stand) {
|
||||
DialogUtils.showWarningMessage('提示', '请输入入库口');
|
||||
return;
|
||||
}
|
||||
if (!this.orderInList.length) {
|
||||
DialogUtils.showWarningMessage('提示', '请先绑定原材料');
|
||||
return;
|
||||
}
|
||||
if (this.isBusy) return;
|
||||
|
||||
this.loadingAction = 'submitting';
|
||||
uni.showLoading({ title: '正在提交', mask: true });
|
||||
try {
|
||||
const res = await WmsApiClient.rawStockIn({ vehicleNo: vehicle, standId: stand });
|
||||
const code = Number(res.code);
|
||||
if (code === 0) {
|
||||
DialogUtils.showSuccessMessage('成功', '原材料入库成功');
|
||||
this.vehicleNo = '';
|
||||
this.goodsId = '';
|
||||
this.standId = '';
|
||||
this.orderInList = [];
|
||||
} else {
|
||||
DialogUtils.showWarningMessage('入库未成功', `${res.message || '提交失败'} (${res.code})`);
|
||||
}
|
||||
} catch (error) {
|
||||
DialogUtils.showErrorMessage('错误', this.resolveError(error));
|
||||
} finally {
|
||||
uni.hideLoading();
|
||||
this.loadingAction = '';
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
position: relative;
|
||||
height: 120rpx;
|
||||
padding: 0;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 56rpx;
|
||||
}
|
||||
|
||||
.tip {
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f2fdff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
color: #333;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #e6f7ff;
|
||||
text-align: center;
|
||||
line-height: 40rpx;
|
||||
margin-right: 12rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
color: #333;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
height: 96rpx;
|
||||
border-radius: 12rpx;
|
||||
border: 1px solid #e6e6e6;
|
||||
background: #fff;
|
||||
padding: 0 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left-icon {
|
||||
color: #05DCEF;
|
||||
font-size: 36rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
width: 100%;
|
||||
height: 92rpx;
|
||||
border: none;
|
||||
color: #fff;
|
||||
border-radius: 46rpx;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
font-size: 32rpx;
|
||||
box-shadow: 0 8rpx 22rpx rgba(5, 220, 239, .25);
|
||||
text-align: center;
|
||||
margin-bottom: 28rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.action-button[disabled] {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.list-section {
|
||||
margin-top: 12rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.list-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.list-count {
|
||||
font-size: 28rpx;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.empty-placeholder {
|
||||
height: 140rpx;
|
||||
border-radius: 12rpx;
|
||||
border: 1px dashed #d9d9d9;
|
||||
color: #999;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.card-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 8rpx 22rpx rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.card-header-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 30rpx;
|
||||
color: #111;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
font-size: 26rpx;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.card-action {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 32rpx;
|
||||
border: none;
|
||||
background: rgba(255, 77, 79, .12);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card-action[disabled] {
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
.card-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.card-value {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.submit {
|
||||
width: 100%;
|
||||
height: 92rpx;
|
||||
border: none;
|
||||
color: #fff;
|
||||
border-radius: 46rpx;
|
||||
background: linear-gradient(90deg, var(--grad-primary-start), var(--grad-primary-mid));
|
||||
font-size: 32rpx;
|
||||
box-shadow: 0 8rpx 22rpx rgba(52, 199, 89, .25);
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.submit[disabled] {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.back {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 80rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
border: none;
|
||||
color: #05DCEF;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
BIN
static/fonts/MaterialIcons-Regular.ttf
Normal file
BIN
static/fonts/MaterialIcons-Regular.ttf
Normal file
Binary file not shown.
BIN
static/logo.png
Normal file
BIN
static/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
28
static/mui-icons.css
Normal file
28
static/mui-icons.css
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
@font-face {
|
||||
font-family: 'muiicon';
|
||||
src: url('/static/fonts/MaterialIcons-Regular.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
.icon { font-family: 'muiicon' !important; font-style: normal; font-weight: normal; display: inline-block; line-height: 1; text-transform: none; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
||||
.icon-menu:before { content: "\e5d2"; }
|
||||
.icon-notifications:before { content: "\e7f4"; }
|
||||
.icon-local_shipping:before { content: "\e558"; }
|
||||
.icon-inventory:before { content: "\e179"; }
|
||||
.icon-arrow_back:before { content: "\e5c4"; }
|
||||
.icon-save:before { content: "\e161"; }
|
||||
.icon-qr_code_2:before { content: "\f00a"; }
|
||||
.icon-info:before { content: "\e88e"; }
|
||||
.icon-add:before { content: "\e145"; }
|
||||
.icon-task_alt:before { content: "\e2e6"; }
|
||||
.icon-edit:before { content: "\e3c9"; }
|
||||
.icon-delete:before { content: "\e872"; }
|
||||
@font-face {
|
||||
font-family: 'Material Icons Local';
|
||||
src: url('/static/fonts/MaterialIcons-Regular.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
.mi{font-family:'Material Icons Local','Material Icons';font-weight:normal;font-style:normal;font-size:inherit;display:inline-block;line-height:1;letter-spacing:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga';-webkit-font-feature-settings:'liga';text-rendering:optimizeLegibility}
|
||||
BIN
static/uni.ttf
Normal file
BIN
static/uni.ttf
Normal file
Binary file not shown.
1
static/web/image-resize-3.0.1.min.js
vendored
Normal file
1
static/web/image-resize-3.0.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
static/web/quill-1.3.7.min.js
vendored
Normal file
7
static/web/quill-1.3.7.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
61
template.h5.html
Normal file
61
template.h5.html
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<title>
|
||||
<%= htmlWebpackPlugin.options.title %>
|
||||
</title>
|
||||
<!-- 正式发布的时候使用,开发期间不启用。↓ -->
|
||||
<!-- <script src="/h5/touch-emulator.js"></script>
|
||||
<script>
|
||||
TouchEmulator();
|
||||
if (document.documentElement.clientWidth > 1024) {
|
||||
window.location.href = '/h5/pcguide.html#'+location.pathname+location.search;
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?";// 百度统计key
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script> -->
|
||||
<!-- 正式发布的时候使用,开发期间不启用。↑ -->
|
||||
<script>
|
||||
// document.addEventListener('DOMContentLoaded', function() {
|
||||
// document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
|
||||
// })
|
||||
</script>
|
||||
<script src="<%= BASE_URL %>static/web/image-resize-3.0.1.min.js"></script>
|
||||
<script src="<%= BASE_URL %>static/web/quill-1.3.7.min.js"></script>
|
||||
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 该文件为 H5 平台的模板 HTML,并非应用入口。 -->
|
||||
<!-- 请勿在此文件编写页面代码或直接运行此文件。 -->
|
||||
<!-- 详见文档:https://uniapp.dcloud.io/collocation/manifest?id=h5-template -->
|
||||
<noscript>
|
||||
<strong>Please enable JavaScript to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
<script>
|
||||
/*BAIDU_STAT*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
86
uni.scss
Normal file
86
uni.scss
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* 这里是uni-app内置的常用样式变量
|
||||
*
|
||||
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
|
||||
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
|
||||
*
|
||||
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
|
||||
*/
|
||||
|
||||
/* 颜色变量 */
|
||||
|
||||
/* 行为相关颜色 */
|
||||
$uni-color-primary: #007aff;
|
||||
$uni-color-success: #4cd964;
|
||||
$uni-color-warning: #f0ad4e;
|
||||
$uni-color-error: #dd524d;
|
||||
|
||||
/* 文字基本颜色 */
|
||||
$uni-text-color:#333;//基本色
|
||||
$uni-text-color-inverse:#fff;//反色
|
||||
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
|
||||
$uni-text-color-placeholder: #808080;
|
||||
$uni-text-color-disable:#c0c0c0;
|
||||
|
||||
/* 背景颜色 */
|
||||
$uni-bg-color:#ffffff;
|
||||
$uni-bg-color-grey:#f8f8f8;
|
||||
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
|
||||
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
|
||||
|
||||
/* 边框颜色 */
|
||||
$uni-border-color:#e5e5e5;
|
||||
|
||||
/* 尺寸变量 */
|
||||
|
||||
/* 文字尺寸 */
|
||||
$uni-font-size-sm:12px;
|
||||
$uni-font-size-base:14px;
|
||||
$uni-font-size-lg:16;
|
||||
|
||||
/* 图片尺寸 */
|
||||
$uni-img-size-sm:20px;
|
||||
$uni-img-size-base:26px;
|
||||
$uni-img-size-lg:40px;
|
||||
|
||||
/* Border Radius */
|
||||
$uni-border-radius-sm: 2px;
|
||||
$uni-border-radius-base: 3px;
|
||||
$uni-border-radius-lg: 6px;
|
||||
$uni-border-radius-circle: 50%;
|
||||
|
||||
/* 水平间距 */
|
||||
$uni-spacing-row-sm: 5px;
|
||||
$uni-spacing-row-base: 10px;
|
||||
$uni-spacing-row-lg: 15px;
|
||||
|
||||
/* 垂直间距 */
|
||||
$uni-spacing-col-sm: 4px;
|
||||
$uni-spacing-col-base: 8px;
|
||||
$uni-spacing-col-lg: 12px;
|
||||
|
||||
/* 透明度 */
|
||||
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
|
||||
|
||||
/* 文章场景相关 */
|
||||
$uni-color-title: #2C405A; // 文章标题颜色
|
||||
$uni-font-size-title:20px;
|
||||
$uni-color-subtitle: #555555; // 二级标题颜色
|
||||
$uni-font-size-subtitle:26px;
|
||||
$uni-color-paragraph: #3F536E; // 文章段落颜色
|
||||
$uni-font-size-paragraph:15px;
|
||||
:root {
|
||||
--grad-primary-start: #05DCEF;
|
||||
--grad-primary-mid: #7DE2F5;
|
||||
--grad-primary-end: #B8F2FF;
|
||||
--grad-contrast-start: #00F5A0;
|
||||
--grad-contrast-end: #00D9F5;
|
||||
--bg-gray: #F5F5F5;
|
||||
--text-color: #333333;
|
||||
--text-light: #666666;
|
||||
}
|
||||
142
utils/dialog.js
Normal file
142
utils/dialog.js
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/**
|
||||
* 弹窗工具类 - 基于 Uniapp 原生 API 的简洁实现
|
||||
*
|
||||
* 使用系统原生弹窗 API,无需自定义组件
|
||||
* 提供统一的接口用于显示成功、警告、错误消息和 Toast
|
||||
*
|
||||
* @example
|
||||
* // 显示成功消息
|
||||
* DialogUtils.showSuccessMessage('成功', '数据已保存');
|
||||
*
|
||||
* // 显示确认对话框
|
||||
* uni.showModal({
|
||||
* title: '确认',
|
||||
* content: '确定删除吗?',
|
||||
* success: (res) => {
|
||||
* if (res.confirm) {
|
||||
* // 执行删除
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* // 显示 Toast 提示
|
||||
* DialogUtils.toast('保存中...', 'loading');
|
||||
*/
|
||||
|
||||
export const DialogUtils = {
|
||||
/**
|
||||
* 显示成功消息弹窗
|
||||
* @param {string} title - 弹窗标题
|
||||
* @param {string} message - 消息内容
|
||||
* @param {string} confirmText - 确认按钮文本
|
||||
*/
|
||||
showSuccessMessage(title = '成功', message = '', confirmText = '我知道了') {
|
||||
uni.showModal({
|
||||
title,
|
||||
content: message,
|
||||
showCancel: false,
|
||||
confirmText
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示警告消息弹窗
|
||||
* @param {string} title - 弹窗标题
|
||||
* @param {string} message - 消息内容
|
||||
* @param {string} confirmText - 确认按钮文本
|
||||
*/
|
||||
showWarningMessage(title = '提示', message = '', confirmText = '我知道了') {
|
||||
uni.showModal({
|
||||
title,
|
||||
content: message,
|
||||
showCancel: false,
|
||||
confirmText
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示错误消息弹窗
|
||||
* @param {string} title - 弹窗标题
|
||||
* @param {string} message - 消息内容
|
||||
* @param {string} confirmText - 确认按钮文本
|
||||
*/
|
||||
showErrorMessage(title = '错误', message = '', confirmText = '我知道了') {
|
||||
uni.showModal({
|
||||
title,
|
||||
content: message,
|
||||
showCancel: false,
|
||||
confirmText
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示确认对话框(带取消按钮)
|
||||
* @param {string} title - 弹窗标题
|
||||
* @param {string} message - 消息内容
|
||||
* @param {string} confirmText - 确认按钮文本
|
||||
* @param {string} cancelText - 取消按钮文本
|
||||
* @returns {Promise<boolean>} 用户是否点击了确认
|
||||
*
|
||||
* @example
|
||||
* const confirmed = await DialogUtils.showConfirm('确认删除', '确定要删除这条记录吗?');
|
||||
* if (confirmed) {
|
||||
* // 执行删除操作
|
||||
* }
|
||||
*/
|
||||
async showConfirm(
|
||||
title = '确认',
|
||||
message = '',
|
||||
confirmText = '确定',
|
||||
cancelText = '取消'
|
||||
) {
|
||||
return new Promise((resolve) => {
|
||||
uni.showModal({
|
||||
title,
|
||||
content: message,
|
||||
showCancel: true,
|
||||
confirmText,
|
||||
cancelText,
|
||||
success: (res) => {
|
||||
resolve(res.confirm);
|
||||
},
|
||||
fail: () => {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示 Toast 提示
|
||||
* @param {string} message - 提示消息
|
||||
* @param {string} icon - 图标类型 (success/error/loading/none)
|
||||
* @param {number} duration - 显示时长(毫秒),默认 2000
|
||||
*
|
||||
* @example
|
||||
* // 显示加载中提示
|
||||
* DialogUtils.toast('加载中...', 'loading');
|
||||
*
|
||||
* // 显示成功提示(2秒后自动隐藏)
|
||||
* DialogUtils.toast('保存成功', 'success');
|
||||
*/
|
||||
toast(message, icon = 'none', duration = 2000) {
|
||||
uni.showToast({
|
||||
title: message,
|
||||
icon,
|
||||
duration,
|
||||
mask: false
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 隐藏 Toast 提示
|
||||
*
|
||||
* @example
|
||||
* DialogUtils.toast('加载中...', 'loading', 0);
|
||||
* // ... 执行某些操作
|
||||
* DialogUtils.hideToast();
|
||||
*/
|
||||
hideToast() {
|
||||
uni.hideToast();
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user