增加知识科普tab页,将tab页之外的页面移到page-sub中,移除husky

master
chris 4 weeks ago
parent 37300e3745
commit be218b70f5

@ -159,7 +159,6 @@
"cross-env": "^10.0.0", "cross-env": "^10.0.0",
"eslint": "^9.31.0", "eslint": "^9.31.0",
"eslint-plugin-format": "^1.0.1", "eslint-plugin-format": "^1.0.1",
"husky": "^9.1.7",
"lint-staged": "^15.2.10", "lint-staged": "^15.2.10",
"miniprogram-api-typings": "^4.1.0", "miniprogram-api-typings": "^4.1.0",
"openapi-ts-request": "^1.6.7", "openapi-ts-request": "^1.6.7",

@ -199,9 +199,6 @@ importers:
eslint-plugin-format: eslint-plugin-format:
specifier: ^1.0.1 specifier: ^1.0.1
version: 1.0.1(eslint@9.34.0(jiti@2.6.1)) version: 1.0.1(eslint@9.34.0(jiti@2.6.1))
husky:
specifier: ^9.1.7
version: 9.1.7
lint-staged: lint-staged:
specifier: ^15.2.10 specifier: ^15.2.10
version: 15.5.2 version: 15.5.2
@ -3963,11 +3960,6 @@ packages:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'} engines: {node: '>=16.17.0'}
husky@9.1.7:
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
engines: {node: '>=18'}
hasBin: true
iconv-lite@0.4.24: iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -11311,8 +11303,6 @@ snapshots:
human-signals@5.0.0: {} human-signals@5.0.0: {}
husky@9.1.7: {}
iconv-lite@0.4.24: iconv-lite@0.4.24:
dependencies: dependencies:
safer-buffer: 2.1.2 safer-buffer: 2.1.2

@ -0,0 +1,93 @@
/*
* @Author: chris
* @Date: 2025-11-10 10:21:14
* @LastEditors: chris
* @LastEditTime: 2025-11-17 10:26:02
*/
import { http } from '@/http/http'
export interface IDeviceDataQuery {
deptId: string
page: number
pageSize: number
type: number
deviceId: string
beginTime: string
endTime: string
}
export interface IDeviceDataItem {
id: string
deptId: string
deviceNo: string
model: string
type: number
data: string
createTime: string
}
// 查询设备数据列表
export async function listDeviceData(query: IDeviceDataQuery) {
return await http.get<IDeviceDataItem>('/business/device-data/list', query)
}
// 导出设备数据
export function exportDeviceData(query) {
return http.post('/business/device-data/export', query, { responseType: 'blob' })
}
// 查询设备数据统计分析数据
export function getDeviceDataAnalysis(params) {
return http.get('/business/device-data/analysis', params)
}
// export function listDeviceData(query) {
// return request({
// url: "/business/device-data/list",
// method: "get",
// params: query,
// });
// }
// 查询设备数据详细
// export function getDeviceData(id) {
// return request({
// url: "/business/device-data/" + id,
// method: "get",
// });
// }
// // 查询设备数据统计分析数据
// export function getDeviceDataAnalysis(params) {
// return request({
// url: "/business/device-data/analysis",
// method: "get",
// params,
// });
// }
// // 新增设备数据
// export function addDeviceData(data) {
// return request({
// url: "/business/device-data",
// method: "post",
// data,
// });
// }
// // 修改设备数据
// export function updateDeviceData(data) {
// return request({
// url: "/business/device-data",
// method: "put",
// data,
// });
// }
// // 删除设备数据
// export function delDeviceData(id) {
// return request({
// url: "/business/device-data/" + id,
// method: "delete",
// });
// }

@ -0,0 +1,27 @@
/*
* @Author: chris
* @Date: 2026-01-19 16:49:53
* @LastEditors: chris
* @LastEditTime: 2026-01-19 16:59:22
*/
import { http } from '@/http/http'
export interface IKnowledgeListQuery {
deptId: number
content?: string
pageNum: number
pageSize: number
}
export interface IKnowledge {
id: number
title: string
content: string
createTime: string
updateTime: string
}
// 获取知识列表
export async function getKnowledgeList(query: IKnowledgeListQuery) {
return await http.get<IKnowledge[]>('/business/article/list', query)
}

