NewsForm.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <template>
  2. <el-container>
  3. <el-aside width="40%">
  4. <div class="select-item">
  5. <div v-for="(news, index) in newsList" :key="index">
  6. <div
  7. class="news-main father"
  8. v-if="index === 0"
  9. :class="{ activeAddNews: activeNewsIndex === index }"
  10. @click="activeNewsIndex = index"
  11. >
  12. <div class="news-content">
  13. <img class="material-img" :src="news.thumbUrl" />
  14. <div class="news-content-title">{{ news.title }}</div>
  15. </div>
  16. <div class="child" v-if="newsList.length > 1">
  17. <el-button type="info" circle size="small" @click="() => moveDownNews(index)">
  18. <Icon icon="ep:arrow-down-bold" />
  19. </el-button>
  20. <el-button
  21. v-if="isCreating"
  22. type="danger"
  23. circle
  24. size="small"
  25. @click="() => removeNews(index)"
  26. >
  27. <Icon icon="ep:delete" />
  28. </el-button>
  29. </div>
  30. </div>
  31. <div
  32. class="news-main-item father"
  33. v-if="index > 0"
  34. :class="{ activeAddNews: activeNewsIndex === index }"
  35. @click="activeNewsIndex = index"
  36. >
  37. <div class="news-content-item">
  38. <div class="news-content-item-title">{{ news.title }}</div>
  39. <div class="news-content-item-img">
  40. <img class="material-img" :src="news.thumbUrl" width="100%" />
  41. </div>
  42. </div>
  43. <div class="child">
  44. <el-button
  45. v-if="newsList.length > index + 1"
  46. circle
  47. type="info"
  48. size="small"
  49. @click="() => moveDownNews(index)"
  50. >
  51. <Icon icon="ep:arrow-down-bold" />
  52. </el-button>
  53. <el-button
  54. v-if="index > 0"
  55. type="info"
  56. circle
  57. size="small"
  58. @click="() => moveUpNews(index)"
  59. >
  60. <Icon icon="ep:arrow-up-bold" />
  61. </el-button>
  62. <el-button
  63. v-if="isCreating"
  64. type="danger"
  65. size="small"
  66. circle
  67. @click="() => removeNews(index)"
  68. >
  69. <Icon icon="ep:delete" />
  70. </el-button>
  71. </div>
  72. </div>
  73. </div>
  74. <el-row justify="center" class="ope-row">
  75. <el-button
  76. type="primary"
  77. circle
  78. @click="plusNews"
  79. v-if="newsList.length < 8 && isCreating"
  80. >
  81. <Icon icon="ep:plus" />
  82. </el-button>
  83. </el-row>
  84. </div>
  85. </el-aside>
  86. <el-main>
  87. <div v-if="newsList.length > 0">
  88. <!-- 标题、作者、原文地址 -->
  89. <el-row :gutter="20">
  90. <el-input v-model="activeNewsItem.title" placeholder="请输入标题(必填)" />
  91. <el-input
  92. v-model="activeNewsItem.author"
  93. placeholder="请输入作者"
  94. style="margin-top: 5px"
  95. />
  96. <el-input
  97. v-model="activeNewsItem.contentSourceUrl"
  98. placeholder="请输入原文地址"
  99. style="margin-top: 5px"
  100. />
  101. </el-row>
  102. <!-- 封面和摘要 -->
  103. <el-row :gutter="20">
  104. <el-col :span="12">
  105. <CoverSelect v-model="activeNewsItem" :is-first="activeNewsIndex === 0" />
  106. </el-col>
  107. <el-col :span="12">
  108. <p>摘要:</p>
  109. <el-input
  110. :rows="8"
  111. type="textarea"
  112. v-model="activeNewsItem.digest"
  113. placeholder="请输入摘要"
  114. class="digest"
  115. maxlength="120"
  116. />
  117. </el-col>
  118. </el-row>
  119. <!--富文本编辑器组件-->
  120. <el-row>
  121. <Editor v-model="activeNewsItem.content" :editor-config="editorConfig" />
  122. </el-row>
  123. </div>
  124. </el-main>
  125. </el-container>
  126. </template>
  127. <script setup lang="ts">
  128. import { Editor } from '@/components/Editor'
  129. import { createEditorConfig } from '../editor-config'
  130. import CoverSelect from './CoverSelect.vue'
  131. import { type NewsItem, createEmptyNewsItem } from './types'
  132. const message = useMessage()
  133. const props = defineProps<{
  134. isCreating: boolean
  135. modelValue: NewsItem[] | null
  136. }>()
  137. const accountId = inject<number>('accountId')
  138. // ========== 文件上传 ==========
  139. const UPLOAD_URL = import.meta.env.VITE_BASE_URL + '/admin-api/mp/material/upload-permanent' // 上传永久素材的地址
  140. const editorConfig = createEditorConfig(UPLOAD_URL, accountId)
  141. // v-model=newsList
  142. const emit = defineEmits<{
  143. (e: 'update:modelValue', v: NewsItem[])
  144. }>()
  145. const newsList = computed<NewsItem[]>({
  146. get() {
  147. return props.modelValue === null ? [createEmptyNewsItem()] : props.modelValue
  148. },
  149. set(val) {
  150. emit('update:modelValue', val)
  151. }
  152. })
  153. const activeNewsIndex = ref(0)
  154. const activeNewsItem = computed<NewsItem>(() => newsList.value[activeNewsIndex.value])
  155. // 将图文向下移动
  156. const moveDownNews = (index: number) => {
  157. const temp = newsList.value[index]
  158. newsList.value[index] = newsList.value[index + 1]
  159. newsList.value[index + 1] = temp
  160. activeNewsIndex.value = index + 1
  161. }
  162. // 将图文向上移动
  163. const moveUpNews = (index: number) => {
  164. const temp = newsList.value[index]
  165. newsList.value[index] = newsList.value[index - 1]
  166. newsList.value[index - 1] = temp
  167. activeNewsIndex.value = index - 1
  168. }
  169. // 删除指定 index 的图文
  170. const removeNews = async (index: number) => {
  171. try {
  172. await message.confirm('确定删除该图文吗?')
  173. newsList.value.splice(index, 1)
  174. if (activeNewsIndex.value === index) {
  175. activeNewsIndex.value = 0
  176. }
  177. } catch {}
  178. }
  179. // 添加一个图文
  180. const plusNews = () => {
  181. newsList.value.push(createEmptyNewsItem())
  182. activeNewsIndex.value = newsList.value.length - 1
  183. }
  184. </script>
  185. <style lang="scss" scoped>
  186. .ope-row {
  187. padding-top: 5px;
  188. margin-top: 5px;
  189. text-align: center;
  190. border-top: 1px solid #eaeaea;
  191. }
  192. .el-row {
  193. margin-bottom: 20px;
  194. }
  195. .el-row:last-child {
  196. margin-bottom: 0;
  197. }
  198. .digest {
  199. display: inline-block;
  200. width: 100%;
  201. vertical-align: top;
  202. }
  203. /* 新增图文 */
  204. .news-main {
  205. width: 100%;
  206. height: 120px;
  207. margin: auto;
  208. background-color: #fff;
  209. }
  210. .news-content {
  211. position: relative;
  212. width: 100%;
  213. height: 120px;
  214. background-color: #acadae;
  215. }
  216. .news-content-title {
  217. position: absolute;
  218. bottom: 0;
  219. left: 0;
  220. display: inline-block;
  221. width: 98%;
  222. height: 25px;
  223. padding: 1%;
  224. overflow: hidden;
  225. font-size: 15px;
  226. color: #fff;
  227. text-overflow: ellipsis;
  228. white-space: nowrap;
  229. background-color: black;
  230. opacity: 0.65;
  231. }
  232. .news-main-item {
  233. width: 100%;
  234. padding: 5px 0;
  235. margin: auto;
  236. background-color: #fff;
  237. border-top: 1px solid #eaeaea;
  238. }
  239. .news-content-item {
  240. position: relative;
  241. margin-left: -3px;
  242. }
  243. .news-content-item-title {
  244. display: inline-block;
  245. width: 70%;
  246. font-size: 12px;
  247. }
  248. .news-content-item-img {
  249. display: inline-block;
  250. width: 25%;
  251. background-color: #acadae;
  252. }
  253. .select-item {
  254. width: 60%;
  255. padding: 10px;
  256. margin: 0 auto 10px;
  257. border: 1px solid #eaeaea;
  258. .activeAddNews {
  259. border: 5px solid #2bb673;
  260. }
  261. }
  262. .father .child {
  263. position: relative;
  264. bottom: 25px;
  265. display: none;
  266. text-align: center;
  267. }
  268. .father:hover .child {
  269. display: block;
  270. }
  271. .material-img {
  272. width: 100%;
  273. height: 100%;
  274. }
  275. </style>