ihzero 2022-11-03 17:58:54 +08:00
commit 282a5f38e8
18 changed files with 1303 additions and 248 deletions

View File

@ -33,6 +33,7 @@
"gen:icon": "esno ./build/generate/icon/index.ts"
},
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@ant-design/colors": "^6.0.0",
"@ant-design/icons-vue": "^6.1.0",
"@iconify/iconify": "^2.2.1",

View File

@ -149,6 +149,20 @@ export function getAgriculturalBasic(mode: ErrorMessageMode = 'modal') {
},
)
}
/**
* @description:
*/
export function getTownAgriculturalBasic(params, mode: ErrorMessageMode = 'modal') {
return defHttp.get(
{
url: '/api/agricultural-basic',
params,
},
{
errorMessageMode: mode,
},
)
}
/**
* @description:
*/
@ -230,3 +244,87 @@ export function editPassword(id: string, data, mode: ErrorMessageMode = 'modal')
},
)
}
/**
* @description:
*/
export function getCrops(data, mode: ErrorMessageMode = 'modal') {
return defHttp.get(
{
url: `/api/crops`,
data,
},
{
errorMessageMode: mode,
},
)
}
/**
* @description:
*/
export function addaGriculturalBasic(data, mode: ErrorMessageMode = 'modal') {
return defHttp.post(
{
url: `/api/agricultural-basic`,
data,
},
{
errorMessageMode: mode,
},
)
}
/**
* @description:
*/
export function editGriculturalBasic(id: string, data, mode: ErrorMessageMode = 'modal') {
return defHttp.put(
{
url: `/api/agricultural-basic/${id}`,
data,
},
{
errorMessageMode: mode,
},
)
}
/**
* @description:
*/
export function deleteGriculturalBasic(id: string, mode: ErrorMessageMode = 'modal') {
return defHttp.delete(
{
url: `/api/agricultural-basic/${id}`,
},
{
errorMessageMode: mode,
},
)
}
/**
* @description:
*/
export function GriculturalBasicInfo(id: string, mode: ErrorMessageMode = 'modal') {
return defHttp.get(
{
url: `/api/agricultural-basic/${id}`,
},
{
errorMessageMode: mode,
},
)
}
/**
* @description:
*/
export function getCropYields(params, mode: ErrorMessageMode = 'modal') {
return defHttp.get(
{
url: `/api/crop-yields`,
params,
},
{
errorMessageMode: mode,
},
)
}

View File

@ -1,5 +1,5 @@
import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
import type { AppRouteModule } from '/@/router/types'
import { LAYOUT } from '/@/router/constant'
const main: AppRouteModule = {
path: '/base',
name: 'Base',
@ -20,13 +20,21 @@ const main: AppRouteModule = {
},
},
{
path: 'industrial-structure',
name: 'BaseIndustrialStructure',
component: () => import('/@/views/base/industrial-structure/index.vue'),
path: 'base-data',
name: 'BaseData',
component: () => import('/@/views/base/base-data/index.vue'),
meta: {
title: '农业产业结构',
title: '全市基地数据',
},
},
// {
// path: 'industrial-structure',
// name: 'BaseIndustrialStructure',
// component: () => import('/@/views/base/industrial-structure/index.vue'),
// meta: {
// title: '农业产业结构',
// },
// },
{
path: 'ranking-list',
name: 'BaseRankingList',
@ -52,6 +60,6 @@ const main: AppRouteModule = {
},
},
],
};
}
export default main;
export default main

View File

@ -90,3 +90,13 @@ export const withInstall = <T>(component: T, alias?: string) => {
}
return component as T & Plugin
}
export function formatDataByObject(obj) {
const arr: any[] = []
Object.keys(obj).forEach((e) => {
arr.push({
label: obj[e],
value: e,
})
})
return arr
}

View File

