This commit is contained in:
xuziqiang
2024-05-15 17:29:42 +08:00
commit d0155dbe3c
7296 changed files with 1832517 additions and 0 deletions

View 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;
};

View 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>

View 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>

View 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>

View 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>

View 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.不是keyvalue形式使用默认标签
* 2.是keyvalue形式是否已存在搜索标签替换 否:使用默认标签,递归再判断一次
* @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);
}
/**
* 处理filterArrayShowspan展示
* 页面展示的选中值
* @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>

View 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>

View 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>

View 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>

View 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;
}

File diff suppressed because it is too large Load Diff

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>