Compare commits

..

173 Commits

Author SHA1 Message Date
msb_39367 550ea9ddf9 feature: 添加打包资源
1 year ago
王景 a83f41c883 feat: update package-lock
2 years ago
王景 0237f70511 docs: 添加Node版本描述
2 years ago
ch 24df19af14 Merge branch 'fix/0714-ch'
3 years ago
ch fad9ff6ccb fix:修改服务启动名
3 years ago
ch f360233caa Merge branch 'fix/0714-ch' of https://internel-git.mashibing.cn/yanxuan-frontend/shop-app
3 years ago
ch 0508a6d9df pref:固定端口号
3 years ago
ch 041e15495d fix:下架商品
3 years ago
ch 2a42d55fb6 fix:static地址
3 years ago
ch d1f0e5fbd4 支付回调页面
3 years ago
ch 874340f948 修改打包方式
3 years ago
ch 39f379875b pay
3 years ago
ch 5d8a42f48c pay
3 years ago
ch b59d0698ae clear
3 years ago
ch 194587ba1b fix: 商品已下架
3 years ago
ch 9f195f09b7 fix: 商品已下架
3 years ago
ch a35cb72501 fix:点赞提示,已够吗问题
3 years ago
ch c010790f96 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch f20ced40ad 分页最后一页判断修改
3 years ago
ch 03dab5bd96 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 314f79f2ae 分页最后一页判断修改
3 years ago
ch 24ba55fa25 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 7e7ab987b1 分页最后一页判断修改
3 years ago
ch 5832a43169 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch b3cd9615ca 分页最后一页判断修改
3 years ago
ch fb70e2306f 修改分页判断
3 years ago
ch 0ce97eb86b Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 460ba55458 提交
3 years ago
ch 325bee7e80 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch ffc9769565 修改评论跳转
3 years ago
ch 0ce7d20886 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 4011d2d49c 上传预览
3 years ago
ch 774b4f4064 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 4880836a27 产品验收问题
3 years ago
ch 251d091312 Merge branch 'feature/alipay-0629-ch' into msb_test
3 years ago
ch 987566558d 增长支付订单状态查询
3 years ago
ch ffdb3e549f Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch e0b48c9ea6 fix:修改产品验收问题
3 years ago
ch 1e6de755bd APP支付
3 years ago
ch d4a93fed3d Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch b198706159 取消商家回复
3 years ago
ch 6cc4720315 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 71f9e086d7 评论回复实时显示顺序问题
3 years ago
ch df1a27323d Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 4490d1af99 追评
3 years ago
ch f278d52028 修改标签统计接口Token
3 years ago
ch 0391e24745 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 3e9f8e617e fix:回复长英文显示问题
3 years ago
ch 907bcabad2 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 3739301d83 fix:修改页面刷新全局样式丢失问题
3 years ago
ch 5dc332c05f Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 3d61ca3c1e 修改回复自己用户名
3 years ago
ch 410a721436 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 588a41d196 修改
3 years ago
ch 03e27975b4 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 9722f26b90 fix:评论
3 years ago
ch b2d1f2d9c0 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch f6c4182e68 纯空格不让提交
3 years ago
ch efdf803082 纯空格不让提交
3 years ago
ch e5634eeacb 配置支付宝支付
3 years ago
ch d1ba4b1842 Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 2b3e4a954b config
3 years ago
ch 46a6767afb Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch f11b0ee085 config
3 years ago
ch 96def57eaa Merge branch 'feature/comment-0620-ch' into msb_test
3 years ago
ch 6b2adcaf6a config
3 years ago
ch 2f1332b05d Merge branch 'feature/alipay-0629-ch' into msb_test
3 years ago
ch ec932a429b 支付宝支付
3 years ago
ch 5b6e2d46d2 评论统计不做TK校验
3 years ago
ch a27ce47f65 评论统计不做TK校验
3 years ago
ch 6892041093 修改评论数量
3 years ago
ch a75e6b163c fix:去追评报错
3 years ago
ch 98f910dbe9 fix:评论商品信息跳转到详情
3 years ago
ch 1d3fa277b2 fix:其他评价的页面点击事件
3 years ago
ch ac0fc976ed fix:其他评价的页面点击事件
3 years ago
ch 4e65eeb11a fix: 修复预览相关BUg
3 years ago
ch dd0b6345d5 修改BUg
3 years ago
ch 8f0bc44425 fix:修改点赞
3 years ago
ch 5c62b79812 修改图标
3 years ago
ch 925d13cc40 fix:个人中心添加待评价按钮
3 years ago
ch 90e514436b 修改预览页面
3 years ago
ch 8891cc6734 评价
3 years ago
ch e814e1aa0c Merge branch 'fix/0614-ch' into msb_prod
3 years ago
ch 5aff24319d fix:修改?
3 years ago
ch 47c4d8c11c Merge branch 'fix/0614-ch' into msb_prod
3 years ago
ch c2d0857ca1 fix:消息不显示
3 years ago
ch eacb092b13 feat:修改微信支付参数
3 years ago
ch 017b4da59a fix:会话最后一条消息内容英文强制换行
3 years ago
ch c62a4ec0c9 fix:会话最后一条消息内容英文强制换行
3 years ago
ch c9aa0198ff fix:英文换行
3 years ago
ch bbfb4e3108 fix:英文换行
3 years ago
ch f6fb143a69 解决uui 冲突
3 years ago
ch 226109cdbf fix:update sdk
3 years ago
ch 0d24392296 fix:英文强制换行
3 years ago
ch cdceda6747 fix:退出页面清除当前选中
3 years ago
ch 07f0f61983 fix: 客服转移消息
3 years ago
ch d694741ee1 pref:客服转移提示
3 years ago
ch 9067de54da fix:修改提示消息
3 years ago
ch ae4a4284c9 fix: 在当前会话页面刷新报错问题
3 years ago
ch 99f950804b fix:update im SDK
3 years ago
ch 118282854d fix:update IM SDK
3 years ago
ch e3fd51b216 fix:修改SKD
3 years ago
ch 653bbf90f7 fix: imSdk
3 years ago
ch ecd5c60430 头像 昵称
3 years ago
ch d5ec34c4da fix:删除xxx
3 years ago
ch 71e6293742 fix: IM退出数据清空问题
3 years ago
ch 58657621d9 打包命令退回
3 years ago
ch a29f1065e4 fix: 修改发送商品不显示问题
3 years ago
ch e7c43936f1 添加uuid
3 years ago
ch 5e79d78bfc pref:打包测试
3 years ago
ch c474aa0d26 修改
3 years ago
ch 4ca27cdf8a fix:创建会话未设置当前会话ID
3 years ago
ch daac64b0c9 fix:判断socket打开才创建会话
3 years ago
ch 2df056765f fix: 会话列表展示
3 years ago
ch 0df5bbcc18 fix: 会话ID取错修改
3 years ago
ch 5d795b11b4 取消创建会话调已读
3 years ago
ch 536067e197 添加uuid
3 years ago
ch 51ee94d573 修改SDK
3 years ago
ch 0bdab1c4bf im升级
3 years ago
ch e891706f3d im
3 years ago
ch 317cf6225d fix:分类广告
3 years ago
ch f6bdf195c0 fix: 修复虚拟商品优化问题
3 years ago
ch 02552acb69 fix:调整广告跳转方式判断
3 years ago
ch d6516bbfcf 广告跳转
3 years ago
ch 7e4dd1b9ca pref:页面路由组件文件夹重命名
3 years ago
ch 34852eeec8 Merge branch 'fix/0523-ch' into msb_test
3 years ago
ch debe46ebfb 正常消息
3 years ago
ch 75b09e3653 Merge branch 'fix/0523-ch' into msb_test
3 years ago
ch 048171750a 修改消息组展示
3 years ago
ch f29f120e0c Merge branch 'fix/0523-ch' into msb_test
3 years ago
ch 9bd1c4ba50 打开客服
3 years ago
ch 73f29f9490 Merge branch 'fix/0523-ch' into msb_test
3 years ago
ch 46aa9ad5e7 链接跳转
3 years ago
ch 9ec2ca4f1b Merge branch 'fix/0523-ch' into msb_test
3 years ago
ch 502f445666 发货消息
3 years ago
ch 5575d46da0 Merge branch 'fix/0523-ch' into msb_test
3 years ago
ch b0f9249b5b 修改支付跳转
3 years ago
ch d586378092 Merge branch 'fix/0523-ch' into msb_test
3 years ago
ch 5a2d4195b6 修改支付跳转
3 years ago
ch 99e2219f92 Merge branch 'fix/0523-ch' into msb_test
3 years ago
ch 3dc6c233af 虚拟商品订单详情
3 years ago
ch 02127626e3 Merge branch 'fix/0523-ch' into msb_test
3 years ago
ch c4b9f8a4a2 打包问题解决
3 years ago
ch b82d29807f Merge branch 'msb_test' of http://internel-git.mashibing.cn/yanxuan-frontend/shop-app into msb_test
3 years ago
ch b2f9127904 Merge branch 'fix/0523-ch' into msb_test
3 years ago
覃昌波 ea1b0e80cc 更新 Dockerfile
3 years ago
ch a40aa558e1 添加文件
3 years ago
ch 693cc0417a 预订单下单加参数
3 years ago
ch 7df16f45ca 修改打包命令
3 years ago
ch 2734b3c272 pref:提交订单时,虚拟商品不传地址Id
3 years ago
ch 1173416e6c pref:购物车不允许多类型商品同时结算
3 years ago
ch 742b847a45 系统消息跳转
3 years ago
ch 5100284312 pref:IM消息重发
3 years ago
ch 29b6ff483c perf:工作流程改成cli
3 years ago
ch 7477feaf3b perf:环境变量
3 years ago
ch abe6b4f405 perf:删除unpackage目录
3 years ago
ch 567ee059a1 perf:增加js-util-all库引用
3 years ago
ch 3f3b164c64 fix:删除多余im图标
3 years ago
陈辉 86f6c2afeb Merge branch 'msb_test' into 'msb_prod'
3 years ago
ch 5fec7cde3b 打包
3 years ago
ch 8164a9cd76 Merge branch 'fix/0521-ch' into msb_test
3 years ago
ch 07d765b228 Merge branch 'fix/0521-ch' into msb_test
3 years ago
ch 462a98ed19 打包
3 years ago
ch bf72b62c13 修改未读
3 years ago
ch bd2ccede25 打包
3 years ago
ch f5a52891a9 处理商品发送显示空的问题
3 years ago
ch ae7a2e8400 打包
3 years ago
ch 9750bd2740 解决重商品跳进来没历史消息的问题,创建会话后SessionID没辅助导致
3 years ago
ch 188e746404 打包
3 years ago
ch 598ab73781 修改Im
3 years ago
ch 649b91d8a1 第一版IM封装
3 years ago
ch 990cd56690 IM 封装
3 years ago
ch 0cf30a999f IM封装
3 years ago

@ -0,0 +1,3 @@
VUE_APP_BASE_URL = https://k8s-horse-gateway.mashibing.cn
VUE_APP_STATIC_URL = https://k8s-shop-app.mashibing.cn
VUE_APP_IM_URL = wss://k8s-horse-gateway.mashibing.cn

@ -0,0 +1,3 @@
VUE_APP_BASE_URL = https://you-gateway.mashibing.com
VUE_APP_STATIC_URL = https://you-app.mashibing.com
VUE_APP_IM_URL = wss://you-gateway.mashibing.com

@ -0,0 +1,3 @@
VUE_APP_BASE_URL = https://you-gateway.mashibing.com
VUE_APP_STATIC_URL = https://you-app.mashibing.com
VUE_APP_IM_URL = wss://you-gateway.mashibing.com

@ -0,0 +1,3 @@
VUE_APP_BASE_URL = https://k8s-horse-gateway.mashibing.com
VUE_APP_STATIC_URL = https://k8s-shop-app.mashibing.com
VUE_APP_IM_URL = wss://k8s-horse-gateway.mashibing.cn

32
.gitignore vendored

@ -1,8 +1,28 @@
.hbuilderx/
.DS_Store
node_modules/
unpackage/
dist/
.history/
.hbuilderx/
env.js
node_moudel/
package-lock.json
unpackage/dist/build/.automator/
unpackage/dist/dev
unpackage/cache
yarn.lock
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.project
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

@ -1,4 +1,4 @@
FROM nginx
COPY unpackage/dist/build/h5 /usr/share/nginx/html
COPY dist/build/h5 /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

