You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

386 lines
13 KiB

9 months ago
9 months ago
9 months ago
9 months ago
8 months ago
9 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
8 months ago
9 months ago
9 months ago
8 months ago
9 months ago
8 months ago
9 months ago
8 months ago
9 months ago
9 months ago
8 months ago
9 months ago
8 months ago
9 months ago
9 months ago
9 months ago
8 months ago
9 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
8 months ago
9 months ago
9 months ago
9 months ago
8 months ago
9 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
8 months ago
9 months ago
9 months ago
8 months ago
9 months ago
  1. <template>
  2. <div class="content">
  3. <div class="search">
  4. <a-space>
  5. <a-input v-model:value="searchlist.blogtitle" placeholder="博客标题" />
  6. <a-input v-model:value="searchlist.typename" placeholder="分类名称" />
  7. <a-range-picker :locale="locale" show-time @change="onChange" />
  8. </a-space>
  9. <a-space style="margin-left: 16px;">
  10. <a-button @click="search">查询</a-button>
  11. <a-button type="primary" ghost @click="addModal">新增</a-button>
  12. </a-space>
  13. </div>
  14. <div class="modal">
  15. <a-modal v-model:open="formControl.open" title="Basic Modal" width="100%" wrap-class-name="full-modal"
  16. @ok="onSubmit" ok-text="确认" cancel-text="取消">
  17. <a-form ref="formRef" :model="formState" :rules="rules">
  18. <a-flex gap="large" justify="space-between">
  19. <a-form-item label="博客标题" name="blogtitle">
  20. <a-input v-model:value="formState.blogtitle" placeholder="请输入博客标题" class="items" />
  21. </a-form-item>
  22. <a-form-item label="博客类型" name="typeid">
  23. <a-select v-model:value="formState.typeid" placeholder="请选择博客类型" class="items">
  24. <a-select-option v-for="typeItem in typelist" :key="typeItem.key"
  25. :value="typeItem.id">{{
  26. typeItem.typename }}</a-select-option>
  27. </a-select>
  28. </a-form-item>
  29. <a-form-item name="labelname" label="标签名称" class="items">
  30. <a-select v-model:value="formState.labelnames" mode="multiple" placeholder="请选择标签">
  31. <a-select-option :value="label.id" v-for="label in labellist">{{ label.labelname
  32. }}</a-select-option>
  33. </a-select>
  34. </a-form-item>
  35. <a-form-item label="文图地址" name="imglink" class="items">
  36. <a-input v-model:value="formState.imglink" placeholder="请输入文图地址" />
  37. </a-form-item>
  38. </a-flex>
  39. <v-md-editor v-model="formState.blogcontent" height="800px"
  40. left-toolbar="undo redo clear | h bold italic strikethrough quote | ul ol table hr | link image code | save | tip | emoji"
  41. ></v-md-editor>
  42. <a-form-item label="博客备注" name="descr" class="desrpad">
  43. <a-textarea v-model:value="formState.descr" />
  44. </a-form-item>
  45. </a-form>
  46. </a-modal>
  47. </div>
  48. <div class="table">
  49. <a-table bordered :data-source="bloglist" :columns="columns">
  50. <template #bodyCell="{ column, record }">
  51. <template v-if="column.key === 'labels'">
  52. <span>
  53. <a-tag v-for="label in labelfilter(record.labelnames)" :key="label" color="cyan">
  54. {{ label }}
  55. </a-tag>
  56. </span>
  57. </template>
  58. <template v-if="column.dataIndex === 'operation'">
  59. <a-space>
  60. <div>
  61. <a-button size="small" danger @click="delModal(record.id)">删除</a-button>
  62. <a-modal v-model:open="open" title="提示" @ok="ackDelete(record)" ok-text="确认"
  63. cancel-text="取消" @cancel="unDelete">
  64. <p>确认删除吗</p>
  65. </a-modal>
  66. </div>
  67. <a-button size="small" type="primary" ghost @click="editModal(record.id)">{{ record.id
  68. }}编辑</a-button>
  69. <a-button size="small" type="primary" ghost @click="ackWatch(record)">预览</a-button>
  70. </a-space>
  71. </template>
  72. </template>
  73. </a-table>
  74. </div>
  75. </div>
  76. </template>
  77. <script setup lang='ts'>
  78. import { ref, reactive, onMounted, toRefs } from 'vue';
  79. import 'dayjs/locale/zh-cn';
  80. import locale from 'ant-design-vue/es/date-picker/locale/zh_CN';
  81. import dayjs from 'dayjs';
  82. import type { Rule } from 'ant-design-vue/es/form';
  83. import { useRouter } from "vue-router"
  84. import { message } from 'ant-design-vue';
  85. import type { blogInterface } from "@/api/admin/index"
  86. import { get, post, put } from "@/tools/request"
  87. import { blogStore } from '@/stores';
  88. import type { labelInterface, typeInterface } from "@/api/admin/index"
  89. dayjs.locale('zh-cn');
  90. const router = useRouter()
  91. const { delControl } = blogStore()
  92. // 查询
  93. const searchlist = reactive({
  94. blogtitle: "",
  95. typename: "",
  96. start_date: '',
  97. end_date: ''
  98. })
  99. const onChange = (date: string, dateString: string[]) => {
  100. if (date && dateString.length === 2) {
  101. searchlist.start_date = dateString[0];
  102. searchlist.end_date = dateString[1];
  103. } else {
  104. searchlist.start_date = '';
  105. searchlist.end_date = '';
  106. }
  107. console.log(dateString[0]);
  108. };
  109. const search = async () => {
  110. try {
  111. const response = await get("/blogs/list/search", {
  112. blogtitle: searchlist.blogtitle,
  113. typename: searchlist.typename,
  114. start_date: searchlist.start_date,
  115. end_date: searchlist.end_date
  116. });
  117. if (response && response.data) {
  118. bloglist.value = response.data.data.map((items: any, index: any) => ({
  119. key: (index + 1).toString(),
  120. blogtitle: items.blogtitle,
  121. typename: items.typename,
  122. blogcontent: items.blogcontent,
  123. wordcount: items.wordcount,
  124. labelnames: items.labelnames,
  125. create_at: dayjs(items.create_at).format('YYYY-MM-DD HH:mm:ss'),
  126. update_at: dayjs(items.update_at).format('YYYY-MM-DD HH:mm:ss'),
  127. descr: items.descr
  128. }));
  129. console.log(response)
  130. } else {
  131. console.log("request has no data");
  132. }
  133. } catch (error) {
  134. console.error("request is exception", error);
  135. }
  136. };
  137. const formState = ref({
  138. id: '',
  139. blogtitle: '',
  140. typeid: null,
  141. blogcontent: "",
  142. labelnames: [],
  143. descr: '',
  144. imglink: ""
  145. });
  146. const labellist = ref<labelInterface[]>([])
  147. const labelList = async () => {
  148. try {
  149. await get(
  150. "labels/list"
  151. ).then(response => {
  152. if (response) {
  153. labellist.value = response.data.data.map((items: any) => ({
  154. id: items.id,
  155. labelname: items.labelname,
  156. descr: items.descr
  157. }))
  158. } else {
  159. console.log("the interface request data does not exist!")
  160. }
  161. })
  162. } catch (error) {
  163. console.error("Failed to fetch data", error);
  164. }
  165. }
  166. const typelist = ref<typeInterface[]>([])
  167. const typeList = async () => {
  168. try {
  169. await get("/types/list").then(response => {
  170. if (response) {
  171. typelist.value = response.data.data.map((items: any) => ({
  172. ...items
  173. }))
  174. } else {
  175. console.log("the interface request data does not exist!")
  176. }
  177. })
  178. } catch (error) {
  179. console.error("Failed to fetch data", error);
  180. }
  181. }
  182. const rules: Record<string, Rule[]> = {
  183. blogtitle: [
  184. { required: true, message: '请输入博客标题', trigger: 'change' },
  185. { min: 4, max: 20, message: '博客标题允许4-20个字符', trigger: 'blur' },
  186. ],
  187. descr: [
  188. { required: false },
  189. { max: 255, message: '博客备注不得超过255个字符', trigger: 'change' },
  190. ],
  191. };
  192. const formControl=ref({
  193. open:false,
  194. ids:null
  195. })
  196. const formRef = ref();
  197. const addModal = () => {
  198. formControl.value.open = true
  199. console.log(formControl.value.ids)
  200. }
  201. const editModal = (id: any) => {
  202. formControl.value.ids = id
  203. formControl.value.open = true
  204. get(
  205. `/blogs/list/search/${id}`
  206. ).then(response=>{
  207. formState.value=response.data.data
  208. })
  209. console.log(formControl.value.ids)
  210. }
  211. const onSubmit = () => {
  212. formRef.value
  213. .validate()
  214. .then(async () => {
  215. const labels = formState.value.labelnames.map(labelId => {
  216. const label = labellist.value.find((label: any) => label.id === labelId);
  217. return label ? { "id": label.id, "labelname": label.labelname, "descr": label.descr } : null;
  218. });
  219. const formdata = {
  220. blog: {
  221. blogtitle: formState.value.blogtitle,
  222. typeid: formState.value.typeid,
  223. imglink: formState.value.imglink,
  224. blogcontent: formState.value.blogcontent,
  225. descr: formState.value.descr
  226. },
  227. labels: labels
  228. }
  229. if (formControl.value.ids) {
  230. await put(`/blogs/update/${formControl.value.ids}`, formdata)
  231. formControl.value.open=false,
  232. blogList()
  233. } else {
  234. await post('/blogs/add', formdata);
  235. formControl.value.open=false,
  236. blogList()
  237. }
  238. })
  239. };
  240. const open = ref<boolean>(false);
  241. const delModal = (id: any) => {
  242. delControl.open = true;
  243. delControl.ids = id;
  244. };
  245. const bloglist = ref<blogInterface[]>([])
  246. const blogList = async () => {
  247. try {
  248. const response = await get("/blogs/list");
  249. if (response) {
  250. bloglist.value = response.data.data.map((items: any, index: any) => ({
  251. key: (index + 1).toString(),
  252. id: items.id,
  253. blogtitle: items.blogtitle,
  254. create_at: dayjs(items.create_at).format('YYYY-MM-DD HH:mm:ss'),
  255. update_at: dayjs(items.update_at).format('YYYY-MM-DD HH:mm:ss'),
  256. readnum: items.readnum,
  257. readminite: items.readminite,
  258. wordcount: items.blogcontent.length,
  259. img: items.img,
  260. blogcontent: items.blogcontent,
  261. typename: items.typename,
  262. labelnames: items.labelnames
  263. }))
  264. } else {
  265. console.log("bloglist is not exits")
  266. }
  267. } catch (error) {
  268. console.log("bloglist is error")
  269. }
  270. }
  271. const labelfilter = (labelnames: any) => {
  272. let labels = []
  273. if (labelnames) {
  274. try {
  275. labels = JSON.parse(labelnames)
  276. } catch (error) {
  277. console.error('Invalid JSON string:', labelnames);
  278. }
  279. }
  280. return Array.isArray(labels) ? labels.filter(label => label && label.trim() !== '') : [];
  281. }
  282. // 博客删除
  283. // const ackDelete = async (record:{key:string,id:number}) => {
  284. // remove(`/blogs/delete/${toDelete.id}`).then((response) => {
  285. // if (response.status === 200) {
  286. // dataSource.value = dataSource.value.filter(item => item.key !== record.key);
  287. // open.value = false;
  288. // }
  289. // message.info("删除成功")
  290. // }).catch(error => {
  291. // console.error('There was an error deleting the blog!', error);
  292. // })
  293. // };
  294. // 博客修改
  295. const ackUpdate = async (id: any) => {
  296. router.push(`/admin/blogmanage/update/${id}`);
  297. };
  298. const ackWatch = async (record: { key: string, id: number }) => {
  299. router.push(`/admin/blogmanage/watch`);
  300. }
  301. const unDelete = async () => {
  302. message.warn("取消删除")
  303. }
  304. const columns = [
  305. {
  306. title: '序号',
  307. dataIndex: 'key',
  308. width: '5%',
  309. },
  310. {
  311. title: '博客标题',
  312. dataIndex: 'blogtitle',
  313. width: '20%',
  314. },
  315. {
  316. title: '博客分类',
  317. dataIndex: 'typename',
  318. },
  319. {
  320. title: '博客标签',
  321. dataIndex: 'labelnames',
  322. key: "labels"
  323. },
  324. {
  325. title: '总字数',
  326. dataIndex: 'wordcount',
  327. key: "wordcount"
  328. },
  329. {
  330. title: '发布时间',
  331. dataIndex: 'create_at',
  332. },
  333. {
  334. title: '修改时间',
  335. dataIndex: 'update_at',
  336. },
  337. {
  338. title: '操作',
  339. dataIndex: 'operation',
  340. },
  341. ];
  342. onMounted(async () => {
  343. blogList()
  344. typeList()
  345. labelList()
  346. });
  347. </script>
  348. <style scoped>
  349. .content {
  350. padding: 24px 24px 0 24px;
  351. }
  352. .search {
  353. margin: 0 0 24px 0;
  354. }
  355. .vditor {
  356. width: 100%;
  357. border: 1px solid #ccc;
  358. }
  359. .items {
  360. width: calc(75vw/4);
  361. }
  362. .desrpad {
  363. padding: 24px 0 0 0;
  364. }
  365. </style>