HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

SyntaxError 语法错误:如果在HbuilderX中发现这类问题,又不知道是哪个地方,可以用VScode定位错误地方。

用了半年多Hbuilderx,因为它定位不准,此类相关问题花了不少时间。

今天更是错误信息连是哪个文件都不知道,哪一行更是不知道。太坑了。

用了半年多Hbuilderx,因为它定位不准,此类相关问题花了不少时间。

今天更是错误信息连是哪个文件都不知道,哪一行更是不知道。太坑了。

SyntaxError: Unexpected token '}'

HbuilderX对这种错误给出的错误信息很难找到具体错误地方,

在VScode和Cursor一目了然就发现是这个地方的错误。

唉,曾经浪费了好多时间在找这类错误上。

不知道Hbuilderx是哪个地方没有安装好,按说这个语法错误提示应该个简单功能啊。

继续阅读 »

HbuilderX对这种错误给出的错误信息很难找到具体错误地方,

在VScode和Cursor一目了然就发现是这个地方的错误。

唉,曾经浪费了好多时间在找这类错误上。

不知道Hbuilderx是哪个地方没有安装好,按说这个语法错误提示应该个简单功能啊。

收起阅读 »

基于Vue3+TS+VITE搭建好用的UNI-APP项目基础模板

uniapp模板 项目模板 uni_app vue3

插件市场地址:Vue3-Uni-TS-Template基础模板

背景

uni-appApp/H5/小程序全平台支持Vue 3.0开发,且全平台支持Vite编译器,使用vue3+Vite可以让我们的uni-app项目获得了多快好省四大收益:

  • 更多的语法支持,支持组合式API,业务聚焦,开发效率更高;
  • 更快的编译速度,H5平台十倍加速,小程序、App加速30%以上;
  • 更好的运行性能,用户端响应更快,体验更好;
  • 更小的代码体积,瘦身30%以上,更省体积、更省流量

而使用TypeScript则可以为我们带来:

  • 更好的类型检查:TypeScript可以在编译时检查类型错误,避免在运行时出现错误。

  • 更好的代码提示:TypeScript可以根据类型信息提供更好的代码提示,提高开发效率。

  • 更好的可维护性:TypeScript可以使代码更加清晰易懂,减少出错的可能性,提高代码的可维护性。

  • 更好的扩展性:TypeScript可以使用ES6和ES7的新特性,使代码更加现代化,同时也可以使用第三方库和框架。

  • 更好的团队协作:TypeScript可以使团队成员之间的代码更加一致,减少出错的可能性,提高团队协作效率

介绍

本插件是一个基于uni-app和fant-mini-plus的vue3项目基础模板,使用vue3、vite4、fant-mini-plus、pinia、uni-mini-router、axios、fant-axios-adapter开发,基于koa实现mock功能

特性

  • 使用vue3/vite4/pinia/axios等主流框架开发
  • 引入fant-mini-plus组件库,开箱即用
  • 引入Eslint/Prettier,统一前端代码风格
  • 引入lint-staged/husky/commitlint/commitizen/standard-version,统一代码提交规范,自动生成版本发布日志
  • 使用pinia,并支持持久化存储
  • 提供koa-mock本地mock服务
  • 支持axios取消请求(基于requestTask.abort()实现)

    注意!!!

    插件市场会将类似.eslintrc.js这种以.开头的文件忽略掉,故我将.改成了_上传。如果直接在插件市场下载,需要手动将_改为.,涉及文件及目录包含_husky_vscode_env.development_env.production_eslintignore_eslintrc.js_git-cz.json_gitignore_prettierrc等文件。也可以到giteeclonefork

项目依赖

功能 依赖库
组件库 fant-mini-plus
路由(name跳转和导航守卫) uni-mini-router
网络请求(支持取消请求) axios fant-axios-adapter
代码规范 Eslint Prettier
提交规范 lint-staged husky commitlint commitizen
发布日志 standard-version
状态管理 pinia(自定义插件实现持久化)

项目结构

uniapp-vue3-fant-ts  
├─ .env.development----------------------------------环境变量文件dev时生效,VITE_BASEURL为接口地址  
├─ .env.production-----------------------------------环境变量文件build时生效,VITE_BASEURL为接口地址  
├─ .eslintignore-------------------------------------eslint豁免校验的配置,配置的文件免于eslint校验  
├─ .eslintrc.js--------------------------------------eslint配置,指定eslint校验规则  
├─ .gitignore  
├─ .husky--------------------------------------------git hooks,此处配置了pre-commit和commit-msg两个  
├─ .prettierrc---------------------------------------prettier配置,指定prettier校验规则  
├─ .vscode-------------------------------------------vscode配置,实现保存的时候格式化  
├─ CHANGELOG.md--------------------------------------版本发布日志  
├─ README.md  
├─ commitlint.config.js------------------------------commitlint配置文件  
├─ index.html  
├─ koa-mock------------------------------------------基于koa实现的本地mock项目  
│    ├─ api------------------------------------------将接口返回的json放在此目录,接口即为api/文件名  
├─ package.json  
├─ src-----------------------------------------------项目资源目录  
│    ├─ App.vue--------------------------------------uni的主组件,所有页面都是在App.vue下进行切换的,是页面入口文件  
│    ├─ api------------------------------------------API文件,用于集中管理接口  
│    ├─ http-----------------------------------------axios拦截器,用于自定义请求和响应拦截  
│    ├─ main.ts--------------------------------------入口文件,主要作用是初始化vue实例、定义全局组件  
│    ├─ manifest.json--------------------------------应用的配置文件,用于指定应用的名称、图标、权限等  
│    ├─ model----------------------------------------API接口用到的typescript模型,集中管理  
│    ├─ pages----------------------------------------业务页面  
│    ├─ pages.json-----------------------------------页面全局配置,决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等  
│    ├─ router---------------------------------------路由配置,导航守卫  
│    ├─ static---------------------------------------资源文件,图片等  
│    ├─ store----------------------------------------pinia配置,持久化存储  
│    │    ├─ index.ts  
│    │    └─ persist.ts------------------------------持久化插件  
│    ├─ types.d.ts-----------------------------------typescript的声明文件  
│    ├─ uni.scss-------------------------------------全局scss变量,无需引入可以直接使用  
│    └─ uni_modules----------------------------------uni-app插件  
│           ├─ fant-mini-plus------------------------一款vue3组件库  
│           └─ mp-html-------------------------------富文本插件  
├─ tsconfig.json-------------------------------------TypeScript 编译器的配置文件  
├─ versionrc.js--------------------------------------standard-version的配置文件  
├─ vite.config.ts------------------------------------用于配置 vite 的编译选项  
└─ yarn.lock-----------------------------------------Yarn生成的锁文件,它用于记录在使用Yarn安装包时确切的依赖关系和版本号

开发步骤

  • 安装依赖
    • 安装前端项目依赖:yarn
    • 安装mock项目依赖:yarn mock:install
  • 运行
    • 运行前端项目:yarn dev:h5 (其他平台命令在uni-app官网或者package.json中查看)
    • 运行mock项目:yarn mock:dev
  • 提交代码
    • 暂存准备提交的代码(举例添加当前目录下的所有文件到暂存区):git add .
    • 提交:yarn commit,输入之后回车,我们可以选择提交类型。
  • 发布版本(此处操作会使package.json中的version变更,可以此为版本号对显示)
    • 大版本升级1.0.0->2.0.0:yarn release-major
    • 中版本升级1.0.0->1.1.0:yarn release-minor
    • 小版本升级(一般是补丁)1.0.0->1.0.1:yarn release-patch
    • 注意:执行完发布版本的命令之后需要将执行终端中打印的命令将生成的tag推送到git。此操作完成之后会自动生成版本发布日志,可以用于对外发布

开发提示

  • 使用typescript开发可以提高前后端交互的效率,提升前端项目的健壮性。在开发过程中需要做到尽量减少使用any。
    小程序样式变量统一定义在uni.scss中,作为规范,若设计稿上使用的颜色存在于uni.scss中,则使用uni.scss中的变量,如不存在则需要沟通是否新增变量。

  • 提交代码时,feat和fix类型的提交尽量将任务单号作为前缀方便追踪改动关联的需求,如:feat: TASK-1000 新增某个功能。

  • css原子化,如果有需要可以自行引入UnoCSS,本项目暂时不会使用。

继续阅读 »

插件市场地址:Vue3-Uni-TS-Template基础模板

背景

