index.vue 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192
  1. <template>
  2. <div class="app-container h-full flex flex-1 flex-col">
  3. <!-- 搜索 -->
  4. <page-search
  5. ref="searchRef"
  6. :search-config="searchConfig"
  7. @query-click="handleQueryClick"
  8. @reset-click="handleResetClick"
  9. ></page-search>
  10. <!-- 列表 -->
  11. <page-content
  12. ref="contentRef"
  13. :content-config="contentConfig"
  14. @add-click="handleAddClick"
  15. @export-click="handleExportClick"
  16. @search-click="handleSearchClick"
  17. @toolbar-click="handleToolbarClick"
  18. @operate-click="handleOperateClick"
  19. @filter-change="handleFilterChange"
  20. >
  21. <template #stationType="scope">
  22. <DictLabel v-model="scope.row[scope.prop]" code="charging_station_type" />
  23. </template>
  24. <template #stationStatus="scope">
  25. <DictLabel v-model="scope.row[scope.prop]" code="charging_station_status" />
  26. </template>
  27. <template #construction="scope">
  28. <DictLabel v-model="scope.row[scope.prop]" code="charging_construction_type" />
  29. </template>
  30. <!-- // 0、平台 1、企业 2、渠道方-->
  31. <template #salesType="scope">
  32. <el-tag v-if="scope.row.salesType == 0" type="danger">平台</el-tag>
  33. <el-tag v-if="scope.row.salesType == 1" type="primary">企业</el-tag>
  34. <el-tag v-if="scope.row.salesType == 2" type="warning">渠道方</el-tag>
  35. </template>
  36. </page-content>
  37. <!-- 新增 -->
  38. <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
  39. <template #addServiceFee>
  40. <el-table v-loading="loading" :data="getServiceFeeData()" style="width: 100%">
  41. <el-table-column prop="periodFlag" label="时段标志" width="180">
  42. <template #default="scope">
  43. <span v-if="scope.row.periodFlag === 1">尖</span>
  44. <span v-else-if="scope.row.periodFlag === 2">峰</span>
  45. <span v-else-if="scope.row.periodFlag === 3">平</span>
  46. <span v-else-if="scope.row.periodFlag === 4">谷</span>
  47. <span v-else>{{ scope.row.periodFlag }}</span>
  48. </template>
  49. </el-table-column>
  50. <el-table-column prop="timePeriod" label="时间段" width="180"></el-table-column>
  51. <el-table-column
  52. prop="electricityFee"
  53. label="电费(元/度)"
  54. width="180"
  55. ></el-table-column>
  56. <el-table-column
  57. prop="settlementServiceFee"
  58. label="结算服务费(元)"
  59. width="180"
  60. ></el-table-column>
  61. <el-table-column
  62. prop="settlementFeeTotal"
  63. label="结算费合计(元/度)"
  64. width="180"
  65. ></el-table-column>
  66. <el-table-column prop="operationServiceFee" label="运营服务费(元)" width="180">
  67. <template #default="scope">
  68. <div v-if="editingRowIndex === scope.$index">
  69. <el-input v-model="editingValue" size="small"></el-input>
  70. <ElButton
  71. type="text"
  72. size="small"
  73. @click="saveEditOperationServiceFee(scope.row, scope.$index)"
  74. >
  75. 保存
  76. </ElButton>
  77. <ElButton type="text" size="small" @click="cancelEditOperationServiceFee">
  78. 取消
  79. </ElButton>
  80. </div>
  81. <span
  82. v-else
  83. class="text-#0FDCC4"
  84. @click="startEditOperationServiceFee(scope.row, scope.$index)"
  85. >
  86. {{ scope.row.operationServiceFee }}
  87. </span>
  88. </template>
  89. </el-table-column>
  90. <el-table-column
  91. prop="valueAddedFees"
  92. label="增值费用(元)"
  93. width="180"
  94. ></el-table-column>
  95. <el-table-column
  96. prop="salesTotalPrice"
  97. label="销售合计价格(元/度)"
  98. width="180"
  99. ></el-table-column>
  100. </el-table>
  101. </template>
  102. <template #addStationFee="{ formData }">
  103. <div class="flex items-center">
  104. <el-input
  105. v-model="formData.stationFee"
  106. placeholder="请输入整站设置统一服务费"
  107. style="width: 200px"
  108. @keyup.enter="() => confirmUniformFeeSetting(formData.stationFee)"
  109. />
  110. <ElButton
  111. type="primary"
  112. size="small"
  113. class="ml-2"
  114. @click="() => confirmUniformFeeSetting(formData.stationFee)"
  115. >
  116. 确定
  117. </ElButton>
  118. </div>
  119. </template>
  120. </page-modal>
  121. <!-- 编辑 -->
  122. <page-modal
  123. ref="editModalRef"
  124. :modal-config="editModalConfig"
  125. @submit-click="handleSubmitClick"
  126. >
  127. <template #editServiceFee>
  128. <el-table v-loading="loading" :data="getServiceFeeData()" style="width: 100%">
  129. <el-table-column prop="periodFlag" label="时段标志" width="180">
  130. <template #default="scope">
  131. <span v-if="scope.row.periodFlag === 1">尖</span>
  132. <span v-else-if="scope.row.periodFlag === 2">峰</span>
  133. <span v-else-if="scope.row.periodFlag === 3">平</span>
  134. <span v-else-if="scope.row.periodFlag === 4">谷</span>
  135. <span v-else>{{ scope.row.periodFlag }}</span>
  136. </template>
  137. </el-table-column>
  138. <el-table-column prop="timePeriod" label="时间段" width="180"></el-table-column>
  139. <el-table-column
  140. prop="electricityFee"
  141. label="电费(元/度)"
  142. width="180"
  143. ></el-table-column>
  144. <el-table-column
  145. prop="settlementServiceFee"
  146. label="结算服务费(元)"
  147. width="180"
  148. ></el-table-column>
  149. <el-table-column
  150. prop="settlementFeeTotal"
  151. label="结算费合计(元/度)"
  152. width="180"
  153. ></el-table-column>
  154. <el-table-column prop="operationServiceFee" label="运营服务费(元)" width="180">
  155. <template #default="scope">
  156. <div v-if="editingRowIndex === scope.$index">
  157. <el-input v-model="editingValue" size="small"></el-input>
  158. <ElButton
  159. type="text"
  160. size="small"
  161. @click="saveEditOperationServiceFee(scope.row, scope.$index)"
  162. >
  163. 保存
  164. </ElButton>
  165. <ElButton type="text" size="small" @click="cancelEditOperationServiceFee">
  166. 取消
  167. </ElButton>
  168. </div>
  169. <span
  170. v-else
  171. class="text-#0FDCC4"
  172. @click="startEditOperationServiceFee(scope.row, scope.$index)"
  173. >
  174. {{ scope.row.operationServiceFee }}
  175. </span>
  176. </template>
  177. </el-table-column>
  178. <el-table-column
  179. prop="valueAddedFees"
  180. label="增值费用(元)"
  181. width="180"
  182. ></el-table-column>
  183. <el-table-column
  184. prop="salesTotalPrice"
  185. label="销售合计价格(元/度)"
  186. width="180"
  187. ></el-table-column>
  188. </el-table>
  189. </template>
  190. <template #editStationFee="{ formData }">
  191. <div class="flex items-center">
  192. <el-input
  193. v-model="formData.stationFee"
  194. placeholder="请输入整站设置统一服务费"
  195. style="width: 200px"
  196. @keyup.enter="() => confirmUniformFeeSetting(formData.stationFee)"
  197. />
  198. <ElButton
  199. type="primary"
  200. size="small"
  201. class="ml-2"
  202. @click="() => confirmUniformFeeSetting(formData.stationFee)"
  203. >
  204. 确定
  205. </ElButton>
  206. </div>
  207. </template>
  208. </page-modal>
  209. </div>
  210. </template>
  211. <script setup lang="ts">
  212. defineOptions({ name: "ThirdPartyStationInfo" });
  213. import { ref, h } from "vue";
  214. import { ElMessage, ElMessageBox, ElButton } from "element-plus";
  215. import ThirdPartyStationInfoAPI, {
  216. ThirdPartyStationInfoForm,
  217. ThirdPartyStationInfoPageQuery,
  218. } from "@/api/operationsManage/billing-strategy-api";
  219. import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
  220. import usePage from "@/components/CURD/usePage";
  221. import { useDictStoreHook } from "@/store/modules/dict-store"; // 添加字典存储导入
  222. // 组合式 CRUD
  223. const {
  224. searchRef,
  225. contentRef,
  226. addModalRef,
  227. editModalRef,
  228. handleQueryClick,
  229. handleResetClick,
  230. handleAddClick: originalHandleAddClick,
  231. handleEditClick,
  232. handleExportClick,
  233. handleSearchClick,
  234. handleFilterChange,
  235. } = usePage();
  236. // 自定义新增点击处理函数,在打开新增模态框时重置统一费用设置状态
  237. const handleAddClick = () => {
  238. // 重置统一费用设置状态
  239. isUniformFeeSet.value = false;
  240. // 调用原始的新增点击处理函数
  241. originalHandleAddClick();
  242. };
  243. // 字典store
  244. const dictStore = useDictStoreHook();
  245. // 搜索配置
  246. const searchConfig: ISearchConfig = reactive({
  247. permPrefix: "system:billing-strategy",
  248. formItems: [
  249. {
  250. type: "input",
  251. label: "充电站ID",
  252. prop: "stationId",
  253. attrs: {
  254. placeholder: "充电站ID",
  255. clearable: true,
  256. style: { width: "200px" },
  257. },
  258. },
  259. {
  260. type: "input",
  261. label: "充电站名称",
  262. prop: "stationName",
  263. attrs: {
  264. placeholder: "充电站名称",
  265. clearable: true,
  266. style: { width: "200px" },
  267. },
  268. },
  269. {
  270. type: "select", // 修改为select类型
  271. label: "站点状态",
  272. prop: "stationStatus",
  273. attrs: {
  274. placeholder: "请选择站点状态",
  275. clearable: true,
  276. style: { width: "200px" },
  277. },
  278. async initFn(formItem) {
  279. await dictStore.loadDictItems("charging_station_status");
  280. formItem.options = dictStore.getDictItems("charging_station_status");
  281. },
  282. },
  283. ],
  284. });
  285. // 列表配置
  286. const contentConfig: IContentConfig<ThirdPartyStationInfoPageQuery> = reactive({
  287. // 权限前缀
  288. permPrefix: "system:billing-strategy",
  289. table: {
  290. border: true,
  291. highlightCurrentRow: true,
  292. },
  293. // 主键
  294. pk: "id",
  295. // 列表查询接口
  296. indexAction: ThirdPartyStationInfoAPI.getPage,
  297. // 删除接口
  298. deleteAction: ThirdPartyStationInfoAPI.deleteByIds,
  299. // 数据解析函数
  300. parseData(res: any) {
  301. return {
  302. total: res.total,
  303. list: res.list,
  304. };
  305. },
  306. // 分页配置
  307. pagination: {
  308. background: true,
  309. layout: "total, sizes, prev, pager, next, jumper",
  310. pageSize: 20,
  311. pageSizes: [10, 20, 30, 50],
  312. },
  313. // 工具栏配置
  314. toolbar: ["add", "delete"],
  315. defaultToolbar: ["refresh", "filter"],
  316. // 表格列配置
  317. cols: [
  318. { type: "selection", width: 55, align: "center" },
  319. { label: "充电站ID", prop: "stationId" },
  320. {
  321. label: "充电站名称",
  322. prop: "stationName",
  323. },
  324. {
  325. label: "单位名称",
  326. prop: "unitName",
  327. formatter: (row: any) => {
  328. return row.unitName || "--";
  329. },
  330. },
  331. { label: "省市辖区编码", prop: "areaCode" },
  332. { label: "详细地址", prop: "address" },
  333. { label: "服务电话", prop: "serviceTel" },
  334. { label: "站点类型", prop: "stationType", templet: "custom" },
  335. // { label: "站点类型", prop: "stationTips" },
  336. {
  337. label: "站点状态",
  338. prop: "stationStatus",
  339. templet: "custom",
  340. },
  341. {
  342. label: "收费类型",
  343. prop: "salesType",
  344. templet: "custom",
  345. },
  346. {
  347. label: "操作",
  348. prop: "operation",
  349. width: 220,
  350. templet: "tool",
  351. operat: [
  352. "edit",
  353. "delete",
  354. // {
  355. // name: "setTips",
  356. // text: "设置提示语",
  357. // attrs: {
  358. // type: "success",
  359. // },
  360. // },
  361. ],
  362. },
  363. ],
  364. });
  365. const stationId = ref();
  366. // 新增配置
  367. const addModalConfig: IModalConfig<ThirdPartyStationInfoForm> = reactive({
  368. // 权限前缀
  369. permPrefix: "system:billing-strategy",
  370. // 主键
  371. pk: "id",
  372. // 弹窗配置
  373. dialog: {
  374. title: "新增",
  375. width: 1200,
  376. draggable: true,
  377. },
  378. form: {
  379. labelWidth: 140,
  380. },
  381. // 表单项配置
  382. formItems: [
  383. {
  384. type: "select",
  385. attrs: {
  386. placeholder: "请选择充电站",
  387. style: {
  388. width: "200px",
  389. },
  390. },
  391. rules: [{ required: true, message: "充电站不能为空", trigger: "blur" }],
  392. label: "选择电站",
  393. prop: "stationId",
  394. options: [],
  395. events: {
  396. change: (value: string) => {
  397. stationId.value = value;
  398. const formData = addModalRef.value?.getFormData();
  399. const salesType = formData?.salesType;
  400. if (value && salesType !== undefined) {
  401. // 平台类型直接调用
  402. if (salesType === 0) {
  403. fetchBillingStrategy(value, salesType, 0, 0);
  404. }
  405. // 企业类型需要检查是否有企业ID
  406. else if (salesType === 1) {
  407. const firmId = formData?.firmId;
  408. if (firmId) {
  409. fetchBillingStrategy(value, salesType, 0, firmId);
  410. }
  411. }
  412. // 渠道方类型需要检查是否有渠道方ID
  413. else if (salesType === 2) {
  414. const thirdPartyId = formData?.id;
  415. if (thirdPartyId) {
  416. fetchBillingStrategy(value, salesType, thirdPartyId, 0);
  417. }
  418. }
  419. }
  420. },
  421. },
  422. async initFn(formItem) {
  423. try {
  424. // 调用接口获取充电站数据
  425. const response = await ThirdPartyStationInfoAPI.getChargingPileSelect();
  426. if (Array.isArray(response)) {
  427. formItem.options = response.map((item) => ({
  428. label: item.stationName,
  429. value: item.id,
  430. }));
  431. } else {
  432. console.warn("接口返回数据格式不正确,期望数组格式:", response);
  433. formItem.options = [];
  434. }
  435. } catch (error) {
  436. console.error("获取充电站数据失败:", error);
  437. formItem.options = [];
  438. }
  439. },
  440. },
  441. {
  442. type: "radio",
  443. label: "收费类型",
  444. prop: "salesType",
  445. attrs: {
  446. placeholder: "请选择收费类型",
  447. },
  448. // 0、平台 1、企业 2、渠道方
  449. options: [
  450. {
  451. label: "平台",
  452. value: 0,
  453. },
  454. {
  455. label: "企业",
  456. value: 1,
  457. },
  458. {
  459. label: "渠道方",
  460. value: 2,
  461. },
  462. ],
  463. rules: [{ required: true, message: "收费类型不能为空", trigger: "blur" }],
  464. events: {
  465. change: (value: number) => {
  466. const formData = addModalRef.value?.getFormData();
  467. const stationId = formData?.stationId;
  468. if (stationId && value !== undefined) {
  469. if (value === 0) {
  470. fetchBillingStrategy(stationId, value, 0, 0);
  471. } else if (value === 1) {
  472. serviceFeeData.value = [];
  473. const firmId = formData?.firmId;
  474. if (firmId) {
  475. fetchBillingStrategy(stationId, value, 0, firmId);
  476. }
  477. } else if (value === 2) {
  478. serviceFeeData.value = [];
  479. const thirdPartyId = formData?.id;
  480. if (thirdPartyId) {
  481. fetchBillingStrategy(stationId, value, thirdPartyId, 0);
  482. }
  483. }
  484. }
  485. },
  486. },
  487. },
  488. {
  489. type: "select",
  490. label: "选择企业",
  491. prop: "firmId",
  492. attrs: {
  493. placeholder: "请选择企业",
  494. style: {
  495. width: "200px",
  496. },
  497. },
  498. rules: [
  499. {
  500. required: true,
  501. message: "企业不能为空",
  502. trigger: "blur",
  503. validator: (_, value, callback, formData) => {
  504. // 只有当收费类型为1(企业)时才校验
  505. if (formData.salesType === 1) {
  506. if (!value) {
  507. callback(new Error("企业不能为空"));
  508. } else {
  509. callback();
  510. }
  511. } else {
  512. callback();
  513. }
  514. },
  515. },
  516. ],
  517. options: [],
  518. visible: (formData: any) => {
  519. return formData.salesType === 1;
  520. },
  521. events: {
  522. change: (value: number) => {
  523. const formData = addModalRef.value?.getFormData();
  524. const stationId = formData?.stationId;
  525. const salesType = formData?.salesType;
  526. if (stationId && salesType === 1 && value) {
  527. fetchBillingStrategy(stationId, salesType, 0, value);
  528. }
  529. },
  530. },
  531. async initFn(formItem) {
  532. try {
  533. // 调用接口获取企业数据
  534. const response = await ThirdPartyStationInfoAPI.getFirmSelect();
  535. if (Array.isArray(response)) {
  536. formItem.options = response.map((item) => ({
  537. label: item.name,
  538. value: item.id,
  539. }));
  540. }
  541. } catch (error) {
  542. console.error("获取企业数据失败:", error);
  543. }
  544. },
  545. },
  546. {
  547. type: "select",
  548. label: "选择渠道方",
  549. prop: "id",
  550. attrs: {
  551. placeholder: "请选择渠道方",
  552. style: {
  553. width: "200px",
  554. },
  555. },
  556. rules: [
  557. {
  558. required: true,
  559. message: "渠道方不能为空",
  560. trigger: "blur",
  561. validator: (_, value, callback, formData) => {
  562. if (formData.salesType === 2) {
  563. if (!value) {
  564. callback(new Error("渠道方不能为空"));
  565. } else {
  566. callback();
  567. }
  568. } else {
  569. callback();
  570. }
  571. },
  572. },
  573. ],
  574. options: [],
  575. visible: (formData: any) => {
  576. return formData.salesType === 2;
  577. },
  578. events: {
  579. change: (value: number) => {
  580. // 当渠道方选择改变时调用接口
  581. const formData = addModalRef.value?.getFormData();
  582. const stationId = formData?.stationId;
  583. const salesType = formData?.salesType;
  584. if (stationId && salesType === 2 && value) {
  585. fetchBillingStrategy(stationId, salesType, value, 0);
  586. }
  587. },
  588. },
  589. async initFn(formItem) {
  590. try {
  591. // 调用接口获取渠道方数据
  592. const response = await ThirdPartyStationInfoAPI.getChannelSelect();
  593. if (Array.isArray(response)) {
  594. formItem.options = response.map((item) => ({
  595. label: item.stationName,
  596. value: item.id,
  597. }));
  598. }
  599. } catch (error) {
  600. console.error("获取渠道方数据失败:", error);
  601. }
  602. },
  603. },
  604. {
  605. type: "custom",
  606. label: "整站设置统一服务费",
  607. prop: "stationFee",
  608. slotName: "addStationFee",
  609. },
  610. {
  611. type: "custom",
  612. label: "服务费设置",
  613. prop: "serviceFee",
  614. slotName: "addServiceFee",
  615. },
  616. ],
  617. // 提交函数
  618. formAction: (data: ThirdPartyStationInfoForm) => {
  619. // 检查统一费用是否已设置
  620. if (!isUniformFeeSet.value) {
  621. ElMessageBox.confirm("统一费用尚未设置,确定要提交吗?", "确认提交", {
  622. cancelButtonText: "去设置",
  623. type: "warning",
  624. })
  625. .then(() => {
  626. // 用户确认提交,即使未设置统一费用
  627. if (data.id) {
  628. // 编辑
  629. // return ThirdPartyStationInfoAPI.update(data.id as string, data);
  630. return Promise.resolve();
  631. } else {
  632. // return ThirdPartyStationInfoAPI.create(data);
  633. return Promise.resolve();
  634. }
  635. })
  636. .catch(() => {
  637. // 用户选择去设置,不提交
  638. ElMessage.info("请先设置统一费用");
  639. return Promise.reject(new Error("未设置统一费用"));
  640. });
  641. } else {
  642. // 统一费用已设置,正常提交
  643. if (data.id) {
  644. // 编辑
  645. // return ThirdPartyStationInfoAPI.update(data.id as string, data);
  646. return Promise.resolve();
  647. } else {
  648. // return ThirdPartyStationInfoAPI.create(data);
  649. return Promise.resolve();
  650. }
  651. }
  652. },
  653. });
  654. // 编辑配置
  655. const editModalConfig: IModalConfig<ThirdPartyStationInfoForm> = reactive({
  656. permPrefix: "system:billing-strategy",
  657. component: "drawer",
  658. drawer: {
  659. title: "编辑",
  660. size: 1000,
  661. },
  662. pk: "id",
  663. formAction(data: any) {
  664. // 检查统一费用是否已设置
  665. if (!isUniformFeeSet.value) {
  666. return ElMessageBox.confirm("统一费用尚未设置,确定要提交吗?", "确认提交", {
  667. confirmButtonText: "确定提交",
  668. cancelButtonText: "去设置",
  669. type: "warning",
  670. })
  671. .then(() => {
  672. // 用户确认提交,即使未设置统一费用
  673. return Promise.resolve();
  674. })
  675. .catch(() => {
  676. // 用户选择去设置,不提交
  677. ElMessage.info("请先设置统一费用");
  678. return Promise.reject(new Error("未设置统一费用"));
  679. });
  680. } else {
  681. // 统一费用已设置,正常提交
  682. return Promise.resolve();
  683. }
  684. },
  685. formItems: addModalConfig.formItems.map((item) => {
  686. // 为编辑模态框复制表单项,并添加相应的事件监听器
  687. if (item.prop === "stationId") {
  688. return {
  689. ...item,
  690. events: {
  691. change: (value: string) => {
  692. // 当充电站选择改变时,根据当前的收费类型进行不同处理
  693. const formData = editModalRef.value?.getFormData();
  694. const salesType = formData?.salesType;
  695. if (value && salesType !== undefined) {
  696. // 平台类型直接调用
  697. if (salesType === 0) {
  698. fetchBillingStrategy(value, salesType, null, null);
  699. }
  700. // 企业类型需要检查是否有企业ID
  701. else if (salesType === 1) {
  702. serviceFeeData.value = [];
  703. const firmId = formData?.firmId;
  704. if (firmId) {
  705. fetchBillingStrategy(value, salesType, null, firmId);
  706. }
  707. }
  708. // 渠道方类型需要检查是否有渠道方ID
  709. else if (salesType === 2) {
  710. serviceFeeData.value = [];
  711. const thirdPartyId = formData?.id; // 注意这里使用的是 id 字段
  712. if (thirdPartyId) {
  713. fetchBillingStrategy(value, salesType, thirdPartyId, null);
  714. }
  715. }
  716. }
  717. },
  718. },
  719. // 在编辑模式下确保选项已加载
  720. async initFn(formItem) {
  721. try {
  722. // 调用接口获取充电站数据
  723. const response = await ThirdPartyStationInfoAPI.getChargingPileSelect();
  724. if (Array.isArray(response)) {
  725. formItem.options = response.map((item) => ({
  726. label: item.stationName,
  727. value: item.id,
  728. }));
  729. } else {
  730. console.warn("接口返回数据格式不正确,期望数组格式:", response);
  731. formItem.options = [];
  732. }
  733. } catch (error) {
  734. console.error("获取充电站数据失败:", error);
  735. formItem.options = [];
  736. }
  737. },
  738. };
  739. } else if (item.prop === "salesType") {
  740. return {
  741. ...item,
  742. events: {
  743. change: (value: number) => {
  744. // 当收费类型选择改变时,根据不同的收费类型进行不同处理
  745. const formData = editModalRef.value?.getFormData();
  746. const stationId = formData?.stationId;
  747. if (stationId && value !== undefined) {
  748. // 平台类型直接调用
  749. if (value === 0) {
  750. fetchBillingStrategy(stationId, value, null, null);
  751. }
  752. // 企业类型需要等待企业选择后再调用
  753. else if (value === 1) {
  754. const firmId = formData?.firmId;
  755. if (firmId) {
  756. fetchBillingStrategy(stationId, value, null, firmId);
  757. }
  758. }
  759. // 渠道方类型需要等待渠道方选择后再调用
  760. else if (value === 2) {
  761. const thirdPartyId = formData?.id; // 注意这里使用的是 id 字段
  762. if (thirdPartyId) {
  763. fetchBillingStrategy(stationId, value, thirdPartyId, null);
  764. }
  765. }
  766. }
  767. },
  768. },
  769. };
  770. } else if (item.prop === "firmId") {
  771. // 企业下拉框添加显隐控制和change事件
  772. return {
  773. ...item,
  774. rules: [
  775. {
  776. required: true,
  777. message: "企业不能为空",
  778. trigger: "blur",
  779. validator: (_, value, callback, formData) => {
  780. // 只有当收费类型为1(企业)时才校验
  781. if (formData.salesType === 1) {
  782. if (!value) {
  783. callback(new Error("企业不能为空"));
  784. } else {
  785. callback();
  786. }
  787. } else {
  788. callback();
  789. }
  790. },
  791. },
  792. ],
  793. visible: (formData: any) => {
  794. // 只有当收费类型为1(企业)时才显示
  795. return formData.salesType === 1;
  796. },
  797. events: {
  798. change: (value: number) => {
  799. // 当企业选择改变时调用接口
  800. const formData = editModalRef.value?.getFormData();
  801. const stationId = formData?.stationId;
  802. const salesType = formData?.salesType;
  803. if (stationId && salesType === 1 && value) {
  804. fetchBillingStrategy(stationId, salesType, 0, value);
  805. }
  806. },
  807. },
  808. // 在编辑模式下确保选项已加载
  809. async initFn(formItem) {
  810. try {
  811. // 调用接口获取企业数据
  812. const response = await ThirdPartyStationInfoAPI.getFirmSelect();
  813. if (Array.isArray(response)) {
  814. formItem.options = response.map((item) => ({
  815. label: item.name,
  816. value: item.id,
  817. }));
  818. }
  819. } catch (error) {
  820. console.error("获取企业数据失败:", error);
  821. }
  822. },
  823. };
  824. } else if (item.prop === "id") {
  825. // 渠道方下拉框添加显隐控制和change事件
  826. return {
  827. ...item,
  828. rules: [
  829. {
  830. required: true,
  831. message: "渠道方不能为空",
  832. trigger: "blur",
  833. validator: (_, value, callback, formData) => {
  834. // 只有当收费类型为2(渠道方)时才校验
  835. if (formData.salesType === 2) {
  836. if (!value) {
  837. callback(new Error("渠道方不能为空"));
  838. } else {
  839. callback();
  840. }
  841. } else {
  842. callback();
  843. }
  844. },
  845. },
  846. ],
  847. visible: (formData: any) => {
  848. // 只有当收费类型为2(渠道方)时才显示
  849. return formData.salesType === 2;
  850. },
  851. events: {
  852. change: (value: number) => {
  853. // 当渠道方选择改变时调用接口
  854. const formData = editModalRef.value?.getFormData();
  855. const stationId = formData?.stationId;
  856. const salesType = formData?.salesType;
  857. if (stationId && salesType === 2 && value) {
  858. fetchBillingStrategy(stationId, salesType, value, 0);
  859. }
  860. },
  861. },
  862. // 在编辑模式下确保选项已加载
  863. async initFn(formItem) {
  864. try {
  865. // 调用接口获取渠道方数据
  866. const response = await ThirdPartyStationInfoAPI.getChannelSelect();
  867. if (Array.isArray(response)) {
  868. formItem.options = response.map((item) => ({
  869. label: item.stationName,
  870. value: item.id,
  871. }));
  872. }
  873. } catch (error) {
  874. console.error("获取渠道方数据失败:", error);
  875. }
  876. },
  877. };
  878. } else if (item.prop === "stationFee") {
  879. // 整站设置统一服务费
  880. return {
  881. ...item,
  882. type: "custom",
  883. slotName: "editStationFee",
  884. };
  885. } else if (item.prop === "serviceFee") {
  886. // 服务费设置
  887. return {
  888. ...item,
  889. type: "custom",
  890. slotName: "editServiceFee",
  891. };
  892. }
  893. return item;
  894. }),
  895. });
  896. // 处理操作按钮点击
  897. const handleOperateClick = (data: IObject) => {
  898. if (data.name === "edit") {
  899. handleEditClick(data.row, async () => {
  900. // 根据销售类型传入相应参数
  901. const stationId = data.row.id;
  902. const salesType = data.row.salesType !== undefined ? data.row.salesType : 0; // 默认为平台类型
  903. let firmId = 0;
  904. let thirdPartyId = 0;
  905. // 根据销售类型设置相应参数
  906. if (salesType === 1) {
  907. firmId = data.row.firmId || 0;
  908. } else if (salesType === 2) {
  909. thirdPartyId = data.row.thirdPartyId || data.row.id || 0;
  910. }
  911. // 重置统一费用设置状态
  912. isUniformFeeSet.value = false;
  913. // 获取表单数据
  914. const formData = await ThirdPartyStationInfoAPI.getFormData(
  915. stationId,
  916. salesType,
  917. firmId,
  918. thirdPartyId
  919. );
  920. // 获取服务费策略数据并填充到表格中
  921. fetchBillingStrategy(stationId, salesType, thirdPartyId, firmId);
  922. // 确保表单数据包含必要的字段用于回显
  923. const enrichedFormData = {
  924. ...formData,
  925. stationId,
  926. salesType,
  927. firmId: firmId || formData.firmId,
  928. id: thirdPartyId || formData.id, // 注意这里使用的是 id 字段存储 thirdPartyId
  929. };
  930. // 返回表单数据用于回显
  931. return enrichedFormData;
  932. });
  933. } else if (data.name === "setTips") {
  934. // 显示设置提示语弹窗
  935. ElMessageBox.prompt("设置提示语", {
  936. confirmButtonText: "确定",
  937. cancelButtonText: "取消",
  938. inputPlaceholder: "请输入提示语",
  939. inputValue: data.row.tips || "",
  940. inputPattern: /^.{0,100}$/,
  941. inputErrorMessage: "提示语长度不能超过100个字符",
  942. })
  943. .then(async ({ value }) => {
  944. try {
  945. await ThirdPartyStationInfoAPI.setTips({
  946. stationId: parseInt(data.row.id),
  947. stationTips: value,
  948. });
  949. ElMessage.success("提示语设置成功");
  950. // 刷新列表
  951. contentRef.value?.getList();
  952. } catch (error) {
  953. console.error("设置提示语失败:", error);
  954. }
  955. })
  956. .catch(() => {
  957. ElMessage.info("取消设置");
  958. });
  959. }
  960. };
  961. // 处理工具栏按钮点击(删除等)
  962. const handleToolbarClick = (name: string) => {
  963. console.log(name);
  964. };
  965. // 存储从接口获取的服务费数据
  966. const serviceFeeData = ref([]);
  967. const loading = ref(false);
  968. // 存储正在编辑的行索引
  969. const editingRowIndex = ref(-1);
  970. // 存储编辑时的临时值
  971. const editingValue = ref("");
  972. // 跟踪统一费用是否已设置
  973. const isUniformFeeSet = ref(false);
  974. // 获取服务费表格数据
  975. const getServiceFeeData = () => {
  976. return serviceFeeData.value;
  977. };
  978. // 调用getBillingStrategy接口获取服务费数据
  979. const fetchBillingStrategy = async (
  980. stationId: string,
  981. salesType: number,
  982. thirdPartyId: number = 0,
  983. firmId: number = 0
  984. ) => {
  985. loading.value = true;
  986. try {
  987. const response = await ThirdPartyStationInfoAPI.getBillingStrategy(
  988. stationId,
  989. salesType,
  990. thirdPartyId,
  991. firmId
  992. );
  993. console.log("获取计费策略数据:", response);
  994. // 格式化时间函数
  995. const formatTimePeriod = (timeStr: string) => {
  996. if (!timeStr || timeStr.length !== 6) return timeStr;
  997. const hours = timeStr.substring(0, 2);
  998. const minutes = timeStr.substring(2, 4);
  999. const seconds = timeStr.substring(4, 6);
  1000. return `${hours}:${minutes}:${seconds}`;
  1001. };
  1002. if (response && Array.isArray(response)) {
  1003. serviceFeeData.value = response.map((item) => ({
  1004. timePeriod: formatTimePeriod(item.timePeriod),
  1005. electricityFee: item.electricityPrice,
  1006. settlementServiceFee: item.settlementServiceFee,
  1007. settlementFeeTotal: item.settlementTotalPrice,
  1008. operationServiceFee: item.operationServiceFee,
  1009. salesTotalPrice: item.saleTotalPrice,
  1010. valueAddedFees: item.valueAddedFees,
  1011. periodFlag: item.periodFlag,
  1012. }));
  1013. } else {
  1014. serviceFeeData.value = [];
  1015. }
  1016. } catch (error) {
  1017. console.error("获取计费策略数据失败:", error);
  1018. serviceFeeData.value = [];
  1019. } finally {
  1020. loading.value = false;
  1021. }
  1022. };
  1023. // 开始编辑运营服务费
  1024. const startEditOperationServiceFee = (row: any, index: number) => {
  1025. editingRowIndex.value = index;
  1026. editingValue.value = row.operationServiceFee || "";
  1027. };
  1028. // 保存编辑的运营服务费
  1029. const saveEditOperationServiceFee = async (row: any, index: number) => {
  1030. // 验证输入值是否为数字(整数或小数)
  1031. if (!/^-?\d+(\.\d+)?$/.test(editingValue.value)) {
  1032. ElMessage.error("请输入有效的数字");
  1033. return;
  1034. }
  1035. // 获取当前表单数据以获取必要参数
  1036. let formData = addModalRef.value?.getFormData();
  1037. if (!formData || !formData.stationId) {
  1038. formData = editModalRef.value?.getFormData();
  1039. }
  1040. if (!formData || !formData.stationId) {
  1041. ElMessage.error("无法获取表单数据,请确保已选择电站");
  1042. return;
  1043. }
  1044. // 构造保存参数数组
  1045. const saveParamsArray = [
  1046. {
  1047. timePeriod: row.timePeriod.replace(/:/g, ""), // 移除时间中的冒号
  1048. operationServiceFee: parseFloat(editingValue.value),
  1049. stationInfoId: formData.stationId,
  1050. salesType: formData.salesType,
  1051. periodFlag: row.periodFlag,
  1052. },
  1053. ];
  1054. // 根据销售类型添加额外参数
  1055. if (formData.salesType === 1) {
  1056. // 企业类型
  1057. saveParamsArray[0].firmId = formData.firmId;
  1058. } else if (formData.salesType === 2) {
  1059. // 渠道方类型
  1060. saveParamsArray[0].thirdPartyId = formData.id;
  1061. }
  1062. try {
  1063. // 调用保存接口,传递数组参数
  1064. await ThirdPartyStationInfoAPI.saveBillingStrategy(saveParamsArray);
  1065. ElMessage.success("保存成功");
  1066. // 更新本地数据
  1067. serviceFeeData.value[index].operationServiceFee = parseFloat(editingValue.value);
  1068. // 标记统一费用已设置
  1069. isUniformFeeSet.value = true;
  1070. // 重置编辑状态
  1071. editingRowIndex.value = -1;
  1072. editingValue.value = "";
  1073. } catch (error) {
  1074. ElMessage.error("保存失败: " + (error as Error).message);
  1075. }
  1076. };
  1077. // 取消编辑
  1078. const cancelEditOperationServiceFee = () => {
  1079. editingRowIndex.value = -1;
  1080. editingValue.value = "";
  1081. };
  1082. // 处理统一费用设置
  1083. const handleUniformFeeSetting = async (uniformFee: string) => {
  1084. // 验证输入值是否为数字(整数或小数)
  1085. if (!uniformFee || !/^-?\d+(\.\d+)?$/.test(uniformFee)) {
  1086. // 如果输入为空或无效,则不执行任何操作
  1087. return;
  1088. }
  1089. const feeValue = parseFloat(uniformFee);
  1090. // 获取当前表单数据以获取必要参数
  1091. let formData = addModalRef.value?.getFormData();
  1092. if (!formData || !formData.stationId) {
  1093. formData = editModalRef.value?.getFormData();
  1094. }
  1095. if (!formData || !formData.stationId) {
  1096. ElMessage.error("无法获取表单数据,请确保已选择电站");
  1097. return;
  1098. }
  1099. // 构造保存参数数组
  1100. const saveParamsArray = serviceFeeData.value.map((row: any) => ({
  1101. timePeriod: row.timePeriod.replace(/:/g, ""), // 移除时间中的冒号
  1102. operationServiceFee: feeValue,
  1103. stationInfoId: formData.stationId,
  1104. salesType: formData.salesType,
  1105. periodFlag: row.periodFlag,
  1106. // 根据销售类型添加额外参数
  1107. ...(formData.salesType === 1 && { firmId: formData.firmId }), // 企业类型
  1108. ...(formData.salesType === 2 && { thirdPartyId: formData.id }), // 渠道方类型
  1109. }));
  1110. try {
  1111. // 调用保存接口,传递数组参数
  1112. await ThirdPartyStationInfoAPI.saveBillingStrategy(saveParamsArray);
  1113. ElMessage.success("统一设置成功");
  1114. // 更新本地数据
  1115. serviceFeeData.value = serviceFeeData.value.map((row: any) => ({
  1116. ...row,
  1117. operationServiceFee: feeValue,
  1118. }));
  1119. // 标记统一费用已设置
  1120. isUniformFeeSet.value = true;
  1121. } catch (error) {
  1122. ElMessage.error("统一设置失败: " + (error as Error).message);
  1123. }
  1124. };
  1125. // 确认统一费用设置
  1126. const confirmUniformFeeSetting = (uniformFee: string) => {
  1127. // 验证输入值是否为数字(整数或小数)
  1128. if (!uniformFee || !/^-?\d+(\.\d+)?$/.test(uniformFee)) {
  1129. ElMessage.error("请输入有效的数字");
  1130. return;
  1131. }
  1132. const feeValue = parseFloat(uniformFee);
  1133. ElMessageBox.confirm(`确定要将所有时间段的运营服务费设置为 ${feeValue} 元吗?`, "确认设置", {
  1134. confirmButtonText: "确定",
  1135. cancelButtonText: "取消",
  1136. type: "warning",
  1137. })
  1138. .then(() => {
  1139. handleUniformFeeSetting(uniformFee);
  1140. })
  1141. .catch(() => {
  1142. // 用户取消操作
  1143. ElMessage.info("已取消设置");
  1144. });
  1145. };
  1146. </script>