@yuku123/z-project-frontend-component 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/z-project-frontend-component.es.js +3419 -0
- package/dist/z-project-frontend-component.umd.js +51 -0
- package/package.json +44 -0
- package/src/index.js +4 -0
- package/src/pages/ProjectApp.jsx +18 -0
- package/src/pages/ProjectCreate.jsx +197 -0
- package/src/pages/ProjectDetail.jsx +208 -0
- package/src/pages/ProjectList.jsx +122 -0
- package/src/services/api.js +76 -0
- package/src/utils/request.js +5 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {useEffect, useRef, useState} from 'react'
|
|
2
|
+
import {useNavigate, useParams} from 'react-router-dom'
|
|
3
|
+
import {Alert, Button, Card, Empty, Input, message, Popconfirm, Space, Spin, Statistic, Tag} from 'antd'
|
|
4
|
+
import {ApiOutlined, ArrowLeftOutlined, CheckCircleOutlined, RobotOutlined, SendOutlined, UserOutlined} from '@ant-design/icons'
|
|
5
|
+
import request from '../utils/request'
|
|
6
|
+
|
|
7
|
+
const ROLE_META = {
|
|
8
|
+
USER: {color: 'blue', icon: <UserOutlined/>, label: 'CEO'},
|
|
9
|
+
ASSISTANT: {color: 'purple', icon: <RobotOutlined/>, label: 'Agent'},
|
|
10
|
+
SYSTEM: {color: 'default', icon: null, label: '系统'},
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const STATUS_TAG = {
|
|
14
|
+
OPEN: {color: 'blue', text: '待开始'},
|
|
15
|
+
IN_PROGRESS: {color: 'green', text: '进行中'},
|
|
16
|
+
COMPLETED: {color: 'default', text: '已完成'},
|
|
17
|
+
ARCHIVED: {color: 'default', text: '已归档'},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ProjectDetail() {
|
|
21
|
+
const {id} = useParams()
|
|
22
|
+
const navigate = useNavigate()
|
|
23
|
+
const [project, setProject] = useState(null)
|
|
24
|
+
const [messages, setMessages] = useState([])
|
|
25
|
+
const [input, setInput] = useState('')
|
|
26
|
+
const [sending, setSending] = useState(false)
|
|
27
|
+
const chatRef = useRef(null)
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (id) loadAll()
|
|
31
|
+
}, [id])
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
// 滚到底
|
|
35
|
+
if (chatRef.current) {
|
|
36
|
+
chatRef.current.scrollTop = chatRef.current.scrollHeight
|
|
37
|
+
}
|
|
38
|
+
}, [messages])
|
|
39
|
+
|
|
40
|
+
const loadAll = async () => {
|
|
41
|
+
try {
|
|
42
|
+
const [p, m] = await Promise.all([
|
|
43
|
+
request.get(`/project/${id}`),
|
|
44
|
+
request.get(`/project/${id}/messages`),
|
|
45
|
+
])
|
|
46
|
+
setProject(p?.data ?? p)
|
|
47
|
+
setMessages(Array.isArray(m?.data ?? m) ? (m?.data ?? m) : [])
|
|
48
|
+
} catch (e) {
|
|
49
|
+
message.error('加载项目失败')
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const handleSend = async () => {
|
|
54
|
+
if (!input.trim()) return
|
|
55
|
+
const text = input.trim()
|
|
56
|
+
setInput('')
|
|
57
|
+
setSending(true)
|
|
58
|
+
// 乐观: 先把 user 消息渲染出去
|
|
59
|
+
const tempUser = {seq: -1, role: 'USER', content: text, createdAt: new Date().toISOString()}
|
|
60
|
+
setMessages(prev => [...prev, tempUser])
|
|
61
|
+
try {
|
|
62
|
+
const res = await request.post(`/project/${id}/chat`, {message: text})
|
|
63
|
+
if (res && res.success !== false) {
|
|
64
|
+
const r = res.data ?? res
|
|
65
|
+
const a = r.message
|
|
66
|
+
setMessages(prev => {
|
|
67
|
+
// 去掉临时的 user, 换成后端返回的完整序列
|
|
68
|
+
const filtered = prev.filter(m => m.seq !== -1)
|
|
69
|
+
return [...filtered, ...(a ? [a] : [])]
|
|
70
|
+
})
|
|
71
|
+
// 重新拉一次保证 seq 对齐
|
|
72
|
+
setTimeout(loadAll, 100)
|
|
73
|
+
} else {
|
|
74
|
+
message.error(res?.message || '发送失败')
|
|
75
|
+
}
|
|
76
|
+
} catch (e) {
|
|
77
|
+
message.error('发送失败: ' + (e?.message || ''))
|
|
78
|
+
} finally {
|
|
79
|
+
setSending(false)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const handleComplete = async () => {
|
|
84
|
+
try {
|
|
85
|
+
const res = await request.post(`/project/${id}/status?status=COMPLETED`)
|
|
86
|
+
if (res && res.success !== false) {
|
|
87
|
+
message.success('已标记完成')
|
|
88
|
+
loadAll()
|
|
89
|
+
}
|
|
90
|
+
} catch (e) {
|
|
91
|
+
message.error('操作失败')
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!project) return <div style={{padding: 24}}><Spin/></div>
|
|
96
|
+
|
|
97
|
+
const status = STATUS_TAG[project.status] || {color: 'default', text: project.status}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div style={{padding: 24, height: 'calc(100vh - 64px)', display: 'flex', flexDirection: 'column', gap: 16}}>
|
|
101
|
+
{/* 头部 */}
|
|
102
|
+
<Card
|
|
103
|
+
title={
|
|
104
|
+
<Space>
|
|
105
|
+
<Button type="text" icon={<ArrowLeftOutlined/>} onClick={() => navigate('/project/list')}/>
|
|
106
|
+
<ApiOutlined/>
|
|
107
|
+
<span style={{fontWeight: 500}}>{project.name}</span>
|
|
108
|
+
<Tag color={status.color}>{status.text}</Tag>
|
|
109
|
+
</Space>
|
|
110
|
+
}
|
|
111
|
+
extra={
|
|
112
|
+
<Space>
|
|
113
|
+
{project.contextSize > 0 && <Tag color="blue">上下文 {project.contextSize} B</Tag>}
|
|
114
|
+
<Tag>{project.agentRole}</Tag>
|
|
115
|
+
{project.modelCode && <Tag color="purple">{project.modelCode}</Tag>}
|
|
116
|
+
{project.status !== 'COMPLETED' && project.status !== 'ARCHIVED' && (
|
|
117
|
+
<Popconfirm title="标记完成?" onConfirm={handleComplete}>
|
|
118
|
+
<Button icon={<CheckCircleOutlined/>}>标记完成</Button>
|
|
119
|
+
</Popconfirm>
|
|
120
|
+
)}
|
|
121
|
+
</Space>
|
|
122
|
+
}
|
|
123
|
+
>
|
|
124
|
+
{project.description && <div style={{color: '#666', marginBottom: 8}}>{project.description}</div>}
|
|
125
|
+
<Space size="large">
|
|
126
|
+
<Statistic title="消息数" value={project.messageCount || 0}/>
|
|
127
|
+
{project.sourceSubscriptionId && (
|
|
128
|
+
<Statistic title="来源订阅" value={`#${project.sourceSubscriptionId}`}/>
|
|
129
|
+
)}
|
|
130
|
+
<Statistic title="立项" value={project.createdAt ? new Date(project.createdAt).toLocaleString() : '-'}/>
|
|
131
|
+
</Space>
|
|
132
|
+
</Card>
|
|
133
|
+
|
|
134
|
+
{/* 聊天区 */}
|
|
135
|
+
<Card style={{flex: 1, display: 'flex', flexDirection: 'column'}}
|
|
136
|
+
bodyStyle={{flex: 1, display: 'flex', flexDirection: 'column', padding: 0}}>
|
|
137
|
+
<div ref={chatRef} style={{flex: 1, overflow: 'auto', padding: 16, background: '#fafafa'}}>
|
|
138
|
+
{messages.length === 0 ? (
|
|
139
|
+
<Empty description="还没有消息, 跟 Agent 说点什么?"/>
|
|
140
|
+
) : (
|
|
141
|
+
<Space orientation="vertical" style={{width: '100%'}} size="middle">
|
|
142
|
+
{messages.map(m => {
|
|
143
|
+
const meta = ROLE_META[m.role] || ROLE_META.SYSTEM
|
|
144
|
+
const isUser = m.role === 'USER'
|
|
145
|
+
return (
|
|
146
|
+
<div key={m.id || m.seq} style={{
|
|
147
|
+
display: 'flex',
|
|
148
|
+
justifyContent: isUser ? 'flex-end' : 'flex-start',
|
|
149
|
+
}}>
|
|
150
|
+
<div style={{
|
|
151
|
+
maxWidth: '70%',
|
|
152
|
+
background: isUser ? '#1677ff' : '#fff',
|
|
153
|
+
color: isUser ? '#fff' : '#000',
|
|
154
|
+
border: isUser ? 'none' : '1px solid #e8e8e8',
|
|
155
|
+
borderRadius: 8,
|
|
156
|
+
padding: '10px 14px',
|
|
157
|
+
boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
|
|
158
|
+
}}>
|
|
159
|
+
<div style={{fontSize: 12, opacity: 0.7, marginBottom: 4}}>
|
|
160
|
+
<Space size={4}>
|
|
161
|
+
{meta.icon}
|
|
162
|
+
{meta.label}
|
|
163
|
+
{m.seq > 0 && <span>#{m.seq}</span>}
|
|
164
|
+
{m.latencyMs > 0 && <span>· {m.latencyMs}ms</span>}
|
|
165
|
+
{m.modelCode && <span>· {m.modelCode}</span>}
|
|
166
|
+
</Space>
|
|
167
|
+
</div>
|
|
168
|
+
<div style={{whiteSpace: 'pre-wrap', wordBreak: 'break-word'}}>
|
|
169
|
+
{m.content}
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
)
|
|
174
|
+
})}
|
|
175
|
+
{sending && (
|
|
176
|
+
<div style={{display: 'flex', justifyContent: 'flex-start'}}>
|
|
177
|
+
<div style={{background: '#fff', border: '1px solid #e8e8e8', borderRadius: 8, padding: '10px 14px'}}>
|
|
178
|
+
<Spin size="small"/> 思考中...
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
)}
|
|
182
|
+
</Space>
|
|
183
|
+
)}
|
|
184
|
+
</div>
|
|
185
|
+
{project.status === 'COMPLETED' || project.status === 'ARCHIVED' ? (
|
|
186
|
+
<Alert style={{margin: 12, marginBottom: 0}}
|
|
187
|
+
type="info" message="项目已完成 / 归档, 不再接受新消息"
|
|
188
|
+
showIcon/>
|
|
189
|
+
) : (
|
|
190
|
+
<div style={{borderTop: '1px solid #f0f0f0', padding: 12, display: 'flex', gap: 8}}>
|
|
191
|
+
<Input.TextArea
|
|
192
|
+
value={input}
|
|
193
|
+
onChange={e => setInput(e.target.value)}
|
|
194
|
+
onPressEnter={e => { if (!e.shiftKey) { e.preventDefault(); handleSend() } }}
|
|
195
|
+
placeholder="输入消息, Enter 发送 (Shift+Enter 换行)"
|
|
196
|
+
autoSize={{minRows: 1, maxRows: 6}}
|
|
197
|
+
disabled={sending}
|
|
198
|
+
/>
|
|
199
|
+
<Button type="primary" icon={<SendOutlined/>} loading={sending} onClick={handleSend}
|
|
200
|
+
disabled={!input.trim()}>发送</Button>
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
</Card>
|
|
204
|
+
</div>
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export default ProjectDetail
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {useEffect, useState} from 'react'
|
|
2
|
+
import {useNavigate} from 'react-router-dom'
|
|
3
|
+
import {Button, Card, message, Popconfirm, Space, Table, Tabs, Tag} from 'antd'
|
|
4
|
+
import {ApiOutlined, CheckCircleOutlined, PlusOutlined, ReloadOutlined} from '@ant-design/icons'
|
|
5
|
+
import request from '../utils/request'
|
|
6
|
+
|
|
7
|
+
const STATUS_TAG = {
|
|
8
|
+
OPEN: {color: 'blue', text: '待开始'},
|
|
9
|
+
IN_PROGRESS: {color: 'green', text: '进行中'},
|
|
10
|
+
COMPLETED: {color: 'default', text: '已完成'},
|
|
11
|
+
ARCHIVED: {color: 'default', text: '已归档'},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function ProjectList() {
|
|
15
|
+
const navigate = useNavigate()
|
|
16
|
+
const [tab, setTab] = useState('mine')
|
|
17
|
+
const [data, setData] = useState([])
|
|
18
|
+
const [loading, setLoading] = useState(false)
|
|
19
|
+
|
|
20
|
+
const fetchData = async () => {
|
|
21
|
+
setLoading(true)
|
|
22
|
+
try {
|
|
23
|
+
const url = tab === 'mine'
|
|
24
|
+
? '/project/list/mine?limit=100'
|
|
25
|
+
: `/project/list/by-status?status=${tab}&limit=100`
|
|
26
|
+
const res = await request.get(url)
|
|
27
|
+
const list = res?.data ?? res ?? []
|
|
28
|
+
setData(Array.isArray(list) ? list : [])
|
|
29
|
+
} catch (e) {
|
|
30
|
+
message.error('加载项目失败: ' + (e?.message || ''))
|
|
31
|
+
} finally {
|
|
32
|
+
setLoading(false)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
fetchData()
|
|
38
|
+
}, [tab])
|
|
39
|
+
|
|
40
|
+
const handleComplete = async (id) => {
|
|
41
|
+
try {
|
|
42
|
+
const res = await request.post(`/project/${id}/status?status=COMPLETED`)
|
|
43
|
+
if (res && res.success !== false) {
|
|
44
|
+
message.success('已标记完成')
|
|
45
|
+
fetchData()
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
message.error('操作失败: ' + (e?.message || ''))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const columns = [
|
|
53
|
+
{title: 'ID', dataIndex: 'id', width: 60},
|
|
54
|
+
{title: '名称', dataIndex: 'name', render: (n, r) => (
|
|
55
|
+
<Space orientation="vertical" size={0}>
|
|
56
|
+
<a onClick={() => navigate(`/project/${r.id}`)} style={{fontWeight: 500}}>{n}</a>
|
|
57
|
+
{r.description && <span style={{color: '#999', fontSize: 12}}>{r.description}</span>}
|
|
58
|
+
</Space>
|
|
59
|
+
)},
|
|
60
|
+
{title: '来源', dataIndex: 'sourceType', width: 100, render: (t) => <Tag>{t}</Tag>},
|
|
61
|
+
{title: '订阅', dataIndex: 'sourceSubscriptionId', width: 80,
|
|
62
|
+
render: (v) => v ? `#${v}` : <span style={{color: '#ccc'}}>-</span>},
|
|
63
|
+
{title: '状态', dataIndex: 'status', width: 100, render: (s) => {
|
|
64
|
+
const m = STATUS_TAG[s] || {color: 'default', text: s}
|
|
65
|
+
return <Tag color={m.color}>{m.text}</Tag>
|
|
66
|
+
}},
|
|
67
|
+
{title: '消息数', dataIndex: 'messageCount', width: 80},
|
|
68
|
+
{title: '上下文', width: 130, render: (_, r) => (
|
|
69
|
+
<Space size={4}>
|
|
70
|
+
<Tag color="blue">{r.contextSize || 0}B</Tag>
|
|
71
|
+
</Space>
|
|
72
|
+
)},
|
|
73
|
+
{title: '最近活动', dataIndex: 'lastMessageAt', width: 150,
|
|
74
|
+
render: t => t ? new Date(t).toLocaleString() : <span style={{color: '#ccc'}}>-</span>},
|
|
75
|
+
{title: '操作', width: 160, render: (_, r) => (
|
|
76
|
+
<Space size={4}>
|
|
77
|
+
<Button type="link" size="small" onClick={() => navigate(`/project/${r.id}`)}>对话</Button>
|
|
78
|
+
{r.status !== 'COMPLETED' && r.status !== 'ARCHIVED' && (
|
|
79
|
+
<Popconfirm title="标记为已完成?" onConfirm={() => handleComplete(r.id)}>
|
|
80
|
+
<Button type="link" size="small" icon={<CheckCircleOutlined/>}>完成</Button>
|
|
81
|
+
</Popconfirm>
|
|
82
|
+
)}
|
|
83
|
+
</Space>
|
|
84
|
+
)},
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div style={{padding: 24}}>
|
|
89
|
+
<Card
|
|
90
|
+
title={<Space><ApiOutlined/>项目中心 (CEO 立项 + Agent 对话)</Space>}
|
|
91
|
+
extra={
|
|
92
|
+
<Space>
|
|
93
|
+
<Button icon={<ReloadOutlined/>} onClick={fetchData}>刷新</Button>
|
|
94
|
+
<Button type="primary" icon={<PlusOutlined/>}
|
|
95
|
+
onClick={() => navigate('/project/new')}>新建项目</Button>
|
|
96
|
+
</Space>
|
|
97
|
+
}
|
|
98
|
+
>
|
|
99
|
+
<Tabs
|
|
100
|
+
activeKey={tab}
|
|
101
|
+
onChange={setTab}
|
|
102
|
+
items={[
|
|
103
|
+
{key: 'mine', label: '我的进行中'},
|
|
104
|
+
{key: 'OPEN', label: '待开始'},
|
|
105
|
+
{key: 'IN_PROGRESS', label: '进行中'},
|
|
106
|
+
{key: 'COMPLETED', label: '已完成'},
|
|
107
|
+
]}
|
|
108
|
+
/>
|
|
109
|
+
<Table
|
|
110
|
+
rowKey="id"
|
|
111
|
+
columns={columns}
|
|
112
|
+
dataSource={data}
|
|
113
|
+
loading={loading}
|
|
114
|
+
size="middle"
|
|
115
|
+
pagination={{pageSize: 20}}
|
|
116
|
+
/>
|
|
117
|
+
</Card>
|
|
118
|
+
</div>
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default ProjectList
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 兼容垫片: 之前在 z-opc-main-starter-frontend/services/ 里的所有 axios 客户端
|
|
3
|
+
* 这里直接 re-export z-frontend-common 的 utils/request, 并提供 makeApi 工具.
|
|
4
|
+
*/
|
|
5
|
+
import request, {authRequest, setupInterceptors} from '../utils/request'
|
|
6
|
+
export {request as default, authRequest, setupInterceptors}
|
|
7
|
+
|
|
8
|
+
function makeApi(name) {
|
|
9
|
+
return {
|
|
10
|
+
list: (params) => request.get(`/${name}/list`, {params}).then(r => r.data),
|
|
11
|
+
page: (params) => request.get(`/${name}/page`, {params}).then(r => r.data),
|
|
12
|
+
get: (id) => request.get(`/${name}/${id}`).then(r => r.data),
|
|
13
|
+
create: (data) => request.post(`/${name}`, data).then(r => r.data),
|
|
14
|
+
update: (id, data) => request.put(`/${name}/${id}`, data).then(r => r.data),
|
|
15
|
+
delete: (id) => request.delete(`/${name}/${id}`).then(r => r.data),
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const configApi = makeApi('config')
|
|
20
|
+
export const approvalApi = makeApi('approval')
|
|
21
|
+
export const designerApi = makeApi('designer')
|
|
22
|
+
export const mcpApi = makeApi('mcp')
|
|
23
|
+
export const mockPlatformApi = makeApi('mock')
|
|
24
|
+
export const namingApi = makeApi('naming')
|
|
25
|
+
export const opsApi = makeApi('ops')
|
|
26
|
+
export const ossApi = makeApi('oss')
|
|
27
|
+
export const scriptApi = makeApi('script')
|
|
28
|
+
export const skillApi = makeApi('skill')
|
|
29
|
+
export const agentApi = makeApi('agent')
|
|
30
|
+
export const flowApi = makeApi('flow')
|
|
31
|
+
export const traceApi = makeApi('trace')
|
|
32
|
+
export const llmApi = makeApi('llm')
|
|
33
|
+
export const productApi = makeApi('product')
|
|
34
|
+
export const sceneApi = makeApi('scene')
|
|
35
|
+
export const ctcAcAccountApi = makeApi('ctc/ac/accounts')
|
|
36
|
+
export const ctcAcLoginLogApi = makeApi('ctc/ac/login-log')
|
|
37
|
+
export const ctcAcTenantApi = makeApi('ctc/ac/tenants')
|
|
38
|
+
export const ctcAcDomainApi = makeApi('ctc/ac/domains')
|
|
39
|
+
export const ctcAcOrgApi = makeApi('ctc/ac/orgs')
|
|
40
|
+
export const ctcAcDeptApi = makeApi('ctc/ac/depts')
|
|
41
|
+
export const ctcAcGroupApi = makeApi('ctc/ac/groups')
|
|
42
|
+
export const ctcAuthorizationApi = makeApi('ctc/authorization')
|
|
43
|
+
export const metaAppApi = makeApi('meta-app')
|
|
44
|
+
export const jobApi = makeApi('schedule/job')
|
|
45
|
+
export const webideApi = makeApi('webide')
|
|
46
|
+
export const privateConfigApi = makeApi('private-config')
|
|
47
|
+
export const ctcSurlApi = makeApi('ctc/surl')
|
|
48
|
+
export const ctcUserApi = makeApi('ctc/user')
|
|
49
|
+
export const agentTeamApi = makeApi('agent/team')
|
|
50
|
+
export const workspaceApi = makeApi('agent/team/workspace')
|
|
51
|
+
|
|
52
|
+
// === 项目 / 任务 / 鉴权 ===
|
|
53
|
+
export const login = (data) => request.post('/ctc/auth/login', data).then(r => r.data)
|
|
54
|
+
export const getCurrentUser = () => {
|
|
55
|
+
try { return Promise.resolve(JSON.parse(localStorage.getItem('userInfo') || 'null')) }
|
|
56
|
+
catch { return Promise.resolve(null) }
|
|
57
|
+
}
|
|
58
|
+
export const switchTenant = (tenantCode, domainCode) =>
|
|
59
|
+
authRequest.post('/ctc/auth/switch-tenant', {tenantCode: tenantCode || '', domainCode: domainCode || ''}).then(r => r.data)
|
|
60
|
+
export const listMyProjects = () => authRequest.get('/project/user/list').then(r => r.data)
|
|
61
|
+
export const createProject = (data) => request.post('/project', data).then(r => r.data)
|
|
62
|
+
export const updateProject = (id, data) => request.put(`/project/${id}`, data).then(r => r.data)
|
|
63
|
+
export const archiveProject = (id) => request.put(`/project/${id}/archive`).then(r => r.data)
|
|
64
|
+
export const listTasksByProject = (projectId) => request.get('/task/project/list', {params: {projectId}}).then(r => r.data)
|
|
65
|
+
export const createTask = (data) => request.post('/task', data).then(r => r.data)
|
|
66
|
+
export const completeTask = (id) => request.post('/task/complete', null, {params: {taskId: id}}).then(r => r.data)
|
|
67
|
+
export const moveTask = (taskId, targetListId, position) =>
|
|
68
|
+
request.put('/task/move', null, {params: {taskId, targetListId, position}}).then(r => r.data)
|
|
69
|
+
export const listTasksByList = (listId) => request.get('/task/list', {params: {listId}}).then(r => r.data)
|
|
70
|
+
export const getDomainByTenantCode = async () => [{domainCode: 'default', domainName: '默认域'}]
|
|
71
|
+
export const getTenantList = async () => []
|
|
72
|
+
export const getDynamicMenu = async () => []
|
|
73
|
+
export const userOrgRelApi = {
|
|
74
|
+
usersByDept: () => Promise.resolve([]),
|
|
75
|
+
usersByGroup: () => Promise.resolve([]),
|
|
76
|
+
}
|