123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- <template>
- <selector-card
- class="area-selector card"
- @onConfirm="onConfirm"
- @onCancel="onCancel"
- >
- <div slot="header">选择区域</div>
- <div class="selector-content" v-loading="loading">
- <div class="search-container">
- <el-input v-model="searchContent" placeholder="搜索" prefix-icon="el-icon-search"></el-input>
- </div>
- <div class="select-list scrollbar" ref="selectList">
- <div v-for="(item, key) in provinceListMap" :key="key" class="select-group-container">
- <div class="index-anchor" :id="key" :data-index="key" v-if="key !== '#'">{{ key }}</div>
- <el-collapse-transition v-for="(province, ii) in item" :key="ii*2">
- <div class="select-group tab-content global" v-if="province.name == '全国'">
- <button
- class="j-button-item global"
- :class="{
- active: province.selectedState,
- [province.id]: true
- }"
- @click="changeCityState(province, '#')"
- >{{ province.name }}</button>
- </div>
- <div class="select-group" v-else>
- <div class="tab" :class="province.id">
- <div class="tab-name-container" @click="clickCheckbox(province)">
- <div class="j-checkbox" :class="province.selectedState"></div>
- <span class="tab-name">{{ province.name }}</span>
- </div>
- <span style="flex: 1; height: 100%;" @click="changeExpandState($event, province)"></span>
- <span
- v-if="province.canExpanded"
- @click="changeExpandState($event, province)">
- <i
- class="el-icon-arrow-down"
- :class="{
- rotate180: province.expanded
- }"></i>
- </span>
- </div>
- <el-collapse-transition>
- <div v-show="province.expanded" class="tab-content">
- <div class="content-list">
- <button v-for="(city, iii) in province.children" :key="iii*3"
- class="j-button-item"
- :class="{
- active: city.selected,
- [city.id]: true
- }"
- :disabled="!city.canSelected"
- @click="changeCityState(province, city)"
- >{{ city.city }}</button>
- </div>
- </div>
- </el-collapse-transition>
- </div>
- </el-collapse-transition>
- </div>
- </div>
- </div>
- </selector-card>
- </template>
- <script>
- import { Input, Icon } from 'element-ui'
- import 'element-ui/lib/theme-chalk/base.css'
- import CollapseTransition from 'element-ui/lib/transitions/collapse-transition'
- import SelectorCard from '@/components/selector/SelectorCard.vue'
- import chinaMapJSON from '@/assets/js/china_area.js'
- import { provinceListMapExp } from '@/assets/js/selector.js'
- import { debounce, getRandomString } from '@/utils/'
- export default {
- name: 'area-selector-card',
- components: {
- [Input.name]: Input,
- [Icon.name]: Icon,
- [CollapseTransition.name]: CollapseTransition,
- SelectorCard
- },
- props: {
- // 初始化城市数据
- // 刚进入页面需要被选中的城市数据
- initCityMap: {
- type: Object,
- default () {
- return {
- // '北京': [],
- // '安徽': [],
- // '广东': [
- // '揭阳市',
- // '茂名市',
- // '韶关市'
- // ],
- // '河北': [
- // '邯郸市',
- // '秦皇岛市',
- // '保定市'
- // ],
- // '福建': [
- // '福州市',
- // '厦门市',
- // '宁德市'
- // ],
- // '重庆': []
- }
- }
- }
- },
- data () {
- return {
- searchContent: '',
- loading: false,
- // 省份与字母IndexBar对照表
- provinceListMapExp,
- provinceListMap: {
- // A: [
- // {
- // name: '安徽',
- // expanded: false,
- // canExpanded: true,
- // selectedState: '',
- // children: []
- // }
- // ]
- },
- // indexBar数据
- indexList: [],
- provinceExp: {
- name: '',
- // 展开状态
- expanded: false,
- // 是否可以展开
- canExpanded: false,
- // 选中状态: half(半选)、checked(全选)、''(未选中)、checkeddisabled(全选不能点击)、nonedisabled(未选不能点击)
- selectedState: '',
- children: [],
- id: ''
- }
- }
- },
- watch: {
- initCityMap (newVal, oldVal) {
- this.setCitySelected(newVal)
- },
- searchContent: debounce(function (newVal, oldVal) {
- const search = newVal
- const { findP } = this.getProvinceWithString(search)
- const id = findP[0] && findP[0].id
- if (id) {
- if (findP[0].canExpanded) {
- findP[0].expanded = true
- }
- this.$nextTick(() => {
- const wrapper = document.querySelector('.area-selector.card')
- this.$refs.selectList.scrollTop = wrapper.querySelector(`.${id}`).offsetTop
- // document.querySelector(`.${id}`).scrollIntoView() // 兼容性有问题
- })
- }
- }, 300)
- },
- created () {
- this.initIndexBarAndAreaMap()
- this.provinceListMap['#'][0].selectedState = 'checked'
- this.setCitySelected(this.initCityMap)
- },
- methods: {
- changeLoadingState (s) {
- this.loading = s
- },
- // 整理城市数据列表(并初始化indexBar数据)
- initIndexBarAndAreaMap () {
- // 整理数据得到indexListMap(),同时获得indexList
- const provinceListMap = {}
- const indexList = []
- for (const key in this.provinceListMapExp) {
- const areaArr = []
- indexList.push(key)
- this.provinceListMapExp[key].forEach(pName => {
- const provinceExp = JSON.parse(JSON.stringify(this.provinceExp))
- provinceExp.name = pName
- provinceExp.id = `ap-${getRandomString(8).toLowerCase()}`
- if (pName !== '全国') {
- const cities = this.getCitiesFromJSONMap(pName)
- // console.log(pName, cities)
- // 筛选掉直辖市和特别行政区(台湾省也不不需要展开)
- if (cities.ProRemark === '省份' || cities.ProRemark === '自治区') {
- if (cities.ProID === 32) {
- provinceExp.children = []
- provinceExp.canExpanded = false
- } else {
- cities.city.forEach(c => {
- // 将市区重组成一个新的对象
- return provinceExp.children.push({
- city: c.name,
- selected: false,
- canSelected: true,
- id: `ac-${getRandomString(8).toLowerCase()}`
- })
- })
- }
- } else {
- provinceExp.children = []
- provinceExp.canExpanded = false
- }
- }
- provinceExp.canExpanded = provinceExp.children.length !== 0
- areaArr.push(provinceExp)
- })
- provinceListMap[key] = areaArr
- }
- this.provinceListMap = provinceListMap
- this.indexList = indexList
- // 给provinceListMap赋值
- for (const k in provinceListMap) {
- this.$set(this.provinceListMap, k, provinceListMap[k])
- }
- },
- // 循环chinaMapJSON,找到对应省下面对应的市
- getCitiesFromJSONMap (provinceName) {
- return chinaMapJSON.find(item => item.name.indexOf(provinceName) !== -1)
- },
- // 输入字符串,找到其所在省份
- getProvinceWithString (s = '') {
- // 找是否有省份相同的
- const findP = [] // 匹配到的省份数组
- const findC = [] // 匹配到的市数组
- if (s) {
- for (const key in this.provinceListMap) {
- const item = this.provinceListMap[key]
- for (let i = 0; i < item.length; i++) {
- if (item[i].name.includes(s)) {
- findP.push(item[i])
- }
- if (Array.isArray(item[i].children) && item[i].children.length !== 0) {
- item[i].children.forEach(city => {
- if (city.city.includes(s)) {
- findP.push(item[i])
- findC.push(city)
- }
- })
- }
- }
- }
- }
- return {
- findP,
- findC
- }
- },
- // 控制城市盒子展开和收起
- changeExpandState (e, province) {
- if (!province.canExpanded) return
- province.expanded = !province.expanded
- },
- // 城市选择按钮点击事件
- // 根据城市的选择情况判断省份的选择情况
- changeCityState (province, city) {
- if (city === '#') {
- return this.setCitySelected()
- }
- // 全国置为空
- this.provinceListMap['#'][0].selectedState = ''
- city.selected = !city.selected
- // 判断省份的选择状态
- let count = 0
- const cityLength = province.children.length
- if (cityLength) {
- province.children.forEach(v => {
- // 前提是可点击的
- if (v.canSelected && v.selected) {
- count++
- }
- })
- } else {
- // 直辖市或自治区
- province.canExpanded = false
- province.expanded = false
- }
- // 选中状态: half(半选)、checked(全选)、''(未选中)、checkeddisabled(全选不能点击)、nonedisabled(未选不能点击)
- if (count === 0) {
- province.selectedState = ''
- } else if (count < cityLength) {
- province.selectedState = 'half'
- } else if (count === cityLength) {
- province.selectedState = 'checked'
- } else {
- province.selectedState = ''
- }
- const pState = this.checkAllProvinceState()
- // 如果所有省份被全选,则取消所有选中,让全国选中
- if (pState.allSelected || pState.noSelected) {
- this.setCitySelected()
- }
- },
- // 省份checkbox点击事件
- clickCheckbox (province) {
- const state = province.selectedState
- if (state === 'checkeddisabled' || state === 'nonedisabled') return
- // 全国置为空
- this.provinceListMap['#'][0].selectedState = ''
- if (state === '' || state === 'half') {
- province.children.forEach(v => (v.selected = true))
- province.selectedState = 'checked'
- } else {
- province.children.forEach(v => (v.selected = false))
- province.selectedState = ''
- }
- const pState = this.checkAllProvinceState()
- // 如果所有省份被全选,则取消所有选中,让全国选中
- if (pState.allSelected || pState.noSelected) {
- this.setCitySelected()
- }
- },
- // 检查是否所有省份按钮被全选中
- // 全部被全选->返回true
- checkAllProvinceState () {
- const stateArr = []
- for (const key in this.provinceListMap) {
- this.provinceListMap[key].forEach(item => {
- if (item.name !== '全国') {
- if (item.selectedState === '') {
- stateArr.push('checked')
- } else if (item.selectedState === 'checked') {
- stateArr.push('unchecked')
- } else {
- stateArr.push('other')
- }
- }
- })
- }
- // 统计不同状态的个数
- const counter = {
- checked: 0,
- unchecked: 0,
- other: 0
- }
- for (let i = 0; i < stateArr.length; i++) {
- const k = stateArr[i]
- if (counter[k]) {
- counter[k] += 1
- } else {
- counter[k] = 1
- }
- }
- // console.log(counter)
- return {
- state: stateArr,
- allSelected: counter.checked === stateArr.length,
- noSelected: counter.unchecked === stateArr.length
- }
- },
- // 初始化选中城市数据
- setCitySelected (data) {
- // 设置全国
- if (!data || Object.keys(data).length === 0) {
- // 其他全部设置不选中,全国设置选中
- for (const key in this.provinceListMap) {
- this.provinceListMap[key].forEach(item => {
- item.selectedState = ''
- item.children.forEach(iitem => {
- iitem.selected = false
- })
- if (item.name === '全国') {
- item.selectedState = 'checked'
- }
- })
- }
- } else {
- // 先将所有城市选择取消
- this.setCitySelected()
- // 设置某几个省份被选中
- for (const key in this.provinceListMap) {
- this.provinceListMap[key].forEach(item => {
- const selectCityArr = data[item.name]
- if (Array.isArray(selectCityArr)) {
- if (selectCityArr.length === 0) {
- // 全省被选中
- item.children.forEach(iitem => {
- iitem.selected = true
- })
- item.selectedState = 'checked'
- } else {
- // 省份中的某些市被选中
- item.children.forEach(iitem => {
- if (selectCityArr.indexOf(iitem.city) !== -1) {
- iitem.selected = true
- }
- })
- item.selectedState = 'half'
- }
- }
- if (item.name === '全国') {
- item.selectedState = ''
- }
- })
- }
- }
- },
- // 获取当前选中城市数据
- getSelectedCity () {
- const counter = {}
- // 判断是否全国被选中
- if (this.provinceListMap['#'][0].selectedState === 'checked') {
- return counter
- }
- // 全国没有被选中,排除循环全国
- for (const key in this.provinceListMap) {
- if (key === '#') continue
- this.provinceListMap[key].forEach(item => {
- // 当前省份下被选中的城市数量
- const selectedCityArr = []
- const cityTotalCount = item.children.length
- item.children.forEach(iitem => {
- if (iitem.selected && iitem.canSelected) {
- selectedCityArr.push(iitem.city)
- }
- })
- // 计算出当前省份下的城市是否被全选了
- if (cityTotalCount === selectedCityArr.length && item.selectedState === 'checked') {
- // 城市被全选
- counter[item.name] = []
- } else {
- if (selectedCityArr.length !== 0) {
- counter[item.name] = selectedCityArr
- }
- }
- })
- }
- return counter
- },
- // 统计城市分布数量
- getCityCount () {
- const selectedCount = {
- // 全国被选中时,country为1
- country: 1,
- province: 0,
- city: {
- // 一共选了多少个城市
- totalCount: 0,
- // 分布在几个省份
- pCount: 0
- }
- }
- const selected = this.getSelectedCity()
- if (Object.keys(selected).length === 0) {
- // 全国
- } else {
- selectedCount.country = 0
- for (const p in selected) {
- if (selected[p].length === 0) {
- selectedCount.province++
- } else {
- selectedCount.city.pCount++
- selected[p].forEach(() => {
- selectedCount.city.totalCount++
- })
- }
- }
- }
- const tipText = {
- p: selectedCount.province === 0 ? '' : selectedCount.province + '个省',
- c: selectedCount.city.totalCount === 0 ? '' : selectedCount.city.totalCount + '个市',
- s: selectedCount.city.pCount === 1 ? '' : '(分布在' + selectedCount.city.pCount + '个省内)',
- text: ''
- }
- if (selectedCount.province === 1) {
- tipText.text = '全国'
- } else {
- let dot = ''
- if (selectedCount.city.totalCount !== 0 && selectedCount.province !== 0) {
- dot = '、'
- }
- if (selectedCount.city.totalCount === 0 || selectedCount.city.totalCount === 1) {
- tipText.s = ''
- }
- tipText.text = tipText.p + dot + tipText.c + tipText.s
- }
- return {
- data: selectedCount,
- zh: tipText
- }
- },
- onCancel () {
- this.$emit('onCancel')
- },
- onConfirm () {
- const selectedCity = this.getSelectedCity()
- this.$emit('onConfirm', selectedCity)
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .j-checkbox {
- width: 18px;
- height: 18px;
- border-radius: 50%;
- border: 1px solid #e0e0e0;
- cursor: pointer;
- &.checked {
- border: 0;
- background: url('~@/assets/images/icon/checked.png') no-repeat;
- background-size: 20px;
- &[disabled] {
- background: url('~@/assets/images/icon/checked_disabled.png') no-repeat;
- background-size: 100%;
- }
- }
- &.half {
- border: 0;
- background: url('~@/assets/images/icon/checked-half.png') no-repeat;
- background-size: 20px;
- }
- }
- .j-button-item {
- &.global {
- padding: 6px 8px;
- height: 24px;
- line-height: 24px;
- font-weight: 700;
- color: inherit;
- border-color: rgba(0,0,0,.05);
- }
- }
- [class^=el-icon-] {
- transition: transform 0.2s ease;
- }
- .rotate180 {
- transform: rotate(180deg);
- }
- .j-button-item {
- border-color: transparent;
- }
- .select-group {
- font-size: 14px;
- &.global {
- padding: 0 18px;
- }
- .tab {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 18px;
- height: 40px;
- border-bottom: 1px solid rgba(0,0,0,.05);
- cursor: pointer;
- }
- .tab-name-container {
- display: flex;
- align-items: center;
- .tab-name {
- margin-left: 10px;
- font-weight: bold;
- }
- }
- .tab-content {
- padding: 0 18px;
- // border-bottom: 1px solid rgba(0,0,0,.05);
- .content-list {
- display: flex;
- flex-wrap: wrap;
- padding: 2px 0;
- }
- }
- }
- </style>
|