Commit e3003645 authored by 万成波's avatar 万成波

Merge remote-tracking branch 'origin/master'

parents a22b63a9 f153f60a
@font-face {
font-family: "iconfont"; /* Project id 5030701 */
/* Color fonts */
src:
url('iconfont.woff2?t=1758858203810') format('woff2'),
url('iconfont.woff?t=1758858203810') format('woff'),
url('iconfont.ttf?t=1758858203810') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-biaoqing-bugaoxing:before {
content: "\e65b";
}
.icon-meigui:before {
content: "\e62f";
}
.icon-a-heshi:before {
content: "\e6af";
}
.icon-a-jizhi:before {
content: "\e6b1";
}
.icon-be024:before {
content: "\e667";
}
.icon-emo_bucuo:before {
content: "\e6a6";
}
.icon-huaji:before {
content: "\e601";
}
.icon-neijiu:before {
content: "\e600";
}
.icon-icon-test:before {
content: "\e60d";
}
.icon-icon-test1:before {
content: "\e60f";
}
.icon-jingdai:before {
content: "\e627";
}
.icon-coolapk_emotion__koubi:before {
content: "\e615";
}
.icon-liuhan:before {
content: "\e603";
}
.icon-qinqin:before {
content: "\e610";
}
.icon-kulian:before {
content: "\e611";
}
.icon-jingxi:before {
content: "\e616";
}
.icon-feiwen:before {
content: "\e700";
}
.icon-zaijian:before {
content: "\e61f";
}
.icon-shuizhao-01:before {
content: "\e669";
}
.icon-biaoqing-weixiao:before {
content: "\e65f";
}
.icon-ico_emoji_26:before {
content: "\e67f";
}
.icon-a-ziyuan135:before {
content: "\e60c";
}
.icon-a-ziyuan123:before {
content: "\e60e";
}
.icon-emoji-18:before {
content: "\e69b";
}
.icon-a-Facewithcoldsweat:before {
content: "\e64a";
}
.icon-a-kelian:before {
content: "\e6aa";
}
.icon-bangzhuzhongxin:before {
content: "\e634";
}
.icon-a-Thumbsup:before {
content: "\e656";
}
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -7,6 +7,8 @@ import './assets/styles/element-variables.scss' ...@@ -7,6 +7,8 @@ import './assets/styles/element-variables.scss'
import '@/assets/styles/index.scss' // global css import '@/assets/styles/index.scss' // global css
import '@/assets/styles/ruoyi.scss' // ruoyi css import '@/assets/styles/ruoyi.scss' // ruoyi css
import '../public/iconfont/iconfont.css'
import '../public/iconfont/iconfont.js'
import App from './App' import App from './App'
import store from './store' import store from './store'
import router from './router' import router from './router'
......
const emotions = [{
code: 'icon-meigui',
name: '玫瑰',
symbol: '[玫瑰]'
},
{
code: 'icon-a-heshi',
name: '合十',
symbol: '[合十]'
},
{
code: 'icon-a-jizhi',
name: '机智',
symbol: '[机智]'
},
{
code: 'icon-huaji',
name: '滑稽',
symbol: '[滑稽]'
},
{
code: 'icon-be024',
name: '兴奋',
symbol: '[兴奋]'
},
{
code: 'icon-emo_bucuo',
name: '调皮',
symbol: '[调皮]'
},
{
code: 'icon-neijiu',
name: '内疚',
symbol: '[内疚]'
},
{
code: 'icon-icon-test',
name: '疲倦',
symbol: '[疲倦]'
},
{
code: 'icon-icon-test1',
name: '大哭',
symbol: '[大哭]'
},
{
code: 'icon-jingdai',
name: '惊呆',
symbol: '[惊呆]'
},
{
code: 'icon-coolapk_emotion__koubi',
name: '抠鼻',
symbol: '[抠鼻]'
},
{
code: 'icon-liuhan',
name: '流汗',
symbol: '[流汗]'
},
{
code: 'icon-qinqin',
name: '亲亲',
symbol: '[亲亲]'
},
{
code: 'icon-kulian',
name: '哭脸',
symbol: '[哭脸]'
},
{
code: 'icon-jingxi',
name: '惊喜',
symbol: '[惊喜]'
},
{
code: 'icon-zaijian',
name: '再见',
symbol: '[再见]'
},
{
code: 'icon-shuizhao-01',
name: '睡着了',
symbol: '[睡着了]'
},
{
code: 'icon-biaoqing-weixiao',
name: '微笑',
symbol: '[微笑]'
},
{
code: 'icon-ico_emoji_26',
name: '叹气',
symbol: '[叹气]'
},
{
code: 'icon-feiwen',
name: '飞吻',
symbol: '[飞吻]'
},
{
code: 'icon-a-ziyuan135',
name: '偷笑',
symbol: '[偷笑]'
},
{
code: 'icon-a-ziyuan123',
name: '鼓掌',
symbol: '[鼓掌]'
},
{
code: 'icon-emoji-18',
name: '抓狂',
symbol: '[抓狂]'
},
{
code: 'icon-a-Facewithcoldsweat',
name: '尴尬',
symbol: '[尴尬]'
},
{
code: 'icon-a-kelian',
name: '可怜',
symbol: '[可怜]'
},
{
code: 'icon-bangzhuzhongxin',
name: '爱心',
symbol: '[爱心]'
},
{
code: 'icon-a-Thumbsup',
name: '点赞',
symbol: '[点赞]'
},
{
code:'icon-biaoqing-bugaoxing',
name: '不高兴',
symbol: '[不高兴]'
}
]
export default emotions;
\ No newline at end of file
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
</template> </template>
</div> </div>
<div class="dynamic-content-box"> <div class="dynamic-content-box">
<div class="text">{{ form.content }}</div> <div class="text" v-html="formatContent(form.content)"></div>
<div class="imgs" v-if="form.type == 'IMAGE' && form.attachments && form.attachments.length > 0"> <div class="imgs" v-if="form.type == 'IMAGE' && form.attachments && form.attachments.length > 0">
<template v-for="(item, index) in form.attachments"> <template v-for="(item, index) in form.attachments">
<el-image class="item" :src="item.url" :preview-src-list="[item.url]"></el-image> <el-image class="item" :src="item.url" :preview-src-list="[item.url]"></el-image>
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
<img src="@/assets/images/favorite-active.png" v-if="form.isLike == 1" class="icon"></img> <img src="@/assets/images/favorite-active.png" v-if="form.isLike == 1" class="icon"></img>
<div class="num">{{ form.likeCount }}</div> <div class="num">{{ form.likeCount }}</div>
</div> </div>
<div class="comment-box"> <div class="comment-box" style="border-top: 0;">
<img class="icon" src="@/assets/images/comment-icon.png"></img> <img class="icon" src="@/assets/images/comment-icon.png"></img>
<div class="text">评论</div> <div class="text">评论</div>
</div> </div>
...@@ -62,7 +62,19 @@ ...@@ -62,7 +62,19 @@
<div class="username">{{ item.nickName }}:</div> <div class="username">{{ item.nickName }}:</div>
<div class="datetime">{{ item.createTime }}</div> <div class="datetime">{{ item.createTime }}</div>
</div> </div>
<div class="content">{{ item.content }}</div> <div class="content">
<div class="text" v-if="!item.replyNickName">
{{ item.content }}
</div>
<div class="text" v-else>
回复<span style="color: #0058B6;">{{ item.replyNickName }}:</span>{{ item.content }}
</div>
<div class="operation-text">
<div v-if="item.isFeatured == 1" style="color: #F2AC39;margin-right: 10rpx;">精选</div>
<div @click.stop="handleDelete(item)">删除</div>
</div>
</div>
</div> </div>
<div class="show-more-box" v-if="commentList.length < total" @click="loadMore"> <div class="show-more-box" v-if="commentList.length < total" @click="loadMore">
加载更多 加载更多
...@@ -74,6 +86,8 @@ ...@@ -74,6 +86,8 @@
</template> </template>
<script> <script>
import { detailData, getMomentComments } from '@/api/moments' import { detailData, getMomentComments } from '@/api/moments'
import { delData } from '@/api/comment';
import emotions from '@/utils/emjo';
export default { export default {
data() { data() {
return { return {
...@@ -85,14 +99,21 @@ export default { ...@@ -85,14 +99,21 @@ export default {
}, },
commentList: [], commentList: [],
total: 0, total: 0,
currentRow:{} currentRow: {},
emotions,
// 预创建表情符号到iconfont的映射表,提高性能
emotionMap: {}
} }
}, },
methods: { methods: {
openModal(row) { openModal(row) {
this.visible = true; this.visible = true;
this.currentRow = {...row} this.currentRow = { ...row }
this.queryParams.pageNum = 1; this.queryParams.pageNum = 1;
// 初始化表情映射表
this.emotions.forEach(emotion => {
this.emotionMap[emotion.symbol] = emotion.code;
});
this.commentList = []; this.commentList = [];
this.getData(); this.getData();
this.getComments() this.getComments()
...@@ -116,6 +137,84 @@ export default { ...@@ -116,6 +137,84 @@ export default {
this.queryParams.pageNum += 1; this.queryParams.pageNum += 1;
this.getComments() this.getComments()
}, },
// 删除
handleDelete(row) {
this.$modal.confirm('确认要删除评论内容为:' + row.content + '的数据吗?').then(function () {
return delData(row.id);
}).then(() => {
this.$modal.msgSuccess("删除成功");
this.commentList = [];
this.getComments()
this.$emit('refresh')
})
},
/**
* 将内容中的表情符号转换为iconfont标签
* @param {string} content - 原始内容
* @returns {string} 转换后的HTML字符串
*/
formatContent(content) {
if (!content) return '';
let result = '';
let remainingContent = content;
// 提取所有表情符号并按长度排序(长表情优先匹配)
const emotionSymbols = Object.keys(this.emotionMap).sort((a, b) => b.length - a.length);
while (remainingContent.length > 0) {
let found = false;
// 查找是否有表情符号
for (const symbol of emotionSymbols) {
if (remainingContent.startsWith(symbol)) {
// 添加表情标签
result += `<i class="iconfont ${this.emotionMap[symbol]} emotion-icon"></i>`;
remainingContent = remainingContent.slice(symbol.length);
found = true;
break;
}
}
// 如果没有找到表情,提取普通文本
if (!found) {
// 找到下一个表情的位置
let nextEmotionIndex = remainingContent.length;
for (const symbol of emotionSymbols) {
const index = remainingContent.indexOf(symbol);
if (index !== -1 && index < nextEmotionIndex) {
nextEmotionIndex = index;
}
}
// 提取文本并进行HTML转义,防止XSS
const text = this.escapeHtml(remainingContent.slice(0, nextEmotionIndex));
result += text;
remainingContent = remainingContent.slice(nextEmotionIndex);
}
}
return result;
},
/**
* HTML转义处理,防止XSS攻击
* @param {string} str - 需要转义的字符串
* @returns {string} 转义后的字符串
*/
escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>"']/g, char => {
const entities = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;'
};
return entities[char] || char;
});
}
} }
} }
...@@ -356,6 +455,29 @@ export default { ...@@ -356,6 +455,29 @@ export default {
.content { .content {
color: black; color: black;
margin: 5px 0; margin: 5px 0;
display: flex;
.text {
width: calc(100% - 90px);
}
.operation-text {
width: 90px;
text-align: right;
font-size: 12px;
display: flex;
align-items: center;
justify-content: flex-end;
cursor: pointer;
}
.mr10 {
margin-right: 6px;
}
.add-text {
color: #0058B6;
}
} }
.tips { .tips {
......
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
</el-form-item> </el-form-item>
<el-form-item label="创建时间"> <el-form-item label="创建时间">
<el-date-picker placeholder="请选择" type="daterange" format="yyyy-MM-dd" value-format="yyyy-MM-dd" <el-date-picker placeholder="请选择" type="daterange" format="yyyy-MM-dd" value-format="yyyy-MM-dd"
v-model="dateRange" range-separator="至" start-placeholder="开始时间" v-model="dateRange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间"
end-placeholder="结束时间" @change="dateChange"></el-date-picker> @change="dateChange"></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button> <el-button type="primary" @click="handleQuery">查询</el-button>
...@@ -21,7 +21,11 @@ ...@@ -21,7 +21,11 @@
</el-row> </el-row>
<el-table :data="tableList"> <el-table :data="tableList">
<el-table-column label="序号" width="55" type="index" align="center"></el-table-column> <el-table-column label="序号" width="55" type="index" align="center"></el-table-column>
<el-table-column label="动态内容" prop="content" align="center"></el-table-column> <el-table-column label="动态内容" prop="content" align="center">
<template #default="{ row }">
<div v-html="formatContent(row.content)" class="content-container"></div>
</template>
</el-table-column>
<el-table-column label="话题" align="center" prop="topicNames" width="240"></el-table-column> <el-table-column label="话题" align="center" prop="topicNames" width="240"></el-table-column>
<el-table-column label="帐号" prop="userName" align="center" width="140"></el-table-column> <el-table-column label="帐号" prop="userName" align="center" width="140"></el-table-column>
<el-table-column label="姓名" prop="nickName" align="center" width="120"></el-table-column> <el-table-column label="姓名" prop="nickName" align="center" width="120"></el-table-column>
...@@ -39,13 +43,14 @@ ...@@ -39,13 +43,14 @@
:limit.sync="queryParams.pageSize" @pagination="getList" /> :limit.sync="queryParams.pageSize" @pagination="getList" />
</div> </div>
<!-- 动态详情 --> <!-- 动态详情 -->
<detail-dialog ref="detailDialogRef"></detail-dialog> <detail-dialog ref="detailDialogRef" @refresh="getList"></detail-dialog>
</div> </div>
</template> </template>
<script> <script>
import { listData, delData } from '@/api/moments'; import { listData, delData } from '@/api/moments';
import DetailDialog from './DetailDialog.vue'; import DetailDialog from './DetailDialog.vue';
import emotions from '@/utils/emjo';
export default { export default {
name: 'Moments', name: 'Moments',
data() { data() {
...@@ -57,13 +62,19 @@ export default { ...@@ -57,13 +62,19 @@ export default {
tableList: [], tableList: [],
total: 0, total: 0,
dateRange: [], dateRange: [],
emotions,
// 预创建表情符号到iconfont的映射表,提高性能
emotionMap: {}
} }
}, },
components: { components: {
DetailDialog DetailDialog
}, },
created() { created() {
// 初始化表情映射表
this.emotions.forEach(emotion => {
this.emotionMap[emotion.symbol] = emotion.code;
});
}, },
mounted() { mounted() {
this.getList() this.getList()
...@@ -77,7 +88,6 @@ export default { ...@@ -77,7 +88,6 @@ export default {
this.total = res.total this.total = res.total
}) })
}, },
// 新增 // 新增
handleDetail(row) { handleDetail(row) {
this.$refs.detailDialogRef.openModal(row) this.$refs.detailDialogRef.openModal(row)
...@@ -91,8 +101,8 @@ export default { ...@@ -91,8 +101,8 @@ export default {
this.getList() this.getList()
}) })
}, },
dateChange(val){ dateChange(val) {
if(val.length > 0){ if (val.length > 0) {
this.queryParams['params[startTime]'] = val[0] this.queryParams['params[startTime]'] = val[0]
this.queryParams['params[endTime]'] = val[1] this.queryParams['params[endTime]'] = val[1]
} }
...@@ -110,6 +120,73 @@ export default { ...@@ -110,6 +120,73 @@ export default {
} }
this.dateRange = [] this.dateRange = []
this.getList() this.getList()
},
/**
* 将内容中的表情符号转换为iconfont标签
* @param {string} content - 原始内容
* @returns {string} 转换后的HTML字符串
*/
formatContent(content) {
if (!content) return '';
let result = '';
let remainingContent = content;
// 提取所有表情符号并按长度排序(长表情优先匹配)
const emotionSymbols = Object.keys(this.emotionMap).sort((a, b) => b.length - a.length);
while (remainingContent.length > 0) {
let found = false;
// 查找是否有表情符号
for (const symbol of emotionSymbols) {
if (remainingContent.startsWith(symbol)) {
// 添加表情标签
result += `<i class="iconfont ${this.emotionMap[symbol]} emotion-icon"></i>`;
remainingContent = remainingContent.slice(symbol.length);
found = true;
break;
}
}
// 如果没有找到表情,提取普通文本
if (!found) {
// 找到下一个表情的位置
let nextEmotionIndex = remainingContent.length;
for (const symbol of emotionSymbols) {
const index = remainingContent.indexOf(symbol);
if (index !== -1 && index < nextEmotionIndex) {
nextEmotionIndex = index;
}
}
// 提取文本并进行HTML转义,防止XSS
const text = this.escapeHtml(remainingContent.slice(0, nextEmotionIndex));
result += text;
remainingContent = remainingContent.slice(nextEmotionIndex);
}
}
return result;
},
/**
* HTML转义处理,防止XSS攻击
* @param {string} str - 需要转义的字符串
* @returns {string} 转义后的字符串
*/
escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>"']/g, char => {
const entities = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;'
};
return entities[char] || char;
});
} }
} }
} }
......
...@@ -56,7 +56,9 @@ export default { ...@@ -56,7 +56,9 @@ export default {
this.upload.open = false; this.upload.open = false;
this.upload.isUploading = false; this.upload.isUploading = false;
this.$refs.upload.clearFiles(); this.$refs.upload.clearFiles();
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true }); console.log(response)
let msg = `总共:${response.data.totalCount}条,成功:${response.data.successCount},失败:${response.data.failCount}`
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
this.$emit('handleOk') this.$emit('handleOk')
}, },
// 提交上传文件 // 提交上传文件
......
...@@ -13,5 +13,6 @@ ...@@ -13,5 +13,6 @@
</script> </script>
<style> <style>
@import '/static/iconfont/iconfont.css'
/*每个页面公共css */ /*每个页面公共css */
</style> </style>
...@@ -26,8 +26,9 @@ ...@@ -26,8 +26,9 @@
@click="handleFeatured(item)">加入精选</view> @click="handleFeatured(item)">加入精选</view>
<view class="mr10" v-if="form.isSelf==1&&activeIndex==1 &&item.isFeatured==1&&form.isEnableFeaturedComment==1" @click="handleFeatured(item)"> <view class="mr10" v-if="form.isSelf==1&&activeIndex==1 &&item.isFeatured==1&&form.isEnableFeaturedComment==1" @click="handleFeatured(item)">
取消精选</view> 取消精选</view>
<view v-if="form.isSelf==1&&activeIndex==1" @click="handleDelete(item)">删除</view> <view v-if="activeIndex==0&&item.isFeatured==1" style="color: #F2AC39;margin-right: 10rpx;">精选</view>
<view v-if="activeIndex==0&&item.isFeatured==1" style="color: #F2AC39;">精选</view> <view v-if="item.isSelf==1" @click.stop="handleDelete(item)">删除</view>
</view> </view>
</view> </view>
</view> </view>
...@@ -246,7 +247,7 @@ ...@@ -246,7 +247,7 @@
.operation-text { .operation-text {
width: 180rpx; width: 180rpx;
text-align: right; text-align: right;
font-size: 28rpx; font-size: 24rpx;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
......
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
<image v-if="deleteable" class="more-icon" @click.stop="handleDelDynamics" <image v-if="deleteable" class="more-icon" @click.stop="handleDelDynamics"
src="/static/images/more-icon.png"></image> src="/static/images/more-icon.png"></image>
<!-- 删除按钮 --> <!-- 删除按钮 -->
<view class="del-btn" v-if="showDelBtn" @click.stop="handleDel" :style="'left:' + clientX + 'rpx;' +'top:' + clientY + 'rpx'"> <view class="del-btn" v-if="showDelBtn" @click.stop="handleDel"
:style="'left:' + clientX + 'rpx;' +'top:' + clientY + 'rpx'">
删除动态</view> 删除动态</view>
</view> </view>
<view class="topics-box" v-if="form.topicNames && form.topicNames.length >0"> <view class="topics-box" v-if="form.topicNames && form.topicNames.length >0">
...@@ -16,8 +17,9 @@ ...@@ -16,8 +17,9 @@
<view class="topic-box-common" @click.stop="toTopicDetailPage(name,index)"># {{name}}</view> <view class="topic-box-common" @click.stop="toTopicDetailPage(name,index)"># {{name}}</view>
</template> </template>
</view> </view>
<view class="dynamic-content-box"> <view class="dynamic-content-box" >
<view class="text">{{form.content}}</view>
<rich-text v-if="preNodes.length > 0" :nodes="preNodes"></rich-text>
<view class="imgs" v-if="form.type=='IMAGE'&&form.attachments && form.attachments.length > 0"> <view class="imgs" v-if="form.type=='IMAGE'&&form.attachments && form.attachments.length > 0">
<template v-for="(item,index) in form.attachments"> <template v-for="(item,index) in form.attachments">
<image class="item" :src="item.url" @click.stop="preview(item.url)"></image> <image class="item" :src="item.url" @click.stop="preview(item.url)"></image>
...@@ -25,7 +27,8 @@ ...@@ -25,7 +27,8 @@
</view> </view>
<view class="imgs" v-if="form.type=='VIDEO'&&form.attachments && form.attachments.length > 0"> <view class="imgs" v-if="form.type=='VIDEO'&&form.attachments && form.attachments.length > 0">
<template v-for="(item,index) in form.attachments"> <template v-for="(item,index) in form.attachments">
<video class="item" :src="item.url"></video> <video class="item" :src="item.url" :show-fullscreen-btn="false" :show-center-play-btn="false"
@click.stop="videoPreview(item.url)"></video>
</template> </template>
</view> </view>
</view> </view>
...@@ -70,7 +73,7 @@ ...@@ -70,7 +73,7 @@
<!-- 已投票,显示投票结果 --> <!-- 已投票,显示投票结果 -->
<view v-if="form.isVote==1"> <view v-if="form.isVote==1">
<view class="pk-results"> <view class="pk-results">
<view class="left" @click.stop="toPKDetailPage(0)"> <view class="left" @click.stop="toPKDetailPage(0)">
<view class="left-num">{{form.voteOptions[0].name}} {{form.voteOptions[0].voteCount}}</view> <view class="left-num">{{form.voteOptions[0].name}} {{form.voteOptions[0].voteCount}}</view>
</view> </view>
<view class="right" @click.stop="toPKDetailPage(1)"> <view class="right" @click.stop="toPKDetailPage(1)">
...@@ -79,10 +82,12 @@ ...@@ -79,10 +82,12 @@
</view> </view>
</view> </view>
<view class="pk-results"> <view class="pk-results">
<view class="left" :style="'flex:' + form.voteOptions[0].voteCount" @click.stop="toPKDetailPage(0)"> <view class="left" :style="'flex:' + form.voteOptions[0].voteCount"
@click.stop="toPKDetailPage(0)">
<view class="results-item left-item"></view> <view class="results-item left-item"></view>
</view> </view>
<view class="right" :style="'flex:' + form.voteOptions[1].voteCount" @click.stop="toPKDetailPage(1)"> <view class="right" :style="'flex:' + form.voteOptions[1].voteCount"
@click.stop="toPKDetailPage(1)">
<view class="results-item right-item"></view> <view class="results-item right-item"></view>
</view> </view>
</view> </view>
...@@ -100,20 +105,27 @@ ...@@ -100,20 +105,27 @@
<view class="username">{{item.nickName}}:</view> <view class="username">{{item.nickName}}:</view>
<view class="datetime">2025-07-01 04:21:21</view> <view class="datetime">2025-07-01 04:21:21</view>
</view> </view>
<view class="content" v-if="!item.replyNickName" @click.stop="handleReplayComment(item)"> <view class="content">
{{item.content}}</view> <view class="text" v-if="!item.replyNickName" @click.stop="handleReplayComment(item)">
<view class="content" v-else @click.stop="handleReplayComment(item)"> {{item.content}}
回复<span style="color: #0058B6;">{{item.replyNickName}}:</span>:{{item.content}} </view>
<view class="text" v-else @click.stop="handleReplayComment(item)">
回复<span style="color: #0058B6;">{{item.replyNickName}}:</span>{{item.content}}
</view>
<view class="operation-text">
<view v-if="item.isFeatured==1" style="color: #F2AC39;margin-right: 10rpx;">精选</view>
<view v-if="item.isSelf==1" @click.stop="handleDelete(item)">删除</view>
</view>
</view> </view>
<!-- <view class="tips">未精选,仅自己可见</view> -->
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 发布评论区 --> <!-- 发布评论区 -->
<publish-comment :form="form" ref="publishCommentRef" @refresh="$emit('refresh')"></publish-comment> <publish-comment :form="form" ref="publishCommentRef" @refresh="$emit('refresh')"></publish-comment>
<!-- 视频预览 -->
<video-preview ref="videoPreviewRef"></video-preview>
</view> </view>
</template> </template>
...@@ -121,23 +133,29 @@ ...@@ -121,23 +133,29 @@
import { import {
dynamicLikeUnlike, dynamicLikeUnlike,
delMonment, delMonment,
dynamicVote dynamicVote,
delComment
} from '@/api/api.js' } from '@/api/api.js'
import PublishComment from './PublishComment.vue'; import PublishComment from './PublishComment.vue';
import VideoPreview from './VideoPreview.vue';
import emotions from '@/utils/emjo';
export default { export default {
name: "Dynamic-Item", name: "Dynamic-Item",
data() { data() {
return { return {
commentList: [{}, {}], commentList: [],
showComment: false, showComment: false,
commentContent: '', commentContent: '',
clientX: 0, clientX: 0,
clientY: 0, clientY: 0,
showDelBtn: false showDelBtn: false,
preNodes: [],
emotions
}; };
}, },
components: { components: {
PublishComment PublishComment,
VideoPreview
}, },
props: { props: {
form: { form: {
...@@ -149,6 +167,19 @@ ...@@ -149,6 +167,19 @@
default: false default: false
} }
}, },
watch: {
// 当内容变化时自动更新预览
"form.content": {
immediate: true,
handler(val, oldVal) {
if (val === oldVal) {
return;
}
this.preNodes = this.formatContent(val)
}
}
},
methods: { methods: {
// 点赞/取消点赞 // 点赞/取消点赞
handleUpdateLikeStatus(likeStatus) { handleUpdateLikeStatus(likeStatus) {
...@@ -156,11 +187,11 @@ ...@@ -156,11 +187,11 @@
momentId: this.form.id, momentId: this.form.id,
likeStatus likeStatus
} }
if(likeStatus == '1'){ if (likeStatus == '1') {
this.form.likeCount = this.form.likeCount + 1 this.form.likeCount = this.form.likeCount + 1
this.form.isLike = 1 this.form.isLike = 1
} }
if(likeStatus == '0'){ if (likeStatus == '0') {
this.form.likeCount = this.form.likeCount - 1 this.form.likeCount = this.form.likeCount - 1
this.form.isLike = 0 this.form.isLike = 0
} }
...@@ -171,6 +202,60 @@ ...@@ -171,6 +202,60 @@
}) })
}, },
formatContent(content) {
const nodes = [];
const emotionMap = {};
// 创建表情符号到iconfont的映射
this.emotions.forEach(emotion => {
emotionMap[emotion.symbol] = emotion.code;
});
// 提取所有表情符号并按长度排序
const emotionSymbols = Object.keys(emotionMap).sort((a, b) => b.length - a.length);
while (content.length > 0) {
let found = false;
// 查找是否有表情符号
for (const symbol of emotionSymbols) {
if (content.startsWith(symbol)) {
// 添加表情节点
nodes.push({
name: 'span',
attrs: {
class: `iconfont ${emotionMap[symbol]} emotion-icon`
},
});
content = content.slice(symbol.length);
found = true;
break;
}
}
// 如果没有找到表情,提取普通文本
if (!found) {
// 找到下一个表情的位置
let nextEmotionIndex = content.length;
for (const symbol of emotionSymbols) {
const index = content.indexOf(symbol);
if (index !== -1 && index < nextEmotionIndex) {
nextEmotionIndex = index;
}
}
// 提取文本
const text = content.slice(0, nextEmotionIndex);
nodes.push({
type: 'text',
text: text
});
content = content.slice(nextEmotionIndex);
}
}
return nodes;
},
// 打开评论区 // 打开评论区
handleOpenComment() { handleOpenComment() {
// this.showComment = true // this.showComment = true
...@@ -188,17 +273,17 @@ ...@@ -188,17 +273,17 @@
this.clientY = ((e.target.offsetTop * 2) + 34); this.clientY = ((e.target.offsetTop * 2) + 34);
}, },
// 删除动态 // 删除动态
handleDel(){ handleDel() {
let _this = this; let _this = this;
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '是否确认删除动态?', content: '是否确认删除动态?',
success: function (res) { success: function(res) {
if (res.confirm) { if (res.confirm) {
delMonment(_this.form.id).then(res=>{ delMonment(_this.form.id).then(res => {
if(res.code == 200){ if (res.code == 200) {
uni.showToast({ uni.showToast({
title:'删除成功', title: '删除成功',
}) })
_this.$emit('refresh'); _this.$emit('refresh');
uni.$emit('hanldeDynamicRefresh') uni.$emit('hanldeDynamicRefresh')
...@@ -226,12 +311,39 @@ ...@@ -226,12 +311,39 @@
} }
}) })
}, },
// 预览 // 删除评论
handleDelete(item) {
let _this = this;
uni.showModal({
title: '提示',
content: `是否确认删除评论:${item.content}?`,
success: function(res) {
if (res.confirm) {
delComment(item.id).then(res => {
if (res.code == 200) {
uni.showToast({
title: `删除成功`
})
_this.$emit('refresh');
}
})
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
// 预览图片
preview(url) { preview(url) {
uni.previewImage({ uni.previewImage({
urls: [url] urls: [url]
}); });
}, },
//预览视频
videoPreview(url) {
this.$refs.videoPreviewRef.openModal(url)
},
toDynamicDetailPage() { toDynamicDetailPage() {
let pathName = window.location.pathname let pathName = window.location.pathname
if (pathName.indexOf('/pages/dynamic-detail/dynamic-detail') > -1) { if (pathName.indexOf('/pages/dynamic-detail/dynamic-detail') > -1) {
...@@ -273,4 +385,11 @@ ...@@ -273,4 +385,11 @@
text-align: center; text-align: center;
box-shadow: 0px 2px 12rpx 0px rgba(0, 0, 0, 0.2); box-shadow: 0px 2px 12rpx 0px rgba(0, 0, 0, 0.2);
} }
</style>
.emotion-icon {
font-size: 22px;
margin: 0 2px;
vertical-align: middle;
color: #333;
}
</style>
\ No newline at end of file
...@@ -4,12 +4,12 @@ ...@@ -4,12 +4,12 @@
<block v-if="fileList && fileList.length > 0"> <block v-if="fileList && fileList.length > 0">
<view class="pic-list-item" v-for="(img, i) in fileList" :key="i"> <view class="pic-list-item" v-for="(img, i) in fileList" :key="i">
<block v-if="isVideo"> <block v-if="isVideo">
<video @click.stop :src="img.url" controls class="pic" <video @click.stop="videoPreview(img.url)" :src="img.url" controls class="pic"
:style="'width:'+width+'rpx;'+'height:'+height+'rpx'"></video> :style="'width:'+width+'rpx;'+'height:'+height+'rpx'"></video>
</block> </block>
<block v-else> <block v-else>
<image class="pic" :style="'width:'+width+'rpx;'+'height:'+height+'rpx'" :src="img.url" mode="" <image class="pic" :style="'width:'+width+'rpx;'+'height:'+height+'rpx'" :src="img.url" mode=""
@error="(e) => imgError(e, img)" @click="preview(img.url)"></image> @error="(e) => imgError(e, img)" @click.stop="preview(img.url)"></image>
<view v-if="img.error" class="pic-error">加载失败</view> <view v-if="img.error" class="pic-error">加载失败</view>
</block> </block>
<icon @click="handleRemove(i)" class="clear-icon" type="clear" size="20" /> <icon @click="handleRemove(i)" class="clear-icon" type="clear" size="20" />
...@@ -35,7 +35,17 @@ ...@@ -35,7 +35,17 @@
v-if="!isImg"></image> v-if="!isImg"></image>
<image src="/static/images/publish/video-disabled-icon.png" class="icons" v-else></image> <image src="/static/images/publish/video-disabled-icon.png" class="icons" v-else></image>
<!-- 表情 --> <!-- 表情 -->
<image src="/static/images/publish/face-icon.png" class="icons"></image> <image src="/static/images/publish/face-icon.png" class="icons" @click="handleShowEmjo"></image>
<!-- 表情展示区 -->
<view class="emjo-box" v-if="showEmjo" :style="'left:' + clientX + 'rpx;' +'top:' + clientY + 'rpx'">
<view class="emotion-list">
<view class="emotion-item" v-for="(emotion, index) in emotions" :key="index"
:title="emotion.name" @click="handleSelectEmjo(emotion)">
<text class="iconfont" :class="emotion.code"></text>
</view>
</view>
</view>
<!-- 话题 --> <!-- 话题 -->
<image src="/static/images/publish/topic-icon.png" class="icons" @click="handleOpenTopicSelection"> <image src="/static/images/publish/topic-icon.png" class="icons" @click="handleOpenTopicSelection">
</image> </image>
...@@ -44,6 +54,11 @@ ...@@ -44,6 +54,11 @@
<!-- @ --> <!-- @ -->
<!-- <image src="/static/images/publish/important-icon.png" class="icons"></image> --> <!-- <image src="/static/images/publish/important-icon.png" class="icons"></image> -->
</view> </view>
<!-- 视频预览 -->
<video-preview ref="videoPreviewRef"></video-preview>
</view> </view>
</template> </template>
...@@ -57,6 +72,8 @@ ...@@ -57,6 +72,8 @@
getToken getToken
} from '@/utils/auth.js'; } from '@/utils/auth.js';
import $modal from '@/utils/modal.js' import $modal from '@/utils/modal.js'
import VideoPreview from './VideoPreview.vue';
import emotions from '@/utils/emjo';
export default { export default {
name: 'BatchImageUpload', name: 'BatchImageUpload',
props: { props: {
...@@ -88,6 +105,9 @@ ...@@ -88,6 +105,9 @@
default: 200 default: 200
} }
}, },
components: {
VideoPreview
},
data() { data() {
return { return {
uploadUrl: '/bbs/mobile/common/upload', //上传地址 uploadUrl: '/bbs/mobile/common/upload', //上传地址
...@@ -96,7 +116,11 @@ ...@@ -96,7 +116,11 @@
fileList: [], fileList: [],
isVideo: false, isVideo: false,
isImg: false, isImg: false,
limiData: undefined limiData: undefined,
emotions,
showEmjo: false,
clientX: 0,
clientY: 0
}; };
}, },
computed: { computed: {
...@@ -161,7 +185,7 @@ ...@@ -161,7 +185,7 @@
} }
_this.fileList = newlist; _this.fileList = newlist;
this.limiData = this.limit - newlist.length this.limiData = this.limit - newlist.length
_this.$emit('componentsType', _this.isVideo ? 'VIDEO':'IMAGE') _this.$emit('componentsType', _this.isVideo ? 'VIDEO' : 'IMAGE')
}, },
immediate: true immediate: true
} }
...@@ -357,9 +381,31 @@ ...@@ -357,9 +381,31 @@
urls: [url] urls: [url]
}); });
}, },
//预览视频
videoPreview(url) {
this.$refs.videoPreviewRef.openModal(url)
},
// 选择话题 // 选择话题
handleOpenTopicSelection() { handleOpenTopicSelection() {
this.showEmjo = false
this.$emit('handleTopic') this.$emit('handleTopic')
},
// 打开表情选择
handleShowEmjo(e) {
this.showEmjo = !this.showEmjo
this.clientX = (e.target.offsetLeft * 2 - 158);
this.clientY = ((e.target.offsetTop * 2) + 34);
},
// 选择表情
handleSelectEmjo(emotion){
this.showEmjo = false
this.$emit('handleEmjo',emotion)
},
// 关闭表情窗
closeEmjo(){
if(this.showEmjo){
this.showEmjo = false
}
} }
} }
}; };
...@@ -367,8 +413,6 @@ ...@@ -367,8 +413,6 @@
<style scoped lang="scss"> <style scoped lang="scss">
.ImagesUpload { .ImagesUpload {
.pic-box { .pic-box {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
...@@ -434,4 +478,34 @@ ...@@ -434,4 +478,34 @@
} }
} }
} }
.emjo-box {
position: fixed;
width: 90vw;
min-height: 100rpx;
background-color: white;
box-shadow: 0px 2px 12rpx 0px rgba(0, 0, 0, 0.2);
border-radius: 10rpx;
z-index: 999;
.emotion-list {
display: flex;
flex-wrap: wrap;
padding: 10px;
overflow-y: auto;
}
.emotion-item {
width: 14%;
height:100rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
}
}
</style> </style>
\ No newline at end of file
<template>
<view class="video-preview-shadow" v-if="visible">
<div class="preview-box">
<view class="title">预览</view>
<video :src="url" class="video" :autoplay="autoplay"></video>
<view class="btn" @click="visible=false">关闭</view>
</div>
</view>
</template>
<script>
export default {
name: "VideoPreview",
data() {
return {
visible: false,
url: '',
autoplay:false
};
},
methods: {
openModal(url) {
this.visible = true
this.$nextTick(() => {
this.url = url;
this.autoplay = true
})
}
}
}
</script>
<style lang="scss" scoped>
.video-preview-shadow {
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.3);
z-index: 999;
position: fixed;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
.preview-box {
width: 90vw;
height: 1200rpx;
background-color: white;
border-radius: 20rpx;
.title{
height: 100rpx;
line-height: 100rpx;
text-align: center;
font-size: 32rpx;
font-weight: bold;
}
.video{
width: 100%;
height:calc(100% - 220rpx);
display: flex;
align-items: center;
justify-content: center;
}
.btn{
height: 120rpx;
line-height: 120rpx;
text-align: center;
font-weight: bold;
}
}
}
</style>
\ No newline at end of file
...@@ -3,6 +3,8 @@ import App from './App' ...@@ -3,6 +3,8 @@ import App from './App'
import { createSSRApp } from 'vue' import { createSSRApp } from 'vue'
import store from './store' import store from './store'
import './static/iconfont/iconfont.js'
export function createApp() { export function createApp() {
const app = createSSRApp(App) const app = createSSRApp(App)
app.use(store) app.use(store)
......
<template> <template>
<view class="page-container"> <view class="page-container">
<view class="search-box-common"> <view class="search-box-common">
<image src="/static/images/search-icon.png" class="search-icon"></image> <image src="/static/images/search-icon.png" class="search-icon" @click="refreshContentList"></image>
<input placeholder="搜索" placeholder-class="placeholder-class" /> <input placeholder="搜索" style="width: 100%;" v-model="queryParams.content" @keypress.enter="refreshContentList" placeholder-class="placeholder-class" />
</view> </view>
<scroll-view class="scroll-view-index" scroll-y :show-scrollbar="false" v-if="list.length>0" @scrolltolower="loadMore"> <scroll-view class="scroll-view-index" scroll-y :show-scrollbar="false" v-if="list.length>0"
@scrolltolower="loadMore">
<template v-for="(item,index) in list"> <template v-for="(item,index) in list">
<dynamic-item :form="item" @refresh="getList"></dynamic-item> <dynamic-item :form="item" @refresh="refreshContentList"></dynamic-item>
</template> </template>
</scroll-view> </scroll-view>
<scroll-view class="scroll-view-index" scroll-y :show-scrollbar="false" v-else> <scroll-view class="scroll-view-index" scroll-y :show-scrollbar="false" v-else>
...@@ -16,7 +17,7 @@ ...@@ -16,7 +17,7 @@
<!-- 发布按钮 --> <!-- 发布按钮 -->
<movable-area class="movable-area"> <movable-area class="movable-area">
<movable-view class="add-dynamic-box move-button" :x="area.x" :y="area.y" direction="all" @change="onChange" <movable-view class="add-dynamic-box move-button" :x="area.x" :y="area.y" direction="all" @change="onChange"
damping="30" @click="toPublishPage"> damping="30" @click="toPublishPage">
<image class="icon" src="/static/images/publish-icon.png"></image> <image class="icon" src="/static/images/publish-icon.png"></image>
</movable-view> </movable-view>
</movable-area> </movable-area>
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
<script> <script>
import DynamicItem from '@/components/Dynamic-Item.vue' import DynamicItem from '@/components/Dynamic-Item.vue'
import ListEmpty from '@/components/ListEmpty.vue'; import ListEmpty from '@/components/ListEmpty.vue';
import emotions from '@/utils/emjo';
import { import {
dynamicList dynamicList
} from '@/api/api.js' } from '@/api/api.js'
...@@ -41,7 +43,8 @@ ...@@ -41,7 +43,8 @@
area: { area: {
x: 330, x: 330,
y: 600 y: 600
} },
emotions
} }
}, },
components: { components: {
...@@ -62,24 +65,27 @@ ...@@ -62,24 +65,27 @@
}, },
onPullDownRefresh() { onPullDownRefresh() {
this.refreshContentList() this.refreshContentList()
setTimeout(()=>{ setTimeout(() => {
uni.stopPullDownRefresh(); uni.stopPullDownRefresh();
}, 1000) }, 1000)
}, },
methods: { methods: {
getList() { getList() {
let _this = this;
uni.showLoading({ uni.showLoading({
title:'加载中...' title: '加载中...'
}) })
dynamicList(this.queryParams).then(res => { dynamicList(this.queryParams).then(res => {
this.list = [...this.list,...res.rows]; this.list = [...this.list, ...res.rows]
this.total = res.total this.total = res.total
}).finally(()=>{ }).finally(() => {
uni.hideLoading() uni.hideLoading()
}) })
}, },
loadMore() { loadMore() {
if(this.list.length < this.total){ if (this.list.length < this.total) {
this.queryParams.pageNum += 1; this.queryParams.pageNum += 1;
this.getList() this.getList()
} }
...@@ -115,10 +121,11 @@ ...@@ -115,10 +121,11 @@
position: fixed; position: fixed;
height: 100%; height: 100%;
width: 100%; width: 100%;
z-index: 999; z-index: 998;
pointer-events: none; pointer-events: none;
left: 0; left: 0;
top: 0; top: 0;
.move-button { .move-button {
pointer-events: auto; pointer-events: auto;
display: flex; display: flex;
......
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
</view> </view>
<view class="publish-box"> <view class="publish-box">
<textarea class="textarea" v-model="form.content" placeholder="分享有趣事~" <textarea class="textarea" v-model="form.content" placeholder="分享有趣事~"
placeholder-class="placeholder-class" /> placeholder-class="placeholder-class" @focus="handleFocus" />
<image-upload v-model="form.attachments" :limit="9" :showIcons="true" <image-upload v-model="form.attachments" ref="uploadRef" :limit="9" :showEmjo="showEmjo" :showIcons="true"
@componentsType="handleSetComponentsType" @handleTopic="handleOpenTopicSelection"></image-upload> @componentsType="handleSetComponentsType" @handleTopic="handleOpenTopicSelection"
@handleEmjo="handleAddEmjo"></image-upload>
</view> </view>
<view class="permission-box"> <view class="permission-box">
<view class="item"> <view class="item">
...@@ -32,7 +33,7 @@ ...@@ -32,7 +33,7 @@
<!-- pk数据编辑 --> <!-- pk数据编辑 -->
<view class="pk-edit-box" v-if="form.isEnableVote==1"> <view class="pk-edit-box" v-if="form.isEnableVote==1">
<view class="textarea-box"> <view class="textarea-box">
<textarea class="textarea" v-model="form.voteTitle" placeholder="请输入PK标题~(0/12)" <textarea class="textarea" v-model="form.voteTitle" maxlength="12" placeholder="请输入PK标题~(0/12)"
placeholder-class="placeholder-class" /> placeholder-class="placeholder-class" />
</view> </view>
<view class="pk-type-box"> <view class="pk-type-box">
...@@ -60,11 +61,13 @@ ...@@ -60,11 +61,13 @@
<view class="choice-box" v-if="form.voteOptionType=='TEXT'"> <view class="choice-box" v-if="form.voteOptionType=='TEXT'">
<view class="choice-item text-item"> <view class="choice-item text-item">
<view class="label">选项1</view> <view class="label">选项1</view>
<input class="text" maxlength="4" v-model="form.voteOptions[0].name" placeholder="请输入选项1(4字以内)" /> <input class="text" maxlength="4" v-model="form.voteOptions[0].name"
placeholder="请输入选项1(4字以内)" />
</view> </view>
<view class="choice-item text-item"> <view class="choice-item text-item">
<view class="label">选项2</view> <view class="label">选项2</view>
<input class="text" maxlength="4" v-model="form.voteOptions[1].name" placeholder="请输入选项2(4字以内)" /> <input class="text" maxlength="4" v-model="form.voteOptions[1].name"
placeholder="请输入选项2(4字以内)" />
</view> </view>
</view> </view>
</view> </view>
...@@ -116,6 +119,7 @@ ...@@ -116,6 +119,7 @@
crateNewDynamic crateNewDynamic
} from '@/api/api.js'; } from '@/api/api.js';
import ImageUpload from '@/components/ImageUpload.vue' import ImageUpload from '@/components/ImageUpload.vue'
import emotions from '@/utils/emjo';
export default { export default {
data() { data() {
return { return {
...@@ -149,7 +153,9 @@ ...@@ -149,7 +153,9 @@
}], }],
}, },
voteOptionsAffirmative: [], voteOptionsAffirmative: [],
voteOptionsOpposing: [] voteOptionsOpposing: [],
emotions,
showEmjo:false
} }
}, },
components: { components: {
...@@ -164,10 +170,32 @@ ...@@ -164,10 +170,32 @@
this.topicList.push({ this.topicList.push({
name: topicName, name: topicName,
id: topicId, id: topicId,
}) })
} }
}, },
// 当内容变化时自动更新预览
watch: {
"form.content": {
immediate: true,
handler(val, oldVal) {
if (val === oldVal) return;
if (val && val.length < oldVal.length) {
// 计算删除的字符长度(正常退格删1个,表情需要删N个)
const deleteLen = oldVal.length - val.length;
// 检查删除前的末尾是否是表情,且删除长度不够(比如表情是4个字符,只删了1个)
const sortedEmojis = [...this.emotions].sort((a, b) => b.symbol.length - a.symbol.length);
for (const emoji of sortedEmojis) {
if (oldVal.endsWith(emoji.symbol) && deleteLen < emoji.symbol.length) {
// 补删剩余的表情字符(比如表情4个字符,已删1个,再删3个)
this.form.content = oldVal.slice(0, -emoji.symbol.length);
break;
}
}
}
}
}
},
methods: { methods: {
// 允许评论,开启精选等操作 // 允许评论,开启精选等操作
handleChange(e, field) { handleChange(e, field) {
...@@ -180,7 +208,7 @@ ...@@ -180,7 +208,7 @@
this.form.voteOptionType = e.detail.value this.form.voteOptionType = e.detail.value
}, },
handleSetComponentsType(type) { handleSetComponentsType(type) {
if (type && this.form.attachments.length > 0) { if (type && this.form.attachments && this.form.attachments.length > 0) {
this.form.type = type this.form.type = type
} else { } else {
this.form.type = 'TEXT' this.form.type = 'TEXT'
...@@ -211,7 +239,7 @@ ...@@ -211,7 +239,7 @@
this.$set(this.form.voteOptions[0], 'imageUrl', this.voteOptionsAffirmative[0].url); this.$set(this.form.voteOptions[0], 'imageUrl', this.voteOptionsAffirmative[0].url);
this.$set(this.form.voteOptions[1], 'imageUrl', this.voteOptionsOpposing[0].url); this.$set(this.form.voteOptions[1], 'imageUrl', this.voteOptionsOpposing[0].url);
} }
if(!this.form.voteTitle){ if (!this.form.voteTitle) {
uni.showToast({ uni.showToast({
title: '请输入PK标题', title: '请输入PK标题',
icon: 'error' icon: 'error'
...@@ -290,6 +318,40 @@ ...@@ -290,6 +318,40 @@
} }
}); });
},
// textarea聚焦时关闭表情窗口
handleFocus(){
this.$refs.uploadRef.closeEmjo()
},
// 添加表情
handleAddEmjo(emotion) {
console.log(emotion)
this.form.content = this.form.content + emotion.symbol
},
// 删除表情
deleteContent() {
// 1. 处理 form.content 为空的情况,直接返回避免报错
if (!this.form.content || this.form.content.trim() === '') {
return;
}
let deleted = false;
// 2. 按表情符号长度排序(长符号优先,避免短符号误匹配)
const sortedEmojis = [...this.emotions].sort((a, b) => b.symbol.length - a.symbol.length);
let lastIndex = -1;
// 3. 检查内容是否以某个表情符号结尾,记录最后一个表情符号的位置
for (const emoji of sortedEmojis) {
if (this.form.content.endsWith(emoji.symbol)) {
lastIndex = this.form.content.lastIndexOf(emoji.symbol);
deleted = true;
}
}
// 4. 若找到表情符号,删除所有匹配的表情符号
if (deleted) {
this.form.content = this.form.content.slice(0, lastIndex);
} else {
// 5. 若未匹配到表情,删除最后一个普通字符
this.form.content = this.form.content.slice(0, -1);
}
} }
} }
} }
......
@font-face {
font-family: "iconfont"; /* Project id 5030701 */
/* Color fonts */
src:
url('iconfont.woff2?t=1758858203810') format('woff2'),
url('iconfont.woff?t=1758858203810') format('woff'),
url('iconfont.ttf?t=1758858203810') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-biaoqing-bugaoxing:before {
content: "\e65b";
}
.icon-meigui:before {
content: "\e62f";
}
.icon-a-heshi:before {
content: "\e6af";
}
.icon-a-jizhi:before {
content: "\e6b1";
}
.icon-be024:before {
content: "\e667";
}
.icon-emo_bucuo:before {
content: "\e6a6";
}
.icon-huaji:before {
content: "\e601";
}
.icon-neijiu:before {
content: "\e600";
}
.icon-icon-test:before {
content: "\e60d";
}
.icon-icon-test1:before {
content: "\e60f";
}
.icon-jingdai:before {
content: "\e627";
}
.icon-coolapk_emotion__koubi:before {
content: "\e615";
}
.icon-liuhan:before {
content: "\e603";
}
.icon-qinqin:before {
content: "\e610";
}
.icon-kulian:before {
content: "\e611";
}
.icon-jingxi:before {
content: "\e616";
}
.icon-feiwen:before {
content: "\e700";
}
.icon-zaijian:before {
content: "\e61f";
}
.icon-shuizhao-01:before {
content: "\e669";
}
.icon-biaoqing-weixiao:before {
content: "\e65f";
}
.icon-ico_emoji_26:before {
content: "\e67f";
}
.icon-a-ziyuan135:before {
content: "\e60c";
}
.icon-a-ziyuan123:before {
content: "\e60e";
}
.icon-emoji-18:before {
content: "\e69b";
}
.icon-a-Facewithcoldsweat:before {
content: "\e64a";
}
.icon-a-kelian:before {
content: "\e6aa";
}
.icon-bangzhuzhongxin:before {
content: "\e634";
}
.icon-a-Thumbsup:before {
content: "\e656";
}
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -10,7 +10,7 @@ $common-width:94vw; ...@@ -10,7 +10,7 @@ $common-width:94vw;
@mixin common-page($height) { @mixin common-page($height) {
background-color: rgba(244, 244, 244, 1); background-color: rgba(244, 244, 244, 1);
height: $height; height: $height;
color: rgba(16, 16, 16, 1);
font-size: 28rpx; font-size: 28rpx;
font-family: Arial; font-family: Arial;
padding: 2rpx 0; padding: 2rpx 0;
......
...@@ -252,6 +252,25 @@ ...@@ -252,6 +252,25 @@
.content{ .content{
color: black; color: black;
margin: 10rpx 0; margin: 10rpx 0;
display: flex;
.text {
width: calc(100% - 180rpx);
}
.operation-text {
width: 180rpx;
text-align: right;
font-size: 24rpx;
display: flex;
align-items: center;
justify-content: flex-end;
}
.mr10{
margin-right: 12rpx;
}
.add-text {
color: #0058B6;
}
} }
.tips{ .tips{
font-size: 24rpx; font-size: 24rpx;
......
const emotions = [{
code: 'icon-meigui',
name: '玫瑰',
symbol: '[玫瑰]'
},
{
code: 'icon-a-heshi',
name: '合十',
symbol: '[合十]'
},
{
code: 'icon-a-jizhi',
name: '机智',
symbol: '[机智]'
},
{
code: 'icon-huaji',
name: '滑稽',
symbol: '[滑稽]'
},
{
code: 'icon-be024',
name: '兴奋',
symbol: '[兴奋]'
},
{
code: 'icon-emo_bucuo',
name: '调皮',
symbol: '[调皮]'
},
{
code: 'icon-neijiu',
name: '内疚',
symbol: '[内疚]'
},
{
code: 'icon-icon-test',
name: '疲倦',
symbol: '[疲倦]'
},
{
code: 'icon-icon-test1',
name: '大哭',
symbol: '[大哭]'
},
{
code: 'icon-jingdai',
name: '惊呆',
symbol: '[惊呆]'
},
{
code: 'icon-coolapk_emotion__koubi',
name: '抠鼻',
symbol: '[抠鼻]'
},
{
code: 'icon-liuhan',
name: '流汗',
symbol: '[流汗]'
},
{
code: 'icon-qinqin',
name: '亲亲',
symbol: '[亲亲]'
},
{
code: 'icon-kulian',
name: '哭脸',
symbol: '[哭脸]'
},
{
code: 'icon-jingxi',
name: '惊喜',
symbol: '[惊喜]'
},
{
code: 'icon-zaijian',
name: '再见',
symbol: '[再见]'
},
{
code: 'icon-shuizhao-01',
name: '睡着了',
symbol: '[睡着了]'
},
{
code: 'icon-biaoqing-weixiao',
name: '微笑',
symbol: '[微笑]'
},
{
code: 'icon-ico_emoji_26',
name: '叹气',
symbol: '[叹气]'
},
{
code: 'icon-feiwen',
name: '飞吻',
symbol: '[飞吻]'
},
{
code: 'icon-a-ziyuan135',
name: '偷笑',
symbol: '[偷笑]'
},
{
code: 'icon-a-ziyuan123',
name: '鼓掌',
symbol: '[鼓掌]'
},
{
code: 'icon-emoji-18',
name: '抓狂',
symbol: '[抓狂]'
},
{
code: 'icon-a-Facewithcoldsweat',
name: '尴尬',
symbol: '[尴尬]'
},
{
code: 'icon-a-kelian',
name: '可怜',
symbol: '[可怜]'
},
{
code: 'icon-bangzhuzhongxin',
name: '爱心',
symbol: '[爱心]'
},
{
code: 'icon-a-Thumbsup',
name: '点赞',
symbol: '[点赞]'
},
{
code:'icon-biaoqing-bugaoxing',
name: '不高兴',
symbol: '[不高兴]'
}
]
export default emotions;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment