提交 d05fc171 编写于 作者: DCloud_JSON's avatar DCloud_JSON

因应用市场最新要求,登陆功能去掉默认同意隐私政策。改为用户手动点击。账号登陆添加图形验证码

上级 3e0ee960
......@@ -6,7 +6,7 @@
`base app`,是一个云端一体的、集成了商用项目开发常见功能的项目模板。
如果说uniCloud admin是管理端项目的基础模板,那么base app则是用户端、尤其是移动端的基础模板。
在HBuilderX新建项目时选择base app项目模板,在这个模板基础之上快速填充自己的业务,即可很快完成一个应用。
地址:[https://gitee.com/dcloud/base-app](https://gitee.com/dcloud/base-app)
地址:[https://gitee.com/dcloud/uni-starter](https://gitee.com/dcloud/uni-starter)
#### 项目背景
我们一直想出一个结合uni-app、uniCloud和openDB以及uni-id和uniCloud admin等,uni全家桶的项目模板;
......@@ -48,7 +48,7 @@ base app将节省开发者大量的时间,让开发者集中精力在自己的
有了base app,再加上schema2code生成前端页面,一个简单应用就可以快速完成。
base app + uniCloud admin,应用开发从未如此简单快捷!
base app提供了baseapp.config.js,可指定该应用是否强制登录才能进入首页,可配置选择登录注册方式以及不同方式的优先级等。
base app提供了uni-starter.config.js,可指定该应用是否强制登录才能进入首页,可配置选择登录注册方式以及不同方式的优先级等。
```
module.exports = {
......@@ -59,7 +59,7 @@ module.exports = {
//点击悬浮下载栏后打开的网页链接
"openUrl": 'https://sj.qq.com/myapp/detail.htm?apkName=com.tencent.android.qqdownloader&info=6646FD239A6EBA9E2DEE5DFC7E18D867',
//左侧显示的应用名称
"appname": 'base-app',
"appname": 'uni-starter',
//应用的图标
"logo": './static/logo.png',
}
......@@ -87,7 +87,7 @@ module.exports = {
//关于应用
"about": {
//应用名称
"appName": "base-app",
"appName": "uni-starter",
//应用logo
"logo": "/static/logo.png",
//公司名称
......@@ -119,11 +119,11 @@ module.exports = {
### 应用配置
#### 目录结构
<pre>
base-app
uni-starter
├─uniCloud-aliyun
│ ├─cloudfunctions 云函数目录
│ | ├─common 公共模块
│ │ | ├─uni-config-center baseapp的服务端配置中心,项目所有云函数的配置在这里填写
│ │ | ├─uni-config-center uni-starter的服务端配置中心,项目所有云函数的配置在这里填写
│ │ | | ├─index.js config-center入口文件
│ │ | | └─uni-id uni-id模块配置目录
│ │ | | ├─config.json uni-id对应的配置数据
......@@ -199,7 +199,7 @@ base-app
├─uni_modules 存放[uni_module](/uni_modules)规范的插件。
├─main.js Vue初始化入口文件
├─App.vue 应用配置,用来配置App全局样式以及监听 <a href="/collocation/frame/lifecycle?id=应用生命周期">应用生命周期</a>
├─baseconfig baseapp的前端的配置文件,项目所有模块的配置在这里填写。详见该文件的代码注释。
├─baseconfig uni-starter的前端的配置文件,项目所有模块的配置在这里填写。详见该文件的代码注释。
├─manifest.json 配置应用名称、appid、logo、版本等打包信息,<a href="/collocation/manifest">详见</a>
└─pages.json 配置页面路由、导航条、选项卡等页面类信息,<a href="/collocation/pages">详见</a>
</pre>
......@@ -229,7 +229,7 @@ base-app
}
```
1. onLaunch还执行了initApp()包含以下操作
1. 读取baseapp.config并挂载到globalData的config下
1. 读取uni-starter.config并挂载到globalData的config下
2. 读取应用版本号,并存到globalData下。
3. 检查是否有可更新的应用版本。
4. 网络的变化与toast方式提醒
......
......@@ -7,7 +7,7 @@ module.exports = {
//点击悬浮下载栏后打开的网页链接
"openUrl": 'https://sj.qq.com/myapp/detail.htm?apkName=com.tencent.android.qqdownloader&info=6646FD239A6EBA9E2DEE5DFC7E18D867',
//左侧显示的应用名称
"appname": 'base-app',
"appname": 'uni-starter',
//应用的图标
"logo": './static/logo.png',
}
......@@ -35,7 +35,7 @@ module.exports = {
//关于应用
"about": {
//应用名称
"appName": "base-app",
"appName": "uni-starter",
//应用logo
"logo": "/static/logo.png",
//公司名称
......
import baseappConfig from '@/baseapp.config.js';
import uniStarterConfig from '@/uni-starter.config.js';
//应用初始化页
// #ifdef APP-PLUS
import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
import callCheckVersion from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version';
// #endif
// #ifdef APP-PLUS
import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
import callCheckVersion from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version';
// #endif
export default function() {
setTimeout(()=>{
// baseappConfig挂载到getApp().
// uniStarterConfig挂载到getApp().
const app = getApp({allowDefault: true})
app.globalData.config = baseappConfig;
app.globalData.config = uniStarterConfig;
},30)
// 初始化appVersion(仅app生效)
// 初始化appVersion(仅app生效)
initAppVersion();
/*
这里应用了[拦截器](https://uniapp.dcloud.io/api/interceptor?id=addinterceptor)实现了,路由拦截。当应用无访问摄像头/相册权限,引导跳到设置界面
......@@ -23,84 +23,84 @@ export default function() {
needLogin,
login
}
} = baseappConfig //需要登陆的页面
let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"];
list.forEach(item => {
uni.addInterceptor(item, {
invoke(e) { // 调用前拦截
//console.log(e);
const token = uni.getStorageSync('uni_id_token')
// console.log(token);
const url = e.url.split('?')[0]
//拦截强制登陆页面
if (needLogin.includes(url) && token == '') {
console.log('该页面需要登陆,即将跳转到login页面');
uni.showToast({
title: '该页面需要登陆,即将跳转到login页面',
icon: 'none'
})
uni.navigateTo({
url: "/pages/ucenter/login-page/index/index"
})
return false
}
//控制登陆优先级
if (url == '/pages/ucenter/login-page/index/index') {
//一键登录(uniVerify)、账号(username)、验证码登陆(短信smsCode)
if (login[0] == 'uniVerify') {
// console.log(e.url,url);
if (e.url == url) {
e.url += '?'
}
e.url += "univerify_first=true"
} else if (login[0] == 'username') {
e.url = "/pages/ucenter/login-page/pwd-login/pwd-login"
}
}
return true
},
fail(err) { // 失败回调拦截
console.log(err);
},
})
})
} = uniStarterConfig //需要登陆的页面
let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"];
list.forEach(item => {
uni.addInterceptor(item, {
invoke(e) { // 调用前拦截
//console.log(e);
const token = uni.getStorageSync('uni_id_token')
// console.log(token);
const url = e.url.split('?')[0]
//拦截强制登陆页面
if (needLogin.includes(url) && token == '') {
console.log('该页面需要登陆,即将跳转到login页面');
uni.showToast({
title: '该页面需要登陆,即将跳转到login页面',
icon: 'none'
})
uni.navigateTo({
url: "/pages/ucenter/login-page/index/index"
})
return false
}
//控制登陆优先级
if (url == '/pages/ucenter/login-page/index/index') {
//一键登录(uniVerify)、账号(username)、验证码登陆(短信smsCode)
if (login[0] == 'uniVerify') {
// console.log(e.url,url);
if (e.url == url) {
e.url += '?'
}
e.url += "univerify_first=true"
} else if (login[0] == 'username') {
e.url = "/pages/ucenter/login-page/pwd-login/pwd-login"
}
}
return true
},
fail(err) { // 失败回调拦截
console.log(err);
},
})
})
//当应用无访问摄像头/相册权限,引导跳到设置界面
uni.addInterceptor('chooseImage', {
fail(e) { // 失败回调拦截
console.log(e);
if (
e.errCode === 11 && uni.getSystemInfoSync().platform == "android" ||
e.errCode === 2 && uni.getSystemInfoSync().platform == "ios"
){
uni.showModal({
title:"无法访问摄像头",
content: "当前无摄像头访问权限,建议前往设置",
confirmText: "前往设置",
success(e) {
if (e.confirm) {
openAppPermissionSetting()
}
}
});
}
if(e.errCode === 12 && uni.getSystemInfoSync().platform == "android"){
uni.showModal({
title:"无法访问相册",
content: "当前无系统相册访问权限,建议前往设置",
confirmText: "前往设置",
success(e) {
if (e.confirm) {
openAppPermissionSetting()
}
}
});
}
}
})
// #ifdef APP-PLUS
//当应用无访问摄像头/相册权限,引导跳到设置界面
uni.addInterceptor('chooseImage', {
fail(e) { // 失败回调拦截
console.log(e);
if (
e.errCode === 11 && uni.getSystemInfoSync().platform == "android" ||
e.errCode === 2 && uni.getSystemInfoSync().platform == "ios"
){
uni.showModal({
title:"无法访问摄像头",
content: "当前无摄像头访问权限,建议前往设置",
confirmText: "前往设置",
success(e) {
if (e.confirm) {
openAppPermissionSetting()
}
}
});
}
if(e.errCode === 12 && uni.getSystemInfoSync().platform == "android"){
uni.showModal({
title:"无法访问相册",
content: "当前无系统相册访问权限,建议前往设置",
confirmText: "前往设置",
success(e) {
if (e.confirm) {
openAppPermissionSetting()
}
}
});
}
}
})
// #ifdef APP-PLUS
// 监听并提示设备网络状态变化
uni.onNetworkStatusChange(res=> {
console.log(res.isConnected);
......@@ -118,38 +118,38 @@ export default function() {
duration:3000
})
}
});
// #endif
}
/**
* // 初始化appVersion
*/
function initAppVersion() {
// #ifdef APP-PLUS
let appid = plus.runtime.appid;
plus.runtime.getProperty(appid, (wgtInfo) => {
let appVersion = plus.runtime;
let currentVersion = appVersion.versionCode > wgtInfo.versionCode ? appVersion : wgtInfo;
getApp({
allowDefault: true
}).appVersion = {
...currentVersion,
appid,
hasNew: false
}
// 检查更新小红点
callCheckVersion().then(res => {
console.log('检查是否有可以更新的版本', res);
if (res.result.code > 0) {
// 有新版本
getApp({
allowDefault: true
}).appVersion.hasNew = true;
}
})
});
// 检查更新
checkUpdate();
// #endif
});
// #endif
}
/**
* // 初始化appVersion
*/
function initAppVersion() {
// #ifdef APP-PLUS
let appid = plus.runtime.appid;
plus.runtime.getProperty(appid, (wgtInfo) => {
let appVersion = plus.runtime;
let currentVersion = appVersion.versionCode > wgtInfo.versionCode ? appVersion : wgtInfo;
getApp({
allowDefault: true
}).appVersion = {
...currentVersion,
appid,
hasNew: false
}
// 检查更新小红点
callCheckVersion().then(res => {
console.log('检查是否有可以更新的版本', res);
if (res.result.code > 0) {
// 有新版本
getApp({
allowDefault: true
}).appVersion.hasNew = true;
}
})
});
// 检查更新
checkUpdate();
// #endif
}
\ No newline at end of file
/*
创建在h5端全局悬浮引导用户下载app的功能,
如不需要本功能直接移除配置文件baseapp.config.js下的h5/openApp即可
如不需要本功能直接移除配置文件uni-starter.config.js下的h5/openApp即可
*/
import CONFIG from '../baseapp.config.js';
import CONFIG from '../uni-starter.config.js';
const CONFIG_OPEN = CONFIG?.h5?.openApp?.openUrl ? CONFIG.h5.openApp : {};
// 仅H5端添加"打开APP"
// 仅H5端添加"打开APP"
export default function() {
// #ifdef H5
if (!CONFIG_OPEN.openUrl) return;
let openLogo = CONFIG_OPEN.logo ?
`<img src="${CONFIG_OPEN.logo}" style="width: 2rem;height: 2rem;border-radius: 3px;">` : '';
let openApp = document.createElement("div");
// #ifdef H5
if (!CONFIG_OPEN.openUrl) return;
let openLogo = CONFIG_OPEN.logo ?
`<img src="${CONFIG_OPEN.logo}" style="width: 2rem;height: 2rem;border-radius: 3px;">` : '';
let openApp = document.createElement("div");
openApp.id = 'openApp';
openApp.style =
'position: fixed;background:#FFFFFF;box-shadow: #eeeeee 1px 1px 9px; ;top: 0;left: 0;right: 0;z-index: 999;width: 100%;height: 45px;display: flex;flex-direction: row;justify-content: space-between;align-items: center;box-sizing: border-box;padding: 0 0.5rem;'
openApp.innerHTML = `
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;">
${openLogo}
<div style="padding-left: 0.3rem;font-size: 12px;">${CONFIG_OPEN.appname || ''}</div>
</div>
<div class="openBtn" style="padding: 5px;font-size:12px;border-radius: 2px;border: 1px solid #007AFF;color: #007AFF;">下载app</div>
`;
document.body.insertBefore(openApp, document.body.firstChild);
document.body.style = 'height:calc(100% - 45px); margin-top:45px;';
openApp.addEventListener('click', e => {
var target = e.target || e.srcElement;
if (target.className.indexOf('openBtn') >= 0) {
window.location.href = CONFIG_OPEN.openUrl;
}
openApp.style =
'position: fixed;background:#FFFFFF;box-shadow: #eeeeee 1px 1px 9px; ;top: 0;left: 0;right: 0;z-index: 999;width: 100%;height: 45px;display: flex;flex-direction: row;justify-content: space-between;align-items: center;box-sizing: border-box;padding: 0 0.5rem;'
openApp.innerHTML = `
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;">
${openLogo}
<div style="padding-left: 0.3rem;font-size: 12px;">${CONFIG_OPEN.appname || ''}</div>
</div>
<div class="openBtn" style="padding: 5px;font-size:12px;border-radius: 2px;border: 1px solid #007AFF;color: #007AFF;">下载app</div>
`;
document.body.insertBefore(openApp, document.body.firstChild);
document.body.style = 'height:calc(100% - 45px); margin-top:45px;';
openApp.addEventListener('click', e => {
var target = e.target || e.srcElement;
if (target.className.indexOf('openBtn') >= 0) {
window.location.href = CONFIG_OPEN.openUrl;
}
})
//#endif
//#endif
}
<template>
<view class="root">
<text>登陆即表示同意</text>
<checkbox-group @change="setAgree" class="checkbox-group">
<checkbox style="transform: scale(0.8);" />
<text>同意</text>
</checkbox-group>
<template v-for="(agreement,index) in agreements">
<text class="agreement" @click="navigateTo(agreement)">{{agreement.title}}</text>
<text class="hint" v-if="hasAnd(agreements,index)"></text>
......@@ -27,11 +30,15 @@
},
hasAnd(agreements,index){
return agreements.length-1>index
},
setAgree(e){
this.isAgree = !this.isAgree
this.$emit('setAgree',this.isAgree)
}
},
data() {
return {
isAgree:false
};
}
}
......@@ -43,6 +50,10 @@
font-size: 28rpx;
color: #8a8f8b;
}
.checkbox-group{
display: flex;
flex-direction: row;
}
.agreement{
color:#04498c;
}
......
......@@ -5,9 +5,6 @@
<image class="logo" :src="item.logo" mode="widthFix"></image>
<text class="login-title">{{item.text}}</text>
</view>
<!-- #ifdef MP -->
<!-- #endif -->
</view>
</template>
<script>
......@@ -41,7 +38,13 @@
oauthServices: []
}
},
props: {
props: {
agree:{
type:Boolean,
default(){
return false
}
},
config: {
type: Object,
default () {
......@@ -166,7 +169,14 @@
})
}
},
login_before(type, navigateBack = true) {
login_before(type, navigateBack = true) {
if(!this.agree){
return uni.showToast({
title: '你未同意隐私政策协议',
icon: 'none'
});
}
console.log(arguments);
let oauthService = this.oauthServices.find((service) => service.id == type)
console.log(type);
......
{
"name" : "base-app",
"appid" : "",
"name" : "uni-starter",
"appid" : "__UNI__E5373F7",
"description" : "集成了商用项目开发常见功能的项目模板",
"versionName" : "1.0.0",
"versionCode" : "100",
......
{
"id": "uniCloud-base-app",
"displayName": "baseApp",
"id": "uni-starter",
"displayName": "uni-starter",
"version": "1.0.0",
"description": "集成了商用项目开发常见功能的项目模板",
"keywords": [
"baseApp",
"uni-starter",
"login",
"登录模版",
"uniCloud",
......@@ -13,7 +13,7 @@
"usearch",
"unisearch"
],
"repository": "https://gitee.com/dcloud/base-app",
"repository": "https://gitee.com/dcloud/uni-starter",
"engines": {
"HBuilderX": "^3.1.0"
},
......
{
"pages": [
{
"pages": [
{
"path": "pages/list/list",
"style": {
"navigationStyle":"custom",
"enablePullDownRefresh": true
}
},
"style": {
"navigationStyle":"custom",
"enablePullDownRefresh": true
}
},
{
"path": "pages/grid/grid",
"style": {
......@@ -61,17 +61,22 @@
"navigationStyle": "custom"
}
}, {
"path": "uni_modules/uni-feedback/pages/opendb-feedback/list",
"path": "uni_modules/opendb-feedback/pages/opendb-feedback/list",
"style": {
"navigationBarTitleText": "常见问题"
}
}, {
"path": "uni_modules/uni-feedback/pages/opendb-feedback/add",
"path": "uni_modules/opendb-feedback/pages/opendb-feedback/add",
"style": {
"navigationBarTitleText": "问题反馈"
}
}, {
"path": "uni_modules/uni-feedback/pages/opendb-feedback/detail",
"path": "uni_modules/opendb-feedback/pages/opendb-feedback/detail",
"style": {
"navigationBarTitleText": "问题反馈"
}
}, {
"path": "uni_modules/opendb-feedback/pages/opendb-feedback/edit",
"style": {
"navigationBarTitleText": "问题反馈"
}
......@@ -90,7 +95,7 @@
// #endif
}
},
},
// {
// "path": "pages/ucenter/agree-list/agree-list",
// "style": {
......@@ -108,7 +113,7 @@
// "style": {
// "navigationBarTitleText": "服务协议"
// }
// },
// },
{
"path": "uni_modules/uni-upgrade-center-app/pages/upgrade-popup",
"style": {
......@@ -189,9 +194,9 @@
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "base-app",
"navigationBarTitleText": "uni-starter",
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8",
"enablePullDownRefresh": false
},
"condition": {
......@@ -229,4 +234,4 @@
"text": "我的"
}]
}
}
}
......@@ -78,7 +78,7 @@
'userInfo': 'user/info',
'hasLogin': 'user/hasLogin'
}),
baseappConfig() {
uniStarterConfig() {
return getApp().globalData.config
}
},
......@@ -109,30 +109,50 @@
icon: 'none',
title: '出错了,新闻ID为空'
})
}
}
},
methods: {
setReadNewsLog(){
let item = {
"article_id":this.id,
"last_time":Date.now()
},
readNewsLog = uni.getStorageSync('readNewsLog')||[],
index = -1;
readNewsLog.forEach(({article_id},i)=>{
if(article_id == item.article_id){
index = i
}
})
if(index === -1){
readNewsLog.push(item)
}else{
readNewsLog.splice(index,1,item)
}
uni.setStorageSync('readNewsLog',readNewsLog)
console.log(readNewsLog);
},
setFavorite() {
if (!this.hasLogin){
return console.log('未登陆用户');
}
let article_id = this.id,
last_time = Date.now();
if (!this.hasLogin){
return console.log('未登陆用户');
}
let article_id = this.id,
last_time = Date.now();
console.log({article_id,last_time});
readNewsLog.where(`"article_id" == "${article_id}" && "user_id"==$env.uid`)
.update({last_time})
.then(({result:{updated}}) => {
console.log('updated',updated);
if (!updated) {
readNewsLog.add({article_id}).then(e=>{
console.log(e);
readNewsLog.where(`"article_id" == "${article_id}" && "user_id"==$env.uid`)
.update({last_time})
.then(({result:{updated}}) => {
console.log('updated',updated);
if (!updated) {
readNewsLog.add({article_id}).then(e=>{
console.log(e);
}).catch(err => {
console.log(err);
})
}
})
}
}).catch(err => {
console.log(err);
})
})
},
loadData(data) {
//如果上一页未传递标题过来(如搜索直达详情),则从新闻详情中读取标题
......@@ -143,7 +163,7 @@
});
}
this.setFavorite();
this.setReadNewsLog();
},
/**
* followClick
......@@ -168,7 +188,7 @@
uniShare({
content: { //公共的分享类型(type)、链接(herf)、标题(title)、summary(描述)、imageUrl(缩略图)
type: 0,
href: this.baseappConfig.h5.url + `/#/pages/list/detail?id=${_id}&title=${title}`,
href: this.uniStarterConfig.h5.url + `/#/pages/list/detail?id=${_id}&title=${title}`,
title: this.title,
summary: excerpt,
imageUrl: avatar + '?x-oss-process=image/resize,m_fill,h_100,w_100' //压缩图片解决,在ios端分享图过大导致的图片失效问题
......@@ -197,9 +217,9 @@
scene: "WXSceneSession",
type: 5,
miniProgram: {
id: this.baseappConfig.mp.weixin.id,
id: this.uniStarterConfig.mp.weixin.id,
path: `/pages/list/detail?id=${_id}&title=${title}`,
webUrl: this.baseappConfig.h5.url +
webUrl: this.uniStarterConfig.h5.url +
`/#/pages/list/detail?id=${_id}&title=${title}`,
type: 0
},
......@@ -325,4 +345,4 @@
font-size: 15px;
overflow: hidden;
}
</style>
</style>
......@@ -27,7 +27,7 @@ import uniShare from 'uni_modules/uni-share/js_sdk/uni-share.js';
// #endif
},
computed:{
baseappConfig(){
uniStarterConfig(){
console.log(getApp());
return getApp().globalData.config
}
......@@ -41,7 +41,7 @@ import uniShare from 'uni_modules/uni-share/js_sdk/uni-share.js';
},
created() {
console.log();
this.about = this.baseappConfig.about
this.about = this.uniStarterConfig.about
uni.setNavigationBarTitle({
title:'关于'+this.about.appName
})
......
<template>
<view class="content">
<!-- 顶部文字 -->
<text class="title">登陆后即可展示自己</text>
<uni-agreements></uni-agreements>
<!-- 登录框 -->
<input type="number" class="input-box" :inputBorder="false" v-model="phone" maxlength="11" placeholder="请输入手机号"/>
<button class="get-code" :class="{isPhone}" :disabled="!isPhone" :type="isPhone?'primary':'default'"
@click="sendShortMsg">获取短信验证码</button>
<text class="tip">未注册的手机号验证通过后将自动注册</text>
<!-- 顶部文字 -->
<text class="title">登陆后即可展示自己</text>
<uni-agreements @setAgree="agree = $event"></uni-agreements>
<!-- 登录框 -->
<input type="number" class="input-box" :inputBorder="false" v-model="phone" maxlength="11" placeholder="请输入手机号"/>
<button class="get-code" :class="{isPhone}" :disabled="!isPhone" :type="isPhone?'primary':'default'"
@click="sendShortMsg">获取短信验证码</button>
<text class="tip">未注册的手机号验证通过后将自动注册</text>
<!-- 登录按钮弹窗 -->
<uni-quick-login ref="uniQuickLogin"></uni-quick-login>
<uni-quick-login :agree="agree" ref="uniQuickLogin"></uni-quick-login>
</view>
</template>
......@@ -17,16 +17,17 @@
var univerify_first,currentWebview;//是否一键登陆优先
export default {
data() {
return {
phone:""
return {
phone:"",
agree:false
}
},
computed: {
isPhone(){
return /^1\d{10}$/.test(this.phone);
}
},
onLoad(e) {
computed: {
isPhone(){
return /^1\d{10}$/.test(this.phone);
}
},
onLoad(e) {
//是否优先启动一键登陆。即:页面一加载就启动一键登录
univerify_first = e.univerify_first
//#ifdef APP-PLUS
......@@ -61,13 +62,19 @@ var univerify_first,currentWebview;//是否一键登陆优先
//#endif
},
methods: {
sendShortMsg() {
sendShortMsg() {
if(!this.agree){
return uni.showToast({
title: '你未同意隐私政策协议',
icon: 'none'
});
}
// 发送验证吗
uni.showLoading();
uni.navigateTo({
url: '/pages/ucenter/login-page/phone-code/phone-code?phoneNumber=' + this.phone,
complete: () => {
uni.hideLoading();
complete: () => {
uni.hideLoading();
}
});
},
......@@ -81,6 +88,6 @@ var univerify_first,currentWebview;//是否一键登陆优先
}
</script>
<style lang="scss">
<style lang="scss">
@import url("../common/login-page.css");
</style>
<template>
<view class="content">
<!-- 顶部文字 -->
<text class="title">用户名密码登录</text>
<uni-agreements></uni-agreements>
<input type="number" class="input-box" :inputBorder="false" v-model="username" placeholder="请输入手机号/用户名"></input>
<input type="password" class="input-box" :inputBorder="false" v-model="password" placeholder="请输入密码"></input>
<button class="send-btn" :disabled="!canLogin" :type="canLogin?'primary':'default'" @click="pwdLogin">登录</button>
<!-- 忘记密码 -->
<view class="auth-box">
<text class="link" @click="toRetrievePwd">忘记密码</text>
<text class="link" @click="toRegister">注册账号</text>
<template>
<view class="content">
<!-- 顶部文字 -->
<text class="title">用户名密码登录</text>
<uni-agreements @setAgree="agree = $event"></uni-agreements>
<input type="number" class="input-box" :inputBorder="false" v-model="username" placeholder="请输入手机号/用户名"></input>
<input type="password" class="input-box" :inputBorder="false" v-model="password" placeholder="请输入密码"></input>
<view class="captcha-box" v-if="captchaBase64">
<image class="captcha-img" @click="createCaptcha" :src="captchaBase64" mode="widthFix"></image>
<input type="text" class="input-box captcha" :inputBorder="false" v-model="captcha" placeholder="请输入验证码"></input>
</view>
<uni-quick-login ref="uniQuickLogin"></uni-quick-login>
<button class="send-btn" :disabled="!canLogin" :type="canLogin?'primary':'default'"
@click="pwdLogin">登录</button>
<!-- 忘记密码 -->
<view class="auth-box">
<text class="link" @click="toRetrievePwd">忘记密码</text>
<text class="link" @click="toRegister">注册账号</text>
</view>
<uni-quick-login :agree="agree" ref="uniQuickLogin"></uni-quick-login>
</view>
</template>
<script>
import mixin from '../common/login-page.mixin.js';
export default {
mixins:[mixin],
mixins: [mixin],
data() {
return {
"password":"",
"username":""
return {
"password": "",
"username": "",
"agree": false,
"captchaBase64":"",
"captcha":""
}
},
computed: {
canLogin() {
return this.username.length && this.isPwd;
},
isPwd(){
return /^.{6,20}$/.test(this.password);
},
isPhone(){
return /^1\d{10}$/.test(this.phone);
return this.username.length && this.isPwd;
},
isPwd() {
return /^.{6,20}$/.test(this.password);
},
isPhone() {
return /^1\d{10}$/.test(this.phone);
},
},
methods: {
// 页面跳转,找回密码
toRetrievePwd() {
uni.navigateTo({
url: '../pwd-retrieve/pwd-retrieve?phoneNumber=' + (this.isPhone?this.username:'') + '&phoneArea=' + this.currenPhoneArea
url: '../pwd-retrieve/pwd-retrieve?phoneNumber=' + (this.isPhone ? this.username : '') +
'&phoneArea=' + this.currenPhoneArea
})
},
/**
* 密码登录
*/
pwdLogin() {
// 下边是可以登录
this.request('user-center/login',
{
"username":this.username,
"password":this.password
},result=>{
console.log(result);
if(result.code === 0){
this.loginSuccess(result)
if (!this.agree) {
return uni.showToast({
title: '你未同意隐私政策协议',
icon: 'none'
});
}
// 下边是可以登录
this.request('user-center/login', {
"username": this.username,
"password": this.password,
"captcha":this.captcha
}, result => {
console.log(result);
if (result.code === 0) {
this.loginSuccess(result)
} else {
if (result.needCaptcha) {
uni.showToast({
title: result.msg,
icon: 'none'
});
this.createCaptcha()
}else{
uni.showModal({
title: '错误',
......@@ -63,15 +85,26 @@
showCancel: false,
confirmText: '知道了'
});
}
}
}
})
},
createCaptcha(){
this.request(
'user-center/createCaptcha', {
scene: "login"
},
result => {
if (result.code === 0) {
this.captchaBase64 = result.captchaBase64
}
)
})
},
/* 前往注册 */
toRegister(e){
toRegister(e) {
console.log(e);
uni.navigateTo({
url:'/pages/ucenter/login-page/register/register'
url: '/pages/ucenter/login-page/register/register'
})
}
}
......@@ -80,19 +113,35 @@
<style>
@import url("../common/login-page.css");
.auth-box {
flex-direction: row;
.auth-box {
flex-direction: row;
justify-content: space-between;
margin-top: 20px;
}
.auth-box .link{
font-size: 26rpx;
}
margin-top: 20px;
}
.auth-box .link {
font-size: 26rpx;
}
.login-text-sub {
color: #8a8f8b;
}
.toRegister{
.toRegister {
margin-top: 80px;
width: 600rpx;
}
.captcha-box{
flex-direction: row;
align-items: center;
justify-content: flex-end;
}
.captcha-img{
margin:15px 15px 0 0;
width: 250rpx;
}
.captcha{
width: 350rpx;
}
</style>
</style>
<template>
<view class="container">
<unicloud-db ref="udb" v-slot:default="{data, pagination, loading, hasMore, error}" where="user_id == $env.uid"
orderby="last_time desc" collection="read-news-log,opendb-news-articles" @load="isLoading == false" @error="isLoading == false"
field="article_id.title,article_id._id,last_time" :page-size="10">
<view class="container">
<unicloud-db ref="udb" v-slot:default="{data, pagination, loading, hasMore, error}" :where="udbWhere"
collection="opendb-news-articles" @load="isLoading == false" @error="isLoading == false"
field="title,_id" :page-size="10">
<view v-if="data && data.length">
<uni-list>
<uni-list-item v-for="(item, index) in data" :key="index" :clickable="true"
@click="handleItemClick(item)">
<view slot="body">
<text>{{item.article_id[0].title}}</text>
<uni-dateformat class="article-date" :date="item.last_time" format="yyyy-MM-dd hh:mm"
<text>{{item.title}}</text>
<uni-dateformat class="article-date" :date="readNewsLog[index].last_time" format="yyyy-MM-dd hh:mm"
:threshold="[0, 0]" />
</view>
</uni-list-item>
......@@ -32,8 +32,16 @@
contentdown: '',
contentrefresh: '',
contentnomore: '',
}
},
readNewsLog:[],
udbWhere:''
}
},
onLoad() {
this.readNewsLog = uni.getStorageSync('readNewsLog')||[];
let readNewsLogIds = this.readNewsLog.map(({article_id})=>article_id)
console.log(typeof readNewsLogIds,readNewsLogIds);
this.udbWhere = `"_id" in ${JSON.stringify(readNewsLogIds)}`
},
onPullDownRefresh() {
this.refreshData();
......@@ -52,7 +60,7 @@
},
handleItemClick(item) {
uni.navigateTo({
url: '/pages/list/detail?id=' + item.article_id[0]._id + '&title=' + item.article_id[0].title
url: '/pages/list/detail?id=' + item._id + '&title=' + item.title
})
}
}
......
......@@ -4,18 +4,21 @@
<image class="logo-img" :src="userInfo.avatar||avatarUrl"></image>
<view class="logo-title">
<text class="uer-name">{{userInfo.nickname||userInfo.username||userInfo.mobile||'未登录'}}</text>
<uni-icons class="icon" color="#FFFFFF" type="arrowright" v-if="!login" size="16"></uni-icons>
</view>
</view>
<uni-grid class="grid" :column="5" :showBorder="false" :square="true">
<uni-grid-item class="item" v-for="(item,index) in gridList" @click.native="tapGrid(index)" :key="index">
<uni-icons class="icon" color="#5d5e64" :type="item.icon" size="28"></uni-icons>
<uni-icons class="icon" color="#ffd44f" :type="item.icon" size="26"></uni-icons>
<text class="text">{{item.text}}</text>
</uni-grid-item>
</uni-grid>
<uni-list class="center-list" v-for="(sublist , index) in ucenterList" :key="index">
<uni-list-item v-for="(item,i) in sublist" :title="item.title" link :rightText="item.rightText" :key="i"
:clickable="true" :to="item.to" @click="ucenterListClick(item)">
:clickable="true" :to="item.to"
@click="ucenterListClick(item)"
:show-extra-icon="true"
:extraIcon="{type:item.icon,color:'#999'}"
>
<view v-if="item.showBadge" class="item-footer" slot="footer">
<text class="item-footer-text">{{item.rightText}}</text>
<view class="item-footer-badge"></view>
......@@ -38,7 +41,7 @@
export default {
data() {
return {
avatarUrl: '/static/uni-center/logo.png',
avatarUrl: '/static/uni-center/defaultAvatarUrl.png',
gridList: [{
"text": "文字1",
"icon": "chat"
......@@ -64,30 +67,36 @@
[
// #ifdef APP-PLUS
{
title: '去评分',
event: 'gotoMarket'
"title": '去评分',
"event": 'gotoMarket',
"icon":"hand-thumbsup"
},
//#endif
{
title: '阅读过的文章',
to: '/pages/ucenter/read-news-log/read-news-log',
},
"title": '阅读过的文章',
"to": '/pages/ucenter/read-news-log/read-news-log',
"icon":"flag"
},
{
title: '我的积分',
to: '',
event: 'getScore'
"title": '我的积分',
"to": '',
"event": 'getScore',
"icon":"paperplane"
}
],
[{
title: '问题与反馈',
to: '/uni_modules/uni-feedback/pages/opendb-feedback/list' // /pages/ucenter/uni-feedback/uni-feedback uni_modules/uni-feedback/pages/opendb-feedback/list
"title": '问题与反馈',
"to": '/uni_modules/opendb-feedback/pages/opendb-feedback/add',
"icon":"help"
}, {
title: '设置',
to: '/pages/ucenter/settings/settings'
"title": '设置',
"to": '/pages/ucenter/settings/settings',
"icon":"settings"
}],
[{
title: '关于',
to: '/pages/ucenter/about/about'
"title": '关于',
"to": '/pages/ucenter/about/about',
"icon":"info"
}]
]
}
......@@ -97,7 +106,8 @@
this.ucenterList[this.ucenterList.length - 2].unshift({
title: '检查更新',
rightText: this.appVersion.version + '-' + this.appVersion.versionCode,
event: 'checkVersion',
event: 'checkVersion',
icon:'loop',
showBadge: this.appVersion.hasNew
})
//#endif
......@@ -223,7 +233,7 @@
width: 750rpx;
padding: 20rpx;
padding-top: 50px;
background-color: #2F85FC;
background-image: url(../../static/uni-center/headers.png);
flex-direction: column;
align-items: center;
}
......@@ -232,11 +242,9 @@
width: 150rpx;
height: 150rpx;
border-radius: 150rpx;
border: solid 1px #FFFFFF;
}
.logo-title {
height: 150rpx;
flex: 1;
align-items: center;
justify-content: space-between;
......@@ -244,8 +252,8 @@
}
.uer-name {
height: 60rpx;
line-height: 60rpx;
height: 100rpx;
line-height: 100rpx;
font-size: 38rpx;
color: #FFFFFF;
}
......@@ -263,11 +271,14 @@
.grid {
background-color: #FFFFFF;
margin: 25rpx 0;
margin-bottom: 15rpx;
padding: 10rpx 0;
}
.uni-grid .text {
font-size: 26rpx;
font-size: 30rpx;
height: 30px;
line-height: 30px;
color: #817f82;
}
......@@ -309,4 +320,4 @@
/* #endif */
background-color: #DD524D;
}
</style>
</style>
......@@ -159,9 +159,9 @@ exports.main = async (event, context) => {
case 'login':
let passed = false;
let needCaptcha = await getNeedCaptcha();
console.log('needCaptcha',needCaptcha);
if (needCaptcha) {
res = await uniCaptcha.verify(params)
res = await uniCaptcha.verify({...params,scene:'login'})
if (res.code === 0) passed = true;
}
......
......@@ -23,7 +23,7 @@
"is_silently": false,
"is_mandatory": false,
"appid": "__UNI__03B096E",
"name": "base-app",
"name": "uni-starter",
"title": "新增升级中心",
"contents": "新增升级中心",
"platform": [
......@@ -67,26 +67,26 @@
"sort": 1,
"category_id": "",
"description": ""
},
{
"status": true,
"bannerfile": {
"name": "094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg",
"extname": "jpg",
"fileType": "image",
"url": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg",
"size": 70880,
"image": {
"width": 500,
"height": 333,
"location": "blob:http://localhost:8081/1a6f718a-4012-476a-9172-590fef2cc518"
},
"path": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg"
},
"open_url": "https://www.dcloud.io/",
"title": "",
"category_id": "",
"description": ""
},
{
"status": true,
"bannerfile": {
"name": "094a9dc0-50c0-11eb-b680-7980c8a877b8.jpg",
"extname": "jpg",
"fileType": "image",
"url": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg",
"size": 70880,
"image": {
"width": 500,
"height": 333,
"location": "blob:http://localhost:8081/1a6f718a-4012-476a-9172-590fef2cc518"
},
"path": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-76ce2c5e-31c7-4d81-8fcf-ed1541ecbc6e/9db94cb4-a5e0-4ed9-b356-b42a392b3112.jpg"
},
"open_url": "https://www.dcloud.io/",
"title": "",
"category_id": "",
"description": ""
}]
}
}
\ No newline at end of file
......@@ -33,8 +33,8 @@
</view>
</template>
<script>
import {about} from '@/baseapp.config.js';
<script>
import {about} from '@/uni-starter.config.js';
export default {
data() {
return {
......
<template>
<view class="feedback-body feedback-uploader">
<view class="uni-uploader">
<view class="uni-uploader-head" v-if="isChoose">
<view class="uni-uploader-title">点击预览图片</view>
<view class="uni-uploader-info">{{ imgs.length }}/5</view>
</view>
<view class="uni-uploader-body">
<view class="uni-uploader__files">
<block v-for="(image, index) in imgs" :key="index">
<view class="uni-uploader__file" style="position: relative;">
<image class="uni-uploader__img" :src="image" @tap="previewImage(index)">
</image>
<view class="close-view" v-if="isChoose" @click="close(index)">x</view>
</view>
</block>
<view class="uni-uploader__input-box" v-show="isChoose && imgs.length < 5">
<view class="uni-uploader__input" @tap="chooseImg"></view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name:"feedback-imgs",
props:{
imgs:{
type:Array,
default:()=>[]
},
isChoose:{
type:Boolean,
default:false
},
},
methods:{
/**
* 关闭图片
* @param {Object} e
*/
close(e) {
this.$emit( 'close', e)
},
/**
* 选择图片
*/
chooseImg() {
this.$emit('chooseImg');
},
/**
* 预览图片
* @param {Object} index
*/
previewImage(index) {
uni.previewImage({
urls: this.imgs,
current: this.imgs[index]
});
},
}
}
</script>
<style>
.close-view {
text-align: center;
line-height: 14px;
height: 16px;
width: 16px;
border-radius: 50%;
background: #ff5053;
color: #ffffff;
position: absolute;
top: -6px;
right: -4px;
font-size: 12px;
}
/* 上传 */
.uni-uploader {
flex: 1;
flex-direction: column;
}
.uni-uploader-head {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.uni-uploader-info {
color: #B2B2B2;
}
.uni-uploader-body {
margin-top: 16rpx;
}
.uni-uploader__files {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.uni-uploader__file {
margin: 10rpx;
width: 210rpx;
height: 210rpx;
}
.uni-uploader__img {
display: block;
width: 210rpx;
height: 210rpx;
}
.uni-uploader__input-box {
position: relative;
margin: 10rpx;
width: 208rpx;
height: 208rpx;
border: 2rpx solid #D9D9D9;
}
.uni-uploader__input-box:before,
.uni-uploader__input-box:after {
content: " ";
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
background-color: #D9D9D9;
}
.uni-uploader__input-box:before {
width: 4rpx;
height: 79rpx;
}
.uni-uploader__input-box:after {
width: 79rpx;
height: 4rpx;
}
.uni-uploader__input-box:active {
border-color: #999999;
}
.uni-uploader__input-box:active:before,
.uni-uploader__input-box:active:after {
background-color: #999999;
}
.uni-uploader__input {
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
/*问题反馈*/
.feedback-title {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 20rpx;
color: #8f8f94;
font-size: 28rpx;
}
.feedback-star-view.feedback-title {
justify-content: flex-start;
margin: 0;
}
.feedback-quick {
position: relative;
padding-right: 40rpx;
}
.feedback-quick:after {
font-family: uniicons;
font-size: 40rpx;
content: '\e581';
position: absolute;
right: 0;
top: 50%;
color: #bbb;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.feedback-body {
background: #fff;
}
.feedback-textare {
height: 200rpx;
font-size: 34rpx;
line-height: 50rpx;
width: 100%;
box-sizing: border-box;
padding: 20rpx 30rpx 0;
}
.feedback-input {
font-size: 34rpx;
height: 50rpx;
min-height: 50rpx;
padding: 15rpx 20rpx;
line-height: 50rpx;
}
.feedback-uploader {
padding: 22rpx 20rpx;
padding-left: 0;
}
.feedback-star {
font-family: uniicons;
font-size: 40rpx;
margin-left: 6rpx;
}
.feedback-star-view {
margin-left: 20rpx;
}
.feedback-star:after {
content: '\e408';
}
.feedback-star.active {
color: #FFB400;
}
.feedback-star.active:after {
content: '\e438';
}
.feedback-submit {
background: #007AFF;
color: #FFFFFF;
margin: 20rpx;
}
.uni-uploader-title{
text-align: right;
flex: 1;
color: #B2B2B2;
padding-right: 10rpx;
}
</style>
// 表单校验规则由 schema2code 生成,不建议直接修改校验规则,而建议通过 schema2code 生成, 详情: https://uniapp.dcloud.net.cn/uniCloud/schema
const validator = {
"user_id": {
"rules": [
{
"format": "string"
}
]
},
"create_date": {
"rules": [
{
"format": "timestamp"
}
]
},
"content": {
"rules": [
{
"format": "string"
}
]
},
"imgs": {
"rules": [
{
"format": "array"
}
]
},
"is_reply": {
"rules": [
{
"format": "bool"
}
]
},
"feedback_id": {
"rules": [
{
"format": "string"
}
]
},
"contact": {
"rules": [
{
"format": "string"
}
]
},
"mobile": {
"rules": [
{
"format": "string"
}
]
},
"reply_count": {
"rules": [
{
"format": "int"
}
]
}
}
const enumConverter = {}
export { validator, enumConverter }
{
"id": "opendb-feedback",
"displayName": "opendb-feedback",
"version": "1.0.0",
"description": "",
"keywords": [
"opendb-feedback"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"uniCloud",
"Admin插件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uni-dateformat",
"uni-badge",
"uni-icons",
"uni-link",
"uni-load-more",
"uni-forms",
"uni-group",
"uni-list",
"uni-fab",
"uni-datetime-picker",
"uni-file-picker",
"uni-easyinput",
"uni-data-checkbox",
"switch"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "u",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}
\ No newline at end of file
<template>
<view class="uni-container">
<uni-forms ref="form" :value="formData" :rules="rules" validate-trigger="submit" err-show-type="toast">
<uni-forms-item name="content" label="反馈内容" required>
<uni-easyinput placeholder="请输入反馈内容" type="textarea" v-model="formData.content" trim="right" />
</uni-forms-item>
<uni-forms-item name="imgs" label="反馈截图">
<feedback-imgs :isChoose="true" :imgs="formData.imgs" @close="close" @chooseImg="chooseImg"></feedback-imgs>
</uni-forms-item>
<uni-forms-item name="contact" label="联系人" required>
<uni-easyinput placeholder="请输入联系人" v-model="formData.contact" trim="both" />
</uni-forms-item>
<uni-forms-item name="mobile" label="联系方式" required>
<uni-easyinput placeholder="请输入手机号/邮箱" v-model="formData.mobile" trim="both" />
</uni-forms-item>
<view class="uni-button-group">
<button type="primary" class="uni-button" @click="submit">提交</button>
</view>
</uni-forms>
</view>
</template>
<script>
const db = uniCloud.database();
const dbCollectionName = 'opendb-feedback';
import feedbackImgs from '../../components/feedback-imgs';
export default {
components:{feedbackImgs},
data() {
return {
formData: {
"user_id": (uni.getStorageSync('userInfo') || {
_id: ''
})._id,
"create_date": null,
"content": "",
"imgs": [],
"is_reply": null,
"feedback_id": "",
"contact": "",
"mobile": "",
"reply_count": null
},
formOptions: {},
rules: {
content: {
rules: [{
required: true,
errorMessage: '请填写反馈内容',
}]
},
contact:{
rules:[{
required: true,
errorMessage: '请填写联系人',
}]
},
mobile:{
rules:[{
required: true,
errorMessage: '请填写联系方式',
}]
}
}
}
},
onReady() {
this.$refs.form.setRules(this.rules)
},
methods: {
/**
* 触发表单提交
*/
async submit() {
uni.showLoading({
mask: true
})
this.$refs.form.submit().then((res) => {
this.uploadImgs(res.imgs)
.then(imgs=>{
res.imgs = imgs;
res.create_date = Date.now();
res.is_reply = true;
this.submitForm(res)
});
}).catch((errors) => {
console.log(errors);
uni.hideLoading()
})
},
/**
* 上传图片
*/
async uploadImgs(imgs){
let cloud_list = [];
for (let i = 0; i < imgs.length; i++) {
let img = await uniCloud.uploadFile({
filePath: imgs[i],
cloudPath: 'feedback.jpg'
});
cloud_list.push(img.fileID);
}
return cloud_list;
},
submitForm(value) {
// 使用 clientDB 提交数据
db.collection(dbCollectionName).add(value).then((res) => {
uni.showModal({
title: '您的反馈已经提交成功,请耐心等待。',
showCancel: false,
success: res => {
uni.navigateBack()
},
});
}).catch((err) => {
uni.showModal({
content: err.message || '反馈失败',
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
},
/**
* 关闭图片
* @param {Object} e
*/
close(e) {
this.formData.imgs.splice(e, 1);
},
/**
* 选择图片
*/
chooseImg() {
//选择图片
uni.chooseImage({
sourceType: ['camera', 'album'],
sizeType: 'compressed',
count: 5 - this.formData.imgs.length,
success: res => {
this.formData.imgs = this.formData.imgs.concat(res.tempFilePaths);
}
});
},
}
}
</script>
<style>
.uni-container {
padding: 15px;
}
.uni-input-border,
.uni-textarea-border {
width: 100%;
font-size: 14px;
color: #666;
border: 1px #e5e5e5 solid;
border-radius: 5px;
box-sizing: border-box;
}
.uni-input-border {
padding: 0 10px;
height: 35px;
}
.uni-textarea-border {
padding: 10px;
height: 80px;
}
.uni-button-group {
margin-top: 50px;
display: flex;
justify-content: center;
}
.uni-button {
width: 184px;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
line-height: 1;
margin: 0;
}
.uni-button-group {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
</style>
<template>
<view class="container">
<unicloud-db ref="udb" v-slot:default="{data, loading, error, options}" :options="options"
collection="opendb-feedback"
field="user_id,create_date,content,imgs,is_reply,feedback_id,contact,mobile,reply_count" :where="queryWhere"
:getone="true" :manual="true">
<view v-if="error">{{error.message}}</view>
<view v-else-if="loading">
<uni-load-more :contentText="loadMore" status="loading"></uni-load-more>
</view>
<view v-else-if="data">
<uni-forms-item name="content" label="反馈内容">
<uni-easyinput :disabled="true" placeholder="请输入反馈内容" type="textarea" v-model="data.content" trim="right" />
</uni-forms-item>
<uni-forms-item v-if="data.imgs.length>0" name="imgs" label="反馈截图">
<feedback-imgs :imgs="data.imgs"></feedback-imgs>
</uni-forms-item>
<uni-forms-item name="contact" label="联系人">
<uni-easyinput :disabled="true" placeholder="请输入联系人" v-model="data.contact" trim="both" />
</uni-forms-item>
<uni-forms-item name="mobile" label="联系方式">
<uni-easyinput :disabled="true" placeholder="请输入手机号/邮箱" v-model="data.mobile" trim="both" />
</uni-forms-item>
</view>
</unicloud-db>
</view>
</template>
<script>
// 由schema2code生成,包含校验规则和enum静态数据
import {
enumConverter
} from '../../js_sdk/validator/opendb-feedback.js';
import feedbackImgs from '../../components/feedback-imgs';
export default {
components:{feedbackImgs},
data() {
return {
queryWhere: '',
loadMore: {
contentdown: '',
contentrefresh: '',
contentnomore: ''
},
options: {
// 将scheme enum 属性静态数据中的value转成text
...enumConverter
}
}
},
onLoad(e) {
this._id = e.id
},
onReady() {
if (this._id) {
this.queryWhere = '_id=="' + this._id + '"'
}
}
}
</script>
<style>
.container {
padding: 10px;
}
.btns {
margin-top: 10px;
display: flex;
flex-direction: row;
}
.btns button {
flex: 1;
}
.btn-delete {
margin-left: 10px;
}
</style>
<template>
<view class="uni-container">
<uni-forms ref="form" :value="formData" validate-trigger="submit" err-show-type="toast">
<uni-forms-item name="user_id" label="">
<uni-easyinput placeholder="留言反馈用户ID/回复留言用户ID,参考uni-id-users表" v-model="formData.user_id" />
</uni-forms-item>
<uni-forms-item name="create_date" label="">
<uni-datetime-picker return-type="timestamp" :value="formData.create_date" />
</uni-forms-item>
<uni-forms-item name="content" label="">
<uni-easyinput placeholder="留言内容/回复内容" v-model="formData.content" trim="right" />
</uni-forms-item>
<uni-forms-item name="imgs" label="">
<uni-data-checkbox :multiple="true" v-model="formData.imgs" />
</uni-forms-item>
<uni-forms-item name="is_reply" label="">
<switch @change="binddata('is_reply', $event.detail.value)" :checked="formData.is_reply" />
</uni-forms-item>
<uni-forms-item name="feedback_id" label="">
<uni-easyinput placeholder="被回复留言ID" v-model="formData.feedback_id" />
</uni-forms-item>
<uni-forms-item name="contact" label="">
<uni-easyinput placeholder="联系人" v-model="formData.contact" trim="both" />
</uni-forms-item>
<uni-forms-item name="mobile" label="">
<uni-easyinput placeholder="联系电话" v-model="formData.mobile" trim="both" />
</uni-forms-item>
<uni-forms-item name="reply_count" label="">
<uni-easyinput placeholder="被回复条数" type="number" v-model="formData.reply_count" />
</uni-forms-item>
<view class="uni-button-group">
<button type="primary" class="uni-button" @click="submit">提交</button>
</view>
</uni-forms>
</view>
</template>
<script>
import { validator } from '../../js_sdk/validator/opendb-feedback.js';
const db = uniCloud.database();
const dbCollectionName = 'opendb-feedback';
function getValidator(fields) {
let reuslt = {}
for (let key in validator) {
if (fields.indexOf(key) > -1) {
reuslt[key] = validator[key]
}
}
return reuslt
}
export default {
data() {
return {
formData: {
"user_id": "",
"create_date": null,
"content": "",
"imgs": [],
"is_reply": null,
"feedback_id": "",
"contact": "",
"mobile": "",
"reply_count": null
},
formOptions: {},
rules: {
...getValidator(["user_id","create_date","content","imgs","is_reply","feedback_id","contact","mobile","reply_count"])
}
}
},
onLoad(e) {
const id = e.id
this.formDataId = id
this.getDetail(id)
},
onReady() {
this.$refs.form.setRules(this.rules)
},
methods: {
/**
* 触发表单提交
*/
submit() {
uni.showLoading({
mask: true
})
this.$refs.form.submit().then((res) => {
this.submitForm(res)
}).catch((errors) => {
uni.hideLoading()
})
},
submitForm(value) {
// 使用 clientDB 提交数据
db.collection(dbCollectionName).doc(this.formDataId).update(value).then((res) => {
uni.showToast({
icon: 'none',
title: '修改成功'
})
this.getOpenerEventChannel().emit('refreshData')
setTimeout(() => uni.navigateBack(), 500)
}).catch((err) => {
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
},
/**
* 获取表单数据
* @param {Object} id
*/
getDetail(id) {
uni.showLoading({
mask: true
})
db.collection(dbCollectionName).doc(id).field('user_id,create_date,content,imgs,is_reply,feedback_id,contact,mobile,reply_count').get().then((res) => {
const data = res.result.data[0]
if (data) {
this.formData = data
}
}).catch((err) => {
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false
})
}).finally(() => {
uni.hideLoading()
})
}
}
}
</script>
<style>
.uni-container {
padding: 15px;
}
.uni-input-border,
.uni-textarea-border {
width: 100%;
font-size: 14px;
color: #666;
border: 1px #e5e5e5 solid;
border-radius: 5px;
box-sizing: border-box;
}
.uni-input-border {
padding: 0 10px;
height: 35px;
}
.uni-textarea-border {
padding: 10px;
height: 80px;
}
.uni-button-group {
margin-top: 50px;
display: flex;
justify-content: center;
}
.uni-button {
width: 184px;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
line-height: 1;
margin: 0;
}
</style>
<template>
<view class="container">
<unicloud-db ref="udb" v-slot:default="{data, pagination, loading, hasMore, error}" collection="opendb-feedback"
field="content, title" where="is_reply == false" @load="isLoading == false" @error="isLoading == false">
<view v-if="data && data.length">
<uni-collapse :accordion="true">
<uni-collapse-item v-for="(item, index) in data" :key="index" :title="item.title" :show-animation="true">
<text class="content">{{ item.content }}</text>
</uni-collapse-item>
</uni-collapse>
</view>
</unicloud-db>
<uni-fab ref="fab" horizontal="right" vertical="bottom" :pop-menu="false" @fabClick="fabClick" />
</view>
</template>
<script>
export default {
data() {
return {
isLoading:true,
}
},
methods: {
refreshData() {
this.$refs.udb.loadData({
clear: true
}, (res) => {
console.log(res);
})
},
fabClick() {
// 打开新增页面
uni.navigateTo({
url: './add',
events: {
// 监听新增数据成功后, 刷新当前页面数据
refreshData: () => {
this.$refs.udb.loadData({
clear: true
})
}
}
})
}
}
}
</script>
<style>
.content{
padding: 20rpx;
}
</style>
# opendb-feedback
\ No newline at end of file
{
"bsonType": "object",
"permission": {
"read": true, //"doc.is_reply == false",
"create": true,
"update": false,
"delete": false
},
"properties": {
"_id": {
"description": "ID,系统自动生成"
},
"user_id": {
"bsonType": "string",
"description": "留言反馈用户ID\/回复留言用户ID,参考uni-id-users表",
"foreignKey": "uni-id-users._id",
"forceDefaultValue": {
"$env": "uid"
}
},
"title": {
"description": "反馈标题(用于列表展示)"
},
"ip": {
"forceDefaultValue": {
"$env": "clientIP"
}
},
"create_date": {
"bsonType": "timestamp",
"description": "留言时间\/回复留言时间",
"forceDefaultValue": {
"$env": "now"
}
},
"content": {
"bsonType": "string",
"description": "留言内容\/回复内容",
"trim": "right"
},
"imgs": {
"bsonType": "array",
"description": "图片列表"
},
"is_reply": {
"bsonType": "bool",
"description": "是否是回复类型"
},
"feedback_id": {
"bsonType": "string",
"description": "被回复留言ID"
},
"contact": {
"bsonType": "string",
"description": "联系人",
"trim": "both"
},
"mobile": {
"bsonType": "string",
"description": "联系电话",
"trim": "both"
},
"reply_count": {
"bsonType": "int",
"description": "被回复条数"
}
}
}
#### 本功能基于[底部图标菜单](https://ext.dcloud.net.cn/plugin?id=4858)封装而成。
### 示例代码
```
uniShare({
content: { //公共的分享参数配置 类型(type)、链接(herf)、标题(title)、summary(描述)、imageUrl(缩略图)
type: 0,
href: 'https://uniapp.dcloud.io/',
title: '标题',
summary: '描述',
imageUrl: 'https://img-cdn-aliyun.dcloud.net.cn/stream/icon/__UNI__HelloUniApp.png'
},
menus: [{
"img": "/static/app-plus/sharemenu/wechatfriend.png",
"text": "微信好友",
"share": { //当前项的分享参数配置。可覆盖公共的配置如下:分享到微信小程序,配置了type=5
"provider": "weixin",
"scene": "WXSceneSession"
}
},
{
"img": "/static/app-plus/sharemenu/wechatmoments.png",
"text": "微信朋友圈",
"share": {
"provider": "weixin",
"scene": "WXSenceTimeline"
}
},
{
"img": "/static/app-plus/sharemenu/mp_weixin.png",
"text": "微信小程序",
"share": {
provider: "weixin",
scene: "WXSceneSession",
type: 5,
miniProgram: {
id: baseappConfig.mp.weixin.id,
path: `/pages/list/detail?id=${_id}&title=${title}`,
webUrl: baseappConfig.h5.url +
`/#/pages/list/detail?id=${_id}&title=${title}`,
type: 0
},
}
},
{
"img": "/static/app-plus/sharemenu/weibo.png",
"text": "微博",
"share": {
"provider": "sinaweibo"
}
},
{
"img": "/static/app-plus/sharemenu/qq.png",
"text": "QQ",
"share": {
"provider": "qq"
}
},
{
"img": "/static/app-plus/sharemenu/copyurl.png",
"text": "复制",
"share": "copyurl"
},
{
"img": "/static/app-plus/sharemenu/more.png",
"text": "更多",
"share": "shareSystem"
}
],
cancelText: "取消分享",
}, e => { //callback
console.log(e);
uniShare({
content: { //公共的分享参数配置 类型(type)、链接(herf)、标题(title)、summary(描述)、imageUrl(缩略图)
type: 0,
href: 'https://uniapp.dcloud.io/',
title: '标题',
summary: '描述',
imageUrl: 'https://img-cdn-aliyun.dcloud.net.cn/stream/icon/__UNI__HelloUniApp.png'
},
menus: [{
"img": "/static/app-plus/sharemenu/wechatfriend.png",
"text": "微信好友",
"share": { //当前项的分享参数配置。可覆盖公共的配置如下:分享到微信小程序,配置了type=5
"provider": "weixin",
"scene": "WXSceneSession"
}
},
{
"img": "/static/app-plus/sharemenu/wechatmoments.png",
"text": "微信朋友圈",
"share": {
"provider": "weixin",
"scene": "WXSenceTimeline"
}
},
{
"img": "/static/app-plus/sharemenu/mp_weixin.png",
"text": "微信小程序",
"share": {
provider: "weixin",
scene: "WXSceneSession",
type: 5,
miniProgram: {
id: uniStarterConfig.mp.weixin.id,
path: `/pages/list/detail?id=${_id}&title=${title}`,
webUrl: uniStarterConfig.h5.url +
`/#/pages/list/detail?id=${_id}&title=${title}`,
type: 0
},
}
},
{
"img": "/static/app-plus/sharemenu/weibo.png",
"text": "微博",
"share": {
"provider": "sinaweibo"
}
},
{
"img": "/static/app-plus/sharemenu/qq.png",
"text": "QQ",
"share": {
"provider": "qq"
}
},
{
"img": "/static/app-plus/sharemenu/copyurl.png",
"text": "复制",
"share": "copyurl"
},
{
"img": "/static/app-plus/sharemenu/more.png",
"text": "更多",
"share": "shareSystem"
}
],
cancelText: "取消分享",
}, e => { //callback
console.log(e);
})
```
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册