lzq 2025-07-31 18:21:44 +08:00
parent e1df05b96e
commit f812ce5d43
21 changed files with 602 additions and 171 deletions

View File

@ -50,7 +50,7 @@
#tabs .active {
border-bottom-color: #FF0000;
border-bottom-color: #f00;
color: #222;
}
@ -88,7 +88,7 @@
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #E7E1CD;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
@ -218,7 +218,7 @@
.markdown > p,
.markdown > blockquote,
.markdown > .highlight,
.markdown > ol,
.markdown >ol,
.markdown>ul {
width: 80%;
}
@ -288,7 +288,7 @@
.markdown blockquote {
font-size: 90%;
color: #999999;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
@ -318,7 +318,7 @@
display: inline-block;
}
.markdown > br,
.markdown >br,
.markdown>p>br {
clear: both;
}
@ -378,12 +378,12 @@
}
.hljs-addition {
color: #55A532;
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #BD2C00;
color: #bd2c00;
background-color: #ffecec;
}

View File

@ -55,15 +55,15 @@
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe667;</span>
<div class="name">menu</div>
<div class="code-name">&amp;#xe667;</div>
<span class="icon iconfont">&#xe61f;</span>
<div class="name">igw-f-contact</div>
<div class="code-name">&amp;#xe61f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe689;</span>
<div class="name">menu_3rolepermiss</div>
<div class="code-name">&amp;#xe689;</div>
<span class="icon iconfont">&#xe667;</span>
<div class="name">menu</div>
<div class="code-name">&amp;#xe667;</div>
</li>
<li class="dib">
@ -156,9 +156,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1753872505940') format('woff2'),
url('iconfont.woff?t=1753872505940') format('woff'),
url('iconfont.ttf?t=1753872505940') format('truetype');
src: url('iconfont.woff2?t=1753923827999') format('woff2'),
url('iconfont.woff?t=1753923827999') format('woff'),
url('iconfont.ttf?t=1753923827999') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@ -184,6 +184,15 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-igw-f-role"></span>
<div class="name">
igw-f-contact
</div>
<div class="code-name">.icon-igw-f-role
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-menu"></span>
<div class="name">
@ -193,15 +202,6 @@
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-menu_rolepermiss"></span>
<div class="name">
menu_3rolepermiss
</div>
<div class="code-name">.icon-menu_rolepermiss
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-cog"></span>
<div class="name">
@ -339,18 +339,18 @@
<li class="dib">
<svg aria-hidden="true" class="icon svg-icon">
<use xlink:href="#icon-menu"></use>
<use xlink:href="#icon-igw-f-role"></use>
</svg>
<div class="name">menu</div>
<div class="code-name">#icon-menu</div>
<div class="name">igw-f-contact</div>
<div class="code-name">#icon-igw-f-role</div>
</li>
<li class="dib">
<svg aria-hidden="true" class="icon svg-icon">
<use xlink:href="#icon-menu_rolepermiss"></use>
<use xlink:href="#icon-menu"></use>
</svg>
<div class="name">menu_3rolepermiss</div>
<div class="code-name">#icon-menu_rolepermiss</div>
<div class="name">menu</div>
<div class="code-name">#icon-menu</div>
</li>
<li class="dib">

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4985351 */
src: url('iconfont.woff2?t=1753872505940') format('woff2'),
url('iconfont.woff?t=1753872505940') format('woff'),
url('iconfont.ttf?t=1753872505940') format('truetype');
src: url('iconfont.woff2?t=1753923827999') format('woff2'),
url('iconfont.woff?t=1753923827999') format('woff'),
url('iconfont.ttf?t=1753923827999') format('truetype');
}
.iconfont {
@ -13,12 +13,12 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-menu:before {
content: "\e667";
.icon-igw-f-role:before {
content: "\e61f";
}
.icon-menu_rolepermiss:before {
content: "\e689";
.icon-menu:before {
content: "\e667";
}
.icon-cog:before {

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,13 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "24493756",
"name": "igw-f-contact",
"font_class": "igw-f-role",
"unicode": "e61f",
"unicode_decimal": 58911
},
{
"icon_id": "1174312",
"name": "menu",
@ -12,13 +19,6 @@
"unicode": "e667",
"unicode_decimal": 58983
},
{
"icon_id": "12891317",
"name": "menu_3rolepermiss",
"font_class": "menu_rolepermiss",
"unicode": "e689",
"unicode_decimal": 59017
},
{
"icon_id": "1261484",
"name": "cog",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -10,3 +10,9 @@ html {
#app {
padding: 5px;
}
.ix-modal-wrapper,
.ix-mask {
position: absolute;
}

View File

@ -19,7 +19,7 @@ import { reloadUserInfo } from '@/common/app'
enum SpecialPage {
Main = 'main',
Login = 'login',
Home = 'home',
Home = 'tsp',
NotFund = 'notFund'
}

View File

@ -55,7 +55,7 @@ function errHandler({code, msg, message}: R) {
console.log(msg, message)
break
default:
Toast.error(message)
Toast.error(message ?? '操作失败')
}
}
@ -94,6 +94,9 @@ httpUtil.interceptors.response.use(
response => {
// console.log('HTTP 请求结果', response.config.url, response)
// vite 代理失败时 响应码为 200 响应内容为空
if (response.config.responseType !== 'json') {
return Promise.resolve(response)
}
if (response.data == null) {
response.data = {code: 99999, msg: '无响应内容', message: '无响应内容', data: null, headers: response.headers}
}
@ -204,6 +207,62 @@ export function postMltForm<T>(url: string, body: any, config?: AxiosConfig, dis
})
}
function getFileName(contentDisposition: string) {
// 检查content-disposition是否存在
if (!contentDisposition) {
return null
}
// 查找filename=部分
const match = contentDisposition.match(/filename=(.+)/)
if (!match || match.length < 2) {
return null
}
// 提取并解码文件名
const fileNameEncoded = match[1].trim()
// 移除可能存在的引号
const fileNameWithoutQuotes = fileNameEncoded.replace(/["']/g, '')
// 解码URL编码的字符串
return decodeURIComponent(fileNameWithoutQuotes)
}
export function download(url: string, params?: any, disposeErr: boolean = true) {
return httpUtil.get(url, {params, paramsSerializer, responseType: 'arraybuffer'})
.then(res => {
let data = res.data
if (!data || data.byteLength <= 0) {
// 错误提示
return
}
const contentDisposition = res.headers['Content-Disposition'] ?? res.headers['content-disposition']
const fileName = getFileName(contentDisposition) ?? '下载的文件'
// 将二进制流转为blob
const blob = new Blob([ data ])
// 创建新的URL并指向File对象或者Blob对象的地址
const blobURL = window.URL.createObjectURL(blob)
// 创建a标签用于跳转至下载链接
const tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobURL
tempLink.setAttribute('download', decodeURI(fileName))
// 兼容某些浏览器不支持HTML5的download属性
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
// 挂载a标签
document.body.appendChild(tempLink)
tempLink.click()
document.body.removeChild(tempLink)
// 释放blob URL地址
window.URL.revokeObjectURL(blobURL)
})
.catch(res => {
if (disposeErr) errHandler(res.response.data)
return Promise.reject(res.response.data)
})
}
export default {
get,
post,

View File

@ -13,6 +13,7 @@ import { DateObjectUnits } from 'luxon/src/datetime'
*/
export enum FMT {
month = 'yyyy-MM',
date = 'yyyy-MM-dd',
time = 'HH:mm',
time_sec = 'HH:mm:ss',

View File

@ -2,8 +2,8 @@ export {}
declare global {
namespace IconfontTypes {
type name = 'menu'
| 'menu_rolepermiss'
type name = 'igw-f-role'
| 'menu'
| 'cog'
| 'user'
| 'bar-chart'

View File

@ -4,7 +4,12 @@ import { useFormGroup } from '@idux/cdk'
import { TableColumn } from '@idux/components/table'
import { TablePagination } from '@idux/components/table/src/types'
import Charts from '@/components/echarts/Charts.vue'
import DisposeRecodeDetail from '@/pages/dispose-recode/DisposeRecodeDetail.vue'
import DisposeRecodeApi from '@/pages/dispose-recode/dispose-recode-api.ts'
import Times from '@/common/utils/times.ts'
const disposeRecodeDetail = ref<InstanceType<typeof DisposeRecodeDetail> | null>(null)
const disposeRecode = ref<HTMLElement | undefined>(undefined)
const chartOption = reactive<Echarts.Option>({
aria: {
enabled: true,
@ -20,7 +25,7 @@ const chartOption = reactive<Echarts.Option>({
},
legend: {
show: true,
top: 0
top: 0,
},
grid: {
outerBounds: {
@ -33,7 +38,7 @@ const chartOption = reactive<Echarts.Option>({
xAxis: [
{
type: 'category',
data: [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],
data: [],
axisTick: {
alignWithLabel: true
}
@ -47,67 +52,58 @@ const chartOption = reactive<Echarts.Option>({
type: 'value'
}
],
series: [
{
name: 'Direct1',
type: 'bar',
data: [ 10, 52, 200, 334, 390, 330, 220 ],
},
{
name: 'Direct2',
type: 'bar',
data: [ 10, 52, 200, 334, 390, 330, 220 ]
}
]
series: []
})
interface Data {
id: string
pointName: string
street: string
microdistrict: string
propertyManagement: string
statusTxt: string
status: 'ZhengChang' | 'FenZhengChang'
}
const formGroup = useFormGroup<DisposeRecodeTypes.SearchParam>({
keywords: [ '' ],
})
const formGroup = useFormGroup({})
const fullData: Data[] = []
for (let index = 0; index < 10; index++) {
fullData.push({
id: index + '',
pointName: `Edrward ${index}`,
street: `Edrward ${index}`,
microdistrict: `Edrward ${index}`,
propertyManagement: `Edrward ${index}`,
statusTxt: `Edrward ${index}`,
status: index % 2 === 0 ? 'ZhengChang' : 'FenZhengChang',
})
}
const data = ref(fullData)
const datasource = ref<DisposeRecodeTypes.DisposeRecodeData[]>()
const columns: TableColumn[] = [
{
title: '名称',
dataKey: 'pointName',
customCell: 'pointName'
title: '来源地',
dataKey: 'origin',
},
{
title: '所属街道',
dataKey: 'street',
title: '清运公司',
dataKey: 'clearingCompany',
},
{
title: '所属小区',
dataKey: 'microdistrict',
title: '消纳场名称',
dataKey: 'disposalSite',
},
{
title: '所属物业',
dataKey: 'propertyManagement'
title: '车牌号',
dataKey: 'licensePlate',
},
{
title: '运营状态',
dataKey: 'statusTxt',
customCell: 'status',
title: '联系人',
dataKey: 'contact',
},
{
title: '联系电话',
dataKey: 'contactPhone',
},
{
title: '进场磅重(吨)',
dataKey: 'inWeight',
},
{
title: '出场磅重(吨)',
dataKey: 'outWeight',
},
{
title: '净重(吨)',
dataKey: 'suttleWeight',
},
{
title: '进场时间',
dataKey: 'inTime',
},
{
title: '出场时间',
dataKey: 'outTime',
},
{
title: '操作',
@ -128,17 +124,65 @@ const pagination = reactive<TablePagination>({
pagination.pageSize = pageSize
}
})
const tableSpin = ref(false)
function searchHandler() {
console.log('------', formGroup.getValue())
tableSpin.value = true
DisposeRecodeApi.paging({
...formGroup.getValue(),
current: pagination.pageIndex ?? 1,
size: pagination.pageSize ?? 10,
})
.then(res => {
pagination.pageIndex = res.data.current
pagination.pageSize = res.data.size
pagination.total = res.data.total
datasource.value = res.data.records
})
.finally(() => {
tableSpin.value = false
})
}
function exportHandler() {
DisposeRecodeApi.exportData([ '2025-01-01', '2025-01-31' ])
}
const monthValue = ref<Date>(new Date())
function renderChart() {
DisposeRecodeApi.statistics(Times.format(monthValue.value, Times.FMT.month))
.then(res => {
chartOption.xAxis[0].data = res.data.xAxis
let series = []
for (let seriesKey in res.data.series) {
series.push({
name: seriesKey,
type: 'bar',
data: res.data.series[seriesKey],
})
}
chartOption.series = series
})
}
onMounted(() => {
searchHandler()
renderChart()
})
</script>
<template>
<div class="dispose-recode">
<div ref="disposeRecode" class="dispose-recode">
<div class="title">垃圾处置记录</div>
<div class="desc">管理和分析垃圾处置数据</div>
<IxCard class="graph-card">
<template #header>
<div class="graph-tool">
<div>垃圾处置量统计</div>
<IxDatePicker type="month"/>
<IxDatePicker v-model:value="monthValue" type="month" @change="renderChart"/>
</div>
</template>
<Charts :option="chartOption"/>
@ -147,31 +191,26 @@ const pagination = reactive<TablePagination>({
<template #header>
<div class="table-tool">
<div>垃圾处置记录</div>
<IxForm :control="formGroup" class="search-form" layout="inline">
<IxForm :control="formGroup" class="search-form" layout="inline" @submit.prevent="searchHandler">
<IxTooltip placement="top" title="输入内容回车搜索">
<IxFormItem messageTooltip>
<IxInput clearable control="keywords" placeholder="请输入" @clear="searchHandler"/>
</IxFormItem>
</IxTooltip>
<IxFormItem messageTooltip>
<IxInput placeholder="请输入收纳点名称"/>
</IxFormItem>
<IxFormItem messageTooltip>
<IxInput placeholder="请输入收纳点名称"/>
<IxButton icon="search" mode="primary" type="submit"/>
</IxFormItem>
</IxForm>
<IxButton icon="download" mode="primary"></IxButton>
<IxButton icon="download" mode="primary" @click="exportHandler"></IxButton>
</div>
</template>
<IxTable :columns="columns" :dataSource="data" :pagination="pagination" autoHeight class="data-table" getKey="id">
<template #pointName="{record}">
<span style="color: #165DFF;border-radius: 100%;background-color: #165DFF1A;width: 2.25rem;height: 2.25rem;display: inline-flex;justify-content: center;align-items: center;margin-right: 0.5rem"><i class="iconfont icon-mapmarker" style="display: block"></i></span>
<span>{{ record.pointName }}</span>
</template>
<template #action>
<IxButton class="detail-btn" icon="eye" mode="text">查看</IxButton>
</template>
<template #status="{record}">
<IxTag v-if="record.status === 'ZhengChang'" status="success">{{ record.statusTxt }}</IxTag>
<IxTag v-else status="error">{{ record.statusTxt }}</IxTag>
<IxTable :columns="columns" :dataSource="datasource" :pagination="pagination" autoHeight class="data-table" getKey="id">
<template #action="{record}">
<IxButton class="detail-btn" icon="eye" mode="text" @click="disposeRecodeDetail?.open(record)"></IxButton>
</template>
</IxTable>
</IxCard>
<DisposeRecodeDetail ref="disposeRecodeDetail" :container="disposeRecode"/>
</div>
</template>
@ -179,7 +218,7 @@ const pagination = reactive<TablePagination>({
.dispose-recode {
display: flex;
flex-direction: column;
height: 100%;
height: 150%;
width: 100%;
.title {
@ -256,8 +295,12 @@ const pagination = reactive<TablePagination>({
.search-form {
flex 6
:deep(.ix-form-item) {
flex 2
:deep(.ix-form-item):nth-child(1) {
flex 7
}
:deep(.ix-form-item):nth-child(2) {
flex 1
}
}

View File

@ -0,0 +1,95 @@
<script lang="ts" setup>
let props = defineProps<{
container?: HTMLElement;
}>()
const show = ref(false)
const ins = ref<HTMLElement | null>(null)
const data = reactive<DisposeRecodeTypes.DisposeRecodeData>({
id: '',
origin: '',
clearingCompany: '',
licensePlate: '',
contact: '',
contactPhone: '',
inWeight: '',
outWeight: '',
suttleWeight: '',
inTime: '',
outTime: '',
disposalSite: '',
})
onMounted(() => {
console.log(ins)
let container = props.container
if (container) {
container.style.position = 'relative'
for (let child of container.children) {
console.log(child)
}
}
})
defineExpose({
open(detail: DisposeRecodeTypes.DisposeRecodeData) {
Object.assign(data, detail)
show.value = true
}
})
</script>
<template>
<IxModal ref="ins" v-model:visible="show" :container="container" :mask="false" cancel-text="" class="dispose-recode-detail" draggable header="详情" maskClosable type="default" width="40rem">
<p class="detail-item"><span>来源地</span><span>{{ data.origin }}</span></p>
<p class="detail-item"><span>清运公司</span><span>{{ data.clearingCompany }}</span></p>
<p class="detail-item"><span>车牌号</span><span>{{ data.licensePlate }}</span></p>
<p class="detail-item"><span>联系人</span><span>{{ data.contact }}</span></p>
<p class="detail-item"><span>联系电话</span><span>{{ data.contactPhone }}</span></p>
<p class="detail-item"><span>进场磅重</span><span>{{ data.inWeight }}</span></p>
<p class="detail-item"><span>出场磅重</span><span>{{ data.outWeight }}</span></p>
<p class="detail-item"><span>净重</span><span>{{ data.suttleWeight }}</span></p>
<p class="detail-item"><span>进场时间</span><span>{{ data.inTime }}</span></p>
<p class="detail-item"><span>出场时间</span><span>{{ data.outTime }}</span></p>
<p class="detail-item"><span>消纳场名称</span><span>{{ data.disposalSite }}</span></p>
<template #footer="{ cancel:_, ok }">
<IxButton mode="primary" @click="ok"></IxButton>
</template>
</IxModal>
</template>
<style lang="stylus" scoped>
.dispose-recode-detail .detail-item:not(:last-child) {
border-bottom: 1px #E5E7EB dashed;
padding-bottom: 0.5rem;
}
:deep(.ix-modal-wrapper),
:deep(.ix-mask) {
position absolute
}
.detail-item {
display flex
justify-content start
align-items center
span:nth-child(1) {
flex 10
font-weight bold
}
span:nth-child(2) {
flex 14
text-align end
padding-right 10%
}
span {
display inline-block
color #1D2129
font-size 1.25rem
}
}
</style>

View File

@ -0,0 +1,32 @@
import {
download,
get,
post
} from '@/common/utils/http-util.ts'
export default {
paging(data: DisposeRecodeTypes.SearchParam & G.PageParam) {
return get<G.PageResult<DisposeRecodeTypes.DisposeRecodeData>>('/dispose_record/paging', data)
},
list(pid: string | null = null) {
return get<DisposeRecodeTypes.DisposeRecodeData[]>('/dispose_record/list', {pid: pid})
},
detail(id: string) {
return get<DisposeRecodeTypes.DisposeRecodeData>('/dispose_record/detail', {id})
},
add(data: DisposeRecodeTypes.DisposeRecodeData) {
return post('/dispose_record/add', data)
},
modify(data: DisposeRecodeTypes.DisposeRecodeData) {
return post('/dispose_record/modify', data)
},
del(ids: string[]) {
return post('/dispose_record/del', ids)
},
exportData(date: string[]) {
return download('/dispose_record/export', {date})
},
statistics(date: string) {
return get<DisposeRecodeTypes.StatisticsResult>('/dispose_record/statistics', {date})
},
}

View File

@ -0,0 +1,30 @@
export {}
declare global {
namespace DisposeRecodeTypes {
interface DisposeRecodeData {
id: string
origin: string
clearingCompany: string
licensePlate: string
contact: string
contactPhone: string
inWeight: string
outWeight: string
suttleWeight: string
inTime: string
outTime: string
disposalSite: string
}
interface SearchParam {
keywords: string
}
interface StatisticsResult {
xAxis: string[]
series: {
[key: string]: number[]
}
}
}
}

View File

@ -3,37 +3,35 @@ import { SelectData } from '@idux/components/select/src/types'
import { useFormGroup } from '@idux/cdk'
import { TableColumn } from '@idux/components/table'
import { TablePagination } from '@idux/components/table/src/types'
import CreateTsp from '@/pages/tsp/create-tsp.vue'
import TspApi from '@/pages/tsp/tsp-api.ts'
import { ref } from 'vue'
const createTsp = ref<InstanceType<typeof CreateTsp> | null>(null)
const dataSource: SelectData[] = [
{key: '', label: '所有'},
{key: 'ZhengChang', label: '正常运营'},
{key: 'FenZhengChang', label: '非正常运营'},
]
interface Data {
id: string
pointName: string
street: string
microdistrict: string
propertyManagement: string
statusTxt: string
status: 'ZhengChang' | 'FenZhengChang'
}
const pagination = reactive<TablePagination>({
pageIndex: 1,
pageSize: 10,
total: 0,
size: 'sm',
showTotal: true,
onChange(pageIndex: number, pageSize: number) {
console.log('------', pageIndex, pageSize)
pagination.pageIndex = pageIndex
pagination.pageSize = pageSize
}
})
const formGroup = useFormGroup({})
const fullData: Data[] = []
for (let index = 0; index < 10; index++) {
fullData.push({
id: index + '',
pointName: `Edrward ${index}`,
street: `Edrward ${index}`,
microdistrict: `Edrward ${index}`,
propertyManagement: `Edrward ${index}`,
statusTxt: `Edrward ${index}`,
status: index % 2 === 0 ? 'ZhengChang' : 'FenZhengChang',
})
}
const data = ref(fullData)
const formGroup = useFormGroup<TspTypes.SearchParam>({
pointName: [ '' ],
status: [ '' ],
})
const datasource = ref<TspTypes.TspData[]>()
const columns: TableColumn[] = [
{
title: '名称',
@ -42,7 +40,7 @@ const columns: TableColumn[] = [
},
{
title: '所属街道',
dataKey: 'street',
dataKey: 'streetName',
},
{
title: '所属小区',
@ -63,18 +61,40 @@ const columns: TableColumn[] = [
customCell: 'action',
}
]
const tableSpin = ref(false)
const statisticsResult = reactive<TspTypes.StatisticsResult>({
totalCount: 0,
totalCountGrowthRate: 0,
onlineCount: 0,
onlineCountGrowthRate: 0,
offlineCount: 0,
offlineCountGrowthRate: 0,
})
const pagination = reactive<TablePagination>({
pageIndex: 1,
pageSize: 10,
total: 100,
size: 'sm',
showTotal: true,
onChange(pageIndex: number, pageSize: number) {
console.log('------', pageIndex, pageSize)
pagination.pageIndex = pageIndex
pagination.pageSize = pageSize
}
function searchHandler() {
tableSpin.value = true
TspApi.paging({
...formGroup.getValue(),
current: pagination.pageIndex ?? 1,
size: pagination.pageSize ?? 10,
})
.then(res => {
pagination.pageIndex = res.data.current
pagination.pageSize = res.data.size
pagination.total = res.data.total
datasource.value = res.data.records
})
.finally(() => {
tableSpin.value = false
})
}
onMounted(() => {
searchHandler()
TspApi.statistics()
.then(res => {
Object.assign(statisticsResult, res.data)
})
})
</script>
<template>
@ -85,24 +105,24 @@ const pagination = reactive<TablePagination>({
<IxCard>
<div>
<div>收纳点总数</div>
<div>24</div>
<div><i class="iconfont icon-arrow-up"></i>较上月增长 12%</div>
<div>{{ statisticsResult.totalCount }}</div>
<div><i class="iconfont icon-arrow-up"></i>较上月增长 {{ statisticsResult.totalCountGrowthRate }}%</div>
</div>
<Iconfont name="map-pin" wrapper/>
</IxCard>
<IxCard>
<div>
<div>正常运营数量</div>
<div>24</div>
<div><i class="iconfont icon-arrow-up"></i>较上月增长 8%</div>
<div>{{ statisticsResult.onlineCount }}</div>
<div><i class="iconfont icon-arrow-up"></i>较上月增长 {{ statisticsResult.onlineCountGrowthRate }}%</div>
</div>
<Iconfont name="check-circle-fill" wrapper/>
</IxCard>
<IxCard>
<div>
<div>非正常运营数量</div>
<div>24</div>
<div><i class="iconfont icon-arrow-up"></i>较上月增长 2%</div>
<div>{{ statisticsResult.offlineCount }}</div>
<div><i class="iconfont icon-arrow-up"></i>较上月增长 {{ statisticsResult.offlineCountGrowthRate }}%</div>
</div>
<Iconfont name="exclamationcircle-f" wrapper/>
</IxCard>
@ -111,18 +131,25 @@ const pagination = reactive<TablePagination>({
<template #header>
<div class="table-tool">
<div>收纳点列表</div>
<IxForm :control="formGroup" class="search-form" layout="inline">
<IxForm :control="formGroup" class="search-form" layout="inline" @submit.prevent="searchHandler">
<IxTooltip placement="top" title="输入内容回车搜索">
<IxFormItem messageTooltip>
<IxInput clearable control="pointName" placeholder="请输入收纳点名称" @clear="searchHandler"/>
</IxFormItem>
</IxTooltip>
<IxFormItem messageTooltip>
<IxInput placeholder="请输入收纳点名称"/>
<IxSelect :dataSource="dataSource" control="status" @change="searchHandler"/>
</IxFormItem>
<IxFormItem messageTooltip>
<IxSelect :dataSource="dataSource"/>
<IxButton icon="search" mode="primary" type="submit"/>
</IxFormItem>
<IxButton icon="plus" mode="primary" type="submit">添加收纳点</IxButton>
</IxForm>
<IxButton icon="plus" mode="primary" @click="createTsp?.open()"></IxButton>
</div>
</template>
<IxTable :columns="columns" :dataSource="data" :pagination="pagination" autoHeight class="data-table" getKey="id">
<IxTable :columns="columns"
:dataSource="datasource"
:pagination="pagination" :spin="tableSpin" autoHeight class="data-table" getKey="id">
<template #pointName="{record}">
<Iconfont name="mapmarker" wrapper/>
<span>{{ record.pointName }}</span>
@ -136,6 +163,7 @@ const pagination = reactive<TablePagination>({
</template>
</IxTable>
</IxCard>
<CreateTsp ref="createTsp"/>
</div>
</template>
@ -294,9 +322,16 @@ const pagination = reactive<TablePagination>({
}
.search-form {
:deep(.ix-form-item) {
flex 2
:deep(.ix-form-item):nth-child(1) {
flex 5
}
:deep(.ix-form-item):nth-child(2) {
flex 2
}
:deep(.ix-form-item):nth-child(3) {
flex 1
}
:deep(.ix-button ) {

View File

@ -0,0 +1,66 @@
<script lang="ts" setup>
import {
useFormGroup,
Validators
} from '@idux/cdk'
import Toast from '@/components/toast'
import TspApi from '@/pages/tsp/tsp-api.ts'
const show = ref(false)
const formGroup = useFormGroup<TspTypes.AddParam>({
pointName: [ undefined, Validators.required ],
streetName: [ undefined, Validators.required ],
microdistrict: [ undefined, Validators.required ],
propertyManagement: [ undefined, Validators.required ],
videoUrl: [ undefined, Validators.required ],
})
const gutter = [ 40, 0 ]
function submit() {
if (formGroup.valid.value) {
TspApi.add({...formGroup.getValue(), status: 'ZhengChang'})
.then(_ => {
Toast.success('添加成功')
formGroup.reset()
})
} else {
formGroup.markAsDirty()
Toast.error('请填写完整信息')
}
return false
}
defineExpose({
open() {
show.value = true
}
})
</script>
<template>
<IxModal v-model:visible="show" cancel-text="" header="添加收纳点" ok-text="" type="default" width="40rem" @cancel="formGroup.reset()" @close="formGroup.reset()" @ok="submit">
<IxForm :colonless="false" :control="formGroup" :control-col="{span: 19}" :label-col="5" label-align="end" style="height: 23.4rem">
<IxFormItem :gutter="gutter" label="收纳点名称" messageTooltip>
<IxInput control="pointName" placeholder="请输入收纳点名称"/>
</IxFormItem>
<IxFormItem :gutter="gutter" label="所属街道" messageTooltip>
<IxInput control="streetName" placeholder="请输入街道名称"/>
</IxFormItem>
<IxFormItem :gutter="gutter" label="所属小区" messageTooltip>
<IxInput control="microdistrict" placeholder="请输入小区名称"/>
</IxFormItem>
<IxFormItem :gutter="gutter" label="所属物业" messageTooltip>
<IxInput control="propertyManagement" placeholder="请输入物业名称"/>
</IxFormItem>
<IxFormItem :gutter="gutter" label="视频地址" messageTooltip>
<IxInput control="videoUrl" placeholder="请输入视频地址"/>
</IxFormItem>
</IxForm>
</IxModal>
</template>
<style lang="stylus" scoped>
</style>

View File

@ -0,0 +1,28 @@
import {
get,
post
} from '@/common/utils/http-util.ts'
export default {
paging(data: TspTypes.SearchParam & G.PageParam) {
return get<G.PageResult<TspTypes.TspData>>('/temp_storage_point/paging', data)
},
list(pid: string | null = null) {
return get<TspTypes.TspData[]>('/temp_storage_point/list', {pid: pid})
},
detail(id: string) {
return get<TspTypes.TspData>('/temp_storage_point/detail', {id})
},
add(data: TspTypes.AddParam) {
return post('/temp_storage_point/add', data)
},
modify(data: TspTypes.TspData) {
return post('/temp_storage_point/modify', data)
},
del(ids: string[]) {
return post('/temp_storage_point/del', ids)
},
statistics() {
return get<TspTypes.StatisticsResult>('/temp_storage_point/statistics')
},
}

36
src/pages/tsp/tsp.d.ts vendored 100644
View File

@ -0,0 +1,36 @@
export {}
declare global {
namespace TspTypes {
interface TspData {
id: string
pointName: string
streetName: string
microdistrict: string
propertyManagement: string
status: 'ZhengChang' | 'FenZhengChang'
}
interface AddParam {
pointName?: string
streetName?: string
microdistrict?: string
propertyManagement?: string
status?: 'ZhengChang' | 'FenZhengChang'
videoUrl?: string
}
interface SearchParam {
pointName: string
status: 'ZhengChang' | 'FenZhengChang' | ''
}
interface StatisticsResult {
totalCount: number
totalCountGrowthRate: number
onlineCount: number
onlineCountGrowthRate: number
offlineCount: number
offlineCountGrowthRate: number
}
}
}