diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index 415916d20..9b572c9a2 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -1993,7 +1993,7 @@ class DialogController extends AbstractController } /** - * @api {post} api/dialog/msg/wordchain 15. 发送接龙消息 + * @api {post} api/dialog/msg/wordchain 44. 发送接龙消息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 @@ -2018,8 +2018,6 @@ class DialogController extends AbstractController $text = trim(Request::input('text')); $list = Request::input('list'); // - $result = []; - // WebSocketDialog::checkDialog($dialog_id); $strlen = mb_strlen($text); $noimglen = mb_strlen(preg_replace("/]*?>/i", "", $text)); @@ -2054,8 +2052,118 @@ class DialogController extends AbstractController 'userid' => $userid, 'uuid' => $uuid ?: Base::generatePassword(36), ]; - $result = WebSocketDialogMsg::sendMsg(null, $dialog_id, 'word-chain', $msgData, $user->userid); - // - return $result; + return WebSocketDialogMsg::sendMsg(null, $dialog_id, 'word-chain', $msgData, $user->userid); } + + /** + * @api {post} api/dialog/msg/vote 45. 发起投票 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup dialog + * @apiName msg__vote + * + * @apiParam {Number} dialog_id 对话ID + * @apiParam {String} text 投票内容 + * @apiParam {Array} type 投票类型 + * @apiParam {String} [uuid] 投票ID + * @apiParam {Array} [list] 投票列表 + * @apiParam {Number} [multiple] 多选 + * @apiParam {Number} [anonymous] 匿名 + * @apiParam {Array} [vote] 投票 + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function msg__vote() + { + $user = User::auth(); + // + $dialog_id = intval(Request::input('dialog_id')); + $uuid = trim(Request::input('uuid')); + $text = trim(Request::input('text')); + $type = trim(Request::input('type','create')); + $multiple = intval(Request::input('multiple')) ?: 0; + $anonymous = intval(Request::input('anonymous')) ?: 0; + $list = Request::input('list'); + $vote = Request::input('vote') ?: []; + $votes = is_array($vote) ? $vote : [$vote]; + // + WebSocketDialog::checkDialog($dialog_id); + // + $action = null; + $userid = $user->userid; + $result = []; + if($type != 'create'){ + if($type == 'vote' && empty($votes)){ + return Base::retError('参数错误'); + } + if (empty($uuid)) { + return Base::retError('参数错误'); + } + $dialogMsgs = WebSocketDialogMsg::whereDialogId($dialog_id) + ->whereType('vote') + ->orderByDesc('created_at') + ->where('msg','like',"%$uuid%") + ->get(); + // + if($type == 'again'){ + $res = WebSocketDialogMsg::sendMsg(null, $dialog_id, 'vote', $dialogMsgs[0]->msg, $user->userid); + if(Base::isError($res)){ + return $res; + } + $result[] = $res['data']; + }else{ + foreach($dialogMsgs as $dialogMsg){ + $action = "change-{$dialogMsg->id}"; + $msgData = $dialogMsg->msg; + if($type == 'finish'){ + $msgData['state'] = 0; + }else{ + $msgDataVotes = $msgData['votes'] ?? []; + if(in_array($userid,array_column($msgDataVotes,'userid'))){ + return Base::retError('不能重复投票'); + } + $msgDataVotes[] = [ + 'userid' => $userid, + 'votes' => $votes, + ]; + $msgData['votes'] = $msgDataVotes; + } + $res = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'vote', $msgData, $user->userid); + if(Base::isError($res)){ + return $res; + } + $result[] = $res['data']; + } + } + }else{ + $strlen = mb_strlen($text); + $noimglen = mb_strlen(preg_replace("/]*?>/i", "", $text)); + if ($strlen < 1) { + return Base::retError('内容不能为空'); + } + if ($noimglen > 200000) { + return Base::retError('内容最大不能超过200000字'); + } + $msgData = [ + 'text' => $text, + 'list' => $list, + 'userid' => $userid, + 'uuid' => $uuid ?: Base::generatePassword(36), + 'multiple' => $multiple, + 'anonymous' => $anonymous, + 'votes' => [], + 'state' => 1 + ]; + $res = WebSocketDialogMsg::sendMsg($action, $dialog_id, 'vote', $msgData, $user->userid); + if(Base::isError($res)){ + return $res; + } + $result[] = $res['data']; + } + return Base::retSuccess('发送成功', $result); + } + } diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php index 909e0caf4..76856da6b 100644 --- a/app/Models/WebSocketDialogMsg.php +++ b/app/Models/WebSocketDialogMsg.php @@ -522,6 +522,7 @@ class WebSocketDialogMsg extends AbstractModel switch ($data['type']) { case 'text': case 'word-chain': + case 'vote': return $this->previewTextMsg($data['msg']['text'], $preserveHtml); case 'record': return "[语音]"; @@ -860,10 +861,10 @@ class WebSocketDialogMsg extends AbstractModel if (empty($dialogMsg)) { throw new ApiException('消息不存在'); } - if ($dialogMsg->type !== 'text') { + if ($dialogMsg->type !== 'text' && $dialogMsg->type !== 'vote') { throw new ApiException('此消息不支持此操作'); } - if ($dialogMsg->userid != $sender) { + if ($dialogMsg->userid != $sender && $dialogMsg->type !== 'vote') { throw new ApiException('仅支持修改自己的消息'); } // diff --git a/language/original-api.txt b/language/original-api.txt index 6c645b314..18cb1584c 100644 --- a/language/original-api.txt +++ b/language/original-api.txt @@ -480,3 +480,5 @@ Api接口文档 保存任务详情至文件失败 保存任务详情至文件失败,请重试 移动成功 + +不能重复投票 diff --git a/language/original-web.txt b/language/original-web.txt index 002a01f4c..4b480a8fe 100644 --- a/language/original-web.txt +++ b/language/original-web.txt @@ -1434,6 +1434,7 @@ APP推送 发起接龙 由 发起接龙,参与接龙目前共(*)人 +请输入接龙主题 请输入接龙内容 可填写接龙格式 重复内容将不再计入接龙结果 @@ -1443,3 +1444,24 @@ APP推送 接龙结果 选择群组发起接龙 来自 +发起投票 +投票结果 +发起 +请输入投票主题 +请输入选项内容 +允许多选 +匿名投票 +投票 +匿名 +实名 +单选 +多选 +请选择后投票 +立即投票 +票 +再次发送 +再次发送投票? +结束投票 +确定结束投票? +已发送 +选择群组发起投票 diff --git a/public/images/application/vote.svg b/public/images/application/vote.svg new file mode 100644 index 000000000..f6ea4a14e --- /dev/null +++ b/public/images/application/vote.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/public/images/application/word-chain.svg b/public/images/application/word-chain.svg index c9aa05f06..278f49e1c 100644 --- a/public/images/application/word-chain.svg +++ b/public/images/application/word-chain.svg @@ -1 +1,38 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/assets/js/functions/web.js b/resources/assets/js/functions/web.js index cfcc4d9cd..9b3703ee0 100755 --- a/resources/assets/js/functions/web.js +++ b/resources/assets/js/functions/web.js @@ -818,8 +818,11 @@ if ($A.isJson(data)) { switch (data.type) { case 'text': - case 'word-chain': return $A.getMsgTextPreview(data.msg.text, imgClassName) + case 'word-chain': + return `[${$A.L('接龙')}]` + $A.getMsgTextPreview(data.msg.text, imgClassName) + case 'vote': + return `[${$A.L('投票')}]` + $A.getMsgTextPreview(data.msg.text, imgClassName) case 'record': return `[${$A.L('语音')}]` case 'meeting': diff --git a/resources/assets/js/pages/manage/application.vue b/resources/assets/js/pages/manage/application.vue index 4991dcc5e..f3af9012d 100644 --- a/resources/assets/js/pages/manage/application.vue +++ b/resources/assets/js/pages/manage/application.vue @@ -191,11 +191,11 @@ { h.type = 'admin'; return h; }); // - this.applyList = [...applyList, ...appApplyList, ...adminApplyList]; + this.applyList = [...applyList, ...appApplyList, ...adminApplyList].sort((a, b) => { + if (a.sort < b.sort) { + return -1; + } else if (a.sort > b.sort) { + return 1; + } else { + return 0; + } + }); }, getLogoPath(name) { name = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); @@ -418,12 +427,10 @@ export default { $A.eeuiAppScan(this.scanResult); return; case 'word-chain': - this.sendData = []; - this.$refs.wordChainRef.onSelection() - return; case 'vote': this.sendData = []; - this.$refs.wordChainRef.onSelection() + this.sendType = item.value; + this.$refs.wordChainAndVoteRef.onSelection() return; } this.$emit("on-click", item.value) @@ -554,12 +561,12 @@ export default { }, }); }, - // 前往接龙 - onWordChain(){ + // 前往接龙与投票 + goWordChainAndVote(){ const dialog_id = Number(this.sendData[0].replace('d:', '')) if(this.windowPortrait){ this.$store.dispatch("openDialog", dialog_id ).then(() => { - this.$store.state.dialogDroupWordChain = { + this.$store.state[ this.sendType == 'word-chain' ?'dialogDroupWordChain' : 'dialogGroupVote'] = { type: 'create', dialog_id: dialog_id } @@ -567,7 +574,7 @@ export default { }else{ this.goForward({ name: 'manage-messenger', params: { dialog_id: dialog_id}}); setTimeout(()=>{ - this.$store.state.dialogDroupWordChain = { + this.$store.state[ this.sendType == 'word-chain' ?'dialogDroupWordChain' : 'dialogGroupVote'] = { type: 'create', dialog_id: dialog_id } diff --git a/resources/assets/js/pages/manage/components/DialogGroupVote.vue b/resources/assets/js/pages/manage/components/DialogGroupVote.vue index bf0e527b6..216510b53 100644 --- a/resources/assets/js/pages/manage/components/DialogGroupVote.vue +++ b/resources/assets/js/pages/manage/components/DialogGroupVote.vue @@ -13,7 +13,7 @@ {{ $L('取消') }}
- {{ dialogGroupVote.type == 'create' ? $L('发起接龙') : $L('接龙结果') }} + {{ dialogGroupVote.type == 'create' ? $L('发起投票') : $L('投票结果') }}
@@ -44,15 +44,21 @@
  • - - - - + +
  • - +
+
+ {{ $L('允许多选') }} + +
+
+ {{ $L('匿名投票') }} + +
@@ -73,6 +79,8 @@ export default { createId: 0, value: "", list: [], + multiple: 0, + anonymous: 0, oldData: '', loadIng: 0, @@ -131,12 +139,10 @@ export default { this.createId = this.userId; this.list.push({ id: Date.now(), - userid: this.userId, text: "" }); this.list.push({ id: Date.now() + 1, - userid: this.userId, text: "" }); } @@ -151,23 +157,18 @@ export default { }, methods: { - add(){ + onAdd(){ this.list.push({ id: Date.now(), - type: 'text', - userid: this.userId, - text: this.userInfo.nickname, + text: "", }); this.scrollTo(); }, - del(){ - this.list.push({ - id: Date.now(), - type: 'text', - userid: this.userId, - text: this.userInfo.nickname, - }); + onDel(index){ + if( this.list.length > 2 ){ + this.list.splice(index, 1); + } }, scrollTo(){ @@ -177,44 +178,30 @@ export default { }, onSend() { - if( !this.isEdit ){ + if(!this.isEdit){ return; } - const texts = this.list.map(h=> h.text); - if( texts.length != [...new Set(texts)].length ){ - $A.modalConfirm({ - content: '重复内容将不再计入接龙结果', - cancelText: '返回编辑', - okText: '继续发送', - onOk: () => { - this.send() - } - }) + // + if(!this.value){ + $A.messageError("请输入投票主题"); + return; + } + if(this.list.find(h=> !h.text)){ + $A.messageError("请输入选项内容"); return; } - this.send(); - }, - - /** - * 发送消息 - */ - send() { - const list = []; - this.list.forEach(h=>{ - if( list.map(h=> h.text).indexOf(h.text) == -1){ - list.push(h); - } - }); // this.loadIng++; this.$store.dispatch("call", { - url: 'dialog/msg/wordchain', + url: 'dialog/msg/vote', method: 'post', data: { dialog_id: this.dialogGroupVote.dialog_id, text: this.value, - list: list, - uuid: this.dialogGroupVote.msgData?.msg?.uuid || '' + list: this.list, + uuid: this.dialogGroupVote.msgData?.msg?.uuid || '', + multiple: this.multiple, + anonymous: this.anonymous } }).then(({data}) => { this.show = false; diff --git a/resources/assets/js/pages/manage/components/DialogGroupWordChain.vue b/resources/assets/js/pages/manage/components/DialogGroupWordChain.vue index c568b956b..5769762bf 100644 --- a/resources/assets/js/pages/manage/components/DialogGroupWordChain.vue +++ b/resources/assets/js/pages/manage/components/DialogGroupWordChain.vue @@ -35,10 +35,16 @@ {{ $L('发起,参与接龙目前共'+num+'人') }}
- +
    -
  • +
  • {{ $L('例') }}
  • @@ -47,7 +53,7 @@
  • - +
@@ -154,7 +160,7 @@ export default { }, methods: { - add(){ + onAdd(){ this.list.push({ id: Date.now(), type: 'text', @@ -174,6 +180,16 @@ export default { if( !this.isEdit ){ return; } + // + if(!this.value){ + $A.messageError("请输入接龙主题"); + return; + } + if( this.list.find(h=> !h.text && h.type != "case") ){ + $A.messageError("请输入接龙内容"); + return; + } + // const texts = this.list.map(h=> h.text); if( texts.length != [...new Set(texts)].length ){ $A.modalConfirm({ @@ -194,20 +210,11 @@ export default { */ send() { const list = []; - let isEmpty = false; this.list.forEach(h=>{ - if(!h.text && h.type != "case"){ - isEmpty = true; - return; - } - if( h.text && list.map(h=> h.text).indexOf(h.text) == -1){ + if(h.text && list.map(h=> h.text).indexOf(h.text) == -1){ list.push(h); } }); - if(isEmpty){ - $A.messageError("请输入接龙内容"); - return; - } // this.loadIng++; this.$store.dispatch("call", { diff --git a/resources/assets/js/pages/manage/components/DialogView.vue b/resources/assets/js/pages/manage/components/DialogView.vue index 0b7065f3f..e8baca8db 100644 --- a/resources/assets/js/pages/manage/components/DialogView.vue +++ b/resources/assets/js/pages/manage/components/DialogView.vue @@ -84,6 +84,54 @@
  • {{ $L('参与接龙') }}>
  • + +
    +
    + {{ $L('投票') }} + {{ msgData.msg.multiple == 1 ? $L('多选') : $L('单选')}} + {{ msgData.msg.multiple == 1 ? $L('匿名') : $L('实名')}} +
    +
    
    +                    
    +                    
    +                
    @@ -533,6 +581,55 @@ export default { unfoldWordChain(e){ e.target.parentNode?.parentNode?.classList.add('expand') + }, + + onVote(type,msgData){ + if(type != 'vote'){ + $A.modalConfirm({ + content: type == 'finish' ? '确定结束投票?': '再次发送投票?', + cancelText: '取消', + okText: '确定', + onOk: () => { + this.vote(type,msgData); + } + }); + return; + } + this.vote(type,msgData); + }, + + vote(type,msgData){ + this.$set(msgData.msg,'_loadIng',1) + this.$store.dispatch("call", { + url: 'dialog/msg/vote', + method: 'post', + data: { + dialog_id: msgData.dialog_id, + uuid: msgData.msg.uuid, + vote: msgData.msg._vote || [], + type: type + } + }).then(({data}) => { + if(type == 'again'){ + $A.messageSuccess("已发送"); + } + data.forEach(d => { + this.$store.dispatch("saveDialogMsg", d ); + }); + }).catch(({msg}) => { + $A.modalError(msg); + }).finally(_ => { + this.$set(msgData.msg,'_loadIng',0) + }); + }, + + getVoteProgress(msgData, id){ + const num = msgData.votes.filter(h=>(h.votes || '').indexOf(id) != -1).length + let progress = '0.00'; + if(num){ + progress = (msgData.votes.length / num * 100).toFixed(2) + } + return {num, progress}; } } } diff --git a/resources/assets/sass/pages/components/dialog-droup-word-chain.scss b/resources/assets/sass/pages/components/dialog-droup-word-chain.scss index 3d1ae57c9..ddf395a9a 100644 --- a/resources/assets/sass/pages/components/dialog-droup-word-chain.scss +++ b/resources/assets/sass/pages/components/dialog-droup-word-chain.scss @@ -57,7 +57,6 @@ height: 100%; max-height: calc(100vh - 370px); .source{ - padding-top: 10px; margin-right: 32px; span{ color: #84C56A; @@ -93,26 +92,47 @@ display: flex; gap: 10px; padding: 5px 0; + color: #7f7f7f; span{ - min-width: 30px; - height: 30px; - line-height: 30px; + min-width: 28px; + height: 28px; + line-height: 28px; margin-top: 2px; background-color: #f2f2f2; border-radius: 100%; text-align: center; font-size: 12px; - color: #7f7f7f; + + } + .taskfont{ + font-size: 28px; + cursor: pointer; + line-height: 34px; + user-select: none; + transform: scale(0.92); + &.disabled{ + opacity: 0.5; + cursor: no-drop; + } } &.add{ .taskfont{ - font-size: 30px; - cursor: pointer; - line-height: 30px; - user-select: none; + line-height: 32px; + transform: scale(1); } } - + } + } + .switch-row{ + padding: 10px 5px; + margin: 0 32px 0 0; + display: flex; + border-top: 1px solid #f0f0f0; + span.label{ + flex: 1; + } + &:last-child{ + border-bottom: 1px solid #f0f0f0; } } } @@ -127,6 +147,9 @@ body.window-portrait { } .word-chain-body{ max-height: 100%; + ul{ + flex: none; + } } } } diff --git a/resources/assets/sass/pages/components/dialog-wrapper.scss b/resources/assets/sass/pages/components/dialog-wrapper.scss index 2fb41ed8e..41f7aa8ea 100644 --- a/resources/assets/sass/pages/components/dialog-wrapper.scss +++ b/resources/assets/sass/pages/components/dialog-wrapper.scss @@ -990,6 +990,86 @@ } } } + + .content-word-vote{ + min-width: 200px; + max-width: 260px; + .vote-msg-head{ + margin-bottom: 8px; + color: #0bc037; + line-height: 18px; + span{ + padding: 2px 4px; + border-radius: 3px; + background-color: #dee2fa; + margin: 0 4px; + font-size: 12px; + color: #7076e4; + } + } + .ivu-checkbox-group,.ivu-radio-group{ + margin-top: 10px; + width: 100%; + .ivu-checkbox-wrapper,.ivu-radio-wrapper{ + display: block; + width: 100%; + height: 34px; + line-height: 34px; + .ivu-checkbox-inner{ + border-radius: 100%; + } + } + } + .vote-result-body{ + font-size: 12px; + margin-top: 14px; + ul{ + list-style-type:none; + li{ + margin-bottom: 14px; + .vote-option-title{ + margin-bottom: 3px; + } + .ivu-progress-inner{ + background-color: #e2e2e2; + } + .avatar-row{ + gap: 2px; + margin-top: 2px; + display: flex; + overflow: auto; + padding-bottom: 4px; + &::-webkit-scrollbar { + background: none; + width: 6px; + height: 6px; + } + &::-webkit-scrollbar-thumb { + background: #d4d4d4; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + } + } + } + li:last-child{ + margin-bottom: 10px; + } + } + >span,.ticket-num span{ + margin-right: 10px; + } + } + .btn-row{ + display: flex; + text-align: center; + padding: 10px 0 5px 0; + gap: 10px; + .ivu-btn{ + flex: 1; + } + } + } } .dialog-emoji { @@ -1242,16 +1322,17 @@ .content-word-chain{ ul{ - li{ - .expand{ - color: #23241f; - } - } - li.participate{ + li.participate, li .expand{ color: #23241f; } } } + + .content-word-vote{ + .vote-msg-head{ + color: #23241f; + } + } } .dialog-emoji { diff --git a/resources/assets/statics/public/images/application/vote.svg b/resources/assets/statics/public/images/application/vote.svg new file mode 100644 index 000000000..f6ea4a14e --- /dev/null +++ b/resources/assets/statics/public/images/application/vote.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/resources/assets/statics/public/images/application/word-chain.svg b/resources/assets/statics/public/images/application/word-chain.svg index c9aa05f06..278f49e1c 100644 --- a/resources/assets/statics/public/images/application/word-chain.svg +++ b/resources/assets/statics/public/images/application/word-chain.svg @@ -1 +1,38 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + +