ContentConfig.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <!--
  2. * @since: 2024-12-30
  3. * ContentConfig.vue
  4. -->
  5. <template>
  6. <div class="container"></div>
  7. <card-layout title="内容配置" :isShowWraning="false" :mandatory="false">
  8. <el-form
  9. ref="ruleFormRef"
  10. label-width="auto"
  11. :model="ruleForm"
  12. :label-position="labelPosition"
  13. class="el-form-outer"
  14. >
  15. <el-form-item label="简介内容" prop="introduction" class="transprant">
  16. <el-input
  17. v-model="ruleForm.introduction"
  18. placeholder="请输入500字以内的简介内容"
  19. type="textarea"
  20. :rows="5"
  21. maxlength="500"
  22. show-word-limit
  23. :disabled="isDisabled"
  24. />
  25. </el-form-item>
  26. <el-form-item label="详情内容" prop="contentType" class="transprant">
  27. <el-radio-group v-model="ruleForm.contentType" :disabled="isDisabled">
  28. <el-radio :value="item.value" v-for="item in contentTypeOptinos" :key="item.value">{{
  29. item.label
  30. }}</el-radio>
  31. </el-radio-group>
  32. </el-form-item>
  33. <el-form-item
  34. label=" "
  35. prop="contentUrl"
  36. class="transprant"
  37. v-if="ruleForm.contentType === ContentTypeEnum.LINK"
  38. >
  39. <el-input
  40. v-model="ruleForm.contentUrl"
  41. placeholder="请将链接地址粘贴到此处"
  42. :disabled="isDisabled"
  43. />
  44. </el-form-item>
  45. <el-form-item
  46. label=" "
  47. prop="content"
  48. v-if="ruleForm.contentType === ContentTypeEnum.RICHTEXT"
  49. >
  50. <div style="border: 1px solid #ccc">
  51. <Toolbar
  52. style="border-bottom: 1px solid #ccc"
  53. :editor="editorRef"
  54. :defaultConfig="toolbarConfig"
  55. />
  56. <Editor
  57. style="height: 500px; overflow-y: hidden"
  58. v-model="valueHtml"
  59. :defaultConfig="editorConfig"
  60. @onCreated="handleCreated"
  61. @onChange="handleChange"
  62. @onDestroyed="handleDestroyed"
  63. @onFocus="handleFocus"
  64. @onBlur="handleBlur"
  65. />
  66. </div>
  67. </el-form-item>
  68. <el-form-item label="操作人" prop="operator" class="transprant">
  69. <el-input v-model="ruleForm.operator" :disabled="true" />
  70. </el-form-item>
  71. </el-form>
  72. </card-layout>
  73. </template>
  74. <script setup lang="ts">
  75. import { onBeforeUnmount, ref, shallowRef, watch } from 'vue';
  76. import type { FormProps } from 'element-plus';
  77. import '@wangeditor/editor/dist/css/style.css'; // 引入 css
  78. import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
  79. import type { IEditorConfig } from '@wangeditor/editor';
  80. import { RuleFormView, ContentTypeEnum } from './../type';
  81. import { contentTypeOptinos } from '../../constant';
  82. import CardLayout from './CardLayout.vue';
  83. import { useUserStore } from '@/store/modules/user';
  84. interface Props {
  85. dataSoure: RuleFormView;
  86. isDisabled: boolean;
  87. }
  88. const props = defineProps<Props>();
  89. const labelPosition = ref<FormProps['labelPosition']>('left');
  90. const userStore = useUserStore();
  91. /**
  92. * 表单相关操作
  93. */
  94. type Rule = Pick<RuleFormView, 'introduction' | 'contentType' | 'content' | 'operator' | 'contentUrl'>;
  95. const ruleForm = ref<Rule>({
  96. introduction: '',
  97. contentType: ContentTypeEnum.RICHTEXT,
  98. content: '',
  99. operator: '',
  100. contentUrl: '',
  101. });
  102. watch(
  103. () => props.dataSoure,
  104. (value) => {
  105. if (value) {
  106. const { introduction, contentType, content, operator, contentUrl } = value;
  107. ruleForm.value = {
  108. introduction,
  109. contentType,
  110. content,
  111. operator,
  112. contentUrl,
  113. };
  114. if (content) {
  115. valueHtml.value = content;
  116. }
  117. }
  118. },
  119. {
  120. immediate: true,
  121. deep: true,
  122. },
  123. );
  124. /********************* 富文本区域配置与方法 **********************/
  125. // 编辑器实例,必须用 shallowRef
  126. const editorRef = shallowRef();
  127. // 内容 HTML
  128. const valueHtml = ref();
  129. // 排除工具栏选项
  130. const toolbarConfig = {
  131. excludeKeys: [
  132. 'insertTable', // 插入表格
  133. 'deleteTable', // 删除表格
  134. 'insertVideo',
  135. 'uploadVideo',
  136. 'codeBlock', // 代码块
  137. 'emotion', // 表情
  138. ],
  139. };
  140. const editorConfig: Partial<IEditorConfig> = {
  141. placeholder: '请输入内容...',
  142. MENU_CONF: {},
  143. };
  144. editorConfig.MENU_CONF!['uploadImage'] = {
  145. // 上传图片的配置
  146. server: '/eye_api_bak/api/systemMessage/uploadPicture', // form-data fieldName ,默认值 'wangeditor-uploaded-image'
  147. fieldName: 'file',
  148. // 单个文件的最大体积限制,默认为 2M
  149. maxFileSize: 5 * 1024 * 1024, // 1M
  150. // 最多可上传几个文件,默认为 100
  151. maxNumberOfFiles: 10,
  152. // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
  153. allowedFileTypes: ['image/*'],
  154. // 自定义上传参数。参数会被添加到 formData 中,一起上传到服务端。
  155. meta: {
  156. bizType: 'PUSH_MESSAGE',
  157. fileName: ''
  158. },
  159. // 将 meta 拼接到 url 参数中,默认 false
  160. metaWithUrl: false,
  161. // 自定义增加 http header
  162. headers: {
  163. Accept: 'application/json, text/plain, */*',
  164. // ContentType: 'application/json;charset=UTF-8'
  165. Satoken: userStore.getToken,
  166. Tenantid: userStore.getTenantId,
  167. },
  168. // 跨域是否传递 cookie ,默认为 false
  169. withCredentials: true,
  170. // 超时时间,默认为 10 秒
  171. timeout: 10 * 1000, // 5 秒
  172. // 上传之前触发
  173. onBeforeUpload(file) {
  174. console.log('file: ', file);
  175. return file;
  176. // 可以 return
  177. // 1. return file 或者 new 一个 file ,接下来将上传
  178. // 2. return false ,不上传这个 file
  179. },
  180. // 上传进度的回调函数
  181. onProgress(progress: number) {
  182. // progress 是 0-100 的数字
  183. console.log('progress', progress);
  184. },
  185. // 单个文件上传成功之后
  186. onSuccess(file: File, res: any) {
  187. console.log(`${file.name} 上传成功`, res);
  188. },
  189. // 单个文件上传失败
  190. onFailed(file: File, res: any) {
  191. console.log(`${file.name} 上传失败`, res);
  192. },
  193. // 上传错误,或者触发 timeout 超时
  194. onError(file: File, err: any, res: any) {
  195. console.log(`${file.name} 上传出错`, err, res);
  196. },
  197. };
  198. // 执行 createEditor 组件销毁时,也及时销毁编辑器
  199. onBeforeUnmount(() => {
  200. const editor = editorRef.value;
  201. if (editor == null) return;
  202. editor.destroy();
  203. });
  204. const handleCreated = (editor) => {
  205. editorRef.value = editor;
  206. };
  207. const handleChange = (editor) => {
  208. // console.log('change:', editor.children)
  209. };
  210. const handleDestroyed = (editor) => {
  211. console.log('destroyed', editor);
  212. };
  213. const handleFocus = (editor) => {
  214. // console.log('focus', editor)
  215. };
  216. const handleBlur = (editor) => {
  217. console.log('blur', editor);
  218. };
  219. const buildFormdata = () => {
  220. return {
  221. ...ruleForm.value,
  222. content: valueHtml.value,
  223. };
  224. };
  225. defineExpose({ buildFormdata });
  226. </script>
  227. <style scoped lang="scss"></style>