Explorar el Código

feat(router): 添加运营模块路由配置

新增operation模块及其accounting-strategy子路由,
完善相关路由映射和类型定义

feat(api): 新增充电桩和计费策略相关API接口

- 添加获取充电桩信息集合API
- 创建运营计费策略相关接口:获取充电站信息、查询策略费用、保存批量策略费用
- 定义第三方充电站信息和服务费率配置类型

feat(view): 实现运营计费策略管理页面

创建accounting-strategy页面,包含充电站信息表格展示、
计费策略编辑功能,支持时段费率配置和多渠道费用设置

refactor(select): 优化终端管理页面站点选择组件

将终端管理页面的所属站点字段从普通输入框改为API选择器,
集成充电桩信息查询接口提供下拉选项

chore(env): 调整测试环境配置

注释本地IP配置,启用生产环境API地址

chore(log): 移除调试日志

删除ApiSelect组件中的调试打印语句
zhangtao hace 4 días
padre
commit
7afae1024c

+ 2 - 2
.env.test

@@ -3,9 +3,9 @@
 # VITE_SERVICE_BASE_URL=https://b8dbdde.r39.cpolar.top #王
 # VITE_SERVICE_BASE_URL=http://89561bkaq794.vicp.fun:53846 #张
 # VITE_SERVICE_BASE_URL=https://33cb5a99.r9.vip.cpolar.cn#田
-VITE_SERVICE_BASE_URL=http://192.168.0.19:8080 #邓
+# VITE_SERVICE_BASE_URL=http://192.168.0.19:8080 #邓
 # VITE_SERVICE_BASE_URL=http://d7b0f86.r36.cpolar.top #付
-# VITE_SERVICE_BASE_URL=https://smqjh.api.zswlgz.com
+VITE_SERVICE_BASE_URL=https://smqjh.api.zswlgz.com
 # VITE_SERVICE_BASE_URL=http://47.109.84.152:8081 #测试本地服务器
 # VITE_SERVICE_BASE_URL=/plt #测试打包服务器
 

+ 0 - 1
src/components/zt/ApiSelect/api-select.vue

@@ -50,7 +50,6 @@ async function fetchApi() {
   }
   const res = await api(params);
   options.value = unref(bindValue).pagination ? [...options.value, ...res.data.records] : get(res, props.resultFeild);
