Kaynağa Gözat

文章管理

Alex 9 ay önce
ebeveyn
işleme
b4203e34de

+ 9 - 1
src/styles/index.scss

@@ -248,6 +248,14 @@ html.disabled-transition :not(.view-transition-trigger) * {
 }
 
 
+.article-dialog {
+    .el-dialog__body {
+        max-height: 80vh;
+        overflow-y: auto;
+    }
+}
+
+
 //基础信息展示样式
 .common-row {
     padding-left: 12px;
@@ -307,4 +315,4 @@ html.disabled-transition :not(.view-transition-trigger) * {
     .el-select-dropdown .el-select-dropdown__item {
         white-space: normal;
     }
-}
+}

+ 222 - 0
src/views/marketing/article/components/ArticleDialog.vue

@@ -0,0 +1,222 @@
+<template>
+  <el-dialog
+    v-model="visible"
+    :title="form.code ? '编辑文章' : '新建文章'"
+    width="1000px"
+    destroy-on-close
+    @closed="handleClose"
+    top="5vh"
+    class="article-dialog"
+  >
+    <el-form :model="form" ref="formRef" label-width="100px" :rules="rules">
+      <el-form-item label="文章标题" prop="title">
+        <el-input v-model="form.title" placeholder="请输入文章标题"></el-input>
+      </el-form-item>
+
+      <div class="sub-article-section">
+        <div class="sub-article-header">
+          <h3>副文章列表</h3>
+          <el-button
+            type="primary"
+            size="small"
+            @click="addSubArticle"
+            v-if="form.type == 2"
+            >添加副文章</el-button
+          >
+        </div>
+
+        <div
+          v-for="(subArticle, index) in form.subArticleList"
+          :key="index"
+          class="sub-article-item"
+        >
+          <div class="sub-article-header">
+            <h4>副文章 #{{ index + 1 }}</h4>
+            <el-button
+              type="danger"
+              size="small"
+              circle
+              @click="removeSubArticle(index)"
+              :icon="Delete"
+              v-if="form.type == 2"
+            ></el-button>
+          </div>
+
+          <el-form-item
+            :label="'副标题'"
+            :prop="'subArticleList.' + index + '.subTitle'"
+            :rules="{
+              required: true,
+              message: '请输入副标题',
+              trigger: 'blur'
+            }"
+          >
+            <el-input
+              v-model="subArticle.subTitle"
+              placeholder="请输入副标题"
+            ></el-input>
+          </el-form-item>
+
+          <el-form-item
+            :label="'副内容'"
+            :prop="'subArticleList.' + index + '.subContent'"
+            :rules="{
+              required: true,
+              message: '请输入副内容',
+              trigger: 'blur'
+            }"
+          >
+            <TinymceEditor
+              v-model="subArticle.subContent"
+              :height="300"
+              placeholder="请输入副内容"
+            />
+          </el-form-item>
+        </div>
+
+        <div v-if="form.subArticleList.length === 0" class="empty-sub-articles">
+          <el-empty description="暂无副文章,请点击添加按钮创建"></el-empty>
+        </div>
+      </div>
+    </el-form>
+
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="visible = false">取消</el-button>
+        <el-button type="primary" @click="submitForm">确认</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+  import { ref, reactive, defineEmits, watch } from 'vue';
+  import { EleMessage } from 'ele-admin-plus/es';
+  import { Delete } from '@element-plus/icons-vue';
+  import request from '@/utils/request';
+  import TinymceEditor from '@/components/TinymceEditor/index.vue';
+
+  // Remove the props for modelValue and articleData
+  const emit = defineEmits(['success']);
+
+  // 表单引用
+  const formRef = ref(null);
+
+  // 可见性
+  const visible = ref(false);
+
+  // 表单数据
+  const form = reactive({
+    type: "1",
+    code: '',
+    title: '',
+    subArticleList: []
+  });
+
+  // 表单验证规则
+  const rules = {
+    title: [{ required: true, message: '请输入文章标题', trigger: 'blur' }]
+  };
+
+  // 添加副文章
+  function addSubArticle() {
+    form.subArticleList.push({
+      subTitle: '',
+      subContent: ''
+    });
+  }
+
+  // 删除副文章
+  function removeSubArticle(index) {
+    form.subArticleList.splice(index, 1);
+  }
+
+  // 提交表单
+  function submitForm() {
+    formRef.value?.validate((valid) => {
+      if (valid) {
+        const method = form.code ? 'update' : 'add';
+        let params = JSON.parse(JSON.stringify(form));
+        delete params.type;
+        request.post(`/sys/articleInfo/${method}`, params).then((res) => {
+          if (res.data.code === 200) {
+            EleMessage.success(method === 'update' ? '更新成功' : '添加成功');
+            visible.value = false;
+            emit('success');
+          } else {
+            EleMessage.error(res.data.msg || '操作失败');
+          }
+        });
+      }
+    });
+  }
+
+  // 关闭时重置表单
+  function handleClose() {
+    formRef.value?.resetFields();
+    Object.assign(form, {
+      code: '',
+      title: '',
+      subArticleList: []
+    });
+  }
+
+  // 添加对外暴露的 handleOpen 方法
+  function handleOpen(articleData = {}) {
+    // 设置表单数据
+    form.code = articleData.code || '';
+    form.title = articleData.title || '';
+    form.type = articleData.type || '1';
+    form.subArticleList = articleData.subArticleList?.length
+      ? [...articleData.subArticleList]
+      : [];
+
+    // 如果是新建模式且没有副文章,添加一个空的副文章
+    if (!form.code && !form.subArticleList.length) {
+      addSubArticle();
+    }
+
+    // 打开弹窗
+    visible.value = true;
+  }
+
+  // 暴露方法供外部调用
+  defineExpose({
+    handleOpen
+  });
+</script>
+
+<style lang="scss">
+  .article-dialog {
+    :deep(.el-dialog__body) {
+      max-height: 70vh;
+      overflow-y: auto;
+    }
+  }
+
+  .sub-article-section {
+    margin-top: 20px;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    padding: 15px;
+  }
+
+  .sub-article-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 15px;
+  }
+
+  .sub-article-item {
+    margin-bottom: 20px;
+    padding: 15px;
+    border: 1px dashed #e4e7ed;
+    border-radius: 4px;
+    background-color: #f8f9fa;
+  }
+
+  .empty-sub-articles {
+    margin: 30px 0;
+  }
+</style>

