6 Commits cc12b726b8 ... c7308063bf

Autor SHA1 Mensagem Data
  zhangtao c7308063bf fix(order-detail): 修复充电度数字段显示错误 há 1 semana atrás
  zhangtao 74420ba81e fix(order-detail): 修复订单详情页面数据显示错误 há 1 semana atrás
  zhangtao b726d9615a refactor(components): 移除未使用的naive-ui组件类型声明 há 2 semanas atrás
  zhangtao ba929b3085 feat(api): 添加充电订单相关类型定义 há 2 semanas atrás
  zhangtao e3f35cd119 feat(film-manage): 新增电影管理模块功能 há 2 semanas atrás
  zhangtao 7afae1024c feat(router): 添加运营模块路由配置 há 3 semanas atrás

+ 3 - 3
.env.test

@@ -2,10 +2,10 @@
 
 # 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.1.89: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) {

+ 2 - 0
src/components/zt/Form/hooks/useForm.ts

@@ -52,6 +52,8 @@ export function useForm(props?: Props): UseFormReturnType {
     },
     setFormProps: async (formProps: Partial<FormProps>) => {
       const form = await getForm();
+      console.log(formProps, 'formProps');
+
       await form.setFormProps(formProps);
     },
 

+ 33 - 25
src/components/zt/Table/z-table.vue

@@ -89,7 +89,33 @@ export default defineComponent({
       return data.value;
     }
     const getTableLoding = computed(() => loading.value);
+    const handleCommonTime = (form: Recordable) => {
+      const fieldMapToTime = getTableProps.value.fieldMapToTime;
 
+      if (!Array.isArray(fieldMapToTime)) {
+        return form;
+      }
+      fieldMapToTime.forEach(mapping => {
+        const [source, target] = mapping;
+        const formTimeKey = Array.isArray(source) ? source[0] : source;
+
+        let endTimeKey;
+        let startTimeKey;
+        if (Array.isArray(target)) {
+          if (typeof target[0] === 'string' && typeof target[1] === 'string') {
+            [startTimeKey, endTimeKey] = target;
+          } else if (Array.isArray(target[1])) {
+            [startTimeKey, endTimeKey] = target[1];
+          }
+        }
+
+        if (formTimeKey && startTimeKey && endTimeKey && form[formTimeKey]) {
+          [form[startTimeKey], form[endTimeKey]] = form[formTimeKey];
+          Reflect.deleteProperty(form, formTimeKey);
+        }
+      });
+      return form as Recordable;
+    };
     const TableMethod: TableMethods = {
       refresh: fetchSearchData,
       setSearchProps,
@@ -97,7 +123,7 @@ export default defineComponent({
       setTableConfig,
       getTableCheckedRowKeys,
       getTableData,
-      getSeachForm,
+      getSeachForm: () => handleCommonTime(getSeachForm()),
       setFieldsValue,
       getTableLoding
     };
@@ -126,31 +152,10 @@ export default defineComponent({
     async function fetchSearchData() {
       handleSearch();
     }
-    function handleSearch() {
-      const form = getSeachForm();
-      console.log(form);
 
-      if (getTableProps.value.fieldMapToTime && form[getTableProps.value.fieldMapToTime[0][0] as unknown as string]) {
-        const [startTimeKey, endTimeKey] = getTableProps.value.fieldMapToTime[0][1];
-        const formTimeKey = getTableProps.value.fieldMapToTime[0][0];
-        form[startTimeKey] = form[formTimeKey][0];
-        form[endTimeKey] = form[formTimeKey][1];
-        // delete form[formTimeKey];
-        Reflect.deleteProperty(form, formTimeKey);
-      }
-
-      if (
-        getTableProps.value.fieldMapToTime &&
-        getTableProps.value.fieldMapToTime[1] &&
-        form[getTableProps.value.fieldMapToTime[1][0] as unknown as string]
-      ) {
-        const [startTimeKey, endTimeKey] = getTableProps.value.fieldMapToTime[1][1];
-        const formTimeKey = getTableProps.value.fieldMapToTime[1][0];
-        form[startTimeKey] = form[formTimeKey][0];
-        form[endTimeKey] = form[formTimeKey][1];
-        // delete form[formTimeKey];
-        Reflect.deleteProperty(form, formTimeKey);
-      }
+    function handleSearch() {
+      const form = handleCommonTime(getSeachForm());
+      console.log(propsData.defaultParams, 'propsData.defaultParams');
 
       // 查询默认值,重置的时候不重置这个参数
       if (getTableProps.value.defaultParamsNotReset) {
@@ -209,6 +214,9 @@ export default defineComponent({
         </template>
       </TableHeaderOperation>
     </template>
+    <template #header>
+      <slot name="header" :loading="loading"></slot>
+    </template>
     <NDataTable
       v-bind="NTableProps"
       v-model:checked-row-keys="checkedRowKeys"

+ 94 - 0
src/components/zt/editor/index.vue

@@ -0,0 +1,94 @@
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+import WangEditor from 'wangeditor';
+import { fetchUpload } from '@/service/api/common';
+
+const editor = ref<WangEditor>();
+const domRef = ref<HTMLElement>();
+
+const context = defineModel<string>();
+const props = defineProps<{
+  height?: number;
+}>();
+const uploadConfig = {
+  // 服务器端点
+  server: '/smqjh-system/api/v1/files', // 请替换为你的图片上传接口
+  // 限制大小
+  maxSize: 5 * 1024 * 1024, // 5M
+  // 限制类型
+  allowedFileTypes: ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'],
+  // 超时时间
+  timeout: 30 * 1000
+};
+function renderWangEditor() {
+  editor.value = new WangEditor(domRef.value);
+  setEditorConfig();
+  configureImageUpload();
+  editor.value.create();
+  if (context.value) {
+    editor.value.txt.html(context.value);
+  }
+  editor.value.config.onchange = (newHtml: string) => {
+    context.value = newHtml;
+    console.log(newHtml, 'newHtml');
+  };
+}
+
+function setEditorConfig() {
+  console.log(editor.value, 'editor.valueeditor.value');
+
+  if (editor.value?.config?.zIndex) {
+    editor.value.config.zIndex = 1000;
+  }
+  if (editor.value) {
+    editor.value.config.height = props.height || 800;
+  }
+  // 启用粘贴文本保持格式
+  editor.value!.config.pasteFilterStyle = true;
+  editor.value!.config.pasteIgnoreImg = false;
+  editor.value!.config.pasteTextHandle = (content: string) => {
+    return content;
+  };
+}
+function configureImageUpload() {
+  if (!editor.value) return;
+
+  // 配置图片上传参数
+  const config = editor.value.config;
+
+  // 设置图片上传配置
+  config.uploadImgServer = uploadConfig.server;
+  config.uploadImgMaxSize = uploadConfig.maxSize;
+  config.uploadImgAccept = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
+  config.uploadImgTimeout = uploadConfig.timeout;
+  config.customUploadImg = async (files: File[], insertFn: (url: string) => void) => {
+    try {
+      const result = await fetchUpload(files[0]);
+      const imageUrl = result.data.url;
+      insertFn(imageUrl);
+    } catch (error) {
+      window.$message?.error(`上传失败${error}`);
+    }
+  };
+}
+onMounted(() => {
+  renderWangEditor();
+});
+</script>
+
+<template>
+  <div class="h-full w-full">
+    <div ref="domRef" class="w-full bg-white dark:bg-dark"></div>
+  </div>
+</template>
+
+<style scoped>
+:deep(.w-e-toolbar) {
+  background: inherit !important;
+  border-color: #999 !important;
+}
+:deep(.w-e-text-container) {
+  background: inherit;
+  border-color: #999 !important;
+}
+</style>

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

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

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

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

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

@@ -22,6 +22,9 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
   login: () => import("@/views/_builtin/login/index.vue"),
   device_manage: () => import("@/views/device/manage/index.vue"),
   "device_terminal-manage": () => import("@/views/device/terminal-manage/index.vue"),
+  "film-manage_config": () => import("@/views/film-manage/config/index.vue"),
+  "film-manage_film-list": () => import("@/views/film-manage/film-list/index.vue"),
+  "film-manage_setprice": () => import("@/views/film-manage/setprice/index.vue"),
   "goods-center_store-goods": () => import("@/views/goods-center/store-goods/index.vue"),
   "goods-center_type-admin": () => import("@/views/goods-center/type-admin/index.vue"),
   "government_government-list": () => import("@/views/government/government-list/index.vue"),
@@ -36,6 +39,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"),

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

@@ -68,6 +68,44 @@ export const generatedRoutes: GeneratedRoute[] = [
       }
     ]
   },
+  {
+    name: 'film-manage',
+    path: '/film-manage',
+    component: 'layout.base',
+    meta: {
+      title: 'film-manage',
+      i18nKey: 'route.film-manage'
+    },
+    children: [
+      {
+        name: 'film-manage_config',
+        path: '/film-manage/config',
+        component: 'view.film-manage_config',
+        meta: {
+          title: 'film-manage_config',
+          i18nKey: 'route.film-manage_config'
+        }
+      },
+      {
+        name: 'film-manage_film-list',
+        path: '/film-manage/film-list',
+        component: 'view.film-manage_film-list',
+        meta: {
+          title: 'film-manage_film-list',
+          i18nKey: 'route.film-manage_film-list'
+        }
+      },
+      {
+        name: 'film-manage_setprice',
+        path: '/film-manage/setprice',
+        component: 'view.film-manage_setprice',
+        meta: {
+          title: 'film-manage_setprice',
+          i18nKey: 'route.film-manage_setprice'
+        }
+      }
+    ]
+  },
   {
     name: 'goods-center',
     path: '/goods-center',
@@ -265,6 +303,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',

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

@@ -184,6 +184,10 @@ const routeMap: RouteMap = {
   "device": "/device",
   "device_manage": "/device/manage",
   "device_terminal-manage": "/device/terminal-manage",
+  "film-manage": "/film-manage",
+  "film-manage_config": "/film-manage/config",
+  "film-manage_film-list": "/film-manage/film-list",
+  "film-manage_setprice": "/film-manage/setprice",
   "goods-center": "/goods-center",
   "goods-center_store-goods": "/goods-center/store-goods",
   "goods-center_type-admin": "/goods-center/type-admin",
@@ -203,6 +207,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
+  });
+}

+ 45 - 0
src/service/api/film-manage/config/index.ts

@@ -0,0 +1,45 @@
+import { request } from '@/service/request';
+
+/**
+ * 电影倒计时配置
+ * @param data
+ * @returns
+ */
+export function fetchCountdown(data: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/movie/countdown',
+    method: 'POST',
+    data
+  });
+}
+/**
+ * 退改签协议配置
+ * @param data
+ * @returns
+ */
+export function fetchProtocol(data: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/movie/protocol',
+    method: 'post',
+    data
+  });
+}
+
+/**
+ * 购票须知
+ * @param data
+ */
+export function fetchNotice(data: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/movie/ticket/notice',
+    method: 'post',
+    data
+  });
+}
+
+export function fetchGetConfig() {
+  return request({
+    url: '/smqjh-pms/api/v1/movie/config',
+    method: 'GET'
+  });
+}

+ 36 - 0
src/service/api/film-manage/film-list/index.ts

@@ -0,0 +1,36 @@
+import { request } from '@/service/request';
+
+/**
+ * 电影列表
+ */
+export function fetchMovieList(params: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/movie/page',
+    method: 'POST',
+    data: params
+  });
+}
+
+/**
+ * 电影设置价格列表
+ * @param params
+ * @returns
+ */
+export function fetchSetMoivePrice(params: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/movie/price',
+    method: 'get',
+    params
+  });
+}
+
+/** \
+ * 获取电影影院列表商户列表
+ */
+export function fetchMoivecinemaList(params: any) {
+  return request({
+    url: '/smqjh-pms/api/v1/movie/cinemaList',
+    method: 'GET',
+    params
+  });
+}

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