@ -2,21 +2,31 @@
* @Author: ch
* @Date: 2022-03-17 11:30:06
* @LastEditors: ch
* @LastEditTime: 2022-03-22 18:07:54
* @LastEditTime: 2022-05-23 21:18:34
* @Description: file content
-->
# shopping-app
严选移动端
## 前置环境
由于项目依赖问题,开发 `node` 版本必须是 `14` or `16`
如果想安装多个版本,可以通过使用 `nvm` 进行版本管理。然具体安装可以参考下面教程
[window install nvm](https://juejin.cn/post/7074108351524634655)
[mac install nvm](https://juejin.cn/post/7206882855200047161)
## 公共方法utils
- 公共方法统一放置utils文件夹内可以按分类建方法文件 如验证类verify.js 请求类request.js
## 公共方法 utils
- 公共方法统一放置 utils 文件夹内,可以按分类建方法文件 如:验证类 verify.js 请求类 request.js
- 所有公共方法采用大驼峰命名法
- 所有的方法都从index.js输出引入时统一引入index不允许直接引入方法文件
- 所有的方法都从 index.js 输出,引入时统一引入 index不允许直接引入方法文件
- 所有方法文件如果导出的是多个方法,不允许在定义方法时导出,必须在文件底部一一导出,并附上方法简单的注释
``` js
```js
// 正确
import {Req, IsPhone} from '@/common/utils';
@ -39,17 +49,20 @@ export const IsPhone = (str) => {....}
export const IsEmail = (str) => {....}
```
## 组件
- 请务必使用easycom模式引入第三方组件
- 根目录的components 只放置真正的组件某个页面的业务模块应该在pages的相应目录下新建components目录放置
- 所有的自定义组件文件名以大驼峰命名且在templet中使用也用大驼峰形式使用
- 请务必使用 easycom 模式引入第三方组件
- 根目录的 components 只放置真正的组件,某个页面的业务模块应该在 pages 的相应目录下新建 components 目录放置
- 所有的自定义组件文件名以大驼峰命名,且在 templet 中使用也用大驼峰形式使用
## 请求
- 所有请求方法命名以Api+请求类型+具体方) 法命名
- 所有请求使用ToAsyncAwait 包裹
- 不允许使用try catch 和 then 处理返回结果
``` js
- 所有请求使用 ToAsyncAwait 包裹
- 不允许使用 try catch 和 then 处理返回结果
```js
// 使用示例
// xxapi.js
import {ToAsyncAwait, ReqestTk} from '@/common/utils'
@ -60,7 +73,7 @@ export const IsEmail = (str) => {....}
}
// user.vue
improt {ApiGetUserInfo} from '@/common/api/xxapi.js';
const getUserInfo = async () =>{
const {error, result} = await ApiGetUserInfo();
if(error){
@ -71,6 +84,8 @@ export const IsEmail = (str) => {....}
}
```
## 图片
- 按pages目录结果放置图片
- 有公共使用的图片请放到static/common文件夹内
- 按 pages 目录结果放置图片
- 有公共使用的图片请放到 static/common 文件夹内

@ -0,0 +1,63 @@
const plugins = []
if (process.env.UNI_OPT_TREESHAKINGNG) {
plugins.push(require('@dcloudio/vue-cli-plugin-uni-optimize/packages/babel-plugin-uni-api/index.js'))
}
if (
(
process.env.UNI_PLATFORM === 'app-plus' &&
process.env.UNI_USING_V8
) ||
(
process.env.UNI_PLATFORM === 'h5' &&
process.env.UNI_H5_BROWSER === 'builtin'
)
) {
const path = require('path')
const isWin = /^win/.test(process.platform)
const normalizePath = path => (isWin ? path.replace(/\\/g, '/') : path)
const input = normalizePath(process.env.UNI_INPUT_DIR)
try {
plugins.push([
require('@dcloudio/vue-cli-plugin-hbuilderx/packages/babel-plugin-console'),
{
file (file) {
file = normalizePath(file)
if (file.indexOf(input) === 0) {
return path.relative(input, file)
}
return false
}
}
])
} catch (e) {}
}
process.UNI_LIBRARIES = process.UNI_LIBRARIES || ['@dcloudio/uni-ui']
process.UNI_LIBRARIES.forEach(libraryName => {
plugins.push([
'import',
{
'libraryName': libraryName,
'customName': (name) => {
return `${libraryName}/lib/${name}/${name}`
}
}
])
})
module.exports = {
presets: [
[
'@vue/app',
{
modules: 'commonjs',
useBuiltIns: process.env.UNI_PLATFORM === 'h5' ? 'usage' : 'entry'
}
]
],
plugins
}

File diff suppressed because one or more lines are too long

@ -1,85 +0,0 @@
/**
* 枚举类
* Enum.IMAGE.name => "图片"
* Enum.getNameByKey('IMAGE') => "图片"
* Enum.getValueByKey('IMAGE') => 10
* Enum.getNameByValue(10) => "图片"
* Enum.getData() => [{key: "IMAGE", name: "图片", value: 10}]
*/
class Enum {
constructor (param) {
const keyArr = []
const valueArr = []
if (!Array.isArray(param)) {
throw new Error('param is not an array!')
}
param.map(element => {
if (!element.key || !element.name) {
return
}
// 保存key值组成的数组方便A.getName(name)类型的调用
keyArr.push(element.key)
valueArr.push(element.value)
// 根据key生成不同属性值以便A.B.name类型的调用
this[element.key] = element
if (element.key !== element.value) {
this[element.value] = element
}
})
// 保存源数组
this.data = param
this.keyArr = keyArr
this.valueArr = valueArr
// 防止被修改
// Object.freeze(this)
}
// 根据key得到对象
keyOf (key) {
return this.data[this.keyArr.indexOf(key)]
}
// 根据key得到对象
valueOf (key) {
return this.data[this.valueArr.indexOf(key)]
}
// 根据key获取name值
getNameByKey (key) {
const prop = this.keyOf(key)
if (!prop) {
throw new Error('No enum constant' + key)
}
return prop.name
}
// 根据value获取name值
getNameByValue (value) {
const prop = this.valueOf(value)
if (!prop) {
throw new Error('No enum constant' + value)
}
return prop.name
}
// 根据key获取value值
getValueByKey (key) {
const prop = this.keyOf(key)
if (!prop) {
throw new Error('No enum constant' + key)
}
return prop.key
}
// 返回源数组
getData () {
return this.data
}
}
export default Enum

@ -1,62 +0,0 @@
/*
* @Author: ch
* @Date: 2022-03-22 18:28:52
* @LastEditors: ch
* @LastEditTime: 2022-04-29 14:13:05
* @Description: file content
*/
import Vue from 'vue'
import Vuex from 'vuex';
Vue.use(Vuex);
const
// token
TOKEN = 'tk',
// 用户信息
USER_INFO = 'ui',
// 地址列表
ADDRESS = 'ads',
// oppenId
OPPED_ID = 'oi';
export default new Vuex.Store({
state : {
token : uni.getStorageSync(TOKEN) || '',
userInfo : JSON.parse(uni.getStorageSync(USER_INFO) || '{}'),
address : JSON.parse(uni.getStorageSync(ADDRESS) || '[]'),
sessionData : [],
sessionMsgCount : 0,
sessionMsgId : '',
openId : uni.getStorageSync(OPPED_ID) || ''
},
mutations:{
SET_TOKEN (state, token = ''){
state.token = token;
uni.setStorageSync(TOKEN, token);
},
SET_USER_INFO (state, userInfo = {}){
state.userInfo = userInfo;
uni.setStorageSync(USER_INFO, JSON.stringify(userInfo));
},
SET_ADDRESS (state, address = []){
state.address = address;
uni.setStorageSync(ADDRESS, JSON.stringify(address));
},
SET_SESSION_DATA (state, data){
state.sessionData = data ;
},
SET_SESSION_MSG_COUNT (state, data){
state.sessionMsgCount = data;
},
SET_OPEN_ID (state, data){
state.openId = data;
uni.setStorageSync(OPPED_ID, data);
},
SET_SESSION_MSG_ID (state, data){
state.sessionMsgId = data;
}
},
actions : {
}
})

@ -1,18 +0,0 @@
/*
* @Author: ch
* @Date: 2022-03-22 16:52:28
* @LastEditors: ch
* @LastEditTime: 2022-05-06 21:06:14
* @Description: 所有工具类统一在这输出
*/
import * as util from './utils';
import * as requset from './requset';
import * as websocket from './requset';
import * as wxpay from './wxpay';
export * from './utils';
export * from './requset';
export * from './websocket';
export * from './wxpay';
export default { ...util, ...requset, ...websocket, ...wxpay}

@ -1,57 +0,0 @@
/*
* @Author: ch
* @Date: 2022-05-06 15:33:55
* @LastEditors: ch
* @LastEditTime: 2022-05-06 21:02:39
* @Description: file content
*/
import { ApiPostWxH5Pay, ApiPostWxJsApiPay } from '@/common/api/wx';
import { Wxpay } from './wxpay';
export const pay = ({orderId, openId, payType})=>{
if(payType === 'wxpay'){
Wxpay({orderId, openId});
}
// #ifdef H5
if(openId) {
// 微信JSAPI
const {error, result} = await ApiPostWxJsApiPay({orderId,openId});
if(error){
uni.$u.toast(error.message);
return false;
}
/*
* 公众号id appId String(16) wx8888888888888888
时间戳 timeStamp String(32) 1414561699 当前的时间
随机字符串 nonceStr String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串不长于32位推荐随机数生成算法
订单详情扩展字符串 package String(128) prepay_id=123456789 统一下单接口返回的prepay_id参数值提交格式如prepay_id=***
签名方式 signType String(32) MD5 签名类型默认为MD5支持HMAC-SHA256和MD5注意此处需与统一下单的签名类型一致
签名 paySign String(64) C380BEC2BFD727A4B6845133519F3AD6 签名
*/
const par = result.dataInfo;
WeixinJSBridge.invoke('getBrandWCPayRequest', {
appId : par.appId,
timeStamp : par.timeStamp,
nonceStr : par.nonceStr,
package : par.packageValue,
signType : par.signType,
paySign : par.paySign
}, res => {
if(res.err_msg !== 'get_brand_wcpay_request:cancel'){
uni.navigateTo({
url : `/payResult?orderId=${orderId}&payType=wxjsapi`
});
}
})
}else{
// h5支付
const {error, result} = await ApiPostWxH5Pay({orderId});
if(error){
uni.$u.toast(error.message);
return false;
}
const redirect_url = decodeURIComponent(`https://you-app.mashibing.com/payResult?orderId=${orderId}&payType=wxh5`);
window.location.href = `${result.dataInfo.payUrl}&redirect_url=${redirect_url}`;
}
// #ednif
}

@ -1,176 +0,0 @@
/*
* @Author: ch
* @Date: 2022-03-17 19:15:10
* @LastEditors: ch
* @LastEditTime: 2022-05-07 17:18:50
* @Description: 一些无法归类的公共方法容器
*/
/**
* 处理async await 标识结果处理
*
* @param {*} promise promise对象
* @param {*} isFromatResult 是否处理结果
* @returns {error,result} error有错为错误对象没错为null result正确的返回结果
* isFromatResult 为false时直接返回promise对象不做任何处理
*
*/
const ToAsyncAwait = (promise, isFromatResult = true) => {
if(!isFromatResult){
return promise;
}else{
return promise.then((res) => ({error:null,result:res})).catch((err) => ({error:err,result:null}));
}
}
/**
* 首次运行时把定时器赋值给一个变量 第二次执行时
* 如果间隔没超过定时器设定的时间则会清除掉定时器
* 重新设定定时器 依次反复 当我们停止下来时
* 没有执行清除定时器 超过一定时间后触发回调函数
*/
const Debounce = (fn, delay) => {
let timer
return function() {
const that = this
const _args = arguments // 存一下传入的参数
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function() {
fn.apply(that, _args)
}, delay)
}
}
/**
* 匹配phone
*/
const IsPhone = (str) => /^(1[3-9]\d{9})$/.test(str);
/**
* 判断数值类型包括整数和浮点数
*/
const IsNumber = (str) => (isDouble(str) || isInteger(str)) ? true : false;
/**
* 匹配integer
*/
const IsInteger = (str) => {
if (str == null || str == "") return false
var result = str.match(/^[-\+]?\d+$/)
if (result == null) return false
return true
}
/**
* 匹配double或float
*/
const IsDouble = (str) => {
if (str == null || str == "") return false
var result = str.match(/^[-\+]?\d+(\.\d+)?$/)
if (result == null) return false
return true
}
/**
*
* 时间格式化
* @param {number|string|Date} d 时间参数能被new Date识别的数字字符串日期
* @param {string} fmt 时间格式参数 字符串类型 默认'yyyy/mm/dd'
*/
const formatDate = (date, fmt = 'yyyy/mm/dd' ) =>{
// 处理不识别的时间表示字符串如2020年01月01日00时00分00秒
if(date.constructor === String){
date = date.replace(/\D+/ig,'/');
let arr = date.split('/');
// 长度大于3说明带了时分秒信息 特殊时间处理格式
if(arr.length > 3){
let time = ` ${arr[3]}:${arr[4]}:${arr[5]}`
arr.length = 3;
date = arr.join('/') + time;
}
};
try{
date = date ? date.constructor === Date ? date : new Date(date) : new Date();
} catch(e){
throw new Error('不能识别的时间格式');
}
const o = {
'm+': date.getMonth() + 1, //月份
'd+': date.getDate(), //日
'h+': date.getHours(), //小时
'i+': date.getMinutes(), //分
's+': date.getSeconds(), //秒ji“
'q+': Math.floor((date.getMonth() + 3) / 3), //季度
'l+': date.getMilliseconds() //毫秒
};
if (/(y+)/i.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear().toString()).substr(4 - RegExp.$1.length));
}
for (let k in o) {
if (new RegExp(`(${k})`, 'i').test(fmt)) {
const str = o[k].toString();
fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : (`0${str}`).substr(str.length-1));
}
}
return fmt;
}
const createUUID = (len,radix) => {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
let uuid = [],
i=0;
radix = radix || chars.length;
if(len){
for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
}else{
let r = 0;
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random()*16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join('');
}
const toSearchJson = (search)=>{
search = search.replace(/\?/g,'&');
let searchArr = search.split('&'),
obj = {};
searchArr.forEach(i =>{
const me = i.split('=');
if(me[0]){
obj[me[0]] = decodeURIComponent(me[1]);
}
});
return obj;
}
const getPlatform = ()=>{
}
// 工具类的文件需要把文件提供的工具类统一放最下方做一个统一输出
export {
// async await 标识结果处理
ToAsyncAwait,
// 防抖函数
Debounce,
// 判断是否为手机号
IsPhone,
// 判断是否为数字
IsNumber,
// 判断是否为整数
IsInteger,
// 判断是否double或float
IsDouble,
// 时间格式化
formatDate,
createUUID,
toSearchJson
}

@ -1,156 +0,0 @@
/*
* @Author: ch
* @Date: 2022-04-25 14:39:19
* @LastEditors: ch
* @LastEditTime: 2022-05-17 18:14:57
* @Description: file content
*/
import store from "../store";
import {createUUID, formatDate} from '@/common/utils';
let sessionListFlag = false;
const sessionList = (ctx) => {
sessionListFlag = true;
// 来新消息先查会话列表是否一存在,存在则加消息数。不存在则向会话列表加一个会话框
ctx.sessionVOS.forEach(item => {
if (item.lastMessage) {
item.lastMessage.createTimeStamp = formatDate(item.lastMessage.createTimeStamp, 'mm-dd hh:ii')
item.lastMessage.payload = JSON.parse(item.lastMessage.payload || {});
}
let historyData = store.state.sessionData;
let hisIndex = historyData.findIndex(i => i.id === item.id);
if(hisIndex >= 0){
historyData[hisIndex].lastMessage = item.lastMessage;
historyData[hisIndex].unreadCount++;
store.commit('SET_SESSION_DATA', historyData);
}else{
item.messageList = [];
store.commit('SET_SESSION_DATA',[...historyData, item]);
}
});
// 消息总数
store.commit('SET_SESSION_MSG_COUNT', ctx.totalUnreadCount);
}
const historyMsg = (ctx) => {
if(!ctx.length){
return false;
}
// ctx.reverse();
let newData = store.state.sessionData;
const hisIdx = newData.findIndex(i => i.id === ctx[0].sessionId);
ctx.forEach(item => {
item.createTimeStamp = formatDate(item.createTimeStamp, 'mm-dd hh:ii');
item.payload = JSON.parse(item.payload)
})
// newData[hisIdx].messageList = ctx.concat(newData[hisIdx].messageList);
newData[hisIdx].messageList = newData[hisIdx].messageList.concat(ctx);
store.commit('SET_SESSION_DATA', newData);
}
/**
* 收到某个会话的信息
* @param {*} ctx
*/
const sessionMsg = (ctx)=>{
// 没接到会话列表时接到的消息不做处理
if(!sessionListFlag){
return
}
ctx.payload = JSON.parse(ctx.payload || {});
ctx.createTimeStamp = formatDate(ctx.createTimeStamp || new Date(), 'mm-dd hh:ii')
let historyData = store.state.sessionData;
const hisIndex = historyData.findIndex(i => i.id === ctx.sessionId);
// 不在当前会话框则全局消息加1
if(ctx.sessionId !== store.state.sessionMsgId){
store.commit('SET_SESSION_MSG_COUNT', store.state.sessionMsgCount + 1);
}
if(hisIndex >= 0){
// 存在会话往现有会话增加一条消息
const curHisData = historyData[hisIndex];
// curHisData.messageList = [ ctx, ...(curHisData.messageList || [])]
curHisData.messageList.push(ctx); //= [ ctx, ...(curHisData.messageList || [])]
// 不在当前会话框则会话框消息加1
if(ctx.sessionId !== store.state.sessionMsgId){
curHisData.unreadCount++;
}
store.commit('SET_SESSION_DATA', historyData);
}else{
// 会话列表不存在,则创建一个会话
store.commit('SET_SESSION_DATA',[...historyData, {
fromAvatar : ctx.fromAvatar,
fromId : ctx.fromId,
fromNickname : ctx.fromNickname,
id : ctx.id,
lastMessage : ctx,
messageList : [ctx],
unreadCount : 1
}]);
}
}
// 用户自己创建了一个会话
const createSessionMain = (ctx) => {
let historyData = store.state.sessionData;
const curHis = historyData.find(i => i.id === ctx.id);
store.commit('SET_SESSION_MSG_ID', ctx.id);
if (curHis) {
return false;
}
store.commit('SET_SESSION_DATA',[...historyData, {
...ctx,
unreadCount : 0,
messageList : []
}]);
}
export let MsbWebSkt = null;
const token = 'eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjQwNzA4ODAwMDAsImlkIjo5LCJ0aW1lIjoxNjUyNTAwMDUyNTgwfQ.VAMmJTU1FAcw-vD39E_7EHoDaEdKD3A53L-kdcF6Hj_ygs1urs7xfTNsE2Z_Tb5wXRkS_j-qm0pGnRgfD2EQebc8Pcd28-Rp2W8o-CmCw2_uZUZbfUGeFRZ_ihsFCCpsepU7uEkA9L--cI_u3jw9xdNqzkiXYAVeAXsJ2j570bk'
export const MsbWebSktInit = () => {
return new Promise((resolve, reject) => {
MsbWebSkt = uni.connectSocket({
url : `wss://you-gateway.mashibing.com/ws?client=${store.state.token}&type=1`, // url是websocket连接ip
// url : `wss://k8s-horse-gateway.mashibing.cn/ws?client=${store.state.token}&type=1`, // url是websocket连接ip
// url : `ws://192.168.10.93:8090/ws?client=${token}&type=1`, // url是websocket连接ip
fail: e => {
reject(e)
}
});
MsbWebSkt.onOpen(() => {
MsbWebSkt.onMessage((res) => {
const data = JSON.parse(res.data || {});
if (data.code === 200) {
const ctx = data.content;
switch (data.traceType) {
// 会话列表
case 1:
sessionList(ctx);
break;
// 历史消息
case 2:
case 23:
historyMsg(ctx);
break;
// 创建会话
case 21:
createSessionMain(ctx);
break;
// 会话消息
case 25:
case 20:
case 7:
sessionMsg(ctx)
break;
default:
break;
}
}
})
resolve();
});
MsbWebSkt.onClose(() => {
console.log('断开。。。。。');
})
})
}

@ -1,39 +0,0 @@
/*
* @Author: ch
* @Date: 2022-04-19 16:14:03
* @LastEditors: ch
* @LastEditTime: 2022-04-19 17:20:08
* @Description: file content
*/
import Confirm from '../UiConfirm.vue';
export default {
install (Vue) {
// 创建构造类
const ConfirmConstructor = Vue.extend(Confirm)
const showNextConfirm = function (options) {
// 实例化组件
const instance = new ConfirmConstructor({
el: document.createElement('div')
})
// 处理参数
for (const prop in options) {
instance[prop] = options[prop];
}
// 插入Body
document.body.appendChild(instance.$el)
Vue.nextTick(() => {
instance.show = true
})
}
const confirmFn = (options) => {
return showNextConfirm(options);
}
Vue.prototype.$msb ?
Vue.prototype.$msb.confirm = confirmFn :
Vue.prototype.$msb={confirm : confirmFn }
}
}

@ -1,27 +0,0 @@
<!--
* @Author: ch
* @Date: 2022-05-05 10:57:05
* @LastEditors: ch
* @LastEditTime: 2022-05-05 16:53:23
* @Description: file content
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

23183
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,27 +1,123 @@
{
"name": "shopping-app",
"version": "1.0.0",
"description": "严选",
"main": "main.js",
"name": "shop-app",
"version": "0.1.0",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"server": "npm run dev:h5",
"server:test": "npm run dev:h5-test",
"server:bate": "npm run dev:h5-bate",
"server:prod": "npm run dev:h5-prod",
"build:test": "npm run build:h5-test",
"build:bate": "npm run build:h5-bate",
"build:prod": "npm run build:h5-prod",
"build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build",
"build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
"build:h5-test": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build --mode test",
"build:h5-bate": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build --mode bate",
"build:h5-prod": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build --mode prod",
"build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build",
"build:mp-alipay": "cross-env NODE_ENV=production UNI_PLATFORM=mp-alipay vue-cli-service uni-build",
"build:mp-baidu": "cross-env NODE_ENV=production UNI_PLATFORM=mp-baidu vue-cli-service uni-build",
"build:mp-jd": "cross-env NODE_ENV=production UNI_PLATFORM=mp-jd vue-cli-service uni-build",
"build:mp-kuaishou": "cross-env NODE_ENV=production UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build",
"build:mp-lark": "cross-env NODE_ENV=production UNI_PLATFORM=mp-lark vue-cli-service uni-build",
"build:mp-qq": "cross-env NODE_ENV=production UNI_PLATFORM=mp-qq vue-cli-service uni-build",
"build:mp-toutiao": "cross-env NODE_ENV=production UNI_PLATFORM=mp-toutiao vue-cli-service uni-build",
"build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build",
"build:mp-xhs": "cross-env NODE_ENV=production UNI_PLATFORM=mp-xhs vue-cli-service uni-build",
"build:quickapp-native": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-native vue-cli-service uni-build",
"build:quickapp-webview": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview vue-cli-service uni-build",
"build:quickapp-webview-huawei": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build",
"build:quickapp-webview-union": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build",
"dev:app-plus": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch",
"dev:custom": "cross-env NODE_ENV=development uniapp-cli custom",
"dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve",
"dev:h5-test": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve --mode test",
"dev:h5-bate": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve --mode bate",
"dev:h5-prod": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve --mode prod",
"dev:mp-360": "cross-env NODE_ENV=development UNI_PLATFORM=mp-360 vue-cli-service uni-build --watch",
"dev:mp-alipay": "cross-env NODE_ENV=development UNI_PLATFORM=mp-alipay vue-cli-service uni-build --watch",
"dev:mp-baidu": "cross-env NODE_ENV=development UNI_PLATFORM=mp-baidu vue-cli-service uni-build --watch",
"dev:mp-jd": "cross-env NODE_ENV=development UNI_PLATFORM=mp-jd vue-cli-service uni-build --watch",
"dev:mp-kuaishou": "cross-env NODE_ENV=development UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build --watch",
"dev:mp-lark": "cross-env NODE_ENV=development UNI_PLATFORM=mp-lark vue-cli-service uni-build --watch",
"dev:mp-qq": "cross-env NODE_ENV=development UNI_PLATFORM=mp-qq vue-cli-service uni-build --watch",
"dev:mp-toutiao": "cross-env NODE_ENV=development UNI_PLATFORM=mp-toutiao vue-cli-service uni-build --watch",
"dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch",
"dev:mp-xhs": "cross-env NODE_ENV=development UNI_PLATFORM=mp-xhs vue-cli-service uni-build --watch",
"dev:quickapp-native": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-native vue-cli-service uni-build --watch",
"dev:quickapp-webview": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview vue-cli-service uni-build --watch",
"dev:quickapp-webview-huawei": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build --watch",
"dev:quickapp-webview-union": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build --watch",
"info": "node node_modules/@dcloudio/vue-cli-plugin-uni/commands/info.js",
"serve:quickapp-native": "node node_modules/@dcloudio/uni-quickapp-native/bin/serve.js",
"test:android": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=android jest -i",
"test:h5": "cross-env UNI_PLATFORM=h5 jest -i",
"test:ios": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=ios jest -i",
"test:mp-baidu": "cross-env UNI_PLATFORM=mp-baidu jest -i",
"test:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin jest -i"
},
"repository": {
"type": "git",
"url": "http://internel-git.mashibing.cn/chenhui/shopping-app.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@dcloudio/uni-app-plus": "^2.0.1-34720220422002",
"@dcloudio/uni-h5": "^2.0.1-34720220422002",
"@dcloudio/uni-helper-json": "*",
"@dcloudio/uni-i18n": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-360": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-alipay": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-baidu": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-jd": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-kuaishou": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-lark": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-qq": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-toutiao": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-vue": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-weixin": "^2.0.1-34720220422002",
"@dcloudio/uni-mp-xhs": "^2.0.1-34720220422002",
"@dcloudio/uni-quickapp-native": "^2.0.1-34720220422002",
"@dcloudio/uni-quickapp-webview": "^2.0.1-34720220422002",
"@dcloudio/uni-stat": "^2.0.1-34720220422002",
"@vue/shared": "^3.0.0",
"caniuse-lite": "^1.0.30001640",
"core-js": "^3.6.5",
"flyio": "^0.6.2",
"js-util-all": "^1.0.6",
"mp-html": "^2.2.2",
"regenerator-runtime": "^0.12.1",
"uni-read-pages": "^1.0.5",
"uni-simple-router": "^2.0.7",
"uview-ui": "^2.0.29",
"vuex": "^3.6.2"
"vue": "^2.6.11",
"vuex": "^3.2.0"
},
"devDependencies": {
"@dcloudio/uni-helper-json": "^1.0.13",
"git-repo-info": "^2.1.1"
"@babel/runtime": "~7.12.0",
"@dcloudio/types": "^2.6.7",
"@dcloudio/uni-automator": "2.0.2-4010520240507001",
"@dcloudio/uni-cli-i18n": "2.0.2-4010520240507001",
"@dcloudio/uni-cli-shared": "2.0.2-4010520240507001",
"@dcloudio/uni-migration": "2.0.2-4010520240507001",
"@dcloudio/uni-template-compiler": "2.0.2-4010520240507001",
"@dcloudio/vue-cli-plugin-hbuilderx": "2.0.2-4010520240507001",
"@dcloudio/vue-cli-plugin-uni": "2.0.2-4010520240507001",
"@dcloudio/vue-cli-plugin-uni-optimize": "2.0.2-4010520240507001",
"@dcloudio/webpack-uni-mp-loader": "2.0.2-4010520240507001",
"@dcloudio/webpack-uni-pages-loader": "2.0.2-4010520240507001",
"@vue/cli-plugin-babel": "~4.5.15",
"@vue/cli-service": "~4.5.15",
"babel-plugin-import": "1.13.8",
"cross-env": "7.0.3",
"jest": "25.5.4",
"mini-types": "*",
"miniprogram-api-typings": "*",
"postcss-comment": "^2.0.0",
"sass": "^1.52.1",
"vue-template-compiler": "^2.6.11"
},
"browserslist": [
"Android >= 4.4",
"ios >= 9"
],
"uni-app": {
"scripts": {}
}
}

