|
@@ -1,52 +1,227 @@
|
|
|
<template>
|
|
|
<view class="content">
|
|
|
- 意见反馈
|
|
|
- <up-overlay :show="show">
|
|
|
- <login @showChange='showChange'></login>
|
|
|
- </up-overlay>
|
|
|
+ <form @submit="formSubmit">
|
|
|
+ <view class="value other">
|
|
|
+ <view class="title">问题类型</view>
|
|
|
+ <view class="label">
|
|
|
+ <picker @change="typeChange" :range="typeList" range-key='label'>
|
|
|
+ <view class="picker">{{form.type_name||'请选择 >'}}</view>
|
|
|
+ </picker>
|
|
|
+ <span v-if="errors.type" class="error-message">{{ errors.type }}</span>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="brief other margin">
|
|
|
+ <view class="title">问题描述</view>
|
|
|
+ <view class="label">
|
|
|
+ <textarea name='brief' :value="form.brief" placeholder="请写下您的建议,如功能需求、产品吐槽等,我们会努力改进" auto-height />
|
|
|
+ <span v-if="errors.brief" class="error-message">{{ errors.brief }}</span>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="brief other margin">
|
|
|
+ <view class="title">图片(可上传三张)</view>
|
|
|
+ <view class="label">
|
|
|
+ <up-upload :fileList="form.file" @afterRead="afterRead" @delete="deletePic" name="icon" multiple
|
|
|
+ :maxCount="3"></up-upload>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="button">
|
|
|
+ <button type="warn" size="mini" form-type="submit">保存</button>
|
|
|
+ </view>
|
|
|
+ </form>
|
|
|
+ <view class="view">查看历史反馈</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
- import login from "@/components/login.vue"
|
|
|
+ import moment from 'moment';
|
|
|
import { inject, computed, ref } from 'vue';
|
|
|
//该依赖已内置不需要单独安装
|
|
|
import { onShow, onPullDownRefresh } from "@dcloudio/uni-app";
|
|
|
// 请求接口
|
|
|
const $api = inject('$api');
|
|
|
- const $config = inject('$config');
|
|
|
- // 基本信息
|
|
|
- const config = ref({ logo: [], file: [] });
|
|
|
- const list = ref([]);
|
|
|
- const total = ref(0);
|
|
|
- // 遮罩层
|
|
|
- const show = ref(false);
|
|
|
+ const $apifile = inject('$apifile');
|
|
|
+ // 表单
|
|
|
+ const form = ref({ file: [] });
|
|
|
+ // 字典表
|
|
|
+ const typeList = ref([]);
|
|
|
+ const errors = ref({});
|
|
|
// user
|
|
|
const user = computed(() => {
|
|
|
return uni.getStorageSync('user');
|
|
|
})
|
|
|
onShow(async () => {
|
|
|
- await searchConfig();
|
|
|
await searchOther();
|
|
|
- await search();
|
|
|
- if (!user.value) show.value = true
|
|
|
})
|
|
|
- // config信息
|
|
|
- const searchConfig = async () => {
|
|
|
- config.value = uni.getStorageSync('config');
|
|
|
- };
|
|
|
// 其他查询信息
|
|
|
- const searchOther = async () => { };
|
|
|
- // 查询
|
|
|
- const search = async () => { };
|
|
|
- const showChange = () => {
|
|
|
- show.value = false
|
|
|
+ const searchOther = async () => {
|
|
|
+ let res;
|
|
|
+ // 问题类型
|
|
|
+ res = await $api(`dictData`, 'GET', { code: 'opinionType', is_use: '0' });
|
|
|
+ if (res.errcode === 0) typeList.value = res.data;
|
|
|
+ };
|
|
|
+ // 学历类型选择
|
|
|
+ const typeChange = (e) => {
|
|
|
+ const data = typeList.value[e.detail.value]
|
|
|
+ if (data) {
|
|
|
+ form.value.type = data.value
|
|
|
+ form.value.type_name = data.label
|
|
|
+ }
|
|
|
+ };
|
|
|
+ // 删除图片
|
|
|
+ const deletePic = (event) => {
|
|
|
+ const file = form.value.file.filter(i => i.url !== event.file.url)
|
|
|
+ form.value.file = file
|
|
|
+ };
|
|
|
+ // 新增图片
|
|
|
+ const afterRead = async (event) => {
|
|
|
+ const url = event.file[0].url
|
|
|
+ const result = await $apifile(`/web/learn_opinion/upload`, 'file', url, 'file');
|
|
|
+ if (result.errcode === 0) form.value.file = [...form.value.file, ...[result]]
|
|
|
+ };
|
|
|
+ // 自定义的验证函数
|
|
|
+ const validateObject = (obj : any) => {
|
|
|
+ const errors : any = {};
|
|
|
+ if (!obj.type || obj.type.trim() === '') {
|
|
|
+ errors.type = '请填写问题类型!';
|
|
|
+ }
|
|
|
+ if (!obj.brief || obj.brief.trim() === '') {
|
|
|
+ errors.brief = '请填写问题描述!';
|
|
|
+ }
|
|
|
+ // 如果有错误,返回错误对象
|
|
|
+ if (Object.keys(errors).length > 0) {
|
|
|
+ return errors;
|
|
|
+ }
|
|
|
+ // 如果没有错误,返回null或undefined
|
|
|
+ return null;
|
|
|
}
|
|
|
+ // 处理两个对象合并
|
|
|
+ const mergeObjectsWithoutDuplicates = async (obj1, obj2) => {
|
|
|
+ return Object.keys({ ...obj1, ...obj2 }).reduce((acc, key) => {
|
|
|
+ if (!acc.hasOwnProperty(key)) {
|
|
|
+ if (key == 'type_name') {
|
|
|
+ delete acc[key]
|
|
|
+ } else {
|
|
|
+ // 如果累加器对象(acc)中不存在该键,则添加它
|
|
|
+ acc[key] = obj1.hasOwnProperty(key) ? obj1[key] : obj2[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return acc;
|
|
|
+ }, {});
|
|
|
+ }
|
|
|
+ // 保存
|
|
|
+ const formSubmit = async (e) => {
|
|
|
+ // 调用验证函数
|
|
|
+ const data : any = await mergeObjectsWithoutDuplicates(e.detail.value, form.value);
|
|
|
+ const errorsInfo = await validateObject(data);
|
|
|
+ // 检查是否有错误
|
|
|
+ if (errorsInfo) {
|
|
|
+ errors.value = errorsInfo
|
|
|
+ // 遍历错误对象并显示错误信息
|
|
|
+ for (const key in errorsInfo) {
|
|
|
+ if (errorsInfo.hasOwnProperty(key)) {
|
|
|
+ console.error(`${key} 错误: ${errorsInfo[key]}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ errors.value = {}
|
|
|
+ data.user = user.value._id
|
|
|
+ if (user.value.role_type == 'Student') data.userType = '1'
|
|
|
+ else data.userType = '0'
|
|
|
+ data.time = moment().format('YYYY-MM-DD HH:mm:ss')
|
|
|
+ const res = await $api(`opinion`, 'POST', data);
|
|
|
+ if (res.errcode == '0') {
|
|
|
+ uni.showToast({
|
|
|
+ title: '反馈成功!',
|
|
|
+ icon: 'success'
|
|
|
+ });
|
|
|
+ uni.navigateBack({
|
|
|
+ delta: 1
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
</script>
|
|
|
<style lang="scss" scoped>
|
|
|
.content {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
+ min-height: 100vh;
|
|
|
background-color: var(--f1Color);
|
|
|
+
|
|
|
+ .margin {
|
|
|
+ margin: 3vw 0 0 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .other {
|
|
|
+ padding: 3vw 2vw;
|
|
|
+ border-bottom: 1px solid var(--footColor);
|
|
|
+ }
|
|
|
+
|
|
|
+ .value {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ background-color: var(--mainColor);
|
|
|
+
|
|
|
+ .label {
|
|
|
+ text-align: right;
|
|
|
+
|
|
|
+ .input {
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+
|
|
|
+ .image {
|
|
|
+ width: 15vw;
|
|
|
+ height: 15vw;
|
|
|
+ border-radius: 20vw;
|
|
|
+ }
|
|
|
+
|
|
|
+ .error-message {
|
|
|
+ margin: 5px 0 0 0;
|
|
|
+ color: var(--ff0Color);
|
|
|
+ font-size: var(--font12Size);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .brief {
|
|
|
+ background-color: var(--mainColor);
|
|
|
+
|
|
|
+ .title {
|
|
|
+ margin: 0 0 2vw 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .label {
|
|
|
+ .error-message {
|
|
|
+ margin: 5px 0 0 0;
|
|
|
+ color: var(--ff0Color);
|
|
|
+ font-size: var(--font12Size);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .button {
|
|
|
+ text-align: center;
|
|
|
+ margin: 2vw 0 0 0;
|
|
|
+
|
|
|
+ button {
|
|
|
+ width: 80%;
|
|
|
+ font-size: var(--font16Size);
|
|
|
+ color: var(--mainColor);
|
|
|
+ background-color: var(--3c9Color);
|
|
|
+ border-radius: 10vw;
|
|
|
+ margin: 0 2vw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .view {
|
|
|
+ width: 100%;
|
|
|
+ position: fixed;
|
|
|
+ bottom: 2vw;
|
|
|
+ left: 0;
|
|
|
+ text-align: center;
|
|
|
+ color: var(--3c9Color);
|
|
|
+ font-size: var(--font12Size);
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|