| | |
| | | <view class="sales-accoun"> |
| | | <!-- 使用通用页面头部组件 --> |
| | | <PageHeader title="知识库" |
| | | @back="goBack" /> |
| | | @back="goBack"> |
| | | <template #right> |
| | | <view class="header-actions"> |
| | | <uni-icons v-if="!batchMode" |
| | | type="download" |
| | | size="20" |
| | | color="#333" |
| | | @click="handleExport" /> |
| | | <uni-icons v-if="!batchMode" |
| | | type="trash" |
| | | size="20" |
| | | color="#333" |
| | | @click="enterBatchMode" /> |
| | | <uni-icons v-if="batchMode" |
| | | type="trash" |
| | | size="20" |
| | | color="#333" |
| | | @click="handleBatchDelete" /> |
| | | <uni-icons v-if="batchMode" |
| | | type="closeempty" |
| | | size="20" |
| | | color="#333" |
| | | @click="exitBatchMode" /> |
| | | </view> |
| | | </template> |
| | | </PageHeader> |
| | | <!-- 搜索和筛选区域 --> |
| | | <view class="search-section"> |
| | | <view class="search-bar"> |
| | |
| | | color="#999"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="filter-row"> |
| | | <view class="filter-input"> |
| | | <up-input v-model="typeName" |
| | | readonly |
| | | placeholder="请选择知识类型" |
| | | @click="showTypeSheet = true" /> |
| | | </view> |
| | | <view class="filter-reset" |
| | | @click="resetFilters"> |
| | | 重置 |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 拜访记录列表 --> |
| | | <view class="ledger-list" |
| | |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">知识标题:{{ item.title || '-' }}</text> |
| | | </view> |
| | | <view v-if="batchMode" |
| | | class="select-icon" |
| | | @click.stop="toggleSelection(item)"> |
| | | <uni-icons :type="isSelected(item) ? 'checkbox-filled' : 'checkbox'" |
| | | size="20" |
| | | :color="isSelected(item) ? '#667eea' : '#999'" /> |
| | | </view> |
| | | </view> |
| | | <up-divider></up-divider> |
| | |
| | | </view> |
| | | </view> |
| | | <!-- 按钮区域 --> |
| | | <view class="action-buttons"> |
| | | <view class="action-buttons" |
| | | v-if="!batchMode"> |
| | | <u-button type="info" |
| | | size="small" |
| | | class="action-btn" |
| | | plain |
| | | @click="viewDetail(item, 3)"> |
| | | @click.stop="viewDetail(item, 3)"> |
| | | 查看详情 |
| | | </u-button> |
| | | <u-button type="primary" |
| | | size="small" |
| | | class="action-btn" |
| | | @click="viewDetail(item, 2)"> |
| | | @click.stop="viewDetail(item, 2)"> |
| | | 编辑 |
| | | </u-button> |
| | | <u-button type="error" |
| | | size="small" |
| | | class="action-btn" |
| | | plain |
| | | @click="confirmDelete(item)"> |
| | | @click.stop="confirmDelete(item)"> |
| | | 删除 |
| | | </u-button> |
| | | </view> |
| | |
| | | size="24" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | |
| | | <up-action-sheet :show="showTypeSheet" |
| | | :actions="typeOptions" |
| | | title="请选择知识类型" |
| | | @select="onTypeSelect" |
| | | @close="showTypeSheet = false" /> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | delKnowledgeBase, |
| | | } from "@/api/managementMeetings/knowledgeBase"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import config from "@/config"; |
| | | import { getToken } from "@/utils/auth"; |
| | | |
| | | defineOptions({ name: "knowledge-base-index" }); |
| | | |
| | |
| | | |
| | | // 搜索关键词 |
| | | const name = ref(""); |
| | | const type = ref(""); |
| | | const showTypeSheet = ref(false); |
| | | |
| | | // 拜访记录数据 |
| | | const visitList = ref([]); |
| | | const batchMode = ref(false); |
| | | const selectedIds = ref([]); |
| | | |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | |
| | | }; |
| | | |
| | | const { knowledge_type } = useDict("knowledge_type"); |
| | | const typeOptions = computed(() => { |
| | | const opts = (knowledge_type?.value || []).map(item => ({ |
| | | name: item.label, |
| | | value: item.value |
| | | })); |
| | | return [{ name: "全部", value: "" }, ...opts]; |
| | | }); |
| | | |
| | | const typeName = computed(() => { |
| | | const item = typeOptions.value.find(i => String(i.value) === String(type.value)); |
| | | return item ? item.name : (type.value || ""); |
| | | }); |
| | | |
| | | const onTypeSelect = (action) => { |
| | | type.value = action.value; |
| | | showTypeSheet.value = false; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | name.value = ""; |
| | | type.value = ""; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // 格式化回款方式 |
| | | const formatReceiptType = params => { |
| | |
| | | current: -1, |
| | | size: -1, |
| | | title: name.value, |
| | | type: type.value || undefined, |
| | | }; |
| | | listKnowledgeBase(params) |
| | | .then(res => { |
| | |
| | | }); |
| | | }; |
| | | |
| | | const enterBatchMode = () => { |
| | | batchMode.value = true; |
| | | selectedIds.value = []; |
| | | }; |
| | | |
| | | const exitBatchMode = () => { |
| | | batchMode.value = false; |
| | | selectedIds.value = []; |
| | | }; |
| | | |
| | | const toggleSelection = item => { |
| | | const id = item?.id; |
| | | if (!id) return; |
| | | const idx = selectedIds.value.findIndex(v => String(v) === String(id)); |
| | | if (idx > -1) { |
| | | selectedIds.value.splice(idx, 1); |
| | | } else { |
| | | selectedIds.value.push(id); |
| | | } |
| | | }; |
| | | |
| | | const isSelected = item => { |
| | | const id = item?.id; |
| | | if (!id) return false; |
| | | return selectedIds.value.some(v => String(v) === String(id)); |
| | | }; |
| | | |
| | | const handleBatchDelete = () => { |
| | | if (selectedIds.value.length === 0) { |
| | | showToast("请选择要删除的知识"); |
| | | return; |
| | | } |
| | | uni.showModal({ |
| | | title: "删除确认", |
| | | content: `确定要删除选中的 ${selectedIds.value.length} 条知识吗?`, |
| | | success: res => { |
| | | if (res.confirm) { |
| | | deleteKnowledge(selectedIds.value); |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // 删除确认 |
| | | const confirmDelete = item => { |
| | | uni.showModal({ |
| | |
| | | // 执行删除 |
| | | const deleteKnowledge = id => { |
| | | showLoadingToast("删除中..."); |
| | | delKnowledgeBase([id]) |
| | | const ids = Array.isArray(id) ? id : [id]; |
| | | delKnowledgeBase(ids) |
| | | .then(res => { |
| | | closeToast(); |
| | | if (res.code === 200) { |
| | | showToast("删除成功"); |
| | | if (batchMode.value) { |
| | | exitBatchMode(); |
| | | } |
| | | getList(); // 重新获取列表 |
| | | } else { |
| | | showToast("删除失败"); |
| | |
| | | closeToast(); |
| | | showToast("删除失败"); |
| | | }); |
| | | }; |
| | | |
| | | const handleExport = async () => { |
| | | try { |
| | | uni.showLoading({ title: "导出中...", mask: true }); |
| | | const params = { |
| | | title: name.value || undefined, |
| | | type: type.value || undefined, |
| | | }; |
| | | const query = Object.entries(params) |
| | | .filter(([, v]) => v !== undefined && v !== null && v !== "") |
| | | .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`) |
| | | .join("&"); |
| | | const url = config.baseUrl + "/knowledgeBase/export" + (query ? `?${query}` : ""); |
| | | const res = await uni.downloadFile({ |
| | | url, |
| | | header: { |
| | | Authorization: "Bearer " + getToken(), |
| | | }, |
| | | }); |
| | | uni.hideLoading(); |
| | | |
| | | if (res.statusCode !== 200) { |
| | | showToast("导出失败"); |
| | | return; |
| | | } |
| | | uni.openDocument({ |
| | | filePath: res.tempFilePath, |
| | | showMenu: true, |
| | | fail: () => showToast("打开文件失败"), |
| | | }); |
| | | } catch (e) { |
| | | uni.hideLoading(); |
| | | showToast("导出失败"); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | |
| | | z-index: 100; |
| | | } |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | padding-right: 8px; |
| | | } |
| | | |
| | | .filter-row { |
| | | margin-top: 10px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .filter-input { |
| | | flex: 1; |
| | | } |
| | | |
| | | .filter-reset { |
| | | padding: 6px 12px; |
| | | background: #f5f7fa; |
| | | border-radius: 6px; |
| | | color: #666; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .select-icon { |
| | | padding-left: 10px; |
| | | } |
| | | |
| | | .action-buttons { |
| | | display: flex; |
| | | justify-content: flex-end; |