完成APP盘点

master
lynn 7 months ago
parent b1cdd623ad
commit a07388aa0d

@ -2,6 +2,12 @@
export default {
onLaunch: function() {
console.log('App Launch')
const token = uni.getStorageSync('token');
if (!token) {
uni.reLaunch({ url: '/pages/login/login' });
} else {
uni.reLaunch({ url: '/pages/index/index' });
}
},
onShow: function() {
console.log('App Show')
@ -14,4 +20,4 @@
<style>
/*每个页面公共css */
</style>
</style>

105
api.js

@ -0,0 +1,105 @@
import { BASE_API } from './config'
// 登录接口
export function login(username, password) {
return new Promise((resolve, reject) => {
uni.request({
url: BASE_API + 'api/admin/auth/login',
method: 'POST',
data: {
username,
password
},
success: resolve,
fail: reject
})
})
}
// 获取用户信息接口
export function getUserInfo() {
const token = uni.getStorageSync('token')
return new Promise((resolve, reject) => {
uni.request({
url: BASE_API + 'api/admin/auth/me',
method: 'POST',
data: {
token
},
success: resolve,
fail: reject
})
})
}
// 退出登录接口
export function logoutApi() {
const token = uni.getStorageSync('token')
return new Promise((resolve, reject) => {
uni.request({
url: BASE_API + 'api/admin/auth/logout',
method: 'POST',
data: { token },
success: resolve,
fail: reject
})
})
}
// 获取物资详情接口
export function getMaterialInfo(id) {
const token = uni.getStorageSync('token')
return new Promise((resolve, reject) => {
uni.request({
url: BASE_API + 'api/admin/material-infos/show',
method: 'GET',
data: {
id,
token
},
success: resolve,
fail: reject
})
})
}
// 盘点保存接口
export function saveInventoryCheck(data) {
const token = uni.getStorageSync('token')
return new Promise((resolve, reject) => {
console.log("confirm接口")
uni.request({
url: BASE_API + 'api/admin/material-infos-plan-link/confirm',
method: 'POST',
data: {
...data,
token
},
success: resolve,
fail: reject
})
})
}
// 文件上传接口
export function uploadFile(filePath) {
const token = uni.getStorageSync('token')
return new Promise((resolve, reject) => {
uni.uploadFile({
url: BASE_API + 'api/admin/upload-file',
filePath,
name: 'file',
formData: { token },
success: (res) => {
// 假设后端返回 { code: 0, data: { id: 123, url: '...' } }
try {
const data = JSON.parse(res.data)
resolve(data)
} catch (e) {
reject(e)
}
},
fail: reject
})
})
}

@ -0,0 +1,7 @@
// 全局配置文件
export const BASE_API = 'http://192.168.60.99:8004/'
// 导出配置
export default {
BASE_API
}

@ -1,9 +1,11 @@
import App from './App'
import config from './config'
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
Vue.config.productionTip = false
Vue.prototype.$config = config
App.mpType = 'app'
const app = new Vue({
...App
@ -15,6 +17,7 @@ app.$mount()
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.config.globalProperties.$config = config
return {
app
}

@ -29,12 +29,6 @@
"style": {
"navigationBarTitleText": "物资盘点"
}
},
{
"path": "pages/detail/detail",
"style": {
"navigationBarTitleText": "物资详情"
}
}
],
"globalStyle": {

@ -1,149 +0,0 @@
<template>
<view class="detail-bg">
<view class="detail-card">
<view class="detail-list">
<view class="detail-item">
<text class="detail-label">物资名称</text>
<text class="detail-value">{{ info.name }}</text>
</view>
<view class="detail-item">
<text class="detail-label">物资编号</text>
<text class="detail-value">{{ info.code }}</text>
</view>
<view class="detail-item">
<text class="detail-label">库存数量</text>
<text class="detail-value stock">{{ info.stockQty }}</text>
</view>
<view class="detail-item">
<text class="detail-label">物资图片</text>
<image v-if="info.photo" :src="info.photo" class="detail-img" mode="aspectFill" />
<view v-else class="img-placeholder">无图片</view>
</view>
<view class="detail-item remark-item">
<text class="detail-label">备注</text>
<text class="detail-remark">{{ info.remark || '无' }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
info: {
name: '',
code: '',
stockQty: '',
photo: '',
remark: ''
}
}
},
onLoad(options) {
// options.code
this.info = {
name: '仓库A物资',
code: options.code || '未知',
stockQty: 100,
photo: '',
remark: '无特殊说明'
}
}
}
</script>
<style>
.detail-bg {
min-height: 100vh;
background: linear-gradient(180deg, #eaf1fb 0%, #f7fafd 100%);
display: flex;
align-items: center;
justify-content: center;
}
.detail-card {
width: 94vw;
max-width: 500px;
background: #fff;
border-radius: 28px;
box-shadow: 0 8px 32px rgba(64,158,255,0.10);
padding: 38px 18px 28px 18px;
display: flex;
flex-direction: column;
align-items: stretch;
}
.detail-title {
font-size: 26px;
font-weight: 800;
color: #409eff;
text-align: center;
margin-bottom: 32px;
letter-spacing: 2px;
}
.detail-list {
display: flex;
flex-direction: column;
gap: 0;
}
.detail-item {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
background: #f7fafd;
border-radius: 16px;
margin-bottom: 18px;
padding: 18px 16px;
box-shadow: 0 2px 8px rgba(64,158,255,0.04);
}
.detail-label {
color: #888;
font-size: 16px;
min-width: 80px;
margin-right: 10px;
}
.detail-value {
color: #222;
font-size: 16px;
font-weight: 600;
flex: 1;
text-align: right;
word-break: break-all;
}
.detail-value.stock {
color: #409eff;
font-weight: 700;
}
.detail-img {
width: 90px;
height: 90px;
border-radius: 10px;
margin-left: 10px;
background: #f0f1f3;
border: 1px solid #e3e8f0;
}
.img-placeholder {
width: 90px;
height: 90px;
border-radius: 10px;
background: #f0f1f3;
color: #bbb;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
margin-left: 10px;
}
.remark-item {
align-items: flex-start;
}
.detail-remark {
color: #666;
font-size: 15px;
flex: 1;
text-align: right;
line-height: 1.7;
word-break: break-all;
}
</style>

@ -5,15 +5,15 @@
<button class="main-btn" @click="scanInventory"></button>
<button class="main-btn outline" @click="scanView"></button>
</view>
<view class="recent-section">
<view class="recent-title">最近盘点记录</view>
<view class="recent-list">
<view class="recent-item" v-for="(item, idx) in recentRecords" :key="idx">
<view class="recent-info">
<text class="recent-name">{{item.title}}</text>
<text class="recent-time">{{item.time}}</text>
<view class="task-section">
<view class="task-title">盘点任务列表</view>
<view class="task-list">
<view class="task-item" v-for="(item, idx) in taskList" :key="idx" @click="goTaskDetail(item)">
<view class="task-info">
<text class="task-name">{{item.title}}</text>
<text class="task-time">{{item.time}}</text>
</view>
<text class="recent-status" :class="item.status">{{item.statusText}}</text>
<text class="task-status" :class="item.status">{{item.statusText}}</text>
</view>
</view>
</view>
@ -26,10 +26,25 @@
data() {
return {
currentDate: '',
recentRecords: [
{ title: '仓库A盘点', time: '2024-03-20 14:30', status: 'completed', statusText: '已完成' },
{ title: '办公用品盘点', time: '2024-03-19 10:15', status: 'processing', statusText: '进行中' },
{ title: '设备资产盘点', time: '2024-03-18 16:45', status: 'completed', statusText: '已完成' }
taskList: [
{
title: '仓库A盘点',
time: '2024-06-01 10:00',
status: 'pending',
statusText: '待完成'
},
{
title: '仓库B盘点',
time: '2024-05-28 14:30',
status: 'completed',
statusText: '已完成'
},
{
title: '仓库C盘点',
time: '2024-05-25 09:15',
status: 'in-progress',
statusText: '进行中'
}
]
}
},
@ -47,8 +62,21 @@
scanInventory() {
uni.scanCode({
success: (res) => {
console.log("url:", res.result);
let url = res.result;
let id = '';
// id=xxx
const match = url.match(/[?&]id=([^&]+)/);
if (match) {
id = match[1];
}
console.log("id:", id);
if (!id) {
uni.showToast({ title: '二维码无效', icon: 'none' });
return;
}
uni.navigateTo({
url: `/pages/inventory/inventory?code=${encodeURIComponent(res.result)}`
url: `/pages/inventory/inventory?code=${encodeURIComponent(id)}`
});
},
fail: () => {
@ -59,14 +87,29 @@
scanView() {
uni.scanCode({
success: (res) => {
let url = res.result;
let id = '';
const match = url.match(/[?&]id=([^&]+)/);
if (match) {
id = match[1];
}
if (!id) {
uni.showToast({ title: '二维码无效', icon: 'none' });
return;
}
uni.navigateTo({
url: `/pages/detail/detail?code=${encodeURIComponent(res.result)}`
url: `/pages/inventory/inventory?code=${encodeURIComponent(id)}&view=1`
});
},
fail: () => {
uni.showToast({ title: '扫码失败', icon: 'none' });
}
});
},
goTaskDetail(item) {
uni.navigateTo({
url: `/pages/task-detail/task-detail?title=${encodeURIComponent(item.title)}&time=${encodeURIComponent(item.time)}&status=${item.status}&statusText=${encodeURIComponent(item.statusText)}`
});
}
}
}
@ -142,7 +185,7 @@
background: #f0f7ff;
}
.recent-section {
.task-section {
width: 92%;
max-width: 520px;
margin: 0 auto;
@ -152,20 +195,20 @@
padding: 28rpx 20rpx 18rpx 20rpx;
}
.recent-title {
.task-title {
font-size: 28rpx;
color: #222;
font-weight: 700;
margin-bottom: 18rpx;
}
.recent-list {
.task-list {
display: flex;
flex-direction: column;
gap: 14rpx;
}
.recent-item {
.task-item {
display: flex;
justify-content: space-between;
align-items: center;
@ -173,40 +216,45 @@
border-bottom: 1px solid #f0f0f0;
}
.recent-item:last-child {
.task-item:last-child {
border-bottom: none;
}
.recent-info {
.task-info {
display: flex;
flex-direction: column;
}
.recent-name {
.task-name {
font-size: 24rpx;
color: #333;
font-weight: 600;
}
.recent-time {
.task-time {
font-size: 20rpx;
color: #999;
margin-top: 2rpx;
}
.recent-status {
.task-status {
font-size: 20rpx;
padding: 4rpx 14rpx;
border-radius: 16rpx;
}
.recent-status.completed {
.task-status.pending {
background-color: #fff3e0;
color: #ff9800;
}
.task-status.completed {
background-color: #e8f5e9;
color: #4caf50;
}
.recent-status.processing {
background-color: #fff3e0;
color: #ff9800;
.task-status.in-progress {
background-color: #e8f5e9;
color: #4caf50;
}
</style>

@ -1,61 +1,92 @@
<template>
<view class="inventory-bg">
<view class="inventory-card">
<view class="form-group">
<text class="form-label">盘点人</text>
<input class="form-input" :value="userName" disabled />
</view>
<view class="form-group">
<text class="form-label">盘点日期</text>
<input class="form-input" :value="date" disabled />
</view>
<view class="form-group">
<text class="form-label">库存数量</text>
<input class="form-input" :value="stockQty" disabled />
<view class="readonly-group">
<view class="readonly-item">
<text class="readonly-label">物资名称</text>
<text class="readonly-value">{{ materialName }}</text>
</view>
<view class="readonly-item">
<text class="readonly-label">物质代码</text>
<text class="readonly-value">{{ materialCode }}</text>
</view>
<view class="readonly-item">
<text class="readonly-label">规格型号</text>
<text class="readonly-value">{{ materialSpec }}</text>
</view>
<view class="readonly-item">
<text class="readonly-label">库存数量</text>
<text class="readonly-value">{{ stockQty }}{{ unit ? ' ' + unit : '' }}</text>
</view>
</view>
<view class="form-group">
<view class="form-group" v-if="!isViewMode">
<text class="form-label">盘点数量</text>
<input class="form-input" type="number" v-model="countQty" placeholder="请输入盘点数量" />
</view>
<view class="form-group">
<view class="form-group" v-if="!isViewMode">
<text class="form-label">盘点备注</text>
<textarea class="form-textarea" v-model="remark" placeholder="请输入备注" />
<textarea class="form-textarea" v-model="remark" placeholder="请输入备注信息" />
</view>
<view class="form-group">
<view class="form-group" v-if="!isViewMode">
<text class="form-label">照片上传</text>
<view class="photo-upload">
<view v-if="photo" class="photo-preview">
<view v-for="(photo, index) in photos" :key="index" class="photo-preview">
<image :src="photo" mode="aspectFill" class="photo-img" />
<text class="photo-del" @click="photo=''"></text>
<view class="photo-del" @click="deletePhoto(index)">
<text class="delete-icon">×</text>
</view>
</view>
<button v-else class="photo-btn" @click="choosePhoto"></button>
<button v-if="photos.length < 3" class="photo-btn" @click="choosePhoto">
<text class="iconfont icon-camera"></text>
<text class="btn-text">上传照片</text>
</button>
</view>
</view>
<button class="submit-btn" @click="submit"></button>
<button class="submit-btn" v-if="!isViewMode" @click="submit"></button>
</view>
</view>
</template>
<script>
import { getMaterialInfo, saveInventoryCheck, uploadFile } from '@/api.js'
export default {
data() {
return {
userName: '张三',
date: '',
stockQty: 100, //
isViewMode: false,
stockQty: '',
countQty: '',
remark: '',
photo: ''
photo: '',
photos: [],
materialName: '',
materialSpec: '',
unit: '',
materialCode: '',
material_infos_plan_id: '',
materialId: ''
}
},
onLoad(options) {
this.isViewMode = options.view === '1';
this.date = this.getToday();
// options.code
//
if (options.code) {
//
// code
// this.stockQty = ...
this.materialId = options.code;
console.log("materialId:", this.materialId);
if (this.materialId) {
getMaterialInfo(this.materialId).then(response => {
console.log("response:", response);
if (response.data) {
this.materialName = response.data.zichanmingcheng || '-'
this.materialSpec = response.data.guigexinghao || '-'
this.unit = response.data.jiliangdanwei || '-'
this.materialCode = response.data.wuzibianma || '-'
this.material_infos_plan_id = response.data.material_infos_plan_id || ''
this.stockQty = response.data.inventorys_total || '0'
} else {
uni.showToast({ title: '未获取到物资信息', icon: 'none' })
}
}).catch(() => {
uni.showToast({ title: '获取物资信息失败', icon: 'none' })
})
}
},
methods: {
@ -67,20 +98,71 @@ export default {
return `${y}-${m}-${d}`;
},
choosePhoto() {
if (this.photos.length >= 3) {
uni.showToast({ title: '最多上传3张照片', icon: 'none' });
return;
}
uni.chooseImage({
count: 1,
count: 3 - this.photos.length,
success: (res) => {
this.photo = res.tempFilePaths[0];
this.photos = [...this.photos, ...res.tempFilePaths];
}
});
},
submit() {
deletePhoto(index) {
this.photos.splice(index, 1);
},
async submit() {
if (!this.countQty) {
uni.showToast({ title: '请输入盘点数量', icon: 'none' });
return;
}
uni.showToast({ title: '盘点提交成功', icon: 'success' });
//
if (!/^(0|[1-9][0-9]*)$/.test(this.countQty)) {
uni.showToast({ title: '盘点数量必须为0或正整数', icon: 'none' });
return;
}
uni.showLoading({ title: '提交中...' });
// 1. file_id
let file_ids = [];
for (let i = 0; i < this.photos.length; i++) {
try {
const res = await uploadFile(this.photos[i]);
if (res && res.id) {
file_ids.push(res.id);
}
} catch (e) {
uni.hideLoading();
uni.showToast({ title: '图片上传失败', icon: 'none' });
return;
}
}
// 2.
const data = {
status: '1',
material_info_id: this.materialId, // id
check_num: this.countQty,
remark: this.remark,
file_ids
// material_infos_plan_idstatuscheck_date
};
console.log("data:", data);
// 3.
saveInventoryCheck(data).then(res => {
console.log("res:", res);
uni.hideLoading();
if (res && (!res.data || res.data.errcode === undefined)) {
uni.showToast({ title: '盘点提交成功', icon: 'success' });
setTimeout(() => {
uni.reLaunch({ url: '/pages/index/index' });
}, 1200);
} else {
uni.showToast({ title: res.data.errmsg || '提交失败', icon: 'none' });
}
}).catch(() => {
uni.hideLoading();
uni.showToast({ title: '提交失败', icon: 'none' });
});
}
}
}
@ -89,97 +171,153 @@ export default {
<style>
.inventory-bg {
min-height: 100vh;
background: linear-gradient(180deg, #eaf1fb 0%, #f7fafd 100%);
display: flex;
align-items: center;
justify-content: center;
background: #f5f6f7;
padding: 24rpx;
}
.inventory-card {
width: 92vw;
max-width: 480px;
background: #fff;
border-radius: 24px;
box-shadow: 0 8px 32px rgba(64,158,255,0.10);
padding: 48px 24px 32px 24px;
border-radius: 24rpx;
padding: 32rpx 24rpx;
margin-bottom: 24rpx;
}
.readonly-group {
margin-bottom: 32rpx;
padding: 24rpx;
background: #f8f9fa;
border-radius: 16rpx;
}
.readonly-item {
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: space-between;
margin-bottom: 20rpx;
}
.readonly-item:last-child {
margin-bottom: 0;
}
.readonly-label {
color: #666;
font-size: 28rpx;
}
.readonly-value {
color: #333;
font-size: 28rpx;
font-weight: 500;
}
.form-group {
margin-bottom: 28px;
display: flex;
flex-direction: column;
margin-bottom: 32rpx;
}
.form-label {
font-size: 16px;
color: #3a3a3a;
margin-bottom: 10px;
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
font-weight: 500;
}
.form-input {
height: 48px;
border: 1.5px solid #e3e8f0;
border-radius: 12px;
padding: 0 14px;
font-size: 17px;
background: #f6f8fa;
color: #222;
margin-bottom: 2px;
}
.form-input[disabled] {
background: #f0f1f3;
color: #aaa;
height: 88rpx;
background: #f8f9fa;
border: none;
border-radius: 16rpx;
padding: 0 24rpx;
font-size: 28rpx;
color: #333;
}
.form-textarea {
min-height: 80px;
border: 1.5px solid #e3e8f0;
border-radius: 12px;
padding: 10px 14px;
font-size: 16px;
background: #f6f8fa;
color: #222;
min-height: 160rpx;
background: #f8f9fa;
border: none;
border-radius: 16rpx;
padding: 20rpx 24rpx;
font-size: 28rpx;
color: #333;
}
.photo-upload {
display: flex;
align-items: center;
}
.photo-btn {
height: 44px;
background: #409eff;
color: #fff;
font-size: 16px;
border-radius: 8px;
padding: 0 24px;
border: none;
flex-wrap: wrap;
gap: 20rpx;
}
.photo-preview {
position: relative;
width: 160rpx;
height: 160rpx;
}
.photo-btn {
width: 160rpx;
height: 160rpx;
background: #f8f9fa;
border-radius: 16rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0;
}
.photo-btn .iconfont {
font-size: 48rpx;
color: #666;
margin-bottom: 8rpx;
}
.btn-text {
font-size: 24rpx;
color: #666;
}
.photo-preview {
position: relative;
}
.photo-img {
width: 80px;
height: 80px;
border-radius: 8px;
margin-right: 12px;
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
}
.photo-del {
position: absolute;
top: -16rpx;
right: -16rpx;
width: 40rpx;
height: 40rpx;
background: rgba(0,0,0,0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.delete-icon {
color: #ff4d4f;
font-size: 14px;
cursor: pointer;
font-size: 32rpx;
font-weight: bold;
line-height: 1;
}
.submit-btn {
margin-top: 18px;
height: 52px;
background: linear-gradient(90deg, #409eff 0%, #3b7cff 100%);
width: 100%;
height: 88rpx;
background: #409eff;
color: #fff;
font-size: 20px;
font-weight: 700;
border-radius: 14px;
letter-spacing: 8px;
box-shadow: 0 4px 16px rgba(64,158,255,0.13);
font-size: 32rpx;
font-weight: 500;
border-radius: 44rpx;
margin-top: 48rpx;
}
.submit-btn:active {
background: linear-gradient(90deg, #337ecc 60%, #2a5db0 100%);
opacity: 0.9;
}
</style>
</style>

@ -17,6 +17,7 @@
</template>
<script>
import { login } from '@/api.js'
export default {
data() {
return {
@ -33,16 +34,37 @@ export default {
});
return;
}
uni.showToast({
title: '登录成功',
icon: 'success',
duration: 1500
});
setTimeout(() => {
uni.switchTab({
url: '/pages/index/index'
uni.showLoading({ title: '登录中...', mask: true });
login(this.username, this.password)
.then(response => {
uni.hideLoading();
console.log(response)
if (response.data && response.data.errcode !== undefined) {
uni.showToast({
title: response.data.errmsg || '登录失败',
icon: 'none'
});
} else if (response.data) {
console.log(response.data.access_token)
if (response.data.access_token) {
uni.setStorageSync('token', response.data.access_token)
}
uni.showToast({
title: '登录成功',
icon: 'success',
duration: 1500
});
setTimeout(() => {
uni.switchTab({
url: '/pages/index/index'
});
}, 1500);
}
})
.catch(() => {
uni.hideLoading();
uni.showToast({ title: '网络错误', icon: 'none' });
});
}, 1500);
}
}
}

@ -1,17 +1,20 @@
<template>
<view class="profile-container">
<view class="avatar-section">
<image class="avatar" src="/static/avatar.png" mode="aspectFill"></image>
<text class="username">管理员</text>
<image class="avatar" :src="userInfo.avatar || '/static/profile/avatar.png'" mode="aspectFill"></image>
</view>
<view class="info-section">
<view class="info-item">
<text class="label">账号</text>
<text class="value">admin</text>
<text class="label">用户名</text>
<text class="value">{{ userInfo.username || '-' }}</text>
</view>
<view class="info-item">
<text class="label">角色</text>
<text class="value">系统管理员</text>
<text class="label">姓名</text>
<text class="value">{{ userInfo.name || '-' }}</text>
</view>
<view class="info-item">
<text class="label">部门</text>
<text class="value">{{ (userInfo.department && userInfo.department.name) || '-' }}</text>
</view>
</view>
<button class="logout-btn" @click="logout">退</button>
@ -19,9 +22,32 @@
</template>
<script>
import { getUserInfo } from '@/api.js'
export default {
data() {
return {
userInfo: {}
}
},
onShow() {
getUserInfo().then(response => {
if (response.data) {
console.log("返回数据", response.data)
this.userInfo = response.data
} else {
uni.showToast({
title: response.data.errmsg || '获取信息失败',
icon: 'none'
})
}
}).catch(() => {
uni.showToast({ title: '网络错误', icon: 'none' })
})
},
methods: {
logout() {
uni.removeStorageSync('token');
uni.reLaunch({ url: '/pages/login/login' });
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

@ -1,8 +1,8 @@
{
"hash": "eb1a7fc1",
"hash": "da7460ea",
"configHash": "4bf35047",
"lockfileHash": "2865f1ca",
"browserHash": "153c7f83",
"lockfileHash": "515ad130",
"browserHash": "6a07c7fb",
"optimized": {},
"chunks": {}
}

@ -2,7 +2,7 @@
;(function(){
let u=void 0,isReady=false,onReadyCallbacks=[],isServiceReady=false,onServiceReadyCallbacks=[];
const __uniConfig = {"pages":[],"globalStyle":{"backgroundColor":"#F8F8F8","navigationBar":{"backgroundColor":"#F8F8F8","titleText":"物资盘点","type":"default","titleColor":"#000000"},"isNVue":false},"nvue":{"compiler":"uni-app","styleCompiler":"uni-app","flex-direction":"column"},"renderer":"auto","appname":"stocktaking","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":true},"compilerVersion":"4.36","entryPagePath":"pages/index/index","entryPageQuery":"","realEntryPagePath":"","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000},"tabBar":{"position":"bottom","color":"#999999","selectedColor":"#007AFF","borderStyle":"black","blurEffect":"none","fontSize":"10px","iconWidth":"24px","spacing":"3px","height":"50px","list":[{"pagePath":"pages/index/index","text":"盘点","iconPath":"/static/tabbar/inventory.png","selectedIconPath":"/static/tabbar/inventory-active.png"},{"pagePath":"pages/profile/profile","text":"个人中心","iconPath":"/static/tabbar/profile.png","selectedIconPath":"/static/tabbar/profile-active.png"}],"backgroundColor":"#ffffff","selectedIndex":0,"shown":true},"locales":{},"darkmode":false,"themeConfig":{}};
const __uniRoutes = [{"path":"pages/index/index","meta":{"isQuit":true,"isEntry":true,"isTabBar":true,"tabBarIndex":0,"navigationBar":{"titleText":"物资盘点","type":"default"},"isNVue":false}},{"path":"pages/profile/profile","meta":{"isQuit":true,"isTabBar":true,"tabBarIndex":1,"navigationBar":{"titleText":"个人中心","type":"default"},"isNVue":false}},{"path":"pages/login/login","meta":{"navigationBar":{"titleText":"登录","type":"default"},"isNVue":false}},{"path":"pages/scan/scan","meta":{"navigationBar":{"titleText":"扫码盘点","type":"default"},"isNVue":false}},{"path":"pages/inventory/inventory","meta":{"navigationBar":{"titleText":"物资盘点","type":"default"},"isNVue":false}},{"path":"pages/detail/detail","meta":{"navigationBar":{"titleText":"物资详情","type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute));
const __uniRoutes = [{"path":"pages/index/index","meta":{"isQuit":true,"isEntry":true,"isTabBar":true,"tabBarIndex":0,"navigationBar":{"titleText":"物资盘点","type":"default"},"isNVue":false}},{"path":"pages/profile/profile","meta":{"isQuit":true,"isTabBar":true,"tabBarIndex":1,"navigationBar":{"titleText":"个人中心","type":"default"},"isNVue":false}},{"path":"pages/login/login","meta":{"navigationBar":{"titleText":"登录","type":"default"},"isNVue":false}},{"path":"pages/scan/scan","meta":{"navigationBar":{"titleText":"扫码盘点","type":"default"},"isNVue":false}},{"path":"pages/inventory/inventory","meta":{"navigationBar":{"titleText":"物资盘点","type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute));
__uniConfig.styles=[];//styles
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});

@ -31,6 +31,13 @@ if (uni.restoreGlobal) {
}
(function(vue) {
"use strict";
function formatAppLog(type, filename, ...args) {
if (uni.__log__) {
uni.__log__(type, filename, ...args);
} else {
console[type].apply(console, [...args, filename]);
}
}
const _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
@ -38,14 +45,29 @@ if (uni.restoreGlobal) {
}
return target;
};
const _sfc_main$6 = {
const _sfc_main$5 = {
data() {
return {
currentDate: "",
recentRecords: [
{ title: "仓库A盘点", time: "2024-03-20 14:30", status: "completed", statusText: "已完成" },
{ title: "办公用品盘点", time: "2024-03-19 10:15", status: "processing", statusText: "进行中" },
{ title: "设备资产盘点", time: "2024-03-18 16:45", status: "completed", statusText: "已完成" }
taskList: [
{
title: "仓库A盘点",
time: "2024-06-01 10:00",
status: "pending",
statusText: "待完成"
},
{
title: "仓库B盘点",
time: "2024-05-28 14:30",
status: "completed",
statusText: "已完成"
},
{
title: "仓库C盘点",
time: "2024-05-25 09:15",
status: "in-progress",
statusText: "进行中"
}
]
};
},
@ -63,8 +85,20 @@ if (uni.restoreGlobal) {
scanInventory() {
uni.scanCode({
success: (res) => {
formatAppLog("log", "at pages/index/index.vue:65", "url:", res.result);
let url = res.result;
let id = "";
const match = url.match(/[?&]id=([^&]+)/);
if (match) {
id = match[1];
}
formatAppLog("log", "at pages/index/index.vue:73", "id:", id);
if (!id) {
uni.showToast({ title: "二维码无效", icon: "none" });
return;
}
uni.navigateTo({
url: `/pages/inventory/inventory?code=${encodeURIComponent(res.result)}`
url: `/pages/inventory/inventory?code=${encodeURIComponent(id)}`
});
},
fail: () => {
@ -75,18 +109,33 @@ if (uni.restoreGlobal) {
scanView() {
uni.scanCode({
success: (res) => {
let url = res.result;
let id = "";
const match = url.match(/[?&]id=([^&]+)/);
if (match) {
id = match[1];
}
if (!id) {
uni.showToast({ title: "二维码无效", icon: "none" });
return;
}
uni.navigateTo({
url: `/pages/detail/detail?code=${encodeURIComponent(res.result)}`
url: `/pages/inventory/inventory?code=${encodeURIComponent(id)}&view=1`
});
},
fail: () => {
uni.showToast({ title: "扫码失败", icon: "none" });
}
});
},
goTaskDetail(item) {
uni.navigateTo({
url: `/pages/task-detail/task-detail?title=${encodeURIComponent(item.title)}&time=${encodeURIComponent(item.time)}&status=${item.status}&statusText=${encodeURIComponent(item.statusText)}`
});
}
}
};
function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock("view", { class: "index-bg" }, [
vue.createElementVNode("view", { class: "index-content" }, [
vue.createElementVNode("view", { class: "btn-group" }, [
@ -99,28 +148,29 @@ if (uni.restoreGlobal) {
onClick: _cache[1] || (_cache[1] = (...args) => $options.scanView && $options.scanView(...args))
}, "扫码查看")
]),
vue.createElementVNode("view", { class: "recent-section" }, [
vue.createElementVNode("view", { class: "recent-title" }, "最近盘点记录"),
vue.createElementVNode("view", { class: "recent-list" }, [
vue.createElementVNode("view", { class: "task-section" }, [
vue.createElementVNode("view", { class: "task-title" }, "盘点任务列表"),
vue.createElementVNode("view", { class: "task-list" }, [
(vue.openBlock(true), vue.createElementBlock(
vue.Fragment,
null,
vue.renderList($data.recentRecords, (item, idx) => {
vue.renderList($data.taskList, (item, idx) => {
return vue.openBlock(), vue.createElementBlock("view", {
class: "recent-item",
key: idx
class: "task-item",
key: idx,
onClick: ($event) => $options.goTaskDetail(item)
}, [
vue.createElementVNode("view", { class: "recent-info" }, [
vue.createElementVNode("view", { class: "task-info" }, [
vue.createElementVNode(
"text",
{ class: "recent-name" },
{ class: "task-name" },
vue.toDisplayString(item.title),
1
/* TEXT */
),
vue.createElementVNode(
"text",
{ class: "recent-time" },
{ class: "task-time" },
vue.toDisplayString(item.time),
1
/* TEXT */
@ -129,13 +179,13 @@ if (uni.restoreGlobal) {
vue.createElementVNode(
"text",
{
class: vue.normalizeClass(["recent-status", item.status])
class: vue.normalizeClass(["task-status", item.status])
},
vue.toDisplayString(item.statusText),
3
/* TEXT, CLASS */
)
]);
], 8, ["onClick"]);
}),
128
/* KEYED_FRAGMENT */
@ -145,33 +195,157 @@ if (uni.restoreGlobal) {
])
]);
}
const PagesIndexIndex = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["render", _sfc_render$5], ["__file", "D:/Lynn/Langye/stocktaking/pages/index/index.vue"]]);
const _imports_0 = "/static/avatar.png";
const _sfc_main$5 = {
const PagesIndexIndex = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["render", _sfc_render$4], ["__file", "D:/Lynn/Langye/stocktaking/pages/index/index.vue"]]);
const BASE_API = "http://192.168.60.99:8004/";
const config = {
BASE_API
};
function login(username, password) {
return new Promise((resolve, reject) => {
uni.request({
url: BASE_API + "api/admin/auth/login",
method: "POST",
data: {
username,
password
},
success: resolve,
fail: reject
});
});
}
function getUserInfo() {
const token = uni.getStorageSync("token");
return new Promise((resolve, reject) => {
uni.request({
url: BASE_API + "api/admin/auth/me",
method: "POST",
data: {
token
},
success: resolve,
fail: reject
});
});
}
function getMaterialInfo(id) {
const token = uni.getStorageSync("token");
return new Promise((resolve, reject) => {
uni.request({
url: BASE_API + "api/admin/material-infos/show",
method: "GET",
data: {
id,
token
},
success: resolve,
fail: reject
});
});
}
function saveInventoryCheck(data) {
const token = uni.getStorageSync("token");
return new Promise((resolve, reject) => {
formatAppLog("log", "at api.js:70", "confirm接口");
uni.request({
url: BASE_API + "api/admin/material-infos-plan-link/confirm",
method: "POST",
data: {
...data,
token
},
success: resolve,
fail: reject
});
});
}
function uploadFile(filePath) {
const token = uni.getStorageSync("token");
return new Promise((resolve, reject) => {
uni.uploadFile({
url: BASE_API + "api/admin/upload-file",
filePath,
name: "file",
formData: { token },
success: (res) => {
try {
const data = JSON.parse(res.data);
resolve(data);
} catch (e) {
reject(e);
}
},
fail: reject
});
});
}
const _sfc_main$4 = {
data() {
return {
userInfo: {}
};
},
onShow() {
getUserInfo().then((response) => {
if (response.data) {
formatAppLog("log", "at pages/profile/profile.vue:36", "返回数据", response.data);
this.userInfo = response.data;
} else {
uni.showToast({
title: response.data.errmsg || "获取信息失败",
icon: "none"
});
}
}).catch(() => {
uni.showToast({ title: "网络错误", icon: "none" });
});
},
methods: {
logout() {
uni.removeStorageSync("token");
uni.reLaunch({ url: "/pages/login/login" });
}
}
};
function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock("view", { class: "profile-container" }, [
vue.createElementVNode("view", { class: "avatar-section" }, [
vue.createElementVNode("image", {
class: "avatar",
src: _imports_0,
src: $data.userInfo.avatar || "/static/profile/avatar.png",
mode: "aspectFill"
}),
vue.createElementVNode("text", { class: "username" }, "管理员")
}, null, 8, ["src"])
]),
vue.createElementVNode("view", { class: "info-section" }, [
vue.createElementVNode("view", { class: "info-item" }, [
vue.createElementVNode("text", { class: "label" }, "账号:"),
vue.createElementVNode("text", { class: "value" }, "admin")
vue.createElementVNode("text", { class: "label" }, "用户名:"),
vue.createElementVNode(
"text",
{ class: "value" },
vue.toDisplayString($data.userInfo.username || "-"),
1
/* TEXT */
)
]),
vue.createElementVNode("view", { class: "info-item" }, [
vue.createElementVNode("text", { class: "label" }, "姓名:"),
vue.createElementVNode(
"text",
{ class: "value" },
vue.toDisplayString($data.userInfo.name || "-"),
1
/* TEXT */
)
]),
vue.createElementVNode("view", { class: "info-item" }, [
vue.createElementVNode("text", { class: "label" }, "角色:"),
vue.createElementVNode("text", { class: "value" }, "系统管理员")
vue.createElementVNode("text", { class: "label" }, "部门:"),
vue.createElementVNode(
"text",
{ class: "value" },
vue.toDisplayString($data.userInfo.department && $data.userInfo.department.name || "-"),
1
/* TEXT */
)
])
]),
vue.createElementVNode("button", {
@ -180,8 +354,8 @@ if (uni.restoreGlobal) {
}, "退出登录")
]);
}
const PagesProfileProfile = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["render", _sfc_render$4], ["__file", "D:/Lynn/Langye/stocktaking/pages/profile/profile.vue"]]);
const _sfc_main$4 = {
const PagesProfileProfile = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$3], ["__file", "D:/Lynn/Langye/stocktaking/pages/profile/profile.vue"]]);
const _sfc_main$3 = {
data() {
return {
username: "",
@ -197,20 +371,39 @@ if (uni.restoreGlobal) {
});
return;
}
uni.showToast({
title: "登录成功",
icon: "success",
duration: 1500
uni.showLoading({ title: "登录中...", mask: true });
login(this.username, this.password).then((response) => {
uni.hideLoading();
formatAppLog("log", "at pages/login/login.vue:41", response);
if (response.data && response.data.errcode !== void 0) {
uni.showToast({
title: response.data.errmsg || "登录失败",
icon: "none"
});
} else if (response.data) {
formatAppLog("log", "at pages/login/login.vue:48", response.data.access_token);
if (response.data.access_token) {
uni.setStorageSync("token", response.data.access_token);
}
uni.showToast({
title: "登录成功",
icon: "success",
duration: 1500
});
setTimeout(() => {
uni.switchTab({
url: "/pages/index/index"
});
}, 1500);
}
}).catch(() => {
uni.hideLoading();
uni.showToast({ title: "网络错误", icon: "none" });
});
setTimeout(() => {
uni.switchTab({
url: "/pages/index/index"
});
}, 1500);
}
}
};
function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock("view", { class: "login-bg" }, [
vue.createElementVNode("view", { class: "login-card" }, [
vue.createElementVNode("view", { class: "login-title" }, "欢迎登录"),
@ -256,8 +449,8 @@ if (uni.restoreGlobal) {
])
]);
}
const PagesLoginLogin = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$3], ["__file", "D:/Lynn/Langye/stocktaking/pages/login/login.vue"]]);
const _sfc_main$3 = {
const PagesLoginLogin = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$2], ["__file", "D:/Lynn/Langye/stocktaking/pages/login/login.vue"]]);
const _sfc_main$2 = {
data() {
return {
result: ""
@ -276,7 +469,7 @@ if (uni.restoreGlobal) {
}
}
};
function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock("view", { class: "scan-bg" }, [
vue.createElementVNode("view", { class: "scan-title" }, "扫码盘点"),
vue.createElementVNode("button", {
@ -298,23 +491,46 @@ if (uni.restoreGlobal) {
])) : vue.createCommentVNode("v-if", true)
]);
}
const PagesScanScan = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$2], ["__file", "D:/Lynn/Langye/stocktaking/pages/scan/scan.vue"]]);
const _sfc_main$2 = {
const PagesScanScan = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1], ["__file", "D:/Lynn/Langye/stocktaking/pages/scan/scan.vue"]]);
const _sfc_main$1 = {
data() {
return {
userName: "张三",
date: "",
stockQty: 100,
// 假数据,实际应根据扫码结果获取
isViewMode: false,
stockQty: "",
countQty: "",
remark: "",
photo: ""
photo: "",
photos: [],
materialName: "",
materialSpec: "",
unit: "",
materialCode: "",
material_infos_plan_id: "",
materialId: ""
};
},
onLoad(options) {
this.isViewMode = options.view === "1";
this.date = this.getToday();
if (options.code)
;
this.materialId = options.code;
formatAppLog("log", "at pages/inventory/inventory.vue:73", "materialId:", this.materialId);
if (this.materialId) {
getMaterialInfo(this.materialId).then((response) => {
formatAppLog("log", "at pages/inventory/inventory.vue:76", "response:", response);
if (response.data) {
this.materialName = response.data.zichanmingcheng || "-";
this.materialSpec = response.data.guigexinghao || "-";
this.unit = response.data.jiliangdanwei || "-";
this.materialCode = response.data.wuzibianma || "-";
this.material_infos_plan_id = response.data.material_infos_plan_id || "";
this.stockQty = response.data.inventorys_total || "0";
} else {
uni.showToast({ title: "未获取到物资信息", icon: "none" });
}
}).catch(() => {
uni.showToast({ title: "获取物资信息失败", icon: "none" });
});
}
},
methods: {
getToday() {
@ -325,50 +541,120 @@ if (uni.restoreGlobal) {
return `${y}-${m}-${d}`;
},
choosePhoto() {
if (this.photos.length >= 3) {
uni.showToast({ title: "最多上传3张照片", icon: "none" });
return;
}
uni.chooseImage({
count: 1,
count: 3 - this.photos.length,
success: (res) => {
this.photo = res.tempFilePaths[0];
this.photos = [...this.photos, ...res.tempFilePaths];
}
});
},
submit() {
deletePhoto(index) {
this.photos.splice(index, 1);
},
async submit() {
if (!this.countQty) {
uni.showToast({ title: "请输入盘点数量", icon: "none" });
return;
}
uni.showToast({ title: "盘点提交成功", icon: "success" });
if (!/^(0|[1-9][0-9]*)$/.test(this.countQty)) {
uni.showToast({ title: "盘点数量必须为0或正整数", icon: "none" });
return;
}
uni.showLoading({ title: "提交中..." });
let file_ids = [];
for (let i = 0; i < this.photos.length; i++) {
try {
const res = await uploadFile(this.photos[i]);
if (res && res.id) {
file_ids.push(res.id);
}
} catch (e) {
uni.hideLoading();
uni.showToast({ title: "图片上传失败", icon: "none" });
return;
}
}
const data = {
status: "1",
material_info_id: this.materialId,
// 或实际物资id字段
check_num: this.countQty,
remark: this.remark,
file_ids
// 其他参数如 material_infos_plan_id、status、check_date 可按需补充
};
formatAppLog("log", "at pages/inventory/inventory.vue:149", "data:", data);
saveInventoryCheck(data).then((res) => {
formatAppLog("log", "at pages/inventory/inventory.vue:152", "res:", res);
uni.hideLoading();
if (res && (!res.data || res.data.errcode === void 0)) {
uni.showToast({ title: "盘点提交成功", icon: "success" });
setTimeout(() => {
uni.reLaunch({ url: "/pages/index/index" });
}, 1200);
} else {
uni.showToast({ title: res.data.errmsg || "提交失败", icon: "none" });
}
}).catch(() => {
uni.hideLoading();
uni.showToast({ title: "提交失败", icon: "none" });
});
}
}
};
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock("view", { class: "inventory-bg" }, [
vue.createElementVNode("view", { class: "inventory-card" }, [
vue.createElementVNode("view", { class: "form-group" }, [
vue.createElementVNode("text", { class: "form-label" }, "盘点人"),
vue.createElementVNode("input", {
class: "form-input",
value: $data.userName,
disabled: ""
}, null, 8, ["value"])
]),
vue.createElementVNode("view", { class: "form-group" }, [
vue.createElementVNode("text", { class: "form-label" }, "盘点日期"),
vue.createElementVNode("input", {
class: "form-input",
value: $data.date,
disabled: ""
}, null, 8, ["value"])
]),
vue.createElementVNode("view", { class: "form-group" }, [
vue.createElementVNode("text", { class: "form-label" }, "库存数量"),
vue.createElementVNode("input", {
class: "form-input",
value: $data.stockQty,
disabled: ""
}, null, 8, ["value"])
vue.createElementVNode("view", { class: "readonly-group" }, [
vue.createElementVNode("view", { class: "readonly-item" }, [
vue.createElementVNode("text", { class: "readonly-label" }, "物资名称"),
vue.createElementVNode(
"text",
{ class: "readonly-value" },
vue.toDisplayString($data.materialName),
1
/* TEXT */
)
]),
vue.createElementVNode("view", { class: "readonly-item" }, [
vue.createElementVNode("text", { class: "readonly-label" }, "物质代码"),
vue.createElementVNode(
"text",
{ class: "readonly-value" },
vue.toDisplayString($data.materialCode),
1
/* TEXT */
)
]),
vue.createElementVNode("view", { class: "readonly-item" }, [
vue.createElementVNode("text", { class: "readonly-label" }, "规格型号"),
vue.createElementVNode(
"text",
{ class: "readonly-value" },
vue.toDisplayString($data.materialSpec),
1
/* TEXT */
)
]),
vue.createElementVNode("view", { class: "readonly-item" }, [
vue.createElementVNode("text", { class: "readonly-label" }, "库存数量"),
vue.createElementVNode(
"text",
{ class: "readonly-value" },
vue.toDisplayString($data.stockQty) + vue.toDisplayString($data.unit ? " " + $data.unit : ""),
1
/* TEXT */
)
])
]),
vue.createElementVNode("view", { class: "form-group" }, [
!$data.isViewMode ? (vue.openBlock(), vue.createElementBlock("view", {
key: 0,
class: "form-group"
}, [
vue.createElementVNode("text", { class: "form-label" }, "盘点数量"),
vue.withDirectives(vue.createElementVNode(
"input",
@ -384,15 +670,18 @@ if (uni.restoreGlobal) {
), [
[vue.vModelText, $data.countQty]
])
]),
vue.createElementVNode("view", { class: "form-group" }, [
])) : vue.createCommentVNode("v-if", true),
!$data.isViewMode ? (vue.openBlock(), vue.createElementBlock("view", {
key: 1,
class: "form-group"
}, [
vue.createElementVNode("text", { class: "form-label" }, "盘点备注"),
vue.withDirectives(vue.createElementVNode(
"textarea",
{
class: "form-textarea",
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.remark = $event),
placeholder: "请输入备注"
placeholder: "请输入备注信息"
},
null,
512
@ -400,148 +689,82 @@ if (uni.restoreGlobal) {
), [
[vue.vModelText, $data.remark]
])
]),
vue.createElementVNode("view", { class: "form-group" }, [
])) : vue.createCommentVNode("v-if", true),
!$data.isViewMode ? (vue.openBlock(), vue.createElementBlock("view", {
key: 2,
class: "form-group"
}, [
vue.createElementVNode("text", { class: "form-label" }, "照片上传"),
vue.createElementVNode("view", { class: "photo-upload" }, [
$data.photo ? (vue.openBlock(), vue.createElementBlock("view", {
(vue.openBlock(true), vue.createElementBlock(
vue.Fragment,
null,
vue.renderList($data.photos, (photo, index) => {
return vue.openBlock(), vue.createElementBlock("view", {
key: index,
class: "photo-preview"
}, [
vue.createElementVNode("image", {
src: photo,
mode: "aspectFill",
class: "photo-img"
}, null, 8, ["src"]),
vue.createElementVNode("view", {
class: "photo-del",
onClick: ($event) => $options.deletePhoto(index)
}, [
vue.createElementVNode("text", { class: "delete-icon" }, "×")
], 8, ["onClick"])
]);
}),
128
/* KEYED_FRAGMENT */
)),
$data.photos.length < 3 ? (vue.openBlock(), vue.createElementBlock("button", {
key: 0,
class: "photo-preview"
}, [
vue.createElementVNode("image", {
src: $data.photo,
mode: "aspectFill",
class: "photo-img"
}, null, 8, ["src"]),
vue.createElementVNode("text", {
class: "photo-del",
onClick: _cache[2] || (_cache[2] = ($event) => $data.photo = "")
}, "删除")
])) : (vue.openBlock(), vue.createElementBlock("button", {
key: 1,
class: "photo-btn",
onClick: _cache[3] || (_cache[3] = (...args) => $options.choosePhoto && $options.choosePhoto(...args))
}, "上传照片"))
onClick: _cache[2] || (_cache[2] = (...args) => $options.choosePhoto && $options.choosePhoto(...args))
}, [
vue.createElementVNode("text", { class: "iconfont icon-camera" }),
vue.createElementVNode("text", { class: "btn-text" }, "上传照片")
])) : vue.createCommentVNode("v-if", true)
])
]),
vue.createElementVNode("button", {
])) : vue.createCommentVNode("v-if", true),
!$data.isViewMode ? (vue.openBlock(), vue.createElementBlock("button", {
key: 3,
class: "submit-btn",
onClick: _cache[4] || (_cache[4] = (...args) => $options.submit && $options.submit(...args))
}, "提交盘点")
])
]);
}
const PagesInventoryInventory = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1], ["__file", "D:/Lynn/Langye/stocktaking/pages/inventory/inventory.vue"]]);
const _sfc_main$1 = {
data() {
return {
info: {
name: "",
code: "",
stockQty: "",
photo: "",
remark: ""
}
};
},
onLoad(options) {
this.info = {
name: "仓库A物资",
code: options.code || "未知",
stockQty: 100,
photo: "",
remark: "无特殊说明"
};
}
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock("view", { class: "detail-bg" }, [
vue.createElementVNode("view", { class: "detail-card" }, [
vue.createElementVNode("view", { class: "detail-list" }, [
vue.createElementVNode("view", { class: "detail-item" }, [
vue.createElementVNode("text", { class: "detail-label" }, "物资名称"),
vue.createElementVNode(
"text",
{ class: "detail-value" },
vue.toDisplayString($data.info.name),
1
/* TEXT */
)
]),
vue.createElementVNode("view", { class: "detail-item" }, [
vue.createElementVNode("text", { class: "detail-label" }, "物资编号"),
vue.createElementVNode(
"text",
{ class: "detail-value" },
vue.toDisplayString($data.info.code),
1
/* TEXT */
)
]),
vue.createElementVNode("view", { class: "detail-item" }, [
vue.createElementVNode("text", { class: "detail-label" }, "库存数量"),
vue.createElementVNode(
"text",
{ class: "detail-value stock" },
vue.toDisplayString($data.info.stockQty),
1
/* TEXT */
)
]),
vue.createElementVNode("view", { class: "detail-item" }, [
vue.createElementVNode("text", { class: "detail-label" }, "物资图片"),
$data.info.photo ? (vue.openBlock(), vue.createElementBlock("image", {
key: 0,
src: $data.info.photo,
class: "detail-img",
mode: "aspectFill"
}, null, 8, ["src"])) : (vue.openBlock(), vue.createElementBlock("view", {
key: 1,
class: "img-placeholder"
}, "无图片"))
]),
vue.createElementVNode("view", { class: "detail-item remark-item" }, [
vue.createElementVNode("text", { class: "detail-label" }, "备注"),
vue.createElementVNode(
"text",
{ class: "detail-remark" },
vue.toDisplayString($data.info.remark || "无"),
1
/* TEXT */
)
])
])
onClick: _cache[3] || (_cache[3] = (...args) => $options.submit && $options.submit(...args))
}, "提交盘点")) : vue.createCommentVNode("v-if", true)
])
]);
}
const PagesDetailDetail = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__file", "D:/Lynn/Langye/stocktaking/pages/detail/detail.vue"]]);
const PagesInventoryInventory = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__file", "D:/Lynn/Langye/stocktaking/pages/inventory/inventory.vue"]]);
__definePage("pages/index/index", PagesIndexIndex);
__definePage("pages/profile/profile", PagesProfileProfile);
__definePage("pages/login/login", PagesLoginLogin);
__definePage("pages/scan/scan", PagesScanScan);
__definePage("pages/inventory/inventory", PagesInventoryInventory);
__definePage("pages/detail/detail", PagesDetailDetail);
function formatAppLog(type, filename, ...args) {
if (uni.__log__) {
uni.__log__(type, filename, ...args);
} else {
console[type].apply(console, [...args, filename]);
}
}
const _sfc_main = {
onLaunch: function() {
formatAppLog("log", "at App.vue:4", "App Launch");
const token = uni.getStorageSync("token");
if (!token) {
uni.reLaunch({ url: "/pages/login/login" });
} else {
uni.reLaunch({ url: "/pages/index/index" });
}
},
onShow: function() {
formatAppLog("log", "at App.vue:7", "App Show");
formatAppLog("log", "at App.vue:13", "App Show");
},
onHide: function() {
formatAppLog("log", "at App.vue:10", "App Hide");
formatAppLog("log", "at App.vue:16", "App Hide");
}
};
const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "D:/Lynn/Langye/stocktaking/App.vue"]]);
function createApp() {
const app = vue.createVueApp(App);
app.config.globalProperties.$config = config;
return {
app
};

@ -4,7 +4,7 @@
"iPhone",
"iPad"
],
"id": "",
"id": "__UNI__A789D1D",
"name": "stocktaking",
"version": {
"name": "1.0.0",

@ -1,92 +0,0 @@
.detail-bg {
min-height: 100vh;
background: linear-gradient(180deg, #eaf1fb 0%, #f7fafd 100%);
display: flex;
align-items: center;
justify-content: center;
}
.detail-card {
width: 94vw;
max-width: 500px;
background: #fff;
border-radius: 28px;
box-shadow: 0 8px 32px rgba(64,158,255,0.10);
padding: 38px 18px 28px 18px;
display: flex;
flex-direction: column;
align-items: stretch;
}
.detail-title {
font-size: 26px;
font-weight: 800;
color: #409eff;
text-align: center;
margin-bottom: 32px;
letter-spacing: 2px;
}
.detail-list {
display: flex;
flex-direction: column;
gap: 0;
}
.detail-item {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
background: #f7fafd;
border-radius: 16px;
margin-bottom: 18px;
padding: 18px 16px;
box-shadow: 0 2px 8px rgba(64,158,255,0.04);
}
.detail-label {
color: #888;
font-size: 16px;
min-width: 80px;
margin-right: 10px;
}
.detail-value {
color: #222;
font-size: 16px;
font-weight: 600;
flex: 1;
text-align: right;
word-break: break-all;
}
.detail-value.stock {
color: #409eff;
font-weight: 700;
}
.detail-img {
width: 90px;
height: 90px;
border-radius: 10px;
margin-left: 10px;
background: #f0f1f3;
border: 1px solid #e3e8f0;
}
.img-placeholder {
width: 90px;
height: 90px;
border-radius: 10px;
background: #f0f1f3;
color: #bbb;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
margin-left: 10px;
}
.remark-item {
align-items: flex-start;
}
.detail-remark {
color: #666;
font-size: 15px;
flex: 1;
text-align: right;
line-height: 1.7;
word-break: break-all;
}

@ -60,7 +60,7 @@
.main-btn.outline:active {
background: #f0f7ff;
}
.recent-section {
.task-section {
width: 92%;
max-width: 520px;
margin: 0 auto;
@ -69,51 +69,55 @@
box-shadow: 0 4px 18px rgba(64,158,255,0.07);
padding: 0.875rem 0.625rem 0.5625rem 0.625rem;
}
.recent-title {
.task-title {
font-size: 0.875rem;
color: #222;
font-weight: 700;
margin-bottom: 0.5625rem;
}
.recent-list {
.task-list {
display: flex;
flex-direction: column;
gap: 0.4375rem;
}
.recent-item {
.task-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.4375rem 0;
border-bottom: 1px solid #f0f0f0;
}
.recent-item:last-child {
.task-item:last-child {
border-bottom: none;
}
.recent-info {
.task-info {
display: flex;
flex-direction: column;
}
.recent-name {
.task-name {
font-size: 0.75rem;
color: #333;
font-weight: 600;
}
.recent-time {
.task-time {
font-size: 0.625rem;
color: #999;
margin-top: 0.0625rem;
}
.recent-status {
.task-status {
font-size: 0.625rem;
padding: 0.125rem 0.4375rem;
border-radius: 0.5rem;
}
.recent-status.completed {
.task-status.pending {
background-color: #fff3e0;
color: #ff9800;
}
.task-status.completed {
background-color: #e8f5e9;
color: #4caf50;
}
.recent-status.processing {
background-color: #fff3e0;
color: #ff9800;
.task-status.in-progress {
background-color: #e8f5e9;
color: #4caf50;
}

@ -1,96 +1,131 @@
.inventory-bg {
min-height: 100vh;
background: linear-gradient(180deg, #eaf1fb 0%, #f7fafd 100%);
display: flex;
align-items: center;
justify-content: center;
background: #f5f6f7;
padding: 0.75rem;
}
.inventory-card {
width: 92vw;
max-width: 480px;
background: #fff;
border-radius: 24px;
box-shadow: 0 8px 32px rgba(64,158,255,0.10);
padding: 48px 24px 32px 24px;
border-radius: 0.75rem;
padding: 1rem 0.75rem;
margin-bottom: 0.75rem;
}
.readonly-group {
margin-bottom: 1rem;
padding: 0.75rem;
background: #f8f9fa;
border-radius: 0.5rem;
}
.readonly-item {
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: space-between;
margin-bottom: 0.625rem;
}
.readonly-item:last-child {
margin-bottom: 0;
}
.readonly-label {
color: #666;
font-size: 0.875rem;
}
.readonly-value {
color: #333;
font-size: 0.875rem;
font-weight: 500;
}
.form-group {
margin-bottom: 28px;
display: flex;
flex-direction: column;
margin-bottom: 1rem;
}
.form-label {
font-size: 16px;
color: #3a3a3a;
margin-bottom: 10px;
font-size: 0.875rem;
color: #333;
margin-bottom: 0.5rem;
font-weight: 500;
}
.form-input {
height: 48px;
border: 1.5px solid #e3e8f0;
border-radius: 12px;
padding: 0 14px;
font-size: 17px;
background: #f6f8fa;
color: #222;
margin-bottom: 2px;
}
.form-input[disabled] {
background: #f0f1f3;
color: #aaa;
height: 2.75rem;
background: #f8f9fa;
border: none;
border-radius: 0.5rem;
padding: 0 0.75rem;
font-size: 0.875rem;
color: #333;
}
.form-textarea {
min-height: 80px;
border: 1.5px solid #e3e8f0;
border-radius: 12px;
padding: 10px 14px;
font-size: 16px;
background: #f6f8fa;
color: #222;
min-height: 5rem;
background: #f8f9fa;
border: none;
border-radius: 0.5rem;
padding: 0.625rem 0.75rem;
font-size: 0.875rem;
color: #333;
}
.photo-upload {
display: flex;
align-items: center;
}
.photo-btn {
height: 44px;
background: #409eff;
color: #fff;
font-size: 16px;
border-radius: 8px;
padding: 0 24px;
border: none;
flex-wrap: wrap;
gap: 0.625rem;
}
.photo-preview {
position: relative;
width: 5rem;
height: 5rem;
}
.photo-btn {
width: 5rem;
height: 5rem;
background: #f8f9fa;
border-radius: 0.5rem;
display: flex;
align-items: center;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0;
}
.photo-btn .iconfont {
font-size: 1.5rem;
color: #666;
margin-bottom: 0.25rem;
}
.btn-text {
font-size: 0.75rem;
color: #666;
}
.photo-preview {
position: relative;
}
.photo-img {
width: 80px;
height: 80px;
border-radius: 8px;
margin-right: 12px;
width: 5rem;
height: 5rem;
border-radius: 0.5rem;
}
.photo-del {
position: absolute;
top: -0.5rem;
right: -0.5rem;
width: 1.25rem;
height: 1.25rem;
background: rgba(0,0,0,0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.delete-icon {
color: #ff4d4f;
font-size: 14px;
cursor: pointer;
font-size: 1rem;
font-weight: bold;
line-height: 1;
}
.submit-btn {
margin-top: 18px;
height: 52px;
background: linear-gradient(90deg, #409eff 0%, #3b7cff 100%);
width: 100%;
height: 2.75rem;
background: #409eff;
color: #fff;
font-size: 20px;
font-weight: 700;
border-radius: 14px;
letter-spacing: 8px;
box-shadow: 0 4px 16px rgba(64,158,255,0.13);
font-size: 1rem;
font-weight: 500;
border-radius: 1.375rem;
margin-top: 1.5rem;
}
.submit-btn:active {
background: linear-gradient(90deg, #337ecc 60%, #2a5db0 100%);
opacity: 0.9;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Loading…
Cancel
Save