@ -1,926 +0,0 @@
<template>
<view class="chat-list" :class="{'loading':!loading,'isInput':isInput==true,'otherInput':otherInput==true}">
<view v-for="(item,index) in msgData" :key="item.id" :id="item.id" :class="{'nowAt':item.messageId == isNowAit}">
<!-- <view v-if="index>0&&item.sendTime - msgList[index-1].sendTime > 300000">
<view class="chat-item-hint">
<view style="background: none;">{{getTime(item.sendTime)}}</view>
</view>
</view> -->
<!-- 接收的消息 -->
<view class="chat-item-l" v-if="item.fromId != userInfo.id">
<view class="head-img">
<image class="head-img" src="@/static/message/xt.png"></image>
</view>
<view>
<view class="group-user-name clamp" v-if="imObject && imObject.type===2">
{{item.fromNickName||item.nickName}}
</view>
<!-- 文字类型消息 -->
<view v-if="item.type == msgType['txt']" class="chat-item-content" :class="{'jump-box':false}">
<!-- 普通文字消息 -->
<view>{{item.ctx}}</view>
</view>
<!-- 图片类型消息 -->
<view v-if="item.type == msgType['img']" class="chat-item-content-voice" >
<img class="des-img" mode='aspectFit' :src="item.ctx" @click="previewImg(item.ctx)">
</img>
</view>
<!-- 视频类型消息 -->
<view v-if="item.type == msgType['video']" class="chat-item-content-video" @click="clickVideo(item)">
<image class="v-icon" src="@/static/im/ship@2x.png" mode=""></image>
<view class="v-bottom">
<image class="v-b-icon" src="@/static/im/xiazai.png"></image>
</view>
</view>
</view>
</view>
<!-- 发送的消息 -->
<view class="chat-item-r" v-else >
<!-- 文字类型消息 -->
<view v-if="item.type == msgType['txt']" class="chat-item-content" :style="{'maxWidth':Width + 'px'}">
<view v-html="getUrl(item.content)"></view>
</view>
<!-- 图片类型消息 -->
<view v-if="item.type == msgType['img']" class="chat-item-content-voice" :style="{'maxWidth':Width + 'px'}">
<image class="des-img" mode='aspectFit' :src="item.ctx" @click="previewImg(item.ctx)">
</image>
</view>
<!-- 视频类型消息 -->
<view v-if="item.type == msgType['video']" class="chat-item-content-video" @click="clickVideo(item)">
<image class="v-icon" src="@/static/im/ship@2x.png" mode=""></image>
<view class="v-bottom">
<image class="v-b-icon" src="@/static/im/xiazai.png"></image>
</view>
</view>
<!-- 头像 -->
<view class="head-img">
<image class="head-img" src="@/static/message/xt.png"></image>
</view>
</view>
<view v-if="item.type === msgType['tips']">
<view class="chat-item-hint">
<view>{{item.ctx}}</view>
</view>
</view>
</view>
<!-- <view class="ait-jump" @click="aitJump"> -->
<view class="ait-jump" v-if="aitMsgId" @click="aitJump">
<image src="@/static/im/aitbtn1.png"></image>
<text>有人@</text>
<image @click.stop="aitMsgId=''" src="@/static/im/aitbtn2.png"></image>
</view>
<!-- <yinrh-menu-popup v-model="popup" :pop-data="menu" dynamic :x="x" :y="y" direction="row" theme="dark"
:placement="place" @tapPopup="tapPopup" /> -->
</view>
</template>
<script>
import {MSG_TYPE} from '@/common/dicts/im';
export default {
props: {
imObject: {
type: Object,
default: () => {}
},
isInput: {
type: Boolean,
default: false
},
otherInput: {
type: Boolean,
default: false
},
unReadCount: {
type: [String, Number],
default: 0
},
identity: {
type: [String, Number],
default: 0
}
},
data() {
return {
msgType : MSG_TYPE,
msgData :[
{id : 1, fromId : 9, type:1, ctx : 'ype 1文本消息 2语音消息 3图片消息 4视频消息 7提示消息'},
{id : 5, fromId : 3, type:1, ctx: 'ype 1文本消息 2语音消息 3图片消息 4视频消息 7提示消息'},
{id : 2, fromId : 9, type:7, ctx: '给你移动了客服'},
{id : 3, fromId : 2, type:3, ctx : 'https://msb-edu-dev.oss-cn-beijing.aliyuncs.com/mall-product/product2bca55bb5c3adb4d02a7a76039d097eewww800-800.jpg'},
{id : 3, fromId : 2, type:3, ctx : 'https://msb-edu-dev.oss-cn-beijing.aliyuncs.com/uc/account-avatar/aitbtn1.png'},
// {id : 4, formId : 9, type:4},
],
Width: '',
userNo: '',
historyParams: {},
msgList: [],
pullLoad: true,
firstPage: true,
loading: true,
popup: false,
aitMsgId: '',
isNowAit: '',
menu: [{
title: '转发'
}, {
title: '撤回'
}, {
title: '收藏'
}, {
title: '复制'
}],
value: false, // menutrue-
place: 'bottom-end',
x: 0,
y: 0,
selectMsg: {},
//
innerAudioContext: {},
//
audioItem: {}
}
},
created() {
// let app = this;
// uni.getSystemInfo({
// success: function(res) {
// app.Width = res.windowWidth - 116;
// app.Height = res.windowHeight;
// }
// });
// app.innerAudioContext = uni.createInnerAudioContext();
// app.innerAudioContext.autoplay = true;
// app.innerAudioContext.onEnded(() => {
// app.stopAudio()
// })
// //
// app.userNo = uni.getStorageSync('loginToken').userNo
// // app.historyParams = {
// // sessionId: app.imObject.sessionId,
// // msgCount: 100,
// // msgId: '',
// // }
// app.firstPage = true;
// app.msgList = [];
// let msgData = this.imDB.where({
// sessionId: app.imObject.sessionId
// }).limit(100).orderBy('messageId', 'desc').get()
// msgData = msgData.reverse()
// if (app.unReadCount > 0 || msgData.length <= 0) {
// app.getHistoryMsg();
// app.$emit('clearUnReadCount')
// } else {
// app.randerList(msgData)
// app.firstPage = false;
// }
//pushlist
},
beforeDestroy() {
this.chatListOnload()
},
computed: {
imDB: function() {
return this.$store.state.imDB;
},
userInfo (){
return this.$store.state.userInfo
}
},
methods: {
aitJump() {
let app = this;
uni.createSelectorQuery().select(".chat-list").boundingClientRect(data => { //
uni.createSelectorQuery().select("#msg" + app.aitMsgId).boundingClientRect((res) => { //
uni.pageScrollTo({
duration: 0, //0uniapp bug
scrollTop: res.top - data.top, //
})
app.isNowAit = app.aitMsgId
app.aitMsgId = ''
}).exec()
}).exec();
},
chatListOnload() {
uni.$off('scoket')
this.innerAudioContext.stop();
// this.msgList = [];
},
reprot(item) {
},
getUrl(str) {
},
fnThrottle(method, delay, duration) {
},
getHistoryMsg() {
},
randerList(list) {
},
getContent(item) {
},
clickVideo(item) {
let app = this;
uni.showModal({
title: '提示',
content: '暂时无法播放视频,是否下载视频文件?',
success: function(res) {
if (res.confirm) {
app.download(item)
}
}
});
},
download(item) {
if (item.isLoading) {
return false;
}
this.downloadFile(item)
},
scrollTop() {
if (this.pullLoad) {
this.getHistoryMsg();
}
},
tapPopup(e) {
},
copy() {
},
deleteMessage() {
},
forwardMsg() {
},
previewImg(logourl) {
let _this = this;
let imgsArray = [];
imgsArray[0] = logourl
uni.previewImage({
current: 0,
urls: imgsArray
});
},
playAudio(item) {
},
stopAudio() {
},
getSize(num) {
},
getTime(time) {
return dateFormat(time, 'yyyy-MM-dd HH:mm')
},
jumpPages(type, id, flag) {
},
delUnReadCount(res) {
let app = this;
delUnReadCount({
sessionId: res.sessionId,
userId: app.userNo,
platform: 2
})
}
}
}
</script>
<script module="test" lang="renderjs">
export default {
mounted() {
window.addEventListener("click", function(e) {
if (e.target.id == 'isa') {
uni.navigateTo({
url: 'pages/open-url/index?src=' + e.target.innerHTML
})
}
});
},
}
</script>
<style lang="scss" scoped>
.chat-item-content {
color: #0062CC;
position: relative;
.file-loading {
width: 22rpx;
height: 22rpx;
position: absolute;
left: -50rpx;
}
.file-fail {
height: 80rpx;
width: 110rpx;
position: absolute;
left: -110rpx;
background: url('@/static/im/fail.png') no-repeat;
background-position-x: 20rpx;
background-position-y: top;
background-size: 50rpx 50rpx;
&::before {
position: absolute;
content: '发送失败';
font-size: 20rpx;
font-family: PingFang SC;
font-weight: 400;
color: #999999;
width: 200rpx;
top: 52rpx;
}
}
}
.otherInput {
padding-bottom: 500rpx !important;
}
.isInput {
// padding-bottom: 718rpx !important;
}
.loading {
opacity: 0;
}
.chat-list {
padding: 20rpx 32rpx;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
.chat-item-time {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
font-size: 24rpx;
font-family: DIN;
font-weight: 500;
color: #69707F;
margin: 30rpx 0;
}
.chat-item-hint {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
view {
background: #EEEEEE;
border-radius: 25rpx;
padding: 12rpx 42rpx;
font-size: 24rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
}
}
.chat-item-l {
display: flex;
align-items: top;
margin: 20rpx 0;
.head-img {
width: 64rpx;
height: 64rpx;
margin-right: 20rpx;
border-radius: 50%;
}
.group-user-name {
font-size: 28rpx;
color: #999999;
padding-bottom: 12rpx;
max-width: 300rpx;
}
.jump-box {
position: relative;
padding-bottom: 56rpx !important;
}
.chat-item-content {
background: #FFFFFF;
border-radius: 2rpx 16rpx 16rpx 16rpx;
margin-right: 84rpx;
background-color: #fff;
box-sizing: border-box;
padding: 20rpx 30rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
display: flex;
align-items: center;
word-break: break-all;
white-space: pre-wrap;
.audio-icon {
width: 24rpx;
height: 32rpx;
}
}
.chat-item-content-video {
width: 287rpx;
height: 161rpx;
background: #DCDCDC;
border-radius: 2rpx 16rpx 16rpx 16rpx;
position: relative;
.v-icon {
position: absolute;
left: 120rpx;
top: 48rpx;
width: 46rpx;
height: 46rpx;
}
.v-bottom {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 40rpx;
background: rgba(0, 0, 0, 0.4);
border-radius: 2rpx 16rpx 16rpx 16rpx;
.v-b-icon {
position: absolute;
width: 23rpx;
height: 22rpx;
right: 20rpx;
bottom: 9rpx;
}
}
}
.chat-item-content-voice {
display: flex;
align-items: center;
width: auto;
max-width: 400rpx;
height: auto;
// height: 320rpx;
// background: #FB3A4E;
border-radius: 2rpx 2rpx 16rpx 16rpx;
font-size: 28rpx;
font-family: DIN;
font-weight: 500;
color: #333333;
// padding: 30rpx;
.des-img,
.des-video {
// width: 100%;
height: 150rpx;
// margin-right: 15rpx;
}
}
.chat-item-content-datum {
width: 465rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: #FFFFFF;
border-radius: 2rpx 16rpx 16rpx 16rpx;
margin-right: 84rpx;
background-color: #fff;
box-sizing: border-box;
padding: 35rpx 30rpx;
.datum-info {
display: flex;
flex-flow: column;
flex: 1;
.datum-title {
flex: 1;
width: 300rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 32rpx;
line-height: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
}
.datum-size {
margin-top: 15rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
}
}
}
.chat-item-content-picture {
display: flex;
align-items: center;
border-radius: 2rpx 16rpx 16rpx 16rpx;
background-color: #fff;
box-sizing: border-box;
overflow: hidden;
image {
width: 213rpx;
}
}
}
.chat-item-r {
display: flex;
align-items: top;
justify-content: flex-end;
margin: 20rpx 0;
>.head-img {
margin-left: 20rpx;
}
.head-img {
width: 64rpx;
height: 64rpx;
padding: 0;
border-radius: 50%;
}
.chat-item-content {
background: #FB3A4E;
border-radius: 16rpx 2rpx 16rpx 16rpx;
margin-left: 84rpx;
box-sizing: border-box;
padding: 20rpx 30rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FFFFFF;
word-break: break-all;
white-space: pre-wrap;
display: flex;
align-items: center;
.audio-icon {
width: 24rpx;
height: 32rpx;
}
}
.chat-item-content-video {
width: 287rpx;
height: 161rpx;
background: #DCDCDC;
border-radius: 16rpx 2rpx 16rpx 16rpx;
position: relative;
.v-icon {
position: absolute;
left: 120rpx;
top: 48rpx;
width: 46rpx;
height: 46rpx;
}
.v-bottom {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 40rpx;
background: rgba(0, 0, 0, 0.4);
border-radius: 2rpx 2rpx 16rpx 16rpx;
.v-b-icon {
position: absolute;
width: 23rpx;
height: 22rpx;
right: 20rpx;
bottom: 9rpx;
}
}
}
.chat-item-content-voice {
display: flex;
align-items: center;
justify-content: flex-end;
width: auto;
max-width: 400rpx;
height: auto;
// height: 320rpx;
// background: #FB3A4E;
border-radius: 16rpx 2rpx 16rpx 16rpx;
font-size: 28rpx;
font-family: DIN;
font-weight: 500;
color: #333333;
// padding: 30rpx;
.des-img,
.des-video {
// width: 100%;
height: 150rpx;
// margin-right: 15rpx;
}
}
.chat-item-content-datum {
width: 465rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: #FFFFFF;
border-radius: 16rpx 2rpx 16rpx 16rpx;
margin-left: 84rpx;
background-color: #fff;
box-sizing: border-box;
padding: 35rpx 30rpx;
position: relative;
.file-loading {
width: 22rpx;
height: 22rpx;
position: absolute;
left: -50rpx;
}
.file-fail {
height: 80rpx;
width: 110rpx;
position: absolute;
left: -110rpx;
background: url('@/static/im/fail.png') no-repeat;
background-position-x: 15rpx;
background-position-y: top;
background-size: 50rpx 50rpx;
&::before {
position: absolute;
content: '发送失败';
font-size: 20rpx;
font-family: PingFang SC;
font-weight: 400;
color: #999999;
width: 200rpx;
top: 52rpx;
}
}
.datum-info {
display: flex;
flex-flow: column;
flex: 1;
.datum-title {
flex: 1;
width: 300rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 32rpx;
line-height: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
}
.datum-size {
margin-top: 15rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
}
}
}
.chat-item-content-picture {
display: flex;
align-items: center;
border-radius: 16rpx 2rpx 16rpx 16rpx;
background-color: #fff;
box-sizing: border-box;
overflow: hidden;
image {
width: 213rpx;
}
}
}
}
.datum-type {
image {
width: 74rpx;
height: 77rpx;
}
}
.jump-ques {
color: #00CCFF;
}
.voice-mask {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba($color: #000000, $alpha: 0.6);
z-index: 1003;
.vm-box {
width: 617rpx;
height: 514rpx;
background: #FFFFFF;
border-radius: 16rpx;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 40rpx;
.vm-title {
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
margin-bottom: 25rpx;
}
.vm-user {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.vmu-img {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.vmu-txt {
font-size: 30rpx;
font-family: PingFang SC;
font-weight: bold;
color: #333333;
}
}
.vm-msg {
width: 377rpx;
height: 76rpx;
background: #FB3A4E;
border-radius: 2rpx 16rpx 16rpx 16rpx;
display: flex;
align-items: center;
padding-left: 30rpx;
margin-bottom: 20rpx;
.vmm-img {
width: 24rpx;
height: 32rpx;
transform: rotate(180deg);
}
.vmm-txt {
font-size: 28rpx;
font-family: DIN;
font-weight: 500;
color: #F3F5F6;
}
}
.vm-ipt {
width: 537rpx;
height: 68rpx;
background: #F6F6F6;
border-radius: 8rpx;
font-size: 28rpx;
color: #999;
padding-left: 20rpx;
}
.vm-btm {
height: 106rpx;
width: 100%;
position: absolute;
left: 0;
bottom: 0;
display: flex;
border-top: 1rpx solid #F5F6F6;
.vmb-cel,
.vmb-sub {
width: 50%;
height: 100%;
text-align: center;
line-height: 106rpx;
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
}
.vmb-cel {
border-right: 1rpx solid #F5F6F6;
}
}
}
}
.chat-item-l {
/deep/.at_span {
&::after {
content: attr(data-name);
color: #FB3A4E;
}
}
}
.chat-item-r {
/deep/.at_span {
&::after {
content: attr(data-name);
color: #ffffff;
}
}
}
.ait-jump {
position: fixed;
right: 0rpx;
bottom: 200rpx;
width: 273rpx;
height: 78rpx;
background: #FFFFFF;
box-shadow: 0rpx 4rpx 30rpx 1rpx rgba(0, 0, 0, 0.06);
border-radius: 100rpx 0rpx 0rpx 100rpx;
z-index: 9999;
display: flex;
align-items: center;
padding: 0 30rpx;
image {
width: 16rpx;
height: 16rpx;
&:nth-child(1) {
margin-right: 20rpx;
}
&:last-child {
margin-left: auto;
}
}
text {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FB3A4E;
}
}
.nowAt {
animation: isAt 3s 1;
-webkit-animation: isAt 3s 1;
/* Safari 和 Chrome */
}
@keyframes isAt {
0% {
background: #eee;
}
100% {
background: #fff;
}
}
</style>

@ -1,913 +0,0 @@
<template>
<!-- -->
<view class="chat-bottom" :style="{'bottom':isInput==true?getKeyHeight()+'px':'0px'}" @touchend='endAudio'>
<view class="bottom-operation">
<view class="goVoice" @click="setAudioShow">
<image src="../../../static/im/voice.png" mode=""></image>
</view>
<!-- <u-input type="textarea" maxlength="300" :clearable="false" height="80rpx" v-model="chatInfo" placeholder-style="color:#999;" placeholder="输入文字..." /> -->
<textarea v-if="!isIos()&&!audioShow" class="textarea" :class="{'textarea2':chatInfo}" maxlength="300"
v-model="chatInfo" @focus='onInput' placeholder-style="color:#999;" placeholder="输入文字..."
:auto-height='true' :adjust-position="false" />
<textarea v-if="isIos()&&!audioShow" class="textarea" :class="{'textarea2':chatInfo}" maxlength="300"
v-model="chatInfo" @focus='onInput' placeholder-style="color:#999;" placeholder="输入文字..."
:auto-height='true' />
<!-- <view class="textarea" v-if="audioShow"></view> -->
<view class="goexpression" @click="setEmojiShow">
<image src="../../../static/im/expression.png" mode=""></image>
</view>
<view v-show="chatInfo" class="send-btn" @touchend.prevent="sendMsg">
发送
</view>
<view class="more" v-show="!chatInfo" @click="setMoreShow">
<image src="../../../static/im/more.png" mode=""></image>
</view>
</view>
<view class="bottom-box">
<view v-show="emojiShow" class="emoji-box">
<view class="tools-box">
<text class="emoji" @click="setEmoji(item)"
v-for="(item,index) in getEmoji()">{{entitiestoUtf16(item)}}</text>
</view>
</view>
<view v-show="moreShow" class="operation-box">
<view class="operation-item" @click="goQuick">
<view class="item-btn">
<image src="../../../static/im/reply.png" mode=""></image>
</view>
<view class="item-hint">回复</view>
</view>
<view class="operation-item" @click="chooseImage">
<view class="item-btn">
<image src="../../../static/im/picture.png" mode=""></image>
</view>
<view class="item-hint">相册</view>
</view>
<view class="operation-item" @click="chooseFile">
<view class="item-btn">
<image src="../../../static/im/file.png" mode=""></image>
</view>
<view class="item-hint">文件</view>
</view>
<view class="operation-item" @click="chatCollect">
<view class="item-btn">
<image src="../../../static/im/collect.png"></image>
</view>
<view class="item-hint">收藏</view>
</view>
</view>
</view>
<u-popup v-model="showAitFlag" mode="bottom" border-radius="16" >
<!-- -->
<view class="ait-box" :style="{'height':isInput==true? (425 - keyHeight/2) +'px':'425px'}">
<view class="ab-title" >
<u-icon class='abt-close' name="arrow-down" color="#333333" size="24"></u-icon>
<text class="abt-txt">选择提醒的人</text>
</view>
<view class="ipt-wrp">
<image class="ipt-icon" src="@/static/sousuo.png"></image>
<input type="text" confirm-type="search" v-model="aitKeyword" class="ipt" placeholder="昵称搜索" >
</view>
<view class="ab-item" @click="setAit()">
<view class="abi-all">@</view>
<view class="abi-name">@所有人({{groupPeoList.length}})</view>
</view>
<view class="ab-item" @click="setAit(item)" v-for="(item,index) in groupPeoList" v-if="item.nickName.includes(aitKeyword)">
<image class="abi-img" :src="item.avatar"></image>
<view class="abi-name clamp">{{item.nickName}}</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import {
utf16toEntities,
entitiestoUtf16
} from '@/common/js/utils.js'
import emojiData from '@/common/js/emoji.js'
import {
sendMsg,
sendGroupMsg,
sendSpecialMessage,
sendTextMsg,
getUserByGroupId
} from '@/common/api-request/im.js'
import cCircle from "@/components/Your_Exios-Circle/Your_Exios-Circle.vue"
import api from '@/common/js/api.js'
const baseUrl = api.host['local'];
export default {
components: {
cCircle
},
props: {
toUserNo: {
type: [String, Number],
default: ''
},
sendType: {
type: [String, Number],
default: ''
},
isInput: {
type: Boolean,
default: false
},
keyHeight: {
type: Number,
default: 0
},
isScreen: {
type: Boolean,
default: false
}
},
data() {
return {
chatInfo: '',
userNo: '',
moreShow: false,
emojiShow: false,
audioShow: false,
showAitFlag: false,
groupPeoList: [],
recorderManager: {}, //
innerAudioContext: {}, //
voicePath: [], //
timer: null, //
audioText: '长按录音',
audioStart: false,
sendFlag: true, //
voiceCel: false,
stamp: 0, //
txtIndex: 0, //
aitKeyword: '',
isserchinput:false
};
},
created() {
let app = this;
app.userNo = uni.getStorageSync('loginToken').userNo
//
app.recorderManager = uni.getRecorderManager();
console.log('录音对象', app.recorderManager)
app.innerAudioContext = uni.createInnerAudioContext();
app.innerAudioContext.autoplay = true;
app.recorderManager.onStop(function(res) {
console.log('-------停止录音', res)
let now = new Date().getTime()
let obj = {
path: res.tempFilePath,
duration: parseInt((now - app.stamp) / 100) * 100
}
if (obj.duration < 1000) {
uni.showToast({
icon: 'none',
title: '录音太短了!'
})
setTimeout(() => {
app.cancelAudio()
}, 200)
} else {
//
if (!app.voiceCel) {
app.voicePath.push(obj);
app.sendAudio()
}
}
});
if (app.sendType === 2) {
getUserByGroupId(app.toUserNo).then(res => {
console.log('--------------获取群成员', res.data)
app.groupPeoList = res.data
app.groupPeoList.forEach((el, idx) => {
if (el.userNo == app.userNo) {
app.groupPeoList.splice(idx, 1)
}
})
})
}
},
beforeDestroy() {
let app = this;
if (app.innerAudioContext.stop) {
app.innerAudioContext.stop()
}
if (app.timer) {
clearInterval(app.timer)
app.timer = null;
}
},
onShow() {
},
methods: {
getserchInput(){
//
uni.onKeyboardHeightChange(res => {
if (res.height > 0) {
console.log(res.height,'禁停')
// app.keyHeight = res.height
// app.isInput = true;
} else {
// app.isInput = false;
}
})
},
setAit(item) {
let app = this;
app.showAitFlag = false;
//,便
let curEnd = app.chatInfo.slice(app.txtIndex)
let curStr = app.chatInfo.slice(0, app.txtIndex)
if (item) {
app.chatInfo = curStr + item.nickName + '\t' + curEnd
} else {
app.chatInfo = curStr + '所有人' + '\t' + curEnd
}
console.log(item)
},
//
getKeyHeight() {
// if (this.isIos()) {
// if (this.isScreen) {
// return (this.keyHeight - 35)
// } else {
// return (this.keyHeight - 5)
// }
// } else {
// // return this.keyHeight
// return 0
// }
return 0
},
async sendMsg() {
let app = this;
let ids = []
let isAt = false;
if (app.sendType === 2) {
//@\t
let reg = /@.*?\\t/gi;
let result = JSON.stringify(app.chatInfo).match(reg)
if (result) {
for (let i = 0; i < result.length; i++) {
let el = result[i];
let cur = el.slice(1, el.length - 2)
if (cur == '所有人') {
app.groupPeoList.forEach(val => {
ids.push(val.userNo)
})
break;
} else {
app.groupPeoList.forEach(val => {
if (val.nickName == cur) {
ids.push(val.userNo)
}
})
}
console.log(cur)
}
}
isAt = ids.length > 0 ? true : false;
}
if (!app.sendFlag) {
return false;
}
app.sendFlag = false;
let res;
if (!app.chatInfo) {
uni.showToast({
icon: 'none',
title: "请输入聊天内容!"
})
app.sendFlag = true;
return false;
}
if (app.sendType === 1) {
//
res = await sendTextMsg({
content: app.chatInfo,
fromUserNo: app.userNo,
toUserNo: app.toUserNo
})
} else {
//
res = await sendTextMsg({
content: app.chatInfo,
fromUserNo: app.userNo,
groupId: app.toUserNo,
atUsers: ids,
isAt: isAt
})
}
if (res.code === 200) {
app.chatInfo = '';
uni.$emit('scoket', JSON.stringify(res.data))
// app.audioShow = false;
// app.moreShow = false;
// app.emojiShow = false;
} else {
uni.showToast({
icon: 'none',
title: res.msg
})
}
app.sendFlag = true;
},
chooseImage() {
let app = this;
uni.chooseImage({
count: 1, //9
sizeType: ['original', 'compressed'], //
sourceType: ['album', 'camera'], //
success: function(res) {
console.log(res.tempFilePaths[0]);
let filePath = res.tempFilePaths[0]
app.uploadFile(filePath, 1)
}
});
},
chooseFile() {
let app = this;
if (this.isIos()) {
const iOSFileSelect = uni.requireNativePlugin('YangChuan-YCiOSFileSelect');
let params = {
"document-types": ["public.text", "public.zip", "public.movie", "public.data", "com.adobe.pdf",
"com.microsoft.word.doc", "com.adobe.postscript", "com.microsoft.excel.xls",
"com.adobe.encapsulated- postscript", "com.microsoft.powerpoint.ppt",
"com.adobe.photoshop- image", "com.microsoft.word.rtf",
"com.microsoft.advanced- systems-format", "com.microsoft.advanced- stream-redirector"
],
"isBase64": 0
}
iOSFileSelect.show(params, result => {
let filePath = result.url
console.log(result,'result')
let fileName = result.lastName
let fileSize = result.version
app.uploadFile(filePath, 4,fileName,fileSize)
})
} else {
const plugin = uni.requireNativePlugin('GuoWei-SelectFileModule')
plugin.chooseFile({
count: 1
},
result => {
let filePath = result.files[0].url
let fileName = result.files[0].name
let fileSize = result.files[0].size
console.log('-----im文件选择', result)
app.uploadFile(filePath, 4, fileName, fileSize)
}
)
}
},
startAudio() {
console.log('开始录音', this.recorderManager);
let app = this
clearInterval(app.timer)
app.timer = null;
app.stamp = new Date().getTime()
uni.vibrateShort({
success: function() {
console.log('开始录音2', this.recorderManager);
app.timer = setInterval(() => {
uni.showToast({
icon: 'none',
title: '录音时间过长!'
})
app.endAudio()
app.$emit('longStop')
}, 60000)
console.log('开始录音3', this.recorderManager);
app.recorderManager.start();
app.audioText = '松开停止'
}
});
},
endAudio(val = false) {
let app = this;
app.voiceCel = val;
if (app.timer) {
console.log('录音结束');
clearInterval(app.timer)
app.timer = null;
app.recorderManager.stop();
app.audioText = '点击播放'
}
},
playVoice() {
console.log('播放录音');
// console.log('this.voicePath', this.voicePath, this.audioStart);
if (!this.audioStart) {
this.audioStart = true
this.innerAudioContext.src = this.voicePath[0];
this.innerAudioContext.play();
this.audioText = '暂停播放'
} else if (this.audioStart) {
this.audioStart = false
this.innerAudioContext.stop()
this.audioText = '点击播放'
}
},
cancelAudio() {
let app = this;
// app.audioShow = false;
app.moreShow = false;
app.emojiShow = false;
app.timer = null;
app.innerAudioContext.stop()
app.voicePath.length > 0 ? app.voicePath.shift() : app.voicePath = []
app.audioStart = false;
app.audioText = '长按录音'
},
sendAudio() {
let app = this;
if (app.voicePath.length == 1) {
app.uploadFile(app.voicePath[0].path, 2)
}
},
uploadFile(filePath, type, fileName = '', fileSize = 0) {
let header = {}
let app = this;
if (uni.getStorageSync('loginToken')) {
let token = uni.getStorageSync('loginToken');
header['Authorization'] = token.tokenHead + token.token;
header['R-Authorization'] = token.tokenHead + token.refreshToken;
// header['Content-Type'] = 'multipart/form-data'
}
let scoketParams = {
isLoading: 'load',
fromUserNo: app.userNo,
toUserNo: app.sendType == 1 ? app.toUserNo : '',
groupId: app.sendType == 2 ? app.toUserNo : '',
type: type,
fileName: fileName,
fileSize: fileSize,
filePath: filePath,
audioSeconds: app.voicePath && app.voicePath.length > 0 ? app.voicePath[0].duration : ''
}
console.log('ios',scoketParams)
uni.$emit('scoket', JSON.stringify(scoketParams))
uni.uploadFile({
url: baseUrl + '/edu-im/file/upload',
filePath: filePath,
header: header,
name: "file",
success: function(res) {
let resData = JSON.parse(res.data).data
let params = {
content: resData.content,
fileName: resData.filename,
size: resData.size,
fromUserNo: app.userNo,
toUserNo: app.sendType == 1 ? app.toUserNo : '',
groupId: app.sendType == 2 ? app.toUserNo : '',
type: type,
audioSeconds: app.voicePath && app.voicePath.length > 0 ? app.voicePath[0]
.duration : ''
}
sendSpecialMessage(params).then(ret => {
console.log('ret', ret)
if (ret.code === 200) {
console.log('-----------------im发送图片', ret)
app.chatInfo = '';
uni.$emit('scoket', JSON.stringify(ret.data))
app.innerAudioContext.stop()
// app.audioShow = false;
app.moreShow = false;
app.emojiShow = false;
app.audioStart = false;
app.audioText = '长按录音'
if (app.voicePath.length > 1) {
app.voicePath.shift()
setTimeout(() => {
app.sendAudio()
}, 1000)
} else {
app.voicePath = []
}
} else {
uni.showToast({
icon: 'none',
title: ret.msg
})
}
})
},
fail: function(error) {
// uni.showToast({
// title: "", //
// duration: 3000, //
// icon: "none", //"success""loading"
// });
scoketParams.isLoading = 'fail'
uni.$emit('scoket', JSON.stringify(scoketParams))
}
})
},
chatCollect() {
uni.navigateTo({
url: '/pages/im/chat/chatCollect'
})
},
goQuick() {
uni.navigateTo({
url: '/pages/im/quick/index?sendType=' + this.sendType + '&toUserNo=' + this.toUserNo
})
},
getEmoji() {
return [...emojiData[0].list, ...emojiData[1].list, ...emojiData[4].list]
},
setEmoji(data) {
let str = ' ' + this.entitiestoUtf16(data) + ' '
this.chatInfo += str;
},
onInput() {
this.emojiShow = false;
this.audioShow = false;
this.moreShow = false;
this.$emit('setInput', false)
},
setMoreShow() {
this.moreShow = !this.moreShow;
this.emojiShow = false;
this.audioShow = false;
this.$emit('setInput', this.moreShow)
},
setAudioShow() {
this.audioShow = !this.audioShow;
this.emojiShow = false;
this.moreShow = false;
this.$emit('setInput', false)
// this.$emit('setInput', this.audioShow)
},
setEmojiShow() {
// this.chatInfo = '@\t@asdadad1\t2 '
// console.log(JSON.stringify(this.chatInfo))
// return false;
this.emojiShow = !this.emojiShow;
this.moreShow = false;
this.audioShow = false;
this.$emit('setInput', this.emojiShow)
},
utf16toEntities(val) {
return utf16toEntities(val)
},
entitiestoUtf16(val) {
return entitiestoUtf16(val)
},
},
watch: {
audioShow: function(n, o) {
this.$emit('onAudioShow', n)
},
chatInfo: function(n, o) {
let app = this;
if (app.sendType != 2) {
return false;
}
//
uni.getSelectedTextRange({
success: res => {
app.txtIndex = res.end;
//,便
let curEnd = n.slice(res.end)
let curStr = n.slice(0, res.end)
/*
新增艾特相关
*/
let oldStr1 = o.slice(0, res.end)
if (curStr == oldStr1 + '@') {
app.aitKeyword = '';
app.showAitFlag = true;
uni.hideKeyboard()
}
/*
删除艾特相关
*/
//
let oldStr = o.slice(0, res.end + 1)
//\t,
if (curStr.length < oldStr.length && curStr + '\t' == oldStr) {
//
let num = curStr.lastIndexOf('@')
let nowStr = curStr.slice(0, num)
app.chatInfo = nowStr + curEnd;
console.log('删除艾特')
}
}
})
}
}
}
</script>
<style lang="scss">
// .isInput {
// bottom: 570rpx !important;
// }
.chat-bottom {
position: fixed;
bottom: 0;
width: 100%;
min-height: 148rpx;
background-color: #fff;
display: flex;
flex-flow: column;
.bottom-operation {
width: 100%;
display: flex;
align-items: flex-end;
padding: 17px 30rpx;
}
.goVoice {
width: 52rpx;
height: 52rpx;
margin-right: 10rpx;
margin-bottom: 15rpx;
image {
width: 100%;
height: 100%;
}
}
.textarea {
height: 80rpx;
max-height: 200rpx !important;
overflow: auto;
width: 490rpx;
line-height: 80rpx;
background: #F8F8F8;
border-radius: 40rpx;
padding: 0 40rpx;
}
.textarea2 {
width: 420rpx;
line-height: 48rpx;
min-height: 80rpx;
/deep/.uni-textarea-textarea{
padding-top: 16rpx;
}
}
.audio {
flex: 1;
background: #F8F8F8;
border-radius: 39rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333;
height: 78rpx;
text-align: center;
line-height: 78rpx;
}
/deep/.u-input {
flex: 1;
background: #F8F8F8;
border-radius: 39rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333;
max-height: 200rpx !important;
overflow-y: auto;
.u-input__textarea {
display: flex;
align-items: center;
box-sizing: border-box;
padding: 20rpx 40rpx;
max-height: 200rpx;
}
}
.goexpression {
width: 52rpx;
height: 53rpx;
margin-left: 15rpx;
margin-right: 19rpx;
margin-bottom: 15rpx;
image {
width: 100%;
height: 100%;
}
}
.more {
width: 53rpx;
height: 53rpx;
margin-bottom: 15rpx;
image {
width: 100%;
height: 100%;
}
}
.send-btn {
display: flex;
align-items: center;
justify-content: center;
width: 120rpx;
height: 60rpx;
background-color: #fb3a4e;
color: #fff;
font-size: 28rpx;
border-radius: 10rpx;
margin-bottom: 15rpx;
transition: all .3s;
}
.operation-box {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: space-around;
background-color: #f8f8f8;
padding: 0 30rpx;
.operation-item {
width: 120rpx;
padding: 30rpx 0;
.item-btn {
display: flex;
align-items: center;
justify-content: center;
width: 120rpx;
height: 120rpx;
background-color: #fff;
border-radius: 16rpx;
image {
width: 50rpx;
height: 50rpx;
}
}
.item-hint {
text-align: center;
font-size: 24rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
margin-top: 13rpx;
}
}
}
}
.bottom-box {
max-height: 300rpx;
overflow-y: auto;
}
.emoji-box {
padding: 0 30rpx 30rpx;
.emoji {
margin: 10rpx;
font-size: 48rpx;
}
}
.audio-box {
height: 300rpx;
display: flex;
justify-content: center;
align-items: center;
.circle {
margin: 0 100rpx;
width: 150rpx;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.audio-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
margin-bottom: 20rpx;
}
}
}
.ait-box {
width: 750rpx;
height: 850rpx;
background-color: #fff;
z-index: 999;
.ab-title {
position: relative;
display: flex;
justify-content: center;
align-items: center;
margin-top: 40rpx;
.abt-close {
position: absolute;
top: 10rpx;
left: 40rpx;
}
.abt-txt {
font-size: 28rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #000000;
}
}
.ab-item {
width: 750rpx;
height: 120rpx;
display: flex;
padding: 0 40rpx;
align-items: center;
&:hover {
background-color: #EEEEEE;
}
.abi-all {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 30rpx;
background-color: #FFEBED;
text-align: center;
line-height: 80rpx;
font-size: 36rpx;
font-family: SF Pro Display-Medium, SF Pro Display;
font-weight: 500;
color: #FB3A4E;
}
.abi-img {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 30rpx;
}
.abi-name {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
}
}
}
.ipt-wrp {
margin: 18rpx auto;
position: relative;
width: 690rpx;
.ipt-icon {
position: absolute;
left: 28rpx;
height: 24rpx;
width: 24rpx;
top: 20rpx;
}
.ipt {
width: 100%;
height: 64rpx;
background: #f6f6f6;
border-radius: 32rpx;
font-size: 28rpx;
color: #999;
padding-left: 76rpx;
}
}
</style>

@ -1,127 +0,0 @@
<!--
* @Author: ch
* @Date: 2022-03-26 14:32:03
* @LastEditors: ch
* @LastEditTime: 2022-05-17 16:22:13
* @Description: file content
-->
<template>
<view class="main">
<view class="msg-item" v-for="item in msgData" :key="item.id">
<view class="msg-item--title">
<text>{{item.payload.title }} {{item.id}}</text>
<text class="msg-item--time">{{item.createTimeStamp}}</text>
</view>
<view class="msg-item--con">
<image class="msg-item--img" src="@/static/message/xt.png" mode="widthFix"/>
<view class="msg-item--desc">{{item.payload.content}}</view>
</view>
</view>
<u-loadmore status="loading" v-if="!msgData.length"/>
</view>
</template>
<script>
import {MsbWebSkt, createUUID} from '@/common/utils';
export default {
data(){
return {
}
},
computed:{
curSessionData(){
return this.$store.state.sessionData.find(i =>i.id == this.$store.state.sessionMsgId)
},
msgData (){
return this.curSessionData ? this.curSessionData.messageList : [];
}
},
onReachBottom(){
this.getHistoryMsg();
},
onShow(){
if(!this.$store.state.sessionMsgId){
this.$Router.back();
return false
}
uni.setNavigationBarTitle({
title : this.curSessionData.fromNickname
});
this.getHistoryMsg();
this.readMsg();
},
methods:{
/**
* 获取历史消息
*/
getHistoryMsg(){
this.loading = true;
const lastMsg = this.msgData[this.msgData.length - 1] || {};
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "2",
content: {
sessionId : this.$store.state.sessionMsgId,
topMessageId : lastMsg.id || null
}
})
})
},
/**
* 已读消息
*/
readMsg(){
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "6",
content: {
sessionId : this.$store.state.sessionMsgId
}
})
});
//
const count = this.$store.state.sessionMsgCount - this.curSessionData.unreadCount;
//
this.curSessionData.unreadCount = 0;
this.$store.commit('SET_SESSION_MSG_COUNT', count)
}
}
}
</script>
<style lang="scss" scoped>
.msg-item{
padding: 40rpx 0 0 40rpx;
&--title{
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 40rpx;
font-size: $font-size-lg;
color: $color-grey6;
}
&--time{
font-size: $font-size-sm;
color: $color-grey4;
}
&--con{
display: flex;
border-bottom: 1px solid $color-grey2;
padding: 40rpx 0;
}
&--img{
width: 140rpx;
height: 140rpx;
border-radius: 12rpx;
margin-right: 40rpx;
}
&--desc{
width: 455rpx;
font-size: $font-size-base;
line-height: 39rpx;
color: $color-grey5;
}
}
</style>