@ -0,0 +1,77 @@
<template>
<BasicDrawer
v-bind="$attrs"
@register="registerDrawer"
showFooter
:title="getTitle"
width="1000px"
@ok="handleSubmit"
>
<BasicForm @register="registerForm"> </BasicForm>
<Map v-model:modelValue="mapInfo" />
</BasicDrawer>
</template>
<script lang="ts" setup>
import { ref, computed, unref, watch } from 'vue'
import { BasicForm, useForm } from '/@/components/Form/index'
import { accountFormSchema } from './base.data'
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'
import Map from './map.vue'
import { addaGriculturalBasic, editGriculturalBasic } from '/@/api/sys/user'
const mapInfo = ref({
address: '',
latitude: '',
longitude: '',
})
const emits = defineEmits(['success', 'register'])
const isUpdate = ref(false)
const getTitle = computed(() => (!isUpdate.value ? '新增基地' : '编辑基地'))
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 90,
baseColProps: { span: 24 },
schemas: accountFormSchema,
showActionButtonGroup: false,
})
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
resetFields()
setDrawerProps({ confirmLoading: false })
isUpdate.value = data?.isUpdate
if (unref(isUpdate)) {
mapInfo.value = {
address: data.address,
latitude: data.address_lat,
longitude: data.address_lng,
}
setFieldsValue({
...data,
crops_ids: data.crops.map((e) => e.id),
})
}
})
watch(mapInfo, (newValue) => {
setFieldsValue({
address: newValue?.address,
address_lat: newValue?.latitude,
address_lng: newValue.longitude,
})
})
const handleSubmit = async () => {
try {
const values = await validate()
values.type = 1
setDrawerProps({ confirmLoading: true })
if (values.id) {
//
await editGriculturalBasic(values.id, values)
} else {
//
await addaGriculturalBasic(values)
}
closeDrawer()
emits('success')
} finally {
setDrawerProps({ confirmLoading: false })
}
}
</script>

View File

@ -0,0 +1,132 @@
import { BasicColumn } from '/@/components/Table'
import { FormSchema } from '/@/components/Table'
import { getCrops } from '/@/api/sys/user'
export const columns: BasicColumn[] = [
{
title: '基地名称',
dataIndex: 'name',
},
{
title: '基地负责人',
dataIndex: 'person',
},
{
title: '基地农作物',
dataIndex: 'crops',
},
{
title: '基地经度',
dataIndex: 'address_lat',
},
{
title: '基地纬度',
dataIndex: 'address_lng',
},
{
title: '基地地址',
dataIndex: 'address',
},
{
title: '基地面积',
dataIndex: 'areas',
},
{
title: '基地就业人数',
dataIndex: 'workforce',
},
{
title: '基地描述',
dataIndex: 'description',
},
{
width: 180,
title: '操作',
dataIndex: 'action',
align: 'center',
fixed: undefined,
},
]
export const searchFormSchema: FormSchema[] = []
export const accountFormSchema: FormSchema[] = [
{
field: 'id',
label: '基地ID',
required: false,
dynamicDisabled: true,
component: 'Input',
ifShow: ({ values }) => {
return !!values.id
},
},
{
field: 'name',
label: '基地名称',
required: true,
component: 'Input',
},
{
field: 'address',
label: '基地地址',
required: true,
component: 'Input',
},
{
field: 'person',
label: '基地负责人',
required: true,
component: 'Input',
},
{
field: 'areas',
label: '基地面积',
required: true,
component: 'Input',
},
{
field: 'workforce',
label: '基地人数',
required: true,
component: 'Input',
},
{
field: 'crops_ids',
label: '基地农作物',
required: true,
component: 'ApiSelect',
componentProps: {
api: async () => {
const res = await getCrops({ type: 'all', crop_type: 1 })
return res.map((e) => {
return {
...e,
disabled: e.is_end === 1,
}
})
},
labelField: 'name',
valueField: 'id',
mode: 'multiple',
},
},
{
field: 'description',
label: '基地介绍',
required: false,
component: 'InputTextArea',
},
{
field: 'address_lat',
label: '基地经度',
required: false,
component: 'Input',
},
{
field: 'address_lng',
label: '基地纬度',
required: false,
component: 'Input',
},
]

View File

