MyOvertime.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. <script setup lang="ts">
  2. import { reactive, ref, nextTick, computed } from 'vue'
  3. import { ColumnProps } from '@/components/TableBase/interface/index'
  4. import {
  5. Overtime_User_list,
  6. Overtime_Stat,
  7. Overtime_Add,
  8. Overtime_Del,
  9. Overtime_Edit
  10. } from '@/api/workAttendance/index'
  11. import { User_List } from '@/api/user/index'
  12. import { UpFileToken } from '@/api/public/index'
  13. import Dialog from '@/components/dialog/Dialog.vue'
  14. import Drawer from '@/components/Drawer/index.vue'
  15. import { Edit, Delete, View, Plus } from '@element-plus/icons-vue'
  16. import type {
  17. FormInstance,
  18. FormRules,
  19. UploadFile,
  20. UploadInstance,
  21. UploadProps,
  22. UploadRawFile,
  23. UploadFiles
  24. } from 'element-plus'
  25. import { ElMessageBox, ElMessage, genFileId } from 'element-plus'
  26. import { floatReg } from '@/views/salary/salary/relus'
  27. import { GlobalStore } from '@/stores/index'
  28. import { dayJs, getFormatDuration, qiniuUpLoadFun, upLoadQiniu } from '@/utils/common'
  29. const globalStore = GlobalStore()
  30. const TableRef = ref()
  31. const TableStatRef = ref()
  32. const columns: ColumnProps[] = [
  33. { prop: 'T_start_time', label: '开始时间' },
  34. { prop: 'T_end_time', label: '结束时间' },
  35. { prop: 'T_duration', label: '时长', name: 'T_duration' },
  36. { prop: 'T_State', label: '审核', name: 'T_State' },
  37. { prop: 'operation', label: '操作', width: 200, fixed: 'right' }
  38. ]
  39. const columns_Stat: ColumnProps[] = [
  40. { prop: 'T_duration', label: '时长' },
  41. { prop: 'RemainingTime', label: '剩余时长' },
  42. { prop: 'T_type_name', label: '事项' },
  43. { prop: 'T_approver_name', label: '处理人' },
  44. { prop: 'UpdateTime', label: '时间' }
  45. ]
  46. const initParam = {
  47. User_tokey: globalStore.GET_User_tokey
  48. }
  49. const openDrawerOvertime = (str: string, row: any) => {
  50. drawerRef.value.openDrawer()
  51. if (str === 'edit') {
  52. isNew = false
  53. nextTick(() => {
  54. form.value = { ...row }
  55. uuid = row.T_approver
  56. form.value.T_approver = row.T_user_name
  57. form.value.T_id = row.Id
  58. })
  59. }
  60. }
  61. const disabled = ref(false)
  62. const OvertimeView = (row: any) => {
  63. disabled.value = true
  64. drawerRef.value.openDrawer()
  65. nextTick(() => {
  66. form.value = { ...row }
  67. form.value.T_approver = row.T_user_name
  68. })
  69. }
  70. const OvertimeDelete = (row: any) => {
  71. ElMessageBox.confirm('您确定要删除加班申请吗?', '警告', {
  72. confirmButtonText: '确定',
  73. cancelButtonText: '取消',
  74. type: 'warning'
  75. })
  76. .then(async () => {
  77. const res: any = await Overtime_Del({ User_tokey: globalStore.GET_User_tokey, T_id: row.Id })
  78. if (res.Code === 200) {
  79. ElMessage({
  80. type: 'success',
  81. message: '删除成功!'
  82. })
  83. nextTick(() => {
  84. TableRef.value.getTableList()
  85. })
  86. }
  87. })
  88. .catch(() => {
  89. ElMessage({
  90. type: 'warning',
  91. message: '取消成功!'
  92. })
  93. })
  94. }
  95. const AddOvertime = (formEl: FormInstance | undefined) => {
  96. if (!formEl) return
  97. if (!prove_img) {
  98. ElMessage({
  99. type: 'warning',
  100. message: `请等待图片上传完成`
  101. })
  102. return
  103. }
  104. formEl.validate(async valid => {
  105. if (valid) {
  106. let res: any = {}
  107. form.value.T_duration = duration.value * 60
  108. if (isNew) {
  109. res = await Overtime_Add({ ...form.value, T_approver: uuid })
  110. } else {
  111. res = await Overtime_Edit({ ...form.value, T_approver: uuid })
  112. }
  113. if (res.Code === 200) {
  114. ElMessage({
  115. type: 'success',
  116. message: `${isNew ? '申请' : '修改'}成功!`
  117. })
  118. nextTick(() => {
  119. drawerRef.value.closeDrawer()
  120. TableRef.value.getTableList()
  121. resetForm(ruleFormRef.value)
  122. isNew = true
  123. })
  124. }
  125. } else {
  126. return false
  127. }
  128. })
  129. }
  130. // Drawer
  131. const drawerRef = ref()
  132. const ruleFormRef = ref<FormInstance>()
  133. type Fn = () => void
  134. const callbackDrawer = (done: Fn) => {
  135. disabled.value = false
  136. nextTick(() => {
  137. resetForm(ruleFormRef.value)
  138. done()
  139. })
  140. }
  141. const resetForm = (formEl: FormInstance | undefined) => {
  142. if (!formEl) return
  143. formEl.resetFields()
  144. }
  145. const validate_float = (rule: any, value: any, callback: any) => {
  146. if (value === '') {
  147. callback(new Error('请输入加班时长'))
  148. } else {
  149. if (floatReg.test(value) || /\d+/.test(value)) {
  150. callback()
  151. } else {
  152. callback(new Error('时间必须是数字或小数'))
  153. }
  154. }
  155. }
  156. const rules = reactive<FormRules>({
  157. T_type: [{ required: true, message: '请选择请假类型', trigger: 'blur' }],
  158. T_start_time: [{ required: true, message: '请选择开始时间', trigger: 'blur' }],
  159. T_end_time: [{ required: true, message: '请选择结束时间', trigger: 'blur' }],
  160. T_duration: [{ required: true, validator: validate_float, trigger: 'blur' }],
  161. T_approver: [{ required: true, message: '请选择审批人', trigger: 'blur' }]
  162. })
  163. let uuid = ''
  164. const duration = computed(() => {
  165. const end_time = dayJs(form.value.T_end_time)
  166. if (!form.value.T_end_time) {
  167. return 0
  168. }
  169. let count: number = end_time.diff(form.value.T_start_time, 'hour', true)
  170. let num: number = Number(count.toFixed(2))
  171. return Math.round(num * 10) / 10
  172. })
  173. const form = ref({
  174. T_start_time: '',
  175. T_end_time: '',
  176. T_duration: 0,
  177. T_text: '',
  178. T_approver: '',
  179. T_prove_img: '',
  180. T_id: ''
  181. })
  182. const formLabelWidth = ref('100px')
  183. let isNew = true
  184. const selectApprover = () => {
  185. dialog.value.DialogOpen()
  186. }
  187. // dialog
  188. const dialog = ref()
  189. const search = ref('')
  190. const tableApproverRef = ref()
  191. const Dialogcolumns: ColumnProps[] = [{ prop: 'T_name', label: '名字', name: 'T_name' }]
  192. const approverInitParam = {
  193. User_tokey: globalStore.GET_User_tokey,
  194. T_name: '',
  195. T_dept_leader: 1
  196. }
  197. const searchHandle = () => {
  198. approverInitParam.T_name = search.value
  199. tableApproverRef.value.searchTable()
  200. }
  201. const getApproverInfo = (row: any) => {
  202. uuid = row.T_uuid
  203. form.value.T_approver = row.T_name
  204. dialog.value.DialogClose()
  205. }
  206. const onResize = () => {
  207. const height = document.documentElement.clientHeight
  208. return height / 2
  209. }
  210. // upload file
  211. const upload = ref()
  212. let uploadData = { token: '', key: '' }
  213. const dialogImageUrl = ref('')
  214. const dialogVisible = ref(false)
  215. const fileList = ref([])
  216. let prove_img = true
  217. const handlePictureCardPreview = async (file: any) => {
  218. console.log(file, fileList)
  219. dialogImageUrl.value = file.url as string
  220. dialogVisible.value = true
  221. }
  222. const beforeUpload = async (file: any) => {
  223. console.log(file)
  224. let reg = /^image/g
  225. if (!reg.test(file.type)) {
  226. ElMessage({
  227. type: 'error',
  228. message: '必须上传图片!!'
  229. })
  230. return
  231. }
  232. prove_img = false
  233. let suffix = file.type.split('/')[1]
  234. const res: any = await UpFileToken({ User_tokey: globalStore.GET_User_tokey, T_suffix: suffix })
  235. uploadData.token = res.Data
  236. uploadData.key = file.name
  237. }
  238. const handleExceed: UploadProps['onExceed'] = (files: any) => {
  239. console.log(files)
  240. upload.value.clearFiles()
  241. const file = files[0] as UploadRawFile
  242. file.uid = genFileId()
  243. console.log(file)
  244. upload.value.handleStart(file)
  245. submitUpload()
  246. }
  247. const submitUpload = () => {
  248. upload.value.submit()
  249. }
  250. const onSuccess = (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  251. console.log(response, uploadFile, uploadFiles)
  252. form.value.T_prove_img = response.key
  253. prove_img = true
  254. ElMessage({
  255. type: 'success',
  256. message: '图片上传成功!!'
  257. })
  258. }
  259. const onError = () => {
  260. // console.log(error, uploadFile, uploadFiles)
  261. ElMessage({
  262. type: 'error',
  263. message: '图片上传失败!!'
  264. })
  265. }
  266. const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
  267. console.log(uploadFile, uploadFiles, fileList)
  268. form.value.T_prove_img = ''
  269. }
  270. // const handleProgress = (evt: UploadProgressEvent, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  271. // console.log(evt, uploadFile, uploadFiles)
  272. // }
  273. </script>
  274. <template>
  275. <el-row :gutter="24" class="h-100">
  276. <el-col :span="12" class="padding-right-0 h-100">
  277. <TableBase
  278. ref="TableRef"
  279. :columns="columns"
  280. :requestApi="Overtime_User_list"
  281. :initParam="initParam"
  282. layout="total,sizes,prev, pager, next"
  283. >
  284. <template #table-header>
  285. <div class="table-header">
  286. <h4>我的加班</h4>
  287. <el-button type="primary" @click="openDrawerOvertime">申请加班</el-button>
  288. </div>
  289. </template>
  290. <template #T_duration="{ row }"> {{ getFormatDuration(row.T_duration) }} </template>
  291. <template #T_State="{ row }">
  292. <el-tag v-if="row.T_State === 1" type="success">通过</el-tag>
  293. <el-tag v-else-if="row.T_State === 2" type="warning">未通过</el-tag>
  294. <el-tag v-else type="danger">待审核</el-tag>
  295. </template>
  296. <template #right="{ row }">
  297. <el-button
  298. v-if="row.T_State !== 1"
  299. link
  300. type="primary"
  301. size="small"
  302. :icon="Edit"
  303. @click="openDrawerOvertime('edit', row)"
  304. >编辑</el-button
  305. >
  306. <el-button
  307. v-if="row.T_State !== 1"
  308. link
  309. type="danger"
  310. size="small"
  311. :icon="Delete"
  312. @click="OvertimeDelete(row)"
  313. >删除</el-button
  314. >
  315. <el-button v-if="row.T_State === 1" link type="success" size="small" :icon="View" @click="OvertimeView(row)"
  316. >查看</el-button
  317. >
  318. <!-- <el-icon><View /></el-icon> -->
  319. </template>
  320. </TableBase></el-col
  321. >
  322. <el-col :span="12" class="h-100" style="overflow: hidden">
  323. <TableBase
  324. ref="TableStatRef"
  325. :columns="columns_Stat"
  326. :requestApi="Overtime_Stat"
  327. :initParam="initParam"
  328. layout="total,sizes,prev, pager, next"
  329. >
  330. <template #table-header="{ pageable }">
  331. <el-button text
  332. ><h4>剩余总时长:{{ pageable.RemainingTime }}</h4></el-button
  333. ></template
  334. >
  335. <template #T_name="{ row }">
  336. <el-button type="primary" text>{{ row.T_name }}</el-button>
  337. </template>
  338. </TableBase>
  339. </el-col>
  340. <Drawer ref="drawerRef" :handleClose="callbackDrawer">
  341. <template #header="{ params }">
  342. <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - 申请</h4>
  343. </template>
  344. <el-form ref="ruleFormRef" :model="form" :rules="rules">
  345. <el-form-item label="开始时间:" :label-width="formLabelWidth" prop="T_start_time">
  346. <el-date-picker
  347. style="width: 100%"
  348. class="my-date-picker"
  349. v-model="form.T_start_time"
  350. type="datetime"
  351. :disabled="disabled"
  352. placeholder="选择开始时间"
  353. format="YYYY-MM-DD HH:mm:ss"
  354. value-format="YYYY-MM-DD HH:mm:ss"
  355. />
  356. </el-form-item>
  357. <el-form-item label="结束时间:" :label-width="formLabelWidth" prop="T_end_time">
  358. <el-date-picker
  359. style="width: 100%"
  360. class="my-date-picker"
  361. v-model="form.T_end_time"
  362. type="datetime"
  363. :disabled="disabled"
  364. placeholder="选择结束时间"
  365. format="YYYY-MM-DD HH:mm:ss"
  366. value-format="YYYY-MM-DD HH:mm:ss"
  367. />
  368. </el-form-item>
  369. <el-form-item label="加班时长:" :label-width="formLabelWidth" prop="T_duration">
  370. <el-input v-model.number="duration" autocomplete="off" :disabled="true" placeholder="请假时长" />
  371. </el-form-item>
  372. <el-form-item label="取证:" :label-width="formLabelWidth">
  373. <el-upload
  374. ref="upload"
  375. :disabled="disabled"
  376. v-model:file-list="fileList"
  377. action="https://up-z2.qiniup.com"
  378. list-type="picture-card"
  379. :limit="1"
  380. :auto-upload="true"
  381. :before-upload="beforeUpload"
  382. :on-success="onSuccess"
  383. :on-error="onError"
  384. :on-exceed="handleExceed"
  385. :on-remove="handleRemove"
  386. :on-preview="handlePictureCardPreview"
  387. :data="uploadData"
  388. >
  389. <el-icon><Plus /></el-icon>
  390. </el-upload>
  391. <el-dialog v-model="dialogVisible">
  392. <img w-full :src="dialogImageUrl" class="full-img" alt="Preview Image" />
  393. </el-dialog>
  394. </el-form-item>
  395. <el-form-item label="内容:" :label-width="formLabelWidth" prop="T_text">
  396. <el-input
  397. v-model="form.T_text"
  398. :disabled="disabled"
  399. autocomplete="off"
  400. type="textarea"
  401. :autosize="{ minRows: 4, maxRows: 6 }"
  402. placeholder="请输入内容"
  403. />
  404. </el-form-item>
  405. <el-form-item label="审批:" :label-width="formLabelWidth" prop="T_approver">
  406. <el-input
  407. :disabled="disabled"
  408. v-model="form.T_approver"
  409. autocomplete="off"
  410. placeholder="审批人"
  411. @focus="selectApprover"
  412. />
  413. </el-form-item>
  414. <el-form-item :label-width="formLabelWidth">
  415. <el-button v-if="isNew" color="#626aef" @click="AddOvertime(ruleFormRef)" :disabled="disabled"
  416. >添加</el-button
  417. >
  418. <el-button v-else color="#626aef" @click="AddOvertime(ruleFormRef)">修改</el-button>
  419. </el-form-item>
  420. </el-form>
  421. </Drawer>
  422. <Dialog ref="dialog" width="30%">
  423. <template #header>
  424. <h3>选择审批人</h3>
  425. </template>
  426. <TableBase
  427. ref="tableApproverRef"
  428. :columns="Dialogcolumns"
  429. :initParam="approverInitParam"
  430. :requestApi="User_List"
  431. layout="total, prev, pager, next"
  432. :onResize="onResize"
  433. >
  434. <template #table-header>
  435. <el-row :gutter="20">
  436. <el-col :span="20" class="d-flex">
  437. <span class="inline-flex">账户查询:</span>
  438. <el-input v-model="search" type="text" class="w-50 m-2" />
  439. <el-button type="primary" @click="searchHandle">搜索</el-button>
  440. </el-col>
  441. </el-row>
  442. </template>
  443. <template #T_name="{ row }">
  444. <el-button type="primary" link @click="getApproverInfo(row)">{{ row.T_name }}</el-button>
  445. </template>
  446. </TableBase>
  447. </Dialog>
  448. </el-row>
  449. </template>
  450. <style scoped lang="scss">
  451. .table-header {
  452. width: 100%;
  453. display: flex;
  454. justify-content: space-between;
  455. align-items: center;
  456. }
  457. .full-img {
  458. width: 100%;
  459. height: 100%;
  460. }
  461. .d-flex {
  462. display: flex;
  463. align-items: center;
  464. .inline-flex {
  465. white-space: nowrap;
  466. }
  467. }
  468. </style>