uni-appApp/H5/小程序全平台支持Vue 3.0开发,且全平台支持Vite编译器,使用vue3+Vite可以让我们的uni-app项目获得了多快好省四大收益:

  • 更多的语法支持,支持组合式API,业务聚焦,开发效率更高;
  • 更快的编译速度,H5平台十倍加速,小程序、App加速30%以上;
  • 更好的运行性能,用户端响应更快,体验更好;
  • 更小的代码体积,瘦身30%以上,更省体积、更省流量

而使用TypeScript则可以为我们带来:

  • 更好的类型检查:TypeScript可以在编译时检查类型错误,避免在运行时出现错误。

  • 更好的代码提示:TypeScript可以根据类型信息提供更好的代码提示,提高开发效率。

  • 更好的可维护性:TypeScript可以使代码更加清晰易懂,减少出错的可能性,提高代码的可维护性。

  • 更好的扩展性:TypeScript可以使用ES6和ES7的新特性,使代码更加现代化,同时也可以使用第三方库和框架。

  • 更好的团队协作:TypeScript可以使团队成员之间的代码更加一致,减少出错的可能性,提高团队协作效率

介绍

本插件是一个基于uni-app和fant-mini-plus的vue3项目基础模板,使用vue3、vite4、fant-mini-plus、pinia、uni-mini-router、axios、fant-axios-adapter开发,基于koa实现mock功能

特性

  • 使用vue3/vite4/pinia/axios等主流框架开发
  • 引入fant-mini-plus组件库,开箱即用
  • 引入Eslint/Prettier,统一前端代码风格
  • 引入lint-staged/husky/commitlint/commitizen/standard-version,统一代码提交规范,自动生成版本发布日志
  • 使用pinia,并支持持久化存储
  • 提供koa-mock本地mock服务
  • 支持axios取消请求(基于requestTask.abort()实现)

    注意!!!

    插件市场会将类似.eslintrc.js这种以.开头的文件忽略掉,故我将.改成了_上传。如果直接在插件市场下载,需要手动将_改为.,涉及文件及目录包含_husky_vscode_env.development_env.production_eslintignore_eslintrc.js_git-cz.json_gitignore_prettierrc等文件。也可以到giteeclonefork

项目依赖

功能 依赖库
组件库 fant-mini-plus
路由(name跳转和导航守卫) uni-mini-router
网络请求(支持取消请求) axios fant-axios-adapter
代码规范 Eslint Prettier
提交规范 lint-staged husky commitlint commitizen
发布日志 standard-version
状态管理 pinia(自定义插件实现持久化)

项目结构

uniapp-vue3-fant-ts  
├─ .env.development----------------------------------环境变量文件dev时生效,VITE_BASEURL为接口地址  
├─ .env.production-----------------------------------环境变量文件build时生效,VITE_BASEURL为接口地址  
├─ .eslintignore-------------------------------------eslint豁免校验的配置,配置的文件免于eslint校验  
├─ .eslintrc.js--------------------------------------eslint配置,指定eslint校验规则  
├─ .gitignore  
├─ .husky--------------------------------------------git hooks,此处配置了pre-commit和commit-msg两个  
├─ .prettierrc---------------------------------------prettier配置,指定prettier校验规则  
├─ .vscode-------------------------------------------vscode配置,实现保存的时候格式化  
├─ CHANGELOG.md--------------------------------------版本发布日志  
├─ README.md  
├─ commitlint.config.js------------------------------commitlint配置文件  
├─ index.html  
├─ koa-mock------------------------------------------基于koa实现的本地mock项目  
│    ├─ api------------------------------------------将接口返回的json放在此目录,接口即为api/文件名  
├─ package.json  
├─ src-----------------------------------------------项目资源目录  
│    ├─ App.vue--------------------------------------uni的主组件,所有页面都是在App.vue下进行切换的,是页面入口文件  
│    ├─ api------------------------------------------API文件,用于集中管理接口  
│    ├─ http-----------------------------------------axios拦截器,用于自定义请求和响应拦截  
│    ├─ main.ts--------------------------------------入口文件,主要作用是初始化vue实例、定义全局组件  
│    ├─ manifest.json--------------------------------应用的配置文件,用于指定应用的名称、图标、权限等  
│    ├─ model----------------------------------------API接口用到的typescript模型,集中管理  
│    ├─ pages----------------------------------------业务页面  
│    ├─ pages.json-----------------------------------页面全局配置,决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等  
│    ├─ router---------------------------------------路由配置,导航守卫  
│    ├─ static---------------------------------------资源文件,图片等  
│    ├─ store----------------------------------------pinia配置,持久化存储  
│    │    ├─ index.ts  
│    │    └─ persist.ts------------------------------持久化插件  
│    ├─ types.d.ts-----------------------------------typescript的声明文件  
│    ├─ uni.scss-------------------------------------全局scss变量,无需引入可以直接使用  
│    └─ uni_modules----------------------------------uni-app插件  
│           ├─ fant-mini-plus------------------------一款vue3组件库  
│           └─ mp-html-------------------------------富文本插件  
├─ tsconfig.json-------------------------------------TypeScript 编译器的配置文件  
├─ versionrc.js--------------------------------------standard-version的配置文件  
├─ vite.config.ts------------------------------------用于配置 vite 的编译选项  
└─ yarn.lock-----------------------------------------Yarn生成的锁文件,它用于记录在使用Yarn安装包时确切的依赖关系和版本号

开发步骤

  • 安装依赖
    • 安装前端项目依赖:yarn
    • 安装mock项目依赖:yarn mock:install
  • 运行
    • 运行前端项目:yarn dev:h5 (其他平台命令在uni-app官网或者package.json中查看)
    • 运行mock项目:yarn mock:dev
  • 提交代码
    • 暂存准备提交的代码(举例添加当前目录下的所有文件到暂存区):git add .
    • 提交:yarn commit,输入之后回车,我们可以选择提交类型。
  • 发布版本(此处操作会使package.json中的version变更,可以此为版本号对显示)
    • 大版本升级1.0.0->2.0.0:yarn release-major
    • 中版本升级1.0.0->1.1.0:yarn release-minor
    • 小版本升级(一般是补丁)1.0.0->1.0.1:yarn release-patch
    • 注意:执行完发布版本的命令之后需要将执行终端中打印的命令将生成的tag推送到git。此操作完成之后会自动生成版本发布日志,可以用于对外发布

开发提示

  • 使用typescript开发可以提高前后端交互的效率,提升前端项目的健壮性。在开发过程中需要做到尽量减少使用any。
    小程序样式变量统一定义在uni.scss中,作为规范,若设计稿上使用的颜色存在于uni.scss中,则使用uni.scss中的变量,如不存在则需要沟通是否新增变量。

  • 提交代码时,feat和fix类型的提交尽量将任务单号作为前缀方便追踪改动关联的需求,如:feat: TASK-1000 新增某个功能。

  • css原子化,如果有需要可以自行引入UnoCSS,本项目暂时不会使用。

收起阅读 »

CanvasContext.drawImage iOS系统H5端图片空白

