220 lines
7.5 KiB
Vue
220 lines
7.5 KiB
Vue
<script setup>
|
|
import { ref } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { createProject } from '../api'
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
const router = useRouter()
|
|
|
|
const form = ref({
|
|
name: '',
|
|
description: '',
|
|
mode: 'git',
|
|
git_url: '',
|
|
git_branch: 'master',
|
|
git_username: '',
|
|
git_password: '',
|
|
build_script: '',
|
|
run_command: '',
|
|
output_binary: '',
|
|
env_vars: '',
|
|
port: null,
|
|
})
|
|
|
|
const submitting = ref(false)
|
|
|
|
const handleSubmit = async () => {
|
|
if (!form.value.name) {
|
|
ElMessage.warning('Project name is required')
|
|
return
|
|
}
|
|
submitting.value = true
|
|
try {
|
|
await createProject(form.value)
|
|
ElMessage.success('Project created')
|
|
router.push('/')
|
|
} catch (e) {
|
|
ElMessage.error(e.response?.data?.error || 'Failed to create project')
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="create-page">
|
|
<el-page-header @back="router.push('/')" class="page-back">
|
|
<template #content>
|
|
<span class="back-title">New Project</span>
|
|
</template>
|
|
</el-page-header>
|
|
|
|
<el-card class="form-card" shadow="never">
|
|
<el-form :model="form" label-position="top" class="project-form">
|
|
<!-- Basic info -->
|
|
<div class="form-section">
|
|
<h3 class="section-title">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
|
|
Basic Information
|
|
</h3>
|
|
<div class="form-row two-col">
|
|
<el-form-item label="Project Name" required>
|
|
<el-input v-model="form.name" placeholder="my-project" size="large" />
|
|
</el-form-item>
|
|
<el-form-item label="Port">
|
|
<el-input-number v-model="form.port" :min="1" :max="65535" placeholder="8080" size="large" style="width:100%" />
|
|
</el-form-item>
|
|
</div>
|
|
<el-form-item label="Description">
|
|
<el-input v-model="form.description" type="textarea" :rows="2" placeholder="A short description of this project..." />
|
|
</el-form-item>
|
|
</div>
|
|
|
|
<!-- Source -->
|
|
<div class="form-section">
|
|
<h3 class="section-title">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
|
Source Code
|
|
</h3>
|
|
<el-form-item label="Source Mode">
|
|
<el-radio-group v-model="form.mode" class="mode-switch">
|
|
<el-radio-button value="git">
|
|
<span class="radio-inner">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><circle cx="18" cy="6" r="3"/><path d="M18 9v1a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V9"/><path d="M12 12v3"/></svg>
|
|
Git Repository
|
|
</span>
|
|
</el-radio-button>
|
|
<el-radio-button value="upload">
|
|
<span class="radio-inner">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
|
File Upload
|
|
</span>
|
|
</el-radio-button>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
|
|
<template v-if="form.mode === 'git'">
|
|
<el-form-item label="Git URL">
|
|
<el-input v-model="form.git_url" placeholder="https://gitee.com/user/repo.git" size="large" />
|
|
</el-form-item>
|
|
<div class="form-row two-col">
|
|
<el-form-item label="Branch">
|
|
<el-input v-model="form.git_branch" placeholder="master" />
|
|
</el-form-item>
|
|
<el-form-item label="Username (private repos)">
|
|
<el-input v-model="form.git_username" placeholder="optional" />
|
|
</el-form-item>
|
|
</div>
|
|
<el-form-item label="Password / Token">
|
|
<el-input v-model="form.git_password" type="password" placeholder="optional" show-password />
|
|
</el-form-item>
|
|
</template>
|
|
|
|
<template v-if="form.mode === 'upload'">
|
|
<el-alert type="info" :closable="false" show-icon>
|
|
After creating this project, go to its detail page to upload your source files as .zip or .tar.gz.
|
|
</el-alert>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Build & Run -->
|
|
<div class="form-section">
|
|
<h3 class="section-title">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="14.5 17.5 3 6 3 3 6 3 17.5 14.5"/><line x1="13" y1="19" x2="19" y2="13"/><line x1="16" y1="16" x2="20" y2="20"/><line x1="19" y1="21" x2="21" y2="19"/></svg>
|
|
Build & Run Configuration
|
|
</h3>
|
|
<el-form-item label="Build Script">
|
|
<el-input v-model="form.build_script" type="textarea" :rows="3"
|
|
placeholder="go build -o app ." />
|
|
</el-form-item>
|
|
<div class="form-row two-col">
|
|
<el-form-item label="Output Binary">
|
|
<el-input v-model="form.output_binary" placeholder="app" />
|
|
</el-form-item>
|
|
<el-form-item label="Run Command">
|
|
<el-input v-model="form.run_command" placeholder="./app" />
|
|
</el-form-item>
|
|
</div>
|
|
<el-form-item label="Environment Variables">
|
|
<el-input v-model="form.env_vars" type="textarea" :rows="2"
|
|
placeholder='{"DB_HOST": "192.168.1.100", "DB_PORT": "3306"}' />
|
|
</el-form-item>
|
|
</div>
|
|
|
|
<div class="form-actions">
|
|
<el-button size="large" @click="router.push('/')">Cancel</el-button>
|
|
<el-button type="primary" size="large" :loading="submitting" @click="handleSubmit">
|
|
Create Project
|
|
</el-button>
|
|
</div>
|
|
</el-form>
|
|
</el-card>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.create-page { max-width: 760px; margin: 0 auto; }
|
|
|
|
.page-back { margin-bottom: 20px; }
|
|
.back-title { font-size: 20px; font-weight: 700; color: #1a1d2e; }
|
|
|
|
.form-card {
|
|
border-radius: 14px;
|
|
border: 1px solid #ebeef2;
|
|
box-shadow: 0 1px 3px #00000005;
|
|
}
|
|
.form-card :deep(.el-card__body) { padding: 28px 32px; }
|
|
|
|
/* ── Sections ── */
|
|
.form-section {
|
|
margin-bottom: 28px;
|
|
padding-bottom: 24px;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
.form-section:last-of-type { border-bottom: none; margin-bottom: 24px; padding-bottom: 0; }
|
|
|
|
.section-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 15px;
|
|
font-weight: 700;
|
|
color: #1a1d2e;
|
|
margin-bottom: 16px;
|
|
}
|
|
.section-title svg { width: 18px; height: 18px; color: #409eff; }
|
|
|
|
.form-row { display: flex; gap: 16px; }
|
|
.form-row.two-col > * { flex: 1; }
|
|
|
|
/* ── Mode switch ── */
|
|
.mode-switch { width: 100%; }
|
|
.mode-switch :deep(.el-radio-button) { flex: 1; }
|
|
.mode-switch :deep(.el-radio-button__inner) {
|
|
width: 100%;
|
|
padding: 14px 20px;
|
|
font-weight: 500;
|
|
}
|
|
.radio-inner {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 6px;
|
|
}
|
|
.radio-inner svg { width: 16px; height: 16px; }
|
|
|
|
/* ── Form items ── */
|
|
.project-form :deep(.el-form-item__label) {
|
|
font-weight: 600;
|
|
color: #444;
|
|
padding-bottom: 4px;
|
|
}
|
|
|
|
.form-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 12px;
|
|
padding-top: 8px;
|
|
}
|
|
</style>
|