2025-06-10 15:30:22 +08:00

323 lines
9.1 KiB
Vue

<template>
<div class="textarea-wrap">
<a-textarea v-model:value="taskContent" @keydown="handleKeyCode($event)" :auto-size="{ minRows: 3 }" placeholder="请输入任务内容" ref="textareaRef" />
<div class="textarea-toolbar">
<div class="textarea-toolbar-item">
<a-space>
<!-- 提醒设置 -->
<a-popover v-model:open="noticePopoverVisible" title="提醒设置" trigger="click" @openChange="handleNoticeOpenChange">
<template #content>
<a-date-picker
v-model:value="form.tempNoticeData"
show-time
placeholder="请选择"
format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DD HH:mm:ss"
style="margin-bottom: 10px"
/>
<div style="display: flex; justify-content: flex-end">
<a-space>
<a-button type="default" @click="handleNoticeCancel">取消</a-button>
<a-button type="primary" @click="handleNoticeConfirm">确认</a-button>
</a-space>
</div>
</template>
<img v-if="form.noticeFlag" src="/@/assets/images/task/bell.png" alt="bell" title="提醒" />
<img v-else src="/@/assets/images/task/bell-default.png" alt="bell" title="提醒" />
</a-popover>
<!-- 添加标签 -->
<a-popover v-model:open="labelPopoverVisible" title="添加标签" trigger="click" @openChange="handleLabelOpenChange">
<template #content>
<div class="label-content">
<div
class="label-item"
v-for="item in props.dictValueList"
:key="item.valueCode"
:class="{ active: form.tempLabel === item.valueCode }"
@click="handleLabelClick(item)"
>
<div class="round"></div>
<span>{{ item.valueName }}</span>
</div>
</div>
<div style="display: flex; justify-content: flex-end">
<a-space>
<a-button type="default" @click="handleLabelCancel">取消</a-button>
<a-button type="primary" @click="handleAddLabel">确认</a-button>
</a-space>
</div>
</template>
<img v-if="form.label" src="/@/assets/images/task/tag.png" alt="tag" title="标签" />
<img v-else src="/@/assets/images/task/tag-default.png" alt="tag" title="标签" />
</a-popover>
<!-- 上传文件 -->
<a-popover v-model:open="filePopoverVisible" title="上传文件" trigger="click" @openChange="handleFileOpenChange">
<template #content>
<FileUpload
list-type="text"
:maxUploadSize="5"
buttonText="拖拽或上传附件"
:defaultFileList="fileList"
:multiple="false"
@change="handleChangeFile"
style="margin-bottom: 10px"
/>
<div style="display: flex; justify-content: flex-end">
<a-space>
<a-button type="default" @click="handleFileCancel">取消</a-button>
<a-button type="primary" @click="handleFileConfirm">确认</a-button>
</a-space>
</div>
</template>
<img v-if="form.fileUrl" src="/@/assets/images/task/file.png" alt="file" title="文件" />
<img v-else src="/@/assets/images/task/file-default.png" alt="file" title="文件" />
</a-popover>
</a-space>
</div>
<div class="textarea-toolbar-item">
<CloseOutlined @click="handleCancel" />
<CheckOutlined @click="handleConfirm" />
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, reactive } from 'vue';
import dayjs from 'dayjs';
import { CloseOutlined, CheckOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { taskApi } from '/@/api/business/task/task-api';
import FileUpload from '/@/components/support/file-upload/index.vue';
const taskContent = ref('');
const textareaRef = ref(null);
const emit = defineEmits(['cancel', 'confirm']);
const noticePopoverVisible = ref(false);
const labelPopoverVisible = ref(false);
const filePopoverVisible = ref(false);
const props = defineProps({
planDate: {
type: String,
default: dayjs().format('YYYY-MM-DD'),
},
taskType: {
type: String,
default: '',
},
dictValueList: {
type: Array,
default: () => [],
},
taskData: {
type: Object,
default: () => {},
},
});
let form = reactive({});
const fileList = ref([]);
const defaultForm = ref({
title: '',
fileUrl: '',
noticeFlag: 0,
noticeData: '',
tempNoticeData: '',
repeatFlag: 0,
repeatNum: 0,
repeatUnit: 1,
importantFlag: 0,
label: '',
tempLabel: undefined,
type: props.taskType,
planDate: props.planDate,
});
const handleNoticeOpenChange = (open) => {
if (!open) {
form.tempNoticeData = form.noticeData;
}
};
// 提醒取消
const handleNoticeCancel = () => {
form.tempNoticeData = form.noticeData;
noticePopoverVisible.value = false;
};
// 提醒确认
const handleNoticeConfirm = async () => {
form.noticeData = form.tempNoticeData || '';
form.noticeFlag = form.tempNoticeData ? 1 : 0;
noticePopoverVisible.value = false;
};
const handleLabelOpenChange = (open) => {
if (!open) {
form.tempLabel = form.label;
}
};
const handleKeyCode = (e) => {
console.log(e);
if (e.key === 'Enter') {
e.preventDefault();
handleConfirm();
}
};
// 添加标签
const handleAddLabel = async () => {
form.label = form.tempLabel;
labelPopoverVisible.value = false;
};
// 标签点击
const handleLabelClick = (item) => {
if (form.tempLabel === item.valueCode) {
form.tempLabel = undefined;
} else {
form.tempLabel = item.valueCode;
}
};
// 标签取消
const handleLabelCancel = () => {
form.tempLabel = form.label;
labelPopoverVisible.value = false;
};
// 文件上传
const handleChangeFile = (file) => {
fileList.value = file;
};
// 文件打开
const handleFileOpenChange = () => {
fileList.value = JSON.parse(form.fileUrl || '[]');
};
// 文件取消
const handleFileCancel = () => {
filePopoverVisible.value = false;
fileList.value = JSON.parse(form.fileUrl || '[]');
};
// 文件确认
const handleFileConfirm = () => {
if (fileList.value.length > 0) {
form.fileUrl = JSON.stringify(fileList.value);
}
filePopoverVisible.value = false;
};
const handleCancel = () => {
emit('cancel');
};
const handleConfirm = async () => {
if (taskContent.value.trim() === '') {
message.error('请输入任务内容');
return;
}
const param = {
...form,
title: taskContent.value,
};
const api = form.id ? taskApi.update : taskApi.add;
const res = await api(param);
if (res.ok) {
message.success(form.id ? '编辑成功' : '新增成功');
emit('confirm');
}
};
onMounted(() => {
textareaRef.value?.focus();
Object.assign(form, defaultForm.value);
if (props.taskData) {
Object.assign(form, props.taskData);
taskContent.value = props.taskData.title;
fileList.value = JSON.parse(props.taskData.fileUrl || '[]');
}
});
</script>
<style lang="less" scoped>
.textarea-wrap {
width: 100%;
position: relative;
:deep(.ant-input) {
padding-bottom: 32px;
&:focus {
~ .textarea-toolbar {
border-color: #20b066;
// box-shadow: 0 0 0 2px rgba(32, 176, 102, 0.2);
}
}
}
.textarea-toolbar {
width: 100%;
// border-top: 1px solid #40bd78;
border: 1px solid #d9d9d9;
border-radius: 0 0 4px 4px;
background-color: #fff;
height: 32px;
display: flex;
align-items: center;
position: absolute;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: space-between;
.textarea-toolbar-item {
display: flex;
align-items: center;
gap: 10px;
padding: 0 12px;
.anticon {
cursor: pointer;
font-size: 16px;
}
img {
cursor: pointer;
}
}
}
}
.label-content {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 10px 0 20px;
.label-item {
display: flex;
align-items: center;
padding: 4px 10px;
border-radius: 16px;
background-color: #f7f7f7;
cursor: pointer;
&.active {
background: #20b066;
span {
color: #fff;
}
}
.round {
width: 12px;
height: 12px;
border-radius: 50%;
background: #ffd900;
margin-right: 8px;
}
span {
font-size: 12px;
line-height: 14px;
color: #4d4d4d;
}
}
}
</style>