hook.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import dayjs from "dayjs";
  2. import { message } from "@/utils/message";
  3. import { addDialog } from "@/components/ReDialog";
  4. import {
  5. getAnnouncementList,
  6. getAnnouncementById,
  7. createAnnouncement,
  8. updateAnnouncement,
  9. deleteAnnouncement,
  10. publishAnnouncement,
  11. offlineAnnouncement,
  12. setAnnouncementTop
  13. } from "@/api/announcement";
  14. import type { PaginationProps } from "@pureadmin/table";
  15. import { deviceDetection } from "@pureadmin/utils";
  16. import { onMounted, reactive, ref, toRaw } from "vue";
  17. import {
  18. ElForm,
  19. ElInput,
  20. ElFormItem,
  21. ElSelect,
  22. ElOption,
  23. ElRadioGroup,
  24. ElRadioButton
  25. } from "element-plus";
  26. import type { SearchFormProps, AnnouncementItemProps } from "./types";
  27. export function useAnnouncement() {
  28. const form = reactive<SearchFormProps>({
  29. title: "",
  30. type: undefined,
  31. status: undefined
  32. });
  33. const loading = ref(true);
  34. const dataList = ref([]);
  35. const pagination = reactive<PaginationProps>({
  36. total: 0,
  37. pageSize: 10,
  38. currentPage: 1,
  39. background: true
  40. });
  41. const typeMap = {
  42. 1: "系统公告",
  43. 2: "活动公告",
  44. 3: "维护公告",
  45. 4: "其他"
  46. };
  47. const statusMap = {
  48. 0: { text: "草稿", type: "info" },
  49. 1: { text: "已发布", type: "success" },
  50. 2: { text: "已下线", type: "warning" }
  51. };
  52. const columns: TableColumnList = [
  53. {
  54. label: "公告ID",
  55. prop: "id",
  56. width: 80
  57. },
  58. {
  59. label: "标题",
  60. prop: "title",
  61. minWidth: 200,
  62. showOverflowTooltip: true
  63. },
  64. {
  65. label: "类型",
  66. prop: "type",
  67. minWidth: 100,
  68. formatter: ({ type }) => typeMap[type] || "未知"
  69. },
  70. {
  71. label: "置顶",
  72. prop: "isTop",
  73. minWidth: 80,
  74. cellRenderer: ({ row }) => (
  75. <el-tag type={row.isTop === 1 ? "danger" : "info"}>
  76. {row.isTop === 1 ? "置顶" : "否"}
  77. </el-tag>
  78. )
  79. },
  80. {
  81. label: "阅读量",
  82. prop: "readCount",
  83. minWidth: 90
  84. },
  85. {
  86. label: "状态",
  87. prop: "status",
  88. minWidth: 100,
  89. cellRenderer: ({ row }) => {
  90. const item = statusMap[row.status] || { text: "未知", type: "info" };
  91. return <el-tag type={item.type}>{item.text}</el-tag>;
  92. }
  93. },
  94. {
  95. label: "发布时间",
  96. prop: "publishTime",
  97. minWidth: 160,
  98. formatter: ({ publishTime }) =>
  99. publishTime ? dayjs(publishTime).format("YYYY-MM-DD HH:mm:ss") : "-"
  100. },
  101. {
  102. label: "操作",
  103. fixed: "right",
  104. width: 280,
  105. slot: "operation"
  106. }
  107. ];
  108. async function onSearch() {
  109. loading.value = true;
  110. try {
  111. const searchParams: any = {
  112. page: pagination.currentPage,
  113. pageSize: pagination.pageSize
  114. };
  115. // 只添加非空的搜索条件
  116. if (form.title) searchParams.title = form.title;
  117. if (form.type !== undefined) searchParams.type = form.type;
  118. if (form.status !== undefined) searchParams.status = form.status;
  119. const { data } = await getAnnouncementList(searchParams);
  120. if (data) {
  121. dataList.value = data.list || [];
  122. pagination.total = data.total || 0;
  123. }
  124. } finally {
  125. loading.value = false;
  126. }
  127. }
  128. function resetForm(formEl) {
  129. if (!formEl) return;
  130. formEl.resetFields();
  131. pagination.currentPage = 1;
  132. onSearch();
  133. }
  134. async function handleDelete(row) {
  135. const { code } = await deleteAnnouncement(row.id);
  136. if (code === 0) {
  137. message("删除成功", { type: "success" });
  138. onSearch();
  139. }
  140. }
  141. async function handlePublish(row) {
  142. const { code } = await publishAnnouncement(row.id);
  143. if (code === 0) {
  144. message("发布成功", { type: "success" });
  145. onSearch();
  146. }
  147. }
  148. async function handleOffline(row) {
  149. const { code } = await offlineAnnouncement(row.id);
  150. if (code === 0) {
  151. message("下线成功", { type: "success" });
  152. onSearch();
  153. }
  154. }
  155. async function handleSetTop(row) {
  156. const newIsTop = row.isTop === 1 ? 0 : 1;
  157. const { code } = await setAnnouncementTop(row.id, newIsTop);
  158. if (code === 0) {
  159. message(newIsTop === 1 ? "置顶成功" : "取消置顶成功", { type: "success" });
  160. onSearch();
  161. }
  162. }
  163. function handleSizeChange(val: number) {
  164. pagination.pageSize = val;
  165. onSearch();
  166. }
  167. function handleCurrentChange(val: number) {
  168. pagination.currentPage = val;
  169. onSearch();
  170. }
  171. function openDialog(title = "新增", row?: AnnouncementItemProps) {
  172. const formInline = reactive({
  173. id: row?.id,
  174. title: row?.title ?? "",
  175. content: row?.content ?? "",
  176. type: row?.type ?? 1,
  177. coverImage: row?.coverImage ?? "",
  178. status: row?.status ?? 0
  179. });
  180. const ruleFormRef = ref();
  181. addDialog({
  182. title: `${title}公告`,
  183. props: {
  184. formInline,
  185. ruleFormRef
  186. },
  187. width: "46%",
  188. draggable: true,
  189. fullscreen: deviceDetection(),
  190. contentRenderer: () => (
  191. <ElForm
  192. ref={ruleFormRef}
  193. model={formInline}
  194. label-width="100px"
  195. >
  196. <ElFormItem
  197. label="公告标题"
  198. prop="title"
  199. rules={[{ required: true, message: "请输入公告标题", trigger: "blur" }]}
  200. >
  201. <ElInput v-model={formInline.title} placeholder="请输入公告标题" clearable />
  202. </ElFormItem>
  203. <ElFormItem
  204. label="公告类型"
  205. prop="type"
  206. rules={[{ required: true, message: "请选择公告类型", trigger: "change" }]}
  207. >
  208. <ElSelect v-model={formInline.type} placeholder="请选择类型" class="w-full">
  209. <ElOption label="系统公告" value={1} />
  210. <ElOption label="活动公告" value={2} />
  211. <ElOption label="维护公告" value={3} />
  212. <ElOption label="其他" value={4} />
  213. </ElSelect>
  214. </ElFormItem>
  215. <ElFormItem
  216. label="公告内容"
  217. prop="content"
  218. rules={[{ required: true, message: "请输入公告内容", trigger: "blur" }]}
  219. >
  220. <ElInput
  221. v-model={formInline.content}
  222. type="textarea"
  223. placeholder="请输入公告内容"
  224. rows={6}
  225. />
  226. </ElFormItem>
  227. <ElFormItem label="封面图片" prop="coverImage">
  228. <ElInput v-model={formInline.coverImage} placeholder="请输入封面图片URL" clearable />
  229. </ElFormItem>
  230. <ElFormItem label="状态" prop="status">
  231. <ElRadioGroup v-model={formInline.status}>
  232. <ElRadioButton value={0}>草稿</ElRadioButton>
  233. <ElRadioButton value={1}>立即发布</ElRadioButton>
  234. </ElRadioGroup>
  235. </ElFormItem>
  236. </ElForm>
  237. ),
  238. beforeSure: async (done, { options }) => {
  239. const formRef = options.props.ruleFormRef;
  240. if (!formRef.value) return;
  241. try {
  242. const valid = await formRef.value.validate().catch(() => false);
  243. if (!valid) return;
  244. const formData = toRaw(options.props.formInline);
  245. if (formData.id) {
  246. const { code } = await updateAnnouncement(formData.id, formData);
  247. if (code === 0) {
  248. message(`修改公告 ${formData.title} 成功`, { type: "success" });
  249. done();
  250. onSearch();
  251. }
  252. } else {
  253. const { code } = await createAnnouncement(formData);
  254. if (code === 0) {
  255. message(`新增公告 ${formData.title} 成功`, { type: "success" });
  256. done();
  257. onSearch();
  258. }
  259. }
  260. } catch (error) {
  261. message("操作失败", { type: "error" });
  262. }
  263. }
  264. });
  265. }
  266. onMounted(() => {
  267. onSearch();
  268. });
  269. return {
  270. form,
  271. loading,
  272. columns,
  273. dataList,
  274. pagination,
  275. typeMap,
  276. statusMap,
  277. onSearch,
  278. resetForm,
  279. openDialog,
  280. handleDelete,
  281. handlePublish,
  282. handleOffline,
  283. handleSetTop,
  284. handleSizeChange,
  285. handleCurrentChange
  286. };
  287. }