+ 231 - 2
src/views/marketing/article/index.vue

@@ -1,4 +1,233 @@
 <template>
-  <div> 文章管理 </div>
+  <ele-page flex-table>
+    <common-table ref="pageRef" :pageConfig="pageConfig" :columns="columns">
+      <template #toolbar>
+        <div style="display: flex; align-items: center; gap: 10px">
+          <!-- <el-button
+            type="primary"
+            plain
+            :icon="PlusOutlined"
+            @click="handleUpdate()"
+          >
+            新建文章
+          </el-button> -->
+          <!-- <el-button
+            type="danger"
+            plain
+            :icon="DeleteOutlined"
+            @click="handleDelete()"
+          >
+            删除
+          </el-button> -->
+
+          <div style="display: flex; align-items: center; gap: 10px">
+            <el-input
+              v-model="search"
+              placeholder="请输入文章名称"
+              style="width: 300px"
+            />
+            <el-button type="primary" @click="reload()">查询</el-button>
+            <el-button type="primary" plain @click="reset">重置</el-button>
+          </div>
+        </div>
+      </template>
+
+      <template #status="{ row }">
+        <el-switch
+          :model-value="row.status === '1'"
+          style="--el-switch-on-color: #13ce66"
+          @change="statusSwitchChange($event, row)"
+        />
+        <el-text style="margin-left: 5px">{{
+          row.status === '1' ? '已开启' : '已关闭'
+        }}</el-text>
+      </template>
+
+      <template #action="{ row }">
+        <div>
+          <el-button type="primary" link @click="handleUpdate(row)">
+            [编辑]
+          </el-button>
+          <!-- <el-button type="danger" link @click="handleDelete(row)">
+            [删除]
+          </el-button> -->
+        </div>
+      </template>
+    </common-table>
+
+    <!-- 使用文章编辑弹窗组件 -->
+    <article-dialog
+      ref="articleDialogRef"
+      @success="handleDialogSuccess"
+    />
+  </ele-page>
 </template>