@ -0,0 +1,22 @@
const path = require('path')
module.exports = {
parser: require('postcss-comment'),
plugins: [
require('postcss-import')({
resolve (id, basedir, importOptions) {
if (id.startsWith('~@/')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3))
} else if (id.startsWith('@/')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2))
} else if (id.startsWith('/') && !id.startsWith('//')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1))
}
return id
}
}),
require('autoprefixer')({
remove: process.env.UNI_PLATFORM !== 'h5'
}),
require('@dcloudio/vue-cli-plugin-uni/packages/postcss')
]
}

@ -0,0 +1,32 @@
<!--
* @Author: ch
* @Date: 2022-05-27 17:44:36
* @LastEditors: ch
* @LastEditTime: 2022-06-29 21:37:10
* @Description: file content
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
<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" />
</head>
<body class="page">
<noscript>
<strong>Please enable JavaScript to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

@ -1,22 +1,23 @@
<!--
* @Author: ch
* @Date: 2019-04-01 01:47:12
* @Date: 2022-05-23 20:13:23
* @LastEditors: ch
* @LastEditTime: 2022-04-30 17:47:26
* @LastEditTime: 2022-05-24 10:00:15
* @Description: file content
-->
<script>
export default {
onLaunch: function() {
// console.log('App Launch')
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
// console.log('App Hide')
console.log('App Hide')
}
}
</script>
<style lang="scss">
@ -37,6 +38,4 @@
/* 兼容 iOS >= 11.2 */
}
/* #endif */
</style>
</style>

@ -0,0 +1,18 @@
/*
* @Author: ch
* @Date: 2022-05-31 11:17:38
* @LastEditors: ch
* @LastEditTime: 2022-05-31 11:35:10
* @Description: file content
*/
import {ToAsyncAwait, MsbRequest} from '@/common/utils';
import { AD_PLATFORM } from '../dicts/ad';
const BASE_URL = '/mall/marketing';
// 获取广告列表
export const ApiGetAdList = (params) => ToAsyncAwait(MsbRequest.get(`${BASE_URL}/app/advertisement`, {
platform : AD_PLATFORM.MOBILE,
...params
}))

@ -2,13 +2,34 @@
* @Author: ch
* @Date: 2022-04-06 17:29:13
* @LastEditors: ch
* @LastEditTime: 2022-04-15 14:17:14
* @LastEditTime: 2022-05-31 11:18:44
* @Description: file content
*/
import {ToAsyncAwait, MsbRequestTk} from '@/common/utils';
import {ToAsyncAwait, MsbRequest, MsbRequestTk} from '@/common/utils';
const BASE_URL = '/mall/base';
const BASE_URL_UC = '/uc';
/**
* 登录
* @param {*} data
*/
export const ApiPostLogin = (data) => ToAsyncAwait(MsbRequest.post(`${BASE_URL_UC}/user/login`, data));
/**
* 退出登录
* @param {*} data
*/
export const ApiGetLogout= () => ToAsyncAwait(MsbRequest.get(`${BASE_URL_UC}/user/logout`));
/**
* 获取手机验证码
* @param {*} params
*/
export const ApiGetCode = (params) => ToAsyncAwait(MsbRequest.get(`${BASE_URL_UC}/user/login/verificationCode`, params));
/**
* 获取收货地址
*/

