| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="client-visit-detail"> |
| | | <PageHeader :title="detailType === 1 ? 'æ°å¢ç¥è¯' : (detailType === 2 ? 'ç¼è¾ç¥è¯' : 'ç¥è¯è¯¦æ
')" |
| | | @back="goBack" /> |
| | | <view class="form-container"> |
| | | <u-form ref="formRef" |
| | | :model="form" |
| | | label-width="90"> |
| | | <u-cell-group title="åºæ¬ä¿¡æ¯"> |
| | | <u-form-item label="ç¥è¯æ é¢" |
| | | prop="title" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="form.title" |
| | | :readonly="readonly" |
| | | placeholder="请è¾å
¥ç¥è¯æ é¢" /> |
| | | </u-form-item> |
| | | <u-form-item label="ç¥è¯ç±»å" |
| | | prop="type" |
| | | required |
| | | border-bottom> |
| | | <u-input v-model="typeName" |
| | | readonly |
| | | placeholder="è¯·éæ©ç¥è¯ç±»å" |
| | | @click="!readonly && (showTypeSheet = true)" /> |
| | | <template v-if="!readonly" |
| | | #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showTypeSheet = true"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="éç¨åºæ¯" |
| | | prop="scenario" |
| | | border-bottom> |
| | | <u-input v-model="form.scenario" |
| | | :readonly="readonly" |
| | | placeholder="请è¾å
¥éç¨åºæ¯" /> |
| | | </u-form-item> |
| | | <u-form-item label="è§£å³æç" |
| | | prop="efficiency" |
| | | border-bottom> |
| | | <u-input v-model="efficiencyName" |
| | | readonly |
| | | placeholder="è¯·éæ©è§£å³æç" |
| | | @click="!readonly && (showEfficiencySheet = true)" /> |
| | | <template v-if="!readonly" |
| | | #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showEfficiencySheet = true"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | </u-cell-group> |
| | | |
| | | <u-cell-group title="详ç»å
容"> |
| | | <u-form-item label="é®é¢æè¿°" |
| | | required |
| | | prop="problem" |
| | | border-bottom> |
| | | <u-textarea v-model="form.problem" |
| | | rows="4" |
| | | :disabled="readonly" |
| | | placeholder="请è¾å
¥é®é¢æè¿°" /> |
| | | </u-form-item> |
| | | <u-form-item label="è§£å³æ¹æ¡" |
| | | prop="solution" |
| | | required |
| | | border-bottom> |
| | | <u-textarea v-model="form.solution" |
| | | rows="4" |
| | | :disabled="readonly" |
| | | placeholder="请è¾å
¥è§£å³æ¹æ¡" /> |
| | | </u-form-item> |
| | | <u-form-item label="å
³é®è¦ç¹" |
| | | prop="keyPoints" |
| | | border-bottom> |
| | | <u-textarea v-model="form.keyPoints" |
| | | rows="4" |
| | | :disabled="readonly" |
| | | placeholder="请è¾å
¥å
³é®è¦ç¹ï¼ç¨éå·åé" /> |
| | | </u-form-item> |
| | | </u-cell-group> |
| | | |
| | | <u-cell-group title="å
¶ä»ä¿¡æ¯"> |
| | | <u-form-item label="å建人" |
| | | prop="creator" |
| | | border-bottom> |
| | | <u-input v-model="form.creator" |
| | | readonly |
| | | placeholder="è¯·éæ©å建人" |
| | | @click="!readonly && (showCreatorSheet = true)" /> |
| | | <template v-if="!readonly" |
| | | #right> |
| | | <up-icon name="arrow-right" |
| | | @click="showCreatorSheet = true"></up-icon> |
| | | </template> |
| | | </u-form-item> |
| | | <u-form-item label="ä½¿ç¨æ¬¡æ°" |
| | | prop="usageCount" |
| | | border-bottom> |
| | | <uni-number-box v-model="form.usageCount" |
| | | :min="0" |
| | | :disabled="readonly" /> |
| | | </u-form-item> |
| | | </u-cell-group> |
| | | |
| | | <u-cell-group title="éä»¶ææ"> |
| | | <view class="upload-section"> |
| | | <view class="upload-tip" v-if="!readonly"> |
| | | æ¯æææ¡£ï¼doc, docx, xls, xlsx, pdf, txtï¼åå¾çï¼jpg, jpeg, png, gifï¼æ ¼å¼ |
| | | </view> |
| | | <view class="file-list" v-if="form.commonFileList && form.commonFileList.length > 0"> |
| | | <view v-for="(file, index) in form.commonFileList" :key="index" class="file-item"> |
| | | <up-icon name="file-text" size="20" color="#667eea"></up-icon> |
| | | <text class="file-name" @click="previewFile(file)">{{ file.name || file.fileName }}</text> |
| | | <up-icon v-if="!readonly" name="close-circle-fill" size="18" color="#ff4d4f" @click="handleRemoveFile(index)"></up-icon> |
| | | </view> |
| | | </view> |
| | | <view v-if="!readonly" class="upload-btn-container"> |
| | | <up-upload :fileList="fileList" |
| | | @afterRead="afterRead" |
| | | @delete="deleteFile" |
| | | multiple |
| | | :maxCount="10" |
| | | :previewImage="true"> |
| | | <view class="custom-upload-btn"> |
| | | <up-icon name="plus" size="24" color="#999"></up-icon> |
| | | <text>æ·»å éä»¶</text> |
| | | </view> |
| | | </up-upload> |
| | | </view> |
| | | </view> |
| | | </u-cell-group> |
| | | </u-form> |
| | | |
| | | <!-- æäº¤æé® --> |
| | | <view v-if="!readonly" |
| | | class="footer-btns"> |
| | | <u-button class="cancel-btn" |
| | | @click="goBack">åæ¶</u-button> |
| | | <u-button class="sign-btn" |
| | | type="primary" |
| | | @click="handleSubmit" |
| | | :loading="loading">ä¿å</u-button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- éæ©å¨ --> |
| | | <up-action-sheet :show="showTypeSheet" |
| | | :actions="typeOptions" |
| | | title="è¯·éæ©ç¥è¯ç±»å" |
| | | @select="onTypeSelect" |
| | | @close="showTypeSheet = false" /> |
| | | <up-action-sheet :show="showEfficiencySheet" |
| | | :actions="efficiencyOptions" |
| | | title="è¯·éæ©è§£å³æç" |
| | | @select="onEfficiencySelect" |
| | | @close="showEfficiencySheet = false" /> |
| | | <up-action-sheet :show="showCreatorSheet" |
| | | :actions="creatorOptions" |
| | | title="è¯·éæ©å建人" |
| | | @select="onCreatorSelect" |
| | | @close="showCreatorSheet = false" /> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, computed } from "vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { useDict } from "@/utils/dict"; |
| | | import { |
| | | addKnowledgeBase, |
| | | updateKnowledgeBase, |
| | | } from "@/api/managementMeetings/knowledgeBase"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user"; |
| | | import upload from "@/utils/upload"; |
| | | |
| | | defineOptions({ name: "knowledge-base-detail" }); |
| | | |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | // 页é¢ç¶æ |
| | | const detailType = ref(1); // 1-æ°å¢, 2-ç¼è¾, 3-详æ
|
| | | const knowledgeId = ref(""); |
| | | const readonly = ref(false); |
| | | const loading = ref(false); |
| | | const formRef = ref(null); |
| | | |
| | | // è¡¨åæ°æ® |
| | | const form = ref({ |
| | | title: "", |
| | | type: "", |
| | | scenario: "", |
| | | efficiency: "", |
| | | problem: "", |
| | | solution: "", |
| | | keyPoints: "", |
| | | creator: userStore.nickName || "", |
| | | usageCount: 0, |
| | | tempFileIds: [], |
| | | commonFileList: [] |
| | | }); |
| | | |
| | | // éæ©å¨ç¶æ |
| | | const showTypeSheet = ref(false); |
| | | const showEfficiencySheet = ref(false); |
| | | const showCreatorSheet = ref(false); |
| | | |
| | | // æ°æ®åå
¸ |
| | | const { knowledge_type } = useDict("knowledge_type"); |
| | | const typeOptions = computed(() => { |
| | | return (knowledge_type?.value || []).map(item => ({ |
| | | name: item.label, |
| | | value: item.value |
| | | })); |
| | | }); |
| | | |
| | | const typeName = computed(() => { |
| | | const item = typeOptions.value.find(i => String(i.value) === String(form.value.type)); |
| | | return item ? item.name : (form.value.type || ""); |
| | | }); |
| | | |
| | | const efficiencyOptions = [ |
| | | { name: "æ¾èæå", value: "high" }, |
| | | { name: "ä¸è¬æå", value: "medium" }, |
| | | { name: "轻微æå", value: "low" } |
| | | ]; |
| | | |
| | | const efficiencyName = computed(() => { |
| | | const item = efficiencyOptions.find(i => i.value === form.value.efficiency); |
| | | return item ? item.name : (form.value.efficiency || ""); |
| | | }); |
| | | |
| | | const creatorOptions = ref([]); |
| | | |
| | | // æä»¶ä¸ä¼ ç¸å
³ |
| | | const fileList = ref([]); |
| | | |
| | | // è¿åä¸ä¸é¡µ |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // éæ©å¤ç |
| | | const onTypeSelect = (action) => { |
| | | form.value.type = action.value; |
| | | showTypeSheet.value = false; |
| | | }; |
| | | |
| | | const onEfficiencySelect = (action) => { |
| | | form.value.efficiency = action.value; |
| | | showEfficiencySheet.value = false; |
| | | }; |
| | | |
| | | const onCreatorSelect = (action) => { |
| | | form.value.creator = action.name; |
| | | showCreatorSheet.value = false; |
| | | }; |
| | | |
| | | // è·åå建人å表 |
| | | const getCreatorOptions = async () => { |
| | | try { |
| | | const res = await userListNoPageByTenantId(); |
| | | if (res.code === 200) { |
| | | creatorOptions.value = (res.data || []).map(item => ({ |
| | | name: item.nickName, |
| | | value: item.userId |
| | | })); |
| | | } |
| | | } catch (e) { |
| | | console.error("è·åå建人å表失败:", e); |
| | | } |
| | | }; |
| | | |
| | | // æä»¶å¤ç |
| | | const afterRead = async (event) => { |
| | | const { file } = event; |
| | | const lists = [].concat(file); |
| | | |
| | | for (let i = 0; i < lists.length; i++) { |
| | | const item = lists[i]; |
| | | try { |
| | | uni.showLoading({ title: 'ä¸ä¼ ä¸...' }); |
| | | const res = await upload({ |
| | | url: '/file/upload', |
| | | filePath: item.url, |
| | | name: 'file' |
| | | }); |
| | | uni.hideLoading(); |
| | | |
| | | if (res.code === 200) { |
| | | if (!form.value.tempFileIds) form.value.tempFileIds = []; |
| | | form.value.tempFileIds.push(res.data.tempId); |
| | | |
| | | if (!form.value.commonFileList) form.value.commonFileList = []; |
| | | form.value.commonFileList.push({ |
| | | id: res.data.tempId, |
| | | name: item.name || 'æªå½åæä»¶', |
| | | url: res.data.url |
| | | }); |
| | | |
| | | showToast('ä¸ä¼ æå'); |
| | | } else { |
| | | showToast(res.msg || 'ä¸ä¼ 失败'); |
| | | } |
| | | } catch (e) { |
| | | uni.hideLoading(); |
| | | console.error('ä¸ä¼ 失败:', e); |
| | | showToast('ä¸ä¼ 失败'); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const deleteFile = (event) => { |
| | | const { index } = event; |
| | | form.value.commonFileList.splice(index, 1); |
| | | if (form.value.tempFileIds) { |
| | | form.value.tempFileIds.splice(index, 1); |
| | | } |
| | | }; |
| | | |
| | | const handleRemoveFile = (index) => { |
| | | form.value.commonFileList.splice(index, 1); |
| | | if (form.value.tempFileIds) { |
| | | form.value.tempFileIds.splice(index, 1); |
| | | } |
| | | }; |
| | | |
| | | const previewFile = (file) => { |
| | | if (file.url) { |
| | | // 妿æ¯å¾çï¼é¢è§å¾ç |
| | | const isImage = /\.(jpg|jpeg|png|gif)$/i.test(file.name || file.fileName || file.url); |
| | | if (isImage) { |
| | | uni.previewImage({ |
| | | urls: [file.url] |
| | | }); |
| | | } else { |
| | | // å
¶ä»æä»¶å°è¯æå¼ |
| | | uni.downloadFile({ |
| | | url: file.url, |
| | | success: (res) => { |
| | | if (res.statusCode === 200) { |
| | | uni.openDocument({ |
| | | filePath: res.tempFilePath, |
| | | success: () => console.log('æå¼ææ¡£æå') |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const handleSubmit = async () => { |
| | | if (!form.value.title) return showToast("请è¾å
¥ç¥è¯æ é¢"); |
| | | if (!form.value.type) return showToast("è¯·éæ©ç¥è¯ç±»å"); |
| | | if (!form.value.problem) return showToast("请è¾å
¥é®é¢æè¿°"); |
| | | if (!form.value.solution) return showToast("请è¾å
¥è§£å³æ¹æ¡"); |
| | | |
| | | try { |
| | | loading.value = true; |
| | | const apiCall = detailType.value === 1 ? addKnowledgeBase : updateKnowledgeBase; |
| | | const res = await apiCall(form.value); |
| | | loading.value = false; |
| | | |
| | | if (res.code === 200) { |
| | | showToast("ä¿åæå"); |
| | | setTimeout(() => goBack(), 500); |
| | | } else { |
| | | showToast(res.msg || "ä¿å失败"); |
| | | } |
| | | } catch (e) { |
| | | loading.value = false; |
| | | console.error("ä¿å失败:", e); |
| | | showToast("ç³»ç»å¼å¸¸ï¼ä¿å失败"); |
| | | } |
| | | }; |
| | | |
| | | onLoad(options => { |
| | | detailType.value = Number(options.detailType || 1); |
| | | knowledgeId.value = options.id || ""; |
| | | readonly.value = detailType.value === 3; |
| | | |
| | | if (detailType.value !== 1) { |
| | | try { |
| | | const cached = uni.getStorageSync("knowledgeBase"); |
| | | if (cached) { |
| | | // ç¡®ä¿æ°æ®æ¯å¯¹è±¡ç±»å |
| | | const data = typeof cached === 'string' ? JSON.parse(cached) : cached; |
| | | |
| | | // å¦æä¼ å
¥äº idï¼åæ ¡éª id æ¯å¦ä¸è´ |
| | | if (!knowledgeId.value || String(data.id) === String(knowledgeId.value)) { |
| | | form.value = JSON.parse(JSON.stringify(data)); |
| | | // å
¼å®¹å¤çæä»¶å表 |
| | | if (form.value.commonFileList) { |
| | | form.value.tempFileIds = form.value.commonFileList.map(f => f.id || f.tempId); |
| | | } |
| | | } |
| | | } |
| | | } catch (e) { |
| | | console.error("è§£æç¼åæ°æ®å¤±è´¥:", e); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | getCreatorOptions(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | @import "@/static/scss/form-common.scss"; |
| | | |
| | | .client-visit-detail { |
| | | min-height: 100vh; |
| | | background: #f8f9fa; |
| | | padding-bottom: 80px; |
| | | } |
| | | |
| | | .form-container { |
| | | padding: 10px; |
| | | background: #fff; |
| | | } |
| | | |
| | | .upload-section { |
| | | padding: 15px 0; |
| | | |
| | | .section-title { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: bold; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .upload-tip { |
| | | font-size: 12px; |
| | | color: #999; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .file-list { |
| | | margin-bottom: 15px; |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | align-items: center; |
| | | background: #f5f7fa; |
| | | padding: 8px 12px; |
| | | border-radius: 4px; |
| | | margin-bottom: 8px; |
| | | |
| | | .file-name { |
| | | flex: 1; |
| | | margin: 0 10px; |
| | | font-size: 14px; |
| | | color: #667eea; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .custom-upload-btn { |
| | | width: 80px; |
| | | height: 80px; |
| | | border: 1px dashed #d9d9d9; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | background: #fafafa; |
| | | |
| | | text { |
| | | font-size: 12px; |
| | | color: #999; |
| | | margin-top: 5px; |
| | | } |
| | | } |
| | | |
| | | .footer-btns { |
| | | position: fixed; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: #fff; |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 15px 0; |
| | | box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05); |
| | | z-index: 100; |
| | | |
| | | .u-button { |
| | | width: 45%; |
| | | border-radius: 25px; |
| | | } |
| | | |
| | | .sign-btn { |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | border: none; |
| | | } |
| | | } |
| | | </style> |