mirror of
https://gitee.com/fit2cloud-feizhiyun/1Panel.git
synced 2024-11-30 19:08:06 +08:00
feat: 增加单独修改文件权限页面
This commit is contained in:
parent
9db23ed649
commit
12527de2ba
@ -62,3 +62,17 @@ func (b *BaseApi) DeleteFile(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) ChangeFileMode(c *gin.Context) {
|
||||
var req dto.FileCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
err := fileService.ChangeMode(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/utils/files"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
)
|
||||
@ -48,6 +49,11 @@ func (f FileService) GetFileTree(op dto.FileOption) ([]dto.FileTree, error) {
|
||||
func (f FileService) Create(op dto.FileCreate) error {
|
||||
|
||||
fo := files.NewFileOp()
|
||||
|
||||
if fo.Stat(op.Path) {
|
||||
return errors.New("file is exist")
|
||||
}
|
||||
|
||||
if op.IsDir {
|
||||
return fo.CreateDir(op.Path, fs.FileMode(op.Mode))
|
||||
}
|
||||
@ -64,6 +70,11 @@ func (f FileService) Delete(op dto.FileDelete) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (f FileService) ChangeMode(op dto.FileCreate) error {
|
||||
fo := files.NewFileOp()
|
||||
return fo.Chmod(op.Path, fs.FileMode(op.Mode))
|
||||
}
|
||||
|
||||
func getUuid() string {
|
||||
b := make([]byte, 16)
|
||||
io.ReadFull(rand.Reader, b)
|
||||
|
@ -19,6 +19,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
|
||||
fileRouter.POST("/tree", baseApi.GetFileTree)
|
||||
fileRouter.POST("", baseApi.CreateFile)
|
||||
fileRouter.POST("/del", baseApi.DeleteFile)
|
||||
fileRouter.POST("/mode", baseApi.ChangeFileMode)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,14 @@ func (f FileOp) DeleteDir(dst string) error {
|
||||
return f.Fs.RemoveAll(dst)
|
||||
}
|
||||
|
||||
func (f FileOp) Stat(dst string) bool {
|
||||
info, _ := f.Fs.Stat(dst)
|
||||
if info != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f FileOp) DeleteFile(dst string) error {
|
||||
return f.Fs.Remove(dst)
|
||||
}
|
||||
@ -50,3 +58,7 @@ func (f FileOp) WriteFile(dst string, in io.Reader, mode fs.FileMode) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FileOp) Chmod(dst string, mode fs.FileMode) error {
|
||||
return f.Fs.Chmod(dst, mode)
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"items": [
|
||||
{
|
||||
"name": "var",
|
||||
"isDir": true,
|
||||
"mode": 775,
|
||||
"user": "root",
|
||||
"group": "root",
|
||||
"size": 2048,
|
||||
"updateTime": "2022-08-11T11:05:22.001+08:00"
|
||||
},
|
||||
{
|
||||
"name": "test.txt",
|
||||
"isDir": false,
|
||||
"mode": 775,
|
||||
"user": "root",
|
||||
"group": "root",
|
||||
"size": 4096,
|
||||
"updateTime": "2022-08-11T11:05:22.001+08:00"
|
||||
}
|
||||
],
|
||||
"total": 2
|
||||
}
|
||||
}
|
@ -16,3 +16,7 @@ export const CreateFile = (form: File.FileCreate) => {
|
||||
export const DeleteFile = (form: File.FileDelete) => {
|
||||
return http.post<File.File>('files/del', form);
|
||||
};
|
||||
|
||||
export const ChangeFileMode = (form: File.FileCreate) => {
|
||||
return http.post<File.File>('files/mode', form);
|
||||
};
|
||||
|
@ -44,7 +44,7 @@ interface Props {
|
||||
const roles = ref<string[]>(['0', '1', '2', '3', '4', '5', '6', '7']);
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
mode: '0775',
|
||||
mode: '0755',
|
||||
});
|
||||
|
||||
const { mode } = toRefs(props);
|
||||
@ -53,7 +53,7 @@ let form = ref<RoleForm>({
|
||||
owner: { r: true, w: true, x: true },
|
||||
group: { r: true, w: true, x: true },
|
||||
public: { r: true, w: false, x: true },
|
||||
mode: '0775',
|
||||
mode: '0755',
|
||||
});
|
||||
const em = defineEmits(['getMode']);
|
||||
|
||||
|
@ -7,6 +7,7 @@ import i18n from '@/lang';
|
||||
* @param {Function} api 操作数据接口的api方法(必传)
|
||||
* @param {Object} params 携带的操作数据参数 {id,params}(必传)
|
||||
* @param {String} message 提示信息(必传)
|
||||
* @param {String} loading 页面loading
|
||||
* @param {String} confirmType icon类型(不必传,默认为 warning)
|
||||
* @return Promise
|
||||
*/
|
||||
@ -14,22 +15,28 @@ export const useDeleteData = <P = any, R = any>(
|
||||
api: (params: P) => Promise<R>,
|
||||
params: Parameters<typeof api>[0],
|
||||
message: string,
|
||||
loading: boolean,
|
||||
confirmType: HandleData.MessageType = 'error',
|
||||
) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
loading = true;
|
||||
ElMessageBox.confirm(i18n.global.t(`${message}`) + '?', i18n.global.t('commons.msg.deleteTitle'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: confirmType,
|
||||
draggable: true,
|
||||
}).then(async () => {
|
||||
const res = await api(params);
|
||||
if (!res) return reject(false);
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: i18n.global.t('commons.msg.deleteSuccess'),
|
||||
})
|
||||
.then(async () => {
|
||||
const res = await api(params);
|
||||
if (!res) return reject(false);
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: i18n.global.t('commons.msg.deleteSuccess'),
|
||||
});
|
||||
resolve(true);
|
||||
})
|
||||
.finally(() => {
|
||||
loading = false;
|
||||
});
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
64
frontend/src/views/file-management/change-role.vue
Normal file
64
frontend/src/views/file-management/change-role.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="open"
|
||||
:before-close="handleClose"
|
||||
:title="$t('file.setRole')"
|
||||
width="30%"
|
||||
@open="onOpen"
|
||||
v-loading="loading"
|
||||
>
|
||||
<FileRole :mode="mode" @get-mode="getMode"></FileRole>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit()">{{ $t('commons.button.confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { File } from '@/api/interface/file';
|
||||
import { ChangeFileMode } from '@/api/modules/files';
|
||||
import i18n from '@/lang';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import FileRole from '@/components/file-role/index.vue';
|
||||
interface Props {
|
||||
open: boolean;
|
||||
file: Object;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
let form = ref<File.FileCreate>({ path: '', isDir: false, mode: 0o755 });
|
||||
let loading = ref<Boolean>(false);
|
||||
let mode = ref('0755');
|
||||
|
||||
const em = defineEmits(['close']);
|
||||
const handleClose = () => {
|
||||
em('close', false);
|
||||
};
|
||||
|
||||
const onOpen = () => {
|
||||
const f = props.file as File.FileCreate;
|
||||
form.value.isDir = f.isDir;
|
||||
form.value.path = f.path;
|
||||
mode.value = String(f.mode);
|
||||
};
|
||||
|
||||
const getMode = (val: number) => {
|
||||
form.value.mode = val;
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
loading.value = true;
|
||||
ChangeFileMode(form.value)
|
||||
.then(() => {
|
||||
ElMessage.success(i18n.global.t('commons.msg.updateSuccess'));
|
||||
handleClose();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
</script>
|
@ -7,12 +7,13 @@
|
||||
@open="onOpen"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-form ref="fileForm" label-position="left" :model="form" label-width="100px">
|
||||
<el-form-item :label="$t('file.path')"> <el-input v-model="form.path" /></el-form-item>
|
||||
<el-form ref="fileForm" label-position="left" :model="form" label-width="100px" :rules="rules">
|
||||
<el-form-item :label="$t('file.path')"> <el-input v-model="getPath" disabled /></el-form-item>
|
||||
<el-form-item :label="$t('file.name')"> <el-input v-model="name" /></el-form-item>
|
||||
<el-checkbox v-model="isLink" :label="$t('file.link')"></el-checkbox>
|
||||
</el-form>
|
||||
<el-checkbox v-model="setRole" :label="$t('file.setRole')"></el-checkbox>
|
||||
<FileRole v-if="setRole" :mode="'0775'" @get-mode="getMode"></FileRole>
|
||||
<FileRole v-if="setRole" :mode="'0755'" @get-mode="getMode"></FileRole>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
|
||||
@ -23,17 +24,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { toRefs, ref } from 'vue';
|
||||
import { toRefs, ref, reactive, computed } from 'vue';
|
||||
import { File } from '@/api/interface/file';
|
||||
import { ElMessage, FormInstance } from 'element-plus';
|
||||
import { ElMessage, FormInstance, FormRules } from 'element-plus';
|
||||
import { CreateFile } from '@/api/modules/files';
|
||||
import i18n from '@/lang';
|
||||
import FileRole from '@/components/file-role/index.vue';
|
||||
import { Rules } from '@/global/form-rues';
|
||||
|
||||
const fileForm = ref<FormInstance>();
|
||||
let loading = ref<Boolean>(false);
|
||||
let setRole = ref<Boolean>(false);
|
||||
let isLink = ref<Boolean>(false);
|
||||
let loading = ref(false);
|
||||
let setRole = ref(false);
|
||||
let isLink = ref(false);
|
||||
let name = ref('');
|
||||
let path = ref('');
|
||||
|
||||
const props = defineProps({
|
||||
open: Boolean,
|
||||
@ -46,10 +50,18 @@ const handleClose = () => {
|
||||
em('close', open);
|
||||
};
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
name: [Rules.required],
|
||||
});
|
||||
|
||||
const getMode = (val: number) => {
|
||||
form.value.mode = val;
|
||||
};
|
||||
|
||||
let getPath = computed(() => {
|
||||
return path.value + '/' + name.value;
|
||||
});
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
@ -57,6 +69,7 @@ const submit = async (formEl: FormInstance | undefined) => {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
form.value.path = getPath.value;
|
||||
CreateFile(form.value)
|
||||
.then(() => {
|
||||
ElMessage.success(i18n.global.t('commons.msg.createSuccess'));
|
||||
@ -72,5 +85,7 @@ const onOpen = () => {
|
||||
const f = file?.value as File.FileCreate;
|
||||
form.value.isDir = f.isDir;
|
||||
form.value.path = f.path;
|
||||
path.value = f.path;
|
||||
name.value = '';
|
||||
};
|
||||
</script>
|
||||
|
@ -54,9 +54,6 @@
|
||||
<el-dropdown-item command="file">
|
||||
<svg-icon iconName="p-file-normal"></svg-icon>{{ $t('file.file') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="link">
|
||||
<svg-icon iconName="p-file-normal"></svg-icon>{{ $t('file.linkFile') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@ -74,7 +71,11 @@
|
||||
<el-link :underline="false" @click="open(row)">{{ row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.mode')" prop="mode"> </el-table-column>
|
||||
<el-table-column :label="$t('file.mode')" prop="mode">
|
||||
<template #default="{ row }">
|
||||
<el-link :underline="false" @click="openMode(row)">{{ row.mode }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.user')" prop="user"> </el-table-column>
|
||||
<el-table-column :label="$t('file.group')" prop="group"> </el-table-column>
|
||||
<el-table-column :label="$t('file.size')" prop="size"> </el-table-column>
|
||||
@ -95,7 +96,8 @@
|
||||
/>
|
||||
</ComplexTable>
|
||||
</el-col>
|
||||
<CreateFile :open="openCreate" :file="fileCreate" @close="close"></CreateFile>
|
||||
<CreateFile :open="openCreate" :file="fileCreate" @close="closeCreate"></CreateFile>
|
||||
<ChangeRole :open="openModePage" :file="modeForm" @close="closeMode"></ChangeRole>
|
||||
</el-row>
|
||||
</LayoutContent>
|
||||
</template>
|
||||
@ -111,6 +113,7 @@ import { File } from '@/api/interface/file';
|
||||
import BreadCrumbs from '@/components/bread-crumbs/index.vue';
|
||||
import BreadCrumbItem from '@/components/bread-crumbs/bread-crumbs-item.vue';
|
||||
import CreateFile from './create.vue';
|
||||
import ChangeRole from './change-role.vue';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
|
||||
let data = ref();
|
||||
@ -123,6 +126,8 @@ let fileTree = ref<File.FileTree[]>([]);
|
||||
let expandKeys = ref<string[]>([]);
|
||||
let openCreate = ref<boolean>(false);
|
||||
let fileCreate = ref<File.FileCreate>({ path: '/', isDir: false, mode: 0o755 });
|
||||
let openModePage = ref<boolean>(false);
|
||||
let modeForm = ref<File.FileCreate>({ path: '/', isDir: false, mode: 0o755 });
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
@ -228,24 +233,25 @@ const handleCreate = (commnad: string) => {
|
||||
};
|
||||
|
||||
const delFile = async (row: File.File | null) => {
|
||||
// let ids: Array<number> = [];
|
||||
|
||||
// if (row === null) {
|
||||
// selects.value.forEach((item: File.File) => {
|
||||
// ids.push(item.id);
|
||||
// });
|
||||
// } else {
|
||||
// ids.push(row.id);
|
||||
// }
|
||||
await useDeleteData(DeleteFile, row as File.FileDelete, 'commons.msg.delete');
|
||||
await useDeleteData(DeleteFile, row as File.FileDelete, 'commons.msg.delete', loading.value);
|
||||
search(req);
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
const closeCreate = () => {
|
||||
openCreate.value = false;
|
||||
search(req);
|
||||
};
|
||||
|
||||
const openMode = (item: File.File) => {
|
||||
modeForm.value = item;
|
||||
openModePage.value = true;
|
||||
};
|
||||
|
||||
const closeMode = () => {
|
||||
openModePage.value = false;
|
||||
search(req);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search(req);
|
||||
});
|
||||
@ -257,6 +263,7 @@ const buttons = [
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('file.mode'),
|
||||
click: openMode,
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('file.zip'),
|
||||
|
Loading…
Reference in New Issue
Block a user