BasicInfo.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <!--
  2. * @since: 2024-12-30
  3. * BasicInfo.vue
  4. -->
  5. <template>
  6. <CardLayout title="基础配置" :isShowWraning="false" :mandatory="false">
  7. <el-form
  8. ref="ruleFormRef"
  9. style="max-width: 600px"
  10. label-width="auto"
  11. :model="ruleForm"
  12. :rules="formRules"
  13. :label-position="labelPosition"
  14. class="el-form-outer"
  15. >
  16. <el-form-item label="消息样式: " prop="messageType">
  17. <el-radio-group v-model="ruleForm.messageType" :disabled="isDisabled">
  18. <el-radio :value="item.value" v-for="item in messageTypeOptions" :key="item.value"
  19. >{{ item.label }}
  20. </el-radio>
  21. </el-radio-group>
  22. </el-form-item>
  23. <el-form-item label="消息标题: " prop="title">
  24. <el-input
  25. v-model="ruleForm.title"
  26. placeholder="请输入20字以内的消息标题"
  27. maxlength="20"
  28. show-word-limit
  29. :disabled="isDisabled"
  30. />
  31. </el-form-item>
  32. <el-form-item
  33. label="Banner图片: "
  34. prop="bannerUrl"
  35. v-if="ruleForm.messageType === MessageTypeEnum.BANNER"
  36. >
  37. <img v-if="ruleForm.bannerUrl" :src="ruleForm.bannerUrl" class="avatar" />
  38. <el-upload
  39. v-else
  40. class="avatar-uploader"
  41. :action="actionUrl"
  42. :headers="getHeaders()"
  43. :data="{ bizType: 'PROBLEM_REPORT' }"
  44. :show-file-list="false"
  45. :on-success="handleAvatarSuccess"
  46. :before-upload="beforeAvatarUpload"
  47. >
  48. <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
  49. </el-upload>
  50. </el-form-item>
  51. <el-form-item label="推送渠道: " prop="pushChannel">
  52. <el-checkbox-group v-model="ruleForm.pushChannel">
  53. <el-checkbox
  54. v-for="item in pushChannelName"
  55. :key="item.value"
  56. :value="item.value"
  57. :label="item.label"
  58. :disabled="isDisabled"
  59. />
  60. </el-checkbox-group>
  61. </el-form-item>
  62. <el-form-item label="失效时间: " prop="expirationTime">
  63. <el-date-picker
  64. v-model="ruleForm.expirationTime"
  65. type="datetime"
  66. placeholder="请选择失效时间"
  67. format="YYYY/MM/DD hh:mm:ss"
  68. value-format="YYYY-MM-DD hh:mm:ss"
  69. :disabled="isDisabled"
  70. />
  71. </el-form-item>
  72. <PushObject
  73. ref="childFromRef"
  74. :recipientType="ruleForm.recipientType"
  75. :userGroupList="ruleForm.userGroupList"
  76. :customUserList="ruleForm.customUserList"
  77. :disabled="isDisabled"
  78. />
  79. </el-form>
  80. </CardLayout>
  81. </template>
  82. <script setup lang="ts">
  83. import { ref, watch, computed, unref } from 'vue';
  84. import type { FormInstance, FormProps, FormRules, UploadProps } from 'element-plus';
  85. import { ElMessage } from 'element-plus';
  86. import { Plus } from '@element-plus/icons-vue';
  87. import PushObject from '../../components/PushObject.vue';
  88. import CardLayout from './CardLayout.vue';
  89. import { RuleFormView, MessageTypeEnum, RuleFormAdd } from '../type';
  90. import { messageTypeOptions, pushChannelName } from '../../constant';
  91. import urlJoin from 'url-join';
  92. import { useGlobSetting } from '@/hooks/setting';
  93. import { getHeaders } from '@/utils/http/axios';
  94. const { urlPrefix } = useGlobSetting();
  95. interface Props {
  96. dataSoure: RuleFormView;
  97. isDisabled: boolean;
  98. }
  99. const props = defineProps<Props>();
  100. const labelPosition = ref<FormProps['labelPosition']>('left');
  101. const childFromRef = ref<InstanceType<typeof PushObject>>();
  102. const actionUrl = computed(() => {
  103. return urlJoin(urlPrefix!, `/issue/uploadPicture`);
  104. });
  105. /**
  106. * 表单相关操作
  107. */
  108. type Rule = Omit<RuleFormView, 'introduction' | 'contentType' | 'content' | 'contentUrl' | 'operator'>;
  109. const formRules: FormRules<Rule> = {
  110. messageType: [{ required: true, trigger: 'change', message: '请选择消息样式' }],
  111. title: [{ required: true, trigger: 'change', message: '请输入消息标题' }],
  112. pushChannel: [{ required: true, trigger: 'change', message: '请选择推送渠道' }],
  113. recipientType: [{ required: true, trigger: 'change', message: '请选择推送对象' }],
  114. bannerUrl: [{ required: true, trigger: 'change', message: '请选择banner图片' }],
  115. };
  116. const ruleForm = ref<Rule>({
  117. messageType: MessageTypeEnum.BANNER,
  118. title: '',
  119. bannerUrl:'',
  120. pushChannel: [],
  121. expirationTime: '',
  122. recipientType: 1, // 默认全员
  123. userGroupList:[],
  124. customUserList: [],
  125. });
  126. watch(
  127. () => props.dataSoure,
  128. (value) => {
  129. if (value) {
  130. ruleForm.value = { ...value };
  131. }
  132. },
  133. {
  134. immediate: true,
  135. deep: true,
  136. },
  137. );
  138. const ruleFormRef = ref<FormInstance>();
  139. const isValidate = ref<boolean>();
  140. const validate = async () => {
  141. if (!ruleFormRef.value) return;
  142. try {
  143. const isSuccess = await ruleFormRef.value.validate();
  144. if (isSuccess) {
  145. const childValue = childFromRef.value!.getChildValue();
  146. childFromRef.value!.submitForm().then((res) => {
  147. isValidate.value = res;
  148. });
  149. const basicInfo = { ...unref(ruleForm) } as RuleFormAdd;
  150. if (childValue) {
  151. basicInfo.recipientType = childValue.recipientType!;
  152. basicInfo.userGroupList = childValue.userGroupList!;
  153. basicInfo.customUserList = childValue.customUserList!;
  154. }
  155. return basicInfo;
  156. }
  157. } catch (error) {
  158. ElMessage.error('请完善信息填写');
  159. throw error;
  160. }
  161. };
  162. const handleAvatarSuccess: UploadProps['onSuccess'] = (response) => {
  163. // 注意,这里 response 是 没有被拦截处理
  164. ruleForm.value.bannerUrl = response.data.url;
  165. console.log(ruleForm.value);
  166. };
  167. const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
  168. if (!['image/jpeg', 'image/png'].includes(rawFile.type)) {
  169. ElMessage.error('Avatar picture must be JPG format!');
  170. return false;
  171. } else if (rawFile.size / 1024 / 1024 > 5) {
  172. ElMessage.error('Avatar picture size can not exceed 2MB!');
  173. return false;
  174. }
  175. return true;
  176. };
  177. defineExpose({ validate });
  178. </script>
  179. <style scoped lang="scss">
  180. .avatar-uploader .avatar {
  181. width: 58px;
  182. height: 58px;
  183. display: block;
  184. }
  185. .avatar-uploader .el-upload {
  186. border: 1px dashed var(--el-border-color);
  187. border-radius: 6px;
  188. cursor: pointer;
  189. position: relative;
  190. overflow: hidden;
  191. transition: var(--el-transition-duration-fast);
  192. }
  193. .avatar-uploader .el-upload:hover {
  194. border-color: var(--el-color-primary);
  195. }
  196. .el-icon.avatar-uploader-icon {
  197. font-size: 28px;
  198. color: #8c939d;
  199. width: 58px;
  200. height: 58px;
  201. text-align: center;
  202. }
  203. </style>