新增“知识科普”栏目功能

master
chris 5 days ago
parent d5a2dda1b5
commit b186df32f5

@ -20,7 +20,7 @@
"@element-plus/icons-vue": "2.3.1",
"@kjgl77/datav-vue3": "^1.7.4",
"@pureadmin/utils": "^2.5.0",
"@vueup/vue-quill": "1.2.0",
"@vueup/vue-quill": "^1.2.0",
"@vueuse/core": "10.11.0",
"axios": "0.28.1",
"echarts": "5.5.1",
@ -50,5 +50,8 @@
"vite": "5.3.2",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1"
},
"overrides": {
"quill": "2.0.2"
}
}

@ -0,0 +1,60 @@
/*
* @Author: chris
* @Date: 2025-09-05 11:34:53
* @LastEditors: chris
* @LastEditTime: 2025-12-03 15:18:14
*/
import request from "@/utils/request";
// 查询科普知识列表
export function listKnowledge(query) {
return request({
url: "/business/article/list",
method: "get",
params: query,
});
}
// 查询科普知识详细
export function getKnowledge(id) {
return request({
url: "/business/article/" + id,
method: "get",
});
}
// 新增科普知识
export function addKnowledge(data) {
return request({
url: "/business/article",
method: "post",
data,
});
}
// 修改科普知识
export function updateKnowledge(data) {
return request({
url: "/business/article",
method: "put",
data,
});
}
// 删除科普知识
export function delKnowledge(id) {
return request({
url: "/business/article/" + id,
method: "delete",
});
}
// 导出科普知识数据
export function exportKnowledge(query) {
return request({
url: "/business/article/export",
method: "post",
params: query,
responseType: "blob",
});
}

Binary file not shown.

@ -0,0 +1,85 @@
<!--
* @Author: chris
* @Date: 2025-08-08 16:17:48
* @LastEditors: chris
* @LastEditTime: 2025-12-03 14:51:46
-->
<template>
<common-action-toolbar
:buttons="buttons"
:selectedRows="props.selectedRows"
:columns="props.columns"
v-model:showSearch="showSearch"
@buttonClick="handleButtonClick"
@queryTable="handleQuery"
></common-action-toolbar>
</template>
<script setup>
import CommonActionToolbar from '@/components/CommonActionToolbar'
const props = defineProps({
selectedRows: { type: Array, required: true },
columns: { type: Array, required: true },
showSearch: { type: Boolean, required: true }
})
const emits = defineEmits(['add', 'update', 'delete', 'export', 'query', 'update:showSearch'])
const showSearch = computed({
get: () => props.showSearch,
set: (value) => emits('update:showSearch', value)
})
//
const buttons = reactive([
{
key: 'add',
text: '新增',
type: 'primary',
plain: true,
icon: 'Plus',
action: 'add'
},
{
key: 'update',
text: '修改',
type: 'success',
plain: true,
icon: 'Edit',
action: 'update',
disabled: computed(() => props.selectedRows.length !== 1)
},
{
key: 'delete',
text: '删除',
type: 'danger',
plain: true,
icon: 'Delete',
action: 'delete',
disabled: computed(() => props.selectedRows.length === 0)
}
])
//
function handleButtonClick(action) {
switch (action) {
case 'add':
emits('add')
break
case 'update':
emits('update')
break
case 'delete':
emits('delete')
break
case 'export':
emits('export')
break
}
}
//
function handleQuery() {
emits('query')
}
</script>

@ -0,0 +1,125 @@
<template>
<el-dialog :title="title" v-model="dialogOpen" width="50%" append-to-body>
<el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
<el-row :gutter="10">
<el-col :span="24">
<el-form-item label="标题" prop="title">
<el-input v-model="form.title" placeholder="请输入标题" maxlength="100" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择类型" clearable style="width: 200px">
<el-option v-for="dict in types" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="内容" prop="content">
<Editor v-model="form.content" :height="500" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { addKnowledge, updateKnowledge } from '@/api/knowledge'
import Editor from '@/components/Editor'
import { defaultForm } from '../config'
defineExpose({
setForm,
resetForm,
})
const props = defineProps({
open: { type: Boolean, required: true },
title: { type: String, required: true },
types: { type: Array, required: true },
})
const form = ref({ ...defaultForm })
const emits = defineEmits(['update:open', 'success'])
// open
const dialogOpen = ref(props.open)
//
const rules = {
title: [{ required: true, message: '请填写标题', trigger: 'blur' }],
type: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
content: [{ required: true, message: '请填写内容', trigger: 'blur' }],
}
//
const formRef = ref(null)
const { proxy } = getCurrentInstance()
// props.open
watch(
() => props.open,
(newValue) => {
dialogOpen.value = newValue
}
)
//
watch(
dialogOpen,
(newValue) => {
if (newValue !== props.open) {
emits('update:open', newValue)
}
}
)
// emits
//
async function submitForm() {
try {
await formRef.value.validate()
if (form.value.id) {
await updateKnowledge(form.value)
proxy.$modal.msgSuccess('修改成功')
} else {
await addKnowledge(form.value)
proxy.$modal.msgSuccess('新增成功')
}
dialogOpen.value = false
resetForm()
emits('success')
} catch (error) {
//
if (error !== 'cancel') {
proxy.$modal.msgError('操作失败: ' + (error.message || '未知错误'))
}
}
}
//
function cancel() {
resetForm()
dialogOpen.value = false
}
function setForm(data) {
form.value = { ...data }
}
function resetForm() {
form.value = { ...defaultForm }
formRef.value?.resetFields()
}
</script>

