first commit

This commit is contained in:
97694731 2026-06-15 16:37:57 +08:00
parent 61fcf7dd71
commit 9053419211
455 changed files with 78155 additions and 20503 deletions

View File

@ -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
View File

@ -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 官方组件库
{
"pages": [ ### 工具库
{ - **crypto-js 4.2.0** - 加密算法库
"path": "pages/index/index", - **SCSS** - 样式预处理
"style": {
"navigationBarTitleText": "图书上传系统" ### 开发工具
- **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
{
// API 基础 URL
baseURL: 'https://api.buzhiyushu.cn',
// 客户端 ID
clientId: '1400a724f627ddc73d8f4dd344f80a5e',
// 授权类型xcx: 小程序)
grantType: 'xcx',
// 请求超时时间(毫秒)
timeout: 10000
}
```
### 3. 小程序配置
编辑 [manifest.json](manifest.json),配置微信小程序 AppID
```json
{
"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: {
"path": "pages/isbn-upload/index", ...mapActions(['setUserInfo', 'setToken'])
"style": {
"navigationBarTitleText": "ISBN上传"
} }
}, }
{ ```
"path": "pages/title-upload/index",
"style": {
"navigationBarTitleText": "仅书名上传"
}
}
// 其他已有页面...
]
}
```
2. **测试新页面功能** ### 组件使用
- 确保所有功能正常运行
- 检查页面间的跳转是否流畅
- 验证各项功能是否与原系统一致
## 注意事项 项目使用了 easycom 自动引入组件,无需手动引入:
- 拆分后的页面共用同样的组件和样式 ```vue
- 保留了原始功能,只是将其分散到不同页面 <template>
- 如有UI上的问题可能需要调整相应的样式 <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) - 初始版本发布
- 用户登录注册
- 图书上传功能
- 商品管理
- 仓库管理
---
## 开发团队
本项目由知书团队开发维护。
---
## 许可证
本项目为商业项目,版权归知书团队所有。

View File

@ -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)
// 发送请求 // 发送请求

View File

@ -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);
// hiddenfalse
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('发现待上传的识图照片,准备上传');
// hiddenfalse使
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 @@
// hiddentrue // hiddentrue
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)) {

File diff suppressed because it is too large Load Diff

View File

@ -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) => {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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(() => {

View File

@ -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
View 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
View 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
View 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
View File

@ -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
View 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
View 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
View 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

Binary file not shown.

2
node_modules/de-indent/.npmignore generated vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
.DS_Store

45
node_modules/de-indent/index.js generated vendored Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,379 @@
# he [![Build status](https://travis-ci.org/mathiasbynens/he.svg?branch=master)](https://travis-ci.org/mathiasbynens/he) [![Code coverage status](https://codecov.io/github/mathiasbynens/he/coverage.svg?branch=master)](https://codecov.io/github/mathiasbynens/he?branch=master) [![Dependency status](https://gemnasium.com/mathiasbynens/he.svg)](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 arent printable ASCII symbols and `&`, `<`, `>`, `"`, `'`, and `` ` ``, replacing them with character references.
```js
he.encode('foo © bar ≠ baz 𝌆 qux');
// → 'foo &#xA9; bar &#x2260; baz &#x1D306; 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. `&copy;`) in the output — hexadecimal escapes (e.g. `&#xA9;`) 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 &#xA9; bar &#x2260; baz &#x1D306; qux'
// Passing an `options` object to `encode`, to explicitly disallow named references:
he.encode('foo © bar ≠ baz 𝌆 qux', {
'useNamedReferences': false
});
// → 'foo &#xA9; bar &#x2260; baz &#x1D306; qux'
// Passing an `options` object to `encode`, to explicitly allow named references:
he.encode('foo © bar ≠ baz 𝌆 qux', {
'useNamedReferences': true
});
// → 'foo &copy; bar &ne; baz &#x1D306; qux'
```
#### `decimal`
The default value for the `decimal` option is `false`. If the option is enabled, `encode` will generally use decimal escapes (e.g. `&#169;`) rather than hexadecimal escapes (e.g. `&#xA9;`). 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. `&copy;`) 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 &#xA9; bar &#x2260; baz &#x1D306; qux'
// Passing an `options` object to `encode`, to explicitly disable decimal escapes:
he.encode('foo © bar ≠ baz 𝌆 qux', {
'decimal': false
});
// → 'foo &#xA9; bar &#x2260; baz &#x1D306; qux'
// Passing an `options` object to `encode`, to explicitly enable decimal escapes:
he.encode('foo © bar ≠ baz 𝌆 qux', {
'decimal': true
});
// → 'foo &#169; bar &#8800; baz &#119558; 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 &copy; bar &ne; baz &#119558; 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 dont 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 &#xA9; bar &#x2260; baz &#x1D306; qux'
// Passing an `options` object to `encode`, to explicitly encode all symbols:
he.encode('foo © bar ≠ baz 𝌆 qux', {
'encodeEverything': true
});
// → '&#x66;&#x6F;&#x6F;&#x20;&#xA9;&#x20;&#x62;&#x61;&#x72;&#x20;&#x2260;&#x20;&#x62;&#x61;&#x7A;&#x20;&#x1D306;&#x20;&#x71;&#x75;&#x78;'
// This setting can be combined with the `useNamedReferences` option:
he.encode('foo © bar ≠ baz 𝌆 qux', {
'encodeEverything': true,
'useNamedReferences': true
});
// → '&#x66;&#x6F;&#x6F;&#x20;&copy;&#x20;&#x62;&#x61;&#x72;&#x20;&ne;&#x20;&#x62;&#x61;&#x7A;&#x20;&#x1D306;&#x20;&#x71;&#x75;&#x78;'
```
#### `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');
// → '&#x1;'
// Passing an `options` object to `encode`, to explicitly enable error-tolerant mode:
he.encode('\x01', {
'strict': false
});
// → '&#x1;'
// 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 &#xA9; 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 &copy; bar &ne; baz &#x1D306; 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 &copy; bar &ne; baz &#x1D306; 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&ampbar');
// → 'foo&bar'
// Passing an `options` object to `decode`, to explicitly assume an HTML text context:
he.decode('foo&ampbar', {
'isAttributeValue': false
});
// → 'foo&bar'
// Passing an `options` object to `decode`, to explicitly assume an HTML attribute value context:
he.decode('foo&ampbar', {
'isAttributeValue': true
});
// → 'foo&ampbar'
```
#### `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&ampbar');
// → 'foo&bar'
// Passing an `options` object to `decode`, to explicitly enable error-tolerant mode:
he.decode('foo&ampbar', {
'strict': false
});
// → 'foo&bar'
// Passing an `options` object to `decode`, to explicitly enable strict mode:
he.decode('foo&ampbar', {
'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&ampbar');
// → 'foo&ampbar'
```
### `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)">');
// → '&lt;img src=&#x27;x&#x27; onerror=&quot;prompt(1)&quot;&gt;'
```
### `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&#xF6;o &#x2665; b&#xE5;r &#x1D306; baz
$ he --encode --use-named-refs 'föo ♥ bår 𝌆 baz'
f&ouml;o &hearts; b&aring;r &#x1D306; baz
$ he --decode 'f&ouml;o &hearts; b&aring;r &#x1D306; 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, 1537
* IE 611
* 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 thats 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
| [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](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
View 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 \'&copy; &#x1D306;\' | 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 dont actively support. However, we dont 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

File diff suppressed because one or more lines are too long

78
node_modules/he/man/he.1 generated vendored Normal file
View 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 `&#xA9;`, but it wouldn't turn `+` into `&#x2B;` since there is no point in doing so. Additionally, it replaces any remaining non-ASCII symbols with a hexadecimal escape sequence (e.g. `&#x1D306;`). The return value of this function is always valid HTML.
.It Sy "--encode --use-named-refs"
Enable the use of named character references (like `&copy;`) 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 `&#169;` instead of `&#xA9;`.
.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 '&copy;&#x1D306;'"
Print the decoded version of the given HTML string.
.It Sy "echo\ '&copy;&#x1D306;'\ |\ 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
View 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
View 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
View 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

File diff suppressed because one or more lines are too long

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
View 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
View 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
View 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
View File

@ -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"
}
} }
} }
} }