@ -0,0 +1,86 @@
<template>
<div class="pt-20px">
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" @click="handleCreate"> </a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<div class="flex items-center justify-center">
<TableAction
:actions="[
{ label: '编辑', onClick: handleEdit.bind(null, record) },
{
label: '删除',
popConfirm: {
title: '是否确认删除',
placement: 'topRight',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</div>
</template>
<template v-if="column.key === 'crops'">
<Button
class="mx-2px"
v-for="(item, index) in record['crops']"
:key="index"
type="link"
>{{ item.name }}</Button
>
</template>
</template>
</BasicTable>
<BaseDrawer @register="registerDrawer" @success="handleSuccess" />
</div>
</template>
<script lang="ts" setup>
import { BasicTable, useTable, TableAction } from '/@/components/Table'
import { deleteGriculturalBasic, getTownAgriculturalBasic } from '/@/api/sys/user'
import { message } from 'ant-design-vue'
import { useDrawer } from '/@/components/Drawer'
import BaseDrawer from './BaseDrawer.vue'
import { Button } from 'ant-design-vue'
import { columns, searchFormSchema } from './base.data'
const [registerDrawer, { openDrawer }] = useDrawer()
const [registerTable, { reload }] = useTable({
title: '基地列表',
api: async (e) => {
const res = await getTownAgriculturalBasic({ type: 1, ...e })
return res
},
rowKey: 'id',
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
useSearchForm: false,
showTableSetting: true,
bordered: true,
showIndexColumn: true,
})
const handleSuccess = () => {
message.success('操作成功')
reload()
}
const handleCreate = () => {
openDrawer(true, {
isUpdate: false,
})
}
const handleEdit = (record: Recordable) => {
openDrawer(true, {
...record,
isUpdate: true,
})
}
const handleDelete = async (record: Recordable) => {
await deleteGriculturalBasic(record.id)
message.success('删除成功')
reload()
}
</script>

View File

@ -0,0 +1,115 @@
<template>
<div class="map-wrapper">
<div id="mapcontainer"></div>
</div>
</template>
<script setup>
import { shallowRef, computed, onMounted, ref, watch, nextTick } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader'
import { cloneWith } from 'lodash'
window._AMapSecurityConfig = {
securityJsCode: 'c18396f675a1469441ec75a190c70ee7',
}
const props = defineProps({
modelValue: {
type: Object,
default() {
return {}
},
},
})
const emit = defineEmits(['update:modelValue'])
const map = shallowRef(null)
//
const location = computed({
get() {
return props.modelValue
},
set(val) {
emit('update:modelValue', val)
},
})
watch(location, (val) => {
if (val.longitude && val.latitude) {
nextTick(() => {
drawMarker(val.longitude, val.latitude)
})
}
})
let placeSearch, AMapObj, marker, geocoder
function initMap() {
AMapLoader.load({
key: '98a4438e9a1e86bc285783f68656f7b5', // WebKey load
version: '2.0',
}).then((AMap) => {
AMapObj = AMap
map.value = new AMap.Map('mapcontainer', {
center: [104.739928, 29.484215], //
zoom: 5, //
})
//
map.value.on('click', onMapClick)
if (location?.value?.longitude) {
drawMarker(location?.value?.longitude, location?.value?.latitude)
}
AMap.plugin(
['AMap.ToolBar', 'AMap.Scale', 'AMap.Geolocation', 'AMap.PlaceSearch', 'AMap.Geocoder'],
() => {
geocoder = new AMap.Geocoder({
city: '全国',
})
},
)
})
}
onMounted(() => {
initMap()
})
//
function onMapClick(e) {
const { lng, lat } = e.lnglat
drawMarker(lng, lat)
//
geocoder.getAddress([lng, lat], (status, result) => {
if (status === 'complete' && result.info === 'OK') {
const { addressComponent, formattedAddress } = result.regeocode
let { city, province, district } = addressComponent
if (!city) {
//
city = province
}
location.value = {
longitude: lng,
latitude: lat,
address: formattedAddress,
}
}
})
}
// marker
function drawMarker(longitude, latitude) {
if (marker) {
marker.setMap(null)
}
marker = new AMapObj.Marker({
position: new AMapObj.LngLat(longitude, latitude),
anchor: 'bottom-center',
})
map.value.add(marker)
}
</script>
<style lang="less" scoped>
.map-wrapper {
position: relative;
width: 100%;
height: 400px;
#mapcontainer {
width: 100%;
height: 100%;
}
}
</style>

View File

@ -0,0 +1,55 @@
<template>
<BasicDrawer
v-bind="$attrs"
@register="registerDrawer"
showFooter
:title="getTitle"
width="500px"
@ok="handleSubmit"
>
<BasicForm @register="registerForm"> </BasicForm>
</BasicDrawer>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue'
import { BasicForm, useForm } from '/@/components/Form/index'
import { accountFormSchema } from './city.data'
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'
import { addaGriculturalBasic, editGriculturalBasic } from '/@/api/sys/user'
const emits = defineEmits(['success', 'register'])
const isUpdate = ref(false)
const getTitle = computed(() => (!isUpdate.value ? '新增街镇' : '编辑街镇'))
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: accountFormSchema,
showActionButtonGroup: false,
})
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
resetFields()
setDrawerProps({ confirmLoading: false })
isUpdate.value = data?.isUpdate
if (unref(isUpdate)) {
setFieldsValue({
...data,
})
}
})
const handleSubmit = async () => {
try {
const values = await validate()
values.type = 2
if (values.id) {
//
await editGriculturalBasic(values.id, values)
} else {
//
await addaGriculturalBasic(values)
}
closeDrawer()
emits('success')
} finally {
setDrawerProps({ confirmLoading: false })
}
}
</script>

