manage-notification.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. <template>
  2. <main class="notification">
  3. <div class="section">
  4. <div class="section-item" v-for="(item, index) in groupList" :key="index">
  5. <div class="userInfo">
  6. <img class="img" :src="item.avatar" />
  7. <div class="username">
  8. <div class="users">
  9. <span class="name">{{ item.username }}</span>
  10. <span class="tag">{{ formatJobType(item.clientType) }}</span>
  11. </div>
  12. <div class="userTime">
  13. <span class="time">{{ item.createTime }}</span>
  14. <span class="tag" v-if="item.topFlag">置顶</span>
  15. </div>
  16. </div>
  17. <n-popover
  18. v-if="isAuth"
  19. trigger="click"
  20. ref="popoverRef"
  21. :to="false"
  22. placement="bottom-end"
  23. width="160px"
  24. :show-arrow="false"
  25. class="popoverContainer"
  26. >
  27. <template #trigger>
  28. <i class="iconMorePoint iconMore"></i>
  29. </template>
  30. <div class="p-list">
  31. <div class="p-item" @click="onOperation(item, 'edit', index)">
  32. 编辑公告
  33. </div>
  34. <div class="p-item" @click="onOperation(item, 'top', index)">
  35. 设为置顶
  36. </div>
  37. <div
  38. class="p-item p-red"
  39. @click="onOperation(item, 'delete', index)"
  40. >
  41. 删除公告
  42. </div>
  43. </div>
  44. </n-popover>
  45. </div>
  46. <div class="section-content">
  47. <h2>{{ item.title }}</h2>
  48. <div class="content">
  49. {{ item.content }}
  50. </div>
  51. </div>
  52. </div>
  53. <div class="list-item" v-if="groupList.length < total" @click="onMore">
  54. 查看更多
  55. </div>
  56. </div>
  57. <div class="theEmpty" v-if="!loading && groupList.length <= 0">
  58. <img class="emptyImg" src="../../../assets/nomore.png" />
  59. <p>暂无群公告</p>
  60. <n-button
  61. round
  62. type="info"
  63. class="notificationBtn"
  64. @click="onOperation({}, 'add')"
  65. >发布公告</n-button
  66. >
  67. </div>
  68. <n-tooltip
  69. trigger="hover"
  70. :show-arrow="false"
  71. class="toolsNotifi"
  72. v-if="groupList.length > 0 && !isEdit"
  73. >
  74. <template #trigger>
  75. <div class="iconNotifiAdd" @click="onOperation({}, 'add')"></div>
  76. </template>
  77. 发布公告
  78. </n-tooltip>
  79. <div class="edit-notification" v-if="isEdit">
  80. <div class="input-section">
  81. <div class="input-title">
  82. <i class="iconNotifit1"></i>
  83. <span>公告标题</span>
  84. </div>
  85. <div class="input-content">
  86. <n-input
  87. v-model:value="title"
  88. style="
  89. --n-caret-color: #198cfe;
  90. --n-border-hover: 1px solid #198cfe;
  91. --n-border-focus: 1px solid #198cfe;
  92. --n-loading-color: #198cfe;
  93. --n-box-shadow-focus: 0 0 0 2px rgba(25 140 254, 0.2);
  94. "
  95. type="textarea"
  96. placeholder="请输入公告标题"
  97. show-count
  98. :maxlength="50"
  99. />
  100. </div>
  101. </div>
  102. <div class="input-section">
  103. <div class="input-title">
  104. <i class="iconNotifit2"></i>
  105. <span>公告内容</span>
  106. </div>
  107. <div class="input-content notice-content">
  108. <n-input
  109. v-model:value="input"
  110. style="
  111. --n-caret-color: #198cfe;
  112. --n-border-hover: 1px solid #198cfe;
  113. --n-border-focus: 1px solid #198cfe;
  114. --n-loading-color: #198cfe;
  115. --n-box-shadow-focus: 0 0 0 2px rgba(25 140 254, 0.2);
  116. "
  117. type="textarea"
  118. placeholder="请输入公告内容"
  119. :rows="10"
  120. show-count
  121. :maxlength="200"
  122. />
  123. </div>
  124. </div>
  125. <div class="input-section">
  126. <div class="input-title input-slider">
  127. <span>设置为置顶</span>
  128. <Slider :open="topFlag" />
  129. </div>
  130. <!-- :open="conversation.groupProfile.muteAllMembers" @change="setAllMuteTime" -->
  131. </div>
  132. <div
  133. :class="['submitBtn', !title || !input ? 'disabled' : '']"
  134. @click="onSubmit"
  135. >
  136. 发布
  137. </div>
  138. </div>
  139. </main>
  140. </template>
  141. <script lang="ts">
  142. import {
  143. defineComponent,
  144. watchEffect,
  145. reactive,
  146. toRefs,
  147. onMounted,
  148. ref,
  149. } from "vue";
  150. import {
  151. imGroupNoticePage,
  152. imGroupNoticeSave,
  153. imGroupNoticeUpdate,
  154. imGroupNoticeRemove,
  155. } from "../../../../api";
  156. import Slider from "../../../components/sliderTUI/index.vue";
  157. const ManageNotification = defineComponent({
  158. components: {
  159. Slider,
  160. },
  161. props: {
  162. data: {
  163. type: Object,
  164. default: () => ({}),
  165. },
  166. isAuth: {
  167. type: Boolean,
  168. default: false,
  169. },
  170. },
  171. setup(props: any, ctx: any) {
  172. const data: any = reactive({
  173. groupProfile: {},
  174. total: 0,
  175. id: "",
  176. title: "",
  177. input: "",
  178. topFlag: true, // 是否置顶
  179. loading: false,
  180. groupList: [],
  181. isEdit: false,
  182. page: 1,
  183. rows: 20,
  184. });
  185. const popoverRef: any = ref();
  186. const getNotification = async () => {
  187. data.loading = true;
  188. try {
  189. // 获取群公告
  190. let res = await imGroupNoticePage({
  191. groupId: data.groupProfile.groupID,
  192. page: data.page,
  193. rows: data.rows,
  194. });
  195. const result = res.data.rows || [];
  196. data.total = res.data.total || 0;
  197. if (result.length > 0) {
  198. data.groupList = [...data.groupList, ...result];
  199. }
  200. } catch (e: any) {
  201. //
  202. }
  203. data.loading = false;
  204. };
  205. const onMore = () => {
  206. data.page = data.page + 1;
  207. getNotification();
  208. };
  209. watchEffect(() => {
  210. data.groupProfile = props.data;
  211. // 不使用默认的数据,从我们自己的数据库中选择
  212. // data.input = data.groupProfile.notification;
  213. getNotification();
  214. });
  215. const formatJobType = (jobType: string) => {
  216. const template = {
  217. TEACHER: "音乐老师",
  218. ADMIN: "管理员",
  219. HEADMASTER: "校长",
  220. } as any;
  221. return template[jobType];
  222. };
  223. // 更新群资料
  224. // const updateProfile = async () => {
  225. // if (data.title && data.input) {
  226. // // ctx.emit("update", { key: "notification", value: data.input });
  227. // // data.groupProfile.notification = data.input;
  228. // // data.input = "";
  229. // try {
  230. // const params = {
  231. // clientType: "TEACHER",
  232. // groupId: data.groupProfile.groupID,
  233. // content: data.input,
  234. // title: data.title,
  235. // };
  236. // if (data.groupList.id) {
  237. // await imGroupNoticeUpdate({
  238. // ...params,
  239. // id: data.groupList.id,
  240. // });
  241. // } else {
  242. // await imGroupNoticeSave(params);
  243. // }
  244. // data.groupList.content = data.input;
  245. // ctx.emit("update", { key: "notification", value: data.title });
  246. // data.groupProfile.notification = data.title;
  247. // data.groupList.content = data.input;
  248. // data.groupList.title = data.title;
  249. // data.input = "";
  250. // getNotification();
  251. // } catch {
  252. // //
  253. // }
  254. // }
  255. // data.isEdit = !data.isEdit;
  256. // };
  257. const onOperation = async (item: any, type: string, index?: number) => {
  258. try {
  259. if (type === "edit") {
  260. ctx.emit("changeStatus", "edit");
  261. data.isEdit = true;
  262. data.title = item.title;
  263. data.input = item.content;
  264. data.topFlag = item.topFlag;
  265. data.id = item.id;
  266. } else if (type === "top") {
  267. await imGroupNoticeUpdate({
  268. groupId: data.groupProfile.groupID,
  269. topFlag: true,
  270. id: item.id,
  271. });
  272. data.page = 1;
  273. data.groupList = [];
  274. getNotification();
  275. } else if (type === "delete") {
  276. await imGroupNoticeRemove({
  277. id: item.id,
  278. });
  279. data.page = 1;
  280. data.groupList = [];
  281. getNotification();
  282. } else if (type === "add") {
  283. data.isEdit = true;
  284. data.title = "";
  285. data.input = "";
  286. data.topFlag = "";
  287. data.id = "";
  288. ctx.emit("changeStatus", "add");
  289. }
  290. } catch (e) {
  291. //
  292. }
  293. popoverRef.value[index]?.setShow(false);
  294. };
  295. const onSubmit = async () => {
  296. if (!data.title || !data.input) {
  297. return;
  298. }
  299. try {
  300. //
  301. if (data.id) {
  302. await imGroupNoticeUpdate({
  303. groupId: data.groupProfile.groupID,
  304. topFlag: data.topFlag,
  305. title: data.title,
  306. content: data.input,
  307. id: data.id,
  308. });
  309. } else {
  310. await imGroupNoticeSave({
  311. groupId: data.groupProfile.groupID,
  312. topFlag: data.topFlag,
  313. title: data.title,
  314. content: data.input,
  315. });
  316. }
  317. data.isEdit = false;
  318. ctx.emit("changeStatus", "submit");
  319. data.page = 1;
  320. data.groupList = [];
  321. getNotification();
  322. } catch {
  323. //
  324. }
  325. };
  326. const onCloseEdit = () => {
  327. data.isEdit = false;
  328. };
  329. ctx.expose({
  330. onCloseEdit,
  331. });
  332. return {
  333. ...toRefs(data),
  334. popoverRef,
  335. formatJobType,
  336. onMore,
  337. // updateProfile,
  338. onOperation,
  339. onSubmit,
  340. };
  341. },
  342. });
  343. export default ManageNotification;
  344. </script>
  345. <style lang="scss" scoped>
  346. @import url("../../../styles/common.scss");
  347. @import url("../../../styles/icon.scss");
  348. .notification {
  349. position: relative;
  350. flex: 1;
  351. // padding: 20px;
  352. display: flex;
  353. flex-direction: column;
  354. background: #f8f9fc;
  355. }
  356. .list-item {
  357. padding: 13px;
  358. align-items: center;
  359. font-size: 14px;
  360. overflow: hidden;
  361. text-align: center;
  362. cursor: pointer;
  363. }
  364. .section-item {
  365. background: #fff;
  366. padding: 20px;
  367. margin-bottom: 8px;
  368. .userInfo {
  369. position: relative;
  370. display: flex;
  371. align-items: center;
  372. border-bottom: 1px solid #f2f2f2;
  373. padding-bottom: 16px;
  374. margin-bottom: 16px;
  375. .img {
  376. width: 45px;
  377. height: 45px;
  378. border-radius: 6px;
  379. margin-right: 10px;
  380. }
  381. .iconMore {
  382. width: 23px;
  383. height: 17px;
  384. position: absolute;
  385. right: 0;
  386. top: 5px;
  387. cursor: pointer;
  388. }
  389. }
  390. .users {
  391. display: flex;
  392. align-items: center;
  393. .name {
  394. font-weight: 600;
  395. font-size: 16px;
  396. color: #333333;
  397. line-height: 22px;
  398. }
  399. .tag {
  400. margin-left: 5px;
  401. font-weight: 400;
  402. font-size: 12px;
  403. line-height: 17px;
  404. color: #2089ff;
  405. background: #e8f4ff;
  406. border-radius: 3px;
  407. border: 1px solid rgba(25, 140, 254, 0.5);
  408. padding: 0 4px;
  409. }
  410. }
  411. .userTime {
  412. .time {
  413. font-size: 13px;
  414. color: #777777;
  415. line-height: 18px;
  416. }
  417. .tag {
  418. font-size: 12px;
  419. color: #f44541;
  420. line-height: 17px;
  421. border-radius: 10px;
  422. border: 1px solid #f44541;
  423. padding: 0 8px;
  424. border-radius: 20px;
  425. margin-left: 6px;
  426. }
  427. }
  428. .section-content {
  429. h2 {
  430. font-weight: 600;
  431. font-size: 16px;
  432. color: #333333;
  433. line-height: 24px;
  434. }
  435. .content {
  436. font-size: 16px;
  437. color: #777777;
  438. line-height: 24px;
  439. word-break: break-all;
  440. }
  441. }
  442. }
  443. .iconNotifiAdd {
  444. position: absolute;
  445. bottom: 24px;
  446. right: 16px;
  447. width: 58px;
  448. height: 58px;
  449. }
  450. .theEmpty {
  451. width: 100%;
  452. display: flex;
  453. justify-content: center;
  454. align-items: center;
  455. flex-direction: column;
  456. flex: 1;
  457. background: #fff;
  458. padding-bottom: 40px;
  459. .emptyImg {
  460. width: 210px;
  461. height: 210px;
  462. }
  463. p {
  464. font-size: 16px;
  465. color: #aaaaaa;
  466. line-height: 22px;
  467. }
  468. }
  469. .notificationBtn {
  470. margin-top: 10px;
  471. }
  472. .edit-notification {
  473. position: absolute;
  474. top: 0;
  475. left: 0;
  476. right: 0;
  477. bottom: 0;
  478. z-index: 9;
  479. background: #fff;
  480. padding: 0 20px;
  481. .input-section {
  482. padding-top: 20px;
  483. }
  484. .input-title {
  485. display: flex;
  486. align-items: center;
  487. font-size: 16px;
  488. color: #333333;
  489. line-height: 22px;
  490. padding-bottom: 10px;
  491. .iconNotifit1,
  492. .iconNotifit2 {
  493. width: 22px;
  494. height: 22px;
  495. margin-right: 6px;
  496. flex-shrink: 0;
  497. }
  498. span {
  499. line-height: 1;
  500. padding-bottom: 4px;
  501. }
  502. }
  503. .input-slider {
  504. display: flex;
  505. align-items: center;
  506. justify-content: space-between;
  507. span {
  508. padding-bottom: 0;
  509. }
  510. .slider-box {
  511. cursor: pointer;
  512. }
  513. }
  514. .n-input {
  515. border-radius: 6px !important;
  516. .n-input-wrapper {
  517. padding-bottom: 18px !important;
  518. }
  519. }
  520. }
  521. .submitBtn {
  522. margin-top: 30px;
  523. background: #198cfe;
  524. border-radius: 8px;
  525. font-weight: 600;
  526. font-size: 18px;
  527. color: #ffffff;
  528. line-height: 47px;
  529. text-align: center;
  530. cursor: pointer;
  531. &.disabled {
  532. opacity: 0.7;
  533. cursor: not-allowed;
  534. }
  535. }
  536. </style>
  537. <style lang="scss">
  538. .notice-content {
  539. .n-input .n-input-wrapper {
  540. padding-bottom: 18px !important;
  541. }
  542. }
  543. .popoverContainer {
  544. background: #ffffff;
  545. box-shadow: 0px 2px 17px 0px rgba(0, 0, 0, 0.08) !important;
  546. border-radius: 12px !important;
  547. padding: 8px !important;
  548. margin-top: 8px !important;
  549. .p-item {
  550. margin-bottom: 8px;
  551. font-size: 16px;
  552. color: #131415;
  553. border-radius: 8px;
  554. padding: 12px 0;
  555. text-align: center;
  556. cursor: pointer;
  557. &:hover {
  558. background: #f3f3f5;
  559. }
  560. &:last-child {
  561. margin-bottom: 0;
  562. }
  563. &.p-red {
  564. color: #f44541;
  565. }
  566. }
  567. }
  568. .toolsNotifi .n-popover__content {
  569. color: #fff !important;
  570. }
  571. .notificationBtn .n-button__content {
  572. color: #ffffff;
  573. }
  574. </style>