This commit is contained in:
unknown 2024-07-05 02:07:18 +08:00
parent bd27f18e4c
commit 9004b72ba2
55 changed files with 22758 additions and 8336 deletions

View File

@ -0,0 +1,3 @@
{
"type": "module"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -4,71 +4,70 @@
/* add fonts here */
/* @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); */
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--foreground: 0 0% 5%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--card-foreground: 0 0% 5%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--popover-foreground: 0 0% 5%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--primary: 190 56% 55%;
--primary-foreground: 0 0% 2%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--secondary: 0 0% 96%;
--secondary-foreground: 0 0% 5%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--muted: 0 0% 96%;
--muted-foreground: 0 0% 45%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--accent: 0 0% 96%;
--accent-foreground: 0 0% 5%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--destructive: 0 84% 60%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 92%;
--input: 0 0% 85%;
--ring: 0 0% 76%;
--border:214.3 31.8% 91.4%;
--input:214.3 31.8% 91.4%;
--ring:221.2 83.2% 53.3%;
--radius: 0.5rem;
}
.dark {
--background:222.2 84% 4.9%;
--foreground:210 40% 98%;
--background: 0 0% 0%;
--foreground: 0 0% 98%;
--card:222.2 84% 4.9%;
--card-foreground:210 40% 98%;
--card: 0 0% 3%;
--card-foreground: 0 0% 98%;
--popover:222.2 84% 4.9%;
--popover-foreground:210 40% 98%;
--popover: 0 0% 3%;
--popover-foreground: 0 0% 98%;
--primary:217.2 91.2% 59.8%;
--primary-foreground:222.2 47.4% 11.2%;
--primary: 187 71% 53%;
--primary-foreground: 0 0% 2%;
--secondary:217.2 32.6% 17.5%;
--secondary-foreground:210 40% 98%;
--secondary: 0 0% 6%;
--secondary-foreground: 0 0% 98%;
--muted:217.2 32.6% 17.5%;
--muted-foreground:215 20.2% 65.1%;
--muted: 0 0% 9%;
--muted-foreground: 0 0% 49%;
--accent:217.2 32.6% 17.5%;
--accent-foreground:210 40% 98%;
--accent: 0 0% 6%;
--accent-foreground: 0 0% 98%;
--destructive:0 62.8% 30.6%;
--destructive-foreground:210 40% 98%;
--destructive: 0 84% 60%;
--destructive-foreground: 0 0% 98%;
--border:217.2 32.6% 17.5%;
--input:217.2 32.6% 17.5%;
--ring:224.3 76.3% 48%;
--border: 0 0% 11%;
--input: 0 0% 16%;
--ring: 187 71% 40%;
}
}
@layer base {
* {
@apply border-border;

View File

@ -33,6 +33,11 @@ effect(() => {
onDeactivated(() => {
window.removeEventListener('resize', listener)
})
defineExpose({
chart,
chartEle,
})
</script>
<template>

View File

@ -22,7 +22,7 @@ defineProps<{
</div>
</div>
</div>
<Iconify :icon="icon" class="absolute top-1/2 right-9 -translate-y-1/2 w-[45px] h-[45px]" />
<Iconify :icon="icon" class="absolute top-1/2 right-12 -translate-y-1/2 w-[45px] h-[45px]" />
<svg
viewBox="0 0 420 92" fill="none" class="absolute top-0 left-0 -z-[1]" xmlns="http://www.w3.org/2000/svg"
xmlns:anim="http://www.w3.org/2000/anim" anim="" anim:transform-origin="50% 50%" anim:duration="1"

View File

@ -7,16 +7,16 @@ defineProps<{
</script>
<template>
<div class="w-[96px] h-[66px] relative">
<div class="w-[110px] h-[66px] relative">
<div class="space-y-3 ">
<div class="flex items-end gap-3">
<Iconify :icon="icon" class="w-[38px] h-[38px] " />
<Iconify :icon="icon" class="w-[38px] h-[38px] shrink-0 " />
<div class="text-[24px] text-shadow">
{{ value }}
</div>
</div>
<div>
<div class="text-[14px]">
{{ title }}
</div>
</div>

View File

@ -6,7 +6,7 @@ defineProps<{
<template>
<div class="w-[460px] h-fit relative my-2">
<div class="absolute z-10 text-2xl tracking-wide -top-1 left-14 text-cyan-200">
<div class="absolute z-10 text-3xl tracking-wide -top-[1px] left-14 text-cyan-200">
{{ title }}
</div>
<FrameV1HeaderSvg class="absolute " />

View File

@ -3,10 +3,10 @@
<Background class="z-10" />
<div class="absolute top-0 left-0 z-10 flex items-end h-full pl-[150px] gap-[100px] pb-[32px]">
<div class="pb-[12px]">
<h1 class="text-3xl font-bold">
<h1 class="text-4xl font-bold">
农村废弃物特征数据库
</h1>
<p class="opacity-60">
<p class="text-lg opacity-60">
Rural waste characteristics database
</p>
</div>

View File

@ -1,4 +1,4 @@
<script setup lang="ts">
<script setup lang="ts" generic="Task extends { [key: string]: any;}">
import type {
ColumnDef,
ColumnFiltersState,
@ -17,18 +17,8 @@ import {
} from '@tanstack/vue-table'
import { ref } from 'vue'
import type { Task } from '../data/schema'
import DataTablePagination from './DataTablePagination.vue'
import DataTableToolbar from './DataTableToolbar.vue'
import { valueUpdater } from '@/lib/utils'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/lib/registry/new-york/ui/table'
interface DataTableProps {
columns: ColumnDef<Task, any>[]
@ -51,10 +41,10 @@ const table = useVueTable({
get rowSelection() { return rowSelection.value },
},
enableRowSelection: true,
onSortingChange: updaterOrValue => valueUpdater(updaterOrValue, sorting),
onColumnFiltersChange: updaterOrValue => valueUpdater(updaterOrValue, columnFilters),
onColumnVisibilityChange: updaterOrValue => valueUpdater(updaterOrValue, columnVisibility),
onRowSelectionChange: updaterOrValue => valueUpdater(updaterOrValue, rowSelection),
onSortingChange: (updaterOrValue: any) => valueUpdater(updaterOrValue, sorting),
onColumnFiltersChange: (updaterOrValue: any) => valueUpdater(updaterOrValue, columnFilters),
onColumnVisibilityChange: (updaterOrValue: any) => valueUpdater(updaterOrValue, columnVisibility),
onRowSelectionChange: (updaterOrValue: any) => valueUpdater(updaterOrValue, rowSelection),
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
@ -66,8 +56,10 @@ const table = useVueTable({
<template>
<div class="space-y-4">
<DataTableToolbar :table="table" />
<div class="rounded-md border">
<DataTableToolbar :table="table">
<slot name="toolbar" />
</DataTableToolbar>
<div class="">
<Table>
<TableHeader>
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
@ -83,7 +75,7 @@ const table = useVueTable({
:key="row.id"
:data-state="row.getIsSelected() && 'selected'"
>
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id" class=" text-[16px]">
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
</TableCell>
</TableRow>
@ -103,4 +95,4 @@ const table = useVueTable({
<DataTablePagination :table="table" />
</div>
</template>
</template>(: any)(: any)

View File

@ -1,20 +1,5 @@
<script setup lang="ts">
<script setup lang="ts" generic="Task extends { [key: string]: any;}">
import type { Column } from '@tanstack/vue-table'
import type { Task } from '../data/schema'
import ArrowDownIcon from '~icons/radix-icons/arrow-down'
import ArrowUpIcon from '~icons/radix-icons/arrow-up'
import CaretSortIcon from '~icons/radix-icons/caret-sort'
import EyeNoneIcon from '~icons/radix-icons/eye-none'
import { cn } from '@/lib/utils'
import { Button } from '@/lib/registry/new-york/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/lib/registry/new-york/ui/dropdown-menu'
interface DataTableColumnHeaderProps {
column: Column<Task, any>
@ -40,23 +25,23 @@ export default {
class="-ml-3 h-8 data-[state=open]:bg-accent"
>
<span>{{ title }}</span>
<ArrowDownIcon v-if="column.getIsSorted() === 'desc'" class="ml-2 h-4 w-4" />
<ArrowUpIcon v-else-if=" column.getIsSorted() === 'asc'" class="ml-2 h-4 w-4" />
<CaretSortIcon v-else class="ml-2 h-4 w-4" />
<Iconify v-if="column.getIsSorted() === 'desc'" icon="solar:arrow-down-line-duotone" class="w-4 h-4 ml-2" />
<Iconify v-if="column.getIsSorted() === 'asc'" icon="solar:arrow-down-line-duotone" class="w-4 h-4 ml-2 rotate-180 " />
<Iconify v-else icon="solar:transfer-vertical-line-duotone" class="w-4 h-4 ml-2" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem @click="column.toggleSorting(false)">
<ArrowUpIcon class="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
<Iconify icon="solar:arrow-down-line-duotone" class=" rotate-180 mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Asc
</DropdownMenuItem>
<DropdownMenuItem @click="column.toggleSorting(true)">
<ArrowDownIcon class="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
<Iconify icon="solar:arrow-down-line-duotone" class="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Desc
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem @click="column.toggleVisibility(false)">
<EyeNoneIcon class="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
<Iconify icon="solar:eye-closed-bold-duotone" class="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Hide
</DropdownMenuItem>
</DropdownMenuContent>

View File

@ -1,22 +1,7 @@
<script setup lang="ts">
<script setup lang="ts" generic="Task extends { [key: string]: any;}">
import type { Column } from '@tanstack/vue-table'
import type { Component } from 'vue'
import { computed } from 'vue'
import type { Task } from '../data/schema'
import PlusCircledIcon from '~icons/radix-icons/plus-circled'
import CheckIcon from '~icons/radix-icons/check'
import { Badge } from '@/lib/registry/new-york/ui/badge'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@/lib/registry/new-york/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/new-york/ui/popover'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { cn } from '@/lib/utils'
interface DataTableFacetedFilter {
column?: Column<Task, any>
@ -38,13 +23,13 @@ const selectedValues = computed(() => new Set(props.column?.getFilterValue() as
<Popover>
<PopoverTrigger as-child>
<Button variant="outline" size="sm" class="h-8 border-dashed">
<PlusCircledIcon class="mr-2 h-4 w-4" />
<Iconify icon="solar:traffic-economy-line-duotone" class="w-4 h-4 mr-2" />
{{ title }}
<template v-if="selectedValues.size > 0">
<Separator orientation="vertical" class="mx-2 h-4" />
<Separator orientation="vertical" class="h-4 mx-2" />
<Badge
variant="secondary"
class="rounded-sm px-1 font-normal lg:hidden"
class="px-1 font-normal rounded-sm lg:hidden"
>
{{ selectedValues.size }}
</Badge>
@ -52,7 +37,7 @@ const selectedValues = computed(() => new Set(props.column?.getFilterValue() as
<Badge
v-if="selectedValues.size > 2"
variant="secondary"
class="rounded-sm px-1 font-normal"
class="px-1 font-normal rounded-sm"
>
{{ selectedValues.size }} selected
</Badge>
@ -63,7 +48,7 @@ const selectedValues = computed(() => new Set(props.column?.getFilterValue() as
.filter((option) => selectedValues.has(option.value))"
:key="option.value"
variant="secondary"
class="rounded-sm px-1 font-normal"
class="px-1 font-normal rounded-sm"
>
{{ option.label }}
</Badge>
@ -107,11 +92,11 @@ const selectedValues = computed(() => new Set(props.column?.getFilterValue() as
: 'opacity-50 [&_svg]:invisible',
)"
>
<CheckIcon :class="cn('h-4 w-4')" />
<Iconify icon="solar:unread-linear" :class="cn('h-4 w-4')" />
</div>
<component :is="option.icon" v-if="option.icon" class="mr-2 h-4 w-4 text-muted-foreground" />
<component :is="option.icon" v-if="option.icon" class="w-4 h-4 mr-2 text-muted-foreground" />
<span>{{ option.label }}</span>
<span v-if="facets?.get(option.value)" class="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
<span v-if="facets?.get(option.value)" class="flex items-center justify-center w-4 h-4 ml-auto font-mono text-xs">
{{ facets.get(option.value) }}
</span>
</CommandItem>

View File

@ -1,19 +1,5 @@
<script setup lang="ts">
<script setup lang="ts" generic="Task extends { [key: string]: any;}">
import type { Table } from '@tanstack/vue-table'
import type { Task } from '../data/schema'
import ChevronLeftIcon from '~icons/radix-icons/chevron-left'
import ChevronRightIcon from '~icons/radix-icons/chevron-right'
import DoubleArrowLeftIcon from '~icons/radix-icons/double-arrow-left'
import DoubleArrowRightIcon from '~icons/radix-icons/double-arrow-right'
import { Button } from '@/lib/registry/new-york/ui/button'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/lib/registry/new-york/ui/select'
interface DataTablePaginationProps {
table: Table<Task>
@ -53,39 +39,39 @@ defineProps<DataTablePaginationProps>()
<div class="flex items-center space-x-2">
<Button
variant="outline"
class="hidden h-8 w-8 p-0 lg:flex"
class="hidden w-8 h-8 p-0 lg:flex"
:disabled="!table.getCanPreviousPage()"
@click="table.setPageIndex(0)"
>
<span class="sr-only">Go to first page</span>
<DoubleArrowLeftIcon class="h-4 w-4" />
<Iconify icon="ph:caret-double-left-light" class="w-4 h-4" />
</Button>
<Button
variant="outline"
class="h-8 w-8 p-0"
class="w-8 h-8 p-0"
:disabled="!table.getCanPreviousPage()"
@click="table.previousPage()"
>
<span class="sr-only">Go to previous page</span>
<ChevronLeftIcon class="h-4 w-4" />
<Iconify icon="ph:caret-left" class="w-4 h-4" />
</Button>
<Button
variant="outline"
class="h-8 w-8 p-0"
class="w-8 h-8 p-0"
:disabled="!table.getCanNextPage()"
@click="table.nextPage()"
>
<span class="sr-only">Go to next page</span>
<ChevronRightIcon class="h-4 w-4" />
<Iconify icon="ph:caret-right" class="w-4 h-4" />
</Button>
<Button
variant="outline"
class="hidden h-8 w-8 p-0 lg:flex"
class="hidden w-8 h-8 p-0 lg:flex"
:disabled="!table.getCanNextPage()"
@click="table.setPageIndex(table.getPageCount() - 1)"
>
<span class="sr-only">Go to last page</span>
<DoubleArrowRightIcon class="h-4 w-4" />
<Iconify icon="ph:caret-double-right-light" class="w-4 h-4" />
</Button>
</div>
</div>

View File

@ -1,32 +1,21 @@
<script setup lang="ts">
<script setup lang="ts" generic="Task extends { [key: string]: any;}">
import type { Row } from '@tanstack/vue-table'
import { computed } from 'vue'
import { labels } from '../data/data'
import { taskSchema } from '../data/schema'
import type { Task } from '../data/schema'
import DotsHorizontalIcon from '~icons/radix-icons/dots-horizontal'
import { Button } from '@/lib/registry/new-york/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from '@/lib/registry/new-york/ui/dropdown-menu'
interface DataTableRowActionsProps {
row: Row<Task>
menus: {
label: string
divider?: boolean
icon?: string
click: (row: Row<Task>) => void
child?: {
label: string
icon?: string
click: (row: Row<Task>) => void
}[]
}[]
}
const props = defineProps<DataTableRowActionsProps>()
const task = computed(() => taskSchema.parse(props.row.original))
defineProps<DataTableRowActionsProps>()
</script>
<template>
@ -36,30 +25,31 @@ const task = computed(() => taskSchema.parse(props.row.original))
variant="ghost"
class="flex h-8 w-8 p-0 data-[state=open]:bg-muted"
>
<DotsHorizontalIcon class="h-4 w-4" />
<Iconify icon="ph:dots-three-outline-fill" class="w-4 h-4" />
<span class="sr-only">Open menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" class="w-[160px]">
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Make a copy</DropdownMenuItem>
<DropdownMenuItem>Favorite</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>Labels</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuRadioGroup :value="task.label">
<DropdownMenuRadioItem v-for="label in labels" :key="label.value" :value="label.value">
{{ label.label }}
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuSeparator />
<DropdownMenuItem>
Delete
<DropdownMenuShortcut></DropdownMenuShortcut>
</DropdownMenuItem>
<template v-for="menu in menus" :key="menu.label">
<DropdownMenuSeparator v-if="menu.divider" />
<DropdownMenuSub v-else-if="(menu?.child?.length ?? 0) > 0" :label="menu.label">
<DropdownMenuSubTrigger>{{ menu.label }}</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem v-for="child in menu.child" :key="child.label" @click="child.click(row)">
{{ child.label }}
<DropdownMenuShortcut>
<Iconify :icon="child.icon" class="size-4" />
</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuItem v-else @click="menu.click(row)">
{{ menu.label }}
<DropdownMenuShortcut>
<Iconify :icon="menu.icon" class="size-4" />
</DropdownMenuShortcut>
</DropdownMenuItem>
</template>
</DropdownMenuContent>
</DropdownMenu>
</template>

View File

@ -1,17 +1,21 @@
<script setup lang="ts">
<script setup lang="ts" generic="Task extends { [key: string]: any;}">
import type { Table } from '@tanstack/vue-table'
import { computed } from 'vue'
import type { Task } from '../data/schema'
import { priorities, statuses } from '../data/data'
import Iconify from '../iconify.vue'
import DataTableFacetedFilter from './DataTableFacetedFilter.vue'
import DataTableViewOptions from './DataTableViewOptions.vue'
import Cross2Icon from '~icons/radix-icons/cross-2'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Input } from '@/lib/registry/new-york/ui/input'
interface DataTableToolbarProps {
table: Table<Task>
filter?: {
label: string
key: keyof Task
options: {
label: string
value: string
icon?: string
}[]
}[]
}
const props = defineProps<DataTableToolbarProps>()
@ -20,37 +24,44 @@ const isFiltered = computed(() => props.table.getState().columnFilters.length >
</script>
<template>
<div class="flex items-center justify-between">
<div class="flex flex-1 items-center space-x-2">
<div>
<div class="flex justify-center items-center w-full py-10">
<Input
placeholder="Filter tasks..."
:model-value="(table.getColumn('title')?.getFilterValue() as string) ?? ''"
class="h-8 w-[150px] lg:w-[250px]"
@input="table.getColumn('title')?.setFilterValue($event.target.value)"
placeholder="搜索..."
:model-value="table.getColumn('title')?.getFilterValue() as string"
class="h-12 w-[550px]" @input="table.getColumn('title')?.setFilterValue($event.target.value)"
/>
<DataTableFacetedFilter
v-if="table.getColumn('status')"
:column="table.getColumn('status')"
title="Status"
:options="statuses"
/>
<DataTableFacetedFilter
v-if="table.getColumn('priority')"
:column="table.getColumn('priority')"
title="Priority"
:options="priorities"
/>
<Button
v-if="isFiltered"
variant="ghost"
class="h-8 px-2 lg:px-3"
@click="table.resetColumnFilters()"
>
Reset
<Cross2Icon class="ml-2 h-4 w-4" />
</Button>
</div>
<DataTableViewOptions :table="table" />
<div class="flex items-center justify-between">
<div class="flex items-center flex-1 space-x-2">
<DataTableFacetedFilter
v-for="(item, i) in filter"
:key="i"
:column="table.getColumn(item.key.toString())"
:title="item.label"
:options="item.options.map(i => ({
label: i.label,
value: i.value,
icon: h(Iconify, { icon: i.icon }),
}))"
/>
<div>
<Button
v-if="isFiltered"
variant="ghost"
class="h-8 px-2 lg:px-3"
@click="table.resetColumnFilters()"
>
Reset
<Iconify icon="ph:x" class="w-4 h-4 ml-2" />
</Button>
</div>
</div>
<div class="flex gap-2 justify-center items-center">
<slot />
<DataTableViewOptions :table="table" />
</div>
</div>
</div>
</template>

View File

@ -1,18 +1,6 @@
<script setup lang="ts">
<script setup lang="ts" generic="Task extends { [key: string]: any;}">
import type { Table } from '@tanstack/vue-table'
import { computed } from 'vue'
import type { Task } from '../data/schema'
import MixerHorizontalIcon from '~icons/radix-icons/mixer-horizontal'
import { Button } from '@/lib/registry/new-york/ui/button'
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/lib/registry/new-york/ui/dropdown-menu'
interface DataTableViewOptionsProps {
table: Table<Task>
@ -33,9 +21,9 @@ const columns = computed(() => props.table.getAllColumns()
<Button
variant="outline"
size="sm"
class="ml-auto hidden h-8 lg:flex"
class="hidden h-8 ml-auto lg:flex"
>
<MixerHorizontalIcon class="mr-2 h-4 w-4" />
<Iconify icon="ph:faders-horizontal-duotone" class="w-4 h-4 mr-2" />
View
</Button>
</DropdownMenuTrigger>

View File

@ -1,26 +1,10 @@
<script setup lang="ts">
import {
Avatar,
AvatarFallback,
AvatarImage,
} from '@/lib/registry/new-york/ui/avatar'
import { Button } from '@/lib/registry/new-york/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from '@/lib/registry/new-york/ui/dropdown-menu'
</script>
<template>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="ghost" class="relative h-8 w-8 rounded-full">
<Button variant="ghost" class="relative w-8 h-8 rounded-full">
<Avatar class="h-9 w-9">
<AvatarImage src="/avatars/03.png" alt="@shadcn" />
<AvatarFallback>SC</AvatarFallback>
@ -28,7 +12,7 @@ import {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent class="w-56" align="end">
<DropdownMenuLabel class="font-normal flex">
<DropdownMenuLabel class="flex font-normal">
<div class="flex flex-col space-y-1">
<p class="text-sm font-medium leading-none">
shadcn

View File

@ -0,0 +1,92 @@
<script setup lang="ts">
const props = defineProps<{
onSubmit: (data: ZodInfer<typeof schema>) => Promise<void>
}>()
const schema = z.object({
endPoint: z.string({
required_error: 'endpoint字段不能为空',
}),
accessKey: z.string({
required_error: 'accesskey字段不能为空',
}),
secretKey: z.string({
required_error: 'secretkey字段不能为空',
}),
port: z.number({
required_error: 'port字段不能为空',
}).optional(),
region: z.string().optional(),
useSSL: z.boolean(),
})
const formValues = {
endPoint: 'fgws3.jwanfs.ocloud.ihep.ac.cn',
accessKey: 'NsccczIb9DsAQ7RNMIkv',
secretKey: 'gBGR9UdhESQntF7wbMqB',
region: 'us-east-1',
useSSL: true,
}
const open = ref(false)
async function onSubmit(values: ZodInfer<typeof schema>) {
open.value = false
await props.onSubmit(values)
}
</script>
<template>
<Dialog v-model:open="open">
<DialogTrigger as-child>
<Button class="font-bold tracking-wide" size="xs">
添加资源化技术
</Button>
</DialogTrigger>
<DialogScrollContent class="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>资源化技术信息</DialogTitle>
<DialogDescription>
请填写所需要的资源化技术信息等
</DialogDescription>
</DialogHeader>
<Form class="space-y-4" :schema="schema" :initial-values="formValues" @submit="onSubmit">
<FormFieldItem v-slot="{ componentField }" label="EndPoint" name="endPoint" required>
<Input type="text" v-bind="componentField" placeholder="" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="AccessKey" name="accessKey" required>
<Input type="text" v-bind="componentField" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="SecretKey" name="secretKey" required>
<Input type="text" v-bind="componentField" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="端口" name="port">
<Input type="number" v-bind="componentField" />
</FormFieldItem>
<FormFieldItem name="useSSL">
<template #content="{ value, handleChange }">
<div class="col-span-1" />
<div class="flex col-span-3 gap-x-3">
<FormControl>
<Checkbox :checked="value" @update:checked="handleChange" />
</FormControl>
<div class="space-y-1 leading-none">
<FormLabel>useSSL</FormLabel>
<FormDescription>
是否启用安全HTTPS认证
</FormDescription>
<FormMessage />
</div>
</div>
</template>
</FormFieldItem>
<DialogFooter class="mt-4">
<Button type="submit" class="font-bold tracking-wide">
保存
</Button>
</DialogFooter>
</Form>
</DialogScrollContent>
</Dialog>
</template>

View File

@ -0,0 +1,77 @@
<script setup lang="ts">
const props = defineProps<{
env: ResourceTechnologyInfo['env']
}>()
const schema = z.object({
yieldDistance: z.number(),
area: z.number(),
terrain: z.string().min(1),
supplyWater: z.string().min(1),
supplyElectricity: z.string().min(1),
dischargeWater: z.string().min(1),
temperature: z.number(),
airPressure: z.number(),
})
const edit = ref(false)
const open = ref(false)
async function onSubmit(values: ZodInfer<typeof schema>) {
open.value = false
}
</script>
<template>
<Dialog v-model:open="open">
<DialogTrigger as-child>
<Button class="font-bold tracking-wide">
查看所需场地条件
</Button>
</DialogTrigger>
<DialogScrollContent class="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>所需场地条件</DialogTitle>
<DialogDescription>
通过点击编辑按钮可以编辑所需场地条件
</DialogDescription>
</DialogHeader>
<Form class="space-y-4" :schema="schema" :initial-values="env" @submit="onSubmit">
<FormFieldItem v-slot="{ componentField }" label="退让距离" name="yieldDistance" required>
<Input type="text" v-bind="componentField" placeholder="" :disabled="!edit" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="占地面积" name="area" required>
<Input type="text" v-bind="componentField" :disabled="!edit" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="地形" name="terrain" required>
<Input type="text" v-bind="componentField" :disabled="!edit" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="供水要求" name="supplyWater">
<Input type="text" v-bind="componentField" :disabled="!edit" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="排水要求" name="dischargeWater" required>
<Input type="text" v-bind="componentField" :disabled="!edit" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="供电要求" name="supplyElectricity" required>
<Input type="text" v-bind="componentField" :disabled="!edit" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="温度" name="temperature" required>
<Input type="text" v-bind="componentField" :disabled="!edit" />
</FormFieldItem>
<FormFieldItem v-slot="{ componentField }" label="气压" name="airPressure" required>
<Input type="text" v-bind="componentField" :disabled="!edit" />
</FormFieldItem>
<DialogFooter class="mt-4">
<Button type="button" class="font-bold tracking-wide" @click="edit = !edit">
{{ edit ? '取消编辑' : '编辑' }}
</Button>
<Button v-if="edit" type="submit" class="font-bold tracking-wide">
保存
</Button>
</DialogFooter>
</Form>
</DialogScrollContent>
</Dialog>
</template>

View File

@ -0,0 +1,27 @@
<script setup lang="ts">
defineProps<{
resource: ResourceTechnologyInfo
}>()
const open = ref(false)
</script>
<template>
<Dialog v-model:open="open">
<DialogTrigger as-child>
<img :src="resource.craftImage" class="size-[100px]">
</DialogTrigger>
<DialogScrollContent class="sm:max-w-[600px]">
<DialogHeader>
<DialogTitle>工艺流程</DialogTitle>
<DialogDescription>
工艺流程设计图
</DialogDescription>
</DialogHeader>
<div class="flex justify-center items-center w-full">
<img :src="resource.craftImage" class="size-[600px]">
</div>
</DialogScrollContent>
</Dialog>
</template>

View File

@ -0,0 +1,3 @@
<template>
test
</template>

View File

@ -1,12 +1,9 @@
<script setup lang="ts">
const props = defineProps<{
data: {
value: number
name: string
}[]
}>()
import type { EChartsType } from 'echarts'
const mapName = 'china'
const colorMode = useColorMode()
const info = useInfoStore()
const option = shallowRef({
tooltip: {
// show: false,
@ -23,50 +20,7 @@ const option = shallowRef({
left: '10%',
},
geo: [
{
map: mapName,
zlevel: -1,
zoom: 1.0,
silent: true,
layoutCenter: ['50%', '50%'],
layoutSize: '100%',
roam: false,
itemStyle: {
borderColor: 'rgba(192,245,249,.8)',
borderWidth: 3,
shadowColor: '#6FFDFF',
shadowOffsetY: 0,
shadowBlur: 10,
// areaColor: 'rgba(29,85,139,.6)',
},
regions: [
{
name: '南海诸岛',
itemStyle: {
//
opacity: 0, // 0
},
label: {
show: false, //
},
},
],
emphasis: {
//
label: {
show: false,
color: '#fff',
},
},
label: {
//
show: false, //
color: '#fff',
fontSize: 12,
fontFamily: 'Arial',
},
},
{
show: true,
map: mapName,
@ -103,7 +57,7 @@ const option = shallowRef({
colorStops: [
{
offset: 0.5,
color: '#0D59C1', // 0%
color: '#0D59C100', // 0%
},
{
offset: 1,
@ -130,155 +84,74 @@ const option = shallowRef({
areaColor: linearColor('#1cfbfe', '#3348e7'),
},
},
{
type: 'map',
map: mapName,
zlevel: -2,
zoom: 1.0,
regions: [
{
name: '南海诸岛',
itemStyle: {
//
opacity: 0, // 0
},
label: {
show: false, //
},
},
],
layoutCenter: ['50%', '51.4%'],
layoutSize: '100%',
roam: false,
silent: true,
itemStyle: {
// borderColor: "rgba(35, 161, 184,0.5)",
// shadowColor: "#193f6bcc",
// shadowOffsetY: 5,
// shadowBlur: 1,
areaColor: '#257AB2',
},
},
{
type: 'map',
map: mapName,
zlevel: -3,
zoom: 1.0,
regions: [
{
name: '南海诸岛',
itemStyle: {
//
opacity: 0, // 0
},
label: {
show: false, //
},
},
],
layoutCenter: ['50%', '52.4%'],
layoutSize: '100%',
roam: false,
silent: true,
itemStyle: {
borderColor: 'rgba(7, 65, 117,0.5)',
shadowColor: 'rgba(7, 65, 117,0.8)',
shadowOffsetY: 10,
shadowBlur: 0,
areaColor: '#0A2763',
},
},
],
series: [
// {
// type: 'effectScatter',
// coordinateSystem: 'geo',
// symbolSize: 4,
// itemStyle: {
// color: '#ffffff', //
// shadowBlur: 1,
// shadowColor: '#333',
// },
// tooltip: {
// trigger: 'item',
// backgroundColor: 'transparent',
// borderColor: 'transparent',
// extraCssText: 'z-index:100;color:#fff;',
// confine: true, // tooltip
// formatter(params: any) {
// //
// return `<div style="box-shadow: 0 0 10px #00000083; padding: 10px; position: absolute; top: 0; left:0; border-radius: 4px; border: 1px solid #0479ff; background: linear-gradient(to bottom, #0739a7 0%,#174ba0ef 100%);z-index:1000">
// <div style='color:#ee9803; font-size: 18px;'><b>${
// params.data.Province
// }-${params.data.City}</b></div>
{
type: 'effectScatter',
coordinateSystem: 'geo',
symbolSize: 4,
itemStyle: {
color: '#ffffff', //
shadowBlur: 1,
shadowColor: '#333',
},
tooltip: {
trigger: 'item',
backgroundColor: 'transparent',
borderColor: 'transparent',
extraCssText: 'z-index:100;color:#fff;',
confine: true, // tooltip
// ${
// params.data.DataCenterList.length
// ? '<div style=\'color:#eeae40; font-size: 14px;\'><b></b></div>'
// : ''
// }
formatter(params: any) {
console.log(params)
//
return `<div style="box-shadow: 0 0 10px #00000083; padding: 10px; position: absolute; top: 0; left:0; border-radius: 4px; border: 1px solid #0479ff; background: linear-gradient(to bottom, #0739a7 0%,#174ba0ef 100%);z-index:1000">
<div style='color:#ee9803; font-size: 18px;'><b>TEST</b></div>
</div>`
},
},
// ${params.data.DataCenterList.map((i: any) => {
// return `
// <div style="display: flex; align-items: center;padding-top: 6px;">
// <div style="height: 6px; width: 6px; border-radius: 50%; background:#F4BD59; margin-right: 10px;"></div> <span style='color:#fff;font-size: 13px;margin-right: 20px;'>${i.DataCenterName}</span>
// </div>
// `
// }).join('')}
label: {
show: true,
color: '#fff',
position: [10, 10],
textShadowColor: 'black',
textShadowBlur: 4,
formatter(obj: any) {
return obj.name
},
},
zlevel: 12,
data: info.data.map(i => ({
name: i.name,
value: i.value,
})),
},
{
type: 'effectScatter',
coordinateSystem: 'geo',
rippleEffect: {
brushType: 'fill',
},
// symbolSize(val: any) {
// const value = val[2]
// if (value < max)
// return 15
// ${
// params.data.CdnList.length
// ? '<div style=\'color:#eeae40; font-size: 14px;\'><b>CDN</b></div>'
// : ''
// }
// ${params.data.CdnList.map((i: any) => {
// return `
// <div style="display: flex; align-items: center;padding-top: 6px;">
// <div style="height: 6px; width: 6px; border-radius: 50%; background:#F4BD59; margin-right: 10px;"></div> <span style='color:#fff;font-size: 13px;margin-right: 20px;'>${i.CdnName}</span>
// </div>
// `
// }).join('')}
// </div>`
// },
// },
// label: {
// show: true,
// color: '#fff',
// position: [10, 10],
// textShadowColor: 'black',
// textShadowBlur: 4,
// formatter(obj: any) {
// return obj.name
// },
// },
// zlevel: 12,
// data: [...map.values()],
// },
// {
// type: 'effectScatter',
// coordinateSystem: 'geo',
// rippleEffect: {
// brushType: 'fill',
// },
// symbolSize(val: any) {
// const value = val[2]
// if (value < max)
// return 15
// return 20
// },
// showEffectOn: 'render', //
// itemStyle: {
// color: '#FEBE13', //
// shadowBlur: 10,
// shadowColor: '#333',
// },
// zlevel: 6,
// data: [...map.values()].filter(i => !!i.CdnList.length),
// },
// return 20
// },
showEffectOn: 'render', //
itemStyle: {
color: '#FEBE13', //
shadowBlur: 10,
shadowColor: '#333',
},
zlevel: 6,
data: info.data.map(i => ({
name: i.name,
value: i.value,
})),
},
// {
// type: 'custom',
// geoIndex: 0,
@ -310,12 +183,55 @@ const option = shallowRef({
// },
],
})
const china = ref<{
chart: EChartsType
chartEle: HTMLDivElement
}>()
let timer: any
onMounted(() => {
console.log(info.data[1].name)
const datalen = info.data.length
let count = 0
timer && clearInterval(timer)
timer = setInterval(() => {
console.log(china.value?.chart)
china.value?.chart.dispatchAction({
type: 'downplay',
})
china.value?.chart.dispatchAction({
type: 'highlight',
dataIndex: count,
})
china.value?.chart.dispatchAction({
type: 'showTip',
dataIndex: count,
})
count++
count = count % datalen
info.setShow(count)
}, 3000)
})
onDeactivated(() => {
clearInterval(timer)
})
effect(() => {
console.log(china.value?.chart)
})
</script>
<template>
<ClientOnly fallback-tag="div" fallback="Loading comments...">
<Chart
class="chart" :options="option"
ref="china" class="chart" :options="option"
/>
</ClientOnly>
</template>

View File

@ -1,96 +1,14 @@
<script setup lang="ts">
const invoices = [
{
invoice: 'INV001',
paymentStatus: 'Paid',
totalAmount: '$250.00',
paymentMethod: 'Credit Card',
},
{
invoice: 'INV002',
paymentStatus: 'Pending',
totalAmount: '$150.00',
paymentMethod: 'PayPal',
},
{
invoice: 'INV003',
paymentStatus: 'Unpaid',
totalAmount: '$350.00',
paymentMethod: 'Bank Transfer',
},
{
invoice: 'INV004',
paymentStatus: 'Paid',
totalAmount: '$450.00',
paymentMethod: 'Credit Card',
},
{
invoice: 'INV005',
paymentStatus: 'Paid',
totalAmount: '$550.00',
paymentMethod: 'PayPal',
},
{
invoice: 'INV006',
paymentStatus: 'Pending',
totalAmount: '$200.00',
paymentMethod: 'Bank Transfer',
},
{
invoice: 'INV007',
paymentStatus: 'Unpaid',
totalAmount: '$300.00',
paymentMethod: 'Credit Card',
},
]
const info = useInfoStore()
const data = computed(() => {
return info.data[info.showIndex]
})
</script>
<template>
<FrameV1 title="重点区域监控">
<div class="flex justify-around">
<FrameImg src="/social-card.png" title="测试测试士大夫" icon="solar:adhesive-plaster-line-duotone" class="h-[90px]" />
<FrameImg src="/social-card.png" title="测试测试士大夫" icon="solar:adhesive-plaster-line-duotone" class="h-[90px]" />
<div class="flex justify-around gap-4 pt-5">
<FrameImg v-for="item in data.info.monitor" :key="item.name" src="/social-card.png" :title="item.name" icon="solar:adhesive-plaster-line-duotone" class="h-[120px]" />
</div>
<Table class="mt-6">
<TableHeader class=" bg-white/5">
<TableRow>
<TableHead class="w-[100px] h-8">
Invoice
</TableHead>
<TableHead class="h-8 ">
Status
</TableHead>
<TableHead class="h-8 ">
Method
</TableHead>
<TableHead class="h-8 text-right">
Amount
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell class="font-medium">
INV001
</TableCell>
<TableCell>Paid</TableCell>
<TableCell>Credit Card</TableCell>
<TableCell class="text-right">
$250.00
</TableCell>
</TableRow>
<TableRow class="bg-red-500/20 hover:bg-red-500/25">
<TableCell class="font-medium">
INV001
</TableCell>
<TableCell>Paid</TableCell>
<TableCell>Credit Card</TableCell>
<TableCell class="text-right">
$250.00
</TableCell>
</TableRow>
</TableBody>
</Table>
</FrameV1>
</template>

View File

@ -1,52 +1,71 @@
<script setup lang="ts">
const list = [{
name: '园区名称',
value: 'xx科技园',
}, {
name: '简称年份',
value: '2021年',
}, {
name: '物业公司',
value: 'xx物业公司',
}, {
name: '建筑面积',
value: '10000平方米',
}, {
name: '占地面积',
value: '10000平方米',
}, {
name: '容积率',
value: '10000',
}]
const info = useInfoStore()
const digitals = [
{
title: '总面积(m2)',
value: 87512,
},
{
title: '楼栋数量',
value: 1000,
},
{
title: '办公人数',
value: 1000,
},
{
title: '总入住企业',
value: 1000,
},
{
title: '入住率',
value: 1000,
},
{
title: '总人数',
value: 1000,
},
]
const data = computed(() => {
return info.data[info.showIndex]
})
const digitalsV2 = [
const list = computed(() => {
return [{
name: '地区名称',
value: data.value.name,
}, {
name: '经纬度',
value: data.value.value.join(','),
}, {
name: '收集年份',
value: `${data.value.info.collect_year}`,
}, {
name: '经济面积',
value: `${data.value.info.economic.area}平方米`,
}, {
name: '人口总数',
value: data.value.info.economic.population,
}, {
name: '人均收入',
value: `${data.value.info.economic.revenue}元/年`,
}]
})
const digitals = computed(() => {
return [
{
title: '气温(℃)',
value: data.value.info.climate.temperature,
},
{
title: '降水量(cc)',
value: data.value.info.climate.precipitation,
},
{
title: '日照时长(小时)',
value: data.value.info.climate.sunshine,
},
{
title: '土壤种类',
value: data.value.info.soil.length,
},
{
title: '产业',
value: data.value.info.industry.length,
},
{
title: '资源化技术',
value: data.value.info.garbage_feature.data.length,
},
]
})
const digitalsV2 = computed(() => {
return data.value.info.garbage_feature.data.map(i => ({
title: i.type,
value: i.details.length,
unit: '项',
icon: i.icon,
}))
})
const i = [
{
title: '公寓',
value: 123,
@ -71,23 +90,11 @@ const digitalsV2 = [
unit: '加',
icon: 'solar:airbuds-remove-line-duotone',
},
{
title: '餐厅',
value: 123,
unit: '加',
icon: 'solar:airbuds-remove-line-duotone',
},
{
title: '休想学',
value: 123,
unit: '加',
icon: 'solar:airbuds-remove-line-duotone',
},
]
</script>
<template>
<FrameV1 title="区简介">
<FrameV1 title="地区简介">
<div class="">
<div class="grid grid-cols-2 gap-1 px-6">
<div v-for="(item, index) in list" :key="index" class="grid grid-cols-5 ">
@ -104,7 +111,7 @@ const digitalsV2 = [
<DigitalV1 v-for="(item, index) in digitals" :key="index" v-bind="item" />
</div>
<div class="flex items-center justify-center mt-6 mb-4">
<TitleV1 title="园区环境" />
<TitleV1 title="地区废弃物特征库" />
</div>
<div class="w-full">

View File

@ -1,14 +1,15 @@
<script setup lang="ts">
const items = [{
title: '人员统计',
value: 'people',
title: '土壤统计',
value: 'soil',
}, {
title: '设备统计',
value: 'equipment',
}, {
title: '能耗统计',
value: 'energy',
title: '产业统计',
value: 'industry',
}]
const info = useInfoStore()
const data = computed(() => {
return info.data[info.showIndex]
})
const selected = ref(0)
@ -16,33 +17,110 @@ function change(value: number) {
selected.value = value
}
const digital = [
{
title: '员工',
value: 245,
icon: 'solar:adhesive-plaster-line-duotone',
},
{
title: '安保',
value: 245,
icon: 'solar:adhesive-plaster-line-duotone',
},
{
title: '访客',
value: 245,
icon: 'solar:adhesive-plaster-line-duotone',
},
]
const count = computed(() => {
return selected ? data.value.info.industry.length : data.value.info.soil.length
})
const value = computed(() => {
if (selected.value === 0) {
return {
count: data.value.info.soil.length,
unit: '种',
icon: 'solar:adhesive-plaster-line-duotone',
title: '区域内现土壤总计',
details: [{
title: '养分特征数量',
value: data.value.info.soil.reduce((a, b) => { return a + b.nutrient.length }, 0),
icon: 'solar:atom-bold-duotone',
}, {
title: '土壤环境数量',
value: data.value.info.soil.reduce((a, b) => { return a + b.env.length }, 0),
icon: 'solar:bacteria-bold-duotone',
}, {
title: '微生物数量共计',
value: data.value.info.soil.reduce((a, b) => { return a + b.microorganism_count }, 0),
icon: 'solar:bug-bold-duotone',
}],
table: {
columns: [{
title: '类型',
key: 'type',
}, {
title: '土壤养分特征',
key: 'nutrient',
}, {
title: '土壤环境',
key: 'env',
}, {
title: '微生物数量',
key: 'microorganism_count',
}],
list: data.value.info.soil,
},
}
}
else {
return {
count: data.value.info.industry.length,
unit: '个',
icon: 'solar:buildings-broken',
title: '区域内现产业总计',
details: [{
title: '农作物共计',
value: data.value.info.industry.reduce((a, b) => { return a + b.crop.length }, 0),
icon: 'solar:floor-lamp-bold-duotone',
}, {
title: '畜产品共计',
value: data.value.info.industry.reduce((a, b) => { return a + b.livestock.length }, 0),
icon: 'solar:dumbbells-bold-duotone',
}, {
title: '产业结构共计',
value: new Set(data.value.info.industry.map(i => i.construction)).size,
icon: 'solar:buildings-bold-duotone',
}],
table: {
columns: [{
title: '名称',
key: 'name',
}, {
title: '产业结构',
key: 'construction',
}, {
title: '高值化需求',
key: 'premium_requirement',
}],
list: data.value.info.industry,
},
}
}
})
</script>
<template>
<FrameV1 title="人测统计">
<div class="space-y-4 ">
<FrameV1 title="概况统计">
<div class="space-y-6 ">
<TabsV2 v-model:model-value="selected" :items="items" :selected="selected" :change="change" />
<DigitalV4 title="园区现有总人数" :value="4555" unit="人" icon="solar:adhesive-plaster-line-duotone" />
<DigitalV4 :title="value.title" :value="value.count" :unit="value.unit" :icon="value.icon" />
<div class="flex justify-between">
<DigitalV5 v-for="item in digital" :key="item.title" v-bind="item" />
<DigitalV5 v-for="item in value.details" :key="item.title" v-bind="item" />
</div>
</div>
<Table class="mt-6">
<TableHeader class=" bg-white/5">
<TableRow>
<TableHead v-for="(item, i) in value.table?.columns" :key="item.key" class="h-8" :class="{ 'text-right': i === value.table.columns.length - 1 }">
{{ item.title }}
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow v-for="item in value.table?.list" :key="item.type">
<TableCell v-for="(col, i) in value.table?.columns" :key="col.key" class="text-lg font-medium" :class="{ 'text-right': i === value.table.columns.length - 1 }">
{{ Array.isArray(item[col.key]) ? item[col.key].join(', ') : item[col.key] }}
</TableCell>
</TableRow>
</TableBody>
</Table>
</FrameV1>
</template>

View File

@ -1,80 +0,0 @@
export const useFileStore = defineStore('files', {
state: (): {
pendings: {
path: string
file: File
}[],
success: {
path: string
file: File
}[],
error: {
path: string
file: File
}[],
uploading: {
path: string
file: File
}[]
isFilesSiderOpen: Ref<boolean>
} => ({
pendings: [],
success: [],
error: [],
uploading: [],
isFilesSiderOpen: ref(false)
}),
actions: {
pendingFiles(files: File[], path: string) {
files.forEach(f => {
this.pendings.push({ file: f, path })
})
},
removePendingFile(file: File) {
const index = this.pendings.findIndex(f => f.file.name === file.name)
if (index !== -1) {
this.pendings.splice(index, 1)
}
},
removeUploadingFile(file: File) {
const index = this.uploading.findIndex(f => f.file.name === file.name)
if (index !== -1) {
this.uploading.splice(index, 1)
}
},
uploadFileSuccess(file: File, path: string) {
this.removeUploadingFile(file)
this.success.push({ file, path })
},
uploadFileError(file: File, path: string) {
this.removeUploadingFile(file)
this.error.push({ file, path })
},
uploadFile(): { file: File, path: string } | undefined {
const f = this.pendings.shift()
f && this.uploading.push(f)
return f
},
recoverErrorFile(file: File, path: string) {
const index = this.error.findIndex(f => f.file.name === file.name)
if (index !== -1) {
this.error.splice(index, 1)
}
this.pendings.push({ file, path })
}
},
// persist: {
// storage: persistedState.sessionStorage
// }
})
// @ts-ignore
if (import.meta.hot)
// @ts-ignore
import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))

View File

@ -0,0 +1,338 @@
export const useInfoStore = defineStore('info', {
state: (): {
data: {
name: string
value: number[]
info: {
city: string
collect_year: number
economic: {
area: number
population: number
revenue: number
}
climate: {
temperature: number
precipitation: number
sunshine: number
}
monitor: {
name: string
value: number[]
link: string
}[]
soil: {
type: string
nutrient: string[]
env: string[]
organic_carbon: boolean
microorganism_count: number
gene: string
}[]
industry: {
name: string
value: number[]
construction: string
premium_requirement: string
crop: {
name: string
type: string
area: number
yield: number
}[]
livestock: {
type: string
name: string
count: number
}[]
}[]
garbage_feature: {
collect_year: number
data: {
type: string
icon: string
details: {
type: string
count: number
quotient: {
water: number
organic_material: number
carbon: number
nitrogen: number
kalium: number
heavy_metal: number
}
physical: {
unit_weight: number
porosity: number
pHvalue: number
}
biochemical: string
energy: string
dispose_way: string
}[]
}[]
}
}
}[]
showIndex: number
} => ({
showIndex: 0,
data: [{
name: '北京',
value: [...geoCoordMap['北京']],
info: {
city: '北京',
collect_year: 2024,
monitor: [{
name: 'xx检测站',
value: [222, 222],
link: 'http://www.baidu.com',
}, {
name: 'yy检测站',
value: [222, 222],
link: 'http://www.baidu.com',
}],
economic: {
area: 1000,
population: 1000,
revenue: 2000,
},
climate: {
temperature: 26,
precipitation: 400,
sunshine: 4050,
},
soil: [{
type: 'xx土壤',
nutrient: ['xx', 'yy', 'zz'],
env: ['丘陵'],
organic_carbon: false,
microorganism_count: 10000,
gene: '未知',
}, {
type: 'yy土壤',
nutrient: ['xx', 'yy', 'zz'],
env: ['盆地'],
organic_carbon: true,
microorganism_count: 2000,
gene: '未知',
}],
industry: [{
name: 'xx企业',
value: [222, 222],
construction: '产业结构',
premium_requirement: '什么什么什么什么什么什么什么什么什么',
crop: [{
name: 'test',
type: 'test',
area: 1000,
yield: 120000990,
}],
livestock: [{
type: 'test2',
name: 'test2',
count: 123123451,
}],
}],
garbage_feature: {
collect_year: 2012,
data: [{
type: '厨余垃圾',
icon: 'solar:airbuds-remove-line-duotone',
details: [{
type: '香蕉皮',
count: 12312312,
quotient: {
water: 12,
organic_material: 23,
carbon: 35,
nitrogen: 21,
kalium: 1,
heavy_metal: 23,
},
physical: {
unit_weight: 12,
porosity: 23,
pHvalue: 213,
},
biochemical: '什么什么升华特性',
energy: '什么什么能量特征',
dispose_way: '怎么怎么怎么怎么怎么去处理',
}, {
type: '西瓜皮',
count: 12312,
quotient: {
water: 2,
organic_material: 3,
carbon: 32,
nitrogen: 41,
kalium: 12,
heavy_metal: 3,
},
physical: {
unit_weight: 1,
porosity: 3,
pHvalue: 23,
},
biochemical: '什么什么升华特性',
energy: '什么什么能量特征',
dispose_way: '怎么怎么怎么怎么怎么去处理',
}],
}, {
type: '非集约化禽畜粪便',
icon: 'solar:airbuds-right-line-duotone',
details: [],
}, {
type: '田园尾菜桔秆',
icon: 'solar:airbuds-case-open-line-duotone',
details: [],
}],
},
},
}, {
name: '成都',
value: [...geoCoordMap['成都']],
info: {
city: '成都',
collect_year: 2024,
economic: {
area: 200,
population: 400,
revenue: 2450,
},
climate: {
temperature: 24,
precipitation: 700,
sunshine: 3050,
},
monitor: [{
name: '成都xx检测站',
value: [222, 222],
link: 'http://www.baidu.com',
}, {
name: '吃的yy检测站',
value: [222, 222],
link: 'http://www.baidu.com',
}],
soil: [{
type: 'aa土壤',
nutrient: ['aa', 'yy', 'zz', 'sdf'],
env: ['丘陵'],
organic_carbon: false,
microorganism_count: 1000,
gene: '未知',
}, {
type: 'bb土壤',
nutrient: ['xa', 'dy', 'gz'],
env: ['盆地', '平原'],
organic_carbon: true,
microorganism_count: 2000,
gene: '未知',
}],
industry: [{
name: 'yy企业',
value: [100, 100],
construction: '产业结构',
premium_requirement: '什么什么什么什么什么什么什么什么什么',
crop: [{
name: 'test',
type: 'test',
area: 1000,
yield: 120000990,
}],
livestock: [{
type: 'test2',
name: 'test2',
count: 123123451,
}],
}],
garbage_feature: {
collect_year: 2012,
data: [{
type: '厨余垃圾',
icon: 'solar:airbuds-remove-line-duotone',
details: [{
type: '香蕉皮',
count: 12312312,
quotient: {
water: 12,
organic_material: 23,
carbon: 35,
nitrogen: 21,
kalium: 1,
heavy_metal: 23,
},
physical: {
unit_weight: 12,
porosity: 23,
pHvalue: 213,
},
biochemical: '什么什么升华特性',
energy: '什么什么能量特征',
dispose_way: '怎么怎么怎么怎么怎么去处理',
}, {
type: '西瓜皮',
count: 12312,
quotient: {
water: 2,
organic_material: 3,
carbon: 32,
nitrogen: 41,
kalium: 12,
heavy_metal: 3,
},
physical: {
unit_weight: 1,
porosity: 3,
pHvalue: 23,
},
biochemical: '什么什么升华特性',
energy: '什么什么能量特征',
dispose_way: '怎么怎么怎么怎么怎么去处理',
}],
}, {
type: '非集约化禽畜粪便',
icon: 'solar:airbuds-right-line-duotone',
details: [{
type: '猪粪',
count: 12312,
quotient: {
water: 2,
organic_material: 3,
carbon: 3,
nitrogen: 21,
kalium: 12,
heavy_metal: 3,
},
physical: {
unit_weight: 122,
porosity: 2,
pHvalue: 2123,
},
biochemical: '什么什么升华特性',
energy: '什么什么能量特征',
dispose_way: '怎么怎么怎么怎么怎么去处理',
}],
}, {
type: '田园尾菜桔秆',
icon: 'solar:airbuds-case-open-line-duotone',
details: [],
}],
},
},
}],
}),
actions: {
setShow(index: number) {
this.showIndex = index
},
},
// persist: {
// storage: persistedState.sessionStorage,
// },
})
if (import.meta.hot)
import.meta.hot.accept(acceptHMRUpdate(useInfoStore, import.meta.hot))

View File

@ -1,54 +0,0 @@
export interface Notice {
state: "loading" | "pending" | "error" | "success"
title: string
desc: string
time: Date
}
export const useNoticeStore = defineStore('notice', {
state: (): {
notices: Notice[]
} => ({
notices: [],
}),
actions: {
update(title: string, desc: string, state: "loading" | "pending" | "error" | "success") {
const index = this.notices.findIndex((i) => i.title === title)
if (index !== -1) {
this.notices.splice(index, 1, { state: "loading", title, desc, time: new Date() })
}
},
noticePending(title: string, desc: string) {
this.notices.push({ state: "pending", title, desc, time: new Date() })
},
async filesPending(path: string, files: File[]) {
files.map((file) => {
this.noticePending(file.name, `${merticKB(file.size)}${file.type}`)
})
},
// async filesUpload() {
// const res = await bucketApi.detail.uploadUrl({ Path: path })
// for (const file of Array.from(files)) {
// try {
// this.update(file.name, `${merticKB(file.size)}${file.type},文件上传中...`, 'loading')
// await bucketApi.detail.upload(file, res.url, res.token)
// } catch (e: any) {
// // 单个文件上传失败报错信息
// this.update(file.name, `${merticKB(file.size)}${file.type},文件上传失败!`, 'error')
// }
// }
// }
},
persist: {
storage: persistedState.sessionStorage
}
})
// @ts-ignore
if (import.meta.hot)
// @ts-ignore
import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))

View File

@ -0,0 +1,50 @@
export const usePolicyStore = defineStore('policy', {
state: (): {
policy: {
name: string
description: string
url: string
type: '国家' | '地方' | '企业'
}[]
} => ({
policy: [
{
name: '政策1',
url: 'https://www.baidu.com/policy1',
description: '这是政策1的描述',
type: '国家',
},
{
name: '政策2',
url: 'https://www.baidu.com/policy1',
description: '这是政策1的描述',
type: '国家',
},
{
name: '政策3',
url: 'https://www.baidu.com/policy1',
description: '这是政策1的描述',
type: '国家',
},
{
name: '政策2',
url: 'https://www.example.com/policy2',
description: '这是政策2的描述',
type: '企业',
},
{
name: '政策3',
url: 'https://www.example.com/policy3',
description: '这是政策3的描述',
type: '地方',
},
],
}),
persist: {
storage: persistedState.sessionStorage,
},
})
if (import.meta.hot)
import.meta.hot.accept(acceptHMRUpdate(usePolicyStore, import.meta.hot))

View File

@ -1,24 +1,232 @@
import type { UserInfo } from '~/api/user'
export const useResourceStore = defineStore('user', {
state: (): {
search:string
searchThrottled:Ref<string>
} => ({
search:'',
searchThrottled:refThrottled(ref(''),300)
}),
actions: {
},
persist: {
storage: persistedState.sessionStorage
export interface ResourceTechnologyInfo {
properThing: string[]
craftImage: string
device: string
env: {
yieldDistance: number
area: number
terrain: string
supplyWater: string
supplyElectricity: string
dischargeWater: string
temperature: number
airPressure: number
// ...
}
production: string[]
discharge: {
effluent: string[]
sludge: string[]
gas: string[]
}
work: string
cost: number
efficiency: string
utilization: string
}
export const useResourceStore = defineStore('resource', {
state: (): {
resources: ResourceTechnologyInfo[]
policy: {
name: string
description: string
url: string
type: '国家' | '地方' | '企业'
}[]
} => ({
resources: [{
properThing: ['易拉罐', '垃圾箱'],
craftImage: 'https://pic1.zhimg.com/v2-f24231dafa70ba95a2f84262471dadfc_r.jpg',
device: '垃圾箱',
env: {
yieldDistance: 110,
area: 4042,
terrain: '丘陵',
supplyWater: '什么什么供水要求',
supplyElectricity: '什么什么供电要求',
dischargeWater: '什么什么排水要求',
temperature: 29,
airPressure: 1000,
// ...
},
production: ['塑料', '金属'],
discharge: {
effluent: ['水', '污水'],
sludge: ['污泥', 'xx污泥'],
gas: ['氢', '甲烷', '乙烷'],
},
work: '6名工人',
cost: 222450,
efficiency: '100%',
utilization: '高效',
},
// 帮我再编写5条类似数据
{
properThing: ['玩具', '塑料袋'],
craftImage: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.BsxwuPonB9eNr4ti6SnbnAHaGV?pid=ImgDet&w=474&h=405&rs=1',
device: '垃圾管理系统',
env: {
yieldDistance: 140,
area: 2800,
terrain: '湿地',
supplyWater: '湖水',
supplyElectricity: '水力发电',
dischargeWater: '湖泊',
temperature: 27,
airPressure: 1002,
},
production: ['塑料', '再生材料'],
discharge: {
effluent: ['废水'],
sludge: ['废渣'],
gas: ['氧气', '氮气'],
},
work: '4名工人',
cost: 160000,
efficiency: '92%',
utilization: '高效',
},
{
properThing: ['纸杯', '食品包装'],
craftImage: 'https://img.zcool.cn/community/019559594098fca8012193a303c702.png@2o.png',
device: '垃圾处理装置',
env: {
yieldDistance: 130,
area: 3200,
terrain: '山区',
supplyWater: '地下水',
supplyElectricity: '生物质能',
dischargeWater: '河流',
temperature: 26,
airPressure: 1008,
},
production: ['纸张', '有机肥料'],
discharge: {
effluent: ['废水'],
sludge: ['有机废物'],
gas: ['氧气', '甲烷'],
},
work: '5名工人',
cost: 170000,
efficiency: '88%',
utilization: '中等',
},
{
properThing: ['食品包装', '塑料袋'],
craftImage: 'https://www.yuaoq.com/template/draw/uncategorized/10378/10378_template.png',
device: '垃圾处理机',
env: {
yieldDistance: 150,
area: 3500,
terrain: '沿海',
supplyWater: '海水',
supplyElectricity: '风力发电',
dischargeWater: '海洋',
temperature: 28,
airPressure: 1003,
},
production: ['塑料', '有机肥料'],
discharge: {
effluent: ['废水'],
sludge: ['有机废物'],
gas: ['二氧化碳', '氢气'],
},
work: '6名工人',
cost: 200000,
efficiency: '95%',
utilization: '高效',
},
{
properThing: ['玻璃瓶', '金属罐'],
craftImage: 'https://ts1.cn.mm.bing.net/th/id/R-C.ea57da618322715a29309637a45aa83b?rik=rvuZvWzTb2t%2bFA&riu=http%3a%2f%2fdocs-aliyun.cn-hangzhou.oss.aliyun-inc.com%2fassets%2fpic%2f117771%2fcn_zh%2f1557311249692%2f%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7+2019-05-08+%E4%B8%8B%E5%8D%886.26.51.png&ehk=AEDi9kQLAwki5lezNmCNfa0H4cHaLsSAyiNrcKmTr9k%3d&risl=&pid=ImgRaw&r=0',
device: '垃圾回收机',
env: {
yieldDistance: 100,
area: 2500,
terrain: '城市',
supplyWater: '循环水',
supplyElectricity: '太阳能',
dischargeWater: '雨水管道',
temperature: 30,
airPressure: 1005,
},
production: ['玻璃', '金属'],
discharge: {
effluent: ['废水'],
sludge: ['废渣'],
gas: ['氧气', '氮气'],
},
work: '5名工人',
cost: 180000,
efficiency: '85%',
utilization: '高效',
},
{
properThing: ['纸箱', '塑料瓶'],
craftImage: 'https://www.yuaoq.com/template/draw/uncategorized/11562/11562_template.png',
device: '垃圾桶',
env: {
yieldDistance: 120,
area: 3000,
terrain: '平原',
supplyWater: '自来水',
supplyElectricity: '市电',
dischargeWater: '污水处理厂',
temperature: 25,
airPressure: 1010,
},
production: ['塑料', '纸张'],
discharge: {
effluent: ['废水'],
sludge: ['污泥'],
gas: ['二氧化碳', '氧气'],
},
work: '4名工人',
cost: 150000,
efficiency: '90%',
utilization: '中等',
},
],
policy: [
{
name: '政策1',
url: 'https://www.baidu.com/policy1',
description: '这是政策1的描述',
type: '国家',
},
{
name: '政策2',
url: 'https://www.baidu.com/policy1',
description: '这是政策1的描述',
type: '国家',
},
{
name: '政策3',
url: 'https://www.baidu.com/policy1',
description: '这是政策1的描述',
type: '国家',
},
{
name: '政策2',
url: 'https://www.example.com/policy2',
description: '这是政策2的描述',
type: '企业',
},
{
name: '政策3',
url: 'https://www.example.com/policy3',
description: '这是政策3的描述',
type: '地方',
},
],
}),
persist: {
storage: persistedState.sessionStorage,
},
})
// @ts-ignore
if (import.meta.hot)
// @ts-ignore
import.meta.hot.accept(acceptHMRUpdate(useResourceStore, import.meta.hot))

View File

@ -11,8 +11,6 @@
<slot />
</main>
<BackgroundLeft class="absolute left-0 h-full select-none " />
<BackgroundLeft class="absolute right-0 rotate-180 scale-x-150 h-full select-none " />
<!-- <BackgroundRight class="absolute right-0 h-full select-none" /> -->
</div>
</template>

View File

@ -1,24 +1,28 @@
<script setup lang="ts">
const digitalsV2 = [
{
title: 'string',
value: 123,
icon: 'solar:airbuds-remove-line-duotone',
title: '资源化技术',
value: 1231,
icon: 'solar:bonfire-linear',
to: '/resource_technology',
},
{
title: 'string',
title: '政策',
value: 123,
icon: 'solar:airbuds-remove-line-duotone',
icon: 'solar:buildings-2-broken',
to: '/policy',
},
{
title: 'string',
title: '新闻',
value: 123,
icon: 'solar:airbuds-remove-line-duotone',
icon: 'solar:cart-3-broken',
to: '/news',
},
{
title: 'string',
title: '标准',
value: 123,
icon: 'solar:airbuds-remove-line-duotone',
icon: 'solar:calculator-line-duotone',
to: '/standard',
},
]
@ -32,16 +36,19 @@ function change(value: number) {
<template>
<div class="fixed bottom-0 left-0 flex flex-col justify-between h-full pt-[170px] z-10 p-14 ">
<div class="space-y-6 ">
<DigitalV3 v-for="(item, index) in digitalsV2" :key="index" v-bind="item" />
<NuxtLink v-for="(item, index) in digitalsV2" :key="index" class="block" :to="item.to">
<DigitalV3 v-bind="item" />
</NuxtLink>
</div>
<Warning :count="35" />
</div>
<div class="fixed bottom-0 right-0 flex h-full gap-6 pt-[150px] z-10 p-6">
<div class="fixed bottom-0 right-0 flex h-full gap-6 pt-[150px] z-10 p-6 pr-14 pb-14">
<div class="flex flex-col justify-between">
<SectionOne />
<SectionTwo />
<SectionFour />
<SectionFive />
<!-- <SectionFour /> -->
</div>
<!-- <div class="flex flex-col justify-between">
<SectionTwo />
@ -49,7 +56,9 @@ function change(value: number) {
<SectionFive />
</div> -->
</div>
<div class="fixed top-0 left-0 w-full h-full bg-black/50">
<div class="fixed top-0 left-0 w-full h-full ">
<SectionChinaMap />
</div>
<BackgroundLeft class="absolute top-0 left-0 h-full select-none -z-[1]" />
<BackgroundLeft class="absolute top-0 right-0 h-full scale-x-150 rotate-180 select-none -z-[1]" />
</template>

66
apps/web/pages/news.vue Normal file
View File

@ -0,0 +1,66 @@
<script setup lang="tsx">
const data = [
{
title: '农村有机物',
description: '有关于农村有机物的新闻',
image: 'https://picsum.photos/id/10/640/360',
},
{
title: '农村有机肥',
description: '有关于农村有机肥的新闻',
image: 'https://picsum.photos/id/13/640/360',
},
{
title: '农村化肥',
description: '有关于农村化肥的新闻',
image: 'https://picsum.photos/id/12/640/360',
},
{
title: '农村农药',
description: '有关于农村农药的新闻',
image: 'https://picsum.photos/id/11/640/360',
},
{
title: '农村种子',
description: '有关于农村种子的新闻',
image: 'https://picsum.photos/id/14/640/360',
},
]
</script>
<template>
<section class="">
<div class="max-w-screen-xl px-4 py-8 mx-auto lg:py-16 lg:px-6">
<div class="max-w-screen-lg text-gray-500 sm:text-lg dark:text-gray-400">
<h2 class="mb-4 text-4xl font-bold tracking-tight text-gray-900 dark:text-white">
有关于农村废弃物特征的诸多诸多新闻
</h2>
<p class="mb-4 font-light">
远程办理智慧农业直播带货如今的乡村变得越来越硬核手机成为了新农具网络成为了新农资直播成为了新农活数字技术在为经济增长提供强大动力的同时也正以多种形式助力乡村振兴
</p>
<p class="mb-4 font-medium">
2018中共中央国务院印发的乡村振兴战略规划2018-2022指出大力发展数字农业实施智慧农业工程和互联网+现代农业行动建设具有广泛性的农村电子商务发展基础设施加快建立健全适应农产品电商发展的标准体系等
<br>在政策的指引下近年来浙江江苏山东福建等地一批批的专业淘宝村纷纷涌现在浙江丽水通过一系列政策保障以农村青年大学毕业生为主要服务群体以优质农特产品网上销售为工作重点的农村电子商务这几年如雨后春笋般冒了出来
</p>
<a href="#" class="inline-flex items-center font-medium text-primary-600 hover:text-primary-800 dark:text-primary-500 dark:hover:text-primary-700">
查看更多
<svg class="w-6 h-6 ml-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" /></svg>
</a>
</div>
</div>
</section>
<div class="grid grid-cols-2 gap-4 pt-10 mx-auto px-[500px] md:grid-cols-3 lg:grid-cols-4">
<Card v-for="item in data" :key="item.title" class="overflow-hidden">
<CardContent class="p-0">
<img :src="item.image" alt="news" class="w-full">
</CardContent>
<CardHeader>
<CardTitle>{{ item.title }}</CardTitle>
<CardDescription>{{ item.description }}</CardDescription>
</CardHeader>
</Card>
</div>
</template>

View File

@ -0,0 +1,3 @@
<template>
<NuxtPage />
</template>

View File

@ -0,0 +1,80 @@
<script setup lang="tsx">
import type { ColumnDef } from '@tanstack/vue-table'
import DataTable from '~/components/common/table/DataTable.vue'
import DataTableColumnHeader from '~/components/common/table/DataTableColumnHeader.vue'
import DataTableRowActions from '~/components/common/table/DataTableRowActions.vue'
const router = useRoute()
const resource = usePolicyStore()
type Task = typeof resource.policy[0]
console.log(router.params.name)
const columns: ColumnDef<Task, any>[] = [
{
accessorKey: 'name',
header: ({ column }) =>
<DataTableColumnHeader column={column} title="政策名称" />,
cell: ({ row }) => <div class='w-26'>{row.original.name}</div>,
enableSorting: false,
enableHiding: false,
},
{
accessorKey: 'description',
header: ({ column }) =>
<DataTableColumnHeader column={column} title="描述" />,
cell: ({ row }) => <div class='w-26'>{row.original.description}</div>,
},
{
accessorKey: 'url',
header: ({ column }) =>
<DataTableColumnHeader column={column} title="地址" />,
cell: ({ row }) => <a href={row.original.url} target='_blank' class='w-26 text-primary '>{row.original.url}</a>,
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
id: 'actions',
cell: ({ row }) => (
<DataTableRowActions row={row} menus={[{
label: '修改',
icon: 'tabler:edit',
click: () => {
console.log('edit', row.original)
},
}, {
label: '删除',
icon: 'tabler:trash',
click: () => {
console.log('delete', row.original)
},
}]}/>
),
},
]
onMounted(() => {
console.log(resource.policy)
console.log(resource.policy.filter(i => i.type === router.params.name))
})
</script>
<template>
<div class="p-4 mt-10 mx-autoto ">
<div class="flex justify-center">
<h1 class="text-3xl font-bold mb-4 text-center">
{{ router.params.name }}政策
</h1>
</div>
<DataTable :data="resource.policy.filter(i => i.type === router.params.name)" :columns="columns" class="px-10">
<template #toolbar>
<Button size="xs">
添加
</Button>
</template>
</DataTable>
</div>
</template>

View File

@ -0,0 +1,43 @@
<script setup lang="tsx">
</script>
<template>
<div class="w-full flex justify-center gap-10 my-[300px]">
<NuxtLink to="/policy/国家">
<Card class="overflow-hidden w-[300px] h-[500px] flex flex-col ">
<CardContent class="p-0 flex-1">
<img src="https://r1.visitbeijing.com.cn/vbj-s/2012/1207/20121207034517194.jpg" alt="news" class="w-full h-full object-cover">
</CardContent>
<CardHeader>
<CardTitle class="text-center">
国家政策
</CardTitle>
</CardHeader>
</Card>
</NuxtLink>
<NuxtLink to="/policy/地方">
<Card class="overflow-hidden w-[300px] h-[500px] flex flex-col ">
<CardContent class="p-0 flex-1">
<img src="https://ts1.cn.mm.bing.net/th/id/R-C.9d9900102aea31b95588cd15dcf7e2ac?rik=EjFSTbg5c5V6ng&riu=http%3a%2f%2fp0.ifengimg.com%2fa%2f2016_51%2fccf711fb8a241d2_size259_w880_h580.jpeg&ehk=9lqQs4Bh0pNVyZP40FLRTrH%2bjh47aLKB6K5mTHQGRso%3d&risl=&pid=ImgRaw&r=0" alt="news" class="w-full h-full object-cover">
</CardContent>
<CardHeader>
<CardTitle class="text-center">
地方政策
</CardTitle>
</CardHeader>
</Card>
</NuxtLink>
<NuxtLink to="/policy/企业">
<Card class="overflow-hidden w-[300px] h-[500px] flex flex-col ">
<CardContent class="p-0 flex-1">
<img src="https://ts1.cn.mm.bing.net/th/id/R-C.5307c6c84d509b6d734429944227cf2d?rik=PkoltCVAAkQD4A&riu=http%3a%2f%2fwww.tech-sonic.net%2fImgUpload%2fimages%2f0905-1.jpg&ehk=C944SM%2bikWpqeVXPv1QgKwOioMud6LwZUu4lKwoOiwQ%3d&risl=&pid=ImgRaw&r=0" alt="news" class="w-full h-full object-cover">
</CardContent>
<CardHeader>
<CardTitle class="text-center">
企业政策
</CardTitle>
</CardHeader>
</Card>
</NuxtLink>
</div>
</template>

View File

@ -0,0 +1,131 @@
<script setup lang="tsx">
import type { ColumnDef } from '@tanstack/vue-table'
import Badge from '../../../packages/ui/components/ui/badge/Badge.vue'
import DataTable from '~/components/common/table/DataTable.vue'
import DataTableColumnHeader from '~/components/common/table/DataTableColumnHeader.vue'
import DataTableRowActions from '~/components/common/table/DataTableRowActions.vue'
import EditEnv from '~/components/form/resource/edit-env.vue'
import ShowCraft from '~/components/form/resource/show-craft.vue'
const resource = useResourceStore()
const columns: ColumnDef<ResourceTechnologyInfo, any>[] = [
{
accessorKey: 'properThing',
header: ({ column }) => <DataTableColumnHeader column={column} title="适用废弃物" />,
cell: ({ row }) => <div class='w-26'>{row.original.properThing.join(',')}</div>,
enableSorting: false,
enableHiding: false,
},
{
accessorKey: 'craftImage',
header: ({ column }) => <DataTableColumnHeader column={column} title="工艺流程" />,
cell: ({ row }) => (<ShowCraft resource={row.original}/>),
},
{
accessorKey: 'device',
header: ({ column }) => <DataTableColumnHeader column={column} title="所需设备" />,
cell: ({ row }) => <div class='w-26'>{row.original.device}</div>,
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
accessorKey: 'env',
header: ({ column }) => <DataTableColumnHeader column={column} title="场地条件" />,
cell: ({ row }) => <EditEnv env={row.original.env} />,
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
accessorKey: 'production',
header: ({ column }) => <DataTableColumnHeader column={column} title="产出产品" />,
cell: ({ row }) => <div class='w-26'>{row.original.production}</div>,
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
accessorKey: 'work',
header: ({ column }) => <DataTableColumnHeader column={column} title="用工" />,
cell: ({ row }) => <div class='w-26'>{row.original.work}</div>,
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
accessorKey: 'discharge',
header: ({ column }) => <DataTableColumnHeader column={column} title="排放物" />,
cell: ({ row }) => (
<>
<div class=''>{ row.original.discharge.effluent.map(i => (<Badge class="bg-blue-500">{i}</Badge>))} </div>
<div class=''>{ row.original.discharge.sludge.map(i => (<Badge class="bg-orange-500">{i}</Badge>))} </div>
<div class=''>{ row.original.discharge.gas.map(i => (<Badge class="bg-gray-500">{i}</Badge>))} </div>
</>
),
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
accessorKey: 'cost',
header: ({ column }) => <DataTableColumnHeader column={column} title="成本" />,
cell: ({ row }) => <div class='w-26'>{row.original.cost}</div>,
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
accessorKey: 'efficiency',
header: ({ column }) => <DataTableColumnHeader column={column} title="效益" />,
cell: ({ row }) => <div class='w-26'>{row.original.efficiency}</div>,
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
accessorKey: 'utilization',
header: ({ column }) => <DataTableColumnHeader column={column} title="资源利用效率" />,
cell: ({ row }) => <div class='w-26'>{row.original.utilization}</div>,
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
id: 'actions',
cell: ({ row }) => (
<DataTableRowActions row={row} menus={[{
label: '编辑',
icon: 'tabler:edit',
click: () => {
console.log('edit', row.original)
},
}, {
label: '删除',
icon: 'tabler:trash',
click: () => {
console.log('delete', row.original)
},
}]}/>
),
},
]
</script>
<template>
<div class="p-4 mt-10 mx-autoto ">
<div class="flex justify-center">
<h1 class="text-3xl font-bold mb-4 text-center">
资源化技术信息
</h1>
</div>
<DataTable :data="resource.resources" :columns="columns" class="px-10">
<template #toolbar>
<FormResourceCreateForm />
</template>
</DataTable>
</div>
</template>

151
apps/web/pages/standard.vue Normal file
View File

@ -0,0 +1,151 @@
<script setup lang="tsx">
const data = [
{
title: '电动汽车远程服务于管理系统技术规范',
description: '',
image: '1.png',
},
{
title: '住宅装饰装修工程施工规范',
description: '',
image: '/2.jpg',
},
]
</script>
<template>
<div class="flex flex-col pt-20">
<main class="grid flex-1 gap-4 p-4 overflow-auto md:grid-cols-2 lg:grid-cols-6">
<div class="relative flex-col items-start hidden gap-8 md:flex">
<form class="grid items-start w-full gap-6">
<fieldset class="grid gap-6 p-4 border rounded-lg">
<legend class="px-1 -ml-1 text-sm font-medium">
搜索条件
</legend>
<div class="grid gap-3">
<Label for="model">标准来源</Label>
<Select>
<SelectTrigger
id="model"
class="items-start [&_[data-description]]:hidden"
>
<SelectValue placeholder="Select a model" />
</SelectTrigger>
<SelectContent>
<SelectItem value="genesis">
<div class="flex items-start gap-3 text-muted-foreground">
<Rabbit class="size-5" />
<div class="grid gap-0.5">
<p>
Neural
<span class="font-medium text-foreground">
Genesis
</span>
</p>
<p class="text-xs" data-description>
Our fastest model for general use cases.
</p>
</div>
</div>
</SelectItem>
<SelectItem value="explorer">
<div class="flex items-start gap-3 text-muted-foreground">
<Bird class="size-5" />
<div class="grid gap-0.5">
<p>
Neural
<span class="font-medium text-foreground">
Explorer
</span>
</p>
<p class="text-xs" data-description>
Performance and speed for efficiency.
</p>
</div>
</div>
</SelectItem>
<SelectItem value="quantum">
<div class="flex items-start gap-3 text-muted-foreground">
<Turtle class="size-5" />
<div class="grid gap-0.5">
<p>
Neural
<span class="font-medium text-foreground">
Quantum
</span>
</p>
<p class="text-xs" data-description>
The most powerful model for complex computations.
</p>
</div>
</div>
</SelectItem>
</SelectContent>
</Select>
</div>
<div class="grid gap-3">
<Label for="temperature">Temperature</Label>
<Input id="temperature" type="number" placeholder="0.4" />
</div>
<div class="grid grid-cols-2 gap-4">
<div class="grid gap-3">
<Label for="top-p">Top P</Label>
<Input id="top-p" type="number" placeholder="0.7" />
</div>
<div class="grid gap-3">
<Label for="top-k">Top K</Label>
<Input id="top-k" type="number" placeholder="0.0" />
</div>
</div>
</fieldset>
<fieldset class="grid gap-6 p-4 border rounded-lg">
<legend class="px-1 -ml-1 text-sm font-medium">
Messages
</legend>
<div class="grid gap-3">
<Label for="role">Role</Label>
<Select default-value="system">
<SelectTrigger>
<SelectValue placeholder="Select a role" />
</SelectTrigger>
<SelectContent>
<SelectItem value="system">
System
</SelectItem>
<SelectItem value="user">
User
</SelectItem>
<SelectItem value="assistant">
Assistant
</SelectItem>
</SelectContent>
</Select>
</div>
<div class="grid gap-3">
<Label for="content">Content</Label>
<Textarea
id="content"
placeholder="You are a..."
class="min-h-[9.5rem]"
/>
</div>
</fieldset>
</form>
</div>
<div class="relative flex h-full col-span-5 p-4">
<div class="grid w-full grid-cols-2 gap-4 mx-auto md:grid-cols-3 lg:grid-cols-5">
<Card v-for="item in data" :key="item.title" class="overflow-hidden">
<CardContent class="p-0">
<img :src="item.image" alt="news" class="w-full">
</CardContent>
<CardHeader>
<CardTitle>{{ item.title }}</CardTitle>
<CardDescription>{{ item.description }}</CardDescription>
</CardHeader>
</Card>
</div>
</div>
</main>
</div>
</template>

BIN
apps/web/public/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

BIN
apps/web/public/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -190,7 +190,7 @@ const data = [
{ name: '武汉', value: 273 },
{ name: '大庆', value: 279 },
]
const geoCoordMap = {
export const geoCoordMap = {
: [121.15, 31.89],
: [109.781327, 39.608266],
: [120.38, 37.35],
@ -383,6 +383,10 @@ const geoCoordMap = {
: [125.03, 46.58],
}
export function dataMap(value: keyof typeof geoCoordMap) {
}
function convertData(data: {
name: string
value: number
@ -399,96 +403,3 @@ function convertData(data: {
}
return res
}
export default function getData() {
return {
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300,
},
backgroundColor: '#404a59',
title: {
text: 'Air quality of major cities in China',
subtext: 'data from PM25.in',
sublink: 'http://www.pm25.in',
left: 'center',
textStyle: {
color: '#fff',
},
},
tooltip: {
trigger: 'item',
},
legend: {
orient: 'vertical',
y: 'bottom',
x: 'right',
data: ['pm2.5'],
textStyle: {
color: '#fff',
},
},
geo: {
map: 'china',
emphasis: {
label: {
show: false,
},
itemStyle: {
areaColor: '#2a333d',
},
},
itemStyle: {
areaColor: '#323c48',
borderColor: '#111',
},
},
series: [
{
name: 'pm2.5',
type: 'scatter',
coordinateSystem: 'geo',
data: convertData(data),
symbolSize: val => val[2] / 10,
tooltip: {
formatter(val) {
return `${val.name}: ${val.value[2]}`
},
},
itemStyle: {
color: '#ddb926',
},
},
{
name: 'Top 5',
type: 'effectScatter',
coordinateSystem: 'geo',
data: convertData(data.sort((a, b) => b.value - a.value).slice(0, 6)),
symbolSize: val => val[2] / 10,
showEffectOn: 'render',
rippleEffect: {
brushType: 'stroke',
},
emphasis: {
scale: true,
},
tooltip: {
formatter(val) {
return `${val.name}: ${val.value[2]}`
},
},
label: {
formatter: '{b}',
position: 'right',
show: true,
},
itemStyle: {
color: '#f4e925',
shadowBlur: 10,
shadowColor: '#333',
},
zlevel: 1,
},
],
}
}

15
apps/web/utils/utils.ts Normal file
View File

@ -0,0 +1,15 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
import type { Updater } from '@tanstack/vue-table'
import type { Ref } from 'vue'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function valueUpdater<T extends Updater<any>>(updaterOrValue: T, ref: Ref) {
ref.value
= typeof updaterOrValue === 'function'
? updaterOrValue(ref.value)
: updaterOrValue
}

View File

@ -0,0 +1,23 @@
<script setup lang="ts" generic="T extends ZodObject<ZodRawShape>">
import { toTypedSchema } from '@vee-validate/zod'
import type { InvalidSubmissionHandler, SubmissionHandler } from 'vee-validate'
import { Form } from 'vee-validate'
import type { infer as ZodInfer, ZodObject, ZodRawShape } from 'zod'
const props = defineProps<{
as?: string
schema: T
initialValues: ZodInfer<T>
onSubmit: SubmissionHandler< ZodInfer<T>> // (values: ZodInfer<T>) => Promise<void> | void
onInvalidSubmit?: InvalidSubmissionHandler
keepValues?: boolean
}>()
const formSchema = toTypedSchema(props.schema)
</script>
<template>
<Form v-bind="props" :validation-schema="formSchema">
<slot />
</Form>
</template>

View File

@ -0,0 +1,32 @@
<script setup lang="ts">
import { Form, Field as FormField } from 'vee-validate'
import FormControl from './FormControl.vue'
import FormItem from './FormItem.vue'
import FormLabel from './FormLabel.vue'
import FormMessage from './FormMessage.vue'
defineProps<{
name: string
label?: string
required?: boolean
}>()
</script>
<template>
<FormField v-slot="{ componentField, value, handleChange }" :name="name">
<FormItem class="grid items-center grid-cols-4 gap-4">
<slot name="content" :component-field="componentField" :value="value" :handle-change="handleChange">
<FormLabel class="flex items-start justify-start font-medium gap-1">
{{ label }}
<span v-if="required" class="text-destructive"> *</span>
</FormLabel>
<div class="col-span-3">
<FormControl>
<slot :component-field="componentField" :value="value" :handle-change="handleChange" />
</FormControl>
<FormMessage />
</div>
</slot>
</FormItem>
</FormField>
</template>

View File

@ -1,12 +1,8 @@
<script lang="ts">
import type { HTMLAttributes } from 'vue'
</script>
<script lang="ts" setup>
import { provide } from 'vue'
import { type HTMLAttributes, provide } from 'vue'
import { useId } from 'radix-vue'
import { cn } from '../../../lib/utils'
import { FORM_ITEM_INJECTION_KEY } from './useFormItemKey'
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys'
const props = defineProps<{
class?: HTMLAttributes['class']
@ -17,7 +13,7 @@ provide(FORM_ITEM_INJECTION_KEY, id)
</script>
<template>
<div :class="cn('space-y-2', props.class)">
<div :class="cn('space-y-2 ', props.class)">
<slot />
</div>
</template>

View File

@ -1,9 +1,9 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import type { LabelProps } from 'radix-vue'
import { useFormField } from './useFormField'
import { cn } from '../../../lib/utils'
import { Label } from '../../../components/ui/label'
import { Label } from '../label'
import { useFormField } from './useFormField'
const props = defineProps<LabelProps & { class?: HTMLAttributes['class'] }>()

View File

@ -1,6 +1,9 @@
export { Form, Field as FormField } from 'vee-validate'
// export { Form, Field as FormField } from 'vee-validate'
export { default as FormItem } from './FormItem.vue'
export { default as FormLabel } from './FormLabel.vue'
export { default as FormControl } from './FormControl.vue'
export { default as FormMessage } from './FormMessage.vue'
export { default as FormDescription } from './FormDescription.vue'
export { default as FormFieldItem } from './FormFieldItem.vue'
export { default as Form } from './Form.vue'
export { FORM_ITEM_INJECTION_KEY } from './injectionKeys'

View File

@ -0,0 +1,4 @@
import type { InjectionKey } from 'vue'
export const FORM_ITEM_INJECTION_KEY
= Symbol() as InjectionKey<string>

View File

@ -1,6 +1,6 @@
import { FieldContextKey, useFieldError, useIsFieldDirty, useIsFieldTouched, useIsFieldValid } from 'vee-validate'
import { inject } from 'vue'
import { FORM_ITEM_INJECTION_KEY } from './useFormItemKey'
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys'
export function useFormField() {
const fieldContext = inject(FieldContextKey)

View File

@ -1,8 +1,8 @@
export {
PaginationRoot as Pagination,
PaginationList,
PaginationListItem,
} from 'radix-vue'
// export {
// PaginationRoot as Pagination,
// PaginationList,
// PaginationListItem,
// } from 'radix-vue'
export { default as PaginationEllipsis } from './PaginationEllipsis.vue'
export { default as PaginationFirst } from './PaginationFirst.vue'
export { default as PaginationLast } from './PaginationLast.vue'

View File

@ -1,6 +1,15 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
import type { Updater } from '@tanstack/vue-table'
import type { Ref } from 'vue'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function valueUpdater<T extends Updater<any>>(updaterOrValue: T, ref: Ref) {
ref.value
= typeof updaterOrValue === 'function'
? updaterOrValue(ref.value)
: updaterOrValue
}

View File

@ -11,6 +11,10 @@ export default defineNuxtModule({
path: join(__dirname, 'components/ui'),
// prefix: 'Shadcn',
})
dirs.push({
path: join(__dirname, 'lib'),
// prefix: 'Shadcn',
})
},
},
})

View File

@ -18,6 +18,7 @@
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.13",
"@tanstack/vue-table": "8.19.2",
"@unovis/ts": "^1.4.0",
"@unovis/vue": "^1.4.0",
"@vee-validate/zod": "^4.12.7",

File diff suppressed because it is too large Load Diff