@@ -0,0 +1,53 @@
+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
+  });
+}
+
+/**
+ * 删除市民策略费用
+ * @param params
+ * @returns
+ */
+export function fetchDeletePolicyFee(params: any) {
+  return request({
+    url: '/smqjh-system/api/zsdd/deletePolicyFee',
+    method: 'POST',
+    data: params
+  });
+}

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

@@ -681,6 +681,7 @@ declare namespace Api {
       [property: string]: any;
     }
     interface deliveryOrder {
+      chargeOrder: ChargeOrdeVo;
       /**
        * 取消原因
        */
@@ -878,8 +879,92 @@ declare namespace Api {
        * 买家手机号
        */
       userMobile?: string;
+      /**
+       * 电影票详情
+       */
+      yppDetail?: TicketOrderChannelDto;
+      /**
+       * 业务类型
+       */
+      businessType: string;
+      [property: string]: any;
+    }
+    interface ChargeOrdeVo {
+      /**
+       * 充电订单号
+       */
+      chargeOrderNo?: string;
+      /**
+       * 充电时间:秒
+       */
+      chargeTime?: number;
+      /**
+       * 充电设备接口编码
+       */
+      connectorId?: string;
+      /**
+       * 结束充电时间
+       */
+      endTime?: string;
+      /**
+       * 用户ID
+       */
+      memberId?: number;
+      /**
+       * 订购流水号
+       */
+      orderNumber?: string;
+      /**
+       * 电站名称
+       */
+      powerStationName?: string;
+      /**
+       * 预充值金额
+       */
+      preAmt?: number;
+      /**
+       * 平台实际收取金额
+       */
+      realCost?: number;
+      /**
+       * 平台总服务费
+       */
+      realServiceCost?: number;
+      /**
+       * 备注
+       */
+      remarks?: string;
+      /**
+       * 开始充电时间
+       */
+      startTime?: string;
+      /**
+       * 第三方充电站ID
+       */
+      stationId?: string;
+      /**
+       * 充电订单状态
+       */
+      status?: number;
+      /**
+       * 0:用户手动停止充电;1:客户归属地运营商平台停止充电;2:BMS停止充电;3:充电机设备故障;4:连接器断开;其它:自定义
+       */
+      stopMethod?: number;
+      /**
+       * 实际充电度数(单位:0.001 kw/h)
+       */
+      totalCharge?: number;
+      /**
+       * 三方电费
+       */
+      totalMoney?: number;
+      /**
+       * 充电度数
+       */
+      totalPower?: number;
       [property: string]: any;
     }
+
     interface OrderItemElement {
       /**
        * 商品实际金额 = 商品总金额 - 分摊的优惠金额
@@ -1505,6 +1590,257 @@ declare namespace Api {
       withdrawApplyCount?: number;
       [property: string]: any;
     }
+    interface TicketOrderChannelDto {
+      /**
+       * 所属APPId
+       */
+      appId?: null | string;
+      /**
+       * 观众列表ID,用,分隔
+       */
+      audience?: null | string;
+      /**
+       * 头像地址
+       */
+      avatarUrl?: null | string;
+      /**
+       * 购买方式  1快速票、0特惠票
+       */
+      buyModel?: number;
+      /**
+       * 影院地址
+       */
+      cinemaAddress?: null | string;
+      /**
+       * 影院经度
+       */
+      cinemaLat?: number;
+      /**
+       * 影院纬度
+       */
+      cinemaLng?: number;
+      /**
+       * 影院名称
+       */
+      cinemaName?: null | string;
+      /**
+       * 城市名称
+       */
+      cityName?: null | string;
+      /**
+       * 优惠券ID
+       */
+      couponId?: null | string;
+      /**
+       * 创建时间
+       */
+      createTime?: Date;
+      /**
+       * 企业ID
+       */
+      entId?: null | string;
+      /**
+       * 企业参数
+       */
+      entPara?: null | string;
+      /**
+       * 当前订单佣金
+       */
+      epCommission?: number;
+      /**
+       * 订单结算价
+       */
+      epCostPrice?: number;
+      /**
+       * 上级代理佣金(无上级代理为0)
+       */
+      epParentCommission?: number;
+      /**
+       * 企业订单结算价已退金额
+       */
+      epRefundAmount?: number;
+      /**
+       * 归属企业用户id
+       */
+      epUserId?: string;
+      /**
+       * 过期时间
+       */
+      expireTime?: Date | null;
+      /**
+       * 附加数据
+       */
+      extData?: null | string;
+      /**
+       * 图片列表
+       */
+      files?: FileStoreDto[] | null;
+      /**
+       * 完成时间
+       */
+      finishTime?: Date | null;
+      /**
+       * 性别
+       */
+      gender?: null | string;
+      /**
+       * 影厅名称
+       */
+      hallName?: null | string;
+      id?: string;
+      /**
+       * 邀请企业佣金(无邀请企业为0)
+       */
+      inviteEpCommission?: number;
+      /**
+       * 出票时间(单位:秒)
+       */
+      makeTicketSeconds?: number;
+      /**
+       * 电影名称
+       */
+      movieName?: null | string;
+      /**
+       * 影票类型(英语 3D)
+       */
+      movieVersion?: null | string;
+      /**
+       * 昵称
+       */
+      nickName?: null | string;
+      /**
+       * 自定义订单号
+       */
+      orderCustomId?: null | string;
+      /**
+       * 外部平台支付凭证号
+       */
+      orderPayId?: null | string;
+      /**
+       * 外部平台退款凭证号
+       */
+      orderRefundId?: null | string;
+      orderState?: number;
+      /**
+       * 订单状态
+       */
+      orderStateName?: null | string;
+      /**
+       * 市场原价(单价)
+       */
+      originPrice?: number;
+      /**
+       * 支付时间
+       */
+      paidTime?: Date | null;
+      payAppId?: null | string;
+      payTranId?: null | string;
+      /**
+       * 电影海报地址
+       */
+      postImageUrl?: null | string;
+      seatList?: string[] | null;
+      /**
+       * 座位名称
+       */
+      seatNames?: null | string;
+      /**
+       * 开场时间
+       */
+      sessionBeginTime?: Date;
+      /**
+       * 结束时间
+       */
+      sessionEndTime?: Date | null;
+      /**
+       * 供应商ID
+       */
+      supplierId?: null | string;
+      /**
+       * 是否支持自动换座位
+       */
+      switchSeat?: boolean;
+      /**
+       * 取票码
+       */
+      ticketCode?: null | string;
+      /**
+       * 取票码列表
+       */
+      ticketCodeList?: string[] | null;
+      /**
+       * 取票码文字信息
+       */
+      ticketCodeText?: null | string;
+      /**
+       * 分销商总价(非分销模式为0)
+       */
+      totalDistributionPrice?: number;
+      /**
+       * 订单用户总价
+       */
+      totalUserPrice?: number;
+      /**
+       * 更新时间
+       */
+      updateTime?: Date | null;
+      /**
+       * 用户ID
+       */
+      userId?: string;
+      /**
+       * 微信form_id
+       */
+      wxFormId?: null | string;
+      /**
+       * 微信PrepayId
+       */
+      wxPrepayId?: null | string;
+    }
+
+    /**
+     * FileStoreDto
+     */
+    interface FileStoreDto {
+      /**
+       * 文件描述
+       */
+      fileDescription?: null | string;
+      /**
+       * 文件扩展名
+       */
+      fileExtension?: null | string;
+      /**
+       * 文件Hash
+       */
+      fileHash?: null | string;
+      /**
+       * 文件类型
+       */
+      fileMIME?: null | string;
+      /**
+       * 文件名称
+       */
+      fileName?: null | string;
+      /**
+       * 文件操作
+       */
+      fileOperation?: number;
+      /**
+       * 文件路径
+       */
+      filePath?: null | string;
+      id?: string;
+      /**
+       * 多图片索引
+       */
+      index?: number;
+      /**
+       * 最后操作时间
+       */
+      lastOperatedTime?: Date;
+      orderId?: string;
+    }
   }
   namespace Store {
     interface ShopDetail {
@@ -1759,5 +2095,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;
+    }
   }
 }

