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.

384 lines
13 KiB

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