使用CanvasContext.drawImage时,在App、小程序、安卓系统H5都正常显示图片,但是在iOS系统H5显示空白(此时图片尺寸为:30244032)。经过测试,当把CanvasContext.drawImage(dx, dy, dWidth, dHeight)内的dWidth和dHeight调到一定尺寸就能正常显示,我使用的固定尺寸:10081344。测试环境信息:iphone 13,iOS16.4.1。
添加水印代码如下
setimg(path){
return new Promise((resolve, reject) => {
let date = this.timeFormat()
uni.getImageInfo({
   src: path,
   success: res => {
console.log(res)
// this.img = res
   this.canvasSiz.width = 1008;
this.canvasSiz.height = 1344;
//初始化画布
// #ifdef APP-PLUS || H5
const ctx = uni.createCanvasContext('canvas-clipper', this);
ctx.setFillStyle('#00ffff')
     ctx.fillRect(0, 0, this.canvasSiz.width, this.canvasSiz.height);
     ctx.drawImage(res.path, 0, 0, this.canvasSiz.width, this.canvasSiz.height);
     ctx.setFontSize(40);
     ctx.setFillStyle('#ff0000');
let textToWidth = this.canvasSiz.width/2-160;
let textToHeight = this.canvasSiz.height-50;
ctx.fillText(date, textToWidth, textToHeight);
setTimeout(() => {
       ctx.draw(false, () => {
         setTimeout(() => {
         uni.canvasToTempFilePath({
canvasId: 'canvas-clipper',
x: 0,
y: 0,
width: this.canvasSiz.width,
height: this.canvasSiz.height,
destWidth: this.canvasSiz.width,
destHeight: this.canvasSiz.height,
success: ress => {
// #ifdef H5
resolve(ress.tempFilePath)
// #endif
// #ifndef H5
uni.saveImageToPhotosAlbum({//保存到手机
    filePath: ress.tempFilePath,
     success: (res1) => {
resolve(res1.path)
    }
   })
// #endif
},
fail: (err) => {
  console.log(err);
}
        });
       }, 500);
    });
     },100);
// #endif
// #ifdef MP-WEIXIN
const query = wx.createSelectorQuery().in(this) // 创建一个dom元素节点查询器
query.select('#canvas-clipper')
.fields({ // 需要获取的节点相关信息
node: true, // 是否返回节点对应的 Node 实例
size: true // 是否返回节点尺寸(width height)
}).exec((res1) => {
// console.log(res1)
const canvas = res1[0].node // canvas就是我们要操作的画布节点
const ctx = canvas.getContext('2d') // 以2d模式,获取一个画布节点的上下文对象
// 不加这段,矩形底部会显示不全,我还没看懂
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = res.width dpr
canvas.height = res.height
dpr
ctx.scale(dpr, dpr)
let img = canvas.createImage()
img.src = path
img.onload = function(){
ctx.drawImage(img, 0, 0,res.width, res.height);
ctx.fillStyle = '#ff0000'
ctx.font = 'bold 68px serif'
let textToWidth = res.width/2-260;
let textToHeight = res.height-50;
ctx.fillText(date, textToWidth, textToHeight);
wx.canvasToTempFilePath({
canvas: canvas,
x: 0,
y: 0,
width: res.width,
height: res.height,
destWidth: res.width,
destHeight: res.height,
success(res2) {
uni.saveImageToPhotosAlbum({//保存到手机
    filePath: res2.tempFilePath,
     success: (res3) => {
resolve(res2.tempFilePath)
    }
   })
}
})
}
img.onerror = function(err){
console.log(err)
}
})
// #endif
   },
fail: (err) => {
  console.log(err);
}
});
})
},

继续阅读 »

使用CanvasContext.drawImage时,在App、小程序、安卓系统H5都正常显示图片,但是在iOS系统H5显示空白(此时图片尺寸为:30244032)。经过测试,当把CanvasContext.drawImage(dx, dy, dWidth, dHeight)内的dWidth和dHeight调到一定尺寸就能正常显示,我使用的固定尺寸:10081344。测试环境信息:iphone 13,iOS16.4.1。
添加水印代码如下
setimg(path){
return new Promise((resolve, reject) => {
let date = this.timeFormat()
uni.getImageInfo({
   src: path,
   success: res => {
console.log(res)
// this.img = res
   this.canvasSiz.width = 1008;
this.canvasSiz.height = 1344;
//初始化画布
// #ifdef APP-PLUS || H5
const ctx = uni.createCanvasContext('canvas-clipper', this);
ctx.setFillStyle('#00ffff')
     ctx.fillRect(0, 0, this.canvasSiz.width, this.canvasSiz.height);
     ctx.drawImage(res.path, 0, 0, this.canvasSiz.width, this.canvasSiz.height);
     ctx.setFontSize(40);
     ctx.setFillStyle('#ff0000');
let textToWidth = this.canvasSiz.width/2-160;
let textToHeight = this.canvasSiz.height-50;
ctx.fillText(date, textToWidth, textToHeight);
setTimeout(() => {
       ctx.draw(false, () => {
         setTimeout(() => {
         uni.canvasToTempFilePath({
canvasId: 'canvas-clipper',
x: 0,
y: 0,
width: this.canvasSiz.width,
height: this.canvasSiz.height,
destWidth: this.canvasSiz.width,
destHeight: this.canvasSiz.height,
success: ress => {
// #ifdef H5
resolve(ress.tempFilePath)
// #endif
// #ifndef H5
uni.saveImageToPhotosAlbum({//保存到手机
    filePath: ress.tempFilePath,
     success: (res1) => {
resolve(res1.path)
    }
   })
// #endif
},
fail: (err) => {
  console.log(err);
}
        });
       }, 500);
    });
     },100);
// #endif
// #ifdef MP-WEIXIN
const query = wx.createSelectorQuery().in(this) // 创建一个dom元素节点查询器
query.select('#canvas-clipper')
.fields({ // 需要获取的节点相关信息
node: true, // 是否返回节点对应的 Node 实例
size: true // 是否返回节点尺寸(width height)
}).exec((res1) => {
// console.log(res1)
const canvas = res1[0].node // canvas就是我们要操作的画布节点
const ctx = canvas.getContext('2d') // 以2d模式,获取一个画布节点的上下文对象
// 不加这段,矩形底部会显示不全,我还没看懂
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = res.width dpr
canvas.height = res.height
dpr
ctx.scale(dpr, dpr)
let img = canvas.createImage()
img.src = path
img.onload = function(){
ctx.drawImage(img, 0, 0,res.width, res.height);
ctx.fillStyle = '#ff0000'
ctx.font = 'bold 68px serif'
let textToWidth = res.width/2-260;
let textToHeight = res.height-50;
ctx.fillText(date, textToWidth, textToHeight);
wx.canvasToTempFilePath({
canvas: canvas,
x: 0,
y: 0,
width: res.width,
height: res.height,
destWidth: res.width,
destHeight: res.height,
success(res2) {
uni.saveImageToPhotosAlbum({//保存到手机
    filePath: res2.tempFilePath,
     success: (res3) => {
resolve(res2.tempFilePath)
    }
   })
}
})
}
img.onerror = function(err){
console.log(err)
}
})
// #endif
   },
fail: (err) => {
  console.log(err);
}
});
})
},

收起阅读 »

微信登录H5页面的10003错误:scope参数错误或没有权限

微信登录

先说结论:只有已经通过认证的服务号才能解决。

找到微信论坛的帖子:
https://developers.weixin.qq.com/doc/search.html?source=enter&query=10003&doc_type=offiaccount&jumpbackUrl=%2Fdoc%2F&timestamp=1684372079

再看公众号--设置与开发--接口权限:
网页授权仅限通过微信认证的服务号
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

特别注意:个人订阅号,无法通过微信认证。

!!也就是个人开发者,如果你没有能搞到一个通过微信认证的服务号,就不要搞“微信登录”了。

非常希望Dcloud改进文档
应该从一开始就在uni-id介绍微信登录的地方,醒目提示这个关键信息啊。浪费我太多太多精力和时间在此问题的探索和解答上了。

Dcloud你们有很好的地方,但是文档和问题答疑方面问题太大了,你们真的反思一下商业模式了。

继续阅读 »

先说结论:只有已经通过认证的服务号才能解决。

找到微信论坛的帖子:
https://developers.weixin.qq.com/doc/search.html?source=enter&query=10003&doc_type=offiaccount&jumpbackUrl=%2Fdoc%2F&timestamp=1684372079

再看公众号--设置与开发--接口权限:
网页授权仅限通过微信认证的服务号
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

特别注意:个人订阅号,无法通过微信认证。

!!也就是个人开发者,如果你没有能搞到一个通过微信认证的服务号,就不要搞“微信登录”了。

非常希望Dcloud改进文档
应该从一开始就在uni-id介绍微信登录的地方,醒目提示这个关键信息啊。浪费我太多太多精力和时间在此问题的探索和解答上了。

Dcloud你们有很好的地方,但是文档和问题答疑方面问题太大了,你们真的反思一下商业模式了。

收起阅读 »

uni-ai 实现一个简单的流式响应代码

stream

这是云对象中的代码

