diff --git a/src/api/user/index.ts b/src/api/user/index.ts index aa43a40..8ad5154 100644 --- a/src/api/user/index.ts +++ b/src/api/user/index.ts @@ -135,3 +135,14 @@ export function deleteBatchUserById(data: API.DeleteBatchUserParams) { data, }); } + +/** + * @description 重置密码 + */ +export function resetUserPassword(params: { id: string }) { + return request({ + url: `user/reset`, + method: 'post', + data: params, + }); +} diff --git a/src/assets/icons/resetPwd.svg b/src/assets/icons/resetPwd.svg new file mode 100644 index 0000000..da3ca67 --- /dev/null +++ b/src/assets/icons/resetPwd.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/utils/issueCache.ts b/src/utils/issueCache.ts index 4697f0d..80a709d 100644 --- a/src/utils/issueCache.ts +++ b/src/utils/issueCache.ts @@ -27,7 +27,7 @@ export const saveIssueFormCache = (formData: Partial) => { 'customer', 'productId', 'versionId', - 'appVersion', + // 'appVersion', 'agent' ] as const; diff --git a/src/views/client/issue/index.vue b/src/views/client/issue/index.vue index 18f36cf..cdd094f 100644 --- a/src/views/client/issue/index.vue +++ b/src/views/client/issue/index.vue @@ -225,13 +225,13 @@ handleAdd(); }, }, - // { - // title: '编辑工单', - // icon: EditOutlined, - // onClick: () => { - // handleEdit(curRow.value); - // }, - // }, + { + title: '编辑工单', + icon: EditOutlined, + onClick: () => { + handleEdit(curRow.value); + }, + }, // { // title: '删除工单', // icon: DeleteOutlined, @@ -357,7 +357,7 @@ cachedData.customer = res.customer; cachedData.productId = res.productId; cachedData.versionId = res.versionId; - cachedData.appVersion = res.appVersion; + // cachedData.appVersion = res.appVersion; cachedData.agent = res.agent; } diff --git a/src/views/question/knowledge/detail.vue b/src/views/question/knowledge/detail.vue index 992ff9a..74d4459 100644 --- a/src/views/question/knowledge/detail.vue +++ b/src/views/question/knowledge/detail.vue @@ -9,26 +9,76 @@ 修改时间: --> @@ -38,10 +88,11 @@ import { useForm } from '@/components/core/schema-form'; import { getEditFormSchema } from './formSchemas'; import { useRoute } from 'vue-router'; - import { onMounted, ref } from 'vue'; - import { findOneById } from '@/api/knowledgeBase'; + import { onMounted, ref, computed, watch } from 'vue'; + import { findOneById, fetchKnowledgeBasePageList } from '@/api/knowledgeBase'; import { SvgIcon } from '@/components/basic/svg-icon'; import { stateTypeList } from './data'; + import { LeftOutlined, RightOutlined, SearchOutlined } from '@ant-design/icons-vue'; const route = useRoute(); const editorOptions = ref({ theme: 'snow', @@ -66,7 +117,47 @@ const current = ref(0); - const { id } = route.query; + // 当前 id(用于 watch) + const currentId = ref(route.query.id as string | undefined); + + // 上一页和下一页的 id + const prevId = ref(null); + const nextId = ref(null); + + // 查询参数(排除id)- 直接从 route.query 获取,确保响应式更新 + const queryParams = computed(() => { + const params: Record = {}; + Object.keys(route.query).forEach((key) => { + // 排除 id 参数 + if ( + key !== 'id' && + route.query[key] !== undefined && + route.query[key] !== null && + route.query[key] !== '' + ) { + params[key] = String(route.query[key]); + } + }); + return params; + }); + + // 是否有查询参数 + const hasQueryParams = computed(() => { + return Object.keys(queryParams.value).length > 0; + }); + + // 格式化日期范围显示 + const formatDateRange = (dateStr: string) => { + if (!dateStr) return ''; + // 如果是逗号分隔的日期范围 + if (dateStr.includes(',')) { + const dates = dateStr.split(','); + if (dates.length === 2) { + return `${dates[0]} 至 ${dates[1]}`; + } + } + return dateStr; + }; const [SchemaForm, formRef] = useForm({ labelWidth: 150, @@ -81,35 +172,232 @@ disabled: true, }); - onMounted(async () => { - loading.value = true; - const res = await findOneById({ id: id as string }); - loading.value = false; - - if (res?.files && Array.isArray(res.files) && res.files.length) { - res.files = res.files.map((e) => { - return { - name: e.originalFilename, - url: e.url, - id: e.id, - }; + // 获取列表数据,找到当前记录的位置 + const loadListData = async () => { + try { + // 构建查询参数 + const searchParams: any = { + current: 1, + size: 1000, // 获取足够多的数据以便查找 + }; + + // 处理查询参数 - 从 route.query 直接获取,确保获取到最新的查询参数 + const currentQuery = route.query; + if (currentQuery.title) { + searchParams.title = String(currentQuery.title); + } + if (currentQuery.description) { + searchParams.description = String(currentQuery.description); + } + if (currentQuery.createTime) { + const dateStr = String(currentQuery.createTime); + if (dateStr.includes(',')) { + const dates = dateStr.split(','); + if (dates.length === 2) { + searchParams.startDate = dates[0]; + searchParams.endDate = dates[1]; + } + } + } + + // 添加其他可能的查询参数 + Object.keys(currentQuery).forEach((key) => { + if (key !== 'id' && key !== 'title' && key !== 'description' && key !== 'createTime') { + const value = currentQuery[key]; + if (value !== undefined && value !== null && value !== '') { + searchParams[key] = String(value); + } + } }); + + console.log('加载列表数据,查询参数:', searchParams); + const res = await fetchKnowledgeBasePageList(searchParams); + const list = res.data?.records || []; + console.log('获取到的列表数据,总数:', list.length); + + // 找到当前记录在列表中的位置 + const currentIdValue = currentId.value || (route.query.id as string); + const currentIndex = list.findIndex((item: any) => item.id == currentIdValue); + console.log('当前记录索引:', currentIndex, '当前ID:', currentIdValue); + + if (currentIndex !== -1) { + // 设置上一页和下一页的 id + prevId.value = currentIndex > 0 ? list[currentIndex - 1].id : null; + nextId.value = currentIndex < list.length - 1 ? list[currentIndex + 1].id : null; + console.log('上一页ID:', prevId.value, '下一页ID:', nextId.value); + } else { + // 如果找不到当前记录,清空上一页和下一页 + prevId.value = null; + nextId.value = null; + console.warn('未找到当前记录在列表中'); + } + } catch (error) { + console.error('获取列表数据失败:', error); + prevId.value = null; + nextId.value = null; } + }; - if (res.historys && Array.isArray(res.historys) && res.historys.length) { - historyList.value = res.historys.map((e) => { - return { - state: e.state, - remark: `${e.createUserName}于${e.createTime} : ${e.remark}`, - title: stateTypeList.find((item) => item.value === e.state)?.label, - }; - }); + // 加载详情数据 + const loadDetailData = async (targetId: string) => { + if (!targetId) { + return; } - current.value = res?.state; + currentId.value = targetId; + loading.value = true; + + try { + const res = await findOneById({ id: targetId }); + + if (res?.files && Array.isArray(res.files) && res.files.length) { + res.files = res.files.map((e) => { + return { + name: e.originalFilename, + url: e.url, + id: e.id, + }; + }); + } + + if (res.historys && Array.isArray(res.historys) && res.historys.length) { + historyList.value = res.historys.map((e) => { + return { + state: e.state, + remark: `${e.createUserName}于${e.createTime} : ${e.remark}`, + title: stateTypeList.find((item) => item.value === e.state)?.label, + }; + }); + } else { + historyList.value = []; + } + + current.value = res?.state; - formRef?.setFieldsValue(res); - formRef?.clearValidate(); + formRef?.setFieldsValue(res); + formRef?.clearValidate(); + + // 重新加载列表数据以更新上一页和下一页 + await loadListData(); + } catch (error) { + console.error('加载详情数据失败:', error); + } finally { + loading.value = false; + } + }; + + // 跳转到上一页 + const handlePrev = () => { + if (prevId.value) { + loadDetailData(prevId.value); + } + }; + + // 跳转到下一页 + const handleNext = () => { + if (nextId.value) { + loadDetailData(nextId.value); + } + }; + + onMounted(async () => { + const currentIdValue = route.query.id as string; + if (!currentIdValue) { + console.warn('未找到 id 参数'); + return; + } + await loadDetailData(currentIdValue); }); + + // 监听路由变化,重新加载数据(用于从外部路由跳转过来的情况) + watch( + () => route.query.id, + async (newId) => { + if (newId && newId !== currentId.value) { + await loadDetailData(newId as string); + } + }, + ); - + diff --git a/src/views/question/knowledge/index.vue b/src/views/question/knowledge/index.vue index e94ac8a..903681c 100644 --- a/src/views/question/knowledge/index.vue +++ b/src/views/question/knowledge/index.vue @@ -351,11 +351,37 @@ }; const handleView = (record: TableListItem) => { + // 获取查询表单的参数 + const queryFormRef = dynamicTableInstance?.getQueryFormRef(); + const queryParams: Record = { + id: record.id, + }; + + if (queryFormRef) { + const formValues = queryFormRef.getFieldsValue(); + // 过滤空值,只传递有值的查询参数 + Object.keys(formValues).forEach((key) => { + const value = formValues[key]; + // 过滤掉空字符串、null、undefined和空数组 + if ( + value !== undefined && + value !== null && + value !== '' && + !(Array.isArray(value) && value.length === 0) + ) { + // 如果是数组,转换为字符串(用于日期范围等) + if (Array.isArray(value)) { + queryParams[key] = value.join(','); + } else { + queryParams[key] = value; + } + } + }); + } + router.push({ path: '/question/knowledgeDetail', - query: { - id: record.id, - }, + query: queryParams, }); }; diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue index 334048c..8960702 100644 --- a/src/views/system/user/index.vue +++ b/src/views/system/user/index.vue @@ -54,6 +54,7 @@ deleteUserById, deleteBatchUserById, updateAuditState, + resetUserPassword, } from '@/api/user'; import { computed, reactive, ref, toRaw } from 'vue'; import { Modal, message, Alert } from 'ant-design-vue'; @@ -99,6 +100,12 @@ vShow: record.auditState === 0, onClick: () => handleAudit(record), }, + { + icon: 'resetPwd', + color: '#f59e0b', + label: '重置密码', + onClick: () => handleResetPassword(record), + }, { icon: 'delete', color: '#ec6f6f', @@ -152,6 +159,50 @@ dynamicTableInstance?.reload(); }; + const handleResetPassword = async (record: TableListItem) => { + const { id, username } = record; + if (!id) return; + + Modal.confirm({ + title: '重置密码', + content: `确定要重置用户 "${username}" 的密码吗?`, + icon: , + centered: true, + onOk: async () => { + try { + const res = await resetUserPassword({ id }); + const newPassword = res; + + // 复制密码到剪切板 + try { + await navigator.clipboard.writeText(newPassword); + Modal.success({ + title: '重置密码成功', + content: `新密码为:${newPassword}(已复制到剪切板)`, + centered: true, + }); + } catch (clipboardError) { + // 如果剪切板API失败,使用传统方法 + const textArea = document.createElement('textarea'); + textArea.value = newPassword; + document.body.appendChild(textArea); + textArea.select(); + document.execCommand('copy'); + document.body.removeChild(textArea); + + Modal.success({ + title: '重置密码成功', + content: `新密码为:${newPassword}(已复制到剪切板)`, + centered: true, + }); + } + } catch (error) { + message.error('重置密码失败'); + } + }, + }); + }; + const handleEdit = (record: TableListItem) => { openUserModal(record); }; diff --git a/vite.config.ts b/vite.config.ts index ad82baf..80883d9 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -126,7 +126,7 @@ export default defineConfig({ // }, '/server': { target: 'http://43.137.2.78:8085', - // target: 'http://192.168.2.64:8089', + // target: 'http://192.168.2.22:8089', changeOrigin: true, rewrite: (path) => path.replace(/^\/server/, '/server'), },