@ -0,0 +1,44 @@
<!--
* @Author: chris
* @Date: 2025-08-08 16:17:37
* @LastEditors: chris
* @LastEditTime: 2025-12-03 15:30:34
-->
<template>
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="标题" prop="title">
<el-input v-model="queryParams.title" placeholder="标题" clearable style="width: 240px" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="queryParams.type" placeholder="类型" clearable style="width: 240px">
<el-option v-for="dict in props.types" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"></el-button>
<el-button icon="Refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
</template>
<script setup>
// props
const props = defineProps({
queryParams: { type: Object, required: true },
types: { type: Array, required: true }
})
//
const emits = defineEmits(['query', 'reset'])
//
function handleQuery() {
emits('query')
}
//
function resetQuery() {
emits('reset')
}
</script>

@ -0,0 +1,69 @@
<!--
* @Author: chris
* @Date: 2025-08-08 16:17:54
* @LastEditors: chris
* @LastEditTime: 2025-12-08 16:37:07
-->
<template>
<el-table
v-loading="loading"
:data="list"
@selection-change="handleSelectionChange"
border
highlight-current-row
style="width: 100%"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="标题" align="center" prop="title" v-if="columns[0].visible" :show-overflow-tooltip="true" />
<el-table-column label="类型" align="center" prop="type" v-if="columns[1].visible">
<template #default="scope">
<span>{{ selectDictLabel(props.types, scope.row.type) }}</span>
</template>
</el-table-column>
<el-table-column label="内容" align="center" prop="content" :show-overflow-tooltip="true" v-if="columns[2].visible" />
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[3].visible">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right">
<template #default="scope">
<!-- <el-tooltip content="查看" placement="top">
<el-button link type="primary" icon="Eye" @click="handleView(scope.row)" v-hasPermi="['business:orchard:query']"></el-button>
</el-tooltip> -->
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['business:orchard:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['business:orchard:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</template>
<script setup>
import { parseTime, selectDictLabel } from '@/utils/ruoyi'
import { statusColorMap } from '../config'
const props = defineProps({
list: { type: Array, required: true },
loading: { type: Boolean, required: true },
columns: { type: Array, required: true },
types: { type: Array, required: true }
})
const emits = defineEmits(['selection-change', 'view', 'update', 'delete'])
function handleSelectionChange(selection) {
emits('selection-change', selection)
}
function handleUpdate(row) {
emits('update', row)
}
function handleDelete(row) {
emits('delete', row)
}
</script>

@ -0,0 +1,25 @@
/*
* @Author: chris
* @Date: 2025-09-05 10:12:41
* @LastEditors: chris
* @LastEditTime: 2025-12-08 14:55:43
*/
// 列配置
export const columnsConfig = [
{ key: 0, label: "标题", visible: true },
{ key: 1, label: "类型", visible: true },
{ key: 2, label: "正文", visible: true },
{ key: 3, label: "创建时间", visible: true },
];
// 状态颜色映射
export const statusColorMap = {
0: "success",
1: "danger",
};
export const defaultForm = {
title: "",
type: "", // 1: 种植技术 2:虫害知识 3:病害知识
content: "<p></p>",
};

@ -0,0 +1,221 @@
<template>
<div class="app-container">
<!-- 搜索表单 -->
<search-form
v-show="showSearch"
:query-params="queryParams"
:types="knowledgeTypes"
@query="handleQuery"
@reset="resetQuery"
/>
<!-- 操作按钮 -->
<action-buttons
:selected-rows="selectedRows"
:columns="columns"
v-model:show-search="showSearch"
@add="handleAdd"
@update="handleUpdate"
@delete="handleDelete"
@export="handleExport"
@query="getList"
/>
<!-- 数据表格 -->
<know-table
:list="knowledgeList"
:loading="loading"
:columns="columns"
:types="knowledgeTypes"
@selection-change="handleSelectionChange"
@view="handleView"
@update="handleUpdate"
@delete="handleDelete"
/>
<!-- 分页 -->
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改果园对话框 -->
<form-dialog
ref="formDialogRef"
v-model:open="open"
:types="knowledgeTypes"
:title="title"
@success="getList"
/>
</div>
</template>
<script setup name="Knowledge">
import { listKnowledge, delKnowledge } from '@/api/knowledge'
import { columnsConfig } from './config.js'
import Pagination from '@/components/Pagination'
import SearchForm from './components/SearchForm'
import ActionButtons from './components/ActionButtons'
import knowTable from './components/knowTable'
import FormDialog from './components/FormDialog'
import { useDict } from '@/utils/dict'
//
const { proxy } = getCurrentInstance()
const columns = reactive(columnsConfig)
const formDialogRef = ref(null)
//
const knowledgeList = ref([])
const open = ref(false)
const viewOpen = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const selectedRows = ref([])
const total = ref(0)
const title = ref('')
const dateRange = ref([])
const currentId = ref(null)
//
const { knowledge_type: knowledgeTypes } = useDict('knowledge_type')
//
const data = reactive({
queryParams: {
title: null,
type: null,
pageNum: 1,
pageSize: 10,
}
})
const { queryParams } = toRefs(data)
//
onMounted(() => {
getList()
})
/** 查询果园设备列表 */
async function getList() {
loading.value = true
try {
const res = await listKnowledge(queryParams.value)
knowledgeList.value = res.rows || []
total.value = res.total || 0
} catch (error) {
proxy.$modal.msgError('获取科普知识列表失败: ' + (error.message || '未知错误'))
console.error('Failed to get knowledge list:', error)
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1
getList()
}
/** 重置按钮操作 */
function resetQuery() {
proxy?.$refs.queryRef?.resetFields()
dateRange.value = []
handleQuery()
}
/** 新增按钮操作 */
function handleAdd() {
open.value = true
title.value = '新增科普知识'//
formDialogRef.value.resetForm()
}
/** 修改按钮操作 */
function handleUpdate(row) {
open.value = true
title.value = '修改科普知识的详细信息'
// row
const targetRow = row || (selectedRows.value.length ? selectedRows.value[0] : null)
if (targetRow) {
formDialogRef.value.setForm(targetRow)
}
}
/** 查看按钮操作 */
function handleView(row) {
viewOpen.value = true
currentId.value = row.id
}
/** 删除按钮操作 */
async function handleDelete(row) {
const ids = row ? [row.id] : selectedRows.value.map(item => item.id)
const knowledgeNames = row ? [row.title] : selectedRows.value.map(item => item.title)
try {
await proxy.$confirm(`是否确认删除选中的${knowledgeNames.join(',')}科普知识数据?`)
await delKnowledge(ids)
proxy.$modal.msgSuccess('删除成功')
getList()
selectedRows.value = [] //
} catch (error) {
//
if (error !== 'cancel') {
proxy.$modal.msgError('删除失败: ' + (error.message || '未知错误'))
}
}
}
/** 导出按钮操作 */
async function handleExport() {
const fileName = await proxy.download('/business/device/export', queryParams.value, `虫情设备数据_${new Date().getTime()}.xlsx`)
fileName && proxy.$download.name(fileName)
}
/** 选择框选中数据变化 */
function handleSelectionChange(selection) {
selectedRows.value = selection
}
</script>
<style lang="scss" scoped>
.app-container {
@apply p-5 bg-white min-h-[calc(100vh-120px)] rounded-lg shadow-sm;
}
.mb-3 {
margin-bottom: 12px;
}
//
:deep(.el-table) {
@apply text-sm border border-gray-200 rounded-lg overflow-hidden;
.el-table__header-wrapper {
@apply bg-gray-50;
}
.el-table__body {
@apply bg-white;
}
.el-table__row {
@apply hover:bg-blue-50 transition-colors duration-150;
}
.el-table-column--selection {
@apply w-12;
}
}
//
:deep(.el-dialog) {
@apply rounded-lg overflow-hidden shadow-lg;
}
:deep(.el-dialog__header) {
@apply bg-gray-50 border-b border-gray-200;
}
.dialog-footer {
@apply flex justify-center mt-4 space-x-3;
}
</style>
Loading…
Cancel
Save