// 流式输出的ai调用  
    async streamAi(obj, sseChannel) {  

        // 根据类型拼接提示词  
        let messages  
        switch (obj.type) {  
            case 'grammar':  
                messages = grammarMsg(obj.msg)  
                break  
            case '问答':  
                messages = qaMsg(obj.question, obj.msg)  
                break  
            case '翻译':  
                messages = translatorMsg(obj.question, obj.msg)  
                break;  
            case 'word':  
                messages = wordMsg(obj.msg)  
                break;  
        }  

        // AI对象  
        const LLMManager = uniCloud.ai.getLLMManager({  
            provider: 'openai',  
            apiKey: '*****',  
            proxy: '*****'  
        })  

        // 调用 ai 开通 stream   
        let res = await LLMManager.chatCompletion({  
            messages,    // 传给 openai api 的信息  
            sseChannel,   // 从前端传过来的 sseChannel 通道  
            stream: true  // 开通流式数据  
        })  
        if (res) {  

            let reply = ""   // 这个是用来拼接 openai 返回的数据,跟返回客户端的没有关系,用来保存到数据库  

            return new Promise((resolve, reject) => {          //流式给客户端返回数据  

                const channel = uniCloud.deserializeSSEChannel(sseChannel)   // 这个不知道干啥,反正通过这个得到 channel 的通道  
                                // 注意这里,openai 是一个字一个字返回的,有些ai是一句一句返回的,还有一人 on('line') 的事件,是用来接收一句一句返回的。我用的是openai所有没有写那个 line 事件。  
                res.on('message', async (message) => {        // openai 返回数据时 会触发 这个 message 回调 在这里向客户端一点点发送数据。  
                    reply += message   
                    await channel.write(message)  // 这个 write 会向客户端发送数据 openai 是一个字一个字发送  
                    // console.log('---message----', message)  
                })  
                res.on('end', async () => {  // 发送完毕后 会触发这个事件 这里可以 做一些其他操作  

                    // 保存用户内容  这个不用看,是把上面 reply 拼接的数据保存到数据库  
                    if (obj.type == "问答" || obj.type == "翻译") {  
                        // 把我的回答保存到数据库  
                        let res = await dbJql.collection('bl_answer').where(  
                            `userId=="${obj.id}"&&contentId=="${obj.contentId}"`).get()  
                        if (res.data.length == 1) {  
                            dbJql.collection('bl_answer').where(  
                                    `userId=="${obj.id}"&&contentId=="${obj.contentId}"`)  
                                .update({  
                                    myanswer: obj.msg,  
                                })  
                        } else {  
                            dbJql.collection('bl_answer').add({  
                                myanswer: obj.msg,  
                                contentId: obj.contentId,  
                                userId: obj.id  
                            })  
                        }  
                    }  

                    // 输出结束后  
                    await channel.end(points)   // 这个 end 是告诉前端客户端数据发送结束了,也可以带一个参数,跟 return 差不多。  

                    resolve({  
                        errCode: 0  
                    })  
                })  
                res.on('error', (err) => {  
                    console.error('---error----', err)  
                    reject(err)  
                })  
            })  
        }  
    }

这是前端代码

async onAi() {  

                // 这是提交给后端的数据  
                let contentObj = {  
                    msg: this.msg, //这个是关键数据,就是你要发送给 openai 的内容  
                    id: this.$props.user._id,  
                    type: this.$props.data.type,  
                    points: this.$props.data.points,  
                    contentId: this.$props.data._id._value,   
                    question: this.$props.data.question,   
                }  

                // ai 调用  这个是正常的云对象调用  
                // const aiReplyDB = uniCloud.importObject('aiReplyObj')  
                // let res = await aiReplyDB.ai(contentObj)  

                // 流式 ai 调用  
                let sseChannel = new uniCloud.SSEChannel()  // 这个就是提交给云对象的通道,通过这个接收数据。  
                sseChannel.on('message', (message) => {  // 这个跟后端的一样,后面 write 的时候 会触发这个 message 事件 用来接收后端发送过来的数据  
                    // console.log(message)  
                    this.aiReply += message  // 这里把数据显示到界面上,就会出现打字机的效果(一点一点输出内容)  
                })  

                sseChannel.on('end', (points) => {  //后端的 end() 会触发这个 end 做一些其他的操作。  
                    // uni.hideLoading()  
                    // console.log(points)  
                    let resData = {  
                        points: points,  
                        answer: this.msg  
                    }  
                    this.curMsg = this.msg  
                    //回调父组件函数e  
                    this.$emit('onAi', resData)  // 这个是组件回调给父组件的函数,这个onAi是父组件的函数,这里和上面的 onAi名字写成一样的了,跟上面的没关系了。  
                })  

                await sseChannel.open()  // 这个是打开通道(上面两个 sseChannel.on() 是绑定事件,是在返回数据的时候执行。并不是执行了上面的sseChannel.on()再来执行这个 sseChannel.open() )  

                                // 上面的都是准备,这里才开始调用云对象  
                const streamAi = uniCloud.importObject('aiReplyObj')  
                streamAi.streamAi(contentObj, sseChannel).then(res => {  
                    // console.log("then", res)  
                    sseChannel.close()  // 这个代码是我自己加的,感觉还是要把之前的 open 给关闭掉。  
                })  

            }

这些是前后端实现一个简单的流式数据请求的代码。在这之前,还要开通 uniPush 2.0 这个内容的文档写的还可以,能看明白。
https://zh.uniapp.dcloud.io/unipush-v2.html#%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B

对了,有一个很重要的事情,我写完代码的时候有一个错误提示:没有这张表:opendb-tempdata。 所以数据库里要新建一张叫:opendb-tempdata 的数据表。保存uniPush 的ID 好像是。

继续阅读 »

这是云对象中的代码

// 流式输出的ai调用  
    async streamAi(obj, sseChannel) {  

        // 根据类型拼接提示词  
        let messages  
        switch (obj.type) {  
            case 'grammar':  
                messages = grammarMsg(obj.msg)  
                break  
            case '问答':  
                messages = qaMsg(obj.question, obj.msg)  
                break  
            case '翻译':  
                messages = translatorMsg(obj.question, obj.msg)  
                break;  
            case 'word':  
                messages = wordMsg(obj.msg)  
                break;  
        }  

        // AI对象  
        const LLMManager = uniCloud.ai.getLLMManager({  
            provider: 'openai',  
            apiKey: '*****',  
            proxy: '*****'  
        })  

        // 调用 ai 开通 stream   
        let res = await LLMManager.chatCompletion({  
            messages,    // 传给 openai api 的信息  
            sseChannel,   // 从前端传过来的 sseChannel 通道  
            stream: true  // 开通流式数据  
        })  
        if (res) {  

            let reply = ""   // 这个是用来拼接 openai 返回的数据,跟返回客户端的没有关系,用来保存到数据库  

            return new Promise((resolve, reject) => {          //流式给客户端返回数据  

                const channel = uniCloud.deserializeSSEChannel(sseChannel)   // 这个不知道干啥,反正通过这个得到 channel 的通道  
                                // 注意这里,openai 是一个字一个字返回的,有些ai是一句一句返回的,还有一人 on('line') 的事件,是用来接收一句一句返回的。我用的是openai所有没有写那个 line 事件。  
                res.on('message', async (message) => {        // openai 返回数据时 会触发 这个 message 回调 在这里向客户端一点点发送数据。  
                    reply += message   
                    await channel.write(message)  // 这个 write 会向客户端发送数据 openai 是一个字一个字发送  
                    // console.log('---message----', message)  
                })  
                res.on('end', async () => {  // 发送完毕后 会触发这个事件 这里可以 做一些其他操作  

                    // 保存用户内容  这个不用看,是把上面 reply 拼接的数据保存到数据库  
                    if (obj.type == "问答" || obj.type == "翻译") {  
                        // 把我的回答保存到数据库  
                        let res = await dbJql.collection('bl_answer').where(  
                            `userId=="${obj.id}"&&contentId=="${obj.contentId}"`).get()  
                        if (res.data.length == 1) {  
                            dbJql.collection('bl_answer').where(  
                                    `userId=="${obj.id}"&&contentId=="${obj.contentId}"`)  
                                .update({  
                                    myanswer: obj.msg,  
                                })  
                        } else {  
                            dbJql.collection('bl_answer').add({  
                                myanswer: obj.msg,  
                                contentId: obj.contentId,  
                                userId: obj.id  
                            })  
                        }  
                    }  

                    // 输出结束后  
                    await channel.end(points)   // 这个 end 是告诉前端客户端数据发送结束了,也可以带一个参数,跟 return 差不多。  

                    resolve({  
                        errCode: 0  
                    })  
                })  
                res.on('error', (err) => {  
                    console.error('---error----', err)  
                    reject(err)  
                })  
            })  
        }  
    }

这是前端代码

