feat:import使用module文件名

This commit is contained in:
1708-huayu 2025-07-18 19:02:14 +08:00
parent a2eb3d0473
commit 8ca5bb4e31
16 changed files with 438 additions and 152 deletions

View File

@ -0,0 +1,69 @@
.reverseScrollList {
display: flex;
flex-direction: column; /* 垂直反转 */
/*overflow-y: auto;*/
}
/* 容器样式 */
.container {
display: flex;
justify-content: center;
/* 水平居中 */
align-items: center;
/* 垂直居中 */
width: 100%;
}
/* 线条与文本容器 */
.lineWithText {
position: relative;
width: 80%;
/* 根据需要调整宽度 */
text-align: center;
}
/* 使用伪元素创建线条 */
.lineWithText::before {
content: '';
position: absolute;
top: 50%;
left: 0;
width: 100%;
border-top: 1px solid #c8c7cc;
/* 线条颜色和粗细 */
z-index: 0;
/* 在文本下面 */
}
/* 中间文本 */
.centerText {
background-color: white;
/* 背景色,确保文本清晰可见 */
padding: 0 10px;
/* 给文本一些内边距 */
position: relative;
z-index: 1;
/* 确保文本在上面 */
font-size: 16px;
/* 文本大小 */
color: #c8c7cc;
}
.logTaskContent {
width: 80%;
padding: 10rpx 40rpx;
.detailLine {
margin-top: 0.5rem;
display: inline-block;
padding: 4rpx 20rpx;
border: solid;
border-radius: 20rpx;
border-color: #1677FF;
white-space: normal;
word-break: break-all;
word-wrap: break-word;
font-size: 1rem;
}
.detailLineClick {
background-color: #1677FF;
color: #fff;
}
}

View File

