知识库-分享-功能联调

main
ysn 2 days ago
parent 2b2f02be72
commit def8b10005
  1. 101
      src/views/knowledge/index.vue
  2. 229
      src/views/message/components/CreateGroupDialog.vue

@ -276,7 +276,7 @@
<!-- 分享弹窗 - 选择联系人 -->
<CreateGroupDialog
:visible.sync="shareDialogVisible"
ref="createGroupDialogRef"
title="选择人员"
:min-select-count="1"
@confirm="handleShareToContacts"
@ -313,7 +313,7 @@ export default {
CreateGroupDialog,
},
computed: {
...mapGetters(["config"]),
...mapGetters(["config", "userInfo"]),
},
data() {
return {
@ -598,54 +598,45 @@ export default {
})
.catch(() => {});
},
handleShare(row) {
this.shareItem = row;
this.shareDialogVisible = true;
this.$refs.createGroupDialogRef.show();
},
handleShareToContacts(selectedMembers) {
console.log("选中的成员:", selectedMembers, this.userInfo);
if (selectedMembers && selectedMembers.length > 0) {
const { id, netConfig } = this.$store.state.user;
const currentUserId = id;
const minioEndpoint =
netConfig?.MINIO_ENDPOINT_HTTPS?.trim() || "http://47.92.6.51:9100/";
selectedMembers.forEach((shareTarget) => {
const payloadContent = {
file_name: this.shareItem?.title || "",
file_path: this.shareItem?.file_path || "",
file_size: this.shareItem?.file_size || "",
file_time: this.shareItem?.create_time || "",
knowledge_id: this.shareItem?.id || 0,
thumbnail_path: this.shareItem?.thumbnail_path || "",
};
const messageData = {
client_id: `utalk-client-${currentUserId}`,
message: {
at_users: [],
message_id: 0,
payload: {
content: JSON.stringify(payloadContent),
file_duration: 0,
file_ico: "",
file_name: this.shareItem?.title || "",
file_path: this.shareItem?.file_path || "",
file_size: this.shareItem?.file_size || "",
file_type: this.shareItem?.file_type || "",
},
scene: 1,
source_id: currentUserId,
target_id: shareTarget.id,
timestamp: 0,
type: MessageType.KNOWLEDGE_SHAREs,
postMessagePushToUser({
client_id: "utalk-client-" + this.userInfo.id,
message: {
at_users: [],
message_id: 0,
payload: {
content: JSON.stringify({
file_name: this.shareItem.file_name,
file_path: this.shareItem.file_path,
file_size: this.shareItem.file_size,
knowledge_id: this.shareItem.id,
thumbnail_path: this.shareItem.thumbnail_path,
file_time: this.shareItem.file_time,
}),
file_duration: 0,
file_ico: "",
file_name: this.shareItem.file_name,
file_path: this.shareItem.file_path,
file_size: this.shareItem.file_size,
file_type: this.shareItem.file_type,
},
target_id: shareTarget.id,
topic: "/user/",
};
postMessagePushToUser(messageData).then((res) => {
this.$modal.msgSuccess(`已分享给 ${shareTarget.name}`);
});
scene: 1,
source_id: this.userInfo.id,
target_id: selectedMembers[0].id,
timestamp: 0,
type: "knowledge_share",
},
target_id: selectedMembers[0].id,
topic: "/user/¯",
}).then((res) => {
this.$modal.msgSuccess(`已分享给 ${selectedMembers[0].name}`);
});
this.shareDialogVisible = false;
this.shareItem = null;
}
@ -744,7 +735,13 @@ export default {
}
},
//
async saveKnowledgeToDB(filePath, file, thumbnailPath = "", bucket = "", recordId = null) {
async saveKnowledgeToDB(
filePath,
file,
thumbnailPath = "",
bucket = "",
recordId = null
) {
console.log("saveKnowledgeToDB:", filePath, file, thumbnailPath, bucket);
// bucket/object
@ -772,7 +769,9 @@ export default {
console.log("知识库记录保存成功");
//
if (recordId) {
const minioEndpoint = this.$store.state.user.netConfig?.MINIO_ENDPOINT_HTTPS?.trim() || "http://47.92.6.51:9100/";
const minioEndpoint =
this.$store.state.user.netConfig?.MINIO_ENDPOINT_HTTPS?.trim() ||
"http://47.92.6.51:9100/";
const fileUrl = minioEndpoint + fullFilePath;
this.updateTransferRecord(recordId, true, fileUrl);
}
@ -786,7 +785,8 @@ export default {
const records = existingData ? JSON.parse(existingData) : [];
// ID
const recordId = Date.now() + "_" + Math.random().toString(36).substr(2, 9);
const recordId =
Date.now() + "_" + Math.random().toString(36).substr(2, 9);
// transfer_type: 3
const newRecord = {
@ -821,7 +821,9 @@ export default {
const records = existingData ? JSON.parse(existingData) : [];
//
const recordIndex = records.findIndex(record => record.id === recordId);
const recordIndex = records.findIndex(
(record) => record.id === recordId
);
if (recordIndex !== -1) {
records[recordIndex].is_success = isSuccess;
records[recordIndex].is_uploading = false; //
@ -847,12 +849,14 @@ export default {
display: flex;
justify-content: space-between;
align-items: center;
.left {
display: flex;
align-items: center;
gap: 8px;
}
}
//
.resource-thumb {
width: 230px;
@ -873,6 +877,7 @@ export default {
display: flex;
justify-content: space-around;
padding: 20px 0;
.type-item {
width: 120px;
height: 120px;
@ -883,11 +888,13 @@ export default {
align-items: center;
justify-content: center;
cursor: pointer;
i {
font-size: 40px;
color: #409eff;
margin-bottom: 10px;
}
&:hover {
border-color: #409eff;
background: #f5f7fa;

@ -1,62 +1,35 @@
<template>
<div>
<el-dialog
:title="title"
:visible.sync="visible"
width="38%"
append-to-body
:before-close="handleClose"
>
<el-dialog :title="title" :visible.sync="visible" width="38%" append-to-body :before-close="handleClose">
<el-row :gutter="20" class="body">
<!-- 左侧联系人选择区域 -->
<el-col :span="12">
<!-- 搜索框 -->
<el-input
v-model="searchText"
placeholder="搜索联系人"
suffix-icon="el-icon-search"
clearable
@keyup.enter.native="handleSearch"
@clear="handleClearSearch"
style="margin-bottom: 10px"
/>
<el-input v-model="searchText" placeholder="搜索联系人" suffix-icon="el-icon-search" clearable
@keyup.enter.native="handleSearch" @clear="handleClearSearch" style="margin-bottom: 10px" />
<!-- 标签页 -->
<el-tabs v-model="activeName" @tab-click="handleClick">
<!-- 最近联系人 -->
<el-tab-pane label="最近联系人" name="recentContacts">
<div v-loading="loading" class="contact-list">
<div
v-for="member in displayMembers"
:key="member.id"
:class="[
'member-item',
{
active: isSelected(member.id),
offline: !member.online,
},
]"
@click="toggleSelect(member)"
>
<el-checkbox
v-model="member.selected"
:disabled="isSelectDisabled && !member.selected"
@change="toggleSelect(member)"
>
<div v-for="member in displayMembers" :key="member.id" :class="[
'member-item',
{
active: isSelected(member.id),
offline: !member.online,
},
]" @click="toggleSelect(member)">
<el-checkbox v-model="member.selected" :disabled="isSelectDisabled && !member.selected"
@change="toggleSelect(member)">
<div class="member-item-label">
<el-avatar
:src="member.avatar"
icon="el-icon-user-solid"
/>
<el-avatar :src="member.avatar" icon="el-icon-user-solid" />
<span class="member-name">{{ member.name }}</span>
<span v-if="member.online" class="online-dot" />
</div>
</el-checkbox>
</div>
<el-empty
v-if="!loading && displayMembers.length === 0"
description="暂无联系人"
/>
<el-empty v-if="!loading && displayMembers.length === 0" description="暂无联系人" />
</div>
</el-tab-pane>
@ -65,11 +38,7 @@
<div v-loading="loading" class="contact-list">
<div v-for="group in recentGroups" :key="group.id">
<!-- 群组标题 -->
<div
class="group-header"
:class="{ expanded: group.expanded }"
@click="toggleGroupExpand(group)"
>
<div class="group-header" :class="{ expanded: group.expanded }" @click="toggleGroupExpand(group)">
<el-avatar :size="32" :src="group.avatar" />
<span class="group-name">{{ group.name }}</span>
<i class="el-icon-arrow-right expand-icon" />
@ -77,35 +46,21 @@
<!-- 群成员列表 -->
<div v-if="group.expanded">
<!-- 全选 -->
<div
class="member-item"
:class="{ selected: group.allSelected }"
@click="toggleGroupAllSelect(group)"
>
<el-checkbox
v-model="group.allSelected"
@change="toggleGroupAllSelect(group)"
/>
<div class="member-item" :class="{ selected: group.allSelected }"
@click="toggleGroupAllSelect(group)">
<el-checkbox v-model="group.allSelected" @change="toggleGroupAllSelect(group)" />
<span class="member-name">全选</span>
</div>
<!-- 成员列表 -->
<div
v-for="member in group.members"
:key="member.id"
:class="[
'member-item',
{
active: isSelected(member.id),
offline: !member.online,
},
]"
@click="toggleSelect(member)"
>
<el-checkbox
v-model="member.selected"
:disabled="isSelectDisabled && !member.selected"
@change="toggleSelect(member)"
>
<div v-for="member in group.members" :key="member.id" :class="[
'member-item',
{
active: isSelected(member.id),
offline: !member.online,
},
]" @click="toggleSelect(member)">
<el-checkbox v-model="member.selected" :disabled="isSelectDisabled && !member.selected"
@change="toggleSelect(member)">
<div class="member-item-label">
<el-avatar :size="32" :src="member.avatar" />
<span class="member-name">{{ member.name }}</span>
@ -114,25 +69,16 @@
</div>
</div>
</div>
<el-empty
v-if="!loading && recentGroups.length === 0"
description="暂无群组"
/>
<el-empty v-if="!loading && recentGroups.length === 0" description="暂无群组" />
</div>
</el-tab-pane>
<!-- 组织架构 -->
<el-tab-pane label="组织架构" name="organization">
<div v-loading="loading" class="contact-list">
<el-tree
ref="orgTree"
:data="treeData"
:props="{ label: 'name', children: 'children' }"
:highlight-current="true"
node-key="id"
:expand-on-click-node="false"
@node-click="handleOrgNodeClick"
>
<el-tree ref="orgTree" :data="treeData" :props="{ label: 'name', children: 'children' }"
:highlight-current="true" node-key="id" :expand-on-click-node="false"
@node-click="handleOrgNodeClick">
<template #default="{ node, data }">
<div class="custom-tree-node">
<!-- 组织节点 -->
@ -142,30 +88,19 @@
</template>
<!-- 全选节点 -->
<template v-else-if="data.type === 'all'">
<el-checkbox
:indeterminate="data.indeterminate"
v-model="data.checkAll"
@change="handleOrgCheckAllChange(data)"
>
<el-checkbox :indeterminate="data.indeterminate" v-model="data.checkAll"
@change="handleOrgCheckAllChange(data)">
全选
</el-checkbox>
</template>
<!-- 用户节点 -->
<div v-else-if="data.type === 'user'" class="userline">
<el-checkbox
:value="isSelected(data.id)"
@change="toggleSelect(data)"
:disabled="isSelectDisabled && !isSelected(data.id)"
>
<el-checkbox :value="isSelected(data.id)" @change="toggleSelect(data)"
:disabled="isSelectDisabled && !isSelected(data.id)">
<div class="member-item-label">
<el-avatar
:size="32"
:src="
$store.state.user.netConfig
.MINIO_ENDPOINT_HTTPS + data.avatar
"
icon="el-icon-user-solid"
/>
<el-avatar :size="32" :src="$store.state.user.netConfig
.MINIO_ENDPOINT_HTTPS + data.avatar
" icon="el-icon-user-solid" />
<span class="member-name">{{ data.name }}</span>
<span v-if="data.online" class="online-dot" />
</div>
@ -184,59 +119,36 @@
</el-col>
<!-- 右侧已选人员 -->
<el-col
:span="12"
style="height: 100%; display: flex; flex-direction: column"
>
<el-col :span="12" style="height: 100%; display: flex; flex-direction: column">
<div class="selected-header">
已选人员({{ selectedMembers.length }}
<template v-if="maxSelectCount > 0">/{{ maxSelectCount }}</template
>)
<span
v-if="selectedMembers.length > 0"
class="clear-btn"
@click="clearAllSelected"
>
<template v-if="maxSelectCount > 0">/{{ maxSelectCount }}</template>)
<span v-if="selectedMembers.length > 0" class="clear-btn" @click="clearAllSelected">
<i class="el-icon-close" />
</span>
</div>
<div class="selected-list">
<div
v-for="member in selectedMembers"
:key="member.id"
class="selected-item"
>
<div v-for="member in selectedMembers" :key="member.id" class="selected-item">
<el-avatar :size="40" :src="member.avatar" />
<span class="selected-name">{{ member.name }}</span>
<span class="remove-btn" @click="removeSelected(member)">
<i class="el-icon-close" />
</span>
</div>
<el-empty
v-if="selectedMembers.length === 0"
description="暂无选中人员"
class="empty-tip"
/>
<el-empty v-if="selectedMembers.length === 0" description="暂无选中人员" class="empty-tip" />
</div>
<div
v-if="
maxSelectCount > 0 && selectedMembers.length >= maxSelectCount
"
class="max-tip"
>
<div v-if="
maxSelectCount > 0 && selectedMembers.length >= maxSelectCount
" class="max-tip">
已达到最大选择数量({{ maxSelectCount }})
</div>
</el-col>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button @click="handleClose"> </el-button>
<el-button
type="primary"
:loading="confirmLoading"
:disabled="selectedMembers.length < minSelectCount"
@click="confirmCreateGroup"
>
{{ confirmText }}
<el-button type="primary" :loading="confirmLoading" :disabled="selectedMembers.length < minSelectCount"
@click="confirmCreateGroup">
确认
</el-button>
</span>
</el-dialog>
@ -258,21 +170,13 @@ export default {
type: String,
default: "发起群聊",
},
visible: {
type: Boolean,
default: false,
},
minSelectCount: {
type: Number,
default: 2,
default: 1,
},
maxSelectCount: {
type: Number,
default: 0,
},
confirmText: {
type: String,
default: "创建",
default: 1,
},
confirmCallback: {
type: Function,
@ -281,6 +185,7 @@ export default {
},
data() {
return {
visible: false,
activeName: "recentContacts",
searchText: "",
loading: false,
@ -302,14 +207,11 @@ export default {
return [...online, ...offline];
},
},
watch: {
visible(val) {
if (val) {
this.loadData();
}
},
},
methods: {
show() {
this.visible = true;
this.loadData();
},
async loadData() {
await Promise.all([
this.fetchRecentContacts(),
@ -748,13 +650,8 @@ export default {
//
handleClose(done) {
this.$confirm("确认关闭?")
.then((_) => {
this.clearAllSelected();
this.searchText = "";
done();
})
.catch((_) => {});
this.clearAllSelected();
this.searchText = "";
},
//
@ -765,14 +662,16 @@ export default {
}
this.confirmLoading = true;
try {
// 1. props
if (typeof this.confirmCallback === "function") {
await this.confirmCallback(this.selectedMembers);
}
// 2. confirm
this.$emit("confirm", this.selectedMembers);
this.$emit("create-success", this.selectedMembers);
// 3.
this.clearAllSelected();
this.searchText = "";
this.$emit("update:visible", false);
this.visible = false;
} catch (e) {
this.$message.error("操作失败");
} finally {
@ -786,6 +685,7 @@ export default {
<style lang="scss" scoped>
.body {
height: 450px;
.contact-list {
height: 400px;
overflow-y: auto;
@ -812,10 +712,12 @@ export default {
color: #909399;
}
}
.member-item-label {
display: flex;
align-items: center;
gap: 8px;
.member-name {
font-size: 14px;
color: #303133;
@ -970,6 +872,7 @@ export default {
align-items: center;
justify-content: center;
}
.userline {
display: flex;
align-items: center;

Loading…
Cancel
Save