View File

@ -0,0 +1,127 @@
import { BasicColumn } from '/@/components/Table'
import { FormSchema } from '/@/components/Table'
// import { getCrops } from '/@/api/sys/user'
export const columns: BasicColumn[] = [
{
title: '街镇名称',
dataIndex: 'name',
},
// {
// title: '农作物',
// dataIndex: 'cultivated',
// },
// {
// title: '街镇经度',
// dataIndex: 'address_lat',
// },
// {
// title: '街镇纬度',
// dataIndex: 'address_lng',
// },
{
title: '街镇地址',
dataIndex: 'address',
},
{
title: '街镇面积',
dataIndex: 'areas',
},
{
title: '街镇就业人数',
dataIndex: 'workforce',
},
{
title: '街镇描述',
dataIndex: 'description',
},
{
width: 180,
title: '操作',
dataIndex: 'action',
align: 'center',
fixed: undefined,
},
]
export const searchFormSchema: FormSchema[] = []
export const accountFormSchema: FormSchema[] = [
{
field: 'id',
label: '街镇ID',
required: false,
dynamicDisabled: true,
component: 'Input',
ifShow: ({ values }) => {
return !!values.id
},
},
{
field: 'name',
label: '街镇名称',
required: true,
component: 'Input',
},
{
field: 'address',
label: '街镇地址',
required: true,
component: 'Input',
},
// {
// field: 'person',
// label: '街镇负责人',
// required: true,
// component: 'Input',
// },
{
field: 'areas',
label: '街镇面积',
required: true,
component: 'Input',
},
{
field: 'workforce',
label: '街镇人数',
required: true,
component: 'Input',
},
// {
// field: 'crops_ids',
// label: '街镇农作物',
// required: true,
// component: 'ApiSelect',
// componentProps: {
// api: async () => {
// const res = await getCrops({ type: 'all', crop_type: 2 })
// return res.map((e) => {
// return {
// ...e,
// disabled: e.is_end === 1,
// }
// })
// },
// labelField: 'name',
// valueField: 'id',
// mode: 'multiple',
// },
// },
// {
// field: 'address_lat',
// label: '街镇经度',
// required: false,
// component: 'Input',
// },
// {
// field: 'address_lng',
// label: '街镇纬度',
// required: false,
// component: 'Input',
// },
{
field: 'description',
label: '街镇介绍',
required: false,
component: 'InputTextArea',
},
]

View File

@ -1,8 +1,8 @@
<template>
<div class="pl-10px mb-10px">
<div class="flex items-center">
<SvgIcon :name="data.icon" :size="16" color="transparent" />
<div class="text-[#828fa2] text-14px ml-4px">{{ data.title }}</div>
<SvgIcon name="city" :size="16" color="transparent" />
<div class="text-[#828fa2] text-14px ml-4px">{{ data.name }}</div>
</div>
<div class="mt-6px">
<span class="text-28px font-bold">{{ data.value }}</span>
@ -12,14 +12,14 @@
</template>
<script setup>
import { SvgIcon } from '/@/components/Icon/index';
import { SvgIcon } from '/@/components/Icon/index'
defineProps({
loading: Boolean,
data: {
type: Object,
default: () => {},
},
});
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,78 +1,60 @@
<template>
<PageWrapper>
<a-card title="全市数据统计">
<template #extra>
<!-- <template #extra>
<a-button size="small">编辑</a-button>
</template>
<a-card-grid class="!md:w-1/3 !xl:w-1/7 !w-full">
<CountItem :data="{ title: '幅员面积', icon: 'city', unit: '平方公里', value: 794.41 }" />
</a-card-grid>
<a-card-grid class="!md:w-1/3 !xl:w-1/7 !w-full">
<CountItem :data="{ title: '镇街', icon: 'city', unit: '个', value: 13 }" />
</a-card-grid>
<a-card-grid class="!md:w-1/3 !xl:w-1/7 !w-full">
<CountItem :data="{ title: '水产品产量', icon: 'city', unit: '万吨', value: 3.72 }" />
</a-card-grid>
<a-card-grid class="!md:w-1/3 !xl:w-1/7 !w-full">
<CountItem :data="{ title: '粮食产量', icon: 'city', unit: '万吨', value: 33.28 }" />
</a-card-grid>
<a-card-grid class="!md:w-1/3 !xl:w-1/7 !w-full">
<CountItem :data="{ title: '人口', icon: 'city', unit: '万人', value: 77.94 }" />
</a-card-grid>
<a-card-grid class="!md:w-1/3 !xl:w-1/7 !w-full">
<CountItem :data="{ title: '耕地总面积', icon: 'city', unit: '万亩', value: 59.66 }" />
</a-card-grid>
<a-card-grid class="!md:w-1/3 !xl:w-1/7 !w-full">
<CountItem :data="{ title: '生猪年出栏', icon: 'city', unit: '万头', value: 42.01 }" />
</template> -->
<a-card-grid
class="!md:w-1/3 !xl:w-1/7 !w-full"
v-for="(item, index) in CityDate"
:key="index"
>
<CountItem :data="item" />
</a-card-grid>
</a-card>
<a-card title="基地数据" class="!mt-4">
<template #extra>
<a-button size="small">新增</a-button>
</template>
<BasicTable @register="registerTable" />
</a-card>
<div class="mt-20px">
<BasicTable @register="registerTable">
<template #toolbar>
<!-- <a-button type="primary" @click="handleCreate"> </a-button> -->
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<div class="flex items-center justify-center">
<TableAction
:actions="[
{ label: '编辑', onClick: handleEdit.bind(null, record) },
// {
// label: '',
// popConfirm: {
// title: '',
// placement: 'topRight',
// confirm: handleDelete.bind(null, record),
// },
// },
]"
/>
</div>
</template>
</template>
</BasicTable>
</div>
<DeviceDrawer @register="registerDrawer" @success="handleSuccess" />
</PageWrapper>
</template>
<script lang="ts">
import { PageWrapper } from '/@/components/Page';
import { defineComponent } from 'vue';
import { Card } from 'ant-design-vue';
import CountItem from './components/CountItem.vue';
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
const columns: BasicColumn[] = [
{
title: '基地名称',
dataIndex: 'roleName',
},
{
title: '基地农作物',
dataIndex: 'roleValue',
},
{
title: '负责人',
dataIndex: 'orderNo',
},
{
title: '经度',
dataIndex: 'createTime',
},
{
title: '纬度',
dataIndex: 'remark1',
},
{
title: '基地面积',
dataIndex: 'remark2',
},
{
title: '就业人数',
dataIndex: 'remark3',
},
];
import { PageWrapper } from '/@/components/Page'
import { defineComponent, onMounted, ref } from 'vue'
import { Card } from 'ant-design-vue'
import CountItem from './components/CountItem.vue'
import DeviceDrawer from './DeviceDrawer.vue'
import { getCitydataStatistics } from '/@/api/sys/other'
import { getTownAgriculturalBasic, deleteGriculturalBasic } from '/@/api/sys/user'
import { columns } from './city.data'
import { BasicTable, useTable, TableAction } from '/@/components/Table'
import { useDrawer } from '/@/components/Drawer'
import { message } from 'ant-design-vue'
export default defineComponent({
components: {
[Card.name]: Card,
@ -80,28 +62,58 @@
CountItem,
PageWrapper,
BasicTable,
TableAction,
DeviceDrawer,
},
setup() {
const CityDate = ref([])
const [registerDrawer, { openDrawer }] = useDrawer()
const [registerTable, { reload }] = useTable({
// api: getRoleListByPage,
api: async () => {
const res = await getTownAgriculturalBasic({ type: 2 })
return res
},
columns,
useSearchForm: false,
showTableSetting: false,
bordered: true,
showIndexColumn: false,
actionColumn: {
width: 80,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: undefined,
},
});
})
const handleCreate = () => {
openDrawer(true, {
isUpdate: false,
})
}
const handleEdit = (record: Recordable) => {
openDrawer(true, {
...record,
isUpdate: true,
})
}
const handleDelete = async (record: Recordable) => {
await deleteGriculturalBasic(record.id)
message.success('删除成功')
reload()
}
const handleSuccess = () => {
message.success('操作成功')
reload()
}
onMounted(async () => {
let res = await getCitydataStatistics()
CityDate.value = res
})
return {
CityDate,
registerTable,
};
handleCreate,
handleEdit,
handleDelete,
registerDrawer,
handleSuccess,
}
},
});
})
</script>
<style scoped></style>

View File

