zouzexu 2 mēneši atpakaļ
vecāks
revīzija
e4c76a9480

+ 234 - 0
src/views/equipmentManage/third-party-equipment-info/index.vue

@@ -0,0 +1,234 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    ></page-content>
+
+    <!-- 新增 -->
+    <page-modal
+      ref="addModalRef"
+      :modal-config="addModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "ThirdPartyEquipmentInfo" });
+
+import ThirdPartyEquipmentInfoAPI, {
+  ThirdPartyEquipmentInfoForm,
+  ThirdPartyEquipmentInfoPageQuery,
+} from "@/api/equipmentManage/third-party-equipment-info-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "system:third-party-equipment-info",
+  formItems: [
+    {
+      type: "input",
+      label: "设备编码",
+      prop: "equipmentId",
+      attrs: {
+        placeholder: "设备编码",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "input",
+      label: "设备名称",
+      prop: "equipmentName",
+      attrs: {
+        placeholder: "设备名称",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<ThirdPartyEquipmentInfoPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "system:third-party-equipment-info",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: ThirdPartyEquipmentInfoAPI.getPage,
+  // 删除接口
+  deleteAction: ThirdPartyEquipmentInfoAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["export"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "接口ID", prop: "connectorId" },
+    { label: "接口名称", prop: "connectorName" },
+    { label: "所属充电站", prop: "stationName" },
+    { label: "总功率(kW)", prop: "power" },
+    { label: "设备编码", prop: "equipmentId" },
+    { label: "设备名称", prop: "equipmentName" },
+    {
+      label: "车位号",
+      prop: "parkNo",
+      templet: "format",
+      formatter: (row: any) => {
+        return row.parkNo || "--";
+      },
+    },
+    {
+      label: "国家标准",
+      prop: "nationalStandard",
+      templet: "format",
+      formatter: (row: any) => {
+        return row.nationalStandard == 1 ? "2011" : "2015";
+      },
+    },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: ["edit"],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<ThirdPartyEquipmentInfoForm> = reactive({
+  // 权限前缀
+  permPrefix: "system:third-party-equipment-info",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "设备编码",
+        disabled: true,
+      },
+      label: "设备编码",
+      prop: "equipmentId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "设备名称",
+        disabled: true,
+      },
+      label: "设备名称",
+      prop: "equipmentName",
+    },
+  ],
+  // 提交函数
+  formAction: (data: ThirdPartyEquipmentInfoForm) => {
+    if (data.id) {
+      // 编辑
+      return ThirdPartyEquipmentInfoAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return ThirdPartyEquipmentInfoAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<ThirdPartyEquipmentInfoForm> = reactive({
+  permPrefix: "system:third-party-equipment-info",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return ThirdPartyEquipmentInfoAPI.update(data.id as string, data);
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await ThirdPartyEquipmentInfoAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>

+ 386 - 0
src/views/equipmentManage/third-party-station-info/index.vue

@@ -0,0 +1,386 @@
+<template>
+  <div class="app-container">
+    <div class="search-container">
+      <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+        <el-form-item label="充电站ID" prop="stationId">
+          <el-input
+            v-model="queryParams.stationId"
+            placeholder="请输入充电站ID"
+            clearable
+            @keyup.enter="handleQuery"
+          />
+        </el-form-item>
+
+        <el-form-item label="充电站名称" prop="stationName">
+          <el-input
+            v-model="queryParams.stationName"
+            placeholder="请输入充电站名称"
+            clearable
+            @keyup.enter="handleQuery"
+          />
+        </el-form-item>
+
+        <el-form-item label="站点状态" prop="stationStatus">
+          <el-select
+            v-model="queryParams.stationStatus"
+            placeholder="请选择站点状态"
+            clearable
+            style="width: 200px"
+          >
+            <el-option label="未知" :value="0" />
+            <el-option label="建设中" :value="1" />
+            <el-option label="关闭下线" :value="5" />
+            <el-option label="维护中" :value="6" />
+            <el-option label="正常使用" :value="50" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="search-buttons">
+          <el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
+          <el-button icon="refresh" @click="handleResetQuery">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <el-card shadow="never">
+      <el-table
+        ref="dataTableRef"
+        v-loading="loading"
+        :data="pageData"
+        highlight-current-row
+        border
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column
+          key="stationId"
+          label="充电站ID"
+          prop="stationId"
+          min-width="150"
+          align="center"
+        />
+        <el-table-column
+          key="equipmentOwnerId"
+          label="设备所属方"
+          prop="equipmentOwnerName"
+          min-width="150"
+          align="center"
+        >
+          <template #default="scope">
+            {{ scope.row.equipmentOwnerName ? scope.row.equipmentOwnerName : "--" }}
+          </template>
+        </el-table-column>
+        <el-table-column
+          key="stationName"
+          label="充电站名称"
+          prop="stationName"
+          min-width="150"
+          align="center"
+        />
+        <el-table-column
+          key="address"
+          label="详细地址"
+          prop="address"
+          min-width="150"
+          align="center"
+        />
+        <el-table-column
+          key="serviceTel"
+          label="服务电话"
+          prop="serviceTel"
+          min-width="150"
+          align="center"
+        />
+        <!--        1-公共,50-个人,100-公交(专用),101-环卫(专用),102-物流(专用),103-出租车(专用),255-其他-->
+        <el-table-column
+          key="stationType"
+          label="站点类型"
+          prop="stationType"
+          min-width="150"
+          align="center"
+        >
+          <template #default="scope">
+            <el-tag v-if="scope.row.stationType === 1" type="primary">公共</el-tag>
+            <el-tag v-if="scope.row.stationType === 50" type="danger">个人</el-tag>
+            <el-tag v-if="scope.row.stationType === 100" type="info">公交(专用)</el-tag>
+            <el-tag v-if="scope.row.stationType === 101" type="primary">环卫(专用)</el-tag>
+            <el-tag v-if="scope.row.stationType === 102" type="success">物流(专用)</el-tag>
+            <el-tag v-if="scope.row.stationType === 103" type="warning">物流(专用)</el-tag>
+            <el-tag v-if="scope.row.stationType === 255" type="info">物流(专用)</el-tag>
+          </template>
+        </el-table-column>
+        <!--        0-未知,1-建设中,5-关闭下线,6-维护中,50-正常使用-->
+        <el-table-column
+          key="stationStatus"
+          label="站点状态"
+          prop="stationStatus"
+          min-width="150"
+          align="center"
+        >
+          <template #default="scope">
+            <el-tag v-if="scope.row.stationStatus === 0" type="warning">未知</el-tag>
+            <el-tag v-if="scope.row.stationStatus === 1" type="danger">建设中</el-tag>
+            <el-tag v-if="scope.row.stationStatus === 5" type="info">关闭下线</el-tag>
+            <el-tag v-if="scope.row.stationStatus === 6" type="primary">维护中</el-tag>
+            <el-tag v-if="scope.row.stationStatus === 50" type="success">正常使用</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column
+          key="updateTime"
+          label="更新时间"
+          prop="updateTime"
+          min-width="150"
+          align="center"
+        />
+        <el-table-column label="操作" align="center" width="300">
+          <template #default="scope">
+            <el-button type="primary" icon="edit" size="small" @click="handleOpenDialog(scope.row)">
+              编辑
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination
+        v-if="total > 0"
+        v-model:total="total"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        @pagination="handleQuery()"
+      />
+    </el-card>
+    <el-drawer v-model="drawer" :direction="direction" title="详情编辑">
+      <el-form
+        ref="dataFormRef"
+        :model="formDataInfo"
+        :rules="formRules"
+        label-width="100px"
+        label-position="left"
+      >
+        <el-form-item label="站点名称" prop="stationName">
+          <el-input v-model="formDataInfo.stationName" disabled />
+        </el-form-item>
+        <el-form-item label="站点营业时间" prop="ownBusinessHours">
+          <el-input v-model="formDataInfo.ownBusinessHours" disabled />
+        </el-form-item>
+        <el-form-item label="站点轮播图" prop="bannerPictures">
+          <MultiImageUpload
+            v-model="bannerPicturesRef"
+            @update:model-value="
+              () => {
+                // 手动触发表单验证
+                if (dataFormRef.value) {
+                  dataFormRef.value.validateField('bannerPictures');
+                }
+              }
+            "
+          ></MultiImageUpload>
+        </el-form-item>
+        <el-form-item label="站点提示语" prop="stationTips">
+          <el-input v-model="formDataInfo.stationTips" />
+        </el-form-item>
+        <el-form-item label="站点联系电话" prop="customerServiceHotline">
+          <el-input v-model="formDataInfo.customerServiceHotline" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div style="flex: auto">
+          <el-button @click="drawer = false">取消</el-button>
+          <el-button type="primary" @click="confirmClick">确认</el-button>
+        </div>
+      </template>
+    </el-drawer>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, onMounted } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+defineOptions({
+  name: "ThirdPartyStationInfo",
+  inheritAttrs: false,
+});
+
+import ThirdPartyStationInfoAPI, {
+  ThirdPartyStationInfoPageVO,
+  ThirdPartyStationInfoForm,
+  ThirdPartyStationInfoPageQuery,
+} from "@/api/equipmentManage/third-party-station-info-api";
+// 导入MultiImageUpload组件
+import MultiImageUpload from "@/components/Upload/MultiImageUpload.vue";
+
+const queryFormRef = ref();
+const dataFormRef = ref();
+
+const loading = ref(false);
+const removeIds = ref<number[]>([]);
+const total = ref(0);
+
+const queryParams = reactive<ThirdPartyStationInfoPageQuery>({
+  pageNum: 1,
+  pageSize: 10,
+});
+
+// 第三方充电站信息表格数据
+const pageData = ref<ThirdPartyStationInfoPageVO[]>([]);
+
+// 弹窗
+const dialog = reactive({
+  title: "",
+  visible: false,
+});
+
+// 第三方充电站信息表单数据
+const formData = reactive<ThirdPartyStationInfoForm>({});
+
+/** 查询第三方充电站信息 */
+function handleQuery() {
+  loading.value = true;
+  ThirdPartyStationInfoAPI.getPage(queryParams)
+    .then((data) => {
+      pageData.value = data.list;
+      total.value = data.total;
+    })
+    .finally(() => {
+      loading.value = false;
+    });
+}
+
+/** 重置第三方充电站信息查询 */
+function handleResetQuery() {
+  queryFormRef.value!.resetFields();
+  queryParams.pageNum = 1;
+  handleQuery();
+}
+
+/** 行复选框选中记录选中ID集合 */
+function handleSelectionChange(selection: any) {
+  removeIds.value = selection.map((item: any) => item.id);
+}
+
+const drawer = ref(false);
+const formDataInfo = ref({});
+const direction = ref("rtl");
+
+// 添加表单验证规则
+const formRules = {
+  stationTips: [{ required: true, message: "站点提示语不能为空", trigger: "blur" }],
+  customerServiceHotline: [{ required: true, message: "站点联系电话不能为空", trigger: "blur" }],
+};
+
+// 定义轮播图引用
+const bannerPicturesRef = ref();
+
+/** 打开第三方充电站信息弹窗 */
+function handleOpenDialog(row?: any) {
+  drawer.value = true;
+  // 清空之前的轮播图数据
+  bannerPicturesRef.value = [];
+  ThirdPartyStationInfoAPI.getFormData(parseInt(row.id)).then((data) => {
+    formDataInfo.value = data;
+    if (data.bannerPictures && Array.isArray(data.bannerPictures)) {
+      bannerPicturesRef.value = data.bannerPictures;
+    } else {
+      bannerPicturesRef.value = [];
+    }
+  });
+}
+
+/** 点击确认按钮 */
+function confirmClick() {
+  // 验证表单
+  dataFormRef.value.validate((valid: boolean) => {
+    if (valid) {
+      // 构造提交数据
+      const submitData = {
+        id: formDataInfo.value.id, // 站点id
+        stationTips: formDataInfo.value.stationTips, // 站点提示语
+        customerServiceHotline: formDataInfo.value.customerServiceHotline || "", // 站点联系电话,表单没返则为空
+        bannerPictures: bannerPicturesRef.value || [], // 站点banner图,格式为['图片链接','图片链接','图片链接']
+      };
+
+      // 调用stationUpdate接口方法进行提交
+      loading.value = true;
+      ThirdPartyStationInfoAPI.stationUpdate(submitData)
+        .then(() => {
+          ElMessage.success("保存成功");
+          drawer.value = false;
+          handleQuery(); // 重新查询数据
+        })
+        .catch((error: any) => {
+          ElMessage.error("保存失败: " + error.message);
+        })
+        .finally(() => {
+          loading.value = false;
+        });
+    }
+  });
+}
+
+/** 提交第三方充电站信息表单 */
+function handleSubmit() {
+  dataFormRef.value.validate((valid: any) => {
+    if (valid) {
+      loading.value = true;
+      const id = formData.id;
+      if (id) {
+        ThirdPartyStationInfoAPI.update(id, formData)
+          .then(() => {
+            ElMessage.success("修改成功");
+            handleCloseDialog();
+            handleResetQuery();
+          })
+          .finally(() => (loading.value = false));
+      } else {
+        ThirdPartyStationInfoAPI.add(formData)
+          .then(() => {
+            ElMessage.success("新增成功");
+            handleCloseDialog();
+            handleResetQuery();
+          })
+          .finally(() => (loading.value = false));
+      }
+    }
+  });
+}
+
+/** 关闭第三方充电站信息弹窗 */
+function handleCloseDialog() {
+  dialog.visible = false;
+  dataFormRef.value.resetFields();
+  dataFormRef.value.clearValidate();
+  formData.id = undefined;
+}
+
+/** 删除第三方充电站信息 */
+function handleDelete(id?: number) {
+  const ids = [id || removeIds.value].join(",");
+  if (!ids) {
+    ElMessage.warning("请勾选删除项");
+    return;
+  }
+
+  ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(
+    () => {
+      loading.value = true;
+      ThirdPartyStationInfoAPI.deleteByIds(ids)
+        .then(() => {
+          ElMessage.success("删除成功");
+          handleResetQuery();
+        })
+        .finally(() => (loading.value = false));
+    },
+    () => {
+      ElMessage.info("已取消删除");
+    }
+  );
+}
+
+onMounted(() => {
+  handleQuery();
+});
+</script>

+ 288 - 0
src/views/operationsManage/advertising/index.vue

@@ -0,0 +1,288 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    ></page-content>
+
+    <!-- 新增 -->
+    <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
+      <template #picture="{ formData }">
+        <SingleImageUpload v-model="formData.picture" />
+      </template>
+    </page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    >
+      <template #picture="{ formData }">
+        <SingleImageUpload v-model="formData.picture" />
+      </template>
+    </page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, reactive, defineOptions, onMounted } from "vue";
+import SingleImageUpload from "@/components/Upload/SingleImageUpload.vue";
+import { useDictStore } from "@/store";
+
+defineOptions({ name: "Advertising" });
+
+import AdvertisingAPI, {
+  AdvertisingForm,
+  AdvertisingPageQuery,
+} from "@/api/operationsManage/advertising-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 获取字典store
+const dictStore = useDictStore();
+
+// 广告位置选项
+const advertisingPositionOptions = ref<Array<{ label: string; value: string | number }>>([]);
+
+// 加载广告位置字典
+const loadAdvertisingPositionDict = async () => {
+  await dictStore.loadDictItems("advertising_position");
+  advertisingPositionOptions.value = dictStore.getDictItems("advertising_position");
+};
+
+// 组件挂载时加载字典
+onMounted(() => {
+  loadAdvertisingPositionDict();
+});
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "system:advertising",
+  formItems: [
+    {
+      type: "input",
+      label: "名称",
+      prop: "name",
+      attrs: {
+        placeholder: "请输入",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<AdvertisingPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "system:advertising",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: AdvertisingAPI.getPage,
+  // 删除接口
+  deleteAction: AdvertisingAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "名称", prop: "name" },
+    {
+      label: "状态", // 1.上线 2.下线
+      prop: "status",
+      formatter: (value: any) => {
+        return value.status === 1 ? "上线" : "下线";
+      },
+    },
+    { label: "排序", prop: "sort" },
+    {
+      label: "广告位置", //  1.首页弹框
+      prop: "position",
+      formatter: (row: any) => {
+        return "首页弹窗";
+      },
+    },
+    { label: "图片", prop: "picture", templet: "image" },
+    {
+      label: "跳转路径",
+      prop: "skipUrl",
+      formatter: (value: any) => {
+        return value.skipUrl || "无";
+      },
+    },
+    { label: "创建时间", prop: "createTime" },
+    { label: "更新时间", prop: "updateTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: ["edit", "delete"],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<AdvertisingForm> = reactive({
+  // 权限前缀
+  permPrefix: "system:advertising",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "名称",
+      },
+      rules: [{ required: true, message: "名称不能为空", trigger: "blur" }],
+      label: "名称",
+      prop: "name",
+    },
+    {
+      type: "switch",
+      attrs: {
+        activeValue: 1,
+        inactiveValue: 0,
+      },
+      initialValue: 1,
+      label: "状态",
+      prop: "status",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "排序",
+      },
+      rules: [{ required: true, message: "排序不能为空", trigger: "blur" }],
+      label: "排序",
+      prop: "sort",
+    },
+    {
+      permission: "system:advertising:position",
+      type: "select",
+      attrs: {
+        placeholder: "请选择广告位置",
+      },
+      label: "广告位置",
+      prop: "position",
+      options: advertisingPositionOptions,
+    },
+    {
+      type: "custom",
+      label: "图片",
+      prop: "picture",
+      slotName: "picture",
+      rules: [{ required: true, message: "请上传图片", trigger: "change" }],
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "跳转路径",
+      },
+      label: "跳转路径",
+      prop: "skipUrl",
+    },
+  ],
+  // 提交函数
+  formAction: (data: AdvertisingForm) => {
+    if (data.id) {
+      // 编辑
+      return AdvertisingAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return AdvertisingAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<AdvertisingForm> = reactive({
+  permPrefix: "system:advertising",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return AdvertisingAPI.update(data.id as string, data);
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await AdvertisingAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>

+ 1192 - 0
src/views/operationsManage/billing-strategy/index.vue

@@ -0,0 +1,1192 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #stationType="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="charging_station_type" />
+      </template>
+      <template #stationStatus="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="charging_station_status" />
+      </template>
+      <template #construction="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="charging_construction_type" />
+      </template>
+      <!--      // 0、平台 1、企业 2、渠道方-->
+      <template #salesType="scope">
+        <el-tag v-if="scope.row.salesType == 0" type="danger">平台</el-tag>
+        <el-tag v-if="scope.row.salesType == 1" type="primary">企业</el-tag>
+        <el-tag v-if="scope.row.salesType == 2" type="warning">渠道方</el-tag>
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
+      <template #addServiceFee>
+        <el-table v-loading="loading" :data="getServiceFeeData()" style="width: 100%">
+          <el-table-column prop="timePeriod" label="时间段" width="180"></el-table-column>
+          <el-table-column
+            prop="electricityFee"
+            label="电费(元/度)"
+            width="180"
+          ></el-table-column>
+          <el-table-column
+            prop="settlementServiceFee"
+            label="结算服务费(元)"
+            width="180"
+          ></el-table-column>
+          <el-table-column
+            prop="settlementFeeTotal"
+            label="结算费合计(元/度)"
+            width="180"
+          ></el-table-column>
+          <el-table-column prop="operationServiceFee" label="运营服务费(元)" width="180">
+            <template #default="scope">
+              <div v-if="editingRowIndex === scope.$index">
+                <el-input v-model="editingValue" size="small"></el-input>
+                <ElButton
+                  type="text"
+                  size="small"
+                  @click="saveEditOperationServiceFee(scope.row, scope.$index)"
+                >
+                  保存
+                </ElButton>
+                <ElButton type="text" size="small" @click="cancelEditOperationServiceFee">
+                  取消
+                </ElButton>
+              </div>
+              <span
+                v-else
+                class="text-#0FDCC4"
+                @click="startEditOperationServiceFee(scope.row, scope.$index)"
+              >
+                {{ scope.row.operationServiceFee }}
+              </span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop="valueAddedFees"
+            label="增值费用(元)"
+            width="180"
+          ></el-table-column>
+          <el-table-column
+            prop="salesTotalPrice"
+            label="销售合计价格(元/度)"
+            width="180"
+          ></el-table-column>
+          <el-table-column prop="periodFlag" label="时段标志" width="180">
+            <template #default="scope">
+              <span v-if="scope.row.periodFlag === 1">尖</span>
+              <span v-else-if="scope.row.periodFlag === 2">峰</span>
+              <span v-else-if="scope.row.periodFlag === 3">平</span>
+              <span v-else-if="scope.row.periodFlag === 4">谷</span>
+              <span v-else>{{ scope.row.periodFlag }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+      </template>
+      <template #addStationFee="{ formData }">
+        <div class="flex items-center">
+          <el-input
+            v-model="formData.stationFee"
+            placeholder="请输入整站设置统一服务费"
+            style="width: 200px"
+            @keyup.enter="() => confirmUniformFeeSetting(formData.stationFee)"
+          />
+          <ElButton
+            type="primary"
+            size="small"
+            class="ml-2"
+            @click="() => confirmUniformFeeSetting(formData.stationFee)"
+          >
+            确定
+          </ElButton>
+        </div>
+      </template>
+    </page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    >
+      <template #editServiceFee>
+        <el-table v-loading="loading" :data="getServiceFeeData()" style="width: 100%">
+          <el-table-column prop="timePeriod" label="时间段" width="180"></el-table-column>
+          <el-table-column
+            prop="electricityFee"
+            label="电费(元/度)"
+            width="180"
+          ></el-table-column>
+          <el-table-column
+            prop="settlementServiceFee"
+            label="结算服务费(元)"
+            width="180"
+          ></el-table-column>
+          <el-table-column
+            prop="settlementFeeTotal"
+            label="结算费合计(元/度)"
+            width="180"
+          ></el-table-column>
+          <el-table-column prop="operationServiceFee" label="运营服务费(元)" width="180">
+            <template #default="scope">
+              <div v-if="editingRowIndex === scope.$index">
+                <el-input v-model="editingValue" size="small"></el-input>
+                <ElButton
+                  type="text"
+                  size="small"
+                  @click="saveEditOperationServiceFee(scope.row, scope.$index)"
+                >
+                  保存
+                </ElButton>
+                <ElButton type="text" size="small" @click="cancelEditOperationServiceFee">
+                  取消
+                </ElButton>
+              </div>
+              <span
+                v-else
+                class="text-#0FDCC4"
+                @click="startEditOperationServiceFee(scope.row, scope.$index)"
+              >
+                {{ scope.row.operationServiceFee }}
+              </span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop="valueAddedFees"
+            label="增值费用(元)"
+            width="180"
+          ></el-table-column>
+          <el-table-column
+            prop="salesTotalPrice"
+            label="销售合计价格(元/度)"
+            width="180"
+          ></el-table-column>
+          <el-table-column prop="periodFlag" label="时段标志" width="180">
+            <template #default="scope">
+              <span v-if="scope.row.periodFlag === 1">尖</span>
+              <span v-else-if="scope.row.periodFlag === 2">峰</span>
+              <span v-else-if="scope.row.periodFlag === 3">平</span>
+              <span v-else-if="scope.row.periodFlag === 4">谷</span>
+              <span v-else>{{ scope.row.periodFlag }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+      </template>
+      <template #editStationFee="{ formData }">
+        <div class="flex items-center">
+          <el-input
+            v-model="formData.stationFee"
+            placeholder="请输入整站设置统一服务费"
+            style="width: 200px"
+            @keyup.enter="() => confirmUniformFeeSetting(formData.stationFee)"
+          />
+          <ElButton
+            type="primary"
+            size="small"
+            class="ml-2"
+            @click="() => confirmUniformFeeSetting(formData.stationFee)"
+          >
+            确定
+          </ElButton>
+        </div>
+      </template>
+    </page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "ThirdPartyStationInfo" });
+import { ref, h } from "vue";
+import { ElMessage, ElMessageBox, ElButton } from "element-plus";
+import ThirdPartyStationInfoAPI, {
+  ThirdPartyStationInfoForm,
+  ThirdPartyStationInfoPageQuery,
+} from "@/api/operationsManage/billing-strategy-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+import { useDictStoreHook } from "@/store/modules/dict-store"; // 添加字典存储导入
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick: originalHandleAddClick,
+  handleEditClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 自定义新增点击处理函数,在打开新增模态框时重置统一费用设置状态
+const handleAddClick = () => {
+  // 重置统一费用设置状态
+  isUniformFeeSet.value = false;
+  // 调用原始的新增点击处理函数
+  originalHandleAddClick();
+};
+// 字典store
+const dictStore = useDictStoreHook();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "system:billing-strategy",
+  formItems: [
+    {
+      type: "input",
+      label: "充电站ID",
+      prop: "stationId",
+      attrs: {
+        placeholder: "充电站ID",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "input",
+      label: "充电站名称",
+      prop: "stationName",
+      attrs: {
+        placeholder: "充电站名称",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "select", // 修改为select类型
+      label: "站点状态",
+      prop: "stationStatus",
+      attrs: {
+        placeholder: "请选择站点状态",
+        clearable: true,
+        style: { width: "200px" },
+      },
+      async initFn(formItem) {
+        await dictStore.loadDictItems("charging_station_status");
+        formItem.options = dictStore.getDictItems("charging_station_status");
+      },
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<ThirdPartyStationInfoPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "system:billing-strategy",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: ThirdPartyStationInfoAPI.getPage,
+  // 删除接口
+  deleteAction: ThirdPartyStationInfoAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "充电站ID", prop: "stationId" },
+    {
+      label: "充电站名称",
+      prop: "stationName",
+    },
+    {
+      label: "单位名称",
+      prop: "unitName",
+      formatter: (row: any) => {
+        return row.unitName || "--";
+      },
+    },
+    { label: "省市辖区编码", prop: "areaCode" },
+    { label: "详细地址", prop: "address" },
+    { label: "服务电话", prop: "serviceTel" },
+    { label: "站点类型", prop: "stationType", templet: "custom" },
+    // { label: "站点类型", prop: "stationTips" },
+    {
+      label: "站点状态",
+      prop: "stationStatus",
+      templet: "custom",
+    },
+    {
+      label: "收费类型",
+      prop: "salesType",
+      templet: "custom",
+    },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: [
+        "edit",
+        "delete",
+        // {
+        //   name: "setTips",
+        //   text: "设置提示语",
+        //   attrs: {
+        //     type: "success",
+        //   },
+        // },
+      ],
+    },
+  ],
+});
+
+const stationId = ref();
+// 新增配置
+const addModalConfig: IModalConfig<ThirdPartyStationInfoForm> = reactive({
+  // 权限前缀
+  permPrefix: "system:billing-strategy",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 1200,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 140,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "select",
+      attrs: {
+        placeholder: "请选择充电站",
+        style: {
+          width: "200px",
+        },
+      },
+      rules: [{ required: true, message: "充电站不能为空", trigger: "blur" }],
+      label: "选择电站",
+      prop: "stationId",
+      options: [],
+      events: {
+        change: (value: string) => {
+          stationId.value = value;
+          const formData = addModalRef.value?.getFormData();
+          const salesType = formData?.salesType;
+
+          if (value && salesType !== undefined) {
+            // 平台类型直接调用
+            if (salesType === 0) {
+              fetchBillingStrategy(value, salesType, 0, 0);
+            }
+            // 企业类型需要检查是否有企业ID
+            else if (salesType === 1) {
+              const firmId = formData?.firmId;
+              if (firmId) {
+                fetchBillingStrategy(value, salesType, 0, firmId);
+              }
+            }
+            // 渠道方类型需要检查是否有渠道方ID
+            else if (salesType === 2) {
+              const thirdPartyId = formData?.id;
+              if (thirdPartyId) {
+                fetchBillingStrategy(value, salesType, thirdPartyId, 0);
+              }
+            }
+          }
+        },
+      },
+      async initFn(formItem) {
+        try {
+          // 调用接口获取充电站数据
+          const response = await ThirdPartyStationInfoAPI.getChargingPileSelect();
+          if (Array.isArray(response)) {
+            formItem.options = response.map((item) => ({
+              label: item.stationName,
+              value: item.id,
+            }));
+          } else {
+            console.warn("接口返回数据格式不正确,期望数组格式:", response);
+            formItem.options = [];
+          }
+        } catch (error) {
+          console.error("获取充电站数据失败:", error);
+          formItem.options = [];
+        }
+      },
+    },
+    {
+      type: "radio",
+      label: "收费类型",
+      prop: "salesType",
+      attrs: {
+        placeholder: "请选择收费类型",
+      },
+      // 0、平台 1、企业 2、渠道方
+      options: [
+        {
+          label: "平台",
+          value: 0,
+        },
+        {
+          label: "企业",
+          value: 1,
+        },
+        {
+          label: "渠道方",
+          value: 2,
+        },
+      ],
+      rules: [{ required: true, message: "收费类型不能为空", trigger: "blur" }],
+      events: {
+        change: (value: number) => {
+          const formData = addModalRef.value?.getFormData();
+          const stationId = formData?.stationId;
+
+          if (stationId && value !== undefined) {
+            if (value === 0) {
+              fetchBillingStrategy(stationId, value, 0, 0);
+            } else if (value === 1) {
+              serviceFeeData.value = [];
+              const firmId = formData?.firmId;
+              if (firmId) {
+                fetchBillingStrategy(stationId, value, 0, firmId);
+              }
+            } else if (value === 2) {
+              serviceFeeData.value = [];
+              const thirdPartyId = formData?.id;
+              if (thirdPartyId) {
+                fetchBillingStrategy(stationId, value, thirdPartyId, 0);
+              }
+            }
+          }
+        },
+      },
+    },
+    {
+      type: "select",
+      label: "选择企业",
+      prop: "firmId",
+      attrs: {
+        placeholder: "请选择企业",
+        style: {
+          width: "200px",
+        },
+      },
+      rules: [
+        {
+          required: true,
+          message: "企业不能为空",
+          trigger: "blur",
+          validator: (_, value, callback, formData) => {
+            // 只有当收费类型为1(企业)时才校验
+            if (formData.salesType === 1) {
+              if (!value) {
+                callback(new Error("企业不能为空"));
+              } else {
+                callback();
+              }
+            } else {
+              callback();
+            }
+          },
+        },
+      ],
+      options: [],
+      visible: (formData: any) => {
+        return formData.salesType === 1;
+      },
+      events: {
+        change: (value: number) => {
+          const formData = addModalRef.value?.getFormData();
+          const stationId = formData?.stationId;
+          const salesType = formData?.salesType;
+          if (stationId && salesType === 1 && value) {
+            fetchBillingStrategy(stationId, salesType, 0, value);
+          }
+        },
+      },
+      async initFn(formItem) {
+        try {
+          // 调用接口获取企业数据
+          const response = await ThirdPartyStationInfoAPI.getFirmSelect();
+          if (Array.isArray(response)) {
+            formItem.options = response.map((item) => ({
+              label: item.name,
+              value: item.id,
+            }));
+          }
+        } catch (error) {
+          console.error("获取企业数据失败:", error);
+        }
+      },
+    },
+    {
+      type: "select",
+      label: "选择渠道方",
+      prop: "id",
+      attrs: {
+        placeholder: "请选择渠道方",
+        style: {
+          width: "200px",
+        },
+      },
+      rules: [
+        {
+          required: true,
+          message: "渠道方不能为空",
+          trigger: "blur",
+          validator: (_, value, callback, formData) => {
+            if (formData.salesType === 2) {
+              if (!value) {
+                callback(new Error("渠道方不能为空"));
+              } else {
+                callback();
+              }
+            } else {
+              callback();
+            }
+          },
+        },
+      ],
+      options: [],
+      visible: (formData: any) => {
+        return formData.salesType === 2;
+      },
+      events: {
+        change: (value: number) => {
+          // 当渠道方选择改变时调用接口
+          const formData = addModalRef.value?.getFormData();
+          const stationId = formData?.stationId;
+          const salesType = formData?.salesType;
+
+          if (stationId && salesType === 2 && value) {
+            fetchBillingStrategy(stationId, salesType, value, 0);
+          }
+        },
+      },
+      async initFn(formItem) {
+        try {
+          // 调用接口获取渠道方数据
+          const response = await ThirdPartyStationInfoAPI.getChannelSelect();
+          if (Array.isArray(response)) {
+            formItem.options = response.map((item) => ({
+              label: item.stationName,
+              value: item.id,
+            }));
+          }
+        } catch (error) {
+          console.error("获取渠道方数据失败:", error);
+        }
+      },
+    },
+    {
+      type: "custom",
+      label: "整站设置统一服务费",
+      prop: "stationFee",
+      slotName: "addStationFee",
+    },
+    {
+      type: "custom",
+      label: "服务费设置",
+      prop: "serviceFee",
+      slotName: "addServiceFee",
+    },
+  ],
+  // 提交函数
+  formAction: (data: ThirdPartyStationInfoForm) => {
+    // 检查统一费用是否已设置
+    if (!isUniformFeeSet.value) {
+      ElMessageBox.confirm("统一费用尚未设置,确定要提交吗?", "确认提交", {
+        cancelButtonText: "去设置",
+        type: "warning",
+      })
+        .then(() => {
+          // 用户确认提交,即使未设置统一费用
+          if (data.id) {
+            // 编辑
+            // return ThirdPartyStationInfoAPI.update(data.id as string, data);
+            return Promise.resolve();
+          } else {
+            // return ThirdPartyStationInfoAPI.create(data);
+            return Promise.resolve();
+          }
+        })
+        .catch(() => {
+          // 用户选择去设置,不提交
+          ElMessage.info("请先设置统一费用");
+          return Promise.reject(new Error("未设置统一费用"));
+        });
+    } else {
+      // 统一费用已设置,正常提交
+      if (data.id) {
+        // 编辑
+        // return ThirdPartyStationInfoAPI.update(data.id as string, data);
+        return Promise.resolve();
+      } else {
+        // return ThirdPartyStationInfoAPI.create(data);
+        return Promise.resolve();
+      }
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<ThirdPartyStationInfoForm> = reactive({
+  permPrefix: "system:billing-strategy",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 1000,
+  },
+  pk: "id",
+  formAction(data: any) {
+    // 检查统一费用是否已设置
+    if (!isUniformFeeSet.value) {
+      return ElMessageBox.confirm("统一费用尚未设置,确定要提交吗?", "确认提交", {
+        confirmButtonText: "确定提交",
+        cancelButtonText: "去设置",
+        type: "warning",
+      })
+        .then(() => {
+          // 用户确认提交,即使未设置统一费用
+          return Promise.resolve();
+        })
+        .catch(() => {
+          // 用户选择去设置,不提交
+          ElMessage.info("请先设置统一费用");
+          return Promise.reject(new Error("未设置统一费用"));
+        });
+    } else {
+      // 统一费用已设置,正常提交
+      return Promise.resolve();
+    }
+  },
+  formItems: addModalConfig.formItems.map((item) => {
+    // 为编辑模态框复制表单项,并添加相应的事件监听器
+    if (item.prop === "stationId") {
+      return {
+        ...item,
+        events: {
+          change: (value: string) => {
+            // 当充电站选择改变时,根据当前的收费类型进行不同处理
+            const formData = editModalRef.value?.getFormData();
+            const salesType = formData?.salesType;
+            if (value && salesType !== undefined) {
+              // 平台类型直接调用
+              if (salesType === 0) {
+                fetchBillingStrategy(value, salesType, null, null);
+              }
+              // 企业类型需要检查是否有企业ID
+              else if (salesType === 1) {
+                serviceFeeData.value = [];
+                const firmId = formData?.firmId;
+                if (firmId) {
+                  fetchBillingStrategy(value, salesType, null, firmId);
+                }
+              }
+              // 渠道方类型需要检查是否有渠道方ID
+              else if (salesType === 2) {
+                serviceFeeData.value = [];
+                const thirdPartyId = formData?.id; // 注意这里使用的是 id 字段
+                if (thirdPartyId) {
+                  fetchBillingStrategy(value, salesType, thirdPartyId, null);
+                }
+              }
+            }
+          },
+        },
+        // 在编辑模式下确保选项已加载
+        async initFn(formItem) {
+          try {
+            // 调用接口获取充电站数据
+            const response = await ThirdPartyStationInfoAPI.getChargingPileSelect();
+            if (Array.isArray(response)) {
+              formItem.options = response.map((item) => ({
+                label: item.stationName,
+                value: item.id,
+              }));
+            } else {
+              console.warn("接口返回数据格式不正确,期望数组格式:", response);
+              formItem.options = [];
+            }
+          } catch (error) {
+            console.error("获取充电站数据失败:", error);
+            formItem.options = [];
+          }
+        },
+      };
+    } else if (item.prop === "salesType") {
+      return {
+        ...item,
+        events: {
+          change: (value: number) => {
+            // 当收费类型选择改变时,根据不同的收费类型进行不同处理
+            const formData = editModalRef.value?.getFormData();
+            const stationId = formData?.stationId;
+
+            if (stationId && value !== undefined) {
+              // 平台类型直接调用
+              if (value === 0) {
+                fetchBillingStrategy(stationId, value, null, null);
+              }
+              // 企业类型需要等待企业选择后再调用
+              else if (value === 1) {
+                const firmId = formData?.firmId;
+                if (firmId) {
+                  fetchBillingStrategy(stationId, value, null, firmId);
+                }
+              }
+              // 渠道方类型需要等待渠道方选择后再调用
+              else if (value === 2) {
+                const thirdPartyId = formData?.id; // 注意这里使用的是 id 字段
+                if (thirdPartyId) {
+                  fetchBillingStrategy(stationId, value, thirdPartyId, null);
+                }
+              }
+            }
+          },
+        },
+      };
+    } else if (item.prop === "firmId") {
+      // 企业下拉框添加显隐控制和change事件
+      return {
+        ...item,
+        rules: [
+          {
+            required: true,
+            message: "企业不能为空",
+            trigger: "blur",
+            validator: (_, value, callback, formData) => {
+              // 只有当收费类型为1(企业)时才校验
+              if (formData.salesType === 1) {
+                if (!value) {
+                  callback(new Error("企业不能为空"));
+                } else {
+                  callback();
+                }
+              } else {
+                callback();
+              }
+            },
+          },
+        ],
+        visible: (formData: any) => {
+          // 只有当收费类型为1(企业)时才显示
+          return formData.salesType === 1;
+        },
+        events: {
+          change: (value: number) => {
+            // 当企业选择改变时调用接口
+            const formData = editModalRef.value?.getFormData();
+            const stationId = formData?.stationId;
+            const salesType = formData?.salesType;
+
+            if (stationId && salesType === 1 && value) {
+              fetchBillingStrategy(stationId, salesType, 0, value);
+            }
+          },
+        },
+        // 在编辑模式下确保选项已加载
+        async initFn(formItem) {
+          try {
+            // 调用接口获取企业数据
+            const response = await ThirdPartyStationInfoAPI.getFirmSelect();
+            if (Array.isArray(response)) {
+              formItem.options = response.map((item) => ({
+                label: item.name,
+                value: item.id,
+              }));
+            }
+          } catch (error) {
+            console.error("获取企业数据失败:", error);
+          }
+        },
+      };
+    } else if (item.prop === "id") {
+      // 渠道方下拉框添加显隐控制和change事件
+      return {
+        ...item,
+        rules: [
+          {
+            required: true,
+            message: "渠道方不能为空",
+            trigger: "blur",
+            validator: (_, value, callback, formData) => {
+              // 只有当收费类型为2(渠道方)时才校验
+              if (formData.salesType === 2) {
+                if (!value) {
+                  callback(new Error("渠道方不能为空"));
+                } else {
+                  callback();
+                }
+              } else {
+                callback();
+              }
+            },
+          },
+        ],
+        visible: (formData: any) => {
+          // 只有当收费类型为2(渠道方)时才显示
+          return formData.salesType === 2;
+        },
+        events: {
+          change: (value: number) => {
+            // 当渠道方选择改变时调用接口
+            const formData = editModalRef.value?.getFormData();
+            const stationId = formData?.stationId;
+            const salesType = formData?.salesType;
+
+            if (stationId && salesType === 2 && value) {
+              fetchBillingStrategy(stationId, salesType, value, 0);
+            }
+          },
+        },
+        // 在编辑模式下确保选项已加载
+        async initFn(formItem) {
+          try {
+            // 调用接口获取渠道方数据
+            const response = await ThirdPartyStationInfoAPI.getChannelSelect();
+            if (Array.isArray(response)) {
+              formItem.options = response.map((item) => ({
+                label: item.stationName,
+                value: item.id,
+              }));
+            }
+          } catch (error) {
+            console.error("获取渠道方数据失败:", error);
+          }
+        },
+      };
+    } else if (item.prop === "stationFee") {
+      // 整站设置统一服务费
+      return {
+        ...item,
+        type: "custom",
+        slotName: "editStationFee",
+      };
+    } else if (item.prop === "serviceFee") {
+      // 服务费设置
+      return {
+        ...item,
+        type: "custom",
+        slotName: "editServiceFee",
+      };
+    }
+    return item;
+  }),
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      // 根据销售类型传入相应参数
+      const stationId = data.row.id;
+      const salesType = data.row.salesType !== undefined ? data.row.salesType : 0; // 默认为平台类型
+      let firmId = 0;
+      let thirdPartyId = 0;
+
+      // 根据销售类型设置相应参数
+      if (salesType === 1) {
+        firmId = data.row.firmId || 0;
+      } else if (salesType === 2) {
+        thirdPartyId = data.row.thirdPartyId || data.row.id || 0;
+      }
+
+      // 重置统一费用设置状态
+      isUniformFeeSet.value = false;
+
+      // 获取表单数据
+      const formData = await ThirdPartyStationInfoAPI.getFormData(
+        stationId,
+        salesType,
+        firmId,
+        thirdPartyId
+      );
+
+      // 获取服务费策略数据并填充到表格中
+      fetchBillingStrategy(stationId, salesType, thirdPartyId, firmId);
+
+      // 确保表单数据包含必要的字段用于回显
+      const enrichedFormData = {
+        ...formData,
+        stationId,
+        salesType,
+        firmId: firmId || formData.firmId,
+        id: thirdPartyId || formData.id, // 注意这里使用的是 id 字段存储 thirdPartyId
+      };
+
+      // 返回表单数据用于回显
+      return enrichedFormData;
+    });
+  } else if (data.name === "setTips") {
+    // 显示设置提示语弹窗
+    ElMessageBox.prompt("设置提示语", {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      inputPlaceholder: "请输入提示语",
+      inputValue: data.row.tips || "",
+      inputPattern: /^.{0,100}$/,
+      inputErrorMessage: "提示语长度不能超过100个字符",
+    })
+      .then(async ({ value }) => {
+        try {
+          await ThirdPartyStationInfoAPI.setTips({
+            stationId: parseInt(data.row.id),
+            stationTips: value,
+          });
+          ElMessage.success("提示语设置成功");
+          // 刷新列表
+          contentRef.value?.getList();
+        } catch (error) {
+          console.error("设置提示语失败:", error);
+        }
+      })
+      .catch(() => {
+        ElMessage.info("取消设置");
+      });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+
+// 存储从接口获取的服务费数据
+const serviceFeeData = ref([]);
+const loading = ref(false);
+// 存储正在编辑的行索引
+const editingRowIndex = ref(-1);
+// 存储编辑时的临时值
+const editingValue = ref("");
+// 跟踪统一费用是否已设置
+const isUniformFeeSet = ref(false);
+
+// 获取服务费表格数据
+const getServiceFeeData = () => {
+  return serviceFeeData.value;
+};
+
+// 调用getBillingStrategy接口获取服务费数据
+const fetchBillingStrategy = async (
+  stationId: string,
+  salesType: number,
+  thirdPartyId: number = 0,
+  firmId: number = 0
+) => {
+  loading.value = true;
+  try {
+    const response = await ThirdPartyStationInfoAPI.getBillingStrategy(
+      stationId,
+      salesType,
+      thirdPartyId,
+      firmId
+    );
+    console.log("获取计费策略数据:", response);
+
+    // 格式化时间函数
+    const formatTimePeriod = (timeStr: string) => {
+      if (!timeStr || timeStr.length !== 6) return timeStr;
+      const hours = timeStr.substring(0, 2);
+      const minutes = timeStr.substring(2, 4);
+      const seconds = timeStr.substring(4, 6);
+      return `${hours}:${minutes}:${seconds}`;
+    };
+    if (response && Array.isArray(response)) {
+      serviceFeeData.value = response.map((item) => ({
+        timePeriod: formatTimePeriod(item.timePeriod),
+        electricityFee: item.electricityPrice,
+        settlementServiceFee: item.settlementServiceFee,
+        settlementFeeTotal: item.settlementTotalPrice,
+        operationServiceFee: item.operationServiceFee,
+        salesTotalPrice: item.saleTotalPrice,
+        valueAddedFees: item.valueAddedFees,
+        periodFlag: item.periodFlag,
+      }));
+    } else {
+      serviceFeeData.value = [];
+    }
+  } catch (error) {
+    console.error("获取计费策略数据失败:", error);
+    serviceFeeData.value = [];
+  } finally {
+    loading.value = false;
+  }
+};
+
+// 开始编辑运营服务费
+const startEditOperationServiceFee = (row: any, index: number) => {
+  editingRowIndex.value = index;
+  editingValue.value = row.operationServiceFee || "";
+};
+
+// 保存编辑的运营服务费
+const saveEditOperationServiceFee = async (row: any, index: number) => {
+  // 验证输入值是否为数字(整数或小数)
+  if (!/^-?\d+(\.\d+)?$/.test(editingValue.value)) {
+    ElMessage.error("请输入有效的数字");
+    return;
+  }
+
+  // 获取当前表单数据以获取必要参数
+  let formData = addModalRef.value?.getFormData();
+  if (!formData || !formData.stationId) {
+    formData = editModalRef.value?.getFormData();
+  }
+
+  if (!formData || !formData.stationId) {
+    ElMessage.error("无法获取表单数据,请确保已选择电站");
+    return;
+  }
+
+  // 构造保存参数数组
+  const saveParamsArray = [
+    {
+      timePeriod: row.timePeriod.replace(/:/g, ""), // 移除时间中的冒号
+      operationServiceFee: parseFloat(editingValue.value),
+      stationInfoId: formData.stationId,
+      salesType: formData.salesType,
+      periodFlag: row.periodFlag,
+    },
+  ];
+
+  // 根据销售类型添加额外参数
+  if (formData.salesType === 1) {
+    // 企业类型
+    saveParamsArray[0].firmId = formData.firmId;
+  } else if (formData.salesType === 2) {
+    // 渠道方类型
+    saveParamsArray[0].thirdPartyId = formData.id;
+  }
+  try {
+    // 调用保存接口,传递数组参数
+    await ThirdPartyStationInfoAPI.saveBillingStrategy(saveParamsArray);
+    ElMessage.success("保存成功");
+    // 更新本地数据
+    serviceFeeData.value[index].operationServiceFee = parseFloat(editingValue.value);
+    // 标记统一费用已设置
+    isUniformFeeSet.value = true;
+    // 重置编辑状态
+    editingRowIndex.value = -1;
+    editingValue.value = "";
+  } catch (error) {
+    ElMessage.error("保存失败: " + (error as Error).message);
+  }
+};
+
+// 取消编辑
+const cancelEditOperationServiceFee = () => {
+  editingRowIndex.value = -1;
+  editingValue.value = "";
+};
+
+// 处理统一费用设置
+const handleUniformFeeSetting = async (uniformFee: string) => {
+  // 验证输入值是否为数字(整数或小数)
+  if (!uniformFee || !/^-?\d+(\.\d+)?$/.test(uniformFee)) {
+    // 如果输入为空或无效,则不执行任何操作
+    return;
+  }
+
+  const feeValue = parseFloat(uniformFee);
+
+  // 获取当前表单数据以获取必要参数
+  let formData = addModalRef.value?.getFormData();
+  if (!formData || !formData.stationId) {
+    formData = editModalRef.value?.getFormData();
+  }
+
+  if (!formData || !formData.stationId) {
+    ElMessage.error("无法获取表单数据,请确保已选择电站");
+    return;
+  }
+
+  // 构造保存参数数组
+  const saveParamsArray = serviceFeeData.value.map((row: any) => ({
+    timePeriod: row.timePeriod.replace(/:/g, ""), // 移除时间中的冒号
+    operationServiceFee: feeValue,
+    stationInfoId: formData.stationId,
+    salesType: formData.salesType,
+    periodFlag: row.periodFlag,
+    // 根据销售类型添加额外参数
+    ...(formData.salesType === 1 && { firmId: formData.firmId }), // 企业类型
+    ...(formData.salesType === 2 && { thirdPartyId: formData.id }), // 渠道方类型
+  }));
+
+  try {
+    // 调用保存接口,传递数组参数
+    await ThirdPartyStationInfoAPI.saveBillingStrategy(saveParamsArray);
+    ElMessage.success("统一设置成功");
+
+    // 更新本地数据
+    serviceFeeData.value = serviceFeeData.value.map((row: any) => ({
+      ...row,
+      operationServiceFee: feeValue,
+    }));
+
+    // 标记统一费用已设置
+    isUniformFeeSet.value = true;
+  } catch (error) {
+    ElMessage.error("统一设置失败: " + (error as Error).message);
+  }
+};
+
+// 确认统一费用设置
+const confirmUniformFeeSetting = (uniformFee: string) => {
+  // 验证输入值是否为数字(整数或小数)
+  if (!uniformFee || !/^-?\d+(\.\d+)?$/.test(uniformFee)) {
+    ElMessage.error("请输入有效的数字");
+    return;
+  }
+
+  const feeValue = parseFloat(uniformFee);
+
+  ElMessageBox.confirm(`确定要将所有时间段的运营服务费设置为 ${feeValue} 元吗?`, "确认设置", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(() => {
+      handleUniformFeeSetting(uniformFee);
+    })
+    .catch(() => {
+      // 用户取消操作
+      ElMessage.info("已取消设置");
+    });
+};
+</script>

+ 302 - 0
src/views/operationsManage/discounts-activity/index.vue

@@ -0,0 +1,302 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    >
+      <template #type="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="discount_type" v-bind="scope.attrs" />
+      </template>
+    </page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #type="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="discount_type" />
+      </template>
+      <template #status="scope">
+        <el-tag :type="scope.row.status === 0 ? 'danger' : 'success'">
+          {{ scope.row.status === 0 ? "未启用" : "启用" }}
+        </el-tag>
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal
+      ref="addModalRef"
+      :modal-config="addModalConfig"
+      @submit-click="handleSubmitClick"
+    >
+      <template #type="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="discount_type" v-bind="scope.attrs" />
+      </template>
+    </page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    >
+      <template #type="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="discount_type" v-bind="scope.attrs" />
+      </template>
+    </page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "DiscountsActivity" });
+
+import DiscountsActivityAPI, {
+  DiscountsActivityForm,
+  DiscountsActivityPageQuery,
+} from "@/api/operationsManage/discounts-activity-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+import { useDictStoreHook } from "@/store/modules/dict-store";
+import Dict from "@/components/Dict/index.vue";
+import DictLabel from "@/components/Dict/DictLabel.vue";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+const dictStore = useDictStoreHook();
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "operationsManage:discounts-activity",
+  formItems: [
+    {
+      type: "input",
+      label: "活动名称",
+      prop: "name",
+      attrs: {
+        placeholder: "活动名称",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "custom",
+      slotName: "type",
+      label: "优惠类型", // 1-新用户首单立减 2-活动折扣
+      prop: "type",
+      attrs: {
+        placeholder: "请选择优惠类型",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "select",
+      label: "状态", // 0-未启用 1-启用
+      prop: "status",
+      attrs: {
+        placeholder: "状态",
+        clearable: true,
+        style: { width: "200px" },
+      },
+      options: [
+        {
+          label: "未启用",
+          value: 0,
+        },
+        {
+          label: "启用",
+          value: 1,
+        },
+      ],
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<DiscountsActivityPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "operationsManage:discounts-activity",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: DiscountsActivityAPI.getPage,
+  // 删除接口
+  deleteAction: DiscountsActivityAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "活动名称", prop: "name" },
+    {
+      label: "优惠类型", // 1-新用户首单立减 2-活动折扣
+      prop: "type",
+      templet: "custom",
+      slotName: "type"
+    },
+    { label: "优惠金额/折扣", prop: "discount" },
+    { label: "活动描述", prop: "activityDesc" },
+    {
+      label: "状态", // 0-未启用 1-启用
+      prop: "status",
+      templet: "custom",
+    },
+    { label: "创建时间", prop: "createTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: ["edit", "delete"],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<DiscountsActivityForm> = reactive({
+  // 权限前缀
+  permPrefix: "operationsManage:discounts-activity",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 140,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "活动名称",
+        style: { width: "300px" },
+      },
+      label: "活动名称",
+      prop: "name",
+      rules: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
+    },
+    {
+      type: "custom",
+      slotName: "type",
+      label: "优惠类型",
+      prop: "type",
+      rules: [{ required: true, message: "请选择优惠类型", trigger: "blur" }],
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "优惠金额/折扣",
+        style: { width: "300px" },
+      },
+      label: "优惠金额/折扣",
+      prop: "discount",
+      rules: [{ required: true, message: "优惠金额不能为空", trigger: "blur" }],
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "活动描述",
+        style: { width: "300px" },
+      },
+      label: "活动描述",
+      prop: "activityDesc",
+    },
+    {
+      type: "switch",
+      attrs: {
+        activeText: "启用",
+        inactiveText: "禁用",
+        activeValue: 1,
+        inactiveValue: 0,
+      },
+      initialValue: 1,
+      label: "状态", // 0-未启用 1-启用
+      prop: "status",
+    },
+  ],
+  // 提交函数
+  formAction: (data: DiscountsActivityForm) => {
+    if (data.id) {
+      // 编辑
+      return DiscountsActivityAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return DiscountsActivityAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<DiscountsActivityForm> = reactive({
+  permPrefix: "operationsManage:discounts-activity",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return DiscountsActivityAPI.update(data.id as string, data);
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await DiscountsActivityAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>

+ 412 - 0
src/views/operationsManage/promotion-coupon-template/index.vue

@@ -0,0 +1,412 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #status="scope">
+        <!--        1.上线 2.下线-->
+        <el-tag :type="scope.row.status === 2 ? 'danger' : 'success'">
+          {{ scope.row.status === 2 ? "下线" : "上线" }}
+        </el-tag>
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal
+      ref="addModalRef"
+      :modal-config="addModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "PromotionCouponTemplate" });
+
+import PromotionCouponTemplateAPI, {
+  PromotionCouponTemplateForm,
+  PromotionCouponTemplatePageQuery,
+} from "@/api/operationsManage/promotion-coupon-template-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "business:promotion-coupon-template",
+  formItems: [
+    {
+      type: "input",
+      label: "优惠劵名称",
+      prop: "name",
+      attrs: {
+        placeholder: "优惠劵名称",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "select",
+      label: "上线状态", // 1.上线 2.下线
+      prop: "status",
+      attrs: {
+        placeholder: "上线状态", // 1.上线 2.下线
+        clearable: true,
+        style: { width: "200px" },
+      },
+      options: [
+        {
+          label: "下线",
+          value: 2,
+        },
+        {
+          label: "上线",
+          value: 1,
+        },
+      ],
+    },
+    {
+      type: "date-picker",
+      label: "创建时间",
+      prop: "createTime",
+      attrs: {
+        type: "daterange",
+        "range-separator": "~",
+        "start-placeholder": "开始时间",
+        "end-placeholder": "截止时间",
+        "value-format": "YYYY-MM-DD",
+        style: { width: "240px" },
+      },
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<PromotionCouponTemplatePageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "business:promotion-coupon-template",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: (params: any) => {
+    // 处理创建时间范围参数
+    if ("createTime" in params) {
+      const createTime = params.createTime as string[];
+      if (createTime?.length > 1) {
+        params.startTime = createTime[0] + " 00:00:00";
+        params.endTime = createTime[1] + " 23:59:59";
+      }
+      delete params.createTime;
+    }
+    return PromotionCouponTemplateAPI.getPage(params);
+  },
+  // 删除接口
+  deleteAction: PromotionCouponTemplateAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "优惠劵名称", prop: "name" },
+    { label: "描述", prop: "description" },
+    { label: "领取后有效期(天)", prop: "failureTime" },
+    {
+      label: "上线状态", //( 0-下线 1-上线)
+      prop: "status",
+      templet: "custom",
+    },
+    {
+      label: "发放数量", //, -1 - 则表示不限制
+      prop: "totalCount",
+    },
+    {
+      label: "发放时间",
+      prop: "validTime",
+      formatter: (row: any) => {
+        // 获取时分和星期几数据(注意:这里是从row对象中获取原始字段)
+        const hour = row.validTimeHour || "";
+        const weeks = row.validTimeWeeks || "";
+        // 如果没有数据,返回空字符串
+        if (!hour && !weeks) {
+          return "";
+        }
+        // 解析星期几数据
+        let weekText = "";
+        if (weeks) {
+          const weekArray = weeks.split(",").map((w) => w.trim());
+          const weekMap: { [key: string]: string } = {
+            "1": "星期一",
+            "2": "星期二",
+            "3": "星期三",
+            "4": "星期四",
+            "5": "星期五",
+            "6": "星期六",
+            "7": "星期日",
+          };
+
+          const weekNames = weekArray.map((w) => weekMap[w]).filter((w) => w);
+          if (weekNames.length > 0) {
+            weekText = `(${weekNames.join(",")})`;
+          }
+        }
+        // 返回拼接后的格式:17:30(星期二,星期三)
+        return `${hour}${weekText}`;
+      },
+    },
+    {
+      label: "设置满多少金额可用(元)", //,单位:分
+      prop: "usePrice",
+    },
+    {
+      label: "优惠金额(元)", //,单位:分
+      prop: "discountPrice",
+      // formatter: (row: any) => {
+      //   // 转换为元并保留两位小数
+      //   return (row.discountPrice / 100).toFixed(2);
+      // },
+    },
+    { label: "创建时间", prop: "createTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: ["edit", "delete"],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<PromotionCouponTemplateForm> = reactive({
+  // 权限前缀
+  permPrefix: "business:promotion-coupon-template",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 160,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "优惠劵名称",
+      },
+      rules: [{ required: true, message: "优惠劵名称不能为空", trigger: "blur" }],
+      label: "优惠劵名称",
+      prop: "name",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "描述",
+      },
+      rules: [{ required: true, message: "描述不能为空", trigger: "blur" }],
+      label: "描述",
+      prop: "description",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "领取后有效期",
+      },
+      rules: [{ required: true, message: "领取后有效期", trigger: "blur" }],
+      label: "领取后有效期(天)",
+      prop: "failureTime",
+    },
+    {
+      type: "switch",
+      attrs: {
+        activeText: "启用",
+        inactiveText: "禁用",
+        activeValue: 1,
+        inactiveValue: 2,
+      },
+      initialValue: 1,
+      label: "上线状态", // ( 0-下线 1-上线)
+      prop: "status",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "发放数量", //, -1 - 则表示不限制
+      },
+      rules: [{ required: true, message: "发放数量", trigger: "blur" }],
+      label: "发放数量",
+      prop: "totalCount",
+    },
+    {
+      type: "input-tag",
+      label: "领取时间-星期",
+      prop: "validTimeWeeks",
+      attrs: {
+        placeholder: "请选择星期几(可多选)",
+        config: {
+          buttonAttrs: { btnText: "输入数字,如:周一输入1" },
+        },
+      },
+      rules: [{ required: true, message: "日期不能为空", trigger: "blur" }],
+    },
+    {
+      type: "time-select",
+      attrs: {
+        placeholder: "请选择时分",
+        clearable: true,
+        format: "HH:mm",
+        valueFormat: "HH:mm",
+      },
+      rules: [{ required: true, message: "时间不能为空", trigger: "blur" }],
+      label: "领取时间-时分",
+      prop: "validTimeHour",
+    },
+    {
+      type: "input-number",
+      attrs: {
+        placeholder: "设置满多少金额可用", //,单位:分
+      },
+      rules: [{ required: true, message: "设置满多少金额可用不能为空", trigger: "blur" }],
+      label: "设置满多少金额可用",
+      prop: "usePrice",
+    },
+    {
+      type: "input-number",
+      attrs: {
+        placeholder: "优惠金额",
+      },
+      rules: [{ required: true, message: "优惠金额不能为空", trigger: "blur" }],
+      label: "优惠金额",
+      prop: "discountPrice",
+    },
+  ],
+  // 提交函数
+  beforeSubmit: (data: PromotionCouponTemplateForm) => {
+    // 将星期数组转换为逗号分隔的字符串
+    if (Array.isArray(data.validTimeWeeks)) {
+      data.validTimeWeeks = data.validTimeWeeks.join(",");
+    }
+
+    // 删除金额转换为分的逻辑
+    return data;
+  },
+  formAction(data: any) {
+    if (data.id) {
+      // 编辑
+      return PromotionCouponTemplateAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return PromotionCouponTemplateAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<PromotionCouponTemplateForm> = reactive({
+  permPrefix: "business:promotion-coupon-template",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  beforeSubmit: (data: PromotionCouponTemplateForm) => {
+    // 将星期数组转换为逗号分隔的字符串
+    if (Array.isArray(data.validTimeWeeks)) {
+      data.validTimeWeeks = data.validTimeWeeks.join(",");
+    }
+
+    // 删除金额转换为分的逻辑
+    return data;
+  },
+  formAction(data: any) {
+    return PromotionCouponTemplateAPI.update(data.id as string, data);
+  },
+  form: {
+    labelWidth: 160,
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      const formData = await PromotionCouponTemplateAPI.getFormData(data.row.id);
+      // 将逗号分隔的星期字符串转换为数组格式以适应input-tag组件
+      if (formData.validTimeWeeks && typeof formData.validTimeWeeks === "string") {
+        formData.validTimeWeeks = formData.validTimeWeeks.split(",");
+      } else if (!formData.validTimeWeeks) {
+        formData.validTimeWeeks = [];
+      }
+      return formData;
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>

+ 276 - 0
src/views/operationsManage/recharge-level/index.vue

@@ -0,0 +1,276 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    ></page-content>
+
+    <!-- 新增 -->
+    <page-modal
+      ref="addModalRef"
+      :modal-config="addModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "RechargeLevel" });
+
+import RechargeLevelAPI, {
+  RechargeLevelForm,
+  RechargeLevelPageQuery,
+} from "@/api/operationsManage/recharge-level-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "business:recharge-level",
+  formItems: [
+    {
+      type: "input",
+      label: "充电券名称",
+      prop: "name",
+      attrs: {
+        placeholder: "充电券名称",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "input",
+      label: "充值金额",
+      prop: "money",
+      attrs: {
+        placeholder: "充值金额",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "select",
+      label: "状态 ", //(0-不可用 1-可用)
+      prop: "status",
+      attrs: {
+        placeholder: "状态", // (0-不可用 1-可用)
+        clearable: true,
+        style: { width: "200px" },
+      },
+      options: [
+        {
+          label: "不可用",
+          value: 0,
+        },
+        {
+          label: "可用",
+          value: 1,
+        },
+      ],
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<RechargeLevelPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "business:recharge-level",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: RechargeLevelAPI.getPage,
+  // 删除接口
+  deleteAction: RechargeLevelAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "充电券名称", prop: "name" },
+    { label: "充值金额", prop: "money" },
+    { label: "创建时间", prop: "createTime" },
+    { label: "状态 (0-不可用 1-可用)", prop: "status" },
+    { label: "充值提示", prop: "tips" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: ["edit", "delete"],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<RechargeLevelForm> = reactive({
+  // 权限前缀
+  permPrefix: "business:recharge-level",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电券名称",
+      },
+      label: "充电券名称",
+      prop: "name",
+      rules: [
+        {
+          required: true,
+          message: "请输入充电券名称",
+          trigger: "blur",
+        },
+      ],
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充值金额",
+      },
+      label: "充值金额",
+      prop: "money",
+      rules: [
+        {
+          required: true,
+          message: "请输入充值金额",
+          trigger: "blur",
+        },
+      ],
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充值提示",
+      },
+      label: "充值提示",
+      prop: "tips",
+      rules: [
+        {
+          required: true,
+          message: "请输入充值提示",
+          trigger: "blur",
+        },
+      ],
+    },
+    {
+      type: "switch",
+      attrs: {
+        activeText: "启用",
+        inactiveText: "禁用",
+        activeValue: 1,
+        inactiveValue: 0,
+      },
+      initialValue: 1,
+      label: "状态", // (0-不可用 1-可用)
+      prop: "status",
+    },
+  ],
+  // 提交函数
+  formAction: (data: RechargeLevelForm) => {
+    if (data.id) {
+      // 编辑
+      return RechargeLevelAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return RechargeLevelAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<RechargeLevelForm> = reactive({
+  permPrefix: "business:recharge-level",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return RechargeLevelAPI.update(data.id as string, data);
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await RechargeLevelAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>

+ 352 - 0
src/views/operationsManage/third-party-info/index.vue

@@ -0,0 +1,352 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    >
+      <template #status="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="status" v-bind="scope.attrs" />
+      </template>
+    </page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #status="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="status" />
+      </template>
+      <template #ecAttach="scope">
+        <div v-if="scope.row[scope.prop]">
+          <el-image 
+            v-if="isImageFile(scope.row[scope.prop])"
+            :src="scope.row[scope.prop]"
+            :preview-src-list="[scope.row[scope.prop]]"
+            preview-teleported
+            style="width: 50px; height: 50px; cursor: pointer;"
+            fit="cover"
+            lazy
+          />
+          <el-link 
+            v-else
+            :href="scope.row[scope.prop]" 
+            target="_blank"
+            type="primary"
+            :underline="false"
+          >
+            {{ getFileName(scope.row[scope.prop]) }}
+          </el-link>
+        </div>
+        <span v-else>无</span>
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
+      <template #status="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="status" v-bind="scope.attrs" />
+      </template>
+      <template #ecAttach="scope">
+        <FileUpload 
+          v-model="fileList[scope.prop]" 
+          :limit="1" 
+          @update:model-value="handleFileUploadChange($event, scope.formData, scope.prop)"
+        />
+      </template>
+    </page-modal>
+
+    <!-- 编辑 -->
+    <page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
+      <template #status="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="status" v-bind="scope.attrs" />
+      </template>
+      <template #ecAttach="scope">
+        <FileUpload 
+          v-model="fileList[scope.prop]" 
+          :limit="1" 
+          @update:model-value="handleFileUploadChange($event, scope.formData, scope.prop)"
+        />
+      </template>
+    </page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "ThirdPartyInfo" });
+
+import ThirdPartyInfoAPI, { ThirdPartyInfoForm, ThirdPartyInfoPageQuery } from "@/api/operationsManage/third-party-info-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+import FileUpload from "@/components/Upload/FileUpload.vue";
+import { ref } from "vue";
+import type { FileInfo } from "@/api/file-api";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 文件列表
+const fileList = ref<Record<string, FileInfo[]>>({});
+
+// 处理文件上传变化
+const handleFileUploadChange = (files: any[], formData: any, prop: string) => {
+  if (files && files.length > 0) {
+    // 取第一个文件的URL作为值
+    formData[prop] = files[0].url;
+  } else {
+    formData[prop] = undefined;
+  }
+};
+
+// 判断是否为图片文件
+const isImageFile = (url: string) => {
+  if (!url) return false;
+  const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
+  const ext = url.split('.').pop()?.toLowerCase() || '';
+  return imageExtensions.includes(ext);
+};
+
+// 获取文件名
+const getFileName = (url: string) => {
+  if (!url) return '';
+  const fileName = url.split('/').pop() || '';
+  return fileName || '下载文件';
+};
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "system:third-party-info",
+  formItems: [
+    {
+      type: "input",
+      label: "对接商名称",
+      prop: "ecName",
+      attrs: {
+        placeholder: "对接商名称",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "custom",
+      slotName: "status",
+      label: "状态", //0 正常 1 停用
+      prop: "status",
+      attrs: {
+        placeholder: "状态",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<ThirdPartyInfoPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "system:third-party-info",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: ThirdPartyInfoAPI.getPage,
+  // 删除接口
+  deleteAction: ThirdPartyInfoAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "对接商名称", prop: "ecName" },
+    { label: "联系人", prop: "contactName" },
+    { label: "联系人电话", prop: "contactPhone" },
+    {
+      label: "协议附件",
+      prop: "ecAttach",
+      templet: "custom",
+      slotName: "ecAttach"
+    },
+    { label: "对接编号", prop: "appId" },
+    { label: "授权码", prop: "authCode" },
+    {
+      label: "状态", //0 正常 1 停用
+      prop: "status",
+      templet: "custom",
+      slotName: "status"
+    },
+    { label: "备注", prop: "remark" },
+    { label: "创建时间", prop: "createTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: ["edit", "delete"],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<ThirdPartyInfoForm> = reactive({
+  // 权限前缀
+  permPrefix: "system:third-party-info",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "对接商名称"
+      },
+      label: "对接商名称",
+      prop: "ecName",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "联系人"
+      },
+      label: "联系人",
+      prop: "contactName",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "联系人电话"
+      },
+      label: "联系人电话",
+      prop: "contactPhone",
+    },
+    {
+      type: "custom",
+      label: "协议附件",
+      prop: "ecAttach",
+      slotName: "ecAttach",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "对接编号"
+      },
+      label: "对接编号",
+      prop: "appId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "授权码"
+      },
+      label: "授权码",
+      prop: "authCode",
+    },
+    {
+      type: "custom",
+      label: "状态", //0 正常 1 停用
+      prop: "status",
+      slotName: "status",
+      attrs: {
+        placeholder: "状态",
+        style: { width: "100%" }
+      },
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "备注"
+      },
+      label: "备注",
+      prop: "remark",
+    },
+  ],
+  // 提交函数
+  formAction: (data: ThirdPartyInfoForm) => {
+    if (data.id) {
+      // 编辑
+      return ThirdPartyInfoAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return ThirdPartyInfoAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<ThirdPartyInfoForm> = reactive({
+  permPrefix: "system:third-party-info",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return ThirdPartyInfoAPI.update(data.id as string, data);
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await ThirdPartyInfoAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+
+</script>

+ 278 - 0
src/views/operationsManage/user-exchange-integral-rule/index.vue

@@ -0,0 +1,278 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #status="scope">
+        <el-tag :type="scope.row.status == 1 ? 'success' : 'warning'">
+          {{ scope.row.status == 1 ? "启用" : "停用" }}
+        </el-tag>
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal
+      ref="addModalRef"
+      :modal-config="addModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "UserExchangeIntegralRule" });
+
+import UserExchangeIntegralRuleAPI, {
+  UserExchangeIntegralRuleForm,
+  UserExchangeIntegralRulePageQuery,
+} from "@/api/operationsManage/user-exchange-integral-rule-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+import { ElMessageBox, ElMessage } from "element-plus";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "business:user-exchange-integral-rule",
+  formItems: [
+    {
+      type: "select",
+      label: "是否启用", //(0-停用 1-启用)
+      prop: "status",
+      attrs: {
+        placeholder: "是否启用", //(0-停用 1-启用)
+        clearable: true,
+        style: { width: "200px" },
+      },
+      options: [
+        { label: "停用", value: 0 },
+        { label: "启用", value: 1 },
+      ],
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<UserExchangeIntegralRulePageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "business:user-exchange-integral-rule",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: UserExchangeIntegralRuleAPI.getPage,
+  // 删除接口
+  deleteAction: UserExchangeIntegralRuleAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "兑换度数基数", prop: "degree" },
+    { label: "1度电获得的积分", prop: "gainIntegral" },
+    {
+      label: "是否启用", //(0-停用 1-启用)
+      prop: "status",
+      templet: "custom",
+    },
+    { label: "创建时间", prop: "createTime" },
+    { label: "更新时间", prop: "updateTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: [
+        {
+          name: "switchs",
+          text: (row: any) => (row.status === 1 ? "停用" : "启用"),
+          attrs: (row: any) => ({
+            type: row.status === 1 ? "danger" : "success",
+            link: true,
+            size: "small",
+            icon: "open",
+          }),
+        },
+        "edit",
+        "delete",
+      ],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<UserExchangeIntegralRuleForm> = reactive({
+  // 权限前缀
+  permPrefix: "business:user-exchange-integral-rule",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "兑换度数基数", //(按照多少度进行兑换)
+      },
+      label: "兑换度数基数", //(按照多少度进行兑换)
+      prop: "degree",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "1度电获得的积分", //(如1度1积分)
+      },
+      label: "1度电获得的积分", //(如1度1积分)
+      prop: "gainIntegral",
+    },
+    {
+      type: "switch",
+      attrs: {
+        activeText: "启用",
+        inactiveText: "禁用",
+        activeValue: 1,
+        inactiveValue: 0,
+      },
+      initialValue: 1,
+      label: "是否启用", //(0-停用 1-启用)
+      prop: "status",
+    },
+  ],
+  // 提交函数
+  formAction: (data: UserExchangeIntegralRuleForm) => {
+    if (data.id) {
+      // 编辑
+      return UserExchangeIntegralRuleAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return UserExchangeIntegralRuleAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<UserExchangeIntegralRuleForm> = reactive({
+  permPrefix: "business:user-exchange-integral-rule",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return UserExchangeIntegralRuleAPI.update(data.id as string, data);
+  },
+  formItems: [
+    {
+      type: "switch",
+      attrs: {
+        activeText: "启用",
+        inactiveText: "禁用",
+        activeValue: 1,
+        inactiveValue: 0,
+      },
+      initialValue: 1,
+      label: "是否启用", //(0-停用 1-启用)
+      prop: "status",
+    },
+  ], // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await UserExchangeIntegralRuleAPI.getFormData(data.row.id);
+    });
+  }
+  if (data.name === "switchs") {
+    ElMessageBox.confirm(
+      `只能启用一条规则,确认要${data.row.status === 1 ? "停用" : "启用"}该兑换规则吗?`,
+      "提示",
+      {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }
+    )
+      .then(() => {
+        UserExchangeIntegralRuleAPI.enable(data.row.id).then(() => {
+          ElMessage.success(`${data.row.status === 1 ? "停用" : "启用"}成功`);
+          contentRef.value?.handleRefresh();
+        });
+      })
+      .catch(() => {
+        // ElMessage.info(err);
+      });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>

+ 675 - 0
src/views/orderManage/charge-order-info/index.vue

@@ -0,0 +1,675 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    >
+      <template #orderType="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="charge_order_type" v-bind="scope.attrs" />
+      </template>
+      <template #status="scope">
+        <Dict
+          v-model="scope.formData[scope.prop]"
+          code="charge_order_status"
+          v-bind="scope.attrs"
+        />
+      </template>
+      <template #stopType="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="stop_type" v-bind="scope.attrs" />
+      </template>
+      <template #maspStatus="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="masp_status" v-bind="scope.attrs" />
+      </template>
+    </page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #orderType="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="charge_order_type" />
+      </template>
+      <template #status="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="charge_order_status" />
+      </template>
+      <template #stopType="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="stop_type" />
+      </template>
+      <template #maspStatus="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="masp_status" />
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
+      <template #orderType="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="charge_order_type" v-bind="scope.attrs" />
+      </template>
+      <template #status="scope">
+        <Dict
+          v-model="scope.formData[scope.prop]"
+          code="charge_order_status"
+          v-bind="scope.attrs"
+        />
+      </template>
+      <template #stopType="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="stop_type" v-bind="scope.attrs" />
+      </template>
+      <template #maspStatus="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="masp_status" v-bind="scope.attrs" />
+      </template>
+    </page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    >
+      <template #orderType="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="charge_order_type" v-bind="scope.attrs" />
+      </template>
+      <template #status="scope">
+        <Dict
+          v-model="scope.formData[scope.prop]"
+          code="charge_order_status"
+          v-bind="scope.attrs"
+        />
+      </template>
+      <template #stopType="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="stop_type" v-bind="scope.attrs" />
+      </template>
+      <template #maspStatus="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="masp_status" v-bind="scope.attrs" />
+      </template>
+    </page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "ChargeOrderInfo" });
+
+import ChargeOrderInfoAPI, {
+  ChargeOrderInfoForm,
+  ChargeOrderInfoPageQuery,
+} from "@/api/orderManage/charge-order-info-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "orderManage:charge-order-info",
+  formItems: [
+    {
+      type: "custom",
+      slotName: "orderType",
+      label: "订单类型", // 1 个人订单 2 集团订单
+      prop: "orderType",
+      attrs: {
+        placeholder: "订单类型",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "input",
+      label: "充电桩编号",
+      prop: "equipmentId",
+      attrs: {
+        placeholder: "充电桩编号",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "input",
+      label: "充电订单号",
+      prop: "chargeOrderNo",
+      attrs: {
+        placeholder: "充电订单号",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "custom",
+      slotName: "status",
+      label: "状态", //0待启动 1 充电中 2 结算中 3 已完成, 5未成功充电
+      prop: "status",
+      attrs: {
+        placeholder: "状态",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "custom",
+      slotName: "stopType",
+      label: "停止类型", //1 主动停止 2 充满停止 3 余额不足停止, 4电桩按钮停止
+      prop: "stopType",
+      attrs: {
+        placeholder: "停止类型",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "input",
+      label: "请求启动充电的手机号",
+      prop: "phoneNum",
+      attrs: {
+        placeholder: "请求启动充电的手机号",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "custom",
+      slotName: "maspStatus",
+      label: "补缴状态", // 0.无需补缴  1.待补缴  2.已补缴
+      prop: "maspStatus",
+      attrs: {
+        placeholder: "补缴状态",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<ChargeOrderInfoPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "orderManage:charge-order-info",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: ChargeOrderInfoAPI.getPage,
+  // 删除接口
+  deleteAction: ChargeOrderInfoAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  // toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    {
+      label: "订单类型", // 1 个人订单 2 集团订单
+      prop: "orderType",
+      templet: "custom",
+      slotName: "orderType",
+    },
+    { label: "充电桩编号", prop: "equipmentId" },
+    { label: "充电订单号", prop: "chargeOrderNo" },
+    { label: "充电开始时间", prop: "startTime" },
+    { label: "充电结束时间", prop: "endTime" },
+    {
+      label: "充电时间", //:秒
+      prop: "chargeTime",
+    },
+    {
+      label: "状态", //0待启动 1 充电中 2 结算中 3 已完成, 5未成功充电
+      prop: "status",
+      templet: "custom",
+      slotName: "status",
+    },
+    { label: "充电消费总额", prop: "thirdPartyTotalCost" },
+    { label: "充电服务费", prop: "thirdPartyServerfee" },
+    { label: "充电金额", prop: "thirdPartyElecfee" },
+    {
+      label: "实际充电度数", //(单位:0.001 kw/h)
+      prop: "totalCharge",
+    },
+    { label: "平台实际收取金额", prop: "realCost" },
+    { label: "平台总服务费", prop: "realServiceCost" },
+    {
+      label: "停止类型", //1 主动停止 2 充满停止 3 余额不足停止, 4电桩按钮停止
+      prop: "stopType",
+      templet: "custom",
+      slotName: "stopType",
+    },
+    { label: "请求启动充电的手机号", prop: "phoneNum" },
+    {
+      label: "车牌号", //( 停车减免必传,格式确保正确)
+      prop: "plateNum",
+    },
+    { label: "充电结束原因", prop: "stopReason" },
+    {
+      label: "推送", //:充电明细信息
+      prop: "chargeDetails",
+    },
+    { label: "充电站id", prop: "thirdPartyStationId" },
+    { label: "预充值金额", prop: "preAmt" },
+    { label: "平台预扣服务费", prop: "realPredictServiceCost" },
+    {
+      label: "补缴金额", //(智停)
+      prop: "maspAmount",
+    },
+    { label: "平台补缴金额", prop: "maspRealAmount" },
+    { label: "需要补缴的总金额", prop: "totalMaspMoney" },
+    {
+      label: "补缴状态", //  0.无需补缴  1.待补缴  2.已补缴
+      prop: "maspStatus",
+      templet: "custom",
+      slotName: "maspStatus",
+    },
+    { label: "补缴时间", prop: "maspTime" },
+    { label: "补缴描述", prop: "maspDesc" },
+    { label: "优惠金额", prop: "discountMoney" },
+    { label: "优惠描述", prop: "discountDesc" },
+    { label: "优惠活动ID", prop: "discountInfoId" },
+    { label: "服务费", prop: "realThirdCost" },
+    { label: "企业id", prop: "firmId" },
+    { label: "企业专享优惠价", prop: "firmPrice" },
+    { label: "优惠券金额", prop: "couponPrice" },
+    { label: "备注", prop: "remark" },
+    { label: "创建时间", prop: "createTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: ["edit", "delete"],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<ChargeOrderInfoForm> = reactive({
+  // 权限前缀
+  permPrefix: "orderManage:charge-order-info",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "custom",
+      label: "订单类型",
+      prop: "orderType",
+      slotName: "orderType",
+      attrs: {
+        placeholder: "订单类型",
+        style: { width: "100%" },
+      },
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电桩编号",
+      },
+      label: "充电桩编号",
+      prop: "equipmentId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电订单号",
+      },
+      label: "充电订单号",
+      prop: "chargeOrderNo",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电开始时间",
+      },
+      label: "充电开始时间",
+      prop: "startTime",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电结束时间",
+      },
+      label: "充电结束时间",
+      prop: "endTime",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电时间",
+      },
+      label: "充电时间(秒)",
+      prop: "chargeTime",
+    },
+    {
+      type: "custom",
+      label: "状态",
+      prop: "status",
+      slotName: "status",
+      attrs: {
+        placeholder: "状态",
+        style: { width: "100%" },
+      },
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电消费总额",
+      },
+      label: "充电消费总额",
+      prop: "thirdPartyTotalCost",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电服务费",
+      },
+      label: "充电服务费",
+      prop: "thirdPartyServerfee",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电金额",
+      },
+      label: "充电金额",
+      prop: "thirdPartyElecfee",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "实际充电度数",
+      },
+      label: "实际充电度数", //(单位:0.001 kw/h)
+      prop: "totalCharge",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "平台实际收取金额",
+      },
+      label: "平台实际收取金额",
+      prop: "realCost",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "平台总服务费",
+      },
+      label: "平台总服务费",
+      prop: "realServiceCost",
+    },
+    {
+      type: "custom",
+      label: "停止类型", //1 主动停止 2 充满停止 3 余额不足停止, 4电桩按钮停止
+      prop: "stopType",
+      slotName: "stopType",
+      attrs: {
+        placeholder: "停止类型",
+        style: { width: "100%" },
+      },
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "请求启动充电的手机号",
+      },
+      label: "请求启动充电的手机号",
+      prop: "phoneNum",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "车牌号",
+      },
+      label: "车牌号", //( 停车减免必传,格式确保正确)
+      prop: "plateNum",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电结束原因",
+      },
+      label: "充电结束原因",
+      prop: "stopReason",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "推送",
+      },
+      label: "推送", //:充电明细信息
+      prop: "chargeDetails",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "充电站id",
+      },
+      label: "充电站id",
+      prop: "thirdPartyStationId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "预充值金额",
+      },
+      label: "预充值金额",
+      prop: "preAmt",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "平台预扣服务费",
+      },
+      label: "平台预扣服务费",
+      prop: "realPredictServiceCost",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "补缴金额",
+      },
+      label: "补缴金额", //(智停)
+      prop: "maspAmount",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "平台补缴金额",
+      },
+      label: "平台补缴金额",
+      prop: "maspRealAmount",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "需要补缴的总金额",
+      },
+      label: "需要补缴的总金额",
+      prop: "totalMaspMoney",
+    },
+    {
+      type: "custom",
+      label: "补缴状态", //  0.无需补缴  1.待补缴  2.已补缴
+      prop: "maspStatus",
+      slotName: "maspStatus",
+      attrs: {
+        placeholder: "补缴状态",
+        style: { width: "100%" },
+      },
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "补缴时间",
+      },
+      label: "补缴时间",
+      prop: "maspTime",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "补缴描述",
+      },
+      label: "补缴描述", //(默认:系统扣除)
+      prop: "maspDesc",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "优惠金额",
+      },
+      label: "优惠金额",
+      prop: "discountMoney",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "优惠描述",
+      },
+      label: "优惠描述", //(前端展示为优惠描述+优惠金额)
+      prop: "discountDesc",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "优惠活动ID",
+      },
+      label: "优惠活动ID",
+      prop: "discountInfoId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "按平台计费规则,实际收取的服务费",
+      },
+      label: "按平台计费规则,实际收取的服务费",
+      prop: "realThirdCost",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "企业id",
+      },
+      label: "企业id",
+      prop: "firmId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "企业专享优惠价",
+      },
+      label: "企业专享优惠价",
+      prop: "firmPrice",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "优惠券金额",
+      },
+      label: "优惠券金额",
+      prop: "couponPrice",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "备注",
+      },
+      label: "备注",
+      prop: "remark",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "创建时间",
+      },
+      label: "创建时间",
+      prop: "createTime",
+    },
+  ],
+  // 提交函数
+  formAction: (data: ChargeOrderInfoForm) => {
+    if (data.id) {
+      // 编辑
+      return ChargeOrderInfoAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return ChargeOrderInfoAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<ChargeOrderInfoForm> = reactive({
+  permPrefix: "orderManage:charge-order-info",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return ChargeOrderInfoAPI.update(data.id as string, data);
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await ChargeOrderInfoAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>

+ 323 - 0
src/views/orderManage/user-order-info/index.vue

@@ -0,0 +1,323 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    >
+      <template #orderStatus="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="order_status" v-bind="scope.attrs" />
+      </template>
+    </page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #orderStatus="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="order_status" />
+      </template>
+      <template #orderType="scope">
+        <DictLabel v-model="scope.row[scope.prop]" code="order_type" />
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
+      <template #orderStatus="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="order_status" v-bind="scope.attrs" />
+      </template>
+      <template #orderType="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="order_type" v-bind="scope.attrs" />
+      </template>
+    </page-modal>
+
+    <!-- 编辑 -->
+    <page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
+      <template #orderStatus="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="order_status" v-bind="scope.attrs" />
+      </template>
+      <template #orderType="scope">
+        <Dict v-model="scope.formData[scope.prop]" code="order_type" v-bind="scope.attrs" />
+      </template>
+    </page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "UserOrderInfo" });
+
+import UserOrderInfoAPI, { UserOrderInfoForm, UserOrderInfoPageQuery } from "@/api/orderManage/user-order-info-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "orderManage:user-order-info",
+  formItems: [
+    {
+      type: "custom",
+      slotName: "orderStatus",
+      label: "订单状态", // 1 待支付  2 已支付 3 已取消 4 已退款
+      prop: "orderStatus",
+      attrs: {
+        placeholder: "订单状态",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "input",
+      label: "订单编号",
+      prop: "orderNo",
+      attrs: {
+        placeholder: "订单编号",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    }
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<UserOrderInfoPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "orderManage:user-order-info",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: UserOrderInfoAPI.getPage,
+  // 删除接口
+  deleteAction: UserOrderInfoAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  // toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "订单编号", prop: "orderNo" },
+    { label: "订单金额", prop: "orderMoney" },
+    { label: "订单剩余金额", prop: "lastMoney" },
+    { label: "支付时间", prop: "payTime" },
+    { label: "第三方支付单号", prop: "outTradeNo" },
+    {
+      label: "订单状态", // 1 待支付  2 已支付 3 已取消 4 已退款
+      prop: "orderStatus",
+      templet: "custom",
+      slotName: "orderStatus"
+    },
+    {
+      label: "订单类型", //1 微信 2 第三方
+      prop: "orderType",
+      templet: "custom",
+      slotName: "orderType"
+    },
+    { label: "退款金额", prop: "refundMoney" },
+    { label: "退款时间", prop: "refundTime" },
+    { label: "备注", prop: "remark" },
+    { label: "创建时间", prop: "createTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: ["edit", "delete"],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<UserOrderInfoForm> = reactive({
+  // 权限前缀
+  permPrefix: "orderManage:user-order-info",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "订单编号"
+      },
+      label: "订单编号",
+      prop: "orderNo",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "订单金额"
+      },
+      label: "订单金额",
+      prop: "orderMoney",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "订单剩余金额"
+      },
+      label: "订单剩余金额",
+      prop: "lastMoney",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "支付时间"
+      },
+      label: "支付时间",
+      prop: "payTime",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "第三方支付单号"
+      },
+      label: "第三方支付单号",
+      prop: "outTradeNo",
+    },
+    {
+      type: "custom",
+      label: "订单状态", // 1 待支付  2 已支付 3 已取消 4 已退款
+      prop: "orderStatus",
+      slotName: "orderStatus",
+      attrs: {
+        placeholder: "订单状态",
+        style: { width: "100%" }
+      },
+    },
+    {
+      type: "custom",
+      label: "订单类型", // 1 微信 2 第三方
+      prop: "orderType",
+      slotName: "orderType",
+      attrs: {
+        placeholder: "订单类型",
+        style: { width: "100%" }
+      },
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "退款金额"
+      },
+      label: "退款金额",
+      prop: "refundMoney",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "退款时间"
+      },
+      label: "退款时间",
+      prop: "refundTime",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "备注"
+      },
+      label: "备注",
+      prop: "remark",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "创建时间"
+      },
+      label: "创建时间",
+      prop: "createTime",
+    },
+  ],
+  // 提交函数
+  formAction: (data: UserOrderInfoForm) => {
+    if (data.id) {
+      // 编辑
+      return UserOrderInfoAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return UserOrderInfoAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<UserOrderInfoForm> = reactive({
+  permPrefix: "orderManage:user-order-info",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return UserOrderInfoAPI.update(data.id as string, data);
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await UserOrderInfoAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+
+</script>

+ 293 - 0
src/views/toBManage/firm-info/index.vue

@@ -0,0 +1,293 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #status="scope">
+        <el-tag :type="scope.row.status == 0 ? 'warning' : 'success'">
+          {{ scope.row.status == 0 ? "已下线" : "上线中" }}
+        </el-tag>
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal
+      ref="addModalRef"
+      :modal-config="addModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+    <el-dialog v-model="dialogTableVisible" title="查看邀请码" width="500px">
+      <div class="relative">
+        <div>
+          <el-image :src="qrCode_bg" fit="cover" style="width: 100%; height: 100%" />
+        </div>
+        <div class="absolute top-1.42/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
+          <qr-code
+            :value="'https://cd.api.zswlgz.com/deviceid?frimId=' + firmId"
+            width="170"
+            :color="{ dark: '#1D6AEC' }"
+            margin="3"
+          />
+        </div>
+      </div>
+      <template #footer>
+        <el-button type="primary" :loading="downloadLoading" @click="dowloadQrCode">下载</el-button>
+        <el-button @click="dialogTableVisible = false">取消</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "FirmInfo" });
+import qrCode_bg from "@/assets/images/qrCode_bg.jpg";
+import qrCode from "vue-qrcode";
+import FirmInfoAPI, { FirmInfoForm, FirmInfoPageQuery } from "@/api/toBManage/firm-info-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+import html2canvas from "html2canvas";
+import { ElMessage } from "element-plus";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "business:firm-info",
+  formItems: [
+    {
+      type: "input",
+      label: "企业名称",
+      prop: "name",
+      attrs: {
+        placeholder: "企业名称",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "select",
+      label: "状态", // 0 已下线  1 上线中
+      prop: "status",
+      attrs: {
+        placeholder: "请输入状态",
+        clearable: true,
+        style: { width: "200px" },
+      },
+      options: [
+        { label: "已下线", value: 0 },
+        { label: "上线中", value: 1 },
+      ],
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<FirmInfoPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "business:firm-info",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: FirmInfoAPI.getPage,
+  // 删除接口
+  deleteAction: FirmInfoAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "企业名称", prop: "name" },
+    { label: "员工数", prop: "empNum" },
+    {
+      label: "状态", // 0 已下线  1 上线中
+      prop: "status",
+      templet: "custom",
+    },
+    { label: "创建时间", prop: "createTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      templet: "tool",
+      operat: [
+        {
+          name: "qrCode",
+          text: "查看邀请码",
+          attrs: {
+            type: "success",
+            icon: "Setting",
+          },
+        },
+        "edit",
+        "delete",
+      ],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<FirmInfoForm> = reactive({
+  // 权限前缀
+  permPrefix: "business:firm-info",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "企业名称",
+      },
+      label: "企业名称",
+      prop: "name",
+    },
+    {
+      type: "switch",
+      attrs: {
+        activeValue: 1,
+        inactiveValue: 0,
+      },
+      initialValue: 1,
+      label: "状态", // 0 已下线  1 上线中
+      prop: "status",
+    },
+  ],
+  // 提交函数
+  formAction: (data: FirmInfoForm) => {
+    if (data.id) {
+      // 编辑
+      return FirmInfoAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return FirmInfoAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<FirmInfoForm> = reactive({
+  permPrefix: "business:firm-info",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return FirmInfoAPI.update(data.id as string, data);
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const dialogTableVisible = ref(false);
+const firmId = ref();
+const firmName= ref();
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await FirmInfoAPI.getFormData(data.row.id);
+    });
+  }
+  if (data.name === "qrCode") {
+    dialogTableVisible.value = true;
+    firmId.value = parseInt(data.row.id);
+    firmName.value = data.row.name;
+  }
+};
+
+// 二维码海报下载
+const downloadLoading = ref(false);
+const dowloadQrCode = () => {
+  downloadLoading.value = true;
+  const qrCodeElement = document.querySelector(".relative") as HTMLElement;
+  if (qrCodeElement) {
+    html2canvas(qrCodeElement)
+      .then((canvas) => {
+        const link = document.createElement("a");
+        link.download = `${firmName.value}_${firmId.value}.png`;
+        link.href = canvas.toDataURL("image/png");
+        link.click();
+        downloadLoading.value = false;
+        ElMessage.success("下载成功!");
+      })
+      .catch((error) => {
+        downloadLoading.value = false;
+        ElMessage.error("下载失败,请重试!");
+        console.error("下载失败:", error);
+      });
+  } else {
+    downloadLoading.value = false;
+    ElMessage.error("未找到二维码元素!");
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>

+ 314 - 0
src/views/toBManage/user-firm/index.vue

@@ -0,0 +1,314 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #type="scope">
+        <el-tag :type="scope.row.type == 1 ? 'warning' : 'success'">
+          {{ scope.row.type == 1 ? "管理员" : "普通员工" }}
+        </el-tag>
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
+      <template #type="{ formData }">
+        <el-radio-group v-model="formData.type">
+          <!--          1 管理员 2普通员工-->
+          <el-radio :value="1">管理员</el-radio>
+          <el-radio :value="2">普通员工</el-radio>
+        </el-radio-group>
+      </template>
+    </page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    >
+      <template #type="{ formData }">
+        <el-radio-group v-model="formData.type">
+          <!--          1 管理员 2普通员工-->
+          <el-radio :value="1">管理员</el-radio>
+          <el-radio :value="2">普通员工</el-radio>
+        </el-radio-group>
+      </template>
+    </page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "UserFirm" });
+
+import UserFirmAPI, { UserFirmForm, UserFirmPageQuery } from "@/api/toBManage/user-firm-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "business:user-firm",
+  formItems: [
+    {
+      type: "input",
+      label: "手机号",
+      prop: "phone",
+      attrs: {
+        placeholder: "手机号",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "select",
+      label: "身份", // 1 管理员 2普通员工
+      prop: "type",
+      attrs: {
+        placeholder: "请选择身份", // 1 管理员 2普通员工
+        clearable: true,
+        style: { width: "200px" },
+      },
+      options: [
+        {
+          label: "管理员",
+          value: 1,
+        },
+        {
+          label: "普通员工",
+          value: 2,
+        },
+      ],
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<UserFirmPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "business:user-firm",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: UserFirmAPI.getPage,
+  // 删除接口
+  deleteAction: UserFirmAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  toolbar: ["add", "delete"],
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "用户id", prop: "userId" },
+    { label: "手机号", prop: "phone" },
+    {
+      label: "身份", // 1 管理员 2普通员工
+      prop: "type",
+      templet: "custom",
+    },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: ["edit", "delete"],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<UserFirmForm> = reactive({
+  // 权限前缀
+  permPrefix: "business:user-firm",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "输入手机号",
+      },
+      label: "手机号",
+      prop: "phone",
+    },
+    {
+      type: "select",
+      label: "所属企业",
+      prop: "firmId",
+      attrs: {
+        placeholder: "选择所属企业",
+        style: {
+          width: "200px",
+        },
+      },
+      options: [],
+      async initFn(item) {
+        try {
+          // 调用接口获取充电站数据
+          const response = await UserFirmAPI.getFirmList();
+          if (Array.isArray(response)) {
+            item.options = response.map((item) => ({
+              label: item.name,
+              value: item.id,
+            }));
+          }
+        } catch (error) {
+          console.error("Failed to fetch data:", error);
+        }
+      },
+    },
+    {
+      type: "custom",
+      label: "身份", // 1 管理员 2普通员工
+      prop: "type",
+      rules: [{ required: true, message: "请选择身份", trigger: "change" }],
+      attrs: {
+        placeholder: "身份", // 1 管理员 2普通员工
+      },
+    },
+  ],
+  // 提交函数
+  formAction: (data: UserFirmForm) => {
+    if (data.id) {
+      // 编辑
+      return UserFirmAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return UserFirmAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<UserFirmForm> = reactive({
+  permPrefix: "business:user-firm",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return UserFirmAPI.update(data.id as string, data);
+  },
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "手机号",
+        disabled: true, // 编辑时禁用手机号输入框
+      },
+      label: "手机号",
+      prop: "phone",
+    },
+    {
+      type: "select",
+      label: "所属企业",
+      prop: "firmId",
+      attrs: {
+        placeholder: "选择所属企业",
+        style: {
+          width: "200px",
+        },
+      },
+      options: [],
+      async initFn(item) {
+        try {
+          // 调用接口获取充电站数据
+          const response = await UserFirmAPI.getFirmList();
+          if (Array.isArray(response)) {
+            item.options = response.map((item) => ({
+              label: item.name,
+              value: item.id,
+            }));
+          }
+        } catch (error) {
+          console.error("Failed to fetch data:", error);
+        }
+      },
+    },
+    {
+      type: "custom",
+      label: "身份",
+      prop: "type",
+      rules: [{ required: true, message: "请选择身份", trigger: "change" }],
+      attrs: {
+        placeholder: "身份",
+      },
+    },
+  ], // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "edit") {
+    handleEditClick(data.row, async () => {
+      return await UserFirmAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>

+ 84 - 0
src/views/userManage/user-feedback/components/MultiImageDisplay.vue

@@ -0,0 +1,84 @@
+<!-- 多图显示组件 -->
+<template>
+  <div class="multi-image-display">
+    <template v-if="imageList.length > 0">
+      <div 
+        v-for="(image, index) in imageList" 
+        :key="index" 
+        class="image-item"
+      >
+        <el-image
+          :src="getFullImageUrl(image)"
+          :preview-src-list="imageList.map(img => getFullImageUrl(img))"
+          :initial-index="index"
+          :preview-teleported="true"
+          :style="`width: ${imageWidth}; height: ${imageHeight};`"
+          fit="cover"
+          lazy
+        />
+      </div>
+    </template>
+    <div v-else class="no-image">
+      暂无图片
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { getFullImageUrl } from "@/utils";
+
+interface Props {
+  images?: string;
+  imageWidth?: string;
+  imageHeight?: string;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  images: "",
+  imageWidth: "100px",
+  imageHeight: "100px"
+});
+
+// 将逗号分隔的图片字符串转换为数组
+const imageList = computed(() => {
+  if (!props.images) return [];
+  
+  // 如果是数组格式,直接返回
+  if (Array.isArray(props.images)) {
+    return props.images;
+  }
+  
+  // 如果是逗号分隔的字符串,分割成数组
+  if (typeof props.images === 'string' && props.images.includes(',')) {
+    return props.images
+      .split(',')
+      .map(url => url.trim())
+      .filter(url => url !== '');
+  }
+  
+  // 如果是单个URL,返回单元素数组
+  if (props.images) {
+    return [props.images];
+  }
+  
+  return [];
+});
+</script>
+
+<style scoped lang="scss">
+.multi-image-display {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.image-item {
+  border-radius: 6px;
+  overflow: hidden;
+}
+
+.no-image {
+  color: #999;
+  font-size: 14px;
+}
+</style>

+ 297 - 0
src/views/userManage/user-feedback/index.vue

@@ -0,0 +1,297 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    >
+      <template #replyStatus="scope">
+        <el-tag :type="scope.row.replyStatus == 1 ? 'success' : 'warning'">
+          {{ scope.row.replyStatus == 1 ? "已回复" : "未回复" }}
+        </el-tag>
+      </template>
+    </page-content>
+
+    <!-- 新增 -->
+    <page-modal
+      ref="addModalRef"
+      :modal-config="addModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    >
+      <template #images="{ formData, attrs }">
+        <MultiImageDisplay :images="formData.images" v-bind="attrs" />
+      </template>
+    </page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+import SingleImageUpload from "@/components/Upload/SingleImageUpload.vue";
+import MultiImageDisplay from "./components/MultiImageDisplay.vue";
+
+defineOptions({ name: "UserFeedback" });
+
+import UserFeedbackAPI, {
+  UserFeedbackForm,
+  UserFeedbackPageQuery,
+} from "@/api/userManage/user-feedback-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "system:user-feedback",
+  formItems: [
+    {
+      type: "select",
+      label: "问题类型",
+      prop: "type",
+      attrs: {
+        placeholder: "请输入",
+        clearable: true,
+        style: { width: "200px" },
+        options: [
+          {
+            value: "1",
+            label: "投诉吐槽",
+          },
+          {
+            value: "2",
+            label: "功能异常",
+          },
+          {
+            value: "3",
+            label: "体验问题",
+          },
+          {
+            value: "4",
+            label: "功能建议",
+          },
+          {
+            value: "9",
+            label: "其他",
+          },
+        ],
+      },
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<UserFeedbackPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "system:user-feedback",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: UserFeedbackAPI.getPage,
+  // 删除接口
+  deleteAction: UserFeedbackAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    {
+      label: "问题类型", //(1投诉吐槽,2功能异常,3体验问题,4功能建议,9其他 )
+      prop: "type",
+      templet: "list",
+      selectList: {
+        1: "投诉吐槽",
+        2: "功能异常",
+        3: "体验问题",
+        4: "功能建议",
+        9: "其他",
+      },
+    },
+    { label: "问题描述", prop: "description" },
+    {
+      label: "相关图片",
+      prop: "images",
+      templet: "image",
+    },
+    { label: "联系方式(手机号或者邮箱)", prop: "contactWay" },
+    { label: "答复信息", prop: "reply" },
+    { label: "回复时间", prop: "replyTime" },
+    {
+      label: "回复状态", //0未回复,1已回复
+      prop: "replyStatus",
+      templet: "custom",
+    },
+    { label: "创建时间", prop: "createTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: [
+        {
+          name: "reply",
+          text: "回复",
+          attrs: { icon: "el-icon-Comment", type: "primary" },
+          render: (row: any) => row.replyStatus !== 1, // 仅未回复时显示
+        },
+        "delete",
+      ],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<UserFeedbackForm> = reactive({
+  // 权限前缀
+  permPrefix: "system:user-feedback",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "问题描述",
+        disabled: true,
+      },
+      label: "问题描述",
+      prop: "description",
+    },
+    {
+      type: "custom",
+      label: "图片",
+      prop: "images",
+      slotName: "images",
+      attrs: {
+        disabled: true,
+      },
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "联系方式(手机号或者邮箱)",
+        disabled: true,
+      },
+      label: "联系方式(手机号或者邮箱)",
+      prop: "contactWay",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "答复信息",
+      },
+      rules: [{ required: true, message: "请输入答复信息", trigger: "change" }],
+      label: "答复信息",
+      prop: "reply",
+    },
+  ],
+  // 提交函数
+  formAction: (data: UserFeedbackForm) => {
+    if (data.id) {
+      // 编辑
+      return UserFeedbackAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return UserFeedbackAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<UserFeedbackForm> = reactive({
+  permPrefix: "system:user-feedback",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 800,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return UserFeedbackAPI.update(data.id as string, data.reply as string, data);
+  },
+  formItems: addModalConfig.formItems, // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "reply") {
+    handleEditClick(data.row, async () => {
+      return await UserFeedbackAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+
+const getImageList = (images: string): string[] => {
+  if (!images) return [];
+  return images
+    .split(",")
+    .map((url) => url.trim())
+    .filter((url) => url !== "");
+};
+</script>

+ 300 - 0
src/views/userManage/user-info/index.vue

@@ -0,0 +1,300 @@
+<template>
+  <div class="app-container h-full flex flex-1 flex-col">
+    <!-- 搜索 -->
+    <page-search
+      ref="searchRef"
+      :search-config="searchConfig"
+      @query-click="handleQueryClick"
+      @reset-click="handleResetClick"
+    ></page-search>
+
+    <!-- 列表 -->
+    <page-content
+      ref="contentRef"
+      :content-config="contentConfig"
+      @add-click="handleAddClick"
+      @export-click="handleExportClick"
+      @search-click="handleSearchClick"
+      @toolbar-click="handleToolbarClick"
+      @operate-click="handleOperateClick"
+      @filter-change="handleFilterChange"
+    ></page-content>
+
+    <!-- 新增 -->
+    <page-modal
+      ref="addModalRef"
+      :modal-config="addModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+
+    <!-- 编辑 -->
+    <page-modal
+      ref="editModalRef"
+      :modal-config="editModalConfig"
+      @submit-click="handleSubmitClick"
+    ></page-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({ name: "UserInfo" });
+
+import UserInfoAPI, { UserInfoForm, UserInfoPageQuery } from "@/api/userManage/user-info-api";
+import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
+import usePage from "@/components/CURD/usePage";
+
+// 组合式 CRUD
+const {
+  searchRef,
+  contentRef,
+  addModalRef,
+  editModalRef,
+  handleQueryClick,
+  handleResetClick,
+  handleAddClick,
+  handleEditClick,
+  handleSubmitClick,
+  handleExportClick,
+  handleSearchClick,
+  handleFilterChange,
+} = usePage();
+
+// 搜索配置
+const searchConfig: ISearchConfig = reactive({
+  permPrefix: "business:user-info",
+  formItems: [
+    {
+      type: "input",
+      label: "所属集团编号",
+      prop: "ecId",
+      attrs: {
+        placeholder: "所属集团编号",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "input",
+      label: "手机号",
+      prop: "phone",
+      attrs: {
+        placeholder: "手机号",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+    {
+      type: "input",
+      label: "所属第三方", //(无则为自营)
+      prop: "groupId",
+      attrs: {
+        placeholder: "所属第三方",
+        clearable: true,
+        style: { width: "200px" },
+      },
+    },
+  ],
+});
+
+// 列表配置
+const contentConfig: IContentConfig<UserInfoPageQuery> = reactive({
+  // 权限前缀
+  permPrefix: "business:user-info",
+  table: {
+    border: true,
+    highlightCurrentRow: true,
+  },
+  // 主键
+  pk: "id",
+  // 列表查询接口
+  indexAction: UserInfoAPI.getPage,
+  // 删除接口
+  deleteAction: UserInfoAPI.deleteByIds,
+  // 数据解析函数
+  parseData(res: any) {
+    return {
+      total: res.total,
+      list: res.list,
+    };
+  },
+  // 分页配置
+  pagination: {
+    background: true,
+    layout: "total, sizes, prev, pager, next, jumper",
+    pageSize: 20,
+    pageSizes: [10, 20, 30, 50],
+  },
+  // 工具栏配置
+  defaultToolbar: ["refresh", "filter"],
+  // 表格列配置
+  cols: [
+    { type: "selection", width: 55, align: "center" },
+    { label: "所属集团编号", prop: "ecId" },
+    { label: "手机号", prop: "phone" },
+    { label: "积分", prop: "integralNum" },
+    { label: "所属第三方", prop: "groupId" }, //(无则为自营)
+    { label: "创建时间", prop: "createTime" },
+    {
+      label: "操作",
+      prop: "operation",
+      width: 220,
+      templet: "tool",
+      operat: [
+        {
+          name: "detail",
+          text: "详情",
+          attrs: {
+            type: "primary",
+            icon: "el-icon-view",
+          },
+        },
+      ],
+    },
+  ],
+});
+
+// 新增配置
+const addModalConfig: IModalConfig<UserInfoForm> = reactive({
+  // 权限前缀
+  permPrefix: "business:user-info",
+  // 主键
+  pk: "id",
+  // 弹窗配置
+  dialog: {
+    title: "新增",
+    width: 800,
+    draggable: true,
+  },
+  form: {
+    labelWidth: 100,
+  },
+  // 表单项配置
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "所属集团编号",
+      },
+      label: "所属集团编号",
+      prop: "ecId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "手机号",
+      },
+      label: "手机号",
+      prop: "phone",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "积分",
+      },
+      label: "积分",
+      prop: "integralNum",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "所属第三方(无则为自营)",
+      },
+      label: "所属第三方(无则为自营)",
+      prop: "groupId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "创建时间",
+      },
+      label: "创建时间",
+      prop: "createTime",
+    },
+  ],
+  // 提交函数
+  formAction: (data: UserInfoForm) => {
+    if (data.id) {
+      // 编辑
+      return UserInfoAPI.update(data.id as string, data);
+    } else {
+      // 新增
+      return UserInfoAPI.create(data);
+    }
+  },
+});
+
+// 编辑配置
+const editModalConfig: IModalConfig<UserInfoForm> = reactive({
+  permPrefix: "business:user-info",
+  component: "drawer",
+  drawer: {
+    title: "编辑",
+    size: 500,
+  },
+  pk: "id",
+  formAction(data: any) {
+    return UserInfoAPI.update(data.id as string, data);
+  },
+  formItems: [
+    {
+      type: "input",
+      attrs: {
+        placeholder: "所属集团编号",
+        disabled: true,
+      },
+      label: "所属集团编号",
+      prop: "ecId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "手机号",
+        disabled: true,
+      },
+      label: "手机号",
+      prop: "phone",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "积分",
+        disabled: true,
+      },
+      label: "积分",
+      prop: "integralNum",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "所属第三方", //(无则为自营)
+        disabled: true,
+      },
+      label: "所属第三方", //(无则为自营)
+      prop: "groupId",
+    },
+    {
+      type: "input",
+      attrs: {
+        placeholder: "创建时间",
+        disabled: true,
+      },
+      label: "创建时间",
+      prop: "createTime",
+    },
+  ], // 复用新增的表单项
+});
+
+// 处理操作按钮点击
+const handleOperateClick = (data: IObject) => {
+  if (data.name === "detail") {
+    handleEditClick(data.row, async () => {
+      return await UserInfoAPI.getFormData(data.row.id);
+    });
+  }
+};
+
+// 处理工具栏按钮点击(删除等)
+const handleToolbarClick = (name: string) => {
+  console.log(name);
+};
+</script>