+ 2 - 4
src/typings/components.d.ts

@@ -21,6 +21,7 @@ declare module 'vue' {
     DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
     DictSelect: typeof import('./../components/zt/dict-select/index.vue')['default']
     DictTag: typeof import('./../components/zt/dict-tag/index.vue')['default']
+    Editor: typeof import('./../components/zt/editor/index.vue')['default']
     ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
     FullScreen: typeof import('./../components/common/full-screen.vue')['default']
     GithubLink: typeof import('./../components/custom/github-link.vue')['default']
@@ -55,17 +56,13 @@ declare module 'vue' {
     NCollapse: typeof import('naive-ui')['NCollapse']
     NCollapseItem: typeof import('naive-ui')['NCollapseItem']
     NColorPicker: typeof import('naive-ui')['NColorPicker']
-    NCountdown: typeof import('naive-ui')['NCountdown']
     NDataTable: typeof import('naive-ui')['NDataTable']
-    NDescriptions: typeof import('naive-ui')['NDescriptions']
-    NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
     NDialogProvider: typeof import('naive-ui')['NDialogProvider']
     NDivider: typeof import('naive-ui')['NDivider']
     NDrawer: typeof import('naive-ui')['NDrawer']
     NDrawerContent: typeof import('naive-ui')['NDrawerContent']
     NDropdown: typeof import('naive-ui')['NDropdown']
     NEmpty: typeof import('naive-ui')['NEmpty']
-    NFlex: typeof import('naive-ui')['NFlex']
     NForm: typeof import('naive-ui')['NForm']
     NFormItem: typeof import('naive-ui')['NFormItem']
     NGi: typeof import('naive-ui')['NGi']
@@ -98,6 +95,7 @@ declare module 'vue' {
     NSwitch: typeof import('naive-ui')['NSwitch']
     NTab: typeof import('naive-ui')['NTab']
     NTable: typeof import('naive-ui')['NTable']
+    NTabPane: typeof import('naive-ui')['NTabPane']
     NTabs: typeof import('naive-ui')['NTabs']
     NTag: typeof import('naive-ui')['NTag']
     NTbody: typeof import('naive-ui')['NTbody']

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

@@ -38,6 +38,10 @@ declare module "@elegant-router/types" {
     "device": "/device";
     "device_manage": "/device/manage";
     "device_terminal-manage": "/device/terminal-manage";
+    "film-manage": "/film-manage";
+    "film-manage_config": "/film-manage/config";
+    "film-manage_film-list": "/film-manage/film-list";
+    "film-manage_setprice": "/film-manage/setprice";
     "goods-center": "/goods-center";
     "goods-center_store-goods": "/goods-center/store-goods";
     "goods-center_type-admin": "/goods-center/type-admin";
@@ -57,6 +61,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";
@@ -118,12 +124,14 @@ declare module "@elegant-router/types" {
     | "404"
     | "500"
     | "device"
+    | "film-manage"
     | "goods-center"
     | "government"
     | "home"
     | "iframe-page"
     | "login"
     | "manage"
+    | "operation"
     | "order-manage"
     | "user-center"
     | "user-management"
@@ -153,6 +161,9 @@ declare module "@elegant-router/types" {
     | "login"
     | "device_manage"
     | "device_terminal-manage"
+    | "film-manage_config"
+    | "film-manage_film-list"
+    | "film-manage_setprice"
     | "goods-center_store-goods"
     | "goods-center_type-admin"
     | "government_government-list"
@@ -167,6 +178,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',

+ 125 - 0
src/views/film-manage/config/index.vue

@@ -0,0 +1,125 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NInputNumber } from 'naive-ui';
+import { fetchCountdown, fetchGetConfig, fetchNotice, fetchProtocol } from '@/service/api/film-manage/config';
+import { useForm } from '@/components/zt/Form/hooks/useForm';
+import type { FormSchema } from '@/components/zt/Form/types/form';
+type ActiveTab = 'countdown' | 'protocol' | 'notice';
+const activeTab = ref<ActiveTab>('countdown');
+const protocol = ref('');
+const countForm: FormSchema[] = [
+  {
+    label: '选票倒计时',
+    field: 'countdownVotes',
+    component: 'NInput',
+    render({ model, field }) {
+      return (
+        <NInputNumber
+          v-slots={{ suffix: () => '分钟' }}
+          value={model[field]}
+          onUpdate:value={val => (model[field] = val)}
+          min={0}
+          max={600000}
+        ></NInputNumber>
+      );
+    }
+  },
+  {
+    label: '付款倒计时',
+    field: 'countdownPay',
+    component: 'NInput',
+    render({ model, field }) {
+      return (
+        <NInputNumber
+          v-slots={{ suffix: () => '分钟' }}
+          value={model[field]}
+          onUpdate:value={val => (model[field] = val)}
+          max={10}
+          min={0}
+        ></NInputNumber>
+      );
+    }
+  }
+];
+const notice = ref();
+const submitLoding = ref(false);
+const [countdownForm, { getFieldsValue, setFieldsValue }] = useForm({
+  schemas: countForm,
+  labelWidth: 120,
+  layout: 'horizontal',
+  gridProps: {
+    cols: '1 xl:4 s:1 l:3',
+    itemResponsive: true
+  },
+  collapsedRows: 1,
+  showActionButtonGroup: false
+});
+async function handleChange(value: ActiveTab) {
+  activeTab.value = value;
+}
+async function handleSubmit() {
+  console.log(protocol.value, 'protocol');
+  submitLoding.value = true;
+
+  try {
+    if (activeTab.value == 'countdown') {
+      await fetchCountdown({ ...getFieldsValue() });
+    }
+    if (activeTab.value == 'protocol') {
+      await fetchProtocol({ protocol: protocol.value });
+    }
+    if (activeTab.value == 'notice') {
+      await fetchNotice({ notice: notice.value });
+    }
+    submitLoding.value = false;
+    window.$message?.success('保存成功');
+  } catch {
+    submitLoding.value = false;
+  }
+}
+
+async function getData() {
+  const { data, error } = await fetchGetConfig();
+  console.log(data);
+  if (!error) {
+    setFieldsValue({ ...data });
+    notice.value = data.notice;
+    protocol.value = data.protocol;
+  }
+}
+getData();
+</script>
+
+<template>
+  <div>
+    <NCard :bordered="false" class="h-full flex-1 card-wrapper bg-white sm:flex-1-hidden dark:bg-dark" size="small">
+      <NTabs v-model:value="activeTab" size="large" animated @update-value="handleChange">
+        <NTabPane name="countdown" tab="倒计时" display-directive="show">
+          <BasicForm @register-form="countdownForm"></BasicForm>
+        </NTabPane>
+        <NTabPane name="protocol" tab="退改签协议">
+          <div class="flex pt50px">
+            <div class="w120px flex-shrink-0 text-end">改签协议:</div>
+            <Editor v-model:model-value="protocol"></Editor>
+          </div>
+        </NTabPane>
+        <NTabPane name="notice" tab="购票须知">
+          <div class="flex">
+            <div class="w120px flex-shrink-0 text-end">购买须知:</div>
+            <NInput v-model:value="notice" type="textarea" placeholder="请输入购票须知" />
+          </div>
+        </NTabPane>
+      </NTabs>
+      <div class="mt40px w-full flex items-center justify-center">
+        <NButton :loading="submitLoding" type="primary" @click="handleSubmit">
+          <template #icon>
+            <SvgIcon icon="ant-design:save-outlined"></SvgIcon>
+          </template>
+          保存
+        </NButton>
+      </div>
+    </NCard>
+  </div>
+</template>
+
+<style lang="scss" scoped></style>

+ 211 - 0
src/views/film-manage/film-list/index.vue

@@ -0,0 +1,211 @@
+<script setup lang="tsx">
+import { ref, useTemplateRef } from 'vue';
+import { NButton } from 'naive-ui';
+import { router } from '@/router';
+import { fetchGetChannelList, fetchImportGoods } from '@/service/api/goods-center/store-goods';
+import { fetchMoivecinemaList, fetchMovieList } from '@/service/api/film-manage/film-list';
+import { useTable } from '@/components/zt/Table/hooks/useTable';
+const importTemplateRef = useTemplateRef('importTemplateRef');
+
+const options = ref<Api.goods.Channel[]>([]);
+const statusList = ['上架', '可售', '下架', '即将上映'];
+const columns: NaiveUI.TableColumn<Api.goods.ShopSku>[] = [
+  {
+    type: 'selection',
+    align: 'center',
+    width: 48,
+    fixed: 'left'
+  },
+  {
+    key: 'supId',
+    title: '商品ID',
+    align: 'left',
+    width: 200,
+    render: (row: any) => {
+      return (
+        <div class={'flex items-center'}>
+          <div>统一ID:</div>
+          <div>{row.movieId}</div>
+        </div>
+      );
+    }
+  },
+  {
+    key: 'pic',
+    title: '商品图片',
+    align: 'center',
+    width: 120,
+    render: (row: any) => {
+      return <n-image src={row.posterUrl} width={60} height={60}></n-image>;
+    }
+  },
+
+  {
+    key: 'movieName',
+    title: '商品名称',
+    align: 'center',
+    width: 120,
+    ellipsis: {
+      tooltip: true
+    }
+  },
+  {
+    key: 'businessType',
+    title: '业务类型',
+    align: 'center',
+    width: 120,
+    ellipsis: {
+      tooltip: true
+    }
+  },
+  {
+    key: 'cinemaName',
+    title: '商户',
+    align: 'center',
+    width: 120,
+    ellipsis: {
+      tooltip: true
+    }
+  },
+  {
+    key: 'channelVOS',
+    title: '价格',
+    align: 'center',
+    width: 120,
+    render: (row: any) => {
+      return (
+        <div>
+          {row.moviePriceVo.map((it: Api.government.ChannelVO) => {
+            return (
+              <div>
+                {it.channl}:¥{it.price}
+              </div>
+            );
+          })}
+        </div>
+      );
+    }
+  },
+  {
+    key: 'status',
+    title: '状态',
+    align: 'center',
+    width: 120,
+    render: (row: any) => {
+      return (
+        <div class="flex items-center justify-center">
+          <n-badge color={row.status == 0 ? 'green' : 'red'} value={row.status} dot />
+          <span class="ml-2">{statusList[row.status]}</span>
+        </div>
+      );
+    }
+  },
+  {
+    key: 'updateTime',
+    title: '更新时间',
+    align: 'center',
+    width: 120,
+    ellipsis: {
+      tooltip: true
+    }
+  }
+];
+const [registerTable] = useTable({
+  searchFormConfig: {
+    schemas: [
+      {
+        label: '商户',
+        component: 'ApiSelect',
+        field: 'cinemaName',
+        componentProps: {
+          api: fetchMoivecinemaList,
+          labelFeild: 'name',
+          valueFeild: 'name'
+        }
+      },
+      {
+        label: '关键词',
+        component: 'NInput',
+        field: 'keywords'
+      },
+      {
+        label: '状态',
+        field: 'status',
+        component: 'NSelect',
+        componentProps: {
+          options: [
+            {
+              label: '上架',
+              value: 0
+            },
+            {
+              label: '可售',
+              value: 1
+            },
+            {
+              label: '下架',
+              value: 2
+            },
+            {
+              label: '即将上映',
+              value: 3
+            }
+          ]
+        }
+      }
+    ],
+    inline: false,
+    size: 'small',
+    labelPlacement: 'left',
+    isFull: false
+  },
+  tableConfig: {
+    keyField: 'cinemaId',
+    title: '电影列表',
+    showAddButton: false,
+    scrollX: 1800,
+    fieldMapToTime: [
+      ['price', ['minPrice', 'maxPrice']],
+      ['createTime', ['startTime', 'endTime']]
+    ]
+  }
+});
+
+async function handleSubmitImport(file: File) {
+  const { error } = await fetchImportGoods({ file });
+  if (!error) {
+    importTemplateRef.value?.closeModal();
+  }
+  importTemplateRef.value?.setSubLoading(false);
+}
+
+function handleModalPrice(row: Api.goods.ShopSku) {
+  router.push({ path: '/film-manage/setprice', query: { movieId: row.movieId, cinemaId: row.cinemaId } });
+}
+async function getData() {
+  const { data, error } = await fetchGetChannelList();
+  if (!error) {
+    options.value = data;
+  }
+}
+getData();
+</script>
+
+<template>
+  <LayoutTable>
+    <ZTable :columns="columns" :api="fetchMovieList" @register="registerTable">
+      <template #op="{ row }">
+        <NButton size="small" ghost type="primary" @click="handleModalPrice(row)">设置渠道及价格</NButton>
+      </template>
+    </ZTable>
+    <ZImportTemplate
+      ref="importTemplateRef"
+      url="/smqjh-pms/api/v1/channelProd/template/download"
+      template-text="商品渠道及价格导入模版.xlsx"
+      modal-text="导入商品销售渠道及价格"
+      @submit="handleSubmitImport"
+    ></ZImportTemplate>
+  </LayoutTable>
+</template>
+
+<style scoped></style>

+ 67 - 0
src/views/film-manage/setprice/index.vue

@@ -0,0 +1,67 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { useRoute } from 'vue-router';
+import { fetchSetMoivePrice } from '@/service/api/film-manage/film-list';
+const route = useRoute();
+const dataList = ref([]);
+async function getData() {
+  const { data } = await fetchSetMoivePrice(route.query);
+  dataList.value = data;
+}
+const columns: NaiveUI.TableColumn<Api.goods.ShopSku>[] = [
+  {
+    title: '规格信息',
+    key: 'specInfo',
+    width: 200,
+    render: row => {
+      return (
+        <div>
+          <div class={'text-gray'}>规格ID: {row.areaId} </div>
+          <div> {row.movieName} </div>
+        </div>
+      );
+    }
+  },
+  {
+    title: '企业',
+    key: 'enterpriseName',
+    width: 300,
+    align: 'center',
+    render(row) {
+      return row.channel.map((it: any) => {
+        return <div>{it}</div>;
+      });
+    }
+  },
+  {
+    title: '当前单价(元)',
+    key: 'price',
+    align: 'center',
+    width: 200,
+    render(row) {
+      return row.channel.map((_it: any) => {
+        return <div>{row.originPrice}</div>;
+      });
+    }
+  },
+  {
+    title: '改后单价(元)',
+    key: 'newprice',
+    width: 200
+  }
+];
+getData();
+</script>
+
+<template>
+  <div>
+    <NCard :bordered="false" class="h-full flex-1 card-wrapper bg-white sm:flex-1-hidden dark:bg-dark" size="small">
+      <h1 class="text-24px font-semibold">设置价格</h1>
+      <div class="mt40px">
+        <NDataTable :columns="columns" :data="dataList" :bordered="false" />
+      </div>
+    </NCard>
+  </div>
+</template>
+
+<style scoped></style>

+ 1 - 1
src/views/goods-center/store-goods/index.vue

@@ -323,7 +323,7 @@ const [registerTable, { getTableCheckedRowKeys, refresh, getTableData, getSeachF
     isFull: false
   },
   tableConfig: {
-    keyField: 'id',
+    keyField: 'skuId',
     title: '商品列表',
     showAddButton: false,
     scrollX: 1800,

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

@@ -0,0 +1,485 @@
+<script setup lang="tsx">
+import { nextTick, ref } from 'vue';
+import { NButton, NDataTable, NInputNumber, NTag } from 'naive-ui';
+import {
+  fetchDeletePolicyFee,
+  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
+              precision={2}
+              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: 1400,
+  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: 100,
+  layout: 'horizontal',
+  gridProps: {
+    cols: '1',
+    itemResponsive: true
+  },
+  collapsedRows: 1,
+  showActionButtonGroup: false
+});
+const initFirmIds = ref<number[]>([]);
+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) : [];
+    initFirmIds.value = channelIds;
+    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');
+}
+async 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
+        };
+      })
+    };
+  });
+  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
+        };
+      })
+    };
+  });
+  const nowFirmIds = modelData.channelPartyId;
+  const delids = initFirmIds.value.filter(it => !nowFirmIds.includes(it));
+  console.log(delids, '删除ids============');
+  try {
+    await fetchsaveBatchPolicyFee(tableData);
+    if (delids.length) {
+      const updatePolicyFeeFroms = delids.map(it => {
+        return {
+          firmId: it,
+          stationInfoId: modelData.stationId
+        };
+      });
+      await fetchDeletePolicyFee(updatePolicyFeeFroms);
+    }
+    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>

+ 131 - 247
src/views/order-manage/normal-order/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="tsx">
-import { onMounted, reactive, ref, unref, useTemplateRef, watch } from 'vue';
+import { nextTick, ref, watch } from 'vue';
 import { useRouter } from 'vue-router';
 import { NTag, useDialog } from 'naive-ui';
 import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
@@ -11,157 +11,83 @@ import {
   fetchGetDeliveryStatusNum
 } from '@/service/api/order-manage/normal-order';
 // import { fetchGetLoginUserList } from '@/service/api/common';
-import { useAppStore } from '@/store/modules/app';
-import { defaultTransform, useNaivePaginatedTable } from '@/hooks/common/table';
 import { useAuth } from '@/hooks/business/auth';
 // import { copyTextToClipboard } from '@/utils/zt';
 import { commonExport } from '@/utils/common';
-import { $t } from '@/locales';
-import { useForm } from '@/components/zt/Form/hooks/useForm';
 import { useModal } from '@/components/zt/Modal/hooks/useModal';
 import { useTable } from '@/components/zt/Table/hooks/useTable';
 // import { type } from '../../../../packages/axios/src/index';
 import { SearchForm, orderStatus } from './normal-order';
-import DeliveryModal from './component/delivery-modal.vue';
-import NormalMoadl from './component/normal-modal.vue';
 const router = useRouter();
-const appStore = useAppStore();
-const checkedRowKeys = ref([]);
 const activeTab = ref('all');
 const statusList = ref<{ label: string; value: string; num?: number }[]>([]);
-const orderMoadl = useTemplateRef('orderMoadl');
 // const ShipmentModal = useTemplateRef('Shipment');
 const channelIdList = ref([]);
-const searchForm = ref();
-const searchParams = reactive({
-  pageNum: 1,
-  pageSize: 10
-});
-const [registerSearchForm, { getFieldsValue, setFieldsValue }] = useForm({
-  schemas: [
-    // {
-    //   field: 'channelIdList',
-    //   label: '所属企业',
-    //   component: 'ApiSelect',
-    //   componentProps: {
-    //     api: () => fetchGetLoginUserList(),
-    //     labelFeild: 'channelName',
-    //     valueFeild: 'id',
-    //     multiple: true,
-    //     onUpdateValue: () => {
-    //       nextTick(() => {
-    //         handleSearch();
-    //       });
-    //     },
-    //     getOptions: async (options: any) => {
-    //       await setFieldsValue({ channelIdList: [options[0].id] });
-    //       handleSearch();
-    //     }
-    //   }
-    // },
-    ...SearchForm
-  ],
-  showAdvancedButton: false,
-  labelWidth: 120,
-  layout: 'horizontal',
-  size: 'small',
-  gridProps: {
-    cols: '1 xl:4 s:1 l:3',
-    itemResponsive: true
-  },
-  collapsedRows: 1
-});
-const { columns, data, loading, getData, mobilePagination } = useNaivePaginatedTable({
-  api: () => fetchGetDeliveryOrderList({ ...searchParams, orderStatus: activeTab.value, ...unref(searchForm) }),
-  transform: response => defaultTransform(response),
-  immediate: false,
-  paginationProps: {
-    pageSizes: import.meta.env.VITE_PAGE_SIZE.split(',').map(Number)
+const columns: NaiveUI.TableColumn<Api.delivery.deliveryOrder>[] = [
+  {
+    key: 'orderNumber',
+    title: '订单编号',
+    align: 'center',
+    width: 220
   },
-  onPaginationParamsChange: params => {
-    searchParams.pageNum = Number(params.page);
-    searchParams.pageSize = Number(params.pageSize);
+  {
+    key: 'consigneeAddress',
+    title: '业务类型',
+    align: 'center',
+    width: 120,
+    render: row => {
+      return <NTag class={'mt7'}>{row.businessType}</NTag>;
+    }
   },
-  columns: () => [
-    {
-      key: 'orderNumber',
-      title: '订单编号',
-      align: 'center',
-      width: 220
-    },
-    {
-      key: 'consigneeAddress',
-      title: '业务类型',
-      align: 'center',
-      width: 120,
-      render: row => {
-        return <NTag class={'mt7'}>{row.businessType}</NTag>;
-      }
-    },
-    {
-      key: 'info',
-      title: '客户信息',
-      align: 'center',
-      width: 220,
-      render: row => {
-        return (
-          <div class={'mt7'}>
-            <div>
-              {row.consigneeName}
-              {row.consigneeMobile}
-            </div>
-            <div>{row.consigneeAddress}</div>
-          </div>
-        );
-      }
-    },
-    {
-      key: 'status',
-      title: '订单状态',
-      align: 'center',
-      width: 120,
-      render: row => {
-        const statusKey = row.hbOrderStatus as keyof typeof orderStatus;
-        const statusText = orderStatus[statusKey] || '未知状态';
-        return <NTag class={'mt7'}>{statusText}</NTag>;
-      }
-    },
-    {
-      key: 'createTime',
-      title: '下单时间',
-      align: 'center',
-      width: 180,
-      render: row => {
-        return <div>{row.createTime?.replace('T', '  ')}</div>;
-      }
-    },
-    {
-      key: 'operate',
-      title: $t('common.operate'),
-      align: 'center',
-      width: 150,
-      fixed: 'right',
-      render: row => {
-        return (
-          <div class={'mt7'}>
-            <n-button size="small" type="primary" ghost onClick={() => handleOrderDetail(row)}>
-              订单详情
-            </n-button>
+  {
+    key: 'info',
+    title: '客户信息',
+    align: 'center',
+    width: 220,
+    render: row => {
+      return (
+        <div class={'mt7'}>
+          <div>
+            {row.consigneeName}
+            {row.consigneeMobile}
           </div>
-        );
-      }
+          <div>{row.consigneeAddress}</div>
+        </div>
+      );
     }
-  ]
-});
+  },
+  {
+    key: 'status',
+    title: '订单状态',
+    align: 'center',
+    width: 120,
+    render: row => {
+      const statusKey = row.hbOrderStatus as keyof typeof orderStatus;
+      const statusText = orderStatus[statusKey] || '未知状态';
+      return <NTag class={'mt7'}>{statusText}</NTag>;
+    }
+  },
+  {
+    key: 'createTime',
+    title: '下单时间',
+    align: 'center',
+    width: 180,
+    render: row => {
+      return <div>{row.createTime?.replace('T', '  ')}</div>;
+    }
+  }
+];
 
-const [registerTable, { refresh, setTableLoading }] = useTable({
+const [registerTable, { refresh, setTableLoading, getSeachForm, getTableData }] = useTable({
+  searchFormConfig: {
+    schemas: [...SearchForm]
+  },
   tableConfig: {
     keyField: 'id',
     title: '',
     showAddButton: false,
-    showTableHeaderAction: false,
-    showSearch: false,
-    minHeight: 400
+    showTableHeaderAction: true,
+    fieldMapToTime: [['createTime', ['startTime', 'endTime']]]
   }
 });
 
@@ -285,12 +211,12 @@ function handleOrderDetail(row: Api.delivery.deliveryOrder) {
   // orderMoadl.value?.open(String(row.orderNumber));
 }
 async function getNums() {
-  const form = getFieldsValue();
+  const form = getSeachForm();
   const params = {
     ...form,
     channelIdList: channelIdList.value,
-    startTime: form.createTime ? form.createTime[0] : null,
-    endTime: form.createTime ? form.createTime[1] : null,
+    startTime: form?.createTime && form.createTime[0],
+    endTime: form?.createTime && form.createTime[1],
     createTime: null
   };
   const { data: keyData } = await fetchGetDeliveryStatusNum(params);
@@ -331,37 +257,8 @@ async function getNums() {
   statusList.value = updatedOrderStatusList;
 }
 
-watch(
-  () => [activeTab.value],
-  () => {
-    searchParams.pageNum = 1;
-    getData();
-  }
-);
-function handleSearch() {
-  const form = getFieldsValue();
-  if (form.createTime) {
-    form.startTime = form.createTime[0];
-    form.endTime = form.createTime[1];
-    delete form.createTime;
-  }
-  channelIdList.value = form.channelIdList;
-  searchForm.value = form;
-  getData();
-  getNums();
-}
-onMounted(() => {
-  getData();
-  getNums();
-});
+getNums();
 
-function handleReset() {
-  searchForm.value = getFieldsValue();
-  searchForm.value.channelIdList = channelIdList.value;
-  setFieldsValue({ channelIdList: channelIdList.value });
-  getData();
-  getNums();
-}
 // async function handleCopy(row: Api.delivery.deliveryOrder, key: string) {
 //   if (!row[key]) {
 //     window.$message?.error('订单编号不存在');
@@ -369,25 +266,16 @@ function handleReset() {
 //   }
 //   await copyTextToClipboard(row[key]);
 // }
-function handleRefsh() {
-  getData();
-  getNums();
-}
+
 async function handleExport() {
-  loading.value = true;
+  setTableLoading(true);
   try {
     // await commonExport(
     //   '/platform/order/export',
     //   { ...getFieldsValue(), orderStatus: activeTab.value },
     //   '正常订单列表.xlsx'
     // );
-    const newParams = getFieldsValue();
-    if (newParams.createTime) {
-      newParams.startTime = newParams.createTime[0];
-      newParams.endTime = newParams.createTime[1];
-      delete newParams.createTime;
-    }
-    await fetchExportOrderList({ ...newParams, orderStatus: activeTab.value });
+    await fetchExportOrderList({ ...getSeachForm(), orderStatus: activeTab.value });
     dialog.success({
       title: '提示',
       content: () => {
@@ -406,92 +294,88 @@ async function handleExport() {
       onNegativeClick: () => {}
     });
   } finally {
-    loading.value = false;
+    setTableLoading(false);
   }
 }
 
 async function handleExportLog() {
-  loading.value = true;
+  setTableLoading(true);
   try {
     openModal();
   } finally {
-    loading.value = false;
+    setTableLoading(false);
   }
 }
+watch(
+  () => [activeTab.value],
+  () => {
+    nextTick(() => {
+      refresh();
+    });
+  }
+);
 </script>
 
 <template>
-  <div class="flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
-    <NCard :bordered="false" size="small">
-      <NCollapse display-directive="show" default-expanded-names="search">
-        <NCollapseItem title="搜索" name="search">
-          <BasicForm @register-form="registerSearchForm" @submit="handleSearch" @reset="handleReset" />
-        </NCollapseItem>
-      </NCollapse>
-    </NCard>
-    <NCard :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
+  <LayoutTable>
+    <ZTable
+      :columns="columns"
+      :api="fetchGetDeliveryOrderList"
+      :default-params="{ orderStatus: activeTab }"
+      @register="registerTable"
+    >
+      <template #op="{ row }">
+        <NButton size="small" type="primary" ghost @click="handleOrderDetail(row)">订单详情</NButton>
+      </template>
       <template #header>
-        <div class="mr3">订单列表</div>
-        <NScrollbar x-scrollable>
-          <div class="flex items-center">
-            <div class="max-w-800px">
-              <NTabs v-model:value="activeTab" type="line" animated display-directive="show">
-                <NTab
-                  v-for="item in statusList"
-                  :key="item.value"
-                  :name="item.value"
-                  :tab="`${item.label}(${item.num})`"
-                ></NTab>
-              </NTabs>
+        <div class="flex items-center">
+          <NScrollbar x-scrollable>
+            <div class="flex items-center">
+              <div class="max-w-800px">
+                <NTabs v-model:value="activeTab" type="line" animated display-directive="show">
+                  <NTab
+                    v-for="item in statusList"
+                    :key="item.value"
+                    :name="item.value"
+                    :tab="`${item.label}(${item.num})`"
+                  ></NTab>
+                </NTabs>
+              </div>
             </div>
-          </div>
-        </NScrollbar>
+          </NScrollbar>
+        </div>
       </template>
-      <template #header-extra>
-        <NButton
-          v-if="useAuth().hasAuth('order:user:export')"
-          size="small"
-          type="primary"
-          class="ml20px mt30px"
-          ghost
-          :loading="loading"
-          :disabled="data.length == 0"
-          @click="handleExport"
-        >
-          <template #icon>
-            <SvgIcon icon="mingcute:file-export-line"></SvgIcon>
-          </template>
-          导出
-        </NButton>
-        <NButton
-          v-if="useAuth().hasAuth('order:user:export')"
-          size="small"
-          type="primary"
-          class="ml20px mt30px"
-          ghost
-          :loading="loading"
-          @click="handleExportLog"
-        >
-          导出记录
-        </NButton>
+      <template #prefix="{ loading }">
+        <div class="flex items-center">
+          <NButton
+            v-if="useAuth().hasAuth('order:user:export')"
+            size="small"
+            type="primary"
+            class="ml20px mt30px"
+            ghost
+            :loading="loading"
+            :disabled="getTableData().length == 0"
+            @click="handleExport"
+          >
+            <template #icon>
+              <SvgIcon icon="mingcute:file-export-line"></SvgIcon>
+            </template>
+            导出
+          </NButton>
+          <NButton
+            v-if="useAuth().hasAuth('order:user:export')"
+            size="small"
+            type="primary"
+            class="ml20px mt30px"
+            ghost
+            :loading="loading"
+            @click="handleExportLog"
+          >
+            导出记录
+          </NButton>
+        </div>
       </template>
-
-      <NDataTable
-        v-model:checked-row-keys="checkedRowKeys"
-        :columns="columns"
-        :data="data"
-        size="small"
-        :flex-height="!appStore.isMobile"
-        :scroll-x="1800"
-        :loading="loading"
-        :row-key="row => row.orderId"
-        remote
-        class="sm:h-full"
-        :pagination="mobilePagination"
-      />
-      <NormalMoadl ref="orderMoadl" @finish="handleRefsh"></NormalMoadl>
-      <DeliveryModal ref="Shipment" @finish="handleRefsh"></DeliveryModal>
-    </NCard>
+    </ZTable>
     <BasicModal @register="registerModalPrice" @after-leave="refresh">
       <LayoutTable>
         <ZTable
@@ -503,7 +387,7 @@ async function handleExportLog() {
         ></ZTable>
       </LayoutTable>
     </BasicModal>
-  </div>
+  </LayoutTable>
 </template>
 
 <style scoped></style>

+ 43 - 1
src/views/order-manage/normal-order/normal-order.ts

@@ -183,7 +183,36 @@ export const orderStatus = {
   [orderStatusEnum.ORDER_ARRIVE]: '订单已送达',
   [orderStatusEnum.ORDER_COMPLETE]: '订单已完成'
 };
-
+export enum yppStatusEnum {
+  /**
+   *
+   *  待支付
+   */
+  WAIT_PAY = 0,
+  /**
+   * 已支付
+   */
+  WAIT_DELIVERY = 1,
+  /**
+   * 出票成功
+   */
+  SUCCESS = 4,
+  /**
+   * 出票失败(退款)
+   */
+  REFUND_FAIL = 7,
+  /**
+   * 超时未支付(取消)
+   */
+  TIMEOUT_CANCEL = 8
+}
+export const yppStatus = {
+  [yppStatusEnum.WAIT_PAY]: '待支付',
+  [yppStatusEnum.WAIT_DELIVERY]: '已支付',
+  [yppStatusEnum.SUCCESS]: '出票成功',
+  [yppStatusEnum.REFUND_FAIL]: '出票失败(退款)',
+  [yppStatusEnum.TIMEOUT_CANCEL]: '超时未支付(取消)'
+};
 /**
  // 1:申请退款 2:退款成功 3:部分退款成功 4:退款失败
  *
@@ -284,3 +313,16 @@ export const deliveryInfo: FormSchema[] = [
     }
   }
 ];
+
+/**
+ * 0:用户手动停止充电;1:客户归属地运营商平台停止充电;2:BMS停止充电;3:充电机设备故障;4:连接器断开;其它:自定义
+ */
+
+export const chargeMethod = [
+  '用户手动停止充电',
+  '客户归属地运营商平台停止充电',
+  'BMS停止充电',
+  '充电机设备故障',
+  '连接器断开',
+  '其它'
+];

+ 213 - 61
src/views/order-manage/order-detail/index.vue

@@ -3,18 +3,22 @@ import { ref } from 'vue';
 import { useRoute } from 'vue-router';
 // import { NFlex } from 'naive-ui';
 import dayjs from 'dayjs';
+import duration from 'dayjs/plugin/duration';
 import { fetchGetNomalOrderInfo } from '@/service/api/order-manage/normal-order';
 // import { useAppStore } from '@/store/modules/app';
 // import { copyTextToClipboard } from '@/utils/zt';
-import { orderColumns, orderStatus, orderStatusEnum } from '../normal-order/normal-order';
-import DeliveryModal from '../normal-order/component/delivery-modal.vue';
+import {
+  chargeMethod,
+  orderColumns,
+  orderStatus,
+  orderStatusEnum,
+  yppStatus,
+  yppStatusEnum
+} from '../normal-order/normal-order';
 const orderInfo = ref<Api.delivery.deliveryOrder>();
 // const appStore = useAppStore();
 const TimeDown = ref<number>(0);
-// const ShipmentRef = useTemplateRef('Shipment');
-const emit = defineEmits<{
-  (e: 'finish'): void;
-}>();
+dayjs.extend(duration);
 const route = useRoute();
 async function open(orderNumber: string) {
   const { data, error } = await fetchGetNomalOrderInfo(orderNumber);
@@ -45,7 +49,7 @@ defineExpose({ open });
 //       return 1;
 //     case orderStatusEnum.ORDER_ACCEPT:
 //       return 2;
-//     case orderStatusEnum.ORDER_WAIT_DELIVERY:
+//     case orderStatusEnum.ORDER_WAIT_DELIVE RY:
 //       return 2;
 //     case orderStatusEnum.WAIT_DELIVERY:
 //       return 2;
@@ -63,6 +67,23 @@ defineExpose({ open });
 //   copyTextToClipboard(String(value));
 //   // copyTextToClipboard(String(orderInfo.value?.orderNumber));
 // }
+function secondsToTime(seconds: number) {
+  // 创建 duration 对象
+  const time = dayjs.duration(seconds, 'seconds');
+
+  // 获取时分秒
+  const hours = Math.floor(seconds / 3600);
+  const minutes = time.minutes();
+  const secs = time.seconds();
+
+  // 格式化为两位数字符串
+  const formatTime = (num: number) => num.toString().padStart(2, '0');
+
+  if (hours > 0) {
+    return `${formatTime(hours)}:${formatTime(minutes)}:${formatTime(secs)}`;
+  }
+  return `00:${formatTime(minutes)}:${formatTime(secs)}`;
+}
 </script>
 
 <template>
@@ -71,25 +92,35 @@ defineExpose({ open });
       <div class="mr-20px w-300px">
         <div class="mb-10px text-16px font-semibold">
           统一状态:
-          {{ orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus] }}
+          <template v-if="orderInfo.businessType != 'DYY'">
+            {{ orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus] }}
+          </template>
+          <template v-else>
+            {{ yppStatus[orderInfo.yppDetail?.orderState as keyof typeof yppStatus] }}
+          </template>
         </div>
         <NCard size="small" title="订单概览" :bordered="false">
           <div>订单编号:{{ orderInfo.orderNumber }}</div>
           <div>业务类型:{{ orderInfo.businessType }}</div>
           <div>
             订单状态:
-            {{
-              orderInfo.hbOrderStatus == 20 ||
-              orderInfo.hbOrderStatus == 30 ||
-              orderInfo.hbOrderStatus == 40 ||
-              orderInfo.hbOrderStatus == 70
-                ? '进行中'
-                : orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus]
-            }}
+            <template v-if="orderInfo.businessType != 'DYY'">
+              {{
+                orderInfo.hbOrderStatus == 20 ||
+                orderInfo.hbOrderStatus == 30 ||
+                orderInfo.hbOrderStatus == 40 ||
+                orderInfo.hbOrderStatus == 70
+                  ? '进行中'
+                  : orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus]
+              }}
+            </template>
+            <template v-else>
+              {{ yppStatus[orderInfo.yppDetail?.orderState as keyof typeof yppStatus] }}
+            </template>
           </div>
-          <div>订单金额:{{ orderInfo.actualTotal }}</div>
+          <div>订单金额:{{ orderInfo.orderMoney }}</div>
 
-          <div>订单创建:{{ orderInfo?.remarks || '暂无' }}</div>
+          <div>订单创建:{{ orderInfo?.createTime || '暂无' }}</div>
           <div>创建人:用户自助下单</div>
           <div>备注:{{ orderInfo?.remarks || '暂无' }}</div>
         </NCard>
@@ -123,57 +154,178 @@ defineExpose({ open });
         <div class="flex">
           <div class="mb-10px text-16px font-semibold">
             业务状态:
-            {{ orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus] }}
+            <template v-if="orderInfo.businessType != 'DYY'">
+              {{ orderStatus[orderInfo.hbOrderStatus as keyof typeof orderStatus] }}
+            </template>
+            <template v-else>
+              {{ yppStatus[orderInfo.yppDetail?.orderState as keyof typeof yppStatus] }}
+            </template>
           </div>
           <div class="mb-10px ml-20px text-16px font-semibold">第三方订单编号:{{ orderInfo.orderNumber }}</div>
         </div>
         <NCard size="small" title="业务信息" :bordered="false">
-          <div class="pb-20px font-semibold">01 商家信息</div>
-          <div>门店名称:{{ orderInfo.shopName || '暂无' }}</div>
-          <div>门店地址:{{ orderInfo.shopAddress || '暂无' }}</div>
-          <div>联系电话:{{ orderInfo.tel || '暂无' }}</div>
-          <div class="py-20px font-semibold">02 商品&费用</div>
-          <NDataTable :columns="orderColumns" :data="orderInfo.orderItemList" :bordered="false" />
+          <template v-if="orderInfo.businessType == 'XSB'">
+            <div class="pb-20px font-semibold">01 商家信息</div>
+            <div>门店名称:{{ orderInfo.shopName || '暂无' }}</div>
+            <div>门店地址:{{ orderInfo.shopAddress || '暂无' }}</div>
+            <div>联系电话:{{ orderInfo.tel || '暂无' }}</div>
+            <div class="py-20px font-semibold">02 商品&费用</div>
+            <NDataTable :columns="orderColumns" :data="orderInfo.orderItemList" :bordered="false" />
 
-          <NTable :single-line="false">
-            <NThead>
-              <NTr>
-                <NTh>费用类型</NTh>
-                <NTh>金额/元</NTh>
-              </NTr>
-            </NThead>
-            <NTbody>
-              <NTr>
-                <NTd>商品总额</NTd>
-                <NTd>{{ orderInfo.total }}</NTd>
-              </NTr>
-              <NTr>
-                <NTd>配送费(快递)</NTd>
-                <NTd>{{ orderInfo.freightAmount }}</NTd>
-              </NTr>
-              <NTr>
-                <NTd>积分</NTd>
-                <NTd>-{{ (Number(orderInfo.offsetPoints) / 100).toFixed(2) || 0 }}</NTd>
-              </NTr>
-              <NTr>
-                <NTd v-if="orderInfo.hbOrderStatus == orderStatusEnum.WAIT_PAY">需付款</NTd>
-                <NTd
-                  v-if="
-                    orderInfo.hbOrderStatus != orderStatusEnum.WAIT_PAY &&
-                    orderInfo.hbOrderStatus != orderStatusEnum.ORDER_CANCEL
-                  "
-                >
-                  实际付款
-                </NTd>
-                <NTd v-if="orderInfo.hbOrderStatus == orderStatusEnum.ORDER_CANCEL">应付款</NTd>
-                <NTd>{{ orderInfo.actualTotal }}</NTd>
-              </NTr>
-            </NTbody>
-          </NTable>
+            <NTable :single-line="false">
+              <NThead>
+                <NTr>
+                  <NTh>费用类型</NTh>
+                  <NTh>金额/元</NTh>
+                </NTr>
+              </NThead>
+              <NTbody>
+                <NTr>
+                  <NTd>商品总额</NTd>
+                  <NTd>{{ orderInfo.total }}</NTd>
+                </NTr>
+                <NTr>
+                  <NTd>配送费(快递)</NTd>
+                  <NTd>{{ orderInfo.freightAmount }}</NTd>
+                </NTr>
+                <NTr>
+                  <NTd>积分</NTd>
+                  <NTd>-{{ (Number(orderInfo.offsetPoints) / 100).toFixed(2) || 0 }}</NTd>
+                </NTr>
+                <NTr>
+                  <NTd v-if="orderInfo.hbOrderStatus == orderStatusEnum.WAIT_PAY">需付款</NTd>
+                  <NTd
+                    v-if="
+                      orderInfo.hbOrderStatus != orderStatusEnum.WAIT_PAY &&
+                      orderInfo.hbOrderStatus != orderStatusEnum.ORDER_CANCEL
+                    "
+                  >
+                    实际付款
+                  </NTd>
+                  <NTd v-if="orderInfo.hbOrderStatus == orderStatusEnum.ORDER_CANCEL">应付款</NTd>
+                  <NTd>{{ orderInfo.actualTotal }}</NTd>
+                </NTr>
+              </NTbody>
+            </NTable>
+          </template>
+          <template v-if="orderInfo.businessType == 'DYY'">
+            <div class="pb-20px font-semibold">01 影片与场次信息</div>
+            <div>影片名称:{{ orderInfo.yppDetail?.movieName || '暂无' }}</div>
+            <div class="flex items-center justify-between">
+              <div>影院名称:{{ orderInfo.yppDetail?.cinemaName || '--' }}</div>
+              <div>影厅:{{ orderInfo.yppDetail?.hallName || '--' }}</div>
+            </div>
+            <div class="flex items-center justify-between">
+              <div>放映时间:{{ orderInfo.yppDetail?.sessionBeginTime || '--' }}</div>
+              <div>结束时间:{{ orderInfo.yppDetail?.sessionEndTime || '--' }}</div>
+            </div>
+            <div class="flex items-center justify-between">
+              <div>版本类型:{{ orderInfo.yppDetail?.movieVersion || '--' }}</div>
+              <div>语言:{{ orderInfo.yppDetail?.movieVersion || '--' }}</div>
+            </div>
+            <div class="flex items-center justify-between">
+              <div>座位信息: {{ orderInfo.yppDetail?.seatNames || '--' }}</div>
+              <div>快速票:{{ orderInfo.yppDetail?.buyModel ? '是' : '否' }}</div>
+            </div>
+            <div class="pb-20px font-semibold">02 价格与结算信息</div>
+            <div v-if="yppStatusEnum.WAIT_PAY == orderInfo.yppDetail?.orderState" class="font-semibold">
+              【客户需支付信息】
+            </div>
+            <div
+              :class="[
+                yppStatusEnum.WAIT_PAY != orderInfo.yppDetail?.orderState ? 'flex items-center justify-between' : ''
+              ]"
+            >
+              <div v-if="yppStatusEnum.WAIT_PAY != orderInfo.yppDetail?.orderState">
+                <div class="font-semibold">【客户已支付信息】</div>
+                <div>市场原价(单价):¥{{ orderInfo.yppDetail?.originPrice || '--' }}</div>
+                <div>原票价总价:¥{{ orderInfo.yppDetail?.totalUserPrice || '--' }}</div>
+                <div>积分抵扣: ¥{{ (Number(orderInfo.offsetPoints) / 100).toFixed(2) || 0 }}</div>
+                <div>客户实际支付:¥{{ orderInfo.actualTotal }}</div>
+              </div>
+              <template v-if="yppStatusEnum.WAIT_PAY != orderInfo.yppDetail?.orderState">
+                <div>
+                  <div class="font-semibold">【预存款结算信息】</div>
+                  <div>企业扣款价:¥--</div>
+                  <div>结算价已退金额:¥--</div>
+                  <div>订单成本价:¥--</div>
+                </div>
+              </template>
+            </div>
+            <template
+              v-if="
+                [yppStatusEnum.SUCCESS, yppStatusEnum.REFUND_FAIL].includes(orderInfo.yppDetail?.orderState as number)
+              "
+            >
+              <div class="pb-20px font-semibold">03 取票与退款信息</div>
+              <div v-if="yppStatusEnum.SUCCESS == orderInfo.yppDetail?.orderState" class="flex flex-wrap items-center">
+                <div>
+                  <div>座位:{{ orderInfo.yppDetail?.seatNames }}</div>
+                  <div>取票码:{{ orderInfo.yppDetail?.ticketCode || '--' }}</div>
+                </div>
+              </div>
+              <template v-if="yppStatusEnum.REFUND_FAIL == orderInfo.yppDetail?.orderState">
+                <div class="flex">
+                  <div>退款金额:</div>
+                  <div>
+                    <div>{{ orderInfo.yppDetail?.epRefundAmount }}</div>
+                    <div class="text-gray">
+                      退还金额:{{ orderInfo.yppDetail?.epRefundAmount }}元 退还积分:{{ orderInfo.offsetPoints }}
+                    </div>
+                  </div>
+                </div>
+              </template>
+            </template>
+          </template>
+          <template v-if="orderInfo.businessType == 'CD'">
+            <div class="flex flex-wrap items-center">
+              <div class="mr10px flex-shrink-0">
+                <div>终端编号:{{ orderInfo.chargeOrder.connectorId || '--' }}</div>
+                <div>充电电站:{{ orderInfo.chargeOrder.powerStationName || '--' }}</div>
+                <div>充电开始时间:{{ orderInfo.chargeOrder.startTime || '--' }}</div>
+                <div>充电结束时间:{{ orderInfo.chargeOrder.endTime || '--' }}</div>
+                <div>充电时长:{{ secondsToTime(Number(orderInfo.chargeOrder.chargeTime)) || '--' }}</div>
+                <div>充电度数:{{ orderInfo.chargeOrder.totalPower || '--' }}</div>
+                <div>
+                  终止方式:{{ chargeMethod[orderInfo.chargeOrder.stopMethod as keyof typeof chargeMethod] || '--' }}
+                </div>
+                <div>详细地址:{{ orderInfo.chargeOrder.powerStationName?.split('-')[0] || '--' }}</div>
+              </div>
+              <NTable v-if="orderInfo.hbOrderStatus == orderStatusEnum.ORDER_COMPLETE" :single-line="false">
+                <NThead>
+                  <NTr>
+                    <NTh>费用类型</NTh>
+                    <NTh>金额/元</NTh>
+                  </NTr>
+                </NThead>
+                <NTbody>
+                  <NTr>
+                    <NTd>电费</NTd>
+                    <NTd>{{ orderInfo.chargeOrder.totalMoney }}</NTd>
+                  </NTr>
+                  <NTr>
+                    <NTd>结算服务费</NTd>
+                    <NTd>0</NTd>
+                  </NTr>
+                  <NTr>
+                    <NTd>运营服务费</NTd>
+                    <NTd>{{ orderInfo.chargeOrder.realServiceCost }}</NTd>
+                  </NTr>
+                  <NTr>
+                    <NTd>订单金额</NTd>
+                    <NTd>{{ orderInfo.actualTotal }}</NTd>
+                  </NTr>
+                  <NTr>
+                    <NTd>积分抵扣</NTd>
+                    <NTd>{{ orderInfo.actualTotal }}</NTd>
+                  </NTr>
+                </NTbody>
+              </NTable>
+            </div>
+          </template>
         </NCard>
       </div>
     </div>
-    <DeliveryModal ref="Shipment" @finish="emit('finish')"></DeliveryModal>
   </div>
 </template>