Write.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <script setup lang="tsx">
  2. import { Form, FormSchema } from '@/components/Form'
  3. import { useForm } from '@/hooks/web/useForm'
  4. import { PropType, reactive, watch, ref } from 'vue'
  5. import { NewsTableData } from '@/api/manage/types'
  6. import { useValidator } from '@/hooks/web/useValidator'
  7. import { IDomEditor } from '@wangeditor/editor'
  8. import { useStorage } from '@/hooks/web/useStorage'
  9. import { Icon } from '@/components/Icon'
  10. import { uploadFile } from '@/api/common'
  11. const { required } = useValidator()
  12. const { getStorage } = useStorage()
  13. const props = defineProps({
  14. currentRow: {
  15. type: Object as PropType<Nullable<NewsTableData>>,
  16. default: () => null
  17. }
  18. })
  19. const articlePic = ref(props.currentRow?.articlePic)
  20. const { formRegister, formMethods } = useForm()
  21. const { setValues, getFormData, getElFormExpose, setSchema } = formMethods
  22. type InsertFnType = (url: string, alt: string, href: string) => void
  23. const schema = reactive<FormSchema[]>([
  24. {
  25. field: 'articleTitle',
  26. label: '文章标题',
  27. component: 'Input',
  28. formItemProps: {
  29. rules: [required()]
  30. },
  31. colProps: {
  32. span: 24
  33. }
  34. },
  35. {
  36. field: 'introduce',
  37. label: '文章简介',
  38. component: 'Input',
  39. formItemProps: {
  40. rules: [required()]
  41. },
  42. colProps: {
  43. span: 24
  44. },
  45. componentProps: {
  46. type: 'textarea',
  47. placeholder: '请输入'
  48. }
  49. },
  50. {
  51. field: 'categoryId',
  52. label: '文章类目',
  53. component: 'Select',
  54. colProps: {
  55. span: 8
  56. },
  57. componentProps: {
  58. options: [
  59. {
  60. label: '新闻资讯',
  61. value: '0'
  62. },
  63. {
  64. label: '经典案例',
  65. value: '1'
  66. },
  67. {
  68. label: '解决方案',
  69. value: '2'
  70. },
  71. {
  72. label: '技术支持',
  73. value: '3'
  74. }
  75. ]
  76. }
  77. },
  78. {
  79. field: 'weight',
  80. label: '权重',
  81. component: 'InputNumber',
  82. value: 0,
  83. colProps: {
  84. span: 8
  85. },
  86. formItemProps: {
  87. rules: [required()]
  88. }
  89. },
  90. {
  91. field: 'articleViews',
  92. label: '当前浏览量',
  93. component: 'InputNumber',
  94. value: 0,
  95. colProps: {
  96. span: 8
  97. },
  98. formItemProps: {
  99. rules: [required()]
  100. }
  101. },
  102. {
  103. field: 'articlePic',
  104. component: 'Upload',
  105. label: '文章展图',
  106. componentProps: {
  107. httpRequest: (data: any) => {
  108. let file = data.file
  109. let formData = new FormData()
  110. formData.append('file', file)
  111. uploadFile(file.size <= 1024 * 1000 ? formData : file).then((response) => {
  112. console.log(response)
  113. let resUrl = ''
  114. if (file.size <= 1024 * 1000) {
  115. resUrl = response.data.virtualPath
  116. } else {
  117. resUrl = response.url
  118. }
  119. setValues({
  120. articlePic: resUrl
  121. })
  122. articlePic.value = response.data.resUrl
  123. })
  124. // uploadFile(formData).then((response) => {
  125. // setValues({
  126. // articlePic: response.data.virtualPath
  127. // })
  128. // articlePic.value = response.data.virtualPath
  129. // })
  130. },
  131. showFileList: false,
  132. class: 'NewsUploader',
  133. headers: {
  134. token: getStorage('token')
  135. },
  136. onSuccess: (response) => {
  137. setValues({
  138. articlePic: response.data.virtualPath
  139. })
  140. articlePic.value = response.data.virtualPath
  141. },
  142. slots: {
  143. default: () => (
  144. <>
  145. {articlePic.value ? <img src={articlePic.value} class="avatar" /> : null}
  146. {!articlePic.value ? (
  147. <Icon class="avatar-uploader-icon" icon="ep:plus" size={28}></Icon>
  148. ) : null}
  149. </>
  150. )
  151. }
  152. }
  153. },
  154. {
  155. field: 'remark',
  156. label: '备注',
  157. component: 'Input',
  158. colProps: {
  159. span: 24
  160. },
  161. componentProps: {
  162. type: 'textarea',
  163. placeholder: '请输入'
  164. }
  165. },
  166. {
  167. field: 'articleContent',
  168. label: '文章内容',
  169. component: 'Editor',
  170. colProps: {
  171. span: 24
  172. },
  173. componentProps: {
  174. defaultHtml: '',
  175. editorConfig: {
  176. MENU_CONF: {
  177. uploadImage: {
  178. fieldName: 'file',
  179. headers: {
  180. token: getStorage('token')
  181. },
  182. async customUpload(file: File, insertFn: InsertFnType) {
  183. // 定义表单数据
  184. let formData = new FormData()
  185. formData.append('file', file)
  186. // 上传文件
  187. uploadFile(formData).then((res) => {
  188. // 调用 insertFn 方法插入图片
  189. insertFn(res.data.virtualPath, res.data.fileName, '')
  190. })
  191. },
  192. customInsert(res: any, insertFn: InsertFnType) {
  193. // 从 res 中找到 url alt href ,然后插入图片
  194. insertFn(res.data.virtualPath, res.fileName, '')
  195. }
  196. },
  197. uploadVideo: {
  198. fieldName: 'file',
  199. headers: {
  200. token: getStorage('token')
  201. },
  202. async customUpload(file: File, insertFn: InsertFnType) {
  203. let formData = new FormData()
  204. formData.append('file', file)
  205. uploadFile(formData).then((res) => {
  206. // 将视频上传成功后的路径和文件名插入到指定位置
  207. insertFn(res.data.virtualPath, res.data.fileName, '')
  208. })
  209. },
  210. customInsert(res: any, insertFn: InsertFnType) {
  211. // 从 res 中找到 url alt href ,然后插入视频
  212. insertFn(res.data.virtualPath, res.fileName, '')
  213. }
  214. }
  215. }
  216. },
  217. // @ts-ignore
  218. onChange: (edit: IDomEditor) => {
  219. setValues({
  220. articleContent: edit.getHtml()
  221. })
  222. }
  223. }
  224. }
  225. ])
  226. const rules = reactive({})
  227. // 定义一个submit函数,用于提交表单
  228. const submit = async () => {
  229. // 获取表单元素
  230. const elForm = await getElFormExpose()
  231. // 调用表单元素的validate方法,获取表单验证结果
  232. const valid = await elForm?.validate().catch((err) => {
  233. // 如果表单验证失败,输出错误信息
  234. console.log(err)
  235. })
  236. // 如果表单验证成功,获取表单数据
  237. if (valid) {
  238. const formData = await getFormData()
  239. // 返回表单数据
  240. return formData
  241. }
  242. }
  243. watch(
  244. () => props.currentRow,
  245. (currentRow) => {
  246. // 如果当前行为空,则返回
  247. if (!currentRow) return
  248. // 设置值
  249. setValues(currentRow)
  250. // 设置模式
  251. setSchema([
  252. {
  253. field: 'articleContent',
  254. path: 'componentProps.defaultHtml',
  255. value: currentRow.articleContent
  256. }
  257. ])
  258. // 设置图片
  259. articlePic.value = currentRow.articlePic
  260. },
  261. {
  262. deep: true,
  263. immediate: true
  264. }
  265. )
  266. defineExpose({
  267. submit
  268. })
  269. </script>
  270. <template>
  271. <Form :rules="rules" @register="formRegister" :schema="schema" />
  272. </template>
  273. <style scoped>
  274. .NewsUploader .avatar {
  275. width: 178px;
  276. height: 178px;
  277. display: block;
  278. }
  279. </style>
  280. <style lang="less">
  281. /**添加图片上传组件样式 */
  282. .NewsUploader .el-upload {
  283. border: 1px dashed var(--el-border-color);
  284. border-radius: 6px;
  285. cursor: pointer;
  286. position: relative;
  287. overflow: hidden;
  288. transition: var(--el-transition-duration-fast);
  289. img {
  290. width: 178px;
  291. height: 178px;
  292. }
  293. .avatar-uploader-icon {
  294. font-size: 28px;
  295. color: #8c939d;
  296. width: 178px;
  297. height: 178px;
  298. text-align: center;
  299. }
  300. }
  301. </style>