提交 a0c67504 编写于 作者: 芊里

问题反馈页面完成

上级 fd0ec305
......@@ -67,7 +67,18 @@
"navigationStyle": "custom"
}
}, {
"path": "pages/ucenter/uni-feedback/uni-feedback",
"path": "uni_modules/opendb-feedback/pages/opendb-feedback/list",
"style": {
"navigationBarTitleText": "反馈列表",
"enablePullDownRefresh":true
}
}, {
"path": "uni_modules/opendb-feedback/pages/opendb-feedback/add",
"style": {
"navigationBarTitleText": "问题反馈"
}
}, {
"path": "uni_modules/opendb-feedback/pages/opendb-feedback/detail",
"style": {
"navigationBarTitleText": "问题反馈"
}
......@@ -187,27 +198,12 @@
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#F8F8F8"
},
// "condition": {
// "list": [{
// "path": "uni_modules/uni-login-page/pages/index/index",
// "style": {
// "navigationBarTitleText": "",
// "app-plus": {
// "animationType": "none",
// "popGesture": "none",
// "titleNView": {
// "buttons": [{
// "text": "帮助",
// "type": "none",
// "fontSize": "16px",
// "width": "60px"
// }]
// }
// }
// }
// }],
// "current": 0
// },
"condition": {
"list": [{
"path": "uni_modules/opendb-feedback/pages/opendb-feedback/list"
}],
"current": 0
},
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#007AFF",
......
......@@ -81,7 +81,7 @@
],
[{
title: '反馈',
to: '/pages/ucenter/uni-feedback/uni-feedback'
to: '/uni_modules/opendb-feedback/pages/opendb-feedback/list' // /pages/ucenter/uni-feedback/uni-feedback uni_modules/opendb-feedback/pages/opendb-feedback/list
}, {
title: '设置',
to: '/pages/ucenter/settings/settings'
......
{
"bsonType": "object",
"permission": {
"read": "doc.uid == auth.uid && doc.article_status == 0 || doc.article_status == 1",
"create": true,
"update": "doc.uid == auth.uid",
"delete": "doc.uid == auth.uid"
},
"properties": {
"_id": {
"description": "ID,系统自动生成"
},
"user_id": {
"bsonType": "string",
"description": "留言反馈用户ID\/回复留言用户ID,参考uni-id-users表",
"foreignKey": "uni-id-users._id"
},
"create_date": {
"bsonType": "timestamp",
"description": "留言时间\/回复留言时间"
},
"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": "被回复条数"
}
}
}
<template>
<view class="page">
<view class="feedback-title">
<text>问题和意见</text>
<text class="feedback-quick" @tap="chooseMsg">快速键入</text>
</view>
<view class="feedback-body"><textarea placeholder="请详细描述你的问题和意见..." v-model="sendDate.content"
class="feedback-textare"></textarea></view>
<view class="feedback-title"><text>图片(选填,提供问题截图,总大小10M以下)</text></view>
<view class="feedback-body feedback-uploader">
<view class="uni-uploader">
<view class="uni-uploader-head">
<view class="uni-uploader-head" v-if="isChoose">
<view class="uni-uploader-title">点击预览图片</view>
<view class="uni-uploader-info">{{ imageList.length }}/5</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 imageList" :key="index">
<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" @click="close(index)">x</view>
<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="imageList.length < 5">
<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>
<view class="feedback-title"><text>联系方式</text></view>
<view class="feedback-body"><input class="feedback-input" v-model="sendDate.contact"
placeholder="(选填,方便我们联系你 )" /></view>
<view class="feedback-title feedback-star-view">
<text>应用评分</text>
<view class="feedback-star-view">
<uni-rate v-model="sendDate.score" color="#bbb"></uni-rate>
</view>
</view>
<button type="default" class="feedback-submit" @tap="send">提交</button>
<view class="feedback-title"><text>用户反馈的结果可在app打包后于DCloud开发者中心查看</text></view>
</view>
</template>
<script>
const db = uniCloud.database();
const dbCmd = db.command;
export default {
data() {
return {
msgContents: ['界面显示错乱', '启动缓慢,卡出翔了', 'UI无法直视,丑哭了', '偶发性崩溃'],
stars: [1, 2, 3, 4, 5],
imageList: [],
sendDate: {
score: 0,
content: '',
contact: ''
}
};
name:"feedback-imgs",
props:{
imgs:{
type:Array,
default:()=>[]
},
onLoad() {
// #ifdef APP-PLUS
this.deviceInfo = {
appid: plus.runtime.appid,
imei: plus.device.imei, //设备标识
p: plus.os.name === 'Android' ? 'a' : 'i', //平台类型,i表示iOS平台,a表示Android平台。
md: plus.device.model, //设备型号
app_version: plus.runtime.version,
plus_version: plus.runtime.innerVersion, //基座版本号
os: plus.os.version,
net: '' + plus.networkinfo.getCurrentType()
};
//#endif
this.deviceInfo = uni.getSystemInfo();
// #ifndef APP-PLUS
//#endif
this.sendDate = Object.assign(this.deviceInfo, this.sendDate);
isChoose:{
type:Boolean,
default:false
}
},
methods: {
methods:{
/**
* 关闭图片
* @param {Object} e
*/
close(e) {
this.imageList.splice(e, 1);
this.$emit( 'close', e)
},
/**
* 快速输入
*/
chooseMsg() {
uni.showActionSheet({
itemList: this.msgContents,
success: res => {
this.sendDate.content = this.msgContents[res.tapIndex];
}
});
},
/**
* 选择图片
*/
chooseImg() {
//选择图片
uni.chooseImage({
sourceType: ['camera', 'album'],
sizeType: 'compressed',
count: 5 - this.imageList.length,
success: res => {
this.imageList = this.imageList.concat(res.tempFilePaths);
}
});
},
/**
* 评分
* @param {Object} e
*/
chooseStar(e) {
//点击评星
this.sendDate.score = e;
this.$emit('chooseImg');
},
/**
* 预览图片
......@@ -126,91 +56,15 @@
*/
previewImage(index) {
uni.previewImage({
urls: this.imageList,
current: this.imageList[index]
urls: this.imgs,
current: this.imgs[index]
});
},
/**
* 提交
*/
send() {
//发送反馈
if (this.sendDate.content.length === 0) {
uni.showModal({
content: '请输入问题和意见',
showCancel: false
});
return;
}
uni.showLoading({
title: '上传中...'
});
let imgs = this.imageList.map((value, index) => {
return {
name: 'images' + index,
uri: value
};
});
// TODO 服务端限制上传文件一次最大不超过 2M, 图片一次最多不超过5张
this.request(this.sendDate, imgs)
.then(res => {
console.log(res);
if(res.success){
this.sendDate.content = '';
this.imageList = [];
return uni.showToast({
title: '反馈成功',
icon: 'none'
});
}
uni.showToast({
title: `err${res.result?.code}:${res.result?.message}`,
icon: 'none'
});
})
.catch(err => {
console.log(err);
})
.finally(()=>{
uni.hideLoading();
})
},
/**
* 发送请求到后台
*/
async request(sendDate, imgs) {
let cloud_list = [];
for (let i = 0; i < imgs.length; i++) {
let res = await uniCloud.uploadFile({
filePath: imgs[i].uri,
cloudPath: 'feedback.jpg',
onUploadProgress: function(progressEvent) {}
})
cloud_list.push(res.fileID);
}
console.log('cloud_', cloud_list);
return db.collection('opendb-feedback').add({
// ...sendDate,
"create_date": Date.now(),
"content": this.sendDate.content,
"mobile": this.sendDate.contact,
imgs: cloud_list
})
}
}
};
</script>
<style>
page {
background-color: #efeff4;
}
.input-view {
font-size: 28rpx;
}
.close-view {
text-align: center;
......@@ -225,44 +79,53 @@
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;
margin: 10rpx;
width: 208rpx;
height: 208rpx;
border: 2rpx solid #D9D9D9;
}
.uni-uploader__input-box:before,
.uni-uploader__input-box:after {
content: " ";
......@@ -273,21 +136,26 @@
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;
......@@ -297,6 +165,7 @@
height: 100%;
opacity: 0;
}
/*问题反馈*/
.feedback-title {
display: flex;
......@@ -307,14 +176,17 @@
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;
......@@ -326,9 +198,11 @@
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.feedback-body {
background: #fff;
}
.feedback-textare {
height: 200rpx;
font-size: 34rpx;
......@@ -337,6 +211,7 @@
box-sizing: border-box;
padding: 20rpx 30rpx 0;
}
.feedback-input {
font-size: 34rpx;
height: 50rpx;
......@@ -344,29 +219,37 @@
padding: 15rpx 20rpx;
line-height: 50rpx;
}
.feedback-uploader {
padding: 22rpx 20rpx;
}
.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;
}
</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="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="请输入反馈内容" type="textarea" v-model="formData.content" trim="right" />
</uni-forms-item>
<uni-forms-item name="imgs" label="反馈截图">
<!-- <uni-data-checkbox :multiple="true" v-model="formData.imgs" /> -->
<feedback-imgs :isChoose="true" :imgs="formData.imgs" @close="close" @chooseImg="chooseImg"></feedback-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>
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();
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.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} 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="user_id,create_date,content,imgs,is_reply,feedback_id,contact,mobile,reply_count">
<view v-if="error">{{error.message}}</view>
<view v-else-if="data">
<uni-list>
<uni-list-item v-for="(item, index) in data" :key="index" showArrow :clickable="true" @click="handleItemClick(item._id)">
<view slot="body">
<!-- 此处默认显示为_id,请根据需要自行修改为其他字段 -->
{{item.content}}
</view>
</uni-list-item>
</uni-list>
</view>
<uni-load-more :status="loading?'loading':(hasMore ? 'more' : 'noMore')"></uni-load-more>
</unicloud-db>
<uni-fab ref="fab" horizontal="right" vertical="bottom" :pop-menu="false" @fabClick="fabClick" />
</view>
</template>
<script>
export default {
data() {
return {
loadMore: {
contentdown: '',
contentrefresh: '',
contentnomore: ''
}
}
},
onPullDownRefresh() {
this.$refs.udb.loadData({
clear: true
}, (res) => {
uni.stopPullDownRefresh()
})
},
onReachBottom() {
this.$refs.udb.loadMore()
},
methods: {
handleItemClick(id) {
uni.navigateTo({
url: './detail?id=' + id
})
},
fabClick() {
// 打开新增页面
uni.navigateTo({
url: './add',
events: {
// 监听新增数据成功后, 刷新当前页面数据
refreshData: () => {
this.$refs.udb.loadData({
clear: true
})
}
}
})
}
}
}
</script>
<style>
</style>
# opendb-feedback
\ No newline at end of file
{
"bsonType": "object",
"permission": {
"read": "auth.uid && doc.uid == auth.uid",
"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"
},
"create_date": {
"bsonType": "timestamp",
"description": "留言时间/回复留言时间"
},
"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": "被回复条数"
}
}
}
\ No newline at end of file
## 0.1.4(2021-04-09)
- 修复 nvue 下无法选中的问题
## 0.1.3(2021-03-22)
- 新增 disabled属性
## 0.1.2(2021-02-24)
......
......@@ -470,7 +470,7 @@
.hidden {
position: absolute;
transform: scale(0);
// transform: scale(0);
opacity: 0;
}
......
{
"id": "uni-data-checkbox",
"displayName": "DataCheckbox 数据选择器",
"version": "0.1.3",
"version": "0.1.4",
"description": "通过数据驱动的单选框和复选框",
"keywords": [
"checkbox",
......
## DataCheckbox 数据驱动的单选复选框
> **组件名:uni-data-checkbox**
> 代码块: `uDataCheckbox`
......@@ -13,6 +14,7 @@
在uniCloud开发中,`DB Schema`中配置了enum枚举等类型后,在web控制台的[自动生成表单](https://uniapp.dcloud.io/uniCloud/schema?id=autocode)功能中,会自动生成``uni-data-checkbox``组件并绑定好data
> **注意事项**
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
> - 组件需要依赖 `sass` 插件 ,请自行手动安装
> - 本组件为数据驱动,目的是快速投入使用,只可通过 style 覆盖有限样式,不支持自定义更多样式
......
......@@ -59,13 +59,7 @@ function getDate(time) {
}
switch (typeof time) {
case 'string':
{
// 2020-12-12T12:12:12.000Z、2020-12-12T12:12:12.000
if (time.indexOf('T') > -1) {
return new Date(time)
}
return new Date(time.replace(/-/g, '/'))
}
default:
return new Date(time)
}
......
......@@ -2,7 +2,6 @@
<view class="uni-easyinput" :class="{'uni-easyinput-error':msg}" :style="{color:inputBorder && msg?'#dd524d':styles.color}">
<view class="uni-easyinput__content" :class="{'is-input-border':inputBorder ,'is-input-error-border':inputBorder && msg,'is-textarea':type==='textarea','is-disabled':disabled}"
:style="{'border-color':inputBorder && msg?'#dd524d':styles.borderColor,'background-color':disabled?styles.disableColor:''}">
<slot name="left"></slot>
<uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc" @click="onClickIcon('prefix')"></uni-icons>
<textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea" :class="{'input-padding':inputBorder}"
:name="name" :value="val" :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled"
......
## 0.0.6(2021-04-09)
- 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug
## 0.0.5(2021-04-09)
- 优化 更新组件示例
## 0.0.4(2021-04-09)
- 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔
## 0.0.3(2021-02-05)
- 调整为uni_modules目录规范
- 修复 微信小程序不指定 fileExtname 属性选择失败的Bug
<template>
<view class="uni-file-picker">
<view v-if="title" class="uni-file-picker__header">
<text class="file-title">{{title}}</text>
<text class="file-count">{{filesList.length}}/{{limitLength}}</text>
<text class="file-title">{{ title }}</text>
<text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
</view>
<upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly" :image-styles="imageStyles"
:files-list="filesList" :limit="limitLength" :disablePreview="disablePreview" :delIcon="delIcon" @uploadFiles="uploadFiles"
@choose="choose" @delFile="delFile">
<upload-image
v-if="fileMediatype === 'image' && showType === 'grid'"
:readonly="readonly"
:image-styles="imageStyles"
:files-list="filesList"
:limit="limitLength"
:disablePreview="disablePreview"
:delIcon="delIcon"
@uploadFiles="uploadFiles"
@choose="choose"
@delFile="delFile"
>
<slot>
<view class="is-add">
<view class="icon-add"></view>
......@@ -14,16 +23,24 @@
</view>
</slot>
</upload-image>
<upload-file v-if="(fileMediatype !== 'image' || showType !== 'grid')" :readonly="readonly" :list-styles="listStyles"
:files-list="filesList" :showType="showType" :delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
<upload-file
v-if="fileMediatype !== 'image' || showType !== 'grid'"
:readonly="readonly"
:list-styles="listStyles"
:files-list="filesList"
:showType="showType"
:delIcon="delIcon"
@uploadFiles="uploadFiles"
@choose="choose"
@delFile="delFile"
>
<slot><button type="primary" size="mini">选择文件</button></slot>
</upload-file>
</view>
</template>
<script>
/**
/**
* FilePicker
* @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
* @tutorial https://ext.dcloud.net.cn/plugin?id=-1
......@@ -65,14 +82,12 @@
* @event {Function} delete 文件从列表移除时触发
*/
import {
chooseAndUploadFile
} from './choose-and-upload-file.js'
import uploadImage from './upload-image.vue'
import uploadFile from './upload-file.vue'
let fileInput = null
export default {
name: "uniFilePicker",
import { chooseAndUploadFile } from './choose-and-upload-file.js'
import uploadImage from './upload-image.vue'
import uploadFile from './upload-file.vue'
let fileInput = null
export default {
name: 'uniFilePicker',
components: {
uploadImage,
uploadFile
......@@ -80,7 +95,7 @@
props: {
value: {
type: [Array, Object],
default () {
default() {
return []
}
},
......@@ -122,8 +137,8 @@
},
// 文件类型筛选
fileExtname: {
type: Array,
default () {
type: [Array, String],
default() {
return []
}
},
......@@ -133,7 +148,7 @@
},
listStyles: {
type: Object,
default () {
default() {
return {
// 是否显示边框
border: true,
......@@ -146,7 +161,7 @@
},
imageStyles: {
type: Object,
default () {
default() {
return {
width: 'auto',
height: 'auto'
......@@ -185,7 +200,7 @@
},
data() {
return {
files: []
files: [],
}
},
computed: {
......@@ -213,11 +228,21 @@
return 9
}
return this.limit
},
extname(){
if (!Array.isArray(this.fileExtname)) {
let extname = this.fileExtname.replace(/(\[|\])/g,'')
return extname.split(',')
} else {
return this.fileExtname
}
return []
}
},
created() {
// this.files = Object.assign([], this.value)
this.tempData = {}
},
methods: {
/**
......@@ -264,36 +289,35 @@
// fail: function(res) {}
// });
this.chooseFiles()
},
/**
* 选择文件并上传
*/
chooseFiles() {
// API 正式发布前,使用本地API上传函数
if(!uniCloud.chooseAndUploadFile){
if (!uniCloud.chooseAndUploadFile) {
uniCloud.chooseAndUploadFile = chooseAndUploadFile
}
uniCloud.chooseAndUploadFile({
uniCloud
.chooseAndUploadFile({
type: this.fileMediatype,
compressed: false,
// TODO 如果为空,video 有问题
extension: this.fileExtname.length> 0 ? this.fileExtname: undefined,
extension: this.extname.length > 0 ? this.extname : undefined,
count: this.limitLength - this.files.length, //默认9
onChooseFile: async (res) => {
onChooseFile: async res => {
if ((Number(this.limitLength) === 1 && this.disablePreview && !this.disabled) || this.returnType === 'object') {
this.files = []
}
let filePaths = []
let files = []
if (this.fileExtname && this.fileExtname.length > 0) {
if (this.extname && this.extname.length > 0) {
res.tempFiles.forEach(v => {
let fileFullName = this.getFileExt(v.name)
const extname = fileFullName.ext.toLowerCase()
if (this.fileExtname.indexOf(extname) !== -1) {
if (this.extname.indexOf(extname) !== -1) {
files.push(v)
filePaths.push(v.path)
}
......@@ -310,7 +334,6 @@
files = res.tempFiles
}
let currentData = []
for (let i = 0; i < files.length; i++) {
if (this.limitLength - this.files.length <= 0) break
......@@ -323,9 +346,9 @@
}
this.$emit('select', {
tempFiles: currentData,
tempFilePaths: filePaths,
tempFilePaths: filePaths
})
res.tempFiles = files
// 停止自动上传
if (!this.autoUpload) {
res.tempFiles = []
......@@ -338,14 +361,15 @@
// })
}
},
onUploadProgress: (progressEvent) => {
onUploadProgress: progressEvent => {
this.setProgress(progressEvent, progressEvent.index)
},
}).then(result => {
}
})
.then(result => {
this.setSuccessAndError(result.tempFiles)
}).catch(err => {
console.log('选择失败', err);
})
.catch(err => {
console.log('选择失败', err)
})
},
......@@ -355,12 +379,14 @@
*/
uploadFiles(files) {
files = [].concat(files)
this.uploadCloudFiles(files, 5, (res) => {
this.uploadCloudFiles(files, 5, res => {
this.setProgress(res, res.index, true)
}).then(result => {
})
.then(result => {
this.setSuccessAndError(result)
}).catch(err => {
console.log('err', err);
})
.catch(err => {
console.log('err', err)
})
},
......@@ -418,10 +444,8 @@
*/
setProgress(progressEvent, index, type) {
const fileLenth = this.files.length
const percentNum = index / fileLenth * 100
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
const percentNum = (index / fileLenth) * 100
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
let idx = index
if (!type) {
idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid)
......@@ -432,7 +456,7 @@
this.$emit('progress', {
index: idx,
progress: parseInt(percentCompleted),
tempFile: this.files[idx],
tempFile: this.files[idx]
})
},
......@@ -453,12 +477,12 @@
* @param {Object} name
*/
getFileExt(name) {
const last_len = name.lastIndexOf(".");
const len = name.length;
const last_len = name.lastIndexOf('.')
const len = name.length
return {
name: name.substring(0, last_len),
ext: name.substring(last_len + 1, len)
};
}
},
/**
......@@ -487,16 +511,16 @@
let fileFullName = this.getFileExt(files.name)
const extname = fileFullName.ext.toLowerCase()
let filedata = {
"name": files.name,
"uuid": files.uuid,
"extname": extname || '',
"cloudPath": files.cloudPath,
"fileType": files.fileType,
"url": files.path || files.path,
"size": files.size, //单位是字节
"image": {},
"path": files.path,
"video": {}
name: files.name,
uuid: files.uuid,
extname: extname || '',
cloudPath: files.cloudPath,
fileType: files.fileType,
url: files.path || files.path,
size: files.size, //单位是字节
image: {},
path: files.path,
video: {}
}
if (type === 'image') {
const imageinfo = await this.getFileInfo(files.path)
......@@ -512,21 +536,21 @@
*/
uploadCloudFiles(files, max = 5, onUploadProgress) {
files = JSON.parse(JSON.stringify(files))
const len = files.length;
let count = 0;
const len = files.length
let count = 0
let self = this
return new Promise((resolve) => {
return new Promise(resolve => {
while (count < max) {
next();
next()
}
function next() {
let cur = count++;
let cur = count++
if (cur >= len) {
!files.find((item) => !item.url && !item.errMsg) && resolve(files);
return;
!files.find(item => !item.url && !item.errMsg) && resolve(files)
return
}
const fileItem = files[cur];
const fileItem = files[cur]
const index = self.files.findIndex(v => v.uuid === fileItem.uuid)
fileItem.url = ''
delete fileItem.errMsg
......@@ -536,34 +560,33 @@
filePath: fileItem.path,
cloudPath: fileItem.cloudPath,
fileType: fileItem.fileType,
onUploadProgress: (res) => {
onUploadProgress: res => {
res.index = index
onUploadProgress &&
onUploadProgress(res);
},
onUploadProgress && onUploadProgress(res)
}
})
.then((res) => {
fileItem.url = res.fileID;
.then(res => {
fileItem.url = res.fileID
fileItem.index = index
if (cur < len) {
next();
next()
}
})
.catch((res) => {
fileItem.errMsg = res.errMsg || res.message;
.catch(res => {
fileItem.errMsg = res.errMsg || res.message
fileItem.index = index
if (cur < len) {
next();
next()
}
});
})
}
});
})
},
setEmit() {
let data = []
if(this.returnType === 'object'){
if (this.returnType === 'object') {
data = this.backObject(this.files)[0]
}else{
} else {
data = this.backObject(this.files)
}
this.$emit('input', data)
......@@ -594,53 +617,53 @@
}
}
}
}
}
</script>
<style>
.uni-file-picker {
.uni-file-picker {
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
/* #endif */
}
}
.uni-file-picker__header {
.uni-file-picker__header {
padding-top: 5px;
padding-bottom: 10px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: space-between;
}
}
.file-title {
.file-title {
font-size: 14px;
color: #333;
}
}
.file-count {
.file-count {
font-size: 14px;
color: #999;
}
}
.is-add {
.is-add {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
}
.icon-add {
.icon-add {
width: 50px;
height: 5px;
background-color: #f1f1f1;
border-radius: 2px;
}
}
.rotate {
.rotate {
position: absolute;
transform: rotate(90deg);
}
}
</style>
......@@ -145,7 +145,6 @@
for (let i in obj) {
classles += `${i}:${obj[i]};`
}
console.log(classles);
return classles
}
},
......
{
"id": "uni-file-picker",
"displayName": "FilePicker 文件选择上传",
"version": "0.0.3",
"version": "0.0.6",
"description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间",
"keywords": [
"uni-ui",
......
## FilePicker 文件选择上传
> **组件名:uni-file-picker**
> 代码块: `uFilePicker`
文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
> **注意事项**
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
> - 组件需要依赖 `sass` 插件 ,请自行手动安装
> - 使用组件需要绑定服务空间
......@@ -31,7 +33,7 @@
| title | String | - | - | 组件标题,右侧显示上传计数 |
| mode | String | list | list/grid | 选择文件后的文件列表样式 |
| file-mediatype| String | image | image/video/all | 选择文件类型,all 只支持 H5 和微信小程序平台 |
| file-extname | Array | - | - | 选择文件后缀,根据 `file-mediatype` 属性而不同|
| file-extname | Array\String | - | - | 选择文件后缀,字符串的情况下需要用逗号分隔(推荐使用字符串),根据 `file-mediatype` 属性而不同|
| list-styles | Object | - | - | `mode:list` 时的样式 |
| image-styles | Object | - | - | `mode:grid` 时的样式 |
......@@ -174,7 +176,7 @@ export default {
配置 `file-mediatype` 属性为 `image`,限定只选择图片
配置 `file-extname` 属性为 `['png','jpg']`,限定只选择 `png``jpg`后缀的图片
配置 `file-extname` 属性为 `'png,jpg'`,限定只选择 `png``jpg`后缀的图片
配置 `limit` 属性为 1 ,则最多选择一张图片
......@@ -186,7 +188,7 @@ export default {
v-model="imageValue"
file-mediatype="image"
mode="grid"
file-extname="['png','jpg']"
file-extname="png,jpg"
:limit="1"
@progress="progress"
@success="success"
......
## 0.0.3(2021-03-09)
- 新增 href 属性支持 tel:|mailto:
## 0.0.2(2021-02-05)
- 调整为uni_modules目录规范
{
"id": "uni-link",
"displayName": "Link 超链接",
"version": "0.0.2",
"version": "0.0.3",
"description": "uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打",
"keywords": [
"",
......
......@@ -39,6 +39,8 @@
</template>
<script>
/**
* ListItem 列表子组件
* @description 列表子组件
......@@ -368,7 +370,7 @@ $list-item-pd: $uni-spacing-col-lg $uni-spacing-row-lg;
/* #endif */
height: $uni-img-size-base;
width: $uni-img-size-base;
margin-right: 10px;
marin-right: 10px;
}
.uni-icon-wrapper {
......
## 1.1.7(2021-03-30)
- 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug
## 1.1.6(2021-02-05)
- 调整为uni_modules目录规范
......@@ -28,7 +28,10 @@
</template>
<script>
const platform = uni.getSystemInfoSync().platform
let platform
setTimeout(() => {
platform = uni.getSystemInfoSync().platform
}, 16)
/**
* LoadMore 加载更多
......
{
"id": "uni-load-more",
"displayName": "LoadMore 加载更多",
"version": "1.1.6",
"version": "1.1.7",
"description": "LoadMore 组件,常用在列表里面,做滚动加载使用。",
"keywords": [
"loadmore",
......
### LoadMore 加载更多
> **组件名:uni-load-more**
> 代码块: `uLoadMore`
......@@ -27,7 +28,7 @@
|属性名 |类型 | 可选值 |默认值 |说明 |
|:-: |:-: |:-: |:-: |:-: |
|iconSize |Number |- |24 |指定图标大小 |
|status |String |more/loading/moMore |more |loading 的状态 |
|status |String |more/loading/noMore |more |loading 的状态 |
|showIcon |Boolean|- |true |是否显示 loading 图标 |
|iconType |String |snow/circle/auto |auto |指定图标样式|
|color |String |- |#777777 |图标和文字颜色 |
......@@ -51,6 +52,7 @@
> **说明**
> `iconType`为`snow`时,在`APP-NVUE`平台不可设置大小,在非`APP-NVUE`平台不可设置颜色
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册