index.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. <template>
  2. <doc-alert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
  3. <!-- 搜索工作栏 -->
  4. <ContentWrap>
  5. <el-form
  6. class="-mb-15px"
  7. :model="queryParams"
  8. ref="queryFormRef"
  9. :inline="true"
  10. label-width="68px"
  11. >
  12. <el-form-item label="公众号" prop="accountId">
  13. <el-select v-model="queryParams.accountId" placeholder="请选择公众号">
  14. <el-option
  15. v-for="item in accountList"
  16. :key="item.id"
  17. :label="item.name"
  18. :value="item.id"
  19. />
  20. </el-select>
  21. </el-form-item>
  22. <el-form-item>
  23. <el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
  24. <el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
  25. <el-button type="primary" plain @click="handleAdd" v-hasPermi="['mp:draft:create']">
  26. <Icon icon="ep:plus" />新增
  27. </el-button>
  28. </el-form-item>
  29. </el-form>
  30. </ContentWrap>
  31. <!-- 列表 -->
  32. <ContentWrap>
  33. <div class="waterfall" v-loading="loading">
  34. <template v-for="item in list" :key="item.articleId">
  35. <div class="waterfall-item" v-if="item.content && item.content.newsItem">
  36. <wx-news :articles="item.content.newsItem" />
  37. <!-- 操作按钮 -->
  38. <el-row class="ope-row">
  39. <el-button
  40. type="success"
  41. circle
  42. @click="handlePublish(item)"
  43. v-hasPermi="['mp:free-publish:submit']"
  44. >
  45. <Icon icon="fa:upload" />
  46. </el-button>
  47. <el-button
  48. type="primary"
  49. circle
  50. @click="handleUpdate(item)"
  51. v-hasPermi="['mp:draft:update']"
  52. >
  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. >
  61. <Icon icon="ep:delete" />
  62. </el-button>
  63. </el-row>
  64. </div>
  65. </template>
  66. </div>
  67. <!-- 分页记录 -->
  68. <Pagination
  69. :total="total"
  70. v-model:page="queryParams.pageNo"
  71. v-model:limit="queryParams.pageSize"
  72. @pagination="getList"
  73. />
  74. </ContentWrap>
  75. <!-- TODO @Dhb52:迁移成独立路由 -->
  76. <div class="app-container">
  77. <!-- 添加或修改草稿对话框 -->
  78. <Teleport to="body">
  79. <el-dialog
  80. :title="operateMaterial === 'add' ? '新建图文' : '修改图文'"
  81. width="80%"
  82. top="20px"
  83. v-model="dialogNewsVisible"
  84. :before-close="dialogNewsClose"
  85. :close-on-click-modal="false"
  86. >
  87. <div class="left">
  88. <div class="select-item">
  89. <div v-for="(news, index) in articlesAdd" :key="news.id">
  90. <div
  91. class="news-main father"
  92. v-if="index === 0"
  93. :class="{ activeAddNews: isActiveAddNews === index }"
  94. @click="activeNews(index)"
  95. >
  96. <div class="news-content">
  97. <img class="material-img" v-if="news.thumbUrl" :src="news.thumbUrl" />
  98. <div class="news-content-title">{{ news.title }}</div>
  99. </div>
  100. <div class="child" v-if="articlesAdd.length > 1">
  101. <el-button size="small" @click="downNews(index)"
  102. ><Icon icon="ep:sort-down" />下移</el-button
  103. >
  104. <el-button v-if="operateMaterial === 'add'" size="small" @click="minusNews(index)"
  105. ><Icon icon="ep:delete" />删除
  106. </el-button>
  107. </div>
  108. </div>
  109. <div
  110. class="news-main-item father"
  111. v-if="index > 0"
  112. :class="{ activeAddNews: isActiveAddNews === index }"
  113. @click="activeNews(index)"
  114. >
  115. <div class="news-content-item">
  116. <div class="news-content-item-title">{{ news.title }}</div>
  117. <div class="news-content-item-img">
  118. <img
  119. class="material-img"
  120. v-if="news.thumbUrl"
  121. :src="news.thumbUrl"
  122. height="100%"
  123. />
  124. </div>
  125. </div>
  126. <div class="child">
  127. <el-button
  128. v-if="articlesAdd.length > index + 1"
  129. size="small"
  130. @click="downNews(index)"
  131. ><Icon icon="ep:sort-down" />下移
  132. </el-button>
  133. <el-button size="small" @click="upNews(index)"
  134. ><Icon icon="ep:sort-up" />上移</el-button
  135. >
  136. <el-button
  137. v-if="operateMaterial === 'add'"
  138. type="danger"
  139. size="small"
  140. @click="minusNews(index)"
  141. ><Icon icon="ep:delete" />删除
  142. </el-button>
  143. </div>
  144. </div>
  145. </div>
  146. <el-row justify="center" class="ope-row">
  147. <el-button
  148. type="primary"
  149. circle
  150. @click="plusNews(item)"
  151. v-if="articlesAdd.length < 8 && operateMaterial === 'add'"
  152. >
  153. <Icon icon="ep:plus" />
  154. </el-button>
  155. </el-row>
  156. </div>
  157. </div>
  158. <div class="right" v-loading="addMaterialLoading" v-if="articlesAdd.length > 0">
  159. <br />
  160. <br />
  161. <br />
  162. <br />
  163. <!-- 标题、作者、原文地址 -->
  164. <el-input v-model="articlesAdd[isActiveAddNews].title" placeholder="请输入标题(必填)" />
  165. <el-input
  166. v-model="articlesAdd[isActiveAddNews].author"
  167. placeholder="请输入作者"
  168. style="margin-top: 5px"
  169. />
  170. <el-input
  171. v-model="articlesAdd[isActiveAddNews].contentSourceUrl"
  172. placeholder="请输入原文地址"
  173. style="margin-top: 5px"
  174. />
  175. <!-- 封面和摘要 -->
  176. <div class="input-tt">封面和摘要:</div>
  177. <div>
  178. <div class="thumb-div">
  179. <img
  180. class="material-img"
  181. v-if="articlesAdd[isActiveAddNews].thumbUrl"
  182. :src="articlesAdd[isActiveAddNews].thumbUrl"
  183. :class="isActiveAddNews === 0 ? 'avatar' : 'avatar1'"
  184. />
  185. <Icon
  186. v-else
  187. icon="ep:plus"
  188. class="avatar-uploader-icon"
  189. :class="isActiveAddNews === 0 ? 'avatar' : 'avatar1'"
  190. />
  191. <div class="thumb-but">
  192. <el-upload
  193. :action="actionUrl"
  194. :headers="headers"
  195. multiple
  196. :limit="1"
  197. :file-list="fileList"
  198. :data="uploadData"
  199. :before-upload="beforeThumbImageUpload"
  200. :on-success="handleUploadSuccess"
  201. >
  202. <template #trigger>
  203. <el-button size="small" type="primary">本地上传</el-button>
  204. </template>
  205. <el-button
  206. size="small"
  207. type="primary"
  208. @click="openMaterial"
  209. style="margin-left: 5px"
  210. >素材库选择</el-button
  211. >
  212. <template #tip>
  213. <div class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div>
  214. </template>
  215. </el-upload>
  216. </div>
  217. <Teleport to="body">
  218. <el-dialog title="选择图片" v-model="dialogImageVisible" width="80%">
  219. <WxMaterialSelect
  220. ref="materialSelectRef"
  221. :objData="{ type: 'image', accountId: queryParams.accountId }"
  222. @select-material="selectMaterial"
  223. />
  224. </el-dialog>
  225. </Teleport>
  226. </div>
  227. <el-input
  228. :rows="8"
  229. type="textarea"
  230. v-model="articlesAdd[isActiveAddNews].digest"
  231. placeholder="请输入摘要"
  232. class="digest"
  233. maxlength="120"
  234. style="float: right"
  235. />
  236. </div>
  237. <!--富文本编辑器组件-->
  238. <el-row>
  239. <wx-editor
  240. v-model="articlesAdd[isActiveAddNews].content"
  241. :account-id="uploadData.accountId"
  242. v-if="hackResetEditor"
  243. />
  244. </el-row>
  245. </div>
  246. <template #footer>
  247. <div class="dialog-footer">
  248. <el-button @click="dialogNewsVisible = false">取 消</el-button>
  249. <el-button type="primary" @click="submitForm">提 交</el-button>
  250. </div>
  251. </template>
  252. </el-dialog>
  253. </Teleport>
  254. </div>
  255. </template>
  256. <script setup name="MpDraft">
  257. import WxEditor from '@/views/mp/components/wx-editor/WxEditor.vue'
  258. import WxNews from '@/views/mp/components/wx-news/main.vue'
  259. import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue'
  260. import { getAccessToken } from '@/utils/auth'
  261. import { createDraft, deleteDraft, getDraftPage, updateDraft } from '@/api/mp/draft'
  262. import { getSimpleAccountList } from '@/api/mp/account'
  263. import { submitFreePublish } from '@/api/mp/freePublish'
  264. const message = useMessage() // 消息
  265. // 可以用改本地数据模拟,避免API调用超限
  266. // import drafts from './mock'
  267. const loading = ref(true) // 列表的加载中
  268. const total = ref(0) // 列表的总页数
  269. const list = ref([]) // 列表的数据
  270. const queryParams = reactive({
  271. pageNo: 1,
  272. pageSize: 10,
  273. accountId: undefined
  274. })
  275. const queryFormRef = ref() // 搜索的表单
  276. const accountList = ref([]) // 公众号账号列表
  277. // ========== 文件上传 ==========
  278. const materialSelectRef = ref()
  279. const BASE_URL = import.meta.env.VITE_BASE_URL
  280. const actionUrl = ref(BASE_URL + '/admin-api/mp/material/upload-permanent') // 上传永久素材的地址
  281. const headers = ref({ Authorization: 'Bearer ' + getAccessToken() }) // 设置上传的请求头部
  282. const fileList = ref([])
  283. const uploadData = reactive({
  284. type: 'image',
  285. accountId: 1
  286. })
  287. // ========== 草稿新建 or 修改 ==========
  288. const dialogNewsVisible = ref(false)
  289. const addMaterialLoading = ref(false) // 添加草稿的 loading 标识
  290. const articlesAdd = ref([])
  291. const isActiveAddNews = ref(0)
  292. const dialogImageVisible = ref(false)
  293. const operateMaterial = ref('add')
  294. const articlesMediaId = ref('')
  295. const hackResetEditor = ref(false)
  296. /** 初始化 **/
  297. onMounted(async () => {
  298. accountList.value = await getSimpleAccountList()
  299. // 选中第一个
  300. if (accountList.value.length > 0) {
  301. // @ts-ignore
  302. queryParams.accountId = accountList.value[0].id
  303. }
  304. await getList()
  305. })
  306. // ======================== 列表查询 ========================
  307. /** 设置账号编号 */
  308. const setAccountId = (accountId) => {
  309. queryParams.accountId = accountId
  310. uploadData.accountId = accountId
  311. }
  312. /** 查询列表 */
  313. const getList = async () => {
  314. // 如果没有选中公众号账号,则进行提示。
  315. if (!queryParams.accountId) {
  316. message.error('未选中公众号,无法查询草稿箱')
  317. return false
  318. }
  319. loading.value = true
  320. try {
  321. const drafts = await getDraftPage(queryParams)
  322. drafts.list.forEach((item) => {
  323. const newsItem = item.content.newsItem
  324. // 将 thumbUrl 转成 picUrl,保证 wx-news 组件可以预览封面
  325. newsItem.forEach((article) => {
  326. article.picUrl = article.thumbUrl
  327. })
  328. })
  329. list.value = drafts.list
  330. total.value = drafts.total
  331. } finally {
  332. loading.value = false
  333. }
  334. }
  335. /** 搜索按钮操作 */
  336. const handleQuery = () => {
  337. queryParams.pageNo = 1
  338. // 默认选中第一个
  339. if (queryParams.accountId) {
  340. setAccountId(queryParams.accountId)
  341. }
  342. getList()
  343. }
  344. /** 重置按钮操作 */
  345. const resetQuery = () => {
  346. queryFormRef.value.resetFields()
  347. // 默认选中第一个
  348. if (accountList.value.length > 0) {
  349. setAccountId(accountList.value[0].id)
  350. }
  351. handleQuery()
  352. }
  353. // ======================== 新增/修改草稿 ========================
  354. /** 新增按钮操作 */
  355. const handleAdd = () => {
  356. resetEditor()
  357. reset()
  358. // 打开表单,并设置初始化
  359. operateMaterial.value = 'add'
  360. dialogNewsVisible.value = true
  361. }
  362. /** 更新按钮操作 */
  363. const handleUpdate = (item) => {
  364. resetEditor()
  365. reset()
  366. articlesMediaId.value = item.mediaId
  367. articlesAdd.value = JSON.parse(JSON.stringify(item.content.newsItem))
  368. // 打开表单,并设置初始化
  369. operateMaterial.value = 'edit'
  370. dialogNewsVisible.value = true
  371. }
  372. /** 提交按钮 */
  373. const submitForm = () => {
  374. // TODO @Dhb52: 参考别的模块写法,改成 await 方式
  375. addMaterialLoading.value = true
  376. if (operateMaterial.value === 'add') {
  377. createDraft(queryParams.accountId, articlesAdd.value)
  378. .then(() => {
  379. message.notifySuccess('新增成功')
  380. dialogNewsVisible.value = false
  381. getList()
  382. })
  383. .finally(() => {
  384. addMaterialLoading.value = false
  385. })
  386. } else {
  387. updateDraft(queryParams.accountId, articlesMediaId.value, articlesAdd.value)
  388. .then(() => {
  389. message.notifySuccess('更新成功')
  390. dialogNewsVisible.value = false
  391. getList()
  392. })
  393. .finally(() => {
  394. addMaterialLoading.value = false
  395. })
  396. }
  397. }
  398. // 关闭弹窗
  399. const dialogNewsClose = async (done) => {
  400. try {
  401. await message.confirm('修改内容可能还未保存,确定关闭吗?')
  402. reset()
  403. resetEditor()
  404. done()
  405. } catch {}
  406. }
  407. // 表单重置
  408. const reset = () => {
  409. isActiveAddNews.value = 0
  410. articlesAdd.value = [buildEmptyArticle()]
  411. }
  412. // 表单 Editor 重置
  413. const resetEditor = () => {
  414. hackResetEditor.value = false // 销毁组件
  415. nextTick(() => {
  416. hackResetEditor.value = true // 重建组件
  417. })
  418. }
  419. // 将图文向下移动
  420. const downNews = (index) => {
  421. let temp = articlesAdd.value[index]
  422. articlesAdd.value[index] = articlesAdd.value[index + 1]
  423. articlesAdd.value[index + 1] = temp
  424. isActiveAddNews.value = index + 1
  425. }
  426. // 将图文向上移动
  427. const upNews = (index) => {
  428. let temp = articlesAdd.value[index]
  429. articlesAdd.value[index] = articlesAdd.value[index - 1]
  430. articlesAdd.value[index - 1] = temp
  431. isActiveAddNews.value = index - 1
  432. }
  433. // 选中指定 index 的图文
  434. const activeNews = (index) => {
  435. resetEditor()
  436. isActiveAddNews.value = index
  437. }
  438. // 删除指定 index 的图文
  439. const minusNews = async (index) => {
  440. try {
  441. await message.confirm('确定删除该图文吗?')
  442. articlesAdd.value.splice(index, 1)
  443. if (isActiveAddNews.value === index) {
  444. isActiveAddNews.value = 0
  445. }
  446. } catch {}
  447. }
  448. // 添加一个图文
  449. const plusNews = () => {
  450. articlesAdd.value.push(buildEmptyArticle())
  451. isActiveAddNews.value = articlesAdd.value.length - 1
  452. }
  453. // 创建空的 article
  454. const buildEmptyArticle = () => {
  455. return {
  456. title: '',
  457. thumbMediaId: '',
  458. author: '',
  459. digest: '',
  460. showCoverPic: '',
  461. content: '',
  462. contentSourceUrl: '',
  463. needOpenComment: '',
  464. onlyFansCanComment: '',
  465. thumbUrl: ''
  466. }
  467. }
  468. // ======================== 文件上传 ========================
  469. const beforeThumbImageUpload = (file) => {
  470. addMaterialLoading.value = true
  471. const isType =
  472. file.type === 'image/jpeg' ||
  473. file.type === 'image/png' ||
  474. file.type === 'image/gif' ||
  475. file.type === 'image/bmp' ||
  476. file.type === 'image/jpg'
  477. if (!isType) {
  478. message.error('上传图片格式不对!')
  479. addMaterialLoading.value = false
  480. return false
  481. }
  482. const isLt = file.size / 1024 / 1024 < 2
  483. if (!isLt) {
  484. message.error('上传图片大小不能超过 2M!')
  485. addMaterialLoading.value = false
  486. return false
  487. }
  488. // 校验通过
  489. return true
  490. }
  491. const handleUploadSuccess = (response, file, fileList) => {
  492. addMaterialLoading.value = false
  493. if (response.code !== 0) {
  494. message.error('上传出错:' + response.msg)
  495. return false
  496. }
  497. // 重置上传文件的表单
  498. fileList.value = []
  499. // 设置草稿的封面字段
  500. articlesAdd.value[isActiveAddNews.value].thumbMediaId = response.data.mediaId
  501. articlesAdd.value[isActiveAddNews.value].thumbUrl = response.data.url
  502. }
  503. // 选择 or 上传完素材,设置回草稿
  504. const selectMaterial = (item) => {
  505. dialogImageVisible.value = false
  506. articlesAdd.value[isActiveAddNews.value].thumbMediaId = item.mediaId
  507. articlesAdd.value[isActiveAddNews.value].thumbUrl = item.url
  508. }
  509. // 打开素材选择
  510. const openMaterial = () => {
  511. dialogImageVisible.value = true
  512. try {
  513. materialSelectRef.value.queryParams.accountId = queryParams.accountId // 强制设置下 accountId,避免二次查询不对
  514. materialSelectRef.value.handleQuery() // 刷新列表,失败也无所谓
  515. } catch (e) {}
  516. }
  517. // ======================== 草稿箱发布 ========================
  518. const handlePublish = async (item) => {
  519. const accountId = queryParams.accountId
  520. const mediaId = item.mediaId
  521. const content =
  522. '你正在通过发布的方式发表内容。 发布不占用群发次数,一天可多次发布。已发布内容不会推送给用户,也不会展示在公众号主页中。 发布后,你可以前往发表记录获取链接,也可以将发布内容添加到自定义菜单、自动回复、话题和页面模板中。'
  523. try {
  524. await message.confirm(content)
  525. await submitFreePublish(accountId, mediaId)
  526. message.notifySuccess('发布成功')
  527. await getList()
  528. } catch {}
  529. }
  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. message.notifySuccess('删除成功')
  538. await getList()
  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>