|
@@ -1,383 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="WorkHoursSelect">
|
|
|
- <div class="select-title">
|
|
|
- <span>工作量分配</span>
|
|
|
- </div>
|
|
|
- <div class="select-list">
|
|
|
- <div class="title">
|
|
|
- <div class="left">
|
|
|
- {{ '工作项目' + (defaultTotalTime ? `(总耗时${defaultTotalTime}小时)` : '') }}
|
|
|
- </div>
|
|
|
- <div class="right">
|
|
|
- <el-input
|
|
|
- ref="searchInputRef"
|
|
|
- placeholder="搜索项目"
|
|
|
- v-model="searchValue"
|
|
|
- @input="searchOnchange"
|
|
|
- clearable
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <ul class="list-ul">
|
|
|
- <li v-for="(item, index) in dataList" :key="index" @click="handleClick(item)">
|
|
|
- <div class="project-write">
|
|
|
- <el-popover
|
|
|
- ref="popperRef"
|
|
|
- popper-class="popover-panel"
|
|
|
- placement="top-end"
|
|
|
- :width="400"
|
|
|
- trigger="click"
|
|
|
- :visible="item.visible"
|
|
|
- >
|
|
|
- <template #reference>
|
|
|
- <div class="time-select" @click="item.visible = true">
|
|
|
- <span>{{ item.count ? `(${item.count}小时)` : '' }}</span>
|
|
|
- {{ item['name'] }}
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #default>
|
|
|
- <div class="work-hours-select-panel">
|
|
|
- <p class="title">{{ item['name'] }} 工时 (小时)</p>
|
|
|
- <div class="panel-input-number">
|
|
|
- 输入时间:
|
|
|
- <el-input-number
|
|
|
- v-model="item.count"
|
|
|
- class="input-number"
|
|
|
- :min="0"
|
|
|
- :max="80"
|
|
|
- controls-position="right"
|
|
|
- @change="changeCount"
|
|
|
- />
|
|
|
- </div>
|
|
|
- <div class="panel-numeric-key">
|
|
|
- <ul>
|
|
|
- <li
|
|
|
- v-for="(num, i) in 40"
|
|
|
- :style="{
|
|
|
- background: i + 1 === item.count ? '#1b80eb' : '#f7f8fa',
|
|
|
- color: i + 1 === item.count ? '#fff' : '#2d333c'
|
|
|
- }"
|
|
|
- :key="i"
|
|
|
- @click="changeCount(num, index)"
|
|
|
- >
|
|
|
- <span>{{ num }}</span>
|
|
|
- </li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- <div class="panel-submit">
|
|
|
- <el-button type="primary" @click="panelSubmit(index)">确定</el-button>
|
|
|
- <el-button type="danger" @click="changeCount(0, index)">清零</el-button>
|
|
|
- <el-button type="info" @click="panelCancel(index)"> 取消 </el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </el-popover>
|
|
|
- </div>
|
|
|
- </li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script setup lang="ts">
|
|
|
-/** 周日报相同文件 */
|
|
|
-import request from '@/config/axios'
|
|
|
-import { getUserInfo } from '@/utils/tool'
|
|
|
-// 获取用户信息
|
|
|
-const userInfo = getUserInfo()
|
|
|
-
|
|
|
-defineOptions({ name: 'WorkHoursSelect' })
|
|
|
-
|
|
|
-interface IProp {
|
|
|
- onChange: (any) => any
|
|
|
- initialData?: any[] // 初始数据
|
|
|
- nowDate: string
|
|
|
-}
|
|
|
-const props = defineProps<IProp>()
|
|
|
-const { onChange } = props
|
|
|
-const { initialData, nowDate } = toRefs(props)
|
|
|
-// 默认工作项目总时长
|
|
|
-const defaultTotalTime = ref(0)
|
|
|
-// 工时填写弹框的ref
|
|
|
-const popperRef = ref<any>(null)
|
|
|
-// 初始化数据
|
|
|
-const dataList = ref<any[]>([])
|
|
|
-
|
|
|
-const setInitData = () => {
|
|
|
- if (initialData.value && initialData.value.length > 0) {
|
|
|
- const initialObj = {}
|
|
|
- initialData.value.forEach((item) => {
|
|
|
- initialObj[item.projectId] = item.workTime
|
|
|
- })
|
|
|
- const timer = setTimeout(() => {
|
|
|
- dataList.value = dataList.value.map((item) => {
|
|
|
- let count = 0
|
|
|
- if (initialObj[item.id]) {
|
|
|
- count = initialObj[item.id]
|
|
|
- }
|
|
|
- return {
|
|
|
- ...item,
|
|
|
- count
|
|
|
- }
|
|
|
- })
|
|
|
- dataList.value.sort((x, y) => y['count'] - x['count'])
|
|
|
- clearTimeout(timer)
|
|
|
- }, 0)
|
|
|
- } else {
|
|
|
- getAllProject()
|
|
|
- }
|
|
|
- getTotalTime()
|
|
|
-}
|
|
|
-
|
|
|
-watch(
|
|
|
- () => nowDate.value,
|
|
|
- () => {
|
|
|
- setInitData()
|
|
|
- }
|
|
|
-)
|
|
|
-
|
|
|
-onMounted(() => {
|
|
|
- getAllProject()
|
|
|
- setInitData()
|
|
|
-})
|
|
|
-// 获取任务列表
|
|
|
-const getAllProject = async () => {
|
|
|
- const { records } = await request.get(
|
|
|
- {
|
|
|
- url: `/project/page`,
|
|
|
- params: {
|
|
|
- pageSize: -1,
|
|
|
- userId: userInfo.id ?? ''
|
|
|
- }
|
|
|
- },
|
|
|
- '/business'
|
|
|
- )
|
|
|
- dataList.value = records.map((item) => {
|
|
|
- return {
|
|
|
- visible: false,
|
|
|
- count: 0,
|
|
|
- name: item.xmmc,
|
|
|
- id: item.id
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 临时存储数量
|
|
|
-const tempCount = ref(0)
|
|
|
-
|
|
|
-// 工时数量
|
|
|
-const changeCount = (val, index) => {
|
|
|
- // 获取临时数量
|
|
|
- tempCount.value = dataList.value[index].count
|
|
|
- dataList.value[index].count = val
|
|
|
-}
|
|
|
-
|
|
|
-// 计算总工时
|
|
|
-const getTotalTime = () => {
|
|
|
- defaultTotalTime.value = dataList.value.reduce((total, item) => {
|
|
|
- return total + item.count
|
|
|
- }, 0)
|
|
|
-}
|
|
|
-
|
|
|
-// 提交工时
|
|
|
-const panelSubmit = (index: number) => {
|
|
|
- dataList.value[index].visible = false
|
|
|
- getTotalTime()
|
|
|
- tempCount.value = 0
|
|
|
- // 提交后进行一下数据排序
|
|
|
- dataList.value.sort((x, y) => y['count'] - x['count'])
|
|
|
-
|
|
|
- // 提交工时后将已填数据传出去
|
|
|
- const countList = dataList.value
|
|
|
- .filter((item) => item.count > 0)
|
|
|
- .map((item) => ({
|
|
|
- workTime: item.count,
|
|
|
- projectId: item.id
|
|
|
- }))
|
|
|
- onChange(countList)
|
|
|
-
|
|
|
- // 提交工时后清空搜索框
|
|
|
- if (tempSearchList.value.length > 0) {
|
|
|
- searchInputRef.value?.clear()
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 取消选择
|
|
|
-const panelCancel = (index: number) => {
|
|
|
- dataList.value[index] = {
|
|
|
- ...dataList.value[index],
|
|
|
- visible: false,
|
|
|
- count: tempCount.value
|
|
|
- }
|
|
|
- getTotalTime()
|
|
|
-}
|
|
|
-
|
|
|
-// 搜索内容的临时存储
|
|
|
-const searchInputRef = ref<any>(null)
|
|
|
-const tempSearchList = ref<any[]>([])
|
|
|
-const searchValue = ref('')
|
|
|
-// 搜索内容改变时触发
|
|
|
-const searchOnchange = (val: string) => {
|
|
|
- // 搜索框第一次搜索时触发
|
|
|
- if (tempSearchList.value.length < dataList.value.length) {
|
|
|
- tempSearchList.value = dataList.value
|
|
|
- }
|
|
|
- // 搜索内容为空时,清空搜索列表,并显示全部数据
|
|
|
- if (val.length == 0) {
|
|
|
- clearSearch()
|
|
|
- return
|
|
|
- }
|
|
|
- // 使用 filter() 方法进行模糊查询
|
|
|
- const filteredArray = tempSearchList.value.filter((item) => {
|
|
|
- // 创建正则表达式,忽略大小写,并且匹配关键词
|
|
|
- const regex = new RegExp(val, 'i')
|
|
|
- // 使用正则表达式测试数组中的每个元素
|
|
|
- return regex.test(item.name)
|
|
|
- })
|
|
|
- dataList.value = filteredArray
|
|
|
-}
|
|
|
-// 清除内容时触发
|
|
|
-const clearSearch = () => {
|
|
|
- const countObj = {}
|
|
|
- dataList.value.forEach((item) => {
|
|
|
- countObj[item.id] = item.count
|
|
|
- })
|
|
|
- tempSearchList.value.forEach((item) => {
|
|
|
- if (countObj[item.id]) {
|
|
|
- // 如果有值,则赋值
|
|
|
- item.count = countObj[item.id]
|
|
|
- }
|
|
|
- })
|
|
|
- // 顺便进行一下数据排序
|
|
|
- tempSearchList.value.sort((x, y) => y['count'] - x['count'])
|
|
|
- // 还原全部数据
|
|
|
- dataList.value = tempSearchList.value
|
|
|
- tempSearchList.value = []
|
|
|
- getTotalTime()
|
|
|
-}
|
|
|
-
|
|
|
-interface IProject {
|
|
|
- name: string
|
|
|
-}
|
|
|
-const emit = defineEmits<{
|
|
|
- (e: 'select', val: IProject): void
|
|
|
-}>()
|
|
|
-
|
|
|
-const handleClick = (val: IProject): void => {
|
|
|
- emit('select', val)
|
|
|
-}
|
|
|
-</script>
|
|
|
-
|
|
|
-<style lang="scss" scoped>
|
|
|
-.WorkHoursSelect {
|
|
|
- width: 100%;
|
|
|
- height: calc(100% - 110px);
|
|
|
- display: flex;
|
|
|
-
|
|
|
- .select-title {
|
|
|
- font-family:
|
|
|
- Microsoft YaHei,
|
|
|
- Microsoft YaHei;
|
|
|
- width: 100px;
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 400;
|
|
|
- height: 36px;
|
|
|
- line-height: 36px;
|
|
|
- text-align: right;
|
|
|
- padding-right: 12px;
|
|
|
- :before {
|
|
|
- content: '*';
|
|
|
- color: var(--el-color-danger);
|
|
|
- margin-right: 4px;
|
|
|
- }
|
|
|
- }
|
|
|
- .select-list {
|
|
|
- flex: 1;
|
|
|
- height: 100%;
|
|
|
- border: 1px solid #dee0e3;
|
|
|
- .title {
|
|
|
- padding: 8px 20px;
|
|
|
- background-color: #f7f8fa;
|
|
|
- color: #121518;
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
- .list-ul {
|
|
|
- width: 100%;
|
|
|
- height: calc(100% - 50px);
|
|
|
- overflow-y: scroll;
|
|
|
- text-align: center;
|
|
|
- background-color: #fff;
|
|
|
- width: 100%;
|
|
|
- li {
|
|
|
- text-align: left;
|
|
|
- color: #2d333c;
|
|
|
- padding: 6px 10px;
|
|
|
- cursor: pointer;
|
|
|
- justify-content: space-between;
|
|
|
- &:hover {
|
|
|
- background-color: #d3e5ff;
|
|
|
- }
|
|
|
-
|
|
|
- .project-write {
|
|
|
- width: 100%;
|
|
|
- padding-left: 10px;
|
|
|
- }
|
|
|
- }
|
|
|
- .time-select {
|
|
|
- span {
|
|
|
- color: #1b80eb;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-.work-hours-select-panel {
|
|
|
- .title {
|
|
|
- padding: 10px;
|
|
|
- text-align: center;
|
|
|
- background-color: #f7f8fa;
|
|
|
- color: #121518;
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 400;
|
|
|
- }
|
|
|
- .popover-panel {
|
|
|
- padding: 0 !important;
|
|
|
- }
|
|
|
- .panel-input-number {
|
|
|
- height: 21px;
|
|
|
- margin: 10px 0;
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 400;
|
|
|
- color: #404956;
|
|
|
- .input-number {
|
|
|
- width: calc(100% - 86px);
|
|
|
- }
|
|
|
- }
|
|
|
- .panel-numeric-key {
|
|
|
- text-align: center;
|
|
|
- overflow-y: auto;
|
|
|
- height: calc(100% - 42px);
|
|
|
- ul {
|
|
|
- padding: 20px 0;
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(8, 36px);
|
|
|
- gap: 10px;
|
|
|
- justify-content: space-between; /* 水平居中 */
|
|
|
- align-items: center; /* 垂直居中 */
|
|
|
- li {
|
|
|
- text-align: center;
|
|
|
- height: 35px;
|
|
|
- line-height: 35px;
|
|
|
- border-radius: 2px;
|
|
|
- color: #2d333c;
|
|
|
- cursor: pointer;
|
|
|
- }
|
|
|
- li:hover {
|
|
|
- border: 2px solid #1b80eb;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-</style>
|