async onAi() {  

                // 这是提交给后端的数据  
                let contentObj = {  
                    msg: this.msg, //这个是关键数据,就是你要发送给 openai 的内容  
                    id: this.$props.user._id,  
                    type: this.$props.data.type,  
                    points: this.$props.data.points,  
                    contentId: this.$props.data._id._value,   
                    question: this.$props.data.question,   
                }  

                // ai 调用  这个是正常的云对象调用  
                // const aiReplyDB = uniCloud.importObject('aiReplyObj')  
                // let res = await aiReplyDB.ai(contentObj)  

                // 流式 ai 调用  
                let sseChannel = new uniCloud.SSEChannel()  // 这个就是提交给云对象的通道,通过这个接收数据。  
                sseChannel.on('message', (message) => {  // 这个跟后端的一样,后面 write 的时候 会触发这个 message 事件 用来接收后端发送过来的数据  
                    // console.log(message)  
                    this.aiReply += message  // 这里把数据显示到界面上,就会出现打字机的效果(一点一点输出内容)  
                })  

                sseChannel.on('end', (points) => {  //后端的 end() 会触发这个 end 做一些其他的操作。  
                    // uni.hideLoading()  
                    // console.log(points)  
                    let resData = {  
                        points: points,  
                        answer: this.msg  
                    }  
                    this.curMsg = this.msg  
                    //回调父组件函数e  
                    this.$emit('onAi', resData)  // 这个是组件回调给父组件的函数,这个onAi是父组件的函数,这里和上面的 onAi名字写成一样的了,跟上面的没关系了。  
                })  

                await sseChannel.open()  // 这个是打开通道(上面两个 sseChannel.on() 是绑定事件,是在返回数据的时候执行。并不是执行了上面的sseChannel.on()再来执行这个 sseChannel.open() )  

                                // 上面的都是准备,这里才开始调用云对象  
                const streamAi = uniCloud.importObject('aiReplyObj')  
                streamAi.streamAi(contentObj, sseChannel).then(res => {  
                    // console.log("then", res)  
                    sseChannel.close()  // 这个代码是我自己加的,感觉还是要把之前的 open 给关闭掉。  
                })  

            }

这些是前后端实现一个简单的流式数据请求的代码。在这之前,还要开通 uniPush 2.0 这个内容的文档写的还可以,能看明白。
https://zh.uniapp.dcloud.io/unipush-v2.html#%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B

对了,有一个很重要的事情,我写完代码的时候有一个错误提示:没有这张表:opendb-tempdata。 所以数据库里要新建一张叫:opendb-tempdata 的数据表。保存uniPush 的ID 好像是。

收起阅读 »

uni-file-picker自定义上传时,展示进度条

实现思路:根据源码得知,进度条状态通过文件 uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue 中的setProgress设置。修改思路也是基于此方法。

  1. 自定义上传时,自定义一个回调方法,拿到progress,接下来会用到。
    /**  
    * @description 文件上传  
    * @param filePath 文件路径  
    * @param uuid 文件uuid  
    * @param progressCallBack 上传进度回调  
    */  
    export function uploadFile(filePath, uuid, progressCallBack) {  
    return new Promise(resolve => {  
        const uploadTask = uni.uploadFile({  
            header: {  
                'token': uni.getStorageSync('token'),  
                'enterpriseId': 'test'  
            },  
            url: `${baseUrl}/file-serv/file/upload`, // 上传图片的接口  
            filePath,  
            formData: {  
                name: 'file'  
            },  
            success: async (uploadFileRes) => {  
                // 获取图片list  
                const {  
                    data  
                } = uploadFileRes  
                const {  
                    code,  
                    data: d  
                } = JSON.parse(data)  
                if (code == 200) {  
                    resolve({  
                        url: d.url,  
                        uuid  
                    })  
                }  
            }  
        })  
        // 通过uploadTask 监听上传进度   
        uploadTask.onProgressUpdate(res => {  
            progressCallBack(res)  
        })  
    })  
    }

    上传文件时调用:

    async onSelect(e) { // 组件回调方法,在这里做文件上传动作  
    const _this = this  
    const uuid = e.tempFiles[0]['uuid']  
    const res = await uploadFile(e.tempFilePaths[0], uuid, function(progress) {  
        console.log('progress:', progress)  
        _this.$refs.uploadFile.setProgressByCustomUplaod(uuid, progress.progress) // 通过组件实例调用设置进度条的方法  
    })  
    },
  2. 在组件中设置ref,目的是得到组件实例。通过实例即可调用组件方法setProgress。为了与源代码区分,定义新的方法setProgressByCustomUplaod
    <uni-file-picker ref="uploadFile" fileMediatype="image" @select="onSelect" @delete="onDelete" :limit="5" />

    调用:_this.$refs.uploadFile.setProgressByCustomUplaod(uuid, progress.progress)

  3. uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue文件中新增方法setProgressByCustomUplaod
    /**  
    * @description 自定义上传时,设置进度条状态  
    * @param uuid 文件uuid,目的是通过uuid从文件列表中取出当前正在上传的文件  
    * @param progress 上传进度  
    */  
    setProgressByCustomUplaod(uuid, progress) {  
    const currentFile = this.files.find(item => item.uuid == uuid)  
    if (currentFile) {  
        currentFile.progress = progress  
    }  
    }
继续阅读 »

实现思路:根据源码得知,进度条状态通过文件 uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue 中的setProgress设置。修改思路也是基于此方法。

  1. 自定义上传时,自定义一个回调方法,拿到progress,接下来会用到。
    /**  
    * @description 文件上传  
    * @param filePath 文件路径  
    * @param uuid 文件uuid  
    * @param progressCallBack 上传进度回调  
    */  
    export function uploadFile(filePath, uuid, progressCallBack) {  
    return new Promise(resolve => {  
        const uploadTask = uni.uploadFile({  
            header: {  
                'token': uni.getStorageSync('token'),  
                'enterpriseId': 'test'  
            },  
            url: `${baseUrl}/file-serv/file/upload`, // 上传图片的接口  
            filePath,  
            formData: {  
                name: 'file'  
            },  
            success: async (uploadFileRes) => {  
                // 获取图片list  
                const {  
                    data  
                } = uploadFileRes  
                const {  
                    code,  
                    data: d  
                } = JSON.parse(data)  
                if (code == 200) {  
                    resolve({  
                        url: d.url,  
                        uuid  
                    })  
                }  
            }  
        })  
        // 通过uploadTask 监听上传进度   
        uploadTask.onProgressUpdate(res => {  
            progressCallBack(res)  
        })  
    })  
    }

    上传文件时调用:

    async onSelect(e) { // 组件回调方法,在这里做文件上传动作  
    const _this = this  
    const uuid = e.tempFiles[0]['uuid']  
    const res = await uploadFile(e.tempFilePaths[0], uuid, function(progress) {  
        console.log('progress:', progress)  
        _this.$refs.uploadFile.setProgressByCustomUplaod(uuid, progress.progress) // 通过组件实例调用设置进度条的方法  
    })  
    },
  2. 在组件中设置ref,目的是得到组件实例。通过实例即可调用组件方法setProgress。为了与源代码区分,定义新的方法setProgressByCustomUplaod
    <uni-file-picker ref="uploadFile" fileMediatype="image" @select="onSelect" @delete="onDelete" :limit="5" />

    调用:_this.$refs.uploadFile.setProgressByCustomUplaod(uuid, progress.progress)

  3. uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue文件中新增方法setProgressByCustomUplaod
    /**  
    * @description 自定义上传时,设置进度条状态  
    * @param uuid 文件uuid,目的是通过uuid从文件列表中取出当前正在上传的文件  
    * @param progress 上传进度  
    */  
    setProgressByCustomUplaod(uuid, progress) {  
    const currentFile = this.files.find(item => item.uuid == uuid)  
    if (currentFile) {  
        currentFile.progress = progress  
    }  
    }
收起阅读 »

使用uni.downloadFile在微信小程序内下载文件发现一个很神奇的现象

downloadFile

小程序内使用wx.downloadFile是限制最大文件200M的


我在fail回调内做了判断用来提示用户

在实际使用中我发现,哪怕文件超过200M,uni.downloadFile这个API也并不会进入fail回调中,而是会直接开始下载,在将超时时间设置的足够久以后,多大的文件都能下载成功并保存至相册。

所以不清楚是uni.downloadFile这个API没有文件200M以内的限制呢,还是微信那边取消了200M以内的限制

继续阅读 »

小程序内使用wx.downloadFile是限制最大文件200M的


我在fail回调内做了判断用来提示用户

在实际使用中我发现,哪怕文件超过200M,uni.downloadFile这个API也并不会进入fail回调中,而是会直接开始下载,在将超时时间设置的足够久以后,多大的文件都能下载成功并保存至相册。

所以不清楚是uni.downloadFile这个API没有文件200M以内的限制呢,还是微信那边取消了200M以内的限制

收起阅读 »

多客交友社交系统/H5多端圈子社区论坛系统交友/博客/社交/陌生人社交即时聊天私域话题社区论

DUOKE多客圈子论坛社区系统,含完整的后台PHP系统。功能:小程序授权登陆,H5和APP,手机号登陆,发帖,建圈子、发活动。圈主可置顶推荐帖子,关注、粉丝、点赞等。可作为圈子贴吧、小红书等自媒体。

多客圈子论坛社区(交友、博客、社交、陌生人社交、即时聊天)前后端开源PHP - 更新日志
1.9(2023-05-16)
1.修复头像上传问题。 2.修复上传图片问题

1.8(2023-03-30)
修改小程序获取头像和昵称接口。可直接单独读取头像和昵称。

