浏览代码

feat:新增首页弹窗广告

叶静 1 周之前
父节点
当前提交
e1e63b7472

+ 157 - 0
src/app/shop/admin/content/adv/edit.vue

@@ -0,0 +1,157 @@
+<template>
+  <el-container v-loading="loading" :element-loading-text="t('modules.adv.loading')">
+    <el-main>
+      <el-form :model="form.model" :rules="form.rules" ref="formRef" :label-width="formLabelWidth">
+        <el-form-item :label="t('modules.adv.advType')" prop="advType">
+          <el-select v-model="form.model.advType" :placeholder="t('modules.adv.selectAdvType')" style="width: 100%">
+            <el-option :label="t('modules.adv.indexPage')" value="INDEX" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item :label="t('modules.adv.advTitle')" prop="title">
+          <el-input v-model="form.model.title" :placeholder="t('modules.adv.enterAdvTitle')"></el-input>
+        </el-form-item>
+
+        <el-form-item :label="t('modules.adv.advImage')" prop="advImage">
+          <sa-upload-image v-model="imageArray" :max-count="1" :accept="['jpg', 'jpeg', 'png']" :max-size="5"
+            :direct-upload="true" :width="375" :height="200" :placeholder="t('modules.adv.uploadImage')" />
+        </el-form-item>
+
+        <el-form-item :label="t('modules.adv.linkType')" prop="linkType">
+          <el-radio-group v-model="form.model.linkType">
+            <el-radio label="IN">{{ t('modules.adv.internalLink') }}</el-radio>
+            <el-radio label="OUT">{{ t('modules.adv.externalLink') }}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <el-form-item :label="t('modules.adv.link')" prop="link">
+          <el-input v-model="form.model.link" :placeholder="t('modules.adv.enterLink')"></el-input>
+        </el-form-item>
+
+        <el-form-item :label="t('modules.adv.serialNo')" prop="serialNo">
+          <el-input-number v-model="form.model.serialNo" :min="0" :max="999" style="width: 100%" />
+        </el-form-item>
+
+        <el-form-item :label="t('modules.adv.status')" prop="statusFlag">
+          <el-radio-group v-model="form.model.statusFlag">
+            <el-radio :label="true">{{ t('modules.adv.enabled') }}</el-radio>
+            <el-radio :label="false">{{ t('modules.adv.disabled') }}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+    </el-main>
+    <el-footer class="sa-footer--submit">
+      <el-button v-if="modal.params.type == 'add'" type="primary" @click="confirm">{{ t('modules.adv.save')
+        }}</el-button>
+      <el-button v-if="modal.params.type == 'edit'" type="primary" @click="confirm">{{ t('modules.adv.update')
+        }}</el-button>
+    </el-footer>
+  </el-container>
+</template>
+<script setup>
+import { cloneDeep } from 'lodash';
+import { onMounted, reactive, ref, unref, computed } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { ElMessage } from 'element-plus';
+import { useFormConfig } from '@/hooks/useFormConfig';
+import { api } from '../content.service';
+
+const { t } = useI18n();
+
+// 使用表单配置hooks
+const { formLabelWidth } = useFormConfig({ enWidth: '140px' });
+const emit = defineEmits(['modalCallBack']);
+const props = defineProps({
+  modal: {
+    type: Object,
+  },
+});
+
+// 添加 编辑 form
+let formRef = ref(null);
+const loading = ref(false);
+
+// 图片数组和字符串的双向绑定
+const imageArray = computed({
+  get() {
+    return form.model.advImage ? [form.model.advImage] : [];
+  },
+  set(value) {
+    form.model.advImage = value.length > 0 ? value[0] : '';
+  },
+});
+
+const form = reactive({
+  model: {
+    advType: 'INDEX',
+    title: '',
+    advImage: '',
+    linkType: 'IN',
+    link: '',
+    serialNo: 0,
+    statusFlag: true,
+  },
+  rules: {
+    advType: [{ required: true, message: t('modules.adv.advTypeRequired'), trigger: 'change' }],
+    title: [{ required: true, message: t('modules.adv.advTitleRequired'), trigger: 'blur' }],
+    advImage: [{ required: true, message: t('modules.adv.advImageRequired'), trigger: 'change' }],
+    linkType: [{ required: true, message: t('modules.adv.linkTypeRequired'), trigger: 'change' }],
+    serialNo: [{ required: true, message: t('modules.adv.serialNoRequired'), trigger: 'blur' }],
+  },
+});
+
+// 获取详情
+async function getDetail(id) {
+  loading.value = true;
+  try {
+    const { code, data } = await api.adv.detail(id);
+    if (code == 200) {
+      form.model = {
+        advType: data.advType || 'INDEX',
+        title: data.title || '',
+        advImage: data.advImage || '',
+        linkType: data.linkType || 'IN',
+        link: data.link || '',
+        serialNo: data.serialNo || 0,
+        statusFlag: data.statusFlag ?? true,
+      };
+    }
+  } catch (error) {
+    console.error('获取详情失败:', error);
+    ElMessage.error(t('modules.adv.getDetailFailed'));
+  }
+  loading.value = false;
+}
+
+// 表单关闭时提交
+async function confirm() {
+  unref(formRef).validate(async (valid) => {
+    if (!valid) return;
+
+    let submitForm = cloneDeep(form.model);
+    try {
+      const { code } =
+        props.modal.params.type == 'add'
+          ? await api.adv.add(submitForm)
+          : await api.adv.edit(props.modal.params.id, submitForm);
+      if (code == 200) {
+        ElMessage.success(t('message.saveSuccess'));
+        emit('modalCallBack', { event: 'confirm' });
+      }
+    } catch (error) {
+      console.error('提交失败:', error);
+      ElMessage.error(t('message.submitFailed'));
+    }
+  });
+}
+
+async function init() {
+  if (props.modal.params.id) {
+    await getDetail(props.modal.params.id);
+  }
+}
+
+onMounted(() => {
+  init();
+});
+</script>

