A optimize project

This commit is contained in:
zhenorzz 2021-05-21 18:56:23 +08:00
parent 616715bebd
commit 8384690979
12 changed files with 1091 additions and 724 deletions

View File

@ -213,7 +213,7 @@ func (Crontab) Edit(gp *core.Goploy) *core.Response {
return &core.Response{}
}
// import many crontab
// Import many crontab
func (Crontab) Import(gp *core.Goploy) *core.Response {
type ReqData struct {
ServerID int64 `json:"serverId" validate:"gt=0"`
@ -272,7 +272,7 @@ func (Crontab) Import(gp *core.Goploy) *core.Response {
return &core.Response{}
}
// DeleteRow one Crontab
// Remove one Crontab
func (Crontab) Remove(gp *core.Goploy) *core.Response {
type ReqData struct {
ID int64 `json:"id" validate:"gt=0"`

View File

@ -470,6 +470,13 @@ func (Project) AddFile(gp *core.Goploy) *core.Response {
return &core.Response{Code: core.Error, Message: err.Error()}
}
_, err := os.Stat(core.GetProjectFilePath(reqData.ProjectID))
if err != nil {
err := os.MkdirAll(core.GetProjectFilePath(reqData.ProjectID), os.ModePerm)
if err != nil {
return &core.Response{Code: core.Error, Message: err.Error()}
}
}
file, err := os.Create(path.Join(core.GetProjectFilePath(reqData.ProjectID), reqData.Filename))
if err != nil {
panic(err)
@ -508,6 +515,14 @@ func (Project) EditFile(gp *core.Goploy) *core.Response {
return &core.Response{Code: core.Error, Message: err.Error()}
}
_, err = os.Stat(core.GetProjectFilePath(projectFileData.ProjectID))
if err != nil {
err := os.MkdirAll(core.GetProjectFilePath(projectFileData.ProjectID), os.ModePerm)
if err != nil {
return &core.Response{Code: core.Error, Message: err.Error()}
}
}
file, err := os.Create(path.Join(core.GetProjectFilePath(projectFileData.ProjectID), projectFileData.Filename))
if err != nil {
panic(err)

View File

@ -34,7 +34,7 @@ var (
Gwg sync.WaitGroup
)
// GetCurrentPath if env = 'production' return absolute else return relative
// GetAssetDir if env = 'production' return absolute else return relative
func GetAssetDir() string {
if AssetDir != "" {
return AssetDir

View File

@ -1,4 +1,191 @@
import Axios from './axios'
import { Request, Pagination, ID, Total } from './types'
export class ProjectServerData {
public datagram!: {
detail: {
id: number
projectId: number
serverId: number
serverName: string
serverIP: string
serverPort: number
serverOwner: string
serverPassword: string
serverPath: string
serverDescription: string
insertTime: string
updateTime: string
}
}
}
export class ProjectServerList extends Request {
readonly url = '/project/getBindServerList'
readonly method = 'get'
public param: ID
public datagram!: {
list: ProjectServerData['datagram']['detail'][]
}
constructor(param: ProjectServerList['param']) {
super()
this.param = param
}
}
export class ProjectServerAdd extends Request {
readonly url = '/project/addServer'
readonly method = 'post'
public param: {
projectId: number
serverIds: number[]
}
constructor(param: ProjectServerAdd['param']) {
super()
this.param = param
}
}
export class ProjectServerRemove extends Request {
readonly url = '/project/removeServer'
readonly method = 'delete'
public param: {
projectServerId: number
}
constructor(param: ProjectServerRemove['param']) {
super()
this.param = param
}
}
export class ProjectUserData {
public datagram!: {
detail: {
id: number
namespaceId: number
projectId: number
projectName: string
userId: number
userName: string
role: string
insertTime: string
updateTime: string
}
}
}
export class ProjectUserList extends Request {
readonly url = '/project/getBindUserList'
readonly method = 'get'
public param: ID
public datagram!: {
list: ProjectUserData['datagram']['detail'][]
}
constructor(param: ProjectUserList['param']) {
super()
this.param = param
}
}
export class ProjectUserAdd extends Request {
readonly url = '/project/addUser'
readonly method = 'post'
public param: {
projectId: number
userIds: number[]
}
constructor(param: ProjectUserAdd['param']) {
super()
this.param = param
}
}
export class ProjectUserRemove extends Request {
readonly url = '/project/removeUser'
readonly method = 'delete'
public param: {
projectUserId: number
}
constructor(param: ProjectUserRemove['param']) {
super()
this.param = param
}
}
export class ProjectFileData {
public datagram!: {
detail: {
id: number
projectId: number
filename: string
insertTime: string
updateTime: string
}
}
}
export class ProjectFileList extends Request {
readonly url = '/project/getProjectFileList'
readonly method = 'get'
public param: ID
public datagram!: {
list: ProjectFileData['datagram']['detail'][]
}
constructor(param: ProjectUserList['param']) {
super()
this.param = param
}
}
export class ProjectFileContent extends Request {
readonly url = '/project/getProjectFileContent'
readonly method = 'get'
public param: ID
public datagram!: {
content: string
}
constructor(param: ProjectUserList['param']) {
super()
this.param = param
}
}
export class ProjectFileAdd extends Request {
readonly url = '/project/addFile'
readonly method = 'post'
public param: {
projectId: number
content: string
filename: string
}
public datagram!: ID
constructor(param: ProjectFileAdd['param']) {
super()
this.param = param
}
}
export class ProjectFileEdit extends Request {
readonly url = '/project/editFile'
readonly method = 'put'
public param: {
id: number
content: string
}
constructor(param: ProjectFileEdit['param']) {
super()
this.param = param
}
}
export class ProjectFileRemove extends Request {
readonly url = '/project/removeFile'
readonly method = 'delete'
public param: {
projectFileId: number
}
constructor(param: ProjectFileRemove['param']) {
super()
this.param = param
}
}
/**
* @return {Promise}
@ -34,54 +221,6 @@ export function getRemoteBranchList(url) {
})
}
/**
* @param {id} id
* @return {Promise}
*/
export function getBindServerList(id) {
return Axios.request({
url: '/project/getBindServerList',
method: 'get',
params: { id },
})
}
/**
* @param {id} id
* @return {Promise}
*/
export function getBindUserList(id) {
return Axios.request({
url: '/project/getBindUserList',
method: 'get',
params: { id },
})
}
/**
* @param {id} id
* @return {Promise}
*/
export function getProjectFileList(id) {
return Axios.request({
url: '/project/getProjectFileList',
method: 'get',
params: { id },
})
}
/**
* @param {id} id
* @return {Promise}
*/
export function getProjectFileContent(id) {
return Axios.request({
url: '/project/getProjectFileContent',
method: 'get',
params: { id },
})
}
/**
* @param {string} project
* @param {string} owner
@ -128,68 +267,6 @@ export function remove(id) {
})
}
export function addUser(data) {
return Axios.request({
url: '/project/addUser',
method: 'post',
data,
})
}
export function removeUser(projectUserId) {
return Axios.request({
url: '/project/removeUser',
method: 'delete',
data: {
projectUserId,
},
})
}
export function addServer(data) {
return Axios.request({
url: '/project/addServer',
method: 'post',
data,
})
}
export function removeServer(projectServerId) {
return Axios.request({
url: '/project/removeServer',
method: 'delete',
data: {
projectServerId,
},
})
}
export function addFile(data) {
return Axios.request({
url: '/project/addFile',
method: 'post',
data,
})
}
export function editFile(data) {
return Axios.request({
url: '/project/editFile',
method: 'put',
data,
})
}
export function removeFile(projectFileId) {
return Axios.request({
url: '/project/removeFile',
method: 'delete',
data: {
projectFileId,
},
})
}
export function addTask(data) {
return Axios.request({
url: '/project/addTask',

View File

@ -136,7 +136,7 @@ export const asyncRoutes: RouteRecordRaw[] = [
{
path: 'index',
name: 'ProjectIndex',
component: () => import('@/views/project/index.vue'),
component: () => import('@/views/project/manage/index.vue'),
meta: {
title: 'project',
icon: 'project',

View File

@ -1,5 +1,9 @@
<template>
<el-dialog v-model="dialogVisible" :title="$t('manage')">
<el-dialog
v-model="dialogVisible"
:title="$t('manage')"
:close-on-click-modal="false"
>
<el-row class="app-bar" type="flex" justify="end">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd" />
<el-row

View File

@ -0,0 +1,343 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="$t('manage')"
custom-class="file-dialog"
:close-on-click-modal="false"
>
<el-row
v-if="formProps.show === 'file-list'"
type="flex"
justify="space-between"
align="middle"
style="margin: 10px 0"
>
<span>{{ $t('projectPage.projectFileTips') }}</span>
<el-button type="primary" icon="el-icon-plus" @click="handleAppendFile" />
</el-row>
<el-form ref="fileForm" :model="formData" class="file-form" @submit.prevent>
<template v-if="formProps.show === 'file-list'">
<el-form-item
v-for="(file, index) in formData.files"
:key="index"
label-width="0"
prop="directory"
>
<el-row type="flex">
<el-input
v-model.trim="file.filename"
autocomplete="off"
placeholder="path/to/example/.env"
:disabled="file.state === 'success'"
:readonly="file.state === 'success'"
style="flex: 1"
>
<template #prepend>{{ formProps.projectPath }}</template>
<template v-if="file.state === 'success'" #suffix>
<i
class="el-input__icon el-icon-check"
style="color: #67c23a; font-size: 16px; font-weight: 900"
/>
</template>
<template v-else-if="file.state === 'loading'" #suffix>
<i
class="el-input__icon el-icon-loading"
style="font-size: 14px; font-weight: 900"
/>
</template>
<template v-else #suffix>
<i
class="el-input__icon el-icon-close"
style="color: #f56c6c; font-size: 16px; font-weight: 900"
/>
</template>
</el-input>
<el-upload
ref="upload"
style="margin: 0 12px"
:action="`${formProps.action}?projectFileId=${file.id}&projectId=${file.projectId}&filename=${file.filename}`"
:before-upload="(uploadFile) => beforeUpload(uploadFile, index)"
:on-success="
(response, uploadFile, uploadFileList) =>
handleUploadSuccess(
response,
uploadFile,
uploadFileList,
index
)
"
:show-file-list="false"
:disabled="!validateFilename(file, index)"
multiple
>
<el-button
:disabled="!validateFilename(file, index)"
type="text"
icon="el-icon-upload"
/>
</el-upload>
<el-button
:disabled="!validateFilename(file, index)"
type="text"
icon="el-icon-edit"
@click="getProjectFileContent(file, index)"
/>
<el-button
type="text"
icon="el-icon-delete"
@click="removeFile(index)"
/>
</el-row>
</el-form-item>
</template>
<el-form-item
v-else
v-loading="formProps.editContentLoading"
prop="projectFileEdit"
label-width="0px"
>
<v-ace-editor
v-model:value="formData.content"
lang="sh"
theme="github"
style="height: 500px"
/>
</el-form-item>
</el-form>
<template v-if="formProps.show !== 'file-list'" #footer>
<el-button @click="formProps.show = 'file-list'">
{{ $t('cancel') }}
</el-button>
<el-button
:disabled="formProps.disabled"
type="primary"
@click="fileSubmit"
>
{{ $t('confirm') }}
</el-button>
</template>
</el-dialog>
</template>
<script lang="ts">
import {
ProjectFileList,
ProjectFileContent,
ProjectFileAdd,
ProjectFileEdit,
ProjectFileRemove,
} from '@/api/project'
import { VAceEditor } from 'vue3-ace-editor'
import { role } from '@/utils/namespace'
import { ElMessageBox, ElMessage } from 'element-plus'
import { computed, watch, defineComponent, reactive } from 'vue'
import { HttpResponse, ID } from '@/api/types'
interface FormFileInfo {
id: number
projectId: number
filename: string
content: string
state: string
}
export default defineComponent({
components: {
VAceEditor,
},
props: {
modelValue: {
type: Boolean,
default: false,
},
projectId: {
type: Number,
default: 0,
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const dialogVisible = computed({
get: () => props.modelValue,
set: (val) => {
emit('update:modelValue', val)
},
})
const formData = reactive({
files: [] as FormFileInfo[],
content: '',
projectId: 0,
})
const getProjectFileList = (projectId: number) => {
new ProjectFileList({ id: projectId }).request().then((response) => {
formData.files = response.data.list.map((item) => {
return { ...item, state: 'success', content: '' }
})
})
}
watch(
() => props.modelValue,
(val: typeof props['modelValue']) => {
if (val === true) {
getProjectFileList(props.projectId)
}
}
)
return {
role,
dialogVisible,
getProjectFileList,
formData,
}
},
data() {
return {
formProps: {
projectPath: '/data/www/goploy/',
action: import.meta.env.VITE_APP_BASE_API + '/project/uploadFile',
show: 'file-list',
editContentLoading: false,
disabled: false,
selectedIndex: -1,
},
}
},
watch: {
projectId: function (newVal) {
this.formData.projectId = newVal
},
},
methods: {
getProjectFileContent(file: FormFileInfo, index: number) {
this.formProps.selectedIndex = index
this.formProps.show = 'edit-content'
if (file.id === 0) {
this.formData.content = this.formData.files[index]['content']
return
}
this.formData.content = ''
this.formProps.editContentLoading = true
new ProjectFileContent({ id: file.id })
.request()
.then((response) => {
this.formData.content = this.formData.files[index]['content'] =
response.data.content
})
.finally(() => {
this.formProps.editContentLoading = false
})
},
handleAppendFile() {
this.formData.files.push({
filename: '',
projectId: this.projectId,
state: 'loading',
content: '',
id: 0,
})
},
beforeUpload(file: File, index: number) {
this.formData.files[index].state = 'loading'
ElMessage.info(this.$t('uploading'))
},
handleUploadSuccess(
response: HttpResponse<ID>,
file: File,
fileList: File[],
index: number
) {
if (response.code !== 0) {
this.formData.files[index].state = 'fail'
ElMessage.error(response.message)
} else {
this.formData.files[index].id = response.data.id
this.formData.files[index].state = 'success'
ElMessage.success('Success')
}
},
validateFilename(file: FormFileInfo, index: number) {
const filename = file.filename
if (file.state === 'success') {
return true
} else if (filename === '') {
return false
} else if (filename.substr(filename.length - 1, 1) === '/') {
return false
}
const filenames = this.formData.files.map((item) => item.filename)
filenames.splice(index, 1)
return filenames.indexOf(filename) === -1
},
fileSubmit() {
const file = this.formData.files[this.formProps.selectedIndex]
this.formProps.disabled = true
if (file.id === 0) {
new ProjectFileAdd({
projectId: file.projectId,
filename: file.filename,
content: this.formData.content,
})
.request()
.then((response) => {
this.formData.files[this.formProps.selectedIndex].id =
response.data.id
this.formData.files[this.formProps.selectedIndex].state = 'success'
this.formProps.show = 'file-list'
ElMessage.success('Success')
})
.finally(() => {
this.formProps.disabled = false
})
} else {
new ProjectFileEdit({
id: file.id,
content: this.formData.content,
})
.request()
.then(() => {
this.formData.files[this.formProps.selectedIndex].state = 'success'
this.formProps.show = 'file-list'
ElMessage.success('Success')
})
.finally(() => {
this.formProps.disabled = false
})
}
},
removeFile(index: number) {
if (this.formData.files[index].id === 0) {
this.formData.files.splice(index, 1)
} else {
ElMessageBox.confirm(
this.$t('projectPage.removeFileTips', {
filename: this.formData.files[index].filename,
}),
this.$t('tips'),
{
confirmButtonText: this.$t('confirm'),
cancelButtonText: this.$t('cancel'),
type: 'warning',
}
)
.then(() => {
new ProjectFileRemove({
projectFileId: this.formData.files[index].id,
})
.request()
.then(() => {
ElMessage.success('Success')
this.formData.files.splice(index, 1)
})
})
.catch(() => {
ElMessage.info('Cancel')
})
}
},
},
})
</script>

View File

@ -0,0 +1,249 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="$t('manage')"
:close-on-click-modal="false"
>
<el-row class="app-bar" type="flex" justify="end">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd" />
<el-row
v-if="showAddView"
type="flex"
justify="center"
style="margin-top: 10px; width: 100%"
>
<el-form ref="form" :inline="true" :rules="formRules" :model="formData">
<el-form-item
:label="$t('server')"
label-width="120px"
prop="serverIds"
>
<el-select v-model="formData.serverIds" multiple>
<el-option
v-for="(item, index) in serverOption"
:key="index"
:label="item.label"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item style="margin-right: 0px; margin-bottom: 5px">
<el-button
type="primary"
:disabled="formProps.disabled"
@click="add"
>
{{ $t('confirm') }}
</el-button>
<el-button @click="showAddView = false">
{{ $t('cancel') }}
</el-button>
</el-form-item>
</el-form>
</el-row>
</el-row>
<el-table
v-loading="tableLoading"
border
stripe
highlight-current-row
:data="tableData"
style="width: 100%"
>
<el-table-column prop="serverId" :label="$t('serverId')" width="100" />
<el-table-column
prop="serverName"
:label="$t('serverName')"
width="120"
/>
<el-table-column
prop="serverDescription"
:label="$t('serverDescription')"
min-width="200"
show-overflow-tooltip
/>
<el-table-column
prop="insertTime"
:label="$t('insertTime')"
width="160"
align="center"
/>
<el-table-column
prop="updateTime"
:label="$t('updateTime')"
width="160"
align="center"
/>
<el-table-column
prop="operation"
:label="$t('op')"
width="80"
align="center"
>
<template #default="scope">
<el-button
type="danger"
icon="el-icon-delete"
@click="remove(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="dialogVisible = false">
{{ $t('cancel') }}
</el-button>
</template>
</el-dialog>
</template>
<script lang="ts">
import {
ProjectServerData,
ProjectServerList,
ProjectServerAdd,
ProjectServerRemove,
} from '@/api/project'
import { ServerOption } from '@/api/server'
import Validator from 'async-validator'
import { ElMessageBox, ElMessage } from 'element-plus'
import { computed, watch, defineComponent, ref, Ref } from 'vue'
export default defineComponent({
props: {
modelValue: {
type: Boolean,
default: false,
},
projectId: {
type: Number,
default: 0,
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const tableData: Ref<ProjectServerList['datagram']['list']> = ref([])
const dialogVisible = computed({
get: () => props.modelValue,
set: (val) => {
emit('update:modelValue', val)
},
})
const tableLoading = ref(false)
const getBindServerList = (projectId: number) => {
tableLoading.value = true
new ProjectServerList({ id: projectId })
.request()
.then((response) => {
tableData.value = response.data.list
})
.finally(() => {
tableLoading.value = false
})
}
watch(
() => props.modelValue,
(val: typeof props['modelValue']) => {
if (val === true) {
getBindServerList(props.projectId)
}
}
)
const showAddView = ref(false)
const handleAdd = () => {
showAddView.value = true
}
const serverOption: Ref<ServerOption['datagram']['list']> = ref([])
watch(showAddView, (val: boolean) => {
if (val === true) {
new ServerOption().request().then((response) => {
serverOption.value = response.data.list
})
}
})
return {
dialogVisible,
getBindServerList,
tableLoading,
tableData,
showAddView,
handleAdd,
serverOption,
}
},
data() {
return {
formProps: {
disabled: false,
},
formData: {
projectId: 0,
serverIds: [],
},
formRules: {
serverIds: [
{
type: 'array',
required: true,
message: 'Server required',
trigger: 'change',
},
],
},
}
},
watch: {
projectId: function (newVal) {
this.formData.projectId = newVal
},
},
methods: {
add() {
;(this.$refs.form as Validator).validate((valid: boolean) => {
if (valid) {
this.formProps.disabled = true
new ProjectServerAdd(this.formData)
.request()
.then(() => {
ElMessage.success('Success')
this.getBindServerList(this.formData.projectId)
})
.finally(() => {
this.formProps.disabled = false
})
} else {
return false
}
})
},
remove(data: ProjectServerData['datagram']['detail']) {
ElMessageBox.confirm(
this.$t('crontabPage.removeCrontabServerTips'),
this.$t('tips'),
{
confirmButtonText: this.$t('confirm'),
cancelButtonText: this.$t('cancel'),
type: 'warning',
}
)
.then(() => {
new ProjectServerRemove({
projectServerId: data.id,
})
.request()
.then(() => {
ElMessage.success('Success')
this.getBindServerList(data.projectId)
})
})
.catch(() => {
ElMessage.info('Cancel')
})
},
},
})
</script>

View File

@ -0,0 +1,259 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="$t('manage')"
:close-on-click-modal="false"
>
<el-row class="app-bar" type="flex" justify="end">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd" />
<el-row
v-if="showAddView"
type="flex"
justify="center"
style="margin-top: 10px; width: 100%"
>
<el-form ref="form" :inline="true" :rules="formRules" :model="formData">
<el-form-item
:label="$t('user')"
label-width="60px"
prop="userIds"
style="margin-bottom: 5px"
>
<el-select
v-model="formData.userIds"
:loading="userLoading"
multiple
clearable
filterable
>
<el-option
v-for="(item, index) in userOption.filter(
(item) => [role.Admin, role.Manager].indexOf(item.role) === -1
)"
:key="index"
:label="item.userName"
:value="item.userId"
/>
</el-select>
</el-form-item>
<el-form-item style="margin-right: 0px; margin-bottom: 5px">
<el-button
type="primary"
:disabled="formProps.disabled"
@click="add"
>
{{ $t('confirm') }}
</el-button>
<el-button @click="showAddView = false">
{{ $t('cancel') }}
</el-button>
</el-form-item>
</el-form>
</el-row>
</el-row>
<el-table
v-loading="tableLoading"
border
stripe
highlight-current-row
:data="tableData"
style="width: 100%"
>
<el-table-column prop="userId" :label="$t('userId')" width="100" />
<el-table-column prop="userName" :label="$t('userName')" />
<el-table-column
prop="insertTime"
:label="$t('insertTime')"
width="135"
align="center"
/>
<el-table-column
prop="updateTime"
:label="$t('updateTime')"
width="135"
align="center"
/>
<el-table-column
prop="operation"
:label="$t('op')"
width="80"
align="center"
fixed="right"
>
<template #default="scope">
<el-button
v-show="role.hasManagerPermission()"
type="danger"
icon="el-icon-delete"
@click="remove(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="dialogVisible = false">
{{ $t('cancel') }}
</el-button>
</template>
</el-dialog>
</template>
<script lang="ts">
import {
ProjectUserData,
ProjectUserList,
ProjectUserAdd,
ProjectUserRemove,
} from '@/api/project'
import { NamespaceUserOption } from '@/api/namespace'
import { role } from '@/utils/namespace'
import Validator from 'async-validator'
import { ElMessageBox, ElMessage } from 'element-plus'
import { computed, watch, defineComponent, ref, Ref } from 'vue'
export default defineComponent({
props: {
modelValue: {
type: Boolean,
default: false,
},
projectId: {
type: Number,
default: 0,
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
let tableData: Ref<ProjectUserList['datagram']['list']> = ref([])
const dialogVisible = computed({
get: () => props.modelValue,
set: (val) => {
emit('update:modelValue', val)
},
})
let tableLoading = ref(false)
const getBindUserList = (projectId: number) => {
tableLoading.value = true
new ProjectUserList({ id: projectId })
.request()
.then((response) => {
tableData.value = response.data.list
})
.finally(() => {
tableLoading.value = false
})
}
watch(
() => props.modelValue,
(val: typeof props['modelValue']) => {
if (val === true) {
getBindUserList(props.projectId)
}
}
)
let showAddView = ref(false)
const handleAdd = () => {
showAddView.value = true
}
const userLoading = ref(false)
let userOption: Ref<NamespaceUserOption['datagram']['list']> = ref([])
watch(showAddView, (val: boolean) => {
if (val === true) {
userLoading.value = true
new NamespaceUserOption()
.request()
.then((response) => {
userOption.value = response.data.list
})
.finally(() => {
userLoading.value = false
})
}
})
return {
role,
dialogVisible,
getBindUserList,
tableLoading,
tableData,
showAddView,
handleAdd,
userLoading,
userOption,
}
},
data() {
return {
formProps: {
disabled: false,
},
formData: {
projectId: 0,
userIds: [],
},
formRules: {
userIds: [
{
type: 'array',
required: true,
message: 'User required',
trigger: 'change',
},
],
role: [{ required: true, message: 'Role required', trigger: 'change' }],
},
}
},
watch: {
projectId: function (newVal) {
this.formData.projectId = newVal
},
},
methods: {
add() {
;(this.$refs.form as Validator).validate((valid: boolean) => {
if (valid) {
this.formProps.disabled = true
new ProjectUserAdd(this.formData)
.request()
.then(() => {
this.showAddView = false
ElMessage.success('Success')
this.getBindUserList(this.formData.projectId)
})
.finally(() => {
this.formProps.disabled = false
})
} else {
return false
}
})
},
remove(data: ProjectUserData['datagram']['detail']) {
ElMessageBox.confirm(
this.$t('namespacePage.removeUserTips'),
this.$t('tips'),
{
confirmButtonText: this.$t('confirm'),
cancelButtonText: this.$t('cancel'),
type: 'warning',
}
)
.then(() => {
new ProjectUserRemove({ projectUserId: data.id })
.request()
.then(() => {
ElMessage.success('Success')
this.getBindUserList(data.projectId)
})
})
.catch(() => {
ElMessage.info('Cancel')
})
},
},
})
</script>

View File

@ -568,332 +568,30 @@
</el-button>
</template>
</el-dialog>
<el-dialog v-model="dialogServerVisible" :title="$t('manage')">
<el-row class="app-bar" type="flex" justify="end">
<el-button
type="primary"
icon="el-icon-plus"
@click="handleAddServer"
<TheServerDialog
v-model="dialogServerVisible"
:project-id="selectedItem.id"
/>
</el-row>
<el-table
border
stripe
highlight-current-row
:data="tableServerData"
style="width: 100%"
>
<el-table-column prop="serverId" :label="$t('serverId')" width="100" />
<el-table-column
prop="serverName"
:label="$t('serverName')"
min-width="120"
/>
<el-table-column
prop="serverDescription"
:label="$t('serverDescription')"
min-width="200"
show-overflow-tooltip
/>
<el-table-column
prop="insertTime"
:label="$t('insertTime')"
width="135"
align="center"
/>
<el-table-column
prop="updateTime"
:label="$t('updateTime')"
width="135"
align="center"
/>
<el-table-column
prop="operation"
:label="$t('op')"
width="80"
align="center"
fixed="right"
>
<template #default="scope">
<el-button
type="danger"
icon="el-icon-delete"
@click="removeServer(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<template #footer class="dialog-footer">
<el-button @click="dialogServerVisible = false">
{{ $t('cancel') }}
</el-button>
</template>
</el-dialog>
<el-dialog v-model="dialogUserVisible" :title="$t('manage')">
<el-row class="app-bar" type="flex" justify="end">
<el-button type="primary" icon="el-icon-plus" @click="handleAddUser" />
</el-row>
<el-table
border
stripe
highlight-current-row
:data="tableUserData"
style="width: 100%"
>
<el-table-column prop="userId" :label="$t('userId')" width="100" />
<el-table-column prop="userName" :label="$t('userName')" />
<el-table-column
prop="insertTime"
:label="$t('insertTime')"
width="135"
align="center"
/>
<el-table-column
prop="updateTime"
:label="$t('updateTime')"
width="135"
align="center"
/>
<el-table-column
prop="operation"
:label="$t('op')"
width="80"
align="center"
fixed="right"
>
<template #default="scope">
<el-button
v-show="role.hasManagerPermission()"
type="danger"
icon="el-icon-delete"
@click="removeUser(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<template #footer class="dialog-footer">
<el-button @click="dialogUserVisible = false">
{{ $t('cancel') }}
</el-button>
</template>
</el-dialog>
<el-dialog
<TheUserDialog v-model="dialogUserVisible" :project-id="selectedItem.id" />
<TheFileDialog
v-model="dialogFileManagerVisible"
:title="$t('manage')"
custom-class="file-dialog"
>
<el-row
v-if="fileFormProps.show === 'file-list'"
type="flex"
justify="space-between"
align="middle"
style="margin: 10px 0"
>
<span>{{ $t('projectPage.projectFileTips') }}</span>
<el-button
type="primary"
icon="el-icon-plus"
@click="handleAppendFile"
:project-id="selectedItem.id"
/>
</el-row>
<el-form
ref="fileForm"
:rules="fileFormRules"
:model="fileFormData"
class="file-form"
>
<template v-if="fileFormProps.show === 'file-list'">
<el-form-item
v-for="(file, index) in fileFormData.files"
:key="index"
label-width="0"
prop="directory"
>
<el-row type="flex">
<el-input
v-model.trim="file.filename"
autocomplete="off"
placeholder="path/to/example/.env"
:disabled="file.state === 'success'"
:readonly="file.state === 'success'"
>
<template #prepend>{{ fileFormProps.projectPath }}</template>
<template v-if="file.state === 'success'" #suffix>
<i
class="el-input__icon el-icon-check"
style="color: #67c23a; font-size: 16px; font-weight: 900"
/>
</template>
<template v-else-if="file.state === 'loading'" #suffix>
<i
class="el-input__icon el-icon-loading"
style="font-size: 14px; font-weight: 900"
/>
</template>
<template v-else #suffix>
<i
class="el-input__icon el-icon-close"
style="color: #f56c6c; font-size: 16px; font-weight: 900"
/>
</template>
</el-input>
<el-upload
ref="upload"
style="margin: 0 12px"
:action="`${fileFormProps.action}?projectFileId=${file.id}&projectId=${file.projectId}&filename=${file.filename}`"
:before-upload="(uploadFile) => beforeUpload(uploadFile, index)"
:on-success="
(response, uploadFile, uploadFileList) =>
handleUploadSuccess(
response,
uploadFile,
uploadFileList,
index
)
"
:show-file-list="false"
:disabled="!validateFilename(file, index)"
multiple
>
<el-button
:disabled="!validateFilename(file, index)"
type="text"
icon="el-icon-upload"
/>
</el-upload>
<el-button
:disabled="!validateFilename(file, index)"
type="text"
icon="el-icon-edit"
@click="getProjectFileContent(file, index)"
/>
<el-button
type="text"
icon="el-icon-delete"
@click="removeFile(index)"
/>
</el-row>
</el-form-item>
</template>
<el-form-item v-else prop="projectFileEdit" label-width="0px">
<v-ace-editor
v-model:value="fileFormData.content"
v-loading="fileFormProps.editContentLoading"
:lang="getScriptLang()"
theme="github"
style="height: 500px"
/>
</el-form-item>
</el-form>
<template v-if="fileFormProps.show !== 'file-list'" #footer>
<el-button @click="fileFormProps.show = 'file-list'">
{{ $t('cancel') }}
</el-button>
<el-button
:disabled="fileFormProps.disabled"
type="primary"
@click="fileSubmit"
>
{{ $t('confirm') }}
</el-button>
</template>
</el-dialog>
<el-dialog v-model="dialogAddServerVisible" :title="$t('add')">
<el-form
ref="addServerForm"
:rules="addServerFormRules"
:model="addServerFormData"
>
<el-form-item :label="$t('server')" label-width="80px" prop="serverIds">
<el-select
v-model="addServerFormData.serverIds"
multiple
style="width: 100%"
>
<el-option
v-for="(item, index) in serverOption"
:key="index"
:label="item.label"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogAddServerVisible = false">
{{ $t('cancel') }}
</el-button>
<el-button
:disabled="addServerFormProps.disabled"
type="primary"
@click="addServer"
>
{{ $t('confirm') }}
</el-button>
</template>
</el-dialog>
<el-dialog v-model="dialogAddUserVisible" :title="$t('add')">
<el-form
ref="addUserForm"
:rules="addUserFormRules"
:model="addUserFormData"
>
<el-form-item :label="$t('member')" label-width="80px" prop="userIds">
<el-select
v-model="addUserFormData.userIds"
multiple
style="width: 100%"
>
<el-option
v-for="(item, index) in userOption.filter(
(item) => [role.Admin, role.Manager].indexOf(item.role) === -1
)"
:key="index"
:label="item.userName"
:value="item.userId"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogAddUserVisible = false">
{{ $t('cancel') }}
</el-button>
<el-button
:disabled="addUserFormProps.disabled"
type="primary"
@click="addUser"
>
{{ $t('confirm') }}
</el-button>
</template>
</el-dialog>
</el-row>
</template>
<script>
import tableHeight from '@/mixin/tableHeight'
import { parseGitURL } from '@/utils'
import { NamespaceUserOption } from '@/api/namespace'
import { getOption as getServerOption } from '@/api/server'
import { ServerOption } from '@/api/server'
import {
getList,
getTotal,
getBindServerList,
getBindUserList,
getProjectFileList,
getRemoteBranchList,
getProjectFileContent,
add,
edit,
remove,
addServer,
addUser,
addFile,
editFile,
removeFile,
setAutoDeploy,
removeServer,
removeUser,
} from '@/api/project'
import { role } from '@/utils/namespace'
import { VAceEditor } from 'vue3-ace-editor'
@ -901,12 +599,17 @@ import 'ace-builds/src-noconflict/mode-sh'
import 'ace-builds/src-noconflict/mode-python'
import 'ace-builds/src-noconflict/mode-php'
import 'ace-builds/src-noconflict/theme-github'
import TheServerDialog from './TheServerDialog.vue'
import TheUserDialog from './TheUserDialog.vue'
import TheFileDialog from './TheFileDialog.vue'
import { defineComponent } from 'vue'
export default defineComponent({
name: 'ProjectIndex',
components: {
VAceEditor,
TheServerDialog,
TheUserDialog,
TheFileDialog,
},
mixins: [tableHeight],
data() {
@ -933,8 +636,6 @@ export default defineComponent({
dialogAutoDeployVisible: false,
dialogServerVisible: false,
dialogUserVisible: false,
dialogAddServerVisible: false,
dialogAddUserVisible: false,
dialogFileManagerVisible: false,
serverOption: [],
userOption: [],
@ -946,8 +647,6 @@ export default defineComponent({
rows: 16,
total: 0,
},
tableServerData: [],
tableUserData: [],
formProps: {
reviewURLParamOption: [
{
@ -1048,53 +747,6 @@ export default defineComponent({
id: 0,
autoDeploy: 0,
},
fileFormProps: {
projectPath: '/data/www/goploy/',
action: import.meta.env.VITE_APP_BASE_API + '/project/uploadFile',
show: 'file-list',
editContentLoading: false,
disabled: false,
selectedIndex: -1,
},
fileFormData: {
files: [],
content: '',
},
fileFormRules: {},
addServerFormProps: {
disabled: false,
},
addServerFormData: {
projectId: 0,
serverIds: [],
},
addServerFormRules: {
serverIds: [
{
type: 'array',
required: true,
message: 'Server required',
trigger: 'change',
},
],
},
addUserFormProps: {
disabled: false,
},
addUserFormData: {
projectId: 0,
userIds: [],
},
addUserFormRules: {
userIds: [
{
type: 'array',
required: true,
message: 'User required',
trigger: 'change',
},
],
},
}
},
created() {
@ -1182,71 +834,20 @@ export default defineComponent({
},
handleServer(data) {
this.getBindServerList(data.id)
// projectID
this.addServerFormData.projectId = data.id
this.selectedItem = data
this.dialogServerVisible = true
},
handleUser(data) {
this.getBindUserList(data.id)
// projectID
this.addUserFormData.projectId = data.id
this.selectedItem = data
this.dialogUserVisible = true
},
handleAddServer() {
this.dialogAddServerVisible = true
},
handleAddUser() {
this.dialogAddUserVisible = true
},
handleFile(data) {
this.getProjectFileList(data.id)
this.selectedItem = data
this.dialogFileManagerVisible = true
},
handleAppendFile() {
this.fileFormData.files.push({
filename: '',
projectId: this.selectedItem.id,
state: 'loading',
id: 0,
})
},
validateFilename(file, index) {
const filename = file.filename
if (file.state === 'success') {
return true
} else if (filename === '') {
return false
} else if (filename.substr(filename.length - 1, 1) === '/') {
return false
}
const filenames = this.fileFormData.files.map((item) => item.filename)
filenames.splice(index, 1)
return filenames.indexOf(filename) === -1
},
beforeUpload(file, index) {
this.fileFormData.files[index].state = 'loading'
this.$message.info(this.$i18n.t('uploading'))
},
handleUploadSuccess(response, file, fileList, index) {
if (response.code !== 0) {
this.fileFormData.files[index].state = 'fail'
this.$message.error(response.message)
} else {
this.fileFormData.files[index].id = response.data.id
this.fileFormData.files[index].state = 'success'
this.$message.success('Success')
}
},
submit() {
this.$refs.form.validate((valid) => {
if (valid) {
@ -1322,154 +923,8 @@ export default defineComponent({
})
},
addServer() {
this.$refs.addServerForm.validate((valid) => {
if (valid) {
this.addServerFormProps.disabled = true
addServer(this.addServerFormData)
.then(() => {
this.dialogAddServerVisible = false
this.$message.success('Success')
this.getBindServerList(this.addServerFormData.projectId)
})
.finally(() => {
this.addServerFormProps.disabled = false
})
} else {
return false
}
})
},
addUser() {
this.$refs.addUserForm.validate((valid) => {
if (valid) {
this.addUserFormProps.disabled = true
addUser(this.addUserFormData)
.then(() => {
this.dialogAddUserVisible = false
this.$message.success('Success')
this.getBindUserList(this.addUserFormData.projectId)
})
.finally(() => {
this.addUserFormProps.disabled = false
})
} else {
return false
}
})
},
removeServer(data) {
this.$confirm(
this.$i18n.t('projectPage.removeServerTips', {
serverName: data.serverName,
}),
this.$i18n.t('tips'),
{
confirmButtonText: this.$i18n.t('confirm'),
cancelButtonText: this.$i18n.t('cancel'),
type: 'warning',
}
)
.then(() => {
removeServer(data.id).then(() => {
this.$message.success('Success')
this.getBindServerList(data.projectId)
})
})
.catch(() => {
this.$message.info('Cancel')
})
},
removeUser(data) {
this.$confirm(
this.$i18n.t('projectPage.removeUserTips', { userName: data.userName }),
this.$i18n.t('tips'),
{
confirmButtonText: this.$i18n.t('confirm'),
cancelButtonText: this.$i18n.t('cancel'),
type: 'warning',
}
)
.then(() => {
removeUser(data.id).then(() => {
this.$message.success('Success')
this.getBindUserList(data.projectId)
})
})
.catch(() => {
this.$message.info('Cancel')
})
},
removeFile(index) {
if (this.fileFormData.files[index].id === 0) {
this.fileFormData.files.splice(index, 1)
} else {
this.$confirm(
this.$i18n.t('projectPage.removeFileTips', {
filename: this.fileFormData.files[index].filename,
}),
this.$i18n.t('tips'),
{
confirmButtonText: this.$i18n.t('confirm'),
cancelButtonText: this.$i18n.t('cancel'),
type: 'warning',
}
)
.then(() => {
removeFile(this.fileFormData.files[index].id).then(() => {
this.$message.success('Success')
this.fileFormData.files.splice(index, 1)
})
})
.catch(() => {
this.$message.info('Cancel')
})
}
},
fileSubmit() {
const file = this.fileFormData.files[this.fileFormProps.selectedIndex]
this.fileFormProps.disabled = true
if (file.id === 0) {
addFile({
projectId: file.projectId,
filename: file.filename,
content: this.fileFormData.content,
})
.then((response) => {
this.fileFormData.files[this.fileFormProps.selectedIndex].id =
response.data.id
this.fileFormData.files[this.fileFormProps.selectedIndex].state =
'success'
this.fileFormProps.show = 'file-list'
this.$message.success('Success')
})
.finally(() => {
this.fileFormProps.disabled = false
})
} else {
editFile({
id: file.id,
content: this.fileFormData.content,
})
.then(() => {
this.fileFormData.files[this.fileFormProps.selectedIndex].state =
'success'
this.fileFormProps.show = 'file-list'
this.$message.success('Success')
})
.finally(() => {
this.fileFormProps.disabled = false
})
}
},
getOptions() {
getServerOption().then((response) => {
new ServerOption().request().then((response) => {
this.serverOption = response.data.list
this.serverOption.map((element) => {
element.label =
@ -1480,7 +935,7 @@ export default defineComponent({
return element
})
})
getUserOption().then((response) => {
new NamespaceUserOption().request().then((response) => {
this.userOption = response.data.list
})
},
@ -1502,48 +957,6 @@ export default defineComponent({
})
},
getProjectFileList(projectID) {
getProjectFileList(projectID).then((response) => {
this.fileFormData.files = response.data.list.map((item) => {
item.state = 'success'
return item
})
})
},
getProjectFileContent(file, index) {
this.fileFormProps.selectedIndex = index
this.fileFormProps.show = 'edit-content'
this.fileFormData.content = this.fileFormData.files[index]['content']
? this.fileFormData.files[index]['content']
: ''
if (file.id === 0) {
return
}
this.fileFormProps.editContentLoading = true
getProjectFileContent(file.id)
.then((response) => {
this.fileFormData.content = this.fileFormData.files[index][
'content'
] = response.data.content
})
.finally(() => {
this.fileFormProps.editContentLoading = false
})
},
getBindServerList(projectID) {
getBindServerList(projectID).then((response) => {
this.tableServerData = response.data.list
})
},
getBindUserList(projectID) {
getBindUserList(projectID).then((response) => {
this.tableUserData = response.data.list
})
},
getRemoteBranchList() {
if (this.formProps.branch.length > 0) {
return

View File

@ -1,5 +1,9 @@
<template>
<el-dialog v-model="dialogVisible" :title="$t('import')">
<el-dialog
v-model="dialogVisible"
:title="$t('import')"
:close-on-click-modal="false"
>
<el-row>
<el-row type="flex" style="width: 100%">
<el-select

View File

@ -1,5 +1,9 @@
<template>
<el-dialog v-model="dialogVisible" :title="$t('manage')">
<el-dialog
v-model="dialogVisible"
:title="$t('manage')"
:close-on-click-modal="false"
>
<el-row class="app-bar" type="flex" justify="end">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd" />
<el-row
@ -204,7 +208,6 @@ export default defineComponent({
new CrontabServerAdd(this.formData)
.request()
.then(() => {
this.dialogVisible = false
ElMessage.success('Success')
this.getBindServerList(this.formData.crontabId)
})