push
This commit is contained in:
48
lib/component/form/input/index.ts
Normal file
48
lib/component/form/input/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { App } from 'vue';
|
||||
import nsInput from './input.vue';
|
||||
import nsGroup from './input-group.vue';
|
||||
import nsSearch from './input-search.vue';
|
||||
import nsTextArea from './textarea.vue';
|
||||
import nsPassword from './input-password.vue';
|
||||
import nsIp from './input-ip.vue';
|
||||
import nsInputModalTable from './input-modal-table.vue';
|
||||
import nsInputAddress from './input-address.vue';
|
||||
import nsInputText from './input-text.vue';
|
||||
import nsInputNumber from './input-number.vue';
|
||||
import nsInputCity from './input-city.vue';
|
||||
import nsInputCityV2 from './input-cityV2.vue';
|
||||
import nsInputModal from './input-modal.vue';
|
||||
import nsInputColor from './input-color.vue';
|
||||
import nsFilter from './input-filter.vue';
|
||||
nsInput.Group = nsGroup;
|
||||
nsInput.Search = nsSearch;
|
||||
nsInput.Text = nsInputText;
|
||||
nsInput.TextArea = nsTextArea;
|
||||
nsInput.Password = nsPassword;
|
||||
nsInput.ip = nsIp;
|
||||
nsInput.modaltable = nsInputModalTable;
|
||||
nsInput.address = nsInputAddress;
|
||||
nsInput.number = nsInputNumber;
|
||||
nsInput.city = nsInputCity;
|
||||
nsInput.cityV2 = nsInputCityV2;
|
||||
nsInput.modal = nsInputModal;
|
||||
nsInput.color = nsInputColor;
|
||||
nsInput.Filter = nsFilter;
|
||||
export const NsInput = function (app: App) {
|
||||
app.component(nsInput.name, nsInput);
|
||||
app.component(nsInput.Text.name, nsInput.Text);
|
||||
app.component(nsInput.Group.name, nsInput.Group);
|
||||
app.component(nsInput.Search.name, nsInput.Search);
|
||||
app.component(nsInput.TextArea.name, nsInput.TextArea);
|
||||
app.component(nsInput.Password.name, nsInput.Password);
|
||||
app.component(nsInput.ip.name, nsInput.ip);
|
||||
app.component(nsInput.modaltable.name, nsInput.modaltable);
|
||||
app.component(nsInput.address.name, nsInput.address);
|
||||
app.component(nsInput.number.name, nsInput.number);
|
||||
app.component(nsInput.city.name, nsInput.city);
|
||||
app.component(nsInput.cityV2.name, nsInput.cityV2);
|
||||
app.component(nsInput.modal.name, nsInput.modal);
|
||||
app.component(nsInput.color.name, nsInput.color);
|
||||
app.component(nsInput.Filter.name, nsInput.Filter);
|
||||
return app;
|
||||
};
|
||||
223
lib/component/form/input/input-address.vue
Normal file
223
lib/component/form/input/input-address.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<div style="">
|
||||
<ns-input
|
||||
class="ns-input-area"
|
||||
:disabled="true"
|
||||
placeholder="请选择"
|
||||
@click="chose"
|
||||
:value="floorName"
|
||||
>
|
||||
<template #addonAfter>
|
||||
<a-button class="ns-area" style="background: #5cc0d1; height: 32px" @click="chose"
|
||||
>{{ addonAfter }}
|
||||
</a-button>
|
||||
</template>
|
||||
</ns-input>
|
||||
</div>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
:maskClosable="false"
|
||||
@ok="handleOkAddress"
|
||||
@cancel="cancelModel"
|
||||
width="900px"
|
||||
style="top: 3%"
|
||||
>
|
||||
<p class="title">地址选择</p>
|
||||
<ns-view-list-table v-bind="tableConfigAddress" :row-selection="rowSelectionAddress" />
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { http } from '/nerv-lib/util/http';
|
||||
|
||||
// let rowSelection='';
|
||||
export default defineComponent({
|
||||
name: 'NsInputAddress',
|
||||
// props: {
|
||||
// addonAfter: {
|
||||
// type: String,
|
||||
// default: '',
|
||||
// },
|
||||
// },
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
props: ['size', 'id', 'field', 'formModel', 'allowClear', 'value', 'addonAfter', 'disabled'],
|
||||
emits: ['change', 'blur'],
|
||||
setup() {
|
||||
const pra = {
|
||||
pageSize: 10,
|
||||
regionType: 2,
|
||||
};
|
||||
let resData = ref();
|
||||
http.get('/api/community/community/objs/Region', pra).then((res) => {
|
||||
// console.log(res.data.data);
|
||||
resData.value = res.data.data;
|
||||
});
|
||||
return { resData };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeKey: '1',
|
||||
//地址选择
|
||||
tableConfigAddress: {
|
||||
api: '/api/community/community/objs/BedChoose/Inspection?regionType=2',
|
||||
defaultPageSize: 5,
|
||||
columns: [
|
||||
{
|
||||
title: '楼栋单元',
|
||||
dataIndex: 'floorName',
|
||||
fixed: 'left',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '楼层',
|
||||
dataIndex: 'floorNum',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '房间',
|
||||
dataIndex: 'roomName',
|
||||
width: 200,
|
||||
},
|
||||
],
|
||||
formConfig: {
|
||||
schemas: [
|
||||
///api/community/community/objs/Region?regionType=2
|
||||
{
|
||||
field: 'regionUuid',
|
||||
component: 'nsSelectApi',
|
||||
label: '楼栋单元',
|
||||
// addLink: ['regionName', 'regionUuid'],
|
||||
autoAddLink: true,
|
||||
componentProps: {
|
||||
autoSelectFirst: false,
|
||||
api: '/api/community/community/objs/Region',
|
||||
params: {
|
||||
pageSize: 10,
|
||||
regionType: 2,
|
||||
},
|
||||
resultField: 'data.data',
|
||||
labelField: 'regionName',
|
||||
valueField: 'regionUuid',
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'floorUuid',
|
||||
label: '楼层',
|
||||
dynamicParams: {
|
||||
regionUuid: 'fieldLink.regionUuid.regionUuid',
|
||||
},
|
||||
defaultParams: {
|
||||
regionType: 2,
|
||||
pageSize: 10,
|
||||
},
|
||||
component: 'nsSelectApi',
|
||||
defaultValue: '',
|
||||
componentProps: {
|
||||
autoSelectFirst: false,
|
||||
api: '/api/community/community/objs/Region',
|
||||
resultField: 'data.data',
|
||||
labelField: 'regionName',
|
||||
valueField: 'regionUuid',
|
||||
// options: [
|
||||
// {
|
||||
// label: '全部',
|
||||
// value: 'all',
|
||||
// },
|
||||
// ],
|
||||
},
|
||||
},
|
||||
],
|
||||
params: {},
|
||||
},
|
||||
// columnActions: {
|
||||
// width: 0,
|
||||
// title: '',
|
||||
// actions: [
|
||||
// {
|
||||
// label: '启动',
|
||||
// name: 'start',
|
||||
// ifShow: true,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
rowKey: 'roomUuid',
|
||||
},
|
||||
visible: false,
|
||||
regionUuid: '',
|
||||
roomUuid: '',
|
||||
floorName: '',
|
||||
rootLevelCode: '',
|
||||
rowSelectionAddress: {
|
||||
type: 'radio',
|
||||
onSelect: (record: DataItem, selected: boolean, selectedRows: DataItem[]) => {
|
||||
this.roomUuid = selectedRows[0].roomUuid;
|
||||
this.queryName = selectedRows[0].floorName;
|
||||
this.spliceAddress =
|
||||
selectedRows[0].floorName +
|
||||
'-' +
|
||||
selectedRows[0].floorNum +
|
||||
'-' +
|
||||
selectedRows[0].roomName;
|
||||
// console.log(selectedRows[0]);
|
||||
// console.log("type");
|
||||
// console.log(this.floorName);
|
||||
// console.log(this.type);
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
chose() {
|
||||
this.visible = true;
|
||||
},
|
||||
cancelModel() {
|
||||
this.floorName = '';
|
||||
},
|
||||
handleOkAddress() {
|
||||
this.floorName = this.spliceAddress;
|
||||
// console.log(this.resData);
|
||||
this.resData.forEach((item) => {
|
||||
for (let val in item) {
|
||||
if (item[val] == this.queryName) {
|
||||
this.rootLevelCode = item['rootLevelCode'];
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.$emit('change', { address: this.floorName, rootLevelCode: this.rootLevelCode });
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ns-area:hover,
|
||||
:focus {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.ns-area {
|
||||
color: white;
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
background: rgba(223, 227, 233, 0.5);
|
||||
}
|
||||
|
||||
:deep(.ant-input:focus) {
|
||||
border-color: rgba(223, 227, 233, 0.5);
|
||||
border-right-width: 0px !important;
|
||||
outline: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
border-right: 0px;
|
||||
}
|
||||
|
||||
:deep(.ns-area) {
|
||||
border: 0px;
|
||||
}
|
||||
</style>
|
||||
399
lib/component/form/input/input-city.vue
Normal file
399
lib/component/form/input/input-city.vue
Normal file
@@ -0,0 +1,399 @@
|
||||
<!-- @format -->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-show="false">{{ getDetail }}</div>
|
||||
|
||||
<a-input
|
||||
placeholder="请选择"
|
||||
readonly="readonly"
|
||||
style="cursor: pointer"
|
||||
v-model:value="choiceCity"
|
||||
@click.stop="showCard">
|
||||
<template #suffix> <ns-icon name="drow" size="12" @click.stop="showCard" /></template>
|
||||
</a-input>
|
||||
<div v-show="visible" class="selectCard">
|
||||
<header class="card_header">
|
||||
<div :style="{ background: regionLevel === 1 ? '#fff' : '' }" @click.stop="check(1)"
|
||||
>省</div
|
||||
>
|
||||
<div :style="{ background: regionLevel === 2 ? '#fff' : '' }" @click.stop="check(2)"
|
||||
>市</div
|
||||
>
|
||||
<div :style="{ background: regionLevel === 3 ? '#fff' : '' }" @click.stop="check(3)"
|
||||
>区</div
|
||||
>
|
||||
</header>
|
||||
<ul>
|
||||
<li
|
||||
:class="choiceCity.split('/')[regionLevel - 1] === item.name ? 'isChoice' : ''"
|
||||
@click.stop="choiceItem(item)"
|
||||
v-for="item in data[regionLevel]"
|
||||
:key="item.code"
|
||||
>{{ item.name }}</li
|
||||
>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch, computed } from 'vue';
|
||||
import { http } from '/nerv-lib/util/http';
|
||||
export default defineComponent({
|
||||
name: 'NsInputCity',
|
||||
props: {
|
||||
value: {},
|
||||
defaultHomeAddress: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
defaultProvinceCode: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
defaultCityCode: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
defaultAreaCode: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
api: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isSeparate: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['change'],
|
||||
setup(props, context) {
|
||||
const visible = ref(false);
|
||||
let choiceCity = ref('');
|
||||
let province = ref([]); //省
|
||||
let city = ref([]); //市
|
||||
let area = ref([]); //区
|
||||
let provinceCode = ref('');
|
||||
let cityCode = ref('');
|
||||
let areaCode = ref('');
|
||||
let addre = props.defaultHomeAddress.split('/');
|
||||
if (props.defaultHomeAddress) {
|
||||
context.emit('change', [
|
||||
addre[0],
|
||||
addre[1],
|
||||
addre[2],
|
||||
props.defaultProvinceCode,
|
||||
props.defaultCityCode,
|
||||
props.defaultAreaCode,
|
||||
props.defaultHomeAddress,
|
||||
]);
|
||||
}
|
||||
|
||||
choiceCity.value = props.defaultHomeAddress || '';
|
||||
// province.value = addRe[0] || '';
|
||||
// city.value = addRe[1] || '';
|
||||
// area.value = addRe[2] || '';
|
||||
provinceCode.value = props.defaultProvinceCode || '';
|
||||
cityCode.value = props.defaultCityCode || '';
|
||||
areaCode.value = props.defaultAreaCode || '';
|
||||
|
||||
const data = ref([]);
|
||||
data.value[1] = province.value;
|
||||
const regionLevel = ref(1);
|
||||
const showCard = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
const closeCard = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
const check = (e) => {
|
||||
regionLevel.value = e;
|
||||
};
|
||||
const initData = (regionLevel: Number, code?: Number) => {
|
||||
switch (regionLevel) {
|
||||
case 1:
|
||||
province.value = [];
|
||||
|
||||
break;
|
||||
case 2:
|
||||
city.value = [];
|
||||
data.value[3] = [];
|
||||
break;
|
||||
case 3:
|
||||
area.value = [];
|
||||
break;
|
||||
}
|
||||
async function getDate() {
|
||||
try {
|
||||
let date = {};
|
||||
date.regionLevel = regionLevel;
|
||||
code ? (date.parentCode = code) : '';
|
||||
const res = await http.get(props.api || '/api/pension/pension/objs/BaseArea', date);
|
||||
if (res.success) {
|
||||
switch (regionLevel) {
|
||||
case 1:
|
||||
province.value = res.data;
|
||||
break;
|
||||
case 2:
|
||||
city.value = res.data;
|
||||
break;
|
||||
case 3:
|
||||
area.value = res.data;
|
||||
break;
|
||||
}
|
||||
data.value[regionLevel] = res.data;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
getDate();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [
|
||||
props.defaultHomeAddress,
|
||||
props.defaultProvinceCode,
|
||||
props.defaultCityCode,
|
||||
props.defaultAreaCode,
|
||||
],
|
||||
([defaultHomeAddress, defaultProvinceCode, defaultCityCode, defaultAreaCode]) => {
|
||||
if (defaultHomeAddress) {
|
||||
choiceCity.value = defaultHomeAddress;
|
||||
}
|
||||
if (defaultProvinceCode) {
|
||||
regionLevel.value = 1;
|
||||
provinceCode.value = defaultProvinceCode;
|
||||
regionLevel.value = 2;
|
||||
initData(regionLevel.value, provinceCode.value);
|
||||
}
|
||||
if (defaultCityCode) {
|
||||
regionLevel.value = 2;
|
||||
cityCode.value = defaultCityCode;
|
||||
initData(regionLevel.value, provinceCode.value);
|
||||
regionLevel.value = 3;
|
||||
initData(regionLevel.value, cityCode.value);
|
||||
}
|
||||
if (defaultAreaCode) {
|
||||
areaCode.value = defaultAreaCode;
|
||||
regionLevel.value = 3;
|
||||
initData(regionLevel.value, cityCode.value);
|
||||
}
|
||||
},
|
||||
);
|
||||
const getDetail = computed(() => {
|
||||
const { value } = props;
|
||||
if (!value) {
|
||||
regionLevel.value = 1;
|
||||
choiceCity.value = '';
|
||||
provinceCode.value = '';
|
||||
cityCode.value = '';
|
||||
areaCode.value = '';
|
||||
//test.value = [];
|
||||
} else {
|
||||
}
|
||||
// choiceCity.value = value;
|
||||
return value;
|
||||
});
|
||||
watch(
|
||||
() => props.value,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
data.value[2] = [];
|
||||
data.value[3] = [];
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
return {
|
||||
getDetail,
|
||||
choiceCity,
|
||||
showCard,
|
||||
closeCard,
|
||||
regionLevel,
|
||||
check,
|
||||
visible,
|
||||
initData,
|
||||
province,
|
||||
city,
|
||||
area,
|
||||
provinceCode,
|
||||
cityCode,
|
||||
areaCode,
|
||||
data,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
val
|
||||
? document.body.addEventListener('click', this.closeCard)
|
||||
: document.body.removeEventListener('click', this.closeCard);
|
||||
},
|
||||
choiceCity(val) {
|
||||
let info = { label: '', provinceCode: '', cityCode: '', areaCode: '' };
|
||||
let addressArr = this.defaultHomeAddress.split('/');
|
||||
info.provinceCode = this.provinceCode;
|
||||
info.cityCode = this.cityCode;
|
||||
info.areaCode = this.areaCode;
|
||||
if (this.isSeparate) {
|
||||
info.label = val.split('/');
|
||||
this.$emit('change', [
|
||||
info.label[0],
|
||||
info.label[1],
|
||||
info.label[2],
|
||||
info.provinceCode,
|
||||
info.cityCode,
|
||||
info.areaCode,
|
||||
val,
|
||||
]);
|
||||
} else {
|
||||
info.label = val;
|
||||
this.$emit('change', [info.label, info.provinceCode, info.cityCode, info.areaCode, val]);
|
||||
}
|
||||
},
|
||||
// regionLevel(val) {
|
||||
// switch (val) {
|
||||
// case 1:
|
||||
// this.data = this.province;
|
||||
// break;
|
||||
// case 2:
|
||||
// this.data = this.city;
|
||||
// // this.area = [];
|
||||
// break;
|
||||
// case 3:
|
||||
// this.data = this.area;
|
||||
// break;
|
||||
// }
|
||||
// },
|
||||
},
|
||||
created() {
|
||||
this.initData(this.regionLevel);
|
||||
},
|
||||
methods: {
|
||||
// initData(regionLevel: Number, code?: Number) {
|
||||
// const that = this;
|
||||
// switch (regionLevel) {
|
||||
// case 1:
|
||||
// that.province = [];
|
||||
// break;
|
||||
// case 2:
|
||||
// that.city = [];
|
||||
// break;
|
||||
// case 3:
|
||||
// that.area = [];
|
||||
// break;
|
||||
// }
|
||||
// async function getDate() {
|
||||
// try {
|
||||
// let date = {};
|
||||
// date.regionLevel = regionLevel;
|
||||
// code ? (date.parentCode = code) : '';
|
||||
// const res = await http.get('/api/pension/pension/objs/BaseArea', date);
|
||||
// if (res.success) {
|
||||
// switch (that.regionLevel) {
|
||||
// case 1:
|
||||
// that.province = res.data;
|
||||
// break;
|
||||
// case 2:
|
||||
// that.city = res.data;
|
||||
// break;
|
||||
// case 3:
|
||||
// that.area = res.data;
|
||||
// break;
|
||||
// }
|
||||
// that.data = res.data;
|
||||
// }
|
||||
// } catch (err) {
|
||||
// console.log(err);
|
||||
// }
|
||||
// }
|
||||
// getDate();
|
||||
// },
|
||||
choiceItem(e: any) {
|
||||
switch (this.regionLevel) {
|
||||
case 1:
|
||||
this.choiceCity = e.name;
|
||||
this.provinceCode = e.code;
|
||||
this.cityCode = '';
|
||||
this.areaCode = '';
|
||||
this.initData(2, e.code);
|
||||
this.regionLevel = 2;
|
||||
break;
|
||||
case 2:
|
||||
this.choiceCity = this.choiceCity.split('/')[0] + `/${e.name}`;
|
||||
this.cityCode = e.code;
|
||||
this.areaCode = '';
|
||||
this.initData(3, e.code);
|
||||
this.regionLevel = 3;
|
||||
break;
|
||||
case 3:
|
||||
this.choiceCity =
|
||||
this.choiceCity.split('/')[0] + '/' + this.choiceCity.split('/')[1] + `/${e.name}`;
|
||||
this.areaCode = e.code;
|
||||
this.closeCard();
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.selectCard {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
width: 300px;
|
||||
height: 180px;
|
||||
transform: translateY(5px);
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
|
||||
animation: move-down 0.5s;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
.card_header {
|
||||
width: 100%;
|
||||
height: 26px;
|
||||
background: rgb(238, 238, 238);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #ccc;
|
||||
div {
|
||||
text-align: center;
|
||||
width: 34%;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
overflow: auto;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
li {
|
||||
line-height: 30px;
|
||||
cursor: pointer;
|
||||
padding-left: 10px;
|
||||
&:hover {
|
||||
background-color: rgba(3, 141, 218, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.isChoice {
|
||||
color: rgb(3, 141, 218);
|
||||
}
|
||||
@keyframes move-down {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
height: 0px;
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
height: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
366
lib/component/form/input/input-cityV2.vue
Normal file
366
lib/component/form/input/input-cityV2.vue
Normal file
@@ -0,0 +1,366 @@
|
||||
<!-- @format -->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-input
|
||||
placeholder="请选择"
|
||||
readonly="readonly"
|
||||
style="cursor: pointer"
|
||||
v-model:value="choiceCityName"
|
||||
@click.stop="showCard">
|
||||
<template #suffix> <ns-icon name="drow" size="12" @click.stop="showCard" /></template>
|
||||
</a-input>
|
||||
<div v-show="visible" class="selectCard">
|
||||
<header class="card_header">
|
||||
<div :style="{ background: regionLevel === 1 ? '#fff' : '' }" @click.stop="check(1)"
|
||||
>省</div
|
||||
>
|
||||
<div :style="{ background: regionLevel === 2 ? '#fff' : '' }" @click.stop="check(2)"
|
||||
>市</div
|
||||
>
|
||||
<div :style="{ background: regionLevel === 3 ? '#fff' : '' }" @click.stop="check(3)"
|
||||
>区</div
|
||||
>
|
||||
</header>
|
||||
<ul>
|
||||
<li
|
||||
:class="choiceCity[regionLevel - 1] === item.name ? 'isChoice' : ''"
|
||||
@click.stop="choiceItem(item)"
|
||||
v-for="item in data[regionLevel]"
|
||||
:key="item.code"
|
||||
>{{ item.name }}</li
|
||||
>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { cloneDeep, isArray, isEqual, isObject } from 'lodash-es';
|
||||
import { defineComponent, ref, watch, computed, onMounted } from 'vue';
|
||||
import { http } from '/nerv-lib/util/http';
|
||||
export default defineComponent({
|
||||
name: 'NsInputCityV2',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
api: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
fieldMap: {
|
||||
type: Object || Array,
|
||||
default: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
formModel: {
|
||||
type: Object || Array,
|
||||
},
|
||||
isSeparate: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['change'],
|
||||
setup(props, { emit }) {
|
||||
// console.log(props);
|
||||
const visible = ref(false);
|
||||
const hasload = ref(false);
|
||||
let choiceCity = ref<any[]>([]);
|
||||
let choiceCityName = ref('');
|
||||
let province = ref([]); //省
|
||||
let city = ref([]); //市
|
||||
let area = ref([]); //区
|
||||
const data = ref([]);
|
||||
data.value[1] = province.value;
|
||||
const regionLevel = ref(1);
|
||||
|
||||
const showCard = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
const closeCard = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
const check = (e) => {
|
||||
regionLevel.value = e;
|
||||
initData(regionLevel.value);
|
||||
};
|
||||
|
||||
const choiceItem = (e: any) => {
|
||||
let currObject = [
|
||||
{ name: 0, code: 3, enmpitIndex: [1, 2, 4, 5] },
|
||||
{ name: 1, code: 4, enmpitIndex: [2, 5] },
|
||||
{ name: 2, code: 5, enmpitIndex: [] },
|
||||
];
|
||||
let enmpitIndex = [[1, 2, 4, 5], [2, 5], []][regionLevel.value - 1];
|
||||
let cureleve: { name: number; code: number; enmpitIndex: any[] } =
|
||||
currObject[regionLevel.value - 1];
|
||||
const update = () => {
|
||||
let copecity = cloneDeep(choiceCity.value);
|
||||
Object.keys(cureleve).forEach((el: string) => {
|
||||
let index = cureleve[el];
|
||||
let value = e[el];
|
||||
copecity[index] = value;
|
||||
});
|
||||
enmpitIndex.forEach((el) => {
|
||||
copecity[el] = null;
|
||||
});
|
||||
choiceCity.value = [...copecity];
|
||||
};
|
||||
switch (regionLevel.value) {
|
||||
case 1:
|
||||
update();
|
||||
regionLevel.value = 2;
|
||||
initData(2, e.code);
|
||||
break;
|
||||
case 2:
|
||||
update();
|
||||
regionLevel.value = 3;
|
||||
initData(3, e.code);
|
||||
break;
|
||||
case 3:
|
||||
update();
|
||||
closeCard();
|
||||
break;
|
||||
}
|
||||
};
|
||||
const initData = (regionLevel: Number, code?: Number) => {
|
||||
let parentcode = '';
|
||||
switch (regionLevel) {
|
||||
case 1:
|
||||
province.value = [];
|
||||
data.value[2] = [];
|
||||
data.value[3] = [];
|
||||
break;
|
||||
case 2:
|
||||
parentcode = choiceCity.value[3];
|
||||
city.value = [];
|
||||
data.value[3] = [];
|
||||
break;
|
||||
case 3:
|
||||
parentcode = choiceCity.value[4];
|
||||
area.value = [];
|
||||
break;
|
||||
}
|
||||
async function getDate() {
|
||||
try {
|
||||
let body: Recordable = {};
|
||||
body['regionLevel'] = regionLevel;
|
||||
if (parentcode || regionLevel == 1) {
|
||||
body.parentCode = parentcode;
|
||||
const res = await http.get(props.api || '/api/pension/pension/objs/BaseArea', body);
|
||||
if (res.success) {
|
||||
switch (regionLevel) {
|
||||
case 1:
|
||||
province.value = res.data;
|
||||
break;
|
||||
case 2:
|
||||
city.value = res.data;
|
||||
break;
|
||||
case 3:
|
||||
area.value = res.data;
|
||||
break;
|
||||
}
|
||||
data.value[regionLevel] = res.data;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
getDate();
|
||||
};
|
||||
const updateValue = (newv) => {
|
||||
let leve = 0;
|
||||
let code = '';
|
||||
let cityMap = newv;
|
||||
newv.forEach((el, i) => {
|
||||
if (el) {
|
||||
leve = i;
|
||||
}
|
||||
});
|
||||
if (!props.isSeparate) {
|
||||
regionLevel.value = leve > 0 ? leve : 1;
|
||||
} else {
|
||||
regionLevel.value = leve > 2 ? leve - 2 : 1;
|
||||
}
|
||||
regionLevel.value == 1 ? '' : (code = cityMap[leve - 1]);
|
||||
if (!props.isSeparate) {
|
||||
newv.forEach((el, i) => {
|
||||
if (i === 0) {
|
||||
let cityNameList = el.split('/');
|
||||
cityNameList.forEach((item, index) => {
|
||||
cityMap[index] = item;
|
||||
});
|
||||
} else {
|
||||
cityMap[i + 3] = el;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cityMap = newv;
|
||||
}
|
||||
choiceCity.value = cityMap;
|
||||
initData(regionLevel.value, code);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initData(regionLevel.value);
|
||||
if (isArray(props.value) && props.value.length) {
|
||||
updateValue(props.value);
|
||||
}
|
||||
if (props.defaultValue.length) {
|
||||
updateValue(props.defaultValue);
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => choiceCity.value,
|
||||
(newv, oldv) => {
|
||||
if (newv) {
|
||||
let list = [];
|
||||
newv.forEach((el, i) => {
|
||||
if (el && i < 3) {
|
||||
list.push(el);
|
||||
}
|
||||
});
|
||||
choiceCityName.value = list.join('/');
|
||||
if (!props.isSeparate) {
|
||||
emit('change', [choiceCityName, ...newv.slice(1)]);
|
||||
} else {
|
||||
emit('change', newv);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newv, oldv) => {
|
||||
if (!isEqual(newv, oldv) && !isEqual(newv, choiceCity.value)) {
|
||||
updateValue(newv);
|
||||
}
|
||||
if (props.value?.length > 5 && props.value[4]) {
|
||||
regionLevel.value = 3;
|
||||
initData(regionLevel.value);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.formModel,
|
||||
(newv, oldv) => {
|
||||
if (newv && !props.value.length && props.fieldMap.length && !hasload.value) {
|
||||
let cityMap = [];
|
||||
let leve = 0;
|
||||
let code = '';
|
||||
props.fieldMap.forEach((el, i) => {
|
||||
cityMap[i] = newv[el];
|
||||
if (newv[el]) {
|
||||
leve = i;
|
||||
}
|
||||
});
|
||||
if (!props.isSeparate) {
|
||||
regionLevel.value = leve > 0 ? leve : 1;
|
||||
} else {
|
||||
regionLevel.value = leve > 2 ? leve - 2 : 1;
|
||||
}
|
||||
regionLevel.value == 1 ? '' : (code = cityMap[leve - 1]);
|
||||
// choiceCity.value = cityMap;
|
||||
updateValue(cityMap, code);
|
||||
hasload.value = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => visible.value,
|
||||
(val) => {
|
||||
val
|
||||
? document.body.addEventListener('click', closeCard)
|
||||
: document.body.removeEventListener('click', closeCard);
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
choiceCity,
|
||||
showCard,
|
||||
closeCard,
|
||||
regionLevel,
|
||||
check,
|
||||
choiceItem,
|
||||
visible,
|
||||
province,
|
||||
city,
|
||||
area,
|
||||
data,
|
||||
choiceCityName,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.selectCard {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
width: 300px;
|
||||
height: 180px;
|
||||
transform: translateY(5px);
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
|
||||
animation: move-down 0.5s;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
.card_header {
|
||||
width: 100%;
|
||||
height: 26px;
|
||||
background: rgb(238, 238, 238);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #ccc;
|
||||
div {
|
||||
text-align: center;
|
||||
width: 34%;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
overflow: auto;
|
||||
list-style: none;
|
||||
padding: 10px;
|
||||
li {
|
||||
line-height: 30px;
|
||||
cursor: pointer;
|
||||
padding-left: 10px;
|
||||
&:hover {
|
||||
background-color: rgba(3, 141, 218, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.isChoice {
|
||||
color: rgb(3, 141, 218);
|
||||
}
|
||||
@keyframes move-down {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
height: 0px;
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
height: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
52
lib/component/form/input/input-color.vue
Normal file
52
lib/component/form/input/input-color.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<a-input>
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
|
||||
<slot :name="item" v-bind="data || {}"> </slot>
|
||||
</template>
|
||||
<template #suffix>
|
||||
<div class="colorRange">
|
||||
<input class="colorInput" v-model="color" type="color" />
|
||||
</div>
|
||||
</template>
|
||||
</a-input>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInputColor',
|
||||
emits: ['change', 'validateChange'],
|
||||
setup(props, { attrs, emit }) {
|
||||
const color = ref(attrs.value);
|
||||
watch(
|
||||
() => color.value,
|
||||
(val) => {
|
||||
emit('change', val);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
return {
|
||||
color,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.colorRange {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
width: 32px;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAv1JREFUOE9dk01oXFUUx///e+97M0km7cRO2rRJ05lF60JKErIoAcFm4cZSa8GNFaRuXAlp7bq2XbgSLOlCV0IXLgQ3CuJCkBTcdVHjSkHrzKK0GtPOR+a9N+/djyPzihi8cLhcOL/fOVzOIf53mr8snLWu8YZT8xecX2i6cFxcmP+5CEe3rT14C2trnf0I/32cbdfrT110IwtmI/cHaMNx2NCCC01Y34STE3CuIciizdBTt3Cx1RuzpWAMO3IrD2Y58UpSV2UeFsX6kyzCi+XtfAthNA3JDKQw27Jr1/Fuq1cKzj+e/qTw6mo6hr3hwBqkYU5yd5q5X0IRTovLFylJBcgiSB4JrdoMl2av8vKTajOjbudBYeQVEq8xFvTcAaR2FZl7Gbk7A580BHsVPhcYwGpIodb53m5814LvOK+Yi8K4i74z2B1V2S1WMbBvSp6uQZ7VKMMYGEUQa2QsgOMmP+ipB07UiguAE4WRUBIXcSeP0UlP4e/kQymeLFG6U4LEEIWBOAV4BQZ2eH0AEQBCSgigB2EDMfQavyeH8eODe4L2POM0grKE8UqMB7WH6ADyToJAgIoUDYEGOEHgkKac6gr/WtnGDwtL8kcD7E+Uf1h278cxBr9I2YmIEzGBCgQ1RTSUyGwB1H4D8erH8ufu23w4MycPD5M7NUhvEkhjMDf4id8VuF0BN6oAaxTUFVArKPFTsPpIwMvvo//rRQz5ErrxLB7NKOzUgN4UMKziLu9bvBKTW5MQTmtKlAmjIRB1gXgH4LVLyO6fQ4KTyLEoiT7EZ1NGdmvg4xitcpDagtuTgVdMAahUYIaE6Usp0R+dR/79a5KiyRzHYHFEClVnv1LZXM14pRR0BXXJucVMllUKmAQwexQzEKrP1sV+dY4jWUDBoyhkFh4vbDscWW+Bz0e5lHRRVxY3dMINnQI6AfSeUH15BuHzCzIKcyyrY+ZOgombLaz8t0z71zNro4kBbuqUyzqRJfXtMsKnr3esO/a1DQe/mcZb9/bn/wP2RI8xMJtt1AAAAABJRU5ErkJggg==)
|
||||
no-repeat;
|
||||
background-size: 16px 16px;
|
||||
background-position: center;
|
||||
.colorInput {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
872
lib/component/form/input/input-filter-input-showSpan.vue
Normal file
872
lib/component/form/input/input-filter-input-showSpan.vue
Normal file
@@ -0,0 +1,872 @@
|
||||
<!-- eslint-disable vue/v-on-event-hyphenation -->
|
||||
<template>
|
||||
<!-- 选中后展示 -->
|
||||
<div
|
||||
class="filter-array-group"
|
||||
:style="focusObj.divFocus ? '' : 'display:inline-flex'"
|
||||
v-if="filterArrayShow.length > 0"
|
||||
@click.stop="focusShowDiv">
|
||||
<div
|
||||
v-for="(item, index) in filterArrayShow"
|
||||
:key="index"
|
||||
:class="[
|
||||
item.isEdit ? 'filter-array-span-edit' : 'filter-array-span',
|
||||
!focusObj.divFocus ? 'filter-array-span-nofocus' : '',
|
||||
]"
|
||||
:style="
|
||||
focusObj.divFocus
|
||||
? item.isEdit
|
||||
? 'padding-right: 0px;'
|
||||
: 'padding-right: 20px;'
|
||||
: 'padding-right: 4px;line-height:14px'
|
||||
">
|
||||
<div
|
||||
v-if="item.isEdit && item.item.componentType == 'NsInput'"
|
||||
class="filter-array-span-edit-div"
|
||||
:style="'width:' + item.inputWidth + 'px'">
|
||||
<!-- @showFilterLabel="showFilterLabel" -->
|
||||
<ns-input-filter-input
|
||||
ref="spanItem"
|
||||
:value="item.spanFilterValue"
|
||||
:chooseItem="item"
|
||||
@change="nsSpanInputChange"
|
||||
@pressEnter="spanPressEnter"
|
||||
@chooseIsEmptyLabel="chooseIsEmptyLabel"
|
||||
@keydown="onKeyUpDeleteSpan"
|
||||
:spanIndex="index" />
|
||||
</div>
|
||||
<div
|
||||
v-else-if="item.isEdit && item.item.componentType == 'NsSingleSearch'"
|
||||
class="filter-array-span-edit-div"
|
||||
:style="'width:' + item.inputWidth + 'px'">
|
||||
<ns-input-filter-singleSearch
|
||||
ref="spanItem"
|
||||
:value="item.spanFilterValue"
|
||||
:chooseItem="item"
|
||||
:optionsValue="item.optionsValue"
|
||||
@change="nsInputChange"
|
||||
@pressEnter="spanPressEnter"
|
||||
@chooseOptions="chooseSingleOptions"
|
||||
@keydown="onKeyUpDeleteSpan"
|
||||
:spanIndex="index" />
|
||||
</div>
|
||||
<div
|
||||
v-else-if="item.isEdit && item.item.componentType == 'NsMultiSearch'"
|
||||
class="filter-array-span-edit-div"
|
||||
:style="'width:' + item.inputWidth + 'px'">
|
||||
<ns-input-filter-multiSearch
|
||||
ref="spanItem"
|
||||
:value="item.spanFilterValue"
|
||||
:chooseItem="item"
|
||||
:optionsValue="item.optionsValue"
|
||||
@pressEnter="spanPressEnter"
|
||||
@change="nsInputChange"
|
||||
@canel="canel"
|
||||
@chooseOptions="chooseMultiOptions"
|
||||
@keydown="onKeyUpDeleteSpan"
|
||||
:spanIndex="index" />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
@click.stop="editSpan(item, index)"
|
||||
:class="[focusObj.divFocus ? 'span-group-div' : 'span-noFcous-div']">
|
||||
<span class="filter-array-span-1"> {{ item.label }}: </span>
|
||||
<span class="filter-array-span-2">
|
||||
{{ item.value }}
|
||||
</span>
|
||||
<span
|
||||
class="close-span close-icon"
|
||||
v-if="focusObj.divFocus"
|
||||
@click.stop="delSpan(item, index)">
|
||||
</span>
|
||||
</div>
|
||||
<!-- 下拉过滤标签 -->
|
||||
<!-- <div v-if="showFilterLabelIndex == index && item.isEdit" class="ns-input-filter-content">
|
||||
<div class="ns-input-filter-list">
|
||||
<ul v-for="(item, index) in realTimeFilterList" :key="index" class="filter-list-text">
|
||||
<li v-if="index == 0" class="is-disabled"> 选择资源属性进行过滤</li>
|
||||
<li @click="chooseLabel(item, index)">
|
||||
{{ item.label }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, watch } from 'vue';
|
||||
import { NsMessage } from '/nerv-lib/component/message';
|
||||
import NsInputFilterInput from './input-filter-input.vue';
|
||||
import NsInputFilterSingleSearch from './input-filter-singleSearch.vue';
|
||||
import NsInputFilterMultiSearch from './input-filter-multiSearch.vue';
|
||||
import * as utils from './input-filter-util';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInputFilterInputShowSpan',
|
||||
components: {
|
||||
'ns-input-filter-input': NsInputFilterInput,
|
||||
'ns-input-filter-singleSearch': NsInputFilterSingleSearch,
|
||||
'ns-input-filter-multiSearch': NsInputFilterMultiSearch,
|
||||
},
|
||||
props: {
|
||||
filterList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
filterArrayShowList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
selectedFilterList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
focusObjProp: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
filterArrayList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
defaultFilterObj: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
'change',
|
||||
'spanPressEnter',
|
||||
'filterformatconvert',
|
||||
'searchFilter',
|
||||
'processSelected',
|
||||
'chooseSingleOptions',
|
||||
'chooseMultiOptions',
|
||||
],
|
||||
setup(props, ctx) {
|
||||
// console.log('props')
|
||||
// console.log(props)
|
||||
//选中后展示的标签与值
|
||||
const filterArrayShow = ref(props.filterArrayShowList);
|
||||
|
||||
// const showFilterLabelIndex = ref(-1);
|
||||
//下拉标签数据,props.filterList
|
||||
const filterListOperation = ref(props.filterList);
|
||||
//默认搜索标签
|
||||
const defaultFilter = ref(props.defaultFilterObj);
|
||||
|
||||
//各焦点控制值
|
||||
const focusObj = ref(props.focusObjProp);
|
||||
|
||||
const spanItem = ref(null);
|
||||
|
||||
//当前选中的标签
|
||||
const chooseItem = ref({
|
||||
isChoose: false,
|
||||
isEdit: false,
|
||||
componentType: '',
|
||||
item: {},
|
||||
empty: false,
|
||||
notEmpty: false,
|
||||
isShowEmptyDiv: false,
|
||||
showEmptyOption: [],
|
||||
});
|
||||
|
||||
//input输入框绑定值
|
||||
let filterValue = ref();
|
||||
|
||||
//计算input宽度
|
||||
let inputWidth = computed(() => {
|
||||
if (typeof filterValue.value == 'undefined') {
|
||||
return 8 + 'px';
|
||||
}
|
||||
return DataLength(filterValue.value) + 'px';
|
||||
});
|
||||
|
||||
//传入后端/表格filter字段
|
||||
let filterArray = ref(props.filterArrayList);
|
||||
|
||||
/**
|
||||
* 选中了某个标签
|
||||
* 1.判断组件类型--确定展示形态
|
||||
* 2.是否不为空--是否要给用户选择
|
||||
* @param item
|
||||
* @param index
|
||||
*/
|
||||
function chooseLabel(item: any) {
|
||||
if (
|
||||
item.componentType != 'NsInput' &&
|
||||
item.componentType != 'NsSingleSearch' &&
|
||||
item.componentType != 'NsMultiSearch'
|
||||
) {
|
||||
NsMessage.error('暂未支持' + item.componentType + '形态!');
|
||||
return;
|
||||
}
|
||||
focusObj.value.focus = true;
|
||||
filterValue.value = String(item.label + ':');
|
||||
focusObj.value.filterLabelFocus = false;
|
||||
pushChooseItem(item, true);
|
||||
}
|
||||
|
||||
//处理是否空值选中信息
|
||||
function pushChooseItem(item: any, isShowDiv: boolean) {
|
||||
//将选中的信息放在chooseItem中
|
||||
chooseItem.value.item = item;
|
||||
chooseItem.value.componentType =
|
||||
typeof item.componentType != 'undefined' ? item.componentType : 'NsInput';
|
||||
chooseItem.value.isChoose = isShowDiv;
|
||||
//判断空值
|
||||
if (typeof item.empty != 'undefined') chooseItem.value.empty = item.empty;
|
||||
if (typeof item.notEmpty != 'undefined') chooseItem.value.notEmpty = item.notEmpty;
|
||||
if (chooseItem.value.empty) {
|
||||
chooseItem.value.isShowEmptyDiv = isShowDiv;
|
||||
chooseItem.value.showEmptyOption.push({ label: '空', value: 'empty' });
|
||||
}
|
||||
if (chooseItem.value.notEmpty) {
|
||||
chooseItem.value.isShowEmptyDiv = isShowDiv;
|
||||
chooseItem.value.showEmptyOption.push({ label: '不为空', value: 'notEmpty' });
|
||||
}
|
||||
}
|
||||
|
||||
//监听输入内容
|
||||
function valueChange(text: { data: string }) {
|
||||
// if (text.data !== null) {
|
||||
// DataLength(text.data);
|
||||
// }
|
||||
if (typeof text.data != 'undefined' && text.data == ':') {
|
||||
let label = filterValue.value.split(':');
|
||||
let chooseItem = filterListOperation.value.filter(function (elem) {
|
||||
return elem.label == label[0];
|
||||
});
|
||||
if (chooseItem.length > 0 && label.length <= 2) {
|
||||
chooseLabel(chooseItem[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//监听键盘删除
|
||||
function onKeyUpDelete(event: { key: string }) {
|
||||
if (event.key == 'Backspace') {
|
||||
if (typeof filterValue.value != 'undefined' && filterValue.value != '') {
|
||||
return;
|
||||
}
|
||||
filterArrayShow.value.pop();
|
||||
filterArray.value.pop();
|
||||
selectedFilter.value.pop();
|
||||
}
|
||||
}
|
||||
|
||||
//span图标删除
|
||||
function onKeyUpDeleteSpan(index: number, isEmit: boolean) {
|
||||
// debugger;
|
||||
filterArrayShow.value.splice(index, 1);
|
||||
filterArray.value.splice(index, 1);
|
||||
selectedFilter.value.splice(index, 1);
|
||||
if (typeof isEmit == 'undefined' || isEmit) {
|
||||
emitData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有正在修改的span,点击搜索icon时默认已完成输入
|
||||
*/
|
||||
function isEditSpanIng() {
|
||||
filterArrayShow.value.forEach((item: any, index) => {
|
||||
if (item.isEdit) {
|
||||
if (typeof item.changeValue != 'undefined') {
|
||||
if (
|
||||
(item.spanFilterValue.indexOf(':空') != -1 ||
|
||||
item.spanFilterValue.indexOf(':不为空') != -1) &&
|
||||
item.value == ''
|
||||
) {
|
||||
let emptyItem = {
|
||||
label: item.spanFilterValue.indexOf(':空') != -1 ? '空' : '不为空',
|
||||
value: item.spanFilterValue.indexOf(':空') != -1 ? 'empty' : 'notEmpty',
|
||||
};
|
||||
chooseIsEmptyLabel(emptyItem, item.changeValue);
|
||||
} else {
|
||||
spanPressEnter(item.changeValue, index, true, false);
|
||||
}
|
||||
} else {
|
||||
spanPressEnter(item.spanFilterValue, index, true, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* span标签回车搜索
|
||||
* 需要改对应标签的值
|
||||
* 1.filterArrayShow 2.filterArray 3.selectedFilter(当前选中的标签被删除至默认标签时)
|
||||
* @param value
|
||||
* @param index
|
||||
* @param isChange true仅修改/false修改且搜索
|
||||
*/
|
||||
function spanPressEnter(
|
||||
value: string,
|
||||
index: number,
|
||||
isChange: boolean,
|
||||
isEditCheck: boolean,
|
||||
) {
|
||||
let isEditArray = filterArrayShow.value.filter(function (elem: any) {
|
||||
return elem.isEdit == true;
|
||||
});
|
||||
if (isEditArray.length > 1 && isEditCheck) {
|
||||
isEditSpanIng();
|
||||
return;
|
||||
}
|
||||
//还是以key:value形式
|
||||
if (value.indexOf(':') != -1) {
|
||||
let label = value.split(':')[0];
|
||||
let sliceIndex = value.indexOf(':') + 1;
|
||||
if (value.split(':')[1] == '') {
|
||||
return;
|
||||
}
|
||||
//原标签修改
|
||||
if (label == filterArrayShow.value[index].label) {
|
||||
filterArrayShow.value[index].value = value.slice(sliceIndex);
|
||||
let checkRes = checkData([selectedFilter.value[index]], value.slice(sliceIndex));
|
||||
searchFilter([selectedFilter.value[index]], value.slice(sliceIndex), checkRes, index);
|
||||
filterArrayShow.value[index].isEdit = false;
|
||||
if (!isChange) {
|
||||
emitData();
|
||||
}
|
||||
} else {
|
||||
spanEditDefault(value, index, true);
|
||||
}
|
||||
} else {
|
||||
spanEditDefault(value, index, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 原标签被删除的场景
|
||||
* 1.不是key:value形式,使用默认标签
|
||||
* 2.是key:value形式:是否已存在搜索标签,是:替换 否:使用默认标签,递归再判断一次
|
||||
* @param value
|
||||
* @param index
|
||||
* @param isKeyValue 是否有":"
|
||||
*/
|
||||
function spanEditDefault(value: string, index: any, isKeyValue: boolean) {
|
||||
//debugger
|
||||
//直接使用默认标签
|
||||
if (!isKeyValue) {
|
||||
value = defaultFilter.value.label + ':' + value;
|
||||
return spanEditDefault(value, index, true);
|
||||
} else {
|
||||
let sliceIndex = value.indexOf(':') + 1;
|
||||
//先匹配是否存在于过滤标签中
|
||||
let isExist = filterListOperation.value.filter(function (elem) {
|
||||
return elem.label == value.split(':')[0];
|
||||
});
|
||||
//存在
|
||||
if (isExist.length > 0) {
|
||||
//判断是否在已搜索的span中
|
||||
let isSelectedExist = selectedFilter.value.filter(function (elem) {
|
||||
return elem.label == value.split(':')[0];
|
||||
});
|
||||
if (isSelectedExist.length > 0) {
|
||||
filterArrayShow.value.forEach((showValue: any, showIndex) => {
|
||||
// debugger;
|
||||
if (showValue.label == value.split(':')[0]) {
|
||||
showValue.value = value.slice(sliceIndex);
|
||||
let checkRes = checkData(isSelectedExist, value.slice(sliceIndex));
|
||||
searchFilter(
|
||||
isSelectedExist,
|
||||
value.slice(sliceIndex),
|
||||
checkRes,
|
||||
showIndex,
|
||||
false,
|
||||
);
|
||||
if (showIndex != index) {
|
||||
//删除原span
|
||||
onKeyUpDeleteSpan(showIndex, false);
|
||||
} else {
|
||||
filterArrayShow.value.splice(showIndex, 1);
|
||||
// filterArray.value.splice(index, 1);
|
||||
selectedFilter.value.splice(showIndex, 1);
|
||||
}
|
||||
//处理选中后展示的标签与值 filterArrayShow
|
||||
processSelected(isSelectedExist, value.slice(sliceIndex));
|
||||
}
|
||||
});
|
||||
}
|
||||
//作为新的搜索条件
|
||||
else {
|
||||
//处理search的值
|
||||
searchFilter(
|
||||
isExist,
|
||||
value.slice(sliceIndex),
|
||||
checkData(isExist, value),
|
||||
undefined,
|
||||
false,
|
||||
);
|
||||
//处理选中后展示的标签与值 filterArrayShow
|
||||
processSelected(isExist, value.slice(sliceIndex));
|
||||
//删除原span
|
||||
onKeyUpDeleteSpan(index, false);
|
||||
}
|
||||
emitData();
|
||||
}
|
||||
//不存在 使用默认标签
|
||||
else {
|
||||
value = defaultFilter.value.label + ':' + value;
|
||||
return spanEditDefault(value, index, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数据格式
|
||||
* 需要注意:
|
||||
* 是否是已选择搜索过的标签
|
||||
*/
|
||||
function filterformatconvert(empty: string | undefined, isEmptyValue: string | undefined) {
|
||||
ctx.emit('filterformatconvert', empty, isEmptyValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* input场景数据校验
|
||||
* @param chooseObj
|
||||
* @param itemValue
|
||||
*/
|
||||
function checkData(chooseObj: any, itemValue: string) {
|
||||
if (
|
||||
typeof chooseObj[0].componentType != 'undefined' &&
|
||||
chooseObj[0].componentType != 'NsInput'
|
||||
)
|
||||
return;
|
||||
let value = itemValue.split('|');
|
||||
let rule =
|
||||
typeof chooseObj[0].componentProps.rule != 'undefined' &&
|
||||
JSON.stringify(chooseObj[0].componentProps.rule) != '{}'
|
||||
? chooseObj[0].componentProps.rule.pattern
|
||||
: 'any';
|
||||
let result = true;
|
||||
switch (rule) {
|
||||
case 'any':
|
||||
break;
|
||||
default:
|
||||
rule = new RegExp(rule.split('/')[1]);
|
||||
value.forEach((elem) => {
|
||||
result = rule.test(elem);
|
||||
});
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理出入后端的值 filterArray
|
||||
* @param chooseObj
|
||||
* @param itemValue
|
||||
* @param checkRes
|
||||
* @param filterArrayIndex
|
||||
*/
|
||||
function searchFilter(
|
||||
chooseObj: any[],
|
||||
itemValue: string,
|
||||
checkRes: boolean,
|
||||
filterArrayIndex: number,
|
||||
isEmit: boolean,
|
||||
) {
|
||||
ctx.emit('searchFilter', chooseObj, itemValue, checkRes, filterArrayIndex, isEmit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理filterArrayShow(span展示)
|
||||
* 页面展示的选中值
|
||||
* @param chooseObj
|
||||
* @param itemValue
|
||||
*/
|
||||
function processSelected(chooseObj: any[], itemValue: string) {
|
||||
ctx.emit('processSelected', chooseObj, itemValue);
|
||||
}
|
||||
|
||||
//已选中的标签
|
||||
const selectedFilter = ref(props.selectedFilterList);
|
||||
|
||||
/**
|
||||
* 单独处理删除
|
||||
* 第一步处理 展示filterArrayShow
|
||||
* 第二步处理过滤标签 把选中的这一项补回来
|
||||
*/
|
||||
function delSpan(item: { label: any }, index: number) {
|
||||
// debugger;
|
||||
filterArrayShow.value.splice(index, 1);
|
||||
selectedFilter.value.forEach((elem: any) => {
|
||||
if (elem.label == item.label) {
|
||||
filterArray.value.forEach((arrayElem: any, elemIndex) => {
|
||||
if (arrayElem.name == elem.key) {
|
||||
filterArray.value.splice(elemIndex, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// console.log('selectedFilter', selectedFilter.value)
|
||||
// console.log('filterArrayShow', filterArrayShow.value)
|
||||
// console.log('filterArray',filterArray.value)
|
||||
selectedFilter.value.splice(index, 1);
|
||||
emitData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 还原chooseItem
|
||||
*/
|
||||
function reductionChooseItem() {
|
||||
chooseItem.value = {
|
||||
isChoose: false,
|
||||
isEdit: false,
|
||||
componentType: '',
|
||||
item: {},
|
||||
empty: false,
|
||||
notEmpty: false,
|
||||
isShowEmptyDiv: false,
|
||||
showEmptyOption: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* inputWidth 计算字符长度
|
||||
* @param fData
|
||||
*/
|
||||
const DataLength = (fData: string) => {
|
||||
return utils.DataLength(fData);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function focusShowDiv() {
|
||||
focusObj.value.filterLabelFocus = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中span,再次输入/修改
|
||||
*/
|
||||
function editSpan(elem: { label: string; value: any }, index: number) {
|
||||
// debugger;
|
||||
if (!focusObj.value.divFocus) {
|
||||
focusObj.value.divFocus = true;
|
||||
// focusObj.value.filterLabelFocus = true;
|
||||
return;
|
||||
}
|
||||
let choose = filterListOperation.value.filter(function (filterItem: any) {
|
||||
return filterItem.label == elem.label;
|
||||
});
|
||||
if (choose.length > 0) {
|
||||
let spanFilterValue = elem.value;
|
||||
filterArrayShow.value[index].spanFilterValue = elem.label + ':' + spanFilterValue;
|
||||
filterArrayShow.value[index].inputWidth = DataLength(
|
||||
filterArrayShow.value[index].spanFilterValue,
|
||||
);
|
||||
}
|
||||
focusObj.value.filterLabelFocus = false;
|
||||
filterArrayShow.value[index].isEdit = true;
|
||||
}
|
||||
|
||||
// //需要监听的数据
|
||||
// watch(
|
||||
// () => props.filterArrayList,
|
||||
// (newValue, oldValue) => {
|
||||
// }
|
||||
// );
|
||||
// watch(
|
||||
// () => filterArrayShow.value,
|
||||
// (newValue, oldValue) => {
|
||||
// },
|
||||
// );
|
||||
|
||||
/**
|
||||
* 各个组件形态
|
||||
* nsInputChange input的值变化
|
||||
*/
|
||||
function nsInputChange(value: string | string[], spanIndex: number) {
|
||||
if (spanIndex != -1) {
|
||||
filterArrayShow.value[spanIndex].inputWidth = DataLength(value);
|
||||
}
|
||||
//删除了标签
|
||||
if (value.indexOf(':') == -1) {
|
||||
reductionChooseItem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个span点击,input组件形态再次输入
|
||||
* nsSpanInputChange
|
||||
*/
|
||||
function nsSpanInputChange(value: string, index: string | number, bool: boolean) {
|
||||
if (value == '') {
|
||||
onKeyUpDeleteSpan(index);
|
||||
} else {
|
||||
if (bool == true) {
|
||||
filterValue.value = value;
|
||||
}
|
||||
filterArrayShow.value[index].changeValue = value;
|
||||
filterArrayShow.value[index].inputWidth = DataLength(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件选中某个空值
|
||||
* @param item
|
||||
*/
|
||||
function chooseIsEmptyLabel(item: { label?: string; value: any }, isEmptyValue: any) {
|
||||
chooseItem.value.isShowEmptyDiv = false;
|
||||
focusObj.value.filterLabelFocus = false;
|
||||
// filterValue.value = '';
|
||||
//空值和不为空值
|
||||
filterformatconvert(item.value, isEmptyValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单选组件 选中
|
||||
*/
|
||||
function chooseSingleOptions(
|
||||
value: string,
|
||||
option: { value: string; label: any },
|
||||
spanIndex: number,
|
||||
) {
|
||||
ctx.emit('chooseSingleOptions', value, option, spanIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* chooseMultiOptions 多选组件
|
||||
*/
|
||||
function chooseMultiOptions(
|
||||
value: string,
|
||||
showValue: any,
|
||||
arrayValue: { value: any },
|
||||
spanIndex: number,
|
||||
) {
|
||||
ctx.emit('chooseMultiOptions', value, showValue, arrayValue, spanIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多选组件取消
|
||||
*/
|
||||
function canel(index: number) {
|
||||
filterValue.value = '';
|
||||
focusObj.value.filterLabelFocus = false;
|
||||
if (index != -1) {
|
||||
filterArrayShow.value[index].isEdit = false;
|
||||
}
|
||||
reductionChooseItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* table发送数据
|
||||
*/
|
||||
function emitData() {
|
||||
// console.log('emitData');
|
||||
// console.log(filterArray.value);
|
||||
ctx.emit('change', filterArray.value);
|
||||
}
|
||||
|
||||
function setFilterArrayList(value: unknown[]) {
|
||||
filterArray.value = value;
|
||||
}
|
||||
|
||||
function setDefaultFilter(value: Record<string, any>) {
|
||||
defaultFilter.value = value;
|
||||
}
|
||||
|
||||
function setFilterArrayShow(value: unknown[], type: any) {
|
||||
// debugger
|
||||
if (typeof type != 'undefined') {
|
||||
selectedFilter.value = value;
|
||||
return;
|
||||
}
|
||||
filterArrayShow.value = value;
|
||||
}
|
||||
|
||||
function onClickOutsideArrayShow() {
|
||||
filterArrayShow.value.forEach((item: any) => {
|
||||
item.isEdit = false;
|
||||
});
|
||||
}
|
||||
|
||||
function showFilterLabel(value: any, index: any) {
|
||||
// console.log('showFilterLabel', value);
|
||||
// filterValue.value = value;
|
||||
// showFilterLabelIndex.value = index;
|
||||
}
|
||||
|
||||
return {
|
||||
editSpan,
|
||||
focus,
|
||||
focusObj,
|
||||
chooseItem,
|
||||
filterArrayShow,
|
||||
inputWidth,
|
||||
filterValue,
|
||||
spanItem,
|
||||
// showFilterLabelIndex,
|
||||
// showFilterLabel,
|
||||
delSpan,
|
||||
chooseLabel,
|
||||
valueChange,
|
||||
onKeyUpDelete,
|
||||
onKeyUpDeleteSpan,
|
||||
chooseIsEmptyLabel,
|
||||
nsInputChange,
|
||||
nsSpanInputChange,
|
||||
spanPressEnter,
|
||||
chooseSingleOptions,
|
||||
chooseMultiOptions,
|
||||
canel,
|
||||
setFilterArrayList,
|
||||
setDefaultFilter,
|
||||
setFilterArrayShow,
|
||||
onClickOutsideArrayShow,
|
||||
focusShowDiv,
|
||||
isEditSpanIng,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ns-input-filter-list {
|
||||
border-radius: 0;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 16px 0 rgb(54 58 80 / 16%);
|
||||
min-width: 150px;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
.ns-input-filter-content {
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.filter-list-text {
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
margin: 0px;
|
||||
}
|
||||
.filter-list-text > li.is-disabled {
|
||||
background-color: transparent;
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.filter-list-text > li:first-child,
|
||||
.filter-list-text > li:last-child {
|
||||
border-radius: 0 0 0 0;
|
||||
}
|
||||
.filter-list-text > li:hover {
|
||||
background-color: #ebeef2;
|
||||
}
|
||||
.filter-list-text > li {
|
||||
margin-bottom: 0;
|
||||
font-size: 12px;
|
||||
padding: 6px 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: #000;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
border: transparent !important;
|
||||
}
|
||||
.ant-input:focus {
|
||||
border: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.filter-array-span-nofocus {
|
||||
max-width: 100px;
|
||||
}
|
||||
.span-noFcous-div {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
max-width: 120px;
|
||||
}
|
||||
.span-group-div {
|
||||
display: inline;
|
||||
max-width: none;
|
||||
white-space: normal;
|
||||
vertical-align: middle;
|
||||
word-break: break-word;
|
||||
}
|
||||
.filter-array-span {
|
||||
position: relative;
|
||||
background-color: #f3f4f7;
|
||||
color: #000;
|
||||
margin: 4px;
|
||||
margin-left: 0px;
|
||||
line-height: 11px;
|
||||
vertical-align: middle;
|
||||
border: 1px solid #f3f4f7;
|
||||
border-radius: 0;
|
||||
padding: 2px 4px;
|
||||
min-height: 20px;
|
||||
|
||||
vertical-align: inherit;
|
||||
display: inline-block;
|
||||
width: max-content;
|
||||
font-size: 12px;
|
||||
vertical-align: top;
|
||||
|
||||
max-width: 470px;
|
||||
|
||||
text {
|
||||
}
|
||||
}
|
||||
.filter-array-span-edit {
|
||||
position: relative;
|
||||
// background-color: #f3f4f7;
|
||||
color: #000;
|
||||
line-height: 10px;
|
||||
vertical-align: middle;
|
||||
border-radius: 0;
|
||||
margin-left: 4px;
|
||||
min-height: 20px;
|
||||
|
||||
vertical-align: inherit;
|
||||
display: inline-block;
|
||||
width: max-content;
|
||||
font-size: 12px;
|
||||
vertical-align: top;
|
||||
max-width: 470px;
|
||||
|
||||
:deep(.ant-input) {
|
||||
padding: 3px 0px !important;
|
||||
display: inline;
|
||||
white-space: normal;
|
||||
vertical-align: middle;
|
||||
word-break: break-word;
|
||||
max-width: 440px;
|
||||
padding-right: 5px !important;
|
||||
}
|
||||
}
|
||||
.filter-array-span-1 ::before {
|
||||
display: inline-block;
|
||||
content: '';
|
||||
}
|
||||
.filter-array-span-2 ::before {
|
||||
display: inline-block;
|
||||
content: '';
|
||||
}
|
||||
.close-span {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
background-repeat: no-repeat;
|
||||
background-position: inherit;
|
||||
font-size: 0;
|
||||
}
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
margin: auto;
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTcuOTk3NCAxQzQuMTMyODQgMSAxIDQuMTMyODUgMSA3Ljk5NzRDMSAxMS44NjE5IDQuMTMyODUgMTQuOTk0OCA3Ljk5NzQgMTQuOTk0OEMxMS44NjE5IDE0Ljk5NDggMTQuOTk0OCAxMS44NjE5IDE0Ljk5NDggNy45OTc0QzE0Ljk5NDggNC4xMzI4NSAxMS44NjE5IDEgNy45OTc0IDFaTTUuNTYyNTQgMTEuNjM4OEw0LjQyMzU3IDEwLjUwNjdMNi45MTQyMSA4LjAwMDk3TDQuNDA4NDQgNS41MTAzMkw1LjU0MDU0IDQuMzcxMzVMOC4wNDYzMyA2Ljg2MTk5TDEwLjUzNyA0LjM1NjIyTDExLjY3NTkgNS40ODgzM0w5LjE4NTMgNy45OTQwOUwxMS42OTExIDEwLjQ4NDhMMTAuNTU5IDExLjYyMzdMOC4wNTMyIDkuMTMzMDlMNS41NjI1NCAxMS42Mzg4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC40Ii8+Cjwvc3ZnPg==);
|
||||
}
|
||||
</style>
|
||||
226
lib/component/form/input/input-filter-input.vue
Normal file
226
lib/component/form/input/input-filter-input.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<!-- eslint-disable vue/v-on-event-hyphenation -->
|
||||
<template>
|
||||
<a-input
|
||||
v-if="!ifTextarea"
|
||||
ref="aComInputFilter"
|
||||
AutoComplete="off"
|
||||
:autofocus="autofocus"
|
||||
v-model:value="filterValue"
|
||||
@pressEnter="pressEnter"
|
||||
@keydown="onKeyUpDelete"
|
||||
@focus="inputFocus" />
|
||||
<a-textarea
|
||||
v-else
|
||||
class="input-textarea-span"
|
||||
ref="aComInputFilter"
|
||||
AutoComplete="off"
|
||||
:autosize="false"
|
||||
:autofocus="autofocus"
|
||||
v-model:value="filterValue"
|
||||
@pressEnter="pressEnter"
|
||||
@keydown="onKeyUpDelete"
|
||||
@focus="inputFocus" />
|
||||
<span style="position: absolute; top: -9999px; left: 0px; white-space: pre; font-size: 12px">
|
||||
>
|
||||
{{ filterValue }}
|
||||
</span>
|
||||
<!-- 是否不为空选项 -->
|
||||
<div v-if="isEmptyItem.isShowEmptyDiv" class="show-is-empty-content">
|
||||
<div class="ns-input-filter-list">
|
||||
<p class="is-disabled"> 是否是空值 </p>
|
||||
<ul class="filter-list-text">
|
||||
<li
|
||||
@click.stop="chooseIsEmptyLabel(item)"
|
||||
v-for="(item, index) in isEmptyItem.showEmptyOption"
|
||||
:key="index">
|
||||
{{ item.label }}
|
||||
</li>
|
||||
</ul>
|
||||
<p class="is-disabled"> 注:也可直接输入关键字</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, toRaw, watch, onMounted } from 'vue';
|
||||
import * as utils from './input-filter-util';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInputFilterInput',
|
||||
props: {
|
||||
value: String,
|
||||
chooseItem: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
spanIndex: Number,
|
||||
autofocus: Boolean,
|
||||
},
|
||||
emits: ['change', 'pressEnter', 'chooseIsEmptyLabel', 'keydown', 'focus'],
|
||||
setup(props, ctx) {
|
||||
// console.log('NsInputFilterInput');
|
||||
// console.log(props);
|
||||
const focusObj = ref({
|
||||
focus: true,
|
||||
});
|
||||
//span点击 input为textArea
|
||||
// const ifTextarea = ref(typeof props.spanIndex != 'undefined' ? true : false);
|
||||
const ifTextarea = ref(false);
|
||||
|
||||
const aComInputFilter = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
aComInputFilter.value != null
|
||||
? aComInputFilter.value.input.focus({
|
||||
cursor: 'start', // 聚焦在文本的开始位置
|
||||
})
|
||||
: '';
|
||||
}
|
||||
});
|
||||
|
||||
let isEmptyItem = ref(props.chooseItem);
|
||||
let filterValue = ref(toRaw(props.value));
|
||||
let inputWidth = computed(() => {
|
||||
if (typeof filterValue.value == 'undefined') {
|
||||
return 8 + 'px';
|
||||
}
|
||||
return DataLength(filterValue.value) + 'px';
|
||||
});
|
||||
//计算字符长度
|
||||
const DataLength = (fData: string) => {
|
||||
return utils.DataLength(fData);
|
||||
};
|
||||
|
||||
//回车
|
||||
function pressEnter() {
|
||||
// console.log('filterValue.value');
|
||||
// console.log(filterValue.value);
|
||||
ctx.emit('pressEnter', filterValue.value, props.spanIndex, false, true);
|
||||
}
|
||||
|
||||
//删除
|
||||
function onKeyUpDelete(event: { key: string }) {
|
||||
if (event.key == 'Backspace') {
|
||||
if (typeof filterValue.value != 'undefined' && filterValue.value != '') {
|
||||
return;
|
||||
}
|
||||
ctx.emit('keydown', props.spanIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 空值与不为空值选中
|
||||
*/
|
||||
function chooseIsEmptyLabel(item: any) {
|
||||
ctx.emit('chooseIsEmptyLabel', item, filterValue.value);
|
||||
}
|
||||
|
||||
function inputFocus() {
|
||||
ctx.emit('focus');
|
||||
}
|
||||
|
||||
//需要监听的数据
|
||||
watch(
|
||||
() => filterValue.value,
|
||||
(newValue) => {
|
||||
if (newValue != '' && newValue.split(':').length > 1) {
|
||||
if (newValue.split(':')[1] != '') {
|
||||
if (isEmptyItem.value.showEmptyOption.length > 0)
|
||||
isEmptyItem.value.isShowEmptyDiv = false;
|
||||
} else {
|
||||
if (isEmptyItem.value.showEmptyOption.length > 0)
|
||||
isEmptyItem.value.isShowEmptyDiv = true;
|
||||
}
|
||||
} else {
|
||||
if (isEmptyItem.value.showEmptyOption.length > 0)
|
||||
isEmptyItem.value.isShowEmptyDiv = false;
|
||||
// if (typeof props.spanIndex != 'undefined') {
|
||||
// console.log('出现过滤标签');
|
||||
// ctx.emit('showFilterLabel', newValue, props.spanIndex);
|
||||
// }
|
||||
}
|
||||
ctx.emit('change', newValue, props.spanIndex);
|
||||
},
|
||||
);
|
||||
return {
|
||||
inputWidth,
|
||||
filterValue,
|
||||
focusObj,
|
||||
isEmptyItem,
|
||||
aComInputFilter,
|
||||
ifTextarea,
|
||||
pressEnter,
|
||||
onKeyUpDelete,
|
||||
chooseIsEmptyLabel,
|
||||
inputFocus,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ant-input:focus {
|
||||
border: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.show-is-empty-content {
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
left: v-bind(inputWidth);
|
||||
}
|
||||
.ns-input-filter-list {
|
||||
border-radius: 0;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 16px 0 rgb(54 58 80 / 16%);
|
||||
min-width: 150px;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
.filter-list-text {
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
margin: 0px;
|
||||
}
|
||||
.is-disabled {
|
||||
background-color: transparent;
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
cursor: not-allowed;
|
||||
font-size: 12px;
|
||||
margin-bottom: 0px;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
.filter-list-text > li:first-child,
|
||||
.filter-list-text > li:last-child {
|
||||
border-radius: 0 0 0 0;
|
||||
}
|
||||
.filter-list-text > li:hover {
|
||||
background-color: #ebeef2;
|
||||
}
|
||||
.filter-list-text > li {
|
||||
margin-bottom: 0;
|
||||
font-size: 12px;
|
||||
padding: 6px 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: #000;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.input-textarea-span {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
vertical-align: top;
|
||||
height: 28px;
|
||||
font-size: 12px;
|
||||
border: 1px solid rgb(207, 213, 222);
|
||||
border-radius: 0px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
transition: color 0.2s ease-in-out 0s, background-color, border;
|
||||
padding: 3px 0px !important;
|
||||
min-height: 22px !important;
|
||||
line-height: 22px !important;
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
850
lib/component/form/input/input-filter-multiSearch.vue
Normal file
850
lib/component/form/input/input-filter-multiSearch.vue
Normal file
@@ -0,0 +1,850 @@
|
||||
<!-- eslint-disable vue/v-on-event-hyphenation -->
|
||||
<template>
|
||||
<div ref="divComInputFilter">
|
||||
<a-input
|
||||
ref="aComInputFilter"
|
||||
v-if="!ifTextarea"
|
||||
AutoComplete="off"
|
||||
:autofocus="autofocus"
|
||||
v-model:value="filterValue"
|
||||
@keydown="onKeyDownDelete"
|
||||
@keyup="onKeyUpDelete"
|
||||
@pressEnter="pressEnter"
|
||||
@focus="foucus" />
|
||||
<a-textarea
|
||||
ref="aComInputFilter"
|
||||
class="input-textarea-span"
|
||||
v-else
|
||||
AutoComplete="off"
|
||||
:autofocus="autofocus"
|
||||
v-model:value="filterValue"
|
||||
@keydown="onKeyDownDelete"
|
||||
@keyup="onKeyUpDelete"
|
||||
@pressEnter="pressEnter"
|
||||
@focus="foucus" />
|
||||
<span style="position: absolute; top: -9999px; left: 0px; white-space: pre; font-size: 12px">
|
||||
>
|
||||
{{ filterValue }}
|
||||
</span>
|
||||
<!-- 多选选项 -->
|
||||
<div v-if="isOptionsShow" class="show-options-content">
|
||||
<div class="ns-input-filter-list" v-if="multiSearchOptions.length > 0">
|
||||
<ul class="options-list-text" ref="showOptionsContent">
|
||||
<p v-if="emptyLength > 0" class="is-disabled"> 是否是空值 </p>
|
||||
<li
|
||||
@click.stop="chooseOption(item, index)"
|
||||
v-for="(item, index) in multiSearchOptions"
|
||||
:key="index"
|
||||
:style="index < emptyLength ? '' : 'display:none'">
|
||||
<div class="check-label">
|
||||
<a-checkbox
|
||||
v-model:checked="item.checked"
|
||||
:disabled="item.disabled"
|
||||
class="check-label-checked" />
|
||||
<span class="check-label-text">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<p v-if="emptyLength > 0" class="is-disabled"> 按值筛选 </p>
|
||||
<li
|
||||
@click.stop="chooseOption(item, index)"
|
||||
v-for="(item, index) in multiSearchOptions"
|
||||
:key="index"
|
||||
:style="index >= emptyLength ? '' : 'display:none'">
|
||||
<div class="check-label">
|
||||
<a-checkbox
|
||||
v-model:checked="item.checked"
|
||||
:disabled="item.disabled"
|
||||
class="check-label-checked" />
|
||||
<span class="check-label-text">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="options-list-footer">
|
||||
<a-button
|
||||
type="primary"
|
||||
class="footer-submit-button"
|
||||
:disabled="isCanSubmit"
|
||||
@click.stop="submit"
|
||||
>确定</a-button
|
||||
>
|
||||
<a-button class="footer-canel-button" @click.stop="canel">取消</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ns-input-filter-list" v-else>
|
||||
<ul class="options-list-text">
|
||||
<p class="is-disabled"> 暂无数据 </p>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
computed,
|
||||
toRaw,
|
||||
onMounted,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
watch,
|
||||
} from 'vue';
|
||||
import { onClickOutside } from '@vueuse/core';
|
||||
import { http } from '/nerv-lib/util';
|
||||
import * as utils from './input-filter-util';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInputFilterMultiSearch',
|
||||
props: {
|
||||
value: String,
|
||||
chooseItem: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
spanIndex: Number,
|
||||
autofocus: Boolean,
|
||||
optionsValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
emits: ['change', 'chooseOptions', 'keydown', 'canel', 'pressEnter'],
|
||||
setup(props, ctx) {
|
||||
// console.log('NsInputFilterMultiSearch');
|
||||
// console.log(props);
|
||||
const focusObj = ref({
|
||||
focus: true,
|
||||
});
|
||||
const emptyLength = ref(0);
|
||||
|
||||
//span点击 input为textArea
|
||||
// const ifTextarea = ref(typeof props.spanIndex != 'undefined' ? true : false);
|
||||
const ifTextarea = ref(false);
|
||||
const showOptionsContent = ref();
|
||||
const aComInputFilter = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
aComInputFilter.value != null
|
||||
? aComInputFilter.value.input.focus({
|
||||
cursor: 'start', // 聚焦在文本的开始位置
|
||||
})
|
||||
: '';
|
||||
}
|
||||
});
|
||||
const multiSearchOptions = ref([Object]);
|
||||
|
||||
const isOptionsShow = ref(false);
|
||||
|
||||
let isEmptyItem = ref(props.chooseItem);
|
||||
let filterValue = ref(toRaw(props.value));
|
||||
const arrayLength = ref(0);
|
||||
const optionInfo = ref();
|
||||
const page = ref(0);
|
||||
|
||||
const checkedOptions = ref(
|
||||
typeof props.optionsValue != 'undefined'
|
||||
? JSON.parse(JSON.stringify(props.optionsValue))
|
||||
: [],
|
||||
);
|
||||
|
||||
let isCanSubmit = computed(() => {
|
||||
let arry = multiSearchOptions.value.filter(function (item) {
|
||||
return item.checked == true;
|
||||
});
|
||||
if (arry.length <= 0 && multiSearchOptions.value.length > 0) {
|
||||
multiSearchOptions.value[0].value == 'empty'
|
||||
? (multiSearchOptions.value[0].disabled = false)
|
||||
: '';
|
||||
}
|
||||
return arry.length > 0 ? false : true;
|
||||
});
|
||||
|
||||
initOptions();
|
||||
function initOptions() {
|
||||
multiSearchOptions.value = [];
|
||||
isPushEmpty();
|
||||
if (
|
||||
typeof props.chooseItem.item.componentProps.interfaceGet == 'undefined' ||
|
||||
!props.chooseItem.item.componentProps.interfaceGet
|
||||
) {
|
||||
arrayLength.value += props.chooseItem.item.componentProps.options.length;
|
||||
multiSearchOptions.value = multiSearchOptions.value.concat(
|
||||
props.chooseItem.item.componentProps.options,
|
||||
);
|
||||
pushChecked();
|
||||
} else {
|
||||
// multiSearchOptions.value.unshift({
|
||||
// label: '全选',
|
||||
// value: 'allChoose',
|
||||
// });
|
||||
interfaceGetOptions();
|
||||
}
|
||||
}
|
||||
|
||||
function pushChecked() {
|
||||
// multiSearchOptions.value.splice(emptyLength.value, 1, {
|
||||
// label: '全选',
|
||||
// value: 'allChoose',
|
||||
// });
|
||||
// multiSearchOptions.value.unshift();
|
||||
multiSearchOptions.value.forEach((elem) => {
|
||||
elem.checked = false;
|
||||
});
|
||||
pushCheckedOption();
|
||||
isOptionsShow.value = true;
|
||||
}
|
||||
|
||||
function isPushEmpty() {
|
||||
if (isEmptyItem.value.showEmptyOption.length > 0) {
|
||||
emptyLength.value = isEmptyItem.value.showEmptyOption.length;
|
||||
multiSearchOptions.value = JSON.parse(JSON.stringify(isEmptyItem.value.showEmptyOption));
|
||||
let notEmpty = isEmptyItem.value.showEmptyOption.filter(function (arr: {
|
||||
value: string;
|
||||
}) {
|
||||
return arr.value == 'notEmpty';
|
||||
});
|
||||
if (notEmpty.length > 0) {
|
||||
arrayLength.value = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口获取数据
|
||||
*/
|
||||
function interfaceGetOptions() {
|
||||
const requestMethod = props.chooseItem.item.componentProps.requestMethod;
|
||||
const params =
|
||||
typeof props.chooseItem.item.componentProps.params != 'undefined'
|
||||
? props.chooseItem.item.componentProps.params
|
||||
: {};
|
||||
const api =
|
||||
typeof props.chooseItem.item.componentProps.api != 'undefined'
|
||||
? props.chooseItem.item.componentProps.api
|
||||
: '';
|
||||
const config = {
|
||||
// headers: {
|
||||
// 'Content-Type': 'multipart/form-data',
|
||||
// },
|
||||
};
|
||||
const path = props.chooseItem.item.componentProps.labelFieldPath.split('[*]')[0].slice(1);
|
||||
const labelField = props.chooseItem.item.componentProps.labelFieldPath
|
||||
.split('[*]')[1]
|
||||
.slice(1);
|
||||
const value = props.chooseItem.item.componentProps.valueFieldPath.split('[*]')[1].slice(1);
|
||||
const method = requestMethod;
|
||||
let info = {
|
||||
params,
|
||||
api,
|
||||
config,
|
||||
path,
|
||||
labelField,
|
||||
value,
|
||||
method,
|
||||
};
|
||||
optionInfo.value = info;
|
||||
handleParams();
|
||||
switch (requestMethod) {
|
||||
case 'GET':
|
||||
optionInfo.value.method = 'get';
|
||||
break;
|
||||
case 'POST':
|
||||
optionInfo.value.method = 'post';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
interfaceHttp(undefined).then(
|
||||
(value) => {
|
||||
handleScroll();
|
||||
},
|
||||
(reason) => {
|
||||
// console.log(reason);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数
|
||||
*/
|
||||
function handleParams() {
|
||||
let api = optionInfo.value.api;
|
||||
if (api.indexOf('http') != -1) {
|
||||
let i = 1;
|
||||
while (i <= 4) {
|
||||
let delIndex = api.indexOf('/');
|
||||
api = api.slice(delIndex == 0 ? delIndex + 1 : delIndex);
|
||||
i++;
|
||||
}
|
||||
optionInfo.value.api = api;
|
||||
}
|
||||
}
|
||||
|
||||
//是否需要分页
|
||||
const isPaging = ref(true);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function interfaceHttp(params: undefined | Object) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
typeof showOptionsContent.value != 'undefined'
|
||||
? showOptionsContent.value.removeEventListener('scroll', () => {})
|
||||
: '';
|
||||
let api = optionInfo.value.api;
|
||||
optionInfo.value.page = page.value;
|
||||
if (typeof params != 'undefined') {
|
||||
//optionInfo.value.page = page.value;
|
||||
api = optionInfo.value.api;
|
||||
}
|
||||
http[optionInfo.value.method](
|
||||
api,
|
||||
{
|
||||
// optionInfo.value.params
|
||||
page: optionInfo.value.page,
|
||||
},
|
||||
optionInfo.value.config,
|
||||
)
|
||||
.then((res: { [x: string]: any[] }) => {
|
||||
arrayLength.value += res.totalCount;
|
||||
if (
|
||||
res[optionInfo.value.path].length > 0 &&
|
||||
(typeof res.page != 'undefined' ? res.page == page.value : true)
|
||||
) {
|
||||
typeof res.page == 'undefined' ? (isPaging.value = false) : '';
|
||||
res[optionInfo.value.path].forEach((elem: { [x: string]: any }) => {
|
||||
let item = {
|
||||
value: elem[optionInfo.value.value],
|
||||
label: elem[optionInfo.value.labelField],
|
||||
};
|
||||
multiSearchOptions.value.push(item);
|
||||
});
|
||||
if (typeof params != 'undefined') {
|
||||
pushCheckedOption();
|
||||
} else {
|
||||
pushChecked();
|
||||
}
|
||||
} else if (page.value > 0) {
|
||||
page.value--;
|
||||
}
|
||||
isOptionsShow.value = true;
|
||||
resolve('success');
|
||||
})
|
||||
.catch((err: any) => {
|
||||
reject('fail');
|
||||
isOptionsShow.value = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function pushCheckedOption() {
|
||||
checkedOptions.value.length > 0 ? '' : (checkedOptions.value = []);
|
||||
if (
|
||||
typeof props.value != 'undefined' &&
|
||||
props.value.indexOf('空') != -1 &&
|
||||
props.value.indexOf('不为空') == -1
|
||||
) {
|
||||
checkedOptions.value.push('empty');
|
||||
multiSearchOptions.value.forEach((elem) => {
|
||||
checkedOptions.value.forEach((item: any) => {
|
||||
elem.value == item ? (elem.checked = true) : (elem.disabled = true);
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (typeof props.value != 'undefined' && props.value.indexOf('不为空') != -1) {
|
||||
checkedOptions.value.push('notEmpty');
|
||||
}
|
||||
// console.log(checkedOptions.value.length);
|
||||
// if(checkedOptions.value.length)
|
||||
multiSearchOptions.value.forEach((elem) => {
|
||||
checkedOptions.value.forEach((item: any) => {
|
||||
elem.value == item ? (elem.checked = true) : '';
|
||||
});
|
||||
});
|
||||
//有其他选项场景把空选项禁用
|
||||
let ifHaveAllChose = checkedOptions.value.filter(function (elem) {
|
||||
return elem != 'empty';
|
||||
});
|
||||
if (ifHaveAllChose.length > 0) {
|
||||
multiSearchOptions.value[0].value == 'empty'
|
||||
? (multiSearchOptions.value[0].disabled = true)
|
||||
: '';
|
||||
}
|
||||
}
|
||||
|
||||
let inputWidth = computed(() => {
|
||||
if (typeof filterValue.value == 'undefined') {
|
||||
return 8 + 'px';
|
||||
}
|
||||
return DataLength(filterValue.value) + 'px';
|
||||
});
|
||||
|
||||
//计算字符长度
|
||||
const DataLength = (fData: string) => {
|
||||
return utils.DataLength(fData);
|
||||
};
|
||||
|
||||
const divLeft = ref();
|
||||
divLeft.value =
|
||||
DataLength(typeof props.value != 'undefined' ? props.value.split(':')[0] : '') + 'px';
|
||||
|
||||
//回车
|
||||
function pressEnter() {
|
||||
if (
|
||||
props.value.split(':')[0] == filterValue.value.split(':')[0] &&
|
||||
filterValue.value.indexOf(':') != -1
|
||||
) {
|
||||
if (!isCanSubmit.value) {
|
||||
submit();
|
||||
} else {
|
||||
filterValue.value = filterValue.value.split(':')[0] + ':';
|
||||
isOptionsShow.value = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
ctx.emit('pressEnter', filterValue.value, props.spanIndex, true, true);
|
||||
}
|
||||
|
||||
//删除
|
||||
function onKeyDownDelete(event: { key: string }) {
|
||||
if (event.key == 'Backspace') {
|
||||
if (typeof filterValue.value != 'undefined' && filterValue.value != '') {
|
||||
return;
|
||||
}
|
||||
ctx.emit('keydown', props.spanIndex);
|
||||
}
|
||||
}
|
||||
|
||||
function onKeyUpDelete(event: { key: string }) {
|
||||
if (event.key == 'Backspace') {
|
||||
if (typeof filterValue.value != 'undefined' && filterValue.value != '') {
|
||||
if (filterValue.value.indexOf(':') != -1) {
|
||||
let value = filterValue.value.split(':')[1];
|
||||
let labelArray = value.split('|');
|
||||
multiSearchOptions.value.forEach((elem) => {
|
||||
elem.checked = false;
|
||||
labelArray.forEach((item) => {
|
||||
elem.label == item ? (elem.checked = true) : '';
|
||||
});
|
||||
});
|
||||
let ifChooseArray = multiSearchOptions.value.filter(function (elem) {
|
||||
return elem.checked == true;
|
||||
});
|
||||
if (ifChooseArray.length <= 0) {
|
||||
multiSearchOptions.value.forEach((elem) => {
|
||||
elem.disabled = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
isOptionsShow.value = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 空值与不为空值选中
|
||||
*/
|
||||
function chooseOption(item, index: number) {
|
||||
if (item.disabled) return;
|
||||
multiSearchOptions.value[index].checked = !item.checked;
|
||||
if (!item.checked) {
|
||||
checkedOptions.value.forEach((elem: string, elemIndex: any) => {
|
||||
if (elem == item.value) {
|
||||
checkedOptions.value.splice(elemIndex, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (item.value == 'empty') {
|
||||
multiSearchOptions.value.forEach((elem, keyIndex) => {
|
||||
if (keyIndex != index) {
|
||||
keyIndex != 0 ? (elem.checked = false) : '';
|
||||
elem.disabled = item.checked;
|
||||
}
|
||||
});
|
||||
} else if (item.value == 'allChoose') {
|
||||
// multiSearchOptions.value.forEach((elem, keyIndex) => {
|
||||
// if (elem.value == 'empty') {
|
||||
// elem.checked = false;
|
||||
// elem.disabled = item.checked;
|
||||
// } else {
|
||||
// elem.checked = item.checked;
|
||||
// elem.disabled = false;
|
||||
// }
|
||||
// });
|
||||
} else {
|
||||
// multiSearchOptions.value[0].checked = false;
|
||||
// multiSearchOptions.value.forEach((elem, keyIndex) => {
|
||||
// if (elem.value == 'empty') {
|
||||
// elem.checked = false;
|
||||
// elem.disabled = item.checked;
|
||||
// } else {
|
||||
// elem.checked = item.checked;
|
||||
// elem.disabled = false;
|
||||
// }
|
||||
// });
|
||||
}
|
||||
// //判断全选中
|
||||
// let chooseArray = multiSearchOptions.value.filter(function (arr) {
|
||||
// return arr.checked == true;
|
||||
// });
|
||||
// console.log('chooseArray.length');
|
||||
// console.log(chooseArray.length);
|
||||
// console.log(arrayLength.value);
|
||||
// console.log(emptyLength.value);
|
||||
// if (chooseArray.length == arrayLength.value) {
|
||||
// multiSearchOptions.value[emptyLength.value].checked = true;
|
||||
// }
|
||||
// filterValue.value = filterValue.value.split(':')[0] + ':';
|
||||
// let flag = 0;
|
||||
// multiSearchOptions.value.forEach((item) => {
|
||||
// if (item.checked) {
|
||||
// if (flag != 0) {
|
||||
// filterValue.value += '|' + item.label;
|
||||
// } else {
|
||||
// filterValue.value += item.label;
|
||||
// }
|
||||
// flag += 1;
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定
|
||||
*/
|
||||
function submit() {
|
||||
let chooseArray = multiSearchOptions.value.filter(function (elem) {
|
||||
return elem.checked == true;
|
||||
});
|
||||
let filterArrayShowValue = '';
|
||||
let filterArrayEmit = {
|
||||
value: [],
|
||||
};
|
||||
chooseArray.forEach((elem, index) => {
|
||||
elem.label != '全选' ? (filterArrayShowValue += elem.label) : '';
|
||||
index != chooseArray.length - 1 && elem.label != '全选'
|
||||
? (filterArrayShowValue += '|')
|
||||
: '';
|
||||
if (elem.value != 'empty' && elem.value != 'notEmpty') {
|
||||
// if (elem.value == 'allChoose') {
|
||||
// return;
|
||||
// }
|
||||
filterArrayEmit.value.push(elem.value);
|
||||
} else {
|
||||
filterArrayEmit[elem.value] = true;
|
||||
elem.value == 'empty' ? delete filterArrayEmit.value : '';
|
||||
}
|
||||
});
|
||||
let spanIndex = typeof props.spanIndex != 'undefined' ? props.spanIndex : -1;
|
||||
isOptionsShow.value = false;
|
||||
ctx.emit(
|
||||
'chooseOptions',
|
||||
filterValue.value,
|
||||
filterArrayShowValue,
|
||||
filterArrayEmit,
|
||||
spanIndex,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消
|
||||
*/
|
||||
function canel() {
|
||||
isOptionsShow.value = false;
|
||||
ctx.emit('canel', typeof props.spanIndex != 'undefined' ? props.spanIndex : -1);
|
||||
}
|
||||
|
||||
//需要监听的数据
|
||||
watch(
|
||||
() => filterValue.value,
|
||||
(newValue: any, oldValue) => {
|
||||
let spanIndex = typeof props.spanIndex != 'undefined' ? props.spanIndex : -1;
|
||||
ctx.emit('change', newValue, spanIndex);
|
||||
if (newValue.indexOf(':') != -1) {
|
||||
// let label = newValue.split(':')[0];
|
||||
let valueArray =
|
||||
newValue.split(':')[1].indexOf('|') != -1
|
||||
? newValue.split(':')[1].split('|')
|
||||
: [newValue.split(':')[1]];
|
||||
valueArray.length > 0 ? inputCheckedChoose(valueArray) : '';
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
//根据filterValue值自动选中
|
||||
function inputCheckedChoose(valueArray: any[]) {
|
||||
let chooseIndex: number[] = [];
|
||||
let unChooseIndex: number[] = [];
|
||||
valueArray.forEach((valueItem: any) => {
|
||||
let flag = -1;
|
||||
let filter = multiSearchOptions.value.filter(function (elem) {
|
||||
flag += 1;
|
||||
if (elem.label == valueItem) {
|
||||
chooseIndex.push(flag);
|
||||
}
|
||||
return elem.label == valueItem;
|
||||
});
|
||||
});
|
||||
for (let index = 0; index < multiSearchOptions.value.length; index++) {
|
||||
unChooseIndex.push(index);
|
||||
}
|
||||
unChooseIndex = differenceBy(chooseIndex, unChooseIndex);
|
||||
chooseIndex.length > 0
|
||||
? chooseIndex.forEach((chItem) => {
|
||||
!multiSearchOptions.value[chItem].disabled
|
||||
? (multiSearchOptions.value[chItem].checked = true)
|
||||
: '';
|
||||
})
|
||||
: '';
|
||||
unChooseIndex.length > 0
|
||||
? unChooseIndex.forEach((chItem) => {
|
||||
multiSearchOptions.value[chItem].checked = false;
|
||||
})
|
||||
: '';
|
||||
}
|
||||
|
||||
//二次过滤
|
||||
const differenceBy = (array1: string | any[], array2: string | any[]) => {
|
||||
var result = [];
|
||||
for (var i = 0; i < array2.length; i++) {
|
||||
var obj = array2[i];
|
||||
var num = obj;
|
||||
var isExist = false;
|
||||
if (num && num !== 0) {
|
||||
for (var j = 0; j < array1.length; j++) {
|
||||
var aj = array1[j];
|
||||
var n = aj;
|
||||
if (n === num) {
|
||||
isExist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isExist) {
|
||||
result.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
function foucus() {
|
||||
if (multiSearchOptions.value.length <= 0) return;
|
||||
isOptionsShow.value = true;
|
||||
}
|
||||
|
||||
function checkDefault() {
|
||||
if (props.value.indexOf(':') != -1) {
|
||||
let valueArray =
|
||||
props.value.split(':')[1].indexOf('|') != -1
|
||||
? props.value.split(':')[1].split('|')
|
||||
: [props.value.split(':')[1]];
|
||||
valueArray.length > 0 ? inputCheckedChoose(valueArray) : '';
|
||||
}
|
||||
pressEnter();
|
||||
}
|
||||
|
||||
const divComInputFilter = ref(null);
|
||||
/**
|
||||
* 选中div区域外
|
||||
*/
|
||||
onClickOutside(divComInputFilter, () => {
|
||||
isOptionsShow.value = false;
|
||||
});
|
||||
|
||||
//监听滚动代码区域 showOptionsContent
|
||||
let oldScrollTop = 0; // 记录上一次滚动结束后的滚动距离
|
||||
const scrollTop = ref<number>(0); // 记录当前的滚动距离
|
||||
const scrollFixedStatus = ref<boolean>(true);
|
||||
function handleScroll() {
|
||||
nextTick(() => {
|
||||
showOptionsContent.value.addEventListener('scroll', () => {
|
||||
//获取dom滚动距离
|
||||
scrollTop.value = showOptionsContent.value.scrollTop;
|
||||
|
||||
//获取可视区高度
|
||||
const offsetHeight = showOptionsContent.value.offsetHeight;
|
||||
|
||||
//获取滚动条总高度
|
||||
const scrollHeight = showOptionsContent.value.scrollHeight;
|
||||
|
||||
//console.log('scrollTop.value', scrollTop.value, scrollHeight, offsetHeight);
|
||||
|
||||
if (scrollTop.value + offsetHeight >= scrollHeight) {
|
||||
// 把距离顶部的距离加上可视区域的高度 等于或者大于滚动条的总高度就是到达底部
|
||||
// console.log('已滚动到底部');
|
||||
if (isPaging.value) {
|
||||
page.value++;
|
||||
interfaceHttp('bottom');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
onBeforeUnmount(() => {
|
||||
if (typeof showOptionsContent.value != 'undefined' && showOptionsContent.value != null) {
|
||||
showOptionsContent.value.removeEventListener('scroll', () => {}); // 离开当前组件别忘记移除事件监听
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => scrollTop.value,
|
||||
|
||||
(newValue, oldValue) => {
|
||||
setTimeout(() => {
|
||||
if (newValue === window.scrollY) {
|
||||
// 延时执行后当newValue等于window.scrollY,代表滚动结束
|
||||
// console.log('滚动结束');
|
||||
oldScrollTop = newValue; // 每次滚动结束后都要给oldScrollTop赋值
|
||||
// scrollFixedStatus.value = true;
|
||||
}
|
||||
}, 20); // 必须使用延时器,否则每次newValue和window.scrollY都相等,无法判断,20ms刚好大于watch的侦听周期,故延时20ms
|
||||
if (oldScrollTop === oldValue) {
|
||||
scrollFixedStatus.value = false;
|
||||
// 每次滚动开始时oldScrollTop与oldValue相等
|
||||
// console.log('滚动开始');
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
divComInputFilter,
|
||||
foucus,
|
||||
inputWidth,
|
||||
isCanSubmit,
|
||||
divLeft,
|
||||
filterValue,
|
||||
focusObj,
|
||||
isEmptyItem,
|
||||
aComInputFilter,
|
||||
multiSearchOptions,
|
||||
isOptionsShow,
|
||||
ifTextarea,
|
||||
emptyLength,
|
||||
pressEnter,
|
||||
onKeyUpDelete,
|
||||
onKeyDownDelete,
|
||||
chooseOption,
|
||||
submit,
|
||||
canel,
|
||||
showOptionsContent,
|
||||
checkDefault,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ant-input:focus {
|
||||
border: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.show-options-content {
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
left: v-bind(divLeft);
|
||||
}
|
||||
.ns-input-filter-list {
|
||||
border-radius: 0;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 16px 0 rgb(54 58 80 / 16%);
|
||||
min-width: 150px;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
.options-list-footer {
|
||||
margin: 0 10px;
|
||||
border-top: 1px solid #e7eaef;
|
||||
padding: 10px 0;
|
||||
white-space: nowrap;
|
||||
|
||||
.footer-submit-button {
|
||||
width: 60px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.footer-canel-button {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
.options-list-text {
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
margin: 0px;
|
||||
max-height: 180px;
|
||||
|
||||
.check-label {
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
padding-left: 20px;
|
||||
margin-top: 0;
|
||||
margin-right: 18px;
|
||||
min-height: 18px;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
|
||||
:deep(.ant-checkbox-wrapper) {
|
||||
display: inline-flex !important;
|
||||
}
|
||||
|
||||
.check-label-checked {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
.check-label-text {
|
||||
line-height: 20px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.is-disabled {
|
||||
background-color: transparent;
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
cursor: not-allowed;
|
||||
font-size: 12px;
|
||||
margin-bottom: 0px;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
// .options-list-text > li
|
||||
.options-list-text > li:first-child,
|
||||
.options-list-text > li:last-child {
|
||||
border-radius: 0 0 0 0;
|
||||
}
|
||||
.options-list-text > li:hover {
|
||||
background-color: #ebeef2;
|
||||
}
|
||||
.options-list-text > li {
|
||||
margin-bottom: 0;
|
||||
font-size: 12px;
|
||||
padding: 4px 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: #000;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
line-height: 14px;
|
||||
}
|
||||
.input-textarea-span {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
vertical-align: top;
|
||||
height: 28px;
|
||||
font-size: 12px;
|
||||
border: 1px solid rgb(207, 213, 222);
|
||||
border-radius: 0px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
transition: color 0.2s ease-in-out 0s, background-color, border;
|
||||
padding: 3px 0px !important;
|
||||
min-height: 22px !important;
|
||||
line-height: 22px !important;
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
617
lib/component/form/input/input-filter-singleSearch.vue
Normal file
617
lib/component/form/input/input-filter-singleSearch.vue
Normal file
@@ -0,0 +1,617 @@
|
||||
<!-- eslint-disable vue/v-on-event-hyphenation -->
|
||||
<template>
|
||||
<div ref="divComInputFilter">
|
||||
<a-input
|
||||
ref="aComInputFilter"
|
||||
v-if="!ifTextarea"
|
||||
AutoComplete="off"
|
||||
:autofocus="autofocus"
|
||||
v-model:value="filterValue"
|
||||
@keydown="onKeyDownDelete"
|
||||
@keyup="onKeyUpDelete"
|
||||
@pressEnter="pressEnter"
|
||||
@focus="foucus" />
|
||||
<a-textarea
|
||||
ref="aComInputFilter"
|
||||
class="input-textarea-span"
|
||||
v-else
|
||||
AutoComplete="off"
|
||||
:autofocus="autofocus"
|
||||
v-model:value="filterValue"
|
||||
@keydown="onKeyDownDelete"
|
||||
@keyup="onKeyUpDelete"
|
||||
@pressEnter="pressEnter"
|
||||
@focus="foucus" />
|
||||
<span style="position: absolute; top: -9999px; left: 0px; white-space: pre; font-size: 12px">
|
||||
>
|
||||
{{ filterValue }}
|
||||
</span>
|
||||
<!-- 单选选项 -->
|
||||
<div v-if="isOptionsShow" class="show-options-content">
|
||||
<div class="ns-input-filter-list" v-if="singleSearchOptions.length > 0">
|
||||
<ul class="options-list-text" ref="showOptionsContent">
|
||||
<p v-if="emptyLength > 0" class="is-disabled"> 是否是空值 </p>
|
||||
<li
|
||||
@click.stop="chooseOption(item)"
|
||||
v-for="(item, index) in singleSearchOptions"
|
||||
:key="index"
|
||||
:style="index < emptyLength ? '' : 'display:none'"
|
||||
:class="[item.chooseed ? 'choose-li' : '']">
|
||||
{{ item.label }}
|
||||
</li>
|
||||
<p v-if="emptyLength > 0" class="is-disabled"> 按值筛选 </p>
|
||||
<li
|
||||
@click.stop="chooseOption(item)"
|
||||
v-for="(item, index) in singleSearchOptions"
|
||||
:key="index"
|
||||
:style="index >= emptyLength ? '' : 'display:none'"
|
||||
:class="[item.chooseed ? 'choose-li' : '']">
|
||||
{{ item.label }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ns-input-filter-list" v-else>
|
||||
<ul class="options-list-text">
|
||||
<p class="is-disabled"> 暂无数据 </p>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
computed,
|
||||
toRaw,
|
||||
watch,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
nextTick,
|
||||
} from 'vue';
|
||||
import { http } from '/nerv-lib/util';
|
||||
import { onClickOutside } from '@vueuse/core';
|
||||
import * as utils from './input-filter-util';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInputFilterSingleSearch',
|
||||
props: {
|
||||
value: String,
|
||||
chooseItem: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
spanIndex: Number,
|
||||
autofocus: Boolean,
|
||||
optionsValue: Number | String,
|
||||
fileValue: Array,
|
||||
},
|
||||
emits: ['change', 'chooseOptions', 'keydown', 'pressEnter'],
|
||||
setup(props, ctx) {
|
||||
console.log('NsInputFilterSingleSearch');
|
||||
console.log(props);
|
||||
const focusObj = ref({
|
||||
focus: true,
|
||||
});
|
||||
const aComInputFilter = ref(null);
|
||||
//span点击 input为textArea
|
||||
// const ifTextarea = ref(typeof props.spanIndex != 'undefined' ? true : false);
|
||||
const ifTextarea = ref(false);
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
aComInputFilter.value != null
|
||||
? aComInputFilter.value.input.focus({
|
||||
cursor: 'start', // 聚焦在文本的开始位置
|
||||
})
|
||||
: '';
|
||||
}
|
||||
});
|
||||
const singleSearchOptions = ref([]);
|
||||
const isOptionsShow = ref(false);
|
||||
let isEmptyItem = ref(props.chooseItem);
|
||||
let filterValue = ref(toRaw(props.value));
|
||||
const optionInfo = ref();
|
||||
const page = ref(0);
|
||||
const emptyLength = ref(0);
|
||||
/**
|
||||
* 初始化选项
|
||||
*/
|
||||
initOptions();
|
||||
function initOptions() {
|
||||
if (isEmptyItem.value.showEmptyOption.length > 0) {
|
||||
emptyLength.value = isEmptyItem.value.showEmptyOption.length;
|
||||
let array = JSON.stringify(isEmptyItem.value.showEmptyOption);
|
||||
singleSearchOptions.value = JSON.parse(array);
|
||||
} else {
|
||||
emptyLength.value = 0;
|
||||
}
|
||||
if (
|
||||
typeof props.chooseItem.item.componentProps.interfaceGet == 'undefined' ||
|
||||
!props.chooseItem.item.componentProps.interfaceGet
|
||||
) {
|
||||
singleSearchOptions.value = singleSearchOptions.value.concat(
|
||||
props.chooseItem.item.componentProps.options,
|
||||
);
|
||||
if (typeof props.optionsValue != 'undefined') {
|
||||
pushCooseedOption();
|
||||
}
|
||||
isOptionsShow.value = true;
|
||||
} else {
|
||||
interfaceGetOptions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口获取数据
|
||||
*/
|
||||
function interfaceGetOptions() {
|
||||
// debugger;
|
||||
const requestMethod = props.chooseItem.item.componentProps.requestMethod;
|
||||
const params =
|
||||
typeof props.chooseItem.item.componentProps.params != 'undefined'
|
||||
? props.chooseItem.item.componentProps.params
|
||||
: {};
|
||||
const api =
|
||||
typeof props.chooseItem.item.componentProps.api != 'undefined'
|
||||
? props.chooseItem.item.componentProps.api
|
||||
: '';
|
||||
const config = {
|
||||
// headers: {
|
||||
// 'Content-Type': 'multipart/form-data',
|
||||
// },
|
||||
};
|
||||
const path = props.chooseItem.item.componentProps.labelFieldPath.split('[*]')[0].slice(1);
|
||||
const labelField = props.chooseItem.item.componentProps.labelFieldPath
|
||||
.split('[*]')[1]
|
||||
.slice(1);
|
||||
const value = props.chooseItem.item.componentProps.valueFieldPath.split('[*]')[1].slice(1);
|
||||
const method = requestMethod;
|
||||
let info = {
|
||||
params,
|
||||
api,
|
||||
config,
|
||||
path,
|
||||
labelField,
|
||||
value,
|
||||
method,
|
||||
};
|
||||
optionInfo.value = info;
|
||||
handleParams();
|
||||
switch (requestMethod) {
|
||||
case 'GET':
|
||||
optionInfo.value.method = 'get';
|
||||
break;
|
||||
case 'POST':
|
||||
optionInfo.value.method = 'post';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
interfaceHttp(undefined).then(
|
||||
(value) => {
|
||||
// console.log(value);
|
||||
handleScroll();
|
||||
},
|
||||
(reason) => {
|
||||
// console.log(reason);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数
|
||||
*/
|
||||
function handleParams() {
|
||||
let api = optionInfo.value.api;
|
||||
if (api.indexOf('http') != -1) {
|
||||
let i = 1;
|
||||
while (i <= 4) {
|
||||
let delIndex = api.indexOf('/');
|
||||
api = api.slice(delIndex == 0 ? delIndex + 1 : delIndex);
|
||||
i++;
|
||||
}
|
||||
optionInfo.value.api = api;
|
||||
}
|
||||
}
|
||||
|
||||
//是否需要分页
|
||||
const isPaging = ref(true);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function interfaceHttp(params: undefined | Object) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let api = optionInfo.value.api;
|
||||
optionInfo.value.page = page.value;
|
||||
if (typeof params != 'undefined') {
|
||||
// optionInfo.value.page = page.value;
|
||||
api = optionInfo.value.api;
|
||||
}
|
||||
let filterParams = {};
|
||||
if (props.chooseItem.item.componentProps.getParams) {
|
||||
//console.log('filterParams', params);
|
||||
let getParams = props.chooseItem.item.componentProps.getParams;
|
||||
let fileValue = props.fileValue;
|
||||
if (!props.fileValue && sessionStorage.getItem('filterArray')) {
|
||||
fileValue = JSON.parse(sessionStorage.getItem('filterArray'));
|
||||
}
|
||||
for (let i in getParams) {
|
||||
filterParams[i] = fileValue
|
||||
?.filter((item) => {
|
||||
return item.name == getParams[i];
|
||||
})
|
||||
?.map((item) => {
|
||||
return item.value;
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
}
|
||||
//console.log('filterParams', filterParams);
|
||||
http[optionInfo.value.method](
|
||||
api,
|
||||
{
|
||||
...filterParams,
|
||||
// optionInfo.value.params
|
||||
page: optionInfo.value.page,
|
||||
},
|
||||
optionInfo.value.config,
|
||||
)
|
||||
.then((res: { [x: string]: any[] }) => {
|
||||
if (
|
||||
res[optionInfo.value.path].length > 0 &&
|
||||
(typeof res.page != 'undefined' ? res.page == page.value : true)
|
||||
) {
|
||||
typeof res.page == 'undefined' ? (isPaging.value = false) : '';
|
||||
res[optionInfo.value.path].forEach((elem: { [x: string]: any }, index: any) => {
|
||||
let item = {
|
||||
value: elem[optionInfo.value.value],
|
||||
label: elem[optionInfo.value.labelField],
|
||||
};
|
||||
singleSearchOptions.value.push(item);
|
||||
});
|
||||
if (typeof props.optionsValue != 'undefined') {
|
||||
pushCooseedOption();
|
||||
}
|
||||
} else if (page.value > 0) {
|
||||
page.value -= 1;
|
||||
}
|
||||
isOptionsShow.value = true;
|
||||
resolve('success');
|
||||
})
|
||||
.catch((err: any) => {
|
||||
reject('fail');
|
||||
isOptionsShow.value = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据回显
|
||||
*/
|
||||
function pushCooseedOption() {
|
||||
let checkedOptions = props.optionsValue;
|
||||
singleSearchOptions.value.forEach((elem, index) => {
|
||||
elem.value == checkedOptions ? (elem.chooseed = true) : (elem.chooseed = false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算input宽度
|
||||
* 选项div展示位置
|
||||
*/
|
||||
let inputWidth = computed(() => {
|
||||
if (typeof filterValue.value == 'undefined') {
|
||||
return 8 + 'px';
|
||||
}
|
||||
return DataLength(filterValue.value) + 'px';
|
||||
});
|
||||
const DataLength = (fData: string) => {
|
||||
return utils.DataLength(fData);
|
||||
};
|
||||
let divLeft = computed(() => {
|
||||
if (typeof filterValue.value == 'undefined') {
|
||||
return 8 + 'px';
|
||||
}
|
||||
return DataLength(filterValue.value.split(':')[0]) + 'px';
|
||||
});
|
||||
|
||||
//回车
|
||||
function pressEnter() {
|
||||
if (
|
||||
props.value.split(':')[0] == filterValue.value.split(':')[0] &&
|
||||
filterValue.value.indexOf(':') != -1
|
||||
) {
|
||||
let ifChooseed = singleSearchOptions.value.filter(function (item) {
|
||||
return item.chooseed == true;
|
||||
});
|
||||
if (ifChooseed.length > 0) {
|
||||
let spanIndex = typeof props.spanIndex != 'undefined' ? props.spanIndex : -1;
|
||||
ctx.emit('chooseOptions', filterValue.value, ifChooseed[0], spanIndex);
|
||||
} else {
|
||||
filterValue.value = filterValue.value.split(':')[0] + ':';
|
||||
isOptionsShow.value = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
ctx.emit('pressEnter', filterValue.value, props.spanIndex, true, true);
|
||||
}
|
||||
|
||||
//删除
|
||||
function onKeyDownDelete(event: { key: string }) {
|
||||
if (event.key == 'Backspace') {
|
||||
if (typeof filterValue.value != 'undefined' && filterValue.value != '') {
|
||||
return;
|
||||
}
|
||||
ctx.emit('keydown', props.spanIndex);
|
||||
}
|
||||
}
|
||||
|
||||
function onKeyUpDelete(event: { key: string }) {
|
||||
if (event.key == 'Backspace') {
|
||||
if (typeof filterValue.value != 'undefined' && filterValue.value != '') {
|
||||
if (filterValue.value.indexOf(':') != -1) {
|
||||
let value = filterValue.value.split(':')[1];
|
||||
singleSearchOptions.value.forEach((elem, index) => {
|
||||
elem.label == value ? (elem.chooseed = true) : (elem.chooseed = false);
|
||||
});
|
||||
} else {
|
||||
isOptionsShow.value = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 空值与不为空值选中
|
||||
*/
|
||||
function chooseOption(item: any) {
|
||||
isOptionsShow.value = false;
|
||||
let spanIndex = typeof props.spanIndex != 'undefined' ? props.spanIndex : -1;
|
||||
ctx.emit('chooseOptions', filterValue.value, item, spanIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭展示选项区域
|
||||
*/
|
||||
function closeDiv() {
|
||||
isOptionsShow.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开展示选项区域
|
||||
*/
|
||||
function openDiv() {
|
||||
isOptionsShow.value = true;
|
||||
}
|
||||
|
||||
//需要监听的数据
|
||||
watch(
|
||||
() => filterValue.value,
|
||||
(newValue, oldValue) => {
|
||||
let spanIndex = typeof props.spanIndex != 'undefined' ? props.spanIndex : -1;
|
||||
ctx.emit('change', newValue, spanIndex);
|
||||
let value = newValue.split(':')[1];
|
||||
singleSearchOptions.value.forEach((item) => {
|
||||
if (item.label == value) {
|
||||
item.chooseed = true;
|
||||
} else {
|
||||
item.chooseed = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
function foucus() {
|
||||
if (singleSearchOptions.value.length <= 0) return;
|
||||
isOptionsShow.value = true;
|
||||
}
|
||||
|
||||
function checkDefault() {
|
||||
let value = filterValue.value.split(':')[1];
|
||||
singleSearchOptions.value.forEach((item) => {
|
||||
if (item.label == value) {
|
||||
item.chooseed = true;
|
||||
} else {
|
||||
item.chooseed = false;
|
||||
}
|
||||
});
|
||||
pressEnter();
|
||||
}
|
||||
|
||||
const divComInputFilter = ref(null);
|
||||
/**
|
||||
* 选中div区域外
|
||||
*/
|
||||
onClickOutside(divComInputFilter, () => {
|
||||
isOptionsShow.value = false;
|
||||
});
|
||||
|
||||
//监听滚动代码区域 showOptionsContent
|
||||
const showOptionsContent = ref();
|
||||
let oldScrollTop = 0; // 记录上一次滚动结束后的滚动距离
|
||||
const scrollTop = ref<number>(0); // 记录当前的滚动距离
|
||||
const scrollFixedStatus = ref<boolean>(true);
|
||||
function handleScroll() {
|
||||
nextTick(() => {
|
||||
showOptionsContent.value.addEventListener('scroll', () => {
|
||||
//获取dom滚动距离
|
||||
scrollTop.value = showOptionsContent.value.scrollTop;
|
||||
|
||||
//获取可视区高度
|
||||
const offsetHeight = showOptionsContent.value.offsetHeight;
|
||||
|
||||
//获取滚动条总高度
|
||||
const scrollHeight = showOptionsContent.value.scrollHeight;
|
||||
|
||||
//console.log('scrollTop.value', scrollTop.value, scrollHeight, offsetHeight);
|
||||
|
||||
if (scrollTop.value + offsetHeight >= scrollHeight) {
|
||||
// 把距离顶部的距离加上可视区域的高度 等于或者大于滚动条的总高度就是到达底部
|
||||
// console.log('已滚动到底部');
|
||||
if (isPaging.value) {
|
||||
page.value++;
|
||||
interfaceHttp('bottom');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
onBeforeUnmount(() => {
|
||||
if (typeof showOptionsContent.value != 'undefined' && showOptionsContent.value != null) {
|
||||
showOptionsContent.value.removeEventListener('scroll', () => {}); // 离开当前组件别忘记移除事件监听
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => scrollTop.value,
|
||||
|
||||
(newValue, oldValue) => {
|
||||
setTimeout(() => {
|
||||
if (newValue === window.scrollY) {
|
||||
// 延时执行后当newValue等于window.scrollY,代表滚动结束
|
||||
// console.log('滚动结束');
|
||||
oldScrollTop = newValue; // 每次滚动结束后都要给oldScrollTop赋值
|
||||
// scrollFixedStatus.value = true;
|
||||
}
|
||||
}, 20); // 必须使用延时器,否则每次newValue和window.scrollY都相等,无法判断,20ms刚好大于watch的侦听周期,故延时20ms
|
||||
if (oldScrollTop === oldValue) {
|
||||
scrollFixedStatus.value = false;
|
||||
// 每次滚动开始时oldScrollTop与oldValue相等
|
||||
// console.log('滚动开始');
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// watch(
|
||||
// () => props.optionsValue,
|
||||
// (newValue) => {
|
||||
// },
|
||||
// {
|
||||
// immediate: true,
|
||||
// },
|
||||
// );
|
||||
|
||||
function changeDefalutValue(value) {
|
||||
filterValue.value = value.label + ':' + value.value;
|
||||
let filterSpData = filterValue.value.split(':')[1];
|
||||
singleSearchOptions.value.forEach((item) => {
|
||||
if (item.label == filterSpData) {
|
||||
item.chooseed = true;
|
||||
} else {
|
||||
item.chooseed = false;
|
||||
}
|
||||
});
|
||||
let ifChooseed = singleSearchOptions.value.filter(function (item) {
|
||||
return item.chooseed == true;
|
||||
});
|
||||
if (ifChooseed.length > 0) {
|
||||
let spanIndex = typeof props.spanIndex != 'undefined' ? props.spanIndex : -1;
|
||||
ctx.emit('chooseOptions', filterValue.value, ifChooseed[0], spanIndex);
|
||||
} else {
|
||||
filterValue.value = filterValue.value.split(':')[0] + ':';
|
||||
isOptionsShow.value = true;
|
||||
}
|
||||
// return;
|
||||
}
|
||||
|
||||
return {
|
||||
divComInputFilter,
|
||||
foucus,
|
||||
inputWidth,
|
||||
divLeft,
|
||||
filterValue,
|
||||
focusObj,
|
||||
isEmptyItem,
|
||||
aComInputFilter,
|
||||
singleSearchOptions,
|
||||
isOptionsShow,
|
||||
ifTextarea,
|
||||
emptyLength,
|
||||
pressEnter,
|
||||
onKeyDownDelete,
|
||||
onKeyUpDelete,
|
||||
chooseOption,
|
||||
closeDiv,
|
||||
openDiv,
|
||||
checkDefault,
|
||||
changeDefalutValue,
|
||||
showOptionsContent,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ant-input:focus {
|
||||
border: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.show-options-content {
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
left: v-bind(divLeft);
|
||||
}
|
||||
.ns-input-filter-list {
|
||||
border-radius: 0;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 16px 0 rgb(54 58 80 / 16%);
|
||||
min-width: 120px;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
.options-list-text {
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
margin: 0px;
|
||||
max-height: 180px;
|
||||
}
|
||||
.is-disabled {
|
||||
background-color: transparent;
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
cursor: not-allowed;
|
||||
font-size: 12px;
|
||||
margin-bottom: 0px;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
.options-list-text > li:first-child,
|
||||
.options-list-text > li:last-child {
|
||||
border-radius: 0 0 0 0;
|
||||
}
|
||||
.options-list-text > li:hover {
|
||||
background-color: #ebeef2;
|
||||
}
|
||||
.options-list-text > li {
|
||||
margin-bottom: 0;
|
||||
font-size: 12px;
|
||||
padding: 6px 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: #000;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
line-height: 14px;
|
||||
}
|
||||
.options-list-text > li.choose-li {
|
||||
background-color: #00acff;
|
||||
color: #fff;
|
||||
}
|
||||
.input-textarea-span {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
vertical-align: top;
|
||||
height: 28px;
|
||||
font-size: 12px;
|
||||
border: 1px solid rgb(207, 213, 222);
|
||||
border-radius: 0px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
transition: color 0.2s ease-in-out 0s, background-color, border;
|
||||
padding: 3px 0px !important;
|
||||
min-height: 22px !important;
|
||||
line-height: 22px !important;
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
15
lib/component/form/input/input-filter-util.ts
Normal file
15
lib/component/form/input/input-filter-util.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
*通过字符计算长度
|
||||
* @param fData
|
||||
* @returns
|
||||
*/
|
||||
export function DataLength(fData: string) {
|
||||
let intLength = 0;
|
||||
for (let i = 0; i < fData.length; i++) {
|
||||
if (fData.charCodeAt(i) <= 40869 && fData.charCodeAt(i) >= 19968) intLength = intLength + 13;
|
||||
else if (fData.charCodeAt(i) >= 65 && fData.charCodeAt(i) <= 122) intLength = intLength + 7;
|
||||
else if (fData.charCodeAt(i) >= 48 && fData.charCodeAt(i) <= 57) intLength = intLength + 8;
|
||||
else intLength = intLength + 3;
|
||||
}
|
||||
return intLength == 0 ? 8 : intLength + 12;
|
||||
}
|
||||
1511
lib/component/form/input/input-filter.vue
Normal file
1511
lib/component/form/input/input-filter.vue
Normal file
File diff suppressed because it is too large
Load Diff
14
lib/component/form/input/input-group.vue
Normal file
14
lib/component/form/input/input-group.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<a-input-group>
|
||||
<slot></slot>
|
||||
</a-input-group>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInputGroup',
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
||||
215
lib/component/form/input/input-ip.vue
Normal file
215
lib/component/form/input/input-ip.vue
Normal file
@@ -0,0 +1,215 @@
|
||||
<!-- @format -->
|
||||
|
||||
<template>
|
||||
<div class="ipInputContain">
|
||||
<div
|
||||
class="ipInputBox"
|
||||
v-for="(item, index) in defaultValue"
|
||||
:key="index"
|
||||
@mouseover="
|
||||
() => {
|
||||
flag = index;
|
||||
}
|
||||
"
|
||||
@mouseleave="
|
||||
() => {
|
||||
flag = null;
|
||||
}
|
||||
">
|
||||
<!-- 包裹的error外边框 -->
|
||||
<!-- 需求修改内部边框警示去除 -->
|
||||
<!-- <div :class="activeIndex[index] ? 'active' : ''"> -->
|
||||
<ns-input
|
||||
type="number"
|
||||
class="ipInput"
|
||||
:value="defaultValue[index]"
|
||||
@change="onChange(index, $event)"
|
||||
@keydown="onKeyDown(index, $event)"
|
||||
@keyup="onKeyUp(index, $event)"
|
||||
@blur="onBlur" />
|
||||
<!-- </div> -->
|
||||
<div class="tips" v-show="flag == index ? true : false">0~255</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
// 导入的报错icon
|
||||
// import { CloseCircleOutlined } from '@ant-design/icons-vue';
|
||||
export default defineComponent({
|
||||
name: 'NsInputIp',
|
||||
// 传入的报错icon
|
||||
// components: { CloseCircleOutlined },
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// defaultValue: {
|
||||
// type: Array,
|
||||
// default: () => {
|
||||
// return ['', '', '', ''];
|
||||
// },
|
||||
// },
|
||||
},
|
||||
emits: ['change', 'updata:modelValue'],
|
||||
setup(props, { attrs }) {
|
||||
let defaultValue = ['', '', '', ''];
|
||||
let ipValue = props.modelValue;
|
||||
if (ipValue) {
|
||||
let newValue = ipValue.split('.');
|
||||
defaultValue = newValue;
|
||||
}
|
||||
return { defaultValue };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// ip: [1, 2, 3, 4],
|
||||
// 控制下弹框显示隐藏
|
||||
flag: null,
|
||||
// value: [null, null, null, null],
|
||||
// 边框警示
|
||||
activeIndex: [false, false, false, false],
|
||||
// ipAddress: new Array(4),
|
||||
ipAddress: ['', '', '', ''],
|
||||
errShow: false,
|
||||
ipResult: '',
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
defaultValue() {},
|
||||
},
|
||||
mounted() {
|
||||
// console.log(newValue);
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
// this.defaultValue = newValue;
|
||||
},
|
||||
methods: {
|
||||
onKeyDown() {},
|
||||
onBlur(e) {
|
||||
// let value = e.target.value;
|
||||
const res = this.ipAddress.some((item) => item === '');
|
||||
// console.log(typeof(this.ipAddress[1]));
|
||||
if (res) {
|
||||
this.errShow = true;
|
||||
}
|
||||
},
|
||||
onKeyUp(index, e) {
|
||||
let flag = this.ipAddress.some((item) => {
|
||||
return item === '';
|
||||
});
|
||||
if (flag) {
|
||||
this.errShow = true;
|
||||
} else {
|
||||
this.errShow = false;
|
||||
}
|
||||
},
|
||||
onChange(index, e) {
|
||||
// console.log(this.defaultValue);
|
||||
let value = e.target.value;
|
||||
let ip = this.defaultValue;
|
||||
value = value.toString().replace(/[^0-9]/g, '');
|
||||
value = parseInt(value, 10);
|
||||
if (isNaN(value)) {
|
||||
value = '';
|
||||
} else {
|
||||
value = value < 0 ? 0 : value;
|
||||
value = value > 255 ? 255 : value;
|
||||
}
|
||||
ip[index] = value;
|
||||
if (ip[index] === '') {
|
||||
ip[index] = '';
|
||||
this.activeIndex[index] = true;
|
||||
} else {
|
||||
this.activeIndex[index] = false;
|
||||
}
|
||||
let ipResult = this.defaultValue.join('.');
|
||||
this.ipResult = ipResult;
|
||||
this.$emit('change', this.ipResult);
|
||||
this.$emit('updata:modelValue', ipResult);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.active {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.ipInputContain {
|
||||
display: flex;
|
||||
max-width: 298px;
|
||||
width: 236px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
.ipInputBox {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
font-family: heiti, Helvetica, Roboto, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
// color: #172e3d;
|
||||
// overflow: hidden;
|
||||
// -webkit-font-smoothing: antialiased;
|
||||
// -moz-osx-font-smoothing: grayscale;
|
||||
.ipInput {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
padding: 0 5px;
|
||||
min-width: 40px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
// background: inherit;
|
||||
border-radius: 0;
|
||||
color: #172e3d;
|
||||
}
|
||||
.tips {
|
||||
position: absolute;
|
||||
top: 37px;
|
||||
border: 1px solid #ddd;
|
||||
background: #d8d8d8;
|
||||
min-width: 40px;
|
||||
max-width: 300px;
|
||||
height: 25px;
|
||||
box-sizing: border-box;
|
||||
padding: 3px;
|
||||
text-align: center;
|
||||
z-index: 999;
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 10px;
|
||||
border: 5px solid transparent;
|
||||
border-bottom: 5px solid #d8d8d8;
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ipInputBox::after {
|
||||
content: '.';
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: -7px;
|
||||
}
|
||||
.ipInputBox:nth-child(4):after {
|
||||
content: '';
|
||||
}
|
||||
.errorText {
|
||||
margin-top: 10px;
|
||||
color: red;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
</style>
|
||||
263
lib/component/form/input/input-modal-table.vue
Normal file
263
lib/component/form/input/input-modal-table.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<!-- @format -->
|
||||
|
||||
<template>
|
||||
<div style="">
|
||||
<ns-input
|
||||
class="ns-input-area"
|
||||
readonly
|
||||
:disabled="disabled"
|
||||
placeholder="请选择"
|
||||
@click="chose"
|
||||
:value="label">
|
||||
<template #addonAfter>
|
||||
<ns-button type="primary" :disabled="disabled" class="ns-area" @click="chose">{{
|
||||
addonAfter
|
||||
}}</ns-button>
|
||||
</template>
|
||||
</ns-input>
|
||||
<!-- <div></div> -->
|
||||
<span style="color: red">{{ alert }}</span>
|
||||
</div>
|
||||
<div id="inputarea">
|
||||
<ns-modal
|
||||
class="customModal"
|
||||
v-model:visible="visible"
|
||||
:maskClosable="false"
|
||||
@ok="handleOk"
|
||||
@cancel="cancel"
|
||||
width="900px"
|
||||
:bodyStyle="{ padding: '16px' }"
|
||||
:title="title">
|
||||
<div v-if="ifTab">
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane v-for="(item, index) in tabs" :key="index">
|
||||
<template #tab>
|
||||
<span> {{ item.name }} </span>
|
||||
</template>
|
||||
<div style="border: 16px solid #e5ebf0; border-top: none">
|
||||
<ns-view-list-table v-bind="item.tableConfig" :row-selection="rowSelection" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
<div v-else style="border: 16px solid #e5ebf0; border-top: none">
|
||||
<ns-view-list-table v-bind="tableConfig" :row-selection="rowSelection" />
|
||||
</div>
|
||||
</ns-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
// let rowSelection='';
|
||||
export default defineComponent({
|
||||
name: 'NsInputModalTable',
|
||||
props: {
|
||||
api: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
defaultKey: {
|
||||
type: String || Array,
|
||||
default: '',
|
||||
},
|
||||
alert: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
addonAfter: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
ifTab: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
tabs: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
labelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
keyValue: {
|
||||
type: String || Array,
|
||||
default: '',
|
||||
},
|
||||
field: {
|
||||
type: String,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'radio',
|
||||
},
|
||||
formModel: {
|
||||
type: Object,
|
||||
},
|
||||
allowClear: { type: Boolean },
|
||||
size: { type: String },
|
||||
disabled: { type: Boolean, default: false },
|
||||
id: { type: String },
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
bodyStyle: Object,
|
||||
},
|
||||
emits: ['change'],
|
||||
setup(props, { emit, attrs }) {
|
||||
let newValue = attrs.value;
|
||||
var label = '';
|
||||
if (newValue) {
|
||||
label = newValue;
|
||||
emit('change', props.defaultKey);
|
||||
}
|
||||
return {
|
||||
label,
|
||||
};
|
||||
},
|
||||
data(props) {
|
||||
return {
|
||||
activeKey: 0,
|
||||
visible: false,
|
||||
key: props.defaultKey || '' || [],
|
||||
roomUuid: '',
|
||||
//是否选中后在取消的值
|
||||
iflabelValue: '',
|
||||
ifkeyValue: props.defaultKey || '' || [],
|
||||
// label: '',
|
||||
rowSelection: {
|
||||
type: props.type,
|
||||
selectedRowKeys: [props.defaultKey || ''],
|
||||
//默认选中
|
||||
getCheckboxProps(record: { [x: string]: string }) {
|
||||
let uuid = [] || '';
|
||||
//如果有多个table
|
||||
if (props.ifTab) {
|
||||
for (const iterator of props.tabs) {
|
||||
uuid.push(iterator.tableConfig.rowKey);
|
||||
}
|
||||
//单选
|
||||
if (props.type == 'radio') {
|
||||
for (const iterator of uuid) {
|
||||
if (iterator in record) {
|
||||
return {
|
||||
defaultChecked: record[iterator] == props.defaultKey, // 配置默认勾选的列
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
//多选
|
||||
else if (props.type == 'checkbox') {
|
||||
for (const iterator of uuid) {
|
||||
if (iterator in record) {
|
||||
for (const defaultKey of props.defaultKey) {
|
||||
return {
|
||||
defaultChecked: record[iterator] == defaultKey, // 配置默认勾选的列
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//单个table
|
||||
else {
|
||||
if (props.type == 'radio') {
|
||||
return {
|
||||
defaultChecked: record[props.keyValue] == props.defaultKey, // 配置默认勾选的列
|
||||
};
|
||||
} else if (props.type == 'checkbox') {
|
||||
for (const iterator of props.defaultKey) {
|
||||
return {
|
||||
defaultChecked: record[props.keyValue] == iterator, // 配置默认勾选的列
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onChange: (selectedRowKeys: Key[], selectedRows: EnumData[]) => {
|
||||
console.log('ceshi');
|
||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
||||
this.rowSelection.selectedRowKeys = selectedRowKeys;
|
||||
let labelDemo = [];
|
||||
let selectedRowsValue = JSON.parse(JSON.stringify(selectedRows));
|
||||
if (props.type == 'radio') {
|
||||
this.ifkeyValue = selectedRowKeys[0];
|
||||
this.iflabelValue = selectedRowsValue[0][this.labelValue];
|
||||
} else if (props.type == 'checkbox') {
|
||||
for (const iterator of selectedRowsValue) {
|
||||
labelDemo.push(iterator[this.labelValue]);
|
||||
}
|
||||
this.ifkeyValue = selectedRowKeys;
|
||||
this.iflabelValue = labelDemo.join();
|
||||
}
|
||||
console.log(this.ifkeyValue);
|
||||
console.log(this.iflabelValue);
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
chose() {
|
||||
this.visible = true;
|
||||
},
|
||||
handleOk() {
|
||||
//确定--需要赋值
|
||||
this.label = this.iflabelValue;
|
||||
this.key = this.ifkeyValue;
|
||||
this.$emit('change', this.key);
|
||||
this.visible = false;
|
||||
},
|
||||
cancel() {
|
||||
console.log('cancle');
|
||||
//取消-不需要赋值
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.ant-modal-content .ns-table .ns-table-search {
|
||||
// border-bottom: 24px solid #ffffff !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// .ns-area:hover,
|
||||
// :focus {
|
||||
// color: white !important;
|
||||
// }
|
||||
// .ns-area {
|
||||
// color: white;
|
||||
// }
|
||||
|
||||
// :deep(.ant-input) {
|
||||
// background: rgba(223, 227, 233, 0.5);
|
||||
// }
|
||||
|
||||
// :deep(.ant-input:focus) {
|
||||
// border-color: rgba(223, 227, 233, 0.5);
|
||||
// border-right-width: 0px !important;
|
||||
// outline: 0;
|
||||
// box-shadow: none;
|
||||
// }
|
||||
|
||||
// :deep(.ns-area) {
|
||||
// border: 0;
|
||||
// }
|
||||
:deep(.ant-input-group-addon) {
|
||||
padding: 0 !important;
|
||||
border: 0px solid #dcdfe2 !important;
|
||||
height: 30px !important;
|
||||
}
|
||||
:deep(.ant-btn) {
|
||||
// height: 32px !important;
|
||||
}
|
||||
:deep(.ns-table-main) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
231
lib/component/form/input/input-modal.vue
Normal file
231
lib/component/form/input/input-modal.vue
Normal file
@@ -0,0 +1,231 @@
|
||||
<!-- @format -->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ns-input
|
||||
v-if="!baseStyle"
|
||||
class="ns-input-area"
|
||||
:disabled="true"
|
||||
v-model:value="itemName"
|
||||
placeholder="请选择">
|
||||
<template #addonAfter>
|
||||
<a-button type="primary" class="ns-area" style="height: 32px" @click="showModal">{{
|
||||
buttonLabel
|
||||
}}</a-button>
|
||||
<!-- <ns-button type="primary" class="ns-area" style="height: 32px" @click="showModal"
|
||||
>{{ buttonLabel }}
|
||||
</ns-button> -->
|
||||
</template>
|
||||
</ns-input>
|
||||
<span v-else @click="showModal">{{ buttonLabel }}</span>
|
||||
<ns-modal
|
||||
width="1000px"
|
||||
v-if="visible"
|
||||
v-model:visible="visible"
|
||||
:maskClosable="false"
|
||||
class="modal"
|
||||
wrapClassName="ns-modeBase"
|
||||
:title="title">
|
||||
<div class="ns-basic-modecontent">
|
||||
<!-- <h4 class="title">{{ title }}</h4> -->
|
||||
<ns-view-list-table :model="data" v-bind="tableConfig" :row-selection="rowSelection" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button key="back" @click="handleCancel">取消</a-button>
|
||||
<a-button
|
||||
key="submit"
|
||||
:style="{ borderColor: !ischeck ? '#dfe3e9' : '' }"
|
||||
:disabled="!ischeck"
|
||||
type="primary"
|
||||
@click="handleOk"
|
||||
>确定</a-button
|
||||
>
|
||||
</template>
|
||||
</ns-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, ref, watch } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'NsInputModal',
|
||||
props: {
|
||||
baseStyle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
defaultValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showLabel: {
|
||||
type: String,
|
||||
},
|
||||
buttonLabel: {
|
||||
type: String,
|
||||
default: '选择',
|
||||
},
|
||||
rebackInfo: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
reName: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
formModel: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
emits: ['change', 'getItem'],
|
||||
setup(props) {
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
const { showLabel, rebackInfo, tableConfig, data, reName } = props;
|
||||
const visible = ref(false);
|
||||
const itemName = ref('');
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
const selectItem = ref({});
|
||||
const ischeck = ref(false);
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
watch(
|
||||
() => props.defaultValue,
|
||||
(e: any) => {
|
||||
itemName.value = e;
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
visible,
|
||||
showModal,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
showLabel,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
rebackInfo,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
data,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
tableConfig,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
reName,
|
||||
selectItem,
|
||||
ischeck,
|
||||
itemName,
|
||||
handleCancel,
|
||||
rowSelection: {
|
||||
type: 'radio',
|
||||
onSelect: (record: T, selected: Boolean) => {
|
||||
ischeck.value = true;
|
||||
selectItem.value = record;
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleOk() {
|
||||
if (this.reName.length === 0) {
|
||||
this.itemName = this.selectItem[this.showLabel];
|
||||
if (this.rebackInfo.length === 0) {
|
||||
this.$emit('change', this.selectItem);
|
||||
this.$emit('getItem', this.selectItem);
|
||||
} else {
|
||||
if (this.rebackInfo.length === 1) {
|
||||
this.$emit('change', this.selectItem[this.rebackInfo[0]]);
|
||||
this.$emit('getItem', this.selectItem[this.rebackInfo[0]]);
|
||||
} else {
|
||||
let info = {};
|
||||
this.rebackInfo.forEach((item) => {
|
||||
info[item] = this.selectItem[item];
|
||||
});
|
||||
this.$emit('change', info);
|
||||
this.$emit('getItem', info);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.reName.length === this.rebackInfo.length && this.reName.length > 0) {
|
||||
if (this.rebackInfo.length === 1) {
|
||||
let info = {};
|
||||
info[this.reName[0]] = this.selectItem[this.rebackInfo[0]];
|
||||
this.$emit('change', info);
|
||||
this.$emit('getItem', info);
|
||||
} else {
|
||||
let info = {};
|
||||
this.rebackInfo.forEach((item, index) => {
|
||||
info[this.reName[index]] = this.selectItem[item];
|
||||
});
|
||||
this.$emit('change', info);
|
||||
this.$emit('getItem', info);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.visible = false;
|
||||
// this.$emit('ceshi', 'qaq');
|
||||
//this.$emit('change', this.itemName);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ns-area:hover,
|
||||
// :focus {
|
||||
// color: white !important;
|
||||
// }
|
||||
.ns-area {
|
||||
color: white;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
border-right: 0;
|
||||
}
|
||||
:deep(.ns-area) {
|
||||
border: 0;
|
||||
}
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
:deep(.ns-line) {
|
||||
display: none;
|
||||
}
|
||||
:deep(.ant-modal-content) {
|
||||
max-width: 900px;
|
||||
}
|
||||
:deep(.ns-form-item) {
|
||||
width: 250px;
|
||||
}
|
||||
:deep(.ant-input-group-addon) {
|
||||
padding: 0 !important;
|
||||
border: 0px solid #dcdfe2 !important;
|
||||
height: 30px !important;
|
||||
}
|
||||
:deep(.ant-btn) {
|
||||
height: 30px !important;
|
||||
}
|
||||
// :deep(.ns-table-main) {
|
||||
// padding: 0 !important;
|
||||
// }
|
||||
:deep(.ant-input-disabled) {
|
||||
color: #172e3d !important;
|
||||
}
|
||||
:deep(.ns-table-header){
|
||||
padding-left:24px;
|
||||
}
|
||||
// :deep(.ns-table .ns-table-search) {
|
||||
// border-bottom: none !important;
|
||||
// }
|
||||
</style>
|
||||
16
lib/component/form/input/input-number.vue
Normal file
16
lib/component/form/input/input-number.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<a-input-number>
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
|
||||
<slot :name="item" v-bind="data || {}"> </slot>
|
||||
</template>
|
||||
</a-input-number>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'NsInputNumber',
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
15
lib/component/form/input/input-password.vue
Normal file
15
lib/component/form/input/input-password.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<a-input-password>
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
|
||||
<slot :name="item" v-bind="data || {}"> </slot>
|
||||
</template>
|
||||
</a-input-password>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInputPassword',
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
||||
19
lib/component/form/input/input-search.vue
Normal file
19
lib/component/form/input/input-search.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<a-input-search>
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
|
||||
<slot :name="item" v-bind="data || {}"> </slot>
|
||||
</template>
|
||||
</a-input-search>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInputSearch',
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-btn) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
30
lib/component/form/input/input-text.vue
Normal file
30
lib/component/form/input/input-text.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<a-input v-bind="$attrs" class="ns-hide">
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
|
||||
<slot :name="item" v-bind="data || {}"> </slot>
|
||||
</template>
|
||||
</a-input>
|
||||
<span class="ant-input">
|
||||
<template v-if="optionsMap">{{ optionsMap[$attrs.value] }}</template>
|
||||
<template v-else>{{ $attrs.value }}</template>
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInputText',
|
||||
props: {
|
||||
defaultValue: {
|
||||
type: [String, Number],
|
||||
},
|
||||
optionsMap: {},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ant-input,
|
||||
.ant-input:hover {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
</style>
|
||||
16
lib/component/form/input/input.vue
Normal file
16
lib/component/form/input/input.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<a-input>
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
|
||||
<slot :name="item" v-bind="data || {}"> </slot>
|
||||
</template>
|
||||
</a-input>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsInput',
|
||||
setup() {},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
||||
11
lib/component/form/input/textarea.vue
Normal file
11
lib/component/form/input/textarea.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<a-textarea />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NsTextarea',
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
||||
Reference in New Issue
Block a user