index.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <template>
  2. <view class="content">
  3. <form @submit="formSubmit">
  4. <view class="value other">
  5. <view class="title">问题类型</view>
  6. <view class="label">
  7. <picker @change="typeChange" :range="typeList" range-key='label'>
  8. <view class="picker">{{form.type_name||'请选择 >'}}</view>
  9. </picker>
  10. <span v-if="errors.type" class="error-message">{{ errors.type }}</span>
  11. </view>
  12. </view>
  13. <view class="brief other margin">
  14. <view class="title">问题描述</view>
  15. <view class="label">
  16. <textarea name='brief' :value="form.brief" placeholder="请写下您的建议,如功能需求、产品吐槽等,我们会努力改进" auto-height />
  17. <span v-if="errors.brief" class="error-message">{{ errors.brief }}</span>
  18. </view>
  19. </view>
  20. <view class="brief other margin">
  21. <view class="title">图片(可上传三张)</view>
  22. <view class="label">
  23. <up-upload :fileList="form.file" @afterRead="afterRead" @delete="deletePic" name="icon" multiple
  24. :maxCount="3"></up-upload>
  25. </view>
  26. </view>
  27. <view class="button">
  28. <button type="warn" size="mini" form-type="submit">保存</button>
  29. </view>
  30. </form>
  31. <view class="view" @click="toView">查看历史反馈</view>
  32. </view>
  33. </template>
  34. <script setup lang="ts">
  35. import moment from 'moment';
  36. import { inject, computed, ref } from 'vue';
  37. //该依赖已内置不需要单独安装
  38. import { onShow, onPullDownRefresh } from "@dcloudio/uni-app";
  39. // 请求接口
  40. const $api = inject('$api');
  41. const $apifile = inject('$apifile');
  42. // 表单
  43. const form = ref({ file: [] });
  44. // 字典表
  45. const typeList = ref([]);
  46. const errors = ref({});
  47. // user
  48. const user = computed(() => {
  49. return uni.getStorageSync('user');
  50. })
  51. onShow(async () => {
  52. await searchOther();
  53. })
  54. // 其他查询信息
  55. const searchOther = async () => {
  56. let res;
  57. // 问题类型
  58. res = await $api(`dictData`, 'GET', { code: 'opinionType', is_use: '0' });
  59. if (res.errcode === 0) typeList.value = res.data;
  60. };
  61. // 学历类型选择
  62. const typeChange = (e) => {
  63. const data = typeList.value[e.detail.value]
  64. if (data) {
  65. form.value.type = data.value
  66. form.value.type_name = data.label
  67. }
  68. };
  69. // 删除图片
  70. const deletePic = (event) => {
  71. const file = form.value.file.filter(i => i.url !== event.file.url)
  72. form.value.file = file
  73. };
  74. // 新增图片
  75. const afterRead = async (event) => {
  76. const url = event.file[0].url
  77. const result = await $apifile(`/web/learn_opinion/upload`, 'file', url, 'file');
  78. if (result.errcode === 0) form.value.file = [...form.value.file, ...[result]]
  79. };
  80. // 自定义的验证函数
  81. const validateObject = (obj : any) => {
  82. const errors : any = {};
  83. if (!obj.type || obj.type.trim() === '') {
  84. errors.type = '请填写问题类型!';
  85. }
  86. if (!obj.brief || obj.brief.trim() === '') {
  87. errors.brief = '请填写问题描述!';
  88. }
  89. // 如果有错误,返回错误对象
  90. if (Object.keys(errors).length > 0) {
  91. return errors;
  92. }
  93. // 如果没有错误,返回null或undefined
  94. return null;
  95. }
  96. // 处理两个对象合并
  97. const mergeObjectsWithoutDuplicates = async (obj1, obj2) => {
  98. return Object.keys({ ...obj1, ...obj2 }).reduce((acc, key) => {
  99. if (!acc.hasOwnProperty(key)) {
  100. if (key == 'type_name') {
  101. delete acc[key]
  102. } else {
  103. // 如果累加器对象(acc)中不存在该键,则添加它
  104. acc[key] = obj1.hasOwnProperty(key) ? obj1[key] : obj2[key];
  105. }
  106. }
  107. return acc;
  108. }, {});
  109. }
  110. // 保存
  111. const formSubmit = async (e) => {
  112. // 调用验证函数
  113. const data : any = await mergeObjectsWithoutDuplicates(e.detail.value, form.value);
  114. const errorsInfo = await validateObject(data);
  115. // 检查是否有错误
  116. if (errorsInfo) {
  117. errors.value = errorsInfo
  118. // 遍历错误对象并显示错误信息
  119. for (const key in errorsInfo) {
  120. if (errorsInfo.hasOwnProperty(key)) {
  121. console.error(`${key} 错误: ${errorsInfo[key]}`);
  122. }
  123. }
  124. } else {
  125. errors.value = {}
  126. data.user = user.value._id
  127. if (user.value.role_type == 'Student') data.userType = '1'
  128. else data.userType = '0'
  129. data.time = moment().format('YYYY-MM-DD HH:mm:ss')
  130. const res = await $api(`opinion`, 'POST', data);
  131. if (res.errcode == '0') {
  132. uni.showToast({
  133. title: '反馈成功!',
  134. icon: 'success'
  135. });
  136. uni.navigateBack({
  137. delta: 1
  138. })
  139. }
  140. }
  141. };
  142. // 查看历史反馈
  143. const toView = () => {
  144. uni.navigateTo({
  145. url: `/pagesMy/opinion/list`
  146. })
  147. }
  148. </script>
  149. <style lang="scss" scoped>
  150. .content {
  151. display: flex;
  152. flex-direction: column;
  153. min-height: 100vh;
  154. background-color: var(--f1Color);
  155. .margin {
  156. margin: 3vw 0 0 0;
  157. }
  158. .other {
  159. padding: 3vw 2vw;
  160. border-bottom: 1px solid var(--footColor);
  161. }
  162. .value {
  163. display: flex;
  164. justify-content: space-between;
  165. align-items: center;
  166. background-color: var(--mainColor);
  167. .label {
  168. text-align: right;
  169. .input {
  170. text-align: right;
  171. }
  172. .image {
  173. width: 15vw;
  174. height: 15vw;
  175. border-radius: 20vw;
  176. }
  177. .error-message {
  178. margin: 5px 0 0 0;
  179. color: var(--ff0Color);
  180. font-size: var(--font12Size);
  181. }
  182. }
  183. }
  184. .brief {
  185. background-color: var(--mainColor);
  186. .title {
  187. margin: 0 0 2vw 0;
  188. }
  189. .label {
  190. .error-message {
  191. margin: 5px 0 0 0;
  192. color: var(--ff0Color);
  193. font-size: var(--font12Size);
  194. }
  195. }
  196. }
  197. .button {
  198. text-align: center;
  199. margin: 2vw 0 0 0;
  200. button {
  201. width: 80%;
  202. font-size: var(--font16Size);
  203. color: var(--mainColor);
  204. background-color: var(--3c9Color);
  205. border-radius: 10vw;
  206. margin: 0 2vw;
  207. }
  208. }
  209. .view {
  210. width: 100%;
  211. position: fixed;
  212. bottom: 2vw;
  213. left: 0;
  214. text-align: center;
  215. color: var(--3c9Color);
  216. font-size: var(--font12Size);
  217. }
  218. }
  219. </style>