@ -6,9 +6,9 @@
</template>
<script lang="ts" setup>
import { Card } from 'ant-design-vue';
import { Ref, ref, watch } from 'vue';
import { useECharts } from '/@/hooks/web/useECharts';
import { Card } from 'ant-design-vue'
import { Ref, ref, watch } from 'vue'
import { useECharts } from '/@/hooks/web/useECharts'
const props = defineProps({
loading: Boolean,
width: {
@ -19,14 +19,14 @@
type: String as PropType<string>,
default: '300px',
},
});
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
})
const chartRef = ref<HTMLDivElement | null>(null)
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
watch(
() => props.loading,
() => {
if (props.loading) {
return;
return
}
setOptions({
tooltip: {
@ -71,12 +71,12 @@
},
},
],
});
})
},
{
immediate: true,
},
);
)
</script>
<style scoped></style>

View File

@ -0,0 +1,80 @@
<template>
<BasicDrawer
v-bind="$attrs"
@register="registerDrawer"
showFooter
:title="getTitle"
width="500px"
@ok="handleSubmit"
>
<BasicForm @register="registerForm"> </BasicForm>
</BasicDrawer>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue'
import { BasicForm, useForm } from '/@/components/Form/index'
import { accountFormSchema } from './ranking.data'
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'
import { defaultsDeep } from 'lodash-es'
import { createDevice, getDeviceTypes, updateDevice } from '/@/api/sys/other'
const emits = defineEmits(['success', 'register'])
const isUpdate = ref(false)
const getTitle = computed(() => (!isUpdate.value ? '新增设备' : '编辑设备'))
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: accountFormSchema,
showActionButtonGroup: false,
})
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
resetFields()
setDrawerProps({ confirmLoading: false })
isUpdate.value = data?.isUpdate
if (unref(isUpdate)) {
const obj = Object.assign({}, { ...data, ...data?.extends })
const deviceTypes = await getDeviceTypes()
await setFieldsValue({
...obj,
agricultural_base_id: obj.base_id,
type: formatDataByObject(deviceTypes).find((e) => e.label == obj.type)?.value,
})
}
})
const formatDataByObject = (obj) => {
const arr: any[] = []
Object.keys(obj).forEach((e) => {
arr.push({
label: obj[e],
value: e,
})
})
return arr
}
const setValue = (keys: any, value: any) => {
const object = {}
var last = keys.pop()
keys.reduce((o, k) => (o[k] = o[k] || {}), object)[last] = value
return object
}
const handleSubmit = async () => {
try {
const values = await validate()
let params = {}
for (const key in values) {
params = defaultsDeep({}, params, setValue(key.split('.'), values[key]))
}
setDrawerProps({ confirmLoading: true })
if (values.id) {
//
await updateDevice(values.id, params)
} else {
//
await createDevice(params)
}
closeDrawer()
emits('success')
} finally {
setDrawerProps({ confirmLoading: false })
}
}
</script>

View File