1.7(2023-01-13)
增加后台添加帖子 增加后台帖子处理 完善七牛审核

1.3(2022-12-08)
1、取消七牛审核功能,因大家都说不方便。 最新版后端请去gie仓库拿后台。 2、增加微信登录后无法获得头像和昵称,改为新用户强制填写资料。 开源版不易,如果你不喜欢出门左转,请勿恶意差评、诋毁。

1.2.91(2022-11-21)
小程序收回获取头像昵称功能,后端也做相应调整,重新登录时不在获取新昵称。

1.2.9(2022-11-09)
1、优化发帖模式

2、增加圈子展示高度

3、关联关注及点赞数量

1.2.8(2022-10-28)
更新头像上传返回空白 增加部分新功能

1.2.7(2022-10-18)
修复后台上传图片编辑器路径不对。

1.2.6(2022-10-13)
修复安卓打包不能看帖子问题 修复发布活动选择地址问题 请在app设置里配置你的高德地图。

1.2.5(2022-10-08)
修复头像上传问题,增加头像剪裁 增加评论回复按钮 修复UI错位

1.2.4(2022-10-03)
1、修复禁言后发帖功能 2、修复linxun系统首页问题。 3、增加常见问答

1.2.3(2022-09-28)
1、更新APP打包后地图打不开问题 2、更新APP版本发帖按钮问题 3、更新APP打开活动地图无法显示问题。

1.2.2(2022-09-27)
1、修复打包APP不能进入登录页问题 2、修复首页切换热门不能读取帖子问题 3、修复一直UI问题。

1.2.1(2022-09-26)
1、增加勋章功能和领取 2、增加等级,根据经验值获得,经验值在后台填写 3、增加圈主可置顶 删除本圈下面的任意帖子。 4、其他BUG

                                                                            **  多客圈子社区系统免费开源版4.2版**  

● 多客开源团队(www.51duoke.cn)2022年伤肝之作!系统基于TP6 Uni-app框架开发;客户移动端采用uni-app开发,管理后台TH6开发。
● 系统支持微信公众号端、微信小程序端、H5端、PC端多端账号同步,可快速打包生成APP。
● 我们为你准备了完善的后台管理,不需要你懂PHP,按照教程10分钟安装完即可使用,堪比深夜的杜蕾斯还方便。
● 我们为你准备漂亮的UI前端,导入UNI,2分钟编译为小程序,3分钟编译为安卓app,5分钟编译为ios系统。
● 如果,我是说如果,你啥都不会,服务器不会架设,数据库也不会操作,打包也不会,联系我们即可。我们提供一整套安装调试上架服务。
● 当然我们也有商业pro版,已增加各种酷炫功能。
● 当然我们也会接二次定制开发的版本,满足各种需求,展开您的想象,您就可以拥有。

免费开源基础版后端演示
http://quan.51duoke.cn/admin 账号demo 密码demo (仅有查看权限)

后端下载地址:https://gitee.com/multi-customer-open-source/multi-social

后端程序目录
系统需求

PHP >= 7.2.5
MySQL >= 5.6.3
Redis
uniapp安装
将前端UNI目录导入到你的HBuilder里

修改tools/siteinfo.js 里的域名为你的域名即可

如果打包小程序及app 请参考uniapp官方文档

后端安装
将后端php代码放到你的网站根目录即可

====运行WEB目录==== public

====数据库==== 导入目录下的 db.sql 文件 修改目录下的 .env 数据库配置

配置文件路径/.env

APP_DEBUG = true

[APP]
DEFAULT_TIMEZONE = Asia/Shanghai

