feat: 添加投票功能 - 100%

This commit is contained in:
weifashi 2023-12-08 18:05:40 +08:00
parent e9fd223808
commit 9a942c483d
15 changed files with 574 additions and 120 deletions

View File

@ -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("/<img[^>]*?>/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("/<img[^>]*?>/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);
}
}

View File

@ -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('仅支持修改自己的消息');
}
//

View File

@ -480,3 +480,5 @@ Api接口文档
保存任务详情至文件失败
保存任务详情至文件失败,请重试
移动成功
不能重复投票

View File

@ -1434,6 +1434,7 @@ APP推送
发起接龙
发起接龙,参与接龙目前共(*)人
请输入接龙主题
请输入接龙内容
可填写接龙格式
重复内容将不再计入接龙结果
@ -1443,3 +1444,24 @@ APP推送
接龙结果
选择群组发起接龙
来自
发起投票
投票结果
发起
请输入投票主题
请输入选项内容
允许多选
匿名投票
投票
匿名
实名
单选
多选
请选择后投票
立即投票
再次发送
再次发送投票?
结束投票
确定结束投票?
已发送
选择群组发起投票

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
<style type="text/css">
.st0{fill:#87D068;}
.st1{fill:#FFFFFF;}
</style>
<g>
<g>
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
</g>
<g>
<path class="st1" d="M34.4,33.5H25l9.3-9.3c1.2-1.2,1.2-3.3,0-4.5l-6.8-6.8c-1.2-1.2-3.3-1.2-4.5,0L12.9,23.1
c-1.2,1.2-1.2,3.3,0,4.5l6,6h-6c-0.5,0-0.9,0.4-0.9,0.9c0,0.5,0.4,0.9,0.9,0.9H22c0,0,0,0,0,0c0,0,0,0,0,0h12.4
c0.5,0,0.9-0.4,0.9-0.9C35.3,33.9,34.9,33.5,34.4,33.5z M17.1,25.5c0.3-0.3,0.9-0.3,1.2,0l3.4,3.4c0.1,0.1,0.3,0.1,0.4,0l6.7-6.7
c0.3-0.3,0.9-0.3,1.2,0c0.3,0.3,0.3,0.9,0,1.2l-6.7,6.7c-0.4,0.4-0.9,0.6-1.5,0.6c-0.5,0-1.1-0.2-1.5-0.6l-3.4-3.4
C16.8,26.4,16.8,25.9,17.1,25.5L17.1,25.5z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +1,38 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1701947484494" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14371" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M671.1 405.7C524.4 405.7 405 525.1 405 671.8s119.4 266.1 266.1 266.1 266.1-119.4 266.1-266.1-119.4-266.1-266.1-266.1z m74.5 298.1H703v42.6c0 17.6-14.3 31.9-31.9 31.9-17.6 0-31.9-14.3-31.9-31.9v-42.6h-42.6c-17.6 0-31.9-14.3-31.9-31.9 0-17.6 14.3-31.9 31.9-31.9h42.6v-42.6c0-17.6 14.3-31.9 31.9-31.9 17.6 0 31.9 14.3 31.9 31.9V640h42.6c17.6 0 31.9 14.3 31.9 31.9 0 17.6-14.3 31.9-31.9 31.9zM202.6 509.7c0-170.2 138.5-308.7 308.7-308.7 22.3 0 44 2.5 65 7-47.5-73.3-129.8-122-223.4-122C206.2 86 86.8 205.4 86.8 352.1c0 94.1 49.3 176.8 123.2 224.1-4.7-21.4-7.4-43.6-7.4-66.5z" p-id="14372" fill="#1296db"></path><path d="M362.4 671.8c0-170.2 138.5-308.7 308.7-308.7 22.9 0 45.2 2.7 66.6 7.4-46.9-76-130.7-127-226.4-127-146.7 0-266.1 119.4-266.1 266.1 0 94.2 49.4 177 123.5 224.3-4.2-20-6.3-40.8-6.3-62.1z" p-id="14373" fill="#1296db"></path></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
<style type="text/css">
.st0{fill:#87D068;}
.st1{clip-path:url(#SVGID_00000018941778426702510620000006481262296390689922_);}
.st2{fill:#FFFFFF;}
</style>
<g>
<g>
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
</g>
<g>
<defs>
<rect id="SVGID_1_" x="12" y="12" width="24" height="24"/>
</defs>
<clipPath id="SVGID_00000141444668484078751200000003089482387513471677_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#SVGID_00000141444668484078751200000003089482387513471677_);">
<g>
<path class="st2" d="M28.5,21c-4.1,0-7.5,3.4-7.5,7.5c0,4.1,3.4,7.5,7.5,7.5c4.1,0,7.5-3.4,7.5-7.5C36,24.4,32.6,21,28.5,21z
M30.6,29.4h-1.2v1.2c0,0.5-0.4,0.9-0.9,0.9c-0.5,0-0.9-0.4-0.9-0.9v-1.2h-1.2c-0.5,0-0.9-0.4-0.9-0.9c0-0.5,0.4-0.9,0.9-0.9
h1.2v-1.2c0-0.5,0.4-0.9,0.9-0.9c0.5,0,0.9,0.4,0.9,0.9v1.2h1.2c0.5,0,0.9,0.4,0.9,0.9C31.5,29,31,29.4,30.6,29.4z"/>
</g>
<g>
<path class="st2" d="M15.3,23.9c0-4.8,3.9-8.7,8.7-8.7c0.6,0,1.2,0.1,1.8,0.2c-1.3-2.1-3.7-3.4-6.3-3.4c-4.1,0-7.5,3.4-7.5,7.5
c0,2.7,1.4,5,3.5,6.3C15.3,25.2,15.3,24.6,15.3,23.9z"/>
</g>
<g>
<path class="st2" d="M19.8,28.5c0-4.8,3.9-8.7,8.7-8.7c0.6,0,1.3,0.1,1.9,0.2c-1.3-2.1-3.7-3.6-6.4-3.6c-4.1,0-7.5,3.4-7.5,7.5
c0,2.7,1.4,5,3.5,6.3C19.8,29.7,19.8,29.1,19.8,28.5z"/>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -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':

View File

@ -191,11 +191,11 @@
<!-- 发起接龙 -->
<UserSelect
ref="wordChainRef"
ref="wordChainAndVoteRef"
v-model="sendData"
:multiple-max="50"
:title="$L('选择群组发起接龙')"
:before-submit="onWordChain"
:title="sendType == 'vote' ? $L('选择群组发起投票') : $L('选择群组发起接龙')"
:before-submit="goWordChainAndVote"
:show-select-all="false"
:forced-radio="true"
:group="true"
@ -293,7 +293,8 @@ export default {
scanLoginLoad: false,
scanLoginCode: '',
//
sendData: []
sendData: [],
sendType: '',
}
},
activated() {
@ -319,41 +320,49 @@ export default {
methods: {
initList() {
let applyList = [
{ value: "approve", label: "审批中心" },
{ value: "report", label: "工作报告" },
{ value: "okr", label: "OKR管理" },
{ value: "robot", label: "AI机器人" },
{ value: "signin", label: "签到" },
{ value: "meeting", label: "会议" },
{ value: "calendar", label: "日历" },
{ value: "word-chain", label: "接龙" },
{ value: "vote", label: "投票" },
{ value: "approve", label: "审批中心", sort: 1 },
{ value: "report", label: "工作报告", sort: 2 },
{ value: "okr", label: "OKR管理", sort: 3 },
{ value: "robot", label: "AI机器人", sort: 4 },
{ value: "signin", label: "签到", sort: 5 },
{ value: "meeting", label: "会议", sort: 6 },
{ value: "calendar", label: "日历", sort: 7 },
{ value: "word-chain", label: "接龙", sort: 9 },
{ value: "vote", label: "投票", sort: 10 },
];
// wap
let appApplyList = this.windowOrientation != 'portrait' ? (
$A.isEEUiApp ? [
{ value: "scan", label: "扫一扫" }
{ value: "scan", label: "扫一扫", sort: 13 }
] : []
) : [
{ value: "file", label: "文件" },
{ value: "addProject", label: "创建项目" },
{ value: "addTask", label: "添加任务" },
{ value: "scan", label: "扫一扫", show: $A.isEEUiApp },
{ value: "setting", label: "设置" }
{ value: "file", label: "文件", sort: 8 },
{ value: "addProject", label: "创建项目", sort: 11 },
{ value: "addTask", label: "添加任务", sort: 12 },
{ value: "scan", label: "扫一扫", sort: 13 , show: $A.isEEUiApp },
{ value: "setting", label: "设置", sort: 14 }
];
//
let adminApplyList = !this.userIsAdmin ? [] : [
{ value: "okrAnalyze", label: "OKR结果" },
{ value: "ldap", label: "LDAP" },
{ value: "mail", label: "邮件" },
{ value: "appPush", label: "APP推送" },
{ value: "allUser", label: "团队管理" }
{ value: "okrAnalyze", label: "OKR结果", sort: 15 },
{ value: "ldap", label: "LDAP", sort: 16 },
{ value: "mail", label: "邮件", sort: 17 },
{ value: "appPush", label: "APP推送", sort: 18 },
{ value: "allUser", label: "团队管理", sort: 19 }
].map((h) => {
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
}

View File

@ -13,7 +13,7 @@
{{ $L('取消') }}
</div>
<div class="chain-modal-title">
{{ dialogGroupVote.type == 'create' ? $L('发起接龙') : $L('接龙结果') }}
{{ dialogGroupVote.type == 'create' ? $L('发起投票') : $L('投票结果') }}
</div>
<div class="chain-modal-submit" :class="{'disabled': !isEdit}" @click="onSend" >
<div v-if="loadIng > 0" class="submit-loading"><Loading /></div>
@ -44,15 +44,21 @@
</div>
<ul ref="wordChainListRef">
<li v-for="(item,index) in list">
<span>
<i class="taskfont" @click="del">&#xe680;</i>
</span>
<Input v-model="item.text" :disabled="item.userid != userId" :placeholder="$L('请输入选项内容')"/>
<i class="taskfont" :class="{'disabled': list.length <= 2}" @click="onDel(index)">&#xe680;</i>
<Input v-model="item.text" :placeholder="$L('请输入选项内容')"/>
</li>
<li class="add">
<i class="taskfont" @click="add">&#xe78c;</i>
<i class="taskfont" @click="onAdd">&#xe78c;</i>
</li>
</ul>
<div class="switch-row" v-if="dialogGroupVote.type == 'create'">
<span class="label">{{ $L('允许多选') }}</span>
<iSwitch v-model="multiple" :true-value="1" :false-value="0"/>
</div>
<div class="switch-row" v-if="dialogGroupVote.type == 'create'">
<span class="label">{{ $L('匿名投票') }}</span>
<iSwitch v-model="anonymous" :true-value="1" :false-value="0"/>
</div>
</div>
<div slot="footer">
<Button type="default" @click="show=false">{{$L('取消')}}</Button>
@ -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;

View File

@ -35,10 +35,16 @@
<span> {{ $L('发起,参与接龙目前共'+num+'人') }}</span>
</div>
<div class="textarea">
<Input ref="wordChainTextareaRef" v-model="value" type="textarea" :autosize="{minRows: 3,maxRows: 5}" :disabled="dialogDroupWordChain.type != 'create'" />
<Input ref="wordChainTextareaRef"
v-model="value"
type="textarea"
:autosize="{minRows: 3,maxRows: 5}"
:disabled="dialogDroupWordChain.type != 'create'"
:placeholder="$L('请输入接龙主题')"
/>
</div>
<ul ref="wordChainListRef">
<li v-for="(item,index) in list" v-if="item.type == 'case' && (dialogDroupWordChain.type == 'create' || item.text)">
<li v-for="(item) in list" v-if="item.type == 'case' && (dialogDroupWordChain.type == 'create' || item.text)">
<span>{{ $L('例') }}</span>
<Input v-model="item.text" :placeholder="$L('可填写接龙格式')" :disabled="dialogDroupWordChain.type != 'create'" />
</li>
@ -47,7 +53,7 @@
<Input v-model="item.text" :disabled="item.userid != userId" :placeholder="$L('请输入接龙内容')"/>
</li>
<li class="add">
<i class="taskfont" @click="add">&#xe78c;</i>
<i class="taskfont" @click="onAdd">&#xe78c;</i>
</li>
</ul>
</div>
@ -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", {

View File

@ -84,6 +84,54 @@
<li @click="onWordChain" class="participate">{{ $L('参与接龙') }}<span>></span></li>
</ul>
</div>
<!--投票-->
<div v-else-if="msgData.type === 'vote'" class="content-text content-word-vote no-dark-content">
<div class="vote-msg-head">
<i class="taskfont">&#xe7fd;</i> {{ $L('投票') }}
<span>{{ msgData.msg.multiple == 1 ? $L('多选') : $L('单选')}}</span>
<span>{{ msgData.msg.multiple == 1 ? $L('匿名') : $L('实名')}}</span>
</div>
<pre v-html="$A.formatTextMsg(msgData.msg.text, userId)"></pre>
<template v-if="(msgData.msg.votes || []).filter(h=>h.userid == userId).length == 0">
<RadioGroup v-if="msgData.msg.multiple == 0" v-model="msgData.msg._vote" vertical>
<Radio v-for="(item,index) in (msgData.msg.list || [])" :label="item.id" :key="index">
{{item.text}}
</Radio>
</RadioGroup>
<CheckboxGroup v-else v-model="msgData.msg._vote">
<Checkbox v-for="(item,index) in (msgData.msg.list || [])" :label="item.id" :key="index">
{{item.text}}
</Checkbox>
</CheckboxGroup>
<div class="btn-row no-dark-content">
<Button v-if="(msgData.msg._vote || []).length == 0" class="ivu-btn" disabled>{{$L("请选择后投票")}}</Button>
<Button v-else class="ivu-btn" :loading="msgData.msg._loadIng > 0" @click="onVote('vote',msgData)">{{$L("立即投票")}}</Button>
</div>
</template>
<template v-else>
<div class="vote-result-body">
<ul>
<li v-for="item in (msgData.msg.list || [])">
<div class="vote-option-title">{{ item.text }}</div>
<div class="ticket-num">
<span>{{ getVoteProgress(msgData.msg,item.id).num }}{{$L('票')}}</span>
<span>{{ getVoteProgress(msgData.msg,item.id).progress + '%' }}</span>
</div>
<Progress :percent="Number(getVoteProgress(msgData.msg,item.id).progress)" :stroke-width="5" hide-info/>
<div v-if="msgData.msg.anonymous" class="avatar-row">
<template v-for="votes in (msgData.msg.votes || []).filter(h=>h.votes.indexOf(item.id) != -1)">
<UserAvatar :userid="votes.userid" :size="18" />
</template>
</div>
</li>
</ul>
</div>
<div class="btn-row no-dark-content" v-if="msgData.msg.state == 1 && msgData.msg.userid == userId">
<Button class="ivu-btn" :loading="msgData.msg._loadIng > 0" @click="onVote('again',msgData)">{{$L("再次发送")}}</Button>
<Button class="ivu-btn" :loading="msgData.msg._loadIng > 0" @click="onVote('finish',msgData)">{{$L("结束投票")}}</Button>
</div>
</template>
</div>
<!--等待-->
<div v-else-if="msgData.type === 'loading'" class="content-loading">
<Icon v-if="msgData.error === true" type="ios-alert-outline" />
@ -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};
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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 {

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
<style type="text/css">
.st0{fill:#87D068;}
.st1{fill:#FFFFFF;}
</style>
<g>
<g>
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
</g>
<g>
<path class="st1" d="M34.4,33.5H25l9.3-9.3c1.2-1.2,1.2-3.3,0-4.5l-6.8-6.8c-1.2-1.2-3.3-1.2-4.5,0L12.9,23.1
c-1.2,1.2-1.2,3.3,0,4.5l6,6h-6c-0.5,0-0.9,0.4-0.9,0.9c0,0.5,0.4,0.9,0.9,0.9H22c0,0,0,0,0,0c0,0,0,0,0,0h12.4
c0.5,0,0.9-0.4,0.9-0.9C35.3,33.9,34.9,33.5,34.4,33.5z M17.1,25.5c0.3-0.3,0.9-0.3,1.2,0l3.4,3.4c0.1,0.1,0.3,0.1,0.4,0l6.7-6.7
c0.3-0.3,0.9-0.3,1.2,0c0.3,0.3,0.3,0.9,0,1.2l-6.7,6.7c-0.4,0.4-0.9,0.6-1.5,0.6c-0.5,0-1.1-0.2-1.5-0.6l-3.4-3.4
C16.8,26.4,16.8,25.9,17.1,25.5L17.1,25.5z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +1,38 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1701947484494" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14371" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M671.1 405.7C524.4 405.7 405 525.1 405 671.8s119.4 266.1 266.1 266.1 266.1-119.4 266.1-266.1-119.4-266.1-266.1-266.1z m74.5 298.1H703v42.6c0 17.6-14.3 31.9-31.9 31.9-17.6 0-31.9-14.3-31.9-31.9v-42.6h-42.6c-17.6 0-31.9-14.3-31.9-31.9 0-17.6 14.3-31.9 31.9-31.9h42.6v-42.6c0-17.6 14.3-31.9 31.9-31.9 17.6 0 31.9 14.3 31.9 31.9V640h42.6c17.6 0 31.9 14.3 31.9 31.9 0 17.6-14.3 31.9-31.9 31.9zM202.6 509.7c0-170.2 138.5-308.7 308.7-308.7 22.3 0 44 2.5 65 7-47.5-73.3-129.8-122-223.4-122C206.2 86 86.8 205.4 86.8 352.1c0 94.1 49.3 176.8 123.2 224.1-4.7-21.4-7.4-43.6-7.4-66.5z" p-id="14372" fill="#1296db"></path><path d="M362.4 671.8c0-170.2 138.5-308.7 308.7-308.7 22.9 0 45.2 2.7 66.6 7.4-46.9-76-130.7-127-226.4-127-146.7 0-266.1 119.4-266.1 266.1 0 94.2 49.4 177 123.5 224.3-4.2-20-6.3-40.8-6.3-62.1z" p-id="14373" fill="#1296db"></path></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
<style type="text/css">
.st0{fill:#87D068;}
.st1{clip-path:url(#SVGID_00000018941778426702510620000006481262296390689922_);}
.st2{fill:#FFFFFF;}
</style>
<g>
<g>
<path class="st0" d="M36,48H12C5.4,48,0,42.6,0,36V12C0,5.4,5.4,0,12,0h24c6.6,0,12,5.4,12,12v24C48,42.6,42.6,48,36,48z"/>
</g>
<g>
<defs>
<rect id="SVGID_1_" x="12" y="12" width="24" height="24"/>
</defs>
<clipPath id="SVGID_00000141444668484078751200000003089482387513471677_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#SVGID_00000141444668484078751200000003089482387513471677_);">
<g>
<path class="st2" d="M28.5,21c-4.1,0-7.5,3.4-7.5,7.5c0,4.1,3.4,7.5,7.5,7.5c4.1,0,7.5-3.4,7.5-7.5C36,24.4,32.6,21,28.5,21z
M30.6,29.4h-1.2v1.2c0,0.5-0.4,0.9-0.9,0.9c-0.5,0-0.9-0.4-0.9-0.9v-1.2h-1.2c-0.5,0-0.9-0.4-0.9-0.9c0-0.5,0.4-0.9,0.9-0.9
h1.2v-1.2c0-0.5,0.4-0.9,0.9-0.9c0.5,0,0.9,0.4,0.9,0.9v1.2h1.2c0.5,0,0.9,0.4,0.9,0.9C31.5,29,31,29.4,30.6,29.4z"/>
</g>
<g>
<path class="st2" d="M15.3,23.9c0-4.8,3.9-8.7,8.7-8.7c0.6,0,1.2,0.1,1.8,0.2c-1.3-2.1-3.7-3.4-6.3-3.4c-4.1,0-7.5,3.4-7.5,7.5
c0,2.7,1.4,5,3.5,6.3C15.3,25.2,15.3,24.6,15.3,23.9z"/>
</g>
<g>
<path class="st2" d="M19.8,28.5c0-4.8,3.9-8.7,8.7-8.7c0.6,0,1.3,0.1,1.9,0.2c-1.3-2.1-3.7-3.6-6.4-3.6c-4.1,0-7.5,3.4-7.5,7.5
c0,2.7,1.4,5,3.5,6.3C19.8,29.7,19.8,29.1,19.8,28.5z"/>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB