first commit
This commit is contained in:
parent
61fcf7dd71
commit
9053419211
8
App.vue
8
App.vue
@ -64,15 +64,15 @@ export default {
|
|||||||
async checkLoginStatus() {
|
async checkLoginStatus() {
|
||||||
try {
|
try {
|
||||||
// 检查本地存储的登录状态
|
// 检查本地存储的登录状态
|
||||||
const isLoggedIn = await this.$store.dispatch('checkLogin')
|
const isLoggedIn = await this.$store.dispatch('auth/checkLogin')
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
const token = uni.getStorageSync('token')
|
const token = uni.getStorageSync('token')
|
||||||
const userInfo = uni.getStorageSync('userInfo')
|
const userInfo = uni.getStorageSync('userInfo')
|
||||||
if (token && userInfo) {
|
if (token && userInfo) {
|
||||||
// 恢复登录状态
|
// 恢复登录状态
|
||||||
this.$store.commit('SET_TOKEN', token)
|
this.$store.commit('auth/SET_TOKEN', token)
|
||||||
this.$store.commit('SET_USER_INFO', userInfo)
|
this.$store.commit('auth/SET_USER_INFO', userInfo)
|
||||||
this.$store.commit('SET_LOGIN_STATUS', true)
|
this.$store.commit('auth/SET_LOGIN_STATUS', true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
490
README.md
490
README.md
@ -1,63 +1,455 @@
|
|||||||
# 图书上传系统 - 页面拆分说明
|
# 智助小程序 - 开发文档
|
||||||
|
|
||||||
## 已完成工作
|
## 项目简介
|
||||||
|
|
||||||
已将原有的单页面应用拆分为三个独立页面:
|
智助是一款基于 uni-app 开发的图书管理小程序,主要面向图书经销商,提供图书上传、商品管理、仓库管理等功能。项目集成了孔夫子旧书网 API,支持多端发布(微信小程序、支付宝小程序、H5、App)。
|
||||||
|
|
||||||
1. **首页(pages/index/index.vue)**
|
## 目录
|
||||||
- 重新设计为导航页面
|
|
||||||
- 提供两种上传方式的入口:ISBN上传和仅书名上传
|
|
||||||
- 简洁直观的UI,便于用户选择
|
|
||||||
|
|
||||||
2. **ISBN上传页面(pages/isbn-upload/index.vue)**
|
- [技术栈](#技术栈)
|
||||||
- 专门用于ISBN扫码上传
|
- [项目结构](#项目结构)
|
||||||
- 包含扫码功能、书籍信息填写、图片上传等功能
|
- [环境配置](#环境配置)
|
||||||
- 与原页面中的ISBN上传部分功能一致
|
- [开发指南](#开发指南)
|
||||||
|
- [核心功能](#核心功能)
|
||||||
|
- [API 接口](#api-接口)
|
||||||
|
- [组件说明](#组件说明)
|
||||||
|
- [部署发布](#部署发布)
|
||||||
|
- [常见问题](#常见问题)
|
||||||
|
|
||||||
3. **仅书名上传页面(pages/title-upload/index.vue)**
|
---
|
||||||
- 专门用于根据书名搜索上传
|
|
||||||
- 包含书名搜索、结果选择、图片上传等功能
|
|
||||||
- 与原页面中的仅书名上传部分功能一致
|
|
||||||
|
|
||||||
## 下一步操作
|
## 技术栈
|
||||||
|
|
||||||
为完成系统集成,需要进行以下操作:
|
### 前端框架
|
||||||
|
- **Vue.js 2.x** - 核心框架
|
||||||
|
- **uni-app** - 跨端开发框架
|
||||||
|
- **Vuex** - 状态管理
|
||||||
|
|
||||||
1. **更新pages.json配置文件**
|
### UI 组件库
|
||||||
- 添加新创建的页面路径
|
- **uView UI 2.0.38** - 主要 UI 组件库
|
||||||
```json
|
- **uni-ui** - DCloud 官方组件库
|
||||||
|
|
||||||
|
### 工具库
|
||||||
|
- **crypto-js 4.2.0** - 加密算法库
|
||||||
|
- **SCSS** - 样式预处理
|
||||||
|
|
||||||
|
### 开发工具
|
||||||
|
- **HBuilderX** - 官方推荐 IDE
|
||||||
|
- **微信开发者工具** - 小程序调试
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
zhizhu/
|
||||||
|
├── api/ # API 接口定义
|
||||||
|
│ └── kongfz.js # 孔夫子旧书网 API 服务
|
||||||
|
│
|
||||||
|
├── components/ # 自定义组件
|
||||||
|
│ ├── BookConditionSelect.vue # 图书成色选择器
|
||||||
|
│ ├── BookFilterDisplay.vue # 图书筛选展示
|
||||||
|
│ ├── BookProductList.vue # 图书产品列表
|
||||||
|
│ ├── CameraUpload.vue # 相机上传组件
|
||||||
|
│ ├── CategoryDropdown.vue # 分类下拉选择
|
||||||
|
│ ├── FormInput.vue # 表单输入组件
|
||||||
|
│ ├── LocationPicker.vue # 位置选择器
|
||||||
|
│ ├── PriceComparison.vue # 价格比较组件
|
||||||
|
│ ├── PriceStockControl.vue # 价格库存控制
|
||||||
|
│ ├── StoragePicker.vue # 存储选择器
|
||||||
|
│ ├── TabBar.vue # 底部导航栏
|
||||||
|
│ └── WarehouserSelector.vue # 仓库选择器
|
||||||
|
│
|
||||||
|
├── pages/ # 页面文件
|
||||||
|
│ ├── clone-tool/ # 商品克隆工具
|
||||||
|
│ │ └── index.vue
|
||||||
|
│ ├── entry/ # 功能入口
|
||||||
|
│ │ └── index.vue
|
||||||
|
│ ├── goods/ # 商品管理
|
||||||
|
│ │ └── index.vue
|
||||||
|
│ ├── index/ # 首页
|
||||||
|
│ │ └── index.vue
|
||||||
|
│ ├── isbn-upload/ # ISBN 上传
|
||||||
|
│ │ └── index.vue
|
||||||
|
│ ├── login/ # 登录
|
||||||
|
│ │ └── index.vue
|
||||||
|
│ ├── photo-upload/ # 照片上传
|
||||||
|
│ │ └── index.vue
|
||||||
|
│ ├── register/ # 注册(子包)
|
||||||
|
│ │ └── index.vue
|
||||||
|
│ ├── scan/ # 扫码相关
|
||||||
|
│ │ ├── book-records.vue # 上书记录
|
||||||
|
│ │ └── history.vue # 设置
|
||||||
|
│ ├── shelf/ # 货架管理
|
||||||
|
│ │ └── management.vue
|
||||||
|
│ ├── title-upload/ # 书名上传
|
||||||
|
│ │ └── index.vue
|
||||||
|
│ ├── user/ # 用户中心
|
||||||
|
│ │ ├── index.vue # 个人中心
|
||||||
|
│ │ ├── memberSelect.vue # 会员等级选择
|
||||||
|
│ │ └── dispatch-management.vue # 下发管理
|
||||||
|
│ ├── warehouse/ # 仓库管理(子包)
|
||||||
|
│ │ ├── warehouse-select.vue # 选择仓库
|
||||||
|
│ │ ├── create-warehouse.vue # 创建仓库
|
||||||
|
│ │ ├── order-query.vue # 订单查询
|
||||||
|
│ │ └── warehouse-select.vue # 闪上书员工选择
|
||||||
|
│ └── wave/
|
||||||
|
│ ├──wave.json # 禁止刷新
|
||||||
|
│ └──wave.vue # 闪上书页面
|
||||||
|
│
|
||||||
|
├── static/ # 静态资源
|
||||||
|
│ └── tabbar/ # 底部导航图标
|
||||||
|
│
|
||||||
|
├── store/ # Vuex 状态管理
|
||||||
|
│ └── index.js
|
||||||
|
│
|
||||||
|
├── uni_modules/ # uni-app 插件模块
|
||||||
|
│ ├── bcode-camera/ # 条码扫描相机
|
||||||
|
│ ├── uni-fab/ # 悬浮按钮
|
||||||
|
│ ├── uni-icons/ # 图标组件
|
||||||
|
│ ├── uni-scss/ # SCSS 预处理
|
||||||
|
│ └── uview-ui/ # uView UI 组件库
|
||||||
|
│
|
||||||
|
├── utils/ # 工具函数
|
||||||
|
│ ├── clone-tool.js # 克隆工具
|
||||||
|
│ ├── config.js # 配置文件
|
||||||
|
│ ├── request.js # 请求封装
|
||||||
|
│ └── upload.js # 上传功能
|
||||||
|
│
|
||||||
|
├── App.vue # 应用根组件
|
||||||
|
├── main.js # 应用入口
|
||||||
|
├── pages.json # 页面配置
|
||||||
|
├── manifest.json # 应用配置
|
||||||
|
├── package.json # 项目依赖
|
||||||
|
├── uni.scss # 全局样式
|
||||||
|
└── README.md # 项目文档
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 环境配置
|
||||||
|
|
||||||
|
### 1. 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 修改配置
|
||||||
|
|
||||||
|
编辑 [utils/config.js](utils/config.js),配置以下参数:
|
||||||
|
|
||||||
|
```javascript
|
||||||
{
|
{
|
||||||
"pages": [
|
// API 基础 URL
|
||||||
{
|
baseURL: 'https://api.buzhiyushu.cn',
|
||||||
"path": "pages/index/index",
|
|
||||||
"style": {
|
// 客户端 ID
|
||||||
"navigationBarTitleText": "图书上传系统"
|
clientId: '1400a724f627ddc73d8f4dd344f80a5e',
|
||||||
}
|
|
||||||
},
|
// 授权类型(xcx: 小程序)
|
||||||
{
|
grantType: 'xcx',
|
||||||
"path": "pages/isbn-upload/index",
|
|
||||||
"style": {
|
// 请求超时时间(毫秒)
|
||||||
"navigationBarTitleText": "ISBN上传"
|
timeout: 10000
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "pages/title-upload/index",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "仅书名上传"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 其他已有页面...
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **测试新页面功能**
|
### 3. 小程序配置
|
||||||
- 确保所有功能正常运行
|
|
||||||
- 检查页面间的跳转是否流畅
|
|
||||||
- 验证各项功能是否与原系统一致
|
|
||||||
|
|
||||||
## 注意事项
|
编辑 [manifest.json](manifest.json),配置微信小程序 AppID:
|
||||||
|
|
||||||
- 拆分后的页面共用同样的组件和样式
|
```json
|
||||||
- 保留了原始功能,只是将其分散到不同页面
|
{
|
||||||
- 如有UI上的问题,可能需要调整相应的样式
|
"mp-weixin": {
|
||||||
|
"appid": "wx703b8fb6c3da692a",
|
||||||
|
"setting": {
|
||||||
|
"urlCheck": false,
|
||||||
|
"es6": true,
|
||||||
|
"postcss": true,
|
||||||
|
"minified": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 开发工具
|
||||||
|
|
||||||
|
1. 下载并安装 [HBuilderX](https://www.dcloud.io/hbuilderx.html)
|
||||||
|
2. 下载并安装 [微信开发者工具](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)
|
||||||
|
3. 在 HBuilderX 中导入项目
|
||||||
|
4. 运行 → 运行到小程序模拟器 → 微信开发者工具
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 开发指南
|
||||||
|
|
||||||
|
### 页面开发规范
|
||||||
|
|
||||||
|
1. **页面命名**:使用 kebab-case 命名法,如 `isbn-upload/index.vue`
|
||||||
|
2. **组件命名**:使用 PascalCase 命名法,如 `BookProductList.vue`
|
||||||
|
3. **样式编写**:使用 SCSS,统一使用 `uni.scss` 中的变量
|
||||||
|
|
||||||
|
### 请求封装
|
||||||
|
|
||||||
|
使用 [utils/request.js](utils/request.js) 发起网络请求:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import request from '@/utils/request.js'
|
||||||
|
|
||||||
|
// GET 请求
|
||||||
|
request({
|
||||||
|
url: '/api/endpoint',
|
||||||
|
method: 'GET',
|
||||||
|
data: { key: 'value' },
|
||||||
|
loading: true // 显示加载提示
|
||||||
|
}).then(res => {
|
||||||
|
console.log(res.data)
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// POST 请求
|
||||||
|
request({
|
||||||
|
url: '/api/endpoint',
|
||||||
|
method: 'POST',
|
||||||
|
data: { key: 'value' }
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 状态管理
|
||||||
|
|
||||||
|
在 [store/index.js](store/index.js) 中使用 Vuex:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { mapState, mapActions } from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
...mapState(['userInfo', 'token'])
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['setUserInfo', 'setToken'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 组件使用
|
||||||
|
|
||||||
|
项目使用了 easycom 自动引入组件,无需手动引入:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<u-button type="primary">按钮</u-button>
|
||||||
|
<u-input v-model="value" placeholder="请输入内容" />
|
||||||
|
<uni-popup ref="popup">弹窗内容</uni-popup>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心功能
|
||||||
|
|
||||||
|
### 1. 用户认证
|
||||||
|
|
||||||
|
- **登录**:[pages/login/index.vue](pages/login/index.vue)
|
||||||
|
- 支持微信授权登录
|
||||||
|
- 支持账号密码登录
|
||||||
|
|
||||||
|
- **注册**:[pages/register/index.vue](pages/register/index.vue)
|
||||||
|
- 新用户注册流程
|
||||||
|
|
||||||
|
### 2. 图书上传
|
||||||
|
|
||||||
|
提供两种图书上传方式:
|
||||||
|
|
||||||
|
- **ISBN 扫码上传**:[pages/isbn-upload/index.vue](pages/isbn-upload/index.vue)
|
||||||
|
- 扫描 ISBN 码自动识别图书信息
|
||||||
|
- 支持手动输入 ISBN
|
||||||
|
|
||||||
|
- **照片上传**:[pages/photo-upload/index.vue](pages/photo-upload/index.vue)
|
||||||
|
- 拍照上传图书封面
|
||||||
|
- AI 识别图书信息
|
||||||
|
|
||||||
|
### 3. 商品管理
|
||||||
|
|
||||||
|
- **商品列表**:[pages/goods/index.vue](pages/goods/index.vue)
|
||||||
|
- 查看所有商品
|
||||||
|
- 支持搜索和筛选
|
||||||
|
- 支持下拉刷新
|
||||||
|
|
||||||
|
- **商品克隆工具**:[pages/clone-tool/index.vue](pages/clone-tool/index.vue)
|
||||||
|
- 从孔夫子旧书网克隆商品
|
||||||
|
- 批量处理商品信息
|
||||||
|
- 自动价格调整
|
||||||
|
|
||||||
|
### 4. 仓库管理
|
||||||
|
|
||||||
|
- **仓库选择**:[pages/warehouse/warehouse-select.vue](pages/warehouse/warehouse-select.vue)
|
||||||
|
- 选择当前使用的仓库
|
||||||
|
- 支持多仓库切换
|
||||||
|
|
||||||
|
- **创建仓库**:[pages/warehouse/create-warehouse.vue](pages/warehouse/create-warehouse.vue)
|
||||||
|
- 创建新仓库
|
||||||
|
- 配置仓库信息
|
||||||
|
|
||||||
|
- **货架管理**:[pages/shelf/management.vue](pages/shelf/management.vue)
|
||||||
|
- 管理仓库货架
|
||||||
|
- 商品位置分配
|
||||||
|
|
||||||
|
### 5. 用户中心
|
||||||
|
|
||||||
|
- **个人中心**:[pages/user/index.vue](pages/user/index.vue)
|
||||||
|
- 查看用户信息
|
||||||
|
- 会员等级选择
|
||||||
|
- 下发管理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API 接口
|
||||||
|
|
||||||
|
### 后端 API
|
||||||
|
|
||||||
|
**基础 URL**:`https://api.buzhiyushu.cn`
|
||||||
|
|
||||||
|
| 接口 | 方法 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `/auth/login` | POST | 用户登录 |
|
||||||
|
| `/auth/code` | GET | 获取验证码 |
|
||||||
|
| `/auth/tenant/list` | GET | 获取租户列表 |
|
||||||
|
| `/zhishu/shopGoods/xcx` | GET | 获取商品列表 |
|
||||||
|
| `/zhishu/shopGoodsPublished/batchUpdateKfzPlatformId` | POST | 批量更新平台 ID |
|
||||||
|
|
||||||
|
### 孔夫子旧书网 API
|
||||||
|
|
||||||
|
详见 [api/kongfz.js](api/kongfz.js):
|
||||||
|
|
||||||
|
| 函数 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `login(username, password)` | 登录孔夫子账号 |
|
||||||
|
| `fetchItems(token, params)` | 获取商品列表 |
|
||||||
|
| `getItemTplFields(token, itemId)` | 获取商品模板字段 |
|
||||||
|
| `submitItemForm(token, itemData, priceConfig)` | 提交商品表单 |
|
||||||
|
| `deleteItem(token, itemId)` | 删除商品 |
|
||||||
|
| `batchUpdatePddPlatformId(items)` | 批量更新平台 ID |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 组件说明
|
||||||
|
|
||||||
|
### BookConditionSelect.vue
|
||||||
|
图书成色选择器,用于选择图书的新旧程度。
|
||||||
|
|
||||||
|
### BookProductList.vue
|
||||||
|
图书产品列表组件,展示图书信息。
|
||||||
|
|
||||||
|
### CameraUpload.vue
|
||||||
|
相机上传组件,调用相机拍照上传。
|
||||||
|
|
||||||
|
### PriceStockControl.vue
|
||||||
|
价格库存控制组件,管理商品价格和库存。
|
||||||
|
|
||||||
|
### WarehouserSelector.vue
|
||||||
|
仓库选择器,选择和管理仓库。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 部署发布
|
||||||
|
|
||||||
|
### 微信小程序
|
||||||
|
|
||||||
|
1. 在 HBuilderX 中点击 **发行** → **小程序-微信**
|
||||||
|
2. 填写版本号和备注
|
||||||
|
3. 点击 **发行** 按钮
|
||||||
|
4. 在微信开发者工具中打开生成的目录
|
||||||
|
5. 上传代码到微信后台
|
||||||
|
6. 在微信公众平台提交审核
|
||||||
|
|
||||||
|
### H5
|
||||||
|
|
||||||
|
1. 在 HBuilderX 中点击 **发行** → **H5**
|
||||||
|
2. 填写网站标题和域名
|
||||||
|
3. 点击 **发行** 按钮
|
||||||
|
4. 将生成的文件部署到服务器
|
||||||
|
|
||||||
|
### App
|
||||||
|
|
||||||
|
1. 在 HBuilderX 中点击 **发行** → **原生App-云打包**
|
||||||
|
2. 选择平台(Android/iOS)
|
||||||
|
3. 填写应用信息
|
||||||
|
4. 点击 **打包** 按钮
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### 1. 如何添加新页面?
|
||||||
|
|
||||||
|
1. 在 `pages/` 目录下创建页面文件
|
||||||
|
2. 在 `pages.json` 中注册页面路径
|
||||||
|
3. 在 `pages.json` 中配置页面样式
|
||||||
|
|
||||||
|
### 2. 如何修改主题颜色?
|
||||||
|
|
||||||
|
编辑 `uni.scss`,修改全局样式变量:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
$uni-color-primary: #2979ff;
|
||||||
|
$uni-color-success: #4cd964;
|
||||||
|
$uni-color-warning: #f0ad4e;
|
||||||
|
$uni-color-error: #dd524d;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 如何处理跨域问题?
|
||||||
|
|
||||||
|
在 `manifest.json` 中配置白名单域名:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mp-weixin": {
|
||||||
|
"setting": {
|
||||||
|
"urlCheck": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 如何调试网络请求?
|
||||||
|
|
||||||
|
在 `utils/request.js` 中添加日志:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
success: (res) => {
|
||||||
|
console.log('请求成功:', res)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 页面白屏怎么办?
|
||||||
|
|
||||||
|
1. 检查控制台是否有错误
|
||||||
|
2. 确认所有依赖是否正确安装
|
||||||
|
3. 清除缓存重新编译
|
||||||
|
4. 检查页面路径是否正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本历史
|
||||||
|
|
||||||
|
- **v1.0.0** (2024) - 初始版本发布
|
||||||
|
- 用户登录注册
|
||||||
|
- 图书上传功能
|
||||||
|
- 商品管理
|
||||||
|
- 仓库管理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 开发团队
|
||||||
|
|
||||||
|
本项目由知书团队开发维护。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
本项目为商业项目,版权归知书团队所有。
|
||||||
|
|||||||
@ -428,6 +428,17 @@ export function submitItemForm(token, itemData, priceConfig) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理带有 group 的字段(例如尺寸 sizeLength/sizeWidth/sizeHeight)
|
||||||
|
if (field.group && Array.isArray(field.group)) {
|
||||||
|
for (const subField of field.group) {
|
||||||
|
const subKey = subField.key;
|
||||||
|
const subValue = subField.value;
|
||||||
|
if (subKey && subValue !== null && subValue !== undefined) {
|
||||||
|
formData[subKey] = String(subValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (key && value !== null && value !== undefined) {
|
if (key && value !== null && value !== undefined) {
|
||||||
// 特殊处理价格字段
|
// 特殊处理价格字段
|
||||||
if (key === 'price') {
|
if (key === 'price') {
|
||||||
@ -480,6 +491,21 @@ export function submitItemForm(token, itemData, priceConfig) {
|
|||||||
formData.catId = String(categorySection.value);
|
formData.catId = String(categorySection.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const symbols = ['.', ',', ',', '~'];
|
||||||
|
const randomSymbol = symbols[Math.floor(Math.random() * symbols.length)];
|
||||||
|
|
||||||
|
let originalName = formData['itemName']; // 或 form_data.itemName
|
||||||
|
let newName = originalName + randomSymbol;
|
||||||
|
|
||||||
|
if (newName.length > 60) {
|
||||||
|
const maxOriginalLength = 60 - randomSymbol.length;
|
||||||
|
newName = originalName.slice(0, maxOriginalLength) + randomSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
formData['itemName'] = newName;
|
||||||
|
|
||||||
const url = 'https://seller.kongfz.com/pc/itemInfo/add';
|
const url = 'https://seller.kongfz.com/pc/itemInfo/add';
|
||||||
console.log("formData1111111111", formData)
|
console.log("formData1111111111", formData)
|
||||||
// 发送请求
|
// 发送请求
|
||||||
|
|||||||
@ -123,12 +123,12 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取已使用的编号(包括2,因为它预留给识图照片)
|
// 获取已使用的编号(排除识图照片)
|
||||||
const usedNums = new Set(["2"]);
|
const usedNums = new Set();
|
||||||
|
|
||||||
// 收集当前文件列表中已使用的编号,并确保不会重复使用已上传文件的编号
|
// 收集当前文件列表中已使用的编号,只收集普通照片
|
||||||
this.fileList.forEach(file => {
|
this.fileList.forEach(file => {
|
||||||
if (file.num) {
|
if (file.num && !file.isOCR) {
|
||||||
usedNums.add(file.num);
|
usedNums.add(file.num);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -157,49 +157,20 @@
|
|||||||
|
|
||||||
this.fileList.push(newFile);
|
this.fileList.push(newFile);
|
||||||
|
|
||||||
// 重新排序文件列表,但保持识图照片的位置
|
// 重新排序文件列表,确保识图照片始终在最后
|
||||||
this.fileList.sort((a, b) => {
|
this.fileList.sort((a, b) => {
|
||||||
// 如果其中一个是识图照片,保持其位置
|
// 如果其中一个是识图照片,保持在最后
|
||||||
if (a.name?.startsWith('识图-')) return 1;
|
if (a.isOCR || a.name?.startsWith('识图-')) return 1;
|
||||||
if (b.name?.startsWith('识图-')) return -1;
|
if (b.isOCR || b.name?.startsWith('识图-')) return -1;
|
||||||
|
|
||||||
const numA = parseInt(a.num) || 999;
|
const numA = parseInt(a.num) || 999;
|
||||||
const numB = parseInt(b.num) || 999;
|
const numB = parseInt(b.num) || 999;
|
||||||
return numA - numB;
|
return numA - numB;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加到上传队列
|
// 添加到上传队列(只上传普通照片)
|
||||||
await this.addToUploadQueue(newFile, this.fileList.indexOf(newFile));
|
await this.addToUploadQueue(newFile, this.fileList.indexOf(newFile));
|
||||||
|
|
||||||
// 如果这是第一张普通照片(num="1"),检查并上传识图照片
|
|
||||||
if (newFile.num === "1") {
|
|
||||||
const ocrPhoto = this.fileList.find(file =>
|
|
||||||
file.name?.startsWith('识图-') &&
|
|
||||||
file.status === 'ready' &&
|
|
||||||
file.url.startsWith('wxfile://tmp') || file.url.startsWith('http://tmp') // 检查是否为临时路径
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ocrPhoto) {
|
|
||||||
console.log('发现待上传的识图照片,准备上传:', ocrPhoto);
|
|
||||||
// 更新识图照片的hidden属性为false
|
|
||||||
const ocrIndex = this.fileList.indexOf(ocrPhoto);
|
|
||||||
const updatedOcrPhoto = {
|
|
||||||
...ocrPhoto,
|
|
||||||
hidden: false,
|
|
||||||
num: "2" // 确保编号为2
|
|
||||||
};
|
|
||||||
|
|
||||||
this.fileList.splice(ocrIndex, 1, updatedOcrPhoto);
|
|
||||||
|
|
||||||
// 立即上传识图照片
|
|
||||||
await this.addToUploadQueue(updatedOcrPhoto, ocrIndex);
|
|
||||||
|
|
||||||
// 通知父组件更新
|
|
||||||
this.$emit('input', this.fileList);
|
|
||||||
console.log('已将识图照片设置为第二张并添加到上传队列');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标记为已处理
|
// 标记为已处理
|
||||||
this.markAsProcessed(imageUrl);
|
this.markAsProcessed(imageUrl);
|
||||||
|
|
||||||
@ -212,7 +183,8 @@
|
|||||||
num: f.num,
|
num: f.num,
|
||||||
name: f.name,
|
name: f.name,
|
||||||
hidden: f.hidden,
|
hidden: f.hidden,
|
||||||
status: f.status
|
status: f.status,
|
||||||
|
isOCR: f.isOCR
|
||||||
})));
|
})));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -252,25 +224,6 @@
|
|||||||
// 处理单张图片
|
// 处理单张图片
|
||||||
else if (data.url) {
|
else if (data.url) {
|
||||||
await this.processNewImage(data.url);
|
await this.processNewImage(data.url);
|
||||||
|
|
||||||
// 检查是否有识图照片需要上传
|
|
||||||
const ocrPhoto = this.fileList.find(file => file.name?.startsWith('识图-') && file.status === 'ready');
|
|
||||||
if (ocrPhoto) {
|
|
||||||
console.log('发现待上传的识图照片,准备上传');
|
|
||||||
// 更新识图照片的hidden属性为false,使其显示出来
|
|
||||||
const ocrIndex = this.fileList.indexOf(ocrPhoto);
|
|
||||||
this.fileList.splice(ocrIndex, 1, {
|
|
||||||
...ocrPhoto,
|
|
||||||
hidden: false,
|
|
||||||
num: "2" // 设置为第二张照片
|
|
||||||
});
|
|
||||||
|
|
||||||
// 将识图照片添加到上传队列
|
|
||||||
this.addToUploadQueue(ocrPhoto, ocrIndex);
|
|
||||||
|
|
||||||
// 通知父组件更新
|
|
||||||
this.$emit('input', this.fileList);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('处理照片失败:', error);
|
console.error('处理照片失败:', error);
|
||||||
@ -296,11 +249,12 @@
|
|||||||
// 添加识图照片(hidden为true,等待第一张照片上传后再显示)
|
// 添加识图照片(hidden为true,等待第一张照片上传后再显示)
|
||||||
const file = {
|
const file = {
|
||||||
url: data.url,
|
url: data.url,
|
||||||
status: "ready",
|
status: "local", // 改为"local"状态,避免触发上传
|
||||||
message: "待上传",
|
message: "本地识图照片",
|
||||||
name: `识图-${Date.now()}.jpg`,
|
name: `识图-${Date.now()}.jpg`,
|
||||||
num: "2", // 预设为第二张照片
|
num: "999", // 设置为很大的编号,确保排在最后
|
||||||
hidden: true // 初始设置为隐藏
|
hidden: true, // 初始设置为隐藏
|
||||||
|
isOCR: true // 标记为识图照片
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!this.isProcessed(data.url)) {
|
if (!this.isProcessed(data.url)) {
|
||||||
|
|||||||
1775
components/IsbnUploadForm.vue
Normal file
1775
components/IsbnUploadForm.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -108,7 +108,7 @@ export const checkMemberBooksCount = async (options = {}) => {
|
|||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
// 跳转到会员选择页面,指定只显示xcx上书会员
|
// 跳转到会员选择页面,指定只显示xcx上书会员
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/user/memberSelect?from=memberCheck&type=xcx'
|
url: '/pkgUser/memberSelect?from=memberCheck&type=xcx'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 返回一个Promise,在用户返回时重新检查
|
// 返回一个Promise,在用户返回时重新检查
|
||||||
@ -535,7 +535,7 @@ const handleNotMemberModal = (mergedOptions) => {
|
|||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/user/memberSelect?from=kwfw&type=kwfw'
|
url: '/pkgUser/memberSelect?from=kwfw&type=kwfw'
|
||||||
});
|
});
|
||||||
|
|
||||||
getApp().kwfwMemberCallback = (success) => {
|
getApp().kwfwMemberCallback = (success) => {
|
||||||
|
|||||||
2420
components/PhotoUploadForm.vue
Normal file
2420
components/PhotoUploadForm.vue
Normal file
File diff suppressed because it is too large
Load Diff
1318
components/TitleUploadForm.vue
Normal file
1318
components/TitleUploadForm.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -42,14 +42,17 @@
|
|||||||
],
|
],
|
||||||
shelves: [], // 货架列表
|
shelves: [], // 货架列表
|
||||||
locations: [], // 货位列表
|
locations: [], // 货位列表
|
||||||
selectedWarehouse: null,
|
selectedWarehouse: this.initialWarehouse || null, // 从 prop 初始化
|
||||||
selectedSheId: null,
|
selectedSheId: null,
|
||||||
selectedFreId: null
|
selectedFreId: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
// 确保数据加载:如果 initialWarehouse 有值但 columns 还没数据,重新加载
|
||||||
|
if (this.initialWarehouse && this.columns[1].length === 0) {
|
||||||
|
console.log('WarehouserSelector mounted: initialWarehouse存在但columns为空,重新加载数据');
|
||||||
this.initData();
|
this.initData();
|
||||||
this.loadStorageSelection();
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
// 监听initialWarehouse变化,重新初始化
|
// 监听initialWarehouse变化,重新初始化
|
||||||
@ -214,11 +217,32 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 打开选择器
|
// 打开选择器
|
||||||
openPicker() {
|
async openPicker() {
|
||||||
// 在打开选择器前确保数据已加载
|
// 使用 selectedWarehouse 或 initialWarehouse 作为 fallback
|
||||||
if (this.columns[0].length === 0 && this.selectedWarehouse) {
|
const warehouse = this.selectedWarehouse || this.initialWarehouse;
|
||||||
this.columns[0] = [this.selectedWarehouse.name];
|
console.log('openPicker: warehouse=', warehouse, 'columns=', this.columns);
|
||||||
|
|
||||||
|
if (warehouse) {
|
||||||
|
// 确保仓库列有数据
|
||||||
|
if (this.columns[0].length === 0) {
|
||||||
|
this.columns[0] = [warehouse.name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 确保货架列有数据,如果没有则加载
|
||||||
|
if (this.columns[1].length === 0) {
|
||||||
|
console.log('openPicker: 货架数据为空,开始加载');
|
||||||
|
const shelves = await this.fetchShelves(warehouse.id);
|
||||||
|
if (shelves && shelves.length > 0) {
|
||||||
|
// 如果货位数据也为空,加载第一个货架的货位
|
||||||
|
if (this.columns[2].length === 0) {
|
||||||
|
await this.fetchLocations(shelves[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('openPicker: 没有仓库数据');
|
||||||
|
}
|
||||||
|
console.log('openPicker: 最终 columns数据:', this.columns);
|
||||||
this.showPicker = true;
|
this.showPicker = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -321,9 +345,8 @@
|
|||||||
|
|
||||||
// 获取货架列表
|
// 获取货架列表
|
||||||
async fetchShelves(depotId) {
|
async fetchShelves(depotId) {
|
||||||
|
console.log('fetchShelves 被调用, depotId:', depotId);
|
||||||
try {
|
try {
|
||||||
// console.log('获取货架数据,仓库ID:', depotId);
|
|
||||||
|
|
||||||
const response = await uni.request({
|
const response = await uni.request({
|
||||||
url: 'https://api.buzhiyushu.cn/shelves/shelves/sheNamelist',
|
url: 'https://api.buzhiyushu.cn/shelves/shelves/sheNamelist',
|
||||||
data: {
|
data: {
|
||||||
@ -333,6 +356,7 @@
|
|||||||
|
|
||||||
// 使用数组解构获取响应结果
|
// 使用数组解构获取响应结果
|
||||||
const [err, res] = Array.isArray(response) ? response : [null, response];
|
const [err, res] = Array.isArray(response) ? response : [null, response];
|
||||||
|
console.log('fetchShelves 响应:', res?.data);
|
||||||
|
|
||||||
if (!res?.data?.rows) {
|
if (!res?.data?.rows) {
|
||||||
console.error('获取货架数据失败,返回空数据');
|
console.error('获取货架数据失败,返回空数据');
|
||||||
@ -341,9 +365,7 @@
|
|||||||
|
|
||||||
this.shelves = res.data.rows; // 保存货架列表
|
this.shelves = res.data.rows; // 保存货架列表
|
||||||
this.columns[1] = this.shelves.map(item => item.code || '未知货架');
|
this.columns[1] = this.shelves.map(item => item.code || '未知货架');
|
||||||
|
console.log('fetchShelves 完成, shelves:', this.shelves.length, 'columns[1]:', this.columns[1].length);
|
||||||
// console.log('获取到货架数据:', this.shelves);
|
|
||||||
// console.log('货架选项:', this.columns[1]);
|
|
||||||
|
|
||||||
// 更新UI
|
// 更新UI
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
"versionName" : "1.0.0",
|
"versionName" : "1.0.0",
|
||||||
"versionCode" : "100",
|
"versionCode" : "100",
|
||||||
"mp-weixin" : {
|
"mp-weixin" : {
|
||||||
|
"optimization" : {
|
||||||
|
"subPackages" : true // 开启分包优化
|
||||||
|
},
|
||||||
"appid" : "wx703b8fb6c3da692a",
|
"appid" : "wx703b8fb6c3da692a",
|
||||||
"setting" : {
|
"setting" : {
|
||||||
"urlCheck" : false,
|
"urlCheck" : false,
|
||||||
|
|||||||
16
node_modules/.bin/he
generated
vendored
Normal file
16
node_modules/.bin/he
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../he/bin/he" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../he/bin/he" "$@"
|
||||||
|
fi
|
||||||
17
node_modules/.bin/he.cmd
generated
vendored
Normal file
17
node_modules/.bin/he.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
@ECHO off
|
||||||
|
GOTO start
|
||||||
|
:find_dp0
|
||||||
|
SET dp0=%~dp0
|
||||||
|
EXIT /b
|
||||||
|
:start
|
||||||
|
SETLOCAL
|
||||||
|
CALL :find_dp0
|
||||||
|
|
||||||
|
IF EXIST "%dp0%\node.exe" (
|
||||||
|
SET "_prog=%dp0%\node.exe"
|
||||||
|
) ELSE (
|
||||||
|
SET "_prog=node"
|
||||||
|
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||||
|
)
|
||||||
|
|
||||||
|
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\he\bin\he" %*
|
||||||
28
node_modules/.bin/he.ps1
generated
vendored
Normal file
28
node_modules/.bin/he.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||||
|
|
||||||
|
$exe=""
|
||||||
|
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||||
|
# Fix case when both the Windows and Linux builds of Node
|
||||||
|
# are installed in the same directory
|
||||||
|
$exe=".exe"
|
||||||
|
}
|
||||||
|
$ret=0
|
||||||
|
if (Test-Path "$basedir/node$exe") {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "$basedir/node$exe" "$basedir/../he/bin/he" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../he/bin/he" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../he/bin/he" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../he/bin/he" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
43
node_modules/.package-lock.json
generated
vendored
43
node_modules/.package-lock.json
generated
vendored
@ -46,6 +46,27 @@
|
|||||||
"@parcel/watcher-win32-x64": "2.5.1"
|
"@parcel/watcher-win32-x64": "2.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@parcel/watcher-win32-ia32": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@parcel/watcher-win32-x64": {
|
"node_modules/@parcel/watcher-win32-x64": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
|
||||||
@ -103,6 +124,11 @@
|
|||||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/de-indent": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
@ -131,6 +157,14 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/he": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||||
|
"bin": {
|
||||||
|
"he": "bin/he"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/immutable": {
|
"node_modules/immutable": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz",
|
||||||
@ -277,6 +311,15 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"HBuilderX": "^3.1.0"
|
"HBuilderX": "^3.1.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue-template-compiler": {
|
||||||
|
"version": "2.7.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
|
||||||
|
"integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"de-indent": "^1.0.2",
|
||||||
|
"he": "^1.2.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
node_modules/@parcel/watcher-win32-ia32/LICENSE
generated
vendored
Normal file
21
node_modules/@parcel/watcher-win32-ia32/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017-present Devon Govett
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
1
node_modules/@parcel/watcher-win32-ia32/README.md
generated
vendored
Normal file
1
node_modules/@parcel/watcher-win32-ia32/README.md
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
This is the win32-ia32 build of @parcel/watcher. See https://github.com/parcel-bundler/watcher for details.
|
||||||
30
node_modules/@parcel/watcher-win32-ia32/package.json
generated
vendored
Normal file
30
node_modules/@parcel/watcher-win32-ia32/package.json
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "@parcel/watcher-win32-ia32",
|
||||||
|
"version": "2.5.1",
|
||||||
|
"main": "watcher.node",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/parcel-bundler/watcher.git"
|
||||||
|
},
|
||||||
|
"description": "A native C++ Node module for querying and subscribing to filesystem events. Used by Parcel 2.",
|
||||||
|
"license": "MIT",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"watcher.node"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
node_modules/@parcel/watcher-win32-ia32/watcher.node
generated
vendored
Normal file
BIN
node_modules/@parcel/watcher-win32-ia32/watcher.node
generated
vendored
Normal file
Binary file not shown.
2
node_modules/de-indent/.npmignore
generated
vendored
Normal file
2
node_modules/de-indent/.npmignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
45
node_modules/de-indent/index.js
generated
vendored
Normal file
45
node_modules/de-indent/index.js
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
var splitRE = /\r?\n/g
|
||||||
|
var emptyRE = /^\s*$/
|
||||||
|
var needFixRE = /^(\r?\n)*[\t\s]/
|
||||||
|
|
||||||
|
module.exports = function deindent (str) {
|
||||||
|
if (!needFixRE.test(str)) {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
var lines = str.split(splitRE)
|
||||||
|
var min = Infinity
|
||||||
|
var type, cur, c
|
||||||
|
for (var i = 0; i < lines.length; i++) {
|
||||||
|
var line = lines[i]
|
||||||
|
if (!emptyRE.test(line)) {
|
||||||
|
if (!type) {
|
||||||
|
c = line.charAt(0)
|
||||||
|
if (c === ' ' || c === '\t') {
|
||||||
|
type = c
|
||||||
|
cur = count(line, type)
|
||||||
|
if (cur < min) {
|
||||||
|
min = cur
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cur = count(line, type)
|
||||||
|
if (cur < min) {
|
||||||
|
min = cur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines.map(function (line) {
|
||||||
|
return line.slice(min)
|
||||||
|
}).join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function count (line, type) {
|
||||||
|
var i = 0
|
||||||
|
while (line.charAt(i) === type) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
25
node_modules/de-indent/package.json
generated
vendored
Normal file
25
node_modules/de-indent/package.json
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "de-indent",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"description": "remove extra indent from a block of code",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/yyx990803/de-indent.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"deindent"
|
||||||
|
],
|
||||||
|
"author": "Evan You",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/yyx990803/de-indent/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/yyx990803/de-indent#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"mocha": "^2.3.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
30
node_modules/de-indent/test.js
generated
vendored
Normal file
30
node_modules/de-indent/test.js
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
var assert = require('assert')
|
||||||
|
var deindent = require('./index')
|
||||||
|
|
||||||
|
describe('de-indent', function () {
|
||||||
|
|
||||||
|
it('0 indent', function () {
|
||||||
|
var str = '\nabc\n bcd\n cde\nefg'
|
||||||
|
var res = deindent(str)
|
||||||
|
assert.equal(str, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non-0 indent', function () {
|
||||||
|
var str = ' abc\n bcd\n cde\n efg'
|
||||||
|
var res = deindent(str)
|
||||||
|
assert.equal(res, 'abc\n bcd\ncde\n efg')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('tabs', function () {
|
||||||
|
var str = '\tabc\n\t\tbcd\n\tcde\n\t\tefg'
|
||||||
|
var res = deindent(str)
|
||||||
|
assert.equal(res, 'abc\n\tbcd\ncde\n\tefg')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('single line', function () {
|
||||||
|
var str = '\n <h2 class="red">{{msg}}</h2>\n'
|
||||||
|
var res = deindent(str)
|
||||||
|
assert.equal(res, '\n<h2 class="red">{{msg}}</h2>\n')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
20
node_modules/he/LICENSE-MIT.txt
generated
vendored
Normal file
20
node_modules/he/LICENSE-MIT.txt
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright Mathias Bynens <https://mathiasbynens.be/>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
379
node_modules/he/README.md
generated
vendored
Normal file
379
node_modules/he/README.md
generated
vendored
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
# he [](https://travis-ci.org/mathiasbynens/he) [](https://codecov.io/github/mathiasbynens/he?branch=master) [](https://gemnasium.com/mathiasbynens/he)
|
||||||
|
|
||||||
|
_he_ (for “HTML entities”) is a robust HTML entity encoder/decoder written in JavaScript. It supports [all standardized named character references as per HTML](https://html.spec.whatwg.org/multipage/syntax.html#named-character-references), handles [ambiguous ampersands](https://mathiasbynens.be/notes/ambiguous-ampersands) and other edge cases [just like a browser would](https://html.spec.whatwg.org/multipage/syntax.html#tokenizing-character-references), has an extensive test suite, and — contrary to many other JavaScript solutions — _he_ handles astral Unicode symbols just fine. [An online demo is available.](https://mothereff.in/html-entities)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Via [npm](https://www.npmjs.com/):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install he
|
||||||
|
```
|
||||||
|
|
||||||
|
Via [Bower](http://bower.io/):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bower install he
|
||||||
|
```
|
||||||
|
|
||||||
|
Via [Component](https://github.com/component/component):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
component install mathiasbynens/he
|
||||||
|
```
|
||||||
|
|
||||||
|
In a browser:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="he.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
In [Node.js](https://nodejs.org/), [io.js](https://iojs.org/), [Narwhal](http://narwhaljs.org/), and [RingoJS](http://ringojs.org/):
|
||||||
|
|
||||||
|
```js
|
||||||
|
var he = require('he');
|
||||||
|
```
|
||||||
|
|
||||||
|
In [Rhino](http://www.mozilla.org/rhino/):
|
||||||
|
|
||||||
|
```js
|
||||||
|
load('he.js');
|
||||||
|
```
|
||||||
|
|
||||||
|
Using an AMD loader like [RequireJS](http://requirejs.org/):
|
||||||
|
|
||||||
|
```js
|
||||||
|
require(
|
||||||
|
{
|
||||||
|
'paths': {
|
||||||
|
'he': 'path/to/he'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
['he'],
|
||||||
|
function(he) {
|
||||||
|
console.log(he);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### `he.version`
|
||||||
|
|
||||||
|
A string representing the semantic version number.
|
||||||
|
|
||||||
|
### `he.encode(text, options)`
|
||||||
|
|
||||||
|
This function takes a string of text and encodes (by default) any symbols that aren’t printable ASCII symbols and `&`, `<`, `>`, `"`, `'`, and `` ` ``, replacing them with character references.
|
||||||
|
|
||||||
|
```js
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux');
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
```
|
||||||
|
|
||||||
|
As long as the input string contains [allowed code points](https://html.spec.whatwg.org/multipage/parsing.html#preprocessing-the-input-stream) only, the return value of this function is always valid HTML. Any [(invalid) code points that cannot be represented using a character reference](https://html.spec.whatwg.org/multipage/syntax.html#table-charref-overrides) in the input are not encoded:
|
||||||
|
|
||||||
|
```js
|
||||||
|
he.encode('foo \0 bar');
|
||||||
|
// → 'foo \0 bar'
|
||||||
|
```
|
||||||
|
|
||||||
|
However, enabling [the `strict` option](https://github.com/mathiasbynens/he#strict) causes invalid code points to throw an exception. With `strict` enabled, `he.encode` either throws (if the input contains invalid code points) or returns a string of valid HTML.
|
||||||
|
|
||||||
|
The `options` object is optional. It recognizes the following properties:
|
||||||
|
|
||||||
|
#### `useNamedReferences`
|
||||||
|
|
||||||
|
The default value for the `useNamedReferences` option is `false`. This means that `encode()` will not use any named character references (e.g. `©`) in the output — hexadecimal escapes (e.g. `©`) will be used instead. Set it to `true` to enable the use of named references.
|
||||||
|
|
||||||
|
**Note that if compatibility with older browsers is a concern, this option should remain disabled.**
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Using the global default setting (defaults to `false`):
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux');
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
|
||||||
|
// Passing an `options` object to `encode`, to explicitly disallow named references:
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux', {
|
||||||
|
'useNamedReferences': false
|
||||||
|
});
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
|
||||||
|
// Passing an `options` object to `encode`, to explicitly allow named references:
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux', {
|
||||||
|
'useNamedReferences': true
|
||||||
|
});
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `decimal`
|
||||||
|
|
||||||
|
The default value for the `decimal` option is `false`. If the option is enabled, `encode` will generally use decimal escapes (e.g. `©`) rather than hexadecimal escapes (e.g. `©`). Beside of this replacement, the basic behavior remains the same when combined with other options. For example: if both options `useNamedReferences` and `decimal` are enabled, named references (e.g. `©`) are used over decimal escapes. HTML entities without a named reference are encoded using decimal escapes.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Using the global default setting (defaults to `false`):
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux');
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
|
||||||
|
// Passing an `options` object to `encode`, to explicitly disable decimal escapes:
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux', {
|
||||||
|
'decimal': false
|
||||||
|
});
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
|
||||||
|
// Passing an `options` object to `encode`, to explicitly enable decimal escapes:
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux', {
|
||||||
|
'decimal': true
|
||||||
|
});
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
|
||||||
|
// Passing an `options` object to `encode`, to explicitly allow named references and decimal escapes:
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux', {
|
||||||
|
'useNamedReferences': true,
|
||||||
|
'decimal': true
|
||||||
|
});
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `encodeEverything`
|
||||||
|
|
||||||
|
The default value for the `encodeEverything` option is `false`. This means that `encode()` will not use any character references for printable ASCII symbols that don’t need escaping. Set it to `true` to encode every symbol in the input string. When set to `true`, this option takes precedence over `allowUnsafeSymbols` (i.e. setting the latter to `true` in such a case has no effect).
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Using the global default setting (defaults to `false`):
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux');
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
|
||||||
|
// Passing an `options` object to `encode`, to explicitly encode all symbols:
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux', {
|
||||||
|
'encodeEverything': true
|
||||||
|
});
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
|
||||||
|
// This setting can be combined with the `useNamedReferences` option:
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux', {
|
||||||
|
'encodeEverything': true,
|
||||||
|
'useNamedReferences': true
|
||||||
|
});
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `strict`
|
||||||
|
|
||||||
|
The default value for the `strict` option is `false`. This means that `encode()` will encode any HTML text content you feed it, even if it contains any symbols that cause [parse errors](https://html.spec.whatwg.org/multipage/parsing.html#preprocessing-the-input-stream). To throw an error when such invalid HTML is encountered, set the `strict` option to `true`. This option makes it possible to use _he_ as part of HTML parsers and HTML validators.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Using the global default setting (defaults to `false`, i.e. error-tolerant mode):
|
||||||
|
he.encode('\x01');
|
||||||
|
// → ''
|
||||||
|
|
||||||
|
// Passing an `options` object to `encode`, to explicitly enable error-tolerant mode:
|
||||||
|
he.encode('\x01', {
|
||||||
|
'strict': false
|
||||||
|
});
|
||||||
|
// → ''
|
||||||
|
|
||||||
|
// Passing an `options` object to `encode`, to explicitly enable strict mode:
|
||||||
|
he.encode('\x01', {
|
||||||
|
'strict': true
|
||||||
|
});
|
||||||
|
// → Parse error
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `allowUnsafeSymbols`
|
||||||
|
|
||||||
|
The default value for the `allowUnsafeSymbols` option is `false`. This means that characters that are unsafe for use in HTML content (`&`, `<`, `>`, `"`, `'`, and `` ` ``) will be encoded. When set to `true`, only non-ASCII characters will be encoded. If the `encodeEverything` option is set to `true`, this option will be ignored.
|
||||||
|
|
||||||
|
```js
|
||||||
|
he.encode('foo © and & ampersand', {
|
||||||
|
'allowUnsafeSymbols': true
|
||||||
|
});
|
||||||
|
// → 'foo © and & ampersand'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Overriding default `encode` options globally
|
||||||
|
|
||||||
|
The global default setting can be overridden by modifying the `he.encode.options` object. This saves you from passing in an `options` object for every call to `encode` if you want to use the non-default setting.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Read the global default setting:
|
||||||
|
he.encode.options.useNamedReferences;
|
||||||
|
// → `false` by default
|
||||||
|
|
||||||
|
// Override the global default setting:
|
||||||
|
he.encode.options.useNamedReferences = true;
|
||||||
|
|
||||||
|
// Using the global default setting, which is now `true`:
|
||||||
|
he.encode('foo © bar ≠ baz 𝌆 qux');
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `he.decode(html, options)`
|
||||||
|
|
||||||
|
This function takes a string of HTML and decodes any named and numerical character references in it using [the algorithm described in section 12.2.4.69 of the HTML spec](https://html.spec.whatwg.org/multipage/syntax.html#tokenizing-character-references).
|
||||||
|
|
||||||
|
```js
|
||||||
|
he.decode('foo © bar ≠ baz 𝌆 qux');
|
||||||
|
// → 'foo © bar ≠ baz 𝌆 qux'
|
||||||
|
```
|
||||||
|
|
||||||
|
The `options` object is optional. It recognizes the following properties:
|
||||||
|
|
||||||
|
#### `isAttributeValue`
|
||||||
|
|
||||||
|
The default value for the `isAttributeValue` option is `false`. This means that `decode()` will decode the string as if it were used in [a text context in an HTML document](https://html.spec.whatwg.org/multipage/syntax.html#data-state). HTML has different rules for [parsing character references in attribute values](https://html.spec.whatwg.org/multipage/syntax.html#character-reference-in-attribute-value-state) — set this option to `true` to treat the input string as if it were used as an attribute value.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Using the global default setting (defaults to `false`, i.e. HTML text context):
|
||||||
|
he.decode('foo&bar');
|
||||||
|
// → 'foo&bar'
|
||||||
|
|
||||||
|
// Passing an `options` object to `decode`, to explicitly assume an HTML text context:
|
||||||
|
he.decode('foo&bar', {
|
||||||
|
'isAttributeValue': false
|
||||||
|
});
|
||||||
|
// → 'foo&bar'
|
||||||
|
|
||||||
|
// Passing an `options` object to `decode`, to explicitly assume an HTML attribute value context:
|
||||||
|
he.decode('foo&bar', {
|
||||||
|
'isAttributeValue': true
|
||||||
|
});
|
||||||
|
// → 'foo&bar'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `strict`
|
||||||
|
|
||||||
|
The default value for the `strict` option is `false`. This means that `decode()` will decode any HTML text content you feed it, even if it contains any entities that cause [parse errors](https://html.spec.whatwg.org/multipage/syntax.html#tokenizing-character-references). To throw an error when such invalid HTML is encountered, set the `strict` option to `true`. This option makes it possible to use _he_ as part of HTML parsers and HTML validators.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Using the global default setting (defaults to `false`, i.e. error-tolerant mode):
|
||||||
|
he.decode('foo&bar');
|
||||||
|
// → 'foo&bar'
|
||||||
|
|
||||||
|
// Passing an `options` object to `decode`, to explicitly enable error-tolerant mode:
|
||||||
|
he.decode('foo&bar', {
|
||||||
|
'strict': false
|
||||||
|
});
|
||||||
|
// → 'foo&bar'
|
||||||
|
|
||||||
|
// Passing an `options` object to `decode`, to explicitly enable strict mode:
|
||||||
|
he.decode('foo&bar', {
|
||||||
|
'strict': true
|
||||||
|
});
|
||||||
|
// → Parse error
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Overriding default `decode` options globally
|
||||||
|
|
||||||
|
The global default settings for the `decode` function can be overridden by modifying the `he.decode.options` object. This saves you from passing in an `options` object for every call to `decode` if you want to use a non-default setting.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Read the global default setting:
|
||||||
|
he.decode.options.isAttributeValue;
|
||||||
|
// → `false` by default
|
||||||
|
|
||||||
|
// Override the global default setting:
|
||||||
|
he.decode.options.isAttributeValue = true;
|
||||||
|
|
||||||
|
// Using the global default setting, which is now `true`:
|
||||||
|
he.decode('foo&bar');
|
||||||
|
// → 'foo&bar'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `he.escape(text)`
|
||||||
|
|
||||||
|
This function takes a string of text and escapes it for use in text contexts in XML or HTML documents. Only the following characters are escaped: `&`, `<`, `>`, `"`, `'`, and `` ` ``.
|
||||||
|
|
||||||
|
```js
|
||||||
|
he.escape('<img src=\'x\' onerror="prompt(1)">');
|
||||||
|
// → '<img src='x' onerror="prompt(1)">'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `he.unescape(html, options)`
|
||||||
|
|
||||||
|
`he.unescape` is an alias for `he.decode`. It takes a string of HTML and decodes any named and numerical character references in it.
|
||||||
|
|
||||||
|
### Using the `he` binary
|
||||||
|
|
||||||
|
To use the `he` binary in your shell, simply install _he_ globally using npm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g he
|
||||||
|
```
|
||||||
|
|
||||||
|
After that you will be able to encode/decode HTML entities from the command line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ he --encode 'föo ♥ bår 𝌆 baz'
|
||||||
|
föo ♥ bår 𝌆 baz
|
||||||
|
|
||||||
|
$ he --encode --use-named-refs 'föo ♥ bår 𝌆 baz'
|
||||||
|
föo ♥ bår 𝌆 baz
|
||||||
|
|
||||||
|
$ he --decode 'föo ♥ bår 𝌆 baz'
|
||||||
|
föo ♥ bår 𝌆 baz
|
||||||
|
```
|
||||||
|
|
||||||
|
Read a local text file, encode it for use in an HTML text context, and save the result to a new file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ he --encode < foo.txt > foo-escaped.html
|
||||||
|
```
|
||||||
|
|
||||||
|
Or do the same with an online text file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ curl -sL "http://git.io/HnfEaw" | he --encode > escaped.html
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, the opposite — read a local file containing a snippet of HTML in a text context, decode it back to plain text, and save the result to a new file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ he --decode < foo-escaped.html > foo.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Or do the same with an online HTML snippet:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ curl -sL "http://git.io/HnfEaw" | he --decode > decoded.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
See `he --help` for the full list of options.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
_he_ has been tested in at least:
|
||||||
|
|
||||||
|
* Chrome 27-50
|
||||||
|
* Firefox 3-45
|
||||||
|
* Safari 4-9
|
||||||
|
* Opera 10-12, 15–37
|
||||||
|
* IE 6–11
|
||||||
|
* Edge
|
||||||
|
* Narwhal 0.3.2
|
||||||
|
* Node.js v0.10, v0.12, v4, v5
|
||||||
|
* PhantomJS 1.9.0
|
||||||
|
* Rhino 1.7RC4
|
||||||
|
* RingoJS 0.8-0.11
|
||||||
|
|
||||||
|
## Unit tests & code coverage
|
||||||
|
|
||||||
|
After cloning this repository, run `npm install` to install the dependencies needed for he development and testing. You may want to install Istanbul _globally_ using `npm install istanbul -g`.
|
||||||
|
|
||||||
|
Once that’s done, you can run the unit tests in Node using `npm test` or `node tests/tests.js`. To run the tests in Rhino, Ringo, Narwhal, and web browsers as well, use `grunt test`.
|
||||||
|
|
||||||
|
To generate the code coverage report, use `grunt cover`.
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
Thanks to [Simon Pieters](https://simon.html5.org/) ([@zcorpan](https://twitter.com/zcorpan)) for the many suggestions.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [Mathias Bynens](https://mathiasbynens.be/) |
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
_he_ is available under the [MIT](https://mths.be/mit) license.
|
||||||
148
node_modules/he/bin/he
generated
vendored
Normal file
148
node_modules/he/bin/he
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var he = require('../he.js');
|
||||||
|
var strings = process.argv.splice(2);
|
||||||
|
var stdin = process.stdin;
|
||||||
|
var data;
|
||||||
|
var timeout;
|
||||||
|
var action;
|
||||||
|
var options = {};
|
||||||
|
var log = console.log;
|
||||||
|
|
||||||
|
var main = function() {
|
||||||
|
var option = strings[0];
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
if (/^(?:-h|--help|undefined)$/.test(option)) {
|
||||||
|
log(
|
||||||
|
'he v%s - https://mths.be/he',
|
||||||
|
he.version
|
||||||
|
);
|
||||||
|
log([
|
||||||
|
'\nUsage:\n',
|
||||||
|
'\the [--escape] string',
|
||||||
|
'\the [--encode] [--use-named-refs] [--everything] [--allow-unsafe] [--decimal] string',
|
||||||
|
'\the [--decode] [--attribute] [--strict] string',
|
||||||
|
'\the [-v | --version]',
|
||||||
|
'\the [-h | --help]',
|
||||||
|
'\nExamples:\n',
|
||||||
|
'\the --escape \\<img\\ src\\=\\\'x\\\'\\ onerror\\=\\"prompt\\(1\\)\\"\\>',
|
||||||
|
'\techo \'© 𝌆\' | he --decode'
|
||||||
|
].join('\n'));
|
||||||
|
return process.exit(option ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^(?:-v|--version)$/.test(option)) {
|
||||||
|
log('v%s', he.version);
|
||||||
|
return process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
strings.forEach(function(string) {
|
||||||
|
// Process options
|
||||||
|
if (string == '--escape') {
|
||||||
|
action = 'escape';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string == '--encode') {
|
||||||
|
action = 'encode';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string == '--use-named-refs') {
|
||||||
|
action = 'encode';
|
||||||
|
options.useNamedReferences = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string == '--everything') {
|
||||||
|
action = 'encode';
|
||||||
|
options.encodeEverything = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string == '--allow-unsafe') {
|
||||||
|
action = 'encode';
|
||||||
|
options.allowUnsafeSymbols = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string == '--decimal') {
|
||||||
|
action = 'encode';
|
||||||
|
options.decimal = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string == '--decode') {
|
||||||
|
action = 'decode';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string == '--attribute') {
|
||||||
|
action = 'decode';
|
||||||
|
options.isAttributeValue = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string == '--strict') {
|
||||||
|
action = 'decode';
|
||||||
|
options.strict = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Process string(s)
|
||||||
|
var result;
|
||||||
|
if (!action) {
|
||||||
|
log('Error: he requires at least one option and a string argument.');
|
||||||
|
log('Try `he --help` for more information.');
|
||||||
|
return process.exit(1);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
result = he[action](string, options);
|
||||||
|
log(result);
|
||||||
|
count++;
|
||||||
|
} catch(error) {
|
||||||
|
log(error.message + '\n');
|
||||||
|
log('Error: failed to %s.', action);
|
||||||
|
log('If you think this is a bug in he, please report it:');
|
||||||
|
log('https://github.com/mathiasbynens/he/issues/new');
|
||||||
|
log(
|
||||||
|
'\nStack trace using he@%s:\n',
|
||||||
|
he.version
|
||||||
|
);
|
||||||
|
log(error.stack);
|
||||||
|
return process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!count) {
|
||||||
|
log('Error: he requires a string argument.');
|
||||||
|
log('Try `he --help` for more information.');
|
||||||
|
return process.exit(1);
|
||||||
|
}
|
||||||
|
// Return with exit status 0 outside of the `forEach` loop, in case
|
||||||
|
// multiple strings were passed in.
|
||||||
|
return process.exit(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (stdin.isTTY) {
|
||||||
|
// handle shell arguments
|
||||||
|
main();
|
||||||
|
} else {
|
||||||
|
// Either the script is called from within a non-TTY context, or `stdin`
|
||||||
|
// content is being piped in.
|
||||||
|
if (!process.stdout.isTTY) {
|
||||||
|
// The script was called from a non-TTY context. This is a rather uncommon
|
||||||
|
// use case we don’t actively support. However, we don’t want the script
|
||||||
|
// to wait forever in such cases, so…
|
||||||
|
timeout = setTimeout(function() {
|
||||||
|
// …if no piped data arrived after a whole minute, handle shell
|
||||||
|
// arguments instead.
|
||||||
|
main();
|
||||||
|
}, 60000);
|
||||||
|
}
|
||||||
|
data = '';
|
||||||
|
stdin.on('data', function(chunk) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
stdin.on('end', function() {
|
||||||
|
strings.push(data.trim());
|
||||||
|
main();
|
||||||
|
});
|
||||||
|
stdin.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
}());
|
||||||
345
node_modules/he/he.js
generated
vendored
Normal file
345
node_modules/he/he.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
78
node_modules/he/man/he.1
generated
vendored
Normal file
78
node_modules/he/man/he.1
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
.Dd April 5, 2016
|
||||||
|
.Dt he 1
|
||||||
|
.Sh NAME
|
||||||
|
.Nm he
|
||||||
|
.Nd encode/decode HTML entities just like a browser would
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl -escape Ar string
|
||||||
|
.br
|
||||||
|
.Op Fl -encode Ar string
|
||||||
|
.br
|
||||||
|
.Op Fl -encode Fl -use-named-refs Fl -everything Fl -allow-unsafe Ar string
|
||||||
|
.br
|
||||||
|
.Op Fl -decode Ar string
|
||||||
|
.br
|
||||||
|
.Op Fl -decode Fl -attribute Ar string
|
||||||
|
.br
|
||||||
|
.Op Fl -decode Fl -strict Ar string
|
||||||
|
.br
|
||||||
|
.Op Fl v | -version
|
||||||
|
.br
|
||||||
|
.Op Fl h | -help
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
encodes/decodes HTML entities in strings just like a browser would.
|
||||||
|
.Sh OPTIONS
|
||||||
|
.Bl -ohang -offset
|
||||||
|
.It Sy "--escape"
|
||||||
|
Take a string of text and escape it for use in text contexts in XML or HTML documents. Only the following characters are escaped: `&`, `<`, `>`, `"`, and `'`.
|
||||||
|
.It Sy "--encode"
|
||||||
|
Take a string of text and encode any symbols that aren't printable ASCII symbols and that can be replaced with character references. For example, it would turn `©` into `©`, but it wouldn't turn `+` into `+` since there is no point in doing so. Additionally, it replaces any remaining non-ASCII symbols with a hexadecimal escape sequence (e.g. `𝌆`). The return value of this function is always valid HTML.
|
||||||
|
.It Sy "--encode --use-named-refs"
|
||||||
|
Enable the use of named character references (like `©`) in the output. If compatibility with older browsers is a concern, don't use this option.
|
||||||
|
.It Sy "--encode --everything"
|
||||||
|
Encode every symbol in the input string, even safe printable ASCII symbols.
|
||||||
|
.It Sy "--encode --allow-unsafe"
|
||||||
|
Encode non-ASCII characters only. This leaves unsafe HTML/XML symbols like `&`, `<`, `>`, `"`, and `'` intact.
|
||||||
|
.It Sy "--encode --decimal"
|
||||||
|
Use decimal digits rather than hexadecimal digits for encoded character references, e.g. output `©` instead of `©`.
|
||||||
|
.It Sy "--decode"
|
||||||
|
Takes a string of HTML and decode any named and numerical character references in it using the algorithm described in the HTML spec.
|
||||||
|
.It Sy "--decode --attribute"
|
||||||
|
Parse the input as if it was an HTML attribute value rather than a string in an HTML text content.
|
||||||
|
.It Sy "--decode --strict"
|
||||||
|
Throw an error if an invalid character reference is encountered.
|
||||||
|
.It Sy "-v, --version"
|
||||||
|
Print he's version.
|
||||||
|
.It Sy "-h, --help"
|
||||||
|
Show the help screen.
|
||||||
|
.El
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
The
|
||||||
|
.Nm he
|
||||||
|
utility exits with one of the following values:
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width flag -compact
|
||||||
|
.It Li 0
|
||||||
|
.Nm
|
||||||
|
did what it was instructed to do successfully; either it encoded/decoded the input and printed the result, or it printed the version or usage message.
|
||||||
|
.It Li 1
|
||||||
|
.Nm
|
||||||
|
encountered an error.
|
||||||
|
.El
|
||||||
|
.Sh EXAMPLES
|
||||||
|
.Bl -ohang -offset
|
||||||
|
.It Sy "he --escape '<script>alert(1)</script>'"
|
||||||
|
Print an escaped version of the given string that is safe for use in HTML text contexts, escaping only `&`, `<`, `>`, `"`, and `'`.
|
||||||
|
.It Sy "he --decode '©𝌆'"
|
||||||
|
Print the decoded version of the given HTML string.
|
||||||
|
.It Sy "echo\ '©𝌆'\ |\ he --decode"
|
||||||
|
Print the decoded version of the HTML string that gets piped in.
|
||||||
|
.El
|
||||||
|
.Sh BUGS
|
||||||
|
he's bug tracker is located at <https://github.com/mathiasbynens/he/issues>.
|
||||||
|
.Sh AUTHOR
|
||||||
|
Mathias Bynens <https://mathiasbynens.be/>
|
||||||
|
.Sh WWW
|
||||||
|
<https://mths.be/he>
|
||||||
58
node_modules/he/package.json
generated
vendored
Normal file
58
node_modules/he/package.json
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"name": "he",
|
||||||
|
"version": "1.2.0",
|
||||||
|
"description": "A robust HTML entities encoder/decoder with full Unicode support.",
|
||||||
|
"homepage": "https://mths.be/he",
|
||||||
|
"main": "he.js",
|
||||||
|
"bin": "bin/he",
|
||||||
|
"keywords": [
|
||||||
|
"string",
|
||||||
|
"entities",
|
||||||
|
"entity",
|
||||||
|
"html",
|
||||||
|
"encode",
|
||||||
|
"decode",
|
||||||
|
"unicode"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"name": "Mathias Bynens",
|
||||||
|
"url": "https://mathiasbynens.be/"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/mathiasbynens/he.git"
|
||||||
|
},
|
||||||
|
"bugs": "https://github.com/mathiasbynens/he/issues",
|
||||||
|
"files": [
|
||||||
|
"LICENSE-MIT.txt",
|
||||||
|
"he.js",
|
||||||
|
"bin/",
|
||||||
|
"man/"
|
||||||
|
],
|
||||||
|
"directories": {
|
||||||
|
"bin": "bin",
|
||||||
|
"man": "man",
|
||||||
|
"test": "tests"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "node tests/tests.js",
|
||||||
|
"build": "grunt build"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"codecov.io": "^0.1.6",
|
||||||
|
"grunt": "^0.4.5",
|
||||||
|
"grunt-cli": "^1.3.1",
|
||||||
|
"grunt-shell": "^1.1.1",
|
||||||
|
"grunt-template": "^0.2.3",
|
||||||
|
"istanbul": "^0.4.2",
|
||||||
|
"jsesc": "^1.0.0",
|
||||||
|
"lodash": "^4.8.2",
|
||||||
|
"qunit-extras": "^1.4.5",
|
||||||
|
"qunitjs": "~1.11.0",
|
||||||
|
"regenerate": "^1.2.1",
|
||||||
|
"regexgen": "^1.3.0",
|
||||||
|
"requirejs": "^2.1.22",
|
||||||
|
"sort-object": "^3.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
21
node_modules/vue-template-compiler/LICENSE
generated
vendored
Normal file
21
node_modules/vue-template-compiler/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-present, Yuxi (Evan) You
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
162
node_modules/vue-template-compiler/README.md
generated
vendored
Normal file
162
node_modules/vue-template-compiler/README.md
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# vue-template-compiler
|
||||||
|
|
||||||
|
> This package is auto-generated. For pull requests please see [src/platforms/web/entry-compiler.js](https://github.com/vuejs/vue/tree/dev/src/platforms/web/entry-compiler.js).
|
||||||
|
|
||||||
|
This package can be used to pre-compile Vue 2.0 templates into render functions to avoid runtime-compilation overhead and CSP restrictions. In most cases you should be using it with [`vue-loader`](https://github.com/vuejs/vue-loader), you will only need it separately if you are writing build tools with very specific needs.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
npm install vue-template-compiler
|
||||||
|
```
|
||||||
|
|
||||||
|
``` js
|
||||||
|
const compiler = require('vue-template-compiler')
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### compiler.compile(template, [options])
|
||||||
|
|
||||||
|
Compiles a template string and returns compiled JavaScript code. The returned result is an object of the following format:
|
||||||
|
|
||||||
|
``` js
|
||||||
|
{
|
||||||
|
ast: ?ASTElement, // parsed template elements to AST
|
||||||
|
render: string, // main render function code
|
||||||
|
staticRenderFns: Array<string>, // render code for static sub trees, if any
|
||||||
|
errors: Array<string> // template syntax errors, if any
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note the returned function code uses `with` and thus cannot be used in strict mode code.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
- `outputSourceRange` *new in 2.6*
|
||||||
|
- Type: `boolean`
|
||||||
|
- Default: `false`
|
||||||
|
|
||||||
|
Set this to true will cause the `errors` returned in the compiled result become objects in the form of `{ msg, start, end }`. The `start` and `end` properties are numbers that mark the code range of the error source in the template. This can be passed on to the `compiler.generateCodeFrame` API to generate a code frame for the error.
|
||||||
|
|
||||||
|
- `whitespace`
|
||||||
|
- Type: `string`
|
||||||
|
- Valid values: `'preserve' | 'condense'`
|
||||||
|
- Default: `'preserve'`
|
||||||
|
|
||||||
|
The default value `'preserve'` handles whitespaces as follows:
|
||||||
|
|
||||||
|
- A whitespace-only text node between element tags is condensed into a single space.
|
||||||
|
- All other whitespaces are preserved as-is.
|
||||||
|
|
||||||
|
If set to `'condense'`:
|
||||||
|
|
||||||
|
- A whitespace-only text node between element tags is removed if it contains new lines. Otherwise, it is condensed into a single space.
|
||||||
|
- Consecutive whitespaces inside a non-whitespace-only text node are condensed into a single space.
|
||||||
|
|
||||||
|
Using condense mode will result in smaller compiled code size and slightly improved performance. However, it will produce minor visual layout differences compared to plain HTML in certain cases.
|
||||||
|
|
||||||
|
**This option does not affect the `<pre>` tag.**
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``` html
|
||||||
|
<!-- source -->
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
foo
|
||||||
|
</span> <span>bar</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- whitespace: 'preserve' -->
|
||||||
|
<div> <span>
|
||||||
|
foo
|
||||||
|
</span> <span>bar</span> </div>
|
||||||
|
|
||||||
|
<!-- whitespace: 'condense' -->
|
||||||
|
<div><span> foo </span> <span>bar</span></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
- `modules`
|
||||||
|
|
||||||
|
It's possible to hook into the compilation process to support custom template features. **However, beware that by injecting custom compile-time modules, your templates will not work with other build tools built on standard built-in modules, e.g `vue-loader` and `vueify`.**
|
||||||
|
|
||||||
|
An array of compiler modules. For details on compiler modules, refer to the `ModuleOptions` type in [flow declarations](https://github.com/vuejs/vue/blob/dev/flow/compiler.js#L47-L59) and the [built-in modules](https://github.com/vuejs/vue/tree/dev/src/platforms/web/compiler/modules).
|
||||||
|
|
||||||
|
- `directives`
|
||||||
|
|
||||||
|
An object where the key is the directive name and the value is a function that transforms an template AST node. For example:
|
||||||
|
|
||||||
|
``` js
|
||||||
|
compiler.compile('<div v-test></div>', {
|
||||||
|
directives: {
|
||||||
|
test (node, directiveMeta) {
|
||||||
|
// transform node based on directiveMeta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, a compile-time directive will extract the directive and the directive will not be present at runtime. If you want the directive to also be handled by a runtime definition, return `true` in the transform function.
|
||||||
|
|
||||||
|
Refer to the implementation of some [built-in compile-time directives](https://github.com/vuejs/vue/tree/dev/src/platforms/web/compiler/directives).
|
||||||
|
|
||||||
|
- `preserveWhitespace` **Deprecated since 2.6**
|
||||||
|
- Type: `boolean`
|
||||||
|
- Default: `true`
|
||||||
|
|
||||||
|
By default, the compiled render function preserves all whitespace characters between HTML tags. If set to `false`, whitespace between tags will be ignored. This can result in slightly better performance but may affect layout for inline elements.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### compiler.compileToFunctions(template)
|
||||||
|
|
||||||
|
Similar to `compiler.compile`, but directly returns instantiated functions:
|
||||||
|
|
||||||
|
``` js
|
||||||
|
{
|
||||||
|
render: Function,
|
||||||
|
staticRenderFns: Array<Function>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is only useful at runtime with pre-configured builds, so it doesn't accept any compile-time options. In addition, this method uses `new Function()` so it is not CSP-compliant.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### compiler.ssrCompile(template, [options])
|
||||||
|
|
||||||
|
> 2.4.0+
|
||||||
|
|
||||||
|
Same as `compiler.compile` but generates SSR-specific render function code by optimizing parts of the template into string concatenation in order to improve SSR performance.
|
||||||
|
|
||||||
|
This is used by default in `vue-loader@>=12` and can be disabled using the [`optimizeSSR`](https://vue-loader.vuejs.org/en/options.html#optimizessr) option.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### compiler.ssrCompileToFunctions(template)
|
||||||
|
|
||||||
|
> 2.4.0+
|
||||||
|
|
||||||
|
Same as `compiler.compileToFunction` but generates SSR-specific render function code by optimizing parts of the template into string concatenation in order to improve SSR performance.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### compiler.parseComponent(file, [options])
|
||||||
|
|
||||||
|
Parse a SFC (single-file component, or `*.vue` file) into a descriptor (refer to the `SFCDescriptor` type in [flow declarations](https://github.com/vuejs/vue/blob/dev/flow/compiler.js)). This is used in SFC build tools like `vue-loader` and `vueify`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### compiler.generateCodeFrame(template, start, end)
|
||||||
|
|
||||||
|
Generate a code frame that highlights the part in `template` defined by `start` and `end`. Useful for error reporting in higher-level tooling.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
#### `pad`
|
||||||
|
|
||||||
|
`pad` is useful when you are piping the extracted content into other pre-processors, as you will get correct line numbers or character indices if there are any syntax errors.
|
||||||
|
|
||||||
|
- with `{ pad: "line" }`, the extracted content for each block will be prefixed with one newline for each line in the leading content from the original file to ensure that the line numbers align with the original file.
|
||||||
|
- with `{ pad: "space" }`, the extracted content for each block will be prefixed with one space for each character in the leading content from the original file to ensure that the character count remains the same as the original file.
|
||||||
7140
node_modules/vue-template-compiler/browser.js
generated
vendored
Normal file
7140
node_modules/vue-template-compiler/browser.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
6670
node_modules/vue-template-compiler/build.js
generated
vendored
Normal file
6670
node_modules/vue-template-compiler/build.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
32
node_modules/vue-template-compiler/index.js
generated
vendored
Normal file
32
node_modules/vue-template-compiler/index.js
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
try {
|
||||||
|
var vueVersion = require('vue').version
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
var packageName = require('./package.json').name
|
||||||
|
var packageVersion = require('./package.json').version
|
||||||
|
if (vueVersion && vueVersion !== packageVersion) {
|
||||||
|
var vuePath = require.resolve('vue')
|
||||||
|
var packagePath = require.resolve('./package.json')
|
||||||
|
throw new Error(
|
||||||
|
'\n\nVue packages version mismatch:\n\n' +
|
||||||
|
'- vue@' +
|
||||||
|
vueVersion +
|
||||||
|
' (' +
|
||||||
|
vuePath +
|
||||||
|
')\n' +
|
||||||
|
'- ' +
|
||||||
|
packageName +
|
||||||
|
'@' +
|
||||||
|
packageVersion +
|
||||||
|
' (' +
|
||||||
|
packagePath +
|
||||||
|
')\n\n' +
|
||||||
|
'This may cause things to work incorrectly. Make sure to use the same version for both.\n' +
|
||||||
|
'If you are using vue-loader@>=10.0, simply update vue-template-compiler.\n' +
|
||||||
|
'If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump ' +
|
||||||
|
packageName +
|
||||||
|
' to the latest.\n'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = require('./build')
|
||||||
35
node_modules/vue-template-compiler/package.json
generated
vendored
Normal file
35
node_modules/vue-template-compiler/package.json
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "vue-template-compiler",
|
||||||
|
"version": "2.7.16",
|
||||||
|
"description": "template compiler for Vue 2.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"unpkg": "browser.js",
|
||||||
|
"jsdelivr": "browser.js",
|
||||||
|
"browser": "browser.js",
|
||||||
|
"types": "types/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"types/*.d.ts",
|
||||||
|
"*.js"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/vuejs/vue.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"vue",
|
||||||
|
"compiler"
|
||||||
|
],
|
||||||
|
"author": "Evan You",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/vuejs/vue/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"de-indent": "^1.0.2",
|
||||||
|
"he": "^1.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vue": "file:../.."
|
||||||
|
}
|
||||||
|
}
|
||||||
247
node_modules/vue-template-compiler/types/index.d.ts
generated
vendored
Normal file
247
node_modules/vue-template-compiler/types/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
import { VNode } from 'vue'
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Template compilation options / results
|
||||||
|
*/
|
||||||
|
interface CompilerOptions {
|
||||||
|
modules?: ModuleOptions[]
|
||||||
|
directives?: Record<string, DirectiveFunction>
|
||||||
|
preserveWhitespace?: boolean
|
||||||
|
whitespace?: 'preserve' | 'condense'
|
||||||
|
outputSourceRange?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CompilerOptionsWithSourceRange extends CompilerOptions {
|
||||||
|
outputSourceRange: true
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorWithRange {
|
||||||
|
msg: string
|
||||||
|
start: number
|
||||||
|
end: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CompiledResult<ErrorType> {
|
||||||
|
ast: ASTElement | undefined
|
||||||
|
render: string
|
||||||
|
staticRenderFns: string[]
|
||||||
|
errors: ErrorType[]
|
||||||
|
tips: ErrorType[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CompiledResultFunctions {
|
||||||
|
render: () => VNode
|
||||||
|
staticRenderFns: (() => VNode)[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ModuleOptions {
|
||||||
|
preTransformNode: (el: ASTElement) => ASTElement | undefined
|
||||||
|
transformNode: (el: ASTElement) => ASTElement | undefined
|
||||||
|
postTransformNode: (el: ASTElement) => void
|
||||||
|
genData: (el: ASTElement) => string
|
||||||
|
transformCode?: (el: ASTElement, code: string) => string
|
||||||
|
staticKeys?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
type DirectiveFunction = (node: ASTElement, directiveMeta: ASTDirective) => void
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AST Types
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* - 0: FALSE - whole sub tree un-optimizable
|
||||||
|
* - 1: FULL - whole sub tree optimizable
|
||||||
|
* - 2: SELF - self optimizable but has some un-optimizable children
|
||||||
|
* - 3: CHILDREN - self un-optimizable but have fully optimizable children
|
||||||
|
* - 4: PARTIAL - self un-optimizable with some un-optimizable children
|
||||||
|
*/
|
||||||
|
export type SSROptimizability = 0 | 1 | 2 | 3 | 4
|
||||||
|
|
||||||
|
export interface ASTModifiers {
|
||||||
|
[key: string]: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ASTIfCondition {
|
||||||
|
exp: string | undefined
|
||||||
|
block: ASTElement
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ASTElementHandler {
|
||||||
|
value: string
|
||||||
|
params?: any[]
|
||||||
|
modifiers: ASTModifiers | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ASTElementHandlers {
|
||||||
|
[key: string]: ASTElementHandler | ASTElementHandler[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ASTDirective {
|
||||||
|
name: string
|
||||||
|
rawName: string
|
||||||
|
value: string
|
||||||
|
arg: string | undefined
|
||||||
|
modifiers: ASTModifiers | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ASTNode = ASTElement | ASTText | ASTExpression
|
||||||
|
|
||||||
|
export interface ASTElement {
|
||||||
|
type: 1
|
||||||
|
tag: string
|
||||||
|
attrsList: { name: string; value: any }[]
|
||||||
|
attrsMap: Record<string, any>
|
||||||
|
parent: ASTElement | undefined
|
||||||
|
children: ASTNode[]
|
||||||
|
|
||||||
|
processed?: true
|
||||||
|
|
||||||
|
static?: boolean
|
||||||
|
staticRoot?: boolean
|
||||||
|
staticInFor?: boolean
|
||||||
|
staticProcessed?: boolean
|
||||||
|
hasBindings?: boolean
|
||||||
|
|
||||||
|
text?: string
|
||||||
|
attrs?: { name: string; value: any }[]
|
||||||
|
props?: { name: string; value: string }[]
|
||||||
|
plain?: boolean
|
||||||
|
pre?: true
|
||||||
|
ns?: string
|
||||||
|
|
||||||
|
component?: string
|
||||||
|
inlineTemplate?: true
|
||||||
|
transitionMode?: string | null
|
||||||
|
slotName?: string
|
||||||
|
slotTarget?: string
|
||||||
|
slotScope?: string
|
||||||
|
scopedSlots?: Record<string, ASTElement>
|
||||||
|
|
||||||
|
ref?: string
|
||||||
|
refInFor?: boolean
|
||||||
|
|
||||||
|
if?: string
|
||||||
|
ifProcessed?: boolean
|
||||||
|
elseif?: string
|
||||||
|
else?: true
|
||||||
|
ifConditions?: ASTIfCondition[]
|
||||||
|
|
||||||
|
for?: string
|
||||||
|
forProcessed?: boolean
|
||||||
|
key?: string
|
||||||
|
alias?: string
|
||||||
|
iterator1?: string
|
||||||
|
iterator2?: string
|
||||||
|
|
||||||
|
staticClass?: string
|
||||||
|
classBinding?: string
|
||||||
|
staticStyle?: string
|
||||||
|
styleBinding?: string
|
||||||
|
events?: ASTElementHandlers
|
||||||
|
nativeEvents?: ASTElementHandlers
|
||||||
|
|
||||||
|
transition?: string | true
|
||||||
|
transitionOnAppear?: boolean
|
||||||
|
|
||||||
|
model?: {
|
||||||
|
value: string
|
||||||
|
callback: string
|
||||||
|
expression: string
|
||||||
|
}
|
||||||
|
|
||||||
|
directives?: ASTDirective[]
|
||||||
|
|
||||||
|
forbidden?: true
|
||||||
|
once?: true
|
||||||
|
onceProcessed?: boolean
|
||||||
|
wrapData?: (code: string) => string
|
||||||
|
wrapListeners?: (code: string) => string
|
||||||
|
|
||||||
|
// 2.4 ssr optimization
|
||||||
|
ssrOptimizability?: SSROptimizability
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ASTExpression {
|
||||||
|
type: 2
|
||||||
|
expression: string
|
||||||
|
text: string
|
||||||
|
tokens: (string | Record<string, any>)[]
|
||||||
|
static?: boolean
|
||||||
|
// 2.4 ssr optimization
|
||||||
|
ssrOptimizability?: SSROptimizability
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ASTText {
|
||||||
|
type: 3
|
||||||
|
text: string
|
||||||
|
static?: boolean
|
||||||
|
isComment?: boolean
|
||||||
|
// 2.4 ssr optimization
|
||||||
|
ssrOptimizability?: SSROptimizability
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SFC parser related types
|
||||||
|
*/
|
||||||
|
interface SFCParserOptions {
|
||||||
|
pad?: true | 'line' | 'space'
|
||||||
|
deindent?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SFCBlock {
|
||||||
|
type: string
|
||||||
|
content: string
|
||||||
|
attrs: Record<string, string>
|
||||||
|
start?: number
|
||||||
|
end?: number
|
||||||
|
lang?: string
|
||||||
|
src?: string
|
||||||
|
scoped?: boolean
|
||||||
|
module?: string | boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SFCDescriptor {
|
||||||
|
template: SFCBlock | undefined
|
||||||
|
script: SFCBlock | undefined
|
||||||
|
styles: SFCBlock[]
|
||||||
|
customBlocks: SFCBlock[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exposed functions
|
||||||
|
*/
|
||||||
|
export function compile(
|
||||||
|
template: string,
|
||||||
|
options: CompilerOptionsWithSourceRange
|
||||||
|
): CompiledResult<ErrorWithRange>
|
||||||
|
|
||||||
|
export function compile(
|
||||||
|
template: string,
|
||||||
|
options?: CompilerOptions
|
||||||
|
): CompiledResult<string>
|
||||||
|
|
||||||
|
export function compileToFunctions(template: string): CompiledResultFunctions
|
||||||
|
|
||||||
|
export function ssrCompile(
|
||||||
|
template: string,
|
||||||
|
options: CompilerOptionsWithSourceRange
|
||||||
|
): CompiledResult<ErrorWithRange>
|
||||||
|
|
||||||
|
export function ssrCompile(
|
||||||
|
template: string,
|
||||||
|
options?: CompilerOptions
|
||||||
|
): CompiledResult<string>
|
||||||
|
|
||||||
|
export function ssrCompileToFunctions(template: string): CompiledResultFunctions
|
||||||
|
|
||||||
|
export function parseComponent(
|
||||||
|
file: string,
|
||||||
|
options?: SFCParserOptions
|
||||||
|
): SFCDescriptor
|
||||||
|
|
||||||
|
export function generateCodeFrame(
|
||||||
|
template: string,
|
||||||
|
start: number,
|
||||||
|
end: number
|
||||||
|
): string
|
||||||
25
package-lock.json
generated
25
package-lock.json
generated
@ -7,7 +7,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dcloudio/uni-ui": "^1.5.7",
|
"@dcloudio/uni-ui": "^1.5.7",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"uview-ui": "^2.0.38"
|
"uview-ui": "^2.0.38",
|
||||||
|
"vue-template-compiler": "^2.7.16"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"sass": "^1.86.3"
|
"sass": "^1.86.3"
|
||||||
@ -365,6 +366,11 @@
|
|||||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/de-indent": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
@ -393,6 +399,14 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/he": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||||
|
"bin": {
|
||||||
|
"he": "bin/he"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/immutable": {
|
"node_modules/immutable": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz",
|
||||||
@ -539,6 +553,15 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"HBuilderX": "^3.1.0"
|
"HBuilderX": "^3.1.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue-template-compiler": {
|
||||||
|
"version": "2.7.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
|
||||||
|
"integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"de-indent": "^1.0.2",
|
||||||
|
"he": "^1.2.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dcloudio/uni-ui": "^1.5.7",
|
"@dcloudio/uni-ui": "^1.5.7",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"uview-ui": "^2.0.38"
|
"uview-ui": "^2.0.38",
|
||||||
|
"vue-template-compiler": "^2.7.16"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vue-cli-service serve",
|
"dev": "vue-cli-service serve",
|
||||||
|
|||||||
75
pages.json
75
pages.json
@ -45,55 +45,83 @@
|
|||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "个人中心"
|
"navigationBarTitleText": "个人中心"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "pages/user/memberSelect",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "会员等级选择",
|
|
||||||
"navigationBarBackgroundColor": "#F8F9FC"
|
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
|
"subPackages": [
|
||||||
{
|
{
|
||||||
"path": "pages/scan/book-records",
|
"root": "pkgUpload",
|
||||||
"style": {
|
"pages": [
|
||||||
"navigationBarTitleText": "上书记录"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "pages/isbn-upload/index",
|
"path": "isbn-upload/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "Isbn-上传",
|
"navigationBarTitleText": "Isbn-上传",
|
||||||
"enablePullDownRefresh": true
|
"enablePullDownRefresh": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/title-upload/index",
|
"path": "title-upload/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "仅书名-上传"
|
"navigationBarTitleText": "仅书名-上传"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/shelf/management",
|
"path": "photo-upload/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "货架管理"
|
"navigationBarTitleText": "无ISBN-上传",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/goods/index",
|
"root": "pkgManage",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "goods/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "商品管理",
|
"navigationBarTitleText": "商品管理",
|
||||||
"enablePullDownRefresh": true
|
"enablePullDownRefresh": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/clone-tool/index",
|
"path": "shelf/management",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "货架管理"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "clone-tool/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "商品克隆工具",
|
"navigationBarTitleText": "商品克隆工具",
|
||||||
"enablePullDownRefresh": true
|
"enablePullDownRefresh": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"subPackages": [
|
},
|
||||||
|
{
|
||||||
|
"root": "pkgUser",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "memberSelect",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "会员等级选择",
|
||||||
|
"navigationBarBackgroundColor": "#F8F9FC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "dispatch-management",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "下发管理"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "book-records",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "上书记录"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"root": "pages/warehouse",
|
"root": "pages/warehouse",
|
||||||
"pages": [
|
"pages": [
|
||||||
@ -112,6 +140,13 @@
|
|||||||
"animationType": "slide-in-right"
|
"animationType": "slide-in-right"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "order-query",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "仓库订单查询",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -21,12 +21,23 @@
|
|||||||
</view>
|
</view>
|
||||||
<u-icon name="arrow-right" size="18"></u-icon>
|
<u-icon name="arrow-right" size="18"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view class="card" @click="goToOrderQuery">
|
||||||
|
<view class="card-left">
|
||||||
|
<text class="card-title">仓库订单</text>
|
||||||
|
<text class="card-desc">查看仓库订单</text>
|
||||||
|
</view>
|
||||||
|
<u-icon name="arrow-right" size="18"></u-icon>
|
||||||
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { checkKwfwMember } from '@/components/MemberBookCheck.js'
|
import {
|
||||||
|
checkKwfwMember
|
||||||
|
} from '@/components/MemberBookCheck.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
@ -45,7 +56,7 @@ export default {
|
|||||||
|
|
||||||
if (isMember) {
|
if (isMember) {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/clone-tool/index'
|
url: '/pkgManage/clone-tool/index'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -55,6 +66,21 @@ export default {
|
|||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
goToOrderQuery() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/warehouse/order-query'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
goToScanBarcode() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/warehouse/scan-barcode'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
goToBookAllocation() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/book-allocation/index'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,318 +1,140 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page-container">
|
<view class="page-container">
|
||||||
<!-- 选项卡切换 -->
|
<!-- 上传入口 -->
|
||||||
<view class="tab-bar">
|
<view class="upload-entrance">
|
||||||
<view class="nav-tab" :class="{ active: activeTab === 'isbn' }" @click="switchTab('isbn')">
|
<view class="entrance-title">请选择上传方式</view>
|
||||||
ISBN-上传
|
<view class="entrance-cards">
|
||||||
|
<view class="entrance-card" @click="navigateToUpload('isbn')">
|
||||||
|
<view class="card-icon isbn-icon">📚</view>
|
||||||
|
<view class="card-content">
|
||||||
|
<text class="card-title">ISBN-上传</text>
|
||||||
|
<text class="card-desc">扫描或输入ISBN快速上架</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- <view class="nav-tab" :class="{ active: activeTab === 'title' }" @click="switchTab('title')">
|
<view class="card-arrow">›</view>
|
||||||
仅书名-上传
|
</view>
|
||||||
</view> -->
|
<view class="entrance-card" @click="navigateToUpload('photo')">
|
||||||
<view class="nav-tab" :class="{ active: activeTab === 'photo' }" @click="switchTab('photo')">
|
<view class="card-icon photo-icon">📷</view>
|
||||||
无ISBN-上传
|
<view class="card-content">
|
||||||
|
<text class="card-title">无ISBN-上传</text>
|
||||||
|
<text class="card-desc">拍照识别书籍信息上架</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-arrow">›</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="form-content">
|
|
||||||
<isbn-upload-form v-if="activeTab === 'isbn'" :selectedWarehouse="selectedWarehouse" ref="isbnUploadForm"></isbn-upload-form>
|
|
||||||
<!-- <title-upload-form v-if="activeTab === 'title'" :selectedWarehouse="selectedWarehouse"></title-upload-form> -->
|
|
||||||
<photo-upload-form v-if="activeTab === 'photo'" :selectedWarehouse="selectedWarehouse" ref="photoUploadForm"></photo-upload-form>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import IsbnUpload from '@/pages/isbn-upload/index.vue'
|
|
||||||
import titleUpload from '@/pages/title-upload/index.vue'
|
|
||||||
import photoUpload from '@/pages/photo-upload/index.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
|
||||||
'isbn-upload-form': IsbnUpload,
|
|
||||||
'title-upload-form': titleUpload,
|
|
||||||
'photo-upload-form': photoUpload,
|
|
||||||
},
|
|
||||||
options: {
|
options: {
|
||||||
enablePullDownRefresh: true,
|
enablePullDownRefresh: false,
|
||||||
backgroundTextStyle: 'dark'
|
backgroundTextStyle: 'dark'
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentTab: 'isbn',
|
selectedWarehouse: null,
|
||||||
activeTab: 'isbn', // 默认选中ISBN上传
|
|
||||||
selectedWarehouse: '',
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
// 页面加载时获取仓库数据
|
|
||||||
this.selectedWarehouse = uni.getStorageSync("selectedWarehouse") || null;
|
|
||||||
},
|
|
||||||
onShow() {
|
onShow() {
|
||||||
// 每次页面显示时都重新获取仓库
|
// 每次页面显示时都重新获取仓库
|
||||||
this.selectedWarehouse = uni.getStorageSync("selectedWarehouse") || null;
|
this.selectedWarehouse = uni.getStorageSync("selectedWarehouse") || null;
|
||||||
},
|
},
|
||||||
async onLoad() {
|
|
||||||
// const cookiesResponse = await this.getCookies("18904056801", "Long6166@")
|
|
||||||
// console.log(cookiesResponse.cookies.PHPSESSID)
|
|
||||||
// uni.setStorageSync('cookies', cookiesResponse.cookies.PHPSESSID);
|
|
||||||
// console.log('Cookies已保存到本地存储', cookiesResponse.cookies.PHPSESSID);
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
switchTab(tab) {
|
// 导航到上传页面
|
||||||
this.activeTab = tab
|
navigateToUpload(type) {
|
||||||
this.selectedWarehouse = uni.getStorageSync("selectedWarehouse") || null;
|
if (type === 'isbn') {
|
||||||
},
|
uni.navigateTo({
|
||||||
// 纯浏览器端 JavaScript 实现
|
url: '/pkgUpload/isbn-upload/index'
|
||||||
async getCookies(username, password) {
|
|
||||||
try {
|
|
||||||
// 第一步:先发送一个GET请求获取初始会话Cookie
|
|
||||||
const initResponse = await this.uniRequestPromise({
|
|
||||||
url: 'https://login.kongfz.com/Pc/Login/account',
|
|
||||||
method: 'GET'
|
|
||||||
});
|
});
|
||||||
|
} else if (type === 'photo') {
|
||||||
// 提取初始响应中的Cookie
|
uni.navigateTo({
|
||||||
const initCookies = this.extractCookiesFromHeaders(initResponse.header);
|
url: '/pkgUpload/photo-upload/index'
|
||||||
// 第二步:发送登录请求,携带用户名和密码
|
|
||||||
const loginData = {
|
|
||||||
loginName: username,
|
|
||||||
loginPass: password
|
|
||||||
};
|
|
||||||
|
|
||||||
const loginResponse = await this.uniRequestPromise({
|
|
||||||
url: 'https://login.kongfz.com/Pc/Login/account',
|
|
||||||
method: 'POST',
|
|
||||||
data: loginData,
|
|
||||||
header: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
'Cookie': this.formatCookieHeader(initCookies)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 提取登录响应中的Cookie
|
|
||||||
const loginCookies = this.extractCookiesFromHeaders(loginResponse.header);
|
|
||||||
|
|
||||||
// 合并所有Cookie
|
|
||||||
const allCookies = {
|
|
||||||
...initCookies,
|
|
||||||
...loginCookies
|
|
||||||
};
|
|
||||||
|
|
||||||
// 检查登录是否成功(根据实际返回判断)
|
|
||||||
const isLoginSuccess = this.checkLoginSuccess(loginResponse.data);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: isLoginSuccess,
|
|
||||||
cookies: allCookies,
|
|
||||||
responseData: loginResponse.data // 包含服务器返回的原始数据
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('登录请求失败:', error);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error.message || '登录请求发生错误'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 将 uni.request 转换为 Promise 形式
|
|
||||||
*/
|
|
||||||
uniRequestPromise(options) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
uni.request({
|
|
||||||
...options,
|
|
||||||
success: (res) => resolve(res),
|
|
||||||
fail: (err) => reject(err)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从响应头中提取 Cookies
|
|
||||||
*/
|
|
||||||
extractCookiesFromHeaders(headers) {
|
|
||||||
const cookies = {};
|
|
||||||
const cookieHeaders = headers['Set-Cookie'] || headers['set-cookie'];
|
|
||||||
|
|
||||||
if (!cookieHeaders) return cookies;
|
|
||||||
|
|
||||||
// 处理可能是数组或字符串的 Cookie 头
|
|
||||||
const cookieList = Array.isArray(cookieHeaders) ?
|
|
||||||
cookieHeaders : [cookieHeaders];
|
|
||||||
|
|
||||||
cookieList.forEach(cookieStr => {
|
|
||||||
// 提取 cookie 名值对(忽略路径、过期时间等属性)
|
|
||||||
const cookieParts = cookieStr.split(';')[0].split('=');
|
|
||||||
if (cookieParts.length >= 2) {
|
|
||||||
cookies[cookieParts[0].trim()] = cookieParts[1].trim();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return cookies;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 Cookie 对象格式化为请求头字符串
|
|
||||||
*/
|
|
||||||
formatCookieHeader(cookies) {
|
|
||||||
return Object.entries(cookies)
|
|
||||||
.map(([key, value]) => `${key}=${value}`)
|
|
||||||
.join('; ');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查登录是否成功(根据实际接口返回调整)
|
|
||||||
*/
|
|
||||||
checkLoginSuccess(responseData) {
|
|
||||||
// 这里需要根据实际返回的数据结构判断登录是否成功
|
|
||||||
// 示例:假设返回包含 success: true 或 code: 200
|
|
||||||
if (responseData.success === true || responseData.code === 200) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 默认返回 false
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
async onPullDownRefresh() {
|
|
||||||
console.log('父组件触发下拉刷新');
|
|
||||||
try {
|
|
||||||
// 获取当前活动的组件引用
|
|
||||||
let currentComponent;
|
|
||||||
if (this.activeTab === 'isbn') {
|
|
||||||
currentComponent = this.$refs.isbnUploadForm;
|
|
||||||
console.log('找到ISBN组件引用:', !!currentComponent);
|
|
||||||
} else if (this.activeTab === 'photo') {
|
|
||||||
currentComponent = this.$refs.photoUploadForm;
|
|
||||||
console.log('找到Photo组件引用:', !!currentComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果找到组件并且组件有resetData方法,则调用它
|
|
||||||
if (currentComponent && typeof currentComponent.resetData === 'function') {
|
|
||||||
console.log('开始调用子组件resetData方法');
|
|
||||||
|
|
||||||
// 等待resetData方法完成
|
|
||||||
await currentComponent.resetData();
|
|
||||||
|
|
||||||
// 确保视图更新
|
|
||||||
await this.$nextTick();
|
|
||||||
|
|
||||||
console.log('子组件resetData方法调用完成,强制更新视图');
|
|
||||||
currentComponent.$forceUpdate();
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: '刷新成功',
|
|
||||||
icon: 'success',
|
|
||||||
duration: 1500
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.error('找不到子组件或resetData方法不存在');
|
|
||||||
if (currentComponent) {
|
|
||||||
console.log('子组件可用方法:', Object.keys(currentComponent).filter(key => typeof currentComponent[key] === 'function'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果找不到方法,尝试直接访问子组件的数据并重置
|
|
||||||
if (currentComponent) {
|
|
||||||
if (this.activeTab === 'isbn') {
|
|
||||||
console.log('尝试直接重置ISBN组件数据');
|
|
||||||
// 直接重置ISBN组件的关键数据
|
|
||||||
currentComponent.scanResult = '';
|
|
||||||
currentComponent.formData = {
|
|
||||||
isbn: '',
|
|
||||||
sku: '',
|
|
||||||
title: '',
|
|
||||||
art_no: '',
|
|
||||||
more: '',
|
|
||||||
bookName: '',
|
|
||||||
};
|
|
||||||
currentComponent.value4 = 1.00; // 重置价格
|
|
||||||
currentComponent.value3 = 1; // 重置库存
|
|
||||||
currentComponent.fileList1 = [];
|
|
||||||
currentComponent.uploadedImages = [];
|
|
||||||
|
|
||||||
// 重置市场标签
|
|
||||||
currentComponent.marketTags = [{
|
|
||||||
label: '在售:',
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '旧:',
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '新:',
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '已售:',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// 重置品相选择
|
|
||||||
if (currentComponent.$refs.conditionSelect &&
|
|
||||||
typeof currentComponent.$refs.conditionSelect.resetSelection === 'function') {
|
|
||||||
currentComponent.$refs.conditionSelect.resetSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置在售商品列表
|
|
||||||
if (currentComponent.$refs.onSaleProductsComponent) {
|
|
||||||
currentComponent.$refs.onSaleProductsComponent.updateProducts([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 强制更新视图
|
|
||||||
await this.$nextTick();
|
|
||||||
currentComponent.$forceUpdate();
|
|
||||||
console.log('直接重置完成');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: '刷新成功',
|
|
||||||
icon: 'success',
|
|
||||||
duration: 1500
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('刷新失败:', error);
|
|
||||||
uni.showToast({
|
|
||||||
title: '刷新失败',
|
|
||||||
icon: 'none',
|
|
||||||
duration: 1500
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
// 停止下拉刷新动画
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.stopPullDownRefresh();
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.tab-container {
|
.page-container {
|
||||||
padding: 20rpx;
|
padding: 30rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-bar {
|
.upload-entrance {
|
||||||
display: flex;
|
padding: 40rpx 0;
|
||||||
border-radius: 10rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: #ffffff;
|
|
||||||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tab {
|
.entrance-title {
|
||||||
flex: 1;
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 20rpx 0;
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333333;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-tab.active {
|
.entrance-cards {
|
||||||
background-color: #e6f7ff;
|
display: flex;
|
||||||
color: #2979ff;
|
flex-direction: column;
|
||||||
|
gap: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entrance-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx 30rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entrance-card:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 48rpx;
|
||||||
|
margin-right: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.isbn-icon {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-icon {
|
||||||
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-desc {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-arrow {
|
||||||
|
font-size: 48rpx;
|
||||||
|
color: #ccc;
|
||||||
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -10,20 +10,10 @@
|
|||||||
<!-- 密码 -->
|
<!-- 密码 -->
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="password-item">
|
<view class="password-item">
|
||||||
<input
|
<input v-model="loginForm.password" :password="!showPassword" placeholder="请输入密码"
|
||||||
v-model="loginForm.password"
|
class="input password-input" placeholder-class="placeholder" />
|
||||||
:password="!showPassword"
|
<u-icon class="toggle-eye" :name="showPassword ? 'eye' : 'eye-off'" size="28" color="#999"
|
||||||
placeholder="请输入密码"
|
@tap="togglePassword"></u-icon>
|
||||||
class="input password-input"
|
|
||||||
placeholder-class="placeholder"
|
|
||||||
/>
|
|
||||||
<u-icon
|
|
||||||
class="toggle-eye"
|
|
||||||
:name="showPassword ? 'eye' : 'eye-off'"
|
|
||||||
size="28"
|
|
||||||
color="#999"
|
|
||||||
@tap="togglePassword"
|
|
||||||
></u-icon>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@ -37,18 +27,27 @@
|
|||||||
<!-- <button :loading="loading" :disabled="loading" class="login-btn" @tap="handleLogin">
|
<!-- <button :loading="loading" :disabled="loading" class="login-btn" @tap="handleLogin">
|
||||||
{{ loading ? '登录中...' : '立即登录' }}
|
{{ loading ? '登录中...' : '立即登录' }}
|
||||||
</button> -->
|
</button> -->
|
||||||
|
<checkbox-group @change="onPrivacyChange">
|
||||||
|
<label class="remember" style="white-space: nowrap;">
|
||||||
|
<checkbox value="agree" :checked="agreedPrivacy" />
|
||||||
|
<text>我已阅读并同意</text>
|
||||||
|
<text class="register-link-txt" @tap="goToPrivacyPage">
|
||||||
|
《隐私协议》
|
||||||
|
</text>
|
||||||
|
</label>
|
||||||
|
</checkbox-group>
|
||||||
<!-- 微信登录按钮 -->
|
<!-- 微信登录按钮 -->
|
||||||
<view class="container">
|
<button @tap="getUserProfileLogin" class="login-btn" :loading="loading" :disabled="!agreedPrivacy || loading">
|
||||||
<view class="login-box">
|
{{ loading ? '登录中...' : '微信一键登录' }}
|
||||||
<button open-type="getUserInfo" @getuserinfo="wxLogin" class="login-btn" :loading="loading" :disabled="loading">{{ loading ? '登录中...' : '微信一键登录' }}</button>
|
</button>
|
||||||
|
<!-- 查看隐私协议(样式与注册入口一致,居中显示) -->
|
||||||
|
<view class="register-link" @tap="goToPrivacyPage">
|
||||||
|
<text>查看隐私协议</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 注册入口 -->
|
<!-- 注册入口 -->
|
||||||
<view class="register-link" @tap="goToRegister">
|
<!-- <view class="register-link" @tap="goToRegister">
|
||||||
<text>没有账号?立即注册</text>
|
<text>没有账号?立即注册</text>
|
||||||
</view>
|
</view> -->
|
||||||
|
|
||||||
<!-- 操作流程说明 -->
|
<!-- 操作流程说明 -->
|
||||||
<view class="guide-text">
|
<view class="guide-text">
|
||||||
@ -79,6 +78,7 @@
|
|||||||
userId: '',
|
userId: '',
|
||||||
|
|
||||||
},
|
},
|
||||||
|
agreedPrivacy: false, // 用户是否勾选了隐私协议
|
||||||
// 是否显示明文密码
|
// 是否显示明文密码
|
||||||
showPassword: false,
|
showPassword: false,
|
||||||
tenantList: [], // 租户列表
|
tenantList: [], // 租户列表
|
||||||
@ -102,10 +102,75 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
this.checkPrivacyAuth(); // ← 新增:进入页面时检查隐私协议
|
||||||
this.initData()
|
this.initData()
|
||||||
// this.getTenantList()
|
// this.getTenantList()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* 隐私勾选变更:根据checkbox-group返回值设置agreedPrivacy
|
||||||
|
*/
|
||||||
|
onPrivacyChange(e) {
|
||||||
|
const values = Array.isArray(e.detail?.value) ? e.detail.value : []
|
||||||
|
this.agreedPrivacy = values.includes('agree')
|
||||||
|
if (this.agreedPrivacy) {
|
||||||
|
uni.setStorageSync('agreedPrivacy', true)
|
||||||
|
} else {
|
||||||
|
uni.removeStorageSync('agreedPrivacy')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 打开微信隐私协议弹窗或fallback
|
||||||
|
*/
|
||||||
|
goToPrivacyPage() {
|
||||||
|
if (typeof wx !== 'undefined' && wx.openPrivacyContract) {
|
||||||
|
wx.openPrivacyContract({
|
||||||
|
success: () => {},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '隐私弹窗打开失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '当前环境不支持隐私弹窗',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleAgreed(e) {
|
||||||
|
this.agreedPrivacy = e.detail.value.length > 0;
|
||||||
|
},
|
||||||
|
// 检查隐私授权
|
||||||
|
checkPrivacyAuth() {
|
||||||
|
if (!wx.getPrivacySetting) return; // 老版本不支持
|
||||||
|
|
||||||
|
wx.getPrivacySetting({
|
||||||
|
success: (res) => {
|
||||||
|
// 微信检测需要用户授权隐私协议
|
||||||
|
if (res.needAuthorization) {
|
||||||
|
this.showPrivacyContract();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 调起微信官方隐私协议弹窗
|
||||||
|
showPrivacyContract() {
|
||||||
|
wx.openPrivacyContract({
|
||||||
|
success: () => {
|
||||||
|
console.log("用户查看并同意隐私协议");
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: "您必须阅读并同意隐私协议才能继续使用服务",
|
||||||
|
icon: "none"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* 切换密码显示/隐藏
|
* 切换密码显示/隐藏
|
||||||
* 通过切换 showPassword 控制输入框是否密文显示
|
* 通过切换 showPassword 控制输入框是否密文显示
|
||||||
@ -124,8 +189,33 @@
|
|||||||
this.loginForm.rememberMe = true
|
this.loginForm.rememberMe = true
|
||||||
console.log('已自动填充保存的账号密码')
|
console.log('已自动填充保存的账号密码')
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
|
const storedAgreedPrivacy = uni.getStorageSync('agreedPrivacy')
|
||||||
|
this.agreedPrivacy = !!storedAgreedPrivacy
|
||||||
|
},
|
||||||
|
getUserProfileLogin() {
|
||||||
|
if (!this.agreedPrivacy) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先阅读并同意隐私协议',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wx.getUserProfile({
|
||||||
|
desc: '用于完善用户信息',
|
||||||
|
success: (res) => {
|
||||||
|
console.log("获取用户信息成功", res)
|
||||||
|
this.wxLogin(res) // 把用户信息传到 wxLogin
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '您取消了授权',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
// 租户选择变化
|
// 租户选择变化
|
||||||
onTenantChange(e) {
|
onTenantChange(e) {
|
||||||
@ -180,62 +270,78 @@
|
|||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
// 微信登录
|
// 执行微信登录 + 后台登录
|
||||||
async wxLogin(e) {
|
async wxLogin(userProfile) {
|
||||||
if (e.detail.errMsg === 'getUserInfo:ok') {
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
uni.showToast({
|
|
||||||
title: '登录中...',
|
|
||||||
icon: 'loading',
|
|
||||||
mask: true
|
|
||||||
});
|
|
||||||
console.log('开始调用 wx.login');
|
|
||||||
try {
|
|
||||||
const response = await wxLoginRequest(this.loginForm);
|
|
||||||
console.log('登录成功,准备存储用户信息', response);
|
|
||||||
// 存储用户信息和登录状态
|
|
||||||
const userInfo = e.detail.userInfo;
|
|
||||||
// const userInfo = response.data;
|
|
||||||
console.log("userinfo", userInfo)
|
|
||||||
uni.setStorageSync('userInfo', userInfo);
|
|
||||||
|
|
||||||
// 存储手机号码和其他必要信息
|
try {
|
||||||
// const userName = this.loginForm.userName || '';
|
console.log('正在调用 wx.login ...');
|
||||||
const password = this.loginForm.password || '';
|
|
||||||
const phoneNumber = this.loginForm.phoneNumber || '';
|
const loginRes = await new Promise((resolve, reject) => {
|
||||||
const tenantId = this.loginForm.tenantId || '';
|
wx.login({
|
||||||
const nickName = this.loginForm.nickName || '';
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('wx.login 返回:', loginRes);
|
||||||
|
|
||||||
|
// 存入 code
|
||||||
|
this.loginForm.code = loginRes.code;
|
||||||
|
|
||||||
|
// 调用你自己的后台登录接口
|
||||||
|
const response = await wxLoginRequest(this.loginForm);
|
||||||
|
console.log("后台登录成功:", response);
|
||||||
|
|
||||||
|
// 后台返回的用户数据
|
||||||
const userId = response.data.userId || '';
|
const userId = response.data.userId || '';
|
||||||
const openId = response.data.openid || '';
|
const openId = response.data.openid || '';
|
||||||
console.log("userId",userId)
|
|
||||||
console.log("phoneNumber",phoneNumber)
|
// 存储
|
||||||
uni.setStorageSync('phoneNumber', phoneNumber);
|
uni.setStorageSync("userInfo", userProfile);
|
||||||
uni.setStorageSync('password', password);
|
uni.setStorageSync("openId", openId);
|
||||||
// uni.setStorageSync('userName', userName);
|
uni.setStorageSync("userId", userId);
|
||||||
uni.setStorageSync('tenantId', tenantId);
|
uni.setStorageSync("phoneNumber", this.loginForm.phoneNumber);
|
||||||
uni.setStorageSync("nickName", nickName);
|
const currentTime = Date.now();
|
||||||
uni.setStorageSync("userId", userId)
|
uni.setStorageSync('lastSubmitTime', currentTime);
|
||||||
uni.setStorageSync("openId",openId)
|
// 记住账号/密码
|
||||||
// 如果勾选了记住密码,同时保存到记住密码的存储中
|
const phoneNumber = this.loginForm.phoneNumber;
|
||||||
|
const password = this.loginForm.password;
|
||||||
|
|
||||||
if (this.loginForm.rememberMe && phoneNumber && password) {
|
if (this.loginForm.rememberMe && phoneNumber && password) {
|
||||||
uni.setStorageSync('rememberedPhoneNumber', phoneNumber);
|
uni.setStorageSync('rememberedPhoneNumber', phoneNumber);
|
||||||
uni.setStorageSync('rememberedPassword', password);
|
uni.setStorageSync('rememberedPassword', password);
|
||||||
uni.setStorageSync('rememberMe', true);
|
uni.setStorageSync('rememberMe', true);
|
||||||
console.log('微信登录成功,已保存账号密码');
|
|
||||||
}
|
}
|
||||||
// 更新Vuex状态
|
|
||||||
this.$store.commit('SET_USER_INFO', userInfo);
|
|
||||||
this.$store.commit('SET_LOGIN_STATUS', true);
|
|
||||||
|
|
||||||
// 重置最后提交时间戳,避免登录后立即提示过期
|
// Vuex 更新
|
||||||
uni.setStorageSync('lastSubmitTime', Date.now());
|
this.$store.commit('auth/SET_USER_INFO', userProfile);
|
||||||
|
this.$store.commit('auth/SET_LOGIN_STATUS', true);
|
||||||
|
|
||||||
// 调用 addMember 接口
|
// 最后调用你的 addMember 接口
|
||||||
|
this.addMember(userId);
|
||||||
|
|
||||||
|
// 跳转
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/entry/index'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error("wxLogin 错误:", err);
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: err?.msg || '登录失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 单独封装 addMember 避免 wxLogin 过长
|
||||||
|
async addMember(userId) {
|
||||||
try {
|
try {
|
||||||
console.log('调用 addMember 接口,userId:', userId);
|
const res = await uni.request({
|
||||||
|
|
||||||
const response = await uni.request({
|
|
||||||
// url: 'https://newadmin.buzhiyushu.cn/settledMember/record/addMember',
|
|
||||||
url: 'https://go.order.service.buzhiyushu.cn/api/user/insertRecbusinessByUserId',
|
url: 'https://go.order.service.buzhiyushu.cn/api/user/insertRecbusinessByUserId',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
data: {
|
data: {
|
||||||
@ -247,50 +353,24 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('addMember 接口调用结果:', response);
|
console.log("addMember 返回:", res);
|
||||||
|
|
||||||
if (response[1].statusCode === 200) {
|
if (res[1].statusCode !== 200) {
|
||||||
console.log('addMember 接口调用成功');
|
console.warn("addMember 调用失败");
|
||||||
if (response[1].data.code === 500) {
|
|
||||||
console.log(response[1].data.message)
|
|
||||||
}
|
}
|
||||||
} else {
|
} catch (err) {
|
||||||
console.error('addMember 接口调用失败:', response);
|
console.error("addMember 出错:", err);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error('调用 addMember 接口出错:', error);
|
|
||||||
// 接口调用出错不阻止后续流程
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('准备跳转到功能入口页面');
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/entry/index'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log('登录过程中出现错误:', error.msg);
|
|
||||||
uni.showToast({
|
|
||||||
title: error.msg,
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: '您取消了授权',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// 跳转到注册页面
|
// 跳转到注册页面
|
||||||
goToRegister() {
|
// goToRegister() {
|
||||||
uni.navigateTo({
|
// uni.navigateTo({
|
||||||
url: '/pages/register/index'
|
// url: '/pages/register/index'
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -355,6 +435,12 @@
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.privacy-link {
|
||||||
|
display: block;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
.login-btn {
|
.login-btn {
|
||||||
margin-top: 60rpx;
|
margin-top: 60rpx;
|
||||||
background: #007aff;
|
background: #007aff;
|
||||||
@ -391,6 +477,12 @@
|
|||||||
color: #007aff;
|
color: #007aff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.register-link-txt {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
.guide-text {
|
.guide-text {
|
||||||
margin-top: 20rpx;
|
margin-top: 20rpx;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@ -178,126 +178,6 @@
|
|||||||
</picker>
|
</picker>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 本店分类选择模块 -->
|
|
||||||
<!-- <view class="u-demo-block" style="margin-top:40rpx">
|
|
||||||
<view class="section-header">
|
|
||||||
<text class="section-title">本店分类</text>
|
|
||||||
<text class="section-subtitle">请根据需求选择本店分类(不可选)</text>
|
|
||||||
</view>
|
|
||||||
<view class="freight-picker">
|
|
||||||
<picker mode="selector" :range="shopType" @change="onShopType" :value="selectShopTypeIndex">
|
|
||||||
<view class="picker-content">
|
|
||||||
{{ shopType[selectShopTypeIndex] || '请选择本店分类' }}
|
|
||||||
<text class="arrow"></text>
|
|
||||||
</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
</view> -->
|
|
||||||
<!-- 自动提交模块 -->
|
|
||||||
<!-- <view class="auto-submit-container">
|
|
||||||
<view class="section-title">
|
|
||||||
<text class="title-text">自动提交</text>
|
|
||||||
</view>
|
|
||||||
<view class="switch-item">
|
|
||||||
<text>搜索完成后开始拍照</text>
|
|
||||||
<u-switch v-model="searchThenTakePhoto" size="24" @change="handleSearchThenTakePhotoChange"></u-switch>
|
|
||||||
</view>
|
|
||||||
<view class="switch-item">
|
|
||||||
<text>拍照下一步自动提交</text>
|
|
||||||
<u-switch v-model="takePhotoNextSubmit" size="24" @change="handleTakePhotoNextSubmitChange"></u-switch>
|
|
||||||
</view>
|
|
||||||
<view class="switch-item">
|
|
||||||
<text>提交成功提示音(可爱|御姐|随机切换)</text>
|
|
||||||
<u-switch v-model="submitSound" size="24" @change="handleSubmitSoundChange"></u-switch>
|
|
||||||
</view>
|
|
||||||
<view class="switch-item">
|
|
||||||
<text>扫码枪模式</text>
|
|
||||||
<u-switch v-model="scanGunMode" size="24" @change="handleScanGunModeChange"></u-switch>
|
|
||||||
</view>
|
|
||||||
</view> -->
|
|
||||||
<!-- 商品检测 -->
|
|
||||||
<!-- <view class="auto-submit-container">
|
|
||||||
<view class="section-title">
|
|
||||||
<text class="title-text">已有商品检测(已有商品默认自动改价)</text>
|
|
||||||
</view>
|
|
||||||
<view class="switch-item">
|
|
||||||
<text>通过isbn检测商品是否存在(下一本扫描)</text>
|
|
||||||
<u-switch v-model="isbnCheck" size="24" @change="handleIsbnCheckChange"></u-switch>
|
|
||||||
</view>
|
|
||||||
<view class="switch-item">
|
|
||||||
<text>通过货号检测商品是否存在(下一本扫描)</text>
|
|
||||||
<u-switch v-model="articleNumberCheck" size="24" @change="handleArticleNumberCheckChange"></u-switch>
|
|
||||||
</view>
|
|
||||||
</view> -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <view class="u-demo-block" style="margin-top:40rpx">
|
|
||||||
<u-collapse @change="change" @close="close" @open="open">
|
|
||||||
<u-collapse-item title="更多设置" name="Docs guide" style="margin-top:40rpx">
|
|
||||||
<view class="u-demo-block" style="margin-top:40rpx">
|
|
||||||
<view class="section-header">
|
|
||||||
<text class="section-title">货号自定义</text>
|
|
||||||
<text class="section-subtitle">根据自己偏好选择显示</text>
|
|
||||||
</view>
|
|
||||||
<view class="freight-picker">
|
|
||||||
<picker mode="selector" :range="goodsNoOptions" @change="onGoodsNoChange"
|
|
||||||
:value="selectedGoodsNoIndex">
|
|
||||||
<view class="picker-content">
|
|
||||||
{{ goodsNoOptions[selectedGoodsNoIndex] || '请选择输入类型' }}
|
|
||||||
<text class="arrow"></text>
|
|
||||||
</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="u-demo-block">
|
|
||||||
<view class="section-header">
|
|
||||||
<text class="section-title">参考价小数位(改价通用)</text>
|
|
||||||
<text class="section-subtitle">根据自己偏好选择显示</text>
|
|
||||||
</view>
|
|
||||||
<view class="freight-picker">
|
|
||||||
<picker mode="selector" :range="decimalOptions" @change="handleDecimalChange"
|
|
||||||
:value="selectedDecimalIndex">
|
|
||||||
<view class="picker-content">
|
|
||||||
{{ decimalOptions[selectedDecimalIndex] || '请选择输入类型' }}
|
|
||||||
<text class="arrow"></text>
|
|
||||||
</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="u-demo-block" style="margin-top:40rpx">
|
|
||||||
<view class="section-header">
|
|
||||||
<text class="section-title">商品列表价格展示</text>
|
|
||||||
<text class="section-subtitle">根据自己偏好选择显示</text>
|
|
||||||
</view>
|
|
||||||
<view class="freight-picker">
|
|
||||||
<picker mode="selector" :range="priceDisplayOptions" @change="handlePriceDisplayChange"
|
|
||||||
:value="priceDisplayIndex">
|
|
||||||
<view class="picker-content">
|
|
||||||
{{ priceDisplayOptions[priceDisplayIndex] || '请选择输入类型' }}
|
|
||||||
<text class="arrow"></text>
|
|
||||||
</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="u-demo-block" style="margin-top:40rpx">
|
|
||||||
<view class="section-header">
|
|
||||||
<text class="section-title">商品列表展示</text>
|
|
||||||
<text class="section-subtitle">根据自己偏好选择显示</text>
|
|
||||||
</view>
|
|
||||||
<view class="freight-picker">
|
|
||||||
<picker mode="selector" :range="displayModeOptions" @change="handleDisplayModeChange"
|
|
||||||
:value="displayModeIndex">
|
|
||||||
<view class="picker-content">
|
|
||||||
{{ displayModeOptions[displayModeIndex] || '请选择输入类型' }}
|
|
||||||
<text class="arrow"></text>
|
|
||||||
</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</u-collapse-item>
|
|
||||||
</u-collapse>
|
|
||||||
</view> -->
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -307,10 +187,11 @@
|
|||||||
mapState,
|
mapState,
|
||||||
mapMutations
|
mapMutations
|
||||||
} from 'vuex'
|
} from 'vuex'
|
||||||
|
// Vuex 模块拆分后使用命名空间访问
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['priceMode', 'priceType'])
|
...mapState('price', ['priceMode', 'priceType'])
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value1: {
|
value1: {
|
||||||
@ -551,9 +432,10 @@
|
|||||||
uni.setStorageSync('conditionValue', this.conditionValue);
|
uni.setStorageSync('conditionValue', this.conditionValue);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['updatePriceMode', 'updatePriceType', 'updateAverageRange', 'updateSelectedPosition',
|
...mapMutations('price', ['updatePriceMode', 'updatePriceType', 'updateAverageRange',
|
||||||
'updateFreight', 'updateMinValue'
|
'updateFreight', 'updateMinValue'
|
||||||
]),
|
]),
|
||||||
|
...mapMutations('warehouse', ['updateSelectedPosition']),
|
||||||
// 加载保存的账号信息
|
// 加载保存的账号信息
|
||||||
loadAccounts() {
|
loadAccounts() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,94 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="test-container">
|
|
||||||
<view class="test-header">
|
|
||||||
<text class="test-title">会员选择页面测试</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="test-buttons">
|
|
||||||
<button class="test-btn" @click="testCheckMemberBooksCount">
|
|
||||||
测试 checkMemberBooksCount 入口
|
|
||||||
<text class="test-desc">(应该只显示xcx上书会员)</text>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="test-btn" @click="testCheckKwfwMember">
|
|
||||||
测试 checkKwfwMember 入口
|
|
||||||
<text class="test-desc">(应该只显示翻新会员)</text>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="test-btn" @click="testToMemberSelect">
|
|
||||||
测试 toMemberSelect 入口
|
|
||||||
<text class="test-desc">(应该显示所有会员类型)</text>
|
|
||||||
</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
// 模拟 checkMemberBooksCount 的跳转
|
|
||||||
testCheckMemberBooksCount() {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/user/memberSelect?from=memberCheck&type=xcx'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 模拟 checkKwfwMember 的跳转
|
|
||||||
testCheckKwfwMember() {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/user/memberSelect?from=kwfw&type=kwfw'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 模拟 toMemberSelect 的跳转
|
|
||||||
testToMemberSelect() {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/user/memberSelect'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.test-container {
|
|
||||||
padding: 40rpx;
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #f8f9fc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 60rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-title {
|
|
||||||
font-size: 36rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-buttons {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-btn {
|
|
||||||
background-color: #4A5CFF;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
padding: 30rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-desc {
|
|
||||||
font-size: 24rpx;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="version-number">v3.2.63</view>
|
<view class="version-number">v3.2.78 </view>
|
||||||
<view class="menu-list">
|
<view class="menu-list">
|
||||||
<!-- <view class="menu-item" @click="toScanHistory">
|
<!-- <view class="menu-item" @click="toScanHistory">
|
||||||
<view class="menu-left">
|
<view class="menu-left">
|
||||||
@ -26,6 +26,14 @@
|
|||||||
<u-icon name="arrow-right" size="16"></u-icon>
|
<u-icon name="arrow-right" size="16"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view class="menu-item" @click="toDispatchManagement">
|
||||||
|
<view class="menu-left">
|
||||||
|
<u-icon name="list" size="20" color="#909399"></u-icon>
|
||||||
|
<text class="menu-text">线下订单管理</text>
|
||||||
|
</view>
|
||||||
|
<u-icon name="arrow-right" size="16"></u-icon>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="menu-item" @click="toShelfManagement">
|
<view class="menu-item" @click="toShelfManagement">
|
||||||
<view class="menu-left">
|
<view class="menu-left">
|
||||||
<u-icon name="grid" size="20" color="#3c9cff"></u-icon>
|
<u-icon name="grid" size="20" color="#3c9cff"></u-icon>
|
||||||
@ -47,6 +55,13 @@
|
|||||||
</view>
|
</view>
|
||||||
<u-icon name="arrow-right" size="16"></u-icon>
|
<u-icon name="arrow-right" size="16"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="menu-item" @click="toOrderQuery">
|
||||||
|
<view class="menu-left">
|
||||||
|
<u-icon name="list" size="20" color="#ff9900"></u-icon>
|
||||||
|
<text class="menu-text">仓库订单查询</text>
|
||||||
|
</view>
|
||||||
|
<u-icon name="arrow-right" size="16"></u-icon>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- <view class="menu-item" @click="toSettings">
|
<!-- <view class="menu-item" @click="toSettings">
|
||||||
<view class="menu-left">
|
<view class="menu-left">
|
||||||
@ -65,6 +80,7 @@
|
|||||||
import {
|
import {
|
||||||
mapState
|
mapState
|
||||||
} from 'vuex'
|
} from 'vuex'
|
||||||
|
// Vuex 模块拆分后使用命名空间访问
|
||||||
import {
|
import {
|
||||||
checkKwfwMember
|
checkKwfwMember
|
||||||
} from '@/components/MemberBookCheck.js'
|
} from '@/components/MemberBookCheck.js'
|
||||||
@ -73,7 +89,7 @@
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['userInfo'])
|
...mapState('auth', ['userInfo'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toScanHistory() {
|
toScanHistory() {
|
||||||
@ -88,12 +104,17 @@
|
|||||||
},
|
},
|
||||||
toMemberSelect() {
|
toMemberSelect() {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/user/memberSelect'
|
url: '/pkgUser/memberSelect'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
toDispatchManagement() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pkgUser/dispatch-management'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
toShelfManagement() {
|
toShelfManagement() {
|
||||||
// uni.navigateTo({
|
// uni.navigateTo({
|
||||||
// url: '/pages/shelf/management'
|
// url: '/pkgManage/shelf/management'
|
||||||
// })
|
// })
|
||||||
},
|
},
|
||||||
async toCloneTool() {
|
async toCloneTool() {
|
||||||
@ -108,7 +129,7 @@
|
|||||||
// 如果是会员,则跳转到孔网商品翻新页面
|
// 如果是会员,则跳转到孔网商品翻新页面
|
||||||
if (isMember) {
|
if (isMember) {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/clone-tool/index'
|
url: '/pkgManage/clone-tool/index'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 如果不是会员,checkKwfwMember函数内部已经处理了提示和跳转逻辑
|
// 如果不是会员,checkKwfwMember函数内部已经处理了提示和跳转逻辑
|
||||||
@ -126,7 +147,7 @@
|
|||||||
content: '确定要退出登录吗?',
|
content: '确定要退出登录吗?',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
this.$store.dispatch('logout')
|
this.$store.dispatch('auth/logout')
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: '/pages/login/index'
|
url: '/pages/login/index'
|
||||||
})
|
})
|
||||||
@ -167,7 +188,7 @@
|
|||||||
|
|
||||||
// 跳转到上书记录页面并传递数据
|
// 跳转到上书记录页面并传递数据
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/scan/book-records',
|
url: '/pkgUser/book-records',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
// 向打开的页面传递数据
|
// 向打开的页面传递数据
|
||||||
res.eventChannel.emit('bookRecordsData', {
|
res.eventChannel.emit('bookRecordsData', {
|
||||||
@ -188,6 +209,11 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
toOrderQuery() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/warehouse/order-query'
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="create-warehouse-container">
|
<view class="create-warehouse-container">
|
||||||
<view class="header">
|
<!-- <view class="header">
|
||||||
<view class="back-btn" @click="goBack">
|
<view class="back-btn" @click="goBack">
|
||||||
<text class="back-icon">←</text>
|
<text class="back-icon">←</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="title">创建货区</text>
|
<text class="title">创建货区</text>
|
||||||
</view>
|
</view> -->
|
||||||
|
|
||||||
<!-- 步骤指示器 -->
|
<!-- 步骤指示器 -->
|
||||||
<view class="steps">
|
<view class="steps">
|
||||||
|
|||||||
1634
pages/warehouse/order-query.vue
Normal file
1634
pages/warehouse/order-query.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,10 +5,10 @@
|
|||||||
<text class="title">选择一级货区</text>
|
<text class="title">选择一级货区</text>
|
||||||
<text class="subtitle">请选择您要操作的一级货区</text>
|
<text class="subtitle">请选择您要操作的一级货区</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="add-btn" @click="navigateToCreateWarehouse">
|
<!-- <view class="add-btn" @click="navigateToCreateWarehouse">
|
||||||
<text class="add-icon">+</text>
|
<text class="add-icon">+</text>
|
||||||
<text class="add-text">新建</text>
|
<text class="add-text">新建</text>
|
||||||
</view>
|
</view> -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 仓库列表 -->
|
<!-- 仓库列表 -->
|
||||||
@ -455,7 +455,7 @@
|
|||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
// 从本地存储获取手机号和用户ID
|
// 从本地存储获取手机号和用户ID
|
||||||
this.phoneNumber = uni.getStorageSync('phoneNumber');
|
this.phoneNumber = uni.getStorageSync('phoneNumber') || uni.getStorageSync('rememberedPhoneNumber');
|
||||||
this.userId = uni.getStorageSync('userId'); // 获取用户ID
|
this.userId = uni.getStorageSync('userId'); // 获取用户ID
|
||||||
|
|
||||||
if (!this.phoneNumber) {
|
if (!this.phoneNumber) {
|
||||||
|
|||||||
1328
pkgManage/clone-tool/index.vue
Normal file
1328
pkgManage/clone-tool/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -358,7 +358,7 @@
|
|||||||
|
|
||||||
// 跳转到商品管理页面
|
// 跳转到商品管理页面
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/goods/index?code=${encodeURIComponent(locationCode)}`,
|
url: `/pkgManage/goods/index?code=${encodeURIComponent(locationCode)}`,
|
||||||
success: () => {
|
success: () => {
|
||||||
console.log('成功跳转到商品管理页面');
|
console.log('成功跳转到商品管理页面');
|
||||||
},
|
},
|
||||||
@ -139,6 +139,7 @@
|
|||||||
import {
|
import {
|
||||||
mapState
|
mapState
|
||||||
} from 'vuex'
|
} from 'vuex'
|
||||||
|
// Vuex 模块拆分后使用命名空间访问
|
||||||
import CryptoJS from 'crypto-js';
|
import CryptoJS from 'crypto-js';
|
||||||
import CameraUpload from '@/components/CameraUpload.vue';
|
import CameraUpload from '@/components/CameraUpload.vue';
|
||||||
import TabBar from '@/components/TabBar.vue';
|
import TabBar from '@/components/TabBar.vue';
|
||||||
@ -217,7 +218,8 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['priceMode', 'priceType', 'averageRange', 'selectedPosition', 'freight', 'minValue'])
|
...mapState('price', ['priceMode', 'priceType', 'averageRange', 'freight', 'minValue']),
|
||||||
|
...mapState('warehouse', ['selectedPosition'])
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
"tab-bar": TabBar,
|
"tab-bar": TabBar,
|
||||||
@ -280,6 +282,7 @@
|
|||||||
authorOptions: [], // 作者选项
|
authorOptions: [], // 作者选项
|
||||||
press: '',
|
press: '',
|
||||||
author: '', // 书籍作者
|
author: '', // 书籍作者
|
||||||
|
pubDateText: '', // 出版时间
|
||||||
filteredOnSaleProducts: [], // 筛选后的在售商品
|
filteredOnSaleProducts: [], // 筛选后的在售商品
|
||||||
filteredSoldProducts: [], // 筛选后的已售商品
|
filteredSoldProducts: [], // 筛选后的已售商品
|
||||||
isFiltered: false, // 是否已应用筛选
|
isFiltered: false, // 是否已应用筛选
|
||||||
@ -364,6 +367,7 @@
|
|||||||
categoryPathText: '', // 用于显示分类路径
|
categoryPathText: '', // 用于显示分类路径
|
||||||
categoryColumns: [], // 多级分类选择器的所有列数据
|
categoryColumns: [], // 多级分类选择器的所有列数据
|
||||||
categoryLevels: [], // 存储每个级别选中的分类对象
|
categoryLevels: [], // 存储每个级别选中的分类对象
|
||||||
|
kfzBookPic: '', // 存储孔夫子图书官图
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -574,6 +578,7 @@
|
|||||||
|
|
||||||
// 清空所有数据
|
// 清空所有数据
|
||||||
this.scanResult = ''; // 清空ISBN
|
this.scanResult = ''; // 清空ISBN
|
||||||
|
this.kfzBookPic = ''; // 清空孔夫子图书官图
|
||||||
this.formData = {
|
this.formData = {
|
||||||
isbn: '',
|
isbn: '',
|
||||||
sku: '',
|
sku: '',
|
||||||
@ -840,7 +845,7 @@
|
|||||||
if (tab === 'title') {
|
if (tab === 'title') {
|
||||||
// 跳转到仅书名上传页面
|
// 跳转到仅书名上传页面
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/title-upload/index'
|
url: '/pkgUpload/title-upload/index'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -900,7 +905,7 @@
|
|||||||
|
|
||||||
// 跳转到上书记录页面并传递数据
|
// 跳转到上书记录页面并传递数据
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/scan/book-records',
|
url: '/pkgUser/book-records',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
// 向打开的页面传递数据
|
// 向打开的页面传递数据
|
||||||
res.eventChannel.emit('bookRecordsData', {
|
res.eventChannel.emit('bookRecordsData', {
|
||||||
@ -1143,7 +1148,7 @@
|
|||||||
try {
|
try {
|
||||||
// 上传图片
|
// 上传图片
|
||||||
const result = await this.uploadFilePromise(file.url, i);
|
const result = await this.uploadFilePromise(file.url, i);
|
||||||
console.log("图片上传res", result)
|
console.log("图片上传result", result)
|
||||||
// 更新状态为上传成功
|
// 更新状态为上传成功
|
||||||
this.fileList1.splice(i, 1, {
|
this.fileList1.splice(i, 1, {
|
||||||
...file,
|
...file,
|
||||||
@ -1367,9 +1372,9 @@
|
|||||||
// 提交到服务器的方法
|
// 提交到服务器的方法
|
||||||
submitToServer(formData) {
|
submitToServer(formData) {
|
||||||
uni.request({
|
uni.request({
|
||||||
// url: 'https://api.buzhiyushu.cn/zhishu/shopGoods/submit',
|
url: 'https://api.buzhiyushu.cn/zhishu/shopGoods/submit',
|
||||||
|
// url: 'http://192.168.101.209:8080/zhishu/shopGoods/submit',
|
||||||
// url: 'http://192.168.101.127:8080/zhishu/shopGoods/submit',
|
// url: 'http://192.168.101.127:8080/zhishu/shopGoods/submit',
|
||||||
url: 'http://localhost:8080/zhishu/shopGoods/submit',
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: formData,
|
data: formData,
|
||||||
header: {
|
header: {
|
||||||
@ -1403,6 +1408,7 @@
|
|||||||
artNo: responseData.artNo,
|
artNo: responseData.artNo,
|
||||||
};
|
};
|
||||||
this.syncBookToCenter(formData);
|
this.syncBookToCenter(formData);
|
||||||
|
|
||||||
// 创建包含ID的新formData
|
// 创建包含ID的新formData
|
||||||
const newFormData = {
|
const newFormData = {
|
||||||
...formData,
|
...formData,
|
||||||
@ -1434,6 +1440,7 @@
|
|||||||
|
|
||||||
};
|
};
|
||||||
this.scanResult = '';
|
this.scanResult = '';
|
||||||
|
this.kfzBookPic = ''; // 清空孔夫子图书官图
|
||||||
} else {
|
} else {
|
||||||
this.formData2 = {
|
this.formData2 = {
|
||||||
isbn: '',
|
isbn: '',
|
||||||
@ -1484,6 +1491,8 @@
|
|||||||
syncBookToCenter(formData) {
|
syncBookToCenter(formData) {
|
||||||
// 后台接口地址(POST)
|
// 后台接口地址(POST)
|
||||||
const centerUrl = 'https://api.buzhiyushu.cn/zhishu/baseInfo/getXcxData';
|
const centerUrl = 'https://api.buzhiyushu.cn/zhishu/baseInfo/getXcxData';
|
||||||
|
// const centerUrl = 'http://localhost:8080/zhishu/baseInfo/getXcxData';
|
||||||
|
|
||||||
// 仅提取所需参数
|
// 仅提取所需参数
|
||||||
const centerData = {
|
const centerData = {
|
||||||
barcode: formData.barcode,
|
barcode: formData.barcode,
|
||||||
@ -1491,9 +1500,11 @@
|
|||||||
price: formData.price,
|
price: formData.price,
|
||||||
author: formData.author,
|
author: formData.author,
|
||||||
publisher: this.press,
|
publisher: this.press,
|
||||||
|
publishTime: this.pubDateText,
|
||||||
sellCount: formData.sellCount,
|
sellCount: formData.sellCount,
|
||||||
buyCount: formData.buyCount,
|
buyCount: formData.buyCount,
|
||||||
files: formData.files || []
|
files: formData.files || [],
|
||||||
|
kfzBookPic: this.kfzBookPic
|
||||||
};
|
};
|
||||||
console.log("选品中心参数", centerData)
|
console.log("选品中心参数", centerData)
|
||||||
|
|
||||||
@ -1540,24 +1551,18 @@
|
|||||||
const num = (index + 1).toString();
|
const num = (index + 1).toString();
|
||||||
|
|
||||||
uni.uploadFile({
|
uni.uploadFile({
|
||||||
url: "https://api.buzhiyushu.cn/zhishu/shopGoods/uploadImages",
|
// url: "https://api.buzhiyushu.cn/zhishu/shopGoods/uploadImages",
|
||||||
|
url: "https://xcx.uploadfile.yushutx.com/uploadImages",
|
||||||
filePath: url,
|
filePath: url,
|
||||||
name: "file",
|
name: "file",
|
||||||
// formData: {
|
|
||||||
// bookName: bookName,
|
|
||||||
// isbn: isbn,
|
|
||||||
// num: num,
|
|
||||||
// warehouseId: warehouseId, // 添加仓库ID
|
|
||||||
// shelfId: shelfId, // 添加货架ID
|
|
||||||
// locationId: locationId // 添加货位ID
|
|
||||||
// },
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('图片上传成功:', res);
|
console.log('图片上传成功:', res);
|
||||||
// 解析返回结果
|
// 解析返回结果
|
||||||
const data = JSON.parse(res.data);
|
const data = JSON.parse(res.data);
|
||||||
console.log("data", data)
|
console.log("data", data)
|
||||||
const imageUrl = data.url || url;
|
const urlData = JSON.parse(data.data);
|
||||||
|
const imageUrl = urlData.url || url;
|
||||||
|
console.log("url", imageUrl)
|
||||||
// 将上传成功的图片信息存储到数组中
|
// 将上传成功的图片信息存储到数组中
|
||||||
this.uploadedImages.push({
|
this.uploadedImages.push({
|
||||||
url: imageUrl,
|
url: imageUrl,
|
||||||
@ -2024,13 +2029,41 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 调用孔网图书条目接口获取官图(失败不影响主流程)
|
||||||
|
let bookPicRes = null;
|
||||||
|
try {
|
||||||
|
bookPicRes = await uni.request({
|
||||||
|
url: 'https://search.kongfz.com/pc-gw/search-web/client/pc/bookLib/keyword/list',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
keyword: isbn,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
'Cookie': newCookie,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (bookPicError) {
|
||||||
|
console.warn('获取孔网图书官图失败,不影响主流程:', bookPicError);
|
||||||
|
bookPicRes = null;
|
||||||
|
}
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
|
||||||
console.log("在售响应:", res);
|
console.log("在售响应:", res);
|
||||||
const responseData = Array.isArray(res) ? res[1].data : res.data;
|
const responseData = Array.isArray(res) ? res[1].data : res.data;
|
||||||
console.log("已售响应", soldOutRes);
|
console.log("已售响应", soldOutRes);
|
||||||
const responseData1 = Array.isArray(res) ? soldOutRes[1].data.data.data : soldOutRes.data.data.data;
|
if (bookPicRes) {
|
||||||
|
console.log("图书条目响应:", bookPicRes);
|
||||||
|
}
|
||||||
|
const responseData1 = Array.isArray(res) ? soldOutRes[1].data.data.data : soldOutRes.data.data
|
||||||
|
.data;
|
||||||
|
// 安全地获取图书官图,如果失败则为空字符串
|
||||||
|
this.kfzBookPic = '';
|
||||||
|
if (bookPicRes && Array.isArray(bookPicRes) && bookPicRes[1]?.data?.data?.itemResponse?.list?.[0]
|
||||||
|
?.imgUrlEntity?.bigImgUrl) {
|
||||||
|
this.kfzBookPic = bookPicRes[1].data.data.itemResponse.list[0].imgUrlEntity.bigImgUrl;
|
||||||
|
}
|
||||||
|
console.log("图书条目图片:", this.kfzBookPic || '未获取到');
|
||||||
if (responseData.errType === "102" || responseData1.errType === "102") {
|
if (responseData.errType === "102" || responseData1.errType === "102") {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '请登录或切换孔网账号后再进行扫码上书',
|
title: '请登录或切换孔网账号后再进行扫码上书',
|
||||||
@ -2045,9 +2078,11 @@
|
|||||||
const buyCount = res[1].data.data.itemResponse.total;
|
const buyCount = res[1].data.data.itemResponse.total;
|
||||||
const author = res[1].data.data.itemResponse.list[0].author;
|
const author = res[1].data.data.itemResponse.list[0].author;
|
||||||
const press = res[1].data.data.itemResponse.list[0].press;
|
const press = res[1].data.data.itemResponse.list[0].press;
|
||||||
|
const pubDateText = res[1].data.data.itemResponse.list[0].pubDateText
|
||||||
// 保存作者信息到组件数据中
|
// 保存作者信息到组件数据中
|
||||||
this.author = author || '';
|
this.author = author || '';
|
||||||
this.press = press || '';
|
this.press = press || '';
|
||||||
|
this.pubDateText = pubDateText || '';
|
||||||
let bookData = null;
|
let bookData = null;
|
||||||
let productList = [];
|
let productList = [];
|
||||||
|
|
||||||
@ -2196,6 +2231,7 @@
|
|||||||
|
|
||||||
// 清空所有数据
|
// 清空所有数据
|
||||||
this.scanResult = ''; // 清空ISBN
|
this.scanResult = ''; // 清空ISBN
|
||||||
|
this.kfzBookPic = ''; // 清空孔夫子图书官图
|
||||||
console.log('已清空scanResult:', this.scanResult);
|
console.log('已清空scanResult:', this.scanResult);
|
||||||
|
|
||||||
// 重置表单数据
|
// 重置表单数据
|
||||||
191
pkgUpload/backup_20260511_133610/pages.json
Normal file
191
pkgUpload/backup_20260511_133610/pages.json
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
{
|
||||||
|
"easycom": {
|
||||||
|
"^u-(.*)": "@/uni_modules/uview-ui/components/u-$1/u-$1.vue",
|
||||||
|
"autoscan": true,
|
||||||
|
"custom": {
|
||||||
|
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"globalStyle": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationBarTitleText": "智助",
|
||||||
|
"navigationBarBackgroundColor": "#F8F8F8",
|
||||||
|
"backgroundColor": "#F5F5F5",
|
||||||
|
"enablePullDownRefresh": true,
|
||||||
|
"backgroundTextStyle": "dark"
|
||||||
|
},
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "pages/login/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "登录"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/entry/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "功能入口",
|
||||||
|
"navigationBarBackgroundColor": "#F8F8F8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/index/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "首页"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/scan/history",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "设置"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/user/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "个人中心"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subPackages": [
|
||||||
|
{
|
||||||
|
"root": "pkgUpload",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "isbn-upload/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "Isbn-上传",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "title-upload/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "仅书名-上传"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "photo-upload/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "无ISBN-上传",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "pkgManage",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "goods/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品管理",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "shelf/management",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "货架管理"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "clone-tool/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品克隆工具",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "pkgUser",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "memberSelect",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "会员等级选择",
|
||||||
|
"navigationBarBackgroundColor": "#F8F9FC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "dispatch-management",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "下发管理"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "book-records",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "上书记录"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "pages/warehouse",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "warehouse-select",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "选择仓库"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "create-warehouse",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"app-plus": {
|
||||||
|
"titleNView": false,
|
||||||
|
"animationType": "slide-in-right"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "order-query",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "仓库订单查询",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "pages/register",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "注册"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tabBar": {
|
||||||
|
"color": "#7A7E83",
|
||||||
|
"selectedColor": "#2979ff",
|
||||||
|
"borderStyle": "black",
|
||||||
|
"backgroundColor": "#ffffff",
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"pagePath": "pages/index/index",
|
||||||
|
"iconPath": "/static/tabbar/index.png",
|
||||||
|
"selectedIconPath": "/static/tabbar/index.png",
|
||||||
|
"text": "首页"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/scan/history",
|
||||||
|
"iconPath": "/static/tabbar/setting.png",
|
||||||
|
"selectedIconPath": "/static/tabbar/setting.png",
|
||||||
|
"text": "设置"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/user/index",
|
||||||
|
"iconPath": "/static/tabbar/my.png",
|
||||||
|
"selectedIconPath": "/static/tabbar/my.png",
|
||||||
|
"text": "我的"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -285,6 +285,7 @@
|
|||||||
import {
|
import {
|
||||||
mapState
|
mapState
|
||||||
} from 'vuex'
|
} from 'vuex'
|
||||||
|
// Vuex 模块拆分后使用命名空间访问
|
||||||
import BookConditionSelect from '@/components/BookConditionSelect.vue';
|
import BookConditionSelect from '@/components/BookConditionSelect.vue';
|
||||||
import PriceStockControl from '@/components/PriceStockControl.vue';
|
import PriceStockControl from '@/components/PriceStockControl.vue';
|
||||||
import BookProductList from '@/components/BookProductList.vue';
|
import BookProductList from '@/components/BookProductList.vue';
|
||||||
@ -320,7 +321,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['priceMode', 'priceType', 'averageRange', 'selectedPosition', 'freight', 'minValue']),
|
...mapState('price', ['priceMode', 'priceType', 'averageRange', 'freight', 'minValue']),
|
||||||
|
...mapState('warehouse', ['selectedPosition']),
|
||||||
|
|
||||||
// 根据相机状态动态设置页面容器样式
|
// 根据相机状态动态设置页面容器样式
|
||||||
pageContainerStyle() {
|
pageContainerStyle() {
|
||||||
@ -1118,6 +1120,10 @@
|
|||||||
const temporaryImages = [];
|
const temporaryImages = [];
|
||||||
console.log("当前图片数据fileList1", this.fileList1)
|
console.log("当前图片数据fileList1", this.fileList1)
|
||||||
this.fileList1.forEach((file, index) => {
|
this.fileList1.forEach((file, index) => {
|
||||||
|
// 跳过识图照片(本地状态)
|
||||||
|
if (file.isOCR || file.status === 'local') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 检查图片状态是否为error或者路径是临时路径
|
// 检查图片状态是否为error或者路径是临时路径
|
||||||
if (file.status === 'error' ||
|
if (file.status === 'error' ||
|
||||||
(file.url && (file.url.startsWith('file://') ||
|
(file.url && (file.url.startsWith('file://') ||
|
||||||
@ -1149,11 +1155,15 @@
|
|||||||
// 设置上传状态为true
|
// 设置上传状态为true
|
||||||
this.isUploading = true;
|
this.isUploading = true;
|
||||||
|
|
||||||
// 上传所有图片
|
// 过滤出需要上传的普通照片(排除识图照片)
|
||||||
|
const normalPhotos = this.fileList1.filter(file => !file.isOCR && file.status === "ready");
|
||||||
|
console.log('准备上传的普通照片:', normalPhotos);
|
||||||
|
|
||||||
|
// 按顺序上传所有普通照片
|
||||||
for (let i = 0; i < this.fileList1.length; i++) {
|
for (let i = 0; i < this.fileList1.length; i++) {
|
||||||
const file = this.fileList1[i];
|
const file = this.fileList1[i];
|
||||||
// 只上传状态为ready的图片
|
// 只上传状态为ready且非识图的图片
|
||||||
if (file.status === "ready") {
|
if (file.status === "ready" && !file.isOCR) {
|
||||||
// 更新状态为上传中
|
// 更新状态为上传中
|
||||||
this.fileList1.splice(i, 1, {
|
this.fileList1.splice(i, 1, {
|
||||||
...file,
|
...file,
|
||||||
@ -1494,9 +1504,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 图片上传到服务器
|
// 图片上传到服务器(带超时和重试机制)
|
||||||
uploadFilePromise(url, index) {
|
async uploadFilePromise(url, index, retryCount = 0) {
|
||||||
console.log(`uploadFilePromise被调用: url=${url}, index=${index}`);
|
const maxRetries = 2; // 最大重试次数
|
||||||
|
const timeout = 30000; // 30秒超时
|
||||||
|
|
||||||
|
console.log(`uploadFilePromise被调用: url=${url}, index=${index}, retryCount=${retryCount}`);
|
||||||
console.log("fileList1", this.fileList1)
|
console.log("fileList1", this.fileList1)
|
||||||
console.log(`当前fileList1状态:`, JSON.stringify(this.fileList1.map(f => ({
|
console.log(`当前fileList1状态:`, JSON.stringify(this.fileList1.map(f => ({
|
||||||
status: f.status,
|
status: f.status,
|
||||||
@ -1505,12 +1518,10 @@
|
|||||||
num: f.num
|
num: f.num
|
||||||
}))));
|
}))));
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// 检查参数
|
// 检查参数
|
||||||
if (!url) {
|
if (!url) {
|
||||||
console.error('上传失败: url参数为空');
|
console.error('上传失败: url参数为空');
|
||||||
reject(new Error('url参数为空'));
|
throw new Error('url参数为空');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前选项卡下的书名和ISBN
|
// 获取当前选项卡下的书名和ISBN
|
||||||
@ -1523,8 +1534,7 @@
|
|||||||
// 检查书名和ISBN
|
// 检查书名和ISBN
|
||||||
if (!bookName || !isbn) {
|
if (!bookName || !isbn) {
|
||||||
console.error(`上传失败: 书名或ISBN为空, 书名=${bookName}, ISBN=${isbn}`);
|
console.error(`上传失败: 书名或ISBN为空, 书名=${bookName}, ISBN=${isbn}`);
|
||||||
reject(new Error('书名或ISBN为空'));
|
throw new Error('书名或ISBN为空');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`开始上传图片: 书名=${bookName}, ISBN=${isbn}, 索引=${index}`);
|
console.log(`开始上传图片: 书名=${bookName}, ISBN=${isbn}, 索引=${index}`);
|
||||||
@ -1538,24 +1548,28 @@
|
|||||||
}
|
}
|
||||||
console.log(`上传图片编号: ${num}`);
|
console.log(`上传图片编号: ${num}`);
|
||||||
|
|
||||||
uni.uploadFile({
|
return new Promise((resolve, reject) => {
|
||||||
url: "https://api.buzhiyushu.cn/zhishu/shopGoods/uploadImages", // 后端接口地址
|
// 设置超时定时器
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
console.error(`图片上传超时(${timeout}ms): ${url}`);
|
||||||
|
reject(new Error('上传超时'));
|
||||||
|
}, timeout);
|
||||||
|
|
||||||
|
const uploadTask = uni.uploadFile({
|
||||||
|
url: "https://xcx.uploadfile.yushutx.com/uploadImages",
|
||||||
filePath: url,
|
filePath: url,
|
||||||
name: "file",
|
name: "file",
|
||||||
// formData: {
|
timeout: timeout, // 添加超时参数
|
||||||
// bookName: bookName,
|
|
||||||
// isbn: isbn,
|
|
||||||
// num: num,
|
|
||||||
// warehouseId: warehouseId,
|
|
||||||
// shelfId: shelfId,
|
|
||||||
// locationId: locationId,
|
|
||||||
// },
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
|
clearTimeout(timer); // 清除超时定时器
|
||||||
|
// 解析返回结果
|
||||||
console.log('图片上传成功:', res);
|
console.log('图片上传成功:', res);
|
||||||
// 解析返回结果
|
// 解析返回结果
|
||||||
const data = JSON.parse(res.data);
|
const data = JSON.parse(res.data);
|
||||||
const imageUrl = data.url || url;
|
console.log("data", data)
|
||||||
|
const urlData = JSON.parse(data.data);
|
||||||
|
const imageUrl = urlData.url || url;
|
||||||
|
console.log("url", imageUrl)
|
||||||
// 将上传成功的图片信息存储到数组中
|
// 将上传成功的图片信息存储到数组中
|
||||||
this.uploadedImages.push({
|
this.uploadedImages.push({
|
||||||
url: imageUrl,
|
url: imageUrl,
|
||||||
@ -1568,13 +1582,27 @@
|
|||||||
originalUrl: url
|
originalUrl: url
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`图片上传成功,返回URL: ${imageUrl}`);
|
resolve(imageUrl);
|
||||||
resolve(imageUrl); // 如果返回了URL则使用,否则使用原URL
|
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: async (err) => {
|
||||||
|
clearTimeout(timer); // 清除超时定时器
|
||||||
console.error('图片上传失败:', err);
|
console.error('图片上传失败:', err);
|
||||||
|
|
||||||
|
// 如果未达到最大重试次数,则重试
|
||||||
|
if (retryCount < maxRetries) {
|
||||||
|
console.log(`上传失败,准备重试 (${retryCount + 1}/${maxRetries})...`);
|
||||||
|
try {
|
||||||
|
// 等待1秒后重试
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
const result = await this.uploadFilePromise(url, index, retryCount + 1);
|
||||||
|
resolve(result);
|
||||||
|
} catch (retryError) {
|
||||||
|
reject(retryError);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -1619,8 +1647,8 @@
|
|||||||
|
|
||||||
const response = await new Promise((resolve, reject) => {
|
const response = await new Promise((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
// url: 'https://api.buzhiyushu.cn/zhishu/shopGoods/submitFromCopyrightPage',
|
url: 'https://api.buzhiyushu.cn/zhishu/shopGoods/submitFromCopyrightPage',
|
||||||
url: 'http://192.168.101.127:8080/zhishu/shopGoods/submitFromCopyrightPage',
|
// url: 'http://192.168.101.209:8080/zhishu/shopGoods/submitFromCopyrightPage',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: formData,
|
data: formData,
|
||||||
header: {
|
header: {
|
||||||
@ -1783,7 +1811,7 @@
|
|||||||
|
|
||||||
// 跳转到上书记录页面并传递数据
|
// 跳转到上书记录页面并传递数据
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/scan/book-records',
|
url: '/pkgUser/book-records',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
// 向打开的页面传递数据
|
// 向打开的页面传递数据
|
||||||
res.eventChannel.emit('bookRecordsData', {
|
res.eventChannel.emit('bookRecordsData', {
|
||||||
@ -2087,11 +2115,12 @@
|
|||||||
// 将拍摄的照片添加到fileList1中,但不上传
|
// 将拍摄的照片添加到fileList1中,但不上传
|
||||||
const newFile = {
|
const newFile = {
|
||||||
url: tempFilePath,
|
url: tempFilePath,
|
||||||
status: "ready",
|
status: "local", // 改为"local"状态,避免触发上传验证
|
||||||
message: "待上传",
|
message: "本地识图照片",
|
||||||
name: `识图-${Date.now()}.jpg`, // 使用时间戳确保唯一性
|
name: `识图-${Date.now()}.jpg`, // 使用时间戳确保唯一性
|
||||||
hidden: true, // 添加hidden属性,表示不在UI中显示
|
hidden: true, // 添加hidden属性,表示不在UI中显示
|
||||||
num: "2" // 设置为第二张图片
|
num: "999", // 设置为很大的编号,确保排在最后
|
||||||
|
isOCR: true // 标记为识图照片
|
||||||
};
|
};
|
||||||
|
|
||||||
// 移除之前的识图上传图片(如果存在)
|
// 移除之前的识图上传图片(如果存在)
|
||||||
@ -0,0 +1,509 @@
|
|||||||
|
<template>
|
||||||
|
<!-- <view class="form-container"> -->
|
||||||
|
<!-- <view class="view-container"> -->
|
||||||
|
<view class="view-item view-item-3">
|
||||||
|
<view class="label" @click="onLabelClick">货区</view>
|
||||||
|
<view class="select" @click="openPicker">
|
||||||
|
{{ selectedStorage || '请选择货区' }}
|
||||||
|
</view>
|
||||||
|
<u-picker :show="showPicker" ref="uPicker" :columns="columns" @cancel="cancelPicker" @confirm="confirmPicker"
|
||||||
|
@change="changeHandler"></u-picker>
|
||||||
|
</view>
|
||||||
|
<!-- </view> -->
|
||||||
|
<!-- </view> -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'WarehouseSelector',
|
||||||
|
props: {
|
||||||
|
// 初始选中的货区
|
||||||
|
initialStorage: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 原始warehouse对象
|
||||||
|
initialWarehouse: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showPicker: false,
|
||||||
|
selectedStorage: this.initialStorage,
|
||||||
|
warehouse: '',
|
||||||
|
shelf: '',
|
||||||
|
location: '',
|
||||||
|
columns: [
|
||||||
|
[], // 仓库列表
|
||||||
|
[], // 货架列表
|
||||||
|
[] // 货位列表
|
||||||
|
],
|
||||||
|
shelves: [], // 货架列表
|
||||||
|
locations: [], // 货位列表
|
||||||
|
selectedWarehouse: null,
|
||||||
|
selectedSheId: null,
|
||||||
|
selectedFreId: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// initData 会在 initialWarehouse watch 中被调用
|
||||||
|
// loadStorageSelection 会在 selectedWarehouse watch 的 fetchShelves 完成后被调用
|
||||||
|
// 这里不需要重复调用,避免竞态条件
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// 监听initialWarehouse变化,重新初始化
|
||||||
|
initialWarehouse: {
|
||||||
|
handler(newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.selectedWarehouse = newValue;
|
||||||
|
this.initData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
selectedWarehouse: {
|
||||||
|
handler(newVal, oldVal) {
|
||||||
|
if (newVal) {
|
||||||
|
// 重新拉取货架和货位数据
|
||||||
|
this.columns[0] = [newVal.name];
|
||||||
|
this.fetchShelves(newVal.id).then(shelves => {
|
||||||
|
if (shelves.length > 0) {
|
||||||
|
this.columns[1] = shelves.map(item => item.code);
|
||||||
|
this.fetchLocations(shelves[0].id).then(locations => {
|
||||||
|
this.columns[2] = locations.map(item => item.code);
|
||||||
|
|
||||||
|
// 在数据加载完成后,尝试加载该仓库的货区状态
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.loadStorageSelection();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.columns[1] = [];
|
||||||
|
this.columns[2] = [];
|
||||||
|
// 没有货架数据时也要尝试加载货区状态(可能会清空)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.loadStorageSelection();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果是仓库切换(不是初始化),先清空当前状态
|
||||||
|
if (oldVal && oldVal.id !== newVal.id) {
|
||||||
|
console.log(`仓库从${oldVal.id}切换到${newVal.id},清空当前货区状态`);
|
||||||
|
this.clearStorageSelection();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有选择仓库时清空状态
|
||||||
|
this.clearStorageSelection();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 保存货区选择状态到本地存储
|
||||||
|
saveStorageSelection() {
|
||||||
|
// 只有在选择了仓库的情况下才保存货区状态
|
||||||
|
if (!this.selectedWarehouse || !this.selectedWarehouse.id) {
|
||||||
|
console.log('未选择仓库,不保存货区状态');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const storageData = {
|
||||||
|
selectedStorage: this.selectedStorage,
|
||||||
|
warehouse: this.warehouse,
|
||||||
|
shelf: this.shelf,
|
||||||
|
location: this.location,
|
||||||
|
selectedSheId: this.selectedSheId,
|
||||||
|
selectedFreId: this.selectedFreId
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用仓库ID作为键的一部分,为每个仓库单独保存货区状态
|
||||||
|
const storageKey = `selectedStorageData_${this.selectedWarehouse.id}`;
|
||||||
|
uni.setStorageSync(storageKey, storageData);
|
||||||
|
console.log(`保存仓库${this.selectedWarehouse.id}的货区选择状态:`, storageData);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 从本地存储加载货区选择状态
|
||||||
|
loadStorageSelection() {
|
||||||
|
try {
|
||||||
|
// 只有在选择了仓库的情况下才加载货区状态
|
||||||
|
if (!this.selectedWarehouse || !this.selectedWarehouse.id) {
|
||||||
|
console.log('未选择仓库,清空货区状态');
|
||||||
|
this.clearStorageSelection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据仓库ID加载对应的货区状态
|
||||||
|
const storageKey = `selectedStorageData_${this.selectedWarehouse.id}`;
|
||||||
|
const storageData = uni.getStorageSync(storageKey);
|
||||||
|
|
||||||
|
if (storageData) {
|
||||||
|
console.log(`加载仓库${this.selectedWarehouse.id}的货区选择状态:`, storageData);
|
||||||
|
this.selectedStorage = storageData.selectedStorage || '';
|
||||||
|
this.warehouse = storageData.warehouse || '';
|
||||||
|
this.shelf = storageData.shelf || '';
|
||||||
|
this.location = storageData.location || '';
|
||||||
|
this.selectedSheId = storageData.selectedSheId || null;
|
||||||
|
this.selectedFreId = storageData.selectedFreId || null;
|
||||||
|
|
||||||
|
// 强制更新视图
|
||||||
|
this.$forceUpdate();
|
||||||
|
} else {
|
||||||
|
console.log(`仓库${this.selectedWarehouse.id}没有保存的货区状态,清空当前状态`);
|
||||||
|
this.clearStorageSelection();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载货区选择状态失败:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 清空货区选择状态
|
||||||
|
clearStorageSelection() {
|
||||||
|
this.selectedStorage = '';
|
||||||
|
this.warehouse = '';
|
||||||
|
this.shelf = '';
|
||||||
|
this.location = '';
|
||||||
|
this.selectedSheId = null;
|
||||||
|
this.selectedFreId = null;
|
||||||
|
|
||||||
|
// 重置选择器的值
|
||||||
|
this.selectedValues = ['', '', ''];
|
||||||
|
|
||||||
|
// 强制更新视图
|
||||||
|
this.$forceUpdate();
|
||||||
|
console.log('已清空货区选择状态');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
async initData() {
|
||||||
|
if (this.initialWarehouse) {
|
||||||
|
this.selectedWarehouse = this.initialWarehouse;
|
||||||
|
|
||||||
|
// 设置仓库列
|
||||||
|
this.columns[0] = [this.initialWarehouse.name];
|
||||||
|
|
||||||
|
// 加载货架数据
|
||||||
|
if (this.initialWarehouse.id) {
|
||||||
|
const shelves = await this.fetchShelves(this.initialWarehouse.id);
|
||||||
|
// console.log('初始化货架数据:', shelves);
|
||||||
|
|
||||||
|
// 如果存在货架数据,加载第一个货架的货位数据
|
||||||
|
if (shelves && shelves.length > 0) {
|
||||||
|
await this.fetchLocations(shelves[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新选中的货区数据
|
||||||
|
updateSelectedStorage(data) {
|
||||||
|
// console.log('更新货区数据:', data);
|
||||||
|
if (data) {
|
||||||
|
this.selectedStorage = data.storage;
|
||||||
|
this.warehouse = data.warehouse;
|
||||||
|
this.shelf = data.shelf;
|
||||||
|
this.location = data.location;
|
||||||
|
this.selectedSheId = data.shelfId;
|
||||||
|
this.selectedFreId = data.locationId;
|
||||||
|
|
||||||
|
// 强制更新视图
|
||||||
|
this.$forceUpdate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 打开选择器
|
||||||
|
openPicker() {
|
||||||
|
// 在打开选择器前确保数据已加载
|
||||||
|
if (this.columns[0].length === 0 && this.selectedWarehouse) {
|
||||||
|
this.columns[0] = [this.selectedWarehouse.name];
|
||||||
|
}
|
||||||
|
this.showPicker = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 点击标签
|
||||||
|
onLabelClick() {
|
||||||
|
this.$emit('label-click');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 取消选择
|
||||||
|
cancelPicker() {
|
||||||
|
this.showPicker = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 确认选择
|
||||||
|
confirmPicker(e) {
|
||||||
|
const {
|
||||||
|
value
|
||||||
|
} = e;
|
||||||
|
const [warehouse, shelf, location] = value;
|
||||||
|
|
||||||
|
// 只有当三个值都存在时才保存选中的值
|
||||||
|
if (warehouse && shelf && location) {
|
||||||
|
this.warehouse = warehouse;
|
||||||
|
this.shelf = shelf;
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
// 更新显示文本
|
||||||
|
this.selectedStorage = `${warehouse} / ${shelf} / ${location}`;
|
||||||
|
|
||||||
|
// 查找选中项的ID
|
||||||
|
const selectedShelf = this.shelves.find(item => item.code === shelf);
|
||||||
|
const selectedLocation = this.locations.find(item => item.code === location);
|
||||||
|
|
||||||
|
// 保存选中ID
|
||||||
|
this.selectedSheId = selectedShelf?.id;
|
||||||
|
this.selectedFreId = selectedLocation?.id;
|
||||||
|
|
||||||
|
// 保存选择状态到本地存储
|
||||||
|
this.saveStorageSelection();
|
||||||
|
|
||||||
|
// 向父组件发送选中数据
|
||||||
|
this.$emit('storage-selected', {
|
||||||
|
storage: this.selectedStorage,
|
||||||
|
warehouse: this.warehouse,
|
||||||
|
shelf: this.shelf,
|
||||||
|
location: this.location,
|
||||||
|
shelfId: this.selectedSheId,
|
||||||
|
locationId: this.selectedFreId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 如果不完整,提示用户
|
||||||
|
uni.showToast({
|
||||||
|
title: '请完整选择仓库、货架和货位',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.showPicker = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理picker列变化
|
||||||
|
async changeHandler(e) {
|
||||||
|
// 防御性检查,确保e和必要的属性存在
|
||||||
|
if (!e) {
|
||||||
|
console.warn('changeHandler: 事件对象为空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('选择器变化:', e);
|
||||||
|
|
||||||
|
// 获取列索引和选中的索引数组
|
||||||
|
const {
|
||||||
|
columnIndex,
|
||||||
|
index
|
||||||
|
} = e;
|
||||||
|
|
||||||
|
// 如果是选择了仓库(第0列)
|
||||||
|
if (columnIndex === 0) {
|
||||||
|
// 获取选中的仓库
|
||||||
|
const warehouseName = this.columns[0][index];
|
||||||
|
|
||||||
|
// 使用仓库ID获取货架数据
|
||||||
|
if (this.selectedWarehouse && this.selectedWarehouse.id) {
|
||||||
|
await this.fetchShelves(this.selectedWarehouse.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是选择了货架(第1列)
|
||||||
|
if (columnIndex === 1 && this.shelves.length > 0) {
|
||||||
|
// 获取选中的货架
|
||||||
|
const selectedShelf = this.shelves[index];
|
||||||
|
// console.log('选中货架:', selectedShelf);
|
||||||
|
|
||||||
|
// 保存选中的货架ID
|
||||||
|
if (selectedShelf && selectedShelf.id) {
|
||||||
|
this.selectedSheId = selectedShelf.id;
|
||||||
|
await this.fetchLocations(selectedShelf.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取货架列表
|
||||||
|
async fetchShelves(depotId) {
|
||||||
|
try {
|
||||||
|
// console.log('获取货架数据,仓库ID:', depotId);
|
||||||
|
|
||||||
|
const response = await uni.request({
|
||||||
|
url: 'https://api.buzhiyushu.cn/shelves/shelves/sheNamelist',
|
||||||
|
data: {
|
||||||
|
depotId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用数组解构获取响应结果
|
||||||
|
const [err, res] = Array.isArray(response) ? response : [null, response];
|
||||||
|
|
||||||
|
if (!res?.data?.rows) {
|
||||||
|
console.error('获取货架数据失败,返回空数据');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shelves = res.data.rows; // 保存货架列表
|
||||||
|
this.columns[1] = this.shelves.map(item => item.code || '未知货架');
|
||||||
|
|
||||||
|
// console.log('获取到货架数据:', this.shelves);
|
||||||
|
// console.log('货架选项:', this.columns[1]);
|
||||||
|
|
||||||
|
// 更新UI
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.uPicker) {
|
||||||
|
this.$refs.uPicker.setColumnValues(1, this.columns[1]);
|
||||||
|
// 清空货位列
|
||||||
|
this.$refs.uPicker.setColumnValues(2, []);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.shelves;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取货架失败:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取货位列表
|
||||||
|
async fetchLocations(sheId) {
|
||||||
|
if (!sheId) {
|
||||||
|
console.error('获取货位列表失败:未提供货架ID');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// console.log('获取货位数据,货架ID:', sheId);
|
||||||
|
|
||||||
|
const response = await uni.request({
|
||||||
|
url: 'https://api.buzhiyushu.cn/shelves/shelves/freNamelist',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
sheId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用数组解构获取响应结果
|
||||||
|
const [err, res] = Array.isArray(response) ? response : [null, response];
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
console.error('获取货位请求错误:', err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res || !res.data || !res.data.rows) {
|
||||||
|
console.error('货位响应数据格式不正确');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const locations = res.data.rows;
|
||||||
|
// console.log('获取到货位数据:', locations);
|
||||||
|
|
||||||
|
// 保存货位数据
|
||||||
|
this.locations = locations;
|
||||||
|
|
||||||
|
// 更新货位列
|
||||||
|
this.columns[2] = locations.map(item => item.code || '未知货位');
|
||||||
|
// console.log('货位选项:', this.columns[2]);
|
||||||
|
|
||||||
|
// 更新UI
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.uPicker) {
|
||||||
|
this.$refs.uPicker.setColumnValues(2, this.columns[2]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return locations;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取货位列表失败:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 监听外部更新事件
|
||||||
|
this.$on('storage-selected', this.updateSelectedStorage);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// 移除事件监听
|
||||||
|
this.$off('storage-selected', this.updateSelectedStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.view-container {
|
||||||
|
display: flex;
|
||||||
|
background-color: #fff;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
/* 垂直居中 */
|
||||||
|
padding: 3rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
position: relative;
|
||||||
|
/* border-bottom: 1px solid #ccc; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item-3 {
|
||||||
|
flex: 3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item-4 {
|
||||||
|
flex: 4 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item-6 {
|
||||||
|
flex: 6 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item-7 {
|
||||||
|
flex: 7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item>.label {
|
||||||
|
padding: 0rpx 15rpx;
|
||||||
|
text-align: center;
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
color: #000;
|
||||||
|
font-size: 28rpx;
|
||||||
|
background-color: #ccc;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item>.select {
|
||||||
|
padding: 16rpx 0rpx;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 40rpx;
|
||||||
|
font-size: 18rpx;
|
||||||
|
line-height: 45rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
padding-left: 10rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .u-picker__view__column__item[class*="data-v-"] {
|
||||||
|
font-size: 30rpx !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保选择器文本也使用相同大小 */
|
||||||
|
.view-item>.select {
|
||||||
|
font-size: 23rpx !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .data-v-55c89db1 .u-toolbar__wrapper__confirm.data-v-55c89db1 {
|
||||||
|
color: #3c9cff;
|
||||||
|
font-size: 45rpx;
|
||||||
|
padding: 0 15rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
3410
pkgUpload/backup_picker_fix_20260511_143545/isbn-upload-index.vue
Normal file
3410
pkgUpload/backup_picker_fix_20260511_143545/isbn-upload-index.vue
Normal file
File diff suppressed because it is too large
Load Diff
3700
pkgUpload/backup_picker_fix_20260511_143545/photo-upload-index.vue
Normal file
3700
pkgUpload/backup_picker_fix_20260511_143545/photo-upload-index.vue
Normal file
File diff suppressed because it is too large
Load Diff
134
pkgUpload/backup_tab_in_page_20260511_140540/index-index.vue
Normal file
134
pkgUpload/backup_tab_in_page_20260511_140540/index-index.vue
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page-container">
|
||||||
|
<!-- 上传入口 -->
|
||||||
|
<view class="upload-entrance">
|
||||||
|
<view class="entrance-title">请选择上传方式</view>
|
||||||
|
<view class="entrance-cards">
|
||||||
|
<view class="entrance-card" @click="navigateToUpload('isbn')">
|
||||||
|
<view class="card-icon isbn-icon">📚</view>
|
||||||
|
<view class="card-content">
|
||||||
|
<text class="card-title">ISBN-上传</text>
|
||||||
|
<text class="card-desc">扫描或输入ISBN快速上架</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-arrow">›</view>
|
||||||
|
</view>
|
||||||
|
<view class="entrance-card" @click="navigateToUpload('photo')">
|
||||||
|
<view class="card-icon photo-icon">📷</view>
|
||||||
|
<view class="card-content">
|
||||||
|
<text class="card-title">无ISBN-上传</text>
|
||||||
|
<text class="card-desc">拍照识别书籍信息上架</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-arrow">›</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
options: {
|
||||||
|
enablePullDownRefresh: false,
|
||||||
|
backgroundTextStyle: 'dark'
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedWarehouse: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
// 每次页面显示时都重新获取仓库
|
||||||
|
this.selectedWarehouse = uni.getStorageSync("selectedWarehouse") || null;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 导航到上传页面
|
||||||
|
navigateToUpload(type) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pkgUpload/upload-container/index?tab=' + type
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.page-container {
|
||||||
|
padding: 30rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-entrance {
|
||||||
|
padding: 40rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entrance-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entrance-cards {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entrance-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx 30rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entrance-card:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 48rpx;
|
||||||
|
margin-right: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.isbn-icon {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-icon {
|
||||||
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-desc {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-arrow {
|
||||||
|
font-size: 48rpx;
|
||||||
|
color: #ccc;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
3296
pkgUpload/backup_tab_in_page_20260511_140540/isbn-upload-index.vue
Normal file
3296
pkgUpload/backup_tab_in_page_20260511_140540/isbn-upload-index.vue
Normal file
File diff suppressed because it is too large
Load Diff
198
pkgUpload/backup_tab_in_page_20260511_140540/pages.json
Normal file
198
pkgUpload/backup_tab_in_page_20260511_140540/pages.json
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
{
|
||||||
|
"easycom": {
|
||||||
|
"^u-(.*)": "@/uni_modules/uview-ui/components/u-$1/u-$1.vue",
|
||||||
|
"autoscan": true,
|
||||||
|
"custom": {
|
||||||
|
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"globalStyle": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationBarTitleText": "智助",
|
||||||
|
"navigationBarBackgroundColor": "#F8F8F8",
|
||||||
|
"backgroundColor": "#F5F5F5",
|
||||||
|
"enablePullDownRefresh": true,
|
||||||
|
"backgroundTextStyle": "dark"
|
||||||
|
},
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "pages/login/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "登录"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/entry/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "功能入口",
|
||||||
|
"navigationBarBackgroundColor": "#F8F8F8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/index/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "首页"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/scan/history",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "设置"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/user/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "个人中心"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subPackages": [
|
||||||
|
{
|
||||||
|
"root": "pkgUpload",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "upload-container/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "图书上传",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "isbn-upload/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "Isbn-上传",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "title-upload/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "仅书名-上传"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "photo-upload/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "无ISBN-上传",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "pkgManage",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "goods/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品管理",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "shelf/management",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "货架管理"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "clone-tool/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品克隆工具",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "pkgUser",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "memberSelect",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "会员等级选择",
|
||||||
|
"navigationBarBackgroundColor": "#F8F9FC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "dispatch-management",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "下发管理"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "book-records",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "上书记录"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "pages/warehouse",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "warehouse-select",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "选择仓库"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "create-warehouse",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"app-plus": {
|
||||||
|
"titleNView": false,
|
||||||
|
"animationType": "slide-in-right"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "order-query",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "仓库订单查询",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"root": "pages/register",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "注册"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tabBar": {
|
||||||
|
"color": "#7A7E83",
|
||||||
|
"selectedColor": "#2979ff",
|
||||||
|
"borderStyle": "black",
|
||||||
|
"backgroundColor": "#ffffff",
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"pagePath": "pages/index/index",
|
||||||
|
"iconPath": "/static/tabbar/index.png",
|
||||||
|
"selectedIconPath": "/static/tabbar/index.png",
|
||||||
|
"text": "首页"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/scan/history",
|
||||||
|
"iconPath": "/static/tabbar/setting.png",
|
||||||
|
"selectedIconPath": "/static/tabbar/setting.png",
|
||||||
|
"text": "设置"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/user/index",
|
||||||
|
"iconPath": "/static/tabbar/my.png",
|
||||||
|
"selectedIconPath": "/static/tabbar/my.png",
|
||||||
|
"text": "我的"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
3637
pkgUpload/backup_tab_in_page_20260511_140540/photo-upload-index.vue
Normal file
3637
pkgUpload/backup_tab_in_page_20260511_140540/photo-upload-index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,206 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 顶部Tab切换 -->
|
||||||
|
<view class="tab-header">
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'active': currentTab === 'isbn' }"
|
||||||
|
@click="switchTab('isbn')">
|
||||||
|
<text>ISBN上传</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'active': currentTab === 'photo' }"
|
||||||
|
@click="switchTab('photo')">
|
||||||
|
<text>无ISBN上传</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Tab内容区域 -->
|
||||||
|
<view class="tab-content">
|
||||||
|
<!-- 调试信息 -->
|
||||||
|
<view class="debug-info">
|
||||||
|
<text>当前Tab: {{ currentTab }}</text>
|
||||||
|
<text>仓库: {{ selectedWarehouse ? selectedWarehouse.name : '未选择' }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 无仓库数据时显示提示 -->
|
||||||
|
<view v-if="!selectedWarehouse" class="no-warehouse-tip">
|
||||||
|
<text>请先选择仓库</text>
|
||||||
|
<button type="primary" size="mini" @click="goSelectWarehouse">选择仓库</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 有仓库数据时才渲染上传组件 -->
|
||||||
|
<template v-if="selectedWarehouse">
|
||||||
|
<!-- ISBN上传组件 -->
|
||||||
|
<isbn-upload
|
||||||
|
v-show="currentTab === 'isbn'"
|
||||||
|
ref="isbnUpload"
|
||||||
|
:selectedWarehouse="selectedWarehouse"
|
||||||
|
@camera-status-change="handleCameraStatusChange">
|
||||||
|
</isbn-upload>
|
||||||
|
|
||||||
|
<!-- 无ISBN上传组件 -->
|
||||||
|
<photo-upload
|
||||||
|
v-show="currentTab === 'photo'"
|
||||||
|
ref="photoUpload"
|
||||||
|
:selectedWarehouse="selectedWarehouse"
|
||||||
|
@camera-status-change="handleCameraStatusChange">
|
||||||
|
</photo-upload>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import IsbnUpload from '../isbn-upload/index.vue';
|
||||||
|
import PhotoUpload from '../photo-upload/index.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UploadContainer',
|
||||||
|
components: {
|
||||||
|
'isbn-upload': IsbnUpload,
|
||||||
|
'photo-upload': PhotoUpload
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentTab: 'isbn', // 默认显示ISBN上传
|
||||||
|
selectedWarehouse: null,
|
||||||
|
isCameraOpen: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
// 获取传递的仓库信息
|
||||||
|
if (options.warehouse) {
|
||||||
|
try {
|
||||||
|
this.selectedWarehouse = JSON.parse(decodeURIComponent(options.warehouse));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析仓库信息失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从本地存储获取仓库信息
|
||||||
|
if (!this.selectedWarehouse) {
|
||||||
|
this.selectedWarehouse = uni.getStorageSync('selectedWarehouse');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取Tab参数
|
||||||
|
if (options.tab) {
|
||||||
|
this.currentTab = options.tab;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
// 页面显示时,刷新仓库信息
|
||||||
|
const warehouse = uni.getStorageSync('selectedWarehouse');
|
||||||
|
if (warehouse) {
|
||||||
|
this.selectedWarehouse = warehouse;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 切换Tab
|
||||||
|
switchTab(tab) {
|
||||||
|
// 如果相机正在使用,提示用户
|
||||||
|
if (this.isCameraOpen) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先关闭相机',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentTab = tab;
|
||||||
|
|
||||||
|
// 切换时的初始化处理
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (tab === 'isbn' && this.$refs.isbnUpload) {
|
||||||
|
// 可以在这里调用子组件的方法
|
||||||
|
} else if (tab === 'photo' && this.$refs.photoUpload) {
|
||||||
|
// 可以在这里调用子组件的方法
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理相机状态变化
|
||||||
|
handleCameraStatusChange(isOpen) {
|
||||||
|
this.isCameraOpen = isOpen;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 跳转到选择仓库页面
|
||||||
|
goSelectWarehouse() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/warehouse/warehouse-select'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab头部样式 */
|
||||||
|
.tab-header {
|
||||||
|
display: flex;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-bottom: 1rpx solid #e5e5e5;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active text {
|
||||||
|
color: #007AFF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 60rpx;
|
||||||
|
height: 4rpx;
|
||||||
|
background-color: #007AFF;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab内容区域 */
|
||||||
|
.tab-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无仓库提示 */
|
||||||
|
.no-warehouse-tip {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 100rpx 0;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-warehouse-tip text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,508 @@
|
|||||||
|
<template>
|
||||||
|
<!-- <view class="form-container"> -->
|
||||||
|
<!-- <view class="view-container"> -->
|
||||||
|
<view class="view-item view-item-3">
|
||||||
|
<view class="label" @click="onLabelClick">货区</view>
|
||||||
|
<view class="select" @click="openPicker">
|
||||||
|
{{ selectedStorage || '请选择货区' }}
|
||||||
|
</view>
|
||||||
|
<u-picker :show="showPicker" ref="uPicker" :columns="columns" @cancel="cancelPicker" @confirm="confirmPicker"
|
||||||
|
@change="changeHandler"></u-picker>
|
||||||
|
</view>
|
||||||
|
<!-- </view> -->
|
||||||
|
<!-- </view> -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'WarehouseSelector',
|
||||||
|
props: {
|
||||||
|
// 初始选中的货区
|
||||||
|
initialStorage: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 原始warehouse对象
|
||||||
|
initialWarehouse: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showPicker: false,
|
||||||
|
selectedStorage: this.initialStorage,
|
||||||
|
warehouse: '',
|
||||||
|
shelf: '',
|
||||||
|
location: '',
|
||||||
|
columns: [
|
||||||
|
[], // 仓库列表
|
||||||
|
[], // 货架列表
|
||||||
|
[] // 货位列表
|
||||||
|
],
|
||||||
|
shelves: [], // 货架列表
|
||||||
|
locations: [], // 货位列表
|
||||||
|
selectedWarehouse: null,
|
||||||
|
selectedSheId: null,
|
||||||
|
selectedFreId: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initData();
|
||||||
|
this.loadStorageSelection();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// 监听initialWarehouse变化,重新初始化
|
||||||
|
initialWarehouse: {
|
||||||
|
handler(newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.selectedWarehouse = newValue;
|
||||||
|
this.initData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
selectedWarehouse: {
|
||||||
|
handler(newVal, oldVal) {
|
||||||
|
if (newVal) {
|
||||||
|
// 重新拉取货架和货位数据
|
||||||
|
this.columns[0] = [newVal.name];
|
||||||
|
this.fetchShelves(newVal.id).then(shelves => {
|
||||||
|
if (shelves.length > 0) {
|
||||||
|
this.columns[1] = shelves.map(item => item.code);
|
||||||
|
this.fetchLocations(shelves[0].id).then(locations => {
|
||||||
|
this.columns[2] = locations.map(item => item.code);
|
||||||
|
|
||||||
|
// 在数据加载完成后,尝试加载该仓库的货区状态
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.loadStorageSelection();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.columns[1] = [];
|
||||||
|
this.columns[2] = [];
|
||||||
|
// 没有货架数据时也要尝试加载货区状态(可能会清空)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.loadStorageSelection();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果是仓库切换(不是初始化),先清空当前状态
|
||||||
|
if (oldVal && oldVal.id !== newVal.id) {
|
||||||
|
console.log(`仓库从${oldVal.id}切换到${newVal.id},清空当前货区状态`);
|
||||||
|
this.clearStorageSelection();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有选择仓库时清空状态
|
||||||
|
this.clearStorageSelection();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 保存货区选择状态到本地存储
|
||||||
|
saveStorageSelection() {
|
||||||
|
// 只有在选择了仓库的情况下才保存货区状态
|
||||||
|
if (!this.selectedWarehouse || !this.selectedWarehouse.id) {
|
||||||
|
console.log('未选择仓库,不保存货区状态');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const storageData = {
|
||||||
|
selectedStorage: this.selectedStorage,
|
||||||
|
warehouse: this.warehouse,
|
||||||
|
shelf: this.shelf,
|
||||||
|
location: this.location,
|
||||||
|
selectedSheId: this.selectedSheId,
|
||||||
|
selectedFreId: this.selectedFreId
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用仓库ID作为键的一部分,为每个仓库单独保存货区状态
|
||||||
|
const storageKey = `selectedStorageData_${this.selectedWarehouse.id}`;
|
||||||
|
uni.setStorageSync(storageKey, storageData);
|
||||||
|
console.log(`保存仓库${this.selectedWarehouse.id}的货区选择状态:`, storageData);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 从本地存储加载货区选择状态
|
||||||
|
loadStorageSelection() {
|
||||||
|
try {
|
||||||
|
// 只有在选择了仓库的情况下才加载货区状态
|
||||||
|
if (!this.selectedWarehouse || !this.selectedWarehouse.id) {
|
||||||
|
console.log('未选择仓库,清空货区状态');
|
||||||
|
this.clearStorageSelection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据仓库ID加载对应的货区状态
|
||||||
|
const storageKey = `selectedStorageData_${this.selectedWarehouse.id}`;
|
||||||
|
const storageData = uni.getStorageSync(storageKey);
|
||||||
|
|
||||||
|
if (storageData) {
|
||||||
|
console.log(`加载仓库${this.selectedWarehouse.id}的货区选择状态:`, storageData);
|
||||||
|
this.selectedStorage = storageData.selectedStorage || '';
|
||||||
|
this.warehouse = storageData.warehouse || '';
|
||||||
|
this.shelf = storageData.shelf || '';
|
||||||
|
this.location = storageData.location || '';
|
||||||
|
this.selectedSheId = storageData.selectedSheId || null;
|
||||||
|
this.selectedFreId = storageData.selectedFreId || null;
|
||||||
|
|
||||||
|
// 强制更新视图
|
||||||
|
this.$forceUpdate();
|
||||||
|
} else {
|
||||||
|
console.log(`仓库${this.selectedWarehouse.id}没有保存的货区状态,清空当前状态`);
|
||||||
|
this.clearStorageSelection();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载货区选择状态失败:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 清空货区选择状态
|
||||||
|
clearStorageSelection() {
|
||||||
|
this.selectedStorage = '';
|
||||||
|
this.warehouse = '';
|
||||||
|
this.shelf = '';
|
||||||
|
this.location = '';
|
||||||
|
this.selectedSheId = null;
|
||||||
|
this.selectedFreId = null;
|
||||||
|
|
||||||
|
// 重置选择器的值
|
||||||
|
this.selectedValues = ['', '', ''];
|
||||||
|
|
||||||
|
// 强制更新视图
|
||||||
|
this.$forceUpdate();
|
||||||
|
console.log('已清空货区选择状态');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
async initData() {
|
||||||
|
if (this.initialWarehouse) {
|
||||||
|
this.selectedWarehouse = this.initialWarehouse;
|
||||||
|
|
||||||
|
// 设置仓库列
|
||||||
|
this.columns[0] = [this.initialWarehouse.name];
|
||||||
|
|
||||||
|
// 加载货架数据
|
||||||
|
if (this.initialWarehouse.id) {
|
||||||
|
const shelves = await this.fetchShelves(this.initialWarehouse.id);
|
||||||
|
// console.log('初始化货架数据:', shelves);
|
||||||
|
|
||||||
|
// 如果存在货架数据,加载第一个货架的货位数据
|
||||||
|
if (shelves && shelves.length > 0) {
|
||||||
|
await this.fetchLocations(shelves[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新选中的货区数据
|
||||||
|
updateSelectedStorage(data) {
|
||||||
|
// console.log('更新货区数据:', data);
|
||||||
|
if (data) {
|
||||||
|
this.selectedStorage = data.storage;
|
||||||
|
this.warehouse = data.warehouse;
|
||||||
|
this.shelf = data.shelf;
|
||||||
|
this.location = data.location;
|
||||||
|
this.selectedSheId = data.shelfId;
|
||||||
|
this.selectedFreId = data.locationId;
|
||||||
|
|
||||||
|
// 强制更新视图
|
||||||
|
this.$forceUpdate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 打开选择器
|
||||||
|
openPicker() {
|
||||||
|
// 在打开选择器前确保数据已加载
|
||||||
|
if (this.columns[0].length === 0 && this.selectedWarehouse) {
|
||||||
|
this.columns[0] = [this.selectedWarehouse.name];
|
||||||
|
}
|
||||||
|
this.showPicker = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 点击标签
|
||||||
|
onLabelClick() {
|
||||||
|
this.$emit('label-click');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 取消选择
|
||||||
|
cancelPicker() {
|
||||||
|
this.showPicker = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 确认选择
|
||||||
|
confirmPicker(e) {
|
||||||
|
const {
|
||||||
|
value
|
||||||
|
} = e;
|
||||||
|
const [warehouse, shelf, location] = value;
|
||||||
|
|
||||||
|
// 只有当三个值都存在时才保存选中的值
|
||||||
|
if (warehouse && shelf && location) {
|
||||||
|
this.warehouse = warehouse;
|
||||||
|
this.shelf = shelf;
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
// 更新显示文本
|
||||||
|
this.selectedStorage = `${warehouse} / ${shelf} / ${location}`;
|
||||||
|
|
||||||
|
// 查找选中项的ID
|
||||||
|
const selectedShelf = this.shelves.find(item => item.code === shelf);
|
||||||
|
const selectedLocation = this.locations.find(item => item.code === location);
|
||||||
|
|
||||||
|
// 保存选中ID
|
||||||
|
this.selectedSheId = selectedShelf?.id;
|
||||||
|
this.selectedFreId = selectedLocation?.id;
|
||||||
|
|
||||||
|
// 保存选择状态到本地存储
|
||||||
|
this.saveStorageSelection();
|
||||||
|
|
||||||
|
// 向父组件发送选中数据
|
||||||
|
this.$emit('storage-selected', {
|
||||||
|
storage: this.selectedStorage,
|
||||||
|
warehouse: this.warehouse,
|
||||||
|
shelf: this.shelf,
|
||||||
|
location: this.location,
|
||||||
|
shelfId: this.selectedSheId,
|
||||||
|
locationId: this.selectedFreId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 如果不完整,提示用户
|
||||||
|
uni.showToast({
|
||||||
|
title: '请完整选择仓库、货架和货位',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.showPicker = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理picker列变化
|
||||||
|
async changeHandler(e) {
|
||||||
|
// 防御性检查,确保e和必要的属性存在
|
||||||
|
if (!e) {
|
||||||
|
console.warn('changeHandler: 事件对象为空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('选择器变化:', e);
|
||||||
|
|
||||||
|
// 获取列索引和选中的索引数组
|
||||||
|
const {
|
||||||
|
columnIndex,
|
||||||
|
index
|
||||||
|
} = e;
|
||||||
|
|
||||||
|
// 如果是选择了仓库(第0列)
|
||||||
|
if (columnIndex === 0) {
|
||||||
|
// 获取选中的仓库
|
||||||
|
const warehouseName = this.columns[0][index];
|
||||||
|
|
||||||
|
// 使用仓库ID获取货架数据
|
||||||
|
if (this.selectedWarehouse && this.selectedWarehouse.id) {
|
||||||
|
await this.fetchShelves(this.selectedWarehouse.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是选择了货架(第1列)
|
||||||
|
if (columnIndex === 1 && this.shelves.length > 0) {
|
||||||
|
// 获取选中的货架
|
||||||
|
const selectedShelf = this.shelves[index];
|
||||||
|
// console.log('选中货架:', selectedShelf);
|
||||||
|
|
||||||
|
// 保存选中的货架ID
|
||||||
|
if (selectedShelf && selectedShelf.id) {
|
||||||
|
this.selectedSheId = selectedShelf.id;
|
||||||
|
await this.fetchLocations(selectedShelf.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取货架列表
|
||||||
|
async fetchShelves(depotId) {
|
||||||
|
try {
|
||||||
|
// console.log('获取货架数据,仓库ID:', depotId);
|
||||||
|
|
||||||
|
const response = await uni.request({
|
||||||
|
url: 'https://api.buzhiyushu.cn/shelves/shelves/sheNamelist',
|
||||||
|
data: {
|
||||||
|
depotId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用数组解构获取响应结果
|
||||||
|
const [err, res] = Array.isArray(response) ? response : [null, response];
|
||||||
|
|
||||||
|
if (!res?.data?.rows) {
|
||||||
|
console.error('获取货架数据失败,返回空数据');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shelves = res.data.rows; // 保存货架列表
|
||||||
|
this.columns[1] = this.shelves.map(item => item.code || '未知货架');
|
||||||
|
|
||||||
|
// console.log('获取到货架数据:', this.shelves);
|
||||||
|
// console.log('货架选项:', this.columns[1]);
|
||||||
|
|
||||||
|
// 更新UI
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.uPicker) {
|
||||||
|
this.$refs.uPicker.setColumnValues(1, this.columns[1]);
|
||||||
|
// 清空货位列
|
||||||
|
this.$refs.uPicker.setColumnValues(2, []);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.shelves;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取货架失败:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取货位列表
|
||||||
|
async fetchLocations(sheId) {
|
||||||
|
if (!sheId) {
|
||||||
|
console.error('获取货位列表失败:未提供货架ID');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// console.log('获取货位数据,货架ID:', sheId);
|
||||||
|
|
||||||
|
const response = await uni.request({
|
||||||
|
url: 'https://api.buzhiyushu.cn/shelves/shelves/freNamelist',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
sheId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用数组解构获取响应结果
|
||||||
|
const [err, res] = Array.isArray(response) ? response : [null, response];
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
console.error('获取货位请求错误:', err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res || !res.data || !res.data.rows) {
|
||||||
|
console.error('货位响应数据格式不正确');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const locations = res.data.rows;
|
||||||
|
// console.log('获取到货位数据:', locations);
|
||||||
|
|
||||||
|
// 保存货位数据
|
||||||
|
this.locations = locations;
|
||||||
|
|
||||||
|
// 更新货位列
|
||||||
|
this.columns[2] = locations.map(item => item.code || '未知货位');
|
||||||
|
// console.log('货位选项:', this.columns[2]);
|
||||||
|
|
||||||
|
// 更新UI
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.uPicker) {
|
||||||
|
this.$refs.uPicker.setColumnValues(2, this.columns[2]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return locations;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取货位列表失败:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 监听外部更新事件
|
||||||
|
this.$on('storage-selected', this.updateSelectedStorage);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// 移除事件监听
|
||||||
|
this.$off('storage-selected', this.updateSelectedStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.view-container {
|
||||||
|
display: flex;
|
||||||
|
background-color: #fff;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
/* 垂直居中 */
|
||||||
|
padding: 3rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
position: relative;
|
||||||
|
/* border-bottom: 1px solid #ccc; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item-3 {
|
||||||
|
flex: 3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item-4 {
|
||||||
|
flex: 4 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item-6 {
|
||||||
|
flex: 6 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item-7 {
|
||||||
|
flex: 7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item>.label {
|
||||||
|
padding: 0rpx 15rpx;
|
||||||
|
text-align: center;
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
color: #000;
|
||||||
|
font-size: 28rpx;
|
||||||
|
background-color: #ccc;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-item>.select {
|
||||||
|
padding: 16rpx 0rpx;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 40rpx;
|
||||||
|
font-size: 18rpx;
|
||||||
|
line-height: 45rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
padding-left: 10rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .u-picker__view__column__item[class*="data-v-"] {
|
||||||
|
font-size: 30rpx !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保选择器文本也使用相同大小 */
|
||||||
|
.view-item>.select {
|
||||||
|
font-size: 23rpx !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .data-v-55c89db1 .u-toolbar__wrapper__confirm.data-v-55c89db1 {
|
||||||
|
color: #3c9cff;
|
||||||
|
font-size: 45rpx;
|
||||||
|
padding: 0 15rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
3296
pkgUpload/backup_warehouse_fix_20260511_135103/isbn-upload-index.vue
Normal file
3296
pkgUpload/backup_warehouse_fix_20260511_135103/isbn-upload-index.vue
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,169 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 顶部Tab切换 -->
|
||||||
|
<view class="tab-header">
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'active': currentTab === 'isbn' }"
|
||||||
|
@click="switchTab('isbn')">
|
||||||
|
<text>ISBN上传</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'active': currentTab === 'photo' }"
|
||||||
|
@click="switchTab('photo')">
|
||||||
|
<text>无ISBN上传</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Tab内容区域 -->
|
||||||
|
<view class="tab-content">
|
||||||
|
<!-- ISBN上传组件 -->
|
||||||
|
<isbn-upload
|
||||||
|
v-show="currentTab === 'isbn'"
|
||||||
|
ref="isbnUpload"
|
||||||
|
:selectedWarehouse="selectedWarehouse"
|
||||||
|
@camera-status-change="handleCameraStatusChange">
|
||||||
|
</isbn-upload>
|
||||||
|
|
||||||
|
<!-- 无ISBN上传组件 -->
|
||||||
|
<photo-upload
|
||||||
|
v-show="currentTab === 'photo'"
|
||||||
|
ref="photoUpload"
|
||||||
|
:selectedWarehouse="selectedWarehouse"
|
||||||
|
@camera-status-change="handleCameraStatusChange">
|
||||||
|
</photo-upload>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import IsbnUpload from '@/pkgUpload/isbn-upload/index.vue';
|
||||||
|
import PhotoUpload from '@/pkgUpload/photo-upload/index.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UploadContainer',
|
||||||
|
components: {
|
||||||
|
'isbn-upload': IsbnUpload,
|
||||||
|
'photo-upload': PhotoUpload
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentTab: 'isbn', // 默认显示ISBN上传
|
||||||
|
selectedWarehouse: null,
|
||||||
|
isCameraOpen: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
// 获取传递的仓库信息
|
||||||
|
if (options.warehouse) {
|
||||||
|
try {
|
||||||
|
this.selectedWarehouse = JSON.parse(decodeURIComponent(options.warehouse));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析仓库信息失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从本地存储获取仓库信息
|
||||||
|
if (!this.selectedWarehouse) {
|
||||||
|
this.selectedWarehouse = uni.getStorageSync('selectedWarehouse');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取Tab参数
|
||||||
|
if (options.tab) {
|
||||||
|
this.currentTab = options.tab;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
// 页面显示时,刷新仓库信息
|
||||||
|
const warehouse = uni.getStorageSync('selectedWarehouse');
|
||||||
|
if (warehouse) {
|
||||||
|
this.selectedWarehouse = warehouse;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 切换Tab
|
||||||
|
switchTab(tab) {
|
||||||
|
// 如果相机正在使用,提示用户
|
||||||
|
if (this.isCameraOpen) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先关闭相机',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentTab = tab;
|
||||||
|
|
||||||
|
// 切换时的初始化处理
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (tab === 'isbn' && this.$refs.isbnUpload) {
|
||||||
|
// 可以在这里调用子组件的方法
|
||||||
|
} else if (tab === 'photo' && this.$refs.photoUpload) {
|
||||||
|
// 可以在这里调用子组件的方法
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理相机状态变化
|
||||||
|
handleCameraStatusChange(isOpen) {
|
||||||
|
this.isCameraOpen = isOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab头部样式 */
|
||||||
|
.tab-header {
|
||||||
|
display: flex;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-bottom: 1rpx solid #e5e5e5;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active text {
|
||||||
|
color: #007AFF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 60rpx;
|
||||||
|
height: 4rpx;
|
||||||
|
background-color: #007AFF;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab内容区域 */
|
||||||
|
.tab-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
3412
pkgUpload/isbn-upload/index.vue
Normal file
3412
pkgUpload/isbn-upload/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
3699
pkgUpload/photo-upload/index.vue
Normal file
3699
pkgUpload/photo-upload/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page-container" @click="closeDropdown">
|
<view class="page-container" @click="closeDropdown">
|
||||||
<view v-if="currentTab === 'title'" class="form-container">
|
<view v-if="currentTab === 'title'" class="form-container">
|
||||||
<!-- 仓库、货架、货位三级联动选择 -->
|
<!-- 仓库、货架、货位三级联动选择 -->
|
||||||
@ -137,6 +137,7 @@
|
|||||||
import {
|
import {
|
||||||
mapState
|
mapState
|
||||||
} from 'vuex'
|
} from 'vuex'
|
||||||
|
// Vuex 模块拆分后使用命名空间访问
|
||||||
import CryptoJS from 'crypto-js';
|
import CryptoJS from 'crypto-js';
|
||||||
// 导入拍照上传组件
|
// 导入拍照上传组件
|
||||||
import CameraUpload from '@/components/CameraUpload.vue';
|
import CameraUpload from '@/components/CameraUpload.vue';
|
||||||
@ -172,7 +173,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['priceMode', 'priceType', 'averageRange', 'selectedPosition', 'freight', 'minValue']),
|
...mapState('price', ['priceMode', 'priceType', 'averageRange', 'freight', 'minValue']),
|
||||||
|
...mapState('warehouse', ['selectedPosition']),
|
||||||
|
|
||||||
// 添加计算属性
|
// 添加计算属性
|
||||||
displayIsbn: {
|
displayIsbn: {
|
||||||
@ -480,7 +482,7 @@
|
|||||||
if (tab === 'isbn') {
|
if (tab === 'isbn') {
|
||||||
// 跳转到ISBN上传页面
|
// 跳转到ISBN上传页面
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/isbn-upload/index'
|
url: '/pkgUpload/isbn-upload/index'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -531,7 +533,7 @@
|
|||||||
|
|
||||||
// 跳转到上书记录页面并传递数据
|
// 跳转到上书记录页面并传递数据
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/scan/book-records',
|
url: '/pkgUser/book-records',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
// 向打开的页面传递数据
|
// 向打开的页面传递数据
|
||||||
res.eventChannel.emit('bookRecordsData', {
|
res.eventChannel.emit('bookRecordsData', {
|
||||||
206
pkgUpload/upload-container/index.vue
Normal file
206
pkgUpload/upload-container/index.vue
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 顶部Tab切换 -->
|
||||||
|
<view class="tab-header">
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'active': currentTab === 'isbn' }"
|
||||||
|
@click="switchTab('isbn')">
|
||||||
|
<text>ISBN上传</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'active': currentTab === 'photo' }"
|
||||||
|
@click="switchTab('photo')">
|
||||||
|
<text>无ISBN上传</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Tab内容区域 -->
|
||||||
|
<view class="tab-content">
|
||||||
|
<!-- 调试信息 -->
|
||||||
|
<view class="debug-info">
|
||||||
|
<text>当前Tab: {{ currentTab }}</text>
|
||||||
|
<text>仓库: {{ selectedWarehouse ? selectedWarehouse.name : '未选择' }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 无仓库数据时显示提示 -->
|
||||||
|
<view v-if="!selectedWarehouse" class="no-warehouse-tip">
|
||||||
|
<text>请先选择仓库</text>
|
||||||
|
<button type="primary" size="mini" @click="goSelectWarehouse">选择仓库</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 有仓库数据时才渲染上传组件 -->
|
||||||
|
<template v-if="selectedWarehouse">
|
||||||
|
<!-- ISBN上传组件 -->
|
||||||
|
<isbn-upload
|
||||||
|
v-show="currentTab === 'isbn'"
|
||||||
|
ref="isbnUpload"
|
||||||
|
:selectedWarehouse="selectedWarehouse"
|
||||||
|
@camera-status-change="handleCameraStatusChange">
|
||||||
|
</isbn-upload>
|
||||||
|
|
||||||
|
<!-- 无ISBN上传组件 -->
|
||||||
|
<photo-upload
|
||||||
|
v-show="currentTab === 'photo'"
|
||||||
|
ref="photoUpload"
|
||||||
|
:selectedWarehouse="selectedWarehouse"
|
||||||
|
@camera-status-change="handleCameraStatusChange">
|
||||||
|
</photo-upload>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import IsbnUpload from '../isbn-upload/index.vue';
|
||||||
|
import PhotoUpload from '../photo-upload/index.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UploadContainer',
|
||||||
|
components: {
|
||||||
|
'isbn-upload': IsbnUpload,
|
||||||
|
'photo-upload': PhotoUpload
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentTab: 'isbn', // 默认显示ISBN上传
|
||||||
|
selectedWarehouse: null,
|
||||||
|
isCameraOpen: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
// 获取传递的仓库信息
|
||||||
|
if (options.warehouse) {
|
||||||
|
try {
|
||||||
|
this.selectedWarehouse = JSON.parse(decodeURIComponent(options.warehouse));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析仓库信息失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从本地存储获取仓库信息
|
||||||
|
if (!this.selectedWarehouse) {
|
||||||
|
this.selectedWarehouse = uni.getStorageSync('selectedWarehouse');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取Tab参数
|
||||||
|
if (options.tab) {
|
||||||
|
this.currentTab = options.tab;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
// 页面显示时,刷新仓库信息
|
||||||
|
const warehouse = uni.getStorageSync('selectedWarehouse');
|
||||||
|
if (warehouse) {
|
||||||
|
this.selectedWarehouse = warehouse;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 切换Tab
|
||||||
|
switchTab(tab) {
|
||||||
|
// 如果相机正在使用,提示用户
|
||||||
|
if (this.isCameraOpen) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先关闭相机',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentTab = tab;
|
||||||
|
|
||||||
|
// 切换时的初始化处理
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (tab === 'isbn' && this.$refs.isbnUpload) {
|
||||||
|
// 可以在这里调用子组件的方法
|
||||||
|
} else if (tab === 'photo' && this.$refs.photoUpload) {
|
||||||
|
// 可以在这里调用子组件的方法
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理相机状态变化
|
||||||
|
handleCameraStatusChange(isOpen) {
|
||||||
|
this.isCameraOpen = isOpen;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 跳转到选择仓库页面
|
||||||
|
goSelectWarehouse() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/warehouse/warehouse-select'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab头部样式 */
|
||||||
|
.tab-header {
|
||||||
|
display: flex;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-bottom: 1rpx solid #e5e5e5;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active text {
|
||||||
|
color: #007AFF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 60rpx;
|
||||||
|
height: 4rpx;
|
||||||
|
background-color: #007AFF;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab内容区域 */
|
||||||
|
.tab-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无仓库提示 */
|
||||||
|
.no-warehouse-tip {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 100rpx 0;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-warehouse-tip text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
1543
pkgUser/dispatch-management.vue
Normal file
1543
pkgUser/dispatch-management.vue
Normal file
File diff suppressed because it is too large
Load Diff
58
services/index.js
Normal file
58
services/index.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Services 统一入口 - 所有 API 服务的统一导出
|
||||||
|
*
|
||||||
|
* 模块拆分说明:
|
||||||
|
* - auth: 认证服务(登录、注册、用户信息)
|
||||||
|
* - book: 书籍服务(上传、记录、条码识别)
|
||||||
|
* - goods: 商品服务(商品管理、克隆工具)
|
||||||
|
* - warehouse: 仓库服务(仓库、货架、订单)
|
||||||
|
*
|
||||||
|
* 使用方式:
|
||||||
|
* import { authApi, bookApi, goodsApi, warehouseApi } from '@/services'
|
||||||
|
*
|
||||||
|
* 或按需导入:
|
||||||
|
* import { authApi } from '@/services/modules/auth'
|
||||||
|
*/
|
||||||
|
import { authApi, registerApi, userApi } from './modules/auth'
|
||||||
|
import { bookApi, barcodeApi } from './modules/book'
|
||||||
|
import { goodsApi, cloneApi, priceCompareApi } from './modules/goods'
|
||||||
|
import { warehouseApi, shelfApi, orderApi, quickShelfApi } from './modules/warehouse'
|
||||||
|
|
||||||
|
// 统一导出所有 API
|
||||||
|
export {
|
||||||
|
// 认证相关
|
||||||
|
authApi,
|
||||||
|
registerApi,
|
||||||
|
userApi,
|
||||||
|
|
||||||
|
// 书籍相关
|
||||||
|
bookApi,
|
||||||
|
barcodeApi,
|
||||||
|
|
||||||
|
// 商品相关
|
||||||
|
goodsApi,
|
||||||
|
cloneApi,
|
||||||
|
priceCompareApi,
|
||||||
|
|
||||||
|
// 仓库相关
|
||||||
|
warehouseApi,
|
||||||
|
shelfApi,
|
||||||
|
orderApi,
|
||||||
|
quickShelfApi
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认导出对象(兼容旧代码)
|
||||||
|
export default {
|
||||||
|
auth: authApi,
|
||||||
|
register: registerApi,
|
||||||
|
user: userApi,
|
||||||
|
book: bookApi,
|
||||||
|
barcode: barcodeApi,
|
||||||
|
goods: goodsApi,
|
||||||
|
clone: cloneApi,
|
||||||
|
priceCompare: priceCompareApi,
|
||||||
|
warehouse: warehouseApi,
|
||||||
|
shelf: shelfApi,
|
||||||
|
order: orderApi,
|
||||||
|
quickShelf: quickShelfApi
|
||||||
|
}
|
||||||
283
services/modules/auth.js
Normal file
283
services/modules/auth.js
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
/**
|
||||||
|
* 认证服务 - 处理登录、注册、Token 管理
|
||||||
|
* @module services/auth
|
||||||
|
*/
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import config from '@/utils/config'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证相关 API
|
||||||
|
*/
|
||||||
|
export const authApi = {
|
||||||
|
/**
|
||||||
|
* 微信小程序登录
|
||||||
|
* @param {Object} data - 登录参数
|
||||||
|
* @param {String} data.xcxCode - 微信小程序 code
|
||||||
|
* @param {String} data.phoneNumber - 手机号
|
||||||
|
* @param {String} data.password - 密码
|
||||||
|
* @param {String} data.code - 验证码
|
||||||
|
* @returns {Promise<Object>} 登录结果,包含 token 和 userInfo
|
||||||
|
*/
|
||||||
|
wxLogin(data) {
|
||||||
|
return request({
|
||||||
|
url: config.login.url,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取租户列表
|
||||||
|
* @returns {Promise<Array>} 租户列表
|
||||||
|
*/
|
||||||
|
getTenantList() {
|
||||||
|
return request({
|
||||||
|
url: config.tenant.listUrl,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取验证码
|
||||||
|
* @param {String} phoneNumber - 手机号
|
||||||
|
* @returns {Promise<Object>} 验证码信息
|
||||||
|
*/
|
||||||
|
getCaptcha(phoneNumber) {
|
||||||
|
return request({
|
||||||
|
url: config.captcha.url,
|
||||||
|
method: 'GET',
|
||||||
|
data: { phoneNumber }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新 Token
|
||||||
|
* @param {String} refreshToken - 刷新令牌
|
||||||
|
* @returns {Promise<Object>} 新的 token 信息
|
||||||
|
*/
|
||||||
|
refreshToken(refreshToken) {
|
||||||
|
return request({
|
||||||
|
url: '/auth/refresh',
|
||||||
|
method: 'POST',
|
||||||
|
data: { refreshToken }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证 Token 有效性
|
||||||
|
* @returns {Promise<Boolean>} Token 是否有效
|
||||||
|
*/
|
||||||
|
async verifyToken() {
|
||||||
|
try {
|
||||||
|
await request({
|
||||||
|
url: '/auth/verify',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册 API
|
||||||
|
*/
|
||||||
|
export const registerApi = {
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
* @param {Object} data - 注册参数
|
||||||
|
* @param {String} data.phoneNumber - 手机号
|
||||||
|
* @param {String} data.password - 密码
|
||||||
|
* @param {String} data.code - 验证码
|
||||||
|
* @param {String} data.nickName - 昵称
|
||||||
|
* @returns {Promise<Object>} 注册结果
|
||||||
|
*/
|
||||||
|
register(data) {
|
||||||
|
return request({
|
||||||
|
url: '/auth/register',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机号是否已注册
|
||||||
|
* @param {String} phoneNumber - 手机号
|
||||||
|
* @returns {Promise<Boolean>} 是否已注册
|
||||||
|
*/
|
||||||
|
async checkPhoneExists(phoneNumber) {
|
||||||
|
try {
|
||||||
|
const res = await request({
|
||||||
|
url: '/auth/check-phone',
|
||||||
|
method: 'GET',
|
||||||
|
data: { phoneNumber }
|
||||||
|
})
|
||||||
|
return res.data.exists
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户信息 API
|
||||||
|
*/
|
||||||
|
export const userApi = {
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
* @returns {Promise<Object>} 用户信息
|
||||||
|
*/
|
||||||
|
getUserInfo() {
|
||||||
|
return request({
|
||||||
|
url: '/system/user/getInfo',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户信息
|
||||||
|
* @param {Object} data - 用户信息
|
||||||
|
* @returns {Promise<Object>} 更新结果
|
||||||
|
*/
|
||||||
|
updateUserInfo(data) {
|
||||||
|
return request({
|
||||||
|
url: '/system/user/update',
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改密码
|
||||||
|
* @param {Object} data - 密码参数
|
||||||
|
* @param {String} data.oldPassword - 旧密码
|
||||||
|
* @param {String} data.newPassword - 新密码
|
||||||
|
* @returns {Promise<Object>} 修改结果
|
||||||
|
*/
|
||||||
|
changePassword(data) {
|
||||||
|
return request({
|
||||||
|
url: '/system/user/changePassword',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
auth: authApi,
|
||||||
|
register: registerApi,
|
||||||
|
user: userApi
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信登录请求封装(包含 uni.login 流程)
|
||||||
|
* @param {Object} loginForm - 登录表单数据
|
||||||
|
* @returns {Promise<Object>} 登录结果
|
||||||
|
*/
|
||||||
|
export const wxLoginRequest = (loginForm) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.login({
|
||||||
|
success: res => {
|
||||||
|
console.log('wx.login 调用成功,返回的 code 是:', res)
|
||||||
|
if (res.code) {
|
||||||
|
const requestData = {
|
||||||
|
...loginForm,
|
||||||
|
xcxCode: res.code
|
||||||
|
}
|
||||||
|
console.log('请求数据:', requestData)
|
||||||
|
authApi.wxLogin(requestData)
|
||||||
|
.then(response => {
|
||||||
|
console.log('服务器返回的数据:', response)
|
||||||
|
const data = response.data
|
||||||
|
if (data && data.openid && data.access_token) {
|
||||||
|
uni.setStorageSync('token', data.access_token)
|
||||||
|
resolve(response)
|
||||||
|
} else {
|
||||||
|
let errorMsg = response.msg || '登录失败'
|
||||||
|
console.log('登录失败,错误信息:', errorMsg)
|
||||||
|
uni.showToast({ title: '用户名密码不正确', icon: 'none' })
|
||||||
|
reject(response)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log('请求服务器失败,错误信息:', err)
|
||||||
|
uni.showToast({ title: '请求服务器失败', icon: 'none' })
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log('获取登录凭证失败,错误信息:', res.errMsg)
|
||||||
|
uni.showToast({ title: '获取登录凭证失败', icon: 'none' })
|
||||||
|
reject(res)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: err => {
|
||||||
|
console.log('调用 wx.login 失败,错误信息:', err)
|
||||||
|
uni.showToast({ title: '调用 wx.login 失败', icon: 'none' })
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册请求封装
|
||||||
|
* @param {Object} registerForm - 注册表单数据
|
||||||
|
* @returns {Promise<Object>} 注册结果
|
||||||
|
*/
|
||||||
|
export const registerRequest = (registerForm) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
console.log('开始注册,提交数据:', registerForm)
|
||||||
|
uni.request({
|
||||||
|
url: 'https://api.buzhiyushu.cn/auth/register',
|
||||||
|
method: 'POST',
|
||||||
|
data: registerForm,
|
||||||
|
header: {
|
||||||
|
clientId: '1400a724f627ddc73d8f4dd344f80a5e',
|
||||||
|
isToken: 'false',
|
||||||
|
isEncrypt: 'false',
|
||||||
|
repeatSubmit: 'false'
|
||||||
|
},
|
||||||
|
success: response => {
|
||||||
|
console.log('服务器返回的数据:', response.data)
|
||||||
|
if (response.data.code === 200) {
|
||||||
|
uni.showToast({ title: '注册成功', icon: 'success' })
|
||||||
|
resolve(response.data)
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: response.data.msg || '注册失败', icon: 'none' })
|
||||||
|
reject(response.data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: err => {
|
||||||
|
console.log('请求服务器失败,错误信息:', err)
|
||||||
|
uni.showToast({ title: '请求服务器失败', icon: 'none' })
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证手机号格式
|
||||||
|
* @param {String} phoneNumber - 手机号
|
||||||
|
* @returns {Boolean} 是否有效
|
||||||
|
*/
|
||||||
|
export const validatePhoneNumber = (phoneNumber) => {
|
||||||
|
const phoneRegex = /^1[3-9]\d{9}$/
|
||||||
|
return phoneRegex.test(phoneNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证密码强度
|
||||||
|
* @param {String} password - 密码
|
||||||
|
* @returns {{valid: Boolean, message: String}} 验证结果
|
||||||
|
*/
|
||||||
|
export const validatePassword = (password) => {
|
||||||
|
if (password.length < 6) {
|
||||||
|
return { valid: false, message: '密码长度不能少于6位' }
|
||||||
|
}
|
||||||
|
return { valid: true, message: '' }
|
||||||
|
}
|
||||||
211
services/modules/book.js
Normal file
211
services/modules/book.js
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
/**
|
||||||
|
* 书籍服务 - 处理图书上传、记录查询等
|
||||||
|
* @module services/book
|
||||||
|
*/
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import config from '@/utils/config'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 书籍相关 API
|
||||||
|
*/
|
||||||
|
export const bookApi = {
|
||||||
|
/**
|
||||||
|
* 获取用户上书记录
|
||||||
|
* @param {String} phoneNumber - 用户手机号
|
||||||
|
* @param {Number} pageNum - 页码
|
||||||
|
* @param {Number} pageSize - 每页数量
|
||||||
|
* @param {String} date - 日期筛选(可选)
|
||||||
|
* @returns {Promise<Object>} 上书记录列表
|
||||||
|
*/
|
||||||
|
getBookRecords(phoneNumber, pageNum = 1, pageSize = 10, date = '') {
|
||||||
|
let url = `${config.book.records}/${phoneNumber}?pageNum=${pageNum}&pageSize=${pageSize}`
|
||||||
|
if (date) {
|
||||||
|
url += `&date=${date}`
|
||||||
|
}
|
||||||
|
return request({
|
||||||
|
url,
|
||||||
|
method: 'GET',
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传书籍图片
|
||||||
|
* @param {Object} data - 上传参数
|
||||||
|
* @param {String} data.imageUrl - 图片 URL
|
||||||
|
* @param {String} data.isbn - ISBN 号(可选)
|
||||||
|
* @param {String} data.warehouseId - 仓库 ID
|
||||||
|
* @returns {Promise<Object>} 上传结果
|
||||||
|
*/
|
||||||
|
uploadBookImage(data) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/shopGoods/uploadImage',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 ISBN 获取图书信息
|
||||||
|
* @param {String} isbn - ISBN 号
|
||||||
|
* @returns {Promise<Object>} 图书信息
|
||||||
|
*/
|
||||||
|
getBookByIsbn(isbn) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/book/isbn/${isbn}`,
|
||||||
|
method: 'GET',
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据书名搜索图书
|
||||||
|
* @param {String} title - 书名关键词
|
||||||
|
* @param {Number} pageNum - 页码
|
||||||
|
* @param {Number} pageSize - 每页数量
|
||||||
|
* @returns {Promise<Object>} 搜索结果
|
||||||
|
*/
|
||||||
|
searchByTitle(title, pageNum = 1, pageSize = 10) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/book/search',
|
||||||
|
method: 'GET',
|
||||||
|
data: { title, pageNum, pageSize },
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交图书信息
|
||||||
|
* @param {Object} data - 图书信息
|
||||||
|
* @returns {Promise<Object>} 提交结果
|
||||||
|
*/
|
||||||
|
submitBook(data) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/shopGoods/add',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量上传图书
|
||||||
|
* @param {Array} books - 图书列表
|
||||||
|
* @returns {Promise<Object>} 批量上传结果
|
||||||
|
*/
|
||||||
|
batchUpload(books) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/shopGoods/batchAdd',
|
||||||
|
method: 'POST',
|
||||||
|
data: { books },
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条形码识别 API
|
||||||
|
*/
|
||||||
|
export const barcodeApi = {
|
||||||
|
/**
|
||||||
|
* 识别条形码
|
||||||
|
* @param {Object} data - 识别参数
|
||||||
|
* @param {String} data.imageUrl - 条形码图片 URL
|
||||||
|
* @returns {Promise<Object>} 识别结果,包含 ISBN
|
||||||
|
*/
|
||||||
|
recognizeBarcode(data) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/barcode/recognize',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从本地图片识别条形码
|
||||||
|
* @param {String} filePath - 本地图片路径
|
||||||
|
* @returns {Promise<Object>} 识别结果
|
||||||
|
*/
|
||||||
|
async recognizeFromLocal(filePath) {
|
||||||
|
// 先上传图片
|
||||||
|
const uploadRes = await new Promise((resolve, reject) => {
|
||||||
|
uni.uploadFile({
|
||||||
|
url: '/zhishu/file/upload',
|
||||||
|
filePath,
|
||||||
|
name: 'file',
|
||||||
|
success: (res) => resolve(JSON.parse(res.data)),
|
||||||
|
fail: reject
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 再识别条形码
|
||||||
|
if (uploadRes.data && uploadRes.data.url) {
|
||||||
|
return this.recognizeBarcode({ imageUrl: uploadRes.data.url })
|
||||||
|
}
|
||||||
|
throw new Error('图片上传失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据关键词获取作者和出版社数据
|
||||||
|
* @param {string} keyword - 搜索关键词
|
||||||
|
* @param {string} cookies - 用户cookies
|
||||||
|
* @returns {Promise<{authors: string[], publishers: string[]}>} 返回作者和出版社数据
|
||||||
|
*/
|
||||||
|
export const getAuthorAndPublisher = async (keyword, cookies) => {
|
||||||
|
try {
|
||||||
|
const response = await new Promise((resolve, reject) => {
|
||||||
|
uni.request({
|
||||||
|
url: 'https://api.buzhiyushu.cn/zhishu/shopGoods/getAuthorAndPublisher',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
keyword: keyword,
|
||||||
|
cookies: cookies
|
||||||
|
},
|
||||||
|
success: (res) => {
|
||||||
|
console.log('API原始响应:', JSON.stringify(res, null, 2))
|
||||||
|
resolve(res)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('API请求失败:', err)
|
||||||
|
reject(new Error(err.errMsg || '网络请求失败'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.data || response.data.code !== 200) {
|
||||||
|
console.log('API返回数据无效或请求失败')
|
||||||
|
return { authors: [], publishers: [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = response.data.data || []
|
||||||
|
|
||||||
|
const authors = new Set()
|
||||||
|
const publishers = new Set()
|
||||||
|
|
||||||
|
data.forEach(item => {
|
||||||
|
if (item.author) {
|
||||||
|
const author = item.author.trim()
|
||||||
|
if (author) authors.add(author)
|
||||||
|
}
|
||||||
|
if (item.press) {
|
||||||
|
const press = item.press.trim()
|
||||||
|
if (press) publishers.add(press)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
authors: Array.from(authors).filter(Boolean).slice(0, 10),
|
||||||
|
publishers: Array.from(publishers).filter(Boolean).slice(0, 10)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取作者和出版社数据出错:', error)
|
||||||
|
return { authors: [], publishers: [] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
book: bookApi,
|
||||||
|
barcode: barcodeApi
|
||||||
|
}
|
||||||
49
services/modules/category.js
Normal file
49
services/modules/category.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* 孔夫子网分类服务
|
||||||
|
* @module services/category
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图书分类信息
|
||||||
|
* @param {string} cookies - cookies认证信息
|
||||||
|
* @returns {Promise<Array>} - 返回分类列表数据
|
||||||
|
*/
|
||||||
|
export async function getCategory(cookies) {
|
||||||
|
try {
|
||||||
|
if (!cookies) {
|
||||||
|
cookies = uni.getStorageSync('cookies')
|
||||||
|
console.log('从本地存储获取cookies:', cookies)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await uni.request({
|
||||||
|
url: 'https://api.buzhiyushu.cn/api/kongfz/getCategory',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
token: cookies
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const [_, res] = response
|
||||||
|
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
return res.data
|
||||||
|
} else {
|
||||||
|
throw new Error(`获取分类信息失败: ${res.statusCode}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取图书分类信息失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message || '获取图书分类信息失败',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2500
|
||||||
|
})
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getCategory
|
||||||
|
}
|
||||||
197
services/modules/goods.js
Normal file
197
services/modules/goods.js
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
/**
|
||||||
|
* 商品服务 - 处理商品管理、克隆工具等
|
||||||
|
* @module services/goods
|
||||||
|
*/
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品管理 API
|
||||||
|
*/
|
||||||
|
export const goodsApi = {
|
||||||
|
/**
|
||||||
|
* 获取商品列表
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {String} params.keyword - 搜索关键词
|
||||||
|
* @param {String} params.status - 商品状态
|
||||||
|
* @param {Number} params.pageNum - 页码
|
||||||
|
* @param {Number} params.pageSize - 每页数量
|
||||||
|
* @returns {Promise<Object>} 商品列表
|
||||||
|
*/
|
||||||
|
getList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/shopGoods/xcx',
|
||||||
|
method: 'GET',
|
||||||
|
data: params,
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取商品详情
|
||||||
|
* @param {String} id - 商品 ID
|
||||||
|
* @returns {Promise<Object>} 商品详情
|
||||||
|
*/
|
||||||
|
getDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/shopGoods/${id}`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新商品信息
|
||||||
|
* @param {String} id - 商品 ID
|
||||||
|
* @param {Object} data - 商品信息
|
||||||
|
* @returns {Promise<Object>} 更新结果
|
||||||
|
*/
|
||||||
|
update(id, data) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/shopGoods/${id}`,
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除商品
|
||||||
|
* @param {String} id - 商品 ID
|
||||||
|
* @returns {Promise<Object>} 删除结果
|
||||||
|
*/
|
||||||
|
delete(id) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/shopGoods/${id}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量更新孔网平台 ID
|
||||||
|
* @param {Array} items - 更新项列表
|
||||||
|
* @param {String} items[].goodsId - 商品 ID
|
||||||
|
* @param {String} items[].kongfzId - 孔网 ID
|
||||||
|
* @returns {Promise<Object>} 更新结果
|
||||||
|
*/
|
||||||
|
batchUpdateKongfzId(items) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/shopGoodsPublished/batchUpdateKfzPlatformId',
|
||||||
|
method: 'POST',
|
||||||
|
data: { items },
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品上下架
|
||||||
|
* @param {String} id - 商品 ID
|
||||||
|
* @param {Number} status - 状态(1=上架, 0=下架)
|
||||||
|
* @returns {Promise<Object>} 操作结果
|
||||||
|
*/
|
||||||
|
toggleStatus(id, status) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/shopGoods/${id}/status`,
|
||||||
|
method: 'PUT',
|
||||||
|
data: { status }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 克隆工具 API(孔夫子旧书网)
|
||||||
|
*/
|
||||||
|
export const cloneApi = {
|
||||||
|
/**
|
||||||
|
* 从孔夫子克隆商品
|
||||||
|
* @param {Object} data - 克隆参数
|
||||||
|
* @param {String} data.kongfzId - 孔夫子商品 ID
|
||||||
|
* @param {Object} data.priceAdjust - 价格调整配置
|
||||||
|
* @returns {Promise<Object>} 克隆结果
|
||||||
|
*/
|
||||||
|
cloneFromKongfz(data) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/goods/clone/kongfz',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量克隆商品
|
||||||
|
* @param {Array} items - 克隆项列表
|
||||||
|
* @returns {Promise<Object>} 批量克隆结果
|
||||||
|
*/
|
||||||
|
batchClone(items) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/goods/clone/batch',
|
||||||
|
method: 'POST',
|
||||||
|
data: { items },
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取克隆历史
|
||||||
|
* @param {Number} pageNum - 页码
|
||||||
|
* @param {Number} pageSize - 每页数量
|
||||||
|
* @returns {Promise<Object>} 克隆历史记录
|
||||||
|
*/
|
||||||
|
getCloneHistory(pageNum = 1, pageSize = 20) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/goods/clone/history',
|
||||||
|
method: 'GET',
|
||||||
|
data: { pageNum, pageSize }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 价格对比 API
|
||||||
|
*/
|
||||||
|
export const priceCompareApi = {
|
||||||
|
/**
|
||||||
|
* 获取各平台价格对比
|
||||||
|
* @param {String} isbn - ISBN 号
|
||||||
|
* @returns {Promise<Object>} 各平台价格信息
|
||||||
|
*/
|
||||||
|
comparePrices(isbn) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/price/compare',
|
||||||
|
method: 'GET',
|
||||||
|
data: { isbn },
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取孔夫子价格
|
||||||
|
* @param {String} isbn - ISBN 号
|
||||||
|
* @returns {Promise<Object>} 孔夫子价格信息
|
||||||
|
*/
|
||||||
|
getKongfzPrice(isbn) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/price/kongfz',
|
||||||
|
method: 'GET',
|
||||||
|
data: { isbn }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当当价格
|
||||||
|
* @param {String} isbn - ISBN 号
|
||||||
|
* @returns {Promise<Object>} 当当价格信息
|
||||||
|
*/
|
||||||
|
getDangdangPrice(isbn) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/price/dangdang',
|
||||||
|
method: 'GET',
|
||||||
|
data: { isbn }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
goods: goodsApi,
|
||||||
|
clone: cloneApi,
|
||||||
|
priceCompare: priceCompareApi
|
||||||
|
}
|
||||||
227
services/modules/warehouse.js
Normal file
227
services/modules/warehouse.js
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/**
|
||||||
|
* 仓库服务 - 处理仓库管理、货架管理等
|
||||||
|
* @module services/warehouse
|
||||||
|
*/
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仓库相关 API
|
||||||
|
*/
|
||||||
|
export const warehouseApi = {
|
||||||
|
/**
|
||||||
|
* 获取仓库列表
|
||||||
|
* @returns {Promise<Array>} 仓库列表
|
||||||
|
*/
|
||||||
|
getList() {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/warehouse/list',
|
||||||
|
method: 'GET',
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取仓库详情
|
||||||
|
* @param {String} id - 仓库 ID
|
||||||
|
* @returns {Promise<Object>} 仓库详情
|
||||||
|
*/
|
||||||
|
getDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/warehouse/${id}`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建仓库
|
||||||
|
* @param {Object} data - 仓库信息
|
||||||
|
* @param {String} data.name - 仓库名称
|
||||||
|
* @param {String} data.address - 仓库地址
|
||||||
|
* @param {String} data.remark - 备注
|
||||||
|
* @returns {Promise<Object>} 创建结果
|
||||||
|
*/
|
||||||
|
create(data) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/warehouse/add',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新仓库信息
|
||||||
|
* @param {String} id - 仓库 ID
|
||||||
|
* @param {Object} data - 仓库信息
|
||||||
|
* @returns {Promise<Object>} 更新结果
|
||||||
|
*/
|
||||||
|
update(id, data) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/warehouse/${id}`,
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除仓库
|
||||||
|
* @param {String} id - 仓库 ID
|
||||||
|
* @returns {Promise<Object>} 删除结果
|
||||||
|
*/
|
||||||
|
delete(id) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/warehouse/${id}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 货架相关 API
|
||||||
|
*/
|
||||||
|
export const shelfApi = {
|
||||||
|
/**
|
||||||
|
* 获取货架列表
|
||||||
|
* @param {String} warehouseId - 仓库 ID
|
||||||
|
* @returns {Promise<Array>} 货架列表
|
||||||
|
*/
|
||||||
|
getList(warehouseId) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/shelf/list',
|
||||||
|
method: 'GET',
|
||||||
|
data: { warehouseId }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建货架
|
||||||
|
* @param {Object} data - 货架信息
|
||||||
|
* @param {String} data.warehouseId - 仓库 ID
|
||||||
|
* @param {String} data.name - 货架名称
|
||||||
|
* @param {String} data.code - 货架编码
|
||||||
|
* @returns {Promise<Object>} 创建结果
|
||||||
|
*/
|
||||||
|
create(data) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/shelf/add',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新货架信息
|
||||||
|
* @param {String} id - 货架 ID
|
||||||
|
* @param {Object} data - 货架信息
|
||||||
|
* @returns {Promise<Object>} 更新结果
|
||||||
|
*/
|
||||||
|
update(id, data) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/shelf/${id}`,
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除货架
|
||||||
|
* @param {String} id - 货架 ID
|
||||||
|
* @returns {Promise<Object>} 删除结果
|
||||||
|
*/
|
||||||
|
delete(id) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/shelf/${id}`,
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取货架库存
|
||||||
|
* @param {String} shelfId - 货架 ID
|
||||||
|
* @returns {Promise<Array>} 库存列表
|
||||||
|
*/
|
||||||
|
getInventory(shelfId) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/shelf/${shelfId}/inventory`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单查询 API
|
||||||
|
*/
|
||||||
|
export const orderApi = {
|
||||||
|
/**
|
||||||
|
* 查询订单列表
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @param {String} params.warehouseId - 仓库 ID
|
||||||
|
* @param {String} params.status - 订单状态
|
||||||
|
* @param {Number} params.pageNum - 页码
|
||||||
|
* @param {Number} params.pageSize - 每页数量
|
||||||
|
* @returns {Promise<Object>} 订单列表
|
||||||
|
*/
|
||||||
|
getList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/order/list',
|
||||||
|
method: 'GET',
|
||||||
|
data: params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取订单详情
|
||||||
|
* @param {String} orderId - 订单 ID
|
||||||
|
* @returns {Promise<Object>} 订单详情
|
||||||
|
*/
|
||||||
|
getDetail(orderId) {
|
||||||
|
return request({
|
||||||
|
url: `/zhishu/order/${orderId}`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 闪上书(快速上架)API
|
||||||
|
*/
|
||||||
|
export const quickShelfApi = {
|
||||||
|
/**
|
||||||
|
* 批量扫码上架
|
||||||
|
* @param {Array} items - 上架项目列表
|
||||||
|
* @param {String} items[].isbn - ISBN
|
||||||
|
* @param {String} items[].shelfId - 货架 ID
|
||||||
|
* @param {Number} items[].quantity - 数量
|
||||||
|
* @returns {Promise<Object>} 上架结果
|
||||||
|
*/
|
||||||
|
batchShelf(items) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/quick-shelf/batch',
|
||||||
|
method: 'POST',
|
||||||
|
data: { items },
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取上架历史
|
||||||
|
* @param {Number} pageNum - 页码
|
||||||
|
* @param {Number} pageSize - 每页数量
|
||||||
|
* @returns {Promise<Object>} 上架历史记录
|
||||||
|
*/
|
||||||
|
getHistory(pageNum = 1, pageSize = 20) {
|
||||||
|
return request({
|
||||||
|
url: '/zhishu/quick-shelf/history',
|
||||||
|
method: 'GET',
|
||||||
|
data: { pageNum, pageSize }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
warehouse: warehouseApi,
|
||||||
|
shelf: shelfApi,
|
||||||
|
order: orderApi,
|
||||||
|
quickShelf: quickShelfApi
|
||||||
|
}
|
||||||
BIN
static/ding.mp3
Normal file
BIN
static/ding.mp3
Normal file
Binary file not shown.
BIN
static/ding2.mp3
Normal file
BIN
static/ding2.mp3
Normal file
Binary file not shown.
BIN
static/ding3.mp3
Normal file
BIN
static/ding3.mp3
Normal file
Binary file not shown.
121
store/index.js
121
store/index.js
@ -1,105 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Vuex Store - 全局状态管理入口
|
||||||
|
*
|
||||||
|
* 模块拆分说明:
|
||||||
|
* - auth: 用户认证(token、userInfo、登录状态)
|
||||||
|
* - price: 价格配置(priceMode、priceType、运费等)
|
||||||
|
* - warehouse: 仓库管理(仓库选择、仓库列表)
|
||||||
|
*
|
||||||
|
* 使用方式:
|
||||||
|
* - 命名空间访问:this.$store.state.auth.token
|
||||||
|
* - 辅助函数:import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
|
||||||
|
* - mapState('auth', ['token', 'isLogin'])
|
||||||
|
* - mapMutations('price', ['updatePriceMode'])
|
||||||
|
* - mapActions('auth', ['wechatLogin', 'logout'])
|
||||||
|
* - mapGetters('warehouse', ['selectedWarehouse'])
|
||||||
|
*/
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import request from '@/utils/request' // 导入请求工具
|
|
||||||
|
// 导入子模块
|
||||||
|
import auth from './modules/auth'
|
||||||
|
import price from './modules/price'
|
||||||
|
import warehouse from './modules/warehouse'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
// 从本地存储获取初始状态
|
|
||||||
const getInitialState = () => {
|
|
||||||
return {
|
|
||||||
userInfo: null,
|
|
||||||
token: '',
|
|
||||||
isLogin: false,
|
|
||||||
priceMode: parseInt(uni.getStorageSync('current1') || '1'), // 从本地存储获取价格模式
|
|
||||||
priceType: parseInt(uni.getStorageSync('current2') || '1'), // 从本地存储获取价格类型
|
|
||||||
averageRange: parseInt(uni.getStorageSync('averageRange') || '3'), // 从本地存储获取均价范围
|
|
||||||
selectedPosition: parseInt(uni.getStorageSync('selectedPositionIndex') || '0'), // 从本地存储获取选择的位置
|
|
||||||
freight: parseFloat(uni.getStorageSync('value3') || '0'), // 从本地存储获取运费
|
|
||||||
minValue: parseFloat(uni.getStorageSync('value4') || '0.01') // 从本地存储获取最低值
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
state: getInitialState(),
|
modules: {
|
||||||
mutations: {
|
auth,
|
||||||
SET_USER_INFO(state, userInfo) {
|
price,
|
||||||
state.userInfo = userInfo
|
warehouse
|
||||||
},
|
|
||||||
SET_TOKEN(state, token) {
|
|
||||||
state.token = token
|
|
||||||
},
|
|
||||||
SET_LOGIN_STATUS(state, status) {
|
|
||||||
state.isLogin = status
|
|
||||||
},
|
|
||||||
updatePriceMode(state, mode) {
|
|
||||||
state.priceMode = mode
|
|
||||||
uni.setStorageSync('current1', mode) // 保存到本地存储
|
|
||||||
},
|
|
||||||
updatePriceType(state, type) {
|
|
||||||
state.priceType = type
|
|
||||||
uni.setStorageSync('current2', type) // 保存到本地存储
|
|
||||||
},
|
|
||||||
updateAverageRange(state, range) {
|
|
||||||
state.averageRange = range
|
|
||||||
uni.setStorageSync('averageRange', range) // 保存到本地存储
|
|
||||||
},
|
|
||||||
updateSelectedPosition(state, position) {
|
|
||||||
state.selectedPosition = position
|
|
||||||
uni.setStorageSync('selectedPositionIndex', position) // 保存到本地存储
|
|
||||||
},
|
|
||||||
updateFreight(state, value) {
|
|
||||||
state.freight = value
|
|
||||||
uni.setStorageSync('value3', value) // 保存到本地存储
|
|
||||||
},
|
|
||||||
updateMinValue(state, value) {
|
|
||||||
state.minValue = value
|
|
||||||
uni.setStorageSync('value4', value) // 保存到本地存储
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
// 检查登录状态
|
|
||||||
checkLogin({ commit }) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const token = uni.getStorageSync('token')
|
|
||||||
if (token) {
|
|
||||||
commit('SET_TOKEN', token)
|
|
||||||
commit('SET_LOGIN_STATUS', true)
|
|
||||||
// 这里可以添加获取用户信息的请求
|
|
||||||
resolve(true)
|
|
||||||
} else {
|
|
||||||
resolve(false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 修改后的微信登录Action
|
|
||||||
async wechatLogin({ commit }, code) {
|
|
||||||
try {
|
|
||||||
// 使用封装的request方法
|
|
||||||
const res = await request({
|
|
||||||
url: '/api/wechat/login',
|
|
||||||
method: 'POST',
|
|
||||||
data: { code }
|
|
||||||
})
|
|
||||||
|
|
||||||
// 处理响应数据
|
|
||||||
commit('SET_USER_INFO', res.data.userInfo)
|
|
||||||
commit('SET_TOKEN', res.data.token)
|
|
||||||
commit('SET_LOGIN_STATUS', true)
|
|
||||||
uni.setStorageSync('token', res.data.token)
|
|
||||||
|
|
||||||
return res.data
|
|
||||||
} catch (error) {
|
|
||||||
console.error('登录失败:', error)
|
|
||||||
throw new Error('登录请求失败')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 退出登录
|
|
||||||
logout({ commit }) {
|
|
||||||
commit('SET_USER_INFO', null)
|
|
||||||
commit('SET_TOKEN', '')
|
|
||||||
commit('SET_LOGIN_STATUS', false)
|
|
||||||
uni.removeStorageSync('token')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
177
store/modules/auth.js
Normal file
177
store/modules/auth.js
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* Vuex Auth Module - 用户认证状态管理
|
||||||
|
* @module store/modules/auth
|
||||||
|
*/
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 从本地存储获取初始状态
|
||||||
|
const getAuthInitialState = () => {
|
||||||
|
return {
|
||||||
|
userInfo: uni.getStorageSync('userInfo') || null,
|
||||||
|
token: uni.getStorageSync('token') || '',
|
||||||
|
isLogin: !!uni.getStorageSync('token'),
|
||||||
|
openId: uni.getStorageSync('openId') || '',
|
||||||
|
userId: uni.getStorageSync('userId') || '',
|
||||||
|
phoneNumber: uni.getStorageSync('phoneNumber') || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = getAuthInitialState()
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
/**
|
||||||
|
* 设置用户信息
|
||||||
|
* @param {Object} userInfo - 用户信息对象
|
||||||
|
*/
|
||||||
|
SET_USER_INFO(state, userInfo) {
|
||||||
|
state.userInfo = userInfo
|
||||||
|
uni.setStorageSync('userInfo', userInfo)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置访问令牌
|
||||||
|
* @param {String} token - 访问令牌
|
||||||
|
*/
|
||||||
|
SET_TOKEN(state, token) {
|
||||||
|
state.token = token
|
||||||
|
uni.setStorageSync('token', token)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置登录状态
|
||||||
|
* @param {Boolean} status - 登录状态
|
||||||
|
*/
|
||||||
|
SET_LOGIN_STATUS(state, status) {
|
||||||
|
state.isLogin = status
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 OpenID
|
||||||
|
* @param {String} openId - 微信 OpenID
|
||||||
|
*/
|
||||||
|
SET_OPEN_ID(state, openId) {
|
||||||
|
state.openId = openId
|
||||||
|
uni.setStorageSync('openId', openId)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置用户 ID
|
||||||
|
* @param {String} userId - 用户 ID
|
||||||
|
*/
|
||||||
|
SET_USER_ID(state, userId) {
|
||||||
|
state.userId = userId
|
||||||
|
uni.setStorageSync('userId', userId)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置手机号
|
||||||
|
* @param {String} phoneNumber - 手机号
|
||||||
|
*/
|
||||||
|
SET_PHONE_NUMBER(state, phoneNumber) {
|
||||||
|
state.phoneNumber = phoneNumber
|
||||||
|
uni.setStorageSync('phoneNumber', phoneNumber)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有认证信息
|
||||||
|
*/
|
||||||
|
CLEAR_AUTH(state) {
|
||||||
|
state.userInfo = null
|
||||||
|
state.token = ''
|
||||||
|
state.isLogin = false
|
||||||
|
state.openId = ''
|
||||||
|
state.userId = ''
|
||||||
|
state.phoneNumber = ''
|
||||||
|
// 清除本地存储
|
||||||
|
uni.removeStorageSync('userInfo')
|
||||||
|
uni.removeStorageSync('token')
|
||||||
|
uni.removeStorageSync('openId')
|
||||||
|
uni.removeStorageSync('userId')
|
||||||
|
uni.removeStorageSync('phoneNumber')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
/**
|
||||||
|
* 检查登录状态
|
||||||
|
* @returns {Promise<Boolean>} 是否已登录
|
||||||
|
*/
|
||||||
|
checkLogin({ commit, state }) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
if (token) {
|
||||||
|
commit('SET_TOKEN', token)
|
||||||
|
commit('SET_LOGIN_STATUS', true)
|
||||||
|
resolve(true)
|
||||||
|
} else {
|
||||||
|
resolve(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信登录
|
||||||
|
* @param {String} code - 微信登录 code
|
||||||
|
* @returns {Promise<Object>} 登录结果
|
||||||
|
*/
|
||||||
|
async wechatLogin({ commit }, code) {
|
||||||
|
try {
|
||||||
|
const res = await request({
|
||||||
|
url: '/api/wechat/login',
|
||||||
|
method: 'POST',
|
||||||
|
data: { code }
|
||||||
|
})
|
||||||
|
|
||||||
|
commit('SET_USER_INFO', res.data.userInfo)
|
||||||
|
commit('SET_TOKEN', res.data.token)
|
||||||
|
commit('SET_LOGIN_STATUS', true)
|
||||||
|
|
||||||
|
return res.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error('登录失败:', error)
|
||||||
|
throw new Error('登录请求失败')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
*/
|
||||||
|
logout({ commit }) {
|
||||||
|
commit('CLEAR_AUTH')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
*/
|
||||||
|
userInfo: state => state.userInfo,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取令牌
|
||||||
|
*/
|
||||||
|
token: state => state.token,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已登录
|
||||||
|
*/
|
||||||
|
isLogin: state => state.isLogin,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 OpenID
|
||||||
|
*/
|
||||||
|
openId: state => state.openId,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户 ID
|
||||||
|
*/
|
||||||
|
userId: state => state.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
mutations,
|
||||||
|
actions,
|
||||||
|
getters
|
||||||
|
}
|
||||||
139
store/modules/price.js
Normal file
139
store/modules/price.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/**
|
||||||
|
* Vuex Price Module - 价格配置状态管理
|
||||||
|
* @module store/modules/price
|
||||||
|
*
|
||||||
|
* 用于管理图书上传/克隆时的价格调整配置
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 从本地存储获取初始状态
|
||||||
|
const getPriceInitialState = () => {
|
||||||
|
return {
|
||||||
|
// 价格调整模式
|
||||||
|
// 1: 折扣模式(按百分比调整)
|
||||||
|
// 2: 加减值模式(按固定金额调整)
|
||||||
|
// 3: 指定金额模式(直接设置价格)
|
||||||
|
priceMode: parseInt(uni.getStorageSync('current1') || '1'),
|
||||||
|
|
||||||
|
// 价格类型(用于不同场景的价格策略)
|
||||||
|
priceType: parseInt(uni.getStorageSync('current2') || '1'),
|
||||||
|
|
||||||
|
// 均价范围(用于计算参考价格时的范围)
|
||||||
|
averageRange: parseInt(uni.getStorageSync('averageRange') || '3'),
|
||||||
|
|
||||||
|
// 运费设置
|
||||||
|
freight: parseFloat(uni.getStorageSync('value3') || '0'),
|
||||||
|
|
||||||
|
// 最低价格限制
|
||||||
|
minValue: parseFloat(uni.getStorageSync('value4') || '0.01')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = getPriceInitialState()
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
/**
|
||||||
|
* 更新价格模式
|
||||||
|
* @param {Number} mode - 价格模式 (1=折扣, 2=加减值, 3=指定金额)
|
||||||
|
*/
|
||||||
|
updatePriceMode(state, mode) {
|
||||||
|
state.priceMode = mode
|
||||||
|
uni.setStorageSync('current1', mode)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新价格类型
|
||||||
|
* @param {Number} type - 价格类型
|
||||||
|
*/
|
||||||
|
updatePriceType(state, type) {
|
||||||
|
state.priceType = type
|
||||||
|
uni.setStorageSync('current2', type)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新均价范围
|
||||||
|
* @param {Number} range - 均价范围
|
||||||
|
*/
|
||||||
|
updateAverageRange(state, range) {
|
||||||
|
state.averageRange = range
|
||||||
|
uni.setStorageSync('averageRange', range)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新运费
|
||||||
|
* @param {Number} value - 运费金额
|
||||||
|
*/
|
||||||
|
updateFreight(state, value) {
|
||||||
|
state.freight = value
|
||||||
|
uni.setStorageSync('value3', value)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新最低价格
|
||||||
|
* @param {Number} value - 最低价格
|
||||||
|
*/
|
||||||
|
updateMinValue(state, value) {
|
||||||
|
state.minValue = value
|
||||||
|
uni.setStorageSync('value4', value)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置所有价格配置
|
||||||
|
*/
|
||||||
|
resetPriceConfig(state) {
|
||||||
|
state.priceMode = 1
|
||||||
|
state.priceType = 1
|
||||||
|
state.averageRange = 3
|
||||||
|
state.freight = 0
|
||||||
|
state.minValue = 0.01
|
||||||
|
uni.removeStorageSync('current1')
|
||||||
|
uni.removeStorageSync('current2')
|
||||||
|
uni.removeStorageSync('averageRange')
|
||||||
|
uni.removeStorageSync('value3')
|
||||||
|
uni.removeStorageSync('value4')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
/**
|
||||||
|
* 获取当前价格模式
|
||||||
|
*/
|
||||||
|
priceMode: state => state.priceMode,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前价格类型
|
||||||
|
*/
|
||||||
|
priceType: state => state.priceType,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取均价范围
|
||||||
|
*/
|
||||||
|
averageRange: state => state.averageRange,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取运费设置
|
||||||
|
*/
|
||||||
|
freight: state => state.freight,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最低价格限制
|
||||||
|
*/
|
||||||
|
minValue: state => state.minValue,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取完整价格配置对象
|
||||||
|
*/
|
||||||
|
priceConfig: state => ({
|
||||||
|
mode: state.priceMode,
|
||||||
|
type: state.priceType,
|
||||||
|
averageRange: state.averageRange,
|
||||||
|
freight: state.freight,
|
||||||
|
minValue: state.minValue
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
mutations,
|
||||||
|
getters
|
||||||
|
}
|
||||||
124
store/modules/warehouse.js
Normal file
124
store/modules/warehouse.js
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* Vuex Warehouse Module - 仓库状态管理
|
||||||
|
* @module store/modules/warehouse
|
||||||
|
*
|
||||||
|
* 用于管理仓库选择、货架信息等仓库相关状态
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 从本地存储获取初始状态
|
||||||
|
const getWarehouseInitialState = () => {
|
||||||
|
return {
|
||||||
|
// 当前选中的仓库 ID
|
||||||
|
selectedWarehouse: uni.getStorageSync('selectedWarehouse') || '',
|
||||||
|
|
||||||
|
// 仓库列表
|
||||||
|
warehouseList: [],
|
||||||
|
|
||||||
|
// 选择的位置索引(用于仓库选择器)
|
||||||
|
selectedPosition: parseInt(uni.getStorageSync('selectedPositionIndex') || '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = getWarehouseInitialState()
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
/**
|
||||||
|
* 设置选中的仓库
|
||||||
|
* @param {String} warehouseId - 仓库 ID
|
||||||
|
*/
|
||||||
|
setSelectedWarehouse(state, warehouseId) {
|
||||||
|
state.selectedWarehouse = warehouseId
|
||||||
|
uni.setStorageSync('selectedWarehouse', warehouseId)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置仓库列表
|
||||||
|
* @param {Array} list - 仓库列表
|
||||||
|
*/
|
||||||
|
setWarehouseList(state, list) {
|
||||||
|
state.warehouseList = list
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新选择位置索引
|
||||||
|
* @param {Number} position - 位置索引
|
||||||
|
*/
|
||||||
|
updateSelectedPosition(state, position) {
|
||||||
|
state.selectedPosition = position
|
||||||
|
uni.setStorageSync('selectedPositionIndex', position)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除仓库选择
|
||||||
|
*/
|
||||||
|
clearWarehouseSelection(state) {
|
||||||
|
state.selectedWarehouse = ''
|
||||||
|
state.selectedPosition = 0
|
||||||
|
uni.removeStorageSync('selectedWarehouse')
|
||||||
|
uni.removeStorageSync('selectedPositionIndex')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
/**
|
||||||
|
* 加载仓库列表
|
||||||
|
* @returns {Promise<Array>} 仓库列表
|
||||||
|
*/
|
||||||
|
async loadWarehouseList({ commit }) {
|
||||||
|
try {
|
||||||
|
const res = await uni.request({
|
||||||
|
url: '/zhishu/warehouse/list',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.data && res.data.data) {
|
||||||
|
commit('setWarehouseList', res.data.data)
|
||||||
|
return res.data.data
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载仓库列表失败:', error)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择仓库
|
||||||
|
* @param {String} warehouseId - 仓库 ID
|
||||||
|
*/
|
||||||
|
selectWarehouse({ commit }, warehouseId) {
|
||||||
|
commit('setSelectedWarehouse', warehouseId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
/**
|
||||||
|
* 获取当前选中的仓库 ID
|
||||||
|
*/
|
||||||
|
selectedWarehouse: state => state.selectedWarehouse,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取仓库列表
|
||||||
|
*/
|
||||||
|
warehouseList: state => state.warehouseList,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取选择位置索引
|
||||||
|
*/
|
||||||
|
selectedPosition: state => state.selectedPosition,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据仓库 ID 获取仓库信息
|
||||||
|
*/
|
||||||
|
getWarehouseById: state => (id) => {
|
||||||
|
return state.warehouseList.find(w => w.id === id) || null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
mutations,
|
||||||
|
actions,
|
||||||
|
getters
|
||||||
|
}
|
||||||
83
temp_brace_check.js
Normal file
83
temp_brace_check.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = 'D:\\project\\zhizhu\\components\\PhotoUploadForm.vue';
|
||||||
|
const content = fs.readFileSync(path, 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
// 更精确的括号计数 - 考虑字符串和注释
|
||||||
|
let scriptStart = -1;
|
||||||
|
let scriptEnd = -1;
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].includes('<script>') || lines[i].includes('<script ')) {
|
||||||
|
scriptStart = i;
|
||||||
|
}
|
||||||
|
if (lines[i].includes('</script>')) {
|
||||||
|
scriptEnd = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Script section: line ${scriptStart + 1} to ${scriptEnd + 1}`);
|
||||||
|
|
||||||
|
// 简单统计 script 区域的括号
|
||||||
|
let braceCount = 0;
|
||||||
|
let parenCount = 0;
|
||||||
|
let bracketCount = 0;
|
||||||
|
let problematicLines = [];
|
||||||
|
|
||||||
|
for (let i = scriptStart; i <= scriptEnd; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
// 跳过单行注释
|
||||||
|
const codeOnly = line.replace(/\/\/.*$/, '').replace(/\/\*.*?\*\//g, '');
|
||||||
|
|
||||||
|
const opens = (codeOnly.match(/{/g) || []).length;
|
||||||
|
const closes = (codeOnly.match(/}/g) || []).length;
|
||||||
|
const diff = opens - closes;
|
||||||
|
|
||||||
|
braceCount += diff;
|
||||||
|
|
||||||
|
// 记录大幅度变化的行
|
||||||
|
if (Math.abs(diff) >= 2) {
|
||||||
|
problematicLines.push({ line: i + 1, diff, count: braceCount, content: line.trim().substring(0, 80) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\nFinal brace count:', braceCount);
|
||||||
|
|
||||||
|
// 逐行追踪括号变化,找到不平衡的位置
|
||||||
|
console.log('\n=== Tracking brace count at key points ===');
|
||||||
|
braceCount = 0;
|
||||||
|
let keyPoints = [];
|
||||||
|
|
||||||
|
for (let i = scriptStart; i <= scriptEnd; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
const codeOnly = line.replace(/\/\/.*$/, '').replace(/\/\*.*?\*\//g, '');
|
||||||
|
|
||||||
|
const opens = (codeOnly.match(/{/g) || []).length;
|
||||||
|
const closes = (codeOnly.match(/}/g) || []).length;
|
||||||
|
braceCount += opens - closes;
|
||||||
|
|
||||||
|
// 记录方法声明和结束
|
||||||
|
if (line.match(/\s+(async\s+)?\w+\([^)]*\)\s*{/) ||
|
||||||
|
line.match(/^\s+\},\s*$/) ||
|
||||||
|
line.match(/^\s+\};\s*$/)) {
|
||||||
|
keyPoints.push({ line: i + 1, count: braceCount, content: line.trim().substring(0, 60) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出 count=0 的位置(可能是 methods 对象结束)
|
||||||
|
console.log('\nPoints where braceCount = 0:');
|
||||||
|
keyPoints.filter(p => p.count === 0).forEach(p => {
|
||||||
|
console.log(` Line ${p.line}: ${p.content}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 找出 count 变化最大的区域
|
||||||
|
console.log('\nProblematic lines (large brace count changes):');
|
||||||
|
problematicLines.forEach(p => {
|
||||||
|
console.log(` Line ${p.line}: diff=${p.diff}, count=${p.count}, content: ${p.content}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 特别检查 methods 区域
|
||||||
|
console.log('\n=== Methods area key structure ===');
|
||||||
|
keyPoints.filter(p => p.line >= 398 && p.line <= 2000).forEach(p => {
|
||||||
|
console.log(` Line ${p.line}: count=${p.count}, ${p.content}`);
|
||||||
|
});
|
||||||
28
temp_check_data.js
Normal file
28
temp_check_data.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = 'D:\\project\\zhizhu\\components\\PhotoUploadForm.vue';
|
||||||
|
const content = fs.readFileSync(path, 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
// 检查所有包含 'data' 的行
|
||||||
|
console.log('=== Lines containing "data" as property declaration ===\n');
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if ((lines[i].includes('data()') || lines[i].includes('data ()') || lines[i].match(/\sdata\s*:/))) {
|
||||||
|
console.log(`Line ${i + 1}: ${lines[i]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有两个 data() 函数
|
||||||
|
console.log('\n=== Checking for duplicate data() ===\n');
|
||||||
|
let dataCount = 0;
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].match(/\sdata\s*\(\s*\)\s*\{?/)) {
|
||||||
|
dataCount++;
|
||||||
|
console.log(`Data function #${dataCount} at line ${i + 1}: ${lines[i]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 line 509 附近
|
||||||
|
console.log('\n=== Lines 505-515 ===');
|
||||||
|
for (let i = 504; i < 515 && i < lines.length; i++) {
|
||||||
|
console.log(`Line ${i + 1}: ${lines[i]}`);
|
||||||
|
}
|
||||||
109
temp_deep_check.js
Normal file
109
temp_deep_check.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = 'D:\\project\\zhizhu\\components\\PhotoUploadForm.vue';
|
||||||
|
const content = fs.readFileSync(path, 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
// 检查是否有重复的方法声明
|
||||||
|
console.log('=== Checking for duplicate method declarations ===\n');
|
||||||
|
const methodDeclarations = new Map();
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
const match = line.match(/^\s+(async\s+)?(\w+)\s*\([^)]*\)\s*{/);
|
||||||
|
if (match && !line.includes('if (') && !line.includes('if(') &&
|
||||||
|
!line.includes('for (') && !line.includes('for(') &&
|
||||||
|
!line.includes('while (') && !line.includes('while(') &&
|
||||||
|
!line.includes('switch (') && !line.includes('switch(') &&
|
||||||
|
!line.includes('catch (') && !line.includes('catch(')) {
|
||||||
|
const methodName = match[2];
|
||||||
|
if (!methodDeclarations.has(methodName)) {
|
||||||
|
methodDeclarations.set(methodName, []);
|
||||||
|
}
|
||||||
|
methodDeclarations.get(methodName).push(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出重复声明
|
||||||
|
let hasDuplicates = false;
|
||||||
|
for (const [name, lineNumbers] of methodDeclarations) {
|
||||||
|
if (lineNumbers.length > 1) {
|
||||||
|
hasDuplicates = true;
|
||||||
|
console.log(`DUPLICATE: ${name} declared at lines: ${lineNumbers.join(', ')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasDuplicates) {
|
||||||
|
console.log('No duplicate method declarations found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 data() 返回的对象是否完整
|
||||||
|
console.log('\n=== Checking data() section ===');
|
||||||
|
let inData = false;
|
||||||
|
let dataStart = -1;
|
||||||
|
let braceCount = 0;
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].includes('data()') || lines[i].includes('data ()')) {
|
||||||
|
inData = true;
|
||||||
|
dataStart = i;
|
||||||
|
}
|
||||||
|
if (inData) {
|
||||||
|
braceCount += (lines[i].match(/{/g) || []).length;
|
||||||
|
braceCount -= (lines[i].match(/}/g) || []).length;
|
||||||
|
if (braceCount === 0 && i > dataStart) {
|
||||||
|
console.log(`data() section: lines ${dataStart + 1} to ${i + 1}`);
|
||||||
|
// 检查是否有 return 语句
|
||||||
|
let hasReturn = false;
|
||||||
|
for (let j = dataStart; j <= i; j++) {
|
||||||
|
if (lines[j].includes('return {') || lines[j].includes('return{')) {
|
||||||
|
hasReturn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasReturn) {
|
||||||
|
console.log('WARNING: data() section may be missing return statement');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 export default 结构
|
||||||
|
console.log('\n=== Checking export default structure ===');
|
||||||
|
let exportStart = -1;
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].includes('export default {')) {
|
||||||
|
exportStart = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exportStart >= 0) {
|
||||||
|
console.log(`export default starts at line ${exportStart + 1}`);
|
||||||
|
|
||||||
|
// 检查关键属性
|
||||||
|
const keyProps = ['name', 'components', 'props', 'computed', 'data', 'watch', 'methods'];
|
||||||
|
for (const prop of keyProps) {
|
||||||
|
let found = false;
|
||||||
|
for (let i = exportStart; i < lines.length; i++) {
|
||||||
|
if (lines[i].includes(`${prop}:`) || lines[i].includes(`${prop} :`)) {
|
||||||
|
console.log(` ${prop}: found at line ${i + 1}`);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found && prop !== 'props') { // props 可能是可选的
|
||||||
|
console.log(` ${prop}: NOT FOUND`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件末尾结构
|
||||||
|
console.log('\n=== Checking file end structure ===');
|
||||||
|
const lastLines = lines.slice(-30);
|
||||||
|
for (let i = lines.length - 30; i < lines.length; i++) {
|
||||||
|
if (lines[i].includes('</script>') || lines[i].includes('</style>') ||
|
||||||
|
lines[i].includes('};') || lines[i].includes('},') ||
|
||||||
|
lines[i].trim() === '}') {
|
||||||
|
console.log(`Line ${i + 1}: ${lines[i]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
72
temp_final_validate.js
Normal file
72
temp_final_validate.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = 'D:\\project\\zhizhu\\components\\PhotoUploadForm.vue';
|
||||||
|
const content = fs.readFileSync(path, 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
// 找到 script 区域
|
||||||
|
let scriptStart = -1;
|
||||||
|
let scriptEnd = -1;
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].trim() === '<script>') {
|
||||||
|
scriptStart = i;
|
||||||
|
}
|
||||||
|
if (lines[i].trim() === '</script>') {
|
||||||
|
scriptEnd = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Script section: lines ${scriptStart + 1} to ${scriptEnd + 1}`);
|
||||||
|
|
||||||
|
// 精确的括号计数 - 跳过 import 语句中的括号
|
||||||
|
let braceCount = 0;
|
||||||
|
let inImport = false;
|
||||||
|
let importBraceCount = 0;
|
||||||
|
|
||||||
|
for (let i = scriptStart; i <= scriptEnd; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
|
||||||
|
// 检测多行 import
|
||||||
|
if (line.includes('import ') && line.includes('{') && !line.includes('}')) {
|
||||||
|
inImport = true;
|
||||||
|
importBraceCount = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inImport) {
|
||||||
|
importBraceCount += (line.match(/{/g) || []).length;
|
||||||
|
importBraceCount -= (line.match(/}/g) || []).length;
|
||||||
|
if (importBraceCount === 0) {
|
||||||
|
inImport = false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过单行 import
|
||||||
|
if (line.includes('import ') && line.includes('from ')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常代码计数
|
||||||
|
const codeOnly = line.replace(/\/\/.*$/, '').replace(/\/\*.*?\*\//g, '');
|
||||||
|
const noStrings = codeOnly.replace(/'[^']*'/g, '""').replace(/"[^"]*"/g, '""').replace(/`[^`]*`/g, '""');
|
||||||
|
|
||||||
|
const opens = (noStrings.match(/{/g) || []).length;
|
||||||
|
const closes = (noStrings.match(/}/g) || []).length;
|
||||||
|
braceCount += opens - closes;
|
||||||
|
|
||||||
|
// 检查异常
|
||||||
|
if (braceCount < 0) {
|
||||||
|
console.log(`ERROR: Negative brace count at line ${i + 1}`);
|
||||||
|
console.log(` Content: ${line}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\nFinal brace count: ${braceCount}`);
|
||||||
|
console.log(braceCount === 0 ? '✓ Balanced!' : '✗ Unbalanced!');
|
||||||
|
|
||||||
|
// 检查文件末尾
|
||||||
|
console.log('\n=== File ending (lines 1990-2000) ===');
|
||||||
|
for (let i = 1989; i < 2000; i++) {
|
||||||
|
console.log(`Line ${i + 1}: ${lines[i]}`);
|
||||||
|
}
|
||||||
85
temp_find_error.js
Normal file
85
temp_find_error.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = 'D:\\project\\zhizhu\\components\\PhotoUploadForm.vue';
|
||||||
|
const content = fs.readFileSync(path, 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
// 找到 script 区域
|
||||||
|
let scriptStart = -1;
|
||||||
|
let scriptEnd = -1;
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].includes('<script>') || lines[i].includes('<script ')) {
|
||||||
|
scriptStart = i;
|
||||||
|
}
|
||||||
|
if (lines[i].includes('</script>')) {
|
||||||
|
scriptEnd = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 逐行精确计数
|
||||||
|
let braceCount = 0;
|
||||||
|
let sections = [];
|
||||||
|
|
||||||
|
for (let i = scriptStart; i <= scriptEnd; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
// 去掉注释
|
||||||
|
let code = line.replace(/\/\/.*$/, '').replace(/\/\*.*?\*\//g, '');
|
||||||
|
|
||||||
|
// 去掉字符串(简单处理)
|
||||||
|
code = code.replace(/'[^']*'/g, '""').replace(/"[^"]*"/g, '""').replace(/`[^`]*`/g, '""');
|
||||||
|
|
||||||
|
const opens = (code.match(/{/g) || []).length;
|
||||||
|
const closes = (code.match(/}/g) || []).length;
|
||||||
|
const prevCount = braceCount;
|
||||||
|
braceCount += opens - closes;
|
||||||
|
|
||||||
|
// 记录关键位置
|
||||||
|
if (line.includes('export default') ||
|
||||||
|
line.includes('data()') ||
|
||||||
|
line.includes('data ()') ||
|
||||||
|
line.includes('computed:') ||
|
||||||
|
line.includes('watch:') ||
|
||||||
|
line.includes('methods:') ||
|
||||||
|
line.includes('components:') ||
|
||||||
|
line.includes('props:') ||
|
||||||
|
line.includes('</script>')) {
|
||||||
|
sections.push({ line: i + 1, count: braceCount, content: line.trim() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('=== Key sections ===');
|
||||||
|
sections.forEach(s => {
|
||||||
|
console.log(`Line ${s.line}: count=${s.count}, ${s.content}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 逐行追踪,找到 count 第一次变成 1 的位置
|
||||||
|
console.log('\n=== Finding where braceCount becomes 1 ===');
|
||||||
|
braceCount = 0;
|
||||||
|
|
||||||
|
for (let i = scriptStart; i <= scriptEnd; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
let code = line.replace(/\/\/.*$/, '').replace(/\/\*.*?\*\//g, '');
|
||||||
|
code = code.replace(/'[^']*'/g, '""').replace(/"[^"]*"/g, '""').replace(/`[^`]*`/g, '""');
|
||||||
|
|
||||||
|
const opens = (code.match(/{/g) || []).length;
|
||||||
|
const closes = (code.match(/}/g) || []).length;
|
||||||
|
const prevCount = braceCount;
|
||||||
|
braceCount += opens - closes;
|
||||||
|
|
||||||
|
// 检查是否出现异常
|
||||||
|
if (braceCount < 0) {
|
||||||
|
console.log(`Line ${i + 1}: NEGATIVE count ${braceCount}!`);
|
||||||
|
console.log(` Content: ${line}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到 count 变成 1 且之后一直保持 >= 1 的起点
|
||||||
|
if (braceCount === 1 && prevCount === 0) {
|
||||||
|
console.log(`\nBrace count becomes 1 at line ${i + 1}:`);
|
||||||
|
console.log(` ${line}`);
|
||||||
|
console.log(`\nContext (lines ${i-4} to ${i+10}):`);
|
||||||
|
for (let j = Math.max(scriptStart, i - 5); j <= Math.min(scriptEnd, i + 10); j++) {
|
||||||
|
const marker = j === i ? '>>>' : ' ';
|
||||||
|
console.log(`${marker} Line ${j + 1}: ${lines[j]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
temp_find_methods.js
Normal file
47
temp_find_methods.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = 'D:\\project\\zhizhu\\components\\PhotoUploadForm.vue';
|
||||||
|
const content = fs.readFileSync(path, 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
console.log('=== Lines 1300-1330 (around the issue) ===\n');
|
||||||
|
for (let i = 1299; i < 1330; i++) {
|
||||||
|
console.log(`Line ${i + 1}: ${lines[i]}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n=== Looking for methods: declaration ===');
|
||||||
|
for (let i = 390; i < 410; i++) {
|
||||||
|
console.log(`Line ${i + 1}: ${lines[i]}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n=== Checking methods object structure ===');
|
||||||
|
// 找到 methods: 的位置
|
||||||
|
let methodsStart = -1;
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].includes('methods:') && !lines[i].includes('onLoad') && !lines[i].includes('onShow')) {
|
||||||
|
methodsStart = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('methods: starts at line', methodsStart + 1);
|
||||||
|
|
||||||
|
// 找到 methods 对象的结束(应该是 }, 后面跟着 },
|
||||||
|
let braceCount = 0;
|
||||||
|
let started = false;
|
||||||
|
for (let i = methodsStart; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
|
||||||
|
if (line.includes('{')) {
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (started) {
|
||||||
|
braceCount += (line.match(/{/g) || []).length;
|
||||||
|
braceCount -= (line.match(/}/g) || []).length;
|
||||||
|
|
||||||
|
if (braceCount === 0) {
|
||||||
|
console.log(`Methods object might end at line ${i + 1}: ${line}`);
|
||||||
|
console.log(` Next line: ${lines[i + 1]}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
temp_find_script.js
Normal file
32
temp_find_script.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = 'D:\\project\\zhizhu\\components\\PhotoUploadForm.vue';
|
||||||
|
const content = fs.readFileSync(path, 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
console.log('=== Finding script and style boundaries ===\n');
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].includes('<script') || lines[i].includes('</script>') ||
|
||||||
|
lines[i].includes('<style') || lines[i].includes('</style>')) {
|
||||||
|
console.log(`Line ${i + 1}: ${lines[i]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到 </script> 的位置
|
||||||
|
let scriptEndLine = -1;
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].trim() === '</script>') {
|
||||||
|
scriptEndLine = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scriptEndLine >= 0) {
|
||||||
|
console.log(`\n</script> found at line ${scriptEndLine + 1}`);
|
||||||
|
console.log('\nLines around </script>:');
|
||||||
|
for (let i = scriptEndLine - 5; i <= scriptEndLine + 3; i++) {
|
||||||
|
if (i >= 0 && i < lines.length) {
|
||||||
|
console.log(`Line ${i + 1}: ${lines[i]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
temp_fix_brace.js
Normal file
42
temp_fix_brace.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = 'D:\\project\\zhizhu\\components\\PhotoUploadForm.vue';
|
||||||
|
const content = fs.readFileSync(path, 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
console.log('=== Current file ending ===');
|
||||||
|
console.log('Lines 1993-2000:');
|
||||||
|
for (let i = 1992; i < 2000; i++) {
|
||||||
|
console.log(`Line ${i + 1}: "${lines[i]}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查 Line 1996 的内容
|
||||||
|
console.log('\nLine 1996 content analysis:');
|
||||||
|
console.log(` Raw: "${lines[1995]}"`);
|
||||||
|
console.log(` Length: ${lines[1995].length}`);
|
||||||
|
console.log(` Trimmed: "${lines[1995].trim()}"`);
|
||||||
|
|
||||||
|
// 如果 Line 1996 是 " };"(1个tab + 分号),需要改成 " },"(1个tab + 逗号)
|
||||||
|
if (lines[1995].trim() === '};') {
|
||||||
|
// 检查前面是否有 tab
|
||||||
|
const hasTab = lines[1995].startsWith('\t');
|
||||||
|
if (hasTab) {
|
||||||
|
console.log('\nFixing Line 1996: changing " };" to " },"');
|
||||||
|
lines[1995] = '\t},';
|
||||||
|
|
||||||
|
// 写回文件
|
||||||
|
const newContent = lines.join('\n');
|
||||||
|
fs.writeFileSync(path, newContent, 'utf8');
|
||||||
|
console.log('File updated successfully!');
|
||||||
|
|
||||||
|
// 验证
|
||||||
|
console.log('\n=== After fix ===');
|
||||||
|
for (let i = 1992; i < 2000; i++) {
|
||||||
|
console.log(`Line ${i + 1}: "${lines[i]}"`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('Line 1996 does not start with tab, checking further...');
|
||||||
|
console.log(` First char: "${lines[1995][0]}" (code: ${lines[1995].charCodeAt(0)})`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`Line 1996 is not "};", it's "${lines[1995]}"`);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user