feat: 文件支持只读、读/写细化设置

This commit is contained in:
kuaifan 2021-12-29 08:38:45 +08:00
parent cef6646f50
commit 2b0467e00f
6 changed files with 213 additions and 132 deletions

View File

@ -35,12 +35,7 @@ class FileController extends AbstractController
$pid = intval($data['pid']); $pid = intval($data['pid']);
// //
if ($pid > 0) { if ($pid > 0) {
$file = File::find($pid); File::allowFind($pid);
if (empty($file)) {
return Base::retError('Not exist');
}
$file->exceAllow($user->userid);
//
$builder = File::wherePid($pid); $builder = File::wherePid($pid);
} else { } else {
$builder = File::whereUserid($user->userid); $builder = File::whereUserid($user->userid);
@ -61,13 +56,11 @@ class FileController extends AbstractController
} }
} else { } else {
// 获取共享相关 // 获取共享相关
$list = File::where('userid', '!=', $user->userid)->where(function ($query) use ($user) { $list = File::select(['files.*', 'file_users.permission'])
$query->where('share', 1)->orWhere(function ($q2) use ($user) { ->join('file_users', 'files.id', '=', 'file_users.file_id')
$q2->where('share', 2)->whereIn('id', function ($q3) use ($user) { ->where('files.userid', '!=', $user->userid)
$q3->select('file_id')->from('file_users')->where('userid', $user->userid); ->where('file_users.userid', $user->userid)
}); ->get();
});
})->get();
if ($list->isNotEmpty()) { if ($list->isNotEmpty()) {
foreach ($list as $file) { foreach ($list as $file) {
$temp = $file->toArray(); $temp = $file->toArray();
@ -512,34 +505,35 @@ class FileController extends AbstractController
return Base::retError('仅限所有者操作'); return Base::retError('仅限所有者操作');
} }
// //
$userids = FileUser::whereFileId($file->id)->pluck('userid')->toArray(); $list = FileUser::whereFileId($file->id)->get();
// //
return Base::retSuccess('success', [ return Base::retSuccess('success', [
'id' => $file->id, 'id' => $file->id,
'userids' => $userids 'list' => $list
]); ]);
} }
/** /**
* 获取共享信息 * 设置共享
* *
* @apiParam {Number} id 文件ID * @apiParam {Number} id 文件ID
* @apiParam {String} action 动作
* - share: 设置共享
* - unshare: 取消共享
* @apiParam {Number} [share] 共享对象
* - 1: 共享给所有人(限管理员)
* - 2: 共享给指定成员
* @apiParam {Array} [userids] 共享成员,格式: [userid1, userid2, userid3] * @apiParam {Array} [userids] 共享成员,格式: [userid1, userid2, userid3]
* @apiParam {Number} [permission] 共享方式
* - 0:只读
* - 1:读写
* - -1: 删除
*/ */
public function share__update() public function share__update()
{ {
$user = User::auth(); $user = User::auth();
// //
$id = intval(Request::input('id')); $id = intval(Request::input('id'));
$action = Request::input('action');
$share = intval(Request::input('share'));
$userids = Request::input('userids'); $userids = Request::input('userids');
$permission = intval(Request::input('permission'));
//
if (!in_array($permission, [-1, 0, 1])) {
return Base::retError('参数错误');
}
// //
$file = File::whereId($id)->first(); $file = File::whereId($id)->first();
if (empty($file)) { if (empty($file)) {
@ -553,56 +547,45 @@ class FileController extends AbstractController
return Base::retError('已经处于共享文件夹中'); return Base::retError('已经处于共享文件夹中');
} }
// //
if ($action == 'unshare') { if (!is_array($userids) || empty($userids)) {
// 取消共享 return Base::retError('请选择共享对象');
if ($file->share == 1) {
$uids = WebSocket::select(['userid'])->pluck('userid')->toArray();
} else {
$uids = FileUser::whereFileId($file->id)->pluck('userid')->toArray();
} }
$uids = array_values(array_diff($uids, [$user->userid]));
// //
$file->setShare(0);
$message = '取消成功';
} else {
// 设置共享
switch ($share) {
case 1:
$user->isAdmin();
break;
case 2:
$array = []; $array = [];
if (is_array($userids)) { if ($permission === -1) {
// 取消共享
$action = "delete";
foreach ($userids as $userid) { foreach ($userids as $userid) {
if (!intval($userid)) continue; if (!intval($userid)) continue;
if (!User::whereUserid($userid)->exists()) continue; if (FileUser::where([
FileUser::updateInsert([
'file_id' => $file->id, 'file_id' => $file->id,
'userid' => $userid, 'userid' => $userid,
]); ])->delete()) {
$array[] = $userid; $array[] = $userid;
} }
} }
if (empty($array)) { } else {
return Base::retError('请选择共享成员'); // 设置共享
$action = "update";
if (FileUser::whereFileId($file->id)->count() + count($userids) > 100) {
return Base::retError('共享人数上限100个成员');
}
foreach ($userids as $userid) {
if (!intval($userid)) continue;
if (!User::whereUserid($userid)->exists()) continue;
if (FileUser::updateInsert([
'file_id' => $file->id,
'userid' => $userid,
], [
'permission' => $permission,
])) {
$array[] = $userid;
} }
$builder = FileUser::whereFileId($file->id)->whereNotIn('userid', $array);
$uids = (clone $builder)->pluck('userid')->toArray();
$builder->delete();
break;
default:
return Base::retError('请选择共享对象');
} }
$file->setShare($share);
$message = '设置成功';
} }
// //
$file->pushMsg('update', $file); $file->setShare();
if (isset($uids)) { $file->pushMsg($action, $action == "delete" ? null : $file, $array);
$file->pushMsg('delete', null, $uids); return Base::retSuccess($action == "delete" ? "删除成功" : "设置成功", $file);
}
return Base::retSuccess($message, $file);
} }
} }

