From d11f94ca0610417a7c6fc634925e85e28972224c Mon Sep 17 00:00:00 2001
From: zxl <763096477@qq.com>
Date: 星期二, 24 六月 2025 14:16:34 +0800
Subject: [PATCH] 新闻管理

---
 manager/src/views/news/index.vue |  543 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 manager/src/api/news.js          |   39 +++
 2 files changed, 582 insertions(+), 0 deletions(-)

diff --git a/manager/src/api/news.js b/manager/src/api/news.js
new file mode 100644
index 0000000..19aca4e
--- /dev/null
+++ b/manager/src/api/news.js
@@ -0,0 +1,39 @@
+import service from "../libs/axios";
+
+export  const getNews = (params) =>{
+  return service({
+    url: "/news/page",
+    method: "GET",
+    params: params
+  })
+}
+
+export  const addNews = (params) =>{
+  return service({
+    url: "/news",
+    method: "POST",
+    data: params
+  })
+}
+
+export  const editNews = (params) =>{
+  return service({
+    url: "/news",
+    method: "PUT",
+    data: params
+  })
+}
+
+export const publish = (param) =>{
+  return service({
+    url: "/news/publish/"+ param,
+    method: "PUT",
+  })
+}
+
+export const delById = (param) =>{
+  return service({
+    url: "/news/"+ param,
+    method: "DELETE",
+  })
+}
diff --git a/manager/src/views/news/index.vue b/manager/src/views/news/index.vue
new file mode 100644
index 0000000..e9d2d95
--- /dev/null
+++ b/manager/src/views/news/index.vue
@@ -0,0 +1,543 @@
+<template>
+  <div class="news-management">
+    <Card>
+      <!-- 鎼滅储琛ㄥ崟 -->
+      <Form
+        ref="searchForm"
+        @keydown.enter.native="handleSearch"
+        :model="searchForm"
+        inline
+        :label-width="80"
+        class="search-form"
+      >
+        <FormItem label="鏍囬" prop="title">
+          <Input
+            type="text"
+            v-model="searchForm.title"
+            placeholder="璇疯緭鍏ユ爣棰樺悕绉�"
+            clearable
+            @on-clear="handleSearch"
+            style="width: 180px"
+          />
+        </FormItem>
+        <FormItem label="鏄惁鍙戝竷" prop="publish">
+          <Select
+            v-model="searchForm.publish"
+            placeholder="璇烽�夋嫨"
+            style="width: 180px"
+            clearable
+            @on-clear="handleSearch"
+            @on-change="handleSearch"
+          >
+            <Option
+              v-for="item in typeSelect"
+              :value="item.value"
+              :key="item.id"
+            >
+              {{ item.label }}
+            </Option>
+          </Select>
+        </FormItem>
+        <Button
+          @click="handleSearch"
+          type="primary"
+          icon="ios-search"
+          class="search-btn"
+        >鎼滅储</Button>
+        <Button
+          @click="resetSearch"
+          icon="md-refresh"
+          style="margin-left: 8px"
+        >閲嶇疆</Button>
+      </Form>
+
+      <!-- 鎿嶄綔鎸夐挳 -->
+      <Row class="operation">
+        <Button @click="openAdd" type="primary" icon="md-add">鏂板娲诲姩</Button>
+        <Button @click="delBatch" type="error" icon="md-trash" :disabled="selectCount === 0">鎵归噺鍒犻櫎</Button>
+      </Row>
+
+      <!-- 娲诲姩琛ㄦ牸 -->
+      <Table
+        :loading="loading"
+        border
+        :columns="columns"
+        :data="newsList"
+        ref="table"
+        @on-selection-change="showSelect"
+        class="news-table"
+      >
+        <!-- 灏侀潰灞曠ず鎻掓Ы -->
+        <!-- 鎿嶄綔鎸夐挳鎻掓Ы -->
+        <template slot-scope="{ row }" slot="action">
+          <div class="action-btns">
+            <Button
+              type="primary"
+              size="small"
+              @click="changeStatus(row, row.publish ? '涓嬫灦' : '鍙戝竷')"
+              :loading="row.statusLoading"
+            >
+              {{ row.publish  ? '涓嬫灦' : '鍙戝竷' }}
+            </Button>
+            <Button
+              type="info"
+              size="small"
+              @click="detail(row)"
+            >璇︽儏</Button>
+            <Button
+              type="info"
+              size="small"
+              @click="openEdit(row)"
+            >缂栬緫</Button>
+            <Button
+              type="error"
+              size="small"
+              @click="delById(row)"
+            >鍒犻櫎</Button>
+          </div>
+        </template>
+      </Table>
+
+      <!-- 鍒嗛〉 -->
+      <Row type="flex" justify="end" class="page-footer">
+        <Page
+          :current="searchForm.pageNumber"
+          :total="total"
+          :page-size="searchForm.pageSize"
+          @on-change="changePage"
+          @on-page-size-change="changePageSize"
+          :page-size-opts="[10, 20, 50]"
+          size="small"
+          show-total
+          show-elevator
+          show-sizer
+        ></Page>
+      </Row>
+
+      <!-- 鏂伴椈缂栬緫/鏂板妯℃�佹 -->
+      <Modal
+        v-model="modelShow"
+        :title="modelTitle"
+        @on-cancel="modelClose"
+        width="800"
+        :mask-closable="false"
+      >
+        <Form ref="form" :model="newsForm" :label-width="100" :rules="rules">
+          <Row :gutter="16">
+            <Col span="12">
+              <FormItem label="鏍囬" prop="title">
+                <Input
+                  v-model="newsForm.title"
+                  placeholder="璇疯緭鍏ユ爣棰樺悕绉�"
+                  clearable
+                />
+              </FormItem>
+            </Col>
+            <Col span="24">
+              <FormItem label="鏂伴椈鍐呭锛�" prop="content">
+                <editor ref="editor" @input="getReason" />
+              </FormItem>
+            </Col>
+          </Row>
+        </Form>
+
+        <div slot="footer">
+          <Button @click="modelClose">鍙栨秷</Button>
+          <Button type="primary" :loading="submitLoading" @click="saveOrUpdate">鎻愪氦</Button>
+        </div>
+      </Modal>
+
+
+      <Modal
+        v-model="infoModelShow"
+        :title="modelTitle"
+        @on-cancel="infoModelClose"
+        width="800"
+        :mask-closable="false"
+      >
+        <div class="detail-container">
+          <Row :gutter="16">
+            <Col span="12">
+              <div class="detail-item">
+                <label>鏍囬锛�</label>
+                <span>{{ newsInfo.title || '-' }}</span>
+              </div>
+            </Col>
+            <Col span="12">
+              <div class="detail-item">
+                <label>鏄惁鍙戝竷锛�</label>
+                <span>{{newsInfo.publish  ? '宸插彂甯�':'鏈彂甯�'}}</span>
+              </div>
+            </Col>
+
+            <Col span="12">
+              <div class="detail-item" v-if="newsInfo.publish">
+                <label>鍙戝竷鏃堕棿锛�</label>
+                <span>{{ newsInfo.publishDate}}</span>
+              </div>
+            </Col>
+            <Col span="24">
+              <div class="detail-item">
+                <label>鏂伴椈鍐呭锛�</label>
+                <div
+                  class="news-content"
+                  v-html="newsInfo.content || '鏃犲唴瀹�'"
+                ></div>
+              </div>
+            </Col>
+          </Row>
+        </div>
+
+        <div slot="footer">
+          <Button @click="infoModelClose">鍏抽棴</Button>
+        </div>
+      </Modal>
+
+
+      <!-- 鍥剧墖棰勮妯℃�佹 -->
+      <Modal v-model="previewVisible" title="鍥剧墖棰勮" footer-hide>
+        <img :src="previewImageUrl" style="width: 100%">
+      </Modal>
+    </Card>
+  </div>
+</template>
+<script>
+import Editor from '@/components/editor/index.vue'
+import { getNews,editNews,addNews,publish,delById } from '@/api/news.js'
+export default {
+  name: "newsManagement",
+  components: {Editor},
+  data(){
+    return{
+      // 鍥剧墖棰勮
+      previewVisible: false,
+      previewImageUrl: '',
+
+      modelShow:false,
+      modelTitle:'',
+      submitLoading:false,
+      infoModelShow:false,
+      //琛ㄥご
+      columns: [
+        {
+          type: 'selection',
+          width: 60,
+          align: 'center'
+        },
+        {
+          title: '鏍囬',
+          key: 'title',
+          minWidth: 120,
+          tooltip: true
+        },
+        {
+          title: '鍙戝竷',
+          key: 'publish',
+          width: 100,
+          align: 'center',
+          render: (h, params) => {
+            return h('Tag', {
+              props: {
+                color: params.row.publish ? 'green' : 'default'
+              }
+            }, params.row.publish ? '宸插彂甯�' : '鏈彂甯�')
+          }
+        },
+        {
+          title: '鍙戝竷鏃堕棿',
+          key: 'publishDate',
+          width: 300,
+          align: 'center',
+          render: (h, { row }) => {
+            // 濡傛灉 publishDate 涓� null 鎴� undefined锛屾樉绀� "鏈彂甯�"
+            if (row.publishDate == null) {
+              return h('span', { style: { color: '#999' } }, '鏆傛棤');
+            }
+            // 鍚﹀垯姝e父鏄剧ず鏃ユ湡
+            return h('span', row.publishDate);
+          },
+        },
+        {
+          title: '鎿嶄綔',
+          slot: 'action',
+          width: 280,
+          align: 'center',
+          fixed: 'right'
+        }
+      ],
+
+      newsList:[],
+      total:0,
+      newsForm:{
+        id:'',
+        title:'',
+        content:'',
+        publish:0,
+      },
+      newsInfo:{
+        title:'',
+        publish:false,
+        publishDate:'',
+        content:''
+      },
+      rules: {
+        title: [
+          { required: true, message: '璇疯緭鍏ユ爣棰�', trigger: 'blur' },
+          { max: 50, message: '闀垮害涓嶈兘瓒呰繃50涓瓧绗�', trigger: 'blur' }
+        ],
+        publish: [
+          { required: true, message: '璇烽�夋嫨鏄惁鍙戝竷', trigger: 'blur' },
+        ],
+        content: [
+          { required: true, message: '璇疯緭鍏ユ柊闂诲唴瀹�', trigger: 'blur' }
+        ]
+      },
+
+      selectList:[],
+      selectCount:0,
+
+      loading:false,
+
+      searchForm: {
+        title: '',
+        publish: 0,
+        pageNumber: 1,
+        pageSize: 10
+      },
+      typeSelect:[
+        {
+          id:1,
+          label:'鏈彂甯�',
+          value:0
+        },
+        {
+          id:2,
+          label:'宸插彂甯�',
+          value:1
+        }
+
+      ]
+    }
+  },
+  mounted() {
+    this.getNewsList();
+  },
+  methods:{
+    getNewsList(){
+      this.loading = true
+      getNews(this.searchForm).then(res =>{
+        this.loading = false
+        if (res.code === 200) {
+          // 涓烘瘡涓�琛屾坊鍔爈oading鐘舵��
+          this.newsList = res.data.map(item => ({
+            ...item,
+          }))
+          this.total = res.total
+        }
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    getReason(content) {
+      this.newsForm.content = content
+    },
+    saveOrUpdate(){
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          this.submitLoading = true
+          const submitData = {
+            ...this.newsForm,
+            publish: this.newsForm.publish !== 0,  // true 鈫� 1, false 鈫� 0
+          };
+          if (this.newsForm.id){
+            editNews(submitData).then(res => {
+              this.submitLoading = false
+              if (res.code === 200) {
+                this.$Message.success(res.msg)
+                this.modelClose()
+                this.getNewsList()
+              }
+            }).catch(() => {
+              this.submitLoading = false
+            })
+          }else {
+            addNews(submitData).then(res => {
+              this.submitLoading = false
+              if (res.code === 200) {
+                this.$Message.success(res.msg)
+                this.modelClose()
+                this.getNewsList()
+              }
+            }).catch(() => {
+              this.submitLoading = false
+            })
+          }
+
+        }
+      })
+    },
+    infoModelClose(){
+      this.infoModelShow = false;
+    },
+    modelClose(){
+      this.modelShow = false;
+    },
+    changePage(page) {
+      this.searchForm.pageNumber = page
+      this.getNewsList()
+    },
+    // 鏀瑰彉姣忛〉鏉℃暟
+    changePageSize(pageSize) {
+      this.searchForm.pageNumber = 1
+      this.searchForm.pageSize = pageSize
+      this.getNewsList()
+    },
+
+    delById(row){
+      delById(row.id).then(res =>{
+        if (res.code === 200){
+          this.$Message.success(res.msg);
+          this.getNewsList();
+        }
+
+      })
+    },
+    openEdit(row){
+      this.modelTitle = '淇敼鏂伴椈';
+      this.modelShow = true;
+      this.$refs.form.resetFields();
+      this.newsForm.title = row.title;
+      this.newsForm.content = row.content;
+      this.newsForm.id = row.id;
+      this.$refs.editor.setContent(this.newsForm.content)
+    },
+    openAdd(){
+      this.modelTitle = '鏂板鏂伴椈';
+      this.modelShow = true;
+      this.$refs.form.resetFields()
+      this.newsForm.id = '';
+
+    },
+    detail(row){
+      this.modelTitle = '娲诲姩璇︽儏'
+      this.infoModelShow = true
+      this.newsInfo = row
+    },
+    changeStatus(row,action){
+      row.statusLoading = true;
+
+      publish(row.id).then(res =>{
+        row.statusLoading = false
+        if (res.code === 200){
+          this.$Message.success(res.msg);
+          this.getNewsList();
+        }
+      }).catch(() => {
+        row.statusLoading = false
+      })
+    },
+    // 琛ㄦ牸閫夋嫨鍙樺寲
+    showSelect(selection) {
+      this.selectList = selection.map(item => item.id)
+      this.selectCount = selection.length
+    },
+
+    delBatch(){
+
+    },
+    handleSearch(type, value){
+      this.searchForm.pageNumber = 1
+      this.getNewsList()
+    },
+    resetSearch(){
+      this.$refs.searchForm.resetFields()
+      this.searchForm.pageNumber = 1
+      this.getNewsList()
+    },
+
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.activity-management {
+  .search-form {
+    padding: 16px;
+    background: #f8f8f9;
+    border-radius: 4px;
+    margin-bottom: 16px;
+
+    .ivu-form-item {
+      margin-bottom: 16px;
+      margin-right: 16px;
+    }
+
+    .search-btn {
+      margin-left: 8px;
+    }
+  }
+}
+.detail-container {
+  padding: 16px;
+}
+
+.detail-item {
+  margin-bottom: 18px;
+  line-height: 1.5;
+
+  label {
+    display: inline-block;
+    width: 100px;
+    color: #666;
+    font-weight: bold;
+    vertical-align: top;
+  }
+
+  span {
+    display: inline-block;
+    width: calc(100% - 110px);
+  }
+}
+
+.news-content {
+  border: 1px solid #dcdee2;
+  border-radius: 4px;
+  padding: 12px;
+  min-height: 100px;
+  margin-top: 8px;
+}
+.news-table {
+  .media-container {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    height: 100px;
+
+    .thumbnail {
+      max-width: 100%;
+      max-height: 100%;
+      object-fit: contain;
+      cursor: pointer;
+      transition: all 0.3s;
+
+      &:hover {
+        transform: scale(1.05);
+        box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
+      }
+    }
+  }
+
+  .action-btns {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: center;
+
+    .ivu-btn {
+      margin: 4px;
+      font-size: 12px;
+      padding: 2px 6px;
+      min-width: 60px;
+    }
+  }
+}
+</style>

--
Gitblit v1.8.0