-<script setup></script>
+
+<script setup>
+  import { ref, reactive } from 'vue';
+  import { ElMessageBox } from 'element-plus/es';
+  import { EleMessage } from 'ele-admin-plus/es';
+  import { PlusOutlined, DeleteOutlined } from '@/components/icons';
+  import CommonTable from '@/components/CommonPage/CommonTable.vue';
+  import ArticleDialog from './components/ArticleDialog.vue';
+  import request from '@/utils/request';
+
+  const search = ref('');
+  const reset = () => {
+    search.value = '';
+    reload();
+  };
+
+  defineOptions({ name: 'ArticleManage' });
+
+  /** 表格列配置 */
+  const columns = ref([
+    {
+      type: 'selection',
+      columnKey: 'selection',
+      width: 50,
+      align: 'center',
+      fixed: 'left'
+    },
+    {
+      label: '文章名称',
+      prop: 'title',
+      align: 'center',
+      minWidth: 140
+    },
+    {
+      label: '字数',
+      prop: 'wordNum',
+      align: 'center',
+    },
+    // {
+    //   label: '添加时间',
+    //   prop: 'createTime',
+    //   align: 'center',
+    // },
+    {
+      label: '状态',
+      prop: 'status',
+      align: 'center',
+      slot: 'status'
+    },
+    {
+      columnKey: 'action',
+      label: '操作',
+      width: 160,
+      align: 'center',
+      slot: 'action'
+    }
+  ]);
+
+  /** 页面组件实例 */
+  const pageRef = ref(null);
+
+  const pageConfig = reactive({
+    pageUrl: '/sys/articleInfo/list',
+    fileName: '文章管理',
+    cacheKey: 'articleManageTable'
+  });
+
+  //刷新表格
+  function reload(where) {
+    pageRef.value?.reload({ ...where, title: search.value });
+  }
+
+  //修改状态
+  const statusSwitchChange = (value, row) => {
+    let message = value ? '确认开启?' : '确认关闭?';
+    ElMessageBox.confirm(message, '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '关闭',
+      type: 'warning'
+    })
+      .then(() => {
+        const status = value ? '1' : '0';
+        request
+          .post('/sys/articleInfo/updateStatus', {
+            code: row.code,
+            status: status
+          })
+          .then((res) => {
+            if (res.data.code === 200) {
+              EleMessage.success('操作成功');
+              reload();
+            } else {
+              EleMessage.error(res.data.msg);
+            }
+          });
+      })
+      .catch(() => {
+        row.status = value ? '0' : '1';
+      });
+  };
+
+  //删除
+  function handleDelete(row) {
+    let ids = [];
+    let message = '确认删除该文章?';
+
+    if (row?.id) {
+      // 单个删除
+      ids = [row.id];
+    } else {
+      // 批量删除
+      const selections = pageRef.value?.getSelections();
+      if (!selections || selections.length === 0) {
+        EleMessage.warning('请选择要删除的数据');
+        return;
+      }
+      ids = selections.map((item) => item.id);
+      message = `确认删除选中的 ${ids.length} 条数据?`;
+    }
+
+    ElMessageBox.confirm(message, '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning'
+    }).then(() => {
+      request.post('/sys/articleInfo/delete', { idList: ids }).then((res) => {
+        if (res.data.code === 200) {
+          EleMessage.success('删除成功');
+          reload();
+        } else {
+          EleMessage.error(res.data.msg);
+        }
+      });
+    });
+  }
+
+  // 文章编辑相关
+  const articleDialogRef = ref(null);
+
+  // 处理弹窗成功提交的事件
+  function handleDialogSuccess() {
+    reload();
+  }
+
+  //编辑页面
+  function handleUpdate(row) {
+    // 如果是编辑模式,获取文章详情
+    if (row?.code) {
+      request.get(`/sys/articleInfo/getInfo/${row.code}`).then((res) => {
+        if (res.data.code === 200) {
+          articleDialogRef.value.handleOpen(res.data.data);
+        } else {
+          EleMessage.error(res.data.msg || '获取文章详情失败');
+        }
+      });
+    } else {
+      // 新建模式
+      articleDialogRef.value.handleOpen({
+        code: '',
+        title: '',
+        subArticleList: []
+      });
+    }
+  }
+</script>
+
+<style scoped>
+/* 样式已移至组件内 */
+</style>

+ 4 - 4
src/views/recycle/booklist/index.vue

@@ -170,9 +170,9 @@ const columns = ref([
     {
         label: '总回收数量',
         minWidth: 120,
-        prop: 'recycleNum',
+        prop: 'recycleTotalNum',
         sortable: true,
-        columnKey: 'recycleNum'
+        columnKey: 'recycleTotalNum'
     },
     {
         label: '当前剩余回收量',
@@ -189,9 +189,9 @@ const columns = ref([
     },
     {
         label: '库存',
-        prop: 'stockNum',
+        prop: 'stockTotalNum',
         sortable: true,
-        columnKey: 'stockNum',
+        columnKey: 'stockTotalNum',
         slot: 'stock',
         minWidth: 100
     },

+ 1 - 1
src/views/recycle/components/book-info.vue

@@ -65,7 +65,7 @@
         </div>
         <div class="common-text">
           <el-text
-            >(已回收数量:{{ row.recyleNum || 0 }} 当前库存:{{
+            >(已回收数量:{{ row.recycleNum || 0 }} 当前库存:{{
               row.stockNum || 0
             }})</el-text
           >

+ 1 - 5
src/views/recycle/components/book-stock.vue

@@ -12,7 +12,7 @@
       <el-text></el-text>
     </div>
     <div class="common-text">
-      <el-text>合计:{{ totalStock }}</el-text>
+      <el-text>合计:{{ row.stockTotalNum }}</el-text>
       <el-text></el-text>
     </div>
   </div>
@@ -25,8 +25,4 @@
       default: () => {}
     }
   });
-
-  const totalStock = computed(() => {
-    return props.row.stockGeneralNum + props.row.stockGoodNum + props.row.stockBadNum;
-  });
 </script>