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 { #tabs .active {
border-bottom-color: #FF0000; border-bottom-color: #f00;
color: #222; color: #222;
} }
@ -88,7 +88,7 @@
.helps pre { .helps pre {
padding: 20px; padding: 20px;
margin: 10px 0; margin: 10px 0;
border: solid 1px #E7E1CD; border: solid 1px #e7e1cd;
background-color: #fffdef; background-color: #fffdef;
overflow: auto; overflow: auto;
} }
@ -288,7 +288,7 @@
.markdown blockquote { .markdown blockquote {
font-size: 90%; font-size: 90%;
color: #999999; color: #999;
border-left: 4px solid #e9e9e9; border-left: 4px solid #e9e9e9;
padding-left: 0.8em; padding-left: 0.8em;
margin: 1em 0; margin: 1em 0;
@ -378,12 +378,12 @@
} }
.hljs-addition { .hljs-addition {
color: #55A532; color: #55a532;
background-color: #eaffea; background-color: #eaffea;
} }
.hljs-deletion { .hljs-deletion {
color: #BD2C00; color: #bd2c00;
background-color: #ffecec; background-color: #ffecec;
} }

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

@ -55,7 +55,7 @@ function errHandler({code, msg, message}: R) {
console.log(msg, message) console.log(msg, message)
break break
default: default:
Toast.error(message) Toast.error(message ?? '操作失败')
} }
} }
@ -94,6 +94,9 @@ httpUtil.interceptors.response.use(
response => { response => {
// console.log('HTTP 请求结果', response.config.url, response) // console.log('HTTP 请求结果', response.config.url, response)
// vite 代理失败时 响应码为 200 响应内容为空 // vite 代理失败时 响应码为 200 响应内容为空
if (response.config.responseType !== 'json') {
return Promise.resolve(response)
}
if (response.data == null) { if (response.data == null) {
response.data = {code: 99999, msg: '无响应内容', message: '无响应内容', data: null, headers: response.headers} 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 { export default {
get, get,
post, post,

View File

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

View File

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

View File

@ -4,7 +4,12 @@ import { useFormGroup } from '@idux/cdk'
import { TableColumn } from '@idux/components/table' import { TableColumn } from '@idux/components/table'
import { TablePagination } from '@idux/components/table/src/types' import { TablePagination } from '@idux/components/table/src/types'
import Charts from '@/components/echarts/Charts.vue' 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>({ const chartOption = reactive<Echarts.Option>({
aria: { aria: {
enabled: true, enabled: true,
@ -20,7 +25,7 @@ const chartOption = reactive<Echarts.Option>({
}, },
legend: { legend: {
show: true, show: true,
top: 0 top: 0,
}, },
grid: { grid: {
outerBounds: { outerBounds: {
@ -33,7 +38,7 @@ const chartOption = reactive<Echarts.Option>({
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',
data: [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ], data: [],
axisTick: { axisTick: {
alignWithLabel: true alignWithLabel: true
} }
@ -47,67 +52,58 @@ const chartOption = reactive<Echarts.Option>({
type: 'value' type: 'value'
} }
], ],
series: [ 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 ]
}
]
}) })
interface Data { const formGroup = useFormGroup<DisposeRecodeTypes.SearchParam>({
id: string keywords: [ '' ],
pointName: string
street: string
microdistrict: string
propertyManagement: string
statusTxt: string
status: 'ZhengChang' | 'FenZhengChang'
}
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[] = [ const columns: TableColumn[] = [
{ {
title: '名称', title: '来源地',
dataKey: 'pointName', dataKey: 'origin',
customCell: 'pointName'
}, },
{ {
title: '所属街道', title: '清运公司',
dataKey: 'street', dataKey: 'clearingCompany',
}, },
{ {
title: '所属小区', title: '消纳场名称',
dataKey: 'microdistrict', dataKey: 'disposalSite',
}, },
{ {
title: '所属物业', title: '车牌号',
dataKey: 'propertyManagement' dataKey: 'licensePlate',
}, },
{ {
title: '运营状态', title: '联系人',
dataKey: 'statusTxt', dataKey: 'contact',
customCell: 'status', },
{
title: '联系电话',
dataKey: 'contactPhone',
},
{
title: '进场磅重(吨)',
dataKey: 'inWeight',
},
{
title: '出场磅重(吨)',
dataKey: 'outWeight',
},
{
title: '净重(吨)',
dataKey: 'suttleWeight',
},
{
title: '进场时间',
dataKey: 'inTime',
},
{
title: '出场时间',
dataKey: 'outTime',
}, },
{ {
title: '操作', title: '操作',
@ -128,17 +124,65 @@ const pagination = reactive<TablePagination>({
pagination.pageSize = pageSize 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> </script>
<template> <template>
<div class="dispose-recode"> <div ref="disposeRecode" class="dispose-recode">
<div class="title">垃圾处置记录</div> <div class="title">垃圾处置记录</div>
<div class="desc">管理和分析垃圾处置数据</div> <div class="desc">管理和分析垃圾处置数据</div>
<IxCard class="graph-card"> <IxCard class="graph-card">
<template #header> <template #header>
<div class="graph-tool"> <div class="graph-tool">
<div>垃圾处置量统计</div> <div>垃圾处置量统计</div>
<IxDatePicker type="month"/> <IxDatePicker v-model:value="monthValue" type="month" @change="renderChart"/>
</div> </div>
</template> </template>
<Charts :option="chartOption"/> <Charts :option="chartOption"/>
@ -147,31 +191,26 @@ const pagination = reactive<TablePagination>({
<template #header> <template #header>
<div class="table-tool"> <div class="table-tool">
<div>垃圾处置记录</div> <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> <IxFormItem messageTooltip>
<IxInput placeholder="请输入收纳点名称"/> <IxInput clearable control="keywords" placeholder="请输入" @clear="searchHandler"/>
</IxFormItem> </IxFormItem>
</IxTooltip>
<IxFormItem messageTooltip> <IxFormItem messageTooltip>
<IxInput placeholder="请输入收纳点名称"/> <IxButton icon="search" mode="primary" type="submit"/>
</IxFormItem> </IxFormItem>
</IxForm> </IxForm>
<IxButton icon="download" mode="primary"></IxButton> <IxButton icon="download" mode="primary" @click="exportHandler"></IxButton>
</div> </div>
</template> </template>
<IxTable :columns="columns" :dataSource="data" :pagination="pagination" autoHeight class="data-table" getKey="id"> <IxTable :columns="columns" :dataSource="datasource" :pagination="pagination" autoHeight class="data-table" getKey="id">
<template #pointName="{record}"> <template #action="{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> <IxButton class="detail-btn" icon="eye" mode="text" @click="disposeRecodeDetail?.open(record)"></IxButton>
<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>
</template> </template>
</IxTable> </IxTable>
</IxCard> </IxCard>
<DisposeRecodeDetail ref="disposeRecodeDetail" :container="disposeRecode"/>
</div> </div>
</template> </template>
@ -179,7 +218,7 @@ const pagination = reactive<TablePagination>({
.dispose-recode { .dispose-recode {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 150%;
width: 100%; width: 100%;
.title { .title {
@ -256,8 +295,12 @@ const pagination = reactive<TablePagination>({
.search-form { .search-form {
flex 6 flex 6
:deep(.ix-form-item) { :deep(.ix-form-item):nth-child(1) {
flex 2 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 { useFormGroup } from '@idux/cdk'
import { TableColumn } from '@idux/components/table' import { TableColumn } from '@idux/components/table'
import { TablePagination } from '@idux/components/table/src/types' 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[] = [ const dataSource: SelectData[] = [
{key: '', label: '所有'},
{key: 'ZhengChang', label: '正常运营'}, {key: 'ZhengChang', label: '正常运营'},
{key: 'FenZhengChang', label: '非正常运营'}, {key: 'FenZhengChang', label: '非正常运营'},
] ]
interface Data { const pagination = reactive<TablePagination>({
id: string pageIndex: 1,
pointName: string pageSize: 10,
street: string total: 0,
microdistrict: string size: 'sm',
propertyManagement: string showTotal: true,
statusTxt: string onChange(pageIndex: number, pageSize: number) {
status: 'ZhengChang' | 'FenZhengChang' 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[] = [ const columns: TableColumn[] = [
{ {
title: '名称', title: '名称',
@ -42,7 +40,7 @@ const columns: TableColumn[] = [
}, },
{ {
title: '所属街道', title: '所属街道',
dataKey: 'street', dataKey: 'streetName',
}, },
{ {
title: '所属小区', title: '所属小区',
@ -63,18 +61,40 @@ const columns: TableColumn[] = [
customCell: 'action', 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>({ function searchHandler() {
pageIndex: 1, tableSpin.value = true
pageSize: 10, TspApi.paging({
total: 100, ...formGroup.getValue(),
size: 'sm', current: pagination.pageIndex ?? 1,
showTotal: true, size: pagination.pageSize ?? 10,
onChange(pageIndex: number, pageSize: number) { })
console.log('------', pageIndex, pageSize) .then(res => {
pagination.pageIndex = pageIndex pagination.pageIndex = res.data.current
pagination.pageSize = pageSize 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> </script>
<template> <template>
@ -85,24 +105,24 @@ const pagination = reactive<TablePagination>({
<IxCard> <IxCard>
<div> <div>
<div>收纳点总数</div> <div>收纳点总数</div>
<div>24</div> <div>{{ statisticsResult.totalCount }}</div>
<div><i class="iconfont icon-arrow-up"></i>较上月增长 12%</div> <div><i class="iconfont icon-arrow-up"></i>较上月增长 {{ statisticsResult.totalCountGrowthRate }}%</div>
</div> </div>
<Iconfont name="map-pin" wrapper/> <Iconfont name="map-pin" wrapper/>
</IxCard> </IxCard>
<IxCard> <IxCard>
<div> <div>
<div>正常运营数量</div> <div>正常运营数量</div>
<div>24</div> <div>{{ statisticsResult.onlineCount }}</div>
<div><i class="iconfont icon-arrow-up"></i>较上月增长 8%</div> <div><i class="iconfont icon-arrow-up"></i>较上月增长 {{ statisticsResult.onlineCountGrowthRate }}%</div>
</div> </div>
<Iconfont name="check-circle-fill" wrapper/> <Iconfont name="check-circle-fill" wrapper/>
</IxCard> </IxCard>
<IxCard> <IxCard>
<div> <div>
<div>非正常运营数量</div> <div>非正常运营数量</div>
<div>24</div> <div>{{ statisticsResult.offlineCount }}</div>
<div><i class="iconfont icon-arrow-up"></i>较上月增长 2%</div> <div><i class="iconfont icon-arrow-up"></i>较上月增长 {{ statisticsResult.offlineCountGrowthRate }}%</div>
</div> </div>
<Iconfont name="exclamationcircle-f" wrapper/> <Iconfont name="exclamationcircle-f" wrapper/>
</IxCard> </IxCard>
@ -111,18 +131,25 @@ const pagination = reactive<TablePagination>({
<template #header> <template #header>
<div class="table-tool"> <div class="table-tool">
<div>收纳点列表</div> <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> <IxFormItem messageTooltip>
<IxInput placeholder="请输入收纳点名称"/> <IxInput clearable control="pointName" placeholder="请输入收纳点名称" @clear="searchHandler"/>
</IxFormItem>
</IxTooltip>
<IxFormItem messageTooltip>
<IxSelect :dataSource="dataSource" control="status" @change="searchHandler"/>
</IxFormItem> </IxFormItem>
<IxFormItem messageTooltip> <IxFormItem messageTooltip>
<IxSelect :dataSource="dataSource"/> <IxButton icon="search" mode="primary" type="submit"/>
</IxFormItem> </IxFormItem>
<IxButton icon="plus" mode="primary" type="submit">添加收纳点</IxButton>
</IxForm> </IxForm>
<IxButton icon="plus" mode="primary" @click="createTsp?.open()"></IxButton>
</div> </div>
</template> </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}"> <template #pointName="{record}">
<Iconfont name="mapmarker" wrapper/> <Iconfont name="mapmarker" wrapper/>
<span>{{ record.pointName }}</span> <span>{{ record.pointName }}</span>
@ -136,6 +163,7 @@ const pagination = reactive<TablePagination>({
</template> </template>
</IxTable> </IxTable>
</IxCard> </IxCard>
<CreateTsp ref="createTsp"/>
</div> </div>
</template> </template>
@ -294,9 +322,16 @@ const pagination = reactive<TablePagination>({
} }
.search-form { .search-form {
:deep(.ix-form-item) { :deep(.ix-form-item):nth-child(1) {
flex 2 flex 5
}
:deep(.ix-form-item):nth-child(2) {
flex 2
}
:deep(.ix-form-item):nth-child(3) {
flex 1
} }
:deep(.ix-button ) { :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
}
}
}