zxl
2025-06-24 d11f94ca0610417a7c6fc634925e85e28972224c
新闻管理
2个文件已添加
582 ■■■■■ 已修改文件
manager/src/api/news.js 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/views/news/index.vue 543 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manager/src/api/news.js
New file
@@ -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",
  })
}
manager/src/views/news/index.vue
New file
@@ -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' } }, '暂无');
            }
            // 否则正常显示日期
            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) {
          // 为每一行添加loading状态
          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>