util.js 45 KB

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