@ -0,0 +1,30 @@
/*
* @Author: chris
* @Date: 2026-01-19 15:55:45
* @LastEditors: chris
* @LastEditTime: 2026-01-19 16:30:25
*/
import { http } from '@/http/http'
export interface IOrchardInfo {
deptId: number
deptName: string
email: string
leader: string
parentId: number
parentName: string
phone: string
plantingArea: string
plantingNum: string
remark: string
status: string
terrain: string
updateBy: string
updateTime: string
variety: string
}
// 获取果园信息
export async function getOrchardInfo(orchardId: string) {
return await http.get<IOrchardInfo>(`/system/dept/${orchardId}`)
}

@ -0,0 +1,21 @@
import { http } from '@/http/http'
export interface IWarningQuery {
deptId: number
beginTime?: string
endTime?: string
pageNum: number
pageSize: number
}
export interface IWarning {
id: number
deptId: number
notifyContent: string
createTime: string
}
// 获取预警列表
export async function getWarningList(query: IWarningQuery) {
return await http.get<IWarning[]>(`/business/notify-log/list`, query)
}

@ -0,0 +1,17 @@
/*
* @Author: chris
* @Date: 2025-11-10 10:21:14
* @LastEditors: chris
* @LastEditTime: 2026-01-19 15:43:07
*/
import { http } from '@/http/http'
export interface IResetPassword {
userId: string
password: string
}
// 重置用户密码
export async function resetPassword(query: IResetPassword) {
return await http.put<IResetPassword>('/system/user/resetPwd', query)
}

@ -0,0 +1,75 @@
<!--
* @Author: chris
* @Date: 2026-01-06 17:17:48
* @LastEditors: chris
* @LastEditTime: 2026-01-19 15:52:08
-->
<template>
<view class="changePassword-page">
<wd-form ref="form" :model="model">
<wd-cell-group border>
<!-- <wd-input
v-model="form.oldPwd"
label="旧密码"
label-width="100px"
prop="oldPwd"
show-password
clearable
placeholder="请输入旧密码"
:rules="[{ required: true, message: '请填写密码' }]"
/> -->
<wd-input
v-model="form.newPwd"
label="新密码"
label-width="100px"
prop="newPwd"
show-password
clearable
placeholder="请输入新密码"
:rules="[{ required: true, message: '请填写密码' }]"
/>
</wd-cell-group>
<view class="footer">
<wd-button type="primary" size="large" block @click="handleSubmit">
提交
</wd-button>
</view>
</wd-form>
</view>
</template>
<script setup>
import { resetPassword } from '@/api/user'
const form = ref({
// oldPwd: '',
newPwd: '',
})
function handleSubmit() {
// form.value.oldPwd = form.value.oldPwd.trim()
resetPassword({
userId: uni.getStorageSync('userId'),
password: form.value.newPwd,
}).then((res) => {
if (res.code === 200) {
uni.showToast({
title: '密码重置成功',
icon: 'success',
})
uni.navigateBack()
}
form.value.newPwd = form.value.newPwd.trim()
})
}
</script>
<style lang="scss" scoped>
.changePassword-page {
padding: 20rpx;
.footer {
margin-top: 40rpx;
}
}
</style>

@ -0,0 +1,37 @@
<!--
* @Author: chris
* @Date: 2026-01-06 17:43:28
* @LastEditors: chris
* @LastEditTime: 2026-01-19 15:30:16
-->
<script setup>
const content = ref('')
function submitFeedback() {
}
</script>
<template>
<view class="feedback-container">
<wd-form ref="form" :model="model">
<wd-textarea v-model="content" placeholder="请填写反馈内容" />
<view class="footer">
<wd-button type="primary" size="large" block @click="submitFeedback">
提交反馈
</wd-button>
</view>
</wd-form>
</view>
</template>
<style lang="scss" scoped>
.feedback-container {
padding: 20rpx;
background-color: #f5f5f5;
height: 100vh;
}
.footer {
margin-top: 40rpx;
}
</style>