@ -0,0 +1,66 @@
/*
* @Author: ch
* @Date: 2022-06-20 11:38:48
* @LastEditors: ch
* @LastEditTime: 2022-06-30 11:53:57
* @Description: file content
*/
import {ToAsyncAwait, MsbRequest, MsbRequestTk} from '@/common/utils';
const BASE_URL = '/mall/comment';
/**
* 根据商品获取评论列表
* @param {*} param0
*/
export const ApiGetCommentList = (params) =>
ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/app/comment`,params, {
notVerifyToken: true
}));
/**
* 根据商品获取评论总数
* @param {*} param0
*/
export const ApiGetCommentCount = ({productId}) =>
ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/app/comment/getAllCommentCountByProductId/${productId}`,{}, {
notVerifyToken: true
}));
/**
* 根据商品获取标签评论总数
* @param {*} param0
*/
export const ApiGetCommentTabCount = ({productId}) =>
ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/app/comment/listCommentLabel/${productId}`,{}, {
notVerifyToken: true
}));
/**
* 获取订单评论详情
* @param {*} param0
*/
export const ApiGetOrderCommentDetail = ({orderId}) =>
ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/app/comment/listOrderCommentByOrderId/${orderId}`));
/**
* 获取评论详情
* @param {*} param0
*/
export const ApiGetCommentDetail = ({commentId}) =>
ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/app/comment/getCommentDetail/${commentId}`,{}, {
notVerifyToken: true
}));
/**
* 新增评论
* @param {*} param0
*/
export const ApiPostComment = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/app/comment`, data));
/**
* 更新评论有用数
* @param {*} param0
*/
export const ApiPutCommentUseful = (data) =>
ToAsyncAwait(MsbRequestTk.put(`${BASE_URL}/app/comment/updateUsefulCount`,data));

@ -0,0 +1,16 @@
/*
* @Author: ch
* @Date: 2022-05-27 17:44:36
* @LastEditors: ch
* @LastEditTime: 2022-06-09 11:34:36
* @Description: file content
*/
import {ToAsyncAwait, MsbRequestTk} from '@/common/utils';
const BASE_URL = '/mall/im';
/**
* 获取soket登录秘钥
*/
export const ApiGetSoketTicket = () => ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/ticket`, {
ticketType: 'CONNECT_TICKET'
}));

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-17 16:37:45
* @LastEditors: ch
* @LastEditTime: 2022-05-16 15:52:57
* @LastEditTime: 2022-05-24 17:25:25
* @Description: file content
*/
@ -18,7 +18,7 @@ export const ApiPostLogin = (data) => ToAsyncAwait(MsbRequest.post(`${BASE_URL}/
* 退出登录
* @param {*} data
*/
export const ApiGetLogout= () => ToAsyncAwait(MsbRequest.post(`${BASE_URL}/user/logout`));
export const ApiGetLogout= () => ToAsyncAwait(MsbRequest.get(`${BASE_URL}/user/logout`));
/**
* 获取手机验证码
* @param {*} params

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-29 17:38:17
* @LastEditors: ch
* @LastEditTime: 2022-05-10 16:14:53
* @LastEditTime: 2022-06-23 16:21:32
* @Description: file content
*/
import {ToAsyncAwait, MsbRequestTk} from '@/common/utils';
@ -84,8 +84,11 @@ export const ApiPutCancelOrder = (data) =>
export const ApiGetOrderStatistics = () =>
ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/app/tradeOrder/statistics`));
/**
* 获取待评价订单详请列表
*/
export const ApiGetCommentOrderDetailList = (params) =>
ToAsyncAwait(MsbRequestTk.get(`${BASE_URL}/app/tradeOrder/listOrderProductWaitComment`,params))
/**

@ -0,0 +1,51 @@
/*
* @Author: ch
* @Date: 2022-04-28 16:30:54
* @LastEditors: ch
* @LastEditTime: 2022-07-09 10:16:00
* @Description: file content
*/
import {ToAsyncAwait, MsbRequest, MsbRequestTk} from '@/common/utils';
const BASE_URL = '/mall/trade';
export const ApiPostCashierPrepay = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/payCenter/cashierPrepay`, data));
/**
* 微信h5支付获取支付URL
* @param {*} data
*/
export const ApiPostWxH5Pay = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/payCenter/wxPay/h5`, data));
/**
* 微信JSAPI支付获取支付参数
* @param {*} data
*/
export const ApiPostWxJsApiPay = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/payCenter/wxPay/jsapi`, data));
/**
* 微信APP支付
* @param {*} data
*/
export const ApiPostWxAppPay = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/payCenter/wxPay/app`, data));
/**
* 支付宝APP支付
* @param {*} data
*/
export const ApiPostAliAppPay = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/payCenter/aliPay/app`, data));
/**
* 支付宝h5支付获取支付URL
* @param {*} data
*/
export const ApiPostAliH5Pay = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/payCenter/aliPay/wap`, data));

@ -2,13 +2,12 @@
* @Author: ch
* @Date: 2022-04-28 16:30:54
* @LastEditors: ch
* @LastEditTime: 2022-05-17 20:07:26
* @LastEditTime: 2022-06-29 17:16:50
* @Description: file content
*/
import {ToAsyncAwait, MsbRequest, MsbRequestTk} from '@/common/utils';
const BASE_URL = '/mall/trade';
// 第三方鉴权服务
const AUTH_URL = '/third';
const APPID = 'wxd2015f0c56defa02';
@ -33,24 +32,3 @@ export const ApiGetOpenId = ({code}) =>
*/
export const ApiPostThirdInfo = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${AUTH_URL}/third/saveThirdInfo`, {...data, appId:APPID}));
/**
* 微信h5支付获取支付URL
* @param {*} data
*/
export const ApiPostWxH5Pay = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/pay/wxPay/h5`, data));
/**
* 微信JSAPI支付获取支付参数
* @param {*} data
*/
export const ApiPostWxJsApiPay = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/pay/wxPay/jsapi`, data));
/**
* 微信APP支付
* @param {*} data
*/
export const ApiPostWxAppPay = (data) =>
ToAsyncAwait(MsbRequestTk.post(`${BASE_URL}/pay/wxPay/app`, data));

@ -0,0 +1,42 @@
/*
* @Author: ch
* @Date: 2022-05-31 11:05:08
* @LastEditors: ch
* @LastEditTime: 2022-05-31 17:23:02
* @Description: file content
*/
const AD_PLATFORM = {
PC: 2,
MOBILE : 1
}
const AD_LOCATION = {
// 首页轮播图
HOME_BANNER: 1,
// 首页精装区
HOME_HARDCOVER: 2,
// 分类banner
CATEGORY_BANNER : 3
}
const AD_JUMP_TYPE = {
// 商品详情
GOODS: 1,
// 分类
CATEGORY: 2,
// 链接
LINK: 3,
// 不跳转
NO_JUMP : 4
}
export {
// 广告所属平台
AD_PLATFORM,
// 广告位置
AD_LOCATION,
// 广告跳转类型
AD_JUMP_TYPE
}

@ -0,0 +1,33 @@
/*
* @Author: ch
* @Date: 2022-06-20 11:10:23
* @LastEditors: ch
* @LastEditTime: 2022-06-28 16:33:25
* @Description: file content
*/
export default {
TYPE : {
// 评价
COMMENT: 1,
// 追评
FOLLOW_COMMENT: 2,
// 回复
ANSWER: 3,
},
// 是否显示
DISPLAY: {
SHOW: 1,
HIDE : 0
},
// 用户类型
USER_TYPE: {
STORE : 1,
USER : 2
},
// 是否默认评价
IS_DEFAULT_COMMENT: {
YES: 1,
NOT : 0
},
RATE_LABEL : ['非常不满意','不满意','一般','满意','非常满意']
}

@ -0,0 +1,19 @@
/*
* @Author: ch
* @Date: 2022-05-26 15:39:52
* @LastEditors: ch
* @LastEditTime: 2022-05-26 15:47:28
* @Description: file content
*/
// 商品类型
const GOODS_TYPE = {
// 虚拟
VIRTUAL: 2,
// 实物
ENTITY : 1
}
export {
GOODS_TYPE
}

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-05-10 20:50:54
* @LastEditors: ch
* @LastEditTime: 2022-05-13 14:28:29
* @LastEditTime: 2022-05-23 14:29:55
* @Description: file content
*/
@ -20,16 +20,30 @@ const MSG_TYPE = {
// 提示消息
TIP : 7
}
const TYPE = {
// 系统会话类型
const SYS_IO = {
// 连接心跳
HEART: 0,
// 获取会话列表
GET_SESSION_LIST: 1,
// 查询会话消息
GET_SESSION_MSG: 2,
// 发送消息
SEND_MSG: 3,
// 根据消息ID查询消息
GET_ID_MSG: 4,
}
const KF_IO = {
// 会话心跳
HEART: 22,
// 给某个会话发送消息
SEND_MSG: 20,
// 创建会话
CRETAE_SEEION: 21,
// 会话心跳
SESSION_HEART : 22
}
export {
// 消息类型
MSG_TYPE
MSG_TYPE,
SYS_IO,
KF_IO
}

@ -0,0 +1,35 @@
/*
* @Author: ch
* @Date: 2022-05-26 11:49:16
* @LastEditors: ch
* @LastEditTime: 2022-06-22 11:23:02
* @Description: file content
*/
// 订单状态
export const ORDER_STATUS = {
// 待支付
AWAIT_PAY: 1
}
export default {
TYPE : {
//(1, "普通订单"),
NORMAL: 1,
//(2, "免费订单"),
FREE: 2,
//(3, "秒杀订单"),
SECKILL: 3,
//(4, "虚拟商品订单"),
VIRTUAL : 4
},
// 订单状态 1待支付2已关闭3已支付4已发货5已收货6已完成7已追评
STATUS: {
AWAIT_PAY: 1,
CLOSE: 2,
FINISH_PAY: 3,
FINISH_SEND_GOODS: 4,
FINISH_TAKE_GOODS: 5,
FINISH: 6,
FINISH_FOLLOW_COMMENT : 7
}
}

@ -0,0 +1,483 @@
/*
* @Author: ch
* @Date: 2022-05-18 14:54:47
* @LastEditors: ch
* @LastEditTime: 2022-06-14 17:24:14
* @Description: file content
*/
import { CreateUUID, FormatDate, ToAsyncAwait } from "@/common/utils";
import './potoReq';
import './protoRsp'
const connect = Symbol('connect');
const send = Symbol('send');
const onMessage = Symbol('onMessage');
const fromatPotoReq = (traceId, traceType, content) => {
let messageModel = new proto.ReqModel();
messageModel.setTraceid(traceId);
messageModel.setTracetype(traceType);
content && messageModel.setContent(JSON.stringify(content));
return messageModel.serializeBinary();
},
fromatPotoRsp = (data) => {
const res = proto.RspModel.deserializeBinary(new Uint8Array(data));
let ctx = res.getContent();
ctx = ctx ? JSON.parse(ctx) : {};
if (ctx.payload) {
ctx.payload = JSON.parse(ctx.payload);
}
return {
content: ctx,
traceId: res.getTraceid(),
traceType: res.getTracetype(),
code: res.getCode(),
message: res.getMessage(),
};
};
class MsbIm {
defaultOption = {
ioKey: 'traceId',
reconnect: true,
};
socket = null;
isOpen = false;
queue = {};
interceptors = {
dataChangeBefore: null,
dataChangeAfter: null,
onClose: null,
onMessage: null,
};
sessionData = [];
curSessionId = null;
constructor(option) {
this.option = {
...this.defaultOption,
...option,
};
}
/**
* 创建连接返回一个Promise 创建成功并成功打开连接算连接成功
* @param {*} option
*/
[connect](option) {
return new Promise((resolve, reject) => {
const open = () => {
console.log('[im] open');
this.isOpen = true;
resolve(this.socket);
};
const message = async (res) => {
const result = fromatPotoRsp(res.data);
this.interceptors.onMessage && this.interceptors.onMessage(result);
// 处理服务端主动推送的消息
this[onMessage](result);
// 如果再消息堆里有此消息回调,则执行回调,并删除
const cbk = this.queue[result[this.option.ioKey]];
if (cbk) {
cbk(result.code !== 200 ? { error: result } : { result: result });
delete this.queue[result[this.option.ioKey]];
}
};
const close = () => {
console.log('[im] close');
this.interceptors.onClose && this.interceptors.onClose();
};
let isUni = false;
try {
isUni = uni;
} catch (e) {
isUni = false;
}
if (isUni) {
this.socket = uni.connectSocket({
...this.option,
fail(e) {
reject(e);
},
});
this.socket.onOpen(() => {
open();
this.socket.onMessage((res) => {
message(res);
});
});
this.socket.onClose(() => {
close();
});
} else if (WebSocket) {
try {
this.socket = new WebSocket(this.option.url);
this.socket.binaryType = 'arraybuffer';
this.socket.onopen = () => {
open();
};
this.socket.onmessage = (res) => {
message(res);
};
this.socket.onclose = () => {
close();
};
} catch (e) {
reject(e);
}
}
});
}
/**
* 向服务端发送消息||请求返回一个Promise对象收到ioKey对应的消息会算一个同步完成
* @param {*} data
*/
[send](data) {
return new Promise((resolve, reject) => {
if (!this.isOpen) {
return reject('连接未打开');
}
this.queue[data[this.option.ioKey]] = ({ result, error }) => {
if (result) {
resolve(result);
} else {
reject(error);
}
};
const par = fromatPotoReq(data.traceId, data.traceType, data.content);
let isUni = false;
try {
isUni = uni;
} catch (e) {
isUni = false;
}
if (isUni) {
this.socket.send({
data: par,
fail(e) {
reject({ error: e });
},
});
} else if (WebSocket) {
this.socket.send(par);
}
});
}
/**
* 服务端推送消息只处理服务端主动推送的消息
* @param {*} data
*/
[onMessage](data) {
// 判断非服务端主动推送消息不做处理
if (data[this.option.ioKey] || data.code !== 200) {
return false;
}
console.log('[im] 主动接收的消息', data);
let ctx = data.content;
let historyData = [...this.sessionData],
newData = [];
const hisIndex = historyData.findIndex((i) => i.id === ctx.sessionId);
if (hisIndex >= 0) {
// 存在会话往现有会话增加一条消息,并修改最后一条消息为当前消息
const curHisData = historyData[hisIndex];
curHisData.messageList.push(ctx);
curHisData.lastMessage = ctx;
// 不在当前会话窗口则向会话消息加1条未读
if (ctx.sessionId !== this.curSessionId) {
curHisData.unreadCount++;
} else {
this.setRead({
content: {
sessionId: this.curSessionId,
},
});
}
newData = historyData;
} else {
// 会话列表不存在,则创建一个会话
newData = [
...historyData,
{
fromAvatar: ctx.session.fromAvatar,
fromId: ctx.session.fromId,
fromNickname: ctx.session.fromNickname,
id: ctx.sessionId,
lastMessage: ctx,
messageList: [ctx],
updateTimeStamp: ctx.createTimeStamp,
unreadCount: 1,
},
];
}
this.setSessionData(newData);
}
init(config) {
return new Promise((resolve, reject) => {
const heart = () => {
// 要优化 心跳没回复需要重连
setTimeout(async () => {
if (this.isOpen) {
await this[send]({
traceId: CreateUUID(),
traceType: 0,
content: { text: 'ping' },
});
}
heart();
}, 1000);
};
this.option = {
...this.option,
...config,
};
this[connect]()
.then((res) => {
resolve(res);
heart();
})
.catch((e) => {
console.log('eeeee', e);
});
});
}
/**
* 设置数据
*/
setSessionData(data) {
let newData = JSON.parse(JSON.stringify(data));
this.interceptors.dataChangeBefore && this.interceptors.dataChangeBefore(newData, this.sessionData);
this.sessionData = newData;
this.interceptors.dataChangeAfter && this.interceptors.dataChangeAfter(this.sessionData);
}
/**
* 设置当前聊天窗口
* Data为Session数据
* @param {*} data
*/
setCurSessionId(id) {
this.curSessionId = id;
}
/**
* 获取会话列表
* @param {*} params
*/
async getSessionList(params) {
const par = {
traceId: CreateUUID(),
traceType: 1,
...params,
};
console.log('[im] 获取会话列表--start', par);
let { error, result } = await ToAsyncAwait(this[send](par));
console.log('[im] 获取会话列表--end', result, error);
if (error) {
return Promise.reject(error);
}
const { content } = result;
// let newData = [];
content.sessionVOS.forEach((item) => {
if (item.lastMessage) {
item.lastMessage.payload = JSON.parse(item.lastMessage.payload || {});
}
let historyData = this.sessionData;
let hisIndex = historyData.findIndex((i) => i.id === item.id);
if (hisIndex < 0) {
item.messageList = [];
}
// let historyData = this.sessionData;
// let hisIndex = historyData.findIndex((i) => i.id === item.id);
// if (hisIndex >= 0) {
// historyData[hisIndex].lastMessage = item.lastMessage;
// historyData[hisIndex].unreadCount++;
// newData.push(historyData[hisIndex]);
// } else {
// item.messageList = [];
// newData = [...newData, item];
// }
});
this.setSessionData(content.sessionVOS);
return Promise.resolve(result);
}
/**
* 获取会话的历史消息记录
* @param {*} params
*/
async getHistoryMsg() {
const curSessionIdx = this.sessionData.findIndex((i) => i.id === this.curSessionId);
const curSession = this.sessionData[curSessionIdx];
console.log(curSession, this.curSessionId, 'this.curSessionId');
const msgList = curSession.messageList || [];
const par = {
traceId: CreateUUID(),
traceType: 2,
content: {
sessionId: this.curSessionId,
topMessageId: msgList.length ? msgList[0].id : null,
},
};
console.log('[im] 获取会话历史消息--start', par);
const { error, result } = await ToAsyncAwait(this[send](par));
console.log('[im] 获取会话历史消息--end', result, error);
if (error) {
return Promise.reject(error);
}
const { content } = result;
if (content.length) {
let newData = this.sessionData;
content.forEach((item) => {
item.payload = JSON.parse(item.payload);
});
newData[curSessionIdx].messageList = content.concat(newData[curSessionIdx].messageList);
this.setSessionData(newData);
}
return Promise.resolve(result);
}
/**
* 会话已读
* @param {*} params
*/
async setRead(params) {
const par = {
traceId: CreateUUID(),
traceType: '6',
...params,
};
console.log('[im] 会话已读--start', par);
const { error, result } = await this[send](par);
console.log('[im] 会话已读--end', result, error);
let newData = this.sessionData.map((item) => {
if (item.id == params.content.sessionId) {
item.unreadCount = 0;
}
return item;
});
this.setSessionData(newData);
}
/**
* 发送消息
* @param {*} params
*/
async sendMsg(params) {
const index = this.sessionData.findIndex((i) => i.id === this.curSessionId);
let curSession = this.sessionData[index];
// 临时消息体
let par = {
...params,
traceId: CreateUUID(),
traceType: 3,
};
let msgCtx = {
...params.content,
...par,
createTimeStamp: new Date().getTime(),
sendStatus: 'loading',
};
if (typeof msgCtx.payload === 'string') {
msgCtx.payload = JSON.parse(msgCtx.payload);
}
// 点发送,立即把消息加入消息列表,标记为发送中状态
curSession.lastMessage = msgCtx;
curSession.messageList.push(msgCtx);
this.setSessionData(this.sessionData);
// 超过时间未返回视为发送失败
this.timerStatus(msgCtx);
console.log('[im] 发送消息--start', par);
const { error, result } = await ToAsyncAwait(this[send](par));
console.log('[im] 发送消息--end', result, error);
// 接到通知,标记消息是否发送成功
for (let i = curSession.messageList.length; i--; ) {
const item = curSession.messageList[i];
if (item[this.option.ioKey] === par[this.option.ioKey]) {
curSession.messageList[i].sendStatus = msgCtx.sendStatus = error ? 'fail' : 'success';
break;
}
}
let newData = [...this.sessionData];
curSession.lastMessage = msgCtx;
newData[index] = curSession;
this.setSessionData(newData);
if (error) {
return Promise.reject(error);
}
return Promise.resolve(result);
}
/**
* 发送失败时重新发送
* @param {*} params
*/
async resend(params) {
params.sendStatus = 'loading';
this.timerStatus(params);
console.log('[im] 重新发送消息--start', params);
const { error, result } = await ToAsyncAwait(
this[send]({
traceId: params.traceId,
traceType: params.traceType,
content: params.content,
})
);
console.log('[im] 重新发送消息--end', result, error);
params.createTimeStamp = result.createTimeStamp;
if (error) {
params.sendStatus = 'fail';
return Promise.reject(error);
}
params.sendStatus = 'success';
return Promise.resolve(result);
}
timerStatus(msg) {
setTimeout(() => {
if (msg.sendStatus === 'loading') {
msg.sendStatus = 'fail';
delete this.queue[msg.traceId];
}
}, 3000);
}
/**
* 主动创建会话
* @param {*} params
*/
async createSession(params) {
const par = {
traceId: CreateUUID(),
traceType: 9,
...params,
};
console.log('[im] 主动创建会话--start', par);
const { result, error } = await ToAsyncAwait(this[send](par));
console.log('[im] 主动创建会话--end', result, error);
if (error) {
return Promise.reject(error);
}
const { content } = result;
let historyData = this.sessionData;
let curSession = historyData.find((i) => i.id === content.id);
if (!curSession) {
curSession = {
...content,
unreadCount: 0,
messageList: [],
};
const newData = [...historyData, curSession];
this.setSessionData(newData);
}
return Promise.resolve(result);
}
close() {
this.socket.close();
this.socket = null;
this.isOpen = false;
this.setSessionData([]);
}
}
export default MsbIm;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-18 11:11:05
* @LastEditors: ch
* @LastEditTime: 2022-03-18 11:11:05
* @LastEditTime: 2022-07-12 18:31:15
* @Description: file content
*/
import {RouterMount,createRouter} from 'uni-simple-router';
@ -13,6 +13,11 @@ const router = createRouter({
});
//全局路由前置守卫
router.beforeEach((to, from, next) => {
// 兼容页面刷新body样式丢失问题
// console.log(document);
if (document) {
document.body.setAttribute('class', `uni-body ${to.path.replace('/', '').replace(/\//g, '-')}`)
}
next();
});
// 全局路由后置守卫

@ -0,0 +1,72 @@
/*
* @Author: ch
* @Date: 2022-03-22 18:28:52
* @LastEditors: ch
* @LastEditTime: 2022-06-22 15:31:09
* @Description: file content
*/
import Vue from 'vue'
import Vuex from 'vuex';
Vue.use(Vuex);
const
// token
TOKEN = 'tk',
// 用户信息
USER_INFO = 'ui',
// 地址列表
ADDRESS = 'ads',
// oppenId
OPPED_ID = 'oi',
// 每个浏览器创建一个UUID作为同一个用户的标识
UUID = 'uid',
// 评价图片预览数据
COMMENT_PREVIEW = 'comment_preview';
export default new Vuex.Store({
state: {
token: uni.getStorageSync(TOKEN) || '',
userInfo: JSON.parse(uni.getStorageSync(USER_INFO) || '{}'),
address: JSON.parse(uni.getStorageSync(ADDRESS) || '[]'),
openId: uni.getStorageSync(OPPED_ID) || '',
uuid: uni.getStorageSync(UUID) || '',
comment_preview: JSON.parse(uni.getStorageSync(COMMENT_PREVIEW) || '{}'),
imData : [],
imMsgCount : 0
},
mutations:{
SET_TOKEN (state, token = ''){
state.token = token;
uni.setStorageSync(TOKEN, token);
},
SET_USER_INFO (state, userInfo = {}){
state.userInfo = userInfo;
uni.setStorageSync(USER_INFO, JSON.stringify(userInfo));
},
SET_ADDRESS (state, address = []){
state.address = address;
uni.setStorageSync(ADDRESS, JSON.stringify(address));
},
SET_UUID (state, uid){
state.uuid = uid;
uni.setStorageSync(UUID, uid);
},
SET_OPEN_ID (state, data){
state.openId = data;
uni.setStorageSync(OPPED_ID, data);
},
SET_COMMENT_PREVIEW(state, data) {
state.comment_preview = data;
uni.setStorageSync(COMMENT_PREVIEW, JSON.stringify(data));
},
SET_IM_DATA (state, data){
state.imData = data ;
},
SET_IM_MSG_COUNT (state, data){
state.imMsgCount = data;
}
},
actions : {
}
})

@ -0,0 +1,50 @@
/*
* @Author: ch
* @Date: 2022-04-29 14:26:10
* @LastEditors: ch
* @LastEditTime: 2022-07-13 10:37:21
* @Description: file content
*/
import { ApiPostAliH5Pay, ApiPostAliAppPay } from '@/common/api/pay';
const ENV = process.env;
export const Alipay = async ({orderId})=>{
// #ifdef APP-PLUS
const {error, result} = await ApiPostAliAppPay({orderId});
if(error){
uni.$u.toast(error.message);
return false;
}
const par = result.payDataInfo;
uni.requestPayment({
provider: 'alipay',
orderInfo :par.payData,
success(res) {
uni.navigateTo({
url : `/payResult?orderId=${orderId}&payType=appWx`
});
},
fail(e) {
uni.navigateTo({
url : `/payResult?orderId=${orderId}&payType=appWx`
});
}
}).then(res => {
console.log('res',res);
})
// #endif
// #ifdef H5
const { error, result } = await ApiPostAliH5Pay({
orderId,
returnUrl : decodeURIComponent(`${ENV.VUE_APP_STATIC_URL}/payResult?orderId=${orderId}&payType=alih5`)
});
if(error){
uni.$u.toast(error.message);
return false;
}
window.location.href = result.payDataInfo.payUrl;
// #endif
}

@ -0,0 +1,56 @@
/*
* @Author: ch
* @Date: 2022-05-20 11:00:07
* @LastEditors: ch
* @LastEditTime: 2022-07-13 10:37:52
* @Description: file content
*/
import MsbIm from '@/common/plugins/msbIm' ;
import { ToAsyncAwait, FormatJsonSearch } from '@/common/utils';
import { ApiGetCurrentUser } from '@/common/api/account';
import { ApiGetSoketTicket } from '@/common/api/im';
import $store from '@/common/store';
const ENV = process.env;
const Im = new MsbIm({
reconnect: true,
});
const ImInit = async () => {
const { error, result } = await ApiGetSoketTicket();
if (error) {
return false;
}
const par = FormatJsonSearch({
client: result.client,
ticket: result.ticket,
// 1普通用户 2客服链接
connect: 1,
user: $store.state.userInfo.id,
nickname: $store.state.userInfo.nickname,
avatar : $store.state.userInfo.avatar
})
await ToAsyncAwait(Im.init({
url: `${ENV.VUE_APP_IM_URL}/ws${par}`
}))
};
Im.interceptors.dataChangeAfter = () => {
$store.commit('SET_IM_DATA', Im.sessionData);
let msgCount = 0;
Im.sessionData.forEach(i => {
msgCount += i.unreadCount;
})
$store.commit('SET_IM_MSG_COUNT', msgCount);
}
Im.interceptors.onClose = () => {
Im.setSessionData([]);
Im.setCurSessionId(null);
$store.commit('SET_IM_DATA', []);
$store.commit('SET_IM_MSG_COUNT', 0);
}
export {
Im,
ImInit
}

@ -0,0 +1,23 @@
/*
* @Author: ch
* @Date: 2022-03-22 16:52:28
* @LastEditors: ch
* @LastEditTime: 2022-06-29 17:28:39
* @LastEditTime: 2022-06-29 17:22:32
* @Description: 所有工具类统一在这输出
*/
import * as util from './utils';
import * as requset from './requset';
import * as im from './im';
import * as wxpay from './wxpay';
import * as uploadFileOss from './uploadFileOss';
import * as alipay from './alipay';
export * from './utils';
export * from './requset';
export * from './im';
export * from './wxpay';
export * from './uploadFileOss';
export * from './alipay';
export default { ...util, ...requset, ...im, ...wxpay, ...uploadFileOss, ...alipay}

@ -0,0 +1,13 @@
/*
* @Author: ch
* @Date: 2022-05-06 15:33:55
* @LastEditors: ch
* @LastEditTime: 2022-07-13 10:38:34
* @Description: file content
*/
import { Wxpay } from './wxpay';
export const pay = ({orderId, openId, payType})=>{
if(payType === 'wxpay'){
Wxpay({orderId, openId});
}
}

@ -2,20 +2,21 @@
* @Author: ch
* @Date: 2022-03-17 17:42:32
* @LastEditors: ch
* @LastEditTime: 2022-05-17 18:15:04
* @LastEditTime: 2022-07-13 10:39:07
* @Description: 项目接口请求统一处理器返回一个需要token和不需要token的请求封装方法
*/
import MsbUniRequest from '@/common/plugins/msbUniRequest';
import $store from '@/common/store';
import { CreateUUID } from '@/common/utils';
const ENV = process.env;
const ENV = 'prod';
const BASE_URL = {
'test' : 'https://k8s-horse-gateway.mashibing.cn',
'dev' : '',
'release' : '',
'prod' : 'https://you-gateway.mashibing.com'
};
// 获取已有的UUID没则创建一个并保存到locaStorage中下次使用
let uuid = $store.state.uuid
if (!uuid) {
uuid = CreateUUID(16, 2);
$store.commit('SET_UUID', uuid);
}
/**
* 接口返回成功结果统一处理
* @param {*} response
@ -81,7 +82,7 @@ const clearRepeat = (option) =>{
// 不需要token的接口封装
const MsbRequest = new MsbUniRequest();
MsbRequest.baseUrl = BASE_URL[ENV];
MsbRequest.baseUrl = ENV.VUE_APP_BASE_URL;
MsbRequest.use('request', (option) => {
if(option.header.repeat){
@ -91,6 +92,7 @@ MsbRequest.use('request', (option) => {
return isRepeatVerify;
}
}
option.header.uid = uuid;
return option;
})
MsbRequest.use('success', successIntercept);
@ -100,28 +102,29 @@ MsbRequest.use('error', errorIntercept);
// 需要token的接口封装
const MsbRequestTk = new MsbUniRequest();
MsbRequestTk.baseUrl = BASE_URL[ENV];
MsbRequestTk.baseUrl = ENV.VUE_APP_BASE_URL;
MsbRequestTk.use('request', (option) => {
const token = $store.state.token;
if(!token){
option.header.uid = uuid;
if(!token && !option.header.notVerifyToken){
// 登录状态处理没有token直接跳转至登录
uni.redirectTo({
url: '/login'
});
return Promise.reject({message:'要先登录才能操作哦~'});
}else{
option.header = {...option.header, Authorization:token};
if(option.header.repeat){
// 如果当前请求不允许重复调用,则检查重复请求,当前接口有正在请求则不发起请求
const isRepeatVerify = repeatVerify(option);
if(isRepeatVerify){
return isRepeatVerify;
}
}
delete option.header.notVerifyToken
option.header = {...option.header, Authorization:token};
if(option.header.repeat){
// 如果当前请求不允许重复调用,则检查重复请求,当前接口有正在请求则不发起请求
const isRepeatVerify = repeatVerify(option);
if(isRepeatVerify){
return isRepeatVerify;
}
return option;
}
return option;
})
MsbRequestTk.use('success', successIntercept);
MsbRequestTk.use('error', errorIntercept);

@ -0,0 +1,46 @@
/*
* @Author: ch
* @Date: 2022-06-21 15:03:11
* @LastEditors: ch
* @LastEditTime: 2022-06-22 14:49:20
* @Description: file content
*/
import { ApiPostGetOssConfig } from '@/common/api/oss';
import {ToAsyncAwait} from '@/common/utils'
/**
*
* @param {*} file 文件
* @param {*} param1 获取OSS鉴权信息参数参考ApiPostGetOssConfig接口
* @returns {Promise}
*/
export const uploadFileOss = (file, {configId, serviceName}) =>{
return ToAsyncAwait(new Promise(async (resolve, reject) => {
const urlArr = file.url.split('/');
const fileName = file.name || urlArr[urlArr.length - 1];
const { error, result:oss } = await ApiPostGetOssConfig({ configId : `${configId}/`, serviceName });
if (error) {
reject(error);
return false
}
uni.uploadFile({
name : 'file',
filePath : file.url,
url : oss.host,
formData : {
name : fileName,
key : `${oss.dir}${'${filename}'}`,
policy : oss.policy,
OSSAccessKeyId : oss.accessId,
success_action_status : 200,
signature : oss.signature
},
success : async (res)=>{
resolve(`${oss.host}/${oss.dir}${fileName}`);
},
fail(e) {
reject(e);
}
})
}
))
}

@ -0,0 +1,99 @@
/*
* @Author: ch
* @Date: 2022-03-17 19:15:10
* @LastEditors: ch
* @LastEditTime: 2022-07-13 10:39:35
* @Description: 一些无法归类的公共方法容器
*/
import {
toAsyncAwait as ToAsyncAwait,
isPhone as IsPhone,
formatDate as FormatDate,
creatUuid as CreateUUID,
formatSearchJson as FormatSearchJson,
formatJsonSearch as FormatJsonSearch
} from "js-util-all";
import {AD_JUMP_TYPE} from '@/common/dicts/ad';
/**
* 首次运行时把定时器赋值给一个变量 第二次执行时
* 如果间隔没超过定时器设定的时间则会清除掉定时器
* 重新设定定时器 依次反复 当我们停止下来时
* 没有执行清除定时器 超过一定时间后触发回调函数
*/
const Debounce = (fn, delay) => {
let timer
return function () {
const that = this
const _args = arguments // 存一下传入的参数
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function () {
fn.apply(that, _args)
}, delay)
}
}
/**
* 广告跳转
* 兼容APP根据域名判断站内跳转还是站外跳转
* @param {*} link
*/
const AdJump = (item) => {
switch (item.jumpType) {
case AD_JUMP_TYPE.GOODS:
uni.navigateTo({
url: `/goodsDetail?id=${item.jumpUrl}`
});
break;
case AD_JUMP_TYPE.CATEGORY:
uni.navigateTo({
url: `/goodsList?categoryId=${item.jumpUrl}`
});
break;
case AD_JUMP_TYPE.LINK:
// #ifdef H5
window.location.href = item.jumpUrl;
// #endif
// #ifdef APP-PLUS
plus.runtime.openURL(item.jumpUrl);
// #endif
break;
default:
break
}
}
/**
* api接口错误处理
* @param {*} error 错误体
* @param {*} name 接口名 非必传如操作时错误提示不需要提示接口名时可不传
* @return Boolean true 存在错误并处理 false无错误
*/
const HandleApiError = (error, name) => {
let result = false
if (error) {
const tip = name ? `${name}错误:` : ``;
uni.$u.toast(`${tip}${error.message}`);
result = true;
}
return result;
}
// 工具类的文件需要把文件提供的工具类统一放最下方做一个统一输出
export {
// async await 标识结果处理
ToAsyncAwait,
// 判断是否为手机号
IsPhone,
// 时间格式化
FormatDate,
FormatSearchJson,
FormatJsonSearch,
CreateUUID,
// 防抖函数
Debounce,
// 广告跳转
AdJump,
HandleApiError
}

