index.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. <template>
  2. <div class="app-container">
  3. <doc-alert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
  4. <!-- 搜索工作栏 -->
  5. <el-form :model="queryParams" ref="queryFormRef" size="small" :inline="true" label-width="68px">
  6. <el-form-item label="公众号" prop="accountId">
  7. <el-select v-model="queryParams.accountId" placeholder="请选择公众号">
  8. <el-option
  9. v-for="item in accountList"
  10. :key="parseInt(item.id)"
  11. :label="item.name"
  12. :value="parseInt(item.id)"
  13. />
  14. </el-select>
  15. </el-form-item>
  16. <el-form-item>
  17. <el-button type="primary" @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
  18. <el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
  19. </el-form-item>
  20. </el-form>
  21. <!-- 操作工具栏 -->
  22. <el-row :gutter="10" class="mb8">
  23. <el-col :span="1.5">
  24. <el-button
  25. type="primary"
  26. plain
  27. size="small"
  28. @click="handleAdd"
  29. v-hasPermi="['mp:draft:create']"
  30. ><Icon icon="ep:plus" />新增
  31. </el-button>
  32. </el-col>
  33. </el-row>
  34. <!-- 列表 -->
  35. <div class="waterfall" v-loading="loading">
  36. <template v-for="item in list" :key="item.articleId">
  37. <div class="waterfall-item" v-if="item.content && item.content.newsItem">
  38. <wx-news :articles="item.content.newsItem" />
  39. <!-- 操作按钮 -->
  40. <el-row class="ope-row">
  41. <el-button
  42. type="success"
  43. circle
  44. @click="handlePublish(item)"
  45. v-hasPermi="['mp:free-publish:submit']"
  46. ><Icon icon="fa:upload"
  47. /></el-button>
  48. <el-button
  49. type="primary"
  50. circle
  51. @click="handleUpdate(item)"
  52. v-hasPermi="['mp:draft:update']"
  53. ><Icon icon="ep:edit"
  54. /></el-button>
  55. <el-button
  56. type="danger"
  57. circle
  58. @click="handleDelete(item)"
  59. v-hasPermi="['mp:draft:delete']"
  60. ><Icon icon="ep:delete"
  61. /></el-button>
  62. </el-row>
  63. </div>
  64. </template>
  65. </div>
  66. <!-- 分页记录 -->
  67. <pagination
  68. v-show="total > 0"
  69. :total="total"
  70. v-model:page="queryParams.pageNo"
  71. v-model:limit="queryParams.pageSize"
  72. @pagination="getList"
  73. />
  74. <!-- 添加或修改草稿对话框 -->
  75. <Teleport to="body">
  76. <el-dialog
  77. :title="operateMaterial === 'add' ? '新建图文' : '修改图文'"
  78. width="80%"
  79. top="20px"
  80. v-model="dialogNewsVisible"
  81. :before-close="dialogNewsClose"
  82. :close-on-click-modal="false"
  83. >
  84. <div class="left">
  85. <div class="select-item">
  86. <div v-for="(news, index) in articlesAdd" :key="news.id">
  87. <div
  88. class="news-main father"
  89. v-if="index === 0"
  90. :class="{ activeAddNews: isActiveAddNews === index }"
  91. @click="activeNews(index)"
  92. >
  93. <div class="news-content">
  94. <img class="material-img" v-if="news.thumbUrl" :src="news.thumbUrl" />
  95. <div class="news-content-title">{{ news.title }}</div>
  96. </div>
  97. <div class="child" v-if="articlesAdd.length > 1">
  98. <el-button size="small" @click="downNews(index)"
  99. ><Icon icon="ep:sort-down" />下移</el-button
  100. >
  101. <el-button v-if="operateMaterial === 'add'" size="small" @click="minusNews(index)"
  102. ><Icon icon="ep:delete" />删除
  103. </el-button>
  104. </div>
  105. </div>
  106. <div
  107. class="news-main-item father"
  108. v-if="index > 0"
  109. :class="{ activeAddNews: isActiveAddNews === index }"
  110. @click="activeNews(index)"
  111. >
  112. <div class="news-content-item">
  113. <div class="news-content-item-title">{{ news.title }}</div>
  114. <div class="news-content-item-img">
  115. <img
  116. class="material-img"
  117. v-if="news.thumbUrl"
  118. :src="news.thumbUrl"
  119. height="100%"
  120. />
  121. </div>
  122. </div>
  123. <div class="child">
  124. <el-button
  125. v-if="articlesAdd.length > index + 1"
  126. size="small"
  127. @click="downNews(index)"
  128. ><Icon icon="ep:sort-down" />下移
  129. </el-button>
  130. <el-button size="small" @click="upNews(index)"
  131. ><Icon icon="ep:sort-up" />上移</el-button
  132. >
  133. <el-button
  134. v-if="operateMaterial === 'add'"
  135. type="danger"
  136. size="small"
  137. @click="minusNews(index)"
  138. ><Icon icon="ep:delete" />删除
  139. </el-button>
  140. </div>
  141. </div>
  142. </div>
  143. <el-row justify="center" class="ope-row">
  144. <el-button
  145. type="primary"
  146. circle
  147. @click="plusNews(item)"
  148. v-if="articlesAdd.length < 8 && operateMaterial === 'add'"
  149. >
  150. <Icon icon="ep:plus" />
  151. </el-button>
  152. </el-row>
  153. </div>
  154. </div>
  155. <div class="right" v-loading="addMaterialLoading" v-if="articlesAdd.length > 0">
  156. <br />
  157. <br />
  158. <br />
  159. <br />
  160. <!-- 标题、作者、原文地址 -->
  161. <el-input v-model="articlesAdd[isActiveAddNews].title" placeholder="请输入标题(必填)" />
  162. <el-input
  163. v-model="articlesAdd[isActiveAddNews].author"
  164. placeholder="请输入作者"
  165. style="margin-top: 5px"
  166. />
  167. <el-input
  168. v-model="articlesAdd[isActiveAddNews].contentSourceUrl"
  169. placeholder="请输入原文地址"
  170. style="margin-top: 5px"
  171. />
  172. <!-- 封面和摘要 -->
  173. <div class="input-tt">封面和摘要:</div>
  174. <div>
  175. <div class="thumb-div">
  176. <img
  177. class="material-img"
  178. v-if="articlesAdd[isActiveAddNews].thumbUrl"
  179. :src="articlesAdd[isActiveAddNews].thumbUrl"
  180. :class="isActiveAddNews === 0 ? 'avatar' : 'avatar1'"
  181. />
  182. <Icon
  183. v-else
  184. icon="ep:plus"
  185. class="avatar-uploader-icon"
  186. :class="isActiveAddNews === 0 ? 'avatar' : 'avatar1'"
  187. />
  188. <div class="thumb-but">
  189. <el-upload
  190. :action="actionUrl"
  191. :headers="headers"
  192. multiple
  193. :limit="1"
  194. :file-list="fileList"
  195. :data="uploadData"
  196. :before-upload="beforeThumbImageUpload"
  197. :on-success="handleUploadSuccess"
  198. >
  199. <template #trigger>
  200. <el-button size="small" type="primary">本地上传</el-button>
  201. </template>
  202. <el-button
  203. size="small"
  204. type="primary"
  205. @click="openMaterial"
  206. style="margin-left: 5px"
  207. >素材库选择</el-button
  208. >
  209. <template #tip>
  210. <div class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div>
  211. </template>
  212. </el-upload>
  213. </div>
  214. <Teleport to="body">
  215. <el-dialog title="选择图片" v-model="dialogImageVisible" width="80%">
  216. <WxMaterialSelect
  217. ref="materialSelectRef"
  218. :objData="{ type: 'image', accountId: queryParams.accountId }"
  219. @select-material="selectMaterial"
  220. />
  221. </el-dialog>
  222. </Teleport>
  223. </div>
  224. <el-input
  225. :rows="8"
  226. type="textarea"
  227. v-model="articlesAdd[isActiveAddNews].digest"
  228. placeholder="请输入摘要"
  229. class="digest"
  230. maxlength="120"
  231. style="float: right"
  232. />
  233. </div>
  234. <!--富文本编辑器组件-->
  235. <el-row>
  236. <wx-editor
  237. v-model="articlesAdd[isActiveAddNews].content"
  238. :account-id="uploadData.accountId"
  239. v-if="hackResetEditor"
  240. />
  241. </el-row>
  242. </div>
  243. <template #footer>
  244. <div class="dialog-footer">
  245. <el-button @click="dialogNewsVisible = false">取 消</el-button>
  246. <el-button type="primary" @click="submitForm">提 交</el-button>
  247. </div>
  248. </template>
  249. </el-dialog>
  250. </Teleport>
  251. </div>
  252. </template>
  253. <script setup name="MpDraft">
  254. import { ref, onMounted, reactive, nextTick } from 'vue'
  255. import WxEditor from '@/views/mp/components/wx-editor/WxEditor.vue'
  256. import WxNews from '@/views/mp/components/wx-news/main.vue'
  257. import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
  258. import { getAccessToken } from '@/utils/auth'
  259. import { createDraft, deleteDraft, getDraftPage, updateDraft } from '@/api/mp/draft'
  260. import { getSimpleAccountList } from '@/api/mp/account'
  261. import { submitFreePublish } from '@/api/mp/freePublish'
  262. // 可以用改本地数据模拟,避免API调用超限
  263. // import drafts from './mock'
  264. const BASE_URL = import.meta.env.VITE_BASE_URL
  265. const message = useMessage()
  266. const materialSelectRef = ref()
  267. const queryFormRef = ref()
  268. // 遮罩层
  269. const loading = ref(false)
  270. // 显示搜索条件
  271. // 总条数
  272. const total = ref(0)
  273. // 数据列表
  274. const list = ref([])
  275. const queryParams = reactive({
  276. pageNo: 1,
  277. pageSize: 10,
  278. accountId: undefined
  279. })
  280. // ========== 文件上传 ==========
  281. const actionUrl = ref(BASE_URL + '/admin-api/mp/material/upload-permanent') // 上传永久素材的地址
  282. const headers = ref({ Authorization: 'Bearer ' + getAccessToken() }) // 设置上传的请求头部
  283. const fileList = ref([])
  284. const uploadData = reactive({
  285. type: 'image',
  286. accountId: 1
  287. })
  288. // ========== 草稿新建 or 修改 ==========
  289. const dialogNewsVisible = ref(false)
  290. const addMaterialLoading = ref(false) // 添加草稿的 loading 标识
  291. const articlesAdd = ref([])
  292. const isActiveAddNews = ref(0)
  293. const dialogImageVisible = ref(false)
  294. const operateMaterial = ref('add')
  295. const articlesMediaId = ref('')
  296. const hackResetEditor = ref(false)
  297. // 公众号账号列表
  298. const accountList = ref([])
  299. onMounted(async () => {
  300. accountList.value = await getSimpleAccountList()
  301. // 选中第一个
  302. if (accountList.value.length > 0) {
  303. // @ts-ignore
  304. queryParams.accountId = accountList.value[0].id
  305. }
  306. await getList()
  307. })
  308. // ======================== 列表查询 ========================
  309. /** 设置账号编号 */
  310. const setAccountId = (accountId) => {
  311. queryParams.accountId = accountId
  312. uploadData.accountId = accountId
  313. }
  314. /** 查询列表 */
  315. const getList = async () => {
  316. // 如果没有选中公众号账号,则进行提示。
  317. if (!queryParams.accountId) {
  318. message.error('未选中公众号,无法查询草稿箱')
  319. return false
  320. }
  321. loading.value = true
  322. try {
  323. const drafts = await getDraftPage(queryParams)
  324. drafts.list.forEach((item) => {
  325. const newsItem = item.content.newsItem
  326. // 将 thumbUrl 转成 picUrl,保证 wx-news 组件可以预览封面
  327. newsItem.forEach((article) => {
  328. article.picUrl = article.thumbUrl
  329. })
  330. })
  331. list.value = drafts.list
  332. total.value = drafts.total
  333. } finally {
  334. loading.value = false
  335. }
  336. }
  337. /** 搜索按钮操作 */
  338. const handleQuery = () => {
  339. queryParams.pageNo = 1
  340. // 默认选中第一个
  341. if (queryParams.accountId) {
  342. setAccountId(queryParams.accountId)
  343. }
  344. getList()
  345. }
  346. /** 重置按钮操作 */
  347. const resetQuery = () => {
  348. queryFormRef.value.resetFields()
  349. // 默认选中第一个
  350. if (accountList.value.length > 0) {
  351. setAccountId(accountList.value[0].id)
  352. }
  353. handleQuery()
  354. }
  355. // ======================== 新增/修改草稿 ========================
  356. /** 新增按钮操作 */
  357. const handleAdd = () => {
  358. resetEditor()
  359. reset()
  360. // 打开表单,并设置初始化
  361. operateMaterial.value = 'add'
  362. dialogNewsVisible.value = true
  363. }
  364. /** 更新按钮操作 */
  365. const handleUpdate = (item) => {
  366. resetEditor()
  367. reset()
  368. articlesMediaId.value = item.mediaId
  369. articlesAdd.value = JSON.parse(JSON.stringify(item.content.newsItem))
  370. // 打开表单,并设置初始化
  371. operateMaterial.value = 'edit'
  372. dialogNewsVisible.value = true
  373. }
  374. /** 提交按钮 */
  375. const submitForm = () => {
  376. addMaterialLoading.value = true
  377. if (operateMaterial.value === 'add') {
  378. createDraft(queryParams.accountId, articlesAdd.value)
  379. .then(() => {
  380. message.notifySuccess('新增成功')
  381. dialogNewsVisible.value = false
  382. getList()
  383. })
  384. .finally(() => {
  385. addMaterialLoading.value = false
  386. })
  387. } else {
  388. updateDraft(queryParams.accountId, articlesMediaId.value, articlesAdd.value)
  389. .then(() => {
  390. message.notifySuccess('更新成功')
  391. dialogNewsVisible.value = false
  392. getList()
  393. })
  394. .finally(() => {
  395. addMaterialLoading.value = false
  396. })
  397. }
  398. }
  399. // 关闭弹窗
  400. const dialogNewsClose = async (done) => {
  401. try {
  402. await message.confirm('修改内容可能还未保存,确定关闭吗?')
  403. reset()
  404. resetEditor()
  405. done()
  406. } catch {}
  407. }
  408. // 表单重置
  409. const reset = () => {
  410. isActiveAddNews.value = 0
  411. articlesAdd.value = [buildEmptyArticle()]
  412. }
  413. // 表单 Editor 重置
  414. const resetEditor = () => {
  415. hackResetEditor.value = false // 销毁组件
  416. nextTick(() => {
  417. hackResetEditor.value = true // 重建组件
  418. })
  419. }
  420. // 将图文向下移动
  421. const downNews = (index) => {
  422. let temp = articlesAdd.value[index]
  423. articlesAdd.value[index] = articlesAdd.value[index + 1]
  424. articlesAdd.value[index + 1] = temp
  425. isActiveAddNews.value = index + 1
  426. }
  427. // 将图文向上移动
  428. const upNews = (index) => {
  429. let temp = articlesAdd.value[index]
  430. articlesAdd.value[index] = articlesAdd.value[index - 1]
  431. articlesAdd.value[index - 1] = temp
  432. isActiveAddNews.value = index - 1
  433. }
  434. // 选中指定 index 的图文
  435. const activeNews = (index) => {
  436. resetEditor()
  437. isActiveAddNews.value = index
  438. }
  439. // 删除指定 index 的图文
  440. const minusNews = async (index) => {
  441. try {
  442. await message.confirm('确定删除该图文吗?')
  443. articlesAdd.value.splice(index, 1)
  444. if (isActiveAddNews.value === index) {
  445. isActiveAddNews.value = 0
  446. }
  447. } catch {}
  448. }
  449. // 添加一个图文
  450. const plusNews = () => {
  451. articlesAdd.value.push(buildEmptyArticle())
  452. isActiveAddNews.value = articlesAdd.value.length - 1
  453. }
  454. // 创建空的 article
  455. const buildEmptyArticle = () => {
  456. return {
  457. title: '',
  458. thumbMediaId: '',
  459. author: '',
  460. digest: '',
  461. showCoverPic: '',
  462. content: '',
  463. contentSourceUrl: '',
  464. needOpenComment: '',
  465. onlyFansCanComment: '',
  466. thumbUrl: ''
  467. }
  468. }
  469. // ======================== 文件上传 ========================
  470. const beforeThumbImageUpload = (file) => {
  471. addMaterialLoading.value = true
  472. const isType =
  473. file.type === 'image/jpeg' ||
  474. file.type === 'image/png' ||
  475. file.type === 'image/gif' ||
  476. file.type === 'image/bmp' ||
  477. file.type === 'image/jpg'
  478. if (!isType) {
  479. message.error('上传图片格式不对!')
  480. addMaterialLoading.value = false
  481. return false
  482. }
  483. const isLt = file.size / 1024 / 1024 < 2
  484. if (!isLt) {
  485. message.error('上传图片大小不能超过 2M!')
  486. addMaterialLoading.value = false
  487. return false
  488. }
  489. // 校验通过
  490. return true
  491. }
  492. const handleUploadSuccess = (response, file, fileList) => {
  493. addMaterialLoading.value = false
  494. if (response.code !== 0) {
  495. message.error('上传出错:' + response.msg)
  496. return false
  497. }
  498. // 重置上传文件的表单
  499. fileList.value = []
  500. // 设置草稿的封面字段
  501. articlesAdd.value[isActiveAddNews.value].thumbMediaId = response.data.mediaId
  502. articlesAdd.value[isActiveAddNews.value].thumbUrl = response.data.url
  503. }
  504. // 选择 or 上传完素材,设置回草稿
  505. const selectMaterial = (item) => {
  506. dialogImageVisible.value = false
  507. articlesAdd.value[isActiveAddNews.value].thumbMediaId = item.mediaId
  508. articlesAdd.value[isActiveAddNews.value].thumbUrl = item.url
  509. }
  510. // 打开素材选择
  511. const openMaterial = () => {
  512. dialogImageVisible.value = true
  513. try {
  514. materialSelectRef.value.queryParams.accountId = queryParams.accountId // 强制设置下 accountId,避免二次查询不对
  515. materialSelectRef.value.handleQuery() // 刷新列表,失败也无所谓
  516. } catch (e) {}
  517. }
  518. // ======================== 草稿箱发布 ========================
  519. const handlePublish = async (item) => {
  520. const accountId = queryParams.accountId
  521. const mediaId = item.mediaId
  522. const content =
  523. '你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。已发布内容不会推送给用户,也不会展示在公众号主页中。 发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。'
  524. try {
  525. await message.confirm(content)
  526. await submitFreePublish(accountId, mediaId)
  527. getList()
  528. message.notifySuccess('发布成功')
  529. } catch {}
  530. }
  531. const handleDelete = async (item) => {
  532. const accountId = queryParams.accountId
  533. const mediaId = item.mediaId
  534. try {
  535. await message.confirm('此操作将永久删除该草稿, 是否继续?')
  536. await deleteDraft(accountId, mediaId)
  537. getList()
  538. message.notifySuccess('删除成功')
  539. } catch {}
  540. }
  541. </script>
  542. <style lang="scss" scoped>
  543. .pagination {
  544. float: right;
  545. margin-right: 25px;
  546. }
  547. .add_but {
  548. padding: 10px;
  549. }
  550. .ope-row {
  551. margin-top: 5px;
  552. text-align: center;
  553. border-top: 1px solid #eaeaea;
  554. padding-top: 5px;
  555. }
  556. .item-name {
  557. font-size: 12px;
  558. overflow: hidden;
  559. text-overflow: ellipsis;
  560. white-space: nowrap;
  561. text-align: center;
  562. }
  563. .el-upload__tip {
  564. margin-left: 5px;
  565. }
  566. /*新增图文*/
  567. .left {
  568. display: inline-block;
  569. width: 35%;
  570. vertical-align: top;
  571. margin-top: 200px;
  572. }
  573. .right {
  574. display: inline-block;
  575. width: 60%;
  576. margin-top: -40px;
  577. }
  578. .avatar-uploader {
  579. width: 20%;
  580. display: inline-block;
  581. }
  582. .avatar-uploader .el-upload {
  583. border-radius: 6px;
  584. cursor: pointer;
  585. position: relative;
  586. overflow: hidden;
  587. text-align: unset !important;
  588. }
  589. .avatar-uploader .el-upload:hover {
  590. border-color: #165dff;
  591. }
  592. .avatar-uploader-icon {
  593. border: 1px solid #d9d9d9;
  594. font-size: 28px;
  595. color: #8c939d;
  596. width: 120px;
  597. height: 120px;
  598. line-height: 120px;
  599. text-align: center;
  600. }
  601. .avatar {
  602. width: 230px;
  603. height: 120px;
  604. }
  605. .avatar1 {
  606. width: 120px;
  607. height: 120px;
  608. }
  609. .digest {
  610. width: 60%;
  611. display: inline-block;
  612. vertical-align: top;
  613. }
  614. /*新增图文*/
  615. /*瀑布流样式*/
  616. .waterfall {
  617. width: 100%;
  618. column-gap: 10px;
  619. column-count: 5;
  620. margin: 0 auto;
  621. }
  622. .waterfall-item {
  623. padding: 10px;
  624. margin-bottom: 10px;
  625. break-inside: avoid;
  626. border: 1px solid #eaeaea;
  627. }
  628. p {
  629. line-height: 30px;
  630. }
  631. @media (min-width: 992px) and (max-width: 1300px) {
  632. .waterfall {
  633. column-count: 3;
  634. }
  635. p {
  636. color: red;
  637. }
  638. }
  639. @media (min-width: 768px) and (max-width: 991px) {
  640. .waterfall {
  641. column-count: 2;
  642. }
  643. p {
  644. color: orange;
  645. }
  646. }
  647. @media (max-width: 767px) {
  648. .waterfall {
  649. column-count: 1;
  650. }
  651. }
  652. /*瀑布流样式*/
  653. .news-main {
  654. background-color: #ffffff;
  655. width: 100%;
  656. margin: auto;
  657. height: 120px;
  658. }
  659. .news-content {
  660. background-color: #acadae;
  661. width: 100%;
  662. height: 120px;
  663. position: relative;
  664. }
  665. .news-content-title {
  666. display: inline-block;
  667. font-size: 15px;
  668. color: #ffffff;
  669. position: absolute;
  670. left: 0px;
  671. bottom: 0px;
  672. background-color: black;
  673. width: 98%;
  674. padding: 1%;
  675. opacity: 0.65;
  676. overflow: hidden;
  677. text-overflow: ellipsis;
  678. white-space: nowrap;
  679. height: 25px;
  680. }
  681. .news-main-item {
  682. background-color: #ffffff;
  683. padding: 5px 0px;
  684. border-top: 1px solid #eaeaea;
  685. width: 100%;
  686. margin: auto;
  687. }
  688. .news-content-item {
  689. position: relative;
  690. margin-left: -3px;
  691. }
  692. .news-content-item-title {
  693. display: inline-block;
  694. font-size: 12px;
  695. width: 70%;
  696. }
  697. .news-content-item-img {
  698. display: inline-block;
  699. width: 25%;
  700. background-color: #acadae;
  701. }
  702. .input-tt {
  703. padding: 5px;
  704. }
  705. .activeAddNews {
  706. border: 5px solid #2bb673;
  707. }
  708. .news-main-plus {
  709. width: 280px;
  710. text-align: center;
  711. margin: auto;
  712. height: 50px;
  713. }
  714. .icon-plus {
  715. margin: 10px;
  716. font-size: 25px;
  717. }
  718. .select-item {
  719. width: 60%;
  720. padding: 10px;
  721. margin: 0 auto 10px auto;
  722. border: 1px solid #eaeaea;
  723. }
  724. .father .child {
  725. display: none;
  726. text-align: center;
  727. position: relative;
  728. bottom: 25px;
  729. }
  730. .father:hover .child {
  731. display: block;
  732. }
  733. .thumb-div {
  734. display: inline-block;
  735. width: 30%;
  736. text-align: center;
  737. }
  738. .thumb-but {
  739. margin: 5px;
  740. }
  741. .material-img {
  742. width: 100%;
  743. height: 100%;
  744. }
  745. </style>