@ -1,18 +1,17 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import { Avatar, List, message } from 'antd'; import {Avatar, Input, List, message} from 'antd';
import VirtualList from 'rc-virtual-list'; import VirtualList from 'rc-virtual-list';
import { Button, Drawer } from 'antd'; import {Button, Drawer} from 'antd';
interface UserItem { import {ListDiary, SelectDiary} from "@/components/type/Diary";
email: string; import TextArea from "antd/es/input/TextArea";
gender: string; import style from "@/components/DiaryOption.module.css"
name: string; import dayjs from "dayjs";
avatar: string; import {addTaskLogAPI} from "@/components/service/Diary";
}
const CONTAINER_HEIGHT = 400; const CONTAINER_HEIGHT = 400;
const PAGE_SIZE = 20; const PAGE_SIZE = 20;
const DiaryOption = () => { const DiaryOption = (props: SelectDiary) => {
// 抽屉 start // 抽屉 start
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const showDrawer = () => { const showDrawer = () => {
@ -23,17 +22,54 @@ const DiaryOption = () => {
}; };
// 抽屉 end // 抽屉 end
// 头按钮设置 start
const [currentIndex, setCurrentIndex] = useState(1);
// 头按钮设置 end
// 数据 start // 数据 start
const [data, setData] = useState<UserItem[]>([]); const [diaryList, setDiaryList] = useState<ListDiary[]>([]);
const [diaryReduceList, setDiaryReduceList] = useState<ListDiary[]>([])
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const noMore = {id: '0', keyId: 'o0',createdDate:new Date(), description: '没有更多了',taskId:props.taskId, enableFlag: 'day-separate'};
const [noMoreFlag, setNoMoreFlag] = useState(false)
const [sendValue,setSendValue] = useState<string>();
const [sendValueFlag,setSendValueFlag] =useState(false);
const handleSend = ()=>{
if(sendValueFlag){
return
}
setSendValueFlag(true);
if (!sendValue?.trim()) {
message.info("发送信息不能为空");
return;
}
addTaskLogAPI({
description: sendValue!,
taskId: props.taskId,
enableFlag: '1'
}).then(res => {
setDiaryList([res.data.data, ...diaryList])
setSendValue(undefined)
}).finally(() => {
setSendValueFlag(false);
})
}
const appendData = (showMessage = true) => { const appendData = (showMessage = true) => {
const fakeDataUrl = `https://660d2bd96ddfa2943b33731c.mockapi.io/api/users/?page=${page}&limit=${PAGE_SIZE}`; const fakeDataUrl = process.env.NEXT_PUBLIC_TODO_REQUEST_URL + `/task/message/diary/select`;
fetch(fakeDataUrl) fetch(fakeDataUrl, {
method: 'POST', headers: {
'Content-Type': 'application/json', // 指定 JSON 格式
'Authorization': `Bearer ${localStorage.getItem('platform-security')}`,
}, body: JSON.stringify({pageNumber: page, pageSize: 100, data: {taskId: props.taskId}})
})
.then((res) => res.json()) .then((res) => res.json())
.then((body) => { .then((body) => {
const results = Array.isArray(body) ? body : []; const results = Array.isArray(body.data.content) ? body.data.content : [];
setData(data.concat(results)); if (results.length === 0) {
diaryList.push(noMore);
setNoMoreFlag(true);
}
setDiaryList(diaryList.concat(results));
setPage(page + 1); setPage(page + 1);
showMessage && message.success(`${results.length} more items loaded!`); showMessage && message.success(`${results.length} more items loaded!`);
}); });
@ -43,10 +79,38 @@ const DiaryOption = () => {
appendData(false); appendData(false);
}, []); }, []);
useEffect(() => {
console.log("处理日志集合",diaryList)
const returnResult: ListDiary[] = []
diaryList.filter(taskLog => {
if (currentIndex === 0) { return true }
else if (currentIndex === 1 && taskLog.enableFlag === "1") { return true }
else if (currentIndex === 2 && taskLog.enableFlag === "0") { return true }
else return false;
}).reduce((map, taskLog) => {
if (!map.has(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))) {
map.set(dayjs(taskLog.createdDate).format("YYYY-MM-DD"), []);
}
map.get(dayjs(taskLog.createdDate).format("YYYY-MM-DD"))?.push(taskLog);
return map;
}, new Map()).forEach((value, Key) => {
returnResult.push(...value)
returnResult.push({
description: dayjs(Key).isSame(dayjs(), 'date') ? "今天" : dayjs(Key).format("YYYY-MM-DD"),
id: dayjs(Key).format("YYYY-MM-DD"),
enableFlag: "day-separate",
taskId: props.taskId,
createdDate: new Date()
})
})
setDiaryReduceList(returnResult);
}, [diaryList]);
const onScroll = (e: React.UIEvent<HTMLElement, UIEvent>) => { const onScroll = (e: React.UIEvent<HTMLElement, UIEvent>) => {
// Refer to: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#problems_and_solutions // Refer to: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#problems_and_solutions
if ( if (
Math.abs(e.currentTarget.scrollHeight - e.currentTarget.scrollTop - CONTAINER_HEIGHT) <= 1 Math.abs(e.currentTarget.scrollHeight - e.currentTarget.scrollTop - CONTAINER_HEIGHT) <= 1 &&
!noMoreFlag
) { ) {
appendData(); appendData();
} }
@ -54,41 +118,110 @@ const DiaryOption = () => {
// 数据 end // 数据 end
// 点击操作 start
const [clickTaskDiary, setClickTaskDiary] = useState<ListDiary>()
// 点击操作 end
return ( return (
<> <>
<Button type="primary" onClick={showDrawer}> <Button type="primary" onClick={showDrawer}>
</Button> </Button>
<Drawer <Drawer
style={{boxSizing:"border-box"}}
mask={false} mask={false}
title="Basic Drawer" title={props.taskName}
closable={{ 'aria-label': 'Close Button' }} closable={{'aria-label': 'Close Button'}}
onClose={onClose} onClose={onClose}
open={open} open={open}
footer={
<div style={{
display: 'flex',
alignItems: 'stretch', // 关键:强制子项等高
justifyContent: 'space-between',
height: 'auto', // 父容器高度由内容决定
}}>
<TextArea
rows={4}
maxLength={255}
showCount
classNames={{count: 'ant-input-data-count-inner'}}
styles={{
count: {
// color:"red",
bottom: "0px"
}
}}
style={{
resize: 'none',
flex: 1, // 占据剩余空间
}}
value={sendValue}
onChange={(val) => setSendValue(val.target.value)}
placeholder='输入日记心得,长按日记心得有惊喜'
onKeyDown={event => {
console.log({event})
if (event.ctrlKey && event.key === 'Enter') {
handleSend();
// 阻止换行符插入
event.preventDefault();
}
}}
/>
<Button
type="primary"
style={{
flexShrink: 0,
width: '2rem',
whiteSpace: 'normal',
wordWrap: 'break-word',
margin: 0,
padding: "2px",
height: 'auto'// 移除 ,依赖父容器的 alignItems: 'stretch'
}}
loading={sendValueFlag}
onClick={handleSend}
>
{sendValueFlag ? '发送中...' : '发送'}
</Button>
</div>
}
> >
<div className="displayFlexRow"> <div className="displayFlexRow">
<Button type="primary"></Button> <Button style={{flexGrow: 1}} onClick={() => setCurrentIndex(0)}
<Button type="primary"></Button> type={currentIndex == 0 ? "primary" : "default"}></Button>
<Button type="primary"></Button> <Button style={{flexGrow: 1}} onClick={() => setCurrentIndex(1)}
type={currentIndex == 1 ? "primary" : "default"}></Button>
<Button style={{flexGrow: 1}} onClick={() => setCurrentIndex(2)}
type={currentIndex == 2 ? "primary" : "default"}></Button>
</div> </div>
<div> <div>
<List> <List>
<VirtualList <VirtualList
data={data} data={diaryReduceList}
height={CONTAINER_HEIGHT} height={CONTAINER_HEIGHT}
itemHeight={47} itemHeight={47}
itemKey="email" itemKey="email"
onScroll={onScroll} onScroll={onScroll}
className={style.reverseScrollList}
> >
{item => ( {item => (
<List.Item key={item.email}> item.enableFlag === 'day-separate' ?
<List.Item.Meta <div className={style.container} key={item.keyId}>
avatar={<Avatar src={item.avatar} />} <div className={style.lineWithText}>
title={<a href="https://ant.design">{item.name}</a>} <text className={style.centerText}>{item.description}</text>
description={item.email} </div>
/> </div>
<div>Content</div> : <div className={style.logTaskContent} key={item.id}>
</List.Item> <div
className={`${style.detailLine} ${item.id === clickTaskDiary?.id ? style.detailLineClick : ''}`}
onClick={() => setClickTaskDiary(item)}>
<text
style={{textDecoration: item.enableFlag === '0' && currentIndex === 0 ? 'line-through' : ''}}>
{item.description}
</text>
</div>
</div>
)} )}
</VirtualList> </VirtualList>
</List> </List>

View File

@ -0,0 +1,9 @@
import {httpReq} from "@/utils/axiosReq";
import {AddDiary, ListDiary} from "@/components/type/Diary";
import {ResponseVO} from "@/lib/definitions";
import {AxiosResponse} from "axios";
export const addTaskLogAPI= (data:AddDiary):Promise<AxiosResponse<ResponseVO<ListDiary>>> =>{
return httpReq.post(process.env.NEXT_PUBLIC_TODO_REQUEST_URL + "/task/message/diary",
data)
}

View File

@ -0,0 +1,44 @@
export type ListDiary = AddDiary & {
generateTaskId?: string,
lastModifiedDate?: Date,
id: string,
// 不能以数字开头,用户滚动定位
keyId?:string,
createdDate: Date,
deleteFlag?: number
}
export type AddDiary = {
enableFlag: string,
description: string,
taskId: string,
}
export type SelectDiary = {
taskName: string,
taskId: string,
}
async function postJsonData(url: string, data: object) {
const response = await fetch(url, {
method: 'POST', // 或 'PUT'、'PATCH'
headers: {
'Content-Type': 'application/json', // 指定 JSON 格式
},
body: JSON.stringify(data), // 将对象转为 JSON 字符串
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json(); // 解析响应为 JSON
}
// 使用示例
interface User {
name: string;
age: number;
}
const user: User = { name: 'Alice', age: 25 };
postJsonData('https://api.example.com/users', user)
.then((result) => console.log('Success:', result))
.catch((error) => console.error('Error:', error));

View File

@ -50,6 +50,7 @@ export type DataType = TaskMessage&{
actualEndTime?:Date; actualEndTime?:Date;
actualTimeRange?:(string|Dayjs|undefined)[] actualTimeRange?:(string|Dayjs|undefined)[]
children: DataType[]|undefined; children: DataType[]|undefined;
sortNo?:number;
} }
export type DictType={ export type DictType={
id:number; id:number;

View File

@ -3,4 +3,8 @@ import {httpReq} from "@/utils/axiosReq";
export const generateQrcodeAPI = (data:{}) => { export const generateQrcodeAPI = (data:{}) => {
return httpReq.post(process.env.NEXT_PUBLIC_SECURITY_REQUEST_URL + "/V2/wx/login/generate/qrcode", return httpReq.post(process.env.NEXT_PUBLIC_SECURITY_REQUEST_URL + "/V2/wx/login/generate/qrcode",
data) data)
}
export const askLoginAPI = (data:{})=>{
return httpReq.post(process.env.NEXT_PUBLIC_SECURITY_REQUEST_URL + "/V2/wx/login/pc/ask/login",
data)
} }

View File

@ -0,0 +1,9 @@
import {httpReq} from "@/utils/axiosReq";
import {unstable_noStore as noStore} from "next/dist/server/web/spec-extension/unstable-no-store";
export const editExpectAPI = (requestParam: {}) =>{
noStore();
return httpReq.put(
process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/task/editExpect', requestParam)
}

View File

@ -6,10 +6,11 @@ import {
import {message, theme, Modal, Button, Skeleton, Image, QRCode} from 'antd'; import {message, theme, Modal, Button, Skeleton, Image, QRCode} from 'antd';
import React, {useLayoutEffect} from 'react'; import React, {useLayoutEffect} from 'react';
import {useState} from 'react'; import {useState} from 'react';
import {CaptchaLoginSuccess, LoginObject} from "@/lib/login/definitions"; import {LoginObject} from "@/lib/login/definitions";
import {useRouter} from 'next/navigation' import {useRouter} from 'next/navigation'
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import {generateQrcodeAPI} from "@/lib/login/service"; import {askLoginAPI, generateQrcodeAPI} from "@/lib/login/service";
import Cookies from "js-cookie";
export default function XcxLoginPage() { export default function XcxLoginPage() {
const [loaded, setLoaded] = useState(false); const [loaded, setLoaded] = useState(false);
@ -21,49 +22,69 @@ export default function XcxLoginPage() {
const [qrCodeStatus, setQrCodeStatus] = useState<'active' | 'expired' | 'loading' | 'scanned'>(); const [qrCodeStatus, setQrCodeStatus] = useState<'active' | 'expired' | 'loading' | 'scanned'>();
const [qrCodeValue, setQrCodeValue] = useState<string>("-"); const [qrCodeValue, setQrCodeValue] = useState<string>("-");
const generateQrcode = () => { const generateQrcode = () => {
// 不在二维码页面直接返回
if (!qrCodeShow) {
return;
}
if (qrCodeStatus=='loading') { if (qrCodeStatus=='loading') {
message.info({content:"请耐心等待"}) message.info({content:"请耐心等待"})
}else if (qrCodeStatus=='expired') { }else if (qrCodeStatus=='expired') {
generateQrCode() setQrCode()
}else if (qrCodeStatus=='active') { }else if (qrCodeStatus=='active') {
}else if (qrCodeStatus=='scanned') { }else if (qrCodeStatus=='scanned') {
}else { }else {
generateQrCode() setQrCode()
} }
} }
function generateQrCode(){ function setQrCode(){
// 生成唯一id // 生成唯一id
setQrCodeStatus("loading")
const clientId: string = uuidv4(); const clientId: string = uuidv4();
generateQrcodeAPI({clientId}).then(res=>{ generateQrcodeAPI({clientId}).then(res=>{
setQrCodeValue(JSON.stringify({ let qrCodeData={
clientId,serverId:res.data.data clientId,serverId:res.data.data,local:"马上行计划管理"
})) }
setQrCodeValue(JSON.stringify(qrCodeData))
setQrCodeStatus("active")
let timeout=undefined;
// 设置定时器每2000毫秒2秒执行一次myFunction
let timerId =undefined;
timerId = setInterval(askLogin,2000,timeout,timerId,qrCodeData);
// 如果需要停止定时器,可以调用 clearInterval(timerId);
// 例如1分钟后停止定时器
timeout = setTimeout(() => {
setQrCodeStatus("expired")
clearInterval(timerId);
console.log("定时器已停止");
}, 60000);
}).catch(()=>{
setQrCodeStatus("expired")
}) })
} }
// 二维码 end function askLogin(timeout:any,timerId:any,qrCodeData:{}){
askLoginAPI(qrCodeData).then(res=>{
if (res.data.status.success&&res.data.data.length>0){
setQrCodeStatus("scanned")
if(timeout){
clearTimeout(timeout);
clearInterval(timerId);
}
// localStorage.removeItem("platform-security")
localStorage.setItem('platform-security', res.data.data[0].token)
// 删除名为 'platform-security' 的Cookie
// Cookies.remove('platform-security');
// 设置一个有效期为7天的Cookie
Cookies.set('platform-security', res.data.data[0].token, { expires: 7 });
router.push('/task/project')
}
})
}
// 二维码 end
const {token} = theme.useToken(); const {token} = theme.useToken();
const router = useRouter() const router = useRouter()
const [messageApi, contextHolder] = message.useMessage(); const [messageApi, contextHolder] = message.useMessage();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [captchaLoginSuccessList, setCaptchaLoginSuccessList] = useState([]); const [captchaLoginSuccessList, setCaptchaLoginSuccessList] = useState([]);
const captchaUserNameConfirm = (captchaLoginSuccess: CaptchaLoginSuccess) => {
messageApi.open({
type: 'info',
content: "使用帐号" + captchaLoginSuccess.username + "登录成功"
})
localStorage.setItem('platform-security', captchaLoginSuccess.token)
router.push('/task/project')
setOpen(false)
setLoading(false)
}
return ( return (
<div <div
style={{ style={{
@ -72,36 +93,27 @@ export default function XcxLoginPage() {
}} }}
> >
{contextHolder} {contextHolder}
<Modal
open={open}
title="发现您注册有多个帐号,请选择需要登录的账号"
footer={
captchaLoginSuccessList.map((label: CaptchaLoginSuccess, index) => (
<Button key={label.username} onClick={() => captchaUserNameConfirm(label)}>
{label.username}
</Button>
))
}
>
</Modal>
{loaded ? <LoginFormPage {loaded ? <LoginFormPage
backgroundImageUrl="/20-1733751222585.jpg" backgroundImageUrl="/20-1733751222585.jpg"
title="马上行计划管理" title="马上行计划管理"
subTitle={qrCodeShow?undefined:"使用微信扫码打开小程序"}
containerStyle={{ containerStyle={{
backgroundColor: 'rgba(0, 0, 0,0.65)', backgroundColor: 'rgba(0, 0, 0,0.65)',
backdropFilter: 'blur(4px)', backdropFilter: 'blur(4px)',
}} }}
onFinish={(formData: LoginObject) => { onFinish={(formData: LoginObject) => {
// 二维码和小程序扫码来回切换 // 二维码和小程序扫码来回切换
if (!qrCodeShow) {
// 生成二维码
generateQrcode()
}
setQrCodeShow(!qrCodeShow) setQrCodeShow(!qrCodeShow)
// 生成二维码
generateQrcode()
}} }}
submitter={{ searchConfig: { submitText: qrCodeShow?"在我的-PC扫码登录或者返回":"已打开微信小程序,生成登录码。",resetText: '重置2'}}} submitter={{ searchConfig: { submitText: qrCodeShow?"在我的-PC扫码登录或者返回":"已打开微信小程序,生成登录码。",resetText: '重置2'}}}
> >
<div className="displayFlexColumn" style={{margin:'20px'}}> <div className="displayFlexColumn" style={{margin:'20px'}}>
{ {
qrCodeShow?<QRCode value={qrCodeValue} size={300} status={qrCodeStatus} onRefresh={() => console.log('refresh')} />: qrCodeShow?<QRCode value={qrCodeValue} size={300} status={qrCodeStatus} onRefresh={generateQrcode} />:
<Image width={300} src="/static/pc-Web.png"/> <Image width={300} src="/static/pc-Web.png"/>
} }
</div> </div>

View File

@ -3,7 +3,7 @@ import {
ProFormText, ProFormText,
QueryFilter, QueryFilter,
} from '@ant-design/pro-components'; } from '@ant-design/pro-components';
import '@/ui/task/CustomSearchForm.module.css' import '@/ui/task/CustomSearchForm.modules.css'
import {taskStateList} from "@/lib/task/project/data"; import {taskStateList} from "@/lib/task/project/data";
const CustomSearchForm= () => { const CustomSearchForm= () => {
return ( return (

View File

@ -4,7 +4,7 @@ import {Button, DatePicker, Select, Space} from "antd";
import {usePathname, useRouter} from "next/navigation"; import {usePathname, useRouter} from "next/navigation";
import {DetailModelForm} from "@/ui/task/project/DetailModelForm"; import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
import {OPERATION_BUTTON_TYPE, taskStateList} from "@/lib/task/project/data"; import {OPERATION_BUTTON_TYPE, taskStateList} from "@/lib/task/project/data";
import '@/ui/task/TitleOperation.modules.css' import style from '@/ui/task/TitleOperation.module.css'
import LocalContext from "@/ui/LocalContent"; import LocalContext from "@/ui/LocalContent";
import {RequestDateType} from "@/ui/task/RequestDateType"; import {RequestDateType} from "@/ui/task/RequestDateType";
import dayjs, {Dayjs} from "dayjs"; import dayjs, {Dayjs} from "dayjs";
@ -38,7 +38,7 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
document.title = pName; document.title = pName;
} }
}, [pName]); }, [pName]);
return <div className="container"> return <div className={style.container}>
<Space style={{marginTop: 0, "height": "42px", "alignContent": "center"}}> <Space style={{marginTop: 0, "height": "42px", "alignContent": "center"}}>
<DetailModelForm haveButton={true} open={false} operationId={OPERATION_BUTTON_TYPE.ADD} <DetailModelForm haveButton={true} open={false} operationId={OPERATION_BUTTON_TYPE.ADD}
description='添加主线任务' reloadData={refreshData}/> description='添加主线任务' reloadData={refreshData}/>

View File

@ -13,8 +13,7 @@ import {SearchObject} from "@/lib/definitions";
import LocalContext from "@/ui/LocalContent"; import LocalContext from "@/ui/LocalContent";
import withDragAndDrop, {EventInteractionArgs} from "react-big-calendar/lib/addons/dragAndDrop"; import withDragAndDrop, {EventInteractionArgs} from "react-big-calendar/lib/addons/dragAndDrop";
import {TaskEvent} from "@/lib/task/calendar/data"; import {TaskEvent} from "@/lib/task/calendar/data";
import {number} from "prop-types"; import {editExpectAPI} from "@/lib/task/calendar/service";
import {DATE_TIME} from "@/lib/constants";
/** /**
* https://github.com/jquense/react-big-calendar?tab=readme-ov-file * https://github.com/jquense/react-big-calendar?tab=readme-ov-file
@ -26,7 +25,7 @@ const CalShow: React.FC = () => {
dayjs.locale('zh-cn') dayjs.locale('zh-cn')
const [view, setView] = useState<View>('week'); const [view, setView] = useState<View>('week');
const [date, setDate] = useState<Date>(new Date()); const [date, setDate] = useState<Date>(new Date());
const clickRef = useRef<number|undefined|null>(null) const clickRef = useRef<number | undefined | null>(null)
// 展示在页面的任务,默认获取当前月的信息。 // 展示在页面的任务,默认获取当前月的信息。
const [events, setEvents] = useState<TaskEvent[]>([]); const [events, setEvents] = useState<TaskEvent[]>([]);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@ -35,19 +34,21 @@ const CalShow: React.FC = () => {
const [itemId, setItemId] = useState('-1'); const [itemId, setItemId] = useState('-1');
const [expectedStartTime, setExpectedStartTime] = useState<Dayjs>(); const [expectedStartTime, setExpectedStartTime] = useState<Dayjs>();
const [expectedEndTime, setExpectedEndTime] = useState<Dayjs>(); const [expectedEndTime, setExpectedEndTime] = useState<Dayjs>();
const [range, setRange] = useState<{start: Date; end: Date}>({ const [range, setRange] = useState<{ start: Date; end: Date }>({
start: dayjs(date).startOf('week').toDate(), start: dayjs(date).startOf('week').toDate(),
end: dayjs(date).endOf('week').toDate() end: dayjs(date).endOf('week').toDate()
}); });
const state=useContext(LocalContext).taskState; const state = useContext(LocalContext).taskState;
const handleViewChange = (newView: View) => { const handleViewChange = (newView: View) => {
setView(newView); setView(newView);
}; };
var pid = useSearchParams().get('pid'); var pid = useSearchParams().get('pid');
function clearClickTimeout(){
clickRef && typeof clickRef.current=== 'number' &&!isNaN(clickRef.current) && isFinite(clickRef.current)&&window.clearTimeout(clickRef.current) function clearClickTimeout() {
clickRef && typeof clickRef.current === 'number' && !isNaN(clickRef.current) && isFinite(clickRef.current) && window.clearTimeout(clickRef.current)
} }
const handleNavigate = (newDate: Date) => { const handleNavigate = (newDate: Date) => {
console.log('handleNavigate', newDate) console.log('handleNavigate', newDate)
setDate(newDate); setDate(newDate);
@ -62,7 +63,7 @@ const CalShow: React.FC = () => {
}; };
useEffect(() => { useEffect(() => {
console.log("CalShow:useEffect:range",range) console.log("CalShow:useEffect:range", range)
const searchListE = [] const searchListE = []
if (pid != null) { if (pid != null) {
searchListE.push( searchListE.push(
@ -82,7 +83,7 @@ const CalShow: React.FC = () => {
return () => { return () => {
clearClickTimeout() clearClickTimeout()
} }
}, [useContext(LocalContext),range]); }, [useContext(LocalContext), range]);
const message = { const message = {
week: '周', week: '周',
work_week: '工作周', work_week: '工作周',
@ -102,27 +103,27 @@ const CalShow: React.FC = () => {
pageSize: 9999, pageSize: 9999,
pageNumber: 1, pageNumber: 1,
data: searchList, data: searchList,
startTime:range.start, startTime: range.start,
// startTime:dayjs(range.start).format('YYYY-MM-DD HH:mm:ss'), // startTime:dayjs(range.start).format('YYYY-MM-DD HH:mm:ss'),
endTime:range.end, endTime: range.end,
// endTime:dayjs(range.end).format('YYYY-MM-DD HH:mm:ss'), // endTime:dayjs(range.end).format('YYYY-MM-DD HH:mm:ss'),
startColumn:"expected_start_time", startColumn: "expected_start_time",
endColumn:"expected_end_time" endColumn: "expected_end_time"
}) })
getTaskTreeResult(request).then(responseD => { getTaskTreeResult(request).then(responseD => {
if (responseD.status.success) { if (responseD.status.success) {
let result:TaskEvent[] =responseD.data.content.map<TaskEvent>(taskState => { let result: TaskEvent[] = responseD.data.content.map<TaskEvent>(taskState => {
return { return {
start: dayjs(taskState.expectedStartTime).toDate(), start: dayjs(taskState.expectedStartTime).toDate(),
end: dayjs(taskState.expectedEndTime).toDate(), end: dayjs(taskState.expectedEndTime).toDate(),
title: taskState.name, title: taskState.name,
resource: taskState.id, resource: taskState.id,
id:taskState.id, id: taskState.id,
state:taskState.state, state: taskState.state,
priority:taskState.priority priority: taskState.priority
} }
}); });
console.log('responseD.data.content:',result) console.log('responseD.data.content:', result)
setEvents([...result]) setEvents([...result])
} }
}) })
@ -145,14 +146,14 @@ const CalShow: React.FC = () => {
const handleSelectEvent = useCallback( const handleSelectEvent = useCallback(
(event: Event, e: React.SyntheticEvent<HTMLElement>) => { (event: Event, e: React.SyntheticEvent<HTMLElement>) => {
clearClickTimeout() clearClickTimeout()
clickRef.current = window.setTimeout(()=> { clickRef.current = window.setTimeout(() => {
// window.alert(event.title); // window.alert(event.title);
console.log(event) console.log(event)
setOperationId(OPERATION_BUTTON_TYPE.DETAIL) setOperationId(OPERATION_BUTTON_TYPE.DETAIL)
setDescription("任务详情") setDescription("任务详情")
setItemId(event.resource) setItemId(event.resource)
setOpen(true); setOpen(true);
},250) }, 250)
}, },
[] []
) )
@ -166,7 +167,7 @@ const CalShow: React.FC = () => {
const doubleClick = (event: TaskEvent, e: React.SyntheticEvent<HTMLElement>) => { const doubleClick = (event: TaskEvent, e: React.SyntheticEvent<HTMLElement>) => {
clearClickTimeout() clearClickTimeout()
clickRef.current = window.setTimeout(()=>{ clickRef.current = window.setTimeout(() => {
// 数据落库 // 数据落库
commonUpdate({ commonUpdate({
updateColumnList: [{ updateColumnList: [{
@ -185,40 +186,27 @@ const CalShow: React.FC = () => {
const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource); const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource);
const filtered: TaskEvent[] | undefined = prev.filter((ev: TaskEvent) => ev.resource !== event.resource); const filtered: TaskEvent[] | undefined = prev.filter((ev: TaskEvent) => ev.resource !== event.resource);
let result: TaskEvent[] = []; let result: TaskEvent[] = [];
if (existing !== undefined&&filtered !== undefined) { if (existing !== undefined && filtered !== undefined) {
result= [...filtered, {...existing, state:7}]; result = [...filtered, {...existing, state: 7}];
} }
let strings = state.split(","); let strings = state.split(",");
console.log('result',result,strings) console.log('result', result, strings)
return result.filter((ev: TaskEvent) => strings.indexOf(ev.state.toString())>=0); return result.filter((ev: TaskEvent) => strings.indexOf(ev.state.toString()) >= 0);
}) })
},250) }, 250)
} }
const moveEvent = useCallback( const moveEvent = useCallback(
({event, start, end, isAllDay: droppedOnAllDaySlot = false}: EventInteractionArgs<TaskEvent>) => { async ({event, start, end, isAllDay: droppedOnAllDaySlot = false}: EventInteractionArgs<TaskEvent>) => {
console.log("onEventResize || onEventDrop :",start,end) console.log("onEventResize || onEventDrop :", start, end, event)
const {allDay} = event const {allDay} = event
if (!allDay && droppedOnAllDaySlot) { if (!allDay && droppedOnAllDaySlot) {
event.allDay = true event.allDay = true
} }
// 数据落库 editExpectAPI({
commonUpdate({ expectedStartTime: start,
updateColumnList: [{ expectedEndTime: end,
name: 'expectedStartTime', id: event.resource
code: '',
value: start.toLocaleString()
}, {
name: 'expectedEndTime',
code: '',
value: end.toLocaleString()
}],
conditionColumnList: [{
name: 'id',
code: 'id',
operateType: '=',
value: event.resource
}]
}) })
setEvents((prev: TaskEvent[]) => { setEvents((prev: TaskEvent[]) => {
const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource); const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource);
@ -236,51 +224,51 @@ const CalShow: React.FC = () => {
) )
const eventPropGetter = useCallback( const eventPropGetter = useCallback(
(event:TaskEvent) => ({ (event: TaskEvent) => ({
...(event.state===7 ...(event.state === 7
? { className: 'completeTask' } ? {className: 'completeTask'}
: event.priority===3?{ className: 'importantUrgentTask' }: : event.priority === 3 ? {className: 'importantUrgentTask'} :
event.priority===2?{ className: 'importantNotUrgentTask' }: event.priority === 2 ? {className: 'importantNotUrgentTask'} :
event.priority===1?{ className: 'notImportantUrgentTask' }: event.priority === 1 ? {className: 'notImportantUrgentTask'} :
{ className: 'notImportantNotUrgentTask' }), {className: 'notImportantNotUrgentTask'}),
}), }),
[setEvents] [setEvents]
) )
const rangeChange = (rangeLet: Date[]|{ start: Date; end: Date },current?:View|undefined)=>{ const rangeChange = (rangeLet: Date[] | { start: Date; end: Date }, current?: View | undefined) => {
console.log("rangeChange:",rangeLet,(current?current:view)) console.log("rangeChange:", rangeLet, (current ? current : view))
// view 为天的时候类型为数组index:0为当天 // view 为天的时候类型为数组index:0为当天
if ((current?current:view)==="day"&&Array.isArray(rangeLet)) { if ((current ? current : view) === "day" && Array.isArray(rangeLet)) {
if (range.start.valueOf()>rangeLet[0].valueOf()){ if (range.start.valueOf() > rangeLet[0].valueOf()) {
setRange({...range,start:rangeLet[0]}) setRange({...range, start: rangeLet[0]})
}else if (range.end.valueOf()<rangeLet[0].valueOf()){ } else if (range.end.valueOf() < rangeLet[0].valueOf()) {
setRange({...range,end:rangeLet[0]}) setRange({...range, end: rangeLet[0]})
} }
} }
// 为周的时候类型为数组,周一到周日七天 // 为周的时候类型为数组,周一到周日七天
if ((current?current:view)==="week"&&Array.isArray(rangeLet)){ if ((current ? current : view) === "week" && Array.isArray(rangeLet)) {
if (range.start.valueOf()>rangeLet[0].valueOf()){ if (range.start.valueOf() > rangeLet[0].valueOf()) {
setRange({...range,start:rangeLet[0]}) setRange({...range, start: rangeLet[0]})
} }
if (range.end.valueOf()<rangeLet[6].valueOf()){ if (range.end.valueOf() < rangeLet[6].valueOf()) {
setRange({...range,end:rangeLet[6]}) setRange({...range, end: rangeLet[6]})
} }
} }
// 为周的时候类型为对象 // 为周的时候类型为对象
if ((current?current:view)==="month"&& rangeLet && !Array.isArray(rangeLet)){ if ((current ? current : view) === "month" && rangeLet && !Array.isArray(rangeLet)) {
if (range.start.valueOf()>rangeLet.start.valueOf()){ if (range.start.valueOf() > rangeLet.start.valueOf()) {
setRange({...range,start:rangeLet.start}) setRange({...range, start: rangeLet.start})
} }
if (range.end.valueOf()<rangeLet.end.valueOf()){ if (range.end.valueOf() < rangeLet.end.valueOf()) {
setRange({...range,end:rangeLet.end}) setRange({...range, end: rangeLet.end})
} }
} }
} }
return <div className="App" style={{height: '90vh'}}> return <div className="App" style={{height: '90vh'}}>
{open && <DetailModelForm operationId={operationId} description={description} open={open} haveButton={false} {open && <DetailModelForm operationId={operationId} description={description} open={open} haveButton={false}
itemId={itemId} pid={pid?pid:'0'} itemId={itemId}
reloadData={reloadData} expectedStartTime={expectedStartTime} reloadData={reloadData} expectedStartTime={expectedStartTime}
expectedEndTime={expectedEndTime}/>} expectedEndTime={expectedEndTime}/>}
<DragAndDropCalendar <DragAndDropCalendar
// 本地设置 // 本地设置
localizer={localizer} localizer={localizer}

View File

@ -43,7 +43,7 @@ export type PidSelectTree = { label: string; value: string; pid: string; childre
export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => { export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
console.log("DetailModelForm:props:", props) console.log("DetailModelForm:props:", props)
const [form] = Form.useForm<DataType>(); const [form] = Form.useForm<DataType>();
const [pid, setPid] = useState<string>(props.pid ? props.pid : '0'); const [requestTask,setRequestTask]=useState<DataType>()
const [editFormDisable, setEditFormDisable] = useState(props.operationId === OPERATION_BUTTON_TYPE.DETAIL) const [editFormDisable, setEditFormDisable] = useState(props.operationId === OPERATION_BUTTON_TYPE.DETAIL)
// 团队第一层 pid必须为0 // 团队第一层 pid必须为0
const [taskType, setTaskType] = useState('0') const [taskType, setTaskType] = useState('0')
@ -61,6 +61,10 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
task.data.expectedTimeRange = [task.data.expectedStartTime ? dayjs(task.data.expectedStartTime) : undefined, task.data.expectedTimeRange = [task.data.expectedStartTime ? dayjs(task.data.expectedStartTime) : undefined,
task.data.expectedEndTime ? dayjs(task.data.expectedEndTime) : undefined]; task.data.expectedEndTime ? dayjs(task.data.expectedEndTime) : undefined];
form.setFieldsValue(task.data) form.setFieldsValue(task.data)
if (task.data.pid=="0"){
form.setFieldValue("pid",undefined)
}
setRequestTask(task.data)
console.log("form.setFieldsValue(task.data)" + JSON.stringify(task.data)) console.log("form.setFieldsValue(task.data)" + JSON.stringify(task.data))
} else { } else {
message.error(task.status.message); message.error(task.status.message);
@ -89,6 +93,10 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
} }
// 如果不是添加任务需要回显 // 如果不是添加任务需要回显
// Form 当中的 initialValues
// ProComponents 底层也是封装的 antd ,所以用法也和 antd 相同。注意 initialValues 不能被 setState 动态更新,
// 所以你需要用 setFieldsValue 来更新。 initialValues 只在 form 初始化时生效且只生效一次,如果你需要异步加载,
// 推荐使用 request或者 initialValues ? <Form/> : null
return ( return (
<ModalForm<DataType> <ModalForm<DataType>
title={props.description} title={props.description}
@ -145,13 +153,17 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
</Button> </Button>
</Popconfirm> : undefined </Popconfirm> : undefined
, ,
<DiaryOption/>, requestTask&&requestTask.id?<DiaryOption taskId={requestTask.id} taskName={requestTask.name}/>:undefined,
...defaultDoms ...defaultDoms
]; ];
}, },
} : undefined} } : undefined}
onFinish={async (values) => { onFinish={async (values) => {
console.log('Received values of form: ', values); console.log('Received values of form: ', values,{...requestTask,...values});
if (requestTask) {
const {sortNo}=requestTask;
values.sortNo=sortNo;
}
if (values.pid === undefined) { if (values.pid === undefined) {
values.pid = '0' values.pid = '0'
} }
@ -256,11 +268,14 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
{ {
pageSize: 1000, pageSize: 1000,
pageNumber: 1, pageNumber: 1,
data: [{code: 'pid', value: '0', operateType: '='}, { data: [{code: 'pid', value: '0', operateType: '='},
code: 'state', // 如果父任务完成会导致父任务不展示
value: '8,9', // {
operateType: 'IN' // code: 'state',
}, {code: '', value: true, operateType: "TREE"}] // value: '8,9',
// operateType: 'IN'
// },
{code: '', value: true, operateType: "TREE"}]
} }
)).then(result => childReduce(result.data.content)) )).then(result => childReduce(result.data.content))
}} }}
@ -269,8 +284,8 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
fieldProps={{ fieldProps={{
onSelect: (e, node) => { onSelect: (e, node) => {
console.log('onSelect', e, node); console.log('onSelect', e, node);
setPid(e) // setPid(e)
} },
}} }}
disabled={editFormDisable} disabled={editFormDisable}
/> />

View File

@ -38,6 +38,8 @@ httpReq.interceptors.response.use(
message.error('系统异常'); message.error('系统异常');
} else if (response.status >= 400 && response.status <= 500) { } else if (response.status >= 400 && response.status <= 500) {
message.warning('无权限'); message.warning('无权限');
}else if(response.data.status.success == false){
message.error(response.data.status.message);
} }
return response; return response;
}, },