|
|
@ -0,0 +1,16 @@
|
|||
######################################################################
|
||||
# Build Tools
|
||||
|
||||
/unpackage/*
|
||||
/node_modules/*
|
||||
|
||||
######################################################################
|
||||
# Development Tools
|
||||
|
||||
/.idea/*
|
||||
/.vscode/*
|
||||
/.hbuilderx/*
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
## 修改浅色模式样式,增强模块区分度
|
||||
|
||||
### 修改内容:
|
||||
1. **调整CSS变量**(在 `:root` 中)
|
||||
- 背景色:`#f5f7fa` → `#e8ecf1`(增强对比)
|
||||
- 卡片阴影:`rgba(0, 0, 0, 0.05)` → `rgba(0, 0, 0, 0.12)`(更明显)
|
||||
- 边框色:`#e0e0e0` → `#d1d5db`(更清晰的边框)
|
||||
- 分割线:`darkgray` → `#d1d5db`
|
||||
|
||||
2. **增强卡片样式**
|
||||
- 添加微妙的边框效果
|
||||
- 增强阴影深度
|
||||
- 优化卡片与背景的对比
|
||||
|
||||
3. **保持深色模式不变**
|
||||
- 深色模式已经很清晰,无需修改
|
||||
|
||||
### 预期效果:
|
||||
- 模块之间区分更明显
|
||||
- 视觉层次更清晰
|
||||
- 保持简洁风格的同时提升可读性
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<script>
|
||||
import config from './config'
|
||||
import { getToken } from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
this.initApp()
|
||||
},
|
||||
methods: {
|
||||
// 初始化应用
|
||||
initApp() {
|
||||
// 初始化应用配置
|
||||
this.initConfig()
|
||||
// 检查用户登录状态
|
||||
//#ifdef H5
|
||||
this.checkLogin()
|
||||
//#endif
|
||||
},
|
||||
initConfig() {
|
||||
this.globalData.config = config
|
||||
},
|
||||
checkLogin() {
|
||||
if (!getToken()) {
|
||||
this.$tab.reLaunch('/pages/login')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/static/scss/index.scss'
|
||||
</style>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 若依
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<p align="center">
|
||||
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-43e3941654fa3054c9684bf53d1b1d356a1.png">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v1.2.0</h1>
|
||||
<h4 align="center">基于UniApp开发的轻量级移动端框架</h4>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/y_project/RuoYi-App/stargazers"><img src="https://gitee.com/y_project/RuoYi-App/badge/star.svg?theme=dark"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-App"><img src="https://img.shields.io/badge/RuoYi-v1.2.0-brightgreen.svg"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-App/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||
</p>
|
||||
|
||||
## 平台简介
|
||||
|
||||
RuoYi App 移动解决方案,采用uniapp框架,一份代码多终端适配,同时支持APP、小程序、H5!实现了与[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)、[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)完美对接的移动解决方案!目前已经实现登录、我的、工作台、编辑资料、头像修改、密码修改、常见问题、关于我们等基础功能。
|
||||
|
||||
* 配套后端代码仓库地址[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) 或 [RuoYi-Cloud](https://github.com/yangzongzhuan/RuoYi-Cloud) 版本。
|
||||
* 应用框架基于[uniapp](https://uniapp.dcloud.net.cn/),支持小程序、H5、Android和IOS。
|
||||
* 前端组件采用[uni-ui](https://github.com/dcloudio/uni-ui),全端兼容的高性能UI框架。
|
||||
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)
|
||||
|
||||
|
||||
## 技术文档
|
||||
|
||||
- 官网网站:[http://ruoyi.vip](http://ruoyi.vip)
|
||||
- 文档地址:[http://doc.ruoyi.vip](http://doc.ruoyi.vip)
|
||||
- H5页体验:[http://h5.ruoyi.vip](http://h5.ruoyi.vip)
|
||||
- QQ交流群: ①133713780(满)、②146013835(满)、③189091635
|
||||
- 小程序体验
|
||||
|
||||
<img src="https://oscimg.oschina.net/oscnet/up-26c76dc90b92acdbd9ac8cd5252f07c8ad9.jpg" alt="小程序演示"/>
|
||||
|
||||
|
||||
## 演示图
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-21f6f842fdc94540469b4eb43fdadbaf7f8.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-a6f23cf9a371a30165e135eff6d9ae89a9d.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-ff5f62016bf6624c1ff27eee57499dccd44.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-b9a582fdb26ec69d407fabd044d2c8494df.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-96427ee08fca29d77934cfc8d1b1a637cef.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-5fdadc582d24cccd7727030d397b63185a3.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-0a36797b6bcc50c36d40c3c782665b89efc.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-d77995cc00687cedd00d5ac7d68a07ea276.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-fa8f5ab20becf59b4b38c1b92a9989e7109.png"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 登录方法
|
||||
export function login(username, password, code, uuid) {
|
||||
const data = {
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
uuid
|
||||
}
|
||||
return request({
|
||||
'url': '/login',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
'method': 'post',
|
||||
'data': data
|
||||
})
|
||||
}
|
||||
|
||||
// 注册方法
|
||||
export function register(data) {
|
||||
return request({
|
||||
url: '/register',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
return request({
|
||||
'url': '/getInfo',
|
||||
'method': 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出方法
|
||||
export function logout() {
|
||||
return request({
|
||||
'url': '/logout',
|
||||
'method': 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
export function getCodeImg() {
|
||||
return request({
|
||||
'url': '/captchaImage',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'get',
|
||||
timeout: 20000
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
import { getToken } from '@/utils/auth'
|
||||
import { toast } from '@/utils/common'
|
||||
|
||||
// 参数转URL查询字符串
|
||||
function tansParams(params) {
|
||||
let result = ''
|
||||
for (const propName of Object.keys(params)) {
|
||||
const value = params[propName]
|
||||
var part = encodeURIComponent(propName) + "="
|
||||
if (value !== null && value !== "" && typeof (value) !== "undefined") {
|
||||
if (typeof value === 'object') {
|
||||
for (const key of Object.keys(value)) {
|
||||
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
|
||||
let params = propName + '[' + key + ']'
|
||||
var subPart = encodeURIComponent(params) + "="
|
||||
result += subPart + encodeURIComponent(value[key]) + "&"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result += part + encodeURIComponent(value) + "&"
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 专门用于8081端口的请求
|
||||
const request8081 = config => {
|
||||
config.header = config.header || {}
|
||||
|
||||
// 8081端口可能使用不同的认证方式
|
||||
const token = getToken()
|
||||
|
||||
console.log('=== 8081端口请求详情 ===')
|
||||
console.log('Token值:', token)
|
||||
console.log('Token存在:', !!token)
|
||||
|
||||
if (token) {
|
||||
// 尝试多种认证方式
|
||||
// 方式1:Bearer Token(标准JWT)
|
||||
config.header['Authorization'] = 'Bearer ' + token
|
||||
|
||||
// 方式2:直接使用token(不带Bearer)
|
||||
// config.header['Authorization'] = token
|
||||
|
||||
// 方式3:自定义认证头
|
||||
// config.header['X-Auth-Token'] = token
|
||||
|
||||
// 方式4:Cookie方式
|
||||
// config.header['Cookie'] = 'token=' + token
|
||||
}
|
||||
|
||||
// 处理GET请求参数
|
||||
let requestUrl = config.baseUrl + config.url
|
||||
if (config.params) {
|
||||
let url = requestUrl + '?' + tansParams(config.params)
|
||||
requestUrl = url.slice(0, -1)
|
||||
}
|
||||
|
||||
console.log('使用的认证方式:', 'Bearer Token')
|
||||
console.log('请求头:', config.header)
|
||||
console.log('请求URL:', requestUrl)
|
||||
console.log('请求参数:', config.params)
|
||||
console.log('请求方法:', config.method)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// 为PUT请求设置Content-Type
|
||||
if (config.method && config.method.toLowerCase() === 'put') {
|
||||
config.header = {
|
||||
...config.header,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
|
||||
uni.request({
|
||||
method: config.method || 'get',
|
||||
timeout: config.timeout || 10000,
|
||||
url: requestUrl,
|
||||
data: config.data,
|
||||
header: config.header,
|
||||
dataType: 'json'
|
||||
}).then(response => {
|
||||
let [error, res] = response
|
||||
console.log('=== 8081端口响应详情 ===')
|
||||
// console.log('响应状态码:', res.statusCode)
|
||||
console.log('响应头:', res.header)
|
||||
console.log('响应数据:', res.data)
|
||||
console.log('响应数据类型:', typeof res.data)
|
||||
|
||||
if (error) {
|
||||
toast('后端接口连接异常')
|
||||
reject('后端接口连接异常')
|
||||
return
|
||||
}
|
||||
|
||||
const code = res.data.code || 200
|
||||
const msg = res.data.msg || '请求失败'
|
||||
|
||||
// console.log('业务状态码:', code)
|
||||
// console.log('业务消息:', msg)
|
||||
|
||||
if (code === 401) {
|
||||
toast('认证失败,请重新登录')
|
||||
reject('401')
|
||||
} else if (code === 403) {
|
||||
// console.log('=== 403错误详细信息 ===')
|
||||
// console.log('完整响应:', JSON.stringify(res, null, 2))
|
||||
toast('没有权限访问该资源')
|
||||
reject('403')
|
||||
} else if (code === 500) {
|
||||
toast(msg)
|
||||
reject('500')
|
||||
} else if (code !== 200) {
|
||||
toast(msg)
|
||||
reject(code)
|
||||
}
|
||||
|
||||
resolve(res.data)
|
||||
}).catch(error => {
|
||||
console.error('8081端口请求异常:', error)
|
||||
toast('网络请求失败')
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 获取商品列表
|
||||
export function getProductList(params) {
|
||||
const token = getToken()
|
||||
// console.log('=== 商品列表请求诊断 ===')
|
||||
// console.log('1. Token值:', token)
|
||||
// console.log('2. Token长度:', token ? token.length : 0)
|
||||
// console.log('3. Token前缀:', token ? token.substring(0, 20) + '...' : '无')
|
||||
// console.log('4. 请求URL:', 'http://193.112.94.36:8081/mall/product/list')
|
||||
// console.log('5. 认证方式:', 'Bearer Token (与8080共享)')
|
||||
|
||||
return request8081({
|
||||
baseUrl: 'http://193.112.94.36:8081',
|
||||
url: '/mall/product/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 删除商品
|
||||
export function deleteProduct(id) {
|
||||
return request8081({
|
||||
baseUrl: 'http://193.112.94.36:8081',
|
||||
url: `/mall/product/delete/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增商品
|
||||
export function addProduct(data) {
|
||||
return request8081({
|
||||
baseUrl: 'http://193.112.94.36:8081',
|
||||
url: '/mall/product/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 新增商品(带文件上传)
|
||||
export function addProductWithFile(filePath, formData) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: 'http://193.112.94.36:8081/mall/product/add',
|
||||
filePath: filePath,
|
||||
name: 'file',
|
||||
formData: formData,
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + getToken()
|
||||
},
|
||||
success: (uploadRes) => {
|
||||
const res = JSON.parse(uploadRes.data);
|
||||
resolve(res);
|
||||
},
|
||||
fail: (error) => {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 获取商品详情
|
||||
export function getProductDetail(id) {
|
||||
return request8081({
|
||||
baseUrl: 'http://193.112.94.36:8081',
|
||||
url: `/mall/product/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改商品(form-data格式,支持文件上传)
|
||||
export function updateProductWithFile(filePath, formData) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: 'http://193.112.94.36:8081/mall/product/update',
|
||||
filePath: filePath,
|
||||
name: 'file',
|
||||
formData: formData,
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + getToken()
|
||||
},
|
||||
success: (uploadRes) => {
|
||||
const res = JSON.parse(uploadRes.data);
|
||||
resolve(res);
|
||||
},
|
||||
fail: (error) => {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 修改商品
|
||||
export function updateProduct(data) {
|
||||
return request8081({
|
||||
baseUrl: 'http://193.112.94.36:8081',
|
||||
url: '/mall/product/update',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 商品数据导入(文件上传)
|
||||
export function importProductData(filePath, formData) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: 'http://193.112.94.36:8081/mall/product/importData',
|
||||
filePath: filePath,
|
||||
name: 'file',
|
||||
formData: formData,
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + getToken()
|
||||
},
|
||||
success: (uploadRes) => {
|
||||
const res = JSON.parse(uploadRes.data);
|
||||
resolve(res);
|
||||
},
|
||||
fail: (error) => {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 获取导入记录
|
||||
export function getImportRecord() {
|
||||
return request8081({
|
||||
baseUrl: 'http://192.168.0.7:8081',
|
||||
url: '/mall/product/importRecord',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 根据用户ID查询门店列表
|
||||
export function getStoreList(userId) {
|
||||
return request({
|
||||
baseUrl: 'http://193.112.94.36:8081',
|
||||
url: `/mall/store/getUserStore/${userId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询字典数据列表
|
||||
export function listData(query) {
|
||||
return request({
|
||||
url: '/system/dict/data/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询字典数据详细
|
||||
export function getData(dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/' + dictCode,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据字典类型查询字典数据信息
|
||||
export function getDicts(dictType) {
|
||||
return request({
|
||||
url: '/system/dict/data/type/' + dictType,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增字典数据
|
||||
export function addData(data) {
|
||||
return request({
|
||||
url: '/system/dict/data',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改字典数据
|
||||
export function updateData(data) {
|
||||
return request({
|
||||
url: '/system/dict/data',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除字典数据
|
||||
export function delData(dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/' + dictCode,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 测试接口
|
||||
export function getOen() {
|
||||
return request({
|
||||
url: '/system/config/test',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询字典类型列表
|
||||
export function listType(query) {
|
||||
return request({
|
||||
url: '/system/dict/type/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询字典类型详细
|
||||
export function getType(dictId) {
|
||||
return request({
|
||||
url: '/system/dict/type/' + dictId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增字典类型
|
||||
export function addType(data) {
|
||||
return request({
|
||||
url: '/system/dict/type',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改字典类型
|
||||
export function updateType(data) {
|
||||
return request({
|
||||
url: '/system/dict/type',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除字典类型
|
||||
export function delType(dictId) {
|
||||
return request({
|
||||
url: '/system/dict/type/' + dictId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新字典缓存
|
||||
export function refreshCache() {
|
||||
return request({
|
||||
url: '/system/dict/type/refreshCache',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取字典选择框列表
|
||||
export function optionselect() {
|
||||
return request({
|
||||
url: '/system/dict/type/optionselect',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import upload from '@/utils/upload'
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 用户密码重置
|
||||
export function updateUserPwd(oldPassword, newPassword) {
|
||||
const data = {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用户个人信息
|
||||
export function getUserProfile() {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户个人信息
|
||||
export function updateUserProfile(data) {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户头像上传
|
||||
export function uploadAvatar(data) {
|
||||
return upload({
|
||||
url: '/system/user/profile/avatar',
|
||||
name: data.name,
|
||||
filePath: data.filePath
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
<template>
|
||||
<view class="scan-view-container">
|
||||
<!-- 扫码控件容器 -->
|
||||
<view class="scan-box" id="scan-box"></view>
|
||||
|
||||
<!-- 扫码状态 -->
|
||||
<view class="scan-status" v-if="isScanning">
|
||||
<view class="scan-line"></view>
|
||||
<text class="scan-text">正在扫描...</text>
|
||||
</view>
|
||||
|
||||
<!-- 扫码控制按钮 -->
|
||||
<view class="scan-controls" v-if="isScanning">
|
||||
<view class="control-btn" @click="toggleFlash">
|
||||
<uni-icons type="flash" size="22" color="#fff"></uni-icons>
|
||||
<text class="control-text">{{ flashOn ? '关闭手电' : '开启手电' }}</text>
|
||||
</view>
|
||||
<view class="control-btn" @click="pauseScan">
|
||||
<uni-icons type="pause" size="22" color="#fff"></uni-icons>
|
||||
<text class="control-text">{{ scanPaused ? '继续扫码' : '暂停扫码' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ScanView',
|
||||
props: {
|
||||
autoStart: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isScanning: false,
|
||||
scanPaused: false,
|
||||
flashOn: false,
|
||||
barcodeInstance: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.autoStart) {
|
||||
this.initScan();
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.closeScan();
|
||||
},
|
||||
methods: {
|
||||
initScan() {
|
||||
if (typeof plus === 'undefined') {
|
||||
console.log('当前环境不支持plus扫码');
|
||||
this.$emit('error', '扫码功能需要在App中使用');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('开始初始化扫码控件');
|
||||
this.isScanning = true;
|
||||
|
||||
const barcode = plus.barcode.create('barcode', [plus.barcode.CODE_128, plus.barcode.EAN_13], {
|
||||
top: '0',
|
||||
left: '0',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute'
|
||||
});
|
||||
|
||||
console.log('扫码控件创建成功');
|
||||
|
||||
barcode.onmarked = (type, result) => {
|
||||
console.log('识别成功:', result);
|
||||
this.isScanning = false;
|
||||
this.$emit('success', result);
|
||||
};
|
||||
|
||||
barcode.onerror = (error) => {
|
||||
console.error('扫码错误:', error);
|
||||
this.isScanning = false;
|
||||
this.$emit('error', error || '扫码失败');
|
||||
};
|
||||
|
||||
const scanBox = document.getElementById('scan-box');
|
||||
if (scanBox) {
|
||||
scanBox.appendChild(barcode);
|
||||
}
|
||||
|
||||
barcode.start();
|
||||
console.log('扫码已启动');
|
||||
this.barcodeInstance = barcode;
|
||||
},
|
||||
|
||||
closeScan() {
|
||||
if (this.barcodeInstance) {
|
||||
this.barcodeInstance.close();
|
||||
this.barcodeInstance = null;
|
||||
this.isScanning = false;
|
||||
console.log('扫码控件已关闭');
|
||||
}
|
||||
},
|
||||
|
||||
pauseScan() {
|
||||
this.scanPaused = !this.scanPaused;
|
||||
if (this.barcodeInstance) {
|
||||
this.scanPaused ? this.barcodeInstance.pause() : this.barcodeInstance.resume();
|
||||
}
|
||||
this.$emit('pause', this.scanPaused);
|
||||
},
|
||||
|
||||
resumeScan() {
|
||||
if (this.barcodeInstance && this.scanPaused) {
|
||||
this.barcodeInstance.resume();
|
||||
this.scanPaused = false;
|
||||
this.isScanning = true;
|
||||
}
|
||||
},
|
||||
|
||||
toggleFlash() {
|
||||
this.flashOn = !this.flashOn;
|
||||
if (this.barcodeInstance) {
|
||||
this.barcodeInstance.setFlash(this.flashOn);
|
||||
}
|
||||
this.$emit('flash', this.flashOn);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.scan-view-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.scan-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.scan-status {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.scan-line {
|
||||
width: 80%;
|
||||
height: 2px;
|
||||
background-color: #e60012;
|
||||
animation: scanMove 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes scanMove {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(250px);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.scan-text {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.scan-controls {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.control-text {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
<template>
|
||||
<view class="uni-section">
|
||||
<view class="uni-section-header" @click="onClick">
|
||||
<view class="uni-section-header__decoration" v-if="type" :class="type" />
|
||||
<slot v-else name="decoration"></slot>
|
||||
|
||||
<view class="uni-section-header__content">
|
||||
<text :style="{'font-size':titleFontSize,'color':titleColor}" class="uni-section__content-title" :class="{'distraction':!subTitle}">{{ title }}</text>
|
||||
<text v-if="subTitle" :style="{'font-size':subTitleFontSize,'color':subTitleColor}" class="uni-section-header__content-sub">{{ subTitle }}</text>
|
||||
</view>
|
||||
|
||||
<view class="uni-section-header__slot-right">
|
||||
<slot name="right"></slot>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="uni-section-content" :style="{padding: _padding}">
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
/**
|
||||
* Section 标题栏
|
||||
* @description 标题栏
|
||||
* @property {String} type = [line|circle|square] 标题装饰类型
|
||||
* @value line 竖线
|
||||
* @value circle 圆形
|
||||
* @value square 正方形
|
||||
* @property {String} title 主标题
|
||||
* @property {String} titleFontSize 主标题字体大小
|
||||
* @property {String} titleColor 主标题字体颜色
|
||||
* @property {String} subTitle 副标题
|
||||
* @property {String} subTitleFontSize 副标题字体大小
|
||||
* @property {String} subTitleColor 副标题字体颜色
|
||||
* @property {String} padding 默认插槽 padding
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'UniSection',
|
||||
emits:['click'],
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
titleFontSize: {
|
||||
type: String,
|
||||
default: '14px'
|
||||
},
|
||||
titleColor:{
|
||||
type: String,
|
||||
default: '#333'
|
||||
},
|
||||
subTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
subTitleFontSize: {
|
||||
type: String,
|
||||
default: '12px'
|
||||
},
|
||||
subTitleColor: {
|
||||
type: String,
|
||||
default: '#999'
|
||||
},
|
||||
padding: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
_padding(){
|
||||
if(typeof this.padding === 'string'){
|
||||
return this.padding
|
||||
}
|
||||
|
||||
return this.padding?'10px':''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
title(newVal) {
|
||||
if (uni.report && newVal !== '') {
|
||||
uni.report('title', newVal)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" >
|
||||
$uni-primary: #2979ff !default;
|
||||
|
||||
.uni-section {
|
||||
background-color: #fff;
|
||||
.uni-section-header {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 12px 10px;
|
||||
font-weight: normal;
|
||||
|
||||
&__decoration{
|
||||
margin-right: 6px;
|
||||
background-color: $uni-primary;
|
||||
&.line {
|
||||
width: 4px;
|
||||
height: 12px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&.circle {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-top-right-radius: 50px;
|
||||
border-top-left-radius: 50px;
|
||||
border-bottom-left-radius: 50px;
|
||||
border-bottom-right-radius: 50px;
|
||||
}
|
||||
|
||||
&.square {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
color: #333;
|
||||
|
||||
.distraction {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
&-sub {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&__slot-right{
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.uni-section-content{
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// 应用全局配置
|
||||
module.exports = {
|
||||
// baseUrl: 'https://vue.ruoyi.vip/prod-api',
|
||||
|
||||
baseUrl:'http://193.112.94.36:8080',
|
||||
// prodApi: 'https://vue.ruoyi.vip/prod-api',
|
||||
// baseUrl: 'http://localhost:8080',
|
||||
// 应用信息
|
||||
appInfo: {
|
||||
// 应用名称
|
||||
name: "ruoyi-app",
|
||||
// 应用版本
|
||||
version: "1.2.0",
|
||||
// 应用logo
|
||||
logo: "/static/logo.png",
|
||||
// 官方网站
|
||||
site_url: "http://ruoyi.vip",
|
||||
// 政策协议
|
||||
agreements: [{
|
||||
title: "隐私政策",
|
||||
url: "https://ruoyi.vip/protocol.html"
|
||||
},
|
||||
{
|
||||
title: "用户服务协议",
|
||||
url: "https://ruoyi.vip/protocol.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import store from './store' // store
|
||||
import plugins from './plugins' // plugins
|
||||
import './permission' // permission
|
||||
import { getDicts } from "@/api/system/dict/data"
|
||||
|
||||
Vue.use(plugins)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$store = store
|
||||
Vue.prototype.getDicts = getDicts
|
||||
|
||||
App.mpType = 'app'
|
||||
|
||||
const app = new Vue({
|
||||
...App
|
||||
})
|
||||
|
||||
app.$mount()
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"name" : "若依移动端",
|
||||
"appid" : "__UNI__34800B4",
|
||||
"description" : "111",
|
||||
"versionName" : "1.2.0",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
"app-plus" : {
|
||||
"usingComponents" : true,
|
||||
"nvueCompiler" : "uni-app",
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
"modules" : {
|
||||
"Barcode" : {},
|
||||
"Camera" : {}
|
||||
},
|
||||
"distribute" : {
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
"ios" : {
|
||||
"dSYMs" : false,
|
||||
"privacyDescription" : {
|
||||
"NSCameraUsageDescription" : "需要使用相机扫描条码",
|
||||
"NSPhotoLibraryUsageDescription" : "需要访问相册选择图片扫码"
|
||||
}
|
||||
},
|
||||
"sdkConfigs" : {}
|
||||
}
|
||||
},
|
||||
"quickapp" : {},
|
||||
"mp-weixin" : {
|
||||
"appid" : "wxccd7e2a0911b3397",
|
||||
"setting" : {
|
||||
"urlCheck" : false,
|
||||
"es6" : false,
|
||||
"minified" : true,
|
||||
"postcss" : true
|
||||
},
|
||||
"optimization" : {
|
||||
"subPackages" : true
|
||||
},
|
||||
"usingComponents" : true
|
||||
},
|
||||
"vueVersion" : "2",
|
||||
"h5" : {
|
||||
"template" : "static/index.html",
|
||||
"devServer" : {
|
||||
"port" : 9090,
|
||||
"https" : false
|
||||
},
|
||||
"title" : "RuoYi-App",
|
||||
"router" : {
|
||||
"mode" : "hash",
|
||||
"base" : "./"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
{
|
||||
"pages": [{
|
||||
"path": "pages/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/register",
|
||||
"style": {
|
||||
"navigationBarTitleText": "注册"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "若依移动端框架",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/work/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "工作台"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/avatar/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改头像"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/info/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人信息"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/info/edit",
|
||||
"style": {
|
||||
"navigationBarTitleText": "编辑资料"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/pwd/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改密码"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/setting/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "应用设置"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/help/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "常见问题"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/mine/about/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "关于我们"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/common/webview/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "浏览网页"
|
||||
}
|
||||
}, {
|
||||
"path": "pages/common/textview/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "浏览文本"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/menu",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/user",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/settings/settings",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/product/product",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/asset/asset",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/enter/enter",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/addProduct/addProduct",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/storeSelect/storeSelect",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择门店"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/edit/edit",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/Import /Import ",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/userStores/userStores",
|
||||
"style": {
|
||||
"navigationBarTitleText": "用户门店关联"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/userStores/userStores",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"tabBar": {
|
||||
"color": "#000000",
|
||||
"selectedColor": "#000000",
|
||||
"borderStyle": "white",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [{
|
||||
"pagePath": "pages/index",
|
||||
"iconPath": "/static/images/tabbar/Frame 87.png",
|
||||
"selectedIconPath": "/static/images/tabbar/Frame 86.png",
|
||||
"text": "我的店"
|
||||
}, {
|
||||
"pagePath": "pages/work/index",
|
||||
"iconPath": "/static/images/tabbar/Union.png",
|
||||
"selectedIconPath": "/static/images/tabbar/Union-1.png",
|
||||
"text": "热销榜"
|
||||
}, {
|
||||
"pagePath": "pages/mine/index",
|
||||
"iconPath": "/static/images/tabbar/Vector-1.png",
|
||||
"selectedIconPath": "/static/images/tabbar/Vector.png",
|
||||
"text": "消息"
|
||||
}
|
||||
]
|
||||
},
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "RuoYi",
|
||||
"navigationBarBackgroundColor": "#FFFFFF"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,493 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="navbar">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<text class="iconfont">←</text>
|
||||
</view>
|
||||
<view class="nav-title">批量导入</view>
|
||||
<view class="nav-right">
|
||||
<text class="iconfont">···</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选项卡切换 -->
|
||||
<view class="tab-bar">
|
||||
<view class="tab-item" :class="{active: activeTab === 'import'}" @click="activeTab = 'import'">
|
||||
批量导入
|
||||
</view>
|
||||
<view class="tab-item" :class="{active: activeTab === 'record'}" @click="activeTab = 'record'">
|
||||
导入记录
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 批量导入内容区 -->
|
||||
<view v-if="activeTab === 'import'" class="content">
|
||||
<!-- 方式1:导入进货单【核心带完整上传功能】 -->
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<view class="method-tag">方式1</view>
|
||||
<view class="method-title">导入进货单</view>
|
||||
</view>
|
||||
<view class="upload-area" @click="pickFile" v-if="!uploadFileName">
|
||||
<view class="upload-icon">
|
||||
<text class="iconfont">↥</text>
|
||||
</view>
|
||||
<view class="upload-text">点击上传文件</view>
|
||||
<view class="upload-desc">(仅支持10M以下,以xls、xlsx或csv结尾的文件类型)</view>
|
||||
</view>
|
||||
<!-- 已选择文件展示 -->
|
||||
<view class="file-selected" v-else>
|
||||
<view class="file-name">{{uploadFileName}}</view>
|
||||
<view class="file-opt">
|
||||
<text class="reupload" @click="pickFile">重新选择</text>
|
||||
<text class="cancel" @click="cancelFile">取消</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 导入按钮 -->
|
||||
<view class="import-btn-box" v-if="uploadFileName">
|
||||
<button class="import-btn" :loading="isUploading" @click="uploadAndImport">开始导入商品</button>
|
||||
</view>
|
||||
<view class="desc-list">
|
||||
<text class="desc-item">1. 可导入:供应商进货单、其他收银系统的商品导出明细</text>
|
||||
<text class="desc-item">2. 支持录入条形码、商品名称、零售价、库存、进货价、商品品牌</text>
|
||||
<text class="desc-item">3. 导入成功后会在原商品基础上增加库存,不会覆盖原库存</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 方式2:通过电脑端录入 -->
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<view class="method-tag">方式2</view>
|
||||
<view class="method-title">通过电脑端录入</view>
|
||||
</view>
|
||||
<view class="card-content">
|
||||
<view class="content-text">支持导入更大文件、更多商品数、商品信息</view>
|
||||
<view class="link-area" @click="openLink">
|
||||
<text class="link-text">请在电脑端访问 http://e.weidian.com/main</text>
|
||||
<text class="iconfont">↗</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 方式3:微店商品快速录入 -->
|
||||
<view class="card">
|
||||
<view class="card-header">
|
||||
<view class="method-tag">方式3</view>
|
||||
<view class="method-title">微店商品快速录入</view>
|
||||
</view>
|
||||
<view class="card-content">
|
||||
<view class="content-text">如果您没有电脑,可以试试我们的快捷设置功能</view>
|
||||
<view class="link-area" @click="goQuickSetup">
|
||||
<text class="link-text">3-20分钟快速设置价格 ></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 导入记录内容区【补充列表骨架+空数据】 -->
|
||||
<view v-else class="record-content">
|
||||
<view class="empty-state" v-if="recordList.length == 0">
|
||||
<text class="empty-icon">📋</text>
|
||||
<text class="empty-text">暂无导入记录</text>
|
||||
<text class="empty-subtext">导入商品后,记录将展示在这里</text>
|
||||
</view>
|
||||
<view class="record-list" v-else>
|
||||
<view class="record-item" v-for="(item,index) in recordList" :key="index">
|
||||
<view class="record-name">{{item.fileName}}</view>
|
||||
<view class="record-info">
|
||||
<text class="record-time">{{item.createTime}}</text>
|
||||
<text class="record-status" :class="item.status">{{item.statusText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 导入商品相关API
|
||||
import { importProductData, getImportRecord } from '@/api/product';
|
||||
// 导入门店ID获取工具
|
||||
import { getStoreId } from '@/utils/auth';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'import', // 默认选中批量导入
|
||||
uploadFilePath: '', // 选中的文件临时路径
|
||||
uploadFileName: '', // 选中的文件名称
|
||||
isUploading: false, // 是否正在上传/导入中
|
||||
recordList: [] // 导入记录列表
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// 每次进入页面刷新导入记录
|
||||
if(this.activeTab == 'record'){
|
||||
this.getImportRecord();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
uni.navigateBack({ delta: 1 });
|
||||
},
|
||||
|
||||
// ========== 核心:选择文件并校验 ==========
|
||||
pickFile() {
|
||||
uni.chooseFile({
|
||||
count: 1, // 只允许选择1个文件
|
||||
type: 'file',
|
||||
sizeLimit: 10 * 1024 * 1024, // 限制10M以内,和页面提示一致
|
||||
success: (res) => {
|
||||
const tempFile = res.tempFiles[0];
|
||||
const fileName = tempFile.name;
|
||||
const fileSize = tempFile.size;
|
||||
|
||||
// 1. 文件大小校验
|
||||
if (fileSize > 10 * 1024 * 1024) {
|
||||
return uni.showToast({ title: '文件大小不能超过10M', icon: 'none', duration: 2000 });
|
||||
}
|
||||
|
||||
// 2. 文件格式校验 (严格匹配xls/xlsx/csv)
|
||||
const suffix = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
|
||||
const allowSuffix = ['.xls', '.xlsx', '.csv'];
|
||||
if (!allowSuffix.includes(suffix)) {
|
||||
return uni.showToast({ title: '仅支持xls、xlsx、csv格式文件', icon: 'none', duration: 2000 });
|
||||
}
|
||||
|
||||
// 校验通过,赋值文件信息
|
||||
this.uploadFilePath = tempFile.path;
|
||||
this.uploadFileName = fileName;
|
||||
uni.showToast({ title: '文件选择成功', icon: 'success', duration: 1500 });
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({ title: '文件选择失败,请重新选择', icon: 'none' });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 取消已选择的文件
|
||||
cancelFile() {
|
||||
this.uploadFilePath = '';
|
||||
this.uploadFileName = '';
|
||||
},
|
||||
|
||||
// ========== 核心:文件上传+商品导入 主逻辑 ✅ 已对接你的真实接口 ✅ ==========
|
||||
uploadAndImport() {
|
||||
if (!this.uploadFilePath) return;
|
||||
this.isUploading = true; // 开启加载状态,防止重复点击
|
||||
|
||||
// 准备表单数据
|
||||
const formData = {
|
||||
storeId: getStoreId()
|
||||
};
|
||||
|
||||
// 调用API进行商品导入
|
||||
importProductData(this.uploadFilePath, formData)
|
||||
.then(res => {
|
||||
this.isUploading = false;
|
||||
|
||||
// 业务成功:导入成功 匹配接口 code=200
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
title: `导入成功!共导入 ${res.data} 个商品`,
|
||||
icon: 'success',
|
||||
duration: 2500
|
||||
});
|
||||
// 重置文件选择状态
|
||||
this.cancelFile();
|
||||
}
|
||||
// 业务失败:导入失败
|
||||
else {
|
||||
uni.showModal({
|
||||
title: '导入失败',
|
||||
content: res.msg || '文件内容有误,请检查后重新上传',
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.isUploading = false;
|
||||
console.error('文件上传失败:', err);
|
||||
uni.showModal({
|
||||
title: '上传异常',
|
||||
content: '网络异常或服务器连接失败,请检查接口服务后重试',
|
||||
showCancel: false,
|
||||
confirmText: '重新上传'
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 打开电脑端链接
|
||||
openLink() {
|
||||
uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '将在浏览器中打开电脑端录入地址',
|
||||
confirmText: '确认打开',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.openURL({
|
||||
url: 'http://e.weidian.com/main',
|
||||
fail: () => uni.showToast({ title: '打开失败,请手动复制链接', icon: 'none' })
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 跳转快速设置页面
|
||||
goQuickSetup() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/quick-setup/quick-setup'
|
||||
});
|
||||
},
|
||||
|
||||
// 获取导入记录
|
||||
getImportRecord() {
|
||||
uni.showLoading({ title: '加载中...', mask: true });
|
||||
|
||||
// 调用API获取导入记录
|
||||
getImportRecord()
|
||||
.then(res => {
|
||||
if(res.code == 200){
|
||||
this.recordList = res.data || [];
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('获取导入记录失败:', err);
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 全局样式重置 */
|
||||
.container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #e62318;
|
||||
color: white;
|
||||
padding: 20rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.nav-left, .nav-right {
|
||||
width: 60rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
.nav-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 选项卡 */
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
background-color: white;
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 30rpx 0;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
border-bottom: 4rpx solid transparent;
|
||||
}
|
||||
.tab-item.active {
|
||||
color: #e62318;
|
||||
border-bottom-color: #e62318;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 内容区 */
|
||||
.content {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
/* 卡片通用样式 */
|
||||
.card {
|
||||
background-color: white;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 30rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
background-color: white;
|
||||
}
|
||||
.method-tag {
|
||||
background-color: #e62318;
|
||||
color: white;
|
||||
font-size: 24rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
.method-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 上传区域 */
|
||||
.upload-area {
|
||||
margin: 0 30rpx 30rpx;
|
||||
border: 2rpx dashed #ccc;
|
||||
border-radius: 12rpx;
|
||||
padding: 60rpx 30rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.upload-icon {
|
||||
font-size: 48rpx;
|
||||
color: #999;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.upload-text {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.upload-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 已选择文件样式 */
|
||||
.file-selected {
|
||||
margin: 0 30rpx 30rpx;
|
||||
padding: 20rpx;
|
||||
background: #f9f9f9;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.file-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.file-opt {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
.reupload {
|
||||
font-size: 26rpx;
|
||||
color: #e62318;
|
||||
}
|
||||
.cancel {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 导入按钮样式 */
|
||||
.import-btn-box {
|
||||
padding: 0 30rpx 20rpx;
|
||||
}
|
||||
.import-btn {
|
||||
width: 100%;
|
||||
background: #e62318;
|
||||
color: white;
|
||||
font-size: 30rpx;
|
||||
padding: 20rpx 0;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
/* 描述列表 */
|
||||
.desc-list {
|
||||
padding: 0 30rpx 30rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.desc-item {
|
||||
display: block;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
/* 卡片内容区 */
|
||||
.card-content {
|
||||
padding: 0 30rpx 30rpx;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.content-text {
|
||||
margin-bottom: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
.link-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #e62318;
|
||||
}
|
||||
.link-text {
|
||||
font-size: 28rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
/* 导入记录样式 */
|
||||
.record-content {
|
||||
padding: 30rpx;
|
||||
}
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 100rpx 0;
|
||||
color: #999;
|
||||
}
|
||||
.empty-icon {
|
||||
font-size: 60rpx;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.empty-text {
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 10rpx;
|
||||
display: block;
|
||||
}
|
||||
.empty-subtext {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.record-list {
|
||||
background: white;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
.record-item {
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
}
|
||||
.record-name {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.record-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
.record-status.success {
|
||||
color: #07c160;
|
||||
}
|
||||
.record-status.fail {
|
||||
color: #e62318;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,466 @@
|
|||
<template>
|
||||
<view class="add-product-page">
|
||||
<!-- 头部 -->
|
||||
<view class="page-header">
|
||||
<text class="header-title">添加商品</text>
|
||||
</view>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-container">
|
||||
<!-- 商品名称 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">商品名称</text>
|
||||
<input
|
||||
class="item-input"
|
||||
type="text"
|
||||
placeholder="农夫山泉东方树叶乌龙茶500ml"
|
||||
v-model="productInfo.name"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 条码/条码扫码 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">条码</text>
|
||||
<view class="barcode-section">
|
||||
<input
|
||||
class="item-input barcode-input"
|
||||
type="text"
|
||||
placeholder="6921168558032"
|
||||
v-model="productInfo.barcode"
|
||||
/>
|
||||
<view class="scan-btn" @tap="handleScan">
|
||||
<text class="scan-text">扫码</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品图 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">商品图</text>
|
||||
<view class="image-upload-section">
|
||||
<view class="upload-placeholder" @tap="chooseImage">
|
||||
<text class="upload-icon">+</text>
|
||||
<text class="upload-text">点击上传商品图片</text>
|
||||
</view>
|
||||
<image
|
||||
v-if="productInfo.image"
|
||||
:src="productInfo.image"
|
||||
class="product-image"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="divider"></view>
|
||||
|
||||
<!-- 售价 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">售价</text>
|
||||
<view class="price-section">
|
||||
<input
|
||||
class="item-input price-input"
|
||||
type="digit"
|
||||
placeholder="5.0"
|
||||
v-model="productInfo.price"
|
||||
/>
|
||||
<text class="price-unit">元</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 推荐价格 -->
|
||||
<view class="recommend-price-section">
|
||||
<text class="recommend-title">推荐价格(点击快速设置售价)</text>
|
||||
<view class="recommend-list">
|
||||
<view class="recommend-item" @tap="setPrice(5.0)">
|
||||
<text class="recommend-percent">82%商家卖</text>
|
||||
<text class="recommend-price">5.0元</text>
|
||||
</view>
|
||||
<view class="recommend-item" @tap="setPrice(5.5)">
|
||||
<text class="recommend-percent">9%商家卖</text>
|
||||
<text class="recommend-price">5.5元</text>
|
||||
</view>
|
||||
<view class="recommend-item" @tap="setPrice(4.5)">
|
||||
<text class="recommend-percent">2%商家卖</text>
|
||||
<text class="recommend-price">4.5元</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="divider"></view>
|
||||
|
||||
<!-- 进货数量 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">进货数量</text>
|
||||
<view class="quantity-section">
|
||||
<input
|
||||
class="item-input"
|
||||
type="number"
|
||||
placeholder="请输入进货数量"
|
||||
v-model="productInfo.quantity"
|
||||
/>
|
||||
<text class="quantity-unit">件</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 进货价 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">进货价</text>
|
||||
<view class="cost-section">
|
||||
<input
|
||||
class="item-input"
|
||||
type="digit"
|
||||
placeholder="请输入进货价"
|
||||
v-model="productInfo.cost"
|
||||
/>
|
||||
<text class="cost-unit">元</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ✅ 保质期管理 - 红色滑动开关 完美匹配截图 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">保质期管理</text>
|
||||
<switch
|
||||
:checked="productInfo.expirationManagement"
|
||||
@change="toggleExpirationManagement"
|
||||
color="#F53F3F"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 保质期天数 -->
|
||||
<view class="form-item" v-if="productInfo.expirationManagement">
|
||||
<text class="item-label">保质期天数</text>
|
||||
<view class="expiration-options">
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: productInfo.expirationDays === 365 }"
|
||||
@tap="setExpirationDays(365)"
|
||||
>
|
||||
<text class="expiration-text">1年</text>
|
||||
<text class="expiration-days">36天</text>
|
||||
</view>
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: productInfo.expirationDays === 270 }"
|
||||
@tap="setExpirationDays(270)"
|
||||
>
|
||||
<text class="expiration-text">9个月</text>
|
||||
<text class="expiration-days">27天</text>
|
||||
</view>
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: productInfo.expirationDays === 180 }"
|
||||
@tap="setExpirationDays(180)"
|
||||
>
|
||||
<text class="expiration-text">6个月</text>
|
||||
<text class="expiration-days">18天</text>
|
||||
</view>
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: productInfo.expirationDays === 240 }"
|
||||
@tap="setExpirationDays(240)"
|
||||
>
|
||||
<text class="expiration-text">8个月</text>
|
||||
<text class="expiration-days">24天</text>
|
||||
</view>
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: productInfo.expirationDays === 300 }"
|
||||
@tap="setExpirationDays(300)"
|
||||
>
|
||||
<text class="expiration-text">10个月</text>
|
||||
<text class="expiration-days">30天</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 临期提醒天数 -->
|
||||
<view class="form-item" v-if="productInfo.expirationManagement">
|
||||
<text class="item-label">临期提醒天数</text>
|
||||
<view class="warning-days-display">
|
||||
<text class="warning-days-value">{{ warningDaysDisplay }}</text>
|
||||
<text class="warning-days-unit">天</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 生产日期 -->
|
||||
<view class="form-item" v-if="productInfo.expirationManagement">
|
||||
<text class="item-label">生产日期</text>
|
||||
<uni-datetime-picker
|
||||
type="date"
|
||||
v-model="productInfo.productionDate"
|
||||
:start="startDate"
|
||||
:end="endDate"
|
||||
placeholder="请选择生产日期"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 货架码 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">货架码</text>
|
||||
<input
|
||||
class="item-input"
|
||||
type="text"
|
||||
placeholder="请输入货架码"
|
||||
v-model="productInfo.shelfCode"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 商品编码 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">商品编码</text>
|
||||
<input
|
||||
class="item-input"
|
||||
type="text"
|
||||
placeholder="请输入商品编码"
|
||||
v-model="productInfo.productCode"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 保存按钮 -->
|
||||
<view class="save-button-container">
|
||||
<button class="save-button" @tap="handleSave">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addProductWithFile } from '@/api/product'
|
||||
import { getStoreId } from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
productInfo: {
|
||||
name: '',
|
||||
barcode: '',
|
||||
image: '',
|
||||
price: '',
|
||||
quantity: '',
|
||||
cost: '',
|
||||
expirationManagement: false,
|
||||
shelfCode: '',
|
||||
productCode: '',
|
||||
expirationDays: '',
|
||||
warningDays: '',
|
||||
productionDate: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
warningDaysDisplay() {
|
||||
if (!this.productInfo.expirationDays) return '';
|
||||
const days = parseInt(this.productInfo.expirationDays);
|
||||
return days.toString().substring(0, 2);
|
||||
},
|
||||
startDate() {
|
||||
return '2020-01-01';
|
||||
},
|
||||
endDate() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
console.log('页面参数:', options);
|
||||
|
||||
if (options.fromData) {
|
||||
try {
|
||||
const fromData = JSON.parse(decodeURIComponent(options.fromData));
|
||||
console.log('接收到的商品数据:', fromData);
|
||||
this.productInfo.name = fromData.productName || '';
|
||||
this.productInfo.barcode = fromData.productBarCode || '';
|
||||
this.productInfo.price = fromData.storePrice || '';
|
||||
this.productInfo.quantity = fromData.stockQuantity || '';
|
||||
this.productInfo.productCode = fromData.productCode || '';
|
||||
this.productInfo.image = fromData.mainImage || '';
|
||||
} catch (error) {
|
||||
console.error('解析商品数据失败:', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 扫码功能
|
||||
handleScan() {
|
||||
uni.showToast({ title: '扫码功能(模拟)', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
this.productInfo.barcode = '6921168558032'
|
||||
uni.showToast({ title: '扫码成功', icon: 'success' })
|
||||
}, 500)
|
||||
},
|
||||
|
||||
// 选择图片
|
||||
chooseImage() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
this.productInfo.image = res.tempFilePaths[0]
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 设置推荐价格
|
||||
setPrice(price) {
|
||||
this.productInfo.price = price.toString()
|
||||
uni.showToast({ title: `已设置为${price}元`, icon: 'success', duration: 1500 })
|
||||
},
|
||||
|
||||
// ✅ 切换保质期管理-适配红色滑动开关 极简写法
|
||||
toggleExpirationManagement(e) {
|
||||
this.productInfo.expirationManagement = e.detail.value
|
||||
},
|
||||
|
||||
// 设置保质期天数
|
||||
setExpirationDays(days) {
|
||||
this.productInfo.expirationDays = days.toString()
|
||||
const warningDays = days.toString().substring(0, 2);
|
||||
this.productInfo.warningDays = warningDays;
|
||||
uni.showToast({ title: `已设置为${days}天`, icon: 'success', duration: 1500 })
|
||||
},
|
||||
|
||||
// 表单验证-完整校验
|
||||
validateForm() {
|
||||
if (!this.productInfo.name.trim()) { uni.showToast({ title: '请输入商品名称', icon: 'none' });return false }
|
||||
if (!this.productInfo.price) { uni.showToast({ title: '请输入售价', icon: 'none' });return false }
|
||||
// 开启保质期管理 必校验三项
|
||||
if (this.productInfo.expirationManagement) {
|
||||
if (!this.productInfo.expirationDays) { uni.showToast({ title: '请选择保质期天数', icon: 'none' });return false }
|
||||
// 临期提醒天数自动设置,不需要手动输入
|
||||
if (!this.productInfo.productionDate) { uni.showToast({ title: '请选择生产日期', icon: 'none' });return false }
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 保存商品-完整传参
|
||||
async handleSave() {
|
||||
if (!this.validateForm()) return
|
||||
const storeId = getStoreId();
|
||||
if (!storeId) { uni.showToast({ title: '请先选择门店', icon: 'none' });return }
|
||||
|
||||
uni.showLoading({ title: '保存中...' });
|
||||
try {
|
||||
const formData = {
|
||||
productName: this.productInfo.name,
|
||||
productBarCode: this.productInfo.barcode,
|
||||
storePrice: this.productInfo.price,
|
||||
stockQuantity: this.productInfo.quantity,
|
||||
costPrice: this.productInfo.cost,
|
||||
productCode: this.productInfo.productCode,
|
||||
shelfCode: this.productInfo.shelfCode,
|
||||
expirationManagement: this.productInfo.expirationManagement ? 1 : 0,
|
||||
shelfLife: this.productInfo.expirationDays,
|
||||
productionDate: this.productInfo.productionDate,
|
||||
approaching: this.productInfo.warningDays,
|
||||
storeId: storeId
|
||||
};
|
||||
const res = await addProductWithFile(this.productInfo.image || '', formData);
|
||||
uni.hideLoading();
|
||||
if (res.code === 200) {
|
||||
uni.showToast({ title: '商品添加成功', icon: 'success', duration: 2000 });
|
||||
setTimeout(() => { uni.navigateBack({ delta: 1 }); }, 2000);
|
||||
} else {
|
||||
uni.showToast({ title: res.msg || '商品添加失败', icon: 'none' });
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('添加商品失败:', error);
|
||||
uni.showToast({ title: '网络请求失败', icon: 'none' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.add-product-page {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
/* 头部 */
|
||||
.page-header {
|
||||
background-color: #ffffff;
|
||||
padding: 40rpx 32rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.header-title { font-size: 36rpx;font-weight: 600;color: #333333; }
|
||||
|
||||
/* 表单容器 */
|
||||
.form-container { background-color: #ffffff;margin-top: 20rpx; }
|
||||
|
||||
/* 表单项 */
|
||||
.form-item {
|
||||
padding: 32rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.item-label { font-size: 32rpx;color: #333333;font-weight: 500;min-width: 160rpx; }
|
||||
.item-input { flex:1;font-size:32rpx;color:#333;text-align:right;padding:0 20rpx; }
|
||||
.item-input::placeholder { color: #999999; }
|
||||
|
||||
/* 条码区域 */
|
||||
.barcode-section { display:flex;align-items:center;flex:1; }
|
||||
.barcode-input { flex:1;margin-right:20rpx; }
|
||||
.scan-btn { background:#07C160;padding:16rpx 32rpx;border-radius:8rpx; }
|
||||
.scan-text { color:#fff;font-size:28rpx; }
|
||||
|
||||
/* 图片上传区域 */
|
||||
.image-upload-section { flex:1;display:flex;justify-content:flex-end; }
|
||||
.upload-placeholder { width:200rpx;height:200rpx;border:2rpx dashed #ccc;border-radius:8rpx;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#f9f9f9; }
|
||||
.upload-icon { font-size:48rpx;color:#999;margin-bottom:16rpx; }
|
||||
.upload-text { font-size:24rpx;color:#999; }
|
||||
.product-image { width:200rpx;height:200rpx;border-radius:8rpx; }
|
||||
|
||||
/* 价格区域 */
|
||||
.price-section { display:flex;align-items:center;flex:1;justify-content:flex-end; }
|
||||
.price-input { flex:none;width:120rpx;text-align:center; }
|
||||
.price-unit { font-size:32rpx;color:#333;margin-left:10rpx; }
|
||||
|
||||
/* 推荐价格 */
|
||||
.recommend-price-section { padding:32rpx;border-bottom:1rpx solid #f0f0f0; }
|
||||
.recommend-title { font-size:28rpx;color:#666;margin-bottom:24rpx;display:block; }
|
||||
.recommend-list { display:flex;justify-content:space-between; }
|
||||
.recommend-item { display:flex;flex-direction:column;align-items:center;padding:20rpx 24rpx;background:#f9f9f9;border-radius:8rpx;flex:1;margin:0 10rpx; }
|
||||
.recommend-percent { font-size:26rpx;color:#666;margin-bottom:8rpx; }
|
||||
.recommend-price { font-size:30rpx;color:#07C160;font-weight:500; }
|
||||
|
||||
/* 数量/进货价 单位 */
|
||||
.quantity-section,.cost-section { display:flex;align-items:center;flex:1;justify-content:flex-end; }
|
||||
.quantity-unit,.cost-unit { font-size:32rpx;color:#333;margin-left:10rpx; }
|
||||
|
||||
/* ✅ 保质期相关样式 完美匹配 */
|
||||
.expiration-options { display:flex;flex-wrap:wrap;gap:20rpx;flex:1;justify-content:flex-end; }
|
||||
.expiration-item { display:flex;flex-direction:column;align-items:center;padding:16rpx 20rpx;background:#f9f9f9;border-radius:8rpx;min-width:100rpx;border:2rpx solid transparent;transition:all 0.3s; }
|
||||
.expiration-item.active { background:#F53F3F;border-color:#F53F3F; }
|
||||
.expiration-item.active .expiration-text { color:#fff; }
|
||||
.expiration-item.active .expiration-days { color:#fff; }
|
||||
.expiration-text { font-size:28rpx;color:#333; }
|
||||
.expiration-days { font-size:24rpx;color:#999;margin-top:4rpx; }
|
||||
|
||||
/* 临期提醒天数样式 */
|
||||
.warning-days-display { display:flex;align-items:center;flex:1;justify-content:flex-end; }
|
||||
.warning-days-value { font-size:36rpx;color:#F53F3F;font-weight:600;margin-right:8rpx; }
|
||||
.warning-days-unit { font-size:28rpx;color:#666; }
|
||||
|
||||
/* 分隔线 */
|
||||
.divider { height:20rpx;background:#f5f5f5;border-top:1rpx solid #f0f0f0;border-bottom:1rpx solid #f0f0f0; }
|
||||
|
||||
/* 保存按钮 */
|
||||
.save-button-container { position:fixed;bottom:0;left:0;right:0;background:#fff;padding:20rpx 32rpx;border-top:1rpx solid #f0f0f0; }
|
||||
.save-button { background:#F53F3F;color:#ffffff;font-size:34rpx;height:88rpx;line-height:88rpx;border-radius:8rpx; }
|
||||
.save-button::after { border:none; }
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width:750px) {
|
||||
.recommend-list { flex-direction:column; }
|
||||
.recommend-item { margin:10rpx 0; }
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
<template>
|
||||
<view class="page">
|
||||
<!-- 顶部资产卡片 -->
|
||||
<view class="asset-card">
|
||||
<view class="asset-title">我的资产</view>
|
||||
<view class="asset-amount">¥0.00</view>
|
||||
|
||||
<view class="asset-details">
|
||||
<view class="detail-item">
|
||||
<view class="label">可提现</view>
|
||||
<view class="value">¥0.00</view>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<view class="detail-item">
|
||||
<view class="label">待结算</view>
|
||||
<view class="value">¥0.00</view>
|
||||
<view class="hint">次日可提现</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-buttons">
|
||||
<button class="btn primary" @tap="handleRecharge">充值</button>
|
||||
<button class="btn outline" @tap="handleWithdraw">提现到银行卡</button>
|
||||
</view>
|
||||
|
||||
<!-- 资金明细标题 -->
|
||||
<view class="section-title">资金明细</view>
|
||||
|
||||
<!-- 明细列表 -->
|
||||
<view class="detail-list">
|
||||
<view class="list-item" @tap="goToDetail('withdrawable')">
|
||||
<view class="item-left">
|
||||
<view class="item-icon withdrawable">可提现</view>
|
||||
<view class="item-label">¥0.00</view>
|
||||
</view>
|
||||
<view class="item-arrow">></view>
|
||||
</view>
|
||||
|
||||
<view class="list-item active" @tap="goToDetail('pending')">
|
||||
<view class="item-left">
|
||||
<view class="item-icon pending">待结算(元)</view>
|
||||
<view class="item-label">¥0.00</view>
|
||||
</view>
|
||||
<view class="item-arrow">✓</view>
|
||||
</view>
|
||||
|
||||
<view class="list-item" @tap="goToDetail('withdrawn')">
|
||||
<view class="item-left">
|
||||
<view class="item-icon withdrawn">已提现(元)</view>
|
||||
<view class="item-label">¥0.00</view>
|
||||
</view>
|
||||
<view class="item-arrow">></view>
|
||||
</view>
|
||||
|
||||
<view class="list-item" @tap="goToDetail('cash')">
|
||||
<view class="item-left">
|
||||
<view class="item-icon cash">现金支付(元)</view>
|
||||
<view class="item-label">¥0.00</view>
|
||||
</view>
|
||||
<view class="item-arrow">></view>
|
||||
</view>
|
||||
|
||||
<view class="list-item" @tap="goToBankCard">
|
||||
<view class="item-left">
|
||||
<view class="item-icon bank">我的银行卡</view>
|
||||
<view class="item-label">未绑定</view>
|
||||
</view>
|
||||
<view class="item-arrow">></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 常见问题 -->
|
||||
<view class="faq-section">
|
||||
<view class="faq-title">常见问题</view>
|
||||
<!-- 这里可以展开常见问题列表 -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const handleRecharge = () => {
|
||||
uni.showToast({
|
||||
title: '充值功能',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
const handleWithdraw = () => {
|
||||
uni.showToast({
|
||||
title: '提现功能',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
const goToDetail = (type) => {
|
||||
uni.showToast({
|
||||
title: `查看${type}明细`,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
const goToBankCard = () => {
|
||||
uni.showToast({
|
||||
title: '前往绑定银行卡',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: #f8f8f8;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
// 资产卡片
|
||||
.asset-card {
|
||||
background: linear-gradient(135deg, #4a6ee0, #6a8eff);
|
||||
border-radius: 20rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
color: #fff;
|
||||
margin-bottom: 40rpx;
|
||||
box-shadow: 0 10rpx 20rpx rgba(74, 110, 224, 0.2);
|
||||
}
|
||||
|
||||
.asset-title {
|
||||
font-size: 28rpx;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.asset-amount {
|
||||
font-size: 60rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.asset-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 26rpx;
|
||||
opacity: 0.85;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: 24rpx;
|
||||
opacity: 0.7;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 2rpx;
|
||||
height: 80rpx;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
// 操作按钮
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 90rpx;
|
||||
border-radius: 45rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.primary {
|
||||
background-color: #4a6ee0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.outline {
|
||||
background-color: transparent;
|
||||
border: 2rpx solid #4a6ee0;
|
||||
color: #4a6ee0;
|
||||
}
|
||||
}
|
||||
|
||||
// 分区标题
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 30rpx;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
|
||||
// 明细列表
|
||||
.detail-list {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 0 30rpx;
|
||||
margin-bottom: 40rpx;
|
||||
box-shadow: 0 5rpx 15rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.item-icon {
|
||||
color: #4a6ee0;
|
||||
}
|
||||
.item-label {
|
||||
color: #4a6ee0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
|
||||
&.withdrawable::before { content: "💳 "; }
|
||||
&.pending::before { content: "⏳ "; }
|
||||
&.withdrawn::before { content: "💰 "; }
|
||||
&.cash::before { content: "💵 "; }
|
||||
&.bank::before { content: "🏦 "; }
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
// 常见问题
|
||||
.faq-section {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 5rpx 15rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.faq-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<view>
|
||||
<uni-card class="view-title" :title="title">
|
||||
<text class="uni-body view-content">{{ content }}</text>
|
||||
</uni-card>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
title: '',
|
||||
content: ''
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.title = options.title
|
||||
this.content = options.content
|
||||
uni.setNavigationBarTitle({
|
||||
title: options.title
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.view-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.view-content {
|
||||
font-size: 26rpx;
|
||||
padding: 12px 5px 0;
|
||||
color: #333;
|
||||
line-height: 24px;
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<view v-if="params.url">
|
||||
<web-view :webview-styles="webviewStyles" :src="`${params.url}`"></web-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
params: {},
|
||||
webviewStyles: {
|
||||
progress: {
|
||||
color: "#FF3333"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
src: {
|
||||
type: [String],
|
||||
default: null
|
||||
}
|
||||
},
|
||||
onLoad(event) {
|
||||
this.params = event
|
||||
if (event.title) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: event.title
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,892 @@
|
|||
<template>
|
||||
<view class="goods-edit-page">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="navbar">
|
||||
<view class="nav-left" @click="navBack">
|
||||
<text class="icon">←</text>
|
||||
</view>
|
||||
<view class="nav-title">编辑商品</view>
|
||||
<view class="nav-right">
|
||||
<text class="icon">···</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品图片展示区 -->
|
||||
<view class="goods-img-box">
|
||||
<image
|
||||
class="goods-img"
|
||||
:src="goodsInfo.imageUrl"
|
||||
mode="widthFix"
|
||||
@click="previewImage"
|
||||
></image>
|
||||
<view class="change-img-btn" @click="chooseImage">更换图片</view>
|
||||
</view>
|
||||
|
||||
<!-- 核心表单区域 -->
|
||||
<view class="form-container">
|
||||
<!-- 商品名称 -->
|
||||
<view class="form-item">
|
||||
<view class="form-label">
|
||||
<text class="required">*</text>名称
|
||||
</view>
|
||||
<input
|
||||
class="item-input"
|
||||
type="text"
|
||||
placeholder="请输入商品名称"
|
||||
v-model="goodsInfo.name"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 商品单位 -->
|
||||
<view class="form-item">
|
||||
<view class="form-label">商品单位</view>
|
||||
<input
|
||||
class="item-input"
|
||||
type="text"
|
||||
placeholder="请输入商品单位"
|
||||
v-model="goodsInfo.unit"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 条码+扫码按钮 -->
|
||||
<view class="form-item">
|
||||
<view class="form-label">
|
||||
<text class="required">*</text>条码
|
||||
</view>
|
||||
<view class="flex-row">
|
||||
<input
|
||||
class="item-input barcode-input"
|
||||
type="text"
|
||||
placeholder="请输入条码"
|
||||
v-model="goodsInfo.barcode"
|
||||
/>
|
||||
<view class="scan-btn" @click="scanBarcode">扫码</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 售价核心区域(修正排版) -->
|
||||
<view class="form-item">
|
||||
<view class="form-label">
|
||||
<text class="required">*</text>售价
|
||||
</view>
|
||||
<view class="flex-row">
|
||||
<input
|
||||
class="item-input price-input"
|
||||
type="digit"
|
||||
placeholder="请输入售价"
|
||||
v-model="goodsInfo.salePrice"
|
||||
/>
|
||||
<text class="price-unit">元</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 推荐价格提示与选项 -->
|
||||
<view class="price-recommend-section">
|
||||
<view class="price-tips">
|
||||
推荐价格(点击快速设置售价)<text class="tips-icon">i</text>
|
||||
</view>
|
||||
<view class="price-option-list">
|
||||
<view
|
||||
class="price-option"
|
||||
:class="{active: activePrice == item.price}"
|
||||
v-for="item in priceOptions"
|
||||
:key="item.price"
|
||||
@click="setSalePrice(item.price)"
|
||||
>
|
||||
<text class="option-text">{{ item.desc }}</text>
|
||||
<text class="option-price">{{ item.price }}元</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 库存 -->
|
||||
<view class="form-item">
|
||||
<view class="form-label">库存</view>
|
||||
<view class="flex-row">
|
||||
<input
|
||||
class="item-input"
|
||||
type="number"
|
||||
placeholder="请输入库存"
|
||||
v-model="goodsInfo.stock"
|
||||
/>
|
||||
<text class="unit-text">件</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 最近进货价 -->
|
||||
<view class="form-item">
|
||||
<view class="form-label">最近进货价</view>
|
||||
<view class="flex-row">
|
||||
<input
|
||||
class="item-input"
|
||||
type="digit"
|
||||
placeholder="请输入进货价"
|
||||
v-model="goodsInfo.buyPrice"
|
||||
/>
|
||||
<text class="unit-text">元</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 货架码 -->
|
||||
<view class="form-item">
|
||||
<view class="form-label">货架码</view>
|
||||
<input
|
||||
class="item-input"
|
||||
type="text"
|
||||
placeholder="请输入货架码"
|
||||
v-model="goodsInfo.shelfCode"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 添加更多单位按钮 -->
|
||||
<view class="add-unit-btn" @click="addUnit">+ 添加更多单位</view>
|
||||
</view>
|
||||
|
||||
<!-- 其他设置区域 -->
|
||||
<view class="settings-container">
|
||||
<view class="settings-title">其他设置</view>
|
||||
|
||||
<!-- 保质期管理 -->
|
||||
<view class="form-item">
|
||||
<text class="item-label">保质期管理</text>
|
||||
<switch
|
||||
:checked="switchStatus.expire"
|
||||
@change="toggleExpirationManagement"
|
||||
color="#F53F3F"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 保质期天数 -->
|
||||
<view class="form-item" v-if="switchStatus.expire">
|
||||
<text class="item-label">保质期天数</text>
|
||||
<view class="expiration-options">
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: expireSettings.days === 365 }"
|
||||
@tap="setExpirationDays(365)"
|
||||
>
|
||||
<text class="expiration-text">1年</text>
|
||||
<text class="expiration-days">365天</text>
|
||||
</view>
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: expireSettings.days === 270 }"
|
||||
@tap="setExpirationDays(270)"
|
||||
>
|
||||
<text class="expiration-text">9个月</text>
|
||||
<text class="expiration-days">270天</text>
|
||||
</view>
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: expireSettings.days === 180 }"
|
||||
@tap="setExpirationDays(180)"
|
||||
>
|
||||
<text class="expiration-text">6个月</text>
|
||||
<text class="expiration-days">180天</text>
|
||||
</view>
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: expireSettings.days === 240 }"
|
||||
@tap="setExpirationDays(240)"
|
||||
>
|
||||
<text class="expiration-text">8个月</text>
|
||||
<text class="expiration-days">240天</text>
|
||||
</view>
|
||||
<view
|
||||
class="expiration-item"
|
||||
:class="{ active: expireSettings.days === 300 }"
|
||||
@tap="setExpirationDays(300)"
|
||||
>
|
||||
<text class="expiration-text">10个月</text>
|
||||
<text class="expiration-days">300天</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 临期提醒天数 -->
|
||||
<view class="form-item" v-if="switchStatus.expire">
|
||||
<text class="item-label">临期提醒天数</text>
|
||||
<view class="flex-row">
|
||||
<input
|
||||
class="item-input"
|
||||
type="number"
|
||||
placeholder="请输入临期提醒天数"
|
||||
v-model="expireSettings.warnDays"
|
||||
/>
|
||||
<text class="unit-text">天</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 未成年人购买香烟提示 -->
|
||||
<view class="setting-item flex-row-between">
|
||||
<view class="setting-label flex-row">
|
||||
<text>未成年人购买香烟提示</text>
|
||||
<text class="tips-icon">i</text>
|
||||
</view>
|
||||
<view class="switch-box" @click="toggleSwitch('minor')">
|
||||
<view class="switch-bg" :class="{open: switchStatus.minor}"></view>
|
||||
<view class="switch-btn" :class="{open: switchStatus.minor}"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品分类 -->
|
||||
<view class="setting-item flex-row-between">
|
||||
<view class="setting-label">商品分类</view>
|
||||
<input
|
||||
class="item-input"
|
||||
type="text"
|
||||
placeholder="请输入商品分类"
|
||||
v-model="goodsInfo.category"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 商品品牌 -->
|
||||
<view class="setting-item flex-row-between">
|
||||
<view class="setting-label">商品品牌</view>
|
||||
<input
|
||||
class="item-input"
|
||||
type="text"
|
||||
placeholder="请输入商品品牌"
|
||||
v-model="goodsInfo.brand"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 商品编码 -->
|
||||
<view class="setting-item flex-row-between">
|
||||
<view class="setting-label">商品编码</view>
|
||||
<input
|
||||
class="item-input"
|
||||
type="text"
|
||||
placeholder="请输入商品编码"
|
||||
v-model="goodsInfo.code"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定保存按钮 -->
|
||||
<view class="bottom-submit">
|
||||
<button class="save-btn" @click="saveGoods">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProductDetail, updateProduct, updateProductWithFile } from '@/api/product'
|
||||
import { getStoreId } from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
name: 'GoodsEdit',
|
||||
data() {
|
||||
return {
|
||||
productId: null,
|
||||
goodsInfo: {
|
||||
imageUrl: 'https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/1b5ce0f17f5e4942a350d4c58f83dde9.png~tplv-a9rns2rl98-image.png?lk3s=8e244e95&rcl=202601151851433FA5618C22F61C91356F&rrcfp=dafada99&x-expires=2084698303&x-signature=mApXMrIjzYh%2B1B6IE2pHDPy9Z2U%3D',
|
||||
name: '农夫山泉饮用天然水 550ML',
|
||||
unit: '瓶',
|
||||
barcode: '6921168509256',
|
||||
salePrice: 5.5,
|
||||
stock: 1,
|
||||
buyPrice: 1.0,
|
||||
category: '默认',
|
||||
brand: '默认品牌',
|
||||
code: '1',
|
||||
shelfCode: '',
|
||||
productionDate: ''
|
||||
},
|
||||
priceOptions: [
|
||||
{ price: 2.0, desc: '92%商家卖' },
|
||||
{ price: 1.5, desc: '2%商家卖' },
|
||||
{ price: 1.8, desc: '1%商家卖' }
|
||||
],
|
||||
activePrice: 2.0,
|
||||
switchStatus: {
|
||||
expire: true,
|
||||
minor: false
|
||||
},
|
||||
expireSettings: {
|
||||
days: 240,
|
||||
warnDays: 24,
|
||||
activeOption: { label: '8个月', days: 240 }
|
||||
},
|
||||
expireOptionList: [
|
||||
{ label: '1年', days: 365 },
|
||||
{ label: '9个月', days: 270 },
|
||||
{ label: '6个月', days: 180 },
|
||||
{ label: '8个月', days: 240 },
|
||||
{ label: '10个月', days: 300 }
|
||||
]
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
console.log('编辑页面参数:', options);
|
||||
if (options.id) {
|
||||
this.productId = options.id;
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '缺少商品ID参数',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
if (this.productId) {
|
||||
this.loadProductDetail();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
warningDaysDisplay() {
|
||||
if (!this.expireSettings.days) return '';
|
||||
const days = parseInt(this.expireSettings.days);
|
||||
return days.toString().substring(0, 2);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navBack() {
|
||||
uni.navigateBack({ delta: 1 });
|
||||
},
|
||||
previewImage() {
|
||||
uni.previewImage({
|
||||
urls: [this.goodsInfo.imageUrl],
|
||||
current: this.goodsInfo.imageUrl
|
||||
});
|
||||
},
|
||||
chooseImage() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['original', 'compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
this.goodsInfo.imageUrl = res.tempFilePaths[0];
|
||||
}
|
||||
});
|
||||
},
|
||||
scanBarcode() {
|
||||
uni.scanCode({
|
||||
onlyFromCamera: true,
|
||||
scanType: ['barCode'],
|
||||
success: (res) => {
|
||||
this.goodsInfo.barcode = res.result;
|
||||
uni.showToast({ title: '扫码成功', icon: 'success' });
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({ title: '扫码取消', icon: 'none' });
|
||||
}
|
||||
});
|
||||
},
|
||||
setSalePrice(price) {
|
||||
this.activePrice = price;
|
||||
this.goodsInfo.salePrice = price;
|
||||
},
|
||||
toggleSwitch(type) {
|
||||
if (type === 'expire') {
|
||||
this.switchStatus.expire = !this.switchStatus.expire;
|
||||
} else if (type === 'minor') {
|
||||
this.switchStatus.minor = !this.switchStatus.minor;
|
||||
}
|
||||
},
|
||||
toggleExpirationManagement(e) {
|
||||
this.switchStatus.expire = e.detail.value;
|
||||
},
|
||||
setExpirationDays(days) {
|
||||
this.expireSettings.days = days;
|
||||
const warningDays = days.toString().substring(0, 2);
|
||||
this.expireSettings.warnDays = warningDays;
|
||||
uni.showToast({ title: `已设置为${days}天`, icon: 'success', duration: 1500 });
|
||||
},
|
||||
async loadProductDetail() {
|
||||
if (!this.productId) {
|
||||
uni.showToast({
|
||||
title: '缺少商品ID',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('正在获取商品详情,ID:', this.productId);
|
||||
const res = await getProductDetail(this.productId);
|
||||
|
||||
console.log('商品详情接口返回:', res);
|
||||
|
||||
if (res && res.code === 200 && res.data) {
|
||||
const data = res.data;
|
||||
|
||||
this.goodsInfo = {
|
||||
imageUrl: data.mainImage ? 'http://193.112.94.36:8081' + data.mainImage : this.goodsInfo.imageUrl,
|
||||
name: data.productName || this.goodsInfo.name,
|
||||
unit: this.goodsInfo.unit,
|
||||
barcode: data.productBarCode || this.goodsInfo.barcode,
|
||||
salePrice: data.storePrice || this.goodsInfo.salePrice,
|
||||
stock: data.stockQuantity || this.goodsInfo.stock,
|
||||
buyPrice: data.costPrice || this.goodsInfo.buyPrice,
|
||||
category: this.goodsInfo.category,
|
||||
brand: this.goodsInfo.brand,
|
||||
code: data.productCode || this.goodsInfo.code
|
||||
};
|
||||
|
||||
this.activePrice = data.storePrice || this.goodsInfo.salePrice;
|
||||
|
||||
if (data.shelfLife) {
|
||||
this.switchStatus.expire = true;
|
||||
this.expireSettings.days = data.shelfLife;
|
||||
this.expireSettings.warnDays = data.approaching || 24;
|
||||
|
||||
const days = data.shelfLife;
|
||||
const option = this.expireOptionList.find(item => item.days === days);
|
||||
if (option) {
|
||||
this.expireSettings.activeOption = option;
|
||||
}
|
||||
} else {
|
||||
this.switchStatus.expire = false;
|
||||
}
|
||||
|
||||
console.log('商品详情数据:', this.goodsInfo);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res?.msg || '获取商品详情失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取商品详情失败:', error);
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
setExpireDays(item) {
|
||||
this.expireSettings.days = item.days;
|
||||
this.expireSettings.activeOption = item;
|
||||
},
|
||||
addUnit() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '是否添加商品多单位规格?',
|
||||
success: (res) => {
|
||||
if (res.confirm) uni.showToast({ title: '添加成功', icon: 'success' });
|
||||
}
|
||||
});
|
||||
},
|
||||
async saveGoods() {
|
||||
if (!this.goodsInfo.name || !this.goodsInfo.name.trim()) {
|
||||
uni.showToast({ title: '请输入商品名称', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.goodsInfo.salePrice) {
|
||||
uni.showToast({ title: '请输入售价', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.switchStatus.expire && !this.expireSettings.days) {
|
||||
uni.showToast({ title: '请选择保质期天数', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
const storeId = getStoreId();
|
||||
if (!storeId) {
|
||||
uni.showToast({ title: '请先选择门店', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
uni.showLoading({ title: '保存中...' });
|
||||
|
||||
try {
|
||||
const formData = {
|
||||
id: this.productId,
|
||||
productName: this.goodsInfo.name,
|
||||
productBarCode: this.goodsInfo.barcode,
|
||||
storePrice: this.goodsInfo.salePrice,
|
||||
stockQuantity: this.goodsInfo.stock,
|
||||
costPrice: this.goodsInfo.buyPrice,
|
||||
productCode: this.goodsInfo.code,
|
||||
shelfCode: this.goodsInfo.shelfCode || '',
|
||||
expirationManagement: this.switchStatus.expire ? 1 : 0,
|
||||
shelfLife: this.switchStatus.expire ? this.expireSettings.days : null,
|
||||
approaching: this.switchStatus.expire ? this.expireSettings.warnDays : null,
|
||||
productionDate: this.goodsInfo.productionDate || '',
|
||||
storeId: storeId
|
||||
};
|
||||
|
||||
const res = await updateProductWithFile(this.goodsInfo.imageUrl || '', formData);
|
||||
uni.hideLoading();
|
||||
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.navBack();
|
||||
}, 2000);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.msg || '保存失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('保存商品失败:', error);
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 全局样式
|
||||
.goods-edit-page {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
.flex-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.flex-row-between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// 顶部导航栏
|
||||
.navbar {
|
||||
height: 88rpx;
|
||||
background: #E62429;
|
||||
color: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 30rpx;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
.nav-left, .nav-right {
|
||||
width: 60rpx;
|
||||
text-align: center;
|
||||
.icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
// 商品图片区域
|
||||
.goods-img-box {
|
||||
background: #FFFFFF;
|
||||
padding: 40rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
.goods-img {
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
.change-img-btn {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
padding: 12rpx 30rpx;
|
||||
border: 1px solid #E5E5E5;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 表单容器
|
||||
.form-container {
|
||||
background: #FFFFFF;
|
||||
margin-bottom: 20rpx;
|
||||
.form-item {
|
||||
padding: 30rpx;
|
||||
border-bottom: 1px solid #F0F0F0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.form-label {
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
.required {
|
||||
color: #E62429;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
}
|
||||
.form-value {
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.scan-btn {
|
||||
margin-left: 20rpx;
|
||||
padding: 8rpx 20rpx;
|
||||
background: #F5F5F5;
|
||||
border-radius: 4rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
// 推荐价格区域(修正后的排版)
|
||||
.price-recommend-section {
|
||||
padding: 0 30rpx 30rpx;
|
||||
.price-tips {
|
||||
width: 100%;
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
margin-bottom: 15rpx;
|
||||
.tips-icon {
|
||||
display: inline-block;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border: 1px solid #999;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 20rpx;
|
||||
font-size: 18rpx;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
}
|
||||
.price-option-list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
.price-option {
|
||||
flex: 1;
|
||||
background: #F5F5F5;
|
||||
border-radius: 8rpx;
|
||||
padding: 20rpx 10rpx;
|
||||
text-align: center;
|
||||
.option-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
.option-price {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
color: #E62429;
|
||||
font-weight: bold;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
&.active {
|
||||
background: #E62429;
|
||||
.option-text, .option-price {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-placeholder {
|
||||
font-size: 28rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
.right-arrow {
|
||||
font-size: 28rpx;
|
||||
color: #CCCCCC;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
.add-unit-btn {
|
||||
margin: 30rpx;
|
||||
padding: 20rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
border: 1px dashed #CCCCCC;
|
||||
border-radius: 8rpx;
|
||||
background: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
// 其他设置区域
|
||||
.settings-container {
|
||||
background: #FFFFFF;
|
||||
.settings-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1px solid #F0F0F0;
|
||||
}
|
||||
.setting-item {
|
||||
padding: 30rpx;
|
||||
border-bottom: 1px solid #F0F0F0;
|
||||
.setting-label {
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
.tips-icon {
|
||||
display: inline-block;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border: 1px solid #999;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 20rpx;
|
||||
font-size: 18rpx;
|
||||
margin-left: 5rpx;
|
||||
}
|
||||
}
|
||||
.setting-left {
|
||||
.setting-tips {
|
||||
font-size: 24rpx;
|
||||
color: #1677FF;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
}
|
||||
.setting-value {
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.switch-box {
|
||||
width: 70rpx;
|
||||
height: 40rpx;
|
||||
background: #E5E5E5;
|
||||
border-radius: 20rpx;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 4rpx;
|
||||
.switch-bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 20rpx;
|
||||
background: #E5E5E5;
|
||||
transition: all 0.3s ease;
|
||||
&.open {
|
||||
background: #E62429;
|
||||
}
|
||||
}
|
||||
.switch-btn {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
transition: all 0.3s ease;
|
||||
&.open {
|
||||
transform: translateX(30rpx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.expire-options {
|
||||
display: flex;
|
||||
padding: 0 30rpx 30rpx;
|
||||
gap: 15rpx;
|
||||
overflow-x: auto;
|
||||
.expire-option {
|
||||
flex-shrink: 0;
|
||||
background: #F5F5F5;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 8rpx;
|
||||
padding: 15rpx 20rpx;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
.option-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
.option-days {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #E62429;
|
||||
margin-top: 5rpx;
|
||||
}
|
||||
&.active {
|
||||
border-color: #E62429;
|
||||
background: #FFFFFF;
|
||||
.checked-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: -10rpx;
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
background: #E62429;
|
||||
color: #FFFFFF;
|
||||
border-radius: 50%;
|
||||
font-size: 20rpx;
|
||||
line-height: 30rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 保质期相关样式 */
|
||||
.expiration-options { display:flex;flex-wrap:wrap;gap:20rpx;flex:1;justify-content:flex-end; }
|
||||
.expiration-item { display:flex;flex-direction:column;align-items:center;padding:16rpx 20rpx;background:#f9f9f9;border-radius:8rpx;min-width:100rpx;border:2rpx solid transparent;transition:all 0.3s; }
|
||||
.expiration-item.active { background:#F53F3F;border-color:#F53F3F; }
|
||||
.expiration-item.active .expiration-text { color:#fff; }
|
||||
.expiration-item.active .expiration-days { color:#fff; }
|
||||
.expiration-text { font-size:28rpx;color:#333; }
|
||||
.expiration-days { font-size:24rpx;color:#333;margin-top:4rpx; }
|
||||
|
||||
/* 临期提醒天数样式 */
|
||||
.warning-days-display { display:flex;align-items:center;flex:1;justify-content:flex-end; }
|
||||
.warning-days-value { font-size:36rpx;color:#333;font-weight:600;margin-right:8rpx; }
|
||||
.warning-days-unit { font-size:28rpx;color:#333; }
|
||||
|
||||
/* 表单项样式 */
|
||||
.form-item {
|
||||
padding: 32rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.item-label { font-size: 32rpx;color: #333333;font-weight: 500;min-width: 160rpx; }
|
||||
.item-input { flex:1;font-size:32rpx;color:#333;text-align:right;padding:0 20rpx; }
|
||||
.item-input::placeholder { color: #999999; }
|
||||
.barcode-input { flex: 1; }
|
||||
.price-input { flex: 1; }
|
||||
.price-unit { font-size: 32rpx;color: #333;margin-left: 10rpx; }
|
||||
.unit-text { font-size: 32rpx;color: #333;margin-left: 10rpx; }
|
||||
|
||||
// 底部保存按钮
|
||||
.bottom-submit {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #FFFFFF;
|
||||
padding: 20rpx 30rpx;
|
||||
border-top: 1px solid #F0F0F0;
|
||||
z-index: 90;
|
||||
.save-btn {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
background: #E62429;
|
||||
color: #FFFFFF;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 8rpx;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,676 @@
|
|||
<template>
|
||||
<view class="input-list-page">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="navbar">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="left" size="20" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">录入清单</view>
|
||||
<view class="nav-right" @click="goRecord">
|
||||
<uni-icons type="redo" size="18" color="#fff"></uni-icons>
|
||||
<text class="record-text">记录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能操作栏 -->
|
||||
<view class="operation-bar">
|
||||
<view class="search-box">
|
||||
<uni-icons type="search" size="16" color="#999"></uni-icons>
|
||||
<input type="text" placeholder="搜索商品名称、条码" placeholder-class="search-placeholder" />
|
||||
</view>
|
||||
<button class="btn batch-import" @click="goImport">批量导入</button>
|
||||
<button class="btn no-code">无码/生鲜</button>
|
||||
</view>
|
||||
|
||||
<!-- 条码扫描区域 【修复:调整定位和层级】 -->
|
||||
<view class="scan-area">
|
||||
<view class="scan-tip">对准商品条码,自动识别</view>
|
||||
|
||||
<!-- App环境使用扫码控件 【这里的view只做占位,扫码控件是原生图层叠加】 -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="scan-view" id="barcode-view"></view>
|
||||
<view class="scan-status" v-if="isScanning">
|
||||
<view class="scan-line"></view>
|
||||
<text class="scan-status-text">正在扫描...</text>
|
||||
</view>
|
||||
<view class="scan-controls" v-if="isScanning">
|
||||
<view class="control-item" @click.stop="toggleFlash">
|
||||
<uni-icons type="flash" size="22" color="#fff"></uni-icons>
|
||||
<text class="control-text">{{ flashOn ? '关闭手电' : '开启手电' }}</text>
|
||||
</view>
|
||||
<view class="control-item" @click.stop="pauseScan">
|
||||
<uni-icons type="pause" size="22" color="#fff"></uni-icons>
|
||||
<text class="control-text">{{ scanPaused ? '继续扫码' : '暂停扫码' }}</text>
|
||||
</view>
|
||||
<view class="control-item" @click.stop="openFullscreenScan">
|
||||
<uni-icons type="scan" size="22" color="#fff"></uni-icons>
|
||||
<text class="control-text">全屏扫码</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- H5环境显示提示 -->
|
||||
<!-- #ifndef APP-PLUS -->
|
||||
<view class="h5-tip">
|
||||
<text class="tip-text">扫码功能需要在App中使用</text>
|
||||
<button class="scan-btn" @click="startScan">开始扫码</button>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
|
||||
<!-- 空状态区域 -->
|
||||
<view class="empty-area">
|
||||
<image src="/static/empty-icon.png" mode="widthFix" class="empty-icon"></image>
|
||||
<text class="empty-title">暂未录入商品</text>
|
||||
<text class="empty-desc">请在上方选择商品录入方式</text>
|
||||
<uni-icons type="arrowup" size="14" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
flashOn: false,
|
||||
scanPaused: false,
|
||||
isScanning: false,
|
||||
barcodeInstance: null
|
||||
};
|
||||
},
|
||||
// ✅ 修复核心:用mounted初始化,页面DOM渲染完成后再创建扫码控件
|
||||
mounted() {
|
||||
// #ifdef APP-PLUS
|
||||
console.log('=== 扫码页面 mounted ===');
|
||||
this.initScan();
|
||||
// #endif
|
||||
},
|
||||
onShow() {
|
||||
// #ifdef APP-PLUS
|
||||
console.log('=== 扫码页面 onShow ===');
|
||||
// 每次页面显示时都检查权限并初始化(处理从设置返回的情况)
|
||||
this.initScan();
|
||||
// #endif
|
||||
},
|
||||
onUnload() {
|
||||
this.destroyScan(); // 统一销毁扫码控件
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
this.destroyScan(); // 返回时销毁
|
||||
uni.navigateBack({ delta: 1 });
|
||||
},
|
||||
|
||||
goRecord() {
|
||||
uni.showToast({ title: '记录功能开发中', icon: 'none' });
|
||||
},
|
||||
|
||||
goImport() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/Import /Import '
|
||||
});
|
||||
},
|
||||
|
||||
// ✅ 新增:统一的初始化方法(处理权限和扫码初始化)
|
||||
initScan() {
|
||||
console.log('开始初始化扫码流程...');
|
||||
console.log('当前平台:', uni.getSystemInfoSync().platform);
|
||||
console.log('plus对象是否存在:', !!plus);
|
||||
console.log('plus.barcode是否存在:', !!(plus && plus.barcode));
|
||||
|
||||
// 先申请相机权限,再初始化扫码
|
||||
this.requestCameraAuth().then(() => {
|
||||
console.log('相机权限已获取,开始初始化扫码');
|
||||
this.initBarcodeScan();
|
||||
}).catch(err => {
|
||||
console.error('相机权限获取失败:', err);
|
||||
|
||||
// 显示友好的权限提示
|
||||
uni.showModal({
|
||||
title: '权限提示',
|
||||
content: '需要相机权限才能使用扫码功能\n\n请点击"去设置"开启相机权限',
|
||||
confirmText: '去设置',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 跳转到系统设置页面
|
||||
if (uni.getSystemInfoSync().platform === 'android') {
|
||||
const Intent = plus.android.importClass('android.content.Intent');
|
||||
const Settings = plus.android.importClass('android.provider.Settings');
|
||||
const Uri = plus.android.importClass('android.net.Uri');
|
||||
|
||||
const intent = new Intent();
|
||||
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
const uri = Uri.fromParts('package', plus.android.runtimeActivity.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
|
||||
plus.android.runtimeMainActivity().startActivity(intent);
|
||||
} else {
|
||||
// iOS 跳转到设置
|
||||
plus.runtime.openURL('app-settings:');
|
||||
}
|
||||
} else {
|
||||
// 用户取消,返回上一页
|
||||
uni.navigateBack({ delta: 1 });
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// ✅ 新增:统一销毁扫码控件,防止内存泄漏
|
||||
destroyScan() {
|
||||
if (this.barcodeInstance) {
|
||||
this.barcodeInstance.close();
|
||||
this.barcodeInstance = null;
|
||||
}
|
||||
this.isScanning = false;
|
||||
this.scanPaused = false;
|
||||
this.flashOn = false;
|
||||
},
|
||||
|
||||
// ✅ 新增:动态申请相机权限(支持 Android 和 iOS)
|
||||
requestCameraAuth() {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const platform = uni.getSystemInfoSync().platform;
|
||||
console.log('当前平台:', platform);
|
||||
|
||||
if (platform === 'android') {
|
||||
// Android 平台权限申请
|
||||
console.log('Android 平台,开始申请相机权限...');
|
||||
|
||||
// 先检查是否已有权限
|
||||
const main = plus.android.runtimeMainActivity();
|
||||
const Permission = plus.android.importClass('android.Manifest.permission');
|
||||
const PackageManager = plus.android.importClass('android.content.pm.PackageManager');
|
||||
|
||||
const permission = Permission.CAMERA;
|
||||
const result = main.checkSelfPermission(permission);
|
||||
|
||||
console.log('Android 权限检查结果:', result);
|
||||
|
||||
if (result === PackageManager.PERMISSION_GRANTED) {
|
||||
console.log('Android 已有相机权限');
|
||||
resolve();
|
||||
} else {
|
||||
console.log('Android 需要申请相机权限');
|
||||
plus.android.requestPermissions(
|
||||
['android.permission.CAMERA'],
|
||||
(res) => {
|
||||
console.log('Android 权限请求回调:', res);
|
||||
if (res && res.length > 0) {
|
||||
const authResult = res[0].granted;
|
||||
console.log('Android 相机权限授予结果:', authResult);
|
||||
if (authResult) {
|
||||
resolve();
|
||||
} else {
|
||||
reject('用户拒绝了相机权限');
|
||||
}
|
||||
} else {
|
||||
reject('权限请求返回异常');
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
console.error('Android 权限请求失败:', err);
|
||||
reject(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else if (platform === 'ios') {
|
||||
// iOS 平台权限申请
|
||||
console.log('iOS 平台,相机权限在使用时自动申请');
|
||||
|
||||
// iOS 的相机权限通常在第一次使用相机时自动申请
|
||||
// 我们可以直接尝试初始化扫码,如果权限被拒绝会触发系统弹窗
|
||||
resolve();
|
||||
} else {
|
||||
console.log('非移动端平台,跳过权限申请');
|
||||
resolve();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('权限申请异常:', error);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// ✅ 修复所有参数错误的扫码初始化核心方法
|
||||
initBarcodeScan() {
|
||||
if (!plus) {
|
||||
console.log('plus 对象不存在,无法初始化扫码');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果已有扫码实例,先销毁再重新创建
|
||||
if (this.barcodeInstance) {
|
||||
console.log('已存在扫码实例,先销毁再重新创建');
|
||||
this.destroyScan();
|
||||
}
|
||||
|
||||
console.log('开始初始化扫码控件');
|
||||
this.isScanning = true;
|
||||
this.scanPaused = false;
|
||||
|
||||
// 获取屏幕宽高和系统信息,适配所有机型【无单位纯数字,核心修复】
|
||||
const sysInfo = uni.getSystemInfoSync();
|
||||
const statusBarHeight = sysInfo.statusBarHeight || 0;
|
||||
const navBarHeight = 44;
|
||||
const totalNavBarHeight = statusBarHeight + navBarHeight;
|
||||
|
||||
const scanWidth = sysInfo.windowWidth - 30; // 左右各留15px边距
|
||||
const scanHeight = sysInfo.windowWidth * 0.7; // 宽高比适配,扫码框更美观
|
||||
const scanTop = totalNavBarHeight + 80; // 动态计算顶部距离(导航栏+操作栏+间距)
|
||||
|
||||
console.log('系统信息:', {
|
||||
statusBarHeight,
|
||||
navBarHeight,
|
||||
totalNavBarHeight,
|
||||
scanTop,
|
||||
scanWidth,
|
||||
scanHeight
|
||||
});
|
||||
|
||||
// 创建扫码控件 【修复:所有尺寸参数为纯数字,无px】
|
||||
const barcode = plus.barcode.create('barcode',
|
||||
[plus.barcode.CODE_128, plus.barcode.EAN_13, plus.barcode.EAN_8, plus.barcode.QR], // 增加常用码制,识别更广
|
||||
{
|
||||
top: scanTop,
|
||||
left: 15,
|
||||
width: scanWidth,
|
||||
height: scanHeight,
|
||||
scanbarColor: '#e60012', // 扫码线颜色
|
||||
background: '#000',
|
||||
frameColor: '#e60012', // 扫码框颜色
|
||||
scanbarRate: 2, // 扫码线速度
|
||||
scanbarStyle: 'style-radar', // 扫码线样式
|
||||
conserve: true, // 保持扫码状态
|
||||
filename: '_doc/barcode/' // 保存路径
|
||||
}
|
||||
);
|
||||
|
||||
console.log('扫码控件创建成功');
|
||||
|
||||
// 扫码成功回调
|
||||
barcode.onmarked = (type, result) => {
|
||||
if (!result) return; // 过滤空结果
|
||||
console.log('识别成功:', result);
|
||||
this.isScanning = false;
|
||||
uni.showModal({
|
||||
title: '扫码成功',
|
||||
content: `获取到的编码号:${result}`,
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
// ✅ 修复:扫码成功后完整重置状态+继续扫码
|
||||
this.isScanning = true;
|
||||
this.resumeScan();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 扫码错误回调
|
||||
barcode.onerror = (error) => {
|
||||
console.error('扫码错误:', error);
|
||||
uni.showToast({ title: '扫码异常,请重试', icon: 'none' });
|
||||
};
|
||||
|
||||
// 挂载到当前页面
|
||||
const currentWebview = plus.webview.currentWebview();
|
||||
console.log('当前webview:', currentWebview);
|
||||
console.log('扫码控件配置:', {
|
||||
top: scanTop,
|
||||
left: 15,
|
||||
width: scanWidth,
|
||||
height: scanHeight
|
||||
});
|
||||
|
||||
currentWebview.append(barcode);
|
||||
console.log('✅ 扫码控件已成功挂载到webview');
|
||||
|
||||
// 启动扫码并添加回调处理
|
||||
barcode.start((result) => {
|
||||
console.log('扫码启动回调:', result);
|
||||
if (result.code === 0) {
|
||||
console.log('✅ 扫码已成功启动,相机画面应该显示');
|
||||
this.barcodeInstance = barcode;
|
||||
|
||||
// 延迟检查扫码控件状态
|
||||
setTimeout(() => {
|
||||
console.log('扫码控件状态检查:', {
|
||||
exists: !!this.barcodeInstance,
|
||||
isScanning: this.isScanning
|
||||
});
|
||||
}, 1000);
|
||||
} else {
|
||||
console.error('❌ 扫码启动失败:', result.message);
|
||||
uni.showModal({
|
||||
title: '扫码启动失败',
|
||||
content: `错误信息: ${result.message || '未知错误'}\n请检查相机权限是否已开启`,
|
||||
showCancel: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
console.log('扫码控件已挂载,正在启动...');
|
||||
},
|
||||
|
||||
// H5环境扫码(不变,兼容用)
|
||||
startScan() {
|
||||
uni.scanCode({
|
||||
success: (res) => {
|
||||
uni.showModal({ title: '扫码成功', content: `获取到的编码号:${res.result}`, showCancel: false });
|
||||
},
|
||||
fail: () => uni.showToast({ title: '扫码失败', icon: 'none' })
|
||||
});
|
||||
},
|
||||
|
||||
// 暂停/继续扫码(逻辑优化)
|
||||
pauseScan() {
|
||||
if (!this.barcodeInstance) return;
|
||||
this.scanPaused = !this.scanPaused;
|
||||
this.scanPaused ? this.barcodeInstance.pause() : this.barcodeInstance.resume();
|
||||
uni.showToast({ title: this.scanPaused ? '扫码已暂停' : '扫码已继续', icon: 'none' });
|
||||
},
|
||||
|
||||
// 恢复扫码
|
||||
resumeScan() {
|
||||
if (this.barcodeInstance && this.scanPaused) {
|
||||
this.barcodeInstance.resume();
|
||||
this.scanPaused = false;
|
||||
this.isScanning = true;
|
||||
}
|
||||
},
|
||||
|
||||
// 手电开关(不变,逻辑正常)
|
||||
toggleFlash() {
|
||||
if (!this.barcodeInstance) return;
|
||||
this.flashOn = !this.flashOn;
|
||||
this.barcodeInstance.setFlash(this.flashOn);
|
||||
uni.showToast({ title: this.flashOn ? '手电已开启' : '手电已关闭', icon: 'none' });
|
||||
},
|
||||
|
||||
// 打开全屏扫码(使用 uni.scanCode 原生API)
|
||||
openFullscreenScan() {
|
||||
console.log('打开全屏扫码');
|
||||
|
||||
// 暂停嵌入式扫码
|
||||
if (this.barcodeInstance) {
|
||||
this.barcodeInstance.pause();
|
||||
}
|
||||
|
||||
// 调用 uniapp 原生扫码API,核心配置
|
||||
uni.scanCode({
|
||||
scanType: ['barCode', 'qrCode'], // 支持条形码和二维码
|
||||
onlyFromCamera: true, // 只使用相机,不从相册选择
|
||||
success: (res) => {
|
||||
console.log('全屏扫码成功:', res);
|
||||
|
||||
// 显示扫码结果
|
||||
uni.showModal({
|
||||
title: '扫码成功',
|
||||
content: `获取到的编码号:${res.result}`,
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
// 恢复嵌入式扫码
|
||||
this.resumeScan();
|
||||
}
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('全屏扫码失败:', err);
|
||||
|
||||
// 恢复嵌入式扫码
|
||||
this.resumeScan();
|
||||
|
||||
if (err.errMsg !== 'scanCode:fail cancel') {
|
||||
uni.showToast({
|
||||
title: '扫码失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.input-list-page {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
width: 100%;
|
||||
height: calc(var(--status-bar-height) + 44px);
|
||||
background-color: #e60012;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.nav-left, .nav-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.record-text {
|
||||
font-size: 14px;
|
||||
margin-left: 5px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.operation-bar {
|
||||
margin-top: var(--status-bar-height) + 44px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
flex: 1;
|
||||
height: 32px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #eee;
|
||||
color: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ✅ 修复:扫码区域样式优化,适配原生扫码图层 */
|
||||
.scan-area {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
min-height: 350px;
|
||||
margin-top: 10px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.scan-tip {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: rgba(0,0,0,0.7);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.scan-view {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background-color: #000;
|
||||
border: 2px solid #e60012;
|
||||
}
|
||||
|
||||
.scan-status {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9;
|
||||
pointer-events: none; /* 不遮挡扫码识别 */
|
||||
}
|
||||
|
||||
.scan-line {
|
||||
width: 80%;
|
||||
height: 2px;
|
||||
background-color: #e60012;
|
||||
animation: scanMove 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes scanMove {
|
||||
0% { transform: translateY(0); opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
100% { transform: translateY(250px); opacity: 0; }
|
||||
}
|
||||
|
||||
.scan-status-text {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.scan-controls {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.control-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
transition: all 0.3s ease;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.control-item:active {
|
||||
background-color: rgba(230, 0, 18, 0.6);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.control-text {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.h5-tip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.scan-btn {
|
||||
padding: 12px 30px;
|
||||
background-color: #e60012;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.empty-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-bottom: 15px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
<template>
|
||||
<view class="normal-login-container">
|
||||
<view class="logo-content align-center justify-center flex">
|
||||
<image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">
|
||||
</image>
|
||||
<text class="title">若依移动端登录</text>
|
||||
</view>
|
||||
<view class="login-form-content">
|
||||
<view class="input-item flex align-center">
|
||||
<view class="iconfont icon-user icon"></view>
|
||||
<input v-model="loginForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" />
|
||||
</view>
|
||||
<view class="input-item flex align-center">
|
||||
<view class="iconfont icon-password icon"></view>
|
||||
<input v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
|
||||
</view>
|
||||
<view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
|
||||
<view class="iconfont icon-code icon"></view>
|
||||
<input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
|
||||
<view class="login-code">
|
||||
<image :src="codeUrl" @click="getCode" class="login-code-img"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="action-btn">
|
||||
<button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
|
||||
</view>
|
||||
<view class="reg text-center" v-if="register">
|
||||
<text class="text-grey1">没有账号?</text>
|
||||
<text @click="handleUserRegister" class="text-blue">立即注册</text>
|
||||
</view>
|
||||
<view class="xieyi text-center">
|
||||
<text class="text-grey1">登录即代表同意</text>
|
||||
<text @click="handleUserAgrement" class="text-blue">《用户协议》</text>
|
||||
<text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCodeImg } from '@/api/login'
|
||||
import { getToken, setUserId } from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
codeUrl: "",
|
||||
captchaEnabled: true,
|
||||
// 用户注册开关
|
||||
register: false,
|
||||
globalConfig: getApp().globalData.config,
|
||||
loginForm: {
|
||||
username: "admin",
|
||||
password: "123456",
|
||||
code: "",
|
||||
uuid: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getCode()
|
||||
},
|
||||
onLoad() {
|
||||
//#ifdef H5
|
||||
if (getToken()) {
|
||||
this.$tab.reLaunch('/pages/index')
|
||||
}
|
||||
//#endif
|
||||
},
|
||||
methods: {
|
||||
// 用户注册
|
||||
handleUserRegister() {
|
||||
this.$tab.redirectTo(`/pages/register`)
|
||||
},
|
||||
// 隐私协议
|
||||
handlePrivacy() {
|
||||
let site = this.globalConfig.appInfo.agreements[0]
|
||||
this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
|
||||
},
|
||||
// 用户协议
|
||||
handleUserAgrement() {
|
||||
let site = this.globalConfig.appInfo.agreements[1]
|
||||
this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
|
||||
},
|
||||
// 获取图形验证码
|
||||
getCode() {
|
||||
getCodeImg().then(res => {
|
||||
this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled
|
||||
if (this.captchaEnabled) {
|
||||
this.codeUrl = 'data:image/gif;base64,' + res.img
|
||||
this.loginForm.uuid = res.uuid
|
||||
}
|
||||
})
|
||||
},
|
||||
// 登录方法
|
||||
async handleLogin() {
|
||||
if (this.loginForm.username === "") {
|
||||
this.$modal.msgError("请输入账号")
|
||||
} else if (this.loginForm.password === "") {
|
||||
this.$modal.msgError("请输入密码")
|
||||
} else if (this.loginForm.code === "" && this.captchaEnabled) {
|
||||
this.$modal.msgError("请输入验证码")
|
||||
} else {
|
||||
this.$modal.loading("登录中,请耐心等待...")
|
||||
this.pwdLogin()
|
||||
}
|
||||
},
|
||||
// 密码登录
|
||||
async pwdLogin() {
|
||||
this.$store.dispatch('Login', this.loginForm).then(() => {
|
||||
this.$modal.closeLoading()
|
||||
this.loginSuccess()
|
||||
}).catch(() => {
|
||||
if (this.captchaEnabled) {
|
||||
this.getCode()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 登录成功后,处理函数
|
||||
loginSuccess(result) {
|
||||
// 设置用户信息(包含用户ID)
|
||||
this.$store.dispatch('GetInfo').then(res => {
|
||||
console.log('用户信息:', res);
|
||||
|
||||
// 获取并存储用户ID
|
||||
if (res.user && res.user.userId) {
|
||||
setUserId(res.user.userId);
|
||||
console.log('用户ID已存储:', res.user.userId);
|
||||
}
|
||||
|
||||
// 跳转到门店选择页面
|
||||
uni.redirectTo({
|
||||
url: '/pages/storeSelect/storeSelect'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.normal-login-container {
|
||||
width: 100%;
|
||||
|
||||
.logo-content {
|
||||
width: 100%;
|
||||
font-size: 21px;
|
||||
text-align: center;
|
||||
padding-top: 15%;
|
||||
|
||||
image {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form-content {
|
||||
text-align: center;
|
||||
margin: 20px auto;
|
||||
margin-top: 15%;
|
||||
width: 80%;
|
||||
|
||||
.input-item {
|
||||
margin: 20px auto;
|
||||
background-color: #f5f6f7;
|
||||
height: 45px;
|
||||
border-radius: 20px;
|
||||
|
||||
.icon {
|
||||
font-size: 38rpx;
|
||||
margin-left: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
margin-top: 40px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.reg {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.xieyi {
|
||||
color: #333;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.login-code {
|
||||
height: 38px;
|
||||
float: right;
|
||||
|
||||
.login-code-img {
|
||||
height: 38px;
|
||||
position: absolute;
|
||||
margin-left: 10px;
|
||||
width: 200rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<template>
|
||||
<view class="menu">
|
||||
<!-- 顶部用户信息 -->
|
||||
<view class="user-info">
|
||||
<image class="avatar" src="/static/avatar.png"></image>
|
||||
<text class="name">个人店</text>
|
||||
<text class="sub">商店智能店</text>
|
||||
<text class="stats">已上线50万会员 | 管理员</text>
|
||||
</view>
|
||||
|
||||
<!-- 企业管理 -->
|
||||
<view class="section">
|
||||
<text class="section-title">企业管理</text>
|
||||
<view class="section-item">
|
||||
<text class="item-label">企业名称</text>
|
||||
<text class="item-value">总部/管理员</text>
|
||||
</view>
|
||||
<view class="section-item">
|
||||
<text class="item-label">创建企业组织</text>
|
||||
<text class="item-label">创建店铺</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 其他菜单项 -->
|
||||
<view class="menu-list">
|
||||
<view class="menu-item" @click="goToPage('shop')">
|
||||
<text>店</text>
|
||||
<text>202 今日进店</text>
|
||||
</view>
|
||||
<view class="menu-item" @click="goToPage('product')">
|
||||
<text>商品管理</text>
|
||||
</view>
|
||||
<!-- 其他菜单项... -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
goToPage(page) {
|
||||
uni.navigateTo({ url: `/pages/${page}/${page}` })
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.menu {
|
||||
padding: 40rpx;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sub {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin: 10rpx 0;
|
||||
}
|
||||
|
||||
.stats {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin: 30rpx 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.section-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 15rpx 0;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 25rpx 0;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<view class="about-container">
|
||||
<view class="header-section text-center">
|
||||
<image style="width: 150rpx;height: 150rpx;" src="/static/logo200.png" mode="widthFix">
|
||||
</image>
|
||||
<uni-title type="h2" title="若依移动端"></uni-title>
|
||||
</view>
|
||||
|
||||
<view class="content-section">
|
||||
<view class="menu-list">
|
||||
<view class="list-cell list-cell-arrow">
|
||||
<view class="menu-item-box">
|
||||
<view>版本信息</view>
|
||||
<view class="text-right">v{{version}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow">
|
||||
<view class="menu-item-box">
|
||||
<view>官方邮箱</view>
|
||||
<view class="text-right">ruoyi@xx.com</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow">
|
||||
<view class="menu-item-box">
|
||||
<view>服务热线</view>
|
||||
<view class="text-right">400-999-9999</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow">
|
||||
<view class="menu-item-box">
|
||||
<view>公司网站</view>
|
||||
<view class="text-right">
|
||||
<uni-link :href="url" :text="url" showUnderLine="false"></uni-link>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="copyright">
|
||||
<view>Copyright © 2025 ruoyi.vip All Rights Reserved.</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
url: getApp().globalData.config.appInfo.site_url,
|
||||
version: getApp().globalData.config.appInfo.version
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
margin-top: 50rpx;
|
||||
text-align: center;
|
||||
line-height: 60rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.header-section {
|
||||
display: flex;
|
||||
padding: 30rpx 0 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,618 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="page-body uni-content-info">
|
||||
<view class='cropper-content'>
|
||||
<view v-if="isShowImg" class="uni-corpper" :style="'width:'+cropperInitW+'px;height:'+cropperInitH+'px;background:#000'">
|
||||
<view class="uni-corpper-content" :style="'width:'+cropperW+'px;height:'+cropperH+'px;left:'+cropperL+'px;top:'+cropperT+'px'">
|
||||
<image :src="imageSrc" :style="'width:'+cropperW+'px;height:'+cropperH+'px'"></image>
|
||||
<view class="uni-corpper-crop-box" @touchstart.stop="contentStartMove" @touchmove.stop="contentMoveing" @touchend.stop="contentTouchEnd"
|
||||
:style="'left:'+cutL+'px;top:'+cutT+'px;right:'+cutR+'px;bottom:'+cutB+'px'">
|
||||
<view class="uni-cropper-view-box">
|
||||
<view class="uni-cropper-dashed-h"></view>
|
||||
<view class="uni-cropper-dashed-v"></view>
|
||||
<view class="uni-cropper-line-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-line-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-line-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-line-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-tr" data-drag="topTight"></view>
|
||||
<view class="uni-cropper-point point-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-rb" data-drag="rightBottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-bl" data-drag="bottomLeft"></view>
|
||||
<view class="uni-cropper-point point-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
|
||||
<view class="uni-cropper-point point-lt" data-drag="leftTop"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class='cropper-config'>
|
||||
<button type="primary reverse" @click="getImage" style='margin-top: 30rpx;'> 选择头像 </button>
|
||||
<button type="warn" @click="getImageInfo" style='margin-top: 30rpx;'> 提交 </button>
|
||||
</view>
|
||||
<canvas canvas-id="myCanvas" :style="'position:absolute;border: 1px solid red; width:'+imageW+'px;height:'+imageH+'px;top:-9999px;left:-9999px;'"></canvas>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from '@/config'
|
||||
import store from "@/store"
|
||||
import { uploadAvatar } from "@/api/system/user"
|
||||
|
||||
const baseUrl = config.baseUrl
|
||||
let sysInfo = uni.getSystemInfoSync()
|
||||
let SCREEN_WIDTH = sysInfo.screenWidth
|
||||
let PAGE_X, // 手按下的x位置
|
||||
PAGE_Y, // 手按下y的位置
|
||||
PR = sysInfo.pixelRatio, // dpi
|
||||
T_PAGE_X, // 手移动的时候x的位置
|
||||
T_PAGE_Y, // 手移动的时候Y的位置
|
||||
CUT_L, // 初始化拖拽元素的left值
|
||||
CUT_T, // 初始化拖拽元素的top值
|
||||
CUT_R, // 初始化拖拽元素的
|
||||
CUT_B, // 初始化拖拽元素的
|
||||
CUT_W, // 初始化拖拽元素的宽度
|
||||
CUT_H, // 初始化拖拽元素的高度
|
||||
IMG_RATIO, // 图片比例
|
||||
IMG_REAL_W, // 图片实际的宽度
|
||||
IMG_REAL_H, // 图片实际的高度
|
||||
DRAFG_MOVE_RATIO = 1, //移动时候的比例,
|
||||
INIT_DRAG_POSITION = 100, // 初始化屏幕宽度和裁剪区域的宽度之差,用于设置初始化裁剪的宽度
|
||||
DRAW_IMAGE_W = sysInfo.screenWidth // 设置生成的图片宽度
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 页面的初始数据
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
imageSrc: store.getters.avatar,
|
||||
isShowImg: false,
|
||||
// 初始化的宽高
|
||||
cropperInitW: SCREEN_WIDTH,
|
||||
cropperInitH: SCREEN_WIDTH,
|
||||
// 动态的宽高
|
||||
cropperW: SCREEN_WIDTH,
|
||||
cropperH: SCREEN_WIDTH,
|
||||
// 动态的left top值
|
||||
cropperL: 0,
|
||||
cropperT: 0,
|
||||
|
||||
transL: 0,
|
||||
transT: 0,
|
||||
|
||||
// 图片缩放值
|
||||
scaleP: 0,
|
||||
imageW: 0,
|
||||
imageH: 0,
|
||||
|
||||
// 裁剪框 宽高
|
||||
cutL: 0,
|
||||
cutT: 0,
|
||||
cutB: SCREEN_WIDTH,
|
||||
cutR: '100%',
|
||||
qualityWidth: DRAW_IMAGE_W,
|
||||
innerAspectRadio: DRAFG_MOVE_RATIO
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 生命周期函数--监听页面初次渲染完成
|
||||
*/
|
||||
onReady: function () {
|
||||
this.loadImage()
|
||||
},
|
||||
methods: {
|
||||
setData: function (obj) {
|
||||
let that = this
|
||||
Object.keys(obj).forEach(function (key) {
|
||||
that.$set(that.$data, key, obj[key])
|
||||
})
|
||||
},
|
||||
getImage: function () {
|
||||
var _this = this
|
||||
uni.chooseImage({
|
||||
success: function (res) {
|
||||
_this.setData({
|
||||
imageSrc: res.tempFilePaths[0],
|
||||
})
|
||||
_this.loadImage()
|
||||
},
|
||||
})
|
||||
},
|
||||
loadImage: function () {
|
||||
var _this = this
|
||||
|
||||
uni.getImageInfo({
|
||||
src: _this.imageSrc,
|
||||
success: function success(res) {
|
||||
IMG_RATIO = 1 / 1
|
||||
if (IMG_RATIO >= 1) {
|
||||
IMG_REAL_W = SCREEN_WIDTH
|
||||
IMG_REAL_H = SCREEN_WIDTH / IMG_RATIO
|
||||
} else {
|
||||
IMG_REAL_W = SCREEN_WIDTH * IMG_RATIO
|
||||
IMG_REAL_H = SCREEN_WIDTH
|
||||
}
|
||||
let minRange = IMG_REAL_W > IMG_REAL_H ? IMG_REAL_W : IMG_REAL_H
|
||||
INIT_DRAG_POSITION = minRange > INIT_DRAG_POSITION ? INIT_DRAG_POSITION : minRange
|
||||
// 根据图片的宽高显示不同的效果 保证图片可以正常显示
|
||||
if (IMG_RATIO >= 1) {
|
||||
let cutT = Math.ceil((SCREEN_WIDTH / IMG_RATIO - (SCREEN_WIDTH / IMG_RATIO - INIT_DRAG_POSITION)) / 2)
|
||||
let cutB = cutT
|
||||
let cutL = Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH + INIT_DRAG_POSITION) / 2)
|
||||
let cutR = cutL
|
||||
_this.setData({
|
||||
cropperW: SCREEN_WIDTH,
|
||||
cropperH: SCREEN_WIDTH / IMG_RATIO,
|
||||
// 初始化left right
|
||||
cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
|
||||
cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2),
|
||||
cutL: cutL,
|
||||
cutT: cutT,
|
||||
cutR: cutR,
|
||||
cutB: cutB,
|
||||
// 图片缩放值
|
||||
imageW: IMG_REAL_W,
|
||||
imageH: IMG_REAL_H,
|
||||
scaleP: IMG_REAL_W / SCREEN_WIDTH,
|
||||
qualityWidth: DRAW_IMAGE_W,
|
||||
innerAspectRadio: IMG_RATIO
|
||||
})
|
||||
} else {
|
||||
let cutL = Math.ceil((SCREEN_WIDTH * IMG_RATIO - (SCREEN_WIDTH * IMG_RATIO)) / 2)
|
||||
let cutR = cutL
|
||||
let cutT = Math.ceil((SCREEN_WIDTH - INIT_DRAG_POSITION) / 2)
|
||||
let cutB = cutT
|
||||
_this.setData({
|
||||
cropperW: SCREEN_WIDTH * IMG_RATIO,
|
||||
cropperH: SCREEN_WIDTH,
|
||||
// 初始化left right
|
||||
cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2),
|
||||
cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
|
||||
|
||||
cutL: cutL,
|
||||
cutT: cutT,
|
||||
cutR: cutR,
|
||||
cutB: cutB,
|
||||
// 图片缩放值
|
||||
imageW: IMG_REAL_W,
|
||||
imageH: IMG_REAL_H,
|
||||
scaleP: IMG_REAL_W / SCREEN_WIDTH,
|
||||
qualityWidth: DRAW_IMAGE_W,
|
||||
innerAspectRadio: IMG_RATIO
|
||||
})
|
||||
}
|
||||
_this.setData({
|
||||
isShowImg: true
|
||||
})
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 拖动时候触发的touchStart事件
|
||||
contentStartMove(e) {
|
||||
PAGE_X = e.touches[0].pageX
|
||||
PAGE_Y = e.touches[0].pageY
|
||||
},
|
||||
|
||||
// 拖动时候触发的touchMove事件
|
||||
contentMoveing(e) {
|
||||
var _this = this
|
||||
var dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||||
var dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||||
// 左移
|
||||
if (dragLengthX > 0) {
|
||||
if (this.cutL - dragLengthX < 0) dragLengthX = this.cutL
|
||||
} else {
|
||||
if (this.cutR + dragLengthX < 0) dragLengthX = -this.cutR
|
||||
}
|
||||
|
||||
if (dragLengthY > 0) {
|
||||
if (this.cutT - dragLengthY < 0) dragLengthY = this.cutT
|
||||
} else {
|
||||
if (this.cutB + dragLengthY < 0) dragLengthY = -this.cutB
|
||||
}
|
||||
this.setData({
|
||||
cutL: this.cutL - dragLengthX,
|
||||
cutT: this.cutT - dragLengthY,
|
||||
cutR: this.cutR + dragLengthX,
|
||||
cutB: this.cutB + dragLengthY
|
||||
})
|
||||
|
||||
PAGE_X = e.touches[0].pageX
|
||||
PAGE_Y = e.touches[0].pageY
|
||||
},
|
||||
|
||||
contentTouchEnd() {
|
||||
|
||||
},
|
||||
|
||||
// 获取图片
|
||||
getImageInfo() {
|
||||
var _this = this
|
||||
uni.showLoading({
|
||||
title: '图片生成中...',
|
||||
})
|
||||
// 将图片写入画布
|
||||
const ctx = uni.createCanvasContext('myCanvas')
|
||||
ctx.drawImage(_this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H)
|
||||
ctx.draw(true, () => {
|
||||
// 获取画布要裁剪的位置和宽度 均为百分比 * 画布中图片的宽度 保证了在微信小程序中裁剪的图片模糊 位置不对的问题 canvasT = (_this.cutT / _this.cropperH) * (_this.imageH / pixelRatio)
|
||||
var canvasW = ((_this.cropperW - _this.cutL - _this.cutR) / _this.cropperW) * IMG_REAL_W
|
||||
var canvasH = ((_this.cropperH - _this.cutT - _this.cutB) / _this.cropperH) * IMG_REAL_H
|
||||
var canvasL = (_this.cutL / _this.cropperW) * IMG_REAL_W
|
||||
var canvasT = (_this.cutT / _this.cropperH) * IMG_REAL_H
|
||||
uni.canvasToTempFilePath({
|
||||
x: canvasL,
|
||||
y: canvasT,
|
||||
width: canvasW,
|
||||
height: canvasH,
|
||||
destWidth: canvasW,
|
||||
destHeight: canvasH,
|
||||
quality: 0.5,
|
||||
canvasId: 'myCanvas',
|
||||
success: function (res) {
|
||||
uni.hideLoading()
|
||||
let data = {name: 'avatarfile', filePath: res.tempFilePath}
|
||||
uploadAvatar(data).then(response => {
|
||||
store.commit('SET_AVATAR', baseUrl + response.imgUrl)
|
||||
uni.showToast({ title: "修改成功", icon: 'success' })
|
||||
uni.navigateBack()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// 设置大小的时候触发的touchStart事件
|
||||
dragStart(e) {
|
||||
T_PAGE_X = e.touches[0].pageX
|
||||
T_PAGE_Y = e.touches[0].pageY
|
||||
CUT_L = this.cutL
|
||||
CUT_R = this.cutR
|
||||
CUT_B = this.cutB
|
||||
CUT_T = this.cutT
|
||||
},
|
||||
|
||||
// 设置大小的时候触发的touchMove事件
|
||||
dragMove(e) {
|
||||
var _this = this
|
||||
var dragType = e.target.dataset.drag
|
||||
switch (dragType) {
|
||||
case 'right':
|
||||
var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||||
if (CUT_R + dragLength < 0) dragLength = -CUT_R
|
||||
this.setData({
|
||||
cutR: CUT_R + dragLength
|
||||
})
|
||||
break
|
||||
case 'left':
|
||||
var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||||
if (CUT_L - dragLength < 0) dragLength = CUT_L
|
||||
if ((CUT_L - dragLength) > (this.cropperW - this.cutR)) dragLength = CUT_L - (this.cropperW - this.cutR)
|
||||
this.setData({
|
||||
cutL: CUT_L - dragLength
|
||||
})
|
||||
break
|
||||
case 'top':
|
||||
var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||||
if (CUT_T - dragLength < 0) dragLength = CUT_T
|
||||
if ((CUT_T - dragLength) > (this.cropperH - this.cutB)) dragLength = CUT_T - (this.cropperH - this.cutB)
|
||||
this.setData({
|
||||
cutT: CUT_T - dragLength
|
||||
})
|
||||
break
|
||||
case 'bottom':
|
||||
var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||||
if (CUT_B + dragLength < 0) dragLength = -CUT_B
|
||||
this.setData({
|
||||
cutB: CUT_B + dragLength
|
||||
})
|
||||
break
|
||||
case 'rightBottom':
|
||||
var dragLengthX = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||||
var dragLengthY = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||||
|
||||
if (CUT_B + dragLengthY < 0) dragLengthY = -CUT_B
|
||||
if (CUT_R + dragLengthX < 0) dragLengthX = -CUT_R
|
||||
let cutB = CUT_B + dragLengthY
|
||||
let cutR = CUT_R + dragLengthX
|
||||
|
||||
this.setData({
|
||||
cutB: cutB,
|
||||
cutR: cutR
|
||||
})
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cropper-config {
|
||||
padding: 20rpx 40rpx;
|
||||
}
|
||||
|
||||
.cropper-content {
|
||||
min-height: 750rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.uni-corpper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.uni-corpper-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.uni-corpper-content image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-width: 0 !important;
|
||||
max-width: none !important;
|
||||
height: 100%;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
image-orientation: 0deg !important;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 移动图片效果 */
|
||||
.uni-cropper-drag-box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
cursor: move;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 内部的信息 */
|
||||
.uni-corpper-crop-box {
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.uni-corpper-crop-box .uni-cropper-view-box {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
outline: 1rpx solid #69f;
|
||||
outline-color: rgba(102, 153, 255, .75)
|
||||
}
|
||||
|
||||
/* 横向虚线 */
|
||||
.uni-cropper-dashed-h {
|
||||
position: absolute;
|
||||
top: 33.33333333%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 33.33333333%;
|
||||
border-top: 1rpx dashed rgba(255, 255, 255, 0.5);
|
||||
border-bottom: 1rpx dashed rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* 纵向虚线 */
|
||||
.uni-cropper-dashed-v {
|
||||
position: absolute;
|
||||
left: 33.33333333%;
|
||||
top: 0;
|
||||
width: 33.33333333%;
|
||||
height: 100%;
|
||||
border-left: 1rpx dashed rgba(255, 255, 255, 0.5);
|
||||
border-right: 1rpx dashed rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* 四个方向的线 为了之后的拖动事件*/
|
||||
.uni-cropper-line-t {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background-color: #69f;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 1rpx;
|
||||
opacity: 0.1;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.uni-cropper-line-t::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0rpx;
|
||||
width: 100%;
|
||||
-webkit-transform: translate3d(0, -50%, 0);
|
||||
transform: translate3d(0, -50%, 0);
|
||||
bottom: 0;
|
||||
height: 41rpx;
|
||||
background: transparent;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.uni-cropper-line-r {
|
||||
position: absolute;
|
||||
display: block;
|
||||
background-color: #69f;
|
||||
top: 0;
|
||||
right: 0rpx;
|
||||
width: 1rpx;
|
||||
opacity: 0.1;
|
||||
height: 100%;
|
||||
cursor: e-resize;
|
||||
}
|
||||
|
||||
.uni-cropper-line-r::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
width: 41rpx;
|
||||
-webkit-transform: translate3d(-50%, 0, 0);
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.uni-cropper-line-b {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background-color: #69f;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 1rpx;
|
||||
opacity: 0.1;
|
||||
cursor: s-resize;
|
||||
}
|
||||
|
||||
.uni-cropper-line-b::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0rpx;
|
||||
width: 100%;
|
||||
-webkit-transform: translate3d(0, -50%, 0);
|
||||
transform: translate3d(0, -50%, 0);
|
||||
bottom: 0;
|
||||
height: 41rpx;
|
||||
background: transparent;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.uni-cropper-line-l {
|
||||
position: absolute;
|
||||
display: block;
|
||||
background-color: #69f;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1rpx;
|
||||
opacity: 0.1;
|
||||
height: 100%;
|
||||
cursor: w-resize;
|
||||
}
|
||||
|
||||
.uni-cropper-line-l::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
width: 41rpx;
|
||||
-webkit-transform: translate3d(-50%, 0, 0);
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.uni-cropper-point {
|
||||
width: 5rpx;
|
||||
height: 5rpx;
|
||||
background-color: #69f;
|
||||
opacity: .75;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.point-t {
|
||||
top: -3rpx;
|
||||
left: 50%;
|
||||
margin-left: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-tr {
|
||||
top: -3rpx;
|
||||
left: 100%;
|
||||
margin-left: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-r {
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-rb {
|
||||
left: 100%;
|
||||
top: 100%;
|
||||
-webkit-transform: translate3d(-50%, -50%, 0);
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
cursor: n-resize;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
background-color: #69f;
|
||||
position: absolute;
|
||||
z-index: 1112;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.point-b {
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-bl {
|
||||
left: 0%;
|
||||
top: 100%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-l {
|
||||
left: 0%;
|
||||
top: 50%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.point-lt {
|
||||
left: 0%;
|
||||
top: 0%;
|
||||
margin-left: -3rpx;
|
||||
margin-top: -3rpx;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
/* 裁剪框预览内容 */
|
||||
.uni-cropper-viewer {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.uni-cropper-viewer image {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<template>
|
||||
<view class="help-container">
|
||||
<view v-for="(item, findex) in list" :key="findex" :title="item.title" class="list-title">
|
||||
<view class="text-title">
|
||||
<view :class="item.icon"></view>{{ item.title }}
|
||||
</view>
|
||||
<view class="childList">
|
||||
<view v-for="(child, zindex) in item.childList" :key="zindex" class="question" hover-class="hover"
|
||||
@click="handleText(child)">
|
||||
<view class="text-item">{{ child.title }}</view>
|
||||
<view class="line" v-if="zindex !== item.childList.length - 1"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
list: [{
|
||||
icon: 'iconfont icon-github',
|
||||
title: '若依问题',
|
||||
childList: [{
|
||||
title: '若依开源吗?',
|
||||
content: '开源'
|
||||
}, {
|
||||
title: '若依可以商用吗?',
|
||||
content: '可以'
|
||||
}, {
|
||||
title: '若依官网地址多少?',
|
||||
content: 'http://ruoyi.vip'
|
||||
}, {
|
||||
title: '若依文档地址多少?',
|
||||
content: 'http://doc.ruoyi.vip'
|
||||
}]
|
||||
},
|
||||
{
|
||||
icon: 'iconfont icon-help',
|
||||
title: '其他问题',
|
||||
childList: [{
|
||||
title: '如何退出登录?',
|
||||
content: '请点击[我的] - [应用设置] - [退出登录]即可退出登录',
|
||||
}, {
|
||||
title: '如何修改用户头像?',
|
||||
content: '请点击[我的] - [选择头像] - [点击提交]即可更换用户头像',
|
||||
}, {
|
||||
title: '如何修改登录密码?',
|
||||
content: '请点击[我的] - [应用设置] - [修改密码]即可修改登录密码',
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleText(item) {
|
||||
this.$tab.navigateTo(`/pages/common/textview/index?title=${item.title}&content=${item.content}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.help-container {
|
||||
margin-bottom: 100rpx;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.childList {
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 10rpx rgba(193, 193, 193, 0.2);
|
||||
border-radius: 16rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 100%;
|
||||
height: 1rpx;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
.text-title {
|
||||
color: #303133;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-left: 10rpx;
|
||||
|
||||
.iconfont {
|
||||
font-size: 16px;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.text-item {
|
||||
font-size: 28rpx;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.question {
|
||||
color: #606266;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
<template>
|
||||
<view class="mine-container" :style="{height: `${windowHeight}px`}">
|
||||
<!--顶部个人信息栏-->
|
||||
<view class="header-section">
|
||||
<view class="flex padding justify-between">
|
||||
<view class="flex align-center">
|
||||
<view v-if="!avatar" class="cu-avatar xl round bg-white">
|
||||
<view class="iconfont icon-people text-gray icon"></view>
|
||||
</view>
|
||||
<image v-if="avatar" @click="handleToAvatar" :src="avatar" class="cu-avatar xl round" mode="widthFix">
|
||||
</image>
|
||||
<view v-if="!name" @click="handleToLogin" class="login-tip">
|
||||
点击登录
|
||||
</view>
|
||||
<view v-if="name" @click="handleToInfo" class="user-info">
|
||||
<view class="u_title">
|
||||
用户名:{{ name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view @click="handleToInfo" class="flex align-center">
|
||||
<text>个人信息</text>
|
||||
<view class="iconfont icon-right"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="content-section">
|
||||
<view class="mine-actions grid col-4 text-center">
|
||||
<view class="action-item" @click="handleJiaoLiuQun">
|
||||
<view class="iconfont icon-friendfill text-pink icon"></view>
|
||||
<text class="text">交流群</text>
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-service text-blue icon"></view>
|
||||
<text class="text">在线客服</text>
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-community text-mauve icon"></view>
|
||||
<text class="text">反馈社区</text>
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-dianzan text-green icon"></view>
|
||||
<text class="text">点赞我们</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-list">
|
||||
<view class="list-cell list-cell-arrow" @click="handleToEditInfo">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-user menu-icon"></view>
|
||||
<view>编辑资料</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleHelp">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-help menu-icon"></view>
|
||||
<view>常见问题</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleAbout">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-aixin menu-icon"></view>
|
||||
<view>关于我们</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleToSetting">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-setting menu-icon"></view>
|
||||
<view>应用设置</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
name: this.$store.state.user.name
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
avatar() {
|
||||
return this.$store.state.user.avatar
|
||||
},
|
||||
windowHeight() {
|
||||
return uni.getSystemInfoSync().windowHeight - 50
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleToInfo() {
|
||||
this.$tab.navigateTo('/pages/mine/info/index')
|
||||
},
|
||||
handleToEditInfo() {
|
||||
this.$tab.navigateTo('/pages/mine/info/edit')
|
||||
},
|
||||
handleToSetting() {
|
||||
this.$tab.navigateTo('/pages/mine/setting/index')
|
||||
},
|
||||
handleToLogin() {
|
||||
this.$tab.reLaunch('/pages/login')
|
||||
},
|
||||
handleToAvatar() {
|
||||
this.$tab.navigateTo('/pages/mine/avatar/index')
|
||||
},
|
||||
handleHelp() {
|
||||
this.$tab.navigateTo('/pages/mine/help/index')
|
||||
},
|
||||
handleAbout() {
|
||||
this.$tab.navigateTo('/pages/mine/about/index')
|
||||
},
|
||||
handleJiaoLiuQun() {
|
||||
this.$modal.showToast('QQ群:①133713780(满)、②146013835(满)、③189091635')
|
||||
},
|
||||
handleBuilding() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #f5f6f7;
|
||||
}
|
||||
|
||||
.mine-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
|
||||
.header-section {
|
||||
padding: 15px 15px 45px 15px;
|
||||
background-color: #3c96f3;
|
||||
color: white;
|
||||
|
||||
.login-tip {
|
||||
font-size: 18px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.cu-avatar {
|
||||
border: 2px solid #eaeaea;
|
||||
|
||||
.icon {
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
margin-left: 15px;
|
||||
|
||||
.u_title {
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-section {
|
||||
position: relative;
|
||||
top: -50px;
|
||||
|
||||
.mine-actions {
|
||||
margin: 15px 15px;
|
||||
padding: 20px 0px;
|
||||
border-radius: 8px;
|
||||
background-color: white;
|
||||
|
||||
.action-item {
|
||||
.icon {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
margin: 8px 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="example">
|
||||
<uni-forms ref="form" :model="user" labelWidth="80px">
|
||||
<uni-forms-item label="用户昵称" name="nickName">
|
||||
<uni-easyinput v-model="user.nickName" placeholder="请输入昵称" />
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="手机号码" name="phonenumber">
|
||||
<uni-easyinput v-model="user.phonenumber" placeholder="请输入手机号码" />
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="邮箱" name="email">
|
||||
<uni-easyinput v-model="user.email" placeholder="请输入邮箱" />
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="性别" name="sex" required>
|
||||
<uni-data-checkbox v-model="user.sex" :localdata="sexs" />
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
<button type="primary" @click="submit">提交</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUserProfile } from "@/api/system/user"
|
||||
import { updateUserProfile } from "@/api/system/user"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
user: {
|
||||
nickName: "",
|
||||
phonenumber: "",
|
||||
email: "",
|
||||
sex: ""
|
||||
},
|
||||
sexs: [{
|
||||
text: '男',
|
||||
value: "0"
|
||||
}, {
|
||||
text: '女',
|
||||
value: "1"
|
||||
}],
|
||||
rules: {
|
||||
nickName: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '用户昵称不能为空'
|
||||
}]
|
||||
},
|
||||
phonenumber: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '手机号码不能为空'
|
||||
}, {
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
errorMessage: '请输入正确的手机号码'
|
||||
}]
|
||||
},
|
||||
email: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '邮箱地址不能为空'
|
||||
}, {
|
||||
format: 'email',
|
||||
errorMessage: '请输入正确的邮箱地址'
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.getUser()
|
||||
},
|
||||
onReady() {
|
||||
this.$refs.form.setRules(this.rules)
|
||||
},
|
||||
methods: {
|
||||
getUser() {
|
||||
getUserProfile().then(response => {
|
||||
this.user = response.data
|
||||
})
|
||||
},
|
||||
submit(ref) {
|
||||
this.$refs.form.validate().then(res => {
|
||||
updateUserProfile(this.user).then(response => {
|
||||
this.$modal.msgSuccess("修改成功")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.example {
|
||||
padding: 15px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.segmented-control {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<uni-list>
|
||||
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'person-filled'}" title="昵称" :rightText="user.nickName" />
|
||||
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'phone-filled'}" title="手机号码" :rightText="user.phonenumber" />
|
||||
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'email-filled'}" title="邮箱" :rightText="user.email" />
|
||||
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'auth-filled'}" title="岗位" :rightText="postGroup" />
|
||||
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'staff-filled'}" title="角色" :rightText="roleGroup" />
|
||||
<uni-list-item showExtraIcon="true" :extraIcon="{type: 'calendar-filled'}" title="创建日期" :rightText="user.createTime" />
|
||||
</uni-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUserProfile } from "@/api/system/user"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
user: {},
|
||||
roleGroup: "",
|
||||
postGroup: ""
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.getUser()
|
||||
},
|
||||
methods: {
|
||||
getUser() {
|
||||
getUserProfile().then(response => {
|
||||
this.user = response.data
|
||||
this.roleGroup = response.roleGroup
|
||||
this.postGroup = response.postGroup
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<view class="pwd-retrieve-container">
|
||||
<uni-forms ref="form" :value="user" labelWidth="80px">
|
||||
<uni-forms-item name="oldPassword" label="旧密码">
|
||||
<uni-easyinput type="password" v-model="user.oldPassword" placeholder="请输入旧密码" />
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="newPassword" label="新密码">
|
||||
<uni-easyinput type="password" v-model="user.newPassword" placeholder="请输入新密码" />
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="confirmPassword" label="确认密码">
|
||||
<uni-easyinput type="password" v-model="user.confirmPassword" placeholder="请确认新密码" />
|
||||
</uni-forms-item>
|
||||
<button type="primary" @click="submit">提交</button>
|
||||
</uni-forms>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { updateUserPwd } from "@/api/system/user"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
user: {
|
||||
oldPassword: undefined,
|
||||
newPassword: undefined,
|
||||
confirmPassword: undefined
|
||||
},
|
||||
rules: {
|
||||
oldPassword: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '旧密码不能为空'
|
||||
}]
|
||||
},
|
||||
newPassword: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '新密码不能为空',
|
||||
},
|
||||
{
|
||||
minLength: 6,
|
||||
maxLength: 20,
|
||||
errorMessage: '长度在 6 到 20 个字符'
|
||||
}
|
||||
]
|
||||
},
|
||||
confirmPassword: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '确认密码不能为空'
|
||||
}, {
|
||||
validateFunction: (rule, value, data) => data.newPassword === value,
|
||||
errorMessage: '两次输入的密码不一致'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
this.$refs.form.setRules(this.rules)
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
this.$refs.form.validate().then(res => {
|
||||
updateUserPwd(this.user.oldPassword, this.user.newPassword).then(response => {
|
||||
this.$modal.msgSuccess("修改成功")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.pwd-retrieve-container {
|
||||
padding-top: 36rpx;
|
||||
padding: 15px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
<view class="setting-container" :style="{height: `${windowHeight}px`}">
|
||||
<view class="menu-list">
|
||||
<view class="list-cell list-cell-arrow" @click="handleToPwd">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-password menu-icon"></view>
|
||||
<view>修改密码</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleToUpgrade">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-refresh menu-icon"></view>
|
||||
<view>检查更新</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleCleanTmp">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-clean menu-icon"></view>
|
||||
<view>清理缓存</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cu-list menu">
|
||||
<view class="cu-item item-box">
|
||||
<view class="content text-center" @click="handleLogout">
|
||||
<text class="text-black">退出登录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
windowHeight: uni.getSystemInfoSync().windowHeight
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleToPwd() {
|
||||
this.$tab.navigateTo('/pages/mine/pwd/index')
|
||||
},
|
||||
handleToUpgrade() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
},
|
||||
handleCleanTmp() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
},
|
||||
handleLogout() {
|
||||
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
|
||||
this.$store.dispatch('LogOut').then(() => {}).finally(()=>{
|
||||
this.$tab.reLaunch('/pages/index')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.item-box {
|
||||
background-color: #FFFFFF;
|
||||
margin: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10rpx;
|
||||
border-radius: 8rpx;
|
||||
color: #303133;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,704 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<!-- 顶部状态栏/时间 -->
|
||||
<view class="status-bar">
|
||||
<text class="time">9:41</text>
|
||||
</view>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-bar">
|
||||
<view class="search-input">
|
||||
<uni-icons type="search" size="18" color="#999"></uni-icons>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索商品名称、条码"
|
||||
placeholder-class="placeholder"
|
||||
v-model="searchText"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
<uni-icons type="scan" size="18" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 筛选标签 -->
|
||||
<view class="filter-tabs">
|
||||
<scroll-view class="tabs-scroll" scroll-x="true">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: currentTab === 'all' }"
|
||||
@tap="switchTab('all')"
|
||||
>
|
||||
全部
|
||||
</view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: currentTab === 'expiring' }"
|
||||
@tap="switchTab('expiring')"
|
||||
>
|
||||
临期/过期<text class="badge">0</text>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: currentTab === 'outstock' }"
|
||||
@tap="switchTab('outstock')"
|
||||
>
|
||||
缺货<text class="badge">0</text>
|
||||
</view>
|
||||
<view class="tab-item filter-btn" @tap="showFilter = !showFilter">
|
||||
<text>筛选</text>
|
||||
<uni-icons type="arrowdown" size="14" color="#666"></uni-icons>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 微店状态栏 -->
|
||||
<view class="store-status">
|
||||
<view class="status-content">
|
||||
<view class="dui">
|
||||
<image class="dui-img" src="/static/Frame 52.png"></image>
|
||||
</view>
|
||||
<view class="status-left">
|
||||
<text class="status-text">已开启微店商店库</text>
|
||||
<text class="status-num">微店商品库全226+商品</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="status-right" @tap="goToSettings">
|
||||
<text class="set-text">设置</text>
|
||||
<uni-icons type="forward" size="14" color="#666"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<scroll-view class="goods-list" scroll-y="true">
|
||||
<view class="goods-item" v-for="item in goodsList" :key="item.id">
|
||||
<view class="goods-more-icon" @tap="toggleDeleteMenu(item)">
|
||||
<uni-icons type="more-filled" size="18"></uni-icons>
|
||||
</view>
|
||||
<view class="goods-delete-menu" v-if="item.showDeleteMenu" @tap="showDeleteConfirm(item)">
|
||||
<text class="delete-menu-text">删除商品</text>
|
||||
</view>
|
||||
<view class="goods-top" @tap="goToStockDetail(item)">
|
||||
<view class="goods-tou">
|
||||
<image :src="item.mainImage ? 'http://193.112.94.36:8081' + item.mainImage : '/static/687b6f95b14eff60f4b77147b3726ab2.jpg'"></image>
|
||||
</view>
|
||||
<view class="goods-info">
|
||||
<text class="goods-name">{{ item.productName }}</text>
|
||||
<text class="goods-barcode">{{ item.productBarCode }}</text>
|
||||
<text class="goods-price">¥{{ item.storePrice ? item.storePrice.toFixed(2) : '0.00' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="goods-divider"></view>
|
||||
<view class="goods-stock" >
|
||||
<text class="stock-label">总库存</text>
|
||||
<text class="stock-num">{{ item.stockQuantity }} 件</text>
|
||||
<text class="stock-arrow">></text>
|
||||
</view>
|
||||
<!-- <view class="goods-actions">
|
||||
|
||||
<view class="action-btn add-btn" @tap="addGoodsStock(item)">
|
||||
<text>添加商品/库存</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="bottom-actions">
|
||||
<view class="action-btn more-btn" @tap="showMoreActions(item)">
|
||||
<text>更多</text>
|
||||
</view>
|
||||
<button class="action-button primary" @tap="addNewGoods">
|
||||
添加商品/库存
|
||||
</button>
|
||||
<!-- <button class="action-button" @tap="batchOperation">
|
||||
批量操作
|
||||
</button> -->
|
||||
</view>
|
||||
|
||||
<!-- 更多操作弹窗 -->
|
||||
<uni-popup ref="popup" type="bottom">
|
||||
<view class="popup-content">
|
||||
<view class="popup-item" @tap="editGoods(selectedGoods)">
|
||||
<uni-icons type="compose" size="20" color="#333"></uni-icons>
|
||||
<text>编辑商品</text>
|
||||
</view>
|
||||
<view class="popup-item" @tap="adjustStock(selectedGoods)">
|
||||
<uni-icons type="plus" size="20" color="#333"></uni-icons>
|
||||
<text>调整库存</text>
|
||||
</view>
|
||||
<view class="popup-item" @tap="deleteGoods(selectedGoods)">
|
||||
<uni-icons type="trash" size="20" color="#ff4444"></uni-icons>
|
||||
<text style="color: #ff4444;">删除商品</text>
|
||||
</view>
|
||||
<view class="popup-item cancel" @tap="$refs.popup.close()">
|
||||
<text>取消</text>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProductList as fetchProductList, deleteProduct } from '@/api/product'
|
||||
import { getToken, getStoreId } from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currentTab: 'all',
|
||||
searchText: '',
|
||||
showFilter: false,
|
||||
selectedGoods: null,
|
||||
storeId: null, // 门店ID
|
||||
goodsList: [
|
||||
{
|
||||
id: null,
|
||||
productName: '',
|
||||
productBarCode: '',
|
||||
storePrice: 0.00,
|
||||
stockQuantity: 0,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 获取门店ID
|
||||
const storeId = getStoreId();
|
||||
if (storeId) {
|
||||
this.storeId = storeId;
|
||||
console.log('当前门店ID:', storeId);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '请先选择门店',
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
|
||||
this.getProductList();
|
||||
},
|
||||
onShow() {
|
||||
this.getProductList();
|
||||
},
|
||||
methods: {
|
||||
async getProductList(searchParams = {}) {
|
||||
try {
|
||||
const token = getToken();
|
||||
console.log('当前Token:', token);
|
||||
console.log('Token是否存在:', !!token);
|
||||
console.log('查询参数:', searchParams);
|
||||
|
||||
// 添加门店ID参数
|
||||
if (this.storeId) {
|
||||
searchParams.storeId = this.storeId;
|
||||
}
|
||||
|
||||
const res = await fetchProductList(searchParams);
|
||||
|
||||
console.log('商品列表接口返回:', res);
|
||||
|
||||
|
||||
if (res && res.code === 200 && res.data) {
|
||||
this.goodsList = res.data.map(item => ({
|
||||
...item,
|
||||
showDeleteMenu: false
|
||||
}));
|
||||
console.log('处理后的商品列表:', this.goodsList);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res?.msg || '获取商品列表失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取商品列表失败:', error);
|
||||
console.error('错误详情:', JSON.stringify(error));
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
switchTab(tab) {
|
||||
this.currentTab = tab;
|
||||
// 这里可以添加筛选逻辑
|
||||
console.log('切换到标签:', tab);
|
||||
},
|
||||
|
||||
onSearch() {
|
||||
console.log('搜索关键词:', this.searchText);
|
||||
|
||||
if (!this.searchText || this.searchText.trim() === '') {
|
||||
this.getProductList();
|
||||
return;
|
||||
}
|
||||
|
||||
const searchText = this.searchText.trim();
|
||||
let searchParams = {};
|
||||
|
||||
if (/^\d+$/.test(searchText)) {
|
||||
searchParams = { productName: searchText };
|
||||
console.log('商品名称模糊查询:', searchParams);
|
||||
} else {
|
||||
searchParams = { productBarCode: searchText };
|
||||
console.log('按条形码精准查询按:', searchParams);
|
||||
}
|
||||
|
||||
this.getProductList(searchParams);
|
||||
},
|
||||
|
||||
goToSettings() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/settings/index'
|
||||
});
|
||||
},
|
||||
|
||||
showMoreActions(goods) {
|
||||
this.selectedGoods = goods;
|
||||
this.$refs.popup.open();
|
||||
},
|
||||
|
||||
addGoodsStock(goods) {
|
||||
console.log('添加商品库存:', goods);
|
||||
uni.showModal({
|
||||
title: '添加库存',
|
||||
content: `为 ${goods.name} 添加库存`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 执行添加库存操作
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
goToStockDetail(item) {
|
||||
// 跳转到编辑页面,传递商品ID
|
||||
uni.navigateTo({
|
||||
url: `/pages/edit/edit?id=${item.id}`
|
||||
});
|
||||
},
|
||||
|
||||
toggleDeleteMenu(item) {
|
||||
item.showDeleteMenu = !item.showDeleteMenu;
|
||||
},
|
||||
|
||||
showDeleteConfirm(item) {
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: `确定要删除商品"${item.productName}"吗?`,
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
const result = await deleteProduct(item.id);
|
||||
console.log('删除商品结果:', result);
|
||||
|
||||
if (result && result.code === 200) {
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
// 重新加载商品列表
|
||||
this.getProductList();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: result?.msg || '删除失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除商品失败:', error);
|
||||
uni.showToast({
|
||||
title: '删除失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addNewGoods() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/enter/enter'
|
||||
// url: '/pages/addProduct/addProduct'
|
||||
});
|
||||
},
|
||||
|
||||
batchOperation() {
|
||||
uni.showActionSheet({
|
||||
itemList: ['批量修改价格', '批量修改库存', '批量下架'],
|
||||
success: (res) => {
|
||||
console.log('选择了操作:', res.tapIndex);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
editGoods(goods) {
|
||||
this.$refs.popup.close();
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/edit?id=${goods.id}`
|
||||
});
|
||||
},
|
||||
|
||||
adjustStock(goods) {
|
||||
this.$refs.popup.close();
|
||||
uni.showModal({
|
||||
title: '调整库存',
|
||||
content: `调整 ${goods.name} 的库存数量`,
|
||||
editable: true,
|
||||
placeholderText: '请输入调整数量',
|
||||
success: (res) => {
|
||||
if (res.confirm && res.content) {
|
||||
console.log('调整库存数量:', res.content);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
deleteGoods(goods) {
|
||||
this.$refs.popup.close();
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: `确定要删除商品"${goods.name}"吗?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
console.log('删除商品:', goods);
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.goods-tou{
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
margin-right: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.goods-tou image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
padding: 10px 16px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 搜索栏 */
|
||||
.search-bar {
|
||||
padding: 12px 16px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.search-input input {
|
||||
flex: 1;
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 筛选标签 */
|
||||
.filter-tabs {
|
||||
background-color: #fff;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.tabs-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
display: inline-block;
|
||||
padding: 6px 12px;
|
||||
margin-right: 10px;
|
||||
border-radius: 16px;
|
||||
font-size: 14px;
|
||||
background-color: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
background-color: #e8f4ff;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.badge {
|
||||
margin-left: 4px;
|
||||
color: #ff4444;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 微店状态栏 */
|
||||
.store-status {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
margin-top: 8px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.status-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dui {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.dui-img {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
.status-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.status-num {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.status-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 1rpx;
|
||||
}
|
||||
|
||||
.set-text {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* 商品列表 */
|
||||
.goods-list {
|
||||
height: calc(100vh - 320px);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.goods-more-icon {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.goods-delete-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 12px;
|
||||
background-color: #fff;
|
||||
border-radius: 8rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
|
||||
padding: 12rpx 24rpx;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.delete-menu-text {
|
||||
font-size: 28rpx;
|
||||
color: #ff4444;
|
||||
}
|
||||
|
||||
.goods-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.goods-info {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.goods-name {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.goods-barcode {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
font-size: 18px;
|
||||
color: #ff4444;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.goods-divider {
|
||||
height: 1px;
|
||||
border-bottom: 1px dashed #e0e0e0;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.goods-stock {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stock-arrow {
|
||||
font-size: 20px;
|
||||
color: #999;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.stock-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.stock-num {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.goods-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.more-btn {
|
||||
background-color: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
background-color: #e8f4ff;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
/* 底部操作按钮 */
|
||||
.bottom-actions {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
flex: 1;
|
||||
margin: 0 8px;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.action-button.primary {
|
||||
background-color: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
.popup-content {
|
||||
background-color: #fff;
|
||||
border-radius: 16px 16px 0 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.popup-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.popup-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.popup-item text {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.popup-item.cancel {
|
||||
justify-content: center;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
<template>
|
||||
<view class="normal-login-container">
|
||||
<view class="logo-content align-center justify-center flex">
|
||||
<image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">
|
||||
</image>
|
||||
<text class="title">若依移动端注册</text>
|
||||
</view>
|
||||
<view class="login-form-content">
|
||||
<view class="input-item flex align-center">
|
||||
<view class="iconfont icon-user icon"></view>
|
||||
<input v-model="registerForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" />
|
||||
</view>
|
||||
<view class="input-item flex align-center">
|
||||
<view class="iconfont icon-password icon"></view>
|
||||
<input v-model="registerForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
|
||||
</view>
|
||||
<view class="input-item flex align-center">
|
||||
<view class="iconfont icon-password icon"></view>
|
||||
<input v-model="registerForm.confirmPassword" type="password" class="input" placeholder="请输入重复密码" maxlength="20" />
|
||||
</view>
|
||||
<view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
|
||||
<view class="iconfont icon-code icon"></view>
|
||||
<input v-model="registerForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
|
||||
<view class="login-code">
|
||||
<image :src="codeUrl" @click="getCode" class="login-code-img"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="action-btn">
|
||||
<button @click="handleRegister()" class="register-btn cu-btn block bg-blue lg round">注册</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="xieyi text-center">
|
||||
<text @click="handleUserLogin" class="text-blue">使用已有账号登录</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCodeImg, register } from '@/api/login'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
codeUrl: "",
|
||||
captchaEnabled: true,
|
||||
globalConfig: getApp().globalData.config,
|
||||
registerForm: {
|
||||
username: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
code: "",
|
||||
uuid: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getCode()
|
||||
},
|
||||
methods: {
|
||||
// 用户登录
|
||||
handleUserLogin() {
|
||||
this.$tab.navigateTo(`/pages/login`)
|
||||
},
|
||||
// 获取图形验证码
|
||||
getCode() {
|
||||
getCodeImg().then(res => {
|
||||
this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled
|
||||
if (this.captchaEnabled) {
|
||||
this.codeUrl = 'data:image/gif;base64,' + res.img
|
||||
this.registerForm.uuid = res.uuid
|
||||
}
|
||||
})
|
||||
},
|
||||
// 注册方法
|
||||
async handleRegister() {
|
||||
if (this.registerForm.username === "") {
|
||||
this.$modal.msgError("请输入您的账号")
|
||||
} else if (this.registerForm.password === "") {
|
||||
this.$modal.msgError("请输入您的密码")
|
||||
} else if (this.registerForm.confirmPassword === "") {
|
||||
this.$modal.msgError("请再次输入您的密码")
|
||||
} else if (this.registerForm.password !== this.registerForm.confirmPassword) {
|
||||
this.$modal.msgError("两次输入的密码不一致")
|
||||
} else if (this.registerForm.code === "" && this.captchaEnabled) {
|
||||
this.$modal.msgError("请输入验证码")
|
||||
} else {
|
||||
this.$modal.loading("注册中,请耐心等待...")
|
||||
this.register()
|
||||
}
|
||||
},
|
||||
// 用户注册
|
||||
async register() {
|
||||
register(this.registerForm).then(res => {
|
||||
this.$modal.closeLoading()
|
||||
uni.showModal({
|
||||
title: "系统提示",
|
||||
content: "恭喜你,您的账号 " + this.registerForm.username + " 注册成功!",
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
uni.redirectTo({ url: `/pages/login` });
|
||||
}
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
if (this.captchaEnabled) {
|
||||
this.getCode()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.normal-login-container {
|
||||
width: 100%;
|
||||
|
||||
.logo-content {
|
||||
width: 100%;
|
||||
font-size: 21px;
|
||||
text-align: center;
|
||||
padding-top: 15%;
|
||||
|
||||
image {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form-content {
|
||||
text-align: center;
|
||||
margin: 20px auto;
|
||||
margin-top: 15%;
|
||||
width: 80%;
|
||||
|
||||
.input-item {
|
||||
margin: 20px auto;
|
||||
background-color: #f5f6f7;
|
||||
height: 45px;
|
||||
border-radius: 20px;
|
||||
|
||||
.icon {
|
||||
font-size: 38rpx;
|
||||
margin-left: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.register-btn {
|
||||
margin-top: 40px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.xieyi {
|
||||
color: #333;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.login-code {
|
||||
height: 38px;
|
||||
float: right;
|
||||
|
||||
.login-code-img {
|
||||
height: 38px;
|
||||
position: absolute;
|
||||
margin-left: 10px;
|
||||
width: 200rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<view class="settings-container">
|
||||
<!-- 页面标题 -->
|
||||
<view class="page-header">
|
||||
<text class="page-title">设置</text>
|
||||
</view>
|
||||
|
||||
<!-- 通用模块 -->
|
||||
<view class="settings-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">通用</text>
|
||||
</view>
|
||||
<view class="settings-list">
|
||||
<view class="list-item" @click="navigateTo('个人信息')">
|
||||
<text class="item-text">个人信息</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('店铺信息')">
|
||||
<text class="item-text">店铺信息</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('子账号管理')">
|
||||
<text class="item-text">子账号管理</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 门店模块 -->
|
||||
<view class="settings-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">门店</text>
|
||||
</view>
|
||||
<view class="settings-list">
|
||||
<view class="list-item" @click="navigateTo('开关门设置')">
|
||||
<text class="item-text">开关门设置</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('进店语音设置')">
|
||||
<text class="item-text">进店语音设置</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('推荐设置')">
|
||||
<text class="item-text">推荐设置</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 交易模块 -->
|
||||
<view class="settings-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">交易</text>
|
||||
</view>
|
||||
<view class="settings-list">
|
||||
<view class="list-item" @click="navigateTo('支付方式认证')">
|
||||
<text class="item-text">支付方式认证</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('自动化扣')">
|
||||
<text class="item-text">自动化扣</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('音频级别设置')">
|
||||
<text class="item-text">音频级别设置</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('非值守期间现金收银')">
|
||||
<text class="item-text">非值守期间现金收银</text>
|
||||
<text class="iconfont icon-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 页面数据
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navigateTo(pageName) {
|
||||
// 实际项目中,这里会跳转到对应页面
|
||||
uni.showToast({
|
||||
title: `跳转到${pageName}`,
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
});
|
||||
|
||||
// 示例:根据不同页面进行不同跳转
|
||||
// switch(pageName) {
|
||||
// case '个人信息':
|
||||
// uni.navigateTo({ url: '/pages/user/profile' });
|
||||
// break;
|
||||
// case '店铺信息':
|
||||
// uni.navigateTo({ url: '/pages/shop/info' });
|
||||
// break;
|
||||
// // ... 其他页面跳转
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 20rpx;
|
||||
|
||||
.page-header {
|
||||
padding: 30rpx 40rpx;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
|
||||
.page-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin-top: 30rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
margin-left: 30rpx;
|
||||
margin-right: 30rpx;
|
||||
|
||||
.section-header {
|
||||
padding: 28rpx 32rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-list {
|
||||
.list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx 32rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-size: 36rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式适配 */
|
||||
@media (min-width: 768px) {
|
||||
.settings-container {
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
<template>
|
||||
<view class="store-select-container">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="navbar">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="left" size="20" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">选择门店</view>
|
||||
<view class="nav-right"></view>
|
||||
</view>
|
||||
|
||||
<view class="store-list">
|
||||
<view
|
||||
class="store-item"
|
||||
v-for="(item, index) in storeList"
|
||||
:key="index"
|
||||
@click="selectStore(item)"
|
||||
>
|
||||
<view class="store-info">
|
||||
<text class="store-name">{{ item.storeName }}</text>
|
||||
<text class="store-code">编码:{{ item.storeCode }}</text>
|
||||
</view>
|
||||
<uni-icons type="right" size="20" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="empty-state" v-if="storeList.length === 0">
|
||||
<text class="empty-text">暂无门店数据</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getStoreList } from '@/api/store'
|
||||
import { getUserId, setStoreId, setStoreInfo, getStoreId } from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
userId: null,
|
||||
storeList: []
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 获取用户ID
|
||||
const userId = getUserId();
|
||||
if (userId) {
|
||||
this.userId = userId;
|
||||
this.loadStoreList();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '用户ID不存在',
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadStoreList() {
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
});
|
||||
|
||||
// 根据用户ID查询门店列表
|
||||
getStoreList(String(this.userId)).then(res => {
|
||||
uni.hideLoading();
|
||||
|
||||
if (res && res.code === 200) {
|
||||
// 适配不同的数据结构
|
||||
if (Array.isArray(res.data)) {
|
||||
this.storeList = res.data;
|
||||
} else if (res.data && res.data.stores) {
|
||||
this.storeList = res.data.stores;
|
||||
}
|
||||
console.log('门店列表:', this.storeList);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res?.msg || '获取门店列表失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
uni.hideLoading();
|
||||
console.error('获取门店列表失败:', error);
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
goBack() {
|
||||
// 检查是否已经选择了门店
|
||||
const storeId = getStoreId();
|
||||
if (storeId) {
|
||||
// 已选择门店,不允许返回
|
||||
uni.showToast({
|
||||
title: '请选择门店',
|
||||
icon: 'none'
|
||||
});
|
||||
} else {
|
||||
// 未选择门店,返回登录页面
|
||||
uni.navigateBack();
|
||||
}
|
||||
},
|
||||
|
||||
selectStore(store) {
|
||||
console.log('选择门店:', store);
|
||||
|
||||
// 存储门店ID和门店信息
|
||||
setStoreId(store.storeId);
|
||||
setStoreInfo(store);
|
||||
|
||||
// 提示选择成功
|
||||
uni.showToast({
|
||||
title: '已选择门店',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 跳转到首页
|
||||
setTimeout(() => {
|
||||
uni.switchTab({
|
||||
url: '/pages/index'
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.store-select-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding-top: 44px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
background-color: #e60012;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.nav-left, .nav-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.store-list {
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
margin: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.store-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.store-item:active {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.store-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.store-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.store-name {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.store-code {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,319 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<!-- 个人信息标题模块 -->
|
||||
<view class="header-module">
|
||||
<text class="header-title">个人信息</text>
|
||||
</view>
|
||||
|
||||
<view class="one-module">
|
||||
<view class="touXiang">
|
||||
<view class="gearOne"><uni-icons type="camera-filled" size="20"></uni-icons></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 账户信息模块 -->
|
||||
<view class="info-module">
|
||||
<view class="module-item">
|
||||
<text class="item-label">昵称</text>
|
||||
<text class="item-value">名称</text>
|
||||
</view>
|
||||
<view class="module-item">
|
||||
<text class="item-label">绑定微信号</text>
|
||||
<text class="item-value">名称</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 账户安全模块 -->
|
||||
<view class="security-module">
|
||||
<view class="module-title">账户安全</view>
|
||||
<view class="module-item clickable" @tap="handleChangePhone">
|
||||
<text class="item-label">修改手机号</text>
|
||||
<view class="item-right">
|
||||
<text class="arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="module-item clickable" @tap="handleChangePassword">
|
||||
<text class="item-label">修改密码</text>
|
||||
<view class="item-right">
|
||||
<text class="arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 设备管理模块 -->
|
||||
<view class="device-module">
|
||||
<view class="module-title">设备管理</view>
|
||||
<view class="module-item clickable" @tap="handleDeviceManage">
|
||||
<text class="item-label">设备管理</text>
|
||||
<view class="item-right">
|
||||
<text class="arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 账号管理模块 -->
|
||||
<view class="account-module">
|
||||
<view class="module-title">账号管理</view>
|
||||
<view class="module-item clickable last-item" @tap="handleCancelAccount">
|
||||
<text class="item-label">注销账号</text>
|
||||
<view class="item-right">
|
||||
<text class="arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录模块 -->
|
||||
<view class="logout-module">
|
||||
<view class="logout-btn" @tap="handleLogout">
|
||||
<text class="logout-text">退出登录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部提示模块 -->
|
||||
<view class="footer-module">
|
||||
<text class="footer-text">这不是我的账号</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
// 修改手机号
|
||||
handleChangePhone() {
|
||||
uni.showToast({
|
||||
title: '跳转到修改手机号页面',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
// 修改密码
|
||||
handleChangePassword() {
|
||||
uni.showToast({
|
||||
title: '跳转到修改密码页面',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
// 设备管理
|
||||
handleDeviceManage() {
|
||||
uni.showToast({
|
||||
title: '跳转到设备管理页面',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
// 注销账号
|
||||
handleCancelAccount() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要注销账号吗?此操作不可恢复。',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({
|
||||
title: '账号注销申请已提交',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// 退出登录
|
||||
handleLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({
|
||||
title: '退出登录成功',
|
||||
icon: 'success',
|
||||
duration: 1500,
|
||||
success: () => {
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({ url: '/pages/login/login' })
|
||||
}, 1500)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.touXiang{
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #ffffff;
|
||||
background-image: url('/static/687b6f95b14eff60f4b77147b3726ab2.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 40rpx auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gearOne{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
/* ========== 状态栏模块 ========== */
|
||||
.status-bar {
|
||||
background-color: #ffffff;
|
||||
padding: 40rpx 0 20rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* ========== 标题模块 ========== */
|
||||
.header-module {
|
||||
background-color: cornflowerblue;
|
||||
padding: 40rpx 40rpx 60rpx;
|
||||
border-bottom: 2rpx solid #f5f5f5;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 30rpx;
|
||||
/* font-weight: bold; */
|
||||
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* ========== 通用模块样式 ========== */
|
||||
.info-module,
|
||||
.security-module,
|
||||
.device-module,
|
||||
.account-module {
|
||||
background-color: #ffffff;
|
||||
margin-top: 20rpx;
|
||||
padding: 0 40rpx;
|
||||
margin: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
|
||||
}
|
||||
|
||||
/* 模块标题 */
|
||||
.module-title {
|
||||
font-size: 30rpx;
|
||||
color: #999999;
|
||||
padding: 30rpx 0 20rpx;
|
||||
}
|
||||
|
||||
/* 模块项 */
|
||||
.module-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 36rpx 0;
|
||||
border-bottom: 2rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 最后一项去掉底部边框 */
|
||||
.last-item {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 点击效果 */
|
||||
.clickable:active {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
/* 标签样式 */
|
||||
.item-label {
|
||||
font-size: 34rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
/* 值样式 */
|
||||
.item-value {
|
||||
font-size: 34rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
/* 右侧内容 */
|
||||
.item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 箭头样式 */
|
||||
.arrow {
|
||||
font-size: 36rpx;
|
||||
color: #cccccc;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
/* ========== 账户信息模块 ========== */
|
||||
.info-module {
|
||||
margin-top: 0;
|
||||
border-top: 2rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
/* ========== 退出登录模块 ========== */
|
||||
.logout-module {
|
||||
margin-top: 40rpx;
|
||||
padding: 0 40rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logout-btn:active {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.logout-text {
|
||||
font-size: 34rpx;
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* ========== 底部提示模块 ========== */
|
||||
.footer-module {
|
||||
margin-top: 40rpx;
|
||||
padding: 0 40rpx 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 20rpx;
|
||||
color: limegreen;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
<template>
|
||||
<view class="user-stores-container">
|
||||
<!-- 页面标题 -->
|
||||
<view class="page-header">
|
||||
<text class="page-title">用户门店关联</text>
|
||||
<view class="header-right">
|
||||
<button class="save-btn" @click="saveUserStores">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 门店列表 -->
|
||||
<view class="stores-list">
|
||||
<view class="store-item" v-for="store in stores" :key="store.storeId">
|
||||
<view class="store-info">
|
||||
<text class="store-name">{{ store.storeName }}</text>
|
||||
<text class="store-code">编码:{{ store.storeCode }}</text>
|
||||
</view>
|
||||
<uni-data-checkbox
|
||||
:value="store.storeId"
|
||||
:checked="isStoreChecked(store.storeId)"
|
||||
@change="onCheckboxChange"
|
||||
:name="'store-' + store.storeId"
|
||||
></uni-data-checkbox>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" v-if="stores.length === 0">
|
||||
<text class="empty-text">暂无门店数据</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {}, // 用户所关联的门店
|
||||
stores: [], // 所有的门店
|
||||
userId: null,
|
||||
selectedStoreIds: [] // 选中的门店ID数组
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
// 获取用户ID(可以从路由参数或本地存储获取)
|
||||
this.userId = options.userId || 2; // 这里使用默认值2作为示例
|
||||
this.loadUserStores();
|
||||
},
|
||||
methods: {
|
||||
// 加载用户门店数据
|
||||
loadUserStores() {
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
});
|
||||
|
||||
// 模拟后端请求,实际项目中应替换为真实API调用
|
||||
// 假设API返回的数据格式如下:
|
||||
// {
|
||||
// "code": 200,
|
||||
// "data": {
|
||||
// "allStores": [{storeId: 1, storeName: "门店621", storeCode: "门店6code"}, ...],
|
||||
// "stores": [{storeId: 1, storeName: "门店621", storeCode: "门店6code"}, ...],
|
||||
// "userId": 2
|
||||
// },
|
||||
// "msg": "操作成功"
|
||||
// }
|
||||
|
||||
// 这里使用setTimeout模拟异步请求
|
||||
setTimeout(() => {
|
||||
// 模拟后端返回的数据
|
||||
const mockResponse = {
|
||||
code: 200,
|
||||
data: {
|
||||
allStores: [
|
||||
{storeId: 1, storeName: "门店621", storeCode: "门店6code"},
|
||||
{storeId: 2, storeName: "门店622", storeCode: "门店6code2"},
|
||||
{storeId: 3, storeName: "门店623", storeCode: "门店6code3"}
|
||||
],
|
||||
stores: [
|
||||
{storeId: 1, storeName: "门店621", storeCode: "门店6code"}
|
||||
],
|
||||
userId: 2
|
||||
},
|
||||
msg: "操作成功"
|
||||
};
|
||||
|
||||
// 处理返回数据
|
||||
this.form = mockResponse.data.stores || {};
|
||||
this.stores = mockResponse.data.allStores || [];
|
||||
|
||||
// 初始化选中的门店ID数组
|
||||
this.selectedStoreIds = mockResponse.data.stores.map(store => store.storeId);
|
||||
|
||||
uni.hideLoading();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
// 检查门店是否被选中
|
||||
isStoreChecked(storeId) {
|
||||
return this.selectedStoreIds.includes(storeId);
|
||||
},
|
||||
|
||||
// 复选框变化事件
|
||||
onCheckboxChange(e) {
|
||||
const storeId = e.value;
|
||||
const isChecked = e.checked;
|
||||
|
||||
if (isChecked) {
|
||||
// 添加到选中数组
|
||||
if (!this.selectedStoreIds.includes(storeId)) {
|
||||
this.selectedStoreIds.push(storeId);
|
||||
}
|
||||
} else {
|
||||
// 从选中数组中移除
|
||||
this.selectedStoreIds = this.selectedStoreIds.filter(id => id !== storeId);
|
||||
}
|
||||
},
|
||||
|
||||
// 保存用户门店关联
|
||||
saveUserStores() {
|
||||
uni.showLoading({
|
||||
title: '保存中...'
|
||||
});
|
||||
|
||||
// 模拟保存请求,实际项目中应替换为真实API调用
|
||||
setTimeout(() => {
|
||||
// 这里可以将selectedStoreIds发送到后端保存
|
||||
console.log('保存的门店ID:', this.selectedStoreIds);
|
||||
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 返回上一页
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-stores-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx 40rpx;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
padding: 12rpx 30rpx;
|
||||
background-color: #007aff;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.stores-list {
|
||||
margin: 20rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.store-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx 40rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.store-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.store-name {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.store-code {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 300rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 30rpx;
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
<template>
|
||||
<view class="work-container">
|
||||
<!-- 轮播图 -->
|
||||
<uni-swiper-dot class="uni-swiper-dot-box" :info="data" :current="current" field="content">
|
||||
<swiper class="swiper-box" :current="swiperDotIndex" @change="changeSwiper">
|
||||
<swiper-item v-for="(item, index) in data" :key="index">
|
||||
<view class="swiper-item" @click="clickBannerItem(item)">
|
||||
<image :src="item.image" mode="aspectFill" :draggable="false" />
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</uni-swiper-dot>
|
||||
|
||||
<!-- 宫格组件 -->
|
||||
<uni-section title="系统管理" type="line"></uni-section>
|
||||
<view class="grid-body">
|
||||
<uni-grid :column="4" :showBorder="false" @change="changeGrid">
|
||||
<uni-grid-item>
|
||||
<view class="grid-item-box">
|
||||
<uni-icons type="person-filled" size="30"></uni-icons>
|
||||
<text class="text">用户管理</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
<uni-grid-item>
|
||||
<view class="grid-item-box">
|
||||
<uni-icons type="staff-filled" size="30"></uni-icons>
|
||||
<text class="text">角色管理</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
<uni-grid-item>
|
||||
<view class="grid-item-box">
|
||||
<uni-icons type="color" size="30"></uni-icons>
|
||||
<text class="text">菜单管理</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
<uni-grid-item>
|
||||
<view class="grid-item-box">
|
||||
<uni-icons type="settings-filled" size="30"></uni-icons>
|
||||
<text class="text">部门管理</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
<uni-grid-item>
|
||||
<view class="grid-item-box">
|
||||
<uni-icons type="heart-filled" size="30"></uni-icons>
|
||||
<text class="text">岗位管理</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
<uni-grid-item>
|
||||
<view class="grid-item-box">
|
||||
<uni-icons type="bars" size="30"></uni-icons>
|
||||
<text class="text">字典管理</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
<uni-grid-item>
|
||||
<view class="grid-item-box">
|
||||
<uni-icons type="gear-filled" size="30"></uni-icons>
|
||||
<text class="text">参数设置</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
<uni-grid-item>
|
||||
<view class="grid-item-box">
|
||||
<uni-icons type="chat-filled" size="30"></uni-icons>
|
||||
<text class="text">通知公告</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
<uni-grid-item>
|
||||
<view class="grid-item-box">
|
||||
<uni-icons type="wallet-filled" size="30"></uni-icons>
|
||||
<text class="text">日志管理</text>
|
||||
</view>
|
||||
</uni-grid-item>
|
||||
</uni-grid>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
swiperDotIndex: 0,
|
||||
data: [{
|
||||
image: '/static/images/banner/banner01.jpg'
|
||||
},
|
||||
{
|
||||
image: '/static/images/banner/banner02.jpg'
|
||||
},
|
||||
{
|
||||
image: '/static/images/banner/banner03.jpg'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickBannerItem(item) {
|
||||
console.info(item)
|
||||
},
|
||||
changeSwiper(e) {
|
||||
this.current = e.detail.current
|
||||
},
|
||||
changeGrid(e) {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* #ifndef APP-NVUE */
|
||||
page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
view {
|
||||
font-size: 14px;
|
||||
line-height: inherit;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.grid-item-box {
|
||||
flex: 1;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.uni-margin-wrap {
|
||||
width: 690rpx;
|
||||
width: 100%;
|
||||
;
|
||||
}
|
||||
|
||||
.swiper {
|
||||
height: 300rpx;
|
||||
}
|
||||
|
||||
.swiper-box {
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.swiper-item {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
height: 300rpx;
|
||||
line-height: 300rpx;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 500px) {
|
||||
.uni-swiper-dot-box {
|
||||
width: 400px;
|
||||
/* #ifndef APP-NVUE */
|
||||
margin: 0 auto;
|
||||
/* #endif */
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import { getToken } from '@/utils/auth'
|
||||
|
||||
// 登录页面
|
||||
const loginPage = "/pages/login"
|
||||
|
||||
// 页面白名单
|
||||
const whiteList = [
|
||||
'/pages/login', '/pages/register', '/pages/common/webview/index'
|
||||
]
|
||||
|
||||
// 检查地址白名单
|
||||
function checkWhite(url) {
|
||||
const path = url.split('?')[0]
|
||||
return whiteList.indexOf(path) !== -1
|
||||
}
|
||||
|
||||
// 页面跳转验证拦截器
|
||||
let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"]
|
||||
list.forEach(item => {
|
||||
uni.addInterceptor(item, {
|
||||
invoke(to) {
|
||||
if (getToken()) {
|
||||
if (to.url === loginPage) {
|
||||
uni.reLaunch({ url: "/" })
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
if (checkWhite(to.url)) {
|
||||
return true
|
||||
}
|
||||
uni.reLaunch({ url: loginPage })
|
||||
return false
|
||||
}
|
||||
},
|
||||
fail(err) {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import store from '@/store'
|
||||
|
||||
function authPermission(permission) {
|
||||
const all_permission = "*:*:*"
|
||||
const permissions = store.getters && store.getters.permissions
|
||||
if (permission && permission.length > 0) {
|
||||
return permissions.some(v => {
|
||||
return all_permission === v || v === permission
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function authRole(role) {
|
||||
const super_admin = "admin"
|
||||
const roles = store.getters && store.getters.roles
|
||||
if (role && role.length > 0) {
|
||||
return roles.some(v => {
|
||||
return super_admin === v || v === role
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
// 验证用户是否具备某权限
|
||||
hasPermi(permission) {
|
||||
return authPermission(permission)
|
||||
},
|
||||
// 验证用户是否含有指定权限,只需包含其中一个
|
||||
hasPermiOr(permissions) {
|
||||
return permissions.some(item => {
|
||||
return authPermission(item)
|
||||
})
|
||||
},
|
||||
// 验证用户是否含有指定权限,必须全部拥有
|
||||
hasPermiAnd(permissions) {
|
||||
return permissions.every(item => {
|
||||
return authPermission(item)
|
||||
})
|
||||
},
|
||||
// 验证用户是否具备某角色
|
||||
hasRole(role) {
|
||||
return authRole(role)
|
||||
},
|
||||
// 验证用户是否含有指定角色,只需包含其中一个
|
||||
hasRoleOr(roles) {
|
||||
return roles.some(item => {
|
||||
return authRole(item)
|
||||
})
|
||||
},
|
||||
// 验证用户是否含有指定角色,必须全部拥有
|
||||
hasRoleAnd(roles) {
|
||||
return roles.every(item => {
|
||||
return authRole(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import tab from './tab'
|
||||
import auth from './auth'
|
||||
import modal from './modal'
|
||||
|
||||
export default {
|
||||
install(Vue) {
|
||||
// 页签操作
|
||||
Vue.prototype.$tab = tab
|
||||
// 认证对象
|
||||
Vue.prototype.$auth = auth
|
||||
// 模态框对象
|
||||
Vue.prototype.$modal = modal
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
export default {
|
||||
// 消息提示
|
||||
msg(content) {
|
||||
uni.showToast({
|
||||
title: content,
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
// 错误消息
|
||||
msgError(content) {
|
||||
uni.showToast({
|
||||
title: content,
|
||||
icon: 'error'
|
||||
})
|
||||
},
|
||||
// 成功消息
|
||||
msgSuccess(content) {
|
||||
uni.showToast({
|
||||
title: content,
|
||||
icon: 'success'
|
||||
})
|
||||
},
|
||||
// 隐藏消息
|
||||
hideMsg(content) {
|
||||
uni.hideToast()
|
||||
},
|
||||
// 弹出提示
|
||||
alert(content, title) {
|
||||
uni.showModal({
|
||||
title: title || '系统提示',
|
||||
content: content,
|
||||
showCancel: false
|
||||
})
|
||||
},
|
||||
// 确认窗体
|
||||
confirm(content, title) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.showModal({
|
||||
title: title || '系统提示',
|
||||
content: content,
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: function(res) {
|
||||
if (res.confirm) {
|
||||
resolve(res.confirm)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// 提示信息
|
||||
showToast(option) {
|
||||
if (typeof option === "object") {
|
||||
uni.showToast(option)
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: option,
|
||||
icon: "none",
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
},
|
||||
// 打开遮罩层
|
||||
loading(content) {
|
||||
uni.showLoading({
|
||||
title: content,
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
// 关闭遮罩层
|
||||
closeLoading() {
|
||||
try {
|
||||
uni.hideLoading()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
export default {
|
||||
// 关闭所有页面,打开到应用内的某个页面
|
||||
reLaunch(url) {
|
||||
return uni.reLaunch({
|
||||
url: url
|
||||
})
|
||||
},
|
||||
// 跳转到tabBar页面,并关闭其他所有非tabBar页面
|
||||
switchTab(url) {
|
||||
return uni.switchTab({
|
||||
url: url
|
||||
})
|
||||
},
|
||||
// 关闭当前页面,跳转到应用内的某个页面
|
||||
redirectTo(url) {
|
||||
return uni.redirectTo({
|
||||
url: url
|
||||
})
|
||||
},
|
||||
// 保留当前页面,跳转到应用内的某个页面
|
||||
navigateTo(url) {
|
||||
return uni.navigateTo({
|
||||
url: url
|
||||
})
|
||||
},
|
||||
// 关闭当前页面,返回上一页面或多级页面
|
||||
navigateBack() {
|
||||
return uni.navigateBack()
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 557 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 347 B |
|
After Width: | Height: | Size: 867 B |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 263 B |
|
After Width: | Height: | Size: 263 B |
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -0,0 +1,90 @@
|
|||
@font-face {
|
||||
font-family: "iconfont";
|
||||
src: url('@/static/font/iconfont.ttf') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-user:before {
|
||||
content: "\e7ae";
|
||||
}
|
||||
|
||||
.icon-password:before {
|
||||
content: "\e8b2";
|
||||
}
|
||||
|
||||
.icon-code:before {
|
||||
content: "\e699";
|
||||
}
|
||||
|
||||
.icon-setting:before {
|
||||
content: "\e6cc";
|
||||
}
|
||||
|
||||
.icon-share:before {
|
||||
content: "\e739";
|
||||
}
|
||||
|
||||
.icon-edit:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
|
||||
.icon-version:before {
|
||||
content: "\e63f";
|
||||
}
|
||||
|
||||
.icon-service:before {
|
||||
content: "\e6ff";
|
||||
}
|
||||
|
||||
.icon-friendfill:before {
|
||||
content: "\e726";
|
||||
}
|
||||
|
||||
.icon-community:before {
|
||||
content: "\e741";
|
||||
}
|
||||
|
||||
.icon-people:before {
|
||||
content: "\e736";
|
||||
}
|
||||
|
||||
.icon-dianzan:before {
|
||||
content: "\ec7f";
|
||||
}
|
||||
|
||||
.icon-right:before {
|
||||
content: "\e7eb";
|
||||
}
|
||||
|
||||
.icon-logout:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
|
||||
.icon-help:before {
|
||||
content: "\e616";
|
||||
}
|
||||
|
||||
.icon-github:before {
|
||||
content: "\e628";
|
||||
}
|
||||
|
||||
.icon-aixin:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.icon-clean:before {
|
||||
content: "\e607";
|
||||
}
|
||||
|
||||
.icon-refresh:before {
|
||||
content: "\e604";
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 525 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
|
@ -0,0 +1,75 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="<%= BASE_URL %>static/favicon.ico">
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
|
||||
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
|
||||
<style>
|
||||
/* 重置样式,确保跨平台一致性 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* 修复移动端1px边框问题 */
|
||||
.border-1px {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.border-1px::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border: 1px solid #e0e0e0;
|
||||
transform: scale(0.5);
|
||||
transform-origin: 0 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 修复移动端字体模糊问题 */
|
||||
.text-blur-fix {
|
||||
-webkit-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
/* 修复移动端滚动问题 */
|
||||
.scroll-fix {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 修复移动端点击延迟 */
|
||||
.fast-click {
|
||||
touch-action: manipulation;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>本站点必须要开启JavaScript才能运行.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 866 B |