省市级联组件

This commit is contained in:
fengachen
2021-09-01 19:02:12 +08:00
parent d92c946254
commit c59f959373
12 changed files with 3803 additions and 2448 deletions
+275
View File
@@ -0,0 +1,275 @@
.area-zoom-in-top-enter-active,
.area-zoom-in-top-leave-active {
opacity: 1;
transform: scaleY(1);
}
.area-zoom-in-top-enter,
.area-zoom-in-top-leave-active {
opacity: 0;
transform: scaleY(0);
}
.area-select {
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
font-feature-settings: 'tnum';
position: relative;
outline: 0;
display: block;
background-color: #fff;
border: 1px solid #d9d9d9;
border-top-width: 1.02px;
border-radius: 4px;
outline: none;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.area-select-wrap .area-select {
display: inline-block;
}
.area-select * {
box-sizing: border-box;
}
.area-select:hover {
border-color: #40a9ff;
border-right-width: 1px !important;
outline: 0;
}
.area-select:active {
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.area-select.small {
width: 126px;
}
.area-select.medium {
width: 160px;
}
.area-select.large {
width: 194px;
}
.area-select.is-disabled {
background: #eceff5;
cursor: not-allowed;
}
.area-select.is-disabled:hover {
border-color: #e1e2e6;
}
.area-select.is-disabled .area-selected-trigger {
cursor: not-allowed;
}
.area-select .area-selected-trigger {
position: relative;
display: block;
font-size: 14px;
cursor: pointer;
margin: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
height: 100%;
padding: 8px 20px 7px 12px;
}
.area-select .area-select-icon {
position: absolute;
top: 50%;
margin-top: -2px;
right: 6px;
content: "";
width: 0;
height: 0;
border: 6px solid transparent;
border-top-color: rgba(0, 0, 0, 0.25);
transition: all .3s linear;
transform-origin: center;
}
.area-select .area-select-icon.active {
margin-top: -8px;
transform: rotate(180deg);
}
.area-selectable-list-wrap {
position: absolute;
width: 100%;
max-height: 275px;
z-index: 15000;
background-color: #fff;
box-sizing: border-box;
overflow-x: auto;
margin: 2px 0;
border-radius: 4px;
outline: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: opacity 0.15s, transform 0.3s !important;
transform-origin: center top !important;
}
.area-selectable-list {
position: relative;
margin: 0;
padding: 6px 0;
width: 100%;
font-size: 14px;
color: #565656;
list-style: none;
}
.area-selectable-list .area-select-option {
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 0 15px 0 10px;
height: 32px;
line-height: 32px;
}
.area-selectable-list .area-select-option.hover {
background-color: #e6f7ff;
}
// 请选择省市placeholder 为灰色
.placeholderGray .area-selected-trigger {
color: rgb(191,191,191);
}
.area-selectable-list .area-select-option.selected {
color: rgba(0, 0, 0, 0.65);
font-weight: 600;
background-color: #efefef;
}
.cascader-menu-list-wrap {
position: absolute;
white-space: nowrap;
z-index: 15000;
background-color: #fff;
box-sizing: border-box;
overflow: hidden;
font-size: 0;
margin: 2px 0;
border-radius: 4px;
outline: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: opacity 0.15s, transform 0.3s !important;
transform-origin: center top !important;
}
.cascader-menu-list {
position: relative;
margin: 0;
font-size: 14px;
color: #565656;
padding: 6px 0;
list-style: none;
display: inline-block;
height: 204px;
overflow-x: hidden;
overflow-y: auto;
min-width: 160px;
vertical-align: top;
background-color: #fff;
border-right: 1px solid #e4e7ed;
}
.cascader-menu-list:last-child {
border-right: none;
}
.cascader-menu-list .cascader-menu-option {
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 0 15px 0 10px;
height: 32px;
line-height: 32px;
}
.cascader-menu-list .cascader-menu-option.hover,
.cascader-menu-list .cascader-menu-option:hover {
background-color: #e6f7ff;
}
.cascader-menu-list .cascader-menu-option.selected {
color: rgba(0, 0, 0, 0.65);
font-weight: 600;
background-color: #efefef;
}
.cascader-menu-list .cascader-menu-option.cascader-menu-extensible:after {
position: absolute;
top: 50%;
margin-top: -4px;
right: 5px;
content: "";
width: 0;
height: 0;
border: 4px solid transparent;
border-left-color: #a1a4ad;
}
.cascader-menu-list::-webkit-scrollbar,
.area-selectable-list-wrap::-webkit-scrollbar {
width: 8px;
background: transparent;
}
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:decremen,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:end:decrement,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:increment,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:start:increment,
.cascader-menu-list::-webkit-scrollbar-button:vertical:decremen,
.cascader-menu-list::-webkit-scrollbar-button:vertical:end:decrement,
.cascader-menu-list::-webkit-scrollbar-button:vertical:increment,
.cascader-menu-list::-webkit-scrollbar-button:vertical:start:increment {
display: none;
}
.cascader-menu-list::-webkit-scrollbar-thumb:vertical,
.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical {
background-color: #b8b8b8;
border-radius: 4px;
}
.cascader-menu-list::-webkit-scrollbar-thumb:vertical:hover,
.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical:hover {
background-color: #777;
}
.ant-pagination-disabled i{
cursor: not-allowed!important;
}
.urge-num{
color:red;
margin-left: 4px;
}
+78
View File
@@ -0,0 +1,78 @@
import { pcaa } from 'area-data'
/**
* 省市区
*/
export default class Area {
/**
* 构造器
* @param express
*/
constructor() {
const arr = []
const province = pcaa['86']
Object.keys(province).map(key => {
arr.push({ id: key, text: province[key], pid: '86' })
const city = pcaa[key]
Object.keys(city).map(key2 => {
arr.push({ id: key2, text: city[key2], pid: key })
const qu = pcaa[key2]
Object.keys(qu).map(key3 => {
arr.push({ id: key3, text: qu[key3], pid: key2 })
})
})
})
this.all = arr
}
get pca() {
return this.all
}
getCode(text) {
if (!text || text.length === 0) {
return ''
}
for (const item of this.all) {
if (item.text === text) {
return item.id
}
}
}
getText(code) {
if (!code || code.length === 0) {
return ''
}
const arr = []
this.getAreaBycode(code, arr)
return arr.join('/')
}
getRealCode(code) {
const arr = []
this.getPcode(code, arr)
return arr
}
getPcode(id, arr) {
for (const item of this.all) {
if (item.id === id) {
arr.unshift(id)
if (item.pid !== '86') {
this.getPcode(item.pid, arr)
}
}
}
}
getAreaBycode(code, arr) {
// console.log("this.all.length",this.all)
for (const item of this.all) {
if (item.id === code) {
arr.unshift(item.text)
this.getAreaBycode(item.pid, arr)
}
}
}
}
+74
View File
@@ -0,0 +1,74 @@
import pca from '../../../public/map/areaPca'
/**
* 省市区
*/
export default class Area {
/**
* 构造器
* @param express
*/
constructor() {
const arr = []
const province = pca['86']
Object.keys(province).map(key => {
arr.push({ id: key, text: province[key], pid: '86' })
const city = pca[key]
Object.keys(city).map(key2 => {
arr.push({ id: key2, text: city[key2], pid: key })
})
})
this.all = arr
}
get pca() {
return this.all
}
getCode(text) {
if (!text || text.length === 0) {
return ''
}
for (const item of this.all) {
if (item.text === text) {
return item.id
}
}
}
getText(code) {
if (!code || code.length === 0) {
return ''
}
const arr = []
this.getAreaBycode(code, arr)
return arr.join('/')
}
getRealCode(code) {
const arr = []
this.getPcode(code, arr)
return arr
}
getPcode(id, arr) {
for (const item of this.all) {
if (item.id === id) {
arr.unshift(id)
if (item.pid !== '86') {
this.getPcode(item.pid, arr)
}
}
}
}
getAreaBycode(code, arr) {
// console.log("this.all.length",this.all)
for (const item of this.all) {
if (item.id === code) {
arr.unshift(item.text)
this.getAreaBycode(item.pid, arr)
}
}
}
}
+35
View File
@@ -0,0 +1,35 @@
/**
* 获取字符串的长度ascii长度为1 中文长度为2
* @param str
* @returns {number}
*/
export const getStrFullLength = (str = '') =>
str.split('').reduce((pre, cur) => {
const charCode = cur.charCodeAt(0)
if (charCode >= 0 && charCode <= 128) {
return pre + 1
}
return pre + 2
}, 0)
/**
* 给定一个字符串和一个长度,将此字符串按指定长度截取
* @param str
* @param maxLength
* @returns {string}
*/
export const cutStrByFullLength = (str = '', maxLength) => {
let showLength = 0
return str.split('').reduce((pre, cur) => {
const charCode = cur.charCodeAt(0)
if (charCode >= 0 && charCode <= 128) {
showLength += 1
} else {
showLength += 2
}
if (showLength <= maxLength) {
return pre + cur
}
return pre
}, '')
}
+12
View File
@@ -0,0 +1,12 @@
/**
* components util
*/
/**
* 清理空值,对象
* @param children
* @returns {*[]}
*/
export function filterEmpty(children = []) {
return children.filter(c => c.tag || (c.text && c.text.trim() !== ''))
}
+169
View File
@@ -0,0 +1,169 @@
<template>
<div v-if="!reloading" class="j-area-linkage">
<!--area-cascader level-0二级联动 1-三级联动-->
<area-cascader
v-if="_type === enums.type[0]"
:value="innerValue"
:data="pca"
:style="{width}"
v-bind="$attrs"
v-on="_listeners"
@change="handleChange"
:class="isActive ? 'placeholderBlack' : 'placeholderGray'"
/>
<!--area-select level-1二级联动 2-三级联动-->
<area-select
v-else-if="_type === enums.type[1]"
:value="innerValue"
:data="pca"
v-bind="$attrs"
v-on="_listeners"
@change="handleChange"
:class="isActive ? 'placeholderBlack' : 'placeholderGray'"
/>
<div v-else>
<span style="color:red;"> Bad type value: {{_type}}</span>
</div>
</div>
</template>
<script>
import pca from '../../../public/map/areaPca'
import Area from '@comp/_util/AreaPca'
export default {
name: 'JAreaLinkagePca',
props: {
value: {
type: String,
required: false
},
// 组件的类型,可选值:
// select 下拉样式
// cascader 级联样式(默认)
type: {
type: String,
default: 'cascader'
},
width: {
type: String,
default: '100%'
}
},
data() {
return {
pca,
isActive: false, // false placeholder为灰色。 true placeholder 为黑色
innerValue: [],
usedListeners: ['change'],
enums: {
type: ['cascader', 'select']
},
reloading: false,
areaData: ''
}
},
computed: {
_listeners() {
const listeners = { ...this.$listeners }
// 去掉已使用的事件,防止冲突
this.usedListeners.forEach(key => {
delete listeners[key]
})
return listeners
},
_type() {
if (this.enums.type.includes(this.type)) {
return this.type
} else {
console.error(`JAreaLinkage的type属性只能接收指定的值(${this.enums.type.join('|')}`)
return this.enums.type[0]
}
}
},
watch: {
value: {
immediate: true,
handler() {
this.loadDataByValue(this.value)
if (this.value) {
this.isActive = true // 此时value有值
}
}
}
},
created() {
this.initAreaData()
},
methods: {
/** 通过 value 反推 options */
loadDataByValue(value) {
if (!value || value.length === 0) {
this.innerValue = []
this.reloading = true
setTimeout(() => {
this.reloading = false
}, 100)
} else {
this.initAreaData()
const arr = this.areaData.getRealCode(value)
this.innerValue = arr
}
},
/** 通过地区code获取子级 */
loadDataByCode(value) {
const options = []
const data = pca[value]
if (data) {
for (const key in data) {
if (data.hasOwnProperty(key)) {
options.push({ value: key, label: data[key] })
}
}
return options
} else {
return []
}
},
/** 判断是否有子节点 */
hasChildren(options) {
options.forEach(option => {
const data = this.loadDataByCode(option.value)
option.isLeaf = data.length === 0
})
},
handleChange(values) {
const value = values[values.length - 1]
this.$emit('change', value)
},
initAreaData() {
if (!this.areaData) {
this.areaData = new Area()
}
}
},
model: { prop: 'value', event: 'change' }
}
</script>
<style lang="less" scoped>
.j-area-linkage {
height:40px;
/deep/ .area-cascader-wrap .area-select {
width: 100%;
}
/deep/ .area-select .area-selected-trigger {
line-height: 1.15;
}
}
///deep/.placeholderBlack .area-cascader-wrap .area-select.active {
// color: #333 !important;
//}
///deep/.placeholderGray .area-cascader-wrap .area-select {
// color: rgb(191,191,191) !important;
//}
</style>
+101
View File
@@ -0,0 +1,101 @@
<template>
<a-date-picker
:disabledDate="disabledDate"
:disabled="disabled || readOnly"
:placeholder="placeholder"
@change="handleDateChange"
:value="momVal"
:showTime="showTime"
:format="dateFormat"
:mode="mode"
:getCalendarContainer="getCalendarContainer"
/>
</template>
<script>
import moment from 'moment'
export default {
name: 'JDate',
props: {
placeholder: {
type: String,
default: '',
required: false
},
value: {
type: String,
required: false
},
disabledDate: {
type: Function
},
dateFormat: {
type: String,
default: 'YYYY-MM-DD',
required: false
},
// 此属性可以被废弃了
triggerChange: {
type: Boolean,
required: false,
default: false
},
readOnly: {
type: Boolean,
required: false,
default: false
},
disabled: {
type: Boolean,
required: false,
default: false
},
showTime: {
type: Boolean,
required: false,
default: false
},
getCalendarContainer: {
type: Function,
default: (node) => node.parentNode
},
mode: {
type: String,
required: false,
default: 'date'
}
},
data() {
const dateStr = this.value
return {
decorator: '',
momVal: !dateStr ? null : moment(dateStr, this.dateFormat)
}
},
watch: {
value(val) {
if (!val) {
this.momVal = null
} else {
this.momVal = moment(val, this.dateFormat)
}
}
},
methods: {
moment,
handleDateChange(mom, dateStr) {
console.log('handleDateChange----', dateStr)
this.$emit('change', dateStr)
}
},
// 2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型 这个牛逼
model: {
prop: 'value',
event: 'change'
}
}
</script>
<style lang="less" scoped>
/deep/ .ant-input {
padding: 4px 6px;
}
</style>
+3
View File
@@ -26,6 +26,8 @@ import JeroComponents from "@/components/jero/index";
import JDictSelectTag from "./components/dict/index.js";
import '@/utils/filter' // base filtermybatis-plus
import vTrim from "@/utils/vTrim";
import '@/assets/less/JAreaLinkage.less'
import VueAreaLinkage from 'vue-area-linkage'
Vue.config.productionTip = false;
Vue.use(Storage, config.storageOptions);
@@ -35,6 +37,7 @@ Vue.use(router);
Vue.use(JeroComponents);
Vue.use(JDictSelectTag);
Vue.use(vTrim)
Vue.use(VueAreaLinkage)
new Vue({
router,