fix:完善评论组件@圈人功能
This commit is contained in:
@@ -2,35 +2,40 @@
|
|||||||
<div class="comment-app">
|
<div class="comment-app">
|
||||||
<section class="main-publisher">
|
<section class="main-publisher">
|
||||||
<div class="input-wrapper">
|
<div class="input-wrapper">
|
||||||
<el-input
|
<mentionEditor v-model="mainInput" :users="userList" @select="onUserMentioned">
|
||||||
v-model="mainInput"
|
<el-input
|
||||||
type="textarea"
|
v-model="mainInput"
|
||||||
:rows="4"
|
type="textarea"
|
||||||
:placeholder="t('comment.placeholder')"
|
:rows="4"
|
||||||
resize="none"
|
:placeholder="t('comment.placeholder')"
|
||||||
/>
|
resize="none"
|
||||||
<div class="input-tools">
|
/>
|
||||||
<div class="left-icons">
|
</mentionEditor>
|
||||||
<!-- 提到-后续迭代 -->
|
<div class="input-tools">
|
||||||
<!-- <el-button link title="提到">@</el-button> -->
|
<div class="left-icons">
|
||||||
<!-- 图片功能-后续迭代 -->
|
<!-- 提到-后续迭代 -->
|
||||||
<!-- <el-button link title="图片"><el-icon><Picture /></el-icon></el-button> -->
|
<!-- <el-button link title="提到">@</el-button> -->
|
||||||
<!-- 附件功能-后续迭代 -->
|
<!-- 图片功能-后续迭代 -->
|
||||||
<!-- <el-button link title="附件"><el-icon><Paperclip /></el-icon></el-button> -->
|
<!-- <el-button link title="图片"><el-icon><Picture /></el-icon></el-button> -->
|
||||||
|
<!-- 附件功能-后续迭代 -->
|
||||||
<!-- 表情功能 -->
|
<!-- <el-button link title="附件"><el-icon><Paperclip /></el-icon></el-button> -->
|
||||||
<emoji-picker @select="(e) => onSelectEmoji(e, 'main')" />
|
|
||||||
|
<!-- 表情功能 -->
|
||||||
|
<emoji-picker @select="(e) => onSelectEmoji(e, 'main')" />
|
||||||
|
</div>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button
|
||||||
|
class="send-btn"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="submitMainComment"
|
||||||
|
>
|
||||||
|
<el-icon :size="20" :title="t('comment.send')"
|
||||||
|
><Promotion
|
||||||
|
/></el-icon>
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-divider direction="vertical" />
|
|
||||||
<el-button
|
|
||||||
class="send-btn"
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="submitMainComment"
|
|
||||||
>
|
|
||||||
<el-icon :size="20" :title="t('comment.send')"><Promotion /></el-icon>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -127,7 +132,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="time">{{ formatTime(reply.time) }}</span>
|
<span class="time">{{ formatTime(reply.time) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-body">{{ reply.content }}</div>
|
<div class="content-body" v-html="parseMention(reply.content)"></div>
|
||||||
<!-- 回复 删除功能 -->
|
<!-- 回复 删除功能 -->
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<el-button link @click="openReply(item, item)">
|
<el-button link @click="openReply(item, item)">
|
||||||
@@ -153,13 +158,17 @@
|
|||||||
class="inline-publisher"
|
class="inline-publisher"
|
||||||
>
|
>
|
||||||
<div class="input-wrapper">
|
<div class="input-wrapper">
|
||||||
|
<mentionEditor v-model="replyInput" :users="userList" @select="onUserMentioned">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="replyInput"
|
v-model="replyInput"
|
||||||
:placeholder="`${t('comment.reply')} @${activeReply.targetName}...`"
|
:placeholder="`${t('comment.reply')} @${
|
||||||
|
activeReply.targetName
|
||||||
|
}...`"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
resize="none"
|
resize="none"
|
||||||
/>
|
/>
|
||||||
|
</mentionEditor>
|
||||||
<div class="input-tools">
|
<div class="input-tools">
|
||||||
<div class="left-icons">
|
<div class="left-icons">
|
||||||
<!-- 表情功能 -->
|
<!-- 表情功能 -->
|
||||||
@@ -174,7 +183,9 @@
|
|||||||
link
|
link
|
||||||
@click="submitReply"
|
@click="submitReply"
|
||||||
>
|
>
|
||||||
<el-icon :size="20" :title="t('comment.send')"><Promotion /></el-icon>
|
<el-icon :size="20" :title="t('comment.send')"
|
||||||
|
><Promotion
|
||||||
|
/></el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -194,8 +205,9 @@ import EmojiPicker from "./EmojiPicker.vue";
|
|||||||
import NameAvatar from "@/components/nameAvatar/index.vue";
|
import NameAvatar from "@/components/nameAvatar/index.vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useUserStore } from "@/store";
|
import { useUserStore } from "@/store";
|
||||||
import { useRelativeTime } from '@/hooks/useRelativeTime'
|
import { useRelativeTime } from "@/hooks/useRelativeTime";
|
||||||
const { formatTime } = useRelativeTime()
|
import mentionEditor from "./mentionEditor.vue";
|
||||||
|
const { formatTime } = useRelativeTime();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
// 当前用户信息
|
// 当前用户信息
|
||||||
@@ -272,47 +284,60 @@ const cancelReply = () => {
|
|||||||
activeReply.groupId = null;
|
activeReply.groupId = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ 识别解析函数
|
// @圈人的操作
|
||||||
const parseMention = (text) => {
|
const userList = [
|
||||||
if (!text) return "";
|
{ id: 1, nickname: "李星倩" },
|
||||||
// 基础转义
|
{ id: 2, nickname: "冯娜" },
|
||||||
let safeText = text.replace(/</g, "<").replace(/>/g, ">");
|
{ id: 3, nickname: "张三" }
|
||||||
// 正则匹配 @用户,包裹为 span
|
];
|
||||||
return safeText.replace(
|
|
||||||
/@([\u4e00-\u9fa5\w-]+)/g,
|
const onUserMentioned = (user) => {
|
||||||
'<span class="mention-link">@$1</span>'
|
console.log('Mentioned:', user.nickname);
|
||||||
);
|
|
||||||
};
|
|
||||||
// @提及 圈人操作
|
|
||||||
const handleMentionAction = (name) => {
|
|
||||||
const mentionStr = typeof name === "string" ? `@${name} ` : "@";
|
|
||||||
mainInput.value += mentionStr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitMainComment = () => {
|
const submitMainComment = () => {
|
||||||
if (!mainInput.value.trim()) return ElMessage.warning("内容不能为空");
|
if (!mainInput.value.trim()) return ElMessage.warning("内容不能为空");
|
||||||
|
|
||||||
|
const formattedContent = mainInput.value.replace(
|
||||||
|
/@([^\s@]+)/g,
|
||||||
|
'<span class="mention-highlight">@$1</span>'
|
||||||
|
);
|
||||||
|
|
||||||
commentData.value.unshift({
|
commentData.value.unshift({
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
...currentUser.value,
|
...currentUser.value,
|
||||||
content: mainInput.value,
|
content: formattedContent,
|
||||||
time: new Date().valueOf(),
|
time: new Date().valueOf(),
|
||||||
canDelete: true,
|
canDelete: true,
|
||||||
children: [],
|
children: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
mainInput.value = "";
|
mainInput.value = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitReply = () => {
|
// 通用的内容转换函数
|
||||||
const targetGroup = commentData.value.find(
|
const parseMention = (text) => {
|
||||||
(i) => i.id === activeReply.groupId
|
if (!text) return "";
|
||||||
|
return text.replace(
|
||||||
|
/@([^\s@]+)/g,
|
||||||
|
'<span class="mention-highlight">@$1</span>'
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitReply = () => {
|
||||||
|
const targetGroup = commentData.value.find(i => i.id === activeReply.groupId);
|
||||||
if (targetGroup) {
|
if (targetGroup) {
|
||||||
|
const formattedReply = replyInput.value.replace(
|
||||||
|
/@([^\s@]+)/g,
|
||||||
|
'<span class="mention-highlight">@$1</span>'
|
||||||
|
);
|
||||||
|
|
||||||
targetGroup.children.push({
|
targetGroup.children.push({
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
...currentUser.value,
|
...currentUser.value,
|
||||||
replyTo: activeReply.targetName,
|
replyTo: activeReply.targetName,
|
||||||
content: replyInput.value,
|
content: formattedReply, // 使用处理后的 HTML
|
||||||
time: "刚刚",
|
time: new Date().valueOf(),
|
||||||
});
|
});
|
||||||
cancelReply();
|
cancelReply();
|
||||||
}
|
}
|
||||||
@@ -506,6 +531,23 @@ $color-white: #fff;
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 在 style 标签内添加 */
|
||||||
|
:deep(.mention-highlight) {
|
||||||
|
background-color: rgba(64, 158, 255, 0.1); /* 浅蓝色背景 */
|
||||||
|
color: #409eff; /* 蓝色文字 */
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(64, 158, 255, 0.2);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 评论组件骨架屏
|
// 评论组件骨架屏
|
||||||
|
|||||||
235
src/modules/Comment/mentionEditor.vue
Normal file
235
src/modules/Comment/mentionEditor.vue
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mention-container" ref="containerRef">
|
||||||
|
<slot></slot>
|
||||||
|
|
||||||
|
<div ref="mirrorRef" class="textarea-mirror" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<transition name="el-zoom-in-top">
|
||||||
|
<div
|
||||||
|
v-if="showPopover"
|
||||||
|
class="mention-popover"
|
||||||
|
:style="{ top: popoverPos.top + 'px', left: popoverPos.left + 'px' }"
|
||||||
|
>
|
||||||
|
<div class="mention-list" v-if="filteredList.length">
|
||||||
|
<div
|
||||||
|
v-for="(user, index) in filteredList"
|
||||||
|
:key="user.id"
|
||||||
|
:class="['mention-item', { active: index === selectIndex }]"
|
||||||
|
@mousedown.prevent="handleSelect(user)"
|
||||||
|
@mouseenter="selectIndex = index"
|
||||||
|
>
|
||||||
|
<name-avatar :name="user.nickname" :size="20" />
|
||||||
|
<span class="name">{{ user.nickname }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="mention-empty">未找到人员</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted, nextTick, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: String,
|
||||||
|
users: { type: Array, default: () => [] }
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'select']);
|
||||||
|
|
||||||
|
const containerRef = ref(null);
|
||||||
|
const mirrorRef = ref(null);
|
||||||
|
const showPopover = ref(false);
|
||||||
|
const searchKey = ref("");
|
||||||
|
const selectIndex = ref(0);
|
||||||
|
const popoverPos = ref({ top: 0, left: 0 });
|
||||||
|
let targetInput = null; // 存储原生 textarea 引用
|
||||||
|
|
||||||
|
const filteredList = computed(() => {
|
||||||
|
return props.users.filter(u => u.nickname.includes(searchKey.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算像素坐标的核心逻辑
|
||||||
|
const computeCaretPosition = () => {
|
||||||
|
if (!targetInput || !mirrorRef.value) return;
|
||||||
|
|
||||||
|
const val = targetInput.value;
|
||||||
|
const cursorIndex = targetInput.selectionStart;
|
||||||
|
|
||||||
|
// 找到最近的一个 @
|
||||||
|
const textBefore = val.slice(0, cursorIndex);
|
||||||
|
const lastAtPos = textBefore.lastIndexOf("@");
|
||||||
|
|
||||||
|
// 获取 @ 到光标之间的内容作为搜索词
|
||||||
|
searchKey.value = textBefore.slice(lastAtPos + 1);
|
||||||
|
|
||||||
|
// --- 镜像同步逻辑 ---
|
||||||
|
const style = window.getComputedStyle(targetInput);
|
||||||
|
const mirror = mirrorRef.value;
|
||||||
|
|
||||||
|
// 复制所有会影响排版的样式
|
||||||
|
const propList = ['fontFamily', 'fontSize', 'fontWeight', 'lineHeight', 'paddingTop', 'paddingLeft', 'paddingRight', 'paddingBottom', 'borderWidth', 'boxSizing', 'letterSpacing', 'wordBreak'];
|
||||||
|
propList.forEach(prop => mirror.style[prop] = style[prop]);
|
||||||
|
mirror.style.width = targetInput.offsetWidth + 'px';
|
||||||
|
|
||||||
|
// 将内容填入镜像,在 @ 位置插入标记元素
|
||||||
|
const contentBeforeAt = val.slice(0, lastAtPos);
|
||||||
|
const contentAtToCursor = val.slice(lastAtPos, cursorIndex);
|
||||||
|
|
||||||
|
// 使用 textContent 防止 XSS,使用 <br> 处理换行
|
||||||
|
mirror.textContent = contentBeforeAt;
|
||||||
|
const marker = document.createElement('span');
|
||||||
|
marker.textContent = '@';
|
||||||
|
marker.style.color = 'red'; // 仅调试可见
|
||||||
|
mirror.appendChild(marker);
|
||||||
|
|
||||||
|
// 继续填充剩余内容以保持排版一致
|
||||||
|
const remaining = document.createTextNode(contentAtToCursor);
|
||||||
|
mirror.appendChild(remaining);
|
||||||
|
|
||||||
|
// 获取标记位的相对坐标
|
||||||
|
nextTick(() => {
|
||||||
|
popoverPos.value = {
|
||||||
|
// offsetTop 是输入框顶部的偏移 + 标记位高度 - 滚动条高度
|
||||||
|
top: targetInput.offsetTop + marker.offsetTop + parseInt(style.lineHeight) - targetInput.scrollTop,
|
||||||
|
left: targetInput.offsetLeft + marker.offsetLeft
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInput = (e) => {
|
||||||
|
const el = e.target;
|
||||||
|
const val = el.value;
|
||||||
|
const pos = el.selectionStart; // 当前光标位置
|
||||||
|
|
||||||
|
// 1. 获取光标之前的文本
|
||||||
|
const textBefore = val.slice(0, pos);
|
||||||
|
|
||||||
|
// 2. 找到光标前最近的一个 @
|
||||||
|
const lastAtPos = textBefore.lastIndexOf("@");
|
||||||
|
|
||||||
|
if (lastAtPos !== -1) {
|
||||||
|
// 3. 关键逻辑:获取 @ 到光标之间的内容
|
||||||
|
const contentBetween = textBefore.slice(lastAtPos + 1);
|
||||||
|
|
||||||
|
// 4. 判断逻辑:
|
||||||
|
// - 如果 @ 和光标之间有空格,说明这一段提及已结束,不弹窗
|
||||||
|
// - 如果 @ 前面不是空格且不是开头,说明可能是邮箱地址,不弹窗
|
||||||
|
const charBeforeAt = textBefore[lastAtPos - 1];
|
||||||
|
const isAtStartOrAfterSpace = !charBeforeAt || /\s/.test(charBeforeAt);
|
||||||
|
const hasSpaceBetween = /\s/.test(contentBetween);
|
||||||
|
|
||||||
|
if (isAtStartOrAfterSpace && !hasSpaceBetween) {
|
||||||
|
searchKey.value = contentBetween;
|
||||||
|
showPopover.value = true;
|
||||||
|
computeCaretPosition(); // 重新计算位置
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其余情况全部关闭
|
||||||
|
showPopover.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (e) => {
|
||||||
|
if (!showPopover.value) return;
|
||||||
|
if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
selectIndex.value = (selectIndex.value - 1 + filteredList.value.length) % filteredList.value.length;
|
||||||
|
} else if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
selectIndex.value = (selectIndex.value + 1) % filteredList.value.length;
|
||||||
|
} else if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (filteredList.value[selectIndex.value]) handleSelect(filteredList.value[selectIndex.value]);
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
showPopover.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelect = (user) => {
|
||||||
|
const val = props.modelValue;
|
||||||
|
const pos = targetInput.selectionStart;
|
||||||
|
const textBefore = val.slice(0, pos);
|
||||||
|
const lastAtPos = textBefore.lastIndexOf("@");
|
||||||
|
|
||||||
|
const newValue = val.slice(0, lastAtPos) + `@${user.nickname} ` + val.slice(pos);
|
||||||
|
emit('update:modelValue', newValue);
|
||||||
|
emit('select', user);
|
||||||
|
showPopover.value = false;
|
||||||
|
nextTick(() => targetInput.focus());
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
targetInput = containerRef.value.querySelector('textarea');
|
||||||
|
if (targetInput) {
|
||||||
|
targetInput.addEventListener('input', handleInput);
|
||||||
|
targetInput.addEventListener('keydown', handleKeyDown);
|
||||||
|
targetInput.addEventListener('scroll', () => {
|
||||||
|
if (showPopover.value) computeCaretPosition();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mention-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 镜像层:必须绝对定位且隐藏 */
|
||||||
|
.textarea-mirror {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: -999;
|
||||||
|
visibility: hidden;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-popover {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3000;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-list {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 4px 0;
|
||||||
|
|
||||||
|
.mention-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #f0f7ff;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mention-empty {
|
||||||
|
padding: 12px;
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -183,11 +183,13 @@ const columns = computed(()=>[
|
|||||||
prop: "label",
|
prop: "label",
|
||||||
label: "字典名称",
|
label: "字典名称",
|
||||||
align: "center",
|
align: "center",
|
||||||
|
showOverflowTooltip:true,
|
||||||
slot: "labelName",
|
slot: "labelName",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: "value",
|
prop: "value",
|
||||||
label: "字典值",
|
label: "字典值",
|
||||||
|
showOverflowTooltip:true,
|
||||||
align: "center",
|
align: "center",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -206,7 +208,6 @@ const columns = computed(()=>[
|
|||||||
label: "更新时间",
|
label: "更新时间",
|
||||||
align: "center",
|
align: "center",
|
||||||
showOverflowTooltip: true,
|
showOverflowTooltip: true,
|
||||||
width:200,
|
|
||||||
formatter: (val) => {
|
formatter: (val) => {
|
||||||
return val.updateTime
|
return val.updateTime
|
||||||
? dayjs(val.updateTime).format("YYYY-MM-DD HH:mm")
|
? dayjs(val.updateTime).format("YYYY-MM-DD HH:mm")
|
||||||
@@ -217,7 +218,7 @@ const columns = computed(()=>[
|
|||||||
prop: "actions",
|
prop: "actions",
|
||||||
label: "操作",
|
label: "操作",
|
||||||
align: "right",
|
align: "right",
|
||||||
width: "300",
|
width:200,
|
||||||
actions:[
|
actions:[
|
||||||
{
|
{
|
||||||
label: "添加二级字段",
|
label: "添加二级字段",
|
||||||
@@ -381,7 +382,7 @@ defineExpose({
|
|||||||
hasChild.value = item.hasChild ?? false;
|
hasChild.value = item.hasChild ?? false;
|
||||||
// 处理子集的弹窗
|
// 处理子集的弹窗
|
||||||
if (hasChild.value) {
|
if (hasChild.value) {
|
||||||
size.value = "60%";
|
size.value = "65%";
|
||||||
childId.value = item.id;
|
childId.value = item.id;
|
||||||
if (item.onClose) {
|
if (item.onClose) {
|
||||||
onCloseCallback.value = item.onClose;
|
onCloseCallback.value = item.onClose;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<el-form-item label="域名">
|
<el-form-item label="域名">
|
||||||
<el-input v-model="form.domain" placeholder="example.com">
|
<el-input v-model="form.domain" placeholder="example.com">
|
||||||
<template #prefix
|
<template #prefix
|
||||||
><el-icon><Global /></el-icon
|
><el-icon><component is="Global" /></el-icon
|
||||||
></template>
|
></template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -95,7 +95,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: "AddOrgan" });
|
defineOptions({ name: "AddOrgan" });
|
||||||
|
|
||||||
const dialogVisible = ref(true);
|
|
||||||
|
const dialogVisible = defineModel("visible")
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: "",
|
name: "",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<OverflowTabs v-model="activeTab" :items="tabList" :height="60"/>
|
<OverflowTabs v-model="activeTab" :items="tabList" :height="60"/>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
<el-button type="primary" :icon="'Plus'" plain>新增集团</el-button>
|
<el-button type="primary" :icon="'Plus'" plain @click="onAddGroup">新增集团</el-button>
|
||||||
</template>
|
</template>
|
||||||
</stageBreadcrumbs>
|
</stageBreadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 添加集团-->
|
<!-- 添加集团-->
|
||||||
<addOrgan />
|
<addOrgan v-model:visible="showAddOrgan"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -89,6 +89,8 @@ const addValue = ref("");
|
|||||||
const search = ref("");
|
const search = ref("");
|
||||||
const activeName = ref("baseInfo");
|
const activeName = ref("baseInfo");
|
||||||
|
|
||||||
|
const showAddOrgan = ref(false);
|
||||||
|
|
||||||
// 集团Tabs切换
|
// 集团Tabs切换
|
||||||
const activeTab = ref(1);
|
const activeTab = ref(1);
|
||||||
const tabList = ref([
|
const tabList = ref([
|
||||||
@@ -104,6 +106,10 @@ interface Tree {
|
|||||||
children?: Tree[];
|
children?: Tree[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加集团
|
||||||
|
const onAddGroup = () => {
|
||||||
|
showAddOrgan.value = true;
|
||||||
|
};
|
||||||
// 获取图标组件
|
// 获取图标组件
|
||||||
const getIconComponent = (node: any) => {
|
const getIconComponent = (node: any) => {
|
||||||
if (node.level === 1) {
|
if (node.level === 1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user