<template>
<div class="filter-wrapper" :class="layout">
<div class="filter-form-item-wrapper" v-for="(config, key) in configs" :key="key" v-show="!config.hide">
<div class="filter-form-item-control">
<div class="filter-form-item-label" :style="labelStyle">
<span v-if="config.validate" style="color: #f60;font-size: 16px">*</span>
{{ config.label }}
</div>
<div class="filter-form-item-colon">:</div>
<div class="filter-form-item-box" :class="hasSwitch ? 'has-switch' : ''">
<template v-if="config.type === 'text'">
<div class="text" :style="{ width: config.diffVal ? '50%' : '100%' }">{{ config.value }}</div>
<div class="text" :style="{ width: '50%' }" v-if="config.diffVal">{{ config.diffVal }}</div>
</template>
<template v-if="config.type.includes('input')">
<template v-if="config.type === 'input'">
<Input @on-focus="onFocus($event, config)" v-model="config.value" :placeholder="config.placeholder" :style="{ ...config.style, ...style }" :disabled="config.disabled" />
</template>
<template v-if="config.type === 'input-textarea'">
<Input type="textarea" :autosize="true" v-model="config.value" :placeholder="config.placeholder" :style="{ ...config.style, ...style }" :disabled="config.disabled" />
</template>
<template v-if="config.type === 'input-range'">
<Input v-model="config.value" placeholder="min" :style="{ ...config.style, ...style }" :disabled="config.disabled" />
~
<Input v-model="config.value2" placeholder="max" :style="{ ...config.style, ...style }" :disabled="config.disabled" />
</template>
<template v-if="config.type === 'input-number'">
<InputNumber @on-focus="onFocus($event, config)" :max="config.maxNum" :min="config.minNum" v-model="config.value" :formatter="config.formatter" :placeholder="config.placeholder" :style="{ width: '100%', ...config.style, ...style }" :disabled="config.disabled" />
</template>
<template v-if="config.type === 'input-slider-number'">
<Slider :style="{ ...config.style, ...style, marginLeft: '10px' }" :min="1" :max="10000" v-model="config.value"/>
<Input type="number" number style="width: 100px;margin-left: 10px;" v-model="config.value"/>
</template>
<template v-if="config.type === 'input-select'">
<Input v-model="config.value" :style="{ ...config.style, ...style }" :disabled="config.disabled" >
<Select style="width: 80px" v-model="config.value2" slot="prepend">
<Option
v-for="(option, index) in config.options"
v-bind:key="index"
:value="option.value"
>{{ option.label }}</Option>
</Select>
</Input>
</template>
<template v-if="config.type === 'input-range-select'">
<Select style="width: 33%" v-model="config.value" clearable @on-clear="onClear(key)">
<Option
v-for="(option, index) in config.options"
v-bind:key="index"
:value="option.value"
>{{ option.label }}</Option>
</Select>
:
<InputNumber v-model="config.value2" placeholder="min(不填为0)" :max="+config.value3 - 1" :style="{ ...config.style, ...style, width: '30%' }" :disabled="config.disabled" />
~
<InputNumber v-model="config.value3" placeholder="max(不填为0)" :min="+config.value2 + 1" :style="{ ...config.style, ...style, width: '30%' }" :disabled="config.disabled" />
</template>
</template>
<template v-if="config.type.includes('select')">
<template v-if="config.type === 'select'">
<Select
v-model="config.value"
filterable
@on-query-change="onSelectQueryChange($event, config)"
:placeholder="config.placeholder"
:disabled="config.disabled"
:style="{ ...config.style, ...style }">
<Option
v-for="(option, index) in config.options"
v-bind:key="index"
:value="option.value"
>{{ option.label }}</Option>
</Select>
</template>
<template v-if="config.type === 'select-multi'">
<Select
v-model="config.value"
multiple
filterable
:max-tag-count="1"
:disabled="config.disabled"
:placeholder="config.placeholder"
:style="{ ...config.style, ...style }"
@on-change="handleOnSelectMulti($event, key, config)">
<Option :value="config.value.length === config.options.length ? 'empty' : 'all'">
{{ config.value.length === config.options.length ? '全不选' : '全选' }}
</Option>
<Option
v-for="(option, index) in config.options"
v-bind:key="index"
:value="option.value"
>{{ option.label }}</Option>
</Select>
</template>
<template v-if="config.type === 'select-img'">
<Select
v-model="config.value"
:disabled="config.disabled"
:placeholder="config.placeholder"
:style="{ ...config.style, ...style }">
<Option
v-for="(option, index) in config.options"
v-bind:key="index"
:value="option.value"
>
<span class="option-img">
<img :src="getCodeImgUrl(option.value)">
</span>
{{ option.label }}</Option>
</Select>
<div v-if="config.value" class="config-img">
<img :src="getCodeImgUrl(config.value)">
</div>
</template>
</template>
<template v-if="config.type.includes('radio')">
<template v-if="config.type === 'radio-group'">
<RadioGroup v-model="config.value" :style="{ ...config.style, ...style }">
<Radio v-for="(item, index) in config.options" :key="index" :label='item.value' :disabled="config.disabled">{{ item.label }}</Radio>
</RadioGroup>
</template>
</template>
<template v-if="config.type.includes('checkbox')">
<template v-if="config.type === 'checkbox-group'">
<CheckboxGroup v-model="config.value" :style="{ ...config.style, ...style }">
<Checkbox v-for="(item, index) in config.options" :key="index" :label='item.value' :disabled="config.disabled">{{ item.label }}</Checkbox>
</CheckboxGroup>
</template>
</template>
<template v-if="config.type.includes('date')">
<DatePicker
transfer
v-model="config.value"
:type="config.type"
:clearable="false"
:placeholder="config.placeholder"
:style="{ ...config.style, ...style }"
:disabled="config.disabled"
/>
</template>
<template v-if="config.type.includes('upload')">
<NUpload
:value="config.value"
:multiline="config.multiline"
:paste="config.paste"
:maxNum="config.maxNum" />
</template>
<template v-if="config.type === 'button'">
<Button type="primary" :disabled="config.disabled" @click="config.click" :style="{ ...config.style, ...style }">{{ config.label }}</Button>
</template>
<div class="switch" v-if="config.disableCtl && hasSwitch">
<i-switch size="small" :value="config.disabled" @on-change="onSwitchDisabled($event, key, config)"/>
</div>
</div>
</div>
<div class="filter-form-item-tips"
:style="layout === 'vertically' ? { paddingLeft: `calc(${labelStyle.width || '25%'} + 15px)` } : { paddingLeft: `calc(100% - ${config.style ? config.style.width : '150px'})`}">
{{ state ? (!config.disabled && config.validate ? config.validate() : '') : '' }}
</div>
<div class="filter-form-item-slot" v-show="config.showSlot">
<slot :name="config.slot"></slot>
</div>
</div>
<div class="filter-form-item-wrapper" :style="layout === 'vertically' ? { paddingLeft: `calc(${labelStyle.width || '25%'} + 15px)` } : {}" v-if="hasSubmit || hasCancel">
<Button v-if="hasSubmit" style="width: 100px" type="primary" :loading="loading" @click="handleOnSubmit">{{ submitText }}</Button>
<Button v-if="hasCancel" style="width: 100px" type="default" :loading="loading" @click="handleOnCancel">{{ cancelText }}</Button>
</div>
</div>
</template>
<script lang='ts'>
import { Component, Prop, Vue, Emit, Watch } from 'vue-property-decorator'
import { FilterConfigI } from './types'
import NBeautyLabel from '@/components/dumb/N-beauty-label.vue'
import NUpload from '@/components/dumb/N-upload.vue'
import { getCodeImgUrl } from '@/utils'
@Component({ components: { NBeautyLabel, NUpload } })
export default class NFilter extends Vue {
@Prop({ type: Object, default: () => ({}) }) readonly configs!: { [propName: string]: FilterConfigI }
@Prop({ type: String, default: 'horizontally' }) readonly layout!: string
@Prop({ type: Boolean, default: false }) readonly loading!: boolean
@Prop({ type: Boolean, default: true }) readonly hasSubmit!: boolean
@Prop({ type: Boolean, default: true }) readonly hasCancel!: boolean
@Prop({ type: String, default: '查询' }) readonly submitText!: string
@Prop({ type: String, default: '重置' }) readonly cancelText!: string
@Prop({ type: Boolean, default: false }) readonly hasSwitch!: boolean
@Prop({ type: Object, default: () => ({}) }) readonly labelStyle!: {}
state: boolean = false
getCodeImgUrl = getCodeImgUrl
get style() {
if (this.layout === 'vertically') {
return { maxWidth: `calc(100% - 15px - ${ this.hasSwitch ? '60px' : '0' })` }
} else {
return {}
}
}
@Emit('on-submit')
onSubmit() {
return true
}
@Emit('on-get-submit')
onGetSubmit() {
return this.handleOnSubmit
}
@Emit('on-cancel')
handleOnCancel() {
return true
}
onFocus(event: any, config: FilterConfigI) {
config.onFocus && config.onFocus()
}
handleOnSubmit() {
this.state = true
for (const key in this.configs) {
if (this.configs.hasOwnProperty(key)) {
const item = this.configs[key]
const { hide, validate, disabled, value } = item
if (!hide && validate && validate()) {
this.$Message.warning(validate())
return false
}
}
}
this.onSubmit()
this.state = false
return true
}
onSelectQueryChange(query: string, config: FilterConfigI) {
query && config.onSelectQueryChange && config.onSelectQueryChange(query)
}
onSwitchDisabled(disabled: boolean, key: string, config: FilterConfigI) {
this.configs[key].disabled = disabled
this.$forceUpdate()
}
handleOnSelectMulti(selected: string[], key: string, config: FilterConfigI) {
if (selected.includes('all')) {
this.configs[key].value = config.options!.map((c) => c.value)
}
if (selected.includes('empty')) {
this.configs[key].value = []
}
}
onClear(key: string) {
this.$set(this.configs[key], 'value', '')
}
mounted() {
this.onGetSubmit()
if (this.hasSwitch) {
for (const key in this.configs) {
if (this.configs.hasOwnProperty(key)) {
if (this.configs[key].disableCtl) {
this.configs[key].disabled = true
}
}
}
this.$forceUpdate()
}
}
}
</script>
<style scoped lang='less'>
.filter-wrapper {
display: flex;
align-items: top;
flex-wrap: wrap;
font-size: 14px;
&.horizontally, & {
.filter-form-item-wrapper {
margin-right: 10px;
&:last-child {
margin-right: unset;
}
.filter-form-item-control {
display: flex;
align-items: center;
justify-content: space-between;
.filter-form-item-label {
font-size: 12px;
}
.filter-form-item-colon {
width: 15px;
text-align: center;
}
.filter-form-item-box {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
flex: 1;
.text {
width: 100%;
min-height: 30px;
line-height: 30px;
display: inline-block;
vertical-align: middle;
border-radius: 3px;
background-color: #f3f3f3;
text-align: center;
border: 1px solid #dcdee2;
border-radius: 4px;
word-break: break-all;
}
.option-img {
width: 20px;
height: 20px;
display: inline-block;
vertical-align: sub;
background: #439eb4;
border-radius: 3px;
padding: 3px;
img {
width: 100%;
height: 100%;
}
}
.config-img {
margin-left: 5px;
width: 30px;
height: 30px;
border-radius: 3px;
background: #439eb4;
padding: 3px;
img {
width: 100%;
height: 100%;
}
}
}
}
.filter-form-item-tips {
// height: 18px;
margin: 5px 0;
color: red;
font-size: 12px;
}
}
}
&.vertically {
padding: 10px 20px;
flex-direction: row;
.filter-form-item-wrapper {
width: 100%;
display: block;
margin-right: unset;
margin-bottom: 10px;
.filter-form-item-control {
.filter-form-item-label {
width: 25%;
text-align: right;
}
.filter-form-item-box {
width: calc(100% - 25% - 15px);
.switch {
display: inline-block;
width: 30px;
}
&.has-switch {
width: calc(100% - 25% - 15px - 30px);
}
}
}
.filter-form-item-tips {
text-align: left;
}
}
}
}
</style>
<CForm
:configs="formObj"
layout="vertically"
:has-submit="true"
:has-cancel="true"
@on-get-submit="onGetSubmit"
/>
import CForm from '@/components/c-form.vue'
component: { CForm }
data: () {
return {
formObj: { [propName: string]: FilterConfigI } = {
record_id: {
type: 'text',
label: 'Record ID',
value: '',
disabled: true
},
path_consis: {
type: 'select',
label: '路线一致性',
value: 0,
defaultVal: 0,
options: this.baseConfig.path_consis,
disableCtl: !this.isMarkJob,
validate: () => {
const { value, options } = this.formObj.path_consis
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择路线一致性'
}
},
tag: {
type: 'select',
label: '评价结论',
value: 0,
defaultVal: 0,
options: this.baseConfig.manual_tag,
disableCtl: !this.isMarkJob,
validate: () => {
const { value, options } = this.formObj.tag
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择评价结论'
}
},
expect_action: {
type: 'select',
label: '期望播报',
value: '0',
defaultVal: '',
hide: true,
options: this.baseConfig.action,
disableCtl: !this.isMarkJob,
validate: () => {
const { value, options } = this.formObj.expect_action
return value && `${value}` && options!.find((op) => op.value == value) ? '' : '请选择期望播报'
}
},
best_code: {
type: 'select-img',
label: '最佳Code',
value: 0,
defaultVal: 0,
hide: true,
options: [],
disableCtl: !this.isMarkJob,
validate: () => {
const { value, options } = this.formObj.best_code
return value && options!.find((op) => op.value == value) ? '' : '请选择最佳code'
}
},
cross_shape: {
type: 'select',
label: '路口形态',
value: 0,
defaultVal: 0,
hide: true,
options: this.baseConfig.cross_action,
disableCtl: !this.isMarkJob,
validate: () => {
const { value, options } = this.formObj.cross_shape
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择路口形态'
}
},
assist1: {
type: 'select',
label: '辅助动作1',
value: 0,
defaultVal: 0,
hide: true,
options: this.baseConfig.assist_action,
disableCtl: !this.isMarkJob,
validate: () => {
const { value, options } = this.formObj.assist1
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择辅助动作1'
}
},
assist2: {
type: 'select',
label: '辅助动作2',
value: 0,
defaultVal: 0,
hide: true,
options: this.baseConfig.assist_action,
disableCtl: !this.isMarkJob,
validate: () => {
const { value, options } = this.formObj.assist2
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择辅助动作2'
}
},
issue_type: {
type: 'select',
label: '问题类型',
value: 2,
defaultVal: 0,
options: this.baseConfig.issue_type,
hide: true,
disableCtl: !this.isMarkJob,
validate: this.is_public_evaluation && !this.isMarkJob ? undefined : () => {
const { value, options } = this.formObj.issue_type
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择问题类型'
}
},
err_level: {
type: 'select',
label: '问题等级',
value: 1,
defaultVal: 0,
options: this.baseConfig.err_level,
hide: true,
disableCtl: !this.isMarkJob,
validate: this.is_public_evaluation && !this.isMarkJob ? undefined : () => {
const { value, options } = this.formObj.err_level
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择问题等级'
}
},
issue_sub_type: {
type: 'select',
label: '问题原因',
value: 1,
defaultVal: 0,
disableCtl: !this.isMarkJob,
options: this.baseConfig.road_issue_net,
hide: true,
validate: () => {
const { value, options } = this.formObj.issue_sub_type
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择问题原因'
}
},
issue_solution: {
type: 'select',
label: '解决办法',
value: 1,
defaultVal: 0,
disableCtl: !this.isMarkJob,
options: this.baseConfig.issue_solution,
hide: true,
validate: () => {
const { value, options } = this.formObj.issue_solution
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择解决办法'
}
},
navigate: {
type: 'button',
label: '跳转强制诱导',
value: '',
click: () => {
if (!this.curDataRow) {
return
}
const { link_list, map_ver } = this.curDataRow
const { best_code, assist1, assist2, custom_mark } = this.formObj
const link_list_arr = link_list.split(',')
const inlink = link_list_arr[0]
const outlink = link_list_arr[link_list_arr.length - 1]
const passlinks = link_list_arr.slice(1, link_list_arr.length - 1).join(',')
const url = `http://10.89.110.12:8070/#/code/nav-new?` + queryString.stringify({
inlink,
outlink,
passlinks,
roadver: map_ver,
from: 'navicompass',
maincode: best_code.value,
assist: `${assist1.value},${assist2.value}`,
type: 1,
remark: custom_mark.value
})
window.open(url, '_blank')
},
hide: true
},
auto_move: {
type: 'radio-group',
label: '是否流转',
value: 0,
defaultVal: 0,
options: [{ label: '是', value: 1 }, { label: '否', value: 0 }],
hide: true,
disableCtl: !this.isMarkJob,
validate: () => {
const { value, options } = this.formObj.auto_move
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择是否流转'
}
},
issue_point: {
type: 'input',
label: '问题点',
value: '',
defaultVal: '',
placeholder: 'lng,lat',
disableCtl: !this.isMarkJob,
validate: () => this.formObj.issue_point.value ? '' : '请输入问题点',
hide: true
},
issue_status: {
type: 'select',
label: '问题状态',
value: 0,
defaultVal: 0,
options: this.baseConfig.issue_status,
disableCtl: !this.isMarkJob,
hide: true,
validate: () => {
const { value, options } = this.formObj.issue_status
return `${value}` && options!.find((op) => op.value == value) ? '' : '请选择问题状态'
}
},
custom_mark: {
type: 'input-textarea',
label: '备注',
value: '',
placeholder: '备注',
disableCtl: !this.isMarkJob,
},
mark_result: {
type: 'input-textarea',
label: '评测结果记录',
value: '',
hide: this.is_public_evaluation,
placeholder: '评测结果记录',
disabled: true
},
verify_result: {
type: 'input-textarea',
label: '质检结果记录',
value: '',
placeholder: '质检结果记录',
disabled: true
}
}
}
}