util.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017
  1. import { getData } from '@/api/platform/desktop/column'
  2. import { getFile } from '@/utils/avatar'
  3. import { mapState } from 'vuex'
  4. import {
  5. taskTypeOptions,
  6. dashboardStatus,
  7. genderOptions,
  8. favoritesOptions,
  9. noticeOptions,
  10. unreadMessageOptions,
  11. imgOptionsData
  12. } from '@/business/platform/bpmn/constants'
  13. import ActionUtils from '@/utils/action'
  14. import Utils from '@/utils/util'
  15. import {
  16. findAllByCurrUserId,
  17. saveCalendarInfos,
  18. removeCalendarInfos,
  19. delNavigation,
  20. addNavigation,
  21. getNavigation,
  22. sortNavigation
  23. } from '@/api/detection/newHomeApi'
  24. import { isEqual, now } from 'lodash'
  25. import Bus from '@/utils/EventBus'
  26. import newPng from '@/assets/images/homepage/new.png'
  27. import { BASE_URL } from '@/constant'
  28. import dayjs from 'dayjs'
  29. import { scheduleType } from '@/views/constants/schedule'
  30. import { attendanceDetailClockIn } from '@/api/business/attendance'
  31. import { lifeTimeData } from '@/views/business/deviceManagement/constants/simulated'
  32. /**
  33. * 创建组件
  34. */
  35. export function buildComponent(name, column, preview, vm) {
  36. try {
  37. return {
  38. name,
  39. components: {
  40. VueDraggable: () => import('vuedraggable')
  41. },
  42. props: {
  43. params: {
  44. type: Object,
  45. default: () => { }
  46. },
  47. height: {
  48. type: Number,
  49. default: column.height || 300
  50. },
  51. visible: {
  52. type: Boolean,
  53. default: false
  54. },
  55. fullScreen: {
  56. type: Boolean,
  57. default: false
  58. }
  59. },
  60. filters: {
  61. filterStatus(val, type) {
  62. if (Utils.isEmpty(val)) {
  63. return ''
  64. }
  65. const typeMap = {
  66. pending: taskTypeOptions,
  67. already: dashboardStatus,
  68. myRequest: dashboardStatus,
  69. gender: genderOptions,
  70. favorites: favoritesOptions,
  71. notice: noticeOptions,
  72. unreadMessage: unreadMessageOptions
  73. }
  74. if (!typeMap[type]) {
  75. return val
  76. }
  77. return typeMap[type].find((x) => x['value'] === val)
  78. ? typeMap[type].find((x) => x['value'] === val).label
  79. : val
  80. }
  81. },
  82. data() {
  83. const { first = '', second = '' } = this.$store.getters.level
  84. const {
  85. userId,
  86. userList = [],
  87. deptList = [],
  88. menus,
  89. userInfo
  90. } = this.$store.getters
  91. const t1 = deptList.find((i) => i.positionId === first) || {}
  92. const t2 = deptList.find((i) => i.positionId === second) || {}
  93. const locationName = second
  94. ? t1.positionName + t2.positionName
  95. : t1.positionName
  96. return {
  97. userId,
  98. userList,
  99. deptList,
  100. menus,
  101. locationName,
  102. newPng,
  103. imgOptionsData,
  104. positions: userInfo.positions,
  105. loading: false,
  106. title: `${column.name}`,
  107. alias: `${column.alias}`,
  108. attrs: this.getAttrs(),
  109. variables: {}, // 一些变量,比如分页信息
  110. data: null,
  111. totalCount: 0,
  112. quickNavigationData: [],
  113. navigationList: [],
  114. stautusOptions: [],
  115. bpmnFormrenderDialogVisible: false, // 表单
  116. editId: '',
  117. bodyShow: true,
  118. show: false,
  119. showHeight: '',
  120. cardHeight: '100%',
  121. activeName: 'innerMessage',
  122. unreadMessageOption: {},
  123. formName: 'quickNavform',
  124. dialogFormVisible: false,
  125. formLabelWidth: '120px',
  126. quickNavform: {
  127. urlName: '',
  128. urlAddr: '',
  129. display: '_blank',
  130. diDian: second || first
  131. },
  132. defaultForm: {},
  133. pendingTabActiveName: 'user-type',
  134. bodyParams: ActionUtils.formatParams({}, {}, {}),
  135. pendingBusinessOption: {},
  136. bpmn: [],
  137. rules: {
  138. urlName: [
  139. {
  140. required: true,
  141. message: this.$t('validate.required')
  142. }
  143. ],
  144. urlAddr: [
  145. {
  146. required: true,
  147. message: this.$t('validate.required')
  148. }
  149. ],
  150. display: [
  151. {
  152. required: true,
  153. message: this.$t('validate.required')
  154. }
  155. ]
  156. },
  157. calendarDialogForm: {
  158. id: '',
  159. biaoTi: '',
  160. neiRong: '',
  161. kaiShiShiJian: '',
  162. jieShuShiJian: '',
  163. formDate: []
  164. },
  165. colorStatus: ['#e7505a', '#f3c200', '#578ebe', '#1BBC9B'],
  166. // const status = ['急', '重', '轻','缓']
  167. isDragging: false,
  168. draggableOptions: {
  169. handle: '.draggable',
  170. ghostClass: 'sortable-ghost',
  171. distance: 1,
  172. disabled: false,
  173. animation: 200,
  174. axis: 'y'
  175. },
  176. calendarToolbar: this.fullScreen
  177. ? [{ key: 'refresh' }]
  178. : [{ key: 'refresh' }, { key: 'fullscreen' }, { key: 'collapse' }],
  179. isFirstAlert: true, // 是否首次日程提醒
  180. scheduleData: [],
  181. hasMounted: false,
  182. attendanceData: [],
  183. scheduleShift: [],
  184. todaySchedule: [],
  185. tempSelectedValue: '',
  186. searchText: '',
  187. keysList: []
  188. }
  189. },
  190. computed: {
  191. ...mapState({
  192. userInfo: (state) => state.ibps.user.info
  193. })
  194. },
  195. mounted() {
  196. this.defaultForm = JSON.parse(JSON.stringify(this.quickNavform))
  197. this.$nextTick(async () => {
  198. this.fetchData()
  199. // this.attendanceData = await this.getAttendanceData()
  200. // this.scheduleData = await this.getScheduleData()
  201. this.todaySchedule = await this.getTodaySchedule()
  202. })
  203. },
  204. methods: {
  205. // 受控文件查阅模块搜索
  206. searchFile () {
  207. this.$router.push({ path: '/xxgl/nbwj', query: { searchText: this.searchText } })
  208. },
  209. goDetail(nodeInfo) {
  210. if(!nodeInfo){
  211. return
  212. }
  213. this.$router.push({ path: '/xxgl/nbwj', query: {info: JSON.parse(JSON.stringify(nodeInfo)), id:nodeInfo.id} })
  214. },
  215. fetchData(columns, params = {}) {
  216. this.loading = true
  217. this.data = []
  218. this.showHeight = this.getHeight()
  219. const param =
  220. Utils.isNotEmpty(columns) &&
  221. (column.alias === 'unreadMessage' ||
  222. column.alias === 'pendingBusiness')
  223. ? { dataMode: column.dataMode, dataFrom: column.dataFrom }
  224. : column
  225. if (param.alias === 'myCalendar') {
  226. const { getFormatDate, getDate } = this.$common
  227. findAllByCurrUserId()
  228. .then((res) => {
  229. const { data = [] } = res || {}
  230. if (this.isFirstAlert) {
  231. this.isFirstAlert = false
  232. this.showAlert(data)
  233. }
  234. this.data = data.map((i) => ({
  235. id: i.id,
  236. title: i.title,
  237. content: i.content,
  238. start: i.startTime,
  239. // 日期组件日程显示里,end的时间需要多加一天,end:2024-01-02时,日程条只到2024-01-01的日期位置上
  240. end: getFormatDate(
  241. 'string',
  242. 10,
  243. getDate('day', 1, i.endTime)
  244. ),
  245. jieShuShiJian: i.endTime,
  246. zhuangTai: i.emergencyState,
  247. color: this.colorStatus[Number(i.emergencyState) - 1]
  248. }))
  249. })
  250. .catch(() => {
  251. this.$message.error('获取日历日程失败!')
  252. })
  253. } else if (param.alias === 'quickNavigation') {
  254. getNavigation().then((res) => {
  255. const { data = [] } = res || {}
  256. data.forEach((item) => {
  257. if (!item.userId) {
  258. item.urlAddr = `${BASE_URL}#${item.urlAddr}`
  259. }
  260. })
  261. this.quickNavigationData = data
  262. })
  263. } else {
  264. const { first = '', second = '' } = this.$store.getters.level || {}
  265. // console.log('11111111', param, params, typeof params)
  266. let type = true
  267. if(param.alias == 'newmk'){
  268. type = false
  269. params['diDian'] = second || first
  270. }
  271. getData(param, params, type)
  272. .then((res) => {
  273. let { data } = res || {}
  274. if (Utils.isNotEmpty(data) && Utils.isString(data)) {
  275. data = Utils.parseData(res.data)
  276. }
  277. this.data = data && data.dataResult ? data.dataResult : data
  278. this.totalCount =
  279. data && data.pageResult ? data.pageResult.totalCount : 0
  280. // 更新小铃铛消息数量
  281. if (param.alias === 'unreadMessage') {
  282. Bus.$emit('getMessageCount', this.totalCount)
  283. }
  284. this.variables = res.variables
  285. if(param.alias == 'newmk'){
  286. this.keysList = Object.keys(this.variables)
  287. }
  288. this.loading = false
  289. })
  290. .catch((e) => {
  291. this.loading = false
  292. })
  293. }
  294. },
  295. // 过滤日程提醒数据
  296. filterAlertData(data, dayNumber = 3) {
  297. if (dayNumber <= 0) return
  298. const today = dayjs()
  299. const tempCalendarAlertData = data.filter((day) => {
  300. const startTime = dayjs(day.startTime)
  301. const endTime = dayjs(day.endTime)
  302. if (day.popUp) {
  303. if (
  304. (startTime.diff(today, 'day') <= 0 &&
  305. endTime.diff(today, 'day') >= 0) ||
  306. (startTime.diff(today, 'day') <= dayNumber - 2 &&
  307. startTime.diff(today, 'day') >= 0)
  308. ) {
  309. return true
  310. }
  311. }
  312. })
  313. tempCalendarAlertData.sort(
  314. (a, b) => new Date(a.startTime) - new Date(b.startTime)
  315. )
  316. const calendarIds = tempCalendarAlertData.map((item) => item.id)
  317. const calendarAlertData = {}
  318. tempCalendarAlertData.forEach((item) => {
  319. if (calendarAlertData[item.startTime]) {
  320. calendarAlertData[item.startTime].push(item)
  321. } else {
  322. calendarAlertData[item.startTime] = []
  323. calendarAlertData[item.startTime].push(item)
  324. }
  325. })
  326. return { calendarAlertData, calendarIds }
  327. },
  328. // 日程提醒
  329. showAlert(data) {
  330. const calendarAlertData = this.filterAlertData(data)
  331. this.$emit('action-event', 'calendarAlert', calendarAlertData)
  332. },
  333. getPhoto(photo) {
  334. return getFile(photo)
  335. },
  336. getTaskDesc(v) {
  337. if (!v.includes('#')) {
  338. return ''
  339. }
  340. return v.split('#')[1] || ''
  341. },
  342. getTaskInfo(val, arg) {
  343. const arr = val.split('#')
  344. if (!arr[2]) {
  345. return ''
  346. }
  347. // 中文冒号转英文冒号 防止流程定义时输入的冒号格式错误导致转换报错
  348. arr[2] = arr[2].replace(':', ':')
  349. const result = JSON.parse(`{${arr[2]}}`)
  350. if (!result.dept) {
  351. return ''
  352. }
  353. const depts = result.dept.split(',')
  354. const deptNames = []
  355. depts.forEach((item) => {
  356. const t = this.deptList.find((i) => i.positionId === item)
  357. deptNames.push(t ? t.positionName : result.dept)
  358. })
  359. result.deptName = deptNames.join(',')
  360. return result[arg]
  361. },
  362. transformData(val, dataset, from, to) {
  363. if (!val) {
  364. return ''
  365. }
  366. const temp = this[dataset].find((u) => u[from] === val)
  367. return temp ? temp[to] : ''
  368. },
  369. getHeightNoUnit() {
  370. // 高度 - header - 边框
  371. if (!this.visible) {
  372. return this.height ? this.height - 60 - 20 : 150
  373. } else {
  374. return 150
  375. }
  376. },
  377. getHeight(h = 20) {
  378. // 高度 - header - 边框
  379. if (!this.visible) {
  380. return this.height ? `${this.height - 60 - h}px` : '150px'
  381. } else {
  382. return '100%'
  383. }
  384. },
  385. getDashboardHeight() {
  386. return this.height ? `${this.height + 20}px` : '150px'
  387. },
  388. getAttrs() {
  389. const item = JSON.parse(JSON.stringify(column))
  390. item.templateHtml = null
  391. return item
  392. },
  393. /**
  394. * 构建首页日期组件的参数
  395. * @param {*} data
  396. * @returns
  397. */
  398. getFullCalendarConfig(data) {
  399. const events = data === null ? [] : Utils.parseJSON(data)
  400. const config = {
  401. height: preview ? '100%' : this.height ? this.height : 180,
  402. // editable: true, // 允许拖动缩放,不写默认就是false
  403. selectable: true,
  404. dayMaxEvents: 1, // 最多显示3个日程
  405. locale: this.$i18n.locale
  406. ? this.$i18n.locale.toLowerCase()
  407. : 'zh-cn',
  408. events: events,
  409. buttonText: {
  410. today: '今天',
  411. dayGridMonth: '月',
  412. listMonth: '日程'
  413. // week: '周视图',
  414. // day: '日视图',
  415. // prev: '<i class="icon-chevron-left">后退</i>',
  416. // next: '<i class="icon-chevron-right">前进</i>'
  417. },
  418. dateClick: this.handleDateClick, // 日期点击
  419. eventClick: this.handleEventClick,
  420. moreLinkClick: this.handleMoreLinkClick
  421. }
  422. if (preview) {
  423. config.headerToolbar = {
  424. start: '',
  425. center: 'title',
  426. end: 'prev,next,today'
  427. // end: 'prev,next,today,month,agendaWeek,agendaDay,listWeek'
  428. }
  429. delete config['dayMaxEvents']
  430. }
  431. return config
  432. },
  433. handleDateClick(param) {
  434. this.$emit(
  435. 'open',
  436. 'calendar',
  437. [param.dateStr, param.dateStr],
  438. this.data
  439. )
  440. },
  441. handleEventClick(param) {
  442. this.$emit(
  443. 'open',
  444. 'calendar',
  445. [
  446. param.event.startStr,
  447. param.event._def.extendedProps.jieShuShiJian
  448. ],
  449. this.data,
  450. param.event.id
  451. )
  452. },
  453. handleMoreLinkClick(date) {
  454. this.$emit(
  455. 'open',
  456. 'calendar',
  457. [
  458. date.allSegs[0].event.startStr,
  459. date.allSegs[0].event._def.extendedProps.jieShuShiJian
  460. ],
  461. this.data,
  462. date.allSegs[0].event.id
  463. )
  464. },
  465. refreshData() {
  466. this.fetchData()
  467. },
  468. /**
  469. * 处理按钮事件
  470. * @param {*} command
  471. * @param {*} position
  472. * @param {*} data
  473. * @param {*} actions
  474. */
  475. handleActionEvent(command, position, data, actions) {
  476. switch (command) {
  477. case 'refresh': // 刷新
  478. this.refreshData()
  479. break
  480. case 'fullscreen': // 全屏
  481. this.handleFullscreen()
  482. break
  483. case 'more': // 更多
  484. this.handleMore()
  485. break
  486. case 'collapse': // 收缩
  487. case 'expansion': // 展开
  488. this.handleCollapseExpand(command, data, actions)
  489. break
  490. case 'add': // 新增
  491. this.getFormData()
  492. break
  493. default:
  494. break
  495. }
  496. },
  497. emitActionEventHandler(command) {
  498. this.$emit('action-event', command, ...Array.from(arguments).slice(1))
  499. },
  500. // 公告栏目点击事件
  501. handleApprove(id, title) {
  502. this.$emit('action-event', 'approve', {
  503. id,
  504. title
  505. })
  506. },
  507. handleUnreadMessage(id, tableId, tableName) {
  508. this.$emit('action-event', 'unRead', { id, tableId, tableName })
  509. },
  510. // 处理全屏
  511. handleFullscreen() {
  512. this.emitActionEventHandler('fullscreen', { id: this.attrs.id })
  513. },
  514. openPlate(url) {
  515. const menuMap = {
  516. myTraining: 'rygl/rypx/wdpx',
  517. myTesting: 'rygl/kszx/wdks',
  518. myDevices: 'sbgls/mywh',
  519. myDiscard: 'lhjcgl/ypgqcl',
  520. notice: 'tygl/tzgg',
  521. myFacility: 'sshjgl/sshjjk/sshjkzzl',
  522. quickNavigation: 'xtgl/xtsjpz/tyglpz/kjdhpz',
  523. moreFile: 'xxgl/nbwj' //受控文件查阅
  524. }
  525. if (menuMap[url]) {
  526. const alias = menuMap[url].split('/')[0]
  527. const resInfo = this.menus.find((i) => i.alias === alias)
  528. this.$store.dispatch('ibps/menu/activeHeaderSet', {
  529. activeHeader: resInfo.id,
  530. vm: this
  531. })
  532. }
  533. this.$router.push(`/${menuMap[url] || url}`)
  534. },
  535. /**
  536. * 处理更多
  537. */
  538. handleMore() {
  539. if (this.attrs.alias === 'quickNavigation') {
  540. return this.openPlate('quickNavigation')
  541. }
  542. if (!this.attrs.colUrl) {
  543. return this.$message.warning('未设置更多路径的url')
  544. }
  545. this.openPlate(this.attrs.colUrl)
  546. },
  547. // 未读消息
  548. handleClick(option) {
  549. console.log('999999999')
  550. this.unreadMessageOption = option
  551. option[this.activeName].dataMode = column.dataMode
  552. this.fetchData(option[this.activeName])
  553. },
  554. // 待办事务
  555. handleTabClick(option) {
  556. this.pendingBusinessOption = option
  557. option[this.pendingTabActiveName].dataMode = column.dataMode
  558. this.fetchData(option[this.pendingTabActiveName])
  559. },
  560. handleFlowClick(params) {
  561. params.$alias = this.alias
  562. this.emitActionEventHandler('flow', params)
  563. },
  564. handleCollapseExpand(command, data, actions) {
  565. this.bodyShow = !this.bodyShow
  566. const index = actions.findIndex((action) => action.key === data.key)
  567. actions[index].key = this.bodyShow ? 'collapse' : 'expansion'
  568. if (!this.visible) {
  569. this.emitActionEventHandler(command, {})
  570. return
  571. }
  572. this.showHeight = this.bodyShow ? this.getHeight() : 0
  573. this.$refs['toolbar'].callback(actions)
  574. },
  575. formValidate(formName) {
  576. this.$nextTick(() => {
  577. this.$refs[formName].validate(() => { })
  578. })
  579. },
  580. getFormData() {
  581. this.quickNavform = JSON.parse(JSON.stringify(this.defaultForm))
  582. this.formValidate('quickNavform')
  583. this.dialogFormVisible = true
  584. },
  585. handleNavRemove(navId, i) {
  586. this.$confirm('是否确认删除该快捷导航?', '提示', {
  587. confirmButtonText: '确认',
  588. cancelButtonText: '取消',
  589. type: 'warning',
  590. showClose: false,
  591. closeOnClickModal: false
  592. }).then(() => {
  593. delNavigation({ navigateIds: navId }).then(() => {
  594. this.$message.success('删除成功')
  595. this.quickNavigationData.splice(i, 1)
  596. })
  597. })
  598. },
  599. // 错误头像的照片
  600. errorAvatarHandler(data) {
  601. // data.photo = require('https://cube.elemecdn.com/e/fd/0fc7d20532fdaf769a25683617711png.png')
  602. return true
  603. },
  604. close() {
  605. this.$refs[this.formName].resetFields()
  606. this.dialogFormVisible = false
  607. },
  608. saveQuickNav() {
  609. this.$refs[this.formName].validate((valid) => {
  610. if (valid) {
  611. addNavigation({ id: '', ...this.quickNavform }).then((res) => {
  612. this.$message.success('添加成功!')
  613. const { id } = res.variables || {}
  614. this.quickNavigationData.unshift({
  615. id,
  616. ...this.quickNavform,
  617. userId: this.userId
  618. })
  619. this.dialogFormVisible = false
  620. })
  621. } else {
  622. ActionUtils.saveErrorMessage()
  623. }
  624. })
  625. },
  626. handleSortChange() {
  627. this.isDragging = false
  628. const newSort = this.quickNavigationData.map((i) => i.id)
  629. if (isEqual(this.navigationList, newSort)) {
  630. return
  631. }
  632. this.navigationList = newSort
  633. sortNavigation({ orders: this.navigationList.join(',') }).then(() => {
  634. this.$message.success('排序成功!')
  635. })
  636. },
  637. // 父组件调用该方法给日程添加数据
  638. async setCalendarEvents(param) {
  639. const { name = '' } = this.$store.getters
  640. const form = param.form
  641. const paramObject = {
  642. id: form.id ? form.id : '11111',
  643. // diDian: "string", // 地点
  644. userId: this.userId, // 用户id
  645. userName: name, // 用户名
  646. title: form.biaoTi, // 标题
  647. content: form.neiRong, // 内容
  648. startTime: form.formDate[0], // 开始时间
  649. endTime: form.formDate[1], // 结束时间
  650. emergencyState: form.zhuangTai // 紧急状态
  651. }
  652. await saveCalendarInfos(paramObject)
  653. this.refreshData()
  654. },
  655. async hanldeCalendardel(param) {
  656. if (param.form.id) {
  657. await removeCalendarInfos({
  658. calendarIds: param.form.id
  659. })
  660. }
  661. this.refreshData()
  662. },
  663. // 公告栏是否显示new图标
  664. showNewIcon(date, days) {
  665. const nowDate = new Date().getTime()
  666. const targetDate = new Date(date).getTime()
  667. return targetDate + days * 24 * 60 * 60 * 1000 > nowDate
  668. },
  669. handleOverflow(val, length) {
  670. if (val.length > length) {
  671. return val.slice(0, length - 2) + '...'
  672. }
  673. return val
  674. },
  675. getDays(start, end) {
  676. if (!start || !end) {
  677. return 0
  678. }
  679. return (
  680. Math.ceil(
  681. (new Date(end) - new Date(start)) / (1000 * 60 * 60 * 24)
  682. ) + 1
  683. )
  684. },
  685. getTodaySchedule() {
  686. // 获取今日班次
  687. const { first, second } = this.$store.getters.level || {}
  688. const today = this.$common.getDateNow()
  689. // const sql = `select a.*, b.title_,b.type_, b.start_date_, b.end_date_, b.config_, b.overview_, b.id_ as pai_ban_id_ from t_schedule_detail a, t_schedule b where a.parent_id_ = b.id_ and b.di_dian_ = '${second || first}' and a.user_id_ = '${this.userId}' and b.status_ = '已发布'`
  690. // 新查询语句 SELECT ad.* FROM t_attendance_detail ad INNER JOIN t_schedule s ON ad.pai_ban_id_ = s.id_ WHERE ad.ri_qi_ = #{p0} AND ad.yong_hu_id_ = #{p1} AND ad.di_dian_ = #{p2} AND s.status_ = #{p3}
  691. return new Promise((resolve, reject) => {
  692. this.$common
  693. .request('query', {
  694. key: 'getTodayScheduleDataByUid',
  695. params: [today, this.userId, second || first, '已发布']
  696. })
  697. .then((res) => {
  698. const { data = [] } = res.variables || {}
  699. let todaySchedule = []
  700. data.forEach((item) => {
  701. if (item.ban_ci_bie_ming_) {
  702. let obj = {}
  703. obj.label = item.ban_ci_ming_ + '/' + item.ban_ci_bie_ming_
  704. obj.type = 'info' //班次默认显示灰色
  705. if (item.da_ka_shi_jian_1_ && item.da_ka_shi_jian_2_) { //上下班时间都打卡了,班次显示绿色
  706. obj.type = 'success'
  707. } else if (item.da_ka_shi_jian_1_ || item.da_ka_shi_jian_2_) {//上下班时间打卡了其中一个,班次显示橙色
  708. obj.type = 'warning'
  709. }
  710. todaySchedule.push(obj) // 加入今日班次
  711. }
  712. })
  713. resolve(todaySchedule)
  714. })
  715. .catch((error) => {
  716. reject(error)
  717. })
  718. })
  719. },
  720. getAttendanceData() {
  721. const { first, second } = this.$store.getters.level || {}
  722. const today = this.$common.getDateNow()
  723. // const sql = `select a.id_, a.kao_qin_zhuang_ta,a.ri_qi_, a.pai_ban_id_, a.pai_ban_ji_lu_id_, a.ban_ci_bie_ming_, a.ban_ci_kai_shi_, a.ban_ci_jie_shu_, a.ban_ci_ming_, a.da_ka_shi_jian_1_, a.zhuang_tai_1_, a.da_ka_shi_jian_2_, a.zhuang_tai_2_, a.chi_dao_shi_chang FROM t_attendance_detail a JOIN t_schedule b ON a.pai_ban_id_ = b.id_ AND b.status_ = '已发布' WHERE a.di_dian_ = '${second || first}' AND a.ri_qi_ <= '${today}' AND a.yong_hu_id_ = '${this.userId}'`
  724. return new Promise((resolve, reject) => {
  725. this.$common
  726. .request('query', {
  727. key: 'getAttendanceDataByUid',
  728. params: [second || first, today, this.userId]
  729. })
  730. .then((res) => {
  731. const { data = [] } = res.variables || {}
  732. const resultMap = new Map()
  733. data.forEach((item) => {
  734. const { ri_qi_, pai_ban_id_, ban_ci_bie_ming_, ...rest } =
  735. item
  736. // 第一级:按日期分组
  737. if (!resultMap.has(ri_qi_)) {
  738. resultMap.set(ri_qi_, new Map())
  739. }
  740. const dateMap = resultMap.get(ri_qi_)
  741. // 第二级:按排班ID分组
  742. if (!dateMap.has(pai_ban_id_)) {
  743. dateMap.set(pai_ban_id_, new Map())
  744. }
  745. const scheduleMap = dateMap.get(pai_ban_id_)
  746. // 第三级:按班次别名存储完整数据
  747. scheduleMap.set(ban_ci_bie_ming_, rest)
  748. })
  749. // return resultMap
  750. resolve(resultMap)
  751. })
  752. .catch((error) => {
  753. reject(error)
  754. })
  755. })
  756. },
  757. getScheduleData() {
  758. const { first, second } = this.$store.getters.level || {}
  759. // const sql = `select a.*, b.title_,b.type_, b.start_date_, b.end_date_, b.config_, b.overview_, b.id_ as pai_ban_id_ from t_schedule_detail a, t_schedule b where a.parent_id_ = b.id_ and b.di_dian_ = '${second || first}' and a.user_id_ = '${this.userId}' and b.status_ = '已发布'`
  760. return new Promise((resolve, reject) => {
  761. this.$common
  762. .request('query', {
  763. key: 'getScheduleDataByUid',
  764. params: [second || first, this.userId]
  765. })
  766. .then((res) => {
  767. const { data = [] } = res.variables || {}
  768. const eventList = []
  769. const self = this
  770. data.forEach((item) => {
  771. const days = this.getDays(item.start_date_, item.end_date_)
  772. const config = item.config_ ? JSON.parse(item.config_) : {}
  773. const scheduleTypeLabel =
  774. scheduleType.filter((obj) => obj.value === item.type_)[0]
  775. ?.label || ''
  776. const scheduleCreateBy =
  777. self.userList.filter(
  778. (obj) => obj.userId === item.create_by_
  779. )[0]?.userName || ''
  780. const { scheduleShift } = config
  781. this.scheduleShift = scheduleShift
  782. for (let i = 1; i <= days; i++) {
  783. const shift = item[`d${i}_`]
  784. if (shift) {
  785. const date = this.$common.getFormatDate(
  786. 'string',
  787. 10,
  788. this.$common.getDate('day', i - 1, item.start_date_)
  789. )
  790. const nextdate = this.$common.getFormatDate(
  791. 'string',
  792. 10,
  793. this.$common.getDate('day', i, item.start_date_)
  794. )
  795. const enddate = this.$common.getFormatDate(
  796. 'string',
  797. 10,
  798. this.$common.getDate('day', i + 1, item.start_date_)
  799. )
  800. const shiftList = shift.split(',')
  801. shiftList.forEach((s) => {
  802. const t = scheduleShift.find((i) => i.alias === s)
  803. const attendance = self.attendanceData
  804. .get(date)
  805. ?.get(item.pai_ban_id_)
  806. ?.get(s)
  807. eventList.push({
  808. scheduleName: item.title_ ? item.title_ : '', // 排班表名字
  809. scheduleTypeLabel: scheduleTypeLabel || '', // 排班类型
  810. scheduleCreateBy: scheduleCreateBy || '', // 排班创建人
  811. content: t.dateRange
  812. .map((d) => {
  813. return d.type === 'allday'
  814. ? '全天'
  815. : `当天 ${d.startTime}` +
  816. ' 至 ' +
  817. `${d.isSecondDay === 'Y' ? '第二天' : '当天'} ${d.endTime}`
  818. })
  819. .join('\n'),
  820. title: s,
  821. start: date,
  822. end: t.dateRange[0].isSecondDay == 'Y' ? nextdate : date, //是否跨天班次,跨天班次要设置为结束日期的下一天,即开始日期的后天
  823. jieShuShiJian: t.dateRange[0].isSecondDay == 'Y' ? nextdate : date,
  824. kaishishijian: date,
  825. zhuangTai: '',
  826. // id: i,
  827. id: attendance?.id_ || i,
  828. bcolor: t.color,
  829. attendance: attendance || {}, // 考勤状态
  830. //allDay: true, // 标记为全天事件
  831. //display: "auto", // 自动跨天渲染(默认值)
  832. ...t
  833. })
  834. })
  835. }
  836. }
  837. })
  838. // const today = this.$common.getDateNow()
  839. // this.todaySchedule = eventList.filter(i => i.start === today).map(i => i.title)
  840. // console.log(this.todaySchedule)
  841. resolve(eventList)
  842. })
  843. .catch((error) => {
  844. reject(error)
  845. })
  846. })
  847. },
  848. handleScheduleEventClick(param) {
  849. // 排班点击事件
  850. this.$emit(
  851. 'open',
  852. 'banci',
  853. param.event._def.extendedProps,
  854. this.scheduleShift,
  855. param.event._def.title
  856. )
  857. },
  858. showDaKaBtn(targetDay1, targetDay2) { //班次开始或结束时间有一个是今天就显示打卡按钮
  859. // 判断是否展示打卡按钮,当前日期则展示
  860. const today = this.$common.getDateNow()
  861. if (targetDay1 === today || targetDay2 === today) {
  862. return true
  863. } else {
  864. return false
  865. }
  866. },
  867. // 打卡处理逻辑
  868. handleClock(attendance) {
  869. if (Object.keys(attendance).length === 0) {
  870. this.$message.warning('考勤数据异常!')
  871. return
  872. }
  873. // 更新打卡请求
  874. attendanceDetailClockIn({ id: attendance.id_ })
  875. .then(() => {
  876. this.$message.success('打卡成功!')
  877. })
  878. .catch(() => {
  879. // this.$message.warning('打卡失败')
  880. })
  881. },
  882. getTimeDifferenceInMinutes(startTimeStr, endTimeStr) {
  883. // 时间相减分钟数
  884. const startTime = new Date(startTimeStr)
  885. const endTime = new Date(endTimeStr)
  886. const timeDifference = endTime - startTime
  887. return timeDifference / (1000 * 60)
  888. },
  889. // 首页打卡处理逻辑
  890. handleClockFromTab(todaySchedule) {
  891. const today = this.$common.getDateNow()
  892. // 当天仅有一个班次
  893. if (todaySchedule.length === 1) {
  894. this.$emit('dakaSingle', todaySchedule[0].label.split('/')[1])
  895. return
  896. } else {
  897. this.$emit('action-event', 'daka', todaySchedule.map(item => item.label.split('/')[1]))
  898. }
  899. },
  900. async showMySchedule() {
  901. this.attendanceData = await this.getAttendanceData()
  902. this.scheduleData = await this.getScheduleData()
  903. const scheduleConfig = {
  904. height: '100%',
  905. locale: 'zh-cn', // 语言
  906. selectable: true, // 是否可以选中日历格
  907. buttonText: {
  908. // 日历头部按钮中文转换
  909. today: '今天',
  910. dayGridMonth: '月',
  911. listMonth: '',
  912. month: '月',
  913. week: '周视图',
  914. day: '日视图',
  915. list: '表'
  916. // prev: '<i class="icon-chevron-left">后退</i>',
  917. // next: '<i class="icon-chevron-right">前进</i>'
  918. },
  919. headerToolbar: {
  920. // 日历头部按钮位置
  921. // left: 'prev,next today',
  922. // start: '',
  923. right: 'customButton prev,next today',
  924. left: '',
  925. center: 'title'
  926. // right: 'dayGridMonth,timeGridWeek,timeGridDay'
  927. // end: 'prev,next,today,month,agendaWeek,agendaDay,listWeek'
  928. },
  929. customButtons: {
  930. customButton: {
  931. // 定义自定义按钮
  932. text: '补卡',
  933. click: (param) => {
  934. // 打开补卡申请弹窗
  935. this.$emit('open', 'buka')
  936. }
  937. }
  938. },
  939. // events: this.scheduleData, // 排班数组
  940. eventClick: this.handleScheduleEventClick, // 排班点击信息展示
  941. scheduleShift: this.scheduleShift,
  942. eventColor: '#ffffff',
  943. events: this.scheduleData,
  944. // 添加自定义事件渲染
  945. eventContent: (arg) => {
  946. const event = arg.event
  947. const now = new Date()
  948. const dayStart = new Date(event.start)
  949. dayStart.setHours(0, 0, 0, 0)
  950. // 组合DOM内容
  951. const fragment = document.createDocumentFragment()
  952. const content = document.createElement('div')
  953. const titleStr =
  954. event.extendedProps.name + '/' + event.extendedProps.alias
  955. const timeStr =
  956. event.extendedProps.dateRange[0].startTime +
  957. '至' +
  958. event.extendedProps.dateRange[0].endTime
  959. // 考勤是否正常
  960. const status =
  961. event.extendedProps.attendance.kao_qin_zhuang_ta || ''
  962. content.innerHTML = `
  963. <div class="event-header">
  964. <div>
  965. <div style="background:${event.extendedProps.bcolor};height:10px;width:10px;display:inline-block;"> </div>
  966. ${titleStr}
  967. </div>
  968. <div class="button-placeholder">
  969. ${event.extendedProps.jieShuShiJian <=
  970. this.$common.getDateNow()
  971. ? status === '正常'
  972. ? '<i class="el-icon-check" style="color:#409EFF"></i>'
  973. : '<i class="el-icon-warning-outline" style="color:#F5222D"></i>'
  974. : ''
  975. }
  976. </div>
  977. </div>
  978. `
  979. // 打卡按钮显示
  980. if (this.showDaKaBtn(event.extendedProps.kaishishijian, event.extendedProps.jieShuShiJian)) {
  981. const button = document.createElement('button')
  982. button.className = 'clock-btn'
  983. // 根据打卡状态显示不同文本
  984. button.innerHTML = '打卡'
  985. // 绑定点击事件(通过闭包传递当前事件)
  986. button.onclick = (e) => {
  987. e.stopPropagation()
  988. this.handleClock(event.extendedProps.attendance)
  989. }
  990. const placeholder = content.querySelector('.button-placeholder')
  991. placeholder.replaceWith(button)
  992. }
  993. fragment.appendChild(content)
  994. return { domNodes: [fragment] }
  995. }
  996. }
  997. this.$emit('action-event', 'mySchedule', scheduleConfig)
  998. }
  999. },
  1000. template:
  1001. column.templateHtml !== '' ? `${column.templateHtml}` : `<div></div>`
  1002. }
  1003. } catch (error) {
  1004. console.error(error)
  1005. }
  1006. }