@ -2,7 +2,7 @@
* @Author: chris * @Author: chris
* @Date: 2025-10-21 11:42:40 * @Date: 2025-10-21 11:42:40
* @LastEditors: chris * @LastEditors: chris
* @LastEditTime: 2025-10-22 10:26:02 * @LastEditTime: 2025-11-17 10:17:04
--> -->
<script setup name="line-chart"> <script setup name="line-chart">
import LEchart from '@/uni_modules/lime-echart_1.0.5/components/l-echart/l-echart.vue' import LEchart from '@/uni_modules/lime-echart_1.0.5/components/l-echart/l-echart.vue'
@ -24,6 +24,10 @@ const props = defineProps({
const lineChartRef = ref(null) const lineChartRef = ref(null)
let lineChart = null let lineChart = null
watch(() => props.chartData, () => {
refreshChart()
})
onMounted(async () => { onMounted(async () => {
nextTick(async () => { nextTick(async () => {
await initChart() await initChart()

@ -2,7 +2,7 @@
* @Author: chris * @Author: chris
* @Date: 2025-10-21 11:42:40 * @Date: 2025-10-21 11:42:40
* @LastEditors: chris * @LastEditors: chris
* @LastEditTime: 2025-10-22 10:26:22 * @LastEditTime: 2025-11-17 10:19:34
--> -->
<script setup name="pie-chart"> <script setup name="pie-chart">
import LEchart from '@/uni_modules/lime-echart_1.0.5/components/l-echart/l-echart.vue' import LEchart from '@/uni_modules/lime-echart_1.0.5/components/l-echart/l-echart.vue'
@ -52,9 +52,14 @@ const props = defineProps({
const pieChartRef = ref(null) const pieChartRef = ref(null)
let pieChart = null let pieChart = null
watch(() => props.chartData, () => {
refreshChart()
})
onMounted(async () => { onMounted(async () => {
await initChart() await initChart()
}) })
async function initChart() { async function initChart() {
if (!pieChartRef.value) if (!pieChartRef.value)
return return

@ -2,9 +2,13 @@
* @Author: chris * @Author: chris
* @Date: 2025-10-17 11:52:06 * @Date: 2025-10-17 11:52:06
* @LastEditors: chris * @LastEditors: chris
* @LastEditTime: 2025-10-22 14:16:01 * @LastEditTime: 2025-11-17 10:50:12
--> -->
<script setup> <script setup>
import { useToast } from 'wot-design-uni'
import { getDeviceDataAnalysis } from '@/api/deviceData/index'
const echarts = require('../../uni_modules/lime-echart_1.0.5/static/echarts.min2.js') const echarts = require('../../uni_modules/lime-echart_1.0.5/static/echarts.min2.js')
import LineChart from './components/lineChart.vue' import LineChart from './components/lineChart.vue'
@ -16,9 +20,15 @@ definePage({
}, },
}) })
const toast = useToast()
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const query = uni.createSelectorQuery().in(proxy) const query = uni.createSelectorQuery().in(proxy)
const orchardId = ref(0)
const daysRange = getDateRange(7)
const lineChartData = ref({})
const pieChartData = ref({})
const chartSize = ref({ const chartSize = ref({
lineChart: { lineChart: {
width: 0, width: 0,
@ -33,8 +43,57 @@ const chartSize = ref({
onMounted(async () => { onMounted(async () => {
// await getChartCanvasSize('lineChart') // await getChartCanvasSize('lineChart')
// await getChartCanvasSize('pieChart') // await getChartCanvasSize('pieChart')
const res = await getPestAnalysisData()
const { lineRes, pieRes } = formatData(res.rows)
lineChartData.value = createChartData(lineRes, 'line')
pieChartData.value = createChartData(pieRes, 'pie')
}) })
async function getPestAnalysisData() {
try {
return await getDeviceDataAnalysis({
beginTime: daysRange[0],
endTime: daysRange[1],
type: 1,
deptId: orchardId.value,
})
}
catch (error) {
toast.error(`获取虫情数据失败: ${error.message || '未知错误'}`)
console.error('Failed to get soil analysis data:', error)
return error
}
}
function formatData(data = []) {
const lineRes = {}
const pieRes = {}
data.forEach((item) => {
const date = item.createTime.split(' ')[0].replace(/-/g, '/')
lineRes[item.name] ? lineRes[item.name].push(item) : (lineRes[item.name] = [item])
pieRes[date] ? pieRes[date].push(item) : (pieRes[date] = [item])
})
return {
lineRes,
pieRes,
}
}
function createChartData(data, type) {
const keys = Object.keys(data)
const values = []
keys.forEach((key) => {
values.push(data[key].reduce((pre, cur) => cur + (pre.value || 0), 0))
})
return type === 'line' ? { dates: keys, values } : { names: keys, values }
}
function getDateRange(num) {
const nowDate = new Date().toLocaleString().replace(/\//g, '-')
const preDate = new Date(+new Date(nowDate) - num * 24 * 60 * 60 * 1000).toLocaleString().replace(/\//g, '-')
return [preDate, nowDate]
}
async function getCanvasSize(id) { async function getCanvasSize(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
query.select(`#${id}`).boundingClientRect((info) => { query.select(`#${id}`).boundingClientRect((info) => {
@ -72,7 +131,7 @@ async function getChartCanvasSize(id) {
近7天虫害趋势 近7天虫害趋势
</view> --> </view> -->
<!-- <l-echart id="lineChart" ref="lineChartRef" class="chart" /> --> <!-- <l-echart id="lineChart" ref="lineChartRef" class="chart" /> -->
<line-chart id="lineChart" :echarts="echarts" /> <line-chart id="lineChart" :echarts="echarts" :chart-data="lineChartData" />
</wd-card> </wd-card>
<!-- 饼图区域 --> <!-- 饼图区域 -->
@ -80,7 +139,7 @@ async function getChartCanvasSize(id) {
<!-- <view class="chart-title"> <!-- <view class="chart-title">
虫害类型分布 虫害类型分布
</view> --> </view> -->
<pie-chart id="pieChart" :echarts="echarts" /> <pie-chart id="pieChart" :echarts="echarts" :chart-data="pieChartData" />
</wd-card> </wd-card>
</view> </view>
</template> </template>

@ -2,7 +2,7 @@
* @Author: chris * @Author: chris
* @Date: 2025-10-22 10:46:12 * @Date: 2025-10-22 10:46:12
* @LastEditors: chris * @LastEditors: chris
* @LastEditTime: 2025-10-22 15:29:18 * @LastEditTime: 2025-11-17 16:53:08
--> -->
<script setup lang="ts"> <script setup lang="ts">
// //

@ -0,0 +1,60 @@
<!--
* @Author: chris
* @Date: 2026-01-19 17:59:19
* @LastEditors: chris
* @LastEditTime: 2026-01-19 18:18:52
-->
<script setup>
definePage({
style: {
navigationBarTitleText: '预警推送设置',
},
})
const pushPlatform = ref('')
const pushTemplate = ref('')
const platformTypes = [
{
label: '公众号',
value: 'wechat',
},
{
label: '短信',
value: 'sms',
},
]
const templateTypes = [
{
label: '模板1',
value: 'template1',
},
{
label: '模板2',
value: 'template2',
},
]
function handleChange(item, type) {
if (type === 'platform') {
pushPlatform.value = item.value
}
else if (type === 'template') {
pushTemplate.value = item.value
}
}
</script>
<template>
<view class="warning-setting">
<wd-select-picker v-model="pushPlatform" value-key="value" label-key="label" label="推送平台" type="radio" :columns="platformTypes" @change="handleChange($event, 'platform')" />
<wd-select-picker v-model="pushTemplate" value-key="value" label-key="label" label="推送模板" type="radio" :columns="templateTypes" @change="handleChange($event, 'template')" />
</view>
</template>
<style scoped lang="scss">
.warning-setting {
@apply bg-[#f8f8f8] h-[100vh] py-[20rpx];
}
</style>

@ -0,0 +1,154 @@
<!--
* @Author: chris
* @Date: 2025-10-23 16:55:09
* @LastEditors: chris
* @LastEditTime: 2026-01-19 17:52:20
-->
<script setup lang="ts">
// import type { IWarning, IWarningListQuery } from '@/api/warning'
import type { IWarning, IWarningQuery } from '@/api/pest/warning'
import { getWarningList } from '@/api/pest/warning'
import { formatDate } from '@/utils/date'
definePage({
style: {
navigationBarTitleText: '预警信息',
},
})
const queryParams = ref<IWarningQuery>({
deptId: 0,
beginTime: '',
endTime: '',
pageSize: 10,
pageNum: 1,
})
const isLoading = ref<boolean>(false)
const dateRange = ref<number[]>([null, null])
const warningList = ref<IWarning[]>([{
id: 1,
deptId: 1,
notifyContent: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
createTime: '2026-01-19 17:46:57',
}, {
id: 2,
deptId: 1,
notifyContent: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
createTime: '2026-01-19 17:46:57',
}, {
id: 3,
deptId: 1,
notifyContent: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
createTime: '2026-01-19 17:46:57',
}, {
id: 4,
deptId: 1,
notifyContent: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
createTime: '2026-01-19 17:46:57',
}, {
id: 5,
deptId: 1,
notifyContent: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
createTime: '2026-01-19 17:46:57',
}, {
id: 6,
deptId: 1,
notifyContent: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
createTime: '2026-01-19 17:46:57',
}, {
id: 7,
deptId: 1,
notifyContent: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
createTime: '2026-01-19 17:46:57',
}])
async function loadData() {
isLoading.value = true
try {
const res = await getWarningList(queryParams.value)
warningList.value = res || []
}
finally {
isLoading.value = false
}
}
function handleConfirm(value: number[]) {
console.log(value)
queryParams.value.beginTime = formatDate(dateRange.value[0]).split(' ')[0]
queryParams.value.endTime = formatDate(dateRange.value[1]).split(' ')[0]
console.log('handleConfirm', queryParams.value)
}
function handleSubmit(value: number[]) {
console.log(value)
queryParams.value.pageNum = 1
loadData()
}
function handleScrollLower() {
console.log('scroll lower')
}
</script>
<template>
<view class="warning-page">
<wd-drop-menu>
<wd-drop-menu-item title="筛选">
<view class="filter-container">
<wd-calendar v-model="dateRange" type="daterange" @confirm="handleConfirm" />
<wd-button class="submit-btn" type="primary" block @click="handleSubmit">
查询
</wd-button>
</view>
</wd-drop-menu-item>
</wd-drop-menu>
<scroll-view scroll-y @scrolltolower="handleScrollLower">
<view class="warning-list">
<wd-card v-for="(item, index) in warningList" :key="index" class="warning-item">
<view class="warning-content">
<wd-text :text="item.notifyContent" />
<view class="warning-time">
<wd-text size="12px" :text="item.createTime" />
</view>
</view>
</wd-card>
</view>
</scroll-view>
</view>
</template>
<style lang="scss" scoped>
.warning-page {
@apply bg-[#f5f5f5];
}
.filter-container {
@apply p-20px;
.submit-btn {
@apply mt-30px;
}
}
scroll-view {
@apply h-[calc(100vh-48px)];
}
.warning-list {
@apply py-15px;
}
.warning-content {
@apply py-10px;
}
.warning-time {
@apply mt-20px text-right;
> .wd-text {
@apply py-2px px-6px bg-[#f5f5f5] rounded-8px;
}
}
</style>

@ -2,11 +2,9 @@
* @Author: chris * @Author: chris
* @Date: 2025-11-03 09:54:47 * @Date: 2025-11-03 09:54:47
* @LastEditors: chris * @LastEditors: chris
* @LastEditTime: 2025-11-05 10:23:37 * @LastEditTime: 2025-11-05 10:33:42
--> -->
<script setup lang="ts"> <script setup lang="ts">
import { nextTick, onMounted, ref } from 'vue'
// //
interface Message { interface Message {
id: number id: number
@ -25,7 +23,7 @@ const query = uni.createSelectorQuery().in(proxy)
const messages = ref<Message[]>([]) const messages = ref<Message[]>([])
const inputMessage = ref('') const inputMessage = ref('')
const isTyping = ref(false) const isTyping = ref(false)
const chatContainerRef = ref<HTMLElement>(null) const chatContainerRef = ref<HTMLElement | null>(null)
const scrollTop = ref(0) const scrollTop = ref(0)
// //
@ -142,12 +140,6 @@ function sendImg() {
icon: 'none', icon: 'none',
}) })
// #endif // #endif
// #ifdef MP-ALIPAY
my.showToast({
content: '选择图片失败',
type: 'none',
})
// #endif
}, },
}) })
// #endif // #endif

@ -11,21 +11,21 @@ const modules = [
title: '虫情监控', title: '虫情监控',
icon: bug, icon: bug,
desc: '实时监测果园病虫害情况', desc: '实时监测果园病虫害情况',
route: '/pages/pest/pest', route: '/pages-sub/pest/pest',
}, },
{ {
id: 'soil', id: 'soil',
title: '土壤墒情', title: '土壤墒情',
icon: turangshuishi, icon: turangshuishi,
desc: '监测土壤湿度、养分等数据', desc: '监测土壤湿度、养分等数据',
route: '/pages/soil/soil', route: '/pages-sub/soil/soil',
}, },
{ {
id: 'weather', id: 'weather',
title: '气象监测', title: '气象监测',
icon: tianqiyujing, icon: tianqiyujing,
desc: '温度、湿度、光照等数据', desc: '温度、湿度、光照等数据',
route: '/pages/weather/weather', route: '/pages-sub/weather/weather',
}, },
{ {
id: 'drone', id: 'drone',

@ -2,9 +2,11 @@
* @Author: chris * @Author: chris
* @Date: 2025-09-15 09:51:59 * @Date: 2025-09-15 09:51:59
* @LastEditors: chris * @LastEditors: chris
* @LastEditTime: 2025-10-29 14:13:34 * @LastEditTime: 2026-01-19 16:43:21
--> -->
<script setup> <script setup>
import { getOrchardInfo } from '@/api/orchard'
import { getWarningList } from '@/api/pest/warning'
import BasicInfo from './components/basicInfo.vue' import BasicInfo from './components/basicInfo.vue'
import Monitor from './components/monitor.vue' import Monitor from './components/monitor.vue'
import Title from './components/title.vue' import Title from './components/title.vue'
@ -16,11 +18,40 @@ definePage({
}, },
}) })
const loading = ref(false)
const orchardInfo = ref(null)
const orchardId = '105'
const warningList = ref([])
getBaseInfo()
getWarningListData()
//
function getBaseInfo() {
loading.value = true
return getOrchardInfo(orchardId).then((res) => {
orchardInfo.value = res.data
}).finally(() => {
loading.value = false
})
}
//
function getWarningListData() {
return getWarningList({
deptId: orchardInfo.value?.deptId,
pageNum: 1,
pageSize: 5,
}).then((res) => {
warningList.value = res.data
})
}
// //
function handleMoreWarning() { function handleMoreWarning() {
uni.navigateTo({ uni.navigateTo({
// url: '/pages/warning/warning', url: '/pages-sub/warning/warning',
url: '/pages/auth/auth', // url: '/pages/auth/auth',
}) })
} }
</script> </script>
@ -34,9 +65,8 @@ function handleMoreWarning() {
<view class="title-accent" /> <view class="title-accent" />
</view> </view>
</view> </view>
<!-- 首页内容区域 --> <!-- 首页内容区域 -->
<view class="home-content"> <view v-if="!loading" class="home-content">
<!-- 基本信息区域 --> <!-- 基本信息区域 -->
<Title title="基本信息" /> <Title title="基本信息" />
<basic-info /> <basic-info />
@ -51,6 +81,7 @@ function handleMoreWarning() {
</Title> </Title>
<Warning /> <Warning />
</view> </view>
<wd-loading v-else />
</view> </view>
</template> </template>

@ -0,0 +1,239 @@
<!--
* @Author: chris
* @Date: 2025-12-02 09:43:34
* @LastEditors: chris
* @LastEditTime: 2026-01-19 17:07:21
-->
<script setup lang="ts">
import type { IKnowledge, IKnowledgeListQuery } from '@/api/knowledge'
import { getKnowledgeList } from '@/api/knowledge'
//
const searchQuery = ref<string>('')
const queryParams = ref<IKnowledgeListQuery>({
deptId: 0,
content: '',
pageNum: 1,
pageSize: 10,
})
const pestList = ref<IKnowledge[]>([])
const isLoading = ref(false)
//
async function loadData() {
isLoading.value = true
try {
const res = await getKnowledgeList(queryParams.value)
pestList.value = res || []
}
finally {
isLoading.value = false
}
}
//
function handleSearch() {
queryParams.value.content = searchQuery.value
loadData()
}
//
function navigateToDetail(id: number) {
uni.navigateTo({
url: '/pages-sub/pest/detail',
query: { id },
})
}
//
onMounted(() => {
loadData()
})
</script>
<template>
<scroll-view scroll-y class="pest-knowledge-page">
<!-- 搜索框 -->
<view class="search-container">
<view class="search-box">
<uni-icons name="search" size="20" color="#999" class="search-icon" />
<input
v-model="searchQuery"
type="text"
placeholder="搜索虫害知识..."
class="search-input"
@input="handleSearch"
>
</view>
</view>
<!-- 知识列表 -->
<view class="knowledge-list">
<uni-load-more
v-if="isLoading"
status="loading"
class="loading-indicator"
/>
<!-- 列表为空提示 -->
<view v-else-if="pestList.length === 0" class="empty-state">
<uni-icons name="warning-empty" size="60" color="#ccc" />
<text class="empty-text">暂无相关知识</text>
</view>
<!-- 列表项 -->
<view
v-for="item in pestList"
v-else
:key="item.id"
class="knowledge-item"
@click="navigateToDetail(item.id)"
>
<view class="item-image">
<image :src="item.imageUrl" mode="aspectFill" />
</view>
<view class="item-content">
<text class="item-title">{{ item.title }}</text>
<text class="item-description">{{ item.content }}</text>
<view class="item-meta">
<text class="item-date">{{ item.createTime }}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</template>
<style scoped lang="scss">
.pest-knowledge-page {
height: calc(100vh - 50px - var(--safe-area-inset-bottom));
background-color: #f8f8f8;
}
/* 搜索框 */
.search-container {
padding: 12px 20px;
background-color: #fff;
margin-bottom: 20px;
}
.search-box {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 20px;
padding: 0 16px;
}
.search-icon {
margin-right: 8px;
}
.search-input {
flex: 1;
height: 40px;
font-size: 15px;
color: #333;
}
.search-input::placeholder {
color: #999;
}
/* 知识列表 */
.knowledge-list {
padding: 20px;
}
.loading-indicator {
padding: 20px 0;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 0;
}
.empty-text {
margin-top: 12px;
font-size: 14px;
color: #999;
}
/* 列表项 */
.knowledge-item {
display: flex;
background-color: #fff;
border-radius: 12px;
padding: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition:
transform 0.2s,
box-shadow 0.2s;
+ .knowledge-item {
margin-top: 12px;
}
}
.knowledge-item:active {
transform: scale(0.98);
}
.item-image {
width: 100px;
height: 100px;
margin-right: 16px;
flex-shrink: 0;
}
.item-image image {
width: 100%;
height: 100%;
border-radius: 8px;
}
.item-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.item-title {
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 8px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.item-description {
font-size: 13px;
color: #666;
margin-bottom: 12px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.4;
}
.item-meta {
display: flex;
align-items: center;
font-size: 11px;
color: #999;
}
.item-date {
margin-right: 10px;
}
</style>

@ -73,6 +73,27 @@ function handleLogout() {
}, },
}) })
} }
//
function handlePushSetting() {
uni.navigateTo({
url: '/pages-sub/waringSetting/warningSetting',
})
}
//
function handleChangePassword() {
uni.navigateTo({
url: '/pages-sub/changePassword/changePassword',
})
}
//
function handleFeedback() {
uni.navigateTo({
url: '/pages-sub/feedback/feedback',
})
}
</script> </script>
<template> <template>
@ -104,7 +125,9 @@ function handleLogout() {
<wd-card> <wd-card>
<wd-cell-group :border="true"> <wd-cell-group :border="true">
<wd-cell title="我的果园" value="***果园" /> <wd-cell title="我的果园" value="***果园" />
<wd-cell title="推送设置" is-link /> <wd-cell title="推送设置" is-link @click="handlePushSetting" />
<wd-cell title="意见反馈" is-link @click="handleFeedback" />
<wd-cell title="修改密码" is-link @click="handleChangePassword" />
</wd-cell-group> </wd-cell-group>
</wd-card> </wd-card>

@ -1,131 +0,0 @@
<!--
* @Author: chris
* @Date: 2025-10-23 16:55:09
* @LastEditors: chris
* @LastEditTime: 2025-10-24 10:13:44
-->
<script setup lang="ts">
interface Form {
beginTime: string
endTime: string
pageSize: number
pageNum: number
}
interface Warning {
title: string
time: string
}
definePage({
style: {
navigationBarTitleText: '预警信息',
},
})
const form = ref<Form>({
beginTime: '',
endTime: '',
pageSize: 10,
pageNum: 1,
})
const dateRange = ref<number[]>([null, null])
const warningList = ref<Warning[]>([{
title: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
time: '2小时前',
}, {
title: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
time: '2小时前',
}, {
title: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
time: '2小时前',
}, {
title: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
time: '2小时前',
}, {
title: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
time: '2小时前',
}, {
title: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
time: '2小时前',
}, {
title: '监测到果园东区荔枝蒂蛀虫密度异常增高预计48小时内可能大规模爆发可能大规模爆发可能大规模爆发123123123123',
time: '2小时前',
}])
function handleConfirm(value: number[]) {
console.log(value)
form.value.beginTime = dateRange.value[0] as any
form.value.endTime = dateRange.value[1] as any
}
function handleSubmit(value: number[]) {
console.log(value)
}
function handleScrollLower() {
console.log('scroll lower')
}
</script>
<template>
<view class="warning-page">
<wd-drop-menu>
<wd-drop-menu-item title="筛选">
<view class="filter-container">
<wd-calendar v-model="dateRange" type="daterange" @confirm="handleConfirm" />
<wd-button class="submit-btn" type="primary" block @click="handleSubmit">
查询
</wd-button>
</view>
</wd-drop-menu-item>
</wd-drop-menu>
<scroll-view scroll-y @scrolltolower="handleScrollLower">
<view class="warning-list">
<wd-card v-for="(item, index) in warningList" :key="index" class="warning-item">
<view class="warning-content">
<wd-text :text="item.title" />
<view class="warning-time">
<wd-text size="12px" :text="item.time" />
</view>
</view>
</wd-card>
</view>
</scroll-view>
</view>
</template>
<style lang="scss" scoped>
.warning-page {
@apply bg-[#f5f5f5];
}
.filter-container {
@apply p-20px;
.submit-btn {
@apply mt-30px;
}
}
scroll-view {
@apply h-[calc(100vh-48px)];
}
.warning-list {
@apply py-15px;
}
.warning-content {
@apply py-10px;
}
.warning-time {
@apply mt-20px text-right;
> .wd-text {
@apply py-2px px-6px bg-[#f5f5f5] rounded-8px;
}
}
</style>

@ -74,6 +74,15 @@ export const customTabbarList: CustomTabBarItem[] = [
icon: 'i-carbon-ai', icon: 'i-carbon-ai',
// badge: 10, // badge: 10,
}, },
{
pagePath: 'pages/knowledge/knowledge',
text: '知识',
// 1在fg-tabbar.vue页面上引入一下并注释掉见tabbar/index.vue代码第2行
// 2配置到 unocss.config.ts 的 safelist 中
iconType: 'unocss',
icon: 'i-carbon-ibm-watson-knowledge-studio',
// badge: 10,
},
{ {
pagePath: 'pages/me/me', pagePath: 'pages/me/me',
text: '我的', text: '我的',

@ -0,0 +1,25 @@
/*
* @Author: chris
* @Date: 2026-01-19 17:35:18
* @LastEditors: chris
* @LastEditTime: 2026-01-19 17:38:51
*/
export function formatDate(cellValue: string | number): string {
if (cellValue == null || cellValue === '')
return ''
const date = new Date(cellValue)
const year = date.getFullYear()
const month
= date.getMonth() + 1 < 10
? `0${date.getMonth() + 1}`
: date.getMonth() + 1
const day = date.getDate() < 10 ? `0${date.getDate()}` : date.getDate()
const hours = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours()
const minutes
= date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes()
const seconds
= date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()
return (
`${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
)
}

@ -2,7 +2,7 @@
* @Author: chris * @Author: chris
* @Date: 2025-10-20 10:45:46 * @Date: 2025-10-20 10:45:46
* @LastEditors: chris * @LastEditors: chris
* @LastEditTime: 2025-11-03 10:14:17 * @LastEditTime: 2025-12-02 10:12:29
*/ */
import type { import type {
Preset, Preset,
@ -57,7 +57,7 @@ export default defineConfig({
}, },
], ],
// 动态图标需要在这里配置或者写在vue页面中注释掉 // 动态图标需要在这里配置或者写在vue页面中注释掉
safelist: ['i-carbon-code', 'i-carbon-home', 'i-carbon-user', 'i-carbon-ai'], safelist: ['i-carbon-home', 'i-carbon-user', 'i-carbon-ai', 'i-carbon-ibm-watson-knowledge-studio'],
rules: [ rules: [
[ [
'p-safe', 'p-safe',

Loading…
Cancel
Save