[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1 #数据库连接地址
DATABASE = test #数据库名称
USERNAME = username #数据库登录账号
PASSWORD = password #数据库登录密码
HOSTPORT = 3306 #数据库端口
CHARSET = utf8
DEBUG = true

[LANG]
default_lang = zh-cn

[REDIS]
REDIS_HOSTNAME = 127.0.0.1 # redis链接地址
PORT = 6379 #端口号
REDIS_PASSWORD = 123456 #密码
SELECT = 0 #数据库
3.修改目录权限(linux系统)777 /public

====后台登陆==== http://域名/admin 默认账号:admin 密码:6192652

功能简介
1、采用uniapp开发前端,可打包成小程序、APP安卓、app苹果、支付宝小程序等。具体请到uniapp官网查看

2、APP采用短信验证登录,小程序采用微信授权登录。

3、圈子支持后台建立和前台用户新建

4、用户发布帖子可选择圈子和话题,

5、帖子可后台添加和审核,及推荐和热门等操作

6、上传集成阿里云oss和七牛云,也可以本地储存

7、发布内容自动安全检测,采用七牛内容检测接口。

8、支持图片多图上传和拖动排序

9、支持会员等级

10、帖子支持圈子管理员前端推荐和置顶

11、支持勋章和用户等级

Admin(后台功能)
├─ 管理首页
| ├─介绍版本信息、数据统计、常用模块、Echart数据概览
├─ 菜单管理
| ├─后台权限菜单管理 编辑访客权限,处理菜单父子关系,被权限系统依赖(极为重要)
├─ 系统管理
| ├─ 用户管理 - 添加新用户,封号,删号以及给账号分配权限组
| ├─ 权限管理 - 权限组管理,给权限组添加权限,将用户提出权限组
| └─ 上传管理 - 记录所有上传的图片文件信息、定位文件位置大小以及上传时间
├─ 配置管理
| ├─ 基本设置 - 配置网站基本信息:标题、域名、客服电话、前端主题配色、前端字体颜色等前端基础配置信息修改
| ├─ 上传配置 - 文件存储方式选择:本地存储、阿里云OSS、七牛云配置
| └─ 操作日志 - 记录管理员的操作,用于追责,回溯和备案
├─ 站点设置
| ├─ 广告管理 - 删改查
| ├─ 首页管理 - ....
| └─ 单页管理 - ....
├─ 圈子管理
| ├─ 圈子列表 - ....
| └─ 话题管理 - ....
├─ 会员管理
| ├─ 会员列表 - ....
| └─ 勋章管理 - ....
├─ ......

前端页面展示
首页和广场

文档
TP6开发手册

uniapp开发手册

安装使用常见问答
1、关于敏感词过滤问题? 答:请到七牛云注册账号,在我的里会有AccessKey和AccessKeySecret,填写到我们后台的 上传配置七牛云那里,点击保存。 如果是想使用七牛云的图片储存请继续填写后面的空间名和域名等信息,如果不想使用七牛云储存,只需要填写key和secret。然后在选择储存方式为本地。 意思是七牛云这个必填。否则无法过滤。

如果不想过滤请到app/controller/user.php 这个接口文件里搜索 checkCloseSpeck 并把这一行注释掉。

2、关于APP版 发布活动选择地址和查看地址失效问题? 答:请在Hbiuder里 打开manifest.json, 点击APP模块配置,选中maps,选择你要使用那个地图,一般推荐高德和腾讯。请到高德或腾讯开放平台 注册账号,并添加应用。获取到key填写到这里。 因为APP采用的是sdk地图。小程序不需要考虑,是直接用微信的地图。

3、关于短信登录问题? 答:后台关闭短信验证,是为了测试使用的,或者是短信通道临时故障之类使用。就是用户不需要接收短信,直接可以登录。当正式版本发布后,建议你开启短信验证,并去阿里云购买短信,填写在后台即可使用。

4、我们的演示APP或者小程序为啥不能发帖子? 答:演示已经关闭发布功能,因为测试的人太多,天天乱发,影响测试的美观。 请自己搭建后 自己发布到你的数据库去。

5、linux系统需要配置伪静态代码 location / { if (!-e request_filename){ rewrite ^(.*) /index.php?s=$1 last; break; } }

6、windows系统设置伪静态。

请在宝塔里设置伪静态选择TH6 。

7、出现api接口读取500错误,

答:原因很多,大部分是 没有设置伪静态,请看第5条。或者是数据库没连接上。

8、安装后台后访问出现404找不到页面

答:大部分使用为宝塔的网站管理开启了防跨站,请关闭。

不断更新中....

特别鸣谢
排名不分先后,感谢这些软件的开发者:thinkphp、小牛admin、图鸟UI、vue、mysql、redis、uniapp等!

开源版使用须知
1.允许用于个人学习、毕业设计、教学案例、公益事业、商业使用;

2.如果商用必须保留版权信息,请自觉遵守;

3.禁止将本项目的代码和资源进行任何形式的出售,产生的一切任何后果责任由侵权者自负。

版权信息
版权所有Copyright © 2016-2022 by 四川掌上时代科技有限公司旗下多客开源事业部 (http://www.51duoke.cn http://www.siyibang.com)

All rights reserved。

著作权所有者为四川掌上时代科技有限公司。

继续阅读 »

DUOKE多客圈子论坛社区系统,含完整的后台PHP系统。功能:小程序授权登陆,H5和APP,手机号登陆,发帖,建圈子、发活动。圈主可置顶推荐帖子,关注、粉丝、点赞等。可作为圈子贴吧、小红书等自媒体。

多客圈子论坛社区(交友、博客、社交、陌生人社交、即时聊天)前后端开源PHP - 更新日志
1.9(2023-05-16)
1.修复头像上传问题。 2.修复上传图片问题

1.8(2023-03-30)
修改小程序获取头像和昵称接口。可直接单独读取头像和昵称。

1.7(2023-01-13)
增加后台添加帖子 增加后台帖子处理 完善七牛审核

1.3(2022-12-08)
1、取消七牛审核功能,因大家都说不方便。 最新版后端请去gie仓库拿后台。 2、增加微信登录后无法获得头像和昵称,改为新用户强制填写资料。 开源版不易,如果你不喜欢出门左转,请勿恶意差评、诋毁。

1.2.91(2022-11-21)
小程序收回获取头像昵称功能,后端也做相应调整,重新登录时不在获取新昵称。

1.2.9(2022-11-09)
1、优化发帖模式

2、增加圈子展示高度

3、关联关注及点赞数量

1.2.8(2022-10-28)
更新头像上传返回空白 增加部分新功能

1.2.7(2022-10-18)
修复后台上传图片编辑器路径不对。

1.2.6(2022-10-13)
修复安卓打包不能看帖子问题 修复发布活动选择地址问题 请在app设置里配置你的高德地图。

1.2.5(2022-10-08)
修复头像上传问题,增加头像剪裁 增加评论回复按钮 修复UI错位

1.2.4(2022-10-03)
1、修复禁言后发帖功能 2、修复linxun系统首页问题。 3、增加常见问答

1.2.3(2022-09-28)
1、更新APP打包后地图打不开问题 2、更新APP版本发帖按钮问题 3、更新APP打开活动地图无法显示问题。

1.2.2(2022-09-27)
1、修复打包APP不能进入登录页问题 2、修复首页切换热门不能读取帖子问题 3、修复一直UI问题。

1.2.1(2022-09-26)
1、增加勋章功能和领取 2、增加等级,根据经验值获得,经验值在后台填写 3、增加圈主可置顶 删除本圈下面的任意帖子。 4、其他BUG

                                                                            **  多客圈子社区系统免费开源版4.2版**  

● 多客开源团队(www.51duoke.cn)2022年伤肝之作!系统基于TP6 Uni-app框架开发;客户移动端采用uni-app开发,管理后台TH6开发。
● 系统支持微信公众号端、微信小程序端、H5端、PC端多端账号同步,可快速打包生成APP。
● 我们为你准备了完善的后台管理,不需要你懂PHP,按照教程10分钟安装完即可使用,堪比深夜的杜蕾斯还方便。
● 我们为你准备漂亮的UI前端,导入UNI,2分钟编译为小程序,3分钟编译为安卓app,5分钟编译为ios系统。
● 如果,我是说如果,你啥都不会,服务器不会架设,数据库也不会操作,打包也不会,联系我们即可。我们提供一整套安装调试上架服务。
● 当然我们也有商业pro版,已增加各种酷炫功能。
● 当然我们也会接二次定制开发的版本,满足各种需求,展开您的想象,您就可以拥有。

免费开源基础版后端演示
http://quan.51duoke.cn/admin 账号demo 密码demo (仅有查看权限)

后端下载地址:https://gitee.com/multi-customer-open-source/multi-social

后端程序目录
系统需求

PHP >= 7.2.5
MySQL >= 5.6.3
Redis
uniapp安装
将前端UNI目录导入到你的HBuilder里

修改tools/siteinfo.js 里的域名为你的域名即可

如果打包小程序及app 请参考uniapp官方文档

后端安装
将后端php代码放到你的网站根目录即可

====运行WEB目录==== public

====数据库==== 导入目录下的 db.sql 文件 修改目录下的 .env 数据库配置

配置文件路径/.env

APP_DEBUG = true

[APP]
DEFAULT_TIMEZONE = Asia/Shanghai

[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1 #数据库连接地址
DATABASE = test #数据库名称
USERNAME = username #数据库登录账号
PASSWORD = password #数据库登录密码
HOSTPORT = 3306 #数据库端口
CHARSET = utf8
DEBUG = true

[LANG]
default_lang = zh-cn

[REDIS]
REDIS_HOSTNAME = 127.0.0.1 # redis链接地址
PORT = 6379 #端口号
REDIS_PASSWORD = 123456 #密码
SELECT = 0 #数据库
3.修改目录权限(linux系统)777 /public

====后台登陆==== http://域名/admin 默认账号:admin 密码:6192652

功能简介
1、采用uniapp开发前端,可打包成小程序、APP安卓、app苹果、支付宝小程序等。具体请到uniapp官网查看

2、APP采用短信验证登录,小程序采用微信授权登录。

3、圈子支持后台建立和前台用户新建

4、用户发布帖子可选择圈子和话题,

5、帖子可后台添加和审核,及推荐和热门等操作

6、上传集成阿里云oss和七牛云,也可以本地储存

7、发布内容自动安全检测,采用七牛内容检测接口。

8、支持图片多图上传和拖动排序

9、支持会员等级

10、帖子支持圈子管理员前端推荐和置顶

11、支持勋章和用户等级

Admin(后台功能)
├─ 管理首页
| ├─介绍版本信息、数据统计、常用模块、Echart数据概览
├─ 菜单管理
| ├─后台权限菜单管理 编辑访客权限,处理菜单父子关系,被权限系统依赖(极为重要)
├─ 系统管理
| ├─ 用户管理 - 添加新用户,封号,删号以及给账号分配权限组
| ├─ 权限管理 - 权限组管理,给权限组添加权限,将用户提出权限组
| └─ 上传管理 - 记录所有上传的图片文件信息、定位文件位置大小以及上传时间
├─ 配置管理
| ├─ 基本设置 - 配置网站基本信息:标题、域名、客服电话、前端主题配色、前端字体颜色等前端基础配置信息修改
| ├─ 上传配置 - 文件存储方式选择:本地存储、阿里云OSS、七牛云配置
| └─ 操作日志 - 记录管理员的操作,用于追责,回溯和备案
├─ 站点设置
| ├─ 广告管理 - 删改查
| ├─ 首页管理 - ....
| └─ 单页管理 - ....
├─ 圈子管理
| ├─ 圈子列表 - ....
| └─ 话题管理 - ....
├─ 会员管理
| ├─ 会员列表 - ....
| └─ 勋章管理 - ....
├─ ......

前端页面展示
首页和广场

文档
TP6开发手册

uniapp开发手册

安装使用常见问答
1、关于敏感词过滤问题? 答:请到七牛云注册账号,在我的里会有AccessKey和AccessKeySecret,填写到我们后台的 上传配置七牛云那里,点击保存。 如果是想使用七牛云的图片储存请继续填写后面的空间名和域名等信息,如果不想使用七牛云储存,只需要填写key和secret。然后在选择储存方式为本地。 意思是七牛云这个必填。否则无法过滤。

如果不想过滤请到app/controller/user.php 这个接口文件里搜索 checkCloseSpeck 并把这一行注释掉。

2、关于APP版 发布活动选择地址和查看地址失效问题? 答:请在Hbiuder里 打开manifest.json, 点击APP模块配置,选中maps,选择你要使用那个地图,一般推荐高德和腾讯。请到高德或腾讯开放平台 注册账号,并添加应用。获取到key填写到这里。 因为APP采用的是sdk地图。小程序不需要考虑,是直接用微信的地图。

3、关于短信登录问题? 答:后台关闭短信验证,是为了测试使用的,或者是短信通道临时故障之类使用。就是用户不需要接收短信,直接可以登录。当正式版本发布后,建议你开启短信验证,并去阿里云购买短信,填写在后台即可使用。

4、我们的演示APP或者小程序为啥不能发帖子? 答:演示已经关闭发布功能,因为测试的人太多,天天乱发,影响测试的美观。 请自己搭建后 自己发布到你的数据库去。

5、linux系统需要配置伪静态代码 location / { if (!-e request_filename){ rewrite ^(.*) /index.php?s=$1 last; break; } }

6、windows系统设置伪静态。

请在宝塔里设置伪静态选择TH6 。

7、出现api接口读取500错误,

答:原因很多,大部分是 没有设置伪静态,请看第5条。或者是数据库没连接上。

8、安装后台后访问出现404找不到页面

答:大部分使用为宝塔的网站管理开启了防跨站,请关闭。

不断更新中....

特别鸣谢
排名不分先后,感谢这些软件的开发者:thinkphp、小牛admin、图鸟UI、vue、mysql、redis、uniapp等!

开源版使用须知
1.允许用于个人学习、毕业设计、教学案例、公益事业、商业使用;

2.如果商用必须保留版权信息,请自觉遵守;

3.禁止将本项目的代码和资源进行任何形式的出售,产生的一切任何后果责任由侵权者自负。

版权信息
版权所有Copyright © 2016-2022 by 四川掌上时代科技有限公司旗下多客开源事业部 (http://www.51duoke.cn http://www.siyibang.com)

All rights reserved。

著作权所有者为四川掌上时代科技有限公司。

收起阅读 »

renderjs 使用记录


<template>  
    <view>  
        <view @tap="MyRenderjsModule.eventChange" style="width: 300rpx;height: 120rpx;background-color: rgb(0, 255, 0);display: flex;justify-content: center;align-content: center;align-items: center;">Click Me, Please</view>  

        <view :prop="mainTag" :change:prop="MyRenderjsModule.mainTagUpdate"></view>  
    </view>  
</template>  

<script module="MyRenderjsModule" lang="renderjs">  
    export default {  
        data() {  
            return {  
                renderTag: ''  
            }  
        },  
        mounted() {  

        },  
        methods: {  
            mainTagUpdate(newVal, oldVal, ownerInstance, instance) {  
                console.log(`renderTag will update form ${oldVal} to ${newVal} by prop mainTag`)  
                this.renderTag = newVal  

                let mainTag = ''  
                // #ifdef APP-PLUS  
                mainTag = plus.storage.getItem('mainTag')  
                // #endif  
                // #ifndef APP-PLUS  
                mainTag = uni.getStorageSync('mainTag')  
                // #endif  

                console.log(`mainTagUpdate call cache mainTag = ${mainTag}`)  
            },  
            eventChange(event, ownerInstance) {  
                console.log(`Event "eventChange" triggered`)  

                ownerInstance?.callMethod('mainCallBack', {param: 'abc'})  
            },  
        },  
    }  
</script>  

<script>  
    export default {  
        data() {  
            return {  
                mainTag: ''  
            }  
        },  
        onLoad(options) {  

            setInterval(()=>{  
                this.mainTag = (new Date()).toString()  

                // #ifdef APP-PLUS  
                plus.storage.setItem('mainTag', this.mainTag)  
                // #endif  
                // #ifndef APP-PLUS  
                uni.setStorageSync('mainTag', this.mainTag)  
                // #endif  

            }, 3000)  
        },  
        methods: {  
            mainCallBack(obj = {}){  
                console.log(`Event "mainCallBack" triggered by renderjs call "mainCallBack"`)  
            }  
        }  
    }  
</script>  

<style>  

</style>  
继续阅读 »

<template>  
    <view>  
        <view @tap="MyRenderjsModule.eventChange" style="width: 300rpx;height: 120rpx;background-color: rgb(0, 255, 0);display: flex;justify-content: center;align-content: center;align-items: center;">Click Me, Please</view>  

        <view :prop="mainTag" :change:prop="MyRenderjsModule.mainTagUpdate"></view>  
    </view>  
</template>  

<script module="MyRenderjsModule" lang="renderjs">  
    export default {  
        data() {  
            return {  
                renderTag: ''  
            }  
        },  
        mounted() {  

        },  
        methods: {  
            mainTagUpdate(newVal, oldVal, ownerInstance, instance) {  
                console.log(`renderTag will update form ${oldVal} to ${newVal} by prop mainTag`)  
                this.renderTag = newVal  

                let mainTag = ''  
                // #ifdef APP-PLUS  
                mainTag = plus.storage.getItem('mainTag')  
                // #endif  
                // #ifndef APP-PLUS  
                mainTag = uni.getStorageSync('mainTag')  
                // #endif  

                console.log(`mainTagUpdate call cache mainTag = ${mainTag}`)  
            },  
            eventChange(event, ownerInstance) {  
                console.log(`Event "eventChange" triggered`)  

                ownerInstance?.callMethod('mainCallBack', {param: 'abc'})  
            },  
        },  
    }  
</script>  

<script>  
    export default {  
        data() {  
            return {  
                mainTag: ''  
            }  
        },  
        onLoad(options) {  

            setInterval(()=>{  
                this.mainTag = (new Date()).toString()  

                // #ifdef APP-PLUS  
                plus.storage.setItem('mainTag', this.mainTag)  
                // #endif  
                // #ifndef APP-PLUS  
                uni.setStorageSync('mainTag', this.mainTag)  
                // #endif  

            }, 3000)  
        },  
        methods: {  
            mainCallBack(obj = {}){  
                console.log(`Event "mainCallBack" triggered by renderjs call "mainCallBack"`)  
            }  
        }  
    }  
</script>  

<style>  

</style>  
收起阅读 »

安卓APP模拟接口数据另类思路

以下仅仅是自己开发实践中的一点想法,如有错误,请指正:

uniapp开发中,对于h5应用,通常可以使用mocker-api和mockjs来拦截网络请求,但是APP一般都说不支持,我们开发中主要以app为主,有时候需要mock数据做演示包,思考了很久,突然发现uniapp中有一个拦截器,感觉可以用其来实现数据的mock,思路如下:

/**  
 * app mock思路  
 * 常规的拦截只支持h5,不支持app  
 * 可以使用uni自带的拦截器,利用不存在的网址快速失败从而mock结果数据  
 * 接口虽然失败,但是业务可以拿到mock数据  
 */  
let otherMockFlag = true;  
uni.addInterceptor('request', {  
      invoke(args) {  
        // request 触发前拼接 url   
        let mockUrl = '${可以复用自定义的mockjs}'  
        if(otherMockFlag && args.url === mockUrl){  
            args.url = 'http://localhost/mock' + args.url;  
            //快速失败  
            args.timeout = 1;  
            //模拟接口延迟1000毫秒  
            setTimeout(() => {  
                //可以从mockjs自定义的api里面提取数据  
                args.success({  
                    data: {  
                        code: 0,  
                        data: {  
                            a:1,  
                            b:2  
                        }  
                    }  
                })  
            },1000)  
        }  
      },  
      success(args) {  
        //console.log(JSON.stringify(args));  
      },   
      fail(err) {  
        //console.log('interceptor-fail',err)  
      },   
      complete(res) {  
        //console.log('interceptor-complete',res)  
      }  
})

打包app测试,可以mock数据,就是不知道这样有没有其它的问题

继续阅读 »

以下仅仅是自己开发实践中的一点想法,如有错误,请指正:

uniapp开发中,对于h5应用,通常可以使用mocker-api和mockjs来拦截网络请求,但是APP一般都说不支持,我们开发中主要以app为主,有时候需要mock数据做演示包,思考了很久,突然发现uniapp中有一个拦截器,感觉可以用其来实现数据的mock,思路如下:

/**  
 * app mock思路  
 * 常规的拦截只支持h5,不支持app  
 * 可以使用uni自带的拦截器,利用不存在的网址快速失败从而mock结果数据  
 * 接口虽然失败,但是业务可以拿到mock数据  
 */  
let otherMockFlag = true;  
uni.addInterceptor('request', {  
      invoke(args) {  
        // request 触发前拼接 url   
        let mockUrl = '${可以复用自定义的mockjs}'  
        if(otherMockFlag && args.url === mockUrl){  
            args.url = 'http://localhost/mock' + args.url;  
            //快速失败  
            args.timeout = 1;  
            //模拟接口延迟1000毫秒  
            setTimeout(() => {  
                //可以从mockjs自定义的api里面提取数据  
                args.success({  
                    data: {  
                        code: 0,  
                        data: {  
                            a:1,  
                            b:2  
                        }  
                    }  
                })  
            },1000)  
        }  
      },  
      success(args) {  
        //console.log(JSON.stringify(args));  
      },   
      fail(err) {  
        //console.log('interceptor-fail',err)  
      },   
      complete(res) {  
        //console.log('interceptor-complete',res)  
      }  
})

打包app测试,可以mock数据,就是不知道这样有没有其它的问题

收起阅读 »

用HBuilder运行到浏览器调试代码正常,没有报错,但是上传网站到服务器,用域名打开,操作就报错

新打开页面会报错,报错后刷新页面又正常,好奇怪!!

新打开页面会报错,报错后刷新页面又正常,好奇怪!!