add.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <template>
  2. <CommonPage show-footer>
  3. <template #action>
  4. <NButton type="primary" @click="handleAdd">
  5. 保存文章
  6. </NButton>
  7. </template>
  8. <div class="editor-wrap">
  9. <n-form
  10. ref="modalFormRef" class="form wh-full" label-placement="left" label-align="left" :label-width="80"
  11. :model="modalForm"
  12. >
  13. <n-form-item
  14. label="文章名称" path="title" :rule="{
  15. required: true,
  16. message: '请输入文章名称',
  17. trigger: ['input', 'blur'],
  18. }"
  19. >
  20. <n-input v-model:value="modalForm.title" :maxlength="200" show-count />
  21. </n-form-item>
  22. <n-form-item
  23. label="文章分类" path="categoryId" :rule="{
  24. required: true,
  25. type: 'number',
  26. trigger: ['change', 'blur'],
  27. message: '请输入文章分类',
  28. }"
  29. >
  30. <n-tree-select
  31. v-model:value="modalForm.categoryId" :options="allCategory" label-field="title" key-field="id"
  32. placeholder="根分类" clearable style="max-width: 300px;"
  33. />
  34. </n-form-item>
  35. <n-tabs type="line" animated>
  36. <template v-for="(lang, index) in langs" :key="lang">
  37. <n-tab-pane :name="lang" :tab="langLabel[lang]" :index="index">
  38. <n-form-item
  39. label="文章名称" :path="`translations[${index}].title`" :rule="{
  40. required: true,
  41. message: '请输入文章名称',
  42. trigger: ['input', 'blur'],
  43. }"
  44. >
  45. <n-input v-model:value="modalForm.translations[index].title" :maxlength="200" show-count />
  46. </n-form-item>
  47. <div class="h-450">
  48. <VividEditor
  49. v-model="modalForm.translations[index].content" :dark="isDark"
  50. :handle-image-upload="handleUpload" :handle-video-upload="handleVideoUpload"
  51. >
  52. <SlashCommand />
  53. <DragHandle />
  54. <!-- <template #menu>
  55. <ImageExt />
  56. </template> -->
  57. </VividEditor>
  58. </div>
  59. </n-tab-pane>
  60. </template>
  61. </n-tabs>
  62. </n-form>
  63. </div>
  64. </CommonPage>
  65. </template>
  66. <script setup>
  67. import { DragHandle, SlashCommand, VividEditor } from '@4dkankan/vivid'
  68. import { useUserStore } from '@/store/index.js'
  69. import { initTranslations, langLabel, langs } from '@/utils/translations'
  70. import { useDark } from '@vueuse/core'
  71. import { NButton, useThemeVars } from 'naive-ui'
  72. import { ref } from 'vue'
  73. import { useRouter } from 'vue-router'
  74. import categoryApi from '../category/api'
  75. import articleApi from './api'
  76. import '@4dkankan/vivid/dist/style.css'
  77. // const langs = computed(() => String(import.meta.env.VITE_LANGS).split(','))
  78. const isDark = useDark()
  79. const vars = useThemeVars()
  80. const modalFormRef = ref('')
  81. const { userId } = useUserStore()
  82. const router = useRouter()
  83. const modalForm = ref({
  84. title: '',
  85. categoryId: null,
  86. content: '',
  87. userId,
  88. })
  89. initTranslations(modalForm.value, ['title', 'content'])
  90. onMounted(() => {
  91. console.log('VividEditor', VividEditor)
  92. })
  93. const allCategory = ref([])
  94. categoryApi.getAll().then(({ data = [] }) => (allCategory.value = data))
  95. function handleAdd() {
  96. modalFormRef.value?.validate((errors) => {
  97. if (!errors) {
  98. let isPass = true
  99. langs.value.forEach((lang) => {
  100. const trans = modalForm.value.translations.find(i => i.locale === lang)
  101. if (trans.title === '') {
  102. window.$message.error(`请填写文章名称${langLabel[lang]}标题!`)
  103. isPass = false
  104. }
  105. })
  106. if (isPass) {
  107. articleApi.create(modalForm.value)
  108. $message.success('保存成功!')
  109. router.push('/article')
  110. }
  111. }
  112. else {
  113. $message.error('请填写对应项!')
  114. console.log('errors', errors)
  115. }
  116. })
  117. }
  118. function handleUpload(file) {
  119. // eslint-disable-next-line no-async-promise-executor
  120. return new Promise(async (resolve) => {
  121. // console.log('handleUpload', file)
  122. const data = new FormData()
  123. data.append('file', file)
  124. const res = await articleApi.uploadImage(data)
  125. // console.log('res', res)
  126. resolve(res.data)
  127. })
  128. }
  129. function handleVideoUpload(file) {
  130. // eslint-disable-next-line no-async-promise-executor
  131. return new Promise(async (resolve) => {
  132. // console.log('handleUpload', file)
  133. const data = new FormData()
  134. data.append('file', file)
  135. const res = await articleApi.uploadImage(data)
  136. // console.log('res', res)
  137. resolve(res.data)
  138. })
  139. }
  140. </script>
  141. <style>
  142. .editor-wrap {
  143. width: 100%;
  144. height: 100%;
  145. }
  146. </style>