View File

@ -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",

View File

@ -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

View File

@ -21,14 +21,25 @@
</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: {
goToWarehouseSelect() { goToWarehouseSelect() {
uni.navigateTo({ uni.navigateTo({
@ -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,66 +66,81 @@ 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'
})
}
} }
} }
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.entry-container { .entry-container {
padding: 40rpx; padding: 40rpx;
min-height: 100vh; min-height: 100vh;
background: #f8f8f8; background: #f8f8f8;
} }
.header { .header {
margin-bottom: 40rpx; margin-bottom: 40rpx;
} }
.title { .title {
font-size: 36rpx; font-size: 36rpx;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
} }
.subtitle { .subtitle {
display: block; display: block;
margin-top: 12rpx; margin-top: 12rpx;
font-size: 26rpx; font-size: 26rpx;
color: #666; color: #666;
} }
.card-list { .card-list {
background-color: #fff; background-color: #fff;
border-radius: 20rpx; border-radius: 20rpx;
padding: 0 30rpx; padding: 0 30rpx;
} }
.card { .card {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 30rpx 0; padding: 30rpx 0;
border-bottom: 1rpx solid #f5f5f5; border-bottom: 1rpx solid #f5f5f5;
} }
.card:last-child { .card:last-child {
border-bottom: none; border-bottom: none;
} }
.card-left { .card-left {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.card-title { .card-title {
font-size: 32rpx; font-size: 32rpx;
color: #222; color: #222;
} }
.card-desc { .card-desc {
margin-top: 8rpx; margin-top: 8rpx;
font-size: 24rpx; font-size: 24rpx;
color: #888; color: #888;
} }
</style> </style>

View File

@ -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 {
// GETCookie
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>

View File

@ -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">
@ -76,9 +75,10 @@
clientId: '1400a724f627ddc73d8f4dd344f80a5e', clientId: '1400a724f627ddc73d8f4dd344f80a5e',
grantType: 'xcx', grantType: 'xcx',
code: '', code: '',
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;

View File

@ -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 {

View File

@ -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>

View File

@ -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>

View File

@ -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">

File diff suppressed because it is too large Load Diff

View File

@ -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) {

File diff suppressed because it is too large Load Diff

View File

@ -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('成功跳转到商品管理页面');
}, },

View File

@ -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);
// IDformData // IDformData
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);
// //

View 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": "我的"
}
]
}
}

View File

@ -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, // hiddenUI hidden: true, // hiddenUI
num: "2" // num: "999", //
isOCR: true //
}; };
// //

View File

@ -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>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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>

File diff suppressed because it is too large Load Diff

View 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": "我的"
}
]
}
}

File diff suppressed because it is too large Load Diff

View 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>

View File

@ -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>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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', {

View 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>

File diff suppressed because it is too large Load Diff

58
services/index.js Normal file
View 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
View 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
View 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
}

View 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
View 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
}

View 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

Binary file not shown.

BIN
static/ding2.mp3 Normal file

Binary file not shown.

BIN
static/ding3.mp3 Normal file

Binary file not shown.

View File

@ -1,105 +1,34 @@
/**
* Vuex Store - 全局状态管理入口
*
* 模块拆分说明
* - auth: 用户认证tokenuserInfo登录状态
* - price: 价格配置priceModepriceType运费等
* - 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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