@ -2,11 +2,12 @@
* @Author: ch
* @Date: 2022-04-29 14:26:10
* @LastEditors: ch
* @LastEditTime: 2022-05-07 17:07:08
* @LastEditTime: 2022-07-13 10:40:10
* @Description: file content
*/
import { ApiPostWxH5Pay, ApiPostWxJsApiPay, ApiPostWxAppPay } from '@/common/api/wx';
import { ApiPostWxH5Pay, ApiPostWxJsApiPay, ApiPostWxAppPay } from '@/common/api/pay';
const ENV = process.env;
export const Wxpay = async ({orderId,openId})=>{
// #ifdef APP-PLUS
@ -15,7 +16,7 @@ export const Wxpay = async ({orderId,openId})=>{
uni.$u.toast(error.message);
return false;
}
const par = result.dataInfo;
const par = result.payDataInfo;
uni.requestPayment({
provider: 'wxpay',
orderInfo :{
@ -51,12 +52,12 @@ export const Wxpay = async ({orderId,openId})=>{
uni.$u.toast(error.message);
return false;
}
const par = result.dataInfo;
const par = result.payDataInfo;
WeixinJSBridge.invoke('getBrandWCPayRequest', {
appId : par.appId,
timeStamp : par.timeStamp,
nonceStr : par.nonceStr,
package : par.packageValue,
package: par.packageValue,
signType : par.signType,
paySign : par.paySign
}, res => {
@ -73,8 +74,8 @@ export const Wxpay = async ({orderId,openId})=>{
uni.$u.toast(error.message);
return false;
}
const redirect_url = decodeURIComponent(`https://you-app.mashibing.com/payResult?orderId=${orderId}&payType=wxh5`);
window.location.href = `${result.dataInfo.payUrl}&redirect_url=${redirect_url}`;
const redirect_url = decodeURIComponent(`${ENV.VUE_APP_STATIC_URL}/payResult?orderId=${orderId}&payType=wxh5`);
window.location.href = `${result.payDataInfo.h5Url}&redirect_url=${redirect_url}`;
}
// #endif

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-20 16:45:27
* @LastEditors: ch
* @LastEditTime: 2022-05-06 14:26:57
* @LastEditTime: 2022-06-22 19:34:13
* @Description: file content
-->
<template>
@ -38,10 +38,10 @@ export default {
...this.params
});
const res = result.records.map(item => {
// item.labelList[{text:'x',code : 'recommended'},{text:'x',code : 'second_kill'}]
//
item.seckill = false;
item.tagList = [];
item.labelList = item.labelList || [];
item.labelList.forEach(i => {
if(i.code === 'second_kill'){
item.seckill = true;

@ -0,0 +1,85 @@
<!--
* @Author: ch
* @Date: 2022-06-23 10:40:04
* @LastEditors: ch
* @LastEditTime: 2022-06-30 18:01:46
* @Description: file content
-->
<template>
<view class="follow">
<view class="follow--title">{{day}}追评:</view>
<view class="follow--ctx">{{followComment.commentContent}}</view>
<view class="follow--img" v-if="imgs.length">
<image class="follow--img-item" v-for="(item, idx) in imgs" :src="item" :key="idx" @click="preview(idx)" mode="aspectFit"/>
</view>
</view>
</template>
<script>
export default {
props : {
commentDetail : {
type : Object,
default : () => ({})
}
},
computed:{
followComment(){
return this.commentDetail.followComment || {}
},
day(){
const followTime = (new Date(this.followComment.createTime)).getTime();
const commentTime = (new Date(this.commentDetail.createTime)).getTime();
const day = Math.floor((followTime - commentTime) / (24 * 60 * 60 * 1000));
return day > 0 ? `${day}天后` : `当天`;
},
imgs (){
let urls = this.followComment.pictureUrl || '';
return urls ? urls.split(',') : [];
}
},
methods:{
preview(idx){
this.$store.commit('SET_COMMENT_PREVIEW', {
...this.commentDetail,
commentContent : this.followComment.commentContent,
pictureUrl : this.followComment.pictureUrl
});
this.$Router.push({
path : '/goodsCommentPreview',
query : {
idx
}
});
}
}
}
</script>
<style lang="scss" scoped>
.follow{
margin-top: 40rpx;
&--title{
color: $color-yellow3;
font-size: 28rpx;
}
&--ctx{
margin-top: 20rpx;
font-size: 30rpx;
line-height: 46rpx;
word-break: break-all;
}
&--img{
margin-top: 30rpx;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
&-item{
width: 210rpx;
height: 210rpx;
margin:20rpx 0;
}
}
}
</style>

@ -0,0 +1,66 @@
<!--
* @Author: ch
* @Date: 2022-06-20 14:30:45
* @LastEditors: ch
* @LastEditTime: 2022-06-28 21:14:46
* @Description: file content
-->
<template>
<view class="goods" @click="$Router.push(`/goodsDetail?id=${goods.productId}`)">
<image class="goods--img" :src="goods.productPicture" mode="aspectFit" ></image>
<view class="goods--ctx">
<view class="goods--title">{{ goods.productName }}</view>
<view class="goods--footer">
<view class="goods--sku">{{ goods.skuName }}</view>
<view><slot name="btns"></slot></view>
</view>
</view>
</view>
</template>
<script>
export default {
props : {
goods : {
type : Object,
default : ()=>({})
}
}
}
</script>
<style lang="scss" scoped>
.goods {
display: flex;
&--img {
width: 130rpx;
height: 130rpx;
margin-right: 30rpx;
}
&--ctx{
flex: 1;
overflow: hidden;
padding: 20rpx 0;
}
&--title{
font-size: 28rpx;
overflow:hidden;
text-overflow:ellipsis;
white-space: nowrap;
padding-right: 30rpx;
}
&--footer{
display: flex;
justify-content: space-between;
padding-right: 30rpx;
}
&--sku{
font-size: 24rpx;
color: #999;
margin-top: 20rpx;
}
}
</style>

@ -0,0 +1,93 @@
<!--
* @Author: ch
* @Date: 2022-06-21 15:50:01
* @LastEditors: ch
* @LastEditTime: 2022-06-30 18:00:16
* @Description: file content
-->
<template>
<view>
<view class="top">
<u-rate count="5" size="30rpx" :value="commentDetail.commentScore" activeColor="#FFA35B" readonly inactiveColor="#DDD"></u-rate>
<text class="top--time">{{ FormatDate(commentDetail.createTime, 'yyyy-mm-dd hh:ii') }}</text>
</view>
<view class="ctx">{{ commentDetail.commentContent }}</view>
<view class="img" v-if="imgs.length">
<image class="img--item" mode="aspectFit"
v-for="(item, idx) in imgs" :src="item" :key="idx" @click="preview(idx)"/>
</view>
</view>
</template>
<script>
import {FormatDate} from '@/common/utils';
export default {
props : {
commentDetail : {
type : Object,
default : () => ({})
},
imgPreview : {
type: Boolean,
default : true,
require : true
}
},
data(){
return {
curPreview : -1
}
},
computed : {
imgs (){
let urls = this.commentDetail.pictureUrl || '';
return urls ? urls.split(',') : [];
}
},
methods:{
FormatDate,
preview(idx){
if(!this.imgPreview){
return false
}
this.$store.commit('SET_COMMENT_PREVIEW', this.commentDetail);
this.$Router.push({
path : '/goodsCommentPreview',
query : {
idx
}
});
}
}
}
</script>
<style lang="scss" scoped>
.top{
display: flex;
justify-content: space-between;
margin: 20rpx 0;
&--time{
font-size: 24rpx;
color: #999;
}
}
.ctx{
font-size: 30rpx;
line-height: 46rpx;
word-break: break-all;
}
.img{
margin-top: 30rpx;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
&--item{
width: 210rpx;
height: 210rpx;
margin:20rpx 0;
}
}
</style>

@ -0,0 +1,56 @@
<!--
* @Author: ch
* @Date: 2022-06-21 15:59:23
* @LastEditors: ch
* @LastEditTime: 2022-06-30 17:57:25
* @Description: file content
-->
<template>
<view class="merchant">
<view class="merchant--title">
<text class="merchant--name">{{merchantComment.userName}}</text>
<text class="merchant--time">{{ FormatDate(merchantComment.createTime, 'yyyy-mm-dd hh:ii') }}
</text>
</view>
<view class="merchant--ctx">{{merchantComment.commentContent}}</view>
</view>
</template>
<script>
import {FormatDate} from '@/common/utils';
export default {
props : {
merchantComment : {
type : Object,
default : () => ({})
}
},
data(){
return {
}
},
methods : {
FormatDate
}
}
</script>
<style lang="scss" scoped>
.merchant{
margin-top: 40rpx;
padding: 30rpx;
background: #F5F5F5;
&--title{
display: flex;
justify-content: space-between;
margin-bottom: 24rpx;
}
&--time{
color: #999;
font-size: 24rpx;
}
&--ctx,&--name{
font-size: 26rpx;
color: #666;
}
}
</style>

@ -0,0 +1,138 @@
<!--
* @Author: ch
* @Date: 2022-06-20 16:36:14
* @LastEditors: ch
* @LastEditTime: 2022-06-30 21:06:31
* @Description: file content
-->
<template>
<view>
<view class="rate" v-if="type === COMMENT.TYPE.COMMENT">
<text class="rate--title">满意度评分</text>
<u-rate :count="5" v-model="rate" size="47rpx" activeColor="#FFA35B" inactiveColor="#DDD"></u-rate>
<text class="rate--desc">{{rateDesc}}</text>
</view>
<textarea class="textarea" placeholder="从多个维度评价,可以帮助更多想买的人哦~"
:maxlength="500" v-model="commentContent"></textarea>
<u-upload class="upload"
@afterRead="handleUpdateImg" @delete="handleDelImg"
:fileList="fileList" :maxCount="6" :previewFullImage="true">
</u-upload>
<view class="footer">
<UiButton type="solid" :disable="isVerify" @click="handleSubmit">
{{type === COMMENT.TYPE.COMMENT ? '发表评价' : '发表追评'}}
</UiButton>
</view>
</view>
</template>
<script>
import UiButton from '@/components/UiButton.vue';
import {ApiPostComment} from '@/common/api/comment';
import COMMENT from '@/common/dicts/comment';
import {uploadFileOss, HandleApiError} from '@/common/utils';
export default {
components: { UiButton },
props:{
type : {
type : String | Number,
default : COMMENT.TYPE.COMMENT
},
commentDetail : {
type : Object,
default : ()=> ({})
}
},
data(){
return {
COMMENT,
rate : 5,
commentContent : '',
fileList : []
}
},
computed:{
isVerify(){
if(this.type === COMMENT.TYPE.COMMENT){
return !this.rate || !this.commentContent.trim();
}else{
return !this.commentContent.trim();
}
},
isEdit (){
return ( this.commentContent || this.fileList.length > 0) ? true : false
},
rateDesc(){
return COMMENT.RATE_LABEL[this.rate-1];
}
},
watch:{
isEdit(){
this.$emit('editChang',this.isEdit)
}
},
methods:{
async handleSubmit(){
let data = {
commentContent : this.commentContent,
commentType : this.type,
orderProductId : this.commentDetail.orderProductId,
pictureUrl : this.fileList.map(i => i.url).join(',')
}
if(this.type === COMMENT.TYPE.COMMENT){
data.productId = this.commentDetail.productId;
data.commentScore = this.rate;
}else if(this.type === COMMENT.TYPE.FOLLOW_COMMENT){
data.originId = data.parentId = this.commentDetail.id;
}
const {error, result} = await ApiPostComment(data);
if(!HandleApiError(error)){
this.commentContent = '';
this.fileList = [];
this.$nextTick(()=>{
this.$emit('submit',result);
})
}
},
async handleUpdateImg(val){
const {error, result} = await uploadFileOss(val.file, {
configId : 'account-comment',
serviceName : 'comment'
})
if(error){
uni.$u.toast(error);
}
this.fileList.push({url : result});
},
handleDelImg(target){
this.fileList.splice(target.index, 1)
}
}
}
</script>
<style lang="scss" scoped>
.rate{
display: flex;
margin: 50rpx 0;
align-items: center;
&--title{
font-size: 28rpx;
margin-right: 30rpx;
}
&--desc{
font-size: 30rpx;
color: #999;
margin-left: 40rpx;
}
}
.textarea{
height: 200rpx;
margin-bottom: 40rpx;
}
.footer{
width: 100%;
text-align: right;
margin-top: 50rpx;
}
</style>

@ -0,0 +1,96 @@
<!--
* @Author: ch
* @Date: 2022-06-21 16:01:19
* @LastEditors: ch
* @LastEditTime: 2022-07-01 17:55:01
* @Description: file content
-->
<template>
<view class="thumb">
<view class="thumb--item" @click="handleUseful">
<u-icon name="thumb-up-fill" v-if="isLike"></u-icon>
<u-icon name="thumb-up" v-else></u-icon>
<text>{{usefulCount}}</text>
</view>
<view class="thumb--item" @click="$Router.push(`/goodsCommentDetail?id=${commentDetail.id}`)">
<u-icon name="chat"></u-icon>
<text>{{commentDetail.replyCount}}</text>
</view>
</view>
</template>
<script>
import {Debounce, HandleApiError} from '@/common/utils';
import {ApiPutCommentUseful} from '@/common/api/comment'
export default {
props : {
commentDetail : {
type : Object,
default : ()=>({})
}
},
data(){
return {
debounce : null,
isLike : false,
usefulCount : 0
}
},
watch :{
commentDetail :{
handler(){
this.isLike = this.commentDetail.isLike;
this.usefulCount = this.commentDetail.usefulCount;
},
deep:true
}
},
mounted(){
this.isLike = this.commentDetail.isLike;
// console.log('---',this.isLike,this.commentDetail.usefulCount);
this.usefulCount = this.commentDetail.usefulCount;
},
methods:{
handleUseful(){
this.isLike = !this.isLike
if(this.isLike){
this.usefulCount++;
}else{
this.usefulCount--;
}
if(!this.debounce){
this.debounce = Debounce(this.updateUseFul, 500);
}
this.debounce();
},
async updateUseFul(){
if(this.isLike === this.commentDetail.isLike){
return false
}
const {error, result} = await ApiPutCommentUseful({
commentId : this.commentDetail.id,
isLike : this.isLike
});
HandleApiError(error);
}
}
}
</script>
<style lang="scss" scoped>
.thumb{
display: flex;
justify-content: flex-end;
margin-top: 40rpx;
&--item{
display: flex;
align-items: center;
margin-left: 50rpx;
text{
font-size: 24rpx;
margin-left: 10rpx;
}
}
}
</style>

@ -0,0 +1,47 @@
<!--
* @Author: ch
* @Date: 2022-06-21 22:27:52
* @LastEditors: ch
* @LastEditTime: 2022-07-01 17:14:36
* @Description: file content
-->
<template>
<view class="comment-user">
<image class="comment-user--avatr" :src="userData.userAvatar" shape="circle"/>
<view>
<view class="comment-user--name">{{userData.userName}}</view>
<text class="comment-user--sku" v-if="userData.skuName">{{userData.skuName}}</text>
</view>
</view>
</template>
<script>
export default {
props : {
userData : {
type : Object,
default : ()=> ({})
}
}
}
</script>
<style lang="scss" scoped>
.comment-user{
display: flex;
justify-content: flex-start;
&--avatr{
width: 71rpx;
height: 71rpx;
margin-right: 17rpx;
}
&--name{
margin-top: 5rpx;
}
&--sku{
margin-top: 10rpx;
font-size: 24rpx;
color: #999;
}
}
</style>

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-04-19 11:37:50
* @LastEditors: ch
* @LastEditTime: 2022-05-05 11:08:44
* @LastEditTime: 2022-06-29 17:27:12
* @Description: file content
-->
<template>
@ -12,7 +12,7 @@
<script>
import UiRadioPicker from './UiRadioPicker.vue'
import { ApiPostWxPay } from '@/common/api/order';
import { Wxpay } from '@/common/utils';
import { Wxpay, Alipay } from '@/common/utils';
export default {
components: { UiRadioPicker },
props : {
@ -32,13 +32,19 @@ export default {
label : '微信支付',
value : 'wxpay'
},
// {
// label : '',
// value : 'alipay'
// }
]
}
},
mounted(){
// APP
if(!this.$store.state.openId){
this.options.push({
label : '支付宝支付',
value : 'alipay'
})
}
},
methods:{
confirm(val){
const orderId = this.order.orderId;
@ -46,7 +52,8 @@ export default {
Wxpay({orderId, openId: this.$store.state.openId});
this.close();
}else{
uni.$u.toast('暂不支持支付宝支付');
// uni.$u.toast('');
Alipay({orderId})
}
},
close(){

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-04-07 17:22:44
* @LastEditors: ch
* @LastEditTime: 2022-05-07 19:46:04
* @LastEditTime: 2022-05-24 09:57:03
* @Description: file content
-->
@ -175,7 +175,7 @@ export default {
}
}
}
/deep/ {
::v-deep {
.goods-item--pirce{
text{
font-size: $font-size-lg;

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-31 14:49:33
* @LastEditors: ch
* @LastEditTime: 2022-04-22 18:24:57
* @LastEditTime: 2022-05-24 09:57:04
* @Description: file content
-->
<template>
@ -88,7 +88,7 @@ export default {
}
}
}
/deep/{
::v-deep{
.ui-money{
text{
font-size: $font-size-base;

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-04-22 16:48:11
* @LastEditors: ch
* @LastEditTime: 2022-04-22 20:52:22
* @LastEditTime: 2022-05-24 09:57:05
* @Description: file content
-->
<template>
@ -81,7 +81,7 @@ export default {
margin-left: 60rpx;
}
}
/deep/{
::v-deep{
.ui-money{
text{
font-size: $font-size-base;

@ -2,11 +2,11 @@
* @Author: ch
* @Date: 2022-03-31 15:42:55
* @LastEditors: ch
* @LastEditTime: 2022-04-19 13:57:30
* @LastEditTime: 2022-06-22 14:12:46
* @Description: file content
-->
<template>
<view class="ui-white-box">
<view class="ui-white-box" @click="$emit('click')">
<slot></slot>
</view>
</template>
@ -14,6 +14,6 @@
.ui-white-box{
margin: 20rpx 30rpx;
background: $color-grey0;
border-radius: 20rpx;
border-radius: 8rpx;
}
</style>

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2021-07-26 23:22:16
* @LastEditors: ch
* @LastEditTime: 2022-05-16 09:46:02
* @LastEditTime: 2022-06-20 17:38:48
* @Description: file content
*/
import Vue from 'vue';
@ -11,34 +11,39 @@ import App from './App';
import {router,RouterMount} from '@/common/router/index.js';
import uView from 'uview-ui';
import store from '@/common/store';
import Confirm from '@/components/mount/index';
import route from 'uview-ui/libs/util/route';
import {toSearchJson} from '@/common/utils';
import {FormatSearchJson} from '@/common/utils';
import {ApiGetOpenId, ApiGetAuthUrl} from '@/common/api/wx';
import {MsbWebSkt, MsbWebSktInit, createUUID} from '@/common/utils';
import {ApiGetCurrentUser} from '@/common/api/account';
import {Im, ImInit} from '@/common/utils';
import { ApiSktSysGetSession, ApiSktSysHeart } from './common/api/im';
if(store.state.token){
// 进入应用则向IM发起心跳包 以及获取IM会话数据
MsbWebSktInit().then(()=>{
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "1",
content: { sysId : "1"}
})
});
const socketInit = () => {
if (!store.state.userInfo.id) {
setTimeout(() => {
socketInit();
},10000)
return false;
}
// 初始化IM
ImInit().then(() => {
// 获取到会话列表
Im.getSessionList();
});
}
setInterval(()=>{
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "0",
content: { text : "ping"}
})
})
}, 5000);
})
const getUserInfo = async () => {
const { error, result } = await ApiGetCurrentUser();
if (error) {
uni.$u.toast(error.message);
return false;
}
store.commit('SET_USER_INFO', result);
};
if (store.state.token) {
socketInit();
getUserInfo();
}
@ -47,7 +52,7 @@ if(store.state.token){
const ua = navigator ? navigator.userAgent.toLowerCase() : '';
if(ua.includes('micromessenger')) {
if(!store.state.openId){
let query = toSearchJson(window.location.search)
let query = FormatSearchJson(window.location.search)
if(query.code){
ApiGetOpenId({
code : query.code
@ -76,7 +81,6 @@ if(ua.includes('micromessenger')) {
Vue.use(router);
Vue.use(uView);
Vue.use(Confirm);
Vue.prototype.$store = store;
Vue.config.productionTip = false;

@ -1,8 +1,8 @@
{
"name" : "马士兵严选",
"appid" : "__UNI__3FB31B6",
"appid" : "__UNI__5FEB250",
"description" : "",
"versionName" : "1.0.0",
"versionName" : "1.0.1",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
@ -40,10 +40,14 @@
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
],
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ],
"minSdkVersion" : 21
},
/* ios */
"ios" : {},
"ios" : {
"dSYMs" : false
},
/* SDK */
"sdkConfigs" : {
"ad" : {},
@ -52,39 +56,18 @@
"__platform__" : [ "android" ],
"appid" : "wx17b34a4a90ef18f7",
"UniversalLinks" : ""
},
"alipay" : {
"__platform__" : [ "android" ]
}
}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
"hdpi" : "src/static/app-icon/72.png",
"xhdpi" : "src/static/app-icon/96.png",
"xxhdpi" : "src/static/app-icon/144.png",
"xxxhdpi" : "src/static/app-icon/192.png"
}
}
}
@ -118,19 +101,11 @@
"base" : ""
},
"devServer" : {
"proxy" : {
"/uc/" : {
"target" : "https://k8s-horse-gateway.mashibing.cn"
},
"/mall/" : {
"target" : "https://k8s-horse-gateway.mashibing.cn"
},
"/pay/" : {
"target" : "https://k8s-horse-gateway.mashibing.cn"
},
"/oss/" : {
"target" : "https://k8s-horse-gateway.mashibing.cn"
}
"port" : 8080
},
"optimization" : {
"treeShaking" : {
"enable" : true
}
}
}

@ -10,6 +10,13 @@
"backgroundColor" : "#69ADE5"
}
},
{
"path": "pages/webView",
"aliasPath" : "/webView",
"style": {
"navigationBarTitleText": "收银台"
}
},
{
"path": "pages/login",
"aliasPath" : "/login",
@ -58,6 +65,42 @@
"navigationBarTitleText": "马士兵严选"
}
},
{
"path": "pages/goods/enable",
"aliasPath" : "/enable",
"style": {
"navigationBarTitleText": "商品详情"
}
},
{
"path": "pages/goods/comment/list",
"aliasPath" : "/goodsCommentList",
"style": {
"navigationBarTitleText": "全部评价"
}
},
{
"path": "pages/goods/comment/otherList",
"aliasPath" : "/goodsCommentOtherList",
"style": {
"navigationBarTitleText": "全部评价"
}
},
{
"path": "pages/goods/comment/preview",
"aliasPath" : "/goodsCommentPreview",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "马士兵严选"
}
},
{
"path": "pages/goods/comment/detail",
"aliasPath" : "/goodsCommentDetail",
"style": {
"navigationBarTitleText": "全部评价"
}
},
{
"path": "pages/account/index",
"aliasPath" : "/account",
@ -177,6 +220,20 @@
"navigationBarTitleText": "支付中"
}
},
{
"path": "pages/order/comment/index/index",
"aliasPath" : "/comment",
"style": {
"navigationBarTitleText": "发表评价"
}
},
{
"path": "pages/order/comment/success",
"aliasPath" : "/commentSuccess",
"style": {
"navigationBarTitleText": "评价成功"
}
},
{
"path": "pages/order/saleAfter/saleAfterList",
"aliasPath" : "/saleAfterList",

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-22 14:12:18
* @LastEditors: ch
* @LastEditTime: 2022-04-29 23:09:52
* @LastEditTime: 2022-05-24 09:57:08
* @Description: file content
-->
<template>
@ -126,7 +126,7 @@ page {
left: 30rpx;
width: 670rpx;
}
/deep/ {
::v-deep {
.form--city .ui-cell{
padding: 0 40rpx;
}

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-22 14:12:18
* @LastEditors: ch
* @LastEditTime: 2022-05-07 19:07:02
* @LastEditTime: 2022-05-24 09:57:10
* @Description: file content
-->
<template>
@ -160,7 +160,7 @@ page {
left: 30rpx;
width: 670rpx;
}
/deep/ {
::v-deep {
.del-address .ui-cell--title{
color: $color-yellow3;
}

@ -3,13 +3,13 @@
components: { UiCell },: ch
* @Date: 2019-08-22 19:41:20
* @LastEditors: ch
* @LastEditTime: 2022-05-17 18:00:28
* @LastEditTime: 2022-06-28 16:38:33
* @Description: file content
-->
<template>
<view>
<view class="header">
<u-badge class="header--badge" max="99" :value="$store.state.sessionMsgCount"
<u-badge class="header--badge" max="99" :value="$store.state.imMsgCount"
@click="$Router.push('/messageGroup')"></u-badge>
<image class="header--msg" @click="$Router.push('/messageGroup')" src="@/static/account/xx.png" />
</view>
@ -38,6 +38,11 @@
<image class="item-icon" src="@/static/account/sh.png"/>
<view>待收货</view>
</view>
<view class="order-tabs--item" @click="$Router.push('/orderList?tab=5')">
<u-badge class="item-badge" max="99" :value="statistic.waitComment"></u-badge>
<image class="item-icon" src="@/static/account/pj.png"/>
<view>待评价</view>
</view>
</view>
</view>
<view class="cell">
@ -95,7 +100,8 @@ export default {
this.statistic = result;
},
service(){
uni.$u.toast('客服休息中');
// uni.$u.toast('');
this.$Router.push('/messageChat');
}
}
@ -134,6 +140,10 @@ page {
top: 10rpx;
z-index: 99;
border: 1px solid #fff;
/* #ifndef H5 */
top: 90rpx;
/* #endif */
}
&--cell{
border: 0;
@ -208,7 +218,7 @@ page {
border: 0;
}
}
/deep/{
::v-deep{
.header--cell .ui-cell--title{
font-weight: bold;
font-size: $font-size-lg;

@ -2,13 +2,16 @@
* @Author: ch
* @Date: 2022-03-26 14:32:03
* @LastEditors: ch
* @LastEditTime: 2022-05-17 16:22:51
* @LastEditTime: 2022-06-14 20:48:40
* @Description: file content
-->
<template>
<view class="main">
<template v-for="item in msgData">
<view class="send" :key="item.id" v-if="item.fromId == $store.state.userInfo.id">
<text class="send--status" v-if="item.sendStatus === 'loading'"></text>
<text class="send--status send--status__fail" v-if="item.sendStatus === 'fail'" @click="resend(item)"></text>
<template v-if="item.type == MSG_TYPE.CUSTOM">
<GoodsInfo class="send--box" position="msg" v-if="item.payload.id" :goodsInfo="item.payload"/>
<OrderInfo class="send--box" position="msg" v-if="item.payload.orderId" :orderInfo="item.payload"/>
@ -19,13 +22,16 @@
<view class="send--box" v-if="item.type == MSG_TYPE.VIDEO">
<video class="send--box__video" :src="item.payload.url"></video>
</view>
<image class="avatar" :src="item.fromAvatar || '@/static/message/xt.png'" mode="widthFix"/>
<image class="avatar" :src="$store.state.userInfo.avatar || require('@/static/message/xt.png')" mode="widthFix"/>
</view>
<view class="tips" :key="item.id" v-else-if="item.type == MSG_TYPE.TIP">
<view class="tips--box">{{item.payload.text}}</view>
</view>
<view class="tips" :key="item.id" v-else-if="item.type === MSG_TYPE.CUSTOM && item.payload.customType == 'transferWaiterSession'">
<view class="tips--box">现在由客服{{item.payload.toNickname}}为您服务</view>
</view>
<view class="receive" :key="item.id" v-else>
<image class="avatar" :src="item.fromAvatar || '@/static/message/xt.png'" mode="widthFix"/>
<image class="avatar" :src="item.fromAvatar || require('@/static/message/xt.png')" mode="widthFix"/>
<view>
<view class="receive--name">{{item.fromNickname}}</view>
<template v-if="item.type == MSG_TYPE.CUSTOM">
@ -42,18 +48,18 @@
</view>
</view>
</template>
<Footer :goodsInfo="goodsInfo" :orderInfo="orderInfo"></Footer>
<Footer :goodsInfo="goodsInfo" :orderInfo="orderInfo" :sessionId="sessionId"></Footer>
</view>
</template>
<script>
import {MSG_TYPE} from '@/common/dicts/im';
import {MsbWebSkt, createUUID} from '@/common/utils';
import {Im, ToAsyncAwait} from '@/common/utils';
import {ApiGetOrderDetail} from '@/common/api/order';
import {ApiGetGoodsDetail} from '@/common/api/goods';
import UiButton from '@/components/UiButton.vue';
import Footer from './components/Footer.vue';
import GoodsInfo from './components/GoodsInfo.vue';
import OrderInfo from './components/OrderInfo.vue';
import Footer from './modules/Footer.vue';
import GoodsInfo from './modules/GoodsInfo.vue';
import OrderInfo from './modules/OrderInfo.vue';
export default {
components: { UiButton, Footer, GoodsInfo, OrderInfo },
data(){
@ -68,14 +74,19 @@ export default {
},
computed:{
curSessionData(){
return this.$store.state.sessionData.find(i =>i.id == this.$store.state.sessionMsgId) || {}
const data = this.$store.state.imData.find(i => i.id == this.sessionId) || {};
if(data.id){
Im.setCurSessionId(data.id);
}
return data;
},
msgData (){
return this.curSessionData ? this.curSessionData.messageList : [];
}
},
destroyed(){
Im.setCurSessionId(null);
},
watch:{
msgData(){
this.$nextTick(()=>{
@ -99,104 +110,105 @@ export default {
this.$Router.push('/login');
return false;
}
this.goodsId = this.$Route.query.goodsId;
this.orderId = this.$Route.query.orderId;
this.sessionId = this.$Route.query.sessionId;
this.socketInit();
if(this.goodsId){
this.createSessionMain();
this.getGoodsInfo();
}else if(this.orderId){
this.createSessionMain();
}
if(this.orderId){
this.getOrderInfo();
}else if(this.sessionId){
this.$store.commit('SET_SESSION_MSG_ID',this.sessionId);
this.getHistoryMsg();
this.readMsg();
this.heartMsg();
}else{
this.createSessionMain();
}
this.$nextTick(()=>{
uni.pageScrollTo({
scrollTop : 99999,
duration : 0
});
})
},
methods:{
/**
* 创建会话主体
* 如果是从商品或订单进来需要创建会话
*/
createSessionMain(){
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : '21',
content : {
storeId : 1
}
}),
success:()=>{
//
setTimeout(()=>{
this.getHistoryMsg();
this.readMsg();
this.heartMsg();
},1000)
socketInit(){
if(!Im.isOpen){
setTimeout(()=>{
this.socketInit();
}, 100)
return false;
}
if(this.sessionId){
if(!this.msgData?.length){
this.getHistoryMsg();
}
});
this.readMsg();
}else{
this.createSessionMain();
}
},
/**
* 发送心跳
* 创建会话主体
* 如果是从商品或订单进来需要创建会话
*/
heartMsg(){
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : '22',
content : {
storeId : 1
}
}),
complete: () =>{
setTimeout(()=>{
this.heartMsg();
},5000)
async createSessionMain(){
if(!Im.isOpen){
setTimeout(()=>this.createSessionMain(),1000);
return false;
}
const {error, result} = await ToAsyncAwait(Im.createSession({
content : {
sessionType : 3
}
})
}));
if(error){
uni.$u.toast(error.message);
return false;
}
this.sessionId = result.content.id;
Im.setCurSessionId(this.sessionId);
this.getHistoryMsg();
// this.readMsg();
},
/**
* 获取历史消息
*/
getHistoryMsg(){
async getHistoryMsg(){
if(!this.curSessionData.id){
setTimeout(()=>{
this.getHistoryMsg();
}, 500)
return false;
}
this.loading = true;
const lastMsg = this.msgData?.length ? this.msgData[0] : {};
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "23",
content: {
sessionId : this.$store.state.sessionMsgId,
topMessageId : lastMsg.id || null
}
})
})
const {error, result} = await ToAsyncAwait(Im.getHistoryMsg());
if(error){
uni.$u.toast(error.errMsg || error.message);
return false
}
this.loading = false;
},
/**
* 已读消息
* 把当前会话消息置为已读
*/
readMsg(){
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "6",
content: {
sessionId : this.$store.state.sessionMsgId
}
})
});
//
const count = this.$store.state.sessionMsgCount - this.curSessionData.unreadCount;
//
this.curSessionData.unreadCount = 0;
this.$store.commit('SET_SESSION_MSG_COUNT', count)
async readMsg(){
const {error, result} = await ToAsyncAwait(Im.setRead({
content: {
sessionId : this.sessionId
}
}));
if(error){
uni.$u.toast(error.errMsg || error.message);
console.log(error);
return false
}
},
resend(item){
Im.resend(item)
},
/**
* 从订单页进来查询订单信息
@ -265,6 +277,7 @@ page{
color: #333;
font-size: 32rpx;
line-height: 40rpx;
word-break: break-all;
}
&__img{
height: 140rpx;
@ -275,8 +288,19 @@ page{
display: flex;
justify-content: flex-end;
margin-top:30rpx ;
&--status{
font-size: $font-size-sm;
color: $color-grey3;
display: flex;
align-items: center;
line-height: 46rpx;
margin-right: 10rpx;
&__fail{
color: $color-yellow4;
}
}
&--box{
max-width: 552rpx;
max-width: 480rpx;
margin-right:30rpx;
&__txt{
padding: 20rpx 30rpx;
@ -285,6 +309,7 @@ page{
color: #fff;
font-size: 32rpx;
line-height: 46rpx;
word-break: break-all;
}
&__img{
height: 140rpx;

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-05-11 11:45:08
* @LastEditors: ch
* @LastEditTime: 2022-05-16 20:06:16
* @LastEditTime: 2022-06-14 12:55:27
* @Description: file content
-->
<template>
@ -12,9 +12,9 @@
<view class="operation">
<textarea class="operation--input" :focus="focus" v-model="msgCtx" @focus="moreShow = false" @click="emojiShow = false"/>
<view class="operation--btns">
<image class="operation--emoji" src="@/static/imxx/emoji.png" @click="emojiShow = !emojiShow"/>
<image class="operation--emoji" src="@/static/im/emoji.png" @click="emojiShow = !emojiShow"/>
<view class="operation--send" v-if="msgCtx.length" @click="send(msgCtx, MSG_TYPE.TXT)"></view>
<image class="operation--more" v-else src="@/static/imxx/more.png" @click="moreShow = true" />
<image class="operation--more" v-else src="@/static/im/more.png" @click="moreShow = true" />
</view>
</view>
<view class="emoji" v-if="emojiShow">
@ -30,14 +30,14 @@
<view class="other" v-if="moreShow">
<u-upload @afterRead="uploadImg" >
<view class="other--btn">
<image class="other--img" src="@/static/imxx/img.png"/>
<image class="other--img" src="@/static/im/img.png"/>
<view>图片</view>
</view>
</u-upload>
<u-upload @afterRead="uploadVideo" compressed accept="video">
<view class="other--btn">
<image class="other--img" src="@/static/imxx/video.png"/>
<image class="other--img" src="@/static/im/video.png"/>
<view>视频</view>
</view>
</u-upload>
@ -45,7 +45,7 @@
</view>
</template>
<script>
import {MsbWebSkt, createUUID} from '@/common/utils';
import {Im, ToAsyncAwait} from '@/common/utils';
import {MSG_TYPE} from '@/common/dicts/im';
import {Request} from '@/common/utils';
import {ApiPutUser} from '@/common/api/account';
@ -64,6 +64,10 @@ export default {
goodsInfo : {
type : Object,
default : () => ({})
},
sessionId : {
type : Number | String,
default : ''
}
},
data(){
@ -115,7 +119,7 @@ export default {
/**
* 统一发送函数
*/
send(val, type){
async send(val, type){
let payload = {};
switch(type){
case MSG_TYPE.CUSTOM :
@ -130,19 +134,20 @@ export default {
break;
}
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : '20',
content: {
toSessionId : this.$store.state.sessionMsgId,
payload : payload,
toId : 1,
type : type
}
})
});
this.msgCtx = '';
const {error, result} = await ToAsyncAwait(Im.sendMsg({
fromId : this.$store.state.userInfo.id,
content: {
toSessionId : this.sessionId,
payload : payload,
toId : 1,
type : type
}
}));
if(error){
uni.$u.toast(error.message);
return false
}
this.focus = false;
this.$nextTick(() => {
this.focus = true;
@ -153,11 +158,11 @@ export default {
this.msgCtx += str;
},
sendGoods(){
this.send(this.simpleGoods, MSG_TYPE.CUSTOM);
this.send({...this.simpleGoods, customType:'goodsInfo'}, MSG_TYPE.CUSTOM);
this.goodsShow = false;
},
sendOrder(){
this.send(this.simpleOrder, MSG_TYPE.CUSTOM);
this.send({...this.simpleOrder, customType : 'orderInfo'}, MSG_TYPE.CUSTOM);
this.orderShow = false;
},
uploadImg(val){

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-05-13 15:57:31
* @LastEditors: ch
* @LastEditTime: 2022-05-14 13:47:05
* @LastEditTime: 2022-05-24 09:57:11
* @Description: file content
-->
<template>
@ -14,7 +14,7 @@
</view>
<template v-if="position === 'footer'">
<UiButton class="product--btn" size="min" type="gradual" @click="send"></UiButton>
<image class="product--close" src="@/static/imxx/close.png" @click="close"/>
<image class="product--close" src="@/static/im/close.png" @click="close"/>
</template>
</view>
</template>
@ -85,7 +85,7 @@ export default {
right:30rpx;
}
}
/deep/{
::v-deep{
.product--con-price{
text{
color: #FF512B;

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-05-14 14:00:14
* @LastEditors: ch
* @LastEditTime: 2022-05-14 15:57:30
* @LastEditTime: 2022-05-24 09:57:12
* @Description: file content
-->
<template>
@ -78,7 +78,7 @@ export default {
margin-top: 26rpx;
}
}
/deep/{
::v-deep{
.order--con-price{
text{
color: #FF512B;

@ -2,28 +2,38 @@
* @Author: ch
* @Date: 2022-03-22 16:13:00
* @LastEditors: ch
* @LastEditTime: 2022-05-14 16:32:09
* @LastEditTime: 2022-06-17 16:31:20
* @Description: file content
-->
<template>
<view >
<BsEmpty tips="暂时还没有消息哦~" v-if="!$store.state.sessionData.length"
<BsEmpty tips="暂时还没有消息哦~" v-if="!sessionData.length"
:icon="require('@/static/message/empty.png')" />
<view class="msgItem" v-for="item in $store.state.sessionData" :key="item.id"
<view class="msgItem" v-for="item in sessionData" :key="item.id"
@click="openMsg(item)">
<image class="msgItem--headImg" src="@/static/account/tx.png"></image>
<image class="msgItem--headImg" :src="item.fromAvatar || require('@/static/message/xt.png')"></image>
<view class="msgItem--con">
<view>
<text class="msgItem--title">{{item.fromNickname}}</text>
<text class="msgItem--text" v-if="item.lastMessage">
<template v-if="item.lastMessage.type == msgType.TXT">{{item.lastMessage.payload.content || item.lastMessage.payload.text}}</template>
<template v-if="item.lastMessage.type == msgType.CUSTOM">[]</template>
<template v-if="item.lastMessage.type == msgType.CUSTOM">
<template v-if="item.lastMessage.payload.customType === 'app_push'">
{{item.lastMessage.payload.link ? '[链接]' : item.lastMessage.payload.content}}
</template>
<template v-if="item.lastMessage.payload.customType === 'transferWaiterSession'">
[客服转移]
</template>
<template v-else>
{{item.lastMessage.payload.content || '[未知消息]'}}
</template>
</template>
<template v-if="item.lastMessage.type == msgType.IMG">[]</template>
<template v-if="item.lastMessage.type == msgType.VIDEO">[]</template>
</text>
</view>
<view class="msgItem--right">
<text class="msgItem--time" v-if="item.lastMessage">{{item.lastMessage.createTimeStamp}}</text>
<text class="msgItem--time" v-if="item.lastMessage">{{FormatDate(item.lastMessage.createTimeStamp, 'mm-dd hh:ii:ss')}}</text>
<u-badge class="msgItem--badge" :value="item.unreadCount"></u-badge>
</view>
</view>
@ -33,12 +43,13 @@
<script>
import BsEmpty from '../../../components/BsEmpty.vue';
import {MSG_TYPE} from '@/common/dicts/im';
import {FormatDate, Im} from '@/common/utils';
export default {
components: { BsEmpty },
data (){
return {
msgType : MSG_TYPE,
sessionData : this.$store.state.sessionData
}
},
onShow(){
@ -46,15 +57,22 @@ export default {
this.$Router.push('/login');
}
},
computed:{
sessionData (){
return this.$store.state.imData.sort((a,b) => b.updateTimeStamp - a.updateTimeStamp);
}
},
methods:{
FormatDate,
openMsg(item){
this.$store.commit('SET_SESSION_MSG_ID', item.id);
if(item.type === 3){
this.$Router.push(`/messageChat?sessionId=${item.id}`);
}else{
this.$Router.push(`/messageSystem`);
Im.setCurSessionId(item.id);
if(item.type === 4){
if(JSON.parse(item.payload).type === 'system'){
this.$Router.push(`/messageSystem?sessionId=${item.id}`);
return false;
}
}
this.$Router.push(`/messageChat?sessionId=${item.id}`);
}
}
}
@ -94,6 +112,8 @@ export default {
display:-webkit-box;
-webkit-box-orient:vertical;
-webkit-line-clamp:2;
word-break: break-all;
}
&--right{
text-align: right;

@ -0,0 +1,160 @@
<!--
* @Author: ch
* @Date: 2022-03-26 14:32:03
* @LastEditors: ch
* @LastEditTime: 2022-06-02 18:07:09
* @Description: file content
-->
<template>
<view class="main">
<view class="msg-item" v-for="item in msgData" :key="item.id">
<view class="msg-item--title">
<text>{{item.payload.title }}</text>
<text class="msg-item--time">{{FormatDate(item.createTimeStamp, 'mm-dd hh:ii:ss')}}</text>
</view>
<view class="msg-item--con" @click="handleDetail(item)">
<image class="msg-item--img" :src="item.payload.productImageUrls ? item.payload.productImageUrls[0] : require('@/static/message/xt.png')" mode="widthFix"/>
<view class="msg-item--desc">
<view >{{item.payload.content}}</view>
<template v-if="item.payload.customType === 'orderAutoDelivery'">
<template v-for="i in item.payload.virtualProductContentList">
<text class="msg-item--desc-link" v-if="i.shipType === 1" @click="openLink(i)" :key="i.shipContent">[]</text>
<text v-else :key="i.shipContent">{{i.shipContent}}</text>
</template>
</template>
</view>
</view>
</view>
<u-loadmore status="loading" v-if="!msgData || !msgData.length"/>
</view>
</template>
<script>
import {Im, CreateUUID, ToAsyncAwait, FormatDate} from '@/common/utils';
export default {
data(){
return {}
},
computed:{
curSessionData(){
const data = this.$store.state.imData.find(i =>i.id == this.$Route.query.sessionId) || {}
if(data.id){
Im.setCurSessionId(data.id);
}
return data;
},
msgData (){
let data = this.curSessionData ? this.curSessionData.messageList : [];
return Object.assign([],data).reverse();
}
},
watch:{
curSessionData(){
this.setTitle();
}
},
onReachBottom(){
this.getHistoryMsg();
},
onShow(){
if(!this.$route.query.sessionId){
this.$Router.back();
return false
}
this.getHistoryMsg();
this.readMsg();
this.setTitle();
},
methods:{
FormatDate : FormatDate,
/**
* 获取历史消息
*/
setTitle(){
uni.setNavigationBarTitle({
title : this.curSessionData?.fromNickname || '系统消息'
});
},
async getHistoryMsg(){
this.loading = true;
await ToAsyncAwait(Im.getHistoryMsg());
},
/**
* 把当前会话消息置为已读
*/
async readMsg(){
Im.setRead({
content: {
sessionId : this.$route.query.sessionId
}
});
},
/**
* 点击详情跳转
*/
handleDetail(item){
if(['orderPay','orderDelivery','orderCancel'].includes(item.payload.customType)){
this.$Router.push(`/orderDetail?id=${item.payload.primaryId}`)
}else if(['refundFail','refundSuccess'].includes(item.payload.customType)){
this.$Router.push(`/saleAfterDetail?id=${item.payload.primaryId}`)
}else if(item.payload.linkJump){
// #ifdef H5
window.location.href = item.payload.linkJump;
// #endif
// #ifdef APP-PLUS
plus.runtime.openURL(item.payload.linkJump);
// #endif
}
},
openLink(item){
debugger
if(item.shipType === 1){
// #ifdef H5
window.location.href = item.shipContent;
// #endif
// #ifdef APP-PLUS
plus.runtime.openURL(item.shipContent);
// #endif
}
}
}
}
</script>
<style lang="scss" scoped>
.msg-item{
padding: 40rpx 0 0 40rpx;
&--title{
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 40rpx;
font-size: $font-size-lg;
color: $color-grey6;
}
&--time{
font-size: $font-size-sm;
color: $color-grey4;
}
&--con{
display: flex;
border-bottom: 1px solid $color-grey2;
padding: 40rpx 0;
}
&--img{
width: 140rpx;
height: 140rpx;
border-radius: 12rpx;
margin-right: 40rpx;
}
&--desc{
width: 455rpx;
font-size: $font-size-base;
line-height: 39rpx;
color: $color-grey5;
&-link{
color: $color-yellow4;
margin-right: 10rpx;
}
}
}
</style>

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-22 15:09:06
* @LastEditors: ch
* @LastEditTime: 2022-05-16 20:21:20
* @LastEditTime: 2022-06-23 17:45:29
* @Description: file content
-->
<template>
@ -19,7 +19,7 @@
<script>
import UiButton from '@/components/UiButton.vue'
import UiCell from '@/components/UiCell.vue';
import {MsbWebSkt} from '@/common/utils';
import {Im} from '@/common/utils';
import {ApiGetLogout} from '@/common/api/index';
export default {
components: { UiCell, UiButton },
@ -28,6 +28,7 @@ export default {
return this.$store.state.userInfo
}
},
methods:{
logout(){
uni.showModal({
@ -37,12 +38,13 @@ export default {
confirmColor : '#3A83FB',
success:async ({confirm})=>{
if(confirm){
await ApiGetLogout();
this.$store.commit('SET_SESSION_DATA',[])
this.$store.commit('SET_SESSION_MSG_COUNT',0)
this.$store.commit('SET_SESSION_MSG_ID','')
const {error} = await ApiGetLogout();
if(error){
uni.$u.toast(error.message);
return false;
}
this.$store.commit('SET_TOKEN');
MsbWebSkt.close();
Im.close();
this.$Router.replace('/login');
}
}
@ -93,7 +95,7 @@ page {
font-size: $font-size-lg;
}
/deep/{
::v-deep{
.userInfo .uiCell--title{
font-size: $font-size-lg;
}

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-28 15:38:23
* @LastEditors: ch
* @LastEditTime: 2022-05-17 11:44:50
* @LastEditTime: 2022-06-13 20:49:32
* @Description: file content
-->
<template>
@ -38,7 +38,7 @@ export default {
nickname : this.userInfo.nickname
});
if(error){
ui.$u.totas(error.message);
uni.$u.toast(error.message);
return false;
}
this.$store.commit('SET_USER_INFO', {...this.userInfo});

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-28 15:38:23
* @LastEditors: ch
* @LastEditTime: 2022-05-17 17:42:23
* @LastEditTime: 2022-06-13 20:59:54
* @Description: file content
-->
<template>
@ -106,7 +106,7 @@ export default {
const avatar = `${oss.host}/${oss.dir}${fileName}`;
const {error, result} = await ApiPutUser({avatar});
if(error){
ui.$u.totas(error.message);
uni.$u.toast(error.message);
return false
}
this.$store.commit('SET_USER_INFO', {...this.userInfo, avatar});
@ -143,7 +143,7 @@ page {
border: 0;
}
}
/deep/{
::v-deep{
.userInfo .uiCell--title{
font-size: $font-size-lg;
}

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2019-08-22 19:41:20
* @LastEditors: ch
* @LastEditTime: 2022-05-17 11:29:38
* @LastEditTime: 2022-05-30 17:44:51
* @Description: file content
-->
<template>
@ -10,7 +10,7 @@
</template>
<script>
import PageCtx from './components/PageCtx';
import PageCtx from './modules/PageCtx';
export default {
components : {PageCtx},
onShow(){

@ -301,12 +301,22 @@ export default {
* 结算选中的商品
*/
handleOrder() {
const ids = this.checkedIds;
const ids = this.checkedIds;
//
const baseType = this.list.find(i => ids[0] === i.id).productType;
for(let id of ids){
const item = this.list.find(i => id === i.id);
if(item.productType != baseType){
uni.$u.toast('虚拟商品需要单独结算,不可和其他实物订单合并结算哦~');
return false;
}
}
if (ids.length) {
this.$Router.push({
path : '/orderSubmit',
query : {
mode: 'cart',
productType : baseType,
ids:ids.join(',')
}
})
@ -573,7 +583,7 @@ export default {
}
/deep/{
::v-deep{
.cart-item--price{
text{
font-size: $font-size-lg;

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-04-08 18:36:14
* @LastEditors: ch
* @LastEditTime: 2022-05-17 11:29:41
* @LastEditTime: 2022-05-30 17:44:55
* @Description: file content
-->
<template>
@ -10,7 +10,7 @@
</template>
<script>
import PageCtx from './components/PageCtx';
import PageCtx from './modules/PageCtx';
import {ApiGetCartList} from '@/common/api/cart';
export default {
components : {PageCtx},

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save