PeakValleyStrategy.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. <template>
  2. <div>
  3. <el-row :gutter="10" class="mb8">
  4. <el-col :span="1.5">
  5. <el-button
  6. type="primary"
  7. plain
  8. icon="el-icon-plus"
  9. size="mini"
  10. @click="handleStrategyAdd"
  11. v-hasPermi="['basecfg:price:add']"
  12. >
  13. 新增
  14. </el-button>
  15. </el-col>
  16. <right-toolbar :showSearch.sync="showSearch" :search="false" @queryTable="getStrategyList"></right-toolbar>
  17. </el-row>
  18. <el-table v-loading="loading" :data="strategyList">
  19. <el-table-column label="策略编码" align="center" prop="strategyCode" />
  20. <el-table-column label="策略名称" align="center" prop="strategyName" />
  21. <el-table-column label="执行月份" align="center" prop="execMonth" show-overflow-tooltip>
  22. <template slot-scope="scope">
  23. {{formatExecMonth(scope.row.execMonth)}}
  24. </template>
  25. </el-table-column>
  26. <el-table-column label="执行日期" align="center" prop="execDateType">
  27. <template slot-scope="scope">
  28. {{formatDict(scope.row.execDateType,'execDateTypeOptions')}}
  29. </template>
  30. </el-table-column>
  31. <el-table-column label="策略描述" align="center" prop="strategyDesc" show-overflow-tooltip />
  32. <el-table-column label="分时类型" align="center" prop="supportType" show-overflow-tooltip>
  33. <template slot-scope="scope">
  34. {{formatSupportType(scope.row.supportType)}}
  35. </template>
  36. </el-table-column>
  37. <el-table-column label="优先级" align="center" prop="priority" />
  38. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  39. <template slot-scope="scope">
  40. <el-button
  41. size="mini"
  42. type="text"
  43. icon="el-icon-info"
  44. v-if="scope.row.editFlag==0"
  45. @click="handleStrategyDetail(scope.row)"
  46. v-hasPermi="['basecfg:price:edit']"
  47. >
  48. 查看
  49. </el-button>
  50. <el-button
  51. size="mini"
  52. type="text"
  53. icon="el-icon-edit"
  54. v-else
  55. @click="handleStrategyUpdate(scope.row)"
  56. v-hasPermi="['basecfg:price:edit']"
  57. >
  58. 修改
  59. </el-button>
  60. <el-button
  61. size="mini"
  62. type="text"
  63. icon="el-icon-delete"
  64. class="deleteBtn"
  65. @click="handleStrategyDelete(scope.row)"
  66. v-hasPermi="['basecfg:price:remove']"
  67. :disabled="scope.row.editFlag==0"
  68. >
  69. 删除
  70. </el-button>
  71. </template>
  72. </el-table-column>
  73. </el-table>
  74. <!-- 添加或修改峰谷电价策略配置对话框 -->
  75. <el-dialog :title="title" :visible.sync="strategyDialog" width="850px" append-to-body>
  76. <el-form ref="strategyForm" class="strategyForm" :model="strategyForm" :disabled="ifDisabled" label-width="100px">
  77. <el-form-item label="策略编码" prop="strategyCode" required :rules="[{required:true,message:'策略编码不能为空'}]">
  78. <el-input v-model="strategyForm.strategyCode" placeholder="请输入策略编码" />
  79. </el-form-item>
  80. <el-form-item label="策略名称" prop="strategyName" required :rules="[{required:true,message:'策略名称不能为空'}]">
  81. <el-input v-model="strategyForm.strategyName" placeholder="请输入策略名称" />
  82. </el-form-item>
  83. <el-form-item label="执行日期" prop="execDateType" required :rules="[{required:true,message:'请选择执行日期'}]">
  84. <el-select v-model="strategyForm.execDateType" style="width:100%" placeholder="请选择执行日期" @change="execDateTypeChange">
  85. <el-option v-for="item in execDateTypeOptions" :label="item.name" :value="item.value" :key="item.value" />
  86. </el-select>
  87. </el-form-item>
  88. <el-form-item
  89. label="执行说明"
  90. required
  91. :rules="[{required:true,message:'请选择执行说明'}]"
  92. prop="execDate"
  93. v-if="[1,5,6].includes(strategyForm.execDateType)"
  94. >
  95. <el-time-picker
  96. v-if="strategyForm.execDateType==1"
  97. v-model="strategyForm.execDate"
  98. value-format="HH:mm:ss"
  99. :picker-options="{selectableRange: '00:00:00 - 23:59:59'}"
  100. placeholder="选择时间"
  101. >
  102. </el-time-picker>
  103. <el-checkbox-group v-else-if="strategyForm.execDateType==5" v-model="strategyForm.execDate">
  104. <el-checkbox v-for="day in weeks.filter(item=>item.value<6)" :label="day.value" :key="day.value">
  105. {{day.name}}
  106. </el-checkbox>
  107. </el-checkbox-group>
  108. <el-checkbox-group v-else-if="strategyForm.execDateType==6" v-model="strategyForm.execDate">
  109. <el-checkbox v-for="day in weeks" :label="day.value" :key="day.value">{{day.name}}</el-checkbox>
  110. </el-checkbox-group>
  111. </el-form-item>
  112. <el-form-item label="策略描述" prop="strategyDesc">
  113. <el-input v-model="strategyForm.strategyDesc" placeholder="请输入策略描述" />
  114. </el-form-item>
  115. <el-form-item label="执行月份" prop="execMonth" required :rules="[{required:true,message:'请选择执行月份'}]">
  116. <el-checkbox-group v-model="strategyForm.execMonth">
  117. <el-tooltip
  118. v-for="month in availableMonths"
  119. :key="month.value"
  120. :content="month.disabled ? '已占用' : ''"
  121. :disabled="!month.disabled"
  122. placement="top"
  123. effect="dark">
  124. <el-checkbox
  125. :label="month.value"
  126. :disabled="month.disabled">
  127. <span :style="{color: month.disabled ? '#c0c4cc' : ''}">
  128. {{month.label}}
  129. </span>
  130. </el-checkbox>
  131. </el-tooltip>
  132. </el-checkbox-group>
  133. <div v-if="monthConflictTip" style="color: #f56c6c; font-size: 12px; margin-top: 5px;">
  134. {{ monthConflictTip }}
  135. </div>
  136. </el-form-item>
  137. <el-form-item label="分时类型" prop="supportType" required :rules="[{required:true,message:'请选择分时类型'}]">
  138. <el-checkbox-group v-model="strategyForm.supportType" @change="handleSupportTypeChange">
  139. <el-checkbox
  140. v-for="type in timeTypeOptions"
  141. :label="type.value"
  142. :key="type.value"
  143. :disabled="type.value === 0"
  144. >
  145. {{type.label}}
  146. </el-checkbox>
  147. </el-checkbox-group>
  148. </el-form-item>
  149. <el-form-item label="优先级" prop="priority" required>
  150. <el-input-number
  151. v-model="strategyForm.priority"
  152. :min="0"
  153. :max="100"
  154. @change="handlePriorityChange"
  155. label="描述文字">
  156. </el-input-number>
  157. <span style="margin-left: 10px; color: #909399; font-size: 12px;">
  158. 相同优先级的策略月份不能重叠
  159. </span>
  160. </el-form-item>
  161. <el-form-item label="小时电价">
  162. <el-table class="sub-table" :data="strategyForm.hourList" max-height="200px">
  163. <el-table-column label="开始时刻" align="center" prop="startTime">
  164. <template slot-scope="scope">
  165. <el-time-picker
  166. v-model="scope.row.startTime"
  167. size="mini"
  168. value-format="HH:mm:ss"
  169. :picker-options="{selectableRange: '00:00:00 - 23:59:59'}"
  170. placeholder="选择时间"
  171. >
  172. </el-time-picker>
  173. </template>
  174. </el-table-column>
  175. <el-table-column label="结束时刻" align="center" prop="endTime">
  176. <template slot-scope="scope">
  177. <el-time-picker
  178. v-model="scope.row.endTime"
  179. size="mini"
  180. value-format="HH:mm:ss"
  181. :picker-options="{selectableRange: '00:00:00 - 23:59:59'}"
  182. placeholder="选择时间"
  183. >
  184. </el-time-picker>
  185. </template>
  186. </el-table-column>
  187. <el-table-column label="分时类型" align="center" prop="type">
  188. <template slot-scope="scope">
  189. <el-select v-model="scope.row.type" size="mini" style="width:100%" placeholder="请选择">
  190. <el-option
  191. v-for="type in filteredTimeTypes"
  192. :label="type.label"
  193. :value="type.value"
  194. :key="type.value"
  195. />
  196. </el-select>
  197. </template>
  198. </el-table-column>
  199. <el-table-column align="center" width="100" v-if="!ifDisabled">
  200. <template slot="header">
  201. <div class="operateBtns" @click="addSub">
  202. <span>操作</span><i class="el-icon-circle-plus-outline"></i>
  203. </div>
  204. </template>
  205. <template slot-scope="scope">
  206. <i class="el-icon-delete" @click="deleteSub(scope.$index)"></i>
  207. </template>
  208. </el-table-column>
  209. </el-table>
  210. </el-form-item>
  211. </el-form>
  212. <div slot="footer" class="dialog-footer" v-if="!ifDisabled">
  213. <el-button type="primary" @click="submitStrategyForm">确 定</el-button>
  214. <el-button @click="strategyCancel">取 消</el-button>
  215. </div>
  216. </el-dialog>
  217. </div>
  218. </template>
  219. <script>
  220. import { listStrategy, addStrategy, updateStrategy, delStrategy } from '@/api/basecfg/elecAttr'
  221. import commonMethods from '../mixins/commonMethods'
  222. export default {
  223. name: 'PeakValleyStrategy',
  224. mixins: [commonMethods],
  225. data() {
  226. return {
  227. // 遮罩层
  228. loading: false,
  229. // 显示搜索条件
  230. showSearch: true,
  231. // 峰谷电价
  232. strategyList: [],
  233. // 弹出层标题
  234. title: '',
  235. strategyDialog: false,
  236. ifDisabled: false,
  237. // 查询参数
  238. strategyParams: {
  239. pageNum: 1,
  240. pageSize: 10
  241. },
  242. // 执行日期选项
  243. execDateTypeOptions: [
  244. { name: '每天', value: 2 },
  245. { name: '周一至周五', value: 5 },
  246. { name: '自定义', value: 6 }
  247. ],
  248. weeks: [
  249. { name: '周一', value: '1' },
  250. { name: '周二', value: '2' },
  251. { name: '周三', value: '3' },
  252. { name: '周四', value: '4' },
  253. { name: '周五', value: '5' },
  254. { name: '周六', value: '6' },
  255. { name: '周日', value: '7' }
  256. ],
  257. // 月份选项
  258. monthOptions: [
  259. { value: '01', label: '一月' },
  260. { value: '02', label: '二月' },
  261. { value: '03', label: '三月' },
  262. { value: '04', label: '四月' },
  263. { value: '05', label: '五月' },
  264. { value: '06', label: '六月' },
  265. { value: '07', label: '七月' },
  266. { value: '08', label: '八月' },
  267. { value: '09', label: '九月' },
  268. { value: '10', label: '十月' },
  269. { value: '11', label: '十一月' },
  270. { value: '12', label: '十二月' }
  271. ],
  272. // 分时类型选项
  273. timeTypeOptions: [
  274. { value: -2, label: '低谷' },
  275. { value: -1, label: '谷段' },
  276. { value: 0, label: '平段' },
  277. { value: 1, label: '峰段' },
  278. { value: 2, label: '尖峰' }
  279. ],
  280. strategyForm: {
  281. strategyCode: '',
  282. strategyName: '',
  283. strategyDesc: '',
  284. execDateType: '',
  285. execDate: [],
  286. execMonth: [],
  287. supportType: [0],
  288. priority: 0,
  289. hourList: []
  290. }
  291. }
  292. },
  293. computed: {
  294. // 根据选中的分时类型过滤小时电价表格中的选项
  295. filteredTimeTypes() {
  296. return this.timeTypeOptions.filter(type =>
  297. this.strategyForm.supportType.includes(type.value)
  298. )
  299. },
  300. // 获取可用月份(动态禁用已被占用的月份)
  301. availableMonths() {
  302. const currentPriority = this.strategyForm.priority
  303. const samePriorityStrategies = this.strategyList.filter(strategy => {
  304. return strategy.priority === currentPriority &&
  305. strategy.id !== this.strategyForm.id &&
  306. strategy.editFlag !== 0
  307. })
  308. const occupiedMonths = new Set()
  309. samePriorityStrategies.forEach(strategy => {
  310. if (strategy.execMonth) {
  311. strategy.execMonth.split(',').forEach(month => {
  312. occupiedMonths.add(month)
  313. })
  314. }
  315. })
  316. return this.monthOptions.map(month => ({
  317. ...month,
  318. disabled: occupiedMonths.has(month.value)
  319. }))
  320. },
  321. // 月份冲突提示
  322. monthConflictTip() {
  323. const currentPriority = this.strategyForm.priority
  324. const samePriorityStrategies = this.strategyList.filter(strategy => {
  325. return strategy.priority === currentPriority &&
  326. strategy.id !== this.strategyForm.id &&
  327. strategy.editFlag !== 0
  328. })
  329. if (samePriorityStrategies.length > 0) {
  330. const strategies = samePriorityStrategies.map(s => s.strategyName).join('、')
  331. return `提示:优先级${currentPriority}下已存在策略:${strategies}`
  332. }
  333. return ''
  334. }
  335. },
  336. created() {
  337. this.getStrategyList()
  338. },
  339. methods: {
  340. // 格式化分时类型
  341. formatSupportType(supportType) {
  342. if (!supportType) return ''
  343. const types = supportType.split(',').map(v => parseInt(v))
  344. const labels = types.map(type => {
  345. const found = this.timeTypeOptions.find(opt => opt.value === type)
  346. return found ? found.label : ''
  347. }).filter(label => label)
  348. return labels.join('、')
  349. },
  350. // 格式化执行月份
  351. formatExecMonth(execMonth) {
  352. if (!execMonth) return ''
  353. const months = execMonth.split(',')
  354. const labels = months.map(month => {
  355. const found = this.monthOptions.find(opt => opt.value === month)
  356. return found ? found.label : ''
  357. }).filter(label => label)
  358. return labels.join('、')
  359. },
  360. // 处理分时类型变化
  361. handleSupportTypeChange(values) {
  362. // 确保平段始终被选中
  363. if (!values.includes(0)) {
  364. values.push(0)
  365. }
  366. },
  367. // 处理优先级变化
  368. handlePriorityChange(value) {
  369. // 当优先级改变时,清空月份选择中的冲突月份
  370. const availableMonthValues = this.availableMonths
  371. .filter(m => !m.disabled)
  372. .map(m => m.value)
  373. // 过滤掉不可用的月份
  374. this.strategyForm.execMonth = this.strategyForm.execMonth.filter(month =>
  375. availableMonthValues.includes(month)
  376. )
  377. },
  378. // 修改执行日期变更方法
  379. execDateTypeChange(val) {
  380. if (val == 5) {
  381. this.strategyForm.execDate = this.weeks.filter(item => item.value < 6).map(item => item.value)
  382. } else if (val == 6) {
  383. this.strategyForm.execDate = []
  384. } else {
  385. this.strategyForm.execDate = ''
  386. }
  387. },
  388. /** 查询峰谷电价列表 */
  389. getStrategyList() {
  390. const { pageNum, pageSize } = this.strategyParams
  391. this.loading = true
  392. listStrategy({ pageNum, pageSize }).then(response => {
  393. this.strategyList = response.rows
  394. this.loading = false
  395. })
  396. },
  397. handleStrategyDetail(row) {
  398. this.ifDisabled = true
  399. this.resetForm('strategyForm')
  400. // 深拷贝数据,避免直接修改原数据
  401. this.strategyForm = JSON.parse(JSON.stringify(row))
  402. // 处理执行日期
  403. if ([5, 6].includes(this.strategyForm.execDateType)) {
  404. this.strategyForm.execDate = this.strategyForm.execDate ?
  405. this.strategyForm.execDate.split(',') : []
  406. }
  407. // 处理执行月份
  408. this.strategyForm.execMonth = this.strategyForm.execMonth ?
  409. this.strategyForm.execMonth.split(',') : []
  410. // 处理分时类型
  411. this.strategyForm.supportType = this.strategyForm.supportType ?
  412. this.strategyForm.supportType.split(',').map(v => parseInt(v)) : [0]
  413. this.strategyDialog = true
  414. this.title = '查看峰谷电价策略'
  415. },
  416. handleStrategyAdd() {
  417. this.ifDisabled = false
  418. this.resetForm('strategyForm')
  419. this.strategyForm = {
  420. id: null,
  421. strategyCode: '',
  422. strategyName: '',
  423. strategyDesc: '',
  424. execDateType: '',
  425. execDate: [],
  426. execMonth: [],
  427. supportType: [0],
  428. priority: 1,
  429. hourList: []
  430. }
  431. this.strategyDialog = true
  432. this.title = '添加峰谷电价策略'
  433. },
  434. handleStrategyUpdate(row) {
  435. this.ifDisabled = false
  436. this.resetForm('strategyForm')
  437. // 深拷贝数据,避免直接修改原数据
  438. this.strategyForm = JSON.parse(JSON.stringify(row))
  439. // 处理执行日期
  440. if ([5, 6].includes(this.strategyForm.execDateType)) {
  441. this.strategyForm.execDate = this.strategyForm.execDate ?
  442. this.strategyForm.execDate.split(',') : []
  443. }
  444. // 处理执行月份
  445. this.strategyForm.execMonth = this.strategyForm.execMonth ?
  446. this.strategyForm.execMonth.split(',') : []
  447. // 处理分时类型
  448. this.strategyForm.supportType = this.strategyForm.supportType ?
  449. this.strategyForm.supportType.split(',').map(v => parseInt(v)) : [0]
  450. // 确保平段始终被选中
  451. if (!this.strategyForm.supportType.includes(0)) {
  452. this.strategyForm.supportType.push(0)
  453. }
  454. this.strategyDialog = true
  455. this.title = '修改峰谷电价策略'
  456. },
  457. handleStrategyDelete(row) {
  458. this.$modal.confirm('是否确认删除').then(() => {
  459. delStrategy(row.id).then(() => {
  460. this.getStrategyList()
  461. this.$modal.msgSuccess('删除成功')
  462. })
  463. })
  464. },
  465. strategyCancel() {
  466. this.strategyDialog = false
  467. this.resetForm('strategyForm')
  468. // 完全重置表单数据
  469. this.strategyForm = {
  470. id: null,
  471. strategyCode: '',
  472. strategyName: '',
  473. strategyDesc: '',
  474. execDateType: '',
  475. execDate: [],
  476. execMonth: [],
  477. supportType: [0],
  478. priority: 1,
  479. hourList: []
  480. }
  481. // 清除可能的验证状态
  482. this.$nextTick(() => {
  483. if (this.$refs.strategyForm) {
  484. this.$refs.strategyForm.clearValidate()
  485. }
  486. })
  487. },
  488. submitStrategyForm() {
  489. this.$refs['strategyForm'].validate(valid => {
  490. if (valid) {
  491. if (this.strategyForm.hourList.length === 0) {
  492. return this.$modal.msgError(`请添加小时电价数据`)
  493. }
  494. // 检查相同优先级的月份冲突
  495. const monthConflict = this.checkMonthConflict()
  496. if (monthConflict) {
  497. return this.$modal.msgError(`执行月份冲突:${monthConflict}月份已被其他相同优先级的策略使用`)
  498. }
  499. // 验证小时电价数据
  500. let subFlag = false
  501. let dateFlag = false
  502. let validIndex = 0
  503. for (let i = 0; i < this.strategyForm.hourList.length; i++) {
  504. const item = this.strategyForm.hourList[i]
  505. if (item.startTime === '' || item.endTime === '' || item.type === '') {
  506. subFlag = true
  507. validIndex = i + 1
  508. break
  509. }
  510. if (this.compareTime(item.startTime, item.endTime)) {
  511. dateFlag = true
  512. validIndex = i + 1
  513. break
  514. }
  515. }
  516. if (subFlag) {
  517. return this.$modal.msgError(`请补全小时电价第${validIndex}行数据`)
  518. }
  519. if (dateFlag) {
  520. return this.$modal.msgError(`小时电价第${validIndex}行开始时刻不能大于结束时刻`)
  521. }
  522. // 增强的时间段重叠检查
  523. const overlapInfo = this.checkTimeRangesOverlapEnhanced()
  524. if (overlapInfo) {
  525. return this.$modal.msgError(`小时电价时间段冲突:${overlapInfo}`)
  526. }
  527. // 检查时间段完整性
  528. const completenessCheck = this.checkTimeCompleteness()
  529. if (completenessCheck) {
  530. this.$modal.confirm(`${completenessCheck},是否继续保存?`).then(() => {
  531. this.saveStrategyForm()
  532. }).catch(() => {})
  533. } else {
  534. this.saveStrategyForm()
  535. }
  536. }
  537. })
  538. },
  539. // 保存策略表单
  540. saveStrategyForm() {
  541. // 创建表单数据的副本以避免修改原数据
  542. const formData = JSON.parse(JSON.stringify(this.strategyForm))
  543. // 特殊处理执行日期
  544. if ([5, 6].includes(formData.execDateType)) {
  545. formData.execDate = Array.isArray(formData.execDate) ? formData.execDate.join(',') : formData.execDate
  546. }
  547. // 处理执行月份
  548. formData.execMonth = Array.isArray(formData.execMonth) ? formData.execMonth.join(',') : formData.execMonth
  549. // 处理分时类型
  550. formData.supportType = Array.isArray(formData.supportType) ? formData.supportType.join(',') : formData.supportType
  551. formData.hourList.forEach(item => {
  552. item.strategyCode = formData.strategyCode
  553. })
  554. if (formData.id) {
  555. updateStrategy(formData).then(() => {
  556. this.$modal.msgSuccess('修改成功')
  557. this.strategyDialog = false
  558. this.getStrategyList()
  559. })
  560. } else {
  561. addStrategy(formData).then(() => {
  562. this.$modal.msgSuccess('新增成功')
  563. this.strategyDialog = false
  564. this.getStrategyList()
  565. })
  566. }
  567. },
  568. compareTime(t1, t2) {
  569. const d = new Date()
  570. const ft1 = d.setHours(t1.split(':')[0], t1.split(':')[1], t1.split(':')[2])
  571. const ft2 = d.setHours(t2.split(':')[0], t2.split(':')[1], t2.split(':')[2])
  572. return ft1 > ft2
  573. },
  574. // 增强版时间段重叠检查
  575. checkTimeRangesOverlapEnhanced() {
  576. const ranges = this.strategyForm.hourList.map((item, index) => {
  577. const d = new Date()
  578. const ft1 = d.setHours(item.startTime.split(':')[0], item.startTime.split(':')[1], item.startTime.split(':')[2])
  579. const ft2 = d.setHours(item.endTime.split(':')[0], item.endTime.split(':')[1], item.endTime.split(':')[2])
  580. return {
  581. start: ft1,
  582. end: ft2,
  583. startStr: item.startTime,
  584. endStr: item.endTime,
  585. index: index + 1
  586. }
  587. })
  588. // 按开始时间排序
  589. const sortedRanges = ranges.slice().sort((a, b) => a.start - b.start)
  590. // 检查重叠并返回详细信息
  591. for (let i = 1; i < sortedRanges.length; i++) {
  592. if (sortedRanges[i].start < sortedRanges[i - 1].end) {
  593. return `第${sortedRanges[i - 1].index}行(${sortedRanges[i - 1].startStr}-${sortedRanges[i - 1].endStr})与第${sortedRanges[i].index}行(${sortedRanges[i].startStr}-${sortedRanges[i].endStr})存在时间重叠`
  594. }
  595. }
  596. return null
  597. },
  598. // 检查时间完整性
  599. checkTimeCompleteness() {
  600. if (this.strategyForm.hourList.length === 0) return null
  601. const ranges = this.strategyForm.hourList.map(item => {
  602. const startParts = item.startTime.split(':')
  603. const endParts = item.endTime.split(':')
  604. return {
  605. start: parseInt(startParts[0]) * 3600 + parseInt(startParts[1]) * 60 + parseInt(startParts[2]),
  606. end: parseInt(endParts[0]) * 3600 + parseInt(endParts[1]) * 60 + parseInt(endParts[2]),
  607. startStr: item.startTime,
  608. endStr: item.endTime
  609. }
  610. }).sort((a, b) => a.start - b.start)
  611. const gaps = []
  612. const dayInSeconds = 24 * 3600
  613. const tolerance = 1
  614. // 检查开始是否从0点开始
  615. if (ranges[0].start > tolerance) {
  616. gaps.push(`00:00:00 - ${this.formatSeconds(ranges[0].start)}`)
  617. }
  618. // 检查中间的间隙
  619. for (let i = 1; i < ranges.length; i++) {
  620. const gap = ranges[i].start - ranges[i - 1].end
  621. if (gap > tolerance) {
  622. gaps.push(`${this.formatSeconds(ranges[i - 1].end)} - ${this.formatSeconds(ranges[i].start)}`)
  623. }
  624. }
  625. // 检查结束是否到24点
  626. if (ranges[ranges.length - 1].end < dayInSeconds - tolerance) {
  627. gaps.push(`${this.formatSeconds(ranges[ranges.length - 1].end)} - 23:59:59`)
  628. }
  629. if (gaps.length > 0) {
  630. return `以下时间段未配置:${gaps.join('、')}`
  631. }
  632. return null
  633. },
  634. // 辅助方法:将秒数转换为时间格式
  635. formatSeconds(seconds) {
  636. const h = Math.floor(seconds / 3600)
  637. const m = Math.floor((seconds % 3600) / 60)
  638. const s = seconds % 60
  639. return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
  640. },
  641. // 检查相同优先级策略的月份冲突
  642. checkMonthConflict() {
  643. const currentPriority = this.strategyForm.priority
  644. const samePriorityStrategies = this.strategyList.filter(strategy => {
  645. return strategy.priority === currentPriority &&
  646. strategy.id !== this.strategyForm.id &&
  647. strategy.editFlag !== 0
  648. })
  649. const occupiedMonths = new Set()
  650. samePriorityStrategies.forEach(strategy => {
  651. if (strategy.execMonth) {
  652. strategy.execMonth.split(',').forEach(month => {
  653. occupiedMonths.add(month)
  654. })
  655. }
  656. })
  657. const conflictMonths = []
  658. this.strategyForm.execMonth.forEach(month => {
  659. if (occupiedMonths.has(month)) {
  660. const monthLabel = this.monthOptions.find(m => m.value === month)?.label || month
  661. conflictMonths.push(monthLabel)
  662. }
  663. })
  664. if (conflictMonths.length > 0) {
  665. return conflictMonths.join('、')
  666. }
  667. return null
  668. },
  669. addSub() {
  670. this.strategyForm.hourList.push({
  671. startTime: '',
  672. endTime: '',
  673. type: ''
  674. })
  675. },
  676. deleteSub(index) {
  677. this.strategyForm.hourList.splice(index, 1)
  678. }
  679. }
  680. }
  681. </script>
  682. <style lang="scss" scoped>
  683. @import '../styles/dialog.scss';
  684. .sub-table {
  685. .el-icon-delete {
  686. cursor: pointer;
  687. }
  688. .operateBtns {
  689. cursor: pointer;
  690. i {
  691. color: #1990ff;
  692. margin-left: 5px;
  693. }
  694. }
  695. ::v-deep .el-date-editor {
  696. width: 100% !important;
  697. }
  698. }
  699. .custom-checkbox-group {
  700. max-height: 120px;
  701. overflow-y: auto;
  702. }
  703. .strategyForm {
  704. ::v-deep .el-input.is-disabled .el-input__inner {
  705. color: #606266;
  706. background: #fff;
  707. }
  708. }
  709. .deleteBtn:disabled {
  710. color: #c0c4cc !important;
  711. &:hover {
  712. color: #c0c4cc !important;
  713. }
  714. }
  715. </style>