View File

@ -51,7 +51,7 @@ class File extends AbstractModel
use SoftDeletes; use SoftDeletes;
/** /**
* 是否有访问权限(没有时抛出异常) * 是否有访问权限
* @param $userid * @param $userid
*/ */
public function exceAllow($userid) public function exceAllow($userid)
@ -64,8 +64,7 @@ class File extends AbstractModel
/** /**
* 是否有访问权限 * 是否有访问权限
* 自己的文件夹 * 自己的文件夹
* 共享所有人的文件夹 * 在指定共享成员内
* 在指定共享人员内
* @param $userid * @param $userid
* @return bool * @return bool
*/ */
@ -77,16 +76,11 @@ class File extends AbstractModel
} }
$row = $this->getShareInfo(); $row = $this->getShareInfo();
if ($row) { if ($row) {
if ($row->share == 1) {
// ② 共享所有人的文件夹
return true;
} elseif ($row->share == 2) {
// ③ 在指定共享人员内
if (FileUser::whereFileId($row->id)->whereUserid($userid)->exists()) { if (FileUser::whereFileId($row->id)->whereUserid($userid)->exists()) {
// ② 在指定共享成员内
return true; return true;
} }
} }
}
return false; return false;
} }
@ -96,7 +90,7 @@ class File extends AbstractModel
*/ */
public function getShareInfo() public function getShareInfo()
{ {
if ($this->share > 0) { if ($this->share) {
return $this; return $this;
} }
$pid = $this->pid; $pid = $this->pid;
@ -105,7 +99,7 @@ class File extends AbstractModel
if (empty($row)) { if (empty($row)) {
break; break;
} }
if ($row->share > 0) { if ($row->share) {
return $row; return $row;
} }
$pid = $row->pid; $pid = $row->pid;
@ -125,7 +119,7 @@ class File extends AbstractModel
if (empty($row)) { if (empty($row)) {
break; break;
} }
if ($row->share > 0) { if ($row->share) {
return true; return true;
} }
$pid = $row->pid; $pid = $row->pid;
@ -138,8 +132,12 @@ class File extends AbstractModel
* @param $share * @param $share
* @return bool * @return bool
*/ */
public function setShare($share) public function setShare($share = null)
{ {
if ($share === null) {
$share = FileUser::whereFileId($this->id)->count() == 0 ? 0 : 1;
}
if ($this->share != $share) {
AbstractModel::transaction(function () use ($share) { AbstractModel::transaction(function () use ($share) {
$this->share = $share; $this->share = $share;
$this->save(); $this->save();
@ -150,6 +148,7 @@ class File extends AbstractModel
} }
} }
}); });
}
return true; return true;
} }

View File