-  console.log('options', options, unref(bindValue).pagination);
 
   fetchLoading.value = false;
   if (props.setDefaultValue) {

+ 3 - 1
src/locales/langs/en-us.ts

@@ -269,7 +269,9 @@ const local: App.I18n.Schema = {
     'order-manage_order-detail': '',
     device: '',
     device_manage: '',
-    'device_terminal-manage': ''
+    'device_terminal-manage': '',
+    operation: '',
+    'operation_accounting-strategy': ''
   },
   page: {
     login: {

+ 3 - 1
src/locales/langs/zh-cn.ts

@@ -266,7 +266,9 @@ const local: App.I18n.Schema = {
     'order-manage_order-detail': '',
     device: '',
     device_manage: '',
-    'device_terminal-manage': ''
+    'device_terminal-manage': '',
+    operation: '',
+    'operation_accounting-strategy': ''
   },
   page: {
     login: {

+ 1 - 0
src/router/elegant/imports.ts

@@ -36,6 +36,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
   manage_role: () => import("@/views/manage/role/index.vue"),
   manage_schedule: () => import("@/views/manage/schedule/index.vue"),
   manage_user: () => import("@/views/manage/user/index.vue"),
+  "operation_accounting-strategy": () => import("@/views/operation/accounting-strategy/index.vue"),
   "order-manage_normal-order": () => import("@/views/order-manage/normal-order/index.vue"),
   "order-manage_order-detail": () => import("@/views/order-manage/order-detail/index.vue"),
   "user-center": () => import("@/views/user-center/index.vue"),

+ 20 - 0
src/router/elegant/routes.ts

@@ -265,6 +265,26 @@ export const generatedRoutes: GeneratedRoute[] = [
       }
     ]
   },
+  {
+    name: 'operation',
+    path: '/operation',
+    component: 'layout.base',
+    meta: {
+      title: 'operation',
+      i18nKey: 'route.operation'
+    },
+    children: [
+      {
+        name: 'operation_accounting-strategy',
+        path: '/operation/accounting-strategy',
+        component: 'view.operation_accounting-strategy',
+        meta: {
+          title: 'operation_accounting-strategy',
+          i18nKey: 'route.operation_accounting-strategy'
+        }
+      }
+    ]
+  },
   {
     name: 'order-manage',
     path: '/order-manage',

+ 2 - 0
src/router/elegant/transform.ts

@@ -203,6 +203,8 @@ const routeMap: RouteMap = {
   "manage_role": "/manage/role",
   "manage_schedule": "/manage/schedule",
   "manage_user": "/manage/user",
+  "operation": "/operation",
+  "operation_accounting-strategy": "/operation/accounting-strategy",
   "order-manage": "/order-manage",
   "order-manage_normal-order": "/order-manage/normal-order",
   "order-manage_order-detail": "/order-manage/order-detail",

+ 13 - 0
src/service/api/device/terminal-manage/index.ts

@@ -12,3 +12,16 @@ export function fetchGetDeviceList(params: any) {
     data: params
   });
 }
+
+/**
+ * 获取充电桩信息集合(下拉使用)
+ * @param params
+ * @returns
+ */
+export function fetchGegetPartyStationInfo(params: any) {
+  return request({
+    url: '/smqjh-system/api/v1/third-party-charging/getPartyStationInfo',
+    method: 'GET',
+    params
+  });
+}

+ 40 - 0
src/service/api/operation/accounting-strategy/index.ts

@@ -0,0 +1,40 @@
+import { request } from '@/service/request';
+
+/**
+ * 获取充电站信息分页列表(策略列表)
+ * @param params
+ * @returns
+ */
+export function fetchGetgetStationInfoPageByEquipment(params: any) {
+  return request<Api.device.ThirdPartyStationInfoPageVO[]>({
+    url: '/smqjh-system/api/zsdd/getStationInfoPageByEquipment',
+    method: 'POST',
+    data: params
+  });
+}
+
+/**
+ * 查询策略费用
+ * @param params
+ * @returns
+ */
+export function fetchgetPolicyFee(params: any) {
+  return request<Api.device.ServiceRateConfig[]>({
+    url: '/smqjh-system/api/zsdd/getPolicyFee',
+    method: 'GET',
+    params
+  });
+}
+/**
+ * 市民请集合保存策略费用
+ * @param params
+ * @returns
+ */
+
+export function fetchsaveBatchPolicyFee(params: any) {
+  return request({
+    url: '/smqjh-system/api/zsdd/saveBatchPolicyFee',
+    method: 'POST',
+    data: params
+  });
+}

+ 71 - 0
src/typings/api.d.ts

@@ -1759,5 +1759,76 @@ declare namespace Api {
       /** 设备名称 */
       equipmentName?: string;
     }
+    interface ThirdPartyStationInfoPageVO {
+      id: number;
+      /**
+       *  渠道类型
+       */
+      salesType?: number;
+      /** 充电站ID */
+      stationId?: string;
+      /** 充电站名称 */
+      stationName?: string;
+      /** 省市辖区编码 */
+      areaCode?: string;
+      /** 详细地址 */
+      address?: string;
+      /** 服务电话 */
+      serviceTel?: string;
+      /** 站点类型 */
+      stationType?: number;
+      /** 站点状态 */
+      stationStatus?: number;
+      /** 车位数量 */
+      parkNums?: number;
+      /** 建设场所 */
+      construction?: number;
+      /** 营业时间 */
+      busineHours?: string;
+      /** 更新时间 */
+      updateTime?: Date;
+    }
+    /**
+     * 服务费率配置
+     */
+    interface ServiceRateConfig {
+      /**
+       * 渠道名称
+       */
+      channelName: string;
+      /**
+       * 电费
+       */
+      electricityPrice: number;
+      /**
+       *
+          企业ID
+       */
+      firmId: number;
+      operationServiceFee: number;
+      periodFlag: number;
+      /**
+       * 销售合计价格(元/度)
+       */
+      saleTotalPrice: number;
+      /**
+       * 结算服务费
+       */
+      settlementServiceFee: number;
+      settlementTotalPrice: number;
+      thirdPartyId: number;
+      timePeriod: string;
+      valueAddedFees: number;
+      /**
+       * 结算费合计(元/度)
+       */
+      settlementFeeTotal: number;
+      channelCDVOS: ServiceRateConfig[];
+      /**
+       * 运营费
+
+       */
+      opFee: number | null;
+    }
   }
 }

+ 4 - 0
src/typings/elegant-router.d.ts

@@ -57,6 +57,8 @@ declare module "@elegant-router/types" {
     "manage_role": "/manage/role";
     "manage_schedule": "/manage/schedule";
     "manage_user": "/manage/user";
+    "operation": "/operation";
+    "operation_accounting-strategy": "/operation/accounting-strategy";
     "order-manage": "/order-manage";
     "order-manage_normal-order": "/order-manage/normal-order";
     "order-manage_order-detail": "/order-manage/order-detail";
@@ -124,6 +126,7 @@ declare module "@elegant-router/types" {
     | "iframe-page"
     | "login"
     | "manage"
+    | "operation"
     | "order-manage"
     | "user-center"
     | "user-management"
@@ -167,6 +170,7 @@ declare module "@elegant-router/types" {
     | "manage_role"
     | "manage_schedule"
     | "manage_user"
+    | "operation_accounting-strategy"
     | "order-manage_normal-order"
     | "order-manage_order-detail"
     | "user-center"

+ 7 - 2
src/views/device/terminal-manage/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="tsx">
-import { fetchGetDeviceList } from '@/service/api/device/terminal-manage';
+import { fetchGegetPartyStationInfo, fetchGetDeviceList } from '@/service/api/device/terminal-manage';
 import { useTable } from '@/components/zt/Table/hooks/useTable';
 
 const outColumns: NaiveUI.TableColumn<Api.device.manage>[] = [
@@ -56,7 +56,12 @@ const [registerTable] = useTable({
       {
         field: 'stationName',
         label: '所属站点',
-        component: 'NInput'
+        component: 'ApiSelect',
+        componentProps: {
+          api: fetchGegetPartyStationInfo,
+          labelField: 'stationName',
+          valueField: 'stationName'
+        }
       },
       {
         field: 'equipmentId',

+ 466 - 0
src/views/operation/accounting-strategy/index.vue

@@ -0,0 +1,466 @@
+<script setup lang="tsx">
+import { nextTick, ref } from 'vue';
+import { NButton, NDataTable, NInputNumber, NTag } from 'naive-ui';
+import {
+  fetchGetgetStationInfoPageByEquipment,
+  fetchgetPolicyFee,
+  fetchsaveBatchPolicyFee
+} from '@/service/api/operation/accounting-strategy';
+import { fetchGegetPartyStationInfo } from '@/service/api/device/terminal-manage';
+import { fetchGetChannelList } from '@/service/api/goods-center/store-goods';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+import { useModal } from '@/components/zt/Modal/hooks/useModal';
+import { useForm } from '@/components/zt/Form/hooks/useForm';
+const optionsData = ref<Api.goods.Channel[]>([]);
+
+const Columns: NaiveUI.TableColumn<Api.device.ThirdPartyStationInfoPageVO>[] = [
+  {
+    key: 'stationId',
+    title: '充电站ID',
+    align: 'center'
+  },
+  {
+    key: 'stationName',
+    title: '充电站名称',
+    align: 'center'
+  },
+  {
+    key: 'unitName',
+    title: '单位名称',
+    align: 'center'
+  },
+  {
+    key: 'areaCode',
+    title: '省市辖区编码',
+    align: 'center'
+  },
+  {
+    key: 'address',
+    title: '详细地址',
+    align: 'center'
+  },
+  {
+    key: 'serviceTel',
+    title: '服务电话',
+    align: 'center'
+  },
+  {
+    key: 'stationType',
+    title: '站点类型',
+    align: 'center',
+    render: row => {
+      return (
+        <div>
+          {row.stationType === 1 && <n-tag type="primary">公共</n-tag>}
+          {row.stationType === 50 && <n-tag type="danger">个人</n-tag>}
+          {row.stationType === 100 && <n-tag type="info">公交(专用)</n-tag>}
+          {row.stationType === 101 && <n-tag type="primary">环卫(专用)</n-tag>}
+          {row.stationType === 102 && <n-tag type="success">物流(专用)</n-tag>}
+          {row.stationType === 103 && <n-tag type="warning">物流(专用)</n-tag>}
+          {row.stationType === 255 && <n-tag type="info">物流(专用)</n-tag>}
+        </div>
+      );
+    }
+  },
+  {
+    key: 'stationStatus',
+    title: '站点状态',
+    align: 'center',
+    render(rowData) {
+      return (
+        <div>
+          {rowData.stationStatus === 0 && <n-tag type={'warning'}>未知</n-tag>}
+          {rowData.stationStatus === 1 && <n-tag type={'danger'}>建设中</n-tag>}
+          {rowData.stationStatus === 5 && <n-tag type={'info'}>关闭下线</n-tag>}
+          {rowData.stationStatus === 6 && <n-tag type={'primary'}>维护中</n-tag>}
+          {rowData.stationStatus === 50 && <n-tag type={'success'}>正常使用</n-tag>}
+        </div>
+      );
+    }
+  },
+  {
+    key: 'salesType',
+    title: '收费类型',
+    align: 'center',
+    render: row => {
+      return (
+        <div>
+          {row.salesType == 0 && <NTag type={'error'}>平台</NTag>}
+          {row.salesType == 1 && <NTag type={'primary'}>企业</NTag>}
+          {row.salesType == 2 && <NTag type={'warning'}>渠道方</NTag>}
+        </div>
+      );
+    }
+  }
+];
+const ModelColumns: NaiveUI.TableColumn<Api.device.ServiceRateConfig>[] = [
+  {
+    title: '规格信息(时段标志)',
+    key: 'periodFlag',
+    align: 'center',
+    width: 200,
+    render: row => {
+      const arrText = ['尖', '峰', '平', '谷'];
+      return arrText[row.periodFlag - 1];
+    }
+  },
+  {
+    title: '时段',
+    key: 'timePeriod',
+    align: 'center',
+    width: 120
+  },
+  {
+    title: '企业',
+    key: 'periodFlag',
+    align: 'center',
+    width: 200,
+    render: row => {
+      if (!row.channelCDVOS) return '--';
+      if (!row.channelCDVOS.length) return '--';
+      return row.channelCDVOS.map(it => {
+        return <div>{it.channelName}</div>;
+      });
+    }
+  },
+  {
+    title: '电费(元/度)',
+    key: 'electricityPrice',
+    align: 'center',
+    width: 120,
+    render: row => {
+      if (!row.channelCDVOS) return row.electricityPrice;
+      if (!row.channelCDVOS.length) return row.electricityPrice;
+      return row.channelCDVOS.map(it => {
+        return <div class={'mt10px'}>{it.electricityPrice}</div>;
+      });
+    }
+  },
+  {
+    title: '结算服务费(元)',
+    key: 'settlementServiceFee',
+    align: 'center',
+    width: 120,
+    render: row => {
+      if (!row.channelCDVOS) return row.settlementServiceFee;
+      if (!row.channelCDVOS.length) return row.settlementServiceFee;
+      return row.channelCDVOS.map(it => {
+        return <div class={'mt10px'}>{it.settlementServiceFee}</div>;
+      });
+    }
+  },
+  {
+    title: '结算费合计(元/度) ',
+    key: 'settlementTotalPrice',
+    align: 'center',
+    width: 120,
+    render: row => {
+      if (!row.channelCDVOS) return row.settlementTotalPrice;
+      if (!row.channelCDVOS.length) return row.settlementTotalPrice;
+      return row.channelCDVOS.map(it => {
+        return <div class={'mt10px'}>{it.settlementTotalPrice}</div>;
+      });
+    }
+  },
+  {
+    title: '运营服务费(元) ',
+    key: 'opFee',
+    align: 'center',
+    width: 120,
+    render: row => {
+      if (!row.channelCDVOS) return row.opFee;
+      if (!row.channelCDVOS.length) return row.opFee;
+      return row.channelCDVOS.map(it => {
+        return (
+          <div class={'mt10px'}>
+            <NInputNumber step={0.01} value={it.opFee} onUpdate:value={val => (it.opFee = val)} min={0}></NInputNumber>
+          </div>
+        );
+      });
+    }
+  },
+  {
+    title: '销售合计价格(元/度) ',
+    key: 'saleTotalPrice',
+    align: 'center',
+    width: 120,
+    render: row => {
+      if (!row.channelCDVOS) return row.saleTotalPrice;
+      if (!row.channelCDVOS.length) return row.saleTotalPrice;
+      return row.channelCDVOS.map(it => {
+        return <div class={'mt10px'}>{(it.settlementTotalPrice * 100 + Number(it.opFee) * 100) / 100}</div>;
+      });
+    }
+  }
+];
+
+const [registerTable] = useTable({
+  searchFormConfig: {
+    schemas: [
+      {
+        field: 'stationId',
+        label: '充电站ID',
+        component: 'NInput'
+      },
+      {
+        field: 'stationName',
+        label: '充电站名称',
+        component: 'NInput'
+      },
+      {
+        field: 'stationStatus',
+        label: '站点状态',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            {
+              label: '未知',
+              value: 0
+            },
+            {
+              label: '建设中',
+              value: 1
+            },
+            {
+              label: '关闭下线',
+              value: 5
+            },
+            {
+              label: '维护中',
+              value: 6
+            },
+            {
+              label: '正常使用',
+              value: 50
+            }
+          ]
+        }
+      }
+    ],
+    inline: false,
+    size: 'small',
+    labelPlacement: 'left',
+    isFull: false
+  },
+  tableConfig: {
+    keyField: 'id',
+    title: '计费策略',
+    showAddButton: true
+  }
+});
+const [registerModal, { openModal, setModalLoading, setSubLoading, closeModal }] = useModal({
+  title: '',
+  width: 1200,
+  height: 800
+});
+const [registerForm, { getFieldsValue, setFieldsValue, validate }] = useForm({
+  schemas: [
+    {
+      field: 'stationId',
+      label: '选择电站',
+      component: 'ApiSelect',
+      componentProps: {
+        api: fetchGegetPartyStationInfo,
+        labelField: 'stationName',
+        valueField: 'id'
+      },
+      required: true
+    },
+    {
+      field: 'salesType',
+      label: '收费类型',
+      component: 'NRadioGroup',
+      componentProps: {
+        options: [
+          {
+            label: '企业',
+            value: 1
+          },
+          {
+            label: '渠道方',
+            value: 2
+          },
+          {
+            label: '平台',
+            value: 0
+          }
+        ]
+      },
+      defaultValue: 1,
+      required: true
+    },
+    {
+      label: '选择企业',
+      field: 'channelPartyId',
+      component: 'ApiSelect',
+      componentProps: {
+        api: fetchGetChannelList,
+        labelFeild: 'channelName',
+        valueFeild: 'id',
+        multiple: true,
+        onUpdateValue: (value: number[]) => {
+          console.log(value, '=====');
+
+          handleSetTabData(value);
+        },
+        getOptions(options) {
+          optionsData.value = options;
+        }
+      },
+      required: true,
+      ifShow: schema => {
+        return schema.model.salesType === 1;
+      }
+    },
+    // {
+    //   field: 'stationFee',
+    //   label: '整站设置统一服务费',
+    //   component: 'NInput',
+    //   render({ model, field }) {
+    //     return (
+    //       <div class={'flex items-center'}>
+    //         <div class={'w300px'}>
+    //           <NInput value={model[field]} placeholder={'请输入整站设置统一服务费'}></NInput>
+    //         </div>
+    //         <div class={'ml20px'}>
+    //           <NButton>确定</NButton>
+    //         </div>
+    //       </div>
+    //     );
+    //   }
+    // },
+    {
+      field: 'policyFees',
+      label: '服务费设置',
+      component: 'NInput',
+      render: ({ model, field }) => {
+        return <NDataTable columns={ModelColumns} data={model[field]}></NDataTable>;
+      }
+    }
+  ],
+  labelWidth: 180,
+  layout: 'horizontal',
+  gridProps: {
+    cols: '1',
+    itemResponsive: true
+  },
+  collapsedRows: 1,
+  showActionButtonGroup: false
+});
+
+async function getModelTableList() {
+  const model = await getFieldsValue();
+  const { data, error } = await fetchgetPolicyFee({ ...model });
+  if (!error) {
+    const channelIds = data[0].channelCDVOS ? data[0].channelCDVOS.map(it => it.firmId) : [];
+    await setFieldsValue({
+      channelPartyId: channelIds,
+      policyFees: data.map(it => {
+        it.timePeriod = formatTime(it.timePeriod);
+        it.opFee = it.operationServiceFee;
+        it.channelCDVOS = it.channelCDVOS
+          ? it.channelCDVOS.map(its => {
+              return {
+                ...its,
+                electricityPrice: it.electricityPrice,
+                settlementServiceFee: it.settlementServiceFee,
+                settlementTotalPrice: it.settlementTotalPrice
+              };
+            })
+          : [];
+        return it;
+      })
+    });
+  }
+}
+async function handleEidt(row: Api.device.ThirdPartyStationInfoPageVO) {
+  setModalLoading(true);
+  openModal();
+  console.log(row, 'salesType');
+
+  nextTick(async () => {
+    await setFieldsValue({ stationId: row.id });
+    await getModelTableList();
+    setModalLoading(false);
+  });
+}
+function formatTime(timeStr: string): string {
+  if (timeStr.length !== 6) {
+    throw new Error('时间格式不正确,应为6位数字');
+  }
+
+  return timeStr.replace(/(\d{2})(\d{2})(\d{2})/, '$1:$2:$3');
+}
+function handleSetTabData(values: number[]) {
+  const modelData = getFieldsValue();
+  const tableDATA = modelData.policyFees || [];
+  const newTableData = tableDATA.map((it: Api.device.ServiceRateConfig) => {
+    return {
+      ...it,
+      channelCDVOS: values.map(channel => {
+        const findData = optionsData.value.find(channels => channels.id == channel);
+        if (!findData) {
+          return {};
+        }
+        return {
+          firmId: findData.id,
+          channelName: findData.channelName,
+          opFee: it.operationServiceFee,
+          electricityPrice: it.electricityPrice,
+          saleTotalPrice: it.saleTotalPrice,
+          settlementTotalPrice: it.settlementTotalPrice,
+          settlementServiceFee: it.settlementServiceFee
+        };
+      })
+    };
+  });
+  console.log(newTableData, 'newTableData');
+
+  setFieldsValue({ policyFees: newTableData });
+}
+async function handleSubmit() {
+  await validate();
+  setSubLoading(true);
+  const modelData = getFieldsValue();
+  const tableData = modelData.policyFees.map((it: Api.device.ServiceRateConfig) => {
+    return {
+      stationInfoId: modelData.stationId,
+      periodFlag: it.periodFlag,
+      channelCDVOS: it.channelCDVOS.map(channel => {
+        return {
+          firmId: channel.firmId,
+          channelName: channel.channelName,
+          opFee: channel.opFee
+        };
+      })
+    };
+  });
+  try {
+    await fetchsaveBatchPolicyFee(tableData);
+    closeModal();
+  } catch {
+    setSubLoading(false);
+  }
+}
+</script>
+
+<template>
+  <LayoutTable>
+    <ZTable
+      :columns="Columns"
+      :immediate="true"
+      :api="fetchGetgetStationInfoPageByEquipment"
+      @register="registerTable"
+      @add="openModal"
+    >
+      <template #op="{ row }">
+        <NButton size="small" ghost type="primary" @click="handleEidt(row)">编辑</NButton>
+      </template>
+    </ZTable>
+    <BasicModal @register="registerModal" @ok="handleSubmit">
+      <BasicForm @register-form="registerForm"></BasicForm>
+    </BasicModal>
+  </LayoutTable>
+</template>
+
+<style scoped></style>