@ -1,160 +1,78 @@
<template>
<PageWrapper>
<a-card>
<a-tabs :animated="false">
<template v-for="item in achieveList" :key="item.key">
<a-tab-pane>
<template #tab>
<span> {{ item.name }}</span>
</template>
</a-tab-pane>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" @click="handleCreate"> </a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<div class="flex items-center justify-center">
<TableAction
:actions="[
{ label: '编辑', onClick: handleEdit.bind(null, record) },
{
label: '删除',
popConfirm: {
title: '是否确认删除',
placement: 'topRight',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</div>
</template>
</a-tabs>
<div class="flex justify-between">
<ADatePicker
mode="year"
v-model:value="yearValue"
format="YYYY"
:open="yearShow"
@openChange="handleOpenChange"
@panelChange="handlePanelChange"
valueFormat="YYYY"
/>
<a-button>新增</a-button>
</div>
<BasicTable class="p0 mt-4" @register="registerTable" />
</a-card>
</PageWrapper>
</template>
</BasicTable>
<DeviceDrawer @register="registerDrawer" @success="handleSuccess" />
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import { Tabs, Card, DatePicker, Button } from 'ant-design-vue'
import { PageWrapper } from '/@/components/Page'
import { BasicTable, useTable, BasicColumn } from '/@/components/Table'
// import { Moment } from 'moment';
export interface TabItem {
key: string
name: string
}
const achieveList: TabItem[] = [
{
key: '1',
name: '水稻',
<script lang="ts" setup>
import { BasicTable, useTable, TableAction } from '/@/components/Table'
import { getCropYields } from '/@/api/sys/user'
import { message } from 'ant-design-vue'
import { useDrawer } from '/@/components/Drawer'
import DeviceDrawer from './RankingDrawer.vue'
import { columns, searchFormSchema } from './ranking.data'
const [registerDrawer, { openDrawer }] = useDrawer()
const [registerTable, { reload }] = useTable({
title: '产量列表',
api: async (e) => {
console.log(e)
const res = await getCropYields(e)
console.log(res)
return res.list
},
{
key: '2',
name: '耙耙柑',
},
{
key: '3',
name: '活草鱼',
},
{
key: '4',
name: '生猪',
},
{
key: '5',
name: '稻虾',
},
{
key: '6',
name: '乌鱼',
},
{
key: '7',
name: '鲈鱼',
},
{
key: '8',
name: '爱媛',
},
]
const columns: BasicColumn[] = [
{
title: '基地',
dataIndex: 'roleName',
},
{
title: '时间',
dataIndex: 'roleValue',
},
{
title: '录入时间',
dataIndex: 'orderNo',
},
{
title: '录入人员',
dataIndex: 'createTime',
},
{
title: '总产量(斤)',
dataIndex: 'createTime2',
},
{
title: '种养殖面积(亩)',
dataIndex: 'createTime3',
},
{
title: '产值(元)',
dataIndex: 'createTime4',
},
]
export default defineComponent({
components: {
[Tabs.name]: Tabs,
[Tabs.TabPane.name]: Tabs.TabPane,
[Card.name]: Card,
[DatePicker.name]: DatePicker,
[Button.name]: Button,
PageWrapper,
BasicTable,
},
setup() {
const yearShow = ref<boolean>(false)
const yearValue = ref()
const [registerTable, { reload }] = useTable({
// api: getRoleListByPage,
columns,
useSearchForm: false,
showTableSetting: false,
bordered: true,
showIndexColumn: false,
actionColumn: {
width: 80,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: undefined,
},
})
const handleOpenChange = (open: boolean) => {
yearShow.value = open
}
const handlePanelChange = (e) => {
yearShow.value = false
yearValue.value = e
console.log(e.format('YYYY'))
}
return {
yearShow,
yearValue,
achieveList,
registerTable,
handleOpenChange,
handlePanelChange,
}
rowKey: 'id',
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
useSearchForm: true,
showTableSetting: true,
bordered: true,
showIndexColumn: true,
})
</script>
<style lang="less">
.p0 {
.ant-table-wrapper {
padding: 0 !important;
}
const handleSuccess = () => {
message.success('操作成功')
reload()
}
</style>
const handleCreate = () => {
openDrawer(true, {
id: false,
})
}
const handleEdit = (record: Recordable) => {
openDrawer(true, {
...record,
isUpdate: true,
})
}
const handleDelete = async (record: Recordable) => {
// await deleteDevice(record.id)
message.success('删除成功')
reload()
}
</script>

View File

@ -0,0 +1,160 @@
<template>
<PageWrapper>
<a-card>
<a-tabs :animated="false">
<template v-for="item in achieveList" :key="item.key">
<a-tab-pane>
<template #tab>
<span> {{ item.name }}</span>
</template>
</a-tab-pane>
</template>
</a-tabs>
<div class="flex justify-between">
<ADatePicker
mode="year"
v-model:value="yearValue"
format="YYYY"
:open="yearShow"
@openChange="handleOpenChange"
@panelChange="handlePanelChange"
valueFormat="YYYY"
/>
<a-button>新增</a-button>
</div>
<BasicTable class="p0 mt-4" @register="registerTable" />
</a-card>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import { Tabs, Card, DatePicker, Button } from 'ant-design-vue'
import { PageWrapper } from '/@/components/Page'
import { BasicTable, useTable, BasicColumn } from '/@/components/Table'
// import { Moment } from 'moment';
export interface TabItem {
key: string
name: string
}
const achieveList: TabItem[] = [
{
key: '1',
name: '水稻',
},
{
key: '2',
name: '耙耙柑',
},
{
key: '3',
name: '活草鱼',
},
{
key: '4',
name: '生猪',
},
{
key: '5',
name: '稻虾',
},
{
key: '6',
name: '乌鱼',
},
{
key: '7',
name: '鲈鱼',
},
{
key: '8',
name: '爱媛',
},
]
const columns: BasicColumn[] = [
{
title: '基地',
dataIndex: 'roleName',
},
{
title: '时间',
dataIndex: 'roleValue',
},
{
title: '录入时间',
dataIndex: 'orderNo',
},
{
title: '录入人员',
dataIndex: 'createTime',
},
{
title: '总产量(斤)',
dataIndex: 'createTime2',
},
{
title: '种养殖面积(亩)',
dataIndex: 'createTime3',
},
{
title: '产值(元)',
dataIndex: 'createTime4',
},
]
export default defineComponent({
components: {
[Tabs.name]: Tabs,
[Tabs.TabPane.name]: Tabs.TabPane,
[Card.name]: Card,
[DatePicker.name]: DatePicker,
[Button.name]: Button,
PageWrapper,
BasicTable,
},
setup() {
const yearShow = ref<boolean>(false)
const yearValue = ref()
const [registerTable, { reload }] = useTable({
// api: getRoleListByPage,
columns,
useSearchForm: false,
showTableSetting: false,
bordered: true,
showIndexColumn: false,
actionColumn: {
width: 80,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: undefined,
},
})
const handleOpenChange = (open: boolean) => {
yearShow.value = open
}
const handlePanelChange = (e) => {
yearShow.value = false
yearValue.value = e
console.log(e.format('YYYY'))
}
return {
yearShow,
yearValue,
achieveList,
registerTable,
handleOpenChange,
handlePanelChange,
}
},
})
</script>
<style lang="less">
.p0 {
.ant-table-wrapper {
padding: 0 !important;
}
}
</style>

View File

@ -0,0 +1,171 @@
import { BasicColumn } from '/@/components/Table'
import { FormSchema } from '/@/components/Table'
import { getAgriculturalBasic } from '/@/api/sys/user'
import { getDeviceTypes } from '/@/api/sys/other'
import { h } from 'vue'
import { Tag } from 'ant-design-vue'
function formatDataByObject(obj): any[] {
const arr: any[] = []
Object.keys(obj).forEach((e) => {
arr.push({
label: obj[e],
value: e,
})
})
return arr
}
export const columns: BasicColumn[] = [
{
title: '类型',
dataIndex: 'type',
},
{
title: '设备编号',
dataIndex: 'sn',
},
{
title: '基地',
dataIndex: 'base_name',
},
{
title: '监控点',
dataIndex: 'monitoring_point',
},
{
title: '状态',
dataIndex: 'status',
customRender: ({ record }) => {
const status = record.status
// 状态: 0 禁用, 1 在线, 2 离线, 3 故障
const list = [
{
value: 0,
color: 'red',
label: '禁用',
},
{
value: 1,
color: 'green',
label: '在线',
},
{
value: 2,
color: 'pink',
label: '离线',
},
{
value: 3,
color: 'orange',
label: '故障',
},
]
const item = list.find((e) => e.value === status)
const color = item?.color ?? 'red'
const text = item?.label ?? status
return h(Tag, { color: color }, () => text)
},
},
{
width: 180,
title: '操作',
dataIndex: 'action',
align: 'center',
fixed: undefined,
},
]
export const searchFormSchema: FormSchema[] = [
{
field: 'year',
label: '年份',
component: 'ApiSelect',
componentProps: {
api: getAgriculturalBasic,
labelField: 'name',
valueField: 'id',
},
colProps: { span: 6 },
},
{
field: 'crop',
label: '农作物',
component: 'ApiSelect',
componentProps: {
api: async () => {
const res = await getDeviceTypes()
return formatDataByObject(res)
},
},
colProps: { span: 6 },
},
]
export const accountFormSchema: FormSchema[] = [
{
field: 'id',
label: 'ID',
required: false,
dynamicDisabled: true,
component: 'Input',
ifShow: ({ values }) => {
return !!values.id
},
},
{
field: 'time_year',
label: '年份',
required: true,
component: 'DatePicker',
componentProps: {
mode: 'year',
},
},
{
field: 'quarter',
label: '季度',
required: true,
component: 'Input',
},
{
field: 'crop_id',
label: '农作物',
required: true,
component: 'ApiSelect',
componentProps: {
api: async () => {
const res = await getDeviceTypes()
return formatDataByObject(res)
},
},
},
{
field: 'base_id',
label: '区域',
required: true,
component: 'ApiSelect',
componentProps: {
api: async () => {
const res = await getDeviceTypes()
return formatDataByObject(res)
},
},
},
{
field: 'yield',
label: '产量',
required: true,
component: 'Input',
},
{
field: 'cultivated',
label: '种养殖面积',
required: true,
component: 'Input',
},
{
field: 'output',
label: '产值',
required: true,
component: 'Input',
},
]

View File

@ -2,6 +2,11 @@
# yarn lockfile v1
"@amap/amap-jsapi-loader@^1.0.1":
version "1.0.1"
resolved "https://registry.npmmirror.com/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz#9ec4b4d5d2467eac451f6c852e35db69e9f9f0c0"
integrity sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw==
"@ampproject/remapping@^2.1.0":
version "2.2.0"
resolved "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"