286 lines
7.8 KiB
Vue
286 lines
7.8 KiB
Vue
<script setup>
|
||
import {NSpace, NButton, NModal, NInput, NSelect, useMessage, NSpin, NPopconfirm} from 'naive-ui'
|
||
import {onMounted, ref} from "vue";
|
||
import {useRouter} from 'vue-router';
|
||
import DeveloperList from "../components/developer/DeveloperList.vue";
|
||
import ProjectList from "../components/project/ProjectList.vue";
|
||
import RequirementList from "../components/requirement/RequirementList.vue";
|
||
import {errorHandler, request} from "../utils/request.js";
|
||
import {marked} from "marked";
|
||
|
||
const message = useMessage();
|
||
const router = useRouter();
|
||
const systemTitle = ref(import.meta.env.VITE_SYSTEM_TITLE)
|
||
const systemCopyright = ref(import.meta.env.VITE_SYSTEM_COPYRIGHT)
|
||
const showDeveloperListModal = ref(false)
|
||
const showProjectListModal = ref(false)
|
||
const showRequirementListModal = ref(false)
|
||
|
||
const mainContentRef = ref(null)
|
||
const promptTextareaHeight = ref('100px')
|
||
const responseChatContentHeight = ref('100px')
|
||
|
||
const prompt = ref(null)
|
||
|
||
const requirement_id = ref(null)
|
||
const requirementOptions = ref([])
|
||
|
||
// 是否对话中
|
||
const isChatting = ref(false)
|
||
// 是否对话结束
|
||
const isChatDone = ref(true)
|
||
const chatResponse = ref(``)
|
||
|
||
const listRequirement = () => {
|
||
request.get('/requirement/list').then(res => {
|
||
requirementOptions.value = res.data.map(requirement => {
|
||
return {
|
||
label: requirement.title,
|
||
value: requirement.id.toString()
|
||
}
|
||
})
|
||
}).catch(error => {
|
||
errorHandler(error, message, router)
|
||
})
|
||
}
|
||
|
||
onMounted(() => {
|
||
setTimeout(() => {
|
||
const mainContentHeight = parseInt(mainContentRef.value.getBoundingClientRect().height)
|
||
promptTextareaHeight.value = (mainContentHeight - 150) + 'px'
|
||
responseChatContentHeight.value = (mainContentHeight - 100) + 'px'
|
||
}, 500)
|
||
listRequirement();
|
||
})
|
||
|
||
/**
|
||
* 提交内容
|
||
*/
|
||
const onChatSubmit = () => {
|
||
isChatting.value = true
|
||
isChatDone.value = false
|
||
request.post('/chat/work-score', {
|
||
prompt: prompt.value,
|
||
requirement_id: requirement_id.value
|
||
}).then(res => {
|
||
chatResponse.value = res.data.data
|
||
}).catch(error => {
|
||
errorHandler(error, message, router)
|
||
}).finally(() => {
|
||
isChatDone.value = true
|
||
})
|
||
}
|
||
|
||
const logout = () => {
|
||
sessionStorage.removeItem('access_token');
|
||
router.replace('/login')
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="main-layout">
|
||
<header class="main-header">
|
||
<div class="logo">{{systemTitle}}</div>
|
||
<div class="header-actions">
|
||
<n-space>
|
||
<n-button style="background-color: #FFF" @click="showDeveloperListModal = true">
|
||
<span>研发管理</span>
|
||
</n-button>
|
||
<n-button type="primary" @click="showProjectListModal = true">
|
||
<span>项目管理</span>
|
||
</n-button>
|
||
<n-button type="info" @click="showRequirementListModal = true">
|
||
<span>需求管理</span>
|
||
</n-button>
|
||
<n-popconfirm
|
||
positive-text="确认"
|
||
negative-text="取消"
|
||
@positive-click="logout"
|
||
>
|
||
<template #trigger>
|
||
<n-button strong secondary type="success" round>退出系统</n-button>
|
||
</template>
|
||
确认退出系统吗?
|
||
</n-popconfirm>
|
||
</n-space>
|
||
</div>
|
||
</header>
|
||
<main class="main-content" ref="mainContentRef">
|
||
<div class="chat-container" v-show="!isChatting">
|
||
<div class="title">请输入需求相关代码</div>
|
||
<div class="chat">
|
||
<div class="prompt">
|
||
<n-input
|
||
v-model:value="prompt"
|
||
:show-count="true"
|
||
ref="promptTextareaRef"
|
||
type="textarea"
|
||
placeholder="请输入需求描述"
|
||
:style="{
|
||
height: promptTextareaHeight
|
||
}"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div class="actions">
|
||
<n-space align="center" style="width: 750px;">
|
||
选择需求
|
||
<n-select v-model:value="requirement_id"
|
||
:options="requirementOptions"
|
||
placeholder="请选择需求"
|
||
clearable
|
||
style="width: 200px"/>
|
||
</n-space>
|
||
<n-space>
|
||
<n-button type="primary"
|
||
circle
|
||
:disabled="!prompt && !requirement_id"
|
||
@click="onChatSubmit"
|
||
>
|
||
<i class="fas fa-arrow-up"></i>
|
||
</n-button>
|
||
</n-space>
|
||
</div>
|
||
</div>
|
||
<div class="chat-response" v-show="isChatting">
|
||
<div class="content markdown-content"
|
||
:style="{height: responseChatContentHeight}">
|
||
<div v-html="marked(chatResponse)"></div>
|
||
</div>
|
||
<div class="actions">
|
||
<n-space justify="space-between" align="center">
|
||
<n-space align="center" v-if="!isChatDone">
|
||
<n-spin size="small"/>
|
||
<span style="color: var(--success-color)">AI正在分析</span>
|
||
</n-space>
|
||
<n-space v-if="isChatDone"
|
||
style="color: var(--primary-color)">分析结束
|
||
</n-space>
|
||
<n-button type="primary"
|
||
circle
|
||
:disabled="!isChatDone"
|
||
@click="() => {
|
||
isChatting = false
|
||
chatResponse = ''
|
||
}"
|
||
>
|
||
<i class="fas fa-arrow-left"></i>
|
||
</n-button>
|
||
</n-space>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
<footer class="main-footer">
|
||
<p>{{systemCopyright}}</p>
|
||
</footer>
|
||
<n-modal v-model:show="showDeveloperListModal"
|
||
preset="card"
|
||
:style="{width: '1000px', height: responseChatContentHeight}"
|
||
title="研发管理"
|
||
size="small"
|
||
:bordered="false">
|
||
<DeveloperList/>
|
||
</n-modal>
|
||
<n-modal v-model:show="showProjectListModal"
|
||
preset="card"
|
||
:style="{width: '1000px', height: responseChatContentHeight}"
|
||
title="项目管理"
|
||
size="small"
|
||
:bordered="false">
|
||
<ProjectList/>
|
||
</n-modal>
|
||
<n-modal v-model:show="showRequirementListModal"
|
||
preset="card"
|
||
:style="{width: '1000px', height: responseChatContentHeight}"
|
||
title="需求管理"
|
||
size="small"
|
||
:bordered="false"
|
||
@close="() => {
|
||
listRequirement()
|
||
}">
|
||
<RequirementList/>
|
||
</n-modal>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.main-layout {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
|
||
.main-header {
|
||
flex-shrink: 0;
|
||
height: 60px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 10px 20px;
|
||
background-color: #f5f5f5;
|
||
|
||
.logo {
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
color: var(--primary-color)
|
||
}
|
||
}
|
||
|
||
.main-content {
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
.chat-container {
|
||
padding: 15px;
|
||
border-radius: 10px;
|
||
border: 1px solid var(--border-color);
|
||
|
||
.title {
|
||
font-size: 20px;
|
||
text-align: center;
|
||
}
|
||
|
||
.chat {
|
||
margin-top: 10px;
|
||
width: 800px;
|
||
}
|
||
|
||
.actions {
|
||
margin-top: 10px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.chat-response {
|
||
width: 800px;
|
||
padding: 15px;
|
||
border-radius: 10px;
|
||
border: 1px solid var(--border-color);
|
||
|
||
.content {
|
||
overflow: auto;
|
||
|
||
table {
|
||
background-color: black;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
.actions {
|
||
margin-top: 15px;
|
||
}
|
||
}
|
||
|
||
.main-footer {
|
||
flex-shrink: 0;
|
||
height: 30px;
|
||
line-height: 30px;
|
||
background-color: #f5f5f5;
|
||
text-align: center;
|
||
}
|
||
}
|
||
</style> |