@ -4,12 +4,12 @@ namespace App\Models;
/** /**
* Class FileUser * App\Models\FileUser
* *
* @package App\Models
* @property int $id * @property int $id
* @property int|null $file_id 项目ID * @property int|null $file_id 项目ID
* @property int|null $userid 成员ID * @property int|null $userid 成员ID
* @property int|null $permission 权限0只读1读写
* @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|FileUser newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|FileUser newModelQuery()
@ -18,6 +18,7 @@ namespace App\Models;
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|FileUser whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereFileId($value) * @method static \Illuminate\Database\Eloquent\Builder|FileUser whereFileId($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|FileUser whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileUser wherePermission($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|FileUser whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|FileUser whereUserid($value) * @method static \Illuminate\Database\Eloquent\Builder|FileUser whereUserid($value)
* @mixin \Eloquent * @mixin \Eloquent

View File

@ -5,6 +5,7 @@
:transfer="transfer" :transfer="transfer"
:remote-method="searchUser" :remote-method="searchUser"
:placeholder="placeholder" :placeholder="placeholder"
:size="size"
:loading="loading" :loading="loading"
:loading-text="$L('加载中...')" :loading-text="$L('加载中...')"
:default-label="value" :default-label="value"
@ -17,6 +18,7 @@
@on-open-change="openChange" @on-open-change="openChange"
@on-set-default-options="setDefaultOptions"> @on-set-default-options="setDefaultOptions">
<div v-if="multipleMax" slot="drop-prepend" class="user-drop-prepend">{{$L('最多只能选择' + multipleMax + '')}}</div> <div v-if="multipleMax" slot="drop-prepend" class="user-drop-prepend">{{$L('最多只能选择' + multipleMax + '')}}</div>
<slot name="option-prepend"></slot>
<Option <Option
v-for="(item, key) in list" v-for="(item, key) in list"
:value="item.userid" :value="item.userid"
@ -60,6 +62,9 @@
placeholder: { placeholder: {
default: '' default: ''
}, },
size: {
default: 'default'
},
transfer: { transfer: {
type: Boolean, type: Boolean,
default: true default: true

View File

@ -198,21 +198,46 @@
<Modal <Modal
v-model="shareShow" v-model="shareShow"
:title="$L('共享设置')" :title="$L('共享设置')"
:mask-closable="false"> :mask-closable="false"
<Form ref="addProject" :model="shareInfo" label-width="auto" @submit.native.prevent> footer-hide>
<FormItem prop="type" :label="$L('共享对象')"> <Form class="page-file-share-form" :model="shareInfo" @submit.native.prevent inline>
<RadioGroup v-model="shareInfo.share"> <FormItem prop="userids" class="share-userid">
<Radio v-if="userIsAdmin" :label="1">{{$L('所有人')}}</Radio> <UserInput
<Radio :label="2">{{$L('指定成员')}}</Radio> v-model="shareInfo.userids"
</RadioGroup> :disabledChoice="shareAlready"
:multiple-max="100"
:placeholder="$L('选择共享成员')">
<Option slot="option-prepend" :value="0" :label="$L('所有人')">
<div class="user-input-option">
<div class="user-input-avatar"><EAvatar class="avatar" style="line-height:26px" icon="el-icon-s-custom"/></div>
<div class="user-input-nickname">{{ $L('所有人') }}</div>
<div class="user-input-userid">All</div>
</div>
</Option>
</UserInput>
</FormItem> </FormItem>
<FormItem v-if="shareInfo.share === 2" prop="userids" :label="$L('共享成员')"> <FormItem>
<UserInput v-if="!shareInfo.userTmpHide" v-model="shareInfo.userids" :disabledChoice="[shareInfo.userid]" :multiple-max="100" :placeholder="$L('选择共享成员')"/> <Select v-model="shareInfo.permission" :placeholder="$L('权限')">
<Option :value="1">{{$L('读/写')}}</Option>
<Option :value="0">{{$L('只读')}}</Option>
</Select>
</FormItem>
<FormItem>
<Button type="primary" :loading="shareLoad > 0" @click="onShare">{{$L('共享')}}</Button>
</FormItem> </FormItem>
</Form> </Form>
<div slot="footer" class="adaption"> <div v-if="shareList.length > 0">
<Button type="default" :loading="shareLoad > 0" @click="onShare(false)">{{$L('取消共享')}}</Button> <div class="page-file-share-title">{{ $L('已共享成员') }}:</div>
<Button type="primary" :loading="shareLoad > 0" @click="onShare(true)">{{$L('设置共享')}}</Button> <ul class="page-file-share-list">
<li v-for="item in shareList">
<UserAvatar :size="32" :userid="item.userid" show-name tooltip-disabled/>
<Select v-model="item.permission" :placeholder="$L('权限')" @on-change="upShare(item)">
<Option :value="1">{{ $L('读/写') }}</Option>
<Option :value="0">{{ $L('只读') }}</Option>
<Option :value="-1" class="delete">{{ $L('删除') }}</Option>
</Select>
</li>
</ul>
</div> </div>
</Modal> </Modal>
@ -308,7 +333,8 @@ export default {
columns: [], columns: [],
shareShow: false, shareShow: false,
shareInfo: {}, shareInfo: {id: 0, userid: 0, permission: 1},
shareList: [],
shareLoad: 0, shareLoad: 0,
editShow: false, editShow: false,
@ -381,6 +407,14 @@ export default {
return null; return null;
}, },
shareAlready() {
let data = this.shareList ? this.shareList.map(({userid}) => userid) : [];
if (this.shareInfo.userid) {
data.push(this.shareInfo.userid);
}
return data
},
fileList() { fileList() {
const {files, searchKey, pid} = this; const {files, searchKey, pid} = this;
return sortBy(files.filter((file) => { return sortBy(files.filter((file) => {
@ -747,15 +781,10 @@ export default {
case 'share': case 'share':
this.shareInfo = { this.shareInfo = {
id: item.id, id: item.id,
name: item.name,
userid: item.userid, userid: item.userid,
share: item.share, permission: 1,
_share: item.share,
}; };
if (!this.userIsAdmin) { this.shareList = [];
//
this.shareInfo.share = 2;
}
this.shareShow = true; this.shareShow = true;
this.getShare(); this.getShare();
break; break;
@ -899,10 +928,10 @@ export default {
}).then(({data}) => { }).then(({data}) => {
this.shareLoad--; this.shareLoad--;
if (data.id == this.shareInfo.id) { if (data.id == this.shareInfo.id) {
this.shareInfo = Object.assign({userTmpHide: true}, this.shareInfo, data); this.shareList = data.list.map(item => {
this.$nextTick(() => { item._permission = item.permission;
this.$set(this.shareInfo, 'userTmpHide', false); return item;
}) });
} }
}).catch(({msg}) => { }).catch(({msg}) => {
this.shareLoad--; this.shareLoad--;
@ -911,32 +940,58 @@ export default {
}) })
}, },
onShare(share) { onShare() {
if (!share && !this.shareInfo._share) { if (this.shareInfo.userids.length == 0) {
this.shareShow = false; $A.messageWarning("请选择共享成员")
return;
}
if (![1, 2].includes(this.shareInfo.share)) {
$A.messageWarning("请选择共享对象")
return; return;
} }
this.shareLoad++; this.shareLoad++;
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'file/share/update', url: 'file/share/update',
data: Object.assign(this.shareInfo, { data: this.shareInfo,
action: share ? 'share' : 'unshare'
}),
}).then(({data, msg}) => { }).then(({data, msg}) => {
this.shareLoad--; this.shareLoad--;
this.shareShow = false;
$A.messageSuccess(msg) $A.messageSuccess(msg)
this.$store.dispatch("saveFile", data); this.$store.dispatch("saveFile", data);
this.$set(this.shareInfo, 'userids', []);
this.getShare();
}).catch(({msg}) => { }).catch(({msg}) => {
this.shareLoad--; this.shareLoad--;
$A.modalError(msg) $A.modalError(msg)
}) })
}, },
upShare(item) {
if (item.loading === true) {
return;
}
item.loading = true;
//
this.$store.dispatch("call", {
url: 'file/share/update',
data: {
id: this.shareInfo.id,
userids: [item.userid],
permission: item.permission,
},
}).then(({data, msg}) => {
item.loading = false;
item._permission = item.permission;
$A.messageSuccess(msg);
this.$store.dispatch("saveFile", data);
if (item.permission === -1) {
let index = this.shareList.findIndex(({userid}) => userid == item.userid);
if (index > -1) {
this.shareList.splice(index, 1)
}
}
}).catch(({msg}) => {
item.loading = false;
item.permission = item._permission;
$A.modalError(msg)
})
},
/********************文件上传部分************************/ /********************文件上传部分************************/
uploadUpdate(fileList) { uploadUpdate(fileList) {

View File

@ -703,6 +703,44 @@
} }
} }
.page-file-share-form {
display: flex;
margin-bottom: 12px;
.share-userid {
flex: 1;
}
> div {
flex-shrink: 0;
&:last-child {
margin-right: 0;
}
}
}
.page-file-share-title {
margin-top: -8px;
margin-bottom: 14px;
padding: 0 2px;
}
.page-file-share-list {
max-height: 500px;
margin-bottom: 24px;
> li {
display: flex;
align-items: center;
margin-bottom: 12px;
.common-avatar {
flex: 1;
}
.ivu-select {
width: auto;
flex-shrink: 0;
}
.delete {
color: #ff0000;
}
}
}
.page-file-drawer { .page-file-drawer {
.overlay-content { .overlay-content {
border-radius: 20px 20px 0 0 !important; border-radius: 20px 20px 0 0 !important;