+ 275 - 0
src/app/shop/admin/content/adv/index.vue

@@ -0,0 +1,275 @@
+<template>
+  <el-container class="adv-view panel-block">
+    <el-header class="sa-header">
+      <!-- 简化搜索组件 -->
+      <div class="search-container">
+        <sa-search-simple :searchFields="searchFields" :defaultValues="defaultSearchValues"
+          v-model="currentSearchParams" @search="handleSearch" @reset="handleReset">
+        </sa-search-simple>
+      </div>
+      <div class="sa-title sa-flex sa-row-between">
+        <div class="label sa-flex">{{ t('modules.adv.title') }}</div>
+        <div>
+          <el-button class="sa-button-refresh" icon="RefreshRight" @click="handleRefresh"></el-button>
+          <el-button icon="Plus" type="primary" @click="addRow">{{ t('modules.adv.create') }}</el-button>
+        </div>
+      </div>
+    </el-header>
+    <el-main class="sa-p-0">
+      <div class="sa-table-wrap panel-block panel-block--bottom" v-loading="loading">
+        <el-table height="100%" class="sa-table" :data="table.data" @sort-change="fieldFilter" row-key="id" stripe>
+          <template #empty>
+            <sa-empty />
+          </template>
+
+          <el-table-column prop="id" label="ID" min-width="100" sortable="custom">
+          </el-table-column>
+          <el-table-column :label="t('modules.adv.advTitle')" min-width="150">
+            <template #default="scope">
+              <span class="sa-table-line-1">
+                {{ scope.row.title || '-' }}
+              </span>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('modules.adv.advImage')" min-width="120">
+            <template #default="scope">
+              <el-image v-if="scope.row.advImage" :src="scope.row.advImage" style="height: 80px" fit="contain"
+                :preview-src-list="[scope.row.advImage]" :z-index="9999" preview-teleported />
+              <span v-else>-</span>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('modules.adv.advType')" min-width="120">
+            <template #default="scope">
+              <el-tag type="primary">
+                {{ getAdvTypeText(scope.row.advType) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('modules.adv.linkType')" min-width="100">
+            <template #default="scope">
+              <el-tag :type="scope.row.linkType === 'IN' ? 'success' : 'warning'">
+                {{ getLinkTypeText(scope.row.linkType) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('modules.adv.link')" min-width="200">
+            <template #default="scope">
+              <span class="sa-table-line-1">
+                {{ scope.row.link || '-' }}
+              </span>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('modules.adv.serialNo')" min-width="100">
+            <template #default="scope">
+              {{ scope.row.serialNo || 0 }}
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('modules.adv.status')" min-width="100">
+            <template #default="scope">
+              <el-tag :type="scope.row.statusFlag ? 'success' : 'danger'">
+                {{ getStatusText(scope.row.statusFlag) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('modules.adv.createTime')" min-width="160">
+            <template #default="scope">
+              {{ scope.row.createTime || '-' }}
+            </template>
+          </el-table-column>
+          <el-table-column fixed="right" :label="t('modules.adv.actions')" min-width="120">
+            <template #default="scope">
+              <el-button class="is-link" type="primary" @click="editRow(scope.row)">{{ t('modules.adv.edit')
+              }}</el-button>
+              <el-popconfirm width="fit-content" :confirm-button-text="t('modules.adv.confirm')"
+                :cancel-button-text="t('modules.adv.cancel')" :title="t('modules.adv.confirmDelete')"
+                @confirm="deleteApi(scope.row.id)">
+                <template #reference>
+                  <el-button class="is-link" type="danger">{{ t('modules.adv.delete') }}</el-button>
+                </template>
+              </el-popconfirm>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </el-main>
+    <sa-view-bar>
+      <template #right>
+        <sa-pagination :pageData="pageData" @updateFn="getData" />
+      </template>
+    </sa-view-bar>
+  </el-container>
+</template>
+<script setup>
+import { onMounted, reactive, ref, computed } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { api } from '../content.service';
+import { ElMessage } from 'element-plus';
+import { useModal } from '@/sheep/hooks';
+import { usePagination } from '@/sheep/hooks';
+import advEdit from './edit.vue';
+
+const { t } = useI18n();
+const { pageData } = usePagination();
+
+// 搜索字段配置 - 使用计算属性以支持语言切换
+const searchFields = computed(() => ({
+  title: {
+    type: 'input',
+    label: t('modules.adv.advTitle'),
+    placeholder: t('modules.adv.enterAdvTitle'),
+    width: 200,
+  },
+}));
+
+// 默认搜索值
+const defaultSearchValues = reactive({
+  title: '',
+});
+
+// 当前搜索条件 - 使用 ref 支持双向绑定
+const currentSearchParams = ref({});
+
+// 列表
+const table = reactive({
+  data: [],
+  order: '',
+  sort: '',
+});
+const loading = ref(true);
+
+// 广告类型文本映射
+const getAdvTypeText = (type) => {
+  const typeMap = {
+    INDEX: t('modules.adv.indexPage'),
+  };
+  return typeMap[type] || type;
+};
+
+// 链接类型文本映射
+const getLinkTypeText = (type) => {
+  const typeMap = {
+    IN: t('modules.adv.internalLink'),
+    OUT: t('modules.adv.externalLink'),
+  };
+  return typeMap[type] || type;
+};
+
+// 状态文本映射
+const getStatusText = (status) => {
+  return status ? t('modules.adv.enabled') : t('modules.adv.disabled');
+};
+// 获取数据
+async function getData(page, searchParams = null) {
+  if (page) pageData.page = page;
+  loading.value = true;
+
+  // 构建请求参数 - 优先使用传入的参数,否则使用双向绑定的搜索条件
+  const finalSearchParams = searchParams !== null ? searchParams : currentSearchParams.value;
+
+  try {
+    const { code, data } = await api.adv.list({
+      page: pageData.page,
+      size: pageData.size,
+      queryStr: finalSearchParams.title || '',
+    });
+    if (code == 200) {
+      table.data = data.list;
+      pageData.page = data.pageNum;
+      pageData.size = data.pageSize;
+      pageData.total = data.total;
+    }
+  } catch (error) {
+    console.error('获取数据失败:', error);
+    ElMessage.error(t('modules.adv.getDataFailed'));
+    table.data = [];
+  }
+  loading.value = false;
+}
+// table 字段排序
+function fieldFilter({ prop, order }) {
+  table.order = order == 'ascending' ? 'asc' : 'desc';
+  table.sort = prop;
+  // 使用当前搜索条件进行排序
+  const finalSearchParams = currentSearchParams.value || {};
+  getData(pageData.page, finalSearchParams);
+}
+
+// 刷新处理 - 保持当前筛选条件
+const handleRefresh = () => {
+  // 使用当前搜索条件刷新数据
+  const finalSearchParams = currentSearchParams.value || {};
+  getData(pageData.page, finalSearchParams);
+};
+
+function addRow() {
+  useModal(
+    advEdit,
+    { title: t('modules.adv.createAdv'), type: 'add' },
+    {
+      confirm: () => {
+        getData();
+      },
+    },
+  );
+}
+
+function editRow(row) {
+  useModal(
+    advEdit,
+    {
+      title: t('modules.adv.editAdv'),
+      type: 'edit',
+      id: row.id,
+    },
+    {
+      confirm: () => {
+        getData();
+      },
+    },
+  );
+}
+// 删除api
+async function deleteApi(id) {
+  try {
+    const { code } = await api.adv.delete(id);
+    if (code == 200) {
+      ElMessage.success(t('modules.adv.deleteSuccess'));
+      getData();
+    }
+  } catch (error) {
+    console.error('删除失败:', error);
+    ElMessage.error(t('modules.adv.deleteFailed'));
+  }
+}
+
+// 搜索处理
+const handleSearch = () => {
+  // 由于使用了 v-model,currentSearchParams 会自动更新
+  // 直接调用 getData,会自动使用当前的搜索条件
+  getData(1);
+};
+
+// 重置处理
+const handleReset = () => {
+  // 由于使用了 v-model,currentSearchParams 会自动清空
+  // 直接调用 getData,会自动使用当前的搜索条件
+  getData(1, {});
+};
+
+onMounted(() => {
+  getData();
+});
+</script>
+<style lang="scss" scoped>
+.adv-view {
+  .el-header {
+    height: auto;
+  }
+
+  .el-main {
+    .sa-table-wrap {
+      height: 100%;
+    }
+  }
+}
+</style>

+ 0 - 128
src/app/shop/admin/content/banner/edit.vue

@@ -1,128 +0,0 @@
-<template>
-  <el-container>
-    <el-main>
-      <el-form :model="form.model" :rules="form.rules" ref="formRef" label-width="100px">
-        <el-form-item label="广告标题" prop="title">
-          <el-input v-model="form.model.title" placeholder="请填写广告标题"></el-input>
-        </el-form-item>
-        <el-form-item label="广告图片" prop="image">
-          <el-input v-model="form.model.image" placeholder="请填写图片地址"></el-input>
-        </el-form-item>
-        <el-form-item label="广告位置" prop="position">
-          <el-select v-model="form.model.position" placeholder="请选择广告位置">
-            <el-option label="首页轮播" value="home"></el-option>
-            <el-option label="分类页" value="category"></el-option>
-            <el-option label="商品详情" value="goods"></el-option>
-            <el-option label="个人中心" value="profile"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="链接地址" prop="link">
-          <el-input v-model="form.model.link" placeholder="请填写链接地址"></el-input>
-        </el-form-item>
-        <el-form-item label="链接类型" prop="link_type">
-          <el-select v-model="form.model.link_type" placeholder="请选择链接类型">
-            <el-option label="外部链接" value="url"></el-option>
-            <el-option label="商品详情" value="goods"></el-option>
-            <el-option label="分类页面" value="category"></el-option>
-            <el-option label="页面跳转" value="page"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="开始时间" prop="start_time">
-          <el-date-picker
-            v-model="form.model.start_time"
-            type="datetime"
-            placeholder="选择开始时间"
-            format="YYYY-MM-DD HH:mm:ss"
-            value-format="YYYY-MM-DD HH:mm:ss"
-          />
-        </el-form-item>
-        <el-form-item label="结束时间" prop="end_time">
-          <el-date-picker
-            v-model="form.model.end_time"
-            type="datetime"
-            placeholder="选择结束时间"
-            format="YYYY-MM-DD HH:mm:ss"
-            value-format="YYYY-MM-DD HH:mm:ss"
-          />
-        </el-form-item>
-        <el-form-item label="状态" prop="status">
-          <el-select v-model="form.model.status" placeholder="请选择状态">
-            <el-option label="启用" value="active"></el-option>
-            <el-option label="禁用" value="disabled"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="排序" prop="sort">
-          <el-input v-model="form.model.sort" placeholder="请填写排序"></el-input>
-        </el-form-item>
-      </el-form>
-    </el-main>
-    <el-footer class="sa-footer--submit">
-      <el-button v-if="modal.params.type == 'add'" type="primary" @click="confirm">保存</el-button>
-      <el-button v-if="modal.params.type == 'edit'" type="primary" @click="confirm">更新</el-button>
-    </el-footer>
-  </el-container>
-</template>
-<script setup>
-  import { cloneDeep } from 'lodash';
-  import { onMounted, reactive, ref, unref } from 'vue';
-  import { api } from '../content.service';
-  const emit = defineEmits(['modalCallBack']);
-  const props = defineProps({
-    modal: {
-      type: Object,
-    },
-  });
-  // 添加 编辑 form
-  let formRef = ref(null);
-  const form = reactive({
-    model: {
-      title: '',
-      image: '',
-      position: 'home',
-      link: '',
-      link_type: 'url',
-      start_time: '',
-      end_time: '',
-      status: 'active',
-      sort: '0',
-    },
-    rules: {
-      title: [{ required: true, message: '请填写广告标题', trigger: 'blur' }],
-      image: [{ required: true, message: '请填写图片地址', trigger: 'blur' }],
-      position: [{ required: true, message: '请选择广告位置', trigger: 'change' }],
-      link_type: [{ required: true, message: '请选择链接类型', trigger: 'change' }],
-      status: [{ required: true, message: '请选择状态', trigger: 'change' }],
-      sort: [{ required: true, message: '请填写排序', trigger: 'blur' }],
-    },
-  });
-  const loading = ref(false);
-  // 获取详情
-  async function getDetail(id) {
-    loading.value = true;
-    const { code, data } = await api.detail(id);
-    code == '200' && (form.model = data);
-    loading.value = false;
-  }
-  // 表单关闭时提交
-  async function confirm() {
-    unref(formRef).validate(async (valid) => {
-      if (!valid) return;
-      let submitForm = cloneDeep(form.model);
-      const { code } =
-        props.modal.params.type == 'add'
-          ? await api.add(submitForm)
-          : await api.edit(props.modal.params.id, submitForm);
-      if (code == '200') {
-        emit('modalCallBack', { event: 'confirm' });
-      }
-    });
-  }
-  async function init() {
-    if (props.modal.params.id) {
-      await getDetail(props.modal.params.id);
-    }
-  }
-  onMounted(() => {
-    init();
-  });
-</script>

+ 0 - 258
src/app/shop/admin/content/banner/index.vue

@@ -1,258 +0,0 @@
-<template>
-  <el-container class="banner-view panel-block">
-    <el-header class="sa-header">
-      <!-- 简化搜索组件 -->
-      <div class="search-container">
-        <sa-search-simple :searchFields="searchFields" :defaultValues="defaultSearchValues"
-          v-model="currentSearchParams" @search="handleSearch" @reset="handleReset">
-        </sa-search-simple>
-      </div>
-      <div class="sa-title sa-flex sa-row-between">
-        <div class="label sa-flex">广告位管理</div>
-        <div>
-          <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData()"></el-button>
-          <el-button icon="Plus" type="primary" @click="addRow">新建</el-button>
-        </div>
-      </div>
-    </el-header>
-    <el-main class="sa-p-0">
-      <div class="sa-table-wrap panel-block panel-block--bottom" v-loading="loading">
-        <el-table height="100%" class="sa-table" :data="table.data" @selection-change="changeSelection"
-          @sort-change="fieldFilter" row-key="id" stripe>
-          <template #empty>
-            <sa-empty />
-          </template>
-          <el-table-column type="selection" width="48" align="center"></el-table-column>
-          <el-table-column prop="id" label="ID" min-width="100" sortable="custom">
-          </el-table-column>
-          <el-table-column label="广告标题" min-width="150">
-            <template #default="scope">
-              <span class="sa-table-line-1">
-                {{ scope.row.title || '-' }}
-              </span>
-            </template>
-          </el-table-column>
-          <el-table-column label="广告图片" min-width="120">
-            <template #default="scope">
-              <el-image v-if="scope.row.image" :src="scope.row.image" style="width: 60px; height: 40px" fit="cover" />
-              <span v-else>-</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="广告位置" min-width="120">
-            <template #default="scope">
-              <el-tag :type="scope.row.position === 'home' ? 'primary' : 'success'">
-                {{ scope.row.position_text || '-' }}
-              </el-tag>
-            </template>
-          </el-table-column>
-          <el-table-column label="链接地址" min-width="200">
-            <template #default="scope">
-              <span class="sa-table-line-1">
-                {{ scope.row.link || '-' }}
-              </span>
-            </template>
-          </el-table-column>
-          <el-table-column label="状态" min-width="100">
-            <template #default="scope">
-              <el-tag :type="scope.row.status === 'active' ? 'success' : 'danger'">
-                {{ scope.row.status_text || '-' }}
-              </el-tag>
-            </template>
-          </el-table-column>
-          <el-table-column label="排序" min-width="100">
-            <template #default="scope">
-              {{ scope.row.sort || '-' }}
-            </template>
-          </el-table-column>
-          <el-table-column label="创建时间" min-width="160">
-            <template #default="scope">
-              {{ scope.row.create_time || '-' }}
-            </template>
-          </el-table-column>
-          <el-table-column fixed="right" label="操作" min-width="120">
-            <template #default="scope">
-              <el-button class="is-link" type="primary" @click="editRow(scope.row)">编辑</el-button>
-              <el-popconfirm width="fit-content" confirm-button-text="确认" cancel-button-text="取消" title="确认删除这条记录?"
-                @confirm="deleteApi(scope.row.id)">
-                <template #reference>
-                  <el-button class="is-link" type="danger"> 删除 </el-button>
-                </template>
-              </el-popconfirm>
-            </template>
-          </el-table-column>
-        </el-table>
-      </div>
-    </el-main>
-    <sa-view-bar>
-      <template #left>
-        <sa-batch-handle :batchHandleTools="batchHandleTools" :selectedLeng="table.selected.length"
-          @batchHandle="batchHandle"></sa-batch-handle>
-      </template>
-      <template #right>
-        <sa-pagination :pageData="pageData" @updateFn="getData" />
-      </template>
-    </sa-view-bar>
-  </el-container>
-</template>
-<script setup>
-import { onMounted, reactive, ref } from 'vue';
-import { api } from '../content.service';
-import { ElMessageBox } from 'element-plus';
-import { useModal } from '@/sheep/hooks';
-import { usePagination } from '@/sheep/hooks';
-import bannerEdit from './edit.vue';
-const { pageData } = usePagination();
-
-// 搜索字段配置
-const searchFields = reactive({
-  title: {
-    type: 'input',
-    label: '广告标题',
-    placeholder: '请输入广告标题',
-    width: 200,
-  },
-});
-// 默认搜索值
-const defaultSearchValues = reactive({
-  title: '',
-});
-
-// 当前搜索条件 - 使用 ref 支持双向绑定
-const currentSearchParams = ref({});
-
-// 列表
-const table = reactive({
-  data: [],
-  order: '',
-  sort: '',
-  selected: [],
-});
-const loading = ref(true);
-// 获取数据
-async function getData(page, searchParams = null) {
-  if (page) pageData.page = page;
-  loading.value = true;
-
-  // 构建请求参数 - 优先使用传入的参数,否则使用双向绑定的搜索条件
-  const finalSearchParams = searchParams !== null ? searchParams : currentSearchParams.value;
-
-  const { code, data } = await api.list({
-    page: pageData.page,
-    size: pageData.size,
-    order: table.order,
-    ...finalSearchParams,
-    sort: table.sort,
-  });
-  console.log('API 响应:', error, data);
-  if (code == 200) {
-    table.data = data.data;
-    pageData.page = data.current_page;
-    pageData.size = data.per_page;
-    pageData.total = data.total;
-  }
-  loading.value = false;
-}
-// table 字段排序
-function fieldFilter({ prop, order }) {
-  table.order = order == 'ascending' ? 'asc' : 'desc';
-  table.sort = prop;
-  getData();
-}
-//table批量选择
-function changeSelection(row) {
-  table.selected = row;
-}
-// 分页/批量操作
-const batchHandleTools = [
-  {
-    type: 'delete',
-    label: '删除',
-    auth: 'shop.admin.content.banner.delete',
-    class: 'danger',
-  },
-];
-function addRow() {
-  useModal(
-    bannerEdit,
-    { title: '新建广告', type: 'add' },
-    {
-      confirm: () => {
-        getData();
-      },
-    },
-  );
-}
-function editRow(row) {
-  useModal(
-    bannerEdit,
-    {
-      title: '编辑广告',
-      type: 'edit',
-      id: row.id,
-    },
-    {
-      confirm: () => {
-        getData();
-      },
-    },
-  );
-}
-// 删除api 单独批量可以直接调用
-async function deleteApi(id) {
-  await api.delete(id);
-  getData();
-}
-async function batchHandle(type) {
-  let ids = [];
-  table.selected.forEach((row) => {
-    ids.push(row.id);
-  });
-  switch (type) {
-    case 'delete':
-      ElMessageBox.confirm('此操作将删除, 是否继续?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }).then(() => {
-        deleteApi(ids.join(','));
-      });
-      break;
-    default:
-      await api.edit(ids.join(','), {
-        status: type,
-      });
-      getData();
-  }
-}
-
-// 搜索处理
-const handleSearch = (searchParams) => {
-  // 由于使用了 v-model,currentSearchParams 会自动更新
-  // 直接调用 getData,会自动使用当前的搜索条件
-  getData(1);
-};
-
-// 重置处理
-const handleReset = () => {
-  // 由于使用了 v-model,currentSearchParams 会自动清空
-  // 直接调用 getData,会自动使用当前的搜索条件
-  getData(1);
-};
-
-onMounted(() => {
-  getData();
-});
-</script>
-<style lang="scss" scoped>
-.banner-view {
-  .el-header {
-    height: auto;
-  }
-
-  .el-main {
-    .sa-table-wrap {
-      height: 100%;
-    }
-  }
-}
-</style>

+ 37 - 6
src/app/shop/admin/content/content.service.js

@@ -11,9 +11,9 @@ const route = {
   },
   children: [
     {
-      path: 'banner',
-      name: 'shop.admin.content.banner',
-      component: () => import('./banner/index.vue'),
+      path: 'adv',
+      name: 'shop.admin.content.adv',
+      component: () => import('./adv/index.vue'),
       meta: {
         title: '广告位',
       },
@@ -39,9 +39,40 @@ const route = {
 
 const api = {
   // 广告位相关 API
-  banner: {
-    ...CRUD('shop/admin/banner'),
-    select: (params) => SELECT('shop/admin/banner', params),
+  adv: {
+    // 分页查询
+    list: (params) =>
+      request({
+        url: 'mall/adv/page',
+        method: 'GET',
+        params,
+      }),
+    // 新增
+    add: (data) =>
+      request({
+        url: 'mall/adv/save',
+        method: 'POST',
+        data,
+      }),
+    // 详情
+    detail: (id) =>
+      request({
+        url: `mall/adv/info/${id}`,
+        method: 'GET',
+      }),
+    // 更新
+    edit: (id, data) =>
+      request({
+        url: `mall/adv/update/${id}`,
+        method: 'PATCH',
+        data,
+      }),
+    // 删除
+    delete: (id) =>
+      request({
+        url: `mall/adv/delete/${id}`,
+        method: 'DELETE',
+      }),
   },
 
   // 消息推送相关 API

+ 4 - 4
src/app/shop/admin/content/notification/index.vue

@@ -132,10 +132,10 @@ async function getData(page, searchParams = null) {
       queryStr: finalSearchParams.title || '',
     });
     if (code == 200) {
-      table.data = data.records || [];
-      pageData.page = data.current || 1;
-      pageData.size = data.size || 10;
-      pageData.total = data.total || 0;
+      table.data = data.list;
+      pageData.page = data.pageNum;
+      pageData.size = data.pageSize;
+      pageData.total = data.total;
     }
   } catch (error) {
     console.error('获取数据失败:', error);

+ 42 - 0
src/locales/en-US/index.json

@@ -1447,6 +1447,48 @@
       "deleteFailed": "Delete failed",
       "getDataFailed": "Failed to get data"
     },
+    "adv": {
+      "title": "Advertisement Management",
+      "create": "Create Advertisement",
+      "createAdv": "Create Advertisement",
+      "editAdv": "Edit Advertisement",
+      "advType": "Advertisement Type",
+      "advTitle": "Advertisement Title",
+      "advImage": "Advertisement Image",
+      "linkType": "Link Type",
+      "link": "Link URL",
+      "serialNo": "Sort Order",
+      "createTime": "Create Time",
+      "updateTime": "Update Time",
+      "actions": "Actions",
+      "edit": "Edit",
+      "delete": "Delete",
+      "confirm": "Confirm",
+      "cancel": "Cancel",
+      "confirmDelete": "Are you sure you want to delete this record?",
+      "selectAdvType": "Please select advertisement type",
+      "enterAdvTitle": "Please enter advertisement title",
+      "enterLink": "Please enter link URL",
+      "indexPage": "Index Popup",
+      "internalLink": "Internal Link",
+      "externalLink": "External Link",
+      "loading": "Loading...",
+      "save": "Save",
+      "update": "Update",
+      "advTypeRequired": "Please select advertisement type",
+      "advTitleRequired": "Please enter advertisement title",
+      "advImageRequired": "Please upload advertisement image",
+      "linkTypeRequired": "Please select link type",
+      "serialNoRequired": "Please enter sort order",
+      "getDataFailed": "Failed to get data",
+      "getDetailFailed": "Failed to get details",
+      "deleteSuccess": "Delete successful",
+      "deleteFailed": "Delete failed",
+      "uploadImage": "Upload Image",
+      "status": "Status",
+      "enabled": "Enabled",
+      "disabled": "Disabled"
+    },
     "userSelect": {
       "userAccount": "User Account",
       "userPhone": "User Phone",

+ 1 - 1
src/locales/navigation.js

@@ -36,7 +36,7 @@ export const navigationMap = {
   // 三级菜单 - 内容
   'shop.admin.content.notification': 'menu.messagePush',
   'shop.admin.content.sms': 'menu.sms',
-  'shop.admin.content.banner': 'menu.bannerAd',
+  'shop.admin.content.adv': 'menu.bannerAd',
 
   // 三级菜单 - 数据
   'shop.admin.data.report': 'menu.dataReport',

+ 42 - 0
src/locales/zh-CN/index.json

@@ -1455,6 +1455,48 @@
       "deleteFailed": "删除失败",
       "getDataFailed": "获取数据失败"
     },
+    "adv": {
+      "title": "广告位管理",
+      "create": "新建广告",
+      "createAdv": "新建广告",
+      "editAdv": "编辑广告",
+      "advType": "广告类型",
+      "advTitle": "广告标题",
+      "advImage": "广告图片",
+      "linkType": "链接类型",
+      "link": "链接地址",
+      "serialNo": "排序",
+      "createTime": "创建时间",
+      "updateTime": "更新时间",
+      "actions": "操作",
+      "edit": "编辑",
+      "delete": "删除",
+      "confirm": "确认",
+      "cancel": "取消",
+      "confirmDelete": "确认删除这条记录?",
+      "selectAdvType": "请选择广告类型",
+      "enterAdvTitle": "请输入广告标题",
+      "enterLink": "请输入链接地址",
+      "indexPage": "首页弹窗",
+      "internalLink": "内链",
+      "externalLink": "外链",
+      "loading": "加载中...",
+      "save": "保存",
+      "update": "更新",
+      "advTypeRequired": "请选择广告类型",
+      "advTitleRequired": "请输入广告标题",
+      "advImageRequired": "请上传广告图片",
+      "linkTypeRequired": "请选择链接类型",
+      "serialNoRequired": "请输入排序",
+      "getDataFailed": "获取数据失败",
+      "getDetailFailed": "获取详情失败",
+      "deleteSuccess": "删除成功",
+      "deleteFailed": "删除失败",
+      "uploadImage": "上传图片",
+      "status": "状态",
+      "enabled": "启用",
+      "disabled": "禁用"
+    },
     "userSelect": {
       "userAccount": "用户账号",
       "userPhone": "用户手机",

+ 1 - 1
src/sheep/local-data/admin.js

@@ -384,7 +384,7 @@ const menuRulesData = {
               label: '广告位',
               logo: '',
               type: '2',
-              composingKey: 'shop.admin.content.banner',
+              composingKey: 'shop.admin.content.adv',
               url: '',
               seq: 2,
               parentId: '239',