|
@@ -0,0 +1,414 @@
|
|
|
|
+<template>
|
|
|
|
+ <el-row>
|
|
|
|
+ <el-col :span="3">
|
|
|
|
+ <span style="background-color: #11acf5; color: #fff;">
|
|
|
|
+ <el-icon style="display: inline-block; padding-top: 2.5px;" ><Bottom /></el-icon>
|
|
|
|
+ </span>
|
|
|
|
+ <span>基于原始预算降低(百分比)</span>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :span="3">
|
|
|
|
+ <span style="background-color: #11acf5; color: #fff;">
|
|
|
|
+ <el-icon style="display: inline-block; padding-top: 2.5px;"><Top /></el-icon>
|
|
|
|
+ </span>
|
|
|
|
+ <span>基于原始预算升高(百分比)</span>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :span="3">
|
|
|
|
+ <span style="background-color: #3fd4cf; color: #fff;">
|
|
|
|
+ <el-icon style="display: inline-block; padding-top: 2.5px;"><Bottom /></el-icon>
|
|
|
|
+ </span>
|
|
|
|
+ <span>基于原始预算降低(数值)</span>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :span="3">
|
|
|
|
+ <span style="background-color: #3fd4cf; color: #fff;">
|
|
|
|
+ <el-icon style="display: inline-block; padding-top: 2.5px;"><Top /></el-icon>
|
|
|
|
+ </span>
|
|
|
|
+ <span>基于原始预算升高(数值)</span>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :span="3">
|
|
|
|
+ <span style="background-color: #3359b5; color: #fff;">
|
|
|
|
+ <el-icon style="display: inline-block; padding-top: 2.5px;"></el-icon>
|
|
|
|
+ </span>
|
|
|
|
+ <span>固定预算</span>
|
|
|
|
+ </el-col>
|
|
|
|
+ </el-row>
|
|
|
|
+ <div class="calendar">
|
|
|
|
+ <table class="calendar-table calendar-table-hour">
|
|
|
|
+ <thead class="calendar-head">
|
|
|
|
+ <tr>
|
|
|
|
+ <th rowspan="8" class="week-td">星期 / 时间</th>
|
|
|
|
+ <th colspan="12">00:00 - 12:00</th>
|
|
|
|
+ <th colspan="12">12:00 - 24:00</th>
|
|
|
|
+ <th colspan="4" rowspan="2" class="week-td" style="display: none">小时</th>
|
|
|
|
+ </tr>
|
|
|
|
+ <tr>
|
|
|
|
+ <th colspan="1" v-for="(_, i) in 24" :key="i">{{ i }}</th>
|
|
|
|
+ </tr>
|
|
|
|
+ </thead>
|
|
|
|
+ <tbody class="calendar-body">
|
|
|
|
+ <template v-for="(hoursList, week) in items">
|
|
|
|
+ <tr>
|
|
|
|
+ <th class="td-normal">{{ WeekMap[week] }}</th>
|
|
|
|
+ <td
|
|
|
|
+ class="un-selected" v-for="(info, hour) in hoursList" :key="hour"
|
|
|
|
+ @mousedown="handleMouseDown(week, hour, $event)"
|
|
|
|
+ @mouseover="handleMouseMove(week, hour)"
|
|
|
|
+ @mouseup="handleMouseUp"
|
|
|
|
+ :style="{ background: getCellBackgroundColor(info) }"
|
|
|
|
+ >
|
|
|
|
+ <!-- <div class="cell-text">
|
|
|
|
+
|
|
|
|
+ </div> -->
|
|
|
|
+ {{ getCellText(info) }}
|
|
|
|
+ <el-icon v-if="info.kind !=='FixBudget'" style="display: inline-block; padding-top: 2px;">
|
|
|
|
+ <Top v-if="info.kind==='UpPercent'|| info.kind==='UpNumber'"/>
|
|
|
|
+ <Bottom v-if="info.kind==='DownPercent' || info.kind==='DownNumber'"/>
|
|
|
|
+ </el-icon>
|
|
|
|
+ </td>
|
|
|
|
+ </tr>
|
|
|
|
+ </template>
|
|
|
|
+ <tr>
|
|
|
|
+ <th colspan="28" class="clear-bar">
|
|
|
|
+ <span class="middle">可拖动鼠标选择时间段</span>
|
|
|
|
+ <span class="hover-link fr" @click="resetAllBid">全部重置</span>
|
|
|
|
+ </th>
|
|
|
|
+ </tr>
|
|
|
|
+ </tbody>
|
|
|
|
+ </table>
|
|
|
|
+
|
|
|
|
+ <el-dialog
|
|
|
|
+ v-model="dialogVisible"
|
|
|
|
+ :close-on-click-modal="false"
|
|
|
|
+ title="编辑"
|
|
|
|
+ width="30%"
|
|
|
|
+ :before-close="closeDialog"
|
|
|
|
+ >
|
|
|
|
+ <vxe-form :data="formData" :rules="formRules" ref="formRef" @submit="submitBid" @reset="cancelBid">
|
|
|
|
+ <vxe-form-item field="kind" :span="16">
|
|
|
|
+ <el-select v-model="formData.kind" @change="changeKind" style="width: 100%;">
|
|
|
|
+ <el-option
|
|
|
|
+ v-for="item in KindEnum"
|
|
|
|
+ :key="item.value"
|
|
|
|
+ :label="item.label"
|
|
|
|
+ :value="item.value"
|
|
|
|
+ />
|
|
|
|
+ </el-select>
|
|
|
|
+ </vxe-form-item>
|
|
|
|
+ <vxe-form-item field="value" v-if="formData.kind !== ''" :span="8">
|
|
|
|
+ <vxe-input v-model="formData.value" type="float" size="medium" :min="0" :step="formData.kind==='FixBudget'?1:0.01" v-if="isNumber">
|
|
|
|
+ <template #prefix>$</template>
|
|
|
|
+ </vxe-input>
|
|
|
|
+ <vxe-input v-model="formData.value" type="float" size="medium" :min="0" v-else>
|
|
|
|
+ <template #suffix>%</template>
|
|
|
|
+ </vxe-input>
|
|
|
|
+ </vxe-form-item>
|
|
|
|
+ <vxe-form-item align="right" span="24">
|
|
|
|
+ <template #default>
|
|
|
|
+ <vxe-button type="reset" content="取消"></vxe-button>
|
|
|
|
+ <vxe-button type="submit" status="primary" content="确认"></vxe-button>
|
|
|
|
+ </template>
|
|
|
|
+ </vxe-form-item>
|
|
|
|
+ </vxe-form>
|
|
|
|
+ </el-dialog>
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
+import { ref, onMounted, watch, Ref } from 'vue'
|
|
|
|
+import { VXETable, VxeFormInstance, VxeFormPropTypes, VxeFormEvents } from 'vxe-table'
|
|
|
|
+
|
|
|
|
+interface Props {
|
|
|
|
+ data: {value: string, kind: string}[][],
|
|
|
|
+}
|
|
|
|
+interface FormData {
|
|
|
|
+ kind: string,
|
|
|
|
+ value: string
|
|
|
|
+}
|
|
|
|
+const props = defineProps<Props>()
|
|
|
|
+const emits = defineEmits(["cancelBid"])
|
|
|
|
+
|
|
|
|
+const mouseDown = ref(false)
|
|
|
|
+const startRowIndex = ref(0)
|
|
|
|
+const startColIndex = ref(0)
|
|
|
|
+const items = ref([])
|
|
|
|
+const WeekMap = {
|
|
|
|
+ 0: '星期一',
|
|
|
|
+ 1: '星期二',
|
|
|
|
+ 2: '星期三',
|
|
|
|
+ 3: '星期四',
|
|
|
|
+ 4: '星期五',
|
|
|
|
+ 5: '星期六',
|
|
|
|
+ 6: '星期日'
|
|
|
|
+}
|
|
|
|
+const dialogVisible = ref(false)
|
|
|
|
+const selectedItems = ref({})
|
|
|
|
+
|
|
|
|
+const KindEnum = [
|
|
|
|
+ { label: '无需调整预算', value: '' },
|
|
|
|
+ { label: '固定预算', value: 'FixBudget', color: '#3359b5' },
|
|
|
|
+ { label: '基于原始预算降低(百分比)', value: 'DownPercent', color: '#11acf5' },
|
|
|
|
+ { label: '基于原始预算升高(百分比)', value: 'UpPercent', color: '#11acf5' },
|
|
|
|
+ { label: '基于原始预算降低(数值)', value: 'DownNumber', color: '#3fd4cf' },
|
|
|
|
+ { label: '基于原始预算升高(数值)', value: 'UpNumber', color: '#3fd4cf' }
|
|
|
|
+]
|
|
|
|
+
|
|
|
|
+const formData:Ref<FormData> = ref({ kind: 'FixBudget', value: '1.00' })
|
|
|
|
+const isNumber = ref(true)
|
|
|
|
+const formRef = ref<VxeFormInstance>()
|
|
|
|
+const formRules = ref<VxeFormPropTypes.Rules>({
|
|
|
|
+ kind: [
|
|
|
|
+ {
|
|
|
|
+ validator({ itemValue }){
|
|
|
|
+ if (itemValue === undefined || itemValue === null) {
|
|
|
|
+ return new Error('必填项')
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ ],
|
|
|
|
+ value: [
|
|
|
|
+ {
|
|
|
|
+ validator({ itemValue }) {
|
|
|
|
+ if (formData.value.kind === 'FixBudget') {
|
|
|
|
+ if (itemValue < 1) {
|
|
|
|
+ return new Error('固定预算必须大于1')
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if (itemValue <= 0) {
|
|
|
|
+ return new Error('数值必须大于0')
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+onMounted(() => {
|
|
|
|
+ for (let i = 0; i < 7; i++) {
|
|
|
|
+ selectedItems.value[i] = []
|
|
|
|
+ const tmp = []
|
|
|
|
+ for (let j = 0; j < 24; j++) {
|
|
|
|
+ let kind = ''
|
|
|
|
+ let value = ''
|
|
|
|
+ if (props.data.length !== 0) {
|
|
|
|
+ kind = props.data[i][j].kind
|
|
|
|
+ value = props.data[i][j].value
|
|
|
|
+ }
|
|
|
|
+ tmp.push({ kind: kind, value: value })
|
|
|
|
+ }
|
|
|
|
+ items.value.push(tmp)
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+watch(props.data, () => {
|
|
|
|
+ for (let i = 0; i < 7; i++) {
|
|
|
|
+ for (let j = 0; j < 24; j++) {
|
|
|
|
+ items.value[i][j].kind = props.data[i][j].kind
|
|
|
|
+ items.value[i][j].value = props.data[i][j].value
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+const changeKind = () => {
|
|
|
|
+ if (formData.value.kind === 'DownPercent' || formData.value.kind === 'UpPercent') {
|
|
|
|
+ isNumber.value = false
|
|
|
|
+ } else {
|
|
|
|
+ isNumber.value = true
|
|
|
|
+ }
|
|
|
|
+ formRef.value.clearValidate('value')
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const getCellBackgroundColor = (row: any) => {
|
|
|
|
+ if (row.selected) return '#ccdbff'
|
|
|
|
+ if (row.kind) return KindEnum.find(item => item.value===row.kind).color
|
|
|
|
+ return ''
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const getCellText = (row: any) => {
|
|
|
|
+ if (row.kind === 'DownPercent' || row.kind === 'UpPercent') return row.value + '%'
|
|
|
|
+ if (row.value) return '$' + row.value
|
|
|
|
+ return ''
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const handleMouseDown = ( rowIndex: number, colIndex: number, event: any ) => {
|
|
|
|
+ if (event.button === 0) {
|
|
|
|
+ mouseDown.value = true
|
|
|
|
+ startRowIndex.value = rowIndex
|
|
|
|
+ startColIndex.value = colIndex
|
|
|
|
+ items.value[rowIndex][colIndex].selected = true
|
|
|
|
+ selectedItems.value[rowIndex].push(colIndex)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const handleMouseUp = (event: any) => {
|
|
|
|
+ if (event.button === 0) {
|
|
|
|
+ mouseDown.value = false
|
|
|
|
+ startColIndex.value = 0
|
|
|
|
+ startRowIndex.value = 0
|
|
|
|
+ dialogVisible.value = true
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const handleMouseMove = (rowIndex: number, colIndex: number) => {
|
|
|
|
+ if(mouseDown.value) {
|
|
|
|
+ selectCell(rowIndex, colIndex)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const selectCell = (rowIndex: number, colIndex: number) => {
|
|
|
|
+ const rowStart = Math.min(startRowIndex.value, rowIndex)
|
|
|
|
+ const rowEnd = Math.max(startRowIndex.value, rowIndex)
|
|
|
|
+ const cellStart = Math.min(startColIndex.value, colIndex)
|
|
|
|
+ const cellEnd = Math.max(startColIndex.value, colIndex)
|
|
|
|
+
|
|
|
|
+ for (let i = rowStart; i <= rowEnd; i++) {
|
|
|
|
+ for (let j = cellStart; j <= cellEnd; j++) {
|
|
|
|
+ items.value[i][j].selected = true
|
|
|
|
+ selectedItems.value[i].push(j)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const submitBid = () => {
|
|
|
|
+ if (formData.value.kind === '') {
|
|
|
|
+ for (const row of Object.keys(selectedItems.value)) {
|
|
|
|
+ for (const col of selectedItems.value[row]) {
|
|
|
|
+ props.data[row][col].kind = ''
|
|
|
|
+ props.data[row][col].value = ''
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ for (const row of Object.keys(selectedItems.value)) {
|
|
|
|
+ for (const col of selectedItems.value[row]) {
|
|
|
|
+ props.data[row][col].kind = formData.value.kind
|
|
|
|
+ props.data[row][col].value = formData.value.value
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ clearSelectedItems()
|
|
|
|
+ clearCellBackgroundColor()
|
|
|
|
+ dialogVisible.value = false
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const cancelBid = () => {
|
|
|
|
+ clearSelectedItems()
|
|
|
|
+ clearCellBackgroundColor()
|
|
|
|
+ dialogVisible.value = false
|
|
|
|
+ emits('cancelBid')
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const closeDialog = (done: Function) => {
|
|
|
|
+ // console.log("closeDialog")
|
|
|
|
+ cancelBid()
|
|
|
|
+ done()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const clearCellBackgroundColor = () => {
|
|
|
|
+ for (const hoursList of items.value) {
|
|
|
|
+ for (const info of hoursList) {
|
|
|
|
+ info.selected = false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+const clearSelectedItems = () => {
|
|
|
|
+ for (var i = 0; i < 7; i++) {
|
|
|
|
+ selectedItems.value[i] = []
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+const resetAllBid = () => {
|
|
|
|
+ for (let i = 0; i < 7; i++) {
|
|
|
|
+ for (let j = 0; j < 24; j++) {
|
|
|
|
+ items.value[i][j].value = 0
|
|
|
|
+ items.value[i][j].selected = false
|
|
|
|
+ props.data[i][j].kind = ''
|
|
|
|
+ props.data[i][j].value = ''
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
+.calendar {
|
|
|
|
+ background-color: #fff;
|
|
|
|
+ -webkit-user-select: none;
|
|
|
|
+ position: relative;
|
|
|
|
+ display: inline-block;
|
|
|
|
+
|
|
|
|
+ .calendar-table {
|
|
|
|
+ border-collapse: collapse;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .week-td {
|
|
|
|
+ width: 90px;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+table {
|
|
|
|
+ display: table;
|
|
|
|
+ border-collapse: separate;
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
+ text-indent: initial;
|
|
|
|
+ border-spacing: 2px;
|
|
|
|
+ border-color: gray;
|
|
|
|
+
|
|
|
|
+ thead {
|
|
|
|
+ display: table-header-group;
|
|
|
|
+ vertical-align: middle;
|
|
|
|
+ border-color: inherit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ tr {
|
|
|
|
+ border: 1px solid #e0e5f4;
|
|
|
|
+ font-size: 12px;
|
|
|
|
+ text-align: center;
|
|
|
|
+ line-height: 32px;
|
|
|
|
+ color: rgba(0, 0, 0, 0.5);
|
|
|
|
+
|
|
|
|
+ th {
|
|
|
|
+ min-width: 40px;
|
|
|
|
+ border: 1px solid #e0e5f4;
|
|
|
|
+ font-size: 12px;
|
|
|
|
+ text-align: center;
|
|
|
|
+ line-height: 32px;
|
|
|
|
+ background: #f7f8fa;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ td {
|
|
|
|
+ border: 1px solid #e0e5f4;
|
|
|
|
+ font-size: 12px;
|
|
|
|
+ text-align: center;
|
|
|
|
+ line-height: 32px;
|
|
|
|
+ min-width: 40px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ color: #fff;
|
|
|
|
+ &:hover {
|
|
|
|
+ background: #ccdbff;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.clear-bar {
|
|
|
|
+ line-height: 32px;
|
|
|
|
+ padding: 0 12px;
|
|
|
|
+
|
|
|
|
+ .hover-link {
|
|
|
|
+ color: #1c6bde;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ font-size: 13px;
|
|
|
|
+ }
|
|
|
|
+ .fr {
|
|
|
|
+ float: right;
|
|
|
|
+ }
|
|
|
|
+ .middle {
|
|
|
|
+ float: center;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.cell-text {
|
|
|
|
+ color: #fff;
|
|